2010-09-09 Chris Rogers <crogers@google.com>
authorcrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Sep 2010 00:40:23 +0000 (00:40 +0000)
committercrogers@google.com <crogers@google.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Fri, 10 Sep 2010 00:40:23 +0000 (00:40 +0000)
        Reviewed by Kenneth Russell.

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

        No new tests since audio API is not yet implemented.

        * platform/audio/Biquad.cpp: Added.
        (WebCore::Biquad::Biquad):
        (WebCore::Biquad::process):
        (WebCore::Biquad::processFast):
        (WebCore::Biquad::processSliceFast):
        (WebCore::Biquad::reset):
        (WebCore::Biquad::setLowpassParams):
        (WebCore::Biquad::setHighpassParams):
        (WebCore::Biquad::setLowShelfParams):
        (WebCore::Biquad::setZeroPolePairs):
        (WebCore::Biquad::setAllpassPole):
        * platform/audio/Biquad.h: Added.
        (WebCore::Biquad::~Biquad):

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

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

index 23aa646de224114602791dbd9580efa1f6f9d8d0..ebf61641fda96ae24ddc0022265ae45d5901375b 100644 (file)
@@ -1,3 +1,26 @@
+2010-09-09  Chris Rogers  <crogers@google.com>
+
+        Reviewed by Kenneth Russell.
+
+        audio engine: add Biquad files
+        https://bugs.webkit.org/show_bug.cgi?id=44795
+
+        No new tests since audio API is not yet implemented.
+
+        * platform/audio/Biquad.cpp: Added.
+        (WebCore::Biquad::Biquad):
+        (WebCore::Biquad::process):
+        (WebCore::Biquad::processFast):
+        (WebCore::Biquad::processSliceFast):
+        (WebCore::Biquad::reset):
+        (WebCore::Biquad::setLowpassParams):
+        (WebCore::Biquad::setHighpassParams):
+        (WebCore::Biquad::setLowShelfParams):
+        (WebCore::Biquad::setZeroPolePairs):
+        (WebCore::Biquad::setAllpassPole):
+        * platform/audio/Biquad.h: Added.
+        (WebCore::Biquad::~Biquad):
+
 2010-09-09  Chris Rogers  <crogers@google.com>
 
         Reviewed by Kenneth Russell.
