2010-08-30 Chris Rogers <crogers@google.com>
authorcrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 30 Aug 2010 22:43:07 +0000 (22:43 +0000)
committercrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 30 Aug 2010 22:43:07 +0000 (22:43 +0000)
        Reviewed by Kenneth Russell.

        audio engine: add ReverbConvolver class
        https://bugs.webkit.org/show_bug.cgi?id=34912

        No new tests since audio API is not yet implemented.

        * platform/audio: Added.
        * platform/audio/ReverbAccumulationBuffer.cpp: Added.
        (WebCore::ReverbAccumulationBuffer::ReverbAccumulationBuffer):
        (WebCore::ReverbAccumulationBuffer::readAndClear):
        (WebCore::ReverbAccumulationBuffer::updateReadIndex):
        (WebCore::ReverbAccumulationBuffer::accumulate):
        (WebCore::ReverbAccumulationBuffer::reset):
        * platform/audio/ReverbAccumulationBuffer.h: Added.
        (WebCore::ReverbAccumulationBuffer::readIndex):
        (WebCore::ReverbAccumulationBuffer::readTimeFrame):
        * platform/audio/ReverbConvolver.cpp: Added.
        (WebCore::backgroundThreadEntry):
        (WebCore::ReverbConvolver::ReverbConvolver):
        (WebCore::ReverbConvolver::~ReverbConvolver):
        (WebCore::ReverbConvolver::backgroundThreadEntry):
        (WebCore::ReverbConvolver::process):
        (WebCore::ReverbConvolver::reset):
        * platform/audio/ReverbConvolver.h: Added.
        (WebCore::ReverbConvolver::impulseResponseLength):
        (WebCore::ReverbConvolver::inputBuffer):
        (WebCore::ReverbConvolver::useBackgroundThreads):
        * platform/audio/ReverbConvolverStage.cpp: Added.
        (WebCore::ReverbConvolverStage::ReverbConvolverStage):
        (WebCore::ReverbConvolverStage::processInBackground):
        (WebCore::ReverbConvolverStage::process):
        (WebCore::ReverbConvolverStage::reset):
        * platform/audio/ReverbConvolverStage.h: Added.
        (WebCore::ReverbConvolverStage::inputReadIndex):
        * platform/audio/ReverbInputBuffer.cpp: Added.
        (WebCore::ReverbInputBuffer::ReverbInputBuffer):
        (WebCore::ReverbInputBuffer::write):
        (WebCore::ReverbInputBuffer::directReadFrom):
        (WebCore::ReverbInputBuffer::reset):
        * platform/audio/ReverbInputBuffer.h: Added.
        (WebCore::ReverbInputBuffer::writeIndex):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@66419 268f45cc-cd09-0410-ab3c-d52691b4dbfc

WebCore/ChangeLog
WebCore/platform/audio/ReverbAccumulationBuffer.cpp [new file with mode: 0644]
WebCore/platform/audio/ReverbAccumulationBuffer.h [new file with mode: 0644]
WebCore/platform/audio/ReverbConvolver.cpp [new file with mode: 0644]
WebCore/platform/audio/ReverbConvolver.h [new file with mode: 0644]
WebCore/platform/audio/ReverbConvolverStage.cpp [new file with mode: 0644]
WebCore/platform/audio/ReverbConvolverStage.h [new file with mode: 0644]
WebCore/platform/audio/ReverbInputBuffer.cpp [new file with mode: 0644]
WebCore/platform/audio/ReverbInputBuffer.h [new file with mode: 0644]

index 4da57dc26ae1138a13dbcd00f6a1efab7f49a061..ddcd52e9676ba66bb03c410afff59e2261f7d830 100644 (file)
@@ -1,3 +1,48 @@
+2010-08-30  Chris Rogers  <crogers@google.com>
+
+        Reviewed by Kenneth Russell.
+
+        audio engine: add ReverbConvolver class
+        https://bugs.webkit.org/show_bug.cgi?id=34912
+
+        No new tests since audio API is not yet implemented.
+
+        * platform/audio: Added.
+        * platform/audio/ReverbAccumulationBuffer.cpp: Added.
+        (WebCore::ReverbAccumulationBuffer::ReverbAccumulationBuffer):
+        (WebCore::ReverbAccumulationBuffer::readAndClear):
+        (WebCore::ReverbAccumulationBuffer::updateReadIndex):
+        (WebCore::ReverbAccumulationBuffer::accumulate):
+        (WebCore::ReverbAccumulationBuffer::reset):
+        * platform/audio/ReverbAccumulationBuffer.h: Added.
+        (WebCore::ReverbAccumulationBuffer::readIndex):
+        (WebCore::ReverbAccumulationBuffer::readTimeFrame):
+        * platform/audio/ReverbConvolver.cpp: Added.
+        (WebCore::backgroundThreadEntry):
+        (WebCore::ReverbConvolver::ReverbConvolver):
+        (WebCore::ReverbConvolver::~ReverbConvolver):
+        (WebCore::ReverbConvolver::backgroundThreadEntry):
+        (WebCore::ReverbConvolver::process):
+        (WebCore::ReverbConvolver::reset):
+        * platform/audio/ReverbConvolver.h: Added.
+        (WebCore::ReverbConvolver::impulseResponseLength):
+        (WebCore::ReverbConvolver::inputBuffer):
+        (WebCore::ReverbConvolver::useBackgroundThreads):
+        * platform/audio/ReverbConvolverStage.cpp: Added.
+        (WebCore::ReverbConvolverStage::ReverbConvolverStage):
+        (WebCore::ReverbConvolverStage::processInBackground):
+        (WebCore::ReverbConvolverStage::process):
+        (WebCore::ReverbConvolverStage::reset):
+        * platform/audio/ReverbConvolverStage.h: Added.
+        (WebCore::ReverbConvolverStage::inputReadIndex):
+        * platform/audio/ReverbInputBuffer.cpp: Added.
+        (WebCore::ReverbInputBuffer::ReverbInputBuffer):
+        (WebCore::ReverbInputBuffer::write):
+        (WebCore::ReverbInputBuffer::directReadFrom):
+        (WebCore::ReverbInputBuffer::reset):
+        * platform/audio/ReverbInputBuffer.h: Added.
+        (WebCore::ReverbInputBuffer::writeIndex):
+
 2010-08-30  Nikolas Zimmermann  <nzimmermann@rim.com>
 
         Reviewed by Dirk Schulze.
diff --git a/WebCore/platform/audio/ReverbAccumulationBuffer.cpp b/WebCore/platform/audio/ReverbAccumulationBuffer.cpp
new file mode 100644 (file)
index 0000000..7b1c63b
--- /dev/null
@@ -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 (file)
index 0000000..44a0773
--- /dev/null
@@ -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 (file)
index 0000000..719e586
--- /dev/null
@@ -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 (file)
index 0000000..34f77d3
--- /dev/null
@@ -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 (file)
index 0000000..8606502
--- /dev/null
@@ -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 (file)
index 0000000..88351af
--- /dev/null
@@ -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 (file)
index 0000000..f270f6f
--- /dev/null
@@ -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 (file)
index 0000000..aa9cf41
--- /dev/null
@@ -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