Improve Compressor lookahead algorithm (#6953)

This commit is contained in:
Lost Robot
2023-11-08 12:53:59 -08:00
committed by GitHub
parent 6d4343ca94
commit 8c194fc29c
3 changed files with 38 additions and 100 deletions

View File

@@ -60,9 +60,6 @@ CompressorEffect::CompressorEffect(Model* parent, const Descriptor::SubPluginFea
m_yL[0] = m_yL[1] = COMP_NOISE_FLOOR;
m_maxLookaheadVal[0] = 0;
m_maxLookaheadVal[1] = 0;
// 200 ms
m_crestTimeConst = exp(-1.f / (0.2f * m_sampleRate));
@@ -183,9 +180,7 @@ void CompressorEffect::resizeRMS()
void CompressorEffect::calcLookaheadLength()
{
m_lookaheadLength = qMax(m_compressorControls.m_lookaheadLengthModel.value() * 0.001f * m_sampleRate, 1.f);
m_preLookaheadLength = ceil(m_lookaheadDelayLength - m_lookaheadLength);
m_lookaheadLength = std::ceil((m_compressorControls.m_lookaheadLengthModel.value() / 1000.f) * m_sampleRate);
}
void CompressorEffect::calcThreshold()
@@ -249,17 +244,10 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
m_gainResult[0] = m_gainResult[1] = 1;
m_displayPeak[0] = m_displayPeak[1] = COMP_NOISE_FLOOR;
m_displayGain[0] = m_displayGain[1] = COMP_NOISE_FLOOR;
std::fill(std::begin(m_lookaheadBuf[0]), std::end(m_lookaheadBuf[0]), 0);
std::fill(std::begin(m_lookaheadBuf[1]), std::end(m_lookaheadBuf[1]), 0);
m_lookaheadBufLoc[0] = 0;
m_lookaheadBufLoc[1] = 0;
std::fill(std::begin(m_preLookaheadBuf[0]), std::end(m_preLookaheadBuf[0]), 0);
std::fill(std::begin(m_preLookaheadBuf[1]), std::end(m_preLookaheadBuf[1]), 0);
m_preLookaheadBufLoc[0] = 0;
m_preLookaheadBufLoc[1] = 0;
std::fill(std::begin(m_inputBuf[0]), std::end(m_inputBuf[0]), 0);
std::fill(std::begin(m_inputBuf[1]), std::end(m_inputBuf[1]), 0);
m_inputBufLoc = 0;
std::fill(std::begin(m_scLookBuf[0]), std::end(m_scLookBuf[0]), COMP_NOISE_FLOOR);
std::fill(std::begin(m_scLookBuf[1]), std::end(m_scLookBuf[1]), COMP_NOISE_FLOOR);
std::fill(std::begin(m_inLookBuf[0]), std::end(m_inLookBuf[0]), 0);
std::fill(std::begin(m_inLookBuf[1]), std::end(m_inLookBuf[1]), 0);
m_cleanedBuffers = true;
}
return false;
@@ -318,7 +306,7 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
for (int i = 0; i < 2; i++)
{
float inputValue = feedback ? m_prevOut[i] : s[i];
float inputValue = (feedback && !lookahead) ? m_prevOut[i] : s[i];
// Calculate the crest factor of the audio by diving the peak by the RMS
m_crestPeakVal[i] = qMax(qMax(COMP_NOISE_FLOOR, inputValue * inputValue), m_crestTimeConst * m_crestPeakVal[i] + (1 - m_crestTimeConst) * (inputValue * inputValue));
@@ -330,53 +318,6 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
// Grab the peak or RMS value
inputValue = qMax(COMP_NOISE_FLOOR, peakmode ? std::abs(inputValue) : std::sqrt(m_rmsVal[i]));
// The following code uses math magic to semi-efficiently
// find the largest value in the lookahead buffer.
// This can probably be improved.
if (lookahead)
{
// Pre-lookahead delay, so the total delay always matches 20 ms
++m_preLookaheadBufLoc[i];
if (m_preLookaheadBufLoc[i] >= m_preLookaheadLength)
{
m_preLookaheadBufLoc[i] = 0;
}
const float tempInputValue = inputValue;
inputValue = m_preLookaheadBuf[i][m_preLookaheadBufLoc[i]];
m_preLookaheadBuf[i][m_preLookaheadBufLoc[i]] = tempInputValue;
// Increment ring buffer location
++m_lookaheadBufLoc[i];
if (m_lookaheadBufLoc[i] >= m_lookaheadLength)
{
m_lookaheadBufLoc[i] = 0;
}
m_lookaheadBuf[i][m_lookaheadBufLoc[i]] = inputValue;
// If the new input value is larger than the stored maximum,
// store that as the maximum
if (inputValue >= m_maxLookaheadVal[i])
{
m_maxLookaheadVal[i] = inputValue;
m_maxLookaheadTimer[i] = m_lookaheadLength;
}
// Decrement timer. When the timer reaches 0, that means the
// stored maximum value has left the buffer and a new
// maximum value must be found.
if (--m_maxLookaheadTimer[i] <= 0)
{
m_maxLookaheadTimer[i] = std::distance(std::begin(m_lookaheadBuf[i]),
std::max_element(std::begin(m_lookaheadBuf[i]), std::begin(m_lookaheadBuf[i]) + m_lookaheadLength));
m_maxLookaheadVal[i] = m_lookaheadBuf[i][m_maxLookaheadTimer[i]];
m_maxLookaheadTimer[i] = realmod(m_maxLookaheadTimer[i] - m_lookaheadBufLoc[i], m_lookaheadLength);
}
inputValue = m_maxLookaheadVal[i];
}
float t = inputValue;
if (t > m_yL[i])// Attack phase
@@ -415,11 +356,22 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
// Keep it above the noise floor
m_yL[i] = qMax(COMP_NOISE_FLOOR, m_yL[i]);
float scVal = m_yL[i];
if (lookahead)
{
const float temp = scVal;
// Lookahead is calculated by picking the largest value between
// the current sidechain signal and the delayed sidechain signal.
scVal = std::max(m_scLookBuf[i][m_lookWrite], m_scLookBuf[i][(m_lookWrite + m_lookBufLength - m_lookaheadLength) % m_lookBufLength]);
m_scLookBuf[i][m_lookWrite] = temp;
}
// For the visualizer
m_displayPeak[i] = qMax(m_yL[i], m_displayPeak[i]);
m_displayPeak[i] = qMax(scVal, m_displayPeak[i]);
const float currentPeakDbfs = ampToDbfs(m_yL[i]);
const float currentPeakDbfs = ampToDbfs(scVal);
// Now find the gain change that should be applied,
// depending on the measured input value.
@@ -439,7 +391,7 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
: m_thresholdVal + (currentPeakDbfs - m_thresholdVal) * m_ratioVal;
}
m_gainResult[i] = dbfsToAmp(m_gainResult[i]) / m_yL[i];
m_gainResult[i] = dbfsToAmp(m_gainResult[i]) / scVal;
m_gainResult[i] = qMax(m_rangeVal, m_gainResult[i]);
}
@@ -507,18 +459,10 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
// Delay the signal by 20 ms via ring buffer if lookahead is enabled
if (lookahead)
{
++m_inputBufLoc;
if (m_inputBufLoc >= m_lookaheadDelayLength)
{
m_inputBufLoc = 0;
}
const auto temp = std::array{drySignal[0], drySignal[1]};
s[0] = m_inputBuf[0][m_inputBufLoc];
s[1] = m_inputBuf[1][m_inputBufLoc];
m_inputBuf[0][m_inputBufLoc] = temp[0];
m_inputBuf[1][m_inputBufLoc] = temp[1];
s[0] = m_inLookBuf[0][m_lookWrite];
s[1] = m_inLookBuf[1][m_lookWrite];
m_inLookBuf[0][m_lookWrite] = drySignal[0];
m_inLookBuf[1][m_lookWrite] = drySignal[1];
}
else
{
@@ -573,6 +517,8 @@ bool CompressorEffect::processAudioBuffer(sampleFrame* buf, const fpp_t frames)
buf[f][1] = (1 - m_mixVal) * temp2 + m_mixVal * buf[f][1];
outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
if (--m_lookWrite < 0) { m_lookWrite = m_lookBufLength - 1; }
lInPeak = drySignal[0] > lInPeak ? drySignal[0] : lInPeak;
rInPeak = drySignal[1] > rInPeak ? drySignal[1] : rInPeak;
@@ -621,16 +567,13 @@ void CompressorEffect::changeSampleRate()
// 200 ms
m_crestTimeConst = exp(-1.f / (0.2f * m_sampleRate));
// 20 ms
m_lookaheadDelayLength = 0.02 * m_sampleRate;
m_inputBuf[0].resize(m_lookaheadDelayLength);
m_inputBuf[1].resize(m_lookaheadDelayLength);
m_lookaheadBuf[0].resize(m_lookaheadDelayLength);
m_lookaheadBuf[1].resize(m_lookaheadDelayLength);
m_preLookaheadBuf[0].resize(m_lookaheadDelayLength);
m_preLookaheadBuf[1].resize(m_lookaheadDelayLength);
m_lookBufLength = std::ceil((20.f / 1000.f) * m_sampleRate) + 2;
for (int i = 0; i < 2; ++i)
{
m_inLookBuf[i].resize(m_lookBufLength);
m_scLookBuf[i].resize(m_lookBufLength, COMP_NOISE_FLOOR);
}
m_lookWrite = 0;
calcThreshold();
calcKnee();

View File

@@ -81,14 +81,10 @@ private:
enum class StereoLinkMode { Unlinked, Maximum, Average, Minimum, Blend };
std::vector<float> m_preLookaheadBuf[2];
int m_preLookaheadBufLoc[2] = {0};
std::vector<float> m_lookaheadBuf[2];
int m_lookaheadBufLoc[2] = {0};
std::vector<float> m_inputBuf[2];
int m_inputBufLoc = 0;
std::array<std::vector<float>, 2> m_inLookBuf;
std::array<std::vector<float>, 2> m_scLookBuf;
int m_lookWrite;
int m_lookBufLength;
float m_attCoeff;
float m_relCoeff;
@@ -99,8 +95,6 @@ private:
int m_holdTimer[2] = {0, 0};
int m_lookaheadLength;
int m_lookaheadDelayLength;
int m_preLookaheadLength;
float m_thresholdAmpVal;
float m_autoMakeupVal;
float m_outGainVal;

View File

@@ -358,6 +358,7 @@ void CompressorControlDialog::lookaheadChanged()
{
m_lookaheadLengthKnob->setVisible(m_controls->m_lookaheadModel.value());
m_lookaheadEnabledLabel->setVisible(m_controls->m_lookaheadModel.value());
feedbackButton->setVisible(!m_controls->m_lookaheadModel.value());
}