diff --git a/WebCore/platform/audio/Biquad.cpp b/WebCore/platform/audio/Biquad.cpp
new file mode 100644 (file)
index 0000000..6918dd6
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * 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 "Biquad.h"
+
+#include "Accelerate.h"
+#include <algorithm>
+#include <float.h>
+#include <math.h>
+#include <stdio.h>
+
+namespace WebCore {
+
+const int kBufferSize = 1024;
+
+Biquad::Biquad()
+{
+#if OS(DARWIN)
+    // Allocate two samples more for filter history
+    m_inputBuffer.resize(kBufferSize + 2);
+    m_outputBuffer.resize(kBufferSize + 2);
+#endif
+
+    // Initialize as pass-thru (straight-wire, no filter effect)
+    m_a0 = 1.0;
+    m_a1 = 0.0;
+    m_a2 = 0.0;
+    m_b1 = 0.0;
+    m_b2 = 0.0;
+
+    m_g = 1.0;
+
+    reset(); // clear filter memory
+}
+
+void Biquad::process(const float* sourceP, float* destP, size_t framesToProcess)
+{
+#if OS(DARWIN)
+    // Use vecLib if available
+    processFast(sourceP, destP, framesToProcess);
+#else
+    int n = framesToProcess;
+
+    // Create local copies of member variables
+    double x1 = m_x1;
+    double x2 = m_x2;
+    double y1 = m_y1;
+    double y2 = m_y2;
+
+    double a0 = m_a0;
+    double a1 = m_a1;
+    double a2 = m_a2;
+    double b1 = m_b1;
+    double b2 = m_b2;
+
+    while (n--) {
+        // FIXME: this can be optimized by pipelining the multiply adds...
+        float x = *sourceP++;
+        float y = a0*x + a1*x1 + a2*x2 - b1*y1 - b2*y2;
+
+        y *= m_g;
+
+        *destP++ = y;
+
+        // Update state variables
+        x2 = x1;
+        x1 = x;
+        y2 = y1;
+        y1 = y;
+    }
+
+    // Local variables back to member
+    m_x1 = x1;
+    m_x2 = x2;
+    m_y1 = y1;
+    m_y2 = y2;
+
+    m_a0 = a0;
+    m_a1 = a1;
+    m_a2 = a2;
+    m_b1 = b1;
+    m_b2 = b2;
+#endif
+}
+
+#if OS(DARWIN)
+
+// Here we have optimized version using Accelerate.framework
+
+void Biquad::processFast(const float* sourceP, float* destP, size_t framesToProcess)
+{
+    // Filter coefficients
+    double B[5];
+    B[0] = m_a0;
+    B[1] = m_a1;
+    B[2] = m_a2;
+    B[3] = m_b1;
+    B[4] = m_b2;
+
+    double* inputP = m_inputBuffer.data();
+    double* outputP = m_outputBuffer.data();
+
+    double* input2P = inputP + 2;
+    double* output2P = outputP + 2;
+
+    // Break up processing into smaller slices (kBufferSize) if necessary.
+
+    int n = framesToProcess;
+
+    while (n > 0) {
+        int framesThisTime = n < kBufferSize ? n : kBufferSize;
+
+        // Copy input to input buffer
+        for (int i = 0; i < framesThisTime; ++i)
+            input2P[i] = *sourceP++;
+
+        processSliceFast(inputP, outputP, B, framesThisTime);
+
+        // Copy output buffer to output (converts float -> double).
+        for (int i = 0; i < framesThisTime; ++i)
+            *destP++ = static_cast<float>(output2P[i]);
+
+        n -= framesThisTime;
+    }
+}
+
+void Biquad::processSliceFast(double* sourceP, double* destP, double* coefficientsP, size_t framesToProcess)
+{
+    // Use double-precision for filter stability
+    vDSP_deq22D(sourceP, 1, coefficientsP, destP, 1, framesToProcess);
+
+    // Save history.  Note that sourceP and destP reference m_inputBuffer and m_outputBuffer respectively.
+    // These buffers are allocated (in the constructor) with space for two extra samples so it's OK to access
+    // array values two beyond framesToProcess.
+    sourceP[0] = sourceP[framesToProcess - 2 + 2];
+    sourceP[1] = sourceP[framesToProcess - 1 + 2];
+    destP[0] = destP[framesToProcess - 2 + 2];
+    destP[1] = destP[framesToProcess - 1 + 2];
+}
+
+#endif // OS(DARWIN)
+
+
+void Biquad::reset()
+{
+    m_x1 = m_x2 = m_y1 = m_y2 = 0.0;
+
+#if OS(DARWIN)
+    // Two extra samples for filter history
+    double* inputP = m_inputBuffer.data();
+    inputP[0] = 0.0;
+    inputP[1] = 0.0;
+
+    double* outputP = m_outputBuffer.data();
+    outputP[0] = 0.0;
+    outputP[1] = 0.0;
+#endif
+}
+
+void Biquad::setLowpassParams(double cutoff, double resonance)
+{
+    resonance = std::max(0.0, resonance); // can't go negative
+
+    double g = pow(10.0, 0.05 * resonance);
+    double d = sqrt((4.0 - sqrt(16.0 - 16.0 / (g * g))) / 2.0);
+
+    // Compute biquad coefficients for lopass filter
+    double theta = M_PI * cutoff;
+    double sn = 0.5 * d * sin(theta);
+    double beta = 0.5 * (1.0 - sn) / (1.0 + sn);
+    double gamma = (0.5 + beta) * cos(theta);
+    double alpha = 0.25 * (0.5 + beta - gamma);
+
+    m_a0 = 2.0 * alpha;
+    m_a1 = 2.0 * 2.0*alpha;
+    m_a2 = 2.0 * alpha;
+    m_b1 = 2.0 * -gamma;
+    m_b2 = 2.0 * beta;
+}
+
+void Biquad::setHighpassParams(double cutoff, double resonance)
+{
+    resonance = std::max(0.0, resonance); // can't go negative
+
+    double g = pow(10.0, 0.05 * resonance);
+    double d = sqrt((4.0 - sqrt(16.0 - 16.0 / (g * g))) / 2.0);
+
+    // Compute biquad coefficients for highpass filter
+    double theta = M_PI * cutoff;
+    double sn = 0.5 * d * sin(theta);
+    double beta = 0.5 * (1.0 - sn) / (1.0 + sn);
+    double gamma = (0.5 + beta) * cos(theta);
+    double alpha = 0.25 * (0.5 + beta + gamma);
+
+    m_a0 = 2.0 * alpha;
+    m_a1 = 2.0 * -2.0*alpha;
+    m_a2 = 2.0 * alpha;
+    m_b1 = 2.0 * -gamma;
+    m_b2 = 2.0 * beta;
+}
+
+void Biquad::setLowShelfParams(double cutoff, double dbGain)
+{
+    double theta = M_PI * cutoff;
+
+    double A = pow(10.0, dbGain / 40.0);
+    double S = 1.0; // filter slope (1.0 is max value)
+    double alpha = 0.5 * sin(theta) * sqrt((A + 1.0 / A) * (1.0 / S - 1.0) + 2.0);
+
+    double k = cos(theta);
+    double k2 = 2.0 * sqrt(A) * alpha;
+
+    double b0 = A * ((A + 1.0) - (A - 1.0) * k + k2);
+    double b1 = 2.0 * A * ((A - 1.0) - (A + 1.0) * k);
+    double b2 = A * ((A + 1.0) - (A - 1.0) * k - k2);
+    double a0 = (A + 1.0) + (A - 1.0) * k + k2;
+    double a1 = -2.0 * ((A - 1.0) + (A + 1.0) * k);
+    double a2 = (A + 1.0) + (A - 1.0) * k - k2;
+
+    double a0Inverse = 1.0 / a0;
+    
+    m_a0 = b0 * a0Inverse;
+    m_a1 = b1 * a0Inverse;
+    m_a2 = b2 * a0Inverse;
+    m_b1 = a1 * a0Inverse;
+    m_b2 = a2 * a0Inverse;
+}
+
+void Biquad::setZeroPolePairs(const Complex &zero, const Complex &pole)
+{
+    m_a0 = 1.0;
+    m_a1 = -2.0 * zero.real();
+
+    double zeroMag = abs(zero);
+    m_a2 = zeroMag * zeroMag;
+
+    m_b1 = -2.0 * pole.real();
+
+    double poleMag = abs(pole);
+    m_b2 = poleMag * poleMag;
+}
+
+void Biquad::setAllpassPole(const Complex &pole)
+{
+    Complex zero = Complex(1.0, 0.0) / pole;
+    setZeroPolePairs(zero, pole);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
diff --git a/WebCore/platform/audio/Biquad.h b/WebCore/platform/audio/Biquad.h
new file mode 100644 (file)
index 0000000..d68bf4e
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * 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 Biquad_h
+#define Biquad_h
+
+#include "AudioArray.h"
+#include <sys/types.h>
+#include <wtf/Complex.h>
+#include <wtf/Platform.h>
+namespace WebCore {
+
+// A basic biquad (two-zero / two-pole digital filter)
+//
+// It can be configured to a number of common and very useful filters:
+//    lowpass, highpass, shelving, parameteric, notch, allpass, ...
+
+class Biquad {
+public:   
+    Biquad();
+    virtual ~Biquad() { }
+
+    void process(const float* sourceP, float* destP, size_t framesToProcess);
+
+    // cutoff is 0-1 normalized, resonance is in dB >= 0.0
+    void setLowpassParams(double cutoff, double resonance);
+    void setHighpassParams(double cutoff, double resonance);
+
+    void setLowShelfParams(double cutoff, double dbGain);
+
+    // FIXME: need to implement a few more common filters
+    // void setHighShelfParams(double cutoff, double dbGain);
+    // void setParametricEQParams(double cutoff, double resonance);
+
+    // Set the biquad coefficients given a single zero (other zero will be conjugate)
+    // and a single pole (other pole will be conjugate)
+    void setZeroPolePairs(const Complex& zero, const Complex& pole);
+
+    // Set the biquad coefficients given a single pole (other pole will be conjugate)
+    // (The zeroes will be the inverse of the poles)
+    void setAllpassPole(const Complex& pole);
+
+    // Resets filter state
+    void reset();
+
+private:
+    // Filter coefficients
+    double m_a0;
+    double m_a1;
+    double m_a2;
+    double m_b1;
+    double m_b2;
+
+    double m_g;
+
+    // Filter memory
+    double m_x1; // input delayed by 1 sample
+    double m_x2; // input delayed by 2 samples
+    double m_y1; // output delayed by 1 sample
+    double m_y2; // output delayed by 2 samples
+
+#if OS(DARWIN)
+    void processFast(const float* sourceP, float* destP, size_t framesToProcess);
+    void processSliceFast(double* sourceP, double* destP, double* coefficientsP, size_t framesToProcess);
+
+    AudioDoubleArray m_inputBuffer;
+    AudioDoubleArray m_outputBuffer;
+#endif
+};
+
+} // namespace WebCore
+
+#endif // Biquad_h