code:
/*
 * patches_dialog.cpp - display sf2 patches
 *
 * 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 "patches_dialog.h"

#include <QHeaderView>
//#include <QFileInfo>

// Custom list-view item (as for numerical sort purposes...)
class patchItem : public QTreeWidgetItem
{
public:

	// Constructor.
	patchItem( QTreeWidget *pListView,
		QTreeWidgetItem *pItemAfter )
		: QTreeWidgetItem( pListView, pItemAfter ) {}

	// Sort/compare overriden method.
	bool operator< ( const QTreeWidgetItem& other ) const
	{
		int iColumn = QTreeWidgetItem::treeWidget()->sortColumn();
		const QString& s1 = text( iColumn );
		const QString& s2 = other.text( iColumn );
		if( iColumn == 0 || iColumn == 2 )
		{
			return( s1.toInt() < s2.toInt() );
		}
		else
		{
			return( s1 < s2 );
		}
	}
};

// Constructor.
patchesDialog::patchesDialog( QWidget *pParent, Qt::WindowFlags wflags )
	: QDialog( pParent, wflags )
{
	// Setup UI struct...
	setupUi( this );

	m_pSynth = NULL;
	m_iChan  = 0;
	m_iBank  = 0;
	m_iProg  = 0;

	// Soundfonts list view...
	QHeaderView *pHeader = m_progListView->header();
//	pHeader->setResizeMode(QHeaderView::Custom);
	pHeader->setDefaultAlignment(Qt::AlignLeft);
//	pHeader->setDefaultSectionSize(200);
	pHeader->setSectionsMovable(false);
	pHeader->setStretchLastSection(true);

	m_progListView->resizeColumnToContents(0);	// Prog.
	//pHeader->resizeSection(1, 200);					// Name.

	// Initial sort order...
	m_bankListView->sortItems(0, Qt::AscendingOrder);
	m_progListView->sortItems(0, Qt::AscendingOrder);

	// UI connections...
	QObject::connect(m_bankListView,
		SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
		SLOT(bankChanged()));
	QObject::connect(m_progListView,
		SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)),
		SLOT(progChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
	QObject::connect(m_progListView,
		SIGNAL(itemActivated(QTreeWidgetItem*,int)),
		SLOT(accept()));
	QObject::connect(m_okButton,
		SIGNAL(clicked()),
		SLOT(accept()));
	QObject::connect(m_cancelButton,
		SIGNAL(clicked()),
		SLOT(reject()));
}

// Destructor.
patchesDialog::~patchesDialog()
{
}

// Dialog setup loader.
void patchesDialog::setup ( fluid_synth_t * pSynth, int iChan,
						const QString & _chanName,
						LcdSpinBoxModel * _bankModel,
						LcdSpinBoxModel * _progModel,
							QLabel * _patchLabel )
{

	// We'll going to changes the whole thing...
	m_dirty = 0;
	m_bankModel = _bankModel;
	m_progModel = _progModel;
	m_patchLabel =  _patchLabel;

	// Set the proper caption...
	setWindowTitle( _chanName + " - Soundfont patches" );

	// set m_pSynth to NULL so we don't trigger any progChanged events
	m_pSynth = NULL;

	// Load bank list from actual synth stack...
	m_bankListView->setSortingEnabled(false);
	m_bankListView->clear();

	// now it should be safe to set internal stuff
	m_pSynth = pSynth;
	m_iChan  = iChan;

	QTreeWidgetItem *pBankItem = NULL;
	// For all soundfonts (in reversed stack order) fill the available banks...
	int cSoundFonts = ::fluid_synth_sfcount(m_pSynth);
	for (int i = 0; i < cSoundFonts; i++) {
		fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(m_pSynth, i);
		if (pSoundFont) {
#ifdef CONFIG_FLUID_BANK_OFFSET
			int iBankOffset = ::fluid_synth_get_bank_offset(m_pSynth, fluid_sfont_get_id(pSoundFont));
#endif
			fluid_sfont_iteration_start(pSoundFont);
#if FLUIDSYNTH_VERSION_MAJOR < 2
			fluid_preset_t preset;
			fluid_preset_t *pCurPreset = &preset;
#else
			fluid_preset_t *pCurPreset;
#endif
			while ((pCurPreset = fluid_sfont_iteration_next_wrapper(pSoundFont, pCurPreset))) {
				int iBank = fluid_preset_get_banknum(pCurPreset);
#ifdef CONFIG_FLUID_BANK_OFFSET
				iBank += iBankOffset;
#endif
				if (!findBankItem(iBank)) {
					pBankItem = new patchItem(m_bankListView, pBankItem);
					if (pBankItem)
						pBankItem->setText(0, QString::number(iBank));
				}
			}
		}
	}
	m_bankListView->setSortingEnabled(true);

	// Set the selected bank.
	m_iBank = 0;
	fluid_preset_t *pPreset = ::fluid_synth_get_channel_preset(m_pSynth, m_iChan);
	if (pPreset) {
		m_iBank = fluid_preset_get_banknum(pPreset);
#ifdef CONFIG_FLUID_BANK_OFFSET
		m_iBank += ::fluid_synth_get_bank_offset(m_pSynth, fluid_sfont_get_id(fluid_preset_get_sfont(sfont)));
#endif
	}

	pBankItem = findBankItem(m_iBank);
	m_bankListView->setCurrentItem(pBankItem);
	m_bankListView->scrollToItem(pBankItem);
	bankChanged();

	// Set the selected program.
	if (pPreset)
		m_iProg = fluid_preset_get_num(pPreset);
	QTreeWidgetItem *pProgItem = findProgItem(m_iProg);
	m_progListView->setCurrentItem(pProgItem);
	m_progListView->scrollToItem(pProgItem);

	// Done with setup...
	//m_iDirtySetup--;
}

// Stabilize current state form.
void patchesDialog::stabilizeForm()
{
	m_okButton->setEnabled(validateForm());
}

// Validate form fields.
bool patchesDialog::validateForm()
{
	bool bValid = true;

	bValid = bValid && (m_bankListView->currentItem() != NULL);
	bValid = bValid && (m_progListView->currentItem() != NULL);

	return bValid;
}

// Realize a bank-program selection preset.
void patchesDialog::setBankProg ( int iBank, int iProg )
{
	if (m_pSynth == NULL)
		return;

	// just select the synth's program preset...
	::fluid_synth_bank_select(m_pSynth, m_iChan, iBank);
	::fluid_synth_program_change(m_pSynth, m_iChan, iProg);
	// Maybe this is needed to stabilize things around.
	::fluid_synth_program_reset(m_pSynth);
}

// Validate form fields and accept it valid.
void patchesDialog::accept()
{
	if (validateForm()) {
		// Unload from current selected dialog items.
		int iBank = (m_bankListView->currentItem())->text(0).toInt();
		int iProg = (m_progListView->currentItem())->text(0).toInt();
		// And set it right away...
		setBankProg(iBank, iProg);

		if (m_dirty > 0) {
			m_bankModel->setValue( iBank );
			m_progModel->setValue( iProg );
			m_patchLabel->setText( m_progListView->
						currentItem()->text( 1 ) );
		}

		// Do remember preview state...
		// if (m_pOptions)
			// m_pOptions->bPresetPreview = m_ui.PreviewCheckBox->isChecked();
		// We got it.
		QDialog::accept();
	}
}

// Reject settings (Cancel button slot).
void patchesDialog::reject (void)
{
	// Reset selection to initial selection, if applicable...
	if (m_dirty > 0)
		setBankProg(m_bankModel->value(), m_progModel->value());
	// Done (hopefully nothing).
	QDialog::reject();
}

// Find the bank item of given bank number id.
QTreeWidgetItem *patchesDialog::findBankItem ( int iBank )
{
	QList<QTreeWidgetItem *> banks
		= m_bankListView->findItems(
			QString::number(iBank), Qt::MatchExactly, 0);

	QListIterator<QTreeWidgetItem *> iter(banks);
	if (iter.hasNext())
		return iter.next();
	else
		return NULL;
}

// Find the program item of given program number id.
QTreeWidgetItem *patchesDialog::findProgItem ( int iProg )
{
	QList<QTreeWidgetItem *> progs
		= m_progListView->findItems(
			QString::number(iProg), Qt::MatchExactly, 0);

	QListIterator<QTreeWidgetItem *> iter(progs);
	if (iter.hasNext())
		return iter.next();
	else
		return NULL;
}

// Bank change slot.
void patchesDialog::bankChanged (void)
{
	if (m_pSynth == NULL)
		return;

	QTreeWidgetItem *pBankItem = m_bankListView->currentItem();
	if (pBankItem == NULL)
		return;

	int iBankSelected = pBankItem->text(0).toInt();

	// Clear up the program listview.
	m_progListView->setSortingEnabled(false);
	m_progListView->clear();
	QTreeWidgetItem *pProgItem = NULL;
	// For all soundfonts (in reversed stack order) fill the available programs...
	int cSoundFonts = ::fluid_synth_sfcount(m_pSynth);
	for (int i = 0; i < cSoundFonts && !pProgItem; i++) {
		fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont(m_pSynth, i);
		if (pSoundFont) {
#ifdef CONFIG_FLUID_BANK_OFFSET
			int iBankOffset = ::fluid_synth_get_bank_offset(m_pSynth, fluid_sfont_get_id(pSoundFont));
#endif
			fluid_sfont_iteration_start(pSoundFont);
#if FLUIDSYNTH_VERSION_MAJOR < 2
			fluid_preset_t preset;
			fluid_preset_t *pCurPreset = &preset;
#else
			fluid_preset_t *pCurPreset;
#endif
			while ((pCurPreset = fluid_sfont_iteration_next_wrapper(pSoundFont, pCurPreset))) {
				int iBank = fluid_preset_get_banknum(pCurPreset);
#ifdef CONFIG_FLUID_BANK_OFFSET
				iBank += iBankOffset;
#endif
				int iProg = fluid_preset_get_num(pCurPreset);
				if (iBank == iBankSelected && !findProgItem(iProg)) {
					pProgItem = new patchItem(m_progListView, pProgItem);
					if (pProgItem) {
						pProgItem->setText(0, QString::number(iProg));
						pProgItem->setText(1, fluid_preset_get_name(pCurPreset));
						//pProgItem->setText(2, QString::number(fluid_sfont_get_id(pSoundFont)));
						//pProgItem->setText(3, QFileInfo(
						//	fluid_sfont_get_name(pSoundFont).baseName());
					}
				}
			}
		}
	}
	m_progListView->setSortingEnabled(true);

	// Stabilize the form.
	stabilizeForm();
}

// Program change slot.
void patchesDialog::progChanged (QTreeWidgetItem * _curr, QTreeWidgetItem * _prev)
{
	if (m_pSynth == NULL || _curr == NULL)
		return;

	// Which preview state...
	if( validateForm() ) {
		// Set current selection.
		int iBank = (m_bankListView->currentItem())->text(0).toInt();
		int iProg = _curr->text(0).toInt();
		// And set it right away...
		setBankProg(iBank, iProg);
		// Now we're dirty nuff.
		m_dirty++;
	}

	// Stabilize the form.
	stabilizeForm();
}


code:
/*
 * patches_dialog.h - display sf2 patches
 *
 * 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 _PATCHES_DIALOG_H
#define _PATCHES_DIALOG_H

#include "ui_patches_dialog.h"
#include "LcdSpinBox.h"

#include "fluidsynthshims.h"
#include <QWidget>
#include <QLabel>

//----------------------------------------------------------------------------
// qsynthPresetForm -- UI wrapper form.

class patchesDialog : public QDialog, private Ui::patchesDialog
{
	Q_OBJECT

public:

	// Constructor.
	patchesDialog(QWidget *pParent = 0, Qt::WindowFlags wflags = QFlag(0));

	// Destructor.
	virtual ~patchesDialog();

	void setup(fluid_synth_t *pSynth, int iChan, const QString & _chanName,
			LcdSpinBoxModel * _bankModel, LcdSpinBoxModel * _progModel, QLabel *_patchLabel );

public slots:

	void stabilizeForm();
	void bankChanged();
	void progChanged( QTreeWidgetItem * _curr, QTreeWidgetItem * _prev );

protected slots:

	void accept();
	void reject();

protected:

	void setBankProg(int iBank, int iProg);

	QTreeWidgetItem *findBankItem(int iBank);
	QTreeWidgetItem *findProgItem(int iProg);

	bool validateForm();

private:

	// Instance variables.
	fluid_synth_t *m_pSynth;

	int m_iChan;
	int m_iBank;
	int m_iProg;

	//int m_iDirtySetup;
	//int m_iDirtyCount;
	int m_dirty;

	LcdSpinBoxModel * m_bankModel;
	LcdSpinBoxModel * m_progModel;
	QLabel *m_patchLabel;
};

#endif

repo_name: CYBERDEViL/LMMS

license: mit


code:
<ui version="4.0" >
 <author>rncbc aka Rui Nuno Capela</author>
 <comment>qsynth - A fluidsynth Qt GUI Interface.

   Copyright (C) 2003-2007, rncbc aka Rui Nuno Capela. All rights reserved.

   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; if not, write to the Free Software Foundation, Inc.,
   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

</comment>
 <class>patchesDialog</class>
 <widget class="QDialog" name="patchesDialog" >
  <property name="geometry" >
   <rect>
    <x>0</x>
    <y>0</y>
    <width>480</width>
    <height>350</height>
   </rect>
  </property>
  <property name="minimumSize" >
   <size>
    <width>300</width>
    <height>150</height>
   </size>
  </property>
  <property name="windowTitle" >
   <string>Qsynth: Channel Preset</string>
  </property>
  <layout class="QVBoxLayout" >
   <item>
    <widget class="QSplitter" name="m_splitter" >
     <property name="sizePolicy" >
      <sizepolicy vsizetype="Expanding" hsizetype="Expanding" >
       <horstretch>0</horstretch>
       <verstretch>0</verstretch>
      </sizepolicy>
     </property>
     <property name="orientation" >
      <enum>Qt::Horizontal</enum>
     </property>
     <widget class="QTreeWidget" name="m_bankListView" >
      <property name="minimumSize" >
       <size>
        <width>20</width>
        <height>0</height>
       </size>
      </property>
      <property name="maximumSize" >
       <size>
        <width>80</width>
        <height>32767</height>
       </size>
      </property>
      <property name="toolTip" >
       <string>Bank selector</string>
      </property>
      <property name="alternatingRowColors" >
       <bool>true</bool>
      </property>
      <property name="indentation" >
       <number>4</number>
      </property>
      <property name="rootIsDecorated" >
       <bool>false</bool>
      </property>
      <property name="uniformRowHeights" >
       <bool>true</bool>
      </property>
      <property name="itemsExpandable" >
       <bool>false</bool>
      </property>
      <property name="sortingEnabled" >
       <bool>true</bool>
      </property>
      <property name="allColumnsShowFocus" >
       <bool>true</bool>
      </property>
      <column>
       <property name="text" >
        <string>Bank</string>
       </property>
      </column>
     </widget>
     <widget class="QTreeWidget" name="m_progListView" >
      <property name="toolTip" >
       <string>Program selector</string>
      </property>
      <property name="alternatingRowColors" >
       <bool>true</bool>
      </property>
      <property name="indentation" >
       <number>4</number>
      </property>
      <property name="rootIsDecorated" >
       <bool>false</bool>
      </property>
      <property name="uniformRowHeights" >
       <bool>true</bool>
      </property>
      <property name="itemsExpandable" >
       <bool>false</bool>
      </property>
      <property name="sortingEnabled" >
       <bool>true</bool>
      </property>
      <property name="allColumnsShowFocus" >
       <bool>true</bool>
      </property>
      <column>
       <property name="text" >
        <string>Patch</string>
       </property>
      </column>
      <column>
       <property name="text" >
        <string>Name</string>
       </property>
      </column>
     </widget>
    </widget>
   </item>
   <item>
    <layout class="QHBoxLayout" >
     <property name="spacing" >
      <number>4</number>
     </property>
     <property name="leftMargin" >
      <number>0</number>
     </property>
     <property name="topMargin" >
      <number>0</number>
     </property>
     <property name="rightMargin" >
      <number>0</number>
     </property>
     <property name="bottomMargin" >
      <number>0</number>
     </property>
     <item>
      <spacer>
       <property name="orientation" >
        <enum>Qt::Horizontal</enum>
       </property>
       <property name="sizeType" >
        <enum>QSizePolicy::Expanding</enum>
       </property>
       <property name="sizeHint" >
        <size>
         <width>120</width>
         <height>8</height>
        </size>
       </property>
      </spacer>
     </item>
     <item>
      <widget class="QPushButton" name="m_okButton" >
       <property name="toolTip" >
        <string/>
       </property>
       <property name="text" >
        <string>OK</string>
       </property>
       <property name="iconSize" >
        <size>
         <width>0</width>
         <height>0</height>
        </size>
       </property>
       <property name="default" >
        <bool>true</bool>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="m_cancelButton" >
       <property name="toolTip" >
        <string/>
       </property>
       <property name="text" >
        <string>Cancel</string>
       </property>
       <property name="iconSize" >
        <size>
         <width>0</width>
         <height>0</height>
        </size>
       </property>
      </widget>
     </item>
    </layout>
   </item>
  </layout>
 </widget>
 <layoutdefault spacing="4" margin="4" />
 <tabstops>
  <tabstop>m_okButton</tabstop>
  <tabstop>m_cancelButton</tabstop>
 </tabstops>
 <resources/>
 <connections/>
</ui>


code:
/*
 * sf2_player.cpp - a soundfont2 player using fluidSynth
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
 * Copyright (c) 2009-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 "sf2_player.h"

#include <QDebug>
#include <QLayout>
#include <QLabel>
#include <QDomDocument>

#include "ConfigManager.h"
#include "FileDialog.h"
#include "ConfigManager.h"
#include "Engine.h"
#include "InstrumentTrack.h"
#include "InstrumentPlayHandle.h"
#include "Knob.h"
#include "Mixer.h"
#include "NotePlayHandle.h"
#include "PathUtil.h"
#include "SampleBuffer.h"
#include "Song.h"

#include "patches_dialog.h"
#include "ToolTip.h"
#include "LcdSpinBox.h"

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

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT sf2player_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"Sf2 Player",
	QT_TRANSLATE_NOOP( "PluginBrowser", "Player for SoundFont files" ),
	"Paul Giblock <drfaygo/at/gmail/dot/com>",
	0x0100,
	Plugin::Instrument,
	new PluginPixmapLoader( "logo" ),
	"sf2,sf3",
	NULL
} ;

}

struct SF2PluginData
{
	int midiNote;
	int lastPanning;
	float lastVelocity;
	fluid_voice_t * fluidVoice;
	bool isNew;
	f_cnt_t offset;
	bool noteOffSent;
} ;

// Static map of current sfonts
QMap<QString, sf2Font*> sf2Instrument::s_fonts;
QMutex sf2Instrument::s_fontsMutex;

sf2Instrument::sf2Instrument( InstrumentTrack * _instrument_track ) :
	Instrument( _instrument_track, &sf2player_plugin_descriptor ),
	m_srcState( NULL ),
	m_font( NULL ),
	m_fontId( 0 ),
	m_filename( "" ),
	m_lastMidiPitch( -1 ),
	m_lastMidiPitchRange( -1 ),
	m_channel( 1 ),
	m_bankNum( 0, 0, 999, this, tr("Bank") ),
	m_patchNum( 0, 0, 127, this, tr("Patch") ),
	m_gain( 1.0f, 0.0f, 5.0f, 0.01f, this, tr( "Gain" ) ),
	m_reverbOn( false, this, tr( "Reverb" ) ),
	m_reverbRoomSize( FLUID_REVERB_DEFAULT_ROOMSIZE, 0, 1.0, 0.01f, this, tr( "Reverb room size" ) ),
	m_reverbDamping( FLUID_REVERB_DEFAULT_DAMP, 0, 1.0, 0.01, this, tr( "Reverb damping" ) ),
	m_reverbWidth( FLUID_REVERB_DEFAULT_WIDTH, 0, 1.0, 0.01f, this, tr( "Reverb width" ) ),
	m_reverbLevel( FLUID_REVERB_DEFAULT_LEVEL, 0, 1.0, 0.01f, this, tr( "Reverb level" ) ),
	m_chorusOn( false, this, tr( "Chorus" ) ),
	m_chorusNum( FLUID_CHORUS_DEFAULT_N, 0, 10.0, 1.0, this, tr( "Chorus voices" ) ),
	m_chorusLevel( FLUID_CHORUS_DEFAULT_LEVEL, 0, 10.0, 0.01, this, tr( "Chorus level" ) ),
	m_chorusSpeed( FLUID_CHORUS_DEFAULT_SPEED, 0.29, 5.0, 0.01, this, tr( "Chorus speed" ) ),
	m_chorusDepth( FLUID_CHORUS_DEFAULT_DEPTH, 0, 46.0, 0.05, this, tr( "Chorus depth" ) )
{
	for( int i = 0; i < 128; ++i )
	{
		m_notesRunning[i] = 0;
	}

#if QT_VERSION_CHECK(FLUIDSYNTH_VERSION_MAJOR, FLUIDSYNTH_VERSION_MINOR, FLUIDSYNTH_VERSION_MICRO) >= QT_VERSION_CHECK(1,1,9)
	// Deactivate all audio drivers in fluidsynth
	const char *none[] = { NULL };
	fluid_audio_driver_register( none );
#endif
	m_settings = new_fluid_settings();

	//fluid_settings_setint( m_settings, (char *) "audio.period-size", engine::mixer()->framesPerPeriod() );

	// This is just our starting instance of synth.  It is recreated
	// everytime we load a new soundfont.
	m_synth = new_fluid_synth( m_settings );

#if FLUIDSYNTH_VERSION_MAJOR >= 2
	// Get the default values from the setting
	double settingVal;

	fluid_settings_getnum_default(m_settings, "synth.reverb.room-size", &settingVal);
	m_reverbRoomSize.setInitValue(settingVal);
	fluid_settings_getnum_default(m_settings, "synth.reverb.damping", &settingVal);
	m_reverbDamping.setInitValue(settingVal);
	fluid_settings_getnum_default(m_settings, "synth.reverb.width", &settingVal);
	m_reverbWidth.setInitValue(settingVal);
	fluid_settings_getnum_default(m_settings, "synth.reverb.level", &settingVal);
	m_reverbLevel.setInitValue(settingVal);

	fluid_settings_getnum_default(m_settings, "synth.chorus.nr", &settingVal);
	m_chorusNum.setInitValue(settingVal);
	fluid_settings_getnum_default(m_settings, "synth.chorus.level", &settingVal);
	m_chorusLevel.setInitValue(settingVal);
	fluid_settings_getnum_default(m_settings, "synth.chorus.speed", &settingVal);
	m_chorusSpeed.setInitValue(settingVal);
	fluid_settings_getnum_default(m_settings, "synth.chorus.depth", &settingVal);
	m_chorusDepth.setInitValue(settingVal);
#endif

	loadFile( ConfigManager::inst()->sf2File() );

	updateSampleRate();
	updateReverbOn();
	updateReverb();
	updateChorusOn();
	updateChorus();
	updateGain();

	connect( &m_bankNum, SIGNAL( dataChanged() ), this, SLOT( updatePatch() ) );
	connect( &m_patchNum, SIGNAL( dataChanged() ), this, SLOT( updatePatch() ) );

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

	// Gain
	connect( &m_gain, SIGNAL( dataChanged() ), this, SLOT( updateGain() ) );

	// Reverb
	connect( &m_reverbOn, SIGNAL( dataChanged() ), this, SLOT( updateReverbOn() ) );
	connect( &m_reverbRoomSize, SIGNAL( dataChanged() ), this, SLOT( updateReverb() ) );
	connect( &m_reverbDamping, SIGNAL( dataChanged() ), this, SLOT( updateReverb() ) );
	connect( &m_reverbWidth, SIGNAL( dataChanged() ), this, SLOT( updateReverb() ) );
	connect( &m_reverbLevel, SIGNAL( dataChanged() ), this, SLOT( updateReverb() ) );

	// Chorus
	connect( &m_chorusOn, SIGNAL( dataChanged() ), this, SLOT( updateChorusOn() ) );
	connect( &m_chorusNum, SIGNAL( dataChanged() ), this, SLOT( updateChorus() ) );
	connect( &m_chorusLevel, SIGNAL( dataChanged() ), this, SLOT( updateChorus() ) );
	connect( &m_chorusSpeed, SIGNAL( dataChanged() ), this, SLOT( updateChorus() ) );
	connect( &m_chorusDepth, SIGNAL( dataChanged() ), this, SLOT( updateChorus() ) );

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

sf2Instrument::~sf2Instrument()
{
	Engine::mixer()->removePlayHandlesOfTypes( instrumentTrack(),
				PlayHandle::TypeNotePlayHandle
				| PlayHandle::TypeInstrumentPlayHandle );
	freeFont();
	delete_fluid_synth( m_synth );
	delete_fluid_settings( m_settings );
	if( m_srcState != NULL )
	{
		src_delete( m_srcState );
	}

}

void sf2Instrument::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	_this.setAttribute( "src", m_filename );
	m_patchNum.saveSettings( _doc, _this, "patch" );
	m_bankNum.saveSettings( _doc, _this, "bank" );

	m_gain.saveSettings( _doc, _this, "gain" );

	m_reverbOn.saveSettings( _doc, _this, "reverbOn" );
	m_reverbRoomSize.saveSettings( _doc, _this, "reverbRoomSize" );
	m_reverbDamping.saveSettings( _doc, _this, "reverbDamping" );
	m_reverbWidth.saveSettings( _doc, _this, "reverbWidth" );
	m_reverbLevel.saveSettings( _doc, _this, "reverbLevel" );

	m_chorusOn.saveSettings( _doc, _this, "chorusOn" );
	m_chorusNum.saveSettings( _doc, _this, "chorusNum" );
	m_chorusLevel.saveSettings( _doc, _this, "chorusLevel" );
	m_chorusSpeed.saveSettings( _doc, _this, "chorusSpeed" );
	m_chorusDepth.saveSettings( _doc, _this, "chorusDepth" );
}

void sf2Instrument::loadSettings( const QDomElement & _this )
{
	openFile( _this.attribute( "src" ), false );
	m_patchNum.loadSettings( _this, "patch" );
	m_bankNum.loadSettings( _this, "bank" );

	m_gain.loadSettings( _this, "gain" );

	m_reverbOn.loadSettings( _this, "reverbOn" );
	m_reverbRoomSize.loadSettings( _this, "reverbRoomSize" );
	m_reverbDamping.loadSettings( _this, "reverbDamping" );
	m_reverbWidth.loadSettings( _this, "reverbWidth" );
	m_reverbLevel.loadSettings( _this, "reverbLevel" );

	m_chorusOn.loadSettings( _this, "chorusOn" );
	m_chorusNum.loadSettings( _this, "chorusNum" );
	m_chorusLevel.loadSettings( _this, "chorusLevel" );
	m_chorusSpeed.loadSettings( _this, "chorusSpeed" );
	m_chorusDepth.loadSettings( _this, "chorusDepth" );

	updatePatch();
	updateGain();
}

void sf2Instrument::loadFile( const QString & _file )
{
	if( !_file.isEmpty() && QFileInfo( _file ).exists() )
	{
		openFile( _file, false );
		updatePatch();

		// for some reason we've to call that, otherwise preview of a
		// soundfont for the first time fails
		updateSampleRate();
	}

	// setting the first bank and patch number that is found
	auto sSoundCount = ::fluid_synth_sfcount( m_synth );
	for ( int i = 0; i < sSoundCount; ++i ) {
		int iBank = 0;
		int iProg = 0;
		fluid_sfont_t *pSoundFont = ::fluid_synth_get_sfont( m_synth, i );

		if ( pSoundFont ) {
#ifdef CONFIG_FLUID_BANK_OFFSET
			int iBankOff = ::fluid_synth_get_bank_offset( m_synth, fluid_sfont_get_id( pSoundFont ) );
#endif

			fluid_sfont_iteration_start( pSoundFont );
#if FLUIDSYNTH_VERSION_MAJOR < 2
			fluid_preset_t preset;
			fluid_preset_t *pCurPreset = &preset;
#else
			fluid_preset_t *pCurPreset;
#endif

			if ( ( pCurPreset = fluid_sfont_iteration_next_wrapper( pSoundFont, pCurPreset ) ) ) {
				iBank = fluid_preset_get_banknum( pCurPreset );
				iProg = fluid_preset_get_num( pCurPreset );

#ifdef CONFIG_FLUID_BANK_OFFSET
				iBank += iBankOff;
#endif

				::fluid_synth_bank_select( m_synth, 1, iBank );
				::fluid_synth_program_change( m_synth, 1, iProg );
				m_bankNum.setValue( iBank );
				m_patchNum.setValue ( iProg );
				break;
			}
		}
	}
}

AutomatableModel * sf2Instrument::childModel( const QString & _modelName )
{
	if( _modelName == "bank" )
	{
		return &m_bankNum;
	}
	else if( _modelName == "patch" )
	{
		return &m_patchNum;
	}
	qCritical() << "requested unknown model " << _modelName;
	return NULL;
}

QString sf2Instrument::nodeName() const
{
	return sf2player_plugin_descriptor.name;
}

void sf2Instrument::freeFont()
{
	m_synthMutex.lock();

	if ( m_font != NULL )
	{
		s_fontsMutex.lock();
		--(m_font->refCount);

		// No more references
		if( m_font->refCount <= 0 )
		{
			qDebug() << "Really deleting " << m_filename;

			fluid_synth_sfunload( m_synth, m_fontId, true );
			s_fonts.remove( m_filename );
			delete m_font;
		}
		// Just remove our reference
		else
		{
			qDebug() << "un-referencing " << m_filename;

			fluid_synth_remove_sfont( m_synth, m_font->fluidFont );
		}
		s_fontsMutex.unlock();

		m_font = NULL;
	}
	m_synthMutex.unlock();
}

void sf2Instrument::openFile( const QString & _sf2File, bool updateTrackName )
{
	emit fileLoading();

	// Used for loading file
	char * sf2Ascii = qstrdup( qPrintable( PathUtil::toAbsolute( _sf2File ) ) );
	QString relativePath = PathUtil::toShortestRelative( _sf2File );

	// free reference to soundfont if one is selected
	freeFont();

	m_synthMutex.lock();
	s_fontsMutex.lock();

	// Increment Reference
	if( s_fonts.contains( relativePath ) )
	{
		qDebug() << "Using existing reference to " << relativePath;

		m_font = s_fonts[ relativePath ];

		m_font->refCount++;

		m_fontId = fluid_synth_add_sfont( m_synth, m_font->fluidFont );
	}

	// Add to map, if doesn't exist.
	else
	{
		bool loaded = false;
		if( fluid_is_soundfont( sf2Ascii ) )
		{
			m_fontId = fluid_synth_sfload( m_synth, sf2Ascii, true );

			if( fluid_synth_sfcount( m_synth ) > 0 )
			{
				// Grab this sf from the top of the stack and add to list
				m_font = new sf2Font( fluid_synth_get_sfont( m_synth, 0 ) );
				s_fonts.insert( relativePath, m_font );
				loaded = true;
			}
		}

		if(!loaded)
		{
			collectErrorForUI( sf2Instrument::tr( "A soundfont %1 could not be loaded." ).
				arg( QFileInfo( _sf2File ).baseName() ) );
		}
	}

	s_fontsMutex.unlock();
	m_synthMutex.unlock();

	if( m_fontId >= 0 )
	{
		// Don't reset patch/bank, so that it isn't cleared when
		// someone resolves a missing file
		//m_patchNum.setValue( 0 );
		//m_bankNum.setValue( 0 );
		m_filename = relativePath;

		emit fileChanged();
	}

	delete[] sf2Ascii;

	if( updateTrackName || instrumentTrack()->displayName() == displayName() )
	{
		instrumentTrack()->setName( PathUtil::cleanName( _sf2File ) );
	}
}

void sf2Instrument::updatePatch()
{
	if( m_bankNum.value() >= 0 && m_patchNum.value() >= 0 )
	{
		fluid_synth_program_select( m_synth, m_channel, m_fontId,
				m_bankNum.value(), m_patchNum.value() );
	}
}

QString sf2Instrument::getCurrentPatchName()
{
	int iBankSelected = m_bankNum.value();
	int iProgSelected = m_patchNum.value();

	// For all soundfonts (in reversed stack order) fill the available programs...
	int cSoundFonts = ::fluid_synth_sfcount( m_synth );
	for( int i = 0; i < cSoundFonts; i++ )
	{
		fluid_sfont_t *pSoundFont = fluid_synth_get_sfont( m_synth, i );
		if ( pSoundFont )
		{
#ifdef CONFIG_FLUID_BANK_OFFSET
			int iBankOffset =
				fluid_synth_get_bank_offset(
						m_synth, fluid_sfont_get_id(pSoundFont) );
#endif
			fluid_sfont_iteration_start( pSoundFont );
#if FLUIDSYNTH_VERSION_MAJOR < 2
			fluid_preset_t preset;
			fluid_preset_t *pCurPreset = &preset;
#else
			fluid_preset_t *pCurPreset;
#endif
			while ((pCurPreset = fluid_sfont_iteration_next_wrapper(pSoundFont, pCurPreset)))
			{
				int iBank = fluid_preset_get_banknum( pCurPreset );
#ifdef CONFIG_FLUID_BANK_OFFSET
				iBank += iBankOffset;
#endif
				int iProg = fluid_preset_get_num( pCurPreset );
				if( iBank == iBankSelected && iProg ==
								iProgSelected )
				{
					return fluid_preset_get_name( pCurPreset );
				}
			}
		}
	}
	return "";
}

void sf2Instrument::updateGain()
{
	fluid_synth_set_gain( m_synth, m_gain.value() );
}

void sf2Instrument::updateReverbOn()
{
	fluid_synth_set_reverb_on( m_synth, m_reverbOn.value() ? 1 : 0 );
}

void sf2Instrument::updateReverb()
{
	fluid_synth_set_reverb( m_synth, m_reverbRoomSize.value(),
			m_reverbDamping.value(), m_reverbWidth.value(),
			m_reverbLevel.value() );
}

void  sf2Instrument::updateChorusOn()
{
	fluid_synth_set_chorus_on( m_synth, m_chorusOn.value() ? 1 : 0 );
}

void  sf2Instrument::updateChorus()
{
	fluid_synth_set_chorus( m_synth, static_cast<int>( m_chorusNum.value() ),
			m_chorusLevel.value(), m_chorusSpeed.value(),
			m_chorusDepth.value(), 0 );
}

void sf2Instrument::updateSampleRate()
{
	double tempRate;

	// Set & get, returns the true sample rate
	fluid_settings_setnum( m_settings, (char *) "synth.sample-rate", Engine::mixer()->processingSampleRate() );
	fluid_settings_getnum( m_settings, (char *) "synth.sample-rate", &tempRate );
	m_internalSampleRate = static_cast<int>( tempRate );

	if( m_font )
	{
		// Now, delete the old one and replace
		m_synthMutex.lock();
		fluid_synth_remove_sfont( m_synth, m_font->fluidFont );
		delete_fluid_synth( m_synth );

		// New synth
		m_synth = new_fluid_synth( m_settings );
		m_fontId = fluid_synth_add_sfont( m_synth, m_font->fluidFont );
		m_synthMutex.unlock();

		// synth program change (set bank and patch)
		updatePatch();
	}
	else
	{
		// Recreate synth with no soundfonts
		m_synthMutex.lock();
		delete_fluid_synth( m_synth );
		m_synth = new_fluid_synth( m_settings );
		m_synthMutex.unlock();
	}

	m_synthMutex.lock();
	if( Engine::mixer()->currentQualitySettings().interpolation >=
			Mixer::qualitySettings::Interpolation_SincFastest )
	{
		fluid_synth_set_interp_method( m_synth, -1, FLUID_INTERP_7THORDER );
	}
	else
	{
		fluid_synth_set_interp_method( m_synth, -1, FLUID_INTERP_DEFAULT );
	}
	m_synthMutex.unlock();
	if( m_internalSampleRate < Engine::mixer()->processingSampleRate() )
	{
		m_synthMutex.lock();
		if( m_srcState != NULL )
		{
			src_delete( m_srcState );
		}
		int error;
		m_srcState = src_new( Engine::mixer()->currentQualitySettings().libsrcInterpolation(), DEFAULT_CHANNELS, &error );
		if( m_srcState == NULL || error )
		{
			qCritical( "error while creating libsamplerate data structure in Sf2Instrument::updateSampleRate()" );
		}
		m_synthMutex.unlock();
	}
	updateReverb();
	updateChorus();
	updateReverbOn();
	updateChorusOn();
	updateGain();

	// Reset last MIDI pitch properties, which will be set to the correct values
	// upon playing the next note
	m_lastMidiPitch = -1;
	m_lastMidiPitchRange = -1;
}

void sf2Instrument::playNote( NotePlayHandle * _n, sampleFrame * )
{
	if( _n->isMasterNote() || ( _n->hasParent() && _n->isReleased() ) )
	{
		return;
	}

	const f_cnt_t tfp = _n->totalFramesPlayed();

	if( tfp == 0 )
	{
		const float LOG440 = 2.643452676f;

		int midiNote = (int)floor( 12.0 * ( log2( _n->unpitchedFrequency() ) - LOG440 ) - 4.0 );

		// out of range?
		if( midiNote <= 0 || midiNote >= 128 )
		{
			return;
		}
		const int baseVelocity = instrumentTrack()->midiPort()->baseVelocity();

		SF2PluginData * pluginData = new SF2PluginData;
		pluginData->midiNote = midiNote;
		pluginData->lastPanning = 0;
		pluginData->lastVelocity = _n->midiVelocity( baseVelocity );
		pluginData->fluidVoice = NULL;
		pluginData->isNew = true;
		pluginData->offset = _n->offset();
		pluginData->noteOffSent = false;

		_n->m_pluginData = pluginData;

		// insert the nph to the playing notes vector
		m_playingNotesMutex.lock();
		m_playingNotes.append( _n );
		m_playingNotesMutex.unlock();
	}
	else if( _n->isReleased() && ! _n->instrumentTrack()->isSustainPedalPressed() ) // note is released during this period
	{
		SF2PluginData * pluginData = static_cast<SF2PluginData *>( _n->m_pluginData );
		pluginData->offset = _n->framesBeforeRelease();
		pluginData->isNew = false;

		m_playingNotesMutex.lock();
		m_playingNotes.append( _n );
		m_playingNotesMutex.unlock();
	}
}

void sf2Instrument::noteOn( SF2PluginData * n )
{
	m_synthMutex.lock();

	// get list of current voice IDs so we can easily spot the new
	// voice after the fluid_synth_noteon() call
	const int poly = fluid_synth_get_polyphony( m_synth );
	fluid_voice_t * voices[poly];
	unsigned int id[poly];
	fluid_synth_get_voicelist( m_synth, voices, poly, -1 );
	for( int i = 0; i < poly; ++i )
	{
		id[i] = 0;
	}
	for( int i = 0; i < poly && voices[i]; ++i )
	{
		id[i] = fluid_voice_get_id( voices[i] );
	}

	fluid_synth_noteon( m_synth, m_channel, n->midiNote, n->lastVelocity );

	// get new voice and save it
	fluid_synth_get_voicelist( m_synth, voices, poly, -1 );
	for( int i = 0; i < poly && voices[i]; ++i )
	{
		const unsigned int newID = fluid_voice_get_id( voices[i] );
		if( id[i] != newID || newID == 0 )
		{
			n->fluidVoice = voices[i];
			break;
		}
	}

	m_synthMutex.unlock();

	m_notesRunningMutex.lock();
	++m_notesRunning[ n->midiNote ];
	m_notesRunningMutex.unlock();
}

void sf2Instrument::noteOff( SF2PluginData * n )
{
	n->noteOffSent = true;
	m_notesRunningMutex.lock();
	const int notes = --m_notesRunning[n->midiNote];
	m_notesRunningMutex.unlock();

	if( notes <= 0 )
	{
		m_synthMutex.lock();
		fluid_synth_noteoff( m_synth, m_channel, n->midiNote );
		m_synthMutex.unlock();
	}
}

void sf2Instrument::play( sampleFrame * _working_buffer )
{
	const fpp_t frames = Engine::mixer()->framesPerPeriod();

	// set midi pitch for this period
	const int currentMidiPitch = instrumentTrack()->midiPitch();
	if( m_lastMidiPitch != currentMidiPitch )
	{
		m_lastMidiPitch = currentMidiPitch;
		m_synthMutex.lock();
		fluid_synth_pitch_bend( m_synth, m_channel, m_lastMidiPitch );
		m_synthMutex.unlock();
	}

	const int currentMidiPitchRange = instrumentTrack()->midiPitchRange();
	if( m_lastMidiPitchRange != currentMidiPitchRange )
	{
		m_lastMidiPitchRange = currentMidiPitchRange;
		m_synthMutex.lock();
		fluid_synth_pitch_wheel_sens( m_synth, m_channel, m_lastMidiPitchRange );
		m_synthMutex.unlock();
	}
	// if we have no new noteons/noteoffs, just render a period and call it a day
	if( m_playingNotes.isEmpty() )
	{
		renderFrames( frames, _working_buffer );
		instrumentTrack()->processAudioBuffer( _working_buffer, frames, NULL );
		return;
	}

	// processing loop
	// go through noteplayhandles in processing order
	f_cnt_t currentFrame = 0;

	while( ! m_playingNotes.isEmpty() )
	{
		// find the note with lowest offset
		NotePlayHandle * currentNote = m_playingNotes[0];
		for( int i = 1; i < m_playingNotes.size(); ++i )
		{
			SF2PluginData * currentData = static_cast<SF2PluginData *>( currentNote->m_pluginData );
			SF2PluginData * iData = static_cast<SF2PluginData *>( m_playingNotes[i]->m_pluginData );
			if( currentData->offset > iData->offset )
			{
				currentNote = m_playingNotes[i];
			}
		}

		// process the current note:
		// first see if we're synced in frame count
		SF2PluginData * currentData = static_cast<SF2PluginData *>( currentNote->m_pluginData );
		if( currentData->offset > currentFrame )
		{
			renderFrames( currentData->offset - currentFrame, _working_buffer + currentFrame );
			currentFrame = currentData->offset;
		}
		if( currentData->isNew )
		{
			noteOn( currentData );
			if( currentNote->isReleased() ) // if the note is released during the same period, we have to process it again for noteoff
			{
				currentData->isNew = false;
				currentData->offset = currentNote->framesBeforeRelease();
			}
			else // otherwise remove the handle
			{
				m_playingNotesMutex.lock();
				m_playingNotes.remove( m_playingNotes.indexOf( currentNote ) );
				m_playingNotesMutex.unlock();
			}
		}
		else
		{
			noteOff( currentData );
			m_playingNotesMutex.lock();
			m_playingNotes.remove( m_playingNotes.indexOf( currentNote ) );
			m_playingNotesMutex.unlock();
		}
	}

	if( currentFrame < frames )
	{
		renderFrames( frames - currentFrame, _working_buffer + currentFrame );
	}
	instrumentTrack()->processAudioBuffer( _working_buffer, frames, NULL );
}

void sf2Instrument::renderFrames( f_cnt_t frames, sampleFrame * buf )
{
	m_synthMutex.lock();
	if( m_internalSampleRate < Engine::mixer()->processingSampleRate() &&
							m_srcState != NULL )
	{
		const fpp_t f = frames * m_internalSampleRate / Engine::mixer()->processingSampleRate();
#ifdef __GNUC__
		sampleFrame tmp[f];
#else
		sampleFrame * tmp = new sampleFrame[f];
#endif
		fluid_synth_write_float( m_synth, f, tmp, 0, 2, tmp, 1, 2 );

		SRC_DATA src_data;
		src_data.data_in = (float *)tmp;
		src_data.data_out = (float *)buf;
		src_data.input_frames = f;
		src_data.output_frames = frames;
		src_data.src_ratio = (double) frames / f;
		src_data.end_of_input = 0;
		int error = src_process( m_srcState, &src_data );
#ifndef __GNUC__
		delete[] tmp;
#endif
		if( error )
		{
			qCritical( "sf2Instrument: error while resampling: %s", src_strerror( error ) );
		}
		if( src_data.output_frames_gen > frames )
		{
			qCritical( "sf2Instrument: not enough frames: %ld / %d", src_data.output_frames_gen, frames );
		}
	}
	else
	{
		fluid_synth_write_float( m_synth, frames, buf, 0, 2, buf, 1, 2 );
	}
	m_synthMutex.unlock();
}

void sf2Instrument::deleteNotePluginData( NotePlayHandle * _n )
{
	SF2PluginData * pluginData = static_cast<SF2PluginData *>( _n->m_pluginData );
	if( ! pluginData->noteOffSent ) // if we for some reason haven't noteoffed the note before it gets deleted,
									// do it here
	{
		noteOff( pluginData );
		m_playingNotesMutex.lock();
		if( m_playingNotes.indexOf( _n ) >= 0 )
		{
			m_playingNotes.remove( m_playingNotes.indexOf( _n ) );
		}
		m_playingNotesMutex.unlock();
	}
	delete pluginData;
}

PluginView * sf2Instrument::instantiateView( QWidget * _parent )
{
	return new sf2InstrumentView( this, _parent );
}

class sf2Knob : public Knob
{
public:
	sf2Knob( QWidget * _parent ) :
			Knob( knobStyled, _parent )
	{
		setFixedSize( 31, 38 );
	}
};

sf2InstrumentView::sf2InstrumentView( Instrument * _instrument, QWidget * _parent ) :
	InstrumentViewFixedSize( _instrument, _parent )
{
//	QVBoxLayout * vl = new QVBoxLayout( this );
//	QHBoxLayout * hl = new QHBoxLayout();

	sf2Instrument* k = castModel<sf2Instrument>();

	connect( &k->m_bankNum, SIGNAL( dataChanged() ), this, SLOT( updatePatchName() ) );
	connect( &k->m_patchNum, SIGNAL( dataChanged() ), this, SLOT( updatePatchName() ) );

	// File Button
	m_fileDialogButton = new PixmapButton( this );
	m_fileDialogButton->setCursor( QCursor( Qt::PointingHandCursor ) );
	m_fileDialogButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "fileselect_on" ) );
	m_fileDialogButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "fileselect_off" ) );
	m_fileDialogButton->move( 217, 107 );

	connect( m_fileDialogButton, SIGNAL( clicked() ), this, SLOT( showFileDialog() ) );

	ToolTip::add( m_fileDialogButton, tr( "Open SoundFont file" ) );

	// Patch Button
	m_patchDialogButton = new PixmapButton( this );
	m_patchDialogButton->setCursor( QCursor( Qt::PointingHandCursor ) );
	m_patchDialogButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "patches_on" ) );
	m_patchDialogButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "patches_off" ) );
	m_patchDialogButton->setEnabled( false );
	m_patchDialogButton->move( 217, 125 );

	connect( m_patchDialogButton, SIGNAL( clicked() ), this, SLOT( showPatchDialog() ) );

	ToolTip::add( m_patchDialogButton, tr( "Choose patch" ) );

	// LCDs
	m_bankNumLcd = new LcdSpinBox( 3, "21pink", this );
	m_bankNumLcd->move(131, 62);
//	m_bankNumLcd->addTextForValue( -1, "---" );
//	m_bankNumLcd->setEnabled( false );

	m_patchNumLcd = new LcdSpinBox( 3, "21pink", this );
	m_patchNumLcd->move(190, 62);
//	m_patchNumLcd->addTextForValue( -1, "---" );
//	m_patchNumLcd->setEnabled( false );

	/*hl->addWidget( m_fileDialogButton );
	hl->addWidget( m_bankNumLcd );
	hl->addWidget( m_patchNumLcd );
	hl->addWidget( m_patchDialogButton );

	vl->addLayout( hl );*/

	// Next row

	//hl = new QHBoxLayout();

	m_filenameLabel = new QLabel( this );
	m_filenameLabel->setGeometry( 58, 109, 156, 11 );
	m_patchLabel = new QLabel( this );
	m_patchLabel->setGeometry( 58, 127, 156, 11 );

	//hl->addWidget( m_filenameLabel );
