content/media/webaudio/blink/DynamicsCompressorKernel.cpp

Fri, 16 Jan 2015 04:50:19 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Fri, 16 Jan 2015 04:50:19 +0100
branch
TOR_BUG_9701
changeset 13
44a2da4a2ab2
permissions
-rw-r--r--

Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32

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

mercurial