/*
**	PLAY -- Play midi data
**	Mike Hawley, mercilessly hacked by psl
*/
#include <stdio.h>
#include <signal.h>
#include <midi.h>
#include <mpuvar.h>

char	*Mididev	= MPU_DEV_0;
int	Simultaneous = 0;	/* if true, play files at the same time */
int	Sync = 'i';		/* clock sync spec */
int	Record = 0;		/* if true, record (overdub) while playing */
int	otty = 0;		/* if true, output is to a tty */
int	DX7 = 0;		/* if true, send DX7 init & reset sequences */
int	midi;

FILE	*F[8];
FILE	*Open(), *OpenTune();

#d Close(i) sclose(F[i]), f[i] = -1, F[i] = (FILE *)0

main(ac, av)
char	*av[];
{
	char *init_file = NULL;
	Int i, tracks=1, mode = MPU_START_PLAY;
	int f[8], n=0, FromPipe = !isatty(0), Wait=0;
	void interrupt();

	signal(SIGINT, interrupt);

	for_each_argument {
	   Case '7': DX7 = 1;
	   Case 'b': MpuSet(MPU_METRO_MEAS), MpuSet(atoi(argument));
	   Case 'c': init_file = argument;
	   Case 'M': MpuSet(MPU_METRO_ACC);
	   Case 'm': MpuSet(MPU_METRO_NO_ACC);
	   Case 'o': Mididev = argument; break;
	   Case 'r': Record = 1; mode = MPU_START_OVERDUB;
	   Case 'S': Sync = *argument;
	   Case 's': Simultaneous = 1;
	   Case 't': MpuSet(MPU_TEMPO), MpuSet(atoi(argument));
	   Case 'w': Wait = atoi(argument);
	   Case 'x': MpuSet(MPU_EXCLUSIVE_TO_HOST_ON),
		    MpuSet(MPU_SEND_MEASURE_END_OFF);
	   Default : use(1);
	}
	if (DX7)
	    dx7_clear();
	MpuSet(MPU_MIDI_THRU_OFF);
	if (i == ac) {
	    f[0] = fileno(F[0] = stdin);
	    FromPipe = isapipe(f[0]);
	} else if (Simultaneous) {
	    tracks = ac - i;
	    for (; i < ac && n < sizeof f; i++)
		if (F[n] = Open(av[i])) {
		    f[n] = fileno(F[n]);
		    FromPipe += isapipe(f[n]);
		    ++n;
		} else perror(av[i]);
	    if (n == sizeof f)
		fprintf(stderr, "max 8 files\n");
	} else if (F[0] = Open(av[i++]))
	    FromPipe = isapipe(f[0] = fileno(F[0]));
	else
	    exit(1);
	if (Wait || FromPipe)
	    sleep(Wait? Wait : 2); /* let input pipes fill up a little */
	if (!(midi = MidiPlayInit(init_file,f,tracks,mode)))
	    Error(Mididev);
	if (Simultaneous)
	    splay(midi,f,n);
	else do {
	    if (F[0]) {
		play(midi,fileno(F[0]));
		Close(0);
	    }
	} while (i < ac && (F[0] = Open(av[i++])));
	if (!Record)
	    done(midi);
	/* playing finished, continue recording until user hits a key */
	for(;;)
	    record(midi, fileno(stdout));
	/*NOTREACHED*/
}

done(fh)
{
	close(fh);
	if (DX7)
	    dx7_reset(-1);
	exit(0);
}

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

