content/media/webaudio/blink/DynamicsCompressorKernel.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/content/media/webaudio/blink/DynamicsCompressorKernel.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,490 @@
     1.4 +/*
     1.5 + * Copyright (C) 2011 Google Inc. All rights reserved.
     1.6 + *
     1.7 + * Redistribution and use in source and binary forms, with or without
     1.8 + * modification, are permitted provided that the following conditions
     1.9 + * are met:
    1.10 + *
    1.11 + * 1.  Redistributions of source code must retain the above copyright
    1.12 + *     notice, this list of conditions and the following disclaimer.
    1.13 + * 2.  Redistributions in binary form must reproduce the above copyright
    1.14 + *     notice, this list of conditions and the following disclaimer in the
    1.15 + *     documentation and/or other materials provided with the distribution.
    1.16 + * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
    1.17 + *     its contributors may be used to endorse or promote products derived
    1.18 + *     from this software without specific prior written permission.
    1.19 + *
    1.20 + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
    1.21 + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
    1.22 + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
    1.23 + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
    1.24 + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
    1.25 + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
    1.26 + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    1.27 + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    1.28 + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
    1.29 + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    1.30 + */
    1.31 +
    1.32 +#include "DynamicsCompressorKernel.h"
    1.33 +
    1.34 +#include "DenormalDisabler.h"
    1.35 +#include <algorithm>
    1.36 +
    1.37 +#include "mozilla/FloatingPoint.h"
    1.38 +#include "mozilla/Constants.h"
    1.39 +#include "WebAudioUtils.h"
    1.40 +
    1.41 +using namespace std;
    1.42 +
    1.43 +using namespace mozilla::dom; // for WebAudioUtils
    1.44 +using mozilla::IsInfinite;
    1.45 +using mozilla::IsNaN;
    1.46 +
    1.47 +namespace WebCore {
    1.48 +
    1.49 +
    1.50 +// Metering hits peaks instantly, but releases this fast (in seconds).
    1.51 +const float meteringReleaseTimeConstant = 0.325f;
    1.52 +
    1.53 +const float uninitializedValue = -1;
    1.54 +
    1.55 +DynamicsCompressorKernel::DynamicsCompressorKernel(float sampleRate, unsigned numberOfChannels)
    1.56 +    : m_sampleRate(sampleRate)
    1.57 +    , m_lastPreDelayFrames(DefaultPreDelayFrames)
    1.58 +    , m_preDelayReadIndex(0)
    1.59 +    , m_preDelayWriteIndex(DefaultPreDelayFrames)
    1.60 +    , m_ratio(uninitializedValue)
    1.61 +    , m_slope(uninitializedValue)
    1.62 +    , m_linearThreshold(uninitializedValue)
    1.63 +    , m_dbThreshold(uninitializedValue)
    1.64 +    , m_dbKnee(uninitializedValue)
    1.65 +    , m_kneeThreshold(uninitializedValue)
    1.66 +    , m_kneeThresholdDb(uninitializedValue)
    1.67 +    , m_ykneeThresholdDb(uninitializedValue)
    1.68 +    , m_K(uninitializedValue)
    1.69 +{
    1.70 +    setNumberOfChannels(numberOfChannels);
    1.71 +
    1.72 +    // Initializes most member variables
    1.73 +    reset();
    1.74 +
    1.75 +    m_meteringReleaseK =
    1.76 +        static_cast<float>(WebAudioUtils::DiscreteTimeConstantForSampleRate(meteringReleaseTimeConstant, sampleRate));
    1.77 +}
    1.78 +
    1.79 +size_t DynamicsCompressorKernel::sizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
    1.80 +{
    1.81 +    size_t amount = 0;
    1.82 +    amount += m_preDelayBuffers.SizeOfExcludingThis(aMallocSizeOf);
    1.83 +    for (size_t i = 0; i < m_preDelayBuffers.Length(); i++) {
    1.84 +        amount += m_preDelayBuffers[i].SizeOfExcludingThis(aMallocSizeOf);
    1.85 +    }
    1.86 +
    1.87 +    return amount;
    1.88 +}
    1.89 +
    1.90 +void DynamicsCompressorKernel::setNumberOfChannels(unsigned numberOfChannels)
    1.91 +{
    1.92 +    if (m_preDelayBuffers.Length() == numberOfChannels)
    1.93 +        return;
    1.94 +
    1.95 +    m_preDelayBuffers.Clear();
    1.96 +    for (unsigned i = 0; i < numberOfChannels; ++i)
    1.97 +        m_preDelayBuffers.AppendElement(new float[MaxPreDelayFrames]);
    1.98 +}
    1.99 +
   1.100 +void DynamicsCompressorKernel::setPreDelayTime(float preDelayTime)
   1.101 +{
   1.102 +    // Re-configure look-ahead section pre-delay if delay time has changed.
   1.103 +    unsigned preDelayFrames = preDelayTime * sampleRate();
   1.104 +    if (preDelayFrames > MaxPreDelayFrames - 1)
   1.105 +        preDelayFrames = MaxPreDelayFrames - 1;
   1.106 +
   1.107 +    if (m_lastPreDelayFrames != preDelayFrames) {
   1.108 +        m_lastPreDelayFrames = preDelayFrames;
   1.109 +        for (unsigned i = 0; i < m_preDelayBuffers.Length(); ++i)
   1.110 +            memset(m_preDelayBuffers[i], 0, sizeof(float) * MaxPreDelayFrames);
   1.111 +
   1.112 +        m_preDelayReadIndex = 0;
   1.113 +        m_preDelayWriteIndex = preDelayFrames;
   1.114 +    }
   1.115 +}
   1.116 +
   1.117 +// Exponential curve for the knee.
   1.118 +// It is 1st derivative matched at m_linearThreshold and asymptotically approaches the value m_linearThreshold + 1 / k.
   1.119 +float DynamicsCompressorKernel::kneeCurve(float x, float k)
   1.120 +{
   1.121 +    // Linear up to threshold.
   1.122 +    if (x < m_linearThreshold)
   1.123 +        return x;
   1.124 +
   1.125 +    return m_linearThreshold + (1 - expf(-k * (x - m_linearThreshold))) / k;
   1.126 +}
   1.127 +
   1.128 +// Full compression curve with constant ratio after knee.
   1.129 +float DynamicsCompressorKernel::saturate(float x, float k)
   1.130 +{
   1.131 +    float y;
   1.132 +
   1.133 +    if (x < m_kneeThreshold)
   1.134 +        y = kneeCurve(x, k);
   1.135 +    else {
   1.136 +        // Constant ratio after knee.
   1.137 +        float xDb = WebAudioUtils::ConvertLinearToDecibels(x, -1000.0f);
   1.138 +        float yDb = m_ykneeThresholdDb + m_slope * (xDb - m_kneeThresholdDb);
   1.139 +
   1.140 +        y = WebAudioUtils::ConvertDecibelsToLinear(yDb);
   1.141 +    }
   1.142 +
   1.143 +    return y;
   1.144 +}
   1.145 +
   1.146 +// Approximate 1st derivative with input and output expressed in dB.
   1.147 +// This slope is equal to the inverse of the compression "ratio".
   1.148 +// In other words, a compression ratio of 20 would be a slope of 1/20.
   1.149 +float DynamicsCompressorKernel::slopeAt(float x, float k)
   1.150 +{
   1.151 +    if (x < m_linearThreshold)
   1.152 +        return 1;
   1.153 +
   1.154 +    float x2 = x * 1.001;
   1.155 +
   1.156 +    float xDb = WebAudioUtils::ConvertLinearToDecibels(x, -1000.0f);
   1.157 +    float x2Db = WebAudioUtils::ConvertLinearToDecibels(x2, -1000.0f);
   1.158 +
   1.159 +    float yDb = WebAudioUtils::ConvertLinearToDecibels(kneeCurve(x, k), -1000.0f);
   1.160 +    float y2Db = WebAudioUtils::ConvertLinearToDecibels(kneeCurve(x2, k), -1000.0f);
   1.161 +
   1.162 +    float m = (y2Db - yDb) / (x2Db - xDb);
   1.163 +
   1.164 +    return m;
   1.165 +}
   1.166 +
   1.167 +float DynamicsCompressorKernel::kAtSlope(float desiredSlope)
   1.168 +{
   1.169 +    float xDb = m_dbThreshold + m_dbKnee;
   1.170 +    float x = WebAudioUtils::ConvertDecibelsToLinear(xDb);
   1.171 +
   1.172 +    // Approximate k given initial values.
   1.173 +    float minK = 0.1;
   1.174 +    float maxK = 10000;
   1.175 +    float k = 5;
   1.176 +
   1.177 +    for (int i = 0; i < 15; ++i) {
   1.178 +        // A high value for k will more quickly asymptotically approach a slope of 0.
   1.179 +        float slope = slopeAt(x, k);
   1.180 +
   1.181 +        if (slope < desiredSlope) {
   1.182 +            // k is too high.
   1.183 +            maxK = k;
   1.184 +        } else {
   1.185 +            // k is too low.
   1.186 +            minK = k;
   1.187 +        }
   1.188 +
   1.189 +        // Re-calculate based on geometric mean.
   1.190 +        k = sqrtf(minK * maxK);
   1.191 +    }
   1.192 +
   1.193 +    return k;
   1.194 +}
   1.195 +
   1.196 +float DynamicsCompressorKernel::updateStaticCurveParameters(float dbThreshold, float dbKnee, float ratio)
   1.197 +{
   1.198 +    if (dbThreshold != m_dbThreshold || dbKnee != m_dbKnee || ratio != m_ratio) {
   1.199 +        // Threshold and knee.
   1.200 +        m_dbThreshold = dbThreshold;
   1.201 +        m_linearThreshold = WebAudioUtils::ConvertDecibelsToLinear(dbThreshold);
   1.202 +        m_dbKnee = dbKnee;
   1.203 +
   1.204 +        // Compute knee parameters.
   1.205 +        m_ratio = ratio;
   1.206 +        m_slope = 1 / m_ratio;
   1.207 +
   1.208 +        float k = kAtSlope(1 / m_ratio);
   1.209 +
   1.210 +        m_kneeThresholdDb = dbThreshold + dbKnee;
   1.211 +        m_kneeThreshold = WebAudioUtils::ConvertDecibelsToLinear(m_kneeThresholdDb);
   1.212 +
   1.213 +        m_ykneeThresholdDb = WebAudioUtils::ConvertLinearToDecibels(kneeCurve(m_kneeThreshold, k), -1000.0f);
   1.214 +
   1.215 +        m_K = k;
   1.216 +    }
   1.217 +    return m_K;
   1.218 +}
   1.219 +
   1.220 +void DynamicsCompressorKernel::process(float* sourceChannels[],
   1.221 +                                       float* destinationChannels[],
   1.222 +                                       unsigned numberOfChannels,
   1.223 +                                       unsigned framesToProcess,
   1.224 +
   1.225 +                                       float dbThreshold,
   1.226 +                                       float dbKnee,
   1.227 +                                       float ratio,
   1.228 +                                       float attackTime,
   1.229 +                                       float releaseTime,
   1.230 +                                       float preDelayTime,
   1.231 +                                       float dbPostGain,
   1.232 +                                       float effectBlend, /* equal power crossfade */
   1.233 +
   1.234 +                                       float releaseZone1,
   1.235 +                                       float releaseZone2,
   1.236 +                                       float releaseZone3,
   1.237 +                                       float releaseZone4
   1.238 +                                       )
   1.239 +{
   1.240 +    MOZ_ASSERT(m_preDelayBuffers.Length() == numberOfChannels);
   1.241 +
   1.242 +    float sampleRate = this->sampleRate();
   1.243 +
   1.244 +    float dryMix = 1 - effectBlend;
   1.245 +    float wetMix = effectBlend;
   1.246 +
   1.247 +    float k = updateStaticCurveParameters(dbThreshold, dbKnee, ratio);
   1.248 +
   1.249 +    // Makeup gain.
   1.250 +    float fullRangeGain = saturate(1, k);
   1.251 +    float fullRangeMakeupGain = 1 / fullRangeGain;
   1.252 +
   1.253 +    // Empirical/perceptual tuning.
   1.254 +    fullRangeMakeupGain = powf(fullRangeMakeupGain, 0.6f);
   1.255 +
   1.256 +    float masterLinearGain = WebAudioUtils::ConvertDecibelsToLinear(dbPostGain) * fullRangeMakeupGain;
   1.257 +
   1.258 +    // Attack parameters.
   1.259 +    attackTime = max(0.001f, attackTime);
   1.260 +    float attackFrames = attackTime * sampleRate;
   1.261 +
   1.262 +    // Release parameters.
   1.263 +    float releaseFrames = sampleRate * releaseTime;
   1.264 +
   1.265 +    // Detector release time.
   1.266 +    float satReleaseTime = 0.0025f;
   1.267 +    float satReleaseFrames = satReleaseTime * sampleRate;
   1.268 +
   1.269 +    // Create a smooth function which passes through four points.
   1.270 +
   1.271 +    // Polynomial of the form
   1.272 +    // y = a + b*x + c*x^2 + d*x^3 + e*x^4;
   1.273 +
   1.274 +    float y1 = releaseFrames * releaseZone1;
   1.275 +    float y2 = releaseFrames * releaseZone2;
   1.276 +    float y3 = releaseFrames * releaseZone3;
   1.277 +    float y4 = releaseFrames * releaseZone4;
   1.278 +
   1.279 +    // All of these coefficients were derived for 4th order polynomial curve fitting where the y values
   1.280 +    // match the evenly spaced x values as follows: (y1 : x == 0, y2 : x == 1, y3 : x == 2, y4 : x == 3)
   1.281 +    float kA = 0.9999999999999998f*y1 + 1.8432219684323923e-16f*y2 - 1.9373394351676423e-16f*y3 + 8.824516011816245e-18f*y4;
   1.282 +    float kB = -1.5788320352845888f*y1 + 2.3305837032074286f*y2 - 0.9141194204840429f*y3 + 0.1623677525612032f*y4;
   1.283 +    float kC = 0.5334142869106424f*y1 - 1.272736789213631f*y2 + 0.9258856042207512f*y3 - 0.18656310191776226f*y4;
   1.284 +    float kD = 0.08783463138207234f*y1 - 0.1694162967925622f*y2 + 0.08588057951595272f*y3 - 0.00429891410546283f*y4;
   1.285 +    float kE = -0.042416883008123074f*y1 + 0.1115693827987602f*y2 - 0.09764676325265872f*y3 + 0.028494263462021576f*y4;
   1.286 +
   1.287 +    // x ranges from 0 -> 3       0    1    2   3
   1.288 +    //                           -15  -10  -5   0db
   1.289 +
   1.290 +    // y calculates adaptive release frames depending on the amount of compression.
   1.291 +
   1.292 +    setPreDelayTime(preDelayTime);
   1.293 +
   1.294 +    const int nDivisionFrames = 32;
   1.295 +
   1.296 +    const int nDivisions = framesToProcess / nDivisionFrames;
   1.297 +
   1.298 +    unsigned frameIndex = 0;
   1.299 +    for (int i = 0; i < nDivisions; ++i) {
   1.300 +        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1.301 +        // Calculate desired gain
   1.302 +        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1.303 +
   1.304 +        // Fix gremlins.
   1.305 +        if (IsNaN(m_detectorAverage))
   1.306 +            m_detectorAverage = 1;
   1.307 +        if (IsInfinite(m_detectorAverage))
   1.308 +            m_detectorAverage = 1;
   1.309 +
   1.310 +        float desiredGain = m_detectorAverage;
   1.311 +
   1.312 +        // Pre-warp so we get desiredGain after sin() warp below.
   1.313 +        float scaledDesiredGain = asinf(desiredGain) / (0.5f * M_PI);
   1.314 +
   1.315 +        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1.316 +        // Deal with envelopes
   1.317 +        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1.318 +
   1.319 +        // envelopeRate is the rate we slew from current compressor level to the desired level.
   1.320 +        // The exact rate depends on if we're attacking or releasing and by how much.
   1.321 +        float envelopeRate;
   1.322 +
   1.323 +        bool isReleasing = scaledDesiredGain > m_compressorGain;
   1.324 +
   1.325 +        // compressionDiffDb is the difference between current compression level and the desired level.
   1.326 +        float compressionDiffDb = WebAudioUtils::ConvertLinearToDecibels(m_compressorGain / scaledDesiredGain, -1000.0f);
   1.327 +
   1.328 +        if (isReleasing) {
   1.329 +            // Release mode - compressionDiffDb should be negative dB
   1.330 +            m_maxAttackCompressionDiffDb = -1;
   1.331 +
   1.332 +            // Fix gremlins.
   1.333 +            if (IsNaN(compressionDiffDb))
   1.334 +                compressionDiffDb = -1;
   1.335 +            if (IsInfinite(compressionDiffDb))
   1.336 +                compressionDiffDb = -1;
   1.337 +
   1.338 +            // Adaptive release - higher compression (lower compressionDiffDb)  releases faster.
   1.339 +
   1.340 +            // Contain within range: -12 -> 0 then scale to go from 0 -> 3
   1.341 +            float x = compressionDiffDb;
   1.342 +            x = max(-12.0f, x);
   1.343 +            x = min(0.0f, x);
   1.344 +            x = 0.25f * (x + 12);
   1.345 +
   1.346 +            // Compute adaptive release curve using 4th order polynomial.
   1.347 +            // Normal values for the polynomial coefficients would create a monotonically increasing function.
   1.348 +            float x2 = x * x;
   1.349 +            float x3 = x2 * x;
   1.350 +            float x4 = x2 * x2;
   1.351 +            float releaseFrames = kA + kB * x + kC * x2 + kD * x3 + kE * x4;
   1.352 +
   1.353 +#define kSpacingDb 5
   1.354 +            float dbPerFrame = kSpacingDb / releaseFrames;
   1.355 +
   1.356 +            envelopeRate = WebAudioUtils::ConvertDecibelsToLinear(dbPerFrame);
   1.357 +        } else {
   1.358 +            // Attack mode - compressionDiffDb should be positive dB
   1.359 +
   1.360 +            // Fix gremlins.
   1.361 +            if (IsNaN(compressionDiffDb))
   1.362 +                compressionDiffDb = 1;
   1.363 +            if (IsInfinite(compressionDiffDb))
   1.364 +                compressionDiffDb = 1;
   1.365 +
   1.366 +            // As long as we're still in attack mode, use a rate based off
   1.367 +            // the largest compressionDiffDb we've encountered so far.
   1.368 +            if (m_maxAttackCompressionDiffDb == -1 || m_maxAttackCompressionDiffDb < compressionDiffDb)
   1.369 +                m_maxAttackCompressionDiffDb = compressionDiffDb;
   1.370 +
   1.371 +            float effAttenDiffDb = max(0.5f, m_maxAttackCompressionDiffDb);
   1.372 +
   1.373 +            float x = 0.25f / effAttenDiffDb;
   1.374 +            envelopeRate = 1 - powf(x, 1 / attackFrames);
   1.375 +        }
   1.376 +
   1.377 +        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1.378 +        // Inner loop - calculate shaped power average - apply compression.
   1.379 +        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   1.380 +
   1.381 +        {
   1.382 +            int preDelayReadIndex = m_preDelayReadIndex;
   1.383 +            int preDelayWriteIndex = m_preDelayWriteIndex;
   1.384 +            float detectorAverage = m_detectorAverage;
   1.385 +            float compressorGain = m_compressorGain;
   1.386 +
   1.387 +            int loopFrames = nDivisionFrames;
   1.388 +            while (loopFrames--) {
   1.389 +                float compressorInput = 0;
   1.390 +
   1.391 +                // Predelay signal, computing compression amount from un-delayed version.
   1.392 +                for (unsigned i = 0; i < numberOfChannels; ++i) {
   1.393 +                    float* delayBuffer = m_preDelayBuffers[i];
   1.394 +                    float undelayedSource = sourceChannels[i][frameIndex];
   1.395 +                    delayBuffer[preDelayWriteIndex] = undelayedSource;
   1.396 +
   1.397 +                    float absUndelayedSource = undelayedSource > 0 ? undelayedSource : -undelayedSource;
   1.398 +                    if (compressorInput < absUndelayedSource)
   1.399 +                        compressorInput = absUndelayedSource;
   1.400 +                }
   1.401 +
   1.402 +                // Calculate shaped power on undelayed input.
   1.403 +
   1.404 +                float scaledInput = compressorInput;
   1.405 +                float absInput = scaledInput > 0 ? scaledInput : -scaledInput;
   1.406 +
   1.407 +                // Put through shaping curve.
   1.408 +                // This is linear up to the threshold, then enters a "knee" portion followed by the "ratio" portion.
   1.409 +                // The transition from the threshold to the knee is smooth (1st derivative matched).
   1.410 +                // The transition from the knee to the ratio portion is smooth (1st derivative matched).
   1.411 +                float shapedInput = saturate(absInput, k);
   1.412 +
   1.413 +                float attenuation = absInput <= 0.0001f ? 1 : shapedInput / absInput;
   1.414 +
   1.415 +                float attenuationDb = -WebAudioUtils::ConvertLinearToDecibels(attenuation, -1000.0f);
   1.416 +                attenuationDb = max(2.0f, attenuationDb);
   1.417 +
   1.418 +                float dbPerFrame = attenuationDb / satReleaseFrames;
   1.419 +
   1.420 +                float satReleaseRate = WebAudioUtils::ConvertDecibelsToLinear(dbPerFrame) - 1;
   1.421 +
   1.422 +                bool isRelease = (attenuation > detectorAverage);
   1.423 +                float rate = isRelease ? satReleaseRate : 1;
   1.424 +
   1.425 +                detectorAverage += (attenuation - detectorAverage) * rate;
   1.426 +                detectorAverage = min(1.0f, detectorAverage);
   1.427 +
   1.428 +                // Fix gremlins.
   1.429 +                if (IsNaN(detectorAverage))
   1.430 +                    detectorAverage = 1;
   1.431 +                if (IsInfinite(detectorAverage))
   1.432 +                    detectorAverage = 1;
   1.433 +
   1.434 +                // Exponential approach to desired gain.
   1.435 +                if (envelopeRate < 1) {
   1.436 +                    // Attack - reduce gain to desired.
   1.437 +                    compressorGain += (scaledDesiredGain - compressorGain) * envelopeRate;
   1.438 +                } else {
   1.439 +                    // Release - exponentially increase gain to 1.0
   1.440 +                    compressorGain *= envelopeRate;
   1.441 +                    compressorGain = min(1.0f, compressorGain);
   1.442 +                }
   1.443 +
   1.444 +                // Warp pre-compression gain to smooth out sharp exponential transition points.
   1.445 +                float postWarpCompressorGain = sinf(0.5f * M_PI * compressorGain);
   1.446 +
   1.447 +                // Calculate total gain using master gain and effect blend.
   1.448 +                float totalGain = dryMix + wetMix * masterLinearGain * postWarpCompressorGain;
   1.449 +
   1.450 +                // Calculate metering.
   1.451 +                float dbRealGain = 20 * log10(postWarpCompressorGain);
   1.452 +                if (dbRealGain < m_meteringGain)
   1.453 +                    m_meteringGain = dbRealGain;
   1.454 +                else
   1.455 +                    m_meteringGain += (dbRealGain - m_meteringGain) * m_meteringReleaseK;
   1.456 +
   1.457 +                // Apply final gain.
   1.458 +                for (unsigned i = 0; i < numberOfChannels; ++i) {
   1.459 +                    float* delayBuffer = m_preDelayBuffers[i];
   1.460 +                    destinationChannels[i][frameIndex] = delayBuffer[preDelayReadIndex] * totalGain;
   1.461 +                }
   1.462 +
   1.463 +                frameIndex++;
   1.464 +                preDelayReadIndex = (preDelayReadIndex + 1) & MaxPreDelayFramesMask;
   1.465 +                preDelayWriteIndex = (preDelayWriteIndex + 1) & MaxPreDelayFramesMask;
   1.466 +            }
   1.467 +
   1.468 +            // Locals back to member variables.
   1.469 +            m_preDelayReadIndex = preDelayReadIndex;
   1.470 +            m_preDelayWriteIndex = preDelayWriteIndex;
   1.471 +            m_detectorAverage = DenormalDisabler::flushDenormalFloatToZero(detectorAverage);
   1.472 +            m_compressorGain = DenormalDisabler::flushDenormalFloatToZero(compressorGain);
   1.473 +        }
   1.474 +    }
   1.475 +}
   1.476 +
   1.477 +void DynamicsCompressorKernel::reset()
   1.478 +{
   1.479 +    m_detectorAverage = 0;
   1.480 +    m_compressorGain = 1;
   1.481 +    m_meteringGain = 1;
   1.482 +
   1.483 +    // Predelay section.
   1.484 +    for (unsigned i = 0; i < m_preDelayBuffers.Length(); ++i)
   1.485 +        memset(m_preDelayBuffers[i], 0, sizeof(float) * MaxPreDelayFrames);
   1.486 +
   1.487 +    m_preDelayReadIndex = 0;
   1.488 +    m_preDelayWriteIndex = DefaultPreDelayFrames;
   1.489 +
   1.490 +    m_maxAttackCompressionDiffDb = -1; // uninitialized state
   1.491 +}
   1.492 +
   1.493 +} // namespace WebCore

mercurial