From bd00ecfdc3c741adff5efe535ab702309f50d16e Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 16 Nov 2014 18:47:01 +0200 Subject: [PATCH 1/2] Dualfilter updates, add double precision versions of LMMS constants --- include/lmms_constants.h | 15 +++++--- plugins/DualFilter/DualFilter.cpp | 44 ++++++++++++++--------- plugins/DualFilter/DualFilterControls.cpp | 23 ++---------- plugins/DualFilter/DualFilterControls.h | 2 -- 4 files changed, 40 insertions(+), 44 deletions(-) diff --git a/include/lmms_constants.h b/include/lmms_constants.h index 27ea3f2aa..629db886c 100644 --- a/include/lmms_constants.h +++ b/include/lmms_constants.h @@ -25,10 +25,15 @@ #ifndef LMMS_CONSTANTS_H #define LMMS_CONSTANTS_H -const float F_PI = 3.1415926535f; -const float F_E = 2.718281828459045f; -const float F_2PI = 2*F_PI; -const float F_PI_2 = F_PI*0.5; +const double D_PI = 3.14159265358979323846; +const double D_2PI = D_PI * 2.0; +const double D_PI_2 = D_PI * 0.5; +const double D_E = 2.71828182845904523536; + +const float F_PI = (float) D_PI; +const float F_2PI = (float) D_2PI; +const float F_PI_2 = (float) D_PI_2; +const float F_E = (float) D_E; + #endif - diff --git a/plugins/DualFilter/DualFilter.cpp b/plugins/DualFilter/DualFilter.cpp index 47655291d..22036930c 100644 --- a/plugins/DualFilter/DualFilter.cpp +++ b/plugins/DualFilter/DualFilter.cpp @@ -55,7 +55,7 @@ DualFilterEffect::DualFilterEffect( Model* parent, const Descriptor::SubPluginFe { m_filter1 = new basicFilters<2>( engine::mixer()->processingSampleRate() ); m_filter2 = new basicFilters<2>( engine::mixer()->processingSampleRate() ); - + // ensure filters get updated m_filter1changed = true; m_filter2changed = true; @@ -84,25 +84,41 @@ bool DualFilterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames const float d = dryLevel(); const float w = wetLevel(); - m_filter1->setFilterType( m_dfControls.m_filter1Model.value() ); - m_filter2->setFilterType( m_dfControls.m_filter2Model.value() ); - + if( m_dfControls.m_filter1Model.isValueChanged() ) + { + m_filter1->setFilterType( m_dfControls.m_filter1Model.value() ); + m_filter1changed = true; + } + if( m_dfControls.m_filter2Model.isValueChanged() ) + { + m_filter2->setFilterType( m_dfControls.m_filter2Model.value() ); + m_filter2changed = true; + } + const bool enabled1 = m_dfControls.m_enabled1Model.value(); const bool enabled2 = m_dfControls.m_enabled2Model.value(); - // recalculate only when necessary - if( enabled1 && m_filter1changed ) + // recalculate only when necessary: either cut/res is changed, or the changed-flag is set (filter type or samplerate changed) + if( ( enabled1 && ( m_dfControls.m_cut1Model.isValueChanged() || + m_dfControls.m_res1Model.isValueChanged() ) ) || m_filter1changed ) { m_filter1->calcFilterCoeffs( m_dfControls.m_cut1Model.value(), m_dfControls.m_res1Model.value() ); m_filter1changed = false; } - if( enabled2 && m_filter2changed ) + if( ( enabled2 && ( m_dfControls.m_cut2Model.isValueChanged() || + m_dfControls.m_res2Model.isValueChanged() ) ) || m_filter2changed ) { m_filter2->calcFilterCoeffs( m_dfControls.m_cut2Model.value(), m_dfControls.m_res2Model.value() ); m_filter2changed = false; } - + // get mix amounts for wet signals of both filters + const float mix2 = ( ( m_dfControls.m_mixModel.value() + 1.0f ) * 0.5f ); + const float mix1 = 1.0f - mix2; + + const float gain1 = m_dfControls.m_gain1Model.value() * 0.01f; + const float gain2 = m_dfControls.m_gain2Model.value() * 0.01f; + // buffer processing loop for( fpp_t f = 0; f < frames; ++f ) { @@ -110,10 +126,6 @@ bool DualFilterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames sample_t s1[2] = { buf[f][0], buf[f][1] }; // filter 1 sample_t s2[2] = { buf[f][0], buf[f][1] }; // filter 2 - // get mix amounts for wet signals of both filters - const float mix1 = 1.0f - ( ( m_dfControls.m_mixModel.value( f ) + 1.0f ) / 2.0f ); - const float mix2 = ( ( m_dfControls.m_mixModel.value( f ) + 1.0f ) / 2.0f ); - // update filter 1 if( enabled1 ) { @@ -121,8 +133,8 @@ bool DualFilterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames s1[1] = m_filter1->update( s1[1], 1 ); // apply gain - s1[0] *= ( m_dfControls.m_gain1Model.value( f ) / 100.0f ); - s1[1] *= ( m_dfControls.m_gain1Model.value( f ) / 100.0f ); + s1[0] *= gain1; + s1[1] *= gain1; // apply mix s[0] += ( s1[0] * mix1 ); @@ -136,8 +148,8 @@ bool DualFilterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames s2[1] = m_filter2->update( s2[1], 1 ); //apply gain - s2[0] *= ( m_dfControls.m_gain2Model.value( f ) / 100.0f ); - s2[1] *= ( m_dfControls.m_gain2Model.value( f ) / 100.0f ); + s2[0] *= gain2; + s2[1] *= gain2; // apply mix s[0] += ( s2[0] * mix2 ); diff --git a/plugins/DualFilter/DualFilterControls.cpp b/plugins/DualFilter/DualFilterControls.cpp index 73d86b48e..344a34ca0 100644 --- a/plugins/DualFilter/DualFilterControls.cpp +++ b/plugins/DualFilter/DualFilterControls.cpp @@ -51,14 +51,6 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_res2Model( 0.5, basicFilters<0>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 2" ) ), m_gain2Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 2" ) ) { - connect( &m_filter1Model, SIGNAL( dataChanged() ), this, SLOT( updateFilter1() ) ); - connect( &m_cut1Model, SIGNAL( dataChanged() ), this, SLOT( updateFilter1() ) ); - connect( &m_res1Model, SIGNAL( dataChanged() ), this, SLOT( updateFilter1() ) ); - - connect( &m_filter2Model, SIGNAL( dataChanged() ), this, SLOT( updateFilter2() ) ); - connect( &m_cut2Model, SIGNAL( dataChanged() ), this, SLOT( updateFilter2() ) ); - connect( &m_res2Model, SIGNAL( dataChanged() ), this, SLOT( updateFilter2() ) ); - m_filter1Model.addItem( tr( "LowPass" ), new PixmapLoader( "filter_lp" ) ); m_filter1Model.addItem( tr( "HiPass" ), new PixmapLoader( "filter_hp" ) ); m_filter1Model.addItem( tr( "BandPass csg" ), new PixmapLoader( "filter_bp" ) ); @@ -96,17 +88,6 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : -void DualFilterControls::updateFilter1() -{ - m_effect->m_filter1changed = true; -} - -void DualFilterControls::updateFilter2() -{ - m_effect->m_filter2changed = true; -} - - void DualFilterControls::updateFilters() { // swap filters to new ones @@ -118,8 +99,8 @@ void DualFilterControls::updateFilters() // flag filters as needing recalculation - updateFilter1(); - updateFilter2(); + m_effect->m_filter1changed = true; + m_effect->m_filter2changed = true; } diff --git a/plugins/DualFilter/DualFilterControls.h b/plugins/DualFilter/DualFilterControls.h index df66aac3b..65953ba91 100644 --- a/plugins/DualFilter/DualFilterControls.h +++ b/plugins/DualFilter/DualFilterControls.h @@ -62,8 +62,6 @@ public: private slots: - void updateFilter1(); - void updateFilter2(); void updateFilters(); private: From c51ff31b688b6a2e5103740a1cb7a932391f85c9 Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 16 Nov 2014 20:54:16 +0200 Subject: [PATCH 2/2] 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();