--- /dev/null
+/* Copyright (c) 1995, 1996, 1997, 1998, 2000, 2001, 2002, 2003, 2004, 2005 by Arkkra Enterprises */
+/* All rights reserved */
+/*
+ * Name: abshorz.c
+ *
+ * Description: This file contains functions for setting absolute
+ * horizontal coordinates.
+ */
+
+#include <string.h>
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+static void barclefsigs P((void));
+static int barwithssv P((struct MAINLL *mainll_p));
+static void setclefsigwid P((struct MAINLL *mainll_p, struct CHHEAD *chhead_p));
+static void abschunk P((struct MAINLL *mainll_p, struct MAINLL *end_p));
+static void tryabs P((struct MAINLL *mainll_p, double scale, int *scores_p,
+ short measinscore[]));
+static int endchunk P((struct MAINLL *mainll_p));
+static double adjust_measwid4mrpt P((double oldmeaswid, struct CHORD *ch_p));
+static void fillclefsig P((struct CLEFSIG *clefsig_p, struct MAINLL *feed_p));
+static struct MAINLL *trymeasure P((struct MAINLL *mainll_p, double scale,
+ float *measwidth_p, float *adjust_p, int *ressv_p));
+static void setabs P((struct MAINLL *start_p, int scores, short measinscore[]));
+static void chkrestart P((struct MAINLL *start_p, struct MAINLL *end_p));
+static void setabsscore P((struct MAINLL *start_p, struct MAINLL *end_p));
+static void setabschord P((struct CHORD *ch_p, double nomx));
+static double effwidth P((struct CHORD *ch_p));
+static double bardiff P((struct MAINLL *mainll_p, struct MAINLL *end_p));
+static void fixfullmeas P((struct CHORD *ch_p, double x));
+static void restore_grpsyl_west P((void));
+static void setipw P((void));
+static void setipwgrpsyl P((struct MAINLL *mainll_p, struct GRPSYL *gs_p));
+static void setipwchord P((struct MAINLL *mainll_p));
+static void fixendings P((void));
+static void fixreh P((void));
+static void clrinhprint P((void));
+static int hidestaffs P((struct MAINLL *mainll_p, struct MAINLL *ml2_p));
+static int silent P((struct MAINLL *mainll_p, struct MAINLL *ml2_p, int s,
+ int *ressv_p));
+static int getmultinum P((struct MAINLL *mll_p));
+
+/*
+ * Depending on which type of clefsig, get its full width, or its effective
+ * width. The latter is for user requested ones, which may overlap notes.
+ */
+#define EFF_WIDCLEFSIG(clefsig_p) \
+ ((clefsig_p)->clefsize == DFLT_SIZE ? width_clefsig(clefsig_p) : \
+ (clefsig_p)->effwidth)
+
+/*
+ * Define the padding after a clefsig. It's greater when there's a pseudobar,
+ * because we need to allow room for carry-in ties/slurs.
+ */
+#define CSP(clefsig_p) (((clefsig_p)->bar_p == 0 ? 2.0 : 9.0) * STDPAD)
+\f
+/*
+ * Name: abshorz()
+ *
+ * Abstract: Set absolute horizontal coordinates of everything.
+ *
+ * Returns: void
+ *
+ * Description: This function inserts the initial FEED of the piece. Then, for
+ * each section of the piece delimited by FEEDs (initial one and
+ * user-requested ones), it breaks it into the necessary number
+ * of scores (inserting more FEEDs), and sets all the horizontal
+ * absolute coordinates. Finally, it does some fix-up work.
+ */
+
+void
+abshorz()
+
+{
+ struct MAINLL *mainll_p; /* point at any MAINLL member */
+ struct MAINLL *ml2_p; /* point at another MAINLL member */
+ struct MAINLL *mainfeed_p; /* point at MAINLL containing a FEED */
+ struct MAINLL *end_p; /* point to end of a chunk of MAINLL */
+ int gotbar; /* found a bar in this chunk */
+
+
+ debug(16, "abshorz");
+ ml2_p = 0; /* prevent useless 'used before set' warning */
+
+ /*
+ * The parse phase put any user-requested score feeds in the main
+ * linked list. We must now insert a FEED before the first measure,
+ * unless the user already requested one there (unlikely!). Later
+ * we'll add more as needed.
+ */
+ for (mainll_p = Mainllhc_p;
+ mainll_p != 0 && mainll_p->str == S_SSV;
+ mainll_p = mainll_p->next)
+ ; /* skip by all initial SSVs */
+
+ if (mainll_p == 0)
+ pfatal("main linked list has nothing but SSVs");
+
+ if (mainll_p->str != S_FEED) {
+ mainfeed_p = newMAINLLstruct(S_FEED, 0);
+ insertMAINLL(mainfeed_p, mainll_p->prev);
+ }
+
+ /* whenever a CLEFSIG is needed after a bar line, put one there */
+ barclefsigs();
+
+ /*
+ * Find each section of the main linked list, delimited by FEEDs.
+ * For each such section, call abschunk() to set the absolute
+ * horizontal coords for things in that chunk. At the end of each
+ * chunk, back up to avoid including SSVs/PRHEADs/LINEs/CURVEs which
+ * might follow the last measure of the chunk. If a chunk contains no
+ * bar lines (like if there was a useless scorefeed at the end),
+ * don't call abschunk().
+ */
+ /* skip anything before first FEED first */
+ for (mainll_p = Mainllhc_p; mainll_p->str != S_FEED;
+ mainll_p = mainll_p->next)
+ ;
+ for (;;) {
+ gotbar = NO;
+ for (end_p = mainll_p->next;
+ end_p != 0 && end_p->str != S_FEED;
+ ml2_p = end_p, end_p = end_p->next) {
+
+ if (end_p->str == S_BAR)
+ gotbar = YES;
+ }
+
+ if (gotbar == NO) {
+ /*
+ * If end_p is 0, this must be the end of the MLL, and
+ * there was a final feed after all the music data.
+ * There is no need to process this chunk.
+ */
+ if (end_p == 0)
+ break;
+ /*
+ * This chunk must contain a BLOCKHEAD. There is no
+ * need to process it. Set the absolute horizontal
+ * coords. Then point mainll_p at the FEED at the end
+ * of the chunk, and go to the next loop.
+ */
+ mainll_p->u.feed_p->c[AW] = eff_leftmargin(mainll_p);
+ mainll_p->u.feed_p->c[AE] = PGWIDTH -
+ eff_rightmargin(end_p);
+ mainll_p->u.feed_p->c[AX] = (mainll_p->u.feed_p->c[AW]
+ + mainll_p->u.feed_p->c[AE]) / 2.0;
+ mainll_p = end_p;
+ continue;
+ }
+
+ while (ml2_p->str == S_SSV || ml2_p->str == S_PRHEAD ||
+ ml2_p->str == S_LINE || ml2_p->str == S_CURVE)
+ ml2_p = ml2_p->prev;
+ abschunk(mainll_p, ml2_p->next);
+ if (end_p == 0)
+ break;
+ mainll_p = end_p;
+ }
+
+ /* restore west boundaries of GRPSYLs that have associated clefs */
+ restore_grpsyl_west();
+
+ /* set inches per whole ( c[INCHPERWHOLE] ) in the relevant structs */
+ setipw();
+
+ /* move endings that start at end of score to the next score */
+ fixendings();
+
+ /* move rehearsal marks at end of a score to the next score */
+ fixreh();
+
+ /* clear the inhibitprint flag on tablature staffs when appropriate */
+ clrinhprint();
+}
+\f
+/*
+ * Name: barclefsigs()
+ *
+ * Abstract: Put a CLEFSIG after each bar line that requires one.
+ *
+ * Returns: void
+ *
+ * Description: This function loops through the main linked list, applying
+ * SSVs as it goes. Whenever an SSV changes clef, key, or time,
+ * it inserts a CLEFSIG into the list to show what will need to
+ * be printed at that point. It also inserts one after any bar
+ * that is a restart.
+ */
+
+static void
+barclefsigs()
+
+{
+ struct MAINLL *mainll_p; /* point at items in main linked list*/
+ struct MAINLL *maincs_p; /* point at MAINLL with CLEFSIG */
+ struct MAINLL *mll_p; /* point along MLL */
+ struct CLEFSIG *clefsig_p; /* point at CLEFSIG being filled in */
+ struct BAR *bar_p; /* point at the bar */
+ struct CHHEAD *chhead_p; /* point at the latest CHHEAD seen */
+ struct TIMEDSSV *tssv_p; /* point along timed SSV list */
+ struct GRPSYL *gs_p; /* point at a group */
+ int oldstaffs; /* no. of staffs before SSVs */
+ int oldvis[MAXSTAFFS + 1]; /* visibility of staffs before SSVs */
+ int oldclef[MAXSTAFFS + 1]; /* clefs before SSVs */
+ int newclef[MAXSTAFFS + 1]; /* clefs after SSVs */
+ int premidclef[MAXSTAFFS + 1]; /* clefs before applying midmeas SSVs*/
+ int oldkey[MAXSTAFFS + 1]; /* effective keys before SSVs */
+ int newkey[MAXSTAFFS + 1]; /* effective keys after SSVs */
+ int oldnorm[MAXSTAFFS + 1]; /* is staff "normal" (5 line, nontab)?*/
+ int newnorm[MAXSTAFFS + 1]; /* is staff "normal" (5 line, nontab)?*/
+ char oldtimerep[MAXTSLEN]; /* time sig before SSVs */
+ int oldclefsequal, oldkeysequal;/* were they equal on all staffs? */
+ int newclefsequal, newkeysequal;/* are they equal on all staffs? */
+ int gottimedssv; /* were there any timed SSVs? */
+ int vidx; /* voice index */
+ int lastclef; /* last clef printed */
+ RATIONAL offset; /* offset of a group in a measure */
+ RATIONAL lastclefoffset; /* offset of last midmeasure clef */
+ int timechg; /* did they change time sig info? */
+ int change; /* did they change clefs/keys/time? */
+ register int s; /* staff number */
+
+
+ debug(16, "barclefsigs");
+ initstructs(); /* clean out old SSV info */
+
+ /* apply any SSVs that come before the first measure */
+ mainll_p = Mainllhc_p;
+ while (mainll_p != 0 && mainll_p->str == S_SSV) {
+ asgnssv(mainll_p->u.ssv_p);
+ mainll_p = mainll_p->next;
+ }
+
+ chhead_p = 0; /* keep lint happy; will be set before used */
+
+ /*
+ * Loop once for each bar line in the piece that has SSV(s) after it,
+ * or is a RESTART. Whenever this occurs, insert a CLEFSIG after them
+ * if required. RESTART always requires it.
+ */
+ for (;;) {
+ /*
+ * Find the next bar that is either followed by an SSV or has
+ * timed SSVs for the preceding measure or is a restart. These
+ * are the cases where a CLEFSIG may be needed. If we hit the
+ * end of the MLL, break out.
+ */
+ while (mainll_p != 0 && ! (mainll_p->str == S_BAR &&
+ (barwithssv(mainll_p) == YES ||
+ mainll_p->u.bar_p->timedssv_p != 0 ||
+ mainll_p->u.bar_p->bartype == RESTART))) {
+ switch (mainll_p->str) {
+ case S_CHHEAD:
+ /* remember the last chhead */
+ chhead_p = mainll_p->u.chhead_p;
+ break;
+ case S_SSV:
+ /* apply SSVs */
+ asgnssv(mainll_p->u.ssv_p);
+ break;
+ }
+ mainll_p = mainll_p->next;
+ }
+ if (mainll_p == 0) {
+ break;
+ }
+ bar_p = mainll_p->u.bar_p;
+
+ /*
+ * If there were timed SSVs in the measure just ended, we need
+ * to make sure that any clef changes requested actually got
+ * printed before some GRPSYL. If the clef was changed on one
+ * staff by <<score clef = whatever>>, some other staff may be
+ * affected and yet not have a GRPSYL after that point before
+ * which the clef can be printed. In that case, we want to
+ * generate a CLEFSIG at this bar line, to print it.
+ */
+ tssv_p = bar_p->timedssv_p;
+ gottimedssv = tssv_p != 0; /* remember if we had any */
+ if (gottimedssv) {
+ /* get clef state before the timed SSVs */
+ for (s = 1; s <= Score.staffs; s++) {
+ premidclef[s] = svpath(s, CLEF)->clef;
+ }
+ /* assign the timed SSVs */
+ for ( ; tssv_p != 0; tssv_p = tssv_p->next) {
+ asgnssv(&tssv_p->ssv);
+ }
+ }
+
+ /*
+ * Save the current number of staffs, whether they are visible,
+ * and all clefs and effective keys in case the SSVs coming
+ * up will change some of these things. Also, save the timesig
+ * info so we can check if it changed (it is settable only in
+ * the score).
+ * Set oldnorm according to whether the staff is "normal",
+ * capable of having a clef or key sig.
+ * Set oldclefsequal and oldkeysequal according to whether all
+ * staffs have the same values for clef and key.
+ */
+ oldclefsequal = oldkeysequal = YES;
+ for (s = 1; s <= Score.staffs; s++) {
+ oldvis[s] = svpath(s, VISIBLE)->visible;
+ oldclef[s] = svpath(s, CLEF)->clef;
+ oldkey[s] = eff_key(s);
+ oldnorm[s] = svpath(s, STAFFLINES)->stafflines == 5 &&
+ svpath(s, STAFFLINES)->printclef == YES &&
+ ! is_tab_staff(s) ? YES : NO;
+ if (s > 1 && oldclef[s - 1] != oldclef[s])
+ oldclefsequal = NO;
+ if (s > 1 && oldkey[s - 1] != oldkey[s])
+ oldkeysequal = NO;
+ }
+ oldstaffs = Score.staffs;
+ strcpy(oldtimerep, Score.timerep);
+
+ /* see if clefs need printing due to timed SSVs */
+ if (gottimedssv) {
+ /* check this on every staff */
+ for (s = 1; s <= Score.staffs; s++) {
+ /* find this staff's MLL structure */
+ for (mll_p = mainll_p; mll_p->str != S_STAFF ||
+ mll_p->u.staff_p->staffno != s;
+ mll_p = mll_p->prev) {
+ ;
+ }
+ /* don't force clefsig for an invisible staff*/
+ if (mll_p->u.staff_p->visible == NO) {
+ continue;
+ }
+ /*
+ * Find last clef that was printed for this
+ * measure. Start with the value of the clef
+ * at the previous bar (before any midmeasure
+ * clefs). Then update with the ones that were
+ * printed midmeasure, until we have the last.
+ */
+ lastclef = premidclef[s];
+ lastclefoffset = rneg(One);
+ for (vidx = 0; vidx < MAXVOICES; vidx++) {
+ /* look down this voice */
+ offset = Zero;
+ for (gs_p = mll_p->u.staff_p->groups_p[
+ vidx]; gs_p != 0; gs_p = gs_p->next) {
+ if (gs_p->clef != NOCLEF &&
+ GT(offset, lastclefoffset)) {
+ lastclef = gs_p->clef;
+ lastclefoffset = offset;
+ }
+ offset = radd(offset,
+ gs_p->fulltime);
+ }
+ }
+ /*
+ * Set the oldclef to the last one printed.
+ * Then later code will create a CLEFSIG, if
+ * necessary.
+ */
+ oldclef[s] = lastclef;
+ }
+ }
+
+ /*
+ * Loop through this set of SSV(s), applying them. If we hit
+ * the end of the main linked list, break out. We don't want
+ * to put a CLEFSIG after the last bar line, regardless of
+ * whether it changed anything.
+ */
+ mainll_p = mainll_p->next;
+ while (mainll_p != 0 && mainll_p->str == S_SSV) {
+ asgnssv(mainll_p->u.ssv_p);
+ mainll_p = mainll_p->next;
+ }
+ /* Retain mainll_p for later; loop onwards using mll_p. Need */
+ /* to keep looking for SSVs in case there is a block here. */
+ mll_p = mainll_p;
+ while (mll_p != 0 && (mll_p->str == S_SSV ||
+ mll_p->str == S_FEED ||
+ mll_p->str == S_BLOCKHEAD)) {
+ if (mll_p->str == S_SSV) {
+ asgnssv(mll_p->u.ssv_p);
+ }
+ mll_p = mll_p->next;
+ }
+ if (mainll_p == 0)
+ break;
+
+
+ /*
+ * Get the new clefs and effective keys.
+ * Again, find out if the clefs and keys are equal on all
+ * staffs.
+ */
+ newclefsequal = newkeysequal = YES;
+ for (s = 1; s <= Score.staffs; s++) {
+ newclef[s] = svpath(s, CLEF)->clef;
+ newkey[s] = eff_key(s);
+ newnorm[s] = svpath(s, STAFFLINES)->stafflines == 5 &&
+ svpath(s, STAFFLINES)->printclef == YES &&
+ ! is_tab_staff(s) ? YES : NO;
+ if (s > 1 && newclef[s - 1] != newclef[s])
+ newclefsequal = NO;
+ if (s > 1 && newkey[s - 1] != newkey[s])
+ newkeysequal = NO;
+ }
+
+ /* first check if any time sig info changed */
+ if (strcmp(Score.timerep, oldtimerep) != 0)
+ timechg = YES;
+ else
+ timechg = NO;
+
+ /*
+ * If the bar was a restart, we treat it as if it were at the
+ * start of a score. That is, we always print the clefs and
+ * key signatures. The clefs are full size, and no naturals
+ * are printed. Print the time signature only if it changed.
+ */
+ if (bar_p->bartype == RESTART) {
+ /*
+ * We always require a CLEFSIG. Allocate one and put
+ * it between where we are now and the preceding
+ * structure.
+ */
+ maincs_p = newMAINLLstruct(S_CLEFSIG, 0);
+ insertMAINLL(maincs_p, mainll_p->prev);
+ clefsig_p = maincs_p->u.clefsig_p;
+
+ clefsig_p->clefsize = DFLT_SIZE; /* full size */
+ clefsig_p->multinum = getmultinum(maincs_p);
+
+ /*
+ * Note: If the number of staffs is changing here, the
+ * following might not be right. But it doesn't
+ * matter, because in that case this CLEFSIG will be
+ * thrown away later anyway.
+ */
+ for (s = 1; s <= oldstaffs; s++) {
+ /* draw nothing if this staff is not "normal" */
+ if (oldnorm[s] == NO || newnorm[s] == NO)
+ continue;
+ clefsig_p->prclef[s] = YES;
+ clefsig_p->sharps[s] = newkey[s];
+ }
+
+ /* print the time signature if it changed */
+ clefsig_p->prtimesig = timechg;
+
+ continue;
+ }
+
+ /*
+ * When the number of staffs changes, special rules apply.
+ * Handle this situation and continue.
+ */
+ if (oldstaffs != Score.staffs) {
+ /*
+ * Identify the cases where no clefsig is needed, and
+ * continue. This is when time didn't change, and no
+ * clefs or keys are to be printed. Clefs are to be
+ * printed only when all the old staffs had the same
+ * clef, all the new ones had the same clef, and the
+ * old and new clefs are different. The analogous rule
+ * holds for keys. This is because, when the number of
+ * staffs changes, we can't really tell which old staff
+ * corresponds to which new staff (if any), so it's
+ * silly to print a clef or key change on any.
+ */
+ if (timechg == NO &&
+ (oldclefsequal == NO ||
+ newclefsequal == NO ||
+ oldclef[1] == newclef[1]) &&
+ (oldkeysequal == NO ||
+ newkeysequal == NO ||
+ oldkey[1] == newkey[1])) {
+ /* no CLEFSIG needed here */
+ continue;
+ }
+
+ /*
+ * Something changed that requires a CLEFSIG. Allocate
+ * one and put it between where we are now and the
+ * preceding structure, the last SSV we applied.
+ */
+ maincs_p = newMAINLLstruct(S_CLEFSIG, 0);
+ insertMAINLL(maincs_p, mainll_p->prev);
+ clefsig_p = maincs_p->u.clefsig_p;
+
+ /* any clefs to be printed should be small size */
+ clefsig_p->clefsize = SMALLSIZE;
+ clefsig_p->multinum = getmultinum(maincs_p);
+
+ /*
+ * Since the number of staffs is changing, there will
+ * be a scorefeed here, and this CLEFSIG will be at the
+ * end of the first score. Every old staff will have
+ * the same info printed by the CLEFSIG, except that
+ * clef and key never exist on "abnormal" staffs. Check
+ * against staff 1 since we know that has to exist on
+ * both sides. It doesn't hurt to mark even invisible
+ * ones.
+ */
+ for (s = 1; s <= oldstaffs; s++) {
+
+ /* draw nothing if this staff is not "normal" */
+ if (oldnorm[s] == NO || newnorm[s] == NO)
+ continue;
+ /*
+ * Draw the new clef if the clefs on each score
+ * are consistent and they changed.
+ */
+ if (oldclefsequal && newclefsequal &&
+ oldclef[1] != newclef[1])
+ clefsig_p->prclef[s] = YES;
+
+ /*
+ * Draw the new key if the keys on each score
+ * are consistent and they changed. See below
+ * for a more detailed explanation.
+ */
+ if (oldkeysequal && newkeysequal &&
+ oldkey[1] != newkey[1]) {
+ clefsig_p->sharps[s] = newkey[1];
+
+ if (newkey[1] == 0) {
+ clefsig_p->naturals[s] =
+ oldkey[1];
+ } else if (svpath(s, CANCELKEY)->
+ cancelkey == YES) {
+ if (oldkey[1] * newkey[1] < 0) {
+ /* 1 has #s, 1 has &s */
+ clefsig_p->naturals[s] =
+ oldkey[1];
+ } else if (abs(oldkey[1]) >
+ abs(newkey[1])){
+ /* new has fewer accs*/
+ clefsig_p->naturals[s] =
+ oldkey[1] - newkey[1];
+ }
+ }
+ }
+ }
+
+ /* print the time signature if it changed */
+ if (timechg == YES)
+ clefsig_p->prtimesig = YES;
+
+ /* set clefsig's effective width */
+ setclefsigwid(maincs_p, chhead_p);
+
+ continue;
+ }
+
+
+ change = timechg;
+
+ /* see if anything else requiring a CLEFSIG changed */
+ for (s = 1; s <= oldstaffs; s++) {
+ if (oldvis[s] == NO ||
+ svpath(s, VISIBLE)->visible == NO)
+ continue;
+ if (oldclef[s] != newclef[s])
+ change = YES;
+ if (oldkey[s] != newkey[s])
+ change = YES;
+ if (change == YES)
+ break; /* don't waste any more time looping */
+ }
+ if (change == NO)
+ continue; /* no visible time, key, clef changed*/
+
+ /*
+ * If we get here, it means either the clef, effective key, or
+ * time changed on some visible staff. Allocate a CLEFSIG and
+ * put it between where we are now and the preceding structure,
+ * which is the last SSV we applied.
+ */
+ maincs_p = newMAINLLstruct(S_CLEFSIG, 0);
+ insertMAINLL(maincs_p, mainll_p->prev);
+ clefsig_p = maincs_p->u.clefsig_p;
+
+ /* any clefs to be printed should be small size */
+ clefsig_p->clefsize = SMALLSIZE;
+ clefsig_p->multinum = getmultinum(maincs_p);
+
+ /*
+ * Loop through the staffs, marking in the CLEFSIG what should
+ * be drawn.
+ */
+ for (s = 1; s <= Score.staffs; s++) {
+ /* draw nothing if this staff is invisible */
+ if (oldvis[s] == NO)
+ continue;
+
+ /* draw nothing if this staff is not "normal" */
+ if (oldnorm[s] == NO || newnorm[s] == NO)
+ continue;
+
+ /* draw the new clef if the clef changed */
+ if (oldclef[s] != newclef[s])
+ clefsig_p->prclef[s] = YES;
+
+ /*
+ * If the effective key changed, draw the new key
+ * signature. But if the new key has 0 sharps, we
+ * should draw naturals in the shape of the old key
+ * signature. And if cancelkey is set, and a sharp key
+ * is changing to a flat key or vice versa, or the
+ * number of sharps or flats is being reduced, we need
+ * enough naturals to cancel the ones being removed.
+ */
+ if (oldkey[s] != newkey[s]) {
+ clefsig_p->sharps[s] = newkey[s];
+ if (newkey[s] == 0) {
+ clefsig_p->naturals[s] = oldkey[s];
+ } else if (svpath(s, CANCELKEY)->
+ cancelkey == YES) {
+ if (oldkey[s] * newkey[s] < 0) {
+ /* 1 has sharps, 1 has flats */
+ clefsig_p->naturals[s] =
+ oldkey[s];
+ } else if (abs(oldkey[s]) >
+ abs(newkey[s])) {
+ /* new has fewer accidentals */
+ clefsig_p->naturals[s] =
+ oldkey[s] - newkey[s];
+ }
+ }
+ }
+ }
+
+ /*
+ * Finally, print the time signature if it changed.
+ */
+ if (timechg == YES)
+ clefsig_p->prtimesig = YES;
+
+ /* set clefsig's effective width, and widestclef */
+ setclefsigwid(maincs_p, chhead_p);
+ }
+}
+\f
+/*
+ * Name: barwithssv()
+ *
+ * Abstract: Is this bar followed by SSV(s), ignoring FEEDs and BLOCKHEADs?
+ *
+ * Returns: YES or NO
+ *
+ * Description: This function is called with the MLL structure for a BAR.
+ * Ignoring the possible presence of FEEDs and BLOCKHEADs, return
+ * YES if the next structure is an SSV.
+ */
+
+static int
+barwithssv(mainll_p)
+
+struct MAINLL *mainll_p; /* for the BAR */
+
+{
+ struct MAINLL *mll_p; /* loop after the BAR */
+
+
+ for (mll_p = mainll_p->next; mll_p != 0; mll_p = mll_p->next) {
+ switch (mll_p->str) {
+ case S_SSV:
+ return (YES);
+ case S_FEED:
+ case S_BLOCKHEAD:
+ break; /* ignore these, keep looking */
+ default:
+ return (NO);
+ }
+ }
+
+ return (NO); /* hit end of MLL */
+}
+\f
+/*
+ * Name: setclefsigwid()
+ *
+ * Abstract: Set effective width and widest clef of a user requested clefsig.
+ *
+ * Returns: void
+ *
+ * Description: This function is called with a user-requested clefsig. If it
+ * contains clefs, they should be printed before the bar line. If
+ * we are lucky, part or all of the clef's widths can overlap notes
+ * on other staffs. The effective width is the full width minus
+ * the amount that can overlap. This function sets the effective
+ * width of the clefsig, and the widest clef that it contains. We
+ * need to do this now, because we need to use the pseudo absolute
+ * coords set by relxchord() before they are overwritten later in
+ * abshorz.c.
+ */
+
+static void
+setclefsigwid(mainll_p, chhead_p)
+
+struct MAINLL *mainll_p; /* point at the given clefsig's MLL struct */
+struct CHHEAD *chhead_p; /* point at the preceding chhead */
+
+{
+ struct MAINLL *m2_p; /* another pointer down the MLL */
+ struct CLEFSIG *clefsig_p; /* point at a clefsig */
+ struct CHORD *ch_p; /* point at a chord */
+ struct STAFF *staff_p; /* point at a staff */
+ struct GRPSYL *gs_p; /* point at a group in a voice */
+ struct GRPSYL *gs2_p; /* point at a group in a chord */
+ float lasteast; /* phony AE of last chord in measure */
+ float size; /* to be used for clef */
+ float clefspace; /* space needed for printing clefs */
+ float clefwid; /* width of a clef */
+ float widestclef; /* width of the widest to be printed */
+ float staffscale; /* scale for a staff */
+ float gap; /* between last group & last chord */
+ int staffno; /* staff number, 1 to MAXSTAFFS */
+ int clef; /* clef type */
+ int v; /* voice number, 0 to 2 */
+
+
+ debug(16, "setclefsigwid");
+
+ clefsig_p = mainll_p->u.clefsig_p; /* convenient pointer */
+
+ /*
+ * Although relxchord() overlaps chords in various ways, it does not
+ * overlap the last chord in the measure with anything following.
+ * And it sets phony absolute coordinates for each chord, based on
+ * pretending that everything is packed as tight as possible. So, as
+ * the rightmost coord of all groups, we can use the AE of the last
+ * chord of the measure just ended.
+ */
+ for (ch_p = chhead_p->ch_p; ch_p->ch_p != 0; ch_p = ch_p->ch_p)
+ ;
+ lasteast = ch_p->c[AE];
+
+ /*
+ * Init the amount of space needed for clefs to be printed. We start
+ * at zero, and whenever a clef is to be printed, we find out how much
+ * of it can't be overlapped; and clefspace keeps track of the maximum
+ * of these for all staffs.
+ */
+ clefspace = 0.0;
+
+ widestclef = 0.0; /* max width of any clef to be printed */
+
+ size = 3.0/4.0 * DFLT_SIZE; /* small size clefs */
+
+ /*
+ * Loop backwards through the preceding measure, looking for visible
+ * staffs to process.
+ */
+ for (m2_p = mainll_p; m2_p->str != S_CHHEAD; m2_p = m2_p->prev) {
+
+ if (m2_p->str != S_STAFF || m2_p->u.staff_p->visible == NO)
+ continue;
+
+ staff_p = m2_p->u.staff_p;
+ staffno = staff_p->staffno;
+
+ /* if no clef, it doesn't need any space */
+ if (clefsig_p->prclef[staffno] == NO)
+ continue;
+
+ /* find width of this clef, including padding */
+ clef = svpath(staffno, CLEF)->clef;
+ staffscale = svpath(staffno, STAFFSCALE)->staffscale;
+ clefwid = (clefwidth(clef, YES) + CLEFPAD) * staffscale;
+
+ /* remember biggest clef width */
+ if (clefwid > widestclef)
+ widestclef = clefwid;
+
+ /* loop through all voices on this staff */
+ for (v = 0; v < MAXVOICES; v++) {
+
+ /* find last group in this voice */
+ gs_p = staff_p->groups_p[v];
+ if (gs_p == 0)
+ continue;
+ for ( ; gs_p->next != 0; gs_p = gs_p->next)
+ ;
+
+ /*
+ * Find out what chord this group belongs to, by
+ * searching down the GRPSYL list hanging off each
+ * chord in this measure.
+ */
+ for (ch_p = chhead_p->ch_p; ch_p != 0;
+ ch_p = ch_p->ch_p) {
+ for (gs2_p = ch_p->gs_p; gs2_p != 0;
+ gs2_p = gs2_p->gs_p) {
+ /* if found, or after the right staff*/
+ if (gs2_p == gs_p || gs2_p->staffno >
+ gs_p->staffno)
+ break;
+ }
+
+ /*
+ * If we found it, find the gap between this
+ * group's AE and the last chord's. If the
+ * amount of the clef's width that does not fit
+ * in that gap is the greatest so far, save it.
+ */
+ if (gs2_p == gs_p) {
+ gap = lasteast -
+ (ch_p->c[AX] + gs_p->c[RE]);
+
+ if (clefwid - gap > clefspace)
+ clefspace = clefwid - gap;
+
+ break; /* look no more */
+ }
+ }
+ } /* loop through voices on a staff */
+ } /* loop through staffs */
+
+ clefsig_p->widestclef = widestclef;
+
+ /* (effective width) = (real width) - (what can overlap) */
+ clefsig_p->effwidth = width_clefsig(clefsig_p) -
+ (widestclef - clefspace);
+}
+\f
+/*
+ * Name: abschunk()
+ *
+ * Abstract: Set the absolute horz. coords of everything in one chunk.
+ *
+ * Returns: void
+ *
+ * Description: This function is given a chunk of the piece, which is
+ * delimited by FEEDs. It estimates how many inches should
+ * be allocated to each whole note of time. Then it calls
+ * tryabs() repeatedly, trying to find a scale factor that
+ * will avoid having the last score be too empty. Finally,
+ * it calls setabs() to set the absolute horizontal coordinates
+ * of everything in the chunk.
+ */
+
+static void
+abschunk(start_p, end_p)
+
+struct MAINLL *start_p; /* FEED at start of chunk of MAINLL */
+struct MAINLL *end_p; /* points after last struct in chunk (or 0) */
+
+{
+ float totwidth; /* total minimal width of this chunk */
+ float totpseudodur; /* total pseudodur of measures in this chunk */
+ struct MAINLL *mainll_p;/* point at items in main linked list*/
+ struct CHORD *ch_p; /* point at a chord */
+ short *measinscore; /* malloc'ed array; bars in each score */
+ float packfact; /* as it was at the start of this score */
+ float lowscale; /* small guess at inches per whole */
+ float highscale; /* large guess at inches per whole */
+ float midscale; /* average of low and high scale */
+ float measwidth; /* width of a measure */
+ int numbars; /* number of measures in this chunk */
+ int scores; /* number of scores needed for this chunk */
+ int reqscores; /* the number of score required */
+ int trial; /* trial number for getting correct scale */
+
+
+ debug(16, "abschunk file=%s line=%d", start_p->inputfile,
+ start_p->inputlineno);
+
+ /*
+ * For our first estimate of how wide to make everything, we need to
+ * add up the total minimal width and total elapsed time.
+ */
+ /* must apply all SSVs from start, to get the right time sig */
+ initstructs(); /* clean out old SSV info */
+ for (mainll_p = Mainllhc_p; mainll_p != start_p;
+ mainll_p = mainll_p->next) {
+ if (mainll_p->str == S_SSV)
+ asgnssv(mainll_p->u.ssv_p);
+ }
+
+ packfact = Score.packfact; /* use current value (start of score) */
+ totwidth = 0; /* start totals at 0 */
+ totpseudodur = 0;
+ numbars = 0;
+
+ /* loop through chunk, adding up width and time */
+ for ( ; mainll_p != end_p; mainll_p = mainll_p->next) {
+
+ switch (mainll_p->str) {
+ case S_SSV:
+ /* assign to keep time sig accurate */
+ asgnssv(mainll_p->u.ssv_p);
+ break;
+
+ case S_CHHEAD:
+ /*
+ * Add in the minimum widths of all chords in the
+ * measure, and add up the pseudoduration. There are
+ * special things done for mrpt: 1) if all staff(s)
+ * have mrpt, we don't want them to "deserve" as much
+ * space as an mr, so reduce their pseudodur; 2) the
+ * minimum width of the measure must be made wide
+ * enough to contain the symbol. Also, allow room
+ * for any midmeasure clefs. (This will happen
+ * automatically, because the group boundaries still
+ * include their preceding midmeasure clefs at this
+ * point.)
+ */
+ measwidth = 0;
+ ch_p = mainll_p->u.chhead_p->ch_p;
+ if (ABSDIFF(ch_p->width, TEMPMRPTWIDTH) < 0.001) {
+ /* the 0.001 is to allow for roundoff error */
+ ch_p->pseudodur *= 0.5;
+ }
+ for ( ; ch_p != 0; ch_p = ch_p->ch_p) {
+ measwidth += ch_p->width;
+ /* only count time if there is a real width */
+ /* (nonspace, or "us", or padded "s") */
+ if (ch_p->width != 0)
+ totpseudodur += ch_p->pseudodur;
+ }
+ /* add this measure into the total */
+ totwidth += adjust_measwid4mrpt(measwidth,
+ mainll_p->u.chhead_p->ch_p);
+ break;
+
+ case S_CLEFSIG:
+ /* width of clef/key/time to print when changing */
+ totwidth += EFF_WIDCLEFSIG(mainll_p->u.clefsig_p) +
+ CSP(mainll_p->u.clefsig_p);
+ break;
+
+ case S_BAR:
+ /* add width of bar line, and incr no. of bars */
+ totwidth += width_barline(mainll_p->u.bar_p);
+ numbars++;
+ break;
+ }
+ }
+
+ /*
+ * Allocate the measinscore array. We need an element for each score
+ * that this chunk will be broken up into. We don't yet know how
+ * many that will be. So allocate enough for the worst case, where
+ * each measure is so wide that it has to go on a separate score.
+ */
+ MALLOCA(short, measinscore, numbars + 1);
+
+ /*
+ * Our first trial is to allow "packfact" times the minimal
+ * width we have just added up, partly to allow for the stuff at the
+ * start of each score (more CLEFSIGs to be inserted after we know
+ * where the FEEDs are going to be), and partly so things can spread
+ * out nicely.
+ */
+ lowscale = packfact * totwidth / totpseudodur;
+ tryabs(start_p, lowscale, &scores, measinscore);
+
+ /*
+ * If the whole chunk fits on one score, just set the absolute coords
+ * for this score and get out.
+ */
+ if (scores == 1) {
+ setabs(start_p, scores, measinscore);
+ FREE(measinscore);
+ return;
+ }
+
+ /*
+ * However many scores tryabs() says were needed, that is what we will
+ * require. But it's likely that the last score is far from filled up.
+ * It would look bad to just spread out the stuff in the last score.
+ * We want to "balance the load".
+ *
+ * So make up a new scale (highscale) which would probably force us to
+ * use an additional score. Then loop, binary searching, to find a
+ * value for scale almost as big as possible without forcing a new
+ * score.
+ */
+ reqscores = scores;
+ highscale = 3.0 * lowscale;
+ for (trial = 0; trial < 12; trial++) {
+ midscale = (lowscale + highscale) / 2;
+ tryabs(start_p, midscale, &scores, measinscore);
+ if (scores > reqscores) {
+ highscale = midscale;
+ } else { /* must be equal, can never be less */
+ lowscale = midscale;
+ }
+ }
+ /*
+ * If the last one we tried is not a good one, we have to run tryabs
+ * again to reset the scores array properly.
+ */
+ if (midscale != lowscale) {
+ tryabs(start_p, lowscale, &scores, measinscore);
+ }
+
+ setabs(start_p, scores, measinscore);
+
+ FREE(measinscore);
+}
+\f
+/*
+ * Name: tryabs()
+ *
+ * Abstract: Given trial scale, find how many scores are needed, etc.
+ *
+ * Returns: void
+ *
+ * Description: This function, given a proposed scale factor for a chunk
+ * delimited by FEEDs, figures out how many measures would
+ * fit on each score.
+ */
+
+static void
+tryabs(start_p, scale, scores_p, measinscore)
+
+struct MAINLL *start_p; /* FEED at start of chunk of MAINLL */
+double scale; /* inches per "whole" unit of time */
+int *scores_p; /* return number of scores needed */
+short measinscore[]; /* return number of measures in each score */
+
+{
+ struct MAINLL *mainll_p;/* points along main linked list */
+ struct MAINLL *new_p; /* points at first struct in new measure */
+ struct MAINLL *ml2_p; /* another general pointer into MAINLL */
+ struct MAINLL *prevfeed_p;/* where previous FEED is or would go */
+ float remwidth; /* width remaining on this score */
+ float userdelta; /* (user right margin) - (normal right margin)*/
+ int atend; /* are we at the end of the chunk? */
+ struct CLEFSIG clefsig; /* temporary CLEFSIG for start of each score */
+ struct BAR bar; /* temp BAR; may be need by the above CLEFSIG*/
+ float measwidth; /* width needed by one measure */
+ float adjust; /* bar line adjust if last measure in score */
+ int ressv; /* do we have to re-initstructs() and re-SSV?*/
+
+
+ debug(32, "tryabs file=%s line=%d scale=%f", start_p->inputfile,
+ start_p->inputlineno, (float)scale);
+ /* must apply all SSVs from start, to get the right clef/key/time; */
+ setssvstate(start_p);
+
+ mainll_p = start_p;
+
+ /*
+ * Set up for beginning of first score in this chunk.
+ * Find out how much width is available, allowing for
+ * margins and stuff to the left (labels, etc.).
+ */
+ *scores_p = 0; /* no scores yet */
+ /* left margin may have user override; for now, assume right doesn't */
+ remwidth = PGWIDTH - eff_rightmargin((struct MAINLL *)0)
+ - eff_leftmargin(start_p);
+ remwidth -= width_left_of_score(start_p);
+ measinscore[0] = 0;
+
+ /*
+ * If the user overrode the right margin at the end of this chunk, we
+ * need to know the difference between what they requested and what the
+ * normal value is. If they didn't, userdelta will be zero.
+ */
+ userdelta = eff_rightmargin(start_p->next) -
+ eff_rightmargin((struct MAINLL *)0);
+
+ prevfeed_p = start_p; /* init previous FEED to the start of chunk */
+
+ /*
+ * We need to set up a provisional CLEFSIG containing what would need
+ * to be printed at the start of this new score. We can't put it in
+ * the real MAINLL yet, since this function is just trying a
+ * possibility, and cannot alter MAINLL for real. Set a pointer to
+ * a bar, which in real life would be allocated by fillclefsig.
+ * Subtract the clefsig's width from what we have left to work with.
+ */
+ (void)memset((char *)&clefsig, 0, sizeof(clefsig));
+ (void)memset((char *)&bar, 0, sizeof(bar));
+ clefsig.bar_p = &bar;
+ fillclefsig(&clefsig, mainll_p);
+ remwidth -= width_clefsig(&clefsig) + CSP(&clefsig);
+
+ /* loop through chunk, once per measure, finding where FEEDs would go*/
+ for (;;) {
+ /* get width of this measure, and where next one starts */
+ new_p = trymeasure(mainll_p, scale, &measwidth, &adjust,&ressv);
+
+ atend = endchunk(new_p); /* last measure of chunk? */
+
+ if (!atend && (measwidth - adjust) <= remwidth ||
+ atend && (measwidth - adjust) <= remwidth - userdelta ||
+ measinscore[*scores_p] == 0) {
+ /*
+ * There is room for this measure on this score, or
+ * there are no measures here yet so we better force
+ * this one onto there. We'll fail later if this
+ * in fact cannot fit.
+ * Subtract its width from what's left on this score,
+ * and increment the number of measures on it. Point
+ * at the next measure.
+ */
+ remwidth -= measwidth;
+ measinscore[*scores_p]++;
+ mainll_p = new_p;
+
+ /* if we are at the end, inc no. of scores & return */
+ if (atend) {
+ (*scores_p)++;
+ return;
+ }
+ } else {
+ /*
+ * There is not room for this measure on this score.
+ * Increment the number of scores needed.
+ */
+ (*scores_p)++;
+
+ /*
+ * If this last measure ended with SSV(s) after the
+ * bar line that would cause a CLEFSIG, we need to
+ * undo the change so that the new score will start
+ * with the old info. Sadly, we'll have to init
+ * the SSVs and apply them over from the beginning.
+ */
+ if (ressv) {
+ setssvstate(mainll_p);
+ }
+
+ /*
+ * Find out how much width is available, allowing for
+ * margins and stuff to the left (labels, etc.).
+ * For now, assume this is not the last score, so no
+ * user margin override.
+ */
+ remwidth = PGWIDTH - eff_rightmargin((struct MAINLL *)0)
+ - eff_leftmargin((struct MAINLL *)0);
+ remwidth -= pwidth_left_of_score(mainll_p, prevfeed_p);
+
+ prevfeed_p = mainll_p; /* where feed would go */
+
+ /*
+ * We need to set up a provisional CLEFSIG containing
+ * what would need to be printed at the start of this
+ * new score. We can't put it in the real MAINLL yet,
+ * since this function is just trying a possibility,
+ * and cannot alter MAINLL for real. In case a repeat
+ * start is going to be needed, have a bar pointer
+ * ready. Subtract the clefsig's width from what we
+ * have left to work with.
+ */
+ (void)memset((char *)&clefsig, 0, sizeof(clefsig));
+ (void)memset((char *)&bar, 0, sizeof(bar));
+ clefsig.bar_p = &bar;
+ fillclefsig(&clefsig, mainll_p);
+ remwidth -= width_clefsig(&clefsig) + CSP(&clefsig);
+ measinscore[*scores_p] = 0; /* no bars here yet */
+
+ /*
+ * If the measure we just figured is too wide for the
+ * new score we are about to begin, it must be that
+ * we are just padding things too much. (If there
+ * really is too much stuff in the measure, we'll fail
+ * later.) So assume we'll cram in it anyway, set up
+ * 0 width remaining, and prepare for next measure.
+ * We have to reapply the SSVs we removed above, since
+ * we won't be calling trymeasure() again for that
+ * measure.
+ *
+ * If the measure fits, don't do any of this. Just let
+ * trymeasure figure the same one over again, next
+ * time around.
+ */
+ if (!atend && measwidth > remwidth ||
+ atend && measwidth > remwidth - userdelta) {
+ if (ressv) {
+ for (ml2_p = mainll_p; ml2_p != new_p;
+ ml2_p = ml2_p->next) {
+ if (ml2_p->str == S_SSV)
+ asgnssv(ml2_p->u.ssv_p);
+ }
+ }
+ remwidth = 0;
+ measinscore[*scores_p] = 1;
+ mainll_p = new_p;
+
+ /* if at the end, fix no. of scores & ret */
+ if (atend) {
+ (*scores_p)++;
+ return;
+ }
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: endchunk()
+ *
+ * Abstract: Is this MLL item near the end of a chunk?
+ *
+ * Returns: YES or NO
+ *
+ * Description: This function, given a main linked list structure, finds out
+ * whether there is nothing left in this chunk of the MLL other
+ * than possibly SSVs/PRHEADs/LINEs/CURVEs. The very end of a
+ * chunk is determined by the end of the MLL, or the FEED that
+ * begins the next chunk.
+ */
+
+static int
+endchunk(mainll_p)
+
+struct MAINLL *mainll_p; /* points into the MAINLL */
+
+{
+ /* loop past any SSVs or PRHEADs */
+ while (mainll_p != 0 && (mainll_p->str == S_SSV ||
+ mainll_p->str == S_PRHEAD ||
+ mainll_p->str == S_LINE ||
+ mainll_p->str == S_CURVE))
+ mainll_p = mainll_p->next;
+
+ /* if we hit the end or a FEED, we found the end of this chunk */
+ if (mainll_p == 0 || mainll_p->str == S_FEED)
+ return (YES);
+ return (NO);
+}
+\f
+/*
+ * Name: adjust_measwid4mrpt()
+ *
+ * Abstract: If the measure contains a mrpt, adjust the measure's width.
+ *
+ * Returns: the new (possibly increased) measure width
+ *
+ * Description: This function, given the supposed width of a measure and the
+ * first chord of the measure, looks to see if there is an mrpt
+ * in the measure. If so, it enlarges the chord(s) in the measure
+ * if necessary, to make sure there's enough room for the symbol.
+ */
+
+static double
+adjust_measwid4mrpt(oldmeaswid, ch_p)
+
+double oldmeaswid; /* old measure width */
+struct CHORD *ch_p; /* points at a chord, should be first chord in meas */
+
+{
+ int gotmrpt; /* is there an mrpt? */
+ struct GRPSYL *gs_p; /* point down list of GRPSYLs in chord */
+ float newmeaswid; /* possible new measure width */
+ float thismrpt; /* space needed by one mrpt and its padding */
+ float increase; /* how much bigger must we make the measure? */
+
+
+ /*
+ * Scan down the first chord and see if any groups have an mrpt.
+ */
+ gotmrpt = NO;
+ newmeaswid = 0.0;
+ for (gs_p = ch_p->gs_p; gs_p != 0; gs_p = gs_p->gs_p) {
+ if (is_mrpt(gs_p)) {
+ gotmrpt = YES;
+ /* get width of this mrpt + its padding, if any */
+ thismrpt = width(FONT_MUSIC, DFLT_SIZE, C_MEASRPT) *
+ svpath(gs_p->staffno, STAFFSCALE)->staffscale +
+ gs_p->padding;
+ if (thismrpt > newmeaswid) {
+ newmeaswid = thismrpt;
+ }
+ }
+ }
+
+ if (gotmrpt == NO)
+ return (oldmeaswid); /* no mrpt, return original width */
+
+ /* if measure is already wider than all mrpts, return unchanged */
+ if (oldmeaswid >= newmeaswid)
+ return (oldmeaswid);
+
+ /*
+ * Some staff(s) have mrpts, and the existing chord(s) add up to
+ * narrower than the width of an mrpt. It's really rare that this
+ * could happen if there is only one chord. So we will handle it by
+ * forcing the first chord to be wider, rather than allocating the
+ * extra amongst all the chords.
+ */
+ if (ABSDIFF(ch_p->width, TEMPMRPTWIDTH) < 0.001) {
+ /* first chord is all mrpt, so no other chords exist; */
+ /* the 0.001 is to allow for roundoff error */
+ ch_p->c[RE] = newmeaswid / 2.0;
+ ch_p->c[RW] = -newmeaswid / 2.0;
+ ch_p->width = newmeaswid;
+ } else {
+ /* add extra to the right side of the first chord */
+ increase = newmeaswid - oldmeaswid;
+ ch_p->c[RE] += increase;
+ ch_p->width += increase;
+ }
+
+ return (newmeaswid);
+}
+\f
+/*
+ * Name: fillclefsig()
+ *
+ * Abstract: Fill the CLEFSIG for after a FEED.
+ *
+ * Returns: void
+ *
+ * Description: This function, given an empty CLEFSIG structure and a pointer
+ * to a FEED structure in the MAINLL, fills the CLEFSIG according
+ * to what should be printed. If called from tryabs() (bar_p !=
+ * 0), the bar will be set to REPEATSTART if need be, based on the
+ * type of the preceding bar. If called from setabs() (bar_p ==
+ * 0), the same will be done, if need be, after allocating a BAR
+ * and setting the pointer to it. In one bizarre case, abschunk()
+ * calls here directly, and this is treated the same as tryabs();
+ * bar_p != 0.
+ */
+
+static void
+fillclefsig(clefsig_p, feed_p)
+
+struct CLEFSIG *clefsig_p; /* points at empty clefsig to be filled in */
+struct MAINLL *feed_p; /* points at a FEED in the MAINLL */
+
+{
+ struct MAINLL *mainll_p;/* points along the MAINLL */
+ struct BAR *realbar_p; /* point at bar before this feed */
+ float barpad; /* padding on that bar */
+ int s; /* staff number */
+
+
+ /*
+ * On every visible staff, the clef and key signature are to
+ * be printed.
+ */
+ for (s = 1; s <= Score.staffs; s++) {
+ if (svpath(s, VISIBLE)->visible == NO)
+ continue; /* invisible */
+ clefsig_p->prclef[s] = YES;
+ clefsig_p->sharps[s] = eff_key(s);
+ }
+
+ /* clefs to be printed should be regular size */
+ clefsig_p->clefsize = DFLT_SIZE;
+
+ /*
+ * The time signature is to be printed on the first score, or if
+ * it just changed. Search back to see if there was a CLEFSIG just
+ * before this FEED where the time changed, or if this is the first
+ * measure.
+ */
+ for (mainll_p = feed_p->prev; mainll_p != 0 &&
+ mainll_p->str != S_BAR && mainll_p->str != S_CLEFSIG;
+ mainll_p = mainll_p->prev)
+ ;
+
+ /* see chkrestart() for explanation of the bar_p->c[AY] part of this */
+ if (mainll_p == 0 ||
+ mainll_p->str == S_CLEFSIG &&
+ mainll_p->u.clefsig_p->prtimesig == YES ||
+ mainll_p->str == S_BAR &&
+ mainll_p->u.bar_p->c[AY] > 0.0)
+ clefsig_p->prtimesig = YES;
+
+ /*
+ * If the preceding BAR (if any) was a REPEATSTART or REPEATBOTH, it
+ * has to be "split up". Search back to find this bar.
+ */
+ for (mainll_p = feed_p->prev; mainll_p != 0 && mainll_p->str != S_BAR;
+ mainll_p = mainll_p->prev)
+ ;
+
+ if (clefsig_p->bar_p != 0) {
+ /*
+ * tryabs() called us. If there is a preceding bar, and it is
+ * REPEATSTART or REPEATBOTH, it would have to be "split", and
+ * our pseudo bar must be made a REPEATSTART. Otherwise, the
+ * the pseudo bar should be INVISBAR since nothing really
+ * should be printed there. Since tryabs() called us, we can't
+ * tamper with the preceding (real) bar. That's okay; the
+ * change would just make that bar slightly narrower, so
+ * things will still fit on that score.
+ */
+ if (mainll_p != 0 &&
+ (mainll_p->u.bar_p->bartype == REPEATSTART ||
+ mainll_p->u.bar_p->bartype == REPEATBOTH)) {
+ clefsig_p->bar_p->bartype = REPEATSTART;
+ } else {
+ clefsig_p->bar_p->bartype = INVISBAR;
+ }
+ } else {
+ /*
+ * setabs() called us, so we must allocate a pseudo bar and
+ * point the clefsig at it. The same splitting rules apply as
+ * for tryabs(), except since we're now doing it for real, we
+ * have to really alter the preceding bar's bar type in those
+ * cases. This preceding bar's AW and AX must also be
+ * adjusted, since the bar is now going to be narrower than it
+ * was before.
+ */
+ CALLOC(BAR, clefsig_p->bar_p, 1);
+
+ if (mainll_p != 0 &&
+ (mainll_p->u.bar_p->bartype == REPEATSTART ||
+ mainll_p->u.bar_p->bartype == REPEATBOTH)) {
+
+ realbar_p = mainll_p->u.bar_p;
+ realbar_p->bartype = realbar_p->bartype == REPEATSTART ?
+ realbar_p->precbartype : REPEATEND;
+ realbar_p->c[AW] = realbar_p->c[AE] -
+ width_barline(realbar_p);
+ /* to get AX, temporarily set padding to 0; find the */
+ /* width of the resulting nonpadded bar, and subtract*/
+ /* half of that from AE; then restore padding */
+ barpad = realbar_p->padding;
+ realbar_p->padding = 0;
+ realbar_p->c[AX] = realbar_p->c[AE] -
+ width_barline(realbar_p) / 2;
+ realbar_p->padding = barpad;
+ clefsig_p->bar_p->bartype = REPEATSTART;
+ } else {
+ clefsig_p->bar_p->bartype = INVISBAR;
+ }
+ }
+}
+\f
+/*
+ * Name: trymeasure()
+ *
+ * Abstract: Find trial width of a measure.
+ *
+ * Returns: Pointer to the first MAINLL structure of the next measure,
+ * or 0 if we hit the end of MAINLL.
+ *
+ * Description: This function, given a pointer to the first MAINLL structure
+ * in a measure (or the FEED preceding), finds and fills in the
+ * width of the measure.
+ */
+
+static struct MAINLL *
+trymeasure(mainll_p, scale, measwidth_p, adjust_p, ressv_p)
+
+struct MAINLL *mainll_p; /* points first thing in meas, or FEED */
+double scale; /* inches per "whole" unit of time */
+float *measwidth_p; /* width of measure to be filled in */
+float *adjust_p; /* bar line adjust, to be filled in; if last
+ * meas in score, bar shouldn't be padded on
+ * right, so subtract this for measwidth */
+int *ressv_p; /* did we apply a CLEFSIG-causing SSV? */
+
+{
+ struct CHORD *ch_p; /* point at a chord */
+ struct TIMEDSSV *tssv_p;/* point along timed SSV list */
+ float idealwidth; /* the width a chord should be, based on time*/
+
+
+ if (mainll_p == 0)
+ pfatal("invalid mainll_p argument (0) to trymeasure");
+
+ *ressv_p = NO; /* assume no SSVs for now */
+
+ /* every measure has one CHHEAD; find it */
+ while (mainll_p != 0 && mainll_p->str != S_CHHEAD)
+ mainll_p = mainll_p->next;
+ if (mainll_p == 0)
+ pfatal("missing CHHEAD near end of main linked list");
+
+ *measwidth_p = 0;
+
+ /*
+ * For each chord, find out how much width it "deserves",
+ * based on its pseudodur. But if it requires more space
+ * than that, give it what it needs. Accumulate in *measwidth_p.
+ */
+ for (ch_p = mainll_p->u.chhead_p->ch_p; ch_p != 0;
+ ch_p = ch_p->ch_p) {
+
+ idealwidth = scale * ch_p->pseudodur;
+ /* but a chord of all compressible, nonpadded spaces */
+ /* deserves no width */
+ if (ch_p->width == 0)
+ idealwidth = 0;
+ *measwidth_p += MAX(idealwidth, ch_p->width);
+ }
+
+ /*
+ * Find the bar line and add in its width.
+ */
+ while (mainll_p->str != S_BAR)
+ mainll_p = mainll_p->next;
+ *measwidth_p += width_barline(mainll_p->u.bar_p);
+
+ /* apply any timed SSVs */
+ for (tssv_p = mainll_p->u.bar_p->timedssv_p; tssv_p != 0;
+ tssv_p = tssv_p->next) {
+ asgnssv(&tssv_p->ssv);
+ *ressv_p = YES; /* remember we've assigned SSVs */
+ }
+
+ /* need this in case this will be the last measure in the score */
+ *adjust_p = eos_bar_adjust(mainll_p->u.bar_p);
+
+ mainll_p = mainll_p->next; /* point after bar line */
+ /* if end of MAINLL or next measure is starting up, return now */
+ if (mainll_p == 0 || (mainll_p->str != S_SSV &&
+ mainll_p->str != S_CLEFSIG))
+ return (mainll_p);
+
+ /* apply any SSVs at this point */
+ while (mainll_p != 0 && mainll_p->str == S_SSV) {
+ asgnssv(mainll_p->u.ssv_p);
+ mainll_p = mainll_p->next;
+ *ressv_p = YES; /* remember we've assigned SSVs */
+ }
+
+ if (mainll_p != 0 && mainll_p->str == S_CLEFSIG) {
+ *measwidth_p += EFF_WIDCLEFSIG(mainll_p->u.clefsig_p) +
+ CSP(mainll_p->u.clefsig_p);
+ mainll_p = mainll_p->next;
+ }
+
+ return (mainll_p);
+}
+\f
+/*
+ * Name: setabs()
+ *
+ * Abstract: Sets horizontal absolute coordinates for one chunk.
+ *
+ * Returns: void
+ *
+ * Description: This function, given a chunk of the piece delimited by FEEDs,
+ * and the number of measures to be put on each score, loops
+ * through the scores, inserting FEEDs between them, and calling
+ * setabsscore() to set the horizontal absolute coordinates.
+ */
+
+static void
+setabs(start_p, scores, measinscore)
+
+struct MAINLL *start_p; /* FEED at start of chunk of MAINLL */
+int scores; /* number of scores this chunk needs */
+short measinscore[]; /* number of measures in each score */
+
+{
+ struct MAINLL *mainll_p;/* points along main linked list */
+ struct MAINLL *ml2_p; /* another general pointer into MAINLL */
+ int score; /* score number, 0 to scores-1 */
+ int n; /* loop counter */
+
+
+ debug(16, "setabs file=%s line=%d scores=%d", start_p->inputfile,
+ start_p->inputlineno, scores);
+ /* must apply all SSVs from start, to get the right clef/key/time; */
+ setssvstate(start_p);
+
+ /* point at first item in first measure of chunk (skip initial FEED) */
+ mainll_p = start_p->next;
+
+ for (score = 0; score < scores; score++) {
+ /* the first score already has a FEED; insert if later score */
+ if (score != 0) {
+ ml2_p = newMAINLLstruct(S_FEED, 0);
+ insertMAINLL(ml2_p, mainll_p->prev);
+ }
+ mainll_p = mainll_p->prev; /* point at the FEED */
+
+ /*
+ * Insert CLEFSIG following the FEED, and fill as needed.
+ * fillclefsig() will also allocate a BAR, and point the
+ * clefsig at it. If the previous bar line was a REPEATSTART
+ * or REPEATBOTH, it will set REPEATSTART in the new pseudo
+ * BAR, and alter the preceding bar as necessary.
+ */
+ ml2_p = newMAINLLstruct(S_CLEFSIG, 0);
+ insertMAINLL(ml2_p, mainll_p);
+ fillclefsig(ml2_p->u.clefsig_p, mainll_p);
+ ml2_p->u.clefsig_p->multinum = getmultinum(ml2_p);
+
+ /*
+ * Find end of score by searching forward the correct number
+ * of measures. Each measure begins with a CHHEAD, and a block
+ * begins with a BLOCKHEAD, so stop at either of these. Call
+ * a subroutine to process this score.
+ */
+ ml2_p = ml2_p->next; /* point at CHHEAD */
+ for (n = 0; n < measinscore[score]; n++) {
+ do {
+ ml2_p = ml2_p->next;
+ } while (ml2_p != 0 && ml2_p->str != S_CHHEAD &&
+ ml2_p->str != S_BLOCKHEAD);
+ }
+ chkrestart(mainll_p, ml2_p);
+ if (hidestaffs(mainll_p, ml2_p) == YES) {
+ /* if we had to force any staffs invisible, we have to
+ * reapply SSVs so that the new ones we inserted take
+ * effect */
+ setssvstate(start_p);
+ }
+ setabsscore(mainll_p, ml2_p);
+ mainll_p = ml2_p;
+ }
+}
+\f
+/*
+ * Name: chkrestart()
+ *
+ * Abstract: Check for restart bars and remove if necessary.
+ *
+ * Returns: void
+ *
+ * Description: This function, given one score's worth of input, checks for
+ * restart bars. These are used for making a gap in the score.
+ * So when they are the first or last bar in a score, they don't
+ * make sense, and should be removed. Well, not simply removed;
+ * various things need to be done to the main linked list.
+ */
+
+static void
+chkrestart(start_p, end_p)
+
+struct MAINLL *start_p; /* point at the initial FEED of this score */
+struct MAINLL *end_p; /* point after the last thing on this score */
+
+{
+ struct MAINLL *mainll_p;/* points along main linked list */
+ struct MAINLL *m2_p; /* another pointer along main linked list */
+ int s; /* staff number */
+
+
+ /* find first bar on this score; there has to be at least one */
+ for (mainll_p = start_p; mainll_p->str != S_BAR;
+ mainll_p = mainll_p->next)
+ ;
+
+ if (mainll_p->u.bar_p->bartype == RESTART) {
+ /*
+ * The first bar on the score is a restart. So the score would
+ * start with whitespace followed by the restart bar, which
+ * would look bad. So make the restart into an invisbar (which
+ * eliminates the whitespace). A little negative padding is
+ * needed to make things line up. Clean out the beginning of
+ * score clefsig so that nothing prints there. The former
+ * restart's clefsig will print at the start of the line as if
+ * it were the beginning of line clefsig.
+ */
+ mainll_p->u.bar_p->bartype = INVISBAR;
+ mainll_p->u.bar_p->padding = -0.12;
+
+ start_p->next->u.clefsig_p->prtimesig = NO;
+ for (s = 1; s <= MAXSTAFFS; s++) {
+ start_p->next->u.clefsig_p->prclef[s] = NO;
+ start_p->next->u.clefsig_p->sharps[s] = 0;
+ /* no need to zap the naturals, already 0 */
+ }
+ }
+
+ /* find the last bar on this score */
+ m2_p = 0; /* keep lint happy */
+ for ( ; mainll_p != end_p; mainll_p = mainll_p->next) {
+ if (mainll_p->str == S_BAR)
+ m2_p = mainll_p;
+ }
+
+ if (m2_p->u.bar_p->bartype == RESTART) {
+ /*
+ * The last bar on the score is a restart. So the score would
+ * end with whitespace followed by a clefsig, which would look
+ * bad. So make the restart into an invisbar (which eliminates
+ * the whitespace) and remove the clefsig from the MLL, since
+ * we don't want to show those things at a restart. The next
+ * score will now be like a restart.
+ */
+ m2_p->u.bar_p->bartype = INVISBAR;
+ m2_p->u.bar_p->padding = 0;
+
+ mainll_p = m2_p; /* remember bar */
+
+ /* find the clefsig; defensive check for end of MLL */
+ for ( ; m2_p != 0 && m2_p->str != S_CLEFSIG; m2_p = m2_p->next)
+ ;
+ if (m2_p == 0)
+ pfatal("the last bar in the piece is a restart");
+
+ /*
+ * When it comes time to create the coming feed and clefsig,
+ * that clefsig's value for prtimesig depends on the one in the
+ * clefsig we are about to remove. So as a special kluge, if
+ * that value is YES, set the bar's AY to a positive number.
+ * It will get overwritten in absvert.c.
+ */
+ if (m2_p->u.clefsig_p->prtimesig == YES) {
+ mainll_p->u.bar_p->c[AY] = 1.0;
+ }
+
+ m2_p->prev->next = m2_p->next;
+ m2_p->next->prev = m2_p->prev;
+ FREE(m2_p->u.clefsig_p);
+ FREE(m2_p);
+ }
+}
+\f
+/*
+ * Name: setabsscore()
+ *
+ * Abstract: Sets horizontal absolute coordinates for one score.
+ *
+ * Returns: void
+ *
+ * Description: This function, given one score's worth of input, decides how
+ * to space everything horizontally to look pleasing, and then
+ * sets the horizontal absolute coordinates.
+ */
+
+static void
+setabsscore(start_p, end_p)
+
+struct MAINLL *start_p; /* point at the initial FEED of this score */
+struct MAINLL *end_p; /* point after the last thing on this score */
+
+{
+ struct MAINLL *mainll_p;/* points along main linked list */
+ struct MAINLL *m2_p; /* another pointer along main linked list */
+ struct CHORD *ch_p; /* point at a chord */
+ struct CHORD *firstch_p;/* point at first chord in a measure */
+ struct BAR *bar_p; /* convenient pointer at a clefsig's bar */
+ struct MAINLL *mm_p; /* another pointer along MLL */
+ struct MAINLL *lastbarmll_p; /* remember the last bar in the score */
+ struct CLEFSIG *clefsig_p; /* point at a clefsig */
+ struct TIMEDSSV *tssv_p;/* point along a timed SSV list */
+ float prevbarae; /* remember previous bar's AE */
+ float wid; /* temp variable, width of something */
+ float eff_right; /* effective right margin */
+ float scorewidth; /* total width allowed for the score */
+ float totwidth; /* total minimum width */
+ float totwhole; /* total equivalent whole notes of time */
+ float chw; /* total minimum width of chords */
+ float notespace; /* space for chords */
+ float nnotespace; /* space for expandable chords */
+ float ntotwhole; /* total equiv wholes for expandables */
+ float inchpwhole; /* inches each whole note should have */
+ float expanded; /* width of something after expansion */
+ float absx; /* absolute x coordinate */
+ float leftx, rightx; /* start and end positions of a measure */
+ float eff; /* effective width */
+ int toowide; /* number of chords wider than they deserve */
+ int ntoowide; /* new no. of chords wider than deserved */
+
+
+ debug(32, "setabsscore file=%s line=%d",
+ start_p->inputfile, start_p->inputlineno);
+ firstch_p = 0; /* keep lint happy; will be set before used */
+ prevbarae = 0.0; /* keep lint happy; will be set before used */
+ lastbarmll_p = 0; /* keep lint happy; will be set before used */
+
+ /*
+ * Get total available width on this score.
+ */
+ if (end_p == 0) {
+ /* find last feed or last bar, whichever comes last */
+ for (m2_p = Mainlltc_p; m2_p->str != S_FEED &&
+ m2_p->str != S_BAR; m2_p = m2_p->prev)
+ ;
+ if (m2_p->str == S_FEED) {
+ /* a feed after the last bar; use it */
+ eff_right = eff_rightmargin(m2_p);
+ } else {
+ /* no feed after the last bar */
+ eff_right = eff_rightmargin((struct MAINLL *)0);
+ }
+ } else {
+ /*
+ * end_p must be the chhead of the following measure. Its prev
+ * may be a user FEED. (The CLEFSIG which should be between
+ * them has not been inserted yet.)
+ */
+ if (end_p->prev->str == S_FEED) {
+ /* it is a feed, so use it */
+ eff_right = eff_rightmargin(end_p->prev);
+ } else {
+ /* no feed */
+ eff_right = eff_rightmargin((struct MAINLL *)0);
+ }
+ }
+
+ scorewidth = PGWIDTH - eff_right - eff_leftmargin(start_p);
+ scorewidth -= width_left_of_score(start_p);
+
+ /*
+ * Accumulate the total minimum width, total pseudodur in equivalent
+ * wholes, and the total minimum width needed by chords.
+ */
+ totwidth = 0;
+ totwhole = 0;
+ chw = 0;
+ for (mainll_p = start_p; mainll_p != end_p; mainll_p = mainll_p->next){
+ switch (mainll_p->str) {
+
+ case S_SSV:
+ /* assign to keep time sig accurate */
+ asgnssv(mainll_p->u.ssv_p);
+ break;
+
+ case S_CHHEAD:
+ /*
+ * Add in min widths & time of all chords in measure.
+ * Skip any leading space chords in determining first.
+ * (Actually, only compressible, nonpadded spaces.)
+ * The west part of the first chord is considered part
+ * a fixed width. The "effwidth" of a chord is its
+ * east part plus the west part of the next chord, if
+ * any.
+ */
+ for (ch_p = mainll_p->u.chhead_p->ch_p; ch_p != 0 &&
+ ch_p->width == 0; ch_p = ch_p->ch_p)
+ ;
+ if (ch_p == 0)
+ break;
+ totwidth -= ch_p->c[RW]; /* first nonspace */
+ for ( ; ch_p != 0; ch_p = ch_p->ch_p) {
+ totwidth += effwidth(ch_p);
+ chw += effwidth(ch_p);
+
+ /* count time only if not a chord of */
+ /* compressible, nonpadded spaces */
+ if (ch_p->width != 0)
+ totwhole += ch_p->pseudodur;
+ }
+ break;
+
+ case S_CLEFSIG:
+ /*
+ * If this clefsig is the last thing on this score
+ * (except possibly the FEED that starts the next chunk)
+ * find the preceding bar line. If that bar has
+ * hidechanges set, it means that we are not to print
+ * this clefsig.
+ */
+ if (mainll_p->next == end_p ||
+ mainll_p->next->str == S_FEED) {
+ for (m2_p = mainll_p; m2_p->str != S_BAR;
+ m2_p = m2_p->prev)
+ ;
+ if (m2_p->u.bar_p->hidechanges) {
+ mainll_p->u.clefsig_p->hide = YES;
+ mainll_p->u.clefsig_p->effwidth = 0.0;
+ }
+ }
+
+ /* width of clef/key/time/repeatstart when needed */
+ totwidth += EFF_WIDCLEFSIG(mainll_p->u.clefsig_p);
+ /* pad clefsig, unless it's the last thing on score */
+ if (mainll_p->next != end_p &&
+ mainll_p->next->str != S_FEED)
+ totwidth += CSP(mainll_p->u.clefsig_p);
+ break;
+
+ case S_BAR:
+ /* bar's width */
+ totwidth += width_barline(mainll_p->u.bar_p) +
+ mainll_p->u.bar_p->padding;
+ /* apply any timed SSVs */
+ for (tssv_p = mainll_p->u.bar_p->timedssv_p;
+ tssv_p != 0; tssv_p = tssv_p->next) {
+ asgnssv(&tssv_p->ssv);
+ }
+ lastbarmll_p = mainll_p;
+ break;
+ }
+ }
+
+ /*
+ * If the last bar is truly at the end of the line, it doesn't need its
+ * full width, because there is no padding after it. But when there is
+ * a visible clefsig with time or sig there, the bar is not at the end.
+ */
+ for (mm_p = lastbarmll_p; mm_p != 0; mm_p = mm_p->next) {
+ if (mm_p->str == S_STAFF || mm_p->str == S_CLEFSIG) {
+ break;
+ }
+ }
+ if (mm_p == 0 || mm_p->str != S_CLEFSIG ||
+ mm_p->u.clefsig_p->hide == YES) {
+ /* no visible clefsig; get rid of padding */
+ totwidth -= eos_bar_adjust(lastbarmll_p->u.bar_p);
+ } else {
+ /* If there is a clefsig, but it has hidechanges,
+ * or it has no time sigature or any key signatures,
+ * it needs to be moved to the edge of the score. */
+ if (mm_p->u.clefsig_p->prtimesig == NO) {
+ int s;
+ for (s = 1; s <= MAXSTAFFS; s++) {
+ if ((mm_p->u.clefsig_p->sharps[s] != 0 ||
+ mm_p->u.clefsig_p->naturals[s] != 0) &&
+ svpath(s, VISIBLE)->visible == YES) {
+ break;
+ }
+ }
+ if (s > MAXSTAFFS) {
+ totwidth -= eos_bar_adjust(
+ lastbarmll_p->u.bar_p);
+ }
+ }
+ }
+
+ /* fail if even the minimum size for everything doesn't fit */
+ if (totwidth > scorewidth) {
+ if (Score.units == INCHES) {
+ l_ufatal(start_p->inputfile, start_p->inputlineno,
+ "too much (%f inches) to put in score",
+ totwidth * Score.scale_factor);
+ } else {
+ l_ufatal(start_p->inputfile, start_p->inputlineno,
+ "too much (%f cm) to put in score",
+ totwidth * Score.scale_factor *
+ CMPERINCH);
+ }
+ }
+
+ /*
+ * Only chords are allowed to expand when there is extra space;
+ * other items have a fixed width. To find how much space is
+ * available for chords, take the total screen width minus the
+ * space needed by the fixed-size things.
+ */
+ notespace = scorewidth - (totwidth - chw);
+
+ /*
+ * Some chords' "effwidths" are already wider than what they deserve
+ * based on their pseudodur. Let them keep that minimum size. We
+ * will consider their size as fixed and allocate the remaining
+ * space among chords that deserve more. Remove the too-wide (and
+ * just right) chords from the totals. This has to be done
+ * repeatedly, since after each iteration the number of inches
+ * deserved by each remaining chord shrinks. Leave the loop when
+ * it is found that all remaining chords deserve to expand.
+ */
+ ntotwhole = totwhole; /* initially assume all may be expandable */
+ nnotespace = notespace;
+ ntoowide = 0;
+ do {
+ /*
+ * If there are no notes in this score, totwhole will already
+ * be 0 on the first loop iteration, and there is nothing that
+ * can expand. Each measure will be very small, just the width
+ * of the bar line and its padding, and the rightmost bar line
+ * won't be at the right edge of the score. This is usually a
+ * useless situation; but if invisbars are used, and "newscore"
+ * every measure, it provides a way to print blank music paper.
+ *
+ * inchpwhole won't ever get used, but we set it to something
+ * arbitrary in case lint cares. Then break out of this loop.
+ */
+ if (totwhole == 0.0) {
+ inchpwhole = 1.0;
+ break;
+ }
+ /*
+ * Find how much space each whole note worth of chords
+ * deserves, allocating proportionally. Consider just the
+ * ones not known to be too big already.
+ */
+ inchpwhole = nnotespace / ntotwhole;
+
+ /* start with all chords' time and space */
+ ntotwhole = totwhole;
+ nnotespace = notespace;
+
+ toowide = ntoowide; /* remember how many last time */
+ ntoowide = 0;
+
+ /* remove from consideration ones that are too big already */
+ for (mainll_p = start_p; mainll_p != end_p;
+ mainll_p = mainll_p->next) {
+
+ if (mainll_p->str == S_CHHEAD) {
+ /* loop through all chords doing this */
+ for (ch_p = mainll_p->u.chhead_p->ch_p;
+ ch_p != 0; ch_p = ch_p->ch_p) {
+ if (effwidth(ch_p) >=
+ ch_p->pseudodur * inchpwhole) {
+ ntotwhole -= ch_p->pseudodur;
+ nnotespace -= effwidth(ch_p);
+ ntoowide++;
+ }
+ }
+ }
+ }
+
+ /*
+ * In the (rare) case where nothing is now expandable (every-
+ * thing is packed perfectly tightly), we should break out now.
+ * The "<" is defensive.
+ */
+ if (ntotwhole <= 0)
+ break;
+
+ } while (ntoowide > toowide);
+
+ /*
+ * Now inchpwhole is the number of inches that should be given to each
+ * whole note worth of chords that deserve to be wider than their
+ * minimum. Allocate width proportionally to these chords.
+ */
+ for (mainll_p = start_p; mainll_p != end_p; mainll_p = mainll_p->next){
+ if (mainll_p->str == S_CHHEAD) {
+ for (ch_p = mainll_p->u.chhead_p->ch_p;
+ ch_p != 0; ch_p = ch_p->ch_p) {
+
+ /* normal case (proportional) */
+ expanded = ch_p->pseudodur * inchpwhole;
+
+ /* special case to fix space interaction */
+
+ /* but a chord of all compressible, */
+ /* nonpadded spaces deserves no width */
+ if (ch_p->width == 0)
+ expanded = 0;
+
+ /* get min dist needed from our X to next's X */
+ eff = effwidth(ch_p);
+
+ /* the dist we'll really have from X to X */
+ ch_p->fullwidth = MAX(eff, expanded);
+ }
+ }
+ }
+
+ /*
+ * Now that we know everything's width, set all absolute horizontal
+ * coordinates for this score. The absx variable keeps track of
+ * where we are, working from left to right. At all times, keep
+ * track of the start and end of each measure (leftx and rightx)
+ * and the first chord in it, so we can reposition measure rests.
+ */
+ /* first reset SSVs to how they were at start of this score */
+ setssvstate(start_p);
+
+ start_p->u.feed_p->c[AW] = eff_leftmargin(start_p);
+ start_p->u.feed_p->c[AE] = PGWIDTH - eff_right;
+ absx = eff_leftmargin(start_p) + width_left_of_score(start_p);
+ start_p->u.feed_p->c[AX] = absx;
+ leftx = 0.0; /* prevent useless 'used before set' warning */
+
+ for (mainll_p = start_p; mainll_p != end_p; mainll_p = mainll_p->next) {
+ switch (mainll_p->str) {
+ case S_SSV:
+ /* assign to keep time sig accurate */
+ asgnssv(mainll_p->u.ssv_p);
+ break;
+
+ case S_CLEFSIG:
+ clefsig_p = mainll_p->u.clefsig_p;
+
+ /* this kind partly already handled by preceding bar */
+ if (clefsig_p->clefsize == SMALLSIZE &&
+ clefsig_p->hide == NO) {
+ /* absx points at AE of barline, so add width*/
+ /* of clef excluding any clef space */
+ absx += width_clefsig(clefsig_p) -
+ clefsig_p->widestclef +
+ CSP(clefsig_p);
+ leftx = absx;
+ break;
+ }
+
+ /* "beginning of line" or "restart" clefsig */
+ clefsig_p->wclefsiga = absx;
+ if (clefsig_p->hide == NO) {
+ absx += width_clefsig(clefsig_p) +
+ CSP(clefsig_p);
+ }
+ bar_p = clefsig_p->bar_p;
+ if (bar_p != 0) {
+ /* clefsig has a pseudo bar in it; set coords*/
+ bar_p->c[AE] = absx;
+ bar_p->c[AW] = absx - width_barline(bar_p);
+ bar_p->c[AX] = (bar_p->c[AW] + absx) / 2;
+
+ /* remember the AE of this pseudobar */
+ prevbarae = absx;
+ }
+ leftx = absx;
+ break;
+
+ case S_BAR:
+ bar_p = mainll_p->u.bar_p;
+ absx += bar_p->padding;
+
+ /* apply any timed SSVs */
+ for (tssv_p = bar_p->timedssv_p; tssv_p != 0;
+ tssv_p = tssv_p->next) {
+ asgnssv(&tssv_p->ssv);
+ }
+
+ /*
+ * If this bar is followed by a clefsig, any clefs in
+ * it must be printed before this bar. Note that any
+ * padding will go before the clef (see above). But
+ * the previous measure "ends" after the clefs.
+ */
+ for (m2_p = mainll_p; m2_p != 0 &&
+ m2_p->str != S_CLEFSIG &&
+ m2_p->str != S_CHHEAD;
+ m2_p = m2_p->next)
+ ;
+ /* if clefsig that belongs to this bar line . . . */
+ if (m2_p != 0 && m2_p->str == S_CLEFSIG && m2_p->u.
+ clefsig_p->clefsize == SMALLSIZE &&
+ m2_p->u.clefsig_p->hide == NO) {
+ clefsig_p = m2_p->u.clefsig_p;
+
+ /*
+ * Apply SSVs to get the time & clef changes
+ * that occur at this bar, if any, since we
+ * are going to print the new values of them.
+ * After the width_clefsig, restore the SSVs to
+ * the proper state at this bar line.
+ */
+ for (m2_p = mainll_p; m2_p->str != S_CLEFSIG;
+ m2_p = m2_p->next) {
+ if (m2_p->str == S_SSV) {
+ asgnssv(m2_p->u.ssv_p);
+ }
+ }
+ wid = width_clefsig(clefsig_p);
+ setssvstate(mainll_p);
+
+ /* if wid > effwid, this will overlap the */
+ /* widest clef by that difference */
+ clefsig_p->wclefsiga = absx -
+ (wid - clefsig_p->effwidth) +
+ bardiff(mainll_p, end_p);
+
+ /* point absx after any clefs in clefsig */
+ absx += clefsig_p->effwidth -
+ (wid - clefsig_p->widestclef);
+ rightx = clefsig_p->wclefsiga;
+ } else { /* no relevant clefsig */
+ rightx = absx; /* prev measure "ends" here */
+ }
+ bar_p->c[AW] = absx;
+ absx += width_barline(bar_p);
+ bar_p->c[AE] = absx;
+ bar_p->c[AX] = (bar_p->c[AW] + bar_p->c[AE]) / 2.0;
+ fixfullmeas(firstch_p, (leftx + rightx) / 2.0);
+ leftx = absx; /* next measure starts here */
+
+ /*
+ * for each staff in the measure just ended, set its AE
+ * to this bar's AW, and set AX to the midpoint now
+ * that we know both AW and AE.
+ */
+ for (m2_p = mainll_p; m2_p->str != S_CHHEAD;
+ m2_p = m2_p->prev) {
+ if (m2_p->str == S_STAFF) {
+ m2_p->u.staff_p->c[AE] = bar_p->c[AW];
+ m2_p->u.staff_p->c[AX] =
+ (m2_p->u.staff_p->c[AW] +
+ m2_p->u.staff_p->c[AE]) / 2.0;
+ }
+ }
+
+ /* remember the AE of this bar */
+ prevbarae = absx;
+ break;
+
+ case S_STAFF:
+ /* as we come to each staff, set AW to prev bar's AE */
+ mainll_p->u.staff_p->c[AW] = prevbarae;
+ break;
+
+ case S_CHHEAD:
+ for (ch_p = mainll_p->u.chhead_p->ch_p; ch_p != 0 &&
+ ch_p->width == 0; ch_p = ch_p->ch_p)
+ setabschord(ch_p, absx);
+ if ((firstch_p = ch_p) == 0)
+ break;
+ absx -= firstch_p->c[RW];
+ for (ch_p = firstch_p; ch_p != 0; ch_p = ch_p->ch_p) {
+ setabschord(ch_p, absx);
+ absx += ch_p->fullwidth;
+ }
+ break;
+ }
+ }
+}
+\f
+/*
+ * Name: setabschord()
+ *
+ * Abstract: Sets horizontal absolute coordinates for everything in a chord.
+ *
+ * Returns: void
+ *
+ * Description: This function, given a chord, and its absolute offset, sets
+ * the horizontal absolute coordinates of everything in it.
+ */
+
+static void
+setabschord(ch_p, nomx)
+
+struct CHORD *ch_p; /* point at the chord */
+double nomx; /* nominal X coord; may shift it right a bit */
+
+{
+ struct GRPSYL *gs_p; /* point at a group or syllable in chord */
+ struct GRPSYL *g_p; /* point at a group with notes */
+ float extra; /* width available beyond what chord needs */
+ int n; /* loop counter */
+
+
+ /*
+ * Set the CHORD's horizonal absolute coordinates. If the chord had
+ * no room to expand (effwidth == fullwidth), there's no question
+ * where its AX has to be. But otherwise, we want to place it close
+ * to as far left as it can go, but not jammed up against there.
+ */
+ if ((extra = ch_p->fullwidth - effwidth(ch_p)) > 0) {
+ nomx += (extra > 1.20 ? 0.20 : extra / 6);
+ }
+
+ ch_p->c[AX] = nomx;
+ ch_p->c[AW] = nomx + ch_p->c[RW];
+ ch_p->c[AE] = nomx + ch_p->c[RE];
+
+ /*
+ * Loop through all GRPSYLs in this chord, setting absolute horizontal
+ * coordinates. To avoid the aggravation of dealing with SSVs again,
+ * don't bother checking if the staffs in question are visible, just
+ * do it. It doesn't hurt anything to increment garbage.
+ */
+ for (gs_p = ch_p->gs_p; gs_p != 0; gs_p = gs_p->gs_p) {
+ /*
+ * For groups, do the group itself and all the notes in it (if
+ * any), and do the same for all preceding grace groups.
+ */
+ if (gs_p->grpsyl == GS_GROUP) {
+ g_p = gs_p; /* init to the normal group */
+ do {
+ /* do the group itself, based off the chord */
+ g_p->c[AX] = ch_p->c[AX] + g_p->c[RX];
+ g_p->c[AW] = ch_p->c[AX] + g_p->c[RW];
+ g_p->c[AE] = ch_p->c[AX] + g_p->c[RE];
+
+ /* do each note, based off the group */
+ for (n = 0; n < g_p->nnotes; n++) {
+ g_p->notelist[n].c[AX] = g_p->c[AX] +
+ g_p->notelist[n].c[RX];
+ g_p->notelist[n].c[AW] = g_p->c[AX] +
+ g_p->notelist[n].c[RW];
+ g_p->notelist[n].c[AE] = g_p->c[AX] +
+ g_p->notelist[n].c[RE];
+ }
+ g_p = g_p->prev;
+ } while (g_p != 0 && g_p->grpvalue == GV_ZERO);
+ } else {
+ /* this is a syllable; just do the syllable */
+ gs_p->c[AX] = ch_p->c[AX] + gs_p->c[RX];
+ gs_p->c[AW] = ch_p->c[AX] + gs_p->c[RW];
+ gs_p->c[AE] = ch_p->c[AX] + gs_p->c[RE];
+ }
+ }
+}
+\f
+/*
+ * Name: effwidth()
+ *
+ * Abstract: Find "effective" width of a chord.
+ *
+ * Returns: the width
+ *
+ * Description: This function returns the "effective width" of a chord. This
+ * is the (minimum) width of its east part, plus the width of the
+ * west part of the following chord, if there is one.
+ */
+
+static double
+effwidth(ch_p)
+
+struct CHORD *ch_p; /* point at the chord */
+
+{
+ struct CHORD *next_p;
+
+
+ /* compressible, nonpadded spaces count for nothing */
+ if (ch_p->width == 0)
+ return (0.0);
+
+ /* find the next chord, if any, that is not all compressible, */
+ /* nonpadded spaces */
+ for (next_p = ch_p->ch_p; next_p != 0; next_p = next_p->ch_p) {
+ if (next_p->width != 0)
+ break;
+ }
+
+ /*
+ * If it's the last one in the measure, return the east side of the
+ * current chord. Otherwise, return that plus the west side of the
+ * next nonspace chord.
+ */
+ if (next_p == 0)
+ return (ch_p->c[RE]);
+ else
+ return (ch_p->c[RE] - next_p->c[RW]);
+}
+\f
+/*
+ * Name: bardiff()
+ *
+ * Abstract: Find size difference of end of score bar vs. what it will be.
+ *
+ * Returns: void
+ *
+ * Description: When a REPEATSTART occurs at the end of a score, it gets
+ * changed to a SINGLEBAR, and a REPEATBOTH becomes a REPEATEND
+ * (the following pseudobar getting set to REPEATSTART). Other
+ * bartypes are left alone. This function, given the MLL of a bar,
+ * just returns zero if the bar is not at the end of a score; but
+ * otherwise it returns the size of that bartype minus the size of
+ * what it will be replaced by.
+ */
+
+static double
+bardiff(mainll_p, end_p)
+
+struct MAINLL *mainll_p; /* MLL for the bar line */
+struct MAINLL *end_p; /* MLL after end of the score */
+
+{
+ struct MAINLL *mll_p; /* for searching the MLL */
+ struct BAR bar; /* phony BAR structure */
+ double temp; /* hold the width of the orginal bar */
+
+
+ /*
+ * Search forward from the bar. If we hit a CHHEAD before hitting the
+ * end of the score, then this is not the last barline in the score, so
+ * return zero.
+ */
+ for (mll_p = mainll_p; mll_p != end_p; mll_p = mll_p->next) {
+ if (mll_p->str == S_CHHEAD)
+ return (0.0);
+ }
+
+ /* last bar in the score, so do the arithmetic */
+ switch (mainll_p->u.bar_p->bartype) {
+ case REPEATSTART:
+ bar.bartype = REPEATSTART;
+ temp = width_barline(&bar);
+ bar.bartype = SINGLEBAR;
+ return (temp - width_barline(&bar));
+
+ case REPEATBOTH:
+ bar.bartype = REPEATBOTH;
+ temp = width_barline(&bar);
+ bar.bartype = REPEATEND;
+ return (temp - width_barline(&bar));
+ }
+
+ return (0.0); /* all other types remain the same; difference = 0 */
+}
+\f
+/*
+ * Name: fixfullmeas()
+ *
+ * Abstract: Adjust the AE of full measure symbols (mr, multirest, mrpt).
+ *
+ * Returns: void
+ *
+ * Description: This function, given the first chord in a measure (the only
+ * one that can contain a one of these symbols), adjusts the AE
+ * coord of each GRPSYL in the chords that is one of these. AW
+ * stays where it is, near the left bar line, except that for
+ * multirests it moves it to the right, especially for ones
+ * that are drawn with rest symbols. For multirests and
+ * measure repeats, AX gets moved leftwards a little, to be
+ * where it would have been for a measure rest, but for measure
+ * rests, it stays where it is, not far to the right of that.
+ * For all three things, AE is put near the right bar line, the
+ * same distance from it that AW is from the left.
+ */
+
+static void
+fixfullmeas(ch_p, x)
+
+struct CHORD *ch_p; /* point at the chord */
+double x; /* absolute X coord of center of measure */
+
+{
+ struct GRPSYL *gs_p; /* point at a group or syllable in chord */
+
+
+ /* in case we have all spaces */
+ if (ch_p == 0)
+ return;
+
+ debug(32, "fixfullmeas file=%s line=%d x=%f", ch_p->gs_p->inputfile,
+ ch_p->gs_p->inputlineno, (float)x);
+
+ /* loop through all GRPSYLs, resetting AE/AW for full measure symbols */
+ for (gs_p = ch_p->gs_p; gs_p != 0; gs_p = gs_p->gs_p) {
+ /* skip syllables */
+ if (gs_p->grpsyl != GS_GROUP) {
+ continue;
+ }
+
+ if (gs_p->is_meas == YES) {
+ gs_p->c[AE] = x + (x - gs_p->c[AW]);
+ } else if (gs_p->basictime < -1) {
+ /* multirest; move the left end to the right a little */
+ set_staffscale(gs_p->staffno);
+ gs_p->c[AW] += 2.0 * Stepsize;
+ /*
+ * For multirests that are drawn with rest symbols,
+ * the width may need to be reduced. If half the
+ * multirest's width exceeds 10 stepsizes, reduce it
+ * by 0.8 of the excess.
+ */
+ if (gs_p->basictime >= -8 && svpath(gs_p->staffno,
+ RESTSYMMULT)->restsymmult) {
+ if (x - gs_p->c[AW] > 10.0 * Stepsize) {
+ gs_p->c[AW] += ((x - gs_p->c[AW]) -
+ (10.0 * Stepsize)) * 0.8;
+ }
+ }
+ gs_p->c[AE] = x + (x - gs_p->c[AW]);
+ }
+ }
+
+ /* for multirest/mrpt, put AX where it would have been for a mr */
+ if (ch_p->gs_p->basictime < -1 || is_mrpt(gs_p)) {
+ ch_p->c[AX] = ch_p->c[AW] +
+ width(FONT_MUSIC, DFLT_SIZE, C_1REST) / 2;
+ }
+}
+\f
+/*
+ * Name: restore_grpsyl_west()
+ *
+ * Abstract: Restore all GRPSYLs' west coords when there was a clef there.
+ *
+ * Returns: void
+ *
+ * Description: In fixclef() in restsyl.c, we altered the west of any GRPSYL
+ * that was associated with a midmeasure clef. This was needed so
+ * that room would be made for the clefs. Now that the packing
+ * part of abshorz.c is done, we can restore these coords, for the
+ * benefit of the print phrase.
+ */
+
+static void
+restore_grpsyl_west()
+
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ struct GRPSYL *gs_p; /* point along a GRPSYL list */
+ struct GRPSYL *gs2_p; /* look for a grace group's main grp */
+ int vidx; /* voice index */
+ float size; /* to be used for clef */
+ float staffscale; /* scale for a staff */
+ float clefwid; /* width of a clef */
+
+
+ size = 3.0/4.0 * DFLT_SIZE; /* small size clefs */
+ initstructs();
+
+ for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+ switch (mainll_p->str) {
+ case S_SSV:
+ /* keep staffscale up to date */
+ asgnssv(mainll_p->u.ssv_p);
+ continue;
+ case S_STAFF:
+ /* break out to handle staff */
+ break;
+ default:
+ continue;
+ }
+
+ for (vidx = 0; vidx < MAXVOICES; vidx++) {
+ for (gs_p = mainll_p->u.staff_p->groups_p[vidx];
+ gs_p != 0; gs_p = gs_p->next) {
+ if (gs_p->clef == NOCLEF) {
+ continue; /* no clef, nothing to do */
+ }
+
+ staffscale = svpath(gs_p->staffno,
+ STAFFSCALE)->staffscale;
+ clefwid = (clefwidth(gs_p->clef, YES) +
+ CLEFPAD) * staffscale;
+ gs_p->c[RW] += clefwid;
+ gs_p->c[AW] += clefwid;
+
+ /*
+ * If we are a grace group, look ahead to the
+ * main group and restore it too.
+ */
+ if (gs_p->grpvalue == GV_ZERO) {
+ for (gs2_p = gs_p; gs2_p->grpvalue ==
+ GV_ZERO; gs2_p = gs2_p->next) {
+ ;
+ }
+ gs2_p->c[RW] += clefwid;
+ gs2_p->c[AW] += clefwid;
+ }
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: setipw()
+ *
+ * Abstract: Set INCHPERWHOLE "coordinate" for all structures having it.
+ *
+ * Returns: void
+ *
+ * Description: This function sets the special pseudocoord "c[INCHPERWHOLE]"
+ * for all nongrace GRPSYLs, notes, chords, and BARs. BARs is
+ * done right here; for the others, it calls subroutines.
+ */
+
+static void
+setipw()
+
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ struct MAINLL *m2_p; /* look forward for bar line */
+ struct GRPSYL *gs_p; /* point along a GRPSYL list */
+ int timeden; /* denominator of a time signature */
+ int v; /* index into voices or verses */
+
+
+ debug(16, "setipw");
+ initstructs(); /* clean out old SSV info */
+
+ /*
+ * Loop through MLL, applying SSVs and processing each visible linked
+ * list of GRPSYLs.
+ */
+ for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+
+ switch (mainll_p->str) {
+ case S_SSV:
+ /* this is needed to keep time sig up to date */
+ asgnssv(mainll_p->u.ssv_p);
+ break;
+
+ case S_CHHEAD:
+ /* set the thing for all chords in this measure */
+ setipwchord(mainll_p);
+ break;
+
+ case S_STAFF:
+ /* skip this staff if it's invisible */
+ if (mainll_p->u.staff_p->visible == NO)
+ break;
+
+ /* do all the voices on this staff */
+ for (v = 0; v < MAXVOICES && (gs_p = mainll_p->u.
+ staff_p->groups_p[v]) != 0; v++) {
+ setipwgrpsyl(mainll_p, gs_p);
+ }
+
+ /* do all the verses on this staff */
+ for (v = 0; v < mainll_p->u.staff_p->nsyllists; v++) {
+ gs_p = mainll_p->u.staff_p->syls_p[v];
+ setipwgrpsyl(mainll_p, gs_p);
+ }
+
+ break;
+
+ case S_BAR:
+ /*
+ * If this is the ending bar line of a score, ignore
+ * it. The following measure would refer to its
+ * preceding CLEFSIG's pseudo bar instead. So see if
+ * we hit a FEED while trying to find the next CHHEAD.
+ * While doing this, keep track of the denominator of
+ * the time signature, in case it changes at this bar.
+ */
+ timeden = Score.timeden;
+ for (m2_p = mainll_p; m2_p != 0 && m2_p->str != S_FEED
+ && m2_p->str != S_CHHEAD;
+ m2_p = m2_p->next) {
+ if (m2_p->str == S_SSV && m2_p->u.ssv_p->used[
+ TIME] == YES) {
+ timeden = m2_p->u.ssv_p->timeden;
+ }
+ }
+ if (m2_p == 0 || m2_p->str == S_FEED)
+ break;
+
+ /*
+ * This is not the last bar of a score, and m2_p points
+ * at the CHHEAD of the following measure, with timeden
+ * being the denominator of the time sig. The space
+ * between the bar ("count 0") and the first chord
+ * ("count 1") must be multiplied by the number of
+ * counts in a whole note (timeden).
+ */
+ mainll_p->u.bar_p->c[INCHPERWHOLE] = timeden *
+ (m2_p->u.chhead_p->ch_p->c[AX] -
+ mainll_p->u.bar_p->c[AX]);
+ break;
+
+ case S_CLEFSIG:
+ /*
+ * If this clefsig is not at the start of a score,
+ * ignore it. If it is, it will contain a pseudo bar
+ * line, and we need to set that bar's coord just like
+ * for a normal bar line.
+ */
+ if (mainll_p->u.clefsig_p->bar_p == 0)
+ break;
+
+ if (mainll_p->next->str != S_CHHEAD)
+ pfatal("CLEFSIG with pseudo bar not followed by CHHEAD");
+
+ mainll_p->u.clefsig_p->bar_p->c[INCHPERWHOLE] =
+ Score.timeden *
+ (mainll_p->next->u.chhead_p->ch_p->c[AX] -
+ mainll_p->u.clefsig_p->bar_p->c[AX]);
+ break;
+ }
+ }
+}
+\f
+/*
+ * Name: setipwgrpsyl()
+ *
+ * Abstract: Set INCHPERWHOLE "coordinate" for the GRPSYLs in one list.
+ *
+ * Returns: void
+ *
+ * Description: This function sets the special pseudocoord "c[INCHPERWHOLE]"
+ * for all the nongrace GRPSYLs and notes in one voice or verse
+ * list hanging off a STAFF.
+ */
+
+static void
+setipwgrpsyl(mainll_p, gs_p)
+
+struct MAINLL *mainll_p; /* point along main linked list */
+struct GRPSYL *gs_p; /* point along this GRPSYL list */
+
+{
+ struct MAINLL *m2_p; /* look forward for bar line */
+ struct GRPSYL *ngs_p; /* the next nongrace GRPSYL in list */
+ float inchperwhole; /* inches per whole note */
+ int n; /* loop variable */
+
+
+ debug(32, "setipwgrpsyl file=%s line=%d", gs_p->inputfile,
+ gs_p->inputlineno);
+ /* get first nongrace GRPSYL */
+ for ( ; gs_p != 0 && gs_p->grpsyl == GS_GROUP &&
+ gs_p->grpvalue == GV_ZERO; gs_p = gs_p->next)
+ ;
+ if (gs_p == 0)
+ pfatal("nothing but grace notes in measure");
+
+ /*
+ * Loop down the list of GRPSYLs. gs_p always points the current
+ * (nongrace) GRPSYL, whose inches per whole we want to set. ngs_p
+ * points at the next nongrace GRPSYL.
+ */
+ for (;;) {
+ /* find next nongrace GRPSYL; break if none */
+ for (ngs_p = gs_p->next;
+ ngs_p != 0 && ngs_p->grpsyl == GS_GROUP &&
+ ngs_p->grpvalue == GV_ZERO;
+ ngs_p = ngs_p->next)
+ ;
+ if (ngs_p == 0)
+ break;
+
+ /*
+ * Distance between them divided by time gives the space a
+ * a whole note theoretically would have been given.
+ */
+ inchperwhole = (ngs_p->c[AX] - gs_p->c[AX]) /
+ RAT2FLOAT(gs_p->fulltime);
+
+ /* store in GRPSYL & each note (if notes) */
+ gs_p->c[INCHPERWHOLE] = inchperwhole;
+ if (gs_p->grpsyl == GS_GROUP && gs_p->grpcont == GC_NOTES) {
+ for (n = 0; n < gs_p->nnotes; n++)
+ gs_p->notelist[n].c[INCHPERWHOLE]
+ = inchperwhole;
+ }
+
+ /* point current at next, for next iteration */
+ gs_p = ngs_p;
+ }
+
+ /*
+ * We've hit the end of the measure. Loop forward through the MLL
+ * until we find the bar line.
+ */
+ for (m2_p = mainll_p;
+ m2_p != 0 && m2_p->str != S_BAR;
+ m2_p = m2_p->next)
+ ;
+ if (m2_p == 0)
+ pfatal("no bar at end of last measure");
+
+ /* this time use bar line as terminating point */
+ inchperwhole = (m2_p->u.bar_p->c[AX] - gs_p->c[AX]) /
+ RAT2FLOAT(gs_p->fulltime);
+
+ gs_p->c[INCHPERWHOLE] = inchperwhole;
+ if (gs_p->grpsyl == GS_GROUP && gs_p->grpcont == GC_NOTES) {
+ for (n = 0; n < gs_p->nnotes; n++)
+ gs_p->notelist[n].c[INCHPERWHOLE] = inchperwhole;
+ }
+}
+\f
+/*
+ * Name: setipwchord()
+ *
+ * Abstract: Set INCHPERWHOLE "coordinate" for the CHORDs in one list.
+ *
+ * Returns: void
+ *
+ * Description: This function sets the special pseudocoord "c[INCHPERWHOLE]"
+ * for all the CHORDs in the list hanging off of one CHHEAD.
+ */
+
+static void
+setipwchord(mainll_p)
+
+struct MAINLL *mainll_p; /* point at the CHHEAD */
+
+{
+ struct MAINLL *m2_p; /* look forward for bar line */
+ struct CHORD *ch_p, *nch_p; /* point at chords */
+
+
+ debug(32, "setipwchord file=%s line=%d", mainll_p->inputfile,
+ mainll_p->inputlineno);
+ /*
+ * Loop down the list of CHORDs. ch_p always points the current
+ * CHORD, whose inches per whole we want to set. nch_p points at
+ * the next CHORD. When nch_p is 0, ch_p is the last chord, and we
+ * get out of the loop.
+ */
+ for (ch_p = mainll_p->u.chhead_p->ch_p, nch_p = ch_p->ch_p;
+ nch_p != 0; ch_p = nch_p, nch_p = nch_p->ch_p) {
+ /*
+ * Distance between them divided by time gives the space a
+ * a whole note theoretically would have been given.
+ */
+ ch_p->c[INCHPERWHOLE] = (nch_p->c[AX] - ch_p->c[AX]) /
+ RAT2FLOAT(ch_p->duration);
+ }
+
+ /*
+ * We've hit the end of the measure. Loop forward through the MLL
+ * until we find the bar line.
+ */
+ for (m2_p = mainll_p;
+ m2_p != 0 && m2_p->str != S_BAR;
+ m2_p = m2_p->next)
+ ;
+ if (m2_p == 0)
+ pfatal("no bar at end of last measure");
+
+ /* this time use bar line as terminating point */
+ ch_p->c[INCHPERWHOLE] = (m2_p->u.bar_p->c[AX] - ch_p->c[AX]) /
+ RAT2FLOAT(ch_p->duration);
+}
+\f
+/*
+ * Name: fixendings()
+ *
+ * Abstract: Fix endings at end of score and in pseudobars.
+ *
+ * Returns: void
+ *
+ * Description: This function finds endings that start at the final bar of a
+ * score. It moves them so that they will start at the pseudobar
+ * at the start of the next score. Then, wherever an ending is
+ * continuing through a scorefeed, set the pseudobar's endingloc.
+ */
+
+static void
+fixendings()
+
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ struct MAINLL *m2_p; /* look forward for bar line */
+ struct BAR *bar_p; /* point at preceding bar */
+ char *str_p; /* point at an ending string */
+
+
+ debug(16, "fixendings");
+ /*
+ * Loop through the main linked list, looking for endings that start at
+ * the end of a score, and moving them. We do it in reverse, to make
+ * it slightly easier to deal with the case of scores that have only
+ * one measure on them. (Previous endings won't have been moved yet.)
+ */
+ for (mainll_p = Mainlltc_p; mainll_p != 0; mainll_p = mainll_p->prev) {
+ if (mainll_p->str != S_BAR)
+ continue;
+ if (mainll_p->u.bar_p->endingloc != STARTITEM)
+ continue;
+
+ /*
+ * We are at a bar where an ending starts. Find out if this is
+ * at the end of a score, by seeing if we find a FEED before
+ * the next bar.
+ */
+ for (m2_p = mainll_p->next; m2_p != 0 && m2_p->str != S_BAR &&
+ m2_p->str != S_FEED; m2_p = m2_p->next)
+ ;
+ if (m2_p == 0)
+ pfatal("unterminated ending");
+ if (m2_p->str == S_BAR)
+ continue;
+
+ /*
+ * The ending starts at the last bar of a score. We need to
+ * know whether a previous ending also ends there, or not. So
+ * search back to the previous bar. Since we're doing the main
+ * loop in reverse, we don't have to look at pseudobars, only
+ * real ones.
+ */
+ for (m2_p = mainll_p->prev; m2_p != 0 && m2_p->str != S_BAR;
+ m2_p = m2_p->prev)
+ ;
+
+ /*
+ * If the previous bar was the end of an ending or not involved
+ * in one at all, the bar at the end of the score should not be
+ * involved. Otherwise, there was a preceding ending which
+ * ends here (where the new one starts), so mark that it ends.
+ */
+ if (m2_p == 0 || m2_p->u.bar_p->endingloc == ENDITEM ||
+ m2_p->u.bar_p->endingloc == NOITEM)
+
+ mainll_p->u.bar_p->endingloc = NOITEM;
+ else
+ mainll_p->u.bar_p->endingloc = ENDITEM;
+
+ str_p = mainll_p->u.bar_p->endinglabel;
+ mainll_p->u.bar_p->endinglabel = 0;
+
+ /*
+ * Find the first feed after this bar that is not at the start
+ * of a "block", and mark in the following pseudobar that an
+ * ending starts there.
+ */
+ for (m2_p = mainll_p->next; m2_p != 0 && (m2_p->str != S_FEED ||
+ m2_p->next != 0 && m2_p->next->str == S_BLOCKHEAD);
+ m2_p = m2_p->next)
+ ;
+ if (m2_p == 0) {
+ pfatal("can't find any music after ending begins");
+ }
+ m2_p->next->u.clefsig_p->bar_p->endingloc = STARTITEM;
+ m2_p->next->u.clefsig_p->bar_p->endinglabel = str_p;
+ }
+
+ /*
+ * Loop again through the main linked list, this time forwards.
+ * Remember each bar as we find one. Then, adjust the following
+ * pseudobar if need be.
+ */
+ bar_p = 0; /* no previous bar yet */
+ for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+ if (mainll_p->str == S_BAR)
+ bar_p = mainll_p->u.bar_p;
+ if (mainll_p->str == S_CLEFSIG &&
+ mainll_p->u.clefsig_p->bar_p != 0) {
+ /*
+ * We're at a pseudobar. If the preceding bar was
+ * inside an ending, mark the pseudobar that way too.
+ * (If this is the first pseudobar, there won't have
+ * been any preceding bar.)
+ */
+ if (bar_p != 0 && bar_p->endingloc == INITEM)
+ mainll_p->u.clefsig_p->bar_p->endingloc
+ = INITEM;
+ }
+ }
+}
+\f
+/*
+ * Name: fixreh()
+ *
+ * Abstract: Move rehearsal marks at end of a score to the next score.
+ *
+ * Returns: void
+ *
+ * Description: This function finds rehearsal marks at the final bar of a
+ * score. It moves them so that they will be at the pseudobar
+ * at the start of the next score.
+ */
+
+static void
+fixreh()
+
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ struct MAINLL *m2_p; /* look forward for bar line */
+
+
+ debug(16, "fixreh");
+ /*
+ * Loop through the main linked list, looking for rehearsal marks at
+ * the end of a score, and moving them.
+ */
+ for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+ if (mainll_p->str != S_BAR)
+ continue;
+ if (mainll_p->u.bar_p->reh_type == REH_NONE)
+ continue;
+
+ /*
+ * We are at a bar with a rehearsal mark. Find out if this is
+ * at the end of a score, by seeing if we find a FEED before
+ * the next bar.
+ */
+ for (m2_p = mainll_p->next; m2_p != 0 && m2_p->str != S_BAR &&
+ m2_p->str != S_FEED; m2_p = m2_p->next)
+ ;
+ if (m2_p == 0)
+ return; /* nothing more we can do in this case */
+ if (m2_p->str == S_BAR)
+ continue;
+
+ /*
+ * The ending starts at the last bar of a score. m2_p is at
+ * the FEED there, but what follows could be either music or a
+ * "block". If it is a block, we need to keep moving forward
+ * until we find a FEED followed by music.
+ */
+ while (m2_p != 0 && ! IS_CLEFSIG_FEED(m2_p)) {
+ m2_p = m2_p->next;
+ }
+ if (m2_p == 0) {
+ return; /* there is no more music, can't move reh */
+ }
+
+ /*
+ * We found the FEED. Move the rehearsal mark to the pseudo
+ * bar after the FEED.
+ */
+ m2_p->next->u.clefsig_p->bar_p->reh_type =
+ mainll_p->u.bar_p->reh_type;
+ mainll_p->u.bar_p->reh_type = REH_NONE;
+
+ m2_p->next->u.clefsig_p->bar_p->reh_string =
+ mainll_p->u.bar_p->reh_string;
+ mainll_p->u.bar_p->reh_string = 0;
+
+ m2_p->next->u.clefsig_p->bar_p->dist =
+ mainll_p->u.bar_p->dist;
+ mainll_p->u.bar_p->dist = 0;
+
+ m2_p->next->u.clefsig_p->bar_p->dist_usage =
+ mainll_p->u.bar_p->dist_usage;
+ mainll_p->u.bar_p->dist_usage = SD_NONE;
+ }
+}
+\f
+/*
+ * Name: clrinhprint()
+ *
+ * Abstract: Clear the inhibitprint on tablature staffs when appropriate.
+ *
+ * Returns: void
+ *
+ * Description: This function clears the inhibitprint bit in the first group
+ * of a tablature staff after a scorefeed. (Because in that
+ * situation, the group should be printed regardless of the usual
+ * conditions that inhibit printing.) Also, parentheses should be
+ * put around every note (fret number) in such groups.
+ */
+
+static void
+clrinhprint()
+
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ struct GRPSYL *gs_p; /* point at first group */
+ int sawscorefeed; /* did we just see a scorefeed? */
+ int vidx; /* voice index */
+ int n; /* loop through the notes */
+
+
+ debug(16, "clrinhprint");
+ sawscorefeed = YES; /* "new score" at start of song */
+
+ /*
+ * Loop through main linked list, looking for visible tablature STAFFs,
+ * scorefeeds, and bar lines.
+ */
+ for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+
+ switch (mainll_p->str) {
+ case S_FEED:
+ sawscorefeed = YES; /* just saw a feed */
+ continue;
+ case S_BAR:
+ sawscorefeed = NO; /* next bar, forget the feed */
+ continue;
+ case S_STAFF:
+ /*
+ * If we just saw a scorefeed, and this is a visible
+ * tablature staff, break to handle it. Otherwise
+ * continue to the next loop iteration.
+ */
+ if (sawscorefeed == YES &&
+ mainll_p->u.staff_p->visible == YES &&
+ is_tab_staff(mainll_p->u.staff_p->staffno))
+ break;
+ continue;
+ default:
+ continue;
+ }
+
+ /* loop through each possible voice on tab staff */
+ for (vidx = 0; vidx < MAXVOICES; vidx++) {
+
+ /* if voice doesn't exist, break out */
+ gs_p = mainll_p->u.staff_p->groups_p[vidx];
+ if (gs_p == 0)
+ break;
+
+ /* if not a note group, there's nothing to do */
+ if (gs_p->grpcont != GC_NOTES)
+ continue;
+
+ /*
+ * If inhibitprint was set, we need to put parens
+ * around the notes (frets) and clear the bit.
+ */
+ if (gs_p->inhibitprint == YES) {
+ for (n = 0; n < gs_p->nnotes; n++)
+ gs_p->notelist[n].FRET_HAS_PAREN = YES;
+ gs_p->inhibitprint = NO;
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: hidestaffs()
+ *
+ * Abstract: Make staffs invisible if visible=whereused and they're empty.
+ *
+ * Returns: void
+ *
+ * Description: If the user set visible=whereused for staffs, up until now we
+ * have been treating it as visible=y, because the internal field
+ * visible==YES. But now that we know where all the scorefeeds
+ * are, this function looks at hidesilent for the given score, and
+ * when a staff should be invisible based on that, inserts SSVs
+ * and sets the visible field in the STAFF structures to make it
+ * invisible.
+ */
+
+static int
+hidestaffs(mainll_p, ml2_p)
+
+struct MAINLL *mainll_p; /* point at feed at start of score */
+struct MAINLL *ml2_p; /* point at last thing on the score */
+
+{
+ struct SSV *ssv_p; /* a static SSV containing visibility */
+ int s; /* staff number */
+ int firstvis; /* first visible staff in a score */
+ int foundvis; /* is a staff after first still visible? */
+ int forced_invis; /* did we force any staffs invisible? */
+ int ressv; /* must we reapply SSVs from the start? */
+
+
+ debug(16, "hidestaffs");
+
+ /*
+ * Loop through main linked list, applying SSVs and looking for FEEDs.
+ * When a FEED is found, check all the staffs and make the appropriate
+ * ones invisible.
+ */
+ /* find the first (currently) visible staff in this score */
+ firstvis = 0;
+ for (s = 1; s <= Score.staffs; s++) {
+ if (svpath(s, VISIBLE)->visible == YES) {
+ firstvis = s;
+ break;
+ }
+ }
+ if (firstvis == 0) {
+ pfatal("no visible staffs in score");
+ }
+
+ /*
+ * Working bottom up, check each currently visible staff to see whether
+ * it should be made invisible. If so, make it so. But if nothing
+ * below the first visible staff ends up visible, we leave it alone,
+ * since at least one staff must always be visible.
+ * There are cases when silent() has to apply some SSVs. In such
+ * cases, it sets ressv=YES. Sadly, we have to reapply SSVs from the
+ * start in that case.
+ */
+ foundvis = NO;
+ forced_invis = NO;
+ for (s = Score.staffs; s >= firstvis; s--) {
+ if (s == firstvis && foundvis == NO) {
+ /* only the top visible staff remains visible */
+ break;
+ }
+ ssv_p = svpath(s, VISIBLE);
+ if (ssv_p->visible == NO) { /* already invisible */
+ continue;
+ }
+ if (ssv_p->hidesilent == YES) {
+ if (silent(mainll_p, ml2_p, s, &ressv) == YES) {
+ /* silent() forced it invisible */
+ forced_invis = YES;
+ } else {
+ /* silent() left it visible */
+ foundvis = YES;
+ }
+ if (ressv == YES) {
+ setssvstate(mainll_p);
+ }
+ } else { /* hidesilent == NO */
+ foundvis = YES; /* leave it visible */
+ }
+ }
+
+ return (forced_invis);
+}
+\f
+/*
+ * Name: silent()
+ *
+ * Abstract: Make a staff invisible for this score, if appropriate.
+ *
+ * Returns: YES if we made it invisible, else NO
+ *
+ * Description: This function decides whether the given staff should be made
+ * invisible on the given score (line). It should be called only
+ * when visible==YES and hidesilent==YES. If it should be made
+ * invisible, it does that by inserting new "input" SSVs into the
+ * MLL before and after that line, and setting the visible field
+ * in the staffs to NO. There are cases where this function calls
+ * asgnssv(); in those cases it sets *ressv_p to YES, otherwise NO.
+ */
+
+static int
+silent(feedmll_p, ml2_p, s, ressv_p)
+
+struct MAINLL *feedmll_p; /* point along main linked list */
+struct MAINLL *ml2_p; /* point at MLL item at end of this score */
+int s; /* staff number */
+int *ressv_p; /* must the caller reapply SSVs? */
+
+{
+ struct MAINLL *mll_p; /* point along MLL */
+ struct MAINLL *lastbar_p; /* last bar line in score */
+ struct MAINLL *ins_p; /* point at MLL after which a new SSV goes */
+ struct MAINLL *new_p; /* point at MLL struct for a new SSV */
+ struct SSV *ssv_p; /* an SSV */
+ struct STAFF *staff_p; /* point at a STAFF */
+ struct STAFF *pstaff_p; /* point at the previous STAFF */
+ struct GRPSYL *gs_p; /* point at a group or syllable */
+ struct STUFF *stuff_p; /* point along a STUFF list */
+ int vidx; /* voice or verse index */
+
+
+ *ressv_p = NO; /* no SSVs have been applied yet */
+
+ /* find the last bar line in this score; it's where we should stop */
+ lastbar_p = 0;
+ for (mll_p = feedmll_p->next; mll_p != ml2_p && mll_p->str != S_FEED;
+ mll_p = mll_p->next) {
+ if (mll_p->str == S_BAR) {
+ lastbar_p = mll_p;
+ }
+ }
+ /* if none, there is no music here */
+ if (lastbar_p == 0) {
+ return (NO); /* nothing to hide */
+ }
+
+ /*
+ * Loop through this score, checking SSVs and looking in the STAFFs for
+ * this staff number, looking for reasons we must keep the staff
+ * visible.
+ */
+ for (mll_p = feedmll_p; mll_p != lastbar_p; mll_p = mll_p->next) {
+ switch (mll_p->str) {
+ case S_SSV:
+ /*
+ * To minimize the chances that we will apply an SSV
+ * and thus have to initstructs() and reapply from the
+ * beginning, apply only if it is relevent to what we
+ * are doing.
+ */
+ ssv_p = mll_p->u.ssv_p;
+ if (ssv_p->context != C_SCORE && ssv_p->staffno != s) {
+ /* this SSV is irrelevant to our staff */
+ continue;
+ }
+ if (ssv_p->used[VISIBLE] != NO) {
+ /*
+ * This SSV could affect our staff's visibility.
+ * Apply it, and remember that we've now messed
+ * with the fixed SSVs, and so we'll have to
+ * reapply from the start.
+ */
+ asgnssv(ssv_p);
+ *ressv_p = YES;
+ }
+ /*
+ * This staff started this score with visible==YES and
+ * hidesilent==YES. We know we are not going to see an
+ * SSV that causes our staff to go invisible, since
+ * that would have forced a scorefeed. But we could
+ * see one that causes our hidesilent value to be NO,
+ * and in that case we can immediately return NO, since
+ * it must remain visible.
+ */
+ if (svpath(s, VISIBLE)->hidesilent == NO) {
+ return (NO);
+ }
+ continue;
+
+ case S_STAFF:
+ staff_p = mll_p->u.staff_p;
+ if (staff_p->staffno != s) {
+ continue; /* some other staff, ignore */
+ }
+ break; /* break out to handle our staff */
+
+ default:
+ continue;
+ }
+
+ /* decide whether this staff can be made invisible */
+
+ /*
+ * Look at each group in each possible voice. If any contain
+ * notes, our staff must remain visible.
+ */
+ for (vidx = 0; vidx < MAXVOICES; vidx++) {
+ for (gs_p = staff_p->groups_p[vidx]; gs_p != 0;
+ gs_p = gs_p->next) {
+ if (gs_p->grpcont == GC_NOTES) {
+ return (NO);
+ }
+ }
+ }
+
+ /* if there are any syllables, our staff must remain visible */
+ if (staff_p->nsyllists != 0) {
+ return (NO);
+ }
+
+ /* if there is any stuff, our staff must remain visible */
+ if (staff_p->stuff_p != 0) {
+ return (NO);
+ }
+
+ /*
+ * If the previous MLL structure is a staff, it could have
+ * lyrics or "stuff" between it and our staff. If this
+ * previous staff is already invisible, ignore it since these
+ * things would be invisible. But the previous staff is
+ * visible, check for any of them being "between", in which
+ * case our staff must remain visible.
+ */
+ if (mll_p->prev->str == S_STAFF) {
+ pstaff_p = mll_p->prev->u.staff_p;
+ if (pstaff_p->visible == YES) {
+ for (vidx = 0; vidx < pstaff_p->nsyllists;
+ vidx++) {
+ if (pstaff_p->sylplace[vidx] ==
+ PL_BETWEEN) {
+ return (NO);
+ }
+ }
+ for (stuff_p = pstaff_p->stuff_p; stuff_p != 0;
+ stuff_p = stuff_p->next) {
+ if (stuff_p->place == PL_BETWEEN) {
+ return (NO);
+ }
+ }
+ }
+ }
+ }
+
+ /*
+ * At this point we've looked through everything and found that there
+ * is no need to keep this staff visible. So we are going to force it
+ * invisible. If a staff's SSV says visible==NO but it has voice(s)
+ * with visible==YES, it ends up being visible anyhow. So in addition
+ * to forcing the staff to visible=NO, we will unset all its voices'
+ * visibility. Rather than checking how many voices there are, it's
+ * easiest just to force all possible ones invisible.
+ */
+
+ /*
+ * Set ins_p to the SSV after which the new ones should be put. There
+ * may be a CLEFSIG before the FEED; if so, they should be put before
+ * there, otherwise just before the FEED. This is to maintain the
+ * correct ordering of structures; see comment at the end of structs.h.
+ * If the FEED is at the start, ins_p will be 0.
+ */
+ ins_p = feedmll_p->prev;
+ if (ins_p != 0 && ins_p->str == S_CLEFSIG) {
+ ins_p = ins_p->prev;
+ }
+
+ /* force staff's visible to NO */
+ new_p = newMAINLLstruct(S_SSV, -1);
+ ssv_p = new_p->u.ssv_p;
+ ssv_p->context = C_STAFF;
+ ssv_p->staffno = s;
+ ssv_p->used[VISIBLE] = YES;
+ ssv_p->visible = NO;
+ insertMAINLL(new_p, ins_p);
+
+ /* force voices' visible to unset */
+ for (vidx = 0; vidx < MAXVOICES; vidx++) {
+ new_p = newMAINLLstruct(S_SSV, -1);
+ ssv_p = new_p->u.ssv_p;
+ ssv_p->context = C_VOICE;
+ ssv_p->staffno = s;
+ ssv_p->voiceno = vidx + 1;
+ ssv_p->used[VISIBLE] = UNSET;
+ insertMAINLL(new_p, ins_p);
+ }
+
+ /* do not let any SSVs on this line alter this staff's visibility */
+ for (mll_p = feedmll_p; mll_p != lastbar_p; mll_p = mll_p->next) {
+ if (mll_p->str != S_SSV) {
+ continue;
+ }
+ ssv_p = mll_p->u.ssv_p;
+ /*
+ * Since we know we are overriding the score, we don't care if
+ * the score is changing. Just force all staff and voice SSVs
+ * for this staff to not be setting VISIBLE.
+ */
+ if (ssv_p->context != C_SCORE && ssv_p->staffno == s) {
+ ssv_p->used[VISIBLE] = NO;
+ }
+ }
+
+ /* the SSVs to be put at the end go after the last bar line */
+ ins_p = lastbar_p;
+
+ /*
+ * Insert "input" SSVs that will cause the staff's fixed SSV and its
+ * voices' fixed SSVs to be restored to how they would have been if we
+ * hadn't changed anything. That is the state they are in right now.
+ */
+ new_p = newMAINLLstruct(S_SSV, -1);
+ ssv_p = new_p->u.ssv_p;
+ ssv_p->context = C_STAFF;
+ ssv_p->staffno = s;
+ if (Staff[s-1].used[VISIBLE] == YES) {
+ ssv_p->used[VISIBLE] = YES;
+ ssv_p->visible = Staff[s-1].visible;
+ ssv_p->hidesilent = Staff[s-1].hidesilent;
+ } else {
+ ssv_p->used[VISIBLE] = UNSET;
+ }
+ insertMAINLL(new_p, ins_p);
+
+ for (vidx = 0; vidx < MAXVOICES; vidx++) {
+ new_p = newMAINLLstruct(S_SSV, -1);
+ ssv_p = new_p->u.ssv_p;
+ ssv_p->context = C_VOICE;
+ ssv_p->staffno = s;
+ ssv_p->voiceno = vidx + 1;
+ if (Voice[s-1][vidx].used[VISIBLE] == YES) {
+ ssv_p->used[VISIBLE] = YES;
+ ssv_p->visible = Voice[s-1][vidx].visible;
+ ssv_p->hidesilent = Voice[s-1][vidx].hidesilent;
+ } else {
+ ssv_p->used[VISIBLE] = UNSET;
+ }
+ insertMAINLL(new_p, ins_p);
+ }
+
+ /* set visible to NO in every staff of this number on this line */
+ for (mll_p = feedmll_p; mll_p != lastbar_p; mll_p = mll_p->next) {
+ if (mll_p->str == S_STAFF) {
+ staff_p = mll_p->u.staff_p;
+ if (staff_p->staffno == s) {
+ staff_p->visible = NO;
+ }
+ }
+ }
+
+ return (YES);
+}
+\f
+/*
+ * Name: getmultinum()
+ *
+ * Abstract: Find number of measures in the next staff's multirest.
+ *
+ * Returns: The number, or 0 if next staff is not a multirest.
+ *
+ * Description: This function is given an MLL struct, and if it's not a STAFF,
+ * searches forward to the next STAFF. It returns as stated above.
+ */
+
+static int
+getmultinum(mll_p)
+
+struct MAINLL *mll_p; /* point along MLL, starts at the CLEFSIG */
+
+{
+ int basictime; /* of the first group in the first following staff */
+
+
+ /* find the first staff after this clefsig */
+ for ( ; mll_p != 0 && mll_p->str != S_STAFF; mll_p = mll_p->next) {
+ ;
+ }
+
+ /* if no staff, there is no multirest */
+ if (mll_p == 0) {
+ return (0);
+ }
+
+ basictime = mll_p->u.staff_p->groups_p[0]->basictime;
+ return (basictime < -1 ? -basictime : 0);
+}