/*
**	MPU2PC -- Compute pitch change track from MPU data (e.g. for SPX90)
**	psl 9/88
*/
#include	<stdio.h>
#include	<midi.h>

#define	BIGTIME		0x7FFFFFFF
#define	MAXPC		16
#define	MAXCHAN		16
#define	MAXMIDILEN	4	/* longest non-sysex MIDI command */

struct	pcstr	{
	int	out;		/* channel for pitch change info */
	int	in;		/* channel for pitch info */
	int	ref;		/* channel for pitch reference info */
} Pc[MAXPC];

char	Key[MAXCHAN];		/* current note, 0 => no note */
int	Npc	= 0;		/* number of pitch change sets */
int	Sysex	= 0;
int	Bkey	= 60;		/* base key, C3 [?] default */

main(argc, argv)
char	*argv[];
{
	register int i, chan;
	char *cp;
	long now;
	MCMD *mp;

	for (i = 1; i < argc; i++) {
	    if (argv[i][0] == '-') {
		switch (argv[i][1]) {
		case 'b':			/* base key */
		    Bkey = name2key(&argv[i][2]);
		    if (Bkey <= 0 || Bkey > 127)
			goto syntax;
		    break;
		default:
		    goto syntax;
		}
	    } else {
		for (cp = argv[i]; *cp && *cp != '='; cp++);
		if (*cp++ != '=')
		    goto syntax;
		if (Npc >= MAXPC) {
		    fprintf(stderr, "Too many pitch change specs.\n");
		    goto syntax;
		}
		Pc[Npc].out = atoi(argv[i]) - 1;
		Pc[Npc].in = atoi(cp) - 1;
		for (; *cp && *cp != '-'; cp++);
		if (*cp++ != '-')
		    goto syntax;
		Pc[Npc].ref = atoi(cp) - 1;
		if (Pc[Npc].out < 0 || Pc[Npc].out >= MAXCHAN
		 || Pc[Npc].in < 0 || Pc[Npc].in >= MAXCHAN
		 || Pc[Npc].ref < 0 || Pc[Npc].ref >= MAXCHAN)
		    goto syntax;
		Npc++;
	    }
	}
	if (Npc <= 0) {
syntax:
	    fprintf(stderr,
	     "Usage: %s [-bKEY] o=i-r ... <file1 >file2\n", argv[0]);
	    fprintf(stderr, "KEY is the base key for pitch change info.\n");
	    fprintf(stderr, "default is -bC3\n");
	    fprintf(stderr, "o is the output channel for pitch change info.\n");
	    fprintf(stderr, "i is the input channel with pitch info.\n");
	    fprintf(stderr, "r is the input channel with reference pitch.\n");
	    fprintf(stderr, "All channels are in the range 1-16.\n");
	    exit(2);
	}
	putmcmd((FILE *) 0, (MCMD *) 0);
	for (now = 0L; mp = getmcmd(stdin, now); ) {
	    now = mp->when;
	    if ((mp->cmd[0] & M_CMD_MASK) == CH_KEY_ON) {
		chan = mp->cmd[0] & M_CHAN_MASK;
		Key[chan] = mp->cmd[2]? mp->cmd[1] : 0;
		for (i = 0; i < Npc; i++)
		    if (Pc[i].in == chan || Pc[i].ref == chan)
			output(now, Pc[i].out, Key[Pc[i].in], Key[Pc[i].ref]);
	    }
	}
	for (i = 0; i < Npc; i++)
	    output(now + 1, Pc[i].out, 0, 0);	/* force out buffered output */
	exit(0);
}

static	unsigned char Cmdbuf[MAXCHAN << 2];
static	int	Lastkey[MAXCHAN]	= -1;
static	MCMD	M[MAXCHAN];

output(now, chan, inkey, refkey)
long	now;
{
	MCMD *mp;

	mp = &M[chan];
	if (now > mp->when
	 && mp->cmd[0]
	 && mp->cmd[1] != Lastkey[chan]) {
	    putmcmd(stdout, mp);
	    Lastkey[chan] = mp->cmd[1];
	}
	mp->when = now;
	mp->len = 3;
	mp->cmd = &Cmdbuf[chan << 2];
	mp->cmd[0] = CH_KEY_ON | chan;
	mp->cmd[1] = Bkey + ((inkey && refkey)? inkey - refkey : 0);
	mp->cmd[2] = 1;
}
