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

#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

#define	X	0	/* garbage, deleted from input */
#define	M	1	/* misc, passed untouched */
#define	S	2	/* stress, optionally passed untouched */
#define	F	3	/* syntactic structure, optionally passed untouched */
#define	V	4	/* vowel, note duration goes here */
#define	C	5	/* consonant, small duration here */

char	type[]	= {
	0,0,0,0,0,0,0,0,	/* NUL - BEL */
	0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,
	0,0,0,0,0,0,0,0,
	F,F,S,F,M,M,M,S,	/*   - ' */
	M,F,F,M,F,F,F,M,	/* ( - / */
	M,M,M,M,M,M,M,M,	/* 0 - 7 */
	M,M,M,M,M,M,M,F,	/* 8 - ? */
	V,V,M,C,C,V,M,C,	/* @ - G */
	M,V,C,M,V,V,V,V,	/* H - O */
	M,M,V,C,C,V,M,V,	/* P - W */
	M,V,C,0,M,0,V,V,	/* X - _ */
	S,V,C,V,C,V,C,C,	/* ` - g */
	C,V,M,C,C,C,C,V,	/* h - o */
	C,M,C,C,C,V,C,C,	/* p - w */
	V,C,C,M,V,M,M,X,	/* x - DEL */
};

int	Sflg	= 0;		/* allow stress marks through if != 0 */
int	Fflg	= 0;		/* allow structure marks through if != 0 */

main(argc, argv)
char	*argv[];
{
	char *phonfile;
	int i;
	long now;
	FILE *lfp;

	for (i = 1; i < argc; i++) {
	    if (argv[i][0] == '-') {
		switch (argv[i][1]) {
		case 's':		/* don't strip stress */
		    Sflg = 1;
		    break;
		case 'f':		/* don't strip syntactic struct. */
		    Fflg = 1;
		    break;
		default:
		    goto syntax;
		}
	    } else {
		phonfile = argv[i];
		if ((lfp = fopen(phonfile, "r"))) {
		    sing(stdin, lfp, stdout);
		    fclose(lfp);
		} else
		    perror(phonfile);
	    }
	}
	if (!phonfile) {
syntax:
	    fprintf(stderr,
	     "Usage: %s [-f] [-s] PHON [...] <MIDI >DT\n", argv[0]);
	    fprintf(stderr, "where:\n");
	    fprintf(stderr,
	     "  -f disables stripping of syntactic structure marks,\n");
	    fprintf(stderr, "  -s disables stripping of stress marks,\n");
	    fprintf(stderr, "  MIDI is a file of midi data,\n");
	    fprintf(stderr, "  PHON is a file of 1-char phonemes, and\n");
	    fprintf(stderr, "  DT is a file of Dectalk commands.\n");
	    exit(2);
	}
}

sing(ifp, lfp, ofp)
FILE	*ifp, *lfp, *ofp;
{
	register int i, k, v, curmode, dur, pc;
	int key, start, note, pt;
	long now;

	fprintf(ofp, "[");
	curmode = key = 0;
	now = 0;
	start = 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 == key) && start <= now) {
		    dur = MC2MS(now - start);
		    if (key) {
			while ((pc = getc(lfp)) != EOF
			    && (pt = type[pc]) != V) {
			    if (pt == M
			     || (pt == S && Sflg)
			     || (pt == F && Fflg))
				fprintf(ofp, "%c", pc);
			    else if (pt == C) {
				i = dur < 50? dur / 5 : 20;
				dur -= i;
				fprintf(ofp, "%c<%d>", pc, i);
			    }
			}
			if (pc != EOF)		/* got our vowel */
			    fprintf(ofp, "%c<%d,%d>", pc, dur, key2note(key));
			while ((pc = getc(lfp)) != EOF && type[pc] == M)
			    fprintf(ofp, "%c", pc);
			if (pc != EOF)		/* got some non-M phoneme */
			    ungetc(pc, lfp);
			fprintf(ofp, "\n");
		    } else if (dur > 1)
			fprintf(ofp, "_<%d,%d>", dur, key2note(k));
		    key = 0;
		}
		if (v != 0)			/* key-on */
		    key = k;
		start = now;
	    }
	}
	fprintf(ofp, "].\n");
}

key2note(key)
{
	register int note;

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