code:
/*
 * dynamics_processor_controls.h - controls for dynamics_processor-effect
 *
 * Copyright (c) 2014 Vesa Kivimäki <contact/dot/diizy/at/nbl/dot/fi>
 * Copyright (c) 2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 DYNPROC_CONTROLS_H
#define DYNPROC_CONTROLS_H

#include "EffectControls.h"
#include "dynamics_processor_control_dialog.h"
#include "Knob.h"
#include "Graph.h"

class dynProcEffect;

class dynProcControls : public EffectControls
{
	Q_OBJECT
public:
	enum StereoModes
	{
		SM_Maximum,
		SM_Average,
		SM_Unlinked,
		NumStereoModes
	};
	dynProcControls( dynProcEffect * _eff );
	virtual ~dynProcControls()
	{
	}

	virtual void saveSettings( QDomDocument & _doc, QDomElement & _parent );
	virtual void loadSettings( const QDomElement & _this );
	inline virtual QString nodeName() const
	{
		return( "dynamicsprocessor_controls" );
	}

	virtual void setDefaultShape();

	virtual int controlCount()
	{
		return( 6 );
	}

	virtual EffectControlDialog * createView()
	{
		return( new dynProcControlDialog( this ) );
	}

private slots:
	void samplesChanged( int, int );
	void sampleRateChanged();

	void resetClicked();
	void smoothClicked();

	void addOneClicked();
	void subOneClicked();

private:
	dynProcEffect * m_effect;

	FloatModel m_inputModel;
	FloatModel m_outputModel;
	FloatModel m_attackModel;
	FloatModel m_releaseModel;
	graphModel m_wavegraphModel;
	IntModel m_stereomodeModel;

	friend class dynProcControlDialog;
	friend class dynProcEffect;

} ;

#endif


code:
INCLUDE(BuildPlugin)

BUILD_PLUGIN(kicker kicker.cpp kicker.h MOCFILES kicker.h EMBEDDED_RESOURCES artwork.png logo.png)

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * KickerOsc.h - alternative sweeping oscillator
 *
 * Copyright (c) 2006-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2014 grejppi <grejppi/at/gmail.com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 KICKER_OSC_H
#define KICKER_OSC_H

#include "DspEffectLibrary.h"
#include "Oscillator.h"

#include "lmms_math.h"
#include "interpolation.h"
#include "MemoryManager.h"

template<class FX = DspEffectLibrary::StereoBypass>
class KickerOsc
{
	MM_OPERATORS
public:
	KickerOsc( const FX & fx, const float start, const float end, const float noise, const float offset,
		const float slope, const float env, const float diststart, const float distend, const float length ) :
		m_phase( offset ),
		m_startFreq( start ),
		m_endFreq( end ),
		m_noise( noise ),
		m_slope( slope ),
		m_env( env ),
		m_distStart( diststart ),
		m_distEnd( distend ),
		m_hasDistEnv( diststart != distend ),
		m_length( length ),
		m_FX( fx ),
		m_counter( 0 ),
		m_freq( start )
	{
	}

	virtual ~KickerOsc()
	{
	}

	void update( sampleFrame* buf, const fpp_t frames, const float sampleRate )
	{
		for( fpp_t frame = 0; frame < frames; ++frame )
		{
			const double gain = ( 1 - fastPow( ( m_counter < m_length ) ? m_counter / m_length : 1, m_env ) );
			const sample_t s = ( Oscillator::sinSample( m_phase ) * ( 1 - m_noise ) ) + ( Oscillator::noiseSample( 0 ) * gain * gain * m_noise );
			buf[frame][0] = s * gain;
			buf[frame][1] = s * gain;

			// update distortion envelope if necessary
			if( m_hasDistEnv && m_counter < m_length )
			{
				float thres = linearInterpolate( m_distStart, m_distEnd, m_counter / m_length );
				m_FX.leftFX().setThreshold( thres );
				m_FX.rightFX().setThreshold( thres );
			}

			m_FX.nextSample( buf[frame][0], buf[frame][1] );
			m_phase += m_freq / sampleRate;

			const double change = ( m_counter < m_length ) ? ( ( m_startFreq - m_endFreq ) * ( 1 - fastPow( m_counter / m_length, m_slope ) ) ) : 0;
			m_freq = m_endFreq + change;
			++m_counter;
		}
	}

private:
	float m_phase;
	const float m_startFreq;
	const float m_endFreq;
	const float m_noise;
	const float m_slope;
	const float m_env;
	const float m_distStart;
	const float m_distEnd;
	const bool m_hasDistEnv;
	const float m_length;
	FX m_FX;

	unsigned long m_counter;
	double m_freq;

};

#endif


code:
/*
 * kicker.cpp - drum synthesizer
 *
 * Copyright (c) 2006-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2014 grejppi <grejppi/at/gmail.com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 <QPainter>

#include "kicker.h"
#include "Engine.h"
#include "InstrumentTrack.h"
#include "Knob.h"
#include "Mixer.h"
#include "NotePlayHandle.h"
#include "KickerOsc.h"

#include "embed.h"
#include "plugin_export.h"

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT kicker_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"Kicker",
	QT_TRANSLATE_NOOP( "PluginBrowser",
				"Versatile drum synthesizer" ),
	"Tobias Doerffel <tobydox/at/users.sf.net>",
	0x0100,
	Plugin::Instrument,
	new PluginPixmapLoader( "logo" ),
	NULL,
	NULL
} ;

}

kickerInstrument::kickerInstrument( InstrumentTrack * _instrument_track ) :
	Instrument( _instrument_track, &kicker_plugin_descriptor ),
	m_startFreqModel( 150.0f, 5.0f, 1000.0f, 1.0f, this, tr( "Start frequency" ) ),
	m_endFreqModel( 40.0f, 5.0f, 1000.0f, 1.0f, this, tr( "End frequency" ) ),
	m_decayModel( 440.0f, 5.0f, 5000.0f, 1.0f, 5000.0f, this, tr( "Length" ) ),
	m_distModel( 0.8f, 0.0f, 100.0f, 0.1f, this, tr( "Start distortion" ) ),
	m_distEndModel( 0.8f, 0.0f, 100.0f, 0.1f, this, tr( "End distortion" ) ),
	m_gainModel( 1.0f, 0.1f, 5.0f, 0.05f, this, tr( "Gain" ) ),
	m_envModel( 0.163f, 0.01f, 1.0f, 0.001f, this, tr( "Envelope slope" ) ),
	m_noiseModel( 0.0f, 0.0f, 1.0f, 0.01f, this, tr( "Noise" ) ),
	m_clickModel( 0.4f, 0.0f, 1.0f, 0.05f, this, tr( "Click" ) ),
	m_slopeModel( 0.06f, 0.001f, 1.0f, 0.001f, this, tr( "Frequency slope" ) ),
	m_startNoteModel( true, this, tr( "Start from note" ) ),
	m_endNoteModel( false, this, tr( "End to note" ) ),
	m_versionModel( KICKER_PRESET_VERSION, 0, KICKER_PRESET_VERSION, this, "" )
{
}

kickerInstrument::~kickerInstrument()
{
}

void kickerInstrument::saveSettings( QDomDocument & _doc,
							QDomElement & _this )
{
	m_startFreqModel.saveSettings( _doc, _this, "startfreq" );
	m_endFreqModel.saveSettings( _doc, _this, "endfreq" );
	m_decayModel.saveSettings( _doc, _this, "decay" );
	m_distModel.saveSettings( _doc, _this, "dist" );
	m_distEndModel.saveSettings( _doc, _this, "distend" );
	m_gainModel.saveSettings( _doc, _this, "gain" );
	m_envModel.saveSettings( _doc, _this, "env" );
	m_noiseModel.saveSettings( _doc, _this, "noise" );
	m_clickModel.saveSettings( _doc, _this, "click" );
	m_slopeModel.saveSettings( _doc, _this, "slope" );
	m_startNoteModel.saveSettings( _doc, _this, "startnote" );
	m_endNoteModel.saveSettings( _doc, _this, "endnote" );
	m_versionModel.saveSettings( _doc, _this, "version" );
}

void kickerInstrument::loadSettings( const QDomElement & _this )
{
	m_versionModel.loadSettings( _this, "version" );

	m_startFreqModel.loadSettings( _this, "startfreq" );
	m_endFreqModel.loadSettings( _this, "endfreq" );
	m_decayModel.loadSettings( _this, "decay" );
	m_distModel.loadSettings( _this, "dist" );
	if( _this.hasAttribute( "distend" ) )
	{
		m_distEndModel.loadSettings( _this, "distend" );
	}
	else
	{
		m_distEndModel.setValue( m_distModel.value() );
	}
	m_gainModel.loadSettings( _this, "gain" );
	m_envModel.loadSettings( _this, "env" );
	m_noiseModel.loadSettings( _this, "noise" );
	m_clickModel.loadSettings( _this, "click" );
	m_slopeModel.loadSettings( _this, "slope" );
	m_startNoteModel.loadSettings( _this, "startnote" );
	if( m_versionModel.value() < 1 )
	{
		m_startNoteModel.setValue( false );
	}
	m_endNoteModel.loadSettings( _this, "endnote" );

	// Try to maintain backwards compatibility
	if( !_this.hasAttribute( "version" ) )
	{
		m_startNoteModel.setValue( false );
		m_decayModel.setValue( m_decayModel.value() * 1.33f );
		m_envModel.setValue( 1.0f );
		m_slopeModel.setValue( 1.0f );
		m_clickModel.setValue( 0.0f );
	}
	m_versionModel.setValue( KICKER_PRESET_VERSION );
}

QString kickerInstrument::nodeName() const
{
	return kicker_plugin_descriptor.name;
}

typedef DspEffectLibrary::Distortion DistFX;
typedef KickerOsc<DspEffectLibrary::MonoToStereoAdaptor<DistFX> > SweepOsc;

void kickerInstrument::playNote( NotePlayHandle * _n,
						sampleFrame * _working_buffer )
{
	const fpp_t frames = _n->framesLeftForCurrentPeriod();
	const f_cnt_t offset = _n->noteOffset();
	const float decfr = m_decayModel.value() *
		Engine::mixer()->processingSampleRate() / 1000.0f;
	const f_cnt_t tfp = _n->totalFramesPlayed();

	if ( tfp == 0 )
	{
		_n->m_pluginData = new SweepOsc(
					DistFX( m_distModel.value(),
							m_gainModel.value() ),
					m_startNoteModel.value() ? _n->frequency() : m_startFreqModel.value(),
					m_endNoteModel.value() ? _n->frequency() : m_endFreqModel.value(),
					m_noiseModel.value() * m_noiseModel.value(),
					m_clickModel.value() * 0.25f,
					m_slopeModel.value(),
					m_envModel.value(),
					m_distModel.value(),
					m_distEndModel.value(),
					decfr );
	}
	else if( tfp > decfr && !_n->isReleased() )
	{
		_n->noteOff();
	}

	SweepOsc * so = static_cast<SweepOsc *>( _n->m_pluginData );
	so->update( _working_buffer + offset, frames, Engine::mixer()->processingSampleRate() );

	if( _n->isReleased() )
	{
		const float done = _n->releaseFramesDone();
		const float desired = desiredReleaseFrames();
		for( fpp_t f = 0; f < frames; ++f )
		{
			const float fac = ( done+f < desired ) ? ( 1.0f - ( ( done+f ) / desired ) ) : 0;
			_working_buffer[f+offset][0] *= fac;
			_working_buffer[f+offset][1] *= fac;
		}
	}

	instrumentTrack()->processAudioBuffer( _working_buffer, frames + offset, _n );
}

void kickerInstrument::deleteNotePluginData( NotePlayHandle * _n )
{
	delete static_cast<SweepOsc *>( _n->m_pluginData );
}

PluginView * kickerInstrument::instantiateView( QWidget * _parent )
{
	return new kickerInstrumentView( this, _parent );
}

class kickerKnob : public Knob
{
public:
	kickerKnob( QWidget * _parent ) :
			Knob( knobStyled, _parent )
	{
		setFixedSize( 29, 29 );
		setObjectName( "smallKnob" );
	}
};

class kickerEnvKnob : public TempoSyncKnob
{
public:
	kickerEnvKnob( QWidget * _parent ) :
			TempoSyncKnob( knobStyled, _parent )
	{
		setFixedSize( 29, 29 );
		setObjectName( "smallKnob" );
	}
};

class kickerLargeKnob : public Knob
{
public:
	kickerLargeKnob( QWidget * _parent ) :
			Knob( knobStyled, _parent )
	{
		setFixedSize( 34, 34 );
		setObjectName( "largeKnob" );
	}
};

kickerInstrumentView::kickerInstrumentView( Instrument * _instrument,
							QWidget * _parent ) :
	InstrumentViewFixedSize( _instrument, _parent )
{
	const int ROW1 = 14;
	const int ROW2 = ROW1 + 56;
	const int ROW3 = ROW2 + 56;
	const int LED_ROW = 63;
	const int COL1 = 14;
	const int COL2 = COL1 + 56;
	const int COL3 = COL2 + 56;
	const int COL4 = COL3 + 41;
	const int COL5 = COL4 + 41;
	const int END_COL = COL1 + 48;

	m_startFreqKnob = new kickerLargeKnob( this );
	m_startFreqKnob->setHintText( tr( "Start frequency:" ), "Hz" );
	m_startFreqKnob->move( COL1, ROW1 );

	m_endFreqKnob = new kickerLargeKnob( this );
	m_endFreqKnob->setHintText( tr( "End frequency:" ), "Hz" );
	m_endFreqKnob->move( END_COL, ROW1 );

	m_slopeKnob = new kickerKnob( this );
	m_slopeKnob->setHintText( tr( "Frequency slope:" ), "" );
	m_slopeKnob->move( COL3, ROW1 );

	m_gainKnob = new kickerKnob( this );
	m_gainKnob->setHintText( tr( "Gain:" ), "" );
	m_gainKnob->move( COL1, ROW3 );

	m_decayKnob = new kickerEnvKnob( this );
	m_decayKnob->setHintText( tr( "Envelope length:" ), "ms" );
	m_decayKnob->move( COL2, ROW3 );

	m_envKnob = new kickerKnob( this );
	m_envKnob->setHintText( tr( "Envelope slope:" ), "" );
	m_envKnob->move( COL3, ROW3 );

	m_clickKnob = new kickerKnob( this );
	m_clickKnob->setHintText( tr( "Click:" ), "" );
	m_clickKnob->move( COL5, ROW1 );

	m_noiseKnob = new kickerKnob( this );
	m_noiseKnob->setHintText( tr( "Noise:" ), "" );
	m_noiseKnob->move( COL5, ROW3 );

	m_distKnob = new kickerKnob( this );
	m_distKnob->setHintText( tr( "Start distortion:" ), "" );
	m_distKnob->move( COL4, ROW2 );

	m_distEndKnob = new kickerKnob( this );
	m_distEndKnob->setHintText( tr( "End distortion:" ), "" );
	m_distEndKnob->move( COL5, ROW2 );

	m_startNoteToggle = new LedCheckBox( "", this, "", LedCheckBox::Green );
	m_startNoteToggle->move( COL1 + 8, LED_ROW );

	m_endNoteToggle = new LedCheckBox( "", this, "", LedCheckBox::Green );
	m_endNoteToggle->move( END_COL + 8, LED_ROW );

	setAutoFillBackground( true );
	QPalette pal;
	pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) );
	setPalette( pal );
}

kickerInstrumentView::~kickerInstrumentView()
{
}

void kickerInstrumentView::modelChanged()
{
	kickerInstrument * k = castModel<kickerInstrument>();
	m_startFreqKnob->setModel( &k->m_startFreqModel );
	m_endFreqKnob->setModel( &k->m_endFreqModel );
	m_decayKnob->setModel( &k->m_decayModel );
	m_distKnob->setModel( &k->m_distModel );
	m_distEndKnob->setModel( &k->m_distEndModel );
	m_gainKnob->setModel( &k->m_gainModel );
	m_envKnob->setModel( &k->m_envModel );
	m_noiseKnob->setModel( &k->m_noiseModel );
	m_clickKnob->setModel( &k->m_clickModel );
	m_slopeKnob->setModel( &k->m_slopeModel );
	m_startNoteToggle->setModel( &k->m_startNoteModel );
	m_endNoteToggle->setModel( &k->m_endNoteModel );
}

extern "C"
{

// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * m, void * )
{
	return new kickerInstrument( static_cast<InstrumentTrack *>( m ) );
}

}


code:
/*
 * kicker.h - drum synthesizer
 *
 * Copyright (c) 2006-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2014 grejppi <grejppi/at/gmail.com>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 KICKER_H
#define KICKER_H

#include <QObject>
#include "Instrument.h"
#include "InstrumentView.h"
#include "Knob.h"
#include "LedCheckbox.h"
#include "TempoSyncKnob.h"

#define KICKER_PRESET_VERSION 1

class kickerInstrumentView;
class NotePlayHandle;

class kickerInstrument : public Instrument
{
	Q_OBJECT
public:
	kickerInstrument( InstrumentTrack * _instrument_track );
	virtual ~kickerInstrument();

	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 Flags flags() const
	{
		return IsNotBendable;
	}

	virtual f_cnt_t desiredReleaseFrames() const
	{
		return( 512 );
	}

	virtual PluginView * instantiateView( QWidget * _parent );

private:
	FloatModel m_startFreqModel;
	FloatModel m_endFreqModel;
	TempoSyncKnobModel m_decayModel;
	FloatModel m_distModel;
	FloatModel m_distEndModel;
	FloatModel m_gainModel;
	FloatModel m_envModel;
	FloatModel m_noiseModel;
	FloatModel m_clickModel;
	FloatModel m_slopeModel;

	BoolModel m_startNoteModel;
	BoolModel m_endNoteModel;

	IntModel m_versionModel;

	friend class kickerInstrumentView;

} ;

class kickerInstrumentView : public InstrumentViewFixedSize
{
	Q_OBJECT
public:
	kickerInstrumentView( Instrument * _instrument, QWidget * _parent );
	virtual ~kickerInstrumentView();

private:
	virtual void modelChanged();

	Knob * m_startFreqKnob;
	Knob * m_endFreqKnob;
	Knob * m_decayKnob;
	Knob * m_distKnob;
	Knob * m_distEndKnob;
	Knob * m_gainKnob;
	Knob * m_envKnob;
	Knob * m_noiseKnob;
	Knob * m_clickKnob;
	Knob * m_slopeKnob;

	LedCheckBox * m_startNoteToggle;
	LedCheckBox * m_endNoteToggle;

} ;

#endif


code:
INCLUDE(BuildPlugin)

BUILD_PLUGIN(ladspabrowser ladspa_browser.cpp ladspa_browser.h ladspa_description.cpp ladspa_description.h ladspa_port_dialog.cpp ladspa_port_dialog.h MOCFILES ladspa_browser.h ladspa_description.h ladspa_port_dialog.h EMBEDDED_RESOURCES logo.png)


code:
/*
 * ladspa_browser.cpp - dialog to display information about installed LADSPA
 *                      plugins
 *
 * Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
 * Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "ladspa_browser.h"

#include <QHBoxLayout>
#include <QLabel>

#include "gui_templates.h"
#include "ladspa_description.h"
#include "ladspa_port_dialog.h"
#include "TabBar.h"
#include "TabButton.h"

#include "embed.h"
#include "plugin_export.h"

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT ladspabrowser_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"LADSPA Plugin Browser",
	QT_TRANSLATE_NOOP( "PluginBrowser",
				"List installed LADSPA plugins" ),
	"Danny McRae <khjklujn/at/users.sourceforge.net>",
	0x0100,
	Plugin::Tool,
	new PluginPixmapLoader("logo"),
	NULL,
	NULL
} ;

// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * _parent, void * _data )
{
	return new ladspaBrowser;
}

}

ladspaBrowser::ladspaBrowser() :
	ToolPlugin( &ladspabrowser_plugin_descriptor, NULL )
{
}

ladspaBrowser::~ladspaBrowser()
{
}

QString ladspaBrowser::nodeName() const
{
	return ladspabrowser_plugin_descriptor.name;
}

ladspaBrowserView::ladspaBrowserView( ToolPlugin * _tool ) :
	ToolPluginView( _tool  )
{
	QHBoxLayout * hlayout = new QHBoxLayout( this );
	hlayout->setSpacing( 0 );
	hlayout->setMargin( 0 );

	m_tabBar = new TabBar( this, QBoxLayout::TopToBottom );
	m_tabBar->setExclusive( true );
	m_tabBar->setFixedWidth( 72 );

	QWidget * ws = new QWidget( this );
	ws->setFixedSize( 500, 480 );

	QWidget * available = createTab( ws, tr( "Available Effects" ), VALID );
	QWidget * unavailable = createTab( ws, tr( "Unavailable Effects" ),
								INVALID );
	QWidget * instruments = createTab( ws, tr( "Instruments" ), SOURCE );
	QWidget * analysis = createTab( ws, tr( "Analysis Tools" ), SINK );
	QWidget * other = createTab( ws, tr( "Don't know" ), OTHER );

	m_tabBar->addTab( available, tr( "Available Effects" ),
				0, false, true
			)->setIcon( embed::getIconPixmap( "setup_audio" ) );
	m_tabBar->addTab( unavailable, tr( "Unavailable Effects" ),
				1, false, true
			)->setIcon( embed::getIconPixmap(
						"unavailable_sound" ) );
	m_tabBar->addTab( instruments, tr( "Instruments" ),
				2, false, true
			)->setIcon( embed::getIconPixmap(
							"setup_midi" ) );
	m_tabBar->addTab( analysis, tr( "Analysis Tools" ),
				3, false, true
			)->setIcon( embed::getIconPixmap( "analysis" ) );
	m_tabBar->addTab( other, tr( "Don't know" ),
				4, true, true
			)->setIcon( embed::getIconPixmap( "uhoh" ) );

	m_tabBar->setActiveTab( 0 );

	hlayout->addWidget( m_tabBar );
	hlayout->addSpacing( 10 );
	hlayout->addWidget( ws );
	hlayout->addSpacing( 10 );
	hlayout->addStretch();

	hide();
	if( parentWidget() )
	{
		parentWidget()->hide();
		parentWidget()->layout()->setSizeConstraint(
							QLayout::SetFixedSize );

		Qt::WindowFlags flags = parentWidget()->windowFlags();
		flags |= Qt::MSWindowsFixedSizeDialogHint;
		flags &= ~Qt::WindowMaximizeButtonHint;
		parentWidget()->setWindowFlags( flags );
	}
}

ladspaBrowserView::~ladspaBrowserView()
{
}

QWidget * ladspaBrowserView::createTab( QWidget * _parent, const QString & _txt,
							ladspaPluginType _type )
{
	QWidget * tab = new QWidget( _parent );
	tab->setFixedSize( 500, 400 );
	QVBoxLayout * layout = new QVBoxLayout( tab );
	layout->setSpacing( 0 );
	layout->setMargin( 0 );

	const QString type = "<b>" + tr( "Type:" ) + "</b> ";
	QLabel * title = new QLabel( type + _txt, tab );
	QFont f = title->font();
	f.setBold( true );
	title->setFont( pointSize<12>( f ) );

	layout->addSpacing( 5 );
	layout->addWidget( title );
	layout->addSpacing( 10 );

	ladspaDescription * description = new ladspaDescription( tab, _type );
	connect( description, SIGNAL( doubleClicked( const ladspa_key_t & ) ),
				SLOT( showPorts( const ladspa_key_t & ) ) );
	layout->addWidget( description, 1 );

	return tab;
}

void ladspaBrowserView::showPorts( const ladspa_key_t & _key )
{
	ladspaPortDialog ports( _key );
	ports.exec();
}


code:
/*
 * ladspa_browser.h - dialog to display information about installed LADSPA
 *					plugins
 *
 * Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
 * Copyright (c) 2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 _LADSPA_BROWSER_H
#define _LADSPA_BROWSER_H

#include "LadspaManager.h"
#include "ToolPlugin.h"
#include "ToolPluginView.h"

class TabBar;

class ladspaBrowserView : public ToolPluginView
{
	Q_OBJECT
public:
	ladspaBrowserView( ToolPlugin * _tool );
	virtual ~ladspaBrowserView();

public slots:
	void showPorts( const ladspa_key_t & _key );

private:
	TabBar * m_tabBar;

	QWidget * createTab( QWidget * _parent, const QString & _txt,
						ladspaPluginType _type );

} ;

class ladspaBrowser : public ToolPlugin
{
public:
	ladspaBrowser();
	virtual ~ladspaBrowser();

	virtual PluginView * instantiateView( QWidget * )
	{
		return new ladspaBrowserView( this );
	}

	virtual QString nodeName() const;

	virtual void saveSettings( QDomDocument& doc, QDomElement& element )
	{
		Q_UNUSED(doc)
		Q_UNUSED(element)
	}

	virtual void loadSettings( const QDomElement& element )
	{
		Q_UNUSED(element)
	}

} ;

#endif


code:
/*
 * ladspa_description.cpp - LADSPA plugin description
 *
 * Copyright (c) 2007 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "ladspa_description.h"

#include <QGroupBox>
#include <QLabel>
#include <QListWidget>
#include <QScrollArea>
#include <QVBoxLayout>

#include "AudioDevice.h"
#include "Engine.h"
#include "Ladspa2LMMS.h"
#include "Mixer.h"

ladspaDescription::ladspaDescription( QWidget * _parent,
						ladspaPluginType _type ) :
	QWidget( _parent )
{
	Ladspa2LMMS * manager = Engine::getLADSPAManager();

	l_sortable_plugin_t plugins;
	switch( _type )
	{
		case SOURCE:
			plugins = manager->getInstruments();
			break;
		case TRANSFER:
			plugins = manager->getValidEffects();
			break;
		case VALID:
			plugins = manager->getValidEffects();
			break;
		case INVALID:
			plugins = manager->getInvalidEffects();
			break;
		case SINK:
			plugins = manager->getAnalysisTools();
			break;
		case OTHER:
			plugins = manager->getOthers();
			break;
		default:
			break;
	}

	QList<QString> pluginNames;
	for( l_sortable_plugin_t::iterator it = plugins.begin();
			it != plugins.end(); ++it )
	{
		if( _type != VALID ||
			manager->getDescription( ( *it ).second )->inputChannels
				<= Engine::mixer()->audioDev()->channels() )
		{
			pluginNames.push_back( ( *it ).first );
			m_pluginKeys.push_back( ( *it ).second );
		}
	}

	QGroupBox * pluginsBox = new QGroupBox( tr( "Plugins" ), this );
	QListWidget * pluginList = new QListWidget( pluginsBox );
	pluginList->addItems( pluginNames );
	connect( pluginList, SIGNAL( currentRowChanged( int ) ),
						SLOT( rowChanged( int ) ) );
	connect( pluginList, SIGNAL( itemDoubleClicked( QListWidgetItem * ) ),
				SLOT( onDoubleClicked( QListWidgetItem * ) ) );
	( new QVBoxLayout( pluginsBox ) )->addWidget( pluginList );

	QGroupBox * descriptionBox = new QGroupBox( tr( "Description" ), this );
	QVBoxLayout * descriptionLayout = new QVBoxLayout( descriptionBox );
	descriptionLayout->setSpacing( 0 );
	descriptionLayout->setMargin( 0 );

	m_scrollArea = new QScrollArea( descriptionBox );
	descriptionLayout->addWidget( m_scrollArea );

	QVBoxLayout * layout = new QVBoxLayout( this );
	layout->addWidget( pluginsBox );
	layout->addWidget( descriptionBox );

	if( pluginList->count() > 0 )
	{
		pluginList->setCurrentRow( 0 );
		m_currentSelection = m_pluginKeys[0];
		update( m_currentSelection );
	}
}

ladspaDescription::~ladspaDescription()
{
}

void ladspaDescription::update( const ladspa_key_t & _key )
{
	QWidget * description = new QWidget;
	m_scrollArea->setWidget( description );

	QVBoxLayout * layout = new QVBoxLayout( description );
	layout->setSizeConstraint( QLayout::SetFixedSize );

	Ladspa2LMMS * manager = Engine::getLADSPAManager();

	QLabel * name = new QLabel( description );
	name->setText( QWidget::tr( "Name: " ) + manager->getName( _key ) );
	layout->addWidget( name );

	QWidget * maker = new QWidget( description );
	QHBoxLayout * makerLayout = new QHBoxLayout( maker );
	makerLayout->setMargin( 0 );
	makerLayout->setSpacing( 0 );
	layout->addWidget( maker );

	QLabel * maker_label = new QLabel( maker );
	maker_label->setText( QWidget::tr( "Maker: " ) );
	maker_label->setAlignment( Qt::AlignTop );
	QLabel * maker_content = new QLabel( maker );
	maker_content->setText( manager->getMaker( _key ) );
	maker_content->setWordWrap( true );
	makerLayout->addWidget( maker_label );
	makerLayout->addWidget( maker_content, 1 );

	QWidget * copyright = new QWidget( description );
	QHBoxLayout * copyrightLayout = new QHBoxLayout( copyright );
	copyrightLayout->setMargin( 0 );
	copyrightLayout->setSpacing( 0 );
	layout->addWidget( copyright );

	QLabel * copyright_label = new QLabel( copyright );
	copyright_label->setText( QWidget::tr( "Copyright: " ) );
	copyright_label->setAlignment( Qt::AlignTop );

	QLabel * copyright_content = new QLabel( copyright );
	copyright_content->setText( manager->getCopyright( _key ) );
	copyright_content->setWordWrap( true );
	copyrightLayout->addWidget( copyright_label );
	copyrightLayout->addWidget( copyright_content, 1 );

	QLabel * requiresRealTime = new QLabel( description );
	requiresRealTime->setText( QWidget::tr( "Requires Real Time: " ) +
				( manager->hasRealTimeDependency( _key ) ?
							QWidget::tr( "Yes" ) :
							QWidget::tr( "No" ) ) );
	layout->addWidget( requiresRealTime );

	QLabel * realTimeCapable = new QLabel( description );
	realTimeCapable->setText( QWidget::tr( "Real Time Capable: " ) +
					( manager->isRealTimeCapable( _key ) ?
							QWidget::tr( "Yes" ) :
							QWidget::tr( "No" ) ) );
	layout->addWidget( realTimeCapable );

	QLabel * inplaceBroken = new QLabel( description );
	inplaceBroken->setText( QWidget::tr( "In Place Broken: " ) +
					( manager->isInplaceBroken( _key ) ?
							QWidget::tr( "Yes" ) :
							QWidget::tr( "No" ) ) );
	layout->addWidget( inplaceBroken );

	QLabel * channelsIn = new QLabel( description );
	channelsIn->setText( QWidget::tr( "Channels In: " ) + QString::number(
			manager->getDescription( _key )->inputChannels ) );
	layout->addWidget( channelsIn );

	QLabel * channelsOut = new QLabel( description );
	channelsOut->setText( QWidget::tr( "Channels Out: " ) + QString::number(
			manager->getDescription( _key )->outputChannels ) );
	layout->addWidget( channelsOut );
}

void ladspaDescription::rowChanged( int _pluginIndex )
{
	m_currentSelection = m_pluginKeys[_pluginIndex];
	update( m_currentSelection );
}

void ladspaDescription::onDoubleClicked( QListWidgetItem * _item )
{
	emit( doubleClicked( m_currentSelection ) );
}


code:
/*
 * ladspa_description.h - LADSPA plugin description
 *
 * Copyright (c) 2007 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 _LADSPA_DESCRIPTION_H
#define _LADSPA_DESCRIPTION_H

#include <QWidget>

#include "LadspaManager.h"

class QListWidgetItem;
class QScrollArea;

class ladspaDescription : public QWidget
{
	Q_OBJECT
public:
	ladspaDescription( QWidget * _parent, ladspaPluginType _type );
	virtual ~ladspaDescription();

signals:
	void doubleClicked( const ladspa_key_t & );

private:
	QScrollArea * m_scrollArea;

	QList<ladspa_key_t> m_pluginKeys;
	ladspa_key_t m_currentSelection;

	void update( const ladspa_key_t & _key );

private slots:
	void rowChanged( int _pluginIndex );
	void onDoubleClicked( QListWidgetItem * _item );

} ;

#endif


code:
/*
 * ladspa_port_dialog.cpp - dialog to test a LADSPA plugin
 *
 * Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "ladspa_port_dialog.h"

#include <QLayout>
#include <QTableWidget>

#include "embed.h"
#include "Engine.h"
#include "Ladspa2LMMS.h"
#include "Mixer.h"

ladspaPortDialog::ladspaPortDialog( const ladspa_key_t & _key )
{
	Ladspa2LMMS * manager = Engine::getLADSPAManager();

	setWindowIcon( embed::getIconPixmap( "ports" ) );
	setWindowTitle( tr( "Ports" ) );
	setModal( true );

	QVBoxLayout * vlayout = new QVBoxLayout( this );
	vlayout->setSpacing( 0 );
	vlayout->setMargin( 0 );

	int pc = manager->getPortCount( _key );

	QTableWidget * settings = new QTableWidget( pc, 7, this );

	QStringList ports;
	ports.append( tr( "Name" ) );
	ports.append( tr( "Rate" ) );
	ports.append( tr( "Direction" ) );
	ports.append( tr( "Type" ) );
	ports.append( tr( "Min < Default < Max" ) );
	ports.append( tr( "Logarithmic" ) );
	ports.append( tr( "SR Dependent" ) );
	settings->setHorizontalHeaderLabels( ports );

	for( int row = 0; row < pc; row++ )
	{
		for( int col = 0; col < 7; ++col )
		{
			QTableWidgetItem * item = new QTableWidgetItem;
			item->setFlags(QFlag(0));
			settings->setItem( row, col, item );
		}

		int col = 0;
		settings->item( row, col++ )->setText( manager->getPortName( _key, row ) );

		settings->item( row, col++ )->setText( manager->isPortAudio( _key, row ) ?  tr( "Audio" ) : tr( "Control" ) );

		settings->item( row, col++ )->setText( manager->isPortInput( _key, row ) ?  tr( "Input" ) : tr( "Output" ) );

		settings->item( row, col++ )->setText( manager->isPortToggled( _key, row ) ? tr( "Toggled" ) : manager->isInteger( _key, row ) ? tr( "Integer" ) : tr( "Float" ) );

		float min = manager->getLowerBound( _key, row );
		float max = manager->getUpperBound( _key, row );
		float def = manager->getDefaultSetting( _key, row );
		QString range = "";

		if( manager->areHintsSampleRateDependent( _key, row ) )
		{
			if( min != NOHINT )
			{
				min *= Engine::mixer()->processingSampleRate();
			}
			if( max != NOHINT )
			{
				max *= Engine::mixer()->processingSampleRate();
			}
		}

		if( min == NOHINT )
		{
			range += "-Inf < ";
		}
		else if( manager->isInteger( _key, row ) )
		{
			range += QString::number( static_cast<int>( min ) ) +
									" < ";
		}
		else
		{
			range += QString::number( min ) + " < ";
		}

		if( def == NOHINT )
		{
			range += "None < ";
		}
		else if( manager->isInteger( _key, row ) )
		{
			range += QString::number( static_cast<int>( def ) ) +
									" < ";
		}
		else
		{
			range += QString::number( def ) + " < ";
		}

		if( max == NOHINT )
		{
			range += "Inf";
		}
		else if( manager->isInteger( _key, row ) )
		{
			range += QString::number( static_cast<int>( max ) );
		}
		else
		{
			range += QString::number( max );
		}

		if( manager->isPortOutput( _key, row ) ||
					manager->isPortToggled( _key, row ) )
		{
			range = "";
		}

		settings->item( row, col++ )->setText( range );

		if( manager->isLogarithmic( _key, row ) )
		{
			settings->item( row, col )->setText( tr( "Yes" ) );
		}
		col++;

		if( manager->areHintsSampleRateDependent( _key, row ) )
		{
			settings->item( row, col )->setText( tr( "Yes" ) );
		}
	}

	vlayout->addWidget( settings );

	show();
}

ladspaPortDialog::~ladspaPortDialog()
{
}


code:
/*
 * ladspa_port_dialog.h - dialog to test a LADSPA plugin
 *
 * Copyright (c) 2006-2007 Danny McRae <khjklujn/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 _LADSPA_PORT_DIALOG_H
#define _LADSPA_PORT_DIALOG_H

#include <QDialog>

#include "LadspaManager.h"

class ladspaPortDialog : public QDialog
{
	Q_OBJECT
public:
	ladspaPortDialog( const ladspa_key_t & _key );
	virtual ~ladspaPortDialog();

};

#endif


code:
INCLUDE(BuildPlugin)

BUILD_PLUGIN(lb302 lb302.cpp lb302.h MOCFILES lb302.h EMBEDDED_RESOURCES artwork.png logo.png)


code:
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
  noticeable 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?


code:
/*
 * 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 <pgib/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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
 *
 * 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.
 *
 */

// Need to include this first to ensure we get M_PI in MinGW with C++11
#define _USE_MATH_DEFINES
#include <math.h>

#include "lb302.h"
#include "AutomatableButton.h"
#include "Engine.h"
#include "InstrumentPlayHandle.h"
#include "InstrumentTrack.h"
#include "Knob.h"
#include "NotePlayHandle.h"
#include "Oscillator.h"
#include "PixmapButton.h"
#include "ToolTip.h"
#include "BandLimitedWave.h"

#include "embed.h"
#include "plugin_export.h"

// 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
const float sampleRateCutoff = 44100.0f;

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT lb302_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"LB302",
	QT_TRANSLATE_NOOP( "PluginBrowser",
			"Incomplete monophonic imitation tb303" ),
	"Paul Giblock <pgib/at/users.sf.net>",
	0x0100,
	Plugin::Instrument,
	new PluginPixmapLoader( "logo" ),
	NULL,
	NULL
};

}

//
// lb302Filter
//

lb302Filter::lb302Filter(lb302FilterKnobState* p_fs) :
	fs(p_fs),
	vcf_c0(0),
	vcf_e0(0),
	vcf_e1(0)
{
};

void lb302Filter::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 lb302Filter::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 lb302Filter::playNote()
{
	vcf_c0 = vcf_e1;
}

