--- /dev/null
+
+/* Copyright (c) 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */
+/* All rights reserved */
+
+/* miscellaneous utility functions for music publication program, mostly
+ * for the print phase */
+
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+static void grp_octave_adjust P((struct GRPSYL *gs_p, int adj, struct MAINLL *mll_p));
+static void set_height_blockhead P((struct BLOCKHEAD *blockhead_p, int context,
+ struct MAINLL *mll_p));
+static double coord_staffscale P((float *coord_array));
+
+\f
+
+/* set _cur location variable to specified value */
+
+void
+set_cur(x, y)
+
+float x, y; /* x, east, and west get set to x value. y, north and south,
+ * get set to y value */
+
+{
+ float *cur_p; /* coord array for _cur in current context */
+
+
+ /* look up _cur symbol and fill in the values */
+ cur_p = symval("_cur", (float **) 0);
+ cur_p[AX] = cur_p[AE] = cur_p[AW] = x;
+ cur_p[AY] = cur_p[AN] = cur_p[AS] = y;
+}
+\f
+
+/* set the values for location variable _win */
+
+void
+set_win(n, s, e, w)
+
+float n, s, e, w; /* north, south, east, and west */
+
+{
+ float *window; /* coordinate info for _win */
+
+
+ /* look up symbol and fill in values */
+ window = symval("_win", (float **) 0);
+ window[AN] = n;
+ window[AS] = s;
+ window[AE] = e;
+ window[AW] = w;
+ /* set x and y to midpoints of rectangle */
+ window[AY] = s + (n - s)/2.0;
+ window[AX] = w + (e - w)/2.0;
+}
+\f
+
+/* Return width of a bar line. Allow a couple pixels on either side
+ * of the actual line(s) [and dots]. Does not include user padding.
+ * Since an invisbar has no lines or dots, it has zero width.
+ */
+
+double
+width_barline(bar_p)
+
+struct BAR *bar_p; /* return width of this bar line */
+
+{
+ if (bar_p == (struct BAR *) 0) {
+ return(0.0);
+ }
+
+ switch(bar_p->bartype) {
+
+ case SINGLEBAR:
+ return(7 * STDPAD);
+
+ case DOUBLEBAR:
+ return(9 * STDPAD);
+
+ case ENDBAR:
+ return(12 * STDPAD);
+
+ case REPEATSTART:
+ case REPEATEND:
+ return(16 * STDPAD);
+
+ case REPEATBOTH:
+ return(19 * STDPAD);
+
+ case RESTART:
+ return(2.0 * HALF_RESTART_WIDTH);
+
+ case INVISBAR:
+ return(0.0);
+
+ default:
+ pfatal("bad bar type");
+ /*NOTREACHED*/
+ break;
+ }
+
+ /*NOTREACHED*/
+ return(0.0);
+}
+\f
+
+/* Normally, we want some padding on both sides of a bar line,
+ * but at the end of a staff, we don't want right padding.
+ * This applies either if we are at the right
+ * margin or if the next bar is a restart.
+ * This function returns how much to adjust an end-of-score bar line
+ * eastward to make it at the right edge of the score.
+ */
+
+double
+eos_bar_adjust(bar_p)
+
+struct BAR *bar_p; /* the bar to adjust */
+
+{
+ double halfbarwidth;
+
+ halfbarwidth = width_barline(bar_p) / 2.0;
+ switch (bar_p->bartype) {
+ case DOUBLEBAR:
+ return(halfbarwidth - STDPAD - (W_NORMAL / PPI / 2.0));
+ case SINGLEBAR:
+ return(halfbarwidth - (W_NORMAL / PPI / 2.0));
+ case REPEATEND:
+ return(halfbarwidth - (4.0 * STDPAD) - (W_WIDE / PPI / 2.0));
+ case ENDBAR:
+ return(halfbarwidth - (2.0 * STDPAD) - (W_WIDE / PPI / 2.0));
+ default:
+ break;
+ }
+ return(0.0);
+}
+\f
+
+/* width of clef, keysig, timesig */
+
+double
+width_clefsig(clefsig_p)
+
+struct CLEFSIG *clefsig_p; /* return width of this clefsig */
+
+{
+ /* we just call the routine to print a clefsig, but with
+ * flag to tell it to not really print */
+ return(pr_clefsig((struct MAINLL *) 0, clefsig_p, NO));
+}
+\f
+
+/* translate clef name to clef output character */
+
+int
+clefchar(clef)
+
+int clef; /* TREBLE, etc */
+
+{
+ switch(clef) {
+
+ case TREBLE:
+ case TREBLE_8:
+ case FRENCHVIOLIN:
+ case TREBLE_8A:
+ return(C_GCLEF);
+
+ case BASS:
+ return(C_FCLEF);
+
+ default:
+ /* everything else uses the C clef */
+ return(C_CCLEF);
+ }
+}
+\f
+
+/* Returns width of the given clef in inches in the default size.
+ * If is_small is YES, give width of the 3/4 sized one used in mid-score,
+ * rather than the full-sized used at beginning of line.
+ * Caller must adjust by staffscale if they need that.
+ * No padding is included beyond the padding of the clef music character
+ * itself, so caller needs to add any they deem appropriate for aesthetics.
+ */
+
+double
+clefwidth(clef, is_small)
+
+int clef; /* TREBLE, BASS, ALTO, FRENCHVIOLIN, etc */
+int is_small; /* If YES, assume mid-score clef, not full sized one */
+
+{
+ return(width(FONT_MUSIC, (is_small ? (3 * DFLT_SIZE) / 4 : DFLT_SIZE),
+ clefchar(clef)));
+}
+\f
+
+/* Returns where the given clef's baseline should be
+ * relative to the middle line of the staff, in stepsizes.
+ * (Above the middle line is positive, below is negative).
+ * If north_p and/or south_p are non-null, the relative north/south values
+ * of the clef are returned via the pointers. These will be relative
+ * to the middle line of the staff and will be in inches
+ * in the default staffscale, using default size
+ * (unless is_small == YES, in which case it will be 3/4 size).
+ * Note that this should not be called with TABCLEF or NOCLEF.
+ */
+
+int
+clefvert(clef, is_small, north_p, south_p)
+
+int clef; /* TREBLE, BASS, ALTO, FRENCHVIOLIN, etc */
+int is_small; /* If YES, assume mid-score clef, not full sized one.
+ * Note that if both of the following arguments are null,
+ * this is_small argument's value is actually irrelevent. */
+float *north_p; /* if non-null, relative north will be returned here */
+float *south_p; /* if non-null, relative south will be returned here */
+
+{
+ int steps; /* relative to middle line, to be returned */
+
+ switch(clef) {
+
+ case TREBLE:
+ case TREBLE_8:
+ case TREBLE_8A:
+ steps = -2;
+ break;
+
+ case FRENCHVIOLIN:
+ case SOPRANO:
+ steps = -4;
+ break;
+
+ case MEZZOSOPRANO:
+ steps = -2;
+ break;
+
+ case ALTO:
+ steps = 0;
+ break;
+
+ case TENOR:
+ steps = 2;
+ break;
+
+ case BARITONE:
+ steps = 4;
+ break;
+
+ case BASS:
+ steps = 2;
+ break;
+
+ case TABCLEF:
+ default:
+ pfatal("clefvert called with invalid clef %d", clef);
+ /*NOTREACHED*/
+ steps = 0; /* shut up bogus compiler warning */
+ break;
+ }
+
+ /* If caller wants relative north/south values, calculate them */
+ if (north_p != 0 || south_p != 0) {
+ char muschar; /* music character to print for the clef */
+ int clefsize;
+ char tr8str[4]; /* "8" of treble8 or 8treble */
+ float value; /* of north or south */
+
+ muschar = clefchar(clef);
+ clefsize = (is_small ? (3 * DFLT_SIZE) / 4 : DFLT_SIZE);
+ tr8str[0] = FONT_TI;
+ tr8str[1] = 9;
+ tr8str[2] = '8';
+ tr8str[3] = '\0';
+
+ if (north_p != 0) {
+ value = (float) ascent(FONT_MUSIC, clefsize, muschar);
+ if (clef == TREBLE_8A) {
+ value += (float) strheight(tr8str);
+ }
+ *north_p = value + (float)(steps * STEPSIZE);
+ }
+
+ if (south_p != 0) {
+ value = (float) descent(FONT_MUSIC, clefsize, muschar);
+ if (clef == TREBLE_8) {
+ value += (float) strheight(tr8str);
+ }
+ *south_p = -value + (float)(steps * STEPSIZE);
+ }
+ }
+
+ return(steps);
+}
+\f
+
+/* Given a BLOCKHEAD, fill in its height. BLOCKHEADs are a bit strange,
+ * in that they are in a separate coordinate space of unknown size.
+ * So we start out assuming it is infinitely thin. Then we check
+ * each string in the list and keep track of the lowest one, by
+ * pretending to go where it would be printed and adding the descent
+ * of the string. At the end, the height must be the page height minus the
+ * lowest, since upwards is positive */
+
+static void
+set_height_blockhead(blockhead_p, context, mll_p)
+
+struct BLOCKHEAD *blockhead_p; /* which block to get height of */
+int context; /* C_HEADER, etc */
+struct MAINLL *mll_p; /* for getting margin overrides */
+
+{
+ float distance; /* of headfoot from bottom of page */
+ float lowest;
+ float x_offset; /* because of justification */
+ float yval; /* y coordinate value */
+ struct PRINTDATA *pr_p; /* walk through list of things to print */
+ float block_width; /* page width minus margins */
+ float s_descent; /* strdescent() of the current string */
+ float extra; /* how much farther the string descended
+ * than would be expected of a normal,
+ * single line. */
+ struct MAINLL *rightmargin_mll_p; /* mll_p to use for
+ * finding the right margin */
+
+
+ /* Set _win in this context to zero height at top margin */
+ Context = context;
+ rightmargin_mll_p = (mll_p ? mll_p->next : 0);
+ set_win_coord(blockhead_p->c);
+ set_win(PGHEIGHT - EFF_TOPMARGIN, PGHEIGHT - EFF_TOPMARGIN,
+ PGWIDTH - eff_rightmargin(rightmargin_mll_p),
+ eff_leftmargin(mll_p));
+ block_width = PGWIDTH - eff_rightmargin(rightmargin_mll_p) -
+ eff_leftmargin(mll_p);
+
+ if (blockhead_p->printdata_p != (struct PRINTDATA *) 0) {
+ /* set current to left corner */
+ set_cur(eff_leftmargin((struct MAINLL *)0),
+ PGHEIGHT - EFF_TOPMARGIN);
+
+ distance = _Cur[AY];
+
+ /* Process each item in the list */
+ for (pr_p = blockhead_p->printdata_p;
+ pr_p != (struct PRINTDATA *) 0;
+ pr_p = pr_p->next) {
+
+ /* If this is a paragraph,
+ * split it into as many lines as needed. */
+ if (pr_p->justifytype == J_JUSTPARA ||
+ pr_p->justifytype == J_RAGPARA) {
+ pr_p->string = split_string(pr_p->string,
+ block_width);
+ }
+ pr_p->width = strwidth(pr_p->string);
+ /* stretch justified paragraphs to full width */
+ if (pr_p->justifytype == J_JUSTPARA &&
+ pr_p->width < block_width) {
+ pr_p->width = block_width;
+ }
+
+ /* adjust for justification */
+ switch (pr_p->justifytype) {
+
+ case J_RIGHT:
+ x_offset = pr_p->width;
+ break;
+
+ case J_CENTER:
+ x_offset = pr_p->width / 2.0;
+ break;
+
+ default:
+ x_offset = 0.0;
+ break;
+ }
+
+ /* set current to specified location */
+ yval = inpc_y (&(pr_p->location), (char *) 0, -1);
+ set_cur( inpc_x( &(pr_p->location), (char *) 0, -1 )
+ - x_offset, yval);
+
+ /* if user said to go off of south or y of _win,
+ * change to equivalent offset off of north of _win.
+ * because the south and y could change */
+ if (pr_p->location.vtype == AS ||
+ pr_p->location.vtype == AY) {
+ if (pr_p->location.vert_p == blockhead_p->c) {
+ pr_p->location.vtype = AN;
+ pr_p->location.vsteps = (yval
+ - (PGHEIGHT - EFF_TOPMARGIN)
+ ) / STEPSIZE;
+ }
+ }
+
+ /* determine lowest descent of current string */
+ if (pr_p->isPostScript == YES) {
+ s_descent = 0.0;
+ }
+ else {
+ s_descent = strdescent(pr_p->string);
+ }
+ lowest = _Cur[AY] - s_descent;
+
+ /* if lowest of anything found so far, note that */
+ if ( lowest < distance) {
+ distance = lowest;
+ set_win(PGHEIGHT - EFF_TOPMARGIN, distance,
+ PGWIDTH - eff_rightmargin((struct MAINLL *)0),
+ eff_leftmargin((struct MAINLL *)0));
+ }
+
+ /* Set to end of string just "printed."
+ * If the string when down farther than a single line
+ * add in that extra.
+ */
+ if (pr_p->isPostScript == YES) {
+ extra = 0.0;
+ }
+ else {
+ extra = s_descent - fontdescent(pr_p->string[0],
+ pr_p->string[1]);
+ if (extra < 0.0) {
+ extra = 0.0;
+ }
+ }
+ set_cur( _Cur[AX] + pr_p->width, _Cur[AY] - extra);
+ }
+
+ /* set height to lowest distance encountered */
+ blockhead_p->height = (PGHEIGHT - distance - EFF_TOPMARGIN);
+
+ }
+ else {
+ /* empty header/footer */
+ blockhead_p->height = 0.0;
+ }
+
+ /* if was a footer, now we can set the actual _win coordinates,
+ * by offsetting from bottom of page instead of top */
+ if ( (context == C_FOOTER) || (context == C_FOOT2) ) {
+ set_win(EFF_BOTMARGIN + blockhead_p->height, EFF_BOTMARGIN,
+ PGWIDTH - eff_rightmargin((struct MAINLL *)0),
+ eff_leftmargin((struct MAINLL *)0));
+ }
+ set_win_coord(0);
+}
+\f
+
+/* Calculate the height of all blocks, including headers and footers,
+ * and fill in the height field of the struct. */
+
+void
+calc_block_heights()
+
+{
+ struct MAINLL *mll_p;
+ double topheight = -1.0; /* if > 0.0, is height of "top" */
+ double botheight = -1.0; /* if > 0.0, is height of "bottom" */
+
+ debug(2, "calc_block_heights");
+
+ set_height_blockhead(&Header, C_HEADER, 0);
+ set_height_blockhead(&Footer, C_FOOTER, 0);
+ set_height_blockhead(&Header2, C_HEAD2, 0);
+ set_height_blockhead(&Footer2, C_FOOT2, 0);
+
+ /* set main _win to space within margins and header/footer
+ * for first page. */
+ Context = C_MUSIC;
+
+ /* set the size of _page */
+ _Page[AW] = _Page[AS] = 0.0;
+ _Page[AE] = PGWIDTH;
+ _Page[AN] = PGHEIGHT;
+ _Page[AX] = PGWIDTH / 2.0;
+ _Page[AY] = PGHEIGHT / 2.0;
+
+ /* now calculate top/bot and any other blocks in the main list */
+ initstructs();
+ for (mll_p = Mainllhc_p; mll_p != 0; mll_p = mll_p->next) {
+ if (mll_p->str == S_SSV) {
+ /* keep margins up to date */
+ asgnssv(mll_p->u.ssv_p);
+ }
+ else if (mll_p->str == S_FEED) {
+ if (mll_p->u.feed_p->top_p != 0) {
+ set_height_blockhead(mll_p->u.feed_p->top_p,
+ C_TOP, 0);
+ if (topheight < 0.0) {
+ /* save for setting music _win */
+ topheight = mll_p->u.feed_p->top_p->height;
+ }
+ }
+ if (mll_p->u.feed_p->top2_p != 0) {
+ set_height_blockhead(mll_p->u.feed_p->top2_p,
+ C_TOP2, 0);
+ }
+ if (mll_p->u.feed_p->bot_p != 0) {
+ set_height_blockhead(mll_p->u.feed_p->bot_p,
+ C_BOT, 0);
+ if (botheight < 0.0) {
+ /* save for setting music _win */
+ botheight = mll_p->u.feed_p->bot_p->height;
+ }
+ }
+ if (mll_p->u.feed_p->bot2_p != 0) {
+ set_height_blockhead(mll_p->u.feed_p->bot2_p,
+ C_BOT2, 0);
+ }
+ }
+ else if (mll_p->str == S_BLOCKHEAD) {
+ set_height_blockhead(mll_p->u.blockhead_p,
+ C_BLOCK, mll_p);
+ }
+ }
+
+ set_win(PGHEIGHT - EFF_TOPMARGIN - Header.height
+ - (topheight > 0.0 ? topheight : 0.0),
+ EFF_BOTMARGIN + Footer.height
+ + (botheight > 0.0 ? botheight : 0.0),
+ PGWIDTH - eff_rightmargin((struct MAINLL *)0),
+ eff_leftmargin((struct MAINLL *)0));
+}
+\f
+
+/* return number of beams or flags to use for a given basic time */
+
+int
+numbeams(btime)
+
+int btime; /* basic time of note to be checked */
+
+{
+ int n;
+
+ /* no beams for long notes */
+ if (btime <= 4) {
+ return(0);
+ }
+
+ /* number of beams is equal to the number of bits 4 has to be
+ * shifted left in order to equal the given basic time */
+ for (n = 1; (4 << n) <= MAXBASICTIME; n++) {
+ if (btime == (4 << n)) {
+ return(n);
+ }
+ }
+ return(0);
+}
+\f
+
+/* given an accidental (#, &, x, B, n) return its music character
+ * C_SHARP, etc of the proper size. If not a valid accidental,
+ * return 0 */
+
+int acc2char(acc)
+
+int acc;
+
+
+{
+ switch (acc) {
+
+ case '&':
+ return(C_FLAT);
+ case '#':
+ return(C_SHARP);
+ case 'n':
+ return(C_NAT);
+ case 'x':
+ return(C_DBLSHARP);
+ case 'B':
+ return(C_DBLFLAT);
+ default:
+ /* no accidental */
+ return(0);
+ }
+}
+\f
+
+/* get the absolute x and y values for an INPCOORD */
+/* get the proper one of the coordinates of the specified location, and
+ * add in any offsets */
+
+double
+inpc_x(inpcoord_p, fname, lineno)
+
+struct INPCOORD *inpcoord_p; /* return the x value of this inpcoord */
+char *fname; /* filename, for error message */
+int lineno; /* for error message */
+
+{
+ double retval;
+
+
+ /* if hor_p is null, then this is an absolute coord, rather
+ * than relative to some variable */
+ if (inpcoord_p->hor_p == (float *) 0) {
+ if (inpcoord_p->counts != 0.0) {
+ /* parser should have blocked this case */
+ pfatal("can't specify time offset if no variable specified");
+ }
+ retval = inpcoord_p->hsteps * STEPSIZE;
+ }
+
+ /* X location is the x, e, or w value of the specified location
+ * variable, plus any offset plus any time offset */
+ else {
+ retval = inpcoord_p->hor_p [ inpcoord_p->htype ]
+ + inpcoord_p->hsteps * STEPSIZE
+ * coord_staffscale(inpcoord_p->hor_p)
+ + ( inpcoord_p->counts * inpcoord_p->hor_p [INCHPERWHOLE] /
+ (double) Score.timeden );
+ }
+
+ if (retval < 0.0 || retval > PGWIDTH) {
+ if (lineno > 0 && fname != (char *) 0) {
+ l_warning(fname, lineno,
+ "x value of %f is off the page", retval);
+ }
+ }
+ return(retval);
+}
+\f
+
+/* given an inpcoord, return its y coordinate */
+
+double
+inpc_y(inpcoord_p, fname, lineno)
+
+struct INPCOORD *inpcoord_p; /* return y value of this inpcoord */
+char *fname; /* filename, for error message */
+int lineno; /* for error message */
+
+{
+ double retval;
+
+
+ /* if vert_p is null, then this is absolute rather than relative */
+ if ( inpcoord_p->vert_p == (float *) 0) {
+ retval = inpcoord_p->vsteps * STEPSIZE;
+ }
+
+ /* Y value is y, n, or s value of specified location variable
+ * plus any vertical offset plus any vsteps offset */
+ else {
+ retval = inpcoord_p->vert_p [ inpcoord_p->vtype ]
+ + (inpcoord_p->vsteps * STEPSIZE
+ * coord_staffscale(inpcoord_p->vert_p));
+ }
+
+ if (retval < 0.0 || retval > PGHEIGHT) {
+ if (lineno > 0 && fname != (char *) 0) {
+ l_warning(fname, lineno,
+ "y value of %f is off the page", retval);
+ }
+ }
+ return(retval);
+}
+\f
+
+/* Given an INPCOORD, return the appropriate staffscale value. Look
+ * up which staff, if any, the INPCOORD is associated with, and then get
+ * the staffscale out of the SSVs.
+ */
+
+static double
+coord_staffscale(coord_array)
+
+float *coord_array;
+
+{
+ struct COORD_INFO *coord_info_p;
+
+
+ /* figure out what staffscale value to use, based on
+ * which staff the coordinate is associated with */
+ if ((coord_info_p = find_coord(coord_array))
+ != (struct COORD_INFO *) 0 &&
+ coord_info_p->staffno != 0) {
+ return(svpath(coord_info_p->staffno, STAFFSCALE)->staffscale);
+ }
+ else {
+ return(1.0);
+ }
+}
+\f
+
+/* return the y coordinate of the end of a note stem */
+/* (the end farthest from the note head) */
+
+double
+find_y_stem(gs_p)
+
+struct GRPSYL *gs_p; /* which group to get the stem of */
+
+{
+ /* error checks */
+ if (gs_p == (struct GRPSYL *) 0) {
+ pfatal("null group passed to find_y_stem");
+ }
+
+ if (gs_p->nnotes == 0) {
+ pfatal("group with no notes passed to find_y_stem (from line %d, grpcont %d)",
+ gs_p->inputlineno, gs_p->grpcont);
+ }
+
+ /* if stem is up, start at bottom note, if down at top */
+ if (gs_p->stemdir == UP) {
+ return(gs_p->notelist[ gs_p->nnotes - 1].c[AY] + gs_p->stemlen);
+ }
+ else {
+ return(gs_p->notelist[0].c[AY] - gs_p->stemlen);
+ }
+}
+\f
+
+/* return x coordinate of a note stem */
+
+double
+find_x_stem(gs_p)
+
+struct GRPSYL *gs_p; /* return x of stem of this group */
+
+{
+ double stem_adjust; /* to overlap the note head */
+
+ if ( gs_p == (struct GRPSYL *) 0) {
+ pfatal("bad group passed to find_x_stem");
+ }
+
+ /* if called with something longer than a half note, then there
+ * is no real stem. We must be being called for printing slashes,
+ * so in that case, the x of the "stem" is the x of the group */
+ if (gs_p->basictime < 2) {
+ return(gs_p->c[AX]);
+ }
+
+ /* move stem by half of stem width so edge lines up with edge of note */
+ stem_adjust = W_NORMAL / PPI / 2.0;
+ if (gs_p->stemdir == UP) {
+ stem_adjust = -stem_adjust;
+ }
+ return(gs_p->c[AX] + (gs_p->stemx + stem_adjust) * Staffscale);
+}
+\f
+
+/* return the width of a key signature in inches */
+
+double
+width_keysig(sharps, naturals)
+
+int sharps; /* how many sharps to print, or if negative, how many flats. */
+int naturals; /* how many naturals to print for canceling previous key */
+
+{
+ double total_width = 0.0;
+ int size;
+
+ /* In keysig, things are drawn closer together than
+ * in other places, so to get the total width, we first
+ * multiply the width of the sharp, flat, or natural character
+ * by the number of times it is to be printed, then subtract off
+ * two points for each character printed, except for naturals,
+ * which are only jammed together by one point. */
+
+ size = adj_size(DFLT_SIZE, Staffscale, (char *) 0, -1);
+ if (sharps >= 1) {
+ total_width = (width(FONT_MUSIC, size, C_SHARP) - 2.0 * Stdpad)
+ * sharps;
+ }
+ else if (sharps <= -1) {
+ /* negative sharps are flats */
+ total_width = (width(FONT_MUSIC, size, C_FLAT) - 2.0 * Stdpad)
+ * -sharps;
+ }
+ if (naturals != 0) {
+ total_width += (width(FONT_MUSIC, size, C_NAT) - Stdpad)
+ * abs(naturals) + 3.0 * Stdpad;
+ }
+ return(total_width);
+}
+\f
+/*
+ * Name: nextgrpsyl()
+ *
+ * Abstract: Find next GRPSYL in this voice (same measure or not).
+ *
+ * Returns: Pointer to the GRPSYL, or 0 if none.
+ *
+ * Description: This function, given a GRPSYL and the MLL structure it hangs
+ * off of, returns the next GRPSYL in this voice, even if it's in
+ * the next measure. If it is in the next measure, *mll_p_p gets
+ * updated. But if that next measure is a second or later ending,
+ * it's not considered to be a "next" measure, so return 0.
+ */
+
+struct GRPSYL *
+nextgrpsyl(gs_p, mll_p_p)
+
+struct GRPSYL *gs_p; /* the given GRPSYL */
+struct MAINLL **mll_p_p; /* main linked list structure it is hanging off of */
+
+{
+ struct MAINLL *mll_p; /* point at a MLL item */
+ int endingloc; /* of the following barline */
+
+
+ /* if not at end of measure, just return the next GRPSYL */
+ if (gs_p->next != 0) {
+ return (gs_p->next);
+ }
+
+ mll_p = *mll_p_p; /* save original MLL item */
+
+ /*
+ * We hit the end of the measure. We need to find the first group in
+ * the next measure. Find the coming bar line, then the corresponding
+ * staff in the next measure. We do this in case the number of staffs
+ * changes back and forth; we don't want to find the staff in some
+ * later measure.
+ */
+ for (*mll_p_p = (*mll_p_p)->next; *mll_p_p != (struct MAINLL *) 0 &&
+ (*mll_p_p)->str != S_BAR; *mll_p_p = (*mll_p_p)->next) {
+ ;
+ }
+
+ /* if we hit the end of the MLL, there is no next GRPSYL */
+ if (*mll_p_p == (struct MAINLL *) 0) {
+ return (struct GRPSYL *) 0;
+ }
+
+ /* we found a bar; get its endingloc */
+ endingloc = (*mll_p_p)->u.bar_p->endingloc;
+
+ /*
+ * Search for this staff in next measure. If we find a pseudobar while
+ * doing this, save its endingloc in preference to the real bar's.
+ */
+ for (*mll_p_p = (*mll_p_p)->next; *mll_p_p != (struct MAINLL *) 0 &&
+ (*mll_p_p)->str != S_BAR &&
+ ((*mll_p_p)->str != S_STAFF ||
+ (*mll_p_p)->u.staff_p->staffno != gs_p->staffno);
+ *mll_p_p = (*mll_p_p)->next) {
+
+ if ((*mll_p_p)->str == S_CLEFSIG &&
+ (*mll_p_p)->u.clefsig_p->bar_p != (struct BAR *) 0) {
+ endingloc = (*mll_p_p)->u.clefsig_p->bar_p->endingloc;
+ }
+ }
+
+ /* if we hit the end or another bar before finding our staff, return */
+ if (*mll_p_p == (struct MAINLL *) 0 || (*mll_p_p)->str == S_BAR) {
+ return (struct GRPSYL *) 0;
+ }
+
+ /*
+ * We found the appropriate staff in the next measure. But if we have
+ * crossed into a second or later ending, this bar doesn't really
+ * "follow" the previous bar, and we must return null. So if endingloc
+ * shows this is the case, we must search backwards to find out if we
+ * were already in an ending.
+ */
+ if (endingloc == STARTITEM) {
+ while (mll_p != 0 && mll_p->str != S_BAR && (mll_p->str !=
+ S_CLEFSIG || mll_p->u.clefsig_p->bar_p == 0))
+ mll_p = mll_p->prev;
+
+ /* set endingloc of the previous measure */
+ if (mll_p == 0) {
+ endingloc = NOITEM;
+ } else if (mll_p->str == S_BAR) {
+ endingloc = mll_p->u.bar_p->endingloc;
+ } else {
+ endingloc = mll_p->u.clefsig_p->bar_p->endingloc;
+ }
+
+ /* if we were already in an ending, there's no next GRPSYL */
+ if (endingloc == STARTITEM || endingloc == INITEM) {
+ return (struct GRPSYL *) 0;
+ }
+ }
+
+ /* return the first GRPSYL of the appropriate voice */
+ return ((*mll_p_p)->u.staff_p->groups_p[ gs_p->vno - 1 ]);
+}
+\f
+/*
+ * Name: prevgrpsyl()
+ *
+ * Abstract: Find previous GRPSYL in this voice (same measure or not).
+ *
+ * Returns: Pointer to the GRPSYL, or 0 if none.
+ *
+ * Description: This function, given a GRPSYL and the MLL structure it hangs
+ * off of, returns the previous GRPSYL in this voice, even if it's
+ * in an earlier measure. If we are at the start of an ending,
+ * it skips over any previous ending and goes to the measure
+ * preceding the first ending. If the resulting GRPSYL is in a
+ * previous measure, *mll_p_p gets updated.
+ */
+
+struct GRPSYL *
+prevgrpsyl(gs_p, mll_p_p)
+
+struct GRPSYL *gs_p; /* the given GRPSYL */
+struct MAINLL **mll_p_p; /* main linked list structure it is hanging off of */
+
+{
+ struct GRPSYL *gs2_p; /* for looping through prev measure's list */
+ struct BAR *bar_p; /* point at a bar line */
+ struct MAINLL *mll_p; /* point at a MLL item */
+ int pseudo; /* was the last thing we saw a pseudobar? */
+ int barcount; /* how many bar lines we looped backward thru*/
+ int safmoae; /* "started at first measure of an ending" */
+
+
+ /* if not at start of measure, just return the previous GRPSYL */
+ if (gs_p->prev != (struct GRPSYL *) 0) {
+ return (gs_p->prev);
+ }
+
+ /*
+ * We hit the start of the measure. Loop backwards through the MLL
+ * looking for the bar line at the start of the "previous" measure.
+ * If our measure is not the first measure of an ending, this is
+ * simply the bar at the start of the previous measure. Otherwise,
+ * this is the bar before the measure before the first ending. Also
+ * handle the cases where we fall off the start of the MLL.
+ */
+ bar_p = 0;
+ mll_p = *mll_p_p;
+ pseudo = NO;
+ barcount = 0;
+ safmoae = NO;
+ do {
+ /* find preceding bar or pseudobar, if either exists */
+ for (mll_p = mll_p->prev; mll_p != (struct MAINLL *) 0 &&
+ mll_p->str != S_BAR &&
+ (mll_p->str != S_CLEFSIG ||
+ mll_p->u.clefsig_p->bar_p == 0);
+ mll_p = mll_p->prev) {
+ ;
+ }
+
+ /*
+ * If we hit the start of the MLL without crossing any bars, or
+ * just a pseudobar, there is no preceding GRPSYL, so return 0.
+ * (Depending on who is calling this function, the pseudobar at
+ * the start of the song may or may not exist.) Otherwise, it
+ * must be that we started in the second measure, or an ending
+ * that we skipped over started there. Point at the start of
+ * the MLL, and get out of the loop. Note that we can't still
+ * be in the process of skipping over endings: no ending can
+ * start at the first measure of the song, because there is no
+ * bar line there.
+ */
+ if (mll_p == 0) {
+ if (barcount == 0 || (barcount == 1 && pseudo == YES)) {
+ return (struct GRPSYL *) 0;
+ }
+ mll_p = Mainllhc_p;
+ break;
+ }
+
+ barcount++;
+
+ /*
+ * Point bar_p at the relevant bar/pseudobar for checking the
+ * endingloc. If this is a pseudobar, it's relevant. If this
+ * is a bar, it's relevant only if it's the first thing we've
+ * seen, or the thing we saw last was not a pseudobar. That
+ * is, the endingloc of the bar at the end of a score is to be
+ * ignored, because the true endingloc has been moved to the
+ * next score's pseudobar. We need to worry about this because
+ * of the case where a second ending starts at the start of the
+ * new score (STARTITEM) and the previous score ends with
+ * ENDITEM, which should be ignored.
+ */
+ if (mll_p->str == S_BAR) {
+ if (bar_p == 0 || pseudo == NO) {
+ bar_p = mll_p->u.bar_p;
+ }
+ if (pseudo == YES) {
+ barcount--; /* forget this bar */
+ pseudo = NO;
+ }
+ } else {
+ bar_p = mll_p->u.clefsig_p->bar_p;
+ pseudo = YES;
+ }
+
+ /*
+ * If this is the first measure we're backing into, and this
+ * first bar we hit shows that our GRPSYL was in the first
+ * measure of an ending, remember that fact.
+ */
+ if (barcount == 1 && bar_p->endingloc == STARTITEM) {
+ safmoae = YES;
+ }
+
+ /*
+ * Get out, when, in the normal case, we've hit the second meaningful
+ * bar; or in the safmoae case, we've skipped back to the back before
+ * the first bar of the first ending.
+ */
+ } while (! ( (safmoae == NO && barcount == 2) || (safmoae == YES &&
+ (bar_p->endingloc == NOITEM || bar_p->endingloc == ENDITEM))));
+
+ /*
+ * Search forward to the next bar, which is the bar before which we
+ * want to find a GRPSYL. We don't care about pseudobars here.
+ */
+ for (mll_p = mll_p->next; mll_p->str != S_BAR; mll_p = mll_p->next) {
+ ;
+ }
+
+ /*
+ * Now mll_p is the bar before which we want to find the GRPSYL.
+ * Find the corresponding staff in the previous measure. We do
+ * this in case the number of staffs changes back and forth; we
+ * don't want to find the staff in some earlier measure.
+ */
+
+ /* search for this staff in previous measure */
+ for (*mll_p_p = mll_p->prev; *mll_p_p != (struct MAINLL *) 0 &&
+ (*mll_p_p)->str != S_BAR &&
+ ((*mll_p_p)->str != S_STAFF ||
+ (*mll_p_p)->u.staff_p->staffno != gs_p->staffno);
+ *mll_p_p = (*mll_p_p)->prev) {
+ ;
+ }
+
+ /* if we hit the start or another bar before finding our staff, return*/
+ if (*mll_p_p == (struct MAINLL *) 0 || (*mll_p_p)->str == S_BAR) {
+ return (struct GRPSYL *) 0;
+ }
+
+ /* return the last GRPSYL of the appropriate voice */
+ gs2_p = (*mll_p_p)->u.staff_p->groups_p[ gs_p->vno - 1 ];
+ if (gs2_p == (struct GRPSYL *) 0) {
+ return(gs2_p);
+ }
+ while (gs2_p->next != (struct GRPSYL *) 0) {
+ gs2_p = gs2_p->next;
+ }
+
+ return (gs2_p);
+}
+\f
+
+/* if user asked for octave marks, we need to transpose any affected notes
+ * by the appropriate number of octaves. This should be called for a measure
+ * at a time. It will handle all the octave marks
+ * within the measure for the current voice, both those carrying over into
+ * this measure and ones starting there. If an octave mark spills beyond
+ * the end of the measure, the amount to transpose and how long to do so
+ * is saved away for use in the next measure. Checks for things becoming
+ * out of range because of the transposition are not done here; caller
+ * must do that if they care. */
+
+void
+octave_transpose(staff_p, mll_p, vno, normdir)
+
+struct STAFF *staff_p;
+struct MAINLL *mll_p; /* staff_p connects here, used for bends */
+int vno; /* voice number */
+int normdir; /* YES if should move note pitches up for above and
+ * down for below. NO if the inverse should be done */
+
+{
+ struct GRPSYL *gs_p; /* walk through list of GRPSYLs */
+ struct GRPSYL *gs_roll_p; /* group generated for a roll */
+ struct STUFF *stuff_p; /* to look for octave marks */
+ RATIONAL total_time; /* accumulated time in measure */
+ float float_total_time; /* for comparing with count */
+ int carry_adjust; /* adjust to carry into next measure */
+ int carry_bars; /* how many bars to carry over */
+ float carry_counts; /* how many counts in final bar */
+ int staffno; /* which staff we are working with */
+
+
+ staffno = staff_p->staffno;
+ carry_adjust = carry_bars = 0;
+ carry_counts = 0.0;
+
+ /* if currently have octave mark */
+ if (Octave_adjust[staffno] != 0) {
+
+ /* if bar count > 0, transpose all notes in measure */
+ if (--(Octave_bars[staffno]) > 0) {
+
+ for (gs_p = staff_p->groups_p[vno];
+ gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->next) {
+
+ grp_octave_adjust(gs_p, Octave_adjust[staffno], mll_p);
+ }
+ }
+
+ /* otherwise just transpose until specified time */
+ else {
+ total_time = Zero;
+ for (gs_p = staff_p->groups_p[vno];
+ gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->next) {
+
+ float_total_time = RAT2FLOAT(total_time)
+ * Score.timeden + 1.0;
+
+ if (float_total_time <= Octave_count[staffno]) {
+ grp_octave_adjust(gs_p,
+ Octave_adjust[staffno], mll_p);
+ }
+ else {
+ break;
+ }
+ total_time = radd(total_time, gs_p->fulltime);
+ }
+
+ Octave_adjust[staffno] = 0;
+ }
+ }
+
+ /* go through stuff list. If any octave marks, transpose appropriate
+ * notes in this measure. If extends to next measure, remember for
+ * next time. If user put in more than one octave
+ * mark going over the bar, catch that. If there are overlaps within
+ * a measure, it seems like too much trouble to catch this, so just
+ * transpose as often as they say and let them figure out why it
+ * sounds funny. */
+ for (stuff_p = staff_p->stuff_p; stuff_p != (struct STUFF *) 0;
+ stuff_p = stuff_p->next) {
+
+ if (stuff_p->stuff_type == ST_OCTAVE) {
+
+ if (Octave_adjust[staffno] != 0) {
+ l_warning(stuff_p->inputfile,
+ stuff_p->inputlineno,
+ "overlapping octave marks");
+ }
+
+ /* figure out how many octaves to move */
+ Octave_adjust[staffno] = parse_octave(stuff_p->string,
+ stuff_p->place, stuff_p->inputfile,
+ stuff_p->inputlineno);
+
+ /* if this call is for inverse transpostion, adjust
+ * to go in opposite direction */
+ if (normdir == NO) {
+ Octave_adjust[staffno] *= -1;
+ }
+
+ /* figure out to which count the octave mark applies
+ * within this measure. */
+ Octave_count[staffno] = (stuff_p->end.bars > 0 ? 1.0 +
+ RAT2FLOAT(Score.time) * Score.timeden
+ : stuff_p->end.count);
+
+ total_time = Zero;
+ for (gs_p = staff_p->groups_p[vno];
+ gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->next) {
+
+ float_total_time = RAT2FLOAT(total_time) *
+ Score.timeden + 1.0;
+ if (float_total_time >= stuff_p->start.count) {
+
+ if (float_total_time <=
+ Octave_count[staffno]) {
+ /* is within the mark, so move */
+ grp_octave_adjust(gs_p,
+ Octave_adjust[staffno],
+ mll_p);
+ }
+
+ /* special case. If we have a rolled
+ * chord, and user specified
+ * an octave mark without a til clause,
+ * all the chords that got
+ * generated internally to cause the
+ * "roll" effect need to be transposed,
+ * even though they have been moved
+ * in time */
+ if (float_total_time == stuff_p->start.count
+ && stuff_p->end.bars == 0
+ && stuff_p->end.count == 0.0
+ && gs_p->inputlineno < 0) {
+
+ /* adjust all the groups generated
+ * to create the roll effect */
+ for (gs_roll_p = gs_p->next;
+ gs_roll_p != (struct GRPSYL *) 0;
+ gs_roll_p = gs_roll_p->next) {
+
+ grp_octave_adjust(gs_roll_p,
+ Octave_adjust[staffno],
+ mll_p);
+ /* stop when we hit the
+ * end of the roll */
+ if (gs_roll_p->inputlineno > 0) {
+ break;
+ }
+ }
+ }
+ }
+
+ total_time = radd(total_time, gs_p->fulltime);
+ }
+
+ /* if octave mark carried over into subsequent
+ * measure(s), make a note of that for future use */
+ if (stuff_p->end.bars > 0) {
+ if (carry_adjust != 0) {
+ l_warning(stuff_p->inputfile,
+ stuff_p->inputlineno,
+ "overlapping octave marks");
+ }
+ carry_bars = stuff_p->end.bars;
+ carry_counts = stuff_p->end.count;
+ carry_adjust = Octave_adjust[staffno];
+ }
+
+ Octave_adjust[staffno] = 0;
+ }
+ }
+
+ /* above octave marks that were put in on the same input line will
+ * be in backwards order (because above stuff needs to be that
+ * way to pile on correctly). However, if the last octave in the
+ * stuff input line carried over a bar line, we would see it
+ * first and lose the carryover information when handling the earlier
+ * octave marks (which are later in the stuff list). That's why the
+ * carryover information had to be saved. Now we can assign it */
+ if (carry_bars > 0) {
+ Octave_adjust[staffno] = carry_adjust;
+ Octave_bars[staffno] = carry_bars;
+ Octave_count[staffno] = carry_counts;
+ }
+}
+\f
+
+/* transpose all the notes in a group by the specified octave adjustment */
+
+static void
+grp_octave_adjust(gs_p, adj, mll_p)
+
+struct GRPSYL *gs_p; /* adjust this group */
+int adj; /* add this (potentially negative) value to
+ * each notelist octave */
+struct MAINLL *mll_p; /* STAFF of gs_p for finding prev grp for bends */
+
+{
+ register int n;
+ struct GRPSYL *prevgs_p; /* previous group, for bends */
+
+ for (n = 0; n < gs_p->nnotes; n++) {
+ gs_p->notelist[n].octave += adj;
+ }
+ /* Transpose any bends going to this group */
+ prevgs_p = prevgrpsyl(gs_p, &mll_p);
+ if (prevgs_p == 0) {
+ /* If there is no previous group, nothing to transpose. */
+ return;
+ }
+ for (n = 0; n < prevgs_p->nnotes; n++) {
+ if (prevgs_p->notelist[n].is_bend == YES) {
+ /* Note that when is_bend is YES, nslurto will always
+ * be 1, but we loop through as many as there are
+ * (all 1 of them!), to not be dependent
+ * on that piece of inside information.
+ */
+ int s;
+ for (s = 0; s < prevgs_p->notelist[n].nslurto; s++) {
+ prevgs_p->notelist[n].slurtolist[s].octave += adj;
+ }
+ }
+ }
+}
+\f
+
+/* Given a group and note value, return the effective accidental
+ * on that note, (-2 to +2) taking key signature
+ * and previous accidentals into account.
+ * There are certain pathological cases that this doesn't
+ * handle right, notably if the user changes the key signature at a bar
+ * line, and the note in question is tied to from before that bar line,
+ * and the key signature change is such that it changes what the accidental
+ * should be. But it should handle anything less convoluted than that.
+ */
+
+int
+eff_acc(gs_p, note_p, mll_p)
+
+struct GRPSYL *gs_p; /* get effective accidental for note in this group */
+struct NOTE *note_p; /* get for this note */
+struct MAINLL *mll_p; /* main list item that points to gs_p */
+
+{
+ struct MAINLL *orig_mll_p;
+ struct GRPSYL *pgs_p; /* previous group */
+ int n;
+ int tie_break; /* YES if there has been a break
+ * in the chain of notes
+ * tied together */
+ int eff_accidental; /* effective accidental so far */
+
+
+ /* if this group has an explicit accidental, that's also the
+ * effective accidental */
+ if (note_p->accidental != '\0') {
+ /* note: the - 2 is to adjust for natural being element 2
+ * of the Circle array */
+ return( (int) (strchr(Acclets, note_p->accidental) - Acclets) - 2);
+ }
+
+ /* remember which measure we are starting in, so we know when we cross
+ * a bar line */
+ orig_mll_p = mll_p;
+
+ /* init to assume we might have ties */
+ tie_break = NO;
+
+ /* if all else fails, we will use the accidental from the key sig */
+ eff_accidental = acc_from_keysig(note_p->letter, gs_p->staffno,
+ mll_p);
+
+ /* back up until we figure out the effective accidental */
+ for (pgs_p = prevgrpsyl(gs_p, &mll_p); pgs_p != (struct GRPSYL *) 0;
+ pgs_p = prevgrpsyl(pgs_p, &mll_p)) {
+
+
+ /* see if this group contains the note in question */
+ for (n = 0; n < pgs_p->nnotes; n++) {
+ if (pgs_p->notelist[n].letter == note_p->letter &&
+ pgs_p->notelist[n].octave
+ == note_p->octave) {
+ /* it does have the note: it's notelist[n] */
+ break;
+ }
+ }
+
+ /* see if this is end of ties to the note, working backwards */
+ if (n == pgs_p->nnotes) {
+ /* no note at all, so clearly no tied note */
+ tie_break = YES;
+ }
+ else if (pgs_p->notelist[n].tie == NO) {
+ /* end of chain of tied notes */
+ tie_break = YES;
+ }
+
+ if (orig_mll_p == mll_p) {
+ /* we're still in same measure. If we have a matching
+ * note, see if it has an accidental */
+ if (n < pgs_p->nnotes) {
+ if (pgs_p->notelist[n].accidental != '\0') {
+ return( (int) (strchr(Acclets,
+ pgs_p->notelist[n].accidental)
+ - Acclets) - 2);
+ }
+ }
+
+ /* have to keep backing up */
+ }
+ else {
+ /* Now in previous measure. If no matching note,
+ * we use the effective accidental found so far */
+ if (n == pgs_p->nnotes) {
+ return(eff_accidental);
+ }
+
+ /* if the note isn't tied, then use the most recent
+ * effective accidental we found */
+ if (pgs_p->notelist[n].tie == NO || tie_break == YES) {
+ return(eff_accidental);
+ }
+
+ /* if there is an accidental on this note,
+ * then it's the one we're looking for. */
+ if (pgs_p->notelist[n].accidental != '\0') {
+ return( (int) (strchr(Acclets,
+ pgs_p->notelist[n].accidental) -
+ Acclets - 2));
+ }
+
+ /* need to continue working backwards toward
+ * the beginning of this new measure */
+ orig_mll_p = mll_p;
+ eff_accidental = acc_from_keysig(note_p->letter,
+ gs_p->staffno, mll_p);
+ }
+ }
+
+ /* backed up all the way to the beginning of the song, use last we
+ * found */
+ return(eff_accidental);
+}
+\f
+
+/* given a letter and staff number, return 1 if the pitch with that letter
+ * gets a sharps according to the key signature, or -1 if it gets a flat,
+ * or 0 if it gets neither. */
+
+int
+acc_from_keysig(letter, staffno, mll_p)
+
+int letter; /* which pitch */
+int staffno; /* which staff to get the key signature from */
+struct MAINLL *mll_p; /* pitch is from the staff hanging off of here */
+
+{
+ int index; /* where the letter is in circle of fifths */
+ int sharps; /* sharps in key sig for the given staff */
+ struct SSV *ssv_p; /* to get key signature */
+
+
+ /* find letter in circle of fifths */
+ index = strchr(Circle, letter) - Circle + 1;
+
+ /* get key signature. Unfortunately, the SSVs may not be
+ * accurate at the time we are called, so we have to search
+ * backwards in the main list for the most recent relevant
+ * key signature change. */
+ for (sharps = 0; mll_p != (struct MAINLL *) 0; mll_p = mll_p->prev) {
+ if (mll_p->str == S_SSV) {
+ ssv_p = mll_p->u.ssv_p;
+
+ /* does this SSV have a key signature change in it? */
+ if (ssv_p->used[SHARPS] == YES) {
+ if (ssv_p->context == C_STAFF &&
+ ssv_p->staffno == staffno) {
+ /* aha! found the most recent
+ * key signature
+ * for the relevant staff */
+ sharps = ssv_p->sharps;
+ break;
+ }
+ else if (ssv_p->context == C_SCORE) {
+ /* this will be the score-wide default
+ * if we don't find a staff-specific
+ * value, so save this as our default */
+ sharps = ssv_p->sharps;
+ }
+ }
+ }
+ }
+
+ if (sharps > 0) {
+ /* key signature is one with sharps */
+ if (index <= sharps) {
+ /* this letter gets a sharp */
+ return(1);
+ }
+ }
+ else if (sharps < 0) {
+ /* key signature is one with flats */
+ if ( (8 - index) <= -sharps) {
+ /* this letter gets a flat */
+ return(-1);
+ }
+ }
+
+ /* must be a natural */
+ return(0);
+}
+
+/* Set Staffscale, Stepsize, Stdpad, and other similar values based on the
+ * specified staff number. If staff of 0 is given, set Staffscale to the
+ * score value
+ */
+
+void
+set_staffscale(s)
+
+int s; /* which staff */
+
+{
+ Staffscale = (s == 0 ? Score.staffscale
+ : svpath(s, STAFFSCALE)->staffscale);
+ Stepsize = STEPSIZE * Staffscale;
+ Stdpad = STDPAD * Staffscale;
+ Flagsep = FLAGSEP * Staffscale;
+ Smflagsep = SMFLAGSEP * Staffscale;
+ Tupheight = TUPHEIGHT * Staffscale;
+}
+\f
+
+/* Determine the space between lines of a grid. A staff of 0 means use
+ * the score size. A staff of -1 means ATEND. Return distance in inches. */
+
+double
+gridspace(staff)
+
+int staff;
+
+{
+ double space;
+
+ if (staff == -1) {
+ space = ATEND_GS * STEPSIZE * Score.gridscale;
+ }
+ else {
+ space = WHEREUSED_GS * STEPSIZE
+ * svpath(staff, STAFFSCALE)->staffscale
+ * svpath(staff, GRIDSCALE)->gridscale;
+ }
+ return(space);
+}
+\f
+
+/* Given a grid, return (via pointer) the items needed for the PostScript
+ * plus the top fret.
+ */
+
+void
+gridinfo(grid_p, staff, frets_p, fretnum_p, numvert_p, topfret_p)
+
+struct GRID *grid_p;
+int staff; /* 0 == score, -1 = ATEND, otherwise the staff number */
+int *frets_p; /* how many frets high the grid should be */
+int *fretnum_p; /* the N of "N fr" */
+int *numvert_p; /* how many frets from the top to print the "N fr" */
+int *topfret_p; /* the fret number of the top line of the grid */
+
+{
+ int minfret, maxfret; /* smallest and largest frets used */
+ int rightmost_fret; /* the 'N' of 'N fr" if any */
+ int right_stringnum; /* string number having rightmost_fret */
+ int mincurvefret; /* smallest fret number inside curve */
+ int has_o; /* if there are 'o' items */
+ int gridfret;
+ int s;
+
+
+ /* Go through strings finding min/max/rightmost */
+ minfret = MAXFRET + 1;
+ maxfret = MINFRET - 1;
+ mincurvefret = MAXFRET + 1;
+ has_o = NO;
+ rightmost_fret = right_stringnum = 0; /* avoids bogus warnings */
+ for (s = 0; s < grid_p->numstr; s++) {
+ /* x o and - things don't count */
+ if (grid_p->positions[s] > 0) {
+ if (grid_p->positions[s] < minfret) {
+ minfret = grid_p->positions[s];
+ }
+ if (grid_p->positions[s] > maxfret) {
+ maxfret = grid_p->positions[s];
+ }
+ rightmost_fret = grid_p->positions[s];
+ right_stringnum = s;
+
+ /* find smallest fret inside curve */
+ if (grid_p->curvel != 0 && s >= grid_p->curvel - 1 &&
+ s <= grid_p->curver - 1 &&
+ grid_p->positions[s] < mincurvefret) {
+ mincurvefret = grid_p->positions[s];
+ }
+ }
+ else if (grid_p->positions[s] == 0) {
+ has_o = YES;
+ }
+ }
+
+ /* set the values to defaults, then calculate actuals if needed */
+ *frets_p = 5;
+ *fretnum_p = 0;
+ *numvert_p = 0;
+ if (minfret <= MAXFRET) {
+ /* at least one fret was used */
+
+ /* figure out how many frets tall to make the grid */
+ *frets_p = maxfret + 1;
+
+ /* see if gridfret is set */
+ gridfret = svpath(staff == -1 ? 0 : staff, GRIDFRET)->gridfret;
+ if (gridfret != NOGRIDFRET) {
+ /* gridfret is set; see if all frets larger than that.
+ * But we only use "N fr" if there are no 'o' items. */
+ if (has_o == NO && minfret >= gridfret) {
+ /* We will need "N fr"
+ * Usually we use the rightmost string
+ * that has a fret on it,
+ * but there is one special case:
+ * if the curve comes at least that far right,
+ * and the minimum fret inside the curve is
+ * smaller than the rightmost_fret, then we
+ * put the "N fr" by the curve minimum.
+ */
+ if (grid_p->curver - 1 >= right_stringnum
+ && mincurvefret < rightmost_fret) {
+ rightmost_fret = mincurvefret;
+ }
+ *fretnum_p = rightmost_fret;
+ *numvert_p = rightmost_fret - minfret + 1;
+ *frets_p = maxfret - minfret + 2;
+ }
+ }
+
+ if (*frets_p < 5) {
+ /* always at least 4 frets plus top line */
+ *frets_p = 5;
+ }
+ }
+ *topfret_p = (*fretnum_p == 0 ? 0 : *fretnum_p - *numvert_p);
+}
+\f
+
+/* Determine the dimensions of a grid, relative to a point in the middle
+ * of the top line of the grid, and return them via pointers.
+ * If pointers are 0, don't bother; caller doesn't care about these things.
+ * Things in this function must be kept in sync with the PostScript prolog
+ * definition of grids.
+ */
+
+void
+gridsize(grid_p, staff, north_p, south_p, east_p, west_p)
+
+struct GRID *grid_p; /* find the size of this grid */
+int staff; /* use this staff for scaling. 0 means score,
+ * -1 means ATEND */
+float *north_p; /* return values... */
+float *south_p;
+float *east_p;
+float *west_p;
+
+{
+ double space; /* distance between adjacent line of the grid */
+ int frets;
+ int fretnum;
+ int numvert;
+ int topfret;
+ int s; /* string index */
+
+ if (grid_p == 0) {
+ pfatal("gridsize() was passed a null pointer");
+ }
+
+ /* determine distance between grid lines and other needed info */
+ space = gridspace(staff);
+ gridinfo(grid_p, staff, &frets, &fretnum, &numvert, &topfret);
+
+ /* Start with minimum. East and west are equal, at half the
+ * total grid width, based on number of strings. The number of
+ * spaces is the number of strings minus one, but dots, X's,
+ * and O's will hang over the sides, so use the number of strings.
+ * Then adjust east for "N fr" if needed. */
+ if (west_p != 0) {
+ *west_p = -((space * grid_p->numstr) / 2.0);
+ }
+ if (east_p != 0) {
+ *east_p = (space * grid_p->numstr) / 2.0;
+ if (fretnum > 0) {
+ /* We will need "N fr".
+ * Get enough space to hold
+ * font, size, 2 digits, space, "fr", null */
+ char tmp[8];
+
+ /* this is printed in Palatino Roman */
+ tmp[0] = (char) FONT_PR;
+
+ /* Between staffscale and gridscale,
+ * we could get a size that we can't represent
+ * in internal format, so get string width
+ * in default size and adjust afterwards. */
+ tmp[1] = (char) DFLT_SIZE;
+
+ /* since we know there are no funny characters
+ * in this string, we can cheat and not bother
+ * to call the string normalizer. */
+ sprintf(tmp + 2, "%d fr", fretnum);
+
+ *east_p += strwidth(tmp) *
+ (space * PPI * 1.9)/ DFLT_SIZE;
+ }
+ }
+
+ if (north_p != 0) {
+ /* Always put almost one space of padding on top, which allows
+ * room for x's and o's and curves. Even if this particular grid
+ * doesn't have those, many do, so it's nice to line
+ * all of them up as much as possible */
+ *north_p = 0.85 * space;
+ /* If there is a curve above the top fret,
+ * with x's or o's above it, leave some more space */
+ if (grid_p->positions[grid_p->curvel - 1] == topfret + 1
+ || grid_p->positions[grid_p->curver - 1]
+ == topfret + 1) {
+ for (s = grid_p->curvel; s <= grid_p->curver; s++) {
+ if (grid_p->positions[s-1] == 0 ||
+ grid_p->positions[s-1]
+ == -1) {
+ *north_p += 0.7 * space;
+ break;
+ }
+ }
+ }
+ }
+
+ /* Grid is always at least 4 boxes high, more if needed,
+ * plus 1/2 space of padding at bottom */
+ if (south_p != 0) {
+ *south_p = -(frets - 0.5) * space;
+ }
+}
+\f
+
+/* This function returns the minimum distance needed between the current
+ * staff and previous one, given their clefs and allowing for a measure
+ * number. The clefs might be NOCLEF, like if this is for the top
+ * staff of a page or a staff where printclef is false.
+ */
+
+double
+clefspace(prevclef, prevscale, curclef, curscale, measnum)
+
+int prevclef; /* clef on staff above */
+double prevscale; /* staffscale for the staff above */
+int curclef; /* clef on staff below */
+double curscale; /* staffscale for the staff below */
+int measnum; /* YES if a measure number needs to be printed */
+
+{
+ double cur_extend; /* space needed for current clef */
+ double prev_extend; /* space needed for clef above */
+ double space_needed; /* total for both clefs and measure number */
+
+
+ /* Figure out how much the clef on current staff sticks up,
+ * Do in approximate STEPSIZEs here, and adjust later for scale. */
+ switch (curclef) {
+ case TREBLE_8A:
+ cur_extend = 6.0;
+ break;
+ case TREBLE:
+ case TREBLE_8:
+ cur_extend = 4.0;
+ break;
+ case BARITONE:
+ cur_extend = 4.2;
+ break;
+ case TENOR:
+ cur_extend = 2.2;
+ break;
+ case FRENCHVIOLIN:
+ cur_extend = 2.0;
+ break;
+ default:
+ cur_extend = 0.0;
+ break;
+ }
+
+ /* Similar for the clef above, only how much it sticks down
+ * rather than up */
+ switch (prevclef) {
+ case TREBLE:
+ case TREBLE_8A:
+ prev_extend = 3.2;
+ break;
+ case MEZZOSOPRANO:
+ prev_extend = 2.2;
+ break;
+ case SOPRANO:
+ prev_extend = 4.2;
+ break;
+ case FRENCHVIOLIN:
+ case TREBLE_8:
+ prev_extend = 5.2;
+ break;
+ default:
+ prev_extend = 0.0;
+ break;
+ }
+
+ /* Add top and bottom together, adjusting for scale factors,
+ * and adding a little padding */
+ space_needed = prev_extend * STEPSIZE * prevscale +
+ cur_extend * STEPSIZE * curscale +
+ STDPAD * curscale;
+
+ /* Add on the space for the measure number, if necessary.
+ * Note that we can use fontascent since all digits are that high. */
+ if (measnum == YES) {
+ space_needed += fontascent(Score.measnumfamily + Score.measnumfont,
+ Score.measnumsize) + STDPAD;
+ }
+ return(space_needed);
+}
+\f
+/*
+ * Name: eff_rightmargin()
+ *
+ * Abstract: Return the effective right margin for this score.
+ *
+ * Returns: the margin in inches
+ *
+ * Description: There are two reason that code can't just use Score.rightmargin
+ * but must call this function. First, the way the "scale" param
+ * works, we pretend the paper is smaller by that amount and then
+ * in PostScript magnify it back to the real size; but margins are
+ * not to be scaled, so we have to fake out the code by dividing
+ * out the scale here. Second, the user can override the param
+ * on a "newscore" or "newpage". To ignore a user override, pass
+ * 0 for mainll_p, else pass some MLL on that score.
+ */
+
+double
+eff_rightmargin(mainll_p)
+
+struct MAINLL *mainll_p; /* MLL struct on some score, or 0 for normal margin */
+
+{
+ /* if not already at a FEED, find FEED at right end of this score */
+ while (mainll_p != 0 && mainll_p->str != S_FEED)
+ mainll_p = mainll_p->next;
+
+ /* if there is none, or there is no override, use the parameter */
+ if (mainll_p == 0 || mainll_p->u.feed_p->rightmargin < 0.0)
+ return (Score.rightmargin / Score.scale_factor);
+
+ /* use this override value */
+ return (mainll_p->u.feed_p->rightmargin / Score.scale_factor);
+}
+\f
+/*
+ * Name: eff_leftmargin()
+ *
+ * Abstract: Return the effective left margin for this score.
+ *
+ * Returns: the margin in inches
+ *
+ * Description: There are two reason that code can't just use Score.leftmargin
+ * but must call this function. First, the way the "scale" param
+ * works, we pretend the paper is smaller by that amount and then
+ * in PostScript magnify it back to the real size; but margins are
+ * not to be scaled, so we have to fake out the code by dividing
+ * out the scale here. Second, the user can override the param
+ * on a "newscore" or "newpage". To ignore a user override, pass
+ * 0 for mainll_p, else pass some MLL on that score.
+ */
+
+double
+eff_leftmargin(mainll_p)
+
+struct MAINLL *mainll_p; /* MLL struct on some score, or 0 for normal margin */
+
+{
+ /* if not already at a FEED, find FEED at left end of this score */
+ while (mainll_p != 0 && mainll_p->str != S_FEED)
+ mainll_p = mainll_p->prev;
+
+ /* if there is none, or there is no override, use the parameter */
+ if (mainll_p == 0 || mainll_p->u.feed_p->leftmargin < 0.0)
+ return (Score.leftmargin / Score.scale_factor);
+
+ /* use this override value */
+ return (mainll_p->u.feed_p->leftmargin / Score.scale_factor);
+}
+\f
+/*
+ * Name: findprimes()
+ *
+ * Abstract: Find all the prime numbers up to the given number ("max").
+ *
+ * Returns: array indexed 0 to max, each element YES or NO (is index prime?)
+ *
+ * Description: This function mallocs and returns an array of shorts, indexed
+ * from 0 to max. Each element is YES or NO, telling whether its
+ * index is a prime number. The first time it is called, or if
+ * max is greater than the previous time, it calculates all this,
+ * but on other calls it just returns the answer from before.
+ */
+
+short *
+findprimes(max)
+
+int max; /* max integer we need to consider */
+
+{
+ static short *isprime = 0; /* array to be malloc'ed */
+ static int oldmax = 0; /* the max passed in previously */
+ int stop; /* where to stop looking */
+ int prime; /* a prime number */
+ int n; /* loop index */
+
+
+ /* if we've already been here ... */
+ if (isprime != 0) {
+ /* if we've already done the same or more, just return answer*/
+ if (max <= oldmax) {
+ return (isprime);
+ }
+ /* max increased; free the old array */
+ FREE(isprime);
+ }
+ oldmax = max; /* remember if for next time */
+
+ MALLOCA(short, isprime, max + 1);
+
+ /* 0 and 1 are not primes */
+ isprime[0] = isprime[1] = NO;
+
+ /*
+ * We're going to use the Sieve of Eristosthenes. We start out by
+ * assuming everything 2 and greater is prime.
+ */
+ for (n = 2; n <= max; n++) {
+ isprime[n] = YES;
+ }
+
+ /* the following loop can stop when it gets to this point */
+ stop = sqrt((double)max) + 1;
+
+ prime = 2;
+ while (prime <= stop) {
+ /* knock out all multiples of this prime number */
+ for (n = 2 * prime; n <= max; n += prime) {
+ isprime[n] = NO;
+ }
+ /* find the next prime */
+ for (n = prime + 1; n <= stop && isprime[n] == NO; n++)
+ ;
+ prime = n;
+ }
+
+ return (isprime);
+}
+\f
+/*
+ * Name: factor()
+ *
+ * Abstract: Factor the given number.
+ *
+ * Returns: array indexed 0 to num, giving the prime factors
+ *
+ * Description: This function mallocs and returns an array of shorts, indexed
+ * from 0 to max. Each element indexed by a prime number tells
+ * how many times that prime factor occurs in num. All other
+ * elements are 0. The first time it is called, or if num is
+ * different from the previous time, it calculates all this,
+ * but on other calls it just returns the answer from before.
+ */
+
+short *
+factor(num)
+
+int num; /* the integer to be factored */
+
+{
+ static short *factors = 0; /* array to be malloc'ed */
+ static int oldnum = 0; /* the number passed in previously */
+ short *isprime; /* list of which numbers are prime */
+ int orignum; /* remember original num */
+ int n; /* loop index */
+
+
+ /* if we've just done the same number, just return the answer */
+ if (factors != 0) {
+ /* if we've already done the same or more, just return answer*/
+ if (num == oldnum) {
+ return (factors);
+ }
+ /* num changed; free the old array */
+ FREE(factors);
+ }
+ oldnum = num; /* remember it for next time */
+
+ CALLOCA(short, factors, num + 1);
+
+ /* find which numbers up to num are primes */
+ isprime = findprimes(num);
+
+ /*
+ * For every prime number until "num" is used up, divide it into num
+ * as many times as possible, keeping track of how many times.
+ */
+ orignum = num;
+ for (n = 2; n <= orignum && num > 1; n++) {
+ if (isprime[n] == YES) {
+ while (num % n == 0) {
+ num /= n;
+ factors[n]++;
+ }
+ }
+ }
+
+ return (factors);
+}
+\f
+
+/* Return the width of the widest note head in the given GRPSYL. */
+
+double
+widest_head(gs_p)
+
+struct GRPSYL *gs_p;
+
+{
+ double widest; /* widest note head in the group */
+ double thiswidth; /* width of current note */
+ int n; /* note index */
+
+ widest = 0.0;
+ for (n = 0; n < gs_p->nnotes; n++) {
+ thiswidth = width(gs_p->notelist[n].headfont,
+ (gs_p->notelist[n].notesize == GS_NORMAL ?
+ DFLT_SIZE : SMALLSIZE),
+ gs_p->notelist[n].headchar);
+ if (thiswidth > widest) {
+ widest = thiswidth;
+ }
+ }
+ return(widest);
+}