diff --git a/ChangeLog b/ChangeLog index f1c688701..1e259e98a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2007-11-26 Paul Giblock + + * plugins/lb302/lb302.cpp: + * plugins/lb302/lb302.h: + reverted to old lb302. There is a bug in sound output, will look + into it, but I'd rather have a working repository + 2007-11-24 Paul Giblock * plugins/lb302/lb302.cpp: diff --git a/data/presets/LB302/Oh Synth.cs.xml b/data/presets/LB302/Oh Synth.cs.xml new file mode 100644 index 000000000..3b9027fee --- /dev/null +++ b/data/presets/LB302/Oh Synth.cs.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 748b4e0e4..787e624bc 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -3,11 +3,11 @@ * to emulate the Roland TB303 bass synth * * Copyright (c) 2006-2007 Paul Giblock - * + * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * lb302FilterIIR2 is based on the gsyn filter code by Andy Sloane. - * + * * lb302Filter3Pole is based on the TB303 instrument written by * Josep M Comajuncosas for the CSounds library * @@ -51,10 +51,10 @@ // // New config // -#define LB_24_IGNORE_ENVELOPE +#define LB_24_IGNORE_ENVELOPE #define LB_FILTERED //#define LB_DECAY -//#define LB_24_RES_TRICK +//#define LB_24_RES_TRICK #define LB_DIST_RATIO 4.0 #define LB_24_VOL_ADJUST 3.0 @@ -82,7 +82,7 @@ plugin::descriptor lb302_plugin_descriptor = STRINGIFY_PLUGIN_NAME( PLUGIN_NAME ), "LB302", QT_TRANSLATE_NOOP( "pluginBrowser", - "Llamafied tb303" ), + "Incomplete monophonic immitation tb303" ), "Paul Giblock ", 0x0100, plugin::Instrument, @@ -92,562 +92,513 @@ plugin::descriptor lb302_plugin_descriptor = } - // // lb302Filter // -lb302Filter::lb302Filter( lb302FilterKnobState * _fs ) : - m_fs( _fs ), - m_c0( 0 ), - m_e0( 0 ), - m_e1( 0 ) +lb302Filter::lb302Filter(lb302FilterKnobState* p_fs) : + fs(p_fs), + vcf_c0(0), + vcf_e0(0), + vcf_e1(0) { }; - - -void lb302Filter::recalc( void ) +void lb302Filter::recalc() { - m_e1 = exp( 6.109 + 1.5876*(m_fs->envmod) + - 2.1553*(m_fs->cutoff) - 1.2*( 1.0 - (m_fs->reso) ) ); - m_e0 = exp( 5.613 - 0.8*(m_fs->envmod) + - 2.1553*(m_fs->cutoff) - 0.7696*( 1.0 - (m_fs->reso) ) ); + vcf_e1 = exp(6.109 + 1.5876*(fs->envmod) + 2.1553*(fs->cutoff) - 1.2*(1.0-(fs->reso))); + vcf_e0 = exp(5.613 - 0.8*(fs->envmod) + 2.1553*(fs->cutoff) - 0.7696*(1.0-(fs->reso))); + vcf_e0*=M_PI/44100.0; + vcf_e1*=M_PI/44100.0; + vcf_e1 -= vcf_e0; - m_e0 *= M_PI / 44100.0; - m_e1 *= M_PI / 44100.0; - m_e1 -= m_e0; - - m_rescoeff = exp( -1.20 + 3.455 * (m_fs->reso) ); + vcf_rescoeff = exp(-1.20 + 3.455*(fs->reso)); }; - -void lb302Filter::envRecalc( void ) +void lb302Filter::envRecalc() { - m_c0 *= m_fs->envdecay; // Filter Decay. vcf_decay is adjusted for Hz and ENVINC - // vcf_rescoeff = exp(-1.20 + 3.455*(fs->reso)); moved above + vcf_c0 *= fs->envdecay; // Filter Decay. vcf_decay is adjusted for Hz and ENVINC + // vcf_rescoeff = exp(-1.20 + 3.455*(fs->reso)); moved above }; - -void lb302Filter::playNote( void ) +void lb302Filter::playNote() { - m_c0 = m_e1; + vcf_c0 = vcf_e1; } - - // // lb302FilterIIR2 // -lb302FilterIIR2::lb302FilterIIR2( lb302FilterKnobState * _fs ) : - lb302Filter( _fs ), - m_d1( 0 ), - m_d2( 0 ), - m_a( 0 ), - m_b( 0 ), - m_c( 1 ) +lb302FilterIIR2::lb302FilterIIR2(lb302FilterKnobState* p_fs) : + lb302Filter(p_fs), + vcf_d1(0), + vcf_d2(0), + vcf_a(0), + vcf_b(0), + vcf_c(1) { - m_dist = new effectLib::distortion<>( 1.0, 1.0f ); - + m_dist = new effectLib::distortion<>( 1.0, 1.0f); + }; -lb302FilterIIR2::~lb302FilterIIR2( void ) + + +lb302FilterIIR2::~lb302FilterIIR2() { delete m_dist; } -void lb302FilterIIR2::recalc( void ) + +void lb302FilterIIR2::recalc() { - lb302Filter::recalc(); + lb302Filter::recalc(); //m_dist->setThreshold(0.5+(fs->dist*2.0)); - m_dist->setThreshold( m_fs->dist * 75.0 ); + m_dist->setThreshold(fs->dist*75.0); }; - -void lb302FilterIIR2::envRecalc( void ) +void lb302FilterIIR2::envRecalc() { - float k, w; + float k, w; + + lb302Filter::envRecalc(); - lb302Filter::envRecalc(); - - w = m_e0 + m_c0; // e0 is adjusted for Hz and doesn't need ENVINC - k = exp( -w / m_rescoeff ); // Does this mean c0 is inheritantly? - - m_a = 2.0 * cos( 2.0 * w ) * k; - m_b = -k * k; - m_c = 1.0 - m_a - m_b; + w = vcf_e0 + vcf_c0; // e0 is adjusted for Hz and doesn't need ENVINC + k = exp(-w/vcf_rescoeff); + // Does this mean c0 is inheritantly? + vcf_a = 2.0*cos(2.0*w) * k; + vcf_b = -k*k; + vcf_c = 1.0 - vcf_a - vcf_b; } -float lb302FilterIIR2::process( const float & _samp ) +float lb302FilterIIR2::process(const float& samp) { - float ret = ( m_a * m_d1 ) + - ( m_b * m_d2 ) + - ( m_c * _samp ); + float ret = vcf_a*vcf_d1 + vcf_b*vcf_d2 + vcf_c*samp; + // Delayed samples for filter + vcf_d2 = vcf_d1; + vcf_d1 = ret; - // Delayed samples for filter - m_d2 = m_d1; - m_d1 = ret; - - if( m_fs->dist > 0 ) - ret = m_dist->nextSample( ret ); - - // output = IIR2 + dry - return ret; + if(fs->dist > 0) + ret=m_dist->nextSample(ret); + // output = IIR2 + dry + return ret; } - -void lb302FilterIIR2::getState( lb302FilterState * _fs ) +void lb302FilterIIR2::getState(lb302FilterState* fs) { - _fs->iir.c0 = m_c0; - _fs->iir.a = m_a; - _fs->iir.b = m_b; - _fs->iir.c = m_c; - _fs->iir.d1 = m_d1; - _fs->iir.d2 = m_d2; + fs->iir.vcf_c0 = vcf_c0; + fs->iir.vcf_a = vcf_a; + fs->iir.vcf_b = vcf_b; + fs->iir.vcf_c = vcf_c; + fs->iir.vcf_d1 = vcf_d1; + fs->iir.vcf_d2 = vcf_d2; } - -void lb302FilterIIR2::setState( const lb302FilterState * _fs ) +void lb302FilterIIR2::setState(const lb302FilterState* fs) { - m_c0 = _fs->iir.c0; - m_a = _fs->iir.a; - m_b = _fs->iir.b; - m_c = _fs->iir.c; - m_d1 = _fs->iir.d1; - m_d2 = _fs->iir.d2; + vcf_c0 = fs->iir.vcf_c0; + vcf_a = fs->iir.vcf_a; + vcf_b = fs->iir.vcf_b; + vcf_c = fs->iir.vcf_c; + vcf_d1 = fs->iir.vcf_d1; + vcf_d2 = fs->iir.vcf_d2; } - - - // // lb302Filter3Pole // -lb302Filter3Pole::lb302Filter3Pole( lb302FilterKnobState * _fs ) : - lb302Filter( _fs ), - m_ay1( 0 ), - m_ay2( 0 ), - m_aout( 0 ), - m_lastin( 0 ) +lb302Filter3Pole::lb302Filter3Pole(lb302FilterKnobState *p_fs) : + lb302Filter(p_fs), + ay1(0), + ay2(0), + aout(0), + lastin(0) { }; - - -void lb302Filter3Pole::recalc( void ) +void lb302Filter3Pole::recalc() { - // DO NOT CALL BASE CLASS - - m_e0 = 0.000001; - m_e1 = 1.0; + // DO NOT CALL BASE CLASS + vcf_e0 = 0.000001; + vcf_e1 = 1.0; } - -void lb302Filter3Pole::envRecalc( void ) +// TODO: Try using k instead of vcf_reso +void lb302Filter3Pole::envRecalc() { - // TODO: Try using k instead of vcf_reso - float w, k; - float kfco; + float w,k; + float kfco; - lb302Filter::envRecalc(); + lb302Filter::envRecalc(); + + + w = vcf_e0 + vcf_c0; // e0 is adjusted for Hz and doesn't need ENVINC + k = (fs->cutoff > 0.975)?0.975:fs->cutoff; + kfco = 50.f+(k)*((2300.f-1600.f*(fs->envmod))+(w)*(700.f+1500.f*(k)+(1500.f+(k)*(44100.f/2.f-6000.f))*(fs->envmod))); + //+iacc*(.3+.7*kfco*kenvmod)*kaccent*kaccurve*2000 - w = m_e0 + m_c0; // e0 is adjusted for Hz and doesn't need ENVINC - k = ( m_fs->cutoff > 0.975 ) ? 0.975 : m_fs->cutoff; - kfco = 50.f + - k * ( - ( 2300.f - 1600.f * m_fs->envmod) + - w * ( - 700.f + - 1500.f * k + - ( 1500.f + k * ( 44100.f / 2.f - 6000.f ) ) * - m_fs->envmod - ) - ); - //+iacc*(.3+.7*kfco*kenvmod)*kaccent*kaccurve*2000 #ifdef LB_24_IGNORE_ENVELOPE - // kfcn = fs->cutoff; - m_kfcn = 2.0 * kfco / LB_HZ; + // kfcn = fs->cutoff; + kfcn = 2.0 * kfco / LB_HZ; #else - m_kfcn = w; + kfcn = w; #endif - m_kp = ( ( -2.7528 * m_kfcn + 3.0429 ) * m_kfcn + 1.718 ) * m_kfcn - 0.9984; - m_kp1 = m_kp + 1.0; - m_kp1h = 0.5 * m_kp1; + kp = ((-2.7528*kfcn + 3.0429)*kfcn + 1.718)*kfcn - 0.9984; + kp1 = kp+1.0; + kp1h = 0.5*kp1; #ifdef LB_24_RES_TRICK - k = exp( -w / m_rescoeff ); - m_kres = k * - ( ( ( -2.7079 * m_kp1 + 10.963 ) * m_kp1 - 14.934 ) * m_kp1 + 8.4974 ); + k = exp(-w/vcf_rescoeff); + kres = (((k))) * (((-2.7079*kp1 + 10.963)*kp1 - 14.934)*kp1 + 8.4974); #else - m_kres = m_fs->reso * - ( ( ( -2.7079 * m_kp1 + 10.963 ) * m_kp1 - 14.934 ) * m_kp1 + 8.4974 ); + kres = (((fs->reso))) * (((-2.7079*kp1 + 10.963)*kp1 - 14.934)*kp1 + 8.4974); #endif - - m_value = 1.0 + ( (m_fs->dist) * ( 1.5 + 2.0 * m_kres * ( 1.0 - m_kfcn ) ) ); + value = 1.0+( (fs->dist) *(1.5 + 2.0*kres*(1.0-kfcn))); // ENVMOD was DIST*/ } - -float lb302Filter3Pole::process( const float & _samp ) +float lb302Filter3Pole::process(const float& samp) { - float ax1 = m_lastin; - float ay11 = m_ay1; - float ay31 = m_ay2; - - m_lastin = _samp - tanh( m_kres * m_aout ); - m_ay1 = m_kp1h * ( m_lastin + ax1 ) - m_kp * m_ay1; - m_ay2 = m_kp1h * ( m_ay1 + ay11 ) - m_kp * m_ay2; - m_aout = m_kp1h * ( m_ay2 + ay31 ) - m_kp * m_aout; - - return tanh( m_aout * m_value ) * LB_24_VOL_ADJUST / ( 1.0 + m_fs->dist ); + float ax1 = lastin; + float ay11 = ay1; + float ay31 = ay2; + lastin = (samp) - tanh(kres*aout); + ay1 = kp1h * (lastin+ax1) - kp*ay1; + ay2 = kp1h * (ay1 + ay11) - kp*ay2; + aout = kp1h * (ay2 + ay31) - kp*aout; + + return tanh(aout*value)*LB_24_VOL_ADJUST/(1.0+fs->dist); } - -void lb302Filter3Pole::getState( lb302FilterState * _fs ) +void lb302Filter3Pole::getState(lb302FilterState* fs) { - _fs->pole.aout = m_aout; - _fs->pole.c0 = m_c0; - _fs->pole.kp = m_kp; - _fs->pole.kp1h = m_kp1h; - _fs->pole.kres = m_kres; - _fs->pole.ay1 = m_ay1; - _fs->pole.ay2 = m_ay2; - _fs->pole.lastin = m_lastin; - _fs->pole.value = m_value; + fs->pole.aout = aout; + fs->pole.vcf_c0 = vcf_c0; + fs->pole.kp = kp; + fs->pole.kp1h = kp1h; + fs->pole.kres = kres; + fs->pole.ay1 = ay1; + fs->pole.ay2 = ay2; + fs->pole.lastin = lastin; + fs->pole.value = value; } - -void lb302Filter3Pole::setState( const lb302FilterState * _fs ) +void lb302Filter3Pole::setState(const lb302FilterState* fs) { - m_aout = _fs->pole.aout; - m_c0 = _fs->pole.c0; - m_kp = _fs->pole.kp; - m_kp1h = _fs->pole.kp1h; - m_kres = _fs->pole.kres; - m_ay1 = _fs->pole.ay1; - m_ay2 = _fs->pole.ay2; - m_lastin = _fs->pole.lastin; - m_value = _fs->pole.value; + aout = fs->pole.aout; + vcf_c0 = fs->pole.vcf_c0; + kp = fs->pole.kp; + kp1h = fs->pole.kp1h; + kres = fs->pole.kres; + ay1 = fs->pole.ay1; + ay2 = fs->pole.ay2; + lastin = fs->pole.lastin; + value = fs->pole.value; } - - // // LBSynth // lb302Synth::lb302Synth( instrumentTrack * _channel_track ) : - instrument( _channel_track, &lb302_plugin_descriptor ), - - m_vco_shape( SAWTOOTH ), - m_vco_slide( 0 ), - m_vco_slideinc( 0 ), - m_vco_slidebase( 0 ), - m_vco_detune( 0 ), - m_vco_inc( 0.0 ), - m_vco_k( 0 ), - m_vco_c( 0 ), - - m_vcf_envpos( ENVINC ), - - m_vca_attack( 1.0 - 0.96406088 ), - m_vca_decay( 0.99897516 ), - m_vca_a0( 0.5 ), // Experimenting between (0.5) and 1.0 - m_vca_a( 9 ), - m_vca_mode( 3 ), - - m_useHoldNote( false ), - m_sampleCnt( 0 ), - m_releaseFrame( 1<<24 ), - m_catchFrame( 0 ), - m_catchDecay( 0 ), - m_lastFramesPlayed( 1 ), - m_lastOffset( 0 ), - - m_periodStates( NULL ), - m_periodStatesCnt( 0 ) - + instrument( _channel_track, &lb302_plugin_descriptor ) { - // GUI + // GUI - m_vcfCutKnob = new knob( knobBright_26, this, tr( "VCF Cutoff Frequency" ), - _channel_track ); + vcf_cut_knob = new knob( knobBright_26, this, tr( "VCF Cutoff Frequency" ), + _channel_track ); + vcf_cut_knob->setRange( 0.0f, 1.5f, 0.005f ); // Originally [0,1.0] + vcf_cut_knob->setInitValue( 0.75f ); + vcf_cut_knob->move( 75, 130 ); + vcf_cut_knob->setHintText( tr( "Cutoff Freq:" ) + " ", "" ); + vcf_cut_knob->setLabel( tr("CUT") ); - m_vcfCutKnob->setRange( 0.0f, 1.5f, 0.005f ); // Originally [0,1.0] - m_vcfCutKnob->setInitValue( 0.75f ); - m_vcfCutKnob->move( 75, 130 ); - m_vcfCutKnob->setHintText( tr( "Cutoff Freq:" ) + " ", "" ); - m_vcfCutKnob->setLabel( tr( "CUT" ) ); + vcf_res_knob = new knob( knobBright_26, this, tr( "VCF Resonance" ), + _channel_track ); + vcf_res_knob->setRange( 0.0f, 1.25f, 0.005f ); // Originally [0,1.0] + vcf_res_knob->setInitValue( 0.75f ); + vcf_res_knob->move( 120, 130 ); + vcf_res_knob->setHintText( tr( "Resonance:" ) + " ", "" ); + vcf_res_knob->setLabel( tr("RES") ); - m_vcfResKnob = new knob( knobBright_26, this, tr( "VCF Resonance" ), - _channel_track ); + vcf_mod_knob = new knob( knobBright_26, this, tr( "VCF Envelope Mod" ), + _channel_track ); + vcf_mod_knob->setRange( 0.0f, 1.0f, 0.005f ); // Originally [0,1.0] + vcf_mod_knob->setInitValue( 1.0f ); + vcf_mod_knob->move( 165, 130 ); + vcf_mod_knob->setHintText( tr( "Env Mod:" ) + " ", "" ); + vcf_mod_knob->setLabel( tr("ENV MOD") ); - m_vcfResKnob->setRange( 0.0f, 1.25f, 0.005f ); // Originally [0,1.0] - m_vcfResKnob->setInitValue( 0.75f ); - m_vcfResKnob->move( 120, 130 ); - m_vcfResKnob->setHintText( tr( "Resonance:" ) + " ", "" ); - m_vcfResKnob->setLabel( tr( "RES" ) ); + vcf_dec_knob = new knob( knobBright_26, this, tr( "VCF Envelope Decay" ), + _channel_track ); + vcf_dec_knob->setRange( 0.0f, 1.0f, 0.005f ); // Originally [0,1.0] + vcf_dec_knob->setInitValue( 0.1f ); + vcf_dec_knob->move( 210, 130 ); + vcf_dec_knob->setHintText( tr( "Decay:" ) + " ", "" ); + vcf_dec_knob->setLabel( tr("DEC") ); - m_vcfModKnob = new knob( knobBright_26, this, tr( "VCF Envelope Mod" ), - _channel_track ); - - m_vcfModKnob->setRange( 0.0f, 1.0f, 0.005f ); // Originally [0,1.0] - m_vcfModKnob->setInitValue( 1.0f ); - m_vcfModKnob->move( 165, 130 ); - m_vcfModKnob->setHintText( tr( "Env Mod:" ) + " ", "" ); - m_vcfModKnob->setLabel( tr( "ENV MOD" ) ); - - m_vcfDecKnob = new knob( knobBright_26, this, tr( "VCF Envelope Decay" ), - _channel_track ); - - m_vcfDecKnob->setRange( 0.0f, 1.0f, 0.005f ); // Originally [0,1.0] - m_vcfDecKnob->setInitValue( 0.1f ); - m_vcfDecKnob->move( 210, 130 ); - m_vcfDecKnob->setHintText( tr( "Decay:" ) + " ", "" ); - m_vcfDecKnob->setLabel( tr( "DEC" ) ); - - m_slideToggle = new ledCheckBox( "Slide", this, tr( "Slide" ), - _channel_track ); - - m_slideToggle->move( 10, 180 ); - - m_accentToggle = new ledCheckBox( "Accent", this, tr( "Accent" ), - _channel_track ); - m_accentToggle->move( 10, 200 ); - m_accentToggle->setDisabled( true ); - - m_deadToggle = new ledCheckBox( "Dead", this, tr( "Dead" ), - _channel_track ); - - m_deadToggle->move( 10, 220 ); - - m_db24Toggle = new ledCheckBox( "24dB/oct", this, - tr( "303-es-que, 24dB/octave, 3 pole filter" ), - _channel_track ); - - m_db24Toggle->move( 10, 150 ); - - m_slideDecKnob = new knob( knobBright_26, this, tr( "Slide Decay" ), - _channel_track ); - m_slideDecKnob->setRange( 0.0f, 1.0f, 0.005f ); // Originally [0,1.0] - m_slideDecKnob->setInitValue( 0.6f ); - m_slideDecKnob->move( 210, 75 ); - m_slideDecKnob->setHintText( tr( "Slide Decay:" ) + " ", "" ); - m_slideDecKnob->setLabel( tr( "SLIDE")); - - m_vcoFineDetuneKnob = new knob( knobBright_26, this, - tr( "Fine detuning of the VCO. Between -100 and 100 cents." ), - _channel_track ); - m_vcoFineDetuneKnob->setRange( -100.0f, 100.0f, 1.0f ); - m_vcoFineDetuneKnob->setInitValue( 0.0f ); - m_vcoFineDetuneKnob->move( 165, 75 ); - m_vcoFineDetuneKnob->setHintText( tr( "VCO Fine Detuning:" ) + " ", "cents" ); - m_vcoFineDetuneKnob->setLabel( tr( "DETUNE" ) ); + slideToggle = new ledCheckBox( "Slide", this, + tr( "Slide" ), + _channel_track ); + slideToggle->move( 10, 180 ); - m_distKnob = new knob( knobBright_26, this, tr( "Distortion" ), - _channel_track ); - m_distKnob->setRange( 0.0f, 1.0f, 0.01f ); // Originally [0,1.0] - m_distKnob->setInitValue( 0.0f ); - m_distKnob->move( 210, 190 ); - m_distKnob->setHintText( tr( "DIST:" ) + " ", "" ); - m_distKnob->setLabel( tr( "DIST" ) ); - - m_waveKnob = new knob( knobBright_26, this, tr( "Waveform" ), - _channel_track ); - m_waveKnob->setRange( 0.0f, 5.0f, 1.0f ); // Originally [0,1.0] - m_waveKnob->setInitValue( 0.0f ); - m_waveKnob->move( 120, 75 ); - m_waveKnob->setHintText( tr( "WAVE:" ) + " ", "" ); - m_waveKnob->setLabel( tr( "WAVE" ) ); + accentToggle = new ledCheckBox( "Accent", this, + tr( "Accent" ), + _channel_track ); + accentToggle->move( 10, 200 ); + accentToggle->setDisabled(true); - connect( m_vcfCutKnob, SIGNAL( valueChanged( float ) ), - this, SLOT ( filterChanged( float ) ) ); + deadToggle = new ledCheckBox( "Dead", this, + tr( "Dead" ), + _channel_track ); + deadToggle->move( 10, 220 ); - connect( m_vcfResKnob, SIGNAL( valueChanged( float ) ), - this, SLOT ( filterChanged( float ) ) ); + db24Toggle = new ledCheckBox( "24dB/oct", this, + tr( "303-es-que, 24dB/octave, 3 pole filter" ), + _channel_track ); + db24Toggle->move( 10, 150); - connect( m_vcfModKnob, SIGNAL( valueChanged( float ) ), - this, SLOT ( filterChanged( float ) ) ); - connect( m_vcfDecKnob, SIGNAL( valueChanged( float ) ), - this, SLOT ( filterChanged( float ) ) ); + slide_dec_knob = new knob( knobBright_26, this, tr( "Slide Decay" ), + _channel_track ); + slide_dec_knob->setRange( 0.0f, 1.0f, 0.005f ); // Originally [0,1.0] + slide_dec_knob->setInitValue( 0.6f ); + slide_dec_knob->move( 210, 75 ); + slide_dec_knob->setHintText( tr( "Slide Decay:" ) + " ", "" ); + slide_dec_knob->setLabel( tr( "SLIDE")); - connect( m_vcoFineDetuneKnob, SIGNAL( valueChanged( float ) ), - this, SLOT ( detuneChanged( float) ) ); + vco_fine_detune_knob = new knob( knobBright_26, this, + tr("Fine detuning of the VCO. Ranged between -100 and 100 centes."), + _channel_track ); + vco_fine_detune_knob->setRange(-100.0f, 100.0f, 1.0f); + vco_fine_detune_knob->setInitValue(0.0f); + vco_fine_detune_knob->move(165,75); + vco_fine_detune_knob->setHintText( tr( "VCO Fine Detuning:") + " ", "cents"); + vco_fine_detune_knob->setLabel( tr( "DETUNE")); - connect( m_db24Toggle, SIGNAL( toggled( bool ) ), - this, SLOT ( db24Toggled( bool) ) ); - connect( m_distKnob, SIGNAL( valueChanged(float) ), - this, SLOT ( filterChanged( float ) ) ); + dist_knob = new knob( knobBright_26, this, tr( "Distortion" ), + _channel_track ); + dist_knob->setRange( 0.0f, 1.0f, 0.01f ); // Originally [0,1.0] + dist_knob->setInitValue( 0.0f ); + dist_knob->move( 210, 190 ); + dist_knob->setHintText( tr( "DIST:" ) + " ", "" ); + dist_knob->setLabel( tr( "DIST")); - connect( m_waveKnob, SIGNAL( valueChanged(float) ), - this, SLOT ( waveChanged( float ) ) ); + + wave_knob = new knob( knobBright_26, this, tr( "Waveform" ), + _channel_track ); + wave_knob->setRange( 0.0f, 5.0f, 1.0f ); // Originally [0,1.0] + wave_knob->setInitValue( 0.0f ); + wave_knob->move( 120, 75 ); + wave_knob->setHintText( tr( "WAVE:" ) + " ", "" ); + wave_knob->setLabel( tr( "WAVE")); + + + connect( vcf_cut_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( filterChanged( float ) ) ); + + connect( vcf_res_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( filterChanged( float ) ) ); + + connect( vcf_mod_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( filterChanged( float ) ) ); + + connect( vcf_dec_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( filterChanged( float ) ) ); + + connect( vco_fine_detune_knob, SIGNAL( valueChanged( float ) ), + this, SLOT ( detuneChanged( float) ) ); + + connect( db24Toggle, SIGNAL( toggled( bool ) ), + this, SLOT ( db24Toggled( bool) ) ); + + connect( dist_knob, SIGNAL( valueChanged(float) ), + this, SLOT ( filterChanged( float ))); + + connect( wave_knob, SIGNAL( valueChanged(float) ), + this, SLOT ( waveChanged( float ))); setAutoFillBackground( TRUE ); QPalette pal; - pal.setBrush( backgroundRole(), - PLUGIN_NAME::getIconPixmap( "artwork" ) ); + pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( + "artwork" ) ); setPalette( pal ); - // SYNTH + // SYNTH - m_fs.cutoff = 0; - m_fs.envmod = 0; - m_fs.reso = 0; - m_fs.envdecay = 0; - m_fs.dist = 0; + vco_inc = 0.0; + vco_c = 0; + vco_k = 0; + + vco_slide = 0; vco_slideinc = 0; + vco_slidebase = 0; - m_vcf = new lb302FilterIIR2( &m_fs ); + fs.cutoff = 0; fs.envmod = 0; + fs.reso = 0; fs.envdecay = 0; + fs.dist = 0; - recalcFilter(); + vcf_envpos = ENVINC; + vco_detune = 0; - filterChanged( 0.0 ); - detuneChanged( 0.0 ); + vca_mode = 3; vca_a = 0; // Start VCA on an attack. + + //vca_attack = 1.0 - 0.94406088; + vca_attack = 1.0 - 0.96406088; + vca_decay = 0.99897516; + + vco_shape = SAWTOOTH; + + vca_a0 = 0.5; // Experimenting between original (0.5) and 1.0 + vca_a = 9; + vca_mode = 3; + + vcf = new lb302FilterIIR2(&fs); + + use_hold_note = false; + sample_cnt = 0; + release_frame = 1<<24; + catch_frame = 0; + catch_decay = 0; + + recalcFilter(); + + lastFramesPlayed = 1; // because we subtract 1 later + last_offset = 0; + + period_states = NULL; + period_states_cnt = 0; + + filterChanged(0.0); + detuneChanged(0.0); } + + lb302Synth::~lb302Synth() { - delete m_vcf; + delete vcf; } void lb302Synth::saveSettings( QDomDocument & _doc, - QDomElement & _this ) + QDomElement & _this ) { - m_vcfCutKnob->saveSettings( _doc, _this, "vcf_cut" ); - m_vcfResKnob->saveSettings( _doc, _this, "vcf_res" ); - m_vcfModKnob->saveSettings( _doc, _this, "vcf_mod" ); - m_vcfDecKnob->saveSettings( _doc, _this, "vcf_dec" ); + vcf_cut_knob->saveSettings( _doc, _this, "vcf_cut" ); + vcf_res_knob->saveSettings( _doc, _this, "vcf_res" ); + vcf_mod_knob->saveSettings( _doc, _this, "vcf_mod" ); + vcf_dec_knob->saveSettings( _doc, _this, "vcf_dec" ); - m_vcoFineDetuneKnob->saveSettings( _doc, _this, "vco_detune" ); - m_waveKnob->saveSettings( _doc, _this, "shape" ); - m_distKnob->saveSettings( _doc, _this, "dist" ); - m_slideDecKnob->saveSettings( _doc, _this, "slide_dec" ); + vco_fine_detune_knob->saveSettings( _doc, _this, "vco_detune" ); + wave_knob->saveSettings( _doc, _this, "shape"); + dist_knob->saveSettings( _doc, _this, "dist"); + slide_dec_knob->saveSettings( _doc, _this, "slide_dec"); - m_slideToggle->saveSettings( _doc, _this, "slide" ); - m_deadToggle->saveSettings( _doc, _this, "dead" ); - m_db24Toggle->saveSettings( _doc, _this, "db24" ); + slideToggle->saveSettings( _doc, _this, "slide"); + deadToggle->saveSettings( _doc, _this, "dead"); + db24Toggle->saveSettings( _doc, _this, "db24"); } + + void lb302Synth::loadSettings( const QDomElement & _this ) { - m_vcfCutKnob->loadSettings( _this, "vcf_cut" ); - m_vcfResKnob->loadSettings( _this, "vcf_res" ); - m_vcfModKnob->loadSettings( _this, "vcf_mod" ); - m_vcfDecKnob->loadSettings( _this, "vcf_dec" ); + vcf_cut_knob->loadSettings( _this, "vcf_cut" ); + vcf_res_knob->loadSettings( _this, "vcf_res" ); + vcf_mod_knob->loadSettings( _this, "vcf_mod" ); + vcf_dec_knob->loadSettings( _this, "vcf_dec" ); - m_vcoFineDetuneKnob->loadSettings( _this, "vco_detune" ); - m_distKnob->loadSettings( _this, "dist" ); - m_waveKnob->loadSettings( _this, "shape" ); - m_slideDecKnob->loadSettings( _this, "slide_dec" ); + vco_fine_detune_knob->loadSettings( _this, "vco_detune" ); + dist_knob->loadSettings( _this, "dist"); + wave_knob->loadSettings( _this, "shape"); + slide_dec_knob->loadSettings( _this, "slide_dec"); - m_slideToggle->loadSettings( _this, "slide" ); - m_deadToggle->loadSettings( _this, "dead" ); - m_db24Toggle->loadSettings( _this, "db24" ); + slideToggle->loadSettings( _this, "slide"); + deadToggle->loadSettings( _this, "dead"); + db24Toggle->loadSettings( _this, "db24"); - filterChanged( 0.0 ); - detuneChanged( 0.0 ); + filterChanged(0.0); + detuneChanged(0.0); } - // TODO: Split into one function per knob. envdecay doesn't require // recalcFilter. void lb302Synth::filterChanged( float ) { - m_fs.cutoff = m_vcfCutKnob->value(); - m_fs.reso = m_vcfResKnob->value(); - m_fs.envmod = m_vcfModKnob->value(); - m_fs.dist = m_distKnob->value() * LB_DIST_RATIO; + fs.cutoff = vcf_cut_knob->value(); + fs.reso = vcf_res_knob->value(); + fs.envmod = vcf_mod_knob->value(); + fs.dist = LB_DIST_RATIO*dist_knob->value(); - float d = 0.2 + ( 2.3 * m_vcfDecKnob->value() ); - d *= LB_HZ; - m_fs.envdecay = pow( 0.1, 1.0 / d * ENVINC ); - // vcf_envdecay is now adjusted for both sampling rate and ENVINC - - recalcFilter(); + float d = 0.2 + (2.3*vcf_dec_knob->value()); + d*=LB_HZ; // d *= smpl rate + fs.envdecay = pow(0.1, 1.0/d * ENVINC); // decay is 0.1 to the 1/d * ENVINC + // vcf_envdecay is now adjusted for both + // sampling rate and ENVINC + recalcFilter(); } - void lb302Synth::db24Toggled( bool ) { - delete m_vcf; - - if( m_db24Toggle->isChecked() ) { - m_vcf = new lb302Filter3Pole( &m_fs ); - } - else { - m_vcf = new lb302FilterIIR2( &m_fs ); - } - recalcFilter(); + delete vcf; + if(db24Toggle->isChecked()) { + vcf = new lb302Filter3Pole(&fs); + } + else { + vcf = new lb302FilterIIR2(&fs); + } + recalcFilter(); } void lb302Synth::detuneChanged( float ) { - float freq = m_vco_inc * LB_HZ / m_vco_detune; - float slidebase_freq = 0; + float freq = vco_inc*LB_HZ/vco_detune; + float slidebase_freq=0; - if( m_vco_slide ) { - slidebase_freq = m_vco_slidebase * LB_HZ / m_vco_detune; - } + if(vco_slide) { + slidebase_freq = vco_slidebase*LB_HZ/vco_detune; + } + + vco_detune = powf(2.0f, (float)vco_fine_detune_knob->value()/1200.0f); + vco_inc = freq*vco_detune/LB_HZ; - m_vco_detune = powf( 2.0f, - (float)m_vcoFineDetuneKnob->value() / 1200.0f); + // If a slide note is pending, + if(vco_slideinc) + vco_slideinc = vco_inc; - m_vco_inc = freq * m_vco_detune/LB_HZ; - - // If a slide note is pending, - if( m_vco_slideinc ) - m_vco_slideinc = m_vco_inc; - - // If currently sliding, - // May need to rescale vco_slide as well - if( m_vco_slide) - m_vco_slidebase = slidebase_freq * m_vco_detune / LB_HZ; + // If currently sliding, + // May need to rescale vco_slide as well + if(vco_slide) + vco_slidebase=slidebase_freq*vco_detune/LB_HZ; } - // TODO: Set vco_shape in here. void lb302Synth::waveChanged( float ) { - switch( (int)rint( m_waveKnob->value() ) ) { - case 0: - m_waveKnob->setHintText( tr( "Sawtooth " ), "" ); - break; - case 1: - m_waveKnob->setHintText( tr( "Inverted Sawtooth " ), "" ); - break; - case 2: - m_waveKnob->setHintText( tr( "Triangle " ), "" ); - break; - case 3: - m_waveKnob->setHintText( tr( "Square " ), "" ); - break; - case 4: - m_waveKnob->setHintText( tr( "Rounded Square " ), "" ); - break; - case 5: - m_waveKnob->setHintText( tr( "Moog " ), "" ); - break; - } + switch(int(rint(wave_knob->value()))) { + case 0: wave_knob->setHintText(tr("Sawtooth "),""); break; + case 1: wave_knob->setHintText(tr("Inverted Sawtooth "),""); break; + case 2: wave_knob->setHintText(tr("Triangle "),""); break; + case 3: wave_knob->setHintText(tr("Square "),""); break; + case 4: wave_knob->setHintText(tr("Rounded Square "),""); break; + case 5: wave_knob->setHintText(tr("Moog "),""); break; + } } - QString lb302Synth::nodeName( void ) const { return( lb302_plugin_descriptor.name ); @@ -656,216 +607,224 @@ QString lb302Synth::nodeName( void ) const // OBSOLETE. Break apart once we get Q_OBJECT to work. >:[ void lb302Synth::recalcFilter() { - m_vcf->recalc(); + vcf->recalc(); - // THIS IS OLD 3pole/24dB code, I may reintegrate it. Don't need it - // right now. Should be toggled by LB_24_RES_TRICK at the moment. + // THIS IS OLD 3pole/24dB code, I may reintegrate it. Don't need it + // right now. Should be toggled by LB_24_RES_TRICK at the moment. - /*kfcn = 2.0 * (((vcf_cutoff*3000))) / LB_HZ; - kp = ((-2.7528*kfcn + 3.0429)*kfcn + 1.718)*kfcn - 0.9984; - kp1 = kp+1.0; - kp1h = 0.5*kp1; - kres = (((vcf_reso))) * (((-2.7079*kp1 + 10.963)*kp1 - 14.934)*kp1 + 8.4974); - value = 1.0+( (((0))) *(1.5 + 2.0*kres*(1.0-kfcn))); // ENVMOD was DIST*/ + /*kfcn = 2.0 * (((vcf_cutoff*3000))) / LB_HZ; + kp = ((-2.7528*kfcn + 3.0429)*kfcn + 1.718)*kfcn - 0.9984; + kp1 = kp+1.0; + kp1h = 0.5*kp1; + kres = (((vcf_reso))) * (((-2.7079*kp1 + 10.963)*kp1 - 14.934)*kp1 + 8.4974); + value = 1.0+( (((0))) *(1.5 + 2.0*kres*(1.0-kfcn))); // ENVMOD was DIST*/ - m_vcf_envpos = ENVINC; // Trigger filter update in process() + vcf_envpos = ENVINC; // Trigger filter update in process() } +inline int MIN(int a, int b) { + return (a= ENVINC ) { - m_vcf->envRecalc(); + if(vcf_envpos >= ENVINC) { + vcf->envRecalc(); + + vcf_envpos = 0; - m_vcf_envpos = 0; + if (vco_slide) { + vco_inc=vco_slidebase-vco_slide; + // Calculate coeff from dec_knob on knob change. + vco_slide*= 0.9+(slide_dec_knob->value()*0.0999); // TODO: Adjust for Hz and ENVINC - if( m_vco_slide ) { - m_vco_inc = m_vco_slidebase - m_vco_slide; - // Calculate coeff from dec_knob on knob change. - // TODO: Adjust for Hz and ENVINC - m_vco_slide *= 0.9 + ( m_slideDecKnob->value() * 0.0999 ); - } + } } - m_sampleCnt++; - m_vcf_envpos++; + sample_cnt++; + vcf_envpos++; - int decayFrames = 128; + // unused variables + //float old_vco_k = vco_k; + //bool looking; + int decay_frames = 128; // update vco - m_vco_c += m_vco_inc; + vco_c += vco_inc; - if( m_vco_c > 0.5 ) - m_vco_c -= 1.0; + if(vco_c > 0.5) vco_c -= 1.0; - if( m_catchDecay > 0 ) { - if( m_catchDecay < decayFrames ) { - m_catchDecay++; - } - else if( m_useHoldNote ) { - m_useHoldNote = false; - initNote( &m_holdNote ); - } - } + if (catch_decay > 0) { + if (catch_decay < decay_frames) { + catch_decay++; + } + else if (use_hold_note) { + use_hold_note = false; + initNote(&hold_note); + } + } + + + switch(int(rint(wave_knob->value()))) { + case 0: vco_shape = SAWTOOTH; break; + case 1: vco_shape = INVERTED_SAWTOOTH; break; + case 2: vco_shape = TRIANGLE; break; + case 3: vco_shape = SQUARE; break; + case 4: vco_shape = ROUND_SQUARE; break; + case 5: vco_shape = MOOG; break; + default: vco_shape = SAWTOOTH; break; + } - switch( (int)rint( m_waveKnob->value() ) ) { - case 0: m_vco_shape = SAWTOOTH; break; - case 1: m_vco_shape = INVERTED_SAWTOOTH; break; - case 2: m_vco_shape = TRIANGLE; break; - case 3: m_vco_shape = SQUARE; break; - case 4: m_vco_shape = ROUND_SQUARE; break; - case 5: m_vco_shape = MOOG; break; - default: m_vco_shape = SAWTOOTH; break; - } + // add vco_shape_param the changes the shape of each curve. + // merge sawtooths with triangle and square with round square? + switch (vco_shape) { + case SAWTOOTH: // p0: curviness of line + vco_k = vco_c; // Is this sawtooth backwards? + break; - // add vco_shape_param the changes the shape of each curve. - // merge sawtooths with triangle and square with round square? - switch (m_vco_shape) { - case SAWTOOTH: // p0: curviness of line - m_vco_k = m_vco_c; // Is this sawtooth backwards? - break; + case INVERTED_SAWTOOTH: // p0: curviness of line + vco_k = -vco_c; // Is this sawtooth backwards? + break; - case INVERTED_SAWTOOTH: // p0: curviness of line - m_vco_k = -m_vco_c; // Is this sawtooth backwards? - break; + // TODO: I think TRIANGLE is broken. + case TRIANGLE: // p0: duty rev.saw<->triangle<->saw p1: curviness + vco_k = (vco_c*2.0)+0.5; + if (vco_k>0.5) vco_k = 1.0-vco_k; + break; - case TRIANGLE: // p0: duty rev.saw<->triangle<->saw p1: curviness - m_vco_k = ( m_vco_c * 2.0 ) + 0.5; - if( m_vco_k > 0.5 ) - m_vco_k = 1.0 - m_vco_k; - break; + case SQUARE: // p0: slope of top + vco_k = (vco_c<0)?0.5:-0.5; + break; - case SQUARE: // p0: slope of top - m_vco_k = ( m_vco_c < 0 ) ? 0.5 : -0.5; - break; + case ROUND_SQUARE: // p0: width of round + vco_k = (vco_c<0)?(sqrtf(1-(vco_c*vco_c*4))-0.5):-0.5; + break; - case ROUND_SQUARE: // p0: width of round - m_vco_k = ( m_vco_c < 0 ) ? - ( sqrtf( 1 - ( m_vco_c*m_vco_c * 4 ) ) - 0.5) : - -0.5; - break; + case MOOG: // Maybe the fall should be exponential/sinsoidal instead of quadric. + // [-0.5, 0]: Rise, [0,0.25]: Slope down, [0.25,0.5]: Low + vco_k = (vco_c*2.0)+0.5; + if (vco_k>1.0) vco_k = -0.5 ; + else if (vco_k>0.5) { + w = 2.0*(vco_k-0.5)-1.0; + vco_k = 0.5 - sqrtf(1.0-(w*w)); + } + vco_k *= 2.0; // MOOG wave gets filtered away + break; + } - case MOOG: // Maybe the fall should be exponential/sinsoidal instead of quadric. - // [-0.5, 0]: Rise, [0,0.25]: Slope down, [0.25,0.5]: Low - m_vco_k = ( m_vco_c * 2.0 ) + 0.5; - if( m_vco_k > 1.0 ) - m_vco_k = -0.5; - else if( m_vco_k > 0.5 ) { - w = 2.0 * ( m_vco_k - 0.5 ) - 1.0; - m_vco_k = 0.5 - sqrtf( 1.0 - w*w ); - } - m_vco_k *= 2.0; // MOOG wave gets filtered away - break; - } - - // Write out samples. + //vca_a = 0.5; + // Write out samples. #ifdef LB_FILTERED - samp = m_vcf->process( m_vco_k ) * 2.0 * m_vca_a; + samp = vcf->process(vco_k)*2.0*vca_a; #else - samp = m_vco_k * m_vca_a; + samp = vco_k*vca_a; #endif - /* - float releaseFrames = desiredReleaseFrames(); - samp *= (releaseFrames - catch_decay)/releaseFrames; - */ - samp *= (float)( decayFrames - m_catchDecay ) / - (float)decayFrames; + /* + float releaseFrames = desiredReleaseFrames(); + samp *= (releaseFrames - catch_decay)/releaseFrames; + */ + samp *= (float)(decay_frames - catch_decay)/(float)decay_frames; - for( int c=0; c < DEFAULT_CHANNELS; c++ ) { - _outbuf[i][c] = samp; - } + for(int c=0; c= m_releaseFrame ) { - m_vca_mode = 1; - } - - // Handle Envelope - // TODO: Add decay once I figure out how to extend past the end of a note. - if( m_vca_mode == 0 ) { - m_vca_a += ( m_vca_a0 - m_vca_a ) * m_vca_attack; - if( m_sampleCnt >= 0.5 * 44100 ) - m_vca_mode = 2; - } - else if( m_vca_mode == 1) { - m_vca_a *= m_vca_decay; - + if((int)i>=release_frame) { + vca_mode=1; + } + + // Handle Envelope + // TODO: Add decay once I figure out how to extend past the end of a note. + if(vca_mode==0) { + vca_a+=(vca_a0-vca_a)*vca_attack; + if(sample_cnt>=0.5*44100) + vca_mode = 2; + } + else if(vca_mode == 1) { + vca_a *= vca_decay; + // the following line actually speeds up processing - if( m_vca_a < 1/65536.0 ) { - m_vca_a = 0; - m_vca_mode = 3; - } + if(vca_a < (1/65536.0)) { vca_a = 0; vca_mode = 3; } } - // Store state - m_periodStates[i].vco_c = m_vco_c; - m_periodStates[i].vca_a = m_vca_a; // Doesn't change anything (currently) - m_periodStates[i].vca_mode = m_vca_mode; // Doesn't change anything (currently) - m_periodStates[i].sampleCnt = m_sampleCnt; // Doesn't change anything (currently) - m_vcf->getState(&m_periodStates[i].fs); + // Store state + period_states[i].vco_c = vco_c; + period_states[i].vca_a = vca_a; // Doesn't change anything (currently) + period_states[i].vca_mode = vca_mode; // Doesn't change anything (currently) + period_states[i].sample_cnt = sample_cnt; // Doesn't change anything (currently) + vcf->getState(&period_states[i].fs); } return 1; } - /* Prepares the active LB302 note. I separated this into a function because it - * needs to be called onPlayNote() when a new note is started. It also needs + * needs to be called on0playNote() when a new note is started. It also needs * to be called from process() when a prior edge-to-edge note is done releasing. */ -void lb302Synth::initNote( lb302Note * _n ) +void lb302Synth::initNote( lb302Note *n) { - m_catchDecay = 0; + catch_decay = 0; - m_vco_inc = _n->vco_inc; + vco_inc = n->vco_inc; + + // TODO: Try moving to the if() below + if(n->dead == 0 || (vca_mode==1 || vca_mode==3)) { + sample_cnt = 0; + vca_mode = 0; vca_a = 0; + } + else { + vca_mode = 2; + } - // TODO: Try moving to the if() below - if( _n->dead == 0 || ( m_vca_mode == 1 || m_vca_mode == 3 ) ) { - m_sampleCnt = 0; - m_vca_mode = 0; - m_vca_a = 0; - } - else { - m_vca_mode = 2; - } + // Initiate Slide + // TODO: Break out into function, should be called again on detuneChanged + if (vco_slideinc) { + vco_slide = vco_inc-vco_slideinc; + vco_slidebase = vco_inc; + vco_slideinc = 0; + } + else { + vco_slide = 0; + } + // End break-out - // Initiate Slide - // TODO: Break out into function, should be called again on detuneChanged - if( m_vco_slideinc ) { - m_vco_slide = m_vco_inc - m_vco_slideinc; - m_vco_slidebase = m_vco_inc; - m_vco_slideinc = 0; - } - else { - m_vco_slide = 0; - } - // End break-out + // Slide note, save inc for next note + if (slideToggle->value()) { + vco_slideinc = vco_inc; // May need to equal vco_slidebase+vco_slide if last note slid + } - // Slide note, save inc for next note - if( m_slideToggle->value() ) { - // May need to equal vco_slidebase+vco_slide if last note slid - m_vco_slideinc = m_vco_inc; - } - recalcFilter(); + recalcFilter(); + + if(n->dead ==0){ + // Swap next two blocks?? + vcf->playNote(); + // Ensure envelope is recalculated + vcf_envpos = ENVINC; - if( _n->dead == 0 ) { - // Swap next two blocks?? - m_vcf->playNote(); - // Ensure envelope is recalculated - m_vcf_envpos = ENVINC; - - // Better safe then sorry - m_vca_mode = 0; - m_vca_a = 0.0; - } + // Double Check + vca_mode = 0; + vca_a = 0.0; + } } @@ -887,21 +846,18 @@ void lb302Synth::playNote( notePlayHandle * _n, bool ) // per default we resume at the last played frames - only in // some special-cases (which we catch below) we have to resume // somewhere else - f_cnt_t resumePos = m_lastFramesPlayed - 1; + f_cnt_t resume_pos = lastFramesPlayed-1; - bool decayNote = false; + bool decay_note = false; // find out which situation we're in constNotePlayHandleVector v = - notePlayHandle::nphsOfInstrumentTrack( - getInstrumentTrack(), TRUE ); - + notePlayHandle::nphsOfInstrumentTrack( getInstrumentTrack(), TRUE ); // more than one note running? if( v.count() > 1 ) { - const notePlayHandle * on = v.first(); // oldest note - const notePlayHandle * yn = v.last(); // youngest note - + const notePlayHandle * on = v.first(); // oldest note + const notePlayHandle * yn = v.last(); // youngest note // are we playing a released note and the new (youngest) note // has taken over successfully (i.e. played more than the // difference of the two offsets)? @@ -909,7 +865,7 @@ void lb302Synth::playNote( notePlayHandle * _n, bool ) yn->totalFramesPlayed() >= yn->offset() - on->offset() ) { // then we do not need to play something anymore - return; + return; } // have to fill up the frames left to the new note so limit @@ -933,103 +889,120 @@ void lb302Synth::playNote( notePlayHandle * _n, bool ) // the offset of the youngest note, otherwise we get // frames with both waves overlapped engine::getMixer()->clearAudioBuffer( - _n->getInstrumentTrack()->getAudioPort()->firstBuffer(), - framesPerPeriod - yn->offset(), - yn->offset() ); - - resumePos = yn->offset() - on->offset() - 1; + _n->getInstrumentTrack()->getAudioPort()-> + firstBuffer(), + framesPerPeriod - yn->offset(), yn->offset() ); + resume_pos = yn->offset() - on->offset() - 1; // make sure we have positive value, otherwise we're // accessing states out of borders - while( resumePos < 0 ) + while( resume_pos < 0 ) { - resumePos += framesPerPeriod; + resume_pos += framesPerPeriod; } - decayNote = true; - } + decay_note = true; + } } +#ifdef LB_DEBUG + if( _n->released() ) + { + } + else + { + } + +#endif + ///=== WEIRD CODE FOR MONOPHONIC BEHAVIOUR - END === /// /// Malloc our period history buffer - if( m_periodStates == NULL ) { - m_periodStates = new lb302State[framesPerPeriod]; + if (period_states == NULL) { + period_states = new lb302State[framesPerPeriod]; - for( int i = 0; i < framesPerPeriod; i++ ) { - m_periodStates[i].vco_c = m_vco_c; - m_periodStates[i].vca_a = m_vca_a; // Doesn't change anything (currently) - m_periodStates[i].vca_mode = m_vca_mode; // Doesn't change anything (currently) - m_periodStates[i].sampleCnt = m_sampleCnt; // Doesn't change anything (currently) - m_vcf->getState( &m_periodStates[i].fs ); - } - } + for (int i=0; i < framesPerPeriod; i++) { + period_states[i].vco_c = vco_c; + period_states[i].vca_a = vca_a; // Doesn't change anything (currently) + period_states[i].vca_mode = vca_mode; // Doesn't change anything (currently) + period_states[i].sample_cnt = sample_cnt; // Doesn't change anything (currently) + vcf->getState(&period_states[i].fs); + } + } - // now resume at the proper position and process as usual - lb302State * state = &m_periodStates[resumePos]; + // now resume at the proper position and process as usual + lb302State *state = &period_states[resume_pos]; - /// Actually resume the state, now that we have the right state object. - m_vco_c = state->vco_c; - m_vca_a = state->vca_a; - m_vca_mode = state->vca_mode; - m_sampleCnt = state->sampleCnt; - m_vcf->setState( &state->fs ); + /// Actually resume the state, now that we have the right state object. + vco_c = state->vco_c; + vca_a = state->vca_a; + vca_mode = state->vca_mode; + sample_cnt = state->sample_cnt; + vcf->setState(&state->fs); - m_releaseFrame = _n->framesLeft() - desiredReleaseFrames(); + /// Currently have release/decay disabled + // Start the release decay if this is the first release period. + //if (_n->released() && catch_decay == 0) + // catch_decay = 1; + - if( _n->totalFramesPlayed() <= 0 ) { + release_frame = _n->framesLeft() - desiredReleaseFrames(); - // Existing note. Allow it to decay. - if( m_deadToggle->value() == 0 && decayNote ) { + if ( _n->totalFramesPlayed() <= 0 ) { + /// This code is obsolete, hence the "if false" + + // Existing note. Allow it to decay. + if(deadToggle->value()==0 && decay_note) { #ifdef LB_DECAY - if( m_catchDecay < 1 ) { - // BEGIN NOT SURE OF... - //lb302State *st = &period_states[period_states_cnt-1]; - //vca_a = st->vca_a; - //sample_cnt = st->sample_cnt; - // END NOT SURE OF + if (catch_decay < 1) { + // BEGIN NOT SURE OF... + //lb302State *st = &period_states[period_states_cnt-1]; + //vca_a = st->vca_a; + //sample_cnt = st->sample_cnt; + // END NOT SURE OF - // Reserve this note for retrigger in process() - m_holdNote.vco_inc = _n->frequency() * m_vco_detune/LB_HZ; - m_holdNote.dead = m_deadToggle->value(); - m_useHoldNote = true; - m_catchDecay = 1; - } + // Reserve this note for retrigger in process() + hold_note.vco_inc = _n->frequency()*vco_detune/LB_HZ; // TODO: Use actual sampling rate. + hold_note.dead = deadToggle->value(); + use_hold_note = true; + catch_decay = 1; + } + else { + } #else - lb302Note note; - note.vco_inc = _n->frequency() * m_vco_detune/LB_HZ; - note.dead = m_deadToggle->value(); - - initNote( ¬e ); - m_vca_mode = 0; - m_vca_a = state->vca_a; + lb302Note note; + note.vco_inc = _n->frequency()*vco_detune/LB_HZ; // TODO: Use actual sampling rate. + note.dead = deadToggle->value(); + initNote(¬e); + vca_mode=0; + vca_a = state->vca_a; #endif - } - /// Start a new note. - else { - lb302Note note; - note.vco_inc = _n->frequency() * m_vco_detune/LB_HZ; - note.dead = m_deadToggle->value(); - - initNote( ¬e ); - m_useHoldNote = false; - } - } + } + /// Start a new note. + else { + lb302Note note; + note.vco_inc = _n->frequency()*vco_detune/LB_HZ; // TODO: Use actual sampling rate. + note.dead = deadToggle->value(); + initNote(¬e); + use_hold_note = false; + } + } + sampleFrame *buf = new sampleFrame[frames]; - process( buf, frames ); - getInstrumentTrack()->processAudioBuffer( buf, frames, _n ); + process(buf, frames); + getInstrumentTrack()->processAudioBuffer( buf, frames, _n ); - delete[] buf; + delete[] buf; - m_lastFramesPlayed = frames; - //_n->framesLeftForCurrentPeriod(); //_n->totalFramesPlayed(); + lastFramesPlayed = frames;//_n->framesLeftForCurrentPeriod(); //_n->totalFramesPlayed(); } + void lb302Synth::deleteNotePluginData( notePlayHandle * _n ) { } @@ -1039,12 +1012,13 @@ void lb302Synth::deleteNotePluginData( notePlayHandle * _n ) extern "C" { - // neccessary for getting instance out of shared lib - plugin * lmms_plugin_main( void * _data ) - { - return( new lb302Synth( - static_cast( _data ) ) ); - } +// neccessary for getting instance out of shared lib +plugin * lmms_plugin_main( void * _data ) +{ +return( new lb302Synth( + static_cast( _data ) ) ); +} + } diff --git a/plugins/lb302/lb302.h b/plugins/lb302/lb302.h index 54f8b0efe..0311394e7 100644 --- a/plugins/lb302/lb302.h +++ b/plugins/lb302/lb302.h @@ -3,12 +3,12 @@ * emulate the Roland TB303 bass synth * * Copyright (c) 2006-2007 Paul Giblock - * + * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * lb302FilterIIR2 is based on the gsyn filter code by Andy Sloane. - * - * lb302Filter3Pole is based on the TB303 instrument written by + * + * lb302Filter3Pole is based on the TB303 instrument written by * Josep M Comajuncosas for the CSounds library * * This program is free software; you can redistribute it and/or @@ -41,281 +41,260 @@ class knob; class notePlayHandle; - -// lb302FilterKnobState -// Bridges the lb302 plugin and it's active filter. -struct lb302FilterKnobState +class lb302FilterKnobState { -public: - float cutoff; - float reso; - float envmod; - float envdecay; - float dist; + public: + float cutoff; + float reso; + float envmod; + float envdecay; + float dist; }; - -// lb302FilterIIR2State -// State of the IIR2 filter, separated for achieving gapless playback -struct lb302FilterIIR2State +class lb302FilterIIR2State { -public: - float c0; - float a; - float b; - float c; - float d1; - float d2; + public: + float vcf_c0; + float vcf_a; + float vcf_b; + float vcf_c; + float vcf_d1; + float vcf_d2; }; - -// lb302Filter3PoleState -// State of the 3-Pole filter, separated for achieving gapless playback -struct lb302Filter3PoleState +class lb302Filter3PoleState { -public: - float c0; - float kp; - float kp1h; - float kres; - float ay1; - float ay2; - float lastin; - float value; - float aout; + public: + float vcf_c0; + float kp, + kp1h, + kres, + ay1, + ay2, + lastin, + value; + float aout; }; - - -// lb302FilterState -// Captured state of the active filter. Allows lb302State to generically -// hold on to one state or the other, without caring. -typedef union + +typedef union { - lb302FilterIIR2State iir; - lb302Filter3PoleState pole; -} + lb302FilterIIR2State iir; + lb302Filter3PoleState pole; +} lb302FilterState; -// lb302Filter -// Used to filter the lb302. Uses FilterKnobState pointer as input class lb302Filter { -public: - lb302Filter( lb302FilterKnobState* _fs ); - virtual ~lb302Filter( void ) {}; + public: + lb302Filter(lb302FilterKnobState* p_fs); + virtual ~lb302Filter() {}; - virtual void recalc( void ); - virtual void envRecalc( void ); - virtual float process( const float & _samp ) = 0; - virtual void playNote( void ); + virtual void recalc(); + virtual void envRecalc(); + virtual float process(const float& samp)=0; + virtual void playNote(); - virtual void getState( lb302FilterState * _fs ) = 0; - virtual void setState( const lb302FilterState * _fs ) = 0; + virtual void getState(lb302FilterState* fs)=0; + virtual void setState(const lb302FilterState* fs)=0; -protected: - lb302FilterKnobState *m_fs; - - // Filter Decay - float m_c0; // c0=e1 on retrigger; c0*=ed every sample; - float m_e0; // e0 and e1 for interpolation - float m_e1; - float m_rescoeff; // Resonance coefficient [0.30,9.54] + protected: + lb302FilterKnobState *fs; + + // Filter Decay + float vcf_c0; // c0=e1 on retrigger; c0*=ed every sample; cutoff=e0+c0 + float vcf_e0, // e0 and e1 for interpolation + vcf_e1; + float vcf_rescoeff; // Resonance coefficient [0.30,9.54] }; - -// lb302FilterIIR2 -// The IIR2 filter implementation class lb302FilterIIR2 : public lb302Filter { -public: - lb302FilterIIR2( lb302FilterKnobState * _fs ); - virtual ~lb302FilterIIR2( void ); + public: + lb302FilterIIR2(lb302FilterKnobState* p_fs); + virtual ~lb302FilterIIR2(); - virtual void recalc( void ); - virtual void envRecalc( void ); - virtual float process( const float & _samp ); + virtual void recalc(); + virtual void envRecalc(); + virtual float process(const float& samp); - virtual void getState( lb302FilterState * _fs ); - virtual void setState( const lb302FilterState * _fs ); + virtual void getState(lb302FilterState* fs); + virtual void setState(const lb302FilterState* fs); -protected: - float m_d1; // d1 and d2 are added back into the sample with - float m_d2; // vcf_a and b as coefficients. IIR2 resonance - // loop. + protected: + float vcf_d1, // d1 and d2 are added back into the sample with + vcf_d2; // vcf_a and b as coefficients. IIR2 resonance + // loop. - // IIR2 Coefficients for mixing dry and delay. - float m_a; - float m_b; - float m_c; - - effectLib::monoToStereoAdaptor< effectLib::distortion<> > * m_dist_fx; + // IIR2 Coefficients for mixing dry and delay. + float vcf_a, // Mixing coefficients for the final sound. + vcf_b, // + vcf_c; + + effectLib::monoToStereoAdaptor > * m_dist_fx; effectLib::distortion<> * m_dist; }; -// lb302Filter3Pole -// The 3-pole filter implementation class lb302Filter3Pole : public lb302Filter { -public: - lb302Filter3Pole(lb302FilterKnobState * _fs); + public: + lb302Filter3Pole(lb302FilterKnobState* p_fs); - virtual void envRecalc( void ); - virtual void recalc( void ); - virtual float process( const float & _samp ); + //virtual void recalc(); + virtual void envRecalc(); + virtual void recalc(); + virtual float process(const float& samp); - virtual void getState( lb302FilterState * _fs ); - virtual void setState( const lb302FilterState * _fs ); + virtual void getState(lb302FilterState* fs); + virtual void setState(const lb302FilterState* fs); -protected: - float m_kfcn; - float m_kp; - float m_kp1; - float m_kp1h; - float m_kres; - - float m_ay1; - float m_ay2; - float m_aout; - - float m_lastin; - float m_value; + protected: + float kfcn, + kp, + kp1, + kp1h, + kres; + float ay1, + ay2, + aout, + lastin, + value; }; + -// lb302State -// State of the VCA and pointer to VCF state. Used with period states -// in lb302Synth to provide gapless, smooth playback -struct lb302State +class lb302State { public: - float vco_c; - float vca_a; - int vca_mode; - int sampleCnt; + float vco_c; + float vca_a; + int vca_mode; + int sample_cnt; - lb302FilterState fs; + lb302FilterState fs; }; - -// lb302Note -// Description of a note used in lb302: frequency and deadness. Struct -// exists to allow reuse of code contained in initNote(). -struct lb302Note +class lb302Note { public: - float vco_inc; - bool dead; + float vco_inc; + bool dead; }; -// lb302Synth -// Here it is, lb302Synth in all its glory. class lb302Synth : public instrument { - Q_OBJECT - + Q_OBJECT public: lb302Synth( instrumentTrack * _channel_track ); virtual ~lb302Synth(); virtual void FASTCALL playNote( notePlayHandle * _n, - bool _try_parallelizing ); + bool _try_parallelizing ); virtual void FASTCALL deleteNotePluginData( notePlayHandle * _n ); virtual void FASTCALL saveSettings( QDomDocument & _doc, - QDomElement & _parent ); + QDomElement & _parent ); virtual void FASTCALL loadSettings( const QDomElement & _this ); virtual QString nodeName( void ) const; - virtual bool isMonophonic( void ) const { - return true; - } + virtual bool isMonophonic(void) const { + return true; + } virtual f_cnt_t desiredReleaseFrames( void ) const { return 4048; } +private: + + void initNote(lb302Note *note); + + +private: + knob * vcf_cut_knob; + knob * vcf_res_knob; + knob * vcf_dec_knob; + knob * vcf_mod_knob; + + knob * vco_fine_detune_knob; + + knob * dist_knob; + knob * wave_knob; + + ledCheckBox * slideToggle; + ledCheckBox * accentToggle; + ledCheckBox * deadToggle; + ledCheckBox * db24Toggle; + + knob * slide_dec_knob; + public slots: - void filterChanged( float ); - void detuneChanged( float ); - void waveChanged( float ); - void db24Toggled( bool ); + void filterChanged(float); + void detuneChanged(float); + void waveChanged(float); + void db24Toggled( bool ); private: - void initNote( lb302Note * note ); - - knob * m_vcfCutKnob; - knob * m_vcfResKnob; - knob * m_vcfDecKnob; - knob * m_vcfModKnob; - - knob * m_vcoFineDetuneKnob; - - knob * m_distKnob; - knob * m_waveKnob; - - knob * m_slideDecKnob; - - ledCheckBox * m_slideToggle; - ledCheckBox * m_accentToggle; - ledCheckBox * m_deadToggle; - ledCheckBox * m_db24Toggle; + private: - void recalcFilter( void ); + // Oscillator + float vco_inc, // Sample increment for the frequency. Creates Sawtooth. + vco_k, // Raw oscillator sample [-0.5,0.5] + vco_c; // Raw oscillator sample [-0.5,0.5] - int process( sampleFrame *outbuf, const Uint32 size ); + float vco_slide, //* Current value of slide exponential curve. Nonzero=sliding + vco_slideinc, //* Slide base to use in next node. Nonzero=slide next note + vco_slidebase; //* The base vco_inc while sliding. - // Oscillator - enum vcoShape { SAWTOOTH, INVERTED_SAWTOOTH, SQUARE, TRIANGLE, MOOG, ROUND_SQUARE }; - vcoShape m_vco_shape; + float vco_detune; - float m_vco_slide; //* Current value of slide exponential curve. Nonzero=sliding - float m_vco_slideinc; //* Slide base to use in next node. Nonzero=slide next note - float m_vco_slidebase; //* The base vco_inc while sliding. + enum vco_shape_t { SAWTOOTH, INVERTED_SAWTOOTH, SQUARE, TRIANGLE, MOOG, ROUND_SQUARE }; + vco_shape_t vco_shape; - float m_vco_detune; - float m_vco_inc; // Sample increment for the frequency. Creates Sawtooth. - float m_vco_k; // Raw oscillator sample [-0.5,0.5] - float m_vco_c; // Raw oscillator sample [-0.5,0.5] + // User settings + lb302FilterKnobState fs; + lb302Filter *vcf; + lb302Note hold_note; + bool use_hold_note; + + int lastFramesPlayed; + int release_frame; - // More States - int m_vcf_envpos; // Update counter. Updates when >= ENVINC + + // More States + int vcf_envpos; // Update counter. Updates when >= ENVINC - // Envelope State - float m_vca_attack; // Amp attack - float m_vca_decay; // Amp decay - float m_vca_a0; // Initial amplifier coefficient - float m_vca_a; // Amplifier coefficient. + float vca_attack, // Amp attack + vca_decay, // Amp decay + vca_a0, // Initial amplifier coefficient + vca_a; // Amplifier coefficient. + + // Envelope State + int vca_mode; // 0: attack, 1: decay, 2: idle - int m_vca_mode; // 0: attack, 1: decay, 2: idle + // My hacks + int sample_cnt; + + int last_offset; - // User settings - lb302FilterKnobState m_fs; - lb302Filter * m_vcf; - lb302Note m_holdNote; + lb302State *period_states; + int period_states_cnt; - bool m_useHoldNote; + int catch_frame; + int catch_decay; - int m_sampleCnt; - int m_releaseFrame; + void recalcFilter(); - int m_catchFrame; - int m_catchDecay; + int process(sampleFrame *outbuf, const Uint32 size); - int m_lastFramesPlayed; - int m_lastOffset; - - lb302State * m_periodStates; - int m_periodStatesCnt; - -}; +} ; #endif