diff options
Diffstat (limited to 'Source/WebCore/platform/audio/Biquad.cpp')
-rw-r--r-- | Source/WebCore/platform/audio/Biquad.cpp | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/Source/WebCore/platform/audio/Biquad.cpp b/Source/WebCore/platform/audio/Biquad.cpp new file mode 100644 index 0000000..20143e3 --- /dev/null +++ b/Source/WebCore/platform/audio/Biquad.cpp @@ -0,0 +1,282 @@ +/* + * 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 <algorithm> +#include <stdio.h> +#include <wtf/MathExtras.h> + +#if OS(DARWIN) +#include <Accelerate/Accelerate.h> +#endif + +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 = piDouble * 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 = piDouble * 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 = piDouble * 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) |