summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/audio
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/audio')
-rw-r--r--WebCore/platform/audio/Cone.cpp84
-rw-r--r--WebCore/platform/audio/Cone.h63
-rw-r--r--WebCore/platform/audio/FFTConvolver.cpp108
-rw-r--r--WebCore/platform/audio/FFTConvolver.h71
-rw-r--r--WebCore/platform/audio/Reverb.cpp227
-rw-r--r--WebCore/platform/audio/Reverb.h66
-rw-r--r--WebCore/platform/audio/ReverbAccumulationBuffer.cpp117
-rw-r--r--WebCore/platform/audio/ReverbAccumulationBuffer.h67
-rw-r--r--WebCore/platform/audio/ReverbConvolver.cpp228
-rw-r--r--WebCore/platform/audio/ReverbConvolver.h97
-rw-r--r--WebCore/platform/audio/ReverbConvolverStage.cpp163
-rw-r--r--WebCore/platform/audio/ReverbConvolverStage.h83
-rw-r--r--WebCore/platform/audio/ReverbInputBuffer.cpp89
-rw-r--r--WebCore/platform/audio/ReverbInputBuffer.h64
14 files changed, 1527 insertions, 0 deletions
diff --git a/WebCore/platform/audio/Cone.cpp b/WebCore/platform/audio/Cone.cpp
new file mode 100644
index 0000000..91813ab
--- /dev/null
+++ b/WebCore/platform/audio/Cone.cpp
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "Cone.h"
+
+namespace WebCore {
+
+ConeEffect::ConeEffect()
+ : m_innerAngle(360.0)
+ , m_outerAngle(360.0)
+ , m_outerGain(0.0)
+{
+}
+
+double ConeEffect::gain(Vector3 sourcePosition, Vector3 sourceOrientation, Vector3 listenerPosition)
+{
+ if (sourceOrientation.isZero() || ((m_innerAngle == 360.0) && (m_outerAngle == 360.0)))
+ return 1.0; // no cone specified - unity gain
+
+ // Normalized source-listener vector
+ Vector3 sourceToListener = listenerPosition - sourcePosition;
+ sourceToListener.normalize();
+
+ Vector3 normalizedSourceOrientation = sourceOrientation;
+ normalizedSourceOrientation.normalize();
+
+ // Angle between the source orientation vector and the source-listener vector
+ double dotProduct = dot(sourceToListener, normalizedSourceOrientation);
+ double angle = 180.0 * acos(dotProduct) / M_PI;
+ double absAngle = fabs(angle);
+
+ // Divide by 2.0 here since API is entire angle (not half-angle)
+ double absInnerAngle = fabs(m_innerAngle) / 2.0;
+ double absOuterAngle = fabs(m_outerAngle) / 2.0;
+ double gain = 1.0;
+
+ if (absAngle <= absInnerAngle)
+ // No attenuation
+ gain = 1.0;
+ else if (absAngle >= absOuterAngle)
+ // Max attenuation
+ gain = m_outerGain;
+ else {
+ // Between inner and outer cones
+ // inner -> outer, x goes from 0 -> 1
+ double x = (absAngle - absInnerAngle) / (absOuterAngle - absInnerAngle);
+ gain = (1.0 - x) + m_outerGain * x;
+ }
+
+ return gain;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/Cone.h b/WebCore/platform/audio/Cone.h
new file mode 100644
index 0000000..9936f28
--- /dev/null
+++ b/WebCore/platform/audio/Cone.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Cone_h
+#define Cone_h
+
+#include <wtf/Vector3.h>
+
+namespace WebCore {
+
+// Cone gain is defined according to the OpenAL specification
+
+class ConeEffect {
+public:
+ ConeEffect();
+
+ // Returns scalar gain for the given source/listener positions/orientations
+ double gain(Vector3 sourcePosition, Vector3 sourceOrientation, Vector3 listenerPosition);
+
+ // Angles in degrees
+ void setInnerAngle(double innerAngle) { m_innerAngle = innerAngle; }
+ double innerAngle() const { return m_innerAngle; }
+
+ void setOuterAngle(double outerAngle) { m_outerAngle = outerAngle; }
+ double outerAngle() const { return m_outerAngle; }
+
+ void setOuterGain(double outerGain) { m_outerGain = outerGain; }
+ double outerGain() const { return m_outerGain; }
+
+protected:
+ double m_innerAngle;
+ double m_outerAngle;
+ double m_outerGain;
+};
+
+} // namespace WebCore
+
+#endif // Cone_h
diff --git a/WebCore/platform/audio/FFTConvolver.cpp b/WebCore/platform/audio/FFTConvolver.cpp
new file mode 100644
index 0000000..b0211fd
--- /dev/null
+++ b/WebCore/platform/audio/FFTConvolver.cpp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "FFTConvolver.h"
+
+#include "Accelerate.h"
+
+namespace WebCore {
+
+FFTConvolver::FFTConvolver(size_t fftSize)
+ : m_frame(fftSize)
+ , m_readWriteIndex(0)
+ , m_inputBuffer(fftSize) // 2nd half of buffer is always zeroed
+ , m_outputBuffer(fftSize)
+ , m_lastOverlapBuffer(fftSize / 2)
+{
+}
+
+void FFTConvolver::process(FFTFrame* fftKernel, float* sourceP, float* destP, size_t framesToProcess)
+{
+ // FIXME: make so framesToProcess is not required to fit evenly into fftSize/2
+
+ // Copy samples to input buffer (note contraint above!)
+ float* inputP = m_inputBuffer.data();
+
+ // Sanity check
+ bool isCopyGood1 = sourceP && inputP && m_readWriteIndex + framesToProcess <= m_inputBuffer.size();
+ ASSERT(isCopyGood1);
+ if (!isCopyGood1)
+ return;
+
+ memcpy(inputP + m_readWriteIndex, sourceP, sizeof(float) * framesToProcess);
+
+ // Copy samples from output buffer
+ float* outputP = m_outputBuffer.data();
+
+ // Sanity check
+ bool isCopyGood2 = destP && outputP && m_readWriteIndex + framesToProcess <= m_outputBuffer.size();
+ ASSERT(isCopyGood2);
+ if (!isCopyGood2)
+ return;
+
+ memcpy(destP, outputP + m_readWriteIndex, sizeof(float) * framesToProcess);
+ m_readWriteIndex += framesToProcess;
+
+
+ // Check if it's time to perform the next FFT
+ size_t halfSize = fftSize() / 2;
+ if (m_readWriteIndex == halfSize) {
+ // The input buffer is now filled (get frequency-domain version)
+ m_frame.doFFT(m_inputBuffer.data());
+ m_frame.multiply(*fftKernel);
+ m_frame.doInverseFFT(m_outputBuffer.data());
+
+ // Overlap-add 1st half from previous time
+ vadd(m_outputBuffer.data(), 1, m_lastOverlapBuffer.data(), 1, m_outputBuffer.data(), 1, halfSize);
+
+ // Finally, save 2nd half of result
+ bool isCopyGood3 = m_outputBuffer.size() == 2 * halfSize && m_lastOverlapBuffer.size() == halfSize;
+ ASSERT(isCopyGood3);
+ if (!isCopyGood3)
+ return;
+
+ memcpy(m_lastOverlapBuffer.data(), m_outputBuffer.data() + halfSize, sizeof(float) * halfSize);
+
+ // Reset index back to start for next time
+ m_readWriteIndex = 0;
+ }
+}
+
+void FFTConvolver::reset()
+{
+ m_lastOverlapBuffer.zero();
+ m_readWriteIndex = 0;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/FFTConvolver.h b/WebCore/platform/audio/FFTConvolver.h
new file mode 100644
index 0000000..0eec7c1
--- /dev/null
+++ b/WebCore/platform/audio/FFTConvolver.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef FFTConvolver_h
+#define FFTConvolver_h
+
+#include "AudioFloatArray.h"
+#include "FFTFrame.h"
+
+namespace WebCore {
+
+class FFTConvolver {
+public:
+ // fftSize must be a power of two
+ FFTConvolver(size_t fftSize);
+
+ // For now, with multiple calls to Process(), framesToProcess MUST add up EXACTLY to fftSize / 2
+ //
+ // FIXME: Later, we can do more sophisticated buffering to relax this requirement...
+ //
+ // The input to output latency is equal to fftSize / 2
+ //
+ // Processing in-place is allowed...
+ void process(FFTFrame* fftKernel, float* sourceP, float* destP, size_t framesToProcess);
+
+ void reset();
+
+ size_t fftSize() const { return m_frame.fftSize(); }
+
+private:
+ FFTFrame m_frame;
+
+ // Buffer input until we get fftSize / 2 samples then do an FFT
+ size_t m_readWriteIndex;
+ AudioFloatArray m_inputBuffer;
+
+ // Stores output which we read a little at a time
+ AudioFloatArray m_outputBuffer;
+
+ // Saves the 2nd half of the FFT buffer, so we can do an overlap-add with the 1st half of the next one
+ AudioFloatArray m_lastOverlapBuffer;
+};
+
+} // namespace WebCore
+
+#endif // FFTConvolver_h
diff --git a/WebCore/platform/audio/Reverb.cpp b/WebCore/platform/audio/Reverb.cpp
new file mode 100644
index 0000000..886a553
--- /dev/null
+++ b/WebCore/platform/audio/Reverb.cpp
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "Reverb.h"
+
+#include "AudioBus.h"
+#include "AudioFileReader.h"
+#include "ReverbConvolver.h"
+#include <math.h>
+#include <wtf/MathExtras.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+#if OS(DARWIN)
+using namespace std;
+#endif
+
+namespace WebCore {
+
+// Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal
+const double GainCalibration = -58.0;
+
+// A minimum power value to when normalizing a silent (or very quiet) impulse response
+const double MinPower = 0.000125;
+
+static double calculateNormalizationScale(AudioBus* response)
+{
+ // Normalize by RMS power
+ size_t numberOfChannels = response->numberOfChannels();
+ size_t frameSize = response->frameSize();
+
+ double power = 0.0;
+
+ for (size_t i = 0; i < numberOfChannels; ++i) {
+ int n = frameSize;
+ float* p = response->channel(i)->data();
+
+ while (n--) {
+ float sample = *p++;
+ power += sample * sample;
+ }
+ }
+
+ power = sqrt(power / (numberOfChannels * frameSize));
+
+ // Protect against accidental overload
+ if (isinf(power) || isnan(power) || power < MinPower)
+ power = MinPower;
+
+ double scale = 1.0 / power;
+
+ scale *= pow(10.0, GainCalibration * 0.05); // calibrate to make perceived volume same as unprocessed
+
+ // True-stereo compensation
+ if (response->numberOfChannels() == 4)
+ scale *= 0.5;
+
+ return scale;
+}
+
+Reverb::Reverb(AudioBus* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
+{
+ double scale = calculateNormalizationScale(impulseResponse);
+ if (scale)
+ impulseResponse->scale(scale);
+
+ initialize(impulseResponse, renderSliceSize, maxFFTSize, numberOfChannels, useBackgroundThreads);
+
+ // Undo scaling since this shouldn't be a destructive operation on impulseResponse
+ if (scale)
+ impulseResponse->scale(1.0 / scale);
+}
+
+void Reverb::initialize(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
+{
+ m_impulseResponseLength = impulseResponseBuffer->frameSize();
+
+ // The reverb can handle a mono impulse response and still do stereo processing
+ size_t numResponseChannels = impulseResponseBuffer->numberOfChannels();
+ m_convolvers.reserveCapacity(numberOfChannels);
+
+ int convolverRenderPhase = 0;
+ for (size_t i = 0; i < numResponseChannels; ++i) {
+ AudioChannel* channel = impulseResponseBuffer->channel(i);
+
+ ReverbConvolver* convolver = new ReverbConvolver(channel, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads);
+ m_convolvers.append(convolver);
+
+ convolverRenderPhase += renderSliceSize;
+ }
+
+ // For "True" stereo processing we allocate a temporary buffer to avoid repeatedly allocating it in the process() method.
+ // It can be bad to allocate memory in a real-time thread.
+ if (numResponseChannels == 4)
+ m_tempBuffer = new AudioBus(2, MaxFrameSize);
+}
+
+void Reverb::process(AudioBus* sourceBus, AudioBus* destinationBus, size_t framesToProcess)
+{
+ // Do a fairly comprehensive sanity check.
+ // If these conditions are satisfied, all of the source and destination pointers will be valid for the various matrixing cases.
+ bool isSafeToProcess = sourceBus && destinationBus && sourceBus->numberOfChannels() > 0 && destinationBus->numberOfChannels() > 0
+ && framesToProcess <= MaxFrameSize && framesToProcess <= sourceBus->frameSize() && framesToProcess <= destinationBus->frameSize();
+
+ ASSERT(isSafeToProcess);
+ if (!isSafeToProcess)
+ return;
+
+ // For now only handle mono or stereo output
+ if (destinationBus->numberOfChannels() > 2) {
+ destinationBus->zero();
+ return;
+ }
+
+ AudioChannel* destinationChannelL = destinationBus->channel(0);
+ AudioChannel* sourceChannelL = sourceBus->channel(0);
+
+ // Handle input -> output matrixing...
+ size_t numInputChannels = sourceBus->numberOfChannels();
+ size_t numOutputChannels = destinationBus->numberOfChannels();
+ size_t numReverbChannels = m_convolvers.size();
+
+ if (numInputChannels == 2 && numReverbChannels == 2 && numOutputChannels == 2) {
+ // 2 -> 2 -> 2
+ AudioChannel* sourceChannelR = sourceBus->channel(1);
+ AudioChannel* destinationChannelR = destinationBus->channel(1);
+ m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
+ m_convolvers[1]->process(sourceChannelR, destinationChannelR, framesToProcess);
+ } else if (numInputChannels == 1 && numOutputChannels == 2 && numReverbChannels == 2) {
+ // 1 -> 2 -> 2
+ for (int i = 0; i < 2; ++i) {
+ AudioChannel* destinationChannel = destinationBus->channel(i);
+ m_convolvers[i]->process(sourceChannelL, destinationChannel, framesToProcess);
+ }
+ } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 2) {
+ // 1 -> 1 -> 2
+ m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
+
+ // simply copy L -> R
+ AudioChannel* destinationChannelR = destinationBus->channel(1);
+ bool isCopySafe = destinationChannelL->data() && destinationChannelR->data() && destinationChannelL->frameSize() >= framesToProcess && destinationChannelR->frameSize() >= framesToProcess;
+ ASSERT(isCopySafe);
+ if (!isCopySafe)
+ return;
+ memcpy(destinationChannelR->data(), destinationChannelL->data(), sizeof(float) * framesToProcess);
+ } else if (numInputChannels == 1 && numReverbChannels == 1 && numOutputChannels == 1) {
+ // 1 -> 1 -> 1
+ m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
+ } else if (numInputChannels == 2 && numReverbChannels == 4 && numOutputChannels == 2) {
+ // 2 -> 4 -> 2 ("True" stereo)
+ AudioChannel* sourceChannelR = sourceBus->channel(1);
+ AudioChannel* destinationChannelR = destinationBus->channel(1);
+
+ AudioChannel* tempChannelL = m_tempBuffer->channel(0);
+ AudioChannel* tempChannelR = m_tempBuffer->channel(1);
+
+ // Process left virtual source
+ m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
+ m_convolvers[1]->process(sourceChannelL, destinationChannelR, framesToProcess);
+
+ // Process right virtual source
+ m_convolvers[2]->process(sourceChannelR, tempChannelL, framesToProcess);
+ m_convolvers[3]->process(sourceChannelR, tempChannelR, framesToProcess);
+
+ destinationBus->sumFrom(*m_tempBuffer);
+ } else if (numInputChannels == 1 && numReverbChannels == 4 && numOutputChannels == 2) {
+ // 1 -> 4 -> 2 (Processing mono with "True" stereo impulse response)
+ // This is an inefficient use of a four-channel impulse response, but we should handle the case.
+ AudioChannel* destinationChannelR = destinationBus->channel(1);
+
+ AudioChannel* tempChannelL = m_tempBuffer->channel(0);
+ AudioChannel* tempChannelR = m_tempBuffer->channel(1);
+
+ // Process left virtual source
+ m_convolvers[0]->process(sourceChannelL, destinationChannelL, framesToProcess);
+ m_convolvers[1]->process(sourceChannelL, destinationChannelR, framesToProcess);
+
+ // Process right virtual source
+ m_convolvers[2]->process(sourceChannelL, tempChannelL, framesToProcess);
+ m_convolvers[3]->process(sourceChannelL, tempChannelR, framesToProcess);
+
+ destinationBus->sumFrom(*m_tempBuffer);
+ } else {
+ // Handle gracefully any unexpected / unsupported matrixing
+ // FIXME: add code for 5.1 support...
+ destinationBus->zero();
+ }
+}
+
+void Reverb::reset()
+{
+ for (size_t i = 0; i < m_convolvers.size(); ++i)
+ m_convolvers[i]->reset();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/Reverb.h b/WebCore/platform/audio/Reverb.h
new file mode 100644
index 0000000..26f5f8e
--- /dev/null
+++ b/WebCore/platform/audio/Reverb.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef Reverb_h
+#define Reverb_h
+
+#include "ReverbConvolver.h"
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioBus;
+
+// Multi-channel convolution reverb with channel matrixing - one or more ReverbConvolver objects are used internally.
+
+class Reverb {
+public:
+ enum { MaxFrameSize = 256 };
+
+ // renderSliceSize is a rendering hint, so the FFTs can be optimized to not all occur at the same time (very bad when rendering on a real-time thread).
+ Reverb(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads);
+
+ void process(AudioBus* sourceBus, AudioBus* destinationBus, size_t framesToProcess);
+ void reset();
+
+ unsigned impulseResponseLength() const { return m_impulseResponseLength; }
+
+private:
+ void initialize(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads);
+
+ size_t m_impulseResponseLength;
+
+ Vector<OwnPtr<ReverbConvolver> > m_convolvers;
+
+ // For "True" stereo processing
+ OwnPtr<AudioBus> m_tempBuffer;
+};
+
+} // namespace WebCore
+
+#endif // Reverb_h
diff --git a/WebCore/platform/audio/ReverbAccumulationBuffer.cpp b/WebCore/platform/audio/ReverbAccumulationBuffer.cpp
new file mode 100644
index 0000000..7b1c63b
--- /dev/null
+++ b/WebCore/platform/audio/ReverbAccumulationBuffer.cpp
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "ReverbAccumulationBuffer.h"
+
+#include "Accelerate.h"
+
+namespace WebCore {
+
+ReverbAccumulationBuffer::ReverbAccumulationBuffer(size_t length)
+ : m_buffer(length)
+ , m_readIndex(0)
+ , m_readTimeFrame(0)
+{
+}
+
+void ReverbAccumulationBuffer::readAndClear(float* destination, size_t numberOfFrames)
+{
+ size_t bufferLength = m_buffer.size();
+ bool isCopySafe = m_readIndex <= bufferLength && numberOfFrames <= bufferLength;
+
+ ASSERT(isCopySafe);
+ if (!isCopySafe)
+ return;
+
+ size_t framesAvailable = bufferLength - m_readIndex;
+ size_t numberOfFrames1 = std::min(numberOfFrames, framesAvailable);
+ size_t numberOfFrames2 = numberOfFrames - numberOfFrames1;
+
+ float* source = m_buffer.data();
+ memcpy(destination, source + m_readIndex, sizeof(float) * numberOfFrames1);
+ memset(source + m_readIndex, 0, sizeof(float) * numberOfFrames1);
+
+ // Handle wrap-around if necessary
+ if (numberOfFrames2 > 0) {
+ memcpy(destination + numberOfFrames1, source, sizeof(float) * numberOfFrames2);
+ memset(source, 0, sizeof(float) * numberOfFrames2);
+ }
+
+ m_readIndex = (m_readIndex + numberOfFrames) % bufferLength;
+ m_readTimeFrame += numberOfFrames;
+}
+
+void ReverbAccumulationBuffer::updateReadIndex(int* readIndex, size_t numberOfFrames) const
+{
+ // Update caller's readIndex
+ *readIndex = (*readIndex + numberOfFrames) % m_buffer.size();
+}
+
+int ReverbAccumulationBuffer::accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames)
+{
+ size_t bufferLength = m_buffer.size();
+
+ size_t writeIndex = (*readIndex + delayFrames) % bufferLength;
+
+ // Update caller's readIndex
+ *readIndex = (*readIndex + numberOfFrames) % bufferLength;
+
+ size_t framesAvailable = bufferLength - writeIndex;
+ size_t numberOfFrames1 = std::min(numberOfFrames, framesAvailable);
+ size_t numberOfFrames2 = numberOfFrames - numberOfFrames1;
+
+ float* destination = m_buffer.data();
+
+ bool isSafe = writeIndex <= bufferLength && numberOfFrames1 + writeIndex <= bufferLength && numberOfFrames2 <= bufferLength;
+ ASSERT(isSafe);
+ if (!isSafe)
+ return 0;
+
+ vadd(source, 1, destination + writeIndex, 1, destination + writeIndex, 1, numberOfFrames1);
+
+ // Handle wrap-around if necessary
+ if (numberOfFrames2 > 0)
+ vadd(source + numberOfFrames1, 1, destination, 1, destination, 1, numberOfFrames2);
+
+ return writeIndex;
+}
+
+void ReverbAccumulationBuffer::reset()
+{
+ m_buffer.zero();
+ m_readIndex = 0;
+ m_readTimeFrame = 0;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/ReverbAccumulationBuffer.h b/WebCore/platform/audio/ReverbAccumulationBuffer.h
new file mode 100644
index 0000000..44a0773
--- /dev/null
+++ b/WebCore/platform/audio/ReverbAccumulationBuffer.h
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ReverbAccumulationBuffer_h
+#define ReverbAccumulationBuffer_h
+
+#include "AudioFloatArray.h"
+
+namespace WebCore {
+
+// ReverbAccumulationBuffer is a circular delay buffer with one client reading from it and multiple clients
+// writing/accumulating to it at different delay offsets from the read position. The read operation will zero the memory
+// just read from the buffer, so it will be ready for accumulation the next time around.
+class ReverbAccumulationBuffer {
+public:
+ ReverbAccumulationBuffer(size_t length);
+
+ // This will read from, then clear-out numberOfFrames
+ void readAndClear(float* destination, size_t numberOfFrames);
+
+ // Each ReverbConvolverStage will accumulate its output at the appropriate delay from the read position.
+ // We need to pass in and update readIndex here, since each ReverbConvolverStage may be running in
+ // a different thread than the realtime thread calling ReadAndClear() and maintaining m_readIndex
+ // Returns the writeIndex where the accumulation took place
+ int accumulate(float* source, size_t numberOfFrames, int* readIndex, size_t delayFrames);
+
+ size_t readIndex() const { return m_readIndex; }
+ void updateReadIndex(int* readIndex, size_t numberOfFrames) const;
+
+ size_t readTimeFrame() const { return m_readTimeFrame; }
+
+ void reset();
+
+private:
+ AudioFloatArray m_buffer;
+ size_t m_readIndex;
+ size_t m_readTimeFrame; // for debugging (frame on continuous timeline)
+};
+
+} // namespace WebCore
+
+#endif // ReverbAccumulationBuffer_h
diff --git a/WebCore/platform/audio/ReverbConvolver.cpp b/WebCore/platform/audio/ReverbConvolver.cpp
new file mode 100644
index 0000000..719e586
--- /dev/null
+++ b/WebCore/platform/audio/ReverbConvolver.cpp
@@ -0,0 +1,228 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "ReverbConvolver.h"
+
+#include "Accelerate.h"
+#include "AudioBus.h"
+
+namespace WebCore {
+
+const int InputBufferSize = 8 * 16384;
+
+// We only process the leading portion of the impulse response in the real-time thread. We don't exceed this length.
+// It turns out then, that the background thread has about 278msec of scheduling slop.
+// Empirically, this has been found to be a good compromise between giving enough time for scheduling slop,
+// while still minimizing the amount of processing done in the primary (high-priority) thread.
+// This was found to be a good value on Mac OS X, and may work well on other platforms as well, assuming
+// the very rough scheduling latencies are similar on these time-scales. Of course, this code may need to be
+// tuned for individual platforms if this assumption is found to be incorrect.
+const size_t RealtimeFrameLimit = 8192 + 4096; // ~278msec @ 44.1KHz
+
+const size_t MinFFTSize = 256;
+const size_t MaxRealtimeFFTSize = 2048;
+
+static void* backgroundThreadEntry(void* threadData)
+{
+ ReverbConvolver* reverbConvolver = static_cast<ReverbConvolver*>(threadData);
+ reverbConvolver->backgroundThreadEntry();
+ return 0;
+}
+
+ReverbConvolver::ReverbConvolver(AudioChannel* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads)
+ : m_impulseResponseLength(impulseResponse->frameSize())
+ , m_accumulationBuffer(impulseResponse->frameSize() + renderSliceSize)
+ , m_inputBuffer(InputBufferSize)
+ , m_renderSliceSize(renderSliceSize)
+ , m_minFFTSize(MinFFTSize) // First stage will have this size - successive stages will double in size each time
+ , m_maxFFTSize(maxFFTSize) // until we hit m_maxFFTSize
+ , m_useBackgroundThreads(useBackgroundThreads)
+ , m_backgroundThread(0)
+ , m_wantsToExit(false)
+ , m_moreInputBuffered(false)
+{
+ // If we are using background threads then don't exceed this FFT size for the
+ // stages which run in the real-time thread. This avoids having only one or two
+ // large stages (size 16384 or so) at the end which take a lot of time every several
+ // processing slices. This way we amortize the cost over more processing slices.
+ m_maxRealtimeFFTSize = MaxRealtimeFFTSize;
+
+ // For the moment, a good way to know if we have real-time constraint is to check if we're using background threads.
+ // Otherwise, assume we're being run from a command-line tool.
+ bool hasRealtimeConstraint = useBackgroundThreads;
+
+ float* response = impulseResponse->data();
+ size_t totalResponseLength = impulseResponse->frameSize();
+
+ // Because we're not using direct-convolution in the leading portion, the reverb has an overall latency of half the first-stage FFT size
+ size_t reverbTotalLatency = m_minFFTSize / 2;
+
+ size_t stageOffset = 0;
+ int i = 0;
+ size_t fftSize = m_minFFTSize;
+ while (stageOffset < totalResponseLength) {
+ size_t stageSize = fftSize / 2;
+
+ // For the last stage, it's possible that stageOffset is such that we're straddling the end
+ // of the impulse response buffer (if we use stageSize), so reduce the last stage's length...
+ if (stageSize + stageOffset > totalResponseLength)
+ stageSize = totalResponseLength - stageOffset;
+
+ // This "staggers" the time when each FFT happens so they don't all happen at the same time
+ int renderPhase = convolverRenderPhase + i * renderSliceSize;
+
+ OwnPtr<ReverbConvolverStage> stage(new ReverbConvolverStage(response, totalResponseLength, reverbTotalLatency, stageOffset, stageSize, fftSize, renderPhase, renderSliceSize, &m_accumulationBuffer));
+
+ bool isBackgroundStage = false;
+
+ if (this->useBackgroundThreads() && stageOffset > RealtimeFrameLimit) {
+ m_backgroundStages.append(stage.release());
+ isBackgroundStage = true;
+ } else
+ m_stages.append(stage.release());
+
+ stageOffset += stageSize;
+ ++i;
+
+ // Figure out next FFT size
+ fftSize *= 2;
+ if (hasRealtimeConstraint && !isBackgroundStage && fftSize > m_maxRealtimeFFTSize)
+ fftSize = m_maxRealtimeFFTSize;
+ if (fftSize > m_maxFFTSize)
+ fftSize = m_maxFFTSize;
+ }
+
+ // Start up background thread
+ // FIXME: would be better to up the thread priority here. It doesn't need to be real-time, but higher than the default...
+ if (this->useBackgroundThreads() && m_backgroundStages.size() > 0)
+ m_backgroundThread = createThread(WebCore::backgroundThreadEntry, this, "convolution background thread");
+}
+
+ReverbConvolver::~ReverbConvolver()
+{
+ // Wait for background thread to stop
+ if (useBackgroundThreads() && m_backgroundThread) {
+ m_wantsToExit = true;
+
+ // Wake up thread so it can return
+ {
+ MutexLocker locker(m_backgroundThreadLock);
+ m_moreInputBuffered = true;
+ m_backgroundThreadCondition.signal();
+ }
+
+ waitForThreadCompletion(m_backgroundThread, 0);
+ }
+}
+
+void ReverbConvolver::backgroundThreadEntry()
+{
+ while (!m_wantsToExit) {
+ // Wait for realtime thread to give us more input
+ m_moreInputBuffered = false;
+ {
+ MutexLocker locker(m_backgroundThreadLock);
+ while (!m_moreInputBuffered && !m_wantsToExit)
+ m_backgroundThreadCondition.wait(m_backgroundThreadLock);
+ }
+
+ // Process all of the stages until their read indices reach the input buffer's write index
+ int writeIndex = m_inputBuffer.writeIndex();
+
+ // Even though it doesn't seem like every stage needs to maintain its own version of readIndex
+ // we do this in case we want to run in more than one background thread.
+ int readIndex;
+
+ while ((readIndex = m_backgroundStages[0]->inputReadIndex()) != writeIndex) { // FIXME: do better to detect buffer overrun...
+ // The ReverbConvolverStages need to process in amounts which evenly divide half the FFT size
+ const int SliceSize = MinFFTSize / 2;
+
+ // Accumulate contributions from each stage
+ for (size_t i = 0; i < m_backgroundStages.size(); ++i)
+ m_backgroundStages[i]->processInBackground(this, SliceSize);
+ }
+ }
+}
+
+void ReverbConvolver::process(AudioChannel* sourceChannel, AudioChannel* destinationChannel, size_t framesToProcess)
+{
+ bool isSafe = sourceChannel && destinationChannel && sourceChannel->frameSize() >= framesToProcess && destinationChannel->frameSize() >= framesToProcess;
+ ASSERT(isSafe);
+ if (!isSafe)
+ return;
+
+ float* source = sourceChannel->data();
+ float* destination = destinationChannel->data();
+ bool isDataSafe = source && destination;
+ ASSERT(isDataSafe);
+ if (!isDataSafe)
+ return;
+
+ // Feed input buffer (read by all threads)
+ m_inputBuffer.write(source, framesToProcess);
+
+ // Accumulate contributions from each stage
+ for (size_t i = 0; i < m_stages.size(); ++i)
+ m_stages[i]->process(source, framesToProcess);
+
+ // Finally read from accumulation buffer
+ m_accumulationBuffer.readAndClear(destination, framesToProcess);
+
+ // Now that we've buffered more input, wake up our background thread.
+
+ // Not using a MutexLocker looks strange, but we use a tryLock() instead because this is run on the real-time
+ // thread where it is a disaster for the lock to be contended (causes audio glitching). It's OK if we fail to
+ // signal from time to time, since we'll get to it the next time we're called. We're called repeatedly
+ // and frequently (around every 3ms). The background thread is processing well into the future and has a considerable amount of
+ // leeway here...
+ if (m_backgroundThreadLock.tryLock()) {
+ m_moreInputBuffered = true;
+ m_backgroundThreadCondition.signal();
+ m_backgroundThreadLock.unlock();
+ }
+}
+
+void ReverbConvolver::reset()
+{
+ for (size_t i = 0; i < m_stages.size(); ++i)
+ m_stages[i]->reset();
+
+ for (size_t i = 0; i < m_backgroundStages.size(); ++i)
+ m_backgroundStages[i]->reset();
+
+ m_accumulationBuffer.reset();
+ m_inputBuffer.reset();
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/ReverbConvolver.h b/WebCore/platform/audio/ReverbConvolver.h
new file mode 100644
index 0000000..34f77d3
--- /dev/null
+++ b/WebCore/platform/audio/ReverbConvolver.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ReverbConvolver_h
+#define ReverbConvolver_h
+
+#include "AudioFloatArray.h"
+#include "FFTConvolver.h"
+#include "ReverbAccumulationBuffer.h"
+#include "ReverbConvolverStage.h"
+#include "ReverbInputBuffer.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+namespace WebCore {
+
+class AudioChannel;
+
+class ReverbConvolver {
+public:
+ // maxFFTSize can be adjusted (from say 2048 to 32768) depending on how much precision is necessary.
+ // For certain tweaky de-convolving applications the phase errors add up quickly and lead to non-sensical results with
+ // larger FFT sizes and single-precision floats. In these cases 2048 is a good size.
+ // If not doing multi-threaded convolution, then should not go > 8192.
+ ReverbConvolver(AudioChannel* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads);
+ ~ReverbConvolver();
+
+ void process(AudioChannel* sourceChannel, AudioChannel* destinationChannel, size_t framesToProcess);
+ void reset();
+
+ size_t impulseResponseLength() const { return m_impulseResponseLength; }
+
+ ReverbInputBuffer* inputBuffer() { return &m_inputBuffer; }
+
+ bool useBackgroundThreads() const { return m_useBackgroundThreads; }
+ void backgroundThreadEntry();
+
+private:
+ Vector<OwnPtr<ReverbConvolverStage> > m_stages;
+ Vector<OwnPtr<ReverbConvolverStage> > m_backgroundStages;
+ size_t m_impulseResponseLength;
+
+ ReverbAccumulationBuffer m_accumulationBuffer;
+
+ // One or more background threads read from this input buffer which is fed from the realtime thread.
+ ReverbInputBuffer m_inputBuffer;
+
+ // We're given a rendering hint, so the FFTs can be optimized to not all occur at the same time
+ // (very bad when rendering on a real-time thread).
+ size_t m_renderSliceSize;
+
+ // First stage will be of size m_minFFTSize. Each next stage will be twice as big until we hit m_maxFFTSize.
+ size_t m_minFFTSize;
+ size_t m_maxFFTSize;
+
+ // But don't exceed this size in the real-time thread (if we're doing background processing).
+ size_t m_maxRealtimeFFTSize;
+
+ // Background thread and synchronization
+ bool m_useBackgroundThreads;
+ ThreadIdentifier m_backgroundThread;
+ bool m_wantsToExit;
+ bool m_moreInputBuffered;
+ mutable Mutex m_backgroundThreadLock;
+ mutable ThreadCondition m_backgroundThreadCondition;
+};
+
+} // namespace WebCore
+
+#endif // ReverbConvolver_h
diff --git a/WebCore/platform/audio/ReverbConvolverStage.cpp b/WebCore/platform/audio/ReverbConvolverStage.cpp
new file mode 100644
index 0000000..8606502
--- /dev/null
+++ b/WebCore/platform/audio/ReverbConvolverStage.cpp
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "ReverbConvolverStage.h"
+
+#include "Accelerate.h"
+#include "ReverbAccumulationBuffer.h"
+#include "ReverbConvolver.h"
+#include "ReverbInputBuffer.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+ReverbConvolverStage::ReverbConvolverStage(float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength,
+ size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer* accumulationBuffer)
+ : m_fftKernel(fftSize)
+ , m_accumulationBuffer(accumulationBuffer)
+ , m_accumulationReadIndex(0)
+ , m_inputReadIndex(0)
+ , m_impulseResponseLength(responseLength)
+{
+ ASSERT(impulseResponse);
+ ASSERT(accumulationBuffer);
+
+ m_fftKernel.doPaddedFFT(impulseResponse + stageOffset, stageLength);
+ m_convolver = new FFTConvolver(fftSize);
+ m_temporaryBuffer.allocate(renderSliceSize);
+
+ // The convolution stage at offset stageOffset needs to have a corresponding delay to cancel out the offset.
+ size_t totalDelay = stageOffset + reverbTotalLatency;
+
+ // But, the FFT convolution itself incurs fftSize / 2 latency, so subtract this out...
+ size_t halfSize = fftSize / 2;
+ ASSERT(totalDelay >= halfSize);
+ if (totalDelay >= halfSize)
+ totalDelay -= halfSize;
+
+ // We divide up the total delay, into pre and post delay sections so that we can schedule at exactly the moment when the FFT will happen.
+ // This is coordinated with the other stages, so they don't all do their FFTs at the same time...
+ int maxPreDelayLength = std::min(halfSize, totalDelay);
+ m_preDelayLength = totalDelay > 0 ? renderPhase % maxPreDelayLength : 0;
+ if (m_preDelayLength > totalDelay)
+ m_preDelayLength = 0;
+
+ m_postDelayLength = totalDelay - m_preDelayLength;
+ m_preReadWriteIndex = 0;
+ m_framesProcessed = 0; // total frames processed so far
+
+ m_preDelayBuffer.allocate(m_preDelayLength < fftSize ? fftSize : m_preDelayLength);
+}
+
+void ReverbConvolverStage::processInBackground(ReverbConvolver* convolver, size_t framesToProcess)
+{
+ ReverbInputBuffer* inputBuffer = convolver->inputBuffer();
+ float* source = inputBuffer->directReadFrom(&m_inputReadIndex, framesToProcess);
+ process(source, framesToProcess);
+}
+
+void ReverbConvolverStage::process(float* source, size_t framesToProcess)
+{
+ ASSERT(source);
+ if (!source)
+ return;
+
+ // Deal with pre-delay stream : note special handling of zero delay.
+
+ float* preDelayedSource;
+ float* temporaryBuffer;
+ bool isTemporaryBufferSafe = false;
+ if (m_preDelayLength > 0) {
+ // Handles both the read case (call to process() ) and the write case (memcpy() )
+ bool isPreDelaySafe = m_preReadWriteIndex + framesToProcess <= m_preDelayBuffer.size();
+ ASSERT(isPreDelaySafe);
+ if (!isPreDelaySafe)
+ return;
+
+ isTemporaryBufferSafe = framesToProcess <= m_temporaryBuffer.size();
+
+ preDelayedSource = m_preDelayBuffer.data() + m_preReadWriteIndex;
+ temporaryBuffer = m_temporaryBuffer.data();
+ } else {
+ // Zero delay
+ preDelayedSource = source;
+ temporaryBuffer = m_preDelayBuffer.data();
+
+ isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.size();
+ }
+
+ ASSERT(isTemporaryBufferSafe);
+ if (!isTemporaryBufferSafe)
+ return;
+
+ int writeIndex = 0;
+
+ if (m_framesProcessed < m_preDelayLength) {
+ // For the first m_preDelayLength frames don't process the convolver, instead simply buffer in the pre-delay.
+ // But while buffering the pre-delay, we still need to update our index.
+ m_accumulationBuffer->updateReadIndex(&m_accumulationReadIndex, framesToProcess);
+ } else {
+ // Now, run the convolution (into the delay buffer).
+ // An expensive FFT will happen every fftSize / 2 frames.
+ // We process in-place here...
+ m_convolver->process(&m_fftKernel, preDelayedSource, temporaryBuffer, framesToProcess);
+
+ // Now accumulate into reverb's accumulation buffer.
+ writeIndex = m_accumulationBuffer->accumulate(temporaryBuffer, framesToProcess, &m_accumulationReadIndex, m_postDelayLength);
+ }
+
+ // Finally copy input to pre-delay.
+ if (m_preDelayLength > 0) {
+ memcpy(preDelayedSource, source, sizeof(float) * framesToProcess);
+ m_preReadWriteIndex += framesToProcess;
+
+ ASSERT(m_preReadWriteIndex <= m_preDelayLength);
+ if (m_preReadWriteIndex >= m_preDelayLength)
+ m_preReadWriteIndex = 0;
+ }
+
+ m_framesProcessed += framesToProcess;
+}
+
+void ReverbConvolverStage::reset()
+{
+ m_convolver->reset();
+ m_preDelayBuffer.zero();
+ m_accumulationReadIndex = 0;
+ m_inputReadIndex = 0;
+ m_framesProcessed = 0;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/ReverbConvolverStage.h b/WebCore/platform/audio/ReverbConvolverStage.h
new file mode 100644
index 0000000..88351af
--- /dev/null
+++ b/WebCore/platform/audio/ReverbConvolverStage.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ReverbConvolverStage_h
+#define ReverbConvolverStage_h
+
+#include "AudioFloatArray.h"
+#include "FFTFrame.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class ReverbAccumulationBuffer;
+class ReverbConvolver;
+class FFTConvolver;
+
+// A ReverbConvolverStage represents the convolution associated with a sub-section of a large impulse response.
+// It incorporates a delay line to account for the offset of the sub-section within the larger impulse response.
+class ReverbConvolverStage {
+public:
+ // renderPhase is useful to know so that we can manipulate the pre versus post delay so that stages will perform
+ // their heavy work (FFT processing) on different slices to balance the load in a real-time thread.
+ ReverbConvolverStage(float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength,
+ size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer* accumulationBuffer);
+
+ // WARNING: framesToProcess must be such that it evenly divides the delay buffer size (stage_offset).
+ void process(float* source, size_t framesToProcess);
+
+ void processInBackground(ReverbConvolver* convolver, size_t framesToProcess);
+
+ void reset();
+
+ // Useful for background processing
+ int inputReadIndex() const { return m_inputReadIndex; }
+
+private:
+ FFTFrame m_fftKernel;
+ OwnPtr<FFTConvolver> m_convolver;
+
+ AudioFloatArray m_preDelayBuffer;
+
+ ReverbAccumulationBuffer* m_accumulationBuffer;
+ int m_accumulationReadIndex;
+ int m_inputReadIndex;
+
+ size_t m_preDelayLength;
+ size_t m_postDelayLength;
+ size_t m_preReadWriteIndex;
+ size_t m_framesProcessed;
+
+ AudioFloatArray m_temporaryBuffer;
+
+ size_t m_impulseResponseLength;
+};
+
+} // namespace WebCore
+
+#endif // ReverbConvolverStage_h
diff --git a/WebCore/platform/audio/ReverbInputBuffer.cpp b/WebCore/platform/audio/ReverbInputBuffer.cpp
new file mode 100644
index 0000000..f270f6f
--- /dev/null
+++ b/WebCore/platform/audio/ReverbInputBuffer.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "ReverbInputBuffer.h"
+
+namespace WebCore {
+
+ReverbInputBuffer::ReverbInputBuffer(size_t length)
+ : m_buffer(length)
+ , m_writeIndex(0)
+{
+}
+
+void ReverbInputBuffer::write(float* sourceP, size_t numberOfFrames)
+{
+ size_t bufferLength = m_buffer.size();
+ bool isCopySafe = m_writeIndex + numberOfFrames <= bufferLength;
+ ASSERT(isCopySafe);
+ if (!isCopySafe)
+ return;
+
+ memcpy(m_buffer.data() + m_writeIndex, sourceP, sizeof(float) * numberOfFrames);
+
+ m_writeIndex += numberOfFrames;
+ ASSERT(m_writeIndex <= bufferLength);
+
+ if (m_writeIndex >= bufferLength)
+ m_writeIndex = 0;
+}
+
+float* ReverbInputBuffer::directReadFrom(int* readIndex, size_t numberOfFrames)
+{
+ size_t bufferLength = m_buffer.size();
+ bool isPointerGood = readIndex && *readIndex >= 0 && *readIndex + numberOfFrames <= bufferLength;
+ ASSERT(isPointerGood);
+ if (!isPointerGood) {
+ // Should never happen in practice but return pointer to start of buffer (avoid crash)
+ if (readIndex)
+ *readIndex = 0;
+ return m_buffer.data();
+ }
+
+ float* sourceP = m_buffer.data();
+ float* p = sourceP + *readIndex;
+
+ // Update readIndex
+ *readIndex = (*readIndex + numberOfFrames) % bufferLength;
+
+ return p;
+}
+
+void ReverbInputBuffer::reset()
+{
+ m_buffer.zero();
+ m_writeIndex = 0;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/ReverbInputBuffer.h b/WebCore/platform/audio/ReverbInputBuffer.h
new file mode 100644
index 0000000..aa9cf41
--- /dev/null
+++ b/WebCore/platform/audio/ReverbInputBuffer.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2010 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of
+ * its contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef ReverbInputBuffer_h
+#define ReverbInputBuffer_h
+
+#include "AudioFloatArray.h"
+
+namespace WebCore {
+
+// ReverbInputBuffer is used to buffer input samples for deferred processing by the background threads.
+class ReverbInputBuffer {
+public:
+ ReverbInputBuffer(size_t length);
+
+ // The realtime audio thread keeps writing samples here.
+ // The assumption is that the buffer's length is evenly divisible by numberOfFrames (for nearly all cases this will be fine).
+ // FIXME: remove numberOfFrames restriction...
+ void write(float* sourceP, size_t numberOfFrames);
+
+ // Background threads can call this to check if there's anything to read...
+ size_t writeIndex() const { return m_writeIndex; }
+
+ // The individual background threads read here (and hope that they can keep up with the buffer writing).
+ // readIndex is updated with the next readIndex to read from...
+ // The assumption is that the buffer's length is evenly divisible by numberOfFrames.
+ // FIXME: remove numberOfFrames restriction...
+ float* directReadFrom(int* readIndex, size_t numberOfFrames);
+
+ void reset();
+
+private:
+ AudioFloatArray m_buffer;
+ size_t m_writeIndex;
+};
+
+} // namespace WebCore
+
+#endif // ReverbInputBuffer_h