#! /bin/sh
# This is a shell archive, meaning:
# 1. Remove everything above the #! /bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	mc
# This archive created: Thu Dec 17 21:22:03 1987
export PATH; PATH=/bin:$PATH
if test ! -d 'mc'
then
	mkdir 'mc'
fi
cd 'mc'
if test -f 'README'
then
	echo shar: will not over-write existing file "'README'"
else
cat << \SHAR_EOF > 'README'
This program (mc) has an interesting history.  It was written by Bob Brown
at Purdue U around 1982 for a terminal known as the BBN BitGraph.  It was
a success, and perhaps 80k bytes of music got entered.  He graciously gave
me a copy of the source code, and I hacked the back-end to output stuff
suitable for the Chromatics Graphics terminal (CG7900).  Later it was 
ported to several versions of the HDF (a home-brew terminal with sound 
capabilities based on the Commodore 6581 tone generators).

And now there is MIDI.  And Tim Thompson has given us "keynote", which
has a nice, readable, ASCII music representation.  ANNNNNND, there's
that 80k bytes of music....

So, once again, the back-end of mc has been ripped out and re-written.
It now produces keynote-compatible phrases.  The code has also been
re-structured into separate files to make it easier to build on PCs.

I have attempted to straighten out the comments, but consider:  it
originally worked in real-time, used floating point (since it output
note-on's in terms of Hertz!), and supported multiple musical ttys.
Treat the comments with several large grains of salt.

	Steve Falco  moss!saf	9/7/87
SHAR_EOF
fi # end of overwriting check
if test -f 'mac-make.uu'
then
	echo shar: will not over-write existing file "'mac-make.uu'"
else
cat << \SHAR_EOF > 'mac-make.uu'
begin 644 mac-make
M3T)3/2!M8U]Y+F,N;R!M8U]C+F,N;R!M8U]C,BYC+F\-0T]05$E/3E,]("U$
M1$5"54<-#6UC"<0)>T]"4WT-"6QI;FL@+7<@+6,@)TU04R G("UT("=-4%-4
M)R![3T)3?2"V#0D)(GM#3&EB<F%R:65S?2)#4G5N=&EM92YO(+8-"0DB>T-,
M:6)R87)I97-](E-T9$-,:6(N;R"V#0D)(GM#3&EB<F%R:65S?2)#4T%.14QI
M8BYO(+8-"0DB>T-,:6)R87)I97-](D-);G1E<F9A8V4N;R"V#0D)(GM,:6)R
M87)I97-](E1O;VQ,:6)S+F\@M@T)"2UO(&UC#0UM8U]Y+F,)Q EM8U]Y+GD-
F"7EA8V,@+60@;6-?>2YY#0EM=B M>2!Y+G1A8BYC(&UC7WDN8PUM
 
end
SHAR_EOF
fi # end of overwriting check
if test -f 'mc_c.c'
then
	echo shar: will not over-write existing file "'mc_c.c'"
else
cat << \SHAR_EOF > 'mc_c.c'
/*
** mc - a music compiler
**
** Written by Bob Brown '82
**
** Hacked by Steve Falco (moss!saf) '87
*/

#define EXTERN extern

#include <stdio.h>
#include <ctype.h>
#include "mc_h.h"
#include "y.tab.h"

#ifdef macintosh
# include <CursorCtl.h>
#endif

/*
**	Set up the globals as best we can.
*/

initvars()
{
	int i, j;
/*
**   GLOBAL VARIABLES for the compiler portion - always capitalized.
*/

	CurrentAmp = MAXAMP;
	CurrentKey = 0;
	CurrentTempo = DEFTEMPO;
	TimeFactor = WHOLENOTE;	/* all note durations are divided by this */
	CurVoice = 0;
/*
** This table tells how to affect notes according to the key of the music.
** 0 means the note is "as is", 1 means it is SHARP, -1 means it is FLAT.
*/
	for(i = 0; i < 12; i++)
		for(j = 0; j < 12; j++)
			Keytab[i][j] = 0; /* whitewash */

/*C */	/* 0 = C, 1 = C#, ... 11 = B */
/*Db*/	Keytab[1][2] = Keytab[1][4] = Keytab[1][7] = Keytab[1][9] = Keytab[1][11] = -1;
/*D */	Keytab[2][0] = Keytab[2][5] = 1;
/*Eb*/	Keytab[3][4] = Keytab[3][9] = Keytab[3][11] = -1;
/*E */	Keytab[4][0] = Keytab[4][2] = Keytab[4][5] = Keytab[4][7] = 1;
/*F */	Keytab[5][11] = -1;
/*Gb*/	Keytab[6][0] = Keytab[6][2] = Keytab[6][4] = Keytab[6][7] = Keytab[6][9] = Keytab[6][11] = -1;
/*G */	Keytab[7][5] = 1;
/*Ab*/	Keytab[8][2] = Keytab[8][4] = Keytab[8][9] = Keytab[8][11] = -1;
/*A */	Keytab[9][0] = Keytab[9][5] = Keytab[9][7] = 1;
/*Bb*/	Keytab[10][4] = Keytab[10][11] = -1;
/*B */	Keytab[11][0] = Keytab[11][2] = Keytab[11][5] = Keytab[11][7] = Keytab[11][9] = 1;

	for ( i=0 ; i<MAXVOICES ; i++ ) {
		VoiceList[i].dontuse = FALSE;
	}
	
	return;
} /* end initvars */

main(argc, argv)
int argc;
char *argv[];
{
	int i;
	
	initvars();
	
	procargs(argc, argv);
	
	for ( i=0 ; i<MAXVOICES ; i++ ) {
		VoiceList[i].ampl = A.volume;
		VoiceList[i].baseoctave = 0;
		VoiceList[i].deftime = WHOLENOTE / DEFTIME;
		VoiceList[i].dontcheck = TRUE;
		VoiceList[i].measures = 1;
		VoiceList[i].savelead = TRUE;
		VoiceList[i].bomtime = 0;
		VoiceList[i].stolen = 0;
	}
	CurVoice = 0;
	Voicep = &VoiceList[CurVoice];

	/*
	** Whole note is the number of milliseconds per beat when FUDGE = 60000
	** We use 1/20 of that to keep time_o_day in bounds.
	*/

	WholeNote = FUDGE / A.tempo * 4;
	MeasureLen = WHOLENOTE;
	CurrentTempo = A.tempo;

	CurrentAmp = A.volume;
	CurrentItem = newitem();
	yyparse();			/* read in the file and build some tables */
	if ( !A.norun )
		interp();		/* dump out the phrase */
		
	exit(0);
}

