Commit | Line | Data |
---|---|---|
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 | ||
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 | } |