📄 seqread.c
字号:
/**************************************************************************** seqread.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. Copyright 1989 Carnegie Mellon University*****************************************************************************//****************************************************************************** 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* 5-Jul-87 | F.H: Introduced new memory handling from Mac version.* | Changed: init()* | ins_event()* | ins_ctrl()* | ins_note()* | Deleted: reverse()* | nalloc()* | Introduced: event_alloc()* | phase1_FreeMem()* | system.h & system.c dependencies* 10-Feb-88 | fixed parseend to accept blanks and tabs,* | fixed rate scaling of durations* 11-Jun-88 | commented out gprintf of \n to ERROR after parsing finished.* 13-Oct-88 | JCD : exclusive AMIGA version.* 13-Apr-89 | JCD : New portable version.* 31-Jan-90 | GWL : Cleaned up for LATTICE* 30-Jun-90 | RBD : further changes* 2-Apr-91 | JDW : further changes* 30-Jun-91 | RBD : parse '+' and '/' in durations, * after space is comment* 28-Apr-03 | DM : changes for portability*****************************************************************************/#include "switches.h"#include <stdio.h>#include <string.h>#include "cext.h"#include "cmdline.h"#include "midifns.h" /* to get time_type */#include "timebase.h"#include "moxc.h" /* to get debug declared */#include "seq.h"#include "seqread.h"#include "userio.h"/* ctype.h used to be included only by UNIX and AMIGA, surely everyone wants this? */#include "ctype.h"#ifndef toupper/* we're already taking precautions, so inline version of toupper is ok: */#define toupper(c) ((c)-'a'+'A')/* CAUTION: AZTEC V5.0 defines an inline version of toupper called _toupper, but they got it wrong! */#endif/* cmtcmd.h references amiga message ports */#ifdef AMIGA#ifdef LATTICE#include "amiga.h"#endif#include "exec/exec.h"#endif#include "cmtcmd.h"/* public stuff */extern long space; /* remaining free bytes */extern int abort_flag;/**************************************************************************** The following are used to simulate fixed point with the radix point 8 bits from the right:****************************************************************************/#define precise(x) (((time_type) x) << 8)#define seqround(x) ((((time_type) x) + 128) >> 8)#define trunc(x) (((time_type) x) >> 8)#define nullstring(s) (s[0] == EOS)/***************************************************************************** Routines local to this module:****************************************************************************/private void do_a_rest();private time_type doabsdur();private int doabspitch();private void doclock();private void docomment();private void doctrl();private void dodef();private time_type dodur();private void doerror();private int doloud();void domacro();private void donextdur();private int dopitch();private void doprogram();private void dorate();private void doset();private void dospecial();private time_type dosymdur();private void dotempo();private void dotime();private void dovoice();private void fferror();private void init();private int issymbol();private void marker();private void parseend();private void parsefield();private boolean parsenote();private boolean parseparm();private int scan();private int scan1();private long scanint();private void scansymb();private long scansgnint();/***************************************************************************** data structures for parser lookup tables****************************************************************************/struct durt { /* duration translation table */ char symbol; time_type value;};#define durtable_len 7struct durt durtable[durtable_len] = { {'W', 4800L}, {'H', 2400L}, {'Q', 1200L}, {'I', 600L}, {'S', 300L}, {'%', 150L}, {'^', 75L}};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}};char too_many_error[] = "Too many parameters";private char *ssymbols[] = {"TEMPO", "RATE", "CSEC", "MSEC", "SETI", "SETV", "CALL", "RAMP", "CLOCK", "DEF", "END"};#define sym_tempo 0#define sym_rate 1#define sym_csec 2#define sym_msec 3#define sym_seti 4#define sym_setv 5#define sym_call 6#define sym_ramp 7#define sym_clock 8#define sym_def 9#define sym_end 10/* number of symbols */#define sym_n 11#define linesize 100private 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.) */#define nctrl 8private 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 */#define nmacroctrl 10short macctrlx; /* index into the following: */short macctrlnum[nmacroctrl]; /* macro ctrl number, e.g. for ~4(67), or * number of parameters for a symbolic macro */short macctrlparmx[nmacroctrl]; /* ctrl value for ctrl change, or index of * parameters for symbolic macro */short macctrlparms[nmacroctrl*nmacroparms]; /* parameters for symbolic macros */short macctrlnextparm;def_type macctrldef[nmacroctrl]; /* definition for symbolic macro */private time_type time_scale; /* 1000 if centisec, 100 if millisec *//* note: user_specified_time * (time_scale / rate) = millisec *//****************************************************************************** variables private to this module*****************************************************************************/private boolean end_flag = FALSE; /* set "true" when "!END" is seen *//***************************************************************************** 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 intlinex, /* 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 */artic; /* articulation (a percentage of duration) */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 time_typethetime, /* 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) */ticksize; /* set by !clock command, zero for no clock */private int pitchtable[7] = { 69, 71, 60, 62, 64, 65, 67 };extern char score_na[name_length];private seq_type the_score; /* this is the score we are parsing *//* def_append -- append a byte to the current definition *//* * The def data structure: * [code][offset][code][offset]...[0][length][data][data][data]... * where code is 1:nmacroparms for %n, * nmacroparms+1 for %v, * nmacroparms+2:nmacroparms*2+1 for ^n * and offset is the byte offset (from the offset byte) to the data * where the parameter should be substituted * and length is the number of data bytes */boolean def_append(def, nparms, data) unsigned char def[]; int nparms; int data;{ int base = (nparms << 1) + 1; /* this byte is the length */ /* first parameter has to be able to reference last byte: */ if ((def[base])++ >= (254 - (nparms << 1))) { fferror("Data too long"); return FALSE; } def[base + def[base]] = data; return TRUE;}def_type def_lookup(symbol) char *symbol;{ def_type defn = seq_dictionary(the_score); while (defn) { if (strcmp(defn->symbol, symbol) == 0) { return defn; } defn = defn->next; } return NULL;}void def_parm(def, nparms, code) unsigned char def[]; int nparms; int code;{ int i, j; /* in order to insert a 2-byte parameter descriptor, the offsets from * previous descriptors (that precede the data) need to be increased by 2: */ for (i = 1; i < (nparms << 1); i += 2) { def[i] += 2; } /* now i is index of length; work backwards from the last byte, moving * everything up by 2 bytes to make room for the new descriptor: */ for (j = i + def[i] + 2; j > i; j--) { def[j] = def[j - 2]; } /* now i is index of offset; insert the descriptor code (first byte) * and the offset to the parameter location in the message (second byte) */ def[i - 1] = code; def[i] = def[i + 2] + 2;}/***************************************************************************** do_a_rest* Effect: parses a rest (R) command****************************************************************************/private void do_a_rest(){ if (token[fieldx]) fferror("Nothing expected after rest"); rest_flag = TRUE;}/***************************************************************************** doabsdur* Effect: parses an absolute dur (U) command****************************************************************************/private time_type doabsdur(){ time_type result=1000L; register char c; if (isdigit(token[fieldx])) { result = precise(scanint()); /* allow comma or paren for use in parameter lists */ if ((c = token[fieldx]) && (c != ',') && (c != ')') && (c != '+')) { fferror("U must be followed by digits only"); } if (time_scale == 1000) result *= 10; /* convert to ms */ } else fferror("No digit after U"); return result;}/***************************************************************************** doabspitch* Effect: parses an absolute pitch (P) command****************************************************************************/private int doabspitch(){ int result = 60; int startx = fieldx; register char c; int savex; if (isdigit (token[fieldx])) { result = (int) scanint(); /* allow comma or paren for abspitch in parameter */ if ((c = token[fieldx]) && c != ',' && c != ')') fferror("P must be followed by digits only"); else if (result < minpitch) { savex = fieldx; fieldx = startx; fferror("Minimum pitch of 0 will be used"); result = minpitch; fieldx = savex; } else if (result > maxpitch) { savex = fieldx; fieldx = startx; fferror("Maximum pitch of 127 will be used"); result = maxpitch; fieldx = savex; } } else fferror("No digits after P"); return result;}/* doartic -- compute an articulation factor *//* NOTE: artic is a percentage that scales the duration of notes but not the time to the next note onset. It is applied to the final computed duration after all other scaling is applied. */private void doartic(){ if (isdigit(token[fieldx])) { artic = (int) scanint(); if (token[fieldx]) fferror("Only digits were expected here"); } else fferror("No digits after /");}/* docall -- parse a call in the form !CALL fn(p1,p2,p3) *//**/private void docall(){ boolean error_flag = TRUE; ndurp = FALSE; linex += scan(); if (token[0] == 0) fferror("Function name expected"); else { char symbol[100]; struct symb_descr *desc; long value[SEQ_MAX_PARMS]; int i=0; scansymb(symbol); if (fieldx == 1) fferror("Routine name expected"); else if (token[fieldx] != '(') fferror("Open paren expected"); else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -