+
+/* Copyright (c) 1995, 1997, 1998, 1999, 2001, 2002 by Arkkra Enterprises */
+/* All rights reserved */
+
+/* This file contains functions for handling ties and slurs.
+ * This includes doing error checking to make sure
+ * there is a note to tie/slur to.
+ * There is also code to add padding to make space for ties/slurs
+ * that are carried into second endings. */
+
+#include "defines.h"
+#include "structs.h"
+#include "globals.h"
+
+
+
+/* lengths of line segments of carried in tie marks */
+#define HALFTIEPAD (3 * STEPSIZE)
+
+/* infomation about a tied or slurred note that gets carried into an ending */
+struct TIECARRY {
+ short letter; /* pitch to be tied or slurred to */
+ short octave; /* octave of the pitch */
+ short curveno; /* slurto index or -1 for tie */
+ short is_bend; /* if slurto is actually a bend */
+ struct MAINLL *mll_p; /* points to first group */
+ struct GRPSYL *gs_p; /* group of first note */
+ struct TIECARRY *next; /* linked list */
+};
+
+/* linked list of tie carry info for each staff/voice */
+struct TIECARRY *Tiecarryinfolist_p [MAXSTAFFS + 1] [MAXVOICES];
+
+/* flag to mark if there are any carry ins */
+static short Have_carry_ins = NO;
+
+
+/* static functions */
+static void do_tie P((struct MAINLL *mll_p));
+static void do_group_ties P((struct GRPSYL *gs_p, struct MAINLL *mll_p));
+static void chk_slides P((struct NOTE *note_p, struct GRPSYL *gs_p,
+ struct MAINLL *mll_p));
+static void chk_following4slide P((struct NOTE *note_p, struct GRPSYL *gs_p,
+ struct MAINLL *mll_p));
+static struct MAINLL *do_carry_ties P((struct MAINLL *staff_mll_p,
+ struct MAINLL *bar_mll_p));
+static void savetieinfo P((struct MAINLL *mll_p, struct GRPSYL *gs_p));
+static void do_save_tieinfo P((int staffno, int vno, int letter, int octave,
+ int is_tie, struct MAINLL *mainll_p, struct GRPSYL *gs_p,
+ int is_bend));
+static void carryin_ties P((struct MAINLL *mll_p));
+static void add_carryin P((struct STAFF *staff_p));
+static void free_carryin_info P((void));
+static void free_cinfo P((struct TIECARRY *carryinfo_p));
+static void chk4xpose P((struct MAINLL *mll_p));
+static void chkxpstaff P((struct MAINLL *mll_p, int s));
+static void chkxpgrp P((struct GRPSYL *gs_p, char *inputfile, int lineno));
+static void set_inhibitprint_if_appropriate P((struct GRPSYL *gs_p,
+ struct MAINLL *mll_p));
+\f
+
+
+/* go through main list, checking each STAFF struct for ties and slurs.
+ * For each, do appropriate error checking */
+
+void
+tie()
+
+{
+ struct MAINLL * mll_p; /* walk through main list */
+
+
+ debug(2, "tie");
+
+ /* first check for any ties across transpositions, and delete them.
+ * Can't do inside the next loop, because by then a less informative
+ * message could be generated for the error */
+ initstructs();
+ for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0;
+ mll_p = mll_p->next) {
+ if (mll_p->str == S_SSV) {
+ chk4xpose(mll_p);
+ asgnssv(mll_p->u.ssv_p);
+ }
+ }
+
+ /* go through the main list again for other checks */
+ initstructs();
+ for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0;
+ mll_p = mll_p->next) {
+
+ /* process any GRPSYL lists with note info */
+ if (mll_p->str == S_STAFF) {
+ if ( (svpath(mll_p->u.staff_p->staffno, VISIBLE))
+ ->visible == YES) {
+ do_tie(mll_p);
+ }
+ }
+ else if (mll_p->str == S_SSV) {
+ asgnssv(mll_p->u.ssv_p);
+ }
+ }
+}
+\f
+
+/* do the ties and slurs on all groups off a STAFF struct */
+
+static void
+do_tie(mll_p)
+
+struct MAINLL *mll_p; /* the STAFF struct with list of GRPSYLs */
+
+{
+ struct GRPSYL *gs_p; /* walk through GRPSYL list */
+ int v; /* walk through voices per staff */
+ int is_tab; /* YES if tablature staff */
+
+
+ is_tab = is_tab_staff(mll_p->u.staff_p->staffno);
+
+ for (v = 0; v < MAXVOICES; v++) {
+ /* go through entire list of GRPSYLs */
+ for (gs_p = mll_p->u.staff_p->groups_p[v];
+ gs_p != (struct GRPSYL *) 0;
+ gs_p = gs_p->next) {
+
+ /* error check */
+ if (gs_p->grpcont != GC_NOTES) {
+ if (gs_p->tie == YES) {
+ l_warning(mll_p->inputfile,
+ mll_p->inputlineno,
+ "tie can only apply to notes");
+ gs_p->tie = NO;
+ }
+
+ /* if rest or space, nothing more to do */
+ continue;
+ }
+
+ do_group_ties(gs_p, mll_p);
+ if (is_tab == YES) {
+ set_inhibitprint_if_appropriate(gs_p, mll_p);
+ }
+ }
+ }
+}
+\f
+
+/* do ties on all notes in a group that have ties. While we're at it, also
+ * make sure any staffs with clefs not printed, don't have accidentals on any
+ * notes */
+
+static void
+do_group_ties(gs_p, mll_p)
+
+struct GRPSYL *gs_p; /* do ties from this group */
+struct MAINLL *mll_p; /* points to gs_p */
+
+{
+ struct GRPSYL *gs2_p; /* group to tie to */
+ register int n; /* walk through note list */
+ struct NOTE *note1_p; /* info about note */
+ struct NOTE *note2_p; /* note slurred to */
+ int slur; /* index into slurtolist */
+ int d; /* index for deleting illegal slurs */
+ short err = NO; /* error flag */
+ short inhibitprint; /* if to set inhibitprint flag on the
+ * tied-to group (tab staff only) */
+
+
+ /* if all notes in a group are tied on a tablature staff,
+ * then all the corresponding notes on the tied-to group
+ * should have their inhibitprint flag set. So we need to
+ * find out if that is a possibility */
+ if (is_tab_staff(gs_p->staffno) == YES) {
+ if (gs_p->tie == YES) {
+ inhibitprint = YES;
+ }
+ else {
+ /* first assume that all notes will be tied, then
+ * check them all. If any are found that are not
+ * tied, then turn the inhibitprint flag back off */
+ inhibitprint = YES;
+ for (n = 0; n < gs_p->nnotes; n++) {
+ if (gs_p->notelist[n].tie == NO) {
+ inhibitprint = NO;
+ break;
+ }
+ }
+ }
+ }
+ else {
+ /* inhibitprint flag is only used on tablature staffs */
+ inhibitprint = NO;
+ }
+
+ /* go through all notes in group, looking for ties */
+ gs2_p = (struct GRPSYL *) 0;
+ for (n = 0; n < gs_p->nnotes; n++) {
+
+ /* For staffs without clef, make sure there are no accidentals.
+ * But tab staffs keep accidentals, even with no clef.
+ * Philosophically, this isn't really a good place to
+ * do this, but since we're going through the list of notes
+ * anyway here, and time-wise in the scheme of the program
+ * this is the right time, rather than make yet another trip
+ * through the list later we do it here. But for MIDI
+ * purposes, we need to keep the accidental, or the wrong
+ * note will play! */
+ if (svpath(gs_p->staffno, STAFFLINES)->printclef != SS_NORMAL
+ && is_tab_staff(gs_p->staffno) == NO
+ && Doing_MIDI == NO) {
+ gs_p->notelist[n].accidental = '\0';
+ }
+
+ note1_p = &(gs_p->notelist[n]);
+ /* if this note's tie flag is set, check the tie */
+ if ( note1_p->tie == YES) {
+
+ /* if haven't yet found the group to tie to, do that.
+ * (If this is not the first note to be tied
+ * in this group, we would have already found
+ * the other group) */
+ if (gs2_p == (struct GRPSYL *) 0) {
+ gs2_p = find_next_group(mll_p, gs_p, "tie");
+ }
+
+ if (gs2_p == (struct GRPSYL *) 0) {
+ /* if nothing to tie to, cancel the tie. We
+ * will have already printed an error msg. */
+ note1_p->tie = NO;
+ gs_p->tie = NO;
+ }
+ else {
+ /* if the inhibitprint flag is set, set it
+ * in the tied-to group. However,
+ * if the groups have different numbers of
+ * notes and the inhibitprint flag is set,
+ * we have to cancel it, because that only
+ * applies when the tied-from and tied-to
+ * groups have identical notes in them */
+ if (inhibitprint == YES) {
+ if (gs_p->nnotes == gs2_p->nnotes) {
+ gs2_p->inhibitprint = YES;
+ }
+ /* turn flag off, so we don't
+ * waste time checking it
+ * again for each of the remaining
+ * notes in the group */
+ inhibitprint = NO;
+ }
+
+ if (find_matching_note(gs2_p, note1_p->letter,
+ note1_p->octave, "tie")
+ == (struct NOTE *) 0) {
+ note1_p->tie = NO;
+ gs_p->tie = NO;
+ }
+ }
+ }
+
+
+ /* handle all slurs from current note */
+ for (slur = 0; slur < note1_p->nslurto; slur++) {
+
+ /* slides to/from nowhere don't get processed here */
+ if (IS_NOWHERE(note1_p->slurtolist[slur].octave)) {
+ continue;
+ }
+
+ /* if haven't yet found the group to slur to, do that.
+ * (We may have already found it earlier) */
+ if (gs2_p == (struct GRPSYL *) 0) {
+ gs2_p = find_next_group(mll_p, gs_p, "slur");
+ }
+
+ if (gs2_p == (struct GRPSYL *) 0) {
+ /* if nothing to slur to, cancel all slurs */
+ note1_p->nslurto = 0;
+ continue;
+ }
+
+ /* special case of 'M' when a group 'slur'
+ * has been specified. Find matching note
+ * in the second chord */
+ if (note1_p->slurtolist[slur].letter == 'M') {
+ if (gs_p->nnotes != gs2_p->nnotes) {
+ /* only print message first time */
+ if (err == NO) {
+ l_warning(gs_p->inputfile,
+ gs_p->inputlineno,
+ "'slur' requires equal number of notes in each chord");
+ }
+ note2_p = (struct NOTE *) 0;
+
+ /* don't do any more on this
+ * chord, to avoid multiple
+ * error messages */
+ err = YES;
+ }
+ else {
+ note2_p = & (gs2_p->notelist[n]);
+ }
+ }
+ else {
+ note2_p = find_matching_note(gs2_p,
+ note1_p->slurtolist[slur].letter,
+ note1_p->slurtolist[slur].octave,
+ note1_p->is_bend ? "bend" : "slur");
+ }
+
+ if (note2_p != (struct NOTE *) 0) {
+ /* fill in the letter/octave if they had to
+ * be derived */
+ if ((note1_p->slurtolist[slur].letter == 'U')
+ || (note1_p->slurtolist[slur].letter == 'M')
+ || is_tab_staff(gs_p->staffno) == YES) {
+ note1_p->slurtolist[slur].letter =
+ note2_p->letter;
+ note1_p->slurtolist[slur].octave =
+ note2_p->octave;
+ }
+ }
+ else {
+ /* discard this slur--
+ * nothing to slur to */
+ for (d = slur + 1; d < note1_p->nslurto; d++) {
+ note1_p->slurtolist[d-1] =
+ note1_p->slurtolist[d];
+ }
+ (note1_p->nslurto)--;
+ }
+ }
+
+ /* do additional slide checks for tab and tabnote */
+ if (is_tab_staff(gs_p->staffno) == YES
+ || (gs_p->staffno < MAXSTAFFS
+ && is_tab_staff(gs_p->staffno + 1) == YES)) {
+ chk_slides(note1_p, gs_p, mll_p);
+ }
+ }
+}
+
+/* do extra checks for slide. There can be no more than one incoming and one
+ * outgoing slide for any given note */
+
+static void
+chk_slides(note_p, gs_p, mll_p)
+
+struct NOTE *note_p; /* this note might have slides */
+struct GRPSYL *gs_p; /* note is in this group */
+struct MAINLL *mll_p; /* group is tied to this main list struct */
+
+{
+ int s; /* index through slurtolist */
+ int incoming = 0, outgoing = 0; /* how many slides of each type */
+
+
+ /* go through list counting up incoming and outgoing slides */
+ for (s = 0; s < note_p->nslurto; s++) {
+ switch(note_p->slurtolist[s].octave) {
+
+ case OUT_UPWARD:
+ case OUT_DOWNWARD:
+ outgoing++;
+ break;
+
+ case IN_UPWARD:
+ case IN_DOWNWARD:
+ incoming++;
+ break;
+
+ default:
+ outgoing++;
+ /* make sure following group doesn't have any
+ * incoming nowhere slides */
+ chk_following4slide(note_p, gs_p, mll_p);
+ break;
+ }
+ }
+
+ if (incoming > 1) {
+ l_yyerror(gs_p->inputfile, gs_p->inputlineno,
+ "can't have more than one slide into a note");
+ }
+ if (outgoing > 1) {
+ l_yyerror(gs_p->inputfile, gs_p->inputlineno,
+ "can't have more than one slide from a note");
+
+ }
+}
+\f
+
+/* Given a note with a slide to a specific fret,
+ * if there is a following group, see if it has a matching note,
+ * and if so, check that note's slurtolist to see if it
+ * contains any incoming nowhere slides. If so, there is a
+ * problem, because we already have a slide to that note */
+
+static void
+chk_following4slide(note_p, gs_p, mll_p)
+
+struct NOTE *note_p; /* this note has a slide to a specific fret */
+struct GRPSYL *gs_p; /* note is in this group */
+struct MAINLL *mll_p; /* group is attached to this main list struct */
+
+{
+ struct GRPSYL *ngs_p; /* next group */
+ int n; /* index through notes */
+ int ns; /* index through slides on next group note */
+
+
+ if ((ngs_p = nextgrpsyl(gs_p, &mll_p)) == (struct GRPSYL *) 0) {
+ /* no next group, so no problem */
+ return;
+ }
+
+ /* check each note in next group */
+ for (n = 0; n < ngs_p->nnotes; n++) {
+ /* is this the matching note? If the letter matches and
+ * either it's a tab staff or the octave also matches,
+ * then it is the matching note. */
+ if (ngs_p->notelist[n].letter == note_p->letter &&
+ (is_tab_staff(gs_p->staffno) ||
+ ngs_p->notelist[n].octave == note_p->octave)) {
+
+ /* found the matching note. Check its slurtolist */
+ for (ns = 0; ns < ngs_p->notelist[n].nslurto; ns++) {
+ switch (ngs_p->notelist[n].slurtolist[ns].octave) {
+ case IN_UPWARD:
+ case IN_DOWNWARD:
+ l_yyerror(gs_p->inputfile,
+ gs_p->inputlineno,
+ "can't slide to note that has </n> or <\\n>");
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+}
+\f
+
+/* find note in following chord having specified pitch/octave.
+ * If the note is found, return pointer to it, otherwise 0
+ */
+
+struct NOTE *
+find_matching_note(gs_p, letter, octave, type)
+
+struct GRPSYL *gs_p; /* which GRPSYL we're tying to */
+int letter; /* find note with this pitch, 'a' to 'g' */
+int octave; /* find note with this octave */
+char *type; /* "tie", "slur", "slide", or "bend",
+ * or null if not to print any error messages */
+
+{
+ struct NOTE *note2_p; /* note to tie to */
+ register int n2; /* index through notelist of 2nd group */
+
+
+ if (gs_p == (struct GRPSYL *) 0) {
+ return( (struct NOTE *) 0);
+ }
+
+ /* we don't allow tie/slur into a measure repeat. */
+ if (is_mrpt(gs_p) == YES) {
+ l_warning(gs_p->inputfile, gs_p->inputlineno,
+ "tie/slur/bend not allowed into measure rpt");
+ return (struct NOTE *) 0;
+ }
+
+ /* special case. On slurto, if second group has only a single
+ * note, user doesn't have to specify it. We will have marked the
+ * pitch as 'U'. If second group has only one note in it, use that
+ * one. If not, error */
+ if ( letter == 'U') {
+ if ( gs_p->nnotes != 1) {
+ if (type != (char *) 0) {
+ l_warning(gs_p->inputfile, gs_p->inputlineno,
+ "note to %s to not specified", type);
+ }
+ return(struct NOTE *) 0;
+ }
+ else {
+ return( &(gs_p->notelist[0]) );
+ }
+ }
+
+ /* try to find matching note in second note group */
+ /* If first note has an accidental and the corresponding one in
+ * the next group doesn't, that's a
+ * match, 'cause we only print the accidental once.
+ */
+ for (n2 = 0; n2 < gs_p->nnotes; n2++) {
+
+ note2_p = &(gs_p->notelist[n2]);
+
+ if (is_tab_staff(gs_p->staffno) == YES) {
+ /* on tab staff, we just have to match
+ * the string number */
+ if (note2_p->letter == letter) {
+ return(note2_p);
+ }
+ else {
+ continue;
+ }
+ }
+
+ if ( (note2_p->letter == letter)
+ && (note2_p->octave == octave) ) {
+
+ if (type != (char *) 0 && (strcmp(type, "tie") == 0)
+ && (note2_p->accidental != '\0')
+ && is_tab_staff(gs_p->staffno) == NO) {
+ l_warning(gs_p->inputfile, gs_p->inputlineno,
+ "second note of tie not allowed to have an accidental");
+ /* fix it so in case we're called again on the
+ * same note (which is possible), we'll only
+ * print one error message */
+ note2_p->accidental = '\0';
+ }
+
+ /* found it! */
+ return(note2_p);
+ }
+ }
+
+ /* oh-oh. User goofed */
+ if (is_tab_staff(gs_p->staffno) == YES) {
+ if (type != (char *) 0) {
+ l_yyerror(gs_p->inputfile, gs_p->inputlineno,
+ "can't do %s: %s string not in chord%s",
+ type, stringname(letter, gs_p->staffno),
+ gs_p->nnotes == 0 ?
+ " (in fact no strings at all)" : "");
+ }
+ }
+ else {
+ if (type != (char *) 0) {
+ if (letter < 'a' || letter > 'g' || octave < MINOCTAVE
+ || octave > MAXOCTAVE) {
+ l_warning(gs_p->inputfile, gs_p->inputlineno,
+ "can't do %s: note not in chord",
+ type);
+ }
+ else {
+ l_warning(gs_p->inputfile, gs_p->inputlineno,
+ "can't do %s: %c%d not in chord%s",
+ type, letter, octave,
+ gs_p->nnotes == 0 ?
+ " (in fact no notes at all)" : "");
+ }
+ }
+ }
+ return( (struct NOTE *) 0 );
+}
+\f
+
+/* given one GRPSYL, find the next one in the same staff and voice,
+ * which might be in the next measure */
+
+struct GRPSYL *
+find_next_group(mll_p, gs_p, type)
+
+struct MAINLL *mll_p; /* current place in main list */
+struct GRPSYL *gs_p; /* group to tie from */
+char *type; /* "tie" or "slur" */
+
+{
+ struct MAINLL *ml_p;
+
+ ml_p = mll_p;
+ if ((gs_p = nextgrpsyl(gs_p, &ml_p)) == (struct GRPSYL *) 0) {
+ l_warning(mll_p->inputfile, mll_p->inputlineno,
+ "no chord to %s to", type);
+ }
+
+ return(gs_p);
+}
+\f
+
+/* go through main list. If we hit a bar that begins an ending, back up
+ * and go through the previous measure. If the final group of any voice
+ * has any tied or slurred notes, save information about them. Then for
+ * each additional beginning of an ending up until an endending, add
+ * user padding to allow for carried in tie mark. At the endending, free
+ * the information and continue through the rest of the main list */
+
+void
+tie_carry()
+
+{
+ struct MAINLL *mll_p; /* walk through main list */
+ struct MAINLL *first_staff_mll_p; /* points to first STAFF
+ * struct of measure */
+
+
+ debug(2, "tie_carry");
+
+ initstructs();
+ first_staff_mll_p = (struct MAINLL *) 0;
+ for (mll_p = Mainllhc_p; mll_p != (struct MAINLL *) 0;
+ mll_p = mll_p->next) {
+ switch (mll_p->str) {
+
+ case S_STAFF:
+ /* remember where list of staffs begins and skip
+ * the rest of the STAFFs */
+ first_staff_mll_p = mll_p;
+ for ( ; mll_p->next->str == S_STAFF;
+ mll_p = mll_p->next) {
+ ;
+ }
+ break;
+
+ case S_BAR:
+ if (mll_p->u.bar_p->endingloc == STARTITEM) {
+ mll_p = do_carry_ties(first_staff_mll_p, mll_p);
+ }
+ break;
+
+ case S_CLEFSIG:
+ /* actually, it should be impossible to hit this case,
+ * because clefsigs with pseudo-bar haven't been
+ * created yet at the time this is called, but if things
+ * are changed some day so things get done in a different
+ * order, this should then work. */
+ if (mll_p->u.clefsig_p->bar_p != (struct BAR *) 0 &&
+ mll_p->u.clefsig_p->bar_p->endingloc
+ == STARTITEM) {
+ mll_p = do_carry_ties(first_staff_mll_p, mll_p);
+ }
+ break;
+
+ case S_SSV:
+ asgnssv(mll_p->u.ssv_p);
+ break;
+ default:
+ break;
+ }
+ }
+}
+\f
+
+/* Save info about any ties and slurs on the last chords before the beginning
+ * of the ending. Then search forward in main list. If there are any more
+ * beginnings of endings, add padding to the appropriate groups.
+ * Return MAINLL at the end of the last ending processed. */
+
+static struct MAINLL *
+do_carry_ties(staff_mll_p, bar_mll_p)
+
+struct MAINLL *staff_mll_p; /* first staff in measure which ends on
+ * bar that begins an ending */
+struct MAINLL *bar_mll_p; /* the bar that begins an ending */
+
+{
+ struct MAINLL *mll_p; /* walk through list of staffs */
+ int v; /* voice number */
+
+
+ /* save all the tie / slur info */
+ for (mll_p = staff_mll_p; mll_p->str == S_STAFF; mll_p = mll_p->next) {
+
+ for (v = 0; v < MAXVOICES; v++) {
+ savetieinfo(mll_p, mll_p->u.staff_p->groups_p[v]);
+ }
+ }
+
+ /* now search ahead for other endings */
+ for (mll_p = bar_mll_p->next; mll_p != (struct MAINLL *) 0;
+ mll_p = mll_p->next) {
+ if (mll_p->str != S_BAR) {
+ continue;
+ }
+
+ switch (mll_p->u.bar_p->endingloc) {
+ case NOITEM:
+ case ENDITEM:
+ free_carryin_info();
+ return(mll_p);
+ case STARTITEM:
+ carryin_ties(mll_p->next);
+ break;
+ default:
+ break;
+ }
+ }
+ pfatal("fell off end of list while doing tie carries");
+ /*NOTREACHED*/
+ return( (struct MAINLL *) 0);
+}
+\f
+
+/* given a GRPSYL, save info about any notes in it that have ties or slurs */
+
+static void
+savetieinfo(mll_p, gs_p)
+
+struct MAINLL *mll_p; /* main list struct that gs_p is connected to */
+struct GRPSYL *gs_p; /* save info about ties/slurs on last group
+ * in this list */
+
+{
+ int n; /* note index */
+ int s; /* slurto index */
+
+
+ if (gs_p == (struct GRPSYL *) 0) {
+ return;
+ }
+
+ /* find last group in list */
+ for ( ; gs_p->next != (struct GRPSYL *) 0; gs_p = gs_p->next) {
+ ;
+ }
+
+ for (n = 0; n < gs_p->nnotes; n++) {
+
+ /* save tie info */
+ if (gs_p->notelist[n].tie == YES) {
+ do_save_tieinfo(gs_p->staffno, gs_p->vno,
+ gs_p->notelist[n].letter,
+ gs_p->notelist[n].octave, -1,
+ mll_p, gs_p, NO);
+ }
+
+ /* save slurto info */
+ for (s = 0; s < gs_p->notelist[n].nslurto; s++) {
+ do_save_tieinfo(gs_p->staffno, gs_p->vno,
+ gs_p->notelist[n].slurtolist[s].letter,
+ gs_p->notelist[n].slurtolist[s].octave,
+ s, mll_p, gs_p,
+ gs_p->notelist[n].is_bend);
+ }
+ }
+}
+\f
+
+/* save info about one tie or slur mark that will need to be carried into
+ * subsequent endings. Malloc space for info, fill it in, and put into table */
+
+static void
+do_save_tieinfo(staffno, vno, letter, octave, curveno, mll_p, gs_p, is_bend)
+
+int staffno;
+int vno;
+int letter; /* a to g */
+int octave;
+int curveno;
+struct MAINLL *mll_p; /* points to first group */
+struct GRPSYL *gs_p; /* group of first note */
+int is_bend; /* YES if is actually a bend rather than slur */
+
+{
+ struct TIECARRY *new_p;
+
+ MALLOC(TIECARRY, new_p, 1);
+ new_p->letter = (short) letter;
+ new_p->octave = (short) octave;
+ new_p->curveno = (short) curveno;
+ new_p->is_bend = is_bend;
+ new_p->mll_p = mll_p;
+ new_p->gs_p = gs_p;
+ new_p->next = Tiecarryinfolist_p [staffno] [vno - 1];
+ Tiecarryinfolist_p [staffno] [vno - 1] = new_p;
+
+ Have_carry_ins = YES;
+}
+\f
+
+/* Once an ending has been found that may have ties/slurs carried in, use
+ * the saved information to add padding. */
+
+static void
+carryin_ties(mll_p)
+
+struct MAINLL *mll_p; /* look for staffs from here for chords that may have
+ * things tied or slurred in */
+
+{
+ if (Have_carry_ins == NO) {
+ /* nothing to do */
+ return;
+ }
+
+ /* skip everything up to STAFFS */
+ for ( ; mll_p != (struct MAINLL *) 0; mll_p = mll_p->next) {
+ if (mll_p->str == S_STAFF) {
+ add_carryin(mll_p->u.staff_p);
+ }
+ else if (mll_p->str == S_BAR) {
+ break;
+ }
+ }
+}
+\f
+
+/* given a STAFF which is at the beginning of an ending that may have ties/slurs
+ * carried in, go through each voice. If there is anything to carry in,
+ * add appropriate padding, then generate curve */
+
+static void
+add_carryin(staff_p)
+
+struct STAFF *staff_p; /* which staff to do ties/slur carry in on */
+
+{
+ int staffno;
+ int v; /* voice number */
+ int n; /* index into notelist */
+ struct GRPSYL *gs_p; /* first chord in measure */
+ struct TIECARRY *info_p; /* info about things carried in */
+ int found; /* if matching note found in chord */
+ double padding; /* how much padding to add */
+
+
+ staffno = staff_p->staffno;
+ /* do each carried in item on each voice */
+ for (v = 0; v < MAXVOICES; v++) {
+
+ padding = HALFTIEPAD;
+
+ for (info_p = Tiecarryinfolist_p [staffno] [v];
+ info_p != (struct TIECARRY *) 0;
+ info_p = info_p->next) {
+
+ gs_p = staff_p->groups_p[v];
+
+ /* add padding to allow for carried-in mark */
+ gs_p->padding += padding;
+ /* only add padding once per chord! */
+ padding = 0.0;
+
+ /* mark any notes that will get carried-in mark */
+ for (found = NO, n = 0; n < gs_p->nnotes; n++) {
+ if (gs_p->notelist[n].letter
+ == info_p->letter &&
+ gs_p->notelist[n].octave
+ == info_p->octave) {
+
+ /* A carried-in tie on a tablature
+ * staff isn't printed, but the fret
+ * is put in parentheses. */
+ if (is_tab_staff(staff_p->staffno) == YES
+ && info_p->curveno == -1) {
+ gs_p->notelist[n].FRET_HAS_PAREN = YES;
+ }
+ found = YES;
+ break;
+ }
+ }
+
+ if (found == NO) {
+ l_warning(gs_p->inputfile, gs_p->inputlineno,
+ "can't carry tie/slur/bend into ending: %c%d not in chord",
+ info_p->letter, info_p->octave);
+ }
+ }
+ }
+}
+\f
+
+/* free all the tie carry in info */
+
+static void
+free_carryin_info()
+
+{
+ int s;
+ int v;
+
+
+ for (s = 1; s <= MAXSTAFFS; s++) {
+ for (v = 0; v < MAXVOICES; v++) {
+ free_cinfo(Tiecarryinfolist_p [s] [v]);
+ Tiecarryinfolist_p [s] [v] = (struct TIECARRY *) 0;
+ }
+ }
+
+ Have_carry_ins = NO;
+}
+
+
+/* recursively free list of tie carry information */
+
+static void
+free_cinfo(carryinfo_p)
+
+struct TIECARRY *carryinfo_p;
+
+{
+ if (carryinfo_p == (struct TIECARRY *) 0) {
+ return;
+ }
+
+ free_cinfo(carryinfo_p->next);
+ FREE(carryinfo_p);
+}
+\f
+
+/* check if a transposition occurred, and if so, see if there were any
+ * ties that would cross the bar. If so, print warning and discard the tie */
+
+static void
+chk4xpose(mll_p)
+
+struct MAINLL *mll_p; /* containing SSV that might contain transpose */
+
+{
+ struct SSV *ssv_p;
+ int s; /* staff index */
+ int intnum; /* transposition interval */
+ int inttype; /* transposition interval type */
+
+
+ if (mll_p->str != S_SSV) {
+ return;
+ }
+
+ ssv_p = mll_p->u.ssv_p;
+ if (ssv_p->used[TRANSPOSITION] == YES ||
+ ssv_p->used[ADDTRANSPOSITION] == YES) {
+ /* this SSV changes transpose value, need to check further */
+ if (ssv_p->context == C_STAFF) {
+ /* if staff now has a different transpose value than
+ * before, need to see if any notes tied over the
+ * previous bar */
+ s = ssv_p->staffno;
+ totaltrans(s, &inttype, & intnum);
+ if (ssv_p->inttype != inttype
+ || ssv_p->intnum != intnum) {
+ chkxpstaff(mll_p, s);
+ }
+ }
+ else {
+ /* must be score wide change. This is a little
+ * trickier. Go through each staff. If its transpose
+ * value is not set in staff context and it's
+ * different than the new transpose value, then
+ * we need to check for ties */
+ for (s = 1; s <= Score.staffs; s++) {
+ totaltrans(0, &inttype, & intnum);
+ if (Staff[s].used[TRANSPOSITION] == NO &&
+ Staff[s].used[ADDTRANSPOSITION] == NO &&
+ (ssv_p->inttype != inttype
+ || ssv_p->intnum != intnum)) {
+ chkxpstaff(mll_p, s);
+ }
+ }
+ }
+ }
+}
+\f
+
+/* check a specific staff for possible ties across transposition */
+
+static void
+chkxpstaff(mll_p, s)
+
+struct MAINLL *mll_p; /* look backward in main list from here */
+int s; /* which staff */
+
+{
+ int v;
+
+
+ /* back up to find appropriate staff */
+ for (mll_p = mll_p->prev; mll_p != (struct MAINLL *) 0;
+ mll_p = mll_p->prev) {
+ if (mll_p->str == S_STAFF) {
+ if (mll_p->u.staff_p->staffno == s) {
+ /* found the correct staff. check each voice */
+ for (v = 0; v < MAXVOICES; v++) {
+ chkxpgrp(mll_p->u.staff_p->groups_p[v],
+ mll_p->inputfile,
+ mll_p->inputlineno);
+ }
+ return;
+ }
+ else if (mll_p->u.staff_p->staffno < s) {
+ /* user must have increased the number of
+ * staffs as well, so the staff in question
+ * didn't exist in previous measure */
+ return;
+ }
+ }
+ }
+}
+\f
+
+/* find the last group in a list of grpsyls. If it has any ties on it,
+ * print warning message for trying to tie across a transposition, and discard
+ * the tie(s). */
+
+static void
+chkxpgrp(gs_p, inputfile, lineno)
+
+struct GRPSYL *gs_p; /* check this grpsyl list */
+char *inputfile; /* for error message */
+int lineno;
+
+{
+ register int n; /* index through notelist */
+
+
+ /* find last group in list */
+ for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
+
+ if (gs_p->next == (struct GRPSYL *) 0) {
+ /* this is the last group in the measure. See if
+ * it has any ties on it */
+ for (n = 0; n < gs_p->nnotes; n++) {
+ if (gs_p->notelist[n].tie == YES) {
+ /* Aha! User tried to do a tie over
+ * a transpose */
+ l_warning(inputfile, lineno,
+ "can't tie into transposition change (use slur)");
+ /* cancel any and all ties on this grp,
+ * and return, so we don't print more
+ * than one error per group */
+ for (n = 0; n < gs_p->nnotes; n++) {
+ gs_p->notelist[n].tie = NO;
+ }
+ gs_p->tie = NO;
+ return;
+ }
+ }
+ }
+ }
+}
+\f
+
+/* On tablature staffs, if two consecutive groups are tied together,
+ * normally the frets are not printed for the second group, so we need
+ * to set the inhibitprint flag on the group. However if there is any
+ * reason why inhibiting printing on a given group isn't a good idea,
+ * we won't set the inhibitprint flag. So this function checks all the
+ * possible reasons for not setting inhibitprint, and if none of them
+ * apply, then it is set.
+ */
+
+static void
+set_inhibitprint_if_appropriate(gs_p, mll_p)
+
+struct GRPSYL *gs_p;
+struct MAINLL *mll_p;
+
+{
+ struct GRPSYL *nextgs_p; /* the group after gs_p */
+ struct GRPSYL *following_p; /* the group after next_gs_p */
+ int n; /* index through notelist */
+
+
+
+ if ((nextgs_p = nextgrpsyl(gs_p, &mll_p)) == (struct GRPSYL *) 0) {
+ /* no next group, so nothing to set */
+ return;
+ }
+
+ /* if this group and next group don't have same number of notes,
+ * then there won't be an inhibitprint on the next group */
+ if (gs_p->nnotes == 0 || gs_p->nnotes != nextgs_p->nnotes) {
+ return;
+ }
+
+ /* if next group has a "with" list, no inhibitprint */
+ if (nextgs_p->nwith != 0) {
+ return;
+ }
+
+ for (n = 0; n < gs_p->nnotes; n++) {
+ /* if any notes in this group are not tied, then there won't be
+ * an inhibitprint on the next group */
+ if (gs_p->notelist[n].tie == NO) {
+ return;
+ }
+
+ /* if next group has any slides to/from nowhere, or slurs
+ * to the next group, it won't get inhibitprint set */
+ if (nextgs_p->notelist[n].nslurto != 0) {
+ return;
+ }
+ }
+
+ /* next group has a bend of any sort, it doesn't get inhibitprint */
+ for (n = 0; n < nextgs_p->nnotes; n++) {
+ if (HASBEND(nextgs_p->notelist[n]) == YES) {
+ return;
+ }
+ }
+
+ /* if group following next group has a non-prebend bend, then the
+ * next group does not get inhibitprint */
+ if ((following_p = nextgrpsyl(nextgs_p, &mll_p)) != (struct GRPSYL *) 0) {
+ for (n = 0; n < following_p->nnotes; n++) {
+ if (HASBEND(following_p->notelist[n]) == YES &&
+ following_p->notelist[n].FRETNO == NOFRET) {
+ return;
+ }
+ }
+ }
+
+ /* Whew! If we got here, the group passed all the tests to have its
+ * inhibitprint flag set, so set it */
+ nextgs_p->inhibitprint = YES;
+}