/* * 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 * */ /* position.c */ /* part of YAPS - abc to PostScript converter */ /* This file contains routines to calculate symbol positions within */ /* a line of music. */ /* for Microsoft Visual C++ 6.0 and higher */ #ifdef _MSC_VER #define ANSILIBS #endif #include #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.voidptr; addfract(&v->time, arest->len.num, arest->len.denom); }; if ((p->type == NOTE) && (!v->ingrace)) { anote = p->item.voidptr; 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 = 0.0; /* [SDG] 2020-06-03 */ 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); }; }