MidiPlayInit(file, fd, numtracks, mode)
char *file;
int fd[], numtracks, mode;
{
	register int i, tracks=0;
	int f = open(Mididev,2);

	if (f == -1) {
	    perror(Mididev);
	    return(0);
	}
	if (numtracks == 0)
	    numtracks = 1;
	for (i = 0; i < numtracks; i++) {
	    char s[256];
	    int n = read(fd[i],s,sizeof s);

	    tracks |= (1<<i);
	    if (MpuSetTrack(f,i) == -1 || write(f,s,n) != n) {
		perror(Mididev);
		return(0);
	    }
	    if (n < sizeof s) {
		close(fd[i]);
		fd[i] = -1;
	    }
	}
	/* send initialization sequence */
	if (file) {
	    int d;
	    FILE *ifp = sopen(file,"r");

	    if (!ifp)
		Error(file);
	    while (fscanf(ifp,"%x",&d) == 1)
		MpuSet(d);
	    sclose(ifp);
	    MpuSetTrack(f,MPU_TR_COM);
	    MpuFlush(f);
	} else {	/* default initialization sequence */
	    if (Sync == 'f')
		Sync = MPU_FSK_CLOCK;
	    else if (Sync == 'm')
		Sync = MPU_MIDI_CLOCK;
	    else
		Sync = MPU_INT_CLOCK;
	    MpuSend(f,
	     MPU_ACTIVE_TRACKS, tracks,
	     MPU_CLEAR_PLAY_COUNTERS,
	     MPU_BENDER_ON,
	     Sync,
	     mode==0? MPU_START_PLAY : mode,
	     0);
	}
	if (MpuSetTrack(f,0) == -1) {
	    perror(Mididev);
	    return(0);
	}
	return(f);
}

use(i)
{
	MidiError("Usage: %s [files or stdin]\n", av0);
	MidiError("%s\t%s\t%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 init and reset sequences\n",
	    "-b N	set # beats/measure to N\n",
	    "-c file	use 'file' to initialize MPU\n",
	    "-M	metronome on, with accent\n",
	    "-m	metronome on, no accent\n",
	    "-o dev	send output to \"dev\" in place of /dev/mpu0\n",
	    "-r	record (writing the recording to stdout)\n",
	    "-S src	sync clock to src = i (int), f (fsk), or m (MIDI in)\n",
	    "-s	play files simultaneously\n",
	    "-t N	set tempo to N (beats/minute, default=100)\n",
	    "-w	N	wait for N seconds before starting\n",
	    "-x	enable recording system exclusive data\n",
	    0);
	exit(i);
}

record(in,out)	/* copy any recorded data from midi 'in' to 'out' */
{
	char c;
	int n;

	if (!Record)
	    return;
	if (iwait(0,0)) { /* input arrived from keyboard; quit */
	    getchar();
	    done(in);
	    /*NOTREACHED*/
	}
	if (iwait(in,0) && (n = read(in,&c,1)) > 0) {
	    if (otty)
		printf("0x%02x\n",c);
	    else if (write(out,&c,n)!= n)
		return(perror("write"));
	}
}

/* true if a file desc. in 'f' is still open (ie, 'f[i] >= 0').  */
dataleft(f, nfiles)
int f[];
{
	Int i;

	loop(i,nfiles)
	    if (f[i] >= 0)
		return(1);
	return(0);
}

splay(midi,f,nfiles)		/* simultaneously play all files in 'f' */
int f[];
{
	Int n, i;
	char s[512];

	while (dataleft(f,nfiles)) {
	    for (i = 0; i < nfiles; i++) {
		if (f[i] < 0)
		    continue;
		if ((n=read(f[i],s,sizeof s)) > 0) {
		    MpuSetTrack(midi,i);
		    if (write(midi,s,n) != n) {
			perror(Mididev);
			Close(i);
		    }
		} else
		    Close(i);
		record(midi, fileno(stdout));
	    }
	}
	/* following necessary to guard against final pops */
	MpuSetTrack(midi,0);
	putTCIP(midi);
}

play(midi,f) /* play 'f' into midi device */
{
	Int n;
	char s[512];

	while ((n=read(f,s,sizeof s)) > 0)
	    if (write(midi,s,n) != n) {
		perror(Mididev);
		MpuSetTrack(midi, 0);
	    } else
		record(midi,fileno(stdout));
}

FILE *Open(s)
char *s;
{
	FILE *f = sopen(s,"r");

	return(f? f : OpenTune(s));
}

#include <sys/ioctl.h>
#include <mpureg.h>
void
interrupt()
{
/***	signal(SIGINT,SIG_DFL);	/* this line is important */
	ioctl(midi, MPU_IOC_PURGE, 0);
/****	MpuSend(midi, MPU_RESET, 0);	/****/
	done(midi);
}
