1/*
2 * Copyright (C) 2010, Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1.  Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2.  Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23 */
24
25#include "config.h"
26
27#if ENABLE(WEB_AUDIO)
28
29#include "DelayDSPKernel.h"
30
31#include "AudioUtilities.h"
32#include <algorithm>
33
34using namespace std;
35
36const double DefaultMaxDelayTime = 1.0;
37const double SmoothingTimeConstant = 0.020; // 20ms
38
39namespace WebCore {
40
41DelayDSPKernel::DelayDSPKernel(DelayProcessor* processor)
42    : AudioDSPKernel(processor)
43    , m_maxDelayTime(DefaultMaxDelayTime)
44    , m_writeIndex(0)
45    , m_firstTime(true)
46{
47    ASSERT(processor && processor->sampleRate() > 0);
48    if (!processor)
49        return;
50
51    m_buffer.resize(static_cast<size_t>(processor->sampleRate() * DefaultMaxDelayTime));
52    m_buffer.zero();
53
54    m_smoothingRate = AudioUtilities::discreteTimeConstantForSampleRate(SmoothingTimeConstant, processor->sampleRate());
55}
56
57DelayDSPKernel::DelayDSPKernel(double maxDelayTime, double sampleRate)
58    : AudioDSPKernel(sampleRate)
59    , m_maxDelayTime(maxDelayTime)
60    , m_writeIndex(0)
61    , m_firstTime(true)
62{
63    ASSERT(maxDelayTime > 0.0);
64    if (maxDelayTime <= 0.0)
65        return;
66
67    size_t bufferLength = static_cast<size_t>(sampleRate * maxDelayTime);
68    ASSERT(bufferLength);
69    if (!bufferLength)
70        return;
71
72    m_buffer.resize(bufferLength);
73    m_buffer.zero();
74
75    m_smoothingRate = AudioUtilities::discreteTimeConstantForSampleRate(SmoothingTimeConstant, sampleRate);
76}
77
78void DelayDSPKernel::process(const float* source, float* destination, size_t framesToProcess)
79{
80    size_t bufferLength = m_buffer.size();
81    float* buffer = m_buffer.data();
82
83    ASSERT(bufferLength);
84    if (!bufferLength)
85        return;
86
87    ASSERT(source && destination);
88    if (!source || !destination)
89        return;
90
91    double sampleRate = this->sampleRate();
92    double delayTime = delayProcessor() ? delayProcessor()->delayTime()->value() : m_desiredDelayFrames / sampleRate;
93
94    // Make sure the delay time is in a valid range.
95    delayTime = min(maxDelayTime(), delayTime);
96    delayTime = max(0.0, delayTime);
97
98    if (m_firstTime) {
99        m_currentDelayTime = delayTime;
100        m_firstTime = false;
101    }
102
103    int n = framesToProcess;
104    while (n--) {
105        // Approach desired delay time.
106        m_currentDelayTime += (delayTime - m_currentDelayTime) * m_smoothingRate;
107
108        double desiredDelayFrames = m_currentDelayTime * sampleRate;
109
110        double readPosition = m_writeIndex + bufferLength - desiredDelayFrames;
111        if (readPosition > bufferLength)
112            readPosition -= bufferLength;
113
114        // Linearly interpolate in-between delay times.
115        int readIndex1 = static_cast<int>(readPosition);
116        int readIndex2 = (readIndex1 + 1) % bufferLength;
117        double interpolationFactor = readPosition - readIndex1;
118
119        double input = static_cast<float>(*source++);
120        buffer[m_writeIndex] = static_cast<float>(input);
121        m_writeIndex = (m_writeIndex + 1) % bufferLength;
122
123        double sample1 = buffer[readIndex1];
124        double sample2 = buffer[readIndex2];
125
126        double output = (1.0 - interpolationFactor) * sample1 + interpolationFactor * sample2;
127
128        *destination++ = static_cast<float>(output);
129    }
130}
131
132void DelayDSPKernel::reset()
133{
134    m_firstTime = true;
135    m_buffer.zero();
136}
137
138} // namespace WebCore
139
140#endif // ENABLE(WEB_AUDIO)
141