/*
 *	Name:		Main header include file
 *	Project:	MUS File Player Library
 *	Version:	1.75
 *	Author:		Vladimir Arnost (QA-Software)
 *	Last revision:	Mar-9-1996
 *	Compiler:	Borland C++ 3.1, Watcom C/C++ 10.0
 *
 */

#ifndef __MUSLIB_H_
#define __MUSLIB_H_

#ifndef __DEFTYPES_H_
  #include "deftypes.h"
#endif
#include "files.h"

/* Global Definitions */

#define MLVERSION	0x0175
#define MLVERSIONSTR	"1.75"
extern char MLversion[];
extern char MLcopyright[];

#define CHANNELS	16		// total channels 0..CHANNELS-1
#define PERCUSSION	15		// percussion channel

/* MUS file header structure */
struct MUSheader {
	char	ID[4];			// identifier "MUS" 0x1A
	WORD	scoreLen;		// score length
	WORD	scoreStart;		// score start
	WORD	channels;		// primary channels
	WORD	sec_channels;	// secondary channels (??)
	WORD    instrCnt;		// used instrument count
	WORD	dummy;
//	WORD	instruments[...];	// table of used instruments
};

/* OPL2 instrument */
struct OPL2instrument {
/*00*/	BYTE    trem_vibr_1;	/* OP 1: tremolo/vibrato/sustain/KSR/multi */
/*01*/	BYTE	att_dec_1;		/* OP 1: attack rate/decay rate */
/*02*/	BYTE	sust_rel_1;		/* OP 1: sustain level/release rate */
/*03*/	BYTE	wave_1;			/* OP 1: waveform select */
/*04*/	BYTE	scale_1;		/* OP 1: key scale level */
/*05*/	BYTE	level_1;		/* OP 1: output level */
/*06*/	BYTE	feedback;		/* feedback/AM-FM (both operators) */
/*07*/	BYTE    trem_vibr_2;	/* OP 2: tremolo/vibrato/sustain/KSR/multi */
/*08*/	BYTE	att_dec_2;		/* OP 2: attack rate/decay rate */
/*09*/	BYTE	sust_rel_2;		/* OP 2: sustain level/release rate */
/*0A*/	BYTE	wave_2;			/* OP 2: waveform select */
/*0B*/	BYTE	scale_2;		/* OP 2: key scale level */
/*0C*/	BYTE	level_2;		/* OP 2: output level */
/*0D*/	BYTE	unused;
/*0E*/	sshort	basenote;		/* base note offset */
};

/* OP2 instrument file entry */
struct OP2instrEntry {
/*00*/	WORD	flags;				// see FL_xxx below
/*02*/	BYTE	finetune;			// finetune value for 2-voice sounds
/*03*/	BYTE	note;				// note # for fixed instruments
/*04*/	struct OPL2instrument instr[2];	// instruments
};

#define FL_FIXED_PITCH	0x0001		// note has fixed pitch (see below)
#define FL_UNKNOWN	0x0002			// ??? (used in instrument #65 only)
#define FL_DOUBLE_VOICE	0x0004		// use two voices instead of one


#define OP2INSTRSIZE	sizeof(struct OP2instrEntry) // instrument size (36 bytes)
#define OP2INSTRCOUNT	(128 + 81-35+1)	// instrument count

/* From MLOPL_IO.CPP */
#define OPL2CHANNELS	9
#define OPL3CHANNELS	18
#define MAXCHANNELS		18


/* Channel Flags: */
#define CH_SECONDARY	0x01
#define CH_SUSTAIN		0x02
#define CH_VIBRATO		0x04		/* set if modulation >= MOD_MIN */
#define CH_FREE			0x80

struct OPLdata {
	uint	channelInstr[CHANNELS];			// instrument #
	uchar	channelVolume[CHANNELS];		// volume
	uchar	channelLastVolume[CHANNELS];	// last volume
	schar	channelPan[CHANNELS];			// pan, 0=normal
	schar	channelPitch[CHANNELS];			// pitch wheel, 0=normal
	uchar	channelSustain[CHANNELS];		// sustain pedal value
	uchar	channelModulation[CHANNELS];	// modulation pot value
};

struct OPLio {
	void	OPLwriteChannel(uint regbase, uint channel, uchar data1, uchar data2);
	void	OPLwriteValue(uint regbase, uint channel, uchar value);
	void	OPLwriteFreq(uint channel, uint freq, uint octave, uint keyon);
	uint	OPLconvertVolume(uint data, uint volume);
	uint	OPLpanVolume(uint volume, int pan);
	void	OPLwriteVolume(uint channel, struct OPL2instrument *instr, uint volume);
	void	OPLwritePan(uint channel, struct OPL2instrument *instr, int pan);
	void	OPLwriteInstrument(uint channel, struct OPL2instrument *instr);
	void	OPLshutup(void);

	virtual int		OPLinit(uint numchips, uint rate);
	virtual void	OPLdeinit(void);
	virtual void	OPLwriteReg(int which, uint reg, uchar data);

	uint OPLchannels;
};

struct musicBlock {
	musicBlock();
	~musicBlock();

	BYTE *score;
	BYTE *scoredata;
	int playingcount;
	OPLdata driverdata;
	OPLio *io;

	struct OP2instrEntry *OPLinstruments;

	ulong MLtime;

	int playTick();

	void OPLplayNote(uint channel, uchar note, int volume);
	void OPLreleaseNote(uint channel, uchar note);
	void OPLpitchWheel(uint channel, int pitch);
	void OPLchangeControl(uint channel, uchar controller, int value);
	void OPLplayMusic();
	void OPLstopMusic();
	void OPLchangeVolume(uint volume);

	int OPLloadBank (FileReader &data);

private:
	/* OPL channel (voice) data */
	struct channelEntry {
		uchar	channel;		/* MUS channel number */
		uchar	note;			/* note number */
		uchar	flags;			/* see CH_xxx below */
		uchar	realnote;		/* adjusted note number */
		schar	finetune;		/* frequency fine-tune */
		sint	pitch;			/* pitch-wheel value */
		uint	volume;			/* note volume */
		uint	realvolume;		/* adjusted note volume */
		struct OPL2instrument *instr;	/* current instrument */
		ulong	time;			/* note start time */
	} channels[MAXCHANNELS];

	void writeFrequency(uint slot, uint note, int pitch, uint keyOn);
	void writeModulation(uint slot, struct OPL2instrument *instr, int state);
	uint calcVolume(uint channelVolume, uint MUSvolume, uint noteVolume);
	int occupyChannel(uint slot, uint channel,
						 int note, int volume, struct OP2instrEntry *instrument, uchar secondary);
	int releaseChannel(uint slot, uint killed);
	int releaseSustain(uint channel);
	int findFreeChannel(uint flag, uint channel, uchar note);
	struct OP2instrEntry *getInstrument(uint channel, uchar note);

	friend class Stat_opl;

};

enum MUSctrl {
    ctrlPatch = 0,
    ctrlBank,
    ctrlModulation,
    ctrlVolume,
    ctrlPan,
    ctrlExpression,
    ctrlReverb,
    ctrlChorus,
    ctrlSustainPedal,
    ctrlSoftPedal,
    _ctrlCount_,
    ctrlSoundsOff = _ctrlCount_,
    ctrlNotesOff,
    ctrlMono,
    ctrlPoly,
    ctrlResetCtrls
};

#endif // __MUSLIB_H_