//	vl->addLayout( hl );

	// Gain
	m_gainKnob = new sf2Knob( this );
	m_gainKnob->setHintText( tr("Gain:"), "" );
	m_gainKnob->move( 86, 55 );
//	vl->addWidget( m_gainKnob );

	// Reverb
//	hl = new QHBoxLayout();

	m_reverbButton = new PixmapButton( this );
	m_reverbButton->setCheckable( true );
	m_reverbButton->move( 14, 180 );
	m_reverbButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "reverb_on" ) );
	m_reverbButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "reverb_off" ) );
	ToolTip::add( m_reverbButton, tr( "Apply reverb (if supported)" ) );

	m_reverbRoomSizeKnob = new sf2Knob( this );
	m_reverbRoomSizeKnob->setHintText( tr("Room size:"), "" );
	m_reverbRoomSizeKnob->move( 93, 160 );

	m_reverbDampingKnob = new sf2Knob( this );
	m_reverbDampingKnob->setHintText( tr("Damping:"), "" );
	m_reverbDampingKnob->move( 130, 160 );

	m_reverbWidthKnob = new sf2Knob( this );
	m_reverbWidthKnob->setHintText( tr("Width:"), "" );
	m_reverbWidthKnob->move( 167, 160 );

	m_reverbLevelKnob = new sf2Knob( this );
	m_reverbLevelKnob->setHintText( tr("Level:"), "" );
	m_reverbLevelKnob->move( 204, 160 );

