Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor Effect processing #7484

Merged
merged 17 commits into from
Sep 21, 2024
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions include/AudioEngine.h
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,15 @@ class AudioPort;
class AudioEngineWorkerThread;


const fpp_t MINIMUM_BUFFER_SIZE = 32;
const fpp_t DEFAULT_BUFFER_SIZE = 256;
constexpr fpp_t MINIMUM_BUFFER_SIZE = 32;
constexpr fpp_t DEFAULT_BUFFER_SIZE = 256;
constexpr fpp_t MAXIMUM_BUFFER_SIZE = 4096;
messmerd marked this conversation as resolved.
Show resolved Hide resolved

const int BYTES_PER_SAMPLE = sizeof( sample_t );
const int BYTES_PER_INT_SAMPLE = sizeof( int_sample_t );
const int BYTES_PER_FRAME = sizeof( SampleFrame );
constexpr int BYTES_PER_SAMPLE = sizeof(sample_t);
constexpr int BYTES_PER_INT_SAMPLE = sizeof(int_sample_t);
constexpr int BYTES_PER_FRAME = sizeof(SampleFrame);

const float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f;
constexpr float OUTPUT_SAMPLE_MULTIPLIER = 32767.0f;

class LMMS_EXPORT AudioEngine : public QObject
{
Expand Down
5 changes: 3 additions & 2 deletions include/DummyEffect.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,7 @@ class DummyEffect : public Effect
m_originalPluginData( originalPluginData )
{
setName();
setDontRun(true);
}

~DummyEffect() override = default;
Expand All @@ -107,9 +108,9 @@ class DummyEffect : public Effect
return &m_controls;
}

bool processAudioBuffer( SampleFrame*, const fpp_t ) override
ProcessStatus processImpl(SampleFrame*, const fpp_t) override
{
return false;
return ProcessStatus::Sleep;
}

const QDomElement& originalPluginData() const
Expand Down
40 changes: 31 additions & 9 deletions include/Effect.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,8 @@ class LMMS_EXPORT Effect : public Plugin
return "effect";
}


virtual bool processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames ) = 0;
//! Returns true if audio was processed and should continue being processed
bool processAudioBuffer(SampleFrame* buf, const fpp_t frames);

inline ch_cnt_t processorCount() const
{
Expand Down Expand Up @@ -174,14 +173,29 @@ class LMMS_EXPORT Effect : public Plugin


protected:
enum class ProcessStatus
{
//! Unconditionally continue processing
Continue,

//! Calculate the RMS out sum and call `checkGate` to determine whether to stop processing
ContinueIfNotQuiet,

//! Do not continue processing
Sleep
};

/**
Effects should call this at the end of audio processing
* The main audio processing method that runs when plugin is not asleep
*/
virtual ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) = 0;

/**
* Optional method that runs when plugin is sleeping (not enabled,
* not running, not in the Okay state, or in the Don't Run state)
*/
virtual void processBypassedImpl() {}

If the setting "Keep effects running even without input" is disabled,
after "decay" ms of a signal below "gate", the effect is turned off
and won't be processed again until it receives new audio input
*/
void checkGate( double _out_sum );

gui::PluginView* instantiateView( QWidget * ) override;

Expand Down Expand Up @@ -212,6 +226,14 @@ class LMMS_EXPORT Effect : public Plugin


private:
/**
JohannesLorenz marked this conversation as resolved.
Show resolved Hide resolved
If the setting "Keep effects running even without input" is disabled,
after "decay" ms of a signal below "gate", the effect is turned off
and won't be processed again until it receives new audio input
*/
void checkGate(double outSum);


EffectChain * m_parent;
void resample( int _i, const SampleFrame* _src_buf,
sample_rate_t _src_sr,
Expand Down
11 changes: 2 additions & 9 deletions plugins/Amplifier/Amplifier.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -57,11 +57,8 @@ AmplifierEffect::AmplifierEffect(Model* parent, const Descriptor::SubPluginFeatu
}


bool AmplifierEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
Effect::ProcessStatus AmplifierEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if (!isEnabled() || !isRunning()) { return false ; }

double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();

Expand All @@ -86,13 +83,9 @@ bool AmplifierEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)

