code:
/*
 * sfxr.h - declaration of classes of the LMMS sfxr plugin
 * Originally written by Tomas Pettersson. For the original license,
 * please read readme.txt in this directory
 *
 * Copyright (c) 2014 Wong Cho Ching
 *
 * 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 SFXR_H
#define SFXR_H

#include "Instrument.h"
#include "InstrumentView.h"
#include "Knob.h"
#include "Graph.h"
#include "PixmapButton.h"
#include "LedCheckbox.h"
#include "MemoryManager.h"

enum SfxrWaves
{
	SQR_WAVE, SAW_WAVE, SINE_WAVE, NOISE_WAVE, WAVES_NUM
};

const int WAVEFORM_BASE_X = 20;
const int WAVEFORM_BASE_Y = 15;
const int WAVEFORM_BUTTON_WIDTH = 16;

const int GENERATOR_BASE_X = 110;
const int GENERATOR_BASE_Y = 25;
const int GENERATOR_BUTTON_WIDTH = 16;

const int RAND_BUTTON_X = 160;
const int RAND_BUTTON_Y = 4;

const int MUTA_BUTTON_X = 205;
const int MUTA_BUTTON_Y = 4;

const int KNOBS_BASE_X = 20;
const int KNOBS_BASE_Y = 50;
const int KNOB_BLOCK_SIZE_X = 40;
const int KNOB_BLOCK_SIZE_Y = 40;

class sfxrInstrument;

class SfxrSynth
{
	MM_OPERATORS
public:
	SfxrSynth( const sfxrInstrument * s );
	virtual ~SfxrSynth();

	void resetSample( bool restart );
	void update( sampleFrame * buffer, const int32_t frameNum );

	bool isPlaying() const;

private:
	const sfxrInstrument * s;
	bool playing_sample;
	int phase;
	double fperiod;
	double fmaxperiod;
	double fslide;
	double fdslide;
	int period;
	float square_duty;
	float square_slide;
	int env_stage;
	int env_time;
	int env_length[3];
	float env_vol;
	float fphase;
	float fdphase;
	int iphase;
	float phaser_buffer[1024];
	int ipp;
	float noise_buffer[32];
	float fltp;
	float fltdp;
	float fltw;
	float fltw_d;
	float fltdmp;
	float fltphp;
	float flthp;
	float flthp_d;
	float vib_phase;
	float vib_speed;
	float vib_amp;
	int rep_time;
	int rep_limit;
	int arp_time;
	int arp_limit;
	double arp_mod;

} ;

/**
 * @brief A class that simplify the constructor of FloatModel, with value [0,1]
 */
class SfxrZeroToOneFloatModel : public FloatModel
{
public:
	SfxrZeroToOneFloatModel( float val, Model * parent, const QString& displayName ):
		FloatModel( val, 0.0, 1.0, 0.001, parent, displayName )
	{
	}
	/* purpose: prevent the initial value of the model from being changed */
	virtual void loadSettings( const QDomElement& element, const QString& name = QString( "value" ) )
	{
		float oldInitValue = initValue();
		FloatModel::loadSettings(element, name);
		float oldValue = value();
		setInitValue(oldInitValue);
		setValue(oldValue);
	}
};

/**
 * @brief A class that simplify the constructor of FloatModel, with value [-1,1]
 */
class SfxrNegPosOneFloatModel : public FloatModel
{
public:
	SfxrNegPosOneFloatModel(float val, Model * parent, const QString& displayName ):
		FloatModel( val, -1.0, 1.0, 0.001, parent, displayName )
	{
	}
	/* purpose: prevent the initial value of the model from being changed */
	virtual void loadSettings( const QDomElement& element, const QString& name = QString( "value" ) )
	{
		float oldInitValue = initValue();
		FloatModel::loadSettings(element, name);
		float oldValue = value();
		setInitValue(oldInitValue);
		setValue(oldValue);
	}
};

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

	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 PluginView * instantiateView( QWidget * _parent );

	void resetModels();

private:
	SfxrZeroToOneFloatModel m_attModel;
	SfxrZeroToOneFloatModel m_holdModel;
	SfxrZeroToOneFloatModel m_susModel;
	SfxrZeroToOneFloatModel m_decModel;

	SfxrZeroToOneFloatModel m_startFreqModel;
	SfxrZeroToOneFloatModel m_minFreqModel;
	SfxrNegPosOneFloatModel m_slideModel;
	SfxrNegPosOneFloatModel m_dSlideModel;
	SfxrZeroToOneFloatModel m_vibDepthModel;
	SfxrZeroToOneFloatModel m_vibSpeedModel;

	SfxrNegPosOneFloatModel m_changeAmtModel;
	SfxrZeroToOneFloatModel m_changeSpeedModel;

	SfxrZeroToOneFloatModel m_sqrDutyModel;
	SfxrNegPosOneFloatModel m_sqrSweepModel;

	SfxrZeroToOneFloatModel m_repeatSpeedModel;

	SfxrNegPosOneFloatModel m_phaserOffsetModel;
	SfxrNegPosOneFloatModel m_phaserSweepModel;

	SfxrZeroToOneFloatModel m_lpFilCutModel;
	SfxrNegPosOneFloatModel m_lpFilCutSweepModel;
	SfxrZeroToOneFloatModel m_lpFilResoModel;
	SfxrZeroToOneFloatModel m_hpFilCutModel;
	SfxrNegPosOneFloatModel m_hpFilCutSweepModel;

	IntModel m_waveFormModel;

	friend class sfxrInstrumentView;
	friend class SfxrSynth;
};

class sfxrInstrumentView : public InstrumentViewFixedSize
{
	Q_OBJECT
public:
	sfxrInstrumentView( Instrument * _instrument,
					QWidget * _parent );

	virtual ~sfxrInstrumentView() {};

protected slots:
	void genPickup();
	void genLaser();
	void genExplosion();
	void genPowerup();
	void genHit();
	void genJump();
	void genBlip();
	void randomize();
	void mutate();

	void previewSound();

private:
	virtual void modelChanged();

	Knob * m_attKnob; //Attack Time
	Knob * m_holdKnob; //Sustain Time
	Knob * m_susKnob; //Sustain Punch
	Knob * m_decKnob; //Decay Time

	Knob * m_startFreqKnob; //Start Frequency
	Knob * m_minFreqKnob; //Min Frequency
	Knob * m_slideKnob; //Slide
	Knob * m_dSlideKnob; //Delta Slide
	Knob * m_vibDepthKnob; //Vibrato Depth
	Knob * m_vibSpeedKnob; //Vibrato Speed

	Knob * m_changeAmtKnob; //Change Amount
	Knob * m_changeSpeedKnob; //Change Speed

	Knob * m_sqrDutyKnob; //Square Wave Duty
    Knob * m_sqrSweepKnob; //Square Wave Duty Sweep

	Knob * m_repeatSpeedKnob; //Repeat Speed

	Knob * m_phaserOffsetKnob; //Phaser Offset
	Knob * m_phaserSweepKnob; //Phaser Sweep

	Knob * m_lpFilCutKnob; //LP Filter Cutoff
	Knob * m_lpFilCutSweepKnob; //LP Filter Cutoff Sweep
	Knob * m_lpFilResoKnob; //LP Filter Resonance
	Knob * m_hpFilCutKnob; //HP Filter Cutoff
	Knob * m_hpFilCutSweepKnob; //HP Filter Cutoff Sweep

	automatableButtonGroup * m_waveBtnGroup;
	PixmapButton * m_sqrWaveBtn; //NOTE: This button has Square Duty
								//and Square Speed configurable
	PixmapButton * m_sawWaveBtn;
	PixmapButton * m_sinWaveBtn;
	PixmapButton * m_noiseWaveBtn;

	PixmapButton * m_pickupBtn;
	PixmapButton * m_laserBtn;
	PixmapButton * m_explosionBtn;
	PixmapButton * m_powerupBtn;
	PixmapButton * m_hitBtn;
	PixmapButton * m_jumpBtn;
	PixmapButton * m_blipBtn;

	PixmapButton * m_randomizeBtn;
	PixmapButton * m_mutateBtn;

	static QPixmap * s_artwork;
};

#endif


code:
INCLUDE(BuildPlugin)

BUILD_PLUGIN(stereoenhancer stereo_enhancer.cpp stereoenhancer_controls.cpp stereoenhancer_control_dialog.cpp stereo_enhancer.h stereoenhancer_controls.h stereoenhancer_control_dialog.h MOCFILES stereoenhancer_controls.h stereoenhancer_control_dialog.h EMBEDDED_RESOURCES logo.png)

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * stereo_enhancer.cpp - stereo-enhancer-effect-plugin
 *
 * Copyright (c) 2006-2014 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 "stereo_enhancer.h"

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

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT stereoenhancer_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"StereoEnhancer Effect",
	QT_TRANSLATE_NOOP( "PluginBrowser",
				"Plugin for enhancing stereo separation of a stereo input file" ),
	"Lou Herard <lherard/at/gmail.com>",
	0x0100,
	Plugin::Effect,
	new PluginPixmapLoader("logo"),
	NULL,
	NULL
} ;

}

stereoEnhancerEffect::stereoEnhancerEffect(
			Model * _parent,
			const Descriptor::SubPluginFeatures::Key * _key ) :
	Effect( &stereoenhancer_plugin_descriptor, _parent, _key ),
	m_seFX( DspEffectLibrary::StereoEnhancer( 0.0f ) ),
	m_delayBuffer( new sampleFrame[DEFAULT_BUFFER_SIZE] ),
	m_currFrame( 0 ),
	m_bbControls( this )
{
	// TODO:  Make m_delayBuffer customizable?
	clearMyBuffer();
}

stereoEnhancerEffect::~stereoEnhancerEffect()
{
	if( m_delayBuffer )
	{
		delete [] m_delayBuffer;
	}

	m_currFrame = 0;
}

bool stereoEnhancerEffect::processAudioBuffer( sampleFrame * _buf,
							const fpp_t _frames )
{

	// This appears to be used for determining whether or not to continue processing
	// audio with this effect
	double out_sum = 0.0;

	float width;
	int frameIndex = 0;

	if( !isEnabled() || !isRunning() )
	{
		return( false );
	}

	const float d = dryLevel();
	const float w = wetLevel();

	for( fpp_t f = 0; f < _frames; ++f )
	{

		// copy samples into the delay buffer
		m_delayBuffer[m_currFrame][0] = _buf[f][0];
		m_delayBuffer[m_currFrame][1] = _buf[f][1];

		// Get the width knob value from the Stereo Enhancer effect
		width = m_seFX.wideCoeff();

		// Calculate the correct sample frame for processing
		frameIndex = m_currFrame - width;

		if( frameIndex < 0 )
		{
			// e.g. difference = -10, frameIndex = DBS - 10
			frameIndex += DEFAULT_BUFFER_SIZE;
		}

		//sample_t s[2] = { _buf[f][0], _buf[f][1] };	//Vanilla
		sample_t s[2] = { _buf[f][0], m_delayBuffer[frameIndex][1] };	//Chocolate

		m_seFX.nextSample( s[0], s[1] );

		_buf[f][0] = d * _buf[f][0] + w * s[0];
		_buf[f][1] = d * _buf[f][1] + w * s[1];
		out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1];

		// Update currFrame
		m_currFrame += 1;
		m_currFrame %= DEFAULT_BUFFER_SIZE;
	}

	checkGate( out_sum / _frames );
	if( !isRunning() )
	{
		clearMyBuffer();
	}

	return( isRunning() );
}

