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.
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,
float releaseZone4
)
{
- bool isStereo = destinationR;
+ ASSERT(m_preDelayBuffers.size() == numberOfChannels);
+
float sampleRate = this->sampleRate();
float dryMix = 1 - effectBlend;
const int nDivisions = framesToProcess / nDivisionFrames;
+ unsigned frameIndex = 0;
for (int i = 0; i < nDivisions; ++i) {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Calculate desired gain
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
{
- float* delayBufferL = m_preDelayBufferL.data();
- float* delayBufferR = m_preDelayBufferR.data();
int preDelayReadIndex = m_preDelayReadIndex;
int preDelayWriteIndex = m_preDelayWriteIndex;
float detectorAverage = m_detectorAverage;
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;
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.
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;