/* * This file is part of abc2ps, Copyright (C) 1996,1997 Michael Methfessel * See file abc2ps.c for details. */ #include #include #include #include #include #include #include "abc2ps.h" struct STAFF staff_tb[MAXSTAFF]; /* staff table */ int nstaff; /* (0..MAXSTAFF-1) */ tWhistleTab WhistleTab_tb[MAXWHISTLETAB]; /* whistle tablature table */ int nWhistleTab = 0; tStringTab StringTab_tb[MAXSTRINGTAB]; int nStringTab; /* (0..MAXSTRINGTAB-1) */ struct VOICE_S voice_tb[MAXVOICE]; /* voice table */ int nvoice; /* (0..MAXVOICE-1) */ int VoiceMute_tb[MAXVOICE]; int current_voice; /* current voice while parsing */ BOOL VoiceSet; static int nwline; /* current number of vocals */ static char meter_top[81]; /* top part of meter, as string */ static int word,slur; /* variables used for parsing... */ static int last_note,last_real_note; static int pplet,qplet,rplet; static int carryover; /* for interpreting > and < chars */ static int end_slur_next_note; /* for patched in a-b slurs */ static int ntinext,tinext[MAXHD]; /* for chord ties */ char *p; /* global pointers for parsing music line */ char *p0; /* beginning of line */ static int nsym0; /* nsym at start of parsing a line */ static int insert_meter; /* flag to insert time signature */ static char escseq[81]; /* escape sequence string */ static int dlen; /* default length */ int meter1,meter2; /* for meter */ int NoMeter; /* for M:none */ static int mflag,lflag; /* flags for C, l or s in meter */ static char gch[201]; /* guitar chord string */ int sharps_flats,add_pitch; static struct SYMBOL zsym; /* symbol containing zeros */ static char empty_str[] = ""; /* empty string */ /* The original file was implemented with an oddball scheme */ /* combining slurs and ties because they are drawn the same.*/ /* For tablature, we need to distinguish between slurs and */ /* ties so we use this mechanism. */ static int PendingTies[MAXPITCHVAL]; static int get_voice(char *p); static void decomment_line(char *p); static int parse_basic_note(int *pitch, int *length, int *accidental); static char IdentifyDeco(char **t); /* This table is used to adjust the "absolute" pitch for the key in use. */ /* Second index is note C, D, E, F, G, A, B. */ short key_convert[15][7] = { { -1, -1, -1, -1, -1, -1, -1 }, /* seven flats */ { -1, -1, -1, 0, -1, -1, -1 }, /* six flats */ { 0, -1, -1, 0, -1, -1, -1 }, /* five flats */ { 0, -1, -1, 0, 0, -1, -1 }, /* four flats */ { 0, 0, -1, 0, 0, -1, -1 }, /* three flats */ { 0, 0, -1, 0, 0, 0, -1 }, /* two flats */ { 0, 0, 0, 0, 0, 0, -1 }, /* one flat */ { 0, 0, 0, 0, 0, 0, 0 }, /* all natural */ { 0, 0, 0, 1, 0, 0, 0 }, /* one sharp */ { 1, 0, 0, 1, 0, 0, 0 }, /* two sharps */ { 1, 0, 0, 1, 1, 0, 0 }, /* three sharps */ { 1, 1, 0, 1, 1, 0, 0 }, /* four sharps */ { 1, 1, 0, 1, 1, 1, 0 }, /* five sharps */ { 1, 1, 1, 1, 1, 1, 0 }, /* six sharps */ { 1, 1, 1, 1, 1, 1, 1 } /* seven sharps */ }; /* For U: substitution [JSA] 05/00 */ /* ~ @ 0, H-Z @ 1-19, h-w @ 20-35 */ char G_DecoTbl[36] = { D_GRACE, D_HOLD, 0, 0, 0, D_ACCENT, D_LOWM, 0, 0, D_UPM, 0, 0, 0, D_TRILL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, D_UPBOW, D_DOWNBOW, 0 }; /* subroutines connected with parsing the input file */ /* ----- sytax: print message for syntax errror -------- */ static void syntax(char msg[], char *q) { int i,n,len,m1,m2,pp,qq,maxcol=65; if (verbose<=2) printf("\n"); qq=q-p0+1; if (qq<0) qq=0; printf("++++ %s in line %d.%d \n", msg, linenum, qq); m1=0; m2=len=strlen(p0); n=q-p0; if (m2>maxcol) { if (nlen) m2=len; } } printf("%4d ", linenum); pp=5; if (m1>0) { printf("..."); pp+=3; } for (i=m1;i=0 && n<200) { for (i=0;i=MAXSYMS) bug("Internal dimension MAXSYMS too small", 1); k = nsym; nsym++; sym[k] = zsym; sym[k].type = type; sym[k].staff = voice_tb[current_voice].staff; sym[k].StemDir = voice_tb[current_voice].StemDir; sym[k].EndTie = FALSE; for (i = 0; i < MAXHD; i++) sym[k].TabDeco[i] = 0; sym[k].ylo = sym[k].yhi = 0; sym[k].dc.bot = sym[k].dc.top = 0; sym[k].Voice = current_voice; sym[k].Unison = FALSE; sym[k].in_beam = 0; return k; } /* ----- get_clef: get a clef definition ----- */ /* sets the current clef in the current voice */ /* return a pointer to the next non blank char */ static char *get_clef(char *s) { int clef; clef = -1; if (!strncmp(s,"bass", 4)) { clef = BASS; add_pitch = -14; s += 4; } else if (!strncmp(s,"treble", 6)) { clef = TREBLE; add_pitch = 0; s += 6; } else if (!strncmp(s,"alto", 4)) { clef = ALTO; add_pitch = -7; s += 4; } else return s; voice_tb[current_voice].clef = clef; /* current clef */ if (voice_tb[current_voice].nsym == 0) staff_tb[(int) voice_tb[current_voice].staff].clef = clef; /* initial clef */ voice_tb[current_voice].forced_clef = 1; /* don't change */ while (isspace(*s)) s++; return s; } /* ----- get_staves: get staves definition ----- */ void get_staves(char *p) { int i; int staff; int in_bracket = 0; int in_parenth = 0; int in_brace = 0; if (within_tune) { wng("Cannot have staves definition inside a tune", ""); return; } memset(staff_tb, 0, sizeof staff_tb); for (staff = MAXSTAFF; --staff >= 0; ) staff_tb[staff].nvoice = -1; staff = 0; while (*p != '\0') { switch (*p) { case ' ': case '\t': break; case '[': if (in_brace || in_bracket) { wng("Two opening brackets", ""); return; } in_bracket++; if (staff >= MAXSTAFF) { wng("Too many staves", ""); return; } staff_tb[staff].bracket = 1; break; case ']': if (--in_bracket < 0) { wng("No opening brackets", ""); return; } if (staff > 0) staff_tb[staff - 1].bracket_end = 1; break; case '(': if (in_parenth) { wng("Two opening parenthesis", ""); return; } in_parenth++; break; case ')': if (in_parenth == 0) { wng("No opening parenthesis", ""); return; } in_parenth = 0; if (!staff_tb[staff].brace_end) staff++; break; case '{': if (in_parenth || in_brace) { wng("Two opening braces", ""); return; } in_brace++; if (staff >= MAXSTAFF - 1) { wng("Too many staves", ""); return; } staff_tb[staff].brace = 1; staff_tb[staff+1].brace_end = 1; break; case '}': if (in_parenth) { wng("Parenthesis not closed", ""); return; } if (in_brace == 0) { wng("No opening braces", ""); return; } in_brace = 0; if (!staff_tb[staff].brace_end) staff++; /* skip the 2nd staff */ staff++; break; default: if (!isalnum(*p)) { wng("Incorrect character", ""); return; } if (staff >= MAXSTAFF) { wng("Too many staves", ""); return; } i = get_voice(p); voice_tb[i].floating = 0; voice_tb[i].second = 0; switch (in_brace) { case 0: break; case 1: if (!in_parenth) voice_tb[i].floating = 1; in_brace++; break; case 2: voice_tb[i-1].floating = 0; in_brace++; break; case 3: if (!in_parenth && staff_tb[staff].nvoice >= 0) { staff_tb[staff-1].nvoice++; staff_tb[staff].nvoice--; voice_tb[i-1].staff--; voice_tb[i-1].second = 1; voice_tb[i-1].floating = 1; } in_brace++; break; case 4: if (voice_tb[i-1].second) { wng("Too many voices in brace", ""); return; } voice_tb[i-2].floating = 0; voice_tb[i-1].second = 1; in_brace++; break; default: wng("Too many voices in brace", ""); return; } voice_tb[i].staff = staff; if (++staff_tb[staff].nvoice >= 2) { wng("Too many voices on one staff", ""); return; } if (in_parenth) { if (in_parenth > 1) { if (staff_tb[staff].brace_end) { voice_tb[i-1].second = 1; } else { voice_tb[i].second = 1; } } in_parenth++; } else if (!staff_tb[staff].brace_end) ++staff; while (isalnum(*p)) p++; continue; } p++; } nstaff = staff - 1; } /* ----- get_voice: get a voice by name ----- */ /* the voice is created if it does not exist */ static int get_voice(char *p) { char name[32]; int i; int voice; struct SYMBOL *s_sym; int dlen; int meter1,meter2; int mflag,lflag; int sharps_flats; /* There was a bug that caused a crash when selectors were used to */ /* skip over tunes that used multiple voices. When a selected tune */ /* was then parsed, its voices would be preceded by bogus empty */ /* voices. The following line swats that bug. [JSA] */ if (!do_this_tune) return 0; for (i = 0; i < sizeof name - 1; i++) { if (!isalnum(p[i])) break; name[i] = p[i]; } name[i] = '\0'; if (voice_tb[0].name[0] == '\0') voice = 0; /* first voice */ else { for (voice = 0; voice <= nvoice; voice++) { if (strcmp(name, voice_tb[voice].name) == 0) return voice; } if (voice >= MAXVOICE) { wng("Too many voices", ""); voice--; } } nvoice = voice; s_sym = voice_tb[voice].sym; dlen = voice_tb[voice].dlen; meter1 = voice_tb[voice].meter1; meter2 = voice_tb[voice].meter2; mflag = voice_tb[voice].mflag; lflag = voice_tb[voice].lflag; sharps_flats = voice_tb[voice].sharps_flats; memset(&voice_tb[voice], 0, sizeof voice_tb[0]); voice_tb[voice].dlen = dlen; voice_tb[voice].meter1 = meter1; voice_tb[voice].meter2 = meter2; voice_tb[voice].mflag = mflag; voice_tb[voice].lflag = lflag; voice_tb[voice].sharps_flats = sharps_flats; if ((voice_tb[voice].sym = s_sym) == 0) voice_tb[voice].sym = malloc(MAXSYMS * sizeof(struct SYMBOL)); voice_tb[voice].name = getarena(i); strcpy(voice_tb[voice].name, name); voice_tb[voice].staff = nstaff; VoiceSet = TRUE; return voice; } /* ----- voice_init: initialize the general tune characteristics of all potential voices ----- */ static void voice_init(void) { int i; for (i = MAXVOICE; --i >= 0;) { voice_tb[i].dlen = dlen; voice_tb[i].meter1 = meter1; voice_tb[i].meter2 = meter2; voice_tb[i].mflag = mflag; voice_tb[i].lflag = lflag; voice_tb[i].sharps_flats = sharps_flats; voice_tb[i].StemDir = 0; } } /* ----- voice_sw: start/continue parsing of an other voice ----- */ static void voice_sw(int i) { voice_tb[current_voice].nsym = nsym; voice_tb[current_voice].nsym0 = nsym0; voice_tb[current_voice].end_slur_next_note = end_slur_next_note; voice_tb[current_voice].dlen = dlen; voice_tb[current_voice].meter1 = meter1; voice_tb[current_voice].meter2 = meter2; voice_tb[current_voice].mflag = mflag; voice_tb[current_voice].lflag = lflag; voice_tb[current_voice].sharps_flats = sharps_flats; current_voice = i; sym = voice_tb[i].sym; nsym = voice_tb[i].nsym; nsym0 = voice_tb[i].nsym0; end_slur_next_note = voice_tb[i].end_slur_next_note; dlen = voice_tb[i].dlen; meter1 = voice_tb[i].meter1; meter2 = voice_tb[i].meter2; mflag = voice_tb[i].mflag; lflag = voice_tb[i].lflag = lflag; sharps_flats = voice_tb[i].sharps_flats; } /* ----- get_xref: get xref from string ----- */ static int get_xref(char str[]) { int a; char *q; if (strlen(str)==0) { wng("xref string is empty", ""); return 0; } q=str; while (*q != '\0') { if (!isdigit(*q)) { wng("xref string has invalid symbols: ", str); return 0; } q++; } sscanf(str, "%d", &a); return a; } /* ----- set_meter: set meter from string ---- */ static void set_meter(char str[]) { int m1,m2,m1a,m1b,m1c,d,l; char *q, buf[8]; l=strlen(str); if (l==0) { wng("Empty meter string", ""); return; } strncpy(buf, str, 4); buf[4] = 0; NoMeter = 0; if (!stricmp(buf, "none")) { /* M:none */ dlen=EIGHTH; meter1 = meter2 = 4; /* to avoid errors in the symbol spacing functions */ NoMeter = 1; strcpy(meter_top, "free"); } else if (str[0]=='C') { if (str[1]=='|') { meter1=2; mflag=2; } else { meter1=4; mflag=1; } meter2=4; dlen=EIGHTH; strcpy(meter_top,"C"); } else { m1=m2=m1a=m1b=m1c=0; strcpy(meter_top, str); q=strchr(meter_top,'/'); if (!q) { wng("Cannot identify meter, missing /: ", str); m1=m2=1; return; } *q='\0'; if (strchr(meter_top,'+')) { sscanf(str,"%d+%d+%d/", &m1a, &m1b, &m1c); m1=m1a+m1b+m1c; } else { sscanf(str,"%d %d %d/", &m1a, &m1b, &m1c); m1=m1a; if (m1b>m1) m1=m1b; if (m1c>m1) m1=m1c; if (m1>30) { /* handle things like 78/8 */ m1a=m1/100; m1c=m1-100*m1a; m1b=m1c/10; m1c=m1c-10*m1b; m1=m1a; if (m1b>m1) m1=m1b; if (m1c>m1) m1=m1c; } } q++; sscanf(q, "%d", &m2); if (m1*m2 == 0) { wng("Cannot identify meter: ", str); m1=m2=1; return; } d=BASE/m2; if (d*m2 != BASE) { wng("Meter not recognized: ", str); return; } meter1=m1; meter2=m2; dlen=EIGHTH; if (4*meter1 < 3*meter2) dlen=SIXTEENTH; mflag=0; } /* handle old-style change of default length */ lflag=0; if (str[l-1]=='s') { dlen *= 2; lflag=1; } else if (str[l-1]=='l') { dlen /= 2; lflag=-1; } if (verbose>=4) printf("Meter <%s> is %d over %d with default length 1/%d\n", str, meter1, meter2, BASE/dlen); } /* ----- get_sharps_flats: set sharps and flats from string ------ */ /* This part was adapted from abc2mtex by Chris Walshaw */ /* updated 03 Oct 1997 Wil Macaulay - support all modes */ /* sets the current clef in the current voice */ static void get_sharps_flats(char s[], int *sh_fl) { int c,sf,j; char w[81]; /* check for "treble" "bass" "alto" with no other information */ s = get_clef(s); if (*s == '\0') return; c=0; bagpipe=0; switch (s[c]) { case 'F': sf = -1; break; case 'C': sf = 0; break; case 'G': sf = 1; break; case 'D': sf = 2; break; case 'A': sf = 3; break; case 'E': sf = 4; break; case 'B': sf = 5; break; case 'H': bagpipe=1; c++; if (s[c] == 'P') sf=0; else if (s[c] == 'p') sf=2; else wng("unknown bagpipe-like key: ",s); break; default: wng("Using C because key not recognised: ", s); sharps_flats=transpose; return; } c++; if (s[c] == '#') { sf += 7; c++; } else if (s[c] == 'b') { sf -= 7; c++; } /* loop over blank-delimited words: get the next token in lower case */ for (;;) { while (isspace(s[c])) c++; if (s[c]=='\0') break; j=0; while (s[c] != '\0' && !isspace(s[c])) { w[j]=tolower(s[c]); c++; j++; } w[j]='\0'; /* now identify this word */ /* first check for mode specifier */ if ((strncmp(w,"mix",3)) == 0) sf -= 1; /* dorian mode on the second note (D in C scale) */ else if ((strncmp(w,"dor",3)) == 0) sf -= 2; /* phrygian mode on the third note (E in C scale) */ else if ((strncmp(w,"phr",3)) == 0) sf -= 4; /* lydian mode on the fourth note (F in C scale) */ else if ((strncmp(w,"lyd",3)) == 0) sf += 1; /* locrian mode on the seventh note (B in C scale) */ else if ((strncmp(w,"loc",3)) == 0) sf -= 5; /* major and ionian are the same keysig */ else if ((strncmp(w,"maj",3)) == 0) ; else if ((strncmp(w,"ion",3)) == 0) ; /* aeolian, m, minor are the same keysig - sixth note (A in C scale) */ else if ((strncmp(w,"aeo",3)) == 0) sf -= 3; else if ((strncmp(w,"min",3)) == 0) sf -= 3; else if ((strcmp(w,"m")) == 0) sf -= 3; /* check for "bass" "treble" "alto" */ else if (*get_clef(w) == '\0') ; /* clef eaten */ /* check for "+8" or "-8" */ /*fixme:does not work with multi-voices*/ else if (!strcmp(w,"+8")) add_pitch = 7; else if (!strcmp(w,"-8")) add_pitch = -7; else wng("Unknown token in key specifier: ",w); } /* end of loop over blank-delimted words */ if (verbose >= 4) printf("Key <%s> gives sharpsflats %d, cleftype %d\n", s, sf, voice_tb[current_voice].clef); *sh_fl = sf; } /* ----- set_dlen: set default length from string ---- */ static void set_dlen(char str[]) { int l1,l2,d; l1=0; l2=1; sscanf(str,"%d/%d ", &l1, &l2); if (l1 == 0) return; /* empty string.. don't change default length */ d=BASE/l2; if (d*l2 != BASE) { wng("Length incompatible with BASE, using 1/8: ",str); dlen=BASE/8; } else dlen = d*l1; if (verbose>=4) printf("Dlen <%s> sets default note length to %d/%d = 1/%d\n", str, dlen, BASE, BASE/dlen); } /* ----- append_meter: add meter to list of symbols -------- */ static void append_meter(void) { int kk; if ((nsym>0) && (sym[nsym-1].type==TIMESIG)) kk=nsym-1; else kk=add_sym(TIMESIG); sym[kk].u=meter1; sym[kk].v=meter2; sym[kk].w=mflag; sym[kk].text = getarena(strlen(meter_top)); strcpy(sym[kk].text, meter_top); /* if (vb>=30) printf("append_meter <%s> %d %d %d\n", meter_top,meter1,meter2,mflag); */ } /* ----- init_parse_params: initialize variables for parsing --- */ static void init_parse_params(void) { insert_btype=0; slur=0; end_slur_next_note=0; nwline=0; ntinext=0; } /* ----- add_text ---- */ static void add_text(char str[], int type) { if (do_mode!=DO_OUTPUT) return; if (ntext>=NTEXT) { wng("No more room for text line <%s>", str); return; } text[ntext] = getarena(strlen(str)); strcpy(text[ntext], str); text_type[ntext]=type; ntext++; } /* ----- reset_info ---- */ static void reset_info(struct ISTRUCT *inf) { int i; /* reset all info fields except info.xref */ inf->parts = empty_str; inf->area = empty_str; inf->book = empty_str; inf->ncomp = 0; inf->disc = empty_str; inf->group = empty_str; inf->hist = empty_str; inf->info = empty_str; inf->len = empty_str; inf->key = "C"; inf->meter = "4/4"; inf->notes = empty_str; inf->orig = empty_str; inf->rhyth = empty_str; inf->src = empty_str; /* inf->title = "(untitled)"); */ inf->title = empty_str; inf->title2 = empty_str; inf->title3 = empty_str; inf->trans = empty_str; inf->tempo = empty_str; /* For U: substitution [JSA] 05/00 */ /* ~ @ 0, H-Z @ 1-19, h-w @ 20-35 */ for (i = 0; i < 36; i++) inf->DecoTbl[i] = G_DecoTbl[i]; } /* ----- get_default_info: set info to default, except xref field --- */ static void get_default_info(void) { int i; info.parts = default_info.parts; info.area = default_info.area; info.book = default_info.book; info.ncomp = default_info.ncomp; info.disc = default_info.disc; info.group = default_info.group; info.hist = default_info.hist; info.info = default_info.info; info.len = default_info.len; info.key = default_info.key; info.meter = default_info.meter; info.notes = default_info.notes; info.orig = default_info.orig; info.rhyth = default_info.rhyth; info.src = default_info.src; info.title = default_info.title; info.title2 = default_info.title2; info.title3 = default_info.title3; info.trans = default_info.trans; info.tempo = default_info.tempo; /* For U: substitution [JSA] 05/00 */ /* ~ @ 0, H-Z @ 1-19, h-w @ 20-35 */ for (i = 0; i < 36; i++) info.DecoTbl[i] = default_info.DecoTbl[i]; } /* ----- is_info_field: identify any type of info field ---- */ static int is_info_field(char str[]) { if (str[1] == ':' && str[0] != '|') /* not |: at start of music line */ return 1; return 0; } /* ----- is_end_line: identify eof ----- */ static int is_end_line(char str[]) { if (str[0]=='E' && str[1]=='N' && str[2]=='D') return 1; return 0; } /* ----- is_pseudocomment ----- */ static int is_pseudocomment(char str[]) { if (str[0] == '%' && str[1] == '%') return 1; return 0; } /* ----- is_comment ----- */ static int is_comment(char str[]) { if (str[0]=='%' || str[0]=='\\') return 1; return 0; } /* ----- trim_title: move trailing "The" to front ------------ */ static void trim_title(char *s) { char *q, *r; char str[301]; strcpy(str, s); if ((q = strchr(str, ',')) != 0) { if (*q != '\0') { r = q + 1; while (isspace(*r)) r++; if (!strcmp(r, "The")) { *q = '\0'; strcpy(s, "The "); strcat(s, str); return; } } } } /* ----- info_field: identify info line, store in proper place ---- */ /* switch within_block: either goes to default_info or info. Only xref ALWAYS goes to info. */ static int info_field(char str[]) { struct ISTRUCT *inf; int i; char *t, buff[32]; decomment_line(str); if (!is_info_field(str)) return 0; inf = within_block ? &info : &default_info; switch (str[0]) { case 'X': info.xref = strip(&str[2]); xrefnum = get_xref(info.xref); /* Initialize to default of one staff and one voice. */ nstaff = 0; nvoice = 0; current_voice = 0; VoiceSet = FALSE; return XREF; case 'A': inf->area = strip(&str[2]); break; case 'B': inf->book = strip(&str[2]); break; case 'C': if (inf->ncomp>=NCOMP) wng("Too many composer lines",""); else { inf->comp[inf->ncomp] = strip(&str[2]); inf->ncomp++; } break; case 'D': inf->disc = strip(&str[2]); add_text(&str[2], TEXT_D); break; case 'E': break; case 'G': inf->group = strip(&str[2]); break; case 'H': inf->hist = strip(&str[2]); add_text(&str[2], TEXT_H); return HISTORY; case 'I': inf->info = strip(&str[2]); break; case 'K': inf->key = strip(&str[2]); if (!VoiceSet) { strcpy(buff, "V:1 clef=treble"); info_field(buff); } return KEY; case 'L': inf->len = strip(&str[2]); return DLEN; case 'M': inf->meter = strip(&str[2]); return METER; case 'N': inf->notes = strip(&str[2]); add_text(&str[2], TEXT_N); break; case 'O': inf->orig = strip(&str[2]); break; case 'P': inf->parts = strip(&str[2]); return PARTS; case 'Q': inf->tempo = strip(&str[2]); return TEMPO; case 'R': inf->rhyth = strip(&str[2]); break; case 'S': inf->src = strip(&str[2]); break; case 'T': { t = &str[2]; numtitle++; if (numtitle>3) numtitle=3; if (numtitle==1) t = inf->title = strip(&str[2]); else if (numtitle==2) t = inf->title2 = strip(&str[2]); else /*if (numtitle==3)*/ t = inf->title3 = strip(&str[2]); trim_title(t); return TITLE; } case 'U': /* [JSA] 05/00 handle assignable decoration shortcuts, add new decorations. */ t = &str[2]; while (isspace(*t)) t++; if (*t == '~') { i = 0; } else { if ((*t >= 'H') && (*t <= 'Z')) { i = *t - 'G'; } else { if ((*t >= 'h') && (*t <= 'w')) { i = (*t - 'h') + 20; } else { i = -1; } } } if (i < 0) { printf("Warning non-assignable symbol [%c] in U: field!\n", *t); return 0; } t++; inf->DecoTbl[i] = IdentifyDeco(&t); break; case 'V': { int j; char *q; t = &str[2]; while (isspace(*t)) t++; j = nvoice; i = get_voice(t); if (i > j) { /* new voice */ if (nstaff >= MAXSTAFF) { wng("Too many staves",""); return 0; } memset(&staff_tb[++nstaff], 0, sizeof staff_tb[0]); voice_tb[nvoice].staff = nstaff; } voice_sw(i); if ((q = strstr(t, "clef=")) != 0) get_clef(q + 5); else { switch (voice_tb[current_voice].clef) { case TREBLE: add_pitch = 0; break; case BASS: add_pitch = -14; break; case ALTO: add_pitch = -7; break; } } /* [JSA] for forced stem directions */ t = &str[2]; while (isspace(*t)) t++; if ((q = strstr(t, "jsastem="))) { if (strstr(q+8, "dn")) { voice_tb[current_voice].StemDir = -1; } else if (strstr(q+8, "up")) { voice_tb[current_voice].StemDir = 1; } else { voice_tb[current_voice].StemDir = 0; } } #if 0 /*fixme: keep the voice description*/ inf->voice[i] = strip(t); #endif return VOICE_D; } case 'W': add_text(&str[2], TEXT_W); return WORDS; case 'Z': inf->trans = strip(&str[2]); add_text(&str[2], TEXT_Z); break; default: return 0; } return INFO; } /* ----- numeric_pitch ------ */ /* taken from abc2mtex by Chris Walshaw */ /* Modified to add the cpitch parameter */ /* that provides pitch adjust to a scalar*/ /* note position (C=0) instead of a */ /* scalar staff position. JSA 05/01/00 */ static int numeric_pitch(char note) { switch (note) { case 'z': return 14; case 'C': case 'D': case 'E': case 'F': case 'G': return note - 'C' + 16 + add_pitch; case 'A': case 'B': return note - 'A' + 21 + add_pitch; case 'c': case 'd': case 'e': case 'f': case 'g': return note - 'c' + 23 + add_pitch; case 'a': case 'b': return note - 'a' + 28 + add_pitch; } printf("numeric_pitch: cannot identify <%c>\n", note); return 0; } /* ----- append_meter_change ------ */ static void append_meter_change(void) { int m1,m2,f1,l1; m1=meter1; m2=meter2; f1=mflag; l1=lflag; set_meter(info.meter); /* if meter symbol was changed, write the meter symbol */ if ((meter1!=m1) || (meter2!=m2) || (mflag!=f1)) { if (vb>=30) printf("append_meter_change case 1 %d %d %d\n", meter1,meter2,mflag); append_meter(); } /* if meter symbol unchanged, write if not used to change default length */ if ((meter1==m1) || (meter2==m2) || (mflag==f1)) { if (vb>=30) printf("append_meter_change case 2 %d %d %d\n", meter1,meter2,mflag); if (lflag==l1) append_meter(); } } /* ----- append_key_change ------ */ static void append_key_change(void) { int n1,n2,t1,t2,kk,kt; n1=sharps_flats; t1=A_SH; /* get type and sym number for old key */ kt = voice_tb[current_voice].clef; if (n1<0) { n1=-n1; t1=A_FT; } get_sharps_flats(info.key, &sharps_flats); n2=sharps_flats; t2=A_SH; /* get type and sym number for new key */ if (voice_tb[current_voice].clef != kt) { kk=add_sym(CLEF); sym[kk].u = voice_tb[current_voice].clef; sym[kk].v = 1; } if (n2<0) { n2=-n2; t2=A_FT; } if (t1==t2) { /* here if old and new have same type */ if (n2>n1) { /* more new symbols ..*/ kk=add_sym(KEYSIG); /* draw all of them */ sym[kk].u=1; sym[kk].v=n2; sym[kk].w=100; sym[kk].t=t1; } else if (n2f or f->s */ kk=add_sym(KEYSIG); /* neutralize all old symbols */ sym[kk].u=1; sym[kk].v=n1; sym[kk].w=1; sym[kk].t=t1; kk=add_sym(KEYSIG); /* add all new symbols */ sym[kk].u=1; sym[kk].v=n2; sym[kk].w=100; sym[kk].t=t2; } sym[kk].z=sharps_flats; /* save current keysig with symbol*/ } /* ----- init_music_line: init sym list with clef, meter, key ------ */ void init_music_line(int voice) { int kk,n,t; static int meter_f, btype_f; if (verbose>11) printf("init_music_line: s_f=%d, i_m=%d, i_b=%d\n", sharps_flats, insert_meter, insert_btype); current_voice = voice; /* add clef */ kk=add_sym(CLEF); voice_tb[voice].clef = staff_tb[(int) voice_tb[voice].staff].clef; sym[kk].u = voice_tb[voice].clef; sym[kk].v = 0; /* add keysig */ kk = add_sym(KEYSIG); n = voice_tb[voice].sharps_flats; t = A_SH; if (n < 0) { n = -n; t = A_FT; } sym[kk].u=1; sym[kk].v=n; sym[kk].w=100; sym[kk].t=t; sym[kk].z=voice_tb[voice].sharps_flats; /* add time signature if needed */ if (insert_meter || (voice != 0 && meter_f)) { append_meter(); } if (voice == 0) { meter_f = insert_meter; insert_meter = 0; } /* add bar if needed */ if (insert_btype || (voice != 0 && btype_f)) { if (db > 3) printf("insert bar, type %d\n", insert_btype); kk = add_sym(BAR); sym[kk].u = insert_btype; sym[kk].v = insert_num; } if (voice == 0) { btype_f = insert_btype; insert_btype = 0; /* insert_num = 0; ??*/ } } /* ----- handle_escape_sequence ------ */ static void handle_escape_sequence(void) { if (db>3) printf("Handle escape sequence <%s>\n", escseq); switch (info_field(escseq)) { case 0: syntax("Unknown escape sequence", p-1); break; case METER: set_meter(info.meter); append_meter(); break; case KEY: append_key_change(); break; case VOICE_D: if (nsym == 0) init_music_line(current_voice); break; } } /* ----- parse_uint: parse for unsigned integer ----- */ static int parse_uint(void) { int number,ndig; char num[21]; if (!isdigit(*p)) return 0; ndig=0; while (isdigit(*p)) { num[ndig]=*p++; ndig++; } num[ndig]=0; sscanf(num, "%d", &number); if (db>3) printf(" parsed unsigned int %d\n", number); return number; } /* ----- parse_bar: parse for some kind of bar ---- */ static int parse_bar(void) { int k, u, v; u = 0; v = 0; switch (*p) { case '[': /* special cases: [1 or [2 without a preceeding bar, [| */ switch (p[1]) { case '1': case '2': u = B_INVIS; p++; break; case '|': /* code [| for thick-thin bar */ u = B_FAT1; p += 2; break; default: return 0; } break; /* identify valid standard bar types */ case '|': p++; switch (*p) { case '|': u = B_DBL; p++; break; case ':': u = B_LREP; p++; break; case ']': /* code |] for fat end bar */ u = B_FAT2; p++; break; default: u = B_SNGL; } break; case ':': p++; if (*p == '|') { u = B_RREP; p++; } else if (*p == ':') { u = B_DREP; p++; } else { syntax("Syntax error parsing bar", p-1); return 0; } break; default: return 0; } /* see if valid bar is followed by specifier for first or second ending */ switch (*p) { case '1': v = 1; p++; break; case '2': v = 2; p++; break; case '[': if (p[1] == '1') { v = 1; p += 2; } else if (p[1] == '2') { v = 2; p += 2; } break; case ' ': case '\t': if (p[1] == '[') { if (p[2] == '1') { v = 1; p += 3; } else if (p[2] == '2') { v = 2; p += 3; } } break; } k = add_sym(BAR); sym[k].u = u; sym[k].v = v; /* if (db>3) printf (" parsed bar, type %d, end %d\n", u, v); */ return 1; } /* ----- parse_space: parse for whitespace ---- */ static int parse_space(void) { int rc; rc=0; while (isspace(*p)) { rc=1; p++; } if (db>3 && rc) printf(" parsed whitespace\n"); return rc; } /* ----- parse_esc: parse for escape sequence ----- */ static int parse_esc(void) { int nseq; char *pp; if (*p == '\\') { /* try for \...\ sequence */ p++; nseq=0; while ((*p!='\\') && (*p!=0)) { escseq[nseq]=*p; nseq++; p++; } if (*p == '\\') { p++; escseq[nseq]=0; if (db>3) printf(" parsed esc sequence <%s>\n", escseq); return ESCSEQ; } if (cfmt.breakall) return DUMMY; if (db>3) printf(" parsed esc to EOL.. continuation\n"); return CONTINUE; } /* next, try for [..] sequence */ if ((*p=='[') && (*(p+1)>='A') && (*(p+1)<='Z') && (*(p+2)==':')) { pp=p; p++; nseq=0; while ((*p!=']') && (*p!=0)) { escseq[nseq]=*p; nseq++; p++; } if (*p == ']') { p++; escseq[nseq]=0; if (db>3) printf(" parsed esc sequence <%s>\n", escseq); return ESCSEQ; } syntax("Escape sequence [..] not closed", pp); return ESCSEQ; } return 0; } /* ----- parse_nl: parse for newline ----- */ static int parse_nl(void) { if ((*p == '\\')&&(*(p+1)=='\\')) { p+=2; return 1; } return 0; } /* ----- parse_gchord: parse guitar chord, save in buffer ----- */ /**************************************************************** * Changed to recognize "text decorations" with formatting * * prefixes [JSA] 05/00 * ****************************************************************/ static void parse_gchord(int *ct, int dtype[10]) { char *q, buff[256], *bp; int n; tFIFO *Fifo; BOOL FormDone; if (*p != '"') return; q = p; p++; /* Skip past opening double-quote */ n = 0; while (*p && (*p != '"')) { buff[n] = *p; n++; if (n >= 255) { syntax("Text/Chord string too long", q); return; } p++; } buff[n] = '\0'; if (*p == '"') p++; if (*p == 0) { syntax("EOL reached while parsing guitar chord", q); return; } /* We now have the entire quoted section copied into buff as a null-terminated */ /* string. If it is preceded by formatting characters we store it as a text */ /* decoration, otherwise it is a guitar chord. */ /* Furthermore, we parse the formatting characters and store them as numeric */ /* and status variables in the FIFO buffers. By doing this here, instead of */ /* waiting until the music is output, we can interpret them in the context of */ /* the current spacing factors AND when we set up to draw the music we can */ /* more easily preview the values to predict how much vertical space each staff */ /* is going to need. */ if (*ct > 9) return; /* we can only have ten total decorations */ bp = buff; if ((buff[0] == '^') || (buff[0] == '_') || (buff[0] == '@')) { dtype[*ct] = D_STEXT; Fifo = &voice_tb[current_voice].TextDecs; switch(buff[0]) { case '^': Fifo->anchor[Fifo->inidx] = TXTA_TOP; break; case '_': Fifo->anchor[Fifo->inidx] = TXTA_BOT; break; case '@': Fifo->anchor[Fifo->inidx] = TXTA_CLAMP; break; } } else if ((buff[0] == '<') || (buff[0] == '>')) { dtype[*ct] = D_STEXT; Fifo = &voice_tb[current_voice].TextHdDecs; if (buff[0] == '<') { Fifo->anchor[Fifo->inidx] = TXTA_LT; } else { Fifo->anchor[Fifo->inidx] = TXTA_RT; } } else { Fifo = &voice_tb[current_voice].Chords; if (strchr(buff, '/')) { dtype[*ct] = D_GCHORDB; } else { dtype[*ct] = D_GCHORD; } } Fifo->dx[Fifo->inidx] = Fifo->dy[Fifo->inidx] = 0; Fifo->just[Fifo->inidx] = TXTJ_LT; if (dtype[*ct] == D_STEXT) { /* Staff text get additional positioning characters. */ bp++; FormDone = FALSE; while(*bp) { switch (*bp) { case '<': Fifo->dx[Fifo->inidx] -= cfmt.DcDsFont.size * cfmt.StaffTextHFac; break; case '>': Fifo->dx[Fifo->inidx] += cfmt.DcDsFont.size * cfmt.StaffTextHFac; break; case '^': Fifo->dy[Fifo->inidx] += cfmt.DcDsFont.size * cfmt.StaffTextVFac; break; case '_': Fifo->dy[Fifo->inidx] -= cfmt.DcDsFont.size * cfmt.StaffTextVFac; break; case '@': /* We are using any @ after the first character to */ /* indicate the text should be centered. */ Fifo->just[Fifo->inidx] = TXTJ_CN; break; case '{': /* We are using a left curly brace to indicate the */ /* text should be flush right at the note. */ Fifo->just[Fifo->inidx] = TXTJ_RT; break; case '}': /* We are using a right curly brace to indicate the */ /* text should be flush left at the note (default). */ Fifo->just[Fifo->inidx] = TXTJ_LT; break; default: FormDone = TRUE; } if (FormDone) { break; } else { bp++; } } } if (dtype[*ct] == D_NTEXT) { /* Notehead text get additional positioning characters. */ if (Fifo->anchor[Fifo->inidx] == TXTA_LT) Fifo->just[Fifo->inidx] = TXTJ_RT; bp++; FormDone = FALSE; while(*bp) { switch (*bp) { case '<': Fifo->dx[Fifo->inidx] -= 6 * cfmt.NoteTextFac; break; case '>': Fifo->dx[Fifo->inidx] += 6 * cfmt.NoteTextFac; break; case '^': Fifo->dy[Fifo->inidx] += 6 * cfmt.NoteTextFac; break; case '_': Fifo->dy[Fifo->inidx] -= 6 * cfmt.NoteTextFac; break; case '@': /* We are using any @ after the first character to */ /* indicate the text should be centered. */ Fifo->just[Fifo->inidx] = TXTJ_CN; break; case '{': /* We are using a left curly brace to indicate the */ /* text should be flush right at the note. */ Fifo->just[Fifo->inidx] = TXTJ_RT; break; case '}': /* We are using a right curly brace to indicate the */ /* text should be flush left at the note (default). */ Fifo->just[Fifo->inidx] = TXTJ_LT; break; default: FormDone = TRUE; } if (FormDone) { break; } else { bp++; } } } if (Fifo->strings[Fifo->inidx]) { /* We have a FIFO buffer overflow. Warn the user */ printf("Non-fatal error - text decoration FIFO overflow on voice %d.\n", current_voice); } else { Fifo->strings[Fifo->inidx] = (char *)malloc(strlen(buff) + 1); if (Fifo->strings[Fifo->inidx]) { strcpy(Fifo->strings[Fifo->inidx], bp); Fifo->inidx = (Fifo->inidx + 1) % MAXSTRDECO; (*ct)++; } } if (db>3) printf(" parse staff text <%s>\n", bp); } /* [JSA] 05/00 -- implement reassignable decoration shortcuts and new decorations. */ static char IdentifyDeco(char **t) { char buff[256]; char deco = 0; int i = 0; BOOL Unimplemented, Unknown; while(**t && (**t != '!')) (*t)++; if (**t) (*t)++; // First character past opening "!" if (!**t) return deco; while(**t && (**t != '!') && (i < 255)) { buff[i] = **t; i++; (*t)++; } buff[i] = '\0'; Unimplemented = FALSE; Unknown = TRUE; if (!stricmp(buff, "accent")) { deco = D_ACCENT; Unknown = FALSE; } if (!stricmp(buff, "emphasis")) { deco = D_ACCENT; Unknown = FALSE; } if (!stricmp(buff, "upbow")) { deco = D_UPBOW; Unknown = FALSE; } if (!stricmp(buff, "downbow")) { deco = D_DOWNBOW; Unknown = FALSE; } if (!stricmp(buff, "roll")) { deco = D_ROLL; Unknown = FALSE; } if (!stricmp(buff, "fermata")) { deco = D_HOLD; Unknown = FALSE; } if (!stricmp(buff, "trill")) { deco = D_TRILL; Unknown = FALSE; } if (!stricmp(buff, "0")) { deco = D_FINGER0; Unknown = FALSE; } if (!stricmp(buff, "1")) { deco = D_FINGER1; Unknown = FALSE; } if (!stricmp(buff, "2")) { deco = D_FINGER2; Unknown = FALSE; } if (!stricmp(buff, "3")) { deco = D_FINGER3; Unknown = FALSE; } if (!stricmp(buff, "4")) { deco = D_FINGER4; Unknown = FALSE; } if (!stricmp(buff, "5")) { deco = D_FINGER5; Unknown = FALSE; } if (!stricmp(buff, "tenuto")) { deco = D_EMBAR; Unknown = FALSE; } if (!stricmp(buff, "invertedfermata")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "d.s.")) { deco = D_DS; Unknown = FALSE; } if (!stricmp(buff, "d.c.")) { deco = D_DC; Unknown = FALSE; } if (!stricmp(buff, "coda")) { deco = D_CODA; Unknown = FALSE; } if (!stricmp(buff, "segno")) { deco = D_SEGNO; Unknown = FALSE; } if (!stricmp(buff, "longphrase")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "mediumphrase")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "shortphrase")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "breath")) { deco = D_BREATH; Unknown = FALSE; } if (!stricmp(buff, "turn")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "snap")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "thumb")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "open")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "wedge")) { deco = D_WEDGE; Unknown = FALSE; } if (!stricmp(buff, "+")) { deco = D_PLUS; Unknown = FALSE; } if (!stricmp(buff, "pralltriller")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "mordent")) { deco = D_LOWM; Unknown = FALSE; } if (!stricmp(buff, "lowermordent")) { deco = D_LOWM; Unknown = FALSE; } if (!stricmp(buff, "uppermordent")) { deco = D_UPM; Unknown = FALSE; } if (!stricmp(buff, "repeatbar2")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "repeatbar")) { Unimplemented = TRUE; Unknown = FALSE; } if (!stricmp(buff, "fine")) { deco = D_FINE; Unknown = FALSE; } if (!stricmp(buff, "sfz")) { deco = D_sfz; Unknown = FALSE; } if (!stricmp(buff, "ffff")) { deco = D_ffff; Unknown = FALSE; } if (!stricmp(buff, "fff")) { deco = D_fff; Unknown = FALSE; } if (!stricmp(buff, "ff")) { deco = D_ff; Unknown = FALSE; } if (!stricmp(buff, "f")) { deco = D_f; Unknown = FALSE; } if (!stricmp(buff, "mf")) { deco = D_mf; Unknown = FALSE; } if (!stricmp(buff, "p")) { deco = D_p; Unknown = FALSE; } if (!stricmp(buff, "pp")) { deco = D_pp; Unknown = FALSE; } if (!stricmp(buff, "ppp")) { deco = D_ppp; Unknown = FALSE; } if (!stricmp(buff, "pppp")) { deco = D_pppp; Unknown = FALSE; } if (!stricmp(buff, "diminuendo(")) { deco = D_STDIM; Unknown = FALSE; } if (!stricmp(buff, "diminuendo)")) { deco = D_ENDDIM; Unknown = FALSE; } if (!stricmp(buff, "crescendo)")) { deco = D_ENDCRESC; Unknown = FALSE; } if (!stricmp(buff, "crescendo(")) { deco = D_STCRESC; Unknown = FALSE; } if (Unimplemented) printf("Warning: !%s! not implemented - ignored\n", buff); if (Unknown) printf("Warning: !%s! not recognized - ignored\n", buff); return deco; } /* ----- parse_deco: parse for decoration on note ----- */ /* Changed to pass "deco" in as a parameter instead of */ /* merely returning it because now we have to account */ /* for text decorations which are parsed in a different */ /* function. */ static void parse_deco(int *n, int dtype[10], unsigned short *tabdeco) { /* [JSA] */ int deco, val, i; BOOL Done = FALSE; char sp; *tabdeco = 0; /* String tab modification 12/99 [JSA] */ while (!Done) { deco=0; switch (*p) { case '~': if (DECO_IS_ROLL) { deco=D_ROLL; } else { deco=D_GRACE; } break; /* String tablature modification 12/99 [JSA] */ case '@': p++; switch (*p) { case 'H': *tabdeco |= TD_HAMMER; break; case 'P': *tabdeco |= TD_PULL; break; case '~': *tabdeco |= TD_TRILL; break; case 'B': *tabdeco &= ~TD_TYPEMASK; *tabdeco |= (TD_FULL | TD_UP | TD_BEND); break; case 'b': *tabdeco &= ~TD_TYPEMASK; *tabdeco |= (TD_UP | TD_BEND); *tabdeco &= ~TD_FULL; break; case 'R': *tabdeco &= ~TD_TYPEMASK; *tabdeco |= (TD_FULL | TD_BEND); *tabdeco &= ~TD_UP; break; case 'r': *tabdeco &= ~TD_TYPEMASK; *tabdeco |= TD_BEND; *tabdeco &= ~(TD_FULL | TD_UP); break; case '>': case '<': *tabdeco &= ~TD_TYPEMASK; sp = *p; p++; val = strtol(p, &p, 10); p--; if (val >= 0) { if (sp == '>') { *tabdeco |= TD_UP; } else { *tabdeco &= ~TD_UP; } } else { if (sp == '<') { *tabdeco |= TD_UP; } else { *tabdeco &= ~TD_UP; } val = 0 - val; } if (val > 0x001F) val = 1; *tabdeco |= (val << 4); if (sp == '>') { *tabdeco |= TD_SLIDETO; } else { *tabdeco |= TD_SLIDEFROM; } break; default: /* Must be a string number */ val = strtol(p, &p, 10); p--; if (val > 8) val = 0; *tabdeco |= val; } break; case '!': /* ABC 1.6 std JSA 4/21/00 */ deco = IdentifyDeco(&p); break; case '.': /* ABC 1.6 std JSA 5/00 */ deco = D_STACC; break; default: /* [JSA] 05/00 Handle reassignable decoration symbols */ i = -1; if (*p == '~') { i = 0; } else { if ((*p >= 'H') && (*p <= 'Z')) { i = *p - 'G'; } else { if ((*p >= 'h') && (*p <= 'w')) i = (*p - 'h') + 20; } } if (i >= 0) { deco = info.DecoTbl[i]; } else { Done = TRUE; } } if (deco) { dtype[*n]=deco; (*n)++; } if (!Done) p++; } } /* ----- parse_length: parse length specifer for note or rest --- */ int parse_length(void) { int len,fac; len=dlen; /* start with default length */ if (isdigit(*p)) { /* multiply note length */ if ((fac = parse_uint()) != 0) len *= fac; } while (*p=='/') { /* divide note length */ p++; if (isdigit(*p)) fac=parse_uint(); else fac=2; if (len%fac) { syntax("Bad length divisor", p-1); return len; } len /= fac; } return len; } /* ----- parse_grace_sequence --------- */ static int parse_grace_sequence(int pgr[], int agr[]) { char *p0; int n; int pit, len, acc; p0=p; if (*p != '{') return 0; p++; n=0; while (*p != '}') { if (*p == '\0') { syntax("Unbalanced grace note sequence", p0); return 0; } if (!isnote(*p)) { syntax("Unexpected symbol in grace note sequence", p); p++; } parse_basic_note(&pit, &len, &acc); agr[n] = acc; pgr[n] = pit; /* ignore length */ n++; } p++; return n; } /* ----- identify_note: set head type, dots, flags for note --- */ void identify_note(struct SYMBOL *s, char *q) { int head,base,len,flags,dots; if (s->len==0) s->len=s->lens[0]; len=s->len; head=H_FULL; flags=0; if (len>=WHOLE) base=WHOLE; else if (len>=HALF) base=HALF; else if (len>=QUARTER) base=QUARTER; else if (len>=EIGHTH) base=EIGHTH; else if (len>=SIXTEENTH) base=SIXTEENTH; else if (len>=THIRTYSECOND) base=THIRTYSECOND; else if (len>=SIXTYFOURTH) base=SIXTYFOURTH; else syntax("Cannot identify head for note",q); switch (base) { case WHOLE: head=H_OVAL; break; case HALF: head=H_EMPTY; break; case SIXTYFOURTH: flags=4; break; case THIRTYSECOND: flags=3; break; case SIXTEENTH: flags=2; break; case EIGHTH: flags=1; break; } dots=0; if (len==base) dots=0; else if (2*len==3*base) dots=1; else if (4*len==7*base) dots=2; else if (8*len==15*base) dots=3; else syntax("Cannot handle note length for note",q); /* printf ("identify_note: length %d gives head %d, dots %d, flags %d\n", len,head,dots,flags); */ s->head=head; s->dots=dots; s->flags=flags; } /* ----- double_note: change note length for > or < char --- */ /* Note: if sym[i] is a chord, the length shifted to the following note is taken from the first note head. Problem: the crazy syntax permits different lengths within a chord. */ static void double_note(int i, int num, int sign, char *q) { int m,shift,j,len; if ((sym[i].type!=NOTE) && (sym[i].type!=REST)) bug("sym is not NOTE or REST in double_note", 1); shift=0; len=sym[i].lens[0]; for (j=0;j3) printf(" parsed basic note," "length %d/%d = 1/%d, pitch %d\n", len,BASE,BASE/len,pit); return 1; } /* ----- parse_note: parse for one note or rest with all trimmings --- */ static int parse_note() { int k,deco,i,chord,m,type,sl1,sl2,j,adj_sharps_flats; int pitch,length,accidental,invis, adj_pitch; int ngr,pgr[30],agr[30],dtype[10]; char *q,*q0; unsigned short TabDeco; deco = 0; parse_gchord(&deco, dtype); /* guitar chord */ if (*p == ' ') p++; /* (for compatibility) */ ngr=parse_grace_sequence(pgr,agr); /* grace notes */ parse_gchord(&deco, dtype); /* permit chord after graces */ parse_deco(&deco, dtype, &TabDeco); /* decorations */ parse_gchord(&deco, dtype); /* permit chord after deco */ chord=0; /* determine if chord */ q=p; if ((*p=='+') || (*p=='[')) { chord=1; p++; } type=invis=0; switch (*p) { case 'x': case 'y': invis = 1; case 'z': type = REST; break; default: if (isnote(*p) || (chord && ((*p=='(') || (*p == ')') || (*p == '@')))) { /* this just for better error msg */ type=NOTE; } else { return 0; } break; } k=add_sym(type); /* add new symbol to list */ sym[k].dc.n=deco; /* copy over pre-parsed stuff */ sym[k].TabDeco[0] = TabDeco; for (i=0;i0) { sym[k].text = getarena(strlen(gch)); strcpy(sym[k].text, gch); gch[0] = '\0'; } q0=p; if (type==REST) { p++; sym[k].lens[0] = parse_length(); sym[k].npitch=1; sym[k].invis=invis; if (db>3) printf(" parsed rest, length %d/%d = 1/%d\n", sym[k].lens[0],BASE,BASE/sym[k].lens[0]); } else { m=0; /* get pitch and length */ sl1=sl2=0; for (;;) { if (chord && (*p=='(')) { sl1++; sym[k].sl1[m]=sl1; p++; } deco = 0; parse_deco(&deco, dtype, &TabDeco); /* for extra decorations within chord */ for (i=0;i 14) { adj_sharps_flats = 14; wng("More than seven sharps.", ""); } sym[k].abspitch[m] += key_convert[adj_sharps_flats][(pitch - 2) % 7]; /* Adjust absolute pitch for accidentals. */ switch (accidental) { case A_DS: sym[k].abspitch[m]++; case A_SH: sym[k].abspitch[m]++; break; case A_DF: sym[k].abspitch[m]--; case A_FT: sym[k].abspitch[m]--; break; case A_NT: /* Determine from key (sharps_flats) whether a natural */ /* increases, decreases, or doesn't change the pitch. */ if (key_convert[adj_sharps_flats][(pitch - 2) % 7] == -1 ) { /* The keysig flats this note, increase the pitch. */ sym[k].abspitch[m]++; } else if (key_convert[adj_sharps_flats][(pitch - 2) % 7] == 1) { /* The keysig sharps this note, decrease the pitch.*/ sym[k].abspitch[m]--; } break; default: k = k; } /* End tablature modification, 10/99, J. S. Atchley */ /****************************************************************/ sym[k].ti1[m] = sym[k].ti2[m] = 0; for (j=0;j= 0; ) { /* add carryover from previous > or < */ if (sym[k].lens[m]+carryover<=0) syntax("> leads to zero or negative note length",q0); else sym[k].lens[m] += carryover; } carryover=0; if (db>3) printf(" parsed note, decos %d, text <%s>\n", sym[k].dc.n, sym[k].text); identify_note(&sym[k],q0); return type; } /* ----- parse_sym: parse a symbol and return its type -------- */ static int parse_sym() { int i; if (parse_bar()) return BAR; if (parse_space()) return SPACE; if (parse_nl()) return NEWLINE; if ((i=parse_esc())) return i; if ((i=parse_note())) return i; return 0; } /* ----- add_wd ----- */ static char *add_wd(char str[]) { char *rp; int l; if ((l = strlen(str)) == 0) return 0; rp = getarena(l); strcpy(rp, str); return rp; } /* ----- parse_vocals: parse words below a line of music ----- */ /* Use '^' to mark a '-' between syllables - hope nobody needs '^' ! */ static int parse_vocals(char *line) { int isym; char *c,*c1,*w; char word[81]; if ((line[0]!='w') || (line[1]!=':')) return 0; p0=line; isym = nsym0-1; c=line+2; for (;;) { while (isspace(*c)) c++; if (*c=='\0') break; c1=c; if ((*c=='_') || (*c=='*') || (*c=='|') || (*c=='-')) { word[0]=*c; if (*c=='-') word[0]='^'; word[1]='\0'; c++; } else { w=word; *w='\0'; while (*c!=' ' && *c!='\t' && *c!='\0') { if (*c=='_' || *c=='*' || *c=='|') break; if (*c=='-') { if (*(c-1) != '\\') break; w--; *w='-'; } *w=*c; w++; c++; } if (*c=='-') { *w='^' ; w++; c++; } *w='\0'; } /* now word contains a word, possibly with trailing '^', or one of the special characters * | _ - */ if (!strcmp(word,"|")) { /* skip forward to next bar */ isym++; while (sym[isym].type!=BAR && isym=nsym) { syntax("Not enough bar lines for |",c1); break; } } else { /* store word in next note */ w=word; while (*w!='\0') { /* replace * and ~ by space */ if ((*w=='*') || (*w=='~')) *w=' '; w++; } isym++; while (sym[isym].type != NOTE && isym < nsym) isym++; if (isym>=nsym) { syntax("Not enough notes for words",c1); break; } sym[isym].wordp[nwline]=add_wd(word); } if (*c=='\0') break; } nwline++; isym = voice_tb[current_voice].staff; if (staff_tb[isym].nvocal < nwline) staff_tb[isym].nvocal = nwline; return 1; } /* ----- parse_music_line: parse a music line into symbols ----- */ static int parse_music_line(char line[]) { int type,num,nbr; char msg[81]; char *p1,*pmx; static char qtb[10] = {1, 1, 3, 2, 3, 0, 2, 0, 3, 0}; if (nsym == 0) init_music_line(current_voice); nwline=0; nsym0=nsym; nbr=0; p=p0=line; pmx=p+strlen(p); while (*p != 0) { if (p>pmx) break; /* emergency exit */ type = parse_sym(); if ((db>4) && type) printf(" sym[%d] code (%d,%d)\n", nsym-1,sym[nsym-1].type,sym[nsym-1].u); switch (type) { case NEWLINE: if ((nsym>0) && !cfmt.continueall && !cfmt.barsperstaff) { if (current_voice == 0) sym[nsym-1].eoln=1; if (word) { sym[last_note].word_end=1; word=0; } } break; case ESCSEQ: handle_escape_sequence(); break; case REST: if (pplet) { /* n-plet can start on rest */ sym[nsym-1].p_plet=pplet; sym[nsym-1].q_plet=qplet; sym[nsym-1].r_plet=rplet; pplet=0; } last_note=nsym-1; /* need this so > and < work */ p1=p; break; case NOTE: if (!word) { sym[nsym-1].word_st=1; word=1; } sym[nsym-1].slur_st+=nbr; nbr=0; if (end_slur_next_note) sym[nsym-1].slur_end++; end_slur_next_note=0; if (pplet) { /* start of n-plet */ sym[nsym-1].p_plet=pplet; sym[nsym-1].q_plet=qplet; sym[nsym-1].r_plet=rplet; pplet=0; } last_note=last_real_note=nsym-1; if (PendingTies[sym[last_note].pits[0]]) { PendingTies[sym[last_note].pits[0]] = FALSE; sym[last_note].EndTie = TRUE; } p1=p; break; case BAR: case SPACE: if (word) { if (last_real_note>0) sym[last_real_note].word_end=1; word=0; } break; case 0: /* no type */ switch (*p) { case '-': /* a-b tie */ sym[last_note].slur_st++; PendingTies[sym[last_note].pits[0]] = TRUE; end_slur_next_note=1; p++; break; case '(': p++; if (isdigit(*p)) { pplet = *p-'0'; if ((qplet = qtb[pplet]) == 0) qplet = meter1 % 3 == 0 ? 3 : 2; rplet = pplet; p++; if (*p == ':') { p++; if (isdigit(*p)) { qplet=*p-'0'; p++; } if (*p == ':') { p++; if (isdigit(*p)) { rplet=*p-'0'; p++; } } } } else nbr++; break; case ')': if (last_note>0) sym[last_note].slur_end++; else syntax("Unexpected symbol",p); p++; break; case '>': num=1; p++; while (*p == '>') { num++; p++; } if (last_note<0) syntax("No note before > sign", p); else double_note(last_note, num, 1, p1); break; case '<': num=1; p++; while (*p == '<') { num++; p++; } if (last_note<0) syntax("No note before < sign", p); else double_note(last_note, num, -1, p1); break; case '*': /* ignore stars for now */ case '!': /* ditto for '!' */ p++; break; default: if (*p != '\0') sprintf(msg, "Unreexpected symbol \'%c\'", *p); else sprintf(msg, "Unexpected end of line"); syntax(msg, p); p++; break; } } } /* maybe set end-of-line marker, if symbols were added */ if (nsym>nsym0) { if (type == CONTINUE || cfmt.barsperstaff || cfmt.continueall) sym[nsym-1].eoln = 0; else sym[nsym-1].eoln = 1; } /* break words at end of line */ if (word && sym[nsym-1].eoln) { sym[last_note].word_end=1; word=0; } return TO_BE_CONTINUED; } /* ----- is_selected: check selection for current info fields ---- */ static int is_selected(char xref_str[], int npat, char pat[][STRL1], int select_all, int search_field) { int i,a,b,m; if (verbose >= 99) printf("is_selected(%s, %d, s[][], %d, %d)\n", xref_str, npat, select_all, search_field); /* true if select_all or if no selectors given */ if (select_all) return 1; if (isblankstr(xref_str) && npat == 0) return 1; for (i=0;i=2)) m=match(info.title2,pat[i]); if (!m && (numtitle>=3)) m=match(info.title3,pat[i]); } if (m) return 1; } /* check xref against string of numbers */ p = xref_str; while (*p != 0) { parse_space(); a=parse_uint(); if (!a) return 0; /* can happen if invalid chars in string */ parse_space(); if (*p == '-') { p++; parse_space(); b=parse_uint(); if (!b) { if (xrefnum>=a) return 1; } else for (i=a;i<=b;i++) if (xrefnum==i) return 1; } else { if (xrefnum==a) return 1; } if (*p == ',') p++; } return 0; } /* ----- rehash_selectors: split selectors into patterns and xrefs -- */ int rehash_selectors(char sel_str[], char xref_str[], char pat[][STRL1]) { char *q; char arg[501]; int i,npat; npat=0; xref_str[0] = '\0'; q=sel_str; i=0; for (;;) { if (*q==' ' || *q=='\t' || *q=='\0') { arg[i]='\0'; i=0; if (!isblankstr(arg)) { if (arg[0]=='-') /* skip any flags */ ; else if (is_xrefstr(arg)) { strcat(xref_str, arg); strcat(xref_str, " "); } else { /* pattern with * or + */ if ((strchr(arg,'*')) || (strchr(arg,'+'))) { strcpy(pat[npat],arg); } else { /* simple pattern */ strcpy(pat[npat],"*"); strcat(pat[npat],arg); strcat(pat[npat],"*"); } npat++; } } } else { arg[i]=*q; i++; } if (*q=='\0') break; q++; } return npat; } /* ----- decomment_line: cut off after % ----- */ static void decomment_line(char *p) { int i; for (i = strlen(p); --i >= 0; ) if (*p++ == '%') { p[-1] = '\0'; break; } } /* ----- get_line: read line, do first operations on it ----- */ static int get_line(FILE *fp, char ln[]) { int l; ln[0] = '\0'; if (feof(fp)) return 0; fgets(ln, BSIZE, fp); linenum++; l=strlen(ln); if (l>STRL) { if (verbose<=2) printf("\n"); printf("++++ Line %d too long, truncate from %d to %d chars\n", linenum, l, STRL); l=STRL-1; ln[l]='\0'; } if (is_end_line(ln)) return 0; if (ln[l-1]=='\n') ln[l-1]='\0'; if (verbose >= 5 || vb >= 10) printf("%3d %s\n", linenum, ln); return 1; } /* ----- read_line: returns type of line scanned --- */ static int read_line(FILE *fp, int do_music, char line[BSIZE]) { int type,s0; if (!get_line(fp, line)) return E_O_F; if (isblankstr(line)) return BLANK; if (is_pseudocomment(line)) return PSCOMMENT; if (is_comment(line)) return COMMENT; decomment_line(line); if ((type = info_field(line)) != 0) { /* skip after history field. Nightmarish syntax, that. */ if (type != HISTORY) { if (type == XREF) current_voice = 0; return type; } for (;;) { if (!get_line(fp,line)) return E_O_F; if (isblankstr(line)) return BLANK; if (is_info_field(line)) break; add_text(line, TEXT_H); } return info_field(line); } if (!do_music) return COMMENT; if (parse_vocals(line)) return MWORDS; s0 = nsym; type = parse_music_line(line); if (db > 1) printf(" parsed music symbols %d to %d\n", s0, nsym); return type; } /* ----- do_index: print index of abc file ------ */ void do_index(FILE *fp, char xref_str[], int npat, char pat[][STRL1], int select_all, int search_field) { int type,within_tune; char line[BSIZE]; linenum=0; verbose=vb; numtitle=0; write_history=0; within_tune=within_block=do_this_tune=0; reset_info(&default_info); info=default_info; for (;;) { if (!get_line(fp, line)) break; if (is_comment(line)) continue; decomment_line(line); type=info_field(line); switch (type) { case XREF: if (within_block) printf("++++ Tune %d not closed properly \n", xrefnum); clrarena(); numtitle=0; within_tune=0; within_block=1; ntext=0; break; case KEY: if (!within_block) break; if (!within_tune) { tnum2++; if (is_selected(xref_str,npat,pat,select_all,search_field)) { get_sharps_flats(info.key, &sharps_flats); set_meter(info.meter); set_dlen(info.len); printf(" %-4d %-5s %-4s", xrefnum, info.key, info.meter); if (search_field==S_SOURCE) printf(" %-15s", info.src); else if (search_field==S_RHYTHM) printf(" %-8s", info.rhyth); else if (search_field==S_COMPOSER) printf(" %-15s", info.comp[0]); if (numtitle >= 1) { printf(" %s", info.title); if (numtitle >= 2) { printf(" - %s", info.title2); if (numtitle >= 3) printf(" - %s", info.title3); } } printf("\n"); tnum1++; } within_tune=1; } break; } if (isblankstr(line)) { if (within_block && !within_tune) printf("++++ Header not closed in tune %d\n", xrefnum); within_tune=0; within_block=0; info=default_info; } } if (within_block && !within_tune) printf("++++ Header not closed in for tune %d\n", xrefnum); } /* ----- reset_music_line: init pars for parsing music line ------ */ static void reset_music_line(void) { /* nsym=0 tells parser to initialize line with clef and key */ nsym = 0; word = 0; carryover = 0; last_note = last_real_note = -1; pplet = qplet = rplet = 0; gch[0] = '\0'; insert_meter = 1; nsym0 = 0; reset_gen(); } /* ----- check_selected ----- */ static void check_selected(FILE *fp, char xref_str[], int npat, char pat[][STRL1], int sel_all, int search_field) { if (!do_this_tune) { if (is_selected(xref_str,npat,pat,sel_all,search_field)) { do_this_tune=1; verbose=vb; clear_buffer(); /* set to 0 to permit staff breaks in a tune */ use_buffer = !cfmt.ManualPages; /* let user control this [JSA] */ if (epsf) use_buffer = 1; write_tunetop(); } } } /* ----- process_file ----- */ void process_file(FILE *fpin, FILE *fpout, char xref_str[], int npat, char pat[][STRL1], int sel_all, int search_field) { char line[BSIZE]; int type; static char *type_txt[] = { "", "COMMENT", "MUSIC", "TO_BE_CONTINUED", "E_O_F", "INFO", "TITLE", "METER", "PARTS", "KEY", "XREF", "DLEN", "HISTORY", "BLANK", "WORDS", "MWORDS", "PSCOMMENT", "TEMPO", "VOICE_D" }; int i, v; tFIFO *Fifo, *HdFifo, *ChdFifo; for (i = 0; i < MAXPITCHVAL; i++) PendingTies[i] = FALSE; within_tune=within_block=do_this_tune=0; linenum=0; numtitle=0; reset_info(&default_info); info=default_info; verbose=0; if (vb>=20) db=3; else if (vb>=25) db=5; for (;;) { type = read_line(fpin, do_this_tune, line); if ((vb>15) || ((verbose>10)&&within_block)) { printf("process_line, type %d %s\n", type, (unsigned) type >= sizeof type_txt / sizeof type_txt[0] ? "UNKNOWN" : type_txt[type]); } switch (type) { case PSCOMMENT: process_pscomment(fpin, fpout, line); break; case XREF: /* start of new block */ if (!epsf) write_buffer(fpout); /* flush stuff left from %% lines */ if (within_block) printf("\n++++ Last tune not closed properly\n"); /* Clear the "^text" FIFO objects. */ for (v = 0; v < MAXVOICE; v++) { Fifo = &voice_tb[v].TextDecs; HdFifo = &voice_tb[v].TextHdDecs; ChdFifo = &voice_tb[v].Chords; for (i = 0; i < MAXSTRDECO; i++) { if (Fifo->strings[i]) free(Fifo->strings[i]); if (HdFifo->strings[i]) free(HdFifo->strings[i]); if (ChdFifo->strings[i]) free(HdFifo->strings[i]); Fifo->strings[i] = HdFifo->strings[i] = ChdFifo ->strings[i] = NULL; } Fifo->inidx = Fifo->outidx = 0; HdFifo->inidx = HdFifo->outidx = 0; ChdFifo->inidx = ChdFifo->outidx = 0; } clrarena(); get_default_info(); within_block = 1; within_tune = 0; do_this_tune = 0; numtitle=0; ntext=0; init_pdims(); cfmt=dfmt; NoMeter = FALSE; break; case TITLE: case TEMPO: if (!within_block) break; if (within_tune) { /* title or tempo within tune */ if (do_this_tune) { output_music(fpout); if (type == TITLE) write_inside_title(); else write_inside_tempo(); reset_music_line(); } } else check_selected(fpout, xref_str, npat, pat, sel_all, search_field); break; case KEY: if (!within_block) break; if (within_tune) { /* key change within tune */ if (!do_this_tune) break; if (nsym>0) append_key_change(); else get_sharps_flats(info.key, &sharps_flats); } else { /* end of header.. start now */ check_selected(fpout, xref_str, npat, pat, sel_all, search_field); if (do_this_tune) { tunenum++; if (verbose>=3) printf("---- start %d (%s) ----\n", xrefnum, info.title); fflush(stdout); get_sharps_flats(info.key, &sharps_flats); set_meter(info.meter); set_dlen(info.len); check_margin(cfmt.leftmargin); write_heading(); init_parse_params(); reset_music_line(); voice_init(); } within_tune = 1; } break; case METER: if (!within_block) break; if (do_this_tune && within_tune) { if (nsym > 0 && !sym[nsym-1].eoln) append_meter_change(); else { int voice_s, i; voice_s = current_voice; set_meter(info.meter); for (i = 0; i <= nvoice; i++) { voice_sw(i); if (nsym == 0) init_music_line(i); append_meter(); } voice_sw(voice_s); } } break; case DLEN: if (!within_block) break; if (do_this_tune && within_tune) { set_dlen(info.len); if (nsym == 0 || sym[nsym-1].eoln || sym[nsym-2].eoln) { /* (may have a keysig) */ /*fixme: have some other test for setting all the voices*/ int i; for (i = MAXVOICE; --i >= 0; ) voice_tb[i].dlen = dlen; } } break; case PARTS: if (!within_block) break; if (do_this_tune && within_tune) { output_music(fpout); reset_music_line(); write_parts(); } break; case BLANK: /* end of block or file */ case E_O_F: if (do_this_tune) { output_music(fpout); put_words(fpout); if (cfmt.ShowSource) PutSource(fpout); if (cfmt.writehistory) put_history(fpout); if (epsf) { FILE *feps; char fnm[81], finf[MAXINF]; close_output_file(); if (choose_outname) { epsf_title(info.title, fnm); strcat(fnm,".eps"); } else { nepsf++; sprintf(fnm, "%s%03d.eps", outf, nepsf); } sprintf(finf, "%s (%d)", in_file[0], xrefnum); if ((feps = fopen(fnm,"w")) == NULL) rx("Cannot open output file ", fnm); init_ps(feps, finf, 1, cfmt.leftmargin-5, posy+bposy-5, cfmt.leftmargin+cfmt.staffwidth+5, cfmt.pageheight-cfmt.topmargin); init_epsf(feps); write_buffer(feps); printf("\n[%s] %s", fnm, info.title); close_epsf(feps); fclose(feps); in_page=0; init_pdims(); } else { buffer_eob(fpout); write_buffer(fpout); if ((verbose==0) && (tunenum%10==0)) printf("."); if (verbose==2) printf("%s - ", info.title); } verbose=0; } info = default_info; if (within_block && !within_tune) printf("\n++++ Header not closed in tune %d\n", xrefnum); within_tune = within_block = do_this_tune = 0; break; } if (type == E_O_F) break; } if (!epsf) { buffer_eob(fpout); write_buffer(fpout); } }