/*
 * MusE FLUID Synth softsynth plugin
 *
 * Copyright (C) 2002 Robert Ham (node@users.sourcforge.net)
 *
 * $Id: fluidsynti.cpp,v 1.1.1.1 2003/10/29 10:06:05 wschweer Exp $
 *
 */

#include <list>
#include <string>
#include <iostream>

#include "fluidsynti.h"
#include "midictrl.h"

/*** FLUIDParameterSet ***/

FLUIDParameterSet::FLUIDParameterSet (const FLUIDParameterSet& ps)
: _parameters (ps._parameters) {
}

/*** FLUIDSynth ***/

FLUIDSynth::FLUIDSynth (const char * name)
: Mess (name, 2),
	_gain(1.0)
{
	pthread_mutex_init(&_sfloader_mutex,NULL);
	fluid_settings_t* settings;
	settings = new_fluid_settings();

	//  settings.flags &= ~FLUID_AUDIO;
	//  settings.sample_format = FLUID_FLOAT_FORMAT;
	// fluid_settings_setstr(settings, "audio.driver", "alsa");

	_fluidsynth = new_fluid_synth (settings);
	if (!_fluidsynth) {
		M_ERROR("error while creating fluidsynth");
	return;
	}
		_chorusParameters.setParameter("speed", 0.5);
		//FIXME: Changing chorus parameters / turning it on seems to lock the client. Perhaps put it in a separate thread?
		_chorusParameters.setParameter("type" , 1);
		_chorusParameters.setParameter("depth", 0.5);
		_chorusParameters.setParameter("number", 3);
		_chorusParameters.setParameter("level", 0);
		setReverb();
		setChorus();
	//Set up the channels:
	for (int i=0; i<MUSE_FLUID_MAX_NR_OF_CHANNELS; i++)
	{
		_fluidChannels[i].setExternalFontId(MUSE_FLUID_UNSPECIFIED_FONT);
		_fluidChannels[i].setPresetId(MUSE_FLUID_UNSPECIFIED_PRESET);
	}
	_nrOfSoundfonts = 0;
	_currentlyLoadedFonts = 0;
	_lastDir = "";
}

FLUIDSynth::~FLUIDSynth () {
	int err;
	err = delete_fluid_synth (_fluidsynth);
	if (err == -1) {
			std::cerr << DEBUG_ARGS << "error while destroying synth: " << fluid_synth_error(_fluidsynth) << std::endl;
		return;
	}
	//Destroy the mutex
	if (pthread_mutex_destroy(&_sfloader_mutex) != 0)
		std::cerr << DEBUG_ARGS << "Strange, mutex busy! Should not be!" << std::endl;
}

bool FLUIDSynth::init () {
	return false;
}