// Dry/wet mix
currentFrame = currentFrame * d + s * w;

outSum += currentFrame.sumOfSquaredAmplitudes();
}

checkGate(outSum / frames);

return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}


Expand Down
3 changes: 2 additions & 1 deletion plugins/Amplifier/Amplifier.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ class AmplifierEffect : public Effect
public:
AmplifierEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key);
~AmplifierEffect() override = default;
bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override;

ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;

EffectControls* controls() override
{
Expand Down
13 changes: 2 additions & 11 deletions plugins/BassBooster/BassBooster.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,12 +69,8 @@ BassBoosterEffect::BassBoosterEffect( Model* parent, const Descriptor::SubPlugin



bool BassBoosterEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
Effect::ProcessStatus BassBoosterEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
return( false );
}
// check out changed controls
if( m_frequencyChangeNeeded || m_bbControls.m_freqModel.isValueChanged() )
{
Expand All @@ -87,7 +83,6 @@ bool BassBoosterEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames
const float const_gain = m_bbControls.m_gainModel.value();
const ValueBuffer *gainBuffer = m_bbControls.m_gainModel.valueBuffer();

double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();

Expand All @@ -102,13 +97,9 @@ bool BassBoosterEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames

// Dry/wet mix
currentFrame = currentFrame * d + s * w;

outSum += currentFrame.sumOfSquaredAmplitudes();
}

checkGate( outSum / frames );

return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}


Expand Down
3 changes: 2 additions & 1 deletion plugins/BassBooster/BassBooster.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ class BassBoosterEffect : public Effect
public:
BassBoosterEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key );
~BassBoosterEffect() override = default;
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;

ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;

EffectControls* controls() override
{
Expand Down
13 changes: 2 additions & 11 deletions plugins/Bitcrush/Bitcrush.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,157 +100,148 @@
return fastRandf( amt * 2.0f ) - amt;
}

bool BitcrushEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
Effect::ProcessStatus BitcrushEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
return( false );
}

// update values
if( m_needsUpdate || m_controls.m_rateEnabled.isValueChanged() )
{
m_rateEnabled = m_controls.m_rateEnabled.value();
m_bitCounterL = 0.0f;
m_bitCounterR = 0.0f;
}
if( m_needsUpdate || m_controls.m_depthEnabled.isValueChanged() )
{
m_depthEnabled = m_controls.m_depthEnabled.value();
}
if( m_needsUpdate || m_controls.m_rate.isValueChanged() || m_controls.m_stereoDiff.isValueChanged() )
{
const float rate = m_controls.m_rate.value();
const float diff = m_controls.m_stereoDiff.value() * 0.005 * rate;

m_rateCoeffL = ( m_sampleRate * OS_RATE ) / ( rate - diff );
m_rateCoeffR = ( m_sampleRate * OS_RATE ) / ( rate + diff );

m_bitCounterL = 0.0f;
m_bitCounterR = 0.0f;
}
if( m_needsUpdate || m_controls.m_levels.isValueChanged() )
{
m_levels = m_controls.m_levels.value();
m_levelsRatio = 1.0f / (float) m_levels;
}
if( m_needsUpdate || m_controls.m_inGain.isValueChanged() )
{
m_inGain = dbfsToAmp( m_controls.m_inGain.value() );
}
if( m_needsUpdate || m_controls.m_outGain.isValueChanged() )
{
m_outGain = dbfsToAmp( m_controls.m_outGain.value() );
}
if( m_needsUpdate || m_controls.m_outClip.isValueChanged() )
{
m_outClip = dbfsToAmp( m_controls.m_outClip.value() );
}
m_needsUpdate = false;

const float noiseAmt = m_controls.m_inNoise.value() * 0.01f;

