Refactor Effect processing (#7484)

* 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 <sakertooth@gmail.com>

* Follow naming convention for local variables

* Add `MAXIMUM_BUFFER_SIZE` and use it in VstEffect

* Revert "GranularPitchShifterEffect should not call `checkGate`"

This reverts commit 67526f0ffe.

* 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 <1042576+JohannesLorenz@users.noreply.github.com>

* Apply suggestions from code review

Co-authored-by: saker <sakertooth@gmail.com>

---------

Co-authored-by: saker <sakertooth@gmail.com>
Co-authored-by: Johannes Lorenz <1042576+JohannesLorenz@users.noreply.github.com>
This commit is contained in:
Dalton Messmer
2024-09-20 20:00:36 -04:00
committed by GitHub
parent 1d7ed16dc9
commit 18252088ba
53 changed files with 290 additions and 407 deletions

View File

@@ -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
{

View File

@@ -98,6 +98,7 @@ public:
m_originalPluginData( originalPluginData )
{
setName();
setDontRun(true);
}
~DummyEffect() override = default;
@@ -107,9 +108,9 @@ public:
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

View File

@@ -63,9 +63,8 @@ public:
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
{
@@ -174,14 +173,29 @@ public:
protected:
/**
Effects should call this at the end of audio processing
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
};
/**
* 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;
@@ -212,6 +226,14 @@ protected:
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,

View File

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

View File

@@ -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
{

View File

@@ -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() )
{
@@ -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();
@@ -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;
}

View File

@@ -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
{

View File

@@ -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() )
{
@@ -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)
@@ -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;
}

View File

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

View File

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

View File

@@ -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
{

View File

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

View File

@@ -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
{

View File

@@ -81,13 +81,8 @@ DelayEffect::~DelayEffect()
bool DelayEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
Effect::ProcessStatus DelayEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
return( false );
}
double outSum = 0.0;
const float sr = Engine::audioEngine()->outputSampleRate();
const float d = dryLevel();
const float w = wetLevel();
@@ -135,19 +130,17 @@ bool DelayEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
// Dry/wet mix
currentFrame = dryS * d + currentFrame * w;
outSum += currentFrame.sumOfSquaredAmplitudes();
lengthPtr += lengthInc;
amplitudePtr += amplitudeInc;
lfoTimePtr += lfoTimeInc;
feedbackPtr += feedbackInc;
}
checkGate( outSum / frames );
m_delayControls.m_outPeakL = peak.left();
m_delayControls.m_outPeakR = peak.right();
return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}
void DelayEffect::changeSampleRate()

View File

@@ -39,7 +39,9 @@ class DelayEffect : public Effect
public:
DelayEffect(Model* parent , const Descriptor::SubPluginFeatures::Key* key );
~DelayEffect() override;
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{
return &m_delayControls;

View File

@@ -58,14 +58,8 @@ DispersionEffect::DispersionEffect(Model* parent, const Descriptor::SubPluginFea
}
bool DispersionEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
Effect::ProcessStatus DispersionEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if (!isEnabled() || !isRunning())
{
return false;
}
double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();
@@ -122,11 +116,9 @@ bool DispersionEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
buf[f][0] = d * buf[f][0] + w * s[0];
buf[f][1] = d * buf[f][1] + w * s[1];
outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
}
checkGate(outSum / frames);
return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}

View File

@@ -41,7 +41,8 @@ class DispersionEffect : public Effect
public:
DispersionEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key);
~DispersionEffect() override = default;
bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{

View File

@@ -77,23 +77,17 @@ DualFilterEffect::~DualFilterEffect()
bool DualFilterEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
Effect::ProcessStatus DualFilterEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
return( false );
}
double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();
if( m_dfControls.m_filter1Model.isValueChanged() || m_filter1changed )
if (m_dfControls.m_filter1Model.isValueChanged() || m_filter1changed)
{
m_filter1->setFilterType( static_cast<BasicFilters<2>::FilterType>(m_dfControls.m_filter1Model.value()) );
m_filter1changed = true;
}
if( m_dfControls.m_filter2Model.isValueChanged() || m_filter2changed )
if (m_dfControls.m_filter2Model.isValueChanged() || m_filter2changed)
{
m_filter2->setFilterType( static_cast<BasicFilters<2>::FilterType>(m_dfControls.m_filter2Model.value()) );
m_filter2changed = true;
@@ -201,7 +195,6 @@ bool DualFilterEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames
// do another mix with dry signal
buf[f][0] = d * buf[f][0] + w * s[0];
buf[f][1] = d * buf[f][1] + w * s[1];
outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
//increment pointers
cut1Ptr += cut1Inc;
@@ -213,9 +206,7 @@ bool DualFilterEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames
mixPtr += mixInc;
}
checkGate( outSum / frames );
return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}
void DualFilterEffect::onEnabledChanged()

View File

@@ -40,7 +40,8 @@ class DualFilterEffect : public Effect
public:
DualFilterEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key );
~DualFilterEffect() override;
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{

View File

@@ -91,15 +91,8 @@ inline void DynProcEffect::calcRelease()
}
bool DynProcEffect::processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames )
Effect::ProcessStatus DynProcEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
//apparently we can't keep running after the decay value runs out so we'll just set the peaks to zero
m_currentPeak[0] = m_currentPeak[1] = DYN_NOISE_FLOOR;
return( false );
}
//qDebug( "%f %f", m_currentPeak[0], m_currentPeak[1] );
// variables for effect
@@ -107,7 +100,6 @@ bool DynProcEffect::processAudioBuffer( SampleFrame* _buf,
auto sm_peak = std::array{0.0f, 0.0f};
double out_sum = 0.0;
const float d = dryLevel();
const float w = wetLevel();
@@ -140,9 +132,9 @@ bool DynProcEffect::processAudioBuffer( SampleFrame* _buf,
}
}
for( fpp_t f = 0; f < _frames; ++f )
for (fpp_t f = 0; f < frames; ++f)
{
auto s = std::array{_buf[f][0], _buf[f][1]};
auto s = std::array{buf[f][0], buf[f][1]};
// apply input gain
s[0] *= inputGain;
@@ -210,17 +202,18 @@ bool DynProcEffect::processAudioBuffer( SampleFrame* _buf,
s[1] *= outputGain;
// mix wet/dry signals
_buf[f][0] = d * _buf[f][0] + w * s[0];
_buf[f][1] = d * _buf[f][1] + w * s[1];
out_sum += _buf[f][0] * _buf[f][0] + _buf[f][1] * _buf[f][1];
buf[f][0] = d * buf[f][0] + w * s[0];
buf[f][1] = d * buf[f][1] + w * s[1];
}
checkGate( out_sum / _frames );
return( isRunning() );
return ProcessStatus::ContinueIfNotQuiet;
}
void DynProcEffect::processBypassedImpl()
{
// Apparently we can't keep running after the decay value runs out so we'll just set the peaks to zero
m_currentPeak[0] = m_currentPeak[1] = DYN_NOISE_FLOOR;
}

View File

@@ -42,8 +42,9 @@ public:
DynProcEffect( Model * _parent,
const Descriptor::SubPluginFeatures::Key * _key );
~DynProcEffect() override;
bool processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
void processBypassedImpl() override;
EffectControls * controls() override
{

View File

@@ -64,7 +64,7 @@ EqEffect::EqEffect( Model *parent, const Plugin::Descriptor::SubPluginFeatures::
bool EqEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
Effect::ProcessStatus EqEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
const int sampleRate = Engine::audioEngine()->outputSampleRate();
@@ -131,13 +131,6 @@ bool EqEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
m_lp481.setParameters( sampleRate, lpFreq, lpRes, 1 );
if( !isEnabled() || !isRunning () )
{
return( false );
}
if( m_eqControls.m_outGainModel.isValueChanged() )
{
m_outGain = dbfsToAmp(m_eqControls.m_outGainModel.value());
@@ -151,9 +144,9 @@ bool EqEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
m_eqControls.m_inProgress = true;
double outSum = 0.0;
for( fpp_t f = 0; f < frames; ++f )
for (fpp_t f = 0; f < frames; ++f)
{
outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1];
outSum += buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1];
}
const float outGain = m_outGain;
@@ -268,8 +261,6 @@ bool EqEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
m_eqControls.m_outPeakL = m_eqControls.m_outPeakL < outPeak[0] ? outPeak[0] : m_eqControls.m_outPeakL;
m_eqControls.m_outPeakR = m_eqControls.m_outPeakR < outPeak[1] ? outPeak[1] : m_eqControls.m_outPeakR;
checkGate( outSum / frames );
if(m_eqControls.m_analyseOutModel.value( true ) && outSum > 0 && m_eqControls.isViewVisible() )
{
m_eqControls.m_outFftBands.analyze( buf, frames );
@@ -281,7 +272,8 @@ bool EqEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
}
m_eqControls.m_inProgress = false;
return isRunning();
return Effect::ProcessStatus::ContinueIfNotQuiet;
}

View File

@@ -40,7 +40,9 @@ class EqEffect : public Effect
public:
EqEffect( Model * parent , const Descriptor::SubPluginFeatures::Key * key );
~EqEffect() override = default;
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls * controls() override
{
return &m_eqControls;

View File

@@ -85,13 +85,8 @@ FlangerEffect::~FlangerEffect()
bool FlangerEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
Effect::ProcessStatus FlangerEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
return( false );
}
double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();
const float length = m_flangerControls.m_delayTimeModel.value() * Engine::audioEngine()->outputSampleRate();
@@ -127,10 +122,9 @@ bool FlangerEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
buf[f][0] = ( d * dryS[0] ) + ( w * buf[f][0] );
buf[f][1] = ( d * dryS[1] ) + ( w * buf[f][1] );
outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1];
}
checkGate( outSum / frames );
return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}

View File

@@ -41,7 +41,9 @@ class FlangerEffect : public Effect
public:
FlangerEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key );
~FlangerEffect() override;
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{
return &m_flangerControls;

View File

@@ -60,10 +60,8 @@ GranularPitchShifterEffect::GranularPitchShifterEffect(Model* parent, const Desc
}
bool GranularPitchShifterEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
Effect::ProcessStatus GranularPitchShifterEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if (!isEnabled() || !isRunning()) { return false; }
const float d = dryLevel();
const float w = wetLevel();
@@ -245,7 +243,7 @@ bool GranularPitchShifterEffect::processAudioBuffer(SampleFrame* buf, const fpp_
changeSampleRate();
}
return isRunning();
return Effect::ProcessStatus::ContinueIfNotQuiet;
}
void GranularPitchShifterEffect::changeSampleRate()

View File

@@ -48,7 +48,8 @@ class GranularPitchShifterEffect : public Effect
public:
GranularPitchShifterEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key);
~GranularPitchShifterEffect() override = default;
bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{

View File

@@ -101,13 +101,8 @@ void LOMMEffect::changeSampleRate()
}
bool LOMMEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
Effect::ProcessStatus LOMMEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if (!isEnabled() || !isRunning())
{
return false;
}
if (m_needsUpdate || m_lommControls.m_split1Model.isValueChanged())
{
m_lp1.setLowpass(m_lommControls.m_split1Model.value());
@@ -121,7 +116,6 @@ bool LOMMEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
}
m_needsUpdate = false;
float outSum = 0.f;
const float d = dryLevel();
const float w = wetLevel();
@@ -423,11 +417,9 @@ bool LOMMEffect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
buf[f][0] = d * buf[f][0] + w * s[0];
buf[f][1] = d * buf[f][1] + w * s[1];
outSum += buf[f][0] + buf[f][1];
}
checkGate(outSum / frames);
return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}
extern "C"

View File

@@ -45,7 +45,8 @@ class LOMMEffect : public Effect
public:
LOMMEffect(Model* parent, const Descriptor::SubPluginFeatures::Key* key);
~LOMMEffect() override = default;
bool processAudioBuffer(SampleFrame* buf, const fpp_t frames) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{

View File

@@ -129,26 +129,25 @@ void LadspaEffect::changeSampleRate()
bool LadspaEffect::processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames )
Effect::ProcessStatus LadspaEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
m_pluginMutex.lock();
if( !isOkay() || dontRun() || !isRunning() || !isEnabled() )
if (!isOkay() || dontRun() || !isEnabled() || !isRunning())
{
m_pluginMutex.unlock();
return( false );
return ProcessStatus::Sleep;
}
auto frames = _frames;
SampleFrame* o_buf = nullptr;
QVarLengthArray<SampleFrame> sBuf(_frames);
auto outFrames = frames;
SampleFrame* outBuf = nullptr;
QVarLengthArray<SampleFrame> sBuf(frames);
if( m_maxSampleRate < Engine::audioEngine()->outputSampleRate() )
{
o_buf = _buf;
_buf = sBuf.data();
sampleDown( o_buf, _buf, m_maxSampleRate );
frames = _frames * m_maxSampleRate /
outBuf = buf;
buf = sBuf.data();
sampleDown(outBuf, buf, m_maxSampleRate);
outFrames = frames * m_maxSampleRate /
Engine::audioEngine()->outputSampleRate();
}
@@ -163,11 +162,9 @@ bool LadspaEffect::processAudioBuffer( SampleFrame* _buf,
switch( pp->rate )
{
case BufferRate::ChannelIn:
for( fpp_t frame = 0;
frame < frames; ++frame )
for (fpp_t frame = 0; frame < outFrames; ++frame)
{
pp->buffer[frame] =
_buf[frame][channel];
pp->buffer[frame] = buf[frame][channel];
}
++channel;
break;
@@ -176,7 +173,7 @@ bool LadspaEffect::processAudioBuffer( SampleFrame* _buf,
ValueBuffer * vb = pp->control->valueBuffer();
if( vb )
{
memcpy( pp->buffer, vb->values(), frames * sizeof(float) );
memcpy(pp->buffer, vb->values(), outFrames * sizeof(float));
}
else
{
@@ -185,11 +182,9 @@ bool LadspaEffect::processAudioBuffer( SampleFrame* _buf,
// This only supports control rate ports, so the audio rates are
// treated as though they were control rate by setting the
// port buffer to all the same value.
for( fpp_t frame = 0;
frame < frames; ++frame )
for (fpp_t frame = 0; frame < outFrames; ++frame)
{
pp->buffer[frame] =
pp->value;
pp->buffer[frame] = pp->value;
}
}
break;
@@ -218,11 +213,10 @@ bool LadspaEffect::processAudioBuffer( SampleFrame* _buf,
// Process the buffers.
for( ch_cnt_t proc = 0; proc < processorCount(); ++proc )
{
(m_descriptor->run)( m_handles[proc], frames );
(m_descriptor->run)(m_handles[proc], outFrames);
}
// Copy the LADSPA output buffers to the LMMS buffer.
double out_sum = 0.0;
channel = 0;
const float d = dryLevel();
const float w = wetLevel();
@@ -238,11 +232,9 @@ bool LadspaEffect::processAudioBuffer( SampleFrame* _buf,
case BufferRate::ControlRateInput:
break;
case BufferRate::ChannelOut:
for( fpp_t frame = 0;
frame < frames; ++frame )
for (fpp_t frame = 0; frame < outFrames; ++frame)
{
_buf[frame][channel] = d * _buf[frame][channel] + w * pp->buffer[frame];
out_sum += _buf[frame][channel] * _buf[frame][channel];
buf[frame][channel] = d * buf[frame][channel] + w * pp->buffer[frame];
}
++channel;
break;
@@ -255,17 +247,14 @@ bool LadspaEffect::processAudioBuffer( SampleFrame* _buf,
}
}
if( o_buf != nullptr )
if (outBuf != nullptr)
{
sampleBack( _buf, o_buf, m_maxSampleRate );
sampleBack(buf, outBuf, m_maxSampleRate);
}
checkGate( out_sum / frames );
bool is_running = isRunning();
m_pluginMutex.unlock();
return( is_running );
return ProcessStatus::ContinueIfNotQuiet;
}

View File

@@ -47,9 +47,8 @@ public:
const Descriptor::SubPluginFeatures::Key * _key );
~LadspaEffect() override;
bool processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
void setControl( int _control, LADSPA_Data _data );
EffectControls * controls() override

View File

@@ -68,9 +68,8 @@ Lv2Effect::Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key *ke
bool Lv2Effect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
Effect::ProcessStatus Lv2Effect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if (!isEnabled() || !isRunning()) { return false; }
Q_ASSERT(frames <= static_cast<fpp_t>(m_tmpOutputSmps.size()));
m_controls.copyBuffersFromLmms(buf, frames);
@@ -83,7 +82,6 @@ bool Lv2Effect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
m_controls.copyModelsToLmms();
m_controls.copyBuffersToLmms(m_tmpOutputSmps.data(), frames);
double outSum = .0;
bool corrupt = wetLevel() < 0; // #3261 - if w < 0, bash w := 0, d := 1
const float d = corrupt ? 1 : dryLevel();
const float w = corrupt ? 0 : wetLevel();
@@ -91,13 +89,9 @@ bool Lv2Effect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
{
buf[f][0] = d * buf[f][0] + w * m_tmpOutputSmps[f][0];
buf[f][1] = d * buf[f][1] + w * m_tmpOutputSmps[f][1];
auto l = static_cast<double>(buf[f][0]);
auto r = static_cast<double>(buf[f][1]);
outSum += l*l + r*r;
}
checkGate(outSum / frames);
return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}

View File

@@ -42,7 +42,8 @@ public:
*/
Lv2Effect(Model* parent, const Descriptor::SubPluginFeatures::Key* _key);
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override { return &m_controls; }
Lv2FxControls* lv2Controls() { return &m_controls; }

View File

@@ -94,14 +94,8 @@ void MultitapEchoEffect::runFilter( SampleFrame* dst, SampleFrame* src, StereoOn
}
bool MultitapEchoEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
Effect::ProcessStatus MultitapEchoEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
return( false );
}
double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();
@@ -156,12 +150,9 @@ bool MultitapEchoEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frame
{
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;
}

View File

@@ -40,7 +40,8 @@ class MultitapEchoEffect : public Effect
public:
MultitapEchoEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key );
~MultitapEchoEffect() override;
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{

View File

@@ -93,37 +93,29 @@ PeakControllerEffect::~PeakControllerEffect()
}
bool PeakControllerEffect::processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames )
Effect::ProcessStatus PeakControllerEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
PeakControllerEffectControls & c = m_peakControls;
// This appears to be used for determining whether or not to continue processing
// audio with this effect
if( !isEnabled() || !isRunning() )
{
return false;
}
// RMS:
double sum = 0;
if( c.m_absModel.value() )
{
for (auto i = std::size_t{0}; i < _frames; ++i)
for (auto i = std::size_t{0}; i < frames; ++i)
{
// absolute value is achieved because the squares are > 0
sum += _buf[i][0]*_buf[i][0] + _buf[i][1]*_buf[i][1];
sum += buf[i][0] * buf[i][0] + buf[i][1] * buf[i][1];
}
}
else
{
for (auto i = std::size_t{0}; i < _frames; ++i)
for (auto i = std::size_t{0}; i < frames; ++i)
{
// the value is absolute because of squaring,
// so we need to correct it
sum += _buf[i][0] * _buf[i][0] * sign( _buf[i][0] )
+ _buf[i][1] * _buf[i][1] * sign( _buf[i][1] );
sum += buf[i][0] * buf[i][0] * sign(buf[i][0])
+ buf[i][1] * buf[i][1] * sign(buf[i][1]);
}
}
@@ -131,19 +123,19 @@ bool PeakControllerEffect::processAudioBuffer( SampleFrame* _buf,
// this will mute the output after the values were measured
if( c.m_muteModel.value() )
{
for (auto i = std::size_t{0}; i < _frames; ++i)
for (auto i = std::size_t{0}; i < frames; ++i)
{
_buf[i][0] = _buf[i][1] = 0.0f;
buf[i][0] = buf[i][1] = 0.0f;
}
}
float curRMS = sqrt_neg( sum / _frames );
float curRMS = sqrt_neg(sum / frames);
const float tres = c.m_tresholdModel.value();
const float amount = c.m_amountModel.value() * c.m_amountMultModel.value();
curRMS = qAbs( curRMS ) < tres ? 0.0f : curRMS;
m_lastSample = qBound( 0.0f, c.m_baseModel.value() + amount * curRMS, 1.0f );
return isRunning();
return ProcessStatus::Continue;
}

