
# This is a shell archive.  Remove anything before this line, then
# unpack it by saving it in a file and typing "sh file".  (Files
# unpacked will be owned by you and have default permissions.)
#
# This archive contains:
# README makefile pmc_c.c pmc_c2.c pmc_dur.c pmc_init.c pmc_list.c pmc_rep.c pmc_l.l pmc_y.y pmc_h.h

echo x - README
cat > "README" << '//E*O*F README//'
The following is a manual for the pmc polyrhythmic music compiler, by
Steven A. Falco, Dec 1987.  Most of the language, many of the concepts,
and some of the structure is attributable to Bob Brown's original mc
program, 1982, Purdue CS Department.  This manual is an adaptation of
the original opening comments of mc.

This program accepts musical scores transcribed in the language
described by the grammar below.  A brief description is given along
with a few examples.

Usage:
pmc [ options ] inputfile

Options:
-d ... Turn on debug code (only if compiled with -DDEBUG).  This causes
       complete time and duration information to be written to stdout
       (bypasses the optimizer).
-l ... Turn on lex debug (only if compiled with -DLEXDEBUG).
-y ... Turn on yacc debug (only if compiled with -DYYDEBUG).

The score is a mix of control-commands and chord-specifications.
(Chords consist of one or more notes which are sounded simultaneously.)
Blanks are only needed between tokens (chords, commands, etc.) when
an ambiguity might otherwise result.  Pmc is more strict in this regard
than was mc and it is recommended that whitespace separate each chord,
command, bar line, etc.  See the scores provided for a style-guide.

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)
!fff !ff !f !mf !mp !p !pp !ppp (default fff)
!< (increase volume by 1) !< (decrease volume by 1)

Note that the !track and !wolf fence commands are not implemented
in pmc (but they are not syntax errors either).

The music is specified by chord-specifications assigned to any
of several MIDI voices and are of the form:

vN: string-of-chord-specifiers (default N=1)

Chords 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-chords :|
|: string-of-chords 1| first-ending 2| second-ending :|

Nested repeats are allowed.  Any number of endings is allowed.

Chords enclosed in brackets, [ ], are part of a diversion (this is
the polyrhythmic feature).  The duration of the diversion is specified
as a time field (described below); the chords inside the diversion are
forced to fit within the duration of the diversion.  Thus, to get a
quarter-note triplet of the notes c, d, and e, the following would be
used:
    h[qc qd qe]
where the h means "pack what is inside into a half note", and what is
inside is three quarter notes (i.e 3 one-note "chords").  Diversions
can be nested and can appear inside repeats.  (Repeats cannot appear
inside diversions.)

Vertical bars within a string-of-chords denote measure
breaks and are used for measure-length consistency checking
and erasing remembered accidentals.

A chord-specifier is either a "printing comment" OR a "chord".  A
printing comment is any text enclosed in braces: {this is a printing 
comment}.  The printing comment is emited unchanged and may therefore
be used to insert MIDI control information (like voice changes) in the
music.  Note that a non-printing comment is also provided - any text
following a # is totally ignored by pmc.  (Provided the # is not
attached to a note, in which case it is a sharp!)

As mentioned above, 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.  Each octave is stored separately so c#3 does not imply
c4 is sharped.

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.  Both the time field and octave are required
(mc used to allow some defaults here...).

Time field:
   blank ... quarter note or current default
   w or h or q or i or s or t or x - note duration (whole, half, etc.
        i = eighth note, x = sixtyfourth note.)
   a dot means add half to previous note time
   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:
 .     - staccatto
 ~     - 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!  It
         actually gets half the specified duration - this is
         a cheap heuristic to get a staccatto feel.)
 {xxx} - another form of the printing comment.  This kind is emitted after
         the first note of a chord, and hence can be used to override
         volume, duration, channel, etc.

Random comments:
One of the problems with the language described above concerns ties.
If the tie is bounded by the measure, it is easy to handle, just use the
underscore notation.  So a quarter note is tied to a sixteenth note as
    q_sc
for example.  It is harder if a note crosses a bar line.  In particular,
    q_|_sc
is illegal as are any attempted variations.  The only way to handle this
is to leave out the bar line for that measure.  There are two consequences
of this: 1) measure-length audits are less precise (I perform them modulo
the time signature), and 2) accidentals carry over into the next measure
and must be explicitly natured as appropriate.

It gets even worse if a note is tied into a diversion. Consider a quarter
note tied to an eighth note triplet.  The solution here is to move the
quarter note into the diversion.  This requires extending the length of the
diversion by the length of (in this case) a quarter note - further, the
quarter note must be scaled by (in this case) the triplet ratio, so that
when all is said and done, the quarter note retains the correct duration.
Example (assume qc is to be tied to q[ic id ie]):
    q_q[q._ic id ie]
In other words, the diversion duration is q_q where the first q is for the
qc and the second q is for the triplet.  Since everything inside the
brackets will be scaled by 3/2 (triplet), the quarter note must be
scaled by 3/2 to correctly bring it inside.  This is done by adding
a '.'; q_i_ic would also work.

For a truly horrible example, see the score for "The Black Page."  It
contains the diversion:
    q_h_h[g# f# c# e s_h_i_h_if#]
This represents a quarter-note pentuplet of g#, f#, c#, e, and f#, tied
to a pair of half-note f#'s (one of which extended across a bar line!!!)
In order to bring the h_hf# inside, each h was lengthened by a factor
of 5/4 (pentuplet): 1/2 * 5/4 = 5/8 = 1/2 + 1/8 or h_i.  So the final
notation for the sixteenth-pentuplet tied to the two half notes is:
    s_h_i_h_i
(Stop that groaning!!!!)

Lead in's:
I took a stab at lead-in support.  The first time a measure is seen which
is of an incorrect duration (not evenly divisible by the time signature),
it is recorded as the lead-in amount.  It is expected that this occurs in
the first measure of the song but it doesn't have to.  It is also expected
that the last measure of the song will be short by this amount.  In
actuality, the lead-in can be used up by any measure, and several lead-ins
can occur in a given song (provided an old one is used before a new one is
given).  Call it a heuristic or call it a kludge - it works fairly well but
can certainly be fooled.

Audits:
Several audits are performed by pmc.  Most notable are measure length,
voice length, and repeat/diversion depth audits.  As always, diagnostic
meassages are intended to be self-explanitory :-)

/* end */
//E*O*F README//

echo x - makefile
cat > "makefile" << '//E*O*F makefile//'
#
# unix makefile for pmc
#
# Steven A. Falco 12/87
#
###########################################################

OBS= pmc_y.o pmc_l.o pmc_c.o pmc_init.o \
	pmc_dur.o pmc_list.o pmc_c2.o pmc_rep.o

YFLAGS= -d

pmc	:	$(OBS)
	cc $(OBS) -o pmc -ll

pmc_y.c	:	pmc_y.y
	yacc $(YFLAGS) pmc_y.y
	mv y.tab.c pmc_y.c
	mv y.tab.h pmc_y.h
//E*O*F makefile//

echo x - pmc_c.c
cat > "pmc_c.c" << '//E*O*F pmc_c.c//'
/*
 * pmc - a polyrhythmic music compiler
 *
 * based heavily on Bob Brown's original 1982 mc program
 *
 * Steven A. Falco  12/87
 */

#define EXTERN   

#include <stdio.h>
#include "pmc_h.h"

main(argc, argv)
	int argc;
	char *argv[];
{
	extern char *optarg;
	extern int optind;
	
	int c;
	int err;

	init_vars();		/* set up many global variables and tables */
	init_voices();		/* set up all the voice structures */

	err = 0;
	while((c = getopt(argc, argv, "dly")) != EOF) {
		switch(c) {
			case 'd':
#ifdef DEBUG
				Debug = 1;
#else
				fprintf(stderr, "debug is compiled out\n");
				err++;
#endif
				break;
			case 'l':
#ifdef LEXDEBUG
				debug = 1; /* global */
#else
				fprintf(stderr, "lex debug is compiled out\n");
				err++;
#endif
				break;
			case 'y':
#ifdef YYDEBUG
				yydebug = 1; /* global */
#else
				fprintf(stderr, "yacc debug is compiled out\n");
				err++;
#endif
				break;
			case '?':
				err++;
				break;
		}
	}
	if(err) {
		use();
		exit(1);
	}
	if(optind < argc) {
		if(freopen(argv[optind], "r", stdin) == NULL) {
			fprintf(stderr, "can't open %s\n", argv[optind]);
			exit(1);
		}
		File = argv[optind];
	}

	yyparse();		/* build up the linked lists based on users code */

	audit();		/* do some time consistency checks and promote time */
	
	generate();		/* generate keynote phrase from linked lists */

	exit(0);
}

use()
{
	fprintf(stderr, "pmc [-d] [-l] [-y] [file]\n");
	fprintf(stderr, " -d means debug  - prints uncompressed keynote code\n");
	fprintf(stderr, " -l means lex debug - prints lex trash\n");
	fprintf(stderr, " -y means yacc debug - prints parser trash\n");
	fprintf(stderr, " file is the source - stdin is read if no file is given\n");

	return;
}