void FLUIDSynth::processEvent (MEvent * mev) {
	int err;
	int channel = mev->chan();
	//printf("channel: %d font: %d\n",channel, getFontInternalIdByChannel(channel));
	//static std::string st; //Necessary?

	switch (mev->type())
	{
		case SND_SEQ_EVENT_NOTEON:
		case SND_SEQ_EVENT_CONTROL14:
		case SND_SEQ_EVENT_NONREGPARAM:
		case SND_SEQ_EVENT_REGPARAM:

		case SND_SEQ_EVENT_KEYPRESS:
		{
			if (getFontInternalIdByChannel(channel) != MUSE_FLUID_UNSPECIFIED_FONT)
			{
				err = fluid_synth_noteon(_fluidsynth, channel, mev->dataA(), mev->dataB());
				if (err)
				{
					//std::cerr << DEBUG_ARGS << "error processing noteon event: " << fluid_synth_error(_fluidsynth);
					//std::cerr << " Ret val is: " << err << std::endl;
				}
			}
			break;
		}

		case SND_SEQ_EVENT_NOTEOFF:
		{
			if (getFontInternalIdByChannel(channel) != MUSE_FLUID_UNSPECIFIED_FONT)
			{
				err = fluid_synth_noteoff(_fluidsynth, channel, mev->dataA());
				if (err)
				std::cerr << DEBUG_ARGS << "error processing noteoff event: " << fluid_synth_error(_fluidsynth) << std::endl;

			}
			break;
		}

		case SND_SEQ_EVENT_PGMCHANGE:
		{
			if (getFontInternalIdByChannel(channel) != MUSE_FLUID_UNSPECIFIED_FONT)
			{
					//printf("Program change, channel: %d data: %d data A: %d data B: %d\n",channel, mev->data(), mev->dataA(), mev->dataB());

					err = fluid_synth_program_select(_fluidsynth, channel, getFontInternalIdByChannel(channel), 0, mev->dataB());
					if (err)
						std::cerr << DEBUG_ARGS << "error changing soundfont: " << fluid_synth_error(_fluidsynth) << std::endl;
					else
						setChannelPreset(mev->dataB(), channel);
			}
			break;
		}

		case SND_SEQ_EVENT_CONTROLLER:
		{
			if (getFontInternalIdByChannel(channel) != MUSE_FLUID_UNSPECIFIED_FONT)
			{
				static int ctrlLo, ctrlHi, dataLo, dataHi;
				switch(mev->dataA())
				{
					case CTRL_LNRPN:  ctrlLo = mev->dataB(); break;
					case CTRL_HNRPN:  ctrlHi = mev->dataB(); break;
					case CTRL_LDATA:  dataLo = mev->dataB(); break;
					case CTRL_HDATA:  dataHi = mev->dataB(); break;
				}
				if (mev->dataA() == CTRL_HDATA)
				{
					int ctrl = ctrlLo+ctrlHi*128;
					int data = dataLo+dataHi*128;
					err = fluid_synth_cc(_fluidsynth, channel, ctrl, data);
					if (err)
						std::cerr << DEBUG_ARGS << "error processing controller event: " << fluid_synth_error(_fluidsynth) << std::endl;
				}
				else //Take care of normal messages
				{
					if (fluid_synth_cc(_fluidsynth, channel, mev->dataA(), mev->dataB()))
						std::cerr << DEBUG_ARGS << "Error processing controller event" << fluid_synth_error(_fluidsynth) << std::endl;
				}
			break;
			}
		}

		case SND_SEQ_EVENT_PITCHBEND:
		{
			if (getFontInternalIdByChannel(channel) != MUSE_FLUID_UNSPECIFIED_FONT)
			{
				err = fluid_synth_pitch_bend (_fluidsynth, channel, (mev->dataA()<<7) | mev->dataB());
				if (err)
					std::cerr << DEBUG_ARGS << "error processing pitch bend event: " << fluid_synth_error(_fluidsynth) << std::endl;
				break;
			}
		}

		case SND_SEQ_EVENT_SYSEX:
		{
			decodeSysex(mev->data(), mev->dataLen());
			break;
		}

		default:
		{
			std::cerr << DEBUG_ARGS << "unknown MEvent type " << mev->type() << std::endl;
			break;
		}
	}
}


const char * FLUIDSynth::getPatchName (int channel,	int /*hbank*/, int /*lbank*/, int /*prog*/, MType /*type*/)
{
	if (getFontInternalIdByChannel(channel) == MUSE_FLUID_UNSPECIFIED_FONT)
		return "no preset";
	else if (getChannelPreset(channel) == MUSE_FLUID_UNSPECIFIED_PRESET)
		return "no preset";
	else
	{
		//printf("Getpatchname, channel: %d\n",channel);
		fluid_preset_t * preset = fluid_synth_get_channel_preset(_fluidsynth, channel);
		if (!preset) return "no preset";
		return preset->get_name(preset);
	}
}

const MidiPatch * FLUIDSynth::getFirstPatch (int channel) const
{
	static MidiPatch midiPatch;
	//printf("Get first patch. Fluid channel: %d\n", channel);

	midiPatch.typ = 0;
	midiPatch.hbank = 0;

	fluid_preset_t *preset;
	int font_id = getFontInternalIdByChannel(channel);
	if (font_id == MUSE_FLUID_UNSPECIFIED_FONT)
		return 0;
	fluid_sfont_t *sfont = fluid_synth_get_sfont_by_id(_fluidsynth, font_id);

	for (unsigned int bank = 0; bank < 128; ++bank)
	{
		for (unsigned int patch = 0; patch < 128; ++patch)
		{
			preset = sfont->get_preset (sfont, bank, patch);
			if (preset)
			{
				midiPatch.lbank = bank;
				midiPatch.prog = patch;
				midiPatch.name = preset->get_name (preset);
				return &midiPatch;
			}
		}
	}
	return 0;
}