View File

@@ -41,8 +41,8 @@ public:
PeakControllerEffect( Model * parent,
const Descriptor::SubPluginFeatures::Key * _key );
~PeakControllerEffect() override;
bool processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls * controls() override
{

View File

@@ -75,14 +75,8 @@ ReverbSCEffect::~ReverbSCEffect()
sp_destroy(&sp);
}
bool ReverbSCEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
Effect::ProcessStatus ReverbSCEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
return( false );
}
double outSum = 0.0;
const float d = dryLevel();
const float w = wetLevel();
@@ -119,14 +113,9 @@ bool ReverbSCEffect::processAudioBuffer( SampleFrame* buf, const fpp_t frames )
sp_dcblock_compute(sp, dcblk[1], &tmpR, &dcblkR);
buf[f][0] = d * buf[f][0] + w * dcblkL * outGain;
buf[f][1] = d * buf[f][1] + w * dcblkR * outGain;
outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1];
}
checkGate( outSum / frames );
return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}
void ReverbSCEffect::changeSampleRate()

View File

@@ -45,7 +45,8 @@ class ReverbSCEffect : public Effect
public:
ReverbSCEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key );
~ReverbSCEffect() override;
bool processAudioBuffer( SampleFrame* buf, const fpp_t frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{

View File

@@ -77,7 +77,7 @@ Analyzer::~Analyzer()
}
// Take audio data and pass them to the spectrum processor.
bool Analyzer::processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count)
Effect::ProcessStatus Analyzer::processImpl(SampleFrame* buf, const fpp_t frames)
{
// Measure time spent in audio thread; both average and peak should be well under 1 ms.
#ifdef SA_DEBUG
@@ -91,14 +91,12 @@ bool Analyzer::processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count)
}
#endif
if (!isEnabled() || !isRunning ()) {return false;}
// Skip processing if the controls dialog isn't visible, it would only waste CPU cycles.
if (m_controls.isViewVisible())
{
// To avoid processing spikes on audio thread, data are stored in
// a lockless ringbuffer and processed in a separate thread.
m_inputBuffer.write(buffer, frame_count, true);
m_inputBuffer.write(buf, frames, true);
}
#ifdef SA_DEBUG
audio_time = std::chrono::high_resolution_clock::now().time_since_epoch().count() - audio_time;
@@ -107,7 +105,7 @@ bool Analyzer::processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count)
if (audio_time / 1000000.0 > m_max_execution) {m_max_execution = audio_time / 1000000.0;}
#endif
return isRunning();
return ProcessStatus::Continue;
}