/*	hl->addWidget( m_reverbOnLed );
	hl->addWidget( m_reverbRoomSizeKnob );
	hl->addWidget( m_reverbDampingKnob );
	hl->addWidget( m_reverbWidthKnob );
	hl->addWidget( m_reverbLevelKnob );

	vl->addLayout( hl );
*/

	// Chorus
//	hl = new QHBoxLayout();

	m_chorusButton = new PixmapButton( this );
	m_chorusButton->setCheckable( true );
	m_chorusButton->move( 14, 226 );
	m_chorusButton->setActiveGraphic( PLUGIN_NAME::getIconPixmap( "chorus_on" ) );
	m_chorusButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "chorus_off" ) );
	ToolTip::add( m_chorusButton, tr( "Apply chorus (if supported)" ) );

	m_chorusNumKnob = new sf2Knob( this );
	m_chorusNumKnob->setHintText( tr("Voices:"), "" );
	m_chorusNumKnob->move( 93, 206 );

	m_chorusLevelKnob = new sf2Knob( this );
	m_chorusLevelKnob->setHintText( tr("Level:"), "" );
	m_chorusLevelKnob->move( 130 , 206 );

	m_chorusSpeedKnob = new sf2Knob( this );
	m_chorusSpeedKnob->setHintText( tr("Speed:"), "" );
	m_chorusSpeedKnob->move( 167 , 206 );

	m_chorusDepthKnob = new sf2Knob( this );
	m_chorusDepthKnob->setHintText( tr("Depth:"), "" );
	m_chorusDepthKnob->move( 204 , 206 );
