Skip to content

Commit

Permalink
Refactor Effect processing (LMMS#7484)
Browse files Browse the repository at this point in the history
* Move common effect processing code to wrapper method

- Introduce `processImpl` and `sleepImpl` methods, and adapt each effect
plugin to use them
- Use double for RMS out sum in Compressor and LOMM
- Run `checkGate` for GranularPitchShifterEffect
- Minor changes to LadspaEffect
- Remove dynamic allocations and VLAs from VstEffect's process method
- Some minor style/formatting fixes

* Fix VstEffect regression

* GranularPitchShifterEffect should not call `checkGate`

* Apply suggestions from code review

Co-authored-by: saker <[email protected]>

* Follow naming convention for local variables

* Add `MAXIMUM_BUFFER_SIZE` and use it in VstEffect

* Revert "GranularPitchShifterEffect should not call `checkGate`"

This reverts commit 67526f0.

* VstEffect: Simplify setting "Don't Run" state

* Rename `sleepImpl` to `processBypassedImpl`

* Use `MAXIMUM_BUFFER_SIZE` in SetupDialog

* Pass `outSum` as out parameter; Fix LadspaEffect mutex

* Move outSum calculations to wrapper method

* Fix Linux build

* Oops

* Apply suggestions from code review

Co-authored-by: Johannes Lorenz <[email protected]>

* Apply suggestions from code review

Co-authored-by: saker <[email protected]>

---------

Co-authored-by: saker <[email protected]>
Co-authored-by: Johannes Lorenz <[email protected]>
  • Loading branch information
3 people authored Sep 21, 2024
1 parent 1d7ed16 commit 1825208
Show file tree
Hide file tree
Showing 53 changed files with 289 additions and 406 deletions.
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;

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:
/**
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,13 +100,8 @@ inline float BitcrushEffect::noise( float amt )
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() )
{
Expand Down Expand Up @@ -222,7 +217,6 @@ bool BitcrushEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )

// 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)
Expand All @@ -236,12 +230,9 @@ bool BitcrushEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
}
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;
}


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,31 +233,10 @@ void CompressorEffect::calcMix()



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();

Expand Down Expand Up @@ -516,8 +495,6 @@ bool CompressorEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
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;
Expand All @@ -526,15 +503,30 @@ bool CompressorEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
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
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,13 +89,8 @@ void CrossoverEQEffect::sampleRateChanged()
}


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() )
{
Expand Down Expand Up @@ -192,17 +187,14 @@ bool CrossoverEQEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames

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()
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

0 comments on commit 1825208

Please sign in to comment.