Merge pull request #2552 from michaelgregorius/lb303-deletion
Removes the project lb303 which seems to have been inactive since 2008 (fixes #2551)
This commit is contained in:
@@ -25,7 +25,6 @@ ADD_SUBDIRECTORY(kicker)
|
||||
ADD_SUBDIRECTORY(ladspa_browser)
|
||||
ADD_SUBDIRECTORY(LadspaEffect)
|
||||
ADD_SUBDIRECTORY(lb302)
|
||||
#ADD_SUBDIRECTORY(lb303)
|
||||
ADD_SUBDIRECTORY(MidiImport)
|
||||
ADD_SUBDIRECTORY(MidiExport)
|
||||
ADD_SUBDIRECTORY(MultitapEcho)
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
INCLUDE(BuildPlugin)
|
||||
|
||||
BUILD_PLUGIN(lb303 lb303.cpp lb303.h MOCFILES lb303.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png")
|
||||
@@ -1,87 +0,0 @@
|
||||
LB303 Bass Synth - Known bugs, issues, and missing features
|
||||
-----------------------------------------------------------
|
||||
|
||||
2007.02.03 - First release
|
||||
--------------------------
|
||||
|
||||
Important note, in the current incarnation there are many behaviors,
|
||||
constants, and other items that will be changed. This will result
|
||||
in a change of the instrument's timbre and functionality. Therefore,
|
||||
it would be wise to not make any masterpieces with the synth at
|
||||
this point in time.
|
||||
|
||||
BUG:
|
||||
Fix the awful clicking cause by adjacent notes. This problem seems
|
||||
to be caused by 'unexhausted buffers' That is, the problem manifests
|
||||
itself to a greater degree when the user "buffer size" configuration
|
||||
is increased to over 1024 frames or so. The problem is much less
|
||||
noticable when the buffer size is set to 64 frames.
|
||||
|
||||
BUG:
|
||||
The synth does not make accomodations for sampling rates other than
|
||||
44100. This should be easy to fix, but I haven't gotten around to
|
||||
it; nor do I have the means to test it.
|
||||
|
||||
BUG:
|
||||
I get segfaults now and then. Granted, I've rarely used LMMS
|
||||
without also using LB302, so I do not know if the problem is actually
|
||||
in the Bass Synth. I have gotten the backtraces a few times:
|
||||
...
|
||||
mixer::renderNextBuffer()
|
||||
mixer::nextAudioBuffer()
|
||||
QOBject::activate_signal()
|
||||
??()
|
||||
|
||||
and
|
||||
|
||||
...
|
||||
instrumentTrack::play()
|
||||
notePlayHandle()
|
||||
instrumentTrack::processOutEvent()
|
||||
fadeButton::activate()
|
||||
QSingleShotTimer::start()
|
||||
qStartTimer()
|
||||
qKillTimer()
|
||||
QGList::insertAt()
|
||||
|
||||
TODO:
|
||||
Add accent feature. This isn't as bad as it sounds, but will require
|
||||
some tweaking to get right.
|
||||
|
||||
TODO:
|
||||
LB302 contains code for a short fixed-length default decay. If a
|
||||
released note is using a user-defined VCA from the instrument
|
||||
panel, then LB302 should not do anything special. However, if no
|
||||
envelope is specified, then LB302 should request enough release-frames
|
||||
in order to apply the built-in exponential decay.
|
||||
|
||||
TODO:
|
||||
The original TB303's square wave is not 50% duty cycle. The peaks are
|
||||
supposed to slope down slightly. It would be nice to be able to
|
||||
configure this. In fact, each waveform could have a parameter knob to
|
||||
change some aspect of the wave. See lb302.cpp:643 for more info.
|
||||
|
||||
TODO:
|
||||
Must decide on proper action to take when a slide note is interrupted
|
||||
by another slide note. Right now, the slide-from frequency is always
|
||||
replaced with the prior note. However, we may wish to maintain the
|
||||
current frequency in order to make the sound more continuous.
|
||||
|
||||
TODO:
|
||||
The default filter's distortion could use some work.
|
||||
effectLib::distortion<> gets the job done, but the coefficients need
|
||||
to more closely match that of lb302Filter3Pole's tanh distortion.
|
||||
|
||||
TODO:
|
||||
Slide decay needs a better knob mapping. sqrt()? lb302.cpp:588
|
||||
|
||||
TODO:
|
||||
Consider making the slide trigger set the note to slide TO as opposed
|
||||
to the note to slide FROM. I originally did FROM in order to match
|
||||
the real 303. However, TO may be more intuitive.
|
||||
|
||||
TODO:
|
||||
Various code refactoring, as well as precomputing some values.
|
||||
|
||||
TODO:
|
||||
Remove one of the sawtooth waveforms in favor of the other?
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 112 KiB |
@@ -1,843 +0,0 @@
|
||||
/*
|
||||
* lb303.cpp - implementation of class lb303 which is a bass synth attempting
|
||||
* to emulate the Roland TB303 bass synth
|
||||
*
|
||||
* Copyright (c) 2006-2008 Paul Giblock <pgib/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* lb303FilterIIR2 is based on the gsyn filter code by Andy Sloane.
|
||||
*
|
||||
* lb303Filter3Pole 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
|
||||
* 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 <QDomDocument>
|
||||
|
||||
#include "lb303.h"
|
||||
#include "engine.h"
|
||||
#include "InstrumentPlayHandle.h"
|
||||
#include "InstrumentTrack.h"
|
||||
#include "Knob.h"
|
||||
#include "NotePlayHandle.h"
|
||||
#include "templates.h"
|
||||
#include "audio_port.h"
|
||||
|
||||
#include "embed.cpp"
|
||||
|
||||
|
||||
|
||||
// Envelope Recalculation period
|
||||
#define ENVINC 64
|
||||
|
||||
//
|
||||
// New config
|
||||
//
|
||||
#define LB_24_IGNORE_ENVELOPE
|
||||
#define LB_FILTERED
|
||||
//#define LB_DECAY
|
||||
//#define LB_24_RES_TRICK
|
||||
|
||||
#define LB_DIST_RATIO 4.0
|
||||
#define LB_24_VOL_ADJUST 3.0
|
||||
//#define LB_DECAY_NOTES
|
||||
|
||||
#define LB_DEBUG
|
||||
|
||||
#ifdef LB_DEBUG
|
||||
#include <assert.h>
|
||||
#endif
|
||||
|
||||
//
|
||||
// Old config
|
||||
//
|
||||
|
||||
|
||||
//#define engine::mixer()->processingSampleRate() 44100.0f
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
Plugin::Descriptor PLUGIN_EXPORT lb303_plugin_descriptor =
|
||||
{
|
||||
STRINGIFY( PLUGIN_NAME ),
|
||||
"LB303",
|
||||
QT_TRANSLATE_NOOP( "pluginBrowser",
|
||||
"Incomplete monophonic imitation tb303" ),
|
||||
"Paul Giblock <pgib/at/users.sf.net>",
|
||||
0x0100,
|
||||
Plugin::Instrument,
|
||||
new PluginPixmapLoader( "logo" ),
|
||||
NULL
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
//
|
||||
// lb303Filter
|
||||
//
|
||||
|
||||
lb303Filter::lb303Filter(lb303FilterKnobState* p_fs) :
|
||||
fs(p_fs),
|
||||
vcf_c0(0),
|
||||
vcf_e0(0),
|
||||
vcf_e1(0)
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
void lb303Filter::recalc()
|
||||
{
|
||||
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/engine::mixer()->processingSampleRate();
|
||||
vcf_e1*=M_PI/engine::mixer()->processingSampleRate();
|
||||
vcf_e1 -= vcf_e0;
|
||||
|
||||
vcf_rescoeff = exp(-1.20 + 3.455*(fs->reso));
|
||||
};
|
||||
|
||||
|
||||
void lb303Filter::envRecalc()
|
||||
{
|
||||
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 lb303Filter::playNote()
|
||||
{
|
||||
vcf_c0 = vcf_e1;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// lb303FilterIIR2
|
||||
//
|
||||
|
||||
lb303FilterIIR2::lb303FilterIIR2(lb303FilterKnobState* p_fs) :
|
||||
lb303Filter(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);
|
||||
|
||||
};
|
||||
|
||||
|
||||
lb303FilterIIR2::~lb303FilterIIR2()
|
||||
{
|
||||
delete m_dist;
|
||||
}
|
||||
|
||||
|
||||
void lb303FilterIIR2::recalc()
|
||||
{
|
||||
lb303Filter::recalc();
|
||||
//m_dist->setThreshold(0.5+(fs->dist*2.0));
|
||||
m_dist->setThreshold(fs->dist*75.0);
|
||||
};
|
||||
|
||||
|
||||
void lb303FilterIIR2::envRecalc()
|
||||
{
|
||||
float k, w;
|
||||
|
||||
lb303Filter::envRecalc();
|
||||
|
||||
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 lb303FilterIIR2::process(const float& 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;
|
||||
|
||||
if(fs->dist > 0)
|
||||
ret=m_dist->nextSample(ret);
|
||||
|
||||
// output = IIR2 + dry
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// lb303Filter3Pole
|
||||
//
|
||||
|
||||
lb303Filter3Pole::lb303Filter3Pole(lb303FilterKnobState *p_fs) :
|
||||
lb303Filter(p_fs),
|
||||
ay1(0),
|
||||
ay2(0),
|
||||
aout(0),
|
||||
lastin(0)
|
||||
{
|
||||
};
|
||||
|
||||
|
||||
void lb303Filter3Pole::recalc()
|
||||
{
|
||||
// DO NOT CALL BASE CLASS
|
||||
vcf_e0 = 0.000001;
|
||||
vcf_e1 = 1.0;
|
||||
}
|
||||
|
||||
|
||||
// TODO: Try using k instead of vcf_reso
|
||||
void lb303Filter3Pole::envRecalc()
|
||||
{
|
||||
float w,k;
|
||||
float kfco;
|
||||
|
||||
lb303Filter::envRecalc();
|
||||
|
||||
// e0 is adjusted for Hz and doesn't need ENVINC
|
||||
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)) *
|
||||
(fs->envmod)) );
|
||||
//+iacc*(.3+.7*kfco*kenvmod)*kaccent*kaccurve*2000
|
||||
|
||||
|
||||
#ifdef LB_24_IGNORE_ENVELOPE
|
||||
// kfcn = fs->cutoff;
|
||||
kfcn = 2.0 * kfco / engine::mixer()->processingSampleRate();
|
||||
#else
|
||||
kfcn = w;
|
||||
#endif
|
||||
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/vcf_rescoeff);
|
||||
kres = (((k))) * (((-2.7079*kp1 + 10.963)*kp1 - 14.934)*kp1 + 8.4974);
|
||||
#else
|
||||
kres = (((fs->reso))) * (((-2.7079*kp1 + 10.963)*kp1 - 14.934)*kp1 + 8.4974);
|
||||
#endif
|
||||
value = 1.0+( (fs->dist) *(1.5 + 2.0*kres*(1.0-kfcn))); // ENVMOD was DIST
|
||||
}
|
||||
|
||||
|
||||
float lb303Filter3Pole::process(const float& samp)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// LBSynth
|
||||
//
|
||||
|
||||
lb303Synth::lb303Synth( InstrumentTrack * _InstrumentTrack ) :
|
||||
instrument( _InstrumentTrack, &lb303_plugin_descriptor ),
|
||||
vcf_cut_knob( 0.75f, 0.0f, 1.5f, 0.005f, this, tr( "VCF Cutoff Frequency" ) ),
|
||||
vcf_res_knob( 0.75f, 0.0f, 1.25f, 0.005f, this, tr( "VCF Resonance" ) ),
|
||||
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_knob( 0.0f, 0.0f, 5.0f, 1.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" ) )
|
||||
|
||||
{
|
||||
|
||||
connect( engine::mixer(), SIGNAL( sampleRateChanged( ) ),
|
||||
this, SLOT ( filterChanged( ) ) );
|
||||
|
||||
connect( &vcf_cut_knob, SIGNAL( dataChanged( ) ),
|
||||
this, SLOT ( filterChanged( ) ) );
|
||||
|
||||
connect( &vcf_res_knob, SIGNAL( dataChanged( ) ),
|
||||
this, SLOT ( filterChanged( ) ) );
|
||||
|
||||
connect( &vcf_mod_knob, SIGNAL( dataChanged( ) ),
|
||||
this, SLOT ( filterChanged( ) ) );
|
||||
|
||||
connect( &vcf_dec_knob, SIGNAL( dataChanged( ) ),
|
||||
this, SLOT ( filterChanged( ) ) );
|
||||
|
||||
connect( &db24Toggle, SIGNAL( dataChanged( ) ),
|
||||
this, SLOT ( db24Toggled( ) ) );
|
||||
|
||||
connect( &dist_knob, SIGNAL( dataChanged( ) ),
|
||||
this, SLOT ( filterChanged( )));
|
||||
|
||||
|
||||
// SYNTH
|
||||
|
||||
vco_inc = 0.0;
|
||||
vco_c = 0;
|
||||
vco_k = 0;
|
||||
|
||||
vco_slide = 0; vco_slideinc = 0;
|
||||
vco_slidebase = 0;
|
||||
|
||||
fs.cutoff = 0;
|
||||
fs.envmod = 0;
|
||||
fs.reso = 0;
|
||||
fs.envdecay = 0;
|
||||
fs.dist = 0;
|
||||
|
||||
vcf_envpos = ENVINC;
|
||||
|
||||
// Start VCA on an attack.
|
||||
vca_mode = 3;
|
||||
vca_a = 0;
|
||||
|
||||
//vca_attack = 1.0 - 0.94406088;
|
||||
vca_attack = 1.0 - 0.96406088;
|
||||
vca_decay = 0.99897516;
|
||||
|
||||
vco_shape = SAWTOOTH;
|
||||
|
||||
// Experimenting with a0 between original (0.5) and 1.0
|
||||
vca_a0 = 0.5;
|
||||
vca_a = 9;
|
||||
vca_mode = 3;
|
||||
|
||||
vcf = new lb303FilterIIR2(&fs);
|
||||
|
||||
sample_cnt = 0;
|
||||
release_frame = 1<<24;
|
||||
catch_frame = 0;
|
||||
catch_decay = 0;
|
||||
|
||||
recalcFilter();
|
||||
|
||||
last_offset = 0;
|
||||
|
||||
new_freq = -1;
|
||||
current_freq = -1;
|
||||
delete_freq = -1;
|
||||
|
||||
instrumentPlayHandle * iph = new instrumentPlayHandle( this );
|
||||
engine::mixer()->addPlayHandle( iph );
|
||||
|
||||
filterChanged();
|
||||
}
|
||||
|
||||
|
||||
lb303Synth::~lb303Synth()
|
||||
{
|
||||
delete vcf;
|
||||
}
|
||||
|
||||
|
||||
void lb303Synth::saveSettings( QDomDocument & _doc,
|
||||
QDomElement & _this )
|
||||
{
|
||||
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" );
|
||||
|
||||
wave_knob.saveSettings( _doc, _this, "shape");
|
||||
dist_knob.saveSettings( _doc, _this, "dist");
|
||||
slide_dec_knob.saveSettings( _doc, _this, "slide_dec");
|
||||
|
||||
slideToggle.saveSettings( _doc, _this, "slide");
|
||||
deadToggle.saveSettings( _doc, _this, "dead");
|
||||
db24Toggle.saveSettings( _doc, _this, "db24");
|
||||
}
|
||||
|
||||
|
||||
void lb303Synth::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
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" );
|
||||
|
||||
dist_knob.loadSettings( _this, "dist");
|
||||
wave_knob.loadSettings( _this, "shape");
|
||||
slide_dec_knob.loadSettings( _this, "slide_dec");
|
||||
|
||||
slideToggle.loadSettings( _this, "slide");
|
||||
deadToggle.loadSettings( _this, "dead");
|
||||
db24Toggle.loadSettings( _this, "db24");
|
||||
|
||||
filterChanged();
|
||||
}
|
||||
|
||||
// TODO: Split into one function per knob. envdecay doesn't require
|
||||
// recalcFilter.
|
||||
void lb303Synth::filterChanged()
|
||||
{
|
||||
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*vcf_dec_knob.value());
|
||||
|
||||
d *= engine::mixer()->processingSampleRate(); // 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 lb303Synth::db24Toggled()
|
||||
{
|
||||
delete vcf;
|
||||
if(db24Toggle.value()) {
|
||||
vcf = new lb303Filter3Pole(&fs);
|
||||
}
|
||||
else {
|
||||
vcf = new lb303FilterIIR2(&fs);
|
||||
}
|
||||
recalcFilter();
|
||||
}
|
||||
|
||||
|
||||
|
||||
QString lb303Synth::nodeName() const
|
||||
{
|
||||
return( lb303_plugin_descriptor.name );
|
||||
}
|
||||
|
||||
|
||||
void lb303Synth::recalcFilter()
|
||||
{
|
||||
vcf->recalc();
|
||||
|
||||
vcf_envpos = ENVINC; // Trigger filter update in process()
|
||||
}
|
||||
|
||||
inline int MIN(int a, int b) {
|
||||
return (a<b)?a:b;
|
||||
}
|
||||
|
||||
inline float GET_INC(float freq) {
|
||||
return freq/engine::mixer()->processingSampleRate(); // TODO: Use actual sampling rate.
|
||||
}
|
||||
|
||||
int lb303Synth::process(sampleFrame *outbuf, const Uint32 size)
|
||||
{
|
||||
unsigned int i;
|
||||
float w;
|
||||
float samp;
|
||||
|
||||
if( delete_freq == current_freq ) {
|
||||
// Normal release
|
||||
delete_freq = -1;
|
||||
vca_mode = 1;
|
||||
}
|
||||
|
||||
if( new_freq > 0.0f ) {
|
||||
lb303Note note;
|
||||
note.vco_inc = GET_INC( true_freq );
|
||||
note.dead = deadToggle.value();
|
||||
initNote(¬e);
|
||||
|
||||
current_freq = new_freq;
|
||||
|
||||
new_freq = -1.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// TODO: NORMAL RELEASE
|
||||
// vca_mode = 1;
|
||||
|
||||
for(i=0;i<size;i++) {
|
||||
|
||||
// update vcf
|
||||
if(vcf_envpos >= ENVINC) {
|
||||
vcf->envRecalc();
|
||||
|
||||
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
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
sample_cnt++;
|
||||
vcf_envpos++;
|
||||
|
||||
int decay_frames = 128;
|
||||
|
||||
// update vco
|
||||
vco_c += vco_inc;
|
||||
|
||||
if(vco_c > 0.5)
|
||||
vco_c -= 1.0;
|
||||
|
||||
/*LB303
|
||||
if (catch_decay > 0) {
|
||||
if (catch_decay < decay_frames) {
|
||||
catch_decay++;
|
||||
}
|
||||
}*/
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
case INVERTED_SAWTOOTH: // p0: curviness of line
|
||||
vco_k = -vco_c; // Is this sawtooth backwards?
|
||||
break;
|
||||
|
||||
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 SQUARE: // p0: slope of top
|
||||
vco_k = (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 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;
|
||||
}
|
||||
|
||||
// Write out samples.
|
||||
samp = vcf->process(vco_k) * vca_a;
|
||||
|
||||
/*
|
||||
float releaseFrames = desiredReleaseFrames();
|
||||
samp *= (releaseFrames - catch_decay)/releaseFrames;
|
||||
*/
|
||||
//LB303 samp *= (float)(decay_frames - catch_decay)/(float)decay_frames;
|
||||
|
||||
for(int c=0; c<DEFAULT_CHANNELS; c++) {
|
||||
outbuf[i][c]=samp;
|
||||
}
|
||||
|
||||
|
||||
/*LB303
|
||||
if((int)i>=release_frame) {
|
||||
vca_mode=1;
|
||||
}
|
||||
*/
|
||||
|
||||
// Handle Envelope
|
||||
if(vca_mode==0) {
|
||||
vca_a+=(vca_a0-vca_a)*vca_attack;
|
||||
if(sample_cnt>=0.5*engine::mixer()->processingSampleRate())
|
||||
vca_mode = 2;
|
||||
}
|
||||
else if(vca_mode == 1) {
|
||||
vca_a *= vca_decay;
|
||||
|
||||
// the following line actually speeds up processing
|
||||
if(vca_a < (1/65536.0)) {
|
||||
vca_a = 0;
|
||||
vca_mode = 3;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
/* Prepares the active LB303 note. I separated this into a function because it
|
||||
* needs to be called onplayNote() 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 lb303Synth::initNote( lb303Note *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)) {
|
||||
//printf(" good\n");
|
||||
sample_cnt = 0;
|
||||
vca_mode = 0;
|
||||
// LB303:
|
||||
//vca_a = 0;
|
||||
}
|
||||
else {
|
||||
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; // Slide amount
|
||||
vco_slidebase = vco_inc; // The REAL frequency
|
||||
vco_slideinc = 0; // reset from-note
|
||||
}
|
||||
else {
|
||||
vco_slide = 0;
|
||||
}
|
||||
// End break-out
|
||||
|
||||
// Slide-from 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
|
||||
}
|
||||
|
||||
|
||||
recalcFilter();
|
||||
|
||||
if(n->dead ==0){
|
||||
// Swap next two blocks??
|
||||
vcf->playNote();
|
||||
// Ensure envelope is recalculated
|
||||
vcf_envpos = ENVINC;
|
||||
|
||||
// Double Check
|
||||
//vca_mode = 0;
|
||||
//vca_a = 0.0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void lb303Synth::playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer )
|
||||
{
|
||||
if( _n->arpBaseNote() )
|
||||
{
|
||||
//return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
bool decay_note = false;
|
||||
|
||||
release_frame = _n->framesLeft() - desiredReleaseFrames();
|
||||
|
||||
|
||||
if(deadToggle.value() == 0 && decay_note) {
|
||||
}
|
||||
/// Start a new note.
|
||||
else if( _n->totalFramesPlayed() == 0 ) {
|
||||
new_freq = _n->unpitchedFrequency();
|
||||
true_freq = _n->frequency();
|
||||
_n->m_pluginData = this;
|
||||
}
|
||||
|
||||
// Check for slide
|
||||
if( _n->unpitchedFrequency() == current_freq ) {
|
||||
true_freq = _n->frequency();
|
||||
|
||||
if( slideToggle.value() ) {
|
||||
vco_slidebase = GET_INC( true_freq ); // The REAL frequency
|
||||
}
|
||||
else {
|
||||
vco_inc = GET_INC( true_freq );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void lb303Synth::play( sampleFrame * _working_buffer )
|
||||
{
|
||||
//printf(".");
|
||||
const fpp_t frames = engine::mixer()->framesPerPeriod();
|
||||
|
||||
process( _working_buffer, frames);
|
||||
instrumentTrack()->processAudioBuffer( _working_buffer, frames,
|
||||
NULL );
|
||||
}
|
||||
|
||||
|
||||
|
||||
void lb303Synth::deleteNotePluginData( NotePlayHandle * _n )
|
||||
{
|
||||
//printf("GONE\n");
|
||||
if( _n->unpitchedFrequency() == current_freq )
|
||||
{
|
||||
delete_freq = current_freq;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PluginView * lb303Synth::instantiateView( QWidget * _parent )
|
||||
{
|
||||
return( new lb303SynthView( this, _parent ) );
|
||||
}
|
||||
|
||||
|
||||
lb303SynthView::lb303SynthView( Instrument * _instrument, QWidget * _parent ) :
|
||||
InstrumentView( _instrument, _parent )
|
||||
{
|
||||
// GUI
|
||||
m_vcfCutKnob = new Knob( knobBright_26, this );
|
||||
m_vcfCutKnob->move( 75, 130 );
|
||||
m_vcfCutKnob->setHintText( tr( "Cutoff Freq:" ) + " ", "" );
|
||||
m_vcfCutKnob->setLabel( tr("CUT") );
|
||||
|
||||
m_vcfResKnob = new Knob( knobBright_26, this );
|
||||
m_vcfResKnob->move( 120, 130 );
|
||||
m_vcfResKnob->setHintText( tr( "Resonance:" ) + " ", "" );
|
||||
m_vcfResKnob->setLabel( tr("RES") );
|
||||
|
||||
m_vcfModKnob = new Knob( knobBright_26, this );
|
||||
m_vcfModKnob->move( 165, 130 );
|
||||
m_vcfModKnob->setHintText( tr( "Env Mod:" ) + " ", "" );
|
||||
m_vcfModKnob->setLabel( tr("ENV MOD") );
|
||||
|
||||
m_vcfDecKnob = new Knob( knobBright_26, this );
|
||||
m_vcfDecKnob->move( 210, 130 );
|
||||
m_vcfDecKnob->setHintText( tr( "Decay:" ) + " ", "" );
|
||||
m_vcfDecKnob->setLabel( tr("DEC") );
|
||||
|
||||
m_slideToggle = new LedCheckBox( "Slide", this );
|
||||
m_slideToggle->move( 10, 180 );
|
||||
|
||||
m_accentToggle = new LedCheckBox( "Accent", this );
|
||||
m_accentToggle->move( 10, 200 );
|
||||
m_accentToggle->setDisabled(true);
|
||||
|
||||
m_deadToggle = new LedCheckBox( "Dead", this );
|
||||
m_deadToggle->move( 10, 220 );
|
||||
|
||||
m_db24Toggle = new LedCheckBox( "24dB/oct", this );
|
||||
m_db24Toggle->setWhatsThis(
|
||||
tr( "303-es-que, 24dB/octave, 3 pole filter" ) );
|
||||
m_db24Toggle->move( 10, 150);
|
||||
|
||||
|
||||
m_slideDecKnob = new Knob( knobBright_26, this );
|
||||
m_slideDecKnob->move( 210, 75 );
|
||||
m_slideDecKnob->setHintText( tr( "Slide Decay:" ) + " ", "" );
|
||||
m_slideDecKnob->setLabel( tr( "SLIDE"));
|
||||
|
||||
m_distKnob = new Knob( knobBright_26, this );
|
||||
m_distKnob->move( 210, 190 );
|
||||
m_distKnob->setHintText( tr( "DIST:" ) + " ", "" );
|
||||
m_distKnob->setLabel( tr( "DIST"));
|
||||
|
||||
|
||||
m_waveKnob = new Knob( knobBright_26, this );
|
||||
m_waveKnob->move( 120, 75 );
|
||||
m_waveKnob->setHintText( tr( "WAVE:" ) + " ", "" );
|
||||
m_waveKnob->setLabel( tr( "WAVE"));
|
||||
|
||||
|
||||
setAutoFillBackground( true );
|
||||
QPalette pal;
|
||||
pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap(
|
||||
"artwork" ) );
|
||||
setPalette( pal );
|
||||
}
|
||||
|
||||
|
||||
lb303SynthView::~lb303SynthView()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void lb303SynthView::modelChanged()
|
||||
{
|
||||
lb303Synth * syn = castModel<lb303Synth>();
|
||||
|
||||
m_vcfCutKnob->setModel( &syn->vcf_cut_knob );
|
||||
m_vcfResKnob->setModel( &syn->vcf_res_knob );
|
||||
m_vcfDecKnob->setModel( &syn->vcf_dec_knob );
|
||||
m_vcfModKnob->setModel( &syn->vcf_mod_knob );
|
||||
m_slideDecKnob->setModel( &syn->slide_dec_knob );
|
||||
|
||||
m_distKnob->setModel( &syn->dist_knob );
|
||||
m_waveKnob->setModel( &syn->wave_knob );
|
||||
|
||||
m_slideToggle->setModel( &syn->slideToggle );
|
||||
m_accentToggle->setModel( &syn->accentToggle );
|
||||
m_deadToggle->setModel( &syn->deadToggle );
|
||||
m_db24Toggle->setModel( &syn->db24Toggle );
|
||||
}
|
||||
|
||||
|
||||
|
||||
extern "C"
|
||||
{
|
||||
|
||||
// necessary for getting instance out of shared lib
|
||||
Plugin * PLUGIN_EXPORT lmms_plugin_main( Model *, void * _data )
|
||||
{
|
||||
|
||||
return( new lb303Synth(
|
||||
static_cast<InstrumentTrack *>( _data ) ) );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,271 +0,0 @@
|
||||
/*
|
||||
* lb303.h - declaration of class lb303 which is a bass synth attempting to
|
||||
* emulate the Roland TB303 bass synth
|
||||
*
|
||||
* Copyright (c) 2006-2008 Paul Giblock <pgib/at/users.sourceforge.net>
|
||||
*
|
||||
* This file is part of LMMS - http://lmms.io
|
||||
*
|
||||
* lb303FilterIIR2 is based on the gsyn filter code by Andy Sloane.
|
||||
*
|
||||
* lb303Filter3Pole 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
|
||||
* 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 _LB303_H_
|
||||
#define _LB303_H_
|
||||
|
||||
#include "effect_lib.h"
|
||||
#include "Instrument.h"
|
||||
#include "InstrumentView.h"
|
||||
#include "LedCheckbox.h"
|
||||
#include "Knob.h"
|
||||
#include "Mixer.h"
|
||||
|
||||
class lb303SynthView;
|
||||
class NotePlayHandle;
|
||||
|
||||
class lb303FilterKnobState
|
||||
{
|
||||
public:
|
||||
float cutoff;
|
||||
float reso;
|
||||
float envmod;
|
||||
float envdecay;
|
||||
float dist;
|
||||
};
|
||||
|
||||
|
||||
class lb303Filter
|
||||
{
|
||||
public:
|
||||
lb303Filter(lb303FilterKnobState* p_fs);
|
||||
virtual ~lb303Filter() {};
|
||||
|
||||
virtual void recalc();
|
||||
virtual void envRecalc();
|
||||
virtual float process(const float& samp)=0;
|
||||
virtual void playNote();
|
||||
|
||||
protected:
|
||||
lb303FilterKnobState *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]
|
||||
};
|
||||
|
||||
class lb303FilterIIR2 : public lb303Filter
|
||||
{
|
||||
public:
|
||||
lb303FilterIIR2(lb303FilterKnobState* p_fs);
|
||||
virtual ~lb303FilterIIR2();
|
||||
|
||||
virtual void recalc();
|
||||
virtual void envRecalc();
|
||||
virtual float process(const float& samp);
|
||||
|
||||
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 vcf_a, // Mixing coefficients for the final sound.
|
||||
vcf_b, //
|
||||
vcf_c;
|
||||
|
||||
effectLib::monoToStereoAdaptor<effectLib::distortion<> > * m_dist_fx;
|
||||
effectLib::distortion<> * m_dist;
|
||||
};
|
||||
|
||||
|
||||
class lb303Filter3Pole : public lb303Filter
|
||||
{
|
||||
public:
|
||||
lb303Filter3Pole(lb303FilterKnobState* p_fs);
|
||||
|
||||
//virtual void recalc();
|
||||
virtual void envRecalc();
|
||||
virtual void recalc();
|
||||
virtual float process(const float& samp);
|
||||
|
||||
protected:
|
||||
float kfcn,
|
||||
kp,
|
||||
kp1,
|
||||
kp1h,
|
||||
kres;
|
||||
float ay1,
|
||||
ay2,
|
||||
aout,
|
||||
lastin,
|
||||
value;
|
||||
};
|
||||
|
||||
|
||||
|
||||
class lb303Note
|
||||
{
|
||||
public:
|
||||
float vco_inc;
|
||||
bool dead;
|
||||
};
|
||||
|
||||
|
||||
class lb303Synth : public Instrument
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
lb303Synth( InstrumentTrack * _instrument_track );
|
||||
virtual ~lb303Synth();
|
||||
|
||||
virtual void play( sampleFrame * _working_buffer );
|
||||
virtual void playNote( NotePlayHandle * _n,
|
||||
sampleFrame * _working_buffer );
|
||||
virtual void deleteNotePluginData( NotePlayHandle * _n );
|
||||
|
||||
|
||||
virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
|
||||
virtual void loadSettings( const QDomElement & _this );
|
||||
|
||||
virtual QString nodeName() const;
|
||||
|
||||
virtual f_cnt_t desiredReleaseFrames() const
|
||||
{
|
||||
return 0; //4048;
|
||||
}
|
||||
|
||||
virtual PluginView * instantiateView( QWidget * _parent );
|
||||
|
||||
private:
|
||||
|
||||
void initNote(lb303Note *note);
|
||||
|
||||
|
||||
private:
|
||||
FloatModel vcf_cut_knob;
|
||||
FloatModel vcf_res_knob;
|
||||
FloatModel vcf_mod_knob;
|
||||
FloatModel vcf_dec_knob;
|
||||
|
||||
FloatModel vco_fine_detune_knob;
|
||||
|
||||
FloatModel dist_knob;
|
||||
FloatModel wave_knob;
|
||||
FloatModel slide_dec_knob;
|
||||
|
||||
BoolModel slideToggle;
|
||||
BoolModel accentToggle;
|
||||
BoolModel deadToggle;
|
||||
BoolModel db24Toggle;
|
||||
|
||||
|
||||
public slots:
|
||||
void filterChanged();
|
||||
void db24Toggled();
|
||||
|
||||
private:
|
||||
// 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]
|
||||
|
||||
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.
|
||||
|
||||
float vco_detune;
|
||||
|
||||
enum vco_shape_t { SAWTOOTH, INVERTED_SAWTOOTH, SQUARE, TRIANGLE, MOOG, ROUND_SQUARE };
|
||||
vco_shape_t vco_shape;
|
||||
|
||||
// User settings
|
||||
lb303FilterKnobState fs;
|
||||
lb303Filter *vcf;
|
||||
|
||||
int release_frame;
|
||||
|
||||
|
||||
// More States
|
||||
int vcf_envpos; // Update counter. Updates when >= ENVINC
|
||||
|
||||
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, 3: never played
|
||||
|
||||
// My hacks
|
||||
int sample_cnt;
|
||||
|
||||
int last_offset;
|
||||
|
||||
int catch_frame;
|
||||
int catch_decay;
|
||||
|
||||
float new_freq;
|
||||
float current_freq;
|
||||
float delete_freq;
|
||||
float true_freq;
|
||||
|
||||
void recalcFilter();
|
||||
|
||||
int process(sampleFrame *outbuf, const Uint32 size);
|
||||
|
||||
friend class lb303SynthView;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
class lb303SynthView : public InstrumentView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
lb303SynthView( Instrument * _instrument,
|
||||
QWidget * _parent );
|
||||
virtual ~lb303SynthView();
|
||||
|
||||
private:
|
||||
virtual void modelChanged();
|
||||
|
||||
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;
|
||||
|
||||
} ;
|
||||
|
||||
#endif
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.4 KiB |
Reference in New Issue
Block a user