From c51ff31b688b6a2e5103740a1cb7a932391f85c9 Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 16 Nov 2014 20:54:16 +0200 Subject: [PATCH] Dynamics processor improvements --- .../dynamics_processor/dynamics_processor.cpp | 130 +++++++++--------- .../dynamics_processor/dynamics_processor.h | 12 +- .../dynamics_processor_control_dialog.h | 4 +- .../dynamics_processor_controls.cpp | 23 +--- .../dynamics_processor_controls.h | 5 +- 5 files changed, 81 insertions(+), 93 deletions(-) diff --git a/plugins/dynamics_processor/dynamics_processor.cpp b/plugins/dynamics_processor/dynamics_processor.cpp index dcb67de6f..24e421deb 100644 --- a/plugins/dynamics_processor/dynamics_processor.cpp +++ b/plugins/dynamics_processor/dynamics_processor.cpp @@ -49,15 +49,16 @@ Plugin::Descriptor PLUGIN_EXPORT dynamicsprocessor_plugin_descriptor = } - +const float DYN_NOISE_FLOOR = 0.00001f; // -100dBV noise floor +const double DNF_LOG = 5.0; dynProcEffect::dynProcEffect( Model * _parent, const Descriptor::SubPluginFeatures::Key * _key ) : Effect( &dynamicsprocessor_plugin_descriptor, _parent, _key ), m_dpControls( this ) { - currentPeak[0] = 0.0f; - currentPeak[1] = 0.0f; + m_currentPeak[0] = m_currentPeak[1] = DYN_NOISE_FLOOR; + m_needsUpdate = true; } @@ -68,6 +69,15 @@ dynProcEffect::~dynProcEffect() } +inline void dynProcEffect::calcAttack() +{ + m_attCoeff = exp10( ( DNF_LOG / ( m_dpControls.m_attackModel.value() * 0.001 ) ) / engine::mixer()->processingSampleRate() ); +} + +inline void dynProcEffect::calcRelease() +{ + m_relCoeff = exp10( ( -DNF_LOG / ( m_dpControls.m_releaseModel.value() * 0.001 ) ) / engine::mixer()->processingSampleRate() ); +} bool dynProcEffect::processAudioBuffer( sampleFrame * _buf, @@ -76,27 +86,10 @@ bool dynProcEffect::processAudioBuffer( sampleFrame * _buf, if( !isEnabled() || !isRunning () ) { //apparently we can't keep running after the decay value runs out so we'll just set the peaks to zero - currentPeak[0] = 0.0f; - currentPeak[1] = 0.0f; + m_currentPeak[0] = m_currentPeak[1] = DYN_NOISE_FLOOR; return( false ); - -/* if( currentPeak[0] == 0.0f && currentPeak[1] == 0.0f ) return( false ); - else - { - if( currentPeak[0] != 0.0f ) - { - currentPeak[0] = qMax ( currentPeak[0] - - (( 1.0f / ( m_dpControls.m_releaseModel.value() / 1000.0f ) ) / engine::mixer()->processingSampleRate()), 0.0f ); - } - if( currentPeak[1] != 0.0f ) - { - currentPeak[1] = qMax ( currentPeak[1] - - (( 1.0f / ( m_dpControls.m_releaseModel.value() / 1000.0f ) ) / engine::mixer()->processingSampleRate()), 0.0f ); - } - - return( true ); - } */ } + //qDebug( "%f %f", m_currentPeak[0], m_currentPeak[1] ); // variables for effect int i = 0; @@ -107,100 +100,103 @@ bool dynProcEffect::processAudioBuffer( sampleFrame * _buf, double out_sum = 0.0; const float d = dryLevel(); const float w = wetLevel(); + + const int stereoMode = m_dpControls.m_stereomodeModel.value(); + const float inputGain = m_dpControls.m_inputModel.value(); + const float outputGain = m_dpControls.m_outputModel.value(); + + const float * samples = m_dpControls.m_wavegraphModel.samples(); + +// debug code +// qDebug( "peaks %f %f", m_currentPeak[0], m_currentPeak[1] ); + + if( m_dpControls.m_attackModel.isValueChanged() || m_needsUpdate ) + { + calcAttack(); + } + if( m_dpControls.m_releaseModel.isValueChanged() || m_needsUpdate ) + { + calcRelease(); + } + m_needsUpdate = false; -// debug code -// qDebug( "peaks %f %f", currentPeak[0], currentPeak[1] ); - - float att_tmp = ( 1.0f / ( m_dpControls.m_attackModel.value() / 1000.0f ) ) / engine::mixer()->processingSampleRate(); - float rel_tmp = ( 1.0f / ( m_dpControls.m_releaseModel.value() / 1000.0f ) ) / engine::mixer()->processingSampleRate(); - for( fpp_t f = 0; f < _frames; ++f ) { - sample_t s[2] = { _buf[f][0], _buf[f][1] }; + double s[2] = { _buf[f][0], _buf[f][1] }; -// check for nan/inf because they may cause errors? - if( isnanf( s[0] ) ) s[0] = 0.0f; - if( isnanf( s[1] ) ) s[1] = 0.0f; - if( isinff( s[0] ) ) s[0] = 0.0f; - if( isinff( s[1] ) ) s[1] = 0.0f; - +// apply input gain + s[0] *= inputGain; + s[1] *= inputGain; // update peak values for ( i=0; i <= 1; i++ ) { - if( qAbs( s[i] ) > currentPeak[i] ) + if( qAbs( s[i] ) > m_currentPeak[i] ) { - currentPeak[i] = qMin ( currentPeak[i] + att_tmp, qAbs( s[i] ) ); + m_currentPeak[i] = qMin( m_currentPeak[i] * m_attCoeff, qAbs( s[i] ) ); } - else - if( qAbs( s[i] ) < currentPeak[i] ) + else + if( qAbs( s[i] ) < m_currentPeak[i] ) { - currentPeak[i] = qMax ( currentPeak[i] - rel_tmp, qAbs( s[i] ) ); + m_currentPeak[i] = qMax( m_currentPeak[i] * m_relCoeff, qAbs( s[i] ) ); } - - currentPeak[i] = qBound( 0.0f, currentPeak[i], 1.0f ); - + + m_currentPeak[i] = qBound( DYN_NOISE_FLOOR, m_currentPeak[i], 10.0f ); } // account for stereo mode - switch( m_dpControls.m_stereomodeModel.value() ) + switch( stereoMode ) { case dynProcControls::SM_Maximum: { - sm_peak[0] = qMax( currentPeak[0], currentPeak[1] ); - sm_peak[1] = qMax( currentPeak[0], currentPeak[1] ); + sm_peak[0] = sm_peak[1] = qMax( m_currentPeak[0], m_currentPeak[1] ); break; } case dynProcControls::SM_Average: { - sm_peak[0] = ( currentPeak[0] + currentPeak[1] ) / 2; - sm_peak[1] = ( currentPeak[0] + currentPeak[1] ) / 2; + sm_peak[0] = sm_peak[1] = ( m_currentPeak[0] + m_currentPeak[1] ) * 0.5; break; } case dynProcControls::SM_Unlinked: { - sm_peak[0] = currentPeak[0]; - sm_peak[1] = currentPeak[1]; + sm_peak[0] = m_currentPeak[0]; + sm_peak[1] = m_currentPeak[1]; break; } } -// apply input gain - s[0] *= m_dpControls.m_inputModel.value(); - s[1] *= m_dpControls.m_inputModel.value(); - - // start effect for ( i=0; i <= 1; i++ ) { const int lookup = static_cast( sm_peak[i] * 200.0f ); - const float frac = fraction( sm_peak[i] * 200.0f ); - - if( sm_peak[i] > 0 ) - { + const float frac = fraction( sm_peak[i] * 200.0f ); + + if( sm_peak[i] > DYN_NOISE_FLOOR ) + { if ( lookup < 1 ) { - gain = frac * m_dpControls.m_wavegraphModel.samples()[0]; + gain = frac * samples[0]; } else if ( lookup < 200 ) { - gain = linearInterpolate( m_dpControls.m_wavegraphModel.samples()[ lookup - 1 ], - m_dpControls.m_wavegraphModel.samples()[ lookup ], frac ); + gain = linearInterpolate( samples[ lookup - 1 ], + samples[ lookup ], frac ); } else { - gain = m_dpControls.m_wavegraphModel.samples()[199]; + gain = samples[199]; }; - - s[i] *= ( gain / sm_peak[i] ); + + s[i] *= gain; + s[i] /= sm_peak[i]; } } // apply output gain - s[0] *= m_dpControls.m_outputModel.value(); - s[1] *= m_dpControls.m_outputModel.value(); + s[0] *= outputGain; + s[1] *= outputGain; // mix wet/dry signals _buf[f][0] = d * _buf[f][0] + w * s[0]; diff --git a/plugins/dynamics_processor/dynamics_processor.h b/plugins/dynamics_processor/dynamics_processor.h index 77e598fe1..004146eb2 100644 --- a/plugins/dynamics_processor/dynamics_processor.h +++ b/plugins/dynamics_processor/dynamics_processor.h @@ -24,8 +24,8 @@ */ -#ifndef _DYNPROC_H -#define _DYNPROC_H +#ifndef DYNPROC_H +#define DYNPROC_H #include "Effect.h" #include "dynamics_processor_controls.h" @@ -48,11 +48,17 @@ public: private: + void calcAttack(); + void calcRelease(); dynProcControls m_dpControls; // this member array is needed for peak detection - float currentPeak[2]; + float m_currentPeak[2]; + double m_attCoeff; + double m_relCoeff; + + bool m_needsUpdate; friend class dynProcControls; diff --git a/plugins/dynamics_processor/dynamics_processor_control_dialog.h b/plugins/dynamics_processor/dynamics_processor_control_dialog.h index 2c80a77d6..cb9fa0a47 100644 --- a/plugins/dynamics_processor/dynamics_processor_control_dialog.h +++ b/plugins/dynamics_processor/dynamics_processor_control_dialog.h @@ -23,8 +23,8 @@ * */ -#ifndef _DYNPROC_CONTROL_DIALOG_H -#define _DYNPROC_CONTROL_DIALOG_H +#ifndef DYNPROC_CONTROL_DIALOG_H +#define DYNPROC_CONTROL_DIALOG_H #include "EffectControlDialog.h" diff --git a/plugins/dynamics_processor/dynamics_processor_controls.cpp b/plugins/dynamics_processor/dynamics_processor_controls.cpp index 2b66ce8a0..313038a12 100644 --- a/plugins/dynamics_processor/dynamics_processor_controls.cpp +++ b/plugins/dynamics_processor/dynamics_processor_controls.cpp @@ -46,36 +46,21 @@ dynProcControls::dynProcControls( dynProcEffect * _eff ) : m_wavegraphModel( 0.0f, 1.0f, 200, this ), m_stereomodeModel( 0, 0, 2, this, tr( "Stereo mode" ) ) { -/* connect( &m_inputModel, SIGNAL( dataChanged() ), - this, SLOT( changeControl() ) ); - - connect( &m_outputModel, SIGNAL( dataChanged() ), - this, SLOT( changeControl() ) ); - - connect( &m_attackModel, SIGNAL( dataChanged() ), - this, SLOT( changeControl() ) ); - - connect( &m_releaseModel, SIGNAL( dataChanged() ), - this, SLOT( changeControl() ) ); - - connect( &m_stereomodeModel, SIGNAL( dataChanged() ), - this, SLOT( changeControl() ) ); -*/ connect( &m_wavegraphModel, SIGNAL( samplesChanged( int, int ) ), this, SLOT( samplesChanged( int, int ) ) ); - + connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) ); setDefaultShape(); } - -void dynProcControls::changeControl() +void dynProcControls::sampleRateChanged() { -// engine::getSong()->setModified(); + m_effect->m_needsUpdate = true; } + void dynProcControls::samplesChanged( int _begin, int _end) { engine::getSong()->setModified(); diff --git a/plugins/dynamics_processor/dynamics_processor_controls.h b/plugins/dynamics_processor/dynamics_processor_controls.h index 8f0c848f7..63274a99d 100644 --- a/plugins/dynamics_processor/dynamics_processor_controls.h +++ b/plugins/dynamics_processor/dynamics_processor_controls.h @@ -23,8 +23,8 @@ * */ -#ifndef _WAVESHAPER_CONTROLS_H -#define _WAVESHAPER_CONTROLS_H +#ifndef DYNPROC_CONTROLS_H +#define DYNPROC_CONTROLS_H #include "EffectControls.h" #include "dynamics_processor_control_dialog.h" @@ -73,6 +73,7 @@ public: private slots: void changeControl(); void samplesChanged( int, int ); + void sampleRateChanged(); void resetClicked(); void smoothClicked();