/*
**	MPUCLEAN -- Clean up mpu data files:
**	1) remove multiple mode commands
**	2) remove multiple note-ons (unless -m used)
**	3) remove unneeded note-offs
**	4) remove pressure change info (unless -p used)
**	5) remove Timing Clock In Stop messages (unless -tcis used)
**	6) insert TCWME commands every specified number of clocks (if -b used)
**	7) remove multiple TCWME commands (unless -tcwme used)
**	8) generate note-off commands for any notes left on
**	   & assure that a TCWME code is the last datum
**	psl 9/85
*/
#include	<stdio.h>
#include	<midi.h>

#define	MAXCHAN		16
#define	DEFBARLEN	2*MPU_CLOCK_PERIOD

int	key[MAXCHAN][128];

main(argc, argv)
char	*argv[];
{
	register int i, k, v, dt, curmode, lastmode;
	int chan, Mflg, mflg, pflg, tcisflg, tcwmeflg, barlen, status;
	long now, lasttcwme, nexttcwme;

	Mflg = mflg = pflg = tcisflg = tcwmeflg = barlen = 0;
	while (--argc > 0) {
	    if (argv[argc][0] == '-') {
		switch (argv[argc][1]) {
		case 'L':
		    barlen = atoi(&argv[argc][2]);
		    if (barlen == 0)
			barlen = DEFBARLEN;
		    break;
		case 'M':
		    Mflg++;
		    break;
		case 'm':
		    mflg++;
		    break;
		case 'p':
		    pflg++;
		    break;
		case 't':
		    if (argv[argc][2] != 'c')
			goto syntax;
		    if (argv[argc][3] == 'i')
			tcisflg++;
		    else if (argv[argc][3] == 'w')
			tcwmeflg++;
		    else
			goto syntax;
		    break;
		default:
		    goto syntax;
		}
	    } else {
syntax:
		fprintf(stderr, "Usage: %s [options] <old >new\n", argv[0]);
		fprintf(stderr, "options are:\n");
		fprintf(stderr, "-L#        put TCWMEs in every # clocks.\n");
		fprintf(stderr, "-multiple  allow duplicated notes.\n");
		fprintf(stderr, "-Multiple  make duplicated notes 'short'.\n");
		fprintf(stderr, "-pressure  allow after-touch.\n");
		fprintf(stderr, "-tcis      allow RT_TCIS.\n");
		fprintf(stderr, "-tcwme     allow multiple RT_TCWMEs.\n");
		exit(2);
	    }
	}
	curmode = lastmode = 0;
	lasttcwme = -1;
	dt = 0;
	now = 0;
	nexttcwme = barlen;
	while ((i = getc(stdin)) != EOF) {
	    if (i == RT_TCIP || i == RT_TCIS) {
		dt += MPU_CLOCK_PERIOD;
		now += MPU_CLOCK_PERIOD;
		if (barlen && now >= nexttcwme) {
		    i = now - nexttcwme;
		    putdt(dt - i);
		    putc(RT_TCWME, stdout);
		    dt = i;
		    lasttcwme = nexttcwme;
		    nexttcwme += barlen;
		} else if (i == RT_TCIS && tcisflg) {
		    putc(i, stdout);
		    dt -= MPU_CLOCK_PERIOD;
		}
		continue;
	    }
	    dt += i;
	    now += i;
	    if (barlen && now >= nexttcwme) {
		i = now - nexttcwme;
		putdt(dt - i);
		putc(RT_TCWME, stdout);
		dt = i;
		lasttcwme = nexttcwme;
		nexttcwme += barlen;
	    }
	    if ((k = getc(stdin)) == EOF) {
		fprintf(stderr, "EOF after 0x%x (dt=%d)\n", i, dt);
		exit(1);
	    }
	    if (k & M_CMD) {
		if ((k & M_CMD_MASK) == 0xF0) {	/* those F# weirdos */
		    if (k < 0xF8) {		/* sys excl or sys common */
			dt = putdt(dt);
			putc(k, stdout);
			if (k == SX_CMD) {	/* sys excl */
			    do {
				k = getc(stdin);
				putc(k, stdout);
			    } while (k != SX_EOB);
			} else if (k == SC_MSEL) {	/* song position */
			    putc(getc(stdin), stdout);
			    putc(getc(stdin), stdout);
			} else if (k == SC_SSEL)	/* song select */
			    putc(getc(stdin), stdout);
		    } else if (k == RT_TCWME) {
			if (lasttcwme != now || tcwmeflg) {
			    dt = putdt(dt);
			    putc(k, stdout);
			}
			lasttcwme = now;
		    } else {
			dt = putdt(dt);
			putc(k, stdout);
		    }
		    continue;
		}
		curmode = k;
		if ((k = getc(stdin)) == EOF) {
		    fprintf(stderr, "EOF after %x (mode)\n", curmode);
		    exit(1);
		}
	    }
	    status = (curmode & M_CMD_MASK);
	    if (status == CH_POLY_KPRS
	     || status == CH_CTL
	     || status == CH_P_BEND) {
		dt = putdt(dt);
		if (curmode != lastmode)
		    putc(lastmode = curmode, stdout);
		putc(k, stdout);
		if ((v = getc(stdin)) == EOF) {
		    fprintf(stderr, "EOF after %x (mode)\n", curmode);
		    exit(1);
		}
		putc(v, stdout);
		continue;
	    } else if (status == CH_PRG
		    || status == CH_PRESSURE) {
		if (status != CH_PRESSURE || pflg) {
		    dt = putdt(dt);
		    if (curmode != lastmode)
			putc(lastmode = curmode, stdout);
		    putc(k, stdout);
		}
		continue;
	    } else if (status == CH_KEY_ON
		    || status == CH_KEY_OFF) {
		chan = curmode % MAXCHAN;
		if ((v = getc(stdin)) == EOF) {
		    fprintf(stderr, "unexpected EOF in mode %x\n", curmode);
		    exit(1);
		}
		if (status == CH_KEY_OFF) {
		    status = CH_KEY_ON;
		    curmode = (status | chan);
		    v = 0;
		}
		if (v == 0) {
		    --key[chan][k];
		    if (key[chan][k] < 0) {	/* extra note-off */
			key[chan][k] = 0;
			continue;
		    }
		    if (!mflg && key[chan][k])
			continue;		/* not the last note-off */
		} else {
		    key[chan][k]++;
		    if (key[chan][k] > 1 && !mflg) {
			if (Mflg)
			    key[chan][k] = 1;	/* defeat nesting */
			continue;		/* extra note-on */
		    }
		}
		dt = putdt(dt);
		if (curmode != lastmode)
		    putc(lastmode = curmode, stdout);
		putc(k, stdout);
		putc(v, stdout);
	    } else
		fprintf(stderr, "mode=%x?\n", curmode);
	}
	for (chan = MAXCHAN; --chan >= 0; ) {	/* clear any stuck notes */
	    curmode = CH_KEY_ON | chan;
	    for (i = 0; i < 128; i++) {
		while (--key[chan][i] >= 0) {
		    putc(0, stdout);
		    if (lastmode != curmode)
			putc(lastmode = curmode, stdout);
		    putc(i, stdout);
		    putc(0, stdout);
		}
	    }
	}
	if (lasttcwme != now) {
	    putc(0, stdout);	/* a final command */
	    putc(RT_TCWME, stdout);
	}
}

putdt(dt)
{
	while (dt >= 240) {
	    putc(RT_TCIP, stdout);
	    dt -= 240;
	}
	putc(dt, stdout);
	return(0);
}
