From 60ad2c8aa0582e4453aa4b4bac8e24e79c678871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stian=20J=C3=B8rgensrud?= Date: Thu, 11 Dec 2014 00:24:52 +0100 Subject: [PATCH 01/19] New SnareMarch preset and updated KickPower The snare sounds like it would be used for marching. If you can make it brighter without making it sound funny, please do... The kick was too dark for general purpose. Shortened it, raised it in the frequency range and added more noise. --- .../Kicker/{Kick power.xpf => KickPower.xpf} | 20 +-- data/presets/Kicker/SnareMarch.xpf | 161 ++++++++++++++++++ 2 files changed, 171 insertions(+), 10 deletions(-) rename data/presets/Kicker/{Kick power.xpf => KickPower.xpf} (54%) create mode 100644 data/presets/Kicker/SnareMarch.xpf diff --git a/data/presets/Kicker/Kick power.xpf b/data/presets/Kicker/KickPower.xpf similarity index 54% rename from data/presets/Kicker/Kick power.xpf rename to data/presets/Kicker/KickPower.xpf index b4e1daba3..27259ce25 100644 --- a/data/presets/Kicker/Kick power.xpf +++ b/data/presets/Kicker/KickPower.xpf @@ -1,20 +1,20 @@ - + - + - + - - - - + + + + - - - + + + diff --git a/data/presets/Kicker/SnareMarch.xpf b/data/presets/Kicker/SnareMarch.xpf new file mode 100644 index 000000000..d4d1ad0db --- /dev/null +++ b/data/presets/Kicker/SnareMarch.xpf @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From 1ad5ef22d7c99dd171c80c21395d28c378d4b1fd Mon Sep 17 00:00:00 2001 From: Vesa Date: Sat, 13 Dec 2014 11:26:15 +0200 Subject: [PATCH 02/19] Bitcrush: small improvement, also add some stuff to math & constants Constants: - calculate all in long double so as to improve the accuracy of our pre-calculated constants - add some possibly useful constants: reciprocal of pi, square of pi, and reciprocal of e Math: - new math convenience functions: absMax, absMin --- include/lmms_constants.h | 29 +++++++++++++++++++++-------- include/lmms_math.h | 14 ++++++++++++++ plugins/Bitcrush/Bitcrush.cpp | 6 ++++-- 3 files changed, 39 insertions(+), 10 deletions(-) diff --git a/include/lmms_constants.h b/include/lmms_constants.h index 629db886c..6f50c6f8e 100644 --- a/include/lmms_constants.h +++ b/include/lmms_constants.h @@ -25,15 +25,28 @@ #ifndef LMMS_CONSTANTS_H #define LMMS_CONSTANTS_H -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 long double LD_PI = 3.14159265358979323846264338327950288419716939937510; +const long double LD_2PI = LD_PI * 2.0; +const long double LD_PI_2 = LD_PI * 0.5; +const long double LD_PI_R = 1.0 / LD_PI; +const long double LD_PI_SQR = LD_PI * LD_PI; +const long double LD_E = 2.71828182845904523536028747135266249775724709369995; +const long double LD_E_R = 1.0 / LD_E; -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; +const double D_PI = (double) LD_PI; +const double D_2PI = (double) LD_2PI; +const double D_PI_2 = (double) LD_PI_2; +const double D_PI_R = (double) LD_PI_R; +const double D_PI_SQR = (double) LD_PI_SQR; +const double D_E = (double) LD_E; +const double D_E_R = (double) LD_E_R; +const float F_PI = (float) LD_PI; +const float F_2PI = (float) LD_2PI; +const float F_PI_2 = (float) LD_PI_2; +const float F_PI_R = (float) LD_PI_R; +const float F_PI_SQR = (float) LD_PI_SQR; +const float F_E = (float) LD_E; +const float F_E_R = (float) LD_E_R; #endif diff --git a/include/lmms_math.h b/include/lmms_math.h index 016e1a49f..7cde29153 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -289,4 +289,18 @@ static inline float fastSqrt( float n ) return u.f; } +//! returns value furthest from zero +template +static inline T absMax( T a, T b ) +{ + return qAbs(a) > qAbs(b) ? a : b; +} + +//! returns value nearest to zero +template +static inline T absMin( T a, T b ) +{ + return qAbs(a) < qAbs(b) ? a : b; +} + #endif diff --git a/plugins/Bitcrush/Bitcrush.cpp b/plugins/Bitcrush/Bitcrush.cpp index e9ab9639a..8a2822d60 100644 --- a/plugins/Bitcrush/Bitcrush.cpp +++ b/plugins/Bitcrush/Bitcrush.cpp @@ -30,6 +30,8 @@ const int OS_RATE = 5; const float OS_RATIO = 1.0f / OS_RATE; const float CUTOFF_RATIO = 0.353553391f; const int SILENCEFRAMES = 10; +const float OS_RESAMPLE [5] = { 0.0001490062883964112, 0.1645978376763992, 0.6705063120704088, + 0.1645978376763992, 0.0001490062883964112 }; extern "C" { @@ -219,8 +221,8 @@ bool BitcrushEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) float rsum = 0.0f; for( int o = 0; o < OS_RATE; ++o ) { - lsum += m_buffer[f * OS_RATE + o][0] * OS_RATIO; - rsum += m_buffer[f * OS_RATE + o][1] * OS_RATIO; + lsum += m_buffer[f * OS_RATE + o][0] * OS_RESAMPLE[o]; + rsum += m_buffer[f * OS_RATE + o][1] * OS_RESAMPLE[o]; } 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; From 73cad09968d35ee6c7f2bb11dcad7e8428cb7188 Mon Sep 17 00:00:00 2001 From: Vesa V Date: Sat, 13 Dec 2014 13:05:50 +0200 Subject: [PATCH 03/19] Update FxMixer.cpp prevent double adding of jobs --- src/core/FxMixer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 7a21acb92..7f78afc9f 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -99,7 +99,7 @@ inline void FxChannel::processed() void FxChannel::incrementDeps() { m_dependenciesMet.ref(); - if( m_dependenciesMet >= m_receives.size() ) + if( m_dependenciesMet >= m_receives.size() && ! m_queued ) { m_queued = true; MixerWorkerThread::addJob( this ); From 4266de78304ce5c6ffbde34601216f0878935ef9 Mon Sep 17 00:00:00 2001 From: Vesa Date: Sat, 13 Dec 2014 16:28:44 +0200 Subject: [PATCH 04/19] More fixes (Bitcrush, Linkwitz-Riley filter) --- include/BasicFilters.h | 20 ++++++++++---------- plugins/Bitcrush/Bitcrush.cpp | 7 ++++++- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/include/BasicFilters.h b/include/BasicFilters.h index 98c69474a..e09d40c42 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -75,34 +75,34 @@ public: inline void setCoeffs( float freq ) { // wc - const float wc = F_2PI * freq; + const float wc = F_2PI * freq / m_sampleRate; const float wc2 = wc * wc; const float wc3 = wc2 * wc; m_wc4 = wc2 * wc2; // k - const float k = wc / tanf( F_PI * freq / m_sampleRate ); + const float k = wc / tan( wc * 0.5 ); const float k2 = k * k; const float k3 = k2 * k; m_k4 = k2 * k2; // a - static const float sqrt2 = sqrtf( 2.0f ); + static const double sqrt2 = sqrt( 2.0 ); const float sq_tmp1 = sqrt2 * wc3 * k; const float sq_tmp2 = sqrt2 * wc * k3; - m_a = 4.0f * wc2 * k2 + 2.0f * sq_tmp1 + m_k4 + 2.0f * sq_tmp2 + m_wc4; + m_a = 1.0f / ( 4.0f * wc2 * k2 + 2.0f * sq_tmp1 + m_k4 + 2.0f * sq_tmp2 + m_wc4 ); // b - m_b1 = ( 4.0f * ( m_wc4 + sq_tmp1 - m_k4 - sq_tmp2 ) ) / m_a; - m_b2 = ( 6.0f * m_wc4 - 8.0f * wc2 * k2 + 6.0f * m_k4 ) / m_a; - m_b3 = ( 4.0f * ( m_wc4 - sq_tmp1 + sq_tmp2 - m_k4 ) ) / m_a; - m_b4 = ( m_k4 - 2.0f * sq_tmp1 + m_wc4 - 2.0f * sq_tmp2 + 4.0f * wc2 * k2 ) / m_a; + m_b1 = ( 4.0f * ( m_wc4 + sq_tmp1 - m_k4 - sq_tmp2 ) ) * m_a; + m_b2 = ( 6.0f * m_wc4 - 8.0f * wc2 * k2 + 6.0f * m_k4 ) * m_a; + m_b3 = ( 4.0f * ( m_wc4 - sq_tmp1 + sq_tmp2 - m_k4 ) ) * m_a; + m_b4 = ( m_k4 - 2.0f * sq_tmp1 + m_wc4 - 2.0f * sq_tmp2 + 4.0f * wc2 * k2 ) * m_a; } inline void setLowpass( float freq ) { setCoeffs( freq ); - m_a0 = m_wc4 / m_a; + m_a0 = m_wc4 * m_a; m_a1 = 4.0f * m_a0; m_a2 = 6.0f * m_a0; } @@ -110,7 +110,7 @@ public: inline void setHighpass( float freq ) { setCoeffs( freq ); - m_a0 = m_k4 / m_a; + m_a0 = m_k4 * m_a; m_a1 = 4.0f * m_a0; m_a2 = 6.0f * m_a0; } diff --git a/plugins/Bitcrush/Bitcrush.cpp b/plugins/Bitcrush/Bitcrush.cpp index 8a2822d60..84f5b45ba 100644 --- a/plugins/Bitcrush/Bitcrush.cpp +++ b/plugins/Bitcrush/Bitcrush.cpp @@ -80,7 +80,7 @@ void BitcrushEffect::sampleRateChanged() { m_sampleRate = Engine::mixer()->processingSampleRate(); m_filter.setSampleRate( m_sampleRate ); - m_filter.setLowpass( m_sampleRate * CUTOFF_RATIO ); + m_filter.setLowpass( m_sampleRate * ( CUTOFF_RATIO * OS_RATIO ) ); m_needsUpdate = true; } @@ -97,6 +97,11 @@ inline float BitcrushEffect::noise( float amt ) bool BitcrushEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) { + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + // update values if( m_needsUpdate || m_controls.m_rateEnabled.isValueChanged() ) { From a8924a34dd08b75d6020f5aea78242531120455e Mon Sep 17 00:00:00 2001 From: tresf Date: Sat, 13 Dec 2014 11:27:14 -0500 Subject: [PATCH 05/19] Check major/minor version before setting theme directory --- src/core/config_mgr.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/core/config_mgr.cpp b/src/core/config_mgr.cpp index 411de79d5..93f286345 100644 --- a/src/core/config_mgr.cpp +++ b/src/core/config_mgr.cpp @@ -289,7 +289,13 @@ void configManager::loadConfigFile() node = node.nextSibling(); } - if( value( "paths", "artwork" ) != "" ) + // don't use dated theme folders as they break the UI (i.e. 0.4 != 1.0, etc) + bool use_artwork_path = + root.attribute( "version" ).startsWith( + QString::number( LMMS_VERSION_MAJOR ) + "." + + QString::number( LMMS_VERSION_MINOR ) ); + + if( use_artwork_path && value( "paths", "artwork" ) != "" ) { m_artworkDir = value( "paths", "artwork" ); if( !QDir( m_artworkDir ).exists() ) From a182a3e8cc9c512906aee38f1b8e067f31447198 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sat, 13 Dec 2014 12:11:31 -0500 Subject: [PATCH 06/19] Fix scroll bar gap Closes #1437 --- src/tracks/InstrumentTrack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index bc9728e36..20b737444 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -86,7 +86,7 @@ const char * volume_help = QT_TRANSLATE_NOOP( "InstrumentTrack", const int INSTRUMENT_WIDTH = 254; const int INSTRUMENT_HEIGHT = INSTRUMENT_WIDTH; -const int PIANO_HEIGHT = 84; +const int PIANO_HEIGHT = 82; const int INSTRUMENT_WINDOW_CACHE_SIZE = 8; From e6ae2be65a5f3cd55d055be52ad0348ec3586604 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sat, 13 Dec 2014 12:19:20 -0500 Subject: [PATCH 07/19] Bump DualFilter high cutoff to 20k Closes #1395. --- plugins/DualFilter/DualFilterControls.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/DualFilter/DualFilterControls.cpp b/plugins/DualFilter/DualFilterControls.cpp index 344a34ca0..df295c39f 100644 --- a/plugins/DualFilter/DualFilterControls.cpp +++ b/plugins/DualFilter/DualFilterControls.cpp @@ -39,7 +39,7 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_enabled1Model( true, this, tr( "Filter 1 enabled" ) ), m_filter1Model( this, tr( "Filter 1 type" ) ), - m_cut1Model( 7000.0f, 1.0f, 14000.0f, 1.0f, this, tr( "Cutoff 1 frequency" ) ), + m_cut1Model( 7000.0f, 1.0f, 20000.0f, 1.0f, this, tr( "Cutoff 1 frequency" ) ), m_res1Model( 0.5, basicFilters<0>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 1" ) ), m_gain1Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 1" ) ), @@ -47,7 +47,7 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_enabled2Model( true, this, tr( "Filter 2 enabled" ) ), m_filter2Model( this, tr( "Filter 2 type" ) ), - m_cut2Model( 7000.0f, 1.0f, 14000.0f, 1.0f, this, tr( "Cutoff 2 frequency" ) ), + m_cut2Model( 7000.0f, 1.0f, 20000.0f, 1.0f, this, tr( "Cutoff 2 frequency" ) ), 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" ) ) { From 0789bae53a7a58bb61cb18dc917f5723f376cba5 Mon Sep 17 00:00:00 2001 From: Vesa Date: Mon, 15 Dec 2014 10:41:43 +0200 Subject: [PATCH 08/19] Crossover EQ initial commit, also fix bugs in LR4 filter and Fader --- include/BasicFilters.h | 81 ++++--- include/Fader.h | 2 +- plugins/CMakeLists.txt | 1 + plugins/CrossoverEQ/CMakeLists.txt | 3 + plugins/CrossoverEQ/CrossoverEQ.cpp | 219 ++++++++++++++++++ plugins/CrossoverEQ/CrossoverEQ.h | 77 ++++++ .../CrossoverEQ/CrossoverEQControlDialog.cpp | 115 +++++++++ .../CrossoverEQ/CrossoverEQControlDialog.h | 50 ++++ plugins/CrossoverEQ/CrossoverEQControls.cpp | 116 ++++++++++ plugins/CrossoverEQ/CrossoverEQControls.h | 86 +++++++ plugins/CrossoverEQ/artwork.png | Bin 0 -> 52627 bytes plugins/CrossoverEQ/fader_bg.png | Bin 0 -> 234 bytes plugins/CrossoverEQ/fader_empty.png | Bin 0 -> 198 bytes plugins/CrossoverEQ/fader_knob2.png | Bin 0 -> 783 bytes src/gui/widgets/Fader.cpp | 15 +- 15 files changed, 728 insertions(+), 37 deletions(-) create mode 100644 plugins/CrossoverEQ/CMakeLists.txt create mode 100644 plugins/CrossoverEQ/CrossoverEQ.cpp create mode 100644 plugins/CrossoverEQ/CrossoverEQ.h create mode 100644 plugins/CrossoverEQ/CrossoverEQControlDialog.cpp create mode 100644 plugins/CrossoverEQ/CrossoverEQControlDialog.h create mode 100644 plugins/CrossoverEQ/CrossoverEQControls.cpp create mode 100644 plugins/CrossoverEQ/CrossoverEQControls.h create mode 100644 plugins/CrossoverEQ/artwork.png create mode 100644 plugins/CrossoverEQ/fader_bg.png create mode 100644 plugins/CrossoverEQ/fader_empty.png create mode 100644 plugins/CrossoverEQ/fader_knob2.png diff --git a/include/BasicFilters.h b/include/BasicFilters.h index e09d40c42..eda8d7b14 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -64,6 +64,7 @@ public: for( int i = 0; i < CHANNELS; ++i ) { m_z1[i] = m_z2[i] = m_z3[i] = m_z4[i] = 0.0f; + m_y1[i] = m_y2[i] = m_y3[i] = m_y4[i] = 0.0f; } } @@ -75,69 +76,89 @@ public: inline void setCoeffs( float freq ) { // wc - const float wc = F_2PI * freq / m_sampleRate; - const float wc2 = wc * wc; - const float wc3 = wc2 * wc; + const double wc = D_2PI * freq; + const double wc2 = wc * wc; + const double wc3 = wc2 * wc; m_wc4 = wc2 * wc2; // k - const float k = wc / tan( wc * 0.5 ); - const float k2 = k * k; - const float k3 = k2 * k; + const double k = wc / tan( D_PI * freq / m_sampleRate ); + const double k2 = k * k; + const double k3 = k2 * k; m_k4 = k2 * k2; // a static const double sqrt2 = sqrt( 2.0 ); - const float sq_tmp1 = sqrt2 * wc3 * k; - const float sq_tmp2 = sqrt2 * wc * k3; - m_a = 1.0f / ( 4.0f * wc2 * k2 + 2.0f * sq_tmp1 + m_k4 + 2.0f * sq_tmp2 + m_wc4 ); + const double sq_tmp1 = sqrt2 * wc3 * k; + const double sq_tmp2 = sqrt2 * wc * k3; + + m_a = 1.0 / ( 4.0 * wc2 * k2 + 2.0 * sq_tmp1 + m_k4 + 2.0 * sq_tmp2 + m_wc4 ); // b - m_b1 = ( 4.0f * ( m_wc4 + sq_tmp1 - m_k4 - sq_tmp2 ) ) * m_a; - m_b2 = ( 6.0f * m_wc4 - 8.0f * wc2 * k2 + 6.0f * m_k4 ) * m_a; - m_b3 = ( 4.0f * ( m_wc4 - sq_tmp1 + sq_tmp2 - m_k4 ) ) * m_a; - m_b4 = ( m_k4 - 2.0f * sq_tmp1 + m_wc4 - 2.0f * sq_tmp2 + 4.0f * wc2 * k2 ) * m_a; + m_b1 = ( 4.0 * ( m_wc4 + sq_tmp1 - m_k4 - sq_tmp2 ) ) * m_a; + m_b2 = ( 6.0 * m_wc4 - 8.0 * wc2 * k2 + 6.0 * m_k4 ) * m_a; + m_b3 = ( 4.0 * ( m_wc4 - sq_tmp1 + sq_tmp2 - m_k4 ) ) * m_a; + m_b4 = ( m_k4 - 2.0 * sq_tmp1 + m_wc4 - 2.0 * sq_tmp2 + 4.0 * wc2 * k2 ) * m_a; } inline void setLowpass( float freq ) { setCoeffs( freq ); m_a0 = m_wc4 * m_a; - m_a1 = 4.0f * m_a0; - m_a2 = 6.0f * m_a0; + m_a1 = 4.0 * m_a0; + m_a2 = 6.0 * m_a0; } inline void setHighpass( float freq ) { setCoeffs( freq ); m_a0 = m_k4 * m_a; - m_a1 = 4.0f * m_a0; - m_a2 = 6.0f * m_a0; + m_a1 = -4.0 * m_a0; + m_a2 = 6.0 * m_a0; } inline float update( float in, ch_cnt_t ch ) { - const float a0in = m_a0 * in; - const float a1in = m_a1 * in; - const float out = m_z1[ch] + a0in; + const double y = m_a0 * in + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + + ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 ) - + ( m_y1[ch] * m_b1 ) - ( m_y2[ch] * m_b2 ) - + ( m_y3[ch] * m_b3 ) - ( m_y4[ch] * m_b4 ); + + m_z4[ch] = m_z3[ch]; + m_z3[ch] = m_z2[ch]; + m_z2[ch] = m_z1[ch]; + m_z1[ch] = in; - m_z1[ch] = a1in + m_z2[ch] - ( m_b1 * out ); - m_z2[ch] = ( m_a2 * in ) + m_z3[ch] - ( m_b2 * out ); - m_z3[ch] = a1in + m_z4[ch] - ( m_b3 * out ); - m_z4[ch] = a0in - ( m_b4 * out ); + m_y4[ch] = m_y3[ch]; + m_y3[ch] = m_y2[ch]; + m_y2[ch] = m_y1[ch]; + m_y1[ch] = y; - return out; + return y; + +// for some reason converting to direct form 2 doesn't seem to work for this filter +/* const double x = in - ( m_z1[ch] * m_b1 ) - ( m_z2[ch] * m_b2 ) - + ( m_z3[ch] * m_b3 ) - ( m_z4[ch] * m_b4 ); + + m_z4[ch] = m_z3[ch]; + m_z3[ch] = m_z2[ch]; + m_z2[ch] = m_z1[ch]; + m_z1[ch] = x; + + return ( m_a0 * x ) + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + + ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 );*/ } private: float m_sampleRate; - float m_wc4; - float m_k4; - float m_a, m_a0, m_a1, m_a2; - float m_b1, m_b2, m_b3, m_b4; + double m_wc4; + double m_k4; + double m_a, m_a0, m_a1, m_a2; + double m_b1, m_b2, m_b3, m_b4; - typedef float frame[CHANNELS]; + typedef double frame[CHANNELS]; frame m_z1, m_z2, m_z3, m_z4; + frame m_y1, m_y2, m_y3, m_y4; }; typedef LinkwitzRiley<2> StereoLinkwitzRiley; diff --git a/include/Fader.h b/include/Fader.h index 96c3ae732..9e8f034ea 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -103,7 +103,7 @@ private: float fRange = m_model->maxValue() - m_model->minValue(); float realVal = m_model->value() - m_model->minValue(); - return height() - ( ( height() - ( *s_knob ).height() ) * ( realVal / fRange ) ); + return height() - ( ( height() - m_knob->height() ) * ( realVal / fRange ) ); } FloatModel * m_model; diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4df760ecd..cf7a5a94e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -6,6 +6,7 @@ ADD_SUBDIRECTORY(Bitcrush) ADD_SUBDIRECTORY(carlabase) ADD_SUBDIRECTORY(carlapatchbay) ADD_SUBDIRECTORY(carlarack) +ADD_SUBDIRECTORY(CrossoverEQ) ADD_SUBDIRECTORY(delay) ADD_SUBDIRECTORY(DualFilter) ADD_SUBDIRECTORY(dynamics_processor) diff --git a/plugins/CrossoverEQ/CMakeLists.txt b/plugins/CrossoverEQ/CMakeLists.txt new file mode 100644 index 000000000..fbc8407d9 --- /dev/null +++ b/plugins/CrossoverEQ/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(crossovereq CrossoverEQ.cpp CrossoverEQControls.cpp CrossoverEQControlDialog.cpp MOCFILES CrossoverEQControls.h CrossoverEQControlDialog.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp new file mode 100644 index 000000000..a50b6381f --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -0,0 +1,219 @@ +/* + * CrossoverEQ.cpp - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "CrossoverEQ.h" +#include "lmms_math.h" +#include "embed.cpp" + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT crossovereq_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Crossover Equalizer", + QT_TRANSLATE_NOOP( "pluginBrowser", "A 4-band Crossover Equalizer" ), + "Vesa Kivimäki ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +}; + +} + + +CrossoverEQEffect::CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ) : + Effect( &crossovereq_plugin_descriptor, parent, key ), + m_controls( this ), + m_sampleRate( Engine::mixer()->processingSampleRate() ), + m_lp1( m_sampleRate ), + m_lp2( m_sampleRate ), + m_lp3( m_sampleRate ), + m_hp2( m_sampleRate ), + m_hp3( m_sampleRate ), + m_hp4( m_sampleRate ), + m_needsUpdate( true ) +{ + m_tmp1 = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() ); + m_tmp2 = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() ); + m_work = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() ); +} + +CrossoverEQEffect::~CrossoverEQEffect() +{ + MM_FREE( m_tmp1 ); + MM_FREE( m_tmp2 ); + MM_FREE( m_work ); +} + +void CrossoverEQEffect::sampleRateChanged() +{ + m_sampleRate = Engine::mixer()->processingSampleRate(); + m_lp1.setSampleRate( m_sampleRate ); + m_lp2.setSampleRate( m_sampleRate ); + m_lp3.setSampleRate( m_sampleRate ); + m_hp2.setSampleRate( m_sampleRate ); + m_hp3.setSampleRate( m_sampleRate ); + m_hp4.setSampleRate( m_sampleRate ); + m_needsUpdate = true; +} + + +bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +{ + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + + // filters update + if( m_needsUpdate || m_controls.m_xover12.isValueChanged() ) + { + m_lp1.setLowpass( m_controls.m_xover12.value() ); + m_lp1.clearHistory(); + m_hp2.setHighpass( m_controls.m_xover12.value() ); + m_hp2.clearHistory(); + } + if( m_needsUpdate || m_controls.m_xover23.isValueChanged() ) + { + m_lp2.setLowpass( m_controls.m_xover23.value() ); + m_lp2.clearHistory(); + m_hp3.setHighpass( m_controls.m_xover23.value() ); + m_hp3.clearHistory(); + } + if( m_needsUpdate || m_controls.m_xover34.isValueChanged() ) + { + m_lp3.setLowpass( m_controls.m_xover34.value() ); + m_lp3.clearHistory(); + m_hp4.setHighpass( m_controls.m_xover34.value() ); + m_hp4.clearHistory(); + } + + // gain values update + if( m_needsUpdate || m_controls.m_gain1.isValueChanged() ) + { + m_gain1 = dbvToAmp( m_controls.m_gain1.value() ); + } + if( m_needsUpdate || m_controls.m_gain2.isValueChanged() ) + { + m_gain2 = dbvToAmp( m_controls.m_gain2.value() ); + } + if( m_needsUpdate || m_controls.m_gain3.isValueChanged() ) + { + m_gain3 = dbvToAmp( m_controls.m_gain3.value() ); + } + if( m_needsUpdate || m_controls.m_gain4.isValueChanged() ) + { + m_gain4 = dbvToAmp( m_controls.m_gain4.value() ); + } + + // mute values update + const bool mute1 = m_controls.m_mute1.value(); + const bool mute2 = m_controls.m_mute2.value(); + const bool mute3 = m_controls.m_mute3.value(); + const bool mute4 = m_controls.m_mute4.value(); + + m_needsUpdate = false; + + memset( m_work, 0, sizeof( sampleFrame ) * frames ); + + // run temp bands + for( int f = 0; f < frames; ++f ) + { + m_tmp1[f][0] = m_lp2.update( buf[f][0], 0 ); + m_tmp1[f][1] = m_lp2.update( buf[f][1], 1 ); + m_tmp2[f][0] = m_hp3.update( buf[f][0], 0 ); + m_tmp2[f][1] = m_hp3.update( buf[f][1], 1 ); + } + + // run band 1 + if( ! mute1 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_lp1.update( m_tmp1[f][0], 0 ) * m_gain1; + m_work[f][1] += m_lp1.update( m_tmp1[f][1], 1 ) * m_gain1; + } + } + + // run band 2 + if( ! mute2 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_hp2.update( m_tmp1[f][0], 0 ) * m_gain2; + m_work[f][1] += m_hp2.update( m_tmp1[f][1], 1 ) * m_gain2; + } + } + + // run band 3 + if( ! mute3 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_lp3.update( m_tmp2[f][0], 0 ) * m_gain3; + m_work[f][1] += m_lp3.update( m_tmp2[f][1], 1 ) * m_gain3; + } + } + + // run band 4 + if( ! mute4 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_hp4.update( m_tmp2[f][0], 0 ) * m_gain4; + m_work[f][1] += m_hp4.update( m_tmp2[f][1], 1 ) * m_gain4; + } + } + + const float d = dryLevel(); + const float w = wetLevel(); + double outSum = 0.0; + for( int f = 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 ); + + return isRunning(); +} + + +extern "C" +{ + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) +{ + return new CrossoverEQEffect( parent, static_cast( data ) ); +} + +} diff --git a/plugins/CrossoverEQ/CrossoverEQ.h b/plugins/CrossoverEQ/CrossoverEQ.h new file mode 100644 index 000000000..36b3a6bc5 --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQ.h @@ -0,0 +1,77 @@ +/* + * CrossoverEQ.h - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef CROSSOVEREQ_H +#define CROSSOVEREQ_H + +#include "Effect.h" +#include "CrossoverEQControls.h" +#include "ValueBuffer.h" +#include "lmms_math.h" +#include "BasicFilters.h" + +class CrossoverEQEffect : public Effect +{ +public: + CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); + virtual ~CrossoverEQEffect(); + virtual bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ); + + virtual EffectControls* controls() + { + return &m_controls; + } + +private: + CrossoverEQControls m_controls; + + void sampleRateChanged(); + + float m_sampleRate; + + float m_gain1; + float m_gain2; + float m_gain3; + float m_gain4; + + StereoLinkwitzRiley m_lp1; + StereoLinkwitzRiley m_lp2; + StereoLinkwitzRiley m_lp3; + + StereoLinkwitzRiley m_hp2; + StereoLinkwitzRiley m_hp3; + StereoLinkwitzRiley m_hp4; + + sampleFrame * m_tmp1; + sampleFrame * m_tmp2; + sampleFrame * m_work; + + bool m_needsUpdate; + + friend class CrossoverEQControls; +}; + +#endif diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp new file mode 100644 index 000000000..8a9eecf4d --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp @@ -0,0 +1,115 @@ +/* + * CrossoverEQControlDialog.cpp - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include "CrossoverEQControlDialog.h" +#include "CrossoverEQControls.h" +#include "embed.h" +#include "ToolTip.h" +#include "LedCheckbox.h" +#include "Knob.h" +#include "Fader.h" + +CrossoverEQControlDialog::CrossoverEQControlDialog( CrossoverEQControls * controls ) : + EffectControlDialog( controls ) +{ + setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); + setPalette( pal ); + setFixedSize( 167, 188 ); + + // knobs + Knob * xover12 = new Knob( knobBright_26, this ); + xover12->move( 29, 15 ); + xover12->setModel( & controls->m_xover12 ); + xover12->setLabel( "1/2" ); + xover12->setHintText( tr( "Band 1/2 Crossover:" ), " Hz" ); + + Knob * xover23 = new Knob( knobBright_26, this ); + xover23->move( 69, 15 ); + xover23->setModel( & controls->m_xover23 ); + xover23->setLabel( "2/3" ); + xover23->setHintText( tr( "Band 2/3 Crossover:" ), " Hz" ); + + Knob * xover34 = new Knob( knobBright_26, this ); + xover34->move( 109, 15 ); + xover34->setModel( & controls->m_xover34 ); + xover34->setLabel( "3/4" ); + xover34->setHintText( tr( "Band 3/4 Crossover:" ), " Hz" ); + + m_fader_bg = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_bg" ) ); + m_fader_empty = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_empty" ) ); + m_fader_knob = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_knob2" ) ); + + // faders + Fader * gain1 = new Fader( &controls->m_gain1, "Band 1 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain1->move( 7, 56 ); + gain1->setDisplayConversion( false ); + gain1->setHintText( tr( "Band 1 Gain:" ), " dBV" ); + + Fader * gain2 = new Fader( &controls->m_gain2, "Band 2 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain2->move( 47, 56 ); + gain2->setDisplayConversion( false ); + gain2->setHintText( tr( "Band 2 Gain:" ), " dBV" ); + + Fader * gain3 = new Fader( &controls->m_gain3, "Band 3 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain3->move( 87, 56 ); + gain3->setDisplayConversion( false ); + gain3->setHintText( tr( "Band 3 Gain:" ), " dBV" ); + + Fader * gain4 = new Fader( &controls->m_gain4, "Band 4 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain4->move( 127, 56 ); + gain4->setDisplayConversion( false ); + gain4->setHintText( tr( "Band 4 Gain:" ), " dBV" ); + + // leds + LedCheckBox * mute1 = new LedCheckBox( "M", this, tr( "Band 1 Mute" ), LedCheckBox::Red ); + mute1->move( 11, 158 ); + mute1->setModel( & controls->m_mute1 ); + ToolTip::add( mute1, tr( "Mute Band 1" ) ); + + LedCheckBox * mute2 = new LedCheckBox( "M", this, tr( "Band 2 Mute" ), LedCheckBox::Red ); + mute2->move( 51, 158 ); + mute2->setModel( & controls->m_mute2 ); + ToolTip::add( mute2, tr( "Mute Band 2" ) ); + + LedCheckBox * mute3 = new LedCheckBox( "M", this, tr( "Band 3 Mute" ), LedCheckBox::Red ); + mute3->move( 91, 158 ); + mute3->setModel( & controls->m_mute3 ); + ToolTip::add( mute3, tr( "Mute Band 3" ) ); + + LedCheckBox * mute4 = new LedCheckBox( "M", this, tr( "Band 4 Mute" ), LedCheckBox::Red ); + mute4->move( 131, 158 ); + mute4->setModel( & controls->m_mute4 ); + ToolTip::add( mute4, tr( "Mute Band 4" ) ); +} diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.h b/plugins/CrossoverEQ/CrossoverEQControlDialog.h new file mode 100644 index 000000000..08c678886 --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.h @@ -0,0 +1,50 @@ +/* + * CrossoverEQControlDialog.h - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef CROSSOVEREQ_CONTROL_DIALOG_H +#define CROSSOVEREQ_CONTROL_DIALOG_H + +#include +#include "EffectControlDialog.h" + +class CrossoverEQControls; + +class CrossoverEQControlDialog : public EffectControlDialog +{ + Q_OBJECT +public: + CrossoverEQControlDialog( CrossoverEQControls * controls ); + virtual ~CrossoverEQControlDialog() + { + } + +private: + QPixmap m_fader_bg; + QPixmap m_fader_empty; + QPixmap m_fader_knob; +}; + +#endif diff --git a/plugins/CrossoverEQ/CrossoverEQControls.cpp b/plugins/CrossoverEQ/CrossoverEQControls.cpp new file mode 100644 index 000000000..9c58eabff --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControls.cpp @@ -0,0 +1,116 @@ +/* + * CrossoverEQControls.cpp - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "CrossoverEQControls.h" +#include "CrossoverEQ.h" + +CrossoverEQControls::CrossoverEQControls( CrossoverEQEffect * eff ) : + EffectControls( eff ), + m_effect( eff ), + m_xover12( 125.f, 50.f, 10000.f, 1.0f, this, "Band 1/2 Crossover" ), + m_xover23( 1250.f, 50.f, 20000.f, 1.0f, this, "Band 2/3 Crossover" ), + m_xover34( 5000.f, 50.f, 20000.f, 1.0f, this, "Band 3/4 Crossover" ), + m_gain1( 0.f, -60.f, 30.f, 0.1f, this, "Band 1 Gain" ), + m_gain2( 0.f, -60.f, 30.f, 0.1f, this, "Band 2 Gain" ), + m_gain3( 0.f, -60.f, 30.f, 0.1f, this, "Band 3 Gain" ), + m_gain4( 0.f, -60.f, 30.f, 0.1f, this, "Band 4 Gain" ), + m_mute1( false, this, "Mute Band 1" ), + m_mute2( false, this, "Mute Band 2" ), + m_mute3( false, this, "Mute Band 3" ), + m_mute4( false, this, "Mute Band 4" ) +{ + connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) ); + connect( &m_xover12, SIGNAL( dataChanged() ), this, SLOT( xover12Changed() ) ); + connect( &m_xover23, SIGNAL( dataChanged() ), this, SLOT( xover23Changed() ) ); + connect( &m_xover34, SIGNAL( dataChanged() ), this, SLOT( xover34Changed() ) ); + + m_xover12.setScaleLogarithmic( true ); + m_xover23.setScaleLogarithmic( true ); + m_xover34.setScaleLogarithmic( true ); +} + +void CrossoverEQControls::saveSettings( QDomDocument & doc, QDomElement & elem ) +{ + m_xover12.saveSettings( doc, elem, "xover12" ); + m_xover23.saveSettings( doc, elem, "xover23" ); + m_xover34.saveSettings( doc, elem, "xover34" ); + + m_gain1.saveSettings( doc, elem, "gain1" ); + m_gain2.saveSettings( doc, elem, "gain2" ); + m_gain3.saveSettings( doc, elem, "gain3" ); + m_gain4.saveSettings( doc, elem, "gain4" ); + + m_mute1.saveSettings( doc, elem, "mute1" ); + m_mute2.saveSettings( doc, elem, "mute2" ); + m_mute3.saveSettings( doc, elem, "mute3" ); + m_mute4.saveSettings( doc, elem, "mute4" ); +} + +void CrossoverEQControls::loadSettings( const QDomElement & elem ) +{ + m_xover12.loadSettings( elem, "xover12" ); + m_xover23.loadSettings( elem, "xover23" ); + m_xover34.loadSettings( elem, "xover34" ); + + m_gain1.loadSettings( elem, "gain1" ); + m_gain2.loadSettings( elem, "gain2" ); + m_gain3.loadSettings( elem, "gain3" ); + m_gain4.loadSettings( elem, "gain4" ); + + m_mute1.loadSettings( elem, "mute1" ); + m_mute2.loadSettings( elem, "mute2" ); + m_mute3.loadSettings( elem, "mute3" ); + m_mute4.loadSettings( elem, "mute4" ); + + m_effect->m_needsUpdate = true; +} + +void CrossoverEQControls::xover12Changed() +{ + float v = m_xover12.value(); + if( m_xover23.value() < v ) { m_xover23.setValue( v ); } + if( m_xover34.value() < v ) { m_xover34.setValue( v ); } +} + +void CrossoverEQControls::xover23Changed() +{ + float v = m_xover23.value(); + if( m_xover12.value() > v ) { m_xover12.setValue( v ); } + if( m_xover34.value() < v ) { m_xover34.setValue( v ); } +} + +void CrossoverEQControls::xover34Changed() +{ + float v = m_xover34.value(); + if( m_xover12.value() > v ) { m_xover12.setValue( v ); } + if( m_xover23.value() > v ) { m_xover23.setValue( v ); } +} + + +void CrossoverEQControls::sampleRateChanged() +{ + m_effect->sampleRateChanged(); +} diff --git a/plugins/CrossoverEQ/CrossoverEQControls.h b/plugins/CrossoverEQ/CrossoverEQControls.h new file mode 100644 index 000000000..18e87baee --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControls.h @@ -0,0 +1,86 @@ +/* + * CrossoverEQControls.h - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef CROSSOVEREQ_CONTROLS_H +#define CROSSOVEREQ_CONTROLS_H + +#include "EffectControls.h" +#include "CrossoverEQControlDialog.h" + +class CrossoverEQEffect; + +class CrossoverEQControls : public EffectControls +{ + Q_OBJECT +public: + CrossoverEQControls( CrossoverEQEffect * eff ); + virtual ~CrossoverEQControls() {} + + virtual void saveSettings( QDomDocument & doc, QDomElement & elem ); + virtual void loadSettings( const QDomElement & elem ); + inline virtual QString nodeName() const + { + return( "crossoevereqcontrols" ); + } + + virtual int controlCount() + { + return( 11 ); + } + + virtual EffectControlDialog * createView() + { + return( new CrossoverEQControlDialog( this ) ); + } + +private slots: + void xover12Changed(); + void xover23Changed(); + void xover34Changed(); + void sampleRateChanged(); + +private: + CrossoverEQEffect * m_effect; + + FloatModel m_xover12; + FloatModel m_xover23; + FloatModel m_xover34; + + FloatModel m_gain1; + FloatModel m_gain2; + FloatModel m_gain3; + FloatModel m_gain4; + + BoolModel m_mute1; + BoolModel m_mute2; + BoolModel m_mute3; + BoolModel m_mute4; + + friend class CrossoverEQControlDialog; + friend class CrossoverEQEffect; +}; + +#endif diff --git a/plugins/CrossoverEQ/artwork.png b/plugins/CrossoverEQ/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..5510d2b3c3edc757483f2b21a7c3eb957ab2e448 GIT binary patch literal 52627 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 literal 0 HcmV?d00001 diff --git a/plugins/CrossoverEQ/fader_bg.png b/plugins/CrossoverEQ/fader_bg.png new file mode 100644 index 0000000000000000000000000000000000000000..abe28110594016ba54bfbedbbbf0e4083256641b GIT binary patch literal 234 zcmeAS@N?(olHy`uVBq!ia0vp^3P7B~!3HFsZ#L=!Qk(@Ik;M!Q+`=Ht$S`Y;1W=H@ z#M9T6{T>gWn53Y_73rryA;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zq7+XT$B>MBZ*Lj$GAQu49&B6iD1XOc(=dUHjT7z&Ux=~Z?)7V@iuK8NXJ+^wex+v^ m7cU6ZMI>18JG{p0*Iw>mJ7(n&=9lw8+B{wTT-G@yGywn+8#>?s literal 0 HcmV?d00001 diff --git a/plugins/CrossoverEQ/fader_empty.png b/plugins/CrossoverEQ/fader_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..4a95f05aa4ccd260e734b6b792da3853129b606a GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^3P7B~!2~2TE-t+Zq&N#aB8wRqxP?KOkzv*x37{Z* ziKnkC`#l~$UM+)$rAksjA;}Wgh!W@g+}zZ>5(ej@)Wnk16ovB4k_-iRPv3y>Mm}+% zA_q?w$B>MBZ_gSEGB9wkD40I{?w`yh&d+th^VOv*hK&vi0vs$%xTqh}I+ceQcii4m R@Cj%ZgQu&X%Q~loCID9JGCcqQ literal 0 HcmV?d00001 diff --git a/plugins/CrossoverEQ/fader_knob2.png b/plugins/CrossoverEQ/fader_knob2.png new file mode 100644 index 0000000000000000000000000000000000000000..252b485ee8784472c5629cb49f36e94b43e31a04 GIT binary patch literal 783 zcmV+q1MvKbP)E=Z010qNS#tmY3ljhU3ljkVnw%H_000McNliru-V6>6DjHiJ>&yTE02y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00MVOL_t(Y$HkR9P8>lHhQI2W zft7Z6tPtT!BwT?rZ~;r)0cWIKfE*uyjV#$InGaxDTmXrP4bTQASh0(jp{pG9bkAeg zscEUzkC~~zs{X%f;Dd+6&15pU9t;L&d7dAL2qJ*6xm@DCQWQl34XXgnIgVh|pci#qRRf@!_MR(? z#e$ukozwAne3ApW%pH_v*?@{5&TY&>d-S6>Rn%-9*lXupmK9G=PbF}aM`NSW2Op6v|uXN(*(ylSlrUCWU7p6PT7u$==`RmH=@1Ev}PGlz)7)sZ)Jg!{hH z2FOtLNxYjWFjlJ--Uo5#bvWdC&hzs#W`;ONCLs%DStebCSGAReT9HAO^*-Er?-6m# z=kvsUaM0M=Dw2&nny9LZ;c(cCw&A|5@^}WrEV+;>!_3IC3{_=!cQ=({(1%i0Ro4Lo z?(Xi^fThKKBSJ}$`kL>K5Ia>6MA@OhD~fS^eB7o0_V@Se`VT_f$N^nS3p%&%x1Hnz$%4wd3pJ5G#Z^2MNx`Ktztn{dlU#rqda4C zOSY(y*1h*0Rb@7t{kXloodEY8V#nk032+2#|A!yp{d7A0_0fN0{sIMGiQ&E)@hSiS N002ovPDHLkV1oZ)VzdAN literal 0 HcmV?d00001 diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 1af7bb040..0f0bef69d 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -170,7 +170,7 @@ void Fader::mouseMoveEvent( QMouseEvent *mouseEvent ) { int dy = m_moveStartPoint - mouseEvent->globalY(); - float delta = dy * ( m_model->maxValue() - m_model->minValue() ) / (float) ( height() - ( *s_knob ).height() ); + float delta = dy * ( m_model->maxValue() - m_model->minValue() ) / (float) ( height() - ( *m_knob ).height() ); model()->setValue( m_startValue + delta ); @@ -186,7 +186,7 @@ void Fader::mousePressEvent( QMouseEvent* mouseEvent ) if( mouseEvent->button() == Qt::LeftButton && ! ( mouseEvent->modifiers() & Qt::ControlModifier ) ) { - if( mouseEvent->y() >= knobPosY() - ( *s_knob ).height() && mouseEvent->y() < knobPosY() ) + if( mouseEvent->y() >= knobPosY() - ( *m_knob ).height() && mouseEvent->y() < knobPosY() ) { updateTextFloat(); s_textFloat->show(); @@ -346,13 +346,16 @@ void Fader::paintEvent( QPaintEvent * ev) // background painter.drawPixmap( ev->rect(), *m_back, ev->rect() ); - // peak leds //float fRange = abs( m_fMaxPeak ) + abs( m_fMinPeak ); + int height = m_back->height(); + int width = m_back->width() / 2; + int center = m_back->width() - width; + int peak_L = calculateDisplayPeak( m_fPeakValue_L - m_fMinPeak ); int persistentPeak_L = qMax( 3, calculateDisplayPeak( m_persistentPeak_L - m_fMinPeak ) ); - painter.drawPixmap( QRect( 0, peak_L, 11, 116 - peak_L ), *m_leds, QRect( 0, peak_L, 11, 116 - peak_L ) ); + painter.drawPixmap( QRect( 0, peak_L, width, height - peak_L ), *m_leds, QRect( 0, peak_L, width, height - peak_L ) ); if( m_persistentPeak_L > 0.05 ) { @@ -363,7 +366,7 @@ void Fader::paintEvent( QPaintEvent * ev) int peak_R = calculateDisplayPeak( m_fPeakValue_R - m_fMinPeak ); int persistentPeak_R = qMax( 3, calculateDisplayPeak( m_persistentPeak_R - m_fMinPeak ) ); - painter.drawPixmap( QRect( 11, peak_R, 11, 116 - peak_R ), *m_leds, QRect( 11, peak_R, 11, 116 - peak_R ) ); + painter.drawPixmap( QRect( center, peak_R, width, height - peak_R ), *m_leds, QRect( center, peak_R, width, height - peak_R ) ); if( m_persistentPeak_R > 0.05 ) { @@ -373,7 +376,7 @@ void Fader::paintEvent( QPaintEvent * ev) } // knob - painter.drawPixmap( 0, knobPosY() - ( *m_knob ).height(), *s_knob ); + painter.drawPixmap( 0, knobPosY() - m_knob->height(), *m_knob ); } From f357bc7291a3e5e40dcb35a947d9c78b0b84de07 Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 15 Dec 2014 15:54:05 +0000 Subject: [PATCH 09/19] Proposed fix for #1411 Crash on LB302 preset preview . --- plugins/lb302/lb302.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 2f02dffc3..dfc54e005 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -780,10 +780,12 @@ void lb302Synth::processNote( NotePlayHandle * _n ) void lb302Synth::play( sampleFrame * _working_buffer ) { + m_notesMutex.lock(); while( ! m_notes.isEmpty() ) { processNote( m_notes.takeFirst() ); }; + m_notesMutex.unlock(); const fpp_t frames = engine::mixer()->framesPerPeriod(); From f27ea7bc2b97b58b0165e03df7c28c10ab2296c9 Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 15 Dec 2014 21:40:58 +0000 Subject: [PATCH 10/19] Proposed fix for #1432 LB302 preset preview audio cut-off --- plugins/lb302/lb302.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 2f02dffc3..e3e2628c6 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -789,7 +789,7 @@ void lb302Synth::play( sampleFrame * _working_buffer ) process( _working_buffer, frames ); instrumentTrack()->processAudioBuffer( _working_buffer, frames, NULL ); - release_frame = 0; +// release_frame = 0; //removed for issue # 1432 } From 8d3637e754f734235c2163860d6a482da6c5f96f Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 15 Dec 2014 15:54:05 +0000 Subject: [PATCH 11/19] Proposed fix for #1411 Crash on LB302 preset preview . --- plugins/lb302/lb302.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 2f02dffc3..dfc54e005 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -780,10 +780,12 @@ void lb302Synth::processNote( NotePlayHandle * _n ) void lb302Synth::play( sampleFrame * _working_buffer ) { + m_notesMutex.lock(); while( ! m_notes.isEmpty() ) { processNote( m_notes.takeFirst() ); }; + m_notesMutex.unlock(); const fpp_t frames = engine::mixer()->framesPerPeriod(); From 8b2ce06da8c255b0ef8075717182f3aab3cee9a7 Mon Sep 17 00:00:00 2001 From: Dave French Date: Tue, 16 Dec 2014 16:41:08 +0000 Subject: [PATCH 12/19] Proposed fix for 1450 Mem leak in sample-track --- src/core/SampleBuffer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 2db7e26fa..6941c6da9 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -942,6 +942,7 @@ void SampleBuffer::visualize( QPainter & _p, const QRect & _dr, _p.drawPolyline( l, nb_frames / fpp ); _p.drawPolyline( r, nb_frames / fpp ); delete[] l; + delete[] r; } From 91063ab7d2eeaee29545fafdc989f666ab23642d Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 16 Dec 2014 19:40:02 +0000 Subject: [PATCH 13/19] Update Carla plugin to latest API --- plugins/carlabase/carla.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index ad3d92019..db14178a2 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -251,16 +251,16 @@ intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opco switch (opcode) { - case HOST_OPCODE_NULL: + case NATIVE_HOST_OPCODE_NULL: break; - case HOST_OPCODE_UPDATE_PARAMETER: - case HOST_OPCODE_UPDATE_MIDI_PROGRAM: - case HOST_OPCODE_RELOAD_PARAMETERS: - case HOST_OPCODE_RELOAD_MIDI_PROGRAMS: - case HOST_OPCODE_RELOAD_ALL: + case NATIVE_HOST_OPCODE_UPDATE_PARAMETER: + case NATIVE_HOST_OPCODE_UPDATE_MIDI_PROGRAM: + case NATIVE_HOST_OPCODE_RELOAD_PARAMETERS: + case NATIVE_HOST_OPCODE_RELOAD_MIDI_PROGRAMS: + case NATIVE_HOST_OPCODE_RELOAD_ALL: // nothing break; - case HOST_OPCODE_UI_UNAVAILABLE: + case NATIVE_HOST_OPCODE_UI_UNAVAILABLE: handleUiClosed(); break; } @@ -459,7 +459,7 @@ PluginView* CarlaInstrument::instantiateView(QWidget* parent) void CarlaInstrument::sampleRateChanged() { - fDescriptor->dispatcher(fHandle, PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, handleGetSampleRate()); + fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, handleGetSampleRate()); } // ------------------------------------------------------------------- From f2ab783db9443f3cba62867db29aa9fdcacf2a09 Mon Sep 17 00:00:00 2001 From: falkTX Date: Tue, 16 Dec 2014 19:40:31 +0000 Subject: [PATCH 14/19] Fix build when using old linux systems --- src/gui/PianoRoll.cpp | 5 +++++ src/gui/plugin_browser.cpp | 2 ++ 2 files changed, 7 insertions(+) diff --git a/src/gui/PianoRoll.cpp b/src/gui/PianoRoll.cpp index 2b454b381..266a1415a 100644 --- a/src/gui/PianoRoll.cpp +++ b/src/gui/PianoRoll.cpp @@ -70,6 +70,11 @@ #include "text_float.h" +#if QT_VERSION < 0x040800 +#define MiddleButton MidButton +#endif + + typedef AutomationPattern::timeMap timeMap; diff --git a/src/gui/plugin_browser.cpp b/src/gui/plugin_browser.cpp index 9827ce2e7..95ec8b42a 100644 --- a/src/gui/plugin_browser.cpp +++ b/src/gui/plugin_browser.cpp @@ -28,6 +28,8 @@ #include #include +#include // for std::sort + #include "plugin_browser.h" #include "embed.h" #include "debug.h" From f65ec076034f8ce6b96e39832655242e96f9404b Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Tue, 16 Dec 2014 15:32:20 -0500 Subject: [PATCH 15/19] Bump version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fbbd979a..e30f417b8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ INCLUDE(FindPkgConfig) SET(VERSION_MAJOR "1") SET(VERSION_MINOR "0") -SET(VERSION_PATCH "99") +SET(VERSION_PATCH "100") #SET(VERSION_SUFFIX "") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") IF(VERSION_SUFFIX) From da6fd6ef5cd72d98a29cd83cac9d0b2dcb69433a Mon Sep 17 00:00:00 2001 From: Vesa V Date: Tue, 16 Dec 2014 23:00:14 +0200 Subject: [PATCH 16/19] Update opl2instrument.cpp --- plugins/opl2/opl2instrument.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index bb64c901b..88166ab39 100644 --- a/plugins/opl2/opl2instrument.cpp +++ b/plugins/opl2/opl2instrument.cpp @@ -497,7 +497,7 @@ void opl2instrument::loadPatch(unsigned char inst[14]) { void opl2instrument::tuneEqual(int center, float Hz) { float tmp; for(int n=0; n<128; ++n) { - tmp = Hz*pow( 2, ( n - center ) / 12.0 + pitchbend / 1200.0 ); + tmp = Hz*pow( 2.0, ( n - center ) * ( 1.0 / 12.0 ) + pitchbend * ( 1.0 / 1200.0 ) ); fnums[n] = Hz2fnum( tmp ); } } @@ -505,7 +505,7 @@ void opl2instrument::tuneEqual(int center, float Hz) { // Find suitable F number in lowest possible block int opl2instrument::Hz2fnum(float Hz) { for(int block=0; block<8; ++block) { - unsigned int fnum = Hz * pow(2, 20-block) / 49716; + unsigned int fnum = Hz * pow( 2.0, 20.0 - (double)block ) * ( 1.0 / 49716.0 ); if(fnum<1023) { return fnum + (block << 10); } From 347b5a121dc369757a32dbdd46e0bc6ea48985f8 Mon Sep 17 00:00:00 2001 From: Vesa V Date: Tue, 16 Dec 2014 23:02:00 +0200 Subject: [PATCH 17/19] Update papu_instrument.cpp --- plugins/papu/papu_instrument.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/papu/papu_instrument.cpp b/plugins/papu/papu_instrument.cpp index 0b45eacad..8a3d387c9 100644 --- a/plugins/papu/papu_instrument.cpp +++ b/plugins/papu/papu_instrument.cpp @@ -360,11 +360,11 @@ void papuInstrument::playNote( NotePlayHandle * _n, //PRNG Frequency = (1048576 Hz / (ratio + 1)) / 2 ^ (shiftclockfreq + 1) char sopt=0; char ropt=1; - float fopt = 524288.0 / ( ropt * pow( 2, sopt+1 ) ); + float fopt = 524288.0 / ( ropt * pow( 2.0, sopt + 1.0 ) ); float f; for ( char s=0; s<16; s++ ) for ( char r=0; r<8; r++ ) { - f = 524288.0 / ( r * pow( 2, s+1 ) ); + f = 524288.0 / ( r * pow( 2.0, s + 1.0 ) ); if( fabs( freq-fopt ) > fabs( freq-f ) ) { fopt = f; ropt = r; From d21f0a7114b8afccf2acb2f726b2e1df790b8ee5 Mon Sep 17 00:00:00 2001 From: Daniel Winzen Date: Mon, 15 Dec 2014 21:08:16 +0100 Subject: [PATCH 18/19] Remove RackView widget before deleting the ChannelView Fixes the following two errors I spotted using valgrind: When deleting a channel; ==936== Invalid read of size 8 ==936== at 0x56FA1D: FxMixerView::deleteChannel(int) (FxMixerView.cpp:374) ==936== by 0x60E9A79: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.6) ==936== by 0x5216BF1: QAction::triggered(bool) (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.6) ==936== by 0x52185C2: QAction::activate(QAction::ActionEvent) (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.6) ==936== Address 0x14d51b90 is 32 bytes inside a block of size 40 free'd ==936== at 0x4C2C2E0: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==936== by 0x56F9ED: FxMixerView::deleteChannel(int) (FxMixerView.cpp:370) ==936== by 0x60E9A79: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.6) ==936== by 0x5216BF1: QAction::triggered(bool) (in /usr/lib/x86_64-linux-gnu/libQtGui.so.4.8.6) When loading a new project after adding some channels: ==936== Invalid read of size 8 ==936== at 0x570785: FxMixerView::refreshDisplay() (FxMixerView.cpp:202) ==936== by 0x4B590E: Song::clearProject() (Song.cpp:740) ==936== by 0x4B7885: Song::createNewProject() (Song.cpp:817) ==936== by 0x60E9A79: QMetaObject::activate(QObject*, QMetaObject const*, int, void**) (in /usr/lib/x86_64-linux-gnu/libQtCore.so.4.8.6) ==936== Address 0x56a12ab0 is 32 bytes inside a block of size 40 free'd ==936== at 0x4C2C2E0: operator delete(void*) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==936== by 0x57075B: FxMixerView::refreshDisplay() (FxMixerView.cpp:201) ==936== by 0x4B590E: Song::clearProject() (Song.cpp:740) ==936== by 0x4B7885: Song::createNewProject() (Song.cpp:817) --- src/gui/FxMixerView.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 8948e8474..f750bbc76 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -194,11 +194,12 @@ void FxMixerView::refreshDisplay() for( int i = 1; iremoveWidget(m_fxChannelViews[i]->m_fxLine); + m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView ); delete m_fxChannelViews[i]->m_fader; delete m_fxChannelViews[i]->m_muteBtn; delete m_fxChannelViews[i]->m_fxLine; + delete m_fxChannelViews[i]->m_rackView; delete m_fxChannelViews[i]; - m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView ); } m_channelAreaWidget->adjustSize(); @@ -343,15 +344,14 @@ void FxMixerView::deleteChannel(int index) // delete the view chLayout->removeWidget(m_fxChannelViews[index]->m_fxLine); + m_racksLayout->removeWidget( m_fxChannelViews[index]->m_rackView ); delete m_fxChannelViews[index]->m_fader; delete m_fxChannelViews[index]->m_muteBtn; delete m_fxChannelViews[index]->m_fxLine; + delete m_fxChannelViews[index]->m_rackView; delete m_fxChannelViews[index]; m_channelAreaWidget->adjustSize(); - // delete the fx rack - m_racksLayout->removeWidget( m_fxChannelViews[index]->m_rackView ); - // make sure every channel knows what index it is for(int i=0; i Date: Tue, 16 Dec 2014 23:49:39 +0200 Subject: [PATCH 19/19] LR filter -> direct form 2 --- include/BasicFilters.h | 28 ++++------------------------ 1 file changed, 4 insertions(+), 24 deletions(-) diff --git a/include/BasicFilters.h b/include/BasicFilters.h index eda8d7b14..e6dfbcbe3 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -64,7 +64,6 @@ public: for( int i = 0; i < CHANNELS; ++i ) { m_z1[i] = m_z2[i] = m_z3[i] = m_z4[i] = 0.0f; - m_y1[i] = m_y2[i] = m_y3[i] = m_y4[i] = 0.0f; } } @@ -119,34 +118,16 @@ public: inline float update( float in, ch_cnt_t ch ) { - const double y = m_a0 * in + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + - ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 ) - - ( m_y1[ch] * m_b1 ) - ( m_y2[ch] * m_b2 ) - - ( m_y3[ch] * m_b3 ) - ( m_y4[ch] * m_b4 ); - - m_z4[ch] = m_z3[ch]; - m_z3[ch] = m_z2[ch]; - m_z2[ch] = m_z1[ch]; - m_z1[ch] = in; - - m_y4[ch] = m_y3[ch]; - m_y3[ch] = m_y2[ch]; - m_y2[ch] = m_y1[ch]; - m_y1[ch] = y; - - return y; - -// for some reason converting to direct form 2 doesn't seem to work for this filter -/* const double x = in - ( m_z1[ch] * m_b1 ) - ( m_z2[ch] * m_b2 ) - + const double x = in - ( m_z1[ch] * m_b1 ) - ( m_z2[ch] * m_b2 ) - ( m_z3[ch] * m_b3 ) - ( m_z4[ch] * m_b4 ); - + const double y = ( m_a0 * x ) + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + + ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 ); m_z4[ch] = m_z3[ch]; m_z3[ch] = m_z2[ch]; m_z2[ch] = m_z1[ch]; m_z1[ch] = x; - return ( m_a0 * x ) + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + - ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 );*/ + return y; } private: @@ -158,7 +139,6 @@ private: typedef double frame[CHANNELS]; frame m_z1, m_z2, m_z3, m_z4; - frame m_y1, m_y2, m_y3, m_y4; }; typedef LinkwitzRiley<2> StereoLinkwitzRiley;