/*
	hl->addWidget( m_chorusOnLed );
	hl->addWidget( m_chorusNumKnob);
	hl->addWidget( m_chorusLevelKnob);
	hl->addWidget( m_chorusSpeedKnob);
	hl->addWidget( m_chorusDepthKnob);

	vl->addLayout( hl );
*/
	setAutoFillBackground( true );
	QPalette pal;
	pal.setBrush( backgroundRole(), PLUGIN_NAME::getIconPixmap( "artwork" ) );
	setPalette( pal );

	updateFilename();
}

sf2InstrumentView::~sf2InstrumentView()
{
}

void sf2InstrumentView::modelChanged()
{
	sf2Instrument * k = castModel<sf2Instrument>();
	m_bankNumLcd->setModel( &k->m_bankNum );
	m_patchNumLcd->setModel( &k->m_patchNum );

	m_gainKnob->setModel( &k->m_gain );

	m_reverbButton->setModel( &k->m_reverbOn );
	m_reverbRoomSizeKnob->setModel( &k->m_reverbRoomSize );
	m_reverbDampingKnob->setModel( &k->m_reverbDamping );
	m_reverbWidthKnob->setModel( &k->m_reverbWidth );
	m_reverbLevelKnob->setModel( &k->m_reverbLevel );

	m_chorusButton->setModel( &k->m_chorusOn );
	m_chorusNumKnob->setModel( &k->m_chorusNum );
	m_chorusLevelKnob->setModel( &k->m_chorusLevel );
	m_chorusSpeedKnob->setModel( &k->m_chorusSpeed );
	m_chorusDepthKnob->setModel( &k->m_chorusDepth );

	connect( k, SIGNAL( fileChanged() ), this, SLOT( updateFilename() ) );

	connect( k, SIGNAL( fileLoading() ), this, SLOT( invalidateFile() ) );

	updateFilename();
}

