X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mup/blobdiff_plain/cdb3c0882392596f814cf939cbfbd38adc6f2bfe..ddf6330b56bcfb657e0186b24b9b1422c51d3424:/mup/mup/tie.c diff --git a/mup/mup/tie.c b/mup/mup/tie.c new file mode 100644 index 0000000..41849e0 --- /dev/null +++ b/mup/mup/tie.c @@ -0,0 +1,1109 @@ + +/* 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)); + + + +/* 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); + } + } +} + + +/* 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); + } + } + } +} + + +/* 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"); + + } +} + + +/* 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 or <\\n>"); + break; + default: + break; + } + } + } + } +} + + +/* 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 ); +} + + +/* 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); +} + + +/* 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; + } + } +} + + +/* 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); +} + + +/* 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); + } + } +} + + +/* 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; +} + + +/* 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; + } + } +} + + +/* 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); + } + } + } +} + + +/* 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); +} + + +/* 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); + } + } + } + } +} + + +/* 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; + } + } + } +} + + +/* 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; + } + } + } + } +} + + +/* 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; +}