View File

@@ -45,7 +45,8 @@ public:
Analyzer(Model *parent, const Descriptor::SubPluginFeatures::Key *key);
~Analyzer() override;
bool processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls *controls() override {return &m_controls;}
SaProcessor *getProcessor() {return &m_processor;}

View File

@@ -82,28 +82,17 @@ StereoEnhancerEffect::~StereoEnhancerEffect()
bool StereoEnhancerEffect::processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames )
Effect::ProcessStatus StereoEnhancerEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
// This appears to be used for determining whether or not to continue processing
// audio with this effect
double out_sum = 0.0;
if( !isEnabled() || !isRunning() )
{
return( false );
}
const float d = dryLevel();
const float w = wetLevel();
for( fpp_t f = 0; f < _frames; ++f )
for (fpp_t f = 0; f < frames; ++f)
{
// copy samples into the delay buffer
m_delayBuffer[m_currFrame][0] = _buf[f][0];
m_delayBuffer[m_currFrame][1] = _buf[f][1];
m_delayBuffer[m_currFrame][0] = buf[f][0];
m_delayBuffer[m_currFrame][1] = buf[f][1];
// Get the width knob value from the Stereo Enhancer effect
float width = m_seFX.wideCoeff();
@@ -117,27 +106,25 @@ bool StereoEnhancerEffect::processAudioBuffer( SampleFrame* _buf,
frameIndex += DEFAULT_BUFFER_SIZE;
}
//sample_t s[2] = { _buf[f][0], _buf[f][1] }; //Vanilla
auto s = std::array{_buf[f][0], m_delayBuffer[frameIndex][1]}; //Chocolate
//sample_t s[2] = { buf[f][0], buf[f][1] }; //Vanilla
auto s = std::array{buf[f][0], m_delayBuffer[frameIndex][1]}; //Chocolate
m_seFX.nextSample( s[0], s[1] );
_buf[f][0] = d * _buf[f][0] + w * s[0];
_buf[f][1] = d * _buf[f][1] + w * s[1];
out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1];
buf[f][0] = d * buf[f][0] + w * s[0];
buf[f][1] = d * buf[f][1] + w * s[1];
// Update currFrame
m_currFrame += 1;
m_currFrame %= DEFAULT_BUFFER_SIZE;
}
checkGate( out_sum / _frames );
if( !isRunning() )
{
clearMyBuffer();
}
return( isRunning() );
return ProcessStatus::ContinueIfNotQuiet;
}

View File

@@ -40,8 +40,8 @@ public:
StereoEnhancerEffect( Model * parent,
const Descriptor::SubPluginFeatures::Key * _key );
~StereoEnhancerEffect() override;
bool processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls * controls() override
{

View File

@@ -64,44 +64,29 @@ StereoMatrixEffect::StereoMatrixEffect(
bool StereoMatrixEffect::processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames )
Effect::ProcessStatus StereoMatrixEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
// This appears to be used for determining whether or not to continue processing
// audio with this effect
if( !isEnabled() || !isRunning() )
{
return( false );
}
double out_sum = 0.0;
for( fpp_t f = 0; f < _frames; ++f )
for (fpp_t f = 0; f < frames; ++f)
{
const float d = dryLevel();
const float w = wetLevel();
sample_t l = _buf[f][0];
sample_t r = _buf[f][1];
sample_t l = buf[f][0];
sample_t r = buf[f][1];
// Init with dry-mix
_buf[f][0] = l * d;
_buf[f][1] = r * d;
buf[f][0] = l * d;
buf[f][1] = r * d;
// Add it wet
_buf[f][0] += ( m_smControls.m_llModel.value( f ) * l +
buf[f][0] += ( m_smControls.m_llModel.value( f ) * l +
m_smControls.m_rlModel.value( f ) * r ) * w;
_buf[f][1] += ( m_smControls.m_lrModel.value( f ) * l +
buf[f][1] += ( m_smControls.m_lrModel.value( f ) * l +
m_smControls.m_rrModel.value( f ) * r ) * w;
out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1];
}
checkGate( out_sum / _frames );
return( isRunning() );
return ProcessStatus::ContinueIfNotQuiet;
}

View File

@@ -39,8 +39,8 @@ public:
StereoMatrixEffect( Model * parent,
const Descriptor::SubPluginFeatures::Key * _key );
~StereoMatrixEffect() override = default;
bool processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls* controls() override
{

View File

@@ -58,18 +58,17 @@ Vectorscope::Vectorscope(Model *parent, const Plugin::Descriptor::SubPluginFeatu
// Take audio data and store them for processing and display in the GUI thread.
bool Vectorscope::processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count)
Effect::ProcessStatus Vectorscope::processImpl(SampleFrame* buf, const fpp_t frames)
{
if (!isEnabled() || !isRunning ()) {return false;}
// Skip processing if the controls dialog isn't visible, it would only waste CPU cycles.
if (m_controls.isViewVisible())
{
// To avoid processing spikes on audio thread, data are stored in
// a lockless ringbuffer and processed in a separate thread.
m_inputBuffer.write(buffer, frame_count);
m_inputBuffer.write(buf, frames);
}
return isRunning();
return ProcessStatus::Continue;
}

View File

@@ -39,7 +39,8 @@ public:
Vectorscope(Model *parent, const Descriptor::SubPluginFeatures::Key *key);
~Vectorscope() override = default;
bool processAudioBuffer(SampleFrame* buffer, const fpp_t frame_count) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls *controls() override {return &m_controls;}
LocklessRingBuffer<SampleFrame> *getBuffer() {return &m_inputBuffer;}

View File

@@ -65,63 +65,47 @@ VstEffect::VstEffect( Model * _parent,
m_key( *_key ),
m_vstControls( this )
{
bool loaded = false;
if( !m_key.attributes["file"].isEmpty() )
{
openPlugin( m_key.attributes["file"] );
loaded = openPlugin(m_key.attributes["file"]);
}
setDisplayName( m_key.attributes["file"].section( ".dll", 0, 0 ).isEmpty()
? m_key.name : m_key.attributes["file"].section( ".dll", 0, 0 ) );
setDontRun(!loaded);
}
bool VstEffect::processAudioBuffer( SampleFrame* _buf, const fpp_t _frames )
Effect::ProcessStatus VstEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
assert(m_plugin != nullptr);
static thread_local auto tempBuf = std::array<SampleFrame, MAXIMUM_BUFFER_SIZE>();
std::memcpy(tempBuf.data(), buf, sizeof(SampleFrame) * frames);
if (m_pluginMutex.tryLock(Engine::getSong()->isExporting() ? -1 : 0))
{
return false;
m_plugin->process(tempBuf.data(), tempBuf.data());
m_pluginMutex.unlock();
}
if( m_plugin )
const float w = wetLevel();
const float d = dryLevel();
for (fpp_t f = 0; f < frames; ++f)
{
const float d = dryLevel();
#ifdef __GNUC__
SampleFrame buf[_frames];
#else
SampleFrame* buf = new SampleFrame[_frames];
#endif
memcpy( buf, _buf, sizeof( SampleFrame ) * _frames );
if (m_pluginMutex.tryLock(Engine::getSong()->isExporting() ? -1 : 0))
{
m_plugin->process( buf, buf );
m_pluginMutex.unlock();
}
double out_sum = 0.0;
const float w = wetLevel();
for( fpp_t f = 0; f < _frames; ++f )
{
_buf[f][0] = w*buf[f][0] + d*_buf[f][0];
_buf[f][1] = w*buf[f][1] + d*_buf[f][1];
}
for( fpp_t f = 0; f < _frames; ++f )
{
out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1];
}
#ifndef __GNUC__
delete[] buf;
#endif
checkGate( out_sum / _frames );
buf[f][0] = w * tempBuf[f][0] + d * buf[f][0];
buf[f][1] = w * tempBuf[f][1] + d * buf[f][1];
}
return isRunning();
return ProcessStatus::ContinueIfNotQuiet;
}
void VstEffect::openPlugin( const QString & _plugin )
bool VstEffect::openPlugin(const QString& plugin)
{
gui::TextFloat* tf = nullptr;
if( gui::getGUI() != nullptr )
@@ -133,18 +117,19 @@ void VstEffect::openPlugin( const QString & _plugin )
}
QMutexLocker ml( &m_pluginMutex ); Q_UNUSED( ml );
m_plugin = QSharedPointer<VstPlugin>(new VstPlugin( _plugin ));
m_plugin = QSharedPointer<VstPlugin>(new VstPlugin(plugin));
if( m_plugin->failed() )
{
m_plugin.clear();
delete tf;
collectErrorForUI( VstPlugin::tr( "The VST plugin %1 could not be loaded." ).arg( _plugin ) );
return;
collectErrorForUI(VstPlugin::tr("The VST plugin %1 could not be loaded.").arg(plugin));
return false;
}
delete tf;
m_key.attributes["file"] = _plugin;
m_key.attributes["file"] = plugin;
return true;
}