void stereoEnhancerEffect::clearMyBuffer()
{
	int i;
	for (i = 0; i < DEFAULT_BUFFER_SIZE; i++)
	{
		m_delayBuffer[i][0] = 0.0f;
		m_delayBuffer[i][1] = 0.0f;
	}

	m_currFrame = 0;
}

extern "C"
{

// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * _parent, void * _data )
{
	return( new stereoEnhancerEffect( _parent,
		static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(
								_data ) ) );
}

}


code:
/*
 * stereo_enhancer.h - stereo-enhancer-effect-plugin
 *
 * Copyright (c) 2006-2014 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 _STEREO_ENHANCER_H
#define _STEREO_ENHANCER_H

#include "Effect.h"
#include "DspEffectLibrary.h"
#include "Engine.h"
#include "stereoenhancer_controls.h"

class stereoEnhancerEffect : public Effect
{
public:
	stereoEnhancerEffect( Model * parent,
	                      const Descriptor::SubPluginFeatures::Key * _key );
	virtual ~stereoEnhancerEffect();
	virtual bool processAudioBuffer( sampleFrame * _buf,
		                                          const fpp_t _frames );

	virtual EffectControls * controls()
	{
		return( &m_bbControls );
	}

	void clearMyBuffer();

private:
	DspEffectLibrary::StereoEnhancer m_seFX;

	sampleFrame * m_delayBuffer;
	int m_currFrame;

	stereoEnhancerControls m_bbControls;

	friend class stereoEnhancerControls;
} ;

#endif


code:
/*
 * stereoenhancer_control_dialog.cpp - control-dialog for stereoenhancer-effect
 *
 * Copyright (c) 2006-2007 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 <QLayout>

#include "stereoenhancer_control_dialog.h"
#include "stereoenhancer_controls.h"

stereoEnhancerControlDialog::stereoEnhancerControlDialog(
	stereoEnhancerControls * _controls ) :
	EffectControlDialog( _controls )
{
	QHBoxLayout * l = new QHBoxLayout( this );

	Knob * widthKnob = new Knob( knobBright_26, this );
	widthKnob->setModel( &_controls->m_widthModel );
	widthKnob->setLabel( tr( "WIDTH" ) );
	widthKnob->setHintText( tr( "Width:" ) , " samples" );

	l->addWidget( widthKnob );

	this->setLayout(l);
}


code:
/*
 * stereoenhancer_control_dialog.h - control-dialog for stereo-enhancer-effect
 *
 * Copyright (c) 2006 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 _STEREOENHANCER_CONTROL_DIALOG_H
#define _STEREOENHANCER_CONTROL_DIALOG_H

#include "EffectControlDialog.h"

class stereoEnhancerControls;

class stereoEnhancerControlDialog : public EffectControlDialog
{
	Q_OBJECT
public:
	stereoEnhancerControlDialog( stereoEnhancerControls * _controls );
	virtual ~stereoEnhancerControlDialog()
	{
	}

};

#endif


code:
/*
 * stereoenhancer_controls.cpp - control-dialog for stereoenhancer-effect
 *
 * Copyright (c) 2006-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.
 *
 */

#include <QDomElement>

#include "stereoenhancer_controls.h"
#include "stereo_enhancer.h"

stereoEnhancerControls::stereoEnhancerControls( stereoEnhancerEffect * _eff ) :
		EffectControls( _eff ),
		m_effect( _eff ),
		m_widthModel(0.0f, 0.0f, 180.0f, 1.0f, this, tr( "Width" ) )
{
	connect( &m_widthModel, SIGNAL( dataChanged() ),
			this, SLOT( changeWideCoeff() ) );

	changeWideCoeff();
}

void stereoEnhancerControls::changeWideCoeff()
{
	m_effect->m_seFX.setWideCoeff( m_widthModel.value() );
}

void stereoEnhancerControls::loadSettings( const QDomElement & _this )
{
	m_widthModel.loadSettings( _this, "width" );
}

void stereoEnhancerControls::saveSettings( QDomDocument & _doc,
							QDomElement & _this )
{
	m_widthModel.saveSettings( _doc, _this, "width" );
}


code:
/*
 * stereoenhancer_controls.h - controls for stereoEnhancer-effect
 *
 * 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 _STEREO_ENHANCER_CONTROLS_H
#define _STEREO_ENHANCER_CONTROLS_H

#include "EffectControls.h"
#include "stereoenhancer_control_dialog.h"
#include "Knob.h"

class stereoEnhancerEffect;

class stereoEnhancerControls : public EffectControls
{
	Q_OBJECT
public:
	stereoEnhancerControls( stereoEnhancerEffect( * _eff ) );
	virtual ~stereoEnhancerControls()
	{
	}

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

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

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

private slots:
	void changeWideCoeff();

private:
	stereoEnhancerEffect * m_effect;
	FloatModel m_widthModel;

	friend class stereoEnhancerControlDialog;

} ;

#endif /*_STEREO_ENHANCER_CONTROLS_H*/


code:
INCLUDE(BuildPlugin)

BUILD_PLUGIN(stereomatrix stereo_matrix.cpp stereomatrix_controls.cpp stereomatrix_control_dialog.cpp stereo_matrix.h stereomatrix_controls.h stereomatrix_control_dialog.h MOCFILES stereomatrix_controls.h stereomatrix_control_dialog.h EMBEDDED_RESOURCES artwork.png logo.png)


