/*
    TiMidity++ -- MIDI to WAVE converter and player
    Copyright (C) 1999-2002 Masanao Izumo <mo@goice.co.jp>
    Copyright (C) 1995 Tuukka Toivonen <tt@cgs.fi>

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

   instrum.h

   */

#ifndef ___INSTRUM_H_
#define ___INSTRUM_H_

#include <string>
#include "common.h"
#include "sysdep.h"
#include "sffile.h"
#include "sflayer.h"
#include "sfitem.h"

class FSoundFontReader;

namespace TimidityPlus
{

enum
{
	READ_CONFIG_SUCCESS = 0,
	READ_CONFIG_ERROR = 1,
	READ_CONFIG_RECURSION = 2, /* Too much recursion */
	READ_CONFIG_FILE_NOT_FOUND = 3, /* Returned only w. allow_missing_file */
};


struct Sample
{
	splen_t
		loop_start, loop_end, data_length;
	int32_t
		sample_rate, low_freq, high_freq, root_freq;
	int8_t panning, note_to_use;
	int32_t
		envelope_rate[6], envelope_offset[6],
		modenv_rate[6], modenv_offset[6];
	double
		volume;
	sample_t
		*data;
	int32_t
		tremolo_sweep_increment, tremolo_phase_increment,
		vibrato_sweep_increment, vibrato_control_ratio;
	int16_t
		tremolo_depth;
	int16_t vibrato_depth;
	uint8_t
		modes, data_alloced,
		low_vel, high_vel;
	int32_t cutoff_freq;	/* in Hz, [1, 20000] */
	int16_t resonance;	/* in centibels, [0, 960] */
	/* in cents, [-12000, 12000] */
	int16_t tremolo_to_pitch, tremolo_to_fc, modenv_to_pitch, modenv_to_fc,
		envelope_keyf[6], envelope_velf[6], modenv_keyf[6], modenv_velf[6],
		vel_to_fc, key_to_fc;
	int16_t vel_to_resonance;	/* in centibels, [-960, 960] */
	int8_t envelope_velf_bpo, modenv_velf_bpo,
		key_to_fc_bpo, vel_to_fc_threshold;	/* in notes */
	int32_t vibrato_delay, tremolo_delay, envelope_delay, modenv_delay;	/* in samples */
	int16_t scale_freq;	/* in notes */
	int16_t scale_factor;	/* in 1024divs/key */
	int8_t inst_type;
	int32_t sf_sample_index, sf_sample_link;	/* for stereo SoundFont */
	uint16_t sample_type;	/* 1 = Mono, 2 = Right, 4 = Left, 8 = Linked, $8000 = ROM */
	double root_freq_detected;	/* root freq from pitch detection */
	int transpose_detected;	/* note offset from detected root */
	int chord;			/* type of chord for detected pitch */
};

/* Bits in modes: */
enum
{
	MODES_16BIT = (1 << 0),
	MODES_UNSIGNED = (1 << 1),
	MODES_LOOPING = (1 << 2),
	MODES_PINGPONG = (1 << 3),
	MODES_REVERSE = (1 << 4),
	MODES_SUSTAIN = (1 << 5),
	MODES_ENVELOPE = (1 << 6),
	MODES_CLAMPED = (1 << 7), /* ?? (for last envelope??) */

	INST_GUS = 0,
	INST_SF2 = 1,
	INST_MOD = 2,
	INST_PCM = 3,	/* %sample */