void sf2InstrumentView::updateFilename()
{
	sf2Instrument * i = castModel<sf2Instrument>();
	QFontMetrics fm( m_filenameLabel->font() );
	QString file = i->m_filename.endsWith( ".sf2", Qt::CaseInsensitive ) ?
			i->m_filename.left( i->m_filename.length() - 4 ) :
			i->m_filename;
	m_filenameLabel->setText( fm.elidedText( file, Qt::ElideLeft, m_filenameLabel->width() ) );
			//		i->m_filename + "\nPatch: TODO" );

	m_patchDialogButton->setEnabled( !i->m_filename.isEmpty() );

	updatePatchName();

	update();
}

void sf2InstrumentView::updatePatchName()
{
	sf2Instrument * i = castModel<sf2Instrument>();
	QFontMetrics fm( font() );
	QString patch = i->getCurrentPatchName();
	m_patchLabel->setText( fm.elidedText( patch, Qt::ElideLeft, m_patchLabel->width() ) );

	update();
}

void sf2InstrumentView::invalidateFile()
{
	m_patchDialogButton->setEnabled( false );
}

void sf2InstrumentView::showFileDialog()
{
	sf2Instrument * k = castModel<sf2Instrument>();

	FileDialog ofd( NULL, tr( "Open SoundFont file" ) );
	ofd.setFileMode( FileDialog::ExistingFiles );

	QStringList types;
	types << tr( "SoundFont Files (*.sf2 *.sf3)" );
	ofd.setNameFilters( types );

	if( k->m_filename != "" )
	{
		QString f = PathUtil::toAbsolute( k->m_filename );
		ofd.setDirectory( QFileInfo( f ).absolutePath() );
		ofd.selectFile( QFileInfo( f ).fileName() );
	}
	else
	{
		ofd.setDirectory( ConfigManager::inst()->sf2Dir() );
	}

	m_fileDialogButton->setEnabled( false );

	if( ofd.exec() == QDialog::Accepted && !ofd.selectedFiles().isEmpty() )
	{
		QString f = ofd.selectedFiles()[0];
		if( f != "" )
		{
			k->openFile( f );
			Engine::getSong()->setModified();
		}
	}

	m_fileDialogButton->setEnabled( true );
}

void sf2InstrumentView::showPatchDialog()
{
	sf2Instrument * k = castModel<sf2Instrument>();

	patchesDialog pd( this );

	pd.setup( k->m_synth, 1, k->instrumentTrack()->name(), &k->m_bankNum, &k->m_patchNum, m_patchLabel );

	pd.exec();
}

extern "C"
{

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

}


code:
/*
 * sf2_player.h - a soundfont2 player using fluidSynth
 *
 * Copyright (c) 2008 Paul Giblock <drfaygo/at/gmail/dot/com>
 * Copyright (c) 2009-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 SF2_PLAYER_H
#define SF2_PLAYER_H

#include <QMutex>
#include <samplerate.h>

#include "Instrument.h"
#include "PixmapButton.h"
#include "InstrumentView.h"
#include "Knob.h"
#include "LcdSpinBox.h"
#include "LedCheckbox.h"
#include "fluidsynthshims.h"
#include "MemoryManager.h"

class sf2InstrumentView;
class sf2Font;
class NotePlayHandle;

class patchesDialog;
class QLabel;

struct SF2PluginData;

class sf2Instrument : public Instrument
{
	Q_OBJECT
	mapPropertyFromModel(int,getBank,setBank,m_bankNum);
	mapPropertyFromModel(int,getPatch,setPatch,m_patchNum);

public:
	sf2Instrument( InstrumentTrack * _instrument_track );
	virtual ~sf2Instrument();

	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 void loadFile( const QString & _file );

	virtual AutomatableModel * childModel( const QString & _modelName );

	virtual QString nodeName() const;

	virtual f_cnt_t desiredReleaseFrames() const
	{
		return 0;
	}

	virtual Flags flags() const
	{
		return IsSingleStreamed;
	}

	virtual PluginView * instantiateView( QWidget * _parent );

	QString getCurrentPatchName();

	void setParameter( const QString & _param, const QString & _value );

public slots:
	void openFile( const QString & _sf2File, bool updateTrackName = true );
	void updatePatch();
	void updateSampleRate();

	// We can't really support sample-exact with the way IPH and FS work.
	// So, sig/slots work just fine for the synth settings right now.
	void updateReverbOn();
	void updateReverb();
	void updateChorusOn();
	void updateChorus();
	void updateGain();

private:
	static QMutex s_fontsMutex;
	static QMap<QString, sf2Font*> s_fonts;
	static int (* s_origFree)( fluid_sfont_t * );

	SRC_STATE * m_srcState;

	fluid_settings_t* m_settings;
	fluid_synth_t* m_synth;

	sf2Font* m_font;

	int m_fontId;
	QString m_filename;

	// Protect the array of active notes
	QMutex m_notesRunningMutex;

	// Protect synth when we are re-creating it.
	QMutex m_synthMutex;
	QMutex m_loadMutex;

	int m_notesRunning[128];
	sample_rate_t m_internalSampleRate;
	int m_lastMidiPitch;
	int m_lastMidiPitchRange;
	int m_channel;

	LcdSpinBoxModel m_bankNum;
	LcdSpinBoxModel m_patchNum;

	FloatModel m_gain;

	BoolModel m_reverbOn;
	FloatModel m_reverbRoomSize;
	FloatModel m_reverbDamping;
	FloatModel m_reverbWidth;
	FloatModel m_reverbLevel;

	BoolModel m_chorusOn;
	FloatModel m_chorusNum;
	FloatModel m_chorusLevel;
	FloatModel m_chorusSpeed;
	FloatModel m_chorusDepth;

	QVector<NotePlayHandle *> m_playingNotes;
	QMutex m_playingNotesMutex;

private:
	void freeFont();
	void noteOn( SF2PluginData * n );
	void noteOff( SF2PluginData * n );
	void renderFrames( f_cnt_t frames, sampleFrame * buf );

	friend class sf2InstrumentView;

signals:
	void fileLoading();
	void fileChanged();
	void patchChanged();

} ;

// A soundfont in our font-map
class sf2Font
{
	MM_OPERATORS
public:
	sf2Font( fluid_sfont_t * f ) :
		fluidFont( f ),
		refCount( 1 )
	{};

	fluid_sfont_t * fluidFont;
	int refCount;
};

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

private:
	virtual void modelChanged();

	PixmapButton * m_fileDialogButton;
	PixmapButton * m_patchDialogButton;

	LcdSpinBox * m_bankNumLcd;
	LcdSpinBox * m_patchNumLcd;

	QLabel * m_filenameLabel;
	QLabel * m_patchLabel;

	Knob	* m_gainKnob;

	PixmapButton * m_reverbButton;
	Knob	* m_reverbRoomSizeKnob;
	Knob	* m_reverbDampingKnob;
	Knob	* m_reverbWidthKnob;
	Knob	* m_reverbLevelKnob;

	PixmapButton * m_chorusButton;
	Knob * m_chorusNumKnob;
	Knob * m_chorusLevelKnob;
	Knob * m_chorusSpeedKnob;
	Knob * m_chorusDepthKnob;

	static patchesDialog * s_patchDialog;

protected slots:
	void invalidateFile();
	void showFileDialog();
	void showPatchDialog();
	void updateFilename();
	void updatePatchName();
} ;

#endif


code:
INCLUDE(BuildPlugin)

BUILD_PLUGIN(sfxr sfxr.cpp sfxr.h MOCFILES sfxr.h EMBEDDED_RESOURCES *.png)


code:
This is a port of sfxr to LMMS, ported by Wong Cho Ching.

NOTE: Do NOT remove the MIT license below to prevent legal problem.
Original Readme File:

(http://www.drpetter.se/project_sfxr.html)

-----------------------------
sfxr - sound effect generator
-----------------------------
   by DrPetter, 2007-12-14
    developed for LD48#10
-----------------------------

Basic usage:

Start the application, then hit
some of the buttons on the left
side to generate random sounds
matching the button descriptions.

Press "Export .WAV" to save the
current sound as a WAV audio file.
Click the buttons below to change
WAV format in terms of bits per
sample and sample rate.

If you find a sound that is sort
of interesting but not quite what
you want, you can drag some sliders
around until it sounds better.

The Randomize button generates
something completely random.

Mutate slightly alters the current
parameters to automatically create
a variation of the sound.

Advanced usage:

Figure out what each slider does and
use them to adjust particular aspects
of the current sound...

Press the right mouse button on a slider
to reset it to a value of zero.

Press Space or Enter to play the current sound.

The Save/Load sound buttons allow saving
and loading of program parameters to work
on a sound over several sessions.

Volume setting is saved with the sound and
exported to WAV. If you increase it too much
there's a risk of clipping.

Some parameters influence the sound during
playback (particularly when using a non-zero
repeat speed), and dragging these sliders
can cause some interesting effects.
To record this you will need to use an external
recording application, for instance Audacity.
Set the recording source in that application
to "Wave", "Stereo Mix", "Mixed Output" or similar.

Using an external sound editor to capture and edit
sound can also be used to string several sounds
together for more complex results.

Parameter description:
- The top four buttons select base waveform
- First four parameters control the volume envelope
  Attack is the beginning of the sound,
  longer attack means a smoother start.
  Sustain is how long the volume is held constant
  before fading out.
  Increase Sustain Punch to cause a popping
  effect with increased (and falling) volume
  during the sustain phase.
  Decay is the fade-out time.
- Next six are for controlling the sound pitch or
  frequency.
  Start frequency is pretty obvious. Has a large
  impact on the overall sound.
  Min frequency represents a cutoff that stops all
  sound if it's passed during a downward slide.
  Slide sets the speed at which the frequency should
  be swept (up or down).
  Delta slide is the "slide of slide", or rate of change
  in the slide speed.
  Vibrato depth/speed makes for an oscillating
  frequency effect at various strengths and rates.
- Then we have two parameters for causing an abrupt
  change in pitch after a ceratin delay.
  Amount is pitch change (up or down)
  and Speed indicates time to wait before changing
  the pitch.
- Following those are two parameters specific to the
  squarewave waveform.
  The duty cycle of a square describes its shape
  in terms of how large the positive vs negative
  sections are. It can be swept up or down by
  changing the second parameter.
- Repeat speed, when not zero, causes the frequency
  and duty parameters to be reset at regular intervals
  while the envelope and filter continue unhindered.
  This can make for some interesting pulsating effects.
- Phaser offset overlays a delayed copy of the audio
  stream on top of itself, resulting in a kind of tight
  reverb or sci-fi effect.
  This parameter can also be swept like many others.
- Finally, the bottom five sliders control two filters
  which are applied after all other effects.
  The first one is a resonant lowpass filter which has
  a sweepable cutoff frequency.
  The other is a highpass filter which can be used to
  remove undesired low frequency hum in "light" sounds.

----------------------

License
-------

Basically, I don't care what you do with it, anything goes.

To please all the troublesome folks who request a formal license,
I attach the "MIT license" as follows:

--

Copyright (c) 2007 Tomas Pettersson

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.

----------------------

http://www.drpetter.se

  drpetter@gmail.com

----------------------


code:
/*
 * sfxr.cpp - port of sfxr to LMMS
 * 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.
 */

#include <cstdlib>
#include <time.h>

#define rnd(n) (rand()%(n+1))

#define PI 3.14159265f

float frnd(float range)
{
	return (float)rnd(10000)/10000*range;
}

#include <cmath>

#include <QDomElement>

#include "sfxr.h"
#include "Engine.h"
#include "InstrumentTrack.h"
#include "Knob.h"
#include "NotePlayHandle.h"
#include "PixmapButton.h"
#include "ToolTip.h"
#include "Song.h"
#include "MidiEvent.h"
#include "Mixer.h"

#include "embed.h"

#include "plugin_export.h"

extern "C"
{

Plugin::Descriptor PLUGIN_EXPORT sfxr_plugin_descriptor =
{
	STRINGIFY( PLUGIN_NAME ),
	"sfxr",
	QT_TRANSLATE_NOOP( "PluginBrowser",
				"LMMS port of sfxr" ),
	"Wong Cho Ching",
	0x0100,
	Plugin::Instrument,
	new PluginPixmapLoader( "logo" ),
	NULL,
	NULL
} ;

}

SfxrSynth::SfxrSynth( const sfxrInstrument * s ):
	s(s),
	playing_sample( true )
{
    resetSample( false );
}

SfxrSynth::~SfxrSynth()
{

}