/* We wish to perform a length audit on each active voice.  They really should
 * all be the same length.  And all repeats and polyrhythms ought to have ended.
 * Another check is that the lead_in is either 0 (consumed) or a whole measure
 * (meaning there really wasn't one).  Finally, we promote time for the first
 * (top) chord.
 */
audit()
{
	int i;
	int first_one;
	int first_id;
	DUR_TYPE the_dur, another, y;
	VOICE_TYPE *x;
	
	first_one = TRUE;
	first_id = 0;
	
	/* voice 0 is not used */
	for(i = 0; i < MAXVOICES; i++) {
		Line = -1; /* end of file - change error message format */
		x = &Voices[i];
		if(x->first->head != C_NULL) { /* it's active */
			if(i == 0) {
				yyerror("voice 0 (trash voice) is active!");
				continue;
			}
			if(first_one) { /* the once we judge all others against */
				first_one = FALSE;
				first_id = i;
				the_dur = x->first->enclosed_dur;
			} else { /* one to be judged */
				another = dur_sub(the_dur, x->first->enclosed_dur);
				if(another.num != 0) { /* trouble! */
					char *p;
					
					sprintf(Err_buf, "voice %d is ", i);
					if(another.num < 0) {
						another.num = -another.num;
						strcat(Err_buf, "bigger");
					} else {
						strcat(Err_buf, "smaller");
					}
					
					p = Err_buf + strlen(Err_buf);
					sprintf(p, " than voice %d by %d/%d",
						first_id, another.num, another.den);
					yyerror(Err_buf);
				}
			}
			
			/* check for balenced [] and |: :| */
			if(x->r_depth != 0) {
				sprintf(Err_buf, "voice %d has unmatched repeat(s) %d",
					i, x->r_depth);
				yyerror(Err_buf);
			}
			if(x->p_depth != 0) {
				sprintf(Err_buf, "voice %d has unmatched polyrhythms(s) %d",
					i, x->p_depth);
				yyerror(Err_buf);
			}
			
			/* see if the lead_in is ok */
			if(
				(x->lead_in.num != -1) &&	/* never set */
				(x->lead_in.num != 0)		/* consumed */
			) { /* we could be in trouble unless it matches the (old) TS */
				y = dur_div(x->lead_in, x->lead_sig);
				y.num = y.num / y.den;
				y.den = 1;
				y = dur_sub(x->lead_in, dur_mul(y, x->lead_sig));
			
				if(y.num != 0) { /* yep - we are in trouble! */
					Line = x->lead_line; /* restore diagnostic pointers */
					Char = x->lead_char;
					sprintf(Err_buf, "voice %d, lead_in error = %d/%d",
					  i, y.num, y.den);
					yyerror(Err_buf);
				}
			}
			
			/* promote time */
			x->first->duration = x->first->enclosed_dur;
		}
	}
	
	return;
}

/* We must accumulate any pitch-modifiers (accidentals)
 * into the "accidental table".  We must also adjust the
 * pitch for 1) octave, 2) explicit accidentals, 3)
 * implicit accidentals, and 4) key signature.
 */
	unsigned char
do_pitch(pitch, direction, modifier, octave)
	unsigned char pitch;
	char direction;
	int modifier;
	int octave;
{
	int i, j;

	i = (int) pitch;

	/* First of all, rests are special */
	if(i == REST) {
		return(REST);
	}
	
	/* Adjust for the octave so we know roughly where we are.  There are
	 * two components: the explicit octave, and any default (template).
	 * The template is an offset.
	 */
	i += PER_OCTAVE * ((direction * octave) + CurV->base_octave);
	
	/* Do a range check on "i".  We will have to do this again at the end.
	 * We fold to the nearest legitimate octave and issue a warning.
	 */
	if((j = pitch_check(i)) != i) { /* it changed - update and warn */
		i = j;
		yyerror("pitch out of range");
	}

	/* If present, enter the accidental into the table, wiping out any
	 * previous entry.  Once this is done, we can treat implicit and 
	 * explicit accidentals the same.  Note that there is a separate
	 * entry for each line of the staff (so c#3 doesn't imply c4 is sharped)
	 * and there is also a separate entry for each voice (instrument).
	 */
	if(modifier != NOACCIDENT) {
		CurV->accidental[i] = modifier;
	}
	
	/* Accidentals override any key signature information - they don't add!
	 * Note that the KeyTable is global to all octaves and voices.
	 */
	if(CurV->accidental[i] == NOACCIDENT) { /* use the key signature */
		i += KeyTable[CurrentKey][i % PER_OCTAVE];
	} else {
		i += CurV->accidental[i];
	}
	
	/* Do the range check again since we may have just gone out by a
	 * bit.
	 */
	if((j = pitch_check(i)) != i) { /* it changed - update and warn */
		i = j;
		yyerror("pitch out of range");
	}

	return(i);
}

/* Simply figure the index within the keytable (0 to 11) where 0 is c, 1 is
 * db, etc.
 */
do_key(pitch, modifier)
	unsigned char pitch;
	int modifier;
{
	int i, j;

	i = (int) pitch;

	/* rests are bozo - pretend key of c */
	if(i == REST) {
		yyerror("can't have key of REST");
		return(MIDI_C % PER_OCTAVE);
	}
	
	/* adjust for any explicit accidentals */
	if(modifier != NOACCIDENT) {
		i += modifier; /* can't go out of range */
	}
	
	return(i % PER_OCTAVE);
}

/* force the pitch to be in the MIDI range by folding to the nearest octave */
pitch_check(i)
	int i;
{
	while(i < 0) {
		i += PER_OCTAVE;
	}

	while(i >= MAXPITCH) {
		i -= PER_OCTAVE;
	}

	return(i);
}

yyerror(s)
	char *s;
{
	if(Line >= 0) {
#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
	} else {
#ifdef macintosh
		fprintf(stderr,"File \"%s\" # %s\n", File, s);
#else
		fprintf(stderr,"\"%s\": %s\n", File, s);
#endif
	}
	
	return;
}

check_measure(j, save_lead)
	CHORD_TYPE *j;
	int save_lead;
{
	DUR_TYPE i, x;

/* sprintf(Err_buf, "dur %d/%d, encd %d/%d, last %d/%d",
j->duration.num, j->duration.den, j->enclosed_dur.num, j->enclosed_dur.den,
CurV->last_dur.num, CurV->last_dur.den);
yyerror(Err_buf); */

	/* Compute how long this measure is.  See if it is modulo the
	 * time signature.  Update the last_dur value.  Note that we must
	 * reset the last_dur each time we alter the diversion depth (in the yacc
	 * code) because last_dur is "local."
	 */

	i = dur_sub(j->enclosed_dur, CurV->last_dur); /* how long is the measure? */
	CurV->last_dur = j->enclosed_dur; /* reset the beginning point */

	if(i.num < 0) {
		yyerror("internal error: measure length is negative!");
	}
	
	x = dur_div(i, TimeSignature); /* need the integer part - first get ratio */
	x.num = x.num / x.den; /* now remove the residue */
	x.den = 1;
	x = dur_sub(i, dur_mul(x, TimeSignature)); /* remove the whole-measure part */

	/* If we are just starting out, we may have seen a lead_in.  In this case,
	 * we record it and don't announce an error.  The error will still get 
	 * announced at the very end if we don't use the lead_in.  Note: we only
	 * allow a lead_in on bar lines and repeat_start - not on repeat_mid or
	 * repeat_end.  Also, we don't save a lead_in of 0 to allow for leading 
	 * bar lines and repeat_starts.
	 *
	 * Note that we record the time signature at the time of the lead_in.
	 * This is needed both for the audit at the end of the song, and so that
	 * we only try and apply lead_ins to matching time signatures.  We also
	 * record the file position for a better diagnostic.
	 */
	if(save_lead && (CurV->lead_in.num == -1) && (x.num != 0)) {
		CurV->lead_in = x;	/* save it */
		CurV->lead_sig = TimeSignature;
		CurV->lead_line = Line;
		CurV->lead_char = Char;
		x = D_0;			/* and pretend it worked out */
	}
	
	/* We really have no way of knowing when to apply the lead_in so we try
	 * it both ways.  The only way we can lose is if two errors cancel
	 * out - this is pretty unlikely.
	 */
	if(x.num != 0) { /* we should get 0 for x */
		/* but if not, lets try using the lead_in if it exists and TS matches */
		if(
			(CurV->lead_in.num != -1) &&
			(CurV->lead_sig.num == TimeSignature.num) &&
			(CurV->lead_sig.den == TimeSignature.den)	
		) { /* it may still be correct */
			i = dur_add(i, CurV->lead_in);
			x = dur_div(i, TimeSignature);
			x.num = x.num / x.den;
			x.den = 1;
			x = dur_sub(i, dur_mul(x, TimeSignature));
			
			if(x.num != 0) { /* still couldn't get a zero! */
				sprintf(Err_buf, "measure length error: amount = %d/%d", x.num, x.den);
				yyerror(Err_buf);
			} else { /* got zero - consume the lead_in; allow for re-use */
				CurV->lead_in = D_ILL;
			}
		} else { /* no appropriate lead_in - we failed for sure */
			sprintf(Err_buf, "measure length error: amount = %d/%d", x.num, x.den);
			yyerror(Err_buf);
		}
	}
	
	/* see if the grace notes were all consumed */
	if(CurV->stolen.num != 0) {
		sprintf(Err_buf,"warning: needed %d/%d more grace",
			CurV->stolen.num, CurV->stolen.den);
		yyerror(Err_buf);
		CurV->stolen = D_0;
	}

	clear_accident();
	
	return;
}

