/************************************************************************
 * emumidi.c  -- emulate /dev/sequencer2 ('cause it's broke)
 *
 * This code was written by by Nathan Laredo (laredo@gnu.ai.mit.edu)
 * Source code may be freely distributed in unmodified form.
 *************************************************************************/
#include "playmidi.h"
#ifndef USE_SEQUENCER2

SEQ_USE_EXTBUF();

extern int seqfd, play_ext, play_gus, play_sb, gus_dev, sb_dev, wantopl3;
extern struct synth_info card_info[MAX_CARDS];
extern int perc, ticks;

struct chanstate {
    int program;
    int bender;
    int bender_range;
    int controller[255];
    int pressure;
};

struct voicestate {
    int program;
    int note;
    int channel;
    int bender;
    int bender_range;
    int pan;
    int timestamp;
    int dead;
};

static struct voicestate voice[2][64];
static struct chanstate channel[16];
#define CN (ISGUS(chn) ? 0 : 1)

void seq_reset()
{
    int i;
    ioctl(seqfd, SNDCTL_SEQ_RESET);
    if (play_gus || play_sb) {
	for (i = 0; i < 16; i++) {
	    channel[i].bender = 1 << 13;
	    channel[i].bender_range = 16;
	    channel[i].controller[CTL_PAN] = 0;
	    channel[i].controller[CTL_SUSTAIN] = 0;
	}
	if (play_gus)
	    for (i = 0; i < card_info[gus_dev].nr_voices; i++) {
		SEQ_BENDER(gus_dev, i, voice[0][i].bender = 1 << 13);
		SEQ_BENDER_RANGE(gus_dev, i, voice[0][i].bender_range = 16);
		if (voice[0][i].note)
		    SEQ_STOP_NOTE(gus_dev, i, voice[0][i].note, 64);
		voice[0][i].dead = voice[0][i].timestamp =
			voice[0][i].program = -1;
	    }
	if (play_sb)
	    for (i = 0; i < card_info[sb_dev].nr_voices; i++) {
		SEQ_BENDER(sb_dev, i, voice[1][i].bender = 1 << 13);
		SEQ_BENDER_RANGE(sb_dev, i, voice[1][i].bender_range = 16);
		if (voice[1][i].note)
		    SEQ_STOP_NOTE(sb_dev, i, voice[1][i].note, 64);
		voice[1][i].dead = voice[1][i].timestamp =
			voice[1][i].program = -1;
	    }
		
    }
}

void seq_set_patch(dev, chn, pgm)
int dev, chn, pgm;
{
   if (ISMIDI(chn)) {
	SEQ_MIDIOUT(dev, MIDI_PGM_CHANGE + chn);
	SEQ_MIDIOUT(dev, pgm);
   } else
	channel[chn].program = pgm;
}

void seq_stop_note(dev, chn, note, vel)
int dev, chn, note, vel;
{
    int i, card = CN;
    if (ISMIDI(chn)) {
	SEQ_MIDIOUT(dev, MIDI_NOTEOFF + chn);
	SEQ_MIDIOUT(dev, note);
	SEQ_MIDIOUT(dev, vel);
    } else
	for (i = 0; i < card_info[dev].nr_voices; i++)
	    if (voice[card][i].channel == chn &&
		voice[card][i].note == note) {
		voice[card][i].dead = 1;
		voice[card][i].timestamp /= 2;
		if (!channel[chn].controller[CTL_SUSTAIN])
		    SEQ_STOP_NOTE(dev, i, note, vel);
	    }
}

void seq_key_pressure(dev, chn, note, vel)
int dev, chn, note, vel;
{
    int i, card = CN;
    if (ISMIDI(chn)) {
	SEQ_MIDIOUT(dev, MIDI_KEY_PRESSURE + chn);
	SEQ_MIDIOUT(dev, note);
	SEQ_MIDIOUT(dev, vel);
    } else if (vel == 0)
	seq_stop_note(dev, chn, note, 64);
    else
	for (i = 0; i < card_info[dev].nr_voices; i++)
	    if (voice[card][i].channel == chn &&
		voice[card][i].note == note)
		SEQ_KEY_PRESSURE(dev, i, note, vel);
}

void seq_start_note(dev, chn, note, vel)
int dev, chn, note, vel;
{
    int i, oldest = 0, card = CN;
    if (ISMIDI(chn)) {
	SEQ_MIDIOUT(dev, MIDI_NOTEON + chn);
	SEQ_MIDIOUT(dev, note);
	SEQ_MIDIOUT(dev, vel);
    } else if (vel == 0)
	seq_stop_note(dev, chn, note, 64);
    else {
	for (i = 0; i < card_info[dev].nr_voices; i++)
	    if (voice[card][i].timestamp < voice[card][oldest].timestamp)
		    oldest = i;
	i = oldest;
	if (channel[chn].program != voice[card][i].program - 1)
	    SEQ_SET_PATCH(dev, i, (voice[card][i].program =
		channel[chn].program + 1) - 1);
	if (channel[chn].bender_range != voice[card][i].bender_range)
	    SEQ_BENDER_RANGE(dev, i, voice[card][i].bender_range =
		channel[chn].bender_range);
	if (channel[chn].bender != voice[card][i].bender)
	    SEQ_BENDER(dev, i, voice[card][i].bender =
		channel[chn].bender);
	if (channel[chn].controller[CTL_PAN] != voice[card][i].pan)
	    SEQ_CONTROL(dev, i, CTL_PAN, voice[card][i].pan = 
		channel[chn].controller[CTL_PAN]);
	SEQ_START_NOTE(dev, i, note, vel);
	voice[card][i].note = note;
	voice[card][i].channel = chn;
	voice[card][i].timestamp = ticks;
	if (ISPERC(chn))	/* percussion is lowest priority */
	    voice[card][i].timestamp /= 2;
	voice[card][i].dead = 0;
    }
}

static int rpn1[16] =
{ 127, 127, 127, 127, 127, 127, 127, 127,
  127, 127, 127, 127, 127, 127, 127, 127};
static int rpn2[16] =
{ 127, 127, 127, 127, 127, 127, 127, 127,
  127, 127, 127, 127, 127, 127, 127, 127};

void seq_control(dev, chn, p1, p2)
int dev, chn, p1, p2;
{
    int i, card = CN;
    if (ISMIDI(chn)) {
	SEQ_MIDIOUT(dev, MIDI_CTL_CHANGE + chn);
	SEQ_MIDIOUT(dev, p1);
	SEQ_MIDIOUT(dev, p2);
    } else {
	channel[chn].controller[p1] = p2;
	switch (p1) {
	case CTL_PAN:
	    for (i = 0; i < card_info[card].nr_voices; i++)
		if (voice[card][i].channel == chn && voice[card][i].pan != p2)
		    SEQ_CONTROL(dev, i, p1, voice[card][i].pan = p2);
	    break;
	case CTL_SUSTAIN:
	    if (p1 == CTL_SUSTAIN && !p2) {
		for (i = 0; i < card_info[card].nr_voices; i++)
		    if (voice[card][i].channel == chn
			&& voice[card][i].dead) {
			SEQ_STOP_NOTE(dev, i, voice[card][i].note, 64);
			voice[card][i].dead = 0;
		    }
	    }
	    break;
	case CTL_REGIST_PARM_NUM_MSB:
	    rpn1[chn] = p2;
	break;
	case CTL_REGIST_PARM_NUM_LSB:
	    rpn2[chn] = p2;
	break;
	case CTL_DATA_ENTRY:
	    if (rpn1[chn] == 0 && rpn2[chn] == 0) {
		channel[chn].bender_range = p2;
		rpn1[chn] = rpn2[chn] = 127;
		for (i = 0; i < card_info[card].nr_voices; i++)
		    if (voice[card][i].channel == chn
			&& voice[card][i].bender_range != p2)
			SEQ_BENDER_RANGE(dev, i,
			    voice[card][i].bender_range = p2);
	    }
	default:
	    break;
	}
    }
}

void seq_chn_pressure(dev, chn, vel)
int dev, chn, vel;
{
    if (ISMIDI(chn)) {
	SEQ_MIDIOUT(dev, MIDI_CHN_PRESSURE + chn);
	SEQ_MIDIOUT(dev, vel);
    } else
	channel[chn].pressure = vel;
}

void seq_bender(dev, chn, val)
int dev, chn, val;
{
    int card = CN, i;
    if (ISMIDI(chn)) {
	SEQ_MIDIOUT(dev, MIDI_PITCH_BEND + chn);
	SEQ_MIDIOUT(dev, val >> 7);
	SEQ_MIDIOUT(dev, val & 0x7f);
    } else {
	if (val == 0x40)
	    val = 1 << 13;
	channel[chn].bender = val;
	for (i = 0; i < card_info[dev].nr_voices; i++)
	    if (voice[card][i].bender != val && voice[card][i].channel == chn)
		SEQ_BENDER(dev, i, voice[card][i].bender = val);
    }
}
#endif