#ifdef macintosh
perror(x)
	char *x;
{
	fprintf(stderr, "perror - %s\n", x);
}
#endif

/*
**  Lexical routine - returns single characters
**
**  Lines beginning with # are ignored.  Also keeps track of the
**  current line and character position
*/

int Line=1;
int Char=0;
char *File = "stdin";

yylex()
{
	int c, cl;
	char combuf[256];
	
	c = getchar();
#ifdef macintosh
	SpinCursor(1);
#endif
	if ( Char == 0 ) {
		while ( c == '#' ) { /* COMMENT!! */
			while ( (c = getchar()) != '\n' )
				continue;
			Line++;
			c = getchar();
		}
	}
	
	if(c == '\n') {
		Line++;
		Char = 0;
		return(c);
	} else if(c == '{') {
		Char++;
		/* embedded comment on a note as modifier */
		for(cl = 0; (c = getchar()) != '}'; cl++) {
			if(c == '\n') {
				Line++;
				Char = 0;
			}
			combuf[cl] = c;
			Char++;
		}
		Char++;
		combuf[cl] = NULL;
		Comment_p = malloc(cl + 1); /* need space for the null */
		strcpy(Comment_p, combuf);
		return(COMMTOK);
	} else {
		Char++;
		return(c);
	}
	/* NOTREACHED */
	
}

/*
**  Error routine - prints errors suitable for emacs
**
**  For emacs, type argument-prefix compile-it mc -n FILENAME
*/

yyerror(s)
char *s;
{
#ifdef macintosh
	fprintf(stderr,"File \"%s\"; line %d # char %d, %s\n",File,Line,Char,s);
#else
	fprintf(stderr,"\"%s\", line %d: char %d, %s\n",File,Line,Char,s);
#endif
}

/*
** procargs - argument processing procedure
**
** This procedure contains the logic for converting the UNIX argument
** list into global variables
*/

procargs(argc, argv)
int argc;
char *argv[];
{
	int arg, unswitched, more, skip;
	char *swptr;
	bool useen = FALSE;
	int i, v;
	/*
	 * put default values for arguments here
	 */
	unswitched = 0;
	A.tempo = DEFTEMPO;
	A.tflag = FALSE;
	A.norun = FALSE;
	A.newline = FALSE;
	A.envfunc = 0;
	A.noiseperiod = NOISEPERIOD;
	A.envperiod = ENVELOPEPERIOD;
	A.volume = DEFVOLUME;
	A.debug = FALSE;
	A.measfirst = 0;
	A.measlast = 0x7fffffff;
	for ( i=1 ; i<MAXPSG ; i++ )
		A.devices[i] = NULL;
	A.devices[0] = stdout;
	A.numpsg = 1;
	/*
	** Loop across all arguments...
	*/
	for ( arg=1 ; arg<argc ; arg += skip+1) {
		skip = 0;
		if ( argv[arg][0] == '-' ) {
			more = 1;
			swptr = &argv[arg][1];
			while ( more && *swptr!='\0' ) {
				switch ( *swptr++ ) {
				case 'a':
					skip++;
					if ( (A.devices[A.numpsg]=fopen(argv[arg+skip],"w")) == NULL ) {
						fprintf(stderr,"Can't use %s as a PSG\n",argv[arg+skip]);
						exit(1);
					}
					A.numpsg++;
					break;
				case 's':
					A.newline = TRUE;
					break;
				case 'd':
					A.debug = TRUE;
					break;
				case 't':
					skip++;
					A.tempo = atoi(argv[arg+skip]);
					A.tflag = TRUE;
					break;
				case 'v':
					skip++;
					A.volume = atoi(argv[arg+skip]);
					break;
				case 'm':
					skip++;
					A.measfirst = atoi(argv[arg+skip]);
					skip++;
					A.measlast = atoi(argv[arg+skip]);
					break;
				case 'e':
					skip ++;
					A.envfunc = atoi(argv[arg+skip]);
					skip++;
					A.envperiod = atoi(argv[arg+skip]);
					break;
				case 'u':
					skip++;
					if ( !useen ) {
						useen = TRUE;
						for ( i=0 ; i<MAXVOICES ; i++ )
							VoiceList[i].dontuse = TRUE;
					}
					while ( isdigit(*argv[arg+skip])) {
						v = atoi(argv[arg+skip]);
						VoiceList[v-1].dontuse = FALSE;
						skip++;
					}
					skip--;
					break;
				case 'n':
					A.norun = TRUE;
					break;
				default:
					usagexit(argv[0]);
				}
			}
		} else { /* there's no dash in front */
			switch ( unswitched++ ) {
			case 0:
				if (freopen(argv[arg],"r",stdin) == NULL ) {
					perror(argv[arg]);
					exit(1);
				}
				File = argv[arg];
				break;
			default:
				usagexit(argv[0]);
			}
		}
	}
}

usagexit(pgm)
char *pgm;
{
	fprintf(stderr,"usage: %s [-v N] [-u Vs] [-e N P] [-n] [-m m1 m2] [file]\n",pgm);
	exit(1);
}

/*
** newitem - allocate and initialize a new item (a chord)
*/

item_t *
newitem()
{
	item_t *ip;
	ip = instance(_item);
	ip->staccatto = FALSE;
	ip->envelope = 0;
	ip->noise = FALSE;
	ip->numfreqs = 0;
	ip->trill = 0;
	ip->grace = FALSE;
	ip->comment = NULL;
	return ip;
}

/*
**  freq - compute a note's frequency based on its description.  This is now
**	simply an integer in an arbitrary system (I didn't intentionally try to
**	duplicate MIDI's convention - no need to.  SAF)
**
**  Take into consideration the key of the piece and the current measure.
*/