code:
/*
 * stereo_matrix.cpp - stereo-matrix-effect-plugin
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/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 "stereo_matrix.h"

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

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT stereomatrix_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"Stereo Matrix",
	QT_TRANSLATE_NOOP( "PluginBrowser",
				"Plugin for freely manipulating stereo output" ),
	"Paul Giblock <drfaygo/at/gmail.com>",
	0x0100,
	Plugin::Effect,
	new PluginPixmapLoader("logo"),
	NULL,
	NULL
} ;

}

stereoMatrixEffect::stereoMatrixEffect(
			Model * _parent,
			const Descriptor::SubPluginFeatures::Key * _key ) :
	Effect( &stereomatrix_plugin_descriptor, _parent, _key ),
	m_smControls( this )
{
}

stereoMatrixEffect::~stereoMatrixEffect()
{
}

bool stereoMatrixEffect::processAudioBuffer( sampleFrame * _buf,
							const fpp_t _frames )
{

	// This appears to be used for determining whether or not to continue processing
	// audio with this effect
	if( !isEnabled() || !isRunning() )
	{
		return( false );
	}

	double out_sum = 0.0;

	for( fpp_t f = 0; f < _frames; ++f )
	{
		const float d = dryLevel();
		const float w = wetLevel();

		sample_t l = _buf[f][0];
		sample_t r = _buf[f][1];

		// Init with dry-mix
		_buf[f][0] = l * d;
		_buf[f][1] = r * d;

		// Add it wet
		_buf[f][0] += ( m_smControls.m_llModel.value( f ) * l  +
					m_smControls.m_rlModel.value( f ) * r ) * w;

		_buf[f][1] += ( m_smControls.m_lrModel.value( f ) * l  +
					m_smControls.m_rrModel.value( f ) * r ) * w;
		out_sum += _buf[f][0]*_buf[f][0] + _buf[f][1]*_buf[f][1];

	}

	checkGate( out_sum / _frames );

	return( isRunning() );
}

extern "C"
{

// necessary for getting instance out of shared lib
PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * _parent, void * _data )
{
	return( new stereoMatrixEffect( _parent,
		static_cast<const Plugin::Descriptor::SubPluginFeatures::Key *>(
								_data ) ) );
}

}


code:
/*
 * stereo_matrix.h - stereo-matrix-effect-plugin
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/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 _STEREO_MATRIX_H
#define _STEREO_MATRIX_H

#include "Effect.h"
#include "stereomatrix_controls.h"

class stereoMatrixEffect : public Effect
{
public:
	stereoMatrixEffect( Model * parent,
	                      const Descriptor::SubPluginFeatures::Key * _key );
	virtual ~stereoMatrixEffect();
	virtual bool processAudioBuffer( sampleFrame * _buf,
		                                          const fpp_t _frames );

	virtual EffectControls * controls()
	{
		return( &m_smControls );
	}

private:
	stereoMatrixControls m_smControls;

	friend class stereoMatrixControls;
} ;

#endif


code:
/*
 * stereomatrix_control_dialog.cpp - control dialog for stereoMatrix-effect
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/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 <QLayout>
#include <QLabel>

#include "stereomatrix_control_dialog.h"
#include "stereomatrix_controls.h"
#include "embed.h"

stereoMatrixControlDialog::stereoMatrixControlDialog(
	stereoMatrixControls * _controls ) :
	EffectControlDialog( _controls )
{

	setFixedSize( 160, 185 );

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

	Knob * llKnob = new Knob( knobBright_26, this );
	llKnob->setModel( &_controls->m_llModel );
	llKnob->setHintText( tr( "Left to Left Vol:" ) , "" );
	llKnob->move( 10, 79 );

	Knob * lrKnob = new Knob( knobBright_26, this );
	lrKnob->setModel( &_controls->m_lrModel );
	lrKnob->setHintText( tr( "Left to Right Vol:" ) , "" );
	lrKnob->move( 48, 79 );

	Knob * rlKnob = new Knob( knobBright_26, this );
	rlKnob->setModel( &_controls->m_rlModel );
	rlKnob->setHintText( tr( "Right to Left Vol:" ) , "" );
	rlKnob->move( 85, 79 );

	Knob * rrKnob = new Knob( knobBright_26, this );
	rrKnob->setModel( &_controls->m_rrModel );
	rrKnob->setHintText( tr( "Right to Right Vol:" ) , "" );
	rrKnob->move( 123, 79 );
}


code:
/*
 * stereomatrix_control_dialog.h - control dialog for stereoMatrix-effect
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/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 _STEREOMATRIX_CONTROL_DIALOG_H
#define _STEREOMATRIX_CONTROL_DIALOG_H

#include "EffectControlDialog.h"

class stereoMatrixControls;

class stereoMatrixControlDialog : public EffectControlDialog
{
	Q_OBJECT
public:
	stereoMatrixControlDialog( stereoMatrixControls * _controls );
	virtual ~stereoMatrixControlDialog()
	{
	}

};

#endif


code:
/*
 * stereomatrix_controls.cpp - controls for stereoMatrix-effect
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/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 <QDomElement>

#include "stereomatrix_controls.h"
#include "stereo_matrix.h"

stereoMatrixControls::stereoMatrixControls( stereoMatrixEffect * _eff ) :
		EffectControls( _eff ),
		m_effect( _eff ),
		m_llModel( 1.0f, -1.0f, 1.0f, 0.01f, this, tr( "Left to Left" ) ),
		m_lrModel( 0.0f, -1.0f, 1.0f, 0.01f, this, tr( "Left to Right" ) ),
		m_rlModel( 0.0f, -1.0f, 1.0f, 0.01f, this, tr( "Right to Left" ) ),
		m_rrModel( 1.0f, -1.0f, 1.0f, 0.01f, this, tr( "Right to Right" ) )
{
	m_llModel.setCenterValue( 0 );
	m_lrModel.setCenterValue( 0 );
	m_rlModel.setCenterValue( 0 );
	m_rrModel.setCenterValue( 0 );

	connect( &m_llModel, SIGNAL( dataChanged() ),
			this, SLOT( changeMatrix() ) );
	connect( &m_lrModel, SIGNAL( dataChanged() ),
			this, SLOT( changeMatrix() ) );
	connect( &m_rlModel, SIGNAL( dataChanged() ),
			this, SLOT( changeMatrix() ) );
	connect( &m_rrModel, SIGNAL( dataChanged() ),
			this, SLOT( changeMatrix() ) );

	changeMatrix();
}

void stereoMatrixControls::changeMatrix()
{
}

void stereoMatrixControls::loadSettings( const QDomElement & _this )
{
	m_llModel.loadSettings( _this, "l-l" );
	m_lrModel.loadSettings( _this, "l-r" );
	m_rlModel.loadSettings( _this, "r-l" );
	m_rrModel.loadSettings( _this, "r-r" );
}

void stereoMatrixControls::saveSettings( QDomDocument & _doc,
							QDomElement & _this )
{
	m_llModel.saveSettings( _doc, _this, "l-l" );
	m_lrModel.saveSettings( _doc, _this, "l-r" );
	m_rlModel.saveSettings( _doc, _this, "r-l" );
	m_rrModel.saveSettings( _doc, _this, "r-r" );
}


code:
/*
 * stereomatrix_controls.h - controls for stereoMatrix-effect
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/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 _STEREO_MATRIX_CONTROLS_H
#define _STEREO_MATRIX_CONTROLS_H

#include "EffectControls.h"
#include "stereomatrix_control_dialog.h"
#include "Knob.h"

class stereoMatrixEffect;

class stereoMatrixControls : public EffectControls
{
	Q_OBJECT
public:
	stereoMatrixControls( stereoMatrixEffect( * _eff ) );
	virtual ~stereoMatrixControls()
	{
	}

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

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

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

private slots:
	void changeMatrix();

private:
	stereoMatrixEffect * m_effect;

	FloatModel m_llModel;
	FloatModel m_lrModel;
	FloatModel m_rlModel;
	FloatModel m_rrModel;

	friend class stereoMatrixControlDialog;
	friend class stereoMatrixEffect;

} ;

#endif


code:
INCLUDE(BuildPlugin)
INCLUDE_DIRECTORIES("${STK_INCLUDE_DIR}")
LINK_LIBRARIES(${STK_LIBRARY})
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions")
BUILD_PLUGIN(malletsstk mallets.cpp mallets.h MOCFILES mallets.h EMBEDDED_RESOURCES artwork.png logo.png)


code:
/*
 * mallets.cpp - tuned instruments that one would bang upon
 *
 * Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
 * Copyright (c) 2009-2015 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2016 Oskar Wallgren <oskarwallgren13/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 "mallets.h"

#include <QDir>
#include <QMessageBox>

#include "BandedWG.h"
#include "ModalBar.h"
#include "TubeBell.h"

#include "ConfigManager.h"
#include "Engine.h"
#include "gui_templates.h"
#include "GuiApplication.h"
#include "InstrumentTrack.h"
#include "Mixer.h"

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

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT malletsstk_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"Mallets",
	QT_TRANSLATE_NOOP( "PluginBrowser",
				"Tuneful things to bang on" ),
	"Danny McRae <khjklujn/at/users.sf.net>",
	0x0100,
	Plugin::Instrument,
	new PluginPixmapLoader( "logo" ),
	NULL,
	NULL
} ;

}

malletsInstrument::malletsInstrument( InstrumentTrack * _instrument_track ):
	Instrument( _instrument_track, &malletsstk_plugin_descriptor ),
	m_hardnessModel(64.0f, 0.0f, 128.0f, 0.1f, this, tr( "Hardness" )),
	m_positionModel(64.0f, 0.0f, 64.0f, 0.1f, this, tr( "Position" )),
	m_vibratoGainModel(0.0f, 0.0f, 128.0f, 0.1f, this, tr( "Vibrato gain" )),
	m_vibratoFreqModel(0.0f, 0.0f, 128.0f, 0.1f, this, tr( "Vibrato frequency" )),
	m_stickModel(0.0f, 0.0f, 128.0f, 0.1f, this, tr( "Stick mix" )),
	m_modulatorModel(64.0f, 0.0f, 128.0f, 0.1f, this, tr( "Modulator" )),
	m_crossfadeModel(64.0f, 0.0f, 128.0f, 0.1f, this, tr( "Crossfade" )),
	m_lfoSpeedModel(64.0f, 0.0f, 128.0f, 0.1f, this, tr( "LFO speed" )),
	m_lfoDepthModel(64.0f, 0.0f, 128.0f, 0.1f, this, tr( "LFO depth" )),
	m_adsrModel(64.0f, 0.0f, 128.0f, 0.1f, this, tr( "ADSR" )),
	m_pressureModel(64.0f, 0.1f, 128.0f, 0.1f, this, tr( "Pressure" )),
	m_motionModel(64.0f, 0.0f, 128.0f, 0.1f, this, tr( "Motion" )),
//	TODO: m_vibratoModel
	m_velocityModel(64.0f, 0.1f, 128.0f, 0.1f, this, tr( "Speed" )),
	m_strikeModel( true, this, tr( "Bowed" ) ),
	m_presetsModel(this),
	m_spreadModel(0, 0, 255, 1, this, tr( "Spread" )),
	m_versionModel( MALLETS_PRESET_VERSION, 0, MALLETS_PRESET_VERSION, this, "" ),
	m_isOldVersionModel( false, this, "" ),
	m_filesMissing( !QDir( ConfigManager::inst()->stkDir() ).exists() ||
		!QFileInfo( ConfigManager::inst()->stkDir() + "/sinewave.raw" ).exists() )
{
	// ModalBar
	m_presetsModel.addItem( tr( "Marimba" ) );
	m_scalers.append( 4.0 );
	m_presetsModel.addItem( tr( "Vibraphone" ) );
	m_scalers.append( 4.0 );
	m_presetsModel.addItem( tr( "Agogo" ) );
	m_scalers.append( 5.0 );
	m_presetsModel.addItem( tr( "Wood 1" ) );
	m_scalers.append( 4.0 );
	m_presetsModel.addItem( tr( "Reso" ) );
	m_scalers.append( 2.5 );
	m_presetsModel.addItem( tr( "Wood 2" ) );
	m_scalers.append( 5.0 );
	m_presetsModel.addItem( tr( "Beats" ) );
	m_scalers.append( 20.0 );
	m_presetsModel.addItem( tr( "Two fixed" ) );
	m_scalers.append( 5.0 );
	m_presetsModel.addItem( tr( "Clump" ) );
	m_scalers.append( 4.0 );

	// TubeBell
	m_presetsModel.addItem( tr( "Tubular bells" ) );
	m_scalers.append( 1.8 );

	// BandedWG
	m_presetsModel.addItem( tr( "Uniform bar" ) );
	m_scalers.append( 25.0 );
	m_presetsModel.addItem( tr( "Tuned bar" ) );
	m_scalers.append( 10.0 );
	m_presetsModel.addItem( tr( "Glass" ) );
	m_scalers.append( 16.0 );
	m_presetsModel.addItem( tr( "Tibetan bowl" ) );
	m_scalers.append( 7.0 );
}

malletsInstrument::~malletsInstrument()
{
}

void malletsInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	// ModalBar
	m_hardnessModel.saveSettings( _doc, _this, "hardness" );
	m_positionModel.saveSettings( _doc, _this, "position" );
	m_vibratoGainModel.saveSettings( _doc, _this, "vib_gain" );
	m_vibratoFreqModel.saveSettings( _doc, _this, "vib_freq" );
	m_stickModel.saveSettings( _doc, _this, "stick_mix" );

	// TubeBell
	m_modulatorModel.saveSettings( _doc, _this, "modulator" );
	m_crossfadeModel.saveSettings( _doc, _this, "crossfade" );
	m_lfoSpeedModel.saveSettings( _doc, _this, "lfo_speed" );
	m_lfoDepthModel.saveSettings( _doc, _this, "lfo_depth" );
	m_adsrModel.saveSettings( _doc, _this, "adsr" );

	// BandedWG
	m_pressureModel.saveSettings( _doc, _this, "pressure" );
//	m_motionModel.saveSettings( _doc, _this, "motion" );
//	m_vibratoModel.saveSettings( _doc, _this, "vibrato" );
	m_velocityModel.saveSettings( _doc, _this, "velocity" );
	m_strikeModel.saveSettings( _doc, _this, "strike" );

	m_presetsModel.saveSettings( _doc, _this, "preset" );
	m_spreadModel.saveSettings( _doc, _this, "spread" );
	m_versionModel.saveSettings( _doc, _this, "version" );
	m_isOldVersionModel.saveSettings( _doc, _this, "oldversion" );
}

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

	// ModalBar
	m_hardnessModel.loadSettings( _this, "hardness" );
	m_positionModel.loadSettings( _this, "position" );
	m_vibratoGainModel.loadSettings( _this, "vib_gain" );
	m_vibratoFreqModel.loadSettings( _this, "vib_freq" );
	m_stickModel.loadSettings( _this, "stick_mix" );

	// TubeBell
	m_modulatorModel.loadSettings( _this, "modulator" );
	m_crossfadeModel.loadSettings( _this, "crossfade" );
	m_lfoSpeedModel.loadSettings( _this, "lfo_speed" );
	m_lfoDepthModel.loadSettings( _this, "lfo_depth" );
	m_adsrModel.loadSettings( _this, "adsr" );

	// BandedWG
	m_pressureModel.loadSettings( _this, "pressure" );
//	m_motionModel.loadSettings( _this, "motion" );
//	m_vibratoModel.loadSettings( _this, "vibrato" );
	m_velocityModel.loadSettings( _this, "velocity" );
	m_strikeModel.loadSettings( _this, "strike" );

	m_presetsModel.loadSettings( _this, "preset" );
	m_spreadModel.loadSettings( _this, "spread" );
	m_isOldVersionModel.loadSettings( _this, "oldversion" );

	// To maintain backward compatibility
	if( !_this.hasAttribute( "version" ) )
	{
		m_isOldVersionModel.setValue( true );
		m_vibratoGainModel.setValue( 0.0f );
		if( m_presetsModel.value() == 1 )
		{
		/* 	Earlier mallets used the stk internal
			default of 0.2. 0.2 * 128.0 = 25.6 	*/
			m_vibratoGainModel.setValue( 25.6f );
		}
		if( m_presetsModel.value() != 1 )
		{
			// Frequency actually worked for Vibraphone!
			m_vibratoFreqModel.setValue( 0.0f );
		}
		/*	Modalbar preset values, see stk, ModalBar.cpp
			void ModalBar :: setPreset( int preset )
			Stick Mix * 128.0
			m_positionModel values over 64 is formatted to the
			new knob by 128 - x	*/

		switch( m_presetsModel.value() )
		{
			case 0:
				m_hardnessModel.setValue( 55.0f );
				m_positionModel.setValue( 57.0f );
				m_stickModel.setValue( 12.0f );
				break;
			case 1:
				m_hardnessModel.setValue( 50.0f );
				m_positionModel.setValue( 55.0f );// 128 - 73!
				m_stickModel.setValue( 10.0f );
				break;
			case 2:
				m_hardnessModel.setValue( 78.0f );
				m_positionModel.setValue( 46.0f );
				m_stickModel.setValue( 18.0f );
				break;
			case 3:
				m_hardnessModel.setValue( 59.0f );
				m_positionModel.setValue( 48.0f );
				m_stickModel.setValue( 6.0f );
				break;
			case 4:
				m_hardnessModel.setValue( 58.0f );
				m_positionModel.setValue( 32.0f );
				m_stickModel.setValue( 13.0f );
				break;
			case 5:
				m_hardnessModel.setValue( 40.0f );
				m_positionModel.setValue( 57.0f );
				m_stickModel.setValue( 14.0f );
				break;
			case 6:
				m_hardnessModel.setValue( 51.0f );
				m_positionModel.setValue( 38.0f );
				m_stickModel.setValue( 9.0f );
				break;
			case 7:
				m_hardnessModel.setValue( 58.0f );
				m_positionModel.setValue( 58.0f );
				m_stickModel.setValue( 9.0f );
				break;
			case 8:
				m_hardnessModel.setValue( 50.0f );
				m_positionModel.setValue( 55.0f );// 128 - 73!
				m_stickModel.setValue( 10.0f );
				break;
		}
	}
}

