Bug 815643 - Part 3: Import the Convolution processing implementation from Blink; r=roc
authorEhsan Akhgari <ehsan@mozilla.com>
Mon, 10 Jun 2013 16:08:38 -0400
changeset 134587 6bed30223d8f
parent 134586 dc80f47a7123
child 134588 b7efc129d2b1
push id29285
push usereakhgari@mozilla.com
push dateTue, 11 Jun 2013 00:10:14 +0000
treeherdermozilla-inbound@63386b71d1b5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs815643, 150518
milestone24.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 815643 - Part 3: Import the Convolution processing implementation from Blink; r=roc The original code was copied from Blink SVN revision 150518.
content/media/webaudio/blink/DirectConvolver.cpp
content/media/webaudio/blink/DirectConvolver.h
content/media/webaudio/blink/FFTConvolver.cpp
content/media/webaudio/blink/FFTConvolver.h
content/media/webaudio/blink/Reverb.cpp
content/media/webaudio/blink/Reverb.h
content/media/webaudio/blink/ReverbAccumulationBuffer.cpp
content/media/webaudio/blink/ReverbAccumulationBuffer.h
content/media/webaudio/blink/ReverbConvolver.cpp
content/media/webaudio/blink/ReverbConvolver.h
content/media/webaudio/blink/ReverbConvolverStage.cpp
content/media/webaudio/blink/ReverbConvolverStage.h
content/media/webaudio/blink/ReverbInputBuffer.cpp
content/media/webaudio/blink/ReverbInputBuffer.h
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/DirectConvolver.cpp
@@ -0,0 +1,385 @@
+/*
+ * Copyright (C) 2012 Intel 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 "core/platform/audio/DirectConvolver.h"
+
+#if OS(DARWIN)
+#include <Accelerate/Accelerate.h>
+#endif
+
+#include "core/platform/audio/VectorMath.h"
+
+namespace WebCore {
+
+using namespace VectorMath;
+    
+DirectConvolver::DirectConvolver(size_t inputBlockSize)
+    : m_inputBlockSize(inputBlockSize)
+#if USE(WEBAUDIO_IPP)
+    , m_overlayBuffer(inputBlockSize)
+#endif // USE(WEBAUDIO_IPP)
+    , m_buffer(inputBlockSize * 2)
+{
+}
+
+void DirectConvolver::process(AudioFloatArray* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess)
+{
+    ASSERT(framesToProcess == m_inputBlockSize);
+    if (framesToProcess != m_inputBlockSize)
+        return;
+
+    // Only support kernelSize <= m_inputBlockSize
+    size_t kernelSize = convolutionKernel->size();
+    ASSERT(kernelSize <= m_inputBlockSize);
+    if (kernelSize > m_inputBlockSize)
+        return;
+
+    float* kernelP = convolutionKernel->data();
+
+    // Sanity check
+    bool isCopyGood = kernelP && sourceP && destP && m_buffer.data();
+    ASSERT(isCopyGood);
+    if (!isCopyGood)
+        return;
+
+#if USE(WEBAUDIO_IPP)
+    float* outputBuffer = m_buffer.data();
+    float* overlayBuffer = m_overlayBuffer.data();
+    bool isCopyGood2 = overlayBuffer && m_overlayBuffer.size() >= kernelSize && m_buffer.size() == m_inputBlockSize * 2;
+    ASSERT(isCopyGood2);
+    if (!isCopyGood2)
+        return;
+
+    ippsConv_32f(static_cast<const Ipp32f*>(sourceP), framesToProcess, static_cast<Ipp32f*>(kernelP), kernelSize, static_cast<Ipp32f*>(outputBuffer));
+
+    vadd(outputBuffer, 1, overlayBuffer, 1, destP, 1, framesToProcess);
+    memcpy(overlayBuffer, outputBuffer + m_inputBlockSize, sizeof(float) * kernelSize);
+#else
+    float* inputP = m_buffer.data() + m_inputBlockSize;
+
+    // Copy samples to 2nd half of input buffer.
+    memcpy(inputP, sourceP, sizeof(float) * framesToProcess);
+
+#if OS(DARWIN)
+#if defined(__ppc__) || defined(__i386__)
+    conv(inputP - kernelSize + 1, 1, kernelP + kernelSize - 1, -1, destP, 1, framesToProcess, kernelSize);
+#else
+    vDSP_conv(inputP - kernelSize + 1, 1, kernelP + kernelSize - 1, -1, destP, 1, framesToProcess, kernelSize);
+#endif // defined(__ppc__) || defined(__i386__)
+#else
+    // FIXME: The macro can be further optimized to avoid pipeline stalls. One possibility is to maintain 4 separate sums and change the macro to CONVOLVE_FOUR_SAMPLES.
+#define CONVOLVE_ONE_SAMPLE             \
+    sum += inputP[i - j] * kernelP[j];  \
+    j++;
+
+    size_t i = 0;
+    while (i < framesToProcess) {
+        size_t j = 0;
+        float sum = 0;
+        
+        // FIXME: SSE optimization may be applied here.
+        if (kernelSize == 32) {
+            CONVOLVE_ONE_SAMPLE // 1
+            CONVOLVE_ONE_SAMPLE // 2
+            CONVOLVE_ONE_SAMPLE // 3
+            CONVOLVE_ONE_SAMPLE // 4
+            CONVOLVE_ONE_SAMPLE // 5
+            CONVOLVE_ONE_SAMPLE // 6
+            CONVOLVE_ONE_SAMPLE // 7
+            CONVOLVE_ONE_SAMPLE // 8
+            CONVOLVE_ONE_SAMPLE // 9
+            CONVOLVE_ONE_SAMPLE // 10
+
+            CONVOLVE_ONE_SAMPLE // 11
+            CONVOLVE_ONE_SAMPLE // 12
+            CONVOLVE_ONE_SAMPLE // 13
+            CONVOLVE_ONE_SAMPLE // 14
+            CONVOLVE_ONE_SAMPLE // 15
+            CONVOLVE_ONE_SAMPLE // 16
+            CONVOLVE_ONE_SAMPLE // 17
+            CONVOLVE_ONE_SAMPLE // 18
+            CONVOLVE_ONE_SAMPLE // 19
+            CONVOLVE_ONE_SAMPLE // 20
+
+            CONVOLVE_ONE_SAMPLE // 21
+            CONVOLVE_ONE_SAMPLE // 22
+            CONVOLVE_ONE_SAMPLE // 23
+            CONVOLVE_ONE_SAMPLE // 24
+            CONVOLVE_ONE_SAMPLE // 25
+            CONVOLVE_ONE_SAMPLE // 26
+            CONVOLVE_ONE_SAMPLE // 27
+            CONVOLVE_ONE_SAMPLE // 28
+            CONVOLVE_ONE_SAMPLE // 29
+            CONVOLVE_ONE_SAMPLE // 30
+
+            CONVOLVE_ONE_SAMPLE // 31
+            CONVOLVE_ONE_SAMPLE // 32
+
+        } else if (kernelSize == 64) {
+            CONVOLVE_ONE_SAMPLE // 1
+            CONVOLVE_ONE_SAMPLE // 2
+            CONVOLVE_ONE_SAMPLE // 3
+            CONVOLVE_ONE_SAMPLE // 4
+            CONVOLVE_ONE_SAMPLE // 5
+            CONVOLVE_ONE_SAMPLE // 6
+            CONVOLVE_ONE_SAMPLE // 7
+            CONVOLVE_ONE_SAMPLE // 8
+            CONVOLVE_ONE_SAMPLE // 9
+            CONVOLVE_ONE_SAMPLE // 10
+
+            CONVOLVE_ONE_SAMPLE // 11
+            CONVOLVE_ONE_SAMPLE // 12
+            CONVOLVE_ONE_SAMPLE // 13
+            CONVOLVE_ONE_SAMPLE // 14
+            CONVOLVE_ONE_SAMPLE // 15
+            CONVOLVE_ONE_SAMPLE // 16
+            CONVOLVE_ONE_SAMPLE // 17
+            CONVOLVE_ONE_SAMPLE // 18
+            CONVOLVE_ONE_SAMPLE // 19
+            CONVOLVE_ONE_SAMPLE // 20
+
+            CONVOLVE_ONE_SAMPLE // 21
+            CONVOLVE_ONE_SAMPLE // 22
+            CONVOLVE_ONE_SAMPLE // 23
+            CONVOLVE_ONE_SAMPLE // 24
+            CONVOLVE_ONE_SAMPLE // 25
+            CONVOLVE_ONE_SAMPLE // 26
+            CONVOLVE_ONE_SAMPLE // 27
+            CONVOLVE_ONE_SAMPLE // 28
+            CONVOLVE_ONE_SAMPLE // 29
+            CONVOLVE_ONE_SAMPLE // 30
+
+            CONVOLVE_ONE_SAMPLE // 31
+            CONVOLVE_ONE_SAMPLE // 32
+            CONVOLVE_ONE_SAMPLE // 33
+            CONVOLVE_ONE_SAMPLE // 34
+            CONVOLVE_ONE_SAMPLE // 35
+            CONVOLVE_ONE_SAMPLE // 36
+            CONVOLVE_ONE_SAMPLE // 37
+            CONVOLVE_ONE_SAMPLE // 38
+            CONVOLVE_ONE_SAMPLE // 39
+            CONVOLVE_ONE_SAMPLE // 40
+
+            CONVOLVE_ONE_SAMPLE // 41
+            CONVOLVE_ONE_SAMPLE // 42
+            CONVOLVE_ONE_SAMPLE // 43
+            CONVOLVE_ONE_SAMPLE // 44
+            CONVOLVE_ONE_SAMPLE // 45
+            CONVOLVE_ONE_SAMPLE // 46
+            CONVOLVE_ONE_SAMPLE // 47
+            CONVOLVE_ONE_SAMPLE // 48
+            CONVOLVE_ONE_SAMPLE // 49
+            CONVOLVE_ONE_SAMPLE // 50
+
+            CONVOLVE_ONE_SAMPLE // 51
+            CONVOLVE_ONE_SAMPLE // 52
+            CONVOLVE_ONE_SAMPLE // 53
+            CONVOLVE_ONE_SAMPLE // 54
+            CONVOLVE_ONE_SAMPLE // 55
+            CONVOLVE_ONE_SAMPLE // 56
+            CONVOLVE_ONE_SAMPLE // 57
+            CONVOLVE_ONE_SAMPLE // 58
+            CONVOLVE_ONE_SAMPLE // 59
+            CONVOLVE_ONE_SAMPLE // 60
+
+            CONVOLVE_ONE_SAMPLE // 61
+            CONVOLVE_ONE_SAMPLE // 62
+            CONVOLVE_ONE_SAMPLE // 63
+            CONVOLVE_ONE_SAMPLE // 64
+
+        } else if (kernelSize == 128) {
+            CONVOLVE_ONE_SAMPLE // 1
+            CONVOLVE_ONE_SAMPLE // 2
+            CONVOLVE_ONE_SAMPLE // 3
+            CONVOLVE_ONE_SAMPLE // 4
+            CONVOLVE_ONE_SAMPLE // 5
+            CONVOLVE_ONE_SAMPLE // 6
+            CONVOLVE_ONE_SAMPLE // 7
+            CONVOLVE_ONE_SAMPLE // 8
+            CONVOLVE_ONE_SAMPLE // 9
+            CONVOLVE_ONE_SAMPLE // 10
+
+            CONVOLVE_ONE_SAMPLE // 11
+            CONVOLVE_ONE_SAMPLE // 12
+            CONVOLVE_ONE_SAMPLE // 13
+            CONVOLVE_ONE_SAMPLE // 14
+            CONVOLVE_ONE_SAMPLE // 15
+            CONVOLVE_ONE_SAMPLE // 16
+            CONVOLVE_ONE_SAMPLE // 17
+            CONVOLVE_ONE_SAMPLE // 18
+            CONVOLVE_ONE_SAMPLE // 19
+            CONVOLVE_ONE_SAMPLE // 20
+
+            CONVOLVE_ONE_SAMPLE // 21
+            CONVOLVE_ONE_SAMPLE // 22
+            CONVOLVE_ONE_SAMPLE // 23
+            CONVOLVE_ONE_SAMPLE // 24
+            CONVOLVE_ONE_SAMPLE // 25
+            CONVOLVE_ONE_SAMPLE // 26
+            CONVOLVE_ONE_SAMPLE // 27
+            CONVOLVE_ONE_SAMPLE // 28
+            CONVOLVE_ONE_SAMPLE // 29
+            CONVOLVE_ONE_SAMPLE // 30
+
+            CONVOLVE_ONE_SAMPLE // 31
+            CONVOLVE_ONE_SAMPLE // 32
+            CONVOLVE_ONE_SAMPLE // 33
+            CONVOLVE_ONE_SAMPLE // 34
+            CONVOLVE_ONE_SAMPLE // 35
+            CONVOLVE_ONE_SAMPLE // 36
+            CONVOLVE_ONE_SAMPLE // 37
+            CONVOLVE_ONE_SAMPLE // 38
+            CONVOLVE_ONE_SAMPLE // 39
+            CONVOLVE_ONE_SAMPLE // 40
+
+            CONVOLVE_ONE_SAMPLE // 41
+            CONVOLVE_ONE_SAMPLE // 42
+            CONVOLVE_ONE_SAMPLE // 43
+            CONVOLVE_ONE_SAMPLE // 44
+            CONVOLVE_ONE_SAMPLE // 45
+            CONVOLVE_ONE_SAMPLE // 46
+            CONVOLVE_ONE_SAMPLE // 47
+            CONVOLVE_ONE_SAMPLE // 48
+            CONVOLVE_ONE_SAMPLE // 49
+            CONVOLVE_ONE_SAMPLE // 50
+
+            CONVOLVE_ONE_SAMPLE // 51
+            CONVOLVE_ONE_SAMPLE // 52
+            CONVOLVE_ONE_SAMPLE // 53
+            CONVOLVE_ONE_SAMPLE // 54
+            CONVOLVE_ONE_SAMPLE // 55
+            CONVOLVE_ONE_SAMPLE // 56
+            CONVOLVE_ONE_SAMPLE // 57
+            CONVOLVE_ONE_SAMPLE // 58
+            CONVOLVE_ONE_SAMPLE // 59
+            CONVOLVE_ONE_SAMPLE // 60
+
+            CONVOLVE_ONE_SAMPLE // 61
+            CONVOLVE_ONE_SAMPLE // 62
+            CONVOLVE_ONE_SAMPLE // 63
+            CONVOLVE_ONE_SAMPLE // 64
+            CONVOLVE_ONE_SAMPLE // 65
+            CONVOLVE_ONE_SAMPLE // 66
+            CONVOLVE_ONE_SAMPLE // 67
+            CONVOLVE_ONE_SAMPLE // 68
+            CONVOLVE_ONE_SAMPLE // 69
+            CONVOLVE_ONE_SAMPLE // 70
+
+            CONVOLVE_ONE_SAMPLE // 71
+            CONVOLVE_ONE_SAMPLE // 72
+            CONVOLVE_ONE_SAMPLE // 73
+            CONVOLVE_ONE_SAMPLE // 74
+            CONVOLVE_ONE_SAMPLE // 75
+            CONVOLVE_ONE_SAMPLE // 76
+            CONVOLVE_ONE_SAMPLE // 77
+            CONVOLVE_ONE_SAMPLE // 78
+            CONVOLVE_ONE_SAMPLE // 79
+            CONVOLVE_ONE_SAMPLE // 80
+
+            CONVOLVE_ONE_SAMPLE // 81
+            CONVOLVE_ONE_SAMPLE // 82
+            CONVOLVE_ONE_SAMPLE // 83
+            CONVOLVE_ONE_SAMPLE // 84
+            CONVOLVE_ONE_SAMPLE // 85
+            CONVOLVE_ONE_SAMPLE // 86
+            CONVOLVE_ONE_SAMPLE // 87
+            CONVOLVE_ONE_SAMPLE // 88
+            CONVOLVE_ONE_SAMPLE // 89
+            CONVOLVE_ONE_SAMPLE // 90
+
+            CONVOLVE_ONE_SAMPLE // 91
+            CONVOLVE_ONE_SAMPLE // 92
+            CONVOLVE_ONE_SAMPLE // 93
+            CONVOLVE_ONE_SAMPLE // 94
+            CONVOLVE_ONE_SAMPLE // 95
+            CONVOLVE_ONE_SAMPLE // 96
+            CONVOLVE_ONE_SAMPLE // 97
+            CONVOLVE_ONE_SAMPLE // 98
+            CONVOLVE_ONE_SAMPLE // 99
+            CONVOLVE_ONE_SAMPLE // 100
+
+            CONVOLVE_ONE_SAMPLE // 101
+            CONVOLVE_ONE_SAMPLE // 102
+            CONVOLVE_ONE_SAMPLE // 103
+            CONVOLVE_ONE_SAMPLE // 104
+            CONVOLVE_ONE_SAMPLE // 105
+            CONVOLVE_ONE_SAMPLE // 106
+            CONVOLVE_ONE_SAMPLE // 107
+            CONVOLVE_ONE_SAMPLE // 108
+            CONVOLVE_ONE_SAMPLE // 109
+            CONVOLVE_ONE_SAMPLE // 110
+
+            CONVOLVE_ONE_SAMPLE // 111
+            CONVOLVE_ONE_SAMPLE // 112
+            CONVOLVE_ONE_SAMPLE // 113
+            CONVOLVE_ONE_SAMPLE // 114
+            CONVOLVE_ONE_SAMPLE // 115
+            CONVOLVE_ONE_SAMPLE // 116
+            CONVOLVE_ONE_SAMPLE // 117
+            CONVOLVE_ONE_SAMPLE // 118
+            CONVOLVE_ONE_SAMPLE // 119
+            CONVOLVE_ONE_SAMPLE // 120
+
+            CONVOLVE_ONE_SAMPLE // 121
+            CONVOLVE_ONE_SAMPLE // 122
+            CONVOLVE_ONE_SAMPLE // 123
+            CONVOLVE_ONE_SAMPLE // 124
+            CONVOLVE_ONE_SAMPLE // 125
+            CONVOLVE_ONE_SAMPLE // 126
+            CONVOLVE_ONE_SAMPLE // 127
+            CONVOLVE_ONE_SAMPLE // 128
+        } else {
+            while (j < kernelSize) {
+                // Non-optimized using actual while loop.
+                CONVOLVE_ONE_SAMPLE
+            }
+        }
+        destP[i++] = sum;
+    }
+#endif // OS(DARWIN)
+
+    // Copy 2nd half of input buffer to 1st half.
+    memcpy(m_buffer.data(), inputP, sizeof(float) * framesToProcess);
+#endif
+}
+
+void DirectConvolver::reset()
+{
+    m_buffer.zero();
+#if USE(WEBAUDIO_IPP)
+    m_overlayBuffer.zero();
+#endif // USE(WEBAUDIO_IPP)
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/DirectConvolver.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2012 Intel 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 DirectConvolver_h
+#define DirectConvolver_h
+
+#include "core/platform/audio/AudioArray.h"
+
+#if USE(WEBAUDIO_IPP)
+#include <ipps.h>
+#endif // USE(WEBAUDIO_IPP)
+
+namespace WebCore {
+
+class DirectConvolver {
+public:
+    DirectConvolver(size_t inputBlockSize);
+
+    void process(AudioFloatArray* convolutionKernel, const float* sourceP, float* destP, size_t framesToProcess);
+
+    void reset();
+
+private:
+    size_t m_inputBlockSize;
+
+#if USE(WEBAUDIO_IPP)
+    AudioFloatArray m_overlayBuffer;
+#endif // USE(WEBAUDIO_IPP)
+    AudioFloatArray m_buffer;
+};
+
+} // namespace WebCore
+
+#endif // DirectConvolver_h
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/FFTConvolver.cpp
@@ -0,0 +1,120 @@
+/*
+ * 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 "core/platform/audio/FFTConvolver.h"
+
+#include "core/platform/audio/VectorMath.h"
+
+namespace WebCore {
+
+using namespace VectorMath;
+    
+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, const float* sourceP, float* destP, size_t framesToProcess)
+{
+    size_t halfSize = fftSize() / 2;
+
+    // framesToProcess must be an exact multiple of halfSize,
+    // or halfSize is a multiple of framesToProcess when halfSize > framesToProcess.
+    bool isGood = !(halfSize % framesToProcess && framesToProcess % halfSize);
+    ASSERT(isGood);
+    if (!isGood)
+        return;
+
+    size_t numberOfDivisions = halfSize <= framesToProcess ? (framesToProcess / halfSize) : 1;
+    size_t divisionSize = numberOfDivisions == 1 ? framesToProcess : halfSize;
+
+    for (size_t i = 0; i < numberOfDivisions; ++i, sourceP += divisionSize, destP += divisionSize) {
+        // Copy samples to input buffer (note contraint above!)
+        float* inputP = m_inputBuffer.data();
+
+        // Sanity check
+        bool isCopyGood1 = sourceP && inputP && m_readWriteIndex + divisionSize <= m_inputBuffer.size();
+        ASSERT(isCopyGood1);
+        if (!isCopyGood1)
+            return;
+
+        memcpy(inputP + m_readWriteIndex, sourceP, sizeof(float) * divisionSize);
+
+        // Copy samples from output buffer
+        float* outputP = m_outputBuffer.data();
+
+        // Sanity check
+        bool isCopyGood2 = destP && outputP && m_readWriteIndex + divisionSize <= m_outputBuffer.size();
+        ASSERT(isCopyGood2);
+        if (!isCopyGood2)
+            return;
+
+        memcpy(destP, outputP + m_readWriteIndex, sizeof(float) * divisionSize);
+        m_readWriteIndex += divisionSize;
+
+        // Check if it's time to perform the next FFT
+        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)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/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 "core/platform/audio/AudioArray.h"
+#include "core/platform/audio/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, const 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
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/Reverb.cpp
@@ -0,0 +1,243 @@
+/*
+ * 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 "core/platform/audio/Reverb.h"
+
+#include <math.h>
+#include "core/platform/audio/AudioBus.h"
+#include "core/platform/audio/AudioFileReader.h"
+#include "core/platform/audio/ReverbConvolver.h"
+#include "core/platform/audio/VectorMath.h"
+#include <wtf/MathExtras.h>
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+#if OS(DARWIN)
+using namespace std;
+#endif
+
+namespace WebCore {
+
+using namespace VectorMath;
+
+// Empirical gain calibration tested across many impulse responses to ensure perceived volume is same as dry (unprocessed) signal
+const float GainCalibration = -58;
+const float GainCalibrationSampleRate = 44100;
+
+// A minimum power value to when normalizing a silent (or very quiet) impulse response
+const float MinPower = 0.000125f;
+    
+static float calculateNormalizationScale(AudioBus* response)
+{
+    // Normalize by RMS power
+    size_t numberOfChannels = response->numberOfChannels();
+    size_t length = response->length();
+
+    float power = 0;
+
+    for (size_t i = 0; i < numberOfChannels; ++i) {
+        float channelPower = 0;
+        vsvesq(response->channel(i)->data(), 1, &channelPower, length);
+        power += channelPower;
+    }
+
+    power = sqrt(power / (numberOfChannels * length));
+
+    // Protect against accidental overload
+    if (std::isinf(power) || std::isnan(power) || power < MinPower)
+        power = MinPower;
+
+    float scale = 1 / power;
+
+    scale *= powf(10, GainCalibration * 0.05f); // calibrate to make perceived volume same as unprocessed
+
+    // Scale depends on sample-rate.
+    if (response->sampleRate())
+        scale *= GainCalibrationSampleRate / response->sampleRate();
+
+    // True-stereo compensation
+    if (response->numberOfChannels() == 4)
+        scale *= 0.5f;
+
+    return scale;
+}
+
+Reverb::Reverb(AudioBus* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads, bool normalize)
+{
+    float scale = 1;
+
+    if (normalize) {
+        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.
+    // FIXME: What about roundoff? Perhaps consider making a temporary scaled copy
+    // instead of scaling and unscaling in place.
+    if (normalize && scale)
+        impulseResponse->scale(1 / scale);
+}
+
+void Reverb::initialize(AudioBus* impulseResponseBuffer, size_t renderSliceSize, size_t maxFFTSize, size_t numberOfChannels, bool useBackgroundThreads)
+{
+    m_impulseResponseLength = impulseResponseBuffer->length();
+
+    // 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);
+
+        OwnPtr<ReverbConvolver> convolver = adoptPtr(new ReverbConvolver(channel, renderSliceSize, maxFFTSize, convolverRenderPhase, useBackgroundThreads));
+        m_convolvers.append(convolver.release());
+
+        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 = AudioBus::create(2, MaxFrameSize);
+}
+
+void Reverb::process(const 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->length() && framesToProcess <= destinationBus->length(); 
+    
+    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);
+    const 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
+        const 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->length() >= framesToProcess && destinationChannelR->length() >= framesToProcess;
+        ASSERT(isCopySafe);
+        if (!isCopySafe)
+            return;
+        memcpy(destinationChannelR->mutableData(), 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)
+        const 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();
+}
+
+size_t Reverb::latencyFrames() const
+{
+    return !m_convolvers.isEmpty() ? m_convolvers.first()->latencyFrames() : 0;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/Reverb.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 Reverb_h
+#define Reverb_h
+
+#include "core/platform/audio/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, bool normalize);
+
+    void process(const AudioBus* sourceBus, AudioBus* destinationBus, size_t framesToProcess);
+    void reset();
+
+    size_t impulseResponseLength() const { return m_impulseResponseLength; }
+    size_t latencyFrames() const;
+
+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
+    RefPtr<AudioBus> m_tempBuffer;
+};
+
+} // namespace WebCore
+
+#endif // Reverb_h
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbAccumulationBuffer.cpp
@@ -0,0 +1,119 @@
+/*
+ * 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 "core/platform/audio/ReverbAccumulationBuffer.h"
+
+#include "core/platform/audio/VectorMath.h"
+
+namespace WebCore {
+
+using namespace VectorMath;
+
+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)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/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 "core/platform/audio/AudioArray.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
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbConvolver.cpp
@@ -0,0 +1,238 @@
+/*
+ * 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 "core/platform/audio/ReverbConvolver.h"
+
+#include "core/platform/audio/AudioBus.h"
+#include "core/platform/audio/VectorMath.h"
+
+namespace WebCore {
+
+using namespace VectorMath;
+
+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 = 128;
+const size_t MaxRealtimeFFTSize = 2048;
+
+static void backgroundThreadEntry(void* threadData)
+{
+    ReverbConvolver* reverbConvolver = static_cast<ReverbConvolver*>(threadData);
+    reverbConvolver->backgroundThreadEntry();
+}
+
+ReverbConvolver::ReverbConvolver(AudioChannel* impulseResponse, size_t renderSliceSize, size_t maxFFTSize, size_t convolverRenderPhase, bool useBackgroundThreads)
+    : m_impulseResponseLength(impulseResponse->length())
+    , m_accumulationBuffer(impulseResponse->length() + renderSliceSize)
+    , m_inputBuffer(InputBufferSize)
+    , 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;
+
+    const float* response = impulseResponse->data();
+    size_t totalResponseLength = impulseResponse->length();
+
+    // The total latency is zero because the direct-convolution is used in the leading portion.
+    size_t reverbTotalLatency = 0;
+
+    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;
+
+        bool useDirectConvolver = !stageOffset;
+
+        OwnPtr<ReverbConvolverStage> stage = adoptPtr(new ReverbConvolverStage(response, totalResponseLength, reverbTotalLatency, stageOffset, stageSize, fftSize, renderPhase, renderSliceSize, &m_accumulationBuffer, useDirectConvolver));
+
+        bool isBackgroundStage = false;
+
+        if (this->useBackgroundThreads() && stageOffset > RealtimeFrameLimit) {
+            m_backgroundStages.append(stage.release());
+            isBackgroundStage = true;
+        } else
+            m_stages.append(stage.release());
+
+        stageOffset += stageSize;
+        ++i;
+
+        if (!useDirectConvolver) {
+            // 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);
+    }
+}
+
+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(const AudioChannel* sourceChannel, AudioChannel* destinationChannel, size_t framesToProcess)
+{
+    bool isSafe = sourceChannel && destinationChannel && sourceChannel->length() >= framesToProcess && destinationChannel->length() >= framesToProcess;
+    ASSERT(isSafe);
+    if (!isSafe)
+        return;
+        
+    const float* source = sourceChannel->data();
+    float* destination = destinationChannel->mutableData();
+    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();
+}
+
+size_t ReverbConvolver::latencyFrames() const
+{
+    return 0;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbConvolver.h
@@ -0,0 +1,95 @@
+/*
+ * 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 "core/platform/audio/AudioArray.h"
+#include "core/platform/audio/DirectConvolver.h"
+#include "core/platform/audio/FFTConvolver.h"
+#include "core/platform/audio/ReverbAccumulationBuffer.h"
+#include "core/platform/audio/ReverbConvolverStage.h"
+#include "core/platform/audio/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(const 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();
+
+    size_t latencyFrames() const;
+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;
+
+    // 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
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbConvolverStage.cpp
@@ -0,0 +1,182 @@
+/*
+ * 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 "core/platform/audio/ReverbConvolverStage.h"
+
+#include "core/platform/audio/ReverbAccumulationBuffer.h"
+#include "core/platform/audio/ReverbConvolver.h"
+#include "core/platform/audio/ReverbInputBuffer.h"
+#include "core/platform/audio/VectorMath.h"
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+using namespace VectorMath;
+
+ReverbConvolverStage::ReverbConvolverStage(const float* impulseResponse, size_t, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength,
+                                           size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer* accumulationBuffer, bool directMode)
+    : m_accumulationBuffer(accumulationBuffer)
+    , m_accumulationReadIndex(0)
+    , m_inputReadIndex(0)
+    , m_directMode(directMode)
+{
+    ASSERT(impulseResponse);
+    ASSERT(accumulationBuffer);
+
+    if (!m_directMode) {
+        m_fftKernel = adoptPtr(new FFTFrame(fftSize));
+        m_fftKernel->doPaddedFFT(impulseResponse + stageOffset, stageLength);
+        m_fftConvolver = adoptPtr(new FFTConvolver(fftSize));
+    } else {
+        m_directKernel = adoptPtr(new AudioFloatArray(fftSize / 2));
+        m_directKernel->copyToRange(impulseResponse + stageOffset, 0, fftSize / 2);
+        m_directConvolver = adoptPtr(new DirectConvolver(renderSliceSize));
+    }
+    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;
+    if (!m_directMode) {
+        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
+
+    size_t delayBufferSize = m_preDelayLength < fftSize ? fftSize : m_preDelayLength;
+    delayBufferSize = delayBufferSize < renderSliceSize ? renderSliceSize : delayBufferSize;
+    m_preDelayBuffer.allocate(delayBufferSize);
+}
+
+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(const float* source, size_t framesToProcess)
+{
+    ASSERT(source);
+    if (!source)
+        return;
+    
+    // Deal with pre-delay stream : note special handling of zero delay.
+
+    const float* preDelayedSource;
+    float* preDelayedDestination;
+    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();
+
+        preDelayedDestination = m_preDelayBuffer.data() + m_preReadWriteIndex;
+        preDelayedSource = preDelayedDestination;
+        temporaryBuffer = m_temporaryBuffer.data();        
+    } else {
+        // Zero delay
+        preDelayedDestination = 0;
+        preDelayedSource = source;
+        temporaryBuffer = m_preDelayBuffer.data();
+        
+        isTemporaryBufferSafe = framesToProcess <= m_preDelayBuffer.size();
+    }
+    
+    ASSERT(isTemporaryBufferSafe);
+    if (!isTemporaryBufferSafe)
+        return;
+
+    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...
+        if (!m_directMode)
+            m_fftConvolver->process(m_fftKernel.get(), preDelayedSource, temporaryBuffer, framesToProcess);
+        else
+            m_directConvolver->process(m_directKernel.get(), preDelayedSource, temporaryBuffer, framesToProcess);
+
+        // Now accumulate into reverb's accumulation buffer.
+        m_accumulationBuffer->accumulate(temporaryBuffer, framesToProcess, &m_accumulationReadIndex, m_postDelayLength);
+    }
+
+    // Finally copy input to pre-delay.
+    if (m_preDelayLength > 0) {
+        memcpy(preDelayedDestination, 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()
+{
+    if (!m_directMode)
+        m_fftConvolver->reset();
+    else
+        m_directConvolver->reset();
+    m_preDelayBuffer.zero();
+    m_accumulationReadIndex = 0;
+    m_inputReadIndex = 0;
+    m_framesProcessed = 0;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/ReverbConvolverStage.h
@@ -0,0 +1,85 @@
+/*
+ * 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 "core/platform/audio/AudioArray.h"
+#include "core/platform/audio/FFTFrame.h"
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+class ReverbAccumulationBuffer;
+class ReverbConvolver;
+class FFTConvolver;
+class DirectConvolver;
+    
+// 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(const float* impulseResponse, size_t responseLength, size_t reverbTotalLatency, size_t stageOffset, size_t stageLength, size_t fftSize, size_t renderPhase, size_t renderSliceSize, ReverbAccumulationBuffer*, bool directMode = false);
+
+    // WARNING: framesToProcess must be such that it evenly divides the delay buffer size (stage_offset).
+    void process(const 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:
+    OwnPtr<FFTFrame> m_fftKernel;
+    OwnPtr<FFTConvolver> m_fftConvolver;
+
+    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;
+
+    bool m_directMode;
+    OwnPtr<AudioFloatArray> m_directKernel;
+    OwnPtr<DirectConvolver> m_directConvolver;
+};
+
+} // namespace WebCore
+
+#endif // ReverbConvolverStage_h
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/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 "core/platform/audio/ReverbInputBuffer.h"
+
+namespace WebCore {
+
+ReverbInputBuffer::ReverbInputBuffer(size_t length)
+    : m_buffer(length)
+    , m_writeIndex(0)
+{
+}
+
+void ReverbInputBuffer::write(const 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)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/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 "core/platform/audio/AudioArray.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(const 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