#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 5 (of 6)."
# Contents:  phase1.c
# Wrapped by lee@uhccux on Wed Mar 29 09:58:11 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'phase1.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'phase1.c'\"
else
echo shar: Extracting \"'phase1.c'\" \(32964 characters\)
sed "s/^X//" >'phase1.c' <<'END_OF_FILE'
X/* phase1.c -- Phase 1 of adagio compilation...	 */ 
X/*
X * this module parses adagio programs and builds a linked list structure
X * consisting of notes and control changes in time order.
X */
X
X/*****************************************************************************
X*	    Change Log
X*  Date	    | Change
X*-----------+-----------------------------------------------------------------
X* 31-Dec-85 | Created changelog
X* 31-Dec-85 | Add c:\ to include directives
X* 31-Dec-85 | Added standard command scanner, metronome variable, need to add 
X*	    | cmdline_help procedure
X* 31-Dec-85 | Call intr_init
X* 31-Dec-85 | Set musictrace from command line via -trace
X* 31-Dec-85 | Added -poll
X*  1-Jan-86 | Put error messages out to stderr
X*  1-Jan-86 | Set IsAT.	 Can be later overridden by -at and -xt switches,
X*	    | currently only used for diagnostics (may be needed for
X*	    | compatibles, who knows?  In which case remove the tests which
X*	    | confirm the type of processor)
X*  1-Jan-86 | <rgd/jmn> Removed dur-adjusted message
X*  1-Jan-86 | Added miditrace
X* 18-Jan-86 | Shortened durations by 1/200 s to avoid roundoff problems --
X*	    | see buildnote for details.
X*  3-Mar-86 | Allow octave and accidentals in either order after pitch name.
X*	    | Default octave is now one that gets nearest previous pitch,
X*	    |  the tritone (half an octave) interval is descending by default.
X*	    | Special commands handled by table search, !Rate command added
X*	    |  to scale all times by a percentage (50 = half speed).
X*  9-Mar-86 | Use space to limit amount of storage allocation.	Otherwise
X*	    |	exhausting storage in phase1 caused phase2 to fail.
X* 12-Mar-86 | Broke off command line parser into adagio.c, only parser remains
X* 24-Mar-86 | Changed representation from note_struct to event_struct
X*	    | Parse M, N, O, X, and Y as control change commands
X* 23-May-86 | Added , and ; syntax: "," means "N0\n", ";" means "\n"
X* 16-Jul-86 | modify to only call toupper/lower with upper/lower case as
X*	    |  parameter to be compatible with standard C functions
X*  7-Aug-86 | fixed bug with default pitches and rests
X*****************************************************************************/
X
X#include "cext.h"
X#include "stdio.h"
X#include "ctype.h"
X#include "malloc.h"
X#include "adagio.h"
X#include "cmdline.h"
X#include "phase1.h"
X
Xextern long space;	/* remaining free bytes */
X
X/****************************************************************************
X* The following are used to simulate fixed point with the radix point
X* 8 bits from the right:
X****************************************************************************/
X#define unity 256
X#define round(x) (((x)+128)>>8)
X#define precise(x) ((x)<<8)
X
X#define nullstring(s) (s[0] == 0)
X
X/****************************************************************************
X* Routines local to this module:
X****************************************************************************/
Xprivate	     event_type ctrlalloc();
Xprivate		   void	do_a_rest();
Xprivate		   void	doabsdur();
Xprivate		   void	doabspitch();
Xprivate		   void	docomment();
Xprivate		   void	doctrl();
Xprivate		   void	dodur();
Xprivate		   void	doerror();
Xprivate		   void	doloud();
Xprivate		   void	donextdur();
Xprivate		   void	dopitch();
Xprivate		   void	doprogram();
Xprivate		   void	dorate();
Xprivate		   void	dospecial();
Xprivate		   void	dotempo();
Xprivate		   void	dotime();
Xprivate		   void	dovoice();
Xprivate		   void	fferror();
Xprivate		boolean init();
Xprivate		   void	ins_event();
Xprivate		boolean ins_ctrl();
Xprivate		boolean ins_note();
Xprivate		    int issymbol();
Xprivate		   void	marker();
Xprivate	     event_type nalloc();
Xprivate		   void	parseend();
Xprivate		   void	parsefield();
Xprivate		boolean parsenote();
Xprivate		   void	reverse();
Xprivate		    int scan();
Xprivate		    int scan1();
Xprivate		    int scanint();
X
X/****************************************************************************
X* data structures for parser lookup tables
X****************************************************************************/
X
Xstruct durt {	/* duration translation table */
X    char symbol;
X    long value;
X};
Xstruct durt durtable[5] = {
X    'W', 240,
X    'H', 120,
X    'Q', 60,
X    'I', 30,
X    'S', 15
X};
X
Xstruct loudt {	/* loudness translation table */
X    char symbol[4];
X    int value;
X};
X
Xstruct loudt loudtable[] = {
X    "PPP", 20,
X    "PP\0", 26,
X    "P\0\0", 34,
X    "MP\0", 44,
X    "MF\0", 58,
X    "F\0\0", 75,
X    "FF\0", 98,
X    "FFF", 127
X};
X
Xprivate char *ssymbols[] = {{"TEMPO"}, {"RATE"}};
X			/* this was inside istempo, but */
X			/* I moved it here because of a compiler bug */
X
X#define sym_tempo 0
X#define sym_rate 1
X/* number of symbols */
X#define sym_n 2
X
X#define linesize 100
Xprivate char line[linesize];	/* the input line */
Xprivate char token[linesize];	/* a token scanned from the input line */
X
Xprivate boolean pitch_flag;	/* set when a pitch is indicated */
X	/* (if controls changes are given, only allocate a note event if
X	 *  a pitch was specified -- i.e. when pitch_flag is set)
X	 */
Xprivate boolean rest_flag;	/* set when a rest (R) is found */
X	/* this flag is NOT inherited by the next line */
X
Xprivate boolean symbolic_dur_flag;
X		/* true if last dur was not absolute
X		 * (if this is set, then the default duration is changed
X		 *  accordingly when the tempo is changed.)
X		 */
X
Xprivate boolean ctrlflag[nctrl];
X		/* true if control change was present
X		 * ctrlflag[0] true if ANY control change
X		 * was present
X		 */
Xprivate int ctrlval[nctrl];
X		/* the new value of the control */
X
Xprivate int last_prog;	/* saved value of program from previous line */
X	/* (this is needed to implement the rule that note
X	 *  events are generated for rests if the program has changed.)
X	 */
X
X/****************************************************************************
X*				state variables
X* Because each line of an Adagio score inherits properties from the previous
X* line, it makes sense to implement the parser as a collection of routines
X* that make small changes to some global state.	 For example, pitch is a
X* global variable.  When the field G4 is encountered, the dopitch routine
X* assigns the pitch number for G4 to the variable pitch.  After all fields
X* are processed, these variables describe the current note and contain the
X* default parameters for the next note as well.
X*
X* Global variables that are used in this way by the parsing rountines are:
X****************************************************************************/
Xprivate int
X    linex,	/* index of the next character to be scanned */
X    lineno,	/* current line number */
X    fieldx,	/* index of the current character within a field */
X    pitch,	/* pitch of note */
X    loud,	/* loudness of note */
X    voice,	/* voice (midi channel) of note */
X    program;	/* midi program (timbre control) of note */
X
Xprivate boolean ndurp;		/* set when a next (N) is indicated */
X	/* (next time defaults to the current time plus duration unless
X	 *  overridden by a next (N) command whose presence is signalled
X	 *  by ndurp.)
X	 */
X
Xprivate long
X    thetime,	/* the starting time of the note */
X    rate,	/* time rate -- scales time and duration, default = 100 */
X    ntime,	/* the starting time of the next note */
X    dur,	/* the duration of the note */
X    tempo,	/* the current tempo */
X    start;	/* the reference time (time of last !tempo or !rate cmd) */
X
Xprivate int pitchtable[7] = { 57, 59, 48, 50, 52, 53, 55 };
X
Xextern char score_na[name_length];
X
Xprivate int note_count = 0;	/* the number of notes translated */
Xprivate int ctrl_count = 0;	/* ditto for control commands */
X
Xprivate int debug = false;	/* controls verbose printout */
X
X/****************************************************************************
X*				ctrlalloc
X* Outputs: returns an event_type for representing a control change
X*	   returns NULL if space is unavailable
X* Effect: allocates ctrlsize bytes
X* Assumes: space tells how many bytes are available
X****************************************************************************/
Xprivate event_type ctrlalloc()
X{	
X    char *malloc();
X    event_type result;
X    space -= ctrlsize;
X    if (space > 0) {
X	result = (event_type ) malloc(ctrlsize);
X	if (result == NULL)	/* this should never happen ... */
X	    printf("Internal error: Out of memory, space = %ld.\n",space);
X    } else result = NULL;
X    return result;
X}
X
X/****************************************************************************
X*				do_a_rest
X* Effect: parses a rest (R) command
X****************************************************************************/
X
Xprivate void do_a_rest()
X{
X    if (fieldx < strlen(token))
X	fferror("Nothing expected after rest");
X    rest_flag = true;
X}
X
X/****************************************************************************
X*				doabsdur
X* Effect: parses an absolute dur (U) command
X****************************************************************************/
X
Xprivate void doabsdur()
X{
X    if (isdigit(token[fieldx])) {
X	dur = precise( (long) scanint());
X	dur = (dur * 100) / rate;
X	if (fieldx < strlen(token)) {
X	    fieldx = 2;
X	    fferror("U must be followed by digits only");
X	}
X	symbolic_dur_flag = false;
X    } else fferror("No digit after U");
X}
X
X/****************************************************************************
X*				doabspitch
X* Effect: parses an absolute pitch (P) command
X****************************************************************************/
X
Xprivate void doabspitch()
X{
X    if (isdigit (token[fieldx])) {
X	pitch = scanint();
X	pitch_flag = true;
X	if (fieldx < strlen(token))
X	    fferror("P must be followed by digits only");
X	else if (pitch < minpitch) {
X	    fieldx = 1;
X	    fferror("Minimum pitch of 12 will be used");
X	    pitch = minpitch;
X	} else if (pitch > maxpitch) {
X	    fieldx = 1;
X	    fferror("Maximum pitch of 115 will be used");
X	    pitch = maxpitch;
X	}
X    } else fferror("No digits after P");
X}
X
X/****************************************************************************
X*				docomment
X* Effect: parses a comment (*) command
X****************************************************************************/
X
Xprivate void docomment()
X{
X    line[linex] = 0; /* force end of line to skip comment line */
X}
X
X/****************************************************************************
X*				doctrl
X* Inputs:
X*    n: control number
X* Effect: parses a control (J, K, M, O, X, or Y) command
X****************************************************************************/
X
Xprivate void doctrl(n)
X    int n;
X{
X    ctrlval[n] = scanint();
X    if (fieldx < strlen(token) ) {
X	fferror("Only digits expected here");
X    } else {
X	ctrlflag[n] = true;
X	ctrlflag[0] = true;	/* ctrlflag[0] set if any flag is set */
X    }
X}
X
X/****************************************************************************
X*				dodur
X* Effect: parses a duration (S, I, Q, H, or W) command
X****************************************************************************/
X
Xprivate void dodur()
X{
X    int i, dotcnt = 0;
X    long dotfactor;
X
X    for (i=0; i<=5; i++) {
X	if (durtable[i].symbol == token[fieldx-1]) {
X	    dur = precise(durtable[i].value);
X	    break;
X	}
X    }
X    if (i == 5) {
X	fieldx--;
X	fferror("Duration expected: one of W, H, Q, I, or S");
X	return;
X    }
X    while (fieldx < strlen(token)) {
X	if (token[fieldx] == 'T') {	/* triplet notation */
X	    dur = (dur*2) / 3;
X	    fieldx++;
X	} else if (token[fieldx] == '.') {	/* dotted notation */
X	    dotcnt++;
X	    fieldx++;
X	} else if (isdigit(token[fieldx])) {	/* numbers are multipliers */
X	    dur = dur * (long) scanint();
X	} else {
X	    fferror("Bad duration");
X	    fieldx = strlen(token) + 1;
X	}
X    }
X    dotfactor = 1;
X    for (i=1; i<=dotcnt; i++)
X	dotfactor = dotfactor * 2;
X    dur = (2 * dur) - (dur / dotfactor);
X    dur = (dur * 100) / tempo;	/* time in centiseconds */
X    dur = (dur * 100) / rate;
X    symbolic_dur_flag = true;	/* see symbolic_dur_flag declaration */
X}
X
X/****************************************************************************
X*				doerror
X* Effect: parse an unrecognized field by reporting an error
X****************************************************************************/
X
Xprivate void doerror()
X{
X    fieldx = 0;
X    fferror("Bad field");
X}
X
X/****************************************************************************
X*				doloud
X* Effect: parse a loudness (L) command
X****************************************************************************/
X
Xprivate void doloud()
X{
X    int i, j;
X
X    if (strlen(token) <= 1) {
X	fieldx = 0;
X	fferror("L must be followed by loudness indication");
X	return;
X    }
X    if (isdigit(token[fieldx])) {
X	loud = scanint();
X	if (fieldx < strlen(token))
X	    fferror("Digits expected after L");
X	else if (loud > 127) {
X	    fieldx = 1;
X	    fferror("Maximum loudness of 127 will be used");
X	    loud = 127;
X	}
X	return;
X    }
X    if (strlen(token) > 4 ) {	/* maximum is 4, e.g. "Lppp" */
X	fieldx = 0;
X	fferror("Loudness field too long");
X	return;
X    }
X    if (strlen(token) != 4) {	/* pad short symbols with 0	*/
X	i = strlen(token);	/* e.g. "p\0" -> "p\0\0"	*/
X	token[i+1] = '\0';
X    }
X    
X    for (i = 0; i <= 7; i++) {	/* loop through possibilities	*/
X	for (j = 0; j <= 2; j++) {	/* test 3 characters	*/
X	    if (token[fieldx+j] != loudtable[i].symbol[j])
X		break;
X	}
X	if (j == 3) {
X	    loud = loudtable[i].value;
X	    return;
X	}
X    }
X    fieldx = 1;
X    fferror("Bad loudness indication");
X}
X
X/****************************************************************************
X*				donextdur
X* Effect: parse a next (N) command
X* Implementation:
X*	The syntax is N followed by a duration, so save dur and use dodur()
X*	to parse the duration field.  Then restore dur (what a hack!).
X*	The form N<digits> is parsed directly with scanint().
X****************************************************************************/
X
Xprivate void donextdur()
X{
X    long save;	/* save the current duration */
X    
X    ndurp = true;	/* flag that N was given */
X    if (isdigit(token[fieldx])) {
X	ntime = precise( (long) scanint());
X	if (fieldx < strlen(token))
X	    fferror("Only digits were expected here");
X    } else {
X	fieldx++;
X	save = dur;	
X	dodur();
X	ntime = dur;	/* get the result from dur, */
X	dur = save;	/* and then restore it	*/
X    }
X}
X
X/****************************************************************************
X*				dopitch
X* Effect: parses a pitch command
X****************************************************************************/
X
Xprivate void dopitch()
X{
X    int p, octave;
X    int octflag = false;	/* set if octave is specified */
X
X    p = pitchtable[token[0]-'A'];
X    while (true) {
X	if (token[fieldx] == 'S') {			       /* sharp */
X	    p++;
X	    fieldx++;
X	} else if (token[fieldx] == 'N') {			/* skip */
X	    fieldx++;
X	} else if (token[fieldx] == 'F') {			/* flat */
X	    p--;
X	    fieldx++;
X	} else if (isdigit(token[fieldx]) && !octflag) {      /* octave */
X	    octave = scanint();
X	    octflag = true;
X	} else break;				   /* none of the above */
X    }
X    if (octflag) p = (p-48) + 12 * octave;  /* adjust p to given octave */
X    else {		  /* adjust p to note nearest the default pitch */
X	int octdiff = (p + 126 - pitch) / 12;
X	p = p + 120 - (octdiff * 12);
X    }
X    if (fieldx != strlen(token))	    /* any unparsed characters? */
X	fferror("Bad pitch indication");
X    if (p > maxpitch) {				     /* pitch in range? */
X	fieldx = 1;
X	fferror("Pitch too high");
X	p = maxpitch;
X    }
X    pitch = p;
X    pitch_flag = true;
X}
X
X/****************************************************************************
X*				doprogram
X* Effect: parses a program change (Z) command
X****************************************************************************/
X
Xprivate void doprogram()
X{
X    if (isdigit(token[fieldx])) {
X	program = scanint();
X	if (fieldx < strlen(token)) {
X	    fferror("Z must be followed by digits only");
X	} else if (program < minprogram) {
X	    fieldx = 1;
X	    fferror("Minimum program of 1 will be used");
X	    program = minprogram;
X	} else if (program > maxprogram) {
X	    fieldx = 1;
X	    fferror("Maximum program of 128 will be used");
X	    program = maxprogram;
X	}
X    } else fferror("No digit after Z");
X}
X
X/****************************************************************************
X*				dorate
X* Effect: parses a !rate command
X****************************************************************************/
X
Xprivate void dorate()
X{
X    linex += scan(&line[linex]);
X    if (strlen(token) == 0)
X	fferror("rate number expected");
X    else {
X	long oldrate = rate;
X	fieldx = 0;
X	rate = scanint();
X	if (fieldx < strlen(token) )
X	    fferror("Only digits expected here");
X	if (rate == 0) {
X	    fieldx = 0;
X	    fferror("Rate 100 will be used here");
X	    rate = 100;
X	}
X	start = thetime;
X	/* adjust dur in case it is inherited by next note */
X	    dur = (dur * oldrate);
X	    dur = dur / rate;
X    }
X}
X
X/****************************************************************************
X*				dospecial
X* Effect: parses special (those starting with "!") commands
X****************************************************************************/
X
Xprivate void dospecial()
X{
X    switch (issymbol()) {
X	case sym_tempo: dotempo();
X	    break;
X	case sym_rate: dorate();
X	    break;
X	default: fferror("Special command expected");
X    }
X    parseend(); /* flush the rest of the line */
X}
X
X/****************************************************************************
X*				dotempo
X* Effect: parses a !tempo command
X****************************************************************************/
X
Xprivate void dotempo()
X{
X    linex += scan(&line[linex]);
X    if (strlen(token) == 0)
X	fferror("Tempo number expected");
X    else {
X	long oldtempo = tempo;
X	fieldx = 0;
X	tempo = scanint();
X	if (fieldx < strlen(token))
X	    fferror("Only digits expected here");
X	if (tempo == 0) {
X	    fieldx = 0;
X	    fferror("Tempo 100 will be used here");
X	    tempo = 100;
X	}
X	start = thetime;
X	/* adjust dur in case it is inherited by next note */
X	if (symbolic_dur_flag) {
X	    dur = (dur * oldtempo);
X	    dur = dur / tempo;
X	}
X    }
X}
X
X/****************************************************************************
X*				dotime
X* Effect: parses a time (T) command
X* Implementation: see implementation of donextdur()
X****************************************************************************/
X
Xprivate void dotime()
X{
X    int save;
X
X    if (isdigit(token[fieldx])) {
X	thetime = precise( (long) scanint());
X	if (fieldx < strlen(token) )
X	    fferror("Only digits were expected here");
X	} else {
X	    fieldx++;
X	    save = dur; 
X	    dodur(); 
X	    thetime = dur;
X	    dur = save;
X	}
X    thetime += start;	/* time is relative to start */
X}
X
X/****************************************************************************
X*				dovoice
X* Effect: parse a voice (V) command (the voice is the MIDI channel)
X****************************************************************************/
X
Xprivate void dovoice()
X{
X    if (isdigit(token[fieldx])) {
X	voice = scanint();
X	if (fieldx < strlen(token))
X	    fferror("V must be followed by digits only");
X	if (voice > 16) {
X	    fferror("number too high, using 16 instead");
X	    voice = 16;
X	} else if (voice < 1) {
X	    fferror("number too low, using 1 instead");
X	    voice = 1;
X	}
X    } else fferror("No digit after V");
X}
X
X/****************************************************************************
X*				fferror
X* Inputs:
X*	char *s: an error message string
X* Effect:
X*	prints the line with the error
X*	puts a cursor (^) at the error location
X*	prints the error message (s)
X* Implementation:
X*	this routine prints a carat under the character that
X*	was copied into token[fieldx].	E.g. if fieldx = 0, the
X*	carat will point to the first character in the field.
X****************************************************************************/
X
Xprivate void fferror(s)
Xchar *s;
X{
X    fprintf(stderr, "%3d | ", lineno);
X    fprintf(stderr, "%s", line);
X    marker(linex-strlen(token)+fieldx+1+6);
X    fprintf(stderr, "Error: %s.\n", s);
X}
X
X/****************************************************************************
X*				init
X* Outputs:	Returns true if OK, false on error.
X* Effect:	Initializes program state.
X****************************************************************************/
X
Xprivate boolean init()
X{
X    int i;
X
X    debug = cl_switch("-print");
X
X    for (i = 0; i < nctrl; i++) ctrlflag[i] = false;
X
X    dur = precise ((long) 60); /* quarter note */
X    lineno = 0;
X    thetime = 0;
X    pitch = 48;
X    loud = 127;
X    voice = 1;
X    last_prog = program = 1;
X    tempo = 100;
X    rate = 100;
X    start = 0;
X    symbolic_dur_flag = true; /*scale dur by tempo*/
X    return true;
X}
X
X/****************************************************************************
X*				ins_ctrl
X* Inputs:
X*	event_type *score: a linked list in which to insert
X* Outputs:
X*	returns true on success, false on error (not enough space)
X* Effect: 
X*	control events corresponding to current line are inserted in score
X* Implementation:
X*	ctrlflag[i] is true if control i was specified in this line, so
X*	insert one control change for each ctrlflag[i] that is true
X****************************************************************************/
X
Xprivate boolean ins_ctrl(score)
X    event_type *score;
X{
X    int i;
X    event_type ctrl;
X
X    for (i = 1; i < nctrl; i++) {
X	if (ctrlflag[i]) {
X	    ctrlflag[i] = false;
X	    if ((ctrl = ctrlalloc()) == NULL) {
X		return false;
X	    }
X	    ctrl->ntime = round(thetime);
X	    ctrl->nvoice = ctrl_voice(i, voice-1);
X	    ctrl->nline = lineno;
X	    ctrl->next = NULL;
X	    ctrl->u.ctrl.value = ctrlval[i];
X	    ins_event(score, ctrl);
X	    ctrl_count ++;
X	}
X    }
X    return true;
X}
X
X/****************************************************************************
X*				ins_event
X* Inputs:
X*	event_type *p: a linked list in which to insert
X*	event_type event: the new event to insert
X* Effect: 
X*	inserts event into score in reverse time order (this makes inserts
X*	that are sequential in time go fast)
X****************************************************************************/
X
Xprivate void ins_event(p, event)
X    event_type *p;	/* the score */
X    event_type event;	/* the new event to insert */
X{
X    event_type ptr = *p;
X    event_type prv;
X
X    if (ptr == NULL || event->ntime >= ptr->ntime) {
X	event->next = ptr;	/* insert at head of list */
X	*p = event;
X    } else { /* list insert */
X	while (ptr != NULL && event->ntime < ptr->ntime) {
X	    prv = ptr;
X	    ptr = ptr->next;
X	}
X	prv->next = event;
X	event->next = ptr;
X    }
X}
X
X/****************************************************************************
X*				ins_note
X* Inputs:
X*	event_type *score: a linked list in which to insert
X* Outputs:
X*	returns true on success, false on error (not enough space)
X* Effect:
X*	note event (if any) corresponding to current line are inserted in 
X*	score
X* Implementation:
X*	if a note on should occur after a note off and doesn't, and the
X*	two notes have the same pitch, then the note off can cancel the
X*	note on:
X*		|------------------| <- this cancels *
X*			   this -> |-----------| 
X*	To make it unlikely that roundoff will cause this situation,
X*	dur is decreased by one half of a clock tick before rounding.
X*	Also, phase2 gives precedence to note-offs that are simultaneous
X*	with note-ons.
X****************************************************************************/
X
Xprivate boolean ins_note(score)
X    event_type *score;
X{
X    event_type nalloc(), note;
X    if ((note = nalloc()) == NULL) {
X	return false;
X    }
X    note->ntime = round(thetime);
X    note->nvoice = voice - 1;
X    note->nline = lineno;
X    note->next = NULL;
X    if (rest_flag) note->u.note.npitch = NO_PITCH;	/* a rest */
X    else note->u.note.npitch = pitch;
X    note->u.note.ndur = round(dur - (unity/2));
X    note->u.note.nloud = loud;
X    note->u.note.nprogram = program;
X    if (debug)
X	printf("note: time %ld, dur %ld, pitch %d, voice %d, loudness %d\n",
X		note->ntime, note->u.note.ndur, note->u.note.npitch,
X		note->nvoice, note->u.note.nloud);
X    ins_event(score, note);
X    return true;
X}
X
X/****************************************************************************
X*				issymbol
X* Outputs: returns symbol number, or -1 if no match
X* Assumes: token[1] has the symbol to look up (token[0] == '!')
X****************************************************************************/
X
Xprivate int issymbol()
X{
X    int i, symb_num;
X    char *sym;
X
X    for (symb_num = 0; symb_num < sym_n; symb_num++) {
X	sym = ssymbols[symb_num];
X	i = 1;
X	while (true) {
X	    if (token[i] != *sym) break;
X	    if (*sym == 0) return symb_num;
X	    sym++; i++;
X	}
X    }
X    return -1;
X}
X
X/****************************************************************************
X*				marker
X* Inputs:
X*	int count: the number of characters to indent
X* Effect: 
X*	prints a carat (^) at the position specified on file stderr
X****************************************************************************/
X
Xprivate void marker(count)
Xint count;
X{
X    int i;
X    for (i=1; i<=count-1; i++)
X	fprintf (stderr, " ");
X    fprintf (stderr, "^\n");
X}
X
X/****************************************************************************
X*				nalloc
X* Outputs: returns event_type for an event allocated from heap, NULL on error
X* Effect: allocates memory, decreases space accordingly
X****************************************************************************/
X
Xprivate event_type nalloc()
X{	
X    char *malloc();
X    event_type result;
X    space -= sizeof(struct event_struct);
X    if (space > 0) {
X	result = ((event_type ) malloc (sizeof(struct event_struct)));
X	if (result == NULL)
X	    printf("Internal error: Out of memory, space = %ld.\n",space);
X    } else result = NULL;
X    return result;
X}
X
X/*****************************************************************
X*			parseend
X* Effect:
X*	parse the note terminator, either ",", ";", or "\n"
X*
X****************************************************************/
X
Xprivate void parseend()
X{
X    linex += scan1(&line[linex]);
X    switch (token[0]) {
X	case ',':
X	    ndurp = true;	/* switch that next time was specified */
X	    ntime = 0;
X	    break;
X	case ';':
X	case '\n':
X	    break;
X	default:
X	    fferror("Internal error: illegal separator?");
X	    break;
X    }
X}
X
X/****************************************************************************
X*				parsefield
X* Effect: looks at first character of token and calls a parsing routine
X*
X****************************************************************************/
X
Xprivate void parsefield()
X{
X    fieldx = 1;
X    switch (token[0]) {
X	case 'T' : dotime(); break;
X	case 'W': 
X	case 'H':
X	case 'Q':
X	case 'S':
X	case 'I': dodur()  ; break;
X	case 'R': do_a_rest(); break;
X	case 'A':
X	case 'B':
X	case 'C':
X	case 'D':
X	case 'E':
X	case 'F':
X	case 'G': dopitch(); break;
X	case 'P': doabspitch (); break;
X	case 'U': doabsdur(); break;
X	case 'L': doloud(); break;
X	case 'N': donextdur(); break;
X	case 'J': doctrl(1); break;
X	case 'K': doctrl(2); break;
X	case 'M': doctrl(3); break;
X	case 'O': doctrl(4); break;
X	case 'X': doctrl(5); break;
X	case 'Y': doctrl(6); break;
X	case 'V': dovoice(); break;
X	case 'Z': doprogram(); break;
X	default : doerror(); break;
X    }
X}
X
X/****************************************************************************
X*				parsenote
X* Inputs:
X*	event_type *scoreptr: pointer to the note list
X* Effect: 
X*	parses a note line -- control events (if any) and note event (if
X*	present) are inserted into *scoreptr
X* Assumes:
X*	line contains a string to be parsed
X****************************************************************************/
X
Xprivate boolean parsenote(scoreptr)
X    event_type *scoreptr;	/* the translated note list */
X{
X    boolean out_of_memory = false;
X
X    ndurp = false;
X    rest_flag = false;
X
X    /* this loop reads tokens for a note */
X    while (strlen(token) > 0) {
X	parsefield();
X	linex += scan(&line[linex]);
X    }
X
X    parseend(); /* take care of note terminator */
X
X    /* insert a note if
X     *	(1) a pitch was specified OR
X     *	(2) no control was specified and this is not a rest 
X     *		(it's a pitch by default) OR
X     *	(3) there is a program change (even if this is a rest)
X     *
X     * NOTE: program changes during rests are advised since
X     *	synthesizers may not be able to process a program
X     *	change followed immediately by a note-on.  In fact, this
X     *	is why we insert notes whose pitch is NO_PITCH -- so that
X     *	the program change can be processed during the rest.
X     */
X    if (pitch_flag ||
X	(!ctrlflag[0] && !rest_flag) ||
X	(program != last_prog)) {
X	out_of_memory = !ins_note(scoreptr);
X	note_count ++;
X    }
X    /*
X     * insert ctrl's last so that when the score is reversed,
X     * they will be first.
X     */
X    if (ctrlflag[0]) {
X	out_of_memory |= !ins_ctrl(scoreptr);
X	ctrlflag[0] = false;
X    }
X    last_prog = program;
X
X    if (ndurp) thetime += ntime;
X    else thetime += dur;
X
X    return out_of_memory;
X}
X
X/****************************************************************************
X*				phase1
X* Inputs:
X*	FILE *fp: input file
X* Outputs:
X*	returns event_type: the parsed score
X* Effect: 
X*	parses score from input file and builds score data structure
X****************************************************************************/
X
Xevent_type phase1(fp)
XFILE *fp;
X{
X    event_type score = NULL;	/* the translated note list */
X    boolean out_of_memory = false;	/* set when no more memory */
X
X    if (!init()) {  /* something bad happened in init(), STOP */
X	fprintf(stderr,"WOOPS; something strange happened in INIT()!  ...exiting\n");
X	exit(1);
X	return NULL;	/* make lint happy */
X    }
X
X    lineno = 0;
X
X    /* this loop reads lines */
X    while ((fgets(line, linesize, fp) != NULL) && !out_of_memory) {
X	lineno++;
X	linex = 0;
X	/* this loop reads notes from a line */
X	while ((line[linex] != 0) && !out_of_memory) {
X	    /* loop invariant: line[linex] is first char of next note */
X	    pitch_flag = false;
X	    linex += scan(&line[linex]);
X	    if (!nullstring(token)) {
X		if (token[0] == '*') docomment();
X		else if (token[0] == '!') dospecial();
X		else out_of_memory = parsenote(&score);
X	    } else parseend();
X	}
X    }
X
X    fprintf (stderr,"\n");
X
X    if (out_of_memory) {
X	fprintf(stderr,"Out of note memory at line %d,\n", lineno-1);
X	fprintf(stderr,"    the rest of your file will be ignored.\n");
X    }
X
X    printf (
X	"\nPhase 1 completed; %d note(s), %d ctrl(s) have been translated.\n",
X	note_count, ctrl_count);
X
X    reverse(&score);
X    return score;
X}
X
X/****************************************************************************
X*				reverse
X* Inputs:
X*	event_type *p: pointer to a list of notes and control events
X* Effect: reverses note and control events in p
X****************************************************************************/
X
Xprivate void reverse(p)
Xevent_type *p;
X{
X    event_type p1, p2, p3;
X    p1 = *p;
X    if (p1 == NULL) return;
X    p2 = p1->next;
X    p1->next = NULL;
X    while (p2 != NULL) {
X	p3 = p2->next;
X	p2->next = p1;
X	p1 = p2;
X	p2 = p3;
X    }
X    *p = p1;
X}
X
X/****************************************************************************
X*				scan
X* Inputs:
X*	char *start: the string to scan
X* Outputs:
X*	returns int: the index of the next char in start to scan
X* Effect: 
X*	skips over leading blanks
X*	copies characters from start into token, converting to upper case
X*	scanning stops on delimiter: one of space, tab, newline, semicolon
X****************************************************************************/
X
Xprivate int scan(start)
Xchar *start;
X{
X    int i = 0;
X    int j = 0;
X    char c;
X
X    while ((start[i] == ' ')||(start[i] == '\t')) i++;
X
X    while ((c = start[i]) != ' ' && c != '\t' && c != '\n' &&
X	   c != ',' && c != ';') {
X	if (islower(start[i])) token[j] = toupper(start[i]);
X	else token[j] = start[i];
X	j++; i++;
X    }
X    token[j] = '\0';
X    return i;
X}
X
X/****************************************************************************
X*				scan1
X* Inputs:
X*	char *start: the string to scan
X* Outputs:
X*	returns int: the index of the next char in start to scan
X* Effect: 
X*	copies one char from start into token, converting to upper case
X****************************************************************************/
X
Xprivate int scan1(start)
Xchar *start;
X{
X    int i = 0;
X
X    token[0] = *start;
X    if (islower(token[0])) token[0] = toupper(token[0]);
X    
X    if (!nullstring(token)) {
X	token[1] = '\0';
X	i = 1;
X    }
X    return i;
X}
X
X/****************************************************************************
X*				scanint
X* Outputs:
X*	returns int: the scanned integer
X* Effect:
X*	scans an unsigned integer from token, starting at fieldx
X*	fieldx is incremented to end of the integer
X****************************************************************************/
X
Xprivate int scanint()
X{
X    int i = 0;
X    char c;
X    while (fieldx < strlen(token)) {
X	c = token[fieldx];
X	if ((c >= '0') && (c<= '9')) {
X	    i = (i*10) + (c - '0');
X	    fieldx++;
X	} else return i;
X    }
X    return i;
X}
END_OF_FILE
if test 32964 -ne `wc -c <'phase1.c'`; then
    echo shar: \"'phase1.c'\" unpacked with wrong size!
fi
# end of 'phase1.c'
fi
echo shar: End of archive 5 \(of 6\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 archives.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