QString malletsInstrument::nodeName() const
{
	return( malletsstk_plugin_descriptor.name );
}

void malletsInstrument::playNote( NotePlayHandle * _n,
						sampleFrame * _working_buffer )
{
	if( m_filesMissing )
	{
		return;
	}

	int p = m_presetsModel.value();

	const float freq = _n->frequency();
	if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL )
	{
		// If newer projects, adjust velocity to within stk's limits
		float velocityAdjust =
			m_isOldVersionModel.value() ? 100.0 : 200.0;
		const float vel = _n->getVolume() / velocityAdjust;

		// critical section as STK is not thread-safe
		static QMutex m;
		m.lock();
		if( p < 9 )
		{
			_n->m_pluginData = new malletsSynth( freq,
						vel,
						m_stickModel.value(),
						m_hardnessModel.value(),
						m_positionModel.value(),
						m_vibratoGainModel.value(),
						m_vibratoFreqModel.value(),
						p,
						(uint8_t) m_spreadModel.value(),
				Engine::mixer()->processingSampleRate() );
		}
		else if( p == 9 )
		{
			_n->m_pluginData = new malletsSynth( freq,
						vel,
						p,
						m_lfoDepthModel.value(),
						m_modulatorModel.value(),
						m_crossfadeModel.value(),
						m_lfoSpeedModel.value(),
						m_adsrModel.value(),
						(uint8_t) m_spreadModel.value(),
				Engine::mixer()->processingSampleRate() );
		}
		else
		{
			_n->m_pluginData = new malletsSynth( freq,
						vel,
						m_pressureModel.value(),
						m_motionModel.value(),
						m_vibratoModel.value(),
						p - 10,
						m_strikeModel.value() * 128.0,
						m_velocityModel.value(),
						(uint8_t) m_spreadModel.value(),
				Engine::mixer()->processingSampleRate() );
		}
		m.unlock();
		static_cast<malletsSynth *>(_n->m_pluginData)->setPresetIndex(p);
	}

	const fpp_t frames = _n->framesLeftForCurrentPeriod();
	const f_cnt_t offset = _n->noteOffset();

	malletsSynth * ps = static_cast<malletsSynth *>( _n->m_pluginData );
	ps->setFrequency( freq );
	p = ps->presetIndex();

	sample_t add_scale = 0.0f;
	if( p == 10 && m_isOldVersionModel.value() == true )
	{
		add_scale = static_cast<sample_t>( m_strikeModel.value() ) * freq * 2.5f;
	}

	for( fpp_t frame = offset; frame < frames + offset; ++frame )
	{
		_working_buffer[frame][0] = ps->nextSampleLeft() *
				( m_scalers[p] + add_scale );
		_working_buffer[frame][1] = ps->nextSampleRight() *
				( m_scalers[p] + add_scale );
	}

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

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

PluginView * malletsInstrument::instantiateView( QWidget * _parent )
{
	return( new malletsInstrumentView( this, _parent ) );
}

malletsInstrumentView::malletsInstrumentView( malletsInstrument * _instrument,
							QWidget * _parent ) :
        InstrumentViewFixedSize( _instrument, _parent )
{
	m_modalBarWidget = setupModalBarControls( this );
	setWidgetBackground( m_modalBarWidget, "artwork" );
	m_modalBarWidget->move( 0,0 );

	m_tubeBellWidget = setupTubeBellControls( this );
	setWidgetBackground( m_tubeBellWidget, "artwork" );
	m_tubeBellWidget->move( 0,0 );

	m_bandedWGWidget = setupBandedWGControls( this );
	setWidgetBackground( m_bandedWGWidget, "artwork" );
	m_bandedWGWidget->move( 0,0 );

	changePreset(); // Show widget

	m_presetsCombo = new ComboBox( this, tr( "Instrument" ) );
	m_presetsCombo->setGeometry( 140, 50, 99, ComboBox::DEFAULT_HEIGHT );
	m_presetsCombo->setFont( pointSize<8>( m_presetsCombo->font() ) );

	connect( &_instrument->m_presetsModel, SIGNAL( dataChanged() ),
		 this, SLOT( changePreset() ) );

	m_spreadKnob = new Knob( knobVintage_32, this );
	m_spreadKnob->setLabel( tr( "Spread" ) );
	m_spreadKnob->move( 190, 140 );
	m_spreadKnob->setHintText( tr( "Spread:" ), "" );

	// try to inform user about missing Stk-installation
	if( _instrument->m_filesMissing && gui != NULL )
	{
		QMessageBox::information( 0, tr( "Missing files" ),
				tr( "Your Stk-installation seems to be "
					"incomplete. Please make sure "
					"the full Stk-package is installed!" ),
				QMessageBox::Ok );
	}
}

malletsInstrumentView::~malletsInstrumentView()
{
}

void malletsInstrumentView::setWidgetBackground( QWidget * _widget, const QString & _pic )
{
	_widget->setAutoFillBackground( true );
	QPalette pal;
	pal.setBrush( _widget->backgroundRole(),
		PLUGIN_NAME::getIconPixmap( _pic.toLatin1().constData() ) );
	_widget->setPalette( pal );
}

QWidget * malletsInstrumentView::setupModalBarControls( QWidget * _parent )
{
	QWidget * widget = new QWidget( _parent );
	widget->setFixedSize( 250, 250 );

	m_hardnessKnob = new Knob( knobVintage_32, widget );
	m_hardnessKnob->setLabel( tr( "Hardness" ) );
	m_hardnessKnob->move( 30, 90 );
	m_hardnessKnob->setHintText( tr( "Hardness:" ), "" );

	m_positionKnob = new Knob( knobVintage_32, widget );
	m_positionKnob->setLabel( tr( "Position" ) );
	m_positionKnob->move( 110, 90 );
	m_positionKnob->setHintText( tr( "Position:" ), "" );

	m_vibratoGainKnob = new Knob( knobVintage_32, widget );
	m_vibratoGainKnob->setLabel( tr( "Vibrato gain" ) );
	m_vibratoGainKnob->move( 30, 140 );
	m_vibratoGainKnob->setHintText( tr( "Vibrato gain:" ), "" );

	m_vibratoFreqKnob = new Knob( knobVintage_32, widget );
	m_vibratoFreqKnob->setLabel( tr( "Vibrato frequency" ) );
	m_vibratoFreqKnob->move( 110, 140 );
	m_vibratoFreqKnob->setHintText( tr( "Vibrato frequency:" ), "" );

	m_stickKnob = new Knob( knobVintage_32, widget );
	m_stickKnob->setLabel( tr( "Stick mix" ) );
	m_stickKnob->move( 190, 90 );
	m_stickKnob->setHintText( tr( "Stick mix:" ), "" );

	return( widget );
}

QWidget * malletsInstrumentView::setupTubeBellControls( QWidget * _parent )
{
	QWidget * widget = new QWidget( _parent );
	widget->setFixedSize( 250, 250 );

	m_modulatorKnob = new Knob( knobVintage_32, widget );
	m_modulatorKnob->setLabel( tr( "Modulator" ) );
	m_modulatorKnob->move( 30, 90 );
	m_modulatorKnob->setHintText( tr( "Modulator:" ), "" );

	m_crossfadeKnob = new Knob( knobVintage_32, widget );
	m_crossfadeKnob->setLabel( tr( "Crossfade" ) );
	m_crossfadeKnob->move( 110, 90 );
	m_crossfadeKnob->setHintText( tr( "Crossfade:" ), "" );

	m_lfoSpeedKnob = new Knob( knobVintage_32, widget );
	m_lfoSpeedKnob->setLabel( tr( "LFO speed" ) );
	m_lfoSpeedKnob->move( 30, 140 );
	m_lfoSpeedKnob->setHintText( tr( "LFO speed:" ), "" );

	m_lfoDepthKnob = new Knob( knobVintage_32, widget );
	m_lfoDepthKnob->setLabel( tr( "LFO depth" ) );
	m_lfoDepthKnob->move( 110, 140 );
	m_lfoDepthKnob->setHintText( tr( "LFO depth:" ), "" );

	m_adsrKnob = new Knob( knobVintage_32, widget );
	m_adsrKnob->setLabel( tr( "ADSR" ) );
	m_adsrKnob->move( 190, 90 );
	m_adsrKnob->setHintText( tr( "ADSR:" ), "" );

	return( widget );
}