//
// lb302FilterIIR2
//

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 DspEffectLibrary::Distortion( 1.0, 1.0f);

};

lb302FilterIIR2::~lb302FilterIIR2()
{
	delete m_dist;
}

void lb302FilterIIR2::recalc()
{
	lb302Filter::recalc();
	//m_dist->setThreshold(0.5+(fs->dist*2.0));
	m_dist->setThreshold(fs->dist*75.0);
};

void lb302FilterIIR2::envRecalc()
{
	float k, w;

	lb302Filter::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 lb302FilterIIR2::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;
}

//
// lb302Filter3Pole
//

lb302Filter3Pole::lb302Filter3Pole(lb302FilterKnobState *p_fs) :
	lb302Filter(p_fs),
	ay1(0),
	ay2(0),
	aout(0),
	lastin(0)
{
};

void lb302Filter3Pole::recalc()
{
	// DO NOT CALL BASE CLASS
	vcf_e0 = 0.000001;
	vcf_e1 = 1.0;
}

// TODO: Try using k instead of vcf_reso
void lb302Filter3Pole::envRecalc()
{
	float w,k;
	float kfco;

	lb302Filter::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;
    // sampleRateCutoff should not be changed to anything dynamic that is outside the
    // scope of lb302 (like e.g. the mixers sample rate) as this changes the filters cutoff
    // behavior without any modification to its controls.
	kfco = 50.f + (k)*((2300.f-1600.f*(fs->envmod))+(w) *
	                   (700.f+1500.f*(k)+(1500.f+(k)*(sampleRateCutoff/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 lb302Filter3Pole::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
//

lb302Synth::lb302Synth( InstrumentTrack * _instrumentTrack ) :
	Instrument( _instrumentTrack, &lb302_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_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" ) ),
	vca_attack(1.0 - 0.96406088),
	vca_decay(0.99897516),
	vca_a0(0.5),
	vca_a(0.),
	vca_mode(never_played)
{

	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;

	vco_shape = BL_SAWTOOTH;

	vcfs[0] = new lb302FilterIIR2(&fs);
	vcfs[1] = new lb302Filter3Pole(&fs);
	db24Toggled();

	sample_cnt = 0;
	release_frame = 0;
	catch_frame = 0;
	catch_decay = 0;

	last_offset = 0;

	new_freq = false;

	filterChanged();

	InstrumentPlayHandle * iph = new InstrumentPlayHandle( this, _instrumentTrack );
	Engine::mixer()->addPlayHandle( iph );
}

lb302Synth::~lb302Synth()
{
	for (int i=0; i<NUM_FILTERS; ++i) {
		delete vcfs[i];
	}
}

void lb302Synth::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_shape.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 lb302Synth::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");
	slide_dec_knob.loadSettings( _this, "slide_dec");
	wave_shape.loadSettings( _this, "shape");
	slideToggle.loadSettings( _this, "slide");
	deadToggle.loadSettings( _this, "dead");
	db24Toggle.loadSettings( _this, "db24");
 	db24Toggled();

	filterChanged();
}

// TODO: Split into one function per knob.  envdecay doesn't require
// recalcFilter.
void lb302Synth::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 lb302Synth::db24Toggled()
{
	vcf = vcfs[db24Toggle.value()];
	// These recalcFilter calls might suck
	recalcFilter();
}

QString lb302Synth::nodeName() const
{
	return( lb302_plugin_descriptor.name );
}

// OBSOLETE. Break apart once we get Q_OBJECT to work. >:[
void lb302Synth::recalcFilter()
{
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
	vcf.loadRelaxed()->recalc();
#else
	vcf.load()->recalc();
#endif
	// 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))) / engine::mixer()->processingSampleRate();
	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*/

	vcf_envpos = ENVINC; // Trigger filter update in process()
}

inline float GET_INC(float freq) {
	return freq/Engine::mixer()->processingSampleRate();  // TODO: Use actual sampling rate.
}

int lb302Synth::process(sampleFrame *outbuf, const int size)
{
	const float sampleRatio = 44100.f / Engine::mixer()->processingSampleRate();
	float w;
	float samp;

	// Hold on to the current VCF, and use it throughout this period
	lb302Filter *filter = vcf.loadAcquire();

	if( release_frame == 0 || ! m_playingNote )
	{
		vca_mode = decay;
	}

	if( new_freq )
	{
		//printf("  playing new note..\n");
		lb302Note note;
		note.vco_inc = GET_INC( true_freq );
		note.dead = deadToggle.value();
		initNote(&note);

		new_freq = false;
	}

	// TODO: NORMAL RELEASE
	// vca_mode = 1;

	for( int i=0; i<size; i++ )
	{
		// start decay if we're past release
		if( i >= release_frame )
		{
			vca_mode = decay;
		}

		// update vcf
		if(vcf_envpos >= ENVINC) {
			filter->envRecalc();

			vcf_envpos = 0;

			if (vco_slide) {
					vco_inc = vco_slidebase - vco_slide;
					// Calculate coeff from dec_knob on knob change.
					vco_slide -= vco_slide * ( 0.1f - slide_dec_knob.value() * 0.0999f ) * sampleRatio; // TODO: Adjust for ENVINC

			}
		}

		sample_cnt++;
		vcf_envpos++;

		//int  decay_frames = 128;

		// update vco
		vco_c += vco_inc;

		if(vco_c > 0.5)
			vco_c -= 1.0;

		switch(int(rint(wave_shape.value()))) {
			case 0: vco_shape = SAWTOOTH; break;
			case 1: vco_shape = TRIANGLE; break;
			case 2: vco_shape = SQUARE; break;
			case 3: vco_shape = ROUND_SQUARE; break;
			case 4: vco_shape = MOOG; break;
			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;
		}

		// 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 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;

			case SINE:
				// [-0.5, 0.5]  : [-pi, pi]
				vco_k = 0.5f * Oscillator::sinSample( vco_c );
				break;

			case EXPONENTIAL:
				vco_k = 0.5 * Oscillator::expSample( vco_c );
				break;

			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;
		// Write out samples.
#ifdef LB_FILTERED
		//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);

		//samp = vco_k * vca_a;

		if( sample_cnt <= 4 )
		{
	//			vca_a = 0;
		}

#else
		//samp = vco_k*vca_a;
#endif
		/*
		float releaseFrames = desiredReleaseFrames();
		samp *= (releaseFrames - catch_decay)/releaseFrames;
		*/
		//LB302 samp *= (float)(decay_frames - catch_decay)/(float)decay_frames;

		for( int c = 0; c < DEFAULT_CHANNELS; c++ )
		{
			outbuf[i][c] = samp;
		}

		// Handle Envelope
		if(vca_mode==attack) {
			vca_a+=(vca_a0-vca_a)*vca_attack;
			if(sample_cnt>=0.5*Engine::mixer()->processingSampleRate())
				vca_mode = idle;
		}
		else if(vca_mode == decay) {
			vca_a *= vca_decay;

			// the following line actually speeds up processing
			if(vca_a < (1/65536.0)) {
				vca_a = 0;
				vca_mode = never_played;
			}
		}

	}
	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
 *  to be called from process() when a prior edge-to-edge note is done releasing.
 */

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 == decay || vca_mode == never_played)) {
		//printf("    good\n");
		sample_cnt = 0;
		vca_mode = attack;
		// LB303:
		//vca_a = 0;
	}
	else {
		vca_mode = idle;
	}

	initSlide();

	// 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??
#if (QT_VERSION >= QT_VERSION_CHECK(5,14,0))
		vcf.loadRelaxed()->playNote();
#else
		vcf.load()->playNote();
#endif
		// Ensure envelope is recalculated
		vcf_envpos = ENVINC;

		// Double Check
		//vca_mode = 0;
		//vca_a = 0.0;
	}
}

void lb302Synth::initSlide()
{
	// Initiate Slide
	if (vco_slideinc) {
		//printf("    sliding\n");
		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;
	}
}

void lb302Synth::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer )
{
	if( _n->isMasterNote() || ( _n->hasParent() && _n->isReleased() ) )
	{
		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();

	release_frame = qMax( release_frame, _n->framesLeft() + _n->offset() );
}

void lb302Synth::processNote( NotePlayHandle * _n )
{
		/// Start a new note.
		if( _n->m_pluginData != this )
		{
			m_playingNote = _n;
			new_freq = true;
			_n->m_pluginData = this;
		}

		if( ! m_playingNote && ! _n->isReleased() && release_frame > 0 )
		{
			m_playingNote = _n;
			if ( slideToggle.value() )
			{
				vco_slideinc = GET_INC( _n->frequency() );
			}
		}

		// Check for slide
		if( m_playingNote == _n )
		{
			true_freq = _n->frequency();

			if( slideToggle.value() ) {
				vco_slidebase = GET_INC( true_freq );			// The REAL frequency
			}
			else {
				vco_inc = GET_INC( true_freq );
			}
		}
}

void lb302Synth::play( sampleFrame * _working_buffer )
{
	m_notesMutex.lock();
	while( ! m_notes.isEmpty() )
	{
		processNote( m_notes.takeFirst() );
	};
	m_notesMutex.unlock();

	const fpp_t frames = Engine::mixer()->framesPerPeriod();

	process( _working_buffer, frames );
	instrumentTrack()->processAudioBuffer( _working_buffer, frames, NULL );
//	release_frame = 0; //removed for issue # 1432
}

void lb302Synth::deleteNotePluginData( NotePlayHandle * _n )
{
	//printf("GONE\n");
	if( m_playingNote == _n )
	{
		m_playingNote = NULL;
	}
}

PluginView * lb302Synth::instantiateView( QWidget * _parent )
{
	return( new lb302SynthView( this, _parent ) );
}

lb302SynthView::lb302SynthView( Instrument * _instrument, QWidget * _parent ) :
	InstrumentViewFixedSize( _instrument, _parent )
{
	// GUI
	m_vcfCutKnob = new Knob( knobBright_26, this );
	m_vcfCutKnob->move( 75, 130 );
	m_vcfCutKnob->setHintText( tr( "Cutoff Freq:" ), "" );
	m_vcfCutKnob->setLabel( "" );

	m_vcfResKnob = new Knob( knobBright_26, this );
	m_vcfResKnob->move( 120, 130 );
	m_vcfResKnob->setHintText( tr( "Resonance:" ), "" );
	m_vcfResKnob->setLabel( "" );

	m_vcfModKnob = new Knob( knobBright_26, this );
	m_vcfModKnob->move( 165, 130 );
	m_vcfModKnob->setHintText( tr( "Env Mod:" ), "" );
	m_vcfModKnob->setLabel( "" );

	m_vcfDecKnob = new Knob( knobBright_26, this );
	m_vcfDecKnob->move( 210, 130 );
	m_vcfDecKnob->setHintText( tr( "Decay:" ), "" );
	m_vcfDecKnob->setLabel( "" );

	m_slideToggle = new LedCheckBox( "", this );
	m_slideToggle->move( 10, 180 );

/*	m_accentToggle = new LedCheckBox( "", this );
	m_accentToggle->move( 10, 200 );
	m_accentToggle->setDisabled(true);*/ // accent removed pending real implementation - no need for non-functional buttons

	m_deadToggle = new LedCheckBox( "", this );
	m_deadToggle->move( 10, 200 );

	m_db24Toggle = new LedCheckBox( "", this );
	m_db24Toggle->move( 10, 150);
	ToolTip::add( m_db24Toggle,
			tr( "303-es-que, 24dB/octave, 3 pole filter" ) );

	m_slideDecKnob = new Knob( knobBright_26, this );
	m_slideDecKnob->move( 210, 75 );
	m_slideDecKnob->setHintText( tr( "Slide Decay:" ), "" );
	m_slideDecKnob->setLabel( "");

	m_distKnob = new Knob( knobBright_26, this );
	m_distKnob->move( 210, 190 );
	m_distKnob->setHintText( tr( "DIST:" ), "" );
	m_distKnob->setLabel( tr( ""));

	// Shapes
	// move to 120,75
	const int waveBtnX = 10;
	const int waveBtnY = 96;
	PixmapButton * sawWaveBtn = new PixmapButton( this, tr( "Saw wave" ) );
	sawWaveBtn->move( waveBtnX, waveBtnY );
	sawWaveBtn->setActiveGraphic( embed::getIconPixmap(
						"saw_wave_active" ) );
	sawWaveBtn->setInactiveGraphic( embed::getIconPixmap(
						"saw_wave_inactive" ) );
	ToolTip::add( sawWaveBtn,
			tr( "Click here for a saw-wave." ) );

	PixmapButton * triangleWaveBtn =
		new PixmapButton( this, tr( "Triangle wave" ) );
	triangleWaveBtn->move( waveBtnX+(16*1), waveBtnY );
	triangleWaveBtn->setActiveGraphic(
		embed::getIconPixmap( "triangle_wave_active" ) );
	triangleWaveBtn->setInactiveGraphic(
		embed::getIconPixmap( "triangle_wave_inactive" ) );
	ToolTip::add( triangleWaveBtn,
			tr( "Click here for a triangle-wave." ) );

	PixmapButton * sqrWaveBtn = new PixmapButton( this, tr( "Square wave" ) );
	sqrWaveBtn->move( waveBtnX+(16*2), waveBtnY );
	sqrWaveBtn->setActiveGraphic( embed::getIconPixmap(
					"square_wave_active" ) );
	sqrWaveBtn->setInactiveGraphic( embed::getIconPixmap(
					"square_wave_inactive" ) );
	ToolTip::add( sqrWaveBtn,
			tr( "Click here for a square-wave." ) );

	PixmapButton * roundSqrWaveBtn =
		new PixmapButton( this, tr( "Rounded square wave" ) );
	roundSqrWaveBtn->move( waveBtnX+(16*3), waveBtnY );
	roundSqrWaveBtn->setActiveGraphic( embed::getIconPixmap(
					"round_square_wave_active" ) );
	roundSqrWaveBtn->setInactiveGraphic( embed::getIconPixmap(
					"round_square_wave_inactive" ) );
	ToolTip::add( roundSqrWaveBtn,
			tr( "Click here for a square-wave with a rounded end." ) );

	PixmapButton * moogWaveBtn =
		new PixmapButton( this, tr( "Moog wave" ) );
	moogWaveBtn->move( waveBtnX+(16*4), waveBtnY );
	moogWaveBtn->setActiveGraphic(
		embed::getIconPixmap( "moog_saw_wave_active" ) );
	moogWaveBtn->setInactiveGraphic(
		embed::getIconPixmap( "moog_saw_wave_inactive" ) );
	ToolTip::add( moogWaveBtn,
			tr( "Click here for a moog-like wave." ) );

	PixmapButton * sinWaveBtn = new PixmapButton( this, tr( "Sine wave" ) );
	sinWaveBtn->move( waveBtnX+(16*5), waveBtnY );
	sinWaveBtn->setActiveGraphic( embed::getIconPixmap(
						"sin_wave_active" ) );
	sinWaveBtn->setInactiveGraphic( embed::getIconPixmap(
						"sin_wave_inactive" ) );
	ToolTip::add( sinWaveBtn,
			tr( "Click for a sine-wave." ) );

	PixmapButton * exponentialWaveBtn =
		new PixmapButton( this, tr( "White noise wave" ) );
	exponentialWaveBtn->move( waveBtnX+(16*6), waveBtnY );
	exponentialWaveBtn->setActiveGraphic(
		embed::getIconPixmap( "exp_wave_active" ) );
	exponentialWaveBtn->setInactiveGraphic(
		embed::getIconPixmap( "exp_wave_inactive" ) );
	ToolTip::add( exponentialWaveBtn,
			tr( "Click here for an exponential wave." ) );

	PixmapButton * whiteNoiseWaveBtn =
		new PixmapButton( this, tr( "White noise wave" ) );
	whiteNoiseWaveBtn->move( waveBtnX+(16*7), waveBtnY );
	whiteNoiseWaveBtn->setActiveGraphic(
		embed::getIconPixmap( "white_noise_wave_active" ) );
	whiteNoiseWaveBtn->setInactiveGraphic(
		embed::getIconPixmap( "white_noise_wave_inactive" ) );
	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 );
	m_waveBtnGrp->addButton( sqrWaveBtn );
	m_waveBtnGrp->addButton( roundSqrWaveBtn );
	m_waveBtnGrp->addButton( moogWaveBtn );
	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;
	pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap(
			"artwork" ) );
	setPalette( pal );
}

lb302SynthView::~lb302SynthView()
{
}

void lb302SynthView::modelChanged()
{
	lb302Synth * syn = castModel<lb302Synth>();

	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_waveBtnGrp->setModel( &syn->wave_shape );

	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_EXPORT Plugin * lmms_plugin_main( Model * m, void * )
{

	return( new lb302Synth(
		static_cast<InstrumentTrack *>( m ) ) );
}

}


code:
/*
 * lb302.h - declaration of class lb302 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 - https://lmms.io
 *
 * 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
 *
 * 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 LB302_H_
#define LB302_H_

#include "DspEffectLibrary.h"
#include "Instrument.h"
#include "InstrumentView.h"
#include "LedCheckbox.h"
#include "Knob.h"
#include "NotePlayHandle.h"
#include <QMutex>

static const int NUM_FILTERS = 2;

class lb302SynthView;
class NotePlayHandle;

class lb302FilterKnobState
{
	public:
	float cutoff;
	float reso;
	float envmod;
	float envdecay;
	float dist;
};

class lb302Filter
{
	public:
	lb302Filter(lb302FilterKnobState* p_fs);
	virtual ~lb302Filter() {};

	virtual void recalc();
	virtual void envRecalc();
	virtual float process(const float& samp)=0;
	virtual void playNote();

	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]
};

class lb302FilterIIR2 : public lb302Filter
{
	public:
	lb302FilterIIR2(lb302FilterKnobState* p_fs);
	virtual ~lb302FilterIIR2();

	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;

	DspEffectLibrary::Distortion * m_dist;
};

class lb302Filter3Pole : public lb302Filter
{
	public:
	lb302Filter3Pole(lb302FilterKnobState* 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 lb302Note
{
public:
	float vco_inc;
	bool dead;
};

class lb302Synth : public Instrument
{
	Q_OBJECT
public:
	lb302Synth( InstrumentTrack * _instrument_track );
	virtual ~lb302Synth();

	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 Flags flags() const
	{
		return IsSingleStreamed;
	}

	virtual f_cnt_t desiredReleaseFrames() const
	{
		return 0; //4048;
	}

	virtual PluginView * instantiateView( QWidget * _parent );

private:
	void processNote( NotePlayHandle * n );

	void initNote(lb302Note *Note);
	void initSlide();

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;
	IntModel wave_shape;
	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.

	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)
	lb302Filter* vcfs[NUM_FILTERS];

	// User settings
	lb302FilterKnobState fs;
	QAtomicPointer<lb302Filter> 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
	enum VCA_Mode
	{
		attack = 0,
		decay = 1,
		idle = 2,
		never_played = 3
	};
	VCA_Mode vca_mode;

	// My hacks
	int   sample_cnt;

	int   last_offset;

	int catch_frame;
	int catch_decay;

	bool new_freq;
	float true_freq;

	void recalcFilter();

	int process(sampleFrame *outbuf, const int size);

	friend class lb302SynthView;

	NotePlayHandle * m_playingNote;
	NotePlayHandleList m_notes;
	QMutex m_notesMutex;
} ;

class lb302SynthView : public InstrumentViewFixedSize
{
	Q_OBJECT
public:
	lb302SynthView( Instrument * _instrument,
	                QWidget * _parent );
	virtual ~lb302SynthView();

private:
	virtual void modelChanged();

	Knob * m_vcfCutKnob;
	Knob * m_vcfResKnob;
	Knob * m_vcfDecKnob;
	Knob * m_vcfModKnob;

	Knob * m_distKnob;
	Knob * m_slideDecKnob;
	automatableButtonGroup * m_waveBtnGrp;

	LedCheckBox * m_slideToggle;
	/*LedCheckBox * m_accentToggle;*/ // removed pending accent implementation
	LedCheckBox * m_deadToggle;
	LedCheckBox * m_db24Toggle;

} ;

#endif


code:
INCLUDE(BuildPlugin)

BUILD_PLUGIN(monstro Monstro.cpp Monstro.h MOCFILES Monstro.h EMBEDDED_RESOURCES *.png)