template(dur, pch, dir, oct)
	DUR_TYPE dur;
	unsigned char pch;
	char dir;
	int oct;
{
	int i;
	
	if(pch == REST) {
		yyerror("template contains REST");
	}
	
	/* Provide a very rough sanity check on the octave.  Final check is in
	 * do_pitch().
	 */
	if((i = (oct * dir) + (dir < 0 ? dir : 0)) < -MAXOCT || i > MAXOCT) {
		yyerror("octave out of range in template");
		i = 0;
	}
	
	CurV->base_octave = i;
	CurV->def_dur = dur;
	
	return;
}

do_octave(i)
	unsigned char i;
{
	int j;
	
	/* First of all, rests are special */
	if(i == REST) {
		return(REST);
	}
		
	/* We subtract C_OCTAVE to make this an offset, and subtract the
	 * old base since it was added in during do_pitch().
	 */
	j = ((i / PER_OCTAVE) - C_OCTAVE) - CurV->base_octave;
	
	return(j);
}

mod_clear()
{
	/* We reset the modifier globals each time we start a new modifier group */
	Modifiers.staccatto = FALSE;
	Modifiers.grace = FALSE;
	Modifiers.comm = NULL;

	return;
}

/* end */
//E*O*F pmc_c.c//

echo x - pmc_c2.c
cat > "pmc_c2.c" << '//E*O*F pmc_c2.c//'
/*
 * pmc - a polyrhythmic music compiler
 *
 * based heavily on Bob Brown's original 1982 mc program
 *
 * Steven A. Falco  12/87
 *
 * Optimizer code by Tim Thompson
 */

#define EXTERN extern

#include <stdio.h>
#include "pmc_h.h"

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

int	newdur, lastdur;
int	newvol, lastvol;
int	newtm, lasttm;
int	newoct, lastoct;
int the_voice;
int first_one;

/* The data structures are now built.  Let's generate some keynote code! */
generate()
{
	/* loop over all the voices - note that 0 is not used */
	for(the_voice = 1; the_voice < MAXVOICES; the_voice++) {
		/* make sure there is something there */
		if(Voices[the_voice].first->head != C_NULL) {
			first_one = TRUE;	/* first note of a list gets a MIDI channel number */
			lastdur = lastvol = lasttm = lastoct = ILLKEY; /* something illegal */
			Time_of_day = D_0;
			expand(Voices[the_voice].first, D_1);
		}
	}
	
	return;
}

/* There are two chains in the data-structure: "head" and "next".  "Head" must
 * be expanded first.  We do this by recursion since we have to keep track of
 * the location of this block (so we can find its "next"); we also have to keep
 * track of the current scale factor.  Recursion is good for this since the
 * number of nested diversions is small.  We *could* use the same technique to
 * recursively expand the "next" pointers.  But we don't have to, since we are
 * not going to need the block anymore.  Also, we can't afford to, since the max
 * stack depth would be equal to the number of notes in the song.  So we use
 * an inner do-while loop instead.  Ack Oop.
 */
expand(i, s)
	CHORD_TYPE *i;
	DUR_TYPE s;
{
	/* We know "i" itself is not C_NULL. */
	do{
		/* look for diversions before going after "next" pointers */
		if(i->head != C_NULL) {
		
			/* This is a diversion.  It may be part of a repeat or it may be
			 * from a polyrhythm.  In any case, we compute the ratio of
			 * "internal" and "external" time (which will always be 1/1 for
			 * repeats), and recursively expand with the new scale factor.
			 */
			expand(i->head, dur_mul(dur_div(i->duration, i->enclosed_dur), s));
		} else {
			
			/* this is a simple chord - just dump it */
			dump_chord(i, s);
		}
		
		/* When we get here, the "head" chain has been exhausted. */
		i = i->next;
		
	} while(i != C_NULL); /* Keep going till we come to the end of the "next"
						   * chain.  At that point, we return and maybe go down
						   * the next "head" chain.
						   */
		
	return;
	
}

