/*
 * SYSEX / RPN controls
 */

#include "driver.h"
#include "emu8000.h"
#include "emu8000_voice.h"
#include "emu8000_port.h"
#include "emu8000_equalizer.h"

/*
 * RPN control
 */
void
snd_emu8000_rpn_event(emu8000_t *emu, emu8000_port_t *port,
		      emu8000_channel_t *chan, int msb_bit)
{
	int type;
	int val;

	type = (chan->control[SND_MIDI_CTL_RPN_MSB] << 8) |
		chan->control[SND_MIDI_CTL_RPN_LSB];
	val = chan->control[SND_MIDI_CTL_DATA_ENTRY] * 128 +
		chan->control[SND_MIDI_CTL_DATA_ENTRY+0x20];

	switch (type) {
	case 0x0000: /* Pitch bend sensitivity */
		/* MSB only / 1 semitone per 128 */
		if (msb_bit)
			chan->bend_range = val * 100 / 128;
		break;
					
	case 0x0001: /* fine tuning: */
		/* MSB/LSB, 8192=center, 100/8192 cent step */
		port->finetune = val - 8192;
		break;

	case 0x0002: /* coarse tuning */
		/* MSB only / 8192=center, 1 semitone per 128 */
		if (msb_bit)
			port->coarsetune = val - 8192;
		break;

	case 0x7F7F: /* "lock-in" RPN */
		/* ignored */
		break;
	}
		
}


static void
init_midi_status(emu8000_t *emu, emu8000_port_t *port)
{
	int i;

	for (i = 0; i < 16; i++) {
		emu8000_channel_t *chan = port->chanbufs + i;
		chan->control[SND_MIDI_CTL_RPN_MSB] = 0;
		chan->control[SND_MIDI_CTL_RPN_LSB] = 0;
		chan->control[SND_MIDI_CTL_NRPN_MSB] = 0;
		chan->control[SND_MIDI_CTL_NRPN_LSB] = 0;
		chan->nrpn_flag = 0;
		chan->bend_range= 200;
	}

	port->coarsetune = 0;
	port->finetune = 0;
}


/*
 * system exclusive messages
 */
static int
get_channel(unsigned char cmd)
{
	int p = cmd & 0x0f;
	if (p == 0)
		p = 9;
	else if (p < 10)
		p--;
	return p;
}

void
snd_emu8000_sysex(emu8000_t *emu, emu8000_port_t *port,
		  unsigned char *buf, int len)
{
	/* GM on */
	static unsigned char gm_on_macro[] = {
		0x7e,0x7f,0x09,0x01,
	};
	/* XG on */
	static unsigned char xg_on_macro[] = {
		0x43,0x10,0x4c,0x00,0x00,0x7e,0x00,
	};
	/* GS prefix
	 * drum channel: XX=0x1?(channel), YY=0x15, ZZ=on/off
	 * reverb mode: XX=0x01, YY=0x30, ZZ=0-7
	 * chorus mode: XX=0x01, YY=0x38, ZZ=0-7
	 */
	static unsigned char gs_pfx_macro[] = {
		0x41,0x10,0x42,0x12,0x40,/*XX,YY,ZZ*/
	};

#if 0
	/* SC88 system mode set
	 * single module mode: XX=1
	 * double module mode: XX=0
	 */
	static unsigned char gs_mode_macro[] = {
		0x41,0x10,0x42,0x12,0x00,0x00,0x7F,/*ZZ*/
	};
	/* SC88 display macro: XX=01:bitmap, 00:text
	 */
	static unsigned char gs_disp_macro[] = {
		0x41,0x10,0x45,0x12,0x10,/*XX,00*/
	};
#endif

	if (len <= 0 || buf[0] != 0xf0)
		return;
	/* skip first byte */
	buf++;
	len--;

	/* GM on */
	if (len >= sizeof(gm_on_macro) &&
	    memcmp(buf, gm_on_macro, sizeof(gm_on_macro)) == 0) {
		if (port->midi_mode != MODE_GS && port->midi_mode != MODE_XG)
			port->midi_mode = MODE_GM;
		init_midi_status(emu, port);
	}

	/* GS macros */
	else if (len >= sizeof(gs_pfx_macro) &&
		 memcmp(buf, gs_pfx_macro, sizeof(gs_pfx_macro)) == 0) {
		if (port->midi_mode != MODE_GS && port->midi_mode != MODE_XG)
			port->midi_mode = MODE_GS;

		if (buf[5] == 0x00 && buf[6] == 0x7f && buf[7] == 0x00) {
			/* GS reset */
			init_midi_status(emu, port);
		}

		else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x15) {
			/* drum pattern */
			int p = get_channel(buf[5]);
			port->chanbufs[p].drum_flag = buf[7];

		} else if ((buf[5] & 0xf0) == 0x10 && buf[6] == 0x21) {
			/* program */
			int p = get_channel(buf[5]);
			if (! port->chanbufs[p].drum_flag)
				port->chanbufs[p].program = buf[7];

		} else if (buf[5] == 0x01 && buf[6] == 0x30) {
			/* reverb mode */
			emu->reverb_mode = buf[7];
			snd_emu8000_update_reverb_mode(emu);

		} else if (buf[5] == 0x01 && buf[6] == 0x38) {
			/* chorus mode */
			emu->chorus_mode = buf[7];
			snd_emu8000_update_chorus_mode(emu);

		} else if (buf[5] == 0x00 && buf[6] == 0x04) {
			/* master volume */
			port->master_volume = buf[7];
			snd_emu8000_control_port(emu, EMU8000_UPDATE_VOLUME, port);

		}
	}

	/* XG on */
	else if (len >= sizeof(xg_on_macro) &&
		 memcmp(buf, xg_on_macro, sizeof(xg_on_macro)) == 0) {
		int i;
		port->midi_mode = MODE_XG;
		/* reset CC#0 for drums */
		for (i = 0; i < 16; i++) {
			if (port->chanbufs[i].drum_flag)
				port->chanbufs[i].control[SND_MIDI_CTL_BANK_SELECT] = 127;
			else
				port->chanbufs[i].control[SND_MIDI_CTL_BANK_SELECT] = 0;
		}
	}
}

