/*
 * linux io
 */

#include "glib.h"
#include "sblast.h"
#ifdef GUS
#include "ultra.h"
#ifndef NOSOUND
#include <sys/ultrasound.h>
#endif
#endif
#include "midicode.h"

#ifndef NOSOUND
#include <sys/soundcard.h>
#endif
#include <fcntl.h>
#include <sys/time.h>

#define XPN 120
#define MAINV 120

extern int Global_reverberation;
extern int Global_chorus_depth;
extern int Global_main_volume;
extern int Global_reverberation;
extern int Global_expression;
extern int Global_tuning;

#ifdef FMCARD
extern int sb_drum_note;
extern int sb_drum_dur;
extern int sb_transpose;
extern int sb_chorus_spread;
extern int sb_echo_delay;
extern int sb_echo_atten;
extern int sb_other_voice;
#endif

/*
 * some of the following code adapted from:
 * 
 * fmplay and sbiset, by Hannu Savolainen (hsavolai#cs.helsinki.fi)
 * Modifications, bugfixes, and ANSIfied by Rob Hooft (hooft#chem.ruu.nl)
 *
 */
int seq_fd;
static int nrsynths, nrmidis;
int ext_index = -1, ext_dev = -1, gus_dev = -1, sb_dev = -1;
static unsigned char sbbuf[408];
static int sbptr = 0;
static unsigned sb_time = 0;
static int o3_mode = 0;
static int max_cells[3];
static int cell_pitch[3][32];

static int seq_is_open = 0;
static int tried_it_already = 0;

#ifdef GUS
extern int gus_fixed_key;
extern int gus_fix_dur;
extern int gus_transpose;
extern int gus_tune_voice;
extern int gus_chorus_spread;
extern int gus_echo_delay;
extern int gus_echo_atten;
extern int gus_voice_modes;
void gus_vol(int chan, int prog, int cell, int vol);
void card_expression(int card, int chan, int cell, int loud);
void card_main_volume(int card, int chan, int cell);
void gus_max_voices(int num);
void card_bend(int card, int cell, int up);
#ifndef GUS_NUM_VOICES
#define GUS_NUM_VOICES 32
#endif
#endif