dump_chord(i, s)
	CHORD_TYPE *i;
	DUR_TYPE s;
{
	int j;
	char *atom;	
	DUR_TYPE scale;

	scale = dur_mul(i->duration, s);	/* compute the scaled duration */
	scale.num *= i->scale_factor;		/* adjust for tempo and time signature */
	
	if(i->numfreqs == 0) { /* this is a "comment-only" chord */
#ifdef macintosh
		SpinCursor(-1);
#endif
		/* print any comment (used for 'x' commands here) */
		if(i->comment != NULL) {
			printcom(i->comment);
		}	
	} else {
		for(j = 0; j < i->numfreqs; j++) { /* do the notes in the chord */
#ifdef macintosh
			SpinCursor(-1);
#endif
			if(j == 0) { /* first note of chord - always print "rests" */
				printf("%s", key_tone(i->freq[j], &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 != ILLKEY && lastoct != newoct) {
					printf("o%d",newoct);
					lastoct = newoct;
				}
	
#ifdef DEBUG
				if(Debug) {
					lastdur = lasttm = ILLKEY; /* force time and duration to print */
				}
#endif
	
				newtm = Time_of_day.num / Time_of_day.den;
				if((lasttm += lastdur) != newtm) { /* must preceed lastdur = newdur */
					printf("t%d",newtm);
					lasttm = newtm;
				} 
	
				newdur = scale.num / scale.den;
				if(lastdur != newdur) {
					printf("d%d",newdur);
					lastdur = newdur;
				}
									
				newvol = i->amplitude;
				if(lastvol != newvol) {
					printf("v%d",newvol);
					lastvol = newvol;
				}
				
				Time_of_day = dur_add(Time_of_day, scale);
				if(first_one) {
					first_one = FALSE;
					printf("c%d", the_voice); 
				}
				
				/* print any comment (used for voice changes mostly) */
				if(i->comment != NULL) {
					printcom(i->comment);
				}
				
			} else { /* it is the second or later note */
				if(strcmp(atom = key_tone(i->freq[j], &newoct), "r") != 0) {
					/* we want "real" notes only */
					printf(" %s", atom);
					if(lastoct != newoct) {
						printf("o%d", newoct);
						lastoct = newoct;
					}
				}
			}
		} /* loop over all notes in the chord */
		printf(",\n");
	}
}

/* Convert a MIDI pitch (integer) to a keynote pitch (ascii).  As a side
 * effect, modify the incoming octave (pointer).
 */
	char *
key_tone(i, octave)
	int i;
	int *octave;
{

	static char *XXX[] = {
		"c", "c+", "d", "d+", "e", "f", "f+", "g", "g+", "a", "a+", "b"
	};
	int the_pitch, the_octave;
	static char yyy[12];
	
	if(i == REST) {
		*octave = ILLKEY;
		return("r");
	}
	
	the_pitch = i % 12;
	the_octave = i / 12;
	sprintf(yyy, "%s", XXX[the_pitch]);
	*octave = the_octave - 1;
	return(yyy);
}

/* print a comment and expand any escape characters */
printcom(s)
	char *s;
{
	char *p;
	
	for(p = s; *p; p++) { /* loop through the comment */
		if(*p != '\\') { /* normal character */
			putchar(*p);
		} else {
			switch(*++p) {
				case 0:
					return; /* that's not funny! */
				case 'n':
					putchar('\n');
					break;
				case 't':
					putchar('\t');
					break;
				case 'b':
					putchar('\b');
					break;
				case 'r':
					putchar('\r');
					break;
				case 'f':
					putchar('\f');
					break;
				case '\\':
					putchar('\\');
					break;
			}
		}
	}
	
	return;
}

/* end */
//E*O*F pmc_c2.c//

echo x - pmc_dur.c
cat > "pmc_dur.c" << '//E*O*F pmc_dur.c//'
/*
 * pmc - a polyrhythmic music compiler
 *
 * based heavily on Bob Brown's original 1982 mc program
 *
 * Steven A. Falco  12/87
 */

#define EXTERN extern

#include <stdio.h>
#include "pmc_h.h"

/* Find the greatest common divisor (gcd) between the two arguments and
 * divide them by it.  Taken from Knuth's "The Art of Computer Programming,"
 * Vol 2, p298.  (Algorithm B)  The algorithm is based on the following
 * facts:
 *		gcd(u, v) = 2gcd(u/2, v/2)	when u and v are both even
 *		gcd(u, v) = gcd(u/2, v)		when u is even and v is odd
 *		u - v is even				when u and v are both odd
 *		|u - v| < max(u, v)			when u and v are both odd (?)
 *		gcd(u, v) = gcd(u - v, v)	for any u and v
 *		gcd(0, u) = |u|				for any u
 *
 * NOTE: i and j must be positive!
 */
dur_common(i, j)
	int *i, *j;
{
	int t, u, v;

	/* B1: first work directly on i and j to clear any power(s) of 2 */
	while((*i & 1) == 0 && (*j & 1) == 0) { /* they are both divisible by 2 */
		*i >>= 1;
		*j >>= 1;
	}
	
	/* B2: Now one or both is odd.  Make copies and initialize t.  Note that
	 * the original algorithm requires entering the outer loop at two different
	 * points.  Since that is messy, we force t to be even, so we can always
	 * legally execute B3.  We set the sign here so that the first time we
	 * execute B5, we replace u or v as appropriate to force both to be odd.
	 */
	u = *i;				/* which becomes u and which becomes v is arbitrary */
	v = *j;
	if((u & 1) != 0) {	/* u is odd */
		if((v & 1) != 0) {	/* v is odd */
			t = -(v << 1);		/* Both are odd - force t to be even so we can
								 * enter the loop at the top.  We arbitrarily
								 * chose to replace v.
								 */
		} else {			/* v is even */
			t = -v;				/* flag it for replacement */
		}
	} else {			/* u is even (v must be odd from B1) */
		t = u;					/* flag it for replacement */
	}
	
	do {
		do {
			/* B3: Divide t in half (t is initially even and nonzero) */
			t >>= 1;
		
		/* B4: Do that until t becomes odd (it clearly must stay nonzero!) */
		} while((t & 1) == 0);

		/* B5: Now t is odd.  On the first pass of this loop, we use the sign
		 * flag we set in B2 to replace u or v as appropriate so as to
		 * guarantee both u and v are odd.  On later passes, we use the sign
		 * flag as set in B6 to replace the larger of u and v to force
		 * convergence.  Note that u and v must remain odd since they are
		 * only replaced by t (which is odd from B4).
		 */
		if(t > 0) {
			u = t;
		} else {
			v = -t;
		}
	
		/* B6: Subtract u and v to form the new t.  The sign of t reflects which
		 * of u and v is larger.
		 */
		t = u - v;
	
	/* B7 (?)  See if we are done. */
	} while(t != 0);
	
	/* Now u and v are equal and happen to be the gcd of the original u and v.
	 * So adjust i and j, then go home.
	 */
	*i /= u;
	*j /= u;
	
	return;
}

/* reduce a fraction to "simplest form" */
	DUR_TYPE
dur_reduce(i)
	DUR_TYPE i;
{
	int sign_count;
	
	sign_count = 0;
	
	/* be a little careful */
	if(i.den == 0) {
		yyerror("attempted to reduce infinity");
		i.den = 1;
		return(i);
	}

	/* zero is a special case */
	if(i.num == 0) {
		i.den = 1;
		return(i);
	}

	/* work with positive numbers */
	if(i.num < 0) {
		i.num = -i.num;
		sign_count++;
	}
	
	if(i.den < 0) {
		i.den = -i.den;
		sign_count++;
	}
	
	/* We pass only positive (non-zero) numbers to dur_common().
	 * Actually, we pass pointers and dur_common() does the
	 * reduction before returning.
	 */
	dur_common(&i.num, &i.den);
	
	/* now adjust the final sign */
	if(sign_count & 1) { /* odd count means result should be negative */
		i.num = -i.num;
	}
	
	return(i);
}
	
	DUR_TYPE
dur_add(i, j)
	DUR_TYPE i, j;
{
	DUR_TYPE r;
	
	r.num = i.num * j.den + j.num * i.den;
	r.den = i.den * j.den;
	
	return(dur_reduce(r));
}

	DUR_TYPE
dur_sub(i, j)
	DUR_TYPE i, j;
{
	DUR_TYPE r;
	
	r.num = i.num * j.den - j.num * i.den;
	r.den = i.den * j.den;
	
	return(dur_reduce(r));
}

	DUR_TYPE
dur_mul(i, j)
	DUR_TYPE i, j;
{
	DUR_TYPE r;
	
	r.num = i.num * j.num;
	r.den = i.den * j.den;
	
	return(dur_reduce(r));
}

	DUR_TYPE
dur_div(i, j)
	DUR_TYPE i, j;
{
	DUR_TYPE r;
	
	r.num = i.num * j.den;
	r.den = i.den * j.num;
	
	return(dur_reduce(r));
}

/* end */
//E*O*F pmc_dur.c//

echo x - pmc_init.c
cat > "pmc_init.c" << '//E*O*F pmc_init.c//'
/*
 * pmc - a polyrhythmic music compiler
 *
 * based heavily on Bob Brown's original 1982 mc program
 *
 * Steven A. Falco  12/87
 */

#define EXTERN extern

#include <stdio.h>
#include "pmc_h.h"

init_voices()
{
	int i;
	
	/* voice 0 isn't used but clear it anyway */
	for(i = 0; i < MAXVOICES; i++) {
		CurV = &Voices[i];
		clear_accident();
		CurV->ampl = FF_VOL;
		CurV->base_octave = 0;
		CurV->stolen = D_0;
		CurV->def_dur = QUARTER;
		CurV->last_dur = D_0;
		CurV->lead_in = D_ILL;
		CurV->first = new_chord(); /* start us off */
		CurV->r_depth = 0;
		CurV->p_depth = 0;
	}

	return;
}

clear_accident()
{
	int j;
	
	for(j = 0; j < MAXPITCH; j++) {
		CurV->accidental[j] = NOACCIDENT;
	}
	
	return;
}

init_vars()
{
	int i, j, k;
	
	/* set up the pitch translation table based on the MIDI pitches */
	Pitch_table['a' - 'a'] = MIDI_A;
	Pitch_table['b' - 'a'] = MIDI_B;
	Pitch_table['c' - 'a'] = MIDI_C;
	Pitch_table['d' - 'a'] = MIDI_D;
	Pitch_table['e' - 'a'] = MIDI_E;
	Pitch_table['f' - 'a'] = MIDI_F;
	Pitch_table['g' - 'a'] = MIDI_G;
	
	/* set up the key signature table */
	for(i = 0; i < PER_OCTAVE; i++)
		for(j = 0; j < PER_OCTAVE; j++)
			KeyTable[i][j] = 0; /* whitewash */

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

	/* set some misc values */

#ifdef YYDEBUG
	yydebug = 0;	/* this is a global - it doesn't use the capitals convention */
#endif

#ifdef DEBUG
	Debug = 0;
#endif

#ifdef LEXDEBUG
	debug = 0;		/* this is a global - it doesn't use the capitals convention */
#endif

	CurrentKey = 0;
	NumFreqs = CurFreq = 0;
	for(i = 0; i < MAXFREQS; i++) {
		Freq[i] = REST;
	}

	/* set up the lexical analyzer variables */
	File = "stdin";
	Line = 1;
	Char = 0;
	
	/* set up the bizzare constants */
	D_ILL.num = -1;
	D_ILL.den = 1;
	
	D_0.num = 0;
	D_0.den = 1;
	
	D_1.num = 1;
	D_1.den = 1;
	
	D_2.num = 2;
	D_2.den = 1;
	
	QUARTER.num = 1;
	QUARTER.den = 4;
	
	SIXTEEN.num = 1;
	SIXTEEN.den = 16;
		
	/* set up default time values */
	Tempo = NOMTEMPO;
	TimeSignature.num = NOMSIG; /* this must not be reduced */
	TimeSignature.den = NOMSIG;

	return;
}

/* end */
//E*O*F pmc_init.c//

echo x - pmc_list.c
cat > "pmc_list.c" << '//E*O*F pmc_list.c//'
/*
 * pmc - a polyrhythmic music compiler
 *
 * based heavily on Bob Brown's original 1982 mc program
 *
 * Steven A. Falco  12/87
 */

#define EXTERN extern

#include <stdio.h>
#include "pmc_h.h"

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

	CHORD_TYPE *
new_chord()
{
	CHORD_TYPE *i;
	int j;

#ifdef macintosh
		SpinCursor(1);
#endif
	
	if((i = (CHORD_TYPE *)malloc(sizeof(CHORD_TYPE))) == C_NULL) {
		yyerror("out of memory allocating chord");
		exit(1);
	}
	i->next = C_NULL;
	i->head = C_NULL;
	i->tail = C_NULL;
	i->repeat = C_NULL;
	i->back = C_NULL;
	i->rep_val = 0;
	i->amplitude = CurV->ampl;
	for(j = 0; j < MAXFREQS; j++) {
		i->freq[j] = REST;
	}
	i->numfreqs = 0;
	i->duration = D_1;
	i->enclosed_dur = D_0;
	i->scale_factor = FUDGE * TimeSignature.den / Tempo;
	i->comment = NULL;
	return(i);
}

add_chord(where, what, promote)
	CHORD_TYPE *where;
	CHORD_TYPE *what;
	int promote;
{
	VOICE_TYPE *x;
	DUR_TYPE to_take;
	
	/* lets try and be crash-proof */
	if(where == C_NULL || what == C_NULL) {
		yyerror("null pointer!");
		exit(1);
	}
	
	if(where->head == C_NULL) {	/* the first one */
		where->head = what;
	} else { /* another one */
		if(where->tail == C_NULL) {
			yyerror("null pointer!");
			exit(1);
		}
		where->tail->next = what;
	}
	
	where->tail = what;

	/*
	 * Before adding the duration of the new block to the existing block,
	 * we usually wish to "promote" the enclosed duration of the new block
	 * to become its overall duration.  (I.e., duration is normally absolute.)
	 * There are two exceptions to this: 1) when adding a simple chord (since
	 * its enclosed duration is zero), and 2) when adding a diversion (which
	 * has an explicit overall duration).
	 */
	if(promote) {
		what->duration = what->enclosed_dur;
	}

	/* We may adjust the duration of the block based on any previous grace
	 * notes.  We take half the time from the grace note itself when we add
	 * it to the list.
	 */
	if(CurV->stolen.num != 0) { /* duration needs to be stolen */
		to_take = dur_div(what->duration, D_2); /* we allow taking up to half */
		if(dur_sub(to_take, CurV->stolen).num > 0) { /* we can take that much */
			to_take = CurV->stolen; /* don't take more than is wanted! */
		}
		CurV->stolen = dur_sub(CurV->stolen, to_take); /* remember how much we take */
		what->duration = dur_sub(what->duration, to_take); /* take it */
	}
	/* Keep a running sum of the duration in this voice.  This is done
	 * to adjust diversions.
	 */
	where->enclosed_dur = dur_add(where->enclosed_dur, what->duration);
		
	return;
}

do_wolf()
{
	yyerror("wolffence unimplemented");
	
	return;
}

do_track(src, dev)
	int src;
	int dev;
{
	yyerror("track unimplemented");
	
	return;
}

	char *
newstring(s)
	int s;
{
	char *ns;
	
	if((ns = malloc(s)) == NULL) {
		yyerror("out of memory allocating string");
		exit(1);
	}
	
	return(ns);
}

/* end */
//E*O*F pmc_list.c//

echo x - pmc_rep.c
cat > "pmc_rep.c" << '//E*O*F pmc_rep.c//'
/*
 * pmc - a polyrhythmic music compiler
 *
 * based heavily on Bob Brown's original 1982 mc program
 *
 * Steven A. Falco  12/87
 */

#define EXTERN extern

#include <stdio.h>
#include "pmc_h.h"

/* Starting a repeat.  This is not legal in a polyrhythm
 * but is actually very similar to starting one.  There
 * is no outer duration to worry about, but there can be
 * multiple endings so clear the rep_val (current ending
 * value.)
 */	
r_start()
{
	CHORD_TYPE *x;
	
	 /* Is prev. length ok?  Must check before we create the
	  * diversion or we'll check the wrong thing!
	  */
	 check_measure(CurC, SAVE_LEAD);
	
	if(CurV->p_depth != 0) {
		yyerror("can't start repeat inside polyrhythm");
	} else {
		x = new_chord();
		x->back = CurC;
		CurV->r_depth++;
		CurC = x;
		CurC->rep_val = 0;
		CurV->last_dur = D_0; /* start length afresh */
	}
	
	return;
}

/* Now it gets complicated!  A repeat has apparently
 * ended.  Make sure of that, and verify the nesting.
 * There are then two cases to consider.  If we have
 * no numbered endings, then the repeat block must be
 * linked in twice, and nothing else is needed.  If
 * there were numbered endings, all we need is to add
 * the last one.  In either case, step back to our
 * parent and adjust the depth.
 */
r_end()
{
	CHORD_TYPE *x;
	
	if(CurV->p_depth != 0) {
		yyerror("can't end repeat inside polyrhythm");
	} else if(CurV->r_depth <= 0) {
		yyerror("repeat end without repeat start");
	} else if(CurC->repeat == C_NULL) { /* no endings - need two copies of 
										 * repeated section.
										 */
		check_measure(CurC, NO_LEAD); 	/* length-check it first */
		x = new_chord(); 		/* first reference */
		add_chord(x, CurC, PROMOTE);
		add_chord(CurC->back, x, PROMOTE);
	
		x = new_chord(); 		/* second reference */
		add_chord(x, CurC, PROMOTE);
		add_chord(CurC->back, x, PROMOTE);
		
		CurC = CurC->back; 		/* step back to parent */
		CurV->r_depth--;
		CurV->last_dur = CurC->enclosed_dur;
	} else { /* endings */
	
		/* it's almost complete, just need the last ending */
		check_measure(CurC, NO_LEAD); 	/* length-check it first */
		add_chord(CurC->back, CurC, PROMOTE);
		
		CurC = CurC->back; 	/* step back to parent */
		CurV->r_depth--;	
		CurV->last_dur = CurC->enclosed_dur;
	}
	
	return;
}

/* This is the worst pointer hacking in the program.
 * Validate the request, and see that the numbered
 * endings are in sequence.  If so, we have two cases to
 * consider.  If this is the first numbered ending, we
 * really just know about the repeated section - the
 * ending is to follow.  So we make two copies of the
 * repeated section.  The first is added to the parent,
 * while the second is kept around for the *next* ending
 * if any.  The second is always available for further
 * cloning via the "repeat" pointer.  Note that we
 * have to discard (break off) the repeated section
 * so that the first ending is kept isolated till it is
 * complete.  Thus we break "head" (which is sufficient
 * to disconnect the section), and clear the duration.
 *
 * If this is the start of a later ending, we first
 * add the *previous* ending to our parent (since the
 * repeating section is already in place and we have
 * yet to scan *this* ending!).  Then we add in another
 * clone of the repeated section since this ending is
 * coming up next.  Note that we have to "flush" the
 * *previous* ending to make room for *this* ending.
 * Also note that if *this* ending is the last ending,
 * it will actually get installed in the REND rule
 * above.
 *
 * If you think you understand this, you probably need
 * another drink.
 */
r_mid(i)
	int i;
{
	CHORD_TYPE *x;
	 
	if(CurV->p_depth != 0) {
		yyerror("can't have an ending inside polyrhythm");
	} else if(CurV->r_depth <= 0) {
		yyerror("numbered repeat end without repeat start");
	} else {
		/* see if they are in order */
		if(++CurC->rep_val != i) {
			sprintf(Err_buf, "endings out of order, expected %d, got %d",
				CurC->rep_val, i);
			yyerror(Err_buf);
		}
		
		/* the first time, make two copies of
		 * the repeated section, link one in, and save the
		 * other.
		 */
		if(CurC->repeat == C_NULL) {
			check_measure(CurC, NO_LEAD); /* first length-check it */
			x = new_chord();
			*x = *CurC; /* clone it */
			add_chord(CurC->back, x, PROMOTE); /* add one in */

			x = new_chord();
			CurC->repeat = x;
			*x = *CurC; /* clone it */
			
			/* break the repeat section off */
			CurC->head = C_NULL;
			CurC->enclosed_dur = D_0;
			CurV->last_dur = D_0;
		} else {
		
			/* add in a copy of the previous ending */
			check_measure(CurC, NO_LEAD); /* first length-check it */
			x = new_chord();
			*x = *CurC; /* clone it */
			add_chord(CurC->back, x, PROMOTE); /* add it in */

			/* Add the repeated section again.
			 * It has already been checked for length.
			 */
			x = new_chord();
			*x = *(CurC->repeat); /* clone it */
			add_chord(CurC->back, x, PROMOTE); /* add one in */
			
			/* break off the previous ending */
			CurC->head = C_NULL;
			CurC->enclosed_dur = D_0;
			CurV->last_dur = D_0;
		}
	}
	
	return;
}

/* end */
//E*O*F pmc_rep.c//

echo x - pmc_l.l
cat > "pmc_l.l" << '//E*O*F pmc_l.l//'
%{

/*
 * pmc - a polyrhythmic music compiler
 *
 * based heavily on Bob Brown's original 1982 mc program
 *
 * Steven A. Falco  12/87
 */

#define EXTERN extern

#include <stdio.h>
#include "pmc_h.h"
#include "pmc_y.h"

/* Note that the ordering of the rules in this file is critical!  We depend on
 * start references a whole lot since pitches are single letters and conflict
 * with lots of other tokens.
 */
 
%}

%start CMD PCH

%%

fence			{
					Char += yyleng;
					return(FENCE);
				}

key				{
					Char += yyleng;
					return(KEY);
				}

of				{
					Char += yyleng;
					return(OF);
				}

tempo			{
					Char += yyleng;
					return(TEMPO);
				}

time			{
					Char += yyleng;
					return(TIME);
				}

track			{
					Char += yyleng;
					return(TRACK);
				}

wolf			{
					Char += yyleng;
					return(WOLF);
				}

<PCH>"{"[^}]*"}" {
					/* If we are recognizing a chord, this comment
					 * should be attached to it, so we return the token
					 * MCOMMENT.
					 */
					Char += yyleng;
					yytext[yyleng - 1] = NULL; /* zap the '}' */
					yylval.chval = newstring(yyleng - 1); /* make enough room */
					strcpy(yylval.chval, yytext + 1); /* skip the '{' */
					return(MCOMMENT);
				}

