From bd00ecfdc3c741adff5efe535ab702309f50d16e Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 16 Nov 2014 18:47:01 +0200 Subject: [PATCH 1/5] 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 baf7c74ad752b4606219d796e5c1c3d08b0824ed Mon Sep 17 00:00:00 2001 From: Vesa Date: Fri, 14 Nov 2014 20:43:49 +0200 Subject: [PATCH 2/5] Strip denormals --- include/MixerWorkerThread.h | 6 ++++++ src/core/MixerWorkerThread.cpp | 10 ++++++++++ 2 files changed, 16 insertions(+) diff --git a/include/MixerWorkerThread.h b/include/MixerWorkerThread.h index 2b680e8d5..d1ebc73eb 100644 --- a/include/MixerWorkerThread.h +++ b/include/MixerWorkerThread.h @@ -31,6 +31,12 @@ #include "ThreadableJob.h" #include "Mixer.h" +#ifdef __SSE__ +#include +#endif +#ifdef __SSE3__ +#include +#endif class MixerWorkerThread : public QThread { diff --git a/src/core/MixerWorkerThread.cpp b/src/core/MixerWorkerThread.cpp index 834db4afe..817d19c71 100644 --- a/src/core/MixerWorkerThread.cpp +++ b/src/core/MixerWorkerThread.cpp @@ -101,6 +101,16 @@ MixerWorkerThread::MixerWorkerThread( Mixer* mixer ) : m_workingBuf( new sampleFrame[mixer->framesPerPeriod()] ), m_quit( false ) { + // set denormal protection for this thread + #ifdef __SSE3__ + /* DAZ flag */ + _MM_SET_DENORMALS_ZERO_MODE( _MM_DENORMALS_ZERO_ON ); + #endif + #ifdef __SSE__ + /* FTZ flag */ + _MM_SET_FLUSH_ZERO_MODE( _MM_FLUSH_ZERO_ON ); + #endif + // initialize global static data if( queueReadyWaitCond == NULL ) { From 4b4469d6c2308ea93a4e02a69b1cd34e4174b9bd Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 16 Nov 2014 15:19:54 +0200 Subject: [PATCH 3/5] Add denormals stripping to the main thread as well as workerthreads Conflicts: src/core/main.cpp --- include/MixerWorkerThread.h | 4 ++-- src/core/main.cpp | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/include/MixerWorkerThread.h b/include/MixerWorkerThread.h index d1ebc73eb..51da6a91e 100644 --- a/include/MixerWorkerThread.h +++ b/include/MixerWorkerThread.h @@ -22,8 +22,8 @@ * */ -#ifndef _MIXER_WORKER_THREAD_H -#define _MIXER_WORKER_THREAD_H +#ifndef MIXER_WORKER_THREAD_H +#define MIXER_WORKER_THREAD_H #include #include diff --git a/src/core/main.cpp b/src/core/main.cpp index f65ca98c8..1249c57b6 100644 --- a/src/core/main.cpp +++ b/src/core/main.cpp @@ -27,6 +27,14 @@ #include "lmmsversion.h" #include "versioninfo.h" +// denormals stripping +#ifdef __SSE__ +#include +#endif +#ifdef __SSE3__ +#include +#endif + #include #include #include @@ -92,6 +100,16 @@ int main( int argc, char * * argv ) // intialize RNG srand( getpid() + time( 0 ) ); + // set denormal protection for this thread + #ifdef __SSE3__ + /* DAZ flag */ + _MM_SET_DENORMALS_ZERO_MODE( _MM_DENORMALS_ZERO_ON ); + #endif + #ifdef __SSE__ + /* FTZ flag */ + _MM_SET_FLUSH_ZERO_MODE( _MM_FLUSH_ZERO_ON ); + #endif + bool core_only = false; bool fullscreen = true; bool exit_after_import = false; From c51ff31b688b6a2e5103740a1cb7a932391f85c9 Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 16 Nov 2014 20:54:16 +0200 Subject: [PATCH 4/5] 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(); From 0d129b0b93ef6fee3d9485dbae51a9ef67923393 Mon Sep 17 00:00:00 2001 From: Vesa Date: Sun, 16 Nov 2014 22:19:16 +0200 Subject: [PATCH 5/5] Logscale: enabled toggling of knob scale in context menu --- include/knob.h | 2 +- src/core/AutomatableModel.cpp | 27 ++++++++++++++------------- src/gui/widgets/knob.cpp | 8 ++++++++ 3 files changed, 23 insertions(+), 14 deletions(-) diff --git a/include/knob.h b/include/knob.h index 247836a24..ca7be5a4d 100644 --- a/include/knob.h +++ b/include/knob.h @@ -136,7 +136,7 @@ private slots: virtual void enterValue(); void displayHelp(); void friendlyUpdate(); - + void toggleScale(); private: QString displayValue() const; diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index f2721a50a..ed390d14c 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -87,8 +87,6 @@ bool AutomatableModel::isAutomated() const void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, const QString& name ) { - bool automatedOrControlled = false; - if( isAutomated() ) { // automation needs tuple of data (name, id, value) @@ -97,8 +95,6 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co me.setAttribute( "id", id() ); me.setAttribute( "value", m_value ); element.appendChild( me ); - - automatedOrControlled = true; } else { @@ -126,16 +122,15 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co m_controllerConnection->saveSettings( doc, element ); controllerElement.appendChild( element ); - - automatedOrControlled = true; } - if( automatedOrControlled && ( m_scaleType != Linear ) ) - { // note: if we have more scale types than two, make - // a mapper function enums <-> string - if(m_scaleType == Logarithmic) { - element.setAttribute( "scale_type", "log" ); - } + if( m_scaleType == Logarithmic ) + { + element.setAttribute( "scale_type", "log" ); + } + if( m_scaleType == Linear ) + { + element.setAttribute( "scale_type", "linear" ); } } @@ -148,7 +143,13 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString& if( element.hasAttribute("scale_type") ) // wrong in most cases { if( element.attribute("scale_type") == "log" ) - setScaleType( Logarithmic ); + { + setScaleType( Logarithmic ); + } + if( element.attribute("scale_type") == "linear" ) + { + setScaleType( Linear ); + } } else { setScaleType( Linear ); diff --git a/src/gui/widgets/knob.cpp b/src/gui/widgets/knob.cpp index 96b40a22f..787feb250 100644 --- a/src/gui/widgets/knob.cpp +++ b/src/gui/widgets/knob.cpp @@ -483,12 +483,20 @@ void knob::contextMenuEvent( QContextMenuEvent * ) captionMenu contextMenu( model()->displayName(), this ); addDefaultActions( &contextMenu ); + contextMenu.addAction( QPixmap(), + model()->isScaleLogarithmic() ? tr( "Set linear" ) : tr( "Set logarithmic" ), + this, SLOT( toggleScale() ) ); contextMenu.addSeparator(); contextMenu.addHelpAction(); contextMenu.exec( QCursor::pos() ); } +void knob::toggleScale() +{ + model()->setScaleLogarithmic( ! model()->isScaleLogarithmic() ); +} + void knob::dragEnterEvent( QDragEnterEvent * _dee )