/*
**	GC2MPU -- Convert "gc" format files to "mpu" files
**	psl 5/88
*/
#include	<midi.h>
#include	<stdio.h>

#define	P_BLOCK		0
#define	P_SPLIT		1
#define	P_SPLIT3	2
#define	P_ALT		3
#define	MAXART		16
#define	MAXVEL		16
#define	MAXEV		256
#define	REST		-1
#define	NEVER		0x7FFFFFFF;
#define	MAXF		16

int	Nmult	= 1;			/* #MULT value */
int	Cpl;				/* MPU clocks per line of input */
int	Pick;				/* picking pattern */
int	Artic[MAXART];			/* articulation array (in Aunits) */
int	Aunits	= 1000;			/* denominator for Artic[] */
int	Nartic;				/* number of entries in Artic[] */
int	Cartic	= 0;			/* current index into Artic[] */
int	Vel[MAXVEL]	= { 64 };	/* key velocity array */
int	Nvel	= 1;			/* number of entries in Vel[] */
int	Cvel	= 0;			/* current index into Vel[] */
long	T	= 0;			/* cumulative time */
int	Cbs;				/* clocks between strings in strum */
int	Nr;				/* input line number */
int	Nn[] = { 9, 11, 0, 2, 4, 5, 7, };
double	Beats	= 4.;			/* input lines per bar */
double	Tempo	= 100.;			/* arbitrarily, 100. = normal */

struct	pickstr	{
	char	*name;
	int	pick;
} Picks[]	= {
	{ "block",	P_BLOCK, },
	{ "split",	P_SPLIT, },
	{ "split3",	P_SPLIT3, },
	{ "alt",	P_ALT, },
	0,
};


#define	DEFSTYLE	"bum-chang"
struct	stylestr	{
	char	*name;
	int	pick;
	int	cbs;
	int	nartic;
	int	artic[MAXART];
	int	nvel;
	int	vel[MAXART];
} Styles[]	= {
	{ "bum-chang",	P_SPLIT,  3,
		1, { 1000, },
		1, { 64, },		},
	{ "swing",	P_BLOCK,  0,
		2, {  667,  250, },
		1, { 64, },		},
	{ "waltz",	P_SPLIT3, 3,
		3, { 1000,  500,  500, },
		1, { 64, },		},
	0,
};

extern	double	atof();

main(argc, argv)
char	*argv[];
{
	int i;
	FILE *ifp;

	ifp = stdin;
	Cpl = cplcalc(Tempo, Beats);		/* set default clocks/line */
	style(DEFSTYLE);			/* set default style */
	for (i = 1; i < argc; i++) {
	    if (argv[i][0] == '-') {
		switch (argv[i][1]) {
		default:
		    goto syntax;
		}
	    } else if (!(ifp = fopen(argv[i], "r"))) {
		perror(argv[i]);
syntax:
		fprintf(stderr, "Usage: %s [files or stdin]\n", argv[0]);
		exit(2);
	    } else
		gc2mpu(ifp);
	}
	if (ifp == stdin)
	    gc2mpu(ifp);
	exit(0);
}

gc2mpu(ifp)
FILE	*ifp;
{
	char buf[512], a[MAXF][128];
	u_char mbuf[8];
	int i, j, nf, nt, nev, note[MAXEV], kvel[MAXEV];
	int kv1, kv2, kv3, kv4;
	long bt1, et1, bt2, et2, bt3, et3, bt4, et4, tmin, when[MAXEV];
	MCMD m;

	m.cmd = mbuf;
	for (Nr = 1; fgets(buf, sizeof buf, ifp) != NULL; Nr++) {
	    nf = sscanf(buf, "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
	     a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7],
	     a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]);
	    if (nf <= 0)
		continue;
	    if (a[0][0] == '#') {
		if (strcmp(a[0], "#") == 0)
		    continue;
		if (strcmp(a[0], "#ARTIC") == 0
		 || strcmp(a[0], "#ARTICULATION") == 0) {
		    for (i = 1; i < nf; i++)
			Artic[i - 1] = 1000. * atof(a[i]);
		    Nartic = nf - 1;
		    Cartic = 0;
		} else if (strcmp(a[0], "#SPEED") == 0) {
		    Beats = atof(a[1]);
		    Cpl = cplcalc(Tempo, Beats);
		    Cartic = Cvel = 0;
		} else if (strcmp(a[0], "#MULT") == 0
			|| strcmp(a[0], "#MULTIPLICITY") == 0) {
		    Nmult = atoi(a[1]);
		    Cartic = Cvel = 0;
		} else if (strcmp(a[0], "#PICK") == 0) {
		    for (i=0; Picks[i].name && strcmp(a[1],Picks[i].name); i++);
		    if (!Picks[i].name) {
			fprintf(stderr, "Unrecognized pick pat: %s\n", a[1]);
			fprintf(stderr, "Known patterns are:");
			for (i = 0; Picks[i].name; i++)
			    fprintf(stderr, " %s", Picks[i].name);
			fprintf(stderr, "\n");
			exit(1);
		    }
		    Pick = Picks[i].pick;
		    Cartic = Cvel = 0;
		} else if (strcmp(a[0], "#STRUM") == 0) {
		    Cbs = MPU_CLOCK_PERIOD * atof(a[1]) / 2.;
		} else if (strcmp(a[0], "#STYLE") == 0) {
		    Cartic = Cvel = 0;
		    style(a[1]);
		} else if (strcmp(a[0], "#TEMPO") == 0) {
		    Tempo = atoi(a[1]);
		    Cpl = cplcalc(Tempo, Beats);
		} else if (strcmp(a[0], "#VOL") == 0
			|| strcmp(a[0], "#VOLUME") == 0
			|| strcmp(a[0], "#VEL") == 0
			|| strcmp(a[0], "#VELOCITY") == 0) {
		    for (i = 1; i < nf; i++)
			Vel[i - 1] = atoi(a[i]);
		    Nvel = nf - 1;
		    Cvel = 0;
		}
		continue;
	    }
	    for (nt = 0; nt < Nmult; nt++) {
		nev = 0;
		if (Pick == P_BLOCK) {
		    bt1 = T;
		    et1 = bt1 + (Artic[Cartic] * Cpl) / Aunits;
		    Cartic = (Cartic + 1) % Nartic;
		    kv1 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    for (i = 0; i < nf; i++) {
			when[nev] = bt1;
			note[nev] = knum(a[i]);
			kvel[nev++] = kv1;
			when[nev] = et1;
			note[nev] = note[nev - 1];
			kvel[nev++] = 0;
		    }
		} else if (Pick == P_SPLIT) {
		    bt1 = T;
		    et1 = bt1 + (Artic[Cartic] * Cpl) / (2 * Aunits);
		    Cartic = (Cartic + 1) % Nartic;
		    kv1 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    bt2 = T + Cpl / 2;
		    et2 = bt2 + (Artic[Cartic] * Cpl) / (2 * Aunits);
		    Cartic = (Cartic + 1) % Nartic;
		    kv2 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    when[nev] = bt1;
		    note[nev] = knum(a[0]);
		    kvel[nev++] = kv1;
		    when[nev] = et1;
		    note[nev] = note[nev - 1];
		    kvel[nev++] = 0;
		    for (i = 1; i < nf; i++) {
			when[nev] = bt2 + (i - nf + 1) * Cbs;
			note[nev] = knum(a[i]);
			kvel[nev++] = kv2;
			when[nev] = et2;
			note[nev] = note[nev - 1];
			kvel[nev++] = 0;
		    }
		} else if (Pick == P_SPLIT3) {
		    bt1 = T;
		    et1 = bt1 + (Artic[Cartic] * Cpl) / (3 * Aunits);
		    Cartic = (Cartic + 1) % Nartic;
		    kv1 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    bt2 = T + Cpl / 3;
		    et2 = bt2 + (Artic[Cartic] * Cpl) / (3 * Aunits);
		    Cartic = (Cartic + 1) % Nartic;
		    kv2 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    bt3 = T + (Cpl * 2) / 3;
		    et3 = bt3 + (Artic[Cartic] * Cpl) / (3 * Aunits);
		    Cartic = (Cartic + 1) % Nartic;
		    kv3 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    when[nev] = bt1;
		    note[nev] = knum(a[0]);
		    kvel[nev++] = kv1;
		    when[nev] = et1;
		    note[nev] = note[nev - 1];
		    kvel[nev++] = 0;
		    for (i = 1; i < nf; i++) {
			when[nev] = bt2 + (i - nf + 1) * Cbs;
			note[nev] = knum(a[i]);
			kvel[nev++] = kv2;
			when[nev] = et2;
			note[nev] = note[nev - 1];
			kvel[nev++] = 0;
			when[nev] = bt3 + (i - nf + 1) * Cbs;
			note[nev] = note[nev - 1];
			kvel[nev++] = kv3;
			when[nev] = et3;
			note[nev] = note[nev - 1];
			kvel[nev++] = 0;
		    }
		} else if (Pick == P_ALT) {
		    bt1 = T;
		    et1 = bt1 + (Artic[Cartic] * Cpl) / (4 * Aunits);
		    Cartic = (Cartic + 1) % Nartic;
		    kv1 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    bt2 = T + (Cpl * 1) / 4;
		    et2 = bt2 + (Artic[Cartic] * Cpl) / (4 * Aunits);
		    Cartic = (Cartic + 1) % Nartic;
		    kv2 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    bt3 = T + (Cpl * 2) / 4;
		    et3 = bt3 + (Artic[Cartic] * Cpl) / (4 * Aunits);
		    Cartic = (Cartic + 1) % Nartic;
		    kv3 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    bt4 = T + (Cpl * 3) / 4;
		    et4 = bt4 + (Artic[Cartic] * Cpl) / (4 * Aunits);
		    Cartic = (Cartic + 1) % Nartic;
		    kv4 = Vel[Cvel];
		    Cvel = (Cvel + 1) % Nvel;
		    when[nev] = bt1;
		    note[nev] = knum(a[0]);
		    kvel[nev++] = kv1;
		    when[nev] = et1;
		    note[nev] = note[nev - 1];
		    kvel[nev++] = 0;
		    when[nev] = bt3;
		    note[nev] = knum(a[1]);
		    kvel[nev++] = kv3;
		    when[nev] = et3;
		    note[nev] = note[nev - 1];
		    kvel[nev++] = 0;
		    for (i = 2; i < nf; i++) {
			when[nev] = bt2 + (i - nf + 1) * Cbs;
			note[nev] = knum(a[i]);
			kvel[nev++] = kv2;
			when[nev] = et2;
			note[nev] = note[nev - 1];
			kvel[nev++] = 0;
			when[nev] = bt4 + (i - nf + 1) * Cbs;
			note[nev] = note[nev - 1];
			kvel[nev++] = kv4;
			when[nev] = et4;
			note[nev] = note[nev - 1];
			kvel[nev++] = 0;
		    }
		}
		T += Cpl;
		for (i = 0; i < nev; i++)
		    if (note[i] == REST)
			when[i] = NEVER;
		m.len = 3;
		for (;;) {
		    tmin = NEVER;
		    j = -1;
		    for (i = 0; i < nev; i++)
			if (when[i] < tmin)
			    tmin = when[j = i];
		    if (j == -1)
			break;
		    if (tmin > T)
			tmin = T;
		    m.when = tmin;
		    mbuf[0] = CH_KEY_ON;
		    mbuf[1] = note[j];
		    mbuf[2] = kvel[j];
		    putmcmd(stdout, &m);
		    when[j] = NEVER;
		}
	    }
	    m.when = T;
	    m.len = 1;
	    mbuf[0] = RT_TCWME;
	    putmcmd(stdout, &m);
	}
}

cplcalc(t, b)
double	t, b;
{
	return((int) ((100 * 2 * MPU_CLOCK_PERIOD) / (t * b) + 0.5));
}

style(cp)
char	*cp;
{
	int i, j;

	for (i = 0; Styles[i].name && strcmp(cp, Styles[i].name); i++);
	if (!Styles[i].name) {
	    fprintf(stderr, "Unrecognized style: %s\n", cp);
	    fprintf(stderr, "Known styles are:");
	    for (i = 0; Styles[i].name; i++)
		fprintf(stderr, " %s", Styles[i].name);
	    fprintf(stderr, "\n");
	    exit(1);
	}
	Pick = Styles[i].pick;
	Nartic = Styles[i].nartic;
	for (j = Nartic; --j >= 0; Artic[j] = Styles[i].artic[j]);
	Nvel = Styles[i].nvel;
	for (j = Nvel; --j >= 0; Vel[j] = Styles[i].vel[j]);
	Cbs = Styles[i].cbs;
}

knum(code)
char	*code;
{
	register char *cp;
	int note, oct;

	cp = code;
	if (*cp == '-' || *cp == 'R')
	    return(REST);
	if (*cp < 'A' || 'G' < *cp) {
	    fprintf(stderr, "Bad note format '%s' in line %d\n", code, Nr);
	    exit(1);
	}
	note = Nn[*cp++ - 'A'];
	while (*cp == 'b' || *cp == '#')
	    note += (*cp++ == 'b'? -1 : 1);
	oct = *cp - '0';
	if (oct < -1 || oct > 9) {
	    fprintf(stderr, "Octave out of range '%s' in line %d\n", code, Nr);
	    exit(1);
	}
	return(12 + oct * 12 + note);
}