// read input buffer and write it to oversampled buffer
if( m_rateEnabled ) // rate crushing enabled so do that
{
for (auto f = std::size_t{0}; f < frames; ++f)
{
for( int o = 0; o < OS_RATE; ++o )
{
m_buffer[f * OS_RATE + o][0] = m_left;
m_buffer[f * OS_RATE + o][1] = m_right;
m_bitCounterL += 1.0f;
m_bitCounterR += 1.0f;
if( m_bitCounterL > m_rateCoeffL )
{
m_bitCounterL -= m_rateCoeffL;
m_left = m_depthEnabled
? depthCrush( buf[f][0] * m_inGain + noise( buf[f][0] * noiseAmt ) )
: buf[f][0] * m_inGain + noise( buf[f][0] * noiseAmt );
}
if( m_bitCounterR > m_rateCoeffR )
{
m_bitCounterR -= m_rateCoeffR;
m_right = m_depthEnabled
? depthCrush( buf[f][1] * m_inGain + noise( buf[f][1] * noiseAmt ) )
: buf[f][1] * m_inGain + noise( buf[f][1] * noiseAmt );
}
}
}
}
else // rate crushing disabled: simply oversample with zero-order hold
{
for (auto f = std::size_t{0}; f < frames; ++f)
{
for( int o = 0; o < OS_RATE; ++o )
{
m_buffer[f * OS_RATE + o][0] = m_depthEnabled
? depthCrush( buf[f][0] * m_inGain + noise( buf[f][0] * noiseAmt ) )
: buf[f][0] * m_inGain + noise( buf[f][0] * noiseAmt );
m_buffer[f * OS_RATE + o][1] = m_depthEnabled
? depthCrush( buf[f][1] * m_inGain + noise( buf[f][1] * noiseAmt ) )
: buf[f][1] * m_inGain + noise( buf[f][1] * noiseAmt );
}
}
}

// the oversampled buffer is now written, so filter it to reduce aliasing

for (auto f = std::size_t{0}; f < frames * OS_RATE; ++f)
{
if( qMax( qAbs( m_buffer[f][0] ), qAbs( m_buffer[f][1] ) ) >= 1.0e-10f )
{
m_silenceCounter = 0;
m_buffer[f][0] = m_filter.update( m_buffer[f][0], 0 );
m_buffer[f][1] = m_filter.update( m_buffer[f][1], 1 );
}
else
{
if( m_silenceCounter > SILENCEFRAMES )
{
m_buffer[f][0] = m_buffer[f][1] = 0.0f;
}
else
{
++m_silenceCounter;
m_buffer[f][0] = m_filter.update( m_buffer[f][0], 0 );
m_buffer[f][1] = m_filter.update( m_buffer[f][1], 1 );
}
}
}


// now downsample and write it back to main buffer

double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();
for (auto f = std::size_t{0}; f < frames; ++f)
{
float lsum = 0.0f;
float rsum = 0.0f;
for( int o = 0; o < OS_RATE; ++o )
{
lsum += m_buffer[f * OS_RATE + o][0] * OS_RESAMPLE[o];
rsum += m_buffer[f * OS_RATE + o][1] * OS_RESAMPLE[o];
}
buf[f][0] = d * buf[f][0] + w * qBound( -m_outClip, lsum, m_outClip ) * m_outGain;
buf[f][1] = d * buf[f][1] + w * qBound( -m_outClip, rsum, m_outClip ) * m_outGain;
outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1];
}

checkGate( outSum / frames );

return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}


extern "C"
{

// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* parent, void* data )
{

Check notice on line 244 in plugins/Bitcrush/Bitcrush.cpp

View check run for this annotation

codefactor.io / CodeFactor

plugins/Bitcrush/Bitcrush.cpp#L103-L244

Complex Method
return new BitcrushEffect( parent, static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>( data ) );
}

Expand Down
5 changes: 3 additions & 2 deletions plugins/Bitcrush/Bitcrush.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,14 @@ class BitcrushEffect : public Effect
public:
BitcrushEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key );
~BitcrushEffect() override;
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;

ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;