	/* sfSampleType */
	SF_SAMPLETYPE_MONO = 1,
	SF_SAMPLETYPE_RIGHT = 2,
	SF_SAMPLETYPE_LEFT = 4,
	SF_SAMPLETYPE_LINKED = 8,
	SF_SAMPLETYPE_ROM = 0x8000,
};

struct Instrument
{
	int type;
	int samples;
	Sample *sample;
	char *instname;
};

struct ToneBankElement
{
	char *name;
	char *comment;
	Instrument *instrument;
	int8_t note, pan, strip_loop, strip_envelope, strip_tail, loop_timeout,
		font_preset, font_keynote, legato, tva_level, play_note, damper_mode;
	uint8_t font_bank;
	uint8_t instype; /* 0: Normal
				1: %font
				2: %sample
				3-255: reserved
				*/
	int16_t amp;
	int16_t rnddelay;
	int tunenum;
	float *tune;
	int sclnotenum;
	int16_t *sclnote;
	int scltunenum;
	int16_t *scltune;
	int fcnum;
	int16_t *fc;
	int resonum;
	int16_t *reso;
	int trempitchnum, tremfcnum, modpitchnum, modfcnum;
	int16_t *trempitch, *tremfc, *modpitch, *modfc;
	int envratenum, envofsnum;
	int **envrate, **envofs;
	int modenvratenum, modenvofsnum;
	int **modenvrate, **modenvofs;
	int envvelfnum, envkeyfnum;
	int **envvelf, **envkeyf;
	int modenvvelfnum, modenvkeyfnum;
	int **modenvvelf, **modenvkeyf;
	int tremnum, vibnum;
	struct Quantity_ **trem, **vib;
	int16_t vel_to_fc, key_to_fc, vel_to_resonance;
	int8_t reverb_send, chorus_send, delay_send;
};

/* A hack to delay instrument loading until after reading the
	entire MIDI file. */
#define MAGIC_LOAD_INSTRUMENT ((Instrument *)(-1))
#define MAGIC_ERROR_INSTRUMENT ((Instrument *)(-2))
#define IS_MAGIC_INSTRUMENT(ip) ((ip) == MAGIC_LOAD_INSTRUMENT || (ip) == MAGIC_ERROR_INSTRUMENT)

#define DYNAMIC_INSTRUMENT_NAME ""

struct AlternateAssign
{
	/* 128 bit vector:
		* bits[(note >> 5) & 0x3] & (1 << (note & 0x1F))
		*/
	uint32_t bits[4];
	AlternateAssign* next;
};

struct ToneBank
{
	ToneBankElement tone[128];
	AlternateAssign *alt;
};

struct SpecialPatch /* To be used MIDI Module play mode */
{
	int type;
	int samples;
	Sample *sample;
	char *name;
	int32_t sample_offset;
};

enum instrument_mapID
{
	INST_NO_MAP = 0,
	SC_55_TONE_MAP,
	SC_55_DRUM_MAP,
	SC_88_TONE_MAP,
	SC_88_DRUM_MAP,
	SC_88PRO_TONE_MAP,
	SC_88PRO_DRUM_MAP,
	SC_8850_TONE_MAP,
	SC_8850_DRUM_MAP,
	XG_NORMAL_MAP,
	XG_SFX64_MAP,
	XG_SFX126_MAP,
	XG_DRUM_MAP,
	GM2_TONE_MAP,
	GM2_DRUM_MAP,
	NUM_INST_MAP
};

enum
{
	MAP_BANK_COUNT = 256,
	NSPECIAL_PATCH = 256,
	SPECIAL_PROGRAM = -1,
	MAX_MREL = 5000,
	DEFAULT_MREL = 800,
};

struct SFInsts;
struct InstList;
struct SampleList;
struct AIFFCommonChunk;
struct AIFFSoundDataChunk;
struct  SampleImporter;

class Instruments
{
	std::string configFileName;
    FSoundFontReader *sfreader;

	ToneBank standard_tonebank, standard_drumset;

	enum
	{
		INSTRUMENT_HASH_SIZE = 128,
	};

	struct InstrumentCache
	{
		char *name;
		int panning, amp, note_to_use, strip_loop, strip_envelope, strip_tail;
		Instrument *ip;
		InstrumentCache *next;
	};

	InstrumentCache *instrument_cache[INSTRUMENT_HASH_SIZE] = { nullptr };

	/* bank mapping (mapped bank) */
	struct bank_map_elem
	{
		int16_t used = 0, mapid = 0;
		int bankno = 0;
	};
	bank_map_elem map_bank[MAP_BANK_COUNT], map_drumset[MAP_BANK_COUNT];
	int map_bank_counter = 0;

	struct inst_map_elem
	{
		int set, elem, mapped;
	};

	inst_map_elem *inst_map_table[NUM_INST_MAP][128] = { { nullptr} };

