Have the DynamicsCompressorNode support multi-channel data
[WebKit-https.git] / Source / WebCore / platform / audio / DynamicsCompressorKernel.cpp
index a322a33..3e943d1 100644 (file)
@@ -52,20 +52,30 @@ static float saturate(float x, float k)
     return 1 - exp(-k * x);
 }
 
-DynamicsCompressorKernel::DynamicsCompressorKernel(float sampleRate)
+DynamicsCompressorKernel::DynamicsCompressorKernel(float sampleRate, unsigned numberOfChannels)
     : m_sampleRate(sampleRate)
     , m_lastPreDelayFrames(DefaultPreDelayFrames)
-    , m_preDelayBufferL(MaxPreDelayFrames)
-    , m_preDelayBufferR(MaxPreDelayFrames)
     , m_preDelayReadIndex(0)
     , m_preDelayWriteIndex(DefaultPreDelayFrames)
 {
+    setNumberOfChannels(numberOfChannels);
+
     // Initializes most member variables
     reset();
     
     m_meteringReleaseK = 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.
@@ -75,17 +85,17 @@ void DynamicsCompressorKernel::setPreDelayTime(float preDelayTime)
         
     if (m_lastPreDelayFrames != preDelayFrames) {
         m_lastPreDelayFrames = preDelayFrames;
-        m_preDelayBufferL.zero();
-        m_preDelayBufferR.zero();
+        for (unsigned i = 0; i < m_preDelayBuffers.size(); ++i)
+            m_preDelayBuffers[i]->zero();
+
         m_preDelayReadIndex = 0;
         m_preDelayWriteIndex = preDelayFrames;
     }
 }
 
-void DynamicsCompressorKernel::process(const float* sourceL,
-                                       float* destinationL,
-                                       const float* sourceR, /* stereo-linked */
-                                       float* destinationR,
+void DynamicsCompressorKernel::process(float* sourceChannels[],
+                                       float* destinationChannels[],
+                                       unsigned numberOfChannels,
                                        unsigned framesToProcess,
 
                                        float dbThreshold,
@@ -102,7 +112,8 @@ void DynamicsCompressorKernel::process(const float* sourceL,
                                        float releaseZone4
                                        )
 {
-    bool isStereo = destinationR;
+    ASSERT(m_preDelayBuffers.size() == numberOfChannels);
+
     float sampleRate = this->sampleRate();
 
     float dryMix = 1 - effectBlend;
@@ -164,6 +175,7 @@ void DynamicsCompressorKernel::process(const float* sourceL,
 
     const int nDivisions = framesToProcess / nDivisionFrames;
 
+    unsigned frameIndex = 0;
     for (int i = 0; i < nDivisions; ++i) {
         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
         // Calculate desired gain
@@ -247,8 +259,6 @@ void DynamicsCompressorKernel::process(const float* sourceL,
         // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
         {
-            float* delayBufferL = m_preDelayBufferL.data();
-            float* delayBufferR = m_preDelayBufferR.data();
             int preDelayReadIndex = m_preDelayReadIndex;
             int preDelayWriteIndex = m_preDelayWriteIndex;
             float detectorAverage = m_detectorAverage;
@@ -256,32 +266,19 @@ void DynamicsCompressorKernel::process(const float* sourceL,
 
             int loopFrames = nDivisionFrames;
             while (loopFrames--) {
-                float compressorInput;
-                float inputL;
-                float inputR = 0;
+                float compressorInput = 0;
 
                 // Predelay signal, computing compression amount from un-delayed version.
-                if (isStereo) {
-                    float undelayedL = *sourceL++;
-                    float undelayedR = *sourceR++;
-
-                    compressorInput = 0.5f * (undelayedL + undelayedR);
-
-                    inputL = delayBufferL[preDelayReadIndex];
-                    inputR = delayBufferR[preDelayReadIndex];
-
-                    delayBufferL[preDelayWriteIndex] = undelayedL;
-                    delayBufferR[preDelayWriteIndex] = undelayedR;
-                } else {
-                    compressorInput = *sourceL++;
-
-                    inputL = delayBufferL[preDelayReadIndex];
-                    delayBufferL[preDelayWriteIndex] = compressorInput;
+                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;
                 }
 
-                preDelayReadIndex = (preDelayReadIndex + 1) & MaxPreDelayFramesMask;
-                preDelayWriteIndex = (preDelayWriteIndex + 1) & MaxPreDelayFramesMask;
-
                 // Calculate shaped power on undelayed input.
 
                 float scaledInput = compressorInput;
@@ -337,17 +334,14 @@ void DynamicsCompressorKernel::process(const float* sourceL,
                     m_meteringGain += (dbRealGain - m_meteringGain) * m_meteringReleaseK;
 
                 // Apply final gain.
-                if (isStereo) {
-                    float outputL = inputL;
-                    float outputR = inputR;
-
-                    outputL *= totalGain;
-                    outputR *= totalGain;
+                for (unsigned i = 0; i < numberOfChannels; ++i) {
+                    float* delayBuffer = m_preDelayBuffers[i]->data();
+                    destinationChannels[i][frameIndex] = delayBuffer[preDelayReadIndex] * totalGain;
+                }
 
-                    *destinationL++ = outputL;
-                    *destinationR++ = outputR;
-                } else
-                    *destinationL++ = inputL * totalGain;
+                frameIndex++;
+                preDelayReadIndex = (preDelayReadIndex + 1) & MaxPreDelayFramesMask;
+                preDelayWriteIndex = (preDelayWriteIndex + 1) & MaxPreDelayFramesMask;
             }
 
             // Locals back to member variables.
@@ -366,8 +360,9 @@ void DynamicsCompressorKernel::reset()
     m_meteringGain = 1;
 
     // Predelay section.
-    m_preDelayBufferL.zero();
-    m_preDelayBufferR.zero();
+    for (unsigned i = 0; i < m_preDelayBuffers.size(); ++i)
+        m_preDelayBuffers[i]->zero();
+
     m_preDelayReadIndex = 0;
     m_preDelayWriteIndex = DefaultPreDelayFrames;