/* tomidi.c version 1.6.8 * This file is part of the code for abc2midi a converter from abc format * to MIDI. * * 19th August 1996 * James Allwright * Department of Electronics and Computer Science, * University of Southampton, UK * * 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 DIV 480 #define MAXPARTS 100 #define MAXLINE 500 #define INITTEXTS 20 #define INITWORDS 20 #define MAXCHORDNAMES 80 /* 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 */ char part[MAXPARTS]; int parts, partno; int part_start[26], part_count[26]; int partrepno; int err_num, err_denom; int voicesused; /* Tempo handling (Q: field) */ long tempo; int time_num, time_denom, tempo_num, tempo_denom; int relative_tempo, Qtempo; int division = DIV; int div_factor; long delta_time; long tracklen, tracklen1; /* output file generation */ int userfilename = 0; char outname[40], outbase[40]; int check; /* bar length checking */ int bar_num, bar_denom, barno, barsize; int b_num, b_denom; int barchecking; /* generating MIDI output */ int beat; int loudnote, mednote, softnote; int middle_c; int channel, program; #define MAXCHANS 16 int channels[MAXCHANS + 3]; int transpose, global_transpose; int gfact_num, gfact_denom; /* karaoke handling */ int karaoke, wcount, ktied, kspace, koverflow; char* wordlineptr; char** words; int maxwords = INITWORDS; int wordson, noteson, gchordson; int thismline, thiswline, windex, thiswfeature; int wordlineplace; int nowordline; int waitforbar; int wlineno, syllcount; int lyricsyllables, musicsyllables; /* Generating accompaniment */ int gchords, g_started; int basepitch, chordnum; struct notetype { int base; int chan; int vel; }; struct notetype gchord, fun; int g_num, g_denom; int g_next; char gchord_seq[40]; int gchord_len[40]; int g_ptr; /* queue for notes waiting to end */ /* allows us to do general polyphony */ #define QSIZE 50 struct Qitem { int delay; int pitch; int chan; int next; }; struct Qitem Q[QSIZE+1]; int Qhead, freehead, freetail; /* Notes due to finish in the future are held in a queue (linked list) in time order. Qhead points to the head of the list and addtoQ() adds a note to the list. The unused elements of array Q are held in another linked list pointed to by freehead. The tail is pointed to by freetail. removefromQ() removes an element (always from the head of the list) and adds it to the free list. Qinit() initializes the queue and clearQ() outputs all the remaining notes at the end of a track. Qcheck() and PrintQ() are diagnostic routines. */ 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) 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() { 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; int* list; /* 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.6.8\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() { addfeature(LINENUM, lineno, 0, 0); } event_startmusicline() { addfeature(MUSICLINE, 0, 0, 0); } event_endmusicline(endchar) 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) 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) 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); }; } set_gchords(s) /* set up a string which indicates how to generate accompaniment from */ /* guitar chords (i.e. "A", "G" in abc). */ /* called from dodeferred(), startfile() and setbeat() */ char* s; { int seq_len; char* p; int j; p = s; j = 0; seq_len = 0; while (((*p == 'z') || (*p == 'c') || (*p == 'f')) && (j<39)) { gchord_seq[j] = *p; p = p + 1; if ((*p >= '0') && (*p <= '9')) { gchord_len[j] = readnump(&p); } else { gchord_len[j] = 1; }; seq_len = seq_len + gchord_len[j]; j = j + 1; }; if (seq_len == 0) { event_error("Bad gchord"); gchord_seq[0] = 'z'; gchord_len[0] = 1; seq_len = 1; }; gchord_seq[j] = '\0'; if (j == 39) { event_error("Sequence string too long"); }; /* work out unit delay in 1/4 notes*/ g_num = time_num * 4; g_denom = time_denom * seq_len; reduce(&g_num, &g_denom); } dodeferred(s) /* handle package-specific command which has been held over to be */ /* interpreted as MIDI is being generated */ char* s; { char* p; char command[40]; int done; p = s; skipspace(&p); readstr(command, &p); skipspace(&p); done = 0; if (strcmp(command, "program") == 0) { int chan, prog; skipspace(&p); prog = readnump(&p); chan = channel; skipspace(&p); if ((*p >= '0') && (*p <= '9')) { chan = prog - 1; prog = readnump(&p); }; write_program(prog, chan); done = 1; }; if (strcmp(command, "gchord") == 0) { set_gchords(p); done = 1; }; if ((strcmp(command, "chordprog") == 0) && (gchordvoice > 0)) { int prog; prog = readnump(&p); write_program(prog, gchord.chan); done = 1; }; if ((strcmp(command, "bassprog") == 0) && (gchordvoice> 0)) { int prog; prog = readnump(&p); write_program(prog, fun.chan); done = 1; }; if (strcmp(command, "chordvol") == 0) { gchord.vel = readnump(&p); done = 1; }; if (strcmp(command, "bassvol") == 0) { fun.vel = readnump(&p); done = 1; }; if (strcmp(command, "beat") == 0) { skipspace(&p); loudnote = readnump(&p); skipspace(&p); mednote = readnump(&p); skipspace(&p); softnote = readnump(&p); skipspace(&p); beat = readnump(&p); if (beat == 0) { beat = barsize; }; done = 1; }; if (strcmp(command, "control") == 0) { int chan, n, datum; char data[20]; char sel[40]; skipspace(&p); chan = channel; if (isalpha(*p)) { readstr(sel, &p); skipspace(&p); if (strcmp(sel, "bass") == 0) { chan = fun.chan; }; if (strcmp(sel, "chord") == 0) { chan = gchord.chan; }; }; n = 0; while ((n<20) && (*p >= '0') && (*p <= '9')) { datum = readnump(&p); if (datum > 127) { event_error("data must be in the range 0 - 127"); datum = 0; }; data[n] = (char) datum; n = n + 1; skipspace(&p); }; write_control(chan, data, n); done = 1; }; if (done == 0) { event_error("Command not recognized"); }; } 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]; 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 (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; { 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() { 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() { v->laststart = v->thisstart; v->lastend = v->thisend; v->thisstart = notes-1; } marknoteend() { v->thisend = notes-1; if (v->brokenpending != -1) { v->brokenpending = v->brokenpending + 1; if (v->brokenpending == 1) { brokenadjust(); v->brokenpending = -1; }; }; } marknote() { 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) 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) 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 { 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) 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]); }; } softcheckbar(pass) /* allows repeats to be in mid-bar */ int pass; { if (barchecking) { if ((bar_num-barsize*(bar_denom) >= 0) || (barno <= 0)) { checkbar(pass); }; }; } checkbar(pass) /* check to see we have the right number of notes in the bar */ int pass; { char msg[80]; if (barchecking) { /* only generate these errors once */ if (noteson && (partrepno == 0)) { /* allow zero length bars for typesetting purposes */ if ((bar_num-barsize*(bar_denom) != 0) && (bar_num != 0) && ((pass == 2) || (barno != 0))) { sprintf(msg, "Bar %d has %d", barno, bar_num); if (bar_denom != 1) { sprintf(msg+strlen(msg), "/%d", bar_denom); }; sprintf(msg+strlen(msg), " units instead of %d", barsize); if (pass == 2) { strcpy(msg+strlen(msg), " in repeat"); }; event_warning(msg); }; }; }; if (bar_num > 0) { barno = barno + 1; }; bar_num = 0; bar_denom = 1; /* zero place in gchord sequence */ if (gchordson) { g_ptr = 0; addtoQ(0, g_denom, -1, g_ptr, 0); }; } addunits(a, b) /* add a/b to the count of units in the bar */ int a, b; { bar_num = bar_num*(b*b_denom) + (a*b_num)*bar_denom; bar_denom = bar_denom * (b*b_denom); reduce(&bar_num, &bar_denom); } reduce(a, b) /* elimate common factors in fraction a/b */ int *a, *b; { int sign; int t, n, m; if (*a < 0) { sign = -1; *a = -*a; } else { sign = 1; }; /* find HCF using Euclid's algorithm */ if (*a > *b) { n = *a; m = *b; } else { n = *b; m = *a; }; while (m != 0) { t = n % m; n = m; m = t; }; *a = (*a/n)*sign; *b = *b/n; } save_state(vec, a, b, c, d, e) /* save status when we go into a repeat */ int vec[5]; int a, b, c, d, e; { vec[0] = a; vec[1] = b; vec[2] = c; vec[3] = d; vec[4] = e; } restore_state(vec, a, b, c, d, e) /* restore status when we loop back to do second repeat */ int vec[5]; int *a, *b, *c, *d, *e; { *a = vec[0]; *b = vec[1]; *c = vec[2]; *d = vec[3]; *e = vec[4]; } zerobar() /* start a new count of beats in the bar */ { bar_num = 0; bar_denom = 1; barno = barno + 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; }; } int findchannel() /* work out next available channel */ { int j; j = 0; while ((j= MAXCHANS + 3) { event_error("Too many channels required"); j = 0; }; return (j); } findpart(j, partno) /* find out where part starts */ int *j; int *partno; { char newpart; int n; *partno = *partno + 1; while ((*partno < parts) && (part_start[(int)part[*partno] - (int) 'A'] == -1)) { event_error("Part not defined"); *partno = *partno + 1; }; if (*partno >= parts) { *j = notes; } else { newpart = part[*partno]; n = (int) newpart - (int) 'A'; partrepno = part_count[n]; part_count[n]++; *j = part_start[n]; }; if (verbose) { if (*partno < parts) { printf("Doing part %c number %d of %d\n", part[*partno], *partno, parts); }; }; } int findvoice(initplace, voice) /* find where next occurence of correct voice is */ int initplace; int voice; { int foundvoice; int j; foundvoice = 0; j = initplace; while ((j < notes) && (foundvoice == 0)) { if (feature[j] == PART) { if (parts != -1) { /* go to next part label */ findpart(&j, &partno); }; if (voice == 1) { foundvoice = 1; } else { j = j + 1; }; } else { if ((feature[j] == VOICE) && (pitch[j] == voice)) { foundvoice = 1; } else { j = j + 1; }; }; }; return(j); } karaokestarttrack() /* header information for karaoke track based on w: fields */ { int j; text_data("@LENGL"); j = 0; while ((j < notes) && (feature[j] != TITLE)) j = j+1; if (feature[j] == TITLE) { char atitle[200]; strcpy(atitle, "@T"); strcpy(atitle+2, atext[pitch[j]]); text_data(atitle); }; kspace = 0; ktied = 0; koverflow = 0; } int findwline(startline) int startline; /* Find next line of lyrics at or after startline. */ { int place; int done; int newwordline; int inwline, extending; int versecount, target; /* printf("findwline called with %d\n", startline); */ done = 0; inwline = 0; nowordline = 0; newwordline = -1; target = partrepno; if (startline == thismline) { versecount = 0; extending = 0; } else { versecount = target; extending = 1; }; if (thismline == -1) { event_error("First lyrics line must come after first music line"); } else { place = startline + 1; /* search for corresponding word line */ while ((place < notes) && (!done)) { switch (feature[place]) { case WORDLINE: inwline = 1; /* wait for words for this pass */ if (versecount == target) { thiswfeature = place; newwordline = place; windex = pitch[place]; wordlineplace = 0; done = 1; }; break; case WORDSTOP: if (inwline) { versecount = versecount + 1; }; inwline = 0; /* stop if we are part-way through a lyric set */ if (extending) { done = 1; }; break; case PART: done = 1; break; case VOICE: done = 1; break; case MUSICLINE: done = 1; break; default: break; }; place = place + 1; if (done && (newwordline == -1) && (versecount > 0) && (!extending)) { target = partrepno % versecount ; versecount = 0; place = startline+1; done = 0; inwline = 0; }; }; if (newwordline == -1) { /* remember that we couldn't find lyrics */ nowordline = 1; if (lyricsyllables == 0) { event_warning("Line of music without lyrics"); }; }; }; return(newwordline); } int getword(place, w) /* picks up next syllable out of w: field */ int* place; int w; { char syllable[200]; char c; int i; int syllcount; enum {empty, inword, postword, foundnext, failed} syllstatus; i = 0; syllcount = 0; if (w >= wcount) { syllable[i] = '\0'; return ('\0'); }; if (*place == 0) { if ((w % 2) == 0) { syllable[i] = '/'; } else { syllable[i] = '\\'; }; i = i + 1; }; if (kspace) { syllable[i] = ' '; i = i + 1; }; syllstatus = empty; c = *(words[w]+(*place)); while ((syllstatus != postword) && (syllstatus != failed)) { syllable[i] = c; switch(c) { case '\0': if (syllstatus == empty) { syllstatus = failed; } else { syllstatus = postword; kspace = 1; }; break; case '~': syllable[i] = ' '; syllstatus = inword; *place = *place + 1; i = i + 1; break; case '\\': if (*(words[w]+(*place+1)) == '-') { syllable[i] = '-'; syllstatus = inword; *place = *place + 2; i = i + 1; } else { /* treat like plain text */ *place = *place + 1; if (i>0) { syllstatus = inword; i = i + 1; }; }; break; case ' ': if (syllstatus == empty) { *place = *place + 1; } else { syllstatus = postword; *place = *place + 1; kspace = 1; }; break; case '-': if (syllstatus == inword) { syllstatus = postword; *place = *place + 1; kspace = 0; } else { *place = *place + 1; }; break; case '*': if (syllstatus == empty) { syllstatus = postword; *place = *place + 1; } else { syllstatus = postword; }; break; case '_': if (syllstatus == empty) { syllstatus = postword; *place = *place + 1; } else { syllstatus = postword; }; break; case '|': if (syllstatus == empty) { syllstatus = failed; *place = *place + 1; } else { syllstatus = postword; *place = *place + 1; kspace = 1; }; waitforbar = 1; break; default: /* copying plain text character across */ /* first character must be alphabetic */ if ((i>0) || isalpha(syllable[0])) { syllstatus = inword; i = i + 1; }; *place = *place + 1; break; }; c = *(words[w]+(*place)); }; syllable[i] = '\0'; if (syllstatus == failed) { syllcount = 0; } else { syllcount = 1; if (strlen(syllable) > 0) { text_data(syllable); }; }; /* now deal with anything after the syllable */ while ((syllstatus != failed) && (syllstatus != foundnext)) { c = *(words[w]+(*place)); switch (c) { case ' ': *place = *place + 1; break; case '-': *place = *place + 1; kspace = 0; break; case '\t': *place = *place + 1; break; case '_': *place = *place + 1; syllcount = syllcount + 1; break; case '|': if (waitforbar == 0) { *place = *place + 1; waitforbar = 1; } else { syllstatus = failed; }; break; default: syllstatus = foundnext; break; }; }; return(syllcount); } write_syllable(place) int place; /* Write out a syllable. This routine must check that it has a line of */ /* lyrics and find one if it doesn't have one. */ { musicsyllables = musicsyllables + 1; if (waitforbar) { lyricsyllables = lyricsyllables + 1; return; }; if ((!nowordline) && (!waitforbar)) { if (thiswline == -1) { thiswline = findwline(thismline); }; if (!nowordline) { int done; done = 0; while (!done) { if (syllcount == 0) { /* try to get fresh word */ syllcount = getword(&wordlineplace, windex); if (waitforbar) { done = 1; if (syllcount == 0) { lyricsyllables = lyricsyllables + 1; }; } else { if (syllcount == 0) { thiswline = findwline(thiswline); if (thiswline == -1) { done = 1; }; }; }; }; if (syllcount > 0) { /* still finishing off a multi-syllable item */ syllcount = syllcount - 1; lyricsyllables = lyricsyllables + 1; done = 1; }; }; }; }; } checksyllables() /* check line of lyrics matches line of music */ { int done; int syllcount; char msg[80]; /* first make sure all lyric syllables are read */ done = 0; while (!done) { syllcount = getword(&wordlineplace, windex); if (syllcount > 0) { lyricsyllables = lyricsyllables + syllcount; } else { thiswline = findwline(thiswline); if (thiswline == -1) { done = 1; } else { windex = pitch[thiswline]; }; }; }; if (lyricsyllables != musicsyllables) { sprintf(msg, "Verse %d mismatch; %d syllables in music %d in lyrics", partrepno+1, musicsyllables, lyricsyllables); event_error(msg); }; lyricsyllables = 0; musicsyllables = 0; } starttrack() { int i; loudnote = 105; mednote = 95; softnote = 80; transpose = global_transpose; set_meter(time_num, time_denom); div_factor = division; gchords = 1; tracklen = 0L; partno = -1; g_started = 0; g_ptr = 0; Qinit(); channel = findchannel(); if (gchordson) { addtoQ(0, g_denom, -1, g_ptr, 0); fun.base = 36; fun.vel = 80; gchord.base = 48; gchord.vel = 75; channels[channel] = 1; fun.chan = findchannel(); channels[fun.chan] = 1; gchord.chan = findchannel(); channels[fun.chan] = 0; channels[channel] = 0; }; g_next = 0; partrepno = 0; thismline = -1; thiswline = -1; nowordline = 0; waitforbar = 0; musicsyllables = 0; lyricsyllables = 0; for (i=0; i<26; i++) { part_count[i] = 0; }; } long writetrack(xtrack) /* this routine writes a MIDI track */ int xtrack; { int trackvoice; int inchord; int j, pass; int expect_repeat; int slurring; int state[5]; void mf_write_tempo(); trackvoice = xtrack; noteson = 1; wordson = 0; gchordson = 1; if (karaoke) { if (xtrack == 2) { karaokestarttrack(); noteson = 0; wordson = 1; gchordson = 0; trackvoice = wordvoice; } else { if (trackvoice > 2) trackvoice = xtrack - 1; }; }; if (verbose) { printf("track %d, voice %d\n", xtrack, trackvoice); }; if (xtrack == 0) { if (karaoke) { text_data("@KMIDI KARAOKE FILE"); }; mf_write_tempo(tempo); /* write key */ write_keysig(sf, mi); /* write timesig */ write_meter(time_num, time_denom); if ((voicesused) || (karaoke)) { /* type 1 files have no notes in first track */ return(0L); }; }; starttrack(); inchord = 0; /* write notes */ j = 0; if ((voicesused) && (trackvoice != 1)) { j = findvoice(j, trackvoice); }; barno = 0; bar_num = 0; bar_denom = 1; delta_time = 0L; err_num = 0; err_denom = 1; pass = 1; save_state(state, j, barno, div_factor, transpose, channel); slurring = 0; expect_repeat = 0; while (j < notes) { switch(feature[j]) { case NOTE: if (wordson) { write_syllable(j); }; if (noteson) { noteon(j); /* set up note off */ addtoQ(num[j], denom[j], pitch[j] + transpose, channel, -1); }; if (!inchord) { delay(num[j], denom[j], 0); addunits(num[j], denom[j]); }; break; case TNOTE: if (noteson) { noteon(j); /* set up note off */ addtoQ(num[j], denom[j], pitch[j] + transpose, channel, -1); }; break; case REST: if (!inchord) { delay(num[j], denom[j], 0); addunits(num[j], denom[j]); }; break; case CHORDON: inchord = 1; break; case CHORDOFF: if (wordson) { write_syllable(j); }; inchord = 0; delay(num[j], denom[j], 0); addunits(num[j], denom[j]); break; case LINENUM: /* get correct line number for diagnostics */ lineno = pitch[j]; break; case MUSICLINE: if (wordson) { thismline = j; nowordline = 0; }; break; case MUSICSTOP: if (wordson) { checksyllables(); }; break; case PART: if (parts == -1) { char msg[1]; msg[0] = (char) pitch[j]; mf_write_meta_event(0L, marker, msg, 1); } else { findpart(&j, &partno); }; break; case VOICE: /* search on for next occurence of voice */ j = findvoice(j, trackvoice); break; case TEXT: mf_write_meta_event(0L, text_event, atext[pitch[j]], strlen(atext[pitch[j]])); break; case TITLE: mf_write_meta_event(0L, sequence_name, atext[pitch[j]], strlen(atext[pitch[j]])); break; case SINGLE_BAR: waitforbar = 0; checkbar(pass); break; case DOUBLE_BAR: waitforbar = 0; checkbar(pass); break; case BAR_REP: waitforbar = 0; softcheckbar(pass); if (expect_repeat) { event_error("Expected end repeat not found at |:"); }; save_state(state, j, barno, div_factor, transpose, channel); expect_repeat = 1; pass = 1; break; case REP_BAR: waitforbar = 0; softcheckbar(pass); if (pass == 1) { if (!expect_repeat) { event_error("Found unexpected :|"); } else { pass = 2; restore_state(state, &j, &barno, &div_factor, &transpose, &channel); slurring = 0; }; } else { pass = 1; expect_repeat = 0; }; break; case BAR1: waitforbar = 0; checkbar(pass); if (pass == 2) { while ((j 0) { div_factor = pitch[j]; }; break; case CHANNEL: channel = pitch[j]; break; case TRANSPOSE: transpose = pitch[j]; break; case RTRANSPOSE: transpose = transpose + pitch[j]; break; case SLUR_ON: if (slurring) { event_error("Unexpected start of slur found"); }; slurring = 1; break; case SLUR_OFF: if (!slurring) { event_error("Unexpected end of slur found"); }; slurring = 0; break; default: break; }; j = j + 1; }; if (expect_repeat) { event_error("Missing :| at end of tune"); }; clearQ(); tracklen = tracklen + delta_time; if (xtrack == 1) { tracklen1 = tracklen; } else { if ((xtrack != 0) && (tracklen != tracklen1)) { char msg[100]; sprintf(msg, "Track %d is %ld units long not %ld", xtrack, tracklen, tracklen1); event_warning(msg); }; }; return (delta_time); } set_meter(n, m) int n, m; { /* set up barsize */ barsize = n; if (barsize % 3 == 0) { beat = 3; } else { if (barsize % 2 == 0) { beat = 2; } else { beat = barsize; }; }; /* correction factor to make sure we count in the right units */ if (m > 4) { b_num = m/4; b_denom = 1; } else { b_num = 1; b_denom = 4/m; }; } write_meter(n, m) int n, m; { int t, dd; char data[4]; set_meter(n, m); dd = 0; t = m; while (t > 1) { dd = dd + 1; t = t/2; }; data[0] = (char)n; data[1] = (char)dd; if (n%2 == 0) { data[2] = (char)(24*2*n/m); } else { data[2] = (char)(24*n/m); }; data[3] = 8; mf_write_meta_event(0L, time_signature, data, 4); } write_keysig(sf, mi) int sf, mi; { char data[2]; data[0] = (char) (0xff & sf); data[1] = (char) mi; mf_write_meta_event(0L, key_signature, data, 2); } noteon(n) /* compute note data and call noteon_data to write MIDI note event */ int n; { int vel; /* set velocity */ if (bar_num == 0) { vel = loudnote; } else { if ((bar_denom == 1) && ((bar_num % beat) == 0)) { vel = mednote; } else { vel = softnote; }; }; noteon_data(pitch[n] + transpose, channel, vel); } text_data(s) /* write text event to MIDI file */ char* s; { mf_write_meta_event(delta_time, text_event, s, strlen(s)); tracklen = tracklen + delta_time; delta_time = 0L; } noteon_data(pitch, channel, vel) int pitch, channel, vel; { midi_noteon(delta_time, pitch, channel, vel); tracklen = tracklen + delta_time; delta_time = 0L; } write_program(p, channel) int p, channel; { char data[1]; data[0] = p; if (channel >= MAXCHANS) { event_error("Channel limit exceeded\n"); } else { mf_write_midi_event(delta_time, program_chng, channel, data, 1); }; tracklen = tracklen + delta_time; delta_time = 0L; } write_control(channel, data, n) /* write MIDI control event */ int channel, n; char data[]; { if (channel >= MAXCHANS) { event_error("Channel limit exceeded\n"); } else { mf_write_midi_event(delta_time, control_change, channel, data, n); }; } delay(a, b, c) /* wait for time a/b */ int a, b, c; { int dt; dt = (div_factor*a)/b + c; err_num = err_num * b + ((div_factor*a)%b)*err_denom; err_denom = err_denom * b; reduce(&err_num, &err_denom); dt = dt + (err_num/err_denom); err_num = err_num%err_denom; timestep(dt, 0); } midi_noteon(delta_time, pitch, chan, vel) long delta_time; int pitch, chan, vel; { char data[2]; #ifdef NOFTELL extern int nullpass; #endif data[0] = (char) pitch; data[1] = (char) vel; if (channel >= MAXCHANS) { event_error("Channel limit exceeded\n"); } else { mf_write_midi_event(delta_time, note_on, chan, data, 2); #ifdef NOFTELL if (nullpass != 1) { channels[chan] = 1; }; #else channels[chan] = 1; #endif }; } midi_noteoff(delta_time, pitch, chan) long delta_time; int pitch, chan; { char data[2]; data[0] = (char) pitch; data[1] = (char) 0; if (channel >= MAXCHANS) { event_error("Channel limit exceeded\n"); } else { mf_write_midi_event(delta_time, note_off, chan, data, 2); }; } save_note(num, denom, pitch, chan, vel) /* queue up note */ int num, denom; int pitch, chan, vel; { noteon_data(pitch + transpose, chan, vel); addtoQ(num, denom, pitch + transpose, chan, -1); } dogchords(i) /* generate accompaniment notes */ int i; { if ((i == g_ptr) && (g_ptr < strlen(gchord_seq))) { int len; char action; action = gchord_seq[g_ptr]; len = gchord_len[g_ptr]; if ((chordnum == -1) && (action == 'c')) { action = 'f'; }; switch (action) { case 'z': break; case 'c': /* do chord */ if (g_started && gchords) { int i; for (i=0; i wait) { Q[*ptr].delay = Q[*ptr].delay - wait; Q[i].next = *ptr; Q[i].delay = wait; *ptr = i; done = 1; } else { wait = wait - Q[*ptr].delay; ptr = &Q[*ptr].next; }; }; }; } removefromQ(i) int i; { if (i == -1) { printQ(); event_fatal_error("Internal error - nothing to remove from queue"); } else { if (Q[Qhead].delay != 0) { printQ(); event_fatal_error("Internal error - queue head has non-zero time"); }; Qhead = Q[i].next; Q[i].next = freehead; freehead = i; }; } clearQ() { int time; int i; /* remove gchord requests */ time = 0; while ((Qhead != -1) && (Q[Qhead].pitch == -1)) { time = time + Q[Qhead].delay; i = Qhead; Qhead = Q[i].next; Q[i].next = freehead; freehead = i; }; if (Qhead != -1) { timestep(time, 1); }; /* do any remaining note offs, but don't do chord request */ while (Qhead != -1) { event_error("Sustained notes beyond end of track"); timestep(Q[Qhead].delay+1, 1); }; } printQ() { int t; printf("Qhead = %d freehead = %d freetail = %d\n", Qhead, freehead, freetail); t = Qhead; printf("Q:"); while (t != -1) { printf("p(%d)-%d->", Q[t].pitch, Q[t].delay); t = Q[t].next; }; printf("\n"); } timestep(t, atend) int t; int atend; { int time; int headtime; time = t; /* process any notes waiting to finish */ while ((Qhead != -1) && (Q[Qhead].delay < time)) { headtime = Q[Qhead].delay; delta_time = delta_time + (long) headtime; time = time - headtime; advanceQ(headtime); if (Q[Qhead].pitch == -1) { if (!atend) { dogchords(Q[Qhead].chan); }; } else { midi_noteoff(delta_time, Q[Qhead].pitch, Q[Qhead].chan); tracklen = tracklen + delta_time; delta_time = 0L; }; removefromQ(Qhead); }; if (Qhead != -1) { advanceQ(time); }; delta_time = delta_time + (long)time; } advanceQ(t) int t; { if (Qhead == -1) { event_error("Internal error - empty queue"); } else { Q[Qhead].delay = Q[Qhead].delay - t; }; } Qinit() { int i; /* initialize queue of notes waiting to finish */ Qhead = -1; freehead = 0; for (i=0; i= QSIZE)) { failed = 1; printf("Queue corrupted Q[].next = %d\n", nextitem); }; }; qfree = 0; nextitem = freehead; while (nextitem != -1) { qfree = qfree + 1; used[nextitem] = 1; nextitem = Q[nextitem].next; if ((nextitem < -1) || (nextitem >= QSIZE)) { failed = 1; printf("Free Queue corrupted Q[].next = %d\n", nextitem); }; }; if (qfree + qused < QSIZE) { failed = 1; printf("qfree = %d qused = %d\n", qused, qfree); }; for (i=0; i -1) { addfeature(PART, ' ', 0, 0); }; if (headerpartlabel == 1) { event_error("P: field in header should go after K: field"); }; /* if (verbose) { printf("handling broken rhythm\n"); }; */ if (verbose) { printf("handling grace notes\n"); }; dograce(); tiefix(); if (verbose) { printf("fixing repeats\n"); }; if ((parts == -1) && (voicecount == 1)) { fixreps(); }; if (check) { Mf_putc = nullputc; if ((voicesused == 0) && (!karaoke)) { writetrack(0); } else { for (i=0; i<= (voicecount+karaoke); i++) { writetrack(i); }; }; } else { if ((fp = fopen(outname, "wb")) == NULL) { event_fatal_error("File open failed"); }; printf("writing MIDI file %s\n", outname); Mf_putc = myputc; Mf_writetrack = writetrack; if ((voicesused == 0) && (!karaoke)) { mfwrite(0, 1, division, fp); } else { mfwrite(1, voicecount+karaoke+1, division, fp); }; fclose(fp); #ifdef __MACINTOSH__ (void) setOutFileCreator(outname,'Midi','ttxt'); #endif /* __MACINTOSH__ */ }; for (i=0; i