int
opensb()
{
	int i;
	struct synth_info fm_info;
	void sb_resync();

	if (seq_is_open) return(1);
	if (tried_it_already) return(0);
	tried_it_already = 1;
#ifndef NOSOUND
#ifdef MIDIIN
	if ((seq_fd=open("/dev/sequencer", O_RDWR, 0))==-1) {
#else
	if ((seq_fd=open("/dev/sequencer", O_WRONLY, 0))==-1) {
#endif
		return(0);
	}

	if (ioctl (seq_fd, SNDCTL_SEQ_NRSYNTHS, &nrsynths) == -1) {
		return(0);
	}
	if (ioctl (seq_fd, SNDCTL_SEQ_NRMIDIS, &nrmidis) == -1) {
		return(0);
	}
	if (nrmidis > 0) {
		ext_dev = nrmidis - 1; /* 0 for GUS midi interface */
		ext_index = nrsynths;
	}

	for (i = 0; i < nrsynths; i++) {
		fm_info.device = i;
		if (ioctl (seq_fd, SNDCTL_SYNTH_INFO, &fm_info) == -1) {
			return(0);
		}

		if (fm_info.synth_type == SYNTH_TYPE_SAMPLE
			&& fm_info.synth_subtype == SAMPLE_TYPE_GUS) {
				gus_dev = i;
				max_cells[i] = fm_info.nr_voices;
		}
		else if (fm_info.synth_type == SYNTH_TYPE_FM) {
				sb_dev = i;
				max_cells[i] = fm_info.nr_voices;
		}
	}
#ifdef GUS
	if (gus_dev >= 0) {
		if (GUS_NUM_VOICES != 24) gus_max_voices(GUS_NUM_VOICES);
		max_cells[gus_dev] = GUS_NUM_VOICES;
	}
#endif
	if (sb_dev >= 0) {
		int n = sb_dev;
		ioctl(seq_fd, SNDCTL_FM_4OP_ENABLE, &n);
		max_cells[sb_dev] = 6;
	}

	if (sb_dev >= 0) for (i = 0; i < max_cells[sb_dev]; i++)
		cell_pitch[sb_dev][i] = -1;
	if (gus_dev >= 0) for (i = 0; i < max_cells[gus_dev]; i++)
		cell_pitch[gus_dev][i] = -1;

	seq_is_open = 1;
	sb_resync();
	return(1);
#else
	seq_is_open = 0;
	return(0);
#endif
}

closesb()
{
#ifndef NOSOUND

	if (!seq_is_open) return(1);
#ifdef MIDIIN
	while (midipending()) (void)midigetc();
#endif
	(void) ioctl(seq_fd, SNDCTL_SEQ_RESET, 0);
	close(seq_fd);
#endif
	gus_dev = -1;
	sb_dev = -1;
	ext_dev = -1;
	ext_index = -1;
	seq_is_open = 0;
	tried_it_already = 0;
	return(1);
}

void
sbflush(void)
{
	if (!sbptr) return;

#ifndef NOSOUND
	if (write(seq_fd, sbbuf, sbptr) == -1) {
		perror("/dev/sequencer");
		exit(-1);
	}
#endif

	sbptr=0;
}

void
sbwrite(char *msg)
{
	if (sbptr>400) sbflush();

	memcpy(&sbbuf[sbptr], msg, 4);
	sbptr +=4;
}

void
sqwrite(char *msg)
{
	if (sbptr>400) sbflush();

	memcpy(&sbbuf[sbptr], msg, 8);
	sbptr +=8;
}

struct timeval tv;
struct timezone tz;
unsigned epoch = 0;
void midisync()
{
#ifndef NOSOUND
	unsigned jiffies;

	gettimeofday (&tv, &tz);
	jiffies = tv.tv_sec*100 + tv.tv_usec/10000;
	if (!epoch) epoch = jiffies;
	sb_time = jiffies - epoch;
	jiffies = sb_time;
/*
printf("Time %d:%d\n", (sb_time/100)/60, (sb_time/100)%60);
*/
	jiffies = (jiffies << 8) | SEQ_WAIT;
	sbwrite((char*)&jiffies);
#endif
}

void sb_resync()
{	char buf[4];

	buf[0] = SEQ_SYNCTIMER;
	sbwrite(buf);
	sbflush();
	epoch = sb_time = 0;
}

void midich(char c)
{
#ifndef NOSOUND
	char buf[4];

	if (!opensb()) return;
	if (ext_dev < 0) return;
	buf[0] = SEQ_MIDIPUTC;
	buf[1] = c;
	buf[2] = ext_dev;
	buf[3] = 0;
	sbwrite(buf);
#endif
}
int
find_cell(int card, int pitch)
{
	int i, best = -1, octbest = -1, anote = -1;
	for (i = 0; i < max_cells[card]; i++) {
		if (card == sb_dev && o3_mode && i > 5) break;
		if (cell_pitch[card][i] == pitch) {
			best = i;
			break;
		}
		else if (cell_pitch[card][i] == pitch + 12) octbest = i;
		else if (cell_pitch[card][i] == pitch - 12) octbest = i;
		else if (cell_pitch[card][i] >= 0) anote = i;
	}
if (best >= 0) return(best);
if (pitch == -1) return(0);
if (octbest >= 0) return(octbest);
if (anote >= 0) return(anote);
return(0);
}

#ifndef NOSOUND
static void
gus_pan(int cell, int pan)
{   char buf[8];
    buf[0] = SEQ_PRIVATE;
    buf[1] = gus_dev;
    buf[2] = _GUS_VOICEBALA;
    buf[3] = cell;
    *(unsigned short *) &buf[4] = pan;
    *(unsigned short *) &buf[6] = 0;
    sqwrite(buf);
}


static int
card_note_on(int cell, int card, int voice, int pitch, int vol,
	int up, int loud, int pan)
{
	char buf[8];

	if (cell < 0) cell = find_cell(card, -1);
	buf[0] = SEQ_EXTENDED;
	buf[1] = SEQ_PGMCHANGE;
	buf[2] = card;
	buf[3] = cell;
	buf[4] = voice;
	buf[5] = buf[6] = buf[7] = 0;
	sqwrite(buf);
#ifdef GUS
	if (card == gus_dev) {
		card_bend(gus_dev, cell, up);
	}
#endif
#ifdef FMCARD
	if (card == sb_dev) {
		card_bend(sb_dev, cell, up);
	}
#endif
#ifdef GUS
    	if (card == gus_dev) {
		/* gus_vol(chan, prog, cell, vol, 0); */
		card_expression(card, 0, cell, loud);
		card_main_volume(card, 0, cell);
    	}
#endif
	buf[0] = SEQ_EXTENDED;
	buf[1] = SEQ_NOTEON;
	buf[2] = card;
	buf[3] = cell;
	buf[4] = cell_pitch[card][cell] = pitch;
	buf[5] = vol;
	buf[6] = buf[7] = 0;
	sqwrite(buf);

	if (card == gus_dev) gus_pan(cell, pan);

	return(cell);
}
#endif

#ifndef NOSOUND
static void
card_note_off(int cell, int card, int pitch, int vol)
{
	char buf[8];

	if (cell < 0) cell = find_cell(card, pitch);
	buf[0] = SEQ_EXTENDED;
	buf[1] = SEQ_NOTEOFF;
	buf[2] = card;
	buf[3] = cell;
	buf[4] = pitch;
	buf[5] = 0;
	buf[6] = buf[7] = 0;
	sqwrite(buf);
	cell_pitch[card][cell] = -1;
}
#endif

static void
oneplaysb(int card, int pitch, int vol, int dur, int onoff,
	int up, int loud, int echo_loud, int echo, int echo_vol)
{
#ifndef NOSOUND
	int prog, other_voice = 0;
	unsigned jiffies;
	int cell = 0, cell2 = 0, cell_ch = 0;
	int cell_e = 0, cell_e_ch = 0;
	int pitch_p, pitch_c, bend_pitch;


    midisync();

    if (card == sb_dev) {
#ifdef FMCARD
	if (onoff == -1 && sb_drum_dur) return;
	if (sb_drum_dur) onoff = 0;
	if (sb_drum_note) pitch = sb_drum_note;
	if (sb_transpose) pitch += sb_transpose - 64;
	if (sb_other_voice) other_voice = sb_other_voice;
#endif
    }
#ifdef GUS
    else if (card == gus_dev) {
	if (onoff == -1 && gus_fix_dur) return;
	if (gus_fix_dur) onoff = 0;
	if (gus_fixed_key) pitch = gus_fixed_key;
	if (gus_transpose) pitch += gus_transpose - 64;
    }
#endif

    pitch_p = pitch_c = pitch;
    if (up < 0) {
	pitch_p -= up / 2;
	pitch_c += (up - 1) / 2;
	bend_pitch = 0;
    }
    else bend_pitch = up;

    if (pitch < 0) pitch = 0;
    if (pitch > 127) pitch = 127;
    if (pitch_p < 0) pitch_p = 0;
    if (pitch_p > 127) pitch_p = 127;
    if (pitch_c < 0) pitch_c = 0;
    if (pitch_c > 127) pitch_c = 127;

    prog =
#ifdef GUS
		(card == gus_dev)? GUSEDITPROG : SBEDITPROG;
#else
		SBEDITPROG;
#endif

    if (onoff >= 0) {
	if (card == ext_index) {
		midich(MIDI_ON_NOTE+0);
		midich(pitch);
		midich(vol);
	}
	else {
	    cell = card_note_on(-1, card, prog,
		pitch_p, vol, bend_pitch, loud, 7);
	    if (up) cell_ch = card_note_on(-1, card, prog,
		pitch_c, vol, -bend_pitch, loud, 7);

	    if (card == sb_dev && other_voice)
		/*cell2 = card_note_on(-1, card, other_voice - 1, pitch_p,*/
		cell2 = card_note_on(-1, card, prog+2, pitch_p,
			vol, bend_pitch, loud, 7);
	    if (echo) {
		jiffies = sb_time + echo;
		jiffies = (jiffies << 8) | SEQ_WAIT;
		sbwrite((char*)&jiffies);

		if (card == sb_dev) prog++;
		cell_e = card_note_on(-1, card, prog,
			pitch_p, echo_vol, bend_pitch, echo_loud, 15);
		if (up) cell_e_ch = card_note_on(-1, card, prog,
			pitch_c, echo_vol, -bend_pitch, echo_loud, 15);
		if (card == sb_dev) prog--;
	    }
	}
    }

    if (!onoff) {
	if (card == ext_index) sb_time += 15 * dur;
#ifdef FMCARD
	else if (card == sb_dev && sb_drum_dur) sb_time += sb_drum_dur;
#endif
#ifdef GUS
	else if (card == gus_dev && gus_fix_dur) sb_time += gus_fix_dur;
	else if (card == gus_dev) sb_time += 15 * dur;
#endif
	else if (dur < 4) sb_time += 2 * dur;
	else sb_time += 7 * dur;
	jiffies = sb_time;
	jiffies = (jiffies << 8) | SEQ_WAIT;
	sbwrite((char*)&jiffies);
    }

    if (onoff <= 0) {
	if (card == ext_index) {
		midich(MIDI_OFF_NOTE+0);
		midich(pitch);
		midich(vol);
	}
	else {
	    card_note_off(onoff? -1 : cell, card, pitch_p, vol);
	    if (up) card_note_off(onoff? -1 : cell_ch, card, pitch_c, vol);

	    if (card == sb_dev && other_voice)
		card_note_off(onoff? -1 : cell2, card, pitch_p, vol);
	    if (echo) {
		jiffies = sb_time + echo;
		jiffies = (jiffies << 8) | SEQ_WAIT;
		sbwrite((char*)&jiffies);
	        card_note_off(onoff? -1 : cell_e, card, pitch_p, echo_vol);
	        if (up) card_note_off(onoff? -1 : cell_e_ch, card, pitch_c,
			echo_vol);
	    }
	}
    }

    sbflush();
#endif
}

#define SETTING_CHORUS_SPREAD 32

int
playsb(int chan, int pitch, int vel, int dur, int onoff)
{
#ifndef NOSOUND
    int card = -1;
    int up, echo, chorus_spread;
    int reverb_adjust, echo_delay = 64, echo_dur, echo_loud;
    int echo_vel = 64, percussive_flag = 0;

    if (chan < 1 || chan > 3) return;
    if (!opensb()) return;

    switch(chan) {
	case 1: card = sb_dev; break;
	case 2: card = ext_index; break;
	case 3: card = gus_dev; break;
    }

    if (card < 0) return;

    switch(chan) {
#ifdef FMCARD
	case 1: chorus_spread = sb_chorus_spread;
		echo_vel = sb_echo_atten;
		echo_delay = sb_echo_delay;
		break;
#endif
	case 2: chorus_spread = 64;
		echo_vel = 64;
		echo_delay = 64;
		break;
#ifdef GUS
	case 3: chorus_spread = gus_chorus_spread;
		echo_vel = gus_echo_atten;
		echo_delay = gus_echo_delay;
		break;
#endif
	default: chorus_spread = 64;
		echo_vel = 64;
		echo_delay = 64;
		break;
    }

    if (!chorus_spread) chorus_spread = 64;
    chorus_spread -= 64;

    if (chorus_spread >= 0)
    	up = (SETTING_CHORUS_SPREAD * Global_chorus_depth * chorus_spread) / 64;
    else if (Global_chorus_depth) {
    	int ch_adjust = (127 / -chorus_spread) + 1;
    	ch_adjust = (Global_chorus_depth / ch_adjust) + 1;
    	up = -ch_adjust;
    }
    else up = 0;


    echo_delay = (Global_reverberation * echo_delay) >> 9;
    echo_vel = (Global_reverberation * echo_vel) >> 9;

#define REVERB_THRESHHOLD 10
    if (Global_reverberation <= REVERB_THRESHHOLD) echo_delay = 0;

#ifdef FMCARD
    if (card == sb_dev) percussive_flag =
        sb_drum_note;
#endif
#ifdef GUS
    if (card == gus_dev) percussive_flag =
        !(gus_voice_modes & WAVE_SUSTAIN_ON);
#endif

    if (percussive_flag) {
        echo_vel += vel /16;
        echo_vel = vel / 4 - 2 * echo_vel;
	echo_delay /= 2;
    }
    else {
        echo_vel = vel / 3 - echo_vel;
    }
    if (echo_vel < 1) echo_vel = 1;

    echo_dur += echo_delay / 2; /* not used yet */

	reverb_adjust = (Global_reverberation * 50) / 500;
	echo_loud = Global_expression / 2;
	echo_loud -= reverb_adjust;
	if (echo_loud < 1) echo_loud = 1;
	if (percussive_flag) echo_loud /= 16;

/**
printf("reverb %d, reverb_adjust %d, perc=%d, echo_delay %d\n",
Global_reverberation, reverb_adjust, percussive_flag, echo_delay);
**/

    oneplaysb(card, pitch, vel, dur, onoff, up,
	Global_expression, echo_loud, echo_delay,
	echo_vel);

#endif
}

#ifdef SBLAST

#define OWN_FM_VOL
#ifdef OWN_FM_VOL
static char            fm_volume_table[128] =
{-63, -48, -40, -35, -32, -29, -27, -26,	/* 0 -   7 */
 -24, -23, -21, -20, -19, -18, -18, -17,	/* 8 -  15 */
 -16, -15, -15, -14, -13, -13, -12, -12,	/* 16 -  23 */
 -11, -11, -10, -10, -10, -9, -9, -8,	/* 24 -  31 */
 -8, -8, -7, -7, -7, -6, -6, -6,/* 32 -  39 */
 -5, -5, -5, -5, -4, -4, -4, -4,/* 40 -  47 */
 -3, -3, -3, -3, -2, -2, -2, -2,/* 48 -  55 */
 -2, -1, -1, -1, -1, 0, 0, 0,	/* 56 -  63 */
 0, 0, 0, 1, 1, 1, 1, 1,	/* 64 -  71 */
 1, 2, 2, 2, 2, 2, 2, 2,	/* 72 -  79 */
 3, 3, 3, 3, 3, 3, 3, 4,	/* 80 -  87 */
 4, 4, 4, 4, 4, 4, 4, 5,	/* 88 -  95 */
 5, 5, 5, 5, 5, 5, 5, 5,	/* 96 - 103 */
 6, 6, 6, 6, 6, 6, 6, 6,	/* 104 - 111 */
 6, 7, 7, 7, 7, 7, 7, 7,	/* 112 - 119 */
 7, 7, 7, 8, 8, 8, 8, 8};	/* 120 - 127 */

static int
new_fm_vol(int volbyte, int mainvol)
{
    int oldvol, newvol, n;

    oldvol = 0x3f - (volbyte & 0x3f);
    newvol = fm_volume_table[mainvol] + oldvol;

    if (newvol > 0x3f) newvol = 0x3f;
    else if (newvol < 0) newvol = 0;
    n = 0x3f - (newvol & 0x3f);
    return( (volbyte & 0xc0) | (n & 0x3f) );
}

#endif

extern char *Syndata;

int
onesbvoice(int v, char *buf, int pan)
{
#ifndef NOSOUND
	struct sbi_instrument instr;
	char vdata[SBVOICESIZE];
	int i;

	if (!opensb()) return(-1);
	if (sb_dev < 0) return(-1);
	sbflush();
#ifdef OPL3
	o3_mode = 0;
#endif
	instr.channel = v;
	instr.device = sb_dev;
	instr.key = FM_PATCH;

	for (i = 0; i < SBVOICESIZE; i++) vdata[i] = buf[i];
#ifdef OWN_FM_VOL
	vdata[39] = new_fm_vol(vdata[39], Global_main_volume);
	if (vdata[46] & 1) vdata[38] = new_fm_vol(vdata[38], Global_main_volume);
#endif
	vdata[46] = (vdata[46]&0xcf)|pan;
	if (Global_reverberation > REVERB_THRESHHOLD) {
		unsigned val;
		val = vdata[43] & 0x0f;
		if (val > 0) val--;
		vdata[43] = (vdata[43]&0xf0) | val;
	}
	for (i=0;i<16;i++) {
		instr.operators[i] = (i+36 < SBVOICESIZE)? vdata[i+0x24] : 0;
	}
	if (write(seq_fd, (char*)&instr, sizeof(instr)) == -1) return(-1);
	return(0);
#else
	return(-1);
#endif
}
int
sbvoice(int v, char *buf)
{
#ifndef NOSOUND
	int ret, other;
	ret = onesbvoice(v, buf, 0x20);
	if (ret) return(ret);
	ret = onesbvoice(v + 1, buf, 0x10);

	other = getval("othvce");

	if (other) {
		char *ovpt;
		other--;
		if (other > 127) other -= 128;
		ovpt = Syndata + other * SBVOICESIZE;
		if (*ovpt > ' ')
		(void)onesbvoice(v + 2, ovpt, 0x20);
	}


	sb_resync();
	return(ret);
#else
	return(-1);
#endif
}
#endif

#ifdef OPL3
int
oneo3voice(int v, char *buf, int pan)
{
/*
differences are that the key field must be initialized to OPL3_PATCH and
the byte offsets between 11 and 21 are used for OPs 3 and 4.
*/
#ifndef NOSOUND
	struct sbi_instrument instr;
	char vdata[O3VOICESIZE];
	int i, voice_size;

	if (!opensb()) return(-1);
	if (sb_dev < 0) return(-1);
	sbflush();
	o3_mode = 1;
	instr.channel = v;
	instr.device = sb_dev;
	instr.key = OPL3_PATCH;
	voice_size = 22;
	if ((buf[49]&0x3f) == 0x3f &&
	    (buf[50]&0x3f) == 0x3f ) {
		instr.key = FM_PATCH;
		voice_size = 11;
	}

	for (i = 0; i < O3VOICESIZE; i++) vdata[i] = buf[i];

	if (instr.key == FM_PATCH) {
#ifdef OWN_FM_VOL
	    vdata[39] = new_fm_vol(vdata[39], Global_main_volume);
	    if (vdata[46] & 1) vdata[38] = new_fm_vol(vdata[38], Global_main_volume);
#endif
	    vdata[46] = (vdata[46] & 0xcf) | pan;
	    if (Global_reverberation > REVERB_THRESHHOLD) {
		unsigned val;
		val = vdata[43] & 0x0f;
		if (val > 0) val--;
		vdata[43] = (vdata[43]&0xf0) | val;
	    }
	}
	else {
	    int mode;
	    if (vdata[46]&1) mode = 2; else mode = 0;
	    if (vdata[57]&1) mode++;
#ifdef OWN_FM_VOL
	    vdata[50] = new_fm_vol(vdata[50], Global_main_volume);
	    if (mode == 3) vdata[49] = new_fm_vol(vdata[49], Global_main_volume);
	    if (mode == 1) vdata[39] = new_fm_vol(vdata[39], Global_main_volume);
	    if (mode == 2 || mode == 3) vdata[38] = new_fm_vol(vdata[38], Global_main_volume);
#endif
	    vdata[46] = (vdata[46] & 0xcf) | pan;
	    vdata[57] = (vdata[57] & 0xcf) | pan;
	    if (mode == 1 && Global_reverberation > REVERB_THRESHHOLD) {
		unsigned val;
		val = vdata[43] & 0x0f;
		if (val > 0) val--;
		vdata[43] = (vdata[43]&0xf0) | val;
		val = vdata[54] & 0x0f;
		if (val > 0) val--;
		vdata[54] = (vdata[54]&0xf0) | val;
	    }
	}

	for (i = 0; i < 32; i++) {
		instr.operators[i] = (i < voice_size)? vdata[i+36] : 0;
	}
	if (write(seq_fd, (char*)&instr, sizeof(instr)) == -1) return(-1);
	sb_resync();
	return(0);
#else
	return(-1);
#endif
}
int
o3voice(int v, char *buf)
{
#ifndef NOSOUND
	int ret, other;
	ret = oneo3voice(v, buf, 0x20);
	if (!ret) ret = oneo3voice(v + 1, buf, 0x10);

	other = getval("othvce");

	if (other) {
		char *ovpt;
		other--;
		if (other > 127) other -= 128;
		ovpt = Syndata + other * O3VOICESIZE;
		if (*ovpt > ' ')
		(void)oneo3voice(v + 2, ovpt, 0x20);
	}

	sb_resync();
	return(ret);
#else
	return(-1);
#endif
}
#endif

#ifdef MIDIIN

int midipending()
{	int cnt;
	if (!opensb()) return(0);
	if (ext_dev < 0) return(0);
	if (ioctl(seq_fd, SNDCTL_SEQ_GETINCOUNT, &cnt) == -1) return(0);
	else return(cnt);
}

int midigetc(void)
{	int l, havemidi;
	unsigned char buf[4];

	if (!opensb()) return(0);
	if (ext_dev < 0) return(0);
	havemidi = 0;
	while (!havemidi) {
		if ((l=read(seq_fd, buf, 4))==-1) {
			return(0);
		}
		if (l != 4) {
			fprintf(stderr,"could only read %d bytes\n", l);
			return(0);
		}
		if (buf[0] == SEQ_WAIT) {
/**
			sb_time = buf[1] + (buf[2] << 8) + (buf[3] << 16);
**/
		}
		else if (buf[0] == SEQ_MIDIPUTC) havemidi = 1;
	}
	return(buf[1]);
}

int midigetnb()
{
	if (midipending()) return(midigetc());
	return(-1);
}
/* following 2 routines adapted from midifile.c,
 * by Tim Thompson and M. Czeisperger
 */
static
chanmessage(status,c1,c2)
int status;
int c1, c2;
{
    int chan;

    if (!strcmp(Synthname, "Ultrasound")) chan = 3;
    else chan = 1;

if (c2 < 0 || c2 >127) {
	/* fprintf(stderr, "chanmessage: input vol %d\n", c2);*/
	return;
}

    switch ( status & 0xf0 ) {
    case MIDI_OFF_NOTE:
	playsb(chan,c1,c2,0,-1);
	break;
    case MIDI_ON_NOTE:
	playsb(chan,c1,c2,0,1);
	break;
    case MIDI_POLY_TOUCH:
	break;
    case MIDI_CTRL:
	if (c1 != ALL_NOTES_OFF)	;
	break;
    case MIDI_CH_PROGRAM:
	break;
    case MIDI_TOUCH:
	break;
    case MIDI_BEND:
	break;
    }
}

void midimessage()
{
	/* This array is indexed by the high half of a status byte.  It's */
	/* value is either the number of bytes needed (1 or 2) for a channel */
	/* message, or 0 (meaning it's not  a channel message). */
	static int chantype[] = {
		0, 0, 0, 0, 0, 0, 0, 0,		/* 0x00 through 0x70 */
		2, 2, 2, 2, 1, 1, 2, 0		/* 0x80 through 0xf0 */
	};
	static int c = -1, c1 = -1, c2 = -1;
	static int running = 0;	/* 1 when running status used */
	static int status = 0;		/* status value (e.g. 0x90==note-on) */
	int needed;


	if (c == -1) {
		c = midigetnb();
		if (c == -1) return;
	}
	if ( (c & 0x80) == 0 ) {	 /* running status? */
		if ( status == 0 ) {
			/*mferror("unexpected running status")*/
			c = c1 = c2 = -1;
			return;
		}
		running = 1;
	}
	else {
		status = c;
		running = 0;
	}

	needed = chantype[ (status>>4) & 0xf ];

	if ( needed ) {		/* ie. is it a channel message? */

		if ( running ) c1 = c;
		else if (c1 == -1) {
			c1 = midigetnb();
			if (c1 == -1) return;
		}
		if (c1 & 0x80) {
			c = c1;
			c1 = c2 = -1;
			return;
		}
		if (needed>1) {
			if (c2 == -1) c2 = midigetnb();
			if (c2 == -1) return;
		}
		else c2 = 0;

		if (c2 & 0x80) {
			c = c2;
			c1 = c2 = -1;
			return;
		}

		chanmessage( status, c1, c2 );
		c = c1 = c2 = -1;
	}
	/*else mferror("apparent non-channel message");*/
}

#endif

#ifdef GUS

#include <sys/ultrasound.h>

#define Bit8 0
#define Bit16 4 
#define LoopOff 0
#define LoopOn 8
#define UniDir 0
#define BiDir 16
#define Forw 0
#define Backw 64
#define Up 0
#define Down 64


void
gus_max_voices(int num)
{
	char buf[8];
	buf[0] = SEQ_PRIVATE;
	buf[1] = gus_dev;
	buf[2] = _GUS_NUMVOICES;
	buf[3] = 0;
	*(unsigned short*)&buf[4] = num;
	buf[6] = buf[7] = 0; 
	sqwrite(buf);
}

#define GUS_VOLUME 72

#define GUSEXPRAMPMINVOL (11<<8)
#define GUSEXPRAMPMAXVOL (15<<8)
#define GUSMAXVOL (1<<15)

/*
 * Calculate gus volume from note velocity, main volume, expression,
 * and intrinsic patch volume given in patch library.  Expression is
 * multiplied in, so it emphasizes differences in note velocity,
 * while main volume is added in -- I don't know whether this is right,
 * but it seems reasonable to me.  (In the previous stage, main volume
 * controller messages were changed to expression controller messages,
 * if they were found to be used for dynamic volume adjustments, so here,
 * main volume can be assumed to be constant throughout a song.)
 *
 * Intrinsic patch volume is added in, but if over 64 is also multiplied
 * in, so we can give a big boost to very weak voices like nylon
 * guitar and the basses.
 */
unsigned short
gvol(int vel, int mainv, int xpn, int voicev)
{
	int i, m, n, x;

	if (xpn <= 0 || vel == 0) return(0);
/** added **/
	if (xpn < 3) xpn = 3;

	/* A voice volume of 64 is considered neutral, so adjust
	 * the main volume if something other than this neutral
	 * value was assigned in the patch library.
	 */
	x = mainv + 6*(voicev - 64);
	if (x < 0) x = 0;

	/* Boost expression by voice volume above neutral. */
	if (voicev > 65) xpn += (voicev-64)/2;

	/* Combine multiplicative and level components. */
/** changed **/
	/* x = vel*xpn + (voicev/2)*x; */
	x = vel*xpn*2 + (voicev/4)*x;

#ifdef GUS_VOLUME
	/* Further adjustment by installation-specific master
	 * volume control (default 50).
	 */
	x = (x*GUS_VOLUME*GUS_VOLUME)/10000;
#endif

	if (x < 1) return(0);
	else if (x > GUSMAXVOL) x = GUSMAXVOL;

	/* Convert to gus's logarithmic form with 4 bit exponent i
	 * and 8 bit mantissa m.
	 */
	n = x;
	i = 7;
	if (n < 128) {
		while (i > 0 && n < (1<<i)) i--;
	}
	else while (n > 255) {
		n >>= 1;
		i++;
	}
	/* Mantissa is part of linear volume not expressed in
	 * exponent.  (This is not quite like real logs -- I wonder
	 * if it's right.)
	 */
	m = x - (1<<i);

	/* Adjust mantissa to 8 bits. */
	if (m > 0) {
		if (i > 8) m >>= i-8;
		else if (i < 8) m <<= 8-i;
	}

	return((i << 8) + m);
}


void
gus_vol(int chan, int prog, int cell, int vol)
{
	char buf[8];
	int gvolume = getval("gvolume");

	if (vol < 0 || vol > 127) {
		fprintf(stderr, "gus_vol: vol %d out of range\n", vol);
		return;
	}
	buf[0] = SEQ_PRIVATE;
	buf[1] = gus_dev;
	buf[2] = _GUS_VOICEVOL2;
	buf[3] = cell;
	*(unsigned short*)&buf[4] = gvol(vol, MAINV, XPN, gvolume);
	buf[6] = buf[7] = 0; 
	sqwrite(buf);
}


void
card_bend(int card, int cell, int up)
{
	char buf[8];
	int value = 0;

#ifdef GUS
	if (card == gus_dev) {
		value = Global_tuning;
		if (gus_tune_voice) value += gus_tune_voice - 8192;
	}
#endif

	buf[0] = SEQ_EXTENDED;
	buf[1] = SEQ_CONTROLLER;
	buf[2] = card;
	buf[3] = cell;
	buf[4] = CTRL_PITCH_BENDER;
	*(short *)&buf[5] = value + up;
	buf[7] = 0;
	sqwrite(buf);
}
/*
 * Send expression request to card.
 */
void card_expression(int card, int chan, int cell, int loud)
{
	char buf[8];

    buf[0] = SEQ_EXTENDED;
    buf[1] = SEQ_CONTROLLER;
    buf[2] = card;
    buf[3] = cell;
    buf[4] = CTRL_EXPRESSION;
    *(short *) &buf[5] = loud;
    buf[7] = 0;
    sqwrite(buf);
}
/*
 * Send main volume request to card.
 */
void card_main_volume(int card, int chan, int cell)
{
	char buf[8];
    int amount = MAINV;

    buf[0] = SEQ_EXTENDED;
    buf[1] = SEQ_CONTROLLER;
    buf[2] = card;
    buf[3] = cell;
    buf[4] = Global_main_volume;
    *(short *) &buf[5] = amount;
    buf[7] = 0;
    sqwrite(buf);
}

#endif
