Bug 864091 - Part 1: Import the Dynamics Compressor implementation from Blink; r=padenot
authorEhsan Akhgari <ehsan@mozilla.com>
Sat, 20 Apr 2013 19:46:24 -0400
changeset 140475 50ad07904584f2c62f551730bca68dacd1e1d7bc
parent 140474 3c1d661638c3f7c0690ab9c288ed2283abdf567e
child 140476 c2ead35253b026b9c80209362d801bc1a3c8da72
push id2579
push userakeybl@mozilla.com
push dateMon, 24 Jun 2013 18:52:47 +0000
treeherdermozilla-beta@b69b7de8a05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerspadenot
bugs864091, 148720
milestone23.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 864091 - Part 1: Import the Dynamics Compressor implementation from Blink; r=padenot The original code was copied from Blink SVN revision 148720.
content/media/webaudio/blink/DenormalDisabler.h
content/media/webaudio/blink/DynamicsCompressor.cpp
content/media/webaudio/blink/DynamicsCompressor.h
content/media/webaudio/blink/DynamicsCompressorKernel.cpp
content/media/webaudio/blink/DynamicsCompressorKernel.h
content/media/webaudio/blink/README
content/media/webaudio/blink/ZeroPole.cpp
content/media/webaudio/blink/ZeroPole.h
toolkit/content/license.html
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/DenormalDisabler.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2011, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. 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 INC. 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 DenormalDisabler_h
+#define DenormalDisabler_h
+
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+// Deal with denormals. They can very seriously impact performance on x86.
+
+// Define HAVE_DENORMAL if we support flushing denormals to zero.
+#if OS(WINDOWS) && COMPILER(MSVC)
+#define HAVE_DENORMAL
+#endif
+
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+#define HAVE_DENORMAL
+#endif
+
+#ifdef HAVE_DENORMAL
+class DenormalDisabler {
+public:
+    DenormalDisabler()
+            : m_savedCSR(0)
+    {
+#if OS(WINDOWS) && COMPILER(MSVC)
+        // Save the current state, and set mode to flush denormals.
+        //
+        // http://stackoverflow.com/questions/637175/possible-bug-in-controlfp-s-may-not-restore-control-word-correctly
+        _controlfp_s(&m_savedCSR, 0, 0);
+        unsigned int unused;
+        _controlfp_s(&unused, _DN_FLUSH, _MCW_DN);
+#else
+        m_savedCSR = getCSR();
+        setCSR(m_savedCSR | 0x8040);
+#endif
+    }
+
+    ~DenormalDisabler()
+    {
+#if OS(WINDOWS) && COMPILER(MSVC)
+        unsigned int unused;
+        _controlfp_s(&unused, m_savedCSR, _MCW_DN);
+#else
+        setCSR(m_savedCSR);
+#endif
+    }
+
+    // This is a nop if we can flush denormals to zero in hardware.
+    static inline float flushDenormalFloatToZero(float f)
+    {
+#if OS(WINDOWS) && COMPILER(MSVC) && (!_M_IX86_FP)
+        // For systems using x87 instead of sse, there's no hardware support
+        // to flush denormals automatically. Hence, we need to flush
+        // denormals to zero manually.
+        return (fabs(f) < FLT_MIN) ? 0.0f : f;
+#else
+        return f;
+#endif
+    }
+private:
+#if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+    inline int getCSR()
+    {
+        int result;
+        asm volatile("stmxcsr %0" : "=m" (result));
+        return result;
+    }
+
+    inline void setCSR(int a)
+    {
+        int temp = a;
+        asm volatile("ldmxcsr %0" : : "m" (temp));
+    }
+
+#endif
+
+    unsigned int m_savedCSR;
+};
+
+#else
+// FIXME: add implementations for other architectures and compilers
+class DenormalDisabler {
+public:
+    DenormalDisabler() { }
+
+    // Assume the worst case that other architectures and compilers
+    // need to flush denormals to zero manually.
+    static inline float flushDenormalFloatToZero(float f)
+    {
+        return (fabs(f) < FLT_MIN) ? 0.0f : f;
+    }
+};
+
+#endif
+
+} // WebCore
+
+#undef HAVE_DENORMAL
+#endif // DenormalDisabler_h
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/DynamicsCompressor.cpp
@@ -0,0 +1,286 @@
+/*
+ * Copyright (C) 2011 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 "DynamicsCompressor.h"
+
+#include "AudioBus.h"
+#include "AudioUtilities.h"
+#include <wtf/MathExtras.h>
+
+namespace WebCore {
+
+using namespace AudioUtilities;
+    
+DynamicsCompressor::DynamicsCompressor(float sampleRate, unsigned numberOfChannels)
+    : m_numberOfChannels(numberOfChannels)
+    , m_sampleRate(sampleRate)
+    , m_compressor(sampleRate, numberOfChannels)
+{
+    // Uninitialized state - for parameter recalculation.
+    m_lastFilterStageRatio = -1;
+    m_lastAnchor = -1;
+    m_lastFilterStageGain = -1;
+
+    setNumberOfChannels(numberOfChannels);
+    initializeParameters();
+}
+
+void DynamicsCompressor::setParameterValue(unsigned parameterID, float value)
+{
+    ASSERT(parameterID < ParamLast);
+    if (parameterID < ParamLast)
+        m_parameters[parameterID] = value;
+}
+
+void DynamicsCompressor::initializeParameters()
+{
+    // Initializes compressor to default values.
+    
+    m_parameters[ParamThreshold] = -24; // dB
+    m_parameters[ParamKnee] = 30; // dB
+    m_parameters[ParamRatio] = 12; // unit-less
+    m_parameters[ParamAttack] = 0.003f; // seconds
+    m_parameters[ParamRelease] = 0.250f; // seconds
+    m_parameters[ParamPreDelay] = 0.006f; // seconds
+
+    // Release zone values 0 -> 1.
+    m_parameters[ParamReleaseZone1] = 0.09f;
+    m_parameters[ParamReleaseZone2] = 0.16f;
+    m_parameters[ParamReleaseZone3] = 0.42f;
+    m_parameters[ParamReleaseZone4] = 0.98f;
+
+    m_parameters[ParamFilterStageGain] = 4.4f; // dB
+    m_parameters[ParamFilterStageRatio] = 2;
+    m_parameters[ParamFilterAnchor] = 15000 / nyquist();
+    
+    m_parameters[ParamPostGain] = 0; // dB
+    m_parameters[ParamReduction] = 0; // dB
+
+    // Linear crossfade (0 -> 1).
+    m_parameters[ParamEffectBlend] = 1;
+}
+
+float DynamicsCompressor::parameterValue(unsigned parameterID)
+{
+    ASSERT(parameterID < ParamLast);
+    return m_parameters[parameterID];
+}
+
+void DynamicsCompressor::setEmphasisStageParameters(unsigned stageIndex, float gain, float normalizedFrequency /* 0 -> 1 */)
+{
+    float gk = 1 - gain / 20;
+    float f1 = normalizedFrequency * gk;
+    float f2 = normalizedFrequency / gk;
+    float r1 = expf(-f1 * piFloat);
+    float r2 = expf(-f2 * piFloat);
+
+    ASSERT(m_numberOfChannels == m_preFilterPacks.size());
+
+    for (unsigned i = 0; i < m_numberOfChannels; ++i) {
+        // Set pre-filter zero and pole to create an emphasis filter.
+        ZeroPole& preFilter = m_preFilterPacks[i]->filters[stageIndex];
+        preFilter.setZero(r1);
+        preFilter.setPole(r2);
+
+        // Set post-filter with zero and pole reversed to create the de-emphasis filter.
+        // If there were no compressor kernel in between, they would cancel each other out (allpass filter).
+        ZeroPole& postFilter = m_postFilterPacks[i]->filters[stageIndex];
+        postFilter.setZero(r2);
+        postFilter.setPole(r1);
+    }
+}
+
+void DynamicsCompressor::setEmphasisParameters(float gain, float anchorFreq, float filterStageRatio)
+{
+    setEmphasisStageParameters(0, gain, anchorFreq);
+    setEmphasisStageParameters(1, gain, anchorFreq / filterStageRatio);
+    setEmphasisStageParameters(2, gain, anchorFreq / (filterStageRatio * filterStageRatio));
+    setEmphasisStageParameters(3, gain, anchorFreq / (filterStageRatio * filterStageRatio * filterStageRatio));
+}
+
+void DynamicsCompressor::process(const AudioBus* sourceBus, AudioBus* destinationBus, unsigned framesToProcess)
+{
+    // Though numberOfChannels is retrived from destinationBus, we still name it numberOfChannels instead of numberOfDestinationChannels.
+    // It's because we internally match sourceChannels's size to destinationBus by channel up/down mix. Thus we need numberOfChannels
+    // to do the loop work for both m_sourceChannels and m_destinationChannels.
+
+    unsigned numberOfChannels = destinationBus->numberOfChannels();
+    unsigned numberOfSourceChannels = sourceBus->numberOfChannels();
+
+    ASSERT(numberOfChannels == m_numberOfChannels && numberOfSourceChannels);
+
+    if (numberOfChannels != m_numberOfChannels || !numberOfSourceChannels) {
+        destinationBus->zero();
+        return;
+    }
+
+    switch (numberOfChannels) {
+    case 2: // stereo
+        m_sourceChannels[0] = sourceBus->channel(0)->data();
+
+        if (numberOfSourceChannels > 1)
+            m_sourceChannels[1] = sourceBus->channel(1)->data();
+        else
+            // Simply duplicate mono channel input data to right channel for stereo processing.
+            m_sourceChannels[1] = m_sourceChannels[0];
+
+        break;
+    default:
+        // FIXME : support other number of channels.
+        ASSERT_NOT_REACHED();
+        destinationBus->zero();
+        return;
+    }
+
+    for (unsigned i = 0; i < numberOfChannels; ++i)
+        m_destinationChannels[i] = destinationBus->channel(i)->mutableData();
+
+    float filterStageGain = parameterValue(ParamFilterStageGain);
+    float filterStageRatio = parameterValue(ParamFilterStageRatio);
+    float anchor = parameterValue(ParamFilterAnchor);
+
+    if (filterStageGain != m_lastFilterStageGain || filterStageRatio != m_lastFilterStageRatio || anchor != m_lastAnchor) {
+        m_lastFilterStageGain = filterStageGain;
+        m_lastFilterStageRatio = filterStageRatio;
+        m_lastAnchor = anchor;
+
+        setEmphasisParameters(filterStageGain, anchor, filterStageRatio);
+    }
+
+    // Apply pre-emphasis filter.
+    // Note that the final three stages are computed in-place in the destination buffer.
+    for (unsigned i = 0; i < numberOfChannels; ++i) {
+        const float* sourceData = m_sourceChannels[i];
+        float* destinationData = m_destinationChannels[i];
+        ZeroPole* preFilters = m_preFilterPacks[i]->filters;
+
+        preFilters[0].process(sourceData, destinationData, framesToProcess);
+        preFilters[1].process(destinationData, destinationData, framesToProcess);
+        preFilters[2].process(destinationData, destinationData, framesToProcess);
+        preFilters[3].process(destinationData, destinationData, framesToProcess);
+    }
+
+    float dbThreshold = parameterValue(ParamThreshold);
+    float dbKnee = parameterValue(ParamKnee);
+    float ratio = parameterValue(ParamRatio);
+    float attackTime = parameterValue(ParamAttack);
+    float releaseTime = parameterValue(ParamRelease);
+    float preDelayTime = parameterValue(ParamPreDelay);
+
+    // This is effectively a master volume on the compressed signal (pre-blending).
+    float dbPostGain = parameterValue(ParamPostGain);
+
+    // Linear blending value from dry to completely processed (0 -> 1)
+    // 0 means the signal is completely unprocessed.
+    // 1 mixes in only the compressed signal.
+    float effectBlend = parameterValue(ParamEffectBlend);
+
+    float releaseZone1 = parameterValue(ParamReleaseZone1);
+    float releaseZone2 = parameterValue(ParamReleaseZone2);
+    float releaseZone3 = parameterValue(ParamReleaseZone3);
+    float releaseZone4 = parameterValue(ParamReleaseZone4);
+
+    // Apply compression to the pre-filtered signal.
+    // The processing is performed in place.
+    m_compressor.process(m_destinationChannels.get(),
+                         m_destinationChannels.get(),
+                         numberOfChannels,
+                         framesToProcess,
+
+                         dbThreshold,
+                         dbKnee,
+                         ratio,
+                         attackTime,
+                         releaseTime,
+                         preDelayTime,
+                         dbPostGain,
+                         effectBlend,
+
+                         releaseZone1,
+                         releaseZone2,
+                         releaseZone3,
+                         releaseZone4
+                         );
+                         
+    // Update the compression amount.                     
+    setParameterValue(ParamReduction, m_compressor.meteringGain());
+
+    // Apply de-emphasis filter.
+    for (unsigned i = 0; i < numberOfChannels; ++i) {
+        float* destinationData = m_destinationChannels[i];
+        ZeroPole* postFilters = m_postFilterPacks[i]->filters;
+
+        postFilters[0].process(destinationData, destinationData, framesToProcess);
+        postFilters[1].process(destinationData, destinationData, framesToProcess);
+        postFilters[2].process(destinationData, destinationData, framesToProcess);
+        postFilters[3].process(destinationData, destinationData, framesToProcess);
+    }
+}
+
+void DynamicsCompressor::reset()
+{
+    m_lastFilterStageRatio = -1; // for recalc
+    m_lastAnchor = -1;
+    m_lastFilterStageGain = -1;
+
+    for (unsigned channel = 0; channel < m_numberOfChannels; ++channel) {
+        for (unsigned stageIndex = 0; stageIndex < 4; ++stageIndex) {
+            m_preFilterPacks[channel]->filters[stageIndex].reset();
+            m_postFilterPacks[channel]->filters[stageIndex].reset();
+        }
+    }
+
+    m_compressor.reset();
+}
+
+void DynamicsCompressor::setNumberOfChannels(unsigned numberOfChannels)
+{
+    if (m_preFilterPacks.size() == numberOfChannels)
+        return;
+
+    m_preFilterPacks.clear();
+    m_postFilterPacks.clear();
+    for (unsigned i = 0; i < numberOfChannels; ++i) {
+        m_preFilterPacks.append(adoptPtr(new ZeroPoleFilterPack4()));
+        m_postFilterPacks.append(adoptPtr(new ZeroPoleFilterPack4()));
+    }
+
+    m_sourceChannels = adoptArrayPtr(new const float* [numberOfChannels]);
+    m_destinationChannels = adoptArrayPtr(new float* [numberOfChannels]);
+
+    m_compressor.setNumberOfChannels(numberOfChannels);
+    m_numberOfChannels = numberOfChannels;
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/DynamicsCompressor.h
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2011 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 DynamicsCompressor_h
+#define DynamicsCompressor_h
+
+#include "AudioArray.h"
+#include "DynamicsCompressorKernel.h"
+#include "ZeroPole.h"
+
+#include <wtf/OwnArrayPtr.h>
+
+namespace WebCore {
+
+class AudioBus;
+
+// DynamicsCompressor implements a flexible audio dynamics compression effect such as
+// is commonly used in musical production and game audio. It lowers the volume
+// of the loudest parts of the signal and raises the volume of the softest parts,
+// making the sound richer, fuller, and more controlled.
+
+class DynamicsCompressor {
+public:
+    enum {
+        ParamThreshold,
+        ParamKnee,
+        ParamRatio,
+        ParamAttack,
+        ParamRelease,
+        ParamPreDelay,
+        ParamReleaseZone1,
+        ParamReleaseZone2,
+        ParamReleaseZone3,
+        ParamReleaseZone4,
+        ParamPostGain,
+        ParamFilterStageGain,
+        ParamFilterStageRatio,
+        ParamFilterAnchor,
+        ParamEffectBlend,
+        ParamReduction,
+        ParamLast
+    };
+
+    DynamicsCompressor(float sampleRate, unsigned numberOfChannels);
+
+    void process(const AudioBus* sourceBus, AudioBus* destinationBus, unsigned framesToProcess);
+    void reset();
+    void setNumberOfChannels(unsigned);
+
+    void setParameterValue(unsigned parameterID, float value);
+    float parameterValue(unsigned parameterID);
+
+    float sampleRate() const { return m_sampleRate; }
+    float nyquist() const { return m_sampleRate / 2; }
+
+    double tailTime() const { return 0; }
+    double latencyTime() const { return m_compressor.latencyFrames() / static_cast<double>(sampleRate()); }
+
+protected:
+    unsigned m_numberOfChannels;
+
+    // m_parameters holds the tweakable compressor parameters.
+    float m_parameters[ParamLast];
+    void initializeParameters();
+
+    float m_sampleRate;
+
+    // Emphasis filter controls.
+    float m_lastFilterStageRatio;
+    float m_lastAnchor;
+    float m_lastFilterStageGain;
+
+    typedef struct {
+        ZeroPole filters[4];
+    } ZeroPoleFilterPack4;
+
+    // Per-channel emphasis filters.
+    Vector<OwnPtr<ZeroPoleFilterPack4> > m_preFilterPacks;
+    Vector<OwnPtr<ZeroPoleFilterPack4> > m_postFilterPacks;
+
+    OwnArrayPtr<const float*> m_sourceChannels;
+    OwnArrayPtr<float*> m_destinationChannels;
+
+    void setEmphasisStageParameters(unsigned stageIndex, float gain, float normalizedFrequency /* 0 -> 1 */);
+    void setEmphasisParameters(float gain, float anchorFreq, float filterStageRatio);
+
+    // The core compressor.
+    DynamicsCompressorKernel m_compressor;
+};
+
+} // namespace WebCore
+
+#endif // DynamicsCompressor_h
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/DynamicsCompressorKernel.cpp
@@ -0,0 +1,479 @@
+/*
+ * Copyright (C) 2011 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 "DynamicsCompressorKernel.h"
+
+#include "AudioUtilities.h"
+#include "DenormalDisabler.h"
+#include <algorithm>
+#include <wtf/MathExtras.h>
+
+using namespace std;
+
+namespace WebCore {
+
+using namespace AudioUtilities;
+
+// Metering hits peaks instantly, but releases this fast (in seconds).
+const float meteringReleaseTimeConstant = 0.325f;
+
+const float uninitializedValue = -1;
+
+DynamicsCompressorKernel::DynamicsCompressorKernel(float sampleRate, unsigned numberOfChannels)
+    : m_sampleRate(sampleRate)
+    , m_lastPreDelayFrames(DefaultPreDelayFrames)
+    , m_preDelayReadIndex(0)
+    , m_preDelayWriteIndex(DefaultPreDelayFrames)
+    , m_ratio(uninitializedValue)
+    , m_slope(uninitializedValue)
+    , m_linearThreshold(uninitializedValue)
+    , m_dbThreshold(uninitializedValue)
+    , m_dbKnee(uninitializedValue)
+    , m_kneeThreshold(uninitializedValue)
+    , m_kneeThresholdDb(uninitializedValue)
+    , m_ykneeThresholdDb(uninitializedValue)
+    , m_K(uninitializedValue)
+{
+    setNumberOfChannels(numberOfChannels);
+
+    // Initializes most member variables
+    reset();
+
+    m_meteringReleaseK = static_cast<float>(discreteTimeConstantForSampleRate(meteringReleaseTimeConstant, sampleRate));
+}
+
+void DynamicsCompressorKernel::setNumberOfChannels(unsigned numberOfChannels)
+{
+    if (m_preDelayBuffers.size() == numberOfChannels)
+        return;
+
+    m_preDelayBuffers.clear();
+    for (unsigned i = 0; i < numberOfChannels; ++i)
+        m_preDelayBuffers.append(adoptPtr(new AudioFloatArray(MaxPreDelayFrames)));
+}
+
+void DynamicsCompressorKernel::setPreDelayTime(float preDelayTime)
+{
+    // Re-configure look-ahead section pre-delay if delay time has changed.
+    unsigned preDelayFrames = preDelayTime * sampleRate();
+    if (preDelayFrames > MaxPreDelayFrames - 1)
+        preDelayFrames = MaxPreDelayFrames - 1;
+
+    if (m_lastPreDelayFrames != preDelayFrames) {
+        m_lastPreDelayFrames = preDelayFrames;
+        for (unsigned i = 0; i < m_preDelayBuffers.size(); ++i)
+            m_preDelayBuffers[i]->zero();
+
+        m_preDelayReadIndex = 0;
+        m_preDelayWriteIndex = preDelayFrames;
+    }
+}
+
+// Exponential curve for the knee.
+// It is 1st derivative matched at m_linearThreshold and asymptotically approaches the value m_linearThreshold + 1 / k.
+float DynamicsCompressorKernel::kneeCurve(float x, float k)
+{
+    // Linear up to threshold.
+    if (x < m_linearThreshold)
+        return x;
+
+    return m_linearThreshold + (1 - expf(-k * (x - m_linearThreshold))) / k;
+}
+
+// Full compression curve with constant ratio after knee.
+float DynamicsCompressorKernel::saturate(float x, float k)
+{
+    float y;
+
+    if (x < m_kneeThreshold)
+        y = kneeCurve(x, k);
+    else {
+        // Constant ratio after knee.
+        float xDb = linearToDecibels(x);
+        float yDb = m_ykneeThresholdDb + m_slope * (xDb - m_kneeThresholdDb);
+
+        y = decibelsToLinear(yDb);
+    }
+
+    return y;
+}
+
+// Approximate 1st derivative with input and output expressed in dB.
+// This slope is equal to the inverse of the compression "ratio".
+// In other words, a compression ratio of 20 would be a slope of 1/20.
+float DynamicsCompressorKernel::slopeAt(float x, float k)
+{
+    if (x < m_linearThreshold)
+        return 1;
+
+    float x2 = x * 1.001;
+
+    float xDb = linearToDecibels(x);
+    float x2Db = linearToDecibels(x2);
+
+    float yDb = linearToDecibels(kneeCurve(x, k));
+    float y2Db = linearToDecibels(kneeCurve(x2, k));
+
+    float m = (y2Db - yDb) / (x2Db - xDb);
+
+    return m;
+}
+
+float DynamicsCompressorKernel::kAtSlope(float desiredSlope)
+{
+    float xDb = m_dbThreshold + m_dbKnee;
+    float x = decibelsToLinear(xDb);
+
+    // Approximate k given initial values.
+    float minK = 0.1;
+    float maxK = 10000;
+    float k = 5;
+
+    for (int i = 0; i < 15; ++i) {
+        // A high value for k will more quickly asymptotically approach a slope of 0.
+        float slope = slopeAt(x, k);
+
+        if (slope < desiredSlope) {
+            // k is too high.
+            maxK = k;
+        } else {
+            // k is too low.
+            minK = k;
+        }
+
+        // Re-calculate based on geometric mean.
+        k = sqrtf(minK * maxK);
+    }
+
+    return k;
+}
+
+float DynamicsCompressorKernel::updateStaticCurveParameters(float dbThreshold, float dbKnee, float ratio)
+{
+    if (dbThreshold != m_dbThreshold || dbKnee != m_dbKnee || ratio != m_ratio) {
+        // Threshold and knee.
+        m_dbThreshold = dbThreshold;
+        m_linearThreshold = decibelsToLinear(dbThreshold);
+        m_dbKnee = dbKnee;
+
+        // Compute knee parameters.
+        m_ratio = ratio;
+        m_slope = 1 / m_ratio;
+
+        float k = kAtSlope(1 / m_ratio);
+
+        m_kneeThresholdDb = dbThreshold + dbKnee;
+        m_kneeThreshold = decibelsToLinear(m_kneeThresholdDb);
+
+        m_ykneeThresholdDb = linearToDecibels(kneeCurve(m_kneeThreshold, k));
+
+        m_K = k;
+    }
+    return m_K;
+}
+
+void DynamicsCompressorKernel::process(float* sourceChannels[],
+                                       float* destinationChannels[],
+                                       unsigned numberOfChannels,
+                                       unsigned framesToProcess,
+
+                                       float dbThreshold,
+                                       float dbKnee,
+                                       float ratio,
+                                       float attackTime,
+                                       float releaseTime,
+                                       float preDelayTime,
+                                       float dbPostGain,
+                                       float effectBlend, /* equal power crossfade */
+
+                                       float releaseZone1,
+                                       float releaseZone2,
+                                       float releaseZone3,
+                                       float releaseZone4
+                                       )
+{
+    ASSERT(m_preDelayBuffers.size() == numberOfChannels);
+
+    float sampleRate = this->sampleRate();
+
+    float dryMix = 1 - effectBlend;
+    float wetMix = effectBlend;
+
+    float k = updateStaticCurveParameters(dbThreshold, dbKnee, ratio);
+
+    // Makeup gain.
+    float fullRangeGain = saturate(1, k);
+    float fullRangeMakeupGain = 1 / fullRangeGain;
+
+    // Empirical/perceptual tuning.
+    fullRangeMakeupGain = powf(fullRangeMakeupGain, 0.6f);
+
+    float masterLinearGain = decibelsToLinear(dbPostGain) * fullRangeMakeupGain;
+
+    // Attack parameters.
+    attackTime = max(0.001f, attackTime);
+    float attackFrames = attackTime * sampleRate;
+
+    // Release parameters.
+    float releaseFrames = sampleRate * releaseTime;
+
+    // Detector release time.
+    float satReleaseTime = 0.0025f;
+    float satReleaseFrames = satReleaseTime * sampleRate;
+
+    // Create a smooth function which passes through four points.
+
+    // Polynomial of the form
+    // y = a + b*x + c*x^2 + d*x^3 + e*x^4;
+
+    float y1 = releaseFrames * releaseZone1;
+    float y2 = releaseFrames * releaseZone2;
+    float y3 = releaseFrames * releaseZone3;
+    float y4 = releaseFrames * releaseZone4;
+
+    // All of these coefficients were derived for 4th order polynomial curve fitting where the y values
+    // match the evenly spaced x values as follows: (y1 : x == 0, y2 : x == 1, y3 : x == 2, y4 : x == 3)
+    float kA = 0.9999999999999998f*y1 + 1.8432219684323923e-16f*y2 - 1.9373394351676423e-16f*y3 + 8.824516011816245e-18f*y4;
+    float kB = -1.5788320352845888f*y1 + 2.3305837032074286f*y2 - 0.9141194204840429f*y3 + 0.1623677525612032f*y4;
+    float kC = 0.5334142869106424f*y1 - 1.272736789213631f*y2 + 0.9258856042207512f*y3 - 0.18656310191776226f*y4;
+    float kD = 0.08783463138207234f*y1 - 0.1694162967925622f*y2 + 0.08588057951595272f*y3 - 0.00429891410546283f*y4;
+    float kE = -0.042416883008123074f*y1 + 0.1115693827987602f*y2 - 0.09764676325265872f*y3 + 0.028494263462021576f*y4;
+
+    // x ranges from 0 -> 3       0    1    2   3
+    //                           -15  -10  -5   0db
+
+    // y calculates adaptive release frames depending on the amount of compression.
+
+    setPreDelayTime(preDelayTime);
+
+    const int nDivisionFrames = 32;
+
+    const int nDivisions = framesToProcess / nDivisionFrames;
+
+    unsigned frameIndex = 0;
+    for (int i = 0; i < nDivisions; ++i) {
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        // Calculate desired gain
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+        // Fix gremlins.
+        if (std::isnan(m_detectorAverage))
+            m_detectorAverage = 1;
+        if (std::isinf(m_detectorAverage))
+            m_detectorAverage = 1;
+
+        float desiredGain = m_detectorAverage;
+
+        // Pre-warp so we get desiredGain after sin() warp below.
+        float scaledDesiredGain = asinf(desiredGain) / (0.5f * piFloat);
+
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        // Deal with envelopes
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+        // envelopeRate is the rate we slew from current compressor level to the desired level.
+        // The exact rate depends on if we're attacking or releasing and by how much.
+        float envelopeRate;
+
+        bool isReleasing = scaledDesiredGain > m_compressorGain;
+
+        // compressionDiffDb is the difference between current compression level and the desired level.
+        float compressionDiffDb = linearToDecibels(m_compressorGain / scaledDesiredGain);
+
+        if (isReleasing) {
+            // Release mode - compressionDiffDb should be negative dB
+            m_maxAttackCompressionDiffDb = -1;
+
+            // Fix gremlins.
+            if (std::isnan(compressionDiffDb))
+                compressionDiffDb = -1;
+            if (std::isinf(compressionDiffDb))
+                compressionDiffDb = -1;
+
+            // Adaptive release - higher compression (lower compressionDiffDb)  releases faster.
+
+            // Contain within range: -12 -> 0 then scale to go from 0 -> 3
+            float x = compressionDiffDb;
+            x = max(-12.0f, x);
+            x = min(0.0f, x);
+            x = 0.25f * (x + 12);
+
+            // Compute adaptive release curve using 4th order polynomial.
+            // Normal values for the polynomial coefficients would create a monotonically increasing function.
+            float x2 = x * x;
+            float x3 = x2 * x;
+            float x4 = x2 * x2;
+            float releaseFrames = kA + kB * x + kC * x2 + kD * x3 + kE * x4;
+
+#define kSpacingDb 5
+            float dbPerFrame = kSpacingDb / releaseFrames;
+
+            envelopeRate = decibelsToLinear(dbPerFrame);
+        } else {
+            // Attack mode - compressionDiffDb should be positive dB
+
+            // Fix gremlins.
+            if (std::isnan(compressionDiffDb))
+                compressionDiffDb = 1;
+            if (std::isinf(compressionDiffDb))
+                compressionDiffDb = 1;
+
+            // As long as we're still in attack mode, use a rate based off
+            // the largest compressionDiffDb we've encountered so far.
+            if (m_maxAttackCompressionDiffDb == -1 || m_maxAttackCompressionDiffDb < compressionDiffDb)
+                m_maxAttackCompressionDiffDb = compressionDiffDb;
+
+            float effAttenDiffDb = max(0.5f, m_maxAttackCompressionDiffDb);
+
+            float x = 0.25f / effAttenDiffDb;
+            envelopeRate = 1 - powf(x, 1 / attackFrames);
+        }
+
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+        // Inner loop - calculate shaped power average - apply compression.
+        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+        {
+            int preDelayReadIndex = m_preDelayReadIndex;
+            int preDelayWriteIndex = m_preDelayWriteIndex;
+            float detectorAverage = m_detectorAverage;
+            float compressorGain = m_compressorGain;
+
+            int loopFrames = nDivisionFrames;
+            while (loopFrames--) {
+                float compressorInput = 0;
+
+                // Predelay signal, computing compression amount from un-delayed version.
+                for (unsigned i = 0; i < numberOfChannels; ++i) {
+                    float* delayBuffer = m_preDelayBuffers[i]->data();
+                    float undelayedSource = sourceChannels[i][frameIndex];
+                    delayBuffer[preDelayWriteIndex] = undelayedSource;
+
+                    float absUndelayedSource = undelayedSource > 0 ? undelayedSource : -undelayedSource;
+                    if (compressorInput < absUndelayedSource)
+                        compressorInput = absUndelayedSource;
+                }
+
+                // Calculate shaped power on undelayed input.
+
+                float scaledInput = compressorInput;
+                float absInput = scaledInput > 0 ? scaledInput : -scaledInput;
+
+                // Put through shaping curve.
+                // This is linear up to the threshold, then enters a "knee" portion followed by the "ratio" portion.
+                // The transition from the threshold to the knee is smooth (1st derivative matched).
+                // The transition from the knee to the ratio portion is smooth (1st derivative matched).
+                float shapedInput = saturate(absInput, k);
+
+                float attenuation = absInput <= 0.0001f ? 1 : shapedInput / absInput;
+
+                float attenuationDb = -linearToDecibels(attenuation);
+                attenuationDb = max(2.0f, attenuationDb);
+
+                float dbPerFrame = attenuationDb / satReleaseFrames;
+
+                float satReleaseRate = decibelsToLinear(dbPerFrame) - 1;
+
+                bool isRelease = (attenuation > detectorAverage);
+                float rate = isRelease ? satReleaseRate : 1;
+
+                detectorAverage += (attenuation - detectorAverage) * rate;
+                detectorAverage = min(1.0f, detectorAverage);
+
+                // Fix gremlins.
+                if (std::isnan(detectorAverage))
+                    detectorAverage = 1;
+                if (std::isinf(detectorAverage))
+                    detectorAverage = 1;
+
+                // Exponential approach to desired gain.
+                if (envelopeRate < 1) {
+                    // Attack - reduce gain to desired.
+                    compressorGain += (scaledDesiredGain - compressorGain) * envelopeRate;
+                } else {
+                    // Release - exponentially increase gain to 1.0
+                    compressorGain *= envelopeRate;
+                    compressorGain = min(1.0f, compressorGain);
+                }
+
+                // Warp pre-compression gain to smooth out sharp exponential transition points.
+                float postWarpCompressorGain = sinf(0.5f * piFloat * compressorGain);
+
+                // Calculate total gain using master gain and effect blend.
+                float totalGain = dryMix + wetMix * masterLinearGain * postWarpCompressorGain;
+
+                // Calculate metering.
+                float dbRealGain = 20 * log10(postWarpCompressorGain);
+                if (dbRealGain < m_meteringGain)
+                    m_meteringGain = dbRealGain;
+                else
+                    m_meteringGain += (dbRealGain - m_meteringGain) * m_meteringReleaseK;
+
+                // Apply final gain.
+                for (unsigned i = 0; i < numberOfChannels; ++i) {
+                    float* delayBuffer = m_preDelayBuffers[i]->data();
+                    destinationChannels[i][frameIndex] = delayBuffer[preDelayReadIndex] * totalGain;
+                }
+
+                frameIndex++;
+                preDelayReadIndex = (preDelayReadIndex + 1) & MaxPreDelayFramesMask;
+                preDelayWriteIndex = (preDelayWriteIndex + 1) & MaxPreDelayFramesMask;
+            }
+
+            // Locals back to member variables.
+            m_preDelayReadIndex = preDelayReadIndex;
+            m_preDelayWriteIndex = preDelayWriteIndex;
+            m_detectorAverage = DenormalDisabler::flushDenormalFloatToZero(detectorAverage);
+            m_compressorGain = DenormalDisabler::flushDenormalFloatToZero(compressorGain);
+        }
+    }
+}
+
+void DynamicsCompressorKernel::reset()
+{
+    m_detectorAverage = 0;
+    m_compressorGain = 1;
+    m_meteringGain = 1;
+
+    // Predelay section.
+    for (unsigned i = 0; i < m_preDelayBuffers.size(); ++i)
+        m_preDelayBuffers[i]->zero();
+
+    m_preDelayReadIndex = 0;
+    m_preDelayWriteIndex = DefaultPreDelayFrames;
+
+    m_maxAttackCompressionDiffDb = -1; // uninitialized state
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/DynamicsCompressorKernel.h
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2011 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 DynamicsCompressorKernel_h
+#define DynamicsCompressorKernel_h
+
+#include "AudioArray.h"
+
+#include <wtf/OwnPtr.h>
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+class DynamicsCompressorKernel {
+public:
+    DynamicsCompressorKernel(float sampleRate, unsigned numberOfChannels);
+
+    void setNumberOfChannels(unsigned);
+
+    // Performs stereo-linked compression.
+    void process(float* sourceChannels[],
+                 float* destinationChannels[],
+                 unsigned numberOfChannels,
+                 unsigned framesToProcess,
+
+                 float dbThreshold,
+                 float dbKnee,
+                 float ratio,
+                 float attackTime,
+                 float releaseTime,
+                 float preDelayTime,
+                 float dbPostGain,
+                 float effectBlend,
+
+                 float releaseZone1,
+                 float releaseZone2,
+                 float releaseZone3,
+                 float releaseZone4
+                 );
+
+    void reset();
+
+    unsigned latencyFrames() const { return m_lastPreDelayFrames; }
+
+    float sampleRate() const { return m_sampleRate; }
+
+    float meteringGain() const { return m_meteringGain; }
+
+protected:
+    float m_sampleRate;
+
+    float m_detectorAverage;
+    float m_compressorGain;
+
+    // Metering
+    float m_meteringReleaseK;
+    float m_meteringGain;
+
+    // Lookahead section.
+    enum { MaxPreDelayFrames = 1024 };
+    enum { MaxPreDelayFramesMask = MaxPreDelayFrames - 1 };
+    enum { DefaultPreDelayFrames = 256 }; // setPreDelayTime() will override this initial value
+    unsigned m_lastPreDelayFrames;
+    void setPreDelayTime(float);
+
+    Vector<OwnPtr<AudioFloatArray> > m_preDelayBuffers;
+    int m_preDelayReadIndex;
+    int m_preDelayWriteIndex;
+
+    float m_maxAttackCompressionDiffDb;
+
+    // Static compression curve.
+    float kneeCurve(float x, float k);
+    float saturate(float x, float k);
+    float slopeAt(float x, float k);
+    float kAtSlope(float desiredSlope);
+
+    float updateStaticCurveParameters(float dbThreshold, float dbKnee, float ratio);
+
+    // Amount of input change in dB required for 1 dB of output change.
+    // This applies to the portion of the curve above m_kneeThresholdDb (see below).
+    float m_ratio;
+    float m_slope; // Inverse ratio.
+
+    // The input to output change below the threshold is linear 1:1.
+    float m_linearThreshold;
+    float m_dbThreshold;
+
+    // m_dbKnee is the number of dB above the threshold before we enter the "ratio" portion of the curve.
+    // m_kneeThresholdDb = m_dbThreshold + m_dbKnee
+    // The portion between m_dbThreshold and m_kneeThresholdDb is the "soft knee" portion of the curve
+    // which transitions smoothly from the linear portion to the ratio portion.
+    float m_dbKnee;
+    float m_kneeThreshold;
+    float m_kneeThresholdDb;
+    float m_ykneeThresholdDb;
+
+    // Internal parameter for the knee portion of the curve.
+    float m_K;
+};
+
+} // namespace WebCore
+
+#endif // DynamicsCompressorKernel_h
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/README
@@ -0,0 +1,24 @@
+This directory contains the code originally borrowed from the Blink Web Audio
+implementation.  We are forking the code here because in many cases the burden
+of adopting Blink specific utilities is too large compared to the prospect of
+importing upstream fixes by just copying newer versions of the code in the
+future.
+
+The process of borrowing code from Blink is as follows:
+
+* Try to borrow utility classes only, and avoid borrowing code which depends
+  too much on the Blink specific utilities.
+* First, import the pristine files from the Blink repository before adding
+  them to the build system, noting the SVN revision of Blink from which the
+  original files were copied in the commit message.
+* In a separate commit, add the imported source files to the build system,
+  and apply the necessary changes to make it build successfully.
+* Use the code in a separate commit.
+* Never add headers as exported headers.  All headers should be included
+  using the following convention: #include "blink/Header.h".
+* Leave the imported code in the WebCore namespace, and import the needed
+  names into the Mozilla code via `using'.
+* Cherry-pick upsteam fixes manually when needed.  In case you fix a problem
+  that is not Mozilla specific locally, try to upstream your changes into
+  Blink.
+* Ping ehsan for any questions.
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/ZeroPole.cpp
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2011 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 "ZeroPole.h"
+
+#include "DenormalDisabler.h"
+
+namespace WebCore {
+
+void ZeroPole::process(const float *source, float *destination, unsigned framesToProcess)
+{
+    float zero = m_zero;
+    float pole = m_pole;
+
+    // Gain compensation to make 0dB @ 0Hz
+    const float k1 = 1 / (1 - zero);
+    const float k2 = 1 - pole;
+    
+    // Member variables to locals.
+    float lastX = m_lastX;
+    float lastY = m_lastY;
+
+    while (framesToProcess--) {
+        float input = *source++;
+
+        // Zero
+        float output1 = k1 * (input - zero * lastX);
+        lastX = input;
+
+        // Pole
+        float output2 = k2 * output1 + pole * lastY;
+        lastY = output2;
+
+        *destination++ = output2;
+    }
+    
+    // Locals to member variables. Flush denormals here so we don't
+    // slow down the inner loop above.
+    m_lastX = DenormalDisabler::flushDenormalFloatToZero(lastX);
+    m_lastY = DenormalDisabler::flushDenormalFloatToZero(lastY);
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(WEB_AUDIO)
new file mode 100644
--- /dev/null
+++ b/content/media/webaudio/blink/ZeroPole.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2011 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 ZeroPole_h
+#define ZeroPole_h
+
+namespace WebCore {
+
+// ZeroPole is a simple filter with one zero and one pole.
+
+class ZeroPole {
+public:
+    ZeroPole()
+        : m_zero(0)
+        , m_pole(0)
+        , m_lastX(0)
+        , m_lastY(0)
+    {
+    }
+
+    void process(const float *source, float *destination, unsigned framesToProcess);
+
+    // Reset filter state.
+    void reset() { m_lastX = 0; m_lastY = 0; }
+    
+    void setZero(float zero) { m_zero = zero; }
+    void setPole(float pole) { m_pole = pole; }
+    
+    float zero() const { return m_zero; }
+    float pole() const { return m_pole; }
+
+private:
+    float m_zero;
+    float m_pole;
+    float m_lastX;
+    float m_lastY;
+};
+
+} // namespace WebCore
+
+#endif // ZeroPole_h
--- a/toolkit/content/license.html
+++ b/toolkit/content/license.html
@@ -861,17 +861,17 @@ POSSIBILITY OF SUCH DAMAGE.
 </pre>
 
 
 
     <hr>
 
     <h1><a id="apple"></a>Apple License</h1>
 
-    <p>This license applies to certain files in the directories <span class="path">js/src/assembler/assembler/</span>, <span class="path">js/src/assembler/wtf/</span>, <span class="path">js/src/yarr</span>, and <span class="path">widget/cocoa</span>.</p>
+    <p>This license applies to certain files in the directories <span class="path">js/src/assembler/assembler/</span>, <span class="path">js/src/assembler/wtf/</span>, <span class="path">js/src/yarr</span>, <span class="path">content/media/webaudio/blink</span>, and <span class="path">widget/cocoa</span>.</p>
     
 <pre>
 Copyright (C) 2008, 2009 Apple 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