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