diff --git a/data/themes/default/style.css b/data/themes/default/style.css index c2e91bd05..7d41a8f07 100644 --- a/data/themes/default/style.css +++ b/data/themes/default/style.css @@ -648,6 +648,16 @@ WTSynthView knob#mixenvKnob { qproperty-lineWidth: 2; } +WTSynthView knob#xtalkKnob { + color: #fb50fb; + qproperty-outerColor: #fb50fb; + qproperty-innerRadius: 1; + qproperty-outerRadius: 7; + qproperty-centerPointX: 9.5; + qproperty-centerPointY: 9.5; + qproperty-lineWidth: 2; +} + /* 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/wtsynth/WTSynth.cpp b/plugins/wtsynth/WTSynth.cpp index 54e4580f1..bbbb2bcf9 100644 --- a/plugins/wtsynth/WTSynth.cpp +++ b/plugins/wtsynth/WTSynth.cpp @@ -54,6 +54,17 @@ Plugin::Descriptor PLUGIN_EXPORT wtsynth_plugin_descriptor = } +// these need to be here + +float WTSynthObject::s_lvol [NUM_OSCS]; +float WTSynthObject::s_rvol [NUM_OSCS]; +float WTSynthObject::s_mult [NUM_OSCS]; +float WTSynthObject::s_ltune [NUM_OSCS]; +float WTSynthObject::s_rtune [NUM_OSCS]; +float WTSynthObject::s_xtalk; + + + WTSynthObject::WTSynthObject( float * _A1wave, float * _A2wave, float * _B1wave, float * _B2wave, int _amod, int _bmod, const sample_rate_t _samplerate, NotePlayHandle * _nph, fpp_t _frames ) : @@ -94,6 +105,13 @@ WTSynthObject::~WTSynthObject() void WTSynthObject::renderOutput( fpp_t _frames ) { + // calculate frequencies + for( int i = 0; i < NUM_OSCS; i++ ) + { + m_lfreq[i] = ( s_mult[i] / 8 ) * powf( 2, s_ltune[i] / 1200 ); + m_rfreq[i] = ( s_mult[i] / 8 ) * powf( 2, s_rtune[i] / 1200 ); + } + if( m_abuf == NULL ) m_abuf = new sampleFrame[m_fpp]; if( m_bbuf == NULL ) @@ -101,38 +119,21 @@ void WTSynthObject::renderOutput( fpp_t _frames ) for( fpp_t frame = 0; frame < _frames; frame++ ) { - float frac; - - // A2 - frac = fraction( m_lphase[A2_OSC] ); - sample_t A2_L = - ( m_A2wave[ static_cast( m_lphase[A2_OSC] ) % WAVELEN ] * ( 1.0f - frac ) ) + - ( m_A2wave[ static_cast( m_lphase[A2_OSC] + 1 ) % WAVELEN ] * frac ); - A2_L *= m_lvol[A2_OSC]; - frac = fraction( m_rphase[A2_OSC] ); - sample_t A2_R = - ( m_A2wave[ static_cast( m_rphase[A2_OSC] ) % WAVELEN ] * ( 1.0f - frac ) ) + - ( m_A2wave[ static_cast( m_rphase[A2_OSC] + 1 ) % WAVELEN ] * frac ); - A2_R *= m_rvol[A2_OSC]; - - // B2 - frac = fraction( m_lphase[B2_OSC] ); - sample_t B2_L = - ( m_B2wave[ static_cast( m_lphase[B2_OSC] ) % WAVELEN ] * ( 1.0f - frac ) ) + - ( m_B2wave[ static_cast( m_lphase[B2_OSC] + 1 ) % WAVELEN ] * frac ); - B2_L *= m_lvol[B2_OSC]; - frac = fraction( m_rphase[B2_OSC] ); - sample_t B2_R = - ( m_B2wave[ static_cast( m_rphase[B2_OSC] ) % WAVELEN ] * ( 1.0f - frac ) ) + - ( m_B2wave[ static_cast( m_rphase[B2_OSC] + 1 ) % WAVELEN ] * frac ); - B2_R *= m_rvol[B2_OSC]; - // put phases of 1-series oscs into variables because phase modulation might happen float A1_lphase = m_lphase[A1_OSC]; float A1_rphase = m_rphase[A1_OSC]; float B1_lphase = m_lphase[B1_OSC]; float B1_rphase = m_rphase[B1_OSC]; + ///////////// A-series ///////////////// + + // A2 + sample_t A2_L = interpolate( m_A2wave[ static_cast( m_lphase[A2_OSC] ) % WAVELEN ], + m_A2wave[ static_cast( m_lphase[A2_OSC] + 1 ) % WAVELEN ], + fraction( m_lphase[A2_OSC] ) ) * s_lvol[A2_OSC]; + sample_t A2_R = interpolate( m_A2wave[ static_cast( m_rphase[A2_OSC] ) % WAVELEN ], + m_A2wave[ static_cast( m_rphase[A2_OSC] + 1 ) % WAVELEN ], + fraction( m_rphase[A2_OSC] ) ) * s_rvol[A2_OSC]; // if phase mod, add to phases if( m_amod == MOD_PM ) { @@ -141,6 +142,32 @@ void WTSynthObject::renderOutput( fpp_t _frames ) A1_rphase = fmodf( A1_rphase + A2_R * PMOD_AMT, WAVELEN ); while( A1_rphase < 0 ) A1_rphase += WAVELEN; } + // A1 + sample_t A1_L = interpolate( m_A1wave[ static_cast( A1_lphase ) % WAVELEN ], + m_A1wave[ static_cast( A1_lphase + 1 ) % WAVELEN ], + fraction( A1_lphase ) ) * s_lvol[A1_OSC]; + sample_t A1_R = interpolate( m_A1wave[ static_cast( A1_rphase ) % WAVELEN ], + m_A1wave[ static_cast( A1_rphase + 1 ) % WAVELEN ], + fraction( A1_rphase ) ) * s_rvol[A1_OSC]; + + ///////////// B-series ///////////////// + + // B2 + sample_t B2_L = interpolate( m_B2wave[ static_cast( m_lphase[B2_OSC] ) % WAVELEN ], + m_B2wave[ static_cast( m_lphase[B2_OSC] + 1 ) % WAVELEN ], + fraction( m_lphase[B2_OSC] ) ) * s_lvol[B2_OSC]; + sample_t B2_R = interpolate( m_B2wave[ static_cast( m_rphase[B2_OSC] ) % WAVELEN ], + m_B2wave[ static_cast( m_rphase[B2_OSC] + 1 ) % WAVELEN ], + fraction( m_rphase[B2_OSC] ) ) * s_rvol[B2_OSC]; + + // if crosstalk active, add a1 + if( s_xtalk > 0.0 ) + { + B2_L += ( A1_L * s_xtalk ) / 100.0f; + B2_R += ( A1_R * s_xtalk ) / 100.0f; + } + + // if phase mod, add to phases if( m_bmod == MOD_PM ) { B1_lphase = fmodf( B1_lphase + B2_L * PMOD_AMT, WAVELEN ); @@ -148,30 +175,14 @@ void WTSynthObject::renderOutput( fpp_t _frames ) B1_rphase = fmodf( B1_rphase + B2_R * PMOD_AMT, WAVELEN ); while( B1_rphase < 0 ) B1_rphase += WAVELEN; } - - // A1 - frac = fraction( A1_lphase ); - sample_t A1_L = - ( m_A1wave[ static_cast( A1_lphase ) % WAVELEN ] * ( 1.0f - frac ) ) + - ( m_A1wave[ static_cast( A1_lphase + 1 ) % WAVELEN ] * frac ); - A1_L *= m_lvol[A1_OSC]; - frac = fraction( A1_rphase ); - sample_t A1_R = - ( m_A1wave[ static_cast( A1_rphase ) % WAVELEN ] * ( 1.0f - frac ) ) + - ( m_A1wave[ static_cast( A1_rphase + 1 ) % WAVELEN ] * frac ); - A1_R *= m_rvol[A1_OSC]; - // B1 - frac = fraction( B1_lphase ); - sample_t B1_L = - ( m_B1wave[ static_cast( B1_lphase ) % WAVELEN ] * ( 1.0f - frac ) ) + - ( m_B1wave[ static_cast( B1_lphase + 1 ) % WAVELEN ] * frac ); - B1_L *= m_lvol[B1_OSC]; - frac = fraction( B1_rphase ); - sample_t B1_R = - ( m_B1wave[ static_cast( B1_rphase ) % WAVELEN ] * ( 1.0f - frac ) ) + - ( m_B1wave[ static_cast( B1_rphase + 1 ) % WAVELEN ] * frac ); - B1_R *= m_rvol[B1_OSC]; + sample_t B1_L = interpolate( m_B1wave[ static_cast( B1_lphase ) % WAVELEN ], + m_B1wave[ static_cast( B1_lphase + 1 ) % WAVELEN ], + fraction( B1_lphase ) ) * s_lvol[B1_OSC]; + sample_t B1_R = interpolate( m_B1wave[ static_cast( B1_rphase ) % WAVELEN ], + m_B1wave[ static_cast( B1_rphase + 1 ) % WAVELEN ], + fraction( B1_rphase ) ) * s_rvol[B1_OSC]; + // A-series modulation) switch( m_amod ) @@ -224,40 +235,27 @@ void WTSynthObject::renderOutput( fpp_t _frames ) } -void WTSynthObject::updateFrequencies() -{ - // calculate frequencies - for( int i = 0; i < NUM_OSCS; i++ ) - { - m_lfreq[i] = ( m_mult[i] / 8 ) * powf( 2, m_ltune[i] / 1200 ); - m_rfreq[i] = ( m_mult[i] / 8 ) * powf( 2, m_rtune[i] / 1200 ); - } -} - - - void WTSynthObject::changeVolume( int _osc, float _lvol, float _rvol ) { - m_lvol[_osc] = _lvol / 100.0; - m_rvol[_osc] = _rvol / 100.0; + WTSynthObject::s_lvol[_osc] = _lvol / 100.0; + WTSynthObject::s_rvol[_osc] = _rvol / 100.0; // qDebug( "osc %d vol %f %f", _osc, m_lvol[_osc], m_rvol[_osc] ); } void WTSynthObject::changeMult( int _osc, float _mul ) { - m_mult[_osc] = _mul; + s_mult[_osc] = _mul; } void WTSynthObject::changeTune( int _osc, float _ltune, float _rtune ) { - m_ltune[_osc] = _ltune; - m_rtune[_osc] = _rtune; + s_ltune[_osc] = _ltune; + s_rtune[_osc] = _rtune; } - WTSynthInstrument::WTSynthInstrument( InstrumentTrack * _instrument_track ) : Instrument( _instrument_track, &wtsynth_plugin_descriptor ), @@ -293,11 +291,13 @@ WTSynthInstrument::WTSynthInstrument( InstrumentTrack * _instrument_track ) : m_abmix( 0.0f, -100.0f, 100.0f, 0.1f, this, tr( "A-B Mix" ) ), m_envAmt( 0.0f, -200.0f, 200.0f, 1.0f, this, tr( "A-B Mix envelope amount" ) ), - + m_envAtt( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "A-B Mix envelope attack" ) ), m_envHold( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "A-B Mix envelope hold" ) ), m_envDec( 0.0f, 0.0f, 2000.0f, 1.0f, 2000.0f, this, tr( "A-B Mix envelope decay" ) ), + m_xtalk( 0.0f, 0.0f, 100.0f, 0.1f, this, tr( "A1-B2 Crosstalk" ) ), + m_amod( 0, 0, 3, this, tr( "A2-A1 modulation" ) ), m_bmod( 0, 0, 3, this, tr( "B2-B1 modulation" ) ), @@ -328,10 +328,17 @@ WTSynthInstrument::WTSynthInstrument( InstrumentTrack * _instrument_track ) : connect( &b1_rtune, SIGNAL( dataChanged() ), this, SLOT( updateTunes() ) ); connect( &b2_rtune, SIGNAL( dataChanged() ), this, SLOT( updateTunes() ) ); + connect( &m_xtalk, SIGNAL( dataChanged() ), this, SLOT( updateXtalk() ) ); + a1_graph.setWaveToSine(); a2_graph.setWaveToSine(); b1_graph.setWaveToSine(); b2_graph.setWaveToSine(); + + updateMult(); + updateTunes(); + updateVolumes(); + updateXtalk(); } @@ -353,23 +360,6 @@ void WTSynthInstrument::playNote( NotePlayHandle * _n, engine::mixer()->processingSampleRate(), _n, engine::mixer()->framesPerPeriod() ); - w -> changeMult( A1_OSC, a1_mult.value() ); - w -> changeMult( A2_OSC, a2_mult.value() ); - w -> changeMult( B1_OSC, b1_mult.value() ); - w -> changeMult( B2_OSC, b2_mult.value() ); - - w -> changeTune( A1_OSC, a1_ltune.value(), a1_rtune.value() ); - w -> changeTune( A2_OSC, a2_ltune.value(), a2_rtune.value() ); - w -> changeTune( B1_OSC, b1_ltune.value(), b1_rtune.value() ); - w -> changeTune( B2_OSC, b2_ltune.value(), b2_rtune.value() ); - - w -> changeVolume( A1_OSC, leftCh( a1_vol.value(), a1_pan.value() ), rightCh( a1_vol.value(), a1_pan.value() ) ); - w -> changeVolume( A2_OSC, leftCh( a2_vol.value(), a2_pan.value() ), rightCh( a2_vol.value(), a2_pan.value() ) ); - w -> changeVolume( B1_OSC, leftCh( b1_vol.value(), b1_pan.value() ), rightCh( b1_vol.value(), b1_pan.value() ) ); - w -> changeVolume( B2_OSC, leftCh( b2_vol.value(), b2_pan.value() ), rightCh( b2_vol.value(), b2_pan.value() ) ); - - w -> updateFrequencies(); - _n->m_pluginData = w; } @@ -377,35 +367,6 @@ void WTSynthInstrument::playNote( NotePlayHandle * _n, WTSynthObject * w = static_cast( _n->m_pluginData ); - // update oscs if needed - - if( m_volChanged ) - { - w-> changeVolume( A1_OSC, leftCh( a1_vol.value(), a1_pan.value() ), rightCh( a1_vol.value(), a1_pan.value() ) ); - w-> changeVolume( A2_OSC, leftCh( a2_vol.value(), a2_pan.value() ), rightCh( a2_vol.value(), a2_pan.value() ) ); - w-> changeVolume( B1_OSC, leftCh( b1_vol.value(), b1_pan.value() ), rightCh( b1_vol.value(), b1_pan.value() ) ); - w-> changeVolume( B2_OSC, leftCh( b2_vol.value(), b2_pan.value() ), rightCh( b2_vol.value(), b2_pan.value() ) ); - m_volChanged = false; - } - if( m_tuneChanged ) - { - w-> changeTune( A1_OSC, a1_ltune.value(), a1_rtune.value() ); - w-> changeTune( A2_OSC, a2_ltune.value(), a2_rtune.value() ); - w-> changeTune( B1_OSC, b1_ltune.value(), b1_rtune.value() ); - w-> changeTune( B2_OSC, b2_ltune.value(), b2_rtune.value() ); - w-> updateFrequencies(); - m_tuneChanged = false; - } - if( m_multChanged ) - { - w-> changeMult( A1_OSC, a1_mult.value() ); - w-> changeMult( A2_OSC, a2_mult.value() ); - w-> changeMult( B1_OSC, b1_mult.value() ); - w-> changeMult( B2_OSC, b2_mult.value() ); - w-> updateFrequencies(); - m_multChanged = false; - } - sampleFrame * abuf = w->abuf(); sampleFrame * bbuf = w->bbuf(); @@ -453,7 +414,7 @@ void WTSynthInstrument::playNote( NotePlayHandle * _n, ( bbuf[f][1] * bmix ); } } - + // if sample-exact is not enabled, use simpler calculations: // if mix envelope is active, and we haven't gone past the envelope end, use envelope-aware calculation... else if( envAmt != 0.0f && tfp_ < envLen ) @@ -487,7 +448,7 @@ void WTSynthInstrument::playNote( NotePlayHandle * _n, _working_buffer[f][1] = ( abuf[f][1] * amix ) + ( bbuf[f][1] * bmix ); } - } + } // ... mix envelope is inactive or we've past the end of envelope, so use a faster calculation to save cpu else @@ -563,6 +524,8 @@ void WTSynthInstrument::saveSettings( QDomDocument & _doc, m_envHold.saveSettings( _doc, _this, "envHold" ); m_envDec.saveSettings( _doc, _this, "envDec" ); + m_xtalk.saveSettings( _doc, _this, "xtalk" ); + m_amod.saveSettings( _doc, _this, "amod" ); m_bmod.saveSettings( _doc, _this, "bmod" ); m_selectedGraph.saveSettings( _doc, _this, "selgraph" ); @@ -618,6 +581,8 @@ void WTSynthInstrument::loadSettings( const QDomElement & _this ) m_envHold.loadSettings( _this, "envHold" ); m_envDec.loadSettings( _this, "envDec" ); + m_xtalk.loadSettings( _this, "xtalk" ); + m_amod.loadSettings( _this, "amod" ); m_bmod.loadSettings( _this, "bmod" ); m_selectedGraph.loadSettings( _this, "selgraph" ); @@ -638,16 +603,30 @@ PluginView * WTSynthInstrument::instantiateView( QWidget * _parent ) void WTSynthInstrument::updateVolumes() { - m_volChanged = true; + WTSynthObject::changeVolume( A1_OSC, leftCh( a1_vol.value(), a1_pan.value() ), rightCh( a1_vol.value(), a1_pan.value() ) ); + WTSynthObject::changeVolume( A2_OSC, leftCh( a2_vol.value(), a2_pan.value() ), rightCh( a2_vol.value(), a2_pan.value() ) ); + WTSynthObject::changeVolume( B1_OSC, leftCh( b1_vol.value(), b1_pan.value() ), rightCh( b1_vol.value(), b1_pan.value() ) ); + WTSynthObject::changeVolume( B2_OSC, leftCh( b2_vol.value(), b2_pan.value() ), rightCh( b2_vol.value(), b2_pan.value() ) ); } void WTSynthInstrument::updateMult() { - m_multChanged = true; + WTSynthObject::changeMult( A1_OSC, a1_mult.value() ); + WTSynthObject::changeMult( A2_OSC, a2_mult.value() ); + WTSynthObject::changeMult( B1_OSC, b1_mult.value() ); + WTSynthObject::changeMult( B2_OSC, b2_mult.value() ); } void WTSynthInstrument::updateTunes() { - m_tuneChanged = true; + WTSynthObject::changeTune( A1_OSC, a1_ltune.value(), a1_rtune.value() ); + WTSynthObject::changeTune( A2_OSC, a2_ltune.value(), a2_rtune.value() ); + WTSynthObject::changeTune( B1_OSC, b1_ltune.value(), b1_rtune.value() ); + WTSynthObject::changeTune( B2_OSC, b2_ltune.value(), b2_rtune.value() ); } +void WTSynthInstrument::updateXtalk() +{ + WTSynthObject::changeXtalk( m_xtalk.value() ); +} + WTSynthView::WTSynthView( Instrument * _instrument, @@ -690,11 +669,13 @@ WTSynthView::WTSynthView( Instrument * _instrument, makeknob( m_abmixKnob, 4, 3, "A-B Mix", "", "mixKnob" ) makeknob( m_envAmtKnob, 88, 3, "Mix envelope amount", "", "mixenvKnob" ) - + maketsknob( m_envAttKnob, 88, A1ROW, "Mix envelope attack", " ms", "mixenvKnob" ) maketsknob( m_envHoldKnob, 88, A2ROW, "Mix envelope hold", " ms", "mixenvKnob" ) maketsknob( m_envDecKnob, 88, B1ROW, "Mix envelope decay", " ms", "mixenvKnob" ) + makeknob( m_xtalkKnob, 88, B2ROW, "Crosstalk", "", "xtalkKnob" ) + // let's set volume knobs a1_volKnob -> setVolumeKnob( true ); a2_volKnob -> setVolumeKnob( true ); @@ -839,19 +820,19 @@ WTSynthView::WTSynthView( Instrument * _instrument, // waveform modifications m_loadButton = new pixmapButton( this, tr( "Load waveform" ) ); - m_loadButton -> move ( 176, 121 ); + m_loadButton -> move ( 173, 121 ); m_loadButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "load_active" ) ); m_loadButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "load_inactive" ) ); toolTip::add( m_loadButton, tr( "Click to load a waveform from a sample file" ) ); m_phaseLeftButton = new pixmapButton( this, tr( "Phase left" ) ); - m_phaseLeftButton -> move ( 196, 121 ); + m_phaseLeftButton -> move ( 193, 121 ); m_phaseLeftButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "phl_active" ) ); m_phaseLeftButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "phl_inactive" ) ); toolTip::add( m_phaseLeftButton, tr( "Click to shift phase by -15 degrees" ) ); m_phaseRightButton = new pixmapButton( this, tr( "Phase right" ) ); - m_phaseRightButton -> move ( 213, 121 ); + m_phaseRightButton -> move ( 210, 121 ); m_phaseRightButton -> setActiveGraphic( PLUGIN_NAME::getIconPixmap( "phr_active" ) ); m_phaseRightButton -> setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "phr_inactive" ) ); toolTip::add( m_phaseRightButton, tr( "Click to shift phase by +15 degrees" ) ); @@ -912,7 +893,7 @@ WTSynthView::WTSynthView( Instrument * _instrument, connect( m_smoothButton, SIGNAL( clicked() ), this, SLOT( smoothClicked() ) ); connect( m_phaseLeftButton, SIGNAL( clicked() ), this, SLOT( phaseLeftClicked() ) ); connect( m_phaseRightButton, SIGNAL( clicked() ), this, SLOT( phaseRightClicked() ) ); - connect( m_loadButton, SIGNAL( clicked() ), this, SLOT( loadClicked() ) ); + connect( m_loadButton, SIGNAL( clicked() ), this, SLOT( loadClicked() ) ); connect( a1_selectButton, SIGNAL( clicked() ), this, SLOT( updateLayout() ) ); connect( a2_selectButton, SIGNAL( clicked() ), this, SLOT( updateLayout() ) ); @@ -1180,7 +1161,7 @@ void WTSynthView::phaseRightClicked() void WTSynthView::loadClicked() { - QString fileName; + QString fileName; switch( m_selectedGraphGroup->model()->value() ) { case A1_OSC: @@ -1249,6 +1230,7 @@ void WTSynthView::modelChanged() m_envHoldKnob -> setModel( &w -> m_envHold ); m_envDecKnob -> setModel( &w -> m_envDec ); + m_xtalkKnob -> setModel( &w -> m_xtalk ); } diff --git a/plugins/wtsynth/WTSynth.h b/plugins/wtsynth/WTSynth.h index df7aa908b..c412199f4 100644 --- a/plugins/wtsynth/WTSynth.h +++ b/plugins/wtsynth/WTSynth.h @@ -56,20 +56,20 @@ #define B2ROW 95 -const int WAVELEN = 220; -const int PMOD_AMT = 110; +extern const int WAVELEN = 220; +extern const int PMOD_AMT = 110; -const int MOD_MIX = 0; -const int MOD_AM = 1; -const int MOD_RM = 2; -const int MOD_PM = 3; -const int NUM_MODS = 4; +extern const int MOD_MIX = 0; +extern const int MOD_AM = 1; +extern const int MOD_RM = 2; +extern const int MOD_PM = 3; +extern const int NUM_MODS = 4; -const int A1_OSC = 0; -const int A2_OSC = 1; -const int B1_OSC = 2; -const int B2_OSC = 3; -const int NUM_OSCS = 4; +extern const int A1_OSC = 0; +extern const int A2_OSC = 1; +extern const int B1_OSC = 2; +extern const int B2_OSC = 3; +extern const int NUM_OSCS = 4; class WTSynthObject @@ -80,14 +80,17 @@ public: int _amod, int _bmod, const sample_rate_t _samplerate, NotePlayHandle * _nph, fpp_t _frames ); virtual ~WTSynthObject(); + static void changeVolume( int _osc, float _lvol, float _rvol ); + static void changeMult( int _osc, float _mul ); + static void changeTune( int _osc, float _ltune, float _rtune ); + + static inline void changeXtalk( float _xtalk ) + { + s_xtalk = _xtalk; + }; + void renderOutput( fpp_t _frames ); - void updateFrequencies(); - - void changeVolume( int _osc, float _lvol, float _rvol ); - void changeMult( int _osc, float _mul ); - void changeTune( int _osc, float _ltune, float _rtune ); - inline sampleFrame * abuf() const { return m_abuf; @@ -102,11 +105,29 @@ public: } private: - float m_lvol [NUM_OSCS]; - float m_rvol [NUM_OSCS]; - float m_mult [NUM_OSCS]; - float m_ltune [NUM_OSCS]; - float m_rtune [NUM_OSCS]; + static float s_lvol [NUM_OSCS]; + static float s_rvol [NUM_OSCS]; + static float s_mult [NUM_OSCS]; + static float s_ltune [NUM_OSCS]; + static float s_rtune [NUM_OSCS]; + static float s_xtalk; + + // linear interpolation +/* inline sample_t interpolate( sample_t s1, sample_t s2, float x ) + { + return s1 + ( s2 - s1 ) * x; + }*/ + // quick and dirty approximation of cubic interpolation + inline sample_t interpolate( sample_t s1, sample_t s2, float x ) + { + const float x2 = powf( x, 2 ); + const float x3 = powf( x, 3 ); + const float m = s2 - s1; + + return ( ( x3 * 2.0 - x2 * 3.0 + 1.0 ) * s1 ) + + ( ( x3 * -2.0 + x2 * 3.0 ) * s2 ) + + ( ( x + x3 * 2.0 - x2 * 3.0 ) * m ); + } int m_amod; int m_bmod; @@ -160,6 +181,7 @@ public slots: void updateVolumes(); void updateMult(); void updateTunes(); + void updateXtalk(); private: inline float leftCh( float _vol, float _pan ) @@ -205,20 +227,18 @@ private: FloatModel m_abmix; FloatModel m_envAmt; - + TempoSyncKnobModel m_envAtt; TempoSyncKnobModel m_envHold; TempoSyncKnobModel m_envDec; + FloatModel m_xtalk; + IntModel m_amod; IntModel m_bmod; IntModel m_selectedGraph; - bool m_volChanged; - bool m_multChanged; - bool m_tuneChanged; - friend class WTSynthView; }; @@ -278,11 +298,13 @@ private: knob * m_abmixKnob; knob * m_envAmtKnob; - + TempoSyncKnob * m_envAttKnob; TempoSyncKnob * m_envHoldKnob; TempoSyncKnob * m_envDecKnob; + knob * m_xtalkKnob; + automatableButtonGroup * m_selectedGraphGroup; automatableButtonGroup * m_aModGroup; automatableButtonGroup * m_bModGroup; diff --git a/plugins/wtsynth/artwork.png b/plugins/wtsynth/artwork.png index 56413dcea..bd321cdbd 100644 Binary files a/plugins/wtsynth/artwork.png and b/plugins/wtsynth/artwork.png differ