void SfxrSynth::resetSample( bool restart )
{
	if(!restart)
	{
		phase=0;
	}
	fperiod=100.0/(s->m_startFreqModel.value()*s->m_startFreqModel.value()+0.001);
	period=(int)fperiod;
	fmaxperiod=100.0/(s->m_minFreqModel.value()*s->m_minFreqModel.value()+0.001);
	fslide=1.0-pow((double)s->m_slideModel.value(), 3.0)*0.01;
	fdslide=-pow((double)s->m_dSlideModel.value(), 3.0)*0.000001;
	square_duty=0.5f-s->m_sqrDutyModel.value()*0.5f;
	square_slide=-s->m_sqrSweepModel.value()*0.00005f;
	if(s->m_changeAmtModel.value()>=0.0f)
		arp_mod=1.0-pow((double)s->m_changeAmtModel.value(), 2.0)*0.9;
	else
		arp_mod=1.0+pow((double)s->m_changeAmtModel.value(), 2.0)*10.0;
	arp_time=0;
	arp_limit=(int)(pow(1.0f-s->m_changeSpeedModel.value(), 2.0f)*20000+32);
	if(s->m_changeSpeedModel.value()==1.0f)
		arp_limit=0;
	if(!restart)
	{
		// reset filter
		fltp=0.0f;
		fltdp=0.0f;
		fltw=pow(s->m_lpFilCutModel.value(), 3.0f)*0.1f;
		fltw_d=1.0f+s->m_lpFilCutSweepModel.value()*0.0001f;
		fltdmp=5.0f/(1.0f+pow(s->m_lpFilResoModel.value(), 2.0f)*20.0f)*(0.01f+fltw);
		if(fltdmp>0.8f) fltdmp=0.8f;
		fltphp=0.0f;
		flthp=pow(s->m_hpFilCutModel.value(), 2.0f)*0.1f;
		flthp_d=1.0+s->m_hpFilCutSweepModel.value()*0.0003f;
		// reset vibrato
		vib_phase=0.0f;
		vib_speed=pow(s->m_vibSpeedModel.value(), 2.0f)*0.01f;
		vib_amp=s->m_vibDepthModel.value()*0.5f;
		// reset envelope
		env_vol=0.0f;
		env_stage=0;
		env_time=0;

		env_length[0]=(int)(s->m_attModel.value()*s->m_attModel.value()*99999.0f)+1;
		env_length[1]=(int)(s->m_holdModel.value()*s->m_holdModel.value()*99999.0f)+1;
		env_length[2]=(int)(s->m_decModel.value()*s->m_decModel.value()*99999.0f)+1;

		fphase=pow(s->m_phaserOffsetModel.value(), 2.0f)*1020.0f;
		if(s->m_phaserOffsetModel.value()<0.0f) fphase=-fphase;
		fdphase=pow(s->m_phaserSweepModel.value(), 2.0f)*1.0f;
		if(s->m_phaserSweepModel.value()<0.0f) fdphase=-fdphase;
		iphase=abs((int)fphase);
		ipp=0;
		for(int i=0;i<1024;i++)
			phaser_buffer[i]=0.0f;

		for(int i=0;i<32;i++)
			noise_buffer[i]=frnd(2.0f)-1.0f;

		rep_time=0;
		rep_limit=(int)(pow(1.0f-s->m_repeatSpeedModel.value(), 2.0f)*20000+32);
		if(s->m_repeatSpeedModel.value()==0.0f)
			rep_limit=0;
    }
}

void SfxrSynth::update( sampleFrame * buffer, const int32_t frameNum )
{
	for(int i=0;i<frameNum;i++)
	{
        if(!playing_sample)
        {
            for( ch_cnt_t j=0; j < DEFAULT_CHANNELS; j++ )
            {
                buffer[i][j]=0.0f;
            }
        }

		rep_time++;
		if(rep_limit!=0 && rep_time>=rep_limit)
		{
			rep_limit=0;
			resetSample(true);
		}

		// frequency envelopes/arpeggios
		arp_time++;
		if(arp_limit!=0 && arp_time>=arp_limit)
		{
			arp_limit=0;
			fperiod*=arp_mod;
		}
		fslide+=fdslide;
		fperiod*=fslide;
		if(fperiod>fmaxperiod)
		{
			fperiod=fmaxperiod;
			if(s->m_minFreqModel.value()>0.0f)
				playing_sample=false;
		}
		float rfperiod=fperiod;
		if(vib_amp>0.0f)
		{
			vib_phase+=vib_speed;
			rfperiod=fperiod*(1.0+sin(vib_phase)*vib_amp);
		}
		period=(int)rfperiod;
		if(period<8) period=8;
		square_duty+=square_slide;
		if(square_duty<0.0f) square_duty=0.0f;
		if(square_duty>0.5f) square_duty=0.5f;
		// volume envelope
		env_time++;
		if(env_time>env_length[env_stage])
		{
			env_time=0;
			env_stage++;
			if(env_stage==3)
				playing_sample=false;
		}
		if(env_stage==0)
			env_vol=(float)env_time/env_length[0];
		if(env_stage==1)
			env_vol=1.0f+pow(1.0f-(float)env_time/env_length[1], 1.0f)*2.0f*s->m_susModel.value();
		if(env_stage==2)
			env_vol=1.0f-(float)env_time/env_length[2];

		// phaser step
		fphase+=fdphase;
		iphase=abs((int)fphase);
		if(iphase>1023) iphase=1023;

		if(flthp_d!=0.0f)
		{
			flthp*=flthp_d;
			if(flthp<0.00001f) flthp=0.00001f;
			if(flthp>0.1f) flthp=0.1f;
		}

		float ssample=0.0f;
		for(int si=0;si<8;si++) // 8x supersampling
		{
			float sample=0.0f;
			phase++;
			if(phase>=period)
			{
//				phase=0;
				phase%=period;
				if(s->m_waveFormModel.value()==3)
					for(int i=0;i<32;i++)
						noise_buffer[i]=frnd(2.0f)-1.0f;
			}
			// base waveform
			float fp=(float)phase/period;
			switch(s->m_waveFormModel.value())
			{
			case 0: // square
				if(fp<square_duty)
					sample=0.5f;
				else
					sample=-0.5f;
				break;
			case 1: // sawtooth
				sample=1.0f-fp*2;
				break;
			case 2: // sine
				sample=(float)sin(fp*2*PI);
				break;
			case 3: // noise
				sample=noise_buffer[phase*32/period];
				break;
			}
			// lp filter
			float pp=fltp;
			fltw*=fltw_d;
			if(fltw<0.0f) fltw=0.0f;
			if(fltw>0.1f) fltw=0.1f;
			if(s->m_lpFilCutModel.value()!=1.0f)
			{
				fltdp+=(sample-fltp)*fltw;
				fltdp-=fltdp*fltdmp;
			}
			else
			{
				fltp=sample;
				fltdp=0.0f;
			}
			fltp+=fltdp;
			// hp filter
			fltphp+=fltp-pp;
			fltphp-=fltphp*flthp;
			sample=fltphp;
			// phaser
			phaser_buffer[ipp&1023]=sample;
			sample+=phaser_buffer[(ipp-iphase+1024)&1023];
			ipp=(ipp+1)&1023;
			// final accumulation and envelope application
			ssample+=sample*env_vol;
		}
		//ssample=ssample/8*master_vol;

		//ssample*=2.0f*sound_vol;
		ssample*=0.025f;

		if(buffer!=NULL)
		{
			if(ssample>1.0f) ssample=1.0f;
			if(ssample<-1.0f) ssample=-1.0f;
			for( ch_cnt_t j=0; j<DEFAULT_CHANNELS; j++ )
			{
				buffer[i][j]=ssample;
			}
		}
	}
}

bool SfxrSynth::isPlaying() const
{
	return playing_sample;
}

sfxrInstrument::sfxrInstrument( InstrumentTrack * _instrument_track ) :
	Instrument( _instrument_track, &sfxr_plugin_descriptor ),
	m_attModel(0.0f, this, "Attack Time"),
	m_holdModel(0.3f, this, "Sustain Time"),
	m_susModel(0.0f, this, "Sustain Punch"),
	m_decModel(0.4f, this, "Decay Time"),

	m_startFreqModel(0.3f, this, "Start Frequency"),
	m_minFreqModel(0.0f, this, "Min Frequency"),
	m_slideModel(0.0f, this, "Slide"),
	m_dSlideModel(0.0f, this, "Delta Slide"),
	m_vibDepthModel(0.0f, this, "Vibrato Depth"),
	m_vibSpeedModel(0.0f, this, "Vibrato Speed"),

	m_changeAmtModel(0.0f, this, "Change Amount"),
	m_changeSpeedModel(0.0f, this, "Change Speed"),

	m_sqrDutyModel(0.0f, this, "Square Duty"),
	m_sqrSweepModel(0.0f, this, "Duty Sweep"),

	m_repeatSpeedModel(0.0f, this, "Repeat Speed"),

	m_phaserOffsetModel(0.0f, this, "Phaser Offset"),
	m_phaserSweepModel(0.0f, this, "Phaser Sweep"),

	m_lpFilCutModel(1.0f, this, "LP Filter Cutoff"),
	m_lpFilCutSweepModel(0.0f, this, "LP Filter Cutoff Sweep"),
	m_lpFilResoModel(0.0f, this, "LP Filter Resonance"),
	m_hpFilCutModel(0.0f, this, "HP Filter Cutoff"),
	m_hpFilCutSweepModel(0.0f, this, "HP Filter Cutoff Sweep"),
	m_waveFormModel( SQR_WAVE, 0, WAVES_NUM-1, this, tr( "Wave" ) )
{
}

sfxrInstrument::~sfxrInstrument()
{
}

void sfxrInstrument::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	_this.setAttribute( "version", "1" );
	m_attModel.saveSettings( _doc, _this, "att" );
	m_holdModel.saveSettings( _doc, _this, "hold" );
	m_susModel.saveSettings( _doc, _this, "sus" );
	m_decModel.saveSettings( _doc, _this, "dec" );

	m_startFreqModel.saveSettings( _doc, _this, "startFreq" );
	m_minFreqModel.saveSettings( _doc, _this, "minFreq" );
	m_slideModel.saveSettings( _doc, _this, "slide" );
	m_dSlideModel.saveSettings( _doc, _this, "dSlide" );
	m_vibDepthModel.saveSettings( _doc, _this, "vibDepth" );
	m_vibSpeedModel.saveSettings( _doc, _this, "vibSpeed" );

	m_changeAmtModel.saveSettings( _doc, _this, "changeAmt" );
	m_changeSpeedModel.saveSettings( _doc, _this, "changeSpeed" );

	m_sqrDutyModel.saveSettings( _doc, _this, "sqrDuty" );
	m_sqrSweepModel.saveSettings( _doc, _this, "sqrSweep" );

	m_repeatSpeedModel.saveSettings( _doc, _this, "repeatSpeed" );

	m_phaserOffsetModel.saveSettings( _doc, _this, "phaserOffset" );
	m_phaserSweepModel.saveSettings( _doc, _this, "phaserSweep" );

	m_lpFilCutModel.saveSettings( _doc, _this, "lpFilCut" );
	m_lpFilCutSweepModel.saveSettings( _doc, _this, "lpFilCutSweep" );
	m_lpFilResoModel.saveSettings( _doc, _this, "lpFilReso" );
	m_hpFilCutModel.saveSettings( _doc, _this, "hpFilCut" );
	m_hpFilCutSweepModel.saveSettings( _doc, _this, "hpFilCutSweep" );

	m_waveFormModel.saveSettings( _doc, _this, "waveForm" );
}

void sfxrInstrument::loadSettings( const QDomElement & _this )
{

	m_attModel.loadSettings(_this, "att" );
	m_holdModel.loadSettings( _this, "hold" );
	m_susModel.loadSettings( _this, "sus" );
	m_decModel.loadSettings( _this, "dec" );

	m_startFreqModel.loadSettings( _this, "startFreq" );
	m_minFreqModel.loadSettings( _this, "minFreq" );
	m_slideModel.loadSettings( _this, "slide" );
	m_dSlideModel.loadSettings( _this, "dSlide" );
	m_vibDepthModel.loadSettings( _this, "vibDepth" );
	m_vibSpeedModel.loadSettings( _this, "vibSpeed" );

	m_changeAmtModel.loadSettings( _this, "changeAmt" );
	m_changeSpeedModel.loadSettings( _this, "changeSpeed" );

	m_sqrDutyModel.loadSettings( _this, "sqrDuty" );
	m_sqrSweepModel.loadSettings( _this, "sqrSweep" );

	m_repeatSpeedModel.loadSettings( _this, "repeatSpeed" );

	m_phaserOffsetModel.loadSettings( _this, "phaserOffset" );
	m_phaserSweepModel.loadSettings( _this, "phaserSweep" );

	m_lpFilCutModel.loadSettings( _this, "lpFilCut" );
	m_lpFilCutSweepModel.loadSettings( _this, "lpFilCutSweep" );
	m_lpFilResoModel.loadSettings( _this, "lpFilReso" );
	m_hpFilCutModel.loadSettings( _this, "hpFilCut" );
	m_hpFilCutSweepModel.loadSettings( _this, "hpFilCutSweep" );

	m_waveFormModel.loadSettings( _this, "waveForm" );

}

QString sfxrInstrument::nodeName() const
{
	return( sfxr_plugin_descriptor.name );
}

void sfxrInstrument::playNote( NotePlayHandle * _n, sampleFrame * _working_buffer )
{
	float currentSampleRate = Engine::mixer()->processingSampleRate();

    fpp_t frameNum = _n->framesLeftForCurrentPeriod();
    const f_cnt_t offset = _n->noteOffset();
	if ( _n->totalFramesPlayed() == 0 || _n->m_pluginData == NULL )
	{
		_n->m_pluginData = new SfxrSynth( this );
	}
	else if( static_cast<SfxrSynth*>(_n->m_pluginData)->isPlaying() == false )
	{
		memset(_working_buffer + offset, 0, sizeof(sampleFrame) * frameNum);
		_n->noteOff();
		return;
	}

	int32_t pitchedFrameNum = (_n->frequency()/BaseFreq)*frameNum;

	pitchedFrameNum /= ( currentSampleRate / 44100 );

// debug code
//	qDebug( "pFN %d", pitchedFrameNum );

	sampleFrame * pitchedBuffer = new sampleFrame[pitchedFrameNum];
	static_cast<SfxrSynth*>(_n->m_pluginData)->update( pitchedBuffer, pitchedFrameNum );
	for( fpp_t i=0; i<frameNum; i++ )
	{
		for( ch_cnt_t j=0; j<DEFAULT_CHANNELS; j++ )
		{
			_working_buffer[i+offset][j] = pitchedBuffer[i*pitchedFrameNum/frameNum][j];
		}
	}

	delete[] pitchedBuffer;

	applyRelease( _working_buffer, _n );

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

}

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

