diff --git a/CMakeLists.txt b/CMakeLists.txt index 696a1e341ef..4e2109d6815 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) 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 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/include/BasicFilters.h b/include/BasicFilters.h index e09d40c425f..e6dfbcbe3c3 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -75,68 +75,69 @@ 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; - - 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 ); + 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 out; + return y; } 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; }; typedef LinkwitzRiley<2> StereoLinkwitzRiley; diff --git a/include/Fader.h b/include/Fader.h index 96c3ae732f6..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: @@ -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 00000000000..5510d2b3c3e Binary files /dev/null and b/plugins/CrossoverEQ/artwork.png differ diff --git a/plugins/CrossoverEQ/fader_bg.png b/plugins/CrossoverEQ/fader_bg.png new file mode 100644 index 00000000000..abe28110594 Binary files /dev/null and b/plugins/CrossoverEQ/fader_bg.png differ diff --git a/plugins/CrossoverEQ/fader_empty.png b/plugins/CrossoverEQ/fader_empty.png new file mode 100644 index 00000000000..4a95f05aa4c Binary files /dev/null and b/plugins/CrossoverEQ/fader_empty.png differ diff --git a/plugins/CrossoverEQ/fader_knob2.png b/plugins/CrossoverEQ/fader_knob2.png new file mode 100644 index 00000000000..252b485ee87 Binary files /dev/null and b/plugins/CrossoverEQ/fader_knob2.png differ diff --git a/plugins/DualFilter/DualFilterControls.cpp b/plugins/DualFilter/DualFilterControls.cpp index c7f2ba48a0e..b25b8953cbf 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" ) ) { diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index ca2ebab1376..0831bba50b0 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()); } // ------------------------------------------------------------------- diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 8e8785d6e4c..14ecb8fa16f 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -788,16 +788,18 @@ 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(); process( _working_buffer, frames ); instrumentTrack()->processAudioBuffer( _working_buffer, frames, NULL ); - release_frame = 0; +// release_frame = 0; //removed for issue # 1432 } diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index 2a8867c661b..d1613a1aa8c 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); } diff --git a/plugins/papu/papu_instrument.cpp b/plugins/papu/papu_instrument.cpp index 9177d72830a..8e011816444 100644 --- a/plugins/papu/papu_instrument.cpp +++ b/plugins/papu/papu_instrument.cpp @@ -361,11 +361,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; diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 9c1634f4210..c556870c343 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -290,7 +290,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() ) diff --git a/src/core/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 9b7b5fa006c..01778653f98 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -957,6 +957,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; } 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(); diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 90de3d00077..aefdcf2742e 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -194,12 +194,13 @@ 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_soloBtn; 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(); @@ -363,16 +364,15 @@ 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_soloBtn; 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 #include "PluginBrowser.h" +#include // for std::sort + #include "embed.h" #include "debug.h" #include "templates.h" 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 ); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index b32414a3851..06219b94216 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -87,7 +87,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;