diff --git a/data/themes/default/style.css b/data/themes/default/style.css index b68ef0658..bab240bf3 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -644,6 +644,16 @@ WatsynView knob#xtalkKnob { qproperty-outerColor: #fb50fb; } +MonstroView knob { + color: #ffffff; + qproperty-outerColor: #aaaaaa; + qproperty-outerRadius: 9; + qproperty-innerRadius: 6; + qproperty-centerPointX: 10; + qproperty-centerPointY: 10; + qproperty-lineWidth: 2.5; +} + /* palette information - each colour definition must be on a single line, and the line must begin with "palette:", with no leading whitespace * colour codes MUST be of the form #RRGGBB */ diff --git a/plugins/CMakeLists.txt b/plugins/CMakeLists.txt index b1247ce18..c5bf9175f 100644 --- a/plugins/CMakeLists.txt +++ b/plugins/CMakeLists.txt @@ -12,6 +12,7 @@ ADD_SUBDIRECTORY(LadspaEffect) ADD_SUBDIRECTORY(lb302) #ADD_SUBDIRECTORY(lb303) ADD_SUBDIRECTORY(midi_import) +ADD_SUBDIRECTORY(monstro) ADD_SUBDIRECTORY(organic) ADD_SUBDIRECTORY(opl2) ADD_SUBDIRECTORY(papu) diff --git a/plugins/monstro/CMakeLists.txt b/plugins/monstro/CMakeLists.txt new file mode 100644 index 000000000..6a9195b75 --- /dev/null +++ b/plugins/monstro/CMakeLists.txt @@ -0,0 +1,3 @@ +INCLUDE(BuildPlugin) + +BUILD_PLUGIN(monstro Monstro.cpp Monstro.h MOCFILES Monstro.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png") diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp new file mode 100644 index 000000000..900fb4b5d --- /dev/null +++ b/plugins/monstro/Monstro.cpp @@ -0,0 +1,1764 @@ +/* + * Monstro.cpp - a monstrous semi-modular 3-osc synth with modulation matrix + * + * Copyright (c) 2014 Vesa Kivimäki + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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 "Monstro.h" +#include "engine.h" +#include "InstrumentTrack.h" +#include "templates.h" +#include "gui_templates.h" +#include "tooltip.h" +#include "song.h" +#include "lmms_math.h" + +#include "embed.cpp" + + +extern "C" +{ + +Plugin::Descriptor PLUGIN_EXPORT monstro_plugin_descriptor = +{ + STRINGIFY( PLUGIN_NAME ), + "Monstro", + QT_TRANSLATE_NOOP( "pluginBrowser", + "Monstrous 3-oscillator synth with modulation matrix" ), + "Vesa Kivimäki ", + 0x0100, + Plugin::Instrument, + new PluginPixmapLoader( "logo" ), + NULL, + NULL +} ; + +} + + + + +MonstroSynth::MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph, + const sample_rate_t _samplerate, fpp_t _frames ) : + m_parent( _i ), + m_nph( _nph ), + m_samplerate( _samplerate ), + m_fpp( _frames ) +{ + m_env1_buf = new sample_t[_frames]; + m_env2_buf = new sample_t[_frames]; + m_lfo1_buf = new sample_t[_frames]; + m_lfo2_buf = new sample_t[_frames]; + + m_osc1l_phase = 0.0; + m_osc1r_phase = 0.0; + m_osc2l_phase = 0.0; + m_osc2r_phase = 0.0; + m_osc3l_phase = 0.0; + m_osc3r_phase = 0.0; + + m_env1_phase = 0.0; + m_env2_phase = 0.0; + + m_lfo1_phase = 0.0; + m_lfo2_phase = 0.0; + + m_osc1l_last = 0.0; + m_osc1r_last = 0.0; +} + + +MonstroSynth::~MonstroSynth() +{ + delete[] m_env1_buf; + delete[] m_env2_buf; + delete[] m_lfo1_buf; + delete[] m_lfo2_buf; + +} + + +void MonstroSynth::renderOutput( fpp_t _frames, sampleFrame * _buf ) +{ +// macros for modulating with env/lfos +#define modulatefreq( car, mod ) \ + if( mod##_e1 != 0.0 ) car = qBound( MIN_FREQ, car * static_cast( fastPow( 2.0, m_env1_buf[f] * mod##_e1 * 2 ) ), MAX_FREQ ); \ + if( mod##_e2 != 0.0 ) car = qBound( MIN_FREQ, car * static_cast( fastPow( 2.0, m_env2_buf[f] * mod##_e2 * 2 ) ), MAX_FREQ ); \ + if( mod##_l1 != 0.0 ) car = qBound( MIN_FREQ, car * static_cast( fastPow( 2.0, m_lfo1_buf[f] * mod##_l1 ) ), MAX_FREQ ); \ + if( mod##_l2 != 0.0 ) car = qBound( MIN_FREQ, car * static_cast( fastPow( 2.0, m_lfo2_buf[f] * mod##_l2 ) ), MAX_FREQ ); + +#define modulateabs( car, mod ) \ + if( mod##_e1 != 0.0 ) car = qBound( 0.0f, car + mod##_e1 * m_env1_buf[f], 1.0f ); \ + if( mod##_e2 != 0.0 ) car = qBound( 0.0f, car + mod##_e2 * m_env2_buf[f], 1.0f ); \ + if( mod##_l1 != 0.0 ) car = qBound( 0.0f, car + mod##_l1 / 2 * m_lfo1_buf[f], 1.0f ); \ + if( mod##_l2 != 0.0 ) car = qBound( 0.0f, car + mod##_l2 / 2 * m_lfo2_buf[f], 1.0f ); + +#define modulatephs( car, mod ) \ + if( mod##_e1 != 0.0 ) car = fraction( car + mod##_e1 * m_env1_buf[f] ); \ + if( mod##_e2 != 0.0 ) car = fraction( car + mod##_e2 * m_env2_buf[f] ); \ + if( mod##_l1 != 0.0 ) car = fraction( car + mod##_l1 / 2 * m_lfo1_buf[f] ); \ + if( mod##_l2 != 0.0 ) car = fraction( car + mod##_l2 / 2 * m_lfo2_buf[f] ); + +#define modulatevol( car, mod ) \ + if( mod##_e1 > 0.0 ) car = qBound( 0.0f, car * ( 1.0f - mod##_e1 + mod##_e1 * m_env1_buf[f] ), MODCLIP ); \ + if( mod##_e1 < 0.0 ) car = qBound( 0.0f, car * ( 1.0f + mod##_e1 * m_env1_buf[f] ), MODCLIP ); \ + if( mod##_e2 > 0.0 ) car = qBound( 0.0f, car * ( 1.0f - mod##_e2 + mod##_e2 * m_env2_buf[f] ), MODCLIP ); \ + if( mod##_e2 < 0.0 ) car = qBound( 0.0f, car * ( 1.0f + mod##_e2 * m_env2_buf[f] ), MODCLIP ); \ + if( mod##_l1 != 0.0 ) car = qBound( 0.0f, car * ( 1.0f + mod##_l1 * m_lfo1_buf[f] ), MODCLIP ); \ + if( mod##_l2 != 0.0 ) car = qBound( 0.0f, car * ( 1.0f + mod##_l2 * m_lfo2_buf[f] ), MODCLIP ); + + // pre-render env's and lfo's + renderModulators( _frames ); + + // get updated osc1 values + // get pulse width + const float pw = ( m_parent->m_osc1Pw.value() / 100.0 ); + const float o1pw_e1 = ( m_parent->m_pw1env1.value() ); + const float o1pw_e2 = ( m_parent->m_pw1env2.value() ); + const float o1pw_l1 = ( m_parent->m_pw1lfo1.value() ); + const float o1pw_l2 = ( m_parent->m_pw1lfo2.value() ); + + // get phases + const float o1lpo = m_parent->m_osc1l_po; + const float o1rpo = m_parent->m_osc1r_po; + const float o1p_e1 = ( m_parent->m_phs1env1.value() ); + const float o1p_e2 = ( m_parent->m_phs1env2.value() ); + const float o1p_l1 = ( m_parent->m_phs1lfo1.value() ); + const float o1p_l2 = ( m_parent->m_phs1lfo2.value() ); + + // get pitch + const float o1lfb = ( m_parent->m_osc1l_freq * m_nph->frequency() ); + const float o1rfb = ( m_parent->m_osc1r_freq * m_nph->frequency() ); + const float o1f_e1 = ( m_parent->m_pit1env1.value() ); + const float o1f_e2 = ( m_parent->m_pit1env2.value() ); + const float o1f_l1 = ( m_parent->m_pit1lfo1.value() ); + const float o1f_l2 = ( m_parent->m_pit1lfo2.value() ); + + // get volumes + const float o1lv = m_parent->m_osc1l_vol; + const float o1rv = m_parent->m_osc1r_vol; + const float o1v_e1 = ( m_parent->m_vol1env1.value() ); + const float o1v_e2 = ( m_parent->m_vol1env2.value() ); + const float o1v_l1 = ( m_parent->m_vol1lfo1.value() ); + const float o1v_l2 = ( m_parent->m_vol1lfo2.value() ); + + // update osc2 + // get waveform + const int o2w = m_parent->m_osc2Wave.value(); + + // get phases + const float o2lpo = m_parent->m_osc2l_po; + const float o2rpo = m_parent->m_osc2r_po; + const float o2p_e1 = ( m_parent->m_phs2env1.value() ); + const float o2p_e2 = ( m_parent->m_phs2env2.value() ); + const float o2p_l1 = ( m_parent->m_phs2lfo1.value() ); + const float o2p_l2 = ( m_parent->m_phs2lfo2.value() ); + + // get pitch + const float o2lfb = ( m_parent->m_osc2l_freq * m_nph->frequency() ); + const float o2rfb = ( m_parent->m_osc2r_freq * m_nph->frequency() ); + const float o2f_e1 = ( m_parent->m_pit2env1.value() ); + const float o2f_e2 = ( m_parent->m_pit2env2.value() ); + const float o2f_l1 = ( m_parent->m_pit2lfo1.value() ); + const float o2f_l2 = ( m_parent->m_pit2lfo2.value() ); + + // get volumes + const float o2lv = m_parent->m_osc2l_vol; + const float o2rv = m_parent->m_osc2r_vol; + const float o2v_e1 = ( m_parent->m_vol2env1.value() ); + const float o2v_e2 = ( m_parent->m_vol2env2.value() ); + const float o2v_l1 = ( m_parent->m_vol2lfo2.value() ); + const float o2v_l2 = ( m_parent->m_vol2lfo2.value() ); + + + + // update osc3 + // get waveforms + const int o3w1 = m_parent->m_osc3Wave1.value(); + const int o3w2 = m_parent->m_osc3Wave2.value(); + + // get phases + const float o3lpo = m_parent->m_osc3l_po; + const float o3rpo = m_parent->m_osc3r_po; + const float o3p_e1 = ( m_parent->m_phs3env1.value() ); + const float o3p_e2 = ( m_parent->m_phs3env2.value() ); + const float o3p_l1 = ( m_parent->m_phs3lfo1.value() ); + const float o3p_l2 = ( m_parent->m_phs3lfo2.value() ); + + // get pitch modulators + const float o3fb = ( m_parent->m_osc3_freq * m_nph->frequency() ); + const float o3f_e1 = ( m_parent->m_pit3env1.value() ); + const float o3f_e2 = ( m_parent->m_pit3env2.value() ); + const float o3f_l1 = ( m_parent->m_pit3lfo1.value() ); + const float o3f_l2 = ( m_parent->m_pit3lfo2.value() ); + + // get volumes + const float o3lv = m_parent->m_osc3l_vol; + const float o3rv = m_parent->m_osc3r_vol; + const float o3v_e1 = ( m_parent->m_vol3env1.value() ); + const float o3v_e2 = ( m_parent->m_vol3env2.value() ); + const float o3v_l1 = ( m_parent->m_vol3lfo1.value() ); + const float o3v_l2 = ( m_parent->m_vol3lfo2.value() ); + + // get sub + const float o3sub = ( m_parent->m_osc3Sub.value() + 100.0 ) / 200.0; + const float o3s_e1 = ( m_parent->m_sub3env1.value() ); + const float o3s_e2 = ( m_parent->m_sub3env2.value() ); + const float o3s_l1 = ( m_parent->m_sub3lfo1.value() ); + const float o3s_l2 = ( m_parent->m_sub3lfo2.value() ); + + + //o2-o3 modulation + + const int omod = m_parent->m_o23Mod.value(); + + // sync information + + const bool o1ssr = m_parent->m_osc1SSR.value(); + const bool o1ssf = m_parent->m_osc1SSF.value(); + const bool o2sync = m_parent->m_osc2Sync.value(); + const bool o3sync = m_parent->m_osc3Sync.value(); + + /////////////////////////// + // // + // start buffer loop // + // // + /////////////////////////// + + + + for( f_cnt_t f = 0; f < _frames; f++ ) + { + +/* + // debug code + if( f % 10 == 0 ) { + qDebug( "env1 %f -- env1 phase %f", m_env1_buf[f], m_env1_phase ); + qDebug( "env1 pre %f att %f dec %f rel %f ", m_parent->m_env1_pre, m_parent->m_env1_att, + m_parent->m_env1_dec, m_parent->m_env1_rel ); + } +*/ + + ///////////////////////////// + // // + // OSC 1 // + // // + ///////////////////////////// + + // sync send "signals" + bool syncl = false; + bool syncr = false; + + // calc and mod frequencies + float o1l_f = o1lfb; + float o1r_f = o1rfb; + modulatefreq( o1l_f, o1f ) + modulatefreq( o1r_f, o1f ) + + // calc and modulate phase + float o1l_p = m_osc1l_phase + o1lpo; + float o1r_p = m_osc1r_phase + o1rpo; + modulatephs( o1l_p, o1p ) + modulatephs( o1r_p, o1p ) + + // calc and modulate pulse + float o1_pw = pw; + modulateabs( o1_pw, o1pw ) + + // bounds check for phase + if( o1l_p < 0 ) o1l_p += 1.0f; + if( o1r_p < 0 ) o1r_p += 1.0f; + + // pulse wave osc + sample_t O1L = ( o1l_p < o1_pw ) ? 1.0f : -1.0f; + sample_t O1R = ( o1r_p < o1_pw ) ? 1.0f : -1.0f; + + // check for rise/fall, and "send" sync if appropriate + // sync on rise + if( o1ssr ) + { + if( O1L > m_osc1l_last ) syncl = true; + if( O1R > m_osc1r_last ) syncr = true; + } + if( o1ssf ) + { + if( O1L < m_osc1l_last ) syncl = true; + if( O1R < m_osc1r_last ) syncr = true; + } + + // update last before signal is touched + m_osc1l_last = O1L; + m_osc1r_last = O1R; + + // modulate volume + O1L *= o1lv; + O1R *= o1rv; + modulatevol( O1L, o1v ) + modulatevol( O1R, o1v ) + + // update osc1 phases + m_osc1l_phase = fraction( m_osc1l_phase + 1.0f / ( static_cast( m_samplerate ) / o1l_f ) ); + m_osc1r_phase = fraction( m_osc1r_phase + 1.0f / ( static_cast( m_samplerate ) / o1r_f ) ); + + ///////////////////////////// + // // + // OSC 2 // + // // + ///////////////////////////// + + // calc and mod frequencies + float o2l_f = o2lfb; + float o2r_f = o2rfb; + modulatefreq( o2l_f, o2f ) + modulatefreq( o2r_f, o2f ) + + // check for sync + if( o2sync ) + { + if( syncl ) m_osc2l_phase = 0.0f; + if( syncr ) m_osc2r_phase = 0.0f; + } + + // calc and modulate phase + float o2l_p = m_osc2l_phase + o2lpo; + float o2r_p = m_osc2r_phase + o2rpo; + modulatephs( o2l_p, o2p ) + modulatephs( o2r_p, o2p ) + + // bounds check for phase + if( o2l_p < 0 ) o2l_p += 1.0f; + if( o2r_p < 0 ) o2r_p += 1.0f; + + // multi-wave DC Oscillator + sample_t O2L = oscillate( o2w, o2l_p ); + sample_t O2R = oscillate( o2w, o2r_p ); + + // modulate volume + O2L *= o2lv; + O2R *= o2rv; + modulatevol( O2L, o2v ) + modulatevol( O2R, o2v ) + + // update osc2 phases + m_osc2l_phase = fraction( m_osc2l_phase + 1.0f / ( static_cast( m_samplerate ) / o2l_f ) ); + m_osc2r_phase = fraction( m_osc2r_phase + 1.0f / ( static_cast( m_samplerate ) / o2r_f ) ); + + ///////////////////////////// + // // + // OSC 3 // + // // + ///////////////////////////// + + // calc and mod frequencies + float o3l_f = o3fb; + float o3r_f = o3fb; + modulatefreq( o3l_f, o3f ) + modulatefreq( o3r_f, o3f ) + // o2 modulation? + if( omod == MOD_FM ) + { + o3l_f = qBound( MIN_FREQ, o3l_f * powf( 4.0f, O2L ), MAX_FREQ ); + o3r_f = qBound( MIN_FREQ, o3r_f * powf( 4.0f, O2R ), MAX_FREQ ); + } + + // check for sync + if( o3sync ) + { + if( syncl ) m_osc3l_phase = 0.0f; + if( syncr ) m_osc3r_phase = 0.0f; + } + + // calc and modulate phase + float o3l_p = m_osc3l_phase + o3lpo; + float o3r_p = m_osc3r_phase + o3rpo; + modulatephs( o3l_p, o3p ) + modulatephs( o3r_p, o3p ) + // o2 modulation? + if( omod == MOD_PM ) + { + o3l_p = fraction( o3l_p + O2L/2 ); + o3r_p = fraction( o3r_p + O2R/2 ); + } + + // bounds check for phase + if( o3l_p < 0 ) o3l_p += 1.0f; + if( o3r_p < 0 ) o3r_p += 1.0f; + + // multi-wave DC Oscillator, sub-osc 1 + sample_t O3AL = oscillate( o3w1, o3l_p ); + sample_t O3AR = oscillate( o3w1, o3r_p ); + + // multi-wave DC Oscillator, sub-osc 2 + sample_t O3BL = oscillate( o3w2, o3l_p ); + sample_t O3BR = oscillate( o3w2, o3r_p ); + + // calc and modulate sub + float sub = o3sub; + modulateabs( sub, o3s ) + + sample_t O3L = interpolate( O3AL, O3BL, sub ); + sample_t O3R = interpolate( O3AR, O3BR, sub ); + + // modulate volume + O3L *= o3lv; + O3R *= o3rv; + modulatevol( O3L, o3v ) + modulatevol( O3R, o3v ) + // o2 modulation? + if( omod == MOD_AM ) + { + O3L = qBound( -MODCLIP, O3L * qMax( 0.0f, 1.0f + O2L ), MODCLIP ); + O3R = qBound( -MODCLIP, O3R * qMax( 0.0f, 1.0f + O2R ), MODCLIP ); + } + + // update osc3 phases + m_osc3l_phase = fraction( m_osc3l_phase + 1.0f / ( static_cast( m_samplerate ) / o3l_f ) ); + m_osc3r_phase = fraction( m_osc3r_phase + 1.0f / ( static_cast( m_samplerate ) / o3r_f ) ); + + _buf[f][0] = O1L + O3L + ( omod == MOD_MIX ? O2L : 0.0f ); + _buf[f][1] = O1R + O3R + ( omod == MOD_MIX ? O2R : 0.0f ); + } +} + + +void MonstroSynth::renderModulators( fpp_t _frames ) +{ + // LFO phase offsets + const float lfo1_po = m_parent->m_lfo1Phs.value() / 360.0f; + const float lfo2_po = m_parent->m_lfo2Phs.value() / 360.0f; + + // remove cruft from phase counters to prevent overflow + m_lfo1_phase = fraction( m_lfo1_phase ); + m_lfo2_phase = fraction( m_lfo2_phase ); + + // LFO rates + const float lfo1_r = m_parent->m_lfo1Rate.value() / 1000.0f * m_samplerate; + const float lfo2_r = m_parent->m_lfo2Rate.value() / 1000.0f * m_samplerate; + + // frames played before + const f_cnt_t tfp = m_nph->totalFramesPlayed(); + + // LFOs + sample_t lfo1_s; + sample_t lfo2_s; + + // LFO 1 + + switch( m_parent->m_lfo1Wave.value() ) + { + case WAVE_SINE: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::sinSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent-> m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + case WAVE_TRI: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::triangleSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + case WAVE_SAW: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::sawSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + case WAVE_RAMP: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::sawSample( ph ) * -1.0f; + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + case WAVE_SQR: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::squareSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + case WAVE_SQRSOFT: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = oscillate( WAVE_SQRSOFT, ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + case WAVE_MOOG: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::moogSawSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + case WAVE_SINABS: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = oscillate( WAVE_SINABS, ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + case WAVE_EXP: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo1_phase + lfo1_po; + lfo1_s = Oscillator::expSample( ph ); + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent-> m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + case WAVE_NOISE: + default: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + if( t % static_cast( lfo1_r ) == 0 ) m_lfo1_last = Oscillator::noiseSample( 0.0f ); + lfo1_s = m_lfo1_last; + if( t < m_parent->m_lfo1_att ) lfo1_s *= ( static_cast( t ) / m_parent->m_lfo1_att ); + m_lfo1_buf[f] = lfo1_s; + m_lfo1_phase += 1.0 / lfo1_r; + } + break; + } + + // LFO 2 + + switch( m_parent->m_lfo2Wave.value() ) + { + case WAVE_SINE: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::sinSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent-> m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + case WAVE_TRI: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::triangleSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + case WAVE_SAW: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::sawSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + case WAVE_RAMP: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::sawSample( ph ) * -1.0f; + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + case WAVE_SQR: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::squareSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + case WAVE_SQRSOFT: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = oscillate( WAVE_SQRSOFT, ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + case WAVE_MOOG: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::moogSawSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + case WAVE_SINABS: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = oscillate( WAVE_SINABS, ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + case WAVE_EXP: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + const float ph = m_lfo2_phase + lfo2_po; + lfo2_s = Oscillator::expSample( ph ); + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent-> m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + case WAVE_NOISE: + default: + for( f_cnt_t f = 0; f < _frames; f++ ) + { + const f_cnt_t t = f + tfp; + if( t % static_cast( lfo2_r ) == 0 ) m_lfo2_last = Oscillator::noiseSample( 0.0f ); + lfo2_s = m_lfo2_last; + if( t < m_parent->m_lfo2_att ) lfo2_s *= ( static_cast( t ) / m_parent->m_lfo2_att ); + m_lfo2_buf[f] = lfo2_s; + m_lfo2_phase += 1.0 / lfo2_r; + } + break; + } + + ///////////////////////////////////////////// + // + // + // envelopes + // + // + ///////////////////////////////////////////// + + const float env1_s = m_parent-> m_env1Slope.value(); + const float env2_s = m_parent-> m_env2Slope.value(); + + const float env1_sus = m_parent-> m_env1Sus.value(); + const float env2_sus = m_parent-> m_env2Sus.value(); + + for( f_cnt_t f = 0; f < _frames; f++ ) + { + // envelope 1 + + // adjust phase for release + if( m_nph->isReleased() && m_env1_phase < 4.0f ) + { + if( m_env1_phase < 1.0f ) m_env1_phase = 5.0f; + else if( m_env1_phase < 2.0f ) m_env1_phase = 5.0f - fraction( m_env1_phase ); + else if( m_env1_phase < 3.0f ) m_env1_phase = 4.0f; + else m_env1_phase = 4.0f + fraction( m_env1_phase ); + } + + // process envelope + if( m_env1_phase < 1.0f ) // pre-delay phase + { + m_env1_buf[f] = 0.0f; + m_env1_phase = qMin( 1.0f, m_env1_phase + m_parent->m_env1_pre ); + } + else if( m_env1_phase < 2.0f ) // attack phase + { + m_env1_buf[f] = calcSlope( fraction( m_env1_phase ), env1_s ); + m_env1_phase = qMin( 2.0f, m_env1_phase + m_parent->m_env1_att ); + } + else if( m_env1_phase < 3.0f ) // hold phase + { + m_env1_buf[f] = 1.0f; + m_env1_phase = qMin( 3.0f, m_env1_phase + m_parent->m_env1_hold ); + } + else if( m_env1_phase < 4.0f ) // decay phase + { + const sample_t s = 1.0f - fraction( m_env1_phase ); + if( s <= env1_sus ) + { + m_env1_buf[f] = env1_sus; + } + else + { + m_env1_buf[f] = calcSlope( s, env1_s ); + m_env1_phase = qMin( 4.0f - env1_sus, m_env1_phase + m_parent->m_env1_dec ); + if( m_env1_phase == 4.0f ) m_env1_phase = 5.0f; // jump over release if sustain is zero - fix for clicking + } + } + else if( m_env1_phase < 5.0f ) // release phase + { + m_env1_buf[f] = calcSlope( 1.0f - fraction( m_env1_phase ), env1_s ); + m_env1_phase += m_parent->m_env1_rel; + } + else m_env1_buf[f] = 0.0f; + +// qDebug( "env1 %f", m_env1_buf[f] ); + + // envelope 2 + + + + // adjust phase for release + if( m_nph->isReleased() && m_env2_phase < 4.0f ) + { + if( m_env2_phase < 1.0f ) m_env2_phase = 5.0f; + else if( m_env2_phase < 2.0f ) m_env2_phase = 5.0f - fraction( m_env2_phase ); + else if( m_env2_phase < 3.0f ) m_env2_phase = 4.0f; + else m_env2_phase = 4.0f + fraction( m_env2_phase ); + } + + // process envelope + if( m_env2_phase < 1.0f ) // pre-delay phase + { + m_env2_buf[f] = 0.0f; + m_env2_phase = qMin( 1.0f, m_env2_phase + m_parent->m_env2_pre ); + } + else if( m_env2_phase < 2.0f ) // attack phase + { + m_env2_buf[f] = calcSlope( fraction( m_env2_phase ), env2_s ); + m_env2_phase = qMin( 2.0f, m_env2_phase + m_parent->m_env2_att ); + } + else if( m_env2_phase < 3.0f ) // hold phase + { + m_env2_buf[f] = 1.0f; + m_env2_phase = qMin( 3.0f, m_env2_phase + m_parent->m_env2_hold ); + } + else if( m_env2_phase < 4.0f ) // decay phase + { + const sample_t s = 1.0f - fraction( m_env2_phase ); + if( s <= env2_sus ) + { + m_env2_buf[f] = env2_sus; + } + else + { + m_env2_buf[f] = calcSlope( s, env2_s ); + m_env2_phase = qMin( 4.0f - env2_sus, m_env2_phase + m_parent->m_env2_dec ); + if( m_env1_phase == 4.0f ) m_env1_phase = 5.0f; // jump over release if sustain is zero - fix for clicking + } + } + else if( m_env2_phase < 5.0f ) // release phase + { + m_env2_buf[f] = calcSlope( 1.0f - fraction( m_env2_phase), env2_s ); + m_env2_phase += m_parent->m_env2_rel; + } + else m_env2_buf[f] = 0.0f; + + } + +} + + + +MonstroInstrument::MonstroInstrument( InstrumentTrack * _instrument_track ) : + Instrument( _instrument_track, &monstro_plugin_descriptor ), + + m_osc1Vol( 33.0, 0.0, 200.0, 0.1, this, tr( "Osc 1 Volume" ) ), + m_osc1Pan( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 1 Panning" ) ), + m_osc1Crs( 0.0, -24.0, 24.0, 1.0, this, tr( "Osc 1 Coarse detune" ) ), + m_osc1Ftl( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 1 Fine detune left" ) ), + m_osc1Ftr( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 1 Fine detune right" ) ), + m_osc1Spo( 0.0, -180.0, 180.0, 0.1, this, tr( "Osc 1 Stereo phase offset" ) ), + m_osc1Pw( 50.0, 0.0, 100.0, 0.01, this, tr( "Osc 1 Pulse width" ) ), + m_osc1SSR( false, this, tr( "Osc 1 Sync send on rise" ) ), + m_osc1SSF( false, this, tr( "Osc 1 Sync send on fall" ) ), + + m_osc2Vol( 33.0, 0.0, 200.0, 0.1, this, tr( "Osc 2 Volume" ) ), + m_osc2Pan( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 2 Panning" ) ), + m_osc2Crs( 0.0, -24.0, 24.0, 1.0, this, tr( "Osc 2 Coarse detune" ) ), + m_osc2Ftl( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 2 Fine detune left" ) ), + m_osc2Ftr( 0.0, -100.0, 100.0, 1.0, this, tr( "Osc 2 Fine detune right" ) ), + m_osc2Spo( 0.0, -180.0, 180.0, 0.1, this, tr( "Osc 2 Stereo phase offset" ) ), + m_osc2Wave( this, tr( "Osc 2 Waveform" ) ), + m_osc2Sync( false, this, tr( "Osc 2 Sync" ) ), + + m_osc3Vol( 33.0, 0.0, 200.0, 0.1, this, tr( "Osc 3 Volume" ) ), + m_osc3Pan( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 3 Panning" ) ), + m_osc3Crs( 0.0, -24.0, 24.0, 1.0, this, tr( "Osc 3 Coarse detune" ) ), + m_osc3Spo( 0.0, -180.0, 180.0, 0.1, this, tr( "Osc 3 Stereo phase offset" ) ), + m_osc3Sub( 0.0, -100.0, 100.0, 0.1, this, tr( "Osc 3 Sub-oscillator mix" ) ), + m_osc3Wave1( this, tr( "Osc 3 Waveform 1" ) ), + m_osc3Wave2( this, tr( "Osc 3 Waveform 2" ) ), + m_osc3Sync( false, this, tr( "Osc 3 Sync" ) ), + + m_lfo1Wave( this, tr( "LFO 1 Waveform" ) ), + m_lfo1Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "LFO 1 Attack" ) ), + m_lfo1Rate( 1.0f, 0.1, 10000.0, 0.1, 10000.0f, this, tr( "LFO 1 Rate" ) ), + m_lfo1Phs( 0.0, -180.0, 180.0, 0.1, this, tr( "LFO 1 Phase" ) ), + + m_lfo2Wave( this, tr( "LFO 2 Waveform" ) ), + m_lfo2Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "LFO 2 Attack" ) ), + m_lfo2Rate( 1.0f, 0.1, 10000.0, 0.1, 10000.0f, this, tr( "LFO 2 Rate" ) ), + m_lfo2Phs( 0.0, -180.0, 180.0, 0.1, this, tr( "LFO 2 Phase" ) ), + + m_env1Pre( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 1 Pre-delay" ) ), + m_env1Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 1 Attack" ) ), + m_env1Hold( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 1 Hold" ) ), + m_env1Dec( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 1 Decay" ) ), + m_env1Sus( 1.0f, 0.0f, 1.0f, 0.001f, this, tr( "Env 1 Sustain" ) ), + m_env1Rel( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 1 Release" ) ), + m_env1Slope( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Env 1 Slope" ) ), + + m_env2Pre( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 2 Pre-delay" ) ), + m_env2Att( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 2 Attack" ) ), + m_env2Hold( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 2 Hold" ) ), + m_env2Dec( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 2 Decay" ) ), + m_env2Sus( 1.0f, 0.0f, 1.0f, 0.001f, this, tr( "Env 2 Sustain" ) ), + m_env2Rel( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "Env 2 Release" ) ), + m_env2Slope( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Env 2 Slope" ) ), + + m_o23Mod( 0, 0, NUM_MODS - 1, this, tr( "Osc2-3 modulation" ) ), + + m_selectedView( 0, 0, 1, this, tr( "Selected view" ) ), + + m_vol1env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol1-Env1" ) ), + m_vol1env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol1-Env2" ) ), + m_vol1lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol1-LFO1" ) ), + m_vol1lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol1-LFO2" ) ), + + m_vol2env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol2-Env1" ) ), + m_vol2env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol2-Env2" ) ), + m_vol2lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol2-LFO1" ) ), + m_vol2lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol2-LFO2" ) ), + + m_vol3env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol3-Env1" ) ), + m_vol3env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol3-Env2" ) ), + m_vol3lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol3-LFO1" ) ), + m_vol3lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Vol3-LFO2" ) ), + + m_phs1env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs1-Env1" ) ), + m_phs1env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs1-Env2" ) ), + m_phs1lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs1-LFO1" ) ), + m_phs1lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs1-LFO2" ) ), + + m_phs2env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs2-Env1" ) ), + m_phs2env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs2-Env2" ) ), + m_phs2lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs2-LFO1" ) ), + m_phs2lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs2-LFO2" ) ), + + m_phs3env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs3-Env1" ) ), + m_phs3env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs3-Env2" ) ), + m_phs3lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs3-LFO1" ) ), + m_phs3lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Phs3-LFO2" ) ), + + m_pit1env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit1-Env1" ) ), + m_pit1env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit1-Env2" ) ), + m_pit1lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit1-LFO1" ) ), + m_pit1lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit1-LFO2" ) ), + + m_pit2env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit2-Env1" ) ), + m_pit2env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit2-Env2" ) ), + m_pit2lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit2-LFO1" ) ), + m_pit2lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit2-LFO2" ) ), + + m_pit3env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit3-Env1" ) ), + m_pit3env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit3-Env2" ) ), + m_pit3lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit3-LFO1" ) ), + m_pit3lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Pit3-LFO2" ) ), + + m_pw1env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "PW1-Env1" ) ), + m_pw1env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "PW1-Env2" ) ), + m_pw1lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "PW1-LFO1" ) ), + m_pw1lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "PW1-LFO2" ) ), + + m_sub3env1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Sub3-Env1" ) ), + m_sub3env2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Sub3-Env2" ) ), + m_sub3lfo1( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Sub3-LFO1" ) ), + m_sub3lfo2( 0.0f, -1.0f, 1.0f, 0.001f, this, tr( "Sub3-LFO2" ) ) + +{ +// setup waveboxes + setwavemodel( m_osc2Wave ) + setwavemodel( m_osc3Wave1 ) + setwavemodel( m_osc3Wave2 ) + setlfowavemodel( m_lfo1Wave ) + setlfowavemodel( m_lfo2Wave ) + +// make connections: + +// updateVolumes + + connect( &m_osc1Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc1Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc2Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc2Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc3Vol, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + connect( &m_osc3Pan, SIGNAL( dataChanged() ), this, SLOT( updateVolumes() ) ); + +// updateFreq + + connect( &m_osc1Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + connect( &m_osc2Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + connect( &m_osc3Crs, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + + connect( &m_osc1Ftl, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + connect( &m_osc2Ftl, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + + connect( &m_osc1Ftr, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + connect( &m_osc2Ftr, SIGNAL( dataChanged() ), this, SLOT( updateFreq() ) ); + +// updatePO + connect( &m_osc1Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO() ) ); + connect( &m_osc2Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO() ) ); + connect( &m_osc3Spo, SIGNAL( dataChanged() ), this, SLOT( updatePO() ) ); + +// updateEnvelope1 + + connect( &m_env1Pre, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + connect( &m_env1Att, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + connect( &m_env1Hold, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + connect( &m_env1Dec, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + connect( &m_env1Rel, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope1() ) ); + +// updateEnvelope2 + + connect( &m_env2Pre, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + connect( &m_env2Att, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + connect( &m_env2Hold, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + connect( &m_env2Dec, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + connect( &m_env2Rel, SIGNAL( dataChanged() ), this, SLOT( updateEnvelope2() ) ); + +// updateLFOAtts + + connect( &m_lfo1Att, SIGNAL( dataChanged() ), this, SLOT( updateLFOAtts() ) ); + connect( &m_lfo2Att, SIGNAL( dataChanged() ), this, SLOT( updateLFOAtts() ) ); + +// updateSampleRate + + connect( engine::mixer(), SIGNAL( sampleRateChanged() ), this, SLOT( updateSamplerate() ) ); + + m_fpp = engine::mixer()->framesPerPeriod(); + + updateSamplerate(); + updateVolumes(); + updateFreq(); + updatePO(); +} + + +MonstroInstrument::~MonstroInstrument() +{ +} + + +void MonstroInstrument::playNote( NotePlayHandle * _n, + sampleFrame * _working_buffer ) +{ + if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL ) + { + const sample_rate_t samplerate = m_samplerate; + _n->m_pluginData = new MonstroSynth( this, _n, samplerate, m_fpp ); + } + + const fpp_t frames = _n->framesLeftForCurrentPeriod(); + + MonstroSynth * ms = static_cast( _n->m_pluginData ); + + ms->renderOutput( frames, _working_buffer ); + + applyRelease( _working_buffer, _n ); + + instrumentTrack()->processAudioBuffer( _working_buffer, frames, _n ); +} + +void MonstroInstrument::deleteNotePluginData( NotePlayHandle * _n ) +{ + delete static_cast( _n->m_pluginData ); +} + + +void MonstroInstrument::saveSettings( QDomDocument & _doc, + QDomElement & _this ) +{ + m_osc1Vol.saveSettings( _doc, _this, "o1vol" ); + m_osc1Pan.saveSettings( _doc, _this, "o1pan" ); + m_osc1Crs.saveSettings( _doc, _this, "o1crs" ); + m_osc1Ftl.saveSettings( _doc, _this, "o1ftl" ); + m_osc1Ftr.saveSettings( _doc, _this, "o1ftr" ); + m_osc1Spo.saveSettings( _doc, _this, "o1spo" ); + m_osc1Pw.saveSettings( _doc, _this, "o1pw" ); + m_osc1SSR.saveSettings( _doc, _this, "o1ssr" ); + m_osc1SSF.saveSettings( _doc, _this, "o1ssf" ); + + m_osc2Vol.saveSettings( _doc, _this, "o2vol" ); + m_osc2Pan.saveSettings( _doc, _this, "o2pan" ); + m_osc2Crs.saveSettings( _doc, _this, "o2crs" ); + m_osc2Ftl.saveSettings( _doc, _this, "o2ftl" ); + m_osc2Ftr.saveSettings( _doc, _this, "o2ftr" ); + m_osc2Spo.saveSettings( _doc, _this, "o2spo" ); + m_osc2Wave.saveSettings( _doc, _this, "o2wav" ); + m_osc2Sync.saveSettings( _doc, _this, "o2syn" ); + + m_osc3Vol.saveSettings( _doc, _this, "o3vol" ); + m_osc3Pan.saveSettings( _doc, _this, "o3pan" ); + m_osc3Crs.saveSettings( _doc, _this, "o3crs" ); + m_osc3Spo.saveSettings( _doc, _this, "o3spo" ); + m_osc3Sub.saveSettings( _doc, _this, "o3sub" ); + m_osc3Wave1.saveSettings( _doc, _this, "o3wav1" ); + m_osc3Wave2.saveSettings( _doc, _this, "o3wav2" ); + m_osc3Sync.saveSettings( _doc, _this, "o3syn" ); + + m_lfo1Wave.saveSettings( _doc, _this, "l1wav" ); + m_lfo1Att.saveSettings( _doc, _this, "l1att" ); + m_lfo1Rate.saveSettings( _doc, _this, "l1rat" ); + m_lfo1Phs.saveSettings( _doc, _this, "l1phs" ); + + m_lfo2Wave.saveSettings( _doc, _this, "l2wav" ); + m_lfo2Att.saveSettings( _doc, _this, "l2att" ); + m_lfo2Rate.saveSettings( _doc, _this, "l2rat" ); + m_lfo2Phs.saveSettings( _doc, _this, "l2phs" ); + + m_env1Pre.saveSettings( _doc, _this, "e1pre" ); + m_env1Att.saveSettings( _doc, _this, "e1att" ); + m_env1Hold.saveSettings( _doc, _this, "e1hol" ); + m_env1Dec.saveSettings( _doc, _this, "e1dec" ); + m_env1Sus.saveSettings( _doc, _this, "e1sus" ); + m_env1Rel.saveSettings( _doc, _this, "e1rel" ); + m_env1Slope.saveSettings( _doc, _this, "e1slo" ); + + m_env2Pre.saveSettings( _doc, _this, "e2pre" ); + m_env2Att.saveSettings( _doc, _this, "e2att" ); + m_env2Hold.saveSettings( _doc, _this, "e2hol" ); + m_env2Dec.saveSettings( _doc, _this, "e2dec" ); + m_env2Sus.saveSettings( _doc, _this, "e2sus" ); + m_env2Rel.saveSettings( _doc, _this, "e2rel" ); + m_env2Slope.saveSettings( _doc, _this, "e2slo" ); + + m_o23Mod.saveSettings( _doc, _this, "o23mo" ); + + m_vol1env1.saveSettings( _doc, _this, "v1e1" ); + m_vol1env2.saveSettings( _doc, _this, "v1e2" ); + m_vol1lfo1.saveSettings( _doc, _this, "v1l1" ); + m_vol1lfo2.saveSettings( _doc, _this, "v1l2" ); + + m_vol2env1.saveSettings( _doc, _this, "v2e1" ); + m_vol2env2.saveSettings( _doc, _this, "v2e2" ); + m_vol2lfo1.saveSettings( _doc, _this, "v2l1" ); + m_vol2lfo2.saveSettings( _doc, _this, "v2l2" ); + + m_vol3env1.saveSettings( _doc, _this, "v3e1" ); + m_vol3env2.saveSettings( _doc, _this, "v3e2" ); + m_vol3lfo1.saveSettings( _doc, _this, "v3l1" ); + m_vol3lfo2.saveSettings( _doc, _this, "v3l2" ); + + m_phs1env1.saveSettings( _doc, _this, "p1e1" ); + m_phs1env2.saveSettings( _doc, _this, "p1e2" ); + m_phs1lfo1.saveSettings( _doc, _this, "p1l1" ); + m_phs1lfo2.saveSettings( _doc, _this, "p1l2" ); + + m_phs2env1.saveSettings( _doc, _this, "p2e1" ); + m_phs2env2.saveSettings( _doc, _this, "p2e2" ); + m_phs2lfo1.saveSettings( _doc, _this, "p2l1" ); + m_phs2lfo2.saveSettings( _doc, _this, "p2l2" ); + + m_phs3env1.saveSettings( _doc, _this, "p3e1" ); + m_phs3env2.saveSettings( _doc, _this, "p3e2" ); + m_phs3lfo1.saveSettings( _doc, _this, "p3l1" ); + m_phs3lfo2.saveSettings( _doc, _this, "p3l2" ); + + m_pit1env1.saveSettings( _doc, _this, "f1e1" ); + m_pit1env2.saveSettings( _doc, _this, "f1e2" ); + m_pit1lfo1.saveSettings( _doc, _this, "f1l1" ); + m_pit1lfo2.saveSettings( _doc, _this, "f1l2" ); + + m_pit2env1.saveSettings( _doc, _this, "f2e1" ); + m_pit2env2.saveSettings( _doc, _this, "f2e2" ); + m_pit2lfo1.saveSettings( _doc, _this, "f2l1" ); + m_pit2lfo2.saveSettings( _doc, _this, "f2l2" ); + + m_pit3env1.saveSettings( _doc, _this, "f3e1" ); + m_pit3env2.saveSettings( _doc, _this, "f3e2" ); + m_pit3lfo1.saveSettings( _doc, _this, "f3l1" ); + m_pit3lfo2.saveSettings( _doc, _this, "f3l2" ); + + m_pw1env1.saveSettings( _doc, _this, "w1e1" ); + m_pw1env2.saveSettings( _doc, _this, "w1e2" ); + m_pw1lfo1.saveSettings( _doc, _this, "w1l1" ); + m_pw1lfo2.saveSettings( _doc, _this, "w1l2" ); + + m_sub3env1.saveSettings( _doc, _this, "s3e1" ); + m_sub3env2.saveSettings( _doc, _this, "s3e2" ); + m_sub3lfo1.saveSettings( _doc, _this, "s3l1" ); + m_sub3lfo2.saveSettings( _doc, _this, "s3l2" ); + +} + +void MonstroInstrument::loadSettings( const QDomElement & _this ) +{ + m_osc1Vol.loadSettings( _this, "o1vol" ); + m_osc1Pan.loadSettings( _this, "o1pan" ); + m_osc1Crs.loadSettings( _this, "o1crs" ); + m_osc1Ftl.loadSettings( _this, "o1ftl" ); + m_osc1Ftr.loadSettings( _this, "o1ftr" ); + m_osc1Spo.loadSettings( _this, "o1spo" ); + m_osc1Pw.loadSettings( _this, "o1pw" ); + m_osc1SSR.loadSettings( _this, "o1ssr" ); + m_osc1SSF.loadSettings( _this, "o1ssf" ); + + m_osc2Vol.loadSettings( _this, "o2vol" ); + m_osc2Pan.loadSettings( _this, "o2pan" ); + m_osc2Crs.loadSettings( _this, "o2crs" ); + m_osc2Ftl.loadSettings( _this, "o2ftl" ); + m_osc2Ftr.loadSettings( _this, "o2ftr" ); + m_osc2Spo.loadSettings( _this, "o2spo" ); + m_osc2Wave.loadSettings( _this, "o2wav" ); + m_osc2Sync.loadSettings( _this, "o2syn" ); + + m_osc3Vol.loadSettings( _this, "o3vol" ); + m_osc3Pan.loadSettings( _this, "o3pan" ); + m_osc3Crs.loadSettings( _this, "o3crs" ); + m_osc3Spo.loadSettings( _this, "o3spo" ); + m_osc3Sub.loadSettings( _this, "o3sub" ); + m_osc3Wave1.loadSettings( _this, "o3wav1" ); + m_osc3Wave2.loadSettings( _this, "o3wav2" ); + m_osc3Sync.loadSettings( _this, "o3syn" ); + + m_lfo1Wave.loadSettings( _this, "l1wav" ); + m_lfo1Att.loadSettings( _this, "l1att" ); + m_lfo1Rate.loadSettings( _this, "l1rat" ); + m_lfo1Phs.loadSettings( _this, "l1phs" ); + + m_lfo2Wave.loadSettings( _this, "l2wav" ); + m_lfo2Att.loadSettings( _this, "l2att" ); + m_lfo2Rate.loadSettings( _this, "l2rat" ); + m_lfo2Phs.loadSettings( _this, "l2phs" ); + + m_env1Pre.loadSettings( _this, "e1pre" ); + m_env1Att.loadSettings( _this, "e1att" ); + m_env1Hold.loadSettings( _this, "e1hol" ); + m_env1Dec.loadSettings( _this, "e1dec" ); + m_env1Sus.loadSettings( _this, "e1sus" ); + m_env1Rel.loadSettings( _this, "e1rel" ); + m_env1Slope.loadSettings( _this, "e1slo" ); + + m_env2Pre.loadSettings( _this, "e2pre" ); + m_env2Att.loadSettings( _this, "e2att" ); + m_env2Hold.loadSettings( _this, "e2hol" ); + m_env2Dec.loadSettings( _this, "e2dec" ); + m_env2Sus.loadSettings( _this, "e2sus" ); + m_env2Rel.loadSettings( _this, "e2rel" ); + m_env2Slope.loadSettings( _this, "e2slo" ); + + m_o23Mod.loadSettings( _this, "o23mo" ); + + m_vol1env1.loadSettings( _this, "v1e1" ); + m_vol1env2.loadSettings( _this, "v1e2" ); + m_vol1lfo1.loadSettings( _this, "v1l1" ); + m_vol1lfo2.loadSettings( _this, "v1l2" ); + + m_vol2env1.loadSettings( _this, "v2e1" ); + m_vol2env2.loadSettings( _this, "v2e2" ); + m_vol2lfo1.loadSettings( _this, "v2l1" ); + m_vol2lfo2.loadSettings( _this, "v2l2" ); + + m_vol3env1.loadSettings( _this, "v3e1" ); + m_vol3env2.loadSettings( _this, "v3e2" ); + m_vol3lfo1.loadSettings( _this, "v3l1" ); + m_vol3lfo2.loadSettings( _this, "v3l2" ); + + m_phs1env1.loadSettings( _this, "p1e1" ); + m_phs1env2.loadSettings( _this, "p1e2" ); + m_phs1lfo1.loadSettings( _this, "p1l1" ); + m_phs1lfo2.loadSettings( _this, "p1l2" ); + + m_phs2env1.loadSettings( _this, "p2e1" ); + m_phs2env2.loadSettings( _this, "p2e2" ); + m_phs2lfo1.loadSettings( _this, "p2l1" ); + m_phs2lfo2.loadSettings( _this, "p2l2" ); + + m_phs3env1.loadSettings( _this, "p3e1" ); + m_phs3env2.loadSettings( _this, "p3e2" ); + m_phs3lfo1.loadSettings( _this, "p3l1" ); + m_phs3lfo2.loadSettings( _this, "p3l2" ); + + m_pit1env1.loadSettings( _this, "f1e1" ); + m_pit1env2.loadSettings( _this, "f1e2" ); + m_pit1lfo1.loadSettings( _this, "f1l1" ); + m_pit1lfo2.loadSettings( _this, "f1l2" ); + + m_pit2env1.loadSettings( _this, "f2e1" ); + m_pit2env2.loadSettings( _this, "f2e2" ); + m_pit2lfo1.loadSettings( _this, "f2l1" ); + m_pit2lfo2.loadSettings( _this, "f2l2" ); + + m_pit3env1.loadSettings( _this, "f3e1" ); + m_pit3env2.loadSettings( _this, "f3e2" ); + m_pit3lfo1.loadSettings( _this, "f3l1" ); + m_pit3lfo2.loadSettings( _this, "f3l2" ); + + m_pw1env1.loadSettings( _this, "w1e1" ); + m_pw1env2.loadSettings( _this, "w1e2" ); + m_pw1lfo1.loadSettings( _this, "w1l1" ); + m_pw1lfo2.loadSettings( _this, "w1l2" ); + + m_sub3env1.loadSettings( _this, "s3e1" ); + m_sub3env2.loadSettings( _this, "s3e2" ); + m_sub3lfo1.loadSettings( _this, "s3l1" ); + m_sub3lfo2.loadSettings( _this, "s3l2" ); + +} + + +QString MonstroInstrument::nodeName() const +{ + return monstro_plugin_descriptor.name; +} + + +f_cnt_t MonstroInstrument::desiredReleaseFrames() const +{ + return 64; +} + + +PluginView * MonstroInstrument::instantiateView( QWidget * _parent ) +{ + return( new MonstroView( this, _parent ) ); +} + + +void MonstroInstrument::updateVolumes() +{ + m_osc1l_vol = leftCh( m_osc1Vol.value(), m_osc1Pan.value() ); + m_osc1r_vol = rightCh( m_osc1Vol.value(), m_osc1Pan.value() ); + + m_osc2l_vol = leftCh( m_osc2Vol.value(), m_osc2Pan.value() ); + m_osc2r_vol = rightCh( m_osc2Vol.value(), m_osc2Pan.value() ); + + m_osc3l_vol = leftCh( m_osc3Vol.value(), m_osc3Pan.value() ); + m_osc3r_vol = rightCh( m_osc3Vol.value(), m_osc3Pan.value() ); +} + + +void MonstroInstrument::updateFreq() +{ + m_osc1l_freq = powf( 2.0f, m_osc1Crs.value() / 12.0f ) * + powf( 2.0f, m_osc1Ftl.value() / 1200.0f ); + m_osc1r_freq = powf( 2.0f, m_osc1Crs.value() / 12.0f ) * + powf( 2.0f, m_osc1Ftr.value() / 1200.0f ); + + m_osc2l_freq = powf( 2.0f, m_osc2Crs.value() / 12.0f ) * + powf( 2.0f, m_osc2Ftl.value() / 1200.0f ); + m_osc2r_freq = powf( 2.0f, m_osc2Crs.value() / 12.0f ) * + powf( 2.0f, m_osc2Ftr.value() / 1200.0f ); + + m_osc3_freq = powf( 2.0f, m_osc3Crs.value() / 12.0f ); +} + + +void MonstroInstrument::updatePO() +{ + m_osc1l_po = m_osc1Spo.value() / 360.0; + m_osc1r_po = ( m_osc1Spo.value() * -1.0 ) / 360.0; + + m_osc2l_po = m_osc2Spo.value() / 360.0; + m_osc2r_po = ( m_osc2Spo.value() * -1.0 ) / 360.0; + + m_osc3l_po = m_osc3Spo.value() / 360.0; + m_osc3r_po = ( m_osc3Spo.value() * -1.0 ) / 360.0; +} + +void MonstroInstrument::updateEnvelope1() +{ + if( m_env1Pre.value() == 0.0f ) m_env1_pre = 1.0; + else m_env1_pre = 1.0 / ( m_env1Pre.value() / 1000.0f ) / m_samplerate; + if( m_env1Att.value() == 0.0f ) m_env1_att = 1.0; + else m_env1_att = 1.0 / ( m_env1Att.value() / 1000.0f ) / m_samplerate; + if( m_env1Hold.value() == 0.0f ) m_env1_hold = 1.0; + else m_env1_hold = 1.0 / ( m_env1Hold.value() / 1000.0f ) / m_samplerate; + if( m_env1Dec.value() == 0.0f ) m_env1_dec = 1.0; + else m_env1_dec = 1.0 / ( m_env1Dec.value() / 1000.0f ) / m_samplerate; + if( m_env1Rel.value() == 0.0f ) m_env1_rel = 1.0; + else m_env1_rel = 1.0 / ( m_env1Rel.value() / 1000.0f ) / m_samplerate; + + m_env1_len = ( m_env1Pre.value() + m_env1Att.value() + m_env1Hold.value() + m_env1Dec.value() ) * m_samplerate / 1000.0f; + m_env1_relF = m_env1Rel.value() * m_samplerate / 1000.0f; +} +void MonstroInstrument::updateEnvelope2() +{ + if( m_env2Pre.value() == 0.0f ) m_env2_pre = 1.0; + else m_env2_pre = 1.0 / ( m_env2Pre.value() / 1000.0f ) / m_samplerate; + if( m_env2Att.value() == 0.0f ) m_env2_att = 1.0; + else m_env2_att = 1.0 / ( m_env2Att.value() / 1000.0f ) / m_samplerate; + if( m_env2Hold.value() == 0.0f ) m_env2_hold = 1.0; + else m_env2_hold = 1.0 / ( m_env2Hold.value() / 1000.0f ) / m_samplerate; + if( m_env2Dec.value() == 0.0f ) m_env2_dec = 1.0; + else m_env2_dec = 1.0 / ( m_env2Dec.value() / 1000.0f ) / m_samplerate; + if( m_env2Rel.value() == 0.0f ) m_env2_rel = 1.0; + else m_env2_rel = 1.0 / ( m_env2Rel.value() / 1000.0f ) / m_samplerate; + + m_env2_len = ( m_env2Pre.value() + m_env2Att.value() + m_env2Hold.value() + m_env2Dec.value() ) * m_samplerate / 1000.0f; + m_env2_relF = m_env2Rel.value() * m_samplerate / 1000.0f; +} + + +void MonstroInstrument::updateLFOAtts() +{ + m_lfo1_att = m_lfo1Att.value() * m_samplerate / 1000.0f; + m_lfo2_att = m_lfo2Att.value() * m_samplerate / 1000.0f; +} + + +void MonstroInstrument::updateSamplerate() +{ + m_samplerate = engine::mixer()->processingSampleRate(); + updateEnvelope1(); + updateEnvelope2(); + updateLFOAtts(); +} + + +MonstroView::MonstroView( Instrument * _instrument, + QWidget * _parent ) : + InstrumentView( _instrument, _parent ) +{ + m_operatorsView = setupOperatorsView( this ); + setWidgetBackground( m_operatorsView, "artwork_op" ); + m_operatorsView->show(); + m_operatorsView->move( 0, 0 ); + + m_matrixView = setupMatrixView( this ); + setWidgetBackground( m_matrixView, "artwork_mat" ); + m_matrixView->hide(); + m_matrixView->move( 0, 0 ); + +// "tab buttons" + + pixmapButton * m_opViewButton = new pixmapButton( this, NULL ); + m_opViewButton -> move( 0,0 ); + m_opViewButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "opview_active" ) ); + m_opViewButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "opview_inactive" ) ); + toolTip::add( m_opViewButton, tr( "Operators view" ) ); + + pixmapButton * m_matViewButton = new pixmapButton( this, NULL ); + m_matViewButton -> move( 125,0 ); + m_matViewButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "matview_active" ) ); + m_matViewButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "matview_inactive" ) ); + toolTip::add( m_matViewButton, tr( "Matrix view" ) ); + + m_selectedViewGroup = new automatableButtonGroup( this ); + m_selectedViewGroup -> addButton( m_opViewButton ); + m_selectedViewGroup -> addButton( m_matViewButton ); + + connect( m_opViewButton, SIGNAL( clicked() ), this, SLOT( updateLayout() ) ); + connect( m_matViewButton, SIGNAL( clicked() ), this, SLOT( updateLayout() ) ); +} + + +MonstroView::~MonstroView() +{ +} + + +void MonstroView::updateLayout() +{ + switch( m_selectedViewGroup->model()->value() ) + { + case OPVIEW: + m_operatorsView->show(); + m_matrixView->hide(); + break; + case MATVIEW: + m_operatorsView->hide(); + m_matrixView->show(); + break; + } +} + + +void MonstroView::modelChanged() +{ + MonstroInstrument * m = castModel(); + + m_osc1VolKnob-> setModel( &m-> m_osc1Vol ); + m_osc1PanKnob-> setModel( &m-> m_osc1Pan ); + m_osc1CrsKnob-> setModel( &m-> m_osc1Crs ); + m_osc1FtlKnob-> setModel( &m-> m_osc1Ftl ); + m_osc1FtrKnob-> setModel( &m-> m_osc1Ftr ); + m_osc1SpoKnob-> setModel( &m-> m_osc1Spo ); + m_osc1PwKnob-> setModel( &m-> m_osc1Pw ); + m_osc1SSRButton-> setModel( &m-> m_osc1SSR ); + m_osc1SSFButton-> setModel( &m-> m_osc1SSF ); + + m_osc2VolKnob-> setModel( &m-> m_osc2Vol ); + m_osc2PanKnob-> setModel( &m-> m_osc2Pan ); + m_osc2CrsKnob-> setModel( &m-> m_osc2Crs ); + m_osc2FtlKnob-> setModel( &m-> m_osc2Ftl ); + m_osc2FtrKnob-> setModel( &m-> m_osc2Ftr ); + m_osc2SpoKnob-> setModel( &m-> m_osc2Spo ); + m_osc2WaveBox-> setModel( &m-> m_osc2Wave ); + m_osc2SyncButton-> setModel( &m-> m_osc2Sync ); + + m_osc3VolKnob-> setModel( &m-> m_osc3Vol ); + m_osc3PanKnob-> setModel( &m-> m_osc3Pan ); + m_osc3CrsKnob-> setModel( &m-> m_osc3Crs ); + m_osc3SpoKnob-> setModel( &m-> m_osc3Spo ); + m_osc3SubKnob-> setModel( &m-> m_osc3Sub ); + m_osc3Wave1Box-> setModel( &m-> m_osc3Wave1 ); + m_osc3Wave2Box-> setModel( &m-> m_osc3Wave2 ); + m_osc3SyncButton-> setModel( &m-> m_osc3Sync ); + + m_lfo1WaveBox-> setModel( &m-> m_lfo1Wave ); + m_lfo1AttKnob-> setModel( &m-> m_lfo1Att ); + m_lfo1RateKnob-> setModel( &m-> m_lfo1Rate ); + m_lfo1PhsKnob-> setModel( &m-> m_lfo1Phs ); + + m_lfo2WaveBox-> setModel( &m-> m_lfo2Wave ); + m_lfo2AttKnob-> setModel( &m-> m_lfo2Att ); + m_lfo2RateKnob-> setModel( &m-> m_lfo2Rate ); + m_lfo2PhsKnob-> setModel( &m-> m_lfo2Phs ); + + m_env1PreKnob-> setModel( &m-> m_env1Pre ); + m_env1AttKnob-> setModel( &m-> m_env1Att ); + m_env1HoldKnob-> setModel( &m-> m_env1Hold ); + m_env1DecKnob-> setModel( &m-> m_env1Dec ); + m_env1SusKnob-> setModel( &m-> m_env1Sus ); + m_env1RelKnob-> setModel( &m-> m_env1Rel ); + m_env1SlopeKnob-> setModel( &m-> m_env1Slope ); + + m_env2PreKnob-> setModel( &m-> m_env2Pre ); + m_env2AttKnob-> setModel( &m-> m_env2Att ); + m_env2HoldKnob-> setModel( &m-> m_env2Hold ); + m_env2DecKnob-> setModel( &m-> m_env2Dec ); + m_env2SusKnob-> setModel( &m-> m_env2Sus ); + m_env2RelKnob-> setModel( &m-> m_env2Rel ); + m_env2SlopeKnob-> setModel( &m-> m_env2Slope ); + + m_o23ModGroup-> setModel( &m-> m_o23Mod ); + m_selectedViewGroup-> setModel( &m-> m_selectedView ); + + m_vol1env1Knob-> setModel( &m-> m_vol1env1 ); + m_vol1env2Knob-> setModel( &m-> m_vol1env2 ); + m_vol1lfo1Knob-> setModel( &m-> m_vol1lfo1 ); + m_vol1lfo2Knob-> setModel( &m-> m_vol1lfo2 ); + + m_vol2env1Knob-> setModel( &m-> m_vol2env1 ); + m_vol2env2Knob-> setModel( &m-> m_vol2env2 ); + m_vol2lfo1Knob-> setModel( &m-> m_vol2lfo1 ); + m_vol2lfo2Knob-> setModel( &m-> m_vol2lfo2 ); + + m_vol3env1Knob-> setModel( &m-> m_vol3env1 ); + m_vol3env2Knob-> setModel( &m-> m_vol3env2 ); + m_vol3lfo1Knob-> setModel( &m-> m_vol3lfo1 ); + m_vol3lfo2Knob-> setModel( &m-> m_vol3lfo2 ); + + m_phs1env1Knob-> setModel( &m-> m_phs1env1 ); + m_phs1env2Knob-> setModel( &m-> m_phs1env2 ); + m_phs1lfo1Knob-> setModel( &m-> m_phs1lfo1 ); + m_phs1lfo2Knob-> setModel( &m-> m_phs1lfo2 ); + + m_phs2env1Knob-> setModel( &m-> m_phs2env1 ); + m_phs2env2Knob-> setModel( &m-> m_phs2env2 ); + m_phs2lfo1Knob-> setModel( &m-> m_phs2lfo1 ); + m_phs2lfo2Knob-> setModel( &m-> m_phs2lfo2 ); + + m_phs3env1Knob-> setModel( &m-> m_phs3env1 ); + m_phs3env2Knob-> setModel( &m-> m_phs3env2 ); + m_phs3lfo1Knob-> setModel( &m-> m_phs3lfo1 ); + m_phs3lfo2Knob-> setModel( &m-> m_phs3lfo2 ); + + m_pit1env1Knob-> setModel( &m-> m_pit1env1 ); + m_pit1env2Knob-> setModel( &m-> m_pit1env2 ); + m_pit1lfo1Knob-> setModel( &m-> m_pit1lfo1 ); + m_pit1lfo2Knob-> setModel( &m-> m_pit1lfo2 ); + + m_pit2env1Knob-> setModel( &m-> m_pit2env1 ); + m_pit2env2Knob-> setModel( &m-> m_pit2env2 ); + m_pit2lfo1Knob-> setModel( &m-> m_pit2lfo1 ); + m_pit2lfo2Knob-> setModel( &m-> m_pit2lfo2 ); + + m_pit3env1Knob-> setModel( &m-> m_pit3env1 ); + m_pit3env2Knob-> setModel( &m-> m_pit3env2 ); + m_pit3lfo1Knob-> setModel( &m-> m_pit3lfo1 ); + m_pit3lfo2Knob-> setModel( &m-> m_pit3lfo2 ); + + m_pw1env1Knob-> setModel( &m-> m_pw1env1 ); + m_pw1env2Knob-> setModel( &m-> m_pw1env2 ); + m_pw1lfo1Knob-> setModel( &m-> m_pw1lfo1 ); + m_pw1lfo2Knob-> setModel( &m-> m_pw1lfo2 ); + + m_sub3env1Knob-> setModel( &m-> m_sub3env1 ); + m_sub3env2Knob-> setModel( &m-> m_sub3env2 ); + m_sub3lfo1Knob-> setModel( &m-> m_sub3lfo1 ); + m_sub3lfo2Knob-> setModel( &m-> m_sub3lfo2 ); + +} + + +void MonstroView::setWidgetBackground( QWidget * _widget, const QString & _pic ) +{ + _widget->setAutoFillBackground( true ); + QPalette pal; + pal.setBrush( _widget->backgroundRole(), + PLUGIN_NAME::getIconPixmap( _pic.toAscii().constData() ) ); + _widget->setPalette( pal ); +} + + +QWidget * MonstroView::setupOperatorsView( QWidget * _parent ) +{ + // operators view + + QWidget * view = new QWidget( _parent ); + view-> setFixedSize( 250, 250 ); + + makeknob( m_osc1VolKnob, KNOBCOL1, O1ROW, "Volume", "%", "osc1Knob" ) + makeknob( m_osc1PanKnob, KNOBCOL2, O1ROW, "Panning", "", "osc1Knob" ) + makeknob( m_osc1CrsKnob, KNOBCOL3, O1ROW, "Coarse detune", " seminotes", "osc1Knob" ) + makeknob( m_osc1FtlKnob, KNOBCOL4, O1ROW, "Finetune left", " cents", "osc1Knob" ) + makeknob( m_osc1FtrKnob, KNOBCOL5, O1ROW, "Finetune right", " cents", "osc1Knob" ) + makeknob( m_osc1SpoKnob, KNOBCOL6, O1ROW, "Stereo phase offset", " deg", "osc1Knob" ) + makeknob( m_osc1PwKnob, KNOBCOL7, O1ROW, "Pulse width", "%", "osc1Knob" ) + + m_osc1VolKnob -> setVolumeKnob( true ); + + maketinyled( m_osc1SSRButton, 230, 34, "Send sync on pulse rise" ) + maketinyled( m_osc1SSFButton, 230, 44, "Send sync on pulse fall" ) + + makeknob( m_osc2VolKnob, KNOBCOL1, O2ROW, "Volume", "%", "osc2Knob" ) + makeknob( m_osc2PanKnob, KNOBCOL2, O2ROW, "Panning", "", "osc2Knob" ) + makeknob( m_osc2CrsKnob, KNOBCOL3, O2ROW, "Coarse detune", " seminotes", "osc2Knob" ) + makeknob( m_osc2FtlKnob, KNOBCOL4, O2ROW, "Finetune left", " cents", "osc2Knob" ) + makeknob( m_osc2FtrKnob, KNOBCOL5, O2ROW, "Finetune right", " cents", "osc2Knob" ) + makeknob( m_osc2SpoKnob, KNOBCOL6, O2ROW, "Stereo phase offset", " deg", "osc2Knob" ) + + m_osc2VolKnob -> setVolumeKnob( true ); + + m_osc2WaveBox = new comboBox( view ); + m_osc2WaveBox -> setGeometry( 204, O2ROW + 7, 42, 22 ); + m_osc2WaveBox->setFont( pointSize<8>( m_osc2WaveBox->font() ) ); + + maketinyled( m_osc2SyncButton, 204, O2ROW - 3, "Sync oscillator 2" ) + + makeknob( m_osc3VolKnob, KNOBCOL1, O3ROW, "Volume", "%", "osc3Knob" ) + makeknob( m_osc3PanKnob, KNOBCOL2, O3ROW, "Panning", "", "osc3Knob" ) + makeknob( m_osc3CrsKnob, KNOBCOL3, O3ROW, "Coarse detune", " seminotes", "osc3Knob" ) + makeknob( m_osc3SpoKnob, KNOBCOL4, O3ROW, "Stereo phase offset", " deg", "osc3Knob" ) + makeknob( m_osc3SubKnob, KNOBCOL5, O3ROW, "Sub-osc mix", "", "osc3Knob" ) + + m_osc3Wave1Box = new comboBox( view ); + m_osc3Wave1Box -> setGeometry( 160, O3ROW + 7, 42, 22 ); + m_osc3Wave1Box->setFont( pointSize<8>( m_osc3Wave1Box->font() ) ); + + m_osc3Wave2Box = new comboBox( view ); + m_osc3Wave2Box -> setGeometry( 204, O3ROW + 7, 42, 22 ); + m_osc3Wave2Box->setFont( pointSize<8>( m_osc3Wave2Box->font() ) ); + + maketinyled( m_osc3SyncButton, 204, O3ROW - 3, "Sync oscillator 3" ) + + m_lfo1WaveBox = new comboBox( view ); + m_lfo1WaveBox -> setGeometry( 2, LFOROW + 7, 42, 22 ); + m_lfo1WaveBox->setFont( pointSize<8>( m_lfo1WaveBox->font() ) ); + + maketsknob( m_lfo1AttKnob, LFOCOL1, LFOROW, "Attack", " ms", "lfoKnob" ) + maketsknob( m_lfo1RateKnob, LFOCOL2, LFOROW, "Rate", " ms", "lfoKnob" ) + makeknob( m_lfo1PhsKnob, LFOCOL3, LFOROW, "Phase", " deg", "lfoKnob" ) + + m_lfo2WaveBox = new comboBox( view ); + m_lfo2WaveBox -> setGeometry( 127, LFOROW + 7, 42, 22 ); + m_lfo2WaveBox->setFont( pointSize<8>( m_lfo2WaveBox->font() ) ); + + maketsknob( m_lfo2AttKnob, LFOCOL4, LFOROW, "Attack", " ms", "lfoKnob" ) + maketsknob( m_lfo2RateKnob, LFOCOL5, LFOROW, "Rate", " ms", "lfoKnob" ) + makeknob( m_lfo2PhsKnob, LFOCOL6, LFOROW, "Phase", " deg", "lfoKnob" ) + + maketsknob( m_env1PreKnob, KNOBCOL1, E1ROW, "Pre-delay", " ms", "envKnob" ) + maketsknob( m_env1AttKnob, KNOBCOL2, E1ROW, "Attack", " ms", "envKnob" ) + maketsknob( m_env1HoldKnob, KNOBCOL3, E1ROW, "Hold", " ms", "envKnob" ) + maketsknob( m_env1DecKnob, KNOBCOL4, E1ROW, "Decay", " ms", "envKnob" ) + makeknob( m_env1SusKnob, KNOBCOL5, E1ROW, "Sustain", "", "envKnob" ) + maketsknob( m_env1RelKnob, KNOBCOL6, E1ROW, "Release", " ms", "envKnob" ) + makeknob( m_env1SlopeKnob, KNOBCOL7, E1ROW, "Slope", "", "envKnob" ) + + maketsknob( m_env2PreKnob, KNOBCOL1, E2ROW, "Pre-delay", " ms", "envKnob" ) + maketsknob( m_env2AttKnob, KNOBCOL2, E2ROW, "Attack", " ms", "envKnob" ) + maketsknob( m_env2HoldKnob, KNOBCOL3, E2ROW, "Hold", " ms", "envKnob" ) + maketsknob( m_env2DecKnob, KNOBCOL4, E2ROW, "Decay", " ms", "envKnob" ) + makeknob( m_env2SusKnob, KNOBCOL5, E2ROW, "Sustain", "", "envKnob" ) + maketsknob( m_env2RelKnob, KNOBCOL6, E2ROW, "Release", " ms", "envKnob" ) + makeknob( m_env2SlopeKnob, KNOBCOL7, E2ROW, "Slope", "", "envKnob" ) + + // mod selector + pixmapButton * m_mixButton = new pixmapButton( view, NULL ); + m_mixButton -> move( 225, 185 ); + m_mixButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "mix_active" ) ); + m_mixButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "mix_inactive" ) ); + toolTip::add( m_mixButton, tr( "Mix Osc2 with Osc3" ) ); + + pixmapButton * m_amButton = new pixmapButton( view, NULL ); + m_amButton -> move( 225, 185 + 15 ); + m_amButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "am_active" ) ); + m_amButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "am_inactive" ) ); + toolTip::add( m_amButton, tr( "Modulate amplitude of Osc3 with Osc2" ) ); + + pixmapButton * m_fmButton = new pixmapButton( view, NULL ); + m_fmButton -> move( 225, 185 + 15*2 ); + m_fmButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "fm_active" ) ); + m_fmButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "fm_inactive" ) ); + toolTip::add( m_fmButton, tr( "Modulate frequency of Osc3 with Osc2" ) ); + + pixmapButton * m_pmButton = new pixmapButton( view, NULL ); + m_pmButton -> move( 225, 185 + 15*3 ); + m_pmButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "pm_active" ) ); + m_pmButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "pm_inactive" ) ); + toolTip::add( m_pmButton, tr( "Modulate phase of Osc3 with Osc2" ) ); + + m_o23ModGroup = new automatableButtonGroup( view ); + m_o23ModGroup-> addButton( m_mixButton ); + m_o23ModGroup-> addButton( m_amButton ); + m_o23ModGroup-> addButton( m_fmButton ); + m_o23ModGroup-> addButton( m_pmButton ); + + return( view ); +} + + +QWidget * MonstroView::setupMatrixView( QWidget * _parent ) +{ + // matrix view + + QWidget * view = new QWidget( _parent ); + view-> setFixedSize( 250, 250 ); + + makeknob( m_vol1env1Knob, MATCOL1, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol1env2Knob, MATCOL2, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol1lfo1Knob, MATCOL3, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol1lfo2Knob, MATCOL4, MATROW1, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_vol2env1Knob, MATCOL1, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol2env2Knob, MATCOL2, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol2lfo1Knob, MATCOL3, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol2lfo2Knob, MATCOL4, MATROW3, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_vol3env1Knob, MATCOL1, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol3env2Knob, MATCOL2, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol3lfo1Knob, MATCOL3, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_vol3lfo2Knob, MATCOL4, MATROW5, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_phs1env1Knob, MATCOL1, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs1env2Knob, MATCOL2, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs1lfo1Knob, MATCOL3, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs1lfo2Knob, MATCOL4, MATROW2, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_phs2env1Knob, MATCOL1, MATROW4, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs2env2Knob, MATCOL2, MATROW4, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs2lfo1Knob, MATCOL3, MATROW4, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs2lfo2Knob, MATCOL4, MATROW4, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_phs3env1Knob, MATCOL1, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs3env2Knob, MATCOL2, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs3lfo1Knob, MATCOL3, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_phs3lfo2Knob, MATCOL4, MATROW6, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_pit1env1Knob, MATCOL5, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit1env2Knob, MATCOL6, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit1lfo1Knob, MATCOL7, MATROW1, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit1lfo2Knob, MATCOL8, MATROW1, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_pit2env1Knob, MATCOL5, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit2env2Knob, MATCOL6, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit2lfo1Knob, MATCOL7, MATROW3, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit2lfo2Knob, MATCOL8, MATROW3, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_pit3env1Knob, MATCOL5, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit3env2Knob, MATCOL6, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit3lfo1Knob, MATCOL7, MATROW5, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pit3lfo2Knob, MATCOL8, MATROW5, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_pw1env1Knob, MATCOL5, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pw1env2Knob, MATCOL6, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pw1lfo1Knob, MATCOL7, MATROW2, "Modulation amount", "", "matrixKnob" ) + makeknob( m_pw1lfo2Knob, MATCOL8, MATROW2, "Modulation amount", "", "matrixKnob" ) + + makeknob( m_sub3env1Knob, MATCOL5, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_sub3env2Knob, MATCOL6, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_sub3lfo1Knob, MATCOL7, MATROW6, "Modulation amount", "", "matrixKnob" ) + makeknob( m_sub3lfo2Knob, MATCOL8, MATROW6, "Modulation amount", "", "matrixKnob" ) + + return( view ); +} + +extern "C" +{ + +// necessary for getting instance out of shared lib +Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data ) +{ + return new MonstroInstrument( static_cast( _data ) ); +} + + +} + + + +#include "moc_Monstro.cxx" diff --git a/plugins/monstro/Monstro.h b/plugins/monstro/Monstro.h new file mode 100644 index 000000000..b86525ad0 --- /dev/null +++ b/plugins/monstro/Monstro.h @@ -0,0 +1,643 @@ +/* + * Monstro.h - a semi-modular 3-osc synth with modulation matrix + * + * Copyright (c) 2014 Vesa Kivimäki + * + * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net + * + * 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 MONSTRO_H +#define MONSTRO_H + + +#include "Instrument.h" +#include "InstrumentView.h" +#include "AutomatableModel.h" +#include "automatable_button.h" +#include "TempoSyncKnob.h" +#include "NotePlayHandle.h" +#include "pixmap_button.h" +#include "combobox.h" +#include "Oscillator.h" +// #include "fastpow.h" // once grejppi's fastpow gets merged + + +#define makeknob( name, x, y, hint, unit, oname ) \ + name = new knob( knobStyled, view ); \ + name ->move( x, y ); \ + name ->setHintText( tr( hint ) + " ", unit ); \ + name ->setObjectName( oname ); \ + name ->setFixedSize( 20, 20 ); + +#define maketsknob( name, x, y, hint, unit, oname ) \ + name = new TempoSyncKnob( knobStyled, view ); \ + name ->move( x, y ); \ + name ->setHintText( tr( hint ) + " ", unit ); \ + name ->setObjectName( oname ); \ + name ->setFixedSize( 20, 20 ); + +#define maketinyled( name, x, y, ttip ) \ + name = new pixmapButton( view, NULL ); \ + name -> setCheckable( true ); \ + name -> move( x, y ); \ + name -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "tinyled_on" ) ); \ + name -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "tinyled_off" ) ); \ + toolTip::add( name, tr( ttip ) ); + +#define setwavemodel( name ) \ + name .addItem( tr( "Sine wave" ), static_cast( new PluginPixmapLoader( "sin" ) ) ); \ + name .addItem( tr( "Triangle wave" ), static_cast( new PluginPixmapLoader( "tri" ) ) ); \ + name .addItem( tr( "Saw wave" ), static_cast( new PluginPixmapLoader( "saw" ) ) ); \ + name .addItem( tr( "Ramp wave" ), static_cast( new PluginPixmapLoader( "ramp" ) ) ); \ + name .addItem( tr( "Square wave" ), static_cast( new PluginPixmapLoader( "sqr" ) ) ); \ + name .addItem( tr( "Soft square wave" ), static_cast( new PluginPixmapLoader( "sqrsoft" ) ) ); \ + name .addItem( tr( "Moog saw wave" ), static_cast( new PluginPixmapLoader( "moog" ) ) ); \ + name .addItem( tr( "Abs. sine wave" ), static_cast( new PluginPixmapLoader( "sinabs" ) ) ); \ + name .addItem( tr( "Exponential wave" ), static_cast( new PluginPixmapLoader( "exp" ) ) ); \ + name .addItem( tr( "White noise" ), static_cast( new PluginPixmapLoader( "noise" ) ) ); + +#define setlfowavemodel( name ) \ + name .addItem( tr( "Sine wave" ), static_cast( new PluginPixmapLoader( "sin" ) ) ); \ + name .addItem( tr( "Triangle wave" ), static_cast( new PluginPixmapLoader( "tri" ) ) ); \ + name .addItem( tr( "Saw wave" ), static_cast( new PluginPixmapLoader( "saw" ) ) ); \ + name .addItem( tr( "Ramp wave" ), static_cast( new PluginPixmapLoader( "ramp" ) ) ); \ + name .addItem( tr( "Square wave" ), static_cast( new PluginPixmapLoader( "sqr" ) ) ); \ + name .addItem( tr( "Soft square wave" ), static_cast( new PluginPixmapLoader( "sqrsoft" ) ) ); \ + name .addItem( tr( "Moog saw wave" ), static_cast( new PluginPixmapLoader( "moog" ) ) ); \ + name .addItem( tr( "Abs. sine wave" ), static_cast( new PluginPixmapLoader( "sinabs" ) ) ); \ + name .addItem( tr( "Exponential wave" ), static_cast( new PluginPixmapLoader( "exp" ) ) ); \ + name .addItem( tr( "Random" ), static_cast( new PluginPixmapLoader( "rand" ) ) ); + + +const int O1ROW = 22; +const int O2ROW = 22 + 39; +const int O3ROW = 22 + 39 * 2; +const int LFOROW = 22 + 39 * 3; +const int E1ROW = 22 + 39 * 4; +const int E2ROW = 22 + 39 * 5; + +const int KNOBCOL1 = 16; +const int KNOBCOL2 = 16 + 30; +const int KNOBCOL3 = 16 + 30 * 2; +const int KNOBCOL4 = 16 + 30 * 3; +const int KNOBCOL5 = 16 + 30 * 4; +const int KNOBCOL6 = 16 + 30 * 5; +const int KNOBCOL7 = 16 + 30 * 6; + +const int LFOCOL1 = KNOBCOL2; +const int LFOCOL2 = KNOBCOL2 + 26; +const int LFOCOL3 = KNOBCOL2 + 26*2; +const int LFOCOL4 = 171; +const int LFOCOL5 = 171 + 26; +const int LFOCOL6 = 171 + 26*2; + +const int MATCOL1 = 32; +const int MATCOL2 = 32 + 25; +const int MATCOL3 = 32 + 25*2; +const int MATCOL4 = 32 + 25*3; +const int MATCOL5 = 149; +const int MATCOL6 = 149 + 25; +const int MATCOL7 = 149 + 25*2; +const int MATCOL8 = 149 + 25*3; + +const int MATROW1 = 22; +const int MATROW2 = 22 + 39; +const int MATROW3 = 22 + 39*2; +const int MATROW4 = 22 + 39*3; +const int MATROW5 = 22 + 39*4; +const int MATROW6 = 22 + 39*5; + +const int OPVIEW = 0; +const int MATVIEW = 1; + +const int WAVE_SINE = 0; +const int WAVE_TRI = 1; +const int WAVE_SAW = 2; +const int WAVE_RAMP = 3; +const int WAVE_SQR = 4; +const int WAVE_SQRSOFT = 5; +const int WAVE_MOOG = 6; +const int WAVE_SINABS = 7; +const int WAVE_EXP = 8; +const int WAVE_NOISE = 9; +const int NUM_WAVES = 10; + +const int MOD_MIX = 0; +const int MOD_AM = 1; +const int MOD_FM = 2; +const int MOD_PM = 3; +const int NUM_MODS = 4; + +const float MODCLIP = 2.0; + +const float MIN_FREQ = 18.0f; +const float MAX_FREQ = 48000.0f; + +class MonstroInstrument; +class MonstroView; + +// use grejppi's once it's merged +inline double fastPow( double a, double b ) +{ + union + { + double d; + int32_t x[2]; + } u = { a }; + u.x[1] = static_cast( b * ( u.x[1] - 1072632447 ) + 1072632447 ); + u.x[0] = 0; + return u.d; +} + + +class MonstroSynth +{ +public: + MonstroSynth( MonstroInstrument * _i, NotePlayHandle * _nph, + const sample_rate_t _samplerate, fpp_t _frames ); + virtual ~MonstroSynth(); + + void renderOutput( fpp_t _frames, sampleFrame * _buf ); + + inline sample_rate_t samplerate() const + { + return m_samplerate; + } + +private: + + MonstroInstrument * m_parent; + NotePlayHandle * m_nph; + const sample_rate_t m_samplerate; + fpp_t m_fpp; + + sample_t * m_env1_buf; + sample_t * m_env2_buf; + sample_t * m_lfo1_buf; + sample_t * m_lfo2_buf; + + void renderModulators( fpp_t _frames ); + + // linear interpolation + inline sample_t interpolate( sample_t s1, sample_t s2, float x ) + { + return s1 + ( s2 - s1 ) * x; + } + + + inline sample_t calcSlope( sample_t _s, float _slope ) + { + if( _slope == 0.0f ) return _s; + const double exp = fastPow( 10.0, static_cast( _slope * -1.0 ) ); + return fastPow( _s, exp ); + } + + inline sample_t oscillate( int _wave, const float _ph ) + { + switch( _wave ) + { + case WAVE_SINE: + return Oscillator::sinSample( _ph ); + break; + case WAVE_TRI: + return Oscillator::triangleSample( _ph ); + break; + case WAVE_SAW: + return Oscillator::sawSample( _ph ); + break; + case WAVE_RAMP: + return Oscillator::sawSample( _ph ) * -1.0; + break; + case WAVE_SQR: + return Oscillator::squareSample( _ph ); + break; + case WAVE_SQRSOFT: + { + const float ph = fraction( _ph ); + if( ph < 0.1 ) return Oscillator::sinSample( ph * 5 + 0.75 ); + else if( ph < 0.5 ) return 1.0f; + else if( ph < 0.6 ) return Oscillator::sinSample( ph * 5 + 0.75 ); + else return -1.0f; + break; + } + case WAVE_MOOG: + return Oscillator::moogSawSample( _ph ); + break; + case WAVE_SINABS: + return qAbs( Oscillator::sinSample( _ph ) ); + break; + case WAVE_EXP: + return Oscillator::expSample( _ph ); + break; + case WAVE_NOISE: + default: + return Oscillator::noiseSample( _ph ); + break; + } + return 0.0; + } + + + float m_osc1l_phase; + float m_osc1r_phase; + float m_osc2l_phase; + float m_osc2r_phase; + float m_osc3l_phase; + float m_osc3r_phase; + + sample_t m_env1_phase; + sample_t m_env2_phase; + + float m_lfo1_phase; + float m_lfo2_phase; + + sample_t m_lfo1_last; + sample_t m_lfo2_last; + + sample_t m_osc1l_last; + sample_t m_osc1r_last; +}; + +class MonstroInstrument : public Instrument +{ + Q_OBJECT +public: + MonstroInstrument( InstrumentTrack * _instrument_track ); + virtual ~MonstroInstrument(); + + virtual void playNote( NotePlayHandle * _n, + sampleFrame * _working_buffer ); + virtual void deleteNotePluginData( NotePlayHandle * _n ); + + virtual void saveSettings( QDomDocument & _doc, + QDomElement & _this ); + virtual void loadSettings( const QDomElement & _this ); + + virtual QString nodeName() const; + + virtual f_cnt_t desiredReleaseFrames() const; + + virtual PluginView * instantiateView( QWidget * _parent ); + +public slots: + void updateVolumes(); + void updateFreq(); + void updatePO(); + void updateEnvelope1(); + void updateEnvelope2(); + void updateLFOAtts(); + void updateSamplerate(); + +protected: + float m_osc1l_vol; + float m_osc1r_vol; + float m_osc2l_vol; + float m_osc2r_vol; + float m_osc3l_vol; + float m_osc3r_vol; + + float m_osc1l_freq; + float m_osc1r_freq; + float m_osc2l_freq; + float m_osc2r_freq; + float m_osc3_freq; + + float m_osc1l_po; + float m_osc1r_po; + float m_osc2l_po; + float m_osc2r_po; + float m_osc3l_po; + float m_osc3r_po; + + float m_env1_pre; + float m_env1_att; + float m_env1_hold; + float m_env1_dec; + float m_env1_rel; + + float m_env2_pre; + float m_env2_att; + float m_env2_hold; + float m_env2_dec; + float m_env2_rel; + + f_cnt_t m_env1_len; + f_cnt_t m_env2_len; + + f_cnt_t m_env1_relF; + f_cnt_t m_env2_relF; + + f_cnt_t m_lfo1_att; + f_cnt_t m_lfo2_att; + + sample_rate_t m_samplerate; + fpp_t m_fpp; + +private: + inline float leftCh( float _vol, float _pan ) + { + return ( _pan <= 0 ? 1.0 : 1.0 - ( _pan / 100.0 ) ) * _vol / 100.0; + } + + inline float rightCh( float _vol, float _pan ) + { + return ( _pan >= 0 ? 1.0 : 1.0 + ( _pan / 100.0 ) ) * _vol / 100.0; + } + +////////////////////////////////////// +// models of // +// operator view knobs // +// // +////////////////////////////////////// + + FloatModel m_osc1Vol; + FloatModel m_osc1Pan; + FloatModel m_osc1Crs; + FloatModel m_osc1Ftl; + FloatModel m_osc1Ftr; + FloatModel m_osc1Spo; + FloatModel m_osc1Pw; + BoolModel m_osc1SSR; + BoolModel m_osc1SSF; + + FloatModel m_osc2Vol; + FloatModel m_osc2Pan; + FloatModel m_osc2Crs; + FloatModel m_osc2Ftl; + FloatModel m_osc2Ftr; + FloatModel m_osc2Spo; + ComboBoxModel m_osc2Wave; + BoolModel m_osc2Sync; + + FloatModel m_osc3Vol; + FloatModel m_osc3Pan; + FloatModel m_osc3Crs; + FloatModel m_osc3Spo; + FloatModel m_osc3Sub; + ComboBoxModel m_osc3Wave1; + ComboBoxModel m_osc3Wave2; + BoolModel m_osc3Sync; + + ComboBoxModel m_lfo1Wave; + TempoSyncKnobModel m_lfo1Att; + TempoSyncKnobModel m_lfo1Rate; + FloatModel m_lfo1Phs; + + ComboBoxModel m_lfo2Wave; + TempoSyncKnobModel m_lfo2Att; + TempoSyncKnobModel m_lfo2Rate; + FloatModel m_lfo2Phs; + + TempoSyncKnobModel m_env1Pre; + TempoSyncKnobModel m_env1Att; + TempoSyncKnobModel m_env1Hold; + TempoSyncKnobModel m_env1Dec; + FloatModel m_env1Sus; + TempoSyncKnobModel m_env1Rel; + FloatModel m_env1Slope; + + TempoSyncKnobModel m_env2Pre; + TempoSyncKnobModel m_env2Att; + TempoSyncKnobModel m_env2Hold; + TempoSyncKnobModel m_env2Dec; + FloatModel m_env2Sus; + TempoSyncKnobModel m_env2Rel; + FloatModel m_env2Slope; + + IntModel m_o23Mod; + + IntModel m_selectedView; + +////////////////////////////////////// +// models of // +// modulation matrix view knobs // +// // +////////////////////////////////////// + + FloatModel m_vol1env1; + FloatModel m_vol1env2; + FloatModel m_vol1lfo1; + FloatModel m_vol1lfo2; + + FloatModel m_vol2env1; + FloatModel m_vol2env2; + FloatModel m_vol2lfo1; + FloatModel m_vol2lfo2; + + FloatModel m_vol3env1; + FloatModel m_vol3env2; + FloatModel m_vol3lfo1; + FloatModel m_vol3lfo2; + + FloatModel m_phs1env1; + FloatModel m_phs1env2; + FloatModel m_phs1lfo1; + FloatModel m_phs1lfo2; + + FloatModel m_phs2env1; + FloatModel m_phs2env2; + FloatModel m_phs2lfo1; + FloatModel m_phs2lfo2; + + FloatModel m_phs3env1; + FloatModel m_phs3env2; + FloatModel m_phs3lfo1; + FloatModel m_phs3lfo2; + + FloatModel m_pit1env1; + FloatModel m_pit1env2; + FloatModel m_pit1lfo1; + FloatModel m_pit1lfo2; + + FloatModel m_pit2env1; + FloatModel m_pit2env2; + FloatModel m_pit2lfo1; + FloatModel m_pit2lfo2; + + FloatModel m_pit3env1; + FloatModel m_pit3env2; + FloatModel m_pit3lfo1; + FloatModel m_pit3lfo2; + + FloatModel m_pw1env1; + FloatModel m_pw1env2; + FloatModel m_pw1lfo1; + FloatModel m_pw1lfo2; + + FloatModel m_sub3env1; + FloatModel m_sub3env2; + FloatModel m_sub3lfo1; + FloatModel m_sub3lfo2; + + friend class MonstroSynth; + friend class MonstroView; + +}; + +class MonstroView : public InstrumentView +{ + Q_OBJECT +public: + MonstroView( Instrument * _instrument, + QWidget * _parent ); + virtual ~MonstroView(); + +protected slots: + void updateLayout(); + +private: + virtual void modelChanged(); + + void setWidgetBackground( QWidget * _widget, const QString & _pic ); + QWidget * setupOperatorsView( QWidget * _parent ); + QWidget * setupMatrixView( QWidget * _parent ); + +////////////////////////////////////// +// // +// operator view knobs // +// // +////////////////////////////////////// + + knob * m_osc1VolKnob; + knob * m_osc1PanKnob; + knob * m_osc1CrsKnob; + knob * m_osc1FtlKnob; + knob * m_osc1FtrKnob; + knob * m_osc1SpoKnob; + knob * m_osc1PwKnob; + pixmapButton * m_osc1SSRButton; + pixmapButton * m_osc1SSFButton; + + knob * m_osc2VolKnob; + knob * m_osc2PanKnob; + knob * m_osc2CrsKnob; + knob * m_osc2FtlKnob; + knob * m_osc2FtrKnob; + knob * m_osc2SpoKnob; + comboBox * m_osc2WaveBox; + pixmapButton * m_osc2SyncButton; + + knob * m_osc3VolKnob; + knob * m_osc3PanKnob; + knob * m_osc3CrsKnob; + knob * m_osc3SpoKnob; + knob * m_osc3SubKnob; + comboBox * m_osc3Wave1Box; + comboBox * m_osc3Wave2Box; + pixmapButton * m_osc3SyncButton; + + comboBox * m_lfo1WaveBox; + TempoSyncKnob * m_lfo1AttKnob; + TempoSyncKnob * m_lfo1RateKnob; + knob * m_lfo1PhsKnob; + + comboBox * m_lfo2WaveBox; + TempoSyncKnob * m_lfo2AttKnob; + TempoSyncKnob * m_lfo2RateKnob; + knob * m_lfo2PhsKnob; + + TempoSyncKnob * m_env1PreKnob; + TempoSyncKnob * m_env1AttKnob; + TempoSyncKnob * m_env1HoldKnob; + TempoSyncKnob * m_env1DecKnob; + knob * m_env1SusKnob; + TempoSyncKnob * m_env1RelKnob; + knob * m_env1SlopeKnob; + + TempoSyncKnob * m_env2PreKnob; + TempoSyncKnob * m_env2AttKnob; + TempoSyncKnob * m_env2HoldKnob; + TempoSyncKnob * m_env2DecKnob; + knob * m_env2SusKnob; + TempoSyncKnob * m_env2RelKnob; + knob * m_env2SlopeKnob; + + automatableButtonGroup * m_o23ModGroup; + + automatableButtonGroup * m_selectedViewGroup; + + QWidget * m_operatorsView; + QWidget * m_matrixView; + +///////////////////////////////// +// // +// matrix view knobs // +// // +///////////////////////////////// + + knob * m_vol1env1Knob; + knob * m_vol1env2Knob; + knob * m_vol1lfo1Knob; + knob * m_vol1lfo2Knob; + + knob * m_vol2env1Knob; + knob * m_vol2env2Knob; + knob * m_vol2lfo1Knob; + knob * m_vol2lfo2Knob; + + knob * m_vol3env1Knob; + knob * m_vol3env2Knob; + knob * m_vol3lfo1Knob; + knob * m_vol3lfo2Knob; + + knob * m_phs1env1Knob; + knob * m_phs1env2Knob; + knob * m_phs1lfo1Knob; + knob * m_phs1lfo2Knob; + + knob * m_phs2env1Knob; + knob * m_phs2env2Knob; + knob * m_phs2lfo1Knob; + knob * m_phs2lfo2Knob; + + knob * m_phs3env1Knob; + knob * m_phs3env2Knob; + knob * m_phs3lfo1Knob; + knob * m_phs3lfo2Knob; + + knob * m_pit1env1Knob; + knob * m_pit1env2Knob; + knob * m_pit1lfo1Knob; + knob * m_pit1lfo2Knob; + + knob * m_pit2env1Knob; + knob * m_pit2env2Knob; + knob * m_pit2lfo1Knob; + knob * m_pit2lfo2Knob; + + knob * m_pit3env1Knob; + knob * m_pit3env2Knob; + knob * m_pit3lfo1Knob; + knob * m_pit3lfo2Knob; + + knob * m_pw1env1Knob; + knob * m_pw1env2Knob; + knob * m_pw1lfo1Knob; + knob * m_pw1lfo2Knob; + + knob * m_sub3env1Knob; + knob * m_sub3env2Knob; + knob * m_sub3lfo1Knob; + knob * m_sub3lfo2Knob; + +}; + + +#endif diff --git a/plugins/monstro/am_active.png b/plugins/monstro/am_active.png new file mode 100644 index 000000000..785ac4fae Binary files /dev/null and b/plugins/monstro/am_active.png differ diff --git a/plugins/monstro/am_inactive.png b/plugins/monstro/am_inactive.png new file mode 100644 index 000000000..4b1771930 Binary files /dev/null and b/plugins/monstro/am_inactive.png differ diff --git a/plugins/monstro/artwork_mat.png b/plugins/monstro/artwork_mat.png new file mode 100644 index 000000000..1a4240b51 Binary files /dev/null and b/plugins/monstro/artwork_mat.png differ diff --git a/plugins/monstro/artwork_op.png b/plugins/monstro/artwork_op.png new file mode 100644 index 000000000..2f7b69ca0 Binary files /dev/null and b/plugins/monstro/artwork_op.png differ diff --git a/plugins/monstro/exp.png b/plugins/monstro/exp.png new file mode 100644 index 000000000..3f9cc47a0 Binary files /dev/null and b/plugins/monstro/exp.png differ diff --git a/plugins/monstro/fm_active.png b/plugins/monstro/fm_active.png new file mode 100644 index 000000000..cd4ee859d Binary files /dev/null and b/plugins/monstro/fm_active.png differ diff --git a/plugins/monstro/fm_inactive.png b/plugins/monstro/fm_inactive.png new file mode 100644 index 000000000..a96128b7d Binary files /dev/null and b/plugins/monstro/fm_inactive.png differ diff --git a/plugins/monstro/logo.png b/plugins/monstro/logo.png new file mode 100644 index 000000000..aace6a3ce Binary files /dev/null and b/plugins/monstro/logo.png differ diff --git a/plugins/monstro/matview_active.png b/plugins/monstro/matview_active.png new file mode 100644 index 000000000..c9697dc64 Binary files /dev/null and b/plugins/monstro/matview_active.png differ diff --git a/plugins/monstro/matview_inactive.png b/plugins/monstro/matview_inactive.png new file mode 100644 index 000000000..3cba41bac Binary files /dev/null and b/plugins/monstro/matview_inactive.png differ diff --git a/plugins/monstro/mix_active.png b/plugins/monstro/mix_active.png new file mode 100644 index 000000000..a355b290c Binary files /dev/null and b/plugins/monstro/mix_active.png differ diff --git a/plugins/monstro/mix_inactive.png b/plugins/monstro/mix_inactive.png new file mode 100644 index 000000000..5896ca3e4 Binary files /dev/null and b/plugins/monstro/mix_inactive.png differ diff --git a/plugins/monstro/moog.png b/plugins/monstro/moog.png new file mode 100644 index 000000000..1a657b161 Binary files /dev/null and b/plugins/monstro/moog.png differ diff --git a/plugins/monstro/noise.png b/plugins/monstro/noise.png new file mode 100644 index 000000000..cd22af02b Binary files /dev/null and b/plugins/monstro/noise.png differ diff --git a/plugins/monstro/opview_active.png b/plugins/monstro/opview_active.png new file mode 100644 index 000000000..d993b111b Binary files /dev/null and b/plugins/monstro/opview_active.png differ diff --git a/plugins/monstro/opview_inactive.png b/plugins/monstro/opview_inactive.png new file mode 100644 index 000000000..dc5a0a493 Binary files /dev/null and b/plugins/monstro/opview_inactive.png differ diff --git a/plugins/monstro/pm_active.png b/plugins/monstro/pm_active.png new file mode 100644 index 000000000..b62cd9d5c Binary files /dev/null and b/plugins/monstro/pm_active.png differ diff --git a/plugins/monstro/pm_inactive.png b/plugins/monstro/pm_inactive.png new file mode 100644 index 000000000..c09cdb361 Binary files /dev/null and b/plugins/monstro/pm_inactive.png differ diff --git a/plugins/monstro/ramp.png b/plugins/monstro/ramp.png new file mode 100644 index 000000000..94f23b59f Binary files /dev/null and b/plugins/monstro/ramp.png differ diff --git a/plugins/monstro/rand.png b/plugins/monstro/rand.png new file mode 100644 index 000000000..b857b9882 Binary files /dev/null and b/plugins/monstro/rand.png differ diff --git a/plugins/monstro/saw.png b/plugins/monstro/saw.png new file mode 100644 index 000000000..7435f68aa Binary files /dev/null and b/plugins/monstro/saw.png differ diff --git a/plugins/monstro/sin.png b/plugins/monstro/sin.png new file mode 100644 index 000000000..f9441bdfb Binary files /dev/null and b/plugins/monstro/sin.png differ diff --git a/plugins/monstro/sinabs.png b/plugins/monstro/sinabs.png new file mode 100644 index 000000000..067fa0ddb Binary files /dev/null and b/plugins/monstro/sinabs.png differ diff --git a/plugins/monstro/sqr.png b/plugins/monstro/sqr.png new file mode 100644 index 000000000..47748c668 Binary files /dev/null and b/plugins/monstro/sqr.png differ diff --git a/plugins/monstro/sqrsoft.png b/plugins/monstro/sqrsoft.png new file mode 100644 index 000000000..d04a73947 Binary files /dev/null and b/plugins/monstro/sqrsoft.png differ diff --git a/plugins/monstro/tinyled_off.png b/plugins/monstro/tinyled_off.png new file mode 100644 index 000000000..882ac32e8 Binary files /dev/null and b/plugins/monstro/tinyled_off.png differ diff --git a/plugins/monstro/tinyled_on.png b/plugins/monstro/tinyled_on.png new file mode 100644 index 000000000..3b1d97db5 Binary files /dev/null and b/plugins/monstro/tinyled_on.png differ diff --git a/plugins/monstro/tri.png b/plugins/monstro/tri.png new file mode 100644 index 000000000..9764a36e8 Binary files /dev/null and b/plugins/monstro/tri.png differ