abcmidi-20070318/0000775000175000017500000000000010576505314012700 5ustar anselmanselmabcmidi-20070318/store.c0000664000175000017500000032622410576505227014214 0ustar anselmanselm/* * abc2midi - program to convert abc files to MIDI files. * Copyright (C) 1999 James Allwright * e-mail: J.R.Allwright@westminster.ac.uk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* 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. * * James Allwright * * Macintosh Port 30th July 1996 * Wil Macaulay (wil@syndesis.com) */ #define VERSION "2.01 March 15 2007" /* enables reading V: indication in header */ #define XTEN1 1 /*#define INFO_OCTAVE_DISABLED 1*/ /* for Microsoft Visual C++ 6.0 and higher */ #ifdef _MSC_VER #define ANSILIBS #endif #ifdef WIN32 #define snprintf _snprintf #endif #include "abc.h" #include "parseabc.h" #include "parser2.h" #include "midifile.h" #include "genmidi.h" #include #ifdef __MWERKS__ #define __MACINTOSH__ 1 #endif /* __MWERKS__ */ #ifdef __MACINTOSH__ int setOutFileCreator(char *fileName,unsigned long theType, unsigned long theCreator); #endif /* __MACINTOSH__ */ /* define USE_INDEX if your C libraries have index() instead of strchr() */ #ifdef USE_INDEX #define strchr index #endif #ifdef ANSILIBS #include #include #include #else extern char* strchr(); extern void reduce(); #endif /*int snprintf(char *str, size_t size, const char *format, ...);*/ #define MAXLINE 500 #define INITTEXTS 20 #define INITWORDS 20 #define MAXCHANS 16 /* global variables grouped roughly by function */ FILE *fp; programname fileprogram = ABC2MIDI; extern int oldchordconvention; /* for handling +..+ chords */ /* 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; int ratio_a, ratio_b; int velocitychange = 15; int chordstart=0; /* microtonal support and scale temperament */ int active_pitchbend; int microtone=0; int temperament = 0; #define SEMISIZE 4096 int octave_size = 12*SEMISIZE; int fifth_size = 7*SEMISIZE; /* default to 12-edo */ struct voicecontext { /* maps of accidentals for each stave line */ char basemap[7], workmap[7][10]; int basemul[7], workmul[7][10]; int keyset; /* flag to indicate whether key signature set */ int default_length; int voiceno; /* voice number referenced by V: command. To avoid conflicts with split voices, all split voices begin from 32. */ int indexno; /* voice index number in the feat array. It just increments by one and depends on the order the voices are created -- including split voices. */ int topvoiceno,topindexno; /* links to original voice in the split */ int hasgchords; int haswords; int inslur; int ingrace; int octaveshift; int lastbarloc; /* position of last bar line parsed */ int tosplitno,fromsplitno; /* links to split voices and source voice*/ int last_resync_point; /* 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; /* for handling broken rhythms*/ /* broken rythm handling */ int brokentype, brokenmult, brokenpending; int broken_stack[7]; struct voicecontext* next; }; struct voicecontext global; struct voicecontext* v; struct voicecontext* head; struct voicecontext* vaddr[64]; /* address of all voices (by v->indexno) */ /* vaddr is only a convenience for debugging */ int dependent_voice[64]; /* flag to indicate type of voice */ int voicecount; int numsplits=0; int splitdepth = 0; /* 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; int *bentpitch; /* needed for handling microtones */ featuretype *feature; int *pitchline; /* introduced for handling ties */ int notes; int verbose = 0; int titlenames = 0; int got_titlename; int namelimit; int xmatch; int sf, mi; int gchordvoice, wordvoice, drumvoice, dronevoice; int gchordtrack, drumtrack, dronetrack; int ratio_standard = -1; /* flag corresponding to -RS parameter */ /* when ratio_standard != -1 the ratio for a>b is 3:1 instead of 2:1 */ int quiet = -1; /* if not -1 many common warnings and error messages */ /* are suppressed. */ int fermata_fixed = 0; /* flag on how to process fermata */ /* Part handling */ struct vstring part; extern int parts, partno, partlabel; extern int part_start[26], part_count[26]; int voicesused; /* Tempo handling (Q: field) */ int time_num, time_denom; int mtime_num, mtime_denom; long tempo; int tempo_num, tempo_denom; int relative_tempo, Qtempo; extern int division; extern int div_factor; int default_tempo = 120; /* quarter notes per minutes */ /* output file generation */ int userfilename = 0; char *outname = NULL; char *outbase = NULL; int check; int nofnop; /* for suppressing dynamics (ff, pp etc) */ int ntracks; /* bar length checking */ extern int bar_num, bar_denom; int barchecking; /* generating MIDI output */ int middle_c; extern int channels[MAXCHANS + 3]; extern int additive; int gfact_num, gfact_denom, gfact_method; /* for handling grace notes */ /* karaoke handling */ int karaoke, wcount; char** words; int maxwords = INITWORDS; extern int decorators_passback[DECSIZE]; /* a kludge for passing information from the event_handle_instruction to parsenote in parseabc.c */ /* time signature after header processed */ int header_time_num,header_time_denom; int dummydecorator[DECSIZE]; /* used in event_chord */ extern char* featname[]; void addfract(int *xnum, int *xdenom, int a, int b); static void zerobar(); static void addfeature(int f,int p,int n,int d); static void replacefeature(int f, int p, int n, int d, int loc); static void insertfeature(int f, int p, int n, int d, int loc); static void textfeature(int type, char *s); extern long writetrack(); void init_drum_map(); static void fix_enclosed_note_lengths(int from, int end); static int patchup_chordtie(int chordstart,int chordend); static void copymap(struct voicecontext* v); static struct voicecontext* newvoice(n) /* allocate and initialize the data for a new voice */ int n; { struct voicecontext *s; int i,j; s = (struct voicecontext*) checkmalloc(sizeof(struct voicecontext)); voicecount = voicecount + 1; s->voiceno = n; s->indexno = voicecount; s->topvoiceno = n; s->topindexno = voicecount; 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->lastbarloc = -1; s->laststart = -1; s->lastend = -1; s->thisstart = -1; s->thisend = -1; s->brokenpending = -1; s->tosplitno = -1; s->fromsplitno = -1; s->last_resync_point=0; s->next = NULL; for (i=0; i<7; i++) { s->basemap[i] = global.basemap[i]; s->basemul[i] = global.basemul[i]; for (j=0;j<10;j++) { s->workmap[i][j] = global.workmap[i][j]; s->workmul[i][j] = global.workmul[i][j]; }; } s->keyset = global.keyset; s->octaveshift = global.octaveshift; vaddr[voicecount] = s; return(s); } static struct voicecontext* getvoicecontext(n) /* find the data structure for a given voice number */ int n; { struct voicecontext *p; struct voicecontext *q; int i,j; 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; }; /* check that key signature mapping is set if global * key signature set. */ if (p->keyset == 0 && global.keyset) { p->keyset = 1; for (i=0; i<7; i++) { p->basemap[i] = global.basemap[i]; p->basemul[i] = global.basemul[i]; for (j=0;j<10;j++) { p->workmap[i][j] = global.workmap[i][j]; p->workmul[i][j] = global.workmul[i][j]; } }; } return(p); } static void 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; } static 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); } static void 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 [reference number] [-c] [-v] "); printf("[-o filename]\n"); printf(" [-t] [-n ] [-RS] [-NFNP]\n"); printf(" [reference number] selects a tune\n"); printf(" -c selects checking only\n"); printf(" -v selects verbose option\n"); printf(" -ver prints version number and exits\n"); printf(" -o selects output filename\n"); printf(" -t selects filenames derived from tune titles\n"); printf(" -n set limit for length of filename stem\n"); printf(" -RS use 3:1 instead of 2:1 for broken rhythms\n"); printf(" -quiet suppress some common warnings\n"); printf(" -Q default tempo (quarter notes/minute)\n"); printf(" -NFNP don't process !p! or !f!-like fields\n"); printf(" -OCC old chord convention (eg. +CE+)\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]; outbase = addstring(argv[1]); for (j = 0; j< (int) strlen(outbase); j++) { if (outbase[j] == '.') outbase[j] = '\0'; }; }; /* look for filename stem limit */ j = getarg("-n", argc, argv); if (j != -1) { if (argc >= j+1) { namelimit = 0; sscanf(argv[j], "%d", &namelimit); if ((namelimit < 3) || (namelimit > 252)) { event_fatal_error("filename stem limit must be in the range 3 - 252"); }; } else { event_error("No number given, ignoring -n option"); }; }; /* look for default tempo */ j = getarg("-Q", argc,argv); if (j != -1) { if (argc >= j+1) { sscanf(argv[j], "%d", &default_tempo); if (default_tempo < 3) { event_fatal_error("Q parameter is too small\nEnter -Q 240 not -Q 1/4=240"); }; } else { event_error("No number given, ignoring -Q option"); }; }; /* look for user-supplied output filename */ j = getarg("-o", argc, argv); if (j != -1) { if (argc >= j+1) { outname = addstring(argv[j]); userfilename = 1; if (xmatch == 0) { xmatch = -1; }; if (titlenames == 1) { event_warning("-o option over-rides -t option"); titlenames = 0; }; } else { event_error("No filename given, ignoring -o option"); }; }; ratio_standard = getarg("-RS", argc, argv); quiet = getarg("-quiet", argc, argv); dotune = 0; parseroff(); setup_chordnames(); } void event_text(s) /* text found in abc file */ char *s; { char msg[200]; /* to prevent possible buffer overflow in sprintf, which is */ /* a security risk, we truncate s */ #ifdef NO_SNPRINTF sprintf(msg, "Ignoring text: %s", s); /* SS 2005-01-09 */ #else snprintf(msg, sizeof(msg), "Ignoring text: %s", s); /* AL 2005-01-04 */ #endif event_warning(msg); } void event_x_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); } void event_abbreviation(symbol, string, container) /* abbreviation encountered - this is handled within the parser */ char symbol; char *string; char container; { } void event_acciaccatura() { /* does nothing here but outputs a / in abc2abc */ return; } /* support functions for split voices */ static int locate_voice(int start, int indexno) /* This function finds the beginning of voice:indexno in the feature array. It starts looking from j = start */ { int j; j = start; while (j < notes) { if (feature[j] == VOICE && pitch[j] == indexno) { return j; } j++; } return j; } static void sync_voice (struct voicecontext *vv, int sync_to, int ignorecurrentbar) { /* The function scans the contents of the feature[] array between, the last resync point and sync_to (or end of the feature array) depending on the ignorecurrentbar flag. It counts the time of all notes and rests in the topvoice voice belonging to voice vv and adds rests each time it encounters a bar line, to synchronize this voice with its topvoice. It also copies all repeat signs and dynamic markings in the topvoice and inserts them in the current voice. This function is called by event_split_voice, complete_all_voices and event_bar (if split voices are present). The last_resync_point keeps track of what has been scanned, so we only scan the topvoice once. */ int j; char *p; char command[40]; int maxnotes,begin; char message[80]; int snum,sdenom; int insidechord; int voiceno,indexno; voiceno = vv->topvoiceno; indexno = vv->topindexno; snum = 0; sdenom = 1; begin =0; /* to bypass double bar or single bar at beginning */ /* we have already synced to last_resync_point so we continue from here. */ j = vv->last_resync_point; if (voiceno != 1 || j>2) j = locate_voice(j,indexno); else j++; /* bypass bar */ if (ignorecurrentbar) maxnotes = sync_to; else maxnotes = notes-1; /*ignore last voice change */ insidechord = 0; /*printf("syncing voice %d to %d from %d to %d \n",vv->indexno,indexno,j,maxnotes);*/ while (j<=maxnotes) { /* dumpfeat(j,j); */ switch (feature[j]) { case VOICE: if (pitch[j] != indexno) j = locate_voice(j,indexno); break; break; case CHORDON: insidechord = 1; break; case CHORDOFF: case CHORDOFFEX: insidechord = 0; break; case SINGLE_BAR: case DOUBLE_BAR: case BAR_REP: case REP_BAR: case DOUBLE_REP: if (snum>0) { /* bypass REST if no notes processed */ addfeature(REST,0,snum,sdenom); /*printf(" added %d/%d to voice %d\n",snum,sdenom,vv->indexno);*/ snum = 0; sdenom =1; } addfeature(feature[j], 0, 0, denom[j]); /* copy feature */ break; case PLAY_ON_REP: if (feature[j-1] == SINGLE_BAR || feature[j-1] == REP_BAR || feature[j-1] == VOICE) addfeature(feature[j],0,0,denom[j]); else { sprintf(message,"expecting SINGLE_BAR or REP_BAR preceding" " PLAY_ON_REP instead found %s at %d\n",featname[feature[j-1]],j-1); event_error(message); } break; case DYNAMIC: p = atext[pitch[j]]; skipspace(&p); readstr(command, &p, 40); if (strcmp(command, "program") == 0) { textfeature(DYNAMIC, atext[pitch[j]]); } break; case CHANNEL: addfeature(feature[j], pitch[j], 0, 0); /* copy feature */ break; case NOTE: case TNOTE: case REST: if (insidechord < 2) addfract(&snum,&sdenom,num[j],denom[j]); if (insidechord) insidechord++; begin = 1; break; default: break; } j++; } /* There are no more indexno notes between maxnotes and notes-1, so set sync point at the end. */ vv->last_resync_point = notes-1; } int search_backwards_for_last_bar_line (int from) { int found,j; found = 0; j = from; while (!found && j>0) { if (feature[j] == SINGLE_BAR || feature[j] == DOUBLE_BAR || feature[j] == BAR_REP || feature[j] == REP_BAR || feature[j] == PLAY_ON_REP || feature[j] == DOUBLE_REP) {found = 1; break;} j--; } return j; } /* When a split voice is encountered for the first time, we must create a new voice and insert the proper delay (rests) so that it remains in sync with the starting voice. If the split voice already exists, we still sync it to the source voice (there may have been intervening bars with no splits) by adding rests. */ int sync_to; void event_split_voice() { int splitno; int voiceno,indexno; int topvoiceno,topindexno; int program; int octaveshift; voicesused = 1; /* multivoice file */ splitno = v->tosplitno; program = 0; /* a voice split in bar is just like a bar line */ zerobar(); v->lastbarloc = notes; /* in case we need to change it */ voiceno = v->voiceno; indexno = v->indexno; topvoiceno = v->topvoiceno; topindexno = v->topindexno; octaveshift = v->octaveshift; if (topvoiceno == voiceno) sync_to = search_backwards_for_last_bar_line(notes-1); addfeature(SINGLE_BAR,0,0,0); if (splitno == -1) {splitno = 32+numsplits++; v->tosplitno = splitno; } v = getvoicecontext(splitno); splitdepth++; addfeature(VOICE, v->indexno, 0, 0); if (v->fromsplitno == -1) { v->fromsplitno = voiceno; v->topvoiceno = topvoiceno; v->topindexno = topindexno; v->octaveshift = octaveshift; } dependent_voice[v->indexno] = 1; /* when syncing the split voice we want to be sure that we do not include the notes in the last bar in the source voice the notes in the split voice take their place. */ sync_voice (v,sync_to,1); } void recurse_back_to_original_voice () { int previous_voice; previous_voice = v->fromsplitno; while (previous_voice >-1 && splitdepth > 0) { v = getvoicecontext(previous_voice); previous_voice = v->fromsplitno; splitdepth--; } addfeature(VOICE, v->indexno, 0, 0); copymap(v); } void recurse_back_and_change_bar (int type) { int previous_voice; previous_voice = v->fromsplitno; while (previous_voice >-1 && splitdepth > 0) { v = getvoicecontext(previous_voice); if (v->lastbarloc > -1) replacefeature(type, 0,0,0, v->lastbarloc); previous_voice = v->fromsplitno; splitdepth--; } addfeature(VOICE, v->indexno, 0, 0); } static void complete_all_split_voices () { int splitno; struct voicecontext *p; int voiceno,indexno; v = head; while (v != NULL) { splitno = v->tosplitno; if (splitno > -1) { voiceno = v->voiceno; indexno = v->indexno; p = getvoicecontext(splitno); addfeature(VOICE, p->indexno, 0, 0); sync_voice (p,0,0); /* complete fraction of bar */ if(bar_num >0) addfeature(REST, 0, 4*bar_num, bar_denom); } v = v->next; }; } /* end of code for split voices */ void event_tex(s) /* TeX command found - ignore it */ char *s; { } void event_fatal_error(s) /* print error message and halt */ char *s; { event_error(s); exit(1); } void 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 } void 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 } static int autoextend(maxnotes) /* increase the number of abc elements the program can cope with */ int maxnotes; { int newlimit; int *ptr,*ptr2,*ptr3; featuretype *fptr; int i; if (verbose) { event_warning("Extending note capacity"); }; newlimit = maxnotes*2; fptr = (featuretype*) checkmalloc(newlimit*sizeof(featuretype)); for(i=0;i= maxnotes) { maxnotes = autoextend(maxnotes); }; } static void replacefeature(f, p, n, d, loc) int f, p, n, d, loc; { feature[loc] = f; pitch[loc] = p; num[loc] = n; denom[loc] = d; } static void insertfeature(f, p, n, d, loc) /* insert feature in internal table */ int f,p,n,d,loc; { int i; notes = notes + 1; if (notes >= maxnotes) { maxnotes = autoextend(maxnotes); }; for (i=notes;i>loc;i--) { feature[i] = feature[i-1]; pitch[i] = pitch[i-1]; pitchline[i] = pitchline[i-1]; bentpitch[i] = bentpitch[i-1]; num[i] = num[i-1]; denom[i] = denom[i-1]; }; feature[loc] = f; pitch[loc] = p; num[i] = n; denom[i] = d; pitchline[i] = 0; bentpitch[i] = 0; } static void removefeature(loc) int loc; { int i; for (i=loc;i= maxtexts) { maxtexts = textextend(maxtexts, &atext); }; } void event_comment(s) /* comment found in abc */ char *s; { if (dotune) { if (pastheader) { textfeature(TEXT, s); } else { textfeature(TEXT, s); }; }; } void 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; int done; if (dotune == 0) { event_specific_in_header(package,s); return; } if (strcmp(package, "MIDI") == 0) { int ch; int trans, rtrans; p = s; done = 0; skipspace(&p); readstr(command, &p, 40); 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 (trans == 0) { addfeature(GTRANSPOSE, val, 0, 0); } else { addfeature(RTRANSPOSE, val, 0, 0); }; 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, "fermatafixed") == 0) { fermata_fixed = 1; done = 1; }; if (strcmp(command, "fermataproportional") == 0) { fermata_fixed = 0; done = 1; }; if (strcmp(command, "ratio") == 0) { int a, b; skipspace(&p); b = readnump(&p); skipspace(&p); a = readnump(&p); if ((a > 0) && (b > 0)) { ratio_a = a; ratio_b = b; if (ratio_a + ratio_b % 2 == 1) { ratio_a = 2 * a; ratio_b = 2 * b; }; } else { event_error("Invalid ratio"); }; done = 1; }; if (strcmp(command, "grace") == 0) { int a, b; char msg[200]; 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, 1, a, b); } else { gfact_num = a; gfact_denom = b; }; }; done = 1; }; if(strcmp(command,"gracedivider") == 0) { int b; char msg[200]; skipspace(&p); b = -1; b = readnump(&p); if (b < 2) { sprintf(msg, "a number 2 or larger should follow MIDI gracedivider"); event_error(msg); } if (pastheader) addfeature(SETGRACE, 0, 1, b); else {gfact_denom = b; gfact_method = 0;} done = 1; } if (strcmp(command, "trim") == 0) { int a, b; skipspace(&p); a = readnump(&p); if (*p != '/') { event_error("Need / in MIDI trim command (eg trim 1/4)"); } else { p = p + 1; b = readnump(&p); addfeature(SETTRIM, 1, 4*a, b*v->default_length); }; 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] = readsnump(&p); i = i + 1; }; addchordname(name, i, notes); }; done = 1; }; if (strcmp(command, "temperamentlinear") == 0) { double octave_cents=0.0; double fifth_cents=0.0; temperament = 1; middle_c = 60*SEMISIZE; if (sscanf(p," %lf %lf ",&octave_cents,&fifth_cents) == 2) { octave_size = (int)(octave_cents*SEMISIZE/100+0.5); fifth_size = (int)(fifth_cents*SEMISIZE/100+0.5); } else { event_error("Bad format for lineartemperament command"); } done = 1; } if (strcmp(command, "temperamentnormal") == 0) { temperament = 0; event_normal_tone(); done = 1; middle_c = 60; } if (strcmp(command,"drumon") == 0) { addfeature(DRUMON, 0, 0, 0); if ((drumvoice != 0) && (drumvoice != v->indexno)) { event_warning("Implementation limit: drums only supported in one voice"); }; if (v == NULL) event_error("%%MIDI drumon must occur after the first K: header"); else { drumvoice = v->indexno; done = 1; } } if (strcmp(command,"drumoff") == 0) { addfeature(DRUMOFF, 0, 0, 0); done = 1; } if (strcmp(command,"droneon") == 0) { addfeature(DRONEON, 0, 0, 0); if ((dronevoice != 0) && (dronevoice != v->indexno)) { event_warning("Implementation limit: drones only supported in one voice"); }; if (v == NULL) event_error("%%MIDI droneon must occur after the first K: header"); else { dronevoice = v->indexno; done = 1; } } if (strcmp(command,"droneoff") == 0) { addfeature(DRONEOFF, 0, 0, 0); done = 1; } if (strcmp(command, "deltaloudness") == 0) { skipspace(&p); velocitychange = readnump(&p); done = 1; } if (done == 0) { /* add as a command to be interpreted later */ textfeature(DYNAMIC, s); }; } else { /* Parse %%abc directive */ done = 0; if (strcmp(package, "abc") == 0) { p = s; skipspace(&p); /* skip '-' character after abc */ p++; readstr(command, &p, 40); /* Parse %%abc-copyright */ if (strcmp(command, "copyright") == 0) { int lnth; int n; char *ptr; done = 1; skipspace(&p); /* Copy string to parse. * Convert \n, \t, \r, \x and \\ in string to C style character constant defaults. * (Handles carriage return, linefeed and tab characters and hex codes.) */ lnth = 0; ptr = &msg[0]; while ((*p != '\0') && (lnth < 199)) { if (*p == '\\') { p++; switch (*p) { case 'n': *ptr = '\n'; break; case 'r': *ptr = '\r'; break; case 't': *ptr = '\t'; break; case '\\': *ptr = *p; break; case 'x': case 'X': p++; sscanf (p, "%x", &n); *ptr = n; while ((*p != '\0') && (isxdigit (*p))) p++; p--; break; default: *ptr = *p; break; } } else *ptr = *p; ptr++; p++; lnth++; /* Anselm Lingnau 2005-01-04 */ } *ptr = '\0'; textfeature(COPYRIGHT, msg); if (*p != '\0') { /* Anselm Lingnau 2005-01-04 */ event_warning("ABC copyright notice abridged"); } } else { /* Changed by Anselm Lingnau, 2005-01-04, for security */ #ifdef NO_SNPRINTF if (sprintf(msg, "%%%s%s", package, s) > sizeof(msg)) { event_warning("event_specific: comment too long"); } #else if (snprintf(msg,sizeof(msg), "%%%s%s", package, s) > sizeof(msg)) { event_warning("event_specific: comment too long"); } #endif event_comment(msg); } } if (done == 0) { /* changed by Anselm Lingnau on 2004-01-04, for security */ #ifdef NO_SNPRINTF if (sprintf(msg, "%%%s%s", package, s) > sizeof(msg)) { event_warning("event_specific: message too long"); } #else if (snprintf(msg, sizeof(msg), "%%%s%s", package, s) > sizeof(msg)) { event_warning("event_specific: message too long"); } #endif event_comment(msg); } } } /* global variables that can altered by %%MIDI before tune */ int default_middle_c = 60; int default_retain_accidentals = 1; int default_fermata_fixed = 0; int default_ratio_a = 2; int default_ratio_b = 4; void event_specific_in_header(package, s) /* package-specific command found i.e. %%NAME */ /* only %%MIDI commands are actually handled */ char *package, *s; { char command[40]; char *p; int done; if (strcmp(package, "MIDI") == 0) { p = s; done = 0; skipspace(&p); readstr(command, &p, 40); if (strcmp(command, "C") == 0) { int val; skipspace(&p); val = readnump(&p); default_middle_c = val; done = 1; }; if (strcmp(command, "nobarlines") == 0) { default_retain_accidentals = 0; done = 1; }; if (strcmp(command, "barlines") == 0) { default_retain_accidentals = 1; done = 1; }; if (strcmp(command, "fermatafixed") == 0) { default_fermata_fixed = 1; done = 1; }; if (strcmp(command, "fermataproportional") == 0) { default_fermata_fixed = 0; done = 1; }; if (strcmp(command, "ratio") == 0) { int a, b; skipspace(&p); b = readnump(&p); skipspace(&p); a = readnump(&p); if ((a > 0) && (b > 0)) { default_ratio_a = a; default_ratio_b = b; if (default_ratio_a + default_ratio_b % 2 == 1) { default_ratio_a = 2 * a; default_ratio_b = 2 * b; }; } else { event_error("Invalid ratio"); }; 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] = readsnump(&p); i = i + 1; }; addchordname(name, i, notes); }; done = 1; }; if (strcmp(command, "deltaloudness") == 0) { skipspace(&p); velocitychange = readnump(&p); done = 1; } if (done == 0) { event_warning("cannot handle this MIDI directive here"); } } } void event_startinline() /* start of in-line field in abc music line */ { } void event_closeinline() /* end of in-line field in abc music line */ { } void extract_filename(char *f) /* work out filename stem from tune title */ /* name length cannot exceed namelimit characters */ { char buffer[256]; int i; char *p; i = 0; p = f; skipspace(&p); /* avoid initial 'The' or 'the' */ if ((strncmp(p, "The", 3) == 0) || (strncmp(p, "the", 3) == 0)) { p = p + 3; skipspace(&p); }; while ((*p != '\0') && (i < namelimit)) { if (isalnum(*p)) { buffer[i] = *p; i = i + 1; }; p = p + 1; }; buffer[i] = '\0'; if (i == 0) { strcpy(buffer, "notitle"); buffer[namelimit] = '\0'; }; strcpy(&buffer[strlen(buffer)], ".mid"); if (outname != NULL) { free(outname); }; outname = addstring(buffer); got_titlename = 1; } void event_field(k, f) /* Handles R: T: and any other field not handled elsewhere */ /* Added code to handle C: field. */ char k; char *f; { if (dotune) { switch (k) { case 'T': textfeature(TITLE, f); if (titlenames && (!got_titlename)) { extract_filename(f); }; break; case 'C': textfeature(COMPOSER, 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[256]; if (strlen(f) < 256) { sprintf(buff, "%c:%s", k, f); textfeature(TEXT, buff); }; }; }; } else { if (k == 'T') { event_warning("T: outside tune body - possible missing X:"); }; }; } void event_words(p, continuation) /* handles a w: field in the abc */ char* p; int continuation; { karaoke = 1; v->haswords = 1; if ((wordvoice != 0) && (wordvoice != v->indexno)) { event_warning("More than one voice with words in"); }; wordvoice = v->indexno; words[wcount] = addstring(p); addfeature(WORDLINE, wcount, 0, 0); if (continuation == 0) { addfeature(WORDSTOP, 0, 0, 0); }; wcount = wcount + 1; if (wcount >= maxwords) { maxwords = textextend(maxwords, &words); }; } static void 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(1,1); }; if (v->ingrace != 0) { event_error("Previous voice has unfinished grace notes"); v->ingrace = 0; }; } static void char_out(part, ch) /* routine for building up part list */ struct vstring* part; char ch; { /* if (*out - list >= MAXPARTS) { event_error("Expanded part is too large"); } else { **out = ch; *out = *out + 1; parts = parts + 1; }; */ addch(ch, part); parts = parts + 1; } static void 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 */ /********** This feature is not supported ********[SS] 2004-10-08 */ /* A '+' indicates 'additive' behaviour (a part may include repeats). */ /* A '-' indicates 'non-additive' behaviour (repeat marks in the music */ /* are ignored and only repeats implied by the part order statement */ /* are played). */ char spec[]; struct vstring* part; { char* in; int i, j; int stackptr; char* stack[10]; char lastch; stackptr = 0; in = spec; while (((*in >= 'A') && (*in <= 'Z')) || (*in == '(') || (*in == '.') || (*in == ')') || (*in == '+') || (*in == '-') || ((*in >= '0') && (*in <= '9'))) { if (*in == '.') { in = in + 1; }; /* if (*in == '+') { no longer supported * additive = 1; * in = in + 1; * }; * if (*in == '-') { * additive = 0; * in = in + 1; * }; */ if ((*in >= 'A') && (*in <= 'Z')) { char_out(part, *in); lastch = *in; in = in + 1; }; if (*in == '(') { if (stackptr < 10) { stack[stackptr] = part->st + strlen(part->st); 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 = part->st + strlen(part->st); for (i=1; i= '0') && (*in <= '9')) { int repeats; repeats = readnump(&in); if (part->len > 0) { for (i = 1; i 'Z')) { event_error("Part must be one of A-Z"); return; }; if ((headerpartlabel == 1) && (part.st[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; }; }; }; } void event_voice(n, s, vp) /* handles a V: field in the abc */ int n; char *s; struct voice_params *vp; { if (pastheader || XTEN1) { voicesused = 1; if (pastheader) checkbreak(); v = getvoicecontext(n); addfeature(VOICE, v->indexno, 0, 0); dependent_voice[v->indexno] = 0; if (vp->gotoctave) { event_octave(vp->octave,1); }; if (vp->gottranspose) { addfeature(TRANSPOSE, vp->transpose, 0, 0); }; } else { event_warning("V: in header ignored"); }; } void event_length(n) /* handles an L: field in the abc */ int n; { if (pastheader) { v->default_length = n; } else { global.default_length = n; }; } static void 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; }; }; } void event_tempo(n, a, b, rel, pre, post) /* handles a Q: field e.g. Q: a/b = n or Q: Ca/b = n */ /* strings before and after are ignored */ int n; int a, b, rel; char *pre; char *post; { int t_num, t_denom; int new_div; long new_tempo; int tempo_l, tempo_h; 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); /* split up into short ints */ tempo_l = new_tempo & 0xffff; tempo_h = new_tempo >> 16; new_div = (int) ((float)DIV*(float)new_tempo/(float)tempo + 0.5); addfeature(TEMPO, new_div, tempo_h, tempo_l); } else { Qtempo = n; tempo_num = a; tempo_denom = b; relative_tempo = rel; }; }; }; } void 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; }; }; } void event_octave(num, local) /* used internally by other routines when octave=N is encountered */ /* in I: or K: fields */ int num; { if (dotune) { if (pastheader || local) { v->octaveshift = num; } else { global.octaveshift = num; }; }; } void event_info_key(key, value) char* key; char* value; { int num; #ifdef INFO_OCTAVE_DISABLED return; #endif if (strcmp(key, "octave")==0) { num = readsnumf(value); event_octave(num,0); }; if (strcmp(key, "MIDI") == 0) event_specific(key, value); } static void stack_broken(v) /* store away broken rhythm context on encountering grace notes */ struct voicecontext* v; { v->broken_stack[0] = v->laststart; v->broken_stack[1] = v->lastend; v->broken_stack[2] = v->thisstart; v->broken_stack[3] = v->thisend; v->broken_stack[4] = v->brokentype; v->broken_stack[5] = v->brokenmult; v->broken_stack[6] = v->brokenpending; v->laststart = -1; v->lastend = -1; v->thisstart = -1; v->thisend = -1; v->brokenpending = -1; } static void restore_broken(v) /* remember any broken rhythm context after grace notes */ struct voicecontext* v; { if (v->brokenpending != -1) { event_error("Unresolved broken rhythm in grace notes"); }; v->laststart = v->broken_stack[0]; v->lastend = v->broken_stack[1]; v->thisstart = v->broken_stack[2]; v->thisend = v->broken_stack[3]; v->brokentype = v->broken_stack[4]; v->brokenmult = v->broken_stack[5]; v->brokenpending = v->broken_stack[6]; } void event_graceon() /* a { in the abc */ { if (gracenotes) { event_error("Nested grace notes not allowed"); } else { if (v->inchord) { event_error("Grace notes not allowed in chord"); } else { gracenotes = 1; addfeature(GRACEON, 0, 0, 0); v->ingrace = 1; stack_broken(v); }; }; } void event_graceoff() /* a } in the abc */ { if (!gracenotes) { event_error("} without matching {"); } else { gracenotes = 0; addfeature(GRACEOFF, 0, 0, 0); v->ingrace = 0; restore_broken(v); }; } void event_playonrep(s) char* s; /* [X in the abc, where X is a list of numbers */ { int num, converted; char seps[2]; converted = sscanf(s, "%d%1[,-]", &num, seps); if (converted == 0) { event_error("corrupted variant ending"); } else { if ((converted == 1) && (num != 0)) { addfeature(PLAY_ON_REP, 0, 0, num); } else { textfeature(PLAY_ON_REP, s); }; }; } static void 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"); }; }; }; } void event_sluron(t) /* called when ( is encountered in the abc */ int t; { if (v->inslur) event_warning("Slur within slur"); else { addfeature(SLUR_ON, 0, 0, 0); v->inslur = 1; } } void event_sluroff(t) /* called when ) is encountered */ int t; { if (v->inslur) { /*slurtotie(); [SS] 2005-08-13 */ addfeature(SLUR_OFF, 0, 0, 0); v->inslur = 0; } else event_warning("No slur to close"); } void event_tie() /* a tie - has been encountered in the abc */ { if (feature[notes-1] == CHORDOFF || feature[notes-1] == CHORDOFFEX) { /* did a TIE connect with a chord */ patchup_chordtie(chordstart,notes-1); } else addfeature(TIE, 0, 0, 0); } void event_space() /* space character in the abc is ignored by abc2midi */ { /* ignore */ /* printf("Space event\n"); */ } void event_lineend(ch, n) /* called when \ or ! or * or ** is encountered at the end of a line */ char ch; int n; { /* ignore */ } void 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; }; }; } void 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; }; } void event_chord() /* a + has been encountered in the abc */ { if (v->inchord) { event_chordoff(1,1); } else { event_chordon(dummydecorator); }; } static void 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]); }; } static void 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); }; }; } static void brokenadjust() /* adjust lengths of broken notes */ { int num1, num2, denom12; int j; int failed; switch(v->brokenmult) { case 1: num1 = ratio_b; num2 = ratio_a; 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); }; }; } static void 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; } static void 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; }; }; } static void marknote() /* when handling a single note, not a chord, marknotestart() and */ /* marknoteend() can be called together */ { marknotestart(); marknoteend(); } /* just a stub to ignore 'y' */ void event_spacing(n, m) int n,m; { } void event_rest(decorators,n,m,type) /* rest of n/m in the abc */ int n, m,type; int decorators[DECSIZE]; { int num, denom; num = n; denom = m; if (decorators[FERMATA]) { if (fermata_fixed) addfract(&num,&denom,1,1); else num = num*2; }; if (v == NULL) { event_fatal_error("Internal error : no voice allocated"); }; 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)) { 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(); }; } void event_mrest(n,m) /* multiple bar rest of n/m in the abc */ /* we check for m == 1 in the parser */ int n, m; { int i; int decorators[DECSIZE]; decorators[FERMATA]=0; /* it is not legal to pass a fermata to a multirest */ for (i=0; idefault_length), time_denom,0); if (i != n-1) { event_bar(SINGLE_BAR, ""); }; }; } void event_chordon(int chorddecorators[]) /* handles a chord start [ in the abc */ /* the array chorddecorators is needed in toabc.c and yapstree.c */ /* but is not relevant here. */ { if (v->inchord) { event_error("Attempt to nest chords"); } else { chordstart = notes; addfeature(CHORDON, 0, 0, 0); v->inchord = 1; v->chordcount = 0; v->chord_num = 0; v->chord_denom = 1; marknotestart(); }; } void event_chordoff(int chord_n, int chord_m) /* handles a chord close ] in the abc */ { if (!v->inchord) { event_error("Chord already finished"); } else { if (tuplecount > 0) { chord_n = chord_n * tfact_num; chord_m = chord_m * tfact_denom; if (tnote_num == 0) { tnote_num = chord_n; tnote_denom = chord_m; } else { if (tnote_num * chord_m != chord_n * tnote_denom) { if (!specialtuple) { event_warning("Different length notes in tuple for chord"); }; }; } if ((!gracenotes) && (!v->inchord)) { tuplecount = tuplecount - 1; }; }; if(chord_m == 1 && chord_n == 1) /* chord length not set outside [] */ addfeature(CHORDOFF, 0, v->chord_num, v->chord_denom); else { addfeature(CHORDOFFEX, 0, chord_n*4, chord_m*v->default_length); fix_enclosed_note_lengths(chordstart, notes-1); } v->inchord = 0; v->chordcount = 0; marknoteend(); if (tuplecount > 0) --tuplecount; }; } void event_finger(p) /* a 1, 2, 3, 4 or 5 has been found in a guitar chord field */ char *p; { /* does nothing */ } static int pitchof(note, accidental, mult, octave, propogate_accs) /* This code is used for handling gchords */ /* finds MIDI pitch value for note */ /* if propogate_accs is 1, apply any accidental to all instances of */ /* that note in the bar. If propogate_accs is 0, accidental does not */ /* apply to other notes */ char note, accidental; int mult, octave; int propogate_accs; { int p; char acc; int mul, noteno; static int scale[7] = {0, 2, 4, 5, 7, 9, 11}; char *anoctave = "cdefgab"; p = (int) ((long) strchr(anoctave, note) - (long) anoctave); p = scale[p]; acc = accidental; mul = mult; noteno = (int)note - 'a'; if (acc == ' ' && !microtone ) { /* if microtone do not propogate accidentals to this note. */ acc = v->workmap[noteno][octave+4]; mul = v->workmul[noteno][octave+4]; } else { if ((retain_accidentals) && (propogate_accs)) { v->workmap[noteno][octave+4] = acc; v->workmul[noteno][octave+4] = mul; }; }; if (acc == '^') p = p + mul; if (acc == '_') p = p - mul; return p + 12*octave + middle_c; } static int pitchof_b(note, accidental, mult, octave, propogate_accs,pitchbend) /* computes MIDI pitch for note. If global temperament is set, it will apply a linear temperament and return a pitchbend. If propogate_accs is 1, apply any accidental to all instances of that note in the bar. If propogate_accs is 0, accidental does not apply to other notes */ char note, accidental; int mult, octave; int propogate_accs; int *pitchbend; { int p; char acc; int mul, noteno; int pitch4096,pitch,bend; static int scale[7] = {0, 2, 4, 5, 7, 9, 11}; const int accidental_size = 7*fifth_size - 4*octave_size; const int tscale[7] = { 0, 2*fifth_size-octave_size, 4*fifth_size-2*octave_size, -1*fifth_size+octave_size, fifth_size, 3*fifth_size-octave_size, 5*fifth_size-2*octave_size }; static const char *anoctave = "cdefgab"; acc = accidental; mul = mult; noteno = (int)note - 'a'; if (acc == ' ' && !microtone) { /* if microtone do not propogate accidentals to this note. */ acc = v->workmap[noteno][octave+4]; mul = v->workmul[noteno][octave+4]; } else { if ((retain_accidentals) && (propogate_accs)) { v->workmap[noteno][octave+4] = acc; v->workmul[noteno][octave+4] = mul; }; }; p = (int) ((long) strchr(anoctave, note) - (long) anoctave); if (temperament) { p = tscale[p]; if (acc == '^') p = p + mul*accidental_size; if (acc == '_') p = p - mul*accidental_size; pitch4096 = p + octave*octave_size + middle_c; pitch = (SEMISIZE*128+pitch4096+SEMISIZE/2)/SEMISIZE-128; bend = 8192+4096*(pitch4096 - pitch*SEMISIZE)/SEMISIZE; bend = bend<0?0:(bend>16383?16383:bend); } else { p = scale[p]; if (acc == '^') p = p + mul; if (acc == '_') p = p - mul; pitch = p + 12*octave + middle_c; bend = 8192; /* corresponds to zero bend */ } if (!microtone) *pitchbend = bend; /* don't override microtone */ return pitch; } static void 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; int bend_up,bend_down; char *anoctave = "cdefgab"; upoct = octave; downoct = octave; t = (int) ((long) strchr(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_b(up, v->basemap[(int)up - 'a'], 1, upoct, 0,&bend_up); pitchdown = pitchof_b(down, v->basemap[(int)down - 'a'], 1, downoct, 0,&bend_down); bentpitch[notes] = active_pitchbend; addfeature(NOTE, pitch, n*4, m*(v->default_length)*5); marknotestart(); bentpitch[notes] = bend_up; addfeature(NOTE, pitchup, n*4, m*(v->default_length)*5); bentpitch[notes] = active_pitchbend; addfeature(NOTE, pitch, n*4, m*(v->default_length)*5); bentpitch[notes] = bend_down; addfeature(NOTE, pitchdown, n*4, m*(v->default_length)*5); bentpitch[notes] = active_pitchbend; addfeature(NOTE, pitch, n*4, m*(v->default_length)*5); marknoteend(); } static void dotrill(note, octave, n, m, pitch) /* applies a trill to a note */ char note; int octave, n, m; int pitch; { char up; int i, t; int upoct, pitchup; char *anoctave = "cdefgab"; int a, b, count; int bend; upoct = octave; t = (int) ((long) strchr(anoctave, note) - (long) anoctave); up = *(anoctave + ((t+1) % 7)); if (up == 'c') upoct = upoct + 1; pitchup = pitchof_b(up, v->basemap[(int)up - 'a'], 1, upoct, 0,&bend); a = 4; b = m*(v->default_length); count = n; while ((tempo*a)/((long)b) > 100000L) { count = count*2; b = b*2; }; i = 0; while (i < count) { /*if (i == count - 1) { **bug** [SS] 2006-09-10 */ if (i == 0) { marknotestart(); }; if (i%2 == 0) { bentpitch[notes] = bend; addfeature(NOTE, pitchup, a, b); } else { bentpitch[notes] = active_pitchbend; addfeature(NOTE, pitch, a, b); }; i = i + 1; }; marknoteend(); } void makecut (mainpitch, shortpitch,mainbend,shortbend, n,m) int mainpitch,shortpitch,mainbend,shortbend,n,m; { addfeature(GRACEON, 0, 0, 0); bentpitch[notes] = shortbend; addfeature(NOTE, shortpitch, 4,v->default_length); addfeature(GRACEOFF, 0, 0, 0); bentpitch[notes] = mainbend; addfeature(NOTE, mainpitch, 4*n,m*v->default_length); } static void doornament(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"; int nnorm,mnorm,nn; int bend_up,bend_down; upoct = octave; downoct = octave; t = (int) ((long) strchr(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_b(up, v->basemap[(int)up - 'a'], 1, upoct, 0,&bend_up); pitchdown = pitchof_b(down, v->basemap[(int)down - 'a'], 1, downoct, 0,&bend_down); marknotestart(); /* normalize notelength to L:1/8 */ nnorm =n*8; mnorm =m*(v->default_length); reduce(&nnorm,&mnorm); if (nnorm == 3 && mnorm == 1) /* dotted quarter note treated differently */ { nn = n/3; /* in case L:1/16 or smaller */ if(nn < 1) nn=1; bentpitch[notes] = active_pitchbend; /* [SS] 2006-11-3 */ addfeature(NOTE, pitch, 4*nn,v->default_length); makecut(pitch,pitchup,active_pitchbend,bend_up,nn,m); makecut(pitch,pitchdown,active_pitchbend,bend_down,nn,m); } else makecut(pitch,pitchup,active_pitchbend,bend_up,n,m); marknoteend(); } static void 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; }; } void event_note(decorators, accidental, mult, note, xoctave, n, m) /* handles a note in the abc */ int decorators[DECSIZE]; int mult; char accidental, note; int xoctave, n, m; { int num, denom; int octave; int pitch; int pitch_noacc; int dummy; if (v == NULL) { event_fatal_error("Internal error - no voice allocated"); }; octave = xoctave + v->octaveshift; num = n; denom = m; if (v->inchord) v->chordcount = v->chordcount + 1; if (tuplecount > 0 && !v->inchord) { 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)) { 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)); }; /* linear temperament support */ pitch = pitchof_b(note, accidental, mult, octave, 1,&active_pitchbend); pitch_noacc = pitchof_b(note, 0, 0, octave, 0,&dummy); if (decorators[FERMATA]) { if(fermata_fixed) addfract(&num,&denom,1,1); else num = num*2; }; if (v->chordcount == 1) { v->chord_num = num*4; v->chord_denom = denom*(v->default_length); }; if ((decorators[ROLL]) || (decorators[ORNAMENT]) || (decorators[TRILL])) { if (v->inchord) { event_error("Rolls and trills not supported in chords"); } else { if (decorators[TRILL]) { dotrill(note, octave, num, denom, pitch); } else if (decorators[ORNAMENT]) { doornament(note, octave, num, denom, pitch); } else { doroll(note, octave, num, denom, pitch); }; }; /* end of else block for not in chord */ } /* end of if block for ROLL,ORNAMENT,TRILL */ else { if (decorators[STACCATO] || decorators[BREATH]) { if (v->inchord) { if (v->chordcount == 1) { addfeature(REST, pitch, num*4, denom*(v->default_length)); }; pitchline[notes] = pitch_noacc; bentpitch[notes] = active_pitchbend; addfeature(NOTE, pitch, num*4, denom*2*(v->default_length)); } else { pitchline[notes] = pitch_noacc; bentpitch[notes] = active_pitchbend; addfeature(NOTE, pitch, num*4, denom*2*(v->default_length)); marknotestart(); addfeature(REST, pitch, num*4, denom*2*(v->default_length)); marknoteend(); }; } else { pitchline[notes] = pitch_noacc; bentpitch[notes] = active_pitchbend; addfeature(NOTE, pitch, num*4, denom*(v->default_length)); if (!v->inchord) { marknote(); }; if ((v->inslur) && (!v->ingrace)) { addfeature(SLUR_TIE, 0, 0, 0); }; }; }; } void event_microtone(int dir, int a, int b) { int bend; /* pitchwheel range +/- 2 semitones according to General MIDI specification*/ /* resolution of 14bit -- order of bytes is inverted for pitchbend */ bend = dir*((int)(4096.0*a/b))+8192; /* sorry for the float! */ bend = bend<0?0:(bend>16383?16383:bend); active_pitchbend = bend; microtone=1; } void event_normal_tone() { /* event_specific("MIDI", "pitchbend 0 64"); [SS] 2006-09-30 */ microtone = 0; } char *get_accidental(place, accidental) /* read in accidental - used by event_handle_gchord() */ char *place; /* place in string being parsed */ char *accidental; /* pointer to char variable */ { char *p; p = place; *accidental = '='; if (*p == '#') { *accidental = '^'; p = p + 1; }; if (*p == 'b') { *accidental = '_'; p = p + 1; }; return(p); } void event_handle_gchord(s) /* handler for the guitar chords */ char* s; { int basepitch; char accidental, note; char* p; char name[9]; int i; int chordno; int bassnote; int inversion; if ((*s >= '0') && (*s <= '5')) { event_finger(s); return; }; p = s; if (*p == '(') p = p+1; /* ignore leading ( [SS] 2005-03-19*/ 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 (strchr("_^<>@", (int)*p) == NULL) { event_error("Guitar chord does not start with A-G or a-g"); }; return; }; }; p = get_accidental(p, &accidental); basepitch = pitchof(note, accidental, 1, 0, 0) - middle_c; i = 0; while ((i<9) && (*p != ' ') && (*p != '\0') && (*p != '(') && (*p != '/') && (*p != ')')) { /* also ignore closing parentheses ')' [SS] 2005-03-19*/ name[i] = *p; i = i+1; p = p + 1; }; inversion = -1; if (*p == '/') { p = p + 1; if ((*p >= 'A') && (*p <= 'G')) { note = (int)*p - 'A' + 'a'; p = p + 1; p = get_accidental(p, &accidental); inversion = pitchof(note, accidental, 1, 0, 0) - middle_c; } else if ((*p >= 'a') && (*p <= 'g')) { note = (int)*p; p = p + 1; p = get_accidental(p, &accidental); inversion = pitchof(note, accidental, 1, 0, 0) - middle_c; } else { event_error(" / must be followed by A-G or a-g in gchord"); }; }; name[i] = '\0'; chordno = getchordnumber(name); if (chordno == 0) { char msg[200]; 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->indexno)) { event_warning("More than one voice with guitar chords in"); }; gchordvoice = v->indexno; }; if (bassnote) { chordno = -1; }; addfeature(GCHORD, basepitch, inversion, chordno); } void event_handle_instruction(s) /* handler for ! ! instructions */ /* does ppp pp p mp mf f ff fff */ /* also does !drum! and !nodrum! */ char* s; { char buff[MAXLINE]; char* p; char* q; int done; char midimsg[40]; 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; /* add nofnop ... */ if (nofnop == 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 ((strcmp(p,"crescendo(") == 0) || (strcmp(p,"<(") == 0) || (strcmp(p,"crescendo)") == 0) || (strcmp(p,"<)") == 0)) { sprintf(midimsg,"beatmod %d",velocitychange); event_specific("MIDI", midimsg); done = 1; } if ( (strcmp(p,"diminuendo)") == 0) || (strcmp(p,">)") == 0) || (strcmp(p,"diminuendo(") == 0) || (strcmp(p,">(") == 0)) { sprintf(midimsg,"beatmod -%d",velocitychange); event_specific("MIDI", midimsg); done = 1; } }; /* end nofnop */ if (strcmp(p, "drum") == 0) { addfeature(DRUMON, 0, 0, 0); if ((drumvoice != 0) && (drumvoice != v->indexno)) { event_warning("Implementation limit: drums only supported in one voice"); }; drumvoice = v->indexno; done = 1; }; if (strcmp(p, "nodrum") == 0) { addfeature(DRUMOFF, 0, 0, 0); done = 1; }; if (strcmp(s, "fermata") == 0) { decorators_passback[FERMATA] =1; done = 1; }; if (strcmp(s, "trill") == 0) { decorators_passback[TRILL] =1; done = 1; }; if (strcmp(p, "arpeggio") == 0) { addfeature(ARPEGGIO, 0, 0, 0); done = 1; }; if (strcmp(s, "breath") == 0) { decorators_passback[BREATH] =1; done = 1; }; if (done == 0) { sprintf(buff, "instruction !%s! ignored", s); event_warning(buff); }; } static void 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'] = '_'; } static void 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]; }; }; } static void copymap(v) /* sets up working map at the start of each bar */ struct voicecontext* v; { int i,j; for (i=0; i<7; i++) { for (j=0;j<10;j++) { v->workmap[i][j] = v->basemap[i]; v->workmul[i][j] = v->basemul[i]; } }; } /* workaround for problems with PCC compiler */ /* data may be written to an internal buffer */ int myputc(c) char c; { return (putc(c,fp)); } void addfract(int *xnum, int *xdenom, int a, int b) /* add a/b to the count of units in the bar */ { *xnum = (*xnum)*b + a*(*xdenom); *xdenom = (*xdenom) * b; if (*xnum < 0 && *xdenom < 0) {*xnum = -*xnum; *xdenom = -*xdenom;} reduce(xnum, xdenom); } void nondestructive_readstr(out, in, limit) char out[]; char **in; int limit; /* copy across alphanumeric string */ { int i; i = 0; while ((isalpha(*(*in+i))) && (i < limit-1)) { out[i] = *(*in+i); i = i + 1; }; out[i] = '\0'; } static void dotie(j, xinchord,voiceno) /* called in preprocessing stage to handle ties */ /* we need the voiceno in case a tie is broken by a */ /* voice switch. */ int j, xinchord,voiceno; { int tienote, place; int tietodo, done; int newbar; int lastnote, lasttie; int inchord; int tied_num, tied_denom; int localvoiceno; int samechord; char command[40]; /* find note to be tied */ samechord = 0; newbar =0; /* if 1 it allows pitchline[] == pitchline[] test */ if (xinchord) samechord = 1; tienote = j; localvoiceno = voiceno; while ((tienote > 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; /* fix for tied microtone note */ if (feature[tienote+1] == DYNAMIC) { nondestructive_readstr(command, &atext[pitch[tienote+1]], 40); if (strcmp(command, "pitchbend") == 0) { /*printf("need to remove pitchbend following TNOTE\n"); */ removefeature(tienote+1); j = j-1; } } /* fix for tied microtone note inside a slur */ if (feature[tienote+1] == SLUR_TIE && feature[tienote+2] == DYNAMIC) { nondestructive_readstr(command, &atext[pitch[tienote+2]], 40); if (strcmp(command, "pitchbend") == 0) { /*printf("need to remove pitchbend following TNOTE in slur\n"); */ removefeature(tienote+2); j = j-1; } } num[j] = num[tienote]; denom[j] = denom[tienote]; place = j; tietodo = 1; lasttie = j; tied_num = num[tienote]; tied_denom = denom[tienote]; lastnote = -1; done = 0; while ((place < notes) && (tied_num >=0) && (done == 0)) { /*printf("%d %s %d %d/%d ",place,featname[feature[place]],pitch[place],num[place],denom[place]); */ switch (feature[place]) { case SINGLE_BAR: case BAR_REP: case REP_BAR: if (tietodo) newbar = 1; else newbar=0; break; case NOTE: if(localvoiceno != voiceno) break; lastnote = place; if ((tied_num == 0) && (tietodo == 0)) { done = 1; }; if (((pitchline[place] == pitchline[tienote] && newbar) || (pitch[place] == pitch[tienote])) && (tietodo == 1) && (samechord == 0)) { /* tie in note */ if (tied_num != 0) { event_error("Time mismatch at tie"); }; tietodo = 0; pitch[place] = pitch[tienote]; /* in case accidentals did not propagate */ /* 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] = OLDTIE; }; }; if (inchord == 0) { /* subtract time from tied time */ addfract(&tied_num, &tied_denom, -num[place], denom[place]); }; break; case REST: if(localvoiceno != voiceno) break; 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(localvoiceno != voiceno) break; if (lastnote == -1) { event_error("Bad tie: possibly two ties in a row"); } else { if (pitch[lastnote] == pitch[tienote] && samechord == 0) { lasttie = place; tietodo = 1; if (inchord) samechord = 1; }; }; break; case CHORDON: if(localvoiceno != voiceno) break; inchord = 1; break; case CHORDOFF: case CHORDOFFEX: samechord = 0; if(localvoiceno != voiceno) break; inchord = 0; /* subtract time from tied time */ addfract(&tied_num, &tied_denom, -num[place], denom[place]); break; case VOICE: localvoiceno = pitch[place]; default: break; }; /*printf("tied_num = %d done = %d inchord = %d\n",tied_num, done, inchord);*/ place = place + 1; }; if (tietodo == 1) { event_error("Could not find note to be tied"); }; }; /*printf("dotie finished\n");*/ } static void fix_enclosed_note_lengths(int from, int end) { /* in event that the chord length was set outside [], */ /* we must go back and adjust the note length of each note */ /* inside the chord. */ int j; for (j = from; j < end; j++) { if (feature[j] == NOTE || feature[j] == TNOTE) { num[j] = num[end]; denom[j] =denom[end]; } } } static int patchup_chordtie(int chordstart,int chordend) { int i,tieloc; for (i=chordend;i>=chordstart;i--) { if(feature[i]==NOTE && feature[i+1] != TIE) { insertfeature(TIE,0,0,0,i+1); tieloc = i+1; } } return tieloc; } static void tiefix() /* connect up tied notes and cleans up the */ /* note lengths in the chords (eg [ace]3 ) */ { int j; int inchord; int chord_num, chord_denom; int chord_start,chord_end; int voiceno; j = 0; inchord = 0; voiceno = 1; while (j 0) { event_playonrep(replist); }; if (splitdepth > 0) { /* we encountered the repeat while in a split voice; the repeat goes there but we need to also copy it to all its ancestors. */ if (type != SINGLE_BAR) recurse_back_and_change_bar (type); else recurse_back_to_original_voice(); } /* depth == 0 implies the repeat symbol was encountered while we are not in a split voice but we need to put repeat symbol in all split voices */ else { voiceno = v->voiceno; indexno = v->indexno; while (v->tosplitno != -1) { v = getvoicecontext(v->tosplitno); splitdepth++; addfeature(VOICE, v->indexno, 0, 0); sync_voice (v,0,0); /*addfeature(newtype,0 ,0, 0); sync_voice does this*/ if (strlen(replist) > 0) event_playonrep(replist); } if (v->fromsplitno != -1 || splitdepth >0) recurse_back_to_original_voice(); } } static void delendrep(j) int j; /* remove bogus repeat */ { event_error("spurious repeat after second ending"); switch(feature[j]) { case REP_BAR: feature[j] = DOUBLE_BAR; event_warning("replacing :| with double bar ||"); break; case DOUBLE_REP: feature[j] = BAR_REP; event_warning("replacing :: with |:"); break; default: break; }; } static void placeendrep(j) /* patch up missing repeat */ int j; { if (quiet == -1) event_warning("Assuming repeat"); switch(feature[j]) { case DOUBLE_BAR: feature[j] = REP_BAR; break; case SINGLE_BAR: feature[j] = REP_BAR; break; case BAR_REP: feature[j] = DOUBLE_REP; event_warning("replacing with double repeat (::)"); break; default: event_error("Internal error - please report"); break; }; } static void placestartrep(j) /* patch up missing repeat */ int j; { if (quiet == -1) 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: if (quiet == -1) event_warning("replacing |: with double repeat (::)"); feature[j] = DOUBLE_REP; break; case BAR_REP: if (quiet == -1) event_error("Too many end repeats"); break; case DOUBLE_REP: if (quiet == -1) event_error("Too many end repeats"); break; default: event_error("Internal error - please report"); break; }; } static void fixreps() /* find and correct missing repeats in music */ /* abc2midi places an extra || at the start of the music */ /* This can be converted to |: if necessary. */ { int j; int rep_point; /* where to assume a repeat starts */ int expect_repeat; /* = 1 after |: or :: otherwise 0*/ int use_next; /* if 1 set next bar line as ref_point */ int nplays; /* counts PLAY_ON_REP associated with a BAR_REP*/ expect_repeat = 0; use_next = 0; nplays=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: /* |: */ /* if |: .. |: encountered. Report error and change second |: to :: */ if (expect_repeat) { if (quiet == -1) event_error(" found another |: after a |:"); placeendrep(j); }; expect_repeat = 1; use_next = 0; break; case REP_BAR: /* :| */ /* if :| encountered before |: place "|:" at reference point */ if ((!expect_repeat) && (nplays < 1)) { placestartrep(rep_point); }; expect_repeat = 0; rep_point = j; use_next = 0; break; case PLAY_ON_REP: /* [1 or [2 or [1,3 etc */ if(nplays == 0 && !expect_repeat) { if (quiet == -1) event_error(" found [1 or like before |:"); placestartrep(rep_point); } nplays++; break; case DOUBLE_REP: /* :: */ if (!expect_repeat) { if (quiet == -1) event_error(" found :: before |:"); placestartrep(rep_point); }; expect_repeat = 1; break; default: break; }; j = j + 1; }; } static void 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); global.octaveshift = 0; global.keyset = 0; voicecount = 0; head = NULL; for (j=0;j<64;j++) vaddr[j]=NULL; v = NULL; got_titlename = 0; time_num = 4; time_denom = 4; mtime_num = 4; mtime_denom = 4; timesigset = 0; barchecking = 1; global.default_length = -1; event_tempo(default_tempo, 1, 4, 0, NULL, NULL); notes = 0; ntexts = 0; gfact_num = 1; gfact_denom = 4; hornpipe = 0; karaoke = 0; numsplits = 0; retain_accidentals = default_retain_accidentals; fermata_fixed = default_fermata_fixed; if (ratio_standard == -1) { ratio_a = default_ratio_a; ratio_b = default_ratio_b; } else { ratio_a = 2; ratio_b = 6; } wcount = 0; parts = -1; middle_c = default_middle_c; for (j=0; j<26; j++) { part_start[j] = -1; }; headerpartlabel = 0; initvstring(&part); for (j=0; j<16;j++) { channels[j] = 0; }; set_gchords("z"); gchordvoice = 0; set_drums("z"); drumvoice = 0; wordvoice = 0; } void 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"); }; if ((time_num == 12) && (time_denom == 8)) { set_gchords("fzcfzcfzcfzc"); }; } static void 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; struct voicecontext *p; if (headerpartlabel == 1) { part_start[(int)part.st[0] - (int)'A'] = notes; addfeature(PART, part.st[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; }; }; /* ensure default_length is defined for all voices */ p = head; while ((p != NULL)) { if (p->default_length == -1) p->default_length=global.default_length; p = p->next; }; 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; voicesused = 0; } void event_key(sharps, s, modeindex, modmap, modmul, gotkey, gotclef, clefname, octave, transpose, gotoctave, gottranspose) /* handles a K: field */ int sharps; /* sharps is number of sharps in key signature */ int modeindex; /* 0 major, 1,2,3 minor, 4 locrian, etc. */ 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) */ int gotkey, gotclef; int octave, transpose, gotoctave, gottranspose; char* clefname; { int minor; minor =0; if (modeindex >0 && modeindex <4) minor = 1; if ((dotune) && gotkey) { if (pastheader) { setmap(sharps, v->basemap, v->basemul); altermap(v, modmap, modmul); copymap(v); addfeature(KEY, sharps, 0, minor); if (gottranspose) { addfeature(TRANSPOSE, transpose, 0, 0); }; } else { if (gottranspose) { addfeature(GTRANSPOSE, transpose, 0, 0); }; setmap(sharps, global.basemap, global.basemul); altermap(&global, modmap, modmul); global.keyset=1; copymap(&global); sf = sharps; mi = minor; headerprocess(); v = getvoicecontext(1); }; if (gotoctave) { event_octave(octave,0); }; }; } static void finishfile() /* end of tune has been reached - write out MIDI file */ { extern int nullputc(); complete_all_split_voices (); clearvoicecontexts(); init_drum_map(); if (!pastheader) { event_error("No valid 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) && (drumvoice == 0) && (dronevoice==0)) { ntracks = 1; } else { ntracks = voicecount + karaoke + 1; if (gchordvoice != 0) { gchordtrack = ntracks; ntracks = ntracks + 1; }; if (drumvoice != 0) { drumtrack = ntracks; ntracks = ntracks + 1; }; if (dronevoice != 0) { dronetrack = ntracks; ntracks = ntracks + 1; }; }; if (check) { Mf_putc = nullputc; if (ntracks == 1) { writetrack(0); } else { for (i=0; iindexno] = v; pastheader = 0; if (userfilename == 0) { if (outname != NULL) { free(outname); }; sprintf(numstr, "%d", n); if ( (int) strlen(numstr) > namelimit - 1) { numstr[namelimit - 1] = '\0'; }; if ((int) (strlen(outbase) + strlen(numstr)) > namelimit) { strncpy(newname, outbase, namelimit - strlen(numstr)); strcpy(&newname[namelimit - strlen(numstr)], numstr); strcpy(&newname[strlen(newname)], ".mid"); } else { sprintf(newname, "%s%s.mid", outbase, numstr); }; outname = addstring(newname); /* outname = (char*)checkmalloc(strlen(outbase) + 22 + strlen(".mid")); */ /* sprintf(outname, "%s%d.mid", outbase, n); */ }; startfile(); }; } void event_eof() /* end of abc file encountered */ { if (dotune) { dotune = 0; parseroff(); finishfile(); }; if (verbose) { printf("End of File reached\n"); }; free(pitch); free(pitchline); free(bentpitch); free(num); free(denom); free(feature); free(words); free(outname); free(outbase); } int main(argc,argv) int argc; char *argv[]; { char *filename; int i; oldchordconvention = 0; /* for handling +..+ chords */ for (i=0;i #ifdef ANSILIBS #include #endif #include "abc.h" #include "structs.h" #include "sizes.h" extern struct tune thetune; extern double scaledwidth; static void addfract(f, n, m) struct fract* f; int n, m; /* add n/m to fraction pointed to by f */ /* like addunits(), but does not use unitlength */ { f->num = n*f->denom + m*f->num; f->denom = m*f->denom; reducef(f); } static void mulfract(f, n, m) struct fract* f; int n, m; /* multiply n/m to fraction pointed to by f */ /* like addunits(), but does not use unitlength */ { f->num = n*f->num; f->denom = m*f->denom; reducef(f); } static void advance(struct voice* v, int phase, int* items, double* itemspace, double x) /* move on one symbol in the specified voice */ { struct feature* p; struct rest* arest; struct note* anote; struct fract tuplefactor, notelen; int done; int stepon; int zerotime, newline; switch(phase) { case 1: zerotime = 1; newline=0; break; case 2: zerotime = 0; newline=0; break; case 3: zerotime = 0; newline=1; break; default: printf("Internal error: phase = %d\n", phase); exit(1); break; }; *itemspace = 0.0; *items = 0; p = v->place; if (p == NULL) { v->atlineend = 1; }; done = 0; while ((p != NULL) && (done==0)) { p->x = (float) (x + p->xleft); stepon = 1; switch(p->type) { case MUSICLINE: v->inmusic = 1; break; case PRINTLINE: v->inmusic = 0; done = 1; if (!newline) { v->atlineend = 1; stepon = 0; }; break; case CLEF: case KEY: case TIME: if (!v->inmusic) { break; }; case SINGLE_BAR: case DOUBLE_BAR: case BAR_REP: case REP_BAR: case BAR1: case REP_BAR2: case DOUBLE_REP: case THICK_THIN: case THIN_THICK: *itemspace = *itemspace + p->xleft + p->xright; *items = *items + 1; if (!newline) { done = 1; }; break; case REST: case NOTE: tuplefactor = v->tuplefactor; if ((zerotime==1) && (!v->ingrace)) { done = 1; stepon = 0; } else { if ((!v->inchord)&&(!newline)) { done = 1; }; *itemspace = *itemspace + p->xleft + p->xright; *items = *items + 1; if (p->type == REST) { arest = p->item; addfract(&v->time, arest->len.num, arest->len.denom); }; if ((p->type == NOTE) && (!v->ingrace)) { anote = p->item; notelen = anote->len; if (anote->tuplenotes >0) { mulfract(¬elen,tuplefactor.num,tuplefactor.denom); } addfract(&v->time, notelen.num, notelen.denom); /* printf("%c %d/%d %d/%d\n",anote->pitch,notelen.num,notelen.denom, v->time.num,v->time.denom); */ }; }; break; case CHORDON: if ((zerotime==1)&&(!v->ingrace)) { done = 1; stepon = 0; } else { v->inchord = 1; }; break; case CHORDOFF: if (v->inchord == 1) { v->inchord = 0; if ((!v->ingrace)&&(!newline)) { done = 1; }; }; break; case GRACEON: v->ingrace = 1; break; case GRACEOFF: v->ingrace = 0; break; default: break; }; if (stepon) { p = p->next; } else { done = 1; }; }; v->place = p; if (p == NULL) { v->atlineend = 1; }; } static int gefract(struct fract* a, struct fract* b) /* compare two fractions a greater than or equal to b */ /* returns (a >= b) */ { if ((a->num*b->denom) >= (b->num*a->denom)) { return(1); } else { return(0); }; } static int gtfract(struct fract* a, struct fract* b) /* compare two fractions a greater than b */ /* returns (a > b) */ { if ((a->num*b->denom) > (b->num*a->denom)) { return(1); } else { return(0); }; } static int spacemultiline(struct fract* mastertime, struct tune* t) /* calculate spacing for one line (but possibly multiple voices) */ { int i; int items, thisitems, maxitems; int totalitems; double thiswidth, maxwidth; double totalwidth; double x, gap; int done; struct voice* v; struct fract minlen; /* two passes - on the second pass, inter-symbol spacing is */ /* known so elements can be given their correct x position */ gap = 0.0; for (i=0; i<2; i++) { setfract(mastertime, 0, 1); v = firstitem(&t->voices); while (v != NULL) { v->place = v->lineplace; v->ingrace = 0; v->atlineend = 0; setfract(&v->time, 0, 1); v = nextitem(&t->voices); }; done = 0; items = 0; x = 0.0; totalitems = 0; totalwidth = 0.0; /* count up items in a line */ while (done == 0) { maxitems = 0; maxwidth = 0.0; /* first do zero-time symbols */ v = firstitem(&t->voices); while (v != NULL) { if ((!v->atlineend)&&(gefract(mastertime, &v->time))) { advance(v, 1, &thisitems, &thiswidth, x); if (thisitems > maxitems) { maxitems = thisitems; }; if (thiswidth > maxwidth) { maxwidth = thiswidth; }; }; v = nextitem(&t->voices); }; if (maxitems == 0) { /* now try moving forward in time */ /* advance all voices at or before mastertime */ v = firstitem(&t->voices); while (v != NULL) { if ((!v->atlineend)&&(gefract(mastertime, &v->time))) { advance(v, 2, &thisitems, &thiswidth, x); if (thisitems > maxitems) { maxitems = thisitems; }; if (thiswidth > maxwidth) { maxwidth = thiswidth; }; }; v = nextitem(&t->voices); }; /* calculate new mastertime */ v = firstitem(&t->voices); setfract(&minlen, 0, 1); done = 1; while (v != NULL) { if (!v->atlineend) { done = 0; if (minlen.num == 0) { setfract(&minlen, v->time.num, v->time.denom); } else { if (gtfract(&minlen, &v->time)) { setfract(&minlen, v->time.num, v->time.denom); }; }; }; v = nextitem(&t->voices); }; setfract(mastertime, minlen.num, minlen.denom); }; totalitems = totalitems + maxitems; totalwidth = totalwidth + maxwidth; if (maxitems > 0) { x = x + maxwidth + gap; }; }; /* now calculate inter-symbol gap */ if (totalitems > 1) { gap = (scaledwidth - totalwidth)/(totalitems-1); } else { gap = 1.0; }; if (gap < 0.0) { event_error("Overfull music line"); }; if (gap > MAXGAP) { event_error("Underfull music line"); gap = MAXGAP; }; }; if (totalitems == 0) { return(1); } else { return(0); }; } void spacevoices(struct tune* t) { struct fract mastertime; int donelines; struct voice* v; int items; double x1; /* initialize voices */ v = firstitem(&t->voices); while (v != NULL) { v->lineplace = v->first; v->inmusic=0; v = nextitem(&t->voices); }; donelines = 0; while(donelines == 0) { donelines = spacemultiline(&mastertime, t); v = firstitem(&t->voices); while (v != NULL) { v->lineplace = v->place; advance(v, 3, &items, &x1, 0.0); v->lineplace = v->place; v = nextitem(&t->voices); }; }; } static int spaceline(struct voice* v) /* allocate spare space across the width of a single stave line */ /* thereby fixing the x position of all notes and other elements */ /* returns 0 when the end of the voice is reached, 1 otherwise */ { struct feature* p; double x, lastx; int inmusic, items; double itemspace; double gap; itemspace = 0.0; items = 0; inmusic = 0; p = v->place; while ((p != NULL) && (p->type != PRINTLINE)) { switch(p->type) { case MUSICLINE: inmusic = 1; break; case PRINTLINE: inmusic = 0; break; case CLEF: case KEY: case TIME: if (!inmusic) { break; }; case SINGLE_BAR: case DOUBLE_BAR: case BAR_REP: case REP_BAR: case BAR1: case REP_BAR2: case DOUBLE_REP: case THICK_THIN: case THIN_THICK: case REST: case NOTE: itemspace = itemspace + p->xleft + p->xright; items = items + 1; break; default: break; }; p = p->next; }; if (items > 1) { gap = (scaledwidth - itemspace)/((double)(items-1)); } else { gap = 1.0; }; if (gap < 0.0) { event_error("Overfull music line"); }; if (gap > MAXGAP) { event_error("Underfull music line"); gap = MAXGAP; }; /* now assign positions */ x = 0.0; p = v->place; inmusic = 0; while ((p != NULL) && (p->type != PRINTLINE)) { switch(p->type) { case MUSICLINE: inmusic = 1; break; case PRINTLINE: inmusic = 0; break; case CHORDNOTE: p->x = (float) lastx; break; case CLEF: case KEY: case TIME: if (!inmusic) { break; }; case SINGLE_BAR: case DOUBLE_BAR: case BAR_REP: case REP_BAR: case BAR1: case REP_BAR2: case DOUBLE_REP: case THICK_THIN: case THIN_THICK: case REST: case NOTE: x = x + p->xleft; p->x = (float) x; lastx = x; x = x + p->xright + gap; break; default: break; }; p = p->next; }; while ((p!=NULL)&&((p->type == PRINTLINE)||(p->type==LINENUM))) { p = p->next; }; v->place = p; if (p == NULL) { return(0); } else { return(1); }; } void monospace(struct tune* t) { int doneline; struct voice* v; v = firstitem(&t->voices); while (v != NULL) { doneline = 1; v->place = v->first; while (doneline == 1) { doneline = spaceline(v); }; v = nextitem(&t->voices); }; } abcmidi-20070318/legacy_code/0000775000175000017500000000000010576505314015136 5ustar anselmanselmabcmidi-20070318/legacy_code/casecmp.c0000664000175000017500000000225110507010640016700 0ustar anselmanselmstatic int casecmp(s1, s2) /* case-insensitive compare 2 strings */ /* return 0 if equal */ /* 1 if s1 > s2 */ /* -1 if s1 > s2 */ char s1[]; char s2[]; { int i, val, done; char c1, c2; i = 0; done = 0; while (done == 0) { c1 = tolower(s1[i]); c2 = tolower(s2[i]); if (c1 > c2) { val = 1; done = 1; } else { if (c1 < c2) { val = -1; done = 1; } else { if (c1 == '\0') { val = 0; done = 1; } else { i = i + 1; }; }; }; }; return(val); } static int stringcmp(s1, s2) /* case sensitive compare 2 strings */ /* return 0 if equal */ /* 1 if s1 > s2 */ /* -1 if s1 > s2 */ char s1[]; char s2[]; { int i, val, done; i = 0; done = 0; while (done == 0) { if (s1[i] > s2[i]) { val = 1; done = 1; } else { if (s1[i] < s2[i]) { val = -1; done = 1; } else { if (s1[i] == '\0') { val = 0; done = 1; } else { i = i + 1; }; }; }; }; return(val); } abcmidi-20070318/midifile.h0000664000175000017500000000757710507010640014636 0ustar anselmanselm/* definitions for MIDI file parsing code */ #include extern int (*Mf_getc)(); extern void (*Mf_header)(); extern void (*Mf_trackstart)(); extern void (*Mf_trackend)(); extern void (*Mf_noteon)(); extern void (*Mf_noteoff)(); extern void (*Mf_pressure)(); extern void (*Mf_parameter)(); extern void (*Mf_pitchbend)(); extern void (*Mf_program)(); extern void (*Mf_chanpressure)(); extern void (*Mf_sysex)(); extern void (*Mf_metamisc)(); extern void (*Mf_seqspecific)(); extern void (*Mf_seqnum)(); extern void (*Mf_text)(); extern void (*Mf_eot)(); extern void (*Mf_timesig)(); extern void (*Mf_smpte)(); extern void (*Mf_tempo)(); extern void (*Mf_keysig)(); extern void (*Mf_arbitrary)(); extern void (*Mf_error)(); extern long Mf_currtime; extern int Mf_nomerge; /* definitions for MIDI file writing code */ extern int (*Mf_putc)(); extern long (*Mf_writetrack)(); extern int (*Mf_writetempotrack)(); float mf_ticks2sec(); long mf_sec2ticks(); void mfwrite(); void mfread(); static int readtrack(); int mf_write_meta_event(); int mf_write_midi_event(); void mf_write_tempo(); static void readheader(); void mferror(); static void badbyte(); static void metaevent(); static void sysex(); static void chanmessage(); static void msginit(); static void msgadd(); static void biggermsg(); static void mf_write_track_chunk(); static void mf_write_header_chunk(); static void WriteVarLen(); static void write32bit(); static void write16bit(); static int msgleng(); static int eputc(); /* MIDI status commands most significant bit is 1 */ #define note_off 0x80 #define note_on 0x90 #define poly_aftertouch 0xa0 #define control_change 0xb0 #define program_chng 0xc0 #define channel_aftertouch 0xd0 #define pitch_wheel 0xe0 #define system_exclusive 0xf0 #define delay_packet (1111) /* 7 bit controllers */ #define damper_pedal 0x40 #define portamento 0x41 #define sostenuto 0x42 #define soft_pedal 0x43 #define general_4 0x44 #define hold_2 0x45 #define general_5 0x50 #define general_6 0x51 #define general_7 0x52 #define general_8 0x53 #define tremolo_depth 0x5c #define chorus_depth 0x5d #define detune 0x5e #define phaser_depth 0x5f /* parameter values */ #define data_inc 0x60 #define data_dec 0x61 /* parameter selection */ #define non_reg_lsb 0x62 #define non_reg_msb 0x63 #define reg_lsb 0x64 #define reg_msb 0x65 /* Standard MIDI Files meta event definitions */ #define meta_event 0xFF #define sequence_number 0x00 #define text_event 0x01 #define copyright_notice 0x02 #define sequence_name 0x03 #define instrument_name 0x04 #define lyric 0x05 #define marker 0x06 #define cue_point 0x07 #define channel_prefix 0x20 #define end_of_track 0x2f #define set_tempo 0x51 #define smpte_offset 0x54 #define time_signature 0x58 #define key_signature 0x59 #define sequencer_specific 0x74 /* Manufacturer's ID number */ #define Seq_Circuits (0x01) /* Sequential Circuits Inc. */ #define Big_Briar (0x02) /* Big Briar Inc. */ #define Octave (0x03) /* Octave/Plateau */ #define Moog (0x04) /* Moog Music */ #define Passport (0x05) /* Passport Designs */ #define Lexicon (0x06) /* Lexicon */ #define Tempi (0x20) /* Bon Tempi */ #define Siel (0x21) /* S.I.E.L. */ #define Kawai (0x41) #define Roland (0x42) #define Korg (0x42) #define Yamaha (0x43) /* miscellaneous definitions */ #define MThd 0x4d546864 #define MTrk 0x4d54726b #define lowerbyte(x) ((unsigned char)(x & 0xff)) #define upperbyte(x) ((unsigned char)((x & 0xff00)>>8)) abcmidi-20070318/genmidi.c0000664000175000017500000017730310547773136014502 0ustar anselmanselm/* * abc2midi - program to convert abc files to MIDI files. * Copyright (C) 1999 James Allwright * e-mail: J.R.Allwright@westminster.ac.uk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* genmidi.c * This is the code for generating MIDI output from the stored music held * in arrays feature, num, denom, pitch. The top-level routine is * writetrack(). This file is part of abc2midi. * * 14th January 1999 * James Allwright * */ /* for Microsoft Visual C++ Ver 6 and higher */ #ifdef _MSC_VER #define ANSILIBS #endif #include "abc.h" #include "parseabc.h" #include "queues.h" #include "genmidi.h" #include "midifile.h" #include #ifdef ANSILIBS #include #include #endif /* define USE_INDEX if your C libraries have index() instead of strchr() */ #ifdef USE_INDEX #define strchr index #endif #include void single_note_tuning_change(int midikey, float midipitch); void addfract(int *xnum, int *xdenom, int a, int b); float ranfrac () { return rand()/(float) RAND_MAX; } void setbeat(); static void parse_drummap(char **s); /* global variables grouped roughly by function */ extern int lineno; /* source line being parsed */ extern char** atext; /* Named guitar chords */ extern int chordnotes[MAXCHORDNAMES][6]; extern int chordlen[MAXCHORDNAMES]; /* general purpose storage structure */ /* these 6 arrays are used to hold the tune data */ extern int *pitch, *num, *denom; extern int *bentpitch; extern featuretype *feature; extern int notes; extern int verbose; extern int quiet; extern int sf, mi; extern int gchordvoice, wordvoice, drumvoice, dronevoice; extern int gchordtrack, drumtrack, dronetrack; /* Part handling */ extern struct vstring part; int parts, partno, partlabel; int part_start[26], part_count[26]; long introlen, lastlen, partlen[26]; int partrepno; /* int additive; not supported any more [SS] 2004-10-08*/ int err_num, err_denom; extern int voicesused; extern int dependent_voice[]; /* Tempo handling (Q: field) */ extern long tempo; extern int time_num, time_denom; /* time sig. for the tune */ extern int mtime_num, mtime_denom; /* current time sig. when generating MIDI */ int div_factor; int division = DIV; long delta_time; /* time since last MIDI event */ long tracklen, tracklen1; /* output file generation */ extern int ntracks; /* bar length checking */ int bar_num, bar_denom, barno, barsize; int b_num, b_denom; extern int barchecking; /* time signature after header processed */ extern int header_time_num,header_time_denom; /* generating MIDI output */ int beat; int loudnote, mednote, softnote; int beataccents; int velocity_increment = 10; /* for crescendo and decrescendo */ char beatstring[100]; int nbeats; int channel, program; #define MAXCHANS 16 int channels[MAXCHANS+3]; int current_pitchbend[MAXCHANS]; int current_program[MAXCHANS]; int transpose; int global_transpose=0; int chordchannels[10]; /* for handling in voice chords and microtones */ int nchordchannels = 0; /* karaoke handling */ extern int karaoke, wcount; int kspace; char* wordlineptr; extern char** words; int thismline, thiswline, windex, thiswfeature; int wordlineplace; int nowordline; int waitforbar; int wlineno, syllcount; int lyricsyllables, musicsyllables; /* the following are booleans to select features in current track */ int wordson, noteson, gchordson, temposon, drumson, droneon; /* Generating accompaniment */ int gchords, g_started; int basepitch, inversion, chordnum; int gchordnotes[6],gchordnotes_size; 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; struct dronestruct { int chan; /* MIDI channel assigned to drone */ int event; /* stores time in MIDI pulses when last drone event occurred*/ int prog; /* MIDI program (instrument) to use for drone */ int pitch1;/* MIDI pitch of first drone tone */ int vel1; /* MIDI velocity (loudness) of first drone tone */ int pitch2;/* MIDI pitch of second drone tone */ int vel2; /* MIDI velocity (loudnress) of second drone tone */ } drone; /* Generating drum track */ int drum_num, drum_denom; char drum_seq[40]; int drum_len[40]; int drum_velocity[40], drum_program[40]; int drum_ptr, drum_on; int notecount=0; /* number of notes in a chord [ABC..] */ int notedelay=10; /* time interval in MIDI ticks between */ /* start of notes in chord */ int chordattack=0; int staticnotedelay=10; /* introduced to handle !arpeggio! */ int staticchordattack=0; int totalnotedelay=0; /* total time delay introduced */ int trim=1; /* to add a silent gap to note */ int trim_num = 1; int trim_denom = 5; /* channel 10 drum handling */ int drum_map[256]; void 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; } int gtfract(anum,adenom, bnum,bdenom) /* compare two fractions anum/adenom > bnum/bdenom */ /* returns (a > b) */ { if ((anum*bdenom) > (bnum*adenom)) { return(1); } else { return(0); }; } void 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); } void configure_gchord() /* creates a list of notes to played as chord for * a specific guitar chord. Most of the code figures out * how to order the notes when inversions are encountered. */ { int j; int inchord, note; gchordnotes_size = 0; inchord = 0; if (inversion != -1) { /* try to match inversion with basepitch+chordnotes.. */ for (j=0; j basepitch)) { inversion = inversion - 12; gchordnotes[gchordnotes_size] = inversion+gchord.base; gchordnotes_size++; }; }; for (j=0; j= '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 = mtime_num * 4; g_denom = mtime_denom * seq_len; reduce(&g_num, &g_denom); /* printf("%s %d %d\n",s,g_num,g_denom); */ } void set_drums(s) char* s; /* set up a string which indicates drum pattern */ /* called from dodeferred() */ { int seq_len, count, drum_hits; char* p; int i, j, place; p = s; count = 0; drum_hits = 0; seq_len = 0; while (((*p == 'z') || (*p == 'd')) && (count<39)) { if (*p == 'd') { drum_hits = drum_hits + 1; }; drum_seq[count] = *p; p = p + 1; if ((*p >= '0') && (*p <= '9')) { drum_len[count] = readnump(&p); } else { drum_len[count] = 1; }; seq_len = seq_len + drum_len[count]; count = count + 1; }; drum_seq[count] = '\0'; if (seq_len == 0) { event_error("Bad drum sequence"); drum_seq[0] = 'z'; drum_len[0] = 1; seq_len = 1; }; if (count == 39) { event_error("Drum sequence string too long"); }; /* look for program and velocity specifiers */ for (i = 0; i 127) { event_error("Drum program must be in the range 0-127"); } else { drum_program[place] = j; }; place = place + 1; } else { if (i < 2*count) { if (i == drum_hits) { place = 0; }; while (drum_seq[place] != 'd') { place = place + 1; }; if ((j < 1) || (j > 127)) { event_error("Drum velocity must be in the range 1-127"); } else { drum_velocity[place] = j; }; place = place + 1; }; }; i = i + 1; skipspace(&p); }; if (i > 2*drum_hits) { event_error("Too many data items for drum sequence"); }; /* work out unit delay in 1/4 notes*/ drum_num = mtime_num * 4; drum_denom = mtime_denom * seq_len; reduce(&drum_num, &drum_denom); } static void 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) { strcat(msg, " in repeat"); }; if (quiet == -1) 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); }; if (drumson) { drum_ptr = 0; addtoQ(0, drum_denom, -1, drum_ptr, 0); }; } static void softcheckbar(pass) /* allows repeats to be in mid-bar */ int pass; { if (barchecking) { if ((bar_num-barsize*(bar_denom) >= 0) || (barno <= 0)) { checkbar(pass); }; }; } static void 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; } static void 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]; } static int findchannel() /* work out next available channel */ { int j; j = 0; while ((j= MAXCHANS) { event_error("Too many channels required"); j = 0; }; channels[j] = 1; return (j); } static void fillvoice(partno, xtrack, voice) /* check length of this voice at the end of a part */ /* if it is zero, extend it to the correct length */ int partno, xtrack, voice; { char msg[100]; long now; now = tracklen + delta_time; if (partlabel == -1) { if (xtrack == 1) { introlen = now; } else { if (now == 0) { delta_time = delta_time + introlen; now = introlen; } else { if (now != introlen) { sprintf(msg, "Time 0-%ld voice %d, has length %ld", introlen, voice, now); event_error(msg); }; }; }; } else { if (xtrack == 1) { partlen[partlabel] = now - lastlen; } else { if (now - lastlen == 0) { delta_time = delta_time + partlen[partlabel]; now = now + partlen[partlabel]; } else { if (now - lastlen != partlen[partlabel]) { sprintf(msg, "Time %ld-%ld voice %d, part %c has length %ld", lastlen, lastlen+partlen[partlabel], voice, (char) (partlabel + (int) 'A'), now-lastlen); event_error(msg); }; }; }; }; lastlen = now; } static int findpart(j) int j; /* find out where next part starts and update partno */ { int place; place = j; partno = partno + 1; if (partno < parts) { partlabel = (int)part.st[partno] - (int)'A'; } while ((partno < parts) && (part_start[partlabel] == -1)) { event_error("Part not defined"); partno = partno + 1; if (partno < parts) { partlabel = (int)part.st[partno] - (int)'A'; } }; if (partno >= parts) { place = notes; } else { partrepno = part_count[partlabel]; part_count[partlabel]++; place = part_start[partlabel]; }; if (verbose) { if (partno < parts) { printf("Doing part %c number %d of %d\n", part.st[partno], partno, parts); }; }; return(place); } static int partbreak(xtrack, voice, place) /* come to part label in note data - check part length, then advance to */ /* next part if there was a P: field in the header */ int xtrack, voice, place; { int newplace; newplace = place; if (dependent_voice[voice]) return newplace; if (xtrack > 0) { fillvoice(partno, xtrack, voice); }; if (parts != -1) { /* go to next part label */ newplace = findpart(newplace); }; partlabel = (int) pitch[newplace] - (int)'A'; return(newplace); } static int findvoice(initplace, voice, xtrack) /* find where next occurence of correct voice is */ int initplace; int voice, xtrack; { int foundvoice; int j; foundvoice = 0; j = initplace; while ((j < notes) && (foundvoice == 0)) { if (feature[j] == PART) { j = partbreak(xtrack, voice, j); 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); } static void 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; } static void karaokestarttrack (track) int track; /* header information for karaoke track based on w: fields */ { int j; int done; char atitle[200]; /* * Print Karaoke file headers in track 0. * @KMIDI KARAOKE FILE - Karaoke midi file marker) */ if (track == 0) { text_data("@KMIDI KARAOKE FILE"); } /* * Name track 2 "Words" for the lyrics track. * @LENGL - language * Print @T information. * 1st @T line signifies title. * 2nd @T line signifies author. * 3rd @T line signifies copyright. */ if (track == 2) { mf_write_meta_event(0L, sequence_name, "Words", 5); kspace = 0; text_data("@LENGL"); strcpy(atitle, "@T"); } else /* * Write name of song as sequence name in track 0 and as track 1 name. * Print general information about the file using @I marker. * Add to tracks 0 and 1 for various Karaoke (and midi) players to find. */ strcpy(atitle, "@I"); j = 0; done = 3; while ((j < notes) && (done > 0)) { j = j+1; if (feature[j] == TITLE) { if (track != 2) mf_write_meta_event(0L, sequence_name, atext[pitch[j]], strlen (atext[pitch[j]])); strcpy(atitle+2, atext[pitch[j]]); text_data(atitle); done--; } if (feature[j] == COMPOSER) { strcpy(atitle+2, atext[pitch[j]]); text_data(atitle); done--; } if (feature[j] == COPYRIGHT) { strcpy(atitle+2, atext[pitch[j]]); text_data(atitle); done--; } } } static 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); } static int getword(place, w) /* picks up next syllable out of w: field. * It strips out all the control codes ~ - _ * in the * words and sends each syllable out to the Karoake track. * Using the place variable, it loops through each character * in the word until it encounters a space or next control * code. The syllstatus variable controls the loop. After, * the syllable is sent, it then positions the place variable * to the next syllable or control code. * inword --> grabbing the characters in the syllable and * putting them into syllable for output. * postword --> finished grabbing all characters * foundnext--> ready to repeat process for next syllable * empty --> between syllables. * * The variable i keeps count of the number of characters * inserted into the syllable[] char for output to the * karoake track. The kspace variables signals that a * space was encountered. */ 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); } static void 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. The function is called * for each note encountered in feature[j] when the global variable * wordson is set. The function keeps count of the number of notes * in the music and words in the lyrics so that we can check that * they match at the end of a music line. When waitforbar is set * by getword, the function does nothing (allows feature[j] * to advance to next feature) until waitforbar is set to 0 * (by writetrack). */ { 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; }; }; }; }; } static void checksyllables() /* check line of lyrics matches line of music. It grabs * all remaining syllables in the lyric line counting * them as it goes along. It then checks that the number * of syllables matches the number of notes for that music * line */ { 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; } static int inlist(place, passno) int place; int passno; /* decide whether passno matches list/number for variant section */ /* handles representation of [X in the abc */ { int a, b; char* p; int found; char msg[100]; /* printf("passno = %d\n", passno); */ if (denom[place] != 0) { /* special case when this is variant ending for only one pass */ if (passno == denom[place]) { return(1); } else { return(0); }; } else { /* must scan list */ p = atext[pitch[place]]; found = 0; while ((found == 0) && (*p != '\0')) { if (!isdigit(*p)) { sprintf(msg, "Bad variant list : %s", atext[pitch[place]]); event_error(msg); found = 1; }; a = readnump(&p); if (passno == a) { found = 1; }; if (*p == '-') { p = p + 1; b = readnump(&p); if ((passno >= a) && (passno <= b)) { found = 1; }; }; if (*p == ',') { p = p + 1; }; }; return(found); }; } void set_meter(n, m) /* set up variables associated with meter */ int n, m; { mtime_num = n; mtime_denom = m; time_num =n; time_denom=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; }; } static void write_meter(n, m) /* write meter to MIDI file */ 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); } static void write_keysig(sf, mi) /* Write key signature to MIDI file */ int sf, mi; { char data[2]; data[0] = (char) (0xff & sf); data[1] = (char) mi; mf_write_meta_event(0L, key_signature, data, 2); } static void midi_noteon(delta_time, pitch, pitchbend, chan, vel) /* write note on event to MIDI file */ long delta_time; int pitch, chan, vel, pitchbend; { char data[2]; #ifdef NOFTELL extern int nullpass; #endif if (channel >= MAXCHANS) { event_error("Channel limit exceeded\n"); } else { if(pitchbend < 0 || pitchbend > 16383) { event_error("Internal error concerning pitch bend on note on."); } if(pitchbend != current_pitchbend[channel] && chan != 9) { data[0] = (char) (pitchbend&0x7f); data[1] = (char) ((pitchbend>>7)&0x7f); mf_write_midi_event(delta_time,pitch_wheel,chan,data,2); delta_time=0; current_pitchbend[channel] = pitchbend; } if (chan == 9) data[0] = (char) drum_map[pitch]; else data[0] = (char) pitch; data[1] = (char) vel; mf_write_midi_event(delta_time, note_on, chan, data, 2); }; } void midi_noteoff(delta_time, pitch, chan) /* write note off event to MIDI file */ long delta_time; int pitch, chan; { char data[2]; if (chan == 9) data[0] = (char) drum_map[pitch]; else 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); }; } static void noteon_data(pitch, pitchbend, channel, vel) /* write note to MIDI file and adjust delta_time */ int pitch, pitchbend, channel, vel; { midi_noteon(delta_time, pitch, pitchbend, channel, vel); tracklen = tracklen + delta_time; delta_time = 0L; } static void noteon(n) /* compute note data and call noteon_data to write MIDI note event */ int n; { int i, vel; /* set velocity */ if(beataccents == 0) vel = mednote; else if (nbeats > 0) { if ((bar_num*nbeats)%(bar_denom*barsize) != 0) { /* not at a defined beat boundary */ vel = softnote; } else { /* find place in beatstring */ i = ((bar_num*nbeats)/(bar_denom*barsize))%nbeats; switch(beatstring[i]) { case 'f': case 'F': vel = loudnote; break; case 'm': case 'M': vel = mednote; break; default: case 'p': case 'P': vel = softnote; break; }; }; } else { /* no beatstring - use beat algorithm */ if (bar_num == 0) { vel = loudnote; } else { if ((bar_denom == 1) && ((bar_num % beat) == 0)) { vel = mednote; } else { vel = softnote; }; }; } if (channel == 9) noteon_data(pitch[n],bentpitch[n],channel,vel); else noteon_data(pitch[n] + transpose + global_transpose, bentpitch[n], channel, vel); } static void write_program(p, channel) /* write 'change program' (new instrument) command to MIDI file */ 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; } static void write_event(event_type, channel, data, n) /* write MIDI special event such as control_change or pitch_wheel */ int event_type; int channel, n; char data[]; { if (channel >= MAXCHANS) { event_error("Channel limit exceeded\n"); } else { mf_write_midi_event(delta_time, event_type, channel, data, n); }; } static char *select_channel(chan, s) char *s; int *chan; /* used by dodeferred() to set channel to be used */ /* reads 'bass', 'chord' or nothing from string pointed to by p */ { char sel[40]; char *p; p = s; skipspace(&p); *chan = channel; if (isalpha(*p)) { readstr(sel, &p, 40); skipspace(&p); if (strcmp(sel, "bass") == 0) { *chan = fun.chan; }; if (strcmp(sel, "chord") == 0) { *chan = gchord.chan; }; }; return(p); } static int makechordchannels (n) int n; { /* Allocate channels for in voice chords containing microtones. n is the number of channels to allocate which should be less than 10. */ int i,chan; int prog; if (n < 1) return -1; if (n > 9) n = 9; prog = current_program[channel]; chordchannels[0] = channel; /* save active channel number */ for (i=1; i<=n; i++) { chan = findchannel(); chordchannels[i] = chan; write_program(prog, chan); if (verbose) {printf("assigning channel %d to chordchannel\n",chan); } } nchordchannels = n; return n; } static void dodeferred(s,noteson) /* handle package-specific command which has been held over to be */ /* interpreted as MIDI is being generated */ char* s; int noteson; { char* p; char command[40]; int done; int val; p = s; skipspace(&p); readstr(command, &p, 40); skipspace(&p); done = 0; if (strcmp(command,"makechordchannels") == 0) { skipspace(&p); val = readnump(&p); makechordchannels(val); done = 1; } 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); }; if (noteson) { current_program[chan] = prog; write_program(prog, chan); }; done = 1; }; if (strcmp(command, "gchord") == 0) { set_gchords(p); done = 1; }; if (strcmp(command, "drum") == 0) { set_drums(p); done = 1; }; if ((strcmp(command, "chordprog") == 0)) { int prog; prog = readnump(&p); if (gchordson) { write_program(prog, gchord.chan); }; done = 1; }; if ((strcmp(command, "bassprog") == 0)) { int prog; prog = readnump(&p); if (gchordson) { 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, "drone") == 0) { skipspace(&p); val = readnump(&p); if (val > 0) drone.prog = val; skipspace(&p); val = readnump(&p); if (val >0 ) drone.pitch1 = val; skipspace(&p); val = readnump(&p); if (val >0) drone.pitch2 = val; skipspace(&p); val = readnump(&p); if (val >0) drone.vel1 = val; skipspace(&p); val = readnump(&p); if (val >0) drone.vel2 = val; if (drone.prog > 127) event_error("drone prog must be in the range 0-127"); if (drone.pitch1 >127) event_error("drone pitch1 must be in the range 0-127"); if (drone.vel1 >127) event_error("drone vel1 must be in the range 0-127"); if (drone.pitch2 >127) event_error("drone pitch1 must be in the range 0-127"); if (drone.vel2 >127) event_error("drone vel1 must be in the range 0-127"); 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, "beatmod") == 0) { skipspace(&p); velocity_increment = readsnump(&p); loudnote += velocity_increment; mednote += velocity_increment; softnote += velocity_increment; if (loudnote > 127) loudnote = 127; if (mednote > 127) mednote = 127; if (softnote > 127) softnote = 127; if (loudnote < 0) loudnote = 0; if (mednote < 0) mednote = 0; if (softnote < 0) softnote = 0; done = 1; } if (strcmp(command, "beatstring") == 0) { int count; skipspace(&p); count = 0; while ((count < 99) && (strchr("fFmMpP", *p) != NULL)) { beatstring[count] = *p; count = count + 1; p = p + 1; }; beatstring[count] = '\0'; if (strlen(beatstring) == 0) { event_error("beatstring expecting string of 'f', 'm' and 'p'"); } nbeats = strlen(beatstring); done = 1; } if (strcmp(command, "control") == 0) { int chan, n, datum; char data[20]; p = select_channel(&chan, p); 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_event(control_change, chan, data, n); done = 1; }; if( strcmp(command, "beataccents") == 0) { beataccents = 1; done = 1; } if( strcmp(command, "nobeataccents") == 0) { beataccents = 0; done = 1; } if (strcmp(command,"portamento") == 0) { int chan, datum; char data[4]; p = select_channel(&chan, p); data[0] = 65; data[1] = 127; /* turn portamento on */ write_event(control_change, chan, data, 2); data[0] = 5; /* coarse portamento */ datum = readnump(&p); if (datum > 63) { event_error("data must be in the range 0 - 63"); datum = 0; }; data[1] =(char) datum; write_event(control_change, chan, data, 2); done = 1; } if (strcmp(command,"noportamento") == 0) { int chan; char data[4]; p = select_channel(&chan, p); data[0] = 65; data[1] = 0; /* turn portamento off */ write_event(control_change, chan, data, 2); done = 1; } if (strcmp(command, "pitchbend") == 0) { int chan, n, datum; char data[2]; p = select_channel(&chan, p); n = 0; data[0] = 0; data[1] = 0; while ((n<2) && (*p >= '0') && (*p <= '9')) { datum = readnump(&p); if (datum > 255) { event_error("data must be in the range 0 - 255"); datum = 0; }; data[n] = (char) datum; n = n + 1; skipspace(&p); }; /* don't write pitchbend in the header track [SS] 2005-04-02 */ if (noteson) { write_event(pitch_wheel, chan, data, 2); tracklen = tracklen + delta_time; delta_time = 0L; } done = 1; }; if (strcmp(command, "snt") == 0) { /*single note tuning */ int midikey; float midipitch; midikey = readnump(&p); sscanf(p,"%f", &midipitch); single_note_tuning_change(midikey, midipitch); done = 1; } if (strcmp(command,"chordattack") == 0) { staticnotedelay = readnump(&p); notedelay = staticnotedelay; done = 1; }; if (strcmp(command,"randomchordattack") == 0) { staticchordattack = readnump(&p); chordattack = staticchordattack; done = 1; }; if (strcmp(command,"drummap") == 0) { parse_drummap(&p); done = 1; }; if (done == 0) { char errmsg[80]; sprintf(errmsg, "%%%%MIDI command \"%s\" not recognized",command); event_error(errmsg); }; } static void 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); } static void save_note(num, denom, pitch, pitchbend, chan, vel) /* output 'note on' queue up 'note off' for later */ int num, denom; int pitch, pitchbend, chan, vel; { /* don't transpose drum channel */ if(chan == 9) {noteon_data(pitch, pitchbend, chan, vel); addtoQ(num, denom, pitch, chan, -1);} else {noteon_data(pitch + transpose + global_transpose, pitchbend, chan, vel); addtoQ(num, denom, pitch + transpose + global_transpose, chan, -1);} } void dogchords(i) /* generate accompaniment notes */ /* note no microtone or linear temperament support ! */ int i; { int j; if ((i == g_ptr) && (g_ptr < (int) 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 'f': if (g_started && gchords) { /* do fundamental */ save_note(g_num*len, g_denom, basepitch+fun.base, 8192, fun.chan, fun.vel); }; break; case 'b': if (g_started && gchords) { /* do fundamental */ save_note(g_num*len, g_denom, basepitch+fun.base, 8192, fun.chan, fun.vel); }; /* There is no break here so the switch statement continues into the next case 'c' */ case 'c': /* do chord with handling of any 'inversion' note */ if (g_started && gchords) { for(j=0;j0 && g_started && gchords) save_note(g_num*len, g_denom, gchordnotes[0], 8192, gchord.chan, gchord.vel); break; case 'h': if(gchordnotes_size >1 && g_started && gchords) save_note(g_num*len, g_denom, gchordnotes[1], 8192, gchord.chan, gchord.vel); break; case 'i': if(gchordnotes_size >2 && g_started && gchords) save_note(g_num*len, g_denom, gchordnotes[2], 8192, gchord.chan, gchord.vel); break; case 'j': if(gchordnotes_size >3 && g_started && gchords) save_note(g_num*len, g_denom, gchordnotes[3], 8192, gchord.chan, gchord.vel); break; case 'G': if(gchordnotes_size>0 && g_started && gchords) save_note(g_num*len, g_denom, gchordnotes[0]-12, 8192, gchord.chan, gchord.vel); break; case 'H': if(gchordnotes_size >1 && g_started && gchords) save_note(g_num*len, g_denom, gchordnotes[1]-12, 8192, gchord.chan, gchord.vel); break; case 'I': if(gchordnotes_size >2 && g_started && gchords) save_note(g_num*len, g_denom, gchordnotes[2]-12, 8192, gchord.chan, gchord.vel); break; case 'J': if(gchordnotes_size >3 && g_started && gchords) save_note(g_num*len, g_denom, gchordnotes[3]-12, 8192, gchord.chan, gchord.vel); break; default: printf("no such gchord code %c\n",action); }; g_ptr = g_ptr + 1; addtoQ(g_num*len, g_denom, -1, g_ptr, 0); }; }; void dodrums(i) /* generate drum notes */ int i; { if ((i == drum_ptr) && (drum_ptr < (int) strlen(drum_seq))) { int len; char action; action = drum_seq[drum_ptr]; len = drum_len[drum_ptr]; switch (action) { case 'z': break; case 'd': if (drum_on) { save_note(drum_num*len, drum_denom, drum_program[drum_ptr],8192,9, drum_velocity[drum_ptr]); }; }; drum_ptr = drum_ptr + 1; addtoQ(drum_num*len, drum_denom, -1, drum_ptr, 0); }; } void start_drone() { int delta; /* delta = tracklen - drone.event; */ delta = delta_time - drone.event; if (drone.event == 0) write_program(drone.prog, drone.chan); midi_noteon(delta,drone.pitch1+global_transpose,8192,drone.chan,drone.vel1); midi_noteon(delta,drone.pitch2+global_transpose,8192,drone.chan,drone.vel2); /* drone.event = tracklen;*/ drone.event = delta_time; } void stop_drone() { int delta; /* delta = tracklen - drone.event; */ delta = delta_time - drone.event; midi_noteoff(delta,drone.pitch1+global_transpose,drone.chan); midi_noteoff(0,drone.pitch2+global_transpose,drone.chan); /* drone.event = tracklen; */ drone.event = delta_time; } void progress_sequence(i) int i; { if (gchordson) { dogchords(i); }; if (drumson) { dodrums(i); }; } void init_drum_map() { int i; for (i=0;i<256;i++) drum_map[i] = i; } static void parse_drummap(char **s) /* parse abc note and advance character pointer */ /* code stolen from parseabc.c and simplified */ { int mult; char accidental, note; int octave; int midipitch; int mapto; char msg[80]; char *anoctave = "cdefgab"; int scale[7] = {0, 2, 4, 5, 7, 9, 11}; mult = 1; accidental = ' '; note = ' '; /* read accidental */ switch (**s) { case '_': accidental = **s; *s = *s + 1; if (**s == '_') { *s = *s + 1; mult = 2; }; break; case '^': accidental = **s; *s = *s + 1; if (**s == '^') { *s = *s + 1; mult = 2; }; break; case '=': accidental = **s; *s = *s + 1; if (**s == '^') { accidental = **s; *s = *s + 1; } else if (**s == '_') { accidental = **s; *s = *s + 1; } break; default: /* do nothing */ break; }; if ((**s >= 'a') && (**s <= 'g')) { note = **s; octave = 1; *s = *s + 1; while ((**s == '\'') || (**s == ',')) { if (**s == '\'') { octave = octave + 1; *s = *s + 1; }; if (**s == ',') { sprintf(msg, "Bad pitch specifier , after note %c", note); event_error(msg); octave = octave - 1; *s = *s + 1; }; }; } else { octave = 0; if ((**s >= 'A') && (**s <= 'G')) { note = **s + 'a' - 'A'; *s = *s + 1; while ((**s == '\'') || (**s == ',')) { if (**s == ',') { octave = octave - 1; *s = *s + 1; }; if (**s == '\'') { sprintf(msg, "Bad pitch specifier ' after note %c", note + 'A' - 'a'); event_error(msg); octave = octave + 1; *s = *s + 1; }; }; }; }; /*printf("note = %d octave = %d accidental = %d\n",note,octave,accidental);*/ midipitch = (int) ((long) strchr(anoctave, note) - (long) anoctave); if (midipitch <0 || midipitch > 6) { event_error("Malformed note in drummap : expecting a-g or A-G"); return; } midipitch = scale[midipitch]; if (accidental == '^') midipitch += mult; if (accidental == '_') midipitch -= mult; midipitch = midipitch + 12*octave + 60; skipspace(s); mapto = readnump(s); if (mapto == 0) { event_error("Bad drummap: expecting note followed by space and number"); return; } if (mapto < 35 || mapto > 81) event_warning ("drummap destination should be between 35 and 81 inclusive"); /*printf("midipitch = %d map to %d \n",midipitch,mapto);*/ drum_map[midipitch] = mapto; } static void starttrack() /* called at the start of each MIDI track. Sets up all necessary default */ /* and initial values */ { int i; loudnote = 105; mednote = 95; softnote = 80; beatstring[0] = '\0'; beataccents = 1; nbeats = 0; transpose = 0; /* make sure meter is reinitialized for every track * in case it was changed in the middle of the last track */ set_meter(header_time_num,header_time_denom); div_factor = division; gchords = 1; partno = -1; partlabel = -1; g_started = 0; g_ptr = 0; drum_ptr = 0; Qinit(); if (noteson) { channel = findchannel(); if(verbose) printf("assigning channel %d to voice\n",channel); } else { /* set to valid value just in case - should never be used */ channel = 0; }; if (gchordson) { addtoQ(0, g_denom, -1, g_ptr, 0); fun.base = 36; fun.vel = 80; gchord.base = 48; gchord.vel = 75; fun.chan = findchannel(); if(verbose) printf("assigning channel %d to bass voice\n",fun.chan); gchord.chan = findchannel(); if(verbose) printf("assigning channel %d to chordal accompaniment\n",gchord.chan); }; if (droneon) { drone.event =0; drone.chan = findchannel(); if(verbose) printf("assigning channel %d to drone\n",drone.chan); drone.prog = 70; /* bassoon */ drone.vel1 = 80; drone.pitch1 = 45; drone.vel2 = 80; drone.pitch2= 33; } g_next = 0; partrepno = 0; /* thismline = -1; [SS] july 28 2006 */ /* This disables the message First lyrics line must come after first music line When a new voice is started with an inline voice command eg [V:1] abcd| etc. Unfortunately this is part of the abc2-draft.html standard. See canzonetta.abc in abc.sourceforge.net/standard/abc2-draft.html */ thiswline = -1; nowordline = 0; waitforbar = 0; musicsyllables = 0; lyricsyllables = 0; for (i=0; i<26; i++) { part_count[i] = 0; }; for (i=0;i= ntracks) trackvoice = xtrack-ntracks+32; */ wordson = 0; noteson = 1; gchordson = 0; temposon = 0; texton = 1; drumson = 0; droneon = 0; in_varend = 0; maxpass = 2; notedelay = staticnotedelay; chordattack = staticchordattack; trim_num = 0; trim_denom = 1; graceflag = 0; /* ensure that the percussion channel is not selected by findchannel() */ channels[9] = 1; if (karaoke) { if (xtrack < 3) karaokestarttrack(xtrack); /* lyrics are in track 2 (track count starts from 0) */ if (xtrack == 2) { kspace = 0; noteson = 0; wordson = 1; /* * Turn text off for H:, A: and other fields. * Putting it in Karaoke Words track (track 2) can throw off some Karaoke players. */ texton = 0; gchordson = 0; trackvoice = wordvoice; } else { if (trackvoice > 2) trackvoice = xtrack - 1; }; }; /* is this accompaniment track ? */ if ((gchordvoice != 0) && (xtrack == gchordtrack)) { noteson = 0; gchordson = 1; drumson = 0; droneon = 0; temposon = 0; trackvoice = gchordvoice; /* be sure set_meter is called before setbeat even if we * have to call it more than once at the start of the track */ set_meter(header_time_num,header_time_denom); /* printf("calling setbeat for accompaniment track\n"); */ setbeat(); }; /* is this drum track ? */ if ((drumvoice != 0) && (xtrack == drumtrack)) { noteson = 0; gchordson = 0; drumson = 1; droneon =0; temposon = 0; trackvoice = drumvoice; }; /* is this drone track ? */ if ((dronevoice != 0) && (xtrack == dronetrack)) { noteson = 0; gchordson = 0; drumson = 0; droneon = 1; temposon = 0; trackvoice = drumvoice; }; nchordchannels = 0; if (verbose) { printf("track %d, voice %d\n", xtrack, trackvoice); }; if (xtrack == 0) { mf_write_tempo(tempo); /* write key */ write_keysig(sf, mi); /* write timesig */ write_meter(time_num, time_denom); gchordson = 0; temposon = 1; if (ntracks > 1) { /* type 1 files have no notes in first track */ noteson = 0; texton = 0; trackvoice = 1; timekey=0; /* return(0L); */ } } starttrack(); inchord = 0; /* write notes */ j = 0; if ((voicesused) && (trackvoice != 1)) { j = findvoice(j, trackvoice, xtrack); }; barno = 0; bar_num = 0; bar_denom = 1; 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 (nchordchannels >0) channel = chordchannels[0]; if (noteson) { if (inchord) { notecount++; if (notecount > 1) { if(chordattack > 0) notedelay = (int) (chordattack*ranfrac()); delta_time += notedelay; totalnotedelay += notedelay; if (notecount <= nchordchannels+1) channel = chordchannels[notecount-1]; } } noteon(j); /* set up note off */ if (channel == 9) addtoQ(num[j], denom[j], drum_map[pitch[j]], channel, -totalnotedelay -1); else { note_num = num[j]; note_denom = denom[j]; /* turn off slurring prematurely two separate two slurs in a row */ if (slurring && feature[j+1] == SLUR_OFF) slurring = 0; if (trim && !slurring && !graceflag) { if (gtfract(note_num,note_denom,trim_num,trim_denom)) addfract(¬e_num,¬e_denom,-trim_num,trim_denom); /* else addfract(¬e_num,¬e_denom,-note_num,trim_denom*2); */ /* no note trimming for short notes 2006-08-05 at Hudson Lacerda's request. */ } addtoQ(note_num, note_denom, pitch[j] + transpose +global_transpose, channel, -totalnotedelay -1); } }; if (!inchord) { delay(num[j], denom[j], 0); addunits(num[j], denom[j]); notecount =0; totalnotedelay=0; }; break; case TNOTE: if (wordson) { /* counts as 2 syllables : note + first tied note. * We ignore any bar line placed between tied notes * since this causes write_syllable to lose synchrony * with the notes. */ write_syllable(j); waitforbar = 0; write_syllable(j); }; if (noteson) { noteon(j); /* set up note off */ if (channel == 9) addtoQ(num[j], denom[j], drum_map[pitch[j]], channel, -totalnotedelay -1); else addtoQ(num[j], denom[j], pitch[j] + transpose +global_transpose, channel, -totalnotedelay -1); }; break; case OLDTIE: if (wordson) { /* extra syllable beyond first two in a tie */ write_syllable(j); }; break; case REST: if (!inchord) { delay(num[j], denom[j], 0); addunits(num[j], denom[j]); }; break; case CHORDON: inchord = 1; break; case CHORDOFF: case CHORDOFFEX: if (wordson) { write_syllable(j); }; inchord = 0; delay(num[j], denom[j], 0); totalnotedelay=0; notecount=0; notedelay = staticnotedelay; chordattack = staticchordattack; note_num = num[j]; note_denom = denom[j]; addunits(note_num, note_denom); if (trim) { if (gtfract(note_num,note_denom,trim_num,trim_denom)) addfract(¬e_num,¬e_denom,-trim_num,trim_denom); } 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: in_varend = 0; j = partbreak(xtrack, trackvoice, j); if (parts == -1) { char msg[1]; msg[0] = (char) pitch[j]; mf_write_meta_event(0L, marker, msg, 1); }; break; case VOICE: /* search on for next occurence of voice */ j = findvoice(j, trackvoice, xtrack); break; case TEXT: if (texton) { mf_write_meta_event(0L, text_event, atext[pitch[j]], strlen(atext[pitch[j]])); }; break; case TITLE: /* Write name of song as sequence name in track 0 and as track 1 name. */ /* karaokestarttrack routine handles this instead if tune is a Karaoke tune. */ if (!karaoke) { if (xtrack < 2) 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: /* || */ in_varend = 0; waitforbar = 0; softcheckbar(pass); break; case BAR_REP: /* |: */ /* ensures that two |: don't occur in a row */ /* saves position of where to return when :| is encountered */ in_varend = 0; waitforbar = 0; softcheckbar(pass); if ((pass==1)&&(expect_repeat)) { event_error("Expected end repeat not found at |:"); }; save_state(state, j, barno, div_factor, transpose, channel); expect_repeat = 1; pass = 1; maxpass=2; break; case REP_BAR: /* :| */ /* ensures it was preceded by |: so we know where to return */ /* returns index j to the place following |: */ in_varend = 0; waitforbar = 0; softcheckbar(pass); if (pass == 1) { if (!expect_repeat) { event_error("Found unexpected :|"); } else { /* pass = 2; [SS] 2004-10-14 */ pass++; /* we may have multiple repeats */ restore_state(state, &j, &barno, &div_factor, &transpose, &channel); slurring = 0; expect_repeat = 0; }; } else { /* we could have multi repeats. */ /* pass = 1; [SS] 2004-10-14 */ /* we could have accidently have */ /* |: .sect 1.. :| ...sect 2 :|. We don't want to */ /* go back to sect 1 when we encounter :| in sect 2. */ /* We signal that we will expect |: but we wont't check */ if(pass < maxpass) { expect_repeat = 0; pass++; /* we may have multiple repeats */ restore_state(state, &j, &barno, &div_factor, &transpose, &channel); slurring = 0; } }; break; case PLAY_ON_REP: /* |[1 or |[2 or |1 or |2 */ /* keeps count of the pass number and selects the appropriate */ /* to be played for each pass. This code was designed to handle */ /* multirepeats using the inlist() function however the pass */ /* variable is not set up correctly for multirepeats. */ { int passnum; char errmsg[80]; if (in_varend != 0) { event_error("Need || |: :| or :: to mark end of variant ending"); }; passnum = -1; if (((expect_repeat)||(pass>1))) { passnum = pass; } /** else { // additivity remnants if (parts != -1) { passnum = partrepno+1; }; }; ***/ if (passnum == -1) { event_error("multiple endings do not follow |: or ::"); passnum = 1; }; if (inlist(j, passnum) != 1) { j = j + 1; /* if this is not the variant ending to be played on this pass*/ /* then skip to the end of this section watching out for voice*/ /* changes. Usually a section end with a :|, but the last */ /* last section could end with almost anything including a */ /* PART change. */ if(feature[j] == VOICE) j = findvoice(j, trackvoice, xtrack); while ((j 1) { /* Already gone through last time. Process it as a |:*/ /* and continue on. */ expect_repeat = 1; save_state(state, j, barno, div_factor, transpose, channel); pass = 1; maxpass=2; } else { /* should do a repeat unless |: is missing. */ if (!expect_repeat) { /* missing |: don't repeat but set up for next repeat */ /* section. */ event_error("Found unexpected ::"); expect_repeat = 1; save_state(state, j, barno, div_factor, transpose, channel); pass = 1; } else { /* go back and do the repeat */ restore_state(state, &j, &barno, &div_factor, &transpose, &channel); slurring = 0; /*pass = 2; [SS] 2004-10-14*/ pass++; }; }; break; case GCHORD: basepitch = pitch[j]; inversion = num[j]; chordnum = denom[j]; g_started = 1; configure_gchord(); break; case GCHORDON: if (gchordson) { gchords = 1; }; break; case GCHORDOFF: gchords = 0; break; case DRUMON: if (drumson) { drum_on = 1; }; break; case DRUMOFF: drum_on = 0; break; case DRONEON: if ((dronevoice != 0) && (xtrack == dronetrack)) start_drone(); break; case DRONEOFF: if ((dronevoice != 0) && (xtrack == dronetrack)) stop_drone(); break; case ARPEGGIO: notedelay = 3*staticnotedelay; chordattack=3*staticchordattack; break; case GRACEON: graceflag = 1; break; case GRACEOFF: graceflag = 0; break; case DYNAMIC: dodeferred(atext[pitch[j]],noteson); break; case KEY: if(timekey) write_keysig(pitch[j], denom[j]); break; case TIME: if(timekey) { barchecking = pitch[j]; write_meter(num[j], denom[j]); setbeat(); /* NEW [SS] 2003-APR-27 */ } break; case TEMPO: if (temposon) { char data[3]; /* long newtempo; newtempo = ((long)num[j]<<16) | ((long)denom[j] & 0xffff); printf("New tempo = %ld [%x %x]\n", newtempo, num[j], denom[j]); */ data[0] = num[j] & 0xff; data[1] = (denom[j]>>8) & 0xff; data[2] = denom[j] & 0xff; mf_write_meta_event(delta_time, set_tempo, data, 3); tracklen = tracklen + delta_time; delta_time = 0L; /* if (j > 0) { div_factor = pitch[j]; }; */ }; break; case CHANNEL: channel = pitch[j]; break; case TRANSPOSE: transpose = pitch[j]; break; case GTRANSPOSE: global_transpose = pitch[j]; break; case RTRANSPOSE: global_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; case COPYRIGHT: if (xtrack == 0) { mf_write_meta_event(delta_time, copyright_notice, atext[pitch[j]], strlen (atext[pitch[j]])); } break; case SETTRIM: trim_num = num[j]; trim_denom = denom[j]; if (trim_num > 0) trim = 1; else trim = 0; break; default: break; }; j = j + 1; }; if ((expect_repeat)&&(pass==1)) { 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); } char *featname[] = { "SINGLE_BAR", "DOUBLE_BAR", "BAR_REP", "REP_BAR", "PLAY_ON_REP", "REP1", "REP2", "BAR1", "REP_BAR2", "DOUBLE_REP", "THICK_THIN", "THIN_THICK", "PART", "TEMPO", "TIME", "KEY", "REST", "TUPLE", "NOTE", "NONOTE", "OLDTIE", "TEXT", "SLUR_ON", "SLUR_OFF", "TIE", "CLOSE_TIE", "TITLE", "CHANNEL", "TRANSPOSE", "RTRANSPOSE", "GTRANSPOSE", "GRACEON", "GRACEOFF", "SETGRACE", "SETC", "SETTRIM", "GCHORD", "GCHORDON", "GCHORDOFF", "VOICE", "CHORDON", "CHORDOFF", "CHORDOFFEX", "DRUMON", "DRUMOFF", "DRONEON", "DRONEOFF", "SLUR_TIE", "TNOTE", "LT", "GT", "DYNAMIC", "LINENUM", "MUSICLINE", "MUSICSTOP", "WORDLINE", "WORDSTOP", "INSTRUCTION", "NOBEAM", "CHORDNOTE", "CLEF", "PRINTLINE", "NEWPAGE", "LEFT_TEXT", "CENTRE_TEXT", "VSKIP", "COPYRIGHT", "COMPOSER", "ARPEGGIO", "SPLITVOICE" }; dumpfeat (int from, int to) { int i,j; for (i=from;i<=to;i++) { j = feature[i]; printf("%d %s %d %d %d %d \n",i,featname[j],pitch[i],bentpitch[i],num[i],denom[i]); } } /* see file queues.c for routines to handle note queue */ abcmidi-20070318/makefiles/0000775000175000017500000000000010576505314014640 5ustar anselmanselmabcmidi-20070318/makefiles/makefile.wat0000664000175000017500000001020010507010640017106 0ustar anselmanselm# Watcom Win32 Makefile for abcMIDI package # Use mingw or gnu standard make program with this makefile. # # # compilation #ifdefs - you may need to change these defined to get # the code to compile with a different C compiler. # # NOFTELL in midifile.c and genmidi.c selects a version of the file-writing # code which doesn't use file seeking. # # PCCFIX in mftext.c midifile.c midi2abc.c # comments out various things that aren't available in PCC # # USE_INDEX causes index() to be used instead of strchr(). This is needed # by some pre-ANSI C compilers. # # ASCTIME causes asctime() to be used instead of strftime() in pslib.c. # If ANSILIBS is not set, neither routine is used. # # ANSILIBS causes code to include some ANSI standard headers # # KANDR selects functions prototypes without argument prototypes. # CC=wcc386 CFLAGS=-ic:\\watcom\\h;c:\\watcom\\h\\nt -w4 -e25 -zq -od -d2 -5r -bt=nt -mf -DANSILIBS LDFLAGS=sys nt name LDFLAGS2=d all op inc op st=200000 op maxe=25 op q op symf LNK=wlink all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe abc2midi.exe : parseabc.obj store.obj genmidi.obj queues.obj midifile.obj parser2.obj $(LNK) $(LDFLAGS) abc2midi.exe $(LDFLAGS2) FILE parseabc.obj FILE genmidi.obj FILE store.obj \ FILE queues.obj FILE midifile.obj FILE parser2.obj abc2abc.exe : parseabc.obj toabc.obj $(LNK) $(LDFLAGS) abc2abc.exe $(LDFLAGS2) FILE parseabc.obj FILE toabc.obj midi2abc.exe : midifile.obj midi2abc.obj $(LNK) $(LDFLAGS) midi2abc.exe $(LDFLAGS2) FILE midifile.obj FILE midi2abc.obj mftext.exe : midifile.obj mftext.obj crack.obj $(LNK) $(LDFLAGS) mftext.exe $(LDFLAGS2) FILE midifile.obj FILE mftext.obj FILE crack.obj midicopy.exe : midicopy.obj $(LNK) $(LDFLAGS) midicopy.exe $(LDFLAGS2) FILE midicopy.obj yaps.exe : parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj $(LNK) $(LDFLAGS) yaps.exe $(LDFLAGS2) FILE parseabc.obj FILE yapstree.obj FILE drawtune.obj FILE debug.obj FILE position.obj FILE pslib.obj FILE parser2.obj abcmatch.exe : abcmatch.obj matchsup.obj parseabc.obj $(LNK) $(LDFLAGS) abcmatch.exe $(LDFLAGS2) FILE abcmatch.obj FILE matchsup.obj FILE parseabc.obj # common parser object code # parseabc.obj : parseabc.c abc.h parseabc.h $(CC) $(CFLAGS) parseabc.c parser2.obj : parser2.c parseabc.h parser2.h $(CC) $(CFLAGS) parser2.c # objects needed by abc2abc # toabc.obj : toabc.c abc.h parseabc.h $(CC) $(CFLAGS) toabc.c # objects needed by abc2midi # store.obj : store.c abc.h parseabc.h parser2.h genmidi.h $(CC) $(CFLAGS) store.c genmidi.obj : genmidi.c abc.h midifile.h genmidi.h $(CC) $(CFLAGS) genmidi.c # could use -DNOFTELL here tomidi.obj : tomidi.c abc.h midifile.h $(CC) $(CFLAGS) tomidi.c queues.obj: queues.c genmidi.h $(CC) $(CFLAGS) queues.c # common midifile library # # could use -DNOFTELL here midifile.obj : midifile.c midifile.h $(CC) $(CFLAGS) midifile.c # objects needed by yaps # yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h $(CC) $(CFLAGS) yapstree.c drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h $(CC) $(CFLAGS) drawtune.c pslib.obj: pslib.c drawtune.h $(CC) $(CFLAGS) pslib.c position.obj: position.c abc.h structs.h sizes.h $(CC) $(CFLAGS) position.c debug.obj: debug.c structs.h abc.h $(CC) $(CFLAGS) debug.c # objects needed by midi2abc # midi2abc.obj : midi2abc.c midifile.h $(CC) $(CFLAGS) midi2abc.c # objects for mftext # crack.obj : crack.c $(CC) $(CFLAGS) crack.c mftext.obj : mftext.c midifile.h $(CC) $(CFLAGS) mftext.c # objects for midicopy # midicopy.obj : midicopy.c midicopy.h $(CC) $(CFLAGS) midicopy.c #objects for abcmtch # abcmatch.obj : abcmatch.c abc.h $(CC) $(CFLAGS) abcmatch.c matchsup.obj : matchsup.c abc.h parseabc.h parser2.h genmidi.h $(CC) $(CFLAGS) matchsup.c clean: rm *.obj rm *.exe zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe abcmatch.exe zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt abcmidi-20070318/makefiles/makefile.bcc0000664000175000017500000001004310507010640017047 0ustar anselmanselm# Borland command line compiler bcc version 5.5 Makefile for abcMIDI package # Can also use mingw or gnu standard make program with this makefile. # # # compilation #ifdefs - you may need to change these defined to get # the code to compile with a different C compiler. # # NOFTELL in midifile.c and tomidi.c selects a version of the file-writing # code which doesn't use file seeking. # # PCCFIX in mftext.c midifile.c midi2abc.c # comments out various things that aren't available in PCC # # USE_INDEX causes index() to be used instead of strchr(). This is needed # by some pre-ANSI C compilers. # # ASCTIME causes asctime() to be used instead of strftime() in pslib.c. # If ANSILIBS is not set, neither routine is used. # # ANSILIBS causes code to include some ANSI standard headers # # KANDR selects functions prototypes without argument prototypes. # # If your Borland compiler is in another path, change the include # and library options below in CFLAGS and LDFLAGS. # CC=bcc32 CFLAGS=-g0 -v -WC -c -I/bcc55/include LNK=ilink32 LDFLAGS=/ap /L\bcc55\lib c0x32.obj LDFLAGS2=import32.lib cw32.lib all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe abc2midi.exe : parseabc.obj store.obj genmidi.obj queues.obj midifile.obj parser2.obj $(LNK) $(LDFLAGS) parseabc.obj genmidi.obj store.obj \ queues.obj midifile.obj parser2.obj, abc2midi.exe,, $(LDFLAGS2) abc2abc.exe : parseabc.obj toabc.obj $(LNK) $(LDFLAGS) parseabc.obj toabc.obj, abc2abc.exe,, $(LDFLAGS2) midi2abc.exe : midifile.obj midi2abc.obj $(LNK) $(LDFLAGS) midifile.obj midi2abc.obj, midi2abc.exe,, $(LDFLAGS2) mftext.exe : midifile.obj mftext.obj crack.obj $(LNK) $(LDFLAGS) midifile.obj mftext.obj crack.obj, mftext.exe,, $(LDFLAGS2) yaps.exe : parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj $(LNK) $(LDFLAGS) parseabc.obj yapstree.obj drawtune.obj debug.obj \ position.obj pslib.obj parser2.obj, yaps.exe,, $(LDFLAGS2) midicopy.exe: midicopy.obj $(LNK) $(LDFLAGS) midicopy.obj, midicopy.exe,, $(LDFLAGS2) abcmatch.exe : abcmatch.obj matchsup.obj parseabc.obj $(LNK) $(LDFLAGS) abcmatch.obj matchsup.obj parseabc.obj, abcmatch.exe,, $(LDFLAGS2) # common parser object code # parseabc.obj : parseabc.c abc.h parseabc.h $(CC) $(CFLAGS) parseabc.c parser2.obj : parser2.c parseabc.h parser2.h $(CC) $(CFLAGS) parser2.c # objects needed by abc2abc # toabc.obj : toabc.c abc.h parseabc.h $(CC) $(CFLAGS) toabc.c # objects needed by abc2midi # store.obj : store.c abc.h parseabc.h parser2.h genmidi.h $(CC) $(CFLAGS) store.c genmidi.obj : genmidi.c abc.h midifile.h genmidi.h $(CC) $(CFLAGS) genmidi.c queues.obj: queues.c genmidi.h $(CC) $(CFLAGS) queues.c # common midifile library # # could use -DNOFTELL here midifile.obj : midifile.c midifile.h $(CC) $(CFLAGS) midifile.c # objects needed by yaps # yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h $(CC) $(CFLAGS) yapstree.c drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h $(CC) $(CFLAGS) drawtune.c pslib.obj: pslib.c drawtune.h $(CC) $(CFLAGS) pslib.c position.obj: position.c abc.h structs.h sizes.h $(CC) $(CFLAGS) position.c debug.obj: debug.c structs.h abc.h $(CC) $(CFLAGS) debug.c # objects needed by midi2abc # midi2abc.obj : midi2abc.c midifile.h $(CC) $(CFLAGS) midi2abc.c # objects for mftext # crack.obj : crack.c $(CC) $(CFLAGS) crack.c mftext.obj : mftext.c midifile.h $(CC) $(CFLAGS) mftext.c # objects for midicopy # midicopy.obj :midicopy.c midicopy.h $(CC) $(CFLAGS) midicopy.c # objects for abcmatch # abcmatch.obj : abcmatch.c abc.h $(CC) $(CFLAGS) abcmatch. matchsup.obj : matchsup.c abc.h parseabc.h parser2.h $(CC) $(CFLAGS) matchsup.c clean: rm *.obj rm *.exe zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe midicopy.exe zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt abcmidi-20070318/makefiles/djgpp.mak0000664000175000017500000000727610507010640016435 0ustar anselmanselm# DJGPP (DOS port of gcc) Makefile for abcMIDI package # # # compilation #ifdefs - you may need to change these defined to get # the code to compile with a different C compiler. # # NOFTELL in midifile.c and genmidi.c selects a version of the file-writing # code which doesn't use file seeking. # # PCCFIX in mftext.c midifile.c midi2abc.c # comments out various things that aren't available in PCC # # USE_INDEX causes index() to be used instead of strchr(). This is needed # by some pre-ANSI C compilers. # # ASCTIME causes asctime() to be used instead of strftime() in pslib.c. # If ANSILIBS is not set, neither routine is used. # # ANSILIBS causes code to include some ANSI standard headers # # KANDR selects functions prototypes without argument prototypes. # # NO_SNPRINTF causes code to use printf instead of snprintf which # is less secure. CC=gcc CFLAGS=-c -ansi -DANSILIBS -DNO_SNPRINTF -Wformat -Wtraditional # -ansi forces ANSI compliance LNK=gcc all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe\ midicopy.exe abcmatch.exe abc2midi.exe : parseabc.o store.o genmidi.o queues.o midifile.o parser2.o $(LNK) -o abc2midi.exe parseabc.o genmidi.o store.o \ queues.o midifile.o parser2.o abc2abc.exe : parseabc.o toabc.o $(LNK) -o abc2abc.exe parseabc.o toabc.o midi2abc.exe : midifile.o midi2abc.o $(LNK) midifile.o midi2abc.o -o midi2abc.exe mftext.exe : midifile.o mftext.o crack.o $(LNK) midifile.o mftext.o crack.o -o mftext.exe midicopy.exe : midicopy.o $(LNK) midicopy.o -o midicopy.exe abcmatch.exe : abcmatch.o matchsup.o parseabc.o $(LNK) abcmatch.o matchsup.o parseabc.o -o abcmatch.exe yaps.exe : parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o $(LNK) -o yaps.exe parseabc.o yapstree.o drawtune.o debug.o \ position.o pslib.o parser2.o # common parser object code # parseabc.o : parseabc.c abc.h parseabc.h $(CC) $(CFLAGS) parseabc.c parser2.o : parser2.c parseabc.h parser2.h $(CC) $(CFLAGS) parser2.c # objects needed by abc2abc # toabc.o : toabc.c abc.h parseabc.h $(CC) $(CFLAGS) toabc.c # objects needed by abc2midi # store.o : store.c abc.h parseabc.h parser2.h genmidi.h $(CC) $(CFLAGS) store.c genmidi.o : genmidi.c abc.h midifile.h genmidi.h $(CC) $(CFLAGS) genmidi.c # could use -DNOFTELL here tomidi.o : tomidi.c abc.h midifile.h $(CC) $(CFLAGS) tomidi.c queues.o: queues.c genmidi.h $(CC) $(CFLAGS) queues.c midicopy.o: midicopy.c midicopy.h $(CC) $(CFLAGS) midicopy.c abcmatch.o: abcmatch.c abc.h $(CC) $(CFLAGS) abcmatch.c # common midifile library # # could use -DNOFTELL here midifile.o : midifile.c midifile.h $(CC) $(CFLAGS) midifile.c # objects needed by yaps # yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h $(CC) $(CFLAGS) yapstree.c drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h $(CC) $(CFLAGS) drawtune.c pslib.o: pslib.c drawtune.h $(CC) $(CFLAGS) pslib.c position.o: position.c abc.h structs.h sizes.h $(CC) $(CFLAGS) position.c debug.o: debug.c structs.h abc.h $(CC) $(CFLAGS) debug.c # objects needed by midi2abc # midi2abc.o : midi2abc.c midifile.h $(CC) $(CFLAGS) midi2abc.c # objects for mftext # crack.o : crack.c $(CC) $(CFLAGS) crack.c mftext.o : mftext.c midifile.h $(CC) $(CFLAGS) mftext.c # objects for abcmatch # matchsup.o : matchsup.c abc.h parseabc.h parser2.h $(CC) $(CFLAGS) matchsup.c clean: del *.o del *.exe zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe\ abc2abc.exe midicopy.exe zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt abcmidi-20070318/makefiles/makefile.wd0000664000175000017500000001001710507010640016733 0ustar anselmanselm# Watcom DOS 32 bit Makefile for abcMIDI package # Use mingw or gnu standard make program with this makefile. # # # compilation #ifdefs - you may need to change these defined to get # the code to compile with a different C compiler. # # NOFTELL in midifile.c and genmidi.c selects a version of the file-writing # code which doesn't use file seeking. # # PCCFIX in mftext.c midifile.c midi2abc.c # comments out various things that aren't available in PCC # # USE_INDEX causes index() to be used instead of strchr(). This is needed # by some pre-ANSI C compilers. # # ASCTIME causes asctime() to be used instead of strftime() in pslib.c. # If ANSILIBS is not set, neither routine is used. # # ANSILIBS causes code to include some ANSI standard headers # # KANDR selects functions prototypes without argument prototypes. # CC=wcc386 CFLAGS=-ic:\\watcom\\h -w4 -e25 -zq -od -d2 -5r -bt=dos -mf -DANSILIBS LDFLAGS=sys dos4g name LDFLAGS2=d all op inc op st=200000 op maxe=25 op q op symf LNK=wlink all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe abc2midi.exe : parseabc.obj store.obj genmidi.obj queues.obj midifile.obj parser2.obj $(LNK) $(LDFLAGS) abc2midi.exe $(LDFLAGS2) FILE parseabc.obj FILE genmidi.obj FILE store.obj \ FILE queues.obj FILE midifile.obj FILE parser2.obj abc2abc.exe : parseabc.obj toabc.obj $(LNK) $(LDFLAGS) abc2abc.exe $(LDFLAGS2) FILE parseabc.obj FILE toabc.obj midi2abc.exe : midifile.obj midi2abc.obj $(LNK) $(LDFLAGS) midi2abc.exe $(LDFLAGS2) FILE midifile.obj FILE midi2abc.obj mftext.exe : midifile.obj mftext.obj crack.obj $(LNK) $(LDFLAGS) mftext.exe $(LDFLAGS2) FILE midifile.obj FILE mftext.obj FILE crack.obj midicopy.exe: midicopy.obj $(LNK) $(LDFLAGS) midicopy.exe $(LDFLAGS2) FILE midicopy.obj abcmatch.exe : abcmatch.obj matchsup.obj parseabc.obj $(LNK) $(LDFLAGS) abcmatch.exe $(LDFLAGS) FILE abcmatch.obj matchsup.obj parseabc.obj yaps.exe : parseabc.obj yapstree.obj drawtune.obj debug.obj pslib.obj position.obj parser2.obj $(LNK) $(LDFLAGS) yaps.exe $(LDFLAGS2) FILE parseabc.obj FILE yapstree.obj FILE drawtune.obj FILE debug.obj FILE position.obj FILE pslib.obj FILE parser2.obj # common parser object code # parseabc.obj : parseabc.c abc.h parseabc.h $(CC) $(CFLAGS) parseabc.c parser2.obj : parser2.c parseabc.h parser2.h $(CC) $(CFLAGS) parser2.c # objects needed by abc2abc # toabc.obj : toabc.c abc.h parseabc.h $(CC) $(CFLAGS) toabc.c # objects needed by abc2midi # store.obj : store.c abc.h parseabc.h parser2.h genmidi.h $(CC) $(CFLAGS) store.c genmidi.obj : genmidi.c abc.h midifile.h genmidi.h $(CC) $(CFLAGS) genmidi.c # could use -DNOFTELL here queues.obj: queues.c genmidi.h $(CC) $(CFLAGS) queues.c # common midifile library # # could use -DNOFTELL here midifile.obj : midifile.c midifile.h $(CC) $(CFLAGS) midifile.c # objects needed by yaps # yapstree.obj: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h $(CC) $(CFLAGS) yapstree.c drawtune.obj: drawtune.c structs.h sizes.h abc.h drawtune.h $(CC) $(CFLAGS) drawtune.c pslib.obj: pslib.c drawtune.h $(CC) $(CFLAGS) pslib.c position.obj: position.c abc.h structs.h sizes.h $(CC) $(CFLAGS) position.c debug.obj: debug.c structs.h abc.h $(CC) $(CFLAGS) debug.c # objects needed by midi2abc # midi2abc.obj : midi2abc.c midifile.h $(CC) $(CFLAGS) midi2abc.c # objects for mftext # crack.obj : crack.c $(CC) $(CFLAGS) crack.c mftext.obj : mftext.c midifile.h $(CC) $(CFLAGS) mftext.c # objects for midicopy # midicopy.obj :midicopy.c midicopy.h $(CC) $(CFLAGS) midicopy.c # objects for abcmatch # abcmatch.obj :abcmatch.c abc.h $(CC) $(CFLAGS) abcmatch.c matchsup.obj :matchsup.c abc.h parseabc.h parser2.h $(CC) $(CFLAGS) matchsup.c clean: rm *.obj rm *.exe zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt abcmidi-20070318/makefiles/unix.mak0000664000175000017500000000723510507010640016307 0ustar anselmanselm# Generic unix/gcc Makefile for abcMIDI package # # # compilation #ifdefs - you need to compile with these defined to get # the code to compile with PCC. # # NOFTELL in midifile.c and genmidi.c selects a version of the file-writing # code which doesn't use file seeking. # # PCCFIX in mftext.c midifile.c midi2abc.c # comments out various things that aren't available in PCC # # ANSILIBS includes some ANSI header files (which gcc can live without, # but other compilers may want). # # USE_INDEX causes index() to be used instead of strchr(). This is needed # by some pre-ANSI C compilers. # # ASCTIME causes asctime() to be used instead of strftime() in pslib.c. # If ANSILIBS is not set, neither routine is used. # # KANDR selects functions prototypes without argument prototypes. # currently yaps will only compile in ANSI mode. # # # On running make, you may get the mysterious message : # # ', needed by `parseabc.o'. Stop `abc.h # # This means you are using GNU make and this file is in DOS text format. To # cure the problem, change this file from using PC-style end-of-line (carriage # return and line feed) to unix style end-of-line (line feed). CC=gcc CFLAGS=-DANSILIBS -O2 LNK=gcc INSTALL=install prefix=/usr/local binaries=abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch docdir=share/doc/abcmidi bindir=bin mandir=share/man/man1 all : abc2midi midi2abc abc2abc mftext yaps midicopy abcmatch abc2midi : parseabc.o store.o genmidi.o midifile.o queues.o parser2.o $(LNK) -o abc2midi parseabc.o store.o genmidi.o queues.o \ parser2.o midifile.o abc2abc : parseabc.o toabc.o $(LNK) -o abc2abc parseabc.o toabc.o midi2abc : midifile.o midi2abc.o $(LNK) midifile.o midi2abc.o -o midi2abc mftext : midifile.o mftext.o crack.o $(LNK) midifile.o mftext.o crack.o -o mftext yaps : parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o $(LNK) -o yaps parseabc.o yapstree.o drawtune.o debug.o \ position.o pslib.o parser2.o -o yaps midicopy : midicopy.o $(LNK) -o midicopy midicopy.o abcmatch : abcmatch.o matchsup.o parseabc.o $(LNK) abcmatch.o matchsup.o parseabc.o -o abcmatch parseabc.o : parseabc.c abc.h parseabc.h parser2.o : parser2.c abc.h parseabc.h parser2.h toabc.o : toabc.c abc.h parseabc.h # could use -DNOFTELL here genmidi.o : genmidi.c abc.h midifile.h genmidi.h store.o : store.c abc.h parseabc.h midifile.h genmidi.h queues.o : queues.c genmidi.h # could use -DNOFTELL here midifile.o : midifile.c midifile.h midi2abc.o : midi2abc.c midifile.h midicopy.o : midicopy.c midicopy.h abcmatch.o: abcmatch.c abc.h crack.o : crack.c mftext.o : mftext.c midifile.h # objects needed by yaps # yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h pslib.o: pslib.c drawtune.h position.o: position.c abc.h structs.h sizes.h debug.o: debug.c structs.h abc.h #objects for abcmatch # matchsup.o : matchsup.c abc.h parseabc.h parser2.h clean : rm *.o ${binaries} install: abc2midi midi2abc abc2abc mftext midicopy yaps abcmatch $(INSTALL) -m 755 ${binaries} ${prefix}/${bindir} # install documentation test -d ${PREFIX}/share/doc/abcmidi || mkdir -p ${prefix}/${docdir} $(INSTALL) -m 644 doc/*.txt ${prefix}/${docdir} $(INSTALL) -m 644 doc/AUTHORS ${prefix}/${docdir} $(INSTALL) -m 644 doc/CHANGES ${prefix}/${docdir} $(INSTALL) -m 644 VERSION ${prefix}/${docdir} # install manpages test -d ${prefix}/${mandir} || mkdir -p ${prefix}/${mandir}; $(INSTALL) -m 644 doc/*.1 ${prefix}/${mandir} abcmidi-20070318/makefiles/pcc.mak0000664000175000017500000000452510507010640016070 0ustar anselmanselm# PCC Makefile for abcMIDI package # # # compilation #ifdefs - you need to define some of these to get # the code to compile with PCC. # # NOFTELL in midifile.c and genmidi.c selects a version of the file-writing # code which doesn't use file seeking. # # PCCFIX in mftext.c midifile.c midi2abc.c # comments out various things that aren't available in PCC # and applies a fix needed for file writing # # ANSILIBS causes appropriate ANSI .h files to be #included. # # KANDR selects function prototypes with argument prototypes. # # USE_INDEX replaces calls to strchr() with calls to index(). # CC=pcc CFLAGS=-nPCCFIX -nNOFTELL -nUSE_INDEX -nKANDR LNK=pccl all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe midicopy.exe abcmatch.exe abc2midi.exe : parseabc.o store.o genmidi.o queues.o midifile.o parser2.o $(LNK) -Lc:\bin\pcc\ -Oabc2midi parseabc.o store.o genmidi.o queues.o midifile.o parser2.o abc2abc.exe : parseabc.o toabc.o $(LNK) -Lc:\bin\pcc\ -Oabc2abc parseabc.o toabc.o midi2abc.exe : midifile.o midi2abc.o $(LNK) -Lc:\bin\pcc\ midifile.o midi2abc.o -Omidi2abc mftext.exe : midifile.o mftext.o crack.o $(LNK) -Lc:\bin\pcc\ midifile.o mftext.o crack.o -Omftext midicopy.exe: midicopy.o $(LNK) -Lc:\bin\pcc\ midicopy.o -Omidicopy $(CFLAGS) parseabc.o : parseabc.c abc.h parseabc.h $(CC) parseabc.c $(CFLAGS) parser2.o : parser2.c abc.h parseabc.h parser2.h $(CC) parser2.c $(CFLAGS) toabc.o : toabc.c abc.h parseabc.h $(CC) toabc.c $(CFLAGS) genmidi.o : genmidi.c abc.h midifile.h parseabc.h genmidi.h $(CC) genmidi.c $(CFLAGS) store.o : store.c abc.h midifile.h parseabc.h $(CC) store.c $(CFLAGS) queues.o : queues.c genmidi.h $(CC) queues.c $(CFLAGS) midifile.o : midifile.c midifile.h $(CC) midifile.c $(CFLAGS) midi2abc.o : midi2abc.c midifile.h $(CC) midi2abc.c $(CFLAGS) crack.o : crack.c $(CC) crack.c $(CFLAGS) mftext.o : mftext.c midifile.h $(CC) mftext.c $(CFLAGS) midicopy.o : midicopy.c $(CC) midicopy.c $(CFLAGS) abcmatch.o : abcmatch.c $(CC) abcmatch.c $(CFLAGS) matchsup.o : matchsup.c $(CC) matchsup.c $(CFLAGS) clean: del *.exe del *.o zipfile: abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe midicopy.exe abcmatch.exe zip pcexe.zip *.exe readme.txt abcguide.txt demo.abc abcmidi-20070318/makefiles/makefile.ming0000664000175000017500000000703610507010640017262 0ustar anselmanselm# Mingw Win32 Makefile for abcMIDI package # # # compilation #ifdefs - you may need to change these defined to get # the code to compile with a different C compiler. # # NOFTELL in midifile.c and tomidi.c selects a version of the file-writing # code which doesn't use file seeking. # # PCCFIX in mftext.c midifile.c midi2abc.c # comments out various things that aren't available in PCC # # USE_INDEX causes index() to be used instead of strchr(). This is needed # by some pre-ANSI C compilers. # # ASCTIME causes asctime() to be used instead of strftime() in pslib.c. # If ANSILIBS is not set, neither routine is used. # # ANSILIBS causes code to include some ANSI standard headers # # KANDR selects functions prototypes without argument prototypes. # # If your mingw compiler is in another path, change the include # options below in CFLAGS. # CC=gcc CFLAGS=-c -I/mingw/i386-mingw32/include LNK=gcc LDFLAGS=-o all : abc2midi.exe midi2abc.exe abc2abc.exe mftext.exe yaps.exe \ midicopy.exe abcmatch.exe abc2midi.exe : parseabc.o store.o genmidi.o queues.o midifile.o parser2.o $(LNK) $(LDFLAGS) abc2midi.exe parseabc.o genmidi.o store.o \ queues.o midifile.o parser2.o abc2abc.exe : parseabc.o toabc.o $(LNK) $(LDFLAGS) abc2abc.exe parseabc.o toabc.o midi2abc.exe : midifile.o midi2abc.o $(LNK) $(LDFLAGS) midi2abc.exe midifile.o midi2abc.o mftext.exe : midifile.o mftext.o crack.o $(LNK) $(LDFLAGS) mftext.exe midifile.o mftext.o crack.o yaps.exe : parseabc.o yapstree.o drawtune.o debug.o pslib.o position.o parser2.o $(LNK) $(LDFLAGS) yaps.exe parseabc.o yapstree.o drawtune.o debug.o \ position.o pslib.o parser2.o midicopy.exe: midicopy.o $(LNK) $(LDFLAGS) midicopy.exe midicopy.o abcmatch.exe : abcmatch.o matchsup.o parseabc.o $(LNK) abcmatch.o matchsup.o parseabc.o -o abcmatch.exe # common parser object code # parseabc.o : parseabc.c abc.h parseabc.h $(CC) $(CFLAGS) parseabc.c parser2.o : parser2.c parseabc.h parser2.h $(CC) $(CFLAGS) parser2.c # objects needed by abc2abc # toabc.o : toabc.c abc.h parseabc.h $(CC) $(CFLAGS) toabc.c # objects needed by abc2midi # store.o : store.c abc.h parseabc.h parser2.h genmidi.h $(CC) $(CFLAGS) store.c genmidi.o : genmidi.c abc.h midifile.h genmidi.h $(CC) $(CFLAGS) genmidi.c # could use -DNOFTELL here tomidi.o : tomidi.c abc.h midifile.h $(CC) $(CFLAGS) tomidi.c queues.o: queues.c genmidi.h $(CC) $(CFLAGS) queues.c # common midifile library # # could use -DNOFTELL here midifile.o : midifile.c midifile.h $(CC) $(CFLAGS) midifile.c # objects needed by yaps # yapstree.o: yapstree.c abc.h parseabc.h structs.h drawtune.h parser2.h $(CC) $(CFLAGS) yapstree.c drawtune.o: drawtune.c structs.h sizes.h abc.h drawtune.h $(CC) $(CFLAGS) drawtune.c pslib.o: pslib.c drawtune.h $(CC) $(CFLAGS) pslib.c position.o: position.c abc.h structs.h sizes.h $(CC) $(CFLAGS) position.c debug.o: debug.c structs.h abc.h $(CC) $(CFLAGS) debug.c # objects needed by midi2abc # midi2abc.o : midi2abc.c midifile.h $(CC) $(CFLAGS) midi2abc.c # objects for mftext # crack.o : crack.c $(CC) $(CFLAGS) crack.c mftext.o : mftext.c midifile.h $(CC) $(CFLAGS) mftext.c # objects needed for midicopy # midicopy.o : midicopy.c $(CC) $(CFLAGS) midicopy.c clean: rm *.o rm *.exe zipfile: midi2abc.exe abc2midi.exe mftext.exe yaps.exe abc2abc.exe midicopy.exe zip pcexe2.zip *.exe readme.txt abcguide.txt demo.abc yaps.txt abcmidi-20070318/parseabc.c0000664000175000017500000013261410542502206014621 0ustar anselmanselm/* * parseabc.c - code to parse an abc file. This file is used by the * following 3 programs : * abc2midi - program to convert abc files to MIDI files. * abc2abc - program to manipulate abc files. * yaps - program to convert abc to PostScript music files. * Copyright (C) 1999 James Allwright * e-mail: J.R.Allwright@westminster.ac.uk * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * */ /* Macintosh port 30th July 1996 */ /* DropShell integration 27th Jan 1997 */ /* Wil Macaulay (wil@syndesis.com) */ #define TAB 9 #include "abc.h" #include "parseabc.h" #include #include #define SIZE_ABBREVIATIONS ('Z' - 'H' + 1) #ifdef _MSC_VER #define ANSILIBS #define casecmp stricmp #else #define casecmp strcasecmp #endif #define stringcmp strcmp #ifdef __MWERKS__ #define __MACINTOSH__ 1 #endif /* __MWERKS__ */ #ifdef __MACINTOSH__ #define main macabc2midi_main #define STRCHR #endif /* __MACINTOSH__ */ /* define USE_INDEX if your C libraries have index() instead of strchr() */ #ifdef USE_INDEX #define strchr index #endif #ifdef ANSILIBS #include #include #include #else extern char* malloc(); extern char* strchr(); #endif int lineno; static int parsing_started =0; static int parsing, slur; static int inhead, inbody; static int parserinchord; static int ingrace=0; int chorddecorators[DECSIZE]; char decorations[] = ".MLRH~Tuv"; static char *abbreviation[SIZE_ABBREVIATIONS]; int voicecodes = 0; char voicecode[16][30]; /*for interpreting V: string */ int decorators_passback[DECSIZE]; /* this global array is linked as an external to store.c and * yaps.tree.c and is used to pass back decorator information * from event_instruction to parsenote. */ int nokey=0; /* K: none was encountered */ int chord_n,chord_m ; /* for event_chordoff */ int fileline_number = 1; int intune = 1; extern programname fileprogram; int oldchordconvention = 0; char *mode[10] = {"maj", "min", "m", "aeo", "loc", "ion", "dor", "phr", "lyd", "mix"}; int modeshift[10] = {0, -3, -3, -3, -5, 0, -2, -4, 1, -1 }; int modeminor[10] = {0, 1, 1, 1, 0, 0, 0, 0, 0, 0}; int modekeyshift[10] = {0,5,5,5,6,0,1,2,3,4}; int* checkmalloc(bytes) /* malloc with error checking */ int bytes; { int *p; p = (int*) malloc(bytes); if (p == NULL) { printf("Out of memory error - malloc failed!\n"); exit(0); }; return (p); } char* addstring(s) /* create space for string and store it in memory */ char* s; { char* p; p = (char*) checkmalloc(strlen(s)+1); strcpy(p, s); return(p); } void initvstring(s) struct vstring *s; /* initialize vstring (variable length string data structure) */ { s->len = 0; s->limit = 40; s->st = (char*) checkmalloc(s->limit + 1); *(s->st) = '\0'; } void extendvstring(s) struct vstring *s; /* doubles character space available in string */ { char* p; if (s->limit > 0) { s->limit = s->limit * 2; p = (char*) checkmalloc(s->limit + 1); strcpy(p, s->st); free(s->st); s->st = p; } else { initvstring(s); }; } void addch(ch, s) char ch; struct vstring *s; /* appends character to vstring structure */ { if (s->len >= s->limit) { extendvstring(s); }; *(s->st+s->len) = ch; *(s->st+(s->len)+1) = '\0'; s->len = (s->len) + 1; } void addtext(text, s) char* text; struct vstring *s; /* appends a string to vstring data structure */ { int newlen; newlen = s->len + strlen(text); while (newlen >= s->limit) { extendvstring(s); }; strcpy(s->st+s->len, text); s->len = newlen; } void clearvstring(s) struct vstring *s; /* set string to empty */ /* does not deallocate memory ! */ { *(s->st) = '\0'; s->len = 0; } void freevstring(s) struct vstring *s; /* deallocates memory allocated for string */ { if (s->st != NULL) { free(s->st); s->st = NULL; }; s->len = 0; s->limit = 0; } void parseron() { parsing = 1; slur = 0; parsing_started = 1; } void parseroff() { parsing = 0; slur = 0; } 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; j= '0') && ((int)*c <= '9')) *c = *c +1; else return 0; } return 1; } int readnumf(num) char *num; /* read integer from string without advancing character pointer */ { int t; char* p; p =num; if (!isdigit(*p)) { event_error("Missing Number"); }; t = 0; while (((int)*p >= '0') && ((int)*p <= '9')) { t = t * 10 + (int) *p - '0'; p = p + 1; }; return (t); } int readsnumf(s) char* s; /* reads signed integer from string without advancing character pointer */ { char* p; p = s; if (*p == '-') { p = p+1; skipspace(&p); return(-readnumf(p)); } else { return(readnumf(p)); } } int readnump(p) char **p; /* read integer from string and advance character pointer */ { int t; t = 0; while (((int)**p >= '0') && ((int)**p <= '9')) { t = t * 10 + (int) **p - '0'; *p = *p + 1; }; return (t); } int readsnump(p) char** p; /* reads signed integer from string and advance character pointer */ { if (**p == '-') { *p = *p+1; skipspace(p); return(-readnump(p)); } else { return(readnump(p)); } } void readsig(a, b, sig) int *a, *b; char **sig; /* read time signature (meter) from M: field */ { int t; if ((**sig == 'C') || (**sig == 'c')) { *a = 4; *b = 4; return; }; *a = readnump(sig); if ((int)**sig != '/') { event_error("Missing / "); } else { *sig = *sig + 1; }; *b = readnump(sig); if ((*a == 0) || (*b == 0)) { event_error("Expecting fraction in form A/B"); } else { t = *b; while (t > 1) { if (t%2 != 0) { event_error("divisor must be a power of 2"); t = 1; *b = 0; } else { t = t/2; }; }; }; } void readlen(a, b, p) int *a, *b; char **p; /* read length part of a note and advance character pointer */ { int t; *a = readnump(p); if (*a == 0) { *a = 1; }; *b = 1; if (**p == '/') { *p = *p + 1; *b = readnump(p); if (*b == 0) { *b = 2; while (**p == '/') { *b = *b * 2; *p = *p + 1; }; }; }; t = *b; while (t > 1) { if (t%2 != 0) { event_warning("divisor not a power of 2"); t = 1; } else { t = t/2; }; }; } void readlen_nocheck(a, b, p) int *a, *b; char **p; /* read length part of a note and advance character pointer */ { int t; *a = readnump(p); if (*a == 0) { *a = 1; }; *b = 1; if (**p == '/') { *p = *p + 1; *b = readnump(p); if (*b == 0) { *b = 2; while (**p == '/') { *b = *b * 2; *p = *p + 1; }; }; }; t = *b; while (t > 1) { if (t%2 != 0) { /*event_warning("divisor not a power of 2"); */ t = 1; } else { t = t/2; }; }; } int ismicrotone(p,dir) char **p; int dir; { int a, b; readlen_nocheck(&a, &b, p); if (b != 1) { event_microtone(dir,a,b); return 1;} return 0; } int isclef(s, gotoctave, octave, strict) char* s; int *gotoctave, *octave; int strict; /* part of K: parsing - looks for a clef in K: field */ /* format is K:string where string is treble, bass, baritone, tenor, */ /* alto, mezzo, soprano or K:clef=arbitrary */ { int gotclef; s = s; gotclef = 0; if (strncmp(s, "bass", 4) == 0) { gotclef= 1; }; if (strncmp(s, "treble", 6) == 0) { gotclef= 1; }; if (strncmp(s, "treble+8", 8) == 0) { gotclef= 1; *gotoctave=1; *octave =1; }; if (strncmp(s, "treble-8", 8) == 0) { gotclef= 1; *gotoctave=1; *octave = -1; }; if (strncmp(s, "baritone", 8) == 0) { gotclef= 1; }; if (strncmp(s, "tenor", 5) == 0) { gotclef= 1; }; if (strncmp(s, "tenor-8", 7) == 0) { gotclef= 1; *gotoctave=1; *octave= -1; }; if (strncmp(s, "alto", 4) == 0) { gotclef= 1; }; if (strncmp(s, "mezzo", 5) == 0) { gotclef= 1; }; if (strncmp(s, "soprano", 7) == 0) { gotclef= 1; }; /* * only clef=F or clef=f is allowed, or else * we get a conflict with the key signature * indication K:F */ if (strncmp(s, "f",1) == 0 && strict==0) { gotclef = 1; } if (strncmp(s, "F",1) == 0 && strict==0) { gotclef = 1; } if (strncmp(s, "g",1) == 0 && strict==0) { gotclef = 1; } if (strncmp(s, "G",1) == 0 && strict==0) { gotclef = 1; } if (!strict && !gotclef) { gotclef = 1; event_warning("cannot recognize clef indication"); } return(gotclef); } char* readword(word, s) /* part of parsekey, extracts word from input line */ char word[]; char* s; { char* p; int i; p = s; i = 0; while ((*p != '\0') && (*p != ' ') && ((i == 0) || (*p != '='))) { if (i < 29) { word[i] = *p; i = i + 1; }; p = p + 1; }; word[i] = '\0'; return(p); } static void lcase(s) /* convert word to lower case */ char* s; { char* p; p = s; while (*p != '\0') { if (isupper(*p)) { *p = *p + 'a' - 'A'; }; p = p + 1; }; } void init_voicecode() { int i; for (i=0;i<16;i++) voicecode[i][0] = 0; voicecodes =0; } void print_voicecodes() { int i; if (voicecodes == 0) return; printf("voice mapping:\n"); for (i=0;i 15) return -1; strcpy(voicecode[voicecodes],code); voicecodes++; return voicecodes; } /* The following three functions parseclefs, parsetranspose, * parseoctave are used to parse the K: field which not * only specifies the key signature but also other descriptors * used for producing a midi file or postscript file. * * The char* word contains the particular token that * is being interpreted. If the token can be understood, * other parameters are extracted from char ** s and * s is advanced to point to the next token. */ int parseclef(s,word,gotclef,clefstr,gotoctave,octave) char** s; char* word; int *gotclef; char *clefstr; int *gotoctave,*octave; /* extracts string clef= something */ { int successful; skipspace(s); *s = readword(word, *s); successful = 0; if (casecmp(word, "clef") == 0) { skipspace(s); if (**s != '=') { event_error("clef must be followed by '='"); } else { *s = *s + 1; skipspace(s); *s = readword(clefstr, *s); if (isclef(clefstr,gotoctave,octave,0)) { *gotclef = 1; }; }; successful = 1; } else if (isclef(word,gotoctave,octave,1)) { *gotclef = 1; strcpy(clefstr, word); successful = 1; }; return successful; } int parsetranspose(s,word,gottranspose,transpose) /* parses string transpose= number */ char** s; char* word; int *gottranspose; int *transpose; { if (casecmp(word, "transpose") != 0) return 0; skipspace(s); if (**s != '=') { event_error("transpose must be followed by '='"); } else { *s = *s + 1; skipspace(s); *transpose = readsnump(s); *gottranspose = 1; }; return 1; }; int parseoctave(s,word,gotoctave,octave) /* parses string octave= number */ char** s; char* word; int *gotoctave; int *octave; { if (casecmp(word, "octave") != 0) return 0; skipspace(s); if (**s != '=') { event_error("octave must be followed by '='"); } else { *s = *s + 1; skipspace(s); *octave = readsnump(s); *gotoctave = 1; }; return 1; }; int parsename(s,word,gotname,namestring,maxsize) /* parses string name= "string" in V: command for compatability of abc2abc with abcm2ps */ char **s; char * word; int *gotname; char namestring[]; int maxsize; { int i; i = 0; if (casecmp(word, "name") != 0) return 0; skipspace(s); if (**s != '=') { event_error("name must be followed by '='"); } else { *s = *s + 1; skipspace(s); if (**s == '"') /* string enclosed in double quotes */ { namestring[i] = (char) **s; *s = *s + 1; i++; while (i < maxsize && **s != '"' && **s != '\0') {namestring[i] = (char) **s; *s = *s +1; i++; } namestring[i] = (char) **s; /* copy double quotes */ i++; namestring[i]= '\0'; } else /* string not enclosed in double quotes */ { while (i < maxsize && **s != ' ' && **s != '\0') { namestring[i] = (char) **s; *s = *s +1; i++; } namestring[i] = '\0'; } *gotname = 1; } return 1; }; int parsesname(s,word,gotname,namestring,maxsize) /* parses string name= "string" in V: command for compatability of abc2abc with abcm2ps */ char **s; char * word; int *gotname; char namestring[]; int maxsize; { int i; i = 0; if (casecmp(word, "sname") != 0) return 0; skipspace(s); if (**s != '=') { event_error("name must be followed by '='"); } else { *s = *s + 1; skipspace(s); if (**s == '"') /* string enclosed in double quotes */ { namestring[i] = (char) **s; *s = *s + 1; i++; while (i < maxsize && **s != '"' && **s != '\0') {namestring[i] = (char) **s; *s = *s +1; i++; } namestring[i] = (char) **s; /* copy double quotes */ i++; namestring[i]= '\0'; } else /* string not enclosed in double quotes */ { while (i < maxsize && **s != ' ' && **s != '\0') { namestring[i] = (char) **s; *s = *s +1; i++; } namestring[i] = '\0'; } *gotname = 1; } return 1; }; int parsemiddle(s, word, gotmiddle, middlestring, maxsize) /* parse string middle=X in V: command for abcm2ps compatibility */ char **s; char * word; int *gotmiddle; char middlestring[]; int maxsize; { int i; i = 0; if (casecmp(word, "middle") != 0) return 0; skipspace(s); if( **s != '=' ) { event_error("middle must be followed by '='"); } else { *s = *s + 1; skipspace(s); /* we really ought to check the we have a proper note name; for now, just copy non-space characters */ while (i < maxsize && **s != ' ' && **s != '\0') { middlestring[i] = (char) **s; *s = *s + 1; ++i; } middlestring[i] = '\0'; *gotmiddle = 1; } return 1; } int parsekey(str) /* parse contents of K: field */ /* this works by picking up a strings and trying to parse them */ /* returns 1 if valid key signature found, 0 otherwise */ char* str; { char* s; char word[30]; int parsed; int gotclef, gotkey, gotoctave, gottranspose; int foundmode; int transpose, octave; char clefstr[30]; char modestr[30]; char msg[80]; char* moveon; int sf = -1, minor = -1; char modmap[7]; int modmul[7]; int i, j; int cgotoctave,coctave; static char *key = "FCGDAEB"; int modeindex; s = str; transpose = 0; gottranspose = 0; octave = 0; gotkey = 0; gotoctave = 0; gotclef=0; cgotoctave=0; coctave=0; modeindex = 0; for (i=0; i<7; i++) { modmap[i] = ' '; modmul[i] = 1; }; while (*s != '\0') { parsed = parseclef(&s,word,&gotclef,clefstr,&cgotoctave,&coctave); if (!parsed) parsed = parsetranspose(&s,word,&gottranspose,&transpose); if (!parsed) parsed = parseoctave(&s,word,&gotoctave,&octave); if ((parsed == 0) && (casecmp(word, "Hp") == 0)) { sf = 2; minor = 0; gotkey = 1; parsed = 1; }; if ((parsed == 0) && (casecmp(word,"none") ==0)) { gotkey =1; parsed = 1; nokey = 1; minor =0; sf = 0; } if ((parsed == 0) && ((word[0] >= 'A') && (word[0] <= 'G'))) { gotkey = 1; parsed = 1; /* parse key itself */ sf = strchr(key, word[0]) - key - 1; j = 1; /* deal with sharp/flat */ if (word[1] == '#') { sf += 7; j = 2; } else { if (word[1] == 'b') { sf -= 7; j = 2; }; } minor = 0; foundmode = 0; if ( (int) strlen(word) == j) { /* look at next word for mode */ skipspace(&s); moveon = readword(modestr, s); lcase(modestr); for (i = 0; i<10; i++) { if (strncmp(modestr, mode[i], 3) == 0) { foundmode = 1; sf = sf + modeshift[i]; minor = modeminor[i]; modeindex = i; }; }; if (foundmode) { s = moveon; }; } else { strcpy(modestr, &word[j]); lcase(modestr); for (i = 0; i<10; i++) { if (strncmp(modestr, mode[i], 3) == 0) { foundmode = 1; sf = sf + modeshift[i]; minor = modeminor[i]; modeindex = i; }; }; if (!foundmode) { sprintf(msg, "Unknown mode '%s'", &word[j]); event_error(msg); modeindex = 0; }; }; }; if (gotkey) { if (sf > 7) { event_warning("Unusual key representation"); sf = sf - 12; } ; if (sf < -7) { event_warning("Unusual key representation"); sf = sf + 12; }; }; if ((word[0] == '^') || (word[0] == '_') || (word[0] == '=')) { if ((strlen(word) == 2) && (word[1] >= 'a') && (word[1] <= 'g')) { j = (int)word[1] - 'a'; modmap[j] = word[0]; modmul[j] = 1; parsed = 1; } else { if ((strlen(word) == 3) && (word[0] != '=') && (word[0] == word[1]) && (word[2] >= 'a') && (word[2] <= 'g')) { j = (int)word[2] - 'a'; modmap[j] = word[0]; modmul[j] = 2; parsed = 1; }; }; }; if ((parsed == 0) && (strlen(word) > 0)) { sprintf(msg, "Ignoring string '%s' in K: field", word); event_warning(msg); }; }; if (cgotoctave) {gotoctave=1; octave=coctave;} event_key(sf, str, modeindex, modmap, modmul, gotkey, gotclef, clefstr, octave, transpose, gotoctave, gottranspose); return(gotkey); } static void parsevoice(s) char *s; { int num; /* voice number */ struct voice_params vparams; char word[30]; int parsed; int coctave, cgotoctave; vparams.transpose = 0; vparams.gottranspose = 0; vparams.octave = 0; vparams.gotoctave = 0; vparams.gotclef = 0; cgotoctave=0; coctave=0; vparams.gotname = 0; vparams.gotsname = 0; vparams.gotmiddle = 0; skipspace(&s); if (isnumberp(&s) == 1) { num = readnump(&s); } else { num = interpret_voicestring(s); if(num == 0) event_error("No voice number or string in V: field"); if(num == -1) {event_error("More than 16 voices encountered in V: fields"); num =0;} skiptospace(&s); }; skipspace(&s); while (*s != '\0') { parsed = parseclef(&s,word, &vparams.gotclef, vparams.clefname, &cgotoctave,&coctave); if (!parsed) parsed = parsetranspose(&s, word, &vparams.gottranspose, &vparams.transpose); if (!parsed) parsed = parseoctave(&s, word, &vparams.gotoctave, &vparams.octave); if (!parsed) parsed = parsename(&s, word, &vparams.gotname, vparams.namestring, V_STRLEN); if (!parsed) parsed = parsesname(&s, word, &vparams.gotsname, vparams.snamestring, V_STRLEN); if (!parsed) parsed = parsemiddle(&s, word, &vparams.gotmiddle, vparams.middlestring, V_STRLEN); } if (cgotoctave) {vparams.gotoctave=1; vparams.octave=coctave;} event_voice(num, s, &vparams); /* if (gottranspose) printf("transpose = %d\n", vparams.transpose); if (gotoctave) printf("octave= %d\n", vparams.octave); if (gotclef) printf("clef= %s\n", vparams.clefstr); if (gotname) printf("parsevoice: name= %s\n", vparams.namestring); if(gotmiddle) printf("parsevoice: middle= %s\n", vparams.middlestring); */ } static void parsenote(s) char **s; /* parse abc note and advance character pointer */ { int decorators[DECSIZE]; int i, t; int mult; char accidental, note; int octave, n, m; char msg[80]; int microtone; mult = 1; microtone=0; accidental = ' '; note = ' '; for (i = 0; i= 'a') && (**s <= 'g')) { note = **s; octave = 1; *s = *s + 1; while ((**s == '\'') || (**s == ',')) { if (**s == '\'') { octave = octave + 1; *s = *s + 1; }; if (**s == ',') { sprintf(msg, "Bad pitch specifier , after note %c", note); event_error(msg); octave = octave - 1; *s = *s + 1; }; }; } else { octave = 0; if ((**s >= 'A') && (**s <= 'G')) { note = **s + 'a' - 'A'; *s = *s + 1; while ((**s == '\'') || (**s == ',')) { if (**s == ',') { octave = octave - 1; *s = *s + 1; }; if (**s == '\'') { sprintf(msg, "Bad pitch specifier ' after note %c", note + 'A' - 'a'); event_error(msg); octave = octave + 1; *s = *s + 1; }; }; }; }; if (note == ' ') { event_error("Malformed note : expecting a-g or A-G"); } else { readlen(&n, &m, s); event_note(decorators, accidental, mult, note, octave, n, m); if (microtone) event_normal_tone(); }; } char* getrep(p, out) char* p; char* out; /* look for number or list following [ | or :| */ { char* q; int digits; int done; int count; q = p; count = 0; done = 0; digits = 0; while (!done) { if (isdigit(*q)) { out[count] = *q; count = count + 1; q = q + 1; digits = digits + 1; } else { if (((*q == '-')||(*q == ','))&&(digits > 0)&&(isdigit(*(q+1)))) { out[count] = *q; count = count + 1; q = q + 1; digits = 0; } else { done = 1; }; }; }; out[count] = '\0'; return(q); } int checkend(s) char* s; /* returns 1 if we are at the end of the line 0 otherwise */ /* used when we encounter '\' '*' or other special line end characters */ { char* p; int atend; p = s; skipspace(&p); if (*p == '\0') { atend = 1; } else { atend = 0; }; return(atend); } void readstr(out, in, limit) char out[]; char **in; int limit; /* copy across alphanumeric string */ { int i; i = 0; while ((isalpha(**in)) && (i < limit-1)) { out[i] = **in; i = i + 1; *in = *in + 1; }; out[i] = '\0'; } static void parse_precomment(s) char* s; /* handles a comment field */ { char package[40]; char *p; if (*s == '%') { p = s+1; readstr(package, &p, 40); event_specific(package, p); } else { event_comment(s); }; } static void parse_tempo(place) char* place; /* parse tempo descriptor i.e. Q: field */ { char* p; int a, b; int n; int relative; char *pre_string; char *post_string; relative = 0; p = place; pre_string = NULL; if (*p == '"') { p = p + 1; pre_string = p; while ((*p != '"') && (*p != '\0')) { p = p + 1; }; if (*p == '\0') { event_error("Missing closing double quote"); } else { *p = '\0'; p = p + 1; place = p; }; }; while ((*p != '\0') && (*p != '=')) p = p + 1; if (*p == '=') { p = place; skipspace(&p); if (((*p >= 'A') && (*p <= 'G')) || ((*p >= 'a') && (*p <= 'g'))) { relative = 1; p = p + 1; }; readlen(&a, &b, &p); skipspace(&p); if (*p != '=') { event_error("Expecting = in tempo"); }; p = p + 1; } else { a = 1; b = 4; p = place; }; skipspace(&p); n = readnump(&p); post_string = NULL; if (*p == '"') { p = p + 1; post_string = p; while ((*p != '"') && (*p != '\0')) { p = p + 1; }; if (*p == '\0') { event_error("Missing closing double quote"); } else { *p = '\0'; p = p + 1; }; }; event_tempo(n, a, b, relative, pre_string, post_string); } static void preparse_words(s) char *s; /* takes a line of lyrics (w: field) and strips off */ /* any continuation character */ { int continuation; int l; /* printf("Parsing %s\n", s); */ /* strip off any trailing spaces */ l = strlen(s) - 1; while ((l>= 0) && (*(s+l) == ' ')) { *(s+l) = '\0'; l = l - 1; }; if (*(s+l) != '\\') { continuation = 0; } else { continuation = 1; /* remove continuation character */ *(s+l) = '\0'; l = l - 1; while ((l>= 0) && (*(s+l) == ' ')) { *(s+l) = '\0'; l = l - 1; }; }; event_words(s, continuation); } void init_abbreviations() /* initialize mapping of H-Z to strings */ { int i; for (i = 0; i< 'Z' - 'H'; i++) { abbreviation[i] = NULL; }; } void record_abbreviation(char symbol, char *string) /* update record of abbreviations when a U: field is encountered */ { int index; if ((symbol <'H') || (symbol > 'Z')) { return; }; index = symbol - 'H'; if (abbreviation[index] != NULL) { free(abbreviation[index]); }; abbreviation[index] = addstring(string); } char *lookup_abbreviation(char symbol) /* return string which s abbreviates */ { if ((symbol < 'H') || (symbol > 'Z')) { return(NULL); } else { return(abbreviation[symbol - 'H']); }; } void free_abbreviations() /* free up any space taken by abbreviations */ { int i; for (i=0; i 0) { event_length(denom); } else { event_error("invalid denominator"); }; }; break; }; case 'P': event_part(place); break; case 'I': event_info(place); break; case 'V': parsevoice(place); break; case 'Q': parse_tempo(place); break; case 'U': { char symbol; char container; char *expansion; skipspace(&place); if ((*place >= 'H') && (*place <= 'Z')) { symbol = *place; place = place + 1; skipspace(&place); if (*place == '=') { place = place + 1; skipspace(&place); if (*place == '!') { place = place + 1; container = '!'; expansion = place; while ((!iscntrl(*place)) && (*place != '!')) { place = place +1; }; if (*place != '!') { event_error("No closing ! in U: field"); }; *place = '\0'; } else { container = ' '; expansion = place; while (isalnum(*place)) { place = place + 1; }; *place = '\0'; }; if (strlen(expansion) > 0) { record_abbreviation(symbol, expansion); event_abbreviation(symbol, expansion, container); } else { event_error("Missing term in U: field"); }; } else { event_error("Missing '=' U: field ignored"); }; } else { event_warning("only 'H' - 'Z' supported in U: field"); }; }; break; case 'w': preparse_words(place); break; case 'd': /* decoration line in abcm2ps */ break; case 's': break; default: event_field(key, place); }; if (iscomment) { parse_precomment(comment); }; } char* parseinlinefield(p) char* p; /* parse field within abc line e.g. [K:G] */ { char* q; event_startinline(); q = p; while ((*q != ']') && (*q != '\0')) { q = q + 1; }; if (*q == ']') { *q = '\0'; parsefield(*p, p+2); q = q + 1; } else { event_error("missing closing ]"); parsefield(*p, p+2); }; event_closeinline(); return(q); } static void parsemusic(field) char* field; /* parse a line of abc notes */ { char* p; char* comment; char endchar; int iscomment; int starcount; int i; char playonrep_list[80]; int decorators[DECSIZE]; event_startmusicline(); endchar = ' '; comment = field; iscomment = 0; while ((*comment != '\0') && (*comment != '%')) { comment = comment + 1; }; if (*comment == '%') { iscomment = 1; *comment = '\0'; comment = comment + 1; }; p = field; skipspace(&p); while(*p != '\0') { if (((*p >= 'a') && (*p <= 'g')) || ((*p >= 'A') && (*p <= 'G')) || (strchr("_^=", *p) != NULL) || (strchr(decorations, *p) != NULL)) { parsenote(&p); } else { switch(*p) { case '"': { struct vstring gchord; p = p + 1; initvstring(&gchord); while ((*p != '"') && (*p != '\0')) { addch(*p, &gchord); p = p + 1; }; if (*p == '\0') { event_error("Guitar chord name not properly closed"); } else { p = p + 1; }; event_gchord(gchord.st); freevstring(&gchord); break; }; case '|': p = p + 1; switch(*p) { case ':': event_bar(BAR_REP, ""); p = p + 1; break; case '|' : event_bar(DOUBLE_BAR, ""); p = p + 1; break; case ']' : event_bar(THIN_THICK, ""); p = p + 1; break; default : p = getrep(p, playonrep_list); event_bar(SINGLE_BAR, playonrep_list); }; break; case ':': p = p + 1; switch(*p) { case ':': event_bar(DOUBLE_REP, ""); p = p + 1; break; case '|': p = p + 1; p = getrep(p, playonrep_list); event_bar(REP_BAR, playonrep_list); break; default: event_error("Single colon in bar"); }; break; case ' ': event_space(); skipspace(&p); break; case TAB: event_space(); skipspace(&p); break; case '(': p = p + 1; { int t, q, r; t = 0; q = 0; r = 0; t = readnump(&p); if ((t != 0) && (*p == ':')) { p = p + 1; q = readnump(&p); if (*p == ':') { p = p + 1; r = readnump(&p); }; }; if (t == 0) { event_sluron(1); } else { event_tuple(t, q, r); }; }; break; case ')': p = p + 1; event_sluroff(0); break; case '{': p = p + 1; event_graceon(); ingrace=1; break; case '}': p = p + 1; event_graceoff(); ingrace=0; break; case '[': p = p + 1; switch(*p) { /* following lines are now redundant */ /* case '1': p = p + 1; event_rep1(); break; case '2': p = p + 1; event_rep2(); break; */ case '|': p = p + 1; event_bar(THICK_THIN, ""); break; default: if (isdigit(*p)) { p = getrep(p, playonrep_list); event_playonrep(playonrep_list); } else { if (isalpha(*p) && (*(p+1) == ':')) { p = parseinlinefield(p); } else { event_chordon(chorddecorators); parserinchord = 1; }; }; break; }; break; case ']': p = p + 1; readlen(&chord_n, &chord_m, &p); event_chordoff(chord_n,chord_m); parserinchord = 0; for (i = 0; i