/*
**	MJOIN -- Join overlapped or abutted notes
**	psl 2/87
*/
#include	<stdio.h>
#include	<midi.h>

#define	MAXCHAN	16
#define	MAXKEY	128

main(argc, argv)
char	*argv[];
{
	if (argc != 1) {
	    fprintf(stderr, "Usage: %s <old >new\n", argv[0]);
	    exit(2);
	}
	join(stdin, stdout);
}

join(ifp, ofp)
FILE	*ifp, *ofp;
{
	register int i, k, v, curmode, chan;
	int key[MAXCHAN][MAXKEY];
	long pko[MAXCHAN][MAXKEY];	/* pending key off */
	long now, last;

	for (chan = MAXCHAN; --chan >= 0; )
	    for (k = MAXKEY; --k >= 0; )
		key[chan][k] = pko[chan][k] = 0;
	curmode = 0;
	now = last = 0;
	while ((i = getc(ifp)) != EOF) {
	    if (i == 0xF8) {
		now += MPU_CLOCK_PERIOD;
		continue;
	    }
	    now += i;
	    for (chan = MAXCHAN; --chan >= 0; ) {
		for (k = MAXKEY; --k >= 0; ) {
		    if (pko[chan][k] && pko[chan][k] < now) {
			put4(ofp, pko[chan][k] - last, 0x90 | chan, k, 0);
			last = pko[chan][k];
			pko[chan][k] = 0;
			key[chan][k] = 0;
		    }
		}
	    }
	    if ((k = getc(ifp)) == EOF) {
		fprintf(stderr, "EOF after 0x%x (now=%d)\n", i, now);
		exit(1);
	    }
	    if (k & 0x80) {
		if ((k & 0xF0) == 0xF0) {
		    if (k < 0xF8) {		/* sys excl or sys common */
			if (k == 0xF0) {	/* sys excl */
			    put2(ofp, now - last, k);
			    do {
				k = getc(ifp);
				putc(k, ofp);
			    } while (k != 0xF7);
			} else if (k == 0xF2) {	/* song position */
			    k = getc(ifp);
			    put4(ofp, now - last, 0xF2, k, getc(ifp));
			} else if (k == 0xF3) {	/* song select */
			    put3(ofp, now - last, k, getc(ifp));
			} else {
			    put2(ofp, now - last, k);
			}
			curmode = 0;
		    } else			/* sys real time */
			put2(ofp, now - last, k);
		    last = now;
		    continue;
		}
		curmode = k;
		if ((k = getc(ifp)) == EOF) {
		    fprintf(stderr, "EOF after %x (mode)\n", curmode);
		    exit(1);
		}
	    }
	    i = curmode & 0xF0;
	    chan = curmode % MAXCHAN;
	    if (i == 0xA0				/* poly pressure */
	     || i == 0xB0				/* mod wheel */
	     || i == 0xE0) {				/* pitch bend */
		put4(ofp, now - last, curmode, k, getc(ifp));
		last = now;
		continue;
	    } else if (i == 0xC0			/* prog change */
	     || i == 0xD0) {				/* chan pressure */
		put3(ofp, now - last, curmode, k);
		last = now;
		continue;
	    } else if (i == 0x90 || i == 0x80) {	/* key-on/off event */
		if ((v = getc(ifp)) == EOF) {
		    fprintf(stderr, "unexpected EOF in mode %x\n", curmode);
		    exit(1);
		}
		if (i == 0x80)		/* hack to turn 0x80 into 0x90 */
		    v = 0;
		if (v != 0) {			/* key-on */
		    if (key[chan][k] == 0 || join == 0) {
			put4(ofp, now - last, curmode, k, v);
			last = now;
		    }
		    key[chan][k]++;
		    if (pko[chan][k]) {
			pko[chan][k] = 0;
			--key[chan][k];
		    }
		} else {			/* key-off */
		    if (--key[chan][k] < 0) {	/* extra key-off */
			key[chan][k] = 0;
			continue;
		    }
		    if (key[chan][k] == 0 || join == 0) {
			pko[chan][k] = now;
			key[chan][k]++;
		    }
		}
	    }
	}
	for (chan = MAXCHAN; --chan >= 0; ) {
	    for (k = MAXKEY; --k >= 0; ) {
		if (pko[chan][k]) {
		    put4(ofp, pko[chan][k] - last, 0x90 | chan, k, 0);
		    last = pko[chan][k];
		}
	    }
	}
}

put2(ofp, dt, mode)
FILE	*ofp;
long	dt;
{
	if (ofp == (FILE *) 0)
	    return;
	if (dt < 0)
	    dt = 0;
	putdt(ofp, dt);
	putc(mode, ofp);
}

put3(ofp, dt, mode, n)
FILE	*ofp;
long	dt;
{
	if (ofp == (FILE *) 0)
	    return;
	if (dt < 0)
	    dt = 0;
	putdt(ofp, dt);
	putc(mode, ofp);
	putc(n, ofp);
}

put4(ofp, dt, mode, k, v)
FILE	*ofp;
long	dt;
{
	if (ofp == (FILE *) 0)
	    return;
	if (dt < 0)
	    dt = 0;
	putdt(ofp, dt);
	putc(mode, ofp);
	putc(k, ofp);
	putc(v, ofp);
}

putdt(ofp, dt)
FILE	*ofp;
long	dt;
{
	while (dt >= MPU_CLOCK_PERIOD) {
	    putc(RT_TCIP, ofp);
	    dt -= MPU_CLOCK_PERIOD;
	}
	putc((char) dt, ofp);
}
