/* store.c * This file handles all event_X() calls from parseabc.c and stores them * in various arrays and data structures. The midi is then generated by * calling mfwrite() which in turn uses the routine writetrack(). * This file is part of * * abc2midi version 1.7.2 * * 14th January 1999 * James Allwright * * Macintosh Port 30th July 1996 * Wil Macaulay (wil@syndesis.com) */ #include "abc.h" #include "midifile.h" #include #ifdef __MWERKS__ #define __MACINTOSH__ 1 #endif /* __MWERKS__ */ #ifdef __MACINTOSH__ #include int setOutFileCreator(char *fileName,unsigned long theType, unsigned long theCreator); #define STRCHR #endif /* __MACINTOSH__ */ /* define STRCHR if your C libraries have strchr() instead of index() */ #ifdef STRCHR #define index(a,b) (int)strchr((a),(b)) #endif char* index(); #define MAXLINE 500 #define INITTEXTS 20 #define INITWORDS 20 #define MAXCHANS 16 /* global variables grouped roughly by function */ FILE *fp; extern int lineno; int lastlineno; /* parsing stage */ int tuplecount, tfact_num, tfact_denom, tnote_num, tnote_denom; int specialtuple; int gracenotes; int headerpartlabel; int dotune, pastheader; int hornpipe, last_num, last_denom; int timesigset; int retain_accidentals; struct voicecontext { /* maps of accidentals for each stave line */ char basemap[7], workmap[7]; int basemul[7], workmul[7]; int default_length; int voiceno; int hasgchords; int haswords; int inslur; int ingrace; /* chord handling */ int inchord, chordcount; int chord_num, chord_denom; /* details of last 2 notes/chords to apply length-modifiers to */ int laststart, lastend, thisstart, thisend; /* broken rythm handling */ int brokentype, brokenmult, brokenpending; struct voicecontext* next; }; struct voicecontext global; struct voicecontext* v; struct voicecontext* head; int voicecount; /* storage structure for strings */ int maxtexts = INITTEXTS; char** atext; int ntexts = 0; /* Named guitar chords */ char chordname[MAXCHORDNAMES][8]; int chordnotes[MAXCHORDNAMES][6]; int chordlen[MAXCHORDNAMES]; int chordsnamed = 0; /* general purpose storage structure */ int maxnotes; int *pitch, *num, *denom; featuretype *feature; int notes; int verbose = 0; int xmatch; int sf, mi; int gchordvoice, wordvoice; /* Part handling */ extern char part[MAXPARTS]; extern int parts, partno, partlabel; extern int part_start[26], part_count[26]; int voicesused; /* Tempo handling (Q: field) */ int time_num, time_denom; long tempo; int tempo_num, tempo_denom; int relative_tempo, Qtempo; extern int division; extern int div_factor; /* output file generation */ int userfilename = 0; char outname[40], outbase[40]; int check; int ntracks; /* bar length checking */ extern int bar_num, bar_denom; int barchecking; /* generating MIDI output */ int middle_c; int channels[MAXCHANS + 3]; extern int global_transpose; int gfact_num, gfact_denom; /* karaoke handling */ int karaoke, wcount; char** words; int maxwords = INITWORDS; int getarg(option, argc, argv) /* look for argument 'option' in command line */ char *option; char *argv[]; int argc; { int j, place; place = -1; for (j=0; jvoiceno = n; s->default_length = global.default_length; s->hasgchords = 0; s->haswords = 0; s->inslur = 0; s->ingrace = 0; s->inchord = 0; s->chordcount = 0; s->laststart = -1; s->lastend = -1; s->thisstart = -1; s->thisend = -1; s->brokenpending = -1; s->next = NULL; for (i=0; i<7; i++) { s->basemap[i] = global.basemap[i]; s->basemul[i] = global.basemul[i]; s->workmap[i] = global.workmap[i]; s->workmul[i] = global.workmul[i]; }; return(s); } struct voicecontext* getvoicecontext(n) /* find the data structure for a given voice number */ int n; { struct voicecontext *p; struct voicecontext *q; p = head; q = NULL; while ((p != NULL) && (p->voiceno != n)) { q = p; p = p->next; }; if (p == NULL) { p = newvoice(n); if (q != NULL) { q->next = p; }; }; if (head == NULL) { head = p; }; return(p); } clearvoicecontexts() /* free up all the memory allocated to voices */ { struct voicecontext *p; struct voicecontext *q; p = head; while (p != NULL) { q = p->next; free(p); p = q; }; head = NULL; } event_init(argc, argv, filename) /* this routine is called first by parseabc.c */ int argc; char* argv[]; char **filename; { int j; /* look for code checking option */ if (getarg("-c", argc, argv) != -1) { check = 1; } else { check = 0; }; /* look for verbose option */ if (getarg("-v", argc, argv) != -1) { verbose = 1; } else { verbose = 0; }; maxnotes = 500; /* allocate space for notes */ pitch = checkmalloc(maxnotes*sizeof(int)); num = checkmalloc(maxnotes*sizeof(int)); denom = checkmalloc(maxnotes*sizeof(int)); feature = (featuretype*) checkmalloc(maxnotes*sizeof(featuretype)); /* and for text */ atext = (char**) checkmalloc(maxtexts*sizeof(char*)); words = (char**) checkmalloc(maxwords*sizeof(char*)); if ((getarg("-h", argc, argv) != -1) || (argc < 2)) { printf("abc2midi version 1.7.2\n"); printf("Usage : abc2midi [reference number] [-c] [-v] "); printf("[-o filename]\n"); printf(" [reference number] selects a tune\n"); printf(" -c selects checking only\n"); printf(" -v selects verbose option\n"); printf(" -o filename\n"); printf(" The default action is to write a MIDI file for each abc tune\n"); printf(" with the filename N.mid, where is the filestem\n"); printf(" of the abc file and N is the tune reference number. If the -o\n"); printf(" option is used, only one file is written. This is the tune\n"); printf(" specified by the reference number or, if no reference number\n"); printf(" is given, the first tune in the file.\n"); exit(0); } else { xmatch = 0; if ((argc >= 3) && (isdigit(*argv[2]))) { xmatch = readnumf(argv[2]); }; *filename = argv[1]; strcpy(outbase, argv[1]); for (j = 0; j= j+1) { strcpy(outname, argv[j]); userfilename = 1; if (xmatch == 0) { xmatch = -1; }; } else { event_error("No filename given, ignoring -o option"); }; }; dotune = 0; parseroff(); /* set up named guitar chords */ {int list[3] = {0, 4, 7}; addchordname("", 3, list); }; {int list[3] = {0, 3, 7}; addchordname("m", 3, list); }; {int list[4] = {0, 4, 7, 10}; addchordname("7", 4, list); }; {int list[4] = {0, 3, 7, 10}; addchordname("m7", 4, list); }; {int list[4] = {0, 4, 7, 11}; addchordname("maj7", 4, list); }; {int list[4] = {0, 4, 7, 11}; addchordname("M7", 4, list); }; {int list[4] = {0, 4, 7, 9}; addchordname("6", 4, list); }; {int list[4] = {0, 3, 7, 9}; addchordname("m6", 4, list); }; {int list[3] = {0, 4, 8}; addchordname("aug", 3, list); }; {int list[3] = {0, 4, 8}; addchordname("+", 3, list); }; {int list[4] = {0, 4, 8, 10}; addchordname("aug7", 4, list); }; {int list[3] = {0, 3, 6}; addchordname("dim", 3, list); }; {int list[4] = {0, 3, 6, 9}; addchordname("dim7", 4, list); }; {int list[5] = {0, 4, 7, 10, 2}; addchordname("9", 5, list); }; {int list[5] = {0, 3, 7, 10, 2}; addchordname("m9", 5, list); }; {int list[5] = {0, 4, 7, 11, 2}; addchordname("maj9", 5, list); }; {int list[5] = {0, 4, 7, 11, 2}; addchordname("M9", 5, list); }; {int list[6] = {0, 4, 7, 10, 2, 5}; addchordname("11", 6, list); }; {int list[5] = {0, 4, 7, 10, 13}; addchordname("dim9", 5, list); }; {int list[3] = {0, 5, 7}; addchordname("sus", 3, list); }; {int list[3] = {0, 2, 7}; addchordname("sus9", 3, list); }; {int list[4] = {0, 5, 7, 10}; addchordname("7sus4", 4, list); }; {int list[4] = {0, 2, 7, 10}; addchordname("7sus9", 4, list); }; {int list[2] = {0, 7}; addchordname("5", 2, list); }; } event_text(s) /* text found in abc file */ char *s; { char msg[200]; sprintf(msg, "Ignoring text: %s", s); event_warning(msg); } event_reserved(p) /* reserved character H-Z found in abc file */ char p; { char msg[200]; sprintf(msg, "Ignoring reserved character %c", p); event_warning(msg); } event_tex(s) /* TeX command found - ignore it */ char *s; { } event_linebreak() /* reached end of line in abc */ { addfeature(LINENUM, lineno, 0, 0); } event_startmusicline() /* starting to parse line of abc music */ { addfeature(MUSICLINE, 0, 0, 0); } event_endmusicline(endchar) /* finished parsing line of abc music */ char endchar; { addfeature(MUSICSTOP, 0, 0, 0); } event_eof() /* end of abc file encountered */ { if (dotune) { dotune = 0; parseroff(); finishfile(); }; if (verbose) { printf("End of File reached\n"); }; free(pitch); free(num); free(denom); free(feature); free(words); } event_fatal_error(s) /* print error message and halt */ char *s; { event_error(s); exit(1); } event_error(s) /* generic error handler */ char *s; { #ifdef NOFTELL extern int nullpass; if (nullpass != 1) { printf("Error in line %d : %s\n", lineno, s); }; #else printf("Error in line %d : %s\n", lineno, s); #endif } event_warning(s) /* generic warning handler - for flagging possible errors */ char *s; { #ifdef NOFTELL extern int nullpass; if (nullpass != 1) { printf("Warning in line %d : %s\n", lineno, s); }; #else printf("Warning in line %d : %s\n", lineno, s); #endif } textfeature(type, s) /* called while parsing abc - stores an item which requires an */ /* associared string */ int type; char* s; { atext[ntexts] = addstring(s); addfeature(type, ntexts, 0, 0); ntexts = ntexts + 1; if (ntexts >= maxtexts) { maxtexts = textextend(maxtexts, &atext); }; } event_comment(s) /* comment found in abc */ char *s; { if (dotune) { if (pastheader) { textfeature(TEXT, s); } else { textfeature(TEXT, s); }; }; } event_specific(package, s) /* package-specific command found i.e. %%NAME */ /* only %%MIDI commands are actually handled */ char *package, *s; { char msg[200], command[40]; char *p; if (strcmp(package, "MIDI") == 0) { int ch; int done; int trans, rtrans; p = s; done = 0; skipspace(&p); readstr(command, &p); if (strcmp(command, "channel") == 0) { skipspace(&p); ch = readnump(&p) - 1; addfeature(CHANNEL, ch, 0, 0); done = 1; }; trans = strcmp(command, "transpose"); rtrans = strcmp(command, "rtranspose"); if ((trans == 0) || (rtrans == 0)) { int neg, val; skipspace(&p); neg = 0; if (*p == '+') p = p + 1; if (*p == '-') { p = p + 1; neg = 1; }; skipspace(&p); val = readnump(&p); if (neg) val = - val; if (pastheader) { if (trans == 0) { addfeature(TRANSPOSE, val, 0, 0); } else { addfeature(RTRANSPOSE, val, 0, 0); }; } else { if (trans == 0) { global_transpose = val; } else { global_transpose = global_transpose + val; }; }; done = 1; }; if (strcmp(command, "C") == 0) { int val; skipspace(&p); val = readnump(&p); middle_c = val; done = 1; }; if (strcmp(command, "nobarlines") == 0) { retain_accidentals = 0; done = 1; }; if (strcmp(command, "barlines") == 0) { retain_accidentals = 1; done = 1; }; if (strcmp(command, "grace") == 0) { int a, b; char msg[40]; skipspace(&p); a = readnump(&p); if (*p != '/') { event_error("Need / in MIDI grace command"); } else { p = p + 1; }; b = readnump(&p); if ((a < 1) || (b < 1) || (a >= b)) { sprintf(msg, "%d/%d is not a suitable fraction", a, b); event_error(msg); } else { if (pastheader) { addfeature(SETGRACE, a, 0, b); } else { gfact_num = a; gfact_denom = b; }; }; done = 1; }; if (strcmp(command, "gchordon") == 0) { addfeature(GCHORDON, 0, 0, 0); done = 1; }; if (strcmp(command, "gchordoff") == 0) { addfeature(GCHORDOFF, 0, 0, 0); done = 1; }; if (strcmp(command, "chordname") == 0) { char name[20]; int i, notes[6]; skipspace(&p); i = 0; while ((i<19) && (*p != ' ') && (*p != '\0')) { name[i] = *p; p = p + 1; i = i + 1; }; name[i] = '\0'; if (*p != ' ') { event_error("Bad format for chordname command"); } else { i = 0; while ((i<=6) && (*p == ' ')) { skipspace(&p); notes[i] = readnump(&p); i = i + 1; }; addchordname(name, i, notes); }; done = 1; }; if (done == 0) { /* add as a command to be interpreted later */ textfeature(DYNAMIC, s); }; } else { strcpy(msg, "%"); strcpy(msg+strlen(msg), package); strcpy(msg+strlen(msg),s); event_comment(msg); }; } event_startinline() /* start of in-line field in abc music line */ { } event_closeinline() /* end of in-line field in abc music line */ { } event_field(k, f) /* Handles R: T: and any other field not handled elsewhere */ char k; char *f; { if (dotune) { switch (k) { case 'T': textfeature(TITLE, f); break; case 'R': { char* p; p = f; skipspace(&p); if ((strncmp(p, "Hornpipe", 8) == 0) || (strncmp(p, "hornpipe", 8) == 0)) { hornpipe = 1; }; }; break; default: { char buff[100]; if (strlen(f) < 98) { sprintf(buff, "%c:%s", k, f); textfeature(TEXT, buff); }; }; }; }; } event_words(p) /* handles a w: field in the abc */ char* p; { int l; char* s; karaoke = 1; v->haswords = 1; if ((wordvoice != 0) && (wordvoice != v->voiceno)) { event_warning("More than one voice with words in"); }; wordvoice = v->voiceno; /* strip off leading spaces */ s = p; skipspace(&s); words[wcount] = addstring(s); /* strip off any trailing spaces */ l = strlen(s) - 1; while ((l>= 0) && (*(words[wcount]+l) == ' ')) { *(words[wcount]+l) = '\0'; l = l - 1; }; addfeature(WORDLINE, wcount, 0, 0); if (*(words[wcount]+l) != '\\') { addfeature(WORDSTOP, 0, 0, 0); } else { /* remove continuation character */ *(words[wcount]+l) = '\0'; l = l - 1; while ((l>= 0) && (*(words[wcount]+l) == ' ')) { *(words[wcount]+l) = '\0'; l = l - 1; }; }; wcount = wcount + 1; if (wcount >= maxwords) { maxwords = textextend(maxwords, &words); }; } char_out(list, out, ch) /* routine for building up part list */ char* list; char** out; char ch; { if (*out - list >= MAXPARTS) { event_error("Expanded part is too large"); } else { **out = ch; *out = *out + 1; parts = parts + 1; }; } read_spec(spec, part) /* converts a P: field to a list of part labels */ /* e.g. P:A(AB)3(CD)2 becomes P:AABABABCDCD */ char spec[]; char part[]; { char* in; char* out; int i, j; int stackptr; char* stack[10]; stackptr = 0; in = spec; out = part; while (((*in >= 'A') && (*in <= 'Z')) || (*in == '(') || (*in == '.') || (*in == ')') || ((*in >= '0') && (*in <= '9'))) { if (*in == '.') { in = in + 1; }; if ((*in >= 'A') && (*in <= 'Z')) { char_out(part, &out, *in); in = in + 1; }; if (*in == '(') { if (stackptr < 10) { stack[stackptr] = out; stackptr = stackptr + 1; } else { event_error("nesting too deep in part specification"); }; in = in + 1; }; if (*in == ')') { in = in + 1; if (stackptr > 0) { int repeats; char* start; char* stop; if ((*in >= '0') && (*in <= '9')) { repeats = readnump(&in); } else { repeats = 1; }; stackptr = stackptr - 1; start = stack[stackptr]; stop = out; for (i=1; i= '0') && (*in <= '9')) { int repeats; char ch; repeats = readnump(&in); if (out > part) { ch = *(out-1); for (i = 1; i 'Z')) { event_error("Part must be one of A-Z"); return; }; if ((headerpartlabel == 1) && (part[0] == *p)) { /* P: field in header is not a label */ headerpartlabel = 0; /* remove speculative part label */ feature[part_start[(int)*p - (int)'A']] = NONOTE; } else { if (part_start[(int)*p - (int)'A'] != -1) { event_error("Part defined more than once"); }; }; part_start[(int)*p - (int)'A'] = notes; addfeature(PART, (int)*p, 0, 0); checkbreak(); v = getvoicecontext(1); } else { parts = 0; read_spec(p, part); if (parts == 1) { /* might be a label not a specificaton */ headerpartlabel = 1; }; }; }; } checkbreak() /* check that we are in not in chord, grace notes or tuple */ /* called at voice change */ { if (tuplecount != 0) { event_error("Previous voice has an unfinished tuple"); tuplecount = 0; }; if (v->inchord != 0) { event_error("Previous voice has incomplete chord"); event_chordoff(); }; if (v->ingrace != 0) { event_error("Previous voice has unfinished grace notes"); v->ingrace = 0; }; } event_voice(n, s) /* handles a V: field in the abc */ int n; char *s; { addfeature(VOICE, n, 0, 0); voicesused = 1; checkbreak(); v = getvoicecontext(n); } event_length(n) /* handles an L: field in the abc */ int n; { if (pastheader) { v->default_length = n; } else { global.default_length = n; }; } event_blankline() /* blank line found in abc signifies the end of a tune */ { if (dotune) { finishfile(); parseroff(); dotune = 0; }; } event_refno(n) /* handles an X: field (which indicates the start of a tune) */ int n; { if (dotune) { finishfile(); parseroff(); dotune = 0; }; if (verbose) { printf("Reference X: %d\n", n); }; if ((n == xmatch) || (xmatch == 0) || (xmatch == -1)) { if (xmatch == -1) { xmatch = -2; }; parseron(); dotune = 1; pastheader = 0; if (userfilename == 0) { sprintf(outname, "%s%d.mid", outbase, n); }; startfile(); }; } event_tempo(n, a, b, rel) /* handles a Q: field e.g. Q: a/b = n or Q: Ca/b = n */ int n; int a, b, rel; { int t_num, t_denom; int new_div; long new_tempo; if ((n == 0) || ((a!=0) && (b == 0))) { event_error("malformed Q: field ignored"); } else { if (dotune) { if (pastheader) { tempo_num = a; tempo_denom = b; relative_tempo = rel; tempounits(&t_num, &t_denom); new_tempo = (long) 60*1000000*t_denom/(n*4*t_num); new_div = (int) ((float)DIV*(float)new_tempo/(float)tempo + 0.5); addfeature(TEMPO, new_div, 0, 0); } else { Qtempo = n; tempo_num = a; tempo_denom = b; relative_tempo = rel; }; }; }; } event_timesig(n, m, dochecking) /* handles an M: field M:n/m */ int n, m, dochecking; { if (dotune) { if (pastheader) { addfeature(TIME, dochecking, n, m); } else { time_num = n; time_denom = m; timesigset = 1; barchecking = dochecking; }; }; } event_key(sharps, s, minor, modmap, modmul) /* handles a K: field */ int sharps; /* sharps is number of sharps in key signature */ int minor; /* a boolean 0 or 1 */ char *s; /* original string following K: */ char modmap[7]; /* array of accidentals to be applied */ int modmul[7]; /* array giving multiplicity of each accent (1 or 2) */ { if (dotune) { if (pastheader) { setmap(sharps, v->basemap, v->basemul); altermap(v, modmap, modmul); copymap(v); addfeature(KEY, sharps, 0, minor); } else { setmap(sharps, global.basemap, global.basemul); altermap(&global, modmap, modmul); copymap(&global); sf = sharps; mi = minor; headerprocess(); v = newvoice(1); head = v; }; }; } event_graceon() /* a { in the abc */ { if (gracenotes) { event_error("Nested grace notes not allowed"); }; if (v->inchord) { event_error("Grace notes not allowed in chord"); } else { gracenotes = 1; addfeature(GRACEON, 0, 0, 0); v->ingrace = 1; }; } event_graceoff() /* a } in the abc */ { if (!gracenotes) { event_error("} without matching {"); }; gracenotes = 0; addfeature(GRACEOFF, 0, 0, 0); v->ingrace = 0; } event_rep1() /* |1 in the abc */ { if ((notes == 0) || (feature[notes-1] != SINGLE_BAR)) { event_error("[1 must follow a single bar"); } else { feature[notes-1] = BAR1; }; } event_rep2() /* :|2 in the abc */ { if ((notes == 0) || (feature[notes-1] != REP_BAR)) { event_error("[2 must follow a :| "); } else { feature[notes-1] = REP_BAR2; }; } event_slur(t) /* called when s in encountered in the abc */ int t; { if (t) { addfeature(SLUR_ON, 0, 0, 0); v->inslur = 1; } else { slurtotie(); addfeature(SLUR_OFF, 0, 0, 0); v->inslur = 0; }; } event_sluron(t) /* called when ( is encountered in the abc */ int t; { if (t == 1) { addfeature(SLUR_ON, 0, 0, 0); v->inslur = 1; }; } event_sluroff(t) /* called when ) is encountered */ int t; { if (t == 0) { slurtotie(); addfeature(SLUR_OFF, 0, 0, 0); v->inslur = 0; }; } slurtotie() /* converts a pair of identical slurred notes to tied notes */ { int last1, slurtie, last2, failed; int j; if ((!v->ingrace) && (!v->inchord)) { j = notes-1; failed = 0; last1 = -1; while ((j>=0) && (!failed) && (last1 == -1)) { if (feature[j] == NOTE) { last1 = j; }; if ((j<=0) || (feature[j] == REST) || (feature[j] == CHORDOFF) || (feature[j] == GRACEOFF) || (feature[j] == SLUR_ON)) { failed = 1; }; j = j - 1; }; slurtie = -1; while ((j>=0) && (!failed) && (slurtie == -1)) { if (feature[j] == SLUR_TIE) { slurtie = j; }; if ((j<=0) || (feature[j] == REST) || (feature[j] == CHORDOFF) || (feature[j] == GRACEOFF) || (feature[j] == SLUR_ON) || (feature[j] == NOTE)) { failed = 1; }; j = j - 1; }; last2 = -1; while ((j>=0) && (!failed) && (last2 == -1)) { if (feature[j] == NOTE) { last2 = j; }; if ((j<=0) || (feature[j] == REST) || (feature[j] == CHORDOFF) || (feature[j] == GRACEOFF) || (feature[j] == SLUR_ON)) { failed = 1; }; j = j - 1; }; if ((!failed) && (pitch[last1] == pitch[last2])) { /* promote SLUR_TIE to tie */ feature[slurtie] = TIE; event_warning("Slur in abc taken to mean a tie"); } else { if (verbose) { event_warning("Slur ignored"); }; }; }; } event_tie() /* a tie - has been encountered in the abc */ { addfeature(TIE, 0, 0, 0); } event_rest(n,m) /* rest of n/m in the abc */ int n, m; { int num, denom; num = n; denom = m; if (v->inchord) v->chordcount = v->chordcount + 1; if (tuplecount > 0) { num = num * tfact_num; denom = denom * tfact_denom; if (tnote_num == 0) { tnote_num = num; tnote_denom = denom; } else { if (tnote_num * denom != num * tnote_denom) { if (!specialtuple) { event_warning("Different length notes in tuple"); }; }; }; if ((!gracenotes) && ((!v->inchord) || ((v->inchord) && (v->chordcount == 1)))) { tuplecount = tuplecount - 1; }; }; if (v->chordcount == 1) { v->chord_num = num*4; v->chord_denom = denom*(v->default_length); }; if ((!v->ingrace) && ((!v->inchord)||(v->chordcount==1))) { addunits(num, denom*(v->default_length)); }; last_num = 3; /* hornpiping (>) cannot follow rest */ addfeature(REST, 0, num*4, denom*(v->default_length)); if (!v->inchord ) { marknote(); }; } event_bar(type) /* handles bar lines of various types in the abc */ int type; { int newtype; if ((type == THIN_THICK) || (type == THICK_THIN)) { newtype = DOUBLE_BAR; } else { newtype = type; }; addfeature(newtype, 0, 0, 0); copymap(v); zerobar(); } event_space() /* space character in the abc is ignored by abc2midi */ { /* ignore */ /* printf("Space event\n"); */ } event_lineend(ch, n) /* called when \ or ! or * or ** is encountered at the end of a line */ char ch; int n; { /* ignore */ } event_broken(type, mult) /* handles > >> >>> < << <<< in the abc */ int type, mult; { if (v->inchord) { event_error("Broken rhythm not allowed in chord"); } else { if (v->ingrace) { event_error("Broken rhythm not allowed in grace notes"); } else { if ((hornpipe) && (feature[notes-1] == GT)) { /* remove any superfluous hornpiping */ notes = notes - 1; }; /* addfeature(type, mult, 0, 0); */ v->brokentype = type; v->brokenmult = mult; v->brokenpending = 0; }; }; } event_tuple(n, q, r) /* handles triplets (3 and general tuplets (n:q:r in the abc */ int n, q, r; { if (tuplecount > 0) { event_error("nested tuples"); } else { if (r == 0) { specialtuple = 0; tuplecount = n; } else { specialtuple = 1; tuplecount = r; }; if (q != 0) { tfact_num = q; tfact_denom = n; } else { if ((n < 2) || (n > 9)) { event_error("Only tuples (2 - (9 allowed"); tfact_num = 1; tfact_denom = 1; tuplecount = 0; } else { /* deduce tfact_num using standard abc rules */ if ((n == 2) || (n == 4) || (n == 8)) tfact_num = 3; if ((n == 3) || (n == 6)) tfact_num = 2; if ((n == 5) || (n == 7) || (n == 9)) { if ((time_num % 3) == 0) { tfact_num = 3; } else { tfact_num = 2; }; }; tfact_denom = n; }; }; tnote_num = 0; tnote_denom = 0; }; } event_chord() /* a + has been encountered in the abc */ { if (v->inchord) { event_chordoff(); } else { event_chordon(); }; } marknotestart() /* voice data structure keeps a record of last few notes encountered */ /* in order to process broken rhythm. This is called at the start of */ /* a note or chord */ { v->laststart = v->thisstart; v->lastend = v->thisend; v->thisstart = notes-1; } marknoteend() /* voice data structure keeps a record of last few notes encountered */ /* in order to process broken rhythm. This is called at the end of */ /* a note or chord */ { v->thisend = notes-1; if (v->brokenpending != -1) { v->brokenpending = v->brokenpending + 1; if (v->brokenpending == 1) { brokenadjust(); v->brokenpending = -1; }; }; } marknote() /* when handling a single note, not a chord, marknotestart() and */ /* marknoteend() can be called together */ { marknotestart(); marknoteend(); } event_chordon() /* handles a chord start [ in the abc */ { if (v->inchord) { event_error("Attempt to nest chords"); } else { addfeature(CHORDON, 0, 0, 0); v->inchord = 1; v->chordcount = 0; v->chord_num = 0; v->chord_denom = 1; marknotestart(); }; } event_chordoff() /* handles a chord close ] in the abc */ { if (!v->inchord) { event_error("Chord already finished"); } else { addfeature(CHORDOFF, 0, v->chord_num, v->chord_denom); v->inchord = 0; v->chordcount = 0; marknoteend(); }; } splitstring(s, sep, handler) /* breaks up string into fields with sep as the field separator */ /* and calls handler() for each sub-string */ char* s; char sep; void (*handler)(); { char* out; char* p; int fieldcoming; p = s; fieldcoming = 1; while (fieldcoming) { out = p; while ((*p != '\0') && (*p != sep)) p = p + 1; if (*p == sep) { *p = '\0'; p = p + 1; } else { fieldcoming = 0; }; (*handler)(out); }; } void event_handle_instruction(), event_handle_gchord(); event_instruction(s) /* handles a ! ... ! event in the abc */ char* s; { splitstring(s, ';', event_handle_instruction); } int getchordnumber(s) /* looks through list of known chords for chord name given in s */ char *s; { int i; int chordnumber; chordnumber = 0; i = 1; while ((i <= chordsnamed) && (chordnumber == 0)) { if (strcmp(s, chordname[i]) == 0) { chordnumber = i; } else { i = i + 1; }; }; return(chordnumber); } addchordname(s, len, notes) /* adds chord name and note set to list of known chords */ char *s; int notes[]; int len; { int i, j, done; if (strlen(s) > 7) { event_error("Chord name cannot exceed 7 characters"); return; }; if (len > 6) { event_error("Named chord cannot have more than 6 notes"); return; }; i = 0; done = 0; while ((i= MAXCHORDNAMES-1) { event_error("Too many Guitar Chord Names used"); } else { chordsnamed = chordsnamed + 1; strcpy(chordname[chordsnamed], s); chordlen[chordsnamed] = len; for (j=0; j= '0') && (*s <= '5')) { event_finger(s); return; }; p = s; if ((*p >= 'A') && (*p <= 'G')) { note = *p - (int) 'A' + (int) 'a'; bassnote = 0; p = p + 1; } else { if ((*p >= 'a') && (*p <= 'g')) { note = *p; bassnote = 1; p = p + 1; } else { /* Experimental feature supports "_ignored words" */ if (*p != '_') { event_error("Guitar chord does not start with A-G or a-g"); }; return; }; }; accidental = '='; if (*p == '#') { accidental = '^'; p = p + 1; }; if (*p == 'b') { accidental = '_'; p = p + 1; }; basepitch = pitchof(note, accidental, 1, 0) - middle_c; i = 0; while ((i<9) && (*p != ' ') && (*p != '\0')) { name[i] = *p; i = i+1; p = p + 1; }; name[i] = '\0'; chordno = getchordnumber(name); if (chordno == 0) { char msg[40]; sprintf(msg, "Unrecognized chord name \"%s\"", name); event_error(msg); chordno = 1; /* defaults to major */ } else { /* only record voice as having chords if we recognize chord type */ v->hasgchords = 1; if ((gchordvoice != 0) && (gchordvoice != v->voiceno)) { event_warning("More than one voice with guitar chords in"); }; gchordvoice = v->voiceno; }; if (bassnote) { chordno = -1; }; addfeature(GCHORD, basepitch, 0, chordno); } void event_handle_instruction(s) /* handler for ! ! instructions */ /* does ppp pp p mp mf f ff fff */ char* s; { char buff[MAXLINE]; char* p; char* q; int done; p = s; /* remove any leading spaces */ skipspace(&p); /* remove any trailing spaces */ q = p; while ((*q != '\0') && (*q != ' ')) { q = q + 1; }; if (*q == ' ') { *q = '\0'; }; done = 0; if (strcmp(p, "ppp") == 0) { event_specific("MIDI", "beat 30 20 10 1"); done = 1; }; if (strcmp(p, "pp") == 0) { event_specific("MIDI", "beat 45 35 20 1"); done = 1; }; if (strcmp(p, "p") == 0) { event_specific("MIDI", "beat 60 50 35 1"); done = 1; }; if (strcmp(p, "mp") == 0) { event_specific("MIDI", "beat 75 65 50 1"); done = 1; }; if (strcmp(p, "mf") == 0) { event_specific("MIDI", "beat 90 80 65 1"); done = 1; }; if (strcmp(p, "f") == 0) { event_specific("MIDI", "beat 105 95 80 1"); done = 1; }; if (strcmp(p, "ff") == 0) { event_specific("MIDI", "beat 120 110 95 1"); done = 1; }; if (strcmp(p, "fff") == 0) { event_specific("MIDI", "beat 127 125 110 1"); done = 1; }; if (done == 0) { sprintf(buff, "instruction !%s! ignored", s); event_warning(buff); }; } event_finger(p) /* a 1, 2, 3, 4 or 5 has been found in a guitar chord field */ char *p; { /* does nothing */ } hornp(num, denom) /* If we have used R:hornpipe, this routine modifies the rhythm by */ /* applying appropriate broken rhythm */ int num, denom; { if ((hornpipe) && (notes > 0) && (feature[notes-1] != GT)) { if ((num*last_denom == last_num*denom) && (num == 1) && (denom*time_num == 32)) { if (((time_num == 4) && (bar_denom == 8)) || ((time_num == 2) && (bar_denom == 16))) { /* addfeature(GT, 1, 0, 0); */ v->brokentype = GT; v->brokenmult = 1; v->brokenpending = 0; }; }; last_num = num; last_denom = denom; }; } event_note(decorators, accidental, mult, note, octave, n, m) /* handles a note in the abc */ int decorators[DECSIZE]; int mult; char accidental, note; int octave, n, m; { int pitch; int num, denom; num = n; denom = m; if (v->inchord) v->chordcount = v->chordcount + 1; if (tuplecount > 0) { num = num * tfact_num; denom = denom * tfact_denom; if (tnote_num == 0) { tnote_num = num; tnote_denom = denom; } else { if (tnote_num * denom != num * tnote_denom) { if (!specialtuple) { event_warning("Different length notes in tuple"); }; }; }; if ((!gracenotes) && ((!v->inchord) || ((v->inchord) && (v->chordcount == 1)))) { tuplecount = tuplecount - 1; }; }; if ((!v->ingrace) && (!v->inchord)) { hornp(num, denom*(v->default_length)); } else { last_num = 3; /* hornpiping (>) cannot follow chord or grace notes */ }; if ((!v->ingrace) && ((!v->inchord)||(v->chordcount==1))) { addunits(num, denom*(v->default_length)); }; pitch = pitchof(note, accidental, mult, octave); if (decorators[FERMATA]) { num = num*2; }; if (v->chordcount == 1) { v->chord_num = num*4; v->chord_denom = denom*(v->default_length); }; if ((decorators[ROLL]) || (decorators[ORNAMENT])) { if (v->inchord) { event_error("Roll in chord not supported"); } else { doroll(note, octave, num, denom, pitch); }; } else { if (decorators[STACCATO]) { if (v->inchord) { if (v->chordcount == 1) { addfeature(REST, pitch, num*4, denom*(v->default_length)); }; addfeature(NOTE, pitch, num*4, denom*2*(v->default_length)); } else { addfeature(NOTE, pitch, num*4, denom*2*(v->default_length)); marknotestart(); addfeature(REST, pitch, num*4, denom*2*(v->default_length)); marknoteend(); }; } else { addfeature(NOTE, pitch, num*4, denom*(v->default_length)); marknote(); if ((v->inslur) && (!v->ingrace)) { addfeature(SLUR_TIE, 0, 0, 0); }; }; }; } doroll(note, octave, n, m, pitch) /* applies a roll to a note */ char note; int octave, n, m; int pitch; { char up, down; int t; int upoct, downoct, pitchup, pitchdown; char *anoctave = "cdefgab"; upoct = octave; downoct = octave; t = (int) ((long) index(anoctave, note) - (long) anoctave); up = *(anoctave + ((t+1) % 7)); down = *(anoctave + ((t+6) % 7)); if (up == 'c') upoct = upoct + 1; if (down == 'b') downoct = downoct - 1; pitchup = pitchof(up, v->basemap[(int)up - 'a'], 1, upoct); pitchdown = pitchof(down, v->basemap[(int)down - 'a'], 1, downoct); addfeature(NOTE, pitch, n*4, m*(v->default_length)*5); marknotestart(); addfeature(NOTE, pitchup, n*4, m*(v->default_length)*5); addfeature(NOTE, pitch, n*4, m*(v->default_length)*5); addfeature(NOTE, pitchdown, n*4, m*(v->default_length)*5); addfeature(NOTE, pitch, n*4, m*(v->default_length)*5); marknoteend(); } int pitchof(note, accidental, mult, octave) /* finds MIDI pitch value for note */ char note, accidental; int mult, octave; { int p; char acc; int mul, noteno; static int scale[7] = {0, 2, 4, 5, 7, 9, 11}; char *anoctave = "cdefgab"; p = (int) ((long) index(anoctave, note) - (long) anoctave); p = scale[p]; acc = accidental; mul = mult; noteno = (int)note - 'a'; if (acc == ' ') { acc = v->workmap[noteno]; mul = v->workmul[noteno]; } else { if (retain_accidentals) { v->workmap[noteno] = acc; v->workmul[noteno] = mul; }; }; if (acc == '^') p = p + mul; if (acc == '_') p = p - mul; return p + 12*octave + middle_c; } setmap(sf, map, mult) /* work out accidentals to be applied to each note */ int sf; /* number of sharps in key signature -7 to +7 */ char map[7]; int mult[7]; { int j; for (j=0; j<7; j++) { map[j] = '='; mult[j] = 1; }; if (sf >= 1) map['f'-'a'] = '^'; if (sf >= 2) map['c'-'a'] = '^'; if (sf >= 3) map['g'-'a'] = '^'; if (sf >= 4) map['d'-'a'] = '^'; if (sf >= 5) map['a'-'a'] = '^'; if (sf >= 6) map['e'-'a'] = '^'; if (sf >= 7) map['b'-'a'] = '^'; if (sf <= -1) map['b'-'a'] = '_'; if (sf <= -2) map['e'-'a'] = '_'; if (sf <= -3) map['a'-'a'] = '_'; if (sf <= -4) map['d'-'a'] = '_'; if (sf <= -5) map['g'-'a'] = '_'; if (sf <= -6) map['c'-'a'] = '_'; if (sf <= -7) map['f'-'a'] = '_'; } altermap(v, modmap, modmul) /* apply modifiers to a set of accidentals */ struct voicecontext* v; char modmap[7]; int modmul[7]; { int i; for (i=0; i<7; i++) { if (modmap[i] != ' ') { v->basemap[i] = modmap[i]; v->basemul[i] = modmul[i]; }; }; } copymap(v) /* sets up working map at the start of each bar */ struct voicecontext* v; { int j; for (j=0; j<7; j++) { v->workmap[j] = v->basemap[j]; v->workmul[j] = v->basemul[j]; }; } addfeature(f, p, n, d) /* place feature in internal table */ int f, p, n, d; { feature[notes] = f; pitch[notes] = p; num[notes] = n; denom[notes] = d; if ((f == NOTE) || (f == REST) || (f == CHORDOFF)) { reduce(&num[notes], &denom[notes]); }; notes = notes + 1; if (notes >= maxnotes) { maxnotes = autoextend(maxnotes); }; } int autoextend(maxnotes) /* increase the number of abc elements the program can cope with */ int maxnotes; { int newlimit; int *ptr; featuretype *fptr; int i; if (verbose) { event_warning("Extending note capacity"); }; newlimit = maxnotes*2; fptr = (featuretype*) checkmalloc(newlimit*sizeof(featuretype)); for(i=0;i 0) && (feature[tienote] != NOTE) && (feature[tienote] != REST)) { tienote = tienote - 1; }; if (feature[tienote] != NOTE) { event_error("Cannot find note before tie"); } else { inchord = xinchord; /* change NOTE + TIE to TNOTE + REST */ feature[tienote] = TNOTE; feature[j] = REST; num[j] = num[tienote]; denom[j] = denom[tienote]; place = j; tietodo = 1; lasttie = j; tied_num = num[tienote]; tied_denom = denom[tienote]; done = 0; while ((place < notes) && (tied_num >=0) && (done == 0)) { switch (feature[place]) { case NOTE: lastnote = place; if ((tied_num == 0) && (tietodo == 0)) { done = 1; }; if ((pitch[place] == pitch[tienote]) && (tietodo == 1)) { /* tie in note */ if (tied_num != 0) { event_error("Time mismatch at tie"); }; tietodo = 0; /* add time to tied time */ addfract(&tied_num, &tied_denom, num[place], denom[place]); /* add time to tied note */ addfract(&num[tienote], &denom[tienote], num[place], denom[place]); /* change note to a rest */ feature[place] = REST; /* get rid of tie */ if (lasttie != j) { feature[lasttie] = NONOTE; }; }; if (inchord == 0) { /* subtract time from tied time */ addfract(&tied_num, &tied_denom, -num[place], denom[place]); }; break; case REST: if ((tied_num == 0) && (tietodo == 0)) { done = 1; }; if (inchord == 0) { /* subtract time from tied time */ addfract(&tied_num, &tied_denom, -num[place], denom[place]); }; break; case TIE: if (pitch[lastnote] == pitch[tienote]) { lasttie = place; tietodo = 1; }; break; case CHORDON: inchord = 1; break; case CHORDOFF: inchord = 0; /* subtract time from tied time */ addfract(&tied_num, &tied_denom, -num[place], denom[place]); break; default: break; }; place = place + 1; }; if (tietodo == 1) { event_error("Could not find note to be tied"); }; }; } addfract(xnum, xdenom, a, b) /* add a/b to the count of units in the bar */ int *xnum; int *xdenom; int a, b; { *xnum = (*xnum)*b + a*(*xdenom); *xdenom = (*xdenom) * b; reduce(xnum, xdenom); } applybroken(place, type, n) int place, type, n; /* adjust lengths of broken notes */ { int num1, num2, denom12; int j; int forechord, forestart, foreend, backchord, backstart, backend; int failed, lastnote; j = place; failed = 0; forestart = -1; foreend = -1; forechord = 0; /* find following note or chord */ while ((!failed) && (forestart == -1)) { if ((feature[j] == NOTE) || (feature[j] == REST)) { forestart = j; if (forechord) { lastnote = forestart; } else { foreend = forestart; }; }; if ((feature[j] == GRACEON) || (feature[j] == TIE)) { event_error("Unexpected item following broken rhythm"); }; if (feature[j] == CHORDON) { forechord = 1; }; j = j + 1; if (j>=notes) { failed = 1; }; }; /* look for extend of chord if there is one */ while ((!failed) && (foreend == -1)) { if ((feature[j] == NOTE) || (feature[j] == REST)) { lastnote = j; }; if ((feature[j] == GRACEON) || (feature[j] == TIE)) { event_error("Unexpected item following broken rhythm"); }; if (feature[j] == CHORDOFF) { foreend = j; }; j = j + 1; if (j>=notes) { failed = 1; }; }; /* look for note or chord before broken rhythm symbol */ j = place; backend = -1; backstart = -1; backchord = 0; while ((!failed) && (backend == -1)) { if ((feature[j] == NOTE) || (feature[j] == REST)) { backend = j; if (backchord) { lastnote = backend; } else { backstart = backend; }; }; if ((feature[j] == GRACEOFF) || (feature[j] == TIE)) { event_error("Unexpected item preceding broken rhythm"); }; if (feature[j] == CHORDOFF) { backchord = 1; backend = j; }; j = j - 1; if (j<0) { failed = 1; }; }; /* look for extent of chord if there is one */ while ((!failed) && (backstart == -1)) { if ((feature[j] == NOTE) || (feature[j] == REST)) { lastnote = j; }; if ((feature[j] == GRACEON) || (feature[j] == TIE)) { event_error("Unexpected item following broken rhythm"); }; if (feature[j] == CHORDON) { backstart = lastnote; }; j = j - 1; if (j<0) { failed = 1; }; }; switch(n) { case 1: num1 = 4; num2 = 2; break; case 2: num1 = 7; num2 = 1; break; case 3: num1 = 15; num2 = 1; break; }; denom12 = (num1 + num2)/2; if (type == LT) { j = num1; num1 = num2; num2 = j; }; /* check for same length notes */ if ((num[backstart]*denom[forestart]) != (num[forestart]*denom[backstart])) { failed = 1; }; if (failed) { event_error("Cannot apply broken rhythm"); } else { for (j=backstart; j<=backend; j++) { lenmul(j, num1, denom12); }; for (j=forestart; j<=foreend; j++) { lenmul(j, num2, denom12); }; }; } brokenadjust() /* adjust lengths of broken notes */ { int num1, num2, denom12; int j; int failed; switch(v->brokenmult) { case 1: num1 = 4; num2 = 2; break; case 2: num1 = 7; num2 = 1; break; case 3: num1 = 15; num2 = 1; break; }; denom12 = (num1 + num2)/2; if (v->brokentype == LT) { j = num1; num1 = num2; num2 = j; }; failed = 0; if ((v->laststart == -1) || (v->lastend == -1) || (v->thisstart == -1) || (v->thisend == -1)) { failed = 1; } else { /* check for same length notes */ if ((num[v->laststart]*denom[v->thisstart]) != (num[v->thisstart]*denom[v->laststart])) { failed = 1; }; }; if (failed) { event_error("Cannot apply broken rhythm"); } else { /* printf("Adjusting %d to %d and %d to %d\n", v->laststart, v->lastend, v->thisstart, v->thisend); */ for (j=v->laststart; j<=v->lastend; j++) { lenmul(j, num1, denom12); }; for (j=v->thisstart; j<=v->thisend; j++) { lenmul(j, num2, denom12); }; }; } applygrace(place) int place; /* assign lengths to grace notes before generating MIDI */ { int start, end, p; int next_num, next_denom; int fact_num, fact_denom; int grace_num, grace_denom; int j; int nextinchord; int hostnotestart, hostnoteend; j = place; start = -1; while ((j < notes) && (start == -1)) { if (feature[j] == GRACEON) { start = j; }; if (feature[j] == GRACEOFF) { event_error("} with no matching {"); }; j = j + 1; }; /* now find end of grace notes */ end = -1; while ((j < notes) && (end == -1)) { if (feature[j] == GRACEOFF) { end = j; }; if ((feature[j] == GRACEON) && (j != start - 1)) { event_error("nested { not allowed"); }; j = j + 1; }; /* now find following note */ nextinchord = 0; hostnotestart = -1; while ((hostnotestart == -1) && (j < notes)) { if ((feature[j] == NOTE) || (feature[j] == REST)) { hostnotestart = j; }; if (feature[j] == GRACEON) { event_error("Intervening note needed between grace notes"); }; if (feature[j] == CHORDON) { nextinchord = 1; }; j = j + 1; }; hostnoteend = -1; if (nextinchord) { while ((hostnoteend == -1) && (j < notes)) { if (feature[j] == CHORDOFF) { hostnotestart = j; }; j = j + 1; }; } else { hostnoteend = hostnotestart; }; if (hostnotestart == -1) { event_error("No note found to follow grace notes"); } else { /* count up grace units */ grace_num = 0; grace_denom = 1; p = start; while (p <= end) { if ((feature[p] == NOTE) || (feature[p] == REST)) { grace_num = grace_num * denom[p] + grace_denom * num[p]; grace_denom = grace_denom * denom[p]; reduce(&grace_num, &grace_denom); }; p = p + 1; }; /* adjust host note or notes */ p = hostnotestart; while (p <= hostnoteend) { if ((feature[p] == NOTE) || (feature[p] == REST) || (feature[p] == CHORDOFF)) { next_num = num[p]; next_denom = denom[p]; num[p] = num[p] * (gfact_denom - gfact_num); denom[p] = next_denom * gfact_denom; reduce(&num[p], &denom[p]); }; p = p + 1; }; fact_num = next_num * grace_denom * gfact_num; fact_denom = next_denom * grace_num * gfact_denom; reduce(&fact_num, &fact_denom); /* adjust length of grace notes */ p = start; while (p <= end) { lenmul(p, fact_num, fact_denom); p = p + 1; }; }; } dograce() /* assign lengths to grace notes before generating MIDI */ { int j; j = 0; while (j < notes) { if (feature[j] == GRACEON) { applygrace(j); }; if (feature[j] == SETGRACE) { gfact_num = pitch[j]; gfact_denom = denom[j]; }; if (feature[j] == LINENUM) { lineno = pitch[j]; }; j = j + 1; }; } lenmul(n, a, b) /* multiply note length by a/b */ int n, a, b; { if ((feature[n] == NOTE) || (feature[n] == REST) || (feature[n] == CHORDOFF)) { num[n] = num[n] * a; denom[n] = denom[n] * b; reduce(&num[n], &denom[n]); }; } zerobar() /* start a new count of beats in the bar */ { bar_num = 0; bar_denom = 1; } placerep(j) /* patch up missing repeat */ int j; { event_warning("Assuming repeat"); switch(feature[j]) { case DOUBLE_BAR: feature[j] = BAR_REP; break; case SINGLE_BAR: feature[j] = BAR_REP; break; case REP_BAR: feature[j] = DOUBLE_REP; break; case BAR_REP: event_error("Too many end repeats"); break; case DOUBLE_REP: event_error("Too many end repeats"); break; default: event_error("Internal error - please report"); break; }; } fixreps() /* find and correct missing repeats in music */ { int j; int rep_point; int expect_repeat; int use_next; expect_repeat = 0; use_next = 0; j = 0; while (j < notes) { switch(feature[j]) { case SINGLE_BAR: if (use_next) { rep_point = j; use_next = 0; }; break; case DOUBLE_BAR: rep_point = j; use_next = 0; break; case BAR_REP: expect_repeat = 1; use_next = 0; break; case REP_BAR: if (!expect_repeat) { placerep(rep_point); }; expect_repeat = 0; rep_point = j; use_next = 0; break; case REP_BAR2: if (!expect_repeat) { placerep(rep_point); }; expect_repeat = 0; use_next = 1; break; case DOUBLE_REP: if (!expect_repeat) { placerep(rep_point); }; expect_repeat = 1; break; default: break; }; j = j + 1; }; } startfile() /* called at the beginning of an abc tune by event_refno */ /* This sets up all the default values */ { int j; if (verbose) { printf("scanning tune\n"); }; /* set up defaults */ sf = 0; mi = 0; setmap(0, global.basemap, global.basemul); copymap(&global); voicecount = 0; head = NULL; v = NULL; time_num = 4; time_denom = 4; timesigset = 0; barchecking = 1; global.default_length = -1; event_tempo(120, 1, 4, 0); notes = 0; ntexts = 0; gfact_num = 1; gfact_denom = 3; global_transpose = 0; hornpipe = 0; karaoke = 0; retain_accidentals = 1; wcount = 0; parts = -1; middle_c = 60; for (j=0; j<26; j++) { part_start[j] = -1; }; headerpartlabel = 0; for (j=0; j<16;j++) { channels[j] = 0; }; set_gchords("z"); gchordvoice = 0; wordvoice = 0; } tempounits(t_num, t_denom) /* interprets Q: once default length is known */ int *t_num, *t_denom; { /* calculate unit for tempo */ if (tempo_num == 0) { *t_num = 1; *t_denom = global.default_length; } else { if (relative_tempo) { *t_num = tempo_num; *t_denom = tempo_denom*global.default_length; } else { *t_num = tempo_num; *t_denom = tempo_denom; }; }; } setbeat() /* default accompaniment patterns for various time signatures */ { /* set up chord/fundamental sequence if not already set */ if ((time_num == 2) && (time_denom == 2)) { set_gchords("fzczfzcz"); }; if (((time_num == 2) || (time_num == 4)) && (time_denom == 4)) { set_gchords("fzczfzcz"); }; if ((time_num == 3) && (time_denom == 4)) { set_gchords("fzczcz"); }; if ((time_num == 6) && (time_denom == 8)) { set_gchords("fzcfzc"); }; if ((time_num == 9) && (time_denom == 8)) { set_gchords("fzcfzcfzc"); }; } headerprocess() /* called after the K: field has been reached, signifying the end of */ /* the header and the start of the tune */ { int t_num, t_denom; if (headerpartlabel == 1) { part_start[(int)part[0] - (int)'A'] = notes; addfeature(PART, part[0], 0, 0); }; addfeature(DOUBLE_BAR, 0, 0, 0); pastheader = 1; gracenotes = 0; /* not in a grace notes section */ if (!timesigset) { event_warning("No M: in header, using default"); }; /* calculate time for a default length note */ if (global.default_length == -1) { if (((float) time_num)/time_denom < 0.75) { global.default_length = 16; } else { global.default_length = 8; }; }; bar_num = 0; bar_denom = 1; set_meter(time_num, time_denom); if (hornpipe) { if ((time_denom != 4) || ((time_num != 2) && (time_num != 4))) { event_error("Hornpipe must be in 2/4 or 4/4 time"); hornpipe = 0; }; }; tempounits(&t_num, &t_denom); /* make tempo in terms of 1/4 notes */ tempo = (long) 60*1000000*t_denom/(Qtempo*4*t_num); div_factor = division; setbeat(); voicesused = 0; } finishfile() /* end of tune has been reached - write out MIDI file */ { extern int nullputc(); clearvoicecontexts(); if (!pastheader) { event_error("No K: field found at start of tune"); } else { int i; if (parts > -1) { addfeature(PART, ' ', 0, 0); }; if (headerpartlabel == 1) { event_error("P: field in header should go after K: field"); }; if (verbose) { printf("handling grace notes\n"); }; dograce(); tiefix(); if ((parts == -1) && (voicecount == 1)) { if (verbose) { printf("fixing repeats\n"); }; fixreps(); }; if ((voicesused == 0) && (!karaoke) && (gchordvoice == 0)) { ntracks = 1; } else { ntracks = voicecount + karaoke + 1; if (gchordvoice != 0) { ntracks = ntracks + 1; }; }; if (check) { Mf_putc = nullputc; if (ntracks == 1) { writetrack(0); } else { for (i=0; i