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

#ifndef __MUSE_FLUIDSYNTI_H__
#define __MUSE_FLUIDSYNTI_H__

#include <string>
#include <list>
#include <map>
#include <fluidsynth.h>
#include <pthread.h>
#include "fluidsynthgui.h"
#include "mess.h"
#include "debug.h"

class FLUIDSynth_soundfont
{
public:
	FLUIDSynth_soundfont(): _external_id(0), _internal_id(0) {};

	std::string 	_filename;
	std::string		_name;
	unsigned char	_external_id; //The ID the gui knows about
	unsigned char	_internal_id; //The ID for fluidsynth
};

// FluidSynthChannel is used to map different soundfonts to different fluid-channels
// This is to be able to select different presets from specific soundfonts, since
// Fluidsynth has a quite strange way of dealing with fontloading and channels
// We also need this since getFirstPatch and getNextPatch only tells us which channel is
// used, so this works as a connection between soundfonts and fluid-channels (one channel
// can only have one soundfont, but one soundfont can have many channels)
class FluidSynthChannel
{
private:
	unsigned char _soundfont_internal_id;
	unsigned char _soundfont_external_id;
	unsigned int  _preset_id;

public:
	FluidSynthChannel():
		_soundfont_internal_id(0),
		_soundfont_external_id(0),
		_preset_id(MUSE_FLUID_UNSPECIFIED_PRESET) { };
	void setInternalFontId(unsigned char int_id)	{ _soundfont_internal_id = int_id; }
	void setExternalFontId(unsigned char ext_id)	{ _soundfont_external_id = ext_id; }
	void setPresetId(unsigned char preset_id)		{ _preset_id = preset_id; }
	unsigned char getInternalFontId() const		{ return _soundfont_internal_id; }
	unsigned char getExternalFontId() const		{ return _soundfont_external_id; }
	unsigned char getPresetId()				{ return _preset_id; }
};

class FLUIDParameterSet {
private:
  std::map <std::string, std::pair <bool, double> > _parameters;
public:
  FLUIDParameterSet () {}
  FLUIDParameterSet (const FLUIDParameterSet&);
  void setParameter (std::string param, int val) {
    _parameters[param] = std::pair<bool, double> (true, ((double)val)/128.0);
  }
  void setParameter (std::string param, double val) {
    _parameters[param] = std::pair<bool, double> (true, val);
  }
  double getParameter (std::string param) { return _parameters[param].second; }
  bool getParameterModified (std::string param) { return _parameters[param].first; }
};


class FLUIDSynth : public Mess {
private:
	double _gain;
	FLUIDParameterSet _reverbParameters;
	FLUIDParameterSet _chorusParameters;
	pthread_t fontThread;
	void setReverb ();
	void setChorus ();
	void setGain (double gain);
	virtual void write (int, float **, int);
	void decodeSysex (const unsigned char *, int);
	void processSysex (const unsigned char *, int);
	void encodeSysex(const unsigned char *, int);
	const MidiPatch * getFirstPatch (int channel) const;
	void sfChannelChange(unsigned char font_id, unsigned char channel);
	void rewriteChannelSettings(); //used because fluidsynth does some very nasty things when loading a font!
	std::string _lastDir;



public:
	FLUIDSynth (const char *);
	virtual ~FLUIDSynth ();
	virtual bool init ();
	virtual void processEvent (MEvent *);
	virtual const char * getPatchName (int, int, int, int, MType);
	virtual const MidiPatch * getNextPatch (int, const MidiPatch *) const;
	virtual int getMidiInitEvent(int id, RawMidiEvent* ev);
	bool pushSoundfont (const std::string&, int ext_id);
	bool popSoundfont (int ext_id);
	void sendSysexError (const char *);
	void sendSoundFontdata();
	void initSynth();
	int getNextAvailableExternalId();
	fluid_synth_t * _fluidsynth;
	pthread_mutex_t _sfloader_mutex;
	int _nrOfSoundfonts;
	int _currentlyLoadedFonts; //To know whether or not to run the init-parameters

	std::list<FLUIDSynth_soundfont> _soundfontStack;
	FluidSynthChannel _fluidChannels[MUSE_FLUID_MAX_NR_OF_CHANNELS];

int getFontInternalIdByChannel(int channel) const { return (int) _fluidChannels[channel].getInternalFontId(); }
int getFontExternalIdByChannel(int channel) { return (int) _fluidChannels[channel].getExternalFontId(); }
void setFontInternalIdByChannel(int channel, int int_id) { _fluidChannels[channel].setInternalFontId(int_id); }
void setFontExternalIdByChannel(int channel, int ext_id) { _fluidChannels[channel].setExternalFontId(ext_id); }
unsigned char getChannelPreset(int channel) { return (int) _fluidChannels[channel].getPresetId(); }
void setChannelPreset(int preset, unsigned char channel) { _fluidChannels[channel].setPresetId(preset); }
int getFontInternalIdByExtId	(int channel);

};

struct Helper //Only used to pass parameters when calling the loading thread
{
	FLUIDSynth* 	fptr;
	std::string		filename;
	int			id;
};
#endif /* __MUSE_FLUIDSYNTI_H__ */