const MidiPatch* FLUIDSynth::getNextPatch (int channel, const MidiPatch * patch) const {
	static MidiPatch midiPatch;
	//First check if there actually is any soundfont associated to the channel. If not, don't bother
	if (getFontInternalIdByChannel(channel) == MUSE_FLUID_UNSPECIFIED_FONT)
		return 0;
	if (patch == 0) return getFirstPatch (channel);

	midiPatch.typ = 0;
	midiPatch.hbank = 0;

	int font_id = getFontInternalIdByChannel(channel);
	//printf("Font has internal id: %d\n",font_id);
	if (font_id == MUSE_FLUID_UNSPECIFIED_FONT)
		return 0;
	fluid_preset_t* preset;
	fluid_sfont_t* sfont = fluid_synth_get_sfont_by_id(_fluidsynth, font_id);

	unsigned int prog = patch->prog;
	for (unsigned int bank = patch->lbank; bank < 128; ++bank)
	{
		for (prog = patch->prog + 1; prog < 128; ++prog)
		{
			preset = sfont->get_preset (sfont, bank, prog);
			if (preset)
			{
				//printf("Preset info: bank: %d prog: %d name: %s\n",bank, prog, preset->get_name(preset));
				midiPatch.lbank = bank;
				midiPatch.prog = prog;
				midiPatch.name = preset->get_name (preset);
				return &midiPatch;
			}
		}
	}
	return 0;
}


void FLUIDSynth::write (int n, float ** ports, int offset) {
	int err;
	err = fluid_synth_write_float(_fluidsynth, n, ports[0], offset, 1, ports[1], offset, 1);
	if (err) {
		std::cerr << DEBUG_ARGS << "error writing from synth: " << fluid_synth_error(_fluidsynth) << std::endl;
		return;
	}
}

void FLUIDSynth::decodeSysex (const unsigned char * data, int len) {
	data += 1;
	len -= 2;
	unsigned char * decdata = new unsigned char [len / 2];
	for (int i = 0; i < len / 2; i++) {
		decdata[i] = 0xf0 & (data[i * 2] << 4);
		decdata[i] |= (0xf & (data[(i * 2) + 1]));
	}
	processSysex (decdata, len / 2);
	delete decdata;
}

void FLUIDSynth::encodeSysex (const unsigned char * data, int datalen) {
	int ndatalen = (datalen*2);
	unsigned char ndata [ndatalen];
	const unsigned char * p = data;
	for (int i = 0; i < datalen; ++p, ++i) {
		ndata[(i*2)] = (*p >> 4) & 0xf;
		ndata[(i*2)+1] = (*p & 0xf);
	}
	sendSysex (ndata, ndatalen);
}

void FLUIDSynth::processSysex (const unsigned char * data, int len) {
	char * cp;
	double * dp;
	double d;
	int ndatalen;
	unsigned char * ndata;

	switch (*data) {
	case MUSE_FLUID_GUI_REQ_FXPARAMETER_SET:
		cp = ((char *) data) + 2;
		dp = (double *) (cp + strlen (cp) + 1);
		if (*(data+1) == MUSE_FLUID_PARAMETER_REVERB) {
			_reverbParameters.setParameter (std::string(cp), *dp);
			setReverb();
		}
		else {
			_chorusParameters.setParameter (std::string(cp), *dp);
			setChorus();
		}
		break;
	case MUSE_FLUID_GUI_REQ_FXPARAMETER_GET: //GUI has sent a request for something
		cp = ((char *) data) + 2;
		if (*(data+1) == MUSE_FLUID_PARAMETER_REVERB)
			d = _reverbParameters.getParameter (std::string(cp));
		else
			d = _chorusParameters.getParameter (std::string(cp));

		ndatalen = len + sizeof(double);
		ndata = new unsigned char [ndatalen];
		memcpy (ndata, data, len); //Copy the original message
		ndata[0] = MUSE_FLUID_CLIENT_SEND_PARAMETER; //Set it to a client-send-parameter instead
		//Set the last value
		dp = (double *) (ndata + len);
		*dp = d;
		encodeSysex (ndata, ndatalen);
		delete ndata;
		break;
	case MUSE_FLUID_GAIN_SET:
		dp = (double *) (data + 1);
		setGain (*dp);
		break;
	case MUSE_FLUID_GAIN_GET:
		ndatalen = len + sizeof (double);
		ndata = new unsigned char [ndatalen];
		memcpy (ndata, data, len);
		dp = (double *) (ndata + len);
		*dp = _gain;
		encodeSysex (ndata, ndatalen);
		delete ndata;
		break;
	case MUSE_FLUID_SOUNDFONT_PUSH:
		cp = ((char *) data) + 2;
		if (!pushSoundfont (std::string(cp), *(data + 1))) {
			std::string s ("could not load soundfont ");
			s += cp;
			sendSysexError (s.c_str());
		}
		break;
	case MUSE_FLUID_GUI_SOUNDFONT_CHANNEL_SET:
		sfChannelChange(*(data +1), *(data +2));
		//printf("Soundfont channel %d to font %d\n",*(data +2), *(data +1));
	break;
	case MUSE_FLUID_CLIENT_RESTORE_CHANNELDATA:
	{
		unsigned char ext_id = *(data +1);
		unsigned char chnl = *(data +2);
		unsigned int *presetdata = (unsigned int*) &data[3];
		unsigned int preset = *presetdata;
		//printf ("Chan: %d Ext_id: %d Preset: %d\n",chnl, ext_id, preset);
		sfChannelChange(ext_id, chnl);
		setChannelPreset(preset, chnl);
	}
	break;
	case MUSE_FLUID_SOUNDFONT_POP:
		popSoundfont( *(data+1) );
		break;
	case MUSE_FLUID_GUI_REQ_SOUNDFONTS:
		this->sendSoundFontdata();
		break; //It's 2:00 am right now ;-)
	case MUSE_FLUID_GUI_LASTDIR_CHANGE:
		if (*(data +1) != MUSE_FLUID_UNSPECIFIED_LASTDIR)
			_lastDir = std::string((char*) (data + 1));
		else
			_lastDir = "";
		//printf("New lastdir: %s\n",_lastDir.c_str());
		break;
	case MUSE_FLUID_CLIENT_INIT_PARAMS:
		_nrOfSoundfonts = *(data +1); //Currently only one initparameter :-)
		//printf("---Nr of soundfonts: %d\n",*(data +1 ));
		if (_nrOfSoundfonts == _currentlyLoadedFonts) //If there aren't any soundfonts to be loaded, let's rock now.
			initSynth();	//otherwise the load-threads will take care of running initSynth().
		break;
	default:
		//printf("FLUIDSynth::processEvent: DEFAULT\n");
		break;
	}
}

void FLUIDSynth::initSynth()
{
	//printf("Re-initializing channels\n");
	rewriteChannelSettings();
}
//-----------------------------------------------------------
// 			sendSoundFontData
//
// The data sent is stored in chunks in the following order:
// char* 		_filename (variable length)
// unsigned char	_id
// unsigned char	_channel
//-----------------------------------------------------------
void FLUIDSynth::sendSoundFontdata()
{
	int ndatalen = 2; //Reserve 2 places for command and stacksize
	unsigned char* ndata;

	//Calculate length in chars of all strings in the soundfontstack in one string
	for (std::list<FLUIDSynth_soundfont>::iterator it = _soundfontStack.begin();
		it != _soundfontStack.end();
		it++) {
		ndatalen += 1 + strlen (it->_name.c_str());
		ndatalen += MUSE_FLUID_SFDATALEN; //unsigned char for ID
	}
	ndata = new unsigned char [ndatalen]; //Where to store stuff
	*ndata = MUSE_FLUID_CLIENT_SEND_SOUNDFONTS; //The command
	*(ndata + 1) = (unsigned char) _soundfontStack.size (); //Nr of Soundfonts

	// Copy the stuff to ndatalen:
	char* chunk_start = (char *) (ndata + 2);
	int chunk_len, name_len;
	for (std::list<FLUIDSynth_soundfont>::iterator it = _soundfontStack.begin();
		it != _soundfontStack.end();
		++it)
	{
		name_len 			= strlen(it->_name.c_str()) + 1;
		chunk_len			= name_len + MUSE_FLUID_SFDATALEN;
		memcpy (chunk_start, it->_name.c_str(), name_len); //First, store the filename
		*(chunk_start + name_len) = it->_external_id; //The GUI only needs to know about the external id
		chunk_start += chunk_len;
	}
	encodeSysex (ndata, ndatalen);

	//Next, after sending the soundfont data, send channel data:
	unsigned char *chdata, *chdptr;
	int chunk_size = 2;
	int chdata_length = (chunk_size * MUSE_FLUID_MAX_NR_OF_CHANNELS) +1 ; //Command and the 2 channels * 16
	chdata = new unsigned char[chdata_length];
	chdata[0] = MUSE_FLUID_CLIENT_SEND_CHANNELINFO;
	chdptr = (chdata + 1);
	for (int i=0; i<MUSE_FLUID_MAX_NR_OF_CHANNELS; i++)
	{
		*(chdptr   ) = getFontExternalIdByChannel(i); //Font external id
		*(chdptr +1) = i; //Channel nr
		chdptr += chunk_size;
	}
	encodeSysex(chdata, chdata_length);
}