"{"[^}]*"}"		{
					/* Once we see whitespace, the previous rule won't be
					 * active, thus this rule (which is lower priority since
					 * it comes later in the file) will be matched and we get
					 * a stand-alone comment.
					 */
					Char += yyleng;
					yytext[yyleng - 1] = NULL; /* zap the '}' */
					yylval.chval = newstring(yyleng - 1); /* make enough room */
					strcpy(yylval.chval, yytext + 1); /* skip the '{' */
					return(COMMENT);
				}

[1-9][0-9]*"|"	{
					Char += yyleng;
					sscanf(yytext, "%d|", &(yylval.ival));
					return(RMID);
				}

-?[0-9][0-9]*	{
					Char += yyleng;
					sscanf(yytext, "%d", &(yylval.ival));
					return(INTEGER);
				}

<PCH>"b"		{
					/* If we are doing a chord, then we recognize accidentals.
					 * So the first b we see is a pitch, but the second is a
					 * "flat".  Note that the flat rule must come before the
					 * pitch rule for this to work.
					 */
					Char += yyleng;
					yylval.ival = FLAT;
					return(ACCIDENT);
				}

<PCH>"#"		{
					/* Similarly, we only notice sharps as attached to a chord.
					 * Thus, comments (which are a later rule) are recognized
					 * after any whitespace or at the beginning of a line.
					 */
					Char += yyleng;
					yylval.ival = SHARP;
					return(ACCIDENT);
				}