PluginView * sfxrInstrument::instantiateView( QWidget * _parent )
{
	return( new sfxrInstrumentView( this, _parent ) );
}

void sfxrInstrument::resetModels()
{

	m_attModel.reset();
	m_holdModel.reset();
	m_susModel.reset();
	m_decModel.reset();

	m_startFreqModel.reset();
	m_minFreqModel.reset();
	m_slideModel.reset();
	m_dSlideModel.reset();
	m_vibDepthModel.reset();
	m_vibSpeedModel.reset();

	m_changeAmtModel.reset();
	m_changeSpeedModel.reset();

	m_sqrDutyModel.reset();
	m_sqrSweepModel.reset();

	m_repeatSpeedModel.reset();

	m_phaserOffsetModel.reset();
	m_phaserSweepModel.reset();

	m_lpFilCutModel.reset();
	m_lpFilCutSweepModel.reset();
	m_lpFilResoModel.reset();
	m_hpFilCutModel.reset();
	m_hpFilCutSweepModel.reset();

	m_waveFormModel.reset();
}

class sfxrKnob : public Knob
{
public:
	sfxrKnob( QWidget * _parent ) :
			Knob( knobStyled, _parent )
	{
		setFixedSize( 20, 20 );
		setCenterPointX( 10.0 );
		setCenterPointY( 10.0 );
		setTotalAngle( 270.0 );
		setLineWidth( 1 );
	}
};

#define createKnob( _knob, _x, _y, _name )\
	_knob = new sfxrKnob( this ); \
	_knob->setHintText( tr( _name ":" ), "" ); \
	_knob->move( _x, _y ); \
	ToolTip::add( _knob, tr( _name ) );

#define createButton( _button, _x, _y, _name, _resName )\
	_button = new PixmapButton( this, tr( _name ) );\
	_button->move( _x, _y );\
	_button->setActiveGraphic( embed::getIconPixmap( _resName "_active" ) );\
	_button->setInactiveGraphic( embed::getIconPixmap( _resName "_inactive" ) );\
	ToolTip::add( _button, tr( _name ) );

#define createButtonLocalGraphic( _button, _x, _y, _name, _resName )\
	_button = new PixmapButton( this, tr( _name ) );\
	_button->move( _x, _y );\
	_button->setActiveGraphic( PLUGIN_NAME::getIconPixmap( _resName "_active" ) );\
	_button->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( _resName "_inactive" ) );\
	ToolTip::add( _button, tr( _name ) );

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

	createKnob(m_attKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*0, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*0, "Attack Time");
	createKnob(m_holdKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*1, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*0, "Sustain Time");
	createKnob(m_susKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*2, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*0, "Sustain Punch");
	createKnob(m_decKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*3, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*0, "Decay Time");

	m_attKnob	->setObjectName( "envKnob" );
	m_holdKnob	->setObjectName( "envKnob" );
	m_susKnob	->setObjectName( "envKnob" );
	m_decKnob	->setObjectName( "envKnob" );

	createKnob(m_startFreqKnob,	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*0, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*1, "Start Frequency");
	createKnob(m_minFreqKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*1, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*1, "Min Frequency");
	createKnob(m_slideKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*2, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*1, "Slide");
	createKnob(m_dSlideKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*3, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*1, "Delta Slide");
	createKnob(m_vibDepthKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*4, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*1, "Vibrato Depth");
	createKnob(m_vibSpeedKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*5, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*1, "Vibrato Speed");

	m_startFreqKnob	->setObjectName( "freqKnob" );
	m_minFreqKnob	->setObjectName( "freqKnob" );
	m_slideKnob		->setObjectName( "freqKnob" );
	m_dSlideKnob	->setObjectName( "freqKnob" );
	m_vibDepthKnob	->setObjectName( "freqKnob" );
	m_vibSpeedKnob	->setObjectName( "freqKnob" );

	createKnob(m_changeAmtKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*0, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Change Amount");
	createKnob(m_changeSpeedKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*1, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Change Speed");

	m_changeAmtKnob		->setObjectName( "changeKnob" );
	m_changeSpeedKnob	->setObjectName( "changeKnob" );

	createKnob(m_sqrDutyKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*3, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Square Duty (Square wave only)");
    createKnob(m_sqrSweepKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*4, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*2, "Duty Sweep (Square wave only)");

	m_sqrDutyKnob	->setObjectName( "sqrKnob" );
	m_sqrSweepKnob	->setObjectName( "sqrKnob" );

	createKnob(m_repeatSpeedKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*0, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*3, "Repeat Speed");

	m_repeatSpeedKnob	->setObjectName( "repeatKnob" );

	createKnob(m_phaserOffsetKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*3, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*3, "Phaser Offset");
	createKnob(m_phaserSweepKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*4, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*3, "Phaser Sweep");

	m_phaserOffsetKnob	->setObjectName( "phaserKnob" );
	m_phaserSweepKnob	->setObjectName( "phaserKnob" );

	createKnob(m_lpFilCutKnob,		KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*0, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*4, "LP Filter Cutoff");
	createKnob(m_lpFilCutSweepKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*1, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*4, "LP Filter Cutoff Sweep");
	createKnob(m_lpFilResoKnob, 	KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*2, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*4, "LP Filter Resonance");
	createKnob(m_hpFilCutKnob,		KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*3, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*4, "HP Filter Cutoff");
	createKnob(m_hpFilCutSweepKnob, KNOBS_BASE_X+KNOB_BLOCK_SIZE_X*4, KNOBS_BASE_Y+KNOB_BLOCK_SIZE_Y*4, "HP Filter Cutoff Sweep");

	m_lpFilCutKnob		->setObjectName( "filterKnob" );
	m_lpFilCutSweepKnob	->setObjectName( "filterKnob" );
	m_lpFilResoKnob		->setObjectName( "filterKnob" );
	m_hpFilCutKnob		->setObjectName( "filterKnob" );
	m_hpFilCutSweepKnob	->setObjectName( "filterKnob" );

	createButtonLocalGraphic(m_sqrWaveBtn,		KNOBS_BASE_X+WAVEFORM_BUTTON_WIDTH*0, WAVEFORM_BASE_Y, "Square Wave", "sfxr_square_wave");
	createButtonLocalGraphic(m_sawWaveBtn,		KNOBS_BASE_X+WAVEFORM_BUTTON_WIDTH*1, WAVEFORM_BASE_Y, "Saw Wave", "sfxr_saw_wave");
	createButtonLocalGraphic(m_sinWaveBtn,		KNOBS_BASE_X+WAVEFORM_BUTTON_WIDTH*2, WAVEFORM_BASE_Y, "Sine Wave", "sfxr_sin_wave");
	createButtonLocalGraphic(m_noiseWaveBtn,	KNOBS_BASE_X+WAVEFORM_BUTTON_WIDTH*3, WAVEFORM_BASE_Y, "Noise", "sfxr_white_noise_wave");

	m_waveBtnGroup = new automatableButtonGroup( this );
	m_waveBtnGroup->addButton(m_sqrWaveBtn);
	m_waveBtnGroup->addButton(m_sawWaveBtn);
	m_waveBtnGroup->addButton(m_sinWaveBtn);
    m_waveBtnGroup->addButton(m_noiseWaveBtn);

	createButtonLocalGraphic(m_pickupBtn,		GENERATOR_BASE_X+GENERATOR_BUTTON_WIDTH*0, GENERATOR_BASE_Y, "Generate pick up/coin sfx", "pickup");
	createButtonLocalGraphic(m_laserBtn,		GENERATOR_BASE_X+GENERATOR_BUTTON_WIDTH*1, GENERATOR_BASE_Y, "Generate laser/shoot sfx", "laser");
	createButtonLocalGraphic(m_explosionBtn,	GENERATOR_BASE_X+GENERATOR_BUTTON_WIDTH*2, GENERATOR_BASE_Y, "Generate explosion sfx", "explosion");
	createButtonLocalGraphic(m_powerupBtn,		GENERATOR_BASE_X+GENERATOR_BUTTON_WIDTH*3, GENERATOR_BASE_Y, "Generate power up sfx", "powerup");
	createButtonLocalGraphic(m_hitBtn,			GENERATOR_BASE_X+GENERATOR_BUTTON_WIDTH*4, GENERATOR_BASE_Y, "Generate hit/hurt sfx", "hit");
	createButtonLocalGraphic(m_jumpBtn,			GENERATOR_BASE_X+GENERATOR_BUTTON_WIDTH*5, GENERATOR_BASE_Y, "Generate jump sfx", "jump");
	createButtonLocalGraphic(m_blipBtn,			GENERATOR_BASE_X+GENERATOR_BUTTON_WIDTH*6, GENERATOR_BASE_Y, "Generate blip/select sfx", "blip");
	connect( m_pickupBtn, SIGNAL ( clicked() ), this, SLOT ( genPickup() ) );
	connect( m_laserBtn, SIGNAL ( clicked() ), this, SLOT ( genLaser() ) );
	connect( m_explosionBtn, SIGNAL ( clicked() ), this, SLOT ( genExplosion() ) );
	connect( m_powerupBtn, SIGNAL ( clicked() ), this, SLOT ( genPowerup() ) );
	connect( m_hitBtn, SIGNAL ( clicked() ), this, SLOT ( genHit() ) );
	connect( m_jumpBtn, SIGNAL ( clicked() ), this, SLOT ( genJump() ) );
	connect( m_blipBtn, SIGNAL ( clicked() ), this, SLOT ( genBlip() ) );

	createButtonLocalGraphic(m_randomizeBtn,	RAND_BUTTON_X, RAND_BUTTON_Y, "Generate random sfx", "randomize");
	createButtonLocalGraphic(m_mutateBtn,		MUTA_BUTTON_X, MUTA_BUTTON_Y, "Mutate sfx", "mutate");
	connect( m_randomizeBtn, SIGNAL ( clicked() ), this, SLOT ( randomize() ) );
	connect( m_mutateBtn, SIGNAL ( clicked() ), this, SLOT ( mutate() ) );

	//preview sound on generator/random/mutate button clicked
/*  // disabled for now
	connect( m_pickupBtn, SIGNAL ( clicked() ), this, SLOT ( previewSound() ) );
	connect( m_laserBtn, SIGNAL ( clicked() ), this, SLOT ( previewSound() ) );
	connect( m_explosionBtn, SIGNAL ( clicked() ), this, SLOT ( previewSound() ) );
	connect( m_powerupBtn, SIGNAL ( clicked() ), this, SLOT ( previewSound() ) );
	connect( m_hitBtn, SIGNAL ( clicked() ), this, SLOT ( previewSound() ) );
	connect( m_jumpBtn, SIGNAL ( clicked() ), this, SLOT ( previewSound() ) );
	connect( m_blipBtn, SIGNAL ( clicked() ), this, SLOT ( previewSound() ) );
	connect( m_randomizeBtn, SIGNAL ( clicked() ), this, SLOT ( previewSound() ) );
	connect( m_mutateBtn, SIGNAL ( clicked() ), this, SLOT ( previewSound() ) );
*/
}

void sfxrInstrumentView::modelChanged()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();

	m_attKnob->setModel( &s->m_attModel );
	m_holdKnob->setModel( &s->m_holdModel );
	m_susKnob->setModel( &s->m_susModel );
	m_decKnob->setModel( &s->m_decModel );

	m_startFreqKnob->setModel( &s->m_startFreqModel );
	m_minFreqKnob->setModel( &s->m_minFreqModel );
	m_slideKnob->setModel( &s->m_slideModel );
	m_dSlideKnob->setModel( &s->m_dSlideModel );
	m_vibDepthKnob->setModel( &s->m_vibDepthModel );
	m_vibSpeedKnob->setModel( &s->m_vibSpeedModel );

	m_changeAmtKnob->setModel( &s->m_changeAmtModel );
	m_changeSpeedKnob->setModel( &s->m_changeSpeedModel );

	m_sqrDutyKnob->setModel( &s->m_sqrDutyModel );
    m_sqrSweepKnob->setModel( &s->m_sqrSweepModel );

	m_repeatSpeedKnob->setModel( &s->m_repeatSpeedModel );

	m_phaserOffsetKnob->setModel( &s->m_phaserOffsetModel );
	m_phaserSweepKnob->setModel( &s->m_phaserSweepModel );

	m_lpFilCutKnob->setModel( &s->m_lpFilCutModel );
	m_lpFilCutSweepKnob->setModel( &s->m_lpFilCutSweepModel );
	m_lpFilResoKnob->setModel( &s->m_lpFilResoModel );
	m_hpFilCutKnob->setModel( &s->m_hpFilCutModel );
	m_hpFilCutSweepKnob->setModel( &s->m_hpFilCutSweepModel );

	m_waveBtnGroup->setModel( &s->m_waveFormModel );
}

void sfxrInstrumentView::genPickup()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();
	s->resetModels();
	s->m_startFreqModel.setValue( 0.4f+frnd(0.5f) );
	s->m_attModel.setValue( 0.0f );
	s->m_holdModel.setValue( frnd(0.1f) );
	s->m_decModel.setValue( 0.1f+frnd(0.4f) );
	s->m_susModel.setValue( 0.3f+frnd(0.3f) );

	if(rnd(1))
	{
		s->m_changeSpeedModel.setValue( 0.5f+frnd(0.2f) );
		s->m_changeAmtModel.setValue( 0.2f+frnd(0.4f) );
	}
}

