| 1 | /* Copyright (c) 1995, 1996, 1997, 1998, 2000, 2001, 2003, 2004, 2005, 2006 |
| 2 | * by Arkkra Enterprises */ |
| 3 | /* All rights reserved */ |
| 4 | /* |
| 5 | * Name: setnotes.c |
| 6 | * |
| 7 | * Description: This file contains functions for setting relative vertical |
| 8 | * locations of notes. It also sets relative vertical locations |
| 9 | * of the groups that contain notes, considering only the notes. |
| 10 | * It also sets the directions of stems. |
| 11 | */ |
| 12 | |
| 13 | #include "defines.h" |
| 14 | #include "structs.h" |
| 15 | #include "globals.h" |
| 16 | |
| 17 | static void locnotes P((void)); |
| 18 | static void locllnotes P((struct MAINLL *mll_p, int v, |
| 19 | struct MAINLL *nextbar_p)); |
| 20 | static void chktabcollision P((struct GRPSYL *start_p)); |
| 21 | static void intertab P((struct GRPSYL *gs_p, struct MAINLL *mll_p)); |
| 22 | static void setstems P((void)); |
| 23 | static void setonestem P((struct GRPSYL *gs_p)); |
| 24 | static void setopstem P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p)); |
| 25 | static void setfreestem P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p)); |
| 26 | static void set1freestem P((struct GRPSYL *this_p, struct GRPSYL *other_p, |
| 27 | int stemdir)); |
| 28 | static void setbeamedstem P((struct GRPSYL *start_p, int stemdir)); |
| 29 | static void dobunch P((struct GRPSYL *start_p, struct GRPSYL *end_p)); |
| 30 | static void dograce P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p)); |
| 31 | static int setv3stem P((struct GRPSYL *gs_p, int stemdir)); |
| 32 | static int dov3bunch P((struct GRPSYL *start_p, struct GRPSYL *end_p, |
| 33 | int stemdir)); |
| 34 | static void setheads P((void)); |
| 35 | static void setvoiceheads P((struct MAINLL *mll_p, struct GRPSYL *gs_p, |
| 36 | int stafflines, short *shapes, int is_tab, int allx_hsi, |
| 37 | int sharps, int keylet)); |
| 38 | static void fixoneline P((void)); |
| 39 | \f |
| 40 | /* |
| 41 | * Name: setnotes() |
| 42 | * |
| 43 | * Abstract: Sets the relative vert coords of each note, and stem dir. |
| 44 | * |
| 45 | * Returns: void |
| 46 | * |
| 47 | * Description: This function calls subroutines to set the relative vertical |
| 48 | * coordinates of each note and each note group (considering only |
| 49 | * the note heads in it at this point), stem directions, and which |
| 50 | * notehead characters to use. |
| 51 | */ |
| 52 | |
| 53 | void |
| 54 | setnotes() |
| 55 | |
| 56 | { |
| 57 | debug(16, "setnotes"); |
| 58 | |
| 59 | locnotes(); |
| 60 | setstems(); |
| 61 | setheads(); |
| 62 | fixoneline(); |
| 63 | } |
| 64 | \f |
| 65 | /* |
| 66 | * Name: locnotes() |
| 67 | * |
| 68 | * Abstract: Sets the relative vertical coordinates of each note. |
| 69 | * |
| 70 | * Returns: void |
| 71 | * |
| 72 | * Description: This function loops through the main linked list, finding every |
| 73 | * STAFF structure. It calls a subroutine to process each list of |
| 74 | * list of GRPSYLs for groups (not syllables). |
| 75 | */ |
| 76 | |
| 77 | static void |
| 78 | locnotes() |
| 79 | |
| 80 | { |
| 81 | register struct MAINLL *mainll_p; /* point item in main linked list */ |
| 82 | int v; /* index to voice linked lists */ |
| 83 | int did_a_voice; /* have we processed a voice in meas?*/ |
| 84 | struct TIMEDSSV *tssv_p; /* point along a timed SSV list */ |
| 85 | struct MAINLL *nextbar_p; /* the next bar in the MLL */ |
| 86 | |
| 87 | |
| 88 | debug(16, "locnotes"); |
| 89 | initstructs(); /* clean out old SSV info */ |
| 90 | |
| 91 | did_a_voice = NO; /* prevent useless "used before set" warning */ |
| 92 | nextbar_p = 0; /* prevent useless "used before set" warning */ |
| 93 | |
| 94 | /* |
| 95 | * Loop through the main linked list, processing voices. MLL SSVs are |
| 96 | * assigned when encountered. But we also have to handle timed SSVs, |
| 97 | * because they may change the clef. This algorithm would be simpler |
| 98 | * if we called setssvstate() after each voice (to undo the timed |
| 99 | * SSVs), and then always reapplied them when we get to the next bar. |
| 100 | * But to save time, we don't undo them after the last voice, and so |
| 101 | * usually don't have to reassign them at the bar (unless all the |
| 102 | * visible voices had measure repeats, and so we never assigned any). |
| 103 | */ |
| 104 | for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { |
| 105 | |
| 106 | switch (mainll_p->str) { |
| 107 | case S_SSV: |
| 108 | asgnssv(mainll_p->u.ssv_p); |
| 109 | break; |
| 110 | |
| 111 | case S_CHHEAD: |
| 112 | /* find the next bar line */ |
| 113 | for (nextbar_p = mainll_p; nextbar_p->str != S_BAR; |
| 114 | nextbar_p = nextbar_p->next) { |
| 115 | ; |
| 116 | } |
| 117 | /* we haven't processed a voice in this measure yet */ |
| 118 | did_a_voice = NO; |
| 119 | break; |
| 120 | |
| 121 | case S_STAFF: |
| 122 | /* if invisible, there is nothing to do */ |
| 123 | if (mainll_p->u.staff_p->visible == NO) { |
| 124 | break; |
| 125 | } |
| 126 | |
| 127 | /* loop through the voice(s), and process each list */ |
| 128 | for (v = 0; v < MAXVOICES && mainll_p->u.staff_p |
| 129 | ->groups_p[v] != 0; v++) { |
| 130 | |
| 131 | /* meas rpt/rest/space have no notes to do */ |
| 132 | if (mainll_p->u.staff_p->groups_p[v]->is_meas |
| 133 | == YES) { |
| 134 | continue; |
| 135 | } |
| 136 | |
| 137 | /* |
| 138 | * If this is not the first voice we've done in |
| 139 | * this measure, and there are timed SSVs, |
| 140 | * locllnotes() assigned them when we were in |
| 141 | * there for the previous voice we did. So set |
| 142 | * the SSVs back to the state they were in at |
| 143 | * the start of the measure. |
| 144 | */ |
| 145 | if (did_a_voice == YES && nextbar_p->u.bar_p-> |
| 146 | timedssv_p != 0){ |
| 147 | setssvstate(mainll_p); |
| 148 | } |
| 149 | locllnotes(mainll_p, v, nextbar_p); |
| 150 | did_a_voice = YES; |
| 151 | } |
| 152 | break; |
| 153 | |
| 154 | case S_BAR: |
| 155 | if (did_a_voice == NO) { |
| 156 | for (tssv_p = mainll_p->u.bar_p->timedssv_p; |
| 157 | tssv_p != 0; |
| 158 | tssv_p = tssv_p->next) { |
| 159 | asgnssv(&tssv_p->ssv); |
| 160 | } |
| 161 | } |
| 162 | break; |
| 163 | } |
| 164 | } |
| 165 | } |
| 166 | \f |
| 167 | /* |
| 168 | * Name: locllnotes() |
| 169 | * |
| 170 | * Abstract: Set the "stepsup" field for the notes in one GRPSYL list. |
| 171 | * |
| 172 | * Returns: void |
| 173 | * |
| 174 | * Description: This function goes down one of the linked lists of GRPSYLs, |
| 175 | * one that is for groups, not syllables, and sets the stepsup |
| 176 | * value. |
| 177 | */ |
| 178 | |
| 179 | static void |
| 180 | locllnotes(mll_p, v, nextbar_p) |
| 181 | |
| 182 | struct MAINLL *mll_p; /* point at the MLL struct voice hangs off */ |
| 183 | int v; /* voice to loop through */ |
| 184 | struct MAINLL *nextbar_p; /* point at MLL for the next bar line */ |
| 185 | |
| 186 | { |
| 187 | register int upfromc4; /* steps up from middle C */ |
| 188 | register int n; /* loop through all notes in a group */ |
| 189 | int s; /* staff number */ |
| 190 | int slines; /* lines in this staff */ |
| 191 | int clef; /* the clef currently in operation */ |
| 192 | int newclef; /* the new clef, in case it changes */ |
| 193 | struct GRPSYL *gs_p; /* starts pointing at first GRPSYL in list */ |
| 194 | struct TIMEDSSV *tssv_p;/* point along a timed SSV list */ |
| 195 | RATIONAL offset; /* current group's offset into measure */ |
| 196 | |
| 197 | |
| 198 | s = mll_p->u.staff_p->staffno; |
| 199 | debug(32, "locllnotes file=%s line=%d staff=%d vidx=%d", |
| 200 | mll_p->inputfile, mll_p->inputlineno, s, v); |
| 201 | slines = svpath(s, STAFFLINES)->stafflines; |
| 202 | |
| 203 | /* find the initial clef for this staff */ |
| 204 | clef = svpath(s, CLEF)->clef; |
| 205 | |
| 206 | /* point at the first timed SSV for this measure, if there is one */ |
| 207 | tssv_p = nextbar_p->u.bar_p->timedssv_p; |
| 208 | offset = Zero; /* first group's offset into measure */ |
| 209 | |
| 210 | /* loop through every group in this voice */ |
| 211 | for (gs_p = mll_p->u.staff_p->groups_p[v]; gs_p != 0; |
| 212 | gs_p = gs_p->next) { |
| 213 | |
| 214 | /* if no timed SSVs, don't waste time doing the following */ |
| 215 | if (tssv_p != 0) { |
| 216 | /* assign timed SSVs before current offset */ |
| 217 | while (tssv_p != 0 && LT(tssv_p->time_off, offset)) { |
| 218 | asgnssv(&tssv_p->ssv); |
| 219 | tssv_p = tssv_p->next; |
| 220 | } |
| 221 | |
| 222 | /* get clef state just before this group */ |
| 223 | clef = svpath(s, CLEF)->clef; |
| 224 | |
| 225 | /* assign timed SSVs at current offset */ |
| 226 | while (tssv_p != 0 && EQ(tssv_p->time_off, offset)) { |
| 227 | asgnssv(&tssv_p->ssv); |
| 228 | tssv_p = tssv_p->next; |
| 229 | } |
| 230 | |
| 231 | /* get clef for this group */ |
| 232 | newclef = svpath(s, CLEF)->clef; |
| 233 | |
| 234 | /* |
| 235 | * If the clef changed at this time, set it in GRPSYL. |
| 236 | * This could happen with multiple voices on the staff. |
| 237 | * If so, we'll later erase clef from all but one; but |
| 238 | * the choice depends on the coords, which we don't |
| 239 | * know yet, so that is done later. The erasing is |
| 240 | * done in eraseclef() in restsyl.c. |
| 241 | */ |
| 242 | if (newclef != clef) { |
| 243 | clef = newclef; |
| 244 | gs_p->clef = clef; |
| 245 | } |
| 246 | |
| 247 | /* add our group's dur to get ready for next iteration*/ |
| 248 | offset = radd(offset, gs_p->fulltime); |
| 249 | } |
| 250 | |
| 251 | /* nothing more to do for rests or spaces */ |
| 252 | if (gs_p->grpcont != GC_NOTES) |
| 253 | continue; |
| 254 | |
| 255 | /* |
| 256 | * We found a group consisting of notes, normal or tablature. |
| 257 | * First handle the tablature case. |
| 258 | */ |
| 259 | if (clef == TABCLEF) { |
| 260 | /* |
| 261 | * Make sure this voice's notes don't collide with |
| 262 | * some later voice's notes. |
| 263 | */ |
| 264 | chktabcollision(gs_p); |
| 265 | |
| 266 | for (n = 0; n < gs_p->nnotes; n++) { |
| 267 | /* |
| 268 | * Set stepsup to be on the appropriate string. |
| 269 | */ |
| 270 | /* calc steps up from middle of staff */ |
| 271 | gs_p->notelist[n].stepsup = slines |
| 272 | - 1 - 2 * gs_p->notelist[n].STRINGNO; |
| 273 | |
| 274 | } |
| 275 | |
| 276 | continue; |
| 277 | } |
| 278 | |
| 279 | /* |
| 280 | * We found a non-tablature group consisting of notes. For |
| 281 | * each note, find the number of steps it is up from middle C |
| 282 | * and from the center line of the staff. (However, for 1-line |
| 283 | * staffs, we assume center line for now.) |
| 284 | * For CSS notes, we apply an offset to keep it far from the |
| 285 | * normal notes. This is so that setgrps.c will understand |
| 286 | * that CSS and non-CSS notes in a group never interfere. |
| 287 | * Later, absvert.c will find the true stepsup on the other |
| 288 | * staff. |
| 289 | */ |
| 290 | for (n = 0; n < gs_p->nnotes; n++) { |
| 291 | /* set steps up from middle line of staff */ |
| 292 | if (slines == 5) { |
| 293 | /* get steps up from middle C */ |
| 294 | upfromc4 = (gs_p->notelist[n].octave - 4) * 7 + |
| 295 | Letshift[ gs_p->notelist[n].letter - 'a' ]; |
| 296 | |
| 297 | gs_p->notelist[n].stepsup = upfromc4 |
| 298 | + clef - ALTO; |
| 299 | if (gs_p->stemto == CS_ABOVE && |
| 300 | n <= gs_p->stemto_idx) { |
| 301 | gs_p->notelist[n].stepsup += CSS_STEPS; |
| 302 | } else if (gs_p->stemto == CS_BELOW && |
| 303 | n >= gs_p->stemto_idx) { |
| 304 | gs_p->notelist[n].stepsup -= CSS_STEPS; |
| 305 | } |
| 306 | } else { |
| 307 | /* 1-line staff; assume center line for now */ |
| 308 | gs_p->notelist[n].stepsup = 0; |
| 309 | } |
| 310 | } |
| 311 | } |
| 312 | |
| 313 | /* |
| 314 | * Assign any timed SSVs that came after the last group, so that we are |
| 315 | * in the right state for the next measure (if we are the last voice). |
| 316 | */ |
| 317 | while (tssv_p != 0) { |
| 318 | asgnssv(&tssv_p->ssv); |
| 319 | tssv_p = tssv_p->next; |
| 320 | } |
| 321 | } |
| 322 | \f |
| 323 | /* |
| 324 | * Name: chktabcollision() |
| 325 | * |
| 326 | * Abstract: Error if this GRPSYL conflicts with others on this staff. |
| 327 | * |
| 328 | * Returns: void |
| 329 | * |
| 330 | * Description: This function checks for collisions between notes in this |
| 331 | * GRPSYL and notes in GRPSYLs of later voices in this chord on |
| 332 | * this staff. On a tab staff, no two voices are allowed to use |
| 333 | * the same string at the same time. If the frets are different, |
| 334 | * it would be impossible to play, and it seems best to disallow |
| 335 | * it even if they agreed. So if this happens, do an l_ufatal. |
| 336 | */ |
| 337 | |
| 338 | static void |
| 339 | chktabcollision(start_p) |
| 340 | |
| 341 | struct GRPSYL *start_p; /* first voice on this staff in this chord */ |
| 342 | |
| 343 | { |
| 344 | int sv[MAXTABLINES]; /* which voice, if any, is using each string */ |
| 345 | int sidx; /* string index, starting at 0 */ |
| 346 | struct GRPSYL *gs_p; /* a GRPSYL on this staff in this chord */ |
| 347 | int n; /* loop through notes (frets) in GRPSYL */ |
| 348 | |
| 349 | |
| 350 | /* if this chord has no more voices on this staff, return */ |
| 351 | if (start_p->gs_p == 0 || |
| 352 | start_p->gs_p->grpsyl == GS_SYLLABLE || |
| 353 | start_p->gs_p->staffno != start_p->staffno) |
| 354 | return; |
| 355 | |
| 356 | /* we care only about notes (frets); rests and spaces are invisible */ |
| 357 | if (start_p->grpcont != GC_NOTES) |
| 358 | return; |
| 359 | |
| 360 | /* init each string to an invalid voice number */ |
| 361 | for (sidx = 0; sidx < MAXTABLINES; sidx++) { |
| 362 | sv[sidx] = 0; |
| 363 | } |
| 364 | |
| 365 | /* |
| 366 | * Loop from this voice through the last voice on this staff that has |
| 367 | * a GRPSYL in this chord. Don't worry about preceding voices; they |
| 368 | * already were in here and were checked against all these voices. |
| 369 | */ |
| 370 | for (gs_p = start_p; gs_p != 0 && start_p->gs_p->grpsyl == GS_GROUP && |
| 371 | gs_p->staffno == start_p->staffno; gs_p = gs_p->gs_p) { |
| 372 | |
| 373 | /* put each note into array, checking if string already used */ |
| 374 | for (n = 0; n < gs_p->nnotes; n++) { |
| 375 | |
| 376 | if (sv[(int)gs_p->notelist[n].STRINGNO] != 0) { |
| 377 | |
| 378 | l_ufatal(start_p->inputfile, |
| 379 | start_p->inputlineno, |
| 380 | "voices %d and %d on staff %d are using the \"%s\" string at the same time", |
| 381 | sv[(int)gs_p->notelist[n].STRINGNO], |
| 382 | gs_p->vno, |
| 383 | gs_p->staffno, |
| 384 | stringname(gs_p->notelist[n].STRINGNO, |
| 385 | gs_p->staffno)); |
| 386 | } |
| 387 | |
| 388 | sv[(int)gs_p->notelist[n].STRINGNO] = gs_p->vno; |
| 389 | } |
| 390 | } |
| 391 | } |
| 392 | \f |
| 393 | /* |
| 394 | * Name: intertab() |
| 395 | * |
| 396 | * Abstract: Do additional work between tablature groups. |
| 397 | * |
| 398 | * Returns: void |
| 399 | * |
| 400 | * Description: This function does checks to prevent certain bend sequences |
| 401 | * on a tab staff. (It's unclear how such things would be drawn.) |
| 402 | * Also, when it finds the end of a single consecutive bend, it |
| 403 | * alters the previously set northern group boundaries of the |
| 404 | * groups, so that the arrows pointing at the bend strings will go |
| 405 | * up and down appropriately. |
| 406 | * |
| 407 | * This function is called only with groups that have real bends |
| 408 | * (regular or prebends). |
| 409 | */ |
| 410 | #define MAXBDIST 20 /* no. of unique bend distances in a sequence*/ |
| 411 | |
| 412 | static void |
| 413 | intertab(gs_p, mll_p) |
| 414 | |
| 415 | struct GRPSYL *gs_p; /* point at current tablature group */ |
| 416 | struct MAINLL *mll_p; /* point at the main LL struct it hangs off */ |
| 417 | |
| 418 | { |
| 419 | RATIONAL bdist[MAXBDIST]; /* array of bend distances */ |
| 420 | RATIONAL bd; /* a bend distance */ |
| 421 | int bdidx; /* index into table of bend distances*/ |
| 422 | struct GRPSYL *nextgs_p; /* point at the next GRPSYL */ |
| 423 | struct GRPSYL *gs2_p; /* point at earlier GRPSYLs */ |
| 424 | struct MAINLL *mll2_p; /* needed for crossing bar lines */ |
| 425 | int count, count2; /* count numbers of bends */ |
| 426 | int n, k, j; /* loop variables */ |
| 427 | int idx; /* index into a notelist */ |
| 428 | int bad; /* was a bad thing found? */ |
| 429 | |
| 430 | |
| 431 | /* count how many nonnull bends end at this group, remember last one */ |
| 432 | count = 0; |
| 433 | idx = 0; /* prevent useless 'used before set' warning */ |
| 434 | for (n = 0; n < gs_p->nnotes; n++) { |
| 435 | if (HASREALBEND(gs_p->notelist[n])) { |
| 436 | count++; |
| 437 | idx = n; /* remember where bend is */ |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | /* enforce restrictions on the following group, if there is one */ |
| 442 | mll2_p = mll_p; /* we don't want to disturb mll_p */ |
| 443 | nextgs_p = nextgrpsyl(gs_p, &mll2_p); |
| 444 | count2 = 0; /* how many nonnull nonprebend bends are in *nextgs_p */ |
| 445 | |
| 446 | if (nextgs_p != 0 && nextgs_p->grpcont == GC_NOTES) { |
| 447 | |
| 448 | bad = NO; /* init to "nothing is bad" */ |
| 449 | |
| 450 | for (n = 0; n < nextgs_p->nnotes; n++) { |
| 451 | |
| 452 | /* if this note has a nonnull nonprebend bend */ |
| 453 | if (HASREALBEND(nextgs_p->notelist[n]) && |
| 454 | nextgs_p->notelist[n].FRETNO == NOFRET) { |
| 455 | |
| 456 | count2++; |
| 457 | if (count > 1) { |
| 458 | l_ufatal(gs_p->inputfile, |
| 459 | gs_p->inputlineno, |
| 460 | "no bend (other than a release) is allowed to follow a multiple string bend"); |
| 461 | } |
| 462 | |
| 463 | if (count2 > 1) { |
| 464 | l_ufatal(gs_p->inputfile, |
| 465 | gs_p->inputlineno, |
| 466 | "only single string bends are allowed to be consecutive"); |
| 467 | } |
| 468 | |
| 469 | if (nextgs_p->notelist[n].STRINGNO != |
| 470 | gs_p->notelist[idx].STRINGNO) { |
| 471 | bad = YES; |
| 472 | } |
| 473 | } |
| 474 | } |
| 475 | /* |
| 476 | * We check "bad" here instead of inside the above loop, |
| 477 | * because we want to give priority to the "only single string |
| 478 | * bends . . ." message if that condition is happening. |
| 479 | */ |
| 480 | if (bad == YES) { |
| 481 | l_ufatal(gs_p->inputfile, gs_p->inputlineno, |
| 482 | "consecutive bends must be on the same string"); |
| 483 | } |
| 484 | } |
| 485 | |
| 486 | /* |
| 487 | * We know the current group has bend(s). If the following group has |
| 488 | * a nonnull nonprebend bend, just return now. We will handle this |
| 489 | * bend sequence when we find the last nonnull bend in it. |
| 490 | */ |
| 491 | if (count2 > 0) |
| 492 | return; |
| 493 | |
| 494 | /* |
| 495 | * Loop backwards through the sequence of bends. The start should be |
| 496 | * either a bend following no nonnull bend, or a prebend. While |
| 497 | * searching, build a table of all the unique bend distances. Usually |
| 498 | * we break out by finding a group with a nonnull bend, which means we |
| 499 | * went one too far with gs2_p, and gs_p is the start of the sequence. |
| 500 | * But if we hit the start, gs2_p will become 0 and we get out of the |
| 501 | * loop naturally. Again, gs_p is the start of the sequence. |
| 502 | */ |
| 503 | bdidx = 0; /* number of unique bend distances found */ |
| 504 | gs2_p = gs_p; |
| 505 | while (gs2_p != 0) { |
| 506 | /* find which note, if any, has the bend in this group */ |
| 507 | for (n = 0; n < gs2_p->nnotes; n++) { |
| 508 | if (HASREALBEND(gs2_p->notelist[n])) { |
| 509 | bd = ratbend(&gs2_p->notelist[n]); |
| 510 | break; |
| 511 | } |
| 512 | } |
| 513 | |
| 514 | if (n < gs2_p->nnotes) { |
| 515 | /* |
| 516 | * We found a nonnull bend. Search the bdist array to |
| 517 | * see if this value has already occurred. Get out |
| 518 | * when the value is found, or when we find a greater |
| 519 | * value (the list is in ascending order). |
| 520 | */ |
| 521 | for (k = 0; k < bdidx; k++) { |
| 522 | if (GE(bdist[k], bd)) |
| 523 | break; |
| 524 | } |
| 525 | if (k == bdidx) { |
| 526 | /* bd > everything in the array */ |
| 527 | /* add it at the end */ |
| 528 | bdist[k] = bd; |
| 529 | bdidx++; |
| 530 | } else if (GT(bdist[k], bd)) { |
| 531 | /* bd should be put at this position */ |
| 532 | /* move all later ones down a notch */ |
| 533 | for (j = bdidx - 1; j >= k; j--) |
| 534 | bdist[j+1] = bdist[j]; |
| 535 | bdist[k] = bd; |
| 536 | bdidx++; |
| 537 | } |
| 538 | /* else bd is already in the table */ |
| 539 | |
| 540 | if (bdidx >= MAXBDIST) |
| 541 | pfatal("too many unique bend distances in sequence of bends"); |
| 542 | /* if this bend was a prebend, break */ |
| 543 | if (gs2_p->notelist[n].FRETNO != NOFRET) { |
| 544 | gs_p = gs2_p; /* series starts at prebend */ |
| 545 | break; |
| 546 | } |
| 547 | } else { |
| 548 | /* there was no bend; start at the following group; */ |
| 549 | /* gs_p is now the beginning of the sequence */ |
| 550 | break; |
| 551 | } |
| 552 | |
| 553 | /* |
| 554 | * It was a nonprebend bend. Point gs2_p to the preceding |
| 555 | * group, remember the one we just looked at in gs_p, and keep |
| 556 | * looping. |
| 557 | */ |
| 558 | gs_p = gs2_p; |
| 559 | gs2_p = prevgrpsyl(gs2_p, &mll_p); |
| 560 | } |
| 561 | |
| 562 | /* |
| 563 | * Loop forward through these groups. For each one, alter its northern |
| 564 | * boundary according to where its bend distance occurs in the bdist |
| 565 | * table. This will cause the print phase to print the bend strings |
| 566 | * at varying heights so that the arrows will bend up and down as |
| 567 | * appropriate. |
| 568 | */ |
| 569 | while (gs_p != nextgs_p && gs_p != 0) { |
| 570 | /* find which note has the bend in this group, get distance */ |
| 571 | for (n = 0; n < gs_p->nnotes; n++) { |
| 572 | if (HASREALBEND(gs_p->notelist[n])) { |
| 573 | bd = ratbend(&gs_p->notelist[n]); |
| 574 | break; |
| 575 | } |
| 576 | } |
| 577 | /* find distance in table, raise RN proportionally to index */ |
| 578 | for (n = 0; n < bdidx; n++) { |
| 579 | if (EQ(bdist[n], bd)) { |
| 580 | gs_p->c[RN] += 3.0 * STEPSIZE * TABRATIO * n; |
| 581 | break; |
| 582 | } |
| 583 | } |
| 584 | |
| 585 | gs_p = nextgrpsyl(gs_p, &mll_p); |
| 586 | } |
| 587 | } |
| 588 | \f |
| 589 | /* |
| 590 | * Name: setstems() |
| 591 | * |
| 592 | * Abstract: Sets stem direction for each group. |
| 593 | * |
| 594 | * Returns: void |
| 595 | * |
| 596 | * Description: This function sets the stem direction for each group, based |
| 597 | * on the voice scheme at the time and other factors. |
| 598 | */ |
| 599 | |
| 600 | static void |
| 601 | setstems() |
| 602 | |
| 603 | { |
| 604 | /* remember the previous stem direction of voice 3 on each staff */ |
| 605 | short v3stemdir[MAXSTAFFS + 1]; |
| 606 | |
| 607 | int staffno; /* staff number */ |
| 608 | int n; /* loop variable */ |
| 609 | register struct MAINLL *mainll_p; /* point at main linked list item */ |
| 610 | int vscheme; /* current voice scheme */ |
| 611 | |
| 612 | |
| 613 | debug(16, "setstems"); |
| 614 | initstructs(); /* clean out old SSV info */ |
| 615 | |
| 616 | /* set initial default direction of voice 3 stems to be UP */ |
| 617 | for (n = 1; n <= MAXSTAFFS; n++) |
| 618 | v3stemdir[n] = UP; |
| 619 | |
| 620 | /* |
| 621 | * Loop once for each item in the main linked list. Apply any SSVs |
| 622 | * that are found. Call subroutines to process linked lists of |
| 623 | * groups. |
| 624 | */ |
| 625 | for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { |
| 626 | if (mainll_p->str == S_SSV) { |
| 627 | |
| 628 | asgnssv(mainll_p->u.ssv_p); |
| 629 | |
| 630 | } else if (mainll_p->str == S_STAFF && |
| 631 | mainll_p->u.staff_p->visible == YES && |
| 632 | ! is_mrpt(mainll_p->u.staff_p->groups_p[0])) { |
| 633 | /* |
| 634 | * We've found a visible staff, which will have one |
| 635 | * or more voices, depending on the voice scheme. |
| 636 | */ |
| 637 | staffno = mainll_p->u.staff_p->staffno; |
| 638 | vscheme = svpath(staffno, VSCHEME)->vscheme; |
| 639 | |
| 640 | switch (vscheme) { |
| 641 | case V_1: |
| 642 | /* |
| 643 | * There's only one voice on this staff, so |
| 644 | * call a routine to decide which way to point |
| 645 | * each stem. It handles both grace & nongrace. |
| 646 | */ |
| 647 | setonestem(mainll_p->u.staff_p->groups_p[0]); |
| 648 | break; |
| 649 | |
| 650 | case V_2OPSTEM: |
| 651 | /* |
| 652 | * There are two voices on this staff, and |
| 653 | * the stems are always supposed to point |
| 654 | * opposite. Call a routine to mark their |
| 655 | * stem directions. It handles both nongrace |
| 656 | * and grace. |
| 657 | */ |
| 658 | setopstem(mainll_p->u.staff_p->groups_p[0], |
| 659 | mainll_p->u.staff_p->groups_p[1]); |
| 660 | break; |
| 661 | |
| 662 | case V_2FREESTEM: |
| 663 | /* |
| 664 | * There are two voices on this staff, and |
| 665 | * the stems are free to point either way |
| 666 | * when one voice is a space. Call routines |
| 667 | * to mark their stem directions; first |
| 668 | * nongrace, then grace. |
| 669 | */ |
| 670 | setfreestem(mainll_p->u.staff_p->groups_p[0], |
| 671 | mainll_p->u.staff_p->groups_p[1]); |
| 672 | dograce(mainll_p->u.staff_p->groups_p[0], |
| 673 | mainll_p->u.staff_p->groups_p[1]); |
| 674 | |
| 675 | break; |
| 676 | |
| 677 | case V_3OPSTEM: |
| 678 | /* |
| 679 | * This is just like V_2OPSTEM for the first |
| 680 | * two voices, but also allows a voice 3. |
| 681 | */ |
| 682 | setopstem(mainll_p->u.staff_p->groups_p[0], |
| 683 | mainll_p->u.staff_p->groups_p[1]); |
| 684 | v3stemdir[staffno] = setv3stem( |
| 685 | mainll_p->u.staff_p->groups_p[2], |
| 686 | v3stemdir[staffno]); |
| 687 | break; |
| 688 | |
| 689 | case V_3FREESTEM: |
| 690 | /* |
| 691 | * This is just like V_2FREESTEM for the first |
| 692 | * two voices, but also allows a voice 3. |
| 693 | */ |
| 694 | setfreestem(mainll_p->u.staff_p->groups_p[0], |
| 695 | mainll_p->u.staff_p->groups_p[1]); |
| 696 | dograce(mainll_p->u.staff_p->groups_p[0], |
| 697 | mainll_p->u.staff_p->groups_p[1]); |
| 698 | v3stemdir[staffno] = setv3stem( |
| 699 | mainll_p->u.staff_p->groups_p[2], |
| 700 | v3stemdir[staffno]); |
| 701 | |
| 702 | break; |
| 703 | } |
| 704 | } |
| 705 | } |
| 706 | } |
| 707 | \f |
| 708 | /* |
| 709 | * Name: setonestem() |
| 710 | * |
| 711 | * Abstract: Sets stem direction for each group in a linked list for V_1. |
| 712 | * |
| 713 | * Returns: void |
| 714 | * |
| 715 | * Description: This function sets the stem direction for each group in a |
| 716 | * linked list for a voice/measure, for the case where there is |
| 717 | * only one voice on the staff. |
| 718 | */ |
| 719 | |
| 720 | static void |
| 721 | setonestem(gs_p) |
| 722 | |
| 723 | struct GRPSYL *gs_p; /* starts pointing at the first GRPSYL in a list */ |
| 724 | |
| 725 | { |
| 726 | register struct GRPSYL *start_p, *end_p; /* first and last of a set */ |
| 727 | |
| 728 | |
| 729 | debug(32, "setonestem file=%s line=%d", gs_p->inputfile, |
| 730 | gs_p->inputlineno); |
| 731 | /* |
| 732 | * Loop once for each bunch of groups that must be stemmed the same |
| 733 | * way. A beamed group must all be stemmed the same way, but nonbeamed |
| 734 | * notes are independent. |
| 735 | */ |
| 736 | start_p = gs_p; |
| 737 | for (;;) { |
| 738 | /* |
| 739 | * Find next group that has nongrace notes. While doing this, |
| 740 | * set the stemdir for any grace groups encountered. For V_1, |
| 741 | * grace stems always go up. |
| 742 | */ |
| 743 | while (start_p != 0 && (start_p->grpcont != GC_NOTES || |
| 744 | start_p->grpvalue == GV_ZERO)) { |
| 745 | if (start_p->grpcont == GC_NOTES) /* must be grace */ |
| 746 | start_p->stemdir = UP; |
| 747 | start_p = start_p->next; |
| 748 | } |
| 749 | if (start_p == 0) /* get out if no more this measure */ |
| 750 | break; |
| 751 | |
| 752 | /* if this group is not beamed, handle it, and point at next */ |
| 753 | if (start_p->beamloc == NOITEM) { |
| 754 | dobunch(start_p, start_p->next); |
| 755 | start_p = start_p->next; |
| 756 | continue; |
| 757 | } |
| 758 | |
| 759 | /* |
| 760 | * Find end of this beamed group, setting grace groups UP. If |
| 761 | * this is a cross staff beamed group, we may be starting at an |
| 762 | * INITEM or even the ENDITEM, since on this staff STARTITEM |
| 763 | * may have been a space. But that doesn't matter; we still |
| 764 | * look for ENDITEM, whether or not it's also a space; and |
| 765 | * dobunch handles these cases. |
| 766 | */ |
| 767 | for (end_p = start_p; end_p != 0 && |
| 768 | (end_p->grpvalue == GV_ZERO || end_p->beamloc != ENDITEM); |
| 769 | end_p = end_p->next) { |
| 770 | if (end_p->grpvalue == GV_ZERO) |
| 771 | end_p->stemdir = UP; |
| 772 | } |
| 773 | if (end_p == 0) |
| 774 | pfatal("beamed group is not terminated"); |
| 775 | |
| 776 | /* handle this bunch of groups, and point at next */ |
| 777 | dobunch(start_p, end_p->next); |
| 778 | start_p = end_p->next; |
| 779 | } |
| 780 | } |
| 781 | \f |
| 782 | /* |
| 783 | * Name: setopstem() |
| 784 | * |
| 785 | * Abstract: Sets stemdir for v1 or v2 groups for V_2OPSTEM/V_3OPSTEM. |
| 786 | * |
| 787 | * Returns: void |
| 788 | * |
| 789 | * Description: This function sets the stem direction for each group in |
| 790 | * 2 linked lists for a staff/measure, for the case where |
| 791 | * the linked list is for voice 1 or voice 2 and stems are always |
| 792 | * supposed to be opposed. This function does both grace and |
| 793 | * nongrace groups. For this vscheme, they act the same. |
| 794 | * The user can force the stems against the normal direction, |
| 795 | * except that the parse phase blocks any forcing of grace groups. |
| 796 | */ |
| 797 | |
| 798 | static void |
| 799 | setopstem(gs1_p, gs2_p) |
| 800 | |
| 801 | register struct GRPSYL *gs1_p; /* starts at first GRPSYL in voice 1 list */ |
| 802 | register struct GRPSYL *gs2_p; /* starts at first GRPSYL in voice 2 list */ |
| 803 | |
| 804 | { |
| 805 | debug(32, "setopstem file=%s line=%d", gs1_p->inputfile, |
| 806 | gs1_p->inputlineno); |
| 807 | /* mark first voice's stems up */ |
| 808 | while (gs1_p != 0) { |
| 809 | /* if notes or starttime (needed for CSB), mark direction */ |
| 810 | if (gs1_p->grpcont == GC_NOTES || gs1_p->beamloc == STARTITEM) { |
| 811 | /* if grace, or not in beamed group, try to set UP */ |
| 812 | if (gs1_p->grpvalue == GV_ZERO || |
| 813 | gs1_p->beamloc == NOITEM) { |
| 814 | /* if not forced by user, set UP */ |
| 815 | if (gs1_p->stemdir == UNKNOWN) { |
| 816 | gs1_p->stemdir = UP; |
| 817 | } |
| 818 | } else if (gs1_p->beamloc == STARTITEM) { |
| 819 | /* do same for all groups in beamed set */ |
| 820 | setbeamedstem(gs1_p, UP); |
| 821 | } |
| 822 | } |
| 823 | gs1_p = gs1_p->next; |
| 824 | } |
| 825 | |
| 826 | /* mark second voice's stems down */ |
| 827 | while (gs2_p != 0) { |
| 828 | /* if notes or starttime (needed for CSB), mark direction */ |
| 829 | if (gs2_p->grpcont == GC_NOTES || gs2_p->beamloc == STARTITEM) { |
| 830 | /* if grace, or not in beamed group, try to set DOWN */ |
| 831 | if (gs2_p->grpvalue == GV_ZERO || |
| 832 | gs2_p->beamloc == NOITEM) { |
| 833 | /* if not forced by user, set DOWN */ |
| 834 | if (gs2_p->stemdir == UNKNOWN) { |
| 835 | gs2_p->stemdir = DOWN; |
| 836 | } |
| 837 | } else if (gs2_p->beamloc == STARTITEM) { |
| 838 | /* do same for all groups in beamed set */ |
| 839 | setbeamedstem(gs2_p, DOWN); |
| 840 | } |
| 841 | } |
| 842 | gs2_p = gs2_p->next; |
| 843 | } |
| 844 | } |
| 845 | \f |
| 846 | /* |
| 847 | * Name: setfreestem() |
| 848 | * |
| 849 | * Abstract: Sets stemdir for each group in 2 linked lists for V_2FREESTEM. |
| 850 | * |
| 851 | * Returns: void |
| 852 | * |
| 853 | * Description: This function sets the stem direction for each (nongrace) |
| 854 | * group in 2 linked lists for a staff/measure, for the case |
| 855 | * where there are two voices on the staff and the stems are |
| 856 | * allowed to point either way for one voice when the other |
| 857 | * voice has a space. |
| 858 | */ |
| 859 | |
| 860 | static void |
| 861 | setfreestem(gs1_p, gs2_p) |
| 862 | |
| 863 | struct GRPSYL *gs1_p; /* starts pointing at first GRPSYL in voice 1 list */ |
| 864 | struct GRPSYL *gs2_p; /* starts pointing at first GRPSYL in voice 2 list */ |
| 865 | |
| 866 | { |
| 867 | debug(32, "setfreestem file=%s line=%d", gs1_p->inputfile, |
| 868 | gs1_p->inputlineno); |
| 869 | /* call to handle first voice, then call to handle second voice */ |
| 870 | set1freestem(gs1_p, gs2_p, UP); |
| 871 | set1freestem(gs2_p, gs1_p, DOWN); |
| 872 | } |
| 873 | \f |
| 874 | /* |
| 875 | * Name: set1freestem() |
| 876 | * |
| 877 | * Abstract: Sets stemdir for v1 or v2 groups for V_2FREESTEM/V_3FREESTEM. |
| 878 | * |
| 879 | * Returns: void |
| 880 | * |
| 881 | * Description: This function sets the stem direction for each (nongrace) |
| 882 | * group in one linked list for a staff/measure, for the case |
| 883 | * where the linked list is for voice 1 or voice 2 and stems are |
| 884 | * allowed to point either way for one voice when the other |
| 885 | * voice has a space. The function sets the directions just |
| 886 | * for "this" voice; the other voice is only used as a reference |
| 887 | * (we need to check when it has spaces). |
| 888 | */ |
| 889 | |
| 890 | static void |
| 891 | set1freestem(this_p, other_p, stemdir) |
| 892 | |
| 893 | struct GRPSYL *this_p; /* starts pointing at first GRPSYL in linked list */ |
| 894 | /* for the voice whose stems we are now setting */ |
| 895 | struct GRPSYL *other_p; /* starts pointing at first GRPSYL in linked list */ |
| 896 | /* for the other voice */ |
| 897 | int stemdir; /* which way the stem must point if forced */ |
| 898 | |
| 899 | { |
| 900 | register struct GRPSYL *start_p, *end_p; /* first and last of a set */ |
| 901 | RATIONAL vtime, vtime2; /* elapsed time this measure */ |
| 902 | |
| 903 | |
| 904 | debug(32, "set1freestem file=%s line=%d stemdir=%d", this_p->inputfile, |
| 905 | this_p->inputlineno, stemdir); |
| 906 | vtime = Zero; /* init to no time elapsed */ |
| 907 | |
| 908 | /* |
| 909 | * Loop once for each bunch of groups in this voice that must be |
| 910 | * stemmed the same way. A beamed group must all be stemmed the same |
| 911 | * way, but nonbeamed notes are independent. |
| 912 | */ |
| 913 | start_p = this_p; |
| 914 | for (;;) { |
| 915 | /* |
| 916 | * Find next group that has nongrace notes, accumulating |
| 917 | * elapsed time. This code depends on grace notes having |
| 918 | * zero duration. |
| 919 | */ |
| 920 | while (start_p != 0 && (start_p->grpcont != GC_NOTES || |
| 921 | start_p->grpvalue == GV_ZERO)) { |
| 922 | vtime = radd(vtime, start_p->fulltime); |
| 923 | start_p = start_p->next; |
| 924 | } |
| 925 | if (start_p == 0) /* get out if no more this measure */ |
| 926 | break; |
| 927 | |
| 928 | /* if this group is not beamed, handle it, and point at next */ |
| 929 | if (start_p->beamloc == NOITEM) { |
| 930 | vtime2 = radd(vtime, start_p->fulltime); |
| 931 | |
| 932 | if (hasspace(other_p, vtime, vtime2)) { |
| 933 | /* other voice has space; decide stem */ |
| 934 | dobunch(start_p, start_p->next); |
| 935 | } else { |
| 936 | /* |
| 937 | * The other voice has notes/rests; force the |
| 938 | * the direction, unless the user has already |
| 939 | * forced it. |
| 940 | */ |
| 941 | if (start_p->stemdir == UNKNOWN) { |
| 942 | start_p->stemdir = (short)stemdir; |
| 943 | } |
| 944 | } |
| 945 | |
| 946 | start_p = start_p->next; |
| 947 | vtime = vtime2; |
| 948 | continue; |
| 949 | } |
| 950 | |
| 951 | /* find end of this beamed group, ignoring grace groups */ |
| 952 | vtime2 = vtime; |
| 953 | for (end_p = start_p; end_p != 0 && |
| 954 | (end_p->grpvalue == GV_ZERO || end_p->beamloc != ENDITEM); |
| 955 | end_p = end_p->next) |
| 956 | vtime2 = radd(vtime2, end_p->fulltime); |
| 957 | if (end_p == 0) |
| 958 | pfatal("beamed group is not terminated"); |
| 959 | vtime2 = radd(vtime2, end_p->fulltime); /* add in final note */ |
| 960 | |
| 961 | /* handle this bunch of groups, and point at next */ |
| 962 | if (hasspace(other_p, vtime, vtime2)) { |
| 963 | /* other voice has space; decide our stems */ |
| 964 | dobunch(start_p, end_p->next); |
| 965 | } else { |
| 966 | /* other voice has notes/rests; this forces ours */ |
| 967 | setbeamedstem(start_p, stemdir); |
| 968 | } |
| 969 | |
| 970 | vtime = vtime2; |
| 971 | start_p = end_p->next; |
| 972 | } |
| 973 | } |
| 974 | \f |
| 975 | /* |
| 976 | * Name: setbeamedstem() |
| 977 | * |
| 978 | * Abstract: Sets stem direction in beamed set, favoring one direction. |
| 979 | * |
| 980 | * Returns: void |
| 981 | * |
| 982 | * Description: This function is given the first group in a nongrace beamed |
| 983 | * set. It sets all the stem directions, the same way of course. |
| 984 | * It sets them to the given stemdir, unless the user has |
| 985 | * overridden the direction. |
| 986 | */ |
| 987 | |
| 988 | static void |
| 989 | setbeamedstem(start_p, stemdir) |
| 990 | |
| 991 | struct GRPSYL *start_p; /* point at the first GRPSYL in beamed set */ |
| 992 | int stemdir; /* which way we will try to point the stems */ |
| 993 | |
| 994 | { |
| 995 | struct GRPSYL *g_p; /* point into the set */ |
| 996 | int forcedir; /* direction forced by user */ |
| 997 | |
| 998 | |
| 999 | forcedir = UNKNOWN; /* no forcing yet */ |
| 1000 | |
| 1001 | /* look for groups in this set where the user has forced stemdir */ |
| 1002 | for (g_p = start_p; g_p != 0; g_p = g_p->next) { |
| 1003 | /* consider only nongrace note groups */ |
| 1004 | if (g_p->grpcont != GC_NOTES || g_p->grpvalue == GV_ZERO) { |
| 1005 | continue; |
| 1006 | } |
| 1007 | /* if user forced the stemdir */ |
| 1008 | if (g_p->stemdir != UNKNOWN) { |
| 1009 | if (forcedir == UNKNOWN) { |
| 1010 | /* first such occurrence; remember it */ |
| 1011 | forcedir = g_p->stemdir; |
| 1012 | } else if (g_p->stemdir != forcedir) { |
| 1013 | /* any later occurrence must agree */ |
| 1014 | l_warning(g_p->inputfile, g_p->inputlineno, |
| 1015 | "cannot have both 'up' and 'down' stem in same set of beamed or 'alt'-ed note groups"); |
| 1016 | forcedir = g_p->stemdir; /* use latest */ |
| 1017 | } |
| 1018 | } |
| 1019 | if (g_p->beamloc == ENDITEM) { |
| 1020 | break; |
| 1021 | } |
| 1022 | } |
| 1023 | |
| 1024 | /* if user forced any stems, we'll go with that direction */ |
| 1025 | if (forcedir != UNKNOWN) { |
| 1026 | stemdir = forcedir; |
| 1027 | } |
| 1028 | |
| 1029 | /* set all the stems in this set */ |
| 1030 | for (g_p = start_p; g_p != 0; g_p = g_p->next) { |
| 1031 | if (g_p->grpcont != GC_NOTES || g_p->grpvalue == GV_ZERO) { |
| 1032 | continue; |
| 1033 | } |
| 1034 | g_p->stemdir = stemdir; |
| 1035 | if (g_p->beamloc == ENDITEM) { |
| 1036 | break; |
| 1037 | } |
| 1038 | } |
| 1039 | } |
| 1040 | /* |
| 1041 | * Name: dobunch() |
| 1042 | * |
| 1043 | * Abstract: Sets stem direction for a single group or a beamed set. |
| 1044 | * |
| 1045 | * Returns: void |
| 1046 | * |
| 1047 | * Description: This function is given a single (nongrace) group, or a set |
| 1048 | * of them that will be beamed together, for the case where |
| 1049 | * the stems are allowed to go either way. It decides which |
| 1050 | * way is best, and sets the result. |
| 1051 | */ |
| 1052 | |
| 1053 | static void |
| 1054 | dobunch(start_p, end_p) |
| 1055 | |
| 1056 | struct GRPSYL *start_p; /* starts pointing at the first GRPSYL in a bunch */ |
| 1057 | struct GRPSYL *end_p; /* starts pointing after the last GRPSYL in a bunch */ |
| 1058 | |
| 1059 | { |
| 1060 | register struct GRPSYL *gs_p; /* point along list of them */ |
| 1061 | int lonesum; /* sum of offsets of single notes from center*/ |
| 1062 | int topsum; /* sum of offsets of top notes from center */ |
| 1063 | int botsum; /* sum of offsets of bottom notes from center*/ |
| 1064 | int insum; /* sum of offsets of inner notes from center */ |
| 1065 | int n; /* loop counter */ |
| 1066 | int stemdir; /* answer of where stems should point */ |
| 1067 | |
| 1068 | |
| 1069 | /* |
| 1070 | * Loop through all (nongrace) notes in these group(s), adding up |
| 1071 | * the offsets of their outer notes from the center line. For groups |
| 1072 | * that have only one note, count this in lonesum. For other groups, |
| 1073 | * count the top notes and bottom notes separately. We consider only |
| 1074 | * outer notes in these counters, and we count single note groups |
| 1075 | * separately to avoid counting the same note twice. But to be able to |
| 1076 | * breaks ties in the best way, we keep a separate counter for inner |
| 1077 | * notes of groups that have 3 or more notes. |
| 1078 | * While doing this, also keep track of whether the user requested a |
| 1079 | * specific stem direction on any of these groups. If so, there must |
| 1080 | * not be any contradictions between what they asked for. |
| 1081 | */ |
| 1082 | lonesum = topsum = botsum = insum = 0; |
| 1083 | stemdir = UNKNOWN; /* user hasn't asked for anything yet */ |
| 1084 | for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next) { |
| 1085 | /* |
| 1086 | * Consider only note groups. Cross staff beaming can have |
| 1087 | * spaces in the list of groups, and rests need to be skipped. |
| 1088 | */ |
| 1089 | if (gs_p->grpcont == GC_NOTES && gs_p->grpvalue == GV_NORMAL) { |
| 1090 | if (gs_p->stemdir != UNKNOWN) { |
| 1091 | if (stemdir == UNKNOWN) { |
| 1092 | stemdir = gs_p->stemdir; |
| 1093 | } else if (gs_p->stemdir != stemdir) { |
| 1094 | l_warning(gs_p->inputfile, |
| 1095 | gs_p->inputlineno, |
| 1096 | "cannot have both 'up' and 'down' stem in same set of beamed or 'alt'-ed note groups"); |
| 1097 | stemdir = gs_p->stemdir; |
| 1098 | } |
| 1099 | } |
| 1100 | |
| 1101 | if (gs_p->nnotes == 1) { |
| 1102 | lonesum += gs_p->notelist[0].stepsup; |
| 1103 | } else { |
| 1104 | topsum += gs_p->notelist[0].stepsup; |
| 1105 | botsum += gs_p->notelist[ gs_p->nnotes - 1 ]. |
| 1106 | stepsup; |
| 1107 | } |
| 1108 | |
| 1109 | /* this loop happens only if >= 3 notes in the group */ |
| 1110 | for (n = 1; n < gs_p->nnotes - 1; n++ ) { |
| 1111 | insum += gs_p->notelist[n].stepsup; |
| 1112 | } |
| 1113 | } |
| 1114 | } |
| 1115 | |
| 1116 | /* |
| 1117 | * If the user requested a stem direction, that's what they will get, |
| 1118 | * for 5-line regular staffs, but for 1-line regular staffs stems are |
| 1119 | * always UP and for tablature staffs, always DOWN. For tab staffs, the |
| 1120 | * parse phase blocks any user requests for stemdir, so we don't have |
| 1121 | * to cover that in the warning and error messages here. |
| 1122 | * |
| 1123 | * For a regular 5-line staff where the user didn't specify, if we are |
| 1124 | * involved in cross staff beaming, the direction defaults such that |
| 1125 | * the beam ends up between the two staffs; else, these rules apply: |
| 1126 | * If lonesum + topsum + botsum is positive, the "average" outer note |
| 1127 | * in these group(s) is above the center line, so the stems should go |
| 1128 | * down. If negative, they should go up. In case of tie, they should |
| 1129 | * go down, unless we can break the tie by using the inner notes. |
| 1130 | * For 1-line staff, the stem should go up, regardless. |
| 1131 | */ |
| 1132 | if (svpath(start_p->staffno, STAFFLINES)->stafflines == 5 && |
| 1133 | is_tab_staff(start_p->staffno) == NO) { |
| 1134 | if (stemdir == UNKNOWN) { |
| 1135 | switch (start_p->beamto) { |
| 1136 | case CS_ABOVE: /* bm with staff above */ |
| 1137 | stemdir = UP; |
| 1138 | break; |
| 1139 | case CS_BELOW: /* bm with staff below */ |
| 1140 | stemdir = DOWN; |
| 1141 | break; |
| 1142 | case CS_SAME: /* no cross staff beaming */ |
| 1143 | /* normal case: base on note distances */ |
| 1144 | if (lonesum + topsum + botsum > 0) |
| 1145 | stemdir = DOWN; |
| 1146 | else if (lonesum + topsum + botsum < 0) |
| 1147 | stemdir = UP; |
| 1148 | else |
| 1149 | stemdir = insum >= 0 ? DOWN : UP; |
| 1150 | break; |
| 1151 | } |
| 1152 | } |
| 1153 | } else if (is_tab_staff(start_p->staffno) == YES) { |
| 1154 | stemdir = DOWN; |
| 1155 | } else { |
| 1156 | if (stemdir == DOWN) |
| 1157 | l_ufatal(start_p->inputfile, start_p->inputlineno, |
| 1158 | "cannot specify 'down' stem on voice 1 or 2 of a one-line staff"); |
| 1159 | if (stemdir == UP) |
| 1160 | l_warning(start_p->inputfile, start_p->inputlineno, |
| 1161 | "stem direction should not be specified on voice 1 or 2 of a one-line staff"); |
| 1162 | stemdir = UP; /* in case it was UNKNOWN */ |
| 1163 | } |
| 1164 | |
| 1165 | /* mark all groups (doesn't hurt to mark rests and spaces too) */ |
| 1166 | for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next) { |
| 1167 | if (gs_p->grpvalue == GV_NORMAL) |
| 1168 | gs_p->stemdir = (short)stemdir; |
| 1169 | } |
| 1170 | } |
| 1171 | \f |
| 1172 | /* |
| 1173 | * Name: dograce() |
| 1174 | * |
| 1175 | * Abstract: Sets stem direction for a single grace group. |
| 1176 | * |
| 1177 | * Returns: void |
| 1178 | * |
| 1179 | * Description: This function sets stem directions for grace groups when the |
| 1180 | * vscheme V_2FREESTEM. (The V_1 and V_2OPSTEM cases were handled |
| 1181 | * along with nongrace groups.) If the next nongrace group occurs |
| 1182 | * at a time when the other voice has a space, the grace stem goes |
| 1183 | * up (like V_1), else the same as the main group (like V_2OPSTEM). |
| 1184 | * For the first voice, these rules boil down to the fact that |
| 1185 | * graces stems are always up. The second voice can end up |
| 1186 | * going either way. |
| 1187 | */ |
| 1188 | |
| 1189 | static void |
| 1190 | dograce(gs1_p, gs2_p) |
| 1191 | |
| 1192 | register struct GRPSYL *gs1_p; /* starts at first GRPSYL in voice 1 list */ |
| 1193 | register struct GRPSYL *gs2_p; /* starts at first GRPSYL in voice 2 list */ |
| 1194 | |
| 1195 | { |
| 1196 | register struct GRPSYL *gs_p; /* point along list of them */ |
| 1197 | RATIONAL vtime; /* elapsed time in measure */ |
| 1198 | static RATIONAL tiny = {1, 4 * MAXBASICTIME}; |
| 1199 | |
| 1200 | |
| 1201 | /* for the first voice, mark all grace stems up */ |
| 1202 | for (gs_p = gs1_p; gs_p != 0; gs_p = gs_p->next) { |
| 1203 | if (gs_p->grpvalue == GV_ZERO) |
| 1204 | gs_p->stemdir = UP; |
| 1205 | } |
| 1206 | |
| 1207 | /* |
| 1208 | * For the 2nd voice, loop though all groups. For each nongrace group, |
| 1209 | * accumulate the fulltime. For each grace group, find out if the |
| 1210 | * other voice has a space at the moment the following nongrace group |
| 1211 | * starts. If so, treat as V_1. If not, treat as V_2OPSTEM. |
| 1212 | */ |
| 1213 | vtime = Zero; |
| 1214 | for (gs_p = gs2_p; gs_p != 0; gs_p = gs_p->next) { |
| 1215 | if (gs_p->grpvalue == GV_NORMAL) { |
| 1216 | vtime = radd(vtime, gs_p->fulltime); |
| 1217 | } else { |
| 1218 | /* does other voice have space? */ |
| 1219 | if (hasspace(gs1_p, vtime, radd(vtime, tiny)) == YES) { |
| 1220 | gs_p->stemdir = UP; |
| 1221 | } else { |
| 1222 | gs_p->stemdir = DOWN; |
| 1223 | } |
| 1224 | } |
| 1225 | } |
| 1226 | } |
| 1227 | \f |
| 1228 | /* |
| 1229 | * Name: setv3stem() |
| 1230 | * |
| 1231 | * Abstract: Sets stem direction for each group in a linked list for voice 3. |
| 1232 | * |
| 1233 | * Returns: default stem direction after this measure |
| 1234 | * |
| 1235 | * Description: This function sets the stem direction for each group in a |
| 1236 | * linked list for a voice/measure that is for voice 3. Voice 3 |
| 1237 | * ignores the other voices. |
| 1238 | */ |
| 1239 | |
| 1240 | static int |
| 1241 | setv3stem(gs_p, stemdir) |
| 1242 | |
| 1243 | struct GRPSYL *gs_p; /* starts pointing at the first GRPSYL in a list */ |
| 1244 | int stemdir; /* stem direction of the previous group */ |
| 1245 | |
| 1246 | { |
| 1247 | register struct GRPSYL *start_p, *end_p; /* first and last of a set */ |
| 1248 | |
| 1249 | |
| 1250 | debug(32, "setv3stem file=%s line=%d", gs_p->inputfile, |
| 1251 | gs_p->inputlineno); |
| 1252 | /* |
| 1253 | * Loop once for each bunch of groups that must be stemmed the same |
| 1254 | * way. A beamed group must all be stemmed the same way, but nonbeamed |
| 1255 | * notes are independent. |
| 1256 | */ |
| 1257 | start_p = gs_p; |
| 1258 | for (;;) { |
| 1259 | /* |
| 1260 | * Find next group that has nongrace notes. While doing this, |
| 1261 | * set the stemdir for any grace groups encountered. For voice |
| 1262 | * 3, grace stems always go up. |
| 1263 | */ |
| 1264 | while (start_p != 0 && (start_p->grpcont != GC_NOTES || |
| 1265 | start_p->grpvalue == GV_ZERO)) { |
| 1266 | if (start_p->grpcont == GC_NOTES) /* must be grace */ |
| 1267 | start_p->stemdir = UP; |
| 1268 | start_p = start_p->next; |
| 1269 | } |
| 1270 | if (start_p == 0) /* get out if no more this measure */ |
| 1271 | break; |
| 1272 | |
| 1273 | /* if this group is not beamed, handle it, and point at next */ |
| 1274 | if (start_p->beamloc == NOITEM) { |
| 1275 | stemdir = dov3bunch(start_p, start_p->next, stemdir); |
| 1276 | start_p = start_p->next; |
| 1277 | continue; |
| 1278 | } |
| 1279 | |
| 1280 | /* |
| 1281 | * Find end of this beamed group, setting grace groups UP. |
| 1282 | * Note that voice 3 does not allow cross staff beaming. |
| 1283 | */ |
| 1284 | for (end_p = start_p; end_p != 0 && |
| 1285 | (end_p->grpvalue == GV_ZERO || end_p->beamloc != ENDITEM); |
| 1286 | end_p = end_p->next) { |
| 1287 | if (end_p->grpvalue == GV_ZERO) |
| 1288 | end_p->stemdir = UP; |
| 1289 | } |
| 1290 | if (end_p == 0) |
| 1291 | pfatal("beamed group is not terminated"); |
| 1292 | |
| 1293 | /* handle this bunch of groups, and point at next */ |
| 1294 | stemdir = dov3bunch(start_p, end_p->next, stemdir); |
| 1295 | start_p = end_p->next; |
| 1296 | } |
| 1297 | |
| 1298 | return (stemdir); |
| 1299 | } |
| 1300 | \f |
| 1301 | /* |
| 1302 | * Name: dov3bunch() |
| 1303 | * |
| 1304 | * Abstract: Sets stem dir for a single group or a beamed set on voice 3. |
| 1305 | * |
| 1306 | * Returns: stem direction that was chosen |
| 1307 | * |
| 1308 | * Description: This function is given a single (nongrace) group, or a set |
| 1309 | * of them that will be beamed together, for voice 3. It decides |
| 1310 | * which stemdir is needed, and sets it for each group. |
| 1311 | */ |
| 1312 | |
| 1313 | static int |
| 1314 | dov3bunch(start_p, end_p, stemdir) |
| 1315 | |
| 1316 | struct GRPSYL *start_p; /* starts pointing at the first GRPSYL in a bunch */ |
| 1317 | struct GRPSYL *end_p; /* starts pointing after the last GRPSYL in a bunch */ |
| 1318 | int stemdir; /* stem direction of the previous group */ |
| 1319 | |
| 1320 | { |
| 1321 | register struct GRPSYL *gs_p; /* point along list of them */ |
| 1322 | int userdir; /* stemdir requested by user */ |
| 1323 | |
| 1324 | |
| 1325 | /* |
| 1326 | * Loop through all groups in this bunch, keeping track of any user- |
| 1327 | * specified direction. Grace groups are forced to UP but are other- |
| 1328 | * wise ignored. Nongrace groups could be rests, so ignore them. |
| 1329 | */ |
| 1330 | userdir = UNKNOWN; /* user hasn't asked for anything yet */ |
| 1331 | for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next) { |
| 1332 | if (gs_p->grpvalue == GV_ZERO) { |
| 1333 | gs_p->stemdir = UP; /* grace group */ |
| 1334 | } else if (gs_p->grpcont == GC_NOTES) { |
| 1335 | if (gs_p->stemdir != UNKNOWN) { /* user request */ |
| 1336 | if (userdir == UNKNOWN) { |
| 1337 | userdir = gs_p->stemdir; |
| 1338 | } else if (gs_p->stemdir != userdir) { |
| 1339 | l_warning(gs_p->inputfile, |
| 1340 | gs_p->inputlineno, |
| 1341 | "cannot have both 'up' and 'down' stem in same set of beamed or 'alt'-ed note groups"); |
| 1342 | userdir = gs_p->stemdir; |
| 1343 | } |
| 1344 | } |
| 1345 | } |
| 1346 | } |
| 1347 | |
| 1348 | /* if user requested a direction, we will use that, else keep previous*/ |
| 1349 | if (userdir != UNKNOWN) |
| 1350 | stemdir = userdir; |
| 1351 | |
| 1352 | /* mark all nongrace groups; it doesn't hurt to mark rests */ |
| 1353 | for (gs_p = start_p; gs_p != end_p; gs_p = gs_p->next) { |
| 1354 | if (gs_p->grpvalue == GV_NORMAL) |
| 1355 | gs_p->stemdir = (short)stemdir; |
| 1356 | } |
| 1357 | |
| 1358 | return (stemdir); |
| 1359 | } |
| 1360 | \f |
| 1361 | /* |
| 1362 | * Name: setheads() |
| 1363 | * |
| 1364 | * Abstract: Set headshape, headfont, headchar, and coords for all notes. |
| 1365 | * |
| 1366 | * Returns: void |
| 1367 | * |
| 1368 | * Description: This function sets the headshape, headfont, and headchar for |
| 1369 | * all notes. (However, the headchar is changed later, in |
| 1370 | * setgrps.c, in certain cases where two GRPSYLs share a note.) |
| 1371 | * It also sets the relative vertical coords of the notes and |
| 1372 | * their groups. We waited until now to do this so that stemdir |
| 1373 | * would be known. |
| 1374 | */ |
| 1375 | |
| 1376 | static void |
| 1377 | setheads() |
| 1378 | |
| 1379 | { |
| 1380 | struct MAINLL *mainll_p; /* point at main linked list item */ |
| 1381 | struct STAFF *staff_p; /* point at a STAFF */ |
| 1382 | int stafflines; /* lines in a tablature staff */ |
| 1383 | int is_tab; /* is this a tablature staff? */ |
| 1384 | int sharps; /* in the key sig */ |
| 1385 | char keylet; /* letter of the key, assuming major */ |
| 1386 | short *shapes; /* 7 shapes for the 7 notes */ |
| 1387 | int vidx; /* voice index */ |
| 1388 | int allx_hsi; /* headshape index for allx */ |
| 1389 | |
| 1390 | |
| 1391 | debug(16, "setheads"); |
| 1392 | initstructs(); /* clean out old SSV info */ |
| 1393 | |
| 1394 | /* just in case we'll need it later */ |
| 1395 | allx_hsi = get_shape_num("allx"); |
| 1396 | |
| 1397 | /* |
| 1398 | * Loop once for each item in the main linked list. Apply any SSVs |
| 1399 | * that are found. For each voice on each staff, call setvoiceheads |
| 1400 | * to do the the work. |
| 1401 | */ |
| 1402 | for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { |
| 1403 | |
| 1404 | if (mainll_p->str == S_SSV) { |
| 1405 | /* apply the SSV and go to the next item */ |
| 1406 | asgnssv(mainll_p->u.ssv_p); |
| 1407 | continue; |
| 1408 | } |
| 1409 | |
| 1410 | /* deal only with visible staffs that aren't measure rpts */ |
| 1411 | if (mainll_p->str != S_STAFF || |
| 1412 | mainll_p->u.staff_p->visible == NO || |
| 1413 | is_mrpt(mainll_p->u.staff_p->groups_p[0])) { |
| 1414 | continue; |
| 1415 | } |
| 1416 | |
| 1417 | /* |
| 1418 | * We found a staff to work on. Set up some variables we'll |
| 1419 | * be needing. |
| 1420 | */ |
| 1421 | staff_p = mainll_p->u.staff_p; |
| 1422 | stafflines = svpath(staff_p->staffno, STAFFLINES)->stafflines; |
| 1423 | is_tab = svpath(staff_p->staffno, CLEF)->clef == TABCLEF; |
| 1424 | |
| 1425 | /* |
| 1426 | * Find the key letter. We don't care about any sharp or flat |
| 1427 | * in the key name, just the letter. For tab it's meaningless, |
| 1428 | * but that's okay. |
| 1429 | */ |
| 1430 | sharps = eff_key(staff_p->staffno); |
| 1431 | keylet = Circle[(sharps + 1 + 7) % 7]; |
| 1432 | |
| 1433 | /* loop through every possible voice on this staff */ |
| 1434 | for (vidx = 0; vidx < MAXVOICES; vidx++) { |
| 1435 | |
| 1436 | /* point at array of headshapes for this voice */ |
| 1437 | shapes = vvpath(staff_p->staffno, vidx + 1, |
| 1438 | NOTEHEADS)->noteheads; |
| 1439 | |
| 1440 | setvoiceheads(mainll_p, staff_p->groups_p[vidx], |
| 1441 | stafflines, shapes, is_tab, allx_hsi, sharps, |
| 1442 | keylet); |
| 1443 | } |
| 1444 | } |
| 1445 | } |
| 1446 | \f |
| 1447 | /* |
| 1448 | * Name: setvoiceheads() |
| 1449 | * |
| 1450 | * Abstract: Set headshape, headfont, headchar, and coords for one GRPSYL. |
| 1451 | * |
| 1452 | * Returns: void |
| 1453 | * |
| 1454 | * Description: This function sets the headshape, headfont, and headchar for |
| 1455 | * one GRPSYL. (However, the headchar is changed later, in |
| 1456 | * setgrps.c, in certain cases where two GRPSYLs share a note.) |
| 1457 | * It also sets the relative vertical coords of the notes and |
| 1458 | * the group. |
| 1459 | */ |
| 1460 | |
| 1461 | static void |
| 1462 | setvoiceheads(mll_p, gs_p, stafflines, shapes, is_tab, allx_hsi, sharps, keylet) |
| 1463 | |
| 1464 | struct MAINLL *mll_p; /* point at the main LL struct gs_p hangs off */ |
| 1465 | struct GRPSYL *gs_p; /* starts at start of GRPSYL list */ |
| 1466 | int stafflines; /* lines in a tablature staff */ |
| 1467 | short *shapes; /* 7 shapes for the 7 notes */ |
| 1468 | int is_tab; /* is this a tablature staff? */ |
| 1469 | int allx_hsi; /* headshape index for allx */ |
| 1470 | int sharps; /* in the key sig */ |
| 1471 | int keylet; /* letter of the key, assuming major */ |
| 1472 | |
| 1473 | { |
| 1474 | float bendheight; /* total height of bend numbers */ |
| 1475 | int havebend; /* any bends in this group? */ |
| 1476 | int n; /* loop variable */ |
| 1477 | int i; /* temp variable */ |
| 1478 | int hfont; /* font of note head */ |
| 1479 | int hchar; /* char of note head */ |
| 1480 | float vhalf; /* half the vert size of note head */ |
| 1481 | int stepsup; /* local copy */ |
| 1482 | |
| 1483 | |
| 1484 | /* loop through every GRPSYL in voice (may be none) */ |
| 1485 | for ( ; gs_p != 0; gs_p = gs_p->next) { |
| 1486 | |
| 1487 | /* we only care about notes, not rest/space */ |
| 1488 | if (gs_p->grpcont != GC_NOTES) { |
| 1489 | continue; |
| 1490 | } |
| 1491 | |
| 1492 | bendheight = 0; /* init to no bends */ |
| 1493 | havebend = NO; |
| 1494 | |
| 1495 | /* |
| 1496 | * Loop through every note in the GRPSYL, setting its |
| 1497 | * headshape, head font/char, and coords. |
| 1498 | */ |
| 1499 | for (n = 0; n < gs_p->nnotes; n++) { |
| 1500 | |
| 1501 | /* if there is no note-level override... */ |
| 1502 | if (gs_p->notelist[n].headshape == HS_UNKNOWN) { |
| 1503 | |
| 1504 | /* set to group-level override if present */ |
| 1505 | gs_p->notelist[n].headshape = gs_p->headshape; |
| 1506 | |
| 1507 | /* |
| 1508 | * If still no setting (which is the usual |
| 1509 | * case), set according to what the SSVs said. |
| 1510 | * Set i to how far note is above the tonic |
| 1511 | * (assuming a major key). Tab uses tonic. |
| 1512 | * Then get shape from array. |
| 1513 | */ |
| 1514 | if (gs_p->notelist[n].headshape == HS_UNKNOWN) { |
| 1515 | |
| 1516 | if (is_tab) { |
| 1517 | i = 0; /* arbitrary */ |
| 1518 | } else { |
| 1519 | i = (gs_p-> notelist[n].letter |
| 1520 | + 7 - keylet) % 7; |
| 1521 | } |
| 1522 | |
| 1523 | gs_p->notelist[n].headshape = shapes[i]; |
| 1524 | } |
| 1525 | } |
| 1526 | |
| 1527 | /* |
| 1528 | * Now that we know the stepsup (set in locllnotes()) |
| 1529 | * and the headshape, we can set the note's coords. |
| 1530 | */ |
| 1531 | if (is_tab && gs_p->notelist[n].headshape != allx_hsi) { |
| 1532 | |
| 1533 | /* handle tab (except when it's an X) */ |
| 1534 | |
| 1535 | gs_p->notelist[n].c[RY] = gs_p->notelist[n]. |
| 1536 | stepsup * TABRATIO * STEPSIZE; |
| 1537 | |
| 1538 | if (gs_p->notelist[n].FRETNO == NOFRET) { |
| 1539 | /* set RN and RS the same as RY */ |
| 1540 | gs_p->notelist[n].c[RN] = |
| 1541 | gs_p->notelist[n].c[RY]; |
| 1542 | gs_p->notelist[n].c[RS] = |
| 1543 | gs_p->notelist[n].c[RY]; |
| 1544 | } else { |
| 1545 | /* |
| 1546 | * Set vertical coordinates of the |
| 1547 | * "note" (fret number). It is to be |
| 1548 | * centered on the appropriate line. |
| 1549 | */ |
| 1550 | vhalf = strheight(fret_string(&gs_p-> |
| 1551 | notelist[n], gs_p)) / 2.0; |
| 1552 | gs_p->notelist[n].c[RN] = |
| 1553 | gs_p->notelist[n].c[RY] + vhalf; |
| 1554 | gs_p->notelist[n].c[RS] = |
| 1555 | gs_p->notelist[n].c[RY] - vhalf; |
| 1556 | } |
| 1557 | |
| 1558 | } else { |
| 1559 | |
| 1560 | /* handle non-tab and tab X-notes */ |
| 1561 | |
| 1562 | /* find & store music font and char */ |
| 1563 | hchar = nheadchar(gs_p->notelist[n].headshape, |
| 1564 | gs_p->basictime, gs_p->stemdir, &hfont); |
| 1565 | gs_p->notelist[n].headchar = hchar; |
| 1566 | gs_p->notelist[n].headfont = hfont; |
| 1567 | |
| 1568 | /* half the height of the note head */ |
| 1569 | vhalf = height(hfont, gs_p->notelist[n].notesize |
| 1570 | == GS_NORMAL ? DFLT_SIZE : SMALLSIZE, |
| 1571 | hchar) / 2; |
| 1572 | |
| 1573 | /* |
| 1574 | * Set actual relative vertical coords. We need |
| 1575 | * to recalculate the original stepsup, which |
| 1576 | * was modified for CSS notes, because absvert.c |
| 1577 | * needs to know what the note's coords would |
| 1578 | * have been if it hadn't been CSS. Sigh. |
| 1579 | */ |
| 1580 | stepsup = gs_p->notelist[n].stepsup; |
| 1581 | switch (gs_p->stemto) { |
| 1582 | case CS_ABOVE: |
| 1583 | if (n <= gs_p->stemto_idx) { |
| 1584 | stepsup -= CSS_STEPS; |
| 1585 | } |
| 1586 | break; |
| 1587 | case CS_BELOW: |
| 1588 | if (n >= gs_p->stemto_idx) { |
| 1589 | stepsup += CSS_STEPS; |
| 1590 | } |
| 1591 | break; |
| 1592 | } |
| 1593 | gs_p->notelist[n].c[RY] = stepsup * STEPSIZE * |
| 1594 | (is_tab ? TABRATIO : 1.0); |
| 1595 | |
| 1596 | gs_p->notelist[n].c[RN] = |
| 1597 | gs_p->notelist[n].c[RY] + vhalf; |
| 1598 | |
| 1599 | gs_p->notelist[n].c[RS] = |
| 1600 | gs_p->notelist[n].c[RY] - vhalf; |
| 1601 | } |
| 1602 | |
| 1603 | if (is_tab) { |
| 1604 | /* |
| 1605 | * If there was a real bend, add to total height |
| 1606 | * of the bend numbers. |
| 1607 | */ |
| 1608 | if (HASREALBEND(gs_p->notelist[n])) { |
| 1609 | bendheight += strheight(bend_string( |
| 1610 | &gs_p->notelist[n])) + STDPAD; |
| 1611 | } |
| 1612 | |
| 1613 | /* if any bend at all, remember it */ |
| 1614 | if (HASBEND(gs_p->notelist[n])) { |
| 1615 | havebend = YES; |
| 1616 | } |
| 1617 | } |
| 1618 | } |
| 1619 | |
| 1620 | /* |
| 1621 | * Set the group's coords. |
| 1622 | */ |
| 1623 | if (is_tab) { |
| 1624 | /* |
| 1625 | * Set the group's north based on the top of the top |
| 1626 | * bend number if there is one, otherwise the top of |
| 1627 | * the top fret number. We leave 3 "tab stepsizes" of |
| 1628 | * white space between the staff and the lowest bend |
| 1629 | * number, for the arrow. |
| 1630 | */ |
| 1631 | if (havebend == NO) { /* no bends present */ |
| 1632 | /* there must be frets, since no bends */ |
| 1633 | gs_p->c[RN] = gs_p->notelist[0].c[RN] + STDPAD; |
| 1634 | } else { /* bend(s) present */ |
| 1635 | gs_p->c[RN] = (stafflines + 2) * |
| 1636 | STEPSIZE * TABRATIO + bendheight; |
| 1637 | } |
| 1638 | |
| 1639 | /* |
| 1640 | * Set the group's south based on the bottom of the |
| 1641 | * bottom fret number if there is one, otherwise the |
| 1642 | * middle of the staff. |
| 1643 | */ |
| 1644 | if (gs_p->nnotes == 0) { /* no frets present */ |
| 1645 | gs_p->c[RS] = 0; |
| 1646 | } else { /* frets present */ |
| 1647 | gs_p->c[RS] = gs_p->notelist |
| 1648 | [ gs_p->nnotes - 1 ].c[RS] - STDPAD; |
| 1649 | } |
| 1650 | |
| 1651 | /* if bends, do work between this and other groups */ |
| 1652 | if (bendheight > 0) { |
| 1653 | intertab(gs_p, mll_p); |
| 1654 | } |
| 1655 | } else { |
| 1656 | /* |
| 1657 | * Non-tab: use the outermost non-CSS notes, but pad. |
| 1658 | * If all notes are CSS, then set RN and RS to zero. |
| 1659 | */ |
| 1660 | switch (gs_p->stemto) { |
| 1661 | case CS_SAME: |
| 1662 | gs_p->c[RN] = gs_p->notelist |
| 1663 | [0].c[RN] + STDPAD; |
| 1664 | gs_p->c[RS] = gs_p->notelist |
| 1665 | [gs_p->nnotes-1].c[RS] - STDPAD; |
| 1666 | break; |
| 1667 | case CS_ABOVE: |
| 1668 | if (gs_p->stemto_idx == gs_p->nnotes - 1) { |
| 1669 | gs_p->c[RN] = gs_p->c[RS] = 0.0; |
| 1670 | } else { |
| 1671 | gs_p->c[RN] = gs_p->notelist |
| 1672 | [gs_p->stemto_idx+1].c[RN] + STDPAD; |
| 1673 | gs_p->c[RS] = gs_p->notelist |
| 1674 | [gs_p->nnotes-1].c[RS] - STDPAD; |
| 1675 | } |
| 1676 | break; |
| 1677 | case CS_BELOW: |
| 1678 | if (gs_p->stemto_idx == 0) { |
| 1679 | gs_p->c[RN] = gs_p->c[RS] = 0.0; |
| 1680 | } else { |
| 1681 | gs_p->c[RN] = gs_p->notelist |
| 1682 | [0].c[RN] + STDPAD; |
| 1683 | gs_p->c[RS] = gs_p->notelist |
| 1684 | [gs_p->stemto_idx-1].c[RS] - STDPAD; |
| 1685 | } |
| 1686 | break; |
| 1687 | } |
| 1688 | } |
| 1689 | } |
| 1690 | } |
| 1691 | \f |
| 1692 | /* |
| 1693 | * Name: fixoneline() |
| 1694 | * |
| 1695 | * Abstract: Fix stemsup and vertical coord for notes on one-line staffs. |
| 1696 | * |
| 1697 | * Returns: void |
| 1698 | * |
| 1699 | * Description: stepsup and notes' vertical coords are set in locllnotes(). |
| 1700 | * For one-line staffs, it assumes the notes are on the line. |
| 1701 | * But if the notes are not to be on the line, that isn't right. |
| 1702 | * It depends on which voice this is. Now that we have set the |
| 1703 | * stem direction, we need to correct this info. |
| 1704 | */ |
| 1705 | |
| 1706 | static void |
| 1707 | fixoneline() |
| 1708 | |
| 1709 | { |
| 1710 | struct MAINLL *mainll_p; /* point at main linked list item */ |
| 1711 | struct STAFF *staff_p; /* point at a STAFF */ |
| 1712 | struct GRPSYL *gs_p; /* point along a GRPSYL list */ |
| 1713 | int v; /* voice number, 0 or 1 */ |
| 1714 | |
| 1715 | |
| 1716 | debug(16, "fixoneline"); |
| 1717 | initstructs(); /* clean out old SSV info */ |
| 1718 | |
| 1719 | /* |
| 1720 | * Loop once for each item in the main linked list. Apply any SSVs |
| 1721 | * that are found. Move notes that are not to be on the line. |
| 1722 | */ |
| 1723 | for (mainll_p = Mainllhc_p; mainll_p != 0; mainll_p = mainll_p->next) { |
| 1724 | |
| 1725 | if (mainll_p->str == S_SSV) { |
| 1726 | /* apply the SSV and go to the next item */ |
| 1727 | asgnssv(mainll_p->u.ssv_p); |
| 1728 | continue; |
| 1729 | } |
| 1730 | |
| 1731 | /* deal only with visible staffs that aren't measure rpts */ |
| 1732 | if (mainll_p->str != S_STAFF || |
| 1733 | mainll_p->u.staff_p->visible == NO || |
| 1734 | is_mrpt(mainll_p->u.staff_p->groups_p[0])) { |
| 1735 | continue; |
| 1736 | } |
| 1737 | |
| 1738 | staff_p = mainll_p->u.staff_p; |
| 1739 | |
| 1740 | /* deal only with non-tab one-line staffs */ |
| 1741 | if (svpath(staff_p->staffno, STAFFLINES)->stafflines != 1 || |
| 1742 | svpath(staff_p->staffno, CLEF)->clef |
| 1743 | == TABCLEF) { |
| 1744 | continue; |
| 1745 | } |
| 1746 | |
| 1747 | /* |
| 1748 | * Loop through voices 1 and 2, and process each list. Note |
| 1749 | * that voice 3 is always on the line, so we don't need to do |
| 1750 | * anything to it. |
| 1751 | */ |
| 1752 | for (v = 0; v < NORMVOICES && staff_p->groups_p[v] != 0; v++) { |
| 1753 | |
| 1754 | /* change stepsup from 0 only if notes not on the line*/ |
| 1755 | if (vvpath(staff_p->staffno, v + 1, ONTHELINE)-> |
| 1756 | ontheline == YES) { |
| 1757 | continue; |
| 1758 | } |
| 1759 | |
| 1760 | for (gs_p = staff_p->groups_p[v]; gs_p != 0; |
| 1761 | gs_p = gs_p->next) { |
| 1762 | |
| 1763 | /* only notes are to be changed */ |
| 1764 | if (gs_p->grpcont != GC_NOTES) { |
| 1765 | continue; |
| 1766 | } |
| 1767 | |
| 1768 | /* move up or down a step based on voice */ |
| 1769 | if (gs_p->vno == 1) { |
| 1770 | gs_p->notelist[0].stepsup = 1; |
| 1771 | gs_p->notelist[0].c[RY] = STEPSIZE; |
| 1772 | gs_p->notelist[0].c[RN] += STEPSIZE; |
| 1773 | gs_p->notelist[0].c[RS] += STEPSIZE; |
| 1774 | } else { |
| 1775 | gs_p->notelist[0].stepsup = -1; |
| 1776 | gs_p->notelist[0].c[RY] = -STEPSIZE; |
| 1777 | gs_p->notelist[0].c[RN] -= STEPSIZE; |
| 1778 | gs_p->notelist[0].c[RS] -= STEPSIZE; |
| 1779 | } |
| 1780 | } |
| 1781 | } |
| 1782 | } |
| 1783 | } |