/* GSI - general sound interface
 *
 * gsisynth.h
 *
 * By W.H.Scholten 1998 - 2000
 *
 * This file is released to the public domain.
 */


#ifndef _GSI__gsisynth__gsisynth_h
#define _GSI__gsisynth__gsisynth_h

#ifdef _cplusplus
extern "C" {
#endif /* __cplusplus */

#include <gsi/whs_types.h>
#include <gsi/device.h>


/*---------------------------------------------------------------------------*
 *      synth device abstraction.
 *---------------------------------------------------------------------------*/

/* status/errors: */
#define GSISYNTH_OK                              0
#define GSISYNTH_ERROR_OUT_OF_MEMORY            -2
#define GSISYNTH_ERROR_NULL_DEREFERENCE         -3
#define GSISYNTH_ERROR_BAD_INSTRUMENT           -4
#define GSISYNTH_ERROR_BAD_INSTRUMENTFILE       -5


#define GSISYNTH_HW_S struct gsi_synth_device *hw, struct gsi_synth_devicedata *hw_status

typedef struct gsi_synth_devicedata {
	int  dev; /* weg ? */
	int  fd;
	int  fd_ct;
	int  flags; /* e.g. use opl3 mode etc */
	int  instrmap[256];
	unsigned char instrtype[256];
//OBSOLETE	void *voices;
//OBSOLETE	void *synth_specific; /* specific data for a given synth type */
} gsi_synth_devicedata_t;

typedef struct gsi_synth_device {
	char *device; /* the device to open, e.g. /dev/sequencer */
	char id[32]; /* identification string, e.g. sw/midi, or hw/midi, hw/mod, sw/mp3 etc */

	int  (*init) (GSISYNTH_HW_S /*, int modus*/);
	int  (*close) (GSISYNTH_HW_S);
	int  (*write) (GSISYNTH_HW_S);
	void (*set_timeout) (GSISYNTH_HW_S, struct timeval *tv); /* the synth can let the program know how much time it thinks before the current data will be depleted (prevent songs from stopping, that happens..) */
	int  (*buffer_status) (GSISYNTH_HW_S);

	int  (*load_instrument) (GSISYNTH_HW_S, int pgm);

	//  rename: load_instrument_from_data ?
	int  (*write_instrument) (GSISYNTH_HW_S, int pgm, int type, int size, char *data);
	int  (*clear_instrument) (GSISYNTH_HW_S, int pgm);
	int  (*clear_instruments) (GSISYNTH_HW_S);
	int  (*sizeof_default_instrument) (GSISYNTH_HW_S, int pgm);
	int  (*reset) (GSISYNTH_HW_S);

	int  (*instrument_memory_available) (GSISYNTH_HW_S);

	int  total_instrument_memory;
	/* play data */
	int  synth_rate;

	/* Hardware info: */
	int  type;
	int  num_voices; /* VIRTUALIZE (can change for software synth) */
	int  listno; /* enumerator of devices for the given api */
} gsi_synth_device_t;




#define _SONG_NONE     0 /* _SONG_NO_STATUS ? */
#define _SONG_PLAYING  1
#define _SONG_PAUSED   2

/* Synthesizer status flags: */
#define USE_OPL3       1


/* available synthesizer hardware; if multiple, use highest numbered one found
 * (at the moment GUS) by default. */
#define MIDI_HW					-1 /* should make this 254? */
#define SYNTH_HW_NONE			0
#define SYNTH_HW_FM				1
#define SYNTH_HW_OPL			2
#define SYNTH_HW_GUS			3
#define SYNTH_HW_AWE32			4
#define SYNTH_PCM_EMULATION   255


/* Which channels do we allow to play? */
#define IS_PLAYING(x)	(mdata->midi_chn_mask & (1 << x))

/* Default mask for percussion instrument channels. Bit 0=channel 1,
 * bit 9=channel 10 etc. => 0x200=channel 10. */
#define DEFAULT_PERCUSSION_CHANNELS  0x0200

/* A few convenience macro's */
#define IS_PERC(x)     (mdata->midi_percussion_chn & (1 << x))

#define IS_MIDI(chn)   (synth_devices[mdata->midi_channel_output[chn]].type == MIDI_HW)
#define IS_FM(chn)     (synth_devices[mdata->midi_channel_output[chn]].type == SYNTH_HW_OPL)
#define IS_GUS(chn)    (synth_devices[mdata->midi_channel_output[chn]].type == SYNTH_HW_GUS)
#define IS_AWE(chn)    (synth_devices[mdata->midi_channel_output[chn]].type == SYNTH_HW_AWE32)
#define IS_PCM(chn)    (synth_devices[mdata->midi_channel_output[chn]].type == SYNTH_PCM_EMULATION)


/* Structs for the current playing status */
struct channel_status {
	int  program;
	int  bender;
	int  bender_range;
	int  controller[128];
	int  pressure;
};

struct voice_status {
	int  note;
	int  channel;
	int  timestamp;
	int  available; /* playing? */
};



typedef struct gsi_midi_status {
	struct voice_status *voices; /* to save the current status on switch away. */


	int  midi_chn_mask;     /* = 0xffff */
	/* only channels with a bit set are played, for interactive music programs */
	int  midi_percussion_chn; /* = PERCUSSION_CHANNELS*/
	/*^ mask of channels that play percussion, default=channel 10 (bit 9 set)
	 *  use channel masks for each device ... ? */

	int  midi_channel_output[16];
	/* -1=external midi, >=0 is a device in synth_devices[] */


	/* song status: */
	int  midi_note_vel[16][128];
	struct channel_status midi_channel[16];
	int  reverb, chorus;

	unsigned int midi_default_tempo; /* only initialized once, make user selectable? */
	double time_of_last_midi_command;

	int  midi_type;         /* which type of midi interpretation ? _MIDI_GM, _MIDI_GS, _MIDI_MT32 */
	int  midi_reverb;       /* should be user selectable */
	int  midi_chorus;       /* should be user selectable */
	float song_speed;       /* =1.0, playback speed */
	/* CUSTOM INSTRUMENTS: make a list of what was loaded so we can reload. */

	/* Override volumes and pgm's (these will be reset when loading a MIDI or
	 * when MIDI_RESET is given */
	int  midi_force_chn_volume[16]; /* value = volume+1 or 0 for no forced replacement */
	int  midi_force_chn_pgm[16];    /* value = pgm+1 or 0 for no forced replacement */
} gsi_midi_status_t;


enum { _GSI_SONG_MIDI, _GSI_SONG_HMP, _GSI_SONG_MUS, _GSI_SONG_MP3, _GSI_SONG_MOD } gsis_song_l;

typedef struct gsi_song_playstatus {
	int  song_type; /* MIDI, MOD, MP3 etc. */
	void *song; /* a struct that corresponds to the type in type and contains the synth status, voices etc. */
	void *synth; /* synth status, or put in song above? */

	/* timing: */
	int  start_synth_ticks;
	struct timeval start_tv;

	double song_time; /* current time into song */
	int  song_play_status; /* = _SONG_NONE */
	int  song_play_ct;      /* number of times left to play + 1 when playing */
	int  synth_type;        /* the requested device: synth, ext midi, pcm emulation */
	int  song_events_done; /* True if there are no more events (but there may still be output in the buffer!) */

	struct gsi_song *song_data;
	/* all data from the song, with tracks etc. */
	struct gsi_song_status *song_status;
	/* the (playing/reading) status of the song */

	int  dev; /* synth device */
	gsi_synth_device_t *hw;
	gsi_synth_devicedata_t *hw_status;
} gsi_song_playstatus_t;


/* Device attaching: */
void gsikpmidi_attach (gsi_synth_device_t synth_devices[]);
void gsisynth_attach (gsi_synth_device_t synth_devices[]);


/* *** Low level synth functions */
int  gsisynth_init (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status, int synth_type);
int  gsisynth_open (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status);
int  gsisynth_close (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status);

void gsisynth_seq_reset (gsi_song_playstatus_t *d);
void gsisynth_seq_pause (gsi_song_playstatus_t *d);
void gsisynth_seq_continue (gsi_song_playstatus_t *d);
int  gsisynth_seq_set_instrument (gsi_song_playstatus_t *d, int chn, int pgm);
void gsisynth_seq_stop_note (gsi_song_playstatus_t *d, int chn, int note, int vel);
void gsisynth_seq_start_note (gsi_song_playstatus_t *d, int chn, int note, int vel);
void gsisynth_seq_key_pressure (gsi_song_playstatus_t *d, int chn, int note, int vel);
void gsisynth_seq_control (gsi_song_playstatus_t *d, int chn, int p1, int p2);
void gsisynth_seq_chn_pressure (gsi_song_playstatus_t *d, int chn, int vel);
void gsisynth_seq_bender (gsi_song_playstatus_t *d, int chn, int p1, int p2);

//void gsisynth_seq_midi_out (gsi_song_playstatus_t *d, int ch, int data);
void gsisynth_seq_start_timer (gsi_song_playstatus_t *d);
void gsisynth_seq_wait_time (gsi_song_playstatus_t *d, int ticks);

int  gsisynth_seq_flush (gsi_song_playstatus_t *d);

int  gsisynth_buffer_full (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status);   /* True if no more data should be put in buffer */
int  gsisynth_buffer_status (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status); /* 0 = empty, 1 = used, 2 = full */


int  gsisynth_load_instrument (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status, int pgm);
int  gsisynth_write_instrument (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status, int pgm, int type, int size, void *data);
//RENAME TO: int  gsisynth_load_instrument_from_data (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status, int pgm, int type, int size, void *data);

int  gsisynth_clear_instrument (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status, int pgm);
int  gsisynth_clear_instruments (gsi_synth_device_t *hw, gsi_synth_devicedata_t *hw_status);


char *gsisynth_attach_device(char *device, int i);
/* returns device information (type), NULL if not a valid device number. */

/* error reporting: verbose error messages (to syslog ?) */
extern int  gsisynth_verbose;

/* XXX from the server: */
extern gsi_synth_device_t *synth_devices;
extern int  num_synths;


/* synthesizer driver */
extern int  synth_rate; /* number of synth ticks per second */

/* type of midi playing: */
#define _MIDI_GM     0
#define _MIDI_GS     1
#define _MIDI_MT32   2

#ifdef _cplusplus
}
#endif /* __cplusplus */

#endif /* _GSI__gsisynth__gsisynth_h */
