2010-09-07 Chris Rogers <crogers@google.com>
authorcrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Sep 2010 01:02:49 +0000 (01:02 +0000)
committercrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 8 Sep 2010 01:02:49 +0000 (01:02 +0000)
        Reviewed by Kenneth Russell.

        audio engine: add FFTFrame files
        https://bugs.webkit.org/show_bug.cgi?id=34827

        No new tests since audio API is not yet implemented.

        * platform/audio/FFTFrame.cpp: Added.
        (WebCore::FFTFrame::doPaddedFFT):
        (WebCore::FFTFrame::createInterpolatedFrame):
        (WebCore::FFTFrame::interpolateFrequencyComponents):
        (WebCore::FFTFrame::extractAverageGroupDelay):
        (WebCore::FFTFrame::addConstantGroupDelay):
        (WebCore::FFTFrame::print):
        * platform/audio/FFTFrame.h: Added.
        (WebCore::FFTFrame::fftSize):
        (WebCore::FFTFrame::log2FFTSize):
        (WebCore::FFTFrame::dspSplitComplex):
        * platform/audio/mac/FFTFrameMac.cpp: Added.
        (WebCore::FFTFrame::FFTFrame):
        (WebCore::FFTFrame::~FFTFrame):
        (WebCore::FFTFrame::multiply):
        (WebCore::FFTFrame::doFFT):
        (WebCore::FFTFrame::doInverseFFT):
        (WebCore::FFTFrame::fftSetupForSize):
        (WebCore::FFTFrame::cleanup):
        (WebCore::FFTFrame::realData):
        (WebCore::FFTFrame::imagData):

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

WebCore/ChangeLog
WebCore/platform/audio/FFTFrame.cpp [new file with mode: 0644]
WebCore/platform/audio/FFTFrame.h [new file with mode: 0644]
WebCore/platform/audio/mac/FFTFrameMac.cpp [new file with mode: 0644]

index 7e86511f39eb67d955cd85bf55574f3d87bf7ed0..c8434d233952d2bbb4285f64027013a54895500a 100644 (file)
@@ -1,3 +1,34 @@
+2010-09-07  Chris Rogers  <crogers@google.com>
+
+        Reviewed by Kenneth Russell.
+
+        audio engine: add FFTFrame files
+        https://bugs.webkit.org/show_bug.cgi?id=34827
+
+        No new tests since audio API is not yet implemented.
+
+        * platform/audio/FFTFrame.cpp: Added.
+        (WebCore::FFTFrame::doPaddedFFT):
+        (WebCore::FFTFrame::createInterpolatedFrame):
+        (WebCore::FFTFrame::interpolateFrequencyComponents):
+        (WebCore::FFTFrame::extractAverageGroupDelay):
+        (WebCore::FFTFrame::addConstantGroupDelay):
+        (WebCore::FFTFrame::print):
+        * platform/audio/FFTFrame.h: Added.
+        (WebCore::FFTFrame::fftSize):
+        (WebCore::FFTFrame::log2FFTSize):
+        (WebCore::FFTFrame::dspSplitComplex):
+        * platform/audio/mac/FFTFrameMac.cpp: Added.
+        (WebCore::FFTFrame::FFTFrame):
+        (WebCore::FFTFrame::~FFTFrame):
+        (WebCore::FFTFrame::multiply):
+        (WebCore::FFTFrame::doFFT):
+        (WebCore::FFTFrame::doInverseFFT):
+        (WebCore::FFTFrame::fftSetupForSize):
+        (WebCore::FFTFrame::cleanup):
+        (WebCore::FFTFrame::realData):
+        (WebCore::FFTFrame::imagData):
+
 2010-09-07  Brent Fulgham  <bfulgham@webkit.org>
 
         Build fix, no review.
