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/18] 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 b4e1daba350..27259ce25cc 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 00000000000..d4d1ad0db3b --- /dev/null +++ b/data/presets/Kicker/SnareMarch.xpf @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + From a8924a34dd08b75d6020f5aea78242531120455e Mon Sep 17 00:00:00 2001 From: tresf Date: Sat, 13 Dec 2014 11:27:14 -0500 Subject: [PATCH 02/18] 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 411de79d5ae..93f286345bb 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 03/18] 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 bc9728e368e..20b73744497 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 04/18] 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 344a34ca048..df295c39fc2 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 05/18] 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 e09d40c425f..eda8d7b1450 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -64,6 +64,7 @@ class LinkwitzRiley 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 @@ class LinkwitzRiley 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 96c3ae732f6..9e8f034ea5a 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -103,7 +103,7 @@ class Fader : public QWidget, public FloatModelView 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 4df760ecd50..cf7a5a94ec3 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 00000000000..fbc8407d978 --- /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 00000000000..a50b6381f39 --- /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 00000000000..36b3a6bc555 --- /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 00000000000..8a9eecf4df8 --- /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 00000000000..08c67888646 --- /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 00000000000..9c58eabfffc --- /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 00000000000..18e87baeef5 --- /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 1af7bb04056..0f0bef69d2b 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 06/18] 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 2f02dffc352..dfc54e0058a 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 07/18] 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 2f02dffc352..e3e2628c6c8 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 08/18] 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 2f02dffc352..dfc54e0058a 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 09/18] 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 2db7e26fa4b..6941c6da9cb 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 10/18] 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 ad3d9201954..db14178a218 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 11/18] 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 2b454b3812d..266a1415aed 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 9827ce2e7f4..95ec8b42ad6 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 12/18] Bump version --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5fbbd979abf..e30f417b80e 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 13/18] 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 bb64c901b24..88166ab39b7 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 14/18] 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 0b45eacad5f..8a3d387c90b 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 15/18] 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 8948e847441..f750bbc76ab 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 16/18] 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 eda8d7b1450..e6dfbcbe3c3 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -64,7 +64,6 @@ class LinkwitzRiley 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 @@ class LinkwitzRiley 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 @@ class LinkwitzRiley 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; From 43ad2d52bd69044c512563ccdcee9a8fe766c313 Mon Sep 17 00:00:00 2001 From: Dave French Date: Wed, 17 Dec 2014 01:32:10 +0000 Subject: [PATCH 17/18] added EXPORT to Fader to allow use in plugins in windows builds --- include/Fader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Fader.h b/include/Fader.h index 9e8f034ea5a..3e808802a48 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -57,7 +57,7 @@ class TextFloat; -class Fader : public QWidget, public FloatModelView +class EXPORT Fader : public QWidget, public FloatModelView { Q_OBJECT public: From 5f5d405552962d1af10b24f8767a0a74ec6b3f4f Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 20 Dec 2014 22:33:29 +0000 Subject: [PATCH 18/18] Render from start of track. --- src/core/Song.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/core/Song.cpp b/src/core/Song.cpp index c9ac564522f..2dd9c549bee 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -619,6 +619,7 @@ void Song::stop() void Song::startExport() { stop(); + m_playPos[Mode_PlaySong].setTicks( 0 ); playSong();