Home | History | Annotate | Download | only in audio
      1 /*
      2  * Copyright (C) 2011 Google Inc. All rights reserved.
      3  *
      4  * Redistribution and use in source and binary forms, with or without
      5  * modification, are permitted provided that the following conditions
      6  * are met:
      7  *
      8  * 1.  Redistributions of source code must retain the above copyright
      9  *     notice, this list of conditions and the following disclaimer.
     10  * 2.  Redistributions in binary form must reproduce the above copyright
     11  *     notice, this list of conditions and the following disclaimer in the
     12  *     documentation and/or other materials provided with the distribution.
     13  * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
     14  *     its contributors may be used to endorse or promote products derived
     15  *     from this software without specific prior written permission.
     16  *
     17  * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
     18  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
     19  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
     20  * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
     21  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
     22  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
     23  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
     24  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     27  */
     28 
     29 #include "config.h"
     30 
     31 #if ENABLE(WEB_AUDIO)
     32 
     33 #include "platform/audio/DynamicsCompressorKernel.h"
     34 
     35 #include <algorithm>
     36 #include "platform/audio/AudioUtilities.h"
     37 #include "platform/audio/DenormalDisabler.h"
     38 #include "wtf/MathExtras.h"
     39 
     40 namespace blink {
     41 
     42 using namespace AudioUtilities;
     43 
     44 // Metering hits peaks instantly, but releases this fast (in seconds).
     45 const float meteringReleaseTimeConstant = 0.325f;
     46 
     47 const float uninitializedValue = -1;
     48 
     49 DynamicsCompressorKernel::DynamicsCompressorKernel(float sampleRate, unsigned numberOfChannels)
     50     : m_sampleRate(sampleRate)
     51     , m_lastPreDelayFrames(DefaultPreDelayFrames)
     52     , m_preDelayReadIndex(0)
     53     , m_preDelayWriteIndex(DefaultPreDelayFrames)
     54     , m_ratio(uninitializedValue)
     55     , m_slope(uninitializedValue)
     56     , m_linearThreshold(uninitializedValue)
     57     , m_dbThreshold(uninitializedValue)
     58     , m_dbKnee(uninitializedValue)
     59     , m_kneeThreshold(uninitializedValue)
     60     , m_kneeThresholdDb(uninitializedValue)
     61     , m_ykneeThresholdDb(uninitializedValue)
     62     , m_K(uninitializedValue)
     63 {
     64     setNumberOfChannels(numberOfChannels);
     65 
     66     // Initializes most member variables
     67     reset();
     68 
     69     m_meteringReleaseK = static_cast<float>(discreteTimeConstantForSampleRate(meteringReleaseTimeConstant, sampleRate));
     70 }
     71 
     72 void DynamicsCompressorKernel::setNumberOfChannels(unsigned numberOfChannels)
     73 {
     74     if (m_preDelayBuffers.size() == numberOfChannels)
     75         return;
     76 
     77     m_preDelayBuffers.clear();
     78     for (unsigned i = 0; i < numberOfChannels; ++i)
     79         m_preDelayBuffers.append(adoptPtr(new AudioFloatArray(MaxPreDelayFrames)));
     80 }
     81 
     82 void DynamicsCompressorKernel::setPreDelayTime(float preDelayTime)
     83 {
     84     // Re-configure look-ahead section pre-delay if delay time has changed.
     85     unsigned preDelayFrames = preDelayTime * sampleRate();
     86     if (preDelayFrames > MaxPreDelayFrames - 1)
     87         preDelayFrames = MaxPreDelayFrames - 1;
     88 
     89     if (m_lastPreDelayFrames != preDelayFrames) {
     90         m_lastPreDelayFrames = preDelayFrames;
     91         for (unsigned i = 0; i < m_preDelayBuffers.size(); ++i)
     92             m_preDelayBuffers[i]->zero();
     93 
     94         m_preDelayReadIndex = 0;
     95         m_preDelayWriteIndex = preDelayFrames;
     96     }
     97 }
     98 
     99 // Exponential curve for the knee.
    100 // It is 1st derivative matched at m_linearThreshold and asymptotically approaches the value m_linearThreshold + 1 / k.
    101 float DynamicsCompressorKernel::kneeCurve(float x, float k)
    102 {
    103     // Linear up to threshold.
    104     if (x < m_linearThreshold)
    105         return x;
    106 
    107     return m_linearThreshold + (1 - expf(-k * (x - m_linearThreshold))) / k;
    108 }
    109 
    110 // Full compression curve with constant ratio after knee.
    111 float DynamicsCompressorKernel::saturate(float x, float k)
    112 {
    113     float y;
    114 
    115     if (x < m_kneeThreshold)
    116         y = kneeCurve(x, k);
    117     else {
    118         // Constant ratio after knee.
    119         float xDb = linearToDecibels(x);
    120         float yDb = m_ykneeThresholdDb + m_slope * (xDb - m_kneeThresholdDb);
    121 
    122         y = decibelsToLinear(yDb);
    123     }
    124 
    125     return y;
    126 }
    127 
    128 // Approximate 1st derivative with input and output expressed in dB.
    129 // This slope is equal to the inverse of the compression "ratio".
    130 // In other words, a compression ratio of 20 would be a slope of 1/20.
    131 float DynamicsCompressorKernel::slopeAt(float x, float k)
    132 {
    133     if (x < m_linearThreshold)
    134         return 1;
    135 
    136     float x2 = x * 1.001;
    137 
    138     float xDb = linearToDecibels(x);
    139     float x2Db = linearToDecibels(x2);
    140 
    141     float yDb = linearToDecibels(kneeCurve(x, k));
    142     float y2Db = linearToDecibels(kneeCurve(x2, k));
    143 
    144     float m = (y2Db - yDb) / (x2Db - xDb);
    145 
    146     return m;
    147 }
    148 
    149 float DynamicsCompressorKernel::kAtSlope(float desiredSlope)
    150 {
    151     float xDb = m_dbThreshold + m_dbKnee;
    152     float x = decibelsToLinear(xDb);
    153 
    154     // Approximate k given initial values.
    155     float minK = 0.1;
    156     float maxK = 10000;
    157     float k = 5;
    158 
    159     for (int i = 0; i < 15; ++i) {
    160         // A high value for k will more quickly asymptotically approach a slope of 0.
    161         float slope = slopeAt(x, k);
    162 
    163         if (slope < desiredSlope) {
    164             // k is too high.
    165             maxK = k;
    166         } else {
    167             // k is too low.
    168             minK = k;
    169         }
    170 
    171         // Re-calculate based on geometric mean.
    172         k = sqrtf(minK * maxK);
    173     }
    174 
    175     return k;
    176 }
    177 
    178 float DynamicsCompressorKernel::updateStaticCurveParameters(float dbThreshold, float dbKnee, float ratio)
    179 {
    180     if (dbThreshold != m_dbThreshold || dbKnee != m_dbKnee || ratio != m_ratio) {
    181         // Threshold and knee.
    182         m_dbThreshold = dbThreshold;
    183         m_linearThreshold = decibelsToLinear(dbThreshold);
    184         m_dbKnee = dbKnee;
    185 
    186         // Compute knee parameters.
    187         m_ratio = ratio;
    188         m_slope = 1 / m_ratio;
    189 
    190         float k = kAtSlope(1 / m_ratio);
    191 
    192         m_kneeThresholdDb = dbThreshold + dbKnee;
    193         m_kneeThreshold = decibelsToLinear(m_kneeThresholdDb);
    194 
    195         m_ykneeThresholdDb = linearToDecibels(kneeCurve(m_kneeThreshold, k));
    196 
    197         m_K = k;
    198     }
    199     return m_K;
    200 }
    201 
    202 void DynamicsCompressorKernel::process(const float* sourceChannels[],
    203                                        float* destinationChannels[],
    204                                        unsigned numberOfChannels,
    205                                        unsigned framesToProcess,
    206 
    207                                        float dbThreshold,
    208                                        float dbKnee,
    209                                        float ratio,
    210                                        float attackTime,
    211                                        float releaseTime,
    212                                        float preDelayTime,
    213                                        float dbPostGain,
    214                                        float effectBlend, /* equal power crossfade */
    215 
    216                                        float releaseZone1,
    217                                        float releaseZone2,
    218                                        float releaseZone3,
    219                                        float releaseZone4
    220                                        )
    221 {
    222     ASSERT(m_preDelayBuffers.size() == numberOfChannels);
    223 
    224     float sampleRate = this->sampleRate();
    225 
    226     float dryMix = 1 - effectBlend;
    227     float wetMix = effectBlend;
    228 
    229     float k = updateStaticCurveParameters(dbThreshold, dbKnee, ratio);
    230 
    231     // Makeup gain.
    232     float fullRangeGain = saturate(1, k);
    233     float fullRangeMakeupGain = 1 / fullRangeGain;
    234 
    235     // Empirical/perceptual tuning.
    236     fullRangeMakeupGain = powf(fullRangeMakeupGain, 0.6f);
    237 
    238     float masterLinearGain = decibelsToLinear(dbPostGain) * fullRangeMakeupGain;
    239 
    240     // Attack parameters.
    241     attackTime = std::max(0.001f, attackTime);
    242     float attackFrames = attackTime * sampleRate;
    243 
    244     // Release parameters.
    245     float releaseFrames = sampleRate * releaseTime;
    246 
    247     // Detector release time.
    248     float satReleaseTime = 0.0025f;
    249     float satReleaseFrames = satReleaseTime * sampleRate;
    250 
    251     // Create a smooth function which passes through four points.
    252 
    253     // Polynomial of the form
    254     // y = a + b*x + c*x^2 + d*x^3 + e*x^4;
    255 
    256     float y1 = releaseFrames * releaseZone1;
    257     float y2 = releaseFrames * releaseZone2;
    258     float y3 = releaseFrames * releaseZone3;
    259     float y4 = releaseFrames * releaseZone4;
    260 
    261     // All of these coefficients were derived for 4th order polynomial curve fitting where the y values
    262     // match the evenly spaced x values as follows: (y1 : x == 0, y2 : x == 1, y3 : x == 2, y4 : x == 3)
    263     float kA = 0.9999999999999998f*y1 + 1.8432219684323923e-16f*y2 - 1.9373394351676423e-16f*y3 + 8.824516011816245e-18f*y4;
    264     float kB = -1.5788320352845888f*y1 + 2.3305837032074286f*y2 - 0.9141194204840429f*y3 + 0.1623677525612032f*y4;
    265     float kC = 0.5334142869106424f*y1 - 1.272736789213631f*y2 + 0.9258856042207512f*y3 - 0.18656310191776226f*y4;
    266     float kD = 0.08783463138207234f*y1 - 0.1694162967925622f*y2 + 0.08588057951595272f*y3 - 0.00429891410546283f*y4;
    267     float kE = -0.042416883008123074f*y1 + 0.1115693827987602f*y2 - 0.09764676325265872f*y3 + 0.028494263462021576f*y4;
    268 
    269     // x ranges from 0 -> 3       0    1    2   3
    270     //                           -15  -10  -5   0db
    271 
    272     // y calculates adaptive release frames depending on the amount of compression.
    273 
    274     setPreDelayTime(preDelayTime);
    275 
    276     const int nDivisionFrames = 32;
    277 
    278     const int nDivisions = framesToProcess / nDivisionFrames;
    279 
    280     unsigned frameIndex = 0;
    281     for (int i = 0; i < nDivisions; ++i) {
    282         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    283         // Calculate desired gain
    284         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    285 
    286         // Fix gremlins.
    287         if (std::isnan(m_detectorAverage))
    288             m_detectorAverage = 1;
    289         if (std::isinf(m_detectorAverage))
    290             m_detectorAverage = 1;
    291 
    292         float desiredGain = m_detectorAverage;
    293 
    294         // Pre-warp so we get desiredGain after sin() warp below.
    295         float scaledDesiredGain = asinf(desiredGain) / (piOverTwoFloat);
    296 
    297         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    298         // Deal with envelopes
    299         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    300 
    301         // envelopeRate is the rate we slew from current compressor level to the desired level.
    302         // The exact rate depends on if we're attacking or releasing and by how much.
    303         float envelopeRate;
    304 
    305         bool isReleasing = scaledDesiredGain > m_compressorGain;
    306 
    307         // compressionDiffDb is the difference between current compression level and the desired level.
    308         float compressionDiffDb = linearToDecibels(m_compressorGain / scaledDesiredGain);
    309 
    310         if (isReleasing) {
    311             // Release mode - compressionDiffDb should be negative dB
    312             m_maxAttackCompressionDiffDb = -1;
    313 
    314             // Fix gremlins.
    315             if (std::isnan(compressionDiffDb))
    316                 compressionDiffDb = -1;
    317             if (std::isinf(compressionDiffDb))
    318                 compressionDiffDb = -1;
    319 
    320             // Adaptive release - higher compression (lower compressionDiffDb)  releases faster.
    321 
    322             // Contain within range: -12 -> 0 then scale to go from 0 -> 3
    323             float x = compressionDiffDb;
    324             x = std::max(-12.0f, x);
    325             x = std::min(0.0f, x);
    326             x = 0.25f * (x + 12);
    327 
    328             // Compute adaptive release curve using 4th order polynomial.
    329             // Normal values for the polynomial coefficients would create a monotonically increasing function.
    330             float x2 = x * x;
    331             float x3 = x2 * x;
    332             float x4 = x2 * x2;
    333             float releaseFrames = kA + kB * x + kC * x2 + kD * x3 + kE * x4;
    334 
    335 #define kSpacingDb 5
    336             float dbPerFrame = kSpacingDb / releaseFrames;
    337 
    338             envelopeRate = decibelsToLinear(dbPerFrame);
    339         } else {
    340             // Attack mode - compressionDiffDb should be positive dB
    341 
    342             // Fix gremlins.
    343             if (std::isnan(compressionDiffDb))
    344                 compressionDiffDb = 1;
    345             if (std::isinf(compressionDiffDb))
    346                 compressionDiffDb = 1;
    347 
    348             // As long as we're still in attack mode, use a rate based off
    349             // the largest compressionDiffDb we've encountered so far.
    350             if (m_maxAttackCompressionDiffDb == -1 || m_maxAttackCompressionDiffDb < compressionDiffDb)
    351                 m_maxAttackCompressionDiffDb = compressionDiffDb;
    352 
    353             float effAttenDiffDb = std::max(0.5f, m_maxAttackCompressionDiffDb);
    354 
    355             float x = 0.25f / effAttenDiffDb;
    356             envelopeRate = 1 - powf(x, 1 / attackFrames);
    357         }
    358 
    359         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    360         // Inner loop - calculate shaped power average - apply compression.
    361         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    362 
    363         {
    364             int preDelayReadIndex = m_preDelayReadIndex;
    365             int preDelayWriteIndex = m_preDelayWriteIndex;
    366             float detectorAverage = m_detectorAverage;
    367             float compressorGain = m_compressorGain;
    368 
    369             int loopFrames = nDivisionFrames;
    370             while (loopFrames--) {
    371                 float compressorInput = 0;
    372 
    373                 // Predelay signal, computing compression amount from un-delayed version.
    374                 for (unsigned i = 0; i < numberOfChannels; ++i) {
    375                     float* delayBuffer = m_preDelayBuffers[i]->data();
    376                     float undelayedSource = sourceChannels[i][frameIndex];
    377                     delayBuffer[preDelayWriteIndex] = undelayedSource;
    378 
    379                     float absUndelayedSource = undelayedSource > 0 ? undelayedSource : -undelayedSource;
    380                     if (compressorInput < absUndelayedSource)
    381                         compressorInput = absUndelayedSource;
    382                 }
    383 
    384                 // Calculate shaped power on undelayed input.
    385 
    386                 float scaledInput = compressorInput;
    387                 float absInput = scaledInput > 0 ? scaledInput : -scaledInput;
    388 
    389                 // Put through shaping curve.
    390                 // This is linear up to the threshold, then enters a "knee" portion followed by the "ratio" portion.
    391                 // The transition from the threshold to the knee is smooth (1st derivative matched).
    392                 // The transition from the knee to the ratio portion is smooth (1st derivative matched).
    393                 float shapedInput = saturate(absInput, k);
    394 
    395                 float attenuation = absInput <= 0.0001f ? 1 : shapedInput / absInput;
    396 
    397                 float attenuationDb = -linearToDecibels(attenuation);
    398                 attenuationDb = std::max(2.0f, attenuationDb);
    399 
    400                 float dbPerFrame = attenuationDb / satReleaseFrames;
    401 
    402                 float satReleaseRate = decibelsToLinear(dbPerFrame) - 1;
    403 
    404                 bool isRelease = (attenuation > detectorAverage);
    405                 float rate = isRelease ? satReleaseRate : 1;
    406 
    407                 detectorAverage += (attenuation - detectorAverage) * rate;
    408                 detectorAverage = std::min(1.0f, detectorAverage);
    409 
    410                 // Fix gremlins.
    411                 if (std::isnan(detectorAverage))
    412                     detectorAverage = 1;
    413                 if (std::isinf(detectorAverage))
    414                     detectorAverage = 1;
    415 
    416                 // Exponential approach to desired gain.
    417                 if (envelopeRate < 1) {
    418                     // Attack - reduce gain to desired.
    419                     compressorGain += (scaledDesiredGain - compressorGain) * envelopeRate;
    420                 } else {
    421                     // Release - exponentially increase gain to 1.0
    422                     compressorGain *= envelopeRate;
    423                     compressorGain = std::min(1.0f, compressorGain);
    424                 }
    425 
    426                 // Warp pre-compression gain to smooth out sharp exponential transition points.
    427                 float postWarpCompressorGain = sinf(piOverTwoFloat * compressorGain);
    428 
    429                 // Calculate total gain using master gain and effect blend.
    430                 float totalGain = dryMix + wetMix * masterLinearGain * postWarpCompressorGain;
    431 
    432                 // Calculate metering.
    433                 float dbRealGain = 20 * std::log10(postWarpCompressorGain);
    434                 if (dbRealGain < m_meteringGain)
    435                     m_meteringGain = dbRealGain;
    436                 else
    437                     m_meteringGain += (dbRealGain - m_meteringGain) * m_meteringReleaseK;
    438 
    439                 // Apply final gain.
    440                 for (unsigned i = 0; i < numberOfChannels; ++i) {
    441                     float* delayBuffer = m_preDelayBuffers[i]->data();
    442                     destinationChannels[i][frameIndex] = delayBuffer[preDelayReadIndex] * totalGain;
    443                 }
    444 
    445                 frameIndex++;
    446                 preDelayReadIndex = (preDelayReadIndex + 1) & MaxPreDelayFramesMask;
    447                 preDelayWriteIndex = (preDelayWriteIndex + 1) & MaxPreDelayFramesMask;
    448             }
    449 
    450             // Locals back to member variables.
    451             m_preDelayReadIndex = preDelayReadIndex;
    452             m_preDelayWriteIndex = preDelayWriteIndex;
    453             m_detectorAverage = DenormalDisabler::flushDenormalFloatToZero(detectorAverage);
    454             m_compressorGain = DenormalDisabler::flushDenormalFloatToZero(compressorGain);
    455         }
    456     }
    457 }
    458 
    459 void DynamicsCompressorKernel::reset()
    460 {
    461     m_detectorAverage = 0;
    462     m_compressorGain = 1;
    463     m_meteringGain = 1;
    464 
    465     // Predelay section.
    466     for (unsigned i = 0; i < m_preDelayBuffers.size(); ++i)
    467         m_preDelayBuffers[i]->zero();
    468 
    469     m_preDelayReadIndex = 0;
    470     m_preDelayWriteIndex = DefaultPreDelayFrames;
    471 
    472     m_maxAttackCompressionDiffDb = -1; // uninitialized state
    473 }
    474 
    475 } // namespace blink
    476 
    477 #endif // ENABLE(WEB_AUDIO)
    478