diff --git a/WebCore/platform/audio/FFTFrame.cpp b/WebCore/platform/audio/FFTFrame.cpp
new file mode 100644 (file)
index 0000000..17292b6
--- /dev/null
@@ -0,0 +1,269 @@
+/*
+ * 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 "FFTFrame.h"
+
+#include <wtf/Complex.h>
+#include <wtf/MathExtras.h>
+#include <wtf/OwnPtr.h>
+
+namespace WebCore {
+
+void FFTFrame::doPaddedFFT(float* data, size_t dataSize)
+{
+    // Zero-pad the impulse response
+    AudioFloatArray paddedResponse(fftSize()); // zero-initialized
+    paddedResponse.copyToRange(data, 0, dataSize);
+
+    // Get the frequency-domain version of padded response
+    doFFT(paddedResponse.data());
+}
+
+PassOwnPtr<FFTFrame> FFTFrame::createInterpolatedFrame(const FFTFrame& frame1, const FFTFrame& frame2, double x)
+{
+    OwnPtr<FFTFrame> newFrame = adoptPtr(new FFTFrame(frame1.fftSize()));
+
+    newFrame->interpolateFrequencyComponents(frame1, frame2, x);
+
+    // In the time-domain, the 2nd half of the response must be zero, to avoid circular convolution aliasing...
+    int fftSize = newFrame->fftSize();
+    AudioFloatArray buffer(fftSize);
+    newFrame->doInverseFFT(buffer.data());
+    buffer.zeroRange(fftSize / 2, fftSize);
+
+    // Put back into frequency domain.
+    newFrame->doFFT(buffer.data());
+
+    return newFrame.release();
+}
+
+void FFTFrame::interpolateFrequencyComponents(const FFTFrame& frame1, const FFTFrame& frame2, double interp)
+{
+    // FIXME : with some work, this method could be optimized
+
+    float* realP = realData();
+    float* imagP = imagData();
+
+    const float* realP1 = frame1.realData();
+    const float* imagP1 = frame1.imagData();
+    const float* realP2 = frame2.realData();
+    const float* imagP2 = frame2.imagData();
+
+    m_FFTSize = frame1.fftSize();
+    m_log2FFTSize = frame1.log2FFTSize();
+
+    double s1base = (1.0 - interp);
+    double s2base = interp;
+
+    double phaseAccum = 0.0;
+    double lastPhase1 = 0.0;
+    double lastPhase2 = 0.0;
+
+    realP[0] = static_cast<float>(s1base * realP1[0] + s2base * realP2[0]);
+    imagP[0] = static_cast<float>(s1base * imagP1[0] + s2base * imagP2[0]);
+
+    int n = m_FFTSize / 2;
+
+    for (int i = 1; i < n; ++i) {
+        Complex c1(realP1[i], imagP1[i]);
+        Complex c2(realP2[i], imagP2[i]);
+
+        double mag1 = abs(c1);
+        double mag2 = abs(c2);
+
+        // Interpolate magnitudes in decibels
+        double mag1db = 20.0 * log10(mag1);
+        double mag2db = 20.0 * log10(mag2);
+
+        double s1 = s1base;
+        double s2 = s2base;
+
+        double magdbdiff = mag1db - mag2db;
+
+        // Empirical tweak to retain higher-frequency zeroes
+        double threshold =  (i > 16) ? 5.0 : 2.0;
+
+        if (magdbdiff < -threshold && mag1db < 0.0) {
+            s1 = pow(s1, 0.75);
+            s2 = 1.0 - s1;
+        } else if (magdbdiff > threshold && mag2db < 0.0) {
+            s2 = pow(s2, 0.75);
+            s1 = 1.0 - s2;
+        }
+
+        // Average magnitude by decibels instead of linearly
+        double magdb = s1 * mag1db + s2 * mag2db;
+        double mag = pow(10.0, 0.05 * magdb);
+
+        // Now, deal with phase
+        double phase1 = arg(c1);
+        double phase2 = arg(c2);
+
+        double deltaPhase1 = phase1 - lastPhase1;
+        double deltaPhase2 = phase2 - lastPhase2;
+        lastPhase1 = phase1;
+        lastPhase2 = phase2;
+
+        // Unwrap phase deltas
+        if (deltaPhase1 > M_PI)
+            deltaPhase1 -= 2.0 * M_PI;
+        if (deltaPhase1 < -M_PI)
+            deltaPhase1 += 2.0 * M_PI;
+        if (deltaPhase2 > M_PI)
+            deltaPhase2 -= 2.0 * M_PI;
+        if (deltaPhase2 < -M_PI)
+            deltaPhase2 += 2.0 * M_PI;
+
+        // Blend group-delays
+        double deltaPhaseBlend;
+
+        if (deltaPhase1 - deltaPhase2 > M_PI)
+            deltaPhaseBlend = s1 * deltaPhase1 + s2 * (2.0 * M_PI + deltaPhase2);
+        else if (deltaPhase2 - deltaPhase1 > M_PI)
+            deltaPhaseBlend = s1 * (2.0 * M_PI + deltaPhase1) + s2 * deltaPhase2;
+        else
+            deltaPhaseBlend = s1 * deltaPhase1 + s2 * deltaPhase2;
+
+        phaseAccum += deltaPhaseBlend;
+
+        // Unwrap
+        if (phaseAccum > M_PI)
+            phaseAccum -= 2.0 * M_PI;
+        if (phaseAccum < -M_PI)
+            phaseAccum += 2.0 * M_PI;
+
+        Complex c = complexFromMagnitudePhase(mag, phaseAccum);
+
+        realP[i] = static_cast<float>(c.real());
+        imagP[i] = static_cast<float>(c.imag());
+    }
+}
+
+double FFTFrame::extractAverageGroupDelay()
+{
+    float* realP = realData();
+    float* imagP = imagData();
+
+    double aveSum = 0.0;
+    double weightSum = 0.0;
+    double lastPhase = 0.0;
+
+    int halfSize = fftSize() / 2;
+
+    const double kSamplePhaseDelay = (2.0 * M_PI) / double(fftSize());
+
+    // Calculate weighted average group delay
+    for (int i = 0; i < halfSize; i++) {
+        Complex c(realP[i], imagP[i]);
+        double mag = abs(c);
+        double phase = arg(c);
+
+        double deltaPhase = phase - lastPhase;
+        lastPhase = phase;
+
+        // Unwrap
+        if (deltaPhase < -M_PI)
+            deltaPhase += 2.0 * M_PI;
+        if (deltaPhase > M_PI)
+            deltaPhase -= 2.0 * M_PI;
+
+        aveSum += mag * deltaPhase;
+        weightSum += mag;
+    }
+
+    // Note how we invert the phase delta wrt frequency since this is how group delay is defined
+    double ave = aveSum / weightSum;
+    double aveSampleDelay = -ave / kSamplePhaseDelay;
+
+    // Leave 20 sample headroom (for leading edge of impulse)
+    if (aveSampleDelay > 20.0)
+        aveSampleDelay -= 20.0;
+
+    // Remove average group delay (minus 20 samples for headroom)
+    addConstantGroupDelay(-aveSampleDelay);
+
+    // Remove DC offset
+    realP[0] = 0.0f;
+
+    return aveSampleDelay;
+}
+
+void FFTFrame::addConstantGroupDelay(double sampleFrameDelay)
+{
+    int halfSize = fftSize() / 2;
+
+    float* realP = realData();
+    float* imagP = imagData();
+
+    const double kSamplePhaseDelay = (2.0 * M_PI) / double(fftSize());
+
+    double phaseAdj = -sampleFrameDelay * kSamplePhaseDelay;
+
+    // Add constant group delay
+    for (int i = 1; i < halfSize; i++) {
+        Complex c(realP[i], imagP[i]);
+        double mag = abs(c);
+        double phase = arg(c);
+
+        phase += i * phaseAdj;
+
+        Complex c2 = complexFromMagnitudePhase(mag, phase);
+
+        realP[i] = static_cast<float>(c2.real());
+        imagP[i] = static_cast<float>(c2.imag());
+    }
+}
+
+#ifndef NDEBUG
+void FFTFrame::print()
+{
+    FFTFrame& frame = *this;
+    float* realP = frame.realData();
+    float* imagP = frame.imagData();
+    printf("**** \n");
+    printf("DC = %f : nyquist = %f\n", realP[0], imagP[0]);
+
+    int n = m_FFTSize / 2;
+
+    for (int i = 1; i < n; i++) {
+        double mag = sqrt(realP[i] * realP[i] + imagP[i] * imagP[i]);
+        double phase = atan2(realP[i], imagP[i]);
+
+        printf("[%d] (%f %f)\n", i, mag, phase);
+    }
+    printf("****\n");
+}
+#endif // NDEBUG
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/FFTFrame.h b/WebCore/platform/audio/FFTFrame.h
new file mode 100644 (file)
index 0000000..6147fc1
--- /dev/null
@@ -0,0 +1,102 @@
+/*
+ * 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 FFTFrame_h
+#define FFTFrame_h
+
+#include "AudioArray.h"
+
+#if OS(DARWIN)
+#include <Accelerate/Accelerate.h>
+#endif
+
+#include <wtf/PassOwnPtr.h>
+#include <wtf/Platform.h>
+
+namespace WebCore {
+
+// Defines the interface for an "FFT frame", an object which is able to perform a forward
+// and reverse FFT, internally storing the resultant frequency-domain data.
+
+class FFTFrame {
+public:
+    // The constructors, destructor, and methods up to the CROSS-PLATFORM section have platform-dependent implementations.
+
+    FFTFrame(unsigned fftSize);
+    FFTFrame(); // creates a blank/empty frame for later use with createInterpolatedFrame()
+    FFTFrame(const FFTFrame& frame);
+    ~FFTFrame();
+
+    static void cleanup();
+    void doFFT(float* data);
+    void doInverseFFT(float* data);
+    void multiply(const FFTFrame& frame); // multiplies ourself with frame : effectively operator*=()
+
+    float* realData() const;
+    float* imagData() const;
+
+    void print(); // for debugging
+
+    // CROSS-PLATFORM
+    // The remaining public methods have cross-platform implementations:
+
+    // Interpolates from frame1 -> frame2 as x goes from 0.0 -> 1.0
+    static PassOwnPtr<FFTFrame> createInterpolatedFrame(const FFTFrame& frame1, const FFTFrame& frame2, double x);
+
+    void doPaddedFFT(float* data, size_t dataSize); // zero-padding with dataSize <= fftSize
+    double extractAverageGroupDelay();
+    void addConstantGroupDelay(double sampleFrameDelay);
+
+    unsigned fftSize() const { return m_FFTSize; }
+    unsigned log2FFTSize() const { return m_log2FFTSize; }
+
+private:
+    unsigned m_FFTSize;
+    unsigned m_log2FFTSize;
+
+    void interpolateFrequencyComponents(const FFTFrame& frame1, const FFTFrame& frame2, double x);
+
+#if OS(DARWIN)
+    DSPSplitComplex& dspSplitComplex() { return m_frame; }
+    DSPSplitComplex dspSplitComplex() const { return m_frame; }
+
+    static FFTSetup fftSetupForSize(unsigned fftSize);
+
+    static FFTSetup* fftSetups;
+
+    FFTSetup m_FFTSetup;
+
+    DSPSplitComplex m_frame;
+    AudioFloatArray m_realData;
+    AudioFloatArray m_imagData;
+#endif // OS(DARWIN)
+};
+
+} // namespace WebCore
+
+#endif // FFTFrame_h
diff --git a/WebCore/platform/audio/mac/FFTFrameMac.cpp b/WebCore/platform/audio/mac/FFTFrameMac.cpp
new file mode 100644 (file)
index 0000000..0f7efb7
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * 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.
+ */
+
+// Mac OS X - specific FFTFrame implementation
+
+#include "config.h"
+
+#if ENABLE(WEB_AUDIO)
+
+#include "FFTFrame.h"
+
+namespace WebCore {
+
+const int kMaxFFTPow2Size = 24;
+
+FFTSetup* FFTFrame::fftSetups = 0;
+
+// Normal constructor: allocates for a given fftSize
+FFTFrame::FFTFrame(unsigned fftSize)
+    : m_realData(fftSize)
+    , m_imagData(fftSize)
+{
+    m_FFTSize = fftSize;
+    m_log2FFTSize = static_cast<unsigned>(log2(fftSize));
+
+    // We only allow power of two
+    ASSERT(1UL << m_log2FFTSize == m_FFTSize);
+
+    // Lazily create and share fftSetup with other frames
+    m_FFTSetup = fftSetupForSize(fftSize);
+
+    // Setup frame data
+    m_frame.realp = m_realData.data();
+    m_frame.imagp = m_imagData.data();
+}
+
+// Creates a blank/empty frame (interpolate() must later be called)
+FFTFrame::FFTFrame()
+    : m_realData(0)
+    , m_imagData(0)
+{
+    // Later will be set to correct values when interpolate() is called
+    m_frame.realp = 0;
+    m_frame.imagp = 0;
+
+    m_FFTSize = 0;
+    m_log2FFTSize = 0;
+}
+
+// Copy constructor
+FFTFrame::FFTFrame(const FFTFrame& frame)
+    : m_FFTSize(frame.m_FFTSize)
+    , m_log2FFTSize(frame.m_log2FFTSize)
+    , m_FFTSetup(frame.m_FFTSetup)
+    , m_realData(frame.m_FFTSize)
+    , m_imagData(frame.m_FFTSize)
+{
+    // Setup frame data
+    m_frame.realp = m_realData.data();
+    m_frame.imagp = m_imagData.data();
+
+    // Copy/setup frame data
+    unsigned nbytes = sizeof(float) * m_FFTSize;
+    memcpy(realData(), frame.m_frame.realp, nbytes);
+    memcpy(imagData(), frame.m_frame.imagp, nbytes);
+}
+
+FFTFrame::~FFTFrame()
+{
+}
+
+void FFTFrame::multiply(const FFTFrame& frame)
+{
+    FFTFrame& frame1 = *this;
+    const FFTFrame& frame2 = frame;
+
+    float* realP1 = frame1.realData();
+    float* imagP1 = frame1.imagData();
+    const float* realP2 = frame2.realData();
+    const float* imagP2 = frame2.imagData();
+
+    // Scale accounts for vecLib's peculiar scaling
+    // This ensures the right scaling all the way back to inverse FFT
+    float scale = 0.5f;
+
+    // Multiply packed DC/nyquist component
+    realP1[0] *= scale * realP2[0];
+    imagP1[0] *= scale * imagP2[0];
+
+    // Multiply the rest, skipping packed DC/Nyquist components
+    DSPSplitComplex sc1 = frame1.dspSplitComplex();
+    sc1.realp++;
+    sc1.imagp++;
+
+    DSPSplitComplex sc2 = frame2.dspSplitComplex();
+    sc2.realp++;
+    sc2.imagp++;
+
+    unsigned halfSize = m_FFTSize / 2;
+
+    // Complex multiply
+    vDSP_zvmul(&sc1, 1, &sc2, 1, &sc1, 1, halfSize - 1, 1 /* normal multiplication */);
+
+    // We've previously scaled the packed part, now scale the rest.....
+    vDSP_vsmul(sc1.realp, 1, &scale, sc1.realp, 1, halfSize - 1);
+    vDSP_vsmul(sc1.imagp, 1, &scale, sc1.imagp, 1, halfSize - 1);
+}
+
+void FFTFrame::doFFT(float* data)
+{
+    vDSP_ctoz((DSPComplex*)data, 2, &m_frame, 1, m_FFTSize / 2);
+    vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_FORWARD);
+}
+
+void FFTFrame::doInverseFFT(float* data)
+{
+    vDSP_fft_zrip(m_FFTSetup, &m_frame, 1, m_log2FFTSize, FFT_INVERSE);
+    vDSP_ztoc(&m_frame, 1, (DSPComplex*)data, 2, m_FFTSize / 2);
+
+    // Do final scaling so that x == IFFT(FFT(x))
+    float scale = 0.5f / m_FFTSize;
+    vDSP_vsmul(data, 1, &scale, data, 1, m_FFTSize);
+}
+
+FFTSetup FFTFrame::fftSetupForSize(unsigned fftSize)
+{
+    if (!fftSetups) {
+        fftSetups = (FFTSetup*)malloc(sizeof(FFTSetup) * kMaxFFTPow2Size);
+        memset(fftSetups, 0, sizeof(FFTSetup) * kMaxFFTPow2Size);
+    }
+
+    int pow2size = static_cast<int>(log2(fftSize));
+    ASSERT(pow2size < kMaxFFTPow2Size);
+    if (!fftSetups[pow2size])
+        fftSetups[pow2size] = vDSP_create_fftsetup(pow2size, FFT_RADIX2);
+
+    return fftSetups[pow2size];
+}
+
+void FFTFrame::cleanup()
+{
+    if (!fftSetups)
+        return;
+
+    for (int i = 0; i < kMaxFFTPow2Size; ++i) {
+        if (fftSetups[i])
+            vDSP_destroy_fftsetup(fftSetups[i]);
+    }
+
+    free(fftSetups);
+    fftSetups = 0;
+}
+
+float* FFTFrame::realData() const
+{
+    return m_frame.realp;
+}
+    
+float* FFTFrame::imagData() const
+{
+    return m_frame.imagp;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)