	struct UserInstrument
	{
		int8_t bank;
		int8_t prog;
		int8_t source_map;
		int8_t source_bank;
		int8_t source_prog;
		int8_t vibrato_rate;
		int8_t vibrato_depth;
		int8_t cutoff_freq;
		int8_t resonance;
		int8_t env_attack;
		int8_t env_decay;
		int8_t env_release;
		int8_t vibrato_delay;
		UserInstrument *next;
	};

	UserInstrument *userinst_first = (UserInstrument *)NULL;
	UserInstrument *userinst_last = (UserInstrument *)NULL;

	struct UserDrumset {
		int8_t bank;
		int8_t prog;
		int8_t play_note;
		int8_t level;
		int8_t assign_group;
		int8_t pan;
		int8_t reverb_send_level;
		int8_t chorus_send_level;
		int8_t rx_note_off;
		int8_t rx_note_on;
		int8_t delay_send_level;
		int8_t source_map;
		int8_t source_prog;
		int8_t source_note;
		UserDrumset *next;
	};

	struct SFBags
	{
		int nbags;
		uint16_t *bag;
		int ngens;
		SFGenRec *gen;
	};

	SFBags prbags, inbags;

	UserDrumset *userdrum_first = (UserDrumset *)NULL;
	UserDrumset *userdrum_last = (UserDrumset *)NULL;

	AlternateAssign alt[2];

	/* Some functions get aggravated if not even the standard banks are available. */
	ToneBank
		*tonebank[128 + MAP_BANK_COUNT] = { &standard_tonebank },
		*drumset[128 + MAP_BANK_COUNT] = { &standard_drumset };

	Instrument *default_instrument = 0;
	SpecialPatch *special_patch[NSPECIAL_PATCH] = { nullptr };
	int default_program[MAX_CHANNELS] = { 0 };	/* This is only used for tracks that don't specify a program */

	char *default_instrument_name = nullptr;
	int progbase = 0;
	int32_t modify_release = 0;
	bool opt_sf_close_each_file = true;
	char def_instr_name[256] = { '\0' };
	SFInsts *sfrecs = nullptr;
	SFInsts *current_sfrec = nullptr;

	int last_sample_type = 0;
	int last_sample_instrument = 0;
	int last_sample_keyrange = 0;
	SampleList *last_sample_list = nullptr;

	LayerItem layer_items[SF_EOF];

	/* convert from 8bit value to fractional offset (15.15) */
	int32_t to_offset_22(int offset)
	{
		return (int32_t)offset << (7 + 15);
	}

	int32_t calc_rate_i(int diff, double msec);
	int32_t convert_envelope_rate(uint8_t rate);
	int32_t convert_envelope_offset(uint8_t offset);
	int32_t convert_tremolo_sweep(uint8_t sweep);
	int32_t convert_vibrato_sweep(uint8_t sweep, int32_t vib_control_ratio);
	int32_t convert_tremolo_rate(uint8_t rate);
	int32_t convert_vibrato_rate(uint8_t rate);
	void  reverse_data(int16_t *sp, int32_t ls, int32_t le);
	int name_hash(char *name);
	Instrument *search_instrument_cache(char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail);
	void store_instrument_cache(Instrument *ip, char *name, int panning, int amp, int note_to_use, int strip_loop, int strip_envelope, int strip_tail);
	int32_t to_rate(int rate);
	void apply_bank_parameter(Instrument *ip, ToneBankElement *tone);
	Instrument *load_gus_instrument(char *name, ToneBank *bank, int dr, int prog);
	int fill_bank(int dr, int b, int *rc);
	void free_tone_bank_list(ToneBank *tb[]);
	void free_tone_bank(void);
	void free_instrument_map(void);
	int set_default_instrument(char *name);
	void *safe_memdup(void *s, size_t size);
	void MarkInstrument(int banknum, int percussion, int instr);

	//smplfile.c
	Instrument *extract_sample_file(char *);
	int32_t convert_envelope_rate_s(uint8_t rate);
	void initialize_sample(Instrument *inst, int frames, int sample_bits, int sample_rate);
	int get_importers(const char *sample_file, int limit, SampleImporter **importers);
	int get_next_importer(char *sample_file, int start, int count, SampleImporter **importers);

