--- /dev/null
+/* Copyright (c) 1995, 1997, 1998, 1999, 2000, 2002, 2004, 2005 by Arkkra Enterprises */
+/* All rights reserved */
+/*
+ * Name: trnspose.c
+ *
+ * Description: This file contains functions for transposing to different keys.
+ */
+
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+#define BAD (99) /* a bad interval */
+
+/*
+ * For each possible transposition, this table shows the change in the number
+ * of sharps in whatever key we are in. Invalid intervals are marked as BAD.
+ */
+static short Delshtab[5][8] = {
+ /* 1 2 3 4 5 6 7 */
+
+ { 0, -7, -12, -10, -8, -6, -11, -9 }, /* d */
+ { 0, BAD, -5, -3, BAD, BAD, -4, -2 }, /* m */
+ { 0, 0, BAD, BAD, -1, 1, BAD, BAD }, /* P */
+ { 0, BAD, 2, 4, BAD, BAD, 3, 5 }, /* M */
+ { 0, 7, 9, 11, 6, 8, 10, 12 }, /* A */
+};
+
+/* index this by an interval type to get a string naming it */
+static char *Inttab[] =
+ { "diminished", "minor", "perfect", "major", "augmented" };
+
+/*
+ * The following hold the transposition information for the score and all the
+ * staffs. After every bar line it is updated if the transposition changed.
+ * The score's info is stored in inttype[0], intnum[0], and octint[0].
+ */
+static int inttype[MAXSTAFFS+1]; /* interval type of simple interval */
+static int intnum[MAXSTAFFS+1]; /* simple interval (>0, octs removed)*/
+static int octint[MAXSTAFFS+1]; /* number of octaves in interval */
+
+
+static void transnote P((struct GRPSYL *g_p, struct NOTE *n_p, int inttype,
+ int intnum, int octint));
+static void translurto P((struct GRPSYL *g_p, struct NOTE *n_p, int tnum,
+ int toct));
+static void simptrans P((int origtype, int orignum, int *inttype_p,
+ int *intnum_p, int *octint_p));
+static void fixslurto P((int s, struct MAINLL *mainll_p, int nintnum,
+ int noctint));
+\f
+/*
+ * Name: transgroups()
+ *
+ * Abstract: Transpose all GRPSYLs by the requested intervals.
+ *
+ * Returns: void
+ *
+ * Description: This function loops through the main linked list, applying
+ * SSVs to keep the transpositions of the score and each staff
+ * up to date. Whenever it hits a STAFF, it loops through all
+ * the GRPSYLs in the linked list(s) for the voice(s), changing
+ * all the affected information. It also loops through all the
+ * chords, transposing them.
+ */
+
+void
+transgroups()
+
+{
+ struct MAINLL *mainll_p; /* point along main LL */
+ struct GRPSYL *g_p; /* point along LL of groups */
+ struct STUFF *stuff_p; /* point at stuff, looking for chords*/
+ int tinttype, tintnum; /* current total transposition */
+ int ninttype, nintnum, noctint; /* new transposition in standard form*/
+ int v; /* voice number */
+ int s; /* staff number */
+ int n; /* loop variable */
+ int gotssv; /* seen an SSV since the last STAFF? */
+ int rtran; /* rest "transposition" */
+
+
+ debug(16, "transgroups");
+ initstructs(); /* clean out old SSV info */
+
+ /*
+ * Loop through the rest of the main linked list, applying SSVs to keep
+ * the tranposition info up to date, and processing linked lists of
+ * groups and STUFF.
+ */
+ gotssv = YES; /* force init of arrays at start even if no SSVs */
+ for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) {
+
+ switch (mainll_p->str) {
+ case S_SSV:
+ asgnssv(mainll_p->u.ssv_p);
+ gotssv = YES;
+ break;
+
+ case S_STAFF:
+ if (gotssv == YES) {
+ /*
+ * This is the first staff encountered after
+ * hitting SSV(s). If the transposition has
+ * changed on any staff, update any slurto
+ * lists in the previous measure that go across
+ * this bar line, and reset the transposition
+ * tables in preparation to processing this
+ * staff and the rest of the staffs in this
+ * measure.
+ */
+ for (s = 1; s <= Score.staffs; s++) {
+ /*
+ * Convert this staff's new transpostion
+ * to standard form, storing it in
+ * local variables.
+ */
+ totaltrans(s, &tinttype, &tintnum);
+ simptrans(tinttype, tintnum,
+ &ninttype, &nintnum, &noctint);
+
+ /*
+ * If num or oct changed since last
+ * measure, we have to fix up slurto
+ * lists. Interval type is irrelevant.
+ */
+ if (nintnum != intnum[s] ||
+ noctint != octint[s])
+ fixslurto(s, mainll_p,
+ nintnum, noctint);
+
+ /* store whether changed or not */
+ inttype[s] = ninttype;
+ intnum[s] = nintnum;
+ octint[s] = noctint;
+ }
+
+ /* do the score; no slurtos to worry about */
+ totaltrans(0, &tinttype, &tintnum);
+ simptrans(tinttype, tintnum,
+ &inttype[0], &intnum[0], &octint[0]);
+
+ gotssv = NO;
+ }
+
+ /* the staff we're supposed to work on */
+ s = mainll_p->u.staff_p->staffno;
+
+ /* loop through stuff list, transposing chords */
+ for (stuff_p = mainll_p->u.staff_p->stuff_p;
+ stuff_p != 0; stuff_p = stuff_p->next) {
+ if (stuff_p->string != 0 &&
+ stuff_p->modifier == TM_CHORD)
+ stuff_p->string = tranchstr(stuff_p->
+ string, stuff_p->all ? 0 : s);
+ }
+
+ /* never transpose tablature staff */
+ if (is_tab_staff(s))
+ continue;
+
+ /* don't transpose notes if a normal clef is not to */
+ /* be printed */
+ if (svpath(s, STAFFLINES)->printclef != SS_NORMAL)
+ continue;
+
+ /* loop through all voices that can exist */
+ for (v = 0; v < MAXVOICES; v++) {
+ /*
+ * Loop through the voice's list of GRPSYLs.
+ * If the voice doesn't exist, the loop will
+ * execute 0 times.
+ */
+ for (g_p = mainll_p->u.staff_p->groups_p[v];
+ g_p != 0; g_p = g_p->next) {
+
+ if (g_p->grpcont == GC_NOTES) {
+ for (n = 0; n < g_p->nnotes;
+ n++) {
+ transnote(g_p, &g_p->
+ notelist[n],
+ inttype[s],
+ intnum[s],
+ octint[s]);
+ }
+ } else if (g_p->grpcont == GC_REST &&
+ g_p->restdist != NORESTDIST) {
+ /*
+ * The user hardcoded a rest's
+ * position, so "transpose" it.
+ * This is complicated by the
+ * fact that we normally want
+ * to force an even result so
+ * that it will look good.
+ */
+ /* vertical stepsize shift */
+ rtran = 7 * octint[s] +
+ intnum[s] - 1;
+
+ if (EVEN(g_p->restdist)) {
+ g_p->restdist += rtran;
+ /*
+ * Force even result,
+ * rounded away from
+ * center line.
+ */
+ if (ODD(g_p->restdist)){
+ g_p->restdist =
+ g_p->restdist > 0 ?
+ g_p->restdist + 1 :
+ g_p->restdist - 1;
+ }
+ } else {
+ /* no rounding */
+ g_p->restdist += rtran;
+
+ /* warn if odd result*/
+ if (ODD(g_p->restdist)){
+ l_warning(
+ g_p->inputfile,
+ g_p->inputlineno,
+ "'dist' on rest is an odd number, which may look bad");
+ }
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+}
+\f
+/*
+ * Name: transnote()
+ *
+ * Abstract: transpose a note
+ *
+ * Returns: void
+ *
+ * Description: This function alters a NOTE structure according to the given
+ * transposition. This involves changing the note itself (letter,
+ * accidental, and octave) and any notes in its slurred-to list
+ * (letter and octave).
+ */
+
+static void
+transnote(g_p, n_p, ttype, tnum, toct)
+
+struct GRPSYL *g_p; /* ptr to note's group, used only in error messages */
+register struct NOTE *n_p; /* pointer to the note structure */
+int ttype; /* interval type (DIMINISHED, MINOR, . . .) */
+int tnum; /* simple interval (positive, with octaves removed) */
+int toct; /* number of octaves in interval */
+
+{
+ int oldaccnum; /* old accidental number (&&=0, &=1, ...) */
+ int newaccnum; /* new accidental number (&&=0, &=1, ...) */
+ int oldcircnum; /* position of old note in circle of 5ths */
+ int newcircnum; /* position of new note in circle of 5ths */
+ char newlet; /* new note letter */
+ char newacc; /* new accidental letter */
+ int newoct; /* new octave number */
+
+
+ /*
+ * First do the note itself: letter, accidental, octave.
+ */
+ /* calculate new note letter from old */
+ newlet = (n_p->letter - 'a' + tnum - 1) % 7 + 'a';
+
+ if (n_p->accidental == '\0') {
+ newacc = '\0'; /* no acc before, so no acc now */
+ /* set as if natural, for benefit of error messages later */
+ oldaccnum = strchr(Acclets, 'n') - Acclets;
+ } else {
+ /*
+ * There was an accidental, so we need to get the proper
+ * transposition of it. Get position of the old note letter
+ * in the circle of 5ths, and the old accidental index.
+ * The index to the new note letter is shifted by delsh.
+ * If this falls outside the circle string, change the index
+ * and accidental until it lies within the string.
+ */
+ oldcircnum = strchr(Circle, n_p->letter) - Circle;
+ oldaccnum = strchr(Acclets, n_p->accidental) - Acclets;
+ newaccnum = oldaccnum;
+ newcircnum = oldcircnum + Delshtab [ ttype ] [ tnum ];
+ while (newcircnum < 0) {
+ newaccnum--; /* one more flat */
+ newcircnum += 7; /* 7 letters "sharper" */
+ }
+ while (newcircnum >= 7) {
+ newaccnum++; /* one more sharp */
+ newcircnum -= 7; /* 7 letters "flatter" */
+ }
+
+ /* test for accidental overflow */
+ if (newaccnum < 0 || newaccnum > 4) {
+ l_ufatal(g_p->inputfile, g_p->inputlineno,
+ "note %c%s%d is transposed to have triple sharp or flat",
+ n_p->letter, Acctostr[oldaccnum], n_p->octave);
+ }
+
+ newacc = Acclets[newaccnum];
+ }
+
+ /*
+ * Calculate the new octave. Add toct (number of octaves to
+ * transpose) to the old octave. Then, add tnum to the old note
+ * number. If it exceeds a 7th, wrap into the next octave.
+ */
+ newoct = n_p->octave + toct;
+ if (Letshift[n_p->letter - 'a'] + tnum - 1 >= 7)
+ newoct++;
+
+ /* check for octave overflow, and exit if so */
+ if (newoct < MINOCTAVE || newoct > MAXOCTAVE) {
+ l_ufatal(g_p->inputfile, g_p->inputlineno,
+ "note %c%s%d octave is transposed out of range",
+ n_p->letter, Acctostr[oldaccnum], n_p->octave);
+ }
+
+ /* store away the new values */
+ n_p->letter = newlet;
+ n_p->accidental = newacc;
+ n_p->octave = (short)newoct;
+
+
+ /*
+ * Now do any notes in the slurred-to list, notes this note is slurred
+ * to: letter, octave. (There is never an accidental here.)
+ */
+ translurto(g_p, n_p, tnum, toct);
+}
+\f
+/*
+ * Name: translurto()
+ *
+ * Abstract: transpose a note's slurred-to list
+ *
+ * Returns: void
+ *
+ * Description: This function is given a pointer to a note and a transposition.
+ * It transposes the note's slurto list. Notice that the "type"
+ * of transposition interval is not needed, since these lists
+ * never contain accidentals.
+ */
+
+static void
+translurto(g_p, n_p, tnum, toct)
+
+struct GRPSYL *g_p; /* note's group, used only in error messages */
+struct NOTE *n_p; /* note whose slurto list is to be transposed */
+int tnum; /* transposition interval number */
+int toct; /* transposition interval octave */
+
+{
+ int s; /* index into slurto list */
+ char newlet; /* new note letter */
+ int newoct; /* new octave number */
+
+
+ /* loop through each note (if any) in the slurred-to list */
+ for (s = 0; s < n_p->nslurto; s++) {
+
+ /* if this is a slur to or from nowhere, don't change it */
+ if (IS_NOWHERE(n_p->slurtolist[s].octave))
+ continue;
+
+ /* calculate new note letter from old */
+ newlet = (n_p->slurtolist[s].letter - 'a' + tnum - 1) % 7 + 'a';
+
+ newoct = n_p->slurtolist[s].octave + toct;
+ if (Letshift[n_p->slurtolist[s].letter - 'a'] + tnum - 1 >= 7)
+ newoct++;
+
+ /* check for octave overflow, and exit if so */
+ if (newoct < MINOCTAVE || newoct > MAXOCTAVE) {
+ l_ufatal(g_p->inputfile, g_p->inputlineno,
+ "note in slurred-to list transposed to out of range octave (%c%d)",
+ newlet, newoct);
+ }
+
+ /* store away the new values */
+ n_p->slurtolist[s].letter = newlet;
+ n_p->slurtolist[s].octave = (short)newoct;
+ }
+}
+\f
+/*
+ * Name: tranchnote()
+ *
+ * Abstract: transpose a note name that's inside a chord symbol
+ *
+ * Returns: void
+ *
+ * Description: This function is given a letter and accidental that occur
+ * inside a chord symbol. It could be the main chord name itself,
+ * or the name of a note, like the E in "C/E" or "DaddE". It
+ * returns a pointer to a static area containing the transposed
+ * string.
+ */
+
+char *
+tranchnote(letter, acc, s)
+
+int letter; /* A to G */
+int acc; /* one of: 'x', '#', '\0', '&', 'B' */
+int s; /* staff number, needed to get tranposition interval */
+ /* 0 means the score as a whole ("all") */
+
+{
+ static char circle[] = "FCGDAEB"; /* circle of 5ths */
+ static char newchord[3]; /* put transposed result here */
+
+ int oldaccnum; /* old accidental number (&&=0, &=1, ...) */
+ int newaccnum; /* new accidental number (&&=0, &=1, ...) */
+ int oldcircnum; /* position of old note in circle of 5ths */
+ int newcircnum; /* position of new note in circle of 5ths */
+ char newlet; /* new note letter */
+ char newacc; /* new accidental letter */
+
+
+ debug(32, "tranchnote letter=%c acc=%c s=%d", letter,
+ acc==0 ? ' ' : acc, s);
+ /* need to translate naturals so that strchr can use Acclets[] */
+ if (acc == '\0')
+ acc = 'n';
+
+ /* calculate new note letter from old */
+ newlet = (letter - 'A' + intnum[s] - 1) % 7 + 'A';
+
+ /*
+ * Get the proper transposition of the accidental. Get position of the
+ * old note letter in the circle of 5ths, and the old accidental index.
+ * The index to the new note letter is shifted by delsh. If this falls
+ * outside the circle string, change the index and accidental until it
+ * lies within the string.
+ */
+ oldcircnum = strchr(circle, letter) - circle;
+ oldaccnum = strchr(Acclets, acc) - Acclets;
+ newaccnum = oldaccnum;
+ newcircnum = oldcircnum + Delshtab [ inttype[s] ] [ intnum[s] ];
+ while (newcircnum < 0) {
+ newaccnum--; /* one more flat */
+ newcircnum += 7; /* 7 letters "sharper" */
+ }
+ while (newcircnum >= 7) {
+ newaccnum++; /* one more sharp */
+ newcircnum -= 7; /* 7 letters "flatter" */
+ }
+
+ /* test for accidental overflow */
+ if (newaccnum < 0 || newaccnum > 4)
+ ufatal("chord note %c%s is transposed to have triple sharp or flat",
+ letter, Acctostr[oldaccnum]);
+
+ newacc = Acclets[newaccnum];
+
+ /* store away the new values */
+ newchord[0] = newlet;
+ newchord[1] = (newacc == 'n' ? '\0' : newacc);
+ newchord[2] = '\0';
+
+ return (newchord);
+}
+\f
+/*
+ * Name: eff_key()
+ *
+ * Abstract: Return the "effective key" (the key after any transposition).
+ *
+ * Returns: the number of sharps in the effective key (flats are negative)
+ *
+ * Description: This function, given a staff number, returns the number of
+ * sharps currently in effect, considering any transpostion that
+ * may have been requested. If given 0 for the staff number, it
+ * does this for the score's key signature. It assumes the SSVs
+ * are up to date.
+ */
+
+int
+eff_key(staff)
+
+int staff; /* staff number to do it for (0 = score) */
+
+{
+ int sharps; /* sharps in old key (flats count negative) */
+ int origtype; /* original transposition interval type */
+ int orignum; /* original transposition interval number */
+ int inttype; /* interval type of simple interval */
+ int intnum; /* simple interval (positive, octs removed) */
+ int octint; /* number of octaves in interval */
+ int newsharps; /* sharps in key after transposition */
+
+
+ /*
+ * If no normal clef is to be printed, always treat it like there is
+ * no key signature.
+ */
+ if (staff == 0) {
+ if (Score.printclef != SS_NORMAL)
+ return (0);
+ } else {
+ if (svpath(staff, STAFFLINES)->printclef != SS_NORMAL)
+ return (0);
+ }
+
+ /* viewpath to get this staff's current key and transposition */
+ if (staff == 0) {
+ sharps = Score.sharps;
+ } else {
+ sharps = svpath(staff, SHARPS)->sharps;
+ }
+ totaltrans(staff, &origtype, &orignum);
+
+ simptrans(origtype, orignum, &inttype, &intnum, &octint);
+
+ /*
+ * Change number of sharps by the appropriate delta. We assume the
+ * interval isn't BAD, because the parser wouldn't have allowed it.
+ */
+ newsharps = sharps + Delshtab [ inttype ] [ intnum ];
+
+ /* make sure the resulting key is valid */
+ if (newsharps < -7 || newsharps > 7) {
+ /*
+ * Normally we take the final "else" here. But for the "1"
+ * interval there is an ambiguity. If transpose + addtranspose
+ * add up to an aug or dim 1, there are two ways to state the
+ * result. (Also for per 1, but in that case, we'd never have
+ * an invalid key.) So we state both ways of looking at it.
+ */
+ if (orignum == 1 && origtype == AUGMENTED ||
+ orignum == -1 && origtype == DIMINISHED) {
+ ufatal("staff %d: key of %d %s transposed up by augmented 1 or down diminished 1 results in %d %s",
+ staff,
+ abs(sharps),
+ (sharps >= 0 ? "sharps" : "flats"),
+ abs(newsharps),
+ (newsharps >= 0 ? "sharps" : "flats"));
+ } else if (orignum == -1 && origtype == AUGMENTED ||
+ orignum == 1 && origtype == DIMINISHED) {
+ ufatal("staff %d: key of %d %s transposed down by augmented 1 or up diminished 1 results in %d %s",
+ staff,
+ abs(sharps),
+ (sharps >= 0 ? "sharps" : "flats"),
+ abs(newsharps),
+ (newsharps >= 0 ? "sharps" : "flats"));
+ } else {
+ ufatal("staff %d: key of %d %s transposed %s by %s %d results in %d %s",
+ staff,
+ abs(sharps),
+ (sharps >= 0 ? "sharps" : "flats"),
+ (orignum > 0 ? "up" : "down"),
+ Inttab[origtype],
+ abs(orignum),
+ abs(newsharps),
+ (newsharps >= 0 ? "sharps" : "flats"));
+ }
+ }
+ return (newsharps);
+}
+\f
+/*
+ * Name: simptrans()
+ *
+ * Abstract: Simplify a transpostion into standard form.
+ *
+ * Returns: void
+ *
+ * Description: This function, given a transposition, converts it into
+ * standard form (a "simple" upwards interval and an octave).
+ */
+
+static void
+simptrans(origtype, orignum, inttype_p, intnum_p, octint_p)
+
+int origtype; /* original transposition interval type */
+int orignum; /* original transposition interval number */
+int *inttype_p; /* interval type (DIMINISHED, MINOR, . . .) */
+int *intnum_p; /* simple interval (positive, with octaves removed) */
+int *octint_p; /* number of octaves in interval */
+
+{
+ int direction; /* UP or DOWN */
+
+
+ *inttype_p = origtype;
+ *intnum_p = orignum;
+
+ /* set direction; if down, make intnum positive */
+ if (*intnum_p > 0) {
+ direction = UP;
+ } else {
+ direction = DOWN;
+ *intnum_p = -*intnum_p;
+ }
+
+ /* break interval into octaves plus a simple interval */
+ *octint_p = (*intnum_p - 1) / 7;
+ *intnum_p -= 7 * *octint_p;
+
+ /* if downwards, adjust so that *intnum_p is upwards */
+ if (direction == DOWN) {
+ if (*intnum_p == 1) {
+ /* for unison, negate octaves and reverse intvl type */
+ *octint_p = -*octint_p;
+ *inttype_p = 4 - *inttype_p;
+ } else {
+ /* for other intervals, octave becomes one less than */
+ /* negation, and *intnum_p flips as does its type */
+ *octint_p = -1 - *octint_p;
+ *intnum_p = 9 - *intnum_p;
+ *inttype_p = 4 - *inttype_p;
+ }
+ }
+}
+\f
+/*
+ * Name: fixslurto()
+ *
+ * Abstract: Fix transposition of notes in a slurred-to list.
+ *
+ * Returns: void
+ *
+ * Description: Notes in a slurred-to list are initially transposed the same as
+ * the regular notes in that measure. But if they occur in a
+ * group immediately before a bar line where the transposition
+ * changes, they should have been transposed according to the new
+ * transposition. This function is called when entering the new
+ * measure. It searches back and finds any such slurred-to lists
+ * in the previous measure and fixes their transposition.
+ */
+
+static void
+fixslurto(s, mainll_p, nintnum, noctint)
+
+int s; /* staff number */
+struct MAINLL *mainll_p; /* initially points at current staff */
+int nintnum; /* interval number of new transposition */
+int noctint; /* octaves in new transposition */
+
+{
+ struct GRPSYL *g_p; /* point to a group */
+ int deltanum, deltaoct; /* change in transposition */
+ int v; /* voice number */
+ int n; /* loop index */
+
+
+ /* search back to the last staff of the preceding measure, if any */
+ for (mainll_p = mainll_p->prev; mainll_p != 0 &&
+ mainll_p->str != S_STAFF; mainll_p = mainll_p->prev)
+ ;
+ if (mainll_p == 0)
+ return; /* no preceding measure; nothing to do */
+
+ /* search back to the matching staff in that measure, if any */
+ while (mainll_p != 0 && mainll_p->str == S_STAFF &&
+ mainll_p->u.staff_p->staffno != s)
+ mainll_p = mainll_p->prev;
+ if (mainll_p == 0 || mainll_p->str != S_STAFF)
+ return; /* no matching staff (no. of staffs changed) */
+ /* we found a matching staff in the preceding measure */
+
+ /*
+ * "Subtract" the old transposition from the new one, to find the
+ * "delta" transposition. This is what we need to apply to the
+ * slurred-to notes to change them from the old to new transposition.
+ */
+ deltanum = nintnum - intnum[s] + 1;
+ deltaoct = noctint - octint[s];
+ if (deltanum < 1) {
+ deltanum += 7;
+ deltaoct--;
+ }
+
+ /*
+ * mainll_p now points to the matching staff in the preceding measure.
+ * Loop through all voices that can exist.
+ */
+ for (v = 0; v < MAXVOICES; v++) {
+ g_p = mainll_p->u.staff_p->groups_p[v];
+ if (g_p == 0)
+ continue;
+
+ /* find the last grpsyl in this voice */
+ while (g_p->next != 0)
+ g_p = g_p->next;
+
+ /* if it doesn't contain notes, there is nothing to do */
+ if (g_p->grpcont != GC_NOTES)
+ continue;
+
+ /* found a group with notes at end of measure; process it */
+ for (n = 0; n < g_p->nnotes; n++) {
+ translurto(g_p, &g_p->notelist[n], deltanum, deltaoct);
+ }
+ }
+}
+\f
+/*
+ * Name: totaltrans()
+ *
+ * Abstract: Find the current total transposition of a staff or the score.
+ *
+ * Returns: void
+ *
+ * Description: This function is given a staff number, or zero for the score.
+ * It assumes that the SSVs are up to date. It gets the two
+ * tranposition parameters, and adds them to get the current
+ * total transpostion. If it's invalid, it does a ufatal.
+ */
+
+void
+totaltrans(s, type_p, num_p)
+
+int s; /* staff number, or 0 for score */
+int *type_p; /* return type of resulting transposition */
+int *num_p; /* return number of resulting transposition */
+
+{
+ /*
+ * inths is to be indexed by interval type and number (1 through 7).
+ * It gives the number of half steps in the interval.
+ */
+ static short inths[5][8] = {
+ /* 1 2 3 4 5 6 7 */
+
+ { 0, -1, 0, 2, 4, 6, 7, 9 }, /* d */
+ { 0, BAD, 1, 3, BAD, BAD, 8, 10 }, /* m */
+ { 0, 0, BAD, BAD, 5, 7, BAD, BAD }, /* P */
+ { 0, BAD, 2, 4, BAD, BAD, 9, 11 }, /* M */
+ { 0, 1, 3, 5, 6, 8, 10, 12 }, /* A */
+ };
+
+ struct SSV *ssv_p; /* point at the SSV hold a transposition */
+ int type[2]; /* interval types (DIMINISHED, MINOR, . . .) */
+ int num[2]; /* interval numbers (down is negative) */
+ int totalhs; /* total half steps in resulting interval */
+ char place[15]; /* temp storage for error message use */
+ int offset; /* like interval no. but counting from 0 */
+ int n; /* loop variable */
+
+
+ /* get the type and num of each transpostion interval */
+ ssv_p = svpath(s, TRANSPOSITION);
+ type[0] = ssv_p->inttype;
+ num[0] = ssv_p->intnum;
+ ssv_p = svpath(s, ADDTRANSPOSITION);
+ type[1] = ssv_p->addinttype;
+ num[1] = ssv_p->addintnum;
+
+ /*
+ * To get the interval number of the num of the transpostions, we
+ * basically add the two. But musicians unfortunately start counting
+ * intervals from 1 instead of 0, so we have to play some games.
+ */
+ offset = (num[0] > 0 ? num[0] - 1 : num[0] + 1) + /* add true offsets*/
+ (num[1] > 0 ? num[1] - 1 : num[1] + 1);
+ if (offset >= 0) { /* get interval number from offset */
+ *num_p = offset + 1;
+ } else {
+ *num_p = offset - 1;
+ }
+
+ /* accumulate total half steps in both transpositions */
+ totalhs = 0;
+ for (n = 0; n < NUMELEM(num); n++) {
+ if (num[n] > 0) { /* interval is up */
+ while (num[n] > 7) {
+ num[n] -= 7; /* subtract an octave */
+ totalhs += 12; /* add 12 half steps */
+ }
+ /* account for this simple interval */
+ totalhs += inths[type[n]][num[n]];
+ } else { /* interval is down */
+ while (num[n] < -7) {
+ num[n] += 7; /* add an octave */
+ totalhs -= 12; /* subtract 12 half steps */
+ }
+ /* account for this simple interval */
+ totalhs -= inths[type[n]][abs(num[n])];
+ }
+ }
+
+ /* if interval is down, find the up version of offset and halfsteps */
+ if (offset < 0) {
+ offset = -offset;
+ totalhs = -totalhs;
+ }
+ /* bring it into the range of a simple interval */
+ totalhs -= (offset / 7) * 12;
+ offset %= 7;
+
+ /* make sure this simple interval is valid */
+ if (totalhs < inths[DIMINISHED][offset + 1] ||
+ totalhs > inths[AUGMENTED ][offset + 1]) {
+
+ if (s == 0) {
+ (void)sprintf(place, "score");
+ } else {
+ (void)sprintf(place, "staff %d", s);
+ }
+ ufatal("on %s, 'transpose' %s %s %d and 'addtranspose' %s %s %d add up to an invalid interval",
+ place,
+ num[0] > 0 ? "up" : "down", Inttab[type[0]], abs(num[0]),
+ num[1] > 0 ? "up" : "down", Inttab[type[1]], abs(num[1]));
+ }
+
+ /* search table for the type of interval this is; it will be found */
+ for (n = DIMINISHED; n <= AUGMENTED; n++) {
+ if (totalhs == inths[n][offset + 1]) {
+ break;
+ }
+ }
+
+ *type_p = n;
+}