void sfxrInstrumentView::genLaser()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();
	s->resetModels();

	s->m_waveFormModel.setValue( rnd(2) );
	if(s->m_waveFormModel.value()==2 && rnd(1))
		s->m_waveFormModel.setValue( rnd(1) );

	s->m_startFreqModel.setValue( 0.5f+frnd(0.5f) );
	s->m_minFreqModel.setValue(	s->m_startFreqModel.value()-0.2f-frnd(0.6f)	);

	if(s->m_minFreqModel.value()<0.2f)
	{
		s->m_minFreqModel.setValue(0.2f);
	}

	s->m_slideModel.setValue( -0.15f-frnd(0.2f) );

	if(rnd(2)==0)
	{
		s->m_startFreqModel.setValue( 0.3f+frnd(0.6f) );
		s->m_minFreqModel.setValue( frnd(0.1f) );
		s->m_slideModel.setValue( -0.35f-frnd(0.3f) );
	}

	if(rnd(1))
	{
		s->m_sqrDutyModel.setValue( frnd(0.5f) );
		s->m_sqrSweepModel.setValue( 0.2f );
	}
	else
	{
		s->m_sqrDutyModel.setValue( 0.4f+frnd(0.5f) );
		s->m_sqrSweepModel.setValue( -frnd(0.7f) );
	}

	s->m_attModel.setValue( 0.0f );
	s->m_holdModel.setValue( 0.1f+frnd(0.2f) );
	s->m_decModel.setValue( frnd(0.4f) );

	if(rnd(1))
	{
		s->m_susModel.setValue( frnd(0.3f) );
	}

	if(rnd(2)==0)
	{
		s->m_phaserOffsetModel.setValue( frnd(0.2f) );
		s->m_phaserSweepModel.setValue( -frnd(0.2f) );
	}

	if(rnd(1))
		s->m_hpFilCutModel.setValue( frnd(0.3f) );
}

void sfxrInstrumentView::genExplosion()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();
	s->resetModels();

	s->m_waveFormModel.setValue( 3 );

	if(rnd(1))
	{
		s->m_startFreqModel.setValue( 0.1f+frnd(0.4f) );
		s->m_slideModel.setValue( -0.1f+frnd(0.4f) );
	}
	else
	{
		s->m_startFreqModel.setValue( 0.2f+frnd(0.7f) );
		s->m_slideModel.setValue( -0.2f-frnd(0.2f) );
	}
	s->m_startFreqModel.setValue( s->m_startFreqModel.value()*s->m_startFreqModel.value() );

	if(rnd(4)==0)
	{
		s->m_slideModel.setValue( 0.0f );
	}

	if(rnd(2)==0)
	{
		s->m_repeatSpeedModel.setValue( 0.3f+frnd(0.5f) );
	}

	s->m_attModel.setValue( 0.0f );
	s->m_holdModel.setValue( 0.1f+frnd(0.3f) );
	s->m_decModel.setValue( 0.5f );
	if(rnd(1)==0)
	{
		s->m_phaserOffsetModel.setValue( -0.3f+frnd(0.9f) );
		s->m_phaserSweepModel.setValue( -frnd(0.3f) );
	}
	s->m_susModel.setValue( 0.2f+frnd(0.6f) );

	if(rnd(1))
	{
		s->m_vibDepthModel.setValue( frnd(0.7f) );
		s->m_vibSpeedModel.setValue( frnd(0.6f) );
	}
	if(rnd(2)==0)
	{
		s->m_changeSpeedModel.setValue( 0.6f+frnd(0.3f) );
		s->m_changeAmtModel.setValue( 0.8f-frnd(1.6f) );
	}

}

void sfxrInstrumentView::genPowerup()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();
	s->resetModels();

	if(rnd(1))
		s->m_waveFormModel.setValue( 1 );
	else
		s->m_sqrDutyModel.setValue( frnd(0.6f) );
	if(rnd(1))
	{
		s->m_startFreqModel.setValue( 0.2f+frnd(0.3f) );
		s->m_slideModel.setValue( 0.1f+frnd(0.4f) );
		s->m_repeatSpeedModel.setValue( 0.4f+frnd(0.4f) );
	}
	else
	{
		s->m_startFreqModel.setValue( 0.2f+frnd(0.3f) );
		s->m_slideModel.setValue( 0.05f+frnd(0.2f) );
		if(rnd(1))
		{
			s->m_vibDepthModel.setValue( frnd(0.7f) );
			s->m_vibSpeedModel.setValue( frnd(0.6f) );
		}
	}

	s->m_attModel.setValue( 0.0f );
	s->m_holdModel.setValue( frnd(0.4f) );
	s->m_decModel.setValue( 0.1f+frnd(0.4f) );
}

void sfxrInstrumentView::genHit()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();
	s->resetModels();

	s->m_waveFormModel.setValue( rnd(2) );
	if(s->m_waveFormModel.value()==2)
	{
		s->m_waveFormModel.setValue( 3 );
	}
	if(s->m_waveFormModel.value()==0)
	{
		s->m_sqrDutyModel.setValue( frnd(0.6f) );
	}

	s->m_startFreqModel.setValue( 0.2f+frnd(0.6f) );
	s->m_slideModel.setValue( -0.3f-frnd(0.4f) );

	s->m_attModel.setValue( 0.0f );
	s->m_holdModel.setValue( frnd(0.1f) );
	s->m_decModel.setValue( 0.1f+frnd(0.2f) );
	if(rnd(1))
	{
		s->m_hpFilCutModel.setValue( frnd(0.3f) );
	}
}

void sfxrInstrumentView::genJump()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();
	s->resetModels();

	s->m_waveFormModel.setValue( 0 );
	s->m_sqrDutyModel.setValue( frnd(0.6f) );

	s->m_startFreqModel.setValue( 0.3f+frnd(0.3f) );
	s->m_slideModel.setValue( 0.1f+frnd(0.2f) );

	s->m_attModel.setValue( 0.0f );
	s->m_holdModel.setValue( 0.1f+frnd(0.3f) );
	s->m_decModel.setValue( 0.1f+frnd(0.2f) );

	if(rnd(1))
	{
		s->m_hpFilCutModel.setValue( frnd(0.3f) );
	}
	if(rnd(1))
	{

		s->m_lpFilCutModel.setValue( 1.0f-frnd(0.6f) );
	}

}

void sfxrInstrumentView::genBlip()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();
	s->resetModels();

	s->m_waveFormModel.setValue( rnd(1) );
	if( s->m_waveFormModel.value()==0 )
	{
		s->m_sqrDutyModel.setValue( frnd(0.6f) );
	}

	s->m_startFreqModel.setValue( 0.2f+frnd(0.4f) );

	s->m_attModel.setValue( 0.0f );
	s->m_holdModel.setValue( 0.1f+frnd(0.1f) );
	s->m_decModel.setValue( frnd(0.2f) );
	s->m_hpFilCutModel.setValue( 0.1f );
}

void sfxrInstrumentView::randomize()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();

	s->m_startFreqModel.setValue( pow(frnd(2.0f)-1.0f, 2.0f) );
	if(rnd(1))
	{
		s->m_startFreqModel.setValue( pow(frnd(2.0f)-1.0f, 3.0f)+0.5f );
	}
	s->m_minFreqModel.setValue( 0.0f );
	s->m_slideModel.setValue( pow(frnd(2.0f)-1.0f, 5.0f) );
	if( s->m_startFreqModel.value()>0.7f && s->m_slideModel.value()>0.2f )
	{
		s->m_slideModel.setValue( -s->m_slideModel.value() );
	}
	if( s->m_startFreqModel.value()<0.2f && s->m_slideModel.value()<-0.05f )
	{
		s->m_slideModel.setValue( -s->m_slideModel.value() );
	}
	s->m_dSlideModel.setValue( pow(frnd(2.0f)-1.0f, 3.0f) );

	s->m_sqrDutyModel.setValue( frnd(2.0f)-1.0f );
	s->m_sqrSweepModel.setValue( pow(frnd(2.0f)-1.0f, 3.0f) );

	s->m_vibDepthModel.setValue( pow(frnd(2.0f)-1.0f, 3.0f) );
	s->m_vibSpeedModel.setValue( frnd(2.0f)-1.0f );
	//s->m_vibDelayModel.setValue( frnd(2.0f)-1.0f );

	s->m_attModel.setValue( pow(frnd(2.0f)-1.0f, 3.0f) );
	s->m_holdModel.setValue( pow(frnd(2.0f)-1.0f, 2.0f) );
	s->m_decModel.setValue( frnd(2.0f)-1.0f );
	s->m_susModel.setValue( pow(frnd(0.8f), 2.0f) );
	if(s->m_attModel.value()+s->m_holdModel.value()+s->m_decModel.value()<0.2f)
	{
		s->m_holdModel.setValue( s->m_holdModel.value()+0.2f+frnd(0.3f) );
		s->m_decModel.setValue( s->m_decModel.value()+0.2f+frnd(0.3f) );
	}

	s->m_lpFilResoModel.setValue( frnd(2.0f)-1.0f );
	s->m_lpFilCutModel.setValue( 1.0f-pow(frnd(1.0f), 3.0f) );
	s->m_lpFilCutSweepModel.setValue( pow(frnd(2.0f)-1.0f, 3.0f) );
	if(s->m_lpFilCutModel.value()<0.1f && s->m_lpFilCutSweepModel.value()<-0.05f)
	{
		s->m_lpFilCutSweepModel.setValue( -s->m_lpFilCutSweepModel.value() );
	}
	s->m_hpFilCutModel.setValue( pow(frnd(1.0f), 5.0f) );
	s->m_hpFilCutSweepModel.setValue( pow(frnd(2.0f)-1.0f, 5.0f) );

	s->m_phaserOffsetModel.setValue( pow(frnd(2.0f)-1.0f, 3.0f) );
	s->m_phaserSweepModel.setValue( pow(frnd(2.0f)-1.0f, 3.0f) );

	s->m_repeatSpeedModel.setValue( frnd(2.0f)-1.0f );

	s->m_changeSpeedModel.setValue( frnd(2.0f)-1.0f );
	s->m_changeAmtModel.setValue( frnd(2.0f)-1.0f );

}

void sfxrInstrumentView::mutate()
{
	sfxrInstrument * s = castModel<sfxrInstrument>();

	if(rnd(1)) s->m_startFreqModel.setValue( s->m_startFreqModel.value()+frnd(0.1f)-0.05f );
	//		if(rnd(1)) s->m_minFreqModel.setValue( s->m_minFreqModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_slideModel.setValue( s->m_slideModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_dSlideModel.setValue( s->m_dSlideModel.value()+frnd(0.1f)-0.05f );

	if(rnd(1)) s->m_sqrDutyModel.setValue( s->m_sqrDutyModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_sqrSweepModel.setValue( s->m_sqrSweepModel.value()+frnd(0.1f)-0.05f );

	if(rnd(1)) s->m_vibDepthModel.setValue( s->m_vibDepthModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_vibSpeedModel.setValue( s->m_vibSpeedModel.value()+frnd(0.1f)-0.05f );
	//		if(rnd(1)) s->m_vibDelayModel.setValue( s->m_vibDelayModel.value()+frnd(0.1f)-0.05f );

	if(rnd(1)) s->m_attModel.setValue( s->m_attModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_holdModel.setValue( s->m_holdModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_decModel.setValue( s->m_decModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_susModel.setValue( s->m_susModel.value()+frnd(0.1f)-0.05f );

	if(rnd(1)) s->m_lpFilResoModel.setValue( s->m_lpFilResoModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_lpFilCutModel.setValue( s->m_lpFilCutModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_lpFilCutSweepModel.setValue( s->m_lpFilCutSweepModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_hpFilCutModel.setValue( s->m_hpFilCutModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_hpFilCutSweepModel.setValue( s->m_hpFilCutSweepModel.value()+frnd(0.1f)-0.05f );

	if(rnd(1)) s->m_phaserOffsetModel.setValue( s->m_phaserOffsetModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_phaserSweepModel.setValue( s->m_phaserSweepModel.value()+frnd(0.1f)-0.05f );

	if(rnd(1)) s->m_repeatSpeedModel.setValue( s->m_repeatSpeedModel.value()+frnd(0.1f)-0.05f );

	if(rnd(1)) s->m_changeSpeedModel.setValue( s->m_changeSpeedModel.value()+frnd(0.1f)-0.05f );
	if(rnd(1)) s->m_changeAmtModel.setValue( s->m_changeAmtModel.value()+frnd(0.1f)-0.05f );

}

void sfxrInstrumentView::previewSound()
{
	sfxrInstrument* s = castModel<sfxrInstrument>();
	InstrumentTrack* it = s->instrumentTrack();
	it->silenceAllNotes();
	it->processInEvent( MidiEvent( MidiNoteOn, 0, it->baseNoteModel()->value(), MidiDefaultVelocity ) );
}

extern "C"
{

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

}