/*% cc -g -o # % -lmidi -lsun
*/
#include <stdio.h>
#include <midi.h>
#include <mpuvar.h>
#include <ctype.h>

int midi,
    IgnoreSpace = 0,		/* if true, don't play white space */
    SilentSpace = 0,		/* if true, play white space as silence */
    Fold = 1,			/* fold lower case to upper */
    PunctuationTempo = 12,	/* how long (in hertz) a punct note lasts */
    Tempo = 7,			/* how long a normal note lasts */
    Echo = 1,			/* echo the notes as they are played */
    RunOn = 0,			/* repeated notes are held */
    BracketVoice = -1,		/* if > -1, the current voice is taken to
				 * be 'BracketVoice', and is incremented
				 * or decremented with every grouping
				 * symbol
				 */
    BracketTempo = -1,		/* if != 0, tempo is incremented by this
				 * when we enter a bracketed group
				 */
    BracketBoost = 15,		/* amount by which grouped text (...)
				   is amplified; if 0, do nothing */
    BaseVolume = 100,		/* default volume level */
    MaxVolume = 127, MinVolume = 10,
    Crescendo = 0;		/* amount to crescendo for each line */

#define bracket(c) (index("()[]{}<>",c))
#define Lbracket(c) (index("({[<",c))
#define Rbracket(c) (index(")}]>",c))

note(n){ /* play note 'n' */
	static int v=0, length = 0;
	static int prevn = 0;
	
	++length; if (n == '\n') length = 0;
	if (!v) v = BaseVolume? BaseVolume : MaxVolume;
	if (Crescendo)
		v = length? v + Crescendo : BaseVolume;
	if (Echo) printf("%c",n),fflush(stdout);
	if (Fold && islower(n)) n = toupper(n);
	if (isspace(n)) {
		if (IgnoreSpace) return prevn = n;
		if (SilentSpace) return nap(Tempo), prevn = n;
	}
	if bracket(n){
	   if (BracketBoost) { /* raise or lower note amplitude */
		if Lbracket(n) v += BracketBoost;
		if Rbracket(n) v -= BracketBoost;
	   }
	   if (BracketVoice>0){
		int v = BracketVoice;
		if Lbracket(n) v++;
		if Rbracket(n) v--;
		if (v<0) v=0;
		if (v>31) v=31;
		if (v != BracketVoice)
			BracketVoice = v,
			dx7SetVoice(midi,v);
	   }
	   if (BracketTempo){
		int i = Lbracket(n)? BracketTempo : - BracketTempo;
		Tempo += i;
		if (Tempo < 0) Tempo = 0;
		if (Tempo > 60) Tempo = 60;
		if (PunctuationTempo){
			PunctuationTempo += i;
			if (PunctuationTempo < 0) PunctuationTempo = 0;
			if (PunctuationTempo > 60) PunctuationTempo = 60;
		}
	   }
	}
	if (v < MinVolume) v = MinVolume;
	if (v > MaxVolume) v = MaxVolume;
	if (RunOn && prevn == n) ;
	else NoteOn(midi,0,n,v);
	if (PunctuationTempo && ispunct(n)) nap(PunctuationTempo);
	else nap(Tempo);
	if (RunOn && (nextn() == n)) ;
	else NoteOff(midi,0,n);
	prevn = n;
}

char buf[200] = "";
char *bp = buf;

nextn(){
	char c = *bp? *(bp+1) : *bp;
	if (Fold && islower(c)) c = toupper(c);
	return c;
}

muzak(f) FILE *f; { /* read chars from 'f' and play them to a synth */
	if (!f) return;
	while (fgets(buf,sizeof buf,f)){
		bp = buf;
		while (*bp) { note(*bp); bp++; }
	}
	fclose(f);
}

FILE *
Open(s) char *s; {	/* fopen(f) and complain if necessary */
	FILE *f = fopen(s,"r");
	if (!f) perror(s);
	return f;
}

e(a,b){ fprintf(stderr,a,b),fprintf(stderr,"\n");}
#define dont(s) s? "don't " : ""

use(){
	e("Use: %s [-efsr] [-[cptTvbB]#] [files]",av0);
	e(" -e	%secho notes while playing them",dont(Echo));
	e(" -f	%sfold upper case to lower",dont(Fold));
	e(" -s	%signore white space",dont(IgnoreSpace));
	e(" -S	%splay white space as silence",dont(SilentSpace));
	e(" -r	%shold strings of notes",dont(RunOn));
	e(" -c#	crescendo by +# (%d) for each note to the end of each line",Crescendo);
	e(" -p#	duration of punctuation notes = # (%d)",PunctuationTempo);
	e(" -t#	note duration = # (%d)",Tempo);
	e(" -T#	{in,de}crement tempo by # (%d) in bracketed groups ([]{}<>())", BracketTempo);
	e("durations are '#' 60th's of a second.");
	e(" -v#	volume is # (%d)",BaseVolume);
	e(" -b#	boost volume of bracketed groups ([]{}<>()) by # (%d)",BracketBoost);
	e(" -B#	{in,de}crement voice in bracketed groups ([]{}<>())");
	e("    	starting at # (%d)",BracketVoice);
	e("eg, \"cat muzak.c | muzak -t5 -p12\"");
	exit(1);
}

main(ac,av) char *av[]; {
	int i;
	for_each_argument {
	Case 'e': toggle(Echo);
	Case 'f': toggle(Fold);
	Case 's': toggle(IgnoreSpace);
	Case 'S': toggle(SilentSpace);
	Case 'p': PunctuationTempo = atoi(argument);
	Case 't': Tempo = atoi(argument);
	Case 'c': Crescendo = atoi(argument);
	Case 'v': BaseVolume = atoi(argument);
	Case 'b': BracketBoost = atoi(argument);
	Case 'B': BracketVoice = atoi(argument);
	Case 'T': BracketTempo = atoi(argument);
	Case 'r': toggle(RunOn);
	Default : use();
	}
        midi = open("/dev/mpu0",2);
        if (midi == -1) perror("no midi -- bleagh"), exit(1);
        MpuSend(midi, MPU_RESET, 0);
	if (ac == i) muzak(stdin);
	else for (;i<ac;i++) muzak(Open(av[i]));
	exit(0);
}