EffectControls* controls() override
{
return &m_controls;
}

private:
void sampleRateChanged();
float depthCrush( float in );
Expand Down
46 changes: 19 additions & 27 deletions plugins/Compressor/Compressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,308 +233,300 @@



bool CompressorEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
Effect::ProcessStatus CompressorEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if (!isEnabled() || !isRunning())
{
// Clear lookahead buffers and other values when needed
if (!m_cleanedBuffers)
{
m_yL[0] = m_yL[1] = COMP_NOISE_FLOOR;
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_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;
}
else
{
m_cleanedBuffers = false;
}
m_cleanedBuffers = false;

float outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();

float lOutPeak = 0.0;
float rOutPeak = 0.0;
float lInPeak = 0.0;
float rInPeak = 0.0;

const bool midside = m_compressorControls.m_midsideModel.value();
const bool peakmode = m_compressorControls.m_peakmodeModel.value();
const float inBalance = m_compressorControls.m_inBalanceModel.value();
const float outBalance = m_compressorControls.m_outBalanceModel.value();
const bool limiter = m_compressorControls.m_limiterModel.value();
const float blend = m_compressorControls.m_blendModel.value();
const float stereoBalance = m_compressorControls.m_stereoBalanceModel.value();
const bool autoMakeup = m_compressorControls.m_autoMakeupModel.value();
const int stereoLink = m_compressorControls.m_stereoLinkModel.value();
const bool audition = m_compressorControls.m_auditionModel.value();
const bool feedback = m_compressorControls.m_feedbackModel.value();
const bool lookahead = m_compressorControls.m_lookaheadModel.value();

for(fpp_t f = 0; f < frames; ++f)
{
auto drySignal = std::array{buf[f][0], buf[f][1]};
auto s = std::array{drySignal[0] * m_inGainVal, drySignal[1] * m_inGainVal};

// Calculate tilt filters, to bias the sidechain to the low or high frequencies
if (m_tiltVal)
{
calcTiltFilter(s[0], s[0], 0);
calcTiltFilter(s[1], s[1], 1);
}

if (midside)// Convert left/right to mid/side
{
const float temp = s[0];
s[0] = (temp + s[1]) * 0.5;
s[1] = temp - s[1];
}

s[0] *= inBalance > 0 ? 1 - inBalance : 1;
s[1] *= inBalance < 0 ? 1 + inBalance : 1;

m_gainResult[0] = 0;
m_gainResult[1] = 0;

for (int i = 0; i < 2; 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));
m_crestRmsVal[i] = qMax(COMP_NOISE_FLOOR, m_crestTimeConst * m_crestRmsVal[i] + ((1 - m_crestTimeConst) * (inputValue * inputValue)));
m_crestFactorVal[i] = m_crestPeakVal[i] / m_crestRmsVal[i];

m_rmsVal[i] = m_rmsTimeConst * m_rmsVal[i] + ((1 - m_rmsTimeConst) * (inputValue * inputValue));

// Grab the peak or RMS value
inputValue = qMax(COMP_NOISE_FLOOR, peakmode ? std::abs(inputValue) : std::sqrt(m_rmsVal[i]));

float t = inputValue;

if (t > m_yL[i])// Attack phase
{
// We want the "resting value" of our crest factor to be with a sine wave,
// which with this variable has a value of 2.
// So, we pull this value down to 0, and multiply it by the percentage of
// automatic attack control that is applied. We then add 2 back to it.
float crestFactorValTemp = ((m_crestFactorVal[i] - 2.f) * m_autoAttVal) + 2.f;

// Calculate attack value depending on crest factor
const float att = m_autoAttVal
? msToCoeff(2.f * m_compressorControls.m_attackModel.value() / (crestFactorValTemp))
: m_attCoeff;

m_yL[i] = m_yL[i] * att + (1 - att) * t;
m_holdTimer[i] = m_holdLength;// Reset hold timer
}
else// Release phase
{
float crestFactorValTemp = ((m_crestFactorVal[i] - 2.f) * m_autoRelVal) + 2.f;

const float rel = m_autoRelVal
? msToCoeff(2.f * m_compressorControls.m_releaseModel.value() / (crestFactorValTemp))
: m_relCoeff;

if (m_holdTimer[i])// Don't change peak if hold is being applied
{
--m_holdTimer[i];
}
else
{
m_yL[i] = m_yL[i] * rel + (1 - rel) * t;
}
}

// 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(scVal, m_displayPeak[i]);

const float currentPeakDbfs = ampToDbfs(scVal);

// Now find the gain change that should be applied,
// depending on the measured input value.
if (currentPeakDbfs - m_thresholdVal < -m_kneeVal)// Below knee
{
m_gainResult[i] = currentPeakDbfs;
}
else if (currentPeakDbfs - m_thresholdVal < m_kneeVal)// Within knee
{
const float temp = currentPeakDbfs - m_thresholdVal + m_kneeVal;
m_gainResult[i] = currentPeakDbfs + ((limiter ? 0 : m_ratioVal) - 1) * temp * temp / (4 * m_kneeVal);
}
else// Above knee
{
m_gainResult[i] = limiter
? m_thresholdVal
: m_thresholdVal + (currentPeakDbfs - m_thresholdVal) * m_ratioVal;
}

m_gainResult[i] = dbfsToAmp(m_gainResult[i]) / scVal;
m_gainResult[i] = qMax(m_rangeVal, m_gainResult[i]);
}