QWidget * malletsInstrumentView::setupBandedWGControls( QWidget * _parent )
{
	// BandedWG
	QWidget * widget = new QWidget( _parent );
	widget->setFixedSize( 250, 250 );

/*	m_strikeLED = new LedCheckBox( tr( "Bowed" ), widget );
	m_strikeLED->move( 138, 25 );*/

	m_pressureKnob = new Knob( knobVintage_32, widget );
	m_pressureKnob->setLabel( tr( "Pressure" ) );
	m_pressureKnob->move( 30, 90 );
	m_pressureKnob->setHintText( tr( "Pressure:" ), "" );

/*	m_motionKnob = new Knob( knobVintage_32, widget );
	m_motionKnob->setLabel( tr( "Motion" ) );
	m_motionKnob->move( 110, 90 );
	m_motionKnob->setHintText( tr( "Motion:" ), "" );*/

	m_velocityKnob = new Knob( knobVintage_32, widget );
	m_velocityKnob->setLabel( tr( "Speed" ) );
	m_velocityKnob->move( 30, 140 );
	m_velocityKnob->setHintText( tr( "Speed:" ), "" );

/*	m_vibratoKnob = new Knob( knobVintage_32, widget, tr( "Vibrato" ) );
	m_vibratoKnob->setLabel( tr( "Vibrato" ) );
	m_vibratoKnob->move( 110, 140 );
	m_vibratoKnob->setHintText( tr( "Vibrato:" ), "" );*/

	return( widget );
}

void malletsInstrumentView::modelChanged()
{
	malletsInstrument * inst = castModel<malletsInstrument>();
	m_hardnessKnob->setModel( &inst->m_hardnessModel );
	m_positionKnob->setModel( &inst->m_positionModel );
	m_vibratoGainKnob->setModel( &inst->m_vibratoGainModel );
	m_vibratoFreqKnob->setModel( &inst->m_vibratoFreqModel );
	m_stickKnob->setModel( &inst->m_stickModel );
	m_modulatorKnob->setModel( &inst->m_modulatorModel );
	m_crossfadeKnob->setModel( &inst->m_crossfadeModel );
	m_lfoSpeedKnob->setModel( &inst->m_lfoSpeedModel );
	m_lfoDepthKnob->setModel( &inst->m_lfoDepthModel );
	m_adsrKnob->setModel( &inst->m_adsrModel );
	m_pressureKnob->setModel( &inst->m_pressureModel );
//	m_motionKnob->setModel( &inst->m_motionModel );
//	m_vibratoKnob->setModel( &inst->m_vibratoModel );
	m_velocityKnob->setModel( &inst->m_velocityModel );
//	m_strikeLED->setModel( &inst->m_strikeModel );
	m_presetsCombo->setModel( &inst->m_presetsModel );
	m_spreadKnob->setModel( &inst->m_spreadModel );
}

void malletsInstrumentView::changePreset()
{
	malletsInstrument * inst = castModel<malletsInstrument>();
	int _preset = inst->m_presetsModel.value();

	if( _preset < 9 )
	{
		m_tubeBellWidget->hide();
		m_bandedWGWidget->hide();
		m_modalBarWidget->show();
	}
	else if( _preset == 9 )
	{
		m_modalBarWidget->hide();
		m_bandedWGWidget->hide();
		m_tubeBellWidget->show();
	}
	else
	{
		m_modalBarWidget->hide();
		m_tubeBellWidget->hide();
		m_bandedWGWidget->show();
	}
}

// ModalBar
malletsSynth::malletsSynth( const StkFloat _pitch,
				const StkFloat _velocity,
				const StkFloat _control1,
				const StkFloat _control2,
				const StkFloat _control4,
				const StkFloat _control8,
				const StkFloat _control11,
				const int _control16,
				const uint8_t _delay,
				const sample_rate_t _sample_rate ) :
	m_presetIndex(0)
{
	try
	{
		Stk::setSampleRate( _sample_rate );
		Stk::setRawwavePath( QDir( ConfigManager::inst()->stkDir() ).absolutePath()
						.toLocal8Bit().constData() );
#ifndef LMMS_DEBUG
		Stk::showWarnings( false );
#endif

		m_voice = new ModalBar();

		m_voice->controlChange( 16, _control16 );
		m_voice->controlChange( 1, _control1 );
		m_voice->controlChange( 2, _control2 );
		m_voice->controlChange( 4, _control4 );
		m_voice->controlChange( 8, _control8 );
		m_voice->controlChange( 11, _control11 );
		m_voice->controlChange( 128, 128.0f );

		m_voice->noteOn( _pitch, _velocity );
	}
	catch( ... )
	{
		m_voice = NULL;
	}

	m_delay = new StkFloat[256];
	m_delayRead = 0;
	m_delayWrite = _delay;
	for( int i = 0; i < 256; i++ )
	{
		m_delay[i] = 0.0;
	}
}

// TubeBell
malletsSynth::malletsSynth( const StkFloat _pitch,
				const StkFloat _velocity,
				const int _preset,
				const StkFloat _control1,
				const StkFloat _control2,
				const StkFloat _control4,
				const StkFloat _control11,
				const StkFloat _control128,
				const uint8_t _delay,
				const sample_rate_t _sample_rate ) :
	m_presetIndex(0)
{
	try
	{
		Stk::setSampleRate( _sample_rate );
		Stk::setRawwavePath( QDir( ConfigManager::inst()->stkDir() ).absolutePath()
						.toLocal8Bit().constData() );
#ifndef LMMS_DEBUG
		Stk::showWarnings( false );
#endif

		m_voice = new TubeBell();

		m_voice->controlChange( 1, _control1 );
		m_voice->controlChange( 2, _control2 );
		m_voice->controlChange( 4, _control4 );
		m_voice->controlChange( 11, _control11 );
		m_voice->controlChange( 128, _control128 );

		m_voice->noteOn( _pitch, _velocity );
	}
	catch( ... )
	{
		m_voice = NULL;
	}

	m_delay = new StkFloat[256];
	m_delayRead = 0;
	m_delayWrite = _delay;
	for( int i = 0; i < 256; i++ )
	{
		m_delay[i] = 0.0;
	}
}

// BandedWG
malletsSynth::malletsSynth( const StkFloat _pitch,
				const StkFloat _velocity,
				const StkFloat _control2,
				const StkFloat _control4,
				const StkFloat _control11,
				const int _control16,
				const StkFloat _control64,
				const StkFloat _control128,
				const uint8_t _delay,
				const sample_rate_t _sample_rate ) :
	m_presetIndex(0)
{
	try
	{
		Stk::setSampleRate( _sample_rate );
		Stk::setRawwavePath( QDir( ConfigManager::inst()->stkDir() ).absolutePath()
						.toLocal8Bit().constData() );
#ifndef LMMS_DEBUG
		Stk::showWarnings( false );
#endif

		m_voice = new BandedWG();

		m_voice->controlChange( 1, 128.0 );
		m_voice->controlChange( 2, _control2 );
		m_voice->controlChange( 4, _control4 );
		m_voice->controlChange( 11, _control11 );
		m_voice->controlChange( 16, _control16 );
		m_voice->controlChange( 64, _control64 );
		m_voice->controlChange( 128, _control128 );

		m_voice->noteOn( _pitch, _velocity );
	}
	catch( ... )
	{
		m_voice = NULL;
	}

	m_delay = new StkFloat[256];
	m_delayRead = 0;
	m_delayWrite = _delay;
	for( int i = 0; i < 256; i++ )
	{
		m_delay[i] = 0.0;
	}
}

extern "C"
{

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

}


code:
/*
 * mallets.h - tuned instruments that one would bang upon
 *
 * 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.
 *
 */

#ifndef _MALLET_H
#define _MALLET_H

#include "Instrmnt.h"

#include "ComboBox.h"
#include "Instrument.h"
#include "InstrumentView.h"
#include "Knob.h"
#include "NotePlayHandle.h"
#include "LedCheckbox.h"

// As of Stk 4.4 all classes and types have been moved to the namespace "stk".
// However in older versions this namespace does not exist, therefore declare it
// so this plugin builds with all versions of Stk.
namespace stk { } ;
using namespace stk;

static const int MALLETS_PRESET_VERSION = 1;

class malletsSynth
{
public:
	// ModalBar
	malletsSynth( const StkFloat _pitch,
			const StkFloat _velocity,
			const StkFloat _control1,
			const StkFloat _control2,
			const StkFloat _control4,
			const StkFloat _control8,
			const StkFloat _control11,
			const int _control16,
			const uint8_t _delay,
			const sample_rate_t _sample_rate );

	// TubeBell
	malletsSynth( const StkFloat _pitch,
			const StkFloat _velocity,
			const int _preset,
			const StkFloat _control1,
			const StkFloat _control2,
			const StkFloat _control4,
			const StkFloat _control11,
			const StkFloat _control128,
			const uint8_t _delay,
			const sample_rate_t _sample_rate );

	// BandedWG
	malletsSynth( const StkFloat _pitch,
			const StkFloat _velocity,
			const StkFloat _control2,
			const StkFloat _control4,
			const StkFloat _control11,
			const int _control16,
			const StkFloat _control64,
			const StkFloat _control128,
			const uint8_t _delay,
			const sample_rate_t _sample_rate );

	inline ~malletsSynth()
	{
		if (m_voice) {m_voice->noteOff(0.0);}
		delete[] m_delay;
		delete m_voice;
	}

	inline sample_t nextSampleLeft()
	{
		if( m_voice == NULL )
		{
			return( 0.0f );
		}
		else
		{
			StkFloat s = m_voice->tick();
			m_delay[m_delayWrite] = s;
			m_delayWrite++;
			return( s );
		}
	}

	inline sample_t nextSampleRight()
	{
		StkFloat s = m_delay[m_delayRead];
		m_delayRead++;
		return( s );
	}

	inline void setFrequency( const StkFloat _pitch )
	{
		if( m_voice )
		{
			m_voice->setFrequency( _pitch );
		}
	}

	inline int presetIndex()
	{
		return m_presetIndex;
	}

	inline void setPresetIndex(int presetIndex)
	{
		m_presetIndex = presetIndex;
	}

protected:
	int m_presetIndex;
	Instrmnt * m_voice;

	StkFloat * m_delay;
	uint8_t m_delayRead;
	uint8_t m_delayWrite;
};

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

	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 PluginView * instantiateView( QWidget * _parent );

