From 1595c2728e4652d75f1ffc512cd0e227bbb68f74 Mon Sep 17 00:00:00 2001 From: dave Date: Wed, 10 Dec 2014 08:44:25 +0000 Subject: [PATCH 01/15] re initial, no working fft, faders, checkboxes --- plugins/CMakeLists.txt | 1 + plugins/eq/CMakeLists.txt | 3 + plugins/eq/artwork.png | Bin 0 -> 50584 bytes plugins/eq/eqcontrols.cpp | 201 ++++++++++++++++++++ plugins/eq/eqcontrols.h | 127 +++++++++++++ plugins/eq/eqcontrolsdialog.cpp | 164 ++++++++++++++++ plugins/eq/eqcontrolsdialog.h | 94 +++++++++ plugins/eq/eqeffect.cpp | 314 ++++++++++++++++++++++++++++++ plugins/eq/eqeffect.h | 80 ++++++++ plugins/eq/eqfader.cpp | 0 plugins/eq/eqfader.h | 111 +++++++++++ plugins/eq/eqfilter.cpp | 5 + plugins/eq/eqfilter.h | 315 +++++++++++++++++++++++++++++++ plugins/eq/eqhighshelffileter.h | 1 + plugins/eq/eqhp12.h | 0 plugins/eq/eqlowshelffilter.h | 3 + plugins/eq/eqlp12.h | 0 plugins/eq/eqparameterwidget.cpp | 197 +++++++++++++++++++ plugins/eq/eqparameterwidget.h | 158 ++++++++++++++++ plugins/eq/eqpeekfilter.cpp | 3 + plugins/eq/eqpeekfilter.h | 3 + plugins/eq/eqspectrumview.h | 80 ++++++++ plugins/eq/logo.png | Bin 0 -> 3225 bytes 23 files changed, 1860 insertions(+) create mode 100644 plugins/eq/CMakeLists.txt create mode 100644 plugins/eq/artwork.png create mode 100644 plugins/eq/eqcontrols.cpp create mode 100644 plugins/eq/eqcontrols.h create mode 100644 plugins/eq/eqcontrolsdialog.cpp create mode 100644 plugins/eq/eqcontrolsdialog.h create mode 100644 plugins/eq/eqeffect.cpp create mode 100644 plugins/eq/eqeffect.h create mode 100644 plugins/eq/eqfader.cpp create mode 100644 plugins/eq/eqfader.h create mode 100644 plugins/eq/eqfilter.cpp create mode 100644 plugins/eq/eqfilter.h create mode 100644 plugins/eq/eqhighshelffileter.h create mode 100644 plugins/eq/eqhp12.h create mode 100644 plugins/eq/eqlowshelffilter.h create mode 100644 plugins/eq/eqlp12.h create mode 100644 plugins/eq/eqparameterwidget.cpp create mode 100644 plugins/eq/eqparameterwidget.h create mode 100644 plugins/eq/eqpeekfilter.cpp create mode 100644 plugins/eq/eqpeekfilter.h create mode 100644 plugins/eq/eqspectrumview.h create mode 100644 plugins/eq/logo.png diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4df760ecd..e08e37079 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -9,6 +9,7 @@ ADD_SUBDIRECTORY(carlarack) ADD_SUBDIRECTORY(delay) ADD_SUBDIRECTORY(DualFilter) ADD_SUBDIRECTORY(dynamics_processor) +ADD_SUBDIRECTORY(eq) ADD_SUBDIRECTORY(flanger) ADD_SUBDIRECTORY(flp_import) ADD_SUBDIRECTORY(HydrogenImport) diff --git a/plugins/eq/CMakeLists.txt b/plugins/eq/CMakeLists.txt new file mode 100644 index 000000000..72dc8bb5e --- /dev/null +++ b/plugins/eq/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/eq/artwork.png b/plugins/eq/artwork.png new file mode 100644 index 0000000000000000000000000000000000000000..8a4a0418b87ed04c2670276d2cd4b4e5c506439a GIT binary patch literal 50584 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
-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 literal 0 HcmV?d00001 diff --git a/plugins/eq/eqcontrols.cpp b/plugins/eq/eqcontrols.cpp new file mode 100644 index 000000000..efb89a245 --- /dev/null +++ b/plugins/eq/eqcontrols.cpp @@ -0,0 +1,201 @@ +/* + * eqcontrols.cpp - defination of EqControls class. + * + * Copyright (c) 2014 David French + * + * 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 "eqcontrols.h" +#include "eqeffect.h" + + + + +EqControls::EqControls( EqEffect *effect ) : + EffectControls( effect ), + m_effect( effect ), + m_inGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Input gain") ), + m_outGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Output gain" ) ), + m_lowShelfGainModel( 0.0 , -20, 20, 0.001, this, tr( "Low shelf gain" ) ), + m_para1GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 1 gain" ) ), + m_para2GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 2 gain" ) ), + m_para3GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 3 gain" ) ), + m_para4GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 4 gain" ) ), + m_highShelfGainModel( 0.0 , -20, 20, 0.001, this, tr( "High Shelf gain" ) ), + m_hpResModel( 0.707,0.003, 10.0 , 0.001, this, tr( "HP res" ) ), + m_lowShelfResModel( 1.4,0.0, 10.0 , 0.001, this , tr( "Low Shelf res" ) ), + m_para1ResModel( 1.4 ,0.55, 10.0 , 0.001, this , tr( "Peak 1 res" ) ), + m_para2ResModel( 1.4, 0.55, 10.0 , 0.001, this , tr( "Peak 2 res" ) ), + m_para3ResModel( 1.4, 0.55, 10.0 , 0.001, this , tr( "Peak 3 res" ) ), + m_para4ResModel( 1.4, 0.55, 10.0 , 0.001, this , tr( "Peak 4 res" ) ), + m_highShelfResModel( 1.4, 0.001, 10.0 , 0.001, this , tr( "High Shelf res" ) ), + m_lpResModel( 0.707,0.003, 10.0 , 0.001, this , tr( "LP res" ) ), + m_hpFeqModel( 31.0, 30.0, 20000, 0.001, this , tr( "HP freq" ) ), + m_lowShelfFreqModel( 80.0, 25.0, 20000, 0.001, this , tr( "Low Shelf freq" ) ), + m_para1FreqModel( 120.0, 27.0, 20000, 0.001, this , tr( "Peak 1 freq" ) ), + m_para2FreqModel( 250.0, 27.0, 20000, 0.001, this, tr( "Peak 2 freq" ) ), + m_para3FreqModel( 2000.0, 27.0, 20000, 0.001, this , tr( "Peak 3 freq" ) ), + m_para4FreqModel( 4000.0, 27.0, 20000, 0.001, this , tr( "Peak 4 freq" ) ), + m_highShelfFreqModel( 12000.0, 27.0, 20000, 0.001, this , tr( "High shelf freq" ) ), + m_lpFreqModel( 18000.0, 27.0, 20000, 0.001, this , tr( "LP freq" ) ), + m_hpActiveModel( false, this , tr( "HP active" ) ), + m_lowShelfActiveModel( false, this , tr( "Low shelf active" ) ), + m_para1ActiveModel(false, this , tr( "Peak 1 active" ) ), + m_para2ActiveModel( false, this , tr( "Peak 2 active" ) ), + m_para3ActiveModel( false, this , tr( "Peak 3 active" ) ), + m_para4ActiveModel( false, this , tr( "Peak 4 active" ) ), + m_highShelfActiveModel( false, this , tr( "High shelf active" ) ), + m_lpActiveModel( false, this , tr( "LP active" ) ), + m_lp12Model( true, this , tr( "LP 12" ) ), + m_lp24Model( false, this , tr( "LP 24" ) ), + m_lp48Model( false, this , tr( "LP 48" ) ), + m_hp12Model( true, this , tr( "HP 12" ) ), + m_hp24Model( false, this , tr( "HP 24" ) ), + m_hp48Model( false, this , tr( "HP 48" ) ), + m_analyzeModel( true, this , tr( "Analyze enable" ) ) + +{ + m_hpFeqModel.setScaleLogarithmic( true ); + m_lowShelfFreqModel.setScaleLogarithmic( true ); + m_para1FreqModel.setScaleLogarithmic( true ); + m_para2FreqModel.setScaleLogarithmic( true ); + m_para3FreqModel.setScaleLogarithmic( true ); + m_para4FreqModel.setScaleLogarithmic( true ); + m_highShelfFreqModel.setScaleLogarithmic( true ); + m_lpFreqModel.setScaleLogarithmic( true ); + + m_para1GainModel.setScaleLogarithmic( true ); + m_inPeakL = 0; + m_inPeakR = 0; + m_lowShelfPeakL = 0; m_lowShelfPeakR = 0; + m_para1PeakL = 0; m_para1PeakR = 0; + m_para2PeakL = 0; m_para2PeakR = 0; + m_para3PeakL = 0; m_para3PeakR = 0; + m_para4PeakL = 0; m_para4PeakR = 0; + m_highShelfPeakL = 0; m_highShelfPeakR = 0; + +} + + + + +void EqControls::loadSettings( const QDomElement &_this ) +{ + m_inGainModel.loadSettings( _this, "Inputgain" ); + m_outGainModel.loadSettings( _this, "Outputgain"); + m_lowShelfGainModel.loadSettings( _this , "Lowshelfgain" ); + m_para1GainModel.loadSettings( _this, "Peak1gain" ); + m_para2GainModel.loadSettings( _this, "Peak2gain" ); + m_para3GainModel.loadSettings( _this, "Peak3gain" ); + m_para4GainModel.loadSettings( _this, "Peak4gain" ); + m_highShelfGainModel.loadSettings( _this , "HighShelfgain"); + + m_hpResModel.loadSettings( _this ,"HPres"); + m_lowShelfResModel.loadSettings( _this, "LowShelfres" ); + m_para1ResModel.loadSettings( _this ,"Peak1res" ); + m_para2ResModel.loadSettings( _this ,"Peak2res" ); + m_para3ResModel.loadSettings( _this ,"Peak3res" ); + m_para4ResModel.loadSettings( _this ,"Peak4res" ); + m_highShelfResModel.loadSettings( _this, "HighShelfres" ); + m_lpResModel.loadSettings( _this, "LPres"); + + m_hpFeqModel.loadSettings( _this, "HPfreq" ); + m_lowShelfFreqModel.loadSettings( _this, "LowShelffreq" ); + m_para1FreqModel.loadSettings( _this, "Peak1freq" ); + m_para2FreqModel.loadSettings( _this, "Peak2freq" ); + m_para3FreqModel.loadSettings( _this, "Peak3freq" ); + m_para4FreqModel.loadSettings( _this, "Peak4freq" ); + m_highShelfFreqModel.loadSettings( _this, "Highshelffreq" ); + m_lpFreqModel.loadSettings( _this, "LPfreq" ); + + m_hpActiveModel.loadSettings( _this, "HPactive" ); + m_lowShelfActiveModel.loadSettings( _this, "Lowshelfactive" ); + m_para1ActiveModel.loadSettings( _this, "Peak1active"); + m_para2ActiveModel.loadSettings( _this, "Peak2active"); + m_para3ActiveModel.loadSettings( _this, "Peak3active"); + m_para4ActiveModel.loadSettings( _this, "Peak4active"); + m_highShelfActiveModel.loadSettings( _this, "Highshelfactive" ); + m_lpActiveModel.loadSettings( _this, "LPactive" ); + + m_lp12Model.loadSettings( _this , "LP12" ); + m_lp24Model.loadSettings( _this , "LP24" ); + m_lp48Model.loadSettings( _this , "LP48" ); + + m_hp12Model.loadSettings( _this , "HP12" ); + m_hp24Model.loadSettings( _this , "HP24" ); + m_hp48Model.loadSettings( _this , "HP48" ); + m_analyzeModel.loadSettings( _this, "Analyzeenable"); +} + + + + +void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) +{ + + m_inGainModel.saveSettings( doc, parent, "Inputgain" ); + m_outGainModel.saveSettings( doc, parent, "Outputgain"); + m_lowShelfGainModel.saveSettings( doc, parent , "Lowshelfgain" ); + m_para1GainModel.saveSettings( doc, parent, "Peak1gain" ); + m_para2GainModel.saveSettings( doc, parent, "Peak2gain" ); + m_para3GainModel.saveSettings( doc, parent, "Peak3gain" ); + m_para4GainModel.saveSettings( doc, parent, "Peak4gain" ); + m_highShelfGainModel.saveSettings( doc, parent, "HighShelfgain"); + + m_hpResModel.saveSettings( doc, parent ,"HPres"); + m_lowShelfResModel.saveSettings( doc, parent, "LowShelfres" ); + m_para1ResModel.saveSettings( doc, parent,"Peak1res" ); + m_para2ResModel.saveSettings( doc, parent,"Peak2res" ); + m_para3ResModel.saveSettings( doc, parent,"Peak3res" ); + m_para4ResModel.saveSettings( doc, parent,"Peak4res" ); + m_highShelfResModel.saveSettings( doc, parent, "HighShelfres" ); + m_lpResModel.saveSettings( doc, parent, "LPres"); + + m_hpFeqModel.saveSettings( doc, parent, "HPfreq" ); + m_lowShelfFreqModel.saveSettings( doc, parent, "LowShelffreq" ); + m_para1FreqModel.saveSettings( doc, parent, "Peak1freq" ); + m_para2FreqModel.saveSettings( doc, parent, "Peak2freq" ); + m_para3FreqModel.saveSettings( doc, parent, "Peak3freq" ); + m_para4FreqModel.saveSettings( doc, parent, "Peak4freq" ); + m_highShelfFreqModel.saveSettings( doc, parent, "Highshelffreq" ); + m_lpFreqModel.saveSettings( doc, parent, "LPfreq" ); + + m_hpActiveModel.saveSettings( doc, parent, "HPactive" ); + m_lowShelfActiveModel.saveSettings( doc, parent, "Lowshelfactive" ); + m_para1ActiveModel.saveSettings( doc, parent, "Peak1active" ); + m_para2ActiveModel.saveSettings( doc, parent, "Peak2active" ); + m_para3ActiveModel.saveSettings( doc, parent, "Peak3active" ); + m_para4ActiveModel.saveSettings( doc, parent, "Peak4active" ); + m_highShelfActiveModel.saveSettings( doc, parent, "Highshelfactive" ); + m_lpActiveModel.saveSettings( doc, parent, "LPactive" ); + + m_lp12Model.saveSettings( doc, parent, "LP12" ); + m_lp24Model.saveSettings( doc, parent, "LP24" ); + m_lp48Model.saveSettings( doc, parent, "LP48" ); + + m_hp12Model.saveSettings( doc, parent, "HP12" ); + m_hp24Model.saveSettings( doc, parent, "HP24" ); + m_hp48Model.saveSettings( doc, parent, "HP48" ); + m_analyzeModel.saveSettings( doc, parent, "Analyzeenable"); + +} + diff --git a/plugins/eq/eqcontrols.h b/plugins/eq/eqcontrols.h new file mode 100644 index 000000000..f6703919e --- /dev/null +++ b/plugins/eq/eqcontrols.h @@ -0,0 +1,127 @@ +/* + * eqcontrols.h - defination of EqControls class. + * + * Copyright (c) 2014 David French + * + * 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 EQCONTROLS_H +#define EQCONTROLS_H + +#include "EffectControls.h" +#include "eqcontrolsdialog.h" +#include "Knob.h" + +class EqEffect; + +class EqControls : public EffectControls +{ + Q_OBJECT +public: + EqControls( EqEffect* effect ); + virtual ~EqControls() + { + } + virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); + virtual void loadSettings ( const QDomElement &_this ); + inline virtual QString nodeName() const + { + return "Eq"; + } + virtual int controlCount() + { + return 39; + } + virtual EffectControlDialog* createView() + { + printf("create dialog\n"); + return new EqControlsDialog( this ); + } + + float m_inPeakL; + float m_inPeakR; + float m_outPeakL; + float m_outPeakR; + float m_lowShelfPeakL, m_lowShelfPeakR; + float m_para1PeakL, m_para1PeakR; + float m_para2PeakL, m_para2PeakR; + float m_para3PeakL, m_para3PeakR; + float m_para4PeakL, m_para4PeakR; + float m_highShelfPeakL, m_highShelfPeakR; + + + + + +private: + EqEffect* m_effect; + + FloatModel m_inGainModel; + FloatModel m_outGainModel; + FloatModel m_lowShelfGainModel; + FloatModel m_para1GainModel; + FloatModel m_para2GainModel; + FloatModel m_para3GainModel; + FloatModel m_para4GainModel; + FloatModel m_highShelfGainModel; + + FloatModel m_hpResModel; + FloatModel m_lowShelfResModel; + FloatModel m_para1ResModel; + FloatModel m_para2ResModel; + FloatModel m_para3ResModel; + FloatModel m_para4ResModel; + FloatModel m_highShelfResModel; + FloatModel m_lpResModel; + + FloatModel m_hpFeqModel; + FloatModel m_lowShelfFreqModel; + FloatModel m_para1FreqModel; + FloatModel m_para2FreqModel; + FloatModel m_para3FreqModel; + FloatModel m_para4FreqModel; + FloatModel m_highShelfFreqModel; + FloatModel m_lpFreqModel; + + BoolModel m_hpActiveModel; + BoolModel m_lowShelfActiveModel; + BoolModel m_para1ActiveModel; + BoolModel m_para2ActiveModel; + BoolModel m_para3ActiveModel; + BoolModel m_para4ActiveModel; + BoolModel m_highShelfActiveModel; + BoolModel m_lpActiveModel; + + BoolModel m_lp12Model; + BoolModel m_lp24Model; + BoolModel m_lp48Model; + + BoolModel m_hp12Model; + BoolModel m_hp24Model; + BoolModel m_hp48Model; + + BoolModel m_analyzeModel; + + friend class EqControlsDialog; + friend class EqEffect; + +}; + +#endif // EQCONTROLS_H diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp new file mode 100644 index 000000000..704e81f00 --- /dev/null +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -0,0 +1,164 @@ +/* + * eqcontrolsdialog.cpp - defination of EqControlsDialog class. + * + * Copyright (c) 2014 David French + * + * 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 "eqcontrolsdialog.h" +#include "eqcontrols.h" +#include "embed.h" +#include "Fader.h" +#include "eqfader.h" +#include "Engine.h" +#include "AutomatableButton.h" +#include "QWidget" +#include "MainWindow.h" +#include "LedCheckbox.h" + + + + +EqControlsDialog::EqControlsDialog(EqControls *controls) : + EffectControlDialog( controls ) + +{ + m_controls = controls; + setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); + setPalette( pal ); + setFixedSize( 350, 275 ); + + m_parameterWidget = new EqParameterWidget( this ); + m_parameterWidget->move( 50, 5 ); +// m_inSpec = new EqSpectrumView( controls->m_effect, this); + + setBand( 0, &controls->m_hpActiveModel, &controls->m_hpFeqModel, &controls->m_hpResModel, 0, QColor(173, 115, 57), tr( "HP" ) ,0,0); + setBand( 1, &controls->m_lowShelfActiveModel, &controls->m_lowShelfFreqModel, &controls->m_lowShelfResModel, &controls->m_lowShelfGainModel, QColor(255, 0, 0), tr( "Low Shelf" ), &controls->m_lowShelfPeakL , &controls->m_lowShelfPeakR ); + setBand( 2, &controls->m_para1ActiveModel, &controls->m_para1FreqModel, &controls->m_para1ResModel, &controls->m_para1GainModel, QColor(255, 173, 115), tr( "Peak 1" ), &controls->m_para1PeakL, &controls->m_para1PeakR ); + setBand( 3, &controls->m_para2ActiveModel, &controls->m_para2FreqModel, &controls->m_para2ResModel, &controls->m_para2GainModel, QColor(255, 255, 0), tr( "Peak 2" ), &controls->m_para2PeakL, &controls->m_para2PeakR ); + setBand( 4, &controls->m_para3ActiveModel, &controls->m_para3FreqModel, &controls->m_para3ResModel, &controls->m_para3GainModel, QColor(0, 255, 0), tr( "Peak 3" ), &controls->m_para3PeakL, &controls->m_para3PeakR ); + setBand( 5, &controls->m_para4ActiveModel, &controls->m_para4FreqModel, &controls->m_para4ResModel, &controls->m_para4GainModel, QColor(0, 186, 255), tr( "Peak 4" ), &controls->m_para4PeakL, &controls->m_para4PeakR ); + setBand( 6, &controls->m_highShelfActiveModel, &controls->m_highShelfFreqModel, &controls->m_highShelfResModel, &controls->m_highShelfGainModel, QColor(222, 0, 222 ), tr( "High Shelf" ), &controls->m_highShelfPeakL, &controls->m_highShelfPeakR ); + setBand( 7, &controls->m_lpActiveModel, &controls->m_lpFreqModel, &controls->m_lpResModel, 0, QColor(156, 156, 156 ), tr( "LP" ) ,0,0); + int cw = width()/8; //the chanel width in pixels + int ko = ( cw * 0.5 ) - ((new Knob( knobBright_26, 0 ))->width() * 0.5 ); + + m_inGainFader = new EqFader( &controls->m_inGainModel, tr( "In Gain" ), this, &controls->m_inPeakL, &controls->m_inPeakR); + m_inGainFader->move( 10, 5 ); + + m_outGainFader = new EqFader( &controls->m_outGainModel, tr( "Out Gain" ), this, &controls->m_outPeakL, &controls->m_outPeakR ); + m_outGainFader->move( 315, 5 ); + //gain faders + + int fo = (cw * 0.5) - (m_outGainFader->width() * 0.5 ); + + for( int i = 1; i < m_parameterWidget->bandCount() - 1; i++) + { + m_gainFader = new EqFader( m_parameterWidget->getBandModels(i)->gain, tr( "" ), this ,m_parameterWidget->getBandModels( i )->peakL, m_parameterWidget->getBandModels( i )->peakR ); + m_gainFader->move( cw * i + fo , 123 ); + m_gainFader->setMinimumHeight(80); + m_gainFader->resize(m_gainFader->width() , 80); + } + + for( int i = 0; i < m_parameterWidget->bandCount() ; i++) + { + m_resKnob = new Knob( knobBright_26, this ); + m_resKnob->move(cw * i + ko , 205 ); + m_resKnob->setVolumeKnob(false); + m_resKnob->setModel( m_parameterWidget->getBandModels( i )->res ); + + m_freqKnob = new Knob( knobBright_26, this ); + m_freqKnob->move(cw * i + ko, 235 ); + m_freqKnob->setVolumeKnob( false ); + m_freqKnob->setModel( m_parameterWidget->getBandModels( i )->freq ); + + m_activeBox = new LedCheckBox( m_parameterWidget->getBandModels( i )->name , this ); + m_activeBox->move( cw * i + fo + 3, 260 ); + m_activeBox->setModel( m_parameterWidget->getBandModels( i )->active ); + } + + //hp filter type + + m_hp12Box = new LedCheckBox( tr( "12dB" ), this ); + m_hp12Box->move( cw*0 + ko, 175 ); + m_hp12Box->setModel( &controls->m_hp12Model ); + m_hp24Box = new LedCheckBox( tr( "24dB" ), this ); + m_hp24Box->move( cw*0 + ko, 155 ); + m_hp24Box->setModel( &controls->m_hp24Model ); + + m_hp48Box = new LedCheckBox( tr( "48dB" ), this ); + m_hp48Box->move( cw*0 + ko, 135 ); + m_hp48Box->setModel( &controls->m_hp48Model ); + + //LP filter type + + m_lp12Box = new LedCheckBox( tr( "12dB"), this ); + m_lp12Box->move( cw*7 + ko -5 , 175 ); + m_lp12Box->setModel( &controls->m_lp12Model ); + m_lp24Box = new LedCheckBox( tr( "24dB"), this ); + m_lp24Box->move( cw*7 + ko - 5, 155 ); + m_lp24Box->setModel( &controls->m_lp24Model ); + m_lp48Box = new LedCheckBox( tr( "48dB"), this ); + m_lp48Box->move( cw*7 + ko - 5, 135 ); + m_lp48Box->setModel( &controls->m_lp48Model ); + + automatableButtonGroup *lpBtnGrp = new automatableButtonGroup(this,tr ( "lp grp" ) ); + lpBtnGrp->addButton(m_lp12Box); + lpBtnGrp->addButton(m_lp24Box ); + lpBtnGrp->addButton(m_lp48Box ); + connect( m_lp12Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); + connect( m_lp24Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); + connect( m_lp48Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); + connect( &controls->m_lp12Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_lp24Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_lp48Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); + + + + automatableButtonGroup *hpBtnGrp = new automatableButtonGroup( this, tr( "hp grp" ) ); + hpBtnGrp->addButton(m_hp12Box ); + hpBtnGrp->addButton(m_hp24Box ); + hpBtnGrp->addButton(m_hp48Box ); + connect( m_hp12Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( m_hp24Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( m_hp48Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_hp12Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_hp24Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); + connect( &controls->m_hp48Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); + + + + + //Analize Box + m_analyzeBox = new LedCheckBox( tr( "Analyze" ), this ); + m_analyzeBox->move( cw*1 + ko + 5, 10 ); + m_analyzeBox->setModel( &controls->m_analyzeModel ); + +} + +void EqControlsDialog::mouseDoubleClickEvent(QMouseEvent *event) +{ + m_originalHeight = parentWidget()->height() == 150 ? m_originalHeight : parentWidget()->height() ; + parentWidget()->setFixedHeight( parentWidget()->height() == m_originalHeight ? 150 : m_originalHeight ); + update(); +} diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/eq/eqcontrolsdialog.h new file mode 100644 index 000000000..bb4c0cfad --- /dev/null +++ b/plugins/eq/eqcontrolsdialog.h @@ -0,0 +1,94 @@ +/* + * eqcontrolsdialog.h - defination of EqControlsDialog class. + * + * Copyright (c) 2014 David French + * + * 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 EQCONTROLSDIALOG_H +#define EQCONTROLSDIALOG_H + +#include "EffectControlDialog.h" +#include "Fader.h" +#include "Knob.h" +#include "LedCheckbox.h" +#include "eqparameterwidget.h" +#include "MainWindow.h" +#include "qpushbutton.h" +//#include "eqspectrumview.h" + +class EqControls; + +class EqControlsDialog : public EffectControlDialog +{ + +public: + EqControlsDialog( EqControls* controls ); + virtual ~EqControlsDialog() + { + } + + EqBand * setBand(EqControls *controls); + +private slots: + void updateVuMeters(); + +private: + EqControls * m_controls; + + Fader* m_inGainFader; + Fader* m_outGainFader; + Fader* m_gainFader; + Knob* m_resKnob; + Knob* m_freqKnob; + LedCheckBox* m_activeBox; + LedCheckBox* m_lp12Box; + LedCheckBox* m_lp24Box; + LedCheckBox* m_lp48Box; + LedCheckBox* m_hp12Box; + LedCheckBox* m_hp24Box; + LedCheckBox* m_hp48Box; + + LedCheckBox* m_analyzeBox; + + EqParameterWidget* m_parameterWidget; +// EqSpectrumView* m_inSpec; + + virtual void mouseDoubleClickEvent(QMouseEvent *event); + + EqBand* setBand( int index, BoolModel* active, FloatModel* freq, FloatModel* res, FloatModel* gain, QColor color, QString name, float* peakL, float* peakR) + { + EqBand* filterModels = m_parameterWidget->getBandModels( index ); + filterModels->active = active; + filterModels->freq = freq; + filterModels->res = res; + filterModels->color = color; + filterModels->gain = gain; + filterModels->peakL = peakL; + filterModels->peakR = peakR; + return filterModels; + } + + int m_originalHeight; + + +}; + +#endif // EQCONTROLSDIALOG_H diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp new file mode 100644 index 000000000..20be28000 --- /dev/null +++ b/plugins/eq/eqeffect.cpp @@ -0,0 +1,314 @@ +/* + * eqeffect.cpp - defination of EqEffect class. + * + * Copyright (c) 2014 David French + * + * 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 "eqeffect.h" +#include "embed.cpp" +#include "lmms_math.h" +#include "BasicFilters.h" +#include "interpolation.h" +#include "Engine.h" +#include "MainWindow.h" + +//TODO +//re write to store data from each filter(models,name, storeage name ) in a class, stored in array +//then just loop ever array for each section + +const int MAX_BANDS = 249; + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Eq", + QT_TRANSLATE_NOOP( "pluginBrowser", "A native eq plugin" ), + "Dave French ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +} ; + + + + +EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : + Effect( &eq_plugin_descriptor, parent, key ), + m_eqControls( this ) + + + +{ + m_dFilterCount = 4; + m_downsampleFilters = new EqLp12Filter[m_dFilterCount]; + for( int i = 0; i < m_dFilterCount; i++) + { + m_downsampleFilters[i].setFrequency(20000); + m_downsampleFilters[i].setQ(0.66); + m_downsampleFilters[i].setGain(0); + m_downsampleFilters[i].setSampleRate(Engine::mixer()->processingSampleRate() * 2 ); + } + m_upBuf = 0; +} + + + + +EqEffect::~EqEffect() +{ + +} + + + + +bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) +{ + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + double outSum = 0.0; + const float d = dryLevel(); + const float w = wetLevel(); + const float outGain = m_eqControls.m_outGainModel.value(); + const int sampleRate = Engine::mixer()->processingSampleRate() * 2; + sample_t dryS[2]; + sampleFrame m_inPeak; + + //TODO UPSAMPLE + upsample( buf, frames ); + + gain(m_upBuf , m_upBufFrames, m_eqControls.m_inGainModel.value(), &m_inPeak ); + m_eqControls.m_inPeakL = m_eqControls.m_inPeakL < m_inPeak[0] ? m_inPeak[0] : m_eqControls.m_inPeakL; + m_eqControls.m_inPeakR = m_eqControls.m_inPeakR < m_inPeak[1] ? m_inPeak[0] : m_eqControls.m_inPeakR; + + if(m_eqControls.m_hpActiveModel.value() ){ + m_hp12.setSampleRate( sampleRate ); + m_hp12.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp12.setQ( m_eqControls.m_hpResModel.value() ); + m_hp12.processBuffer( m_upBuf , m_upBufFrames ); + + if( m_eqControls.m_hp24Model.value() || m_eqControls.m_hp48Model.value() ) + { + m_hp24.setSampleRate( sampleRate ); + m_hp24.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp24.setQ( m_eqControls.m_hpResModel.value() ); + m_hp24.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_hp48Model.value() ) + { + m_hp480.setSampleRate( sampleRate ); + m_hp480.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp480.setQ( m_eqControls.m_hpResModel.value() ); + m_hp480.processBuffer( m_upBuf , m_upBufFrames ); + + m_hp481.setSampleRate( sampleRate ); + m_hp481.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp481.setQ( m_eqControls.m_hpResModel.value() ); + m_hp481.processBuffer( m_upBuf , m_upBufFrames ); + } + } + + if( m_eqControls.m_lowShelfActiveModel.value() ) + { + m_lowShelf.setSampleRate( sampleRate ); + m_lowShelf.setFrequency( m_eqControls.m_lowShelfFreqModel.value() ); + m_lowShelf.setQ( m_eqControls.m_lowShelfResModel .value() ); + m_lowShelf.setGain( m_eqControls.m_lowShelfGainModel.value() ); + m_lowShelf.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para1ActiveModel.value() ) + { + m_para1.setSampleRate(sampleRate ); + m_para1.setFrequency( m_eqControls.m_para1FreqModel.value() ); + m_para1.setQ( m_eqControls.m_para1ResModel.value() ); + m_para1.setGain( m_eqControls.m_para1GainModel.value() ); + m_para1.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para2ActiveModel.value() ) + { + m_para2.setSampleRate( sampleRate ); + m_para2.setFrequency( m_eqControls.m_para2FreqModel.value() ); + m_para2.setQ( m_eqControls.m_para2ResModel.value() ); + m_para2.setGain( m_eqControls.m_para2GainModel.value() ); + m_para2.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para3ActiveModel.value() ) + { + m_para3.setSampleRate( sampleRate); + m_para3.setFrequency( m_eqControls.m_para3FreqModel.value() ); + m_para3.setQ( m_eqControls.m_para3ResModel.value() ); + m_para3.setGain( m_eqControls.m_para3GainModel.value() ); + m_para3.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para4ActiveModel.value() ) + { + m_para4.setSampleRate( sampleRate ); + m_para4.setFrequency( m_eqControls.m_para4FreqModel.value() ); + m_para4.setQ( m_eqControls.m_para4ResModel.value() ); + m_para4.setGain( m_eqControls.m_para4GainModel.value() ); + m_para4.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_highShelfActiveModel.value() ) + { + m_highShelf.setSampleRate( sampleRate ); + m_highShelf.setFrequency( m_eqControls.m_highShelfFreqModel.value() ); + m_highShelf.setQ( m_eqControls.m_highShelfResModel.value() ); + m_highShelf.setGain( m_eqControls.m_highShelfGainModel.value() ); + m_highShelf.processBuffer( m_upBuf , m_upBufFrames ); + } + + if(m_eqControls.m_lpActiveModel.value() ){ + m_lp12.setSampleRate( sampleRate ); + m_lp12.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp12.setQ( m_eqControls.m_lpResModel.value() ); + m_lp12.processBuffer( m_upBuf , m_upBufFrames ); + + if( m_eqControls.m_lp24Model.value() || m_eqControls.m_lp48Model.value() ) + { + m_lp24.setSampleRate( sampleRate ); + m_lp24.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp24.setQ( m_eqControls.m_lpResModel.value() ); + m_lp24.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_lp48Model.value() ) + { + m_lp480.setSampleRate( sampleRate ); + m_lp480.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp480.setQ( m_eqControls.m_lpResModel.value() ); + m_lp480.processBuffer( m_upBuf , m_upBufFrames ); + + m_lp481.setSampleRate( sampleRate ); + m_lp481.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp481.setQ( m_eqControls.m_lpResModel.value() ); + m_lp481.processBuffer( m_upBuf , m_upBufFrames ); + } + } + + sampleFrame outPeak; + gain( m_upBuf , m_upBufFrames, outGain, &outPeak ); + m_eqControls.m_outPeakL = m_eqControls.m_outPeakL < outPeak[0] ? outPeak[0] : m_eqControls.m_outPeakL; + m_eqControls.m_outPeakR = m_eqControls.m_outPeakR < outPeak[1] ? outPeak[0] : m_eqControls.m_outPeakR; + + //TODO lp filter before downsample + for( int i =0; i < m_dFilterCount; i++) + { + m_downsampleFilters[i].processBuffer(m_upBuf , m_upBufFrames ); + } + + downSample( buf, frames ); + + for( fpp_t f = 0; f < frames; ++f ) + { + buf[f][0] = ( d * dryS[0] ) + ( w * buf[f][0] ); + buf[f][1] = ( d * dryS[1] ) + ( w * buf[f][1] ); + outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; + } + checkGate( outSum / frames ); + + return isRunning(); +} + + + + +void EqEffect::gain(sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak) +{ + peak[0][0] = 0.0f; peak[0][1] = 0.0f; + for( fpp_t f = 0; f < frames; ++f ) + { + buf[f][0] *= scale; + buf[f][1] *= scale; + + if( fabs( buf[f][0] ) > peak[0][0] ) + { + peak[0][0] = fabs( buf[f][0] ); + } + if( fabs( buf[f][1] ) > peak[0][1] ) + { + peak[0][1] = fabs( buf[f][0] ); + } + } + +} + +sampleFrame m_lastUpFrame; +void EqEffect::upsample(sampleFrame *buf, const fpp_t frames) +{ + + if(m_upBufFrames != frames * 2 ) + { + if( m_upBuf ) + { + delete m_upBuf; + } + m_upBuf = new sampleFrame[frames * 2]; + m_upBufFrames = frames * 2; + } + + for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) + { + m_upBuf[f2][0] = linearInterpolate(m_lastUpFrame[0],buf[f][0], 0.5); + m_upBuf[f2][1] = linearInterpolate(m_lastUpFrame[1],buf[f][1], 0.5); + m_upBuf[f2+1][0] = buf[f][0]; + m_upBuf[f2+1][1] = buf[f][1]; + + m_lastUpFrame[0] = buf[f][0]; + m_lastUpFrame[1] = buf[f][1]; + } +} + +void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) +{ + for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) + { + buf[f][0] = m_upBuf[f2][0]; + buf[f][1] = m_upBuf[f2][1]; + } +} + + + + + +extern "C" +{ + +//needed for getting plugin out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) +{ + return new EqEffect( parent , static_cast( data ) ); +} + +}} diff --git a/plugins/eq/eqeffect.h b/plugins/eq/eqeffect.h new file mode 100644 index 000000000..987eac9c4 --- /dev/null +++ b/plugins/eq/eqeffect.h @@ -0,0 +1,80 @@ +/* eqeffect.h - defination of EqEffect class. +* +* Copyright (c) 2014 David French +* +* 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 EQEFFECT_H +#define EQEFFECT_H + +#include "Effect.h" +#include "eqcontrols.h" +#include "lmms_math.h" +#include "BasicFilters.h" +#include "eqfilter.h" + +class EqEffect : public Effect +{ +public: + EqEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); + virtual ~EqEffect(); + virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); + virtual EffectControls* controls() + { + return &m_eqControls; + } + void gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ); + +private: + EqControls m_eqControls; + + EqHp12Filter m_hp12; + EqHp12Filter m_hp24; + EqHp12Filter m_hp480; + EqHp12Filter m_hp481; + + EqLowShelfFilter m_lowShelf; + + EqPeakFilter m_para1; + EqPeakFilter m_para2; + EqPeakFilter m_para3; + EqPeakFilter m_para4; + + EqHighShelfFilter m_highShelf; + + EqLp12Filter m_lp12; + EqLp12Filter m_lp24; + EqLp12Filter m_lp480; + EqLp12Filter m_lp481; + EqLp12Filter* m_downsampleFilters; + int m_dFilterCount; + sampleFrame* m_upBuf; + fpp_t m_upBufFrames; + +// const static int MAX_BANDS = 249; + void upsample( sampleFrame *buf, const fpp_t frames ); + void downSample( sampleFrame *buf, const fpp_t frames ); +// float m_bands[MAX_BANDS]; +// float m_energy; + +}; + +#endif // EQEFFECT_H diff --git a/plugins/eq/eqfader.cpp b/plugins/eq/eqfader.cpp new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/eq/eqfader.h b/plugins/eq/eqfader.h new file mode 100644 index 000000000..65c7a55a0 --- /dev/null +++ b/plugins/eq/eqfader.h @@ -0,0 +1,111 @@ +/* eqfader.h - defination of EqFader class. +* +* Copyright (c) 2014 David French +* +* 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 EQFADER_H +#define EQFADER_H +#include "Fader.h" +#include "EffectControls.h" +#include "MainWindow.h" +#include "qwidget.h" +#include "TextFloat.h" +#include "qlist.h" + + + +class EqFader : public Fader +{ + +public: + Q_OBJECT +public: + EqFader( FloatModel * model, const QString & name, QWidget * parent, float* lPeak, float* rPeak ) : + Fader( model, name, parent) + { + setMinimumSize( 23, 116 ); + setMaximumSize( 23, 116 ); + resize( 23, 116 ); + m_lPeak = lPeak; + m_rPeak = rPeak; + connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( updateVuMeters() ) ); + m_text = 0; + QList tfs = Engine::mainWindow()->findChildren(); + m_text = tfs.last(); + if(m_text) + { + printf("found text float\n"); + } + connect(model , SIGNAL (dataChanged()) , this, SLOT (updateText () )); + m_model = model; + } + + + + + ~EqFader() + { + } + + +private slots: + + void updateVuMeters() + { + const float opl = getPeak_L(); + const float opr = getPeak_R(); + const float fall_off = 1.2; + if( *m_lPeak > opl ) + { + setPeak_L( *m_lPeak ); + *m_lPeak = 0; + } + else + { + setPeak_L( opl/fall_off ); + } + + if( *m_rPeak > opr ) + { + setPeak_R( *m_rPeak ); + *m_rPeak = 0; + } + else + { + setPeak_R( opr/fall_off ); + } + update(); + } + + void updateText() + { + m_text->setText(QString()+m_model->value()); + } + + +private: + float* m_lPeak; + float* m_rPeak; + TextFloat* m_text; + FloatModel* m_model; + +}; +#endif // EQFADER_H diff --git a/plugins/eq/eqfilter.cpp b/plugins/eq/eqfilter.cpp new file mode 100644 index 000000000..7407377b9 --- /dev/null +++ b/plugins/eq/eqfilter.cpp @@ -0,0 +1,5 @@ +#include "eqfilter.h" + + + + diff --git a/plugins/eq/eqfilter.h b/plugins/eq/eqfilter.h new file mode 100644 index 000000000..69ee49c8b --- /dev/null +++ b/plugins/eq/eqfilter.h @@ -0,0 +1,315 @@ +/* + * eqfilter.cpp - defination of EqFilterclass. + * + * Copyright (c) 2014 David French + * + * 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 EQFILTER_H +#define EQFILTER_H + +#include "BasicFilters.h" +#include "lmms_math.h" + +/// +/// \brief The EqFilter class. +/// A wrapper for the StereoBiQuad class, giving it freq, res, and gain controls. +/// It is designed to process periods in one pass, with recalculation of coefficents +/// upon parameter changes. The intention is to use this as a bass class, children override +/// the calcCoefficents() function, providing the coefficents a1, a2, b0, b1, b2. +/// +class EqFilter : public StereoBiQuad +{ +public: + EqFilter() + { + + } + + virtual inline void setSampleRate( int sampleRate ) + { + if( sampleRate != m_sampleRate ) + { + m_sampleRate = sampleRate; + calcCoefficents(); + } + } + + virtual inline void setFrequency( float freq ){ + if ( freq != m_freq ) + { + m_freq = freq; + calcCoefficents(); + } + } + + virtual void setQ( float res ) + { + if ( res != m_res ) + { + m_res = res; + calcCoefficents(); + } + } + + virtual void setGain( float gain ) + { + if ( gain != m_gain ) + { + m_gain = gain; + calcCoefficents(); + } + } + + /// + /// \brief processBuffer + /// \param buf Audio Buffer + /// \param frames Count of sampleFrames in Audio Buffer + /// + virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) + { + for ( fpp_t f = 0 ; f < frames ; ++f) + { + buf[f][0] = update( buf[f][0] , 0); + buf[f][1] = update( buf[f][1] , 1); + } + } + +protected: + /// + /// \brief calcCoefficents + /// Override this in child classes to provide the coefficents, based on + /// Freq, Res and Gain + virtual void calcCoefficents(){ + setCoeffs( 0, 0, 0, 0, 0 ); + + } + + float m_sampleRate; + float m_freq; + float m_res; + float m_gain; +}; + + + + +/// +/// \brief The EqHp12Filter class +/// A 2 pole High Pass Filter +/// Coefficent calculations from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +class EqHp12Filter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = ( 1 + c ) * 0.5; + b1 = ( -( 1 + c ) ); + b2 = ( 1 + c ) * 0.5; + a0 = 1 + alpha; + a1 = ( -2 * c ); + a2 = 1 - alpha; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + + + } +}; + + +/// +/// \brief The EqLp12Filter class. +/// A 2 pole low pass filter +/// Coefficent calculations from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +/// +class EqLp12Filter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = ( 1 - c ) * 0.5; + b1 = 1 - c; + b2 = ( 1 - c ) * 0.5; + a0 = 1 + alpha; + a1 = -2 * c; + a2 = 1 - alpha; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + + + +/// +/// \brief The EqPeakFilter class +/// A Peak Filter +/// Coefficent calculations from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +/// +class EqPeakFilter : public EqFilter +{ +public: + + + virtual void calcCoefficents() + { + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); + float alpha = s / ( 2 * m_res ); + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = 1 + alpha*A; + b1 = -2*c; + b2 = 1 - alpha*A; + a0 = 1 + alpha/A; + a1 = -2*c; + a2 = 1 - alpha/A; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + +class EqLowShelfFilter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); +// float alpha = s / ( 2 * m_res ); + float beta = sqrt( A ) / m_res; + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = A * ( ( A+1 ) - ( A-1 ) * c + beta * s ); + b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * c) ; + b2 = A * ( ( A + 1 ) - ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) + ( A - 1 ) * c + beta * s; + a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * c ); + a2 = ( A + 1 ) + ( A - 1) * c - beta * s; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + + + } +}; + +class EqHighShelfFilter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025 ); + float beta = sqrt( A ) / m_res; + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = A *( ( A +1 ) + ( A - 1 ) * c + beta * s); + b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * c ); + b2 = A * ( ( A + 1 ) + ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) - ( A - 1 ) * c + beta * s; + a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * c ); + a2 = ( A + 1) - ( A - 1 ) * c - beta * s; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + + + + +#endif // EQFILTER_H diff --git a/plugins/eq/eqhighshelffileter.h b/plugins/eq/eqhighshelffileter.h new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/plugins/eq/eqhighshelffileter.h @@ -0,0 +1 @@ + diff --git a/plugins/eq/eqhp12.h b/plugins/eq/eqhp12.h new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/eq/eqlowshelffilter.h b/plugins/eq/eqlowshelffilter.h new file mode 100644 index 000000000..b28b04f64 --- /dev/null +++ b/plugins/eq/eqlowshelffilter.h @@ -0,0 +1,3 @@ + + + diff --git a/plugins/eq/eqlp12.h b/plugins/eq/eqlp12.h new file mode 100644 index 000000000..e69de29bb diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp new file mode 100644 index 000000000..eb329460b --- /dev/null +++ b/plugins/eq/eqparameterwidget.cpp @@ -0,0 +1,197 @@ +/* + * eqparameterwidget.cpp - defination of EqParameterWidget class. + * + * Copyright (c) 2014 David French + * + * 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 "eqparameterwidget.h" +#include "QPainter" +#include "qwidget.h" +#include "lmms_math.h" + +#include "MainWindow.h" +#include "QMouseEvent" + +EqParameterWidget::EqParameterWidget( QWidget *parent ) : + QWidget( parent ), + m_bands ( 0 ), + m_selectedBand ( 0 ) +{ + m_bands = new EqBand[8]; + resize( 250, 116 ); + connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + float totalLength = log10( 21000 ); + m_pixelsPerUnitWidth = width( ) / totalLength ; + float totalHeight = 40; + m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); + m_scale = 1.5; + m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); +} + + + + +EqParameterWidget::~EqParameterWidget() +{ + +} + + + + +void EqParameterWidget::paintEvent( QPaintEvent *event ) +{ + QPainter painter( this ); + for( int i = 0 ; i < bandCount() ; i++ ) + { + m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + float x = freqToXPixel( m_bands[i].freq->value() ); + float y = height() * 0.5; + float gain = 1; + if( m_bands[i].gain ) + { + gain = m_bands[i].gain->value(); + } + y = gainToYPixel( gain ) + 3; + float bw = m_bands[i].freq->value() / m_bands[i].res->value(); + m_bands[i].x = x; m_bands[i].y = y; + painter.drawPoint( x, y ); + painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + if(i == 0 || i == bandCount() - 1 ){ + painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); + } + else + { + painter.drawLine(freqToXPixel(m_bands[i].freq->value()-(bw * 0.5)),y,freqToXPixel(m_bands[i].freq->value()+(bw * 0.5)),y); + } + } + //Draw color band + int sectionLength = width() / bandCount(); + for( int i = 0; i < bandCount(); i++) + { + m_bands[i].color.setAlpha( 255 ); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.drawLine(sectionLength * i , 1, sectionLength * (i+1) , 1); + } +} + + + + +void EqParameterWidget::mousePressEvent( QMouseEvent *event ) +{ + m_oldX = event->x(); m_oldY = event->y(); + m_selectedBand = selectNearestHandle( event->x(), event->y() ); + m_mouseAction = none; + if ( event->button() == Qt::LeftButton ) m_mouseAction = drag; + if ( event->button() == Qt::RightButton ) m_mouseAction = res; +} + + + + +void EqParameterWidget::mouseReleaseEvent( QMouseEvent *event ) +{ + m_selectedBand = 0; + m_mouseAction = none; +} + + + + +void EqParameterWidget::mouseMoveEvent( QMouseEvent *event ) +{ + int deltaX = event->x() - m_oldX; + int deltaR = event->y() - m_oldY; + m_oldX = event->x(); m_oldY = event->y(); + if(m_selectedBand && m_selectedBand->active->value() ) + { + switch ( m_mouseAction ) { + case none : + break; + case drag: + if( m_selectedBand->freq ) m_selectedBand->freq->setValue( xPixelToFreq( m_oldX ) ); + if( m_selectedBand->gain )m_selectedBand->gain->setValue( yPixelToGain( m_oldY ) ); + break; + case res: + if( m_selectedBand->res )m_selectedBand->res->incValue( deltaX * resPixelMultiplyer() ); + if( m_selectedBand->res )m_selectedBand->res->incValue( (-deltaR) * resPixelMultiplyer() ); + break; + default: + break; + } + } +} + + + + +void EqParameterWidget::mouseDoubleClickEvent( QMouseEvent *event ) +{ + EqBand* selected = selectNearestHandle( event->x() , event->y() ); + if( selected ) + { + selected->active->setValue( selected->active->value() ? 0 : 1 ); + } +} + + + + +EqBand* EqParameterWidget::selectNearestHandle( const int x, const int y ) +{ + EqBand* selectedModel = 0; + float* distanceToHandles = new float[bandCount()]; + //calc distance to each handle + for( int i = 0 ; i < bandCount() ; i++) + { + int xOffset = m_bands[i].x - x; + int yOffset = m_bands[i].y - y; + distanceToHandles[i] = fabs(sqrt((xOffset * xOffset ) + ( yOffset * yOffset ) ) ); + } + //select band + int shortestBand = 0; + for (int i = 1 ; i < bandCount() ; i++ ) + { + if ( distanceToHandles [i] < distanceToHandles[shortestBand] ){ + shortestBand = i; + } + } + if(distanceToHandles[shortestBand] < maxDistanceFromHandle() ) + { + selectedModel = &m_bands[shortestBand]; + } + delete distanceToHandles; + return selectedModel; +} + + + + +EqBand::EqBand() : + gain ( 0 ), + res ( 0 ), + freq ( 0 ), + color ( QColor( 255, 255, 255 ) ), + name ( QString( "" ) ) +{ +} diff --git a/plugins/eq/eqparameterwidget.h b/plugins/eq/eqparameterwidget.h new file mode 100644 index 000000000..cb14154b3 --- /dev/null +++ b/plugins/eq/eqparameterwidget.h @@ -0,0 +1,158 @@ +/* + * eqparameterwidget.cpp - defination of EqParameterWidget class. + * + * Copyright (c) 2014 David French + * + * 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 EQPARAMETERWIDGET_H +#define EQPARAMETERWIDGET_H +#include +#include "EffectControls.h" + + +class EqBand +{ +public : + EqBand(); + FloatModel* gain; + FloatModel* res; + FloatModel* freq; + BoolModel* active; + QColor color; + int x; + int y; + QString name; + float* peakL; + float* peakR; + +}; + + + +class EqParameterWidget : public QWidget +{ + +public: + explicit EqParameterWidget( QWidget *parent = 0 ); + ~EqParameterWidget(); + const int bandCount() + { + return 8; + } + + + + const int maxDistanceFromHandle() + { + return 20; + } + + + + + EqBand* getBandModels( int i ) + { + return &m_bands[i]; + } + + + + + const int activeAplha() + { + return 200; + } + + + + + const int inactiveAlpha() + { + return 100; + } + + + + + const float resPixelMultiplyer() + { + return 100; + } + + +signals: + +public slots: + +protected: + virtual void paintEvent ( QPaintEvent * event ); + virtual void mousePressEvent(QMouseEvent * event ); + virtual void mouseReleaseEvent(QMouseEvent * event); + virtual void mouseMoveEvent(QMouseEvent * event); + virtual void mouseDoubleClickEvent(QMouseEvent * event); + +private: + EqBand* m_bands; + float m_pixelsPerUnitWidth; + float m_pixelsPerUnitHeight; + float m_pixelsPerOctave; + float m_scale; + EqBand* m_selectedBand; + + EqBand* selectNearestHandle( const int x, const int y ); + + enum MouseAction { none, drag, res } m_mouseAction; + int m_oldX, m_oldY; + + inline int freqToXPixel( float freq ) + { + return ( log10( freq ) * m_pixelsPerUnitWidth * m_scale ) - ( width() * 0.5 ); + } + + + + + inline float xPixelToFreq( int x ) + { + return pow( 10, ( x + ( width() * 0.5 ) ) / ( m_pixelsPerUnitWidth * m_scale ) ); + } + + + + + inline int gainToYPixel( float gain ) + { + return ( height() - 3) - ( gain * m_pixelsPerUnitHeight ) - ( (height() -3 ) * 0.5); + } + + + + + inline float yPixelToGain( int y ) + { + return ( ( 0.5 * height() ) - y) / m_pixelsPerUnitHeight; + } + +}; + + +#endif // EQPARAMETERWIDGET_H diff --git a/plugins/eq/eqpeekfilter.cpp b/plugins/eq/eqpeekfilter.cpp new file mode 100644 index 000000000..1cc60cf4c --- /dev/null +++ b/plugins/eq/eqpeekfilter.cpp @@ -0,0 +1,3 @@ +#include "eqpeekfilter.h" + + diff --git a/plugins/eq/eqpeekfilter.h b/plugins/eq/eqpeekfilter.h new file mode 100644 index 000000000..b28b04f64 --- /dev/null +++ b/plugins/eq/eqpeekfilter.h @@ -0,0 +1,3 @@ + + + diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h new file mode 100644 index 000000000..542ba6b52 --- /dev/null +++ b/plugins/eq/eqspectrumview.h @@ -0,0 +1,80 @@ +#ifndef EQSPECTRUMVIEW_H +#define EQSPECTRUMVIEW_H + +#include "qpainter.h" +#include "eqeffect.h" +#include "qwidget.h" + +class EqParams +{ +public: + const static int MAX_BANDS = 249; + float m_bands[MAX_BANDS]; + float m_energy; +}; + + +class EqSpectrumView : public QWidget +{ +public: + EqSpectrumView( EqParams* s, QWidget * _parent = 0) : + QWidget( _parent ), + m_sa( s ) + { + setFixedSize( 240, 116 ); + connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + setAttribute( Qt::WA_OpaquePaintEvent, true ); + } + + virtual ~EqSpectrumView() + { + } +EqParams *m_sa; + virtual void paintEvent( QPaintEvent* event ) + { + const int MAX_BANDS = 249; + QPainter p( this ); + const float e = m_sa->m_energy; + if( e <= 0 ) + { + //dont draw anything + return; + } + float * b = m_sa->m_bands; + const int LOWER_Y = -60; // dB + int h; + const int fh = height(); + bool linX = true; + if( linX ) + { + for( int x = 0; x < MAX_BANDS; ++x, ++b ) + { + h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); + if( h < 0 ) h = 0; else if( h >= fh ) continue; + p.drawPoint( x, fh-h ); + } + } + else + { + for( int x = 0; x < 31; ++x, ++b ) + { + h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); + if( h < 0 ) h = 0; else if( h >= fh ) continue; else h = ( h / 3 ) * 3; + p.drawPoint(x * 8, fh-h ); + } + } + } + + + +private: + + +//EqParams *m_sa; +// QImage m_backgroundPlain; +// QImage m_background; + +} ; + + +#endif // EQSPECTRUMVIEW_H diff --git a/plugins/eq/logo.png b/plugins/eq/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..89e9f3680118931dd86f065a8f6bc0b4c631a585 GIT binary patch literal 3225 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 Date: Thu, 11 Dec 2014 00:08:42 +0000 Subject: [PATCH 02/15] spectrum analysis implemented --- include/Fader.h | 6 ++ plugins/eq/CMakeLists.txt | 6 +- plugins/eq/eqcontrols.cpp | 12 ++-- plugins/eq/eqcontrols.h | 8 ++- plugins/eq/eqcontrolsdialog.cpp | 13 +++- plugins/eq/eqcontrolsdialog.h | 5 +- plugins/eq/eqeffect.cpp | 102 ++++++++++++++++++++++++++++--- plugins/eq/eqeffect.h | 63 +++++++++++-------- plugins/eq/eqparameterwidget.cpp | 6 +- plugins/eq/eqspectrumview.h | 76 +++++++++++++++++++---- src/gui/widgets/Fader.cpp | 4 +- 11 files changed, 237 insertions(+), 64 deletions(-) diff --git a/include/Fader.h b/include/Fader.h index f8048e98c..96c3ae732 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -82,6 +82,12 @@ public: { m_displayConversion = b; } + inline void setHintText( const QString & _txt_before, + const QString & _txt_after ) + { + setDescription( _txt_before ); + setUnit( _txt_after ); + } private: virtual void contextMenuEvent( QContextMenuEvent * _me ); diff --git a/plugins/eq/CMakeLists.txt b/plugins/eq/CMakeLists.txt index 72dc8bb5e..b537ab632 100644 --- a/plugins/eq/CMakeLists.txt +++ b/plugins/eq/CMakeLists.txt @@ -1,3 +1,5 @@ INCLUDE(BuildPlugin) - -BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) +LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) +LINK_LIBRARIES(${FFTW3F_LIBRARIES}) +BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/eq/eqcontrols.cpp b/plugins/eq/eqcontrols.cpp index efb89a245..5fe6b6c32 100644 --- a/plugins/eq/eqcontrols.cpp +++ b/plugins/eq/eqcontrols.cpp @@ -35,12 +35,12 @@ EqControls::EqControls( EqEffect *effect ) : m_effect( effect ), m_inGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Input gain") ), m_outGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Output gain" ) ), - m_lowShelfGainModel( 0.0 , -20, 20, 0.001, this, tr( "Low shelf gain" ) ), - m_para1GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 1 gain" ) ), - m_para2GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 2 gain" ) ), - m_para3GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 3 gain" ) ), - m_para4GainModel( 0.0 , -20, 20, 0.001, this, tr( "Peak 4 gain" ) ), - m_highShelfGainModel( 0.0 , -20, 20, 0.001, this, tr( "High Shelf gain" ) ), + m_lowShelfGainModel( 0.0 , -40, 40, 0.001, this, tr( "Low shelf gain" ) ), + m_para1GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 1 gain" ) ), + m_para2GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 2 gain" ) ), + m_para3GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 3 gain" ) ), + m_para4GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 4 gain" ) ), + m_highShelfGainModel( 0.0 , -40, 40, 0.001, this, tr( "High Shelf gain" ) ), m_hpResModel( 0.707,0.003, 10.0 , 0.001, this, tr( "HP res" ) ), m_lowShelfResModel( 1.4,0.0, 10.0 , 0.001, this , tr( "Low Shelf res" ) ), m_para1ResModel( 1.4 ,0.55, 10.0 , 0.001, this , tr( "Peak 1 res" ) ), diff --git a/plugins/eq/eqcontrols.h b/plugins/eq/eqcontrols.h index f6703919e..413bf4176 100644 --- a/plugins/eq/eqcontrols.h +++ b/plugins/eq/eqcontrols.h @@ -33,9 +33,9 @@ class EqEffect; class EqControls : public EffectControls { - Q_OBJECT + Q_OBJECT public: - EqControls( EqEffect* effect ); + explicit EqControls( EqEffect* effect ); virtual ~EqControls() { } @@ -51,7 +51,6 @@ public: } virtual EffectControlDialog* createView() { - printf("create dialog\n"); return new EqControlsDialog( this ); } @@ -66,6 +65,9 @@ public: float m_para4PeakL, m_para4PeakR; float m_highShelfPeakL, m_highShelfPeakR; + FftBands m_inFftBands; + FftBands m_outFftBands; + diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index 704e81f00..1527f59fd 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -33,6 +33,7 @@ #include "QWidget" #include "MainWindow.h" #include "LedCheckbox.h" +//#include "eqspectrumview.h" @@ -48,9 +49,14 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : setPalette( pal ); setFixedSize( 350, 275 ); + m_inSpec = new EqSpectrumView( &controls->m_inFftBands, this); + m_inSpec->move( 50, 5 ); + m_inSpec->color = QColor( 200, 200, 100, 100 ); + m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); + m_outSpec->move( 50, 5 ); + m_outSpec->color = QColor(100, 200, 200, 100); m_parameterWidget = new EqParameterWidget( this ); m_parameterWidget->move( 50, 5 ); -// m_inSpec = new EqSpectrumView( controls->m_effect, this); setBand( 0, &controls->m_hpActiveModel, &controls->m_hpFeqModel, &controls->m_hpResModel, 0, QColor(173, 115, 57), tr( "HP" ) ,0,0); setBand( 1, &controls->m_lowShelfActiveModel, &controls->m_lowShelfFreqModel, &controls->m_lowShelfResModel, &controls->m_lowShelfGainModel, QColor(255, 0, 0), tr( "Low Shelf" ), &controls->m_lowShelfPeakL , &controls->m_lowShelfPeakR ); @@ -66,6 +72,7 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_inGainFader = new EqFader( &controls->m_inGainModel, tr( "In Gain" ), this, &controls->m_inPeakL, &controls->m_inPeakR); m_inGainFader->move( 10, 5 ); + m_outGainFader = new EqFader( &controls->m_outGainModel, tr( "Out Gain" ), this, &controls->m_outPeakL, &controls->m_outPeakR ); m_outGainFader->move( 315, 5 ); //gain faders @@ -78,6 +85,8 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_gainFader->move( cw * i + fo , 123 ); m_gainFader->setMinimumHeight(80); m_gainFader->resize(m_gainFader->width() , 80); + m_gainFader->setDisplayConversion( false ); + m_gainFader->setHintText( tr( "Gain") , "dB"); } for( int i = 0; i < m_parameterWidget->bandCount() ; i++) @@ -86,11 +95,13 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_resKnob->move(cw * i + ko , 205 ); m_resKnob->setVolumeKnob(false); m_resKnob->setModel( m_parameterWidget->getBandModels( i )->res ); + m_resKnob->setHintText( tr( "Resonance:") , ""); m_freqKnob = new Knob( knobBright_26, this ); m_freqKnob->move(cw * i + ko, 235 ); m_freqKnob->setVolumeKnob( false ); m_freqKnob->setModel( m_parameterWidget->getBandModels( i )->freq ); + m_freqKnob->setHintText( tr( "Frequency:" ), "Hz" ); m_activeBox = new LedCheckBox( m_parameterWidget->getBandModels( i )->name , this ); m_activeBox->move( cw * i + fo + 3, 260 ); diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/eq/eqcontrolsdialog.h index bb4c0cfad..fe1326231 100644 --- a/plugins/eq/eqcontrolsdialog.h +++ b/plugins/eq/eqcontrolsdialog.h @@ -32,7 +32,7 @@ #include "eqparameterwidget.h" #include "MainWindow.h" #include "qpushbutton.h" -//#include "eqspectrumview.h" +#include "eqspectrumview.h" class EqControls; @@ -69,7 +69,8 @@ private: LedCheckBox* m_analyzeBox; EqParameterWidget* m_parameterWidget; -// EqSpectrumView* m_inSpec; + EqSpectrumView* m_inSpec; + EqSpectrumView* m_outSpec; virtual void mouseDoubleClickEvent(QMouseEvent *event); diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp index 20be28000..3808f8bf6 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/eq/eqeffect.cpp @@ -34,7 +34,7 @@ //re write to store data from each filter(models,name, storeage name ) in a class, stored in array //then just loop ever array for each section -const int MAX_BANDS = 249; + extern "C" { @@ -58,16 +58,13 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : Effect( &eq_plugin_descriptor, parent, key ), m_eqControls( this ) - - - { - m_dFilterCount = 4; + m_dFilterCount = 10; m_downsampleFilters = new EqLp12Filter[m_dFilterCount]; for( int i = 0; i < m_dFilterCount; i++) { - m_downsampleFilters[i].setFrequency(20000); - m_downsampleFilters[i].setQ(0.66); + m_downsampleFilters[i].setFrequency(22000); + m_downsampleFilters[i].setQ(0.85); m_downsampleFilters[i].setGain(0); m_downsampleFilters[i].setSampleRate(Engine::mixer()->processingSampleRate() * 2 ); } @@ -99,6 +96,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) sample_t dryS[2]; sampleFrame m_inPeak; + analyze( buf, frames, &m_eqControls.m_inFftBands) ; //TODO UPSAMPLE upsample( buf, frames ); @@ -236,6 +234,8 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; } checkGate( outSum / frames ); + analyze( buf, frames, &m_eqControls.m_outFftBands) ; + setBandPeaks( &m_eqControls.m_outFftBands , (int)(sampleRate * 0.5)); return isRunning(); } @@ -298,6 +298,94 @@ void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) } } +void EqEffect::analyze(sampleFrame *buf, const fpp_t frames, FftBands* fft) +{ + const int FFT_BUFFER_SIZE = 2048; + fpp_t f = 0; + if( frames > FFT_BUFFER_SIZE ) + { + fft->m_framesFilledUp = 0; + f = frames - FFT_BUFFER_SIZE; + } + // meger channels + for( ; f < frames; ++f ) + { + fft->m_buffer[fft->m_framesFilledUp] = + ( buf[f][0] + buf[f][1] ) * 0.5; + ++fft->m_framesFilledUp; + } + + if( fft->m_framesFilledUp < FFT_BUFFER_SIZE ) + { + return; + } + + fft->m_sr = Engine::mixer()->processingSampleRate(); + const int LOWEST_FREQ = 0; + const int HIGHEST_FREQ = fft->m_sr / 2; + + fftwf_execute( fft->m_fftPlan ); + absspec( fft->m_specBuf, fft->m_absSpecBuf, FFT_BUFFER_SIZE+1 ); + + compressbands( fft->m_absSpecBuf, fft->m_bands, FFT_BUFFER_SIZE+1, + MAX_BANDS, + (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2)), + (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2))); + fft->m_energy = maximum( fft->m_bands, MAX_BANDS ) / maximum( fft->m_buffer, FFT_BUFFER_SIZE ); + fft->m_framesFilledUp = 0; +} + +float EqEffect::peakBand(float minF, float maxF, FftBands *fft, int sr) +{ + float peak = -60; + float * b = fft->m_bands; + float h = 0; + for(int x = 0; x < MAX_BANDS; x++, b++) + { + if( bandToFreq( x ,sr) >= minF && bandToFreq( x,sr ) <= maxF ) + { + h = 20*( log10( *b / fft->m_energy ) ); + peak = h > peak ? h : peak; + } + } + return (peak+100)/100; +} + +void EqEffect::setBandPeaks(FftBands *fft, int samplerate ) +{ + m_eqControls.m_lowShelfPeakR = m_eqControls.m_lowShelfPeakL = + peakBand( 0, + m_eqControls.m_lowShelfFreqModel.value(), fft , samplerate ); + + m_eqControls.m_para1PeakL = m_eqControls.m_para1PeakR = + peakBand( m_eqControls.m_para1FreqModel.value() + - (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), + m_eqControls.m_para1FreqModel.value() + + (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), fft , samplerate ); + + m_eqControls.m_para2PeakL = m_eqControls.m_para2PeakR = + peakBand( m_eqControls.m_para2FreqModel.value() + - (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), + m_eqControls.m_para2FreqModel.value() + + (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), fft , samplerate ); + + m_eqControls.m_para3PeakL = m_eqControls.m_para3PeakR = + peakBand( m_eqControls.m_para3FreqModel.value() + - (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), + m_eqControls.m_para3FreqModel.value() + + (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), fft , samplerate ); + + m_eqControls.m_para4PeakL = m_eqControls.m_para4PeakR = + peakBand( m_eqControls.m_para4FreqModel.value() + - (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), + m_eqControls.m_para4FreqModel.value() + + (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), fft , samplerate ); + + m_eqControls.m_highShelfPeakL = m_eqControls.m_highShelfPeakR = + peakBand( m_eqControls.m_highShelfFreqModel.value(), + samplerate * 0.5 , fft, samplerate ); +} + diff --git a/plugins/eq/eqeffect.h b/plugins/eq/eqeffect.h index 987eac9c4..0ea793351 100644 --- a/plugins/eq/eqeffect.h +++ b/plugins/eq/eqeffect.h @@ -31,49 +31,60 @@ #include "BasicFilters.h" #include "eqfilter.h" + + class EqEffect : public Effect { public: - EqEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); - virtual ~EqEffect(); - virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); - virtual EffectControls* controls() - { - return &m_eqControls; - } - void gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ); + EqEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); + virtual ~EqEffect(); + virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); + virtual EffectControls* controls() + { + return &m_eqControls; + } + void gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ); private: - EqControls m_eqControls; + EqControls m_eqControls; - EqHp12Filter m_hp12; - EqHp12Filter m_hp24; - EqHp12Filter m_hp480; - EqHp12Filter m_hp481; + EqHp12Filter m_hp12; + EqHp12Filter m_hp24; + EqHp12Filter m_hp480; + EqHp12Filter m_hp481; - EqLowShelfFilter m_lowShelf; + EqLowShelfFilter m_lowShelf; - EqPeakFilter m_para1; - EqPeakFilter m_para2; - EqPeakFilter m_para3; - EqPeakFilter m_para4; + EqPeakFilter m_para1; + EqPeakFilter m_para2; + EqPeakFilter m_para3; + EqPeakFilter m_para4; - EqHighShelfFilter m_highShelf; + EqHighShelfFilter m_highShelf; - EqLp12Filter m_lp12; - EqLp12Filter m_lp24; - EqLp12Filter m_lp480; - EqLp12Filter m_lp481; + EqLp12Filter m_lp12; + EqLp12Filter m_lp24; + EqLp12Filter m_lp480; + EqLp12Filter m_lp481; EqLp12Filter* m_downsampleFilters; int m_dFilterCount; sampleFrame* m_upBuf; fpp_t m_upBufFrames; -// const static int MAX_BANDS = 249; + // const static int MAX_BANDS = 249; void upsample( sampleFrame *buf, const fpp_t frames ); void downSample( sampleFrame *buf, const fpp_t frames ); -// float m_bands[MAX_BANDS]; -// float m_energy; + void analyze( sampleFrame *buf, const fpp_t frames, FftBands* fft ); + float peakBand(float minF, float maxF,FftBands*, int); + + inline float bandToFreq ( int index , int sampleRate ) + { + return index * sampleRate / (MAX_BANDS * 2); + } + + void setBandPeaks( FftBands *fft , int); + // float m_bands[MAX_BANDS]; + // float m_energy; }; diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index eb329460b..39e1ab94f 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -40,7 +40,7 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); float totalLength = log10( 21000 ); m_pixelsPerUnitWidth = width( ) / totalLength ; - float totalHeight = 40; + float totalHeight = 80; m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); m_scale = 1.5; m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); @@ -63,7 +63,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) for( int i = 0 ; i < bandCount() ; i++ ) { m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); float x = freqToXPixel( m_bands[i].freq->value() ); float y = height() * 0.5; float gain = 1; @@ -75,7 +75,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) float bw = m_bands[i].freq->value() / m_bands[i].res->value(); m_bands[i].x = x; m_bands[i].y = y; painter.drawPoint( x, y ); - painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); if(i == 0 || i == bandCount() - 1 ){ painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); } diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index 542ba6b52..ca63ff27d 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -2,38 +2,70 @@ #define EQSPECTRUMVIEW_H #include "qpainter.h" -#include "eqeffect.h" +//#include "eqeffect.h" #include "qwidget.h" +#include "fft_helpers.h" -class EqParams + +const int MAX_BANDS = 512; + +class FftBands { public: - const static int MAX_BANDS = 249; + + fftwf_plan m_fftPlan; + + fftwf_complex * m_specBuf; + float m_absSpecBuf[FFT_BUFFER_SIZE+1]; + float m_buffer[FFT_BUFFER_SIZE*2]; + int m_framesFilledUp; + float m_bands[MAX_BANDS]; float m_energy; + int m_sr; + + FftBands() : + m_framesFilledUp( 0 ), + m_energy( 0 ) + { + memset( m_buffer, 0, sizeof( m_buffer ) ); + + m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); + m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE ); + } }; class EqSpectrumView : public QWidget { + public: - EqSpectrumView( EqParams* s, QWidget * _parent = 0) : + explicit EqSpectrumView( FftBands * b, QWidget * _parent = 0) : QWidget( _parent ), - m_sa( s ) + m_sa( b ) { - setFixedSize( 240, 116 ); + setFixedSize( 250, 116 ); connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); - setAttribute( Qt::WA_OpaquePaintEvent, true ); + setAttribute( Qt::WA_TranslucentBackground, true ); + m_skipBands = MAX_BANDS * 0.5; + float totalLength = log10( 21000); + m_pixelsPerUnitWidth = width( ) / totalLength ; + m_scale = 1.5; + color = QColor( 255, 255, 255, 255 ); + } virtual ~EqSpectrumView() { } -EqParams *m_sa; + QColor color; + FftBands *m_sa; + int m_lastY; virtual void paintEvent( QPaintEvent* event ) { - const int MAX_BANDS = 249; + int m_lastY = height(); QPainter p( this ); + p.setPen( QPen( color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); const float e = m_sa->m_energy; if( e <= 0 ) { @@ -51,7 +83,8 @@ EqParams *m_sa; { h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); if( h < 0 ) h = 0; else if( h >= fh ) continue; - p.drawPoint( x, fh-h ); + p.drawLine(freqToXPixel(bandToFreq(x -1 ) ),m_lastY, freqToXPixel(bandToFreq(x ) ), fh-h ); + m_lastY = fh-h; } } else @@ -67,12 +100,29 @@ EqParams *m_sa; + + inline int bandToXPixel( float band ) + { + return ( log10( band - m_skipBands ) * m_pixelsPerUnitWidth * m_scale ); + } + + inline float bandToFreq ( int index ) + { + return index * m_sa->m_sr / (MAX_BANDS * 2); + } + + + inline int freqToXPixel( float freq ) + { + return ( log10( freq ) * m_pixelsPerUnitWidth * m_scale ) - ( width() * 0.5 ); + } private: + float m_pixelsPerUnitWidth; + float m_scale; + int m_skipBands; + -//EqParams *m_sa; -// QImage m_backgroundPlain; -// QImage m_background; } ; diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 7f1352a57..1af7bb040 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -107,6 +107,7 @@ Fader::Fader( FloatModel * _model, const QString & _name, QWidget * _parent ) : setMaximumSize( 23, 116); resize( 23, 116 ); setModel( _model ); + setHintText( "Volume:","%"); } @@ -141,6 +142,7 @@ Fader::Fader( FloatModel * model, const QString & name, QWidget * parent, QPixma setMaximumSize( m_back->width(), m_back->height() ); resize( m_back->width(), m_back->height() ); setModel( model ); + setHintText( "Volume:","%"); } @@ -324,7 +326,7 @@ void Fader::updateTextFloat() } else { - s_textFloat->setText( QString("Volume: %1 %").arg( m_displayConversion ? m_model->value() * 100 : m_model->value() ) ); + s_textFloat->setText( m_description + " " + QString("%1 ").arg( m_displayConversion ? m_model->value() * 100 : m_model->value() ) + " " + m_unit ); } s_textFloat->moveGlobal( this, QPoint( width() - ( *m_knob ).width() - 5, knobPosY() - 46 ) ); } From a7831a7ccab1ec0537bfb520b72b4eb0efa84e3b Mon Sep 17 00:00:00 2001 From: dave Date: Thu, 11 Dec 2014 16:52:06 +0000 Subject: [PATCH 03/15] Draw grid lines on widget --- plugins/eq/eqparameterwidget.cpp | 25 ++++++++++++++++++++++--- plugins/eq/eqparameterwidget.h | 2 ++ plugins/eq/eqspectrumview.h | 8 ++++++-- 3 files changed, 30 insertions(+), 5 deletions(-) diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index 39e1ab94f..f1d53fa2e 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -44,6 +44,7 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); m_scale = 1.5; m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); + } @@ -59,9 +60,27 @@ EqParameterWidget::~EqParameterWidget() void EqParameterWidget::paintEvent( QPaintEvent *event ) { - QPainter painter( this ); + QPainter painter( this ); + //Draw Frequecy maker lines + painter.setPen( QPen( QColor( 100, 100, 100, 200 ), 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + for( int x = 20 ; x < 100; x += 10) + { + painter.drawLine( freqToXPixel( x ) , 0, freqToXPixel( x ) , height() ); + } + for( int x = 100 ; x < 1000; x += 100) + { + painter.drawLine( freqToXPixel( x ) , 0, freqToXPixel( x ) , height() ); + } + for( int x = 1000 ; x < 11000; x += 1000) + { + painter.drawLine( freqToXPixel( x ) , 0, freqToXPixel( x ) , height() ); + } + //draw 0dB line + painter.drawLine(0, gainToYPixel( 0 ) , width(), gainToYPixel( 0 ) ); + for( int i = 0 ; i < bandCount() ; i++ ) { + m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); float x = freqToXPixel( m_bands[i].freq->value() ); @@ -71,11 +90,11 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) { gain = m_bands[i].gain->value(); } - y = gainToYPixel( gain ) + 3; + y = gainToYPixel( gain ); float bw = m_bands[i].freq->value() / m_bands[i].res->value(); m_bands[i].x = x; m_bands[i].y = y; painter.drawPoint( x, y ); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); if(i == 0 || i == bandCount() - 1 ){ painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); } diff --git a/plugins/eq/eqparameterwidget.h b/plugins/eq/eqparameterwidget.h index cb14154b3..766efad60 100644 --- a/plugins/eq/eqparameterwidget.h +++ b/plugins/eq/eqparameterwidget.h @@ -122,6 +122,8 @@ private: enum MouseAction { none, drag, res } m_mouseAction; int m_oldX, m_oldY; + int *m_xGridBands; + inline int freqToXPixel( float freq ) { diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index ca63ff27d..47490afa3 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -63,9 +63,15 @@ public: int m_lastY; virtual void paintEvent( QPaintEvent* event ) { + const int fh = height(); + const int LOWER_Y = -96; // dB int m_lastY = height(); QPainter p( this ); p.setPen( QPen( color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + //draw grid lines + int y33 = (int)( fh * 2.0 / 3.0 * (20*(log10( 0.33 ) ) - LOWER_Y ) / (-LOWER_Y ) ); + p.drawLine(0, y33, width(), y33); + const float e = m_sa->m_energy; if( e <= 0 ) { @@ -73,9 +79,7 @@ public: return; } float * b = m_sa->m_bands; - const int LOWER_Y = -60; // dB int h; - const int fh = height(); bool linX = true; if( linX ) { From 08821a7290ef64ae1dbbb07cb0036cee92381b0b Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 13 Dec 2014 15:55:28 +0000 Subject: [PATCH 04/15] EQ update filters before downsampling changed ignore wet dry knob scale lines on widget filter type switch loading correctly analyser code all move to one location implment analyze button remove redundant code analyser display omprovments --- plugins/eq/eqcontrols.cpp | 15 +- plugins/eq/eqcontrols.h | 135 +++++----- plugins/eq/eqcontrolsdialog.cpp | 35 +-- plugins/eq/eqcontrolsdialog.h | 69 +++--- plugins/eq/eqeffect.cpp | 122 +++++---- plugins/eq/eqeffect.h | 8 +- plugins/eq/eqfader.h | 14 +- plugins/eq/eqfilter.h | 412 +++++++++++++++++-------------- plugins/eq/eqparameterwidget.cpp | 203 +++++++-------- plugins/eq/eqspectrumview.h | 148 ++++++++--- 10 files changed, 646 insertions(+), 515 deletions(-) diff --git a/plugins/eq/eqcontrols.cpp b/plugins/eq/eqcontrols.cpp index 5fe6b6c32..cb0300382 100644 --- a/plugins/eq/eqcontrols.cpp +++ b/plugins/eq/eqcontrols.cpp @@ -65,14 +65,15 @@ EqControls::EqControls( EqEffect *effect ) : m_para4ActiveModel( false, this , tr( "Peak 4 active" ) ), m_highShelfActiveModel( false, this , tr( "High shelf active" ) ), m_lpActiveModel( false, this , tr( "LP active" ) ), - m_lp12Model( true, this , tr( "LP 12" ) ), + m_lp12Model( false, this , tr( "LP 12" ) ), m_lp24Model( false, this , tr( "LP 24" ) ), m_lp48Model( false, this , tr( "LP 48" ) ), - m_hp12Model( true, this , tr( "HP 12" ) ), + m_hp12Model( false, this , tr( "HP 12" ) ), m_hp24Model( false, this , tr( "HP 24" ) ), m_hp48Model( false, this , tr( "HP 48" ) ), - m_analyzeModel( true, this , tr( "Analyze enable" ) ) - + m_analyzeModel( true, this , tr( "Analyze enable" ) ), + m_lpTypeModel(0,0,2,this, tr( "low pass type") ) , + m_hpTypeModel(0,0,2,this, tr( "high pass type") ) { m_hpFeqModel.setScaleLogarithmic( true ); m_lowShelfFreqModel.setScaleLogarithmic( true ); @@ -92,6 +93,7 @@ EqControls::EqControls( EqEffect *effect ) : m_para3PeakL = 0; m_para3PeakR = 0; m_para4PeakL = 0; m_para4PeakR = 0; m_highShelfPeakL = 0; m_highShelfPeakR = 0; + m_inProgress = false; } @@ -144,6 +146,9 @@ void EqControls::loadSettings( const QDomElement &_this ) m_hp24Model.loadSettings( _this , "HP24" ); m_hp48Model.loadSettings( _this , "HP48" ); m_analyzeModel.loadSettings( _this, "Analyzeenable"); + + m_lpTypeModel.loadSettings( _this, "LP" ); + m_hpTypeModel.loadSettings( _this, "HP" ); } @@ -197,5 +202,7 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_hp48Model.saveSettings( doc, parent, "HP48" ); m_analyzeModel.saveSettings( doc, parent, "Analyzeenable"); + m_lpTypeModel.saveSettings( doc, parent, "LP" ); + m_hpTypeModel.saveSettings( doc, parent, "HP" ); } diff --git a/plugins/eq/eqcontrols.h b/plugins/eq/eqcontrols.h index 413bf4176..f3646cce6 100644 --- a/plugins/eq/eqcontrols.h +++ b/plugins/eq/eqcontrols.h @@ -36,28 +36,28 @@ class EqControls : public EffectControls Q_OBJECT public: explicit EqControls( EqEffect* effect ); - virtual ~EqControls() - { - } - virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); - virtual void loadSettings ( const QDomElement &_this ); - inline virtual QString nodeName() const - { - return "Eq"; - } - virtual int controlCount() - { - return 39; - } - virtual EffectControlDialog* createView() - { - return new EqControlsDialog( this ); - } + virtual ~EqControls() + { + } + virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); + virtual void loadSettings ( const QDomElement &_this ); + inline virtual QString nodeName() const + { + return "Eq"; + } + virtual int controlCount() + { + return 39; + } + virtual EffectControlDialog* createView() + { + return new EqControlsDialog( this ); + } - float m_inPeakL; - float m_inPeakR; - float m_outPeakL; - float m_outPeakR; + float m_inPeakL; + float m_inPeakR; + float m_outPeakL; + float m_outPeakR; float m_lowShelfPeakL, m_lowShelfPeakR; float m_para1PeakL, m_para1PeakR; float m_para2PeakL, m_para2PeakR; @@ -65,64 +65,69 @@ public: float m_para4PeakL, m_para4PeakR; float m_highShelfPeakL, m_highShelfPeakR; - FftBands m_inFftBands; - FftBands m_outFftBands; + EqAnalyser m_inFftBands; + EqAnalyser m_outFftBands; + + bool m_inProgress; private: - EqEffect* m_effect; + EqEffect* m_effect; - FloatModel m_inGainModel; - FloatModel m_outGainModel; - FloatModel m_lowShelfGainModel; - FloatModel m_para1GainModel; - FloatModel m_para2GainModel; - FloatModel m_para3GainModel; - FloatModel m_para4GainModel; - FloatModel m_highShelfGainModel; + FloatModel m_inGainModel; + FloatModel m_outGainModel; + FloatModel m_lowShelfGainModel; + FloatModel m_para1GainModel; + FloatModel m_para2GainModel; + FloatModel m_para3GainModel; + FloatModel m_para4GainModel; + FloatModel m_highShelfGainModel; - FloatModel m_hpResModel; - FloatModel m_lowShelfResModel; - FloatModel m_para1ResModel; - FloatModel m_para2ResModel; - FloatModel m_para3ResModel; - FloatModel m_para4ResModel; - FloatModel m_highShelfResModel; - FloatModel m_lpResModel; + FloatModel m_hpResModel; + FloatModel m_lowShelfResModel; + FloatModel m_para1ResModel; + FloatModel m_para2ResModel; + FloatModel m_para3ResModel; + FloatModel m_para4ResModel; + FloatModel m_highShelfResModel; + FloatModel m_lpResModel; - FloatModel m_hpFeqModel; - FloatModel m_lowShelfFreqModel; - FloatModel m_para1FreqModel; - FloatModel m_para2FreqModel; - FloatModel m_para3FreqModel; - FloatModel m_para4FreqModel; - FloatModel m_highShelfFreqModel; - FloatModel m_lpFreqModel; + FloatModel m_hpFeqModel; + FloatModel m_lowShelfFreqModel; + FloatModel m_para1FreqModel; + FloatModel m_para2FreqModel; + FloatModel m_para3FreqModel; + FloatModel m_para4FreqModel; + FloatModel m_highShelfFreqModel; + FloatModel m_lpFreqModel; - BoolModel m_hpActiveModel; - BoolModel m_lowShelfActiveModel; - BoolModel m_para1ActiveModel; - BoolModel m_para2ActiveModel; - BoolModel m_para3ActiveModel; - BoolModel m_para4ActiveModel; - BoolModel m_highShelfActiveModel; - BoolModel m_lpActiveModel; + BoolModel m_hpActiveModel; + BoolModel m_lowShelfActiveModel; + BoolModel m_para1ActiveModel; + BoolModel m_para2ActiveModel; + BoolModel m_para3ActiveModel; + BoolModel m_para4ActiveModel; + BoolModel m_highShelfActiveModel; + BoolModel m_lpActiveModel; - BoolModel m_lp12Model; - BoolModel m_lp24Model; - BoolModel m_lp48Model; + BoolModel m_lp12Model; + BoolModel m_lp24Model; + BoolModel m_lp48Model; - BoolModel m_hp12Model; - BoolModel m_hp24Model; - BoolModel m_hp48Model; + BoolModel m_hp12Model; + BoolModel m_hp24Model; + BoolModel m_hp48Model; - BoolModel m_analyzeModel; + BoolModel m_analyzeModel; - friend class EqControlsDialog; - friend class EqEffect; + IntModel m_lpTypeModel; + IntModel m_hpTypeModel; + + friend class EqControlsDialog; + friend class EqEffect; }; diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index 1527f59fd..f6424788a 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -51,10 +51,10 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_inSpec = new EqSpectrumView( &controls->m_inFftBands, this); m_inSpec->move( 50, 5 ); - m_inSpec->color = QColor( 200, 200, 100, 100 ); + m_inSpec->color = QColor( 255, 0, 255, 80 ); m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); m_outSpec->move( 50, 5 ); - m_outSpec->color = QColor(100, 200, 200, 100); + m_outSpec->color = QColor(00, 255, 255, 80); m_parameterWidget = new EqParameterWidget( this ); m_parameterWidget->move( 50, 5 ); @@ -134,31 +134,16 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_lp48Box->setModel( &controls->m_lp48Model ); automatableButtonGroup *lpBtnGrp = new automatableButtonGroup(this,tr ( "lp grp" ) ); - lpBtnGrp->addButton(m_lp12Box); - lpBtnGrp->addButton(m_lp24Box ); - lpBtnGrp->addButton(m_lp48Box ); - connect( m_lp12Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); - connect( m_lp24Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); - connect( m_lp48Box, SIGNAL( clicked() ), lpBtnGrp , SLOT( updateButtons() ) ); - connect( &controls->m_lp12Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_lp24Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_lp48Model, SIGNAL( dataChanged() ), lpBtnGrp, SLOT( updateButtons() ) ); - - + lpBtnGrp->addButton( m_lp12Box); + lpBtnGrp->addButton( m_lp24Box ); + lpBtnGrp->addButton( m_lp48Box ); + lpBtnGrp->setModel( &m_controls->m_lpTypeModel, false); automatableButtonGroup *hpBtnGrp = new automatableButtonGroup( this, tr( "hp grp" ) ); - hpBtnGrp->addButton(m_hp12Box ); - hpBtnGrp->addButton(m_hp24Box ); - hpBtnGrp->addButton(m_hp48Box ); - connect( m_hp12Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( m_hp24Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( m_hp48Box, SIGNAL ( clicked() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_hp12Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_hp24Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); - connect( &controls->m_hp48Model, SIGNAL( dataChanged() ), hpBtnGrp, SLOT( updateButtons() ) ); - - - + hpBtnGrp->addButton( m_hp12Box ); + hpBtnGrp->addButton( m_hp24Box ); + hpBtnGrp->addButton( m_hp48Box ); + hpBtnGrp->setModel( &m_controls->m_hpTypeModel,false); //Analize Box m_analyzeBox = new LedCheckBox( tr( "Analyze" ), this ); diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/eq/eqcontrolsdialog.h index fe1326231..fd8327dce 100644 --- a/plugins/eq/eqcontrolsdialog.h +++ b/plugins/eq/eqcontrolsdialog.h @@ -33,6 +33,7 @@ #include "MainWindow.h" #include "qpushbutton.h" #include "eqspectrumview.h" +#include "qlist.h" class EqControls; @@ -40,56 +41,56 @@ class EqControlsDialog : public EffectControlDialog { public: - EqControlsDialog( EqControls* controls ); - virtual ~EqControlsDialog() - { - } + EqControlsDialog( EqControls* controls ); + virtual ~EqControlsDialog() + { + } - EqBand * setBand(EqControls *controls); + EqBand * setBand(EqControls *controls); private slots: - void updateVuMeters(); + void updateVuMeters(); private: - EqControls * m_controls; + EqControls * m_controls; - Fader* m_inGainFader; - Fader* m_outGainFader; - Fader* m_gainFader; - Knob* m_resKnob; - Knob* m_freqKnob; - LedCheckBox* m_activeBox; - LedCheckBox* m_lp12Box; - LedCheckBox* m_lp24Box; - LedCheckBox* m_lp48Box; - LedCheckBox* m_hp12Box; - LedCheckBox* m_hp24Box; - LedCheckBox* m_hp48Box; + Fader* m_inGainFader; + Fader* m_outGainFader; + Fader* m_gainFader; + Knob* m_resKnob; + Knob* m_freqKnob; + LedCheckBox* m_activeBox; + LedCheckBox* m_lp12Box; + LedCheckBox* m_lp24Box; + LedCheckBox* m_lp48Box; + LedCheckBox* m_hp12Box; + LedCheckBox* m_hp24Box; + LedCheckBox* m_hp48Box; - LedCheckBox* m_analyzeBox; + LedCheckBox* m_analyzeBox; - EqParameterWidget* m_parameterWidget; + EqParameterWidget* m_parameterWidget; EqSpectrumView* m_inSpec; EqSpectrumView* m_outSpec; - virtual void mouseDoubleClickEvent(QMouseEvent *event); + virtual void mouseDoubleClickEvent(QMouseEvent *event); EqBand* setBand( int index, BoolModel* active, FloatModel* freq, FloatModel* res, FloatModel* gain, QColor color, QString name, float* peakL, float* peakR) - { - EqBand* filterModels = m_parameterWidget->getBandModels( index ); - filterModels->active = active; - filterModels->freq = freq; - filterModels->res = res; - filterModels->color = color; - filterModels->gain = gain; + { + EqBand* filterModels = m_parameterWidget->getBandModels( index ); + filterModels->active = active; + filterModels->freq = freq; + filterModels->res = res; + filterModels->color = color; + filterModels->gain = gain; filterModels->peakL = peakL; filterModels->peakR = peakR; - return filterModels; - } - - int m_originalHeight; - + return filterModels; + } + int m_originalHeight; }; + + #endif // EQCONTROLSDIALOG_H diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp index 3808f8bf6..9c52d688b 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/eq/eqeffect.cpp @@ -59,14 +59,12 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K Effect( &eq_plugin_descriptor, parent, key ), m_eqControls( this ) { - m_dFilterCount = 10; - m_downsampleFilters = new EqLp12Filter[m_dFilterCount]; + m_dFilterCount = 20; + m_downsampleFilters = new EqLinkwitzRiley[m_dFilterCount]; for( int i = 0; i < m_dFilterCount; i++) { - m_downsampleFilters[i].setFrequency(22000); - m_downsampleFilters[i].setQ(0.85); - m_downsampleFilters[i].setGain(0); - m_downsampleFilters[i].setSampleRate(Engine::mixer()->processingSampleRate() * 2 ); + m_downsampleFilters[i].setFrequency(21500); + m_downsampleFilters[i].setSR(Engine::mixer()->processingSampleRate() * 2 ); } m_upBuf = 0; } @@ -88,15 +86,21 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { return( false ); } + m_eqControls.m_inProgress = true; double outSum = 0.0; - const float d = dryLevel(); - const float w = wetLevel(); const float outGain = m_eqControls.m_outGainModel.value(); const int sampleRate = Engine::mixer()->processingSampleRate() * 2; - sample_t dryS[2]; - sampleFrame m_inPeak; + sampleFrame m_inPeak = { 0, 0 }; + + if(m_eqControls.m_analyzeModel.value() ) + { + m_eqControls.m_inFftBands.analyze( buf, frames ); + } + else + { + m_eqControls.m_inFftBands.clear(); + } - analyze( buf, frames, &m_eqControls.m_inFftBands) ; //TODO UPSAMPLE upsample( buf, frames ); @@ -214,7 +218,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) } } - sampleFrame outPeak; + sampleFrame outPeak = { 0, 0 }; gain( m_upBuf , m_upBufFrames, outGain, &outPeak ); m_eqControls.m_outPeakL = m_eqControls.m_outPeakL < outPeak[0] ? outPeak[0] : m_eqControls.m_outPeakL; m_eqControls.m_outPeakR = m_eqControls.m_outPeakR < outPeak[1] ? outPeak[0] : m_eqControls.m_outPeakR; @@ -229,14 +233,20 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) for( fpp_t f = 0; f < frames; ++f ) { - buf[f][0] = ( d * dryS[0] ) + ( w * buf[f][0] ); - buf[f][1] = ( d * dryS[1] ) + ( w * buf[f][1] ); outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; } checkGate( outSum / frames ); - analyze( buf, frames, &m_eqControls.m_outFftBands) ; - setBandPeaks( &m_eqControls.m_outFftBands , (int)(sampleRate * 0.5)); + if(m_eqControls.m_analyzeModel.value() ) + { + m_eqControls.m_outFftBands.analyze( buf, frames ); + } + else + { + m_eqControls.m_outFftBands.clear(); + } + setBandPeaks( &m_eqControls.m_outFftBands , (int)(sampleRate * 0.5)); + m_eqControls.m_inProgress = false; return isRunning(); } @@ -259,8 +269,8 @@ void EqEffect::gain(sampleFrame *buf, const fpp_t frames, float scale, sampleFra { peak[0][1] = fabs( buf[f][0] ); } - } + } } sampleFrame m_lastUpFrame; @@ -288,54 +298,60 @@ void EqEffect::upsample(sampleFrame *buf, const fpp_t frames) m_lastUpFrame[1] = buf[f][1]; } } +} void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) { for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) { - buf[f][0] = m_upBuf[f2][0]; - buf[f][1] = m_upBuf[f2][1]; + buf[f][0] = m_upBuf[f2+1][0]; + buf[f][1] = m_upBuf[f2+1][1]; } } -void EqEffect::analyze(sampleFrame *buf, const fpp_t frames, FftBands* fft) +void EqEffect::analyze(sampleFrame *buf, const fpp_t frames, EqAnalyser* fft) { - const int FFT_BUFFER_SIZE = 2048; - fpp_t f = 0; - if( frames > FFT_BUFFER_SIZE ) + if(m_eqControls.m_analyzeModel.value() ) { + const int FFT_BUFFER_SIZE = 2048; + fpp_t f = 0; + if( frames > FFT_BUFFER_SIZE ) + { + fft->m_framesFilledUp = 0; + f = frames - FFT_BUFFER_SIZE; + } + // meger channels + for( ; f < frames; ++f ) + { + fft->m_buffer[fft->m_framesFilledUp] = + ( buf[f][0] + buf[f][1] ) * 0.5; + ++fft->m_framesFilledUp; + } + + if( fft->m_framesFilledUp < FFT_BUFFER_SIZE ) + { + return; + } + + fft->m_sr = Engine::mixer()->processingSampleRate(); + const int LOWEST_FREQ = 0; + const int HIGHEST_FREQ = fft->m_sr / 2; + + fftwf_execute( fft->m_fftPlan ); + absspec( fft->m_specBuf, fft->m_absSpecBuf, FFT_BUFFER_SIZE+1 ); + + compressbands( fft->m_absSpecBuf, fft->m_bands, FFT_BUFFER_SIZE+1, + MAX_BANDS, + (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2)), + (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2))); + fft->m_energy = maximum( fft->m_bands, MAX_BANDS ) / maximum( fft->m_buffer, FFT_BUFFER_SIZE ); fft->m_framesFilledUp = 0; - f = frames - FFT_BUFFER_SIZE; + } - // meger channels - for( ; f < frames; ++f ) - { - fft->m_buffer[fft->m_framesFilledUp] = - ( buf[f][0] + buf[f][1] ) * 0.5; - ++fft->m_framesFilledUp; - } - - if( fft->m_framesFilledUp < FFT_BUFFER_SIZE ) - { - return; - } - - fft->m_sr = Engine::mixer()->processingSampleRate(); - const int LOWEST_FREQ = 0; - const int HIGHEST_FREQ = fft->m_sr / 2; - - fftwf_execute( fft->m_fftPlan ); - absspec( fft->m_specBuf, fft->m_absSpecBuf, FFT_BUFFER_SIZE+1 ); - - compressbands( fft->m_absSpecBuf, fft->m_bands, FFT_BUFFER_SIZE+1, - MAX_BANDS, - (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2)), - (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2))); - fft->m_energy = maximum( fft->m_bands, MAX_BANDS ) / maximum( fft->m_buffer, FFT_BUFFER_SIZE ); - fft->m_framesFilledUp = 0; } -float EqEffect::peakBand(float minF, float maxF, FftBands *fft, int sr) + +float EqEffect::peakBand(float minF, float maxF, EqAnalyser *fft, int sr) { float peak = -60; float * b = fft->m_bands; @@ -351,7 +367,7 @@ float EqEffect::peakBand(float minF, float maxF, FftBands *fft, int sr) return (peak+100)/100; } -void EqEffect::setBandPeaks(FftBands *fft, int samplerate ) +void EqEffect::setBandPeaks(EqAnalyser *fft, int samplerate ) { m_eqControls.m_lowShelfPeakR = m_eqControls.m_lowShelfPeakL = peakBand( 0, @@ -399,4 +415,4 @@ Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) return new EqEffect( parent , static_cast( data ) ); } -}} +} diff --git a/plugins/eq/eqeffect.h b/plugins/eq/eqeffect.h index 0ea793351..1fc96c077 100644 --- a/plugins/eq/eqeffect.h +++ b/plugins/eq/eqeffect.h @@ -66,7 +66,7 @@ private: EqLp12Filter m_lp24; EqLp12Filter m_lp480; EqLp12Filter m_lp481; - EqLp12Filter* m_downsampleFilters; + EqLinkwitzRiley* m_downsampleFilters; int m_dFilterCount; sampleFrame* m_upBuf; fpp_t m_upBufFrames; @@ -74,15 +74,15 @@ private: // const static int MAX_BANDS = 249; void upsample( sampleFrame *buf, const fpp_t frames ); void downSample( sampleFrame *buf, const fpp_t frames ); - void analyze( sampleFrame *buf, const fpp_t frames, FftBands* fft ); - float peakBand(float minF, float maxF,FftBands*, int); + void analyze( sampleFrame *buf, const fpp_t frames, EqAnalyser* fft ); + float peakBand(float minF, float maxF,EqAnalyser*, int); inline float bandToFreq ( int index , int sampleRate ) { return index * sampleRate / (MAX_BANDS * 2); } - void setBandPeaks( FftBands *fft , int); + void setBandPeaks( EqAnalyser *fft , int); // float m_bands[MAX_BANDS]; // float m_energy; diff --git a/plugins/eq/eqfader.h b/plugins/eq/eqfader.h index 65c7a55a0..1057b60c0 100644 --- a/plugins/eq/eqfader.h +++ b/plugins/eq/eqfader.h @@ -47,14 +47,6 @@ public: m_lPeak = lPeak; m_rPeak = rPeak; connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( updateVuMeters() ) ); - m_text = 0; - QList tfs = Engine::mainWindow()->findChildren(); - m_text = tfs.last(); - if(m_text) - { - printf("found text float\n"); - } - connect(model , SIGNAL (dataChanged()) , this, SLOT (updateText () )); m_model = model; } @@ -95,16 +87,12 @@ private slots: update(); } - void updateText() - { - m_text->setText(QString()+m_model->value()); - } + private: float* m_lPeak; float* m_rPeak; - TextFloat* m_text; FloatModel* m_model; }; diff --git a/plugins/eq/eqfilter.h b/plugins/eq/eqfilter.h index 69ee49c8b..e6924ca4c 100644 --- a/plugins/eq/eqfilter.h +++ b/plugins/eq/eqfilter.h @@ -38,74 +38,74 @@ class EqFilter : public StereoBiQuad { public: - EqFilter() - { + EqFilter() + { - } + } - virtual inline void setSampleRate( int sampleRate ) - { - if( sampleRate != m_sampleRate ) - { - m_sampleRate = sampleRate; - calcCoefficents(); - } - } + virtual inline void setSampleRate( int sampleRate ) + { + if( sampleRate != m_sampleRate ) + { + m_sampleRate = sampleRate; + calcCoefficents(); + } + } - virtual inline void setFrequency( float freq ){ - if ( freq != m_freq ) - { - m_freq = freq; - calcCoefficents(); - } - } + virtual inline void setFrequency( float freq ){ + if ( freq != m_freq ) + { + m_freq = freq; + calcCoefficents(); + } + } - virtual void setQ( float res ) - { - if ( res != m_res ) - { - m_res = res; - calcCoefficents(); - } - } + virtual void setQ( float res ) + { + if ( res != m_res ) + { + m_res = res; + calcCoefficents(); + } + } - virtual void setGain( float gain ) - { - if ( gain != m_gain ) - { - m_gain = gain; - calcCoefficents(); - } - } + virtual void setGain( float gain ) + { + if ( gain != m_gain ) + { + m_gain = gain; + calcCoefficents(); + } + } - /// - /// \brief processBuffer - /// \param buf Audio Buffer - /// \param frames Count of sampleFrames in Audio Buffer - /// - virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) - { - for ( fpp_t f = 0 ; f < frames ; ++f) - { - buf[f][0] = update( buf[f][0] , 0); - buf[f][1] = update( buf[f][1] , 1); - } - } + /// + /// \brief processBuffer + /// \param buf Audio Buffer + /// \param frames Count of sampleFrames in Audio Buffer + /// + virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) + { + for ( fpp_t f = 0 ; f < frames ; ++f) + { + buf[f][0] = update( buf[f][0] , 0); + buf[f][1] = update( buf[f][1] , 1); + } + } protected: - /// - /// \brief calcCoefficents - /// Override this in child classes to provide the coefficents, based on - /// Freq, Res and Gain - virtual void calcCoefficents(){ - setCoeffs( 0, 0, 0, 0, 0 ); + /// + /// \brief calcCoefficents + /// Override this in child classes to provide the coefficents, based on + /// Freq, Res and Gain + virtual void calcCoefficents(){ + setCoeffs( 0, 0, 0, 0, 0 ); - } + } - float m_sampleRate; - float m_freq; - float m_res; - float m_gain; + float m_sampleRate; + float m_freq; + float m_res; + float m_gain; }; @@ -118,38 +118,38 @@ protected: class EqHp12Filter : public EqFilter { public : - virtual void calcCoefficents() - { + virtual void calcCoefficents() + { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float alpha = s / ( 2 * m_res ); + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = ( 1 + c ) * 0.5; - b1 = ( -( 1 + c ) ); - b2 = ( 1 + c ) * 0.5; - a0 = 1 + alpha; - a1 = ( -2 * c ); - a2 = 1 - alpha; + //calc coefficents + b0 = ( 1 + c ) * 0.5; + b1 = ( -( 1 + c ) ); + b2 = ( 1 + c ) * 0.5; + a0 = 1 + alpha; + a1 = ( -2 * c ); + a2 = 1 - alpha; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; - a0 = 1; + a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); + setCoeffs( a1, a2, b0, b1, b2 ); - } + } }; @@ -161,36 +161,36 @@ public : class EqLp12Filter : public EqFilter { public : - virtual void calcCoefficents() - { + virtual void calcCoefficents() + { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float alpha = s / ( 2 * m_res ); + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = ( 1 - c ) * 0.5; - b1 = 1 - c; - b2 = ( 1 - c ) * 0.5; - a0 = 1 + alpha; - a1 = -2 * c; - a2 = 1 - alpha; + //calc coefficents + b0 = ( 1 - c ) * 0.5; + b1 = 1 - c; + b2 = ( 1 - c ) * 0.5; + a0 = 1 + alpha; + a1 = -2 * c; + a2 = 1 - alpha; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; - a0 = 1; + a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); - } + setCoeffs( a1, a2, b0, b1, b2 ); + } }; @@ -205,108 +205,156 @@ class EqPeakFilter : public EqFilter public: - virtual void calcCoefficents() - { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float A = pow( 10, m_gain * 0.025); - float alpha = s / ( 2 * m_res ); + virtual void calcCoefficents() + { + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); + float alpha = s / ( 2 * m_res ); - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = 1 + alpha*A; - b1 = -2*c; - b2 = 1 - alpha*A; - a0 = 1 + alpha/A; - a1 = -2*c; - a2 = 1 - alpha/A; + //calc coefficents + b0 = 1 + alpha*A; + b1 = -2*c; + b2 = 1 - alpha*A; + a0 = 1 + alpha/A; + a1 = -2*c; + a2 = 1 - alpha/A; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; - a0 = 1; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); - } + setCoeffs( a1, a2, b0, b1, b2 ); + } }; class EqLowShelfFilter : public EqFilter { public : - virtual void calcCoefficents() - { + virtual void calcCoefficents() + { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float A = pow( 10, m_gain * 0.025); -// float alpha = s / ( 2 * m_res ); - float beta = sqrt( A ) / m_res; + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); + // float alpha = s / ( 2 * m_res ); + float beta = sqrt( A ) / m_res; - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate - //calc coefficents - b0 = A * ( ( A+1 ) - ( A-1 ) * c + beta * s ); - b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * c) ; - b2 = A * ( ( A + 1 ) - ( A - 1 ) * c - beta * s); - a0 = ( A + 1 ) + ( A - 1 ) * c + beta * s; - a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * c ); - a2 = ( A + 1 ) + ( A - 1) * c - beta * s; + //calc coefficents + b0 = A * ( ( A+1 ) - ( A-1 ) * c + beta * s ); + b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * c) ; + b2 = A * ( ( A + 1 ) - ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) + ( A - 1 ) * c + beta * s; + a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * c ); + a2 = ( A + 1 ) + ( A - 1) * c - beta * s; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; - a0 = 1; + a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); + setCoeffs( a1, a2, b0, b1, b2 ); - } + } }; class EqHighShelfFilter : public EqFilter { public : - virtual void calcCoefficents() - { + virtual void calcCoefficents() + { - // calc intermediate - float w0 = F_2PI * m_freq / m_sampleRate; - float c = cosf( w0 ); - float s = sinf( w0 ); - float A = pow( 10, m_gain * 0.025 ); - float beta = sqrt( A ) / m_res; + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025 ); + float beta = sqrt( A ) / m_res; - float a0, a1, a2, b0, b1, b2; // coeffs to calculate + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = A *( ( A +1 ) + ( A - 1 ) * c + beta * s); + b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * c ); + b2 = A * ( ( A + 1 ) + ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) - ( A - 1 ) * c + beta * s; + a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * c ); + a2 = ( A + 1) - ( A - 1 ) * c - beta * s; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + +class EqLinkwitzRiley : public StereoLinkwitzRiley +{ +public: + EqLinkwitzRiley() : + StereoLinkwitzRiley( 44100) + { + } + + virtual inline void setSR( int sampleRate ) + { + if( sampleRate != m_sr ) + { + m_sr = sampleRate; + setLowpass(m_freq); + setSampleRate( sampleRate ); + } + } + + + + + virtual inline void setFrequency( float freq ){ + if ( freq != m_freq ) + { + m_freq = freq; + setLowpass(m_freq); + } + } + + + + + virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) + { + for ( fpp_t f = 0 ; f < frames ; ++f) + { + buf[f][0] = update( buf[f][0] , 0); + buf[f][1] = update( buf[f][1] , 1); + } + } +protected: + + float m_freq; + int m_sr; - //calc coefficents - b0 = A *( ( A +1 ) + ( A - 1 ) * c + beta * s); - b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * c ); - b2 = A * ( ( A + 1 ) + ( A - 1 ) * c - beta * s); - a0 = ( A + 1 ) - ( A - 1 ) * c + beta * s; - a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * c ); - a2 = ( A + 1) - ( A - 1 ) * c - beta * s; - //normalise - b0 /= a0; - b1 /= a0; - b2 /= a0; - a1 /= a0; - a2 /= a0; - a0 = 1; - setCoeffs( a1, a2, b0, b1, b2 ); - } }; diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index f1d53fa2e..059555108 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -31,19 +31,22 @@ #include "QMouseEvent" EqParameterWidget::EqParameterWidget( QWidget *parent ) : - QWidget( parent ), - m_bands ( 0 ), - m_selectedBand ( 0 ) + QWidget( parent ), + m_bands ( 0 ), + m_selectedBand ( 0 ) { - m_bands = new EqBand[8]; - resize( 250, 116 ); - connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); - float totalLength = log10( 21000 ); - m_pixelsPerUnitWidth = width( ) / totalLength ; + m_bands = new EqBand[8]; + resize( 250, 116 ); + // connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(200); + float totalLength = log10( 21000 ); + m_pixelsPerUnitWidth = width( ) / totalLength ; float totalHeight = 80; - m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); - m_scale = 1.5; - m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); + m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); + m_scale = 1.5; + m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); } @@ -78,39 +81,39 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) //draw 0dB line painter.drawLine(0, gainToYPixel( 0 ) , width(), gainToYPixel( 0 ) ); - for( int i = 0 ; i < bandCount() ; i++ ) - { + for( int i = 0 ; i < bandCount() ; i++ ) + { - m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); + m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - float x = freqToXPixel( m_bands[i].freq->value() ); - float y = height() * 0.5; - float gain = 1; - if( m_bands[i].gain ) - { - gain = m_bands[i].gain->value(); - } + float x = freqToXPixel( m_bands[i].freq->value() ); + float y = height() * 0.5; + float gain = 1; + if( m_bands[i].gain ) + { + gain = m_bands[i].gain->value(); + } y = gainToYPixel( gain ); - float bw = m_bands[i].freq->value() / m_bands[i].res->value(); - m_bands[i].x = x; m_bands[i].y = y; - painter.drawPoint( x, y ); + float bw = m_bands[i].freq->value() / m_bands[i].res->value(); + m_bands[i].x = x; m_bands[i].y = y; + painter.drawPoint( x, y ); painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); - if(i == 0 || i == bandCount() - 1 ){ - painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); - } - else - { - painter.drawLine(freqToXPixel(m_bands[i].freq->value()-(bw * 0.5)),y,freqToXPixel(m_bands[i].freq->value()+(bw * 0.5)),y); - } - } - //Draw color band - int sectionLength = width() / bandCount(); - for( int i = 0; i < bandCount(); i++) - { - m_bands[i].color.setAlpha( 255 ); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - painter.drawLine(sectionLength * i , 1, sectionLength * (i+1) , 1); - } + if(i == 0 || i == bandCount() - 1 ){ + painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); + } + else + { + painter.drawLine(freqToXPixel(m_bands[i].freq->value()-(bw * 0.5)),y,freqToXPixel(m_bands[i].freq->value()+(bw * 0.5)),y); + } + } + //Draw color band + int sectionLength = width() / bandCount(); + for( int i = 0; i < bandCount(); i++) + { + m_bands[i].color.setAlpha( 255 ); + painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.drawLine(sectionLength * i , 1, sectionLength * (i+1) , 1); + } } @@ -118,11 +121,11 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) void EqParameterWidget::mousePressEvent( QMouseEvent *event ) { - m_oldX = event->x(); m_oldY = event->y(); - m_selectedBand = selectNearestHandle( event->x(), event->y() ); - m_mouseAction = none; - if ( event->button() == Qt::LeftButton ) m_mouseAction = drag; - if ( event->button() == Qt::RightButton ) m_mouseAction = res; + m_oldX = event->x(); m_oldY = event->y(); + m_selectedBand = selectNearestHandle( event->x(), event->y() ); + m_mouseAction = none; + if ( event->button() == Qt::LeftButton ) m_mouseAction = drag; + if ( event->button() == Qt::RightButton ) m_mouseAction = res; } @@ -130,8 +133,8 @@ void EqParameterWidget::mousePressEvent( QMouseEvent *event ) void EqParameterWidget::mouseReleaseEvent( QMouseEvent *event ) { - m_selectedBand = 0; - m_mouseAction = none; + m_selectedBand = 0; + m_mouseAction = none; } @@ -139,26 +142,26 @@ void EqParameterWidget::mouseReleaseEvent( QMouseEvent *event ) void EqParameterWidget::mouseMoveEvent( QMouseEvent *event ) { - int deltaX = event->x() - m_oldX; - int deltaR = event->y() - m_oldY; - m_oldX = event->x(); m_oldY = event->y(); - if(m_selectedBand && m_selectedBand->active->value() ) - { - switch ( m_mouseAction ) { - case none : - break; - case drag: - if( m_selectedBand->freq ) m_selectedBand->freq->setValue( xPixelToFreq( m_oldX ) ); - if( m_selectedBand->gain )m_selectedBand->gain->setValue( yPixelToGain( m_oldY ) ); - break; - case res: - if( m_selectedBand->res )m_selectedBand->res->incValue( deltaX * resPixelMultiplyer() ); - if( m_selectedBand->res )m_selectedBand->res->incValue( (-deltaR) * resPixelMultiplyer() ); - break; - default: - break; - } - } + int deltaX = event->x() - m_oldX; + int deltaR = event->y() - m_oldY; + m_oldX = event->x(); m_oldY = event->y(); + if(m_selectedBand && m_selectedBand->active->value() ) + { + switch ( m_mouseAction ) { + case none : + break; + case drag: + if( m_selectedBand->freq ) m_selectedBand->freq->setValue( xPixelToFreq( m_oldX ) ); + if( m_selectedBand->gain )m_selectedBand->gain->setValue( yPixelToGain( m_oldY ) ); + break; + case res: + if( m_selectedBand->res )m_selectedBand->res->incValue( deltaX * resPixelMultiplyer() ); + if( m_selectedBand->res )m_selectedBand->res->incValue( (-deltaR) * resPixelMultiplyer() ); + break; + default: + break; + } + } } @@ -166,11 +169,11 @@ void EqParameterWidget::mouseMoveEvent( QMouseEvent *event ) void EqParameterWidget::mouseDoubleClickEvent( QMouseEvent *event ) { - EqBand* selected = selectNearestHandle( event->x() , event->y() ); - if( selected ) - { - selected->active->setValue( selected->active->value() ? 0 : 1 ); - } + EqBand* selected = selectNearestHandle( event->x() , event->y() ); + if( selected ) + { + selected->active->setValue( selected->active->value() ? 0 : 1 ); + } } @@ -178,39 +181,39 @@ void EqParameterWidget::mouseDoubleClickEvent( QMouseEvent *event ) EqBand* EqParameterWidget::selectNearestHandle( const int x, const int y ) { - EqBand* selectedModel = 0; - float* distanceToHandles = new float[bandCount()]; - //calc distance to each handle - for( int i = 0 ; i < bandCount() ; i++) - { - int xOffset = m_bands[i].x - x; - int yOffset = m_bands[i].y - y; - distanceToHandles[i] = fabs(sqrt((xOffset * xOffset ) + ( yOffset * yOffset ) ) ); - } - //select band - int shortestBand = 0; - for (int i = 1 ; i < bandCount() ; i++ ) - { - if ( distanceToHandles [i] < distanceToHandles[shortestBand] ){ - shortestBand = i; - } - } - if(distanceToHandles[shortestBand] < maxDistanceFromHandle() ) - { - selectedModel = &m_bands[shortestBand]; - } - delete distanceToHandles; - return selectedModel; + EqBand* selectedModel = 0; + float* distanceToHandles = new float[bandCount()]; + //calc distance to each handle + for( int i = 0 ; i < bandCount() ; i++) + { + int xOffset = m_bands[i].x - x; + int yOffset = m_bands[i].y - y; + distanceToHandles[i] = fabs(sqrt((xOffset * xOffset ) + ( yOffset * yOffset ) ) ); + } + //select band + int shortestBand = 0; + for (int i = 1 ; i < bandCount() ; i++ ) + { + if ( distanceToHandles [i] < distanceToHandles[shortestBand] ){ + shortestBand = i; + } + } + if(distanceToHandles[shortestBand] < maxDistanceFromHandle() ) + { + selectedModel = &m_bands[shortestBand]; + } + delete distanceToHandles; + return selectedModel; } EqBand::EqBand() : - gain ( 0 ), - res ( 0 ), - freq ( 0 ), - color ( QColor( 255, 255, 255 ) ), - name ( QString( "" ) ) + gain ( 0 ), + res ( 0 ), + freq ( 0 ), + color ( QColor( 255, 255, 255 ) ), + name ( QString( "" ) ) { } diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index 47490afa3..3054f0b95 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -1,3 +1,26 @@ + +/* eqspectrumview.h - defination of EqSpectrumView class. +* +* Copyright (c) 2014 David French +* +* 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 EQSPECTRUMVIEW_H #define EQSPECTRUMVIEW_H @@ -5,11 +28,12 @@ //#include "eqeffect.h" #include "qwidget.h" #include "fft_helpers.h" +#include "Engine.h" -const int MAX_BANDS = 512; +const int MAX_BANDS = 2048; -class FftBands +class EqAnalyser { public: @@ -24,15 +48,76 @@ public: float m_energy; int m_sr; - FftBands() : - m_framesFilledUp( 0 ), - m_energy( 0 ) - { - memset( m_buffer, 0, sizeof( m_buffer ) ); + EqAnalyser() + { + m_inProgress=false; + clear(); + } + + + + + bool getInProgress() + { + return m_inProgress; + } + + + + void clear() + { + m_framesFilledUp = 0; + m_energy = 0; + memset( m_buffer, 0, sizeof( m_buffer ) ); m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE ); + } + + + + void analyze(sampleFrame *buf, const fpp_t frames ) + { + m_inProgress=true; + const int FFT_BUFFER_SIZE = 2048; + fpp_t f = 0; + if( frames > FFT_BUFFER_SIZE ) + { + m_framesFilledUp = 0; + f = frames - FFT_BUFFER_SIZE; + } + // meger channels + for( ; f < frames; ++f ) + { + m_buffer[m_framesFilledUp] = + ( buf[f][0] + buf[f][1] ) * 0.5; + ++m_framesFilledUp; + } + + if( m_framesFilledUp < FFT_BUFFER_SIZE ) + { + m_inProgress = false; + return; + } + + m_sr = Engine::mixer()->processingSampleRate(); + const int LOWEST_FREQ = 0; + const int HIGHEST_FREQ = m_sr / 2; + + fftwf_execute( m_fftPlan ); + absspec( m_specBuf, m_absSpecBuf, FFT_BUFFER_SIZE+1 ); + + compressbands( m_absSpecBuf, m_bands, FFT_BUFFER_SIZE+1, + MAX_BANDS, + (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(m_sr /2)), + (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(m_sr /2))); + m_energy = maximum( m_bands, MAX_BANDS ) / maximum( m_buffer, FFT_BUFFER_SIZE ); + m_framesFilledUp = 0; + m_inProgress = false; + } +private: + bool m_inProgress; }; @@ -40,12 +125,15 @@ class EqSpectrumView : public QWidget { public: - explicit EqSpectrumView( FftBands * b, QWidget * _parent = 0) : + explicit EqSpectrumView( EqAnalyser * b, QWidget * _parent = 0) : QWidget( _parent ), m_sa( b ) { setFixedSize( 250, 116 ); - connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + // connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + QTimer *timer = new QTimer(this); + connect(timer, SIGNAL(timeout()), this, SLOT(update())); + timer->start(2000); setAttribute( Qt::WA_TranslucentBackground, true ); m_skipBands = MAX_BANDS * 0.5; float totalLength = log10( 21000); @@ -59,47 +147,37 @@ public: { } QColor color; - FftBands *m_sa; - int m_lastY; + EqAnalyser *m_sa; + QPainterPath pp; virtual void paintEvent( QPaintEvent* event ) { const int fh = height(); - const int LOWER_Y = -96; // dB - int m_lastY = height(); + const int LOWER_Y = -60; // dB QPainter p( this ); p.setPen( QPen( color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - //draw grid lines - int y33 = (int)( fh * 2.0 / 3.0 * (20*(log10( 0.33 ) ) - LOWER_Y ) / (-LOWER_Y ) ); - p.drawLine(0, y33, width(), y33); - const float e = m_sa->m_energy; if( e <= 0 ) { //dont draw anything return; } + if(m_sa->getInProgress() ){ + p.fillPath( pp ,QBrush( color ) ); + return; + } + pp = QPainterPath(); float * b = m_sa->m_bands; int h; - bool linX = true; - if( linX ) + pp.moveTo( 0,height() ); + for( int x = 0; x < MAX_BANDS; ++x, ++b ) { - for( int x = 0; x < MAX_BANDS; ++x, ++b ) - { - h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); - if( h < 0 ) h = 0; else if( h >= fh ) continue; - p.drawLine(freqToXPixel(bandToFreq(x -1 ) ),m_lastY, freqToXPixel(bandToFreq(x ) ), fh-h ); - m_lastY = fh-h; - } - } - else - { - for( int x = 0; x < 31; ++x, ++b ) - { - h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); - if( h < 0 ) h = 0; else if( h >= fh ) continue; else h = ( h / 3 ) * 3; - p.drawPoint(x * 8, fh-h ); - } + h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); + if( h < 0 ) h = 0; else if( h >= fh ) continue; + pp.lineTo( freqToXPixel(bandToFreq(x ) ), fh-h ); } + pp.lineTo(width(), height() ); + pp.closeSubpath(); + p.fillPath( pp ,QBrush( color ) ); } From 77fd245c48753618f1bf2e3bd96675e31e2c45b1 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 13 Dec 2014 20:35:50 +0000 Subject: [PATCH 05/15] EQ updates to UI, Initial Ui complete --- plugins/eq/artwork.png | Bin 50584 -> 27381 bytes plugins/eq/eqcontrolsdialog.cpp | 48 +++++++++++++++++++------------ plugins/eq/eqeffect.cpp | 4 +-- plugins/eq/eqparameterwidget.cpp | 2 +- 4 files changed, 33 insertions(+), 21 deletions(-) diff --git a/plugins/eq/artwork.png b/plugins/eq/artwork.png index 8a4a0418b87ed04c2670276d2cd4b4e5c506439a..73b3473b4f8cd1f390ece29ff24c4754775ea2d0 100644 GIT binary patch literal 27381 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
A`poYU)s$P)mrMz-@#cT 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

qxWw53E{N(<}UdctETZi=&tfo^gY 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

-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 diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index f6424788a..0633a8cf5 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -51,7 +51,7 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_inSpec = new EqSpectrumView( &controls->m_inFftBands, this); m_inSpec->move( 50, 5 ); - m_inSpec->color = QColor( 255, 0, 255, 80 ); + m_inSpec->color = QColor( 255, 0, 255, 150 ); m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); m_outSpec->move( 50, 5 ); m_outSpec->color = QColor(00, 255, 255, 80); @@ -92,45 +92,57 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : for( int i = 0; i < m_parameterWidget->bandCount() ; i++) { m_resKnob = new Knob( knobBright_26, this ); - m_resKnob->move(cw * i + ko , 205 ); + if(i ==0 || i == 7) + { + m_resKnob->move(cw * i + ko , 190 ); + } else + { + m_resKnob->move(cw * i + ko , 205 ); + } m_resKnob->setVolumeKnob(false); m_resKnob->setModel( m_parameterWidget->getBandModels( i )->res ); m_resKnob->setHintText( tr( "Resonance:") , ""); m_freqKnob = new Knob( knobBright_26, this ); - m_freqKnob->move(cw * i + ko, 235 ); + if( i == 0 || i == 7 ) + { + m_freqKnob->move( cw * i + ko, 222 ); + } else + { + m_freqKnob->move(cw * i + ko, 235 ); + } m_freqKnob->setVolumeKnob( false ); m_freqKnob->setModel( m_parameterWidget->getBandModels( i )->freq ); m_freqKnob->setHintText( tr( "Frequency:" ), "Hz" ); - m_activeBox = new LedCheckBox( m_parameterWidget->getBandModels( i )->name , this ); + m_activeBox = new LedCheckBox( m_parameterWidget->getBandModels( i )->name , this , "" , LedCheckBox::Green ); m_activeBox->move( cw * i + fo + 3, 260 ); m_activeBox->setModel( m_parameterWidget->getBandModels( i )->active ); } //hp filter type - m_hp12Box = new LedCheckBox( tr( "12dB" ), this ); - m_hp12Box->move( cw*0 + ko, 175 ); + m_hp12Box = new LedCheckBox( tr( "12dB" ), this , "" , LedCheckBox::Green ); + m_hp12Box->move( cw*0 + ko, 170 ); m_hp12Box->setModel( &controls->m_hp12Model ); - m_hp24Box = new LedCheckBox( tr( "24dB" ), this ); - m_hp24Box->move( cw*0 + ko, 155 ); + m_hp24Box = new LedCheckBox( tr( "24dB" ), this , "" , LedCheckBox::Green ); + m_hp24Box->move( cw*0 + ko, 150 ); m_hp24Box->setModel( &controls->m_hp24Model ); - m_hp48Box = new LedCheckBox( tr( "48dB" ), this ); - m_hp48Box->move( cw*0 + ko, 135 ); + m_hp48Box = new LedCheckBox( tr( "48dB" ), this , "" , LedCheckBox::Green ); + m_hp48Box->move( cw*0 + ko, 130 ); m_hp48Box->setModel( &controls->m_hp48Model ); //LP filter type - m_lp12Box = new LedCheckBox( tr( "12dB"), this ); - m_lp12Box->move( cw*7 + ko -5 , 175 ); + m_lp12Box = new LedCheckBox( tr( "12dB"), this , "" , LedCheckBox::Green ); + m_lp12Box->move( cw*7 + ko -5 , 170 ); m_lp12Box->setModel( &controls->m_lp12Model ); - m_lp24Box = new LedCheckBox( tr( "24dB"), this ); - m_lp24Box->move( cw*7 + ko - 5, 155 ); + m_lp24Box = new LedCheckBox( tr( "24dB"), this , "" , LedCheckBox::Green ); + m_lp24Box->move( cw*7 + ko - 5, 150 ); m_lp24Box->setModel( &controls->m_lp24Model ); - m_lp48Box = new LedCheckBox( tr( "48dB"), this ); - m_lp48Box->move( cw*7 + ko - 5, 135 ); + m_lp48Box = new LedCheckBox( tr( "48dB"), this , "" , LedCheckBox::Green ); + m_lp48Box->move( cw*7 + ko - 5, 130 ); m_lp48Box->setModel( &controls->m_lp48Model ); automatableButtonGroup *lpBtnGrp = new automatableButtonGroup(this,tr ( "lp grp" ) ); @@ -146,8 +158,8 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : hpBtnGrp->setModel( &m_controls->m_hpTypeModel,false); //Analize Box - m_analyzeBox = new LedCheckBox( tr( "Analyze" ), this ); - m_analyzeBox->move( cw*1 + ko + 5, 10 ); + m_analyzeBox = new LedCheckBox( tr( "Analyze" ), this , "" , LedCheckBox::Green ); + m_analyzeBox->move( cw*1 + ko + 5, 15 ); m_analyzeBox->setModel( &controls->m_analyzeModel ); } diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp index 9c52d688b..992aab69b 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/eq/eqeffect.cpp @@ -59,11 +59,11 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K Effect( &eq_plugin_descriptor, parent, key ), m_eqControls( this ) { - m_dFilterCount = 20; + m_dFilterCount = 6; m_downsampleFilters = new EqLinkwitzRiley[m_dFilterCount]; for( int i = 0; i < m_dFilterCount; i++) { - m_downsampleFilters[i].setFrequency(21500); + m_downsampleFilters[i].setFrequency(20000); m_downsampleFilters[i].setSR(Engine::mixer()->processingSampleRate() * 2 ); } m_upBuf = 0; diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index 059555108..6a3ae7aa1 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -40,7 +40,7 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : // connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); - timer->start(200); + timer->start(100); float totalLength = log10( 21000 ); m_pixelsPerUnitWidth = width( ) / totalLength ; float totalHeight = 80; From f64ca3ff454641381121fb080e6c325473e70ef9 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 13 Dec 2014 21:54:50 +0000 Subject: [PATCH 06/15] EQ Clean up of code --- plugins/eq/eqcontrols.cpp | 19 +------ plugins/eq/eqcontrolsdialog.cpp | 6 +- plugins/eq/eqcontrolsdialog.h | 4 +- plugins/eq/eqeffect.cpp | 94 ++++++++------------------------ plugins/eq/eqeffect.h | 4 +- plugins/eq/eqfilter.h | 23 ++++++++ plugins/eq/eqparameterwidget.cpp | 17 +++--- plugins/eq/eqspectrumview.h | 34 ++++++------ 8 files changed, 76 insertions(+), 125 deletions(-) diff --git a/plugins/eq/eqcontrols.cpp b/plugins/eq/eqcontrols.cpp index cb0300382..82cb11191 100644 --- a/plugins/eq/eqcontrols.cpp +++ b/plugins/eq/eqcontrols.cpp @@ -23,7 +23,6 @@ */ #include - #include "eqcontrols.h" #include "eqeffect.h" @@ -72,8 +71,8 @@ EqControls::EqControls( EqEffect *effect ) : m_hp24Model( false, this , tr( "HP 24" ) ), m_hp48Model( false, this , tr( "HP 48" ) ), m_analyzeModel( true, this , tr( "Analyze enable" ) ), - m_lpTypeModel(0,0,2,this, tr( "low pass type") ) , - m_hpTypeModel(0,0,2,this, tr( "high pass type") ) + m_lpTypeModel( 0,0,2,this, tr( "low pass type") ) , + m_hpTypeModel( 0,0,2,this, tr( "high pass type") ) { m_hpFeqModel.setScaleLogarithmic( true ); m_lowShelfFreqModel.setScaleLogarithmic( true ); @@ -83,7 +82,6 @@ EqControls::EqControls( EqEffect *effect ) : m_para4FreqModel.setScaleLogarithmic( true ); m_highShelfFreqModel.setScaleLogarithmic( true ); m_lpFreqModel.setScaleLogarithmic( true ); - m_para1GainModel.setScaleLogarithmic( true ); m_inPeakL = 0; m_inPeakR = 0; @@ -94,7 +92,6 @@ EqControls::EqControls( EqEffect *effect ) : m_para4PeakL = 0; m_para4PeakR = 0; m_highShelfPeakL = 0; m_highShelfPeakR = 0; m_inProgress = false; - } @@ -110,7 +107,6 @@ void EqControls::loadSettings( const QDomElement &_this ) m_para3GainModel.loadSettings( _this, "Peak3gain" ); m_para4GainModel.loadSettings( _this, "Peak4gain" ); m_highShelfGainModel.loadSettings( _this , "HighShelfgain"); - m_hpResModel.loadSettings( _this ,"HPres"); m_lowShelfResModel.loadSettings( _this, "LowShelfres" ); m_para1ResModel.loadSettings( _this ,"Peak1res" ); @@ -119,7 +115,6 @@ void EqControls::loadSettings( const QDomElement &_this ) m_para4ResModel.loadSettings( _this ,"Peak4res" ); m_highShelfResModel.loadSettings( _this, "HighShelfres" ); m_lpResModel.loadSettings( _this, "LPres"); - m_hpFeqModel.loadSettings( _this, "HPfreq" ); m_lowShelfFreqModel.loadSettings( _this, "LowShelffreq" ); m_para1FreqModel.loadSettings( _this, "Peak1freq" ); @@ -128,7 +123,6 @@ void EqControls::loadSettings( const QDomElement &_this ) m_para4FreqModel.loadSettings( _this, "Peak4freq" ); m_highShelfFreqModel.loadSettings( _this, "Highshelffreq" ); m_lpFreqModel.loadSettings( _this, "LPfreq" ); - m_hpActiveModel.loadSettings( _this, "HPactive" ); m_lowShelfActiveModel.loadSettings( _this, "Lowshelfactive" ); m_para1ActiveModel.loadSettings( _this, "Peak1active"); @@ -137,16 +131,13 @@ void EqControls::loadSettings( const QDomElement &_this ) m_para4ActiveModel.loadSettings( _this, "Peak4active"); m_highShelfActiveModel.loadSettings( _this, "Highshelfactive" ); m_lpActiveModel.loadSettings( _this, "LPactive" ); - m_lp12Model.loadSettings( _this , "LP12" ); m_lp24Model.loadSettings( _this , "LP24" ); m_lp48Model.loadSettings( _this , "LP48" ); - m_hp12Model.loadSettings( _this , "HP12" ); m_hp24Model.loadSettings( _this , "HP24" ); m_hp48Model.loadSettings( _this , "HP48" ); m_analyzeModel.loadSettings( _this, "Analyzeenable"); - m_lpTypeModel.loadSettings( _this, "LP" ); m_hpTypeModel.loadSettings( _this, "HP" ); } @@ -165,7 +156,6 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_para3GainModel.saveSettings( doc, parent, "Peak3gain" ); m_para4GainModel.saveSettings( doc, parent, "Peak4gain" ); m_highShelfGainModel.saveSettings( doc, parent, "HighShelfgain"); - m_hpResModel.saveSettings( doc, parent ,"HPres"); m_lowShelfResModel.saveSettings( doc, parent, "LowShelfres" ); m_para1ResModel.saveSettings( doc, parent,"Peak1res" ); @@ -174,7 +164,6 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_para4ResModel.saveSettings( doc, parent,"Peak4res" ); m_highShelfResModel.saveSettings( doc, parent, "HighShelfres" ); m_lpResModel.saveSettings( doc, parent, "LPres"); - m_hpFeqModel.saveSettings( doc, parent, "HPfreq" ); m_lowShelfFreqModel.saveSettings( doc, parent, "LowShelffreq" ); m_para1FreqModel.saveSettings( doc, parent, "Peak1freq" ); @@ -183,7 +172,6 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_para4FreqModel.saveSettings( doc, parent, "Peak4freq" ); m_highShelfFreqModel.saveSettings( doc, parent, "Highshelffreq" ); m_lpFreqModel.saveSettings( doc, parent, "LPfreq" ); - m_hpActiveModel.saveSettings( doc, parent, "HPactive" ); m_lowShelfActiveModel.saveSettings( doc, parent, "Lowshelfactive" ); m_para1ActiveModel.saveSettings( doc, parent, "Peak1active" ); @@ -192,16 +180,13 @@ void EqControls::saveSettings( QDomDocument &doc, QDomElement &parent ) m_para4ActiveModel.saveSettings( doc, parent, "Peak4active" ); m_highShelfActiveModel.saveSettings( doc, parent, "Highshelfactive" ); m_lpActiveModel.saveSettings( doc, parent, "LPactive" ); - m_lp12Model.saveSettings( doc, parent, "LP12" ); m_lp24Model.saveSettings( doc, parent, "LP24" ); m_lp48Model.saveSettings( doc, parent, "LP48" ); - m_hp12Model.saveSettings( doc, parent, "HP12" ); m_hp24Model.saveSettings( doc, parent, "HP24" ); m_hp48Model.saveSettings( doc, parent, "HP48" ); m_analyzeModel.saveSettings( doc, parent, "Analyzeenable"); - m_lpTypeModel.saveSettings( doc, parent, "LP" ); m_hpTypeModel.saveSettings( doc, parent, "HP" ); } diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index 0633a8cf5..f38f8636d 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -38,7 +38,7 @@ -EqControlsDialog::EqControlsDialog(EqControls *controls) : +EqControlsDialog::EqControlsDialog( EqControls *controls ) : EffectControlDialog( controls ) { @@ -94,10 +94,10 @@ EqControlsDialog::EqControlsDialog(EqControls *controls) : m_resKnob = new Knob( knobBright_26, this ); if(i ==0 || i == 7) { - m_resKnob->move(cw * i + ko , 190 ); + m_resKnob->move( cw * i + ko , 190 ); } else { - m_resKnob->move(cw * i + ko , 205 ); + m_resKnob->move( cw * i + ko , 205 ); } m_resKnob->setVolumeKnob(false); m_resKnob->setModel( m_parameterWidget->getBandModels( i )->res ); diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/eq/eqcontrolsdialog.h index fd8327dce..bda074f4a 100644 --- a/plugins/eq/eqcontrolsdialog.h +++ b/plugins/eq/eqcontrolsdialog.h @@ -33,7 +33,7 @@ #include "MainWindow.h" #include "qpushbutton.h" #include "eqspectrumview.h" -#include "qlist.h" + class EqControls; @@ -66,9 +66,7 @@ private: LedCheckBox* m_hp12Box; LedCheckBox* m_hp24Box; LedCheckBox* m_hp48Box; - LedCheckBox* m_analyzeBox; - EqParameterWidget* m_parameterWidget; EqSpectrumView* m_inSpec; EqSpectrumView* m_outSpec; diff --git a/plugins/eq/eqeffect.cpp b/plugins/eq/eqeffect.cpp index 992aab69b..19f990479 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/eq/eqeffect.cpp @@ -30,12 +30,6 @@ #include "Engine.h" #include "MainWindow.h" -//TODO -//re write to store data from each filter(models,name, storeage name ) in a class, stored in array -//then just loop ever array for each section - - - extern "C" { @@ -52,7 +46,7 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = NULL } ; - +} EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : @@ -74,7 +68,6 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K EqEffect::~EqEffect() { - } @@ -100,10 +93,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { m_eqControls.m_inFftBands.clear(); } - - //TODO UPSAMPLE upsample( buf, frames ); - gain(m_upBuf , m_upBufFrames, m_eqControls.m_inGainModel.value(), &m_inPeak ); m_eqControls.m_inPeakL = m_eqControls.m_inPeakL < m_inPeak[0] ? m_inPeak[0] : m_eqControls.m_inPeakL; m_eqControls.m_inPeakR = m_eqControls.m_inPeakR < m_inPeak[1] ? m_inPeak[0] : m_eqControls.m_inPeakR; @@ -222,21 +212,16 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) gain( m_upBuf , m_upBufFrames, outGain, &outPeak ); m_eqControls.m_outPeakL = m_eqControls.m_outPeakL < outPeak[0] ? outPeak[0] : m_eqControls.m_outPeakL; m_eqControls.m_outPeakR = m_eqControls.m_outPeakR < outPeak[1] ? outPeak[0] : m_eqControls.m_outPeakR; - - //TODO lp filter before downsample for( int i =0; i < m_dFilterCount; i++) { m_downsampleFilters[i].processBuffer(m_upBuf , m_upBufFrames ); } - downSample( buf, frames ); - for( fpp_t f = 0; f < frames; ++f ) { outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; } checkGate( outSum / frames ); - if(m_eqControls.m_analyzeModel.value() ) { m_eqControls.m_outFftBands.analyze( buf, frames ); @@ -245,7 +230,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) { m_eqControls.m_outFftBands.clear(); } - setBandPeaks( &m_eqControls.m_outFftBands , (int)(sampleRate * 0.5)); + setBandPeaks( &m_eqControls.m_outFftBands , ( int )( sampleRate * 0.5 ) ); m_eqControls.m_inProgress = false; return isRunning(); } @@ -253,7 +238,7 @@ bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) -void EqEffect::gain(sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak) +void EqEffect::gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ) { peak[0][0] = 0.0f; peak[0][1] = 0.0f; for( fpp_t f = 0; f < frames; ++f ) @@ -273,11 +258,15 @@ void EqEffect::gain(sampleFrame *buf, const fpp_t frames, float scale, sampleFra } } + + + + sampleFrame m_lastUpFrame; -void EqEffect::upsample(sampleFrame *buf, const fpp_t frames) +void EqEffect::upsample( sampleFrame *buf, const fpp_t frames ) { - if(m_upBufFrames != frames * 2 ) + if( m_upBufFrames != frames * 2 ) { if( m_upBuf ) { @@ -286,21 +275,19 @@ void EqEffect::upsample(sampleFrame *buf, const fpp_t frames) m_upBuf = new sampleFrame[frames * 2]; m_upBufFrames = frames * 2; } - for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) { - m_upBuf[f2][0] = linearInterpolate(m_lastUpFrame[0],buf[f][0], 0.5); - m_upBuf[f2][1] = linearInterpolate(m_lastUpFrame[1],buf[f][1], 0.5); + m_upBuf[f2][0] = linearInterpolate( m_lastUpFrame[0],buf[f][0], 0.5 ); + m_upBuf[f2][1] = linearInterpolate( m_lastUpFrame[1],buf[f][1], 0.5 ); m_upBuf[f2+1][0] = buf[f][0]; m_upBuf[f2+1][1] = buf[f][1]; - m_lastUpFrame[0] = buf[f][0]; m_lastUpFrame[1] = buf[f][1]; } } -} -void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) + +void EqEffect::downSample( sampleFrame *buf, const fpp_t frames ) { for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) { @@ -309,49 +296,10 @@ void EqEffect::downSample(sampleFrame *buf, const fpp_t frames) } } -void EqEffect::analyze(sampleFrame *buf, const fpp_t frames, EqAnalyser* fft) -{ - if(m_eqControls.m_analyzeModel.value() ) - { - const int FFT_BUFFER_SIZE = 2048; - fpp_t f = 0; - if( frames > FFT_BUFFER_SIZE ) - { - fft->m_framesFilledUp = 0; - f = frames - FFT_BUFFER_SIZE; - } - // meger channels - for( ; f < frames; ++f ) - { - fft->m_buffer[fft->m_framesFilledUp] = - ( buf[f][0] + buf[f][1] ) * 0.5; - ++fft->m_framesFilledUp; - } - - if( fft->m_framesFilledUp < FFT_BUFFER_SIZE ) - { - return; - } - - fft->m_sr = Engine::mixer()->processingSampleRate(); - const int LOWEST_FREQ = 0; - const int HIGHEST_FREQ = fft->m_sr / 2; - - fftwf_execute( fft->m_fftPlan ); - absspec( fft->m_specBuf, fft->m_absSpecBuf, FFT_BUFFER_SIZE+1 ); - - compressbands( fft->m_absSpecBuf, fft->m_bands, FFT_BUFFER_SIZE+1, - MAX_BANDS, - (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2)), - (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(fft->m_sr /2))); - fft->m_energy = maximum( fft->m_bands, MAX_BANDS ) / maximum( fft->m_buffer, FFT_BUFFER_SIZE ); - fft->m_framesFilledUp = 0; - - } -} -float EqEffect::peakBand(float minF, float maxF, EqAnalyser *fft, int sr) + +float EqEffect::peakBand( float minF, float maxF, EqAnalyser *fft, int sr ) { float peak = -60; float * b = fft->m_bands; @@ -377,25 +325,29 @@ void EqEffect::setBandPeaks(EqAnalyser *fft, int samplerate ) peakBand( m_eqControls.m_para1FreqModel.value() - (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), m_eqControls.m_para1FreqModel.value() - + (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), fft , samplerate ); + + (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), + fft , samplerate ); m_eqControls.m_para2PeakL = m_eqControls.m_para2PeakR = peakBand( m_eqControls.m_para2FreqModel.value() - (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), m_eqControls.m_para2FreqModel.value() - + (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), fft , samplerate ); + + (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), + fft , samplerate ); m_eqControls.m_para3PeakL = m_eqControls.m_para3PeakR = peakBand( m_eqControls.m_para3FreqModel.value() - (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), m_eqControls.m_para3FreqModel.value() - + (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), fft , samplerate ); + + (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), + fft , samplerate ); m_eqControls.m_para4PeakL = m_eqControls.m_para4PeakR = peakBand( m_eqControls.m_para4FreqModel.value() - (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), m_eqControls.m_para4FreqModel.value() - + (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), fft , samplerate ); + + (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), + fft , samplerate ); m_eqControls.m_highShelfPeakL = m_eqControls.m_highShelfPeakR = peakBand( m_eqControls.m_highShelfFreqModel.value(), diff --git a/plugins/eq/eqeffect.h b/plugins/eq/eqeffect.h index 1fc96c077..a2e660cc2 100644 --- a/plugins/eq/eqeffect.h +++ b/plugins/eq/eqeffect.h @@ -71,7 +71,6 @@ private: sampleFrame* m_upBuf; fpp_t m_upBufFrames; - // const static int MAX_BANDS = 249; void upsample( sampleFrame *buf, const fpp_t frames ); void downSample( sampleFrame *buf, const fpp_t frames ); void analyze( sampleFrame *buf, const fpp_t frames, EqAnalyser* fft ); @@ -83,8 +82,7 @@ private: } void setBandPeaks( EqAnalyser *fft , int); - // float m_bands[MAX_BANDS]; - // float m_energy; + }; diff --git a/plugins/eq/eqfilter.h b/plugins/eq/eqfilter.h index e6924ca4c..d704ae517 100644 --- a/plugins/eq/eqfilter.h +++ b/plugins/eq/eqfilter.h @@ -43,6 +43,9 @@ public: } + + + virtual inline void setSampleRate( int sampleRate ) { if( sampleRate != m_sampleRate ) @@ -52,6 +55,9 @@ public: } } + + + virtual inline void setFrequency( float freq ){ if ( freq != m_freq ) { @@ -60,6 +66,9 @@ public: } } + + + virtual void setQ( float res ) { if ( res != m_res ) @@ -69,6 +78,9 @@ public: } } + + + virtual void setGain( float gain ) { if ( gain != m_gain ) @@ -78,6 +90,9 @@ public: } } + + + /// /// \brief processBuffer /// \param buf Audio Buffer @@ -153,6 +168,8 @@ public : }; + + /// /// \brief The EqLp12Filter class. /// A 2 pole low pass filter @@ -236,6 +253,9 @@ public: } }; + + + class EqLowShelfFilter : public EqFilter { public : @@ -309,6 +329,9 @@ public : } }; + + + class EqLinkwitzRiley : public StereoLinkwitzRiley { public: diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index 6a3ae7aa1..f52b3a36b 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -26,7 +26,6 @@ #include "QPainter" #include "qwidget.h" #include "lmms_math.h" - #include "MainWindow.h" #include "QMouseEvent" @@ -47,7 +46,6 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); m_scale = 1.5; m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); - } @@ -55,7 +53,6 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : EqParameterWidget::~EqParameterWidget() { - } @@ -83,8 +80,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) for( int i = 0 ; i < bandCount() ; i++ ) { - - m_bands[i].color.setAlpha(m_bands[i].active->value() ? activeAplha() : inactiveAlpha()); + m_bands[i].color.setAlpha( m_bands[i].active->value() ? activeAplha() : inactiveAlpha() ); painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); float x = freqToXPixel( m_bands[i].freq->value() ); float y = height() * 0.5; @@ -98,7 +94,8 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) m_bands[i].x = x; m_bands[i].y = y; painter.drawPoint( x, y ); painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); - if(i == 0 || i == bandCount() - 1 ){ + if( i == 0 || i == bandCount() - 1 ) + { painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); } else @@ -112,7 +109,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) { m_bands[i].color.setAlpha( 255 ); painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - painter.drawLine(sectionLength * i , 1, sectionLength * (i+1) , 1); + painter.drawLine( sectionLength * i , 1, sectionLength * (i+1) , 1 ); } } @@ -184,15 +181,15 @@ EqBand* EqParameterWidget::selectNearestHandle( const int x, const int y ) EqBand* selectedModel = 0; float* distanceToHandles = new float[bandCount()]; //calc distance to each handle - for( int i = 0 ; i < bandCount() ; i++) + for( int i = 0 ; i < bandCount() ; i++ ) { int xOffset = m_bands[i].x - x; int yOffset = m_bands[i].y - y; - distanceToHandles[i] = fabs(sqrt((xOffset * xOffset ) + ( yOffset * yOffset ) ) ); + distanceToHandles[i] = fabs( sqrt( ( xOffset * xOffset ) + ( yOffset * yOffset ) ) ); } //select band int shortestBand = 0; - for (int i = 1 ; i < bandCount() ; i++ ) + for ( int i = 1 ; i < bandCount() ; i++ ) { if ( distanceToHandles [i] < distanceToHandles[shortestBand] ){ shortestBand = i; diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index 3054f0b95..d2aec61f0 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -1,4 +1,3 @@ - /* eqspectrumview.h - defination of EqSpectrumView class. * * Copyright (c) 2014 David French @@ -38,12 +37,10 @@ class EqAnalyser public: fftwf_plan m_fftPlan; - fftwf_complex * m_specBuf; float m_absSpecBuf[FFT_BUFFER_SIZE+1]; float m_buffer[FFT_BUFFER_SIZE*2]; int m_framesFilledUp; - float m_bands[MAX_BANDS]; float m_energy; int m_sr; @@ -77,7 +74,7 @@ public: - void analyze(sampleFrame *buf, const fpp_t frames ) + void analyze( sampleFrame *buf, const fpp_t frames ) { m_inProgress=true; const int FFT_BUFFER_SIZE = 2048; @@ -110,8 +107,8 @@ public: compressbands( m_absSpecBuf, m_bands, FFT_BUFFER_SIZE+1, MAX_BANDS, - (int)(LOWEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(m_sr /2)), - (int)(HIGHEST_FREQ*(FFT_BUFFER_SIZE+1)/(float)(m_sr /2))); + ( int )( LOWEST_FREQ * ( FFT_BUFFER_SIZE + 1 ) / ( float )( m_sr / 2 ) ), + ( int )( HIGHEST_FREQ * ( FFT_BUFFER_SIZE + 1) / ( float )( m_sr / 2 ) ) ); m_energy = maximum( m_bands, MAX_BANDS ) / maximum( m_buffer, FFT_BUFFER_SIZE ); m_framesFilledUp = 0; m_inProgress = false; @@ -125,12 +122,11 @@ class EqSpectrumView : public QWidget { public: - explicit EqSpectrumView( EqAnalyser * b, QWidget * _parent = 0) : + explicit EqSpectrumView( EqAnalyser * b, QWidget * _parent = 0 ): QWidget( _parent ), m_sa( b ) { setFixedSize( 250, 116 ); - // connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); QTimer *timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(update())); timer->start(2000); @@ -140,12 +136,18 @@ public: m_pixelsPerUnitWidth = width( ) / totalLength ; m_scale = 1.5; color = QColor( 255, 255, 255, 255 ); - } + + + virtual ~EqSpectrumView() { } + + + + QColor color; EqAnalyser *m_sa; QPainterPath pp; @@ -171,13 +173,13 @@ public: pp.moveTo( 0,height() ); for( int x = 0; x < MAX_BANDS; ++x, ++b ) { - h = (int)( fh * 2.0 / 3.0 * (20*(log10( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); + h = (int)( fh * 2.0 / 3.0 * ( 20 * ( log10 ( *b / e ) ) - LOWER_Y ) / (-LOWER_Y ) ); if( h < 0 ) h = 0; else if( h >= fh ) continue; - pp.lineTo( freqToXPixel(bandToFreq(x ) ), fh-h ); + pp.lineTo( freqToXPixel(bandToFreq( x ) ), fh-h ); } - pp.lineTo(width(), height() ); + pp.lineTo( width(), height() ); pp.closeSubpath(); - p.fillPath( pp ,QBrush( color ) ); + p.fillPath( pp, QBrush( color ) ); } @@ -190,7 +192,7 @@ public: inline float bandToFreq ( int index ) { - return index * m_sa->m_sr / (MAX_BANDS * 2); + return index * m_sa->m_sr / (MAX_BANDS * 2 ); } @@ -202,10 +204,6 @@ private: float m_pixelsPerUnitWidth; float m_scale; int m_skipBands; - - - - } ; From 7536584386d468fda8066dd0bd1c3b692bcd5f70 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sat, 13 Dec 2014 22:03:15 +0000 Subject: [PATCH 07/15] EQ remove unused files --- plugins/eq/eqfader.cpp | 0 plugins/eq/eqfilter.cpp | 5 ----- plugins/eq/eqhighshelffileter.h | 1 - plugins/eq/eqhp12.h | 0 plugins/eq/eqlowshelffilter.h | 3 --- plugins/eq/eqlp12.h | 0 plugins/eq/eqpeekfilter.cpp | 3 --- plugins/eq/eqpeekfilter.h | 3 --- 8 files changed, 15 deletions(-) delete mode 100644 plugins/eq/eqfader.cpp delete mode 100644 plugins/eq/eqfilter.cpp delete mode 100644 plugins/eq/eqhighshelffileter.h delete mode 100644 plugins/eq/eqhp12.h delete mode 100644 plugins/eq/eqlowshelffilter.h delete mode 100644 plugins/eq/eqlp12.h delete mode 100644 plugins/eq/eqpeekfilter.cpp delete mode 100644 plugins/eq/eqpeekfilter.h diff --git a/plugins/eq/eqfader.cpp b/plugins/eq/eqfader.cpp deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/eq/eqfilter.cpp b/plugins/eq/eqfilter.cpp deleted file mode 100644 index 7407377b9..000000000 --- a/plugins/eq/eqfilter.cpp +++ /dev/null @@ -1,5 +0,0 @@ -#include "eqfilter.h" - - - - diff --git a/plugins/eq/eqhighshelffileter.h b/plugins/eq/eqhighshelffileter.h deleted file mode 100644 index 8b1378917..000000000 --- a/plugins/eq/eqhighshelffileter.h +++ /dev/null @@ -1 +0,0 @@ - diff --git a/plugins/eq/eqhp12.h b/plugins/eq/eqhp12.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/eq/eqlowshelffilter.h b/plugins/eq/eqlowshelffilter.h deleted file mode 100644 index b28b04f64..000000000 --- a/plugins/eq/eqlowshelffilter.h +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/plugins/eq/eqlp12.h b/plugins/eq/eqlp12.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/eq/eqpeekfilter.cpp b/plugins/eq/eqpeekfilter.cpp deleted file mode 100644 index 1cc60cf4c..000000000 --- a/plugins/eq/eqpeekfilter.cpp +++ /dev/null @@ -1,3 +0,0 @@ -#include "eqpeekfilter.h" - - diff --git a/plugins/eq/eqpeekfilter.h b/plugins/eq/eqpeekfilter.h deleted file mode 100644 index b28b04f64..000000000 --- a/plugins/eq/eqpeekfilter.h +++ /dev/null @@ -1,3 +0,0 @@ - - - From c5c9b59167da7bb90229c587d729d41fccdaf926 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 14 Dec 2014 00:09:30 +0000 Subject: [PATCH 08/15] EQ changed makefile to allow windows build on travis --- plugins/eq/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/eq/CMakeLists.txt b/plugins/eq/CMakeLists.txt index b537ab632..0a5515fc4 100644 --- a/plugins/eq/CMakeLists.txt +++ b/plugins/eq/CMakeLists.txt @@ -2,4 +2,4 @@ INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) -BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") From b595d432d9f4c96bf31dde389317d4a100412325 Mon Sep 17 00:00:00 2001 From: Dave French Date: Sun, 14 Dec 2014 21:13:20 +0000 Subject: [PATCH 09/15] EQ Fixed so builds on win, Changed display colors, made res lines thinner --- plugins/eq/CMakeLists.txt | 3 ++- plugins/eq/eqcontrolsdialog.cpp | 4 ++-- plugins/eq/eqparameterwidget.cpp | 2 +- plugins/eq/eqparameterwidget.h | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/plugins/eq/CMakeLists.txt b/plugins/eq/CMakeLists.txt index 0a5515fc4..9b471d5b6 100644 --- a/plugins/eq/CMakeLists.txt +++ b/plugins/eq/CMakeLists.txt @@ -2,4 +2,5 @@ INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) -BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h ../../src/gui/widgets/Fader.cpp +MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h ../../include/Fader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/eq/eqcontrolsdialog.cpp index f38f8636d..1071b87bb 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/eq/eqcontrolsdialog.cpp @@ -51,10 +51,10 @@ EqControlsDialog::EqControlsDialog( EqControls *controls ) : m_inSpec = new EqSpectrumView( &controls->m_inFftBands, this); m_inSpec->move( 50, 5 ); - m_inSpec->color = QColor( 255, 0, 255, 150 ); + m_inSpec->color = QColor( 255, 0, 0, 150 ); m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); m_outSpec->move( 50, 5 ); - m_outSpec->color = QColor(00, 255, 255, 80); + m_outSpec->color = QColor(0, 255, 0, 80); m_parameterWidget = new EqParameterWidget( this ); m_parameterWidget->move( 50, 5 ); diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index f52b3a36b..784806558 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -93,7 +93,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) float bw = m_bands[i].freq->value() / m_bands[i].res->value(); m_bands[i].x = x; m_bands[i].y = y; painter.drawPoint( x, y ); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); if( i == 0 || i == bandCount() - 1 ) { painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); diff --git a/plugins/eq/eqparameterwidget.h b/plugins/eq/eqparameterwidget.h index 766efad60..6848ba938 100644 --- a/plugins/eq/eqparameterwidget.h +++ b/plugins/eq/eqparameterwidget.h @@ -87,7 +87,7 @@ public: const int inactiveAlpha() { - return 100; + return 50; } From 47e6f3b614995adf71685e89bc3e12a87cd87b8c Mon Sep 17 00:00:00 2001 From: Dave French Date: Mon, 15 Dec 2014 12:38:51 +0000 Subject: [PATCH 10/15] EQ Change colors used, to be more accessable. --- plugins/eq/artwork.png | Bin 27381 -> 27905 bytes plugins/eq/eqcontrolsdialog.cpp | 27 +++++++++++++-------------- plugins/eq/eqparameterwidget.cpp | 17 ++++++----------- plugins/eq/eqparameterwidget.h | 2 +- plugins/eq/eqspectrumview.h | 1 + 5 files changed, 21 insertions(+), 26 deletions(-) diff --git a/plugins/eq/artwork.png b/plugins/eq/artwork.png index 73b3473b4f8cd1f390ece29ff24c4754775ea2d0..33fa4960defbf65b02db84e0b812bec717e4e0c3 100644 GIT binary patch literal 27905 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%0A?-KV-lr7 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 Hu0mjf8ekH` literal 27381 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
A`poYU)s$P)mrMz-@#cT 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

qxWw53E{N(<}UdctETZi=&tfo^gY z$>^TC$zow$kSwcbjBnz{;Qt-tKF18W+)25~*7&#su*O5jz>#l7$7Ekns?E7icawa01k zIccDq3dUMeO(lmii)z@0r^`=1rv_SXcDdQP#ekIadofDDvasZ&?BwL+j^U22+wrj; z-Ejn6OHfL>ww(K50p>9dixh~Rn$Ii-ra2ws^?BBk|2ZG?(5Kv{Soz0j_16w=bWQ** z#Sy1cqEeo8YA~eEKWD<9^y($;{!E$Q$~i!(FoJr$67Q#Z!P*W(t9pyY0*Xz)L$~Pf zO1sthak>}_0CcUAJECEC+)fNIV<}=9FsAa2B`<1|^tGHcv|Rm;J8hD45R`yYnpk}I z!vU9rGN%P9qSp{BM48HqE;K`W9>wT{+@cB-)v%2lJSH`)ZzHms0cFih$hk)`k5((9 z$Q9tQ<`|ylNi`OnEEx_OXs0q7a(UdChihu|SZt#mpH@*Dqf7Z*OlCDqH4&}+BfpTN z%tMR?**cm_inFftBands, this); - m_inSpec->move( 50, 5 ); - m_inSpec->color = QColor( 255, 0, 0, 150 ); + m_inSpec->move( 51, 2 ); + m_inSpec->color = QColor( 238, 154, 120, 80 ); m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); - m_outSpec->move( 50, 5 ); - m_outSpec->color = QColor(0, 255, 0, 80); + m_outSpec->move( 51, 2 ); + m_outSpec->color = QColor(145, 205, 22, 80); m_parameterWidget = new EqParameterWidget( this ); - m_parameterWidget->move( 50, 5 ); + m_parameterWidget->move( 51, 2 ); - setBand( 0, &controls->m_hpActiveModel, &controls->m_hpFeqModel, &controls->m_hpResModel, 0, QColor(173, 115, 57), tr( "HP" ) ,0,0); - setBand( 1, &controls->m_lowShelfActiveModel, &controls->m_lowShelfFreqModel, &controls->m_lowShelfResModel, &controls->m_lowShelfGainModel, QColor(255, 0, 0), tr( "Low Shelf" ), &controls->m_lowShelfPeakL , &controls->m_lowShelfPeakR ); - setBand( 2, &controls->m_para1ActiveModel, &controls->m_para1FreqModel, &controls->m_para1ResModel, &controls->m_para1GainModel, QColor(255, 173, 115), tr( "Peak 1" ), &controls->m_para1PeakL, &controls->m_para1PeakR ); - setBand( 3, &controls->m_para2ActiveModel, &controls->m_para2FreqModel, &controls->m_para2ResModel, &controls->m_para2GainModel, QColor(255, 255, 0), tr( "Peak 2" ), &controls->m_para2PeakL, &controls->m_para2PeakR ); - setBand( 4, &controls->m_para3ActiveModel, &controls->m_para3FreqModel, &controls->m_para3ResModel, &controls->m_para3GainModel, QColor(0, 255, 0), tr( "Peak 3" ), &controls->m_para3PeakL, &controls->m_para3PeakR ); - setBand( 5, &controls->m_para4ActiveModel, &controls->m_para4FreqModel, &controls->m_para4ResModel, &controls->m_para4GainModel, QColor(0, 186, 255), tr( "Peak 4" ), &controls->m_para4PeakL, &controls->m_para4PeakR ); - setBand( 6, &controls->m_highShelfActiveModel, &controls->m_highShelfFreqModel, &controls->m_highShelfResModel, &controls->m_highShelfGainModel, QColor(222, 0, 222 ), tr( "High Shelf" ), &controls->m_highShelfPeakL, &controls->m_highShelfPeakR ); - setBand( 7, &controls->m_lpActiveModel, &controls->m_lpFreqModel, &controls->m_lpResModel, 0, QColor(156, 156, 156 ), tr( "LP" ) ,0,0); + setBand( 0, &controls->m_hpActiveModel, &controls->m_hpFeqModel, &controls->m_hpResModel, 0, QColor(255 ,255, 255), tr( "HP" ) ,0,0); + setBand( 1, &controls->m_lowShelfActiveModel, &controls->m_lowShelfFreqModel, &controls->m_lowShelfResModel, &controls->m_lowShelfGainModel, QColor(255 ,255, 255), tr( "Low Shelf" ), &controls->m_lowShelfPeakL , &controls->m_lowShelfPeakR ); + setBand( 2, &controls->m_para1ActiveModel, &controls->m_para1FreqModel, &controls->m_para1ResModel, &controls->m_para1GainModel, QColor(255 ,255, 255), tr( "Peak 1" ), &controls->m_para1PeakL, &controls->m_para1PeakR ); + setBand( 3, &controls->m_para2ActiveModel, &controls->m_para2FreqModel, &controls->m_para2ResModel, &controls->m_para2GainModel, QColor(255 ,255, 255), tr( "Peak 2" ), &controls->m_para2PeakL, &controls->m_para2PeakR ); + setBand( 4, &controls->m_para3ActiveModel, &controls->m_para3FreqModel, &controls->m_para3ResModel, &controls->m_para3GainModel, QColor(255 ,255, 255), tr( "Peak 3" ), &controls->m_para3PeakL, &controls->m_para3PeakR ); + setBand( 5, &controls->m_para4ActiveModel, &controls->m_para4FreqModel, &controls->m_para4ResModel, &controls->m_para4GainModel, QColor(255 ,255, 255), tr( "Peak 4" ), &controls->m_para4PeakL, &controls->m_para4PeakR ); + setBand( 6, &controls->m_highShelfActiveModel, &controls->m_highShelfFreqModel, &controls->m_highShelfResModel, &controls->m_highShelfGainModel, QColor(255 ,255, 255), tr( "High Shelf" ), &controls->m_highShelfPeakL, &controls->m_highShelfPeakR ); + setBand( 7, &controls->m_lpActiveModel, &controls->m_lpFreqModel, &controls->m_lpResModel, 0, QColor(255 ,255, 255), tr( "LP" ) ,0,0); int cw = width()/8; //the chanel width in pixels int ko = ( cw * 0.5 ) - ((new Knob( knobBright_26, 0 ))->width() * 0.5 ); diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/eq/eqparameterwidget.cpp index 784806558..735c7b72f 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/eq/eqparameterwidget.cpp @@ -81,7 +81,7 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) for( int i = 0 ; i < bandCount() ; i++ ) { m_bands[i].color.setAlpha( m_bands[i].active->value() ? activeAplha() : inactiveAlpha() ); - painter.setPen( QPen( m_bands[i].color, 10, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); float x = freqToXPixel( m_bands[i].freq->value() ); float y = height() * 0.5; float gain = 1; @@ -92,25 +92,20 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) y = gainToYPixel( gain ); float bw = m_bands[i].freq->value() / m_bands[i].res->value(); m_bands[i].x = x; m_bands[i].y = y; - painter.drawPoint( x, y ); + const int radius = 7; + painter.drawEllipse( x - radius , y - radius, radius * 2 ,radius * 2 ); + QString msg = QString ("%1").arg (QString::number (i + 1)); + painter.drawText(x - (radius * 0.5), y + (radius * 0.85 ), msg ); painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); if( i == 0 || i == bandCount() - 1 ) { - painter.drawLine(x, y, x, y - (m_bands[i].res->value() * 4 ) ); + painter.drawLine(x , y, x, y - (m_bands[i].res->value() * 4 ) ); } else { painter.drawLine(freqToXPixel(m_bands[i].freq->value()-(bw * 0.5)),y,freqToXPixel(m_bands[i].freq->value()+(bw * 0.5)),y); } } - //Draw color band - int sectionLength = width() / bandCount(); - for( int i = 0; i < bandCount(); i++) - { - m_bands[i].color.setAlpha( 255 ); - painter.setPen( QPen( m_bands[i].color, 3, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); - painter.drawLine( sectionLength * i , 1, sectionLength * (i+1) , 1 ); - } } diff --git a/plugins/eq/eqparameterwidget.h b/plugins/eq/eqparameterwidget.h index 6848ba938..c47ea259d 100644 --- a/plugins/eq/eqparameterwidget.h +++ b/plugins/eq/eqparameterwidget.h @@ -87,7 +87,7 @@ public: const int inactiveAlpha() { - return 50; + return 100; } diff --git a/plugins/eq/eqspectrumview.h b/plugins/eq/eqspectrumview.h index d2aec61f0..34bf09444 100644 --- a/plugins/eq/eqspectrumview.h +++ b/plugins/eq/eqspectrumview.h @@ -180,6 +180,7 @@ public: pp.lineTo( width(), height() ); pp.closeSubpath(); p.fillPath( pp, QBrush( color ) ); + p.drawPath( pp ); } From 6fb8df486f5f33605665243430d368efc8f8b32f Mon Sep 17 00:00:00 2001 From: Dave French Date: Tue, 16 Dec 2014 14:25:56 +0000 Subject: [PATCH 11/15] EQ changed file and directory names to CamelCase --- plugins/CMakeLists.txt | 2 +- plugins/{eq => Eq}/CMakeLists.txt | 4 ++-- plugins/{eq/eqcontrols.cpp => Eq/EqControls.cpp} | 4 ++-- plugins/{eq/eqcontrols.h => Eq/EqControls.h} | 2 +- .../EqControlsDialog.cpp} | 8 ++++---- .../eqcontrolsdialog.h => Eq/EqControlsDialog.h} | 4 ++-- plugins/{eq/eqeffect.cpp => Eq/EqEffect.cpp} | 6 +++--- plugins/{eq/eqeffect.h => Eq/EqEffect.h} | 4 ++-- plugins/{eq/eqfader.h => Eq/EqFader.h} | 0 plugins/{eq/eqfilter.h => Eq/EqFilter.h} | 0 .../EqParameterWidget.cpp} | 6 +++--- .../eqparameterwidget.h => Eq/EqParameterWidget.h} | 0 .../{eq/eqspectrumview.h => Eq/EqSpectrumView.h} | 0 plugins/{eq => Eq}/artwork.png | Bin plugins/{eq => Eq}/logo.png | Bin 15 files changed, 20 insertions(+), 20 deletions(-) rename plugins/{eq => Eq}/CMakeLists.txt (55%) rename plugins/{eq/eqcontrols.cpp => Eq/EqControls.cpp} (99%) rename plugins/{eq/eqcontrols.h => Eq/EqControls.h} (99%) rename plugins/{eq/eqcontrolsdialog.cpp => Eq/EqControlsDialog.cpp} (98%) rename plugins/{eq/eqcontrolsdialog.h => Eq/EqControlsDialog.h} (97%) rename plugins/{eq/eqeffect.cpp => Eq/EqEffect.cpp} (99%) rename plugins/{eq/eqeffect.h => Eq/EqEffect.h} (98%) rename plugins/{eq/eqfader.h => Eq/EqFader.h} (100%) rename plugins/{eq/eqfilter.h => Eq/EqFilter.h} (100%) rename plugins/{eq/eqparameterwidget.cpp => Eq/EqParameterWidget.cpp} (97%) rename plugins/{eq/eqparameterwidget.h => Eq/EqParameterWidget.h} (100%) rename plugins/{eq/eqspectrumview.h => Eq/EqSpectrumView.h} (100%) rename plugins/{eq => Eq}/artwork.png (100%) rename plugins/{eq => Eq}/logo.png (100%) diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 7807ff544..63c80538e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -10,7 +10,7 @@ ADD_SUBDIRECTORY(CrossoverEQ) ADD_SUBDIRECTORY(delay) ADD_SUBDIRECTORY(DualFilter) ADD_SUBDIRECTORY(dynamics_processor) -ADD_SUBDIRECTORY(eq) +ADD_SUBDIRECTORY(Eq) ADD_SUBDIRECTORY(flanger) ADD_SUBDIRECTORY(flp_import) ADD_SUBDIRECTORY(HydrogenImport) diff --git a/plugins/eq/CMakeLists.txt b/plugins/Eq/CMakeLists.txt similarity index 55% rename from plugins/eq/CMakeLists.txt rename to plugins/Eq/CMakeLists.txt index 9b471d5b6..294005af0 100644 --- a/plugins/eq/CMakeLists.txt +++ b/plugins/Eq/CMakeLists.txt @@ -2,5 +2,5 @@ INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) -BUILD_PLUGIN(eq eqeffect.cpp eqcontrols.cpp eqcontrolsdialog.cpp eqfilter.h eqparameterwidget.cpp eqfader.h eqspectrumview.h ../../src/gui/widgets/Fader.cpp -MOCFILES eqcontrols.h eqparameterwidget.h eqfader.h ../../include/Fader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(eq EqEffect.cpp EqControls.cpp EqControlsDialog.cpp EqFilter.h EqParameterWidget.cpp EqFader.h EqSpectrumView.h ../../src/gui/widgets/Fader.cpp +MOCFILES EqControls.h EqParameterWidget.h EqFader.h ../../include/Fader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/eq/eqcontrols.cpp b/plugins/Eq/EqControls.cpp similarity index 99% rename from plugins/eq/eqcontrols.cpp rename to plugins/Eq/EqControls.cpp index 82cb11191..62d71cabc 100644 --- a/plugins/eq/eqcontrols.cpp +++ b/plugins/Eq/EqControls.cpp @@ -23,8 +23,8 @@ */ #include -#include "eqcontrols.h" -#include "eqeffect.h" +#include "EqControls.h" +#include "EqEffect.h" diff --git a/plugins/eq/eqcontrols.h b/plugins/Eq/EqControls.h similarity index 99% rename from plugins/eq/eqcontrols.h rename to plugins/Eq/EqControls.h index f3646cce6..eec745b6f 100644 --- a/plugins/eq/eqcontrols.h +++ b/plugins/Eq/EqControls.h @@ -26,7 +26,7 @@ #define EQCONTROLS_H #include "EffectControls.h" -#include "eqcontrolsdialog.h" +#include "EqControlsDialog.h" #include "Knob.h" class EqEffect; diff --git a/plugins/eq/eqcontrolsdialog.cpp b/plugins/Eq/EqControlsDialog.cpp similarity index 98% rename from plugins/eq/eqcontrolsdialog.cpp rename to plugins/Eq/EqControlsDialog.cpp index 57ba248de..950ed1272 100644 --- a/plugins/eq/eqcontrolsdialog.cpp +++ b/plugins/Eq/EqControlsDialog.cpp @@ -23,17 +23,17 @@ */ -#include "eqcontrolsdialog.h" -#include "eqcontrols.h" +#include "EqControlsDialog.h" +#include "EqControls.h" #include "embed.h" #include "Fader.h" -#include "eqfader.h" +#include "EqFader.h" #include "Engine.h" #include "AutomatableButton.h" #include "QWidget" #include "MainWindow.h" #include "LedCheckbox.h" -//#include "eqspectrumview.h" + diff --git a/plugins/eq/eqcontrolsdialog.h b/plugins/Eq/EqControlsDialog.h similarity index 97% rename from plugins/eq/eqcontrolsdialog.h rename to plugins/Eq/EqControlsDialog.h index bda074f4a..d220d51ad 100644 --- a/plugins/eq/eqcontrolsdialog.h +++ b/plugins/Eq/EqControlsDialog.h @@ -29,10 +29,10 @@ #include "Fader.h" #include "Knob.h" #include "LedCheckbox.h" -#include "eqparameterwidget.h" +#include "EqParameterWidget.h" #include "MainWindow.h" #include "qpushbutton.h" -#include "eqspectrumview.h" +#include "EqSpectrumView.h" class EqControls; diff --git a/plugins/eq/eqeffect.cpp b/plugins/Eq/EqEffect.cpp similarity index 99% rename from plugins/eq/eqeffect.cpp rename to plugins/Eq/EqEffect.cpp index 19f990479..75b7c8bb1 100644 --- a/plugins/eq/eqeffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -22,7 +22,7 @@ * */ -#include "eqeffect.h" +#include "EqEffect.h" #include "embed.cpp" #include "lmms_math.h" #include "BasicFilters.h" @@ -53,11 +53,11 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K Effect( &eq_plugin_descriptor, parent, key ), m_eqControls( this ) { - m_dFilterCount = 6; + m_dFilterCount = 4; m_downsampleFilters = new EqLinkwitzRiley[m_dFilterCount]; for( int i = 0; i < m_dFilterCount; i++) { - m_downsampleFilters[i].setFrequency(20000); + m_downsampleFilters[i].setFrequency(21000); m_downsampleFilters[i].setSR(Engine::mixer()->processingSampleRate() * 2 ); } m_upBuf = 0; diff --git a/plugins/eq/eqeffect.h b/plugins/Eq/EqEffect.h similarity index 98% rename from plugins/eq/eqeffect.h rename to plugins/Eq/EqEffect.h index a2e660cc2..9a4cd9411 100644 --- a/plugins/eq/eqeffect.h +++ b/plugins/Eq/EqEffect.h @@ -26,10 +26,10 @@ #define EQEFFECT_H #include "Effect.h" -#include "eqcontrols.h" +#include "EqControls.h" #include "lmms_math.h" #include "BasicFilters.h" -#include "eqfilter.h" +#include "EqFilter.h" diff --git a/plugins/eq/eqfader.h b/plugins/Eq/EqFader.h similarity index 100% rename from plugins/eq/eqfader.h rename to plugins/Eq/EqFader.h diff --git a/plugins/eq/eqfilter.h b/plugins/Eq/EqFilter.h similarity index 100% rename from plugins/eq/eqfilter.h rename to plugins/Eq/EqFilter.h diff --git a/plugins/eq/eqparameterwidget.cpp b/plugins/Eq/EqParameterWidget.cpp similarity index 97% rename from plugins/eq/eqparameterwidget.cpp rename to plugins/Eq/EqParameterWidget.cpp index 735c7b72f..cafe5ac23 100644 --- a/plugins/eq/eqparameterwidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -22,7 +22,7 @@ * */ -#include "eqparameterwidget.h" +#include "EqParameterWidget.h" #include "QPainter" #include "qwidget.h" #include "lmms_math.h" @@ -94,8 +94,8 @@ void EqParameterWidget::paintEvent( QPaintEvent *event ) m_bands[i].x = x; m_bands[i].y = y; const int radius = 7; painter.drawEllipse( x - radius , y - radius, radius * 2 ,radius * 2 ); - QString msg = QString ("%1").arg (QString::number (i + 1)); - painter.drawText(x - (radius * 0.5), y + (radius * 0.85 ), msg ); + QString msg = QString ( "%1" ).arg ( QString::number (i + 1) ); + painter.drawText(x - ( radius * 0.5 ), y + ( radius * 0.85 ), msg ); painter.setPen( QPen( m_bands[i].color, 1, Qt::SolidLine, Qt::SquareCap, Qt::BevelJoin ) ); if( i == 0 || i == bandCount() - 1 ) { diff --git a/plugins/eq/eqparameterwidget.h b/plugins/Eq/EqParameterWidget.h similarity index 100% rename from plugins/eq/eqparameterwidget.h rename to plugins/Eq/EqParameterWidget.h diff --git a/plugins/eq/eqspectrumview.h b/plugins/Eq/EqSpectrumView.h similarity index 100% rename from plugins/eq/eqspectrumview.h rename to plugins/Eq/EqSpectrumView.h diff --git a/plugins/eq/artwork.png b/plugins/Eq/artwork.png similarity index 100% rename from plugins/eq/artwork.png rename to plugins/Eq/artwork.png diff --git a/plugins/eq/logo.png b/plugins/Eq/logo.png similarity index 100% rename from plugins/eq/logo.png rename to plugins/Eq/logo.png From 2a448e92429884da3d7c15ae030f24cf40baa7f4 Mon Sep 17 00:00:00 2001 From: Dave French Date: Tue, 16 Dec 2014 23:02:45 +0000 Subject: [PATCH 12/15] EQ cleaning up memory management --- plugins/Eq/EqEffect.cpp | 4 ++++ plugins/Eq/EqParameterWidget.cpp | 4 ++++ plugins/Eq/EqSpectrumView.h | 10 +++++++--- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index 75b7c8bb1..f344ed519 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -68,6 +68,10 @@ EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::K EqEffect::~EqEffect() { + if(m_upBuf) + { + delete m_upBuf; + } } diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp index cafe5ac23..0cbcd1bfb 100644 --- a/plugins/Eq/EqParameterWidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -53,6 +53,10 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : EqParameterWidget::~EqParameterWidget() { + if(m_bands) + { + delete m_bands; + } } diff --git a/plugins/Eq/EqSpectrumView.h b/plugins/Eq/EqSpectrumView.h index 34bf09444..d86899eea 100644 --- a/plugins/Eq/EqSpectrumView.h +++ b/plugins/Eq/EqSpectrumView.h @@ -49,10 +49,16 @@ public: EqAnalyser() { m_inProgress=false; + m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); + m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE ); clear(); } - + virtual ~EqAnalyser() + { + fftwf_destroy_plan( m_fftPlan ); + fftwf_free( m_specBuf ); + } bool getInProgress() @@ -67,8 +73,6 @@ public: m_framesFilledUp = 0; m_energy = 0; memset( m_buffer, 0, sizeof( m_buffer ) ); - m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); - m_fftPlan = fftwf_plan_dft_r2c_1d( FFT_BUFFER_SIZE*2, m_buffer, m_specBuf, FFTW_MEASURE ); } From bac22e96072fe91cefa43222e23569d9edbf4d36 Mon Sep 17 00:00:00 2001 From: Dave French Date: Wed, 17 Dec 2014 02:57:49 +0000 Subject: [PATCH 13/15] EQ tidy up, --- plugins/Eq/CMakeLists.txt | 4 +- plugins/Eq/EqControls.cpp | 2 + plugins/Eq/EqEffect.cpp | 3 +- plugins/Eq/EqFader.h | 4 +- plugins/Eq/EqFilter.h | 4 +- plugins/Eq/EqParameterWidget.cpp | 8 +- plugins/Eq/EqParameterWidget.h | 129 ++++++++++++++++--------------- plugins/Eq/EqSpectrumView.h | 7 +- 8 files changed, 89 insertions(+), 72 deletions(-) diff --git a/plugins/Eq/CMakeLists.txt b/plugins/Eq/CMakeLists.txt index 294005af0..3cd4b8885 100644 --- a/plugins/Eq/CMakeLists.txt +++ b/plugins/Eq/CMakeLists.txt @@ -2,5 +2,5 @@ INCLUDE(BuildPlugin) INCLUDE_DIRECTORIES(${FFTW3F_INCLUDE_DIRS}) LINK_DIRECTORIES(${FFTW3F_LIBRARY_DIRS}) LINK_LIBRARIES(${FFTW3F_LIBRARIES}) -BUILD_PLUGIN(eq EqEffect.cpp EqControls.cpp EqControlsDialog.cpp EqFilter.h EqParameterWidget.cpp EqFader.h EqSpectrumView.h ../../src/gui/widgets/Fader.cpp -MOCFILES EqControls.h EqParameterWidget.h EqFader.h ../../include/Fader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") +BUILD_PLUGIN(eq EqEffect.cpp EqControls.cpp EqControlsDialog.cpp EqFilter.h EqParameterWidget.cpp EqFader.h EqSpectrumView.h +MOCFILES EqControls.h EqParameterWidget.h EqFader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/Eq/EqControls.cpp b/plugins/Eq/EqControls.cpp index 62d71cabc..19b63fc4a 100644 --- a/plugins/Eq/EqControls.cpp +++ b/plugins/Eq/EqControls.cpp @@ -85,6 +85,8 @@ EqControls::EqControls( EqEffect *effect ) : m_para1GainModel.setScaleLogarithmic( true ); m_inPeakL = 0; m_inPeakR = 0; + m_outPeakL = 0; + m_outPeakR = 0; m_lowShelfPeakL = 0; m_lowShelfPeakR = 0; m_para1PeakL = 0; m_para1PeakR = 0; m_para2PeakL = 0; m_para2PeakR = 0; diff --git a/plugins/Eq/EqEffect.cpp b/plugins/Eq/EqEffect.cpp index f344ed519..ee78f459d 100644 --- a/plugins/Eq/EqEffect.cpp +++ b/plugins/Eq/EqEffect.cpp @@ -51,7 +51,8 @@ Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : Effect( &eq_plugin_descriptor, parent, key ), - m_eqControls( this ) + m_eqControls( this ), + m_upBufFrames( 0 ) { m_dFilterCount = 4; m_downsampleFilters = new EqLinkwitzRiley[m_dFilterCount]; diff --git a/plugins/Eq/EqFader.h b/plugins/Eq/EqFader.h index 1057b60c0..0e1a3cc2d 100644 --- a/plugins/Eq/EqFader.h +++ b/plugins/Eq/EqFader.h @@ -32,7 +32,7 @@ -class EqFader : public Fader +class EXPORT EqFader : public Fader { public: @@ -48,6 +48,8 @@ public: m_rPeak = rPeak; connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( updateVuMeters() ) ); m_model = model; + setPeak_L( 0 ); + setPeak_R( 0 ); } diff --git a/plugins/Eq/EqFilter.h b/plugins/Eq/EqFilter.h index d704ae517..b703edc95 100644 --- a/plugins/Eq/EqFilter.h +++ b/plugins/Eq/EqFilter.h @@ -336,7 +336,9 @@ class EqLinkwitzRiley : public StereoLinkwitzRiley { public: EqLinkwitzRiley() : - StereoLinkwitzRiley( 44100) + StereoLinkwitzRiley( 44100), + m_freq(0 ), + m_sr( 1 ) { } diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp index 0cbcd1bfb..4d3cb6e9a 100644 --- a/plugins/Eq/EqParameterWidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -46,6 +46,7 @@ EqParameterWidget::EqParameterWidget( QWidget *parent ) : m_pixelsPerUnitHeight = (height() - 4) / ( totalHeight ); m_scale = 1.5; m_pixelsPerOctave = freqToXPixel( 10000 ) - freqToXPixel( 5000 ); + } @@ -56,6 +57,7 @@ EqParameterWidget::~EqParameterWidget() if(m_bands) { delete m_bands; + m_bands = 0; } } @@ -210,6 +212,10 @@ EqBand::EqBand() : res ( 0 ), freq ( 0 ), color ( QColor( 255, 255, 255 ) ), - name ( QString( "" ) ) + x( 0 ), + y( 0 ), + name ( QString( "" ) ), + peakL( 0 ), + peakR( 0 ) { } diff --git a/plugins/Eq/EqParameterWidget.h b/plugins/Eq/EqParameterWidget.h index c47ea259d..6cc7f1a89 100644 --- a/plugins/Eq/EqParameterWidget.h +++ b/plugins/Eq/EqParameterWidget.h @@ -32,18 +32,19 @@ class EqBand { public : - EqBand(); - FloatModel* gain; - FloatModel* res; - FloatModel* freq; - BoolModel* active; - QColor color; - int x; - int y; - QString name; + EqBand(); + FloatModel* gain; + FloatModel* res; + FloatModel* freq; + BoolModel* active; + QColor color; + int x; + int y; + QString name; float* peakL; float* peakR; + }; @@ -52,51 +53,51 @@ class EqParameterWidget : public QWidget { public: - explicit EqParameterWidget( QWidget *parent = 0 ); - ~EqParameterWidget(); - const int bandCount() - { - return 8; - } + explicit EqParameterWidget( QWidget *parent = 0 ); + ~EqParameterWidget(); + const int bandCount() + { + return 8; + } - const int maxDistanceFromHandle() - { - return 20; - } + const int maxDistanceFromHandle() + { + return 20; + } - EqBand* getBandModels( int i ) - { - return &m_bands[i]; - } + EqBand* getBandModels( int i ) + { + return &m_bands[i]; + } - const int activeAplha() - { - return 200; - } + const int activeAplha() + { + return 200; + } - const int inactiveAlpha() - { + const int inactiveAlpha() + { return 100; - } + } - const float resPixelMultiplyer() - { - return 100; - } + const float resPixelMultiplyer() + { + return 100; + } signals: @@ -104,55 +105,55 @@ signals: public slots: protected: - virtual void paintEvent ( QPaintEvent * event ); - virtual void mousePressEvent(QMouseEvent * event ); - virtual void mouseReleaseEvent(QMouseEvent * event); - virtual void mouseMoveEvent(QMouseEvent * event); - virtual void mouseDoubleClickEvent(QMouseEvent * event); + virtual void paintEvent ( QPaintEvent * event ); + virtual void mousePressEvent(QMouseEvent * event ); + virtual void mouseReleaseEvent(QMouseEvent * event); + virtual void mouseMoveEvent(QMouseEvent * event); + virtual void mouseDoubleClickEvent(QMouseEvent * event); private: - EqBand* m_bands; - float m_pixelsPerUnitWidth; - float m_pixelsPerUnitHeight; - float m_pixelsPerOctave; - float m_scale; - EqBand* m_selectedBand; + EqBand *m_bands; + float m_pixelsPerUnitWidth; + float m_pixelsPerUnitHeight; + float m_pixelsPerOctave; + float m_scale; + EqBand* m_selectedBand; - EqBand* selectNearestHandle( const int x, const int y ); + EqBand* selectNearestHandle( const int x, const int y ); - enum MouseAction { none, drag, res } m_mouseAction; - int m_oldX, m_oldY; + enum MouseAction { none, drag, res } m_mouseAction; + int m_oldX, m_oldY; int *m_xGridBands; - inline int freqToXPixel( float freq ) - { - return ( log10( freq ) * m_pixelsPerUnitWidth * m_scale ) - ( width() * 0.5 ); - } + inline int freqToXPixel( float freq ) + { + return ( log10( freq ) * m_pixelsPerUnitWidth * m_scale ) - ( width() * 0.5 ); + } - inline float xPixelToFreq( int x ) - { - return pow( 10, ( x + ( width() * 0.5 ) ) / ( m_pixelsPerUnitWidth * m_scale ) ); - } + inline float xPixelToFreq( int x ) + { + return pow( 10, ( x + ( width() * 0.5 ) ) / ( m_pixelsPerUnitWidth * m_scale ) ); + } - inline int gainToYPixel( float gain ) - { - return ( height() - 3) - ( gain * m_pixelsPerUnitHeight ) - ( (height() -3 ) * 0.5); - } + inline int gainToYPixel( float gain ) + { + return ( height() - 3) - ( gain * m_pixelsPerUnitHeight ) - ( (height() -3 ) * 0.5); + } - inline float yPixelToGain( int y ) - { - return ( ( 0.5 * height() ) - y) / m_pixelsPerUnitHeight; - } + inline float yPixelToGain( int y ) + { + return ( ( 0.5 * height() ) - y) / m_pixelsPerUnitHeight; + } }; diff --git a/plugins/Eq/EqSpectrumView.h b/plugins/Eq/EqSpectrumView.h index d86899eea..71c64f5a4 100644 --- a/plugins/Eq/EqSpectrumView.h +++ b/plugins/Eq/EqSpectrumView.h @@ -46,7 +46,10 @@ public: int m_sr; - EqAnalyser() + EqAnalyser() : + m_framesFilledUp ( 0 ), + m_energy ( 0 ), + m_sr ( 1 ) { m_inProgress=false; m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); @@ -73,7 +76,7 @@ public: m_framesFilledUp = 0; m_energy = 0; memset( m_buffer, 0, sizeof( m_buffer ) ); - + memset( m_bands, 0, sizeof( m_bands ) ); } From 01c8cbcde09a9727e841b58ab9bdf22c37f7e745 Mon Sep 17 00:00:00 2001 From: Dave French Date: Wed, 17 Dec 2014 12:28:03 +0000 Subject: [PATCH 14/15] EQ remove EXPORT from EqFader --- plugins/Eq/EqFader.h | 2 +- plugins/Eq/EqParameterWidget.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/plugins/Eq/EqFader.h b/plugins/Eq/EqFader.h index 0e1a3cc2d..9ef7f80c3 100644 --- a/plugins/Eq/EqFader.h +++ b/plugins/Eq/EqFader.h @@ -32,7 +32,7 @@ -class EXPORT EqFader : public Fader +class EqFader : public Fader { public: diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp index 4d3cb6e9a..31c74a7f0 100644 --- a/plugins/Eq/EqParameterWidget.cpp +++ b/plugins/Eq/EqParameterWidget.cpp @@ -56,7 +56,7 @@ EqParameterWidget::~EqParameterWidget() { if(m_bands) { - delete m_bands; + delete[] m_bands; m_bands = 0; } } From fae0c8c74e65b363d2d5e954fed5d6948fc72343 Mon Sep 17 00:00:00 2001 From: Dave French Date: Wed, 17 Dec 2014 12:44:22 +0000 Subject: [PATCH 15/15] EQ --- include/Fader.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/Fader.h b/include/Fader.h index 9e8f034ea..3e808802a 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: