diff --git a/.gitignore b/.gitignore index 45d3a7e96..aa4f17ab8 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ /plugins/zynaddsubfx/zynaddsubfx/ExternalPrograms/Spliter/Makefile /plugins/zynaddsubfx/zynaddsubfx/doc/Makefile /plugins/zynaddsubfx/zynaddsubfx/doc/gen/Makefile +/data/locale/*.qm diff --git a/CMakeLists.txt b/CMakeLists.txt index 4e2109d68..c8fa4f7ff 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,8 +14,8 @@ INCLUDE(CheckIncludeFiles) INCLUDE(FindPkgConfig) SET(VERSION_MAJOR "1") -SET(VERSION_MINOR "0") -SET(VERSION_PATCH "100") +SET(VERSION_MINOR "1") +SET(VERSION_PATCH "0") #SET(VERSION_SUFFIX "") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") IF(VERSION_SUFFIX) @@ -563,7 +563,7 @@ FOREACH(_item ${ts_targets}) ADD_DEPENDENCIES(update-locales "${_item}") ENDFOREACH(_item ${ts_targets}) -ADD_CUSTOM_TARGET(finalize-locales) +ADD_CUSTOM_TARGET(finalize-locales ALL) FOREACH(_item ${qm_targets}) ADD_DEPENDENCIES(finalize-locales "${_item}") ENDFOREACH(_item ${qm_targets}) diff --git a/data/locale/ca.qm b/data/locale/ca.qm deleted file mode 100644 index 20b626418..000000000 Binary files a/data/locale/ca.qm and /dev/null differ diff --git a/data/locale/cs.qm b/data/locale/cs.qm deleted file mode 100644 index b45aa4455..000000000 Binary files a/data/locale/cs.qm and /dev/null differ diff --git a/data/locale/de.qm b/data/locale/de.qm deleted file mode 100644 index 353543c22..000000000 Binary files a/data/locale/de.qm and /dev/null differ diff --git a/data/locale/en.qm b/data/locale/en.qm deleted file mode 100644 index be651eede..000000000 --- a/data/locale/en.qm +++ /dev/null @@ -1 +0,0 @@ - Click here and the values from the clipboard will be pasted at the first visible measure. - + 点击这里,选择的值将从剪贴板粘贴到第一个可见的小节。 Automation Editor - no pattern @@ -682,19 +682,19 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com DualFilterControls Filter 1 enabled - 过滤器 1 已启用 + 滤波器 1 已启用 Filter 1 type - 过滤器 1 的类型 + 滤波器 1 的类型 Cutoff 1 frequency - 频谱刀 1 的频率 + 滤波器 1 截频 Q/Resonance 1 - + 滤波器 1 Q值 Gain 1 @@ -702,23 +702,23 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com Mix - + 混合 Filter 2 enabled - 已启用过滤器 2 + 滤波器 2 已启用 Filter 2 type - 过滤器 2 的类型 + 滤波器 2 的类型 Cutoff 2 frequency - 频谱刀 2 的频率 + 滤波器 2 截频 Q/Resonance 2 - + 滤波器 2 Q值 Gain 2 @@ -754,31 +754,31 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com 2x LowPass - + 2 个低通串联 RC LowPass 12dB - + RC 低通(12dB) RC BandPass 12dB - + RC 带通(12dB) RC HighPass 12dB - + RC 高通(12dB) RC LowPass 24dB - + RC 低通(24dB) RC BandPass 24dB - + RC 带通(24dB) RC HighPass 24dB - + RC 高通(24dB) Vocal Formant Filter @@ -849,7 +849,7 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com Wet Level: - 湿度: + 效果度: The Wet/Dry knob sets the ratio between the input signal and the effect signal that forms the output. @@ -865,7 +865,7 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com The Decay knob controls how many buffers of silence must pass before the plugin stops processing. Smaller values will reduce the CPU overhead but run the risk of clipping the tail on delay and reverb effects. - + 衰减旋钮控制在插件停止工作前,缓冲区中加入的静音时常。较小的数值会降低CPU占用率但是可能导致延迟或混响产生撕裂。 GATE @@ -877,11 +877,11 @@ Jeff Bai,邮箱:jeffbaichina@gmail.com The Gate knob controls the signal level that is considered to be 'silence' while deciding when to stop processing signals. - + 门限旋钮设置自动静音时,被认为是静音的信号幅度。 Controls - 控制 + 控制器 Effect plugins function as a chained series of effects where the signal will be processed from top to bottom. @@ -924,7 +924,7 @@ Right clicking will bring up a context menu where you can change the order in wh Attack - 打进声 + 起音 Hold @@ -940,7 +940,7 @@ Right clicking will bring up a context menu where you can change the order in wh Release - 释放 + 释音 Modulation @@ -952,7 +952,7 @@ Right clicking will bring up a context menu where you can change the order in wh LFO Attack - LFO 打进声(attack) + LFO 起音 LFO speed @@ -968,11 +968,11 @@ Right clicking will bring up a context menu where you can change the order in wh Freq x 100 - + 频率 x 100 Modulate Env-Amount - + 调制所有包络 @@ -983,11 +983,11 @@ Right clicking will bring up a context menu where you can change the order in wh Predelay: - + 预延迟 Use this knob for setting predelay of the current envelope. The bigger this value the longer the time before start of actual envelope. - + 使用预延迟旋钮设定此包络的预延迟,较大的值会加长包络开始的时间。 ATT @@ -995,15 +995,15 @@ Right clicking will bring up a context menu where you can change the order in wh Attack: - 打进: + 起音: Use this knob for setting attack-time of the current envelope. The bigger this value the longer the envelope needs to increase to attack-level. Choose a small value for instruments like pianos and a big value for strings. - (ADSR Attack) + 使用起音旋钮设定此包络的起音时间,较大的值会让包络达到起音值的时间增加。为钢琴等乐器选择小值而弦乐选择大值。 HOLD - HOLD + 持续 Hold: @@ -1011,43 +1011,43 @@ Right clicking will bring up a context menu where you can change the order in wh Use this knob for setting hold-time of the current envelope. The bigger this value the longer the envelope holds attack-level before it begins to decrease to sustain-level. - + 使用持续旋钮设定此包络的持续时间。较大的值会在它衰减到持续值时,保持包络在起音值更久。 DEC - DEC + 衰减 Decay: - 衰减: + 衰减: Use this knob for setting decay-time of the current envelope. The bigger this value the longer the envelope needs to decrease from attack-level to sustain-level. Choose a small value for instruments like pianos. - (ADSR Decay) + 使用衰减旋钮设定此包络的衰减值。较大的值会延长包络从起音值衰减到持续值的时间。为钢琴等乐器选择一个小值。 SUST - SUST + 持续 Sustain: - 持幅: + 持续: Use this knob for setting sustain-level of the current envelope. The bigger this value the higher the level on which the envelope stays before going down to zero. - (ADSR Sustain) + 使用持续旋钮设置此包络的持续值,较大的值会增加释放前,包络在此保持的值。 REL - REL + 释音 Release: - 消退: + 释音: Use this knob for setting release-time of the current envelope. The bigger this value the longer the envelope needs to decrease from sustain-level to zero. Choose a big value for soft instruments like strings. - (ADSR Release) + 使用释音旋钮设定此包络的释音时间,较大值会增加包络衰减到零的时间。为弦乐等乐器选择一个大值。 AMT @@ -1055,15 +1055,15 @@ Right clicking will bring up a context menu where you can change the order in wh Modulation amount: - + 调制量 Use this knob for setting modulation amount of the current envelope. The bigger this value the more the according size (e.g. volume or cutoff-frequency) will be influenced by this envelope. - + 使用调制量旋钮设置LFO对此包络的调制量,较大的值会对此包络控制的值(如音量或截频)影响更大。 LFO predelay: - + LFO 预延迟 Use this knob for setting predelay-time of the current LFO. The bigger this value the the time until the LFO starts to oscillate. diff --git a/include/AudioPort.h b/include/AudioPort.h index 0de61ca4d..7412d5eb5 100644 --- a/include/AudioPort.h +++ b/include/AudioPort.h @@ -35,13 +35,15 @@ class EffectChain; class FloatModel; +class BoolModel; class AudioPort : public ThreadableJob { MM_OPERATORS public: - AudioPort( const QString & _name, bool _has_effect_chain = true, - FloatModel * volumeModel = NULL, FloatModel * panningModel = NULL ); + AudioPort( const QString & _name, bool _has_effect_chain = true, + FloatModel * volumeModel = NULL, FloatModel * panningModel = NULL, + BoolModel * mutedModel = NULL ); virtual ~AudioPort(); inline sampleFrame * buffer() @@ -117,14 +119,15 @@ private: fx_ch_t m_nextFxChannel; QString m_name; - + EffectChain * m_effects; PlayHandleList m_playHandles; QMutex m_playHandleLock; - + FloatModel * m_volumeModel; FloatModel * m_panningModel; + BoolModel * m_mutedModel; friend class Mixer; friend class MixerWorkerThread; diff --git a/include/AutomationEditor.h b/include/AutomationEditor.h index 00b4989fb..a96c8ae71 100644 --- a/include/AutomationEditor.h +++ b/include/AutomationEditor.h @@ -131,6 +131,9 @@ protected slots: void setEditMode(AutomationEditor::EditModes mode); void setEditMode(int mode); + void flipYButtonPressed(); + void flipXButtonPressed(); + void setProgressionType(AutomationPattern::ProgressionTypes type); void setProgressionType(int type); void setTension(); @@ -176,6 +179,8 @@ private: static QPixmap * s_toolErase; static QPixmap * s_toolSelect; static QPixmap * s_toolMove; + static QPixmap * s_toolYFlip; + static QPixmap * s_toolXFlip; ComboBoxModel m_zoomingXModel; ComboBoxModel m_zoomingYModel; @@ -281,6 +286,9 @@ private: QAction* m_linearAction; QAction* m_cubicHermiteAction; + ToolButton * m_flipYButton; + ToolButton * m_flipXButton; + Knob * m_tensionKnob; ComboBox * m_zoomingXComboBox; diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 66caddf27..231b73836 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -158,6 +158,8 @@ public: public slots: void clear(); void objectDestroyed( jo_id_t ); + void flipY( int min, int max ); + void flipX( int length = -1 ); private: void cleanObjects(); diff --git a/include/AutomationPatternView.h b/include/AutomationPatternView.h index 2c2a6c96b..76b2e1a47 100644 --- a/include/AutomationPatternView.h +++ b/include/AutomationPatternView.h @@ -51,6 +51,8 @@ protected slots: void changeName(); void disconnectObject( QAction * _a ); void toggleRecording(); + void flipY(); + void flipX(); protected: virtual void constructContextMenu( QMenu * ); diff --git a/include/BBEditor.h b/include/BBEditor.h index 3f9122445..abd325bc2 100644 --- a/include/BBEditor.h +++ b/include/BBEditor.h @@ -57,6 +57,9 @@ public slots: void play(); void stop(); +protected: + virtual void closeEvent( QCloseEvent * _ce ); + private: BBTrackContainerView* m_trackContainerView; ComboBox * m_bbComboBox; diff --git a/include/ControllerRackView.h b/include/ControllerRackView.h index 3a64ff867..eb99ded4d 100644 --- a/include/ControllerRackView.h +++ b/include/ControllerRackView.h @@ -26,6 +26,7 @@ #define CONTROLLER_RACK_VIEW_H #include +#include #include "SerializingObject.h" #include "lmms_basics.h" @@ -56,6 +57,8 @@ public: public slots: void deleteController( ControllerView * _view ); +protected: + virtual void closeEvent( QCloseEvent * _ce ); private slots: virtual void update(); diff --git a/include/Delay.h b/include/Delay.h new file mode 100644 index 000000000..9010232b9 --- /dev/null +++ b/include/Delay.h @@ -0,0 +1,363 @@ +/* + * Delay.h - Delay effect objects to use as building blocks in DSP + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef DELAY_H +#define DELAY_H + +#include "lmms_basics.h" +#include "lmms_math.h" +#include "interpolation.h" +#include "MemoryManager.h" + +// brief usage + +// Classes: + +// CombFeedback: a feedback comb filter - basically a simple delay line, makes a comb shape in the freq response +// CombFeedfwd: a feed-forward comb filter - an "inverted" comb filter, can be combined with CombFeedback to create a net allpass if negative gain is used +// CombFeedbackDualtap: same as CombFeedback but takes two delay values +// AllpassDelay: an allpass delay - combines feedback and feed-forward - has flat frequency response + +// all classes are templated with channel count, any arbitrary channel count can be used for each fx + +// Methods (for all classes): + +// setDelay sets delay amount in frames. It's up to you to make this samplerate-agnostic. +// Fractions are allowed - linear interpolation is used to deal with them +// CombFeedbackDualTap is a special case: it requires 2 delay times + +// setMaxDelay (re)sets the maximum allowed delay, in frames +// NOTE: for performance reasons, there's no bounds checking at setDelay, so make sure you set maxDelay >= delay! + +// clearHistory clears the delay buffer + +// setGain sets the feedback/feed-forward gain, in linear amplitude, negative values are allowed +// 1.0 is full feedback/feed-forward, -1.0 is full negative feedback/feed-forward + +// update runs the fx for one frame - takes as arguments input and number of channel to run, returns output + +template +class CombFeedback +{ +public: + typedef double frame[CHANNELS]; + + CombFeedback( int maxDelay ) : + m_size( maxDelay ), + m_position( 0 ), + m_feedBack( 0.0 ), + m_delay( 0 ), + m_fraction( 0.0 ) + { + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + virtual ~CombFeedback() + { + MM_FREE( m_buffer ); + } + + inline void setMaxDelay( int maxDelay ) + { + if( maxDelay > m_size ) + { + MM_FREE( m_buffer ); + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + m_size = maxDelay; + m_position %= m_size; + } + + inline void clearHistory() + { + memset( m_buffer, 0, sizeof( frame ) * m_size ); + } + + inline void setDelay( double delay ) + { + m_delay = static_cast( ceil( delay ) ); + m_fraction = 1.0 - ( delay - floor( delay ) ); + } + + inline void setGain( double gain ) + { + m_gain = gain; + } + + inline double update( double in, ch_cnt_t ch ) + { + int readPos = m_position - m_delay; + if( readPos < 0 ) { readPos += m_size; } + + const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ); + + ++m_position %= m_size; + + m_buffer[m_position][ch] = in + m_gain * y; + return y; + } + +private: + frame * m_buffer; + int m_size; + int m_position; + double m_gain; + int m_delay; + double m_fraction; +}; + + +template +class CombFeedfwd +{ + typedef double frame[CHANNELS]; + + CombFeedfwd( int maxDelay ) : + m_size( maxDelay ), + m_position( 0 ), + m_feedBack( 0.0 ), + m_delay( 0 ), + m_fraction( 0.0 ) + { + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + virtual ~CombFeedfwd() + { + MM_FREE( m_buffer ); + } + + inline void setMaxDelay( int maxDelay ) + { + if( maxDelay > m_size ) + { + MM_FREE( m_buffer ); + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + m_size = maxDelay; + m_position %= m_size; + } + + inline void clearHistory() + { + memset( m_buffer, 0, sizeof( frame ) * m_size ); + } + + inline void setDelay( double delay ) + { + m_delay = static_cast( ceil( delay ) ); + m_fraction = 1.0 - ( delay - floor( delay ) ); + } + + inline void setGain( double gain ) + { + m_gain = gain; + } + + inline double update( double in, ch_cnt_t ch ) + { + int readPos = m_position - m_delay; + if( readPos < 0 ) { readPos += m_size; } + + const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * m_gain; + + ++m_position %= m_size; + + m_buffer[m_position][ch] = in; + return y; + } + +private: + frame * m_buffer; + int m_size; + int m_position; + double m_gain; + int m_delay; + double m_fraction; +}; + + +template +class CombFeedbackDualtap +{ + typedef double frame[CHANNELS]; + + CombFeedbackDualtap( int maxDelay ) : + m_size( maxDelay ), + m_position( 0 ), + m_feedBack( 0.0 ), + m_delay( 0 ), + m_fraction( 0.0 ) + { + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + virtual ~CombFeedbackDualtap() + { + MM_FREE( m_buffer ); + } + + inline void setMaxDelay( int maxDelay ) + { + if( maxDelay > m_size ) + { + MM_FREE( m_buffer ); + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + m_size = maxDelay; + m_position %= m_size; + } + + inline void clearHistory() + { + memset( m_buffer, 0, sizeof( frame ) * m_size ); + } + + inline void setDelays( double delay1, double delay2 ) + { + m_delay1 = static_cast( ceil( delay1 ) ); + m_fraction1 = 1.0 - ( delay1 - floor( delay1 ) ); + + m_delay2 = static_cast( ceil( delay2 ) ); + m_fraction2 = 1.0 - ( delay2 - floor( delay2 ) ); + } + + inline void setGain( double gain ) + { + m_gain = gain; + } + + inline double update( double in, ch_cnt_t ch ) + { + int readPos1 = m_position - m_delay1; + if( readPos1 < 0 ) { readPos1 += m_size; } + + int readPos2 = m_position - m_delay2; + if( readPos2 < 0 ) { readPos2 += m_size; } + + const double y = linearInterpolate( m_buffer[readPos1][ch], m_buffer[( readPos1 + 1 ) % m_size][ch], m_fraction1 ) + + linearInterpolate( m_buffer[readPos2][ch], m_buffer[( readPos2 + 1 ) % m_size][ch], m_fraction2 ); + + ++m_position %= m_size; + + m_buffer[m_position][ch] = in + m_gain * y; + return y; + } + +private: + frame * m_buffer; + int m_size; + int m_position; + double m_gain; + int m_delay1; + int m_delay2; + double m_fraction1; + double m_fraction2; +}; + + +template +class AllpassDelay +{ +public: + typedef double frame[CHANNELS]; + + AllpassDelay( int maxDelay ) : + m_size( maxDelay ), + m_position( 0 ), + m_feedBack( 0.0 ), + m_delay( 0 ), + m_fraction( 0.0 ) + { + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + virtual ~AllpassDelay() + { + MM_FREE( m_buffer ); + } + + inline void setMaxDelay( int maxDelay ) + { + if( maxDelay > m_size ) + { + MM_FREE( m_buffer ); + m_buffer = MM_ALLOC( frame, maxDelay ); + memset( m_buffer, 0, sizeof( frame ) * maxDelay ); + } + m_size = maxDelay; + m_position %= m_size; + } + + inline void clearHistory() + { + memset( m_buffer, 0, sizeof( frame ) * m_size ); + } + + inline void setDelay( double delay ) + { + m_delay = static_cast( ceil( delay ) ); + m_fraction = 1.0 - ( delay - floor( delay ) ); + } + + inline void setGain( double gain ) + { + m_gain = gain; + } + + inline double update( double in, ch_cnt_t ch ) + { + int readPos = m_position - m_delay; + if( readPos < 0 ) { readPos += m_size; } + + const double y = linearInterpolate( m_buffer[readPos][ch], m_buffer[( readPos + 1 ) % m_size][ch], m_fraction ) + in * -m_gain; + const double x = in + m_gain * y; + + ++m_position %= m_size; + + m_buffer[m_position][ch] = x; + return y; + } + +private: + frame * m_buffer; + int m_size; + int m_position; + double m_gain; + int m_delay; + double m_fraction; +}; + +// convenience typedefs for stereo effects +typedef CombFeedback<2> StereoCombFeedback; +typedef CombFeedfwd<2> StereoCombFeedfwd; +typedef CombFeedbackDualtap<2> StereoCombFeedbackDualtap; +typedef AllpassDelay<2> StereoAllpassDelay; + +#endif diff --git a/include/FxLine.h b/include/FxLine.h index 2cc479799..69f6a9ed0 100644 --- a/include/FxLine.h +++ b/include/FxLine.h @@ -73,6 +73,7 @@ private: private slots: void renameChannel(); void removeChannel(); + void removeUnusedChannels(); void moveChannelLeft(); void moveChannelRight(); void displayHelp(); diff --git a/include/FxMixerView.h b/include/FxMixerView.h index e2cee4be6..0bb5796e0 100644 --- a/include/FxMixerView.h +++ b/include/FxMixerView.h @@ -92,6 +92,9 @@ public: // notify the view that an fx channel was deleted void deleteChannel(int index); + // delete all unused channels + void deleteUnusedChannels(); + // move the channel to the left or right void moveChannelLeft(int index); void moveChannelRight(int index); @@ -99,6 +102,9 @@ public: // make sure the display syncs up with the fx mixer. // useful for loading projects void refreshDisplay(); + +protected: + virtual void closeEvent( QCloseEvent * _ce ); private slots: void updateFaders(); diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index 04b93997f..8bb9bd526 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -38,6 +38,7 @@ #include "Track.h" + class QLineEdit; template class QQueue; class InstrumentFunctionArpeggioView; @@ -202,10 +203,10 @@ public: return &m_effectChannelModel; } + void setIndicator( FadeButton *fb ); signals: void instrumentChanged(); - void newNote(); void midiNoteOn( const Note& ); void midiNoteOff( const Note& ); void nameChanged(); @@ -250,6 +251,8 @@ private: IntModel m_pitchRangeModel; IntModel m_effectChannelModel; + FadeButton *m_fb; + Instrument * m_instrument; InstrumentSoundShaping m_soundShaping; diff --git a/include/ProjectNotes.h b/include/ProjectNotes.h index 80a6748da..998f47d99 100644 --- a/include/ProjectNotes.h +++ b/include/ProjectNotes.h @@ -27,6 +27,7 @@ #define PROJECT_NOTES_H #include +#include #include "JournallingObject.h" @@ -56,6 +57,7 @@ public: protected: + virtual void closeEvent( QCloseEvent * _ce ); void setupActions(); diff --git a/include/RmsHelper.h b/include/RmsHelper.h index 784a5fd3c..a65c5461b 100644 --- a/include/RmsHelper.h +++ b/include/RmsHelper.h @@ -38,7 +38,7 @@ public: } virtual ~RmsHelper() { - if( m_buffer ) delete m_buffer; + if( m_buffer ) delete[] m_buffer; } inline void setSize( int size ) diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 440b10e7e..c8b17b148 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -148,9 +148,11 @@ public: private: FloatModel m_volumeModel; + FloatModel m_panningModel; AudioPort m_audioPort; + friend class SampleTrackView; } ; @@ -181,6 +183,7 @@ private: EffectRackView * m_effectRack; QWidget * m_effWindow; Knob * m_volumeKnob; + Knob * m_panningKnob; } ; diff --git a/include/Song.h b/include/Song.h index fd5630225..90f29cf24 100644 --- a/include/Song.h +++ b/include/Song.h @@ -34,6 +34,7 @@ #include "MeterModel.h" #include "VstSyncController.h" + class AutomationTrack; class Pattern; class TimeLineWidget; @@ -169,18 +170,11 @@ public: return m_recording; } - inline bool isExportDone() const + bool isExportDone() const; + + inline void setRenderBetweenMarkers( bool renderBetweenMarkers ) { - if ( m_exportLoop ) - { - return m_exporting == true && - m_playPos[Mode_PlaySong].getTicks() >= length() * ticksPerTact(); - } - else - { - return m_exporting == true && - m_playPos[Mode_PlaySong].getTicks() >= ( length() + 1 ) * ticksPerTact(); - } + m_renderBetweenMarkers = renderBetweenMarkers; } inline PlayModes playMode() const @@ -344,6 +338,7 @@ private: volatile bool m_recording; volatile bool m_exporting; volatile bool m_exportLoop; + volatile bool m_renderBetweenMarkers; volatile bool m_playing; volatile bool m_paused; diff --git a/include/SongEditor.h b/include/SongEditor.h index 08b41f74b..9b2e6cd94 100644 --- a/include/SongEditor.h +++ b/include/SongEditor.h @@ -73,6 +73,8 @@ public slots: void setEditModeDraw(); void setEditModeSelect(); +protected: + virtual void closeEvent( QCloseEvent * _ce ); private slots: void setHighQuality( bool ); diff --git a/include/Track.h b/include/Track.h index 396c41e52..800524a9e 100644 --- a/include/Track.h +++ b/include/Track.h @@ -67,7 +67,7 @@ const int TRACK_OP_WIDTH_COMPACT = 60; * Tracks can be resized by shift-dragging anywhere inside the track * display. This sets the minimum size in pixels for a track. */ -const int MINIMAL_TRACK_HEIGHT = 8; +const int MINIMAL_TRACK_HEIGHT = 32; const int DEFAULT_TRACK_HEIGHT = 32; const int TCO_BORDER_WIDTH = 2; @@ -120,7 +120,7 @@ public: { return m_length; } - + virtual void movePosition( const MidiTime & _pos ); virtual void changeLength( const MidiTime & _length ); @@ -539,7 +539,9 @@ private: QString m_name; int m_height; +protected: BoolModel m_mutedModel; +private: BoolModel m_soloModel; bool m_mutedBeforeSolo; diff --git a/include/TrackContainerView.h b/include/TrackContainerView.h index 2745f86a4..7e9dee437 100644 --- a/include/TrackContainerView.h +++ b/include/TrackContainerView.h @@ -51,7 +51,7 @@ public: virtual void saveSettings( QDomDocument & _doc, QDomElement & _this ); virtual void loadSettings( const QDomElement & _this ); - QWidget * contentWidget() + QScrollArea * contentWidget() { return( m_scrollArea ); } diff --git a/include/interpolation.h b/include/interpolation.h index cbe274d42..693897587 100644 --- a/include/interpolation.h +++ b/include/interpolation.h @@ -71,9 +71,9 @@ inline float cubicInterpolate( float v0, float v1, float v2, float v3, float x ) float frcu = frsq*v0; float t1 = v3 + 3*v1; - return( v1 + 0.5f * frcu + x * ( v2 - frcu * ( 1.0f/6.0f ) - - t1 * ( 1.0f/6.0f ) - v0 / 3.0f ) + frsq * x * ( t1 * - ( 1.0f/6.0f ) - 0.5f * v2 ) + frsq * ( 0.5f * v2 - v1 ) ); + return( v1 + fastFmaf( 0.5f, frcu, x ) * ( v2 - frcu * ( 1.0f/6.0f ) - + fastFmaf( t1, ( 1.0f/6.0f ), -v0 ) * ( 1.0f/3.0f ) ) + frsq * x * ( t1 * + ( 1.0f/6.0f ) - 0.5f * v2 ) + frsq * fastFmaf( 0.5f, v2, -v1 ) ); } @@ -102,7 +102,7 @@ inline float optimalInterpolate( float v0, float v1, float x ) const float c2 = even * -0.004541102062639801; const float c3 = odd * -1.57015627178718420; - return ( ( c3*z + c2 ) * z + c1 ) * z + c0; + return fastFmaf( fastFmaf( fastFmaf( c3, z, c2 ), z, c1 ), z, c0 ); } @@ -119,7 +119,7 @@ inline float optimal4pInterpolate( float v0, float v1, float v2, float v3, float const float c2 = even1 * -0.246185007019907091 + even2 * 0.24614027139700284; const float c3 = odd1 * -0.36030925263849456 + odd2 * 0.10174985775982505; - return ( ( c3*z + c2 ) * z + c1 ) * z + c0; + return fastFmaf( fastFmaf( fastFmaf( c3, z, c2 ), z, c1 ), z, c0 ); } @@ -130,7 +130,7 @@ inline float lagrangeInterpolate( float v0, float v1, float v2, float v3, float const float c1 = v2 - v0 * ( 1.0f / 3.0f ) - v1 * 0.5f - v3 * ( 1.0f / 6.0f ); const float c2 = 0.5f * (v0 + v2) - v1; const float c3 = ( 1.0f/6.0f ) * ( v3 - v0 ) + 0.5f * ( v1 - v2 ); - return ( ( c3*x + c2 ) * x + c1 ) * x + c0; + return fastFmaf( fastFmaf( fastFmaf( c3, x, c2 ), x, c1 ), x, c0 ); } diff --git a/include/lmms_math.h b/include/lmms_math.h index 7cde29153..d1a9dcc79 100644 --- a/include/lmms_math.h +++ b/include/lmms_math.h @@ -48,10 +48,10 @@ using namespace std; #define _isinff(x) isinf(x) #endif #ifndef exp10 -#define exp10(x) pow( 10, x ) +#define exp10(x) pow( 10.0, x ) #endif #ifndef exp10f -#define exp10f(x) powf( 10, x ) +#define exp10f(x) powf( 10.0f, x ) #endif #endif @@ -239,10 +239,10 @@ static inline float linearToLogScale( float min, float max, float value ) -//! @brief Converts linear amplitude (0-1.0) to dBV scale. +//! @brief Converts linear amplitude (0-1.0) to dBV scale. Handles zeroes as -inf. //! @param amp Linear amplitude, where 1.0 = 0dBV. //! @return Amplitude in dBV. -inf for 0 amplitude. -static inline float ampToDbv( float amp ) +static inline float safeAmpToDbv( float amp ) { return amp == 0.0f ? -INFINITY @@ -250,10 +250,10 @@ static inline float ampToDbv( float amp ) } -//! @brief Converts dBV-scale to linear amplitude with 0dBV = 1.0 +//! @brief Converts dBV-scale to linear amplitude with 0dBV = 1.0. Handles infinity as zero. //! @param dbv The dBV value to convert: all infinites are treated as -inf and result in 0 //! @return Linear amplitude -static inline float dbvToAmp( float dbv ) +static inline float safeDbvToAmp( float dbv ) { return isinff( dbv ) ? 0.0f @@ -261,6 +261,24 @@ static inline float dbvToAmp( float dbv ) } +//! @brief Converts linear amplitude (>0-1.0) to dBV scale. +//! @param amp Linear amplitude, where 1.0 = 0dBV. ** Must be larger than zero! ** +//! @return Amplitude in dBV. +static inline float ampToDbv( float amp ) +{ + return log10f( amp ) * 20.0f; +} + + +//! @brief Converts dBV-scale to linear amplitude with 0dBV = 1.0 +//! @param dbv The dBV value to convert. ** Must be a real number - not inf/nan! ** +//! @return Linear amplitude +static inline float dbvToAmp( float dbv ) +{ + return exp10f( dbv * 0.05f ); +} + + //! returns 1.0f if val >= 0.0f, -1.0 else static inline float sign( float val ) diff --git a/plugins/Amplifier/Amplifier.cpp b/plugins/Amplifier/Amplifier.cpp index 16b57df58..11b4de666 100644 --- a/plugins/Amplifier/Amplifier.cpp +++ b/plugins/Amplifier/Amplifier.cpp @@ -83,7 +83,8 @@ bool AmplifierEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) for( fpp_t f = 0; f < frames; ++f ) { // qDebug( "offset %d, value %f", f, m_ampControls.m_volumeModel.value( f ) ); - + outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; + sample_t s[2] = { buf[f][0], buf[f][1] }; // vol knob @@ -122,7 +123,6 @@ bool AmplifierEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) buf[f][0] = d * buf[f][0] + w * s[0]; buf[f][1] = d * buf[f][1] + w * s[1]; - outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; } checkGate( outSum / frames ); diff --git a/plugins/BassBooster/BassBooster.cpp b/plugins/BassBooster/BassBooster.cpp index 85aa67f37..d75fa2c66 100644 --- a/plugins/BassBooster/BassBooster.cpp +++ b/plugins/BassBooster/BassBooster.cpp @@ -88,13 +88,13 @@ bool BassBoosterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames const float w = wetLevel(); for( fpp_t f = 0; f < frames; ++f ) { + outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; + sample_t s[2] = { buf[f][0], buf[f][1] }; m_bbFX.nextSample( s[0], s[1] ); buf[f][0] = d * buf[f][0] + w * s[0]; buf[f][1] = d * buf[f][1] + w * s[1]; - - outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; } checkGate( outSum / frames ); diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index cf7a5a94e..d7b4cf11e 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -10,7 +10,8 @@ ADD_SUBDIRECTORY(CrossoverEQ) ADD_SUBDIRECTORY(delay) ADD_SUBDIRECTORY(DualFilter) ADD_SUBDIRECTORY(dynamics_processor) -ADD_SUBDIRECTORY(flanger) +ADD_SUBDIRECTORY(Eq) +ADD_SUBDIRECTORY(Flanger) ADD_SUBDIRECTORY(flp_import) ADD_SUBDIRECTORY(HydrogenImport) ADD_SUBDIRECTORY(kicker) diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp index a50b6381f..023bdff9c 100644 --- a/plugins/CrossoverEQ/CrossoverEQ.cpp +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -196,9 +196,9 @@ bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames double outSum = 0.0; for( int f = 0; f < frames; ++f ) { + outSum = buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1]; buf[f][0] = d * buf[f][0] + w * m_work[f][0]; buf[f][1] = d * buf[f][1] + w * m_work[f][1]; - outSum = buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1]; } checkGate( outSum ); diff --git a/plugins/DualFilter/DualFilter.cpp b/plugins/DualFilter/DualFilter.cpp index 801a58efd..035740dbf 100644 --- a/plugins/DualFilter/DualFilter.cpp +++ b/plugins/DualFilter/DualFilter.cpp @@ -155,11 +155,11 @@ bool DualFilterEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames s[0] += ( s2[0] * mix2 ); s[1] += ( s2[1] * mix2 ); } + outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; // do another mix with dry signal buf[f][0] = d * buf[f][0] + w * s[0]; buf[f][1] = d * buf[f][1] + w * s[1]; - outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; } checkGate( outSum / frames ); diff --git a/plugins/Eq/CMakeLists.txt b/plugins/Eq/CMakeLists.txt new file mode 100644 index 000000000..75c9b2911 --- /dev/null +++ b/plugins/Eq/CMakeLists.txt @@ -0,0 +1,6 @@ +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 DBvModel.cpp DBvModel.h +MOCFILES EqControls.h EqParameterWidget.h EqFader.h DBvModel.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/Eq/DBvModel.cpp b/plugins/Eq/DBvModel.cpp new file mode 100644 index 000000000..3bd128ea6 --- /dev/null +++ b/plugins/Eq/DBvModel.cpp @@ -0,0 +1,14 @@ +#include "DBvModel.h" + +DBvModel::DBvModel(float val, float min, float max, float step, + Model *parent, const QString &displayName, + bool defaultConstructed) : + FloatModel( val, min, max, step, parent, displayName, defaultConstructed ) +{ + connect(this, SIGNAL( dataChanged() ), this,SLOT( calcAmp() ) ); +} + +void DBvModel::calcAmp() +{ + m_amp = dbvToAmp( value() ); +} diff --git a/plugins/Eq/DBvModel.h b/plugins/Eq/DBvModel.h new file mode 100644 index 000000000..ef6d80a03 --- /dev/null +++ b/plugins/Eq/DBvModel.h @@ -0,0 +1,31 @@ +#ifndef DBVMODEL +#define DBVMODEL + +#include "AutomatableModel.h" + + +class DBvModel : public FloatModel +{ + Q_OBJECT +public: + DBvModel( float val = 0, float min = 0, float max = 0, float step = 0, + Model * parent = NULL, + const QString& displayName = QString(), + bool defaultConstructed = false ); + inline float getAmp() const + { + return m_amp; + } + +private slots: + void calcAmp(); + +private: + float m_amp; +}; + + + + +#endif // DBVMODEL + diff --git a/plugins/Eq/EqControls.cpp b/plugins/Eq/EqControls.cpp new file mode 100644 index 000000000..76e5b4a95 --- /dev/null +++ b/plugins/Eq/EqControls.cpp @@ -0,0 +1,196 @@ +/* + * 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( 0.0, -60.0, 20.0, 0.01, this, tr( "Input gain") ), + m_outGainModel( -.0, -60.0, 20.0, 0.01, this, tr( "Output 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.55, 10.0 , 0.001, this , tr( "Low Shelf res" ) ), + m_para1BwModel( 0.3, 0.1, 4 , 0.001, this , tr( "Peak 1 BW" ) ), + m_para2BwModel( 0.3, 0.1, 4 , 0.001, this , tr( "Peak 2 BW" ) ), + m_para3BwModel( 0.3, 0.1, 4 , 0.001, this , tr( "Peak 3 BW" ) ), + m_para4BwModel( 0.3, 0.1, 4 , 0.001, this , tr( "Peak 4 BW" ) ), + m_highShelfResModel( 1.4, 0.55, 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( false, this , tr( "LP 12" ) ), + m_lp24Model( false, this , tr( "LP 24" ) ), + m_lp48Model( false, this , tr( "LP 48" ) ), + m_hp12Model( false, this , tr( "HP 12" ) ), + m_hp24Model( false, this , tr( "HP 24" ) ), + m_hp48Model( false, this , tr( "HP 48" ) ), + 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 ); + 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_outPeakL = 0; + m_outPeakR = 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; + m_inProgress = false; + m_analyseIn = true; + m_analyseOut = true; + + m_inGainModel.setScaleLogarithmic( true ); +} + + + + +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_para1BwModel.loadSettings( _this ,"Peak1bw" ); + m_para2BwModel.loadSettings( _this ,"Peak2bw" ); + m_para3BwModel.loadSettings( _this ,"Peak3bw" ); + m_para4BwModel.loadSettings( _this ,"Peak4bw" ); + 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_lpTypeModel.loadSettings( _this, "LP" ); + m_hpTypeModel.loadSettings( _this, "HP" ); +} + + + + +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_para1BwModel.saveSettings( doc, parent,"Peak1bw" ); + m_para2BwModel.saveSettings( doc, parent,"Peak2bw" ); + m_para3BwModel.saveSettings( doc, parent,"Peak3bw" ); + m_para4BwModel.saveSettings( doc, parent,"Peak4bw" ); + 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_lpTypeModel.saveSettings( doc, parent, "LP" ); + m_hpTypeModel.saveSettings( doc, parent, "HP" ); +} + diff --git a/plugins/Eq/EqControls.h b/plugins/Eq/EqControls.h new file mode 100644 index 000000000..dfb75287e --- /dev/null +++ b/plugins/Eq/EqControls.h @@ -0,0 +1,138 @@ +/* + * 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" +#include "DBvModel.h" + +class EqEffect; + + +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 ); + } + + 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; + bool m_analyseIn; + bool m_analyseOut; + + EqAnalyser m_inFftBands; + EqAnalyser m_outFftBands; + + bool m_inProgress; + + bool visable(); + + + + + +private: + EqEffect* m_effect; + + DBvModel m_inGainModel; + DBvModel 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_para1BwModel; + FloatModel m_para2BwModel; + FloatModel m_para3BwModel; + FloatModel m_para4BwModel; + 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; + + IntModel m_lpTypeModel; + IntModel m_hpTypeModel; + + 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..ee91ca8f4 --- /dev/null +++ b/plugins/Eq/EqControlsDialog.cpp @@ -0,0 +1,172 @@ +/* + * 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_inSpec = new EqSpectrumView( &controls->m_inFftBands, this); + m_inSpec->move( 51, 2 ); + m_inSpec->color = QColor( 238, 154, 120, 80 ); + m_outSpec = new EqSpectrumView( &controls->m_outFftBands, this); + m_outSpec->move( 51, 2 ); + m_outSpec->color = QColor(145, 205, 22, 80); + m_parameterWidget = new EqParameterWidget( this , controls ); + m_parameterWidget->move( 51, 2 ); + + 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_para1BwModel, &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_para2BwModel, &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_para3BwModel, &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_para4BwModel, &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 ); + + m_inGainFader = new EqFader( &controls->m_inGainModel, tr( "In Gain" ), this, &controls->m_inPeakL, &controls->m_inPeakR); + m_inGainFader->move( 10, 5 ); + m_inGainFader->setDisplayConversion( false ); + m_inGainFader->setHintText( tr( "Gain" ), "dBv"); + + + + m_outGainFader = new EqFader( &controls->m_outGainModel, tr( "Out Gain" ), this, &controls->m_outPeakL, &controls->m_outPeakR ); + m_outGainFader->move( 315, 5 ); + m_outGainFader->setDisplayConversion( false ); + m_outGainFader->setHintText( tr( "Gain" ), "dBv"); + //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); + m_gainFader->setDisplayConversion( false ); + m_gainFader->setHintText( tr( "Gain") , "dB"); + } + + for( int i = 0; i < m_parameterWidget->bandCount() ; i++) + { + m_resKnob = new Knob( knobBright_26, this ); + 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 ); + if(i > 1 && i < 6) { m_resKnob->setHintText( tr( "Bandwidth: " ) , " Octave" ); } + else { m_resKnob->setHintText( tr( "Resonance : " ) , "" ); } + + m_freqKnob = new Knob( knobBright_26, this ); + 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 , "" , 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 , "" , LedCheckBox::Green ); + m_hp12Box->move( cw*0 + ko, 170 ); + m_hp12Box->setModel( &controls->m_hp12Model ); + 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 , "" , 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 , "" , LedCheckBox::Green ); + m_lp12Box->move( cw*7 + ko -5 , 170 ); + m_lp12Box->setModel( &controls->m_lp12Model ); + 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 , "" , LedCheckBox::Green ); + m_lp48Box->move( cw*7 + ko - 5, 130 ); + 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 ); + 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 ); + hpBtnGrp->setModel( &m_controls->m_hpTypeModel,false); + +} + +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..d220d51ad --- /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; + EqSpectrumView* m_outSpec; + + 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..3407c7953 --- /dev/null +++ b/plugins/Eq/EqEffect.cpp @@ -0,0 +1,265 @@ +/* + * 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" +#include "EqFader.h" + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Equalizer", + 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 ) +{ + +} + + + + +EqEffect::~EqEffect() +{ +} + + + + +bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) +{ + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + m_eqControls.m_inProgress = true; + double outSum = 0.0; + for( fpp_t f = 0; f < frames; ++f ) + { + outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; + } + const float outGain = m_eqControls.m_outGainModel.getAmp(); + const int sampleRate = Engine::mixer()->processingSampleRate(); + sampleFrame m_inPeak = { 0, 0 }; + + if(m_eqControls.m_analyseIn ) + { + m_eqControls.m_inFftBands.analyze( buf, frames ); + } + else + { + m_eqControls.m_inFftBands.clear(); + } + gain(buf , frames, m_eqControls.m_inGainModel.getAmp() , &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[1] : m_eqControls.m_inPeakR; + + if(m_eqControls.m_hpActiveModel.value() ){ + + m_hp12.setParameters( sampleRate, m_eqControls.m_hpFeqModel.value(), m_eqControls.m_hpResModel.value(), 1 ); + m_hp12.processBuffer( buf, frames ); + + if( m_eqControls.m_hp24Model.value() || m_eqControls.m_hp48Model.value() ) + { + m_hp24.setParameters( sampleRate, m_eqControls.m_hpFeqModel.value(), m_eqControls.m_hpResModel.value(), 1 ); + m_hp24.processBuffer( buf, frames ); + } + + if( m_eqControls.m_hp48Model.value() ) + { + m_hp480.setParameters( sampleRate, m_eqControls.m_hpFeqModel.value(), m_eqControls.m_hpResModel.value(), 1 ); + m_hp480.processBuffer( buf, frames ); + + m_hp481.setParameters( sampleRate, m_eqControls.m_hpFeqModel.value(), m_eqControls.m_hpResModel.value(), 1 ); + m_hp481.processBuffer( buf, frames ); + } + } + + if( m_eqControls.m_lowShelfActiveModel.value() ) + { + m_lowShelf.setParameters( sampleRate, m_eqControls.m_lowShelfFreqModel.value(), m_eqControls.m_lowShelfResModel .value(), m_eqControls.m_lowShelfGainModel.value() ); + m_lowShelf.processBuffer( buf, frames ); + } + + if( m_eqControls.m_para1ActiveModel.value() ) + { + m_para1.setParameters( sampleRate, m_eqControls.m_para1FreqModel.value(), m_eqControls.m_para1BwModel.value(), m_eqControls.m_para1GainModel.value() ); + m_para1.processBuffer( buf, frames ); + } + + if( m_eqControls.m_para2ActiveModel.value() ) + { + m_para2.setParameters( sampleRate, m_eqControls.m_para2FreqModel.value(), m_eqControls.m_para2BwModel.value(), m_eqControls.m_para2GainModel.value() ); + m_para2.processBuffer( buf, frames ); + } + + if( m_eqControls.m_para3ActiveModel.value() ) + { + m_para3.setParameters( sampleRate, m_eqControls.m_para3FreqModel.value(), m_eqControls.m_para3BwModel.value(), m_eqControls.m_para3GainModel.value() ); + m_para3.processBuffer( buf, frames ); + } + + if( m_eqControls.m_para4ActiveModel.value() ) + { + m_para4.setParameters( sampleRate, m_eqControls.m_para4FreqModel.value(), m_eqControls.m_para4BwModel.value(), m_eqControls.m_para4GainModel.value() ); + m_para4.processBuffer( buf, frames ); + } + + if( m_eqControls.m_highShelfActiveModel.value() ) + { + m_highShelf.setParameters( sampleRate, m_eqControls.m_highShelfFreqModel.value(), m_eqControls.m_highShelfResModel.value(), m_eqControls.m_highShelfGainModel.value()); + m_highShelf.processBuffer( buf, frames ); + } + + if(m_eqControls.m_lpActiveModel.value() ){ + m_lp12.setParameters( sampleRate, m_eqControls.m_lpFreqModel.value(), m_eqControls.m_lpResModel.value(), 1 ); + m_lp12.processBuffer( buf, frames ); + + if( m_eqControls.m_lp24Model.value() || m_eqControls.m_lp48Model.value() ) + { + m_lp24.setParameters( sampleRate, m_eqControls.m_lpFreqModel.value(), m_eqControls.m_lpResModel.value(), 1 ); + m_lp24.processBuffer( buf, frames ); + } + + if( m_eqControls.m_lp48Model.value() ) + { + m_lp480.setParameters( sampleRate, m_eqControls.m_lpFreqModel.value(), m_eqControls.m_lpResModel.value(), 1 ); + m_lp480.processBuffer( buf, frames ); + + m_lp481.setParameters( sampleRate, m_eqControls.m_lpFreqModel.value(), m_eqControls.m_lpResModel.value(), 1 ); + m_lp481.processBuffer( buf, frames ); + } + } + + sampleFrame outPeak = { 0, 0 }; + gain( buf, frames, 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[1] : m_eqControls.m_outPeakR; + + checkGate( outSum / frames ); + if(m_eqControls.m_analyseOut ) + { + m_eqControls.m_outFftBands.analyze( buf, frames ); + setBandPeaks( &m_eqControls.m_outFftBands , ( int )( sampleRate * 0.5 ) ); + } + else + { + m_eqControls.m_outFftBands.clear(); + } + m_eqControls.m_inProgress = false; + return isRunning(); +} + + + + +float EqEffect::peakBand( float minF, float maxF, EqAnalyser *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(EqAnalyser *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_para1BwModel.value() * 0.5), + m_eqControls.m_para1FreqModel.value() + + (m_eqControls.m_para1FreqModel.value() * m_eqControls.m_para1BwModel.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_para2BwModel.value() * 0.5), + m_eqControls.m_para2FreqModel.value() + + (m_eqControls.m_para2FreqModel.value() * m_eqControls.m_para2BwModel.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_para3BwModel.value() * 0.5), + m_eqControls.m_para3FreqModel.value() + + (m_eqControls.m_para3FreqModel.value() * m_eqControls.m_para3BwModel.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_para4BwModel.value() * 0.5), + m_eqControls.m_para4FreqModel.value() + + (m_eqControls.m_para4FreqModel.value() * m_eqControls.m_para4BwModel.value() * 0.5), + fft , samplerate ); + + m_eqControls.m_highShelfPeakL = m_eqControls.m_highShelfPeakR = + peakBand( m_eqControls.m_highShelfFreqModel.value(), + samplerate * 0.5 , fft, samplerate ); +} + + + + + +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..654ae5bd2 --- /dev/null +++ b/plugins/Eq/EqEffect.h @@ -0,0 +1,104 @@ +/* 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; + } + inline void 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] ); + } + + } + } + +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; + + + + + 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( EqAnalyser *fft , int); + + +}; + +#endif // EQEFFECT_H diff --git a/plugins/Eq/EqFader.h b/plugins/Eq/EqFader.h new file mode 100644 index 000000000..9ef7f80c3 --- /dev/null +++ b/plugins/Eq/EqFader.h @@ -0,0 +1,101 @@ +/* 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_model = model; + setPeak_L( 0 ); + setPeak_R( 0 ); + } + + + + + ~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(); + } + + + + +private: + float* m_lPeak; + float* m_rPeak; + FloatModel* m_model; + +}; +#endif // EQFADER_H diff --git a/plugins/Eq/EqFilter.h b/plugins/Eq/EqFilter.h new file mode 100644 index 000000000..7e75a2e6c --- /dev/null +++ b/plugins/Eq/EqFilter.h @@ -0,0 +1,445 @@ +/* + * 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 inline void setQ( float res ) + { + if ( res != m_res ) + { + m_res = res; + calcCoefficents(); + } + } + + + + + virtual inline void setGain( float gain ) + { + if ( gain != m_gain ) + { + m_gain = gain; + calcCoefficents(); + } + } + + + + virtual inline void setParameters( float sampleRate, float freq, float res, float gain ) + { + bool hasChanged = false; + if( sampleRate != m_sampleRate ) + { + m_sampleRate = sampleRate; + hasChanged = true; + } + if ( freq != m_freq ) + { + m_freq = freq; + hasChanged = true; + } + if ( res != m_res ) + { + m_res = res; + hasChanged = true; + } + if ( gain != m_gain ) + { + m_gain = gain; + hasChanged = true; + } + + if ( hasChanged ) { 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; + float m_bw; +}; + + + + +/// +/// \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 * sinh( log( 2 ) / 2 * m_bw * w0 / sinf(w0) ); + + 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 ); + } + + virtual inline void setParameters( float sampleRate, float freq, float bw, float gain ) + { + bool hasChanged = false; + if( sampleRate != m_sampleRate ) + { + m_sampleRate = sampleRate; + hasChanged = true; + } + if ( freq != m_freq ) + { + m_freq = freq; + hasChanged = true; + } + if ( bw != m_bw ) + { + m_bw = bw; + hasChanged = true; + } + if ( gain != m_gain ) + { + m_gain = gain; + hasChanged = true; + } + + if ( hasChanged ) { calcCoefficents(); } + } +}; + + + + +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 ); + } +}; + + + + +class EqLinkwitzRiley : public StereoLinkwitzRiley +{ +public: + EqLinkwitzRiley() : + StereoLinkwitzRiley( 44100), + m_freq(0 ), + m_sr( 1 ) + { + } + + virtual inline void setSR( int sampleRate ) + { + if( sampleRate != m_sr ) + { + m_sr = sampleRate; + setSampleRate( sampleRate ); + setLowpass(m_freq); + } + } + + + + + 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; + + +}; + + + + +#endif // EQFILTER_H diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp new file mode 100644 index 000000000..d7318de3b --- /dev/null +++ b/plugins/Eq/EqParameterWidget.cpp @@ -0,0 +1,255 @@ +/* + * 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" +#include "EqControls.h" + +EqParameterWidget::EqParameterWidget( QWidget *parent, EqControls * controls ) : + QWidget( parent ), + m_bands ( 0 ), + m_selectedBand ( 0 ) +{ + 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(100); + 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_controls = controls; + tf = new TextFloat(); + tf->hide(); + +} + + + + +EqParameterWidget::~EqParameterWidget() +{ + if(m_bands) + { + delete[] m_bands; + m_bands = 0; + } +} + + + + +void EqParameterWidget::paintEvent( QPaintEvent *event ) +{ + 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, 1, 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 ); + float bw = m_bands[i].freq->value() * m_bands[i].res->value(); + 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 ); + 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 ) ); + } + else + { + painter.drawLine(freqToXPixel(m_bands[i].freq->value()-(bw * 0.5)),y,freqToXPixel(m_bands[i].freq->value()+(bw * 0.5)),y); + } + } +} + + + + +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; + const int inXmin = 228; + const int inXmax = 250; + const int inYmin = 20; + const int inYmax = 30; + + const int outXmin = 228; + const int outXmax = 250; + const int outYmin = 30; + const int outYmax = 40; + + if(event->x() > inXmin && event->x() < inXmax && event->y() > inYmin && event->y() < inYmax ) + { + m_controls->m_analyseIn = !m_controls->m_analyseIn; + } + + if(event->x() > outXmin && event->x() < outXmax && event->y() > outYmin && event->y() < outYmax ) + { + m_controls->m_analyseOut = !m_controls->m_analyseOut; + } + + tf->hide(); +} + + + + +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; + } + } + if( m_oldX > 0 && m_oldX < width() && m_oldY > 0 && m_oldY < height() ) + { + tf->setText( QString::number(xPixelToFreq( m_oldX )) + tr( "Hz ") ); + tf->show(); + const int x = event->x() > width() * 0.5 ? + m_oldX - tf->width() : + m_oldX; + tf->moveGlobal(this, QPoint( x, m_oldY - tf->height() ) ); + } +} + + + + +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 ) ), + x( 0 ), + y( 0 ), + name ( QString( "" ) ), + peakL( 0 ), + peakR( 0 ) +{ +} diff --git a/plugins/Eq/EqParameterWidget.h b/plugins/Eq/EqParameterWidget.h new file mode 100644 index 000000000..eb83ec962 --- /dev/null +++ b/plugins/Eq/EqParameterWidget.h @@ -0,0 +1,166 @@ +/* + * 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" +#include "TextFloat.h" + +class EqControls; + + +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 +{ + Q_OBJECT +public: + explicit EqParameterWidget( QWidget *parent = 0, EqControls * controls = 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; + EqControls *m_controls; + float m_pixelsPerUnitWidth; + float m_pixelsPerUnitHeight; + float m_pixelsPerOctave; + float m_scale; + EqBand* m_selectedBand; + TextFloat *tf; + + EqBand* selectNearestHandle( const int x, const int y ); + + 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 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/EqSpectrumView.h b/plugins/Eq/EqSpectrumView.h new file mode 100644 index 000000000..306937399 --- /dev/null +++ b/plugins/Eq/EqSpectrumView.h @@ -0,0 +1,225 @@ +/* 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 + +#include "qpainter.h" +//#include "eqeffect.h" +#include "qwidget.h" +#include "fft_helpers.h" +#include "Engine.h" + + +const int MAX_BANDS = 2048; + +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; + bool m_active; + + + EqAnalyser() : + m_framesFilledUp ( 0 ), + m_energy ( 0 ), + m_sr ( 1 ), + m_active ( true ) + { + 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() + { + return m_inProgress; + } + + + + void clear() + { + m_framesFilledUp = 0; + m_energy = 0; + memset( m_buffer, 0, sizeof( m_buffer ) ); + memset( m_bands, 0, sizeof( m_bands ) ); + } + + + + void analyze( sampleFrame *buf, const fpp_t frames ) + { + if ( m_active ) + { + 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; + m_active = false; + } + } +private: + bool m_inProgress; +}; + + +class EqSpectrumView : public QWidget +{ + +public: + explicit EqSpectrumView( EqAnalyser * b, QWidget * _parent = 0 ): + QWidget( _parent ), + m_sa( b ) + { + setFixedSize( 250, 116 ); + 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); + m_pixelsPerUnitWidth = width( ) / totalLength ; + m_scale = 1.5; + color = QColor( 255, 255, 255, 255 ); + } + + + + + virtual ~EqSpectrumView() + { + } + + + + + QColor color; + EqAnalyser *m_sa; + QPainterPath pp; + virtual void paintEvent( QPaintEvent* event ) + { + m_sa->m_active = isVisible(); + const int fh = height(); + const int LOWER_Y = -60; // dB + QPainter p( this ); + p.setPen( QPen( color, 1, Qt::SolidLine, Qt::RoundCap, Qt::BevelJoin ) ); + 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; + 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 ) ); + 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 ) ); + p.drawPath( pp ); + } + + + + + 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; +} ; + + +#endif // EQSPECTRUMVIEW_H diff --git a/plugins/Eq/artwork.png b/plugins/Eq/artwork.png new file mode 100644 index 000000000..33fa4960d Binary files /dev/null and b/plugins/Eq/artwork.png differ diff --git a/plugins/flanger/logo.png b/plugins/Eq/logo.png similarity index 100% rename from plugins/flanger/logo.png rename to plugins/Eq/logo.png diff --git a/plugins/Flanger/CMakeLists.txt b/plugins/Flanger/CMakeLists.txt new file mode 100644 index 000000000..c3febd094 --- /dev/null +++ b/plugins/Flanger/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(flanger FlangerEffect.cpp FlangerControls.cpp FlangerControlsDialog.cpp Noise.cpp QuadratureLfo.cpp MonoDelay.cpp MOCFILES FlangerControls.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/Flanger/FlangerControls.cpp b/plugins/Flanger/FlangerControls.cpp new file mode 100644 index 000000000..34d2b8f52 --- /dev/null +++ b/plugins/Flanger/FlangerControls.cpp @@ -0,0 +1,83 @@ +/* + * flangercontrols.cpp - defination of FlangerControls 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 "FlangerControls.h" +#include "FlangerEffect.h" +#include "Engine.h" +#include "Song.h" + + + +FlangerControls::FlangerControls( FlangerEffect *effect ) : + EffectControls ( effect ), + m_effect ( effect ), + m_delayTimeModel(0.001, 0.0001, 0.050, 0.0001, this, tr( "Delay Samples" ) ) , + m_lfoFrequencyModel( 0.25, 0.01, 5, 0.0001, 20000.0 ,this, tr( "Lfo Frequency" ) ), + m_lfoAmountModel( 0.0, 0.0, 0.0025 , 0.0001 , this , tr( "Seconds" ) ), + m_feedbackModel( 0.0 , 0.0 , 1.0 , 0.0001, this, tr( "Regen" ) ), + m_whiteNoiseAmountModel( 0.0 , 0.0 , 0.05 , 0.0001, this, tr( "Noise" ) ), + m_invertFeedbackModel ( false , this, tr( "Invert" ) ) + +{ + connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( changedSampleRate() ) ); +} + + + + +void FlangerControls::loadSettings( const QDomElement &_this ) +{ + m_delayTimeModel.loadSettings( _this, "DelayTimeSamples" ); + m_lfoFrequencyModel.loadSettings( _this, "LfoFrequency" ); + m_lfoAmountModel.loadSettings( _this, "LfoAmount" ); + m_feedbackModel.loadSettings( _this, "Feedback" ); + m_whiteNoiseAmountModel.loadSettings( _this, "WhiteNoise" ); + m_invertFeedbackModel.loadSettings( _this, "Invert" ); + +} + + + + +void FlangerControls::saveSettings( QDomDocument &doc, QDomElement &parent ) +{ + m_delayTimeModel.saveSettings( doc , parent, "DelayTimeSamples" ); + m_lfoFrequencyModel.saveSettings( doc, parent , "LfoFrequency" ); + m_lfoAmountModel.saveSettings( doc, parent , "LfoAmount" ); + m_feedbackModel.saveSettings( doc, parent, "Feedback" ) ; + m_whiteNoiseAmountModel.saveSettings( doc, parent , "WhiteNoise" ) ; + m_invertFeedbackModel.saveSettings( doc, parent, "Invert" ); +} + + + + +void FlangerControls::changedSampleRate() +{ + m_effect->changeSampleRate(); +} + + diff --git a/plugins/flanger/flangercontrols.h b/plugins/Flanger/FlangerControls.h similarity index 57% rename from plugins/flanger/flangercontrols.h rename to plugins/Flanger/FlangerControls.h index bbd52dbee..29f5a5aaf 100644 --- a/plugins/flanger/flangercontrols.h +++ b/plugins/Flanger/FlangerControls.h @@ -27,48 +27,48 @@ #include "EffectControls.h" #include "Knob.h" -#include "flangercontrolsdialog.h" +#include "FlangerControlsDialog.h" class FlangerEffect; class FlangerControls : public EffectControls { - Q_OBJECT + Q_OBJECT public: - FlangerControls( FlangerEffect* effect ); - virtual ~FlangerControls() - { - } - virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); - virtual void loadSettings ( const QDomElement &_this ); - inline virtual QString nodeName() const - { - return "Flanger"; - } - virtual int controlCount() - { - return 5; - } - virtual EffectControlDialog* createView() - { - return new FlangerControlsDialog( this ); - } + FlangerControls( FlangerEffect* effect ); + virtual ~FlangerControls() + { + } + virtual void saveSettings ( QDomDocument& doc, QDomElement& parent ); + virtual void loadSettings ( const QDomElement &_this ); + inline virtual QString nodeName() const + { + return "Flanger"; + } + virtual int controlCount() + { + return 5; + } + virtual EffectControlDialog* createView() + { + return new FlangerControlsDialog( this ); + } private slots: - void changedSampleRate(); + void changedSampleRate(); private: - FlangerEffect* m_effect; - FloatModel m_delayTimeModel; - TempoSyncKnobModel m_lfoFrequencyModel; - FloatModel m_lfoAmountModel; - FloatModel m_feedbackModel; - FloatModel m_whiteNoiseAmountModel; - BoolModel m_invertFeedbackModel; + FlangerEffect* m_effect; + FloatModel m_delayTimeModel; + TempoSyncKnobModel m_lfoFrequencyModel; + FloatModel m_lfoAmountModel; + FloatModel m_feedbackModel; + FloatModel m_whiteNoiseAmountModel; + BoolModel m_invertFeedbackModel; - friend class FlangerControlsDialog; - friend class FlangerEffect; + friend class FlangerControlsDialog; + friend class FlangerEffect; }; diff --git a/plugins/flanger/flangercontrolsdialog.cpp b/plugins/Flanger/FlangerControlsDialog.cpp similarity index 97% rename from plugins/flanger/flangercontrolsdialog.cpp rename to plugins/Flanger/FlangerControlsDialog.cpp index 8b3c61df4..26b223b29 100644 --- a/plugins/flanger/flangercontrolsdialog.cpp +++ b/plugins/Flanger/FlangerControlsDialog.cpp @@ -22,8 +22,8 @@ * */ -#include "flangercontrolsdialog.h" -#include "flangercontrols.h" +#include "FlangerControlsDialog.h" +#include "FlangerControls.h" #include "embed.h" #include "LedCheckbox.h" #include "TempoSyncKnob.h" diff --git a/plugins/flanger/flangercontrolsdialog.h b/plugins/Flanger/FlangerControlsDialog.h similarity index 91% rename from plugins/flanger/flangercontrolsdialog.h rename to plugins/Flanger/FlangerControlsDialog.h index bdfa90d45..1fef65a3e 100644 --- a/plugins/flanger/flangercontrolsdialog.h +++ b/plugins/Flanger/FlangerControlsDialog.h @@ -32,10 +32,10 @@ class FlangerControls; class FlangerControlsDialog : public EffectControlDialog { public: - FlangerControlsDialog( FlangerControls* controls ); - virtual ~FlangerControlsDialog() - { - } + FlangerControlsDialog( FlangerControls* controls ); + virtual ~FlangerControlsDialog() + { + } }; #endif // FLANGERCONTROLSDIALOG_H diff --git a/plugins/Flanger/FlangerEffect.cpp b/plugins/Flanger/FlangerEffect.cpp new file mode 100644 index 000000000..4a029dad8 --- /dev/null +++ b/plugins/Flanger/FlangerEffect.cpp @@ -0,0 +1,151 @@ +/* + * flangereffect.cpp - defination of FlangerEffect 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 "FlangerEffect.h" +#include "Engine.h" +#include "embed.cpp" + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT flanger_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Flanger", + QT_TRANSLATE_NOOP( "pluginBrowser", "A native flanger plugin" ), + "Dave French ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +} ; + + + + +FlangerEffect::FlangerEffect( Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key ) : + Effect( &flanger_plugin_descriptor, parent, key ), + m_flangerControls( this ) +{ + m_lfo = new QuadratureLfo( Engine::mixer()->processingSampleRate() ); + m_lDelay = new MonoDelay( 1, Engine::mixer()->processingSampleRate() ); + m_rDelay = new MonoDelay( 1, Engine::mixer()->processingSampleRate() ); + m_noise = new Noise; +} + + + + +FlangerEffect::~FlangerEffect() +{ + if(m_lDelay ) + { + delete m_lDelay; + } + if( m_rDelay ) + { + delete m_rDelay; + } + if(m_lfo ) + { + delete m_lfo; + } + if(m_noise) + { + delete m_noise; + } +} + + + + +bool FlangerEffect::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 length = m_flangerControls.m_delayTimeModel.value() * Engine::mixer()->processingSampleRate(); + const float noise = m_flangerControls.m_whiteNoiseAmountModel.value(); + float amplitude = m_flangerControls.m_lfoAmountModel.value() * Engine::mixer()->processingSampleRate(); + bool invertFeedback = m_flangerControls.m_invertFeedbackModel.value(); + m_lfo->setFrequency( m_flangerControls.m_lfoFrequencyModel.value() ); + m_lDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); + m_rDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); + sample_t dryS[2]; + float leftLfo; + float rightLfo; + for( fpp_t f = 0; f < frames; ++f ) + { + buf[f][0] += m_noise->tick() * noise; + buf[f][1] += m_noise->tick() * noise; + dryS[0] = buf[f][0]; + dryS[1] = buf[f][1]; + m_lfo->tick(&leftLfo, &rightLfo); + m_lDelay->setLength( ( float )length + ( amplitude * leftLfo ) ); + m_rDelay->setLength( ( float )length+ ( amplitude * rightLfo ) ); + if(invertFeedback) + { + m_lDelay->tick( &buf[f][1] ); + m_rDelay->tick(&buf[f][0] ); + } else + { + m_lDelay->tick( &buf[f][0] ); + m_rDelay->tick( &buf[f][1] ); + } + + 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 FlangerEffect::changeSampleRate() +{ + m_lfo->setSampleRate( Engine::mixer()->processingSampleRate() ); + m_lDelay->setSampleRate( Engine::mixer()->processingSampleRate() ); + m_rDelay->setSampleRate( Engine::mixer()->processingSampleRate() ); +} + + + +extern "C" +{ + +//needed for getting plugin out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) +{ + return new FlangerEffect( parent , static_cast( data ) ); +} + +}} diff --git a/plugins/flanger/flangereffect.h b/plugins/Flanger/FlangerEffect.h similarity index 66% rename from plugins/flanger/flangereffect.h rename to plugins/Flanger/FlangerEffect.h index d476a056a..ac6125623 100644 --- a/plugins/flanger/flangereffect.h +++ b/plugins/Flanger/FlangerEffect.h @@ -27,30 +27,30 @@ #define FLANGEREFFECT_H #include "Effect.h" -#include "flangercontrols.h" -#include "quadraturelfo.h" -#include "monodelay.h" -#include "noise.h" +#include "FlangerControls.h" +#include "QuadratureLfo.h" +#include "MonoDelay.h" +#include "Noise.h" class FlangerEffect : public Effect { public: - FlangerEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); - virtual ~FlangerEffect(); - virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); - virtual EffectControls* controls() - { - return &m_flangerControls; - } - void changeSampleRate(); + FlangerEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); + virtual ~FlangerEffect(); + virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); + virtual EffectControls* controls() + { + return &m_flangerControls; + } + void changeSampleRate(); private: - FlangerControls m_flangerControls; - MonoDelay* m_lDelay; - MonoDelay* m_rDelay; - QuadratureLfo* m_lfo; - Noise* m_noise; + FlangerControls m_flangerControls; + MonoDelay* m_lDelay; + MonoDelay* m_rDelay; + QuadratureLfo* m_lfo; + Noise* m_noise; }; diff --git a/plugins/flanger/monodelay.cpp b/plugins/Flanger/MonoDelay.cpp similarity index 55% rename from plugins/flanger/monodelay.cpp rename to plugins/Flanger/MonoDelay.cpp index 4d1fdaa4c..9afc3ee17 100644 --- a/plugins/flanger/monodelay.cpp +++ b/plugins/Flanger/MonoDelay.cpp @@ -22,20 +22,20 @@ * */ -#include "monodelay.h" +#include "MonoDelay.h" #include "interpolation.h" #include "lmms_math.h" MonoDelay::MonoDelay( int maxTime , int sampleRate ) { - m_buffer = 0; - m_maxTime = maxTime; - m_maxLength = maxTime * sampleRate; - m_length = m_maxLength; + m_buffer = 0; + m_maxTime = maxTime; + m_maxLength = maxTime * sampleRate; + m_length = m_maxLength; - m_index = 0; - m_feedback = 0.0f; - setSampleRate( sampleRate ); + m_index = 0; + m_feedback = 0.0f; + setSampleRate( sampleRate ); } @@ -43,10 +43,10 @@ MonoDelay::MonoDelay( int maxTime , int sampleRate ) MonoDelay::~MonoDelay() { - if( m_buffer ) - { - delete m_buffer; - } + if( m_buffer ) + { + delete m_buffer; + } } @@ -54,24 +54,24 @@ MonoDelay::~MonoDelay() void MonoDelay::tick( sample_t* sample ) { - m_buffer[m_index] = *sample; - int readIndex = m_index - ( int )m_length; - if(readIndex < 0) - { - readIndex += m_maxLength; - } - float fract = fraction( m_length ); - if(readIndex != m_maxLength-1 ) - { - *sample = linearInterpolate(m_buffer[readIndex] , - m_buffer[readIndex+1], fract ); - } else - { - *sample = linearInterpolate(m_buffer[readIndex] , - m_buffer[0], fract ); - } - m_buffer[m_index] += *sample * m_feedback; - m_index = ( m_index +1 ) % m_maxLength; + m_buffer[m_index] = *sample; + int readIndex = m_index - ( int )m_length - 1; + if(readIndex < 0) + { + readIndex += m_maxLength; + } + float fract = 1.0f - fraction( m_length ); + if(readIndex != m_maxLength-1 ) + { + *sample = linearInterpolate(m_buffer[readIndex] , + m_buffer[readIndex+1], fract ); + } else + { + *sample = linearInterpolate(m_buffer[readIndex] , + m_buffer[0], fract ); + } + m_buffer[m_index] += *sample * m_feedback; + m_index = ( m_index +1 ) % m_maxLength; } @@ -79,11 +79,11 @@ void MonoDelay::tick( sample_t* sample ) void MonoDelay::setSampleRate( int sampleRate ) { - if( m_buffer ) - { - delete m_buffer; - } + if( m_buffer ) + { + delete m_buffer; + } - m_buffer = new sample_t[( int )( sampleRate * m_maxTime )]; + m_buffer = new sample_t[( int )( sampleRate * m_maxTime )]; } diff --git a/plugins/flanger/monodelay.h b/plugins/Flanger/MonoDelay.h similarity index 67% rename from plugins/flanger/monodelay.h rename to plugins/Flanger/MonoDelay.h index 8c544fd6c..866c5e697 100644 --- a/plugins/flanger/monodelay.h +++ b/plugins/Flanger/MonoDelay.h @@ -30,31 +30,31 @@ class MonoDelay { public: - MonoDelay( int maxTime , int sampleRate ); - ~MonoDelay(); - inline void setLength( float length ) - { - if( length <= m_maxLength && length >= 0 ) - { - m_length = length; - } - } + MonoDelay( int maxTime , int sampleRate ); + ~MonoDelay(); + inline void setLength( float length ) + { + if( length <= m_maxLength && length >= 0 ) + { + m_length = length; + } + } - inline void setFeedback( float feedback ) - { - m_feedback = feedback; - } + inline void setFeedback( float feedback ) + { + m_feedback = feedback; + } - void tick( sample_t* sample ); - void setSampleRate( int sampleRate ); + void tick( sample_t* sample ); + void setSampleRate( int sampleRate ); private: - sample_t* m_buffer; - int m_maxLength; - float m_length; - int m_index; - float m_feedback; - float m_maxTime; + sample_t* m_buffer; + int m_maxLength; + float m_length; + int m_index; + float m_feedback; + float m_maxTime; }; #endif // MONODELAY_H diff --git a/plugins/flanger/noise.cpp b/plugins/Flanger/Noise.cpp similarity index 87% rename from plugins/flanger/noise.cpp rename to plugins/Flanger/Noise.cpp index bd0d84b98..4d4c06e0a 100644 --- a/plugins/flanger/noise.cpp +++ b/plugins/Flanger/Noise.cpp @@ -22,12 +22,12 @@ * */ -#include "noise.h" +#include "Noise.h" #include "lmms_math.h" Noise::Noise() { - inv_randmax = 1.0/FAST_RAND_MAX; /* for range of 0 - 1.0 */ + inv_randmax = 1.0/FAST_RAND_MAX; /* for range of 0 - 1.0 */ } @@ -35,5 +35,5 @@ Noise::Noise() float Noise::tick() { - return (float) ((2.0 * fast_rand() * inv_randmax) - 1.0); + return (float) ((2.0 * fast_rand() * inv_randmax) - 1.0); } diff --git a/plugins/flanger/noise.h b/plugins/Flanger/Noise.h similarity index 94% rename from plugins/flanger/noise.h rename to plugins/Flanger/Noise.h index 008700ed7..ea0a854bd 100644 --- a/plugins/flanger/noise.h +++ b/plugins/Flanger/Noise.h @@ -28,10 +28,10 @@ class Noise { public: - Noise(); - float tick(); + Noise(); + float tick(); private: - double inv_randmax; + double inv_randmax; }; #endif // NOISE_H diff --git a/plugins/flanger/quadraturelfo.cpp b/plugins/Flanger/QuadratureLfo.cpp similarity index 88% rename from plugins/flanger/quadraturelfo.cpp rename to plugins/Flanger/QuadratureLfo.cpp index 1e4d7d0f1..bc31dc326 100644 --- a/plugins/flanger/quadraturelfo.cpp +++ b/plugins/Flanger/QuadratureLfo.cpp @@ -22,17 +22,17 @@ * */ -#include "quadraturelfo.h" +#include "QuadratureLfo.h" QuadratureLfo::QuadratureLfo( int sampleRate ) { - setSampleRate(sampleRate); + setSampleRate(sampleRate); } void QuadratureLfo::tick( float *s, float *c ) { - *s = sinf( m_phase ); - *c = cosf( m_phase ); - m_phase += m_increment; + *s = sinf( m_phase ); + *c = cosf( m_phase ); + m_phase += m_increment; } diff --git a/plugins/flanger/quadraturelfo.h b/plugins/Flanger/QuadratureLfo.h similarity index 58% rename from plugins/flanger/quadraturelfo.h rename to plugins/Flanger/QuadratureLfo.h index f884f691c..04f2e62df 100644 --- a/plugins/flanger/quadraturelfo.h +++ b/plugins/Flanger/QuadratureLfo.h @@ -30,44 +30,44 @@ class QuadratureLfo { public: - QuadratureLfo( int sampleRate ); - ~QuadratureLfo() - { - } + QuadratureLfo( int sampleRate ); + ~QuadratureLfo() + { + } - inline void setFrequency( double frequency ) - { - if( frequency < 0 || frequency > ( m_samplerate / 2.0 ) || frequency == m_frequency ) - { - return; - } - m_frequency = frequency; - m_increment = m_frequency * m_twoPiOverSr; + inline void setFrequency( double frequency ) + { + if( frequency < 0 || frequency > ( m_samplerate / 2.0 ) || frequency == m_frequency ) + { + return; + } + m_frequency = frequency; + m_increment = m_frequency * m_twoPiOverSr; - if( m_phase >= F_2PI ) - { - m_phase -= F_2PI; - } - } + if( m_phase >= F_2PI ) + { + m_phase -= F_2PI; + } + } - inline void setSampleRate ( int samplerate ) - { - m_samplerate = samplerate; - m_twoPiOverSr = F_2PI / samplerate; - m_increment = m_frequency * m_twoPiOverSr; - } + inline void setSampleRate ( int samplerate ) + { + m_samplerate = samplerate; + m_twoPiOverSr = F_2PI / samplerate; + m_increment = m_frequency * m_twoPiOverSr; + } - void tick( float *s, float *c ); + void tick( float *s, float *c ); private: - double m_frequency; - double m_phase; - double m_increment; - double m_twoPiOverSr; - int m_samplerate; + double m_frequency; + double m_phase; + double m_increment; + double m_twoPiOverSr; + int m_samplerate; }; diff --git a/plugins/flanger/artwork.png b/plugins/Flanger/artwork.png similarity index 100% rename from plugins/flanger/artwork.png rename to plugins/Flanger/artwork.png diff --git a/plugins/Flanger/logo.png b/plugins/Flanger/logo.png new file mode 100644 index 000000000..89e9f3680 Binary files /dev/null and b/plugins/Flanger/logo.png differ diff --git a/plugins/LadspaEffect/calf/src/audio_fx.cpp b/plugins/LadspaEffect/calf/src/audio_fx.cpp index a5695be88..f42ccc0a8 100644 --- a/plugins/LadspaEffect/calf/src/audio_fx.cpp +++ b/plugins/LadspaEffect/calf/src/audio_fx.cpp @@ -567,6 +567,22 @@ lookahead_limiter::lookahead_limiter() { asc_coeff = 1.f; } +lookahead_limiter::~lookahead_limiter() +{ + if( buffer != NULL) + { + free(buffer); + } + if( nextpos != NULL) + { + free(nextpos); + } + if( nextdelta != NULL) + { + free(nextdelta); + } +} + void lookahead_limiter::activate() { is_active = true; diff --git a/plugins/LadspaEffect/calf/src/calf/audio_fx.h b/plugins/LadspaEffect/calf/src/calf/audio_fx.h index f71880289..1799de944 100644 --- a/plugins/LadspaEffect/calf/src/calf/audio_fx.h +++ b/plugins/LadspaEffect/calf/src/calf/audio_fx.h @@ -608,6 +608,7 @@ public: void reset_asc(); bool get_asc(); lookahead_limiter(); + ~lookahead_limiter(); void set_multi(bool set); void process(float &left, float &right, float *multi_buffer); void set_sample_rate(uint32_t sr); diff --git a/plugins/LadspaEffect/calf/src/calf/ladspa_wrap.h b/plugins/LadspaEffect/calf/src/calf/ladspa_wrap.h index d94ebcea3..daf7ae258 100644 --- a/plugins/LadspaEffect/calf/src/calf/ladspa_wrap.h +++ b/plugins/LadspaEffect/calf/src/calf/ladspa_wrap.h @@ -47,6 +47,7 @@ struct ladspa_instance: public plugin_ctl_iface #endif ladspa_instance(audio_module_iface *_module, ladspa_plugin_metadata_set *_ladspa, int sample_rate); + virtual ~ladspa_instance(); virtual const line_graph_iface *get_line_graph_iface() const { return module->get_line_graph_iface(); } virtual float get_param_value(int param_no); virtual void set_param_value(int param_no, float value); diff --git a/plugins/LadspaEffect/calf/src/calf/modules.h b/plugins/LadspaEffect/calf/src/calf/modules.h index fb9ba7790..8cde47f62 100644 --- a/plugins/LadspaEffect/calf/src/calf/modules.h +++ b/plugins/LadspaEffect/calf/src/calf/modules.h @@ -263,6 +263,7 @@ class mono_audio_module: } public: mono_audio_module(); + ~mono_audio_module(); void params_changed(); void activate(); void set_sample_rate(uint32_t sr); @@ -291,6 +292,7 @@ class stereo_audio_module: } public: stereo_audio_module(); + ~stereo_audio_module(); void params_changed(); void activate(); void set_sample_rate(uint32_t sr); diff --git a/plugins/LadspaEffect/calf/src/calf/modules_limit.h b/plugins/LadspaEffect/calf/src/calf/modules_limit.h index d5d4b9a52..850aff80d 100644 --- a/plugins/LadspaEffect/calf/src/calf/modules_limit.h +++ b/plugins/LadspaEffect/calf/src/calf/modules_limit.h @@ -84,6 +84,7 @@ public: uint32_t srate; bool is_active; multibandlimiter_audio_module(); + ~multibandlimiter_audio_module(); void activate(); void deactivate(); void params_changed(); diff --git a/plugins/LadspaEffect/calf/src/modules.cpp b/plugins/LadspaEffect/calf/src/modules.cpp index ba06f0201..98b4b8df3 100644 --- a/plugins/LadspaEffect/calf/src/modules.cpp +++ b/plugins/LadspaEffect/calf/src/modules.cpp @@ -460,6 +460,14 @@ stereo_audio_module::stereo_audio_module() { meter_outR = 0.f; } +stereo_audio_module::~stereo_audio_module() +{ + if( buffer != NULL ) + { + free(buffer); + } +} + void stereo_audio_module::activate() { active = true; } @@ -686,6 +694,14 @@ mono_audio_module::mono_audio_module() { meter_outR = 0.f; } +mono_audio_module::~mono_audio_module() +{ + if( buffer != NULL ) + { + free(buffer); + } +} + void mono_audio_module::activate() { active = true; } diff --git a/plugins/LadspaEffect/calf/src/modules_limit.cpp b/plugins/LadspaEffect/calf/src/modules_limit.cpp index f7fb6fc80..3930078bb 100644 --- a/plugins/LadspaEffect/calf/src/modules_limit.cpp +++ b/plugins/LadspaEffect/calf/src/modules_limit.cpp @@ -252,6 +252,14 @@ multibandlimiter_audio_module::multibandlimiter_audio_module() asc_old = true; } +multibandlimiter_audio_module::~multibandlimiter_audio_module() +{ + if( buffer != NULL) + { + free(buffer); + } +} + void multibandlimiter_audio_module::activate() { is_active = true; diff --git a/plugins/LadspaEffect/calf/src/plugin.cpp b/plugins/LadspaEffect/calf/src/plugin.cpp index 7b651de88..0cc02a885 100644 --- a/plugins/LadspaEffect/calf/src/plugin.cpp +++ b/plugins/LadspaEffect/calf/src/plugin.cpp @@ -54,6 +54,11 @@ ladspa_instance::ladspa_instance(audio_module_iface *_module, ladspa_plugin_meta module->post_instantiate(); } +ladspa_instance::~ladspa_instance() +{ + delete module; +} + float ladspa_instance::get_param_value(int param_no) { // XXXKF hack diff --git a/plugins/LadspaEffect/swh/dj_eq_1901.c b/plugins/LadspaEffect/swh/dj_eq_1901.c index a4baa9602..65205c123 100644 --- a/plugins/LadspaEffect/swh/dj_eq_1901.c +++ b/plugins/LadspaEffect/swh/dj_eq_1901.c @@ -116,6 +116,8 @@ static void activateDj_eq_mono(LADSPA_Handle instance) { } static void cleanupDj_eq_mono(LADSPA_Handle instance) { + Dj_eq_mono *plugin_data = (Dj_eq_mono *)instance; + free(plugin_data->filters); free(instance); } @@ -283,6 +285,8 @@ static void activateDj_eq(LADSPA_Handle instance) { } static void cleanupDj_eq(LADSPA_Handle instance) { + Dj_eq *plugin_data = (Dj_eq *)instance; + free(plugin_data->filters); free(instance); } diff --git a/plugins/LadspaEffect/swh/fast_lookahead_limiter_1913.c b/plugins/LadspaEffect/swh/fast_lookahead_limiter_1913.c index 8589caf3a..eecd72e8e 100644 --- a/plugins/LadspaEffect/swh/fast_lookahead_limiter_1913.c +++ b/plugins/LadspaEffect/swh/fast_lookahead_limiter_1913.c @@ -137,6 +137,7 @@ static void cleanupFastLookaheadLimiter(LADSPA_Handle instance) { #line 188 "fast_lookahead_limiter_1913.xml" FastLookaheadLimiter *plugin_data = (FastLookaheadLimiter *)instance; free(plugin_data->buffer); + free(plugin_data->chunks); free(instance); } diff --git a/plugins/LadspaEffect/swh/imp_1199.c b/plugins/LadspaEffect/swh/imp_1199.c index a5c7d3876..d172a927d 100644 --- a/plugins/LadspaEffect/swh/imp_1199.c +++ b/plugins/LadspaEffect/swh/imp_1199.c @@ -202,6 +202,11 @@ static void cleanupImp(LADSPA_Handle instance) { local_free(plugin_data->op); local_free(plugin_data->overlap); local_free(plugin_data->opc); + unsigned int i; + for (i=0; iimpulse_freq[i]); + } + local_free(plugin_data->impulse_freq); free(instance); } diff --git a/plugins/LadspaEffect/swh/vynil_1905.c b/plugins/LadspaEffect/swh/vynil_1905.c index 187aa0f8f..c6aa448b0 100644 --- a/plugins/LadspaEffect/swh/vynil_1905.c +++ b/plugins/LadspaEffect/swh/vynil_1905.c @@ -163,6 +163,7 @@ static void cleanupVynil(LADSPA_Handle instance) { free(plugin_data->buffer_m); free(plugin_data->buffer_s); free(plugin_data->click_buffer); + free(plugin_data->highp); free(plugin_data->lowp_m); free(plugin_data->lowp_s); free(plugin_data->noise_filt); diff --git a/plugins/LadspaEffect/tap/tap_eq.c b/plugins/LadspaEffect/tap/tap_eq.c index daf9463a1..4cc0eb911 100644 --- a/plugins/LadspaEffect/tap/tap_eq.c +++ b/plugins/LadspaEffect/tap/tap_eq.c @@ -145,7 +145,8 @@ activate_eq(LADSPA_Handle instance) { static void cleanup_eq(LADSPA_Handle instance) { - + eq *plugin_data = (eq *)instance; + free(plugin_data->filters); free(instance); } diff --git a/plugins/LadspaEffect/tap/tap_eqbw.c b/plugins/LadspaEffect/tap/tap_eqbw.c index fd9d15d46..3d6b39f26 100644 --- a/plugins/LadspaEffect/tap/tap_eqbw.c +++ b/plugins/LadspaEffect/tap/tap_eqbw.c @@ -167,7 +167,8 @@ activate_eq(LADSPA_Handle instance) { static void cleanup_eq(LADSPA_Handle instance) { - + eq *plugin_data = (eq *)instance; + free(plugin_data->filters); free(instance); } diff --git a/plugins/SpectrumAnalyzer/SpectrumAnalyzer.cpp b/plugins/SpectrumAnalyzer/SpectrumAnalyzer.cpp index c06d4aa2b..4af1e3ad9 100644 --- a/plugins/SpectrumAnalyzer/SpectrumAnalyzer.cpp +++ b/plugins/SpectrumAnalyzer/SpectrumAnalyzer.cpp @@ -150,7 +150,7 @@ bool SpectrumAnalyzer::processAudioBuffer( sampleFrame* _buf, const fpp_t _frame m_framesFilledUp = 0; - checkGate( 0 ); + checkGate( 1 ); return isRunning(); } diff --git a/plugins/VstEffect/VstSubPluginFeatures.cpp b/plugins/VstEffect/VstSubPluginFeatures.cpp index 8e0c623c3..3f218c284 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.cpp +++ b/plugins/VstEffect/VstSubPluginFeatures.cpp @@ -31,6 +31,7 @@ #include "ConfigManager.h" + VstSubPluginFeatures::VstSubPluginFeatures( Plugin::PluginTypes _type ) : SubPluginFeatures( _type ) { @@ -52,16 +53,40 @@ void VstSubPluginFeatures::fillDescriptionWidget( QWidget * _parent, void VstSubPluginFeatures::listSubPluginKeys( const Plugin::Descriptor * _desc, KeyList & _kl ) const { - QStringList dlls = QDir( ConfigManager::inst()->vstDir() ). - entryList( QStringList() << "*.dll", - QDir::Files, QDir::Name ); + QStringList *dlls = new QStringList(); + const QString path = QString(""); + addPluginsFromDir(dlls, path ); // TODO: eval m_type - for( QStringList::ConstIterator it = dlls.begin(); - it != dlls.end(); ++it ) + for( QStringList::ConstIterator it = dlls->begin(); + it != dlls->end(); ++it ) { EffectKey::AttributeMap am; am["file"] = *it; _kl.push_back( Key( _desc, QFileInfo( *it ).baseName(), am ) ); } + delete dlls; +} + +void VstSubPluginFeatures::addPluginsFromDir( QStringList* filenames, QString path ) const +{ + QStringList dirs = QDir ( ConfigManager::inst()->vstDir() + path ). + entryList( QStringList() << "*" , + QDir::Dirs, QDir::Name ); + for( int i = 0; i < dirs.size(); i++ ) + { + if( dirs.at( i )[0] != '.' ) + { + addPluginsFromDir( filenames, path+QDir::separator() + dirs.at( i ) ); + } + } + QStringList dlls = QDir( ConfigManager::inst()->vstDir() + path ). + entryList( QStringList() << "*.dll", + QDir::Files, QDir::Name ); + for( int i = 0; i < dlls.size(); i++ ) + { + QString fName = path + QDir::separator() + dlls.at( i ); + fName.remove( 0, 1 ); + filenames->append( fName ); + } } diff --git a/plugins/VstEffect/VstSubPluginFeatures.h b/plugins/VstEffect/VstSubPluginFeatures.h index 51fdc05cd..db2adcfab 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.h +++ b/plugins/VstEffect/VstSubPluginFeatures.h @@ -40,8 +40,13 @@ public: virtual void listSubPluginKeys( const Plugin::Descriptor * _desc, KeyList & _kl ) const; - +private: + void addPluginsFromDir(QStringList* filenames, QString path) const; } ; + + + + #endif diff --git a/plugins/delay/delaycontrolsdialog.cpp b/plugins/delay/delaycontrolsdialog.cpp index 01449cd2b..aae4c0aa4 100644 --- a/plugins/delay/delaycontrolsdialog.cpp +++ b/plugins/delay/delaycontrolsdialog.cpp @@ -71,4 +71,3 @@ DelayControlsDialog::DelayControlsDialog( DelayControls *controls ) : } -#include "moc_delaycontrols.cxx" diff --git a/plugins/dynamics_processor/dynamics_processor.cpp b/plugins/dynamics_processor/dynamics_processor.cpp index 859e661d3..e6e06662d 100644 --- a/plugins/dynamics_processor/dynamics_processor.cpp +++ b/plugins/dynamics_processor/dynamics_processor.cpp @@ -214,11 +214,10 @@ bool dynProcEffect::processAudioBuffer( sampleFrame * _buf, s[0] *= outputGain; s[1] *= outputGain; + out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1]; // mix wet/dry signals _buf[f][0] = d * _buf[f][0] + w * s[0]; _buf[f][1] = d * _buf[f][1] + w * s[1]; - - out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1]; } checkGate( out_sum / _frames ); diff --git a/plugins/flanger/CMakeLists.txt b/plugins/flanger/CMakeLists.txt deleted file mode 100644 index bb3579e73..000000000 --- a/plugins/flanger/CMakeLists.txt +++ /dev/null @@ -1,3 +0,0 @@ -INCLUDE(BuildPlugin) - -BUILD_PLUGIN(flanger flangereffect.cpp flangercontrols.cpp flangercontrolsdialog.cpp noise.cpp quadraturelfo.cpp monodelay.cpp MOCFILES flangercontrols.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/flanger/flangercontrols.cpp b/plugins/flanger/flangercontrols.cpp deleted file mode 100644 index 55d7f215c..000000000 --- a/plugins/flanger/flangercontrols.cpp +++ /dev/null @@ -1,83 +0,0 @@ -/* - * flangercontrols.cpp - defination of FlangerControls 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 "flangercontrols.h" -#include "flangereffect.h" -#include "Engine.h" -#include "Song.h" - - - -FlangerControls::FlangerControls( FlangerEffect *effect ) : - EffectControls ( effect ), - m_effect ( effect ), - m_delayTimeModel(0.001, 0.0001, 0.050, 0.0001, this, tr( "Delay Samples" ) ) , - m_lfoFrequencyModel( 0.25, 0.01, 5, 0.0001, 20000.0 ,this, tr( "Lfo Frequency" ) ), - m_lfoAmountModel( 0.0, 0.0, 0.0025 , 0.0001 , this , tr( "Seconds" ) ), - m_feedbackModel( 0.0 , 0.0 , 1.0 , 0.0001, this, tr( "Regen" ) ), - m_whiteNoiseAmountModel( 0.0 , 0.0 , 0.05 , 0.0001, this, tr( "Noise" ) ), - m_invertFeedbackModel ( false , this, tr( "Invert" ) ) - -{ - connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( changedSampleRate() ) ); -} - - - - -void FlangerControls::loadSettings( const QDomElement &_this ) -{ - m_delayTimeModel.loadSettings( _this, "DelayTimeSamples" ); - m_lfoFrequencyModel.loadSettings( _this, "LfoFrequency" ); - m_lfoAmountModel.loadSettings( _this, "LfoAmount" ); - m_feedbackModel.loadSettings( _this, "Feedback" ); - m_whiteNoiseAmountModel.loadSettings( _this, "WhiteNoise" ); - m_invertFeedbackModel.loadSettings( _this, "Invert" ); - -} - - - - -void FlangerControls::saveSettings( QDomDocument &doc, QDomElement &parent ) -{ - m_delayTimeModel.saveSettings( doc , parent, "DelayTimeSamples" ); - m_lfoFrequencyModel.saveSettings( doc, parent , "LfoFrequency" ); - m_lfoAmountModel.saveSettings( doc, parent , "LfoAmount" ); - m_feedbackModel.saveSettings( doc, parent, "Feedback" ) ; - m_whiteNoiseAmountModel.saveSettings( doc, parent , "WhiteNoise" ) ; - m_invertFeedbackModel.saveSettings( doc, parent, "Invert" ); -} - - - - -void FlangerControls::changedSampleRate() -{ - m_effect->changeSampleRate(); -} - - diff --git a/plugins/flanger/flangereffect.cpp b/plugins/flanger/flangereffect.cpp deleted file mode 100644 index a309e38c9..000000000 --- a/plugins/flanger/flangereffect.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * flangereffect.cpp - defination of FlangerEffect 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 "flangereffect.h" -#include "Engine.h" -#include "embed.cpp" - -extern "C" -{ - -Plugin::Descriptor PLUGIN_EXPORT flanger_plugin_descriptor = -{ - STRINGIFY( PLUGIN_NAME ), - "Flanger", - QT_TRANSLATE_NOOP( "pluginBrowser", "A native flanger plugin" ), - "Dave French ", - 0x0100, - Plugin::Effect, - new PluginPixmapLoader( "logo" ), - NULL, - NULL -} ; - - - - -FlangerEffect::FlangerEffect( Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key ) : - Effect( &flanger_plugin_descriptor, parent, key ), - m_flangerControls( this ) -{ - m_lfo = new QuadratureLfo( Engine::mixer()->processingSampleRate() ); - m_lDelay = new MonoDelay( 1, Engine::mixer()->processingSampleRate() ); - m_rDelay = new MonoDelay( 1, Engine::mixer()->processingSampleRate() ); - m_noise = new Noise; -} - - - - -FlangerEffect::~FlangerEffect() -{ - if(m_lDelay ) - { - delete m_lDelay; - } - if( m_rDelay ) - { - delete m_rDelay; - } - if(m_lfo ) - { - delete m_lfo; - } - if(m_noise) - { - delete m_noise; - } -} - - - - -bool FlangerEffect::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 length = m_flangerControls.m_delayTimeModel.value() * Engine::mixer()->processingSampleRate(); - const float noise = m_flangerControls.m_whiteNoiseAmountModel.value(); - float amplitude = m_flangerControls.m_lfoAmountModel.value() * Engine::mixer()->processingSampleRate(); - bool invertFeedback = m_flangerControls.m_invertFeedbackModel.value(); - m_lfo->setFrequency( m_flangerControls.m_lfoFrequencyModel.value() ); - m_lDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); - m_rDelay->setFeedback( m_flangerControls.m_feedbackModel.value() ); - sample_t dryS[2]; - float leftLfo; - float rightLfo; - for( fpp_t f = 0; f < frames; ++f ) - { - buf[f][0] += m_noise->tick() * noise; - buf[f][1] += m_noise->tick() * noise; - dryS[0] = buf[f][0]; - dryS[1] = buf[f][1]; - m_lfo->tick(&leftLfo, &rightLfo); - m_lDelay->setLength( ( float )length + ( amplitude * leftLfo ) ); - m_rDelay->setLength( ( float )length+ ( amplitude * rightLfo ) ); - if(invertFeedback) - { - m_lDelay->tick( &buf[f][1] ); - m_rDelay->tick(&buf[f][0] ); - } else - { - m_lDelay->tick( &buf[f][0] ); - m_rDelay->tick( &buf[f][1] ); - } - - 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 FlangerEffect::changeSampleRate() -{ - m_lfo->setSampleRate( Engine::mixer()->processingSampleRate() ); - m_lDelay->setSampleRate( Engine::mixer()->processingSampleRate() ); - m_rDelay->setSampleRate( Engine::mixer()->processingSampleRate() ); -} - - - -extern "C" -{ - -//needed for getting plugin out of shared lib -Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) -{ - return new FlangerEffect( parent , static_cast( data ) ); -} - -}} diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 4a71437f4..38b789d13 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -172,10 +172,11 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable ) QString p = m_plugin; - if( QFileInfo( p ).dir().isRelative() ) - { - p = ConfigManager::inst()->vstDir() + QDir::separator() + p; - } + if( QFileInfo( p ).dir().isRelative() ) + { + p = ConfigManager::inst()->vstDir() + p; + } + sendMessage( message( IdVstLoadPlugin ).addString( QSTR_TO_STDSTR( p ) ) ); diff --git a/plugins/waveshaper/waveshaper.cpp b/plugins/waveshaper/waveshaper.cpp index ab379d8d1..7389e27e1 100644 --- a/plugins/waveshaper/waveshaper.cpp +++ b/plugins/waveshaper/waveshaper.cpp @@ -130,11 +130,10 @@ bool waveShaperEffect::processAudioBuffer( sampleFrame * _buf, s[0] *= output; s[1] *= output; + out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1]; // mix wet/dry signals _buf[f][0] = d * _buf[f][0] + w * s[0]; _buf[f][1] = d * _buf[f][1] + w * s[1]; - - out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1]; } checkGate( out_sum / _frames ); diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 176dd35f8..7564a46f0 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -373,6 +373,119 @@ float *AutomationPattern::valuesAfter( const MidiTime & _time ) const +void AutomationPattern::flipY( int min, int max ) +{ + timeMap tempMap = m_timeMap; + timeMap::ConstIterator iterate = m_timeMap.lowerBound(0); + float tempValue = 0; + + int numPoints = 0; + + for( int i = 0; ( iterate + i + 1 ) != m_timeMap.end() && ( iterate + i ) != m_timeMap.end() ; i++) + { + numPoints++; + } + + for( int i = 0; i <= numPoints; i++ ) + { + + if ( min < 0 ) + { + tempValue = valueAt( ( iterate + i ).key() ) * -1; + putValue( MidiTime( (iterate + i).key() ) , tempValue, false); + } + else + { + tempValue = max - valueAt( ( iterate + i ).key() ); + putValue( MidiTime( (iterate + i).key() ) , tempValue, false); + } + } + + generateTangents(); + emit dataChanged(); +} + + + + +void AutomationPattern::flipX( int length ) +{ + timeMap tempMap; + + timeMap::ConstIterator iterate = m_timeMap.lowerBound(0); + float tempValue = 0; + int numPoints = 0; + + for( int i = 0; ( iterate + i + 1 ) != m_timeMap.end() && ( iterate + i ) != m_timeMap.end() ; i++) + { + numPoints++; + } + + float realLength = ( iterate + numPoints ).key(); + + if ( length != -1 && length != realLength) + { + if ( realLength < length ) + { + tempValue = valueAt( ( iterate + numPoints ).key() ); + putValue( MidiTime( length ) , tempValue, false); + numPoints++; + for( int i = 0; i <= numPoints; i++ ) + { + tempValue = valueAt( ( iterate + i ).key() ); + cleanObjects(); + MidiTime newTime = MidiTime( length - ( iterate + i ).key() ); + tempMap[newTime] = tempValue; + } + } + else + { + //for ( int i = 0; ( iterate + i ).key() < length ; i++ ) + //{ + // tempValue = valueAt( ( iterate + i ).key() ); + //} + //putValue( MidiTime( length ) , tempValue, false); + //numPoints++; + for( int i = 0; i <= numPoints; i++ ) + { + tempValue = valueAt( ( iterate + i ).key() ); + cleanObjects(); + MidiTime newTime; + + if ( ( iterate + i ).key() <= length ) + { + newTime = MidiTime( length - ( iterate + i ).key() ); + } + else + { + newTime = MidiTime( ( iterate + i ).key() ); + } + tempMap[newTime] = tempValue; + } + } + } + else + { + for( int i = 0; i <= numPoints; i++ ) + { + tempValue = valueAt( ( iterate + i ).key() ); + cleanObjects(); + MidiTime newTime = MidiTime( realLength - ( iterate + i ).key() ); + tempMap[newTime] = tempValue; + } + } + + m_timeMap.clear(); + + m_timeMap = tempMap; + + generateTangents(); + emit dataChanged(); +} + + + + void AutomationPattern::saveSettings( QDomDocument & _doc, QDomElement & _this ) { _this.setAttribute( "pos", startPosition() ); @@ -724,7 +837,7 @@ void AutomationPattern::generateTangents() void AutomationPattern::generateTangents( timeMap::const_iterator it, int numToGenerate ) { - if( m_timeMap.size() < 2 ) + if( m_timeMap.size() < 2 && numToGenerate > 0 ) { m_tangents[it.key()] = 0; return; diff --git a/src/core/EnvelopeAndLfoParameters.cpp b/src/core/EnvelopeAndLfoParameters.cpp index 5685b46f6..90f6747f0 100644 --- a/src/core/EnvelopeAndLfoParameters.cpp +++ b/src/core/EnvelopeAndLfoParameters.cpp @@ -446,14 +446,14 @@ void EnvelopeAndLfoParameters::updateSampleVars() { sample_t * tmp = m_pahdEnv; m_pahdEnv = new sample_t[m_pahdFrames]; - delete tmp; + delete[] tmp; m_pahdBufSize = m_pahdFrames; } if( m_rBufSize < m_rFrames ) { sample_t * tmp = m_rEnv; m_rEnv = new sample_t[m_rFrames]; - delete tmp; + delete[] tmp; m_rBufSize = m_rFrames; } diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index c30f0c9e3..84555fb3c 100644 --- a/src/core/RemotePlugin.cpp +++ b/src/core/RemotePlugin.cpp @@ -131,7 +131,7 @@ bool RemotePlugin::init( const QString &pluginExecutable, m_failed = false; } QString exec = ConfigManager::inst()->pluginDir() + - QDir::separator() + pluginExecutable; + pluginExecutable; QStringList args; // swap in and out for bidirectional communication diff --git a/src/core/Song.cpp b/src/core/Song.cpp index bd6438de6..1f60072b5 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -84,6 +84,7 @@ Song::Song() : m_recording( false ), m_exporting( false ), m_exportLoop( false ), + m_renderBetweenMarkers( false ), m_playing( false ), m_paused( false ), m_loadingProject( false ), @@ -386,6 +387,25 @@ void Song::processNextBuffer() } } +bool Song::isExportDone() const +{ + if ( m_renderBetweenMarkers ) + { + return m_exporting == true && + m_playPos[Mode_PlaySong].getTicks() >= m_playPos[Mode_PlaySong].m_timeLine->loopEnd().getTicks(); + } + if( m_exportLoop) + { + return m_exporting == true && + m_playPos[Mode_PlaySong].getTicks() >= length() * ticksPerTact(); + } + else + { + return m_exporting == true && + m_playPos[Mode_PlaySong].getTicks() >= ( length() + 1 ) * ticksPerTact(); + } +} + @@ -619,6 +639,14 @@ void Song::stop() void Song::startExport() { stop(); + if(m_renderBetweenMarkers) + { + m_playPos[Mode_PlaySong].setTicks( m_playPos[Mode_PlaySong].m_timeLine->loopBegin().getTicks() ); + } + else + { + m_playPos[Mode_PlaySong].setTicks( 0 ); + } playSong(); @@ -883,7 +911,10 @@ void Song::loadProject( const QString & _file_name ) m_loadingProject = true; Engine::projectJournal()->setJournalling( false ); - Engine::mainWindow()->clearErrors(); + if( Engine::mainWindow() ) + { + Engine::mainWindow()->clearErrors(); + } m_fileName = _file_name; m_oldFileName = _file_name; @@ -995,7 +1026,10 @@ void Song::loadProject( const QString & _file_name ) emit projectLoaded(); - Engine::mainWindow()->showErrors( tr( "The following errors occured while loading: " ) ); + if( Engine::mainWindow() ) + { + Engine::mainWindow()->showErrors( tr( "The following errors occured while loading: " ) ); + } m_loadingProject = false; m_modified = false; diff --git a/src/core/Track.cpp b/src/core/Track.cpp index a594ffdba..42ad88dc6 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -998,7 +998,9 @@ bool TrackContentObjectView::mouseMovedDistance( QMouseEvent * _me, int distance */ TrackContentWidget::TrackContentWidget( TrackView * _parent ) : QWidget( _parent ), - m_trackView( _parent ) + m_trackView( _parent ), + m_darkerColor( Qt::SolidPattern ), + m_lighterColor( Qt::SolidPattern ) { setAcceptDrops( true ); diff --git a/src/core/audio/AudioAlsa.cpp b/src/core/audio/AudioAlsa.cpp index 04594f855..93f2d08b2 100644 --- a/src/core/audio/AudioAlsa.cpp +++ b/src/core/audio/AudioAlsa.cpp @@ -523,7 +523,7 @@ AudioAlsa::setupWidget::setupWidget( QWidget * _parent ) : AudioAlsa::setupWidget::~setupWidget() { - + delete m_channels->model(); } diff --git a/src/core/audio/AudioJack.cpp b/src/core/audio/AudioJack.cpp index 004f1e504..482cc4e2f 100644 --- a/src/core/audio/AudioJack.cpp +++ b/src/core/audio/AudioJack.cpp @@ -464,6 +464,7 @@ AudioJack::setupWidget::setupWidget( QWidget * _parent ) : AudioJack::setupWidget::~setupWidget() { + delete m_channels->model(); } diff --git a/src/core/audio/AudioOss.cpp b/src/core/audio/AudioOss.cpp index 0e43793fd..f280da0f9 100644 --- a/src/core/audio/AudioOss.cpp +++ b/src/core/audio/AudioOss.cpp @@ -356,7 +356,7 @@ AudioOss::setupWidget::setupWidget( QWidget * _parent ) : AudioOss::setupWidget::~setupWidget() { - + delete m_channels->model(); } diff --git a/src/core/audio/AudioPort.cpp b/src/core/audio/AudioPort.cpp index b913babd1..e20146097 100644 --- a/src/core/audio/AudioPort.cpp +++ b/src/core/audio/AudioPort.cpp @@ -33,8 +33,9 @@ #include "panning.h" -AudioPort::AudioPort( const QString & _name, bool _has_effect_chain, - FloatModel * volumeModel, FloatModel * panningModel ) : +AudioPort::AudioPort( const QString & _name, bool _has_effect_chain, + FloatModel * volumeModel, FloatModel * panningModel, + BoolModel * mutedModel ) : m_bufferUsage( false ), m_portBuffer( NULL ), m_extOutputEnabled( false ), @@ -42,7 +43,8 @@ AudioPort::AudioPort( const QString & _name, bool _has_effect_chain, m_name( "unnamed port" ), m_effects( _has_effect_chain ? new EffectChain( NULL ) : NULL ), m_volumeModel( volumeModel ), - m_panningModel( panningModel ) + m_panningModel( panningModel ), + m_mutedModel( mutedModel ) { Engine::mixer()->addAudioPort( this ); setExtOutputEnabled( true ); @@ -102,6 +104,11 @@ bool AudioPort::processEffects() void AudioPort::doProcessing() { + if( m_mutedModel && m_mutedModel->value() ) + { + return; + } + const fpp_t fpp = Engine::mixer()->framesPerPeriod(); m_portBuffer = BufferManager::acquire(); // get buffer for processing @@ -118,11 +125,11 @@ void AudioPort::doProcessing() m_bufferUsage = true; MixHelpers::add( m_portBuffer, ph->buffer(), fpp ); } - ph->releaseBuffer(); // gets rid of playhandle's buffer and sets + ph->releaseBuffer(); // gets rid of playhandle's buffer and sets // pointer to null, so if it doesn't get re-acquired we know to skip it next time } } - + if( m_bufferUsage ) { // handle volume and panning @@ -131,7 +138,7 @@ void AudioPort::doProcessing() { ValueBuffer * volBuf = m_volumeModel->valueBuffer(); ValueBuffer * panBuf = m_panningModel->valueBuffer(); - + // both vol and pan have s.ex.data: if( volBuf && panBuf ) { @@ -143,7 +150,7 @@ void AudioPort::doProcessing() m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v; } } - + // only vol has s.ex.data: else if( volBuf ) { @@ -157,7 +164,7 @@ void AudioPort::doProcessing() m_portBuffer[f][1] *= v * r; } } - + // only pan has s.ex.data: else if( panBuf ) { @@ -169,7 +176,7 @@ void AudioPort::doProcessing() m_portBuffer[f][1] *= ( p >= 0 ? 1.0f : 1.0f + p ) * v; } } - + // neither has s.ex.data: else { @@ -182,12 +189,12 @@ void AudioPort::doProcessing() } } } - + // has vol model only else if( m_volumeModel ) { ValueBuffer * volBuf = m_volumeModel->valueBuffer(); - + if( volBuf ) { for( f_cnt_t f = 0; f < fpp; ++f ) @@ -210,7 +217,7 @@ void AudioPort::doProcessing() } // as of now there's no situation where we only have panning model but no volume model // if we have neither, we don't have to do anything here - just pass the audio as is - + // handle effects const bool me = processEffects(); if( me || m_bufferUsage ) @@ -219,7 +226,7 @@ void AudioPort::doProcessing() // TODO: improve the flow here - convert to pull model m_bufferUsage = false; } - + BufferManager::release( m_portBuffer ); // release buffer, we don't need it anymore } diff --git a/src/core/audio/AudioPulseAudio.cpp b/src/core/audio/AudioPulseAudio.cpp index ca80d6eec..a1d1e740f 100644 --- a/src/core/audio/AudioPulseAudio.cpp +++ b/src/core/audio/AudioPulseAudio.cpp @@ -307,7 +307,7 @@ AudioPulseAudio::setupWidget::setupWidget( QWidget * _parent ) : AudioPulseAudio::setupWidget::~setupWidget() { - + delete m_channels->model(); } diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index cbb3067da..fbf1c88af 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -145,6 +145,27 @@ void AutomationPatternView::toggleRecording() } + + +void AutomationPatternView::flipY() +{ + m_pat->flipY( m_pat->getMin(), m_pat->getMax() ); + update(); +} + + + + +void AutomationPatternView::flipX() +{ + //m_pat->flipX( m_pat->length() ); + m_pat->flipX( m_pat->TrackContentObject::length() ); + update(); +} + + + + void AutomationPatternView::constructContextMenu( QMenu * _cm ) { QAction * a = new QAction( embed::getIconPixmap( "automation" ), @@ -168,6 +189,12 @@ void AutomationPatternView::constructContextMenu( QMenu * _cm ) _cm->addAction( embed::getIconPixmap( "record" ), tr( "Set/clear record" ), this, SLOT( toggleRecording() ) ); + _cm->addAction( embed::getIconPixmap( "flip_y" ), + tr( "Flip Vertically (Visible)" ), + this, SLOT( flipY() ) ); + _cm->addAction( embed::getIconPixmap( "flip_x" ), + tr( "Flip Horizontally (Visible)" ), + this, SLOT( flipX() ) ); if( !m_pat->m_objects.isEmpty() ) { _cm->addSeparator(); diff --git a/src/gui/ExportProjectDialog.cpp b/src/gui/ExportProjectDialog.cpp index 7241216b6..4d0158c32 100644 --- a/src/gui/ExportProjectDialog.cpp +++ b/src/gui/ExportProjectDialog.cpp @@ -253,13 +253,17 @@ ProjectRenderer* ExportProjectDialog::prepRender() static_cast(interpolationCB->currentIndex()), static_cast(oversamplingCB->currentIndex()) ); + const int samplerates[5] = { 44100, 48000, 88200, 96000, 192000 }; + const int bitrates[6] = { 64, 128, 160, 192, 256, 320 }; + ProjectRenderer::OutputSettings os = ProjectRenderer::OutputSettings( - samplerateCB->currentText().section(" ", 0, 0).toUInt(), + samplerates[ samplerateCB->currentIndex() ], false, - bitrateCB->currentText().section(" ", 0, 0).toUInt(), + bitrates[ bitrateCB->currentIndex() ], static_cast( depthCB->currentIndex() ) ); Engine::getSong()->setExportLoop( exportLoopCB->isChecked() ); + Engine::getSong()->setRenderBetweenMarkers( renderMarkersCB->isChecked() ); ProjectRenderer* renderer = new ProjectRenderer( qs, os, m_ft, m_fileName ); diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index aefdcf274..b32183e9e 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -395,6 +395,40 @@ void FxMixerView::deleteChannel(int index) +void FxMixerView::deleteUnusedChannels() +{ + TrackContainer::TrackList tracks; + tracks += Engine::getSong()->tracks(); + tracks += Engine::getBBTrackContainer()->tracks(); + + // go through all FX Channels + for(int i = m_fxChannelViews.size()-1; i > 0; --i) + { + // check if an instrument references to the current channel + bool empty=true; + foreach( Track* t, tracks ) + { + if( t->type() == Track::InstrumentTrack ) + { + InstrumentTrack* inst = dynamic_cast( t ); + if( i == inst->effectChannelModel()->value(0) ) + { + empty=false; + break; + } + } + } + FxChannel * ch = Engine::fxMixer()->effectChannel( i ); + // delete channel if no references found + if( empty && ch->m_receives.isEmpty() ) + { + deleteChannel( i ); + } + } +} + + + void FxMixerView::moveChannelLeft(int index) { // can't move master or first channel left or last channel right @@ -480,6 +514,21 @@ void FxMixerView::keyPressEvent(QKeyEvent * e) +void FxMixerView::closeEvent( QCloseEvent * _ce ) + { + if( parentWidget() ) + { + parentWidget()->hide(); + } + else + { + hide(); + } + _ce->ignore(); + } + + + void FxMixerView::setCurrentFxLine( int _line ) { if( _line >= 0 && _line < m_fxChannelViews.size() ) diff --git a/src/gui/MainWindow.cpp b/src/gui/MainWindow.cpp index 633df21f1..f3c6fa8b2 100644 --- a/src/gui/MainWindow.cpp +++ b/src/gui/MainWindow.cpp @@ -664,14 +664,20 @@ void MainWindow::saveWidgetState( QWidget * _w, QDomElement & _de ) _w = _w->parentWidget(); } - _de.setAttribute( "x", _w->x() ); - _de.setAttribute( "y", _w->y() ); _de.setAttribute( "visible", _w->isVisible() ); _de.setAttribute( "minimized", _w->isMinimized() ); _de.setAttribute( "maximized", _w->isMaximized() ); + bool maxed = _w->isMaximized(); + bool mined = _w->isMinimized(); + if( mined || maxed ) { _w->showNormal(); } + + _de.setAttribute( "x", _w->x() ); + _de.setAttribute( "y", _w->y() ); _de.setAttribute( "width", _w->width() ); _de.setAttribute( "height", _w->height() ); + if( maxed ) { _w->showMaximized(); } + if( mined ) { _w->showMinimized(); } } @@ -679,8 +685,8 @@ void MainWindow::saveWidgetState( QWidget * _w, QDomElement & _de ) void MainWindow::restoreWidgetState( QWidget * _w, const QDomElement & _de ) { - QRect r( qMax( 0, _de.attribute( "x" ).toInt() ), - qMax( 0, _de.attribute( "y" ).toInt() ), + QRect r( qMax( 1, _de.attribute( "x" ).toInt() ), + qMax( 1, _de.attribute( "y" ).toInt() ), qMax( 100, _de.attribute( "width" ).toInt() ), qMax( 100, _de.attribute( "height" ).toInt() ) ); if( _de.hasAttribute( "visible" ) && !r.isNull() ) diff --git a/src/gui/dialogs/export_project.ui b/src/gui/dialogs/export_project.ui index e9fcaeb01..6443f8c1f 100644 --- a/src/gui/dialogs/export_project.ui +++ b/src/gui/dialogs/export_project.ui @@ -1,7 +1,8 @@ - + + ExportProjectDialog - - + + 0 0 @@ -9,118 +10,127 @@ 412 - + Export project - + - + - - + + Output - - - -1 + + + 6 - + 9 - - + + File format: - + - - + + Samplerate: - + - + 44100 Hz - + 48000 Hz - + 88200 Hz - + 96000 Hz - + 192000 Hz - - - - -1 + + + + 6 - + + 0 + + + 0 + + + 0 + + 0 - - + + Bitrate: - - + + 2 - + 64 KBit/s - + 128 KBit/s - + 160 KBit/s - + 192 KBit/s - + 256 KBit/s - + 320 KBit/s @@ -130,27 +140,36 @@ - - - + + + + 0 + + + 0 + + + 0 + + 0 - - + + Depth: - + - + 16 Bit Integer - + 32 Bit Float @@ -161,13 +180,13 @@ - + Qt::Vertical - + QSizePolicy::Fixed - + 1 10 @@ -176,21 +195,21 @@ - - + + Please note that not all of the parameters above apply for all file formats. - + true - + Qt::Vertical - + 163 20 @@ -202,71 +221,71 @@ - - + + Quality settings - + - - + + Interpolation: - - + + 1 - + Zero Order Hold - + Sinc Fastest - + Sinc Medium (recommended) - + Sinc Best (very slow!) - - + + Oversampling (use with care!): - + - + 1x (None) - + 2x - + 4x - + 8x @@ -279,12 +298,19 @@ + + + + Export between loop markers + + + - + Qt::Vertical - + 20 40 @@ -298,13 +324,13 @@ - + - + Qt::Horizontal - + 40 20 @@ -313,15 +339,15 @@ - - + + Start - - + + Cancel @@ -329,11 +355,11 @@ - - + + false - + 0 @@ -348,11 +374,11 @@ ExportProjectDialog reject() - + 357 293 - + 202 175 diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 0799bb445..897acfbff 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -69,6 +69,8 @@ QPixmap * AutomationEditor::s_toolDraw = NULL; QPixmap * AutomationEditor::s_toolErase = NULL; QPixmap * AutomationEditor::s_toolSelect = NULL; QPixmap * AutomationEditor::s_toolMove = NULL; +QPixmap * AutomationEditor::s_toolYFlip = NULL; +QPixmap * AutomationEditor::s_toolXFlip = NULL; @@ -98,9 +100,9 @@ AutomationEditor::AutomationEditor() : m_editMode( DRAW ), m_scrollBack( false ), m_gridColor( 0,0,0 ), - m_graphColor(), + m_graphColor( Qt::SolidPattern ), m_vertexColor( 0,0,0 ), - m_scaleColor() + m_scaleColor( Qt::SolidPattern ) { connect( this, SIGNAL( currentPatternChanged() ), this, SLOT( updateAfterPatternChange() ), @@ -118,6 +120,16 @@ AutomationEditor::AutomationEditor() : { m_quantizeModel.addItem( "1/" + QString::number( 1 << i ) ); } + if( s_toolYFlip == NULL ) + { + s_toolYFlip = new QPixmap( embed::getIconPixmap( + "flip_y" ) ); + } + if( s_toolXFlip == NULL ) + { + s_toolXFlip = new QPixmap( embed::getIconPixmap( + "flip_x" ) ); + } connect(&m_quantizeModel, SIGNAL(dataChanged()), this, SLOT(setQuantization())); m_quantizeModel.setValue( m_quantizeModel.findText( "1/16" ) ); @@ -191,10 +203,20 @@ AutomationEditor::~AutomationEditor() void AutomationEditor::setCurrentPattern(AutomationPattern * new_pattern ) { + if (m_pattern) + { + m_pattern->disconnect(this); + } + m_patternMutex.lock(); m_pattern = new_pattern; m_patternMutex.unlock(); + if (m_pattern != nullptr) + { + connect(m_pattern, SIGNAL(dataChanged()), this, SLOT(update())); + } + emit currentPatternChanged(); } @@ -1622,6 +1644,22 @@ void AutomationEditor::setEditMode(AutomationEditor::EditModes mode) + +void AutomationEditor::flipYButtonPressed() +{ + m_pattern->flipY( m_minLevel, m_maxLevel ); +} + + + + +void AutomationEditor::flipXButtonPressed() +{ + m_pattern->flipX(); +} + + + void AutomationEditor::setEditMode(int mode) { setEditMode((AutomationEditor::EditModes) mode); @@ -1998,6 +2036,23 @@ AutomationEditorWindow::AutomationEditorWindow() : QAction* eraseAction = editModeGroup->addAction(embed::getIconPixmap("edit_erase"), tr("Erase mode (Shift+E)")); eraseAction->setShortcut(Qt::SHIFT | Qt::Key_E); + m_flipYButton = new ToolButton( embed::getIconPixmap( "flip_y" ), + tr( "Flip Vertically" ), + m_editor, SLOT( flipYButtonPressed() ), + m_toolBar ); + + m_flipXButton = new ToolButton( embed::getIconPixmap( "flip_x" ), + tr( "Flip Horizontally" ), + m_editor, SLOT( flipXButtonPressed() ), + m_toolBar ); + + m_flipYButton->setWhatsThis( + tr( "Click here and the pattern will be inverted." + "The points are flipped in the y direction. " ) ); + m_flipXButton->setWhatsThis( + tr( "Click here and the pattern will be reversed. " + "The points are flipped in the x direction." ) ); + // TODO: m_selectButton and m_moveButton are broken. // m_selectButton = new QAction(embed::getIconPixmap("edit_select"), tr("Select mode (Shift+S)"), editModeGroup); // m_moveButton = new QAction(embed::getIconPixmap("edit_move"), tr("Move selection mode (Shift+M)"), editModeGroup); @@ -2153,6 +2208,8 @@ AutomationEditorWindow::AutomationEditorWindow() : m_toolBar->addAction(eraseAction); // m_toolBar->addAction(m_selectButton); // m_toolBar->addAction(m_moveButton); + m_toolBar->addWidget(m_flipXButton); + m_toolBar->addWidget(m_flipYButton); m_toolBar->addSeparator(); m_toolBar->addAction(m_discreteAction); m_toolBar->addAction(m_linearAction); diff --git a/src/gui/editors/BBEditor.cpp b/src/gui/editors/BBEditor.cpp index 64a6d1f7c..21949fe73 100644 --- a/src/gui/editors/BBEditor.cpp +++ b/src/gui/editors/BBEditor.cpp @@ -249,3 +249,17 @@ void BBTrackContainerView::updatePosition() //realignTracks(); emit positionChanged( m_currentPosition ); } + +void BBEditor::closeEvent( QCloseEvent * _ce ) + { + if( parentWidget() ) + { + parentWidget()->hide(); + } + else + { + hide(); + } + _ce->ignore(); + } + diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index d989cb85d..91ace4b6b 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -288,7 +288,7 @@ PianoRoll::PianoRoll() : s_toolOpen = new QPixmap( embed::getIconPixmap( "automation" ) ); } - + // init text-float if( s_textFloat == NULL ) { @@ -658,47 +658,29 @@ inline void PianoRoll::drawNoteRect(QPainter & p, int x, int y, { //step note col.setRgb( 0, 255, 0 ); - p.fillRect( x, y, width, KEY_LINE_HEIGHT - 2, col ); } else if( n->selected() ) { col.setRgb( 0x00, 0x40, 0xC0 ); - p.fillRect( x, y, width, KEY_LINE_HEIGHT - 2, col ); - } - else - { - // adjust note to make it a bit faded if it has a lower volume - // in stereo using gradients - QColor lcol = QColor::fromHsv( col.hue(), col.saturation(), - volVal * leftPercent ); - QColor rcol = QColor::fromHsv( col.hue(), col.saturation(), - volVal * rightPercent ); - col = QColor::fromHsv( col.hue(), col.saturation(), volVal ); - - QLinearGradient gradient( x, y, x+width, - y+KEY_LINE_HEIGHT ); - gradient.setColorAt( 0, lcol ); - gradient.setColorAt( 1, rcol ); - p.setBrush( gradient ); - p.setPen( Qt::NoPen ); - p.drawRect( x, y, width, KEY_LINE_HEIGHT-1 ); } - // hilighting lines around the note - p.setPen( Qt::SolidLine ); - p.setBrush( Qt::NoBrush ); + // adjust note to make it a bit faded if it has a lower volume + // in stereo using gradients + QColor lcol = QColor::fromHsv( col.hue(), col.saturation(), + volVal * leftPercent ); + QColor rcol = QColor::fromHsv( col.hue(), col.saturation(), + volVal * rightPercent ); + col = QColor::fromHsv( col.hue(), col.saturation(), volVal ); - col = QColor( noteCol ); + QLinearGradient gradient( x, y, x+width, + y+KEY_LINE_HEIGHT ); + gradient.setColorAt( 0, lcol ); + gradient.setColorAt( 1, rcol ); + p.setBrush( gradient ); p.setPen( QColor::fromHsv( col.hue(), col.saturation(), qMin( 255, volVal*1.7f ) ) ); - p.drawLine( x, y, x + width, y ); - p.drawLine( x, y, x, y + KEY_LINE_HEIGHT - 2 ); - - col = QColor( noteCol ); - p.setPen( QColor::fromHsv( col.hue(), col.saturation(), volVal/1.7 ) ); - p.drawLine( x + width, y, x + width, y + KEY_LINE_HEIGHT - 2 ); - p.drawLine( x, y + KEY_LINE_HEIGHT - 2, x + width, - y + KEY_LINE_HEIGHT - 2 ); + p.setRenderHint(QPainter::Antialiasing); + p.drawRoundedRect( x, y, width, KEY_LINE_HEIGHT-1, 5, 2 ); // that little tab thing on the end hinting at the user // to resize the note @@ -1090,10 +1072,13 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke ) } case Qt::Key_Control: - m_ctrlMode = m_editMode; - m_editMode = ModeSelect; - QApplication::changeOverrideCursor( Qt::ArrowCursor ); - ke->accept(); + if ( isActiveWindow() ) + { + m_ctrlMode = m_editMode; + m_editMode = ModeSelect; + QApplication::changeOverrideCursor( Qt::ArrowCursor ); + ke->accept(); + } break; default: break; @@ -1208,7 +1193,7 @@ inline int PianoRoll::keyAreaBottom() const void PianoRoll::mousePressEvent(QMouseEvent * me ) { m_startedWithShift = me->modifiers() & Qt::ShiftModifier; - + if( hasValidPattern() == false ) { return; @@ -1610,11 +1595,11 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) const int ticks_end = ( x+pixel_range/2 ) * MidiTime::ticksPerTact() / m_ppt + m_currentPosition; const int ticks_middle = x * MidiTime::ticksPerTact() / m_ppt + m_currentPosition; - + // get note-vector of current pattern - NoteVector notes; + NoteVector notes; notes += m_pattern->notes(); - + // go through notes to figure out which one we want to change NoteVector nv; foreach( Note * i, notes ) @@ -1646,7 +1631,7 @@ void PianoRoll::mouseDoubleClickEvent(QMouseEvent * me ) { if( ( *it )->pos().getTicks() != closest->pos().getTicks() ) { - it = nv.erase( it ); + it = nv.erase( it ); } else { @@ -1996,7 +1981,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) // if middle-click, set to defaults volume_t vol; panning_t pan; - + if( me->buttons() & Qt::LeftButton ) { vol = tLimit( MinVolume + @@ -2015,7 +2000,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) vol = DefaultVolume; pan = DefaultPanning; } - + if( m_noteEditMode == NoteEditVolume ) { m_lastNoteVolume = vol; @@ -2103,7 +2088,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * me ) } } - + else if( me->buttons() == Qt::NoButton && m_editMode == ModeDraw ) { // set move- or resize-cursor @@ -2568,14 +2553,14 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) { QColor horizCol = QColor( gridColor() ); QColor vertCol = QColor( gridColor() ); - + QStyleOption opt; opt.initFrom( this ); QPainter p( this ); style()->drawPrimitive( QStyle::PE_Widget, &opt, &p, this ); QBrush bgColor = p.background(); - + // fill with bg color p.fillRect( 0,0, width(), height(), bgColor ); @@ -3009,6 +2994,7 @@ void PianoRoll::paintEvent(QPaintEvent * pe ) int y = (int) y_base - sel_key_start * KEY_LINE_HEIGHT; int h = (int) y_base - sel_key_end * KEY_LINE_HEIGHT - y; p.setPen( QColor( 0, 64, 192 ) ); + p.setBrush( Qt::NoBrush ); p.drawRect( x + WHITE_KEY_WIDTH, y, w, h ); // TODO: Get this out of paint event @@ -3122,11 +3108,11 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) MidiTime::ticksPerTact() / m_ppt + m_currentPosition; int ticks_end = ( x+pixel_range/2 ) * MidiTime::ticksPerTact() / m_ppt + m_currentPosition; - + // get note-vector of current pattern - NoteVector notes; + NoteVector notes; notes += m_pattern->notes(); - + // go through notes to figure out which one we want to change NoteVector nv; foreach( Note * i, notes ) @@ -3139,7 +3125,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) nv += i; } } - if( nv.size() > 0 ) + if( nv.size() > 0 ) { const int step = we->delta() > 0 ? 1.0 : -1.0; if( m_noteEditMode == NoteEditVolume ) @@ -3180,7 +3166,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we ) update(); } } - + // not in note edit area, so handle scrolling/zooming and quantization change else if( we->modifiers() & Qt::ControlModifier && we->modifiers() & Qt::AltModifier ) @@ -3557,7 +3543,7 @@ void PianoRoll::enterValue( NoteVector* nv ) } m_lastNotePanning = new_val; } - + } } @@ -3692,7 +3678,7 @@ void PianoRoll::deleteSelectedNotes() bool update_after_delete = false; m_pattern->addJournalCheckPoint(); - + // get note-vector of current pattern const NoteVector & notes = m_pattern->notes(); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index afbbb2698..d228ef1ed 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -386,6 +386,21 @@ void SongEditor::wheelEvent( QWheelEvent * _we ) +void SongEditor::closeEvent( QCloseEvent * _ce ) + { + if( parentWidget() ) + { + parentWidget()->hide(); + } + else + { + hide(); + } + _ce->ignore(); + } + + + void SongEditor::setMasterVolume( int _new_val ) { @@ -531,7 +546,7 @@ void SongEditor::updatePosition( const MidiTime & _t ) { const int w = width() - widgetWidth - trackOpWidth - - 32; // rough estimation for width of right scrollbar + - contentWidget()->verticalScrollBar()->width(); // width of right scrollbar if( _t > m_currentPosition + w * MidiTime::ticksPerTact() / pixelsPerTact() ) { diff --git a/src/gui/widgets/ControllerRackView.cpp b/src/gui/widgets/ControllerRackView.cpp index 7ec3ab711..327ab5ab1 100644 --- a/src/gui/widgets/ControllerRackView.cpp +++ b/src/gui/widgets/ControllerRackView.cpp @@ -195,4 +195,16 @@ void ControllerRackView::addController() +void ControllerRackView::closeEvent( QCloseEvent * _ce ) + { + if( parentWidget() ) + { + parentWidget()->hide(); + } + else + { + hide(); + } + _ce->ignore(); + } diff --git a/src/gui/widgets/FxLine.cpp b/src/gui/widgets/FxLine.cpp index 36baa782f..adb47d4f8 100644 --- a/src/gui/widgets/FxLine.cpp +++ b/src/gui/widgets/FxLine.cpp @@ -46,7 +46,8 @@ QPixmap * FxLine::s_receiveBgArrow = NULL; FxLine::FxLine( QWidget * _parent, FxMixerView * _mv, int _channelIndex) : QWidget( _parent ), m_mv( _mv ), - m_channelIndex( _channelIndex ) + m_channelIndex( _channelIndex ), + m_backgroundActive( Qt::SolidPattern ) { if( ! s_sendBgArrow ) { @@ -191,14 +192,18 @@ void FxLine::contextMenuEvent( QContextMenuEvent * ) } contextMenu->addAction( tr( "Rename &channel" ), this, SLOT( renameChannel() ) ); contextMenu->addSeparator(); - + if( m_channelIndex != 0 ) // no remove-option in master { contextMenu->addAction( embed::getIconPixmap( "cancel" ), tr( "R&emove channel" ), this, SLOT( removeChannel() ) ); contextMenu->addSeparator(); } - + + contextMenu->addAction( embed::getIconPixmap( "cancel" ), tr( "Remove &unused channels" ), + this, SLOT( removeUnusedChannels() ) ); + contextMenu->addSeparator(); + contextMenu->addHelpAction(); contextMenu->exec( QCursor::pos() ); delete contextMenu; @@ -229,6 +234,13 @@ void FxLine::removeChannel() } +void FxLine::removeUnusedChannels() +{ + FxMixerView * mix = Engine::fxMixerView(); + mix->deleteUnusedChannels(); +} + + void FxLine::moveChannelLeft() { FxMixerView * mix = Engine::fxMixerView(); diff --git a/src/gui/widgets/ProjectNotes.cpp b/src/gui/widgets/ProjectNotes.cpp index bc6908f50..5137e8311 100644 --- a/src/gui/widgets/ProjectNotes.cpp +++ b/src/gui/widgets/ProjectNotes.cpp @@ -396,4 +396,17 @@ void ProjectNotes::loadSettings( const QDomElement & _this ) +void ProjectNotes::closeEvent( QCloseEvent * _ce ) +{ + if( parentWidget() ) + { + parentWidget()->hide(); + } + else + { + hide(); + } + _ce->ignore(); + } + diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 06219b942..f2e14fee5 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -49,6 +49,7 @@ #include "EffectRackView.h" #include "embed.h" #include "Engine.h" +#include "FadeButton.h" #include "FileBrowser.h" #include "FxMixer.h" #include "FxMixerView.h" @@ -104,7 +105,7 @@ InstrumentTrack::InstrumentTrack( TrackContainer* tc ) : tr( "Base note" ) ), m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 0.1f, this, tr( "Volume" ) ), m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr( "Panning" ) ), - m_audioPort( tr( "unnamed_track" ), true, &m_volumeModel, &m_panningModel ), + m_audioPort( tr( "unnamed_track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ), m_pitchModel( 0, MinPitchDefault, MaxPitchDefault, 1, this, tr( "Pitch" ) ), m_pitchRangeModel( 1, 1, 24, this, tr( "Pitch range" ) ), m_effectChannelModel( 0, 0, 0, this, tr( "FX channel" ) ), @@ -338,7 +339,7 @@ void InstrumentTrack::processInEvent( const MidiEvent& event, const MidiTime& ti break; } break; - + default: break; } @@ -378,9 +379,10 @@ void InstrumentTrack::processOutEvent( const MidiEvent& event, const MidiTime& t } ++m_runningMidiNotes[key]; m_instrument->handleMidiEvent( MidiEvent( MidiNoteOn, midiPort()->realOutputChannel(), key, event.velocity() ), time, offset ); - emit newNote(); + } m_midiNotesMutex.unlock(); + if( m_fb ) { m_fb->activate(); } break; case MidiNoteOff: @@ -561,6 +563,12 @@ void InstrumentTrack::removeMidiPortNode( DataFile & _dataFile ) n.item( 0 ).parentNode().removeChild( n.item( 0 ) ); } +void InstrumentTrack::setIndicator(FadeButton *fb) +{ + m_fb = fb; +} + + @@ -840,7 +848,7 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV { widgetWidth = DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT; } - else + else { widgetWidth = DEFAULT_SETTINGS_WIDGET_WIDTH; } @@ -910,9 +918,7 @@ InstrumentTrackView::InstrumentTrackView( InstrumentTrack * _it, TrackContainerV this, SLOT( activityIndicatorPressed() ) ); connect( m_activityIndicator, SIGNAL( released() ), this, SLOT( activityIndicatorReleased() ) ); - connect( _it, SIGNAL( newNote() ), - m_activityIndicator, SLOT( activate() ) ); - + _it->setIndicator( m_activityIndicator ); setModel( _it ); } @@ -949,8 +955,8 @@ InstrumentTrackWindow * InstrumentTrackView::topLevelInstrumentTrackWindow() -// TODO: Add windows to free list on freeInstrumentTrackWindow. -// But, don't NULL m_window or disconnect signals. This will allow windows +// TODO: Add windows to free list on freeInstrumentTrackWindow. +// But, don't NULL m_window or disconnect signals. This will allow windows // that are being show/hidden frequently to stay connected. void InstrumentTrackView::freeInstrumentTrackWindow() { @@ -975,7 +981,7 @@ void InstrumentTrackView::freeInstrumentTrackWindow() { delete m_window; } - + m_window = NULL; } } @@ -1002,7 +1008,7 @@ InstrumentTrackWindow * InstrumentTrackView::getInstrumentTrackWindow() else if( !s_windowCache.isEmpty() ) { m_window = s_windowCache.dequeue(); - + m_window->setInstrumentTrackView( this ); m_window->setModel( model() ); m_window->updateInstrumentView(); @@ -1028,7 +1034,7 @@ InstrumentTrackWindow * InstrumentTrackView::getInstrumentTrackWindow() s_windowCache << m_window; } } - + return m_window; } @@ -1059,7 +1065,7 @@ void InstrumentTrackView::dropEvent( QDropEvent * _de ) void InstrumentTrackView::toggleInstrumentWindow( bool _on ) { getInstrumentTrackWindow()->toggleVisibility( _on ); - + if( !_on ) { freeInstrumentTrackWindow(); @@ -1119,10 +1125,10 @@ void InstrumentTrackView::midiConfigChanged() -class fxLineLcdSpinBox : public LcdSpinBox +class fxLineLcdSpinBox : public LcdSpinBox { public: - fxLineLcdSpinBox( int _num_digits, QWidget * _parent, + fxLineLcdSpinBox( int _num_digits, QWidget * _parent, const QString & _name ) : LcdSpinBox( _num_digits, _parent, _name ) {} @@ -1336,7 +1342,7 @@ void InstrumentTrackWindow::modelChanged() this, SLOT( updateName() ) ); connect( m_track, SIGNAL( instrumentChanged() ), this, SLOT( updateInstrumentView() ) ); - + m_volumeKnob->setModel( &m_track->m_volumeModel ); m_panningKnob->setModel( &m_track->m_panningModel ); m_effectChannelNumber->setModel( &m_track->m_effectChannelModel ); diff --git a/src/tracks/Pattern.cpp b/src/tracks/Pattern.cpp index f53c57713..06fce8643 100644 --- a/src/tracks/Pattern.cpp +++ b/src/tracks/Pattern.cpp @@ -944,7 +944,9 @@ void PatternView::paintEvent( QPaintEvent * ) const float ppt = fixedTCOs() ? ( parentWidget()->width() - 2 * TCO_BORDER_WIDTH ) / (float) m_pat->length().getTact() : - pixelsPerTact(); + ( width() - 2 * TCO_BORDER_WIDTH ) + / (float) m_pat->length().getTact(); + const int x_base = TCO_BORDER_WIDTH; p.setPen( c.darker( 300 ) ); @@ -997,7 +999,7 @@ void PatternView::paintEvent( QPaintEvent * ) // qDebug( "keyrange: %d", keyrange ); // determine height of the pattern view, sans borders - const int ht = height() - 1 - TCO_BORDER_WIDTH * 2; + const int ht = (height() - 1 - TCO_BORDER_WIDTH * 2) -1; // determine maximum height value for drawing bounds checking const int max_ht = height() - 1 - TCO_BORDER_WIDTH; @@ -1022,7 +1024,7 @@ void PatternView::paintEvent( QPaintEvent * ) const float y_key = ( float( central_key - ( *it )->key() ) / keyrange + 1.0f ) / 2; // multiply that by pattern height - const int y_pos = static_cast( TCO_BORDER_WIDTH + y_key * ht ); + const int y_pos = static_cast( TCO_BORDER_WIDTH + y_key * ht ) + 1; // debug code // if( ( *it )->length() > 0 ) qDebug( "key %d, central_key %d, y_key %f, y_pos %d", ( *it )->key(), central_key, y_key, y_pos ); diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 49d59f66c..891790af2 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -47,6 +47,7 @@ #include "EffectRackView.h" #include "TrackLabelButton.h" #include "ConfigManager.h" +#include "panning_constants.h" SampleTCO::SampleTCO( Track * _track ) : @@ -386,11 +387,11 @@ void SampleTCOView::paintEvent( QPaintEvent * _pe ) { p.setFont( pointSize<7>( p.font() ) ); - p.setPen( QColor( 0, 0, 0 ) ); + p.setPen( QColor( 0, 0, 0 ) ); p.drawText( 10, p.fontMetrics().height()+1, "Rec" ); - p.setPen( textColor() ); + p.setPen( textColor() ); p.drawText( 9, p.fontMetrics().height(), "Rec" ); - + p.setBrush( QBrush( textColor() ) ); p.drawEllipse( 4, 5, 4, 4 ); } @@ -405,9 +406,12 @@ SampleTrack::SampleTrack( TrackContainer* tc ) : Track( Track::SampleTrack, tc ), m_volumeModel( DefaultVolume, MinVolume, MaxVolume, 1.0, this, tr( "Volume" ) ), - m_audioPort( tr( "Sample track" ), true, &m_volumeModel, NULL ) + m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, + this, tr( "Panning" ) ), + m_audioPort( tr( "Sample track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ) { setName( tr( "Sample track" ) ); + m_panningModel.setCenterValue( DefaultPanning ); } @@ -492,6 +496,7 @@ void SampleTrack::saveTrackSpecificSettings( QDomDocument & _doc, _this.setAttribute( "icon", tlb->pixmapFile() ); #endif m_volumeModel.saveSettings( _doc, _this, "vol" ); + m_panningModel.saveSettings( _doc, _this, "pan" ); } @@ -513,6 +518,7 @@ void SampleTrack::loadTrackSpecificSettings( const QDomElement & _this ) node = node.nextSibling(); } m_volumeModel.loadSettings( _this, "vol" ); + m_panningModel.loadSettings( _this, "pan" ); } @@ -550,6 +556,14 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_volumeKnob->setLabel( tr( "VOL" ) ); m_volumeKnob->show(); + m_panningKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), + tr( "Panning" ) ); + m_panningKnob->setModel( &_t->m_panningModel ); + m_panningKnob->setHintText( tr( "Panning:" ), "%" ); + m_panningKnob->move( DEFAULT_SETTINGS_WIDGET_WIDTH-24, 2 ); + m_panningKnob->setLabel( tr( "PAN" ) ); + m_panningKnob->show(); + m_effectRack = new EffectRackView( _t->audioPort()->effects() ); m_effectRack->setFixedSize( 240, 242 ); @@ -596,8 +610,3 @@ void SampleTrackView::modelChanged() TrackView::modelChanged(); } - - - - -