void FLUIDSynth::sendSysexError (const char * errorMessage) {
	int len = 2 + strlen (errorMessage);
	unsigned char * data = new unsigned char [len];
	*data = MUSE_FLUID_CLIENT_SEND_ERROR;
	memcpy (data + 1, errorMessage, len - 1);
	encodeSysex (data, len);
	delete data;
}


//---------------------------------------------------------
//   fontLoad
//    helper thread to load soundfont in the
//    background
//---------------------------------------------------------

static void* fontLoadThread(void* t) {
	//Init vars
	Helper* 			h	 		= (Helper*) t;
	FLUIDSynth* 		fptr 			= h->fptr;
	std::string 		filename 		= h->filename;
	pthread_mutex_t*	sfloader_mutex		= &(fptr->_sfloader_mutex);

	//Let only one loadThread have access to the fluidsynth-object at the time
	pthread_mutex_lock(sfloader_mutex);
	int rv = fluid_synth_sfload(fptr->_fluidsynth, filename.c_str(), 1);

	if (rv ==-1) {
			std::cerr << DEBUG_ARGS << "error loading soundfont: " << fluid_synth_error(fptr->_fluidsynth) << std::endl;
			fptr->sendSysexError (fluid_synth_error(fptr->_fluidsynth));

			//Unlock the mutex, or else we might be stuck here forever...
			pthread_mutex_unlock(sfloader_mutex);
			delete h;
			pthread_exit(0);
	}
	//printf("Soundfont %s loaded, index %d\n", filename.c_str(), rv);
	FLUIDSynth_soundfont font;
	font._filename 		= filename;
	font._internal_id		= rv;
	if (h->id == MUSE_FLUID_UNSPECIFIED_ID)
		font._external_id		= fptr->getNextAvailableExternalId();
	else
		font._external_id		= h->id;
	//printf("Font has external id: %d int id:%d\n", font._external_id, font._internal_id);

	//Strip off the filename
	QString temp = QString(filename);
	QString name = temp.right(temp.length() - temp.findRev('/',-1) - 1);
	name = name.left(name.length()-4); //Strip off ".sf2"
	font._name = name.ascii();
	fptr->_soundfontStack.push_front (font);
	fptr->_currentlyLoadedFonts++;
	//Cleanup & unlock:
	pthread_mutex_unlock(sfloader_mutex);
	delete h;

	//printf("Currently loaded fonts: %d Nr of soundfonts: %d\n",fptr->_currentlyLoadedFonts, fptr->_nrOfSoundfonts);
	//Check whether this was the last font or not. If so, run initSynth();
	if (fptr->_nrOfSoundfonts <= fptr->_currentlyLoadedFonts)
		fptr->initSynth();

	fptr->sendSoundFontdata(); //This should update the data in the GUI-window.
	pthread_exit(0);
}

//-----------------------------------
// pushSoundfont - load a soundfont
//-----------------------------------

bool FLUIDSynth::pushSoundfont (const std::string& filename, int external_id) {
	pthread_attr_t* attributes = (pthread_attr_t*) malloc(sizeof(pthread_attr_t));
	pthread_attr_init(attributes);
	pthread_attr_setdetachstate(attributes, PTHREAD_CREATE_DETACHED);

	Helper* helper		= new Helper;
	helper->fptr 		= this;
	helper->filename 		= filename;
	helper->id			= external_id;

	if (pthread_create(&fontThread, attributes, ::fontLoadThread, (void*) helper))
			perror("creating thread failed:");

	pthread_attr_destroy(attributes);
	return true;
}


bool FLUIDSynth::popSoundfont (int ext_id)
{
	bool success = false;
	int int_id = getFontInternalIdByExtId(ext_id);

	if (int_id == MUSE_FLUID_UNSPECIFIED_FONT) {
		std::cerr << DEBUG_ARGS << "Internal error! Request for deletion of Soundfont that is not registered!" << std::endl;
	}
	else
	{
		//Try to unload soundfont
		int err = fluid_synth_sfunload(_fluidsynth, int_id, 0);
		if (err != -1)
		{//Success
			//Check all channels that the font is used in
			for (int i=0; i<MUSE_FLUID_MAX_NR_OF_CHANNELS;  i++)
			{
				//Set them to unspecified and reset preset settings
				if (getFontInternalIdByChannel(i) == int_id)
				{
					setFontInternalIdByChannel(i, MUSE_FLUID_UNSPECIFIED_FONT);
					setFontExternalIdByChannel(i, MUSE_FLUID_UNSPECIFIED_FONT);
					setChannelPreset(MUSE_FLUID_UNSPECIFIED_PRESET, i);
				}
			}
			//Remove it from soundfont stack
			for (std::list<FLUIDSynth_soundfont>::iterator it =_soundfontStack.begin(); it !=_soundfontStack.end(); it++)
			{
				if (it->_internal_id == int_id)
				{
					_soundfontStack.erase(it);
					break;
				}
			}
			//Resend fontdata & re-initialize
			sendSoundFontdata();
			rewriteChannelSettings();
			success = true;
		}
		else //OK, there was trouble
		{
			std::cerr << DEBUG_ARGS << "Error unloading soundfont!" << fluid_synth_error(_fluidsynth) << std::endl;
		}
	}
	return success;
}

void FLUIDSynth::rewriteChannelSettings()
{
	//Walk through the channels, remap internal ID:s to external ID:s (something that actually only needs to be done at
	//startup, since the fonts aren't loaded yet at that time and it isn't possible to give them a correct internal id
	//since they don't have any at that time, this can probably be fixed in a smarter way (but it works..))
	for (int i=0; i<MUSE_FLUID_MAX_NR_OF_CHANNELS; i++)
	{
		int ext_id = getFontExternalIdByChannel(i);
		if (ext_id != MUSE_FLUID_UNSPECIFIED_FONT) //Check if ext_id is set to any sane font
			_fluidChannels[i].setInternalFontId(getFontInternalIdByExtId(ext_id));//if so, get value from the stack
		else
			_fluidChannels[i].setInternalFontId(MUSE_FLUID_UNSPECIFIED_FONT); //if not, set it to unspecified
	}
	//Assign correct presets to all channels
	for (int i=0; i<MUSE_FLUID_MAX_NR_OF_CHANNELS; i++)
	{
		int preset = getChannelPreset(i);
		int int_id = getFontInternalIdByChannel(i);
		//printf("Channel %d, font int-id %d ext_id %d, preset %d\n",i, int_id, getFontExternalIdByChannel(i), preset);
		if (!(preset == MUSE_FLUID_UNSPECIFIED_PRESET || int_id == MUSE_FLUID_UNSPECIFIED_FONT))
		{
			int rv = fluid_synth_program_select(_fluidsynth, i, int_id, 0, preset);
			if (rv)
				std::cerr << DEBUG_ARGS << "Error changing preset! " << fluid_synth_error(_fluidsynth) << std::endl;
		}
	}
}


void FLUIDSynth::setReverb () {
  int i = (int) _reverbParameters.getParameter (std::string("on"));
  fluid_synth_set_reverb_on(_fluidsynth, i);
  if (i) {
    fluid_synth_set_reverb (_fluidsynth,
                           _reverbParameters.getParameter (std::string("roomsize")),
                           _reverbParameters.getParameter (std::string("damping")),
                           _reverbParameters.getParameter (std::string("width")),
                           _reverbParameters.getParameter (std::string("level")));
  }
}

void FLUIDSynth::setChorus ()
{
	int i = (int) _chorusParameters.getParameter (std::string("on"));
	fluid_synth_set_chorus_on(_fluidsynth, i);
	if (i) {
		fluid_synth_set_chorus (_fluidsynth,
						(int) _chorusParameters.getParameter (std::string("number")),
						_chorusParameters.getParameter (std::string("level")),
						_chorusParameters.getParameter (std::string("speed")),
						_chorusParameters.getParameter (std::string("depth")),
						(int) _chorusParameters.getParameter (std::string("type")));
	}
}

void FLUIDSynth::setGain (double gain) {
	_gain = gain;
	fluid_synth_set_gain (_fluidsynth, (float) gain);
}

void FLUIDSynth::sfChannelChange(unsigned char font_id, unsigned char channel)
{
	//printf("Setting channel %d to font with extid %d intid %d\n",channel, font_id, getFontInternalIdByExtId(font_id));
	_fluidChannels[channel].setExternalFontId(font_id);
	_fluidChannels[channel].setInternalFontId(getFontInternalIdByExtId(font_id));
}
//-­-----------------------------------------------------------------------------------------
// getMidiInitEvent
//
// Is called a number of times when the project is saved. Should take care of delivering the
// midi events that will reinitialize the synth when project is loaded again.
// This is very messy, but that's probably because there is no standard way of dealing with
// values of different kinds.
//--------------------------------------------------------------------------------------------
int FLUIDSynth::getMidiInitEvent(int id, RawMidiEvent* ev)
{
	int bufferlen; //length of the data to be wrapped as sysex
	unsigned char *buffer; //the data
	int nrofsoundfonts = _soundfontStack.size();

	//If there are no soundfonts loaded and this is the first run, go for the init-params directly
	if (nrofsoundfonts == 0 && id == 0)
		id = 1;

	//Sysexes with init-params for chorus, reverb etc.
	if (id >= nrofsoundfonts)
	{
		int curparam 	= id - nrofsoundfonts;
		if (nrofsoundfonts > 0) //This is really ugly but works
			curparam = id - nrofsoundfonts + 1;

		if (curparam < 12 + MUSE_FLUID_MAX_NR_OF_CHANNELS)
		{
			if (curparam < 17)
			{
				//Deal with the channels:
				unsigned char channel = (unsigned char) curparam-1;
				unsigned char ext_id  = (unsigned char) getFontExternalIdByChannel(channel);
				unsigned int  preset  = getChannelPreset(channel);
				bufferlen		= 3*sizeof(unsigned char) + sizeof(unsigned int);
				buffer 		= new unsigned char[bufferlen];
				buffer[0] 		= MUSE_FLUID_CLIENT_RESTORE_CHANNELDATA;
				buffer[1]		= ext_id; //Store External Id
				buffer[2]		= channel; //Channel nr
				unsigned int *preset_ptr = (unsigned int*) &buffer[3];
				*preset_ptr		= preset; //Get preset nr
				//printf("Storing channel %d, ext_id %d preset %d\n",channel,ext_id,*preset_ptr);
			}
			else //Deal with various reverb & chorus
			{
				curparam-=MUSE_FLUID_MAX_NR_OF_CHANNELS;
				std::string param_string;
				double	param_value;
				int		FLUIDSynth_parameter;

				if (curparam < 6)
					FLUIDSynth_parameter = MUSE_FLUID_PARAMETER_REVERB;
				else
					FLUIDSynth_parameter = MUSE_FLUID_PARAMETER_CHORUS;

				switch (curparam)
				{						// REVERB PARAMETERS
					case 1:
						param_string 		= std::string("level");
						param_value			= _reverbParameters.getParameter(param_string);
						//printf("Level value: %f\n",param_value);
						break;
					case 2:
						param_string		= std::string("width");
						param_value			= _reverbParameters.getParameter(param_string);
						break;
					case 3:
						param_string		= std::string("damping");
						param_value			= _reverbParameters.getParameter(param_string);
						break;
					case 4:
						param_string		= std::string("roomsize");
						param_value			= _reverbParameters.getParameter(param_string);
						break;
					case 5:
						param_string		= std::string("on");
						param_value			= _reverbParameters.getParameter(param_string);
						break;
					case 6:				// CHORUS PARAMETERS
						param_string		= std::string("number");
						param_value			= _chorusParameters.getParameter(param_string);
						break;
					case 7:
						param_string		= std::string("level");
						param_value			= _chorusParameters.getParameter(param_string);
						break;
					case 8:
						param_string		= std::string("speed");
						param_value			= _chorusParameters.getParameter(param_string);
						break;
					case 9:
						param_string		= std::string("depth");
						param_value			= _chorusParameters.getParameter(param_string);
						break;
					case 10:
						param_string		= std::string("type");
						param_value			= _chorusParameters.getParameter(param_string);
						break;
					case 11:
						param_string		= std::string("on");
						param_value			= _chorusParameters.getParameter(param_string);
						break;
					default:
						std::cerr << DEBUG_ARGS<< "Error while storing! Won't save more parameters." << std::endl;
						return 0;
						break;
				}
				//printf("param: %s value: %f\n", param_string.c_str(), param_value);
				int param_len	= strlen (param_string.c_str()) + 1;
				bufferlen		= 2*sizeof(unsigned char) + sizeof(double) + param_len;
				buffer 		= new unsigned char[bufferlen];
				buffer[0] 		= MUSE_FLUID_GUI_REQ_FXPARAMETER_SET; //Request
				buffer[1]		= FLUIDSynth_parameter;			//Reverb or Chorus
				strcpy((char*) &(buffer[2]),param_string.c_str());
				double *d = (double *) (buffer + param_len + 2); //Get adress to location of data
				*d = param_value;
			}//end chorus
		}// end: if curparam <...
		else //curparam > ...
		{	//Create various parameters that are not related to reverb and chorus.
			curparam = curparam - (11 + MUSE_FLUID_MAX_NR_OF_CHANNELS);
			switch (curparam)
			{
				case 1: //GAIN parameters
					bufferlen = sizeof(unsigned char) + sizeof (double);
					buffer = new unsigned char[bufferlen];
					buffer[0] = MUSE_FLUID_GAIN_SET;
					double *d2;
					d2 = (double*) (buffer + 1);
					*d2 = _gain;
					break;
				case 2:
					{
					int dirstrlen = strlen(_lastDir.c_str());
					bufferlen = 2 + dirstrlen;
					buffer = new unsigned char [bufferlen];
					buffer[0] = MUSE_FLUID_CLIENT_LASTDIR_CHANGE;
					if (dirstrlen > 0)
						memcpy ((buffer + 1), _lastDir.c_str(), dirstrlen +1);
					else
						buffer[1] = MUSE_FLUID_UNSPECIFIED_LASTDIR;
					break;
					}
				case 3: //To get the client to know the last dir on startup as well
					{
					int dirstrlen = strlen(_lastDir.c_str());
					bufferlen = 2 + dirstrlen;
					buffer = new unsigned char [bufferlen];
					buffer[0] = MUSE_FLUID_GUI_LASTDIR_CHANGE;
					if (dirstrlen > 0)
						memcpy ((buffer + 1), _lastDir.c_str(), dirstrlen +1);
					else
						buffer[1] = MUSE_FLUID_UNSPECIFIED_LASTDIR;
					break;

					}
				case 4: //INIT parameters
					//printf("Storing init parameters. Soundfontstack size: %d\n",_soundfontStack.size());
					bufferlen = 2*sizeof(unsigned char);
					buffer = new unsigned char[bufferlen];
					buffer[0] = MUSE_FLUID_CLIENT_INIT_PARAMS;
					buffer[1] = _soundfontStack.size(); //Number of fonts that have to be loaded
					break;
				default:
					return 0;
					break;
			}
		}
	}
	else 		//Deal with the soundfont stack:
	{
		// Create a SOUNDFONT_PUSH-event for a soundfont
		std::list<FLUIDSynth_soundfont>::iterator it = _soundfontStack.begin();
		for(int i = 0; i < id;i++)
			it++; // make it point to the right one

		bufferlen = 4 + strlen( (*it)._filename.c_str());
		buffer = new unsigned char[bufferlen];
		buffer[0] = MUSE_FLUID_SOUNDFONT_PUSH;
		buffer[1] = it->_external_id;
		strcpy((char*)(buffer+2), (*it)._filename.c_str());
	}

	//Wrap it up in a sysex
	const unsigned char *p = buffer;
	int ndatalen = (bufferlen*2);
	unsigned char *ndata = new unsigned char[ndatalen]; //The data to put in the event
	for (int i = 0; i < bufferlen; ++p, ++i)
	{
		ndata[(i*2)] = (*p >> 4) & 0xf;
		ndata[(i*2)+1] = (*p & 0xf);
	}

	delete buffer;
	ev->setType(SND_SEQ_EVENT_SYSEX);
	ev->setData(ndata);
	ev->setDataLen(ndatalen);
	return ++id;
}

