/************************************************* * The PMW Music Typesetter - 3rd incarnation * *************************************************/ /* Copyright (c) Philip Hazel, 1991 - 2019 */ /* Written by Philip Hazel, starting November 1991 */ /* This file last modified: August 2019 */ /* This file contains code for drawing ties and glissandos. */ #include "pmwhdr.h" #include "outhdr.h" #include "pagehdr.h" /* Different actions are required for single notes and for chords. It is simplest to separate these out into entirely separate routines. */ /************************************************ * Output a tie (or short slur) * ************************************************/ /* This function is called to draw a tie between two single notes. They need not have the same pitch, in which case it is really a slur. The variable n_prevtie points to the tie block for the first note, while n_nexttie points to the ongoing tie block, if any. Arguments: x1 the x coordinate of the end of the tie/slur endline TRUE if at end of line flags tie flags specifying type of tie (editorial, dashed, dotted) Returns: nothing */ void out_setnotetie(int x1, BOOL endline, int flags) { BOOL startline = FALSE; BOOL above = n_prevtie->abovecount > 0; BOOL leftup = out_laststemup[out_stave]; b_notestr *left = n_prevtie->note; int slurflags; int tietype; int dstart = 0; int dstop = 1000; int adjustL = 0; int adjustR = 0; int co = 0; int x0 = bar_cont->tiex; int y0 = left->spitch; int y1 = n_pitch; int yy0, yy1; /* If this note is further tied, arrange to leave a bit of a gap in the tie marks. */ int joinedties = (n_nexttie != NULL && n_prevtie->abovecount == n_nexttie->abovecount)? main_stavemagn : 0; /* Check for coupling of first note */ mac_couplepitch(y0, left->flags); /* If the final pitch is zero, make the tie horizontal. This can arise at the end of a line if a tie continues over a totally empty bar. */ if (y1 == 0) y1 = y0; /* Set slur flags and initialize tie type; raise the pitch when the tie is above. */ if (above) { slurflags = tietype = 0; y0 += 8; y1 += 8; } else { slurflags = sflag_b; tietype = 1; } if ((flags & tief_editorial) != 0) slurflags |= sflag_e; if ((flags & tief_dashed) != 0) slurflags |= sflag_i; if ((flags & tief_dotted) != 0) slurflags |= sflag_i | sflag_idot; /* Save basic levels for accidental checking */ yy0 = y0; yy1 = y1; /* Compute the rest of the "tie type" value. There are eight possibilities: 0 stems up, tie above 1 stems up, tie below 2 stems down, tie above 3 stems down, tie below 4 first stem up, tie above 5 first stem up, tie below 6 first stem down, tie above 7 first stem down, tie below This value is used for checking on stem crossings, etc. Continuation ties can only be types 0-3. Also, certain checks on the left end are skipped for continued ties. Remember the fact for style handling. */ if (x0 == 0) { startline = TRUE; if (!n_upflag) tietype += 2; x0 = out_barx - 10*main_stavemagn; y0 = yy0 = y1; } /* A non-continued tie */ else { if (leftup != n_upflag) tietype += 4; if (!leftup) tietype += 2; /* Check for stem crossing at the left-hand end */ if (left->notetype >= minim && (tietype == 0 || tietype == 4)) adjustL += 5*main_stavemagn; /* Check for dotted note at the left-hand end */ if (above && leftup && (left->flags & nf_dot) != 0) { adjustL += 3*main_stavemagn; if ((left->flags & nf_dot2) != 0) adjustL += (35*main_stavemagn)/10; } /* Check for staccato etc. on the left-hand note */ if ((tietype == 1 || tietype == 2 || tietype == 5 || tietype == 6) && (left->acflags & af_dyninside) != 0 && (left->acflags & af_opposite) == 0) { int z = 6 - (y0 & 2); int zz = ((left->acflags & af_ring) == 0)? 2 : 3; if (leftup) y0 -= (y0 == 132)? 4 : (y0 <= 130)? zz : z; else y0 += (y0 == 148)? 4 : (y0 >= 150)? zz : z; } } /* Check on stem crossing at the right-hand end, unless this is an invisible note. */ if (n_notetype >= minim && (tietype == 3 || tietype == 5) && (bar_cont->flags & cf_notes) != 0) adjustR -= 4*main_stavemagn - joinedties; /* Check for staccato etc. at the right-hand end (of a slur, not a tie, of course). */ if ((tietype == 1 || tietype == 2 || tietype == 4 || tietype == 7) && (n_acflags & af_dyninside) != 0 && (n_acflags & af_opposite) == 0) { int z = 6 - (y1 & 2); int zz = ((n_acflags & af_ring) == 0)? 2 : 3; if (n_upflag) y1 -= (y1 == 132)? 4 : (y1 <= 130)? zz : z; else y1 += (y1 == 148)? 4 : (y1 >= 150)? zz : z; } /* Check for enough space if the final note has an accidental. Then ensure that ties do not start or end on stave lines */ if (above) { if (!endline && n_acc >= ac_flat && y1 <= yy1) { y1++; if (x1 - x0 <= 16000 && yy1 > yy0) { y1++; y0++; } } if (y0 <= 148 && (y0 & 2) == 0) y0++; if (y1 <= 148 && (y1 & 2) == 0) y1++; } else { if (!endline && n_acc >= ac_natural && y1 >= yy1) { y1 -= 3; if (x1 - x0 < 16000 && yy1 < yy0) co += 1000; } if (y0 >= 132 && (y0 & 2) == 0) y0--; if (y1 >= 132 && (y1 & 2) == 0) y1--; } /* If the slur is very steep, make it a bit more curvy, and adjust the right hand end in some cases. Otherwise, for very short slurs, make flatter. */ if (abs(y0 - y1) > 10) { co += 2000; } else if (x1 - x0 > 10000) co -= 1000; /* If this is really a tie (the two notes have the same pitch) then it should be horizontal. One end may have been moved to avoid accents, etc. If this is the case, we adjust the other end to keep the tie horizontal. */ if (n_pitch == left->spitch) { if ((above && y0 > y1) || (!above && y0 < y1)) y1 = y0; else y0 = y1; } /* If this is really a slur, make sure that we haven't negated its sense by moving one end to account for accents. If we have, make it at least horizontal. Which end to move depends on whether the tie is above or below. */ else if (n_pitch > left->spitch) { if (y1 < y0) { if ((tietype & 1) == 0) y1 = y0; else y0 = y1; } } else { if (y1 > y0) { if ((tietype & 1) == 0) y0 = y1; else y1 = y0; } } /* When printing right-to-left, certain ties need some horizontal adjustment to cope with stem positions. */ if (main_righttoleft) switch (tietype) { case 0: adjustL -= 4*main_stavemagn; adjustR -= 4*main_stavemagn; break; case 3: adjustL += 4*main_stavemagn; adjustR += 4*main_stavemagn; break; case 5: adjustR += 4*main_stavemagn; break; case 6: adjustR -= 4*main_stavemagn; break; case 7: adjustL += 4*main_stavemagn; break; default: break; } /* Set up the final coordinates, taking into account the style of continued ties, and then output it. */ x0 += adjustL; y0 = (y0 - 132)*main_stavemagn; x1 += adjustR - joinedties; y1 = (y1 - 132)*main_stavemagn; if (endline && curmovt->endlinetiestyle != 0) { x1 += x1 - x0; y1 += y1 - y0; dstop = 500; } else if (startline && curmovt->endlinetiestyle != 0) { x0 -= x1 - x0; y0 -= y1 - y0; dstart = 500; } out_slur(x0, y0, x1, y1, slurflags, co, dstart, dstop); } /************************************************ * Output ties on a chord * ************************************************/ /* The variable tiecount is set to the number of ties which are in the "abnormal" direction for the stem direction. A zero value for x0 means we are continuing at the start of a line. Arguments: notelist pointer to the first note of the chort notecount number of notes in the chord x1 the x coordinate of the end of the ties endflag TRUE if drawing to end of line tieflags type of tie Returns: nothing */ void out_setchordtie(b_notestr **notelist, int notecount, int x1, BOOL endflag, int tieflags) { b_notestr *left = n_prevtie->note; usint acflags = 0; int tiecount = n_upflag? n_prevtie->abovecount : n_prevtie->belowcount; int x0 = bar_cont->tiex; int leftup = out_laststemup[out_stave]; int flags = 0; int slurflags = 0; int count, continued; /* If this note is further tied, arrange to leave a bit of a gap in the tie marks. */ int joinedties = (n_nexttie != NULL && n_prevtie->abovecount == n_nexttie->abovecount)? 1000 : 0; if ((tieflags & tief_editorial) != 0) slurflags |= sflag_e; if ((tieflags & tief_dashed) != 0) slurflags |= sflag_i; if ((tieflags & tief_dotted) != 0) slurflags |= sflag_i | sflag_idot; /* Collect all the flags from the chord, for staccato etc. We have to do this because the notes may be in either order. */ do { flags |= left->flags; acflags |= left->acflags; mac_advancechord(left); } while (left->type == b_chord); /* Set start position for continuations at start of line */ if (x0 == 0) { x0 = out_barx - 10*main_stavemagn; continued = TRUE; } else continued = FALSE; /* Process each note in the chord. We scan the right-hand side chord (supplied in notelist), and tie only those notes which correspond in pitch with a note in the left-hand side. */ for (count = 0; count < notecount; count++) { int found = TRUE; b_notestr *right = notelist[count]; left = n_prevtie->note; while (right->truepitch != left->truepitch) { mac_advancechord(left); if (left->type != b_chord) { found = FALSE; break; } } if (found) { int below = (tiecount > 0 && !n_upflag) || (tiecount <= 0 && n_upflag); int y0 = right->spitch; int xx0, xx1; int adjustL = 0; int adjustR = 0; int dstart = 0; int dstop = 1000; int type; mac_couplepitch(y0, right->flags); if (!below) y0 += 8; /* For each tie, we must determine whether it is inside or outside the chord. A tie is outside if it is EITHER (a) the last one, and in the "normal" direction, unless the stems are in opposite directions and the left one is up, in which case it is "half outside" (b) the first one, and in the abnormal direction, AND EITHER the first note has no stem OR the first note's stem is down or we are at a line start AND EITHER the second note has no stem OR the stem is up OR we are at a line end (for which "no stem" is in fact set) There are two cases where notes are "half outside", which occur when case (b) is true except that a note required not to have a stem does in fact have one. We encode the cases in the variable "type" as follows: 0 => outside 1 => inside 2 => right inside 3 => left inside because otherwise the code is tortuous and repetitious. Another "half outside" case has come to light because an end note of the right-hand chord may match a middle note of the left-hand chord. */ /* Handle tie in normal direction for the last note */ if ((count == notecount - 1 && tiecount <= 0)) { type = (leftup == n_upflag || !leftup || (flags & nf_stem) == 0)? 0 : 3; /* Deal with non-end left-hand note */ if (type == 0 && leftup) { b_notestr *nleft = left; mac_advancechord(nleft); if (nleft->type == b_chord) type = 3; } } /* Handle tie in abnormal direction for the first note */ else if (count == 0 && tiecount > 0) { if ((flags & nf_stem) == 0 || !leftup || continued) { type = ((n_flags & nf_stem) == 0 || n_upflag)? 0 : 2; if (type == 0 && !endflag && right->spitch - (notelist[count+1])->spitch == -2) type = 2; } else type = !n_upflag? 2 : 3; } /* All other cases are inside */ else type = 1; /* When printing right-to-left, things change! */ if (main_righttoleft) switch (type) { case 0: if (n_upflag && !leftup) type = below? 3: 2; break; case 2: if (!n_upflag) type = leftup? 0 : 3; break; case 3: if (leftup) type = n_upflag? 2 : 0; break; default: break; } /* Now make adjustments according to the type */ switch (type) { case 0: /* outside */ case 2: /* right inside */ adjustR = (type == 0)? -joinedties : -3750; /* Check for dynamics on the first chord and adjust the position if necessary. (We assume no dynamics on second chord). */ if ((count == 0 && leftup != n_upflag) || (count == notecount - 1 && leftup == n_upflag)) { if ((acflags & af_dyninside) != 0 && (acflags & af_opposite) == 0) { int z = 6 - (y0 & 2); if (leftup) y0 -= (y0 <= 128)? 4 : z; else y0 += (y0 >= 152)? 4 : z; } } break; case 1: /* inside */ case 3: /* left inside */ if (!continued) { adjustL = 4500; if ((flags & nf_plus) != 0) adjustL += 8000; else if ((flags & nf_dot) != 0) { adjustL += 4000; if ((flags & nf_dot2) != 0) adjustL += 3500; } } if (type == 3) { adjustR = -joinedties; break; /* that's all for left inside */ } if (!endflag) adjustR = -3750; /* Deal with dots moved right (will apply only to stems up) */ if (!continued && (flags & (nf_dot | nf_dotright)) == (nf_dot | nf_dotright)) adjustL += 5500; /* Else deal with intervals of a second */ else if (n_upflag) { if (!continued && count != notecount - 1 && right->spitch - (notelist[count+1])->spitch == 2) adjustL += 5500; } else { if (!endflag && ((count != notecount - 1 && right->spitch - (notelist[count+1])->spitch == -2) || (count != 0 && tiecount > 0 && right->spitch - (notelist[count-1])->spitch == 2))) adjustR -= 5500; } /* Inside ties are adjusted for pitch */ y0 += below? 3 : -3; break; } /* If notes are currently switched off, we have just printed an invisible note. Cancel any horizontal adjustment. This fudge is a cunning way of printing hanging ties. */ if ((bar_cont->flags & cf_notes) == 0) adjustR = 0; /* Correct adjustments for stave magnification */ adjustL = (adjustL*main_stavemagn)/1000; adjustR = (adjustR*main_stavemagn)/1000; /* Now make sure the tie does not end on a staff line, and convert from a pitch value to a points offset. */ if ((y0 & 3) == 0) { if (below) { if (y0 >= 128) y0--; } else if (y0 <= 144) y0++; } /* Set up the final coordinates, taking into account the style of continued ties, and then output it. */ xx0 = x0 + adjustL; xx1 = x1 + adjustR; y0 = (y0 - 132)*main_stavemagn; if (endflag && curmovt->endlinetiestyle != 0) { xx1 += xx1 - xx0; dstop = 500; } else if (continued && curmovt->endlinetiestyle != 0) { xx0 -= xx1 - xx0; dstart = 500; } out_slur(xx0, y0, xx1, y0, (below? sflag_b : 0) | slurflags, (xx1 - xx0 > 10000)? (-main_stavemagn) : 0, dstart, dstop); } /* Adjust the count of those on the "abnormal" side, whether or not we actually printed a tie. */ tiecount--; } } /************************************************* * Output a glissando mark * *************************************************/ /* The positions of the ends of the line are adjusted according to whether the notes are on lines or spaces, and whether or not the right hand one has accidentals. Arguments: x1 the x coordinate of the end of the mark flags type of line required (editorial, dashed, dotted) Returns: nothing */ void out_glissando(int x1, int flags) { b_notestr *left = n_prevtie->note; int x0 = bar_cont->tiex; int y0 = left->spitch; int y1 = n_pitch; /* Check for coupling of first note */ mac_couplepitch(y0, left->flags); if (x0 == 0) /* at start of line */ { x0 = out_barx - 6*main_stavemagn; y0 = (y0+y1)/2; } else x0 += 8*main_stavemagn; if ((left->flags & nf_dot) != 0) { x0 += 4*main_stavemagn; if ((left->flags & nf_dot2) != 0) x0 += (35*main_stavemagn)/10; } x1 -= (15*main_stavemagn)/10 + n_accleft; if ((y0 & 2) == 0) y0 += (y1 > y0)? (+2) : (-2); if ((y1 & 2) == 0) y1 += (y1 > y0)? (-2) : (+2); ps_line(x0, (y0 - 128)*main_stavemagn, x1, (y1 - 128)*main_stavemagn, (3*main_stavemagn)/10, flags); } /* End of settie.c */