	int import_wave_discriminant(char *sample_file);
	int import_wave_load(char *sample_file, Instrument *inst);
	int import_aiff_discriminant(char *sample_file);
	int import_aiff_load(char *sample_file, Instrument *inst);
	int read_AIFFCommonChunk(struct timidity_file *tf, AIFFCommonChunk *comm, int csize, int compressed);
	int read_AIFFSoundData(struct timidity_file *tf, Instrument *inst, AIFFCommonChunk *common);
	int read_AIFFSoundDataChunk(struct timidity_file *tf, AIFFSoundDataChunk *sound, int csize, int mode);

	// sndfont.cpp

	SFInsts *find_soundfont(char *sf_file);
	SFInsts *new_soundfont(char *sf_file);
	void init_sf(SFInsts *rec);
	void end_soundfont(SFInsts *rec);
	Instrument *try_load_soundfont(SFInsts *rec, int order, int bank, int preset, int keynote);
	Instrument *load_from_file(SFInsts *rec, InstList *ip);
	int is_excluded(SFInsts *rec, int bank, int preset, int keynote);
	int is_ordered(SFInsts *rec, int bank, int preset, int keynote);
	int load_font(SFInfo *sf, int pridx);
	int parse_layer(SFInfo *sf, int pridx, LayerTable *tbl, int level);
	int is_global(SFGenLayer *layer);
	void clear_table(LayerTable *tbl);
	void set_to_table(SFInfo *sf, LayerTable *tbl, SFGenLayer *lay, int level);
	void add_item_to_table(LayerTable *tbl, int oper, int amount, int level);
	void merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src);
	void init_and_merge_table(SFInfo *sf, LayerTable *dst, LayerTable *src);
	int sanity_range(LayerTable *tbl);
	int make_patch(SFInfo *sf, int pridx, LayerTable *tbl);
	void make_info(SFInfo *sf, SampleList *vp, LayerTable *tbl);
	double calc_volume(LayerTable *tbl);
	void set_sample_info(SFInfo *sf, SampleList *vp, LayerTable *tbl);
	void set_init_info(SFInfo *sf, SampleList *vp, LayerTable *tbl);
	void reset_last_sample_info(void);
	int abscent_to_Hz(int abscents);
	void set_rootkey(SFInfo *sf, SampleList *vp, LayerTable *tbl);
	void set_rootfreq(SampleList *vp);
	int32_t to_offset(int32_t offset);
	int32_t to_rate(int32_t diff, int timecent);
	int32_t calc_rate(int32_t diff, double msec);
	double to_msec(int timecent);
	int32_t calc_sustain(int sust_cB);
	void convert_volume_envelope(SampleList *vp, LayerTable *tbl);
	void convert_tremolo(SampleList *vp, LayerTable *tbl);
	void convert_vibrato(SampleList *vp, LayerTable *tbl);
	void set_envelope_parameters(SampleList *vp);

	// configfile

	int set_patchconf(const char *name, int line, ToneBank *bank, char *w[], int dr, int mapid, int bankmapfrom, int bankno);
	int strip_trailing_comment(char *string, int next_token_index);
	char *expand_variables(char *string, MBlockList *varbuf, const char *basedir);
	int set_gus_patchconf(const char *name, int line, ToneBankElement *tone, char *pat, char **opts);
	void reinit_tone_bank_element(ToneBankElement *tone);
	int set_gus_patchconf_opts(const char *name, int line, char *opts, ToneBankElement *tone);
	int copymap(int mapto, int mapfrom, int isdrum);
	void copybank(ToneBank *to, ToneBank *from, int mapid, int bankmapfrom, int bankno);

	// sffile.cpp

	int chunkid(char *id);
	int process_list(int size, SFInfo *sf, struct timidity_file *fd);
	int process_info(int size, SFInfo *sf, struct timidity_file *fd);
	int process_sdta(int size, SFInfo *sf, struct timidity_file *fd);
	int process_pdta(int size, SFInfo *sf, struct timidity_file *fd);
	void load_sample_names(int size, SFInfo *sf, struct timidity_file *fd);
	void load_preset_header(int size, SFInfo *sf, struct timidity_file *fd);
	void load_inst_header(int size, SFInfo *sf, struct timidity_file *fd);
	void load_bag(int size, SFBags *bagp, struct timidity_file *fd);
	void load_gen(int size, SFBags *bagp, struct timidity_file *fd);
	void load_sample_info(int size, SFInfo *sf, struct timidity_file *fd);
	void convert_layers(SFInfo *sf);
	void generate_layers(SFHeader *hdr, SFHeader *next, SFBags *bags);
	void free_layer(SFHeader *hdr);
	int load_soundfont(SFInfo *sf, struct timidity_file *fd);
	void free_soundfont(SFInfo *sf);
	void correct_samples(SFInfo *sf);


public:

	Instruments();
	bool load(const char *config);
	~Instruments();

	const ToneBank *toneBank(int i) const
	{
		return tonebank[i];
	}

	int defaultProgram(int i) const
	{
		return default_program[i];
	}

	const ToneBank *drumSet(int i) const
	{
		return drumset[i];
	}

	const SpecialPatch *specialPatch(int i) const
	{
		return special_patch[i];
	}

	void setSpecialPatchOffset(int i, int32_t ofs)
	{
		special_patch[i]->sample_offset = ofs;
	}
	Instrument *defaultInstrument() const
	{
		return default_instrument;
	}

	/* instrum.c */
	int load_missing_instruments(int *rc);
	void free_instruments(int reload_default_inst);
	void free_special_patch(int id);
	void clear_magic_instruments(void);
	Instrument *load_instrument(int dr, int b, int prog);
	int find_instrument_map_bank(int dr, int map, int bk);
	int alloc_instrument_map_bank(int dr, int map, int bk);
	void alloc_instrument_bank(int dr, int bankset);
	int instrument_map(int mapID, int *set_in_out, int *elem_in_out) const;
	void set_instrument_map(int mapID, int set_from, int elem_from, int set_to, int elem_to);
	AlternateAssign *add_altassign_string(AlternateAssign *old, char **params, int n);
	AlternateAssign *find_altassign(AlternateAssign *altassign, int note);
	void copy_tone_bank_element(ToneBankElement *elm, const ToneBankElement *src);
	void free_tone_bank_element(ToneBankElement *elm);
	void free_instrument(Instrument *ip);
	void squash_sample_16to8(Sample *sp);
	Instrument *play_midi_load_instrument(int dr, int bk, int prog, bool *pLoad_success);
	void recompute_userinst(int bank, int prog);
	Instrument *recompute_userdrum(int bank, int prog);
	UserInstrument *get_userinst(int bank, int prog);
	UserDrumset *get_userdrum(int bank, int prog);
	void recompute_userdrum_altassign(int bank, int group);
	/*! initialize GS user drumset. */
	void init_userdrum();
	void free_userdrum();
	void init_userinst() { free_userinst(); }
	void free_userinst();

	void mark_instrument(int newbank, int newprog)
	{
		if (!(tonebank[newbank]->tone[newprog].instrument))
			tonebank[newbank]->tone[newprog].instrument =
			MAGIC_LOAD_INSTRUMENT;
	}

	void mark_drumset(int newbank, int newprog)
	{
		if (!(drumset[newbank]->tone[newprog].instrument))
			drumset[newbank]->tone[newprog].instrument =
			MAGIC_LOAD_INSTRUMENT;
	}

	/* sndfont.c */
	void add_soundfont(char *sf_file, int sf_order, int cutoff_allowed, int resonance_allowed, int amp);
	void remove_soundfont(char *sf_file);
	void init_load_soundfont(void);
	Instrument *load_soundfont_inst(int order, int bank, int preset, int keynote);
	Instrument *extract_soundfont(char *sf_file, int bank, int preset, int keynote);
	int exclude_soundfont(int bank, int preset, int keynote);
	int order_soundfont(int bank, int preset, int keynote, int order);
	char *soundfont_preset_name(int bank, int preset, int keynote, char **sndfile);
	void free_soundfonts(void);
	void PrecacheInstruments(const uint16_t *instruments, int count);


	int read_config_file(const char *name, int self, int allow_missing_file);

	void set_default_instrument()
	{
		set_default_instrument(def_instr_name);
	}

	bool checkConfig(const char *filename)
	{
		return configFileName.compare(filename) == 0;
	}

};


}
#endif /* ___INSTRUM_H_ */