int FLUIDSynth::getNextAvailableExternalId()
{
	unsigned char place[MUSE_FLUID_MAX_NR_OF_CHANNELS];
	for(int i=0; i<MUSE_FLUID_MAX_NR_OF_CHANNELS; i++)
		place[i] = 0;
	for (std::list<FLUIDSynth_soundfont>::iterator it = _soundfontStack.begin(); it != _soundfontStack.end(); it++)
		place[it->_external_id] = 1;
	int i=0;
	while (i < MUSE_FLUID_MAX_NR_OF_CHANNELS && place[i] == 1)
		i++;

	return i;
}

/*** Muse Plugin stuff ***/

static Mess * inst (const char* name) {
  return new FLUIDSynth (name);
}

void initMess() {
  Mess::initMess(1225, "fluidsynth",
    "Bob Ham (node@users.sourceforge.net)",
    "fluidsynth soft synth", "sucks", 2, inst);
}


int FLUIDSynth::getFontInternalIdByExtId(int ext_id)
{
	std::list<FLUIDSynth_soundfont>::iterator it;
	for (it=_soundfontStack.begin(); it !=_soundfontStack.end(); it++)
	{
		if (it->_external_id == ext_id)
			return (int) it->_internal_id;
	}
	return MUSE_FLUID_UNSPECIFIED_FONT;
}
