/* * yaps - program to convert abc files to PostScript. * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA * */ /* yapstree.c - back-end for abc parser. */ /* generates a data structure suitable for typeset music */ #define VERSION "1.86 December 10 2020 yaps" #include #ifdef USE_INDEX #define strchr index #endif /* for Microsoft VC 6++ or higher */ #ifdef _MSC_VER #define ANSILIBS #define snprintf _snprintf #endif #ifdef ANSILIBS #include #include #include #else char* strchr(); #endif #include "abc.h" #include "parseabc.h" #include "parser2.h" #include "structs.h" #include "drawtune.h" extern void setscaling(char *s); extern void font_command(); extern void setup_fonts(); extern void printtune(struct tune *t); extern void set_keysig(struct key *k, struct key *newval); /* forward definition */ void event_clef(char* clefstr, cleftype_t * new_clef); programname fileprogram = YAPS; extern int oldchordconvention; /* for handling +..+ chords */ struct voice* cv; struct tune thetune; char outputname[MAX_OUTPUTNAME + 1]; /* [JA] 2020-11-01 */ char outputroot[MAX_OUTPUTROOT + 1]; char matchstring[256]; int fileopen; int xinhead; int xinbody; int suppress; int debugging; int pagenumbering; int separate_voices; int print_xref; int landscape; int barnums,nnbars; extern int gchords_above; extern int decorators_passback[DECSIZE]; /* a kludge for passing information from the event_handle_instruction to parsenote in parseabc.c */ enum linestattype {fresh, midmusic, endmusicline, postfield}; enum linestattype linestat; int dummydecorator[DECSIZE]; /* used in event_chord */ void setfract(f, a, b) struct fract* f; int a, b; /* assign value to fraction */ { f->num = a; f->denom = b; } void reducef(f) struct fract* f; /* reducef fraction to smallest terms */ { int t, n, m; /* find HCF using Euclid's algorithm */ if (f->num > f->denom) { n = f->num; m = f->denom; } else { n = f->denom; m = f->num; }; while (m != 0) { t = n % m; n = m; m = t; }; f->num = f->num/n; f->denom = f->denom/n; } static struct fract* newfract(int a, int b) /* create an initialized fraction */ { struct fract* f; f = (struct fract*)checkmalloc(sizeof(struct fract)); f->num = a; f->denom = b; return(f); } static timesig_details_t *newtimesig(void) /* create an initialized time signature */ { timesig_details_t *new_timesig; new_timesig = (timesig_details_t *)checkmalloc (sizeof (timesig_details_t)); init_timesig(new_timesig); return new_timesig; } static struct slurtie* newslurtie() /* create a new slur/tie data structure */ { struct slurtie* f; f = (struct slurtie*)checkmalloc(sizeof(struct slurtie)); f->begin = NULL; f->end = NULL; f->crossline = 0; return(f); } static struct atempo* newtempo(int count, int n, int m, int relative, char *pre, char *post) /* create an initialized tempo data structure */ { struct atempo* t; t = (struct atempo*)checkmalloc(sizeof(struct atempo)); t->count = count; t->basenote.num = n; t->basenote.denom = m; t->relative = relative; if (pre == NULL) { t->pre = NULL; } else { t->pre = addstring(pre); }; if (post == NULL) { t->post = NULL; } else { t->post = addstring(post); }; return(t); } static struct vertspacing* newvertspacing() /* create new vertspacing structure */ { struct vertspacing* p; p = (struct vertspacing*)checkmalloc(sizeof(struct vertspacing)); p->height = 0.0; p->descender = 0.0; p->yend = 0.0; p->yinstruct = 0.0; p->ygchord = 0.0; p->ywords = 0.0; return(p); } static struct tuple* newtuple(int n, int q, int r, int label) /* create tuple data structure */ { struct tuple* f; f = (struct tuple*)checkmalloc(sizeof(struct tuple)); f->n = n; f->q = q; f->r = r; f->label = label; f->beamed = 1; return(f); } static struct chord* newchord() /* create chord data structure */ { struct chord* f; f = (struct chord*)checkmalloc(sizeof(struct chord)); f->ytop = 0; f->ybot = 0; return(f); } cleftype_t *newclef (cleftype_t * source_clef) /* create and initialize clef data structure */ { cleftype_t *f; /* [JA] 2020-10-19 */ f = (cleftype_t *) checkmalloc (sizeof (cleftype_t)); copy_clef (f, source_clef); return (f); } struct key* newkey(char* name, int sharps, char accidental[], int mult[]) /* create and initialize key signature */ { struct key* k; int i; k = (struct key*)checkmalloc(sizeof(struct key)); k->name = addstring(name); k->sharps = sharps; for (i=0; i<7; i++) { k->map[i] = accidental[i]; k->mult[i] = mult[i]; }; return(k); } void init_llist(struct llist* l) /* initialize a linked list */ { l->first = NULL; l->last = NULL; l->place = NULL; } void addtolist(struct llist* p, void* item) /* append an item to a linked list */ { struct el* x; x = (struct el*)checkmalloc(sizeof(struct el)); x->next = NULL; x->datum = item; if (p->first == NULL) { p->first = x; p->last = x; } else { p->last->next = x; p->last = x; }; } void* firstitem(struct llist* p) /* find the first item in the list */ /* also initialize for a traversal using nextitem() */ { if (p == NULL) { return(NULL); }; p->place = p->first; if (p->place == NULL) { return(NULL); } else { return(p->place->datum); }; } void* nextitem(struct llist* p) /* return 'next' item in the list. Successive calls return successive */ /* items or NULL after the end of the list has been reached. firstitem() */ /* must be called prior to the first call to nextitem() */ { if (p->place == NULL) { return(NULL); } else { p->place = p->place->next; if (p->place == NULL) { return(NULL); } else { return(p->place->datum); }; }; } void freellist(struct llist* l) /* frees up all dynamically allocated memory used to build the linked list */ { void* p; struct el* e; struct el* olde; /* printf("freellist\n"); */ if (l != NULL) { p = firstitem(l); while (p != NULL) { free(p); p = nextitem(l); }; e = l->first; while (e != NULL) { olde = e; e = e->next; free(olde); }; init_llist(l); }; } static void closebeam(struct voice* v) /* called after a run of notes to be beamed together */ { struct note* n; struct feature* ft; int stemup; int ingrace; if (cv->tuplenotes > 0) { cv->thistuple->beamed = 0; }; if (v->beamroot == v->beamend) { ft = v->beamroot; n = ft->item.voidptr; n->beaming = single; v->beamroot = NULL; return; }; if (v->beammax + v->beammin > 2*4) { stemup = 0; } else { stemup = 1; }; ft = v->beamroot; ingrace = 0; while ((ft != NULL) && (ft != v->beamend)) { switch (ft->type) { case NOTE: if (ingrace == 0) { n = ft->item.voidptr; n->stemup = stemup; }; break; case GRACEON: ingrace = 1; break; case GRACEOFF: ingrace = 0; break; default: break; }; ft = ft->next; }; if (ft == v->beamend) { n = ft->item.voidptr; n->stemup = stemup; n->beaming = endbeam; } else { printf("closebeam: internal data error\n"); exit(1); }; v->beamroot = NULL; } static void closegracebeam(struct voice* v) /* called after a run of grace notes to be beamed together */ { struct note* n; struct feature* ft; ft = v->gracebeamend; if (ft == NULL) { event_error("Missing grace notes"); } else { n = ft->item.voidptr; if (v->gracebeamroot == v->gracebeamend) { n->beaming = single; } else { n->beaming = endbeam; }; }; } static void insertnote(struct feature* chordplace, struct feature* newfeature) /* place NOTE in decreasing pitch order within chord */ { struct note* n; struct note* newnote; struct feature* f; struct feature* previous; int foundplace; newnote = newfeature->item.voidptr; previous = chordplace; f = chordplace->next; foundplace = 0; n = NULL; while ((f != NULL)&&(f->type==NOTE)&&(foundplace == 0)) { n = f->item.voidptr; if (newnote->y > n->y) { foundplace = 1; } else { previous = f; f = f->next; }; }; /* printvoiceline in drawtune.c expects the gchord or * instructions to be associated with the first note in * chord. If the notes are reordered then we need * to move these fields. * if previous == chordplace then move n->gchords and * n->instructions to newnote->gchords and newnote->instructions */ if (previous == chordplace && n != NULL) { newnote->gchords = n->gchords; newnote->instructions = n->instructions; n->gchords = NULL; n->instructions = NULL; } newfeature->next = previous->next; previous->next = newfeature; if (newfeature->next == NULL) { cv->last = newfeature; }; } static void beamitem(featuretype mytype, void* newitem, struct feature* x) /* This routine is responsible for working out which notes are to be */ /* beamed together and recording this in the note structure record */ { struct note* n; /* deal with beaming here */ if (cv->ingrace) { if ((mytype == NOTE) && (newitem != NULL)) { n = newitem; n->stemup = 1; if (cv->gracebeamroot == NULL) { cv->gracebeamroot = x; cv->gracebeamend = x; n->beaming = startbeam; } else { cv->gracebeamend = x; n->beaming = midbeam; }; }; } else { if (cv->beamroot != NULL) { switch (mytype) { case NOTE: n = newitem; if (n->base_exp >= -2) { n->beaming = single; closebeam(cv); } else { /* extend beam */ if (n->y > cv->beammax) { cv->beammax = n->y; }; if (n->y < cv->beammin) { cv->beammin = n->y; }; cv->beamend = x; n->beaming = midbeam; }; break; case KEY: case REST: case NOBEAM: 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 TUPLE: case MUSICSTOP: /* closebeam */ closebeam(cv); break; default: break; }; } else { if (mytype == NOTE) { n = newitem; if (n->base_exp >= -2) { n->beaming = single; } else { n->beaming = startbeam; cv->beammax = n->y; cv->beammin = n->y; cv->beamroot = x; cv->beamend = x; }; }; }; }; } /* initialize a feature struct with empty values */ static void init_feature (struct feature *x, featuretype mytype) { x->next = NULL; x->type = mytype; x->item.voidptr = NULL; x->xleft = 0; x->xright = 0; x->yup = 0; x->ydown = 0; } static struct feature *addnumberfeature (featuretype mytype, int number) /* handles numeric feature types * may mark the end of a beamed section, so still need to call * beamitem() on these */ { struct feature *x; if (cv == NULL) { printf ("ERROR: no current voice in addfeature(status,type=%d\n", mytype); //printf("status->inhead = %d status->inbody = %d\n", status->inhead, status->inbody); exit (0); } x = (struct feature *)checkmalloc (sizeof (struct feature)); init_feature (x, mytype); x->item.number = number; if (cv->first == NULL) { cv->first = x; cv->last = x; beamitem (mytype, NULL, x); } else { if ((cv->last == NULL) || (cv->last->next != NULL)) { printf ("expecting NULL at list end!\n"); exit (0); } cv->last->next = x; cv->last = x; beamitem (mytype, NULL, x); } return (x); } static struct feature* addfeature(featuretype mytype, void* newitem) /* append a new data element to the linked list for the current voice */ /* The element can be a note or lots of other things */ { struct voice* p; struct feature* x; p = cv; /* printf("in addfeature type=%d\n", mytype); */ if (cv == NULL) { printf("ERROR: no current voice in addfeature type=%d\n", mytype); printf("xinhead = %d xinbody = %d\n", xinhead, xinbody); exit(0); }; x = (struct feature*)checkmalloc(sizeof(struct feature)); init_feature (x, mytype); x->item.voidptr = newitem; if (cv->first == NULL) { cv->first = x; cv->last = x; beamitem(mytype, newitem, x); } else { if ((cv->last == NULL)||(cv->last->next != NULL)) { printf("expecting NULL at list end!\n"); exit(0); }; if ((cv->inchord)&&(mytype==NOTE)) { insertnote(cv->chordplace, x); } else { cv->last->next = x; cv->last = x; beamitem(mytype, newitem, x); }; }; return(x); } struct llist* newlist() /* create and initialize a new linked list */ { struct llist* l; l = (struct llist*)checkmalloc(sizeof(struct llist)); init_llist(l); return(l); } static int notenum(int octave, char ch, cleftype_t * clef) /* converts note to number for stave position */ /* note E is zero (bottom line of stave) */ { int n; n = 1 + (7 + ch - 'c') % 7 + 7 * (octave - 1); /* apply shift associated with type of clef */ switch (clef->basic_clef) { default: case basic_clef_treble: case basic_clef_auto: case basic_clef_perc: case basic_clef_none: case basic_clef_undefined: /* no shift */ break; case basic_clef_bass: n = n + 8; break; case basic_clef_alto: n = n + 4; break; } /* apply any shift from the placing of the clef */ n = n + (2 * clef->staveline); /* We ignore octave shift from the clef. This is understood as an * instruction to the player to play in a different octave, but * the written notes do not change position. */ return (n); } int count_dots(int *base, int *base_exp, int n, int m) /* convert fraction to 2^base_exp followed by dots */ /* previously used 1/base instead of 2^base_exp */ /* -1 indicates a note value which is impossible to represent */ { int dots; int start; int a, b; *base = 1; /* set default value if we fail */ *base_exp = 0; if ((n<1)||(m<1)) { return(-1); }; /* check denominator is power of 2 */ a = m; while (a>1) { if (a%2 == 1) { return(-1); }; a = a/2; }; dots = 0; start = m; while (start < n) { start = start*2; *base_exp = *base_exp + 1; }; while (start > n) { start = start/2; *base_exp = *base_exp - 1; }; if (start == 0) { printf("Problem with %d / %d\n", n, m); exit(0); }; *base = m/start; a = n - start; b = start; while (a>0) { dots = dots + 1; b = b/2; a = a - b; if (a< 0) { return(-1); }; }; return(dots); } static char* decstring(int decorators[]) /* creates a string of decorators (ornament, staccato, roll, up-bow etc.) */ /* from a boolean array */ { int i, j; char decs[DECSIZE+1]; j = 0; for (i=0; ilen, a, b); reducef(&n->len); n->dots = count_dots(&n->base, &n->base_exp, n->len.num, n->len.denom); /* if (n->dots == -1) { event_error("Illegal note length"); }; */ n->accents = decstring(decorators); n->accidental = xaccidental; n->acc_offset = 0; n->fliphead = 0; n->mult = xmult; n->octave = xoctave; n->pitch = xnote; n->y = notenum(xoctave, xnote, clef); if (n->y < 4) { n->stemup = 1; } else { n->stemup = 0; }; n->beaming = single; /* initial default value */ n->syllables = NULL; if (cv->ingrace) { n->gchords = NULL; n->instructions = NULL; } else { n->gchords = cv->gchords_pending; cv->gchords_pending = NULL; n->instructions = cv->instructions_pending; cv->instructions_pending = NULL; }; n->stemlength = 0.0; return(n); } static struct rest* newrest(int a, int b, int multi) /* create and set up a new rest structure */ { struct rest* n; n = (struct rest*)checkmalloc(sizeof(struct rest)); setfract(&n->len, a, b); n->dots = count_dots(&n->base, &n->base_exp, a, b); if (n->dots == -1) { event_error("Illegal rest length"); }; n->multibar = multi; if (cv->ingrace) { n->gchords = NULL; n->instructions = NULL; } else { n->gchords = cv->gchords_pending; cv->gchords_pending = NULL; n->instructions = cv->instructions_pending; cv->instructions_pending = NULL; }; return(n); } static void addunits(f, n, m) struct fract* f; int n, m; /* add n/m to fraction pointed to by f */ { f->num = n*f->denom*(cv->unitlen.num) + f->num*(m*cv->unitlen.denom); f->denom = (m*cv->unitlen.denom)*f->denom; reducef(f); } static void addfractions(f,n,m) struct fract* f; int n,m; { f->num = n*f->denom + f->num*m; f->denom = m*f->denom; reducef(f); } static struct voice* newvoice(int n) /* create and set up a new voice data structure */ { struct voice* v; v = (struct voice*)checkmalloc(sizeof(struct voice)); v->first = NULL; v->last = NULL; v->voiceno = n; v->octaveshift = thetune.octaveshift; setfract(&v->unitlen, thetune.unitlen.num, thetune.unitlen.denom); v->changetime = 0; v->inslur = 0; v->ingrace = 0; v->inchord = 0; v->tuplenotes = 0; v->thistuple = NULL; v->tuple_count = 0; v->brokenpending = -1; v->tiespending = 0; v->slurpending = 0; v->slurcount = 0; v->barno = 0; v->barchecking = thetune.barchecking; setfract (&v->barlen, thetune.timesig.num, thetune.timesig.denom); v->clef = newclef(&thetune.clef); if (thetune.keysig == NULL) { printf("Trying to set up voice with no key signature\n"); exit(0); } else { v->keysig = newkey(thetune.keysig->name, thetune.keysig->sharps, thetune.keysig->map, thetune.keysig->mult); }; v->tempo = NULL; setfract(&v->barcount, 0, 1); copy_timesig(&v->timesig, &thetune.timesig); v->lastnote = NULL; v->laststart = NULL; v->lastend = NULL; v->line = header; v->thisstart = NULL; v->thisend = NULL; v->gchords_pending = NULL; v->instructions_pending = NULL; v->beamed_tuple_pending = 0; v->linestart = NULL; v->lineend = NULL; v->more_lyrics = 0; v->lyric_errors = 0; v->thischord = NULL; v->chordplace = NULL; v->beamroot = NULL; v->beamend = NULL; v->gracebeamroot = NULL; v->gracebeamend = NULL; return(v); } static void setvoice(int n) /* set current voice to voice n. If voice n does not exist, create it */ { struct voice* v; struct el* l; int done; if (thetune.voices.first == NULL) { cv = newvoice(n); v = cv; addtolist(&thetune.voices, (void*)v); } else { l = thetune.voices.first; done = 0; while ((done == 0) && (l != NULL)) { if (((struct voice*)l->datum)->voiceno == n) { done = 1; cv = (struct voice*)l->datum; } else { l = l->next; }; }; if (done == 0) { cv = newvoice(n); v = cv; addtolist(&thetune.voices, (void*)v); }; }; } static void init_tune(struct tune* t, int x) /* initialize tune structure */ { t->no = x; t->octaveshift = 0; init_llist(&t->title); t->composer = NULL; t->origin = NULL; t->parts = NULL; init_llist(&t->notes); init_llist(&t->voices); init_timesig(&t->timesig); setfract(&t->unitlen, 0, 1); t->cv = NULL; t->keysig = NULL; init_new_clef (&t->clef); get_standard_clef ("treble", &t->clef); /* default to treble clef */ t->tempo = NULL; init_llist(&t->words); }; static void freekey(struct key* k) /* free up memory allocated for key data structure */ { if (k != NULL) { if (k->name != NULL) { free(k->name); }; free(k); }; } static void freetempo(struct atempo *t) /* free up memory allocated for temp data structure */ { if (t->pre != NULL) { free(t->pre); }; if (t->post != NULL) { free(t->post); }; } static void freefeature(void* item, featuretype type) /* free memory allocated for feature in voice */ { struct note *n; struct rest *r; switch(type) { case NOTE: case CHORDNOTE: n = item; if (n->accents != NULL) { free(n->accents); }; if (n->syllables != NULL) { freellist(n->syllables); free(n->syllables); }; if (n->gchords != NULL) { freellist(n->gchords); free(n->gchords); }; if (n->instructions != NULL) { freellist(n->instructions); free(n->instructions); }; free(n); break; case REST: r = item; if (r->gchords != NULL) { freellist(r->gchords); free(r->gchords); }; if (r->instructions != NULL) { freellist(r->instructions); free(r->instructions); }; free(r); break; case KEY: freekey(item); break; case TEMPO: freetempo(item); break; case CLEF: case TIME: case PART: case CHORDON: case TUPLE: case SLUR_ON: case TIE: case PLAY_ON_REP: case LEFT_TEXT: case PRINTLINE: if (item != NULL) { free(item); }; break; default: break; }; } static void freevoice(struct voice* v) /* free up memory allocated for voice data structure and voice data */ { struct feature* ft; struct feature* oldft; ft = v->first; while (ft != NULL) { freefeature(ft->item.voidptr, ft->type); oldft = ft; ft = ft->next; free(oldft); }; if (v->keysig != NULL) { freekey(v->keysig); v->keysig = NULL; }; if (v->tempo != NULL) { freetempo(v->tempo); free (v->tempo); v->tempo = NULL; }; if (v->clef != NULL) { free(v->clef); }; v->clef = NULL; } static void freetune(struct tune* t) /* free up all dynamically allocated memory associated with tune */ { struct voice* v; if (t->composer != NULL) { free(t->composer); t->composer = NULL; }; if (t->origin != NULL) { free(t->origin); t->origin = NULL; }; if (t->parts != NULL) { free(t->parts); t->parts = NULL; }; freellist(&t->title); freellist(&t->notes); if (t->keysig != NULL) { freekey(t->keysig); t->keysig = NULL; }; if (t->tempo != NULL) { freetempo(t->tempo); free(t->tempo); t->tempo = NULL; }; v = firstitem(&t->voices); while (v != NULL) { freevoice(v); v = nextitem(&t->voices); }; freellist(&t->voices); freellist(&t->words); } static int checkmatch(int refno) /* compares current reference number against list of numbers */ /* following -e argument */ /* returns 1 if tune has been selected and 0 otherwise */ { int select; int n1, n2; char* place; place = matchstring; if (strlen(matchstring)==0) { select = 1; } else { select = 0; while ((select==0)&&(*place >= '0')&&(*place <='9')) { n1 = readnump(&place); if (n1 == refno) { select = 1; } else { if (*place == ',') { place = place+1; } else { if (*place == '-') { place = place+1; n2 = readnump(&place); if ((refno >= n1)&&(refno <= n2)) { select = 1; } else { if (*place == ',') { place = place+1; }; }; }; }; }; }; if ((select==0)&&(*place != '\0')) { event_warning("Number list after -e not fully parsed"); }; }; return(select); } void event_init(argc, argv, filename) int argc; char* argv[]; char** filename; /* initialization routine - called once at the start of the program */ /* interprets the parameters in argv */ { char* place; int filearg; int refmatch; int papsize, margins, newscale; int ier; int j; if (getarg("-ver",argc, argv) != -1) { printf("%s\n",VERSION); exit(0); } if (getarg("-d", argc, argv) != -1) { debugging = 1; } else { debugging = 0; }; if (getarg("-E", argc, argv) != -1) { eps_out = 1; } else { eps_out = 0; }; if (getarg("-OCC",argc,argv) != -1) oldchordconvention=1; if (getarg("-V", argc, argv) != -1) { separate_voices = 1; } else { separate_voices = 0; }; if (getarg("-x", argc, argv) != -1) { print_xref = 1; } else { print_xref = 0; }; if (getarg("-N", argc, argv) != -1) { pagenumbering = 1; } else { pagenumbering = 0; }; if (getarg("-l", argc, argv) != -1) { landscape = 1; } else { landscape = 0; }; newscale = getarg("-s", argc, argv); if ((newscale != -1) && (argc >= newscale+1)) { /* [SS] 2015-02-22 */ setscaling(argv[newscale]); } else { setscaling(""); }; margins = getarg("-M", argc, argv); if ((margins != -1) && (argc >= margins)) { setmargins(argv[margins]); } else { setmargins(""); }; papsize = getarg("-P", argc, argv); if ((papsize != -1) && (argc >= papsize)) { setpagesize(argv[papsize]); } else { setpagesize(""); }; barnums = getarg("-k",argc,argv); ier = 0; if ((barnums != -1) && (argc > barnums)) ier = sscanf(argv[barnums],"%d",&nnbars); if ((barnums != -1) && (ier <1)) nnbars = 1; refmatch = getarg("-e", argc, argv); if (refmatch == -1) { *matchstring = '\0'; } else { if (strlen(argv[refmatch]) < 255) { strcpy(matchstring, argv[refmatch]); } else { event_error("Exceeded character limit for -e string"); exit(1); }; }; if ((getarg("-h", argc, argv) != -1) || (argc < 2)) { printf("yaps version %s\n",VERSION); printf("Usage: yaps []\n"); printf(" possible options are -\n"); printf(" -ver :prints version number and exits\n"); printf(" -d : debug - display data structure\n"); printf(" -e : draw tunes with reference numbers in list\n"); printf(" list is comma-separated and may contain ranges\n"); printf(" but no spaces e.g. 1,3,7-20\n"); printf(" -E : generate Encapsulated PostScript\n"); printf(" -l : landscape mode\n"); printf(" -M XXXxYYY : set margin sizes in points\n"); printf(" 28.3 points = 1cm, 72 points = 1 inch\n"); printf(" -N : add page numbering\n"); printf(" -k [nn] : number every nn bars\n"); printf(" -o : specify output file\n"); printf(" -P ss : paper size; 0 is A4, 1 is US Letter\n"); printf(" or XXXxYYY to set size in points\n"); printf(" -s XX : scaling factor (default is 0.7)\n"); printf(" -V : separate voices in multi-voice tune\n"); printf(" -x : print tune number in X: field\n"); printf(" -OCC : old chord convention (eg. +CE+)\n"); printf("Takes an abc music file and converts it to PostScript.\n"); printf("If no output filename is given, then by default it is\n"); printf("the input filename but with extension .ps .\n"); exit(0); } else { *filename = argv[1]; }; fileopen = 0; filearg = getarg("-o", argc, argv); /* beware of security risk from buffer overflows here [JA] 2020-11-01*/ if (filearg != -1) { if (strlen(argv[filearg]) > MAX_OUTPUTROOT) /* [JA] 2020-11-01 */ { printf("Specified output filename exceeds limit.\n"); exit(1); } #ifdef NO_SNPRINTF sprintf(outputname, "%s",argv[filearg]); /* [SS] 2020-11-01 */ #else snprintf(outputname, MAX_OUTPUTROOT,"%s",argv[filearg]); #endif } else { if (strlen(argv[1]) > MAX_OUTPUTROOT) { printf("Implied output filename exceeds limit.\n"); exit(1); } #ifdef NO_SNPRINTF sprintf(outputname,"%s", argv[1]); /* [SS] 2020-11-01 */ #else snprintf(outputname,MAX_OUTPUTROOT,"%s", argv[1]); #endif place = strchr(outputname, '.'); if (place == NULL) { strcat(outputname, ".ps"); } else { strcpy(place, ".ps"); }; if (strcmp(argv[1], outputname)==0) { printf("argument must be abc file, not PostScript file\n"); exit(1); }; }; /* create filename root for EPS output */ strcpy(outputroot, outputname); place = strchr(outputroot, '.'); if (place != NULL) { *place = '\0'; }; xinbody =0; xinhead = 0; suppress = 0; init_tune(&thetune, -1); setup_fonts(); /* open_output_file(outputname); */ for (j=0;jinchord) { event_error("incomplete chord at end of voice"); v->inchord = 0; }; if (v->brokenpending != -1) { event_error("incomplete broken rhythm at end of voice"); v->brokenpending = -1; }; if ((v->slurcount > 0)||(v->slurpending)) { event_error("incomplete slur at end of voice"); v->slurcount = 0; v->slurpending = 0; }; if (v->tiespending) { event_error("incomplete ties at end of voice"); v->tiespending = 0; }; if (v->gchords_pending != NULL) { freellist(v->gchords_pending); free(v->gchords_pending); v->gchords_pending = NULL; }; if (v->instructions_pending != NULL) { freellist(v->instructions_pending); free(v->instructions_pending); v->instructions_pending = NULL; }; if (v->tuplenotes > 0) { event_error("incomplete tuple at end of voice"); v->tuplenotes = 0; }; } static void check_tune_end(struct tune* t) /* check that all voices have been completed properly */ { struct voice* v; v = firstitem(&t->voices); while (v != NULL) { check_voice_end(v); v = nextitem(&t->voices); }; } void event_eof() /* end of input file has been encountered */ { if (xinbody) { check_tune_end(&thetune); printtune(&thetune); }; freetune(&thetune); close_output_file(); } void event_blankline() /* A blank line has been encountered */ { if (xinbody) { check_tune_end(&thetune); printtune(&thetune); }; freetune(&thetune); xinbody = 0; xinhead = 0; suppress = 0; parseroff(); } void event_text(p) /* Text outside an abc tune has been encountered */ char *p; { } void event_x_reserved(p) char p; { } void event_abbreviation(symbol, string, container) /* abbreviation declaratiion - handled by parser. Ignore it here */ char symbol; char *string; char container; { } void event_acciaccatura() { /* does nothing but outputs a / in toabc.c */ return; } /* [SS] 2015-03-23 */ void event_start_extended_overlay() { event_error("extended overlay not implemented in yaps"); } void event_stop_extended_overlay() { event_error("extended overlay not implemented in yaps"); } void event_split_voice() { addnumberfeature(SPLITVOICE, lineno); event_error("voice split not implemented in yaps"); } void event_tex(s) char *s; /* A TeX command has been found in the abc */ { } void event_linebreak() /* A linebreak has been encountered */ { /* [SS] 2015-11-15 * changed (void*) to (int *) */ if (xinbody) { addnumberfeature(LINENUM, lineno); }; } static void tidy_ties() /* create CLOSE_TIE features for ties straddling two lines of music */ /* CLOSE_TIE appears in the new music line */ { struct feature* ft; struct slurtie* s; int i; for (i=0; itiespending; i++) { ft = cv->tie_place[i]; /* pointer to TIE feature */ s = ft->item.voidptr; addfeature(CLOSE_TIE, s); }; } void event_startmusicline() /* We are at the start of a line of abc notes */ { voice_context_t *parser_voice; parser_voice = &voicecode[voicenum - 1]; cv->linestart = addnumberfeature(MUSICLINE, 0); if (cv->more_lyrics != 0) { event_error("Missing continuation w: field"); cv->more_lyrics = 0; }; if ((cv->line == header) || (cv->line == newline)) { addfeature(CLEF, newclef(cv->clef)); addfeature(KEY, newkey(cv->keysig->name, cv->keysig->sharps, cv->keysig->map, cv->keysig->mult)); if ((cv->line == header)||(cv->changetime)) { timesig_details_t *timesig; timesig = newtimesig(); copy_timesig(timesig, &parser_voice->timesig); addfeature (TIME, timesig); cv->changetime = 0; }; cv->line = midline; tidy_ties(); }; cv->lineend = NULL; } static void divide_ties() /* mark unresolved ties and slurs as straddling two lines of music */ { struct feature* ft; struct slurtie* s; int i; for (i=0; itiespending; i++) { ft = cv->tie_place[i]; /* pointer to TIE feature */ s = ft->item.voidptr; s->crossline = 1; }; for (i=0; islurcount; i++) { s = cv->slur_place[i]; s->crossline = 1; }; } /* A score_linebreak has been encountered [JA] 2020-10-07*/ void event_score_linebreak (char ch) { if (xinbody) { /* end current score line - code from endmusicline */ cv->lineend = addnumberfeature (MUSICSTOP, 0); addfeature (PRINTLINE, newvertspacing ()); cv->line = newline; divide_ties (); /* start new score line - startmusicline */ event_startmusicline (); } } void event_endmusicline(endchar) char endchar; /* We are at the end of a line of abc notes */ { cv->lineend = addnumberfeature(MUSICSTOP, 0); if ((endchar == ' ') || (endchar == '!')) { addfeature(PRINTLINE, newvertspacing()); cv->line = newline; divide_ties(); }; } void event_error(s) char *s; /* report any error message */ { printf("Error in line %d : %s\n", lineno, s); } void event_warning(s) char *s; /* report any warning message */ { printf("Warning in line %d : %s\n", lineno, s); } void event_comment(s) char *s; /* A comment has been encountered in the input */ { } int make_open() /* called as fileopen = make_open() */ /* if file is not already open, open it and set fileopen */ { if (fileopen == 0) { open_output_file(outputname, (void*)NULL); fileopen = 1; }; return(1); } void event_specific(p, str) char *p; /* first word after %% */ char *str; /* string following first word */ /* The special comment %% has been found */ /* abc2midi uses it for the %%MIDI commands */ /* yaps implements some of the abc2ps commands */ { char* s; double vspace; char units[80]; int count; /* ensure file has been opened since most commands require this */ if (!eps_out) { fileopen = make_open(); }; s = str; skipspace(&s); if (strcmp(p, "newpage") == 0) { if (xinbody == 0) { if (fileopen) { newpage(); }; } else { addfeature(NEWPAGE, (void*)NULL); }; }; if (strcmp(p, "text") == 0) { if (xinbody == 0) { if (fileopen) { lefttext(s); }; } else { addfeature(LEFT_TEXT, addstring(s)); }; }; if ((strcmp(p, "centre") == 0) || (strcmp(p, "center") == 0)) { if (xinbody == 0) { if (fileopen) { centretext(s); }; } else { addfeature(CENTRE_TEXT, addstring(s)); }; }; if (strcmp(p, "vskip") == 0) { count = sscanf(s, "%lf%s", &vspace, units); if (count > 0) { if ((count >= 2) && (strncmp(units, "cm", 2) == 0)) { vspace = vspace*28.3; }; if ((count >= 2) && (strncmp(units, "in", 2) == 0)) { vspace = vspace*72.0 ; }; if (xinbody == 0) { if (fileopen) { vskip(vspace); }; } else { addnumberfeature(VSKIP, (int)vspace); }; }; }; if (strcmp(p, "chordsabove") == 0) { gchords_above = 1; }; if (strcmp(p, "chordsbelow") == 0) { gchords_above = 0; }; /* look for font-related commands */ font_command(p, s); } void event_field(k, f) char k; char *f; /* A field line has been encountered in the input abc */ { switch (k) { case 'T': if (debugging) { printf("T:%s\n", f); }; addtolist(&thetune.title, addstring(f)); break; case 'C': if (thetune.composer != NULL) { event_error("More than one C: field in tune"); } else { thetune.composer = addstring(f); }; break; case 'O': if (thetune.origin != NULL) { event_error("More than one O: field in tune"); } else { thetune.origin = addstring(f); }; break; case 'W': if (debugging) { printf("W:%s\n", f); }; addtolist(&thetune.words, addstring(f)); break; case 'N': addtolist(&thetune.notes, addstring(f)); break; default: break; }; } struct feature* findbar(struct feature* wordplace, int* errors) /* hunts through voice to find next bar data structure */ { struct feature* ft; ft = wordplace; while ((ft != NULL) && (ft != cv->lineend) && (ft->type != MUSICSTOP) && (ft->type != SINGLE_BAR) && (ft->type != DOUBLE_BAR) && (ft->type != BAR_REP) && (ft->type != REP_BAR) && (ft->type != DOUBLE_REP) && (ft->type != THICK_THIN) && (ft->type != THIN_THICK) && (ft->type != BAR1) && (ft->type != REP_BAR2)) { ft = ft->next; }; if ((ft == NULL) || (ft == cv->lineend) || (ft->type == MUSICSTOP)) { *errors = *errors + 1; } else { ft = ft->next; }; return(ft); } struct feature* apply_syll(char* s, struct feature* wordplace, int* errors) /* attaches a syllable from the lyrics to the appropriate note */ { struct note* n; struct feature* ft; int inchord; ft = wordplace; inchord = 0; while ((ft != NULL) && (ft != cv->lineend) && (ft->type != NOTE)) { if (ft->type == CHORDON) { inchord = 1; }; ft = ft->next; /* skip over any grace notes */ if ((ft != NULL) && (ft->type == GRACEON)) { while ((ft != NULL) && (ft != cv->lineend) && (ft->type != GRACEOFF)) { ft = ft->next; }; }; }; if ((ft == NULL) || (ft == cv->lineend)) { *errors = *errors + 1; return(ft); }; if (ft->type == NOTE) { n = ft->item.voidptr; if (n->syllables == NULL) { n->syllables = newlist(); }; /* if (strlen(s) < 80) { ISOdecode(s, isocode); addtolist(n->syllables, addstring(isocode)); } else { */ addtolist(n->syllables, addstring(s)); /* }; */ }; ft = ft->next; /* skip over any chord */ if (inchord) { while ((ft != NULL) && (ft->type != CHORDOFF) && (ft != cv->lineend)) { ft = ft->next; }; }; return(ft); } void event_words(p, continuation) char* p; int continuation; /* A line of lyrics (w: ) has been encountered in the abc */ { struct vstring syll; char* q; struct feature* wordplace; unsigned char ch; int errors; if (!xinbody) { if (!suppress) { event_error("w: field outside tune body"); }; return; }; wordplace = cv->linestart; if (cv->more_lyrics) { errors = cv->lyric_errors; } else { errors = 0; }; if (wordplace == NULL) { event_error("No notes to match words"); return; }; initvstring(&syll); q = p; skipspace(&q); while (*q != '\0') { clearvstring(&syll); ch = *q; while(ch=='|') { wordplace = findbar(wordplace, &errors); q++; ch = *q; }; while (((ch>127)||isalnum(ch)||ispunct(ch))&& (ch != '_')&&(ch != '-')&&(ch != '*')&& (ch != '|')) { if (ch == '~') { ch = ' '; }; if ((ch == '\\') && (*(q+1)=='-')) { ch = '-'; q++; }; /* syllable[i] = ch; */ addch(ch, &syll); q++; ch = *q; }; skipspace(&q); if (ch == '-') { addch(ch, &syll); while (isspace(ch)||(ch=='-')) { q++; ch = *q; }; }; if (syll.len > 0) { wordplace = apply_syll(syll.st, wordplace, &errors); } else { if (ch=='_') { clearvstring(&syll); addch('_', &syll); wordplace = apply_syll(syll.st, wordplace, &errors); q++; ch = *q; }; if (ch=='*') { clearvstring(&syll); addch(' ', &syll); wordplace = apply_syll(syll.st, wordplace, &errors); q++; ch = *q; }; }; }; if (continuation) { cv->more_lyrics = 1; cv->lyric_errors = errors; cv->linestart = wordplace; } else { cv->more_lyrics = 0; if (errors > 0) { event_error("Lyric line too long for music"); } else { clearvstring(&syll); wordplace = apply_syll(syll.st, wordplace, &errors); if (errors == 0) { event_error("Lyric line too short for music"); }; }; }; freevstring(&syll); } /* [SS] 2014-08-16 */ void appendfield (morewords) char *morewords; { printf("appendfield not implemented here\n"); } void event_part(s) char* s; /* A part field (P: ) has been encountered in the abc */ { char label[200]; /* [SS] 2010-12-12 */ if (xinhead) { if (thetune.parts != NULL) { event_error("Multiple P: fields in header"); } else { thetune.parts = addstring(s); }; }; if (xinbody) { /* addfeature(PART, addstring(s)); */ /* PART is handled like an instruction */ if (cv->instructions_pending == NULL) { cv->instructions_pending = newlist(); }; sprintf(label, ":p%s", s); addtolist(cv->instructions_pending, addstring(label)); }; } void event_voice(n, s, vp) int n; char *s; struct voice_params *vp; /* A voice field (V: ) has been encountered */ { if (xinbody) { setvoice(n); if (vp->gotclef) { event_clef (vp->clefname, &vp->new_clef); } if (vp->gotoctave) { event_octave (vp->octave, 0); } } else { if (!suppress) { event_error("V: field outside tune body"); }; }; } void event_length(n) int n; /* A length field (L: ) has been encountered */ { if (xinhead) { setfract(&thetune.unitlen, 1, n); } else { if (xinbody) { setfract(&cv->unitlen, 1, n); } else { if (!suppress) { event_warning("L: field outside tune ignored"); }; }; }; } void event_default_length (n) int n; /* Handles a missing L: field */ { event_length(n); } void event_refno(n) int n; /* A reference field (X: ) has been encountered. This indicates the start */ /* of a new tune */ { if (xinhead) { event_error("incomplete tune"); }; if (xinbody) { check_tune_end(&thetune); printtune(&thetune); }; freetune(&thetune); xinbody = 0; xinhead = 0; suppress = 0; parseroff(); if (debugging) { printf("X:%d\n", n); }; if (checkmatch(n)) { parseron(); /* fileopen = make_open(); */ xinhead = 1; xinbody = 0; init_tune(&thetune, n); } else { suppress = 1; }; } void event_tempo(n, a, b, relative, pre, post) int n, a, b; int relative; char *pre; /* text before tempo */ char *post; /* text after tempo */ /* A tempo field Q: has been encountered in the abc */ /* Q:a/b=N will have relative = 0 */ /* Q:N will have a=0 and b=0 */ /* Q:Ca/b = N will have relative = 1 */ { if (xinhead) { thetune.tempo = newtempo(n, a, b, relative, pre, post); } else { if (xinbody) { if ((a == 0) && (b == 0)) { addfeature(TEMPO, newtempo(n, cv->unitlen.num, cv->unitlen.denom, 0, pre, post)); } else { addfeature(TEMPO, newtempo(n, a, b, relative, pre, post)); }; } else { if (!suppress) { event_warning("Q: field outside tune ignored"); }; }; }; } void event_timesig (timesig) timesig_details_t *timesig; /* A time signature (M: ) has been encountered in the abc */ { int checkbars; if (timesig->type == TIMESIG_FREE_METER) { checkbars = 0; } else { checkbars = 1; } if (xinhead) { copy_timesig(&thetune.timesig, timesig); thetune.barchecking = checkbars; } else { if (xinbody) { timesig_details_t *time_timesig; time_timesig = newtimesig(); copy_timesig(time_timesig, timesig); addfeature (TIME, time_timesig); setfract (&cv->barlen, timesig->num, timesig->denom); if (cv->line != midline) { cv->changetime = 1; } } else { if (!suppress) { event_warning ("M: field outside tune ignored"); }; }; }; } void event_clef(char* clefstr, cleftype_t * new_clef) /* a clef has been encountered in the abc */ { if (new_clef->basic_clef == basic_clef_undefined) { return; } if (xinbody) { copy_clef (cv->clef, new_clef); addfeature (CLEF, newclef (new_clef)); }; if ((xinhead) && (!xinbody)) { copy_clef (&thetune.clef, new_clef); }; } 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'] = '_'; } void altermap(basemap, basemul, modmap, modmul) /* apply modifiers to a set of accidentals */ char basemap[7], modmap[7]; int basemul[7], modmul[7]; { int i; for (i=0; i<7; i++) { if (modmap[i] != ' ') { basemap[i] = modmap[i]; basemul[i] = modmul[i]; }; }; } void resolve_tempo(struct atempo* t, struct fract* unitlen) /* Tempo may be given relative to the unit note length. At the start of */ /* the tune, we will know what this is and can deduce the tempo value. */ /* Can also deduce tempo if it has been given as a number only. */ { if (t->relative==1) { setfract(&t->basenote, t->basenote.num*unitlen->num, t->basenote.denom*unitlen->denom); reducef(&t->basenote); t->relative = 0; }; if ((t->basenote.num == 0) && (t->basenote.denom == 0)) { setfract(&t->basenote, unitlen->num, unitlen->denom); reducef(&t->basenote); }; } static void start_body() /* We have reached the end of the header section and need to set */ /* default values for anything not explicitly declared */ { parseron(); /* missing M: is handled by setting default M:none */ /* missing L: is handled by event_default_length */ if (thetune.tempo != NULL) { resolve_tempo(thetune.tempo, &thetune.unitlen); }; } void event_true_key(sharps, s, modeindex, modmap, modmul) int sharps; char *s; int modeindex; /* 0 major, 1,2,3 minor, 4 locrian, etc. */ char modmap[7]; int modmul[7]; /* key detected in K: field */ { char basemap[7]; int basemul[7]; struct key* akey; int minor; minor =0; if (modeindex >0 && modeindex <4) minor = 1; setmap(sharps, basemap, basemul); altermap(basemap, basemul, modmap, modmul); if (xinbody) { akey = newkey(s, sharps, basemap, basemul); addfeature(KEY, akey); set_keysig(cv->keysig, akey); }; if (xinhead) { if (thetune.keysig == NULL) { thetune.keysig = newkey(s, sharps, basemap, basemul); } else { event_warning("Key specified twice"); }; xinbody = 1; xinhead = 0; setvoice(1); start_body(); }; } void event_octave(int num, int local) /* deals with the special command I:octave=N */ { if (xinhead) { thetune.octaveshift = num; }; if (xinbody) { cv->octaveshift = num; }; } void event_key(sharps, s, minor, modmap, modmul, modmicrotone, gotkey, gotclef, clefstr, new_clef, octave, transpose, gotoctave, gottranspose, explict) int sharps; char *s; int minor; char modmap[7]; int modmul[7]; struct fraction modmicrotone[7]; /* [SS] 2014-01-06 */ int gotkey, gotclef; char* clefstr; /* [JA] 2020-10-19 */ cleftype_t * new_clef; int octave, transpose, gotoctave, gottranspose; int explict; /* A key field (K: ) has been encountered */ { if (xinhead || xinbody) { if (gotclef==1) { event_clef(clefstr, new_clef); }; if (gotkey==1) { event_true_key(sharps, s, minor, modmap, modmul); }; }; if (gotclef) { event_octave(new_clef->octave_offset, 0); } if (gotoctave) { event_octave(octave, 0); }; } static void checkbar(int type) /* Make sure that bar lasts for the correct musical time */ { int valid; char msg[80]; int a1, a2; valid = 0; a1 = cv->barlen.num*cv->barcount.denom; a2 = cv->barcount.num*cv->barlen.denom; if (a1 == a2) { cv->barcount.num = 0; cv->barcount.denom = 1; cv->barno = cv->barno + 1; } else { if (((type==BAR_REP)||(type==REP_BAR)||(type==DOUBLE_REP)) && (a1 > a2)) { /* do nothing */ } else { if ((cv->barno > 0) && (cv->barcount.num != 0) && (cv->barchecking != 0)) { sprintf(msg, "Bar %d is %d/%d not %d/%d", cv->barno, cv->barcount.num, cv->barcount.denom, cv->barlen.num, cv->barlen.denom); event_warning(msg); cv->barcount.num = 0; cv->barcount.denom = 1; cv->barno = cv->barno + 1; } else { cv->barcount.num = 0; cv->barcount.denom = 1; cv->barno = cv->barno + 1; }; }; }; } void event_bar(type, playonrep_list) int type; char* playonrep_list; /* A bar has been encountered in the abc */ { if (cv->inchord) { event_warning("Bar line not permitted within chord"); return; }; checkbar(type); /* increment bar number if bar complete */ /* [SS] 2015-11-15 * changed (void*) to (int *) */ addnumberfeature(type, cv->barno); /* save bar number */ if ((playonrep_list != NULL) && (strlen(playonrep_list) > 0)) { event_playonrep(playonrep_list); }; } void event_space() /* A region of whitespace has been encountered */ { addfeature(NOBEAM, NULL); } void event_graceon() /* start of grace note(s) */ { if (cv->inchord) { event_error("grace notes not allowed within chord"); return; }; if (cv->ingrace) { event_error("nested grace notes not allowed"); return; }; cv->gracebeamroot = NULL; cv->gracebeamend = NULL; cv->ingrace = 1; addfeature(GRACEON, NULL); } void event_graceoff() /* end of grace note(s) */ { if (!cv->ingrace) { event_error("No grace notes to close"); return; }; if (cv->inchord) { event_error("cannot close grace notes within chord"); return; }; addfeature(GRACEOFF, NULL); closegracebeam(cv); cv->ingrace = 0; cv->gracebeamroot = NULL; cv->gracebeamend = NULL; } void event_rep1() /* start of first repeat */ { addfeature(REP1, NULL); } void event_rep2() /* start of second repeat */ { addfeature(REP2, NULL); } void event_playonrep(s) char* s; /* play on repeat(s) X - where X can be a list */ { addfeature(PLAY_ON_REP, addstring(s)); } void event_broken(type, mult) /* handles > >> >>> < << <<< in the abc */ int type, mult; { if (cv->inchord) { event_error("Broken rhythm not allowed in chord"); } else { if (cv->ingrace) { event_error("Broken rhythm not allowed in grace notes"); } else { cv->brokentype = type; cv->brokenmult = mult; cv->brokenpending = 0; }; }; } void event_tuple(n, q, r) int n, q, r; /* Start of a tuple has been encountered (e.g. triplet) */ /* Meaning is "play next r notes at q/n of notated value" */ /* where all 3 exist, otherwise r defaults to n and ratio */ /* is deduced from standard rules if q is missing */ { if (cv->tuplenotes != 0) { event_error("tuple within tuple not allowed"); return; }; if (r != 0) { cv->tuplenotes = r; } else { cv->tuplenotes = n; }; if (q != 0) { cv->tuplefactor.num = q; cv->tuplefactor.denom = n; } else { cv->tuplefactor.denom = n; if ((n == 2) || (n == 4) || (n == 8)) cv->tuplefactor.num = 3; if ((n == 3) || (n == 6)) cv->tuplefactor.num = 2; if ((n == 5) || (n == 7) || (n == 9)) { if ((cv->barlen.num % 3) == 0) { cv->tuplefactor.num = 3; } else { cv->tuplefactor.num = 2; }; }; }; cv->thistuple = newtuple(cv->tuplefactor.denom, cv->tuplefactor.num, cv->tuplenotes, n); addfeature(TUPLE, cv->thistuple); } void event_startinline() { } void event_closeinline() { } void event_handle_gchord(s) char* s; /* Guitar/Accompaniment chord placed in linked list for association */ /* with next suitable note */ { if (cv->gchords_pending == NULL) { cv->gchords_pending = newlist(); }; if (*s == '_') { if (cv->instructions_pending == NULL) { cv->instructions_pending = newlist(); }; addtolist(cv->instructions_pending, addstring(s+1)); } else { addtolist(cv->gchords_pending, addstring(s)); }; } void event_handle_instruction(s) char* s; /* An instruction (! !) has been encountered */ { char* inst; static char segno[3] = ":s"; static char coda[3] = ":c"; struct dynamic *psaction; int done; done = 0; inst = s; if (strcmp(s, "fermata") == 0) { decorators_passback[4] =1; /* don't show !fermata!. Treat it like H in music line */ return; } if (strcmp(s, "trill") == 0) { decorators_passback[6] =1; /* don't show !trill!. Treat it like T in music line */ return; } if (strcmp(s, "segno") == 0) { inst = segno; done = 1; }; if (strcmp(s, "coda") == 0) { inst = coda; done = 1; }; if (done == 1) { if (cv->instructions_pending == NULL) { cv->instructions_pending = newlist(); }; addtolist(cv->instructions_pending, addstring(inst)); return; } if (strcmp(s,"red") == 0) { psaction = (struct dynamic*) checkmalloc(sizeof(struct note)); psaction->color = 'r'; addfeature(DYNAMIC,psaction); } if (strcmp(s,"black") == 0) { psaction = (struct dynamic*) checkmalloc(sizeof(struct note)); psaction->color = 'b'; addfeature(DYNAMIC,psaction); } } struct slurtie* resolve_slur(struct feature* lastnote) /* when an end-of-slur marker ')' is found, this routine works out */ /* where the other end of the slur was */ { int i; int resolved; struct slurtie* s; resolved = 0; if (lastnote != NULL) { for (i=0; islurcount; i++) { if ((resolved == 0) && (cv->slur_place[i]->begin != NULL) && (cv->slur_place[i]->begin != lastnote)) { resolved = 1; cv->slur_place[i]->end = lastnote; s = cv->slur_place[i]; cv->slur_place[i] = cv->slur_place[cv->slurcount-1]; cv->slurcount = cv->slurcount - 1; }; }; }; if (resolved == 0) { event_error("Could not find start of slur to match close slur"); s = NULL; }; return(s); } static void startslurs(struct feature* firstnote) /* When a note is found after a (, set note to be start of slur */ /* Note may be the start of more than one slur */ { int i; for (i=0; islurcount; i++) { if (cv->slur_place[i]->begin == NULL) { cv->slur_place[i]->begin = firstnote; }; }; } void event_sluron(t) int t; /* start of slur */ { struct slurtie* s; s = newslurtie(); addfeature(SLUR_ON, s); cv->slurpending = 1; if (cv->slurcount < MAX_SLURS) { cv->slur_place[cv->slurcount] = s; cv->slurcount = cv->slurcount+1; } else { event_error("Static limit on number of slurs exceeded"); }; } void event_sluroff(t) int t; /* end of slur */ { struct slurtie* s; s = resolve_slur(cv->lastnote); addfeature(SLUR_OFF, s); } static void resolve_ties(struct feature* f) /* try to match up note to previous tied note */ { struct feature* ft; struct slurtie* s; struct note* m; struct note* n; int i, j; n = f->item.voidptr; for (i=0; itiespending; i++) { ft = cv->tie_place[i]; /* pointer to TIE feature */ s = ft->item.voidptr; ft = s->begin; /* pointer to NOTE feature */ m = ft->item.voidptr; if (m->y == n->y) { /* pitch match found */ s->end = f; j = cv->tiespending; cv->tie_place[i] = cv->tie_place[j-1]; cv->tie_status[i] = cv->tie_status[j-1]; cv->tiespending = j-1; }; }; } static void advance_ties() /* deal with unresolved tied notes as musical time advances */ { int i, j; for (i=0; itiespending; i++) { cv->tie_status[i]++; }; i = 0; j = cv->tiespending; while (i < j) { if (cv->tie_status[i] >= 2) { event_error("No note to tie to"); cv->tie_place[i] = cv->tie_place[j-1]; cv->tie_status[i] = cv->tie_status[j-1]; j = j - 1; } else { i = i + 1; }; }; cv->tiespending = j; } void event_tie() /* tie encountered in the abc */ { struct slurtie* s; struct feature* place; int i; if ((cv->lastnote == NULL)||(cv->lastnote->type != NOTE)) { event_error("No note to tie"); } else { s = newslurtie(); place = addfeature(TIE, s); s->begin = cv->lastnote; cv->lastnote = NULL; i = cv->tiespending; if (i < MAX_TIES) { cv->tie_place[i] = place; cv->tie_status[i] = 0; cv->tiespending = cv->tiespending + 1; } else { event_error("Internal limit on ties exceeded"); }; }; } void event_lineend(ch, n) char ch; int n; /* Line ending with n copies of special character ch */ { } static void lenmul(n, a, b) /* multiply note length by a/b */ struct feature* n; int a, b; { struct note *anote; struct rest* arest; struct fract* afract; if (n->type == NOTE) { anote = n->item.voidptr; afract = &anote->len; afract->num = afract->num * a; afract->denom = afract->denom * b; reducef(afract); /* re-calculate base, base_exp and dots */ anote->dots = count_dots(&anote->base, &anote->base_exp, anote->len.num, anote->len.denom); }; if (n->type == REST) { arest = n->item.voidptr; afract = &arest->len; afract->num = afract->num * a; afract->denom = afract->denom * b; reducef(afract); /* re-calculate base, base_exp and dots */ arest->dots = count_dots(&arest->base, &arest->base_exp, arest->len.num, arest->len.denom); }; } struct fract* getlenfract(struct feature *f) /* find fractional length of NOTE or REST */ { struct fract *len; struct note *anote; struct rest *arest; len = NULL; if (f->type == NOTE) { anote = f->item.voidptr; len = &(anote->len); }; if (f->type == REST) { arest = f->item.voidptr; len = &(arest->len); }; return(len); } static void brokenadjust() /* adjust lengths of broken notes */ { int num1, num2, denom12; struct feature* j; struct fract* fr1; struct fract* fr2; int temp; int failed; int done; switch(cv->brokenmult) { case 1: num1 = 3; num2 = 1; break; case 2: num1 = 7; num2 = 1; break; case 3: num1 = 15; num2 = 1; break; default: num1 = num2 = 1; /* [SDG] 2020-06-03 */ }; denom12 = (num1 + num2)/2; if (cv->brokentype == LT) { temp = num1; num1 = num2; num2 = temp; }; failed = 0; if ((cv->laststart == NULL) || (cv->lastend == NULL) || (cv->thisstart == NULL) || (cv->thisend == NULL)) { failed = 1; } else { /* check for same length notes */ fr1 = getlenfract(cv->laststart); fr2 = getlenfract(cv->thisstart); /* fr1 = cv->laststart->item.voidptr; fr2 = cv->thisstart->item.voidptr; */ if ((fr1->num * fr2->denom) != (fr2->num * fr1->denom)) { failed = 1; }; }; if (failed) { event_error("Cannot apply broken rhythm"); } else { /* printf("Adjusting %d to %d and %d to %d\n", cv->laststart, cv->lastend, cv->thisstart, cv->thisend); */ j = cv->laststart; done = 0; while (done == 0) { lenmul(j, num1, denom12); done = (j == cv->lastend); j = j->next; }; j = cv->thisstart; done = 0; while (done == 0) { lenmul(j, num2, denom12); done = (j == cv->thisend); j = j->next; }; }; } static void marknotestart(struct feature* place) /* 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 */ { cv->laststart = cv->thisstart; cv->lastend = cv->thisend; cv->thisstart = place; } static void marknoteend(struct feature* place) /* 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 */ { cv->thisend = place; if (cv->brokenpending != -1) { cv->brokenpending = cv->brokenpending + 1; if (cv->brokenpending == 1) { brokenadjust(); cv->brokenpending = -1; }; }; } static void marknote(struct feature* place) /* when handling a single note, not a chord, marknotestart() and */ /* marknoteend() can be called together */ { marknotestart(place); marknoteend(place); } static void markchord(struct feature* chordplace) /* find start and end of chord for applying broken rhythm */ { struct feature* first; struct feature* last; first = chordplace->next; last = first; while ((last->next != NULL)&&(last->next->type==NOTE)) { last = last->next; }; marknotestart(first); marknoteend(last); } void event_chord() /* handles old '+' notation which marks the start and end of each chord */ { if (cv->inchord) { event_chordoff(1,1); } else { event_chordon(dummydecorator); }; } void event_chordon(int chorddecorators[]) /* start of a chord */ /* the array chorddecorators is not used yet. */ { if (cv->inchord) { event_error("nested chords found"); return; }; cv->inchord = 1; cv->chordcount = 0; cv->thischord = newchord(); cv->chordplace = addfeature(CHORDON, cv->thischord); } void event_ignore () { }; /* [SS] 2018-12-21 */ void fix_enclosed_note_lengths(struct feature* chordplace, int chord_n, int chord_m, int * base, int * base_exp) { struct feature* f; struct note* anote; if (chord_n ==1 && chord_m ==1) return; f = chordplace->next; anote = f->item.voidptr; /* remove old note length from barcount */ addfractions(&cv->barcount, -anote->len.num, anote->len.denom); while ((f != NULL)&&((f->type==NOTE)||(f->type=CHORDNOTE))) { anote = f->item.voidptr; /* remove old note length from barcount */ setfract(&anote->len, chord_n*cv->unitlen.num, chord_m*cv->unitlen.denom); reducef(&anote->len); /*printf("NOTE %c%c %d / %d\n", anote->accidental, anote->pitch, anote->len.num, anote->len.denom); */ anote->dots = count_dots(&anote->base, &anote->base_exp, anote->len.num, anote->len.denom); f = f->next; *base = anote->base; *base_exp = anote->base_exp; } /* and new note length to barcount */ addfractions(&cv->barcount, anote->len.num, anote->len.denom); } void event_chordoff(int chord_n, int chord_m) /* end of a chord */ { struct feature* ft; struct chord* thechord; struct note* firstnote; int base,base_exp; if (!cv->inchord) { event_error("no chord to close"); return; }; ft = cv->chordplace; if ((ft != NULL) && (ft->next != NULL) && (ft->next->type == NOTE)) { thechord = ft->item.voidptr; firstnote = ft->next->item.voidptr; /* beaming for 1st note in chord */ beamitem(NOTE, firstnote, ft->next); markchord(ft); thechord->base = firstnote->base; thechord->base_exp = firstnote->base_exp; } else { thechord = NULL; /* [SDG] 2020-06-04 */ event_error("mis-formed chord"); }; if (cv->chordplace) { fix_enclosed_note_lengths(cv->chordplace,chord_n,chord_m,&base,&base_exp); if (chord_n != 1 || chord_m != 1) { thechord->base = base; thechord->base_exp = base_exp; } } cv->inchord = 0; cv->thischord = NULL; cv->chordplace = NULL; advance_ties(); addfeature(CHORDOFF, NULL); } /* just a stub to ignore 'y' */ void event_spacing(n, m) int n,m; { } void xevent_rest(n, m, multi) int n, m, multi; /* A rest has been encountered in the abc */ /* multi is 0 for ordinary rests or count for multibar rests */ { struct feature* restplace; if (cv->ingrace) { event_warning("rest in grace notes ignored"); return; }; if (cv->inchord) { event_warning("rest in chord ignored"); return; }; if ((multi > 0) && (cv->tuplenotes > 0)) { event_error("Multiple bar rest not allowed in tuple"); }; advance_ties(); restplace = addfeature(REST, newrest(n*cv->unitlen.num, m*cv->unitlen.denom, multi)); if (cv->slurpending) { startslurs(restplace); cv->slurpending = 0; }; cv->lastnote = restplace; if (cv->inchord) { cv->chordcount = cv->chordcount + 1; } else { marknote(restplace); }; if ((!cv->ingrace) && (!cv->inchord || (cv->chordcount == 1))) { if (cv->tuplenotes == 0) { if (multi == 0) { addunits(&cv->barcount, n, m); }; } else { addunits(&cv->barcount, n*cv->tuplefactor.num, m*cv->tuplefactor.denom); cv->thistuple->beamed = 0; cv->tuplenotes = cv->tuplenotes - 1; if (cv->tuplenotes == 0) { cv->thistuple = NULL; }; }; }; } void event_rest(decorators,n,m,type) int n, m,type; int decorators[DECSIZE]; /* A rest has been encountered in the abc */ { xevent_rest(n, m, 0); } void event_mrest(n,m,c) int n, m; char c; /* [SS] 2017-04-19 to distinguish X from Z in abc2abc */ /* A multiple bar rest has been encountered in the abc */ { xevent_rest(1, 1, n); } void event_note(decorators, clef, xaccidental, xmult, xnote, xoctave, n, m) int decorators[DECSIZE]; cleftype_t *clef; /* [JA] 2020-10-19 */ int xmult; char xaccidental, xnote; int xoctave, n, m; /* note found in abc */ { struct note* nt; struct feature* noteplace; struct chord* thechord; int pitchval; nt = newnote(decorators, clef, xaccidental, xmult, xnote, xoctave, n * cv->unitlen.num, m * cv->unitlen.denom); nt->tuplenotes = cv->tuplenotes; noteplace = addfeature(NOTE, nt); cv->lastnote = noteplace; resolve_ties(noteplace); if (cv->slurpending) { startslurs(noteplace); cv->slurpending = 0; }; if (!cv->inchord) { marknote(noteplace); advance_ties(); } else { thechord = cv->thischord; pitchval = notenum(xoctave, xnote, clef); if (cv->chordcount == 0) { thechord->ytop = pitchval; thechord->ybot = pitchval; } else { if (pitchval > thechord->ytop) { thechord->ytop = pitchval; }; if (pitchval < thechord->ybot) { thechord->ybot = pitchval; }; }; cv->chordcount = cv->chordcount + 1; }; if ((!cv->ingrace) && (!cv->inchord || (cv->chordcount == 1))) { if (cv->tuplenotes == 0) { addunits(&cv->barcount, n, m); } else { addunits(&cv->barcount, n*cv->tuplefactor.num, m*cv->tuplefactor.denom); if (nt->base_exp > -3) { cv->thistuple->beamed = 0; }; cv->tuplenotes = cv->tuplenotes - 1; if (cv->tuplenotes == 0) { cv->thistuple = NULL; /* prevent beaming from tuple to non-tuple notes */ addfeature(NOBEAM, NULL); }; }; }; } /* these functions are here to satisfy the linker */ void event_microtone(int dir, int a, int b) { } void event_temperament(char *line) { } void event_normal_tone() { } void event_info_key(key, value) char* key; char* value; /* handles a (key,value) pair found in an I: field */ { int num; if (strcmp(key, "clef")==0) { cleftype_t new_clef; /* [JA] 2020-10-19 */ int valid_clef; init_new_clef (&new_clef); valid_clef = get_standard_clef (value, &new_clef); if (!valid_clef) { valid_clef = get_extended_clef_details (value, &new_clef); } if (valid_clef) { event_clef (value, &new_clef); } }; if (strcmp(key, "octave")==0) { num = readsnumf(value); event_octave(num,0); }; } int main(argc,argv) int argc; char *argv[]; { char *filename; int i; oldchordconvention = 0; for (i=0;i