freq(note, octave)
int note, octave;
{
	int key;
	int afreq;
	
	if ( note & REST )
		return(-1);
	/*
	**  Adjust the octave based on the base octave for the voice
	*/
	afreq = Voicep->baseoctave;
	if ( note & LOWER )
		afreq -= octave+1;
	else
		afreq += octave;
		
	/*
	**	Now handle implicit and explicit accidentals
	*/
	key = note & FREQ;
	if ( note & FLAT )
		key--;
	if ( note & SHARP )
		key++;
	if ( (note & (NATURAL|FLAT|SHARP)) == 0 ) {
		switch ( Voicep->accidental[key-1] ) {
			case NATURAL:
				break;
			case SHARP:
				key++;
				break;
			case FLAT:
				key--;
				break;
			default:
				key += Keytab[CurrentKey][key-1];
		}
	}
	
	/*
	**	Straighten out the range and offsets
	*/
	if ( key <= 0 ) key += 12;
	if ( key > 12 ) key -=12;
	if( key < 10 ) {
		key += 2;
	} else {
		key -= 10;
	}
	
	/*
	**	Return a positive integer "n" where n%12 = 0 for A and n/12 is the
	**	octave (with an entirely arbitrary bias).
	*/
	return(((afreq + 8) * 12) + key);
}

/*
**	Add a note to a chord
*/

addfreq(iptr,freq)
item_t *iptr;
int freq;
{
	if ( iptr->numfreqs >= MAXFREQS )
		return;
	iptr->freq[iptr->numfreqs++] = freq;
}

/*
**	Switch keys.
*/

newkey(note)
int note;
{
	int tone;
	
	tone = note & FREQ;
	if ( note & FLAT )
		tone --;
	if ( note & SHARP )
		tone ++;
	if ( tone <= 0 ) tone += 12;
	if ( tone > 12 ) tone -=12;
	
	CurrentKey = tone-1;
}


/*
**  wolffence - handle the wolf fence command
**
**  This procedure reinitializes the voices so that almost everything above
**  the wolf fence is ignored.  The current time signature is not reset.
*/

wolffence()
{
	int i;
	for ( i=0 ; i<MAXVOICES ; i++ ) {
		VoiceList[i].ampl = A.volume;
		VoiceList[i].baseoctave = 0;
		VoiceList[i].deftime = WHOLENOTE / DEFTIME;
		VoiceList[i].dontcheck = TRUE;
		VoiceList[i].measures = 1;
		VoiceList[i].savelead = TRUE;
		VoiceList[i].bomtime = 0;
		VoiceList[i].duration = 0;
		VoiceList[i].lastending = 0;
		VoiceList[i].leadin = 0;
		VoiceList[i].repeating = FALSE;
		VoiceList[i].vhead = NULL;		/* should free	*/
		VoiceList[i].vtail = NULL;		/* up storage	*/
		VoiceList[i].stolen = 0;
	}
	CurVoice = 0;
	Voicep = &VoiceList[CurVoice];

	CurrentAmp = A.volume;
	CurrentItem = newitem();

}
/*
**  track - handle the !track command
**
**  The current voice is extended to the length (duration) of the
**  specified voice by adding notes either octave(s) above or
**  octave(s) below the notes in the named voice.
**
*/

track(oldvoice,octaves)
int oldvoice;
int octaves;
{
	register item_t *iptr, *optr;
	int dur, curdur;

	/*
	**  Locate the position in oldvoice corresponding to the
	**  current position in voice.
	*/

	if ( Voicep->duration >= VoiceList[oldvoice].duration ) {
		yyerror("can't track, current voice isn't shorter");
		return;
	}
	dur = 0;
	curdur = Voicep->duration;
	iptr = VoiceList[oldvoice].vhead;
	while ( dur < curdur ) {
		dur += iptr->time;
		optr = iptr;
		iptr = iptr->link;
	}
	if ( dur != curdur ) {

		/*
		** the length of the current voice falls in the middle of
		** the previous note in the old voice, must add just a portion
		** of it to the current voice
		*/

		yyerror("warning: when beginning tracking, voices don't align");
		trackcopy(optr,octaves,dur-curdur);

	}

	/*
	** Copy the old voice, element by element, until the end of the
	** old voice
	*/

	while ( iptr != NULL ) {
		trackcopy(iptr,octaves,iptr->time);
		iptr = iptr->link;
	}
}

/*
**  trackcopy - copy utility for the tracking option.
**
**  copy an item at *iptr into current voice, applying octave change
**  and using new time.
*/

trackcopy(iptr,octaves,newtime)
register item_t *iptr;
int octaves;
int newtime;
{
	register item_t *nptr;
	int i;

	nptr = instance(_item);
	*nptr = *iptr;
	nptr->time = newtime;
	for ( i=0 ; i<iptr->numfreqs ; i++ ) {
		nptr->freq[i] += octaves * 12;
	}
	addlist(nptr,Voicep);
}

/*
**  measureend - handle end of measures (functional)
**
**  functions to perform:
**	save leadin if Voicep->savelead is TRUE
**	check for time consistency
**	reset default accidentals
**  make sure grace notes were consumed
*/

measureend(leadin)
bool leadin;
{
	int len, i;
	char err[80], *fract();
	if ( Voicep->savelead ) {
		Voicep->savelead = FALSE;
		Voicep->leadin = Voicep->duration-Voicep->bomtime;
	}
	/*
	**  Unless turned off for this measure, perform length check on the
	**  measure.
	*/
	if ( Voicep->dontcheck )
		Voicep->dontcheck = FALSE;
	else {
		len = Voicep->duration-Voicep->bomtime+(leadin?Voicep->leadin:0);
		if ( len % MeasureLen != 0 ) {


#ifdef DEBUG
if ( A.debug )
	fprintf(stderr,
		">len=%d V->leadin=%d V->duration=%d V->bomtime=%d MeasureLen=%d\n",
		len,Voicep->leadin,Voicep->duration,Voicep->bomtime,MeasureLen);
#endif


			sprintf(err,"warning: measure is %s",fract(len,MeasureLen));
			yyerror(err);
		}
	}
	Voicep->bomtime = Voicep->duration;
	/*
	**  Reset measure accidentals
	*/
	for ( i=0 ; i<12 ; i++ )
		Voicep->accidental[i] = 0;
	
	/*
	**  Ok, Gracie
	*/
	if ( Voicep->stolen != 0 ) {
		sprintf(err,"warning: needed %d more grace",Voicep->stolen);
		yyerror(err);
		Voicep->stolen = 0;
	}
}