<PCH>"%"		{
					Char += yyleng;
					yylval.ival = NATURAL;
					return(ACCIDENT);
				}

"#"				{
					while(input() != '\n') {
						; /* eat the comment */
					}
					unput('\n'); /* put the new-line back */
				}

<CMD>fff		{
					/* the volume tokens conflict with pitches (at least the
					 * Forte (f) token does).  So these must be directly
					 * adjacent to the BANG to be recognized (i.e. during a
					 * command).
					 */
					Char += yyleng;
					yylval.ival = FFF_VOL;
					return(VOLUME);
				}

<CMD>ff			{
					Char += yyleng;
					yylval.ival = FF_VOL;
					return(VOLUME);
				}

<CMD>f			{
					Char += yyleng;
					yylval.ival = F_VOL;
					return(VOLUME);
				}

<CMD>mf			{
					Char += yyleng;
					yylval.ival = MF_VOL;
					return(VOLUME);
				}

<CMD>mp			{
					Char += yyleng;
					yylval.ival = MP_VOL;
					return(VOLUME);
				}

<CMD>p			{
					Char += yyleng;
					yylval.ival = P_VOL;
					return(VOLUME);
				}

<CMD>pp			{
					Char += yyleng;
					yylval.ival = PP_VOL;
					return(VOLUME);
				}

<CMD>ppp		{
					Char += yyleng;
					yylval.ival = PPP_VOL;
					return(VOLUME);
				}

[a-g]			{
					/* Finally, if nothing else matches, we can go for a
					 * pitch.
					 */
					BEGIN PCH; /* allows us to recognize attached things */
					Char += yyleng;
					yylval.nval.pitch = Pitch_table[yytext[0] - 'a'];
					yylval.nval.direction = UP;
					return(PITCH);
				}

[A-G]			{
					BEGIN PCH; /* allows us to recognize attached things */
					Char += yyleng;
					yylval.nval.pitch = Pitch_table[yytext[0] - 'A'] - PER_OCTAVE;
					yylval.nval.direction = DOWN;
					return(PITCH);
				}

r				{
					BEGIN PCH; /* allows us to recognize attached things */
					Char += yyleng;
					yylval.nval.pitch = REST;
					yylval.nval.direction = 0; /* unused */
					return(PITCH);
				}

"*"				{
					BEGIN PCH; /* allows us to recognize attached things */
					Char += yyleng;
					return(STAR);
				}

"^"				{
					BEGIN PCH; /* allows us to recognize attached things */
					Char += yyleng;
					return(UPARROW);
				}

v				{
					Char += yyleng;
					return(VOICE);
				}

[whqistx]		{
					/* Durations are carefully chosen not to conflict with
					 * pitches.  That would be too much!
					 */
					Char += yyleng;
					yylval.dval.num = 1;
					switch(yytext[0]) {
						case 'w':
							yylval.dval.den = 1;
							break;
						case 'h':
							yylval.dval.den = 2;
							break;
						case 'q':
							yylval.dval.den = 4;
							break;
						case 'i':
							yylval.dval.den = 8;
							break;
						case 's':
							yylval.dval.den = 16;
							break;
						case 't':
							yylval.dval.den = 32;
							break;
						case 'x':
							yylval.dval.den = 64;
							break;
						default:
							yyerror("illegal duration during lexical analysis!");
							break;
					}
					
					return(DURATION);
				}

"&"				{
					/* We must turn off PCH when chaining notes so that we
					 * can recognize the first 'b' after an '&' as a pitch
					 * rather than an accidental.
					 */
					BEGIN 0;
					Char += yyleng;
					return(AMPERSAND);
				}

"<"				{
					Char += yyleng;
					return(LANGLE);
				}

">"				{
					Char += yyleng;
					return(RANGLE);
				}

"~"				{
					Char += yyleng;
					return(TWIDDLE);
				}

"["				{
					Char += yyleng;
					return(LBRACKET);
				}

"]"				{
					Char += yyleng;
					return(RBRACKET);
				}

"("				{
					Char += yyleng;
					return(LPAREN);
				}

")"				{
					Char += yyleng;
					return(RPAREN);
				}

"|:"			{
					Char += yyleng;
					return(RSTART);
				}

":|"			{
					Char += yyleng;
					return(REND);
				}

"|"				{
					Char += yyleng;
					return(BAR);
				}

"/"				{
					Char += yyleng;
					return(SLASH);
				}

"!"				{
					/* Allow volumes to be recognized.  Allows the pitch "f" to
					 * co-exist with the volume "f".
					 */
					BEGIN CMD;
					Char += yyleng;
					return(BANG);
				}

":"				{
					Char += yyleng;
					return(COLON);
				}

"_"				{
					Char += yyleng;
					return(UNDERBAR);
				}

"."				{
					Char += yyleng;
					return(DOT);
				}

[ \t]			{
					/* Whitespace is the great leveler which allows pitches
					 * to take precidence over other tokens.
					 */
					BEGIN 0;
					Char += yyleng;
				}

\n				{
					BEGIN 0;
					Line++;
					Char = 0;
				}

.				{
					Char += yyleng;
					sprintf(Err_buf, "bogus character (%s)", yytext);
					yyerror(Err_buf);
				}
//E*O*F pmc_l.l//

echo x - pmc_y.y
cat > "pmc_y.y" << '//E*O*F pmc_y.y//'
%{

/*
 * pmc - a polyrhythmic music compiler
 * 
 * based heavily on Bob Brown's original 1982 mc program
 *
 * Steven A. Falco  12/87
 */

#define EXTERN extern

#include <stdio.h>
#include "pmc_h.h"

%}

%union {
	unsigned char ucval;
	DUR_TYPE dval;
	NOTE_TYPE nval;
	CHORD_TYPE *cval;
	char *chval;
	int ival;
}

%token <ival>	ACCIDENT
%token 			AMPERSAND
%token 			BAR
%token 			BANG
%token 			COLON
%token <chval>	COMMENT
%token 			DOT
%token <dval> 	DURATION
%token			FENCE
%token <ival>	INTEGER
%token 			KEY
%token 			LANGLE
%token 			LBRACKET
%token 			LPAREN
%token <chval>	MCOMMENT
%token 			OF
%token <nval>	PITCH
%token 			RANGLE
%token 			RBRACKET
%token 			REND
%token <ival>	RMID
%token 			RPAREN
%token 			RSTART
%token 			SLASH
%token 			STAR
%token 			TEMPO
%token 			TIME
%token 			TRACK
%token 			TWIDDLE
%token 			UNDERBAR
%token 			UPARROW
%token			VOICE
%token <ival>	VOLUME
%token			WOLF

%type <cval>	Chord
%type <dval>	Duration_group
%type <dval>	Duration_item
%type <dval>	Duration_list
%type <ival>	Duration_mod
%type <ucval>	Pitch_item
%type <cval>	Pitch_list
%type <ival>	Pitch_mod
%type <ival>	Pitch_oct

%%

Song			: Voice
					{
						/* We have recognized the whole song.  Now we
						 * must unwind the last voice since it might have
						 * just finished a repeat or polyrhythm.
						 */
						CurV->first = CurC;
					}
				;
				
				/* Note - initially, the voice productions recognized complete
				 * repeats and polyrhythms, and the yacc stack was used to keep
				 * track of all these diversions.  It was clean and elegant and
				 * didn't work because it couldn't handle voice changes in the
				 * middle of a diversion (since yacc's state couldn't easily be
				 * saved and restored while changing voices).  I considered
				 * a preprocessor or multiple passes, but by explicitly keeping
				 * track of diversions (via the "back" pointers), I get better
				 * diagnostics and the context switch is "trivial" (plus it is
				 * faster than the other choices).  Just in case you wondered...
				 */
Voice			: /* null */
					{
						/* We are just starting the song.  Default to voice 1
						 * to set some reasonable pointers.
						 */
						CurrentVoice = 1;
						CurV = &Voices[CurrentVoice];
						CurC = CurV->first;
					}
				| Voice VOICE INTEGER COLON
					{
						/* The voice block always remembers where we were
						 * adding data, so we can restore that pointer
						 * when resuming the voice.  "Where to add" changes
						 * as we enter and leave diversions.
						 */
						CurV->first = CurC; /* save the old state */
						
						if($3 < MAXVOICES){ /* validate the request */
							CurrentVoice = $3;
						} else {
							CurrentVoice = 0;
							yyerror("too many voices");
						}
						
						/* "Restore" the old state.  If this is the first time
						 * we've used this voice, we will be pointing to the
						 * block allocated during init_voices().
						 */
						CurV = &Voices[CurrentVoice];
						CurC = CurV->first;
					}
				| Voice Chord
					{
						/* A simple chord doesn't enclose anything so the
						 * duration is not promoted.  If this note is staccatto,
						 * its duration is lowered to 1/16 and a rest for the
						 * balance of the time is created.  (If it was 1/16
						 * or shorter, nothing happens.)
						 */
						if(Modifiers.staccatto) {
							DUR_TYPE shorten;
							CHORD_TYPE *x;
							
							shorten = dur_sub($2->duration, SIXTEEN);
							if(shorten.num > 0) { /* we can shorten it */
								$2->duration = SIXTEEN;
								add_chord(CurC, $2, NO_PROMOTE); /* add short */
								
								/* now make a REST */
								x = new_chord();
								x->numfreqs = 1;
								x->freq[0] = REST;
								x->duration = shorten;
								add_chord(CurC, x, NO_PROMOTE);
							} else { /* short enough already */
								add_chord(CurC, $2, NO_PROMOTE);
							}
						} else { /* not staccatto */
							add_chord(CurC, $2, NO_PROMOTE);
						}
					}
				| Voice Duration_group LBRACKET
					{
						CHORD_TYPE *x;
						
						/* Start a polyrhythm.  This is legal at any time and
						 * starts out as a simple diversion.  Keep a depth count
						 * so we can audit for matching [] and so we can reject
						 * repeats inside polyrhythms.
						 */
						x = new_chord();
						x->back = CurC; /* point back to our parent */
						CurV->p_depth++;
						CurC = x;		/* move the "addition" point to us */
						CurC->duration = $2; /* remember the allowed duration */
					}
				| Voice RBRACKET
					{
						/* The inner-most polyrhythm has supposedly ended.
						 * Make sure there was one!  If so, add it onto our
						 * parent and don't promote the enclosed duration,
						 * since that is the whole point of a polyrhythm.
						 * Finally, move the addition point back to our
						 * parent and decrease the depth by 1.
						 */
						if(CurV->p_depth <= 0) {
							yyerror("can't end polyrhythm you didn't start");
						} else {
							add_chord(CurC->back, CurC, NO_PROMOTE);
							
							CurC = CurC->back;
							CurV->p_depth--;
						}
					}
				| Voice RSTART
					{
						r_start();
					}
				| Voice REND
					{
						r_end();
					}
				| Voice RMID
					{
						r_mid($2);
					}
				| Voice COMMENT
					{
						CHORD_TYPE *x;
						
						/* promote the enclosed duration of 0 */
						x = new_chord();
						x->comment = $2;
						add_chord(CurC, x, PROMOTE);
					}
				| Voice BANG Command
				| Voice BAR
					{
						check_measure(CurC, SAVE_LEAD);
					}
				| Voice LPAREN Duration_group PITCH Pitch_oct RPAREN
					{
						template($3, $4.pitch, $4.direction, $5);
					}
				| Voice RANGLE
					{
						CurV->ampl = VCHECK(CurV->ampl - 1);
					}
				| Voice LANGLE
					{
						CurV->ampl = VCHECK(CurV->ampl + 1);
					}
				;

Command			: KEY OF PITCH Pitch_mod
					{
						/* This is similar to do_pitch() but we just care
						 * about the relative note, not the octave.  We don't
						 * make entries in the accidental table. (We clear it!)
						 */
						clear_accident();
						CurrentKey = do_key($3.pitch, $4);
					}
				| TIME INTEGER SLASH INTEGER
					{
						if(($2 < MINSIG) || ($2 > MAXSIG) ||
							($4 < MINSIG) || ($4 > MAXSIG)) {
							sprintf(Err_buf, "bogus time signature %d/%d",
								$2, $4);
							yyerror(Err_buf);
							$2 = NOMSIG;
							$4 = NOMSIG;
						}
						
						/* NEVER REDUCE TimeSignature! */
						TimeSignature.num = $2;
						TimeSignature.den = $4;
					}
				| TEMPO INTEGER
					{
						if(($2 < MINTEMPO) || ($2 > MAXTEMPO)) {
							sprintf(Err_buf, "bogus tempo %d(%d : %d)",
								$2, MINTEMPO, MAXTEMPO);
							yyerror(Err_buf);
							$2 = NOMTEMPO;
						}
						Tempo = $2;
					}
				| WOLF FENCE
					{
						do_wolf();
					}
				| TRACK INTEGER INTEGER
					{
						do_track($2, $3);
					}
				| RANGLE
					{
						CurV->ampl = VCHECK(CurV->ampl - 1);
					}
				| LANGLE
					{
						CurV->ampl = VCHECK(CurV->ampl + 1);
					}
				| VOLUME
					{
						CurV->ampl = VCHECK($1);
					}
				;

Chord			: Duration_group Pitch_list Chord_mods
					{
						/* we allocated storage while building the pitch list */
						$2->duration = $1;
						if(Modifiers.grace) {
							/* we will steal our time from succeeding notes */
							CurV->stolen = dur_add(CurV->stolen, $1);
						}
						$2->comment = Modifiers.comm;
						$$ = $2;
					}
				;

Pitch_list		: 	{
						CurFreq = 0;
					}
				  Pitch_item
					{
						/* Store the first pitch of the chord.  We keep
						 * a copy in the global Freqs array for the
						 * chord copy function.
						 */
						$$ = new_chord();
						$$->numfreqs = NumFreqs = 0;
						$$->freq[$$->numfreqs++] = $2;
						Freq[NumFreqs++] = $2;
						CurFreq++;
					}
				| Pitch_list AMPERSAND Pitch_item
					{
						/* See if the next pitch fits in this chord,
						 * store it if so.  Make a copy in Freqs[] too.
						 */
						if($1->numfreqs < MAXFREQS) {
							$1->freq[$1->numfreqs++] = $3;
							Freq[NumFreqs++] = $3;
							CurFreq++;
						} else {
							sprintf(Err_buf, "too many notes in chord (%d)",
								MAXFREQS);
							yyerror(Err_buf);
						}
					}
				| UPARROW
					{
						int i;
						
						/* We are to duplicate the pitches in the chord
						 * from the last chord we saw.  This had better be the
						 * first and only pitch symbol in the chord.  I don't
						 * alter CurFreq here because I don't think '*' after
						 * an '^' makes sense.
						 */
						$$ = new_chord();
						$$->numfreqs = NumFreqs;
						for(i = 0; i < NumFreqs; i++) {
							$$->freq[i] = Freq[i];
						}
					}
				;

Pitch_item		: PITCH Pitch_mod Pitch_oct
					{
						/* We must accumulate any pitch-modifiers (accidentals)
						 * into the "accidental table".  We must also adjust the
						 * pitch for 1) octave, 2) explicit accidentals, 3)
						 * implicit accidentals, and 4) key signature.
						 */
						$$ = do_pitch($1.pitch, $1.direction, $2, $3);
					}
				| STAR
					{
						$$ = Freq[CurFreq];
					}
				;

Pitch_oct		: /* null */
					{
						$$ = 0;
					}
				| INTEGER
					{
						if(($1 < MINOCT) || ($1 > MAXOCT)) {
							sprintf(Err_buf, "bogus octave %d(%d : %d)",
								$1, MINOCT, MAXOCT);
							yyerror(Err_buf);
							$1 = MINOCT;
						}
						$$ = $1;
					}
				;

Pitch_mod		: /* null */
					{
						$$ = NOACCIDENT;
					}
				| Pitch_mod ACCIDENT
					{
						if($1 == NOACCIDENT) {
							$$ = $2; /* the first accidental symbol */
						} else {
							$$ = $1 + $2; /* this is a multiple accidental */
						}
					}
				;

Chord_mods		: /* null */
					{
						/* Before we see any, clear them out. */
						mod_clear();
					}
				| Chord_mods Chord_mod
				;

Chord_mod		: DOT
					{
						Modifiers.staccatto = TRUE;
					}
				| TWIDDLE
					{
						Modifiers.grace = TRUE;
					}
				| MCOMMENT
					{
						CHORD_TYPE *x;
						
						Modifiers.comm = $1;
					}
				;
				
Duration_group	: /* null */
					{
						$$ = CurV->def_dur;
					}
				| Duration_list
					{
						$$ = $1;
					}
				;

Duration_list	: Duration_item
					{
						$$ = $1;
					}
				| Duration_list UNDERBAR Duration_item
					{
						$$ = dur_add($1, $3);
					}

				;
				
Duration_item	: DURATION Duration_mod
					{
						int i;
						DUR_TYPE d, t;
						
						/* see what the basic duration is... */
						d = t = $1;
						
						/* now make it bigger if there are DOTs */
						for(i = 0; i < $2; i++) {
							d = dur_div(d, D_2); /* cut the basic time in half */
							t = dur_add(t, d); /* add it into the sum */
						}
						
						/* return it */
						$$ = t;
					}
				;

Duration_mod	: /* null */
					{
						$$ = 0;
					}
				| Duration_mod DOT
					{
						$$ = $1 + 1;
					}
				;

/* end */
//E*O*F pmc_y.y//

echo x - pmc_h.h
cat > "pmc_h.h" << '//E*O*F pmc_h.h//'
/*
 * pmc - a polyrhythmic music compiler
 *
 * based heavily on Bob Brown's original 1982 mc program
 *
 * Steven A. Falco  12/87
 */

/* tunable parameters */
#define MAXFREQS	8		/* notes per channel */
#define MINVOICE	1		/* lowest usable */
#define MAXVOICES	17		/* MIDI channels + 1 */
#define MAXSTR		256		/* bytes in a string */
#define MINSIG		1		/* minimum x and y in TS = x/y */
#define MAXSIG		64		/* maximum x and y in TS = x/y */
#define NOMSIG		4		/* nominal TS = NOMSIG/NOMSIG */
#define MINTEMPO	1		/* minimum tempo */
#define MAXTEMPO	1000	/* maximum tempo */
#define NOMTEMPO	120		/* nominal tempo */
#define MINOCT		0		/* minimum octave modifier */
#define MAXOCT		10		/* maximum octave modifier */

/* MIDI pitches */
#define MIDI_A (unsigned char)69
#define MIDI_B (unsigned char)71
#define MIDI_C (unsigned char)60
#define MIDI_D (unsigned char)62
#define MIDI_E (unsigned char)64
#define MIDI_F (unsigned char)65
#define MIDI_G (unsigned char)67
	
/* miscellaneous constants */
#ifndef TRUE
#	define TRUE 1
#	define FALSE 0
#endif

#define ILLKEY		-100			/* illegal keynote parameter number */
#define FUDGE		(120 * 24)		/* keynote tempo (120) * resolution (24) */
#define MAXPITCH	128
#define REST		128
#define PER_OCTAVE	12
#define SAVE_LEAD	1				/* lead_in flag = true */
#define NO_LEAD		0				/* lead_in flag = false */
#define UP			1
#define DOWN		-1
#define C_OCTAVE	(MIDI_C / PER_OCTAVE) /* c = 60 so the octave is 60/12 */

/* amplitudes */
#define MAXAMP		127
#define FFF_VOL		127
#define FF_VOL		120
#define F_VOL		100
#define MF_VOL		80
#define MP_VOL		60
#define P_VOL		40
#define PP_VOL		20
#define PPP_VOL		10

#define PROMOTE		1
#define NO_PROMOTE	0

/* pitch modifier values for accidentals */
#define SHARP		1
#define FLAT		-1
#define NATURAL		0
#define NOACCIDENT	128

/* lots of nothing */
#define C_NULL (CHORD_TYPE *)0

/* see if a volume is in range - force it if not */
#define VCHECK(v) (((v)<0)?0:(((v)>MAXAMP)?MAXAMP:(v)))

#ifdef YYDEBUG
extern int yydebug;
#endif

#ifdef DEBUG
EXTERN int Debug;	/* debug flag */
#endif

#ifdef LEXDEBUG
extern int debug;	/* rotten name choice guys! */
#endif

/* Any duration is carried as a fraction consisting of a numerator and
 * a denominator, so that we can handle polyrhythms without round-off
 * problems.  We find Least Common Denominators and all that neat stuff.
 */
typedef struct {
	long num;
	long den;
} DUR_TYPE;

/* A basic pitch is assigned an unsigned value with middle C being the
 * value 60 decimal.  The scale runs from 'c' to 'b' with each semitone
 * assigned its own value, so the B above middle C gets the value 71.
 * This is the standard MIDI numbering system.  We make note of capital
 * letters - they mean "down one octave" - and use 48 for 'C'.  This
 * capitalization business also affects octave adjustments so we keep
 * track of the direction for later.
 */
typedef struct {
	unsigned char pitch;
	char direction;
} NOTE_TYPE;

/* Chords are complex.  They can be just a single pitch (or even a rest),
 * or they can be a whole song (assuming nested polyrhythms).  If no
 * triplets or other groupings are being used, then the "head" pointer
 * will be null, and the chord will be a single entity (of 0 or more pitches).
 * If the "head" pointer is not null, it points to some list of chords or
 * aggregates which should be force-fit into the duration of this chord.  This
 * is recursive, and while emitting a chord, the diversions have to first be
 * traced until a null diversion is reached.  The "repeat" pointer is only
 * used if this "chord" is the top of a |: :| structure, then it points to
 * the master copy of the repeated music.  The master copy is never used
 * directly since it only has one "next" pointer.  Rather, diversions are
 * created to refer to it.  It probably should be freed after parsing :| but
 * it's not worth it for 70 or so bytes.  The more important optimization is
 * to make this structure a union since most chords don't need all the pointers.
 */
typedef struct _chord {
	struct _chord *next;	/* the next chord in the chain */
	struct _chord *head;	/* the first chord in a diversion */
	struct _chord *tail;	/* the place to add to a diversion */
	struct _chord *repeat;	/* the stuff to repeat */
	struct _chord *back;	/* the place to step back to */
	unsigned char rep_val;	/* which ending we are up to */
	unsigned char amplitude;/* the fully resolved value */
	unsigned char freq[MAXFREQS];
	int numfreqs;
	DUR_TYPE duration;		/* duration of chord or aggregate */
	DUR_TYPE enclosed_dur;	/* the duration of what the aggregate encloses */
	unsigned int scale_factor;	/* an aggregate of the TimeSignature and Tempo */
	char *comment;			/* for putting special MIDI data in manually */
} CHORD_TYPE;

/* Each voice consists of a parameter block containing (fairly) static
 * attributes - this block is defined below - and a linked list of "chords",
 * which is kept as a pointer in the parameter block.  Chords are
 * not quite as simple as they may appear - see the CHORD_TYPE for details.
 */
typedef struct {
	int accidental[MAXPITCH];/* +2, +1, 0, -1, -2, or NOACCIDENT are typical */
	unsigned char ampl;		/* default amplitude */
	int base_octave;		/* octave offset to be applied */
	DUR_TYPE stolen;		/* the time that must be stolen from succeeding notes */
	DUR_TYPE def_dur;		/* default duration for this voice */
	DUR_TYPE last_dur;		/* duration as of last measure check */
	DUR_TYPE lead_in;		/* amount of lead-in we saw */
	DUR_TYPE lead_sig;		/* TS in effect at time of lead_in */
	int lead_line;			/* line number of last lead_in */
	int lead_char;			/* character position of last lead_in */
	CHORD_TYPE *first;		/* the first chord in this voice */
	int r_depth;			/* repeat depth */
	int p_depth;			/* polyrhythm depth */
} VOICE_TYPE;

/* There is one copy of this structure since we only process one chord at a
 * time.  It is cleared as a chord modifier group is started, and is used in
 * various places.
 */
typedef struct {
	int staccatto;			/* remember if the current chord is staccatto */
	int grace;				/* remember if the current chord steals duration */
	char *comm;				/* remember where any attached comment is */
} MOD_TYPE;

/* translation table for pitches 'a' through 'g' */
EXTERN unsigned char Pitch_table[7];

/* key signature table and friends */
EXTERN int KeyTable[PER_OCTAVE][PER_OCTAVE];
EXTERN int CurrentKey;

/* lexical analyzer variables */
EXTERN char *File;
EXTERN int Line, Char;

/* the voices */
EXTERN VOICE_TYPE Voices[MAXVOICES];
EXTERN int CurrentVoice;
EXTERN VOICE_TYPE *CurV;
EXTERN CHORD_TYPE *CurC;

/* bizzare constants */
EXTERN DUR_TYPE D_ILL;		/* illegal duration */
EXTERN DUR_TYPE D_0;		/* "0" as a duration */
EXTERN DUR_TYPE D_1;		/* "1" as a duration */
EXTERN DUR_TYPE D_2;		/* "2" as a duration */
EXTERN DUR_TYPE QUARTER;	/* a quarter note */
EXTERN DUR_TYPE SIXTEEN;	/* a sixteenth note */

/* misc variables */
EXTERN unsigned char Freq[MAXFREQS]; /* A parallel structure to the one in
									  * a chord for copying notes and chords
									  * with the '^' operator.
									  */
EXTERN int NumFreqs;		/* more parallels */
EXTERN int CurFreq;			/* The current frequency slot being filled.  This
							 * is very similar to NumFreqs except that they are
							 * set to 0 at different times.  NumFreqs becomes 0
							 * after the first pitch is recognized, while CurFreq
							 * becomes 0 before the first pitch is recognized.
							 * NumFreqs is necessary for the '^' operator while
							 * CurFreq is needed for the '*' operator.
							 */

EXTERN char Err_buf[MAXSTR];/* a buffer for used in yyerror */
EXTERN int Tempo;
EXTERN MOD_TYPE Modifiers;	/* Only need one since we process a single chord
							 * at a time.
							 */

EXTERN DUR_TYPE Time_of_day;/* a running total of time in the expander */
EXTERN DUR_TYPE TimeSignature;

/* non-integer functions */
DUR_TYPE dur_add();
DUR_TYPE dur_sub();
DUR_TYPE dur_mul();
DUR_TYPE dur_div();
DUR_TYPE dur_reduce();

unsigned char do_pitch();

CHORD_TYPE *new_chord();

char *key_tone();
char *newstring();
char *malloc();

/* end */
//E*O*F pmc_h.h//

exit 0
