/************************************************* * The PMW Music Typesetter - 3rd incarnation * *************************************************/ /* Copyright (c) Philip Hazel, 1991 - 2019 */ /* Written by Philip Hazel, starting November 1991 */ /* This file last modified: November 2020 */ /* This file contains the code for generating an image of one individual note or rest. */ #include "pmwhdr.h" #include "pagehdr.h" #include "poshdr.h" #include "outhdr.h" /* Tables used only in this module */ static uschar common[] = { 49, 50, 52, 54, 56, 58, /* stems down */ 49, 50, 51, 53, 55, 57}; /* stems up */ static uschar *reststrings[] = { US"*", US"+", US",", US"-", US".", US"z.w{{y.", US"zzx.w{{y.w{{y.", US"zzzx.w{{y.w{{y.w{{y." }; static uschar *multireststrings[] = { /* Start for 2, last is for 8 */ US"*", US"*z+", US"*{{w*", US"*{{w*xz+", US"*{{w*xz*", US"*{{w*xz*z+", US"*{{w*xz*{{w*" }; static uschar *tailstrings[] = { US"", US"", US"", US"", US"H", US" v tp down up. */ static uschar *accabovestrings[] = { US"U", US"Y", US"W", US"\302\234", US"e", US"g" }; static uschar *accbelowstrings[] = { US"U", US"Z", US"X", US"\302\234", US"f", US"h" }; static int accaboveadjusts[] = { -6000, -1000, -2000, -1000, -1000, -1000 }; static int accbelowadjusts[] = { -2000, 3000, 0, 2000, 1000, 1000 }; /* Further per-accidental adjustments for accents */ /* - ## $ $$ % # */ static int accaccaboveadjusts[] = { 0, 0000, -3000, -3000, -3000, -3000 }; static int accaccbelowadjusts[] = { 0, 0000, 0000, 0000, 3000, 3000 }; /* Dot position adjustments for rests */ static int restdotadjusts[] = { -20, 0, 0, -25, -20, -10, -10, 0 }; /* Table of dynamic numbers in order of printing outside dynamics */ static uschar dyn_list[] = { dyn_gt, dyn_wedge, dyn_tp, dyn_vline, dyn_down, dyn_up }; /* Table for x adjustments for brackets for outside dynamics */ static int outdynxbadjusts[] = { 2000, 0, 2000, 0, 2000, 2000 }; static int outdynybadjusts[] = { -4000, 0, -1500, 0, -1000, -1000 }; /* These tables are for the straightforward ornaments. Those with blank strings are not straightforward, and have individual code. Note that characters greater than 127 must be represented as UTF-8. */ static const char *ornament_strings[] = { /* ferm tr trsh trfl trnat trem1 trem2 trem3 */ "", "", "", "", "", "", "", "", /* mord dmord imord dimord turn iturn arp arpu arpd spread */ "O", "P", "Q", "R", "S", "i", "", "", "", "", /* dsharp dsharprb dsharpsb */ "&", "~v\xc2\x8dv&~v\xc2\x8e", "~v\xc2\x8bv&~v\xc2\x8c", /* flat flatrb flatsb */ "\'", "~\xc2\x8d|\'~\xc2\x8e", "~\xc2\x8b|\'~\xc2\x8c", /* dflat dflatrb dflatsb */ "\'\'", "~\xc2\x8d|\'\'~\xc2\x8e", "~\xc2\x8b|\'\'~\xc2\x8c", /* nat natrb natsb */ "(", "\xc2\x8d(\xc2\x8e", "\xc2\x8b(\xc2\x8c", /* sharp sharprb sharpsb */ "%", "\xc2\x8d%\xc2\x8e", "\xc2\x8b%\xc2\x8c", /* hflat hflatrb hflatsb (style 0) */ "\xc2\xbf", "~\xc2\x8d|\xc2\xbf~\xc2\x8e", "~\xc2\x8b|\xc2\xbf~\xc2\x8c", /* hsharp hsharprb hsharpsb (style 0) */ "\xc2\xbd", "\xc2\x8d\xc2\xbd\xc2\x8e", "\xc2\x8b\xc2\xbd\xc2\x8c", /* hflat hflatrb hflatsb (style 1) */ "\xc3\x80", "~\xc2\x8d|\xc3\x80~\xc2\x8e", "~\xc2\x8b|\xc3\x80~\xc2\x8c", /* hsharp hsharprb hsharpsb (style 1) */ "\xc2\xbe", "\xc2\x8d\xc2\xbe\xc2\x8e", "\xc2\x8b\xc2\xbe\xc2\x8c" }; static int ornament_xadjusts[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, -2, -2, /* dsharps */ 2, -1, -1, /* flats */ 0, -3, -3, /* dflats */ 2, -1, -1, /* naturals */ 1, -1, -1, /* sharps */ 2, -1, -1, /* hflats (0) */ 1, -1, -1, /* hsharps (0) */ 2, -1, -1, /* hflats (1) */ 1, -1, -1, /* hsharps (1) */ }; /* Amount by which to adjust the brackets when bracketing */ static int ornament_yaadjusts[] = { 0, -1000, /* Trill */ -4000, -4000, -4000, 0, 0, 0, 0, 0, -2000, -2000, -1000, -1000, 0, 0, 0, 0, 1000, 2000, 2000, /* dsharps */ 1000, 1000, 1000, /* flats */ 1000, 1000, 1000, /* dflats */ 3000, 3000, 3000, /* naturals */ 3000, 3000, 3000, /* sharps */ 1000, 1000, 1000, /* hflats (0) */ 3000, 3000, 3000, /* hsharps (0) */ 1000, 1000, 1000, /* hflats (1) */ 3000, 3000, 3000 /* hsharps (1) */ }; static int ornament_ybadjusts[] = { 0, -1000, /* Trill */ 2000, 2000, 2000, 0, 0, 0, 0, 0, 2000, 2000, 1000, 1000, 0, 0, 0, 0, 0, -3000, -3000, /* dsharps */ -3000, -4000, -4000, /* flats */ -3000, -4000, -4000, /* dflats */ -2000, -2000, -2000, /* naturals */ -2000, -2000, -2000, /* sharps */ -3000, -4000, -4000, /* hflats (0) */ -2000, -2000, -2000, /* hsharps(0) */ -3000, -4000, -4000, /* hflats (1) */ -2000, -2000, -2000 /* hsharps(1) */ }; /* These tables need only go up to or_iturn, as accidentals are handled specially and already have a bracketting facility, and arpeggios and spread chords can't be bracketed. */ static int ornament_xbrackadjustsL[] = { 4000, /* Fermata */ 3000, /* Trill */ 3000, /* Trill with sharp */ 3000, /* Trill with flat */ 3000, /* Trill with natural */ 0, 0, 0, /* Tremolos - never bracketed */ 2500, /* Mordent */ 2500, /* Double mordent */ 2500, /* Inverted mordent */ 2500, /* Double inverted mordent */ 2500, /* Turn */ 2500 /* Inverted turn */ }; static int ornament_xbrackadjustsR[] = { 4000, /* Fermata */ 3000, /* Trill */ 3000, /* Trill with sharp */ 3000, /* Trill with flat */ 3000, /* Trill with natural */ 0, 0, 0, /* Tremolos - never bracketed */ 2600, /* Mordent */ 5500, /* Double mordent */ 2600, /* Inverted mordent */ 5500, /* Double inverted mordent */ 3500, /* Turn */ 3500 /* Inverted turn */ }; /************************************************* * Print a possibly bracketed accent/ornament * *************************************************/ /* The bracket characters have width, but accent and ornament characters do not. Arguments: str the string for the accent/ornament fontsize the font size x x-position for accent/ornament y y-position for accent/ornament flags bracket flags yadjust general y adjustment if bracketed byadjust specific y adjustment for brackets bxadjustL additional x left adjustment for brackets bxadjustR additional x right adjustment for brackets Returns: nothing */ static void show_brack_acc(uschar *str, int fontsize, int x, int y, int flags, int yadjust, int byadjust, int bxadjustL, int bxadjustR) { int yb; if ((flags & (DO_RBRA|DO_RKET|DO_SBRA|DO_SKET)) != 0) y += yadjust; yb = y + byadjust; if ((flags & DO_RBRA) != 0) ps_wtext(US"\302\215", font_mu, fontsize, x - (35*main_stavemagn)/100 - bxadjustL, yb, 0); else if ((flags & DO_SBRA) != 0) ps_wtext(US"\302\213", font_mu, fontsize, x - (35*main_stavemagn)/100 - bxadjustL, yb, 0); ps_musstring(str, fontsize, x, y); if ((flags & (DO_RKET|DO_SKET)) != 0) { int swidth = font_stringwidth(str, font_mf, fontsize); if (swidth == 0) swidth = (58*fontsize)/100; ps_wtext(((flags & DO_RKET) != 0)? US"\302\216" : US"\302\214", font_mu, fontsize, x + mac_muldiv(swidth, main_stavemagn, 1000) + bxadjustR, yb, 0); } } /************************************************* * Actually print one note * *************************************************/ /* The data about the note is in the n_* global variables. This function just prints the note head and stems. Dots, accents, etc are done elsewhere. Argument: the x coordinate for the note Returns: TRUE if one or more ledger lines were used */ static BOOL show_note(int x) { uschar buff[100]; uschar *p; BOOL positioned = FALSE; BOOL ledgered = FALSE; BOOL inverted = (n_flags & nf_invert) != 0; int noteheadstyle; int top = P_6L; int bot = P_0L; int fontsize = (n_fontsize*main_stavemagn)/1000; int y = out_ystave - (n_pitch - 130)*main_stavemagn - n_pcorrection; int yy; DEBUG(("show_note() start\n")); /* Set the notehead style */ noteheadstyle = ((n_flags & nf_nhharmonic) != 0)? nh_harmonic : ((n_flags & nf_nhcross) != 0)? nh_cross : bar_cont->noteheadstyle; /* Set up for coupled notes */ if ((n_flags & nf_coupleU) != 0) { top += out_upgap; bot += out_upgap; } else if ((n_flags & nf_coupleD) != 0) { top -= out_downgap; bot -= out_downgap; } /* First deal with ledger lines if required. We repeat the code for above & below, as there seems no tidy way of combining it owing to the requirement for <= or >= comparisons. We can optimize into a single music-font string if the size is standard. The existence of breves makes this messy! */ if (n_pitch <= bot && out_stavelines >= 5 && noteheadstyle != nh_none) { int breve_right = 0; int xx = x; if (n_notetype == breve) { xx -= ((((curmovt->breveledgerextra - 2300)*main_stavemagn)/1000) * n_fontsize)/10000; breve_right = mac_muldiv(2*curmovt->breveledgerextra, fontsize, 10000); } ledgered = positioned = TRUE; yy = out_ystave - (bot - 130)*main_stavemagn - n_pcorrection; if (n_fontsize == 10000 && !inverted) { p = buff; while (n_pitch <= bot) { *p++ = curmovt->ledger; *p++ = 'w'; bot -= 4; } *(--p) = 0; /* removes redundant last move */ ps_musstring(buff, fontsize, xx, yy); if (n_notetype == breve) ps_musstring(buff, fontsize, xx + breve_right, yy); } else /* Have to position each line separately */ { int yyy = yy; p = buff; *p++ = curmovt->ledger; if (inverted) { p += sprintf(CS p, n_upflag? "}" : "yy{"); *p++ = curmovt->ledger; } *p = 0; while (yy <= y) { ps_musstring((yy == y && !n_upflag && inverted)? buff+1 : buff, fontsize, xx, yy); yy += 4*main_stavemagn; } if (n_notetype == breve) { yy = yyy; xx += breve_right; while (yy <= y) { ps_musstring((yy == y && !n_upflag && inverted)? buff+1 : buff, fontsize, xx, yy); yy += 4*main_stavemagn; } } } } else if (n_pitch >= top && out_stavelines >= 5 && noteheadstyle != nh_none) { int breve_right = 0; int xx = x; if (n_notetype == breve) { xx -= ((((curmovt->breveledgerextra - 2300)*main_stavemagn)/1000) * n_fontsize)/10000; breve_right = mac_muldiv(2*curmovt->breveledgerextra, fontsize, 10000); } ledgered = positioned = TRUE; yy = out_ystave - (top - 130)*main_stavemagn - n_pcorrection; if (n_fontsize == 10000 && !inverted) { p = buff; while (n_pitch >= top) { *p++ = curmovt->ledger; *p++ = 'x'; top += 4; } *(--p) = 0; /* removes redundant last move */ ps_musstring(buff, fontsize, xx, yy); if (n_notetype == breve) ps_musstring(buff, fontsize, xx + breve_right, yy); } else { int yyy = yy; p = buff; *p++ = curmovt->ledger; if (inverted) { p += sprintf(CS p, n_upflag? "}" : "yy{"); *p++ = curmovt->ledger; } *p = 0; while (yy >= y) { ps_musstring((yy == y && n_upflag && inverted)? buff+1 : buff, fontsize, xx, yy); yy -= 4*main_stavemagn; } if (n_notetype == breve) { yy = yyy; xx += breve_right; while (yy >= y) { ps_musstring((yy == y && n_upflag && inverted)? buff+1 : buff, fontsize, xx, yy); yy -= 4*main_stavemagn; } } } } /* Optimize the common case where there is a complete character available in the music font. */ p = buff; if (n_notetype < dsquaver && n_stemlength == 0 && noteheadstyle == nh_normal && (n_flags & (nf_invert|nf_stem|nf_smallhead)) == nf_stem) { if ((n_flags & nf_appogg) != 0) *p++ = n_upflag? 129 : 130; *p++ = common[n_notetype + n_upflag*6]; *p = 0; ps_musstring(buff, fontsize, x, y); return ledgered; } /* Deal with rarer cases, first dealing with stems & tails. We impose a minimum stemlength at this point. */ if (n_stemlength < -8000) n_stemlength = -8000; if ((n_flags & nf_stem) != 0) { int direction = n_upflag? -1 : +1; int font10 = fontsize; /* 10pt at font scale */ int font2 = font10/5; /* 2pt at font scale */ int font1 = font2/2; /* 1pt at font scale */ if ((n_flags & nf_appogg) != 0) *p++ = n_upflag? 129 : 130; yy = y + (direction*n_stemlength*main_stavemagn)/1000; p += sprintf(CS p, "%s", tailstrings[n_notetype + n_upflag*8]); positioned = TRUE; /* Notes with stems up */ if (n_upflag) { if (yy <= y) /* stem is lengthened */ { int stemch = (noteheadstyle == nh_cross)? 'o' : 'J'; int z = yy; while (z <= y) { p += sprintf(CS p, "%cww|", stemch); z += font10; } p -= 3; *p = 0; ps_musstring(buff, fontsize, x, yy); p = buff; if (z < y + font10) *p++ = stemch; if (noteheadstyle == nh_harmonic) *p++ = 'q'; } else /* stem is shortened */ { int z = yy - font10 - font2; p += sprintf(CS p, "xxx"); while (z <= y) { p += sprintf(CS p, "q|"); z += font2; } *(--p) = 0; ps_musstring(buff, fontsize, x, yy); p = buff; if (z > y) *p++ = 'q'; } } /* Notes with stems down */ else { if (yy >= y) /* stem is lengthened */ { int stemch = (noteheadstyle == nh_cross)? 'p' : 'K'; int z = yy; while (z >= y) { p += sprintf(CS p, "%cxx~", stemch); z -= font10; } p -= 3; *p = 0; ps_musstring(buff, fontsize, x, yy); p = buff; if (z > y - font10) *p++ = stemch; if (noteheadstyle == nh_harmonic) *p++ = 'r'; } else /* stem is shortened */ { int z = yy + font10 + font2; p += sprintf(CS p, "www"); while (z >= y) { p += sprintf(CS p, "r~v"); z -= font1; } *(--p) = 0; ps_musstring(buff, fontsize, x, yy); p = buff; if (z < y) *p++ = 'r'; } } } /* Now add the note head */ if (noteheadstyle != nh_none) { if (inverted) { if (n_upflag) { if (n_notetype == breve) p += sprintf(CS p, "}}}}{{{{z"); else *p++ = 125; } else { if (n_notetype == breve) p += sprintf(CS p, "{yyyyyyyyyyyy}"); else { *p++ = 123; *p++ = 121; *p++ = 121; } } } /* The special case of a small note head is dealt with below; just omit the note head at this point. */ if ((n_flags & nf_smallhead) == 0) { *p++ = headchars[n_notetype + 8*noteheadstyle]; /* When printing right-to-left, we put some redundant spacing *after* inverted noteheads. This is just a fudge to fool the x-coordinate adjusting code into doing (approximately) the right thing. */ if (main_righttoleft && inverted) p += sprintf(CS p, n_upflag? "{{{" : "zzzz"); } } /* Output the music font string. */ *p = 0; ps_musstring(buff, fontsize, x, y); /* In the special case of a small note head, the note head was skipped above. The printing position should be in the correct place for a full size note head if a stem or ledger lines were output above. Arrange to output the notehead at the cue size, with a relative position adjusted to allow for the head size. */ if ((n_flags & nf_smallhead) != 0) { int cue_fontsize = (curmovt->fontsizes)->fontsize_cue; int sm_fontsize = (cue_fontsize * main_stavemagn)/1000; p = buff; *p++ = headchars[n_notetype + 8*noteheadstyle]; *p = 0; if (positioned && (n_flags & nf_stem) != 0) { x = 0; if ((n_upflag && (n_flags & nf_invert) == 0) || (!n_upflag && (n_flags & nf_invert) != 0)) x += 6000 - 3 * (cue_fontsize / 5); y = 2000 - cue_fontsize / 5; ps_relmusstring(buff, sm_fontsize, x, y); } else { x += (((n_notetype == breve)? 19:11)*main_stavemagn)/10; y -= (5*main_stavemagn)/10; ps_musstring(buff, sm_fontsize, x, y); } } /* Return whether ledger lines or not */ DEBUG(("show_note() end\n")); return ledgered; } /************************************************* * Actually print one rest * *************************************************/ /* This function just prints the actual rest (possibly with ledger lines). Dots are handled elsewhere. Arguments: x x coordinate for the rest notetype length of rest Returns: nothing */ static void show_rest(int x, int notetype) { int fontsize = (n_fontsize*main_stavemagn)/1000; int yoffset = n_restlevel; int y; /* Rests longer than a crotchet have to have ledger lines when they are printed off the stave. Also move a semibreve rest down on a 1-line stave and up on a 3-line stave. We must also adjust the position of breve and semibreve rests for cue sizes. */ if (notetype <= minim) { int loffset = 0; yoffset += 8000; switch (notetype) { case -1: /* long rest */ yoffset -= 2000; break; case breve: yoffset += n_pcorrection; /* Fall through */ case minim: if (yoffset > 16000 || yoffset < 0) loffset = -2000; break; case semibreve: if (out_stavelines == 1) yoffset -= 4000; else if (out_stavelines == 3) yoffset += 4000; yoffset += 2*n_pcorrection; if (yoffset > 12000 || yoffset < -4000) loffset = 2000; break; } if (loffset) ps_musstring(US"=", fontsize, x - (10*main_stavemagn)/10, out_ystave - ((yoffset + loffset)*main_stavemagn)/1000); } else yoffset += 4000 + n_pcorrection; y = out_ystave - (yoffset*main_stavemagn)/1000; /* Output a single rest, taking care of the special cases that are used for conventional repeat signs. */ if (notetype >= 0) { uschar *s; if ((n_flags & nf_restrep) == 0) s = reststrings[n_notetype]; else if (n_notetype == crotchet) s = US"\217"; else { s = US"\220\217"; y += 4*main_stavemagn; } ps_musstring(s, fontsize, x, y); } /* Output a multirest sign */ else if (curmovt->codemultirests && out_manyrest < 9) { ps_musstring(multireststrings[out_manyrest - 2], fontsize, x, y - 2000); } /* Output a |----| long rest sign. If the bar is unusually long or unusually short, draw the long rest symbol; note that ps_line() works in conventional coordinates relative to the base line of the stave. The variable n_longrestmid contains the mid-point of the long rest. */ else { int wide; int rs = out_barlinex - n_longrestmid; if ((wide = rs > 40*main_stavemagn) || x - out_barx < 6000) { posstr *p; int xl, xr; int vthick = (3*main_stavemagn)/10; int hthick = 3*main_stavemagn; int adjust = 0; int min = wide? 15000 : 12000; /* Calculate an adjustment for anything at the end of the bar, e.g. a clef change. */ for (p = out_posptr + 1; p->moff <= 0; p++); /* Skip preceding */ for (; p < out_poslast; p++) adjust += p[1].xoff - p->xoff; /* Place the right hand end with respect to the the barline, nearer for a narrow bar, then place the left hand end symmetrically. */ if (adjust < min) adjust = min + adjust/3; else adjust += 5000; xr = out_barlinex - mac_muldiv(adjust, main_stavemagn, 1000); xl = 2*n_longrestmid - xr; ps_line(xl, 8*main_stavemagn, xr, 8*main_stavemagn, hthick, 0); ps_line(xl, 4*main_stavemagn, xl, 12*main_stavemagn, vthick, 0); ps_line(xr, 4*main_stavemagn, xr, 12*main_stavemagn, vthick, 0); } /* Use the long rest character */ else ps_muschar(x, y, mc_longrest, fontsize); } } /************************************************* * Generate one note/rest + dots & accents * *************************************************/ /* The relevant data about the note/rest is all in the n_* global variables. Arguments: none Returns: nothing */ void out_shownote(void) { int fontsize = (n_fontsize*main_stavemagn)/1000; int xn = n_x + n_cueadjust; int yyabove, yybelow; int acc_level, acc_upflag; BOOL ledgered = FALSE; DEBUG(("out_shownote() start\n")); /* If the note is invisible, we skip printing and just show accents, etc. below. This test is for notes on and this note not hidden. */ if ((bar_cont->flags & cf_notes) != 0 && (n_flags & nf_hidden) == 0) { int x_acc = 0; int x_dot = 0; /* If printing a breve, move left to align with semibreve position */ if (n_pitch != 0 && n_notetype == breve) xn -= (23*main_stavemagn)/10; /* First, any accidental is set, prior to the given position */ if (n_acc) { int offset = n_acc; if ((n_flags & nf_accrbra) != 0) offset += 6; else if ((n_flags & nf_accsbra) != 0) offset += 12; if ((n_flags & nf_halfacc) != 0) { offset += ((n_acc == ac_sharp)? (curmovt->hsharpstyle == 0) : (curmovt->hflatstyle == 0))? 18:36; } x_acc = xn - mac_muldiv(n_accleft, n_fontsize, 10000); ps_muschar(x_acc, out_ystave - (n_pitch - 130)*main_stavemagn - n_pcorrection, out_acctable[offset], fontsize); } /* Now we can output the note or rest. */ if (n_pitch == 0) { int notetype; if (out_manyrest == 1) notetype = n_notetype; else { notetype = -1; n_flags &= ~(nf_dot+nf_plus); /* Kill dots for many bar rest */ } show_rest(xn, notetype); } else ledgered = show_note(xn); /* Deal with horizontal dots/plus - fudge for quavers and breves */ if ((n_flags & (nf_dot+nf_plus)) != 0) { BOOL dotplus = (n_flags & nf_plus) != 0; int dotpos = 84; int dotlevel; if (n_pitch == 0) { dotlevel = mac_muldiv(L_3S + n_restlevel, main_stavemagn, 1000); dotpos += restdotadjusts[n_notetype]; } else { int dotpitch = n_pitch | 2; /* force into space above */ if ((n_flags & nf_lowdot) != 0 && (n_pitch & 2) == 0) dotpitch -= 4; if ((n_flags & nf_highdot) != 0 && (n_pitch & 2) != 0) dotpitch += 4; dotlevel = (dotpitch - 130)*main_stavemagn; if (n_notetype == breve) dotpos += 50; } if ((n_flags & nf_dotright) != 0) dotpos += 60; else if (n_upflag && n_notetype >= quaver && n_pitch != 0) dotpos += 16; dotpos = (dotpos*main_stavemagn)/10 + n_dotxadjust; /* For cue notes there are two choices: either to scale the position according to the cue size, or to align the dots with full-sized notes that may be above or below (alignment by centre of dot). */ if ((n_flags & nf_cuesize) != 0) { if ((n_flags & nf_cuedotalign) != 0) dotpos += mac_muldiv(640 - mac_muldiv(640, n_fontsize, 10000), main_stavemagn, 1000) - n_cueadjust; else dotpos = mac_muldiv(dotpos, n_fontsize, 10000); } /* Output the dot(s). The '+' character is 135 in the music font; we have to specify this as a UTF-8 string. */ x_dot = xn + dotpos; ps_musstring(dotplus? US"\302\207" : US"?", fontsize, x_dot, out_ystave - dotlevel - n_pcorrection); if ((n_flags & nf_dot2) != 0) { x_dot += (35*main_stavemagn)/10; ps_musstring(US"?", fontsize, x_dot, out_ystave - dotlevel - n_pcorrection); } if (dotplus) x_dot += 4*main_stavemagn; /* Extra for ) */ } /* Deal with bracketed notehead */ if ((n_flags & nf_headbra) != 0) { int adjustbra = 0; int adjustket = 0; int bfontsize = (6*fontsize)/10; int yb = out_ystave - (n_pitch - 130)*main_stavemagn - (9*main_stavemagn/10); uschar *bra = main_righttoleft? US"\302\216" : US"\302\215"; uschar *ket = main_righttoleft? US"\302\215" : US"\302\216"; if (x_acc == 0) x_acc = xn + main_stavemagn; if (x_dot == 0) x_dot = xn + 5*main_stavemagn; if (n_notetype == breve) adjustket += 5*main_stavemagn; if ((n_flags & nf_smallhead) != 0) { if ((n_flags & nf_stem) != 0) { if (n_upflag) adjustbra += 2*main_stavemagn; else adjustket -= 2*main_stavemagn; } else { adjustbra += 2*main_stavemagn; adjustket -= 2*main_stavemagn; } if (ledgered) { adjustbra -= (15*main_stavemagn)/10; adjustket += main_stavemagn; } } ps_musstring(bra, bfontsize, x_acc - (35*main_stavemagn)/10 + adjustbra, yb); ps_musstring(ket, bfontsize, x_dot + (40*main_stavemagn)/10 + adjustket, yb); } } /* If there are no dynamics and no ornaments, there's nothing more to do */ if ((n_acflags & af_dynamics) == 0 && n_ornament == NULL) return; /* Now set up a level and up flag for expression marks - normally these are from the standard note values, but they are different if the accents are flagged for printing on the same side of the note as the stem. For chords it is arranged that the accents come with the appropriate note head. */ acc_level = (n_pitch - 130)*main_stavemagn; if ((n_acflags & af_opposite) == 0) { acc_upflag = n_upflag; } else { acc_upflag = !n_upflag; if ((n_flags & nf_stem) != 0) acc_level += n_upfactor * mac_muldiv((12000+n_stemlength), main_stavemagn, 1000); } /* Staccato, staccatissimo, ring, & bar go inside the staff. Except for staccato and staccatissimo, they are allowed together - the staccat{issim}o is nearest to the note. We don't need to compensate for ties as the ties themselves are moved in this case. */ if ((n_acflags & af_dyninside) != 0) { int adjust = 4*main_stavemagn; int p = acc_level; if (acc_upflag) { adjust = -adjust; p -= 8*main_stavemagn; /* Accent at notehead; ensure not on line; not for 0 or 1-line staves or for staccatissimo. */ if (out_stavelines >= 2 && (n_acflags & af_staccatiss) == 0) { if (acc_upflag == n_upflag) { if (((n_pitch & 2) == 0) && n_pitch != P_1L && (!ledgered || n_pitch > (P_5L - ((n_flags & nf_coupleD)? out_downgap : 0)))) p -= 2*main_stavemagn; } /* Accent at stem end; ensure not on line; rather assumes stemlength adjustments will be in whole points... */ else { int pp = p/main_stavemagn; if (pp >= (-6) && pp <= 10 && (pp & 2) != 0) p -= 2*main_stavemagn; } } } else /* !acc_upflag */ { /* Accent at notehead; ensure not on line; not for 0 or 1-line staves or for staccatissimo. */ if (out_stavelines >= 2 && (n_acflags & af_staccatiss) == 0) { if (acc_upflag == n_upflag) { if (((n_pitch & 2) == 0) && n_pitch != P_5L && (!ledgered || n_pitch < (P_1L + ((n_flags & nf_coupleU)? out_upgap : 0)))) p += 2*main_stavemagn; } /* Accent at stem end; ensure not on line (rather assumes stemlength adjustments will be in whole points... */ else { int pp = p/main_stavemagn; if (pp >= (-6) && pp <= 10 && (pp & 2) != 0) p += 2*main_stavemagn; } } } if (out_beaming && (acc_upflag != n_upflag)) p += n_upfactor * 1000; if ((n_acflags & af_staccato) != 0) { show_brack_acc(US">", fontsize, xn + out_dynmovex[dyn_staccato], out_ystave - p - out_dynmovey[dyn_staccato], out_dynmovef[dyn_staccato], acc_upflag? 4*main_stavemagn:(-4)*main_stavemagn, -4*main_stavemagn, 0, 0); p += adjust; out_dynmovex[dyn_staccato] = out_dynmovey[dyn_staccato] = out_dynmovef[dyn_staccato] = 0; } /* Leave p the same value as for staccato, with a small adjustment, but we need some further adjustment to cope with the position of the characters in the font. */ else if ((n_acflags & af_staccatiss) != 0) { int pp = (acc_upflag? 25:55)*main_stavemagn/10; if (acc_upflag != n_upflag) pp += (acc_upflag? 1:-1)*main_stavemagn; show_brack_acc(acc_upflag? US"\303\203":US"\303\202", fontsize, xn + out_dynmovex[dyn_staccatiss], out_ystave - p - pp - out_dynmovey[dyn_staccatiss], out_dynmovef[dyn_staccatiss], acc_upflag? 3*main_stavemagn:(-3)*main_stavemagn, 0, 0, 0); p += adjust + (acc_upflag? -1:1)*main_stavemagn; out_dynmovex[dyn_staccatiss] = out_dynmovey[dyn_staccatiss] = out_dynmovef[dyn_staccatiss] = 0; } /* The ring character prints 4 points lower than the other two, and a bit further away from the notehead when clear of stave or ledger lines. */ if ((n_acflags & af_ring) != 0) { int yy = 0; if ((n_flags & nf_couple) == 0) { if (acc_upflag) { if (n_upflag && n_pitch <= P_1S) yy = main_stavemagn; } else { if (!n_upflag && n_pitch >= P_4S) yy = -main_stavemagn; } } show_brack_acc(US"\302\206", fontsize, xn + out_dynmovex[dyn_ring], out_ystave - p - 4*main_stavemagn + yy - out_dynmovey[dyn_ring], out_dynmovef[dyn_ring], acc_upflag? 4*main_stavemagn:(-4)*main_stavemagn, 0, 0, 0); p += adjust + yy; out_dynmovex[dyn_ring] = out_dynmovey[dyn_ring] = out_dynmovef[dyn_ring] = 0; } if ((n_acflags & af_bar) != 0) { show_brack_acc(US"T", fontsize, xn + out_dynmovex[dyn_bar], out_ystave - p - out_dynmovey[dyn_bar], out_dynmovef[dyn_bar], acc_upflag? 4*main_stavemagn:-4*main_stavemagn, -4*main_stavemagn, 2*main_stavemagn, 2*main_stavemagn); out_dynmovex[dyn_bar] = out_dynmovey[dyn_bar] = out_dynmovef[dyn_bar] = 0; } } /* Set up y values for things that print above or below the stave; there is a different set for the accents and the ornaments, but we compute the basic values here for both of them. Set the stemlength to the value for the first note of a chord, which will be the bit that sticks out. */ n_stemlength = n_orig_stemlength; yybelow = misc_ybound(TRUE, n_nexttie, TRUE, FALSE); yyabove = misc_ybound(FALSE, n_nexttie, TRUE, FALSE); /* We can common up the code for the other accents and bowing marks into a loop. This is dependent on the order of the flags in the word, and the bowing marks must come last, because the above/below control is different. All these items print above or below the stave. */ if ((n_acflags & af_dynoutside) != 0) { int i; int yextra = 0; int upflag = acc_upflag; /* Sanitizing compilers may complain about shifting top bits off words, so be pedantic here. Start by getting the first relevant bit to the top position. */ usint f = (n_acflags & 0x1fffffff) << 3; /* The order is: > v V ' down up. We assume that only one of the first four will exist, possibly with one of the last two. */ for (i = 0; i < 6; i++) { int xadjust = 0; int ayybelow = yybelow; int ayyabove = yyabove; if (i == 0) xadjust = main_stavemagn; else if (i == 4) { int newupflag = (bar_cont->flags & cf_bowingabove) == 0; if (newupflag != upflag) yextra = 0; upflag = newupflag; } /* Further adjustments when accents are not on the stem side and there are no "inside" accidentals. Effectively we cancel some of the space put in for accidental signs. We can't take this out of the loop, because the up/down bows may be different to other accents. */ if (upflag == n_upflag && (n_acflags & af_dyninside) == 0) { ayybelow += accaccbelowadjusts[n_acc]; ayyabove += accaccaboveadjusts[n_acc]; } ayybelow = ((ayybelow <= out_stavebottom)? ayybelow : out_stavebottom) - 8000; ayyabove = ((ayyabove > out_stavetop)? ayyabove : out_stavetop) + 3000; /* Print the accent if its bit is set. */ if ((f & 0x80000000) != 0) { int d = dyn_list[i]; /* standard accent number */ int y; uschar *s; if (upflag) { s = accbelowstrings[i]; y = ayybelow + accbelowadjusts[i] - yextra; } else { s = accabovestrings[i]; y = ayyabove + accaboveadjusts[i] + yextra; } show_brack_acc(s, fontsize, n_x + xadjust + out_dynmovex[d], out_ystave - mac_muldiv(y, main_stavemagn, 1000) - out_dynmovey[d], out_dynmovef[d], 0, mac_muldiv(outdynybadjusts[i], main_stavemagn, 1000), mac_muldiv(outdynxbadjusts[i], main_stavemagn, 1000), mac_muldiv(outdynxbadjusts[i], main_stavemagn, 1000)); if (i < 4) yextra = 4000; out_dynmovex[d] = out_dynmovey[d] = out_dynmovef[d] = 0; } f = (f & 0x7fffffff) << 1; } } /* Deal with ornaments. There are only *very* rarely more than one, so don't bother about the recomputation that then happens. */ if (n_ornament != NULL) { yybelow = ((yybelow <= out_stavebottom)? yybelow : out_stavebottom) - 8000; yyabove = ((yyabove > out_stavetop)? yyabove : out_stavetop) + 3000; while (n_ornament != NULL) { uschar s[100]; uschar *p = s; BOOL below = (n_acflags & af_opposite) != 0; int size = fontsize; int ornament = n_ornament->ornament; int x = n_ornament->x + n_x; int y = n_ornament->y; /* Above/below accidentals are special */ if (ornament >= or_dsharp) { size = ((curmovt->fontsizes)->fontsize_vertacc * main_stavemagn)/1000; if (ornament >= or_accbelow) { below = TRUE; y += yybelow + 8000 - (8*size)/10; ornament -= or_accbelow - or_dsharp; } else { below = FALSE; y += yyabove; } } /* Adjust y for other ornaments */ else y += below? yybelow : yyabove; /* Particular code for each ornament */ switch (ornament) { case or_trsh: *p++ = '%'; goto TR; case or_trfl: *p++ = '\''; goto TR; case or_trnat: *p++ = '('; /* Fall through */ case or_tr: TR: { int tsize = (curmovt->fontsizes)->fontsize_trill; int asize = (6*tsize)/10; int *matrix = (curmovt->fontsizes)->fontmatrix_trill; if (matrix != NULL) memcpy(font_transform, matrix, 4*sizeof(int)); out_string(curmovt->trillstring, font_it, tsize, x, out_ystave - mac_muldiv(y, main_stavemagn, 1000), 0); /* For a bracketed trill, use the show_brack_acc() function with an empty string. */ if (n_ornament->bflags != 0) show_brack_acc(US"", size, x, out_ystave - mac_muldiv(y, main_stavemagn, 1000), n_ornament->bflags, 0, below? ornament_ybadjusts[ornament] : ornament_yaadjusts[ornament], ornament_xbrackadjustsL[ornament], ornament_xbrackadjustsR[ornament]); font_reset(); size = asize; if (below) y -= asize; else if (n_ornament->ornament == or_trfl) y += (8*tsize)/10; else y += tsize; x += 2*main_stavemagn; } break; case or_ferm: if (n_pitch == 0) x -= main_stavemagn; *p++ = below? '/' : ')'; break; case or_arp: case or_arpu: case or_arpd: { int h = (n_maxpitch - n_minpitch + 2)/4; if ((n_minpitch & 2) != 0 && (n_maxpitch & 2) != 0) h++; if (ornament == or_arpd) { *p++ = 165; h--; } do *p++ = 145; while (--h >= 0); if (ornament == or_arpu) p[-1] = 164; y = n_minpitch - 130; if ((y & 2) == 0) y -= 2; else if ((n_maxpitch & 2) != 0) y--; y = y*1000 + n_ornament->y; if (n_maxaccleft) x -= n_maxaccleft; else if (n_invertleft) x -= (55*main_stavemagn)/10; } break; case or_spread: { int co; int y0 = n_minpitch - 128; int y1 = n_maxpitch - 128; if (0 <= y0 && y0 <= 16 && (y0 & 3) == 0) y0--; if (0 <= y1 && y1 <= 16 && (y1 & 3) == 0) y1++; co = (4000 * (y1 - y0))/14; if (co > 4000) co = 4000; y0 = mac_muldiv(y0 * 1000 + n_ornament->y, main_stavemagn, 1000); y1 = mac_muldiv(y1 * 1000 + n_ornament->y, main_stavemagn, 1000); x -= 5000; if (n_maxaccleft) x -= n_maxaccleft; else if (n_invertleft) x -= 4*main_stavemagn; out_slur(x, y0, x, y1, 0, co, 0, 1000); } break; /* Tremolos are handled with their own code. Vertical movement is permitted. */ case or_trem3: *p++ = 146; /* Fall through */ case or_trem2: *p++ = 146; /* Fall through */ case or_trem1: *p++ = 146; y = n_pitch - 124; if (n_notetype >= minim) x += (n_upfactor*255*main_stavemagn)/100; if (n_upflag) { int yy = (n_ornament->ornament == or_trem3)? 4000 : 2000; y += (n_ornament->ornament == or_trem1)? 4 : 2; y = y*1000 + n_ornament->y; if (out_beaming && n_stemlength >= yy) y += (n_stemlength - yy)/2; if (main_righttoleft) x -= 5000; } else { int yy = 2000; switch (n_ornament->ornament) { case or_trem3: y -= 18; yy = 4000; break; case or_trem2: y -= 14; break; case or_trem1: y -= (y & 2)? 10 : 12; break; } y = y*1000 + n_ornament->y; if (out_beaming && n_stemlength >= yy) y -= (n_stemlength - yy)/2; if (main_righttoleft) x += 5000; } break; /* Handle those cases that have no complications, but just require setting a string and a position. This includes accidentals printed above and below, though we have a small fudge for half accidental styles. */ default: if (ornament >= or_hflat && ornament <= or_hflatsb) ornament += curmovt->hflatstyle * 6; else if (ornament >= or_hsharp && ornament <= or_hsharpsb) ornament += curmovt->hsharpstyle * 6; p += sprintf(CS p, "%s", ornament_strings[ornament]); x += ornament_xadjusts[ornament] * main_stavemagn; y += below? ornament_ybadjusts[ornament] : ornament_yaadjusts[ornament]; break; } /* Output the string which has been set up (if any). The accidentals treated as ornaments are those printed above notes, and they have their own arrangements for bracketing, so we do not call show_brack_acc for them. Also, bracketing is not available for tremolos, trills, arpeggios, or spread chords. */ if (p > s) { int ybadjust = 0; *p = 0; switch (ornament) { case or_ferm: ybadjust = below? -2000 : 1000; /* Fall through */ case or_mord: case or_dmord: case or_imord: case or_dimord: case or_turn: case or_iturn: show_brack_acc(s, size, x, out_ystave - mac_muldiv(y, main_stavemagn, 1000), n_ornament->bflags, 0, ybadjust, ornament_xbrackadjustsL[ornament], ornament_xbrackadjustsR[ornament]); break; default: ps_musstring(s, size, x, out_ystave - mac_muldiv(y, main_stavemagn, 1000)); break; } } /* Move on to next ornament, if any */ n_ornament = (b_ornamentstr *) ((uschar *)n_ornament + length_table[b_ornament]); if (n_ornament->type == b_Jump) n_ornament = (b_ornamentstr *) ((uschar *)(((b_Jumpstr *)n_ornament)->next) + length_table[b_Jump]); if (n_ornament->type != b_ornament) break; } } DEBUG(("out_shownote() end\n")); } /* End of shownote.c */