private:
	FloatModel m_hardnessModel;
	FloatModel m_positionModel;
	FloatModel m_vibratoGainModel;
	FloatModel m_vibratoFreqModel;
	FloatModel m_stickModel;

	FloatModel m_modulatorModel;
	FloatModel m_crossfadeModel;
	FloatModel m_lfoSpeedModel;
	FloatModel m_lfoDepthModel;
	FloatModel m_adsrModel;

	FloatModel m_pressureModel;
	FloatModel m_motionModel;
	FloatModel m_vibratoModel;
	FloatModel m_velocityModel;

	BoolModel m_strikeModel;

	ComboBoxModel m_presetsModel;
	FloatModel m_spreadModel;
	IntModel m_versionModel;
	BoolModel m_isOldVersionModel;

	QVector<sample_t> m_scalers;

	bool m_filesMissing;

	friend class malletsInstrumentView;

} ;

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

public slots:
	void changePreset();

private:
	virtual void modelChanged();

	void setWidgetBackground( QWidget * _widget, const QString & _pic );
	QWidget * setupModalBarControls( QWidget * _parent );
	QWidget * setupTubeBellControls( QWidget * _parent );
	QWidget * setupBandedWGControls( QWidget * _parent );

	QWidget * m_modalBarWidget;
	Knob * m_hardnessKnob;
	Knob * m_positionKnob;
	Knob * m_vibratoGainKnob;
	Knob * m_vibratoFreqKnob;
	Knob * m_stickKnob;

	QWidget * m_tubeBellWidget;
	Knob * m_modulatorKnob;
	Knob * m_crossfadeKnob;
	Knob * m_lfoSpeedKnob;
	Knob * m_lfoDepthKnob;
	Knob * m_adsrKnob;

	QWidget * m_bandedWGWidget;
	Knob * m_pressureKnob;
//	Knob * m_motionKnob;
//	Knob * m_vibratoKnob;
	Knob * m_velocityKnob;
//	LedCheckBox * m_strikeLED;

	ComboBox * m_presetsCombo;
	Knob * m_spreadKnob;
};

#endif


code:
INCLUDE(BuildPlugin)

BUILD_PLUGIN(tripleoscillator TripleOscillator.cpp TripleOscillator.h MOCFILES TripleOscillator.h EMBEDDED_RESOURCES *.png)


