#include <stdio.h>
#include <midi.h>
/*
**	SCAT -- Scat (on a Dectalk) a MIDI melody
**	psl 1/86
*/

#define	Dcons	6 	/* 6th csyl[] must be d for Sflg */
#define	Bcons	0 	/* 0th csyl[] must be b for Sflg */
#define	Fcons	24 	/* 24th csyl[] must be f for Yflg */
#define	Lcons	29 	/* 29th csyl[] must be l for Yflg */
char	*csyl[]	= {
	"b", "b", "b", "b", "b", "b",
	"d", "d", "d", "d", "d", "d",
	"m", "m", "m",
	"n", "n", "n",
	"k", "k",
	"p", "p",
	"t", "t",
	"f", "f",
	"sh", "v", "w", "l", "r",
};

#define	Oovow	0 	/* 0th vsyl[] must be uw for Sflg */
#define	Eevow	1 	/* 1st vsyl[] must be iy for Sflg */
#define	Aavow	2 	/* 2nd vsyl[] must be aa for Yflg */
#define	Ahvow	3 	/* 3nd vsyl[] must be ah for Yflg */
char	*vsyl[]	= {
	"uw", "iy", "aa", "ah", "aw", "ax", "ih",
	"yu", "eh", "rr", "ay", "ae", "uh", "ow",
	"em", "ix", "el",
};

#define	LASTREST	1

#define	NCSYL	(sizeof csyl / sizeof csyl[0])
#define	NVSYL	(sizeof vsyl / sizeof vsyl[0])

#define	MC2MS(x)	(5 * (x))	/* MIDI clocks to milliseconds */

#define	MAXCHAN	16
#define	MAXKEY	128
#define	UNITY	1024

int	Kflg	= 2000;		/* insert ^Ks once we're this far ahead */
int	Pflg	= 0;		/* Patter heuristic */
int	Sflg	= 0;		/* Sinatra heuristic */
int	Yflg	= 0;		/* Yule heuristic */

char	*gibber();

main(argc, argv)
char	*argv[];
{
	long now;

	if (argc > 1) {
	}
	while (--argc > 0) {
	    if (argv[argc][0] == '-') {
		switch (argv[argc][1]) {
		case 'k':
		    Kflg = atoi(&argv[argc][2]);
		    break;
		case 'p':
		    Pflg++;
		    break;
		case 's':
		    Sflg++;
		    break;
		case 'y':
		    Yflg = LASTREST;
		    break;
		default:
		    goto syntax;
		}
	    } else {
syntax:
		fprintf(stderr,
		 "Usage: %s [OPTIONS] <MIDIfile >DTfile\n", argv[0]);
		fprintf(stderr, "OPTIONS are:\n");
		fprintf(stderr, "-k#\t\tSet flush threshold to #\n");
		fprintf(stderr, "-patter\t\tEncourage repetition\n");
		fprintf(stderr, "-sinatra\tAlternate between [duw] & [biy]\n");
		fprintf(stderr, "-yule\t\tUse [faa] followed by [lah]s\n");
		exit(2);
	    }
	}
	time(&now);
	srand((int) now);
	printf("[");
	scat(stdin, stdout);
	printf("].\n");
}

scat(ifp, ofp)
FILE	*ifp, *ofp;
{
	register int i, k, v, curmode, dur;
	int scatkey, scatstart, note, num, ahead;
	long now;

	curmode = scatkey = num = ahead = 0;
	now = 0;
	scatstart = 9999;
	while ((i = getc(ifp)) != EOF) {
	    if (i == 0xF8) {
		now += MPU_CLOCK_PERIOD;
		continue;
	    }
	    now += i;
	    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 */
			    do {
				k = getc(ifp);
			    } while (k != 0xF7);
			} else if (k == 0xF2) {	/* song position */
			    getc(ifp);
			    getc(ifp);
			} else if (k == 0xF3) {	/* song select */
			    getc(ifp);
			}
			curmode = 0;
		    } else			/* sys real time */
			;
		    continue;
		}
		curmode = k;
		if ((k = getc(ifp)) == EOF) {
		    fprintf(stderr, "EOF after %x (mode)\n", curmode);
		    exit(1);
		}
	    }
	    i = curmode & 0xF0;
	    if (i == 0xA0				/* poly pressure */
	     || i == 0xB0				/* mod wheel */
	     || i == 0xE0) {				/* pitch bend */
		getc(ifp);
		continue;
	    } else if (i == 0xC0			/* prog change */
	     || i == 0xD0) {				/* chan pressure */
		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 || k == scatkey) && scatstart <= now) {
		    dur = MC2MS(now - scatstart);
		    ahead += dur;
		    if (scatkey) {
			fputs(gibber(scatkey, dur), stdout);
			if (Yflg)
			    Yflg = LASTREST + 1;	/* not a rest */
		    } else {
			if (Kflg && ahead > Kflg / 2 && dur > 200) {
			    ahead = Kflg;	/* force ^K out now */
			    dur -= 200;
			}
			if (dur > 1) {
			    fprintf(ofp, "_<%d,%d>", dur, key2note(k));
			    num = 4;		/* force new line */
			    if (Yflg)
				Yflg = LASTREST;		/* a rest */
			}
		    }
		    if (++num >= 4) {
			if (Kflg && ahead >= Kflg) {
			    ahead = 0;
			    fputs("\013\n[", stdout);
			} else
			    fputs("\n", stdout);
			num = 0;
		    }
		    scatkey = 0;
		}
		if (v != 0)			/* key-on */
		    scatkey = k;
		scatstart = now;
	    }
	}
}

char	*
gibber(key, dur)
{
	register int i, j, c, v;
	static char buf[64];
	static int num, oc[4], ov[4];

	c = rand() % NCSYL;
	v = rand() % NVSYL;
	if (Pflg) {
	    if (num < 2 || (rand() % 5) >= Pflg)	/* use new consonant */
		for (j = oc[(num-1) & 3]; c == j; c = rand() % NCSYL);
	    else if (num < 4 || rand() & 010)	/* use consonant from t - 2 */
		c = oc[(num-2) & 3];
	    else				/* use consonant from t - 4 */
		c = oc[(num-4) & 3];
	    oc[num & 3] = c;
	    if (num < 2 || (rand() % 5) >= Pflg)	/* use new vowel */
		for (j = ov[(num-1) & 3]; v == j; v = rand() % NVSYL);
	    else if (num < 4 || rand() & 010)	/* use vowel from t - 2 */
		v = ov[(num-2) & 3];
	    else				/* use vowel from t - 4 */
		v = ov[(num-4) & 3];
	    ov[num & 3] = v;
	    num++;
	}
	if (Sflg) {
	    i = (Sflg++) & 1;
	    c = i? Dcons : Bcons;
	    v = i? Oovow : Eevow;
	}
	if (Yflg) {
	    c = (Yflg == LASTREST)? Fcons : Lcons;
	    v = (Yflg == LASTREST)? Aavow : Ahvow;
	}
	i = dur < 50? dur / 5 : 20;
	j = dur - i;
	sprintf(buf, "%s<%d,%d>%s<%d>", csyl[c], i, key2note(key), vsyl[v], j);
	return(buf);
}

key2note(key)
{
	register int note;

	note = key - 35;
	while (note <= 0)
	    note += 12;
	return(note);
}
