#include #include #include #include #include #include #include extern const char *__progname; static double speedfactor = 1; /* The order implied by these numeric values is important to sort_evtq */ typedef enum { EVTT_OFF = 1, EVTT_ON, EVTT_PED_UP, EVTT_PED_DN } EVTT; typedef struct note NOTE; typedef struct voice VOICE; typedef struct scorenote SCORENOTE; typedef struct score SCORE; typedef struct token TOKEN; typedef struct tuning TUNING; typedef struct macro MACRO; typedef struct mexp MEXP; typedef struct acc ACC; typedef struct dur DUR; typedef struct evt EVT; struct evt { EVT *flink; EVT *blink; EVTT type; int note; int ms; double vol; } ; struct acc { int note; int acc; } ; struct mexp { MEXP *link; unsigned char *text; int len; int ptr; int lineno; int termc; MACRO *mac; } ; struct macro { MACRO *link; char *name; int namelen; char *text; int textlen; int startline; int live; } ; /* * refs counts references from: * - deftune * - voice tune pointers * - scorenote tune pointers * - the name=>tuning mapping table */ struct tuning { TUNING *link; int refs; char *name; int namelen; int c0; } ; struct dur { int kind; #define DK_NONE 1 #define DK_FRAC 2 int num; int den; int dots; } ; struct token { int type; #define TT_EOF 1 #define TT_SETVOICE 2 #define TT_NOTE 3 #define TT_TIE 4 #define TT_CHECK 5 #define TT_BAR 6 #define TT_KEYWORD 7 #define TT_MCALL 8 #define TT_ALIGN 9 union { int voice; struct { DUR dur; int staccato; int note; #define TN_NOTE_REST (-1) #define TN_NOTE_COPY (-2) int accidental; #define TN_ACC_NONE 1 #define TN_ACC_s 2 #define TN_ACC_b 3 #define TN_ACC_n 4 int acccount; int octave; #define TN_OCTAVE_NONE (-1) #define TN_OCTAVE_UP (-2) #define TN_OCTAVE_DOWN (-3) int octshift; } note; int key; #define TK__NONE 0 #define TK_TEMPO 1 #define TK_KEY 2 #define TK_TUNING 3 #define TK_TUNE 4 #define TK_TUNENOW 5 #define TK_VOL 6 #define TK_MVOL 7 #define TK_MACRO 8 #define TK_IDLE 9 #define TK_PEDAL 10 #define TK_UP 11 #define TK_DOWN 12 } u; } ; struct scorenote { int note; int bars; union { struct { int octave; double time; } note; struct { double time; } rest; double vol; TUNING *tune; int pedal; #define PEDAL_UP 1 #define PEDAL_DN 2 } u; } ; struct score { SCORENOTE *notes; int length; int alloc; double time; unsigned int flags; #define SF_IDLE 0x00000001 } ; struct note { NOTE *link; int id; int dead; int mnote; double vol; } ; struct voice { SCORE *score; int scorex; double playat; NOTE *curnote; SCORENOTE *cursn; double vol; TUNING *tune; } ; static double tick; static int keysig[7]; static ACC *accs; static int nacc; static int aacc; static int bars; #define F_REST (-1) #define F_TIE (-2) #define F_END (-3) #define F_VOL (-4) #define F_TUNE (-5) #define F_PEDAL (-6) static int lineno; static MACRO *macros; static TUNING *tunings; static NOTE *notes; static SCORE **scores; static int nscores; static VOICE *voices; static double defvol; static TUNING *deftune; static MEXP *exps; static EVT *evtq; static void tuning_ref(TUNING *t) { t->refs ++; if (t->refs < 1) abort(); } static void tuning_deref(TUNING *t) { t->refs --; if (t->refs < 0) abort(); if (t->refs == 0) { free(t->name); free(t); } } static void init_voice(VOICE *v, SCORE *s) { v->score = s; v->scorex = 0; v->playat = 0; v->curnote = 0; v->cursn = 0; v->vol = defvol; v->tune = deftune; tuning_ref(deftune); } static TUNING *new_tuning(int note, double Hz) { TUNING *t; /* Shift octave until we're within half an octave of A-440. We more or less arbitrarily place D# below, rather than above, A. */ while (Hz < 302.2698024407796024) /* 440 * 2^(-6.5/12) */ { Hz *= 2; note += 12; } while (Hz > 604.5396048815592048) /* 440 * 2^(5.5/12) */ { Hz /= 2; note -= 12; } /* Now find which note is closest to the frequency given. These magic numbers are 440*2^(j/24) for odd values of j, from -11 through +9; these represent the points a quarter-tone between each note and the next. (The j value corresponds to the i value on the same line according to j=i+i+1.) */ if (Hz <= 320.2437002252812180) note += 6; else if (Hz <= 339.2863815897469812) note += 5; else if (Hz <= 359.4613997130419656) note += 4; else if (Hz <= 380.8360868427029317) note += 3; else if (Hz <= 403.4817790100553420) note += 2; else if (Hz <= 427.4740541075865822) note += 1; else if (Hz <= 452.8929841231364927) ; else if (Hz <= 479.8234023727133701) note -= 1; else if (Hz <= 508.3551866238001245) note -= 2; else if (Hz <= 538.5835590540483052) note -= 3; else if (Hz <= 570.6094040464442530) note -= 4; else note -= 5; /* Now, note is the note in the program's representation that corresponds to A-440. This is MIDI value 0x45. The MIDI value corresponding to the notation's C0 (value 0) is thus 0x45-note. */ t = malloc(sizeof(TUNING)); t->refs = 0; t->name = 0; t->namelen = 0; t->c0 = 0x45 - note; return(t); } static void init_postscore(void) { int i; evtq = 0; tick = 0; voices = malloc(nscores*sizeof(VOICE)); for (i=0;ilength;j++) { SCORENOTE *sn; sn = scores[i]->notes + j; printf(" %d: ",j); switch (sn->note) { case F_REST: printf("rest %d\n",sn->u.rest.time); break; case F_TIE: printf("tie\n"); break; case F_END: printf("end\n"); break; case F_WAVE: printf("wave %p (%.*s)\n",(void *)sn->u.wave,sn->u.wave->namelen,sn->u.wave->name); break; case F_ENV: printf("env %p (%.*s)\n",(void *)sn->u.env,sn->u.env->namelen,sn->u.env->name); break; case F_VOL: printf("vol %g\n",sn->u.vol); break; case F_TUNE: printf("tune %p (%.*s)\n",(void *)sn->u.tune,sn->u.tune->namelen,sn->u.tune->name); break; default: if (sn->note < 0) { printf("%d (?)\n",sn->note); } else { printf("%d octave %d time %d\n",sn->note,sn->u.note.octave,sn->u.note.time); } break; } } } } #endif } static NOTE *new_note(void) { NOTE *n; static int nextid = 1; n = malloc(sizeof(NOTE)); n->id = nextid++; n->dead = 0; n->link = notes; notes = n; return(n); } static void gen_event(unsigned char c1, unsigned char c2, unsigned char c3, unsigned char c4, unsigned char c5, unsigned char c6, unsigned char c7, unsigned char c8) { /* putchar(c1); putchar(c2); putchar(c3); putchar(c4); putchar(c5); putchar(c6); putchar(c7); putchar(c8); */ printf("%02x %02x %02x %02x %02x %02x %02x %02x\n",c1,c2,c3,c4,c5,c6,c7,c8); } static void gen_evt(EVTT type, int note, double vol) { EVT *e; e = malloc(sizeof(EVT)); e->type = type; e->note = note; e->vol = vol; e->ms = (tick * 1000) + .5; e->flink = evtq; evtq = e; } static NOTE *start_note(int mnval, double vol) { NOTE *n; if ((mnval >= 21) && (mnval <= 108)) { gen_evt(EVTT_ON,mnval,vol); } else { fprintf(stderr,"starting out-of-range note %d\n",mnval); abort(); } n = new_note(); n->vol = vol; n->mnote = mnval; /* n->volcomp = ((freq > 500) ? 1 : (500/freq)) / 2; */ return(n); } static void kill_note(NOTE *n) { gen_evt(EVTT_OFF,n->mnote,0); n->dead = 1; } static int fire_notes(void) { int i; int n; VOICE *v; SCORENOTE *sn; int any; any = 0; for (i=0;iplayat >= 0) && (tick >= v->playat)) { sn = v->score->notes + v->scorex; switch (sn->note) { case F_TIE: if (! v->cursn) continue; switch (v->cursn->note) { case F_REST: if (v->score->notes[v->scorex+1].note == F_REST) { v->playat += v->score->notes[v->scorex+1].u.rest.time; v->scorex += 2; continue; } break; default: if (v->score->notes[v->scorex+1].note == v->cursn->note) { v->playat += v->score->notes[v->scorex+1].u.note.time; v->scorex += 2; continue; } break; } break; case F_TUNE: tuning_ref(sn->u.tune); tuning_deref(v->tune); v->tune = sn->u.tune; v->scorex ++; continue; break; case F_VOL: v->vol = sn->u.vol; v->scorex ++; continue; break; case F_PEDAL: switch (sn->u.pedal) { case PEDAL_UP: gen_evt(EVTT_PED_UP,128,0); break; case PEDAL_DN: gen_evt(EVTT_PED_DN,128,0); break; default: abort(); break; } v->scorex ++; continue; break; } any = 1; if (v->curnote) { kill_note(v->curnote); v->curnote = 0; } switch (sn->note) { case F_END: v->playat = -1; break; case F_REST: v->curnote = 0; v->cursn = sn; v->scorex ++; v->playat += sn->u.rest.time; break; case F_TIE: v->curnote = 0; v->scorex ++; break; default: if (sn->note < 0) { fprintf(stderr,"%s: playing bad note %d\n",__progname,sn->note); abort(); } n = v->tune->c0 + (sn->note + (12*sn->u.note.octave)); if ((n < 21) || (n > 108)) { fprintf(stderr,"playing out-of-range note %d (%d+%d+(12*%d)) at %g in %d voice %d\n",n,v->tune->c0,sn->note,sn->u.note.octave,v->playat,sn->bars,i); exit(1); } v->curnote = start_note(v->tune->c0+(sn->note+(12*sn->u.note.octave)),v->vol*voices[0].vol); v->cursn = sn; v->scorex ++; v->playat += sn->u.note.time; break; } } } return(any); } static void gen_something(void) { NOTE *n; NOTE **np; double nx; int i; VOICE *v; if (fire_notes()) return; np = ¬es; i = 0; while ((n = *np)) { if (n->dead) { *np = n->link; free(n); i = 1; } else { np = &n->link; } } if (i) return; nx = -1; for (i=0;iplayat < 0) continue; if ((nx < 0) || (v->playat < nx)) nx = v->playat; } if (nx < 0) abort(); tick = nx; } static EVT *sort_evtq(EVT *list) { EVT *a; EVT *b; EVT *e; EVT **t; if (!list || !list->flink) return(list); a = 0; b = 0; while (list) { e = list; list = e->flink; e->flink = a; a = e; if (! list) break; e = list; list = e->flink; e->flink = b; b = e; } a = sort_evtq(a); b = sort_evtq(b); t = &list; while (a || b) { if ( a && ( !b || (a->ms < b->ms) || ( (a->ms == b->ms) && ((int)a->type < (int)b->type) ) ) ) { e = a; a = a->flink; } else { e = b; b = b->flink; } *t = e; t = &e->flink; } *t = 0; return(list); } static const char *evttstr(EVTT t) { static char badbuf[16]; switch (t) { case EVTT_OFF: return("OFF"); break; case EVTT_ON: return("ON"); break; case EVTT_PED_UP: return("PED_UP"); break; case EVTT_PED_DN: return("PED_DN"); break; } sprintf(&badbuf[0],"?%d",(int)t); return(&badbuf[0]); } static void move_offs(void) { EVT *head[129]; EVT *tail[129]; int i; EVT *e; for (i=0;i<129;i++) { head[i] = 0; tail[i] = 0; } while (evtq) { e = evtq; evtq = e->flink; e->blink = tail[e->note]; if (e->blink) e->blink->flink = e; else head[e->note] = e; e->flink = 0; tail[e->note] = e; } for (i=0;i<129;i++) { /* * Normal sequence: on-off-on-off-on-off-... with each gap being at * least 20ms wide. * * This can be violated in three basic ways: * - A gap can be <20ms * - Two identical directions (ons or offs) can be adjacent * - An off can appear when equal numbers of ons and offs have * appeared (either after another off, or as the first item). * * If an on-off sequence appears with <20ms between them: * Delete both. * * If an on appears <20ms after the preceding off: * The item preceding the off must be an on (consecutive offs * are compressed); if it is at least 40ms before the on, just * move the off to 20ms before the later on. Otherwise, drop * the off and the later on. * * If two ons appear in succession: * If the second on is <40ms after the first, just delete the * second one; otherwise, insert an off 20ms before the second. * * If an unpaired off appears: * If the next item is an on, and it occurs within 2ms, delete * both; otherwise, abort. * * If an off appears when multiple unmatched ons exist for that * note, drop it. * * We do this in two passes. The first pass handles the last two * conditions; the second handles the other cases. */ EVT *f; EVT *n; int ons; static void drop(EVT *e) { (e->flink ? e->flink->blink : tail[i]) = e->blink; (e->blink ? e->blink->flink : head[i]) = e->flink; printf("#drop %s %d %d\n",evttstr(e->type),e->note,e->ms); free(e); } e = head[i]; if (! e) continue; ons = 0; for (;e;e=f) { f = e->flink; switch (e->type) { case EVTT_ON: ons ++; break; case EVTT_OFF: if (ons < 1) { if (f && (f->type == EVTT_ON) && (f->ms <= e->ms+2)) { drop(f); f = e->flink; drop(e); continue; } fprintf(stderr,"%s %d %d\n",evttstr(e->type),e->note,e->ms); abort(); } else if (ons > 1) { drop(e); } ons --; break; default: break; } } e = head[i]; if (! e) continue; f = e->flink; while (f) { /* * If an on-off sequence appears with <20ms between them: * Delete both. */ if ( (e->type == EVTT_ON) && (f->type == EVTT_OFF) && (f->ms < e->ms+20) ) { f = f->flink; drop(e); drop(f); if (! f) break; e = f->blink; if (! e) { e = f; f = e->flink; } continue; } /* * If an on appears <20ms after the preceding off: * The item preceding the off must be an on (consecutive offs * are compressed); if it is at least 40ms before the on, * just move the off to 20ms before the later on. Otherwise, * drop the off and the later on. */ if ( (e->type == EVTT_OFF) && (f->type == EVTT_ON) && (f->ms < e->ms+20) ) { if (! e->blink) abort(); if (e->blink->type != EVTT_ON) abort(); if (e->blink->ms <= f->ms-40) { e->ms = f->ms - 20; } else { drop(e); e = f->blink; drop(f); f = e->flink; continue; } } /* * If two ons appear in succession: * If the second on is <40ms after the first, just delete the * second one; otherwise, insert an off 20ms before the * second. */ if ((e->type == EVTT_ON) && (f->type == EVTT_ON)) { if (f->ms < e->ms+40) { drop(f); } else { n = malloc(sizeof(EVT)); n->blink = e; n->flink = f; e->flink = n; f->blink = n; n->type = EVTT_OFF; n->note = i; n->ms = f->ms - 20; e = f; } f = e->flink; continue; } /* * If two offs appear in succession: * Drop the first one. */ if ((e->type == EVTT_OFF) && (f->type == EVTT_OFF)) { drop(e); e = f->blink; if (! e) { e = f; f = e->flink; } continue; } /* Else, just step down the list. */ e = f; f = e->flink; } /* XXX should sanity-check e here, make sure it's reasonable as a last event in the score. */ } evtq = 0; for (i=0;i<129;i++) { while (head[i]) { e = head[i]; head[i] = e->flink; e->flink = evtq; evtq = e; } } evtq = sort_evtq(evtq); } static void flush(void) { EVT *e; int t; int d; evtq = sort_evtq(evtq); for (e=evtq;e;e=e->flink) printf("#%s %d %d\n",evttstr(e->type),e->note,e->ms); printf("#----\n"); move_offs(); for (e=evtq;e;e=e->flink) printf("#%s %d %d\n",evttstr(e->type),e->note,e->ms); gen_event(0x94,0x00,0x7e,0x7f,0x09,0x01,0xf7,0x00); t = 0; while (evtq) { e = evtq; evtq = e->flink; if (e->ms > t) { d = e->ms - t; gen_event(0x81,0x01,0x00,0x00, ((char *)&d)[0],((char *)&d)[1],((char *)&d)[2],((char *)&d)[3]); t = e->ms; } switch (e->type) { case EVTT_ON: { int vi; vi = (e->vol * 127) + .5; if (vi < 0) vi = 0; else if (vi > 127) vi = 127; gen_event(0x93,0x00,0x90,0x00,e->note,vi,0x00,0x00); } break; case EVTT_OFF: gen_event(0x93,0x00,0x80,0x00,e->note,0x00,0x00,0x00); break; case EVTT_PED_DN: gen_event(0x92,0x00,0xb0,0x00,0x40,0x00,0x7f,0x00); break; case EVTT_PED_UP: gen_event(0x92,0x00,0xb0,0x00,0x40,0x00,0x00,0x00); break; default: abort(); break; } free(e); } d = 1; gen_event(0x81,0x01,0x00,0x00, ((char *)&d)[0],((char *)&d)[1],((char *)&d)[2],((char *)&d)[3]); gen_event(0x81,0x01,0x00,0x00, ((char *)&d)[0],((char *)&d)[1],((char *)&d)[2],((char *)&d)[3]); } static int playing(void) { int i; for (i=0;i= 0) return(1); return(!!notes); } static SCORENOTE *addnote(int voice, int pitch, ...) { int i; SCORENOTE n; SCORE *s; SCORENOTE *sn; va_list ap; double duration; if (voice >= nscores) { scores = realloc(scores,(voice+1)*sizeof(SCORE *)); for (i=nscores;i<=voice;i++) { s = malloc(sizeof(SCORE)); s->notes = 0; s->length = 0; s->alloc = 0; s->time = 0; s->flags = 0; scores[i] = s; } nscores = voice + 1; } duration = 0; n.bars = bars; va_start(ap,pitch); switch (pitch) { case F_REST: default: { DUR *dp; dp = va_arg(ap,DUR *); if (dp) { double tempo; double t; double inc; int i; tempo = va_arg(ap,double); t = (tempo * dp->num) / dp->den; inc = t / 2; for (i=dp->dots;i>0;i--) { t += inc; inc /= 2; } duration = t; } else { duration = 0; } if (pitch == F_REST) { n.note = F_REST; n.u.rest.time = duration; } else if (pitch < 0) { fprintf(stderr,"%s: bad pitch %d to addnote\n",__progname,pitch); abort(); } else { n.note = pitch % 12; n.u.note.octave = pitch / 12; n.u.note.time = duration; } } break; case F_TIE: case F_END: n.note = pitch; break; case F_TUNE: n.note = F_TUNE; n.u.tune = va_arg(ap,TUNING *); tuning_ref(n.u.tune); break; case F_VOL: n.note = F_VOL; n.u.vol = va_arg(ap,double); break; case F_PEDAL: n.note = F_PEDAL; n.u.pedal = va_arg(ap,int); break; } va_end(ap); s = scores[voice]; if (s->length >= s->alloc) { s->notes = realloc(s->notes,(s->alloc=s->length+32)*sizeof(SCORENOTE)); } s->time += duration; s->flags &= ~SF_IDLE; sn = &s->notes[s->length++]; *sn = n; return(sn); } static int check_times(void) { int i; int j; double max; double delta; SCORE *s; if (nscores < 2) return(0); max = scores[1]->time; for (i=2;itime > max) max = scores[i]->time; for (i=1;itime > .001) && !(scores[i]->flags & SF_IDLE)) { return(1); } } for <"scores"> (i=1;itime != max) { delta = max - s->time; for (j=s->length-1;j>=0;j--) { if (s->notes[j].note == F_REST) { s->notes[j].u.rest.time += delta; s->time = max; continue <"scores">; } else if (s->notes[j].note >= 0) { s->notes[j].u.note.time += delta; s->time = max; continue <"scores">; } } return(1); } } return(0); } static TUNING *lookup_tuning(const char *n, int l) { TUNING *t; for (t=tunings;t;t=t->link) if ((t->namelen == l) && !bcmp(t->name,n,l)) break; return(t); } static MACRO *lookup_macro(const char *n, int l) { MACRO *m; for (m=macros;m;m=m->link) if ((m->namelen == l) && !bcmp(m->name,n,l)) break; return(m); } static TUNING *lookup_tuning_and_ptr(const char *n, int l, TUNING ***ppp) { TUNING *t; TUNING **tp; for (tp=&tunings;(t=*tp);tp=&t->link) { if ((t->namelen == l) && !bcmp(t->name,n,l)) { *ppp = tp; return(t); } } return(0); } static int get(void) { int c; while (exps && (exps->ptr >= exps->len)) { MEXP *e; lineno = exps->lineno; if (exps->termc == '\n') lineno ++; e = exps->link; free(exps->text); exps->mac->live --; free(exps); exps = e; } c = exps ? exps->text[exps->ptr++] : getchar(); if (c == '\n') lineno ++; return(c); } static void unget(int c) { if (exps) { if ((exps->ptr < 1) || (c != exps->text[exps->ptr-1])) abort(); exps->ptr --; } else { ungetc(c,stdin); } if (c == '\n') lineno --; } #define DIGITS D(0) D(1) D(2) D(3) D(4) D(5) D(6) D(7) D(8) D(9) static int get_number(int c) { int n; n = 0; while (1) { switch (c) { #define D(nn) case '0'+nn: n = (n * 10) + nn; break; DIGITS #undef D default: unget(c); return(n); break; } c = get(); } } static double get_float(int c) { double n; int dv; int gotdot; double d; n = 0; gotdot = 0; d = 10; while (1) { switch (c) { #define D(nn) case '0'+nn: dv = nn; break; DIGITS #undef D case '.': if (gotdot) { default: unget(c); return(n); } gotdot = 1; dv = -1; break; } if (dv >= 0) { if (gotdot) { n += dv / d; d *= 10; } else { n = (n * 10) + dv; } } c = get(); } } static int get_skipwsc(void) { int c; while <"comments"> (1) { c = get(); switch <"intro"> (c) { case '#': while (1) { c = get(); switch (c) { case '\n': continue <"comments">; break; case EOF: case <"intro"> EOF: return(EOF); break; } } break; } if (! isspace(c)) return(c); } } static void verrmsg(const char *, va_list) __attribute__((__format__(__printf__,1,0))); static void verrmsg(const char *fmt, va_list ap) { static void foo(MEXP *e) { if (! e) return; foo(e->link); fprintf(stderr,"line %d, $%.*s, ",e->lineno,e->mac->namelen,e->mac->name); } fprintf(stderr,"%s: ",__progname); foo(exps); fprintf(stderr,"line %d: ",lineno); vfprintf(stderr,fmt,ap); fprintf(stderr,"\n"); } static void errmsg(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void errmsg(const char *fmt, ...) { va_list ap; va_start(ap,fmt); verrmsg(fmt,ap); va_end(ap); } static void err(const char *, ...) __attribute__((__format__(__printf__,1,2))); static void err(const char *fmt, ...) { va_list ap; va_start(ap,fmt); verrmsg(fmt,ap); va_end(ap); exit(1); } static TOKEN get_token(void) { int c; TOKEN t; int n; int i; c = get_skipwsc(); t.type = TT_NOTE; t.u.note.dur.kind = DK_NONE; t.u.note.staccato = 0; t.u.note.accidental = TN_ACC_NONE; t.u.note.octave = TN_OCTAVE_NONE; switch (c) { case EOF: t.type = TT_EOF; return(t); break; case '-': t.type = TT_TIE; return(t); break; case '?': t.type = TT_CHECK; return(t); break; case '|': t.type = TT_BAR; return(t); break; case '$': t.type = TT_MCALL; return(t); break; #define D(nn) case '0'+nn: DIGITS #undef D { int num; int den; n = get_number(c); c = get(); switch (c) { case ':': t.type = TT_SETVOICE; t.u.voice = n; return(t); break; case '!': t.type = TT_ALIGN; t.u.voice = n; return(t); break; case '(': c = get(); #define D(nn) case '0'+nn: switch (c) { DIGITS break; default: err("invalid () after duration"); break; } den = get_number(c); c = get(); switch (c) { case '/': c = get(); switch (c) { DIGITS break; default: err("invalid () after duration"); break; } num = get_number(c); c = get(); switch (c) { case ')': break; default: err("invalid () after duration"); break; } break; case ')': if ((den-1) & (den-2)) err("(%d) not a recognized abbrevation",den); num = den - 1; break; default: err("invalid () after duration"); break; } c = get(); den *= n; n = DK_FRAC; #undef D break; default: num = 1; den = n; break; } t.u.note.dur.kind = DK_FRAC; switch (n) { case DK_FRAC: t.u.note.dur.num = num; t.u.note.dur.den = den; break; default: if ((n < 0) || (n & (n-1))) err("invalid note duration %d",n); t.u.note.dur.num = 1; t.u.note.dur.den = n; break; } t.u.note.dur.dots = 0; t.type = TT_NOTE; while (c == '.') { t.u.note.dur.dots ++; c = get(); } } break; default: if (islower(c)) { char tokbuf[16]; int toklen; toklen = 1; tokbuf[0] = c; while (1) { c = get(); if ((c == EOF) || isspace(c)) break; if (toklen >= sizeof(tokbuf)) err("unrecognized keyword"); tokbuf[toklen++] = c; } unget(c); t.type = TT_KEYWORD; t.u.key = TK__NONE; switch (toklen) { case 2: if (!bcmp(&tokbuf[0],"up",2)) t.u.key = TK_UP; break; case 3: if (!bcmp(&tokbuf[0],"key",3)) t.u.key = TK_KEY; break; case 4: if (!bcmp(&tokbuf[0],"tune",4)) t.u.key = TK_TUNE; if (!bcmp(&tokbuf[0],"idle",4)) t.u.key = TK_IDLE; if (!bcmp(&tokbuf[0],"mvol",4)) t.u.key = TK_MVOL; if (!bcmp(&tokbuf[0],"down",4)) t.u.key = TK_DOWN; break; case 5: if (!bcmp(&tokbuf[0],"tempo",5)) t.u.key = TK_TEMPO; if (!bcmp(&tokbuf[0],"tune!",5)) t.u.key = TK_TUNENOW; if (!bcmp(&tokbuf[0],"macro",5)) t.u.key = TK_MACRO; if (!bcmp(&tokbuf[0],"pedal",5)) t.u.key = TK_PEDAL; break; case 6: if (!bcmp(&tokbuf[0],"tuning",6)) t.u.key = TK_TUNING; if (!bcmp(&tokbuf[0],"volume",6)) t.u.key = TK_VOL; break; } if (t.u.key == TK__NONE) err("unrecognized keyword"); return(t); } break; } if (c == 's') { t.u.note.staccato = 1; c = get(); } switch (c) { case 'C': t.u.note.note = 0; break; case 'D': t.u.note.note = 1; break; case 'E': t.u.note.note = 2; break; case 'F': t.u.note.note = 3; break; case 'G': t.u.note.note = 4; break; case 'A': t.u.note.note = 5; break; case 'B': t.u.note.note = 6; break; case '=': t.u.note.note = TN_NOTE_COPY; return(t); break; case 'R': case 'r': case '_': t.u.note.note = TN_NOTE_REST; return(t); break; default: err("missing/invalid note"); break; } c = get(); switch (c) { case 's': t.u.note.accidental = TN_ACC_s; if (0) { case 'b': t.u.note.accidental = TN_ACC_b; } t.u.note.acccount = 0; i = c; do { t.u.note.acccount ++; c = get(); } while (c == i); break; case 'n': t.u.note.accidental = TN_ACC_n; c = get(); break; } switch (c) { #define D(nn) case '0'+nn: DIGITS #undef D t.u.note.octave = get_number(c); break; case '^': t.u.note.octave = TN_OCTAVE_UP; if (0) { case 'v': t.u.note.octave = TN_OCTAVE_DOWN; } t.u.note.octshift = 0; i = c; do { t.u.note.octshift ++; c = get(); } while (c == i); unget(c); break; default: unget(c); break; } return(t); } static int lookup_accidental(int note, int *accp) { int i; for (i=0;i= aacc) accs = realloc(accs,(aacc=nacc+8)*sizeof(ACC)); accs[nacc++] = (ACC){ .note=note, .acc=acc }; } static int compute_note(const TOKEN *n, const TOKEN *pn, int doacc) { int note; int acc; int macc; note = "\0\2\4\5\7\11\13"[n->u.note.note]; switch (n->u.note.accidental) { case TN_ACC_NONE: acc = keysig[n->u.note.note]; break; case TN_ACC_s: acc = n->u.note.acccount; break; case TN_ACC_b: acc = - n->u.note.acccount; break; case TN_ACC_n: acc = 0; break; default: abort(); break; } note += acc; switch (n->u.note.octave) { case TN_OCTAVE_NONE: { int inc; inc = 0; if (0) { case TN_OCTAVE_UP: inc = n->u.note.octshift; } if (0) { case TN_OCTAVE_DOWN: inc = - n->u.note.octshift; } if (pn->type != TT_NOTE) err("invalid octave defaulting"); while (pn->u.note.note-note > 6) note += 12; if (pn->u.note.note-note == 6) err("ambiguous octave defaulting"); note += inc * 12; } break; default: note += 12 * n->u.note.octave; break; } if (doacc) { switch (n->u.note.accidental) { case TN_ACC_NONE: if (lookup_accidental(note-acc,&macc)) note += macc - acc; break; case TN_ACC_s: case TN_ACC_b: case TN_ACC_n: save_accidental(note-acc,acc); break; } } return(note); } static void set_voice_tune(int voice, TUNING *t) { if (voice < 0) { tuning_ref(t); tuning_deref(deftune); deftune = t; } else { addnote(voice,F_TUNE,t); } } static void get_wsterm(char **sp, int *lp) { char *b; int a; int l; int c; b = 0; a = 0; l = 0; c = get_skipwsc(); while (1) { if ((c == EOF) || isspace(c)) { if (c != EOF) unget(c); *sp = b; *lp = l; return; } if (l >= a) b = realloc(b,a=l+8); b[l++] = c; c = get(); } } static void init_keysig(void) { int i; for (i=0;i<7;i++) keysig[i] = 0; nacc = 0; } static void readscores(void) { int c; int voice; double tempo; TOKEN last_note; TOKEN tok; nscores = 0; scores = 0; tempo = 0; defvol = 1; init_keysig(); accs = 0; aacc = 0; bars = 0; voice = -1; lineno = 1; last_note.type = TT_EOF; while <"mainparse"> (1) { tok = get_token(); switch (tok.type) { case TT_EOF: break <"mainparse">; case TT_SETVOICE: if (tok.u.voice < 1) err("there is no voice 0"); voice = tok.u.voice; last_note.type = TT_EOF; break; case TT_NOTE: if (tok.u.note.dur.kind == DK_NONE) { if (last_note.type != TT_NOTE) err("invalid default time"); tok.u.note.dur = last_note.u.note.dur; } switch (tok.u.note.note) { case TN_NOTE_COPY: if (last_note.type != TT_NOTE) err("invalid default time"); tok.u.note.note = last_note.u.note.note; break; case TN_NOTE_REST: break; default: tok.u.note.note = compute_note(&tok,&last_note,1); if (tok.u.note.note < 0) err("note below octave 0"); break; } if (tempo == 0) err("tempo not yet set"); if (tok.u.note.note == TN_NOTE_REST) { addnote(voice,F_REST,&tok.u.note.dur,tempo); } else if (tok.u.note.staccato) { DUR d; d = tok.u.note.dur; d.den *= 4; addnote(voice,tok.u.note.note,&d,tempo); d.num *= 3; addnote(voice,F_REST,&d,tempo); } else { addnote(voice,tok.u.note.note,&tok.u.note.dur,tempo); } last_note = tok; break; case TT_TIE: if (voice < 0) err("no voice set yet"); addnote(voice,F_TIE); break; case TT_BAR: bars ++; nacc = 0; /* fall through */ case TT_CHECK: if (check_times()) { int i; errmsg("times don't match"); for (i=1;itime); if (scores[i]->flags & SF_IDLE) fprintf(stderr," (idle)"); fprintf(stderr,"\n"); } exit(1); } break; case TT_ALIGN: if (tok.u.voice < 1) err("there is no voice 0"); if (tok.u.voice == voice) err("syncing voice %d with itself",voice); if (tok.u.voice >= nscores) err("syncing with nonexistent voice %d",tok.u.voice); if ( (voice >= nscores) || (scores[voice]->time < scores[tok.u.voice]->time) ) { SCORENOTE *n; n = addnote(voice,F_REST,0); n->u.rest.time = scores[tok.u.voice]->time - scores[voice]->time; scores[voice]->time = scores[tok.u.voice]->time; } break; case TT_KEYWORD: switch (tok.u.key) { case TK_TEMPO: { int dur; int dots; int bpm; double notedur; double dotval; c = get_skipwsc(); if (! isdigit(c)) err("invalid tempo duration"); dur = get_number(c); if (dur & (dur-1)) err("invalid tempo duration"); dots = -1; do { dots ++; c = get(); } while (c == '.'); unget(c); c = get_skipwsc(); if (! isdigit(c)) err("invalid tempo beats/minute"); bpm = get_number(c); notedur = speedfactor / dur; dotval = notedur / 2; for (;dots>0;dots--) { notedur += dotval; dotval /= 2; } tempo = 60 / (bpm * notedur); } break; case TK_KEY: { TOKEN t; init_keysig(); while (1) { c = get_skipwsc(); if (c == '.') break; unget(c); t = get_token(); if (t.type != TT_NOTE) err("non-note in key signature"); if (t.u.note.dur.kind != DK_NONE) err("duration not allowed in key signature"); if (t.u.note.octave != TN_OCTAVE_NONE) err("octave specifier not allowed in key signature"); switch (t.u.note.accidental) { case TN_ACC_NONE: err("accidental marker required in key signature"); break; case TN_ACC_s: keysig[t.u.note.note] = t.u.note.acccount; break; case TN_ACC_b: keysig[t.u.note.note] = - t.u.note.acccount; break; case TN_ACC_n: keysig[t.u.note.note] = 0; break; } } } break; case TK_TUNING: { TOKEN note; double freq; char *b; int l; TUNING *t; get_wsterm(&b,&l); if (0) { case TK_TUNENOW: b = 0; } note = get_token(); if (note.type != TT_NOTE) err("non-note in tuning spec"); if (note.u.note.dur.kind != DK_NONE) err("duration not allowed in tuning spec"); if (note.u.note.octave < 0) err("absolute octave specifier required in tuning spec"); c = get_skipwsc(); freq = get_float(c); if (freq == 0) err("missing/invalid frequency in tuning spec"); t = new_tuning(compute_note(¬e,0,0),freq); if (b) { TUNING *old; TUNING **oldp; t->name = b; t->namelen = l; tuning_ref(t); old = lookup_tuning_and_ptr(b,l,&oldp); if (old) { *oldp = old->link; tuning_deref(old); } t->link = tunings; tunings = t; } else { set_voice_tune(voice,t); } } break; case TK_TUNE: { char *b; int l; get_wsterm(&b,&l); #define CASE(tk,typ,lkup,str,set) case tk: do { typ *v; v = lkup(b,l); \ if (! v) err("unknown " str " name `%.*s'",l,b); set(voice,v); } while (0) switch (tok.u.key) { CASE(TK_TUNE,TUNING,lookup_tuning,"tuning",set_voice_tune); break; } #undef CASE } break; case TK_VOL: { double v; c = get_skipwsc(); v = get_float(c); if (voice < 0) { defvol = v; } else { addnote(voice,F_VOL,v); } } break; case TK_MVOL: { double v; SCORENOTE *n; c = get_skipwsc(); v = get_float(c); if (voice > 0) { n = addnote(0,F_REST,0); if (scores[voice]->time < scores[0]->time) err("retroactive mvol"); n->u.rest.time = scores[voice]->time - scores[0]->time; scores[0]->time = scores[voice]->time; } addnote(0,F_VOL,v); } break; case TK_MACRO: { char *n; int nl; char *b; int a; int l; int ltcount; int redef; int line; MACRO *m; get_wsterm(&n,&nl); c = get_skipwsc(); redef = 0; if (c == '!') { redef = 1; c = get(); } if (c != '<') err("missing < after macro name"); line = lineno; b = 0; a = 0; l = 0; ltcount = 1; while <"body"> (1) { c = get(); switch (c) { case EOF: err("EOF while defining macro `%.*s'",nl,n); break; case '<': ltcount ++; break; case '>': ltcount --; if (ltcount < 1) break <"body">; break; } if (l >= a) b = realloc(b,a=l+16); b[l++] = c; } m = lookup_macro(n,nl); if (m) { if (! redef) err("redefinition of macro `%.*s'",nl,n); free(m->text); free(n); } else { m = malloc(sizeof(MACRO)); m->name = n; m->namelen = nl; m->live = 0; m->link = macros; macros = m; } m->text = b; m->textlen = l; m->startline = line; } break; case TK_IDLE: if (voice < 0) err("idle not valid without a current voice"); addnote(voice,F_REST,0); scores[voice]->flags |= SF_IDLE; break; case TK_PEDAL: { TOKEN t; int ud; SCORENOTE *n; t = get_token(); if (t.type != TT_KEYWORD) err("invalid `pedal'"); switch (t.u.key) { case TK_UP: ud = PEDAL_UP; break; case TK_DOWN: ud = PEDAL_DN; break; default: err("invalid `pedal'"); break; } if (voice > 0) { n = addnote(0,F_REST,0); if (voice >= nscores) addnote(voice,F_REST,0); if (scores[voice]->time < scores[0]->time) err("retroactive `pedal'"); n->u.rest.time = scores[voice]->time - scores[0]->time; scores[0]->time = scores[voice]->time; } addnote(0,F_PEDAL,ud); } break; case TK_UP: err("stray `up'"); break; case TK_DOWN: err("stray `down'"); break; } break; case TT_MCALL: { char *n; int nl; MACRO *m; MEXP *e; get_wsterm(&n,&nl); m = lookup_macro(n,nl); if (! m) err("call to undefined macro `%.*s'",nl,n); if (m->live) err("recursive expansion of macro `%.*s'",nl,n); c = get(); if (c == '\n') lineno --; e = malloc(sizeof(MEXP)); e->text = malloc(m->textlen); bcopy(m->text,e->text,m->textlen); e->len = m->textlen; e->ptr = 0; e->lineno = lineno; lineno = m->startline; e->mac = m; e->termc = c; e->link = exps; exps = e; m->live ++; } break; } } for (c=0;c 0) { skip --; continue; } if (**av != '-') { fprintf(stderr,"%s: unrecognized argument `%s'\n",__progname,*av); errs ++; continue; } if (0) { needarg:; fprintf(stderr,"%s: %s needs a following argument\n",__progname,*av); errs ++; continue; } #define WANTARG() do { if (++skip >= ac) goto needarg; } while (0) if (!strcmp(*av,"-speed")) { WANTARG(); speedfactor *= atof(av[skip]); continue; } #undef WANTARG fprintf(stderr,"%s: unrecognized option `%s'\n",__progname,*av); errs ++; } if (errs) exit(1); } static void init_prescore(void) { deftune = new_tuning(9+(4*12),440); tuning_ref(deftune); tunings = 0; macros = 0; } int main(int, char **); int main(int ac, char **av) { handleargs(ac,av); init_prescore(); readscores(); init_postscore(); while (playing()) gen_something(); flush(); exit(0); }