/*
** fract - compute fraction
*/

char *
fract(dur,len)
int dur, len;
{
	static char buf[40];
	char *size;
	int denom;
	size = dur>len ? "long" : "short";
	dur = dur % len;
	denom = WHOLENOTE;
	while ( dur % 3 == 0 && denom%3 == 0) {
		dur /= 3;
		denom /= 3;
	}
	while ( dur % 2 == 0 && denom%2 == 0) {
		dur /= 2;
		denom /= 2;
	}
	sprintf(buf,"%s %d/%d",size,dur,denom);
	return buf;
}

/*
**  dotime - implement !time command
*/

dotime(beats,note)
int beats;
int note;
{
	int i;
	TimeFactor = (WHOLENOTE * note) / 4;
	MeasureLen = (beats * WHOLENOTE) / 4;
	if ( note==8 && (beats%3)==0 )
		CurrentTempo = A.tempo * 3;
	else
		CurrentTempo = A.tempo;
	WholeNote = FUDGE / CurrentTempo * 4;
}

/*
**  template - process a note template
**
**  Note templates define the default note time and base
**  octave for the current voice.
*/

template(iptr)
register item_t *iptr;
{
	Voicep->deftime = iptr->time*WHOLENOTE/TimeFactor;
	Voicep->baseoctave = iptr->octave;
}

copylist(start,end)
item_t *start;
item_t *end;
{
	item_t *new;
	while ( start != end->link ) {
		new = instance(_item);
		*new = *start;
		addlist(new,Voicep);
		start = start->link;
	}
}

/*
** additem - add an item to a list, resolving staccatto and trills
**		as need be.  (These are still currently ignored.)
*/

additem(iptr,list)
register item_t *iptr;
register voicelist *list;
{
	long savetime;
	int to_take;

	/*
	** Staccatto takes precedence over trills and grace
	*/
	if ( iptr->staccatto ) {
		if ( iptr->time > TimeFactor/16 ) {
			savetime = iptr->time;
			iptr->time = TimeFactor/16;
			addlist(iptr,list);
			iptr = newitem();
			iptr->time = savetime-TimeFactor/16;
			iptr->numfreqs = 1;
			iptr->freq[0] = 0;
			iptr->whole = WholeNote;
			addlist(iptr,list);
		} else
			addlist(iptr,list);
	} else if ( iptr->trill > 0 ) {
		/* not yet */
	} else if ( iptr->grace ) {
		/* grace note steals time from the next note (but never a lot!) */
		list->stolen += iptr->time;
		addlist(iptr,list);
	} else if ( list->stolen != 0 ) { /* normal note and time needs to be stolen */
		if ( (to_take = iptr->time / 2) >= list->stolen ) { /* we can take that much */
			to_take = list->stolen;
		}
		list->stolen -= to_take;
		iptr->time -= to_take;
		addlist(iptr,list);
	} else /* just a normal note */
		addlist(iptr,list);
}

/*
**	Do the actual work of adding an item to a list.
*/

addlist(iptr,list)
item_t *iptr;
voicelist	*list;
{
	/*
	**  If debugging, only add measure to list if within the
	**  given bounds
	*/
	if ( list->measures < A.measfirst || list->measures > A.measlast )
		return;
	if ( list->vhead == NULL )
		list->vhead = iptr;
	else
		list->vtail->link = iptr;
	list->vtail = iptr;
	iptr->link = NULL;
	if ( list->repeating && list->rbegin == NULL )
		list->rbegin = iptr;
	list->duration += iptr->time;
}
SHAR_EOF
fi # end of overwriting check
if test -f 'mc_c2.c'
then
	echo shar: will not over-write existing file "'mc_c2.c'"
else
cat << \SHAR_EOF > 'mc_c2.c'
/*
** mc - a music compiler
**
** Written by Bob Brown '82
**
** Hacked by Steve Falco (moss!saf) and Tim Thompson (twitch!tjt) '87
*/

#define EXTERN extern

#include <stdio.h>
#include <ctype.h>
#include "mc_h.h"

#ifdef macintosh
# include <CursorCtl.h>
#endif

char *kn(), *kp;

/*
**	The following strings are keynote's notation for pitch
*/

char *XXX[] = {
	"a","a+","b","c","c+","d","d+","e","f","f+","g","g+"
};

/*
** The active voice structure is the head of a list of notes to be
** interpreted.  There is one for each voice used.  We no longer use
** newnote and timeleft, because in MIDI, there are separate note-on and
** note-off commands.  We also really don't need an array of these anymore
** because we no longer have to "look across" all lists for the next note.
*/

struct activevoice {
	item_t	*top;		/* top of remaining list		      */
	bool	done;		/* short for top==NULL			      */
	bool	dontuse;	/* TRUE => shut off by -u command argument    */
	bool	newnote;	/* UNUSED!! TRUE => top note hasn't been split yet     */
	int	timeleft;	/* UNUSED!! remaining time this note 0..N	      */
} Active[MAXVOICES];

/*
** The Note array holds all the notes to be played at any one slice
*/

struct note {
	int	ampl;
	bool	empty;
	int	envelope;
	int		freq;
	bool	newnote;
	bool	noise;
	bool	skip;
} Note[MAXNOTES+1];

/*
** Interpreter - just dump the lists since keynote can sort the stuff for us!
*/

interp()
{
	int	matchat;
	bool	matched;
	int	mindur;
	int	notes;
	int	n, f, i;
	register struct activevoice *av;
	item_t	*ip;
	char *atom;
	bool firstone;
	unsigned long time_o_day;
	int	newdur, lastdur;
	int	newvol, lastvol;
	int	newtm, lasttm;
	int	newoct, lastoct;

	for ( i=0 ; i<MAXVOICES ; i++ ) { /* do all the voices */
		av = &Active[i];
		if((av->dontuse = VoiceList[i].dontuse))
			continue;
		av->top = VoiceList[i].vhead;
		av->done = av->top==NULL;
		if ( av->done ) continue;
		firstone = TRUE;	/* first note of a list gets a MIDI channel number */
		time_o_day = 0;		/* to prevent roundoff problems, we keep absolute time */
		lastdur = lastvol = lasttm = lastoct = -100; /* something illegal */
		for(ip = av->top; ip != NULL; ip = ip->link) {	/* do all the chords in the voice */
			if(ip->numfreqs == 0) { /* this is a "comment-only" chord */
#ifdef macintosh
				SpinCursor(-1);
#endif
				/* print any comment (used for 'x' commands here) */
				if(ip->comment != NULL) {
					printf("%s", ip->comment);
				}	
			} else for(f = 0; f < ip->numfreqs; f++) { /* do the notes in the chord */
#ifdef macintosh
				SpinCursor(-1);
#endif
				if(f == 0) { /* first note of chord - always print "rests" */
					printf("%s", kn(ip->freq[f],&newoct) );

					/* The keynote expressions maintain */
					/* all the parameters from the */
					/* previous note, so we don't put */
					/* out info we don't have to. */

					if ( newoct != -100 && lastoct != newoct ) {
						printf("o%d",newoct);
						lastoct = newoct;
					}

#ifdef DEBUG
					if(A.debug) {
						lastdur = lasttm = -100; /* force time and duration to print */
					}
#endif

					newtm = time_o_day / WHOLENOTE;
					if ( (lasttm += lastdur) != newtm ) { /* must preceed lastdur = newdur */
						printf("t%d",newtm);
						lasttm = newtm;
					} 

					newdur=(ip->time * ip->whole)/WHOLENOTE;
					if ( lastdur != newdur ) {
						printf("d%d",newdur);
						lastdur = newdur;
					}
										
					newvol = knm(ip->amplitude);
					if ( lastvol != newvol ) {
						printf("v%d",newvol);
						lastvol = newvol;
					}
					
					time_o_day += ip->time * ip->whole;
					if(firstone) {
						firstone = FALSE;
						printf("c%d", i + 1); 
					}
					
					/* print any comment (used for voice changes mostly) */
					if(ip->comment != NULL) {
						printf("%s", ip->comment);
					}
					
				} else {
					if(strcmp(atom = kn(ip->freq[f],&newoct), "r") != 0) { /* real note only */
						printf(" %s", atom);
						if ( lastoct != newoct ) {
							printf("o%d",newoct);
							lastoct = newoct;
						}
					}
				}
			}
			printf(",\n");
		}
	}
	
	return;
}

/*
**	Put volume (0-15) into MIDI range (0-127)  This should perhaps be
**	log-converted.
*/

knm(i)
	int i;
{
	return(i * 8);
}

/*
**	Make a keynote symbol containing pitch and octave.
*/

char *
kn(i,octave)
int i;
int *octave;
{
	int j,k;
	static char yyy[12];
	
	if(i <= 0) {
		*octave = -100;
		return("r");
	}
	
	j = i % 12;
	k = i / 12;
	if(k < 2) k = 2;
	if(k > 12) k = 12;
	sprintf(yyy, "%s", XXX[j]);
	*octave = k-4;
	return(yyy);
}
SHAR_EOF
fi # end of overwriting check
if test -f 'mc_h.h'
then
	echo shar: will not over-write existing file "'mc_h.h'"
else
cat << \SHAR_EOF > 'mc_h.h'
/*
** mc - a music compiler
**
** Written by Bob Brown '82
**
** Hacked by Steve Falco (moss!saf) '87
*/

#include <stdio.h>
#include <ctype.h>

#ifndef TRUE
#define TRUE 1
#define FALSE 0
typedef char bool;
#endif

/*
**------------------------------------------------------------------------
**
**  Definitions of useful constants, templates, and globals.
*/

#define instance(s) (struct s *)malloc(sizeof(struct s))
#define vcheck(v) (v<0?0:((v)>MAXAMP?MAXAMP:(v)))

#define INFINITY 0x7fffffff

#define MAXFREQS          5		/* Max notes chained with & allowed	*/
#define MAXVOICES         16	/* Max voices that can be defined */
#define NPPSG             16	/* notes per synth */
#define MAXPSG            1		/* number of systems */
#define MAXNOTES  (MAXPSG*NPPSG)	/* Max notes supported */
#define MAXAMP           15		/* max amplitude (internal) */

#define DEFTEMPO        120		/* default beats-per-minute */
#define DEFTIME           4		/* quarter notes are the default */
#define DEFVOLUME        15		/* we like it loud */
#define WHOLENOTE      1152		/* 2**7 * 3**2 This is the parser's notion */
#define FUDGE		   3000		/* should yield keynote clicks */
#define NOISEPERIOD    1000
#define ENVELOPEPERIOD 8000

#define LOWER	 0100		/* fields passed around the parser for tones */
#define SHARP	 0200
#define FLAT	 0400
#define NATURAL	01000
#define FREQ	 0017		/* 1=C, 12=B */
#define REST	 0020


/*
**   A single note or chord within a voice is described by this structure
*/

typedef struct _item {
	struct _item	*link;	/* next in the piece			*/
	int	amplitude;	/* amplitude				*/
	int	envelope;	/* >0 -> apply envelope			*/
	int	freq[MAXFREQS]; /* frequency of current note		*/
	bool	noise;		/* TRUE -> apply nois generator		*/
	int	numfreqs;	/* current number of frequencies defined*/
	int	octave;		/* octave of last note, for template	*/
	bool	staccatto;	/* TRUE -> staccatto applies here	*/
	long	time;		/* time field from current note		*/
	int	trill;		/* trill number				*/
	int	whole;		/* ms per Whole note when parsed	*/
	bool	grace;	/* TRUE -> this is a grace note */
	char *comment;	/* any item can have a string printed */
} item_t;

/*
**  The head of a single voice list is an instance of this structure
*/

typedef struct {
	int	accidental[12];	/* current accidentals, this measure	*/
	int	ampl;		/* current amplitude this voice		*/
	int	baseoctave;	/* default base octave for this voice	*/
	long	bomtime;	/* duration of voice at begin-of-measure*/
	long	deftime;	/* default time-value for this voice	*/
	bool	dontcheck;	/* TRUE => don't check this measure	*/
	bool	dontuse;	/* TRUE => skip this voice		*/
	long	duration;	/* current length in 1/32 notes		*/
	int	lastending;	/* Last numbered ending encountered	*/
	int	leadin;		/* lead-in duration			*/
	int     measures;       /* syntactic measure for debugging	*/
	item_t	*rbegin;	/* ptr to beginning of repeat |:	*/
	item_t	*rend;		/* ptr to end of repeated section 1|	*/
	bool	repeating;	/* TRUE => within a repeat this voice	*/
	bool	savelead;	/* set whenever a lead-in is possible	*/
	item_t	*vhead;		/* head pointer of list of notes	*/
	item_t	*vtail;		/* tail pointer of list of notes	*/
	int		stolen;		/* time we need to steal for grace notes */
} voicelist;

/*
**	All the command line options go here.  Many don't make sense anymore.
*/

typedef struct {
	bool	debug;			/* TRUE => -d specified		      */
	FILE	*devices[MAXPSG];/* devices where PSG's are	      */
	int		envfunc;		/* non-zero => envelope function      */
	int		envperiod;		/* envelope period		      */
	bool	newline;		/* TRUE => put out newline after each */
	int		noiseperiod;	/* noise period			      */
	bool	norun;			/* TRUE => -n, don't run interpreter  */
	int		numpsg;			/* number of PSG's accessible	      */
	int		tempo;			/* tune tempo			      */
	bool	tflag;			/* TRUE => -t option specified	      */
	int		volume;			/* value of -v option		      */
	int     measfirst;      /* -m first measure		      */
	int     measlast;       /* -m last measure		      */
} opts;

/*
**   GLOBAL VARIABLES for the program - always capitalized.
*/

EXTERN int	CurrentAmp;
EXTERN int	CurrentKey;
EXTERN int	CurrentTempo;
EXTERN int	TimeFactor;		/* all note durations are divided by this */
EXTERN int	WholeNote;		/* milliseconds per whole note */
EXTERN int	CurVoice;
EXTERN int	MeasureLen;		/* Parser time per measure % this == 0 */
EXTERN item_t	*CurrentItem;
EXTERN voicelist *Voicep;
EXTERN voicelist VoiceList[MAXVOICES];
EXTERN opts A;
EXTERN int Keytab[12][12];
EXTERN char *Comment_p;		/* pointer to the current comment */

item_t *newitem();
char *malloc();
SHAR_EOF
fi # end of overwriting check
if test -f 'mc_info_old'
then
	echo shar: will not over-write existing file "'mc_info_old'"
else
cat << \SHAR_EOF > 'mc_info_old'
The following are the original opening comments for mc.  They have
been brought up-to-date in terms of new features.

SAF 9/87
===========================================================================

/*
** mc - music compiler for BBN BitGraph
**
** Bob Brown/Purdue University/CS Deptartment/February 82
**
** This program accepts musical scores transcribed in the language
** described fully by the grammar below.  A brief description is given
** below.
**
** Usage:
**	mc [ options ] inputfile
**
** Options:
**	-d ... turn on debug code (only if compiled with -DDEBUG)
**	-e N P ... apply envelope function number N with period P
**		   to all notes without explicit envelope functions.
**	-n ... compile but do not execute the score.
**	-s ... split each PSG command onto a separate line.  Useful
**		for mailing music and debugging (mc -s file | cat -v)
**	-t P ... tempo P, overrides all !tempo commands in the file.
**	-v N ... maximum volume is N, corresponding to !fff
**	-u N* ... use only voices specified (e.g. -u 1 3)
**      -m N M ... only play measures N through M
**	-a dev* ... add devices to available PSG list (eg -a /dev/tty18)
**
** The score is a mix of control-commands and note-specifications.
** Blanks are only needed between tokens (notes, commands, etc) when
** an ambiguity might otherwise result.
**
** Control-commands are of the form
**
**	!command [arguments]
**
** and include
**
**	!key of X  (default C)
**	!tempo X   (default 120)
**	!time X/Y  (default 4/4)
**      !track voice octave (current voice tracks +-octave)
**	!wolf fence (reset voices, everything above is ignored)
**	!fff !ff !f !mf !mp !p !pp !ppp (default fff)
**	!< (increase volume by 1) !< (decrease volume by 1)
**
** The music is specified by note-specifications assigned to any
** of several voice and are of the form
**
**	vN: string-of-note-specifiers (default N=1)
**
** Notes assigned to separate voices are accumulated separately
** and any repeats specified pertain ONLY to the voice in which
** they are defined.  Repeats are of two basic forms, described
** here by example
**
**	|: string-of-notes :|
**	|: string-of-notes 1| first-ending 2| second-ending :|
**
** Nested repeats are prohibited.  No more than 9 endings are
** allowed to prevent the reduce/reduce conflict between
** 83...(eighth-note triplet) and 83| (83rd ending).
**
** Vertical bars within a string-of-notes denote measure
** breaks and are used for measure-length consistency checking
** and erasing remembered accidentals.
**
** A note-specifier is either a "comment" OR a "chord".  A comment is
** any text enclosed in braces: {this is a comment}.  The comment is
** emited unchanged and may therefore be used to insert MIDI control
** information (like voice changes) in the music.
**
** A chord is one or more notes.  A time field precedes the first note
** and specifies the time for all notes of the chord.  A modifier field
** follows the last note and affects all notes of the chord.
**
** Notes explicitly flatted or sharped within a measure are
** implicitly flatted and sharped throughout the measure unless
** explicitly naturaled - in which case the implicit altering is
** stopped.
**
** A note-specifier enclosed in parentheses denotes new defaults
** for the time field and octave.  For example, (8c1) means that
** henceforth notes without time fields are eighth notes and
** the octave starting with the C above middle C is the base
** octave.  These templates only pertain to the voice in which
** they are defined.
**
** Time field:
**    blank ... quarter note or current default
**    w or h or q or 8 or s or t - note duration (whole, half, etc.)
**    a dot means add half to previous note time
**    a three means a piece of a triplet
**    time fields can be chained with underscores (bars) and are added
**
** Note field:
**    lower case letters mean default (middle) C octave or higher
**    upper case letters mean octave below default C or lower (C1->C->c->c1)
**    # means sharp
**    b means flat
**    % means natural
**    * means whatever was in that position in the last note of the
**	current voice.
**    ^ means all the notes in the previous chord of the current voice
**    a digit means number of octaves above (lower case) or below ( upper
**	case note) default c  ( e.g. a0=440hz, a1=880,A=220,A1=110)
**    can be chained with & - e.g. g&c1 make a 2-note "chord"
**
** Modifier field:
**  !N	- envelope function N (0-15)
**  n	- noise generator on this note
**  .	- staccatto (unimplemented)
**  ~   - grace note (steals its time from the time of the next note, won't
**                    ever take more than 1/2 the next note's time!)
**  {xxx} - a comment.  This is emitted after the first note of a chord, and
**           hence can be used to override volume, duration, channel, etc.
**
** Bugs:
**	1) accidentals apply only to the measure in the current voice.
**	2) it isn't always clear which options apply only to the current
**	   voice and which are global.  when in doubt, assume they apply
**	   only to the current voice.
**	3) the shift/reduce conflicts are the result of some tokens ending
**	   in numerics and numbered endings beginning with numerics.
**
** To Do:
**	1) Finish implementing stacatto.  Right now, the compiler accepts
**	   it but the interpreter does not.
**	2) Implement "nod" command.
**	3) Put longer breaks between distinct notes - because the
**	   optimizer puts out short commands on repeated identical
**	   notes, you only hear a faint click.
**	4) Finish implementing multiple-terminal support.
*/
SHAR_EOF
fi # end of overwriting check
if test -f 'mc_y.y'
then
	echo shar: will not over-write existing file "'mc_y.y'"
else
cat << \SHAR_EOF > 'mc_y.y'
%{

/*
** mc - a music compiler
**
** Written by Bob Brown '82
**
** Hacked by Steve Falco (moss!saf) '87
*/

#define EXTERN   

#include <stdio.h>
#include <ctype.h>
#include "mc_h.h"

%}

%union {
	int ival;
	item_t *iptr;
}

%type <iptr> Item

%type <ival> Timefield Time Notetime Timechar Notename Octave
%type <ival> Int Notechar Digit Signint

%type <ival> Note

%token COMMTOK

%%

File	: Optgap Tune Optgap
	;

Tune	: Token
	| Tune Optgap Token
	;

Token	: Item	{ additem ( $1, Voicep ); }

	| 'v' Int ':' {
		if ( $2 > MAXVOICES )
		yyerror("Too many voices");
		else
			CurVoice = $2 - 1;
			Voicep = &VoiceList[CurVoice];
		}
	| Repeat
	| '!' Command
	| '|' { measureend(FALSE);
		Voicep->measures++; }
	| '(' Item ')'
		{ template($2); }
	| '>'	{ Voicep->ampl = vcheck(Voicep->ampl-1);}
	| '<'	{ Voicep->ampl = vcheck(Voicep->ampl+1);}
	;

Item	: Timefield Notes Mod	{ CurrentItem->time = $1*TimeFactor/WHOLENOTE;
				  if (CurrentItem->envelope==0)
					CurrentItem->envelope = A.envfunc;
				  CurrentItem->whole = WholeNote;
				  $$ = CurrentItem;
				  CurrentItem = newitem(); }
	| COMMTOK {
			CurrentItem->numfreqs = 0;
			CurrentItem->whole = 0;
			CurrentItem->time = 0;
			CurrentItem->comment = Comment_p;
			$$ = CurrentItem;
			CurrentItem = newitem();
		}
	;

Timefield:Time			{ CurrentItem->numfreqs = 0;
				  CurrentItem->amplitude = Voicep->ampl;
				  $$ = $1; };
	;

Time	:			{ $$ = Voicep->deftime; }
	| Notetime		{ $$ = $1; }
	| Time '_' Notetime	{ $$ = $1 + $3; }
	;

Notetime: Timechar		{ $$ = $1; }
	| Timechar '.'		{ $$ = $1 + $1 / 2; }
	| Timechar '3'		{ $$ = $1 * 2 / 3; }
	;

Timechar: 'w'			{ $$ = WHOLENOTE / 1; }
	| 'h'			{ $$ = WHOLENOTE / 2; }
	| 'q'			{ $$ = WHOLENOTE / 4; }
	| '8'			{ $$ = WHOLENOTE / 8; }
	| 's'			{ $$ = WHOLENOTE / 16; }
	| 't'			{ $$ = WHOLENOTE / 32; }
	;

Notes	: Notes '&' Note	{ if ( CurrentItem->numfreqs >= MAXFREQS )
					yyerror("Too many notes");
				  else
					CurrentItem->freq[CurrentItem->numfreqs++] = $3;
				}
	| Note			{ CurrentItem->freq[CurrentItem->numfreqs++] = $1; }
	| '^'	{
				int i, j;
				
				j = Voicep->vtail->numfreqs;
				for(i = 0; i < j; i++) {
					CurrentItem->freq[i] = Voicep->vtail->freq[i];
				}
				CurrentItem->numfreqs = j;
		  		CurrentItem->octave = Voicep->vtail->octave;
			}
	;

Note	: Notename
		{ $$ = freq ( $1, 0 );
		   if ( $1 & LOWER )
			CurrentItem->octave = Voicep->baseoctave-1;
		   else
			CurrentItem->octave = Voicep->baseoctave; }
	| Notename Octave
		{ $$ = freq ( $1, $2 );
		  CurrentItem->octave = $2*($1&LOWER ? -1 : 1)-($1&LOWER?1:0); }
	| '*'
		{ $$ = Voicep->vtail->freq[CurrentItem->numfreqs];
		  CurrentItem->octave = Voicep->vtail->octave; }
	;

Notename: Notechar
	| Notechar '#'	{ $$ = $1 | SHARP;
			  Voicep->accidental[($1 & FREQ)-1] = SHARP;}
	| Notechar 'b'	{ $$ = $1 | FLAT;
			  Voicep->accidental[($1 & FREQ)-1] = FLAT;}
	| Notechar '%'	{ $$ = $1 | NATURAL;
			  Voicep->accidental[($1 & FREQ)-1] = NATURAL;}
	;

