/* * Drawing functions. * * This file is part of abcm2ps. * * Copyright (C) 1998-2007 Jean-François Moine * Adapted from abc2ps, Copyright (C) 1996,1997 Michael Methfessel * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include "abcparse.h" #include "abc2ps.h" struct BEAM { /* packages info on one beam */ struct SYMBOL *s1, *s2; float a, b; short nflags; }; static char *acc_tb[] = { "", "sh", "nt", "ft", "dsh", "dft" }; /* scaling stuff */ static float cur_scale = 1; /* voice or staff scale */ static float cur_trans = 0; /* != 0 when scaled staff */ static void draw_note(float x, struct SYMBOL *s, int fl); static void set_sscale(int staff); static void set_tie_room(void); /* -- up/down shift needed to get k*6 -- */ static float rnd6(float y) { int iy; iy = ((int) (y + 2.999) + 12) / 6 * 6 - 12; return iy - y; } /* -- compute the best vertical offset for the beams -- */ static float b_pos(int grace, int stem, int flags, float b) { float d1, d2, shift, depth; float top, bot; shift = !grace ? BEAM_SHIFT : 3; depth = !grace ? BEAM_DEPTH : 1.7; if (stem > 0) { bot = b - (flags - 1) * shift - depth; if (bot > 26) return 0; top = b; } else { top = b + (flags - 1) * shift + depth; if (top < -2) return 0; bot = b; } d1 = rnd6(top - BEAM_OFFSET); d2 = rnd6(bot + BEAM_OFFSET); if (d1 * d1 > d2 * d2) return d2; return d1; } /* -- calculate a beam -- */ /* (the staves may be defined or not) */ static int calculate_beam(struct BEAM *bm, struct SYMBOL *s1) { struct SYMBOL *s, *s2; int notes, nflags, staff, voice, two_staves, two_dir; float x, y, ys, a, b, max_stem_err; float sx, sy, sxx, sxy, syy, a0, stem_xoff, scale; static float min_tb[2][6] = { {STEM_MIN, STEM_MIN, STEM_MIN2, STEM_MIN3, STEM_MIN4, STEM_MIN4}, {STEM_CH_MIN, STEM_CH_MIN, STEM_CH_MIN2, STEM_CH_MIN3, STEM_CH_MIN4, STEM_CH_MIN4} }; /* must have one printed note head */ if (s1->as.flags & s1->next->as.flags & ABC_F_INVIS) return 0; /* find first and last note in beam */ notes = nflags = 0; /* set x positions, count notes and flags */ two_staves = two_dir = 0; staff = s1->staff; voice = s1->voice; stem_xoff = (s1->as.flags & ABC_F_GRACE) ? GSTEM_XOFF : STEM_XOFF; for (s2 = s1; ; s2 = s2->next) { #if 0 if (s2 == 0) { error(1, s1, "No beam end!"); return 0; } #endif if (s2->type != NOTE) continue; if (s2->nflags > nflags) nflags = s2->nflags; notes++; if (s2->staff != staff) two_staves = 1; if (s2->stem != s1->stem) two_dir = 1; if (s2->as.flags & ABC_F_WORD_END) break; } bm->s2 = s2; /* (don't display the flags) */ if (staff_tb[staff].y == 0) { /* staves not defined */ if (two_staves) return 0; } else { /* staves defined */ if (!two_staves && !(s1->as.flags & ABC_F_GRACE)) { bm->s1 = s1; /* beam already calculated */ bm->a = (s1->ys- s2->ys) / (s1->xs - s2->xs); bm->b = s1->ys - s1->xs * bm->a + staff_tb[staff].y; bm->nflags = nflags; return 1; } } sx = sy = sxx = sxy = syy = 0; /* linear fit through stem ends */ for (s = s1; ; s = s->next) { if (s->type != NOTE) continue; if ((scale = voice_tb[s->voice].scale) == 1) scale = staff_tb[s->staff].clef.staffscale; if (s->stem >= 0) x = stem_xoff + s->shhd[0]; else x = -stem_xoff + s->shhd[s->nhd]; x *= scale; x += s->x; s->xs = x; y = s->ys + staff_tb[s->staff].y; sx += x; sy += y; sxx += x * x; sxy += x * y; syy += y * y; if (s == s2) break; } /* beam fct: y=ax+b */ a = (sxy * notes - sx * sy) / (sxx * notes - sx * sx); b = (sy - a * sx) / notes; /* the next few lines modify the slope of the beam */ if (!(s1->as.flags & ABC_F_GRACE)) { if (notes >= 3) { float hh; hh = syy - a * sxy - b * sy; /* flatten if notes not in line */ if (hh > 0 && hh / (notes - 2) > .5) a *= BEAM_FLATFAC; } if (a >= 0) a = BEAM_SLOPE * a / (BEAM_SLOPE + a); /* max steepness for beam */ else a = BEAM_SLOPE * a / (BEAM_SLOPE - a); } else { if (a > BEAM_SLOPE) a = BEAM_SLOPE; else if (a < -BEAM_SLOPE) a = -BEAM_SLOPE; } /* to decide if to draw flat etc. use normalized slope a0 */ a0 = a * (s2->xs - s1->xs) / (20 * (notes - 1)); if (a0 * a0 < BEAM_THRESH * BEAM_THRESH) a = 0; /* flat below threshhold */ b = (sy - a * sx) / notes; /* recalculate b for new slope */ /* if (nflags>1) b=b+2*stem;*/ /* leave a bit more room if several beams */ /* have flat beams when asked */ if (cfmt.flatbeams && voice_tb[voice].key.bagpipe) { if (!(s1->as.flags & ABC_F_GRACE)) b = -11 + staff_tb[staff].y; else b = 35 + staff_tb[staff].y; a = 0; } /*fixme: have a look again*/ /* have room for the symbols in the staff */ max_stem_err = 0; /* check stem lengths */ s = s1; if (two_dir) { /* 2 directions */ /*fixme: more to do*/ if (!(s1->as.flags & ABC_F_GRACE)) ys = BEAM_SHIFT; else ys = 3; ys *= (nflags - 1); ys += BEAM_DEPTH; ys *= .5; if (s1->stem != s2->stem && s1->nflags < s2->nflags) ys *= s2->stem; else ys *= s1->stem; b += ys; } else if (!(s1->as.flags & ABC_F_GRACE)) { /* normal notes */ while (s->ts_prev->type == NOTE && s->ts_prev->time == s->time) s = s->ts_prev; for (; s != 0 && s->time <= s2->time; s = s->ts_next) { float stem_err; if (s->type != NOTE || (s->as.flags & ABC_F_INVIS) || (s->staff != staff && s->voice != voice)) { continue; } if (s->voice == voice) { ys = a * s->x + b - staff_tb[s->staff].y; if (s->nhd == 0) stem_err = min_tb[0][(unsigned) s->nflags]; else stem_err = min_tb[1][(unsigned) s->nflags]; if (s->stem > 0) { if (s->pits[s->nhd] > 26) { stem_err -= 2; if (s->pits[s->nhd] > 28) stem_err -= 2; if (stem_err < 5) stem_err = 5; } stem_err -= ys - (float) (3 * (s->pits[s->nhd] - 18)); } else { if (s->pits[0] < 18) { stem_err -= 2; if (s->pits[0] < 16) stem_err -= 2; if (stem_err < 5) stem_err = 5; } stem_err -= (float) (3 * (s->pits[0] - 18)) - ys; } stem_err += BEAM_DEPTH + BEAM_SHIFT * (s->nflags - 1); } else { /*fixme: KO when two_staves */ ys = a * s->x + b - staff_tb[s->staff].y; if (s1->stem > 0) { if (s->stem > 0) { /*fixme: KO when the voice numbers are inverted */ if (s->voice < voice) continue; if (s->y > ys + 4) continue; } stem_err = s->ymx - ys; } else { if (s->stem < 0) { if (s->voice > voice) continue; if (s->y < ys - 4) continue; } stem_err = ys - s->ymn; } stem_err += 2 + BEAM_DEPTH + BEAM_SHIFT * (s1->nflags - 1); } if (stem_err > max_stem_err) max_stem_err = stem_err; } } else { /* grace notes */ for ( ; ; s = s->next) { float stem_err; ys = a * s->xs + b - staff_tb[s->staff].y; stem_err = GSTEM - 2; if (s->stem > 0) stem_err -= ys - (float) (3 * (s->pits[s->nhd] - 18)); else stem_err += ys - (float) (3 * (s->pits[0] - 18)); stem_err += 3 * (s->nflags - 1); if (stem_err > max_stem_err) max_stem_err = stem_err; if (s == s2) break; } } if (max_stem_err > 0) /* shift beam if stems too short */ b += s1->stem * max_stem_err; /* have room for the gracenotes, bars and clefs */ /*fxime: test*/ if (!two_staves && !two_dir) for (s = s1->next; ; s = s->next) { struct SYMBOL *g; if ((g = s->grace) == 0) { if (s->type == CLEF || (s->type == BAR && !(s->as.flags & ABC_F_INVIS))) { y = a * s->x + b; if (s1->stem > 0) { y = s->ymx - y + BEAM_DEPTH + BEAM_SHIFT * (nflags - 1) + 2; if (y > 0) b += y; } else { y = s->ymn - y - BEAM_DEPTH - BEAM_SHIFT * (nflags - 1) - 7; if (y < 0) b += y; } } if (s == s2) break; continue; } for ( ; g != 0; g = g->next) { y = a * g->x + b; if (s1->stem > 0) { y = g->ys - y + BEAM_DEPTH + BEAM_SHIFT * (nflags - 1) + 2; if (y > 0) b += y; } else { y = g->y - y - BEAM_DEPTH - BEAM_SHIFT * (nflags - 1) - 7; if (y < 0) b += y; } } if (s == s2) break; } if (a == 0) /* shift flat beams onto staff lines */ b += b_pos(s1->as.flags & ABC_F_GRACE, s1->stem, nflags, b - staff_tb[staff].y); /* adjust final stems and rests under beam */ for (s = s1; ; s = s->next) { switch (s->type) { case NOTE: s->ys = a * s->xs + b - staff_tb[s->staff].y; if (s->stem > 0) s->ymx = s->ys + 2.5; else s->ymn = s->ys - 2.5; break; case REST: y = a * s->x + b - staff_tb[s->staff].y; if (s1->stem > 0) { y -= BEAM_DEPTH + BEAM_SHIFT * (nflags - 1); y -= s->head != H_FULL ? 4 : 9; if (s1->multi == 0 && y > 12) y = 12; if (s->y <= y) break; } else { y += BEAM_DEPTH + BEAM_SHIFT * (nflags - 1); y += s->head != H_FULL ? 4 : 11; if (s1->multi == 0 && y < 12) y = 12; if (s->y >= y) break; } if (s->head != H_FULL) { int iy; iy = ((int) (y + 3) + 12) / 6 * 6 - 12; y = iy; } s->y = y; break; } if (s == s2) break; } /* save beam parameters */ if (staff_tb[staff].y == 0) /* if staves not defined */ return 0; bm->s1 = s1; bm->a = a; bm->b = b; bm->nflags = nflags; return 1; } /* -- draw a single beam -- */ /* (the staves are defined) */ static void draw_beam(float x1, float x2, float dy, float h, struct BEAM *bm) { float y1, dy2; if ((bm->s1->sflags & S_TREM) && bm->s1->head != H_EMPTY) { if (bm->s1->head >= H_OVAL) { x1 = bm->s1->x; x2 = bm->s2->x; } dy2 = (x2 - x1) * .7; if (dy2 < 15) dy2 = 15; dy2 = (x2 - x1 - dy2) * .5; x1 += dy2; x2 -= dy2; } y1 = bm->a * x1 + bm->b - dy; x2 -= x1; dy2 = bm->a * x2; putf(h); putx(x2); putf(dy2); putxy(x1, y1); PUT0("bm\n"); } /* -- draw the beams for one word -- */ /* (the staves are defined) */ static void draw_beams(struct BEAM *bm) { struct SYMBOL *s, *s1, *s2; int i, beam_dir; float shift, bshift, bstub, bh; s1 = bm->s1; /*fixme: KO if many staves with different scales*/ set_scale(s1->voice); s2 = bm->s2; if (!(s1->as.flags & ABC_F_GRACE)) { bshift = BEAM_SHIFT; bstub = BEAM_STUB; shift = .34; /* (half width of the stem) */ bh = BEAM_DEPTH; } else { bshift = 3; bstub = 3.2; shift = .29; bh = 1.6; } /*fixme: quick hack for stubs at end of beam and different stem directions*/ beam_dir = s1->stem; if (s1->stem != s2->stem && s1->nflags < s2->nflags) beam_dir = s2->stem; if (beam_dir < 0) bh = -bh; if (cur_trans == 0 && cur_scale != 1) { bm->a /= cur_scale; bm->b = s1->ys - s1->xs * bm->a + staff_tb[s1->staff].y; bshift *= cur_scale; } /* make first beam over whole word and adjust the stem lengths */ draw_beam(s1->xs - shift, s2->xs + shift, 0., bh, bm); for (s = s1; ; s = s->next) { if (s->type == NOTE && s->stem != beam_dir) { s->ys = bm->a * s->xs + bm->b - staff_tb[s->staff].y + bshift * (s->nflags - 1) * s->stem - bh; } if (s == s2) break; } /* other beams with two or more flags */ shift = 0; for (i = 2; i <= bm->nflags; i++) { shift += bshift; for (s = s1; ; s = s->next) { struct SYMBOL *k1, *k2; float x1; if (s->type != NOTE || s->nflags < i) { if (s == s2) break; continue; } k1 = s; for (;;) { if (s == s2) break; s = s->next; if ((s->dur > 0 && s->nflags < i) || (s->sflags & S_BEAM_BR1) || ((s->sflags & S_BEAM_BR2) && i > 2)) { s = s->prev; break; } } k2 = s; while (k2->type != NOTE) k2 = k2->prev; x1 = k1->xs; if (k1 == k2) { if (k1 == s1 || (k1->sflags & S_BEAM_BR1) || ((k1->sflags & S_BEAM_BR2) && i > 2)) { x1 += bstub; } else if (k1 == s2) x1 -= bstub; else { struct SYMBOL *k; k = k1->next; while (k->type != NOTE) k = k->next; if ((k->sflags & S_BEAM_BR1) || ((k->sflags & S_BEAM_BR2) && i > 2)) x1 -= bstub; else { k1 = k1->prev; while (k1->type != NOTE) k1 = k1->prev; if (k1->nflags < k->nflags || (k1->nflags == k->nflags && k1->dots < k->dots)) x1 += bstub; else x1 -= bstub; } } } draw_beam(x1, k2->xs, #if 1 shift * beam_dir, #else shift * k1->stem, /*fixme: more complicated */ #endif bh, bm); if (s == s2) break; } } } /* -- draw the left side of the staves -- */ static void draw_lstaff(float x) { int i, j, l, istart, iend; float yt, yb; if (cfmt.alignbars) return; l = 0; for (i = 0; i < nstaff; i++) { if (staff_tb[i].flags[0] & (OPEN_BRACE | OPEN_BRACKET)) l++; if (!staff_tb[i].empty && staff_tb[i].clef.stafflines != 0) break; if (staff_tb[i].flags[0] & (CLOSE_BRACE | CLOSE_BRACKET)) l--; } for (j = nstaff; j > i; j--) { if (!staff_tb[j].empty && staff_tb[j].clef.stafflines != 0) break; } if (i == j && l == 0) return; set_scale(-1); yb = staff_tb[j].y + staff_tb[j].botbar * staff_tb[j].clef.staffscale; PUT3("%.1f %.1f %.1f bar\n", staff_tb[i].y + staff_tb[i].topbar * staff_tb[i].clef.staffscale - yb, x, yb); for (l = 0; l <= 1; l++) { /* two levels */ for (i = 0; i <= nstaff; i++) { if (!(staff_tb[i].flags[l] & (OPEN_BRACE | OPEN_BRACKET))) continue; j = i; while (staff_tb[j].empty || staff_tb[j].clef.stafflines == 0) { if (staff_tb[j].flags[l] & (CLOSE_BRACE | CLOSE_BRACKET)) break; j++; } if (staff_tb[j].empty || staff_tb[j].clef.stafflines == 0) continue; istart = iend = j; for (;;) { if (!staff_tb[j].empty && staff_tb[j].clef.stafflines != 0) iend = j; if (staff_tb[j].flags[l] & (CLOSE_BRACE | CLOSE_BRACKET)) break; j++; } yt = staff_tb[istart].y + staff_tb[istart].topbar * staff_tb[istart].clef.staffscale; yb = staff_tb[iend].y + staff_tb[iend].botbar * staff_tb[iend].clef.staffscale; PUT4("%.1f %.1f %.1f %s\n", yt - yb, x, yt, (staff_tb[i].flags[l] & OPEN_BRACE) ? "brace" : "bracket"); } x -= 6; } } /* -- draw a staff -- */ static void draw_staff(struct VOICE_S *p_voice, float x1, float x2) { int nlines, staff; float y; /* draw the staff */ staff = p_voice->staff; set_sscale(staff); y = staff_tb[staff].y; nlines = staff_tb[staff].clef.stafflines; switch (nlines) { case 0: return; case 1: y += 12; break; case 2: case 3: y += 6; break; } putx(x2 - x1); PUT1("%d ", nlines); putxy(x1, y); PUT0("staff\n"); } /* -- draw the time signature -- */ static void draw_timesig(float x, struct SYMBOL *s) { int i, staff, l; char *f, meter[64]; float dx; if (s->as.u.meter.nmeter == 0) return; staff = s->staff; x -= s->wl; for (i = 0; i < s->as.u.meter.nmeter; i++) { l = strlen(s->as.u.meter.meter[i].top); if (l > sizeof s->as.u.meter.meter[i].top) l = sizeof s->as.u.meter.meter[i].top; if (s->as.u.meter.meter[i].bot[0] != '\0') { int l2; sprintf(meter, "(%.8s)(%.2s)", s->as.u.meter.meter[i].top, s->as.u.meter.meter[i].bot); f = "tsig"; l2 = strlen(s->as.u.meter.meter[i].bot); if (l2 > sizeof s->as.u.meter.meter[i].bot) l2 = sizeof s->as.u.meter.meter[i].bot; if (l2 > l) l = l2; } else switch (s->as.u.meter.meter[i].top[0]) { case 'C': if (s->as.u.meter.meter[i].top[1] != '|') f = "csig"; else { f = "ctsig"; l--; } meter[0] = '\0'; break; case 'c': if (s->as.u.meter.meter[i].top[1] != '.') f = "imsig"; else { f = "iMsig"; l--; } meter[0] = '\0'; break; case 'o': if (s->as.u.meter.meter[i].top[1] != '.') f = "pmsig"; else { f = "pMsig"; l--; } meter[0] = '\0'; break; case '(': case ')': sprintf(meter, "(\\%s)", s->as.u.meter.meter[i].top); f = "stsig"; break; default: sprintf(meter, "(%.8s)", s->as.u.meter.meter[i].top); f = "stsig"; break; } if (meter[0] != '\0') PUT1("%s ", meter); dx = (float) (13 * l); putxy(x + dx * .5, staff_tb[staff].y); PUT1("%s\n", f); x += dx; } } /* -- draw a key signature -- */ static void draw_keysig(struct VOICE_S *p_voice, float x, struct SYMBOL *s) { int old_sf = s->u; int staff = p_voice->staff; float staffb = staff_tb[staff].y; int i, clef_ix, shift; signed char *p_seq; static char clef_tb[4] = { 7 - 2, 3 - 3, 6 - 4, 7 - 2}; static char sharp_cl[7] = {24, 9, 15, 21, 6, 12, 18}; static char flat_cl[7] = {12, 18, 24, 9, 15, 21, 6}; static signed char sharp1[6] = {-9, 12, -9, -9, 12, -9}; static signed char sharp2[6] = {12, -9, 12, -9, 12, -9}; static signed char flat1[6] = {9, -12, 9, -12, 9, -12}; static signed char flat2[6] = {-12, 9, -12, 9, -12, 9}; clef_ix = clef_tb[(unsigned) staff_tb[staff].clef.type] + staff_tb[staff].clef.line; if (clef_ix >= 7) clef_ix -= 7; /* normal accidentals */ if (s->as.u.key.nacc == 0) { /* if flats to sharps, or sharps to flats, put neutrals */ if (s->as.u.key.sf == 0 || old_sf * s->as.u.key.sf < 0) { /* old sharps */ shift = sharp_cl[clef_ix]; p_seq = shift > 9 ? sharp1 : sharp2; for (i = 0; i < old_sf; i++) { putxy(x, staffb + shift); PUT0("nt0 "); shift += *p_seq++; x += 5; } /* old flats */ shift = flat_cl[clef_ix]; p_seq = shift < 18 ? flat1 : flat2; for (i = 0; i > old_sf; i--) { putxy(x, staffb + shift); PUT0("nt0 "); shift += *p_seq++; x += 5; } if (s->as.u.key.sf != 0) x += 3; /* extra space */ /* if less sharps or flats, put neutrals */ /* sharps */ } else if (s->as.u.key.sf > 0) { if (s->as.u.key.sf < old_sf) { shift = sharp_cl[clef_ix]; p_seq = shift > 9 ? sharp1 : sharp2; for (i = 0; i < s->as.u.key.sf; i++) shift += *p_seq++; for (; i < old_sf; i++) { putxy(x, staffb + shift); PUT0("nt0 "); shift += *p_seq++; x += 5; } x += 3; /* extra space */ } /* flats */ } else /*if (s->as.u.key.sf < 0)*/ { if (s->as.u.key.sf > old_sf) { shift = flat_cl[clef_ix]; p_seq = shift < 18 ? flat1 : flat2; for (i = 0; i > s->as.u.key.sf; i--) shift += *p_seq++; for (; i > old_sf; i--) { putxy(x, staffb + shift); PUT0("nt0 "); shift += *p_seq++; x += 5; } x += 3; /* extra space */ } } /* new sharps */ shift = sharp_cl[clef_ix]; p_seq = shift > 9 ? sharp1 : sharp2; for (i = 0; i < s->as.u.key.sf; i++) { putxy(x, staffb + shift); PUT0("sh0 "); shift += *p_seq++; x += 5; } /* new flats */ shift = flat_cl[clef_ix]; p_seq = shift < 18 ? flat1 : flat2; for (i = 0; i > s->as.u.key.sf; i--) { putxy(x, staffb + shift); PUT0("ft0 "); shift += *p_seq++; x += 5; } } else { int last_acc, last_shift; /* explicit accidentals */ last_acc = s->as.u.key.accs[0]; last_shift = 100; for (i = 0; i < s->as.u.key.nacc; i++) { if (s->as.u.key.accs[i] != last_acc) { last_acc = s->as.u.key.accs[i]; x += 3; } shift = clef_ix * 6 + 3 * (s->as.u.key.pits[i] - 18); while (shift < -3) shift += 21; while (shift > 24 + 3) shift -= 21; if (shift == last_shift + 21 || shift == last_shift - 21) x -= 5; /* same note */ last_shift = shift; putxy(x, staffb + shift); PUT2("%s%d ", acc_tb[last_acc & 0x07], micro_tb[last_acc >> 3]); x += 5; } } if (old_sf != 0 || s->as.u.key.sf != 0 || s->as.u.key.nacc >= 0) PUT0("\n"); } /* -- convert the standard measure bars -- */ static int bar_cnv(int bar_type) { switch (bar_type) { case B_OBRA: case B_CBRA: case (B_OBRA << 4) + B_CBRA: return 0; /* invisible */ case B_COL: return B_BAR; /* dotted */ case (B_CBRA << 4) + B_BAR: return B_BAR; case (B_BAR << 4) + B_COL: bar_type |= (B_OBRA << 8); break; case (B_BAR << 8) + (B_COL << 4) + B_COL: bar_type |= (B_OBRA << 12); break; case (B_BAR << 12) + (B_COL << 8) + (B_COL << 4) + B_COL: bar_type |= (B_OBRA << 16); break; case (B_COL << 4) + B_BAR: case (B_COL << 8) + (B_COL << 4) + B_BAR: case (B_COL << 12) + (B_COL << 8) + (B_COL << 4) + B_BAR: bar_type <<= 4; bar_type |= B_CBRA; break; case (B_COL << 4) + B_COL: bar_type = (B_COL << 12) + (B_CBRA << 8) + (B_OBRA << 4) + B_COL; break; } return bar_type; } /* -- draw a measure bar -- */ static void draw_bar(float x, struct SYMBOL *s) { int staff, bar_type, dotted; float yb, h; char *psf; staff = voice_tb[s->voice].staff; /* (may be != s->staff) */ yb = staff_tb[staff].y; /* if measure repeat, draw the '%' like glyphs */ if (s->as.u.bar.len != 0) { struct SYMBOL *s2; set_scale(s->voice); if (s->as.u.bar.len == 1) { for (s2 = s->prev; s2->type != REST; s2 = s2->prev) ; putxy(s2->x, yb); PUT0("mrep\n"); } else { putxy(s->x, yb); PUT0("mrep2\n"); if (s->voice == first_voice - voice_tb) { /*fixme set_font(s->gcf); */ set_font(cfmt.anf); putxy(s->x, yb + 24 + 4); PUT1("M(%d)showc\n", s->as.u.bar.len); } } } dotted = s->as.u.bar.dotted || s->as.u.bar.type == B_COL; bar_type = bar_cnv(s->as.u.bar.type); if (bar_type == 0) return; /* invisible */ yb += staff_tb[staff].botbar * staff_tb[staff].clef.staffscale; h = staff_tb[staff].bar_height; for (;;) { psf = "bar"; switch (bar_type & 0x07) { case B_BAR: if (dotted) psf = "dotbar"; break; case B_OBRA: case B_CBRA: psf = "thbar"; x -= 3; break; case B_COL: x -= 2; break; } switch (bar_type & 0x07) { default: set_scale(-1); PUT4("%.1f %.1f %.1f %s ", h, x, yb, psf); break; case B_COL: set_sscale(staff); putxy(x + 1, staff_tb[staff].y); PUT0("rdots "); break; } bar_type >>= 4; if (bar_type == 0) break; x -= 3; } PUT0("\n"); /* set_sscale(staff); */ } /* -- draw a rest -- */ /* (the staves are defined) */ static void draw_rest(struct SYMBOL *s) { int i, y; float x, dotx, staffb; static char *rest_tb[NFLAGS_SZ] = { "r128", "r64", "r32", "r16", "r8", "r4", "r2", "r1", "r0", "r00" }; /* if rest alone in the measure, center */ x = s->x + s->shhd[0] * cur_scale; if (s->dur == voice_tb[s->voice].meter.wmeasure) { struct SYMBOL *prev; if (s->next != 0) x = s->next->x; else x = realwidth; prev = s->prev; while (prev->type == TEMPO || prev->type == PART) prev = prev->prev; if (prev->type != BAR && voice_tb[s->voice].second) { for (prev = prev->ts_next; ; prev = prev->ts_next) { switch (prev->type) { case CLEF: case KEYSIG: case TIMESIG: case FMTCHG: case WHISTLE: continue; default: break; } prev = prev->ts_prev; break; } } x = (x + prev->x) * .5; /* center the associated decorations */ if (s->as.u.note.dc.n > 0) deco_update(s, x - s->x); s->x = x; } if ((s->as.flags & ABC_F_INVIS) && !(s->sflags & S_OTHER_HEAD)) return; staffb = staff_tb[s->staff].y; /* bottom of staff */ if (s->sflags & S_REPEAT) { putxy(x, staffb); if (s->doty < 0) PUT0("srep\n"); else { PUT0("mrep\n"); if (s->doty > 1 && s->voice == first_voice - voice_tb) { /*fixme set_font(s->gcf); */ set_font(cfmt.anf); putxy(x, staffb + 24 + 4); PUT1("M(%d)showc\n", s->doty); } } return; } y = s->y; if (s->sflags & S_OTHER_HEAD) { draw_all_deco_head(s, x, y + staffb); return; } i = C_XFLAGS - s->nflags; /* rest_tb index */ if (i == 7 && y == 12 && staff_tb[s->staff].clef.stafflines <= 2) y -= 6; /* semibreve a bit lower */ putxy(x, y + staffb); PUT1("%s ", rest_tb[i]); /* add helper line(s) when greater than minim */ if (i >= 6) { int yb, yt; switch (staff_tb[s->staff].clef.stafflines) { case 0: yb = 12; yt = 12; break; case 1: yb = 6; yt = 18; break; case 2: yb = 0; yt = 18; break; case 3: yb = 0; yt = 24; break; default: yb = -6; yt = staff_tb[s->staff].clef.stafflines * 6; break; } switch (i) { case 6: /* minim */ if (y <= yb || y >= yt) { puty(y + staffb); PUT0("hl "); } break; case 7: /* semibreve */ if (y < yb || y >= yt - 6) { puty(y + 6 + staffb); PUT0("hl "); } break; default: if (y < yb || y >= yt - 6) { puty(y + 6 + staffb); PUT0("hl "); } if (i == 9) /* longa */ y -= 6; if (y <= yb || y >= yt) { puty(y + staffb); PUT0("hl "); } break; } } dotx = 8; for (i = 0; i < s->dots; i++) { PUT1("%.1f 3 dt ", dotx); dotx += 3.5; } PUT0("\n"); } /* -- draw grace notes -- */ /* (the staves are defined) */ static void draw_gracenotes(struct SYMBOL *s) { int yy; float x0, y0, x1, y1, x2, y2, x3, y3, bet1, bet2, dy1, dy2; float staffb; struct SYMBOL *g, *last; struct BEAM bm; /* draw the notes */ bm.s2 = 0; /* (draw flags) */ for (g = s->grace; ; g = g->next) { if (s->grace->next != 0) { /* many notes */ if ((g->sflags & S_WORD_ST) && !(g->as.flags & ABC_F_WORD_END)) { if (calculate_beam(&bm, g)) draw_beams(&bm); } } draw_note(g->x, g, bm.s2 == 0); if (g == bm.s2) bm.s2 = 0; /* (draw flags again) */ if (g->as.flags & ABC_F_SAPPO) { /* (on 1st note only) */ if (g->next == 0) { /* if one note */ x1 = 9; y1 = g->stem > 0 ? 5 : -5; } else { /* many notes */ x1 = (g->next->x - g->x) * .5 + 4; y1 = (g->ys + g->next->ys) * .5 - g->y; if (g->stem > 0) y1 -= 1; else y1 += 1; } putxy(x1, y1); PUT1("g%ca\n", g->stem > 0 ? 'u' : 'd'); } if (g->next == 0) break; } /* slur */ if (voice_tb[s->voice].key.bagpipe /* no slur when bagpipe */ || !cfmt.graceslurs || s->as.u.note.slur_st /* explicit slur */ || s->next == 0 || s->next->type != NOTE) return; last = g; yy = g->y; for (g = s->grace; g != 0; g = g->next) { if (g->y < yy) { yy = g->y; last = g; } } x0 = last->x; y0 = (last->stem >= 0 ? (float) last->y : last->ys) - 5; if (s->grace != last) { x0 -= 4; y0 += 1; } s = s->next; x3 = s->x - 1; if (s->stem < 0) x3 -= 4; y3 = 3 * (s->pits[0] - 18) - 5; dy1 = (x3 - x0) * .4; if (dy1 > 3) dy1 = 3; dy2 = dy1; bet1 = .2; bet2 = .8; if (y0 > y3 + 7) { x0 = last->x - 1; y0 += .5; y3 += 6.5; x3 = s->x - 5.5; dy2 = (y0 - y3) * .2; dy1 = (y0 - y3) * .8; bet1 = 0; } else if (y3 > y0 + 4) { y3 = y0 + 4; x0 = last->x + 2; y0 = (last->stem >= 0 ? last->y : last->ys) - 4; } x1 = bet1 * x3 + (1 - bet1) * x0; y1 = bet1 * y3 + (1 - bet1) * y0 - dy1; x2 = bet2 * x3 + (1 - bet2) * x0; y2 = bet2 * y3 + (1 - bet2) * y0 - dy2; staffb = staff_tb[s->staff].y; /* bottom of staff */ putxy(x1, y1 + staffb); putxy(x2, y2 + staffb); putxy(x3, y3 + staffb); putxy(x0, y0 + staffb); PUT0("gsl\n"); } /* -- set the y offset of the dots -- */ static void setdoty(struct SYMBOL *s, signed char *y_tb) { int m, m1, y, doty; /* set the normal offsets */ doty = s->doty; for (m = 0; m <= s->nhd; m++) { y = 3 * (s->pits[m] - 18); /* note height on staff */ if ((y % 6) == 0) { if (doty != 0) y -= 3; else y += 3; } y_tb[m] = y; } /* dispatch and recenter the dots in the staff spaces */ for (m = 0; m < s->nhd; m++) { if (y_tb[m + 1] > y_tb[m]) continue; m1 = m; while (m1 > 0) { if (y_tb[m1] > y_tb[m1 - 1] + 6) break; m1--; } if (3 * (s->pits[m1] - 18) - y_tb[m1] < y_tb[m + 1] - 3 * (s->pits[m + 1] - 18)) { while (m1 <= m) y_tb[m1++] -= 6; } else y_tb[m + 1] = y_tb[m] + 6; } } /* -- draw m-th head with accidentals and dots -- */ /* (the staves are defined) */ static void draw_basic_note(float x, struct SYMBOL *s, int m, signed char *y_tb) { int i, y, no_head, head, dots, nflags; float staffb, shhd; char *p; char perc_hd[8]; staffb = staff_tb[s->staff].y; /* bottom of staff */ y = 3 * (s->pits[m] - 18); /* note height on staff */ shhd = s->shhd[m] * cur_scale; /* draw the note decorations */ no_head = (s->sflags & S_OTHER_HEAD); if (no_head) draw_all_deco_head(s, x + shhd, y + staffb); if (s->as.u.note.decs[m] != 0) { int n; i = s->as.u.note.decs[m] >> 3; /* index */ n = i + (s->as.u.note.decs[m] & 0x07); /* # deco */ for ( ; i < n; i++) no_head |= draw_deco_head(s->as.u.note.dc.t[i], x + shhd, y + staffb, s->stem); } if (s->as.flags & ABC_F_INVIS) return; /* special case when no head */ if (s->nohdix >= 0) { if ((s->stem > 0 && m <= s->nohdix) || (s->stem < 0 && m >= s->nohdix)) { PUT0("/x "); /* set x y */ putx(x + shhd); PUT0("def/y "); puty(y + staffb); PUT0("def"); return; } } identify_note(s, s->as.u.note.lens[m], &head, &dots, &nflags); /* draw the head */ putxy(x + shhd, y + staffb); if (no_head) p = "/y exch def/x exch def"; else if (s->as.flags & ABC_F_GRACE) p = "ghd"; else if (staff_tb[s->staff].clef.type == PERC && (i = s->as.u.note.accs[m]) != 0) { i &= 0x07; sprintf(perc_hd, "p%shd", acc_tb[i]); p = perc_hd; } else { switch (head) { case H_OVAL: if (s->as.u.note.lens[m] < BREVE) { p = "HD"; break; } if (s->head != H_SQUARE) { p = "HDD"; break; } /* fall thru */ case H_SQUARE: if (s->as.u.note.lens[m] < BREVE * 2) p = "breve"; else p = "longa"; break; case H_EMPTY: p = "Hd"; break; default: p = "hd"; break; } } PUT0(p); /* add a helper line if horizontal shift */ if (s->shhd[m]) { int yy; yy = 0; if (y >= 30) { yy = y; if (yy % 6) yy -= 3; } else if (y <= -6) { yy = y; if (yy % 6) yy += 3; } if (yy) { PUT0(" "); puty(yy + staffb); PUT0("hl"); } } /* draw the dots */ /*fixme: to see for grace notes*/ if (dots) { float dotx; int doty; dotx = (int) (8. + s->xmx); doty = y_tb[m] - y; while (--dots >= 0) { PUT2(" %.1f %d dt", dotx - shhd, doty); dotx += 3.5; } } /* draw the accidental */ if ((i = s->as.u.note.accs[m]) != 0 && staff_tb[s->staff].clef.type != PERC) { x -= s->shac[m] * cur_scale; PUT0(" "); putx(x); PUT2((s->as.flags & ABC_F_GRACE) ? "gsc %s%d grestore" : "y %s%d", acc_tb[i & 0x07], micro_tb[i >> 3]); } } /* -- draw a note or a chord -- */ /* (the staves are defined) */ static void draw_note(float x, struct SYMBOL *s, int fl) { int i, m, ma, y; float staffb, slen; char *hltype; signed char y_tb[MAXHD]; if (s->dots) setdoty(s, y_tb); /* draw the master note, first or last one */ if (cfmt.setdefl) set_defl(s->stem >= 0 ? DEF_STEMUP : 0); ma = s->stem >= 0 ? 0 : s->nhd; if (s->head >= H_OVAL) x += 1; draw_basic_note(x, s, ma, y_tb); staffb = staff_tb[s->staff].y; if (!(s->as.flags & (ABC_F_INVIS | ABC_F_STEMLESS))) { /* add stem and flags */ char c, c2; c = s->stem >= 0 ? 'u' : 'd'; slen = s->ys - s->y; if (!fl || s->nflags <= 0) { /* stem only */ c2 = (s->as.flags & ABC_F_GRACE) ? 'g' : 's'; if (s->nflags > 0) { /* (fix for PS low resolution) */ if (s->stem >= 0) slen -= 1; else slen += 1; } slen /= voice_tb[s->voice].scale; PUT3(" %.1f %c%c", slen, c2, c); } else { /* stem and flags */ if (cfmt.straightflags) c = 's'; /* straight flag */ c2 = (s->as.flags & ABC_F_GRACE) ? 'g' : 'f'; slen /= voice_tb[s->voice].scale; PUT4(" %d %.1f s%c%c", s->nflags, slen, c2, c); } } else if (s->sflags & S_XSTEM) { /* cross-staff stem */ struct SYMBOL *s2; s2 = s->ts_prev; /*fixme:KO when different scales*/ slen = s2->y + staff_tb[s2->staff].y - s->y - staffb; slen /= voice_tb[s->voice].scale; PUT1(" %.1f su", slen); } if (!(s->as.flags & ABC_F_INVIS)) { if (s->as.flags & ABC_F_GRACE) hltype = "ghl"; else { switch (s->head) { default: hltype = "hl"; break; case H_OVAL: hltype = "hl1"; break; case H_SQUARE: hltype = "hl2"; break; } } y = 3 * (s->pits[0] - 18); /* lower helper lines */ i = -6; switch (staff_tb[s->staff].clef.stafflines) { case 0: case 1: i = 6; break; case 2: case 3: i = 0; break; } for ( ; i >= y; i -= 6) { PUT0(" "); puty(i + staffb); PUT1("%s", hltype); } y = 3 * (s->pits[s->nhd] - 18); /* upper helper lines */ switch (staff_tb[s->staff].clef.stafflines) { case 0: case 1: case 2: i = 18; break; case 3: i = 24; break; default: i = staff_tb[s->staff].clef.stafflines * 6; break; } for ( ; i <= y; i += 6) { PUT0(" "); puty(i + staffb); PUT1("%s", hltype); } } /* draw the other notes */ for (m = 0; m <= s->nhd; m++) { if (m == ma) continue; PUT0(" "); draw_basic_note(x, s, m, y_tb); } PUT0("\n"); } /* -- find where to terminate/start a slur -- */ static struct SYMBOL *next_scut(struct SYMBOL *s) { struct SYMBOL *prev; prev = s; for (s = s->next; s != 0; s = s->next) { if (s->type == BAR && ((s->sflags & S_RRBAR) || s->as.u.bar.type == B_THIN_THICK || s->as.u.bar.type == B_THICK_THIN || (s->as.u.bar.repeat_bar && s->as.text != 0 && s->as.text[0] != '1'))) return s; prev = s; } /*fixme: KO when no note for this voice at end of staff */ return prev; } static struct SYMBOL *prev_scut(struct SYMBOL *s) { struct SYMBOL *sym; int voice; float x; voice = s->voice; for (s = s->prev ; s != 0; s = s->prev) { if (s->type == BAR && ((s->sflags & S_RRBAR) || s->as.u.bar.type == B_THIN_THICK || s->as.u.bar.type == B_THICK_THIN || (s->as.u.bar.repeat_bar && s->as.text != 0 && s->as.text[0] != '1'))) return s; } /* return sym before first note/rest/bar */ sym = voice_tb[voice].sym; for (s = sym->next; s != 0; s = s->next) { switch (s->type) { case NOTE: case REST: case BAR: x = s->x; do { s = s->prev; } while (s->x == x); return s; } } return sym; } /* -- decide whether a slur goes up or down -- */ static int slur_direction(struct SYMBOL *k1, struct SYMBOL *k2) { struct SYMBOL *s; int some_upstem, low; some_upstem = low = 0; for (s = k1; ; s = s->next) { if (s->type == NOTE) { if (!(s->as.flags & ABC_F_STEMLESS)) { if (s->stem < 0) return 1; some_upstem = 1; } if (s->pits[0] < 22) /* if under middle staff */ low = 1; } if (s == k2) break; } if (!some_upstem && !low) return 1; return -1; } /* -- output a slur / tie -- */ static void slur_out(float x1, float y1, float x2, float y2, int s, float height, int dotted, int staff) /* if < 0, the staves are defined */ { float alfa, beta, mx, my, xx1, yy1, xx2, yy2, dx, dy, dz; alfa = .3; beta = .45; /* for wide flat slurs, make shape more square */ dy = y2 - y1; if (dy < 0) dy = -dy; dx = x2 - x1; if (dx > 40. && dy / dx < .7) { alfa = .3 + .002 * (dx - 40.); if (alfa > .7) alfa = .7; } /* alfa, beta, and height determine Bezier control points pp1,pp2 * * X====alfa===|===alfa=====X * / | \ * pp1 | pp2 * / height \ * beta | beta * / | \ * p1 m p2 * */ mx = .5 * (x1 + x2); my = .5 * (y1 + y2); xx1 = mx + alfa * (x1 - mx); yy1 = my + alfa * (y1 - my) + height; xx1 = x1 + beta * (xx1 - x1); yy1 = y1 + beta * (yy1 - y1); xx2 = mx + alfa * (x2 - mx); yy2 = my + alfa * (y2 - my) + height; xx2 = x2 + beta * (xx2 - x2); yy2 = y2 + beta * (yy2 - y2); dx = .03 * (x2 - x1); if (dx > 10.) dx = 10.; dy = s; dz = .2 + .001 * (x2 - x1); if (dz > .6) dz = .6; dz *= s; if (staff < 0) { putxy(xx2 - dx, yy2 + dy); putxy(xx1 + dx, yy1 + dy); putxy(x1, y1 + dz); PUT1("0 %.1f ", dz); putxy(xx1, yy1); putxy(xx2, yy2); putxy(x2, y2); putxy(x1, y1); } else { putxy(xx2 - dx, yy2 + dy); PUT1("y%d ", staff); putxy(xx1 + dx, yy1 + dy); PUT1("y%d ", staff); putxy(x1, y1 + dz); PUT2("y%d 0 %.1f ", staff, dz); putxy(xx1, yy1); PUT1("y%d ", staff); putxy(xx2, yy2); PUT1("y%d ", staff); putxy(x2, y2); PUT1("y%d ", staff); putxy(x1, y1); PUT1("y%d ", staff); } PUT0(dotted ? "dSL\n" : "SL\n"); } /* -- check if slur sequence in a multi-voice staff -- */ static int slur_multi(struct SYMBOL *k1, struct SYMBOL *k2) { for (;;) { if (k1->multi != 0) /* if multi voice */ /*fixme: may change*/ return k1->multi; if (k1 == k2) break; k1 = k1->next; } return 0; } /* -- draw a phrasing slur between two symbols -- */ /* (the staves are not yet defined) */ /* (not a pretty routine, this) */ static int draw_slur(struct SYMBOL *k1, struct SYMBOL *k2, int m1, int m2, int slur_type, int dotted) { struct SYMBOL *k; float x1, y1, x2, y2, height, addy; float a, y, z, h, dx, dy; int s, nn, upstaff, two_staves; /*fixme: if two staves, may have upper or lower slur*/ switch (slur_type) { case SL_ABOVE: s = 1; break; case SL_BELOW: s = -1; break; default: if ((s = slur_multi(k1, k2)) == 0) s = slur_direction(k1, k2); break; } nn = 1; upstaff = k1->staff; two_staves = 0; if (k1 != k2) for (k = k1->next; k != 0; k = k->next) { if (k->dur > 0) { /* note or rest */ nn++; if (k->staff != upstaff) { two_staves = 1; if (k->staff < upstaff) upstaff = k->staff; } } if (k == k2) break; } /*fixme: KO when two staves*/ if (two_staves) error(0, k1, "*** multi-staves slurs not treated"); /* fix endpoints */ x1 = k1->x + k1->xmx; /* take the max right side */ if (k1 != k2) x2 = k2->x; else x2 = realwidth; /* (the slur starts on last note of the line) */ y1 = (float) (s > 0 ? k1->ymx + 2 : k1->ymn - 2); y2 = (float) (s > 0 ? k2->ymx + 2 : k2->ymn - 2); if (k1->type == NOTE) { if (s > 0) { if (k1->stem > 0) { x1 += 5; if ((k1->as.flags & ABC_F_WORD_END) && k1->nflags >= -1 && k1->ys > y1 - 3) { /* (pb tuplets) */ if (k1->nflags > 0) { x1 += 2; y1 = k1->ys - 3; } else y1 = k1->ys - 6; } } } else { if (k1->stem < 0) { x1 -= 1; if ((k1->as.flags & ABC_F_WORD_END) && k1->nflags >= -1 && k1->ys < y1 + 3) { if (k1->nflags > 0) { x1 += 2; y1 = k1->ys + 3; } else y1 = k1->ys + 6; } } } } if (k2->type == NOTE) { if (s > 0) { if (k2->stem > 0) { x2 += 1; if ((k2->sflags & S_WORD_ST) && k2->nflags >= -1 && k2->ys > y2 - 3) y2 = k2->ys - 6; } } else { if (k2->stem < 0) { x2 -= 5; if ((k2->sflags & S_WORD_ST) && k2->nflags >= -1 && k2->ys < y2 + 3) y2 = k2->ys + 6; } } } if (k1->type != NOTE) { y1 = y2 + 1.2 * s; x1 = k1->x + k1->wr * .5; if (x1 > x2 - 12) x1 = x2 - 12; } if (k2->type != NOTE) { y2 = y1 + 1.2 * s; if (k1 != k2) x2 = k2->x - k2->wl * .3; } if (nn >= 3) { if (k1->next->x < x1 + 48) { if (s > 0) { y = k1->next->ymx - 2; if (y1 < y) y1 = y; } else { y = k1->next->ymn + 2; if (y1 > y) y1 = y; } } if (k2->prev->x > x2 - 48) { if (s > 0) { y = k2->prev->ymx - 2; if (y2 < y) y2 = y; } else { y = k2->prev->ymn + 2; if (y2 > y) y2 = y; } } } #if 0 /* shift endpoints */ addx = .04 * (x2 - x1); if (addx > 3.0) addx = 3.0; addy = .01 * (x2 - x1); if (addy > 3.0) addy = 3.0; x1 += addx; x2 -= addx; /*fixme: to simplify*/ if (k1->staff == upstaff) y1 += s * addy; else y1 = -6; if (k2->staff == upstaff) y2 += s * addy; else y2 = -6; #endif a = (y2 - y1) / (x2 - x1); /* slur steepness */ if (a > SLUR_SLOPE || a < -SLUR_SLOPE) { if (a > SLUR_SLOPE) a = SLUR_SLOPE; else a = -SLUR_SLOPE; if (a * s > 0) y1 = y2 - a * (x2 - x1); else y2 = y1 + a * (x2 - x1); } /* for big vertical jump, shift endpoints */ y = y2 - y1; if (y > 8) y = 8; else if (y < -8) y = -8; z = y; if (z < 0) z = -z; dx = .5 * z; dy = .3 * y; if (y * s > 0) { x2 -= dx; y2 -= dy; } else { x1 += dx; y1 += dy; } /* special case for grace notes */ if (k1->as.flags & ABC_F_GRACE) x1 = k1->x - GSTEM_XOFF * .5; if (k2->as.flags & ABC_F_GRACE) x2 = k2->x + GSTEM_XOFF * 1.5; h = 0; a = (y2 - y1) / (x2 - x1); if (k1 != k2) { addy = y1 - a * x1; for (k = k1->next; k != k2 ; k = k->next) { if (k->staff != upstaff) continue; switch (k->type) { case NOTE: case REST: if (s > 0) { y = 3 * (k->pits[k->nhd] - 18) + 6; if (y < k->ys + 2) y = k->ys + 2; y -= a * k->x + addy; if (y > h) h = y; } else { y = 3 * (k->pits[0] - 18) - 6; if (y > k->ys - 2) y = k->ys - 2; y -= a * k->x + addy; if (y < h) h = y; } break; case GRACE: { struct SYMBOL *g; for (g = k->grace; g != 0; g = g->next) { y = g->y - a * k->x - addy; if (s > 0) { y += GSTEM + 2; if (y > h) h = y; } else { y -= 2; if (y < h) h = y; } } break; } } } y1 += .45 * h; y2 += .45 * h; h *= .65; } if (nn > 3) height = (.08 * (x2 - x1) + 12) * s; else height = (.03 * (x2 - x1) + 8) * s; if (s > 0) { if (height < 3 * h) height = 3 * h; if (height > 40) height = 40; } else { if (height > 3 * h) height = 3 * h; if (height < -40) height = -40; } y = y2 - y1; if (y < 0) y = -y; if (s > 0) { if (height < .8 * y) height = .8 * y; } else { if (height > -.8 * y) height = -.8 * y; } height *= cfmt.slurheight; /*fixme: ugly!*/ if (m1 >= 0) y1 = (float) (3 * (k1->pits[m1] - 18) + 5 * s); if (m2 >= 0) y2 = (float) (3 * (k2->pits[m2] - 18) + 5 * s); slur_out(x1, y1, x2, y2, s, height, dotted, upstaff); /* have room for other symbols */ dx = x2 - x1; a = (y2 - y1) / dx; /*fixme: it seems to work with .4, but why?*/ addy = y1 - a * x1 + .4 * height; for (k = k1; ; k = k->next) { if (k == k2) break; if (k->staff == upstaff) { y = a * k->x + addy; if (k->ymx < y) k->ymx = y; else if (k->ymn > y) k->ymn = y; if (k->next == k2) dx = x2; else dx = k->next->x; if (k != k1) x1 = k->x; dx -= x1; y_set(k, s > 0, x1, dx, y); } } return s > 0 ? SL_ABOVE : SL_BELOW; } /* -- draw the slurs between 2 symbols --*/ static void draw_slurs(struct SYMBOL *first, struct SYMBOL *last) { struct SYMBOL *s, *s1, *k, *gr1, *gr2; int i, m1, m2, again, gr1_out, slur_type, cont; for (;;) { again = 0; gr1 = gr2 = 0; s = first; for (;;) { if (s == 0 || s == last) { if (gr1 == 0 || (s = gr1->next) == 0 || s == last) break; gr1 = 0; } if (s->type == GRACE) { gr1 = s; s = s->grace; continue; } if ((s->type != NOTE && s->type != REST) || (s->as.u.note.slur_st == 0 && !(s->sflags & S_SL1))) { s = s->next; continue; } k = 0; /* find matching slur end */ s1 = s->next; gr1_out = 0; for (;;) { if (s1 == 0) { if (gr2 != 0) { s1 = gr2->next; gr2 = 0; continue; } if (gr1 == 0 || gr1_out) break; s1 = gr1->next; gr1_out = 1; continue; } if (s1->type == GRACE) { gr2 = s1; s1 = s1->grace; continue; } if (s1->type == BAR && ((s1->sflags & S_RRBAR) || s1->as.u.bar.type == B_THIN_THICK || s1->as.u.bar.type == B_THICK_THIN || (s1->as.u.bar.repeat_bar && s1->as.text != 0 && s1->as.text[0] != '1'))) { k = s1; break; } if (s1->type != NOTE && s1->type != REST) { s1 = s1->next; continue; } if (s1->as.u.note.slur_end || (s1->sflags & S_SL2)) { k = s1; break; } if (s1->as.u.note.slur_st || (s1->sflags & S_SL1)) { again++; break; } if (s1 == last) break; s1 = s1->next; } if (s1 == 0) k = next_scut(s); else if (k == 0) { s = s1; if (s == last) break; continue; } /* if slur in grace note sequence, change the linkages */ if (gr1 != 0) { for (s1 = s; s1->next != 0; s1 = s1->next) ; s1->next = gr1->next; gr1->next->prev = s1; gr1->as.u.note.slur_st = SL_AUTO; } if (gr2 != 0) { gr2->prev->next = gr2->grace; gr2->grace->prev = gr2->prev; gr2->as.u.note.slur_st = SL_AUTO; } if (s->as.u.note.slur_st) { slur_type = s->as.u.note.slur_st & 0x03; s->as.u.note.slur_st >>= 2; m1 = -1; } else { for (m1 = 0; m1 <= s->nhd; m1++) if (s->as.u.note.sl1[m1]) break; slur_type = s->as.u.note.sl1[m1] & 0x03; s->as.u.note.sl1[m1] >>= 2; if (s->as.u.note.sl1[m1] == 0) { for (i = m1 + 1; i <= s->nhd; i++) if (s->as.u.note.sl1[i]) break; if (i > s->nhd) s->sflags &= ~S_SL1; } } m2 = -1; cont = 0; if ((k->type == NOTE || k->type == REST) && (k->as.u.note.slur_end || (k->sflags & S_SL2))) { if (k->as.u.note.slur_end) k->as.u.note.slur_end--; else { for (m2 = 0; m2 <= k->nhd; m2++) if (k->as.u.note.sl2[m2]) break; k->as.u.note.sl2[m2]--; if (k->as.u.note.sl2[m2] == 0) { for (i = m2 + 1; i <= k->nhd; i++) if (k->as.u.note.sl2[i]) break; if (i > k->nhd) k->sflags &= ~S_SL2; } } } else { if (k->type != BAR || (!(k->sflags & S_RRBAR) && k->as.u.bar.type != B_THIN_THICK && k->as.u.bar.type != B_THICK_THIN && (!k->as.u.bar.repeat_bar || k->as.text == 0 || k->as.text[0] == '1'))) cont = 1; } slur_type = draw_slur(s, k, m1, m2, slur_type, s->as.flags & ABC_F_DOTTED_SLUR); if (cont) { /*fixme: the slur types are inverted*/ voice_tb[k->voice].slur_st <<= 2; voice_tb[k->voice].slur_st += slur_type; } /* if slur in grace note sequence, restore the linkages */ if (gr1 != 0) { gr1->next->prev->next = 0; gr1->next->prev = gr1; } if (gr2 != 0) { gr2->prev->next = gr2; gr2->grace->prev = 0; } if (s->as.u.note.slur_st || (s->sflags & S_SL1)) continue; if (s == last) break; s = s->next; } if (again == 0) break; } } /* -- draw a tuplet -- */ /* (the staves are not yet defined) */ /* See 'tuplets' in format.txt about the value of 'u' */ static struct SYMBOL *draw_tuplet(struct SYMBOL *t) { struct SYMBOL *s1, *s2, *sy, *next; int r, upstaff, nb_only, some_slur; float x1, x2, y1, y2, xm, ym, s, s0, yy, yx, dy; next = t; if ((t->u & 0x0f00) == 0x100) /* if 'when' == never */ return next; /* search the first and last notes/rests of the tuplet */ r = t->as.u.tuplet.r_plet; s1 = 0; some_slur = 0; upstaff = t->staff; for (s2 = t->next; s2 != 0; s2 = s2->next) { if (s2->dur == 0 || (s2->type != NOTE && s2->type != REST)) { if (s2->type == TUPLET) { sy = draw_tuplet(s2); /* nested tuplet */ if (sy->time > next->time) next = sy; } else if (s2->type == GRACE) { for (sy = s2->grace; sy != 0; sy = sy->next) { if (sy->as.u.note.slur_st || (sy->sflags & S_SL1)) some_slur = 1; } } continue; } if (s2->as.u.note.slur_st || (s2->sflags & S_SL1)) some_slur = 1; if (s2->staff < upstaff) upstaff = s2->staff; if (s1 == 0) s1 = s2; if (--r <= 0) break; } if (s2 == 0) return next; /* no solution... */ if (s2->time > next->time) next = s2; /* draw the slurs when inside the tuplet */ if (some_slur) draw_slurs(s1, s2); if ((t->u & 0x0f0) == 0x10) { /* 'what' == slur */ nb_only = 1; draw_slur(s1, s2, -1, -1, s1->stem > 0 ? SL_ABOVE : SL_BELOW, 0); } else { /* search if a bracket is needed */ if ((t->u & 0x0f00) == 0x200 /* if 'when' == always */ || s1->type != NOTE || s2->type != NOTE) nb_only = 0; else { nb_only = 1; for (sy = s1; ; sy = sy->next) { if (sy->type != NOTE && sy->type != REST) { if (sy->type == GRACE) continue; nb_only = 0; break; } if (sy == s2) break; if (sy->as.flags & ABC_F_WORD_END) { nb_only = 0; break; } } if (nb_only && !(s1->sflags & (S_WORD_ST | S_BEAM_BR1 | S_BEAM_BR2))) { for (sy = s1->prev; sy != 0; sy = sy->prev) { if (sy->dur != 0 && (sy->type == NOTE || sy->type == REST)) { if (sy->nflags >= s1->nflags) nb_only = 0; break; } } } if (nb_only && !(s2->as.flags & ABC_F_WORD_END)) { for (sy = s2->next; sy != 0; sy = sy->next) { if (sy->dur != 0) { if (!(sy->sflags & (S_BEAM_BR1 | S_BEAM_BR2)) && sy->nflags >= s2->nflags) nb_only = 0; break; } } } } } /* if number only, draw it */ if (nb_only) { float a, b; if ((t->u & 0x0f) == 1) /* if 'value' == none */ return next; xm = (s2->x + s1->x) * .5; a = (s2->ys - s1->ys) / (s2->x - s1->x); b = s1->ys - a * s1->x; #if 1 #if 0 for (sy = s1; ; sy = sy->next) { if (sy->x >= xm) break; } #endif yy = a * xm + b; if (s1->stem > 0) { ym = y_get(s1, 1, xm - 3, 6, 0); if (ym > yy) b += ym - yy; b += 4; } else { ym = y_get(s1, 0, xm - 3, 6, 0); if (ym < yy) b += ym - yy; b -= 12; } #else if (s1->stem > 0) { for (sy = s1; ; sy = sy->next) { yy = a * sy->x + b; ym = sy->ymx; if (ym > yy) b += ym - yy; if (sy == s2) break; } b += 4; } else { for (sy = s1; ; sy = sy->next) { yy = a * sy->x + b; ym = sy->ymn; if (ym < yy) b += ym - yy; if (sy == s2) break; } b -= 12; } #endif if (s1->stem * s2->stem > 0) { if (s1->stem > 0) xm += GSTEM_XOFF; else xm -= GSTEM_XOFF; } ym = a * xm + b; if ((t->u & 0x0f) == 0) /* if 'value' == number */ PUT1("(%d)", t->as.u.tuplet.p_plet); else PUT2("(%d:%d)", t->as.u.tuplet.p_plet, t->as.u.tuplet.q_plet); putxy(xm, ym); PUT1("y%d bnum\n", s1->staff); for (sy = s1; ; sy = sy->next) { if (sy->x >= xm) break; } if (s1->stem > 0) { ym += 8; if (sy->ymx < ym) sy->ymx = (short) ym; y_set(sy, 1, xm - 3, 6, ym); } else { if (sy->ymn > ym) sy->ymn = (short) ym; y_set(sy, 0, xm - 3, 6, ym); } return next; } if ((t->u & 0x0f0) != 0) /* if 'what' != square */ fprintf(stderr, "'what' value of %%%%tuplets not yet coded\n"); /*fixme: two staves not treated*/ /*fixme: to optimize*/ if (s1->multi >= 0) { /* sole or upper voice: the bracket is above the staff */ x1 = s1->x - 4; x2 = s2->x + 4; r = s2->stem >= 0 ? 0 : s2->nhd; if (s2->shhd[r] > 0) x2 += s2->shhd[r]; y1 = 24; if (s1->staff == upstaff) { sy = s1; if (sy->type != NOTE) { for (sy = sy->next; sy != s2; sy = sy->next) if (sy->type == NOTE) break; } ym = y_get(sy, 1, sy->x, 0, 0); if (ym > y1) y1 = ym; if (s1->stem > 0) x1 += 3; } y2 = 24; if (s2->staff == upstaff) { sy = s2; if (sy->type != NOTE) { for (sy = sy->prev; sy != s1; sy = sy->prev) if (sy->type == NOTE) break; } ym = y_get(sy, 1, sy->x, 0, 0); if (ym > y2) y2 = ym; if (s2->stem > 0) x2 += 3; } if (s2->type == NOTE && s2->prev->type == NOTE && s2->dur > s2->prev->dur) x2 += 5; xm = .5 * (x1 + x2); ym = .5 * (y1 + y2); s = (y2 - y1) / (x2 - x1); s0 = 3 * (s2->pits[s2->nhd] - s1->pits[s1->nhd]) / (x2 - x1); if (s0 > 0) { if (s < 0) s = 0; else if (s > s0) s = s0; } else { if (s > 0) s = 0; else if (s < s0) s = s0; } if (s * s < .1 * .1) s = 0; /* shift up bracket if needed */ dy = 0; for (sy = s1; ; sy = sy->next) { if (sy->dur == 0 /* not a note nor a rest */ || sy->staff != upstaff) { if (sy == s2) break; continue; } yy = ym + (sy->x - xm) * s; yx = y_get(sy, 1, sy->x, 0, 0); if (yx - yy > dy) dy = yx - yy; if (sy == s2) break; } ym += dy + 4; y1 = ym + s * (x1 - xm); y2 = ym + s * (x2 - xm); putxy(x2 - x1, y2 - y1); putxy(x1, y1 + 4); PUT1("y%d tubr", upstaff); /* shift the slurs / decorations */ ym += 8; for (sy = s1; ; sy = sy->next) { if (sy->staff == upstaff) { yy = ym + (sy->x - xm) * s; if (sy->ymx < yy) sy->ymx = yy; if (sy == s2) break; y_set(sy, 1, sy->x, sy->next->x - sy->x, yy); } else if (sy == s2) break; } } else { /* lower voice of the staff: the bracket is below the staff */ /*fixme: think to all that again..*/ x1 = s1->x - 8; x2 = s2->x + 2; if (s2->shhd[s2->nhd] > 0) x2 += s2->shhd[s2->nhd]; if (s2->type == NOTE && s2->prev->type == NOTE && s2->dur > s2->prev->dur) x2 += 5; if (s1->staff == upstaff) { sy = s1; if (sy->type != NOTE) { for (sy = sy->next; sy != s2; sy = sy->next) if (sy->type == NOTE) break; } y1 = y_get(sy, 0, sy->x, 0, 0); } else y1 = 0; if (s2->staff == upstaff) { sy = s2; if (sy->type != NOTE) { for (sy = sy->prev; sy != s1; sy = sy->prev) if (sy->type == NOTE) break; } y2 = y_get(sy, 0, sy->x, 0, 0); } else y2 = 0; xm = .5 * (x1 + x2); ym = .5 * (y1 + y2); s = (y2 - y1) / (x2 - x1); s0 = 3 * (s2->pits[0] - s1->pits[0]) / (x2 - x1); if (s0 > 0) { if (s < 0) s = 0; else if (s > s0) s = s0; } else { if (s > 0) s = 0; else if (s < s0) s = s0; } if (s * s < .1 * .1) s = 0; /* shift down bracket if needed */ dy = 0; for (sy = s1; ; sy = sy->next) { if (sy->dur == 0 /* not a note nor a rest */ || sy->staff != upstaff) { if (sy == s2) break; continue; } yy = ym + (sy->x - xm) * s; yx = y_get(sy, 0, sy->x, 0, 0); if (yx - yy < dy) dy = yx - yy; if (sy == s2) break; } ym += dy - 12; y1 = ym + s * (x1 - xm); y2 = ym + s * (x2 - xm); putxy(x2 - x1, y2 - y1); putxy(x1, y1 + 4); PUT1("y%d tubrl",upstaff); /* shift the slurs / decorations */ ym -= 8; for (sy = s1; ; sy = sy->next) { if (sy->staff == upstaff) { if (sy == s2) break; yy = ym + (sy->x - xm) * s; if (sy->ymn > yy) sy->ymn = (short) yy; y_set(sy, 0, sy->x, sy->next->x - sy->x, yy); } if (sy == s2) break; } } /* lower voice */ if ((t->u & 0x0f) == 1) { /* if 'value' == none */ PUT0("\n"); return next; } yy = .5 * (y1 + y2); if ((t->u & 0x0f) == 0) /* if 'value' == number */ PUT1("(%d)", t->as.u.tuplet.p_plet); else PUT2("(%d:%d)", t->as.u.tuplet.p_plet, t->as.u.tuplet.q_plet); putxy(xm, yy); PUT1("y%d bnumb\n", upstaff); return next; } /* -- draw the ties between two notes/chords -- */ static void draw_note_ties(struct SYMBOL *k1, struct SYMBOL *k2, int ntie, int *mhead1, int *mhead2, int dotted, int job) { int i, s, m1, m2, p1, p2, y1, y2; float x1, x2, h; for (i = 0; i < ntie; i++) { m1 = mhead1[i]; p1 = k1->pits[m1]; m2 = mhead2[i]; p2 = k2->pits[m2]; if (k1->as.u.note.ti1[m1] == SL_ABOVE) s = 1; else s = -1; x1 = k1->x; h = k1->shhd[m1]; /* head shift */ if (s > 0) { if (m1 < k1->nhd && k1->pits[m1] + 1 == k1->pits[m1 + 1]) if (k1->shhd[m1 + 1] > h) h = k1->shhd[m1 + 1]; } else { if (m1 > 0 && k1->pits[m1] == k1->pits[m1 - 1] + 1) if (k1->shhd[m1 - 1] > h) h = k1->shhd[m1 - 1]; } if (h > 0) x1 += h; x2 = k2->x; h = k2->shhd[m2]; if (s > 0) { if (m2 < k2->nhd && k2->pits[m2] + 1 == k2->pits[m2 + 1]) if (k2->shhd[m2 + 1] < h) h = k2->shhd[m2 + 1]; } else { if (m2 > 0 && k2->pits[m2] == k2->pits[m2 - 1] + 1) if (k2->shhd[m2 - 1] < h) h = k2->shhd[m2 - 1]; } if (h < 0) x2 += h; if (job == 2) { /* half tie from last note in line */ x2 -= k2->wl; if (k1 == k2) x2 = realwidth; if (x2 < x1 + 16) x2 = x1 + 16; } else if (job == 1) { /* half tie to first note in line */ x1 = k1->x; if (x1 > x2 - 20) x1 = x2 - 20; } if (x2 - x1 > 20) { x1 += 2; x2 -= 2; } y1 = 3 * (p1 - 18) + 2 * s; y2 = 3 * (p2 - 18) + 2 * s; if (job != 1) { if (k1->nhd != 0) x1 += 4.5; else y1 += ((p1 % 2) ? 3 : 2) * s; if (s > 0) { if (k1->nflags > -2 && k1->stem > 0 && k1->nhd == 0) x1 += 4.5; if (!(p1 % 2) && k1->dots > 0) y1 = 3 * (p1 - 18) + 6; } } if (job != 2) { if (k2->nhd != 0) x2 -= 4.5; else y2 += ((p2 % 2) ? 3 : 2) * s; if (s < 0) { if (k2->nflags > -2 && k2->stem < 0 && k2->nhd == 0) x2 -= 4.5; } if (job != 0) y1 = y2; } else { y2 = y1; if (k1 == k2) /* if continuation on next line */ k1->as.u.note.ti1[m1] = s > 0 ? SL_ABOVE : SL_BELOW; } /* tie between 2 staves */ /*fixme: should also do that when clef change?*/ /*fixme:dotted not treated*/ if (k1->staff != k2->staff) { s = k1->staff - k2->staff; y1 = 3 * (p1 - 18) + 3 * s; y2 = 3 * (p2 - 18) - 3 * s; x1 += 4; x2 -= 4; putxy(x1, staff_tb[k1->staff].y + y1); PUT0("M "); putxy(x2, staff_tb[k2->staff].y + y2); PUT0("lineto stroke\n"); continue; } h = (.04 * (x2 - x1) + 8) * s; slur_out(x1, staff_tb[k1->staff].y + y1, x2, staff_tb[k1->staff].y + y2, s, h, dotted, -1); } } /* -- draw ties between neighboring notes/chords -- */ static void draw_ties(struct SYMBOL *k1, struct SYMBOL *k2, int job) /* 0: normal * 1: at start of line * 2: at end of line */ { int i, m1, nh1, pit, ntie, ntie3, time; int mhead1[MAXHD], mhead2[MAXHD], mhead3[MAXHD]; ntie = ntie3 = 0; nh1 = k1->nhd; time = k1->time + k1->dur; /* 2-note case: set up list of ties to draw */ if (job != 2 && k2->time == time) { for (i = 0; i <= nh1; i++) { if (k1->as.u.note.ti1[i] != 0) { pit = k1->as.u.note.pits[i]; for (m1 = k2->nhd; m1 >= 0; m1--) { if (k2->as.u.note.pits[m1] == pit) { mhead1[ntie] = i; mhead2[ntie++] = m1; break; } } if (m1 < 0) mhead3[ntie3++] = i; } } draw_note_ties(k1, k2, ntie, mhead1, mhead2, k1->as.flags & ABC_F_DOTTED_TIE, job); if (ntie3 == 0) return; /* no bad tie */ } else { /* half ties from last note in line */ for (i = 0; i <= nh1; i++) { if (k1->as.u.note.ti1[i]) mhead3[ntie3++] = i; } } /* try an other voice */ k2 = k1->ts_next; while (k2 != 0 && k2->time < time) k2 = k2->ts_next; while (k2 != 0 && k2->time == time) { if (k2->type != NOTE || k2->staff != k1->staff) { k2 = k2->ts_next; continue; } ntie = 0; for (i = ntie3; --i >= 0; ) { pit = k1->as.u.note.pits[mhead3[i]]; for (m1 = k2->nhd; m1 >= 0; m1--) { if (k2->as.u.note.pits[m1] == pit) { mhead1[ntie] = mhead3[i]; mhead2[ntie++] = m1; ntie3--; mhead3[i] = mhead3[ntie3]; break; } } } if (ntie > 0) { draw_note_ties(k1, k2, ntie, mhead1, mhead2, k1->as.flags & ABC_F_DOTTED_TIE, job == 1 ? 1 : 0); if (ntie3 == 0) return; } k2 = k2->ts_next; } if (ntie3 != 0) { if (job != 2) error(1, k1, "Bad tie"); else draw_note_ties(k1, k2 ? k2 : k1, ntie3, mhead3, mhead3, k1->as.flags & ABC_F_DOTTED_TIE, job); } } /* -- draw all ties between neighboring notes -- */ static void draw_all_ties(struct VOICE_S *p_voice) { struct SYMBOL *s1, *s2, *rtie; struct SYMBOL tie; for (s1 = p_voice->sym->next; s1 != 0; s1 = s1->next) if (s1->type != KEYSIG && s1->type != TIMESIG) break; rtie = p_voice->rtie; /* tie from 1st repeat bar */ for (s2 = s1; s2 != 0; s2 = s2->next) { if (s2->type == NOTE) break; if (s2->type != BAR || !s2->as.u.bar.repeat_bar || s2->as.text == 0) continue; if (s2->as.text[0] == '1') /* 1st repeat bar */ rtie = p_voice->tie; else p_voice->tie = rtie; } if (s2 == 0) return; if (p_voice->tie != 0) { /* tie from previous line */ p_voice->tie->x = s1->x + s1->wr; s1 = p_voice->tie; p_voice->tie = 0; s1->staff = s2->staff; s1->ts_next = first_voice->sym->next; /* (for tie to other voice) */ draw_ties(s1, s2, 1); /* tie to 1st note */ } for (;;) { for (s1 = s2; s1 != 0; s1 = s1->next) { if (s1->sflags & S_TI1) break; if (rtie == 0) continue; if (s1->type != BAR || !s1->as.u.bar.repeat_bar || s1->as.text == 0) continue; if (s1->as.text[0] == '1') { /* 1st repeat bar */ rtie = 0; continue; } for (s2 = s1->next; s2 != 0; s2 = s2->next) if (s2->type == NOTE) break; if (s2 == 0) { s1 = 0; break; } memcpy(&tie, rtie, sizeof tie); tie.x = s1->x + s1->wr; tie.next = s2; tie.staff = s2->staff; tie.time = s2->time - tie.dur; draw_ties(&tie, s2, 1); } if (s1 == 0) break; for (s2 = s1->next; s2 != 0; s2 = s2->next) { if (s2->type == NOTE) break; if (s2->type == BAR) { if ((s2->sflags & S_RRBAR) || s2->as.u.bar.type == B_THIN_THICK || s2->as.u.bar.type == B_THICK_THIN) break; if (!s2->as.u.bar.repeat_bar || s2->as.text == 0) continue; if (s2->as.text[0] != '1') break; rtie = s1; /* 1st repeat bar */ } } if (s2 == 0) { draw_ties(s1, s1, 2); p_voice->tie = s1; break; } draw_ties(s1, s2, s2->type == NOTE ? 0 : 2); } p_voice->rtie = rtie; } /* -- draw all phrasing slurs for one staff -- */ /* (the staves are not yet defined) */ static void draw_all_slurs(struct VOICE_S *p_voice) { struct SYMBOL *s, *k; int i, m2, slur_type; unsigned char slur_st; if ((s = p_voice->sym->next) == 0) return; slur_st = p_voice->slur_st; p_voice->slur_st = 0; /* draw the slurs inside the music line */ draw_slurs(s, 0); /* do unbalanced slurs still left over */ for ( ; s != 0; s = s->next) { if (s->type != NOTE && s->type != REST) continue; while (s->as.u.note.slur_end || (s->sflags & S_SL2)) { if (s->as.u.note.slur_end) { s->as.u.note.slur_end--; m2 = -1; } else { for (m2 = 0; m2 <= s->nhd; m2++) if (s->as.u.note.sl2[m2]) break; s->as.u.note.sl2[m2]--; if (s->as.u.note.sl2[m2] == 0) { for (i = m2 + 1; i <= s->nhd; i++) if (s->as.u.note.sl2[i]) break; if (i > s->nhd) s->sflags &= ~S_SL2; } } slur_type = slur_st & 0x03; k = prev_scut(s); draw_slur(k, s, -1, m2, slur_type, 0); if (k->type != BAR || (!(k->sflags & S_RRBAR) && k->as.u.bar.type != B_THIN_THICK && k->as.u.bar.type != B_THICK_THIN && (!k->as.u.bar.repeat_bar || k->as.text == 0 || k->as.text[0] == '1'))) slur_st >>= 2; } } s = p_voice->sym->next; while (slur_st != 0) { slur_type = slur_st & 0x03; slur_st >>= 2; k = next_scut(s); draw_slur(s, k, -1, -1, slur_type, 0); if (k->type != BAR || (!(k->sflags & S_RRBAR) && k->as.u.bar.type != B_THIN_THICK && k->as.u.bar.type != B_THICK_THIN && (!k->as.u.bar.repeat_bar || k->as.text == 0 || k->as.text[0] == '1'))) { /*fixme: the slur types are inverted*/ p_voice->slur_st <<= 2; p_voice->slur_st += slur_type; } } } /* -- draw the lyrics under (or above) notes -- */ /* !! this routine is tied to set_width() !! */ static float draw_lyrics(struct VOICE_S *p_voice, int nly, float y, int incr) { int hyflag, l, j, lflag; char *p; float lastx, w, lskip, desc; struct SYMBOL *s; struct FONTSPEC *f; struct lyrics *ly; struct lyl *lyl; /* treat the tablature */ outft = -1; /* force font output */ if (p_voice->tabhead != 0) { if (incr > 0) y -= p_voice->tabhu; PUT2("/y{%.1f y%d}def ", y, p_voice->staff); set_font(VOCALFONT); PUT3("%.1f 0 y %d %s\n", realwidth, nly, p_voice->tabhead); for (j = 0; j < nly ; j++) { for (s = p_voice->sym->next; s != 0; s = s->next) { if ((ly = s->ly) == 0 || (lyl = ly->lyl[j]) == 0) { if (s->type == BAR) { p = &tex_buf[16]; *p-- = '\0'; l = bar_cnv(s->as.u.bar.type); while (l != 0) { *p-- = "?|[]:???"[l & 0x07]; l >>= 4; } p++; PUT4("(%s)%.1f y %d %s ", p, s->x, j, p_voice->tabbar); } continue; } PUT4("(%s)%.1f y %d %s ", lyl->t, s->x, j, p_voice->tabnote); } PUT0("\n"); } if (incr > 0) return y; return y + p_voice->tabhu; } lskip = 0; /* (compiler warning) */ f = 0; /* (force new font) */ if (incr > 0) { /* under the staff */ j = 0; /*fixme: may not be the current font*/ y -= cfmt.font_tb[VOCALFONT].size; if (y > -cfmt.vocalspace) y = -cfmt.vocalspace; } else { j = nly - 1; nly = -1; if (y < 24 + cfmt.vocalspace - cfmt.font_tb[VOCALFONT].size) y = 24 + cfmt.vocalspace - cfmt.font_tb[VOCALFONT].size; } /*fixme: may not be the current font*/ desc = cfmt.font_tb[VOCALFONT].size * .25; /* descent */ for (; j != nly ; j += incr) { float x0, shift; PUT2("/y{%.1f y%d}def ", y + desc, p_voice->staff); hyflag = lflag = 0; if (p_voice->hy_st & (1 << j)) { hyflag = 1; p_voice->hy_st &= ~(1 << j); } for (s = p_voice->sym->next; /*s != 0*/; s = s->next) if (s->type != KEYSIG && s->type != TIMESIG) break; lastx = s->prev->x; x0 = 0; if (f != 0) lskip = f->size * 1.1; for ( ; s != 0; s = s->next) { if ((ly = s->ly) == 0 || (lyl = ly->lyl[j]) == 0) { switch (s->type) { case REST: case MREST: if (lflag) { PUT2("%.1f %.1f y wln ", x0 - lastx, lastx + 3); lflag = 0; lastx = s->x + s->wr; } } continue; } if (lyl->f != f) { /* font change */ f = lyl->f; set_font(f - cfmt.font_tb); if (lskip < f->size * 1.1) lskip = f->size * 1.1; } p = lyl->t; w = lyl->w; shift = lyl->s; if (hyflag) { if (*p == '\x03') /* '_' */ *p = '\x02'; else if (*p != '\x02') { /* not '-' */ PUT2("%.1f %.1f y hyph ", s->x - shift - lastx, lastx); hyflag = 0; lastx = s->x + s->wr; } } if (lflag && *p != '\x03') { /* not '_' */ PUT2("%.1f %.1f y wln ", x0 - lastx + 3, lastx + 3); lflag = 0; lastx = s->x + s->wr; } if (*p == '\x02' /* '-' */ || *p == '\x03') { /* '_' */ if (x0 == 0 && lastx > s->x - 18) lastx = s->x - 18; if (*p == '\x02') hyflag = 1; else lflag = 1; x0 = s->x - shift; continue; } x0 = s->x - shift; l = strlen(p) - 1; if (p[l] == '\x02') { /* '-' at end */ p[l] = '\0'; hyflag = 1; } PUT2("%.1f y M(%s)lyshow ", x0, p); lastx = x0 + w; } if (hyflag) { x0 = realwidth - 10; if (x0 < lastx + 10) x0 = lastx + 10; PUT2("%.1f %.1f y hyph ", x0 - lastx, lastx); if (cfmt.hyphencont) p_voice->hy_st |= (1 << j); } /* see if any underscore in the next line */ for (s = tsnext; s != 0; s = s->ts_next) if (s->voice == p_voice - voice_tb) break; for ( ; s != 0; s = s->next) { if (s->type == NOTE) { if (s->ly != 0 && s->ly->lyl[j] != 0 && s->ly->lyl[j]->t[0] == '\x03') { lflag = 1; x0 = realwidth - 15; if (x0 < lastx + 12) x0 = lastx + 12; } break; } } if (lflag) PUT2("%.1f %.1f y wln", x0 - lastx + 3, lastx + 3); PUT0("\n"); if (incr > 0) y -= lskip; else y += lskip; } if (incr > 0) y += lskip; return y; } /* -- draw all the lyrics -- */ /* (the staves are not yet defined) */ static void draw_all_lyrics(void) { struct VOICE_S *p_voice; struct SYMBOL *s; int staff, voice, nly, i; struct { short a, b; float top, bot; } lyst_tb[MAXSTAFF]; char nly_tb[MAXVOICE]; char above_tb[MAXVOICE]; char rv_tb[MAXVOICE]; float top, bot; /* check if any lyric */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (p_voice->have_ly) break; } if (p_voice == 0) return; /* compute the number of lyrics per voice - staff * and their y offset on the staff */ memset(above_tb, 0, sizeof above_tb); memset(nly_tb, 0, sizeof nly_tb); memset(lyst_tb, 0, sizeof lyst_tb); staff = -1; top = bot = 0; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { voice = p_voice - voice_tb; if (p_voice->staff != staff) { top = 0; bot = 0; staff = p_voice->staff; } nly = 0; for (s = p_voice->sym->next; s != 0; s = s->next) { struct lyrics *ly; float x, y, w; if ((ly = s->ly) == 0) continue; /*fixme:should get the real width*/ x = s->x; if (ly->lyl[0] != 0) { x -= ly->lyl[0]->s; w = ly->lyl[0]->w; } else w = 10; y = y_get(s, 1, x, w, 0); if (top < y) top = y; y = y_get(s, 0, x, w, 0); if (bot > y) bot = y; for (i = MAXLY; --i >= 0; ) if (ly->lyl[i] != 0) break; i++; if (i > nly) nly = i; } lyst_tb[staff].top = top; lyst_tb[staff].bot = bot; if (nly == 0) continue; nly_tb[voice] = nly; if (p_voice->ly_pos != 0) above_tb[voice] = p_voice->ly_pos > 0; else if (cfmt.vocalabove || (p_voice->next != 0 && p_voice->next->staff == staff && p_voice->next->have_ly)) above_tb[voice] = 1; if (above_tb[voice]) lyst_tb[staff].a = 1; else lyst_tb[staff].b = 1; } /* draw the lyrics under the staves */ i = 0; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (!p_voice->have_ly) continue; voice = p_voice - voice_tb; if (above_tb[voice]) { rv_tb[i++] = voice; continue; } staff = p_voice->staff; lyst_tb[staff].bot = draw_lyrics(p_voice, nly_tb[voice], lyst_tb[staff].bot, 1); if (p_voice->tabhead != 0 && p_voice->tabha != 0) { lyst_tb[staff].top += p_voice->tabha; lyst_tb[staff].a = 1; } } /* draw the lyrics above the staff */ while (--i >= 0) { voice = rv_tb[i]; p_voice = &voice_tb[voice]; staff = p_voice->staff; lyst_tb[staff].top = draw_lyrics(p_voice, nly_tb[voice], lyst_tb[staff].top, -1); } /* set the max y offsets of all symbols */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { staff = p_voice->staff; if (lyst_tb[staff].a) { top = lyst_tb[staff].top + 2; for (s = p_voice->sym->next; s != 0; s = s->next) { /*fixme: may have lyrics crossing a next symbol*/ if (s->ly != 0) { /*fixme:should set the real width*/ y_set(s, 1, s->x, 10, top); } } } if (lyst_tb[staff].b) { bot = lyst_tb[staff].bot - 2; for (s = p_voice->sym->next; s != 0; s = s->next) { if (s->ly != 0) { /*fixme:should set the real width*/ y_set(s, 0, s->x, 10, bot); } } } } } /* -- draw the symbols near the notes -- */ /* (the staves are not yet defined) */ /* order: * - beams * - decorations near the notes * - measure bar numbers * - n-plets * - decorations tied to the notes * - slurs * - guitar chords * - then remaining decorations */ void draw_sym_near(void) { struct VOICE_S *p_voice; struct SYMBOL *s; /* calculate the beams but don't draw them (the staves are undefined) */ for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { struct BEAM bm; for (s = p_voice->sym->next; s != 0; s = s->next) { if (s->type == NOTE && (s->sflags & S_WORD_ST) && !(s->as.flags & ABC_F_WORD_END)) calculate_beam(&bm, s); } } /* initialize the y offsets */ { int i, staff; for (staff = 0; staff <= nstaff; staff++) { for (i = 0; i < YSTEP; i++) { staff_tb[staff].top[i] = 0; staff_tb[staff].bot[i] = 24; } } } set_tie_room(); draw_deco_near(); /* set the min/max vertical offsets */ for (s = first_voice->sym; s != 0; s = s->ts_next) { int y; struct SYMBOL *g; if (s->prev == 0) continue; /* skip the clefs */ if (s->as.flags & ABC_F_INVIS) continue; if ((g = s->grace) != 0) { for ( ; g != 0; g = g->next) { y_set(s, 1, g->x - g->wl, g->wl + g->wr, g->ymx); y_set(s, 0, g->x - g->wl, g->wl + g->wr, g->ymn); } continue; } if (s->type != MREST) { y_set(s, 1, s->x - s->wl, s->wl + s->wr, s->ymx); y_set(s, 0, s->x - s->wl, s->wl + s->wr, s->ymn); } else { y_set(s, 1, s->x - 16, 32, s->ymx); } if (s->type != NOTE) continue; /* have room for the accidentals */ if (s->as.u.note.accs[s->nhd]) { y = s->y + 8; if (s->ymx < y) s->ymx = y; y_set(s, 1, s->x, 0., y); } if (s->as.u.note.accs[0]) { y = s->y; if ((s->as.u.note.accs[0] & 0x07) == A_SH || s->as.u.note.accs[0] == A_NT) y -= 7; else y -= 5; if (s->ymn > y) s->ymn = y; y_set(s, 0, s->x, 0., y); } } if (cfmt.measurenb >= 0) draw_measnb(); draw_deco_note(); for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { for (s = p_voice->sym->next; s != 0; s = s->next) { if (s->type == TUPLET) s = draw_tuplet(s); } draw_all_slurs(p_voice); } /* set the top and bottom for all symbols to be out of the staves */ { int top, bot, i, staff; for (staff = 0; staff <= nstaff; staff++) { top = staff_tb[staff].topbar + 2; bot = staff_tb[staff].botbar - 2; /*fixme:should handle stafflines changes*/ for (i = 0; i < YSTEP; i++) { if (top > staff_tb[staff].top[i]) staff_tb[staff].top[i] = (float) top; if (bot < staff_tb[staff].bot[i]) staff_tb[staff].bot[i] = (float) bot; } } } draw_all_lyrics(); outft = -1; /* force font output */ draw_deco_staff(); } /* -- draw remaining symbols when the staves are defined -- */ static void draw_symbols(struct VOICE_S *p_voice, int first) { struct BEAM bm; struct SYMBOL *s; float x, y; int staff; /* output the PostScript code at start of line */ for (s = p_voice->sym; s != 0; s = s->next) { switch (s->type) { case CLEF: case KEYSIG: case TIMESIG: case TEMPO: case BAR: case WHISTLE: continue; /* skip the symbols added by init_music_line() */ case FMTCHG: if (s->u == PSSEQ) { PUT1("%s\n", s->as.text); s->u = -1; /* done */ continue; } break; } break; } /* draw the staff, skipping the staff breaks */ if (!p_voice->second) { float xstaff, x2; int staff_st; xstaff = 0; staff_st = first; for (s = p_voice->sym; s != 0; s = s->next) { if (s->type != FMTCHG || s->u != STBRK) continue; x2 = s->prev->x; staff = p_voice->staff; if (s->prev->type != BAR) x2 += s->prev->wr; draw_staff(p_voice, xstaff, x2); if (staff_st) draw_lstaff(xstaff); xstaff = s->xmx != 0 ? s->x : x2; if (first) staff_st = s->xmx > .5 CM; } if (xstaff < realwidth - 8) { draw_staff(p_voice, xstaff, realwidth); if (staff_st) draw_lstaff(xstaff); } } bm.s2 = 0; for (s = p_voice->sym; s != 0; s = s->next) { x = s->x; switch (s->type) { case NOTE: set_scale(s->voice); if ((s->sflags & S_WORD_ST) && !(s->as.flags & ABC_F_WORD_END)) { if (calculate_beam(&bm, s)) draw_beams(&bm); } draw_note(x, s, bm.s2 == 0); if (s == bm.s2) bm.s2 = 0; break; case REST: set_scale(s->voice); draw_rest(s); break; case BAR: if (p_voice->second || (s->as.flags & ABC_F_INVIS)) break; draw_bar(x, s); break; case CLEF: staff = s->staff; if (p_voice->second || p_voice->staff != staff) break; /* only one clef per staff */ { int stafflines; float staffscale; if ((stafflines = s->as.u.clef.stafflines) < 0) stafflines = staff_tb[staff].clef.stafflines; if ((staffscale = s->as.u.clef.staffscale) == 0) staffscale = staff_tb[staff].clef.staffscale; if (s->as.u.clef.type >= 0) memcpy(&staff_tb[staff].clef, &s->as.u.clef, /* (for next lines) */ sizeof s->as.u.clef); staff_tb[staff].clef.stafflines = stafflines; staff_tb[staff].clef.staffscale = staffscale; } if (s->as.u.clef.type < 0 || (s->as.flags & ABC_F_INVIS) || staff_tb[staff].empty) break; set_sscale(staff); y = staff_tb[staff].y; putxy(x, y + s->y); PUT2("%c%cclef\n", s->u ? 's' : ' ', "tcbp"[(unsigned) s->as.u.clef.type]); if (s->as.u.clef.octave == 0) break; /*fixme: break the compatibility and avoid strange numbers*/ if (s->as.u.clef.octave > 0) y += s->ymx - 36 - 12; else y += s->ymn + 19 + 5; putxy(x, y); PUT1("oct%c\n", s->as.u.clef.octave > 0 ? 'u' : 'l'); break; case TIMESIG: memcpy(&p_voice->meter, &s->as.u.meter, sizeof p_voice->meter); if (p_voice->second || staff_tb[s->staff].empty) break; if (cfmt.alignbars && s->staff != 0) break; set_sscale(s->staff); draw_timesig(x, s); break; case KEYSIG: memcpy(&p_voice->key, &s->as.u.key, sizeof p_voice->key); if (p_voice->second || staff_tb[s->staff].empty) break; set_sscale(s->staff); draw_keysig(p_voice, x, s); break; case MREST: set_scale(s->voice); PUT1("(%d)\n", s->as.u.bar.len); putxy(x, staff_tb[s->staff].y); PUT0("mrest\n"); break; case GRACE: set_scale(s->voice); draw_gracenotes(s); break; case TEMPO: case STAVES: case PART: case TUPLET: case WHISTLE: break; /* nothing */ case FMTCHG: if (s->u == PSSEQ) { PUT1("%s\n", s->as.text); break; } #if 1 /*fixme: should remove the other format changes */ break; #else /* fall thru */ #endif default: bug("Symbol not drawn", 1); } } set_scale(p_voice - voice_tb); draw_all_ties(p_voice); } /* -- draw all symbols -- */ void draw_all_symb(void) { struct VOICE_S *p_voice; int first; first = nstaff != 0; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if (staff_tb[p_voice->staff].empty) continue; draw_symbols(p_voice, first); first = 0; } } /* -- draw the name/subname of the voices -- */ void draw_vname(float indent) { struct VOICE_S *p_voice; int n, staff; struct { int nl; char *v[8]; } staff_d[MAXSTAFF], *staff_p; char *p, *q; float y, scale; for (staff = nstaff; staff >= 0; staff--) { if (!staff_tb[staff].empty) break; } if (staff < 0) return; memset(staff_d, 0, sizeof staff_d); n = 0; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { scale = p_voice->scale; if (scale != 1) { PUT2("/scvo%d{gsave %.2f dup scale}def\n", p_voice - voice_tb, scale); } staff = p_voice->staff; if (staff_tb[staff].empty) continue; if (p_voice->new_name) { p_voice->new_name = 0; p = p_voice->nm; } else p = p_voice->snm; if (p == 0) continue; if (staff_tb[staff].flags[1] & CLOSE_BRACE) { while (!(staff_tb[staff].flags[1] & OPEN_BRACE)) staff--; } else if (staff_tb[staff].flags[0] & CLOSE_BRACE) { while (!(staff_tb[staff].flags[0] & OPEN_BRACE)) staff--; } staff_p = &staff_d[staff]; for (;;) { staff_p->v[staff_p->nl++] = p; if ((p = strstr(p, "\\n")) == 0 || staff_p->nl >= MAXSTAFF) break; p += 2; } n++; } if (n == 0) return; str_font(VOICEFONT); indent = -indent * .5; /* center */ for (staff = nstaff; staff >= 0; staff--) { staff_p = &staff_d[staff]; if (staff_p->nl == 0) continue; y = staff_tb[staff].y + staff_tb[staff].topbar * .5 * staff_tb[staff].clef.staffscale + 9 * (staff_p->nl - 1) - cfmt.font_tb[VOICEFONT].size * .3; n = staff; if (staff_tb[staff].flags[1] & OPEN_BRACE) { while (!(staff_tb[n].flags[1] & CLOSE_BRACE)) n++; } else if (staff_tb[staff].flags[0] & OPEN_BRACE) { while (!(staff_tb[n].flags[0] & CLOSE_BRACE)) n++; } if (n != staff) y -= (staff_tb[staff].y - staff_tb[n].y) * .5; for (n = 0; n < staff_p->nl; n++) { p = staff_p->v[n]; if ((q = strstr(p, "\\n")) != 0) *q = '\0'; PUT2("%.1f %.1f M ", indent, y); put_str(p, A_CENTER); y -= 18.; if (q != 0) *q = '\\'; } } } /* -- work out accidentals to be applied to each note -- */ static void setmap(int sf, /* number of sharps/flats in key sig (-7 to +7) */ unsigned char *map) /* for 7 notes only */ { int j; for (j = 7; --j >= 0; ) map[j] = A_NULL; switch (sf) { case 7: map[6] = A_SH; case 6: map[2] = A_SH; case 5: map[5] = A_SH; case 4: map[1] = A_SH; case 3: map[4] = A_SH; case 2: map[0] = A_SH; case 1: map[3] = A_SH; break; case -7: map[3] = A_FT; case -6: map[0] = A_FT; case -5: map[4] = A_FT; case -4: map[1] = A_FT; case -3: map[5] = A_FT; case -2: map[2] = A_FT; case -1: map[6] = A_FT; break; } } /* -- output a floating value, and x and y according to the current scale -- */ void putf(float v) { PUT1("%.1f ", v); } void putx(float x) { putf(x / cur_scale); } void puty(float y) { putf(cur_trans == 0 ? y / cur_scale : /* scaled voice */ y - cur_trans); /* scaled staff */ } void putxy(float x, float y) { if (cur_trans == 0) PUT2("%.1f %.1f ", x / cur_scale, y / cur_scale); /* scaled voice */ else PUT2("%.1f %.1f ", x / cur_scale, y - cur_trans); /* scaled staff */ } /* -- set the staff or voice scale -- */ void set_scale(int voice) { struct VOICE_S *p_voice; int staff; float scale, trans; staff = -1; if (voice >= 0) { p_voice = &voice_tb[voice]; scale = p_voice->scale; if (scale == 1) { staff = p_voice->staff; scale = staff_tb[staff].clef.staffscale; } /*fixme: KO when scale of staff != 1*/ } else scale = 1; if (staff >= 0 && scale != 1) trans = staff_tb[staff].y; else trans = 0; if (scale == cur_scale && trans == cur_trans) return; if (cur_scale != 1) PUT0("grestore "); cur_scale = scale; cur_trans = trans; if (scale != 1) { if (staff < 0) PUT1("scvo%d ", voice); else PUT1("scst%d ", staff); } } /* -- set the staff scale (only) -- */ static void set_sscale(int staff) { float scale, trans; if (staff >= 0) scale = staff_tb[staff].clef.staffscale; else scale = 1; if (staff >= 0 && scale != 1) trans = staff_tb[staff].y; else trans = 0; if (scale == cur_scale && trans == cur_trans) return; if (cur_scale != 1) PUT0("grestore "); cur_scale = scale; cur_trans = trans; if (scale != 1) PUT1("scst%d ", staff); } /* -- set the tie directions for one voice -- */ static void set_tie_dir(struct SYMBOL *sym) { struct SYMBOL *s; int i, ntie, dir, sec, pit; for (s = sym; s != 0; s = s->next) { if (!(s->sflags & S_TI1)) continue; /* if other voice, set the ties in opposite direction */ if (s->multi != 0) { /* struct SYMBOL *s2; s2 = s->ts_next; if (s2->time == s->time && s2->staff == s->staff) { */ dir = s->multi > 0 ? SL_ABOVE : SL_BELOW; for (i = 0; i <= s->nhd; i++) { if (s->as.u.note.ti1[i] == SL_AUTO) s->as.u.note.ti1[i] = dir; } continue; /* } */ } /* if one note, set the direction according to the stem */ sec = ntie = 0; pit = 128; for (i = 0; i <= s->nhd; i++) { if (s->as.u.note.ti1[i]) { ntie++; if (pit < 128 && s->as.u.note.pits[i] <= pit + 1) sec++; pit = s->as.u.note.pits[i]; } } if (ntie <= 1) { dir = s->stem < 0 ? SL_ABOVE : SL_BELOW; for (i = 0; i <= s->nhd; i++) { if (s->as.u.note.ti1[i]) { if (s->as.u.note.ti1[i] == SL_AUTO) s->as.u.note.ti1[i] = dir; break; } } continue; } if (sec == 0) { if (ntie & 1) { /* in chords with an odd number of notes, the outer noteheads are paired off * center notes are tied according to their position in relation to the * center line */ ntie = ntie / 2 + 1; dir = SL_BELOW; for (i = 0; i <= s->nhd; i++) { if (s->as.u.note.ti1[i]) { if (--ntie == 0) { /* central tie */ if (s->as.u.note.pits[i] >= 22) dir = SL_ABOVE; } if (s->as.u.note.ti1[i] == SL_AUTO) s->as.u.note.ti1[i] = dir; if (ntie == 0) dir = SL_ABOVE; } } continue; } else { /* even number of notes, ties divided in opposite directions */ ntie /= 2; dir = SL_BELOW; for (i = 0; i <= s->nhd; i++) { if (s->as.u.note.ti1[i]) { if (s->as.u.note.ti1[i] == SL_AUTO) s->as.u.note.ti1[i] = dir; if (--ntie == 0) dir = SL_ABOVE; } } continue; } } /*fixme: treat more than one second */ /* if (nsec == 1) { */ /* When a chord contains the interval of a second, tie those two notes in * opposition; then fill in the remaining notes of the chord accordingly */ pit = 128; for (i = 0; i <= s->nhd; i++) { if (s->as.u.note.ti1[i]) { if (pit < 128 && s->as.u.note.pits[i] <= pit + 1) { ntie = i; break; } pit = s->as.u.note.pits[i]; } } dir = SL_BELOW; for (i = 0; i <= s->nhd; i++) { if (s->as.u.note.ti1[i]) { if (ntie == i) dir = SL_ABOVE; if (s->as.u.note.ti1[i] == SL_AUTO) s->as.u.note.ti1[i] = dir; } } /*fixme.. continue; } ..*/ /* if a chord contains more than one pair of seconds, the pair farthest * from the center line receives the ties drawn in opposition */ } } /* -- have room for the ties out of the staves -- */ static void set_tie_room(void) { struct VOICE_S *p_voice; struct SYMBOL *s, *s2; for (p_voice = first_voice; p_voice; p_voice = p_voice->next) { if ((s = p_voice->sym->next) == 0) continue; set_tie_dir(s); for ( ; s != 0; s = s->next) { float dx, y, dy; if (!(s->sflags & S_TI1)) continue; if (s->pits[0] < 20 && s->as.u.note.ti1[0] == SL_BELOW) ; else if (s->pits[s->nhd] > 24 && s->as.u.note.ti1[s->nhd] == SL_ABOVE) ; else continue; s2 = s->next; while (s2 != 0 && s2->type != NOTE) s2 = s2->next; if (s2 != 0) { if (s2->staff != s->staff) continue; dx = s2->x - s->x - 10; } else dx = realwidth - s->x - 10; if (dx < 100) dy = 9; else if (dx < 300) dy = 12; else dy = 16; if (s->pits[s->nhd] > 24) { y = 3 * (s->pits[s->nhd] - 18) + dy; if (s->ymx < y) s->ymx = y; if (s2 != 0 && s2->ymx < y) s2->ymx = y; y_set(s, 1, s->x + 5, dx, y); } if (s->pits[0] < 20) { y = 3 * (s->pits[0] - 18) - dy; if (s->ymn > y) s->ymn = y; if (s2 != 0 && s2->ymn > y) s2->ymn = y; y_set(s, 0, s->x + 5, dx, y); } } } } /* -- draw the tin whistle tablature -- */ void draw_whistle(void) { struct VOICE_S *p_voice; struct SYMBOL *s; int i, j, pitch, w_pitch, w_octave, sf, tied; unsigned char workmap[70]; /* sharps/flats - base: lowest 'C' */ unsigned char basemap[7]; static char pitnam[12 * 2] = "C\0C#D\0EbE\0F\0F#G\0AbA\0BbB\0"; static int w_tb[12] = { 0x222222, 0x122222, 0x022222, 0x012222, 0x002222, 0x000222, 0x000122, 0x000022, 0x000012, 0x000002, 0x000220, 0x000000 }; static int scale[7] = {0, 2, 4, 5, 7, 9, 11}; /* index = natural note */ static int acc_pitch[6] = {0, 1, 0, -1, 2, -2}; /* index = enum accidentals */ sf = 0; for (i = 0; i < nwhistle; i++) { p_voice = &voice_tb[whistle_tb[i].voice]; if (p_voice != first_voice && p_voice->prev == 0) continue; /* voice not displayed */ w_pitch = whistle_tb[i].pitch; PUT1("(%.2s)tw_head\n", &pitnam[(w_pitch % 12) * 2]); tied = 0; for (s = p_voice->sym->next; s != 0; s = s->next) { switch (s->type) { case NOTE: if (tied) { tied = s->as.u.note.ti1[0]; continue; } break; case KEYSIG: sf = s->as.u.key.sf; setmap(sf, basemap); for (j = 0; j < 10; j++) memcpy(&workmap[7 * j], basemap, 7); continue; case BAR: if (s->as.flags & ABC_F_INVIS) continue; for (j = 0; j < 10; j++) memcpy(&workmap[7 * j], basemap, 7); continue; default: continue; } pitch = s->as.u.note.pits[0] + 19; if (s->as.u.note.accs[0] != 0) { workmap[pitch] = s->as.u.note.accs[0] == A_NT ? A_NULL : (s->as.u.note.accs[0] % 0x07); } pitch = scale[pitch % 7] + acc_pitch[workmap[pitch]] + 12 * (pitch / 7); w_octave = 0; pitch -= w_pitch; while (pitch < 0) { pitch += 12; w_octave--; } while (pitch >= 36) { pitch -= 12; w_octave++; } PUT1("%.2f 0 ", s->x); if (w_octave > 0) PUT0("tw_over "); else if (w_octave < 0) PUT0("tw_under "); w_octave = pitch / 12; if (pitch == 12) pitch = 0x222220; /* only special case (?) */ else pitch = w_tb[pitch % 12]; for (j = 1; j < 7; j++) { PUT1("tw_%d ", pitch & 0x0f); pitch >>= 4; } if (w_octave == 0) PUT0("pop pop"); else if (w_octave == 1) PUT0("tw_p"); else PUT0("tw_pp"); PUT0("\n"); tied = s->as.u.note.ti1[0]; } bskip(63.); } }