View File

@@ -45,8 +45,7 @@ public:
const Descriptor::SubPluginFeatures::Key * _key );
~VstEffect() override = default;
bool processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls * controls() override
{
@@ -55,7 +54,8 @@ public:
private:
void openPlugin( const QString & _plugin );
//! Returns true if plugin was loaded (m_plugin != nullptr)
bool openPlugin(const QString& plugin);
void closePlugin();
QSharedPointer<VstPlugin> m_plugin;

View File

@@ -66,18 +66,11 @@ WaveShaperEffect::WaveShaperEffect( Model * _parent,
bool WaveShaperEffect::processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames )
Effect::ProcessStatus WaveShaperEffect::processImpl(SampleFrame* buf, const fpp_t frames)
{
if( !isEnabled() || !isRunning () )
{
return( false );
}
// variables for effect
int i = 0;
double out_sum = 0.0;
const float d = dryLevel();
const float w = wetLevel();
float input = m_wsControls.m_inputModel.value();
@@ -94,9 +87,9 @@ bool WaveShaperEffect::processAudioBuffer( SampleFrame* _buf,
const float *inputPtr = inputBuffer ? &( inputBuffer->values()[ 0 ] ) : &input;
const float *outputPtr = outputBufer ? &( outputBufer->values()[ 0 ] ) : &output;
for( fpp_t f = 0; f < _frames; ++f )
for (fpp_t f = 0; f < frames; ++f)
{
auto s = std::array{_buf[f][0], _buf[f][1]};
auto s = std::array{buf[f][0], buf[f][1]};
// apply input gain
s[0] *= *inputPtr;
@@ -138,17 +131,14 @@ bool WaveShaperEffect::processAudioBuffer( SampleFrame* _buf,
s[1] *= *outputPtr;
// mix wet/dry signals
_buf[f][0] = d * _buf[f][0] + w * s[0];
_buf[f][1] = d * _buf[f][1] + w * s[1];
out_sum += _buf[f][0] * _buf[f][0] + _buf[f][1] * _buf[f][1];
buf[f][0] = d * buf[f][0] + w * s[0];
buf[f][1] = d * buf[f][1] + w * s[1];
outputPtr += outputInc;
inputPtr += inputInc;
}
checkGate( out_sum / _frames );
return( isRunning() );
return ProcessStatus::ContinueIfNotQuiet;
}

View File

@@ -40,8 +40,8 @@ public:
WaveShaperEffect( Model * _parent,
const Descriptor::SubPluginFeatures::Key * _key );
~WaveShaperEffect() override = default;
bool processAudioBuffer( SampleFrame* _buf,
const fpp_t _frames ) override;
ProcessStatus processImpl(SampleFrame* buf, const fpp_t frames) override;
EffectControls * controls() override
{

View File

@@ -122,6 +122,41 @@ void Effect::loadSettings( const QDomElement & _this )
bool Effect::processAudioBuffer(SampleFrame* buf, const fpp_t frames)
{
if (!isOkay() || dontRun() || !isEnabled() || !isRunning())
{
processBypassedImpl();
return false;
}
const auto status = processImpl(buf, frames);
switch (status)
{
case ProcessStatus::Continue:
break;
case ProcessStatus::ContinueIfNotQuiet:
{
double outSum = 0.0;
for (std::size_t idx = 0; idx < frames; ++idx)
{
outSum += buf[idx].sumOfSquaredAmplitudes();
}
checkGate(outSum / frames);
break;
}
case ProcessStatus::Sleep:
return false;
default:
break;
}
return isRunning();
}
Effect * Effect::instantiate( const QString& pluginName,
Model * _parent,
@@ -146,7 +181,7 @@ Effect * Effect::instantiate( const QString& pluginName,
void Effect::checkGate( double _out_sum )
void Effect::checkGate(double outSum)
{
if( m_autoQuitDisabled )
{
@@ -155,7 +190,7 @@ void Effect::checkGate( double _out_sum )
// Check whether we need to continue processing input. Restart the
// counter if the threshold has been exceeded.
if (_out_sum - gate() <= F_EPSILON)
if (outSum - gate() <= F_EPSILON)
{
incrementBufferCount();
if( bufferCount() > timeout() )

View File

@@ -564,7 +564,7 @@ SetupDialog::SetupDialog(ConfigTab tab_to_open) :
QHBoxLayout * bufferSizeSubLayout = new QHBoxLayout();
m_bufferSizeSlider = new QSlider(Qt::Horizontal, bufferSizeBox);
m_bufferSizeSlider->setRange(1, 128);
m_bufferSizeSlider->setRange(1, MAXIMUM_BUFFER_SIZE / BUFFERSIZE_RESOLUTION);
m_bufferSizeSlider->setTickInterval(8);
m_bufferSizeSlider->setPageStep(8);
m_bufferSizeSlider->setValue(m_bufferSize / BUFFERSIZE_RESOLUTION);