--- /dev/null
+/* Copyright (c) 1995, 1996, 1997, 1998, 2002, 2003, 2004, 2006
+ * by Arkkra Enterprises */
+/* All rights reserved */
+/*
+ * Name: mkchords.c
+ *
+ * Description: This file contains functions for creating CHORD linked lists
+ * and erasing invisible voices.
+ */
+
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+
+static void swingmidi P((void));
+static struct GRPSYL *voicevis P((struct GRPSYL *gs_p));
+static void combine_voices P((void));
+static void chkhand1 P((struct GRPSYL *gs1_p, struct MAINLL *staffmll_p));
+static int chk2groups P((struct GRPSYL *hi_p, struct GRPSYL *lo_p,
+ struct MAINLL *mll_p, int qual));
+static void chkhand2 P((struct GRPSYL *gs1_p, struct MAINLL *staffmll_p));
+static int chk2neighbors P((struct GRPSYL *first_p, struct GRPSYL *second_p,
+ struct GRPSYL *gs1_p, struct MAINLL *mll_p));
+static void dohand P((struct GRPSYL *gs1_p, struct MAINLL *staffmll_p));
+static void comb2groups P((struct GRPSYL *dest_p, struct GRPSYL *src_p,
+ struct MAINLL *mll_p));
+static void addsrc2dest P((struct GRPSYL *dest_p, struct GRPSYL *src_p));
+static int setgrpptrs P((struct GRPSYL *gs1_p, struct GRPSYL *v_p[]));
+static int hastieslur P((struct GRPSYL *gs_p));
+\f
+/*
+ * Name: makechords()
+ *
+ * Abstract: Set up the linked lists of chords.
+ *
+ * Returns: void
+ *
+ * Description: This function scans through the main linked list looking
+ * for STAFF structures. It joins the GRPSYL structures in
+ * the lists that they head, into perpendicular linked lists,
+ * allocating a CHORD to head each of these. It also links
+ * the CHORDs for each measure together into a linked list.
+ *
+ * While doing the above, it also calls voicevis() to check if a
+ * voice should be invisible, and if so changes it to a measure
+ * space. It also applies the swingunit and voicecombine
+ * parameters.
+ */
+
+
+void
+makechords()
+
+{
+ struct MAINLL *mainll_p; /* point at item in main linked list */
+ struct MAINLL *mainch_p; /* for headcell of a chord list */
+
+ RATIONAL *vtime; /* cum. time of each voice and verse */
+ struct GRPSYL **grpsyl_p; /* pointers along GRPSYL lists */
+ struct STAFF *staff_p; /* point at a staff structure */
+
+ RATIONAL mintime; /* the minimum vtime */
+ register int num; /* no. of visible voices/verses */
+ register int v; /* index into grpsyl_p[] */
+ struct CHORD *ch_p; /* pointer to current chord */
+ struct CHORD *och_p; /* pointer to old chord */
+ struct GRPSYL *gs_p; /* pointer to current group/syllable */
+ int n; /* loop variable */
+ int firstgs; /* flag for first group/syllable */
+ int quit; /* flag for being done */
+
+
+ debug(16, "makechords");
+ /*
+ * If we are generating MIDI, we may need to change lengths (fulltimes)
+ * of groups. This has to be done now, before we link groups together
+ * into chords.
+ */
+ if (Doing_MIDI == YES) {
+ swingmidi();
+ }
+
+ gs_p = 0; /* keep lint happy; will be set before used */
+
+ /* malloc enough of these for all voices and verses */
+ MALLOC(rational, vtime, MAXSTAFFS * (MAXVOICES + Maxverses));
+ MALLOC(GRPSYL *, grpsyl_p, MAXSTAFFS * (MAXVOICES + Maxverses));
+
+ mainll_p = Mainllhc_p; /* point at first thing in main LL */
+
+ initstructs(); /* clean out old SSV info */
+
+ /*
+ * Loop once for each measure in the input.
+ */
+ for (;;) {
+ num = 0; /* number of linked lists in measure */
+
+ /*
+ * Look for the first structure in this measure that points off
+ * to a linked list of groups/syllables. If we hit the end of
+ * the main linked list, we're all done, so break out.
+ */
+ while (mainll_p != 0 && mainll_p->str != S_STAFF) {
+ if (mainll_p->str == S_SSV)
+ asgnssv(mainll_p->u.ssv_p);
+ mainll_p = mainll_p->next;
+ }
+
+ if (mainll_p == 0) {
+ FREE(vtime);
+ FREE(grpsyl_p);
+ break;
+ }
+
+ /*
+ * We've found another measure with STAFF in it. Allocate
+ * a chord headcell for this measure, and put it in the
+ * main linked list.
+ */
+ mainch_p = newMAINLLstruct(S_CHHEAD, 0);
+ insertMAINLL(mainch_p, mainll_p->prev);
+
+ /*
+ * Look for the last STAFF structure in the measure. While
+ * doing this, point at first element of all the group/syllable
+ * linked lists, and keep count of them. Ignore invisible
+ * staffs. Skip over any grace groups.
+ */
+ while (mainll_p != 0 && mainll_p->str == S_STAFF) {
+ staff_p = mainll_p->u.staff_p;
+
+ if (staff_p->visible == YES) {
+
+ /* do all the voices on this staff */
+ for (n = 0; n < MAXVOICES &&
+ staff_p->groups_p[n] != 0; n++) {
+ /*
+ * Zap voice if invisible. Set both
+ * of these to point at the first
+ * GRPSYL, which will be a new one if
+ * we zapped it.
+ */
+ grpsyl_p[num] = staff_p->groups_p[n] =
+ voicevis(staff_p->groups_p[n]);
+
+ /* skip leading grace groups */
+ while (grpsyl_p[num] != 0 &&
+ grpsyl_p[num]->grpvalue == GV_ZERO)
+ grpsyl_p[num] = grpsyl_p[num]->next;
+ if (grpsyl_p[num] == 0)
+ pfatal("nothing but grace groups found");
+ num++;
+ }
+
+ /* do all the verses on this staff */
+ for (n = 0; n < staff_p->nsyllists; n++)
+ grpsyl_p[num++] = staff_p->syls_p[n];
+ }
+ mainll_p = mainll_p->next;
+ }
+
+ /*
+ * Set up the first chord from the first note in each
+ * voice/verse. Its linked list of GRPSYLs will include
+ * the first GRPSYL in every linked list off of a visible
+ * STAFF.
+ */
+ MALLOC(CHORD, ch_p, 1);
+ mainch_p->u.chhead_p->ch_p = ch_p; /* point at first chord */
+ ch_p->ch_p = 0; /* only member on list so far */
+ ch_p->starttime = Zero; /* start time = any voice */
+
+ /* point headcell at first and set its first group's time */
+ ch_p->gs_p = grpsyl_p[0];
+ vtime[0] = grpsyl_p[0]->fulltime;
+
+ /* for each remaining one, point prev one at it & set time */
+ for (v = 1; v < num; v++) {
+ grpsyl_p[v-1]->gs_p = grpsyl_p[v];
+ vtime[v] = grpsyl_p[v]->fulltime;
+ }
+ grpsyl_p[num-1]->gs_p = 0; /* terminate linked list */
+
+ /* point at second GRPSYL in each voice/verse, if any */
+ for (v = 0; v < num; v++)
+ grpsyl_p[v] = grpsyl_p[v]->next;
+
+ /*
+ * Loop until groups/syllables in the voices/verses are used
+ * up. Form a chord for each time at which any voice/verse
+ * has a GRPSYL structure, though ignore grace groups.
+ */
+ for (;;) {
+ /*
+ * If every GRPSYL currently pointed at by a grpsyl_p[]
+ * is the last in its list (the measure), there are no
+ * more chords in this measure, and quit will remain
+ * YES.
+ */
+ quit = YES; /* first assume "quit" */
+ for (v = 0; v < num; v++) {
+ /* find next item (if any) not a grace group */
+ while (grpsyl_p[v] != 0 &&
+ grpsyl_p[v]->grpsyl == GS_GROUP &&
+ grpsyl_p[v]->grpvalue == GV_ZERO) {
+
+ grpsyl_p[v] = grpsyl_p[v]->next;
+ }
+
+ /* check if voice/verse has another item */
+ if (grpsyl_p[v] != 0) {
+ quit = NO; /* yes, so don't quit yet */
+ }
+ }
+
+ /* if time to quit, skip rest of loop, and get out */
+ if (quit == YES)
+ break;
+
+ /*
+ * At least one voice/verse has another note in it.
+ * Find the earliest time at which something changes.
+ */
+ mintime = vtime[0];
+ for (v = 1; v < num; v++)
+ if (LT(vtime[v], mintime))
+ mintime = vtime[v];
+
+ /* allocate memory for another chord */
+ och_p = ch_p; /* remember where previous chord is */
+ MALLOC(CHORD, ch_p, 1);
+ och_p->ch_p = ch_p; /* point previous chord at it*/
+ ch_p->ch_p = 0; /* terminate in case last */
+
+ ch_p->starttime = mintime; /* starting time for chord*/
+
+ /*
+ * Form a new linked list. The head cell is the new
+ * chord, and the list connects it to all the groups/
+ * syllables that start at this time.
+ */
+ firstgs = YES;
+ for (v = 0; v < num; v++) {
+ if (EQ(vtime[v], mintime)) {
+ /*
+ * This voice/verse has a grpsyl at
+ * this time. Make the previous one
+ * point at it, set its pointer to 0
+ * in case it turns out to be the last,
+ * and add its length to vtime[v].
+ */
+ if (firstgs == YES) {
+ /* point headcell at first */
+ ch_p->gs_p = grpsyl_p[v];
+ firstgs = NO;
+ } else {
+ /* point previous one at ours*/
+ gs_p->gs_p = grpsyl_p[v];
+ }
+
+ /* set gs_p to point at our new one */
+ gs_p = grpsyl_p[v];
+
+ vtime[v] = radd(vtime[v],
+ gs_p->fulltime);
+
+ /* get next GRPSYL in voice/verse */
+ grpsyl_p[v] = gs_p->next;
+ }
+ }
+
+ gs_p->gs_p = 0; /* terminate linked list */
+ }
+
+ /*
+ * Set the duration of each chord in this measure. It's the
+ * next chord's start time minus this chord's start time.
+ * But for the last chord, it's the time signature minus
+ * this chord's start time. Also set pseudodur, which is a
+ * function of the duration. The amount of width the chord
+ * "deserves" to be allocated is proportional to pseudodur.
+ */
+ for (ch_p = mainch_p->u.chhead_p->ch_p; ch_p->ch_p != 0;
+ ch_p = ch_p->ch_p) {
+ ch_p->duration = rsub(ch_p->ch_p->starttime,
+ ch_p->starttime);
+ ch_p->pseudodur = pow(RAT2FLOAT(ch_p->duration),
+ Score.packexp);
+ }
+ ch_p->duration = rsub(Score.time, ch_p->starttime);
+ ch_p->pseudodur = pow(RAT2FLOAT(ch_p->duration), Score.packexp);
+ }
+
+ /* if voicecombine parm was ever set, combine voices if possible */
+ if (Vcombused == YES) {
+ combine_voices();
+ }
+}
+\f
+/*
+ * Name: swingmidi()
+ *
+ * Abstract: Alter groups' time to implement the "swingunit" parameter.
+ *
+ * Returns: void
+ *
+ * Description: This function loops through every GRPSYL in every voice,
+ * adjusting their fulltime so that they will start and end at
+ * different times, if necessary, to follow the "swingunit" parm.
+ * Each measure is divided into durations of "swingunit", starting
+ * at the beginning. (Usually the timesig divided by swingunit
+ * will be an integer, but if not, the last piece will be shorter.)
+ * The time where one group ends and the next group starts will be
+ * altered in either of these two circumstances:
+ * 1. The current boundary time is halfway into a swingunit, and
+ * each group is at least half a swingunit long.
+ * 2. The current boundary time is 3/4 of the way into a swingunit,
+ * and the first group is at least 3/4 of a swingunit long, and
+ * and the second group is at least 1/4 of a swingunit long.
+ * In both of these cases, the fulltimes are altered so that the
+ * meeting point is 2/3 of the way into the swingunit.
+ */
+
+void
+swingmidi()
+{
+ struct MAINLL *mainll_p; /* point along main linked list */
+ struct GRPSYL *gs_p; /* point along a GRPSYL list */
+ struct GRPSYL *prev_p; /* the GRPSYL before gs_p */
+ int vidx; /* voice index, 0 to MAXVOICES-1 */
+ RATIONAL quot; /* quotient */
+ RATIONAL swingunit; /* from SSV */
+ RATIONAL starttime; /* offset into measure of gs_p */
+ RATIONAL halfswing; /* swingunit/2 */
+ RATIONAL sixthswing; /* swingunit/6 */
+ RATIONAL twelfthswing; /* swingunit/12 */
+ RATIONAL threefourthsswing; /* 3/4 * swingunit */
+ RATIONAL onefourthswing; /* 1/4 * swingunit */
+ static RATIONAL six = {6,1};
+ static RATIONAL twelve = {12,1};
+
+
+ initstructs();
+
+ for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+
+ switch (mainll_p->str) {
+ case S_SSV:
+ /* need to keep swingunit up to date */
+ asgnssv(mainll_p->u.ssv_p);
+ continue;
+ case S_STAFF:
+ break; /* break out and handle this staff */
+ default:
+ continue;
+ }
+
+ /* loop through every voice on this staff */
+ for (vidx = 0; vidx < MAXVOICES; vidx++) {
+
+ swingunit = vvpath(mainll_p->u.staff_p->staffno,
+ vidx + 1, SWINGUNIT)->swingunit;
+
+ /* skip this voice if swingunit was not set */
+ if (EQ(swingunit, Zero)) {
+ continue;
+ }
+
+ /* various rationals we will need in the loop below */
+ halfswing = rdiv(swingunit, Two);
+ threefourthsswing = rmul(swingunit, Three_fourths);
+ onefourthswing = rmul(swingunit, One_fourth);
+ sixthswing = rdiv(swingunit, six);
+ twelfthswing = rdiv(swingunit, twelve);
+
+ /* accumulate starttime */
+ starttime = Zero;
+
+ /* for lint; we'll never really check it when it's 0 */
+ prev_p = 0;
+
+ /* find first nongrace group in voice (0 if none) */
+ gs_p = mainll_p->u.staff_p->groups_p[vidx];
+ gs_p = gs_p != 0 && gs_p->grpvalue == GV_ZERO ?
+ nextnongrace(gs_p) : gs_p;
+
+ /* loop through every nongrace group in this voice */
+ for ( ; gs_p != 0; gs_p = nextnongrace(gs_p)) {
+
+ quot = rdiv(starttime, swingunit);
+
+ /* set starttime for the following group here
+ * because we may alter fulltime below */
+ starttime = radd(starttime, gs_p->fulltime);
+
+ /* handle case 1 (see prolog above) */
+ if (quot.d == 2 &&
+ GE(gs_p->fulltime, halfswing) &&
+ GE(prev_p->fulltime, halfswing)) {
+
+ prev_p->fulltime = radd(
+ prev_p->fulltime, sixthswing);
+ gs_p->fulltime = rsub(
+ gs_p->fulltime, sixthswing);
+ }
+
+ /* handle case 2 (see prolog above) */
+ if (quot.d == 4 && quot.n % 4 == 3 &&
+ GE(gs_p->fulltime, onefourthswing) &&
+ GE(prev_p->fulltime, threefourthsswing)) {
+
+ prev_p->fulltime = rsub(
+ prev_p->fulltime, twelfthswing);
+ gs_p->fulltime = radd(
+ gs_p->fulltime, twelfthswing);
+ }
+
+ prev_p = gs_p;
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: voicevis()
+ *
+ * Abstract: If this voice is to be invisible, make it a measure space.
+ *
+ * Returns: pointer to first GRPSYL; this equals the input pointer if the
+ * voice is visible, else it points at the new measure space GRPSYL
+ *
+ * Description: This function finds out if the given voice is supposed to be
+ * invisible this measure. If so, it throws away the current
+ * GRPSYL list, replacing it with a single GRPSYL that is a
+ * measure space. See the big comment in svpath() in ssv.c.
+ */
+
+
+static struct GRPSYL *
+voicevis(gs_p)
+
+struct GRPSYL *gs_p; /* first GRPSYL in this voice's linked list */
+
+{
+ struct GRPSYL *ngs_p; /* pointer to the new GRPSYL */
+
+
+ /*
+ * At this point we know that the command line -s option allows this
+ * staff to be printed, and in fact this staff will be printed, because
+ * at least one of its voice(s) is supposed to be. (We know this
+ * because where we were called from, staff_p->visible == YES, and that
+ * was set by calling svpath().) If the staff weren't being printed,
+ * we wouldn't need to bother wiping out its invisible voice(s).
+ */
+
+ /*
+ * This voice must be changed to a measure space if the -s option says
+ * it should be invisible, or if the SSVs say so. The vvpath function
+ * checks both things.
+ */
+ if (vvpath(gs_p->staffno, gs_p->vno, VISIBLE)->visible == NO) {
+
+ /*
+ * Allocate a new GRPSYL and make it a measure space, with the
+ * same inputlineno etc. as the first in the current list.
+ */
+ ngs_p = newGRPSYL(GS_GROUP);
+
+ ngs_p->inputlineno = gs_p->inputlineno;
+ ngs_p->inputfile = gs_p->inputfile;
+ ngs_p->staffno = gs_p->staffno;
+ ngs_p->vno = gs_p->vno;
+ ngs_p->grpsyl = GS_GROUP;
+ ngs_p->is_meas = YES;
+ ngs_p->basictime = -1;
+
+ /* in one compiler the following had to be done separately */
+ /* like this, because it couldn't do the structure assignment */
+ ngs_p->fulltime.n = Score.time.n;
+ ngs_p->fulltime.d = Score.time.d;
+
+ ngs_p->grpcont = GC_SPACE;
+ ngs_p->prev = 0;
+ ngs_p->next = 0;
+ ngs_p->gs_p = 0;
+
+ /* throw away the old GRPSYL list */
+ free_grpsyls(gs_p);
+
+ return (ngs_p); /* ret pointer to the measure space */
+ }
+
+ return (gs_p); /* ret pointer to the original, unchanged GRPSYL */
+}
+\f
+/*
+ * Name: combine_voices()
+ *
+ * Abstract: Combine GRPSYLs in voices according to the voicecombine parm.
+ *
+ * Returns: void
+ *
+ * Description: This function, if requested by the voicecombine parameter,
+ * combines the appropriate groups in each chord on each staff
+ * where it can. (The set of groups in one chord on one staff is
+ * called a "hand" in the following code and in some other places
+ * in Mup, because I get tired of spelling out the whole phrase.)
+ * Because of beaming and ties/slurs, some groups may need to
+ * remain uncombined because their neighbors can't be combined.
+ * So the work is done in three passes. The first pass looks at
+ * each hand individually to see what can be done there. The
+ * second pass applies the neighbor rules. The third pass does
+ * the actual combining.
+ */
+/*
+ * NOTE: GRPSYL.pvno is used as a scratch area in these functions. The macro
+ * below defines COMB to be pvno for this use. We rely on the fact that CALLOC
+ * initialized it to 0. We count the low order bit as bit 0. Two sets of bits
+ * are used, as follows.
+ *
+ * Bit "x+y" being set to 1 means that the GRPSYLs of voices x and y can be
+ * combined (looking only at the groups in this chord, not groups they may be
+ * tie/beamed to, etc.). This scheme works only because the only voice numbers
+ * are 1, 2, and 3, so that uses bits 3, 4, and 5. The first pass sets them,
+ * and the second pass reads them.
+ *
+ * Bits 6 through 13 are used in pairs for voice numbers of groups that really
+ * should be combined, with all rules applied. The SHIFT macros below give the
+ * rightmost bit of each of the voice numbers, for the two possible sources and
+ * destinations that can happen, given that only 3 voices can exist. Pass 2
+ * sets these bits, and pass 3 reads them.
+ */
+#define COMB pvno
+#define SRC1SHIFT 6
+#define DEST1SHIFT 8
+#define SRC2SHIFT 10
+#define DEST2SHIFT 12
+
+static void
+combine_voices()
+{
+ struct MAINLL *mll_p; /* point along main linked list */
+ struct MAINLL *staffmll_p; /* point a group's staff's MLL struct */
+ struct CHORD *ch_p; /* point along a chord list */
+ struct GRPSYL *gs1_p; /* point along the GRPSYL list of a chord */
+ int staff; /* staff number we are working on */
+ int pass; /* the three passes we must make */
+
+
+ /* run the three passes */
+ for (pass = 1; pass <= 3; pass++) {
+
+ initstructs(); /* clean out old SSV info */
+
+ for (mll_p = Mainllhc_p; mll_p != 0; mll_p = mll_p->next) {
+
+ switch (mll_p->str) {
+ case S_SSV:
+ /* keep SSVs up to date (but midmeasure SSVs
+ * don't matter for what we're doing here) */
+ asgnssv(mll_p->u.ssv_p);
+ continue;
+ case S_CHHEAD:
+ break; /* break out to handle the chords */
+ default:
+ continue; /* ignore everything else */
+ }
+
+ /*
+ * Loop through each chord in this list.
+ */
+ for (ch_p = mll_p->u.chhead_p->ch_p; ch_p != 0;
+ ch_p = ch_p->ch_p) {
+ /*
+ * Loop through the linked list of GRPSYLs
+ * hanging off this chord. Skip the syllables;
+ * just deal with the groups.
+ */
+ gs1_p = ch_p->gs_p;
+ staff = 0; /* before first staff */
+ for (;;) {
+ /* find first group on next staff */
+ while (gs1_p != 0 &&
+ gs1_p->staffno == staff) {
+ gs1_p = gs1_p->gs_p;
+ }
+ if (gs1_p == 0) { /* no next staff */
+ break;
+ }
+
+ /* remember this new staff number */
+ staff = gs1_p->staffno;
+
+ /* tab ignores vcombine */
+ if (is_tab_staff(staff)) {
+ continue;
+ }
+
+ /* 1-line ignores vcombine */
+ if (svpath(staff, STAFFLINES)->
+ stafflines == 1) {
+ continue;
+ }
+
+ /* if no groups, only syls, ignore */
+ if (gs1_p->grpsyl == GS_SYLLABLE) {
+ continue;
+ }
+
+ /* skip staff if only one voice */
+ if (svpath(staff, VSCHEME)->vscheme
+ == V_1) {
+ continue;
+ }
+
+ /* get the staff's MLL struct */
+ staffmll_p = chmgrp2staffm(
+ mll_p, gs1_p);
+
+ switch (pass) {
+ case 1:
+ /*
+ * Set individual combineability
+ * in the "COMB" field.
+ */
+ chkhand1(gs1_p, staffmll_p);
+ break;
+ case 2:
+ /*
+ * Apply the neighbor rules.
+ */
+ chkhand2(gs1_p, staffmll_p);
+ break;
+ case 3:
+ /*
+ * Do the actual combining.
+ */
+ dohand(gs1_p, staffmll_p);
+ break;
+ }
+ }
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: chkhand1()
+ *
+ * Abstract: Find which voices are combineable, ignoring neighbor rules.
+ *
+ * Returns: void
+ *
+ * Description: This function finds out which groups in this hand can be
+ * combined, of the ones requested by the voicecombine parameter,
+ * but ignoring neighbor rules. It stores the results in
+ * the COMB field of all the groups.
+ */
+
+static void
+chkhand1(gs1_p, mll_p)
+
+struct GRPSYL *gs1_p; /* first group in this hand */
+struct MAINLL *mll_p; /* MLL struct for this group */
+
+{
+ /*
+ * Index the following by voice number to find the expected "height" of
+ * the voices, relative to each other. A higher number means we expect
+ * that that voice should normally be higher.
+ */ /* v1 v2 v3 */
+ static int pitch[MAXVOICES + 1] = { 0, 3, 1, 2 };
+
+ struct SSV *ssv_p; /* the SSV with voice combine info */
+ struct GRPSYL *gs_p; /* point along GRPSYLs of the chord */
+ /* index the following by vno; it points at the GRPSYL for this vno */
+ struct GRPSYL *v_p[MAXVOICES + 1];
+ int slot1, slot2; /* indices into SSV's vcombine array */
+ int firstvno, secondvno; /* two voices from ssv_p->vcombine[] */
+ int combineable; /* YES or NO */
+ int comb; /* accumulate combineable bits */
+
+
+ /* point at each group; if only one, no combining can be done */
+ if (setgrpptrs(gs1_p, v_p) == 1) {
+ return;
+ }
+
+ /* get the list of groups we want to combine */
+ ssv_p = svpath(gs1_p->staffno, VCOMBINE);
+
+ /* for each pair of voices to be combined, see if they can be */
+ comb = 0;
+ for (slot1 = 0; slot1 < MAXVOICES; slot1++) {
+ firstvno = ssv_p->vcombine[slot1];
+ if (firstvno == 0) {
+ break; /* end of list, done with outer loop */
+ }
+ /* voice must have a group in this chord */
+ if (v_p[firstvno] == 0) {
+ continue; /* skip this iteration */
+ }
+ for (slot2 = slot1 + 1; slot2 < MAXVOICES; slot2++) {
+ secondvno = ssv_p->vcombine[slot2];
+ if (secondvno == 0) {
+ break; /* end of list, done with inner loop */
+ }
+ /* voice must have a group in this chord */
+ if (v_p[secondvno] == 0) {
+ continue; /* skip this iteration */
+ }
+ /* must pass higher voice first */
+ if (pitch[firstvno] > pitch[secondvno]) {
+ combineable = chk2groups(v_p[firstvno],
+ v_p[secondvno], mll_p,
+ ssv_p->vcombinequal);
+ } else {
+ combineable = chk2groups(v_p[secondvno],
+ v_p[firstvno], mll_p,
+ ssv_p->vcombinequal);
+ }
+
+ /* if this pair was combineable, remember that */
+ if (combineable == YES) {
+ comb |= 1 << (firstvno + secondvno);
+ }
+ }
+ }
+
+ /* save comb in all the groups, for easy access */
+ for (gs_p = gs1_p; gs_p != 0 && gs_p->staffno == gs1_p->staffno &&
+ gs_p->grpsyl == GS_GROUP; gs_p = gs_p->gs_p) {
+ gs_p->COMB = comb;
+ }
+}
+\f
+/*
+ * Name: chk2groups()
+ *
+ * Abstract: Find whether two groups are combineable.
+ *
+ * Returns: YES or NO
+ *
+ * Description: This function finds out if the given groups can be combined,
+ * ignoring beam and tie/slur issues. If either has preceding
+ * grace groups, they are also checked (via recursive calls) and
+ * they must also be able to be combined, to get a YES answer.
+ */
+
+static int
+chk2groups(hi_p, lo_p, mll_p, qual)
+
+struct GRPSYL *hi_p; /* group that should have higher pitch */
+struct GRPSYL *lo_p; /* group that should have lower pitch */
+struct MAINLL *mll_p; /* MLL struct for this group */
+int qual; /* voice combine qualifier */
+
+{
+ struct GRPSYL *phi_p; /* group before hi_p */
+ struct GRPSYL *plo_p; /* group before lo_p */
+ struct MAINLL *tempmll_p; /* temp copy of mll_p */
+ int widx; /* index into "with" lists */
+ int hidx, lidx; /* index into note lists */
+ int mintop; /* steps above c0 of top group's lowest note */
+ int maxbot; /* steps above c0 of bot group's highest note */
+ int n; /* loop variable */
+ struct NOTE *hnote_p; /* point at a note in the high group */
+ struct NOTE *lnote_p; /* point at a note in the low group */
+ struct SLURTO *hslur_p; /* point at a slur in the high group */
+ struct SLURTO *lslur_p; /* point at a slur in the high group */
+
+
+ /*
+ * Since the groups are in the same chord, we know they start at the
+ * same time. Require that they end at the same time.
+ */
+ if (NE(hi_p->fulltime, lo_p->fulltime)) {
+ return (NO);
+ }
+
+ /* covered by the fulltime check, except for bizarre tuplet cases */
+ if (hi_p->basictime != lo_p->basictime) {
+ return (NO);
+ }
+ if (hi_p->dots != lo_p->dots) {
+ return (NO);
+ }
+
+ /* don't allow "mr" and "1r" (for example) to combine */
+ if (hi_p->is_meas != lo_p->is_meas) {
+ return (NO);
+ }
+
+ /* tuploc must agree */
+ if (hi_p->tuploc != lo_p->tuploc) {
+ return (NO);
+ }
+
+ /* tupcont must agree */
+ if (hi_p->tupcont != lo_p->tupcont) {
+ return (NO);
+ }
+
+ /* if printtup is set in both groups, they must agree */
+ if (hi_p->printtup != PT_DEFAULT && lo_p->printtup != PT_DEFAULT &&
+ hi_p->printtup != lo_p->printtup) {
+ return (NO);
+ }
+
+ /* if tupside is set in both groups, they must agree */
+ if (hi_p->tupside != PL_UNKNOWN && lo_p->tupside != PL_UNKNOWN &&
+ hi_p->tupside != lo_p->tupside) {
+ return (NO);
+ }
+
+ /* don't allow cross staff beamed groups */
+ if (hi_p->beamto != CS_SAME || lo_p->beamto != CS_SAME) {
+ return (NO);
+ }
+
+ /* don't allow cross staff stemmed groups */
+ if (hi_p->stemto != CS_SAME || lo_p->stemto != CS_SAME) {
+ return (NO);
+ }
+
+ /* if either is a space, there can be no other conflict */
+ if (hi_p->grpcont == GC_SPACE || lo_p->grpcont == GC_SPACE) {
+ return (YES);
+ }
+
+ /***** both groups are GC_NOTES and/or GC_REST *****/
+
+ /* group size must agree */
+ if (hi_p->grpsize != lo_p->grpsize) {
+ return (NO);
+ }
+
+ /* if both are rests, there can be no other conflict */
+ if (hi_p->grpcont == GC_REST && lo_p->grpcont == GC_REST) {
+ return (YES);
+ }
+
+ /* if only one is a rest, there is a conflict */
+ if (hi_p->grpcont == GC_REST || lo_p->grpcont == GC_REST) {
+ return (NO);
+ }
+
+ /***** both groups are GC_NOTES *****/
+
+ /* head shape must agree */
+ if (hi_p->headshape != lo_p->headshape) {
+ return (NO);
+ }
+
+ /* beaming must be the same on both, except on grace beamloc doesn't
+ * matter, since if we combine them we'll beam them all anyway */
+ if (hi_p->beamloc != lo_p->beamloc && hi_p->grpvalue == GV_NORMAL) {
+ return (NO);
+ }
+ if (hi_p->breakbeam != lo_p->breakbeam) {
+ return (NO);
+ }
+ if (hi_p->beamslope != NOBEAMANGLE && lo_p->beamslope != NOBEAMANGLE &&
+ hi_p->beamslope != lo_p->beamslope) {
+ return (NO);
+ }
+
+ /* if both stemdirs are set, they must agree */
+ if (hi_p->stemlen != STEMLEN_UNKNOWN &&
+ lo_p->stemlen != STEMLEN_UNKNOWN &&
+ hi_p->stemlen != lo_p->stemlen) {
+ return (NO);
+ }
+
+ /* if both stemdirs are forced, they must agree */
+ if (hi_p->stemdir != UNKNOWN && lo_p->stemdir != UNKNOWN &&
+ hi_p->stemdir != lo_p->stemdir) {
+ return (NO);
+ }
+
+ /* group ties out of this group must agree */
+ if (hi_p->tie != lo_p->tie) {
+ return (NO);
+ }
+
+ /* group ties into this group must agree */
+ tempmll_p = mll_p;
+ phi_p = prevgrpsyl(hi_p, &tempmll_p);
+ tempmll_p = mll_p;
+ plo_p = prevgrpsyl(lo_p, &tempmll_p);
+ if (phi_p != 0 && plo_p != 0 && phi_p->tie != plo_p->tie) {
+ return (NO);
+ }
+
+ /* tremelo slashes or alternation slashes must agree */
+ if (hi_p->slash_alt != lo_p->slash_alt) {
+ return (NO);
+ }
+
+ /* neither group can have a horizontal offset */
+ if (hi_p->ho_usage != HO_NONE || lo_p->ho_usage != HO_NONE) {
+ return (NO);
+ }
+
+ /* "with" lists must both be nonexistent, or else identical */
+ if (hi_p->nwith != lo_p->nwith) {
+ return (NO);
+ }
+ for (widx = 0; widx < hi_p->nwith; widx++) {
+ if (strcmp(hi_p->withlist[widx], lo_p->withlist[widx]) != 0) {
+ return (NO);
+ }
+ }
+
+ /*
+ * Rolls aren't really implemented for voice 3. Voice 3 is just along
+ * for the ride, if it's in the middle of a roll. So we'll let it
+ * combine as long as both groups are in a roll, or not. But for
+ * voices 1 and 2, only allow specific combinations that make sense.
+ */
+ if (hi_p->vno == 1 && lo_p->vno == 2) {
+ switch ((hi_p->roll << 8) | lo_p->roll) {
+ case (NOITEM << 8) | NOITEM:
+ break;
+ case (STARTITEM << 8) | INITEM:
+ case (STARTITEM << 8) | ENDITEM:
+ case (INITEM << 8) | INITEM:
+ case (INITEM << 8) | ENDITEM:
+ if (hi_p->rolldir != lo_p->rolldir) {
+ return (NO);
+ }
+ break;
+ default:
+ return (NO);
+ }
+ } else {
+ if ((hi_p->roll == NOITEM) != (lo_p->roll == NOITEM)) {
+ return (NO);
+ }
+ }
+
+ /*
+ * If each group is preceded by a grace group, those grace groups must
+ * also be combineable. Notice that if there are multiple preceding
+ * grace groups, more recursive calls will occur. If one is preceded
+ * and the other isn't, there is no problem and nothing to check.
+ */
+ if (hi_p->prev != 0 && hi_p->prev->grpvalue == GV_ZERO &&
+ lo_p->prev != 0 && lo_p->prev->grpvalue == GV_ZERO) {
+ if (chk2groups(hi_p->prev, lo_p->prev, mll_p, qual) == NO) {
+ return (NO);
+ }
+ }
+
+ /* if either is a mrpt, the other must be, and there is no conflict */
+ if (hi_p->is_meas == YES) {
+ return (YES);
+ }
+
+ /***** both groups have a notelist *****/
+
+ /*
+ * Do some checks involving overlapping of the groups. We'd like to
+ * use stepsup, but it won't be set to its correct value until
+ * setnotes.c. Fortunately, we don't need to know the absolute
+ * vertical position, only the relative. So just use steps above c0.
+ */
+ mintop = hi_p->notelist[hi_p->nnotes - 1].octave * 7 +
+ Letshift[hi_p->notelist[hi_p->nnotes - 1].letter - 'a'];
+ maxbot = lo_p->notelist[0].octave * 7 +
+ Letshift[lo_p->notelist[0].letter - 'a'];
+ /*
+ * If the lowest note of the high group is higher than the highest of
+ * the low group, we are okay regardless of the vcombine qualifier,
+ * and there are no shared notes to check.
+ */
+ if (mintop > maxbot) {
+ return (YES);
+ }
+
+ /* if they are equal, and parm doesn't allow it, fail */
+ if (mintop == maxbot && qual == VC_NOOVERLAP) {
+ return (NO);
+ }
+
+ /* if there is true overlap, and parm doesn't allow it, fail */
+ if (mintop < maxbot && qual != VC_OVERLAP) {
+ return (NO);
+ }
+
+ /*
+ * Any shared notes must be compatible, so check every note in the top
+ * group against every note in the bottom group. If any are equal,
+ * they must be compatible.
+ */
+ for (hidx = 0; hidx < hi_p->nnotes; hidx++) {
+
+ hnote_p = &hi_p->notelist[hidx];
+
+ for (lidx = 0; lidx < lo_p->nnotes; lidx++) {
+
+ lnote_p = &lo_p->notelist[lidx];
+
+ /* if the notes aren't equal, don't check them */
+ if (hnote_p->octave != lnote_p->octave ||
+ hnote_p->letter != lnote_p->letter) {
+ /* the notes differ; no problem */
+ continue;
+ }
+
+ /* notes match; check all the items that must agree */
+
+ /* any slurs they have must be the same */
+ if (hnote_p->nslurto != lnote_p->nslurto) {
+ return (NO);
+ }
+ for (n = 0; n < hnote_p->nslurto; n++) {
+ hslur_p = &hnote_p->slurtolist[n];
+ lslur_p = &lnote_p->slurtolist[n];
+ if (hslur_p->letter != lslur_p->letter) {
+ return (NO);
+ }
+ if (hslur_p->octave != lslur_p->octave) {
+ return (NO);
+ }
+ if (hslur_p->slurstyle != lslur_p->slurstyle) {
+ return (NO);
+ }
+ if (hslur_p->slurdir != lslur_p->slurdir) {
+ return (NO);
+ }
+ }
+
+ /* must have same accidental, or none */
+ if (hnote_p->accidental != lnote_p->accidental) {
+ return (NO);
+ }
+
+ /* head shape override, or none, must agree */
+ if (hnote_p->headshape != lnote_p->headshape) {
+ return (NO);
+ }
+
+ /* note size must agree */
+ if (hnote_p->notesize != lnote_p->notesize) {
+ return (NO);
+ }
+
+ /* tie related attributes must be the same */
+ if (hnote_p->tie != lnote_p->tie) {
+ return (NO);
+ }
+ if (hnote_p->tiestyle != lnote_p->tiestyle) {
+ return (NO);
+ }
+ if (hnote_p->tiedir != lnote_p->tiedir) {
+ return (NO);
+ }
+ if (hnote_p->is_bend != lnote_p->is_bend) {
+ return (NO);
+ }
+ if (hnote_p->smallbend != lnote_p->smallbend) {
+ return (NO);
+ }
+ }
+ }
+
+ return (YES); /* passed all tests, the groups are combineable */
+}
+\f
+/*
+ * Name: chkhand2()
+ *
+ * Abstract: Make final decision on what can be combined in this hand.
+ *
+ * Returns: void
+ *
+ * Description: This function applies the additional rules about beaming and
+ * ties/slurs to the results of chkhand1(), to decide what groups
+ * in this hand truly should be combined. It stores the results
+ * in the COMB field of all the groups.
+ */
+
+static void
+chkhand2(gs1_p, mll_p)
+
+struct GRPSYL *gs1_p; /* first group in this hand */
+struct MAINLL *mll_p; /* MLL struct for this group */
+
+{
+ struct SSV *ssv_p; /* the SSV with voice combine info */
+ /* index the following by vno; it points at the GRPSYL for this vno */
+ struct GRPSYL *v_p[MAXVOICES + 1];
+ int slot1, slot2; /* indices into SSV's vcombine array */
+ int firstvno, secondvno; /* two voices from ssv_p->vcombine[] */
+ int keep_going; /* try some more? */
+
+
+ /* if we already know no combining can be done, get out */
+ if (gs1_p->COMB == 0) {
+ return;
+ }
+
+ /* point at all the groups in this hand */
+ (void)setgrpptrs(gs1_p, v_p);
+
+ /* get the list of groups we want to combine */
+ ssv_p = svpath(gs1_p->staffno, VCOMBINE);
+
+ /* for each pair of voices to be combined, apply the neighbor rules */
+ for (slot1 = 0; slot1 < MAXVOICES; slot1++) {
+ firstvno = ssv_p->vcombine[slot1];
+ if (firstvno == 0) {
+ break; /* end of list, done with outer loop */
+ }
+ /* voice must have a group in this chord */
+ if (v_p[firstvno] == 0) {
+ continue; /* skip this iteration */
+ }
+ for (slot2 = slot1 + 1; slot2 < MAXVOICES; slot2++) {
+ secondvno = ssv_p->vcombine[slot2];
+ if (secondvno == 0) {
+ break; /* end of list, done with inner loop */
+ }
+ /* voice must have a group in this chord */
+ if (v_p[secondvno] == 0) {
+ continue; /* skip this iteration */
+ }
+ /*
+ * The group with the lower voice number should be the
+ * one that absorbs the other. Pass it first.
+ */
+ if (firstvno < secondvno) {
+ keep_going = chk2neighbors(v_p[firstvno],
+ v_p[secondvno], gs1_p, mll_p);
+ } else {
+ keep_going = chk2neighbors(v_p[secondvno],
+ v_p[firstvno], gs1_p, mll_p);
+ }
+
+ /* get out now if we are told to */
+ if (keep_going == NO) {
+ return;
+ }
+ }
+ }
+}
+\f
+/*
+ * Name: chk2neighbors()
+ *
+ * Abstract: Apply beaming and tie/slur rules to two groups in a hand.
+ *
+ * Returns: YES or NO: should any further combining be attempted?
+ *
+ * Description: This function checks whether the two groups should be combined,
+ * by looking at neighboring groups if beaming or ties/slurs
+ * are involved. It stores the answer in the COMB field of all
+ * the groups.
+ *
+ * If we shouldn't combine the groups, we return YES so that we
+ * can come back and try a different pair, if appropriate. If we
+ * can combine the groups, we may return YES or NO; see comments
+ * below.
+ */
+
+static int
+chk2neighbors(dest_p, src_p, gs1_p, mll_p)
+
+struct GRPSYL *dest_p; /* group that should absorb the other */
+struct GRPSYL *src_p; /* group that should be absorbed */
+struct GRPSYL *gs1_p; /* first group in staff/chord */
+struct MAINLL *mll_p; /* MLL struct for these groups' staff */
+
+{
+ struct GRPSYL *d2_p, *s2_p; /* point at neighboring groups */
+ struct GRPSYL *gs_p; /* point at groups in this chord */
+ struct MAINLL *mll2_p, *mll3_p; /* more pointers into MLL */
+ short mask; /* to be applied to "COMB" */
+ short comb; /* hold combineability bits */
+ int keep_going; /* return code */
+
+
+ /*
+ * Set up the bit that we must check in each chord to see if these two
+ * voices are combineable.
+ */
+ mask = 1 << (dest_p->vno + src_p->vno);
+
+ /*
+ * Start at this chord, and keep looking to the right as long as the
+ * groups are beamed or tied/slurred.
+ */
+ mll2_p = mll_p;
+ for (d2_p = dest_p, s2_p = src_p; d2_p != 0 && s2_p != 0;
+ d2_p = nextglobnongrace(d2_p, &mll2_p),
+ s2_p = nextglobnongrace(s2_p, &mll2_p)) {
+ /*
+ * If the groups in that chord are not combineable, we must not
+ * combine our original groups either. The same flags are set
+ * in d2_p and s2_p, so we could have checked either.
+ */
+ if ((d2_p->COMB & mask) == 0) {
+ return (YES);
+ }
+
+ /*
+ * If the groups are not beamed or tied/slurred to the right,
+ * we don't have to look any farther. For beams, we only have
+ * to check one group, since the other must agree or we
+ * wouldn't be here. For ties/slurs, we only have to check the
+ * the src, since those are the ones that wouldn't work if
+ * these groups were combined and the next ones weren't.
+ */
+ if ((s2_p->beamloc == NOITEM || s2_p->beamloc == ENDITEM) &&
+ hastieslur(s2_p) == NO) {
+ break;
+ }
+ }
+
+ /*
+ * Start at this chord, and keep looking to the left as long as the
+ * groups are beamed or tied/slurred.
+ */
+ mll2_p = mll_p;
+ for (d2_p = dest_p, s2_p = src_p; d2_p != 0 && s2_p != 0;
+ d2_p = prevglobnongrace(d2_p, &mll2_p),
+ s2_p = prevglobnongrace(s2_p, &mll2_p)) {
+
+ if ((d2_p->COMB & mask) == 0) {
+ return (YES);
+ }
+
+ mll3_p = mll2_p; /* don't alter mll2_p */
+ if ((s2_p->beamloc == NOITEM || s2_p->beamloc == STARTITEM) &&
+ hastieslur(prevglobnongrace(s2_p, &mll3_p)) == NO) {
+ break;
+ }
+ }
+
+ /* for convenience, copy the COMB bits into a local variable */
+ comb = dest_p->COMB;
+
+ /*
+ * We passed all the tests, so remember that we should combine the
+ * original groups. Also decide whether any more should be tried.
+ */
+ if (((comb >> SRC1SHIFT) & 0x3) == 0) {
+ /* first groups to be combined in this hand */
+ comb |= src_p->vno << SRC1SHIFT;
+ comb |= dest_p->vno << DEST1SHIFT;
+ /*
+ * If chkhand1() said all the groups in this hand were
+ * combineable, we can come back later and try to combine the
+ * other group. Otherwise, don't. We know the non-combineable
+ * group will fail to combine, so don't waste the time.
+ */
+ if ((comb & 0x38) == ((1 << (1 + 2)) | (1 << (1 + 3)) |
+ (1 << (2 + 3)))) {
+ keep_going = YES;
+ } else {
+ keep_going = NO;
+ }
+ } else {
+ /* second groups to be combined in this hand */
+ comb |= src_p->vno << SRC2SHIFT;
+ comb |= dest_p->vno << DEST2SHIFT;
+ keep_going = NO; /* only 2 combinings can be done */
+ }
+
+ /*
+ * Since we will combine these two groups, the new dest group has
+ * inherited any incompatibility that src might have had with the
+ * other group (if any). So find the new value of the comb bits.
+ * Set this new comb value into all the groups in this hand. We do
+ * this for the benefit of later chords on this staff, if they are
+ * beamed or tied/slurred to us.
+ */
+ switch (dest_p->vno + src_p->vno) {
+ case 1 + 2: /* combined 2 into 1 */
+ /* if 2 and 3 can't combine, now 1 and 3 can't */
+ if ((comb & (1 << (2 + 3))) == 0) {
+ comb &= ~(1 << (1 + 3));
+ }
+ break;
+ case 1 + 3: /* combined 3 into 1 */
+ /* if 3 and 2 can't combine, now 1 and 2 can't */
+ if ((comb & (1 << (3 + 2))) == 0) {
+ comb &= ~(1 << (1 + 2));
+ }
+ break;
+ case 2 + 3: /* combined 3 into 2 */
+ /* if 3 and 1 can't combine, now 2 and 1 can't */
+ if ((comb & (1 << (3 + 1))) == 0) {
+ comb &= ~(1 << (2 + 1));
+ }
+ break;
+ }
+ for (gs_p = gs1_p; gs_p != 0 && gs_p->staffno == gs1_p->staffno &&
+ gs_p->grpsyl == GS_GROUP; gs_p = gs_p->gs_p) {
+ gs_p->COMB = comb;
+ }
+
+ return (keep_going);
+}
+\f
+/*
+ * Name: dohand()
+ *
+ * Abstract: Combine the appropriate GRPSYLs in one chord on one staff.
+ *
+ * Returns: void
+ *
+ * Description: This function combines GRPSYLs in one chord on one staff. It
+ * uses the previously determined list of what is combineable in
+ * gs1_p->COMB.
+ */
+
+static void
+dohand(gs1_p, mll_p)
+
+struct GRPSYL *gs1_p; /* first group on this staff in this chord */
+struct MAINLL *mll_p; /* MLL struct for this group */
+
+{
+ /* index the following by vno; it points at the GRPSYL for this vno */
+ struct GRPSYL *v_p[MAXVOICES + 1];
+ int srcv, destv; /* src and dest voice numbers */
+
+
+ /* if we already know no combining can be done, get out */
+ if (gs1_p->COMB == 0) {
+ return;
+ }
+
+ /* point at all the groups in this hand */
+ (void)setgrpptrs(gs1_p, v_p);
+
+ /* get first src group's voice number */
+ srcv = (gs1_p->COMB >> SRC1SHIFT) & 0x3;
+
+ if (srcv != 0) {
+ /* find the dest's voice and combine */
+ destv = (gs1_p->COMB >> DEST1SHIFT) & 0x3;
+ comb2groups(v_p[destv], v_p[srcv], mll_p);
+
+ /* get second src group's voice number */
+ srcv = (gs1_p->COMB >> SRC2SHIFT) & 0x3;
+
+ if (srcv != 0) {
+ /* find the dest's voice and combine */
+ destv = (gs1_p->COMB >> DEST2SHIFT) & 0x3;
+ comb2groups(v_p[destv], v_p[srcv], mll_p);
+ }
+ }
+}
+\f
+/*
+ * Name: comb2groups()
+ *
+ * Abstract: Combine one group into another.
+ *
+ * Returns: void
+ *
+ * Description: This function combines the src group into the dest group,
+ * including handling any preceding grace groups.
+ */
+
+static void
+comb2groups(dest_p, src_p, mll_p)
+
+struct GRPSYL *dest_p; /* group that should absorb the other */
+struct GRPSYL *src_p; /* group that should be absorbed */
+struct MAINLL *mll_p; /* MLL struct for these groups' staff */
+
+{
+ struct GRPSYL *dgrace_p; /* dest grace group */
+ struct GRPSYL *sgrace_p; /* src grace group */
+ struct GRPSYL *dmem_p; /* remember a group from dest voice */
+ struct GRPSYL *smem_p; /* remember a group from src voice */
+ struct GRPSYL *temp_p; /* temp pointer */
+ struct GRPSYL *fgs_p; /* first grace in src voice */
+
+
+ /*
+ * Add the src into the dest, and make the src group into a space if it
+ * isn't already a space.
+ */
+ addsrc2dest(dest_p, src_p);
+ if (src_p->grpcont != GC_SPACE) {
+ src_p->grpcont = GC_SPACE;
+ src_p->grpvalue = GV_NORMAL;
+ src_p->grpsize = GS_NORMAL;
+ src_p->headshape = HS_UNKNOWN;
+ src_p->beamloc = NOITEM;
+ src_p->beamslope = NOBEAMANGLE;
+ src_p->stemlen = STEMLEN_UNKNOWN;
+ src_p->stemdir = UNKNOWN;
+ src_p->breakbeam = NO;
+ src_p->beamloc = NOITEM;
+ src_p->notelist = 0;
+ src_p->nnotes = 0;
+ src_p->tie = NO;
+ src_p->slash_alt = 0;
+ src_p->restdist = NORESTDIST;
+ src_p->nwith = 0;
+ src_p->roll = NOITEM;
+ }
+
+ /* if source has no grace notes, we're done */
+ if (src_p->prev == 0 || src_p->prev->grpvalue == GV_NORMAL) {
+ return;
+ }
+
+ /*
+ * Loop through the matching grace groups preceding the src and dest
+ * groups, adding the src into the dest. Remember the leftmost pair,
+ * or the nongrace groups if there are no matching pairs.
+ */
+ dmem_p = dest_p;
+ smem_p = src_p;
+ for (sgrace_p = src_p->prev, dgrace_p = dest_p->prev;
+ sgrace_p != 0 && sgrace_p->grpvalue == GV_ZERO &&
+ dgrace_p != 0 && dgrace_p->grpvalue == GV_ZERO;
+ sgrace_p = sgrace_p->prev, dgrace_p = dgrace_p->prev) {
+
+ addsrc2dest(dgrace_p, sgrace_p);
+ dmem_p = dgrace_p; /* remember last one done */
+ smem_p = sgrace_p;
+ }
+
+ if (sgrace_p != 0 && sgrace_p->grpvalue == GV_ZERO) {
+ /* extra src grace group(s) left over must be moved */
+
+ /* sgrace_p is the last (rightmost) grace to move */
+ for (temp_p = sgrace_p; temp_p != 0 && temp_p->grpvalue ==
+ GV_ZERO; temp_p = temp_p->prev) {
+ fgs_p = temp_p;
+ }
+ /* fgs_p is the first (leftmost) grace to move */
+
+ if (dgrace_p == 0) { /* at start of measure? */
+ /* in src voice, skip over all the graces */
+ mll_p->u.staff_p->groups_p[src_p->vno-1] = src_p;
+ src_p->prev = 0;
+
+ /* splice the left grace into the dest voice */
+ mll_p->u.staff_p->groups_p[dest_p->vno-1] = fgs_p;
+ fgs_p->prev = 0;
+ } else {
+ /* in src voice, skip over all the graces */
+ fgs_p->prev->next = src_p;
+ src_p->prev = fgs_p->prev;
+
+ /* splice the left grace into the dest voice */
+ dgrace_p->next = fgs_p;
+ fgs_p->prev = dgrace_p;
+ }
+
+ /* splice the rightmost left over grace into the dest voice */
+ dmem_p->prev = sgrace_p;
+ sgrace_p->next = dmem_p;
+
+ /*
+ * If there is now more than one grace in the dest, we have to
+ * correct beamloc in a couple places.
+ */
+ if (fgs_p != dest_p->prev) {
+ /*
+ * If the dest voice had multiple graces before, the
+ * leftmost one, which was STARTITEM, must be made
+ * INITEM. If it had only one, it was NOITEM and
+ * must be made ENDITEM, but go ahead and set INITEM;
+ * it'll be set correctly below.
+ */
+ if (dmem_p != dest_p) { /* if dest had grace(s) */
+ dmem_p->beamloc = INITEM;
+ }
+
+ /* rightmost dest grace must always be made ENDITEM */
+ dest_p->prev->beamloc = ENDITEM;
+ }
+
+ /* change the voice number of the moved graces */
+ for (temp_p = fgs_p; temp_p != sgrace_p->next;
+ temp_p = temp_p->next) {
+ temp_p->vno = dest_p->vno;
+ }
+ } else {
+ /* no extra src grace group(s); just remove all src graces */
+ if (sgrace_p == 0) {
+ mll_p->u.staff_p->groups_p[src_p->vno-1] = src_p;
+ src_p->prev = 0;
+ } else {
+ smem_p->prev->next = src_p;
+ src_p->prev = smem_p->prev;
+ }
+ }
+ /* could free the src graces, but is it worth the trouble? */
+}
+\f
+/*
+ * Name: addsrc2dest()
+ *
+ * Abstract: Adds src group to dest group.
+ *
+ * Returns: void
+ *
+ * Description: This function adds the src group info (if the src group exists)
+ * into the dest group. But it doesn't mess with linkages. That
+ * is the calling function's job.
+ */
+
+static void
+addsrc2dest(dest_p, src_p)
+
+struct GRPSYL *dest_p; /* destination group must exist */
+struct GRPSYL *src_p; /* source group, or can be NULL */
+
+{
+ struct NOTE *dnote_p; /* point at a note in the destination group */
+ struct NOTE *snote_p; /* point at a note in the source group */
+ struct NOTE *newlist; /* combined notelist */
+ int didx, sidx; /* index into dest and src note lists */
+ int totnotes; /* total number of notes after combining */
+ int n; /* loop variable */
+
+
+ /* if src doesn't exist, there is nothing to do */
+ if (src_p == 0) {
+ return;
+ }
+
+ /* move references to the src group's coords */
+ upd_ref(src_p->c, dest_p->c);
+
+ /* if src is space, nothing to do except carry over this space field */
+ if (src_p->grpcont == GC_SPACE) {
+ if (dest_p->grpcont == GC_SPACE &&
+ src_p->uncompressible == YES) {
+ dest_p->uncompressible = YES;
+ }
+ return;
+ }
+
+ /* padding is the max of the two groups */
+ dest_p->padding = MAX(dest_p->padding, src_p->padding);
+
+ /*
+ * Some fields were allowed to disagree between the groups as long as
+ * one was defaulted. The non-default value should prevail.
+ */
+ if (src_p->printtup != PT_DEFAULT) {
+ dest_p->printtup = src_p->printtup;
+ }
+ if (src_p->tupside != PL_UNKNOWN) {
+ dest_p->tupside = src_p->tupside;
+ }
+ if (src_p->beamslope != NOBEAMANGLE) {
+ dest_p->beamslope = src_p->beamslope;
+ }
+ if (src_p->stemlen != STEMLEN_UNKNOWN) {
+ dest_p->stemlen = src_p->stemlen;
+ }
+ if (src_p->stemdir != UNKNOWN) {
+ dest_p->stemdir = src_p->stemdir;
+ }
+
+ /* only when combining 1 & 2 might we have to adjust roll */
+ if (dest_p->vno == 1 && src_p->vno == 2) {
+ switch ((dest_p->roll << 8) | src_p->roll) {
+ case (NOITEM << 8) | NOITEM:
+ /* dest remains NOITEM */
+ break;
+ case (STARTITEM << 8) | INITEM:
+ /* dest remains STARTITEM */
+ break;
+ case (STARTITEM << 8) | ENDITEM:
+ dest_p->roll = LONEITEM;
+ break;
+ case (INITEM << 8) | INITEM:
+ /* dest remains INITEM */
+ break;
+ case (INITEM << 8) | ENDITEM:
+ dest_p->roll = ENDITEM;
+ break;
+ }
+ }
+
+ /* if one is a rest, so is the other; combine */
+ if (dest_p->grpcont == GC_REST) {
+ /* throw away any restdist; probably would be inappropriate */
+ dest_p->restdist = 0;
+ return;
+ }
+
+ /* if either is a mrpt, the other must be; nothing to copy */
+ if (dest_p->is_meas == YES) {
+ return;
+ }
+
+ /* at this point we know src has notes, & dest has notes or is space */
+
+ /*
+ * Make a new notelist. First copy the old dest notes into there, and
+ * then add the src notes. We can't just realloc the dest, since
+ * pointers into the array must be updated by calling upd_ref for each
+ * array element. If any notes are shared, we won't really need this
+ * many, but it doesn't waste much memory.
+ */
+ MALLOC(NOTE, newlist, dest_p->nnotes + src_p->nnotes);
+ for (n = 0; n < dest_p->nnotes; n++) {
+ newlist[n] = dest_p->notelist[n];
+ }
+
+ /*
+ * Keep track of the number of total resulting notes, which starts out
+ * as the number of notes in dest. Loop through the src notes.
+ */
+ totnotes = dest_p->nnotes;
+ for (sidx = 0; sidx < src_p->nnotes; sidx++) {
+ snote_p = &src_p->notelist[sidx];
+
+ /* see if it matches any of the dest notes in the result array*/
+ for (didx = 0; didx < dest_p->nnotes; didx++) {
+ dnote_p = &newlist[didx];
+ if (dnote_p->octave == snote_p->octave &&
+ dnote_p->letter == snote_p->letter) {
+ /*
+ * If one has parens on the note or acc and the
+ * other doesn't, ensure no parens in result.
+ */
+ if (dnote_p->note_has_paren !=
+ snote_p->note_has_paren) {
+ dnote_p->note_has_paren = NO;
+ }
+ if (dnote_p->acc_has_paren !=
+ snote_p->acc_has_paren) {
+ dnote_p->acc_has_paren = NO;
+ }
+ break;
+ }
+ }
+ if (didx == dest_p->nnotes) {
+ /* didn't find a match, so add it to the end */
+ newlist[totnotes++] = *snote_p;
+ }
+ }
+
+ /* sort the combined list in pitch order */
+ qsort((char *)newlist, (unsigned int)totnotes,
+ sizeof (struct NOTE), notecomp);
+
+ /*
+ * For every original dest and src note, find out where it ended up in
+ * the combined list, and update and location tag references.
+ */
+ for (didx = 0; didx < dest_p->nnotes; didx++) {
+ dnote_p = &dest_p->notelist[didx];
+ for (n = 0; n < totnotes; n++) {
+ if (newlist[n].octave == dnote_p->octave &&
+ newlist[n].octave == dnote_p->letter) {
+ upd_ref(newlist[n].c, dnote_p->c);
+ }
+ }
+ }
+ for (sidx = 0; sidx < src_p->nnotes; sidx++) {
+ dnote_p = &src_p->notelist[sidx];
+ for (n = 0; n < totnotes; n++) {
+ if (newlist[n].octave == dnote_p->octave &&
+ newlist[n].octave == dnote_p->letter) {
+ upd_ref(newlist[n].c, dnote_p->c);
+ }
+ }
+ }
+
+ /* free old dest list, and install the new one and its note count */
+ if (dest_p->notelist != 0) { /* not a space */
+ FREE(dest_p->notelist);
+ }
+ dest_p->notelist = newlist;
+ dest_p->nnotes = totnotes;
+}
+\f
+/*
+ * Name: setgrpptrs()
+ *
+ * Abstract: Set pointers to the groups in a hand.
+ *
+ * Returns: number of GRPSYLs found in this hand
+ *
+ * Description: This function fill in the given array with pointers to the
+ * groups in this hand. When a group doesn't exist for some
+ * voice, a null pointer is used.
+ */
+
+static int
+setgrpptrs(gs1_p, v_p)
+
+struct GRPSYL *gs1_p; /* first group in this hand */
+struct GRPSYL *v_p[]; /* fill this in, index by voice number */
+
+{
+ struct GRPSYL *gs_p; /* point along GRPSYLs of the chord */
+ int v; /* a voice number */
+ int num; /* number of groups found */
+
+
+ /* null out the list of pointers initially (note [0] is not used) */
+ for (v = 1; v <= MAXVOICES; v++) {
+ v_p[v] = 0;
+ }
+ /* for each group in the hand, set pointer to it */
+ num = 0;
+ for (gs_p = gs1_p; gs_p != 0 && gs_p->staffno == gs1_p->staffno &&
+ gs_p->grpsyl == GS_GROUP; gs_p = gs_p->gs_p) {
+ v_p[gs_p->vno] = gs_p;
+ num++;
+ }
+
+ return (num);
+}
+\f
+/*
+ * Name: hastieslur()
+ *
+ * Abstract: Is this group tied/slurred/bent to the next?
+ *
+ * Returns: YES or NO
+ *
+ * Description: This function checks whether there are any ties/slurs/bends to
+ * the next group. If the given group doesn't exist, the answer
+ * is NO.
+ */
+
+static int
+hastieslur(gs_p)
+
+struct GRPSYL *gs_p; /* group to check */
+
+{
+ int idx; /* index into note list */
+
+
+ /* if there's no group, it isn't tied/slurred */
+ if (gs_p == 0) {
+ return (NO);
+ }
+
+ /* check group tie */
+ if (gs_p->tie == YES) {
+ return (YES);
+ }
+
+ /* check note ties and slurs (bends are covered by slurs) */
+ for (idx = 0; idx < gs_p->nnotes; idx++) {
+ if (gs_p->notelist[idx].tie == YES ||
+ gs_p->notelist[idx].nslurto != 0) {
+ return (YES);
+ }
+ }
+
+ return (NO);
+}