#include <stdio.h>
#include <midi.h>
#include <sys/time.h>

Error(s)
{ MidiError("%s: ", av0); perror(s); exit(1); }

use(n)
{
	MidiError("use: %s [flags] > file\n", av0);
	MidiError("%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s\t%s",
	    "flags:\n",
	    "-7	send DX7 reset sequence at end\n",
	    "-B N	set # beats/measure to N\n",
	    "-c file	use 'file' to initialize MPU\n",
	    "-f	stop recording on 0xfc byte (RT_TCIS)\n",
	    "-m	metronome on, no accent\n",
	    "-M	metronome on, with accent\n",
	    "-S src	sync clock to src = i (int), f (fsk), or m (MIDI in)\n",
	    "-t N	set tempo to N (beats/minute, default=100)\n",
	    "-u	unbuffered output\n",
	    "-x	enable recording system exclusive data\n",
	    0);
	exit(n);
}

int	DX7 = 0;		/* if true, send DX7 reset sequence */
int	Fcstop = 0;		/* stop on 0xFC */
char	Sync = 'i';
int	Unbuffered = 0;

main(ac, av)
char **av;
{
	char *init_file = NULL;
	unsigned char buf[BUFSIZ];
	Int i, midi;
	int otty = isatty(1);

	MpuSet(MPU_RESET);
	for_each_argument {
	   Case '7': DX7++;
	   Case 'B': MpuSet(MPU_METRO_MEAS), MpuSet(atoi(argument));
	   Case 'c': init_file = argument;
	   Case 'f': Fcstop++;
	   Case 'M': MpuSet(MPU_METRO_ACC);
	   Case 'm': MpuSet(MPU_METRO_NO_ACC);
	   Case 'S': Sync = *argument;
	   Case 't': MpuSet(MPU_TEMPO), MpuSet(atoi(argument));
	   Case 'u': Unbuffered = 1;
	   Case 'x': MpuSet(MPU_EXCLUSIVE_TO_HOST_ON);
		     MpuSet(MPU_SEND_MEASURE_END_OFF);
	   Default : use(1);
	}
	if ((midi = open(MidiDevice, 2)) == -1) Error(MidiDevice);
	if (Unbuffered) setbuf(stdout,NULL);
	if (init_file) {
		int d;
		FILE *sopen(), *f = sopen(init_file,"r");
		if (!f) Error(init_file);
		while (fscanf(f,"%x",&d) == 1) MpuSet(d);
		sclose(f);
	} else {
		if (Sync == 'f')
		    MpuSet(MPU_FSK_CLOCK);
		else if (Sync == 'm')
		    MpuSet(MPU_MIDI_CLOCK);
		else
		    MpuSet(MPU_INT_CLOCK);
		MpuSet(MPU_BENDER_ON);
		MpuSet(MPU_MIDI_THRU_OFF);
		MpuSet(MPU_START_RECORD);
	}
	if (MpuFlush(midi)) Error(MidiDevice);
	for (;;) {
	    i = eitherwait(0, midi, 1000);
	    if (i == 0) {		/* stop on receipt of stdin */
		getchar();
		break;
	    } else if (i == midi) {
		if (read(midi, buf, 1) <= 0)
		    Error(MidiDevice);
		if (otty)
		    printf("0x%x\n", buf[0]);
		else if (write(fileno(stdout), buf, 1) != 1)
		    Error(MidiDevice);
		if (Fcstop && *buf == RT_TCIS)
		    break;
	    }
	}
	close(midi);
	if (DX7)
	    dx7_reset(-1);
	exit(0);
}

/*
 * Wait until either 'f1' or 'f2' is ready for reading, or 'timeout'.
 * Return -1 for timeout (or error), f1 for f1, or f2 for f2.
 * Example: 'eitherwait(a,b,0)' polls file descriptors a & b
 * without blocking and returns a or b if they're readable, else -1;
 * a has priority over b.
 */
eitherwait(a, b, msec)
unsigned long msec;		/* millisecs */
{
	struct timeval t;
	int readfd;

	readfd = (1 << a) | (1 << b);
	t.tv_sec = msec / 1000;
	t.tv_usec = (msec % 1000) * 1000;
	select(sizeof(int)*8,  &readfd, (int *)0, (int *)0, &t);
	if (readfd & (1 << a))
	    return(a);
	if (readfd & (1 << b))
	    return(b);
	return(-1);
}
