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