code:
/*
 * TripleOscillator.cpp - powerful instrument with three oscillators
 *
 * Copyright (c) 2004-2014 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 <QDomDocument>
#include <QBitmap>
#include <QPainter>

#include "TripleOscillator.h"
#include "AutomatableButton.h"
#include "debug.h"
#include "Engine.h"
#include "InstrumentTrack.h"
#include "Knob.h"
#include "Mixer.h"
#include "NotePlayHandle.h"
#include "PixmapButton.h"
#include "SampleBuffer.h"
#include "ToolTip.h"

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

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT tripleoscillator_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"TripleOscillator",
	QT_TRANSLATE_NOOP( "PluginBrowser",
				"Three powerful oscillators you can modulate "
				"in several ways" ),
	"Tobias Doerffel <tobydox/at/users.sf.net>",
	0x0110,
	Plugin::Instrument,
	new PluginPixmapLoader( "logo" ),
	NULL,
	NULL
} ;

}

OscillatorObject::OscillatorObject( Model * _parent, int _idx ) :
	Model( _parent ),
	m_volumeModel( DefaultVolume / NUM_OF_OSCILLATORS, MinVolume,
			MaxVolume, 1.0f, this, tr( "Osc %1 volume" ).arg( _idx+1 ) ),
	m_panModel( DefaultPanning, PanningLeft, PanningRight, 1.0f, this,
			tr( "Osc %1 panning" ).arg( _idx+1 ) ),
	m_coarseModel( -_idx*KeysPerOctave,
			-2 * KeysPerOctave, 2 * KeysPerOctave, 1.0f, this,
			tr( "Osc %1 coarse detuning" ).arg( _idx+1 ) ),
	m_fineLeftModel( 0.0f, -100.0f, 100.0f, 1.0f, this,
			tr( "Osc %1 fine detuning left" ).arg( _idx+1 ) ),
	m_fineRightModel( 0.0f, -100.0f, 100.0f, 1.0f, this,
			tr( "Osc %1 fine detuning right" ).arg( _idx + 1 ) ),
	m_phaseOffsetModel( 0.0f, 0.0f, 360.0f, 1.0f, this,
			tr( "Osc %1 phase-offset" ).arg( _idx+1 ) ),
	m_stereoPhaseDetuningModel( 0.0f, 0.0f, 360.0f, 1.0f, this,
			tr( "Osc %1 stereo phase-detuning" ).arg( _idx+1 ) ),
	m_waveShapeModel( Oscillator::SineWave, 0,
			Oscillator::NumWaveShapes-1, this,
			tr( "Osc %1 wave shape" ).arg( _idx+1 ) ),
	m_modulationAlgoModel( Oscillator::SignalMix, 0,
				Oscillator::NumModulationAlgos-1, this,
				tr( "Modulation type %1" ).arg( _idx+1 ) ),

	m_sampleBuffer( new SampleBuffer ),
	m_volumeLeft( 0.0f ),
	m_volumeRight( 0.0f ),
	m_detuningLeft( 0.0f ),
	m_detuningRight( 0.0f ),
	m_phaseOffsetLeft( 0.0f ),
	m_phaseOffsetRight( 0.0f )
{
	// Connect knobs with Oscillators' inputs
	connect( &m_volumeModel, SIGNAL( dataChanged() ),
					this, SLOT( updateVolume() ), Qt::DirectConnection );
	connect( &m_panModel, SIGNAL( dataChanged() ),
					this, SLOT( updateVolume() ), Qt::DirectConnection );
	updateVolume();

	connect( &m_coarseModel, SIGNAL( dataChanged() ),
				this, SLOT( updateDetuningLeft() ), Qt::DirectConnection );
	connect( &m_coarseModel, SIGNAL( dataChanged() ),
				this, SLOT( updateDetuningRight() ), Qt::DirectConnection );
	connect( &m_fineLeftModel, SIGNAL( dataChanged() ),
				this, SLOT( updateDetuningLeft() ), Qt::DirectConnection );
	connect( &m_fineRightModel, SIGNAL( dataChanged() ),
				this, SLOT( updateDetuningRight() ), Qt::DirectConnection );
	updateDetuningLeft();
	updateDetuningRight();

	connect( &m_phaseOffsetModel, SIGNAL( dataChanged() ),
			this, SLOT( updatePhaseOffsetLeft() ), Qt::DirectConnection );
	connect( &m_phaseOffsetModel, SIGNAL( dataChanged() ),
			this, SLOT( updatePhaseOffsetRight() ), Qt::DirectConnection );
	connect( &m_stereoPhaseDetuningModel, SIGNAL( dataChanged() ),
			this, SLOT( updatePhaseOffsetLeft() ), Qt::DirectConnection );
	updatePhaseOffsetLeft();
	updatePhaseOffsetRight();

}

OscillatorObject::~OscillatorObject()
{
	sharedObject::unref( m_sampleBuffer );
}

void OscillatorObject::oscUserDefWaveDblClick()
{
	QString af = m_sampleBuffer->openAndSetWaveformFile();
	if( af != "" )
	{
		// TODO:
		//ToolTip::add( m_usrWaveBtn, m_sampleBuffer->audioFile() );
	}
}

void OscillatorObject::updateVolume()
{
	if( m_panModel.value() >= 0.0f )
	{
		const float panningFactorLeft = 1.0f - m_panModel.value()
							/ (float)PanningRight;
		m_volumeLeft = panningFactorLeft * m_volumeModel.value() /
									100.0f;
		m_volumeRight = m_volumeModel.value() / 100.0f;
	}
	else
	{
		m_volumeLeft = m_volumeModel.value() / 100.0f;
		const float panningFactorRight = 1.0f + m_panModel.value()
							/ (float)PanningRight;
		m_volumeRight = panningFactorRight * m_volumeModel.value() /
									100.0f;
	}
}

void OscillatorObject::updateDetuningLeft()
{
	m_detuningLeft = powf( 2.0f, ( (float)m_coarseModel.value() * 100.0f
				+ (float)m_fineLeftModel.value() ) / 1200.0f )
				/ Engine::mixer()->processingSampleRate();
}

void OscillatorObject::updateDetuningRight()
{
	m_detuningRight = powf( 2.0f, ( (float)m_coarseModel.value() * 100.0f
				+ (float)m_fineRightModel.value() ) / 1200.0f )
				/ Engine::mixer()->processingSampleRate();
}

void OscillatorObject::updatePhaseOffsetLeft()
{
	m_phaseOffsetLeft = ( m_phaseOffsetModel.value() +
				m_stereoPhaseDetuningModel.value() ) / 360.0f;
}

void OscillatorObject::updatePhaseOffsetRight()
{
	m_phaseOffsetRight = m_phaseOffsetModel.value() / 360.0f;
}

TripleOscillator::TripleOscillator( InstrumentTrack * _instrument_track ) :
	Instrument( _instrument_track, &tripleoscillator_plugin_descriptor )
{
	for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
	{
		m_osc[i] = new OscillatorObject( this, i );

	}

	connect( Engine::mixer(), SIGNAL( sampleRateChanged() ),
			this, SLOT( updateAllDetuning() ) );
}

TripleOscillator::~TripleOscillator()
{
}

void TripleOscillator::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
	{
		QString is = QString::number( i );
		m_osc[i]->m_volumeModel.saveSettings( _doc, _this, "vol" + is );
		m_osc[i]->m_panModel.saveSettings( _doc, _this, "pan" + is );
		m_osc[i]->m_coarseModel.saveSettings( _doc, _this, "coarse"
									+ is );
		m_osc[i]->m_fineLeftModel.saveSettings( _doc, _this, "finel" +
									is );
		m_osc[i]->m_fineRightModel.saveSettings( _doc, _this, "finer" +
									is );
		m_osc[i]->m_phaseOffsetModel.saveSettings( _doc, _this,
							"phoffset" + is );
		m_osc[i]->m_stereoPhaseDetuningModel.saveSettings( _doc, _this,
							"stphdetun" + is );
		m_osc[i]->m_waveShapeModel.saveSettings( _doc, _this,
							"wavetype" + is );
		m_osc[i]->m_modulationAlgoModel.saveSettings( _doc, _this,
					"modalgo" + QString::number( i+1 ) );
		_this.setAttribute( "userwavefile" + is,
					m_osc[i]->m_sampleBuffer->audioFile() );
	}
}

void TripleOscillator::loadSettings( const QDomElement & _this )
{
	for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
	{
		const QString is = QString::number( i );
		m_osc[i]->m_volumeModel.loadSettings( _this, "vol" + is );
		m_osc[i]->m_panModel.loadSettings( _this, "pan" + is );
		m_osc[i]->m_coarseModel.loadSettings( _this, "coarse" + is );
		m_osc[i]->m_fineLeftModel.loadSettings( _this, "finel" + is );
		m_osc[i]->m_fineRightModel.loadSettings( _this, "finer" + is );
		m_osc[i]->m_phaseOffsetModel.loadSettings( _this,
							"phoffset" + is );
		m_osc[i]->m_stereoPhaseDetuningModel.loadSettings( _this,
							"stphdetun" + is );
		m_osc[i]->m_waveShapeModel.loadSettings( _this, "wavetype" +
									is );
		m_osc[i]->m_modulationAlgoModel.loadSettings( _this,
					"modalgo" + QString::number( i+1 ) );
		m_osc[i]->m_sampleBuffer->setAudioFile( _this.attribute(
							"userwavefile" + is ) );
	}
}

QString TripleOscillator::nodeName() const
{
	return( tripleoscillator_plugin_descriptor.name );
}

void TripleOscillator::playNote( NotePlayHandle * _n,
						sampleFrame * _working_buffer )
{
	if( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL )
	{
		Oscillator * oscs_l[NUM_OF_OSCILLATORS];
		Oscillator * oscs_r[NUM_OF_OSCILLATORS];

		for( int i = NUM_OF_OSCILLATORS - 1; i >= 0; --i )
		{

			// the last oscs needs no sub-oscs...
			if( i == NUM_OF_OSCILLATORS - 1 )
			{
				oscs_l[i] = new Oscillator(
						&m_osc[i]->m_waveShapeModel,
						&m_osc[i]->m_modulationAlgoModel,
						_n->frequency(),
						m_osc[i]->m_detuningLeft,
						m_osc[i]->m_phaseOffsetLeft,
						m_osc[i]->m_volumeLeft );
				oscs_r[i] = new Oscillator(
						&m_osc[i]->m_waveShapeModel,
						&m_osc[i]->m_modulationAlgoModel,
						_n->frequency(),
						m_osc[i]->m_detuningRight,
						m_osc[i]->m_phaseOffsetRight,
						m_osc[i]->m_volumeRight );
			}
			else
			{
				oscs_l[i] = new Oscillator(
						&m_osc[i]->m_waveShapeModel,
						&m_osc[i]->m_modulationAlgoModel,
						_n->frequency(),
						m_osc[i]->m_detuningLeft,
						m_osc[i]->m_phaseOffsetLeft,
						m_osc[i]->m_volumeLeft,
						oscs_l[i + 1] );
				oscs_r[i] = new Oscillator(
						&m_osc[i]->m_waveShapeModel,
						&m_osc[i]->m_modulationAlgoModel,
						_n->frequency(),
						m_osc[i]->m_detuningRight,
						m_osc[i]->m_phaseOffsetRight,
						m_osc[i]->m_volumeRight,
						oscs_r[i + 1] );
			}

			oscs_l[i]->setUserWave( m_osc[i]->m_sampleBuffer );
			oscs_r[i]->setUserWave( m_osc[i]->m_sampleBuffer );

		}

		_n->m_pluginData = new oscPtr;
		static_cast<oscPtr *>( _n->m_pluginData )->oscLeft = oscs_l[0];
		static_cast< oscPtr *>( _n->m_pluginData )->oscRight =
								oscs_r[0];
	}

	Oscillator * osc_l = static_cast<oscPtr *>( _n->m_pluginData )->oscLeft;
	Oscillator * osc_r = static_cast<oscPtr *>( _n->m_pluginData )->oscRight;

	const fpp_t frames = _n->framesLeftForCurrentPeriod();
	const f_cnt_t offset = _n->noteOffset();

	osc_l->update( _working_buffer + offset, frames, 0 );
	osc_r->update( _working_buffer + offset, frames, 1 );

	applyFadeIn(_working_buffer, _n);
	applyRelease( _working_buffer, _n );

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

void TripleOscillator::deleteNotePluginData( NotePlayHandle * _n )
{
	delete static_cast<Oscillator *>( static_cast<oscPtr *>(
						_n->m_pluginData )->oscLeft );
	delete static_cast<Oscillator *>( static_cast<oscPtr *>(
						_n->m_pluginData )->oscRight );
	delete static_cast<oscPtr *>( _n->m_pluginData );
}

PluginView * TripleOscillator::instantiateView( QWidget * _parent )
{
	return new TripleOscillatorView( this, _parent );
}

void TripleOscillator::updateAllDetuning()
{
	for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
	{
		m_osc[i]->updateDetuningLeft();
		m_osc[i]->updateDetuningRight();
	}
}

class TripleOscKnob : public Knob
{
public:
	TripleOscKnob( QWidget * _parent ) :
			Knob( knobStyled, _parent )
	{
		setFixedSize( 28, 35 );
	}
};

// 82, 109

TripleOscillatorView::TripleOscillatorView( Instrument * _instrument,
							QWidget * _parent ) :
	InstrumentViewFixedSize( _instrument, _parent )
{
	setAutoFillBackground( true );
	QPalette pal;
	pal.setBrush( backgroundRole(),
				PLUGIN_NAME::getIconPixmap( "artwork" ) );
	setPalette( pal );

	const int mod_x = 66;
	const int mod1_y = 58;
	const int mod2_y = 75;
	const int osc_y = 109;
	const int osc_h = 52;

	// TODO: clean rewrite using layouts and all that...
	PixmapButton * pm_osc1_btn = new PixmapButton( this, NULL );
	pm_osc1_btn->move( mod_x, mod1_y );
	pm_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
								"pm_active" ) );
	pm_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"pm_inactive" ) );
	ToolTip::add( pm_osc1_btn, tr( "Modulate phase of oscillator 1 by oscillator 2" ) );

	PixmapButton * am_osc1_btn = new PixmapButton( this, NULL );
	am_osc1_btn->move( mod_x + 35, mod1_y );
	am_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
								"am_active" ) );
	am_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"am_inactive" ) );
	ToolTip::add( am_osc1_btn, tr( "Modulate amplitude of oscillator 1 by oscillator 2" ) );

	PixmapButton * mix_osc1_btn = new PixmapButton( this, NULL );
	mix_osc1_btn->move( mod_x + 70, mod1_y );
	mix_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
							"mix_active" ) );
	mix_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"mix_inactive" ) );
	ToolTip::add( mix_osc1_btn, tr( "Mix output of oscillators 1 & 2" ) );

	PixmapButton * sync_osc1_btn = new PixmapButton( this, NULL );
	sync_osc1_btn->move( mod_x + 105, mod1_y );
	sync_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
							"sync_active" ) );
	sync_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"sync_inactive" ) );
	ToolTip::add( sync_osc1_btn, tr( "Synchronize oscillator 1 with "
							"oscillator 2" ) );

	PixmapButton * fm_osc1_btn = new PixmapButton( this, NULL );
	fm_osc1_btn->move( mod_x + 140, mod1_y );
	fm_osc1_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
								"fm_active" ) );
	fm_osc1_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"fm_inactive" ) );
	ToolTip::add( fm_osc1_btn, tr( "Modulate frequency of oscillator 1 by oscillator 2" ) );

	m_mod1BtnGrp = new automatableButtonGroup( this );
	m_mod1BtnGrp->addButton( pm_osc1_btn );
	m_mod1BtnGrp->addButton( am_osc1_btn );
	m_mod1BtnGrp->addButton( mix_osc1_btn );
	m_mod1BtnGrp->addButton( sync_osc1_btn );
	m_mod1BtnGrp->addButton( fm_osc1_btn );

	PixmapButton * pm_osc2_btn = new PixmapButton( this, NULL );
	pm_osc2_btn->move( mod_x, mod2_y );
	pm_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
								"pm_active" ) );
	pm_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"pm_inactive" ) );
	ToolTip::add( pm_osc2_btn, tr( "Modulate phase of oscillator 2 by oscillator 3" ) );

	PixmapButton * am_osc2_btn = new PixmapButton( this, NULL );
	am_osc2_btn->move( mod_x + 35, mod2_y );
	am_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
								"am_active" ) );
	am_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"am_inactive" ) );
	ToolTip::add( am_osc2_btn, tr( "Modulate amplitude of oscillator 2 by oscillator 3" ) );

	PixmapButton * mix_osc2_btn = new PixmapButton( this, NULL );
	mix_osc2_btn->move( mod_x + 70, mod2_y );
	mix_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
							"mix_active" ) );
	mix_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"mix_inactive" ) );
	ToolTip::add( mix_osc2_btn, tr("Mix output of oscillators 2 & 3" ) );

	PixmapButton * sync_osc2_btn = new PixmapButton( this, NULL );
	sync_osc2_btn->move( mod_x + 105, mod2_y );
	sync_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
							"sync_active" ) );
	sync_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"sync_inactive" ) );
	ToolTip::add( sync_osc2_btn, tr( "Synchronize oscillator 2 with oscillator 3" ) );

	PixmapButton * fm_osc2_btn = new PixmapButton( this, NULL );
	fm_osc2_btn->move( mod_x + 140, mod2_y );
	fm_osc2_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
								"fm_active" ) );
	fm_osc2_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"fm_inactive" ) );
	ToolTip::add( fm_osc2_btn, tr( "Modulate frequency of oscillator 2 by oscillator 3" ) );

	m_mod2BtnGrp = new automatableButtonGroup( this );

	m_mod2BtnGrp->addButton( pm_osc2_btn );
	m_mod2BtnGrp->addButton( am_osc2_btn );
	m_mod2BtnGrp->addButton( mix_osc2_btn );
	m_mod2BtnGrp->addButton( sync_osc2_btn );
	m_mod2BtnGrp->addButton( fm_osc2_btn );

	for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
	{
		int knob_y = osc_y + i * osc_h;

		// setup volume-knob
		Knob * vk = new Knob( knobStyled, this );
		vk->setVolumeKnob( true );
		vk->setFixedSize( 28, 35 );
		vk->move( 6, knob_y );
		vk->setHintText( tr( "Osc %1 volume:" ).arg(
							 i+1 ), "%" );

		// setup panning-knob
		Knob * pk = new TripleOscKnob( this );
		pk->move( 35, knob_y );
		pk->setHintText( tr("Osc %1 panning:").arg( i + 1 ), "" );

		// setup coarse-knob
		Knob * ck = new TripleOscKnob( this );
		ck->move( 82, knob_y );
		ck->setHintText( tr( "Osc %1 coarse detuning:" ).arg( i + 1 )
						 , " " + tr( "semitones" ) );

		// setup knob for left fine-detuning
		Knob * flk = new TripleOscKnob( this );
		flk->move( 111, knob_y );
		flk->setHintText( tr( "Osc %1 fine detuning left:" ).
						  arg( i + 1 ),
							" " + tr( "cents" ) );

		// setup knob for right fine-detuning
		Knob * frk = new TripleOscKnob( this );
		frk->move( 140, knob_y );
		frk->setHintText( tr( "Osc %1 fine detuning right:" ).
						  arg( i + 1 ),
							" " + tr( "cents" ) );

		// setup phase-offset-knob
		Knob * pok = new TripleOscKnob( this );
		pok->move( 188, knob_y );
		pok->setHintText( tr( "Osc %1 phase-offset:" ).
						  arg( i + 1 ),
							" " + tr( "degrees" ) );

		// setup stereo-phase-detuning-knob
		Knob * spdk = new TripleOscKnob( this );
		spdk->move( 217, knob_y );
		spdk->setHintText( tr("Osc %1 stereo phase-detuning:" ).
						arg( i + 1 ),
							" " + tr( "degrees" ) );

		int btn_y = 96 + i * osc_h;

		PixmapButton * sin_wave_btn = new PixmapButton( this, NULL );
		sin_wave_btn->move( 128, btn_y );
		sin_wave_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
							"sin_shape_active" ) );
		sin_wave_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"sin_shape_inactive" ) );
		ToolTip::add( sin_wave_btn,
				tr( "Sine wave" ) );

		PixmapButton * triangle_wave_btn =
						new PixmapButton( this, NULL );
		triangle_wave_btn->move( 143, btn_y );
		triangle_wave_btn->setActiveGraphic(
			PLUGIN_NAME::getIconPixmap( "triangle_shape_active" ) );
		triangle_wave_btn->setInactiveGraphic(
			PLUGIN_NAME::getIconPixmap( "triangle_shape_inactive" ) );
		ToolTip::add( triangle_wave_btn,
				tr( "Triangle wave") );

		PixmapButton * saw_wave_btn = new PixmapButton( this, NULL );
		saw_wave_btn->move( 158, btn_y );
		saw_wave_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
							"saw_shape_active" ) );
		saw_wave_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"saw_shape_inactive" ) );
		ToolTip::add( saw_wave_btn,
				tr( "Saw wave" ) );

		PixmapButton * sqr_wave_btn = new PixmapButton( this, NULL );
		sqr_wave_btn->move( 173, btn_y );
		sqr_wave_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
						"square_shape_active" ) );
		sqr_wave_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
						"square_shape_inactive" ) );
		ToolTip::add( sqr_wave_btn,
				tr( "Square wave" ) );

		PixmapButton * moog_saw_wave_btn =
						new PixmapButton( this, NULL );
		moog_saw_wave_btn->move( 188, btn_y );
		moog_saw_wave_btn->setActiveGraphic(
			PLUGIN_NAME::getIconPixmap( "moog_saw_shape_active" ) );
		moog_saw_wave_btn->setInactiveGraphic(
			PLUGIN_NAME::getIconPixmap( "moog_saw_shape_inactive" ) );
		ToolTip::add( moog_saw_wave_btn,
				tr( "Moog-like saw wave" ) );

		PixmapButton * exp_wave_btn = new PixmapButton( this, NULL );
		exp_wave_btn->move( 203, btn_y );
		exp_wave_btn->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
							"exp_shape_active" ) );
		exp_wave_btn->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"exp_shape_inactive" ) );
		ToolTip::add( exp_wave_btn,
				tr( "Exponential wave" ) );

		PixmapButton * white_noise_btn = new PixmapButton( this, NULL );
		white_noise_btn->move( 218, btn_y );
		white_noise_btn->setActiveGraphic(
			PLUGIN_NAME::getIconPixmap( "white_noise_shape_active" ) );
		white_noise_btn->setInactiveGraphic(
			PLUGIN_NAME::getIconPixmap( "white_noise_shape_inactive" ) );
		ToolTip::add( white_noise_btn,
				tr( "White noise" ) );

		PixmapButton * uwb = new PixmapButton( this, NULL );
		uwb->move( 233, btn_y );
		uwb->setActiveGraphic( PLUGIN_NAME::getIconPixmap(
							"usr_shape_active" ) );
		uwb->setInactiveGraphic( PLUGIN_NAME::getIconPixmap(
							"usr_shape_inactive" ) );
		ToolTip::add( uwb, tr( "User-defined wave" ) );

		automatableButtonGroup * wsbg =
			new automatableButtonGroup( this );

		wsbg->addButton( sin_wave_btn );
		wsbg->addButton( triangle_wave_btn );
		wsbg->addButton( saw_wave_btn );
		wsbg->addButton( sqr_wave_btn );
		wsbg->addButton( moog_saw_wave_btn );
		wsbg->addButton( exp_wave_btn );
		wsbg->addButton( white_noise_btn );
		wsbg->addButton( uwb );

		m_oscKnobs[i] = OscillatorKnobs( vk, pk, ck, flk, frk, pok,
							spdk, uwb, wsbg );
	}
}

TripleOscillatorView::~TripleOscillatorView()
{
}

void TripleOscillatorView::modelChanged()
{
	TripleOscillator * t = castModel<TripleOscillator>();
	m_mod1BtnGrp->setModel( &t->m_osc[0]->m_modulationAlgoModel );
	m_mod2BtnGrp->setModel( &t->m_osc[1]->m_modulationAlgoModel );

	for( int i = 0; i < NUM_OF_OSCILLATORS; ++i )
	{
		m_oscKnobs[i].m_volKnob->setModel(
					&t->m_osc[i]->m_volumeModel );
		m_oscKnobs[i].m_panKnob->setModel(
					&t->m_osc[i]->m_panModel );
		m_oscKnobs[i].m_coarseKnob->setModel(
					&t->m_osc[i]->m_coarseModel );
		m_oscKnobs[i].m_fineLeftKnob->setModel(
					&t->m_osc[i]->m_fineLeftModel );
		m_oscKnobs[i].m_fineRightKnob->setModel(
					&t->m_osc[i]->m_fineRightModel );
		m_oscKnobs[i].m_phaseOffsetKnob->setModel(
					&t->m_osc[i]->m_phaseOffsetModel );
		m_oscKnobs[i].m_stereoPhaseDetuningKnob->setModel(
				&t->m_osc[i]->m_stereoPhaseDetuningModel );
		m_oscKnobs[i].m_waveShapeBtnGrp->setModel(
					&t->m_osc[i]->m_waveShapeModel );
		connect( m_oscKnobs[i].m_userWaveButton,
						SIGNAL( doubleClicked() ),
				t->m_osc[i], SLOT( oscUserDefWaveDblClick() ) );
	}
}

extern "C"
{

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

}


code:
/*
 * TripleOscillator.h - declaration of class TripleOscillator a powerful
 *                      instrument-plugin with 3 oscillators
 *
 * Copyright (c) 2004-2014 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 _TRIPLE_OSCILLATOR_H
#define _TRIPLE_OSCILLATOR_H

#include "Instrument.h"
#include "InstrumentView.h"
#include "Oscillator.h"
#include "AutomatableModel.h"

class automatableButtonGroup;
class Knob;
class NotePlayHandle;
class PixmapButton;
class SampleBuffer;

const int NUM_OF_OSCILLATORS = 3;

class OscillatorObject : public Model
{
	MM_OPERATORS
	Q_OBJECT
public:
	OscillatorObject( Model * _parent, int _idx );
	virtual ~OscillatorObject();

private:
	FloatModel m_volumeModel;
	FloatModel m_panModel;
	FloatModel m_coarseModel;
	FloatModel m_fineLeftModel;
	FloatModel m_fineRightModel;
	FloatModel m_phaseOffsetModel;
	FloatModel m_stereoPhaseDetuningModel;
	IntModel m_waveShapeModel;
	IntModel m_modulationAlgoModel;
	SampleBuffer* m_sampleBuffer;

	float m_volumeLeft;
	float m_volumeRight;

	// normalized detuning -> x/sampleRate
	float m_detuningLeft;
	float m_detuningRight;
	// normalized offset -> x/360
	float m_phaseOffsetLeft;
	float m_phaseOffsetRight;

	friend class TripleOscillator;
	friend class TripleOscillatorView;

private slots:
	void oscUserDefWaveDblClick();

	void updateVolume();
	void updateDetuningLeft();
	void updateDetuningRight();
	void updatePhaseOffsetLeft();
	void updatePhaseOffsetRight();

} ;

class TripleOscillator : public Instrument
{
	Q_OBJECT
public:
	TripleOscillator( InstrumentTrack * _track );
	virtual ~TripleOscillator();

	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( 128 );
	}

	virtual PluginView * instantiateView( QWidget * _parent );

protected slots:
	void updateAllDetuning();

private:
	OscillatorObject * m_osc[NUM_OF_OSCILLATORS];

	struct oscPtr
	{
		MM_OPERATORS
		Oscillator * oscLeft;
		Oscillator * oscRight;
	} ;

	friend class TripleOscillatorView;

} ;

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

private:
	virtual void modelChanged();

	automatableButtonGroup * m_mod1BtnGrp;
	automatableButtonGroup * m_mod2BtnGrp;

	struct OscillatorKnobs
	{
		MM_OPERATORS
		OscillatorKnobs( Knob * v,
					Knob * p,
					Knob * c,
					Knob * fl,
					Knob * fr,
					Knob * po,
					Knob * spd,
					PixmapButton * uwb,
					automatableButtonGroup * wsbg ) :
			m_volKnob( v ),
			m_panKnob( p ),
			m_coarseKnob( c ),
			m_fineLeftKnob( fl ),
			m_fineRightKnob( fr ),
			m_phaseOffsetKnob( po ),
			m_stereoPhaseDetuningKnob( spd ),
			m_userWaveButton( uwb ),
			m_waveShapeBtnGrp( wsbg )
		{
		}
		OscillatorKnobs()
		{
		}
		Knob * m_volKnob;
		Knob * m_panKnob;
		Knob * m_coarseKnob;
		Knob * m_fineLeftKnob;
		Knob * m_fineRightKnob;
		Knob * m_phaseOffsetKnob;
		Knob * m_stereoPhaseDetuningKnob;
		PixmapButton * m_userWaveButton;
		automatableButtonGroup * m_waveShapeBtnGrp;

	} ;

	OscillatorKnobs m_oscKnobs[NUM_OF_OSCILLATORS];
} ;

#endif


code:
IF(LMMS_SUPPORT_VST)
   INCLUDE(BuildPlugin)
   LINK_DIRECTORIES("${CMAKE_CURRENT_BINARY_DIR}/..")
   IF(LMMS_BUILD_LINUX)
      LINK_LIBRARIES(-Wl,--enable-new-dtags)
      SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
      SET(CMAKE_INSTALL_RPATH "$ORIGIN")
   ELSE()
      SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${PLUGIN_DIR}")
   ENDIF()
   BUILD_PLUGIN(vestige vestige.cpp vestige.h MOCFILES vestige.h EMBEDDED_RESOURCES "${CMAKE_CURRENT_SOURCE_DIR}/*.png")
   TARGET_LINK_LIBRARIES(vestige vstbase)
ENDIF(LMMS_SUPPORT_VST)