Notechar: 'c'	{ $$ = 1; }
	| 'd'	{ $$ = 3; }
	| 'e'	{ $$ = 5; }
	| 'f'	{ $$ = 6; }
	| 'g'	{ $$ = 8; }
	| 'a'	{ $$ = 10; }
	| 'b'	{ $$ = 12; }
	| 'C'	{ $$ = 1 | LOWER; }
	| 'D'	{ $$ = 3 | LOWER; }
	| 'E'	{ $$ = 5 | LOWER; }
	| 'F'	{ $$ = 6 | LOWER; }
	| 'G'	{ $$ = 8 | LOWER; }
	| 'A'	{ $$ = 10 | LOWER; }
	| 'B'	{ $$ = 12 | LOWER; }
	| 'r'	{ $$ = 1 | REST; }
	;

Octave	: Int
	;

Command : 'k' 'e' 'y' Optgap 'o' 'f' Optgap Notename
			{ newkey($8); }
	| 't' 'e' 'm' 'p' 'o' Optgap Int
			{ if ( !A.tflag ) {
				A.tempo = $7;
				CurrentTempo = A.tempo;
				WholeNote = FUDGE/ $7 * 4;
			  } }
	| 't' 'i' 'm' 'e' Optgap Int Optgap '/' Optgap Int
		{ dotime($6,$10);}
	| 't' 'r' 'a' 'c' 'k' Optgap Int Gap Signint
		{ track($7-1,$9);}
	| 'w' 'o' 'l' 'f' Optgap 'f' 'e' 'n' 'c' 'e'
			{ wolffence(); }
	| 'f' 'f' 'f'	{ Voicep->ampl = vcheck(CurrentAmp); }
	| 'f' 'f'	{ Voicep->ampl = vcheck(CurrentAmp-1); }
	| 'f'		{ Voicep->ampl = vcheck(CurrentAmp-2); }
	| 'm' 'f'	{ Voicep->ampl = vcheck(CurrentAmp-3); }
	| 'm' 'p'	{ Voicep->ampl = vcheck(CurrentAmp-4); }
	| 'p'		{ Voicep->ampl = vcheck(CurrentAmp-5); }
	| 'p' 'p'	{ Voicep->ampl = vcheck(CurrentAmp-6); }
	| 'p' 'p' 'p'	{ Voicep->ampl = vcheck(CurrentAmp-7); }
	| '>'		{ Voicep->ampl = vcheck(Voicep->ampl-1);}
	| '<'		{ Voicep->ampl = vcheck(Voicep->ampl+1);}
	;


Gap	: Gapchar
	| Gap Gapchar
	;

Optgap	:
	| Gap
	;

Gapchar : ' '
	| '\t'
	| '\n'
	;

Signint	: '+' Int	{ $$ = $2; }
	| '-' Int	{ $$ = -$2; }
	| Int
	;

Int	: Int Digit	{ $$ = $1*10 + $2; }
	| Digit
	;

Digit	: '0'		{ $$ = 0; }
	| '1'		{ $$ = 1; }
	| '2'		{ $$ = 2; }
	| '3'		{ $$ = 3; }
	| '4'		{ $$ = 4; }
	| '5'		{ $$ = 5; }
	| '6'		{ $$ = 6; }
	| '7'		{ $$ = 7; }
	| '8'		{ $$ = 8; }
	| '9'		{ $$ = 9; }
	;

Mod	:
	| Mod Modtype
	| Modtype
	;

Modtype	: '!' Int	{ CurrentItem->envelope = $2;}
	| 'n'		{ CurrentItem->noise = TRUE;}
	| '.'		{ CurrentItem->staccatto = TRUE;}
	| '~'		{ CurrentItem->grace = TRUE;}
	| COMMTOK	{ CurrentItem->comment = Comment_p;}
	;


Repeat	: '|' ':'	{ if ( Voicep->repeating )
				yyerror("Can't nest repeats");
			  else {
				Voicep->repeating = TRUE;
				Voicep->bomtime = Voicep->duration;
				Voicep->rbegin = NULL;
				Voicep->rend = NULL;
				Voicep->lastending = 0;
				Voicep->savelead = TRUE;
			  }
			  Voicep->dontcheck = TRUE;
			}

	| ':' '|'	{ if ( !Voicep->repeating )
				yyerror("Not within a repeat");
			  else if ( Voicep->lastending == 0 ) {
				measureend(TRUE);
				copylist(Voicep->rbegin, Voicep->vtail);
				Voicep->bomtime = Voicep->duration;
			  }
			  Voicep->repeating = FALSE;
			  Voicep->measures++;
			}
	| Digit '|'	{ if ( !Voicep->repeating )
				yyerror("Not within a repeat");
			  else if ( $1 != ++Voicep->lastending)
				yyerror("Endings out of order");
			  else {
				measureend($1 > 1);
				if ( Voicep->lastending == 1 )
					Voicep->rend = Voicep->vtail;
				else {
					copylist(Voicep->rbegin, Voicep->rend);
					Voicep->bomtime = Voicep->duration;
				}
			  }
			  Voicep->dontcheck = TRUE;
			  Voicep->measures++;
			}
       ;
%%
SHAR_EOF
fi # end of overwriting check
if test -f 'unix-make'
then
	echo shar: will not over-write existing file "'unix-make'"
else
cat << \SHAR_EOF > 'unix-make'
#
# makefile for mc - music compiler
#
# SAF 9/87
#
###########################################################################

OBS = mc_y.o mc_c.o mc_c2.o

mc:	$(OBS)
	cc -o mc $(OBS)

st-arc:
	arc a mc.arc mc_y.c mc_c.c mc_c2.c mc_h.h
SHAR_EOF
fi # end of overwriting check
if test -f 'y.tab.h'
then
	echo shar: will not over-write existing file "'y.tab.h'"
else
cat << \SHAR_EOF > 'y.tab.h'

typedef union  {
	int ival;
	item_t *iptr;
} YYSTYPE;
extern YYSTYPE yylval;
# define COMMTOK 257
SHAR_EOF
fi # end of overwriting check
cd ..
#	End of shell archive
exit 0
