X-Git-Url: https://git.distorted.org.uk/~mdw/mup/blobdiff_plain/cdb3c0882392596f814cf939cbfbd38adc6f2bfe..ddf6330b56bcfb657e0186b24b9b1422c51d3424:/mup/mup/stuff.c diff --git a/mup/mup/stuff.c b/mup/mup/stuff.c new file mode 100644 index 0000000..08a2259 --- /dev/null +++ b/mup/mup/stuff.c @@ -0,0 +1,2255 @@ +/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2005 by Arkkra Enterprises */ +/* All rights reserved */ +/* + * Name: stuff.c + * + * Description: This file contains functions for handling "stuff". + */ + +#include "defines.h" +#include "structs.h" +#include "globals.h" + +/* for STUFF, apply appropriate staffscale, based on "all" */ +#define STUFFSTEP(stuff_p) ((stuff_p)->all == YES ? \ + Score.staffscale * STEPSIZE : stepsize); + +static void normalizestuff P((void)); +static void movestuff P((struct STAFF *staff1_p, struct STAFF *staff2_p, + struct STUFF *stuff_p)); +static void setphrases P((void)); +static void phrasestuff P((struct MAINLL *msbeg_p, struct STUFF *stuff_p)); +static void set1phrase P((struct MAINLL *msbeg_p, struct STUFF *stuff_p, + int v)); +static void rmstuff P((struct STAFF *staff_p, struct STUFF *stuff_p)); +static void breakstuff P((void)); +static struct STUFF *prevstuff P((struct MAINLL *mainll_p, + struct STUFF *stuff_p)); +static void breakone P((struct MAINLL *m2_p, struct STUFF *stuff_p, + int timenum, char *origstr_p, int depth)); +static void contpedal P((void)); +static void setstuff P((void)); +static double count2coord P((double count, struct BAR *bar_p, + struct CHHEAD *chhead_p, int timeden)); +static int geteast P((struct STUFF *stuff_p, struct MAINLL *m2_p, + short *timeden2_p, struct CHHEAD **chhead2_p_p, + struct BAR **bar2_p_p)); +static int setmrferm P((struct STAFF *staff_p, struct STUFF *stuff_p)); +static int trygrid P((struct MAINLL *mainll_p, struct STUFF *stuff_p)); +static void tieslurstuff P((void)); +static void mktieslurstuff P((struct MAINLL *mllstaff_p, struct GRPSYL *gs_p, + int n, int s)); +static void mkextrastuff P((struct MAINLL *mll_p, struct STUFF *origstuff_p, + struct GRPSYL *gs_p, int n, int s, int stufftype)); + +/* + * Name: stuff() + * + * Abstract: Perform all necessary horizontal operations on STUFF. + * + * Returns: void + * + * Description: This function calls subroutines to do all necessary horizontal + * operations on STUFF. + */ + +void +stuff() +{ + debug(16, "stuff"); + normalizestuff(); + setphrases(); + breakstuff(); + contpedal(); + setstuff(); + tieslurstuff(); +} + +/* + * Name: normalizestuff() + * + * Abstract: Normalize STUFF starting at count N + 1 before a FEED. + * + * Returns: void + * + * Description: This function looks for all STUFF structures that start at + * count N + 1 (where N is the numerator of the time signature) + * in measures preceding a score feed. These things really + * should be drawn at the start of the next score, so this + * function moves them to be at count 0 of the next measure. + * However, if the staff involved is invisible on the next score, + * it doesn't move the STUFF. Also, fermatas and the pedal marks + * PEDAL and ENDPED are not moved. + * Only start.count is used; the policy is to apply start.steps + * only after everything else is done. + */ + +static void +normalizestuff() +{ + struct MAINLL *mainll_p; /* point along main linked list */ + struct MAINLL *m2_p, *m3_p; /* secondary & tertiary MLL pointers */ + struct STUFF *stuff_p; /* point at a STUFF structure */ + struct STUFF *next_p; /* point at the next STUFF structure */ + + + debug(16, "normalizestuff"); + initstructs(); + + /* + * Loop through the main linked list, looking for BARs that immediately + * precede a FEED. When found, adjust the start count of STUFFs in the + * preceding measure if necessary. + */ + for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { + + switch (mainll_p->str) { + case S_SSV: + /* keep SSVs up to date; we need to know time sig */ + asgnssv(mainll_p->u.ssv_p); + continue; + + case S_BAR: + /* break out to handle this BAR */ + break; + + default: + /* nothing to do, skip to next loop */ + continue; + } + + /* + * Find out if this is a bar that immediately precedes a FEED. + * If not, there is no need to adjust any stuff, so continue. + */ + 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 == 0 || m2_p->str == S_CHHEAD) + continue; + + /* + * This bar immediately precedes a FEED. Make sure the number + * of staffs doesn't change across this FEED. (And if what + * follows are blocks, continue looking until we find a FEED + * that is not a block.) If it does, don't move any STUFF. + */ + for (m2_p = mainll_p; m2_p->next != 0; m2_p = m2_p->next) { + /* break out if a FEED that is not followed by block */ + if (m2_p->str == S_FEED && + m2_p->next->str != S_BLOCKHEAD) + break; + /* break out if SSV changes number of staffs */ + if (m2_p->str == S_SSV && + m2_p->u.ssv_p->used[NUMSTAFF] == YES) + break; + } + /* if at end of list or staffs changed, don't move stuff */ + if (m2_p->next == 0 || m2_p->str == S_SSV) + continue; + + /* + * Search back to the CHHEAD of the preceding measure. + */ + for (m2_p = mainll_p; m2_p->str != S_CHHEAD; m2_p = m2_p->prev) + ; + + /* + * Loop through all the staffs in this preceding measure, + * adjusting STUFF when need be. + */ + for (m2_p = m2_p->next; m2_p->str == S_STAFF; + m2_p = m2_p->next) { + + if (m2_p->u.staff_p->visible == NO) + continue; + + /* + * Find the matching STAFF in the next measure. If we + * can't find it (like end of MLL) or it's not visible, + * forget this staff. + */ + for (m3_p = mainll_p->next; m3_p != 0 && + m3_p->str != S_BAR; m3_p = m3_p->next) { + if (m3_p->str == S_STAFF && + m3_p->u.staff_p->staffno == + m2_p->u.staff_p->staffno) + break; + } + if (m3_p == 0 || m3_p->str != S_STAFF || + m3_p->u.staff_p->visible == NO) + continue; + + /* + * Loop through all the stuff on this staff in the + * preceding measure, normalizing it. That is, if it + * starts at count N + 1 (where N is the numerator of + * the time signature), change it to start at count 0 + * of the next bar line. However, don't move fermatas + * or pedal "bounce" or "ending" marks. + */ + for (stuff_p = m2_p->u.staff_p->stuff_p; + stuff_p != 0; stuff_p = next_p) { + /* + * Remember next one in case we have to move + * this one to the next measure's linked list. + */ + next_p = stuff_p->next; + + if (stuff_p->start.count == Score.timenum + 1 && + string_is_sym(stuff_p->string, C_PEDAL, FONT_MUSIC) == NO && + string_is_sym(stuff_p->string, C_ENDPED, FONT_MUSIC) == NO && + string_is_sym(stuff_p->string, C_FERM, FONT_MUSIC) == NO && + string_is_sym(stuff_p->string, C_UFERM, FONT_MUSIC) == NO) { + /* + * Move this stuff from preceding to + * following measure's linked list. + */ + movestuff(m2_p->u.staff_p, + m3_p->u.staff_p, stuff_p); + /* + * Set start count to 0. If there is a + * "til" clause, bars would have to be + * greater than 0, since count can't + * refer to a count in this measure + * (can't be greater than N + 1). So + * we only need to check bars to see if + * there's a "til" clause. If there + * is, decrement it. + */ + stuff_p->start.count = 0; + if (stuff_p->end.bars > 0) + stuff_p->end.bars--; + } + } + } + } +} + +/* + * Name: movestuff() + * + * Abstract: Move a STUFF from one linked list to another. + * + * Returns: void + * + * Description: This function, given two staff pointers, finds the given STUFF + * in the first one's linked list of STUFF. It removes it from + * there and adds it to the start of the second staff's list. + */ + +static void +movestuff(staff1_p, staff2_p, stuff_p) + +struct STAFF *staff1_p; /* first STAFF */ +struct STAFF *staff2_p; /* second STAFF */ +struct STUFF *stuff_p; /* the STUFF to be moved */ + +{ + struct STUFF *s_p; /* loop pointer */ + + + debug(32, "movestuff file=%s line=%d", stuff_p->inputfile, + stuff_p->inputlineno); + if (staff1_p->stuff_p == stuff_p) { + /* + * This STUFF is the first one in the first STAFF's linked + * list. Change the headcell to point at the next one (which + * removes it from this list). This "next one" could be null. + */ + staff1_p->stuff_p = stuff_p->next; + } else { + /* + * Find which STUFF in the first STAFF's list points at the one + * we want to move. Make it point at the following one. + */ + for (s_p = staff1_p->stuff_p; s_p->next != stuff_p; + s_p = s_p->next) + ; + s_p->next = stuff_p->next; + } + + /* + * Make the STUFF we are moving point at what used to be the first + * one in the second STAFF's linked list. Make the second STAFF's + * headcell point at the STUFF we are moving. + */ + stuff_p->next = staff2_p->stuff_p; + staff2_p->stuff_p = stuff_p; +} + +/* + * Name: setphrases() + * + * Abstract: Find endpoints and direction of all phrase marks. + * + * Returns: void + * + * Description: This function loops through the main linked list and all STUFF, + * looking for phrase marks. For each phrase mark, it calls + * phrasestuff() to find out the horizontal positioning of the + * endpoints and whether the phrase mark should be above or below. + */ + +static void +setphrases() +{ + struct MAINLL *mainll_p; /* point along main linked list */ + struct STUFF *stuff_p; /* point at a STUFF structure */ + struct STUFF **phrasearray; /* malloc array of pointers to stuff */ + int numphrases; /* number of phrases in linked list */ + int n; /* loop through phrasearray */ + + + debug(16, "setphrases"); + initstructs(); + + for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { + + switch (mainll_p->str) { + case S_SSV: + /* keep SSVs up to date; we need to know time sig */ + asgnssv(mainll_p->u.ssv_p); + continue; + + case S_STAFF: + /* break out to handle this STAFF */ + break; + + default: + /* nothing to do, skip to next loop */ + continue; + } + + /* + * For each phrase in this staff's stuff list, we need to + * determine whether it should print 0, 1, or 2 phrase marks, + * and the direction and endpoints of each. We can't simply + * loop through the linked list, because the list gets altered + * by the subroutines we call, which can add and delete phrases + * from the list. So we copy the pointers to an array first. + */ + /* count how many phrases are in this staff's stuff list */ + numphrases = 0; + for (stuff_p = mainll_p->u.staff_p->stuff_p; stuff_p != 0; + stuff_p = stuff_p->next) { + if (stuff_p->stuff_type == ST_PHRASE) + numphrases++; + } + + /* if no phrases, nothing to do */ + if (numphrases == 0) + continue; + + /* allocate an array to hold pointer(s) to the phrase(s) */ + MALLOCA(struct STUFF *, phrasearray, numphrases); + + /* fill the array with pointers to each phrase */ + n = 0; + for (stuff_p = mainll_p->u.staff_p->stuff_p; stuff_p != 0; + stuff_p = stuff_p->next) { + if (stuff_p->stuff_type == ST_PHRASE) + phrasearray[n++] = stuff_p; + } + + /* find endpoints and direction of phrase(s) from each stuff */ + for (n = 0; n < numphrases; n++) { + phrasestuff(mainll_p, phrasearray[n]); + } + + FREE(phrasearray); + } +} + +/* + * Name: phrasestuff() + * + * Abstract: Find endpoints and direction of phrase(s) from one STUFF. + * + * Returns: void + * + * Description: This function decides whether the given phrase STUFF should + * try to generate one or two phrase marks above, below, or both. + * If it's both, it inserts a new STUFF in the list for it. Then + * it calls set1phrase() for each desired phrase to find endpoints + * and set the place if it's unknown. + */ + +static void +phrasestuff(msbeg_p, stuff_p) + +struct MAINLL *msbeg_p; /* point at main linked list where phrase starts */ +struct STUFF *stuff_p; /* point at STUFF structure for start of phrase */ + +{ + struct STUFF *stuff2_p; /* if we need to create a 2nd phrase */ + + + debug(32, "phrasestuff file=%s line=%d", stuff_p->inputfile, + stuff_p->inputlineno); + switch (svpath(msbeg_p->u.staff_p->staffno, VSCHEME)->vscheme) { + case V_1: /* one voice */ + /* do phrase mark for first (only) voice if possible */ + set1phrase(msbeg_p, stuff_p, 0); + break; + + case V_2FREESTEM: /* two voices that are free if one is space */ + case V_3FREESTEM: /* same as above; STUFF ignores voice 3 */ + if (chkallspace(msbeg_p, stuff_p, 0) == YES) { + /* first voice all spaces, apply phrase to second */ + set1phrase(msbeg_p, stuff_p, 1); + break; + } else if (chkallspace(msbeg_p, stuff_p, 1) == YES) { + /* second voice all spaces, apply phrase to first */ + set1phrase(msbeg_p, stuff_p, 0); + break; + } + /* FALL THROUGH to handle like V_2OPSTEM */ + + case V_2OPSTEM: /* two voices that always oppose */ + case V_3OPSTEM: /* same as above; STUFF ignores voice 3 */ + switch (stuff_p->place) { + + case PL_ABOVE: + /* phrase requested only for top voice */ + set1phrase(msbeg_p, stuff_p, 0); + break; + + case PL_BELOW: + /* phrase requested only for bottom voice */ + set1phrase(msbeg_p, stuff_p, 1); + break; + + default: + /* + * We're going to have two phrase marks, one above the + * first voice and one below the second voice. Let the + * current STUFF be for the top one, and create a new + * STUFF for the bottom one. + */ + stuff_p->place = PL_ABOVE; + stuff2_p = newSTUFF((char *)0, + stuff_p->dist, + stuff_p->dist_usage, + stuff_p->start.count, + stuff_p->start.steps, + stuff_p->gracebackup, + stuff_p->end.bars, stuff_p->end.count, + ST_PHRASE, NO, PL_BELOW, + stuff_p->inputfile, + stuff_p->inputlineno); + stuff2_p->next = stuff_p->next; + stuff_p->next = stuff2_p; + + set1phrase(msbeg_p, stuff_p, 0); + set1phrase(msbeg_p, stuff2_p, 1); + break; + } + break; + } +} + +/* + * Name: set1phrase() + * + * Abstract: Find endpoints and direction of one phrase. + * + * Returns: void + * + * Description: This function, given a STUFF for a single phrase mark and the + * voice it should apply to, finds the endpoint GRPSYLs for the + * phrase. If it can't find valid GRPSYLs, it removes the phrase + * and prints a warning. Otherwise, it also decides whether the + * phrase should be above or below, if that is not already known. + */ + +static void +set1phrase(msbeg_p, stuff_p, v) + +struct MAINLL *msbeg_p; /* point at MLL (staff) where phrase begins */ +struct STUFF *stuff_p; /* point at STUFF structure for start of phrase */ +int v; /* which voice to attach the phrase to */ + +{ + struct MAINLL *msend_p; /* MLL (staff) where phrase ends */ + struct MAINLL *mll_p; /* for looping through MLL */ + struct GRPSYL *beggrp_p;/* (eventually is) beginning of phrase */ + struct GRPSYL *endgrp_p;/* (eventually is) end of phrase */ + struct GRPSYL *gs_p; /* for looping through GRPSYL lists */ + int staffno; /* the staff this phrase is on */ + int timeden; /* denominator of time sig at end of phrase */ + int up, down; /* count stem directions */ + int n; /* loop variable */ + + + debug(32, "set1phrase file=%s line=%d v=%d", stuff_p->inputfile, + stuff_p->inputlineno, v); + stuff_p->vno = v + 1; /* translate voice numbers 0, 1 to 1, 2 */ + staffno = msbeg_p->u.staff_p->staffno; + + /* + * Find what measure this phrase mark ends in. Along the way, keep + * track of the time signature denominator, in case it changes. If + * getendstuff() returns 0, it means the phrase runs into a multirest, + * which is not allowed. If the phrase is supposed to be attached to + * the bottom voice, but it ends in a measure where the vscheme is 1, + * we must also throw it away. (This condition is signaled by the + * groups_p[v] being a null pointer.) + */ + msend_p = getendstuff(msbeg_p, stuff_p, &timeden); + + if (msend_p == 0) { + l_warning(stuff_p->inputfile, stuff_p->inputlineno, + "removing invalid phrase (runs into multirest)"); + rmstuff(msbeg_p->u.staff_p, stuff_p); + return; + } + if (msend_p->u.staff_p->groups_p[v] == 0) { + l_warning(stuff_p->inputfile, stuff_p->inputlineno, + "removing invalid phrase (covers no notes in last measure)"); + rmstuff(msbeg_p->u.staff_p, stuff_p); + return; + } + + /* + * Find the GRPSYLs that are closest, timewise, to the requested + * beginning and ending times of the phrase mark. + */ + beggrp_p = closestgroup(stuff_p->start.count, + msbeg_p->u.staff_p->groups_p[v], Score.timeden); + endgrp_p = closestgroup(stuff_p->end.count, + msend_p->u.staff_p->groups_p[v], timeden); + + /* + * It's possible that *beggrp_p is a rest or a space, but a phrase must + * start on a note group. So search forward, if necessary, to the first + * note group after this point. If there is none in this measure, this + * is an illegal phrase mark. Also, if the phrase begins and ends in + * the same measure, check that we don't go past the end in doing this + * search. A phrase is not allowed to begin and end at the same group. + * But the two groups can be the same if gracebackup is being used, + * because in that case the beginning group is really a grace group + * before the ending group. In any case, apply gracebackup if + * requested. + */ + while (beggrp_p != 0 && beggrp_p->grpcont != GC_NOTES) { + if (beggrp_p == endgrp_p) + break; + beggrp_p = nextnongrace(beggrp_p); + } + if (beggrp_p == 0) { + l_warning(stuff_p->inputfile, stuff_p->inputlineno, + "removing invalid phrase (covers no notes in first measure)"); + + rmstuff(msbeg_p->u.staff_p, stuff_p); + return; + } + if (beggrp_p == endgrp_p && stuff_p->gracebackup == 0) { + l_warning(stuff_p->inputfile, stuff_p->inputlineno, + "removing invalid phrase (need multiple notes)"); + + rmstuff(msbeg_p->u.staff_p, stuff_p); + return; + } + /* found a valid starting point; back up grace notes if requested */ + for (n = 0; n < stuff_p->gracebackup; n++) { + beggrp_p = beggrp_p->prev; + if (beggrp_p == 0 || beggrp_p->grpvalue != GV_ZERO) { + l_ufatal(stuff_p->inputfile, stuff_p->inputlineno, + "not enough grace groups to back up to for phrase"); + } + } + + /* + * Do the equivalent thing with the end of the phrase mark, except that + * we don't have to deal with gracebackup. + */ + while (endgrp_p != 0 && endgrp_p->grpcont != GC_NOTES && + beggrp_p != endgrp_p) { + endgrp_p = prevnongrace(endgrp_p); + } + if (beggrp_p == endgrp_p) { + l_warning(stuff_p->inputfile, stuff_p->inputlineno, + "removing invalid phrase (need multiple notes)"); + + rmstuff(msbeg_p->u.staff_p, stuff_p); + return; + } + if (endgrp_p == 0) { + l_warning(stuff_p->inputfile, stuff_p->inputlineno, + "removing invalid phrase (covers no notes in last measure)"); + + rmstuff(msbeg_p->u.staff_p, stuff_p); + return; + } + + /* + * We have now determined the correct beginning and ending GRPSYLs for + * the phrase. Store them for later use. + */ + stuff_p->beggrp_p = beggrp_p; + stuff_p->endgrp_p = endgrp_p; + + /* + * If we don't know yet whether the phrase should be drawn above or + * below the staff, decide that now. We will put it on the side that + * has more note heads than stems. So loop through all GRPSYLs within + * this phrase, counting. Ignore grace groups. + */ + if (stuff_p->place == PL_UNKNOWN) { + up = down = 0; + mll_p = msbeg_p; + gs_p = beggrp_p; + if (gs_p->grpvalue == GV_ZERO) + gs_p = nextnongrace(gs_p); + while (gs_p != endgrp_p) { + if (gs_p->grpcont == GC_NOTES) { + if (gs_p->stemdir == UP) + up++; + else + down++; + } + + gs_p = nextnongrace(gs_p); + + /* if we hit the end of a measure, find start of next */ + if (gs_p == 0) { + for (mll_p = mll_p->next; + mll_p->str != S_STAFF || + mll_p->u.staff_p->staffno != staffno; + mll_p = mll_p->next) + ; + gs_p = mll_p->u.staff_p->groups_p[v]; + } + } + /* count final group in phrase (must be GC_NOTES) */ + if (gs_p->stemdir == UP) + up++; + else + down++; + + /* + * Put phrase opposite the majority of the stems. If it's a + * tie, base it on the first group. + */ + if (up > down) + stuff_p->place = PL_BELOW; + else if (up < down) + stuff_p->place = PL_ABOVE; + else if (beggrp_p->stemdir == UP) + stuff_p->place = PL_BELOW; + else + stuff_p->place = PL_ABOVE; + } +} + +/* + * Name: rmstuff() + * + * Abstract: Remove a STUFF from a linked list, and free it. + * + * Returns: void + * + * Description: This function removes the given STUFF from the linked list + * hanging off the given staff, and frees it. + */ + +static void +rmstuff(staff_p, stuff_p) + +struct STAFF *staff_p; /* the staff that the stuff hangs off of */ +struct STUFF *stuff_p; /* the stuff to be removed */ + +{ + struct STUFF *stuff2_p; /* point along STUFF list */ + + + debug(32, "rmstuff file=%s line=%d", stuff_p->inputfile, + stuff_p->inputlineno); + if (staff_p->stuff_p == stuff_p) { + /* the given stuff is the first one in the list */ + staff_p->stuff_p = stuff_p->next; + } else { + /* find which stuff in list points at the one to be removed */ + for (stuff2_p = staff_p->stuff_p; stuff2_p->next != stuff_p; + stuff2_p = stuff2_p->next) + ; + stuff2_p->next = stuff_p->next; + } + + FREE(stuff_p); +} + +/* + * Name: breakstuff() + * + * Abstract: Break all "stuff" that crosses scorefeeds. + * + * Returns: void + * + * Description: This function loops through the main linked list and all STUFF, + * looking for stuff that crosses score feeds. When it finds such + * stuff, it breaks them at that point. + */ + +static void +breakstuff() +{ + struct MAINLL *mainll_p; /* point along main linked list */ + struct STUFF *stuff_p; /* point at a STUFF structure */ + + + debug(16, "breakstuff"); + initstructs(); + + for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { + + switch (mainll_p->str) { + case S_SSV: + /* keep SSVs up to date; we need to know time sig */ + asgnssv(mainll_p->u.ssv_p); + continue; + + case S_STAFF: + /* break out to handle this STAFF */ + break; + + default: + /* nothing to do, skip to next loop */ + continue; + } + + /* + * Loop through all the stuff on this staff. Do it in reverse + * order so that when stuff gets broken, the new STUFFs end up + * in the correct order. + */ + if (mainll_p->u.staff_p->stuff_p != 0) { + + stuff_p = mainll_p->u.staff_p->stuff_p; + while (stuff_p->next != 0) + stuff_p = stuff_p->next; + /* now stuff_p points at the last one in the list */ + + for ( ; stuff_p != 0; stuff_p = prevstuff(mainll_p, + stuff_p)) { + /* + * If there's a "til" clause, and it crosses + * bar lines, call a function to see if it + * needs to be broken, and if so, break it. + */ + if (stuff_p->end.bars > 0) + breakone(mainll_p, stuff_p, + Score.timenum, + stuff_p->string, 1); + } + } + } +} + +/* + * Name: prevstuff() + * + * Abstract: Find stuff preceding the given one. + * + * Returns: pointer to previous stuff, or 0 if none + * + * Description: This function is given a pointer to a staff mainll item and a + * stuff in that list. It finds the preceding stuff, returning + * it, or 0 if none. If stuff linked lists were doubly linked, + * we wouldn't have to go through this aggravation. + */ + +static struct STUFF * +prevstuff(mainll_p, stuff_p) + +struct MAINLL *mainll_p; /* ptr to MLL item for a stuff */ +struct STUFF *stuff_p; /* ptr to current stuff */ + +{ + register struct STUFF *prevstuff_p; + + + prevstuff_p = mainll_p->u.staff_p->stuff_p; /* get 1st stuff in list */ + + /* if current stuff is first stuff, there is none before it */ + if (prevstuff_p == stuff_p) + return (0); + + /* loop until we find it, then return */ + while (prevstuff_p->next != stuff_p) + prevstuff_p = prevstuff_p->next; + return (prevstuff_p); +} + +/* + * Name: breakone() + * + * Abstract: Break one "stuff" item if it crosses scorefeeds. + * + * Returns: void + * + * Description: This function is given a STUFF with a "til" clause that crosses + * bar line(s). It finds out if the STUFF crosses the FEED at the + * end of the score. If so, it stops the current stuff there, and + * starts a continuation on the next score. If there are more bar + * lines yet to be crossed, it calls itself recursively. + */ + +static void +breakone(mllstaff_p, stuff_p, timenum, origstr_p, depth) + +struct MAINLL *mllstaff_p; /* point at MLL struct holding stuff's staff */ +struct STUFF *stuff_p; /* point at a STUFF structure */ +int timenum; /* numerator of current time sig */ +char *origstr_p; /* original string, from first score of stuff */ +int depth; /* depth of recursion */ + +{ + /* + * Make the local variables static, to use less stack when making + * recursive calls. We can get away with this because the recursive + * call is at the end of the function, and the variables aren't used + * after that. + */ + static struct MAINLL *m2_p; /* point along main linked list */ + static struct MAINLL *m3_p; /* point along main linked list */ + static struct MAINLL *m4_p; /* point along main linked list */ + static struct STUFF *s_p; /* point at a new STUFF */ + static struct GRPSYL *gs_p; /* point along a GRPSYL list */ + static int bars; /* count how many bars long it is */ + static int otimenum; /* num of time sig before last bar */ + static float endcount; /* count where the stuff ends */ + + + debug(32, "breakone file=%s line=%d timenum=%d origstr_p=\"%s\"", + stuff_p->inputfile, stuff_p->inputlineno, timenum, + origstr_p == 0 ? "" : origstr_p); + /* + * Save how many bars this stuff crosses (initially, before any + * breaking), and the count where it ends. + */ + bars = stuff_p->end.bars; + endcount = stuff_p->end.count; + + /* + * Loop forward until crossing the given number of bars, or hitting a + * FEED, whichever comes first. Keep the timenum updated. At each + * bar line, save what it was before in otimenum. + */ + for (m2_p = mllstaff_p; bars >= 0 && m2_p != 0 && m2_p->str != S_FEED; + m2_p = m2_p->next) { + + switch (m2_p->str) { + case S_SSV: + if (m2_p->u.ssv_p->used[TIME] == YES) + timenum = m2_p->u.ssv_p->timenum; + break; + + case S_BAR: + otimenum = timenum; + bars--; + break; + + case S_STAFF: + /* + * If this is the first staff of a measure, check the + * first voice to see if it's a multirest. (If it is, + * all voices on all staffs would be.) If so, decrement + * the bars remaining appropriately. It could end up + * less than 0. That's okay; we won't be breaking it. + */ + if (m2_p->u.staff_p->staffno == 1 && + m2_p->u.staff_p->groups_p[0]->basictime < -1) { + + bars += 1 + m2_p->u.staff_p-> + groups_p[0]->basictime; + } + break; + } + } + + /* + * If bars <= -1, this stuff doesn't reach the end of this score, so + * just return. If we hit the end of the piece, end the stuff at the + * last count of the last measure (thus the last bar line). The + * parser has already put out a warning for this. + * If the stuff ends at the pseudobar of the next score, make it end at + * the last bar of this score instead. However, phrases should not be + * adjusted this way, because under these conditions they will get + * pulled to the note at count 1. + */ + if (bars <= -1) + return; + if (m2_p == 0 || bars == 0 && stuff_p->end.count == 0 && + stuff_p->stuff_type != ST_PHRASE) { + stuff_p->end.bars -= bars + 1; + stuff_p->end.count = timenum + 1; + return; + } + + /* + * At this point we know that the stuff truly crosses the FEED we're + * at, and we're going to have to break it. First, terminate the part + * of the stuff on this score and mark the carryout. (However, if the + * next staff starts with a multirest that fully contains the rest of + * the stuff, we'll cancel the carryout later and not insert a new + * stuff for continuation.) + */ + stuff_p->end.bars -= bars + 1; /* only cross this many bars */ + stuff_p->end.count = otimenum + 1; /* end at end of last measure*/ + stuff_p->carryout = YES; + + /* + * Find the matching STAFF in the next measure. It might not exist + * due to blocks. + */ + for (m3_p = m2_p->next; m3_p != 0 && m3_p->str != S_BAR; + m3_p = m3_p->next) { + if (m3_p->str == S_STAFF && m3_p->u.staff_p->staffno == + mllstaff_p->u.staff_p->staffno) + break; + } + if (m3_p == 0) { + return; + } + if (m3_p->str != S_STAFF) + pfatal("can't find staff in main linked list [breakone]"); + + /* + * If the staff has a multirest that's longer than the remaining + * length of the stuff, don't insert a new stuff; just get out now. + */ + if (m3_p->u.staff_p->groups_p[0]->basictime < -1) { + if (bars < -(m3_p->u.staff_p->groups_p[0]->basictime)) { + stuff_p->carryout = NO; /* cancel the carryout */ + return; + } + } + + /* + * Create a new STUFF starting at count 0 of the new measure. Link it + * in to its STAFF and set fields appropriately. + */ + s_p = newSTUFF(origstr_p, stuff_p->dist, stuff_p->dist_usage, + (double)0, (double)0, (short)0, bars, endcount, + stuff_p->stuff_type, stuff_p->modifier, stuff_p->place, + stuff_p->inputfile, stuff_p->inputlineno); + s_p->next = m3_p->u.staff_p->stuff_p; + m3_p->u.staff_p->stuff_p = s_p; + s_p->carryin = YES; + s_p->vno = stuff_p->vno; /* actually only needed for phrases */ + + switch (s_p->stuff_type) { + case ST_ROM: + case ST_BOLD: + case ST_ITAL: + case ST_BOLDITAL: + /* + * If this staff was visible on the previous score, we just + * want the continuation dashes on this score, not the original + * string, so we change the string in that case. (If the staff + * is also invisible on this score, we don't care what happens, + * so don't even check for that.) Otherwise, leave it alone. + * Also, all other types that have strings should always repeat + * them on each score, so leave them alone. + */ + if (mllstaff_p->u.staff_p->visible == YES) + s_p->string = dashstr(stuff_p->string); + break; + case ST_PHRASE: + /* + * The new (2nd half of the) phrase mark has as its first group + * its voice's first GRPSYL in the new score. Its last group + * is the last GRPSYL of the entire phrase. The last group of + * the old (1st half of the) phrase mark is the last GRPSYL on + * that score for this voice. + */ + s_p->beggrp_p = m3_p->u.staff_p->groups_p[ stuff_p->vno - 1 ]; + s_p->endgrp_p = stuff_p->endgrp_p; + /* find matching staff in the measure before the FEED */ + for (m4_p = m2_p; m4_p->str != S_STAFF || + m4_p->u.staff_p->staffno != + mllstaff_p->u.staff_p->staffno; + m4_p = m4_p->prev) + ; + /* find last group in the measure in that voice */ + for (gs_p = m4_p->u.staff_p->groups_p[ stuff_p->vno - 1 ]; + gs_p->next != 0; gs_p = gs_p->next) + ; + stuff_p->endgrp_p = gs_p; + break; + } + + /* + * If there are still more bars to be crossed, call ourselves + * recursively to break the stuff again if necessary. But first check + * to see we aren't too deep in recursion, using too much stack. + */ + if (s_p->end.bars > 0) { + if (depth >= 300) + l_ufatal(stuff_p->inputfile, stuff_p->inputlineno, + "stuff crosses more than 300 scores (break into shorter pieces)"); + breakone(m3_p, s_p, timenum, origstr_p, depth + 1); + } +} + +/* + * Name: contpedal() + * + * Abstract: Insert pedal continuation STUFFs when needed. + * + * Returns: void + * + * Description: When a pedal is to be held down through a score feed, the + * print phase needs to know where (vertically) to draw the pedal + * line on the new score. There might not occur another pedal + * character in the first measure there, or even on that whole + * score. So whenever this happens, regardless of whether there + * are already pedal characters on the new score, this function + * inserts a new STUFF with a special, null pedal indication, at + * count 0 of the first measure of the new score. + */ + +static void +contpedal() + +{ + short inpedal[MAXSTAFFS + 1]; /* does this staff have pedal on now?*/ + short snappedal[MAXSTAFFS + 1]; /* same, when an ending is entered */ + struct MAINLL *mainll_p; /* point along main linked list */ + struct MAINLL *m2_p; /* another pointer along MLL */ + struct STUFF *stuff_p; /* point at a STUFF structure */ + struct STUFF *stuff2_p; /* point at new STUFF structure */ + struct BAR *bar_p; /* point at a bar line */ + int inending; /* are we now in an ending? */ + int s; /* staff number */ + + + debug(16, "contpedal"); + /* init pedal state to NO for all staffs */ + for (s = 1; s <= MAXSTAFFS; s++) + inpedal[s] = NO; + + inending = NO; + + /* + * Loop through main linked list. For each STAFF found, update pedal + * state when necessary. For each FEED found, if there's a pedal + * crossing it, insert a pedal continuation. There is special work to + * do when endings are encountered, since this can change pedal states. + */ + for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { + + switch (mainll_p->str) { + + case S_STAFF: + /* + * Loop through all the stuff on this staff. Whenever + * there's a pedal mark, update the state for that + * staff, except that pedal up/down is irrelevant. + */ + for (stuff_p = mainll_p->u.staff_p->stuff_p; + stuff_p != 0; stuff_p = stuff_p->next) { + + if (stuff_p->stuff_type != ST_PEDAL) + continue; + + if (string_is_sym(stuff_p->string, + C_BEGPED, FONT_MUSIC) == YES) + inpedal[mainll_p->u.staff_p-> + staffno] = YES; + + else if (string_is_sym(stuff_p->string, + C_ENDPED, FONT_MUSIC) == YES) + inpedal[mainll_p->u.staff_p-> + staffno] = NO; + } + break; + + case S_BAR: + /* + * When entering a second or later ending, the state of + * all pedals must be set back to what they were when + * entering the first ending. + * First deal with the case where an ending starts at + * the pseudobar after a feed. In that case, we need + * to ignore any endending on the previous real bar. + */ + bar_p = mainll_p->u.bar_p; + for (m2_p = mainll_p->next; m2_p != 0 && + m2_p->str != S_BAR && + (m2_p->str != S_CLEFSIG || + m2_p->u.clefsig_p->bar_p == 0); + m2_p = m2_p->next) + ; + if (m2_p != 0 && m2_p->str == S_CLEFSIG) { + /* + * If an ending starts at this pseudobar, + * substitute this pseudobar for the real one. + */ + if (m2_p->u.clefsig_p->bar_p->endingloc + == STARTITEM) + bar_p = m2_p->u.clefsig_p->bar_p; + } + + switch (bar_p->endingloc) { + case STARTITEM: + if (inending == NO) { + /* entering first ending */ + /* snapshot pedal states here */ + for (s = 1; s <= MAXSTAFFS; s++) + snappedal[s] = inpedal[s]; + inending = YES; + } else { + /* entering a later ending */ + /* restore snapshotted state */ + for (s = 1; s <= MAXSTAFFS; s++) + inpedal[s] = snappedal[s]; + } + break; + + case ENDITEM: + /* leaving the last ending */ + inending = NO; + break; + } + break; + + case S_FEED: + /* only consider FEEDs that are before music */ + if ( ! IS_CLEFSIG_FEED(mainll_p)) { + break; + } + + /* + * For every staff where the pedal is now down, find + * the structure for that staff in the first measure, + * and insert a STUFF to indicate the continuation of + * the pedal mark line. + */ + for (s = 1; s <= MAXSTAFFS; s++) { + + if (inpedal[s] == NO) + continue; + + for (m2_p = mainll_p; m2_p != 0 && + (m2_p->str != S_STAFF || + m2_p->u.staff_p->staffno != s); + m2_p = m2_p->next) + ; + if (m2_p == 0) + pfatal("couldn't find staff in contpedal"); + /* pedal continuation at count 0, w/ null str */ + stuff2_p = newSTUFF((char *)0, 0, SD_NONE, + (double)0, (double)0, (short)0, 0, + (double)0, ST_PEDAL, NO, PL_BELOW, + (char *)0, -1); + + /* put at beginning of staff's linked list */ + stuff2_p->next = m2_p->u.staff_p->stuff_p; + m2_p->u.staff_p->stuff_p = stuff2_p; + } + break; + } + } +} + +/* + * Name: setstuff() + * + * Abstract: Set horizontal absolute coordinates of "stuff". + * + * Returns: void + * + * Description: This function loops through the main linked list. For every + * visible staff, it loops through its list of STUFF structures, + * setting all their absolute horizontal coordinates. + */ + +static void +setstuff() + +{ + struct MAINLL *mainll_p; /* point along main linked list */ + struct CHHEAD *chhead_p; /* chord headcell of current measure */ + struct BAR *bar_p; /* [pseudo] bar before current meas */ + struct STUFF *stuff_p; /* point at a STUFF structure */ + struct MAINLL *m2_p; /* MLL for end of stuff */ + short timeden2; /* time sig denom at end of stuff */ + struct CHHEAD *chhead2_p; /* chord headcell for end of stuff */ + struct BAR *bar2_p; /* bar line for end of stuff */ + struct GRPSYL *gs_p; /* used for gracebackup */ + float stepsize; /* STEPSIZE scaled by staffscale */ + float leftwid; /* width of string left of align pt. */ + float streast; /* east end of a string */ + int ret; /* return code from geteast */ + int v; /* voice associated with */ + int n; /* loop variable */ + + + debug(16, "setstuff"); + initstructs(); + + chhead_p = 0; /* prevent useless 'used before set' warning */ + bar_p = 0; /* prevent useless 'used before set' warning */ + for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { + /* + * Do various set up work per structure type. If it's a + * visible staff, break and go do the rest of this loop. + * Otherwise, "continue" on to the next structure. + */ + switch (mainll_p->str) { + case S_SSV: + /* keep SSVs up to date; we need to know timeden */ + asgnssv(mainll_p->u.ssv_p); + continue; + + case S_CHHEAD: + /* always remember preceding chord headcell */ + chhead_p = mainll_p->u.chhead_p; + continue; + + case S_BAR: + /* always remember where the preceding bar was */ + bar_p = mainll_p->u.bar_p; + continue; + + case S_CLEFSIG: + /* + * The pseudo bar that's in a clefsig following a + * scorefeed overrides the real bar at the end of the + * previous score. + */ + if (mainll_p->u.clefsig_p->bar_p != 0) + bar_p = mainll_p->u.clefsig_p->bar_p; + continue; + + case S_STAFF: + /* visible staff breaks out to do rest of the loop */ + if (mainll_p->u.staff_p->visible == YES) + break; + continue; + + default: + continue; + } + + /* get stepsize for this staff */ + stepsize = STEPSIZE * svpath(mainll_p->u.staff_p->staffno, + STAFFSCALE)->staffscale; + + /* + * Loop through all the stuff on this staff. + */ + for (stuff_p = mainll_p->u.staff_p->stuff_p; stuff_p != 0; + stuff_p = stuff_p->next) { + /* + * Handle the special case of a fermata on a measure + * rest. No matter what count the user requested, + * we're going to center it above or below the measure + * rest character. We also assume there is no "til" + * clause. + */ + if (string_is_sym(stuff_p->string, C_FERM, FONT_MUSIC) == YES || + string_is_sym(stuff_p->string, C_UFERM, FONT_MUSIC) == YES) { + + if (setmrferm(mainll_p->u.staff_p, stuff_p) + == YES) + continue; + /* + * The fermata is not on a measure rest. Fall + * through to handle like any normal stuff. + */ + } + + /* + * Set c[AX] for the stuff. + */ + if (stuff_p->gracebackup == 0) { + /* + * Use the c[INCHPERWHOLE] coordinate of the + * chord or bar line that is at or before its + * "start". + */ + stuff_p->c[AX] = count2coord( + stuff_p->start.count, bar_p, chhead_p, + Score.timeden) + + stuff_p->start.steps * + STUFFSTEP(stuff_p); + } else { + /* + * Since we have to back up by some number of + * grace notes, we have to find a group to + * associate the STUFF with. Then we can back + * up from there. + */ + v = 0; /* avoid 'used before set' warning */ + switch (svpath(mainll_p->u.staff_p->staffno, + VSCHEME)->vscheme) { + case V_1: /* 1 */ + v = 0; /* only one voice, use it */ + break; + case V_2FREESTEM: /* 2f */ + case V_3FREESTEM: /* 3f */ + if (chkallspace(mainll_p, stuff_p, 0) + == YES) { + /* first voice all spaces */ + /* apply stuff to second */ + v = 1; + break; + } else if (chkallspace(mainll_p, + stuff_p, 1) == YES) { + /* second voice all spaces */ + /* apply stuff to first */ + v = 0; + break; + } + /* FALL THROUGH;handle like V_2OPSTEM */ + case V_2OPSTEM: /* 2o */ + case V_3OPSTEM: /* 3o */ + v = stuff_p->place == PL_ABOVE ? 0 : 1; + break; + } + + /* find closest note group in this voice */ + gs_p = closestgroup(stuff_p->start.count, + mainll_p->u.staff_p->groups_p[v], + Score.timeden); + + /* back up right number of grace groups */ + for (n = 0; n < stuff_p->gracebackup; n++) { + gs_p = gs_p->prev; + if (gs_p == 0 || gs_p->grpvalue + != GV_ZERO) + l_ufatal(stuff_p->inputfile, + stuff_p->inputlineno, + "not enough grace groups to back up to for stuff"); + } + + /* + * Put the stuff at this grace group, except + * that the steps offset must be applied. + */ + stuff_p->c[AX] = gs_p->c[AX] + + stuff_p->start.steps * + STUFFSTEP(stuff_p); + } + + /* + * If this stuff has a "til" clause, find where the + * ending point is and set c[AE] to that. If this + * stuff also has a string, and the string extends + * beyond the point that the "til" clause asks for, + * c[AE] will be changed later to agree with the string. + */ + if (stuff_p->end.bars != 0 || stuff_p->end.count != 0) { + /* + * Init variables to the current position + * (beginning of the stuff). If the stuff is + * supposed to extend across one or more bar + * lines, geteast() will alter them to the + * correct values for the bar where the stuff + * ends. It will blow away the 'til' clause in + * certain multirest scenarios. + */ + m2_p = mainll_p; /* this stuff's staff*/ + timeden2 = Score.timeden; + chhead2_p = chhead_p; + bar2_p = bar_p; + + ret = geteast(stuff_p, m2_p, &timeden2, + &chhead2_p, &bar2_p); + + switch (ret) { + case 1: + /* + * Set c[AE] for the stuff, using the + * c[INCHPERWHOLE] coordinate of the + * chord or bar line that precedes or + * is at the end of it. + */ + stuff_p->c[AE] = count2coord( + stuff_p->end.count, + bar2_p, chhead2_p, timeden2); + break; + case 2: + /* end the stuff a little beyond the */ + /* barline at the start of the stuff */ + stuff_p->c[AE] = bar2_p->c[AX] + + STUFFSTEP(stuff_p); + break; + case 3: + /* no "til" clause anymore */ + /* set to 0 temporarily */ + stuff_p->c[AE] = 0; + break; + } + /* + * If there is still a "til" clause (it wasn't + * blown away), make sure AE isn't left of AX. + * Although the parse phase checks the bars and + * counts for this, it could still happen here + * if start.steps is too big. If it happens, + * warn, and set AE to AX. + */ + if (stuff_p->end.bars != 0 || + stuff_p->end.count != 0) { + if (stuff_p->c[AE] < stuff_p->c[AX]) { + l_warning(stuff_p->inputfile, + stuff_p->inputlineno, + "mark begins after 'til' position; moving 'til' position to the right"); + stuff_p->c[AE] = stuff_p->c[AX]; + } + } + } else { + /* no "til" clause; set to 0 temporarily */ + stuff_p->c[AE] = 0; + } + + /* + * Set c[AW] for all stuff types, and c[AE] for ones + * that don't have a "til" clause. Reset c[AE] for + * ones that have a "til" clause, if they have a string + * that sticks out farther than that. + */ + switch (stuff_p->stuff_type) { + case ST_ROM: + case ST_BOLD: + case ST_ITAL: + case ST_BOLDITAL: + case ST_MUSSYM: + case ST_PEDAL: + case ST_OCTAVE: + /* + * These types (and only these) have a string. + * (However, pedal continuations don't, so + * treat them specially.) Set c[AW] such that + * the first character will be centered at + * c[AX]. + */ + if (stuff_p->string == 0) { + /* must be a pedal continuation; */ + /* these have no width */ + stuff_p->c[AW] = stuff_p->c[AE] = + stuff_p->c[AX]; + break; + } + + /* not a pedal continuation */ + + /* + * If this is a chord with a grid, handle it + * all in the subroutine. + */ + if (stuff_p->modifier == TM_CHORD && trygrid( + mainll_p, stuff_p) == YES) { + break; + } + + leftwid = left_width(stuff_p->string); + stuff_p->c[AW] = stuff_p->c[AX] - leftwid; + + /* find the initial east of the string */ + streast = stuff_p->c[AW] + + strwidth(stuff_p->string); + /* + * If the string would go beyond the right + * margin, try to split it onto multiple lines, + * and reset the string's east. Don't do this + * for chord/analysis/figbass. + */ + if (streast > PGWIDTH - + eff_rightmargin(mainll_p) && + ! IS_CHORDLIKE(stuff_p->modifier)) { + stuff_p->string = split_string( + stuff_p->string, PGWIDTH - + eff_rightmargin(mainll_p) - + stuff_p->c[AW]); + streast = stuff_p->c[AW] + + strwidth(stuff_p->string); + } + + /* + * If this east is beyond the east given by the + * "til" clause (if any), use it for the AE. + */ + if (streast > stuff_p->c[AE]) + stuff_p->c[AE] = streast; + + break; + + case ST_CRESC: + case ST_DECRESC: + case ST_PHRASE: + case ST_TIESLUR: /* due to carry-in into ending */ + case ST_TABSLUR: /* due to carry-in into ending */ + case ST_BEND: /* due to carry-in into ending */ + /* + * These types (and only these) have no string. + * Set c[AW] the same as c[AX]. These always + * have "til" clauses, so c[AE] is already set. + * (Actually, c[AE] is not set for TIESLUR, + * TABSLUR, or BEND, but no one uses it anyhow.) + */ + stuff_p->c[AW] = stuff_p->c[AX]; + break; + + case ST_MIDI: + /* this file doesn't deal with MIDI at all */ + break; + + default: + pfatal("unknown stuff type %d", + stuff_p->stuff_type); + } + } + } +} + +/* + * Name: count2coord() + * + * Abstract: Convert a count in a measure to the absolute horizontal coord. + * + * Returns: coordinate corresponding to the given count + * + * Description: This function, given a count number in a measure, and the + * preceding bar line, chord head cell, and time signature + * denominator, calculates and returns the absolute horizonal + * coordinate of that place. In a measure, the preceding bar line + * (or pseudo bar line, if this is the first measure of a score) + * is regarded as count 0, and the following bar line is regarded + * as count N + 1, where N is the numerator of the time signature. + * Between any adjacent chords, or chord and bar line, time is + * treated as proportional to distance. + */ + +static double +count2coord(count, bar_p, chhead_p, timeden) + +double count; /* count in measure */ +struct BAR *bar_p; /* bar at start of measure (pseudobar if 1st in score)*/ +struct CHHEAD *chhead_p;/* chord headcell for this measure */ +int timeden; /* denominator of time signature in this measure */ + +{ + struct CHORD *ch_p, *nch_p; /* point at CHORD structures */ + float frac; /* what fraction of a whole it starts at */ + float coord; /* the answer */ + + + if (count < 1) { + /* + * The stuff is before the first chord ("count 1"). So we + * consider the preceding bar line to be count 0, and allocate + * time proportionally. + */ + coord = bar_p->c[AX] + (count / timeden) * + bar_p->c[INCHPERWHOLE]; + } else { + /* + * Convert the "count" where this stuff begins to what fraction + * of a whole it starts at. + */ + frac = (count - 1) / timeden; + + /* + * In this loop, ch_p starts at the first chord and nch_p is + * the next chord. They move forward in parallel. We get out + * either when nch_p is 0 (ch_p is the last chord), or when + * nch_p is beyond the place where our stuff goes (the stuff + * goes at or following ch_p). + */ + for (ch_p = chhead_p->ch_p, nch_p = ch_p->ch_p; + nch_p != 0 && RAT2FLOAT(nch_p->starttime) <= frac; + ch_p = nch_p, nch_p = nch_p->ch_p) + ; + + /* + * Subtract to find how far (timewise) the stuff is after chord + * ch_p. Then allocate space proportionally. + */ + coord = ch_p->c[AX] + (frac - RAT2FLOAT(ch_p->starttime)) * + ch_p->c[INCHPERWHOLE]; + } + + return (coord); +} + +/* + * Name: geteast() + * + * Abstract: Point variables at things relevant to the east end of a STUFF. + * + * Returns: 1 if count2coord() should be used to find AE of stuff + * 2 if AE of stuff should be near *bar_p_p due to multirest + * 3 if 'til' clause is being blown away due to multirest + * + * Description: This function is given pointers pertaining to a measure where + * a STUFF begins. It searches forward to the end of the stuff, + * according to the given number of measures. It sets *timeden2_p, + * *chhead2_p_p, and *bar2_p_p to the values they should be for + * the end of the stuff, if the return code is 1 (the usual case). + * For 2 and 3, only *bar2_p_p is guaranteed to be meaningful. + */ + +static int +geteast(stuff_p, m2_p, timeden2_p, chhead2_p_p, bar2_p_p) + +struct STUFF *stuff_p; /* pointer to the stuff */ +struct MAINLL *m2_p; /* starts at start of stuff, change to end */ +short *timeden2_p; /* starts at start of stuff, change to end */ +struct CHHEAD **chhead2_p_p; /* starts at start of stuff, change to end */ +struct BAR **bar2_p_p; /* starts at start of stuff, change to end */ + +{ + int blimit; /* number of bars to search forward past */ + int timenum; /* numerator of time signature */ + int b; /* count bar lines */ + + + blimit = stuff_p->end.bars; /* number of bars to cross */ + + /* if all within one bar, return right away */ + if (blimit == 0) { + /* if starts and ends inside multirest, blow away 'til' clause*/ + if (m2_p->u.staff_p->groups_p[0]->basictime < -1) { + stuff_p->end.count = 0; + return (3); + } + + return (1); /* normal case; retain 'til' clause */ + } + + timenum = Score.timeden; /* keep track of time sig numerator */ + + /* + * The input parameters point at values that are for the beginning of + * the stuff. Loop forward the requested number of bars to get to the + * end of the stuff. While doing this, keep those variables updated. + * By the end of this function, they will be correct for the end of + * the stuff. + */ + b = 0; + /* if it starts at a multirest, account for that */ + if (m2_p->u.staff_p->groups_p[0]->basictime < -1) { + /* if it ends inside the multirest, point at previous bar, */ + /* blow away 'til' clause, and get out */ + if (-(m2_p->u.staff_p->groups_p[0]->basictime) > blimit) { + *bar2_p_p = m2_p->u.bar_p; + stuff_p->end.bars = 0; + stuff_p->end.count = 0; + return (3); + } + + b = -1 - m2_p->u.staff_p->groups_p[0]->basictime; + } + for ( ; b < blimit; b++) { + for (m2_p = m2_p->next; m2_p != 0 && m2_p->str != S_BAR; + m2_p = m2_p->next) { + + switch (m2_p->str) { + case S_SSV: + if (m2_p->u.ssv_p->used[TIME] == YES) { + timenum = m2_p->u.ssv_p->timenum; + *timeden2_p = m2_p->u.ssv_p->timeden; + } + break; + + case S_STAFF: + /* multirests count as multiple measures */ + if (m2_p->u.staff_p->staffno == 1 && + m2_p->u.staff_p->groups_p[0]->basictime < -1) { + + /* add (multi - 1) to no. of bars */ + b -= 1 + m2_p->u.staff_p-> + groups_p[0]->basictime; + + /* + * If the stuff doesn't make it into + * the last measure, or doesn't make it + * to the last measure's bar line, make + * it stop at the bar before multirest. + */ + if (b > blimit || b == blimit && + stuff_p->end.count < timenum + 1) { + return (2); + } + + /* + * If it ends at the bar after the + * multirest, move forward to that bar + * and end the stuff there. + */ + if (b == blimit) { + while (m2_p->str != S_BAR) + m2_p = m2_p->next; + *bar2_p_p = m2_p->u.bar_p; + return (2); + } + } + break; + } + } + + if (m2_p == 0) + pfatal("'til' clause extends beyond end of the piece [geteast1]"); + + *bar2_p_p = m2_p->u.bar_p; + } + + /* + * m2_p points at the bar line preceding or at the place where the + * stuff ends. Continue forward to be pointing at the CHHEAD of that + * measure and also do final variable updates. + */ + for ( ; m2_p != 0 && m2_p->str != S_CHHEAD; + m2_p = m2_p->next) { + + switch (m2_p->str) { + case S_SSV: + if (m2_p->u.ssv_p->used[TIME] == YES) + *timeden2_p = m2_p->u.ssv_p->timeden; + break; + case S_CLEFSIG: + if (m2_p->u.clefsig_p->bar_p != 0) + *bar2_p_p = m2_p->u.clefsig_p->bar_p; + break; + } + } + + if (m2_p == 0) + pfatal("'til' clause extends beyond end of the piece [geteast2]"); + + /* + * If the first staff in this measure has a multirest, return that fact + * and have the stuff end at this bar line. + */ + if (m2_p->next->u.staff_p->groups_p[0]->basictime < -1) + return (2); + + *chhead2_p_p = m2_p->u.chhead_p; + + return (1); /* didn't end during a multirest */ +} + +/* + * Name: setmrferm() + * + * Abstract: Set absolute horizonal coords of a fermata on measure rest/rpt. + * + * Returns: YES or NO + * + * Description: This function is given a staff and a stuff for a fermata there. + * It decides whether the fermata applies to a measure rest or a + * measure repeat. If it does, it sets its coords to align with + * the symbol and returns YES. Otherwise it returns NO. + */ + +static int +setmrferm(staff_p, stuff_p) + +struct STAFF *staff_p; +struct STUFF *stuff_p; + +{ + struct GRPSYL *gs_p; /* point at the relevant grpsyl list */ + float fermx; /* the center of the fermata */ + float fermwidth; /* width of the fermata */ + int font, size, code; /* of the fermata */ + char *s_p; /* point into the string holding the fermata */ + + + debug(32, "setmrferm file=%s line=%d", stuff_p->inputfile, + stuff_p->inputlineno); + /* + * Who knows why the user would put a fermata between two staffs, but + * if they did, treat it as normal stuff. + */ + if (stuff_p->place == PL_BETWEEN) + return (NO); + + /* + * Figure out which voice this fermata is meant to apply to. It could + * be that one voice has a measure rest and the other doesn't. For + * measure repeats, both would have it, so we wouldn't care which + * voice, but we might as well fall through the same code. Point + * at the first GRPSYL in that voice's list. + */ + gs_p = 0; /* prevent useless 'used before set' warning */ + switch (svpath(staff_p->staffno, VSCHEME)->vscheme) { + case V_1: + /* only 1 voice, this must be it */ + gs_p = staff_p->groups_p[0]; + break; + + case V_2OPSTEM: + case V_3OPSTEM: + /* + * Technically we should attach the fermata to the voice it is + * next to. But if that voice is all spaces, that wouldn't + * make much sense. So fall through and handle the same as + * the freestem cases. + */ + + case V_2FREESTEM: + case V_3FREESTEM: + /* apply it to the nearest voice, unless it's all spaces */ + if (stuff_p->place == PL_ABOVE) { + if ( ! hasspace(staff_p->groups_p[0], Zero, Score.time)) + gs_p = staff_p->groups_p[0]; + else + gs_p = staff_p->groups_p[1]; + } else { + if ( ! hasspace(staff_p->groups_p[1], Zero, Score.time)) + gs_p = staff_p->groups_p[1]; + else + gs_p = staff_p->groups_p[0]; + } + break; + } + + /* + * If the relevant voice is not a measure rest or measure repeat, don't + * do anything more. Just return and let the normal "stuff" code + * handle this fermata. + */ + if (gs_p->is_meas == NO) + return (NO); + + /* + * This fermata is on a measure rest/repeat. We can't rely on the AX + * of this; it's offset like a whole rest would be. We have to + * average the AW and AE to know where the center should be. + * If the user requested a stepsize offset, we will apply it to the + * result, so in that case it won't really be centered after all. + */ + fermx = (gs_p->c[AW] + gs_p->c[AE]) / 2 + + stuff_p->start.steps * STEPSIZE; + + /* find width of fermata */ + font = stuff_p->string[0]; + size = stuff_p->string[1]; + s_p = &stuff_p->string[2]; + code = next_str_char(&s_p, &font, &size); + fermwidth = width(font, size, code); + + /* set the absolute horizontal coords */ + stuff_p->c[AX] = fermx; + stuff_p->c[AW] = fermx - fermwidth / 2; + stuff_p->c[AE] = fermx + fermwidth / 2; + + return (YES); +} + +/* + * Name: trygrid() + * + * Abstract: Handle a chord STUFF if there are chord grids. + * + * Returns: YES if a grid will be printed with this chord, NO if not + * + * Description: If there are to be chord grids printed at the end of the song, + * this function sets the "used" flag. If a grid should be + * printed here (where the chord is used), it sets the coordinates + * of this STUFF appropriately. + */ + +static int +trygrid(mainll_p, stuff_p) + +struct MAINLL *mainll_p; /* MLL struct for this chord stuff */ +struct STUFF *stuff_p; /* the chord stuff */ + +{ + int gridswhereused; /* YES or NO for this staff */ + struct GRID *grid_p; /* point to the grid definition */ + char *asciichord_p; /* point at ASCII version of chord */ + float geast, gwest; /* relative coords of the grid */ + float hstrw; /* half the width of the chord string */ + + + gridswhereused = svpath(mainll_p->u.staff_p->staffno, + GRIDSWHEREUSED)->gridswhereused; + + if (Score.gridsatend == NO && gridswhereused == NO) { + return (NO); + } + + grid_p = findgrid(stuff_p->string); + + /* if grid was never defined, warn and return no grid here */ + if (grid_p == 0) { + asciichord_p = ascii_str(stuff_p->string, YES, NO, TM_CHORD); + l_warning(stuff_p->inputfile, stuff_p->inputlineno, + "no grid defined for chord %s", asciichord_p); + return (NO); + } + + if (Score.gridsatend == YES) { /* grids print at end of song*/ + /* mark grid as used if it hasn't been, and keep count */ + if (grid_p->used == NO) { + grid_p->used = YES; + Atend_info.grids_used++; + } + } + + /* if no grid is to be printed here, get out */ + if (gridswhereused == NO) + return (NO); + + /* we only need east & west here */ + gridsize(grid_p, stuff_p->all ? 0 : mainll_p->u.staff_p->staffno, + (float *)0, (float *)0, &geast, &gwest); + hstrw = strwidth(stuff_p->string) / 2.0; + + /* + * The stuff extends as far as necessary to contain both the string and + * the grid. Unlike for other string stuffs, the string is centered at + * this count. (So is the grid, if you ignore any "X fr" string on its + * right side.) Note: "til" clauses are not allowed on chords. + */ + stuff_p->c[AE] = stuff_p->c[AX] + MAX(geast, hstrw); + stuff_p->c[AW] = stuff_p->c[AX] + MIN(gwest, -hstrw); + + return (YES); /* grid gets printed here */ +} + +/* + * Name: tieslurstuff() + * + * Abstract: Create STUFF structures for all ties, slurs, and bends. + * + * Returns: void + * + * Description: This function puts ST_TIESLUR, ST_TABSLUR, and ST_BEND STUFF + * structures in the linked lists. Unlike for the other stuff + * types, the parser never does this. So up until now, there have + * been none of these in the linked lists. More than one STUFF is + * needed if there the tie/slur/bend is across a bar line into + * endings, or across a scorefeed. + */ + +static void +tieslurstuff() +{ + struct MAINLL *mainll_p; /* point along main linked list */ + struct GRPSYL *gs_p; /* point at a group */ + int v; /* voice number, 0 or 1 */ + int n, k; /* loop variables */ + + + debug(16, "tieslurstuff"); + initstructs(); + + /* + * Loop through main linked list, looking for visible STAFFs. + */ + for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { + + if (mainll_p->str == S_SSV) { + /* keep SSVs up to date for benefit of l_warning */ + asgnssv(mainll_p->u.ssv_p); + continue; + } + + if (mainll_p->str != S_STAFF || + mainll_p->u.staff_p->visible == NO) + continue; + + /* + * Loop through all voices on this staff. + */ + for (v = 0; v < MAXVOICES && mainll_p->u.staff_p-> + groups_p[v] != 0; v++) { + /* + * Loop through the groups in this voice. Ignore rests + * and spaces. + */ + for (gs_p = mainll_p->u.staff_p->groups_p[v]; + gs_p != 0; gs_p = gs_p->next) { + + if (gs_p->grpcont != GC_NOTES) + continue; + + /* + * For each note in this group, for each tie + * and slur starting at it, call a function to + * allocate a STUFF for it (more if into an + * ending or scorefeed). However, do not do + * this for ties on a tablature staff. They + * are not to be printed. + */ + for (n = 0; n < gs_p->nnotes; n++) { + if (gs_p->notelist[n].tie == YES && + ! is_tab_staff(mainll_p + ->u.staff_p->staffno)) + mktieslurstuff(mainll_p, + gs_p, n, -1); + + for (k = 0; k < gs_p->notelist[n]. + nslurto; k++) { + mktieslurstuff(mainll_p, + gs_p, n, k); + } + } + } + } + } +} + +/* + * Name: mktieslurstuff() + * + * Abstract: Create STUFF structure(s) for one tie/slur/bend. + * + * Returns: void + * + * Description: This function puts ST_TIESLUR, ST_TABSLUR, or ST_BEND STUFF + * structure(s) in the linked list for one tie or slur curve. + * Normally only one is needed, but if it crosses a scorefeed + * two are needed, and if it crosses into a first ending, one is + * needed for each following ending. + */ + +static void +mktieslurstuff(mllstaff_p, gs_p, n, s) + +struct MAINLL *mllstaff_p; /* MLL of staff where tie/slur/bend starts */ +struct GRPSYL *gs_p; /* ptr to group where tie/slur/bend starts */ +int n; /* index to note in notelist where it starts */ +int s; /* index into slurtolist, or -1 for tie */ + +{ + struct STUFF *stuff_p; /* point at a STUFF structure */ + struct STAFF *staff_p; /* point at staff of mllstaff */ + struct MAINLL *mll_p; /* point along MLL */ + struct MAINLL *mll2_p; /* another pointer along MLL */ + struct BAR *bar_p; /* point to a bar or pseudobar */ + struct MAINLL *ebmll_p; /* end bar MLL struct */ + int endingloc; /* ending location */ + int stufftype; /* which of the stuff types is it? */ + + + staff_p = mllstaff_p->u.staff_p; + + if (s == -1) { /* tie */ + stufftype = ST_TIESLUR; + } else if (gs_p->notelist[n].is_bend == YES) { /* bend */ + stufftype = ST_BEND; + } else if (is_tab_staff(staff_p->staffno) || /* slur on tab or */ + is_tab_staff(staff_p->staffno + 1)) { /* tabnote staff */ + stufftype = ST_TABSLUR; + } else { /* slur on other staff*/ + stufftype = ST_TIESLUR; + } + + /* allocate a STUFF structure for this tie/slur/bend */ + stuff_p = newSTUFF((char *)0, 0, SD_NONE, (double)0, (double)0, 0, 0, + (double)0, stufftype, NO, PL_UNKNOWN, (char *)0, 0); + + /* fill in additional items needed for tie/slur */ + stuff_p->vno = gs_p->vno; + stuff_p->beggrp_p = gs_p; + stuff_p->begnote_p = &gs_p->notelist[n]; + stuff_p->curveno = (short)s; + + /* put the new STUFF into this staff's stuff list, at start of list */ + stuff_p->next = staff_p->stuff_p; + staff_p->stuff_p = stuff_p; + + /* if this is not the last group in measure, no more STUFFs needed */ + if (gs_p->next != 0) + return; + + /* if this is a slur to/from nowhere, no second piece needed */ + if (s >= 0 && (stufftype == ST_TABSLUR || stufftype == ST_TIESLUR) && + IS_NOWHERE(gs_p->notelist[n].slurtolist[s].octave)) + return; + + /* + * We might need more STUFF(s), either because of a scorefeed, or + * because of endings. First check for the scorefeed case. + */ + /* find bar line at end of measure */ + for (ebmll_p = mllstaff_p; ebmll_p != 0 && ebmll_p->str != S_BAR; + ebmll_p = ebmll_p->next) + ; + if (ebmll_p == 0) + pfatal("final measure has no bar line"); + + /* does a feed occur before next staff? */ + for (mll_p = ebmll_p; mll_p != 0 && mll_p->str != S_FEED && + mll_p->str != S_STAFF; mll_p = mll_p->next) + ; + if (mll_p == 0) + return; + + if (mll_p->str == S_FEED) { + /* found a feed, so try to make the extra STUFF */ + stuff_p->carryout = YES; + mkextrastuff(mll_p, stuff_p, gs_p, n, s, stufftype); + } + + /* + * Find whether we need to check for endings. This is true when we are + * at the start of a first ending. So endingloc has to be STARTITEM, + * and the previous bar must not be in an ending. If we are at a FEED, + * look at the pseudobar's endingloc, else look at the previous real + * bar. In any case, search back from the real bar. + */ + if (mll_p->str == S_FEED) { + /* + * Find the next CLEFSIG. Normally it's the next thing after + * this FEED, but it's possible there are block(s) here and so + * it will be after some later FEED. + */ + while (mll_p != 0 && mll_p->str != S_CLEFSIG) + mll_p = mll_p->next; + if (mll_p == 0) + return; /* there must not be any more music */ + bar_p = mll_p->u.clefsig_p->bar_p; + } else { + bar_p = ebmll_p->u.bar_p; + } + if (bar_p->endingloc != STARTITEM) + return; + for (mll2_p = ebmll_p->prev; mll2_p != 0 && mll2_p->str != S_BAR; + mll2_p = mll2_p->prev) + ; + if (mll2_p != 0 && (mll2_p->u.bar_p->endingloc == STARTITEM || + mll2_p->u.bar_p->endingloc == INITEM)) + return; + + /* + * The curve was into a first ending. So we have to loop forward, + * looking for the bar lines at the start of each ending, and try to + * make another STUFF for the curve carrying into that ending. + */ + for ( ; mll_p != 0; mll_p = mll_p->next) { + + if (mll_p->str != S_BAR) + continue; + + /* see if a pseudo bar occurs before the next staff */ + for (mll2_p = mll_p; mll2_p != 0 && mll2_p->str != S_STAFF && + (mll2_p->str != S_CLEFSIG || + mll2_p->u.clefsig_p->bar_p == 0); + mll2_p = mll2_p->next) + ; + if (mll2_p == 0 || mll2_p->str == S_STAFF) { + /* normal case, no pseudobar */ + endingloc = mll_p->u.bar_p->endingloc; + } else { + /* found pseudobar; use it instead of normal bar */ + endingloc = mll2_p->u.clefsig_p->bar_p->endingloc; + } + + switch (endingloc) { + + + case NOITEM: + case ENDITEM: + /* out of all endings, we're done */ + return; + + case STARTITEM: + /* + * We are at a bar that begins another ending. Try to + * find the matching staff in this measure. + */ + mkextrastuff(mll_p, stuff_p, gs_p, n, s, stufftype); + } + } +} + +/* + * Name: mkextrastuff() + * + * Abstract: Create STUFF extra structure(s) for one tie/slur/bend. + * + * Returns: void + * + * Description: This function puts ST_TIESLUR, ST_TABSLUR, or ST_BEND STUFF + * structure(s) in the linked list for the ending piece of a tie, + * slur, or bend that is broken across a scorefeed or into a + * second or later ending. + */ + +static void +mkextrastuff(mll_p, origstuff_p, gs_p, n, s, stufftype) + +struct MAINLL *mll_p; /* starts before first staff in measure */ +struct STUFF *origstuff_p; /* original STUFF for this curve */ +struct GRPSYL *gs_p; /* GRPSYL where curve started */ +int n; /* index to note in notelist where it starts */ +int s; /* index into slurtolist, or -1 for tie */ +int stufftype; /* which of the stuff types is it? */ + +{ + struct STUFF *stuff_p; /* new stuff */ + struct GRPSYL *endgs_p; /* group where tie/slur/bend ends */ + int k; /* loop variable */ + + + /* find first staff */ + for ( ; mll_p != 0 && mll_p->str != S_STAFF; + mll_p = mll_p->next) + ; + + /* find matching staff */ + for ( ; mll_p != 0 && mll_p->str == S_STAFF && + mll_p->u.staff_p->staffno != + gs_p->staffno; mll_p = mll_p->next) + ; + if (mll_p == 0) + pfatal("last bar missing in main linked list [mkextrastuff]"); + if (mll_p->str != S_STAFF) /* number of staffs changed? */ + return; + + + /* allocate a STUFF structure second half of tie/slur/bend */ + stuff_p = newSTUFF((char *)0, 0, SD_NONE, (double)0, (double)0, 0, 0, + (double)0, stufftype, NO, PL_UNKNOWN, (char *)0, 0); + + stuff_p->carryin = YES; + stuff_p->costuff_p = origstuff_p; + + /* fill in additional items needed for tie/slur */ + stuff_p->vno = gs_p->vno; + endgs_p = mll_p->u.staff_p->groups_p[gs_p->vno - 1]; + stuff_p->beggrp_p = endgs_p; /* "beg" is really set to the end here*/ + + /* begnote_p must be pointed at the note where tie/slur ends; find it */ + for (k = 0; k < endgs_p->nnotes; k++) { + if (s == -1) { + /* tie; check for matching note */ + if (endgs_p->notelist[k].letter == + gs_p->notelist[n].letter + && endgs_p->notelist[k].octave == + gs_p->notelist[n].octave) + break; + } else { + /* slur/bend; check for note we're slurring/bending to*/ + if (endgs_p->notelist[k].letter == + gs_p->notelist[n].slurtolist[s].letter + && endgs_p->notelist[k].octave == + gs_p->notelist[n].slurtolist[s].octave) + break; + } + } + if (k == endgs_p->nnotes) { + l_warning(endgs_p->inputfile, endgs_p->inputlineno, + "can't find note being tied, slurred, or bent to"); + FREE(stuff_p); + return; + } + + stuff_p->begnote_p = &endgs_p->notelist[k]; /* end of tie/slur */ + stuff_p->curveno = (short)s; /* same as for the carryout stuff */ + + /* put the new STUFF into this staff's stuff list, at start of list */ + stuff_p->next = mll_p->u.staff_p->stuff_p; + mll_p->u.staff_p->stuff_p = stuff_p; +}