diff --git a/CMakeLists.txt b/CMakeLists.txt index 696a1e341ef..4e2109d6815 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ INCLUDE(FindPkgConfig) SET(VERSION_MAJOR "1") SET(VERSION_MINOR "0") -SET(VERSION_PATCH "99") +SET(VERSION_PATCH "100") #SET(VERSION_SUFFIX "") SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") IF(VERSION_SUFFIX) diff --git a/data/locale/de.qm b/data/locale/de.qm index 5834e25392f..353543c22e1 100644 Binary files a/data/locale/de.qm and b/data/locale/de.qm differ diff --git a/data/locale/de.ts b/data/locale/de.ts index a7d84072f17..16768661416 100644 --- a/data/locale/de.ts +++ b/data/locale/de.ts @@ -654,6 +654,63 @@ Wenn Sie daran interessiert sind LMMS in eine andere Sprache zu übersetzen oder Plugin entfe&rnen + + DelayControls + + Delay Samples + Samples verzögern + + + Feedback + Rückkopplung + + + Lfo Frequency + LFO-Frequenz + + + Lfo Amount + LFO-Stärke + + + + DelayControlsDialog + + Delay + Verzögerung + + + Delay Time + Verzögerungszeit + + + Regen + + + + Feedback Amount + Rückkopplungsstärke + + + Rate + Rate + + + Lfo + LFO + + + Lfo Amt + LFO-Stärke + + + + DetuningHelper + + Note detuning + Noten-Verstimmung + + DualFilterControlDialog @@ -780,6 +837,13 @@ Wenn Sie daran interessiert sind LMMS in eine andere Sprache zu übersetzen oder Vokalformant-Filter + + DummyEffect + + NOT FOUND + NICHT GEFUNDEN + + Effect @@ -3294,7 +3358,7 @@ Bitte besuchen Sie http://lmms.sf.net/wiki für Dokumentationen über LMMS. Bandlimited Ramp wave - + Bandbegrenzte Sägezahnwelle Bandlimited Square wave @@ -3330,7 +3394,7 @@ Bitte besuchen Sie http://lmms.sf.net/wiki für Dokumentationen über LMMS. Digital Ramp wave - + Digitale Sägezahnwelle Digital Square wave @@ -3350,7 +3414,7 @@ Bitte besuchen Sie http://lmms.sf.net/wiki für Dokumentationen über LMMS. Ramp wave - + Sägezahnwelle Square wave @@ -4085,6 +4149,21 @@ Grund: »%2« LMMS Plugin %1 hat keinen Plugin-Deskriptor namens %2! + + PluginBrowser + + Instrument plugins + Instrument-Plugins + + + Instrument browser + Instrument-Browser + + + Drag an instrument into either the Song-Editor, the Beat+Bassline Editor or into an existing instrument track. + Ziehen Sie ein Instrument entweder in den Song-Editor, den Beat+Bassline-Editor oder in eine existierende Instrumentspur. + + ProjectRenderer @@ -6990,10 +7069,6 @@ Doppelklicken auf eines der Plugins zeigt Informaitonen über die Ports an. pluginBrowser - - Instrument plugins - Instrument-Plugins - VST-host for using VST(i)-plugins within LMMS VST-Host zum Benutzen von VST(i)-Plugins innerhalb von LMMS @@ -7056,14 +7131,6 @@ This chip was used in the Commodore 64 computer. Emulation des MOS6581 und MOS8580 SID Chips. Dieser Chip wurde in Commodore 64 Computern genutzt. - - Instrument browser - Instrument-Browser - - - Drag an instrument into either the Song-Editor, the Beat+Bassline Editor or into an existing instrument track. - Ziehen Sie ein Instrument entweder in den Song-Editor, den Beat+Bassline-Editor oder in eine existierende Instrumentspur. - Player for SoundFont files Wiedergabe von SoundFont-Dateien @@ -7148,6 +7215,10 @@ Dieser Chip wurde in Commodore 64 Computern genutzt. Carla Patchbay Instrument Carla Patchbay Instrument + + A native delay plugin + Ein natives Verzögerung-Plugin + projectNotes @@ -7476,6 +7547,57 @@ Latenz: %2 ms Wiedergabe-Courser im AudioFileProcessor anzeigen + + setupWidget + + OSS (Open Sound System) + OSS (Open Sound System) + + + SDL (Simple DirectMedia Layer) + SDL (Simple DirectMedia Layer) + + + ALSA-Sequencer (Advanced Linux Sound Architecture) + ALSA-Sequencer (Advanced Linux Sound Architecture) + + + JACK (JACK Audio Connection Kit) + JACK (JACK Audio Connection Kit) + + + ALSA Raw-MIDI (Advanced Linux Sound Architecture) + ALSA Raw-MIDI (Advanced Linux Sound Architecture) + + + PulseAudio (bad latency!) + PulseAudio (Schlechte Latenz!) + + + Dummy (no sound output) + Dummy (Keine Soundausgabe) + + + Dummy (no MIDI support) + Dummy (Keine MIDI-Unterstützung) + + + WinMM MIDI + WinMM MIDI + + + OSS Raw-MIDI (Open Sound System) + OSS Raw-MIDI (Open Sound System) + + + ALSA (Advanced Linux Sound Architecture) + ALSA (Advanced Linux Sound Architecture) + + + PortAudio + PortAudio + + sf2Instrument @@ -7902,6 +8024,13 @@ Latenz: %2 ms Rechts-nach-rechts + + tabWidget + + Settings for %1 + Einstellungen für %1 + + timeLine diff --git a/data/presets/Kicker/Kick power.xpf b/data/presets/Kicker/KickPower.xpf similarity index 54% rename from data/presets/Kicker/Kick power.xpf rename to data/presets/Kicker/KickPower.xpf index b4e1daba350..27259ce25cc 100644 --- a/data/presets/Kicker/Kick power.xpf +++ b/data/presets/Kicker/KickPower.xpf @@ -1,20 +1,20 @@ - + - + - + - - - - + + + + - - - + + + diff --git a/data/presets/Kicker/SnareMarch.xpf b/data/presets/Kicker/SnareMarch.xpf new file mode 100644 index 00000000000..d4d1ad0db3b --- /dev/null +++ b/data/presets/Kicker/SnareMarch.xpf @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/data/scripts/create_apple_dmg.sh.in b/data/scripts/create_apple_dmg.sh.in index eb77b75bb20..508b455da30 100644 --- a/data/scripts/create_apple_dmg.sh.in +++ b/data/scripts/create_apple_dmg.sh.in @@ -16,10 +16,11 @@ DMG_BACKGROUND_IMG="dmg_branding.png" cp "@CMAKE_SOURCE_DIR@/data/${DMG_BACKGROUND_IMG}" . # you should not need to change these +OS_VER=`sw_vers -productVersion|cut -d"." -f1-2` APP_LOWERCASE=$(echo $APP_NAME|tr '[:upper:]' '[:lower:]') APP_EXE="${APP_NAME}.app/Contents/MacOS/${APP_LOWERCASE}" -VOL_NAME="${APP_NAME} ${VERSION}" # volume name will be "SuperCoolApp 1.0.0" +VOL_NAME="${APP_LOWERCASE}-${VERSION}-mac${OS_VER}" # volume name will be "SuperCoolApp 1.0.0" DMG_TMP="${VOL_NAME}-temp.dmg" DMG_FINAL="${VOL_NAME}.dmg" # final DMG name will be "SuperCoolApp 1.0.0.dmg" STAGING_DIR="./Install" # we copy all our stuff into this dir diff --git a/include/BasicFilters.h b/include/BasicFilters.h index e09d40c425f..e6dfbcbe3c3 100644 --- a/include/BasicFilters.h +++ b/include/BasicFilters.h @@ -75,68 +75,69 @@ class LinkwitzRiley inline void setCoeffs( float freq ) { // wc - const float wc = F_2PI * freq / m_sampleRate; - const float wc2 = wc * wc; - const float wc3 = wc2 * wc; + const double wc = D_2PI * freq; + const double wc2 = wc * wc; + const double wc3 = wc2 * wc; m_wc4 = wc2 * wc2; // k - const float k = wc / tan( wc * 0.5 ); - const float k2 = k * k; - const float k3 = k2 * k; + const double k = wc / tan( D_PI * freq / m_sampleRate ); + const double k2 = k * k; + const double k3 = k2 * k; m_k4 = k2 * k2; // a static const double sqrt2 = sqrt( 2.0 ); - const float sq_tmp1 = sqrt2 * wc3 * k; - const float sq_tmp2 = sqrt2 * wc * k3; - m_a = 1.0f / ( 4.0f * wc2 * k2 + 2.0f * sq_tmp1 + m_k4 + 2.0f * sq_tmp2 + m_wc4 ); + const double sq_tmp1 = sqrt2 * wc3 * k; + const double sq_tmp2 = sqrt2 * wc * k3; + + m_a = 1.0 / ( 4.0 * wc2 * k2 + 2.0 * sq_tmp1 + m_k4 + 2.0 * sq_tmp2 + m_wc4 ); // b - m_b1 = ( 4.0f * ( m_wc4 + sq_tmp1 - m_k4 - sq_tmp2 ) ) * m_a; - m_b2 = ( 6.0f * m_wc4 - 8.0f * wc2 * k2 + 6.0f * m_k4 ) * m_a; - m_b3 = ( 4.0f * ( m_wc4 - sq_tmp1 + sq_tmp2 - m_k4 ) ) * m_a; - m_b4 = ( m_k4 - 2.0f * sq_tmp1 + m_wc4 - 2.0f * sq_tmp2 + 4.0f * wc2 * k2 ) * m_a; + m_b1 = ( 4.0 * ( m_wc4 + sq_tmp1 - m_k4 - sq_tmp2 ) ) * m_a; + m_b2 = ( 6.0 * m_wc4 - 8.0 * wc2 * k2 + 6.0 * m_k4 ) * m_a; + m_b3 = ( 4.0 * ( m_wc4 - sq_tmp1 + sq_tmp2 - m_k4 ) ) * m_a; + m_b4 = ( m_k4 - 2.0 * sq_tmp1 + m_wc4 - 2.0 * sq_tmp2 + 4.0 * wc2 * k2 ) * m_a; } inline void setLowpass( float freq ) { setCoeffs( freq ); m_a0 = m_wc4 * m_a; - m_a1 = 4.0f * m_a0; - m_a2 = 6.0f * m_a0; + m_a1 = 4.0 * m_a0; + m_a2 = 6.0 * m_a0; } inline void setHighpass( float freq ) { setCoeffs( freq ); m_a0 = m_k4 * m_a; - m_a1 = 4.0f * m_a0; - m_a2 = 6.0f * m_a0; + m_a1 = -4.0 * m_a0; + m_a2 = 6.0 * m_a0; } inline float update( float in, ch_cnt_t ch ) { - const float a0in = m_a0 * in; - const float a1in = m_a1 * in; - const float out = m_z1[ch] + a0in; - - m_z1[ch] = a1in + m_z2[ch] - ( m_b1 * out ); - m_z2[ch] = ( m_a2 * in ) + m_z3[ch] - ( m_b2 * out ); - m_z3[ch] = a1in + m_z4[ch] - ( m_b3 * out ); - m_z4[ch] = a0in - ( m_b4 * out ); + const double x = in - ( m_z1[ch] * m_b1 ) - ( m_z2[ch] * m_b2 ) - + ( m_z3[ch] * m_b3 ) - ( m_z4[ch] * m_b4 ); + const double y = ( m_a0 * x ) + ( m_z1[ch] * m_a1 ) + ( m_z2[ch] * m_a2 ) + + ( m_z3[ch] * m_a1 ) + ( m_z4[ch] * m_a0 ); + m_z4[ch] = m_z3[ch]; + m_z3[ch] = m_z2[ch]; + m_z2[ch] = m_z1[ch]; + m_z1[ch] = x; - return out; + return y; } private: float m_sampleRate; - float m_wc4; - float m_k4; - float m_a, m_a0, m_a1, m_a2; - float m_b1, m_b2, m_b3, m_b4; + double m_wc4; + double m_k4; + double m_a, m_a0, m_a1, m_a2; + double m_b1, m_b2, m_b3, m_b4; - typedef float frame[CHANNELS]; + typedef double frame[CHANNELS]; frame m_z1, m_z2, m_z3, m_z4; }; typedef LinkwitzRiley<2> StereoLinkwitzRiley; diff --git a/include/Delay.h b/include/Delay.h new file mode 100644 index 00000000000..9010232b949 --- /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/Fader.h b/include/Fader.h index 96c3ae732f6..3e808802a48 100644 --- a/include/Fader.h +++ b/include/Fader.h @@ -57,7 +57,7 @@ class TextFloat; -class Fader : public QWidget, public FloatModelView +class EXPORT Fader : public QWidget, public FloatModelView { Q_OBJECT public: @@ -103,7 +103,7 @@ class Fader : public QWidget, public FloatModelView float fRange = m_model->maxValue() - m_model->minValue(); float realVal = m_model->value() - m_model->minValue(); - return height() - ( ( height() - ( *s_knob ).height() ) * ( realVal / fRange ) ); + return height() - ( ( height() - m_knob->height() ) * ( realVal / fRange ) ); } FloatModel * m_model; diff --git a/include/SampleTrack.h b/include/SampleTrack.h index 440b10e7ecc..c8b17b1481e 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -148,9 +148,11 @@ class SampleTrack : public Track private: FloatModel m_volumeModel; + FloatModel m_panningModel; AudioPort m_audioPort; + friend class SampleTrackView; } ; @@ -181,6 +183,7 @@ public slots: EffectRackView * m_effectRack; QWidget * m_effWindow; Knob * m_volumeKnob; + Knob * m_panningKnob; } ; diff --git a/include/interpolation.h b/include/interpolation.h index cbe274d42b3..6938975873e 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/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index 4df760ecd50..d7b4cf11ee7 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -6,10 +6,12 @@ ADD_SUBDIRECTORY(Bitcrush) ADD_SUBDIRECTORY(carlabase) ADD_SUBDIRECTORY(carlapatchbay) ADD_SUBDIRECTORY(carlarack) +ADD_SUBDIRECTORY(CrossoverEQ) ADD_SUBDIRECTORY(delay) ADD_SUBDIRECTORY(DualFilter) ADD_SUBDIRECTORY(dynamics_processor) -ADD_SUBDIRECTORY(flanger) +ADD_SUBDIRECTORY(Eq) +ADD_SUBDIRECTORY(Flanger) ADD_SUBDIRECTORY(flp_import) ADD_SUBDIRECTORY(HydrogenImport) ADD_SUBDIRECTORY(kicker) diff --git a/plugins/CrossoverEQ/CMakeLists.txt b/plugins/CrossoverEQ/CMakeLists.txt new file mode 100644 index 00000000000..fbc8407d978 --- /dev/null +++ b/plugins/CrossoverEQ/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(crossovereq CrossoverEQ.cpp CrossoverEQControls.cpp CrossoverEQControlDialog.cpp MOCFILES CrossoverEQControls.h CrossoverEQControlDialog.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/CrossoverEQ/CrossoverEQ.cpp b/plugins/CrossoverEQ/CrossoverEQ.cpp new file mode 100644 index 00000000000..a50b6381f39 --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQ.cpp @@ -0,0 +1,219 @@ +/* + * CrossoverEQ.cpp - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "CrossoverEQ.h" +#include "lmms_math.h" +#include "embed.cpp" + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT crossovereq_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Crossover Equalizer", + QT_TRANSLATE_NOOP( "pluginBrowser", "A 4-band Crossover Equalizer" ), + "Vesa Kivimäki ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +}; + +} + + +CrossoverEQEffect::CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ) : + Effect( &crossovereq_plugin_descriptor, parent, key ), + m_controls( this ), + m_sampleRate( Engine::mixer()->processingSampleRate() ), + m_lp1( m_sampleRate ), + m_lp2( m_sampleRate ), + m_lp3( m_sampleRate ), + m_hp2( m_sampleRate ), + m_hp3( m_sampleRate ), + m_hp4( m_sampleRate ), + m_needsUpdate( true ) +{ + m_tmp1 = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() ); + m_tmp2 = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() ); + m_work = MM_ALLOC( sampleFrame, Engine::mixer()->framesPerPeriod() ); +} + +CrossoverEQEffect::~CrossoverEQEffect() +{ + MM_FREE( m_tmp1 ); + MM_FREE( m_tmp2 ); + MM_FREE( m_work ); +} + +void CrossoverEQEffect::sampleRateChanged() +{ + m_sampleRate = Engine::mixer()->processingSampleRate(); + m_lp1.setSampleRate( m_sampleRate ); + m_lp2.setSampleRate( m_sampleRate ); + m_lp3.setSampleRate( m_sampleRate ); + m_hp2.setSampleRate( m_sampleRate ); + m_hp3.setSampleRate( m_sampleRate ); + m_hp4.setSampleRate( m_sampleRate ); + m_needsUpdate = true; +} + + +bool CrossoverEQEffect::processAudioBuffer( sampleFrame* buf, const fpp_t frames ) +{ + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + + // filters update + if( m_needsUpdate || m_controls.m_xover12.isValueChanged() ) + { + m_lp1.setLowpass( m_controls.m_xover12.value() ); + m_lp1.clearHistory(); + m_hp2.setHighpass( m_controls.m_xover12.value() ); + m_hp2.clearHistory(); + } + if( m_needsUpdate || m_controls.m_xover23.isValueChanged() ) + { + m_lp2.setLowpass( m_controls.m_xover23.value() ); + m_lp2.clearHistory(); + m_hp3.setHighpass( m_controls.m_xover23.value() ); + m_hp3.clearHistory(); + } + if( m_needsUpdate || m_controls.m_xover34.isValueChanged() ) + { + m_lp3.setLowpass( m_controls.m_xover34.value() ); + m_lp3.clearHistory(); + m_hp4.setHighpass( m_controls.m_xover34.value() ); + m_hp4.clearHistory(); + } + + // gain values update + if( m_needsUpdate || m_controls.m_gain1.isValueChanged() ) + { + m_gain1 = dbvToAmp( m_controls.m_gain1.value() ); + } + if( m_needsUpdate || m_controls.m_gain2.isValueChanged() ) + { + m_gain2 = dbvToAmp( m_controls.m_gain2.value() ); + } + if( m_needsUpdate || m_controls.m_gain3.isValueChanged() ) + { + m_gain3 = dbvToAmp( m_controls.m_gain3.value() ); + } + if( m_needsUpdate || m_controls.m_gain4.isValueChanged() ) + { + m_gain4 = dbvToAmp( m_controls.m_gain4.value() ); + } + + // mute values update + const bool mute1 = m_controls.m_mute1.value(); + const bool mute2 = m_controls.m_mute2.value(); + const bool mute3 = m_controls.m_mute3.value(); + const bool mute4 = m_controls.m_mute4.value(); + + m_needsUpdate = false; + + memset( m_work, 0, sizeof( sampleFrame ) * frames ); + + // run temp bands + for( int f = 0; f < frames; ++f ) + { + m_tmp1[f][0] = m_lp2.update( buf[f][0], 0 ); + m_tmp1[f][1] = m_lp2.update( buf[f][1], 1 ); + m_tmp2[f][0] = m_hp3.update( buf[f][0], 0 ); + m_tmp2[f][1] = m_hp3.update( buf[f][1], 1 ); + } + + // run band 1 + if( ! mute1 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_lp1.update( m_tmp1[f][0], 0 ) * m_gain1; + m_work[f][1] += m_lp1.update( m_tmp1[f][1], 1 ) * m_gain1; + } + } + + // run band 2 + if( ! mute2 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_hp2.update( m_tmp1[f][0], 0 ) * m_gain2; + m_work[f][1] += m_hp2.update( m_tmp1[f][1], 1 ) * m_gain2; + } + } + + // run band 3 + if( ! mute3 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_lp3.update( m_tmp2[f][0], 0 ) * m_gain3; + m_work[f][1] += m_lp3.update( m_tmp2[f][1], 1 ) * m_gain3; + } + } + + // run band 4 + if( ! mute4 ) + { + for( int f = 0; f < frames; ++f ) + { + m_work[f][0] += m_hp4.update( m_tmp2[f][0], 0 ) * m_gain4; + m_work[f][1] += m_hp4.update( m_tmp2[f][1], 1 ) * m_gain4; + } + } + + const float d = dryLevel(); + const float w = wetLevel(); + double outSum = 0.0; + for( int f = 0; f < frames; ++f ) + { + buf[f][0] = d * buf[f][0] + w * m_work[f][0]; + buf[f][1] = d * buf[f][1] + w * m_work[f][1]; + outSum = buf[f][0] * buf[f][0] + buf[f][1] * buf[f][1]; + } + + checkGate( outSum ); + + return isRunning(); +} + + +extern "C" +{ + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model* parent, void* data ) +{ + return new CrossoverEQEffect( parent, static_cast( data ) ); +} + +} diff --git a/plugins/CrossoverEQ/CrossoverEQ.h b/plugins/CrossoverEQ/CrossoverEQ.h new file mode 100644 index 00000000000..36b3a6bc555 --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQ.h @@ -0,0 +1,77 @@ +/* + * CrossoverEQ.h - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef CROSSOVEREQ_H +#define CROSSOVEREQ_H + +#include "Effect.h" +#include "CrossoverEQControls.h" +#include "ValueBuffer.h" +#include "lmms_math.h" +#include "BasicFilters.h" + +class CrossoverEQEffect : public Effect +{ +public: + CrossoverEQEffect( Model* parent, const Descriptor::SubPluginFeatures::Key* key ); + virtual ~CrossoverEQEffect(); + virtual bool processAudioBuffer( sampleFrame* buf, const fpp_t frames ); + + virtual EffectControls* controls() + { + return &m_controls; + } + +private: + CrossoverEQControls m_controls; + + void sampleRateChanged(); + + float m_sampleRate; + + float m_gain1; + float m_gain2; + float m_gain3; + float m_gain4; + + StereoLinkwitzRiley m_lp1; + StereoLinkwitzRiley m_lp2; + StereoLinkwitzRiley m_lp3; + + StereoLinkwitzRiley m_hp2; + StereoLinkwitzRiley m_hp3; + StereoLinkwitzRiley m_hp4; + + sampleFrame * m_tmp1; + sampleFrame * m_tmp2; + sampleFrame * m_work; + + bool m_needsUpdate; + + friend class CrossoverEQControls; +}; + +#endif diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp new file mode 100644 index 00000000000..8a9eecf4df8 --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.cpp @@ -0,0 +1,115 @@ +/* + * CrossoverEQControlDialog.cpp - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include + +#include "CrossoverEQControlDialog.h" +#include "CrossoverEQControls.h" +#include "embed.h" +#include "ToolTip.h" +#include "LedCheckbox.h" +#include "Knob.h" +#include "Fader.h" + +CrossoverEQControlDialog::CrossoverEQControlDialog( CrossoverEQControls * controls ) : + EffectControlDialog( controls ) +{ + setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) ); + setPalette( pal ); + setFixedSize( 167, 188 ); + + // knobs + Knob * xover12 = new Knob( knobBright_26, this ); + xover12->move( 29, 15 ); + xover12->setModel( & controls->m_xover12 ); + xover12->setLabel( "1/2" ); + xover12->setHintText( tr( "Band 1/2 Crossover:" ), " Hz" ); + + Knob * xover23 = new Knob( knobBright_26, this ); + xover23->move( 69, 15 ); + xover23->setModel( & controls->m_xover23 ); + xover23->setLabel( "2/3" ); + xover23->setHintText( tr( "Band 2/3 Crossover:" ), " Hz" ); + + Knob * xover34 = new Knob( knobBright_26, this ); + xover34->move( 109, 15 ); + xover34->setModel( & controls->m_xover34 ); + xover34->setLabel( "3/4" ); + xover34->setHintText( tr( "Band 3/4 Crossover:" ), " Hz" ); + + m_fader_bg = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_bg" ) ); + m_fader_empty = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_empty" ) ); + m_fader_knob = QPixmap( PLUGIN_NAME::getIconPixmap( "fader_knob2" ) ); + + // faders + Fader * gain1 = new Fader( &controls->m_gain1, "Band 1 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain1->move( 7, 56 ); + gain1->setDisplayConversion( false ); + gain1->setHintText( tr( "Band 1 Gain:" ), " dBV" ); + + Fader * gain2 = new Fader( &controls->m_gain2, "Band 2 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain2->move( 47, 56 ); + gain2->setDisplayConversion( false ); + gain2->setHintText( tr( "Band 2 Gain:" ), " dBV" ); + + Fader * gain3 = new Fader( &controls->m_gain3, "Band 3 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain3->move( 87, 56 ); + gain3->setDisplayConversion( false ); + gain3->setHintText( tr( "Band 3 Gain:" ), " dBV" ); + + Fader * gain4 = new Fader( &controls->m_gain4, "Band 4 Gain", this, + &m_fader_bg, &m_fader_empty, &m_fader_knob ); + gain4->move( 127, 56 ); + gain4->setDisplayConversion( false ); + gain4->setHintText( tr( "Band 4 Gain:" ), " dBV" ); + + // leds + LedCheckBox * mute1 = new LedCheckBox( "M", this, tr( "Band 1 Mute" ), LedCheckBox::Red ); + mute1->move( 11, 158 ); + mute1->setModel( & controls->m_mute1 ); + ToolTip::add( mute1, tr( "Mute Band 1" ) ); + + LedCheckBox * mute2 = new LedCheckBox( "M", this, tr( "Band 2 Mute" ), LedCheckBox::Red ); + mute2->move( 51, 158 ); + mute2->setModel( & controls->m_mute2 ); + ToolTip::add( mute2, tr( "Mute Band 2" ) ); + + LedCheckBox * mute3 = new LedCheckBox( "M", this, tr( "Band 3 Mute" ), LedCheckBox::Red ); + mute3->move( 91, 158 ); + mute3->setModel( & controls->m_mute3 ); + ToolTip::add( mute3, tr( "Mute Band 3" ) ); + + LedCheckBox * mute4 = new LedCheckBox( "M", this, tr( "Band 4 Mute" ), LedCheckBox::Red ); + mute4->move( 131, 158 ); + mute4->setModel( & controls->m_mute4 ); + ToolTip::add( mute4, tr( "Mute Band 4" ) ); +} diff --git a/plugins/CrossoverEQ/CrossoverEQControlDialog.h b/plugins/CrossoverEQ/CrossoverEQControlDialog.h new file mode 100644 index 00000000000..08c67888646 --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControlDialog.h @@ -0,0 +1,50 @@ +/* + * CrossoverEQControlDialog.h - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef CROSSOVEREQ_CONTROL_DIALOG_H +#define CROSSOVEREQ_CONTROL_DIALOG_H + +#include +#include "EffectControlDialog.h" + +class CrossoverEQControls; + +class CrossoverEQControlDialog : public EffectControlDialog +{ + Q_OBJECT +public: + CrossoverEQControlDialog( CrossoverEQControls * controls ); + virtual ~CrossoverEQControlDialog() + { + } + +private: + QPixmap m_fader_bg; + QPixmap m_fader_empty; + QPixmap m_fader_knob; +}; + +#endif diff --git a/plugins/CrossoverEQ/CrossoverEQControls.cpp b/plugins/CrossoverEQ/CrossoverEQControls.cpp new file mode 100644 index 00000000000..9c58eabfffc --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControls.cpp @@ -0,0 +1,116 @@ +/* + * CrossoverEQControls.cpp - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "CrossoverEQControls.h" +#include "CrossoverEQ.h" + +CrossoverEQControls::CrossoverEQControls( CrossoverEQEffect * eff ) : + EffectControls( eff ), + m_effect( eff ), + m_xover12( 125.f, 50.f, 10000.f, 1.0f, this, "Band 1/2 Crossover" ), + m_xover23( 1250.f, 50.f, 20000.f, 1.0f, this, "Band 2/3 Crossover" ), + m_xover34( 5000.f, 50.f, 20000.f, 1.0f, this, "Band 3/4 Crossover" ), + m_gain1( 0.f, -60.f, 30.f, 0.1f, this, "Band 1 Gain" ), + m_gain2( 0.f, -60.f, 30.f, 0.1f, this, "Band 2 Gain" ), + m_gain3( 0.f, -60.f, 30.f, 0.1f, this, "Band 3 Gain" ), + m_gain4( 0.f, -60.f, 30.f, 0.1f, this, "Band 4 Gain" ), + m_mute1( false, this, "Mute Band 1" ), + m_mute2( false, this, "Mute Band 2" ), + m_mute3( false, this, "Mute Band 3" ), + m_mute4( false, this, "Mute Band 4" ) +{ + connect( Engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( sampleRateChanged() ) ); + connect( &m_xover12, SIGNAL( dataChanged() ), this, SLOT( xover12Changed() ) ); + connect( &m_xover23, SIGNAL( dataChanged() ), this, SLOT( xover23Changed() ) ); + connect( &m_xover34, SIGNAL( dataChanged() ), this, SLOT( xover34Changed() ) ); + + m_xover12.setScaleLogarithmic( true ); + m_xover23.setScaleLogarithmic( true ); + m_xover34.setScaleLogarithmic( true ); +} + +void CrossoverEQControls::saveSettings( QDomDocument & doc, QDomElement & elem ) +{ + m_xover12.saveSettings( doc, elem, "xover12" ); + m_xover23.saveSettings( doc, elem, "xover23" ); + m_xover34.saveSettings( doc, elem, "xover34" ); + + m_gain1.saveSettings( doc, elem, "gain1" ); + m_gain2.saveSettings( doc, elem, "gain2" ); + m_gain3.saveSettings( doc, elem, "gain3" ); + m_gain4.saveSettings( doc, elem, "gain4" ); + + m_mute1.saveSettings( doc, elem, "mute1" ); + m_mute2.saveSettings( doc, elem, "mute2" ); + m_mute3.saveSettings( doc, elem, "mute3" ); + m_mute4.saveSettings( doc, elem, "mute4" ); +} + +void CrossoverEQControls::loadSettings( const QDomElement & elem ) +{ + m_xover12.loadSettings( elem, "xover12" ); + m_xover23.loadSettings( elem, "xover23" ); + m_xover34.loadSettings( elem, "xover34" ); + + m_gain1.loadSettings( elem, "gain1" ); + m_gain2.loadSettings( elem, "gain2" ); + m_gain3.loadSettings( elem, "gain3" ); + m_gain4.loadSettings( elem, "gain4" ); + + m_mute1.loadSettings( elem, "mute1" ); + m_mute2.loadSettings( elem, "mute2" ); + m_mute3.loadSettings( elem, "mute3" ); + m_mute4.loadSettings( elem, "mute4" ); + + m_effect->m_needsUpdate = true; +} + +void CrossoverEQControls::xover12Changed() +{ + float v = m_xover12.value(); + if( m_xover23.value() < v ) { m_xover23.setValue( v ); } + if( m_xover34.value() < v ) { m_xover34.setValue( v ); } +} + +void CrossoverEQControls::xover23Changed() +{ + float v = m_xover23.value(); + if( m_xover12.value() > v ) { m_xover12.setValue( v ); } + if( m_xover34.value() < v ) { m_xover34.setValue( v ); } +} + +void CrossoverEQControls::xover34Changed() +{ + float v = m_xover34.value(); + if( m_xover12.value() > v ) { m_xover12.setValue( v ); } + if( m_xover23.value() > v ) { m_xover23.setValue( v ); } +} + + +void CrossoverEQControls::sampleRateChanged() +{ + m_effect->sampleRateChanged(); +} diff --git a/plugins/CrossoverEQ/CrossoverEQControls.h b/plugins/CrossoverEQ/CrossoverEQControls.h new file mode 100644 index 00000000000..18e87baeef5 --- /dev/null +++ b/plugins/CrossoverEQ/CrossoverEQControls.h @@ -0,0 +1,86 @@ +/* + * CrossoverEQControls.h - A native 4-band Crossover Equalizer + * good for simulating tonestacks or simple peakless (flat-band) equalization + * + * Copyright (c) 2014 Vesa Kivimäki + * Copyright (c) 2006-2014 Tobias Doerffel + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef CROSSOVEREQ_CONTROLS_H +#define CROSSOVEREQ_CONTROLS_H + +#include "EffectControls.h" +#include "CrossoverEQControlDialog.h" + +class CrossoverEQEffect; + +class CrossoverEQControls : public EffectControls +{ + Q_OBJECT +public: + CrossoverEQControls( CrossoverEQEffect * eff ); + virtual ~CrossoverEQControls() {} + + virtual void saveSettings( QDomDocument & doc, QDomElement & elem ); + virtual void loadSettings( const QDomElement & elem ); + inline virtual QString nodeName() const + { + return( "crossoevereqcontrols" ); + } + + virtual int controlCount() + { + return( 11 ); + } + + virtual EffectControlDialog * createView() + { + return( new CrossoverEQControlDialog( this ) ); + } + +private slots: + void xover12Changed(); + void xover23Changed(); + void xover34Changed(); + void sampleRateChanged(); + +private: + CrossoverEQEffect * m_effect; + + FloatModel m_xover12; + FloatModel m_xover23; + FloatModel m_xover34; + + FloatModel m_gain1; + FloatModel m_gain2; + FloatModel m_gain3; + FloatModel m_gain4; + + BoolModel m_mute1; + BoolModel m_mute2; + BoolModel m_mute3; + BoolModel m_mute4; + + friend class CrossoverEQControlDialog; + friend class CrossoverEQEffect; +}; + +#endif diff --git a/plugins/CrossoverEQ/artwork.png b/plugins/CrossoverEQ/artwork.png new file mode 100644 index 00000000000..5510d2b3c3e Binary files /dev/null and b/plugins/CrossoverEQ/artwork.png differ diff --git a/plugins/CrossoverEQ/fader_bg.png b/plugins/CrossoverEQ/fader_bg.png new file mode 100644 index 00000000000..abe28110594 Binary files /dev/null and b/plugins/CrossoverEQ/fader_bg.png differ diff --git a/plugins/CrossoverEQ/fader_empty.png b/plugins/CrossoverEQ/fader_empty.png new file mode 100644 index 00000000000..4a95f05aa4c Binary files /dev/null and b/plugins/CrossoverEQ/fader_empty.png differ diff --git a/plugins/CrossoverEQ/fader_knob2.png b/plugins/CrossoverEQ/fader_knob2.png new file mode 100644 index 00000000000..252b485ee87 Binary files /dev/null and b/plugins/CrossoverEQ/fader_knob2.png differ diff --git a/plugins/DualFilter/DualFilterControls.cpp b/plugins/DualFilter/DualFilterControls.cpp index c7f2ba48a0e..b25b8953cbf 100644 --- a/plugins/DualFilter/DualFilterControls.cpp +++ b/plugins/DualFilter/DualFilterControls.cpp @@ -39,7 +39,7 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_enabled1Model( true, this, tr( "Filter 1 enabled" ) ), m_filter1Model( this, tr( "Filter 1 type" ) ), - m_cut1Model( 7000.0f, 1.0f, 14000.0f, 1.0f, this, tr( "Cutoff 1 frequency" ) ), + m_cut1Model( 7000.0f, 1.0f, 20000.0f, 1.0f, this, tr( "Cutoff 1 frequency" ) ), m_res1Model( 0.5, BasicFilters<0>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 1" ) ), m_gain1Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 1" ) ), @@ -47,7 +47,7 @@ DualFilterControls::DualFilterControls( DualFilterEffect* effect ) : m_enabled2Model( true, this, tr( "Filter 2 enabled" ) ), m_filter2Model( this, tr( "Filter 2 type" ) ), - m_cut2Model( 7000.0f, 1.0f, 14000.0f, 1.0f, this, tr( "Cutoff 2 frequency" ) ), + m_cut2Model( 7000.0f, 1.0f, 20000.0f, 1.0f, this, tr( "Cutoff 2 frequency" ) ), m_res2Model( 0.5, BasicFilters<0>::minQ(), 10.0, 0.01, this, tr( "Q/Resonance 2" ) ), m_gain2Model( 100.0f, 0.0f, 200.0f, 0.1f, this, tr( "Gain 2" ) ) { diff --git a/plugins/Eq/CMakeLists.txt b/plugins/Eq/CMakeLists.txt new file mode 100644 index 00000000000..3cd4b888559 --- /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 +MOCFILES EqControls.h EqParameterWidget.h EqFader.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/Eq/EqControls.cpp b/plugins/Eq/EqControls.cpp new file mode 100644 index 00000000000..19b63fc4a7d --- /dev/null +++ b/plugins/Eq/EqControls.cpp @@ -0,0 +1,195 @@ +/* + * eqcontrols.cpp - defination of EqControls class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include +#include "EqControls.h" +#include "EqEffect.h" + + + + +EqControls::EqControls( EqEffect *effect ) : + EffectControls( effect ), + m_effect( effect ), + m_inGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Input gain") ), + m_outGainModel( 1.0, 0.0, 2.0, 0.001, this, tr( "Output gain" ) ), + m_lowShelfGainModel( 0.0 , -40, 40, 0.001, this, tr( "Low shelf gain" ) ), + m_para1GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 1 gain" ) ), + m_para2GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 2 gain" ) ), + m_para3GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 3 gain" ) ), + m_para4GainModel( 0.0 , -40, 40, 0.001, this, tr( "Peak 4 gain" ) ), + m_highShelfGainModel( 0.0 , -40, 40, 0.001, this, tr( "High Shelf gain" ) ), + m_hpResModel( 0.707,0.003, 10.0 , 0.001, this, tr( "HP res" ) ), + m_lowShelfResModel( 1.4,0.0, 10.0 , 0.001, this , tr( "Low Shelf res" ) ), + m_para1ResModel( 1.4 ,0.55, 10.0 , 0.001, this , tr( "Peak 1 res" ) ), + m_para2ResModel( 1.4, 0.55, 10.0 , 0.001, this , tr( "Peak 2 res" ) ), + m_para3ResModel( 1.4, 0.55, 10.0 , 0.001, this , tr( "Peak 3 res" ) ), + m_para4ResModel( 1.4, 0.55, 10.0 , 0.001, this , tr( "Peak 4 res" ) ), + m_highShelfResModel( 1.4, 0.001, 10.0 , 0.001, this , tr( "High Shelf res" ) ), + m_lpResModel( 0.707,0.003, 10.0 , 0.001, this , tr( "LP res" ) ), + m_hpFeqModel( 31.0, 30.0, 20000, 0.001, this , tr( "HP freq" ) ), + m_lowShelfFreqModel( 80.0, 25.0, 20000, 0.001, this , tr( "Low Shelf freq" ) ), + m_para1FreqModel( 120.0, 27.0, 20000, 0.001, this , tr( "Peak 1 freq" ) ), + m_para2FreqModel( 250.0, 27.0, 20000, 0.001, this, tr( "Peak 2 freq" ) ), + m_para3FreqModel( 2000.0, 27.0, 20000, 0.001, this , tr( "Peak 3 freq" ) ), + m_para4FreqModel( 4000.0, 27.0, 20000, 0.001, this , tr( "Peak 4 freq" ) ), + m_highShelfFreqModel( 12000.0, 27.0, 20000, 0.001, this , tr( "High shelf freq" ) ), + m_lpFreqModel( 18000.0, 27.0, 20000, 0.001, this , tr( "LP freq" ) ), + m_hpActiveModel( false, this , tr( "HP active" ) ), + m_lowShelfActiveModel( false, this , tr( "Low shelf active" ) ), + m_para1ActiveModel(false, this , tr( "Peak 1 active" ) ), + m_para2ActiveModel( false, this , tr( "Peak 2 active" ) ), + m_para3ActiveModel( false, this , tr( "Peak 3 active" ) ), + m_para4ActiveModel( false, this , tr( "Peak 4 active" ) ), + m_highShelfActiveModel( false, this , tr( "High shelf active" ) ), + m_lpActiveModel( false, this , tr( "LP active" ) ), + m_lp12Model( 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_analyzeModel( true, this , tr( "Analyze enable" ) ), + m_lpTypeModel( 0,0,2,this, tr( "low pass type") ) , + m_hpTypeModel( 0,0,2,this, tr( "high pass type") ) +{ + m_hpFeqModel.setScaleLogarithmic( true ); + m_lowShelfFreqModel.setScaleLogarithmic( true ); + 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; +} + + + + +void EqControls::loadSettings( const QDomElement &_this ) +{ + m_inGainModel.loadSettings( _this, "Inputgain" ); + m_outGainModel.loadSettings( _this, "Outputgain"); + m_lowShelfGainModel.loadSettings( _this , "Lowshelfgain" ); + m_para1GainModel.loadSettings( _this, "Peak1gain" ); + m_para2GainModel.loadSettings( _this, "Peak2gain" ); + m_para3GainModel.loadSettings( _this, "Peak3gain" ); + m_para4GainModel.loadSettings( _this, "Peak4gain" ); + m_highShelfGainModel.loadSettings( _this , "HighShelfgain"); + m_hpResModel.loadSettings( _this ,"HPres"); + m_lowShelfResModel.loadSettings( _this, "LowShelfres" ); + m_para1ResModel.loadSettings( _this ,"Peak1res" ); + m_para2ResModel.loadSettings( _this ,"Peak2res" ); + m_para3ResModel.loadSettings( _this ,"Peak3res" ); + m_para4ResModel.loadSettings( _this ,"Peak4res" ); + m_highShelfResModel.loadSettings( _this, "HighShelfres" ); + m_lpResModel.loadSettings( _this, "LPres"); + m_hpFeqModel.loadSettings( _this, "HPfreq" ); + m_lowShelfFreqModel.loadSettings( _this, "LowShelffreq" ); + m_para1FreqModel.loadSettings( _this, "Peak1freq" ); + m_para2FreqModel.loadSettings( _this, "Peak2freq" ); + m_para3FreqModel.loadSettings( _this, "Peak3freq" ); + m_para4FreqModel.loadSettings( _this, "Peak4freq" ); + m_highShelfFreqModel.loadSettings( _this, "Highshelffreq" ); + m_lpFreqModel.loadSettings( _this, "LPfreq" ); + m_hpActiveModel.loadSettings( _this, "HPactive" ); + m_lowShelfActiveModel.loadSettings( _this, "Lowshelfactive" ); + m_para1ActiveModel.loadSettings( _this, "Peak1active"); + m_para2ActiveModel.loadSettings( _this, "Peak2active"); + m_para3ActiveModel.loadSettings( _this, "Peak3active"); + m_para4ActiveModel.loadSettings( _this, "Peak4active"); + m_highShelfActiveModel.loadSettings( _this, "Highshelfactive" ); + m_lpActiveModel.loadSettings( _this, "LPactive" ); + m_lp12Model.loadSettings( _this , "LP12" ); + m_lp24Model.loadSettings( _this , "LP24" ); + m_lp48Model.loadSettings( _this , "LP48" ); + m_hp12Model.loadSettings( _this , "HP12" ); + m_hp24Model.loadSettings( _this , "HP24" ); + m_hp48Model.loadSettings( _this , "HP48" ); + m_analyzeModel.loadSettings( _this, "Analyzeenable"); + 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_para1ResModel.saveSettings( doc, parent,"Peak1res" ); + m_para2ResModel.saveSettings( doc, parent,"Peak2res" ); + m_para3ResModel.saveSettings( doc, parent,"Peak3res" ); + m_para4ResModel.saveSettings( doc, parent,"Peak4res" ); + m_highShelfResModel.saveSettings( doc, parent, "HighShelfres" ); + m_lpResModel.saveSettings( doc, parent, "LPres"); + m_hpFeqModel.saveSettings( doc, parent, "HPfreq" ); + m_lowShelfFreqModel.saveSettings( doc, parent, "LowShelffreq" ); + m_para1FreqModel.saveSettings( doc, parent, "Peak1freq" ); + m_para2FreqModel.saveSettings( doc, parent, "Peak2freq" ); + m_para3FreqModel.saveSettings( doc, parent, "Peak3freq" ); + m_para4FreqModel.saveSettings( doc, parent, "Peak4freq" ); + m_highShelfFreqModel.saveSettings( doc, parent, "Highshelffreq" ); + m_lpFreqModel.saveSettings( doc, parent, "LPfreq" ); + m_hpActiveModel.saveSettings( doc, parent, "HPactive" ); + m_lowShelfActiveModel.saveSettings( doc, parent, "Lowshelfactive" ); + m_para1ActiveModel.saveSettings( doc, parent, "Peak1active" ); + m_para2ActiveModel.saveSettings( doc, parent, "Peak2active" ); + m_para3ActiveModel.saveSettings( doc, parent, "Peak3active" ); + m_para4ActiveModel.saveSettings( doc, parent, "Peak4active" ); + m_highShelfActiveModel.saveSettings( doc, parent, "Highshelfactive" ); + m_lpActiveModel.saveSettings( doc, parent, "LPactive" ); + m_lp12Model.saveSettings( doc, parent, "LP12" ); + m_lp24Model.saveSettings( doc, parent, "LP24" ); + m_lp48Model.saveSettings( doc, parent, "LP48" ); + m_hp12Model.saveSettings( doc, parent, "HP12" ); + m_hp24Model.saveSettings( doc, parent, "HP24" ); + m_hp48Model.saveSettings( doc, parent, "HP48" ); + m_analyzeModel.saveSettings( doc, parent, "Analyzeenable"); + 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 00000000000..eec745b6ff0 --- /dev/null +++ b/plugins/Eq/EqControls.h @@ -0,0 +1,134 @@ +/* + * eqcontrols.h - defination of EqControls class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef EQCONTROLS_H +#define EQCONTROLS_H + +#include "EffectControls.h" +#include "EqControlsDialog.h" +#include "Knob.h" + +class EqEffect; + +class EqControls : public EffectControls +{ + Q_OBJECT +public: + 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; + + EqAnalyser m_inFftBands; + EqAnalyser m_outFftBands; + + bool m_inProgress; + + + + + +private: + EqEffect* m_effect; + + FloatModel m_inGainModel; + FloatModel m_outGainModel; + FloatModel m_lowShelfGainModel; + FloatModel m_para1GainModel; + FloatModel m_para2GainModel; + FloatModel m_para3GainModel; + FloatModel m_para4GainModel; + FloatModel m_highShelfGainModel; + + FloatModel m_hpResModel; + FloatModel m_lowShelfResModel; + FloatModel m_para1ResModel; + FloatModel m_para2ResModel; + FloatModel m_para3ResModel; + FloatModel m_para4ResModel; + FloatModel m_highShelfResModel; + FloatModel m_lpResModel; + + FloatModel m_hpFeqModel; + FloatModel m_lowShelfFreqModel; + FloatModel m_para1FreqModel; + FloatModel m_para2FreqModel; + FloatModel m_para3FreqModel; + FloatModel m_para4FreqModel; + FloatModel m_highShelfFreqModel; + FloatModel m_lpFreqModel; + + BoolModel m_hpActiveModel; + BoolModel m_lowShelfActiveModel; + BoolModel m_para1ActiveModel; + BoolModel m_para2ActiveModel; + BoolModel m_para3ActiveModel; + BoolModel m_para4ActiveModel; + BoolModel m_highShelfActiveModel; + BoolModel m_lpActiveModel; + + BoolModel m_lp12Model; + BoolModel m_lp24Model; + BoolModel m_lp48Model; + + BoolModel m_hp12Model; + BoolModel m_hp24Model; + BoolModel m_hp48Model; + + BoolModel m_analyzeModel; + + 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 00000000000..950ed127283 --- /dev/null +++ b/plugins/Eq/EqControlsDialog.cpp @@ -0,0 +1,171 @@ +/* + * 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 ); + 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_para1ResModel, &controls->m_para1GainModel, QColor(255 ,255, 255), tr( "Peak 1" ), &controls->m_para1PeakL, &controls->m_para1PeakR ); + setBand( 3, &controls->m_para2ActiveModel, &controls->m_para2FreqModel, &controls->m_para2ResModel, &controls->m_para2GainModel, QColor(255 ,255, 255), tr( "Peak 2" ), &controls->m_para2PeakL, &controls->m_para2PeakR ); + setBand( 4, &controls->m_para3ActiveModel, &controls->m_para3FreqModel, &controls->m_para3ResModel, &controls->m_para3GainModel, QColor(255 ,255, 255), tr( "Peak 3" ), &controls->m_para3PeakL, &controls->m_para3PeakR ); + setBand( 5, &controls->m_para4ActiveModel, &controls->m_para4FreqModel, &controls->m_para4ResModel, &controls->m_para4GainModel, QColor(255 ,255, 255), tr( "Peak 4" ), &controls->m_para4PeakL, &controls->m_para4PeakR ); + setBand( 6, &controls->m_highShelfActiveModel, &controls->m_highShelfFreqModel, &controls->m_highShelfResModel, &controls->m_highShelfGainModel, QColor(255 ,255, 255), tr( "High Shelf" ), &controls->m_highShelfPeakL, &controls->m_highShelfPeakR ); + setBand( 7, &controls->m_lpActiveModel, &controls->m_lpFreqModel, &controls->m_lpResModel, 0, QColor(255 ,255, 255), tr( "LP" ) ,0,0); + int cw = width()/8; //the chanel width in pixels + int ko = ( cw * 0.5 ) - ((new Knob( knobBright_26, 0 ))->width() * 0.5 ); + + m_inGainFader = new EqFader( &controls->m_inGainModel, tr( "In Gain" ), this, &controls->m_inPeakL, &controls->m_inPeakR); + m_inGainFader->move( 10, 5 ); + + + m_outGainFader = new EqFader( &controls->m_outGainModel, tr( "Out Gain" ), this, &controls->m_outPeakL, &controls->m_outPeakR ); + m_outGainFader->move( 315, 5 ); + //gain faders + + int fo = (cw * 0.5) - (m_outGainFader->width() * 0.5 ); + + for( int i = 1; i < m_parameterWidget->bandCount() - 1; i++) + { + m_gainFader = new EqFader( m_parameterWidget->getBandModels(i)->gain, tr( "" ), this ,m_parameterWidget->getBandModels( i )->peakL, m_parameterWidget->getBandModels( i )->peakR ); + m_gainFader->move( cw * i + fo , 123 ); + m_gainFader->setMinimumHeight(80); + m_gainFader->resize(m_gainFader->width() , 80); + 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 ); + 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); + + //Analize Box + m_analyzeBox = new LedCheckBox( tr( "Analyze" ), this , "" , LedCheckBox::Green ); + m_analyzeBox->move( cw*1 + ko + 5, 15 ); + m_analyzeBox->setModel( &controls->m_analyzeModel ); + +} + +void EqControlsDialog::mouseDoubleClickEvent(QMouseEvent *event) +{ + m_originalHeight = parentWidget()->height() == 150 ? m_originalHeight : parentWidget()->height() ; + parentWidget()->setFixedHeight( parentWidget()->height() == m_originalHeight ? 150 : m_originalHeight ); + update(); +} diff --git a/plugins/Eq/EqControlsDialog.h b/plugins/Eq/EqControlsDialog.h new file mode 100644 index 00000000000..d220d51ad3b --- /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 00000000000..ee78f459d4a --- /dev/null +++ b/plugins/Eq/EqEffect.cpp @@ -0,0 +1,375 @@ +/* + * 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" + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT eq_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Eq", + QT_TRANSLATE_NOOP( "pluginBrowser", "A native eq plugin" ), + "Dave French ", + 0x0100, + Plugin::Effect, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +} ; + +} + + +EqEffect::EqEffect(Model *parent, const Plugin::Descriptor::SubPluginFeatures::Key *key) : + Effect( &eq_plugin_descriptor, parent, key ), + m_eqControls( this ), + m_upBufFrames( 0 ) +{ + m_dFilterCount = 4; + m_downsampleFilters = new EqLinkwitzRiley[m_dFilterCount]; + for( int i = 0; i < m_dFilterCount; i++) + { + m_downsampleFilters[i].setFrequency(21000); + m_downsampleFilters[i].setSR(Engine::mixer()->processingSampleRate() * 2 ); + } + m_upBuf = 0; +} + + + + +EqEffect::~EqEffect() +{ + if(m_upBuf) + { + delete m_upBuf; + } +} + + + + +bool EqEffect::processAudioBuffer(sampleFrame *buf, const fpp_t frames) +{ + if( !isEnabled() || !isRunning () ) + { + return( false ); + } + m_eqControls.m_inProgress = true; + double outSum = 0.0; + const float outGain = m_eqControls.m_outGainModel.value(); + const int sampleRate = Engine::mixer()->processingSampleRate() * 2; + sampleFrame m_inPeak = { 0, 0 }; + + if(m_eqControls.m_analyzeModel.value() ) + { + m_eqControls.m_inFftBands.analyze( buf, frames ); + } + else + { + m_eqControls.m_inFftBands.clear(); + } + upsample( buf, frames ); + gain(m_upBuf , m_upBufFrames, m_eqControls.m_inGainModel.value(), &m_inPeak ); + m_eqControls.m_inPeakL = m_eqControls.m_inPeakL < m_inPeak[0] ? m_inPeak[0] : m_eqControls.m_inPeakL; + m_eqControls.m_inPeakR = m_eqControls.m_inPeakR < m_inPeak[1] ? m_inPeak[0] : m_eqControls.m_inPeakR; + + if(m_eqControls.m_hpActiveModel.value() ){ + m_hp12.setSampleRate( sampleRate ); + m_hp12.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp12.setQ( m_eqControls.m_hpResModel.value() ); + m_hp12.processBuffer( m_upBuf , m_upBufFrames ); + + if( m_eqControls.m_hp24Model.value() || m_eqControls.m_hp48Model.value() ) + { + m_hp24.setSampleRate( sampleRate ); + m_hp24.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp24.setQ( m_eqControls.m_hpResModel.value() ); + m_hp24.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_hp48Model.value() ) + { + m_hp480.setSampleRate( sampleRate ); + m_hp480.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp480.setQ( m_eqControls.m_hpResModel.value() ); + m_hp480.processBuffer( m_upBuf , m_upBufFrames ); + + m_hp481.setSampleRate( sampleRate ); + m_hp481.setFrequency( m_eqControls.m_hpFeqModel.value() ); + m_hp481.setQ( m_eqControls.m_hpResModel.value() ); + m_hp481.processBuffer( m_upBuf , m_upBufFrames ); + } + } + + if( m_eqControls.m_lowShelfActiveModel.value() ) + { + m_lowShelf.setSampleRate( sampleRate ); + m_lowShelf.setFrequency( m_eqControls.m_lowShelfFreqModel.value() ); + m_lowShelf.setQ( m_eqControls.m_lowShelfResModel .value() ); + m_lowShelf.setGain( m_eqControls.m_lowShelfGainModel.value() ); + m_lowShelf.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para1ActiveModel.value() ) + { + m_para1.setSampleRate(sampleRate ); + m_para1.setFrequency( m_eqControls.m_para1FreqModel.value() ); + m_para1.setQ( m_eqControls.m_para1ResModel.value() ); + m_para1.setGain( m_eqControls.m_para1GainModel.value() ); + m_para1.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para2ActiveModel.value() ) + { + m_para2.setSampleRate( sampleRate ); + m_para2.setFrequency( m_eqControls.m_para2FreqModel.value() ); + m_para2.setQ( m_eqControls.m_para2ResModel.value() ); + m_para2.setGain( m_eqControls.m_para2GainModel.value() ); + m_para2.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para3ActiveModel.value() ) + { + m_para3.setSampleRate( sampleRate); + m_para3.setFrequency( m_eqControls.m_para3FreqModel.value() ); + m_para3.setQ( m_eqControls.m_para3ResModel.value() ); + m_para3.setGain( m_eqControls.m_para3GainModel.value() ); + m_para3.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_para4ActiveModel.value() ) + { + m_para4.setSampleRate( sampleRate ); + m_para4.setFrequency( m_eqControls.m_para4FreqModel.value() ); + m_para4.setQ( m_eqControls.m_para4ResModel.value() ); + m_para4.setGain( m_eqControls.m_para4GainModel.value() ); + m_para4.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_highShelfActiveModel.value() ) + { + m_highShelf.setSampleRate( sampleRate ); + m_highShelf.setFrequency( m_eqControls.m_highShelfFreqModel.value() ); + m_highShelf.setQ( m_eqControls.m_highShelfResModel.value() ); + m_highShelf.setGain( m_eqControls.m_highShelfGainModel.value() ); + m_highShelf.processBuffer( m_upBuf , m_upBufFrames ); + } + + if(m_eqControls.m_lpActiveModel.value() ){ + m_lp12.setSampleRate( sampleRate ); + m_lp12.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp12.setQ( m_eqControls.m_lpResModel.value() ); + m_lp12.processBuffer( m_upBuf , m_upBufFrames ); + + if( m_eqControls.m_lp24Model.value() || m_eqControls.m_lp48Model.value() ) + { + m_lp24.setSampleRate( sampleRate ); + m_lp24.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp24.setQ( m_eqControls.m_lpResModel.value() ); + m_lp24.processBuffer( m_upBuf , m_upBufFrames ); + } + + if( m_eqControls.m_lp48Model.value() ) + { + m_lp480.setSampleRate( sampleRate ); + m_lp480.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp480.setQ( m_eqControls.m_lpResModel.value() ); + m_lp480.processBuffer( m_upBuf , m_upBufFrames ); + + m_lp481.setSampleRate( sampleRate ); + m_lp481.setFrequency( m_eqControls.m_lpFreqModel.value() ); + m_lp481.setQ( m_eqControls.m_lpResModel.value() ); + m_lp481.processBuffer( m_upBuf , m_upBufFrames ); + } + } + + sampleFrame outPeak = { 0, 0 }; + gain( m_upBuf , m_upBufFrames, outGain, &outPeak ); + m_eqControls.m_outPeakL = m_eqControls.m_outPeakL < outPeak[0] ? outPeak[0] : m_eqControls.m_outPeakL; + m_eqControls.m_outPeakR = m_eqControls.m_outPeakR < outPeak[1] ? outPeak[0] : m_eqControls.m_outPeakR; + for( int i =0; i < m_dFilterCount; i++) + { + m_downsampleFilters[i].processBuffer(m_upBuf , m_upBufFrames ); + } + downSample( buf, frames ); + for( fpp_t f = 0; f < frames; ++f ) + { + outSum += buf[f][0]*buf[f][0] + buf[f][1]*buf[f][1]; + } + checkGate( outSum / frames ); + if(m_eqControls.m_analyzeModel.value() ) + { + m_eqControls.m_outFftBands.analyze( buf, frames ); + } + else + { + m_eqControls.m_outFftBands.clear(); + } + setBandPeaks( &m_eqControls.m_outFftBands , ( int )( sampleRate * 0.5 ) ); + m_eqControls.m_inProgress = false; + return isRunning(); +} + + + + +void EqEffect::gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ) +{ + peak[0][0] = 0.0f; peak[0][1] = 0.0f; + for( fpp_t f = 0; f < frames; ++f ) + { + buf[f][0] *= scale; + buf[f][1] *= scale; + + if( fabs( buf[f][0] ) > peak[0][0] ) + { + peak[0][0] = fabs( buf[f][0] ); + } + if( fabs( buf[f][1] ) > peak[0][1] ) + { + peak[0][1] = fabs( buf[f][0] ); + } + + } +} + + + + + +sampleFrame m_lastUpFrame; +void EqEffect::upsample( sampleFrame *buf, const fpp_t frames ) +{ + + if( m_upBufFrames != frames * 2 ) + { + if( m_upBuf ) + { + delete m_upBuf; + } + m_upBuf = new sampleFrame[frames * 2]; + m_upBufFrames = frames * 2; + } + for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) + { + m_upBuf[f2][0] = linearInterpolate( m_lastUpFrame[0],buf[f][0], 0.5 ); + m_upBuf[f2][1] = linearInterpolate( m_lastUpFrame[1],buf[f][1], 0.5 ); + m_upBuf[f2+1][0] = buf[f][0]; + m_upBuf[f2+1][1] = buf[f][1]; + m_lastUpFrame[0] = buf[f][0]; + m_lastUpFrame[1] = buf[f][1]; + } +} + + +void EqEffect::downSample( sampleFrame *buf, const fpp_t frames ) +{ + for( int f = 0, f2 = 0; f < frames; ++f, f2 += 2 ) + { + buf[f][0] = m_upBuf[f2+1][0]; + buf[f][1] = m_upBuf[f2+1][1]; + } +} + + + + +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_para1ResModel.value() * 0.5), + m_eqControls.m_para1FreqModel.value() + + (m_eqControls.m_para1FreqModel.value() / m_eqControls.m_para1ResModel.value() * 0.5), + fft , samplerate ); + + m_eqControls.m_para2PeakL = m_eqControls.m_para2PeakR = + peakBand( m_eqControls.m_para2FreqModel.value() + - (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), + m_eqControls.m_para2FreqModel.value() + + (m_eqControls.m_para2FreqModel.value() / m_eqControls.m_para2ResModel.value() * 0.5), + fft , samplerate ); + + m_eqControls.m_para3PeakL = m_eqControls.m_para3PeakR = + peakBand( m_eqControls.m_para3FreqModel.value() + - (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), + m_eqControls.m_para3FreqModel.value() + + (m_eqControls.m_para3FreqModel.value() / m_eqControls.m_para3ResModel.value() * 0.5), + fft , samplerate ); + + m_eqControls.m_para4PeakL = m_eqControls.m_para4PeakR = + peakBand( m_eqControls.m_para4FreqModel.value() + - (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), + m_eqControls.m_para4FreqModel.value() + + (m_eqControls.m_para4FreqModel.value() / m_eqControls.m_para4ResModel.value() * 0.5), + fft , samplerate ); + + m_eqControls.m_highShelfPeakL = m_eqControls.m_highShelfPeakR = + peakBand( m_eqControls.m_highShelfFreqModel.value(), + samplerate * 0.5 , fft, samplerate ); +} + + + + + +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 00000000000..9a4cd941111 --- /dev/null +++ b/plugins/Eq/EqEffect.h @@ -0,0 +1,89 @@ +/* eqeffect.h - defination of EqEffect class. +* +* Copyright (c) 2014 David French +* +* This file is part of LMMS - http://lmms.io +* +* This program is free software; you can redistribute it and/or +* modify it under the terms of the GNU General Public +* License as published by the Free Software Foundation; either +* version 2 of the License, or (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* General Public License for more details. +* +* You should have received a copy of the GNU General Public +* License along with this program (see COPYING); if not, write to the +* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +* Boston, MA 02110-1301 USA. +* +*/ + + +#ifndef EQEFFECT_H +#define EQEFFECT_H + +#include "Effect.h" +#include "EqControls.h" +#include "lmms_math.h" +#include "BasicFilters.h" +#include "EqFilter.h" + + + +class EqEffect : public Effect +{ +public: + EqEffect( Model* parent , const Descriptor::SubPluginFeatures::Key* key ); + virtual ~EqEffect(); + virtual bool processAudioBuffer( sampleFrame *buf, const fpp_t frames ); + virtual EffectControls* controls() + { + return &m_eqControls; + } + void gain( sampleFrame *buf, const fpp_t frames, float scale, sampleFrame* peak ); + +private: + EqControls m_eqControls; + + EqHp12Filter m_hp12; + EqHp12Filter m_hp24; + EqHp12Filter m_hp480; + EqHp12Filter m_hp481; + + EqLowShelfFilter m_lowShelf; + + EqPeakFilter m_para1; + EqPeakFilter m_para2; + EqPeakFilter m_para3; + EqPeakFilter m_para4; + + EqHighShelfFilter m_highShelf; + + EqLp12Filter m_lp12; + EqLp12Filter m_lp24; + EqLp12Filter m_lp480; + EqLp12Filter m_lp481; + EqLinkwitzRiley* m_downsampleFilters; + int m_dFilterCount; + sampleFrame* m_upBuf; + fpp_t m_upBufFrames; + + void upsample( sampleFrame *buf, const fpp_t frames ); + void downSample( sampleFrame *buf, const fpp_t frames ); + void analyze( sampleFrame *buf, const fpp_t frames, EqAnalyser* fft ); + 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 00000000000..9ef7f80c3e6 --- /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 00000000000..b703edc95e7 --- /dev/null +++ b/plugins/Eq/EqFilter.h @@ -0,0 +1,388 @@ +/* + * eqfilter.cpp - defination of EqFilterclass. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#ifndef EQFILTER_H +#define EQFILTER_H + +#include "BasicFilters.h" +#include "lmms_math.h" + +/// +/// \brief The EqFilter class. +/// A wrapper for the StereoBiQuad class, giving it freq, res, and gain controls. +/// It is designed to process periods in one pass, with recalculation of coefficents +/// upon parameter changes. The intention is to use this as a bass class, children override +/// the calcCoefficents() function, providing the coefficents a1, a2, b0, b1, b2. +/// +class EqFilter : public StereoBiQuad +{ +public: + EqFilter() + { + + } + + + + + virtual inline void setSampleRate( int sampleRate ) + { + if( sampleRate != m_sampleRate ) + { + m_sampleRate = sampleRate; + calcCoefficents(); + } + } + + + + + virtual inline void setFrequency( float freq ){ + if ( freq != m_freq ) + { + m_freq = freq; + calcCoefficents(); + } + } + + + + + virtual void setQ( float res ) + { + if ( res != m_res ) + { + m_res = res; + calcCoefficents(); + } + } + + + + + virtual void setGain( float gain ) + { + if ( gain != m_gain ) + { + m_gain = gain; + calcCoefficents(); + } + } + + + + + /// + /// \brief processBuffer + /// \param buf Audio Buffer + /// \param frames Count of sampleFrames in Audio Buffer + /// + virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) + { + for ( fpp_t f = 0 ; f < frames ; ++f) + { + buf[f][0] = update( buf[f][0] , 0); + buf[f][1] = update( buf[f][1] , 1); + } + } + +protected: + /// + /// \brief calcCoefficents + /// Override this in child classes to provide the coefficents, based on + /// Freq, Res and Gain + virtual void calcCoefficents(){ + setCoeffs( 0, 0, 0, 0, 0 ); + + } + + float m_sampleRate; + float m_freq; + float m_res; + float m_gain; +}; + + + + +/// +/// \brief The EqHp12Filter class +/// A 2 pole High Pass Filter +/// Coefficent calculations from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +class EqHp12Filter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = ( 1 + c ) * 0.5; + b1 = ( -( 1 + c ) ); + b2 = ( 1 + c ) * 0.5; + a0 = 1 + alpha; + a1 = ( -2 * c ); + a2 = 1 - alpha; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + + + } +}; + + + + +/// +/// \brief The EqLp12Filter class. +/// A 2 pole low pass filter +/// Coefficent calculations from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +/// +class EqLp12Filter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float alpha = s / ( 2 * m_res ); + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = ( 1 - c ) * 0.5; + b1 = 1 - c; + b2 = ( 1 - c ) * 0.5; + a0 = 1 + alpha; + a1 = -2 * c; + a2 = 1 - alpha; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + + + +/// +/// \brief The EqPeakFilter class +/// A Peak Filter +/// Coefficent calculations from http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt +/// +class EqPeakFilter : public EqFilter +{ +public: + + + virtual void calcCoefficents() + { + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); + float alpha = s / ( 2 * m_res ); + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = 1 + alpha*A; + b1 = -2*c; + b2 = 1 - alpha*A; + a0 = 1 + alpha/A; + a1 = -2*c; + a2 = 1 - alpha/A; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + + + + +class EqLowShelfFilter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025); + // float alpha = s / ( 2 * m_res ); + float beta = sqrt( A ) / m_res; + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = A * ( ( A+1 ) - ( A-1 ) * c + beta * s ); + b1 = 2 * A * ( ( A - 1 ) - ( A + 1 ) * c) ; + b2 = A * ( ( A + 1 ) - ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) + ( A - 1 ) * c + beta * s; + a1 = -2 * ( ( A - 1 ) + ( A + 1 ) * c ); + a2 = ( A + 1 ) + ( A - 1) * c - beta * s; + + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + + + } +}; + +class EqHighShelfFilter : public EqFilter +{ +public : + virtual void calcCoefficents() + { + + // calc intermediate + float w0 = F_2PI * m_freq / m_sampleRate; + float c = cosf( w0 ); + float s = sinf( w0 ); + float A = pow( 10, m_gain * 0.025 ); + float beta = sqrt( A ) / m_res; + + float a0, a1, a2, b0, b1, b2; // coeffs to calculate + + //calc coefficents + b0 = A *( ( A +1 ) + ( A - 1 ) * c + beta * s); + b1 = -2 * A * ( ( A - 1 ) + ( A + 1 ) * c ); + b2 = A * ( ( A + 1 ) + ( A - 1 ) * c - beta * s); + a0 = ( A + 1 ) - ( A - 1 ) * c + beta * s; + a1 = 2 * ( ( A - 1 ) - ( A + 1 ) * c ); + a2 = ( A + 1) - ( A - 1 ) * c - beta * s; + //normalise + b0 /= a0; + b1 /= a0; + b2 /= a0; + a1 /= a0; + a2 /= a0; + a0 = 1; + + setCoeffs( a1, a2, b0, b1, b2 ); + } +}; + + + + +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; + setLowpass(m_freq); + setSampleRate( sampleRate ); + } + } + + + + + virtual inline void setFrequency( float freq ){ + if ( freq != m_freq ) + { + m_freq = freq; + setLowpass(m_freq); + } + } + + + + + virtual void processBuffer( sampleFrame* buf, const fpp_t frames ) + { + for ( fpp_t f = 0 ; f < frames ; ++f) + { + buf[f][0] = update( buf[f][0] , 0); + buf[f][1] = update( buf[f][1] , 1); + } + } +protected: + + float m_freq; + int m_sr; + + +}; + + + + +#endif // EQFILTER_H diff --git a/plugins/Eq/EqParameterWidget.cpp b/plugins/Eq/EqParameterWidget.cpp new file mode 100644 index 00000000000..31c74a7f021 --- /dev/null +++ b/plugins/Eq/EqParameterWidget.cpp @@ -0,0 +1,221 @@ +/* + * eqparameterwidget.cpp - defination of EqParameterWidget class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + +#include "EqParameterWidget.h" +#include "QPainter" +#include "qwidget.h" +#include "lmms_math.h" +#include "MainWindow.h" +#include "QMouseEvent" + +EqParameterWidget::EqParameterWidget( QWidget *parent ) : + QWidget( parent ), + m_bands ( 0 ), + m_selectedBand ( 0 ) +{ + m_bands = new EqBand[8]; + resize( 250, 116 ); + // connect( Engine::mainWindow(), SIGNAL( periodicUpdate() ), this, SLOT( update() ) ); + 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 ); + +} + + + + +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; +} + + + + +void EqParameterWidget::mouseMoveEvent( QMouseEvent *event ) +{ + int deltaX = event->x() - m_oldX; + int deltaR = event->y() - m_oldY; + m_oldX = event->x(); m_oldY = event->y(); + if(m_selectedBand && m_selectedBand->active->value() ) + { + switch ( m_mouseAction ) { + case none : + break; + case drag: + if( m_selectedBand->freq ) m_selectedBand->freq->setValue( xPixelToFreq( m_oldX ) ); + if( m_selectedBand->gain )m_selectedBand->gain->setValue( yPixelToGain( m_oldY ) ); + break; + case res: + if( m_selectedBand->res )m_selectedBand->res->incValue( deltaX * resPixelMultiplyer() ); + if( m_selectedBand->res )m_selectedBand->res->incValue( (-deltaR) * resPixelMultiplyer() ); + break; + default: + break; + } + } +} + + + + +void EqParameterWidget::mouseDoubleClickEvent( QMouseEvent *event ) +{ + EqBand* selected = selectNearestHandle( event->x() , event->y() ); + if( selected ) + { + selected->active->setValue( selected->active->value() ? 0 : 1 ); + } +} + + + + +EqBand* EqParameterWidget::selectNearestHandle( const int x, const int y ) +{ + EqBand* selectedModel = 0; + float* distanceToHandles = new float[bandCount()]; + //calc distance to each handle + for( int i = 0 ; i < bandCount() ; i++ ) + { + int xOffset = m_bands[i].x - x; + int yOffset = m_bands[i].y - y; + distanceToHandles[i] = fabs( sqrt( ( xOffset * xOffset ) + ( yOffset * yOffset ) ) ); + } + //select band + int shortestBand = 0; + for ( int i = 1 ; i < bandCount() ; i++ ) + { + if ( distanceToHandles [i] < distanceToHandles[shortestBand] ){ + shortestBand = i; + } + } + if(distanceToHandles[shortestBand] < maxDistanceFromHandle() ) + { + selectedModel = &m_bands[shortestBand]; + } + delete distanceToHandles; + return selectedModel; +} + + + + +EqBand::EqBand() : + gain ( 0 ), + res ( 0 ), + freq ( 0 ), + color ( QColor( 255, 255, 255 ) ), + 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 00000000000..6cc7f1a89cf --- /dev/null +++ b/plugins/Eq/EqParameterWidget.h @@ -0,0 +1,161 @@ +/* + * eqparameterwidget.cpp - defination of EqParameterWidget class. + * + * Copyright (c) 2014 David French + * + * This file is part of LMMS - http://lmms.io + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public + * License along with this program (see COPYING); if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + */ + + +#ifndef EQPARAMETERWIDGET_H +#define EQPARAMETERWIDGET_H +#include +#include "EffectControls.h" + + +class EqBand +{ +public : + EqBand(); + FloatModel* gain; + FloatModel* res; + FloatModel* freq; + BoolModel* active; + QColor color; + int x; + int y; + QString name; + float* peakL; + float* peakR; + + +}; + + + +class EqParameterWidget : public QWidget +{ + +public: + explicit EqParameterWidget( QWidget *parent = 0 ); + ~EqParameterWidget(); + const int bandCount() + { + return 8; + } + + + + const int maxDistanceFromHandle() + { + return 20; + } + + + + + EqBand* getBandModels( int i ) + { + return &m_bands[i]; + } + + + + + const int activeAplha() + { + return 200; + } + + + + + const int inactiveAlpha() + { + return 100; + } + + + + + const float resPixelMultiplyer() + { + return 100; + } + + +signals: + +public slots: + +protected: + virtual void paintEvent ( QPaintEvent * event ); + virtual void mousePressEvent(QMouseEvent * event ); + virtual void mouseReleaseEvent(QMouseEvent * event); + virtual void mouseMoveEvent(QMouseEvent * event); + virtual void mouseDoubleClickEvent(QMouseEvent * event); + +private: + EqBand *m_bands; + float m_pixelsPerUnitWidth; + float m_pixelsPerUnitHeight; + float m_pixelsPerOctave; + float m_scale; + EqBand* m_selectedBand; + + EqBand* selectNearestHandle( const int x, const int y ); + + enum MouseAction { none, drag, res } m_mouseAction; + int m_oldX, m_oldY; + 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 00000000000..71c64f5a4d4 --- /dev/null +++ b/plugins/Eq/EqSpectrumView.h @@ -0,0 +1,218 @@ +/* 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; + + + EqAnalyser() : + m_framesFilledUp ( 0 ), + m_energy ( 0 ), + m_sr ( 1 ) + { + m_inProgress=false; + m_specBuf = (fftwf_complex *) fftwf_malloc( ( FFT_BUFFER_SIZE + 1 ) * sizeof( fftwf_complex ) ); + 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 ) + { + m_inProgress=true; + const int FFT_BUFFER_SIZE = 2048; + fpp_t f = 0; + if( frames > FFT_BUFFER_SIZE ) + { + m_framesFilledUp = 0; + f = frames - FFT_BUFFER_SIZE; + } + // meger channels + for( ; f < frames; ++f ) + { + m_buffer[m_framesFilledUp] = + ( buf[f][0] + buf[f][1] ) * 0.5; + ++m_framesFilledUp; + } + + if( m_framesFilledUp < FFT_BUFFER_SIZE ) + { + m_inProgress = false; + return; + } + + m_sr = Engine::mixer()->processingSampleRate(); + const int LOWEST_FREQ = 0; + const int HIGHEST_FREQ = m_sr / 2; + + fftwf_execute( m_fftPlan ); + absspec( m_specBuf, m_absSpecBuf, FFT_BUFFER_SIZE+1 ); + + compressbands( m_absSpecBuf, m_bands, FFT_BUFFER_SIZE+1, + MAX_BANDS, + ( int )( LOWEST_FREQ * ( FFT_BUFFER_SIZE + 1 ) / ( float )( m_sr / 2 ) ), + ( int )( HIGHEST_FREQ * ( FFT_BUFFER_SIZE + 1) / ( float )( m_sr / 2 ) ) ); + m_energy = maximum( m_bands, MAX_BANDS ) / maximum( m_buffer, FFT_BUFFER_SIZE ); + m_framesFilledUp = 0; + m_inProgress = false; + } +private: + bool m_inProgress; +}; + + +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 ) + { + 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 00000000000..33fa4960def 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 00000000000..c3febd0942a --- /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 00000000000..34d2b8f5246 --- /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 bbd52dbee4b..29f5a5aaf33 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 8b3c61df488..26b223b29bf 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 bdfa90d4583..1fef65a3e8a 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 00000000000..4a029dad864 --- /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 d476a056a1c..ac6125623e3 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 4d1fdaa4c36..9afc3ee1710 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_index = 0; - m_feedback = 0.0f; - setSampleRate( sampleRate ); + m_buffer = 0; + m_maxTime = maxTime; + m_maxLength = maxTime * sampleRate; + m_length = m_maxLength; + + 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 8c544fd6c10..866c5e697ae 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 bd0d84b98ae..4d4c06e0a32 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 008700ed788..ea0a854bda9 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 1e4d7d0f150..bc31dc326f3 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 f884f691cb1..04f2e62df05 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 00000000000..89e9f368011 Binary files /dev/null and b/plugins/Flanger/logo.png differ diff --git a/plugins/LadspaEffect/swh/imp_1199.c b/plugins/LadspaEffect/swh/imp_1199.c index ec1b35028a2..a5c7d3876a7 100644 --- a/plugins/LadspaEffect/swh/imp_1199.c +++ b/plugins/LadspaEffect/swh/imp_1199.c @@ -74,7 +74,11 @@ static fftw_real *real_in, *real_out, *comp_in, *comp_out; unsigned int fft_length[IMPULSES]; +#ifdef __clang__ +void impulse2freq(int id, float *imp, unsigned int length, fftw_real *out) +#else inline void impulse2freq(int id, float *imp, unsigned int length, fftw_real *out) +#endif { fftw_real impulse_time[MAX_FFT_LENGTH]; #ifdef FFTW3 diff --git a/plugins/VstEffect/VstSubPluginFeatures.cpp b/plugins/VstEffect/VstSubPluginFeatures.cpp index 8e0c623c305..3f218c2842b 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 51fdc05cdcb..db2adcfabd6 100644 --- a/plugins/VstEffect/VstSubPluginFeatures.h +++ b/plugins/VstEffect/VstSubPluginFeatures.h @@ -40,8 +40,13 @@ class VstSubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures virtual void listSubPluginKeys( const Plugin::Descriptor * _desc, KeyList & _kl ) const; - +private: + void addPluginsFromDir(QStringList* filenames, QString path) const; } ; + + + + #endif diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index ce390192a66..6a0fbef7121 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -742,6 +742,7 @@ AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _ m_to( m_sampleBuffer.frames() ), m_last_from( 0 ), m_last_to( 0 ), + m_last_amp( 0 ), m_startKnob( 0 ), m_endKnob( 0 ), m_loopKnob( 0 ), diff --git a/plugins/carlabase/carla.cpp b/plugins/carlabase/carla.cpp index ca2ebab1376..0831bba50b0 100644 --- a/plugins/carlabase/carla.cpp +++ b/plugins/carlabase/carla.cpp @@ -251,16 +251,16 @@ intptr_t CarlaInstrument::handleDispatcher(const NativeHostDispatcherOpcode opco switch (opcode) { - case HOST_OPCODE_NULL: + case NATIVE_HOST_OPCODE_NULL: break; - case HOST_OPCODE_UPDATE_PARAMETER: - case HOST_OPCODE_UPDATE_MIDI_PROGRAM: - case HOST_OPCODE_RELOAD_PARAMETERS: - case HOST_OPCODE_RELOAD_MIDI_PROGRAMS: - case HOST_OPCODE_RELOAD_ALL: + case NATIVE_HOST_OPCODE_UPDATE_PARAMETER: + case NATIVE_HOST_OPCODE_UPDATE_MIDI_PROGRAM: + case NATIVE_HOST_OPCODE_RELOAD_PARAMETERS: + case NATIVE_HOST_OPCODE_RELOAD_MIDI_PROGRAMS: + case NATIVE_HOST_OPCODE_RELOAD_ALL: // nothing break; - case HOST_OPCODE_UI_UNAVAILABLE: + case NATIVE_HOST_OPCODE_UI_UNAVAILABLE: handleUiClosed(); break; } @@ -459,7 +459,7 @@ PluginView* CarlaInstrument::instantiateView(QWidget* parent) void CarlaInstrument::sampleRateChanged() { - fDescriptor->dispatcher(fHandle, PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, handleGetSampleRate()); + fDescriptor->dispatcher(fHandle, NATIVE_PLUGIN_OPCODE_SAMPLE_RATE_CHANGED, 0, 0, nullptr, handleGetSampleRate()); } // ------------------------------------------------------------------- diff --git a/plugins/flanger/CMakeLists.txt b/plugins/flanger/CMakeLists.txt deleted file mode 100644 index bb3579e731e..00000000000 --- 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 55d7f215cc5..00000000000 --- 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 a309e38c91d..00000000000 --- 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/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 8e8785d6e4c..14ecb8fa16f 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -788,16 +788,18 @@ void lb302Synth::processNote( NotePlayHandle * _n ) void lb302Synth::play( sampleFrame * _working_buffer ) { + m_notesMutex.lock(); while( ! m_notes.isEmpty() ) { processNote( m_notes.takeFirst() ); }; + m_notesMutex.unlock(); const fpp_t frames = Engine::mixer()->framesPerPeriod(); process( _working_buffer, frames ); instrumentTrack()->processAudioBuffer( _working_buffer, frames, NULL ); - release_frame = 0; +// release_frame = 0; //removed for issue # 1432 } diff --git a/plugins/opl2/opl2instrument.cpp b/plugins/opl2/opl2instrument.cpp index 2a8867c661b..d1613a1aa8c 100644 --- a/plugins/opl2/opl2instrument.cpp +++ b/plugins/opl2/opl2instrument.cpp @@ -497,7 +497,7 @@ void opl2instrument::loadPatch(unsigned char inst[14]) { void opl2instrument::tuneEqual(int center, float Hz) { float tmp; for(int n=0; n<128; ++n) { - tmp = Hz*pow( 2, ( n - center ) / 12.0 + pitchbend / 1200.0 ); + tmp = Hz*pow( 2.0, ( n - center ) * ( 1.0 / 12.0 ) + pitchbend * ( 1.0 / 1200.0 ) ); fnums[n] = Hz2fnum( tmp ); } } @@ -505,7 +505,7 @@ void opl2instrument::tuneEqual(int center, float Hz) { // Find suitable F number in lowest possible block int opl2instrument::Hz2fnum(float Hz) { for(int block=0; block<8; ++block) { - unsigned int fnum = Hz * pow(2, 20-block) / 49716; + unsigned int fnum = Hz * pow( 2.0, 20.0 - (double)block ) * ( 1.0 / 49716.0 ); if(fnum<1023) { return fnum + (block << 10); } diff --git a/plugins/papu/papu_instrument.cpp b/plugins/papu/papu_instrument.cpp index 9177d72830a..8e011816444 100644 --- a/plugins/papu/papu_instrument.cpp +++ b/plugins/papu/papu_instrument.cpp @@ -361,11 +361,11 @@ void papuInstrument::playNote( NotePlayHandle * _n, //PRNG Frequency = (1048576 Hz / (ratio + 1)) / 2 ^ (shiftclockfreq + 1) char sopt=0; char ropt=1; - float fopt = 524288.0 / ( ropt * pow( 2, sopt+1 ) ); + float fopt = 524288.0 / ( ropt * pow( 2.0, sopt + 1.0 ) ); float f; for ( char s=0; s<16; s++ ) for ( char r=0; r<8; r++ ) { - f = 524288.0 / ( r * pow( 2, s+1 ) ); + f = 524288.0 / ( r * pow( 2.0, s + 1.0 ) ); if( fabs( freq-fopt ) > fabs( freq-f ) ) { fopt = f; ropt = r; diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 0cc604d63a6..38b789d130f 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 ) ) ); @@ -420,7 +421,7 @@ void VstPlugin::setParameterDump( const QMap & _pdump ) { ( *it ).section( ':', 0, 0 ).toInt(), "", - ( *it ).section( ':', 1, 1 ).toFloat() + ( *it ).section( ':', 2, -1 ).toFloat() } ; m.addInt( item.index ); m.addString( item.shortLabel ); diff --git a/plugins/zynaddsubfx/LocalZynAddSubFx.cpp b/plugins/zynaddsubfx/LocalZynAddSubFx.cpp index ac126c380d3..7bb7e764000 100644 --- a/plugins/zynaddsubfx/LocalZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/LocalZynAddSubFx.cpp @@ -89,6 +89,7 @@ LocalZynAddSubFx::LocalZynAddSubFx() : LocalZynAddSubFx::~LocalZynAddSubFx() { delete m_master; + delete m_ioEngine; if( --s_instanceCount == 0 ) { diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index 9c1634f4210..c556870c343 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -290,7 +290,13 @@ void ConfigManager::loadConfigFile() node = node.nextSibling(); } - if( value( "paths", "artwork" ) != "" ) + // don't use dated theme folders as they break the UI (i.e. 0.4 != 1.0, etc) + bool use_artwork_path = + root.attribute( "version" ).startsWith( + QString::number( LMMS_VERSION_MAJOR ) + "." + + QString::number( LMMS_VERSION_MINOR ) ); + + if( use_artwork_path && value( "paths", "artwork" ) != "" ) { m_artworkDir = value( "paths", "artwork" ); if( !QDir( m_artworkDir ).exists() ) diff --git a/src/core/Controller.cpp b/src/core/Controller.cpp index 71a6dca3a93..325399e0bf2 100644 --- a/src/core/Controller.cpp +++ b/src/core/Controller.cpp @@ -205,8 +205,11 @@ Controller * Controller::create( ControllerTypes _ct, Model * _parent ) if( dummy ) c = dummy; else + { c = new Controller( DummyController, NULL, QString() ); + dummy = c; + } break; case Controller::LfoController: diff --git a/src/core/RemotePlugin.cpp b/src/core/RemotePlugin.cpp index c30f0c9e300..84555fb3c20 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/SampleBuffer.cpp b/src/core/SampleBuffer.cpp index 9b7b5fa006c..01778653f98 100644 --- a/src/core/SampleBuffer.cpp +++ b/src/core/SampleBuffer.cpp @@ -957,6 +957,7 @@ void SampleBuffer::visualize( QPainter & _p, const QRect & _dr, _p.drawPolyline( l, nb_frames / fpp ); _p.drawPolyline( r, nb_frames / fpp ); delete[] l; + delete[] r; } diff --git a/src/core/Song.cpp b/src/core/Song.cpp index c9ac564522f..2dd9c549bee 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -619,6 +619,7 @@ void Song::stop() void Song::startExport() { stop(); + m_playPos[Mode_PlaySong].setTicks( 0 ); playSong(); diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 90de3d00077..aefdcf2742e 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -194,12 +194,13 @@ void FxMixerView::refreshDisplay() for( int i = 1; iremoveWidget(m_fxChannelViews[i]->m_fxLine); + m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView ); delete m_fxChannelViews[i]->m_fader; delete m_fxChannelViews[i]->m_muteBtn; delete m_fxChannelViews[i]->m_soloBtn; delete m_fxChannelViews[i]->m_fxLine; + delete m_fxChannelViews[i]->m_rackView; delete m_fxChannelViews[i]; - m_racksLayout->removeWidget( m_fxChannelViews[i]->m_rackView ); } m_channelAreaWidget->adjustSize(); @@ -363,16 +364,15 @@ void FxMixerView::deleteChannel(int index) // delete the view chLayout->removeWidget(m_fxChannelViews[index]->m_fxLine); + m_racksLayout->removeWidget( m_fxChannelViews[index]->m_rackView ); delete m_fxChannelViews[index]->m_fader; delete m_fxChannelViews[index]->m_muteBtn; delete m_fxChannelViews[index]->m_soloBtn; delete m_fxChannelViews[index]->m_fxLine; + delete m_fxChannelViews[index]->m_rackView; delete m_fxChannelViews[index]; m_channelAreaWidget->adjustSize(); - // delete the fx rack - m_racksLayout->removeWidget( m_fxChannelViews[index]->m_rackView ); - // make sure every channel knows what index it is for(int i=0; i #include "PluginBrowser.h" +#include // for std::sort + #include "embed.h" #include "debug.h" #include "templates.h" diff --git a/src/gui/widgets/Fader.cpp b/src/gui/widgets/Fader.cpp index 1af7bb04056..0f0bef69d2b 100644 --- a/src/gui/widgets/Fader.cpp +++ b/src/gui/widgets/Fader.cpp @@ -170,7 +170,7 @@ void Fader::mouseMoveEvent( QMouseEvent *mouseEvent ) { int dy = m_moveStartPoint - mouseEvent->globalY(); - float delta = dy * ( m_model->maxValue() - m_model->minValue() ) / (float) ( height() - ( *s_knob ).height() ); + float delta = dy * ( m_model->maxValue() - m_model->minValue() ) / (float) ( height() - ( *m_knob ).height() ); model()->setValue( m_startValue + delta ); @@ -186,7 +186,7 @@ void Fader::mousePressEvent( QMouseEvent* mouseEvent ) if( mouseEvent->button() == Qt::LeftButton && ! ( mouseEvent->modifiers() & Qt::ControlModifier ) ) { - if( mouseEvent->y() >= knobPosY() - ( *s_knob ).height() && mouseEvent->y() < knobPosY() ) + if( mouseEvent->y() >= knobPosY() - ( *m_knob ).height() && mouseEvent->y() < knobPosY() ) { updateTextFloat(); s_textFloat->show(); @@ -346,13 +346,16 @@ void Fader::paintEvent( QPaintEvent * ev) // background painter.drawPixmap( ev->rect(), *m_back, ev->rect() ); - // peak leds //float fRange = abs( m_fMaxPeak ) + abs( m_fMinPeak ); + int height = m_back->height(); + int width = m_back->width() / 2; + int center = m_back->width() - width; + int peak_L = calculateDisplayPeak( m_fPeakValue_L - m_fMinPeak ); int persistentPeak_L = qMax( 3, calculateDisplayPeak( m_persistentPeak_L - m_fMinPeak ) ); - painter.drawPixmap( QRect( 0, peak_L, 11, 116 - peak_L ), *m_leds, QRect( 0, peak_L, 11, 116 - peak_L ) ); + painter.drawPixmap( QRect( 0, peak_L, width, height - peak_L ), *m_leds, QRect( 0, peak_L, width, height - peak_L ) ); if( m_persistentPeak_L > 0.05 ) { @@ -363,7 +366,7 @@ void Fader::paintEvent( QPaintEvent * ev) int peak_R = calculateDisplayPeak( m_fPeakValue_R - m_fMinPeak ); int persistentPeak_R = qMax( 3, calculateDisplayPeak( m_persistentPeak_R - m_fMinPeak ) ); - painter.drawPixmap( QRect( 11, peak_R, 11, 116 - peak_R ), *m_leds, QRect( 11, peak_R, 11, 116 - peak_R ) ); + painter.drawPixmap( QRect( center, peak_R, width, height - peak_R ), *m_leds, QRect( center, peak_R, width, height - peak_R ) ); if( m_persistentPeak_R > 0.05 ) { @@ -373,7 +376,7 @@ void Fader::paintEvent( QPaintEvent * ev) } // knob - painter.drawPixmap( 0, knobPosY() - ( *m_knob ).height(), *s_knob ); + painter.drawPixmap( 0, knobPosY() - m_knob->height(), *m_knob ); } diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index b32414a3851..06219b94216 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -87,7 +87,7 @@ const char * volume_help = QT_TRANSLATE_NOOP( "InstrumentTrack", const int INSTRUMENT_WIDTH = 254; const int INSTRUMENT_HEIGHT = INSTRUMENT_WIDTH; -const int PIANO_HEIGHT = 84; +const int PIANO_HEIGHT = 82; const int INSTRUMENT_WINDOW_CACHE_SIZE = 8; diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 49d59f66c7e..f55100698b3 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 ) : @@ -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 ) { 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 );