switch (static_cast<StereoLinkMode>(stereoLink))
{
case StereoLinkMode::Unlinked:
{
break;
}
case StereoLinkMode::Maximum:
{
m_gainResult[0] = m_gainResult[1] = qMin(m_gainResult[0], m_gainResult[1]);
break;
}
case StereoLinkMode::Average:
{
m_gainResult[0] = m_gainResult[1] = (m_gainResult[0] + m_gainResult[1]) * 0.5f;
break;
}
case StereoLinkMode::Minimum:
{
m_gainResult[0] = m_gainResult[1] = qMax(m_gainResult[0], m_gainResult[1]);
break;
}
case StereoLinkMode::Blend:
{
if (blend > 0)// 0 is unlinked
{
if (blend <= 1)// Blend to minimum volume
{
const float temp1 = qMin(m_gainResult[0], m_gainResult[1]);
m_gainResult[0] = linearInterpolate(m_gainResult[0], temp1, blend);
m_gainResult[1] = linearInterpolate(m_gainResult[1], temp1, blend);
}
else if (blend <= 2)// Blend to average volume
{
const float temp1 = qMin(m_gainResult[0], m_gainResult[1]);
const float temp2 = (m_gainResult[0] + m_gainResult[1]) * 0.5f;
m_gainResult[0] = linearInterpolate(temp1, temp2, blend - 1);
m_gainResult[1] = m_gainResult[0];
}
else// Blend to maximum volume
{
const float temp1 = (m_gainResult[0] + m_gainResult[1]) * 0.5f;
const float temp2 = qMax(m_gainResult[0], m_gainResult[1]);
m_gainResult[0] = linearInterpolate(temp1, temp2, blend - 2);
m_gainResult[1] = m_gainResult[0];
}
}
break;
}
}

// Bias compression to the left or right (or mid or side)
if (stereoBalance != 0)
{
m_gainResult[0] = 1 - ((1 - m_gainResult[0]) * (stereoBalance > 0 ? 1 - stereoBalance : 1));
m_gainResult[1] = 1 - ((1 - m_gainResult[1]) * (stereoBalance < 0 ? 1 + stereoBalance : 1));
}

// For visualizer
m_displayGain[0] = qMax(m_gainResult[0], m_displayGain[0]);
m_displayGain[1] = qMax(m_gainResult[1], m_displayGain[1]);

// Delay the signal by 20 ms via ring buffer if lookahead is enabled
if (lookahead)
{
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
{
s[0] = drySignal[0];
s[1] = drySignal[1];
}

auto delayedDrySignal = std::array{s[0], s[1]};

if (midside)// Convert left/right to mid/side
{
const float temp = s[0];
s[0] = (temp + s[1]) * 0.5;
s[1] = temp - s[1];
}

s[0] *= inBalance > 0 ? 1 - inBalance : 1;
s[1] *= inBalance < 0 ? 1 + inBalance : 1;

s[0] *= m_gainResult[0] * m_inGainVal * m_outGainVal * (outBalance > 0 ? 1 - outBalance : 1);
s[1] *= m_gainResult[1] * m_inGainVal * m_outGainVal * (outBalance < 0 ? 1 + outBalance : 1);

if (midside)// Convert mid/side back to left/right
{
const float temp1 = s[0];
const float temp2 = s[1] * 0.5;
s[0] = temp1 + temp2;
s[1] = temp1 - temp2;
}

m_prevOut[0] = s[0];
m_prevOut[1] = s[1];

// Negate wet signal from dry signal
if (audition)
{
s[0] = (-s[0] + delayedDrySignal[0] * m_outGainVal * m_inGainVal);
s[1] = (-s[1] + delayedDrySignal[1] * m_outGainVal * m_inGainVal);
}
else if (autoMakeup)
{
s[0] *= m_autoMakeupVal;
s[1] *= m_autoMakeupVal;
}

// Calculate wet/dry value results
const float temp1 = delayedDrySignal[0];
const float temp2 = delayedDrySignal[1];
buf[f][0] = d * temp1 + w * s[0];
buf[f][1] = d * temp2 + w * s[1];
buf[f][0] = (1 - m_mixVal) * temp1 + m_mixVal * buf[f][0];
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;
lOutPeak = s[0] > lOutPeak ? s[0] : lOutPeak;
rOutPeak = s[1] > rOutPeak ? s[1] : rOutPeak;
}

checkGate(outSum / frames);
m_compressorControls.m_outPeakL = lOutPeak;
m_compressorControls.m_outPeakR = rOutPeak;
m_compressorControls.m_inPeakL = lInPeak;
m_compressorControls.m_inPeakR = rInPeak;

return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}

void CompressorEffect::processBypassedImpl()
{
// Clear lookahead buffers and other values when needed

Check notice on line 516 in plugins/Compressor/Compressor.cpp

View check run for this annotation

codefactor.io / CodeFactor

plugins/Compressor/Compressor.cpp#L236-L516

Complex Method
if (!m_cleanedBuffers)
{
m_yL[0] = m_yL[1] = COMP_NOISE_FLOOR;
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_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;
}
}

// Regular modulo doesn't handle negative numbers correctly. This does.
inline int CompressorEffect::realmod(int k, int n)
Expand Down
4 changes: 3 additions & 1 deletion plugins/Compressor/Compressor.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ class CompressorEffect : public Effect
public:
CompressorEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key);
~CompressorEffect() override = default;
bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override;

ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
void processBypassedImpl() override;

EffectControls* controls() override
{
Expand Down
16 changes: 4 additions & 12 deletions plugins/CrossoverEQ/CrossoverEQ.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -89,123 +89,115 @@
}


bool CrossoverEQEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
Effect::ProcessStatus CrossoverEQEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
return( false );
}

// filters update
if( m_needsUpdate || m_controls.m_xover12.isValueChanged() )
{
m_lp1.setLowpass( m_controls.m_xover12.value() );
m_hp2.setHighpass( m_controls.m_xover12.value() );
}
if( m_needsUpdate || m_controls.m_xover23.isValueChanged() )
{
m_lp2.setLowpass( m_controls.m_xover23.value() );
m_hp3.setHighpass( m_controls.m_xover23.value() );
}
if( m_needsUpdate || m_controls.m_xover34.isValueChanged() )
{
m_lp3.setLowpass( m_controls.m_xover34.value() );
m_hp4.setHighpass( m_controls.m_xover34.value() );
}

// gain values update
if( m_needsUpdate || m_controls.m_gain1.isValueChanged() )
{
m_gain1 = dbfsToAmp( m_controls.m_gain1.value() );
}
if( m_needsUpdate || m_controls.m_gain2.isValueChanged() )
{
m_gain2 = dbfsToAmp( m_controls.m_gain2.value() );
}
if( m_needsUpdate || m_controls.m_gain3.isValueChanged() )
{
m_gain3 = dbfsToAmp( m_controls.m_gain3.value() );
}
if( m_needsUpdate || m_controls.m_gain4.isValueChanged() )
{
m_gain4 = dbfsToAmp( m_controls.m_gain4.value() );
}

// mute values update
const bool mute1 = m_controls.m_mute1.value();
const bool mute2 = m_controls.m_mute2.value();
const bool mute3 = m_controls.m_mute3.value();
const bool mute4 = m_controls.m_mute4.value();

m_needsUpdate = false;

zeroSampleFrames(m_work, frames);

// run temp bands
for (auto f = std::size_t{0}; f < frames; ++f)
{
m_tmp1[f][0] = m_lp2.update( buf[f][0], 0 );
m_tmp1[f][1] = m_lp2.update( buf[f][1], 1 );
m_tmp2[f][0] = m_hp3.update( buf[f][0], 0 );
m_tmp2[f][1] = m_hp3.update( buf[f][1], 1 );
}

// run band 1
if( mute1 )
{
for (auto f = std::size_t{0}; f < frames; ++f)
{
m_work[f][0] += m_lp1.update( m_tmp1[f][0], 0 ) * m_gain1;
m_work[f][1] += m_lp1.update( m_tmp1[f][1], 1 ) * m_gain1;
}
}

// run band 2
if( mute2 )
{
for (auto f = std::size_t{0}; f < frames; ++f)
{
m_work[f][0] += m_hp2.update( m_tmp1[f][0], 0 ) * m_gain2;
m_work[f][1] += m_hp2.update( m_tmp1[f][1], 1 ) * m_gain2;
}
}

// run band 3
if( mute3 )
{
for (auto f = std::size_t{0}; f < frames; ++f)
{
m_work[f][0] += m_lp3.update( m_tmp2[f][0], 0 ) * m_gain3;
m_work[f][1] += m_lp3.update( m_tmp2[f][1], 1 ) * m_gain3;
}
}

// run band 4
if( mute4 )
{
for (auto f = std::size_t{0}; f < frames; ++f)
{
m_work[f][0] += m_hp4.update( m_tmp2[f][0], 0 ) * m_gain4;
m_work[f][1] += m_hp4.update( m_tmp2[f][1], 1 ) * m_gain4;
}
}

const float d = dryLevel();
const float w = wetLevel();
double outSum = 0.0;

for (auto f = std::size_t{0}; f < frames; ++f)
{
buf[f][0] = d * buf[f][0] + w * m_work[f][0];
buf[f][1] = d * buf[f][1] + w * m_work[f][1];
outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
}

checkGate( outSum / frames );

return isRunning();

return ProcessStatus::ContinueIfNotQuiet;
}

void CrossoverEQEffect::clearFilterHistories()

Check notice on line 200 in plugins/CrossoverEQ/CrossoverEQ.cpp

View check run for this annotation

codefactor.io / CodeFactor

plugins/CrossoverEQ/CrossoverEQ.cpp#L92-L200

Complex Method
{
m_lp1.clearHistory();
m_lp2.clearHistory();
Expand Down
3 changes: 2 additions & 1 deletion plugins/CrossoverEQ/CrossoverEQ.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ class CrossoverEQEffect : public Effect
public:
CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key );
~CrossoverEQEffect() override;
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;

ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;

EffectControls* controls() override
{
Expand Down
Loading
Loading