From 40407f6ce647028a79e86456895ec48f1cc09fe3 Mon Sep 17 00:00:00 2001 From: Vesa Date: Sat, 7 Jun 2014 14:33:10 +0300 Subject: [PATCH] LB302: Fix playback bugs (sticking pitch), add bandlimited waves --- plugins/lb302/lb302.cpp | 169 ++++++++++++++++++++++++++++++---------- plugins/lb302/lb302.h | 55 +++++++------ 2 files changed, 158 insertions(+), 66 deletions(-) diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index 83b824d52..331dec240 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -1,14 +1,14 @@ /* - * lb302.cpp - implementation of class lb302 which is a bass synth attempting + * lb302.cpp - implementation of class lb302 which is a bass synth attempting * to emulate the Roland TB303 bass synth * * Copyright (c) 2006-2008 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 @@ -39,6 +39,7 @@ #include "pixmap_button.h" #include "templates.h" #include "tooltip.h" +#include "BandLimitedWave.h" #include "embed.cpp" #include "moc_lb302.cxx" @@ -49,10 +50,10 @@ // // New config // -#define LB_24_IGNORE_ENVELOPE -#define LB_FILTERED +#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 @@ -143,7 +144,7 @@ lb302FilterIIR2::lb302FilterIIR2(lb302FilterKnobState* p_fs) : { m_dist = new DspEffectLibrary::Distortion( 1.0, 1.0f); - + }; @@ -183,7 +184,7 @@ float lb302FilterIIR2::process(const float& samp) vcf_d2 = vcf_d1; vcf_d1 = ret; - if(fs->dist > 0) + if(fs->dist > 0) ret=m_dist->nextSample(ret); // output = IIR2 + dry @@ -200,7 +201,7 @@ lb302Filter3Pole::lb302Filter3Pole(lb302FilterKnobState *p_fs) : ay1(0), ay2(0), aout(0), - lastin(0) + lastin(0) { }; @@ -225,7 +226,7 @@ void lb302Filter3Pole::envRecalc() w = vcf_e0 + vcf_c0; 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)*(engine::mixer()->processingSampleRate()/2.f-6000.f)) * + (700.f+1500.f*(k)+(1500.f+(k)*(engine::mixer()->processingSampleRate()/2.f-6000.f)) * (fs->envmod)) ); //+iacc*(.3+.7*kfco*kenvmod)*kaccent*kaccurve*2000 @@ -249,7 +250,7 @@ void lb302Filter3Pole::envRecalc() } -float lb302Filter3Pole::process(const float& samp) +float lb302Filter3Pole::process(const float& samp) { float ax1 = lastin; float ay11 = ay1; @@ -274,12 +275,12 @@ lb302Synth::lb302Synth( InstrumentTrack * _instrumentTrack ) : vcf_mod_knob( 0.1f, 0.0f, 1.0f, 0.005f, this, tr( "VCF Envelope Mod" ) ), vcf_dec_knob( 0.1f, 0.0f, 1.0f, 0.005f, this, tr( "VCF Envelope Decay" ) ), dist_knob( 0.0f, 0.0f, 1.0f, 0.01f, this, tr( "Distortion" ) ), - wave_shape( 0.0f, 0.0f, 7.0f, this, tr( "Waveform" ) ), + wave_shape( 8.0f, 0.0f, 11.0f, this, tr( "Waveform" ) ), slide_dec_knob( 0.6f, 0.0f, 1.0f, 0.005f, this, tr( "Slide Decay" ) ), slideToggle( false, this, tr( "Slide" ) ), accentToggle( false, this, tr( "Accent" ) ), deadToggle( false, this, tr( "Dead" ) ), - db24Toggle( false, this, tr( "24dB/oct Filter" ) ) + db24Toggle( false, this, tr( "24dB/oct Filter" ) ) { @@ -330,7 +331,7 @@ lb302Synth::lb302Synth( InstrumentTrack * _instrumentTrack ) : vca_attack = 1.0 - 0.96406088; vca_decay = 0.99897516; - vco_shape = SAWTOOTH; + vco_shape = SAWTOOTH; // Experimenting with a0 between original (0.5) and 1.0 vca_a0 = 0.5; @@ -425,7 +426,7 @@ void lb302Synth::filterChanged() void lb302Synth::db24Toggled() { vcf = vcfs[db24Toggle.value()]; - // These recalcFilter calls might suck + // These recalcFilter calls might suck recalcFilter(); } @@ -487,14 +488,14 @@ int lb302Synth::process(sampleFrame *outbuf, const int size) note.dead = deadToggle.value(); initNote(¬e); //printf("%f %f, ", vco_inc, vco_c); - - current_freq = new_freq; + + current_freq = new_freq; new_freq = -1.0f; //printf("GOT_INC %f %f %d\n\n", note.vco_inc, new_freq, vca_mode ); - } + } + - // TODO: NORMAL RELEASE // vca_mode = 1; @@ -543,6 +544,10 @@ int lb302Synth::process(sampleFrame *outbuf, const int size) case 5: vco_shape = SINE; break; case 6: vco_shape = EXPONENTIAL; break; case 7: vco_shape = WHITE_NOISE; break; + case 8: vco_shape = BL_SAWTOOTH; break; + case 9: vco_shape = BL_SQUARE; break; + case 10: vco_shape = BL_TRIANGLE; break; + case 11: vco_shape = BL_MOOG; break; default: vco_shape = SAWTOOTH; break; } @@ -568,7 +573,7 @@ int lb302Synth::process(sampleFrame *outbuf, const int size) 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 + // [-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 ; @@ -577,7 +582,7 @@ int lb302Synth::process(sampleFrame *outbuf, const int size) 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 + vco_k *= 2.0; // MOOG wave gets filtered away break; case SINE: @@ -592,6 +597,22 @@ int lb302Synth::process(sampleFrame *outbuf, const int size) case WHITE_NOISE: vco_k = 0.5 * Oscillator::noiseSample( vco_c ); break; + + case BL_SAWTOOTH: + vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::BLSaw ) * 0.5f; + break; + + case BL_SQUARE: + vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::BLSquare ) * 0.5f; + break; + + case BL_TRIANGLE: + vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::BLTriangle ) * 0.5f; + break; + + case BL_MOOG: + vco_k = BandLimitedWave::oscillate( vco_c + 0.5f, BandLimitedWave::pdToLen( vco_inc ), BandLimitedWave::BLMoog ); + break; } //vca_a = 0.5; @@ -600,8 +621,8 @@ int lb302Synth::process(sampleFrame *outbuf, const int size) //samp = vcf->process(vco_k)*2.0*vca_a; //samp = vcf->process(vco_k)*2.0; samp = filter->process(vco_k) * vca_a; - //printf("%f %d\n", vco_c, sample_cnt); - + //printf("%f %d\n", vco_c, sample_cnt); + //samp = vco_k * vca_a; @@ -609,7 +630,7 @@ int lb302Synth::process(sampleFrame *outbuf, const int size) { // vca_a = 0; } - + #else //samp = vco_k*vca_a; #endif @@ -633,7 +654,7 @@ int lb302Synth::process(sampleFrame *outbuf, const int size) // Handle Envelope if(vca_mode==0) { vca_a+=(vca_a0-vca_a)*vca_attack; - if(sample_cnt>=0.5*engine::mixer()->processingSampleRate()) + if(sample_cnt>=0.5*engine::mixer()->processingSampleRate()) vca_mode = 2; } else if(vca_mode == 1) { @@ -661,7 +682,7 @@ void lb302Synth::initNote( lb302Note *n) catch_decay = 0; vco_inc = n->vco_inc; - + // Always reset vca on non-dead notes, and // Only reset vca on decaying(decayed) and never-played if(n->dead == 0 || (vca_mode==1 || vca_mode==3)) { @@ -695,14 +716,14 @@ void lb302Synth::initNote( lb302Note *n) recalcFilter(); - + if(n->dead ==0){ // Swap next two blocks?? vcf->playNote(); // Ensure envelope is recalculated vcf_envpos = ENVINC; - // Double Check + // Double Check //vca_mode = 0; //vca_a = 0.0; } @@ -711,13 +732,30 @@ void lb302Synth::initNote( lb302Note *n) void lb302Synth::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) { - //fpp_t framesPerPeriod = engine::mixer()->framesPerPeriod(); - if( _n->isMasterNote() ) { return; } + // sort notes: new notes to the end + m_notesMutex.lock(); + if( _n->totalFramesPlayed() == 0 ) + { + m_notes.append( _n ); + } + else + { + m_notes.prepend( _n ); + } + m_notesMutex.unlock(); +} + + + +void lb302Synth::processNote( NotePlayHandle * _n ) +{ + //fpp_t framesPerPeriod = engine::mixer()->framesPerPeriod(); + // Currently have release/decay disabled // Start the release decay if this is the first release period. //if (_n->released() && catch_decay == 0) @@ -731,7 +769,7 @@ void lb302Synth::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) //LB303 if ( _n->totalFramesPlayed() <= 0 ) { // This code is obsolete, hence the "if false" - // Existing note. Allow it to decay. + // Existing note. Allow it to decay. if(deadToggle.value() == 0 && decay_note) { /* lb302Note note; @@ -743,7 +781,7 @@ void lb302Synth::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) } /// Start a new note. - else if( _n->totalFramesPlayed() == 0 ) { + else if( _n->m_pluginData == NULL ) { new_freq = _n->unpitchedFrequency(); true_freq = _n->frequency(); _n->m_pluginData = this; @@ -770,10 +808,14 @@ void lb302Synth::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer ) void lb302Synth::play( sampleFrame * _working_buffer ) { - //printf("."); + while( ! m_notes.isEmpty() ) + { + processNote( m_notes.takeFirst() ); + }; + const fpp_t frames = engine::mixer()->framesPerPeriod(); - process( _working_buffer, frames); + process( _working_buffer, frames); instrumentTrack()->processAudioBuffer( _working_buffer, frames, NULL ); } @@ -783,7 +825,7 @@ void lb302Synth::play( sampleFrame * _working_buffer ) void lb302Synth::deleteNotePluginData( NotePlayHandle * _n ) { //printf("GONE\n"); - if( _n->unpitchedFrequency() == current_freq ) + if( _n->unpitchedFrequency() == current_freq ) { delete_freq = current_freq; } @@ -831,7 +873,7 @@ lb302SynthView::lb302SynthView( Instrument * _instrument, QWidget * _parent ) : m_deadToggle->move( 10, 220 ); m_db24Toggle = new ledCheckBox( "", this ); - m_db24Toggle->setWhatsThis( + m_db24Toggle->setWhatsThis( tr( "303-es-que, 24dB/octave, 3 pole filter" ) ); m_db24Toggle->move( 10, 150); @@ -888,8 +930,8 @@ lb302SynthView::lb302SynthView( Instrument * _instrument, QWidget * _parent ) : "round_square_wave_inactive" ) ); toolTip::add( roundSqrWaveBtn, tr( "Click here for a square-wave with a rounded end." ) ); - - pixmapButton * moogWaveBtn = + + pixmapButton * moogWaveBtn = new pixmapButton( this, tr( "Moog wave" ) ); moogWaveBtn->move( waveBtnX+(16*4), waveBtnY ); moogWaveBtn->setActiveGraphic( @@ -929,6 +971,47 @@ lb302SynthView::lb302SynthView( Instrument * _instrument, QWidget * _parent ) : toolTip::add( whiteNoiseWaveBtn, tr( "Click here for white-noise." ) ); + pixmapButton * blSawWaveBtn = + new pixmapButton( this, tr( "Bandlimited saw wave" ) ); + blSawWaveBtn->move( waveBtnX+(16*9)-8, waveBtnY ); + blSawWaveBtn->setActiveGraphic( + embed::getIconPixmap( "saw_wave_active" ) ); + blSawWaveBtn->setInactiveGraphic( + embed::getIconPixmap( "saw_wave_inactive" ) ); + toolTip::add( blSawWaveBtn, + tr( "Click here for bandlimited saw wave." ) ); + + pixmapButton * blSquareWaveBtn = + new pixmapButton( this, tr( "Bandlimited square wave" ) ); + blSquareWaveBtn->move( waveBtnX+(16*10)-8, waveBtnY ); + blSquareWaveBtn->setActiveGraphic( + embed::getIconPixmap( "square_wave_active" ) ); + blSquareWaveBtn->setInactiveGraphic( + embed::getIconPixmap( "square_wave_inactive" ) ); + toolTip::add( blSquareWaveBtn, + tr( "Click here for bandlimited square wave." ) ); + + pixmapButton * blTriangleWaveBtn = + new pixmapButton( this, tr( "Bandlimited triangle wave" ) ); + blTriangleWaveBtn->move( waveBtnX+(16*11)-8, waveBtnY ); + blTriangleWaveBtn->setActiveGraphic( + embed::getIconPixmap( "triangle_wave_active" ) ); + blTriangleWaveBtn->setInactiveGraphic( + embed::getIconPixmap( "triangle_wave_inactive" ) ); + toolTip::add( blTriangleWaveBtn, + tr( "Click here for bandlimited triangle wave." ) ); + + pixmapButton * blMoogWaveBtn = + new pixmapButton( this, tr( "Bandlimited moog saw wave" ) ); + blMoogWaveBtn->move( waveBtnX+(16*12)-8, waveBtnY ); + blMoogWaveBtn->setActiveGraphic( + embed::getIconPixmap( "moog_saw_wave_active" ) ); + blMoogWaveBtn->setInactiveGraphic( + embed::getIconPixmap( "moog_saw_wave_inactive" ) ); + toolTip::add( blMoogWaveBtn, + tr( "Click here for bandlimited moog saw wave." ) ); + + m_waveBtnGrp = new automatableButtonGroup( this ); m_waveBtnGrp->addButton( sawWaveBtn ); m_waveBtnGrp->addButton( triangleWaveBtn ); @@ -938,6 +1021,10 @@ lb302SynthView::lb302SynthView( Instrument * _instrument, QWidget * _parent ) : m_waveBtnGrp->addButton( sinWaveBtn ); m_waveBtnGrp->addButton( exponentialWaveBtn ); m_waveBtnGrp->addButton( whiteNoiseWaveBtn ); + m_waveBtnGrp->addButton( blSawWaveBtn ); + m_waveBtnGrp->addButton( blSquareWaveBtn ); + m_waveBtnGrp->addButton( blTriangleWaveBtn ); + m_waveBtnGrp->addButton( blMoogWaveBtn ); setAutoFillBackground( true ); QPalette pal; @@ -955,7 +1042,7 @@ lb302SynthView::~lb302SynthView() void lb302SynthView::modelChanged() { lb302Synth * syn = castModel(); - + m_vcfCutKnob->setModel( &syn->vcf_cut_knob ); m_vcfResKnob->setModel( &syn->vcf_res_knob ); m_vcfDecKnob->setModel( &syn->vcf_dec_knob ); @@ -964,7 +1051,7 @@ void lb302SynthView::modelChanged() m_distKnob->setModel( &syn->dist_knob ); m_waveBtnGrp->setModel( &syn->wave_shape ); - + m_slideToggle->setModel( &syn->slideToggle ); m_accentToggle->setModel( &syn->accentToggle ); m_deadToggle->setModel( &syn->deadToggle ); diff --git a/plugins/lb302/lb302.h b/plugins/lb302/lb302.h index b909b2710..b3bfb2100 100644 --- a/plugins/lb302/lb302.h +++ b/plugins/lb302/lb302.h @@ -3,12 +3,12 @@ * emulate the Roland TB303 bass synth * * Copyright (c) 2006-2008 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 @@ -29,8 +29,8 @@ */ -#ifndef _LB302_H_ -#define _LB302_H_ +#ifndef LB302_H_ +#define LB302_H_ #include "DspEffectLibrary.h" #include "Instrument.h" @@ -38,6 +38,8 @@ #include "led_checkbox.h" #include "knob.h" #include "Mixer.h" +#include "NotePlayHandle.h" +#include static const int NUM_FILTERS = 2; @@ -67,12 +69,12 @@ class lb302Filter virtual void playNote(); protected: - lb302FilterKnobState *fs; + 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; + vcf_e1; float vcf_rescoeff; // Resonance coefficient [0.30,9.54] }; @@ -87,13 +89,13 @@ class lb302FilterIIR2 : public lb302Filter virtual float process(const float& samp); protected: - float vcf_d1, // d1 and d2 are added back into the sample with + 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 vcf_a, // Mixing coefficients for the final sound. - vcf_b, // + float vcf_a, // Mixing coefficients for the final sound. + vcf_b, // vcf_c; DspEffectLibrary::Distortion * m_dist; @@ -111,15 +113,15 @@ class lb302Filter3Pole : public lb302Filter virtual float process(const float& samp); protected: - float kfcn, - kp, - kp1, - kp1h, + float kfcn, + kp, + kp1, + kp1h, kres; - float ay1, - ay2, - aout, - lastin, + float ay1, + ay2, + aout, + lastin, value; }; @@ -164,10 +166,10 @@ public: virtual PluginView * instantiateView( QWidget * _parent ); private: + void processNote( NotePlayHandle * n ); void initNote(lb302Note *note); - private: FloatModel vcf_cut_knob; FloatModel vcf_res_knob; @@ -179,7 +181,7 @@ private: FloatModel dist_knob; IntModel wave_shape; FloatModel slide_dec_knob; - + BoolModel slideToggle; BoolModel accentToggle; BoolModel deadToggle; @@ -200,7 +202,8 @@ private: vco_slideinc, //* Slide base to use in next node. Nonzero=slide next note vco_slidebase; //* The base vco_inc while sliding. - enum vco_shape_t { SAWTOOTH, SQUARE, TRIANGLE, MOOG, ROUND_SQUARE, SINE, EXPONENTIAL, WHITE_NOISE }; + enum vco_shape_t { SAWTOOTH, SQUARE, TRIANGLE, MOOG, ROUND_SQUARE, SINE, EXPONENTIAL, WHITE_NOISE, + BL_SAWTOOTH, BL_SQUARE, BL_TRIANGLE, BL_MOOG }; vco_shape_t vco_shape; // Filters (just keep both loaded and switch) @@ -215,9 +218,9 @@ private: // More States int vcf_envpos; // Update counter. Updates when >= ENVINC - float vca_attack, // Amp attack + float vca_attack, // Amp attack vca_decay, // Amp decay - vca_a0, // Initial amplifier coefficient + vca_a0, // Initial amplifier coefficient vca_a; // Amplifier coefficient. // Envelope State @@ -242,6 +245,8 @@ private: friend class lb302SynthView; + NotePlayHandleList m_notes; + QMutex m_notesMutex; } ; @@ -254,7 +259,7 @@ public: private: virtual void modelChanged(); - + knob * m_vcfCutKnob; knob * m_vcfResKnob; knob * m_vcfDecKnob; @@ -263,7 +268,7 @@ private: knob * m_distKnob; knob * m_slideDecKnob; automatableButtonGroup * m_waveBtnGrp; - + ledCheckBox * m_slideToggle; ledCheckBox * m_accentToggle; ledCheckBox * m_deadToggle;