chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / setnotes.c
CommitLineData
69695f33
MW
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
17static void locnotes P((void));
18static void locllnotes P((struct MAINLL *mll_p, int v,
19 struct MAINLL *nextbar_p));
20static void chktabcollision P((struct GRPSYL *start_p));
21static void intertab P((struct GRPSYL *gs_p, struct MAINLL *mll_p));
22static void setstems P((void));
23static void setonestem P((struct GRPSYL *gs_p));
24static void setopstem P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p));
25static void setfreestem P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p));
26static void set1freestem P((struct GRPSYL *this_p, struct GRPSYL *other_p,
27 int stemdir));
28static void setbeamedstem P((struct GRPSYL *start_p, int stemdir));
29static void dobunch P((struct GRPSYL *start_p, struct GRPSYL *end_p));
30static void dograce P((struct GRPSYL *gs1_p, struct GRPSYL *gs2_p));
31static int setv3stem P((struct GRPSYL *gs_p, int stemdir));
32static int dov3bunch P((struct GRPSYL *start_p, struct GRPSYL *end_p,
33 int stemdir));
34static void setheads P((void));
35static 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));
38static 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
53void
54setnotes()
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
77static void
78locnotes()
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
179static void
180locllnotes(mll_p, v, nextbar_p)
181
182struct MAINLL *mll_p; /* point at the MLL struct voice hangs off */
183int v; /* voice to loop through */
184struct 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
338static void
339chktabcollision(start_p)
340
341struct 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
412static void
413intertab(gs_p, mll_p)
414
415struct GRPSYL *gs_p; /* point at current tablature group */
416struct 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
600static void
601setstems()
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
720static void
721setonestem(gs_p)
722
723struct 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
798static void
799setopstem(gs1_p, gs2_p)
800
801register struct GRPSYL *gs1_p; /* starts at first GRPSYL in voice 1 list */
802register 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
860static void
861setfreestem(gs1_p, gs2_p)
862
863struct GRPSYL *gs1_p; /* starts pointing at first GRPSYL in voice 1 list */
864struct 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
890static void
891set1freestem(this_p, other_p, stemdir)
892
893struct GRPSYL *this_p; /* starts pointing at first GRPSYL in linked list */
894 /* for the voice whose stems we are now setting */
895struct GRPSYL *other_p; /* starts pointing at first GRPSYL in linked list */
896 /* for the other voice */
897int 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
988static void
989setbeamedstem(start_p, stemdir)
990
991struct GRPSYL *start_p; /* point at the first GRPSYL in beamed set */
992int 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
1053static void
1054dobunch(start_p, end_p)
1055
1056struct GRPSYL *start_p; /* starts pointing at the first GRPSYL in a bunch */
1057struct 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
1189static void
1190dograce(gs1_p, gs2_p)
1191
1192register struct GRPSYL *gs1_p; /* starts at first GRPSYL in voice 1 list */
1193register 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
1240static int
1241setv3stem(gs_p, stemdir)
1242
1243struct GRPSYL *gs_p; /* starts pointing at the first GRPSYL in a list */
1244int 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
1313static int
1314dov3bunch(start_p, end_p, stemdir)
1315
1316struct GRPSYL *start_p; /* starts pointing at the first GRPSYL in a bunch */
1317struct GRPSYL *end_p; /* starts pointing after the last GRPSYL in a bunch */
1318int 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
1376static void
1377setheads()
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
1461static void
1462setvoiceheads(mll_p, gs_p, stafflines, shapes, is_tab, allx_hsi, sharps, keylet)
1463
1464struct MAINLL *mll_p; /* point at the main LL struct gs_p hangs off */
1465struct GRPSYL *gs_p; /* starts at start of GRPSYL list */
1466int stafflines; /* lines in a tablature staff */
1467short *shapes; /* 7 shapes for the 7 notes */
1468int is_tab; /* is this a tablature staff? */
1469int allx_hsi; /* headshape index for allx */
1470int sharps; /* in the key sig */
1471int 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
1706static void
1707fixoneline()
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}