| 1 | |
| 2 | /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */ |
| 3 | /* All rights reserved */ |
| 4 | |
| 5 | /* functions for printing things off of STAFF structs: notes, stems, |
| 6 | * rests, flags, beams, etc */ |
| 7 | |
| 8 | #include "defines.h" |
| 9 | #include "structs.h" |
| 10 | #include "globals.h" |
| 11 | |
| 12 | |
| 13 | /* This struct is used to build up a mesh that represents cross staff beams. |
| 14 | * This is used to figure out how far from the stem end to offset |
| 15 | * the end of a beam. |
| 16 | * There are a row of these linked horizontally via "next" for each beam. |
| 17 | * The stems are linked vertically via the above_p and below_p pointers. |
| 18 | * To get the stem offset for a given beam, |
| 19 | * the code finds the desired basictime on the appropriate stem, |
| 20 | * and counts how many beams that is from the end of the stem. |
| 21 | */ |
| 22 | struct CSBINFO { |
| 23 | struct CSBINFO *next; /* for next group in same beam */ |
| 24 | struct CSBINFO *above_p; /* beams above this beam */ |
| 25 | struct CSBINFO *below_p; /* beams below this beam */ |
| 26 | struct GRPSYL *gs_p; /* group this info is associated with. |
| 27 | * This is only used on the 8th beam, |
| 28 | * and is just for convenience, |
| 29 | * to save us from having to figure |
| 30 | * it out again later. |
| 31 | */ |
| 32 | int basictime; /* 8, 16, 32, etc represented by beam */ |
| 33 | }; |
| 34 | |
| 35 | /* static functions */ |
| 36 | static void do_syl_joins P((char *syl, double west, double y)); |
| 37 | static void pr_stuff P((struct STUFF *stufflist_p, int staffno, |
| 38 | struct MAINLL *mll_p)); |
| 39 | static int pr_grid P((struct STUFF *stuff_p, int staffnum)); |
| 40 | static void pr_tieslur P((struct STUFF *stuff_p, struct MAINLL *mll_p, |
| 41 | int staffno)); |
| 42 | static int get_ts_style P((struct STUFF *stuff_p, struct MAINLL *mll_p)); |
| 43 | static void pr_rest P((struct GRPSYL *gs_p, struct STAFF *staff_p)); |
| 44 | static double mr_y_loc P((int staffno)); |
| 45 | static void pr_note_dots P((struct NOTE *noteinfo_p, int numdots, |
| 46 | double xdotr, double group_x, double group_y)); |
| 47 | static void pr_parens P((struct NOTE *note_p, struct GRPSYL *gs_p)); |
| 48 | static void pr_stems P((struct GRPSYL *grpsyl_p)); |
| 49 | static double slash_xlen P((struct GRPSYL *grpsyl_p)); |
| 50 | static void pr_flags P((struct GRPSYL *grpsyl_p, double x, double y)); |
| 51 | static void pr_accidental P((struct NOTE *noteinfo_p, struct GRPSYL *grpsyl_p)); |
| 52 | static void pr_leger P((struct NOTE *noteinfo_p, struct GRPSYL *gs_p, |
| 53 | int staffno)); |
| 54 | static int numlegers P((struct NOTE *noteinfo_p)); |
| 55 | static double leger_length P((struct NOTE *noteinfo_p, struct GRPSYL *othergs_p, |
| 56 | int lines, int other_is_prev, int is_intermediate)); |
| 57 | static void pr_tupnums P((struct GRPSYL *gs_p, struct STAFF *staff_p)); |
| 58 | static void pr_beams P((struct GRPSYL *grpsyl_p, int grpvalue, int grpsize)); |
| 59 | static struct CSBINFO *mkcsbmesh P((struct GRPSYL *begin_p, |
| 60 | struct GRPSYL *end_p)); |
| 61 | static int draw_beams P((struct GRPSYL *gs_p, struct GRPSYL *endbeam_p, |
| 62 | int basictime, int grpsize, int grpvalue)); |
| 63 | static double beam_offset P((int nbeams, int gsize, int stemdir)); |
| 64 | static struct GRPSYL *neighboring_note_beam_group P((struct GRPSYL *gs_p, |
| 65 | struct GRPSYL *first_p, int backwards) ); |
| 66 | static int chkgroupings P((int *side_p, struct GRPSYL *thisgs_p)); |
| 67 | static void do_beam P((double x1, double y1, double x2, double y2, |
| 68 | double halfwidth)); |
| 69 | static void pr_cresc P((struct STUFF *stuff_p)); |
| 70 | static void extend P((struct STUFF *stuff_p)); |
| 71 | static int tupdir1voice P((struct GRPSYL *gs_p)); |
| 72 | static int mirror P((char *str, int ch, int font)); |
| 73 | \f |
| 74 | |
| 75 | /* print things off of STAFF struct */ |
| 76 | |
| 77 | void |
| 78 | pr_staff(mll_p) |
| 79 | |
| 80 | struct MAINLL *mll_p; /* which main list struct holds the STAFF struct */ |
| 81 | |
| 82 | { |
| 83 | struct STAFF *staff_p; /* mll_p->u.staff_p */ |
| 84 | struct GRPSYL *grpsyl_p;/* current grpsyl */ |
| 85 | struct MAINLL *barmll_p;/* to find TIMEDSSVs */ |
| 86 | struct TIMEDSSV *tssv_p;/* for mid-measure parameter changes */ |
| 87 | struct TIMEDSSV *t_p; /* walk through the mid-measure changes */ |
| 88 | RATIONAL now; /* how far we are into measure */ |
| 89 | char *savedlyr; /* saved copy of lyric syllable */ |
| 90 | register int n; /* index thru notes in a group */ |
| 91 | struct NOTE *noteinfo_p;/* current note */ |
| 92 | int otherstaff; /* staff number for cross-staff stems */ |
| 93 | int v; /* walk through voices or verses on the staff */ |
| 94 | int size; |
| 95 | |
| 96 | |
| 97 | debug(512, "pr_staff file=%s lineno=%d staff=%d", mll_p->inputfile, |
| 98 | mll_p->inputlineno, mll_p->u.staff_p->staffno); |
| 99 | |
| 100 | staff_p = mll_p->u.staff_p; |
| 101 | |
| 102 | if ( svpath(staff_p->staffno, VISIBLE)->visible == NO) { |
| 103 | /* invisible staffs are easy to print... */ |
| 104 | return; |
| 105 | } |
| 106 | |
| 107 | /* do any syllables */ |
| 108 | for (v = 0; v < staff_p->nsyllists; v++) { |
| 109 | |
| 110 | /* if bottom staff of "between" lyric is invisible, |
| 111 | * the lyric silently disappears from output */ |
| 112 | if (staff_p->sylplace[v] == PL_BETWEEN && |
| 113 | svpath(staff_p->staffno + 1, VISIBLE)->visible |
| 114 | == NO) { |
| 115 | continue; |
| 116 | } |
| 117 | |
| 118 | if (staff_p->syls_p[v] != (struct GRPSYL *) 0 && |
| 119 | staff_p->syls_p[v]->inputlineno > 0) { |
| 120 | /* tell PostScript about user input line reference */ |
| 121 | pr_linenum(staff_p->syls_p[v]->inputfile, |
| 122 | staff_p->syls_p[v]->inputlineno); |
| 123 | } |
| 124 | |
| 125 | /* do all syllables for current verse/place */ |
| 126 | for (grpsyl_p = staff_p->syls_p[v]; |
| 127 | grpsyl_p != (struct GRPSYL *) 0; |
| 128 | grpsyl_p = grpsyl_p->next) { |
| 129 | |
| 130 | if ( grpsyl_p->syl != (char *) 0) { |
| 131 | |
| 132 | /* if <...> before or after syllable that |
| 133 | * were not used for placement, need to |
| 134 | * compensate for that */ |
| 135 | lyr_compensate(grpsyl_p); |
| 136 | |
| 137 | /* Extender printing can alter the lyrics |
| 138 | * string to get rid of the extender so it |
| 139 | * won't print with the syllable. But if we |
| 140 | * are printing pages using -o option we |
| 141 | * may need to have the original |
| 142 | * string preserved, because we may do this |
| 143 | * page again. So make a copy. |
| 144 | */ |
| 145 | if ((savedlyr = malloc(strlen(grpsyl_p->syl) + 1)) |
| 146 | == 0) { |
| 147 | l_no_mem(__FILE__, __LINE__); |
| 148 | } |
| 149 | strcpy(savedlyr, grpsyl_p->syl); |
| 150 | |
| 151 | /* if syllable ends with a dash or underscore, |
| 152 | * they have to be spread between this syllable |
| 153 | * and the next */ |
| 154 | (void) spread_extender(grpsyl_p, mll_p, |
| 155 | grpsyl_p->vno, |
| 156 | staff_p->sylplace[v], YES); |
| 157 | |
| 158 | /* now print the syllable itself */ |
| 159 | pr_string(grpsyl_p->c[AW], grpsyl_p->c[AY], |
| 160 | grpsyl_p->syl, J_LEFT, |
| 161 | grpsyl_p->inputfile, |
| 162 | grpsyl_p->inputlineno); |
| 163 | |
| 164 | /* handle multiple syllables on one chord */ |
| 165 | do_syl_joins(grpsyl_p->syl, |
| 166 | (double) grpsyl_p->c[AW], |
| 167 | (double) grpsyl_p->c[AY]); |
| 168 | /* if string was altered, put original back */ |
| 169 | if (strcmp(grpsyl_p->syl, savedlyr) != 0) { |
| 170 | FREE(grpsyl_p->syl); |
| 171 | grpsyl_p->syl = savedlyr; |
| 172 | } |
| 173 | else { |
| 174 | FREE(savedlyr); |
| 175 | } |
| 176 | } |
| 177 | } |
| 178 | } |
| 179 | |
| 180 | /* Find the BAR that would point to any TIMEDSSVs for this measure. */ |
| 181 | for (barmll_p = mll_p->next; barmll_p->str != S_BAR; barmll_p = barmll_p->next) { |
| 182 | ; |
| 183 | } |
| 184 | t_p = tssv_p = barmll_p->u.bar_p->timedssv_p; |
| 185 | |
| 186 | /* do notes, etc for each voice on the staff */ |
| 187 | for (v = 0; v < MAXVOICES; v++) { |
| 188 | |
| 189 | if (staff_p->groups_p[v] == 0) { |
| 190 | continue; |
| 191 | } |
| 192 | |
| 193 | /* tab staff notes are handled differently */ |
| 194 | if (is_tab_staff(staff_p->staffno) == YES) { |
| 195 | pr_tab_groups(staff_p->groups_p[v], mll_p); |
| 196 | continue; |
| 197 | } |
| 198 | |
| 199 | /* Set up to handle mid-measure changes, if any */ |
| 200 | if (tssv_p != 0) { |
| 201 | setssvstate(mll_p); |
| 202 | } |
| 203 | t_p = tssv_p; |
| 204 | now = Zero; |
| 205 | |
| 206 | /* for each GRPSYL in the list for current voice */ |
| 207 | for ( grpsyl_p = staff_p->groups_p[v]; |
| 208 | grpsyl_p != (struct GRPSYL *) 0; |
| 209 | grpsyl_p = grpsyl_p->next) { |
| 210 | |
| 211 | /* Apply any timed SSVs */ |
| 212 | while (t_p != 0 && LE(t_p->time_off, now) ) { |
| 213 | asgnssv(&t_p->ssv); |
| 214 | t_p = t_p->next; |
| 215 | } |
| 216 | now = radd(now, grpsyl_p->fulltime); |
| 217 | |
| 218 | if (grpsyl_p->clef != NOCLEF) { |
| 219 | float widthclef; |
| 220 | int clefsize; |
| 221 | clefsize = (3 * DFLT_SIZE) / 4; |
| 222 | widthclef = width(FONT_MUSIC, clefsize, |
| 223 | clefchar(grpsyl_p->clef)); |
| 224 | pr_clef(grpsyl_p->staffno, |
| 225 | grpsyl_p->c[AW] - |
| 226 | (widthclef + CLEFPAD) * Staffscale, |
| 227 | YES, clefsize); |
| 228 | } |
| 229 | if (grpsyl_p->grpcont == GC_SPACE) { |
| 230 | /* very easy to print a space -- do nothing! */ |
| 231 | continue; |
| 232 | } |
| 233 | |
| 234 | if (grpsyl_p->grpcont == GC_REST) { |
| 235 | pr_rest(grpsyl_p, staff_p); |
| 236 | continue; |
| 237 | } |
| 238 | |
| 239 | if (is_mrpt(grpsyl_p) == YES) { |
| 240 | pr_mrpt(grpsyl_p, staff_p); |
| 241 | continue; |
| 242 | } |
| 243 | |
| 244 | /* If group has a cross-staff stem, |
| 245 | * figure out which is the other staff */ |
| 246 | if (grpsyl_p->stemto == CS_ABOVE) { |
| 247 | for (otherstaff = grpsyl_p->staffno - 1; |
| 248 | otherstaff >= 1; otherstaff--) { |
| 249 | if (svpath(otherstaff, VISIBLE)->visible |
| 250 | == YES) { |
| 251 | break; |
| 252 | } |
| 253 | } |
| 254 | } |
| 255 | else if (grpsyl_p->stemto == CS_BELOW) { |
| 256 | for (otherstaff = grpsyl_p->staffno + 1; |
| 257 | otherstaff <= Score.staffs; |
| 258 | otherstaff++) { |
| 259 | if (svpath(otherstaff, VISIBLE)->visible |
| 260 | == YES) { |
| 261 | break; |
| 262 | } |
| 263 | } |
| 264 | } |
| 265 | else { |
| 266 | otherstaff = grpsyl_p->staffno; |
| 267 | } |
| 268 | if (otherstaff < 1 || otherstaff > Score.staffs) { |
| 269 | pfatal("failed to find other score for cross-staff stems for leger lines"); |
| 270 | } |
| 271 | |
| 272 | /* do each note in the group */ |
| 273 | for (n = 0; n < grpsyl_p->nnotes; n++) { |
| 274 | |
| 275 | size = (grpsyl_p->notelist[n].notesize == |
| 276 | GS_NORMAL ? DFLT_SIZE : |
| 277 | SMALLSIZE); |
| 278 | |
| 279 | /* we're going to need the NOTE info a lot; |
| 280 | * get its address */ |
| 281 | noteinfo_p = &(grpsyl_p->notelist[n]); |
| 282 | |
| 283 | /* do the note head */ |
| 284 | pr_muschar(noteinfo_p->c[AX], |
| 285 | noteinfo_p->c[AY], |
| 286 | noteinfo_p->headchar, |
| 287 | size, |
| 288 | noteinfo_p->headfont); |
| 289 | |
| 290 | /* do any accidental */ |
| 291 | pr_accidental(noteinfo_p, grpsyl_p); |
| 292 | |
| 293 | /* do any dots */ |
| 294 | pr_note_dots(noteinfo_p, grpsyl_p->dots, |
| 295 | grpsyl_p->xdotr, |
| 296 | (double) grpsyl_p->c[AX], |
| 297 | (double) grpsyl_p->c[AY]); |
| 298 | |
| 299 | /* print parentheses around note if any*/ |
| 300 | if (noteinfo_p->note_has_paren == YES) { |
| 301 | pr_parens(noteinfo_p, grpsyl_p); |
| 302 | } |
| 303 | |
| 304 | /* print small curve for 1/4 bends */ |
| 305 | if (noteinfo_p->smallbend == YES) { |
| 306 | float adjust; |
| 307 | |
| 308 | /* may have to move slightly to avoid |
| 309 | * flag. This is true if group is an |
| 310 | * unbeamed, stem-up group of 8th note |
| 311 | * or shorter duration */ |
| 312 | if (grpsyl_p->basictime >= 8 && |
| 313 | grpsyl_p->stemdir == UP |
| 314 | && grpsyl_p->beamloc |
| 315 | == NOITEM) { |
| 316 | adjust = 2.0 * STEPSIZE; |
| 317 | } |
| 318 | else { |
| 319 | adjust = STEPSIZE; |
| 320 | } |
| 321 | pr_sm_bend( (double) |
| 322 | noteinfo_p->c[AE] + adjust, |
| 323 | (double) |
| 324 | noteinfo_p->c[AY] + 0.5 * STEPSIZE); |
| 325 | } |
| 326 | |
| 327 | /* do any leger lines */ |
| 328 | if (grpsyl_p->stemto == CS_SAME || |
| 329 | (n >= FNNI(grpsyl_p) && |
| 330 | n <= LNNI(grpsyl_p) )) { |
| 331 | pr_leger(noteinfo_p, grpsyl_p, |
| 332 | grpsyl_p->staffno); |
| 333 | } |
| 334 | else { |
| 335 | /* notes are on a different staff */ |
| 336 | pr_leger(noteinfo_p, grpsyl_p, |
| 337 | otherstaff); |
| 338 | } |
| 339 | } |
| 340 | |
| 341 | /* do "with" lists */ |
| 342 | pr_withlist(grpsyl_p); |
| 343 | |
| 344 | /* do stems, flags, slash, and alt */ |
| 345 | pr_stems(grpsyl_p); |
| 346 | |
| 347 | /* print rolls */ |
| 348 | if (gets_roll(grpsyl_p, staff_p, v) == YES) { |
| 349 | print_roll(grpsyl_p); |
| 350 | } |
| 351 | } |
| 352 | |
| 353 | /* assign anything that happened after start of last group */ |
| 354 | while (t_p != 0) { |
| 355 | asgnssv(&t_p->ssv); |
| 356 | t_p = t_p->next; |
| 357 | } |
| 358 | |
| 359 | /* print tuplet numbers if any */ |
| 360 | pr_tupnums(staff_p->groups_p[v], staff_p); |
| 361 | |
| 362 | /* draw beams */ |
| 363 | pr_beams(staff_p->groups_p[v], GV_NORMAL, GS_NORMAL); |
| 364 | pr_beams(staff_p->groups_p[v], GV_ZERO, GS_SMALL); |
| 365 | pr_beams(staff_p->groups_p[v], GV_NORMAL, GS_SMALL); |
| 366 | } |
| 367 | |
| 368 | /* now do any associated STUFFs */ |
| 369 | pr_stuff(staff_p->stuff_p, staff_p->staffno, mll_p); |
| 370 | } |
| 371 | \f |
| 372 | |
| 373 | /* if two syllables are to be joined, draw a little curved line between them */ |
| 374 | |
| 375 | static void |
| 376 | do_syl_joins (syl, west, y) |
| 377 | |
| 378 | char *syl; /* syllable string */ |
| 379 | double west; /* where syllable was printed */ |
| 380 | double y; /* where syllable was printed */ |
| 381 | |
| 382 | { |
| 383 | int font, size; |
| 384 | char *p; /* pointer into syllable string */ |
| 385 | float wid; /* of syllable up to space */ |
| 386 | double x, east; /* of curved line */ |
| 387 | double xinc, yinc; /* increment to move when doing curve */ |
| 388 | double spacewid; /* width of ' ' */ |
| 389 | |
| 390 | |
| 391 | int skipover = NO; |
| 392 | |
| 393 | /* skip past any <...> */ |
| 394 | font = syl[0]; |
| 395 | size = syl[1]; |
| 396 | for (p = syl + 2; *p != '\0'; p++) { |
| 397 | switch ( (unsigned) *p & 0xff) { |
| 398 | case STR_PRE: |
| 399 | case STR_U_PRE: |
| 400 | case STR_PST: |
| 401 | case STR_U_PST: |
| 402 | skipover = YES; |
| 403 | break; |
| 404 | case STR_PRE_END: |
| 405 | case STR_PST_END: |
| 406 | skipover = NO; |
| 407 | break; |
| 408 | case STR_MUS_CHAR: |
| 409 | p += 2; |
| 410 | break; |
| 411 | case STR_FONT: |
| 412 | font = *(p+1); |
| 413 | /*FALLTHRU*/ |
| 414 | case STR_SIZE: |
| 415 | case STR_BACKSPACE: |
| 416 | case STR_PAGENUM: |
| 417 | case STR_NUMPAGES: |
| 418 | p++; |
| 419 | break; |
| 420 | case ' ': |
| 421 | if (skipover == NO && font <= EXT_FONT_OFFSET) { |
| 422 | /* temporarily shorten string to just before |
| 423 | * the space to get width of string up to |
| 424 | * that point */ |
| 425 | *p = '\0'; |
| 426 | wid = strwidth(syl); |
| 427 | *p = ' '; |
| 428 | |
| 429 | /* Calculate dimensions |
| 430 | * and location of curve to be drawn. */ |
| 431 | spacewid = width(font, size, ' '); |
| 432 | xinc = spacewid * 0.3; |
| 433 | yinc = spacewid * 0.15; |
| 434 | x = west + wid - STDPAD; |
| 435 | east = x + spacewid; |
| 436 | |
| 437 | do_linetype(L_NORMAL); |
| 438 | do_moveto(x, y); |
| 439 | do_curveto(x + xinc, y - yinc, |
| 440 | east - xinc, y - yinc, east, y); |
| 441 | } |
| 442 | break; |
| 443 | default: |
| 444 | break; |
| 445 | } |
| 446 | } |
| 447 | } |
| 448 | \f |
| 449 | |
| 450 | /* print things in STUFF list */ |
| 451 | |
| 452 | static void |
| 453 | pr_stuff (stufflist_p, staffno, mll_p) |
| 454 | |
| 455 | struct STUFF *stufflist_p; /* which list of STUFF */ |
| 456 | int staffno; /* which staff the stuff is for */ |
| 457 | struct MAINLL *mll_p; |
| 458 | |
| 459 | { |
| 460 | char lch; /* last character in string */ |
| 461 | |
| 462 | |
| 463 | /* do each item in stuff list */ |
| 464 | for ( ; stufflist_p != (struct STUFF *) 0; |
| 465 | stufflist_p = stufflist_p->next) { |
| 466 | |
| 467 | set_staffscale( (stufflist_p->all == YES) ? 0 : staffno); |
| 468 | |
| 469 | switch (stufflist_p->stuff_type) { |
| 470 | |
| 471 | case ST_MUSSYM: |
| 472 | case ST_OCTAVE: |
| 473 | case ST_ROM: |
| 474 | case ST_BOLD: |
| 475 | case ST_ITAL: |
| 476 | case ST_BOLDITAL: |
| 477 | /* do 'til' clause if any */ |
| 478 | extend(stufflist_p); |
| 479 | |
| 480 | /* if special case of ending in ~ or _, don't print the |
| 481 | * ~ or _ itself */ |
| 482 | if ((lch = last_char(stufflist_p->string)) == '~' || |
| 483 | lch == '_') { |
| 484 | stufflist_p->string[strlen(stufflist_p->string) |
| 485 | -1] = '\0'; |
| 486 | } |
| 487 | |
| 488 | /* print the string at specified place */ |
| 489 | if (stufflist_p->string != (char *) 0) { |
| 490 | |
| 491 | |
| 492 | /* print grid if appropriate, |
| 493 | * otherwise just the string. */ |
| 494 | if (stufflist_p->modifier != TM_CHORD || |
| 495 | svpath(staffno, GRIDSWHEREUSED) |
| 496 | ->gridswhereused == NO || |
| 497 | pr_grid(stufflist_p, |
| 498 | (stufflist_p->all == YES ? |
| 499 | 0 : staffno)) |
| 500 | == NO) { |
| 501 | pr_string (stufflist_p->c[AW], |
| 502 | stufflist_p->c[AY], |
| 503 | stufflist_p->string, J_LEFT, |
| 504 | stufflist_p->inputfile, |
| 505 | stufflist_p->inputlineno); |
| 506 | } |
| 507 | } |
| 508 | |
| 509 | break; |
| 510 | |
| 511 | case ST_CRESC: |
| 512 | case ST_DECRESC: |
| 513 | pr_cresc(stufflist_p); |
| 514 | break; |
| 515 | |
| 516 | case ST_PEDAL: |
| 517 | pr_ped_char(stufflist_p, staffno); |
| 518 | break; |
| 519 | |
| 520 | case ST_PHRASE: |
| 521 | pr_phrase(stufflist_p->crvlist_p, stufflist_p->modifier, |
| 522 | (stufflist_p->modifier == L_NORMAL ? YES : NO), |
| 523 | staffno); |
| 524 | break; |
| 525 | |
| 526 | case ST_TIESLUR: |
| 527 | pr_tieslur(stufflist_p, mll_p, staffno); |
| 528 | break; |
| 529 | |
| 530 | case ST_BEND: |
| 531 | pr_bend(stufflist_p->crvlist_p); |
| 532 | break; |
| 533 | |
| 534 | case ST_TABSLUR: |
| 535 | pr_tabslur(stufflist_p->crvlist_p, |
| 536 | get_ts_style(stufflist_p, mll_p)); |
| 537 | break; |
| 538 | |
| 539 | case ST_MIDI: |
| 540 | break; |
| 541 | |
| 542 | default: |
| 543 | pfatal("unknown stuff type"); |
| 544 | break; |
| 545 | } |
| 546 | } |
| 547 | } |
| 548 | \f |
| 549 | |
| 550 | /* Print a guitar grid. Return YES if grid was found and printed, else NO. */ |
| 551 | |
| 552 | static int |
| 553 | pr_grid(stuff_p, staffnum) |
| 554 | |
| 555 | struct STUFF *stuff_p; |
| 556 | int staffnum; |
| 557 | |
| 558 | { |
| 559 | struct GRID *grid_p; |
| 560 | double space; |
| 561 | float north, south; |
| 562 | |
| 563 | |
| 564 | if ((grid_p = findgrid(stuff_p->string)) == 0) { |
| 565 | /* placement phase should have printed a warning already */ |
| 566 | return(NO); |
| 567 | } |
| 568 | |
| 569 | /* print the grid name */ |
| 570 | pr_string(stuff_p->c[AX] - strwidth(grid_p->name) / 2.0, |
| 571 | stuff_p->c[AY], grid_p->name, J_LEFT, |
| 572 | stuff_p->inputfile, stuff_p->inputlineno); |
| 573 | |
| 574 | space = gridspace(staffnum); |
| 575 | gridsize(grid_p, staffnum, &north, &south, (float *) 0, (float *) 0); |
| 576 | |
| 577 | do_grid(stuff_p->c[AX] - space * (grid_p->numstr - 1) / 2.0, |
| 578 | stuff_p->c[AS] - south, |
| 579 | space, grid_p, staffnum); |
| 580 | return(YES); |
| 581 | } |
| 582 | \f |
| 583 | |
| 584 | /* print ties and slurs */ |
| 585 | |
| 586 | static void |
| 587 | pr_tieslur(stuff_p, mll_p, staffno) |
| 588 | |
| 589 | struct STUFF *stuff_p; |
| 590 | struct MAINLL *mll_p; |
| 591 | int staffno; |
| 592 | |
| 593 | { |
| 594 | int ts_style; /* tie/slur style (L_DOTTED or L_DASHED) */ |
| 595 | |
| 596 | |
| 597 | ts_style = get_ts_style(stuff_p, mll_p); |
| 598 | |
| 599 | /* If tabslur, do that */ |
| 600 | if ( stuff_p->curveno >= 0 && stuff_p->begnote_p->nslurto > 0 |
| 601 | && IS_NOWHERE(stuff_p-> begnote_p->slurtolist |
| 602 | [stuff_p->curveno].octave)) { |
| 603 | pr_tabslur(stuff_p->crvlist_p, ts_style); |
| 604 | return; |
| 605 | } |
| 606 | |
| 607 | /* print a regular tie/slur curve */ |
| 608 | pr_phrase(stuff_p->crvlist_p, ts_style, |
| 609 | (ts_style == L_NORMAL ? YES : NO), staffno ); |
| 610 | } |
| 611 | \f |
| 612 | |
| 613 | /* given a TIESLUR STUFF, return the line type to use for it */ |
| 614 | |
| 615 | static int |
| 616 | get_ts_style(stuff_p, mll_p) |
| 617 | |
| 618 | struct STUFF *stuff_p; |
| 619 | struct MAINLL *mll_p; |
| 620 | |
| 621 | { |
| 622 | struct GRPSYL *prevgrp_p; /* for carryins */ |
| 623 | int n; /* notelist index */ |
| 624 | |
| 625 | |
| 626 | if (stuff_p->carryin == YES) { |
| 627 | prevgrp_p = prevgrpsyl(stuff_p->beggrp_p, &mll_p); |
| 628 | if (stuff_p->curveno >= 0) { |
| 629 | /* a carried-in slur. Need to find a note |
| 630 | * in previous group that is slurred to this one, |
| 631 | * and use its slurstyle. There is some chance |
| 632 | * that there could be more than one slur to this |
| 633 | * note from the same curveno |
| 634 | * and each slur could have a different style, |
| 635 | * in which case we no longer have enough information |
| 636 | * to know which to use, so we just use the first |
| 637 | * we find. */ |
| 638 | for (n = 0; n < prevgrp_p->nnotes; n++) { |
| 639 | |
| 640 | if (prevgrp_p->notelist[n].nslurto |
| 641 | <= stuff_p->curveno) { |
| 642 | /* couldn't have come from this grp */ |
| 643 | continue; |
| 644 | } |
| 645 | |
| 646 | if (prevgrp_p->notelist[n].slurtolist |
| 647 | [stuff_p->curveno].letter |
| 648 | == stuff_p->begnote_p->letter |
| 649 | && prevgrp_p->notelist[n] |
| 650 | .slurtolist[stuff_p->curveno].octave |
| 651 | == stuff_p->begnote_p->octave) { |
| 652 | |
| 653 | return (prevgrp_p->notelist[n]. |
| 654 | slurtolist[stuff_p->curveno] |
| 655 | .slurstyle); |
| 656 | } |
| 657 | } |
| 658 | } |
| 659 | else { |
| 660 | /* a carried-in tie. Need to find matching note |
| 661 | * in previous group, and use its tiestyle. */ |
| 662 | for (n = 0; n < prevgrp_p->nnotes; n++) { |
| 663 | if (prevgrp_p->notelist[n].letter == |
| 664 | stuff_p->begnote_p->letter && |
| 665 | prevgrp_p->notelist[n].octave |
| 666 | == stuff_p->begnote_p->octave) { |
| 667 | return(prevgrp_p->notelist[n].tiestyle); |
| 668 | } |
| 669 | } |
| 670 | } |
| 671 | } |
| 672 | |
| 673 | else { |
| 674 | if (stuff_p->curveno >= 0) { |
| 675 | /* a non-carried-in slur, use slurstyle */ |
| 676 | return(stuff_p->begnote_p->slurtolist |
| 677 | [stuff_p->curveno].slurstyle); |
| 678 | } |
| 679 | else { |
| 680 | /* a non-carried-in tie, use tiestyle */ |
| 681 | return(stuff_p->begnote_p->tiestyle); |
| 682 | } |
| 683 | } |
| 684 | |
| 685 | /* if none of those cases applied, use normal */ |
| 686 | return(L_NORMAL); |
| 687 | } |
| 688 | \f |
| 689 | |
| 690 | /* print a rest symbol */ |
| 691 | |
| 692 | static void |
| 693 | pr_rest(gs_p, staff_p) |
| 694 | |
| 695 | struct GRPSYL *gs_p; /* information about the rest to be printed */ |
| 696 | struct STAFF *staff_p; |
| 697 | |
| 698 | { |
| 699 | int muschar; /* which type of rest character to print */ |
| 700 | int d; /* number of dots */ |
| 701 | float adjust; /* to space dots properly */ |
| 702 | float y; /* vertical location of rest */ |
| 703 | int size; |
| 704 | |
| 705 | |
| 706 | if (gs_p->basictime < -1) { |
| 707 | /* multirest are a special case */ |
| 708 | pr_multirest(gs_p, staff_p); |
| 709 | return; |
| 710 | } |
| 711 | |
| 712 | /* draw the rest */ |
| 713 | muschar = restchar(gs_p->basictime); |
| 714 | /* Half and whole rests outside the staff need to use the version |
| 715 | * that includes a ledger line. So check for that case. |
| 716 | * We used to use characters with ledgers all the time, |
| 717 | * but Ghostscript then sometimes seemed to misplace them |
| 718 | * by one pixel at certain magnifications, which looked bad. */ |
| 719 | if (muschar == C_LL1REST || muschar == C_LL2REST) { |
| 720 | double halfst; |
| 721 | if (svpath(staff_p->staffno, STAFFLINES)->stafflines > 1) { |
| 722 | halfst = halfstaffhi(staff_p->staffno); |
| 723 | } |
| 724 | else { |
| 725 | halfst = 0.0; |
| 726 | } |
| 727 | /* The adjustments to halfst are chosen so that both half |
| 728 | * and whole rests will properly get leger lines when they |
| 729 | * are outside the staff, but not when inside. |
| 730 | */ |
| 731 | if ( (gs_p->c[AN] > (staff_p->c[AY] + halfst + 1.7 * Stepsize)) || |
| 732 | (gs_p->c[AN] < (staff_p->c[AY] - halfst - Stdpad)) ) { |
| 733 | muschar = (muschar == C_LL1REST ? C_1REST : C_2REST); |
| 734 | } |
| 735 | } |
| 736 | size = (gs_p->grpsize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE); |
| 737 | if (gs_p->is_meas == YES) { |
| 738 | /* measure rest is special case, have to move to middle */ |
| 739 | pr_muschar( (gs_p->c[AW] + gs_p->c[AE]) / 2.0, |
| 740 | gs_p->c[AY], muschar, size, FONT_MUSIC); |
| 741 | } |
| 742 | else { |
| 743 | pr_muschar(gs_p->c[AX], gs_p->c[AY], muschar, size, FONT_MUSIC); |
| 744 | } |
| 745 | |
| 746 | /* get ready to print any dots */ |
| 747 | adjust = width(FONT_MUSIC, adj_size(size, Staffscale, (char *) 0, |
| 748 | -1), C_DOT) / 2.0; |
| 749 | y = _Cur[AY] + Stepsize; |
| 750 | |
| 751 | /* print any dots after the rest */ |
| 752 | for (d = 0; d < gs_p->dots; d++) { |
| 753 | /* each time we print a dot, the current location will get |
| 754 | * moved to just beyond that one */ |
| 755 | pr_muschar(_Cur[AX] + adjust + (2.0 * Stdpad), y, C_DOT, size, |
| 756 | FONT_MUSIC); |
| 757 | } |
| 758 | } |
| 759 | \f |
| 760 | |
| 761 | /* print a measure repeat */ |
| 762 | |
| 763 | void |
| 764 | pr_mrpt(gs_p, staff_p) |
| 765 | |
| 766 | struct GRPSYL *gs_p; |
| 767 | struct STAFF *staff_p; |
| 768 | |
| 769 | { |
| 770 | double x; /* horizontal position of number string */ |
| 771 | double y, y_offset; /* vertical location */ |
| 772 | double height, width; /* of meas num string */ |
| 773 | char *numstr; /* ASCII version of numbers of measures */ |
| 774 | |
| 775 | |
| 776 | /* measure repeat has to be moved to the middle of the measure */ |
| 777 | pr_muschar( (gs_p->c[AW] + gs_p->c[AE]) / 2.0, |
| 778 | mr_y_loc(gs_p->staffno), C_MEASRPT, DFLT_SIZE, FONT_MUSIC); |
| 779 | |
| 780 | if (svpath(gs_p->staffno, NUMBERMRPT)->numbermrpt == YES) { |
| 781 | /* print number above the staff */ |
| 782 | y = Staffs_y[gs_p->staffno]; |
| 783 | numstr = mrnum(staff_p, &x, &y_offset, &height, &width); |
| 784 | pr_string(x, y + y_offset, numstr, J_LEFT, (char *) 0, -1); |
| 785 | } |
| 786 | } |
| 787 | \f |
| 788 | |
| 789 | /* given a staff number, return the y at which to print the measure repeat |
| 790 | * or multirest symbols. If the number of staff lines is odd, this is the |
| 791 | * middle line, otherwise the line just above the middle. */ |
| 792 | |
| 793 | static double |
| 794 | mr_y_loc(staffno) |
| 795 | |
| 796 | int staffno; |
| 797 | |
| 798 | { |
| 799 | double y; |
| 800 | |
| 801 | y = Staffs_y[staffno]; |
| 802 | /* if even number of staff lines, move up a stepsize */ |
| 803 | if ( (svpath(staffno, STAFFLINES)->stafflines & 1) == 0) { |
| 804 | y += Stepsize * (is_tab_staff(staffno) ? TABRATIO : 1.0); |
| 805 | } |
| 806 | return(y); |
| 807 | } |
| 808 | \f |
| 809 | |
| 810 | /* print the dots for dotted notes */ |
| 811 | |
| 812 | static void |
| 813 | pr_note_dots(noteinfo_p, numdots, xdotr, group_x, group_y) |
| 814 | |
| 815 | struct NOTE *noteinfo_p; /* which note to dot */ |
| 816 | int numdots; /* how many dots to print */ |
| 817 | double xdotr; /* relative x distance from note to print the dots */ |
| 818 | double group_x; |
| 819 | double group_y; /* coord of group, dots are relative to this */ |
| 820 | |
| 821 | { |
| 822 | float adjust; /* to place dots with proper spacing */ |
| 823 | |
| 824 | |
| 825 | /* if note isn't dotted, nothing to do */ |
| 826 | if (numdots <= 0) { |
| 827 | return; |
| 828 | } |
| 829 | |
| 830 | adjust = width(FONT_MUSIC, adj_size(DFLT_SIZE, Staffscale, |
| 831 | (char *) 0, -1), C_DOT) / 2.0; |
| 832 | |
| 833 | /* go to where first dot belongs */ |
| 834 | set_cur(group_x + xdotr - adjust, group_y + noteinfo_p->ydotr); |
| 835 | |
| 836 | /* print as many dots as necessary */ |
| 837 | for ( ; numdots > 0; numdots--) { |
| 838 | pr_muschar(_Cur[AX] + adjust + (2.0 * Stdpad), |
| 839 | _Cur[AY], C_DOT, DFLT_SIZE, FONT_MUSIC); |
| 840 | } |
| 841 | } |
| 842 | \f |
| 843 | |
| 844 | /* print parentheses around a note. Should only be called if note_has_paren |
| 845 | * is YES */ |
| 846 | |
| 847 | static void |
| 848 | pr_parens(note_p, gs_p) |
| 849 | |
| 850 | struct NOTE *note_p; |
| 851 | struct GRPSYL * gs_p; |
| 852 | |
| 853 | { |
| 854 | char paren_string[4]; |
| 855 | double y; |
| 856 | |
| 857 | |
| 858 | /* make a parentheses string of proper size in internal string format */ |
| 859 | (void) sprintf(paren_string, "%c%c(", FONT_TR, |
| 860 | adj_size((note_p->notesize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE), |
| 861 | Staffscale, (char *) 0, -1)); |
| 862 | |
| 863 | /* center the parentheses vertically on the Y on the note */ |
| 864 | y = note_p->c[AY] - (strascent(paren_string) |
| 865 | - (strheight(paren_string) / 2.0)); |
| 866 | |
| 867 | /* print the left parenthesis */ |
| 868 | pr_string(gs_p->c[AX] + note_p->wlparen, y, |
| 869 | paren_string, J_LEFT, (char *) 0, -1); |
| 870 | |
| 871 | /* now do the right parenthesis */ |
| 872 | paren_string[2] = ')'; |
| 873 | pr_string(gs_p->c[AX] + note_p->erparen - strwidth(paren_string), y, |
| 874 | paren_string, J_LEFT, (char *) 0, -1); |
| 875 | } |
| 876 | \f |
| 877 | |
| 878 | /* print "with" lists */ |
| 879 | |
| 880 | void |
| 881 | pr_withlist(gs_p) |
| 882 | |
| 883 | struct GRPSYL *gs_p; /* GRPSYL that might have with lists */ |
| 884 | |
| 885 | { |
| 886 | float y; /* where to start from */ |
| 887 | float x; |
| 888 | float y_offset, sign; |
| 889 | float x_offset; /* to center first character of item on note */ |
| 890 | float yposition; /* y coordinate at which to print */ |
| 891 | float item_height; /* height of with list item */ |
| 892 | int first_char; /* first char of string to print */ |
| 893 | char *str_p; /* pointer into string to print */ |
| 894 | int font, size; |
| 895 | int index; /* offset into with list */ |
| 896 | int alternate; /* upside version of music symbol */ |
| 897 | float ystaff; /* y of middle of staff */ |
| 898 | float yline; /* y value of staff line */ |
| 899 | float top, bot; /* top and bottom of item to be printed */ |
| 900 | float pad; /* vertical padding around short items */ |
| 901 | int sl; /* staff line index */ |
| 902 | float adjusted_stepsize; /* STEPSIZE or STEPSIZE * TABRATIO |
| 903 | * depending on whether tab staff or not */ |
| 904 | int stafflines; /* how many lines in current staff */ |
| 905 | float minwithheight; /* MINWITHHEIGHT * Staffscale */ |
| 906 | |
| 907 | |
| 908 | if (gs_p->nnotes == 0) { |
| 909 | return; |
| 910 | } |
| 911 | |
| 912 | /* with goes forward from note opposite stem */ |
| 913 | if (gs_p->normwith == YES) { |
| 914 | if (gs_p->stemdir == UP) { |
| 915 | y = gs_p->notelist [gs_p->nnotes - 1] .c[AS]; |
| 916 | x = gs_p->notelist [gs_p->nnotes - 1] .c[AX]; |
| 917 | sign = -1.0; |
| 918 | } |
| 919 | else { |
| 920 | y = gs_p->notelist[0].c[AN]; |
| 921 | x = gs_p->notelist[0].c[AX]; |
| 922 | sign = 1.0; |
| 923 | } |
| 924 | } |
| 925 | else { |
| 926 | /* with goes on opposite side than normal */ |
| 927 | y = find_y_stem(gs_p); |
| 928 | if (gs_p->stemdir == DOWN) { |
| 929 | sign = -1.0; |
| 930 | /* whole notes /double wholes may have |
| 931 | * zero length stems so have to adjust */ |
| 932 | if (gs_p->stemlen <= 0.0) { |
| 933 | y = gs_p->notelist[gs_p->nnotes - 1] .c[AS]; |
| 934 | } |
| 935 | /* beamed notes stems effective stick out a little |
| 936 | * farther, so compensate for that */ |
| 937 | if (gs_p->beamloc != NOITEM) { |
| 938 | y -= POINT; |
| 939 | } |
| 940 | } |
| 941 | else { |
| 942 | sign = 1.0; |
| 943 | if (gs_p->stemlen <= 0.0) { |
| 944 | y = gs_p->notelist[0].c[AN]; |
| 945 | } |
| 946 | if (gs_p->beamloc != NOITEM) { |
| 947 | y += POINT; |
| 948 | } |
| 949 | } |
| 950 | x = gs_p->c[AX]; |
| 951 | } |
| 952 | |
| 953 | /* If a dot, wedge, and uwedge is the only item in the list, |
| 954 | * and it's on the stem side of a group with a stem, it is supposed |
| 955 | * to be aligned with the stem. */ |
| 956 | if (gs_p->normwith == NO && gs_p->nwith == 1 && |
| 957 | gs_p->basictime > 1 && gs_p->stemlen > 0.0 && |
| 958 | is_music_symbol(gs_p->withlist[0]) == YES) { |
| 959 | font = gs_p->withlist[0][0]; |
| 960 | size = gs_p->withlist[0][1]; |
| 961 | str_p = gs_p->withlist[0] + 2; |
| 962 | first_char = next_str_char(&str_p, &font, &size); |
| 963 | if (first_char == C_DOT || first_char == C_WEDGE || |
| 964 | first_char == C_UWEDGE) { |
| 965 | x = find_x_stem(gs_p); |
| 966 | } |
| 967 | } |
| 968 | |
| 969 | y_offset = 0.0; |
| 970 | minwithheight = MINWITHHEIGHT * Staffscale; |
| 971 | |
| 972 | /* do each item in with list */ |
| 973 | for (index = 0; index < gs_p->nwith; index++) { |
| 974 | |
| 975 | /* should center first character on x */ |
| 976 | font = gs_p->withlist[index][0]; |
| 977 | size = gs_p->withlist[index][1]; |
| 978 | str_p = gs_p->withlist[index] + 2; |
| 979 | first_char = next_str_char(&str_p, &font, &size); |
| 980 | |
| 981 | /* get upside down version if necessary */ |
| 982 | if (sign == -1.0 && IS_MUSIC_FONT(font)) { |
| 983 | if ((alternate = mirror(gs_p->withlist[index], |
| 984 | first_char, font)) != first_char) { |
| 985 | *(str_p - 1) = (char) alternate; |
| 986 | } |
| 987 | } |
| 988 | |
| 989 | x_offset = left_width( &(gs_p->withlist[index][0]) ); |
| 990 | |
| 991 | /* get height of item to print */ |
| 992 | item_height = strheight(gs_p->withlist[index]); |
| 993 | |
| 994 | /* if string is so short vertically |
| 995 | * it could get swallowed up in a staff |
| 996 | * line, adjust to fall in a space. Placement phase will have |
| 997 | * allowed MINWITHHEIGHT, so put in middle of that area unless |
| 998 | * that would fall on a line, in which case move somewhat */ |
| 999 | if (item_height < minwithheight) { |
| 1000 | /* need to adjust this one. Start out by putting in |
| 1001 | * middle vertically of reserved area */ |
| 1002 | yposition = y + y_offset + sign * minwithheight / 2.0; |
| 1003 | |
| 1004 | /* no reason to adjust further for 1-line staffs */ |
| 1005 | if ((stafflines = svpath(gs_p->staffno, |
| 1006 | STAFFLINES)->stafflines) > 1) { |
| 1007 | |
| 1008 | /* get stepsize distance based on whether it |
| 1009 | * is a tab staff or not */ |
| 1010 | adjusted_stepsize = (is_tab_staff(gs_p->staffno) |
| 1011 | == YES ? Stepsize * TABRATIO : Stepsize); |
| 1012 | |
| 1013 | /* find y of middle of staff */ |
| 1014 | ystaff = gs_p->notelist[0].c[AY] |
| 1015 | - (gs_p->notelist[0].stepsup |
| 1016 | * adjusted_stepsize); |
| 1017 | |
| 1018 | /* take the extra vertical space alloted to this |
| 1019 | * with list item, and add 1/4 of it on top |
| 1020 | * and bottom as padding. If no staff line is |
| 1021 | * in between the boundaries of the item after |
| 1022 | * adding that padding, it's good enough where |
| 1023 | * it is. Otherwise, if a staff line falls above |
| 1024 | * the middle of the item, move the item |
| 1025 | * down into space. Otherwise move it |
| 1026 | * up into space. |
| 1027 | */ |
| 1028 | pad = (minwithheight - item_height) / 4.0; |
| 1029 | top = yposition + (item_height / 2.0) + pad; |
| 1030 | bot = yposition - (item_height / 2.0) - pad; |
| 1031 | |
| 1032 | /* check each staff line for collisions, from |
| 1033 | * bottom to top */ |
| 1034 | for (sl = -(stafflines - 1); |
| 1035 | sl <= (stafflines - 1); |
| 1036 | sl += 2) { |
| 1037 | |
| 1038 | /* find y of current staff line */ |
| 1039 | yline = ystaff + (sl * adjusted_stepsize); |
| 1040 | |
| 1041 | /* check if current staff line goes |
| 1042 | * through the item |
| 1043 | * as currently placed */ |
| 1044 | if (yline < top && yline > bot) { |
| 1045 | /* collides--need to move */ |
| 1046 | |
| 1047 | if ((top - yline) > |
| 1048 | (yline - bot)) { |
| 1049 | /* move up to area |
| 1050 | * above the line */ |
| 1051 | yposition += 2.0 * pad; |
| 1052 | /* if overdid the move, |
| 1053 | * move back a bit */ |
| 1054 | if (yposition - yline - |
| 1055 | (item_height / 2.0) |
| 1056 | > 0.7 * adjusted_stepsize) { |
| 1057 | yposition -= |
| 1058 | 0.4 * adjusted_stepsize; |
| 1059 | } |
| 1060 | } |
| 1061 | else { |
| 1062 | /* move down to area |
| 1063 | * below the line */ |
| 1064 | yposition -= 2.0 * pad; |
| 1065 | if (yline - yposition - |
| 1066 | (item_height / 2.0) |
| 1067 | > 0.7 * adjusted_stepsize) { |
| 1068 | yposition += |
| 1069 | 0.4 * adjusted_stepsize; |
| 1070 | } |
| 1071 | } |
| 1072 | |
| 1073 | /* only 1 staff line can |
| 1074 | * possibly interfere, |
| 1075 | * and we've found that one, so |
| 1076 | * can jump out of loop */ |
| 1077 | break; |
| 1078 | } |
| 1079 | } |
| 1080 | } |
| 1081 | |
| 1082 | /* adjust y_offset to include the area taken by item */ |
| 1083 | y_offset += minwithheight * sign; |
| 1084 | |
| 1085 | /* up to now, we've been using the center of the item, |
| 1086 | * so now adjust to baseline */ |
| 1087 | if (sign > 0.0) { |
| 1088 | yposition += (item_height / 2.0) |
| 1089 | - strascent(gs_p->withlist[index]); |
| 1090 | } |
| 1091 | else { |
| 1092 | yposition -= (item_height / 2.0) |
| 1093 | - strdescent(gs_p->withlist[index]); |
| 1094 | } |
| 1095 | } |
| 1096 | else { |
| 1097 | /* not too short, handle normally */ |
| 1098 | y_offset += item_height * sign; |
| 1099 | yposition = y + y_offset; |
| 1100 | |
| 1101 | /* adjust to get to baseline of string from top or |
| 1102 | * bottom that we've used up to this point */ |
| 1103 | if (sign > 0.0) { |
| 1104 | yposition -= strascent(gs_p->withlist[index]); |
| 1105 | } |
| 1106 | else { |
| 1107 | yposition += strdescent(gs_p->withlist[index]); |
| 1108 | } |
| 1109 | } |
| 1110 | |
| 1111 | pr_string(x - x_offset, yposition, gs_p->withlist[index], |
| 1112 | J_CENTER, gs_p->inputfile, gs_p->inputlineno); |
| 1113 | } |
| 1114 | } |
| 1115 | \f |
| 1116 | |
| 1117 | /* print note stems and flags. Also print any slashes and alt lines */ |
| 1118 | |
| 1119 | static void |
| 1120 | pr_stems(grpsyl_p) |
| 1121 | |
| 1122 | struct GRPSYL *grpsyl_p; /* which group's stem to print */ |
| 1123 | |
| 1124 | { |
| 1125 | float x, y1, y2; |
| 1126 | float sign; /* 1 or -1 direction for moving to draw slashes */ |
| 1127 | float y_offset, offset, spacing; /* for where to draw slashes */ |
| 1128 | float y_tilt; /* how much to move in y direction to get |
| 1129 | * proper tilt on slashes */ |
| 1130 | float halfwidth; /* half width of slash or alt line */ |
| 1131 | struct GRPSYL *first_p, *last_p; /* beginning and ending group |
| 1132 | * of beam group */ |
| 1133 | int grpsize; /* grpsize field of grpsyl_p */ |
| 1134 | int grpvalue; /* grpvalue field of grpsyl_p */ |
| 1135 | int slash; /* to count number of slashes drawn */ |
| 1136 | struct NOTE *note_p; |
| 1137 | |
| 1138 | |
| 1139 | /* if no stem, nothing to do */ |
| 1140 | if ( grpsyl_p->stemlen <= 0 && grpsyl_p->slash_alt == 0) { |
| 1141 | return; |
| 1142 | } |
| 1143 | |
| 1144 | /* figure out x coordinate of stem */ |
| 1145 | x = find_x_stem(grpsyl_p); |
| 1146 | |
| 1147 | /* if stem is up, start at bottom note, if down, at top */ |
| 1148 | if (grpsyl_p->stemdir == UP) { |
| 1149 | note_p = &(grpsyl_p->notelist[ grpsyl_p->nnotes - 1]); |
| 1150 | y1 = note_p->c[AY]; |
| 1151 | y2 = find_y_stem(grpsyl_p); |
| 1152 | sign = -1; |
| 1153 | } |
| 1154 | else { |
| 1155 | note_p = &(grpsyl_p->notelist [0]); |
| 1156 | y1 = note_p->c[AY]; |
| 1157 | y2 = find_y_stem(grpsyl_p); |
| 1158 | sign = 1; |
| 1159 | } |
| 1160 | |
| 1161 | if (note_p->headchar != 0) { |
| 1162 | y1 += stem_yoff(note_p->headchar, note_p->headfont, |
| 1163 | grpsyl_p->stemdir) |
| 1164 | * (note_p->notesize == GS_NORMAL |
| 1165 | ? Stepsize : Stepsize * SM_FACTOR); |
| 1166 | } |
| 1167 | |
| 1168 | if (grpsyl_p->basictime >= 2) { |
| 1169 | /* print the stem */ |
| 1170 | do_linetype(L_NORMAL); |
| 1171 | |
| 1172 | draw_line(x, y1, x, y2); |
| 1173 | |
| 1174 | /* attach any flags as appropriate */ |
| 1175 | pr_flags(grpsyl_p, (double) x, (double) y2); |
| 1176 | } |
| 1177 | |
| 1178 | /* print any slashes */ |
| 1179 | if (grpsyl_p->slash_alt > 0) { |
| 1180 | |
| 1181 | /* adjust for flags or beams. */ |
| 1182 | if (grpsyl_p->basictime >= 8) { |
| 1183 | offset = (numbeams(grpsyl_p->basictime) - 1) * |
| 1184 | (grpsyl_p->grpsize == GS_NORMAL ? 5.0 : 4.0) |
| 1185 | * Stdpad; |
| 1186 | if (grpsyl_p->beamloc == NOITEM) { |
| 1187 | if (grpsyl_p->grpsize == GS_NORMAL) { |
| 1188 | offset += 8.0 * Stdpad; |
| 1189 | } |
| 1190 | else if (grpsyl_p->basictime != 16) { |
| 1191 | /* 16th small notes don't have any extra |
| 1192 | * stem to account for extra flag */ |
| 1193 | offset += 3.0 * Stdpad; |
| 1194 | } |
| 1195 | } |
| 1196 | } |
| 1197 | else { |
| 1198 | offset = 0.0; |
| 1199 | } |
| 1200 | |
| 1201 | if ( grpsyl_p->beamloc == NOITEM) { |
| 1202 | /* unbeamed things get hard-coded tilt value */ |
| 1203 | if (grpsyl_p->grpvalue == GV_ZERO) { |
| 1204 | y_tilt = (grpsyl_p->stemdir == UP ? 3.5 : -3.5) |
| 1205 | * Stdpad; |
| 1206 | } |
| 1207 | else { |
| 1208 | y_tilt = 2.2 * Stdpad; |
| 1209 | } |
| 1210 | } |
| 1211 | |
| 1212 | else { |
| 1213 | /* beamed. Need to slant slashes the same as beam */ |
| 1214 | |
| 1215 | grpsize = grpsyl_p->grpsize; |
| 1216 | grpvalue = grpsyl_p->grpvalue; |
| 1217 | |
| 1218 | /* find beginning and ending stems */ |
| 1219 | for (first_p = grpsyl_p; (first_p->beamloc != STARTITEM) |
| 1220 | || (first_p->grpsize != grpsize) |
| 1221 | || (first_p->grpvalue != grpvalue); |
| 1222 | first_p = first_p->prev) { |
| 1223 | ; |
| 1224 | } |
| 1225 | |
| 1226 | for (last_p = grpsyl_p; (last_p->beamloc != ENDITEM) |
| 1227 | || (last_p->grpsize != grpsize) |
| 1228 | || (last_p->grpvalue != grpvalue); |
| 1229 | last_p = last_p->next) { |
| 1230 | ; |
| 1231 | } |
| 1232 | |
| 1233 | /* calculate slope from them. We find the ratio of |
| 1234 | * y to x of the beam and apply that proportion to |
| 1235 | * the known x length of the slash to get the y height |
| 1236 | * of the slash, then divide by 2 to get the y distance |
| 1237 | * on either side of the stem. */ |
| 1238 | y_tilt = (((find_y_stem(last_p) - find_y_stem(first_p)) |
| 1239 | * (2.0 * slash_xlen(grpsyl_p))) |
| 1240 | / (find_x_stem(last_p) |
| 1241 | - find_x_stem(first_p))) / 2.0; |
| 1242 | y1 = find_y_stem(first_p); |
| 1243 | } |
| 1244 | |
| 1245 | /* draw the slashes */ |
| 1246 | pr_slashes(grpsyl_p, (double) x, (double) y2, (double) sign, |
| 1247 | (double) offset, (double) y_tilt); |
| 1248 | } |
| 1249 | |
| 1250 | /* print alt group lines if any */ |
| 1251 | if (grpsyl_p->slash_alt < 0) { |
| 1252 | struct GRPSYL *grpsyl2_p; |
| 1253 | float grp2x, grp2y; /* stem of second group */ |
| 1254 | float grp1y_offset, grp2y_offset; |
| 1255 | |
| 1256 | |
| 1257 | if (grpsyl_p->next == (struct GRPSYL *) 0) { |
| 1258 | pfatal("missing second group in alt pair"); |
| 1259 | } |
| 1260 | |
| 1261 | /* figure out how wide to draw the lines and how far apart |
| 1262 | * to make them */ |
| 1263 | if (grpsyl_p->grpsize == GS_NORMAL) { |
| 1264 | halfwidth = W_WIDE * Staffscale / PPI / 2.0; |
| 1265 | spacing = 5.0 * Stdpad; |
| 1266 | } |
| 1267 | else { |
| 1268 | halfwidth = W_MEDIUM * Staffscale / PPI / 2.0; |
| 1269 | spacing = 3.0 * Stdpad; |
| 1270 | } |
| 1271 | |
| 1272 | /* find the stem coordinates of the second group */ |
| 1273 | grpsyl2_p = grpsyl_p->next; |
| 1274 | grp2x = find_x_stem(grpsyl2_p); |
| 1275 | grp2y = find_y_stem(grpsyl2_p); |
| 1276 | |
| 1277 | /* on notes shorter than half note, the lines don't go all the |
| 1278 | * way to the stems */ |
| 1279 | if ( grpsyl_p->basictime >= 4) { |
| 1280 | /* figure out where the y of the end of the line is |
| 1281 | * by multiplying the x value by the tangent of the |
| 1282 | * angle of the line that would go all the way |
| 1283 | * between the stems */ |
| 1284 | grp2y_offset = (grp2x - x - (6.0 * Stdpad)) |
| 1285 | * ((grp2y - y2) / (grp2x - x)); |
| 1286 | grp1y_offset = (6.0 * Stdpad) |
| 1287 | * ((grp2y - y2) / (grp2x - x)); |
| 1288 | /* if 8th notes or shorter, get out of way of beams */ |
| 1289 | offset = numbeams(grpsyl_p->basictime) * spacing; |
| 1290 | x += (6.0 * Stdpad); |
| 1291 | grp2x -= (6.0 * Stdpad); |
| 1292 | } |
| 1293 | else { |
| 1294 | grp1y_offset = 0.0; |
| 1295 | grp2y_offset = grp2y - y2; |
| 1296 | offset = 0.0; |
| 1297 | } |
| 1298 | |
| 1299 | /* draw the alt lines */ |
| 1300 | for (slash = -(grpsyl_p->slash_alt) - 1; slash >= 0; slash--) { |
| 1301 | y_offset = sign * slash * spacing + (sign * offset); |
| 1302 | do_newpath(); |
| 1303 | do_moveto(x, y2 + y_offset + grp1y_offset - halfwidth); |
| 1304 | do_line(x, y2 + y_offset + grp1y_offset + halfwidth); |
| 1305 | do_line(grp2x, y2 + y_offset + grp2y_offset |
| 1306 | + halfwidth); |
| 1307 | do_line(grp2x, y2 + y_offset + grp2y_offset |
| 1308 | - halfwidth); |
| 1309 | do_closepath(); |
| 1310 | do_fill(); |
| 1311 | } |
| 1312 | |
| 1313 | /* earlier phase wanted both groups in alt pair to have |
| 1314 | * slash_alt set, but now we've printed this one, so clear |
| 1315 | * the one on the following group, so it won't try to |
| 1316 | * print another alt group */ |
| 1317 | grpsyl2_p->slash_alt = 0; |
| 1318 | } |
| 1319 | } |
| 1320 | \f |
| 1321 | |
| 1322 | void |
| 1323 | pr_slashes(grpsyl_p, x, y, sign, offset, y_tilt) |
| 1324 | |
| 1325 | struct GRPSYL *grpsyl_p; |
| 1326 | double x; |
| 1327 | double y; |
| 1328 | double sign; |
| 1329 | double offset; |
| 1330 | double y_tilt; |
| 1331 | |
| 1332 | { |
| 1333 | int slash; |
| 1334 | double xlen; |
| 1335 | float y_offset; |
| 1336 | float spacing; |
| 1337 | float halfwidth; |
| 1338 | |
| 1339 | |
| 1340 | /* get length based on note head size */ |
| 1341 | xlen = slash_xlen(grpsyl_p); |
| 1342 | |
| 1343 | /* figure out how wide to make the slashes and how far apart |
| 1344 | * to space them */ |
| 1345 | if (grpsyl_p->grpsize == GS_NORMAL) { |
| 1346 | halfwidth = W_WIDE * Staffscale / PPI / 2.0; |
| 1347 | spacing = 5 * Stdpad; |
| 1348 | } |
| 1349 | else { |
| 1350 | halfwidth = W_MEDIUM * Staffscale / PPI / 2.0; |
| 1351 | spacing = 4 * Stdpad; |
| 1352 | } |
| 1353 | |
| 1354 | for (slash = grpsyl_p->slash_alt; slash > 0; slash--) { |
| 1355 | y_offset = y + sign * (offset + (spacing * slash)); |
| 1356 | |
| 1357 | /* draw filled parallelogram */ |
| 1358 | do_newpath(); |
| 1359 | do_moveto(x - xlen, y_offset - y_tilt - halfwidth); |
| 1360 | do_line(x - xlen, y_offset - y_tilt + halfwidth); |
| 1361 | do_line(x + xlen, y_offset + y_tilt + halfwidth); |
| 1362 | do_line(x + xlen, y_offset + y_tilt - halfwidth); |
| 1363 | do_closepath(); |
| 1364 | do_fill(); |
| 1365 | } |
| 1366 | } |
| 1367 | |
| 1368 | static double |
| 1369 | slash_xlen(grpsyl_p) |
| 1370 | |
| 1371 | struct GRPSYL *grpsyl_p; |
| 1372 | |
| 1373 | { |
| 1374 | return (SLASHHORZ * Stepsize * |
| 1375 | (grpsyl_p->grpsize == GS_NORMAL ? 1.0 : SM_FACTOR)); |
| 1376 | } |
| 1377 | \f |
| 1378 | |
| 1379 | /* print flags on 8th and shorter notes */ |
| 1380 | |
| 1381 | static void |
| 1382 | pr_flags(grpsyl_p, x, y) |
| 1383 | |
| 1384 | struct GRPSYL *grpsyl_p; /* group for which to draw flags */ |
| 1385 | double x; |
| 1386 | double y; /* coord of end of stem */ |
| 1387 | |
| 1388 | { |
| 1389 | int muschar; /* what kind of flag to print */ |
| 1390 | float y_offset; /* from end of stem */ |
| 1391 | int f; /* how many flags */ |
| 1392 | int size; |
| 1393 | |
| 1394 | |
| 1395 | /* only 8th and shorter notes might have flags */ |
| 1396 | if (grpsyl_p->basictime < 8) { |
| 1397 | return; |
| 1398 | } |
| 1399 | |
| 1400 | /* if not a note, no flag */ |
| 1401 | if (grpsyl_p->grpcont != GC_NOTES) { |
| 1402 | return; |
| 1403 | } |
| 1404 | |
| 1405 | /* if beamed, no flag */ |
| 1406 | if (grpsyl_p->beamloc != NOITEM) { |
| 1407 | return; |
| 1408 | } |
| 1409 | |
| 1410 | /* figure out if up/down and whether small/reg */ |
| 1411 | muschar = (grpsyl_p->stemdir == UP ? C_DNFLAG : C_UPFLAG); |
| 1412 | size = (grpsyl_p->grpsize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE); |
| 1413 | |
| 1414 | /* do for each flag. f == 1 less than the number of flags, and is |
| 1415 | * how much to multiply the y_offset by for each flag */ |
| 1416 | for ( f = numbeams(grpsyl_p->basictime) - 1; f >= 0; f--) { |
| 1417 | |
| 1418 | switch (muschar) { |
| 1419 | |
| 1420 | case C_UPFLAG: |
| 1421 | y_offset = f * (grpsyl_p->grpsize == GS_NORMAL ? |
| 1422 | FLAGSEP : SMFLAGSEP); |
| 1423 | break; |
| 1424 | case C_DNFLAG: |
| 1425 | y_offset = -f * (grpsyl_p->grpsize == GS_NORMAL ? |
| 1426 | FLAGSEP : SMFLAGSEP); |
| 1427 | break; |
| 1428 | default: |
| 1429 | pfatal("bad flag type"); |
| 1430 | /*NOTREACHED*/ |
| 1431 | return; /* to shut up compiler warning about unused */ |
| 1432 | } |
| 1433 | |
| 1434 | y_offset *= Staffscale; |
| 1435 | |
| 1436 | /* now that we know where to place the flag, print it */ |
| 1437 | pr_muschar(x + width(FONT_MUSIC, |
| 1438 | adj_size(size, Staffscale, (char *) 0, -1), |
| 1439 | muschar) / 2.0, |
| 1440 | y + y_offset, muschar, size, FONT_MUSIC); |
| 1441 | } |
| 1442 | } |
| 1443 | \f |
| 1444 | |
| 1445 | /* print any accidental */ |
| 1446 | |
| 1447 | static void |
| 1448 | pr_accidental(noteinfo_p, grpsyl_p) |
| 1449 | |
| 1450 | struct NOTE *noteinfo_p; /* info about the note being printed */ |
| 1451 | struct GRPSYL *grpsyl_p; /* info about the group conatining the note */ |
| 1452 | |
| 1453 | { |
| 1454 | int muschar; /* which accidental symbol to draw */ |
| 1455 | int size; |
| 1456 | int a_size; /* size adjusted for Staffscale */ |
| 1457 | |
| 1458 | |
| 1459 | /* figure out which accidental symbol to use */ |
| 1460 | muschar = acc2char(noteinfo_p->accidental); |
| 1461 | |
| 1462 | /* if there is an accidental, print it at specified place */ |
| 1463 | if (muschar != '\0') { |
| 1464 | size = (noteinfo_p->notesize == GS_NORMAL |
| 1465 | ? DFLT_SIZE : SMALLSIZE); |
| 1466 | a_size = adj_size(size, Staffscale, (char *) 0, -1); |
| 1467 | if (noteinfo_p->acc_has_paren == NO) { |
| 1468 | pr_muschar(grpsyl_p->c[AX] + noteinfo_p->waccr |
| 1469 | + width(FONT_MUSIC, a_size, muschar) / 2.0, |
| 1470 | noteinfo_p->c[AY], muschar, size, FONT_MUSIC); |
| 1471 | } |
| 1472 | else { |
| 1473 | /* have to print parentheses in addition to the |
| 1474 | * symbol for the accidental */ |
| 1475 | char paren_string[4]; /* "(" or ")" in internal format */ |
| 1476 | double offset; /* y adjustment of ( ) */ |
| 1477 | |
| 1478 | /* create string for "(" */ |
| 1479 | (void) sprintf(paren_string, "%c%c%c", |
| 1480 | FONT_TR, a_size, '('); |
| 1481 | |
| 1482 | /* to center things vertically on the note, need to |
| 1483 | * adjust parentheses downward by difference between |
| 1484 | * the ascent and half the height of the parenthesis */ |
| 1485 | offset = strascent(paren_string) - |
| 1486 | (strheight(paren_string) / 2.0); |
| 1487 | |
| 1488 | /* print the '(', the accidental, and the ')' */ |
| 1489 | pr_string(grpsyl_p->c[AX] + noteinfo_p->waccr, |
| 1490 | noteinfo_p->c[AY] - offset, |
| 1491 | paren_string, J_LEFT, |
| 1492 | grpsyl_p->inputfile, |
| 1493 | grpsyl_p->inputlineno); |
| 1494 | |
| 1495 | pr_muschar(_Cur[AX] + |
| 1496 | width(FONT_MUSIC, a_size, muschar) / 2.0, |
| 1497 | noteinfo_p->c[AY], muschar, size, |
| 1498 | FONT_MUSIC); |
| 1499 | |
| 1500 | (void) sprintf(paren_string, "%c%c%c", |
| 1501 | FONT_TR, a_size, ')'); |
| 1502 | pr_string(_Cur[AX], noteinfo_p->c[AY] - offset, |
| 1503 | paren_string, J_LEFT, |
| 1504 | grpsyl_p->inputfile, |
| 1505 | grpsyl_p->inputlineno); |
| 1506 | } |
| 1507 | } |
| 1508 | } |
| 1509 | \f |
| 1510 | |
| 1511 | /* print appropriate number of leger lines */ |
| 1512 | |
| 1513 | static void |
| 1514 | pr_leger(noteinfo_p, gs_p, staffno) |
| 1515 | |
| 1516 | struct NOTE *noteinfo_p; /* info about current note */ |
| 1517 | struct GRPSYL *gs_p; /* which group contains the note */ |
| 1518 | int staffno; /* which staff to draw relative to */ |
| 1519 | |
| 1520 | { |
| 1521 | register int lines2draw; /* how many leger lines are needed */ |
| 1522 | float sign; /* 1 for above or -1 for below staff */ |
| 1523 | float y; /* vertical position */ |
| 1524 | float left_leger, right_leger; /* how far legers stick out from note */ |
| 1525 | int is_intermediate; /* YES if inner, NO if outermost */ |
| 1526 | int on_other_side; /* YES if on "wrong" side of stem */ |
| 1527 | |
| 1528 | |
| 1529 | if ((lines2draw = numlegers(noteinfo_p)) < 1) { |
| 1530 | /* No legers needed for this note */ |
| 1531 | return; |
| 1532 | } |
| 1533 | |
| 1534 | /* Is note above or below the middle of the staff? */ |
| 1535 | sign = noteinfo_p->stepsup > 0.0 ? 1.0 : -1.0; |
| 1536 | |
| 1537 | /* For notes on the "wrong" side of the stem, we will only need |
| 1538 | * to draw the outermost leger. */ |
| 1539 | if ( (gs_p->stemdir == DOWN && noteinfo_p->c[AE] < gs_p->c[AX]) || |
| 1540 | (gs_p->stemdir == UP && noteinfo_p->c[AW] > gs_p->c[AX])) { |
| 1541 | on_other_side = YES; |
| 1542 | } |
| 1543 | else { |
| 1544 | on_other_side = NO; |
| 1545 | } |
| 1546 | |
| 1547 | /* Draw the legers */ |
| 1548 | do_linetype(L_NORMAL); |
| 1549 | is_intermediate = NO; |
| 1550 | for ( ; lines2draw > 0; lines2draw--) { |
| 1551 | |
| 1552 | /* Find the y location for the leger line. |
| 1553 | * They are 2 Stepsizes apart, |
| 1554 | * beginning at the edge of the staff */ |
| 1555 | y = Staffs_y[staffno] |
| 1556 | + (sign * (2 + lines2draw) * (2 * Stepsize)); |
| 1557 | |
| 1558 | /* If things are packed really close together, leger lines |
| 1559 | * could bleed into leger lines of the neighboring chord. |
| 1560 | * We need to see if there are any potentially |
| 1561 | * troublesome leger lines on either side, and shorten |
| 1562 | * this leger if necessary to avoid them. |
| 1563 | */ |
| 1564 | left_leger = leger_length(noteinfo_p, gs_p->prev, lines2draw, |
| 1565 | YES, is_intermediate); |
| 1566 | right_leger = leger_length(noteinfo_p, gs_p->next, lines2draw, |
| 1567 | NO, is_intermediate); |
| 1568 | |
| 1569 | draw_line( noteinfo_p->c[AW] - left_leger, y, |
| 1570 | noteinfo_p->c[AE] + right_leger, y); |
| 1571 | is_intermediate = YES; |
| 1572 | |
| 1573 | /* For notes on the "wrong" side of the stem, we only need |
| 1574 | * to draw the outermost leger */ |
| 1575 | if (on_other_side == YES) { |
| 1576 | break; |
| 1577 | } |
| 1578 | } |
| 1579 | } |
| 1580 | \f |
| 1581 | |
| 1582 | /* How many legers to draw is absolute value of stepsup divided |
| 1583 | * by 2 minus the 2 lines that are already in the staff. |
| 1584 | * Note that we only do legers on normal 5-line staffs. */ |
| 1585 | |
| 1586 | static int |
| 1587 | numlegers(noteinfo_p) |
| 1588 | |
| 1589 | struct NOTE *noteinfo_p; |
| 1590 | |
| 1591 | { |
| 1592 | return (abs(noteinfo_p->stepsup) / 2) - 2; |
| 1593 | } |
| 1594 | \f |
| 1595 | |
| 1596 | /* If things are packed really close together, leger lines |
| 1597 | * could bleed into leger lines of the neighboring chord. |
| 1598 | * This function will detect that and shorten them if necessary. |
| 1599 | * To be completely correct, it should check all the voices on the |
| 1600 | * staff, but that would be quite a bit more work, and chances of colliding |
| 1601 | * with another voice's notes is not very high, so we just check |
| 1602 | * the voice of the note in question. |
| 1603 | */ |
| 1604 | |
| 1605 | static double |
| 1606 | leger_length(noteinfo_p, othergs_p, lines, other_is_prev, is_intermediate) |
| 1607 | |
| 1608 | struct NOTE *noteinfo_p; /* we are finding leger length for this note */ |
| 1609 | struct GRPSYL *othergs_p; /* check this group for a too close note */ |
| 1610 | int lines; /* how many leger lines to draw */ |
| 1611 | int other_is_prev; /* YES if othergs_p is ->prev, NO if ->next */ |
| 1612 | int is_intermediate; /* YES if interior, NO is outermost leger */ |
| 1613 | |
| 1614 | { |
| 1615 | int n; /* note index */ |
| 1616 | double distance; /* between 2 notes */ |
| 1617 | double length = 2.2 * Stdpad; /* length of leger. Init to default */ |
| 1618 | double adjust; /* inners can be shortened extra */ |
| 1619 | |
| 1620 | |
| 1621 | if (othergs_p == 0) { |
| 1622 | /* No group to collide with */ |
| 1623 | return(length); |
| 1624 | } |
| 1625 | if (othergs_p->grpcont != GC_NOTES) { |
| 1626 | /* Can't have leger lines */ |
| 1627 | return(length); |
| 1628 | } |
| 1629 | |
| 1630 | /* Legers that are not through or right next to the note |
| 1631 | * can be shortened a bit more to make their gap show up better. |
| 1632 | */ |
| 1633 | adjust = (is_intermediate ? 0.5 * Stdpad : 0.0); |
| 1634 | |
| 1635 | /* See if othergs_p has any notes that are too close */ |
| 1636 | for (n = 0; n < othergs_p->nnotes; n++) { |
| 1637 | if (numlegers( &(othergs_p->notelist[n]) ) < lines) { |
| 1638 | /* Neighboring note has fewer legers; not relevant */ |
| 1639 | continue; |
| 1640 | } |
| 1641 | if (noteinfo_p->stepsup > 0 && |
| 1642 | othergs_p->notelist[n].stepsup < 0) { |
| 1643 | /* Neighboring note's legers are below, ours above. |
| 1644 | * The remaining neighboring notes are irrelevant. */ |
| 1645 | break; |
| 1646 | } |
| 1647 | if (noteinfo_p->stepsup < 0 && |
| 1648 | othergs_p->notelist[n].stepsup > 0) { |
| 1649 | /* Neighboring note's legers are above, ours below. |
| 1650 | * Haven't gotten to any potentially relevant |
| 1651 | * notes yet. */ |
| 1652 | continue; |
| 1653 | } |
| 1654 | |
| 1655 | /* We have a pair of notes whose leger lines might collide. |
| 1656 | * See how far apart they are. */ |
| 1657 | if (other_is_prev == YES) { |
| 1658 | distance = noteinfo_p->c[AW] - othergs_p->notelist[n].c[AE]; |
| 1659 | } |
| 1660 | else { |
| 1661 | distance = othergs_p->notelist[n].c[AW] - noteinfo_p->c[AE]; |
| 1662 | } |
| 1663 | |
| 1664 | /* Ideally, we try to make leger lines 2.2 Stdpads on each side, |
| 1665 | * but if that leaves less than 2.0 Stdpads between them, |
| 1666 | * we shorten them until they get down to 0.7 Stdpads. |
| 1667 | * After that we let them join. That should only happen |
| 1668 | * if things are really tightly packed. |
| 1669 | * The 6.4 is from two legers of 2.2 each with 2.0 between. |
| 1670 | */ |
| 1671 | if (distance < 6.4 * Stdpad) { |
| 1672 | /* Too close. Will have to shorten */ |
| 1673 | length = (distance - (2.0 * Stdpad)) / 2.0 - adjust; |
| 1674 | if (length < 0.7 * Stdpad - adjust) { |
| 1675 | /* No shorter than minimum */ |
| 1676 | length = 0.7 * Stdpad - adjust; |
| 1677 | } |
| 1678 | } |
| 1679 | } |
| 1680 | return (length); |
| 1681 | } |
| 1682 | \f |
| 1683 | |
| 1684 | /* given the first group of a tuplet, return, via pointers, the x coords of |
| 1685 | * the left and right boundaries of the tuplet number and its height. |
| 1686 | * Return pointer to static string containing the tuplet number itself in |
| 1687 | * internal string format */ |
| 1688 | |
| 1689 | char * |
| 1690 | tupnumsize(gs_p, west_p, east_p, height_p, staff_p) |
| 1691 | |
| 1692 | struct GRPSYL *gs_p; |
| 1693 | float *west_p; /* west coord returned here */ |
| 1694 | float *east_p; /* east coord returned here */ |
| 1695 | float *height_p; /* string height returned here */ |
| 1696 | struct STAFF *staff_p; /* staff pointing at gs_p */ |
| 1697 | |
| 1698 | { |
| 1699 | char *numstr; /* tuplet number as internal string */ |
| 1700 | struct GRPSYL *last_gs_p; /* last group in tuplet */ |
| 1701 | float num_x; /* x coord of number */ |
| 1702 | float halfnumwidth; /* half the width of numstr */ |
| 1703 | int tupside; |
| 1704 | int all_cue; |
| 1705 | |
| 1706 | |
| 1707 | /* assume all cue till proven otherwise */ |
| 1708 | all_cue = YES; |
| 1709 | |
| 1710 | /* find x of middle of tuplet number */ |
| 1711 | if (gs_p->tuploc == LONEITEM) { |
| 1712 | if (gs_p->grpsize != GS_SMALL) { |
| 1713 | all_cue = NO; |
| 1714 | } |
| 1715 | num_x = gs_p->c[AX]; |
| 1716 | } |
| 1717 | else { |
| 1718 | for (last_gs_p = gs_p->next; last_gs_p != (struct GRPSYL *) 0; |
| 1719 | last_gs_p = last_gs_p->next) { |
| 1720 | if (gs_p->grpsize != GS_SMALL) { |
| 1721 | all_cue = NO; |
| 1722 | } |
| 1723 | if (last_gs_p->tuploc == ENDITEM) { |
| 1724 | break; |
| 1725 | } |
| 1726 | } |
| 1727 | if (last_gs_p == (struct GRPSYL *) 0) { |
| 1728 | pfatal("missing end tuplet in tupnumsize"); |
| 1729 | } |
| 1730 | |
| 1731 | /* Usually, the x location of tuplet number is average of |
| 1732 | * beginning and end group x coords. But if there is a beam |
| 1733 | * and the number is being printed on the beam side, |
| 1734 | * and there is no bracket being printed, |
| 1735 | * it generally looks better to center between the stems. |
| 1736 | */ |
| 1737 | tupside = tupdir(gs_p, staff_p); |
| 1738 | if (gs_p->beamloc == STARTITEM && last_gs_p->beamloc == ENDITEM |
| 1739 | && ((tupside == PL_ABOVE && gs_p->stemdir == UP) |
| 1740 | || (tupside == PL_BELOW && gs_p->stemdir == DOWN)) |
| 1741 | && tupgetsbrack(gs_p) == NO) { |
| 1742 | num_x = (find_x_stem(last_gs_p) + find_x_stem(gs_p)) / 2.0; |
| 1743 | } |
| 1744 | else { |
| 1745 | num_x = (last_gs_p->c[AX] + gs_p->c[AX]) / 2.0; |
| 1746 | } |
| 1747 | } |
| 1748 | |
| 1749 | /* prepare the string to print */ |
| 1750 | numstr = num2str(gs_p->tupcont); |
| 1751 | /* force to 11-point newcentury bold-italics, unless all cue, |
| 1752 | * then smaller */ |
| 1753 | numstr[0] = FONT_NX; |
| 1754 | numstr[1] = (char) adj_size((all_cue == YES ? 9 : 11), Staffscale, |
| 1755 | (char *) 0, -1); |
| 1756 | halfnumwidth = strwidth(numstr) / 2.0; |
| 1757 | |
| 1758 | /* return the values */ |
| 1759 | *west_p = num_x - halfnumwidth - Stdpad; |
| 1760 | *east_p = num_x + halfnumwidth + Stdpad; |
| 1761 | *height_p = strheight(numstr); |
| 1762 | return(numstr); |
| 1763 | } |
| 1764 | \f |
| 1765 | |
| 1766 | /* go through measure. If there are any tuplets, print a number by them, |
| 1767 | * along with bracket if appropriate. */ |
| 1768 | |
| 1769 | static void |
| 1770 | pr_tupnums(gs_p, staff_p) |
| 1771 | |
| 1772 | struct GRPSYL *gs_p; /* start from here to walk through list of groups */ |
| 1773 | struct STAFF *staff_p; /* staff pointing to gs_p */ |
| 1774 | |
| 1775 | { |
| 1776 | struct GRPSYL *first_gs_p = 0; /* where to begin tuplet label. |
| 1777 | * Initialization is just to shut up bogus |
| 1778 | * compiler warning. */ |
| 1779 | struct GRPSYL *g_p; /* to check for all spaces */ |
| 1780 | float x1, x2; /* where tuplet bracket begins & ends */ |
| 1781 | float num_y; /* y of tuplet number */ |
| 1782 | float y1, y2; /* y coord of ends of bracket */ |
| 1783 | char *numstr; /* ASCII version of tuplet number */ |
| 1784 | float numeast, numwest; /* boundaries of tuplet number */ |
| 1785 | float height; /* of tuplet number */ |
| 1786 | float y_adjust; /* adjustment for space taken by number */ |
| 1787 | float x_adjust; /* from group x to where bracket goes */ |
| 1788 | int num_notes = 0; /* how many notes in tuplet */ |
| 1789 | int need_brack = NO; /* set to YES if the beaming of the notes |
| 1790 | * doesn't match the tuplet boundaries */ |
| 1791 | float brackdir; /* how far in y direction to draw bracket ends |
| 1792 | * (positive or negative depending on the |
| 1793 | * direction that the bracket points) */ |
| 1794 | int size; |
| 1795 | |
| 1796 | |
| 1797 | /* go through all the groups */ |
| 1798 | for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) { |
| 1799 | |
| 1800 | switch (gs_p->tuploc) { |
| 1801 | |
| 1802 | case NOITEM: |
| 1803 | break; |
| 1804 | |
| 1805 | case STARTITEM: |
| 1806 | /* remember beginning for later use */ |
| 1807 | first_gs_p = gs_p; |
| 1808 | num_notes = 1; |
| 1809 | break; |
| 1810 | |
| 1811 | case INITEM: |
| 1812 | num_notes++; |
| 1813 | break; |
| 1814 | |
| 1815 | case LONEITEM: |
| 1816 | first_gs_p = gs_p; |
| 1817 | /*FALLTHRU*/ |
| 1818 | |
| 1819 | case ENDITEM: |
| 1820 | num_notes++; |
| 1821 | |
| 1822 | /* if not to be printed, nothing to do except reinit */ |
| 1823 | if (gs_p->printtup == PT_NEITHER) { |
| 1824 | num_notes = 0; |
| 1825 | break; |
| 1826 | } |
| 1827 | |
| 1828 | /* we don't do tuplet numbers on cross-staff beams-- |
| 1829 | * it's virtually impossible to know where to put them |
| 1830 | */ |
| 1831 | if (gs_p->beamto != CS_SAME) { |
| 1832 | num_notes = 0; |
| 1833 | break; |
| 1834 | } |
| 1835 | |
| 1836 | /* If the tuplet is all spaces, |
| 1837 | * there is nothing to draw a bracket over, |
| 1838 | * and trying to do so causes problems, |
| 1839 | * so don't try. */ |
| 1840 | for (g_p = first_gs_p; g_p->tuploc != NOITEM; |
| 1841 | g_p = g_p->next) { |
| 1842 | if (g_p->grpcont != GC_SPACE) { |
| 1843 | /* good--it has something |
| 1844 | * other than spaces */ |
| 1845 | break; |
| 1846 | } |
| 1847 | |
| 1848 | if (g_p->tuploc == ENDITEM |
| 1849 | || g_p->tuploc == LONEITEM) { |
| 1850 | /* reached end of all-space tuplet */ |
| 1851 | break; |
| 1852 | } |
| 1853 | } |
| 1854 | if (g_p->grpcont == GC_SPACE) { |
| 1855 | /* must have been all spaces */ |
| 1856 | num_notes = 0; |
| 1857 | break; |
| 1858 | } |
| 1859 | |
| 1860 | /* if tuplet doesn't match beaming, need bracket */ |
| 1861 | need_brack = tupgetsbrack(first_gs_p); |
| 1862 | |
| 1863 | if (num_notes == 0) { |
| 1864 | pfatal("no notes in tuplet"); |
| 1865 | } |
| 1866 | |
| 1867 | numstr = tupnumsize(first_gs_p, &numwest, &numeast, |
| 1868 | &height, staff_p); |
| 1869 | |
| 1870 | if (tupdir(first_gs_p, staff_p) == PL_ABOVE) { |
| 1871 | y_adjust = strascent(numstr); |
| 1872 | y1 = first_gs_p->c[AN] - y_adjust; |
| 1873 | y2 = gs_p->c[AN] - y_adjust; |
| 1874 | brackdir = -3.0 * Stdpad; |
| 1875 | } |
| 1876 | else { |
| 1877 | /* print below */ |
| 1878 | y1 = first_gs_p->c[AS]; |
| 1879 | y2 = gs_p->c[AS]; |
| 1880 | brackdir = 3.0 * Stdpad; |
| 1881 | } |
| 1882 | |
| 1883 | /* print tuplet number at correct place */ |
| 1884 | y1 += first_gs_p->tupextend; |
| 1885 | y2 += gs_p->tupextend; |
| 1886 | num_y = (y1 + y2) / 2.0; |
| 1887 | pr_string(numwest + Stdpad, num_y, numstr, J_LEFT, |
| 1888 | gs_p->inputfile, gs_p->inputlineno); |
| 1889 | |
| 1890 | /* add tuplet bracket if necessary */ |
| 1891 | if (need_brack == YES) { |
| 1892 | do_linetype(L_NORMAL); |
| 1893 | |
| 1894 | /* adjust to reach edge of note head */ |
| 1895 | size = (first_gs_p->grpsize == GS_NORMAL ? |
| 1896 | DFLT_SIZE : SMALLSIZE) |
| 1897 | * Staffscale; |
| 1898 | if (first_gs_p->grpcont == GC_NOTES) { |
| 1899 | x_adjust = widest_head(first_gs_p) |
| 1900 | * Staffscale / 2.0; |
| 1901 | } |
| 1902 | else if (first_gs_p->grpcont == GC_REST) { |
| 1903 | x_adjust = width(FONT_MUSIC, size, |
| 1904 | |
| 1905 | restchar(first_gs_p->basictime)) |
| 1906 | / 2.0; |
| 1907 | } |
| 1908 | else { |
| 1909 | x_adjust = 0.0; |
| 1910 | } |
| 1911 | x1 = first_gs_p->c[AX] - x_adjust; |
| 1912 | |
| 1913 | size = (gs_p->grpsize == GS_NORMAL ? |
| 1914 | DFLT_SIZE : SMALLSIZE) |
| 1915 | * Staffscale; |
| 1916 | if (gs_p->grpcont == GC_NOTES) { |
| 1917 | x_adjust = widest_head(gs_p) |
| 1918 | * Staffscale / 2.0; |
| 1919 | } |
| 1920 | else if (gs_p->grpcont == GC_REST) { |
| 1921 | x_adjust = width(FONT_MUSIC, size, |
| 1922 | restchar(gs_p->basictime)) |
| 1923 | / 2.0; |
| 1924 | } |
| 1925 | else { |
| 1926 | x_adjust = 0.0; |
| 1927 | } |
| 1928 | x2 = gs_p->c[AX] + x_adjust; |
| 1929 | |
| 1930 | /* move the bracket line up from the baseline |
| 1931 | * of the number */ |
| 1932 | y1 += (4.0 * Stdpad); |
| 1933 | y2 += (4.0 * Stdpad); |
| 1934 | num_y += (4.0 * Stdpad); |
| 1935 | |
| 1936 | /* figure out how much to adjust y from num_y |
| 1937 | * to account for the space taken up by the |
| 1938 | * number. Use ratio of similar triangles. */ |
| 1939 | if (numwest - x1 == 0.0) { |
| 1940 | /* avoid any chance of divide by 0 */ |
| 1941 | y_adjust = 0.0; |
| 1942 | } |
| 1943 | else { |
| 1944 | y_adjust = (((numeast - numwest |
| 1945 | + (Stdpad * 2.0)) * |
| 1946 | (num_y - y1)) / (numeast - x1)) |
| 1947 | / 2.0; |
| 1948 | } |
| 1949 | |
| 1950 | draw_line(x1, y1, numwest - Stdpad, |
| 1951 | num_y - y_adjust); |
| 1952 | draw_line(numeast + Stdpad, |
| 1953 | num_y + y_adjust, x2, y2); |
| 1954 | draw_line(x1, y1, x1, y1 + brackdir); |
| 1955 | draw_line(x2, y2, x2, y2 + brackdir); |
| 1956 | } |
| 1957 | |
| 1958 | /* re-init in case other tuplets in same measure */ |
| 1959 | num_notes = 0; |
| 1960 | |
| 1961 | break; |
| 1962 | |
| 1963 | default: |
| 1964 | pfatal("bad tuplet type"); |
| 1965 | break; |
| 1966 | } |
| 1967 | } |
| 1968 | } |
| 1969 | \f |
| 1970 | |
| 1971 | /* utility function. Given the first group in a tuplet, return YES if it |
| 1972 | * is to have a bracket printed. It does if the tuplet itself is to be printed, |
| 1973 | * and if not a LONEITEM and if any of the beamlocs do not match the tuploc */ |
| 1974 | |
| 1975 | int |
| 1976 | tupgetsbrack(gs_p) |
| 1977 | |
| 1978 | struct GRPSYL *gs_p; /* first group of tuplet */ |
| 1979 | |
| 1980 | { |
| 1981 | /* If nothing is to be printed or number only, no bracket */ |
| 1982 | if (gs_p->printtup == PT_NEITHER || gs_p->printtup == PT_NUMBER) { |
| 1983 | return(NO); |
| 1984 | } |
| 1985 | |
| 1986 | /* single chord tuplets never get a bracket -- not enough room |
| 1987 | * to draw one */ |
| 1988 | if (gs_p->tuploc == LONEITEM) { |
| 1989 | return(NO); |
| 1990 | } |
| 1991 | |
| 1992 | /* if user insists on a bracket, we oblige */ |
| 1993 | if (gs_p->printtup == PT_BOTH) { |
| 1994 | return(YES); |
| 1995 | } |
| 1996 | |
| 1997 | /* check for mismatches between beamloc and tuploc. */ |
| 1998 | for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) { |
| 1999 | /* grace notes don't count */ |
| 2000 | if (gs_p->grpvalue == GV_ZERO) { |
| 2001 | continue; |
| 2002 | } |
| 2003 | |
| 2004 | if (gs_p->tuploc != gs_p->beamloc) { |
| 2005 | return(YES); |
| 2006 | } |
| 2007 | if (gs_p->tuploc == ENDITEM) { |
| 2008 | /* matched beam everywhere, so no bracket needed */ |
| 2009 | return(NO); |
| 2010 | } |
| 2011 | } |
| 2012 | pfatal("missing end tuplet"); |
| 2013 | |
| 2014 | /*NOTREACHED*/ |
| 2015 | return(NO); |
| 2016 | } |
| 2017 | \f |
| 2018 | |
| 2019 | /* utility function to return PL_ABOVE or PL_BELOW |
| 2020 | * depending on whether the number for |
| 2021 | * the given tuplet should get printed above or below the groups */ |
| 2022 | /* Can be passed any group in the tuplet. If not the first, it will find the |
| 2023 | * first and go from there */ |
| 2024 | |
| 2025 | int |
| 2026 | tupdir(gs_p, staff_p) |
| 2027 | |
| 2028 | struct GRPSYL *gs_p; /* group in tuplet */ |
| 2029 | struct STAFF *staff_p; /* staff pointing to gs_p */ |
| 2030 | |
| 2031 | { |
| 2032 | RATIONAL starttime, endtime; /* begin & end time of tuplet */ |
| 2033 | struct GRPSYL *save_gs_p; /* temporarily save value of gs_p */ |
| 2034 | int othervoice; /* array subscript in staff_p->groups_p |
| 2035 | * of the other voice on this staff */ |
| 2036 | int vscheme; /* V_* value */ |
| 2037 | RATIONAL smalltime; |
| 2038 | |
| 2039 | |
| 2040 | smalltime.n = 1; |
| 2041 | smalltime.d = 2 * MAXBASICTIME; |
| 2042 | |
| 2043 | |
| 2044 | switch (gs_p->tuploc) { |
| 2045 | |
| 2046 | case LONEITEM: |
| 2047 | case STARTITEM: |
| 2048 | /* this is the one we want */ |
| 2049 | break; |
| 2050 | |
| 2051 | case NOITEM: |
| 2052 | pfatal("arg of tupdir is not in a tuplet"); |
| 2053 | /*NOTREACHED*/ |
| 2054 | break; |
| 2055 | default: |
| 2056 | /* have to back up to beginning of tuplet first */ |
| 2057 | for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->prev) { |
| 2058 | if (gs_p->tuploc == STARTITEM) { |
| 2059 | break; |
| 2060 | } |
| 2061 | } |
| 2062 | if (gs_p == (struct GRPSYL *) 0) { |
| 2063 | pfatal("can't find beginning of tuplet"); |
| 2064 | } |
| 2065 | break; |
| 2066 | } |
| 2067 | |
| 2068 | /* figure out which side. First determine vscheme */ |
| 2069 | |
| 2070 | /* there is a circumstance where we're looking at an entire score, |
| 2071 | * (in relvert), and if some of the score has V_1 and some of it |
| 2072 | * doesn't, it's possible for us to get confused and think something |
| 2073 | * isn't V_1 when it is. We would then try to look at the other |
| 2074 | * voice, which is null, and would blow up. To avoid this, if one |
| 2075 | * voice is null, treat measure as V_1 regardless of what vscheme |
| 2076 | * might lead us to believe. |
| 2077 | */ |
| 2078 | if (staff_p->groups_p[1] == (struct GRPSYL *) 0) { |
| 2079 | return(tupdir1voice(gs_p)); |
| 2080 | } |
| 2081 | |
| 2082 | /* voice 3 pays no attention to any other voices. */ |
| 2083 | if (gs_p->vno == 3) { |
| 2084 | return(tupdir1voice(gs_p)); |
| 2085 | } |
| 2086 | |
| 2087 | if ((vscheme = svpath(staff_p->staffno, VSCHEME)->vscheme) == V_1) { |
| 2088 | return(tupdir1voice(gs_p)); |
| 2089 | } |
| 2090 | else if (vscheme == V_2OPSTEM) { |
| 2091 | /* 2 opposing stem voices, always put tuplet above voice 1 and |
| 2092 | * below voice 2 */ |
| 2093 | if (gs_p->tupside != PL_UNKNOWN) { |
| 2094 | l_warning(gs_p->inputfile, gs_p->inputlineno, |
| 2095 | "tuplet side specification not valid when vscheme=2o"); |
| 2096 | /* fix so we don't print error again if called |
| 2097 | * again on this tuplet */ |
| 2098 | gs_p->tupside = PL_UNKNOWN; |
| 2099 | } |
| 2100 | return(gs_p->vno == 1 ? PL_ABOVE : PL_BELOW); |
| 2101 | } |
| 2102 | else { |
| 2103 | /* find the time period taken by tuplet */ |
| 2104 | save_gs_p = gs_p; |
| 2105 | starttime = Zero; |
| 2106 | /* find time to where tuplet begins */ |
| 2107 | for (gs_p = gs_p->prev; gs_p != (struct GRPSYL *) 0; |
| 2108 | gs_p = gs_p->prev) { |
| 2109 | starttime = radd(starttime, gs_p->fulltime); |
| 2110 | } |
| 2111 | /* find time up to last note of tuplet */ |
| 2112 | endtime = starttime; |
| 2113 | for (gs_p = save_gs_p; gs_p->tuploc != ENDITEM |
| 2114 | && gs_p->tuploc != LONEITEM; |
| 2115 | gs_p = gs_p->next) { |
| 2116 | endtime = radd(endtime, gs_p->fulltime); |
| 2117 | } |
| 2118 | /* add on a little bit for the final group of the tuplet */ |
| 2119 | endtime = radd(endtime, smalltime); |
| 2120 | |
| 2121 | /* now check if other voice has space or not */ |
| 2122 | othervoice = (gs_p->vno == 1 ? 1 : 0); |
| 2123 | if (hasspace(staff_p->groups_p [othervoice], starttime, endtime) |
| 2124 | == YES) { |
| 2125 | /* other voice is space: treat like V_1 */ |
| 2126 | return(tupdir1voice(save_gs_p)); |
| 2127 | } |
| 2128 | else { |
| 2129 | /* other voice not space: treat like V_2OPSTEM */ |
| 2130 | if (gs_p->tupside != PL_UNKNOWN) { |
| 2131 | l_warning(gs_p->inputfile, gs_p->inputlineno, |
| 2132 | "tuplet side specification not valid when there are two voices"); |
| 2133 | /* fix so we don't print error again if called |
| 2134 | * again on this tuplet */ |
| 2135 | gs_p->tupside = PL_UNKNOWN; |
| 2136 | } |
| 2137 | return(gs_p->vno == 1 ? PL_ABOVE : PL_BELOW); |
| 2138 | } |
| 2139 | } |
| 2140 | } |
| 2141 | \f |
| 2142 | |
| 2143 | /* return PL_ABOVE or PL_BELOW for tup location assuming a single voice */ |
| 2144 | |
| 2145 | static int |
| 2146 | tupdir1voice(gs_p) |
| 2147 | |
| 2148 | struct GRPSYL *gs_p; /* first group of tuplet */ |
| 2149 | |
| 2150 | { |
| 2151 | int stemdirsum; /* sum of stem directions to see if mostly up or down */ |
| 2152 | |
| 2153 | |
| 2154 | /* if user specified a direction, the answer is easy */ |
| 2155 | if (gs_p->tupside != PL_UNKNOWN) { |
| 2156 | return(gs_p->tupside); |
| 2157 | } |
| 2158 | |
| 2159 | /* Count up stem directions. Whichever side |
| 2160 | * has more stems, put it on that side. In case of tie, |
| 2161 | * arbitrarily choose above. */ |
| 2162 | stemdirsum = 0; |
| 2163 | for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) { |
| 2164 | if (gs_p->grpcont == GC_NOTES && gs_p->grpvalue != GV_ZERO) { |
| 2165 | stemdirsum += (gs_p->stemdir == UP ? 1 : -1); |
| 2166 | } |
| 2167 | if (gs_p->tuploc == LONEITEM || gs_p->tuploc == ENDITEM) { |
| 2168 | break; |
| 2169 | } |
| 2170 | } |
| 2171 | |
| 2172 | return(stemdirsum >= 0 ? PL_ABOVE : PL_BELOW); |
| 2173 | } |
| 2174 | \f |
| 2175 | |
| 2176 | /* go through measure, printing any beams. Gets called once for normal sized |
| 2177 | * notes, once for cue notes, and once for grace note. */ |
| 2178 | |
| 2179 | static void |
| 2180 | pr_beams(gs_p, grpvalue, grpsize) |
| 2181 | |
| 2182 | struct GRPSYL *gs_p; /* list of grpsyls for current measure |
| 2183 | * of current voice */ |
| 2184 | int grpvalue; /* GV_NORMAL, GV_ZERO */ |
| 2185 | int grpsize; /* GS_NORMAL, GS_SMALL */ |
| 2186 | |
| 2187 | { |
| 2188 | struct GRPSYL *startbeam_p; /* first in beam group */ |
| 2189 | int t; /* 8, 16, etc for basictimes */ |
| 2190 | |
| 2191 | |
| 2192 | /* go through all the grpsyls in measure */ |
| 2193 | for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) { |
| 2194 | |
| 2195 | /* skip until we find a STARTITEM |
| 2196 | * on the relevant kind of group */ |
| 2197 | if (gs_p->beamloc != STARTITEM || gs_p->grpvalue != grpvalue |
| 2198 | || gs_p->grpsize != grpsize) { |
| 2199 | continue; |
| 2200 | } |
| 2201 | |
| 2202 | /* when there are cross-staff beams, we will find the beam |
| 2203 | * on both staffs, but only need to draw it once. So skip |
| 2204 | * it the second time */ |
| 2205 | if (gs_p->beamto == CS_ABOVE) { |
| 2206 | continue; |
| 2207 | } |
| 2208 | |
| 2209 | /* find the matching ENDITEM */ |
| 2210 | for (startbeam_p = gs_p; gs_p != 0 && (gs_p->beamloc != ENDITEM |
| 2211 | || gs_p->grpvalue != grpvalue |
| 2212 | || gs_p->grpsize != grpsize); |
| 2213 | gs_p = gs_p->next) { |
| 2214 | |
| 2215 | } |
| 2216 | if (gs_p == 0) { |
| 2217 | pfatal("pr_beams couldn't find end of beam group"); |
| 2218 | } |
| 2219 | |
| 2220 | /* now go through beam group drawing beams for 8th notes, |
| 2221 | * then 16th, etc */ |
| 2222 | for (t = 8; t <= MAXBASICTIME; t <<= 1) { |
| 2223 | if (draw_beams(startbeam_p, gs_p, t, grpsize, grpvalue) |
| 2224 | <= 0) { |
| 2225 | break; |
| 2226 | } |
| 2227 | } |
| 2228 | } |
| 2229 | } |
| 2230 | \f |
| 2231 | |
| 2232 | /* In the case of cross-staff beams with the above staff's stems down, |
| 2233 | * and the below staff's stems up, we need to do extra work. |
| 2234 | * This function builds up a mesh of structs that represent the beams, |
| 2235 | * with a row of CSBINFO structs linked horizontally for each beam, |
| 2236 | * and vertical links at each stem. end_bm_offset() then uses this information |
| 2237 | * to figure out where along the stem a beam ends. |
| 2238 | * This function returns a pointer to the beginning of the 8th note beam. |
| 2239 | * |
| 2240 | * As an example, consider this input: |
| 2241 | * 1: 8.c; 64f beam with staff below; 32.s; 16e; 8s; 8e; 16s; 32.f; 64s ebm; |
| 2242 | * 2: 8.e; 64s beam with staff above; 32.a; 16s; 8g; 8s; 16g; 32.s; 64a ebm; |
| 2243 | * The resulting mesh will look like this: |
| 2244 | * . . . . |
| 2245 | * . . . . |
| 2246 | * (64th) X . . . |
| 2247 | * | . . . |
| 2248 | * (32nd) X --> X . . . |
| 2249 | * | | . . . |
| 2250 | * (16th) X --> X --> X . X --> X (32nd) |
| 2251 | * | | | . | | |
| 2252 | * return_value --> X --> X --> X --> X --> X --> X --> X --> X (8th) |
| 2253 | * . . | | | |
| 2254 | * . . X --> X --> X (16th) |
| 2255 | * . . . | |
| 2256 | * . . . X (64th) |
| 2257 | * . . . . |
| 2258 | * . . . . |
| 2259 | * |
| 2260 | * Each X in the diagram represents a CSBINFO struct. |
| 2261 | * Each row represents a beam. The --> is the "next" field. |
| 2262 | * Each column represents a stem. It is a doubly-linked list, |
| 2263 | * using above_p and below_p fields. |
| 2264 | * The dots show the stem direction. |
| 2265 | */ |
| 2266 | |
| 2267 | static struct CSBINFO * |
| 2268 | mkcsbmesh(begin_p, end_p) |
| 2269 | |
| 2270 | struct GRPSYL *begin_p; /* first group of cross-staff beam on upper staff */ |
| 2271 | struct GRPSYL *end_p; /* 8th note beam goes from begin_p to end_p. |
| 2272 | * There may be zero or more additional beams |
| 2273 | * for shorter durations that span part or all |
| 2274 | * of this list. |
| 2275 | */ |
| 2276 | |
| 2277 | { |
| 2278 | struct CSBINFO *csbi_list_p; /* this points to the 8th note beam |
| 2279 | * list, which is what will |
| 2280 | * ultimately be returned */ |
| 2281 | struct CSBINFO *csbi_p; /* the current information */ |
| 2282 | struct CSBINFO *csbi8_p; /* to walk through 8th list */ |
| 2283 | struct CSBINFO *prevcsbi_p; /* previous in horizontal list */ |
| 2284 | struct CSBINFO *c_p; /* for walking vertical lists */ |
| 2285 | struct GRPSYL *gs_p; /* to walk through beamed groups */ |
| 2286 | int basictime; /* 8, 16, 32, etc */ |
| 2287 | int stemdir; /* stem direction where beam starts */ |
| 2288 | int shortest; /* shortest basictime (8, 16, ...) */ |
| 2289 | |
| 2290 | |
| 2291 | /* There is always at least an 8th note beam that goes the |
| 2292 | * entire length, so make a list for that. */ |
| 2293 | csbi_list_p = prevcsbi_p = 0; |
| 2294 | shortest = 8; |
| 2295 | for (gs_p = begin_p; gs_p != end_p->next; gs_p = nxtbmgrp(gs_p, |
| 2296 | begin_p, end_p->next)) { |
| 2297 | MALLOC(CSBINFO, csbi_p, 1); |
| 2298 | |
| 2299 | /* set horizontal list links */ |
| 2300 | if (csbi_list_p == 0) { |
| 2301 | /* first item on the horizontal list */ |
| 2302 | csbi_list_p = csbi_p; |
| 2303 | } |
| 2304 | else { |
| 2305 | /* link from previous horizontally */ |
| 2306 | prevcsbi_p->next = csbi_p; |
| 2307 | } |
| 2308 | prevcsbi_p = csbi_p; |
| 2309 | csbi_p->next = 0; |
| 2310 | |
| 2311 | /* init vertical list links */ |
| 2312 | csbi_p->above_p = csbi_p->below_p = 0; |
| 2313 | |
| 2314 | /* this is for the 8th note beam */ |
| 2315 | csbi_p->basictime = 8; |
| 2316 | /* save what group this is for, for later convenience */ |
| 2317 | csbi_p->gs_p = gs_p; |
| 2318 | |
| 2319 | /* remember the shortest basictime anywhere in the beam */ |
| 2320 | if (gs_p->basictime > shortest) { |
| 2321 | shortest = gs_p->basictime; |
| 2322 | } |
| 2323 | } |
| 2324 | |
| 2325 | /* For each additional beam, build up a row of structs representing |
| 2326 | * that beam, and link it vertically to the row below or above it, |
| 2327 | * depending on whether the first group of the beam is on the staff |
| 2328 | * above or below the 8th beam. |
| 2329 | */ |
| 2330 | for (basictime = 16; basictime <= shortest; basictime <<= 1) { |
| 2331 | stemdir = UNKNOWN; /* Init to keep lint happy; |
| 2332 | * this will get set to appropriate |
| 2333 | * value before it is actually used. */ |
| 2334 | prevcsbi_p = 0; /* No run of groups found yet */ |
| 2335 | |
| 2336 | /* Walk through list, finding any runs of groups that are |
| 2337 | * at least as short in duration as the current basictime |
| 2338 | * we are looking for. Note this could be as little as a single |
| 2339 | * group in the case of a partial beam. |
| 2340 | * We walk through the GRPSYLs and their |
| 2341 | * corresponding CSBINFO structs in parallel. |
| 2342 | */ |
| 2343 | for (gs_p = begin_p, csbi8_p = csbi_list_p; |
| 2344 | gs_p != end_p->next; |
| 2345 | gs_p = nxtbmgrp(gs_p, begin_p, end_p->next), |
| 2346 | csbi8_p = csbi8_p->next) { |
| 2347 | |
| 2348 | if (gs_p->basictime >= basictime) { |
| 2349 | /* this group is part of a beam of at least |
| 2350 | * as short as the basictime of interest. */ |
| 2351 | MALLOC(CSBINFO, csbi_p, 1); |
| 2352 | csbi_p->next = 0; |
| 2353 | csbi_p->basictime = basictime; |
| 2354 | |
| 2355 | /* If not first group in this beam, |
| 2356 | * link from previous. If is first, |
| 2357 | * save its stem direction. That determines |
| 2358 | * which side of the 8th beam it goes on. */ |
| 2359 | if (prevcsbi_p != 0) { |
| 2360 | prevcsbi_p->next = csbi_p; |
| 2361 | } |
| 2362 | else { |
| 2363 | stemdir = gs_p->stemdir; |
| 2364 | } |
| 2365 | /* Prepare to link more on horizonally, |
| 2366 | * if beam goes further. */ |
| 2367 | prevcsbi_p = csbi_p; |
| 2368 | |
| 2369 | /* set vertical links */ |
| 2370 | if (stemdir == DOWN) { |
| 2371 | /* Must be from staff above. |
| 2372 | * Find current top, and add |
| 2373 | * above there */ |
| 2374 | for (c_p = csbi8_p; c_p->above_p != 0; |
| 2375 | c_p = c_p->above_p) { |
| 2376 | ; |
| 2377 | } |
| 2378 | c_p->above_p = csbi_p; |
| 2379 | csbi_p->below_p = c_p; |
| 2380 | csbi_p->above_p = 0; |
| 2381 | } |
| 2382 | else { |
| 2383 | /* similar for from staff below */ |
| 2384 | for (c_p = csbi8_p; c_p->below_p != 0; |
| 2385 | c_p = c_p->below_p) { |
| 2386 | ; |
| 2387 | } |
| 2388 | c_p->below_p = csbi_p; |
| 2389 | csbi_p->above_p = c_p; |
| 2390 | csbi_p->below_p = 0; |
| 2391 | } |
| 2392 | } |
| 2393 | else { |
| 2394 | /* If we were doing a beam before, |
| 2395 | * it's done now */ |
| 2396 | prevcsbi_p = 0; |
| 2397 | } |
| 2398 | } |
| 2399 | } |
| 2400 | return(csbi_list_p); |
| 2401 | } |
| 2402 | \f |
| 2403 | |
| 2404 | /* draw beams in a beam group for a particular time value, 8th, 16th, etc */ |
| 2405 | /* this gets called repeatedly, first for 8th, then 16ths, etc, until |
| 2406 | * there are no more shorter notes. |
| 2407 | * It returns the number of beams drawn (including partials) */ |
| 2408 | |
| 2409 | |
| 2410 | static int |
| 2411 | draw_beams(gs_p, endbeam_p, basictime, grpsize, grpvalue) |
| 2412 | |
| 2413 | struct GRPSYL *gs_p; /* start of beam group */ |
| 2414 | struct GRPSYL *endbeam_p; /* end of beam group */ |
| 2415 | int basictime; /* draw beam for this basic time: |
| 2416 | * 8, 16, 32, 64, etc */ |
| 2417 | int grpsize; /* GS_NORMAL, GS_SMALL */ |
| 2418 | int grpvalue; /* GV_NORMAL, GV_ZERO */ |
| 2419 | |
| 2420 | { |
| 2421 | int found = 0; /* how many beams found to be drawn */ |
| 2422 | int ngrps; /* how many groups to beam together */ |
| 2423 | struct GRPSYL *first_p = 0;/* first group in beam (the one on the |
| 2424 | * above staff while doing cross-staff beams) */ |
| 2425 | struct GRPSYL *begin_p = 0, *end_p; /* the initialization is |
| 2426 | * to shut up bogus compiler warning */ |
| 2427 | struct GRPSYL *other_p; /* other note that must be used to calculate |
| 2428 | * slope of partial beam */ |
| 2429 | float y_offset; /* from end of stem to draw beam */ |
| 2430 | int side; /* left or right for partial beam */ |
| 2431 | float x_begin, y_begin, x_other, y_other; /* partial beam |
| 2432 | * coordinates */ |
| 2433 | double halfwidth; /* half width of a beam */ |
| 2434 | double end_y_offset; /* to deal with cross staff beams */ |
| 2435 | double slope; /* of partial beam */ |
| 2436 | double halfstem; |
| 2437 | double stemdist; /* distance between stems */ |
| 2438 | double pbeam_len; /* length of partial beam */ |
| 2439 | |
| 2440 | |
| 2441 | /* get relevant group, accounting for cross-staff beams */ |
| 2442 | first_p = gs_p; |
| 2443 | gs_p = neighboring_note_beam_group(gs_p, first_p, NO); |
| 2444 | |
| 2445 | /* go through the list */ |
| 2446 | while ( gs_p != endbeam_p->next) { |
| 2447 | |
| 2448 | /* find however many in a row deserve to get another beam */ |
| 2449 | for (end_p = (struct GRPSYL *) 0, ngrps = 0; |
| 2450 | (gs_p != endbeam_p->next); |
| 2451 | gs_p = nxtbmgrp(gs_p, |
| 2452 | first_p, endbeam_p->next)) { |
| 2453 | |
| 2454 | /* if wrong type (e.g a grace inside of |
| 2455 | * a set of normal notes), skip over */ |
| 2456 | if (gs_p->grpsize != grpsize |
| 2457 | || gs_p->grpvalue != grpvalue ) { |
| 2458 | continue; |
| 2459 | } |
| 2460 | |
| 2461 | /* if not beamed, skip */ |
| 2462 | if ( gs_p->beamloc == NOITEM) { |
| 2463 | pfatal("non-beam inside beam group\n"); |
| 2464 | } |
| 2465 | |
| 2466 | /* if this one deserves another beam, |
| 2467 | * keep track of that. If not, break out */ |
| 2468 | if (gs_p->basictime >= basictime) { |
| 2469 | end_p = gs_p; |
| 2470 | found++; |
| 2471 | if (ngrps == 0) { |
| 2472 | begin_p = gs_p; |
| 2473 | } |
| 2474 | ngrps++; |
| 2475 | if (gs_p->breakbeam == YES && basictime > 8) { |
| 2476 | break; |
| 2477 | } |
| 2478 | } |
| 2479 | else { |
| 2480 | break; |
| 2481 | } |
| 2482 | } |
| 2483 | |
| 2484 | /* prepare to do next one */ |
| 2485 | if (gs_p != endbeam_p->next) { |
| 2486 | gs_p = nxtbmgrp(gs_p, first_p, endbeam_p->next); |
| 2487 | } |
| 2488 | |
| 2489 | /* if none we looked at deserved a beam, keep looking */ |
| 2490 | if (end_p == (struct GRPSYL *) 0) { |
| 2491 | continue; |
| 2492 | } |
| 2493 | |
| 2494 | /* calculate where on stem the beam should start */ |
| 2495 | y_offset = beam_offset(numbeams(basictime), |
| 2496 | begin_p->grpsize, begin_p->stemdir); |
| 2497 | |
| 2498 | if (end_p->grpsize == GS_NORMAL) { |
| 2499 | halfwidth = W_WIDE * Staffscale / PPI / 2.0; |
| 2500 | halfstem = W_NORMAL * Staffscale / PPI / 2.0; |
| 2501 | } |
| 2502 | else { |
| 2503 | halfwidth = W_WIDE * Staffscale * SM_FACTOR / PPI / 2.0; |
| 2504 | halfstem = W_NORMAL * Staffscale * SM_FACTOR / PPI / 2.0; |
| 2505 | } |
| 2506 | |
| 2507 | /* check if single group. |
| 2508 | * If so, need to do a partial beam, otherwise full beam */ |
| 2509 | if (ngrps == 1) { |
| 2510 | /* rests and spaces don't get beams, |
| 2511 | * so don't get partial ones */ |
| 2512 | if (end_p->grpcont != GC_NOTES) { |
| 2513 | continue; |
| 2514 | } |
| 2515 | |
| 2516 | side = pbeamside(end_p, first_p); |
| 2517 | |
| 2518 | /* Now that we decided where the |
| 2519 | * partial beam goes, we can draw it */ |
| 2520 | |
| 2521 | /* in order to figure out the end point of the partial |
| 2522 | * beam, we have to calculate the slope of the beam as |
| 2523 | * if it were a full beam and derive from that where |
| 2524 | * the partial beam will end. */ |
| 2525 | /* determine whether to use prev or next note, and |
| 2526 | * skip any notes of the wrong type! */ |
| 2527 | if (side == PB_LEFT) { |
| 2528 | other_p = prevbmgrp(end_p, first_p); |
| 2529 | } |
| 2530 | else { |
| 2531 | other_p = nxtbmgrp(end_p, first_p, end_p->next); |
| 2532 | } |
| 2533 | |
| 2534 | /* the line then goes from the stem (at y_offset) to |
| 2535 | * a notehead width east or west of the stem, |
| 2536 | * with the y coordinate calculated from the slope |
| 2537 | * of what a full length beam would have been, unless |
| 2538 | * stems are too close, in which case shorten it |
| 2539 | * somewhat. */ |
| 2540 | x_begin = find_x_stem(end_p); |
| 2541 | y_begin = find_y_stem(end_p); |
| 2542 | x_other = find_x_stem(other_p); |
| 2543 | y_other = find_y_stem(other_p); |
| 2544 | |
| 2545 | /* if cross-staff and the two stems are in opposite |
| 2546 | * directions, have to compensate for that */ |
| 2547 | if (end_p->stemdir == other_p->stemdir) { |
| 2548 | /* in same direction */ |
| 2549 | slope = (y_other - y_begin) |
| 2550 | / (x_other - x_begin); |
| 2551 | } |
| 2552 | else { |
| 2553 | double opp_adj; |
| 2554 | |
| 2555 | opp_adj = beam_offset( |
| 2556 | numbeams(other_p->basictime), |
| 2557 | other_p->grpsize, other_p->stemdir); |
| 2558 | slope = (y_other - (y_begin - opp_adj)) |
| 2559 | / (x_other - x_begin); |
| 2560 | } |
| 2561 | |
| 2562 | /* adjust to overlap stem */ |
| 2563 | x_begin += halfstem * side; |
| 2564 | |
| 2565 | /* determine partial beam length */ |
| 2566 | /* find distance between stems */ |
| 2567 | if (x_begin < x_other) { |
| 2568 | stemdist = x_other - x_begin; |
| 2569 | } |
| 2570 | else { |
| 2571 | stemdist = x_begin - x_other; |
| 2572 | } |
| 2573 | /* if wide enough, use note head width, else less */ |
| 2574 | if (stemdist < 5.0 * Stepsize) { |
| 2575 | pbeam_len = 0.4 * stemdist; |
| 2576 | } |
| 2577 | else { |
| 2578 | pbeam_len = widest_head(end_p) * Staffscale; |
| 2579 | } |
| 2580 | |
| 2581 | /* draw the partial beam */ |
| 2582 | do_beam(x_begin, y_begin + y_offset, |
| 2583 | x_begin + pbeam_len * side, |
| 2584 | y_begin + y_offset + side * |
| 2585 | pbeam_len * slope, halfwidth); |
| 2586 | } |
| 2587 | |
| 2588 | else { |
| 2589 | /* draw a normal beam */ |
| 2590 | |
| 2591 | /* For regular beams, can use y_offset directly, |
| 2592 | * but with cross-staff beam, stems may be in opposite |
| 2593 | * directions, so have to call a function to get |
| 2594 | * appropriate offset. |
| 2595 | */ |
| 2596 | if (begin_p->beamto == CS_SAME) { |
| 2597 | end_y_offset = y_offset; |
| 2598 | } |
| 2599 | else { |
| 2600 | end_y_offset = end_bm_offset(first_p, end_p, |
| 2601 | basictime); |
| 2602 | } |
| 2603 | |
| 2604 | /* If the stems on both ends of the beam |
| 2605 | * are zero length, don't draw any beams. |
| 2606 | * If user really wants the beams, |
| 2607 | * they can make one of the ends |
| 2608 | * barely longer than zero. |
| 2609 | */ |
| 2610 | if (begin_p->stemlen <= 0.0 && end_p->stemlen <= 0.0) { |
| 2611 | continue; |
| 2612 | } |
| 2613 | |
| 2614 | /* find end of first stem and last stem and draw |
| 2615 | * the beam at proper offset from there */ |
| 2616 | do_beam(find_x_stem(begin_p) - halfstem, |
| 2617 | find_y_stem(begin_p) + y_offset, |
| 2618 | find_x_stem(end_p) + halfstem, |
| 2619 | find_y_stem(end_p) + end_y_offset, |
| 2620 | halfwidth); |
| 2621 | } |
| 2622 | } |
| 2623 | return(found); |
| 2624 | } |
| 2625 | \f |
| 2626 | |
| 2627 | /* Figure out how far from the end of a stem a beam should be in the |
| 2628 | * case of a cross-staff beam with opposite-direction stems at its ends. |
| 2629 | * Will return some multiple (possibly 0) of the distance between beams, |
| 2630 | * with the proper sign to account for stem direction. |
| 2631 | * Should only be called if the beam in question is a cross-staff beam. |
| 2632 | */ |
| 2633 | |
| 2634 | double |
| 2635 | end_bm_offset(top_first_p, end_p, basictime) |
| 2636 | |
| 2637 | struct GRPSYL *top_first_p; /* the group that has "bm with staff below" */ |
| 2638 | struct GRPSYL *end_p; /* the group where a beam ends. This could be |
| 2639 | * either a group with ebm or some intermediate |
| 2640 | * group that happens to end a beam segment |
| 2641 | * that is shorter. */ |
| 2642 | int basictime; /* the basictime of the beam currently under |
| 2643 | * consideration. The first beam drawn will |
| 2644 | * be 8, the next 16, then 32, etc. */ |
| 2645 | |
| 2646 | { |
| 2647 | static struct CSBINFO *csbi_list_p = 0;/* Info about the cross beams */ |
| 2648 | static struct GRPSYL *cached_gs_p = 0; /* Each time we get a different |
| 2649 | * top_first_p, we calculate its csbi_list |
| 2650 | * and cache it for future calls. This lets |
| 2651 | * us know if we can re-use the cached value. */ |
| 2652 | struct CSBINFO *csbi_p; /* for walking 8th note beam ->next links */ |
| 2653 | struct CSBINFO *c_p; /* for walking vertical links of mesh */ |
| 2654 | int nbeams; /* how many beams from the stem end */ |
| 2655 | |
| 2656 | if (cached_gs_p != top_first_p) { |
| 2657 | /* Cached one is no good; need to recalculate */ |
| 2658 | if (csbi_list_p != 0) { |
| 2659 | /* We had a list before; need to clean it up */ |
| 2660 | struct CSBINFO *nextvert_p; /* to free vert list */ |
| 2661 | struct CSBINFO *nexthor_p; /* to free hor list */ |
| 2662 | /* walk horizontal list */ |
| 2663 | for (csbi_p = csbi_list_p; csbi_p != 0; |
| 2664 | csbi_p = nexthor_p) { |
| 2665 | /* clean up vert list, both directions */ |
| 2666 | for (c_p = csbi_p->above_p; c_p != 0; |
| 2667 | c_p = nextvert_p) { |
| 2668 | nextvert_p = c_p->above_p; |
| 2669 | FREE(c_p); |
| 2670 | } |
| 2671 | for (c_p = csbi_p->below_p; c_p != 0; |
| 2672 | c_p = nextvert_p) { |
| 2673 | nextvert_p = c_p->below_p; |
| 2674 | FREE(c_p); |
| 2675 | } |
| 2676 | nexthor_p = csbi_p->next; |
| 2677 | FREE(csbi_p); |
| 2678 | } |
| 2679 | } |
| 2680 | /* Calculate everything for current beam */ |
| 2681 | csbi_list_p = mkcsbmesh(top_first_p, end_p); |
| 2682 | cached_gs_p = top_first_p; |
| 2683 | } |
| 2684 | |
| 2685 | /* First follow the 8th note CSBINFO list across till we find |
| 2686 | * the one matching the end group. */ |
| 2687 | for (csbi_p = csbi_list_p; csbi_p != 0 && csbi_p->gs_p != end_p; |
| 2688 | csbi_p = csbi_p->next) { |
| 2689 | ; |
| 2690 | } |
| 2691 | if (csbi_p == 0) { |
| 2692 | pfatal("couldn't find beam end group in end_bm_offset()"); |
| 2693 | } |
| 2694 | |
| 2695 | /* Now follow the vertical links until we find the right basic time. |
| 2696 | * It could be on either side of the 8th beam. |
| 2697 | * First we find the end of the stem, then count the number of |
| 2698 | * links we have to follow to get to the one with the right basictime. |
| 2699 | */ |
| 2700 | if (end_p->stemdir == DOWN) { |
| 2701 | /* Must be from staff above, so end of stem is all the way * down the below_p list. |
| 2702 | */ |
| 2703 | for (c_p = csbi_p; c_p->below_p != 0; c_p = c_p->below_p) { |
| 2704 | ; |
| 2705 | } |
| 2706 | /* Now count the number of beams till the one we want */ |
| 2707 | for (nbeams = 1; c_p->basictime != basictime; |
| 2708 | c_p = c_p->above_p) { |
| 2709 | nbeams++; |
| 2710 | } |
| 2711 | if (c_p == 0) { |
| 2712 | pfatal("failed to find cross staff beam info go up"); |
| 2713 | } |
| 2714 | } |
| 2715 | else { |
| 2716 | /* similar for staff below groups */ |
| 2717 | for (c_p = csbi_p; c_p->above_p != 0; c_p = c_p->above_p) { |
| 2718 | ; |
| 2719 | } |
| 2720 | /* Now count the number of beams till the one we want */ |
| 2721 | for (nbeams = 1; c_p->basictime != basictime; |
| 2722 | c_p = c_p->below_p) { |
| 2723 | nbeams++; |
| 2724 | } |
| 2725 | if (c_p == 0) { |
| 2726 | pfatal("failed to find cross staff beam info go up"); |
| 2727 | } |
| 2728 | } |
| 2729 | return (beam_offset(nbeams, end_p->grpsize, end_p->stemdir)); |
| 2730 | } |
| 2731 | \f |
| 2732 | |
| 2733 | /* find y offset on stem based on number of beams, whether normal or small |
| 2734 | * notes, and stem direction */ |
| 2735 | |
| 2736 | static double |
| 2737 | beam_offset(nbeams, gsize, stemdir) |
| 2738 | |
| 2739 | int nbeams; /* how many beams */ |
| 2740 | int gsize; /* GS_NORMAL or GS_SMALL */ |
| 2741 | int stemdir; /* UP or DOWN */ |
| 2742 | |
| 2743 | { |
| 2744 | /* for consistency, it would be nice to use FLAGSEP and SMFLAGSEP |
| 2745 | * for beam separation too, but when we tried that, beams looked too |
| 2746 | * close together, especially on certain low-resolution devices, |
| 2747 | * so that's why we're using 5 and 4 stepsizes. */ |
| 2748 | return ( (nbeams - 1) * (gsize == GS_NORMAL ? 5.0 : 4.0) |
| 2749 | * Staffscale |
| 2750 | * (stemdir == UP ? -POINT : POINT) ); |
| 2751 | } |
| 2752 | \f |
| 2753 | |
| 2754 | /* Given a group inside a beam, return the next group. Usually this will |
| 2755 | * be gs_p->next, but in the case of a cross-staff beam, it might be a |
| 2756 | * group on the other staff */ |
| 2757 | |
| 2758 | struct GRPSYL * |
| 2759 | nxtbmgrp(gs_p, first_p, endnext_p) |
| 2760 | |
| 2761 | struct GRPSYL *gs_p; /* find the beam group after this one */ |
| 2762 | struct GRPSYL *first_p; /* The first group in the top staff of the |
| 2763 | * beam */ |
| 2764 | struct GRPSYL *endnext_p; /* what to return upon reaching the end of |
| 2765 | * the beam. This will be the ->next field of |
| 2766 | * the last group in the beam on the top staff |
| 2767 | * of a cross-staff beam. Returning this lets |
| 2768 | * legacy code (code before we supported |
| 2769 | * cross-staff beams) keep working with minimal |
| 2770 | * changes. */ |
| 2771 | |
| 2772 | { |
| 2773 | int grpsize, grpvalue; |
| 2774 | |
| 2775 | /* If we are passed the first group, it could be a space, |
| 2776 | * in which case we need to use the below staff's group instead. |
| 2777 | */ |
| 2778 | if (gs_p->grpcont == GC_SPACE && gs_p->beamto != CS_SAME) { |
| 2779 | /* Need to hop to below staff. Go down the chord to find |
| 2780 | * the matching cross-staff beam group. */ |
| 2781 | do { |
| 2782 | if ((gs_p = gs_p->gs_p) == (struct GRPSYL *) 0) { |
| 2783 | pfatal("can't find matching beam chord"); |
| 2784 | } |
| 2785 | |
| 2786 | /* skip any lyrics and such till we find the beamed-to group */ |
| 2787 | } while (gs_p->beamto != CS_ABOVE); |
| 2788 | } |
| 2789 | |
| 2790 | /* need to skip past any groups of the wrong kind */ |
| 2791 | grpsize = first_p->grpsize; |
| 2792 | grpvalue = first_p->grpvalue; |
| 2793 | do { |
| 2794 | /* Move to next group. If that gets us to the end |
| 2795 | * of the measure, report that we're done. */ |
| 2796 | if ((gs_p = gs_p->next) == (struct GRPSYL *) 0) { |
| 2797 | return(endnext_p); |
| 2798 | } |
| 2799 | } while (gs_p->grpsize != grpsize || gs_p->grpvalue != grpvalue); |
| 2800 | |
| 2801 | /* if past end of beam group, report that we're done */ |
| 2802 | if (gs_p->beamloc != INITEM && gs_p->beamloc != ENDITEM) { |
| 2803 | return(endnext_p); |
| 2804 | } |
| 2805 | |
| 2806 | return(neighboring_note_beam_group(gs_p, first_p, NO)); |
| 2807 | } |
| 2808 | \f |
| 2809 | |
| 2810 | /* Given a group inside a beam (not the first), |
| 2811 | * return the previous group. Usually this will |
| 2812 | * be gs_p->prev, but in the case of a cross-staff beam, it might be a |
| 2813 | * group on the other staff */ |
| 2814 | |
| 2815 | struct GRPSYL * |
| 2816 | prevbmgrp(gs_p, first_p) |
| 2817 | |
| 2818 | struct GRPSYL *gs_p; /* find the beam group after this one */ |
| 2819 | struct GRPSYL *first_p; /* The first group in the top staff of the |
| 2820 | * beam */ |
| 2821 | |
| 2822 | { |
| 2823 | int grpsize, grpvalue; |
| 2824 | int staffno; |
| 2825 | |
| 2826 | staffno = gs_p->staffno; |
| 2827 | |
| 2828 | /* need to skip past any groups of the wrong kind */ |
| 2829 | grpsize = first_p->grpsize; |
| 2830 | grpvalue = first_p->grpvalue; |
| 2831 | do { |
| 2832 | /* Move to prev group. */ |
| 2833 | if ((gs_p = gs_p->prev) == (struct GRPSYL *) 0) { |
| 2834 | pfatal("prevbmgrp couldn't find prev group"); |
| 2835 | } |
| 2836 | } while (gs_p->grpsize != grpsize || gs_p->grpvalue != grpvalue); |
| 2837 | |
| 2838 | gs_p = neighboring_note_beam_group(gs_p, first_p, YES); |
| 2839 | |
| 2840 | /* if we hopped staffs, then the space on the original staff might |
| 2841 | * have been a long note, in which case the group we have isn't |
| 2842 | * really the one we want. So we have to go forward on this new staff |
| 2843 | * until we find the space that corresponds to the groups we started |
| 2844 | * with, then back up one group from there. That's the one we want */ |
| 2845 | if (staffno != gs_p->staffno) { |
| 2846 | /* we hopped staffs. Go forward to the next space */ |
| 2847 | for (gs_p = gs_p->next; gs_p->grpcont != GC_SPACE; |
| 2848 | gs_p = gs_p->next) { |
| 2849 | ; |
| 2850 | } |
| 2851 | /* now take the group right before the space */ |
| 2852 | gs_p = gs_p->prev; |
| 2853 | } |
| 2854 | return(gs_p); |
| 2855 | } |
| 2856 | \f |
| 2857 | |
| 2858 | /* Given a group in a beam, skip over any embedded rests. |
| 2859 | * Then if the group is not a space, return it as it is. |
| 2860 | * If it is a space, return the corresponding group on the staff |
| 2861 | * that this group is beamed to */ |
| 2862 | |
| 2863 | static struct GRPSYL * |
| 2864 | neighboring_note_beam_group(gs_p, first_p, backwards) |
| 2865 | |
| 2866 | struct GRPSYL *gs_p; /* find the beam group neighboring this one */ |
| 2867 | struct GRPSYL *first_p; /* The first group in the top staff of the |
| 2868 | * beam */ |
| 2869 | int backwards; /* if YES, go backwards (find the previous |
| 2870 | * group rather than the following) */ |
| 2871 | |
| 2872 | { |
| 2873 | struct GRPSYL *tgs_p; /* as we walk down a chord to try to find |
| 2874 | * the group we're looking for, this keeps |
| 2875 | * track of where we are */ |
| 2876 | |
| 2877 | |
| 2878 | /* skip over any embedded rests--they are not notes. */ |
| 2879 | while (gs_p->grpcont == GC_REST) { |
| 2880 | if (backwards == YES) { |
| 2881 | gs_p = gs_p->prev; |
| 2882 | } |
| 2883 | else { |
| 2884 | gs_p = gs_p->next; |
| 2885 | } |
| 2886 | } |
| 2887 | if (gs_p == 0) { |
| 2888 | pfatal("neighboring_note_beam_group didn't find note group"); |
| 2889 | } |
| 2890 | |
| 2891 | /* If this is a cross-staff beam, we may need to hop from |
| 2892 | * staff to staff sometimes. If this group is a space |
| 2893 | * group, then we have to hop now. */ |
| 2894 | if (gs_p->grpcont == GC_SPACE) { |
| 2895 | if (gs_p->beamto == CS_SAME) { |
| 2896 | do { |
| 2897 | if (backwards == YES) { |
| 2898 | gs_p = gs_p->prev; |
| 2899 | } else { |
| 2900 | gs_p = gs_p->next; |
| 2901 | } |
| 2902 | } while (gs_p != 0 && gs_p->grpcont != GC_NOTES); |
| 2903 | } |
| 2904 | |
| 2905 | else if (gs_p->staffno == first_p->staffno) { |
| 2906 | /* Need to hop to below staff. |
| 2907 | * Go down the chord to find |
| 2908 | * the matching cross-staff beam group */ |
| 2909 | do { |
| 2910 | if ((gs_p = gs_p->gs_p) == |
| 2911 | (struct GRPSYL *) 0) { |
| 2912 | pfatal("can't find matching beam chord"); |
| 2913 | } |
| 2914 | |
| 2915 | /* skip any lyrics and such till we find the |
| 2916 | * group beamed to us */ |
| 2917 | } while (gs_p->beamto != CS_ABOVE); |
| 2918 | } |
| 2919 | else { |
| 2920 | /* Need to jump back to staff above. |
| 2921 | * Since the chord linked list is only one way (down) |
| 2922 | * and we need to look up the chord, this is a |
| 2923 | * little harder. Start at the first_p group, which |
| 2924 | * is the first group in the beam on the above staff. |
| 2925 | * Keep going down that staff until we find a chord |
| 2926 | * linked down to gs_p. */ |
| 2927 | for ( ; first_p != (struct GRPSYL *) 0; |
| 2928 | first_p = first_p->next) { |
| 2929 | |
| 2930 | /* walk down the chord */ |
| 2931 | for (tgs_p = first_p->gs_p; |
| 2932 | tgs_p != (struct GRPSYL *) 0; |
| 2933 | tgs_p = tgs_p->gs_p) { |
| 2934 | |
| 2935 | if (tgs_p == gs_p) { |
| 2936 | /* Aha! We found it! */ |
| 2937 | return(first_p); |
| 2938 | } |
| 2939 | |
| 2940 | if (tgs_p->staffno > gs_p->staffno) { |
| 2941 | /* we're past the staff we care |
| 2942 | * about, so this chord can't |
| 2943 | * be the right one. */ |
| 2944 | break; |
| 2945 | } |
| 2946 | } |
| 2947 | } |
| 2948 | |
| 2949 | pfatal("failed to find group when jumping back to above staff"); |
| 2950 | } |
| 2951 | } |
| 2952 | |
| 2953 | return(gs_p); |
| 2954 | } |
| 2955 | \f |
| 2956 | |
| 2957 | /* given a GRPSYL that deserves a partial beam, return PB_LEFT if the beam |
| 2958 | * goes on the left or PB_RIGHT if is goes on the right. */ |
| 2959 | |
| 2960 | int |
| 2961 | pbeamside(gs_p, first_p) |
| 2962 | |
| 2963 | struct GRPSYL *gs_p; |
| 2964 | struct GRPSYL *first_p; |
| 2965 | |
| 2966 | { |
| 2967 | int side; |
| 2968 | int beams2left, beams2right; /* how many beams or dots for notes on |
| 2969 | * either side of current group */ |
| 2970 | struct GRPSYL *prevgs_p, *nextgs_p; |
| 2971 | |
| 2972 | |
| 2973 | /* need to figure out which side of stem to draw the |
| 2974 | * partial beam. First the easy cases: if is STARTITEM, |
| 2975 | * then it has to go on the right, if ENDITEM, it |
| 2976 | * has to go on the left */ |
| 2977 | switch (gs_p->beamloc) { |
| 2978 | case STARTITEM: |
| 2979 | side = PB_RIGHT; |
| 2980 | break; |
| 2981 | |
| 2982 | case ENDITEM: |
| 2983 | side = PB_LEFT; |
| 2984 | break; |
| 2985 | |
| 2986 | case INITEM: |
| 2987 | /* Hmmm. Will have to be more clever. Check the |
| 2988 | * note on either side. If we're at a breakbeam, |
| 2989 | * it's easy to know. Otherwise, if one should have more |
| 2990 | * beams than the other, put the partial on that |
| 2991 | * side */ |
| 2992 | prevgs_p = prevbmgrp(gs_p, first_p); |
| 2993 | nextgs_p = nxtbmgrp(gs_p, first_p, gs_p->next); |
| 2994 | beams2left = numbeams(prevgs_p->basictime); |
| 2995 | beams2right = numbeams(nextgs_p->basictime); |
| 2996 | if (gs_p->breakbeam == YES) { |
| 2997 | side = PB_LEFT; |
| 2998 | } |
| 2999 | else if (prevgs_p != 0 && prevgs_p->breakbeam == YES) { |
| 3000 | side = PB_RIGHT; |
| 3001 | } |
| 3002 | else if (beams2left > beams2right) { |
| 3003 | side = PB_LEFT; |
| 3004 | } |
| 3005 | else if (beams2right > beams2left) { |
| 3006 | side = PB_RIGHT; |
| 3007 | } |
| 3008 | |
| 3009 | /* That was inconclusive. So now we're going to try to decide |
| 3010 | * based on logical groupings of notes; that is, notes grouped |
| 3011 | * according to what the accents should be. */ |
| 3012 | else if (chkgroupings(&side, gs_p) == YES) { |
| 3013 | /* it found an answer and set "side" for us */ |
| 3014 | ; |
| 3015 | } |
| 3016 | else { |
| 3017 | /* ok. that didn't help. |
| 3018 | * See if the notes on either side |
| 3019 | * have more dots than the other. |
| 3020 | * If so, put the partial towards |
| 3021 | * that one. If they are the same, then |
| 3022 | * throw in the towel and just stick it |
| 3023 | * on the left */ |
| 3024 | beams2left = prevgs_p->dots; |
| 3025 | beams2right = nextgs_p->dots; |
| 3026 | if (beams2right > beams2left) { |
| 3027 | side = PB_RIGHT; |
| 3028 | } |
| 3029 | else { |
| 3030 | side = PB_LEFT; |
| 3031 | } |
| 3032 | } |
| 3033 | break; |
| 3034 | |
| 3035 | default: |
| 3036 | pfatal("invalid beamloc passed to pbeamside"); |
| 3037 | /*NOTREACHED*/ |
| 3038 | return(PB_LEFT); /* to shut up bogus compiler warning */ |
| 3039 | } |
| 3040 | |
| 3041 | return(side); |
| 3042 | } |
| 3043 | \f |
| 3044 | /* |
| 3045 | * Name: chkgroupings() |
| 3046 | * |
| 3047 | * Abstract: Decide partial beam side based on groupings of notes. |
| 3048 | * |
| 3049 | * Returns: YES if it found an answer (stored in *side_p), NO if not |
| 3050 | * |
| 3051 | * Description: This function breaks the measure down into successively |
| 3052 | * smaller pieces based on where the accents should be, trying to |
| 3053 | * find a piece where the current GRPSYL falls at the beginning or |
| 3054 | * end of the piece. If the GRPSYL falls at the start of a piece, |
| 3055 | * its partial beam should point right; if end, left. If we get |
| 3056 | * to the point where the pieces are shorter than the GRPSYL |
| 3057 | * itself, we have failed. |
| 3058 | */ |
| 3059 | |
| 3060 | static int |
| 3061 | chkgroupings(side_p, thisgs_p) |
| 3062 | |
| 3063 | int *side_p; /* where to put the answer, if found */ |
| 3064 | struct GRPSYL *thisgs_p; /* the GRPSYL we are working on */ |
| 3065 | |
| 3066 | { |
| 3067 | struct GRPSYL *gs_p; /* point along GRPSYL list */ |
| 3068 | short *factors; /* array to be malloc'ed */ |
| 3069 | int n; /* loop variable */ |
| 3070 | RATIONAL thisstart; /* time offset in measure of thisgs_p */ |
| 3071 | RATIONAL nextstart; /* time offset in measure of next GRPSYL */ |
| 3072 | RATIONAL quotient; /* temp variable for dividing */ |
| 3073 | RATIONAL grouplen; /* time length of a grouping */ |
| 3074 | RATIONAL tupstart; /* time offset where tuplet starts */ |
| 3075 | RATIONAL tupdur; /* time length of a tuplet */ |
| 3076 | int counts; /* count in the current grouplen */ |
| 3077 | int fraction; /* is grouplen a fraction of a count? */ |
| 3078 | int fact; /* a factor */ |
| 3079 | |
| 3080 | |
| 3081 | /* |
| 3082 | * If we're doing grace beams, skip this whole thing, since we're |
| 3083 | * dealing with time values, and they are all zero. |
| 3084 | */ |
| 3085 | if (thisgs_p->grpvalue == GV_ZERO) { |
| 3086 | return (NO); |
| 3087 | } |
| 3088 | |
| 3089 | /* find the time offset of thisgs_p by adding up all previous GRPSYLs*/ |
| 3090 | thisstart = Zero; |
| 3091 | for (gs_p = thisgs_p->prev; gs_p != 0; gs_p = gs_p->prev) { |
| 3092 | thisstart = radd(thisstart, gs_p->fulltime); |
| 3093 | } |
| 3094 | |
| 3095 | /* find offset of GRPSYL following thisgs_p */ |
| 3096 | nextstart = radd(thisstart, thisgs_p->fulltime); |
| 3097 | |
| 3098 | /* |
| 3099 | * Interior notes of tuplets are dealt with in a special way. |
| 3100 | */ |
| 3101 | if (thisgs_p->tuploc == INITEM) { |
| 3102 | /* |
| 3103 | * Find the duration of the tuplet by adding up all the |
| 3104 | * previous GRPSYLs in the tuplet and this GRPSYL and all the |
| 3105 | * later GRPSYLs. (The loops stop when they hit a NOITEM |
| 3106 | * that's not grace.) |
| 3107 | */ |
| 3108 | tupdur = Zero; |
| 3109 | for (gs_p = thisgs_p->prev; gs_p != 0 && |
| 3110 | (gs_p->grpvalue == GV_ZERO || |
| 3111 | gs_p->tuploc != NOITEM); gs_p = gs_p->prev) { |
| 3112 | tupdur = radd(tupdur, gs_p->fulltime); |
| 3113 | } |
| 3114 | /* remember where tuplet starts */ |
| 3115 | tupstart = rsub(thisstart, tupdur); |
| 3116 | for (gs_p = thisgs_p; gs_p != 0 && |
| 3117 | (gs_p->grpvalue == GV_ZERO || |
| 3118 | gs_p->tuploc != NOITEM); gs_p = gs_p->next) { |
| 3119 | tupdur = radd(tupdur, gs_p->fulltime); |
| 3120 | } |
| 3121 | |
| 3122 | /* |
| 3123 | * If the starting point of this tuplet is not at a multiple of |
| 3124 | * its duration, we consider the tuplet synchopated. This is |
| 3125 | * pretty bizarre and not worth trying to deal with. |
| 3126 | */ |
| 3127 | quotient = rdiv(tupstart, tupdur); |
| 3128 | if (quotient.d != 1) { |
| 3129 | return (NO); |
| 3130 | } |
| 3131 | |
| 3132 | /* the first group length to consider is tupdur/tupcont */ |
| 3133 | grouplen = tupdur; |
| 3134 | grouplen.d *= thisgs_p->tupcont; |
| 3135 | rred(&grouplen); |
| 3136 | |
| 3137 | /* loop until an answer is found, or we give up */ |
| 3138 | for (;;) { |
| 3139 | /* |
| 3140 | * If the group length is not longer than our note, it |
| 3141 | * makes no sense to try to see if our note is at the |
| 3142 | * start or end of such a group. Maybe we never hit a |
| 3143 | * match because our note is syncopated. Whatever the |
| 3144 | * reason, we have to give up. |
| 3145 | */ |
| 3146 | if (LE(grouplen, thisgs_p->fulltime)) { |
| 3147 | return (NO); |
| 3148 | } |
| 3149 | |
| 3150 | /* |
| 3151 | * If thisstart/grouplen is an integer, it means |
| 3152 | * thisgs_p is on a grouping boundary; that is, it is |
| 3153 | * the first GRPSYL in a grouping. So point right. |
| 3154 | */ |
| 3155 | quotient = rdiv(thisstart, grouplen); |
| 3156 | if (quotient.d == 1) { |
| 3157 | *side_p = PB_RIGHT; |
| 3158 | return (YES); |
| 3159 | } |
| 3160 | |
| 3161 | /* |
| 3162 | * If nextstart/grouplen is an integer, it means the |
| 3163 | * GRPSYL after thisgs_p is on a grouping boundary, |
| 3164 | * which means that thisgs_p is the last GRPSYL in a |
| 3165 | * grouping. So point left. |
| 3166 | */ |
| 3167 | quotient = rdiv(nextstart, grouplen); |
| 3168 | if (quotient.d == 1) { |
| 3169 | *side_p = PB_LEFT; |
| 3170 | return (YES); |
| 3171 | } |
| 3172 | |
| 3173 | /* divide grouplen by 2 and try again */ |
| 3174 | grouplen = rdiv(grouplen, Two); |
| 3175 | } |
| 3176 | } |
| 3177 | |
| 3178 | /* |
| 3179 | * This is the normal case, not the interior of a tuplet. |
| 3180 | */ |
| 3181 | |
| 3182 | /* get all the prime factors of the time sig's numerator */ |
| 3183 | factors = factor(Score.timenum); |
| 3184 | |
| 3185 | grouplen = Score.time; /* first group is the whole measure */ |
| 3186 | counts = Score.timenum; /* number of counts in measure */ |
| 3187 | |
| 3188 | /* |
| 3189 | * Loop until we find an answer, or until we have to give up. Each |
| 3190 | * time through the loop, we reduce the grouping length. At first, we |
| 3191 | * divide out prime factors from the number of counts in the measure. |
| 3192 | * Once we get down to one count, we start dividing by 2 all the time. |
| 3193 | */ |
| 3194 | for (;;) { |
| 3195 | fraction = YES; /* default to "fraction of a count" */ |
| 3196 | |
| 3197 | /* if there are still multiple counts, divide out a prime */ |
| 3198 | if (counts > 1) { |
| 3199 | /* |
| 3200 | * See if there are any prime factors greater than 4. |
| 3201 | * This only happens with funny timesigs like 10/8 or |
| 3202 | * 7/4. We work down from the top, because the |
| 3203 | * likelyhood is that the highest level grouping is by |
| 3204 | * the biggest factor, when these funny numbers are |
| 3205 | * involved. At least 10/8, for example, is normally |
| 3206 | * 5 groups of 2, not 2 groups of 5. |
| 3207 | */ |
| 3208 | for (n = Score.timenum; n > 4 && factors[n] == 0; n--) |
| 3209 | ; |
| 3210 | /* if we found a 5 or greater, use it */ |
| 3211 | if (n > 4) { |
| 3212 | factors[n]--; |
| 3213 | fact = n; |
| 3214 | /* |
| 3215 | * There are no funny factors (5 or more) left. Next, |
| 3216 | * we need to look for 2s, not 3s yet, because, for |
| 3217 | * example, 6/8 is 2 groups of 3, not 3 groups of 2. |
| 3218 | */ |
| 3219 | } else if (counts % 2 == 0) { |
| 3220 | factors[2]--; |
| 3221 | fact = 2; |
| 3222 | /* no 2s either, so look for 3s */ |
| 3223 | } else if (counts % 3 == 0) { |
| 3224 | factors[3]--; |
| 3225 | fact = 3; |
| 3226 | } else { |
| 3227 | /* no factors left, so flag it by setting fact to 1 */ |
| 3228 | fact = 1; |
| 3229 | } |
| 3230 | |
| 3231 | /* |
| 3232 | * If a factor was found, divide it out, and remember |
| 3233 | * that we are not yet dealing with fractions of a |
| 3234 | * single count. |
| 3235 | */ |
| 3236 | if (fact > 1) { |
| 3237 | counts /= fact; |
| 3238 | fraction = NO; |
| 3239 | } |
| 3240 | } |
| 3241 | |
| 3242 | if (fraction == YES) { |
| 3243 | /* |
| 3244 | * We are dealing with fractions of a count, so divide |
| 3245 | * by 2 from now on. |
| 3246 | */ |
| 3247 | grouplen = rdiv(grouplen, Two); |
| 3248 | } else { |
| 3249 | /* |
| 3250 | * Using the number of counts remaining, form the |
| 3251 | * length in lowest terms. |
| 3252 | */ |
| 3253 | grouplen.n = counts; |
| 3254 | grouplen.d = Score.timeden; |
| 3255 | rred(&grouplen); |
| 3256 | } |
| 3257 | |
| 3258 | /* |
| 3259 | * If the group length is not longer than our note, it makes no |
| 3260 | * sense to try to see if our note is at the start or end of |
| 3261 | * such a group. Maybe we never hit a match because our note |
| 3262 | * is syncopated. Whatever the reason, we have to give up. |
| 3263 | */ |
| 3264 | if (LE(grouplen, thisgs_p->fulltime)) { |
| 3265 | return (NO); |
| 3266 | } |
| 3267 | |
| 3268 | /* |
| 3269 | * If thisstart/grouplen is an integer, it means thisgs_p is on |
| 3270 | * a grouping boundary; that is, it is the first GRPSYL in a |
| 3271 | * grouping. So point right. |
| 3272 | */ |
| 3273 | quotient = rdiv(thisstart, grouplen); |
| 3274 | if (quotient.d == 1) { |
| 3275 | *side_p = PB_RIGHT; |
| 3276 | return (YES); |
| 3277 | } |
| 3278 | |
| 3279 | /* |
| 3280 | * If nextstart/grouplen is an integer, it means the GRPSYL |
| 3281 | * after thisgs_p is on a grouping boundary, which means that |
| 3282 | * thisgs_p is the last GRPSYL in a grouping. So point left. |
| 3283 | */ |
| 3284 | quotient = rdiv(nextstart, grouplen); |
| 3285 | if (quotient.d == 1) { |
| 3286 | *side_p = PB_LEFT; |
| 3287 | return (YES); |
| 3288 | } |
| 3289 | } |
| 3290 | |
| 3291 | return (NO); /* we can never get here; this is for lint */ |
| 3292 | } |
| 3293 | \f |
| 3294 | |
| 3295 | /* actually draw a beam */ |
| 3296 | |
| 3297 | static void |
| 3298 | do_beam(x1, y1, x2, y2, halfwidth) |
| 3299 | |
| 3300 | double x1, y1; /* start beam here */ |
| 3301 | double x2, y2; /* end beam here */ |
| 3302 | double halfwidth; /* go this far up and down from y1 and y2 to get |
| 3303 | * corners of parallelogram that makes up the beam */ |
| 3304 | |
| 3305 | { |
| 3306 | do_newpath(); |
| 3307 | do_moveto(x1, y1 + halfwidth); |
| 3308 | do_line(x2, y2 + halfwidth); |
| 3309 | do_line(x2, y2 - halfwidth); |
| 3310 | do_line(x1, y1 - halfwidth); |
| 3311 | do_closepath(); |
| 3312 | do_fill(); |
| 3313 | } |
| 3314 | \f |
| 3315 | |
| 3316 | /* print a multirest */ |
| 3317 | |
| 3318 | void |
| 3319 | pr_multirest(gs_p, staff_p) |
| 3320 | |
| 3321 | struct GRPSYL *gs_p; /* info about the multirest */ |
| 3322 | struct STAFF *staff_p; |
| 3323 | |
| 3324 | { |
| 3325 | double x; /* horizontal position of number string */ |
| 3326 | double y, y_offset; /* vertical location */ |
| 3327 | double height, width; /* of meas num string */ |
| 3328 | float east, west; /* edges of the multirest */ |
| 3329 | char *numstr; /* ASCII version of numbers of measures */ |
| 3330 | |
| 3331 | |
| 3332 | /* avoid core dumps */ |
| 3333 | if (Score_location_p == (float *) 0) { |
| 3334 | pfatal("can't do multirest: no feed"); |
| 3335 | return; |
| 3336 | } |
| 3337 | |
| 3338 | /* determine where to place the multirest */ |
| 3339 | y = mr_y_loc(gs_p->staffno); |
| 3340 | east = gs_p->c[AE]; |
| 3341 | west = gs_p->c[AW]; |
| 3342 | |
| 3343 | /* If user wants us to use the alternative multirest style of using |
| 3344 | * rest symbols (often used in orchestral music), and the length of |
| 3345 | * the multirest is 8 or less, we use that alternate style, |
| 3346 | * otherwise draw horizontal line along middle staff and two |
| 3347 | * vertical lines near the bar lines. Note that the basictime is |
| 3348 | * the negative of the number of measures of multirest. |
| 3349 | * We have seen rare examples of using the alternate style for all the |
| 3350 | * way up to 11 measures, but consider normal usage only up to 8. |
| 3351 | */ |
| 3352 | if (svpath(staff_p->staffno, RESTSYMMULT)->restsymmult == YES && |
| 3353 | gs_p->basictime > -9) { |
| 3354 | double center; /* can't use AX, must use avg of AE and AW */ |
| 3355 | int size; /* actually will always be normal size, |
| 3356 | * but may as well make code be able to handle |
| 3357 | * small size just in case... */ |
| 3358 | |
| 3359 | center = (gs_p->c[AE] + gs_p->c[AW]) / 2.0; |
| 3360 | size = (gs_p->grpsize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE); |
| 3361 | |
| 3362 | switch (gs_p->basictime) { |
| 3363 | case -2: |
| 3364 | pr_muschar(center, gs_p->c[AY], C_DWHREST, size, FONT_MUSIC); |
| 3365 | break; |
| 3366 | case -3: |
| 3367 | pr_muschar(gs_p->c[AW], gs_p->c[AY], C_DWHREST, size, FONT_MUSIC); |
| 3368 | pr_muschar(gs_p->c[AE], gs_p->c[AY], C_1REST, size, FONT_MUSIC); |
| 3369 | break; |
| 3370 | case -4: |
| 3371 | pr_muschar(center, gs_p->c[AY], C_QWHREST, size, FONT_MUSIC); |
| 3372 | break; |
| 3373 | case -5: |
| 3374 | pr_muschar(gs_p->c[AW], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC); |
| 3375 | pr_muschar(gs_p->c[AE], gs_p->c[AY], C_1REST, size, FONT_MUSIC); |
| 3376 | break; |
| 3377 | case -6: |
| 3378 | pr_muschar(gs_p->c[AW], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC); |
| 3379 | pr_muschar(gs_p->c[AE], gs_p->c[AY], C_DWHREST, size, FONT_MUSIC); |
| 3380 | break; |
| 3381 | case -7: |
| 3382 | pr_muschar(gs_p->c[AW], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC); |
| 3383 | pr_muschar(center, gs_p->c[AY], C_DWHREST, size, FONT_MUSIC); |
| 3384 | pr_muschar(gs_p->c[AE], gs_p->c[AY], C_1REST, size, FONT_MUSIC); |
| 3385 | break; |
| 3386 | case -8: |
| 3387 | pr_muschar(gs_p->c[AW], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC); |
| 3388 | pr_muschar(gs_p->c[AE], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC); |
| 3389 | break; |
| 3390 | default: |
| 3391 | pfatal("restsymmult with illegal number of measures (%d)", |
| 3392 | -(gs_p->basictime) ); |
| 3393 | break; |
| 3394 | } |
| 3395 | } |
| 3396 | else { |
| 3397 | /* draw vertical lines at each end */ |
| 3398 | do_linetype(L_MEDIUM); |
| 3399 | |
| 3400 | draw_line(west, y - (2.0 * Stepsize), west, y + (2.0 * Stepsize)); |
| 3401 | draw_line(east, y - (2.0 * Stepsize), east, y + (2.0 * Stepsize)); |
| 3402 | |
| 3403 | /* draw heavy horizontal */ |
| 3404 | do_linetype(L_WIDE); |
| 3405 | draw_line(west, y, east, y); |
| 3406 | } |
| 3407 | |
| 3408 | if (svpath(staff_p->staffno, PRINTMULTNUM)->printmultnum == YES) { |
| 3409 | /* print number of measures */ |
| 3410 | numstr = mrnum(staff_p, &x, &y_offset, &height, &width); |
| 3411 | pr_string(x, y + y_offset, numstr, J_LEFT, (char *) 0, -1); |
| 3412 | } |
| 3413 | } |
| 3414 | \f |
| 3415 | |
| 3416 | /* Given a STAFF pointing to a multirest or measure repeat GRPSYL, |
| 3417 | * return a string for its number of measures, |
| 3418 | * and return via pointers its x, relative y, height, and width */ |
| 3419 | |
| 3420 | char * |
| 3421 | mrnum(staff_p, x_p, y_offset_p, height_p, width_p) |
| 3422 | |
| 3423 | struct STAFF *staff_p; |
| 3424 | double *x_p; /* return where number starts horizontally */ |
| 3425 | double *y_offset_p; /* return y relative to staff */ |
| 3426 | double *height_p; /* return height of string */ |
| 3427 | double *width_p; /* return width of string */ |
| 3428 | |
| 3429 | { |
| 3430 | struct GRPSYL *gs_p = 0;/* initialize to avoid compiler warning */ |
| 3431 | char *numstr; /* ASCII version of number of measures */ |
| 3432 | int v; /* voice index */ |
| 3433 | |
| 3434 | /* skip over invisible voices */ |
| 3435 | for (v = 0; v < MAXVOICES; v++) { |
| 3436 | if (vvpath(staff_p->staffno, v + 1, VISIBLE)->visible == YES) { |
| 3437 | gs_p = staff_p->groups_p[v]; |
| 3438 | break; |
| 3439 | } |
| 3440 | } |
| 3441 | if (v == MAXVOICES) { |
| 3442 | pfatal("no visible voice found by mrnum"); |
| 3443 | } |
| 3444 | if (gs_p->grpcont == GC_NOTES) { |
| 3445 | /* this is a measure repeat */ |
| 3446 | numstr = num2str(staff_p->mrptnum); |
| 3447 | numstr[0] = FONT_TR; |
| 3448 | numstr[1] = 11; |
| 3449 | } |
| 3450 | else if (gs_p->grpcont == GC_REST) { |
| 3451 | /* this is a multi-rest */ |
| 3452 | numstr = num2str( -(gs_p->basictime) ); |
| 3453 | /* want these in bigger size */ |
| 3454 | /* Essential Dictionary of Music Notation says this |
| 3455 | * should be in the same size and font as time signature. */ |
| 3456 | numstr[0] = FONT_NB; |
| 3457 | numstr[1] = 16; |
| 3458 | } |
| 3459 | else { |
| 3460 | pfatal("wrong group type (%d) passed to mrnum, line %d, staff %d, voice %d", gs_p->grpcont, gs_p->inputlineno, gs_p->staffno, gs_p->vno); |
| 3461 | /*NOTREACHED*/ |
| 3462 | return (char *) 0; /* to shut up bogus compiler warning */ |
| 3463 | } |
| 3464 | numstr[1] = (char) adj_size((int) numstr[1], Staffscale, (char *) 0, -1); |
| 3465 | |
| 3466 | *width_p = strwidth(numstr); |
| 3467 | *height_p = strheight(numstr); |
| 3468 | |
| 3469 | /* x is middle of measure minus 1/2 of number string width */ |
| 3470 | /* y offset is just above staff */ |
| 3471 | *x_p = ((gs_p->c[AE] + gs_p->c[AW]) / 2.0) - (*width_p / 2.0); |
| 3472 | *y_offset_p = halfstaffhi(gs_p->staffno) + Stepsize; |
| 3473 | return(numstr); |
| 3474 | } |
| 3475 | \f |
| 3476 | |
| 3477 | /* given a number, return pointer to string version (with font/size in first |
| 3478 | * 2 bytes. Points to static area overwritten on each call, so if you need a |
| 3479 | * unique copy of it, use copy_string(). Always makes a string in Roman in |
| 3480 | * the default size. */ |
| 3481 | |
| 3482 | char * |
| 3483 | num2str(num) |
| 3484 | |
| 3485 | int num; /* the number to convert */ |
| 3486 | |
| 3487 | { |
| 3488 | static char numstr[12]; |
| 3489 | |
| 3490 | /* get ASCII version of number */ |
| 3491 | (void) sprintf(numstr, "%c%c%d", FONT_TR, |
| 3492 | adj_size(DFLT_SIZE, Staffscale, (char *) 0, -1), num); |
| 3493 | return(numstr); |
| 3494 | } |
| 3495 | \f |
| 3496 | |
| 3497 | /* print cresc or decresc */ |
| 3498 | |
| 3499 | static void |
| 3500 | pr_cresc(stuff_p) |
| 3501 | |
| 3502 | struct STUFF *stuff_p; /* info about what to print and where */ |
| 3503 | |
| 3504 | { |
| 3505 | float x1, x2; /* x coords of west and east points */ |
| 3506 | float line1y1, line2y1; /* y coords of west points */ |
| 3507 | float line1y2, line2y2; /* y coords of east points */ |
| 3508 | |
| 3509 | |
| 3510 | do_linetype(L_NORMAL); |
| 3511 | |
| 3512 | /* get coords for point and midpoint of open end */ |
| 3513 | x1 = stuff_p->c[AW]; |
| 3514 | x2 = stuff_p->c[AE]; |
| 3515 | if (stuff_p->stuff_type == ST_CRESC) { |
| 3516 | line1y1 = line2y1 = (stuff_p->c[AN] + stuff_p->c[AS]) / 2.0; |
| 3517 | /* adjust by 1 point to allow some vertical padding */ |
| 3518 | line1y2 = stuff_p->c[AN] - (1.0 * Stdpad); |
| 3519 | line2y2 = stuff_p->c[AS] + (1.0 * Stdpad); |
| 3520 | } |
| 3521 | else if (stuff_p->stuff_type == ST_DECRESC) { |
| 3522 | line1y2 = line2y2 = (stuff_p->c[AN] + stuff_p->c[AS]) / 2.0; |
| 3523 | line1y1 = stuff_p->c[AN] - (1.0 * Stdpad); |
| 3524 | line2y1 = stuff_p->c[AS] + (1.0 * Stdpad); |
| 3525 | } |
| 3526 | else { |
| 3527 | pfatal("pr_cres called for something other than cresc/decresc"); |
| 3528 | /*NOTREACHED*/ |
| 3529 | return; /* to shut up bogus compiler warning about unused variables */ |
| 3530 | } |
| 3531 | |
| 3532 | /* draw the two sides of the hairpin */ |
| 3533 | draw_line(x1, line1y1, x2, line1y2); |
| 3534 | draw_line(x1, line2y1, x2, line2y2); |
| 3535 | } |
| 3536 | \f |
| 3537 | |
| 3538 | /* if a STUFF has a til clause, may need to extend out. If a trill, extend |
| 3539 | * with wavy line. If octave, use dashed line. If strings ends with a ~, |
| 3540 | * use a wavy line. If ends with an underscore or is figbass, use |
| 3541 | * underline. For everything else, put out periodic dashed. */ |
| 3542 | |
| 3543 | static void |
| 3544 | extend(stuff_p) |
| 3545 | |
| 3546 | struct STUFF *stuff_p; /* a stuff which may have a til clause */ |
| 3547 | |
| 3548 | { |
| 3549 | float extlen; /* length of extension */ |
| 3550 | float y; /* vertical position */ |
| 3551 | float x; /* horizontal position */ |
| 3552 | float segment; /* length of dash + white space */ |
| 3553 | char *dash; /* dash in proper font/size */ |
| 3554 | char lch; /* last character of string */ |
| 3555 | |
| 3556 | |
| 3557 | if (stuff_p->end.bars <= 0 && stuff_p->end.count <= 0.0) { |
| 3558 | /* no til clause, so nothing to do */ |
| 3559 | return; |
| 3560 | } |
| 3561 | |
| 3562 | /* figure out how much to extend */ |
| 3563 | extlen = stuff_p->c[AE] - stuff_p->c[AW] - strwidth(stuff_p->string); |
| 3564 | y = stuff_p->c[AY]; |
| 3565 | |
| 3566 | if (string_is_sym(stuff_p->string, C_TR, FONT_MUSIC) == YES) { |
| 3567 | /* special case of a trill */ |
| 3568 | if ( extlen < Stepsize) { |
| 3569 | /* too short to bother */ |
| 3570 | return; |
| 3571 | } |
| 3572 | |
| 3573 | y += (2.0 * Stepsize); |
| 3574 | draw_wavy(stuff_p->c[AE] - extlen, y, stuff_p->c[AE], y); |
| 3575 | return; |
| 3576 | } |
| 3577 | |
| 3578 | else if ((lch = last_char(stuff_p->string)) == '~') { |
| 3579 | y += strascent(stuff_p->string) / 2.0; |
| 3580 | draw_wavy(stuff_p->c[AE] - extlen, y, stuff_p->c[AE], y); |
| 3581 | return; |
| 3582 | } |
| 3583 | |
| 3584 | else if (lch == '_' || stuff_p->modifier == TM_FIGBASS) { |
| 3585 | do_linetype(L_NORMAL); |
| 3586 | draw_line(stuff_p->c[AE] - extlen, y, stuff_p->c[AE], y); |
| 3587 | return; |
| 3588 | } |
| 3589 | |
| 3590 | else if (stuff_p->stuff_type == ST_OCTAVE) { |
| 3591 | |
| 3592 | if ( extlen < (4.0 * Stepsize)) { |
| 3593 | /* too short to bother */ |
| 3594 | return; |
| 3595 | } |
| 3596 | |
| 3597 | y += (1.5 * Stepsize); |
| 3598 | do_linetype(L_DASHED); |
| 3599 | draw_line(stuff_p->c[AE] - extlen + (2.0 * Stepsize), y, |
| 3600 | stuff_p->c[AE], y); |
| 3601 | |
| 3602 | /* vertical line at end unless carried to next score */ |
| 3603 | if (stuff_p->carryout == NO) { |
| 3604 | draw_line(stuff_p->c[AE], y, stuff_p->c[AE], |
| 3605 | y + (3.0 * Stepsize * |
| 3606 | (stuff_p->place == PL_ABOVE ? -1.0 : 1.0))); |
| 3607 | } |
| 3608 | |
| 3609 | /* put linetype back to solid so some other music character that |
| 3610 | * uses a line won't get messed up */ |
| 3611 | do_linetype(L_NORMAL); |
| 3612 | return; |
| 3613 | } |
| 3614 | |
| 3615 | dash = dashstr(stuff_p->string); |
| 3616 | segment = (3.0 * strwidth(dash)); |
| 3617 | for ( x = stuff_p->c[AE] - extlen + (2.0 * segment) / 3.0; |
| 3618 | x < stuff_p->c[AE]; x += segment) { |
| 3619 | pr_string(x, y, dash, J_LEFT, (char *) 0, -1); |
| 3620 | } |
| 3621 | FREE(dash); |
| 3622 | } |
| 3623 | \f |
| 3624 | |
| 3625 | /* Some characters have upside-down versions that are used |
| 3626 | * if stem is down. This table maps such characters to their flips versions. |
| 3627 | */ |
| 3628 | |
| 3629 | static struct MIRRCHAR { |
| 3630 | int font; /* Which music font. Note that both the |
| 3631 | * normal and inverted characters must be |
| 3632 | * in the same font. (We could relax |
| 3633 | * this constraint by storing a font for each, |
| 3634 | * and returning both character and font, |
| 3635 | * but there's no hardship in this simple way.) |
| 3636 | */ |
| 3637 | char norm; |
| 3638 | char inverted; |
| 3639 | } mirrtbl[] = { |
| 3640 | { FONT_MUSIC, C_FERM, C_UFERM }, |
| 3641 | { FONT_MUSIC, C_ACC_HAT, C_ACC_UHAT }, |
| 3642 | { FONT_MUSIC, C_WEDGE, C_UWEDGE }, |
| 3643 | { 0, 0 } |
| 3644 | }; |
| 3645 | |
| 3646 | |
| 3647 | /* Given a string and the first character in it, if it is a music symbol |
| 3648 | * that has a mirrored version, return that, otherwise, return |
| 3649 | * it as it was. |
| 3650 | */ |
| 3651 | |
| 3652 | static int |
| 3653 | mirror(str, ch, font) |
| 3654 | |
| 3655 | char *str; /* the string to check */ |
| 3656 | int ch; /* the first character (which better be a music character) */ |
| 3657 | int font; /* FONT_MUSIC or some other music font */ |
| 3658 | |
| 3659 | { |
| 3660 | int i; |
| 3661 | |
| 3662 | for (i = 0; mirrtbl[i].norm != '\0'; i++) { |
| 3663 | if (string_is_sym(str, mirrtbl[i].norm, mirrtbl[i].font) == YES) { |
| 3664 | return((int) mirrtbl[i].inverted); |
| 3665 | } |
| 3666 | } |
| 3667 | return(ch); |
| 3668 | } |