Bug 864091 - Part 1: Import the Dynamics Compressor implementation from Blink; r=padenot

The original code was copied from Blink SVN revision 148720.
This commit is contained in:
Ehsan Akhgari 2013-04-20 19:46:24 -04:00
parent d4f13dfcf1
commit 6d48241e2c
9 changed files with 1300 additions and 1 deletions

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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.

View File

@ -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)

View File

@ -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

View File

@ -866,7 +866,7 @@ POSSIBILITY OF SUCH DAMAGE.
<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.