Commit | Line | Data |
---|---|---|
69695f33 MW |
1 | |
2 | /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005 by Arkkra Enterprises */ | |
3 | /* All rights reserved */ | |
4 | ||
5 | /* functions called at parse time to deal with beaming. */ | |
6 | ||
7 | ||
8 | #include "defines.h" | |
9 | #include "structs.h" | |
10 | #include "globals.h" | |
11 | ||
12 | ||
13 | static void subbeam P((struct SSV *ssv_p, RATIONAL outer_time, int obi, | |
14 | struct GRPSYL *begin_gs_p, struct GRPSYL *end_gs_p)); | |
15 | static struct GRPSYL *verify_crossbeam P((struct GRPSYL *gs_p, | |
16 | struct GRPSYL *other_gs_p, RATIONAL start_time, RATIONAL *end_time_p, | |
17 | int staffno, int size)); | |
18 | static void | |
19 | slopelencheck P((struct GRPSYL *first_p, struct GRPSYL *last_p, char *bmtype)); | |
20 | ||
21 | ||
22 | /* this function will get called whenever a group is not the start or end | |
23 | * of a custom beam group. This will fill in the beamloc based on past | |
24 | * history. If we were most recently in a custom beam group, we still are. | |
25 | * If we weren't before, we aren't now either. The non-custom beaming | |
26 | * gets done later after we have collected a whole bar in do_beaming() | |
27 | * which is called via do_bar(). */ | |
28 | ||
29 | void | |
30 | setbeamloc(curr_grp_p, last_grp_p) | |
31 | ||
32 | struct GRPSYL *curr_grp_p; /* the group we're working on */ | |
33 | struct GRPSYL *last_grp_p; /* the last group we did */ | |
34 | ||
35 | { | |
36 | if (curr_grp_p->grpvalue == GV_ZERO) { | |
37 | /* grace notes don't get handled here */ | |
38 | curr_grp_p->beamloc = NOITEM; | |
39 | return; | |
40 | } | |
41 | ||
42 | /* if the previous group is a grace group, that doesn't count. Back | |
43 | * up in the list to the first non-grace or beginning of list */ | |
44 | for ( ; last_grp_p != (struct GRPSYL *) 0 | |
45 | && last_grp_p->grpvalue == GV_ZERO; | |
46 | last_grp_p = last_grp_p->prev) { | |
47 | ; | |
48 | } | |
49 | ||
50 | if (last_grp_p != (struct GRPSYL *) 0) { | |
51 | ||
52 | switch (last_grp_p->beamloc) { | |
53 | ||
54 | case STARTITEM: | |
55 | case INITEM: | |
56 | curr_grp_p->beamto = last_grp_p->beamto; | |
57 | /* Make sure notes are 8th or shorter. Spaces (which | |
58 | * are allowed in cross-staff beams) can be longer. */ | |
59 | if (curr_grp_p->basictime < 8) { | |
60 | /* At this point, spaces are still | |
61 | * pseudo-pitches, so have to figure out | |
62 | * if this group is really notes */ | |
63 | int letter; /* pitch */ | |
64 | int n; /* index through notelist */ | |
65 | for (n = 0; n < curr_grp_p->nnotes; n++) { | |
66 | letter = curr_grp_p->notelist[n].letter; | |
67 | if ((letter >= 'a' && letter <= 'g') || | |
68 | letter == PP_NO_PITCH) { | |
69 | yyerror("beamed notes must be 8th or shorter"); | |
70 | break; | |
71 | } | |
72 | } | |
73 | } | |
74 | ||
75 | /* if previous began beaming, we must be inside now */ | |
76 | /* if in beam before, we still are */ | |
77 | curr_grp_p->beamloc = INITEM; | |
78 | break; | |
79 | ||
80 | case ENDITEM: | |
81 | case NOITEM: | |
82 | default: | |
83 | /* if previous was ending beaming, or | |
84 | * we weren't inside a beam, must be no beam now */ | |
85 | curr_grp_p->beamloc = NOITEM; | |
86 | break; | |
87 | } | |
88 | } | |
89 | else { | |
90 | /* nothing specified before, so no beam */ | |
91 | curr_grp_p->beamloc = NOITEM; | |
92 | } | |
93 | } | |
94 | \f | |
95 | ||
96 | /* take a quick jaunt through a GRPSYL list, seeing if custom | |
97 | * beaming was already done. If so, return YES, otherwise NO. | |
98 | * If there are any non-grace groups with beaming info, there must | |
99 | * have been custom beaming. Also, verify that user didn't attempt to | |
100 | * custom beam a mixture of normal and cue size notes, | |
101 | * or try to put illegal rests or spaces inside a beam. | |
102 | * Spaces are only allowed in cross-staff beams, unless user explicitly | |
103 | * says to beam across spaces. Rests are also only allowed in beams if | |
104 | * the user explicitly states they should be. Rests and spaces cannot be on | |
105 | * the ends, and must be eighth note or shorter. | |
106 | * We allow beaming together chords that are stemmed to another staff | |
107 | * only if all such instances are to the same staff--not some to staff above | |
108 | * and others to staff below--because beaming involving 3 staffs at once is | |
109 | * just too hard to deal with. | |
110 | * Also check that any esbm has at least 2 notes before and after it. | |
111 | */ | |
112 | ||
113 | int | |
114 | has_cust_beaming(grpsyl_p) | |
115 | ||
116 | struct GRPSYL *grpsyl_p; /* list of GRPSYLs to check */ | |
117 | ||
118 | { | |
119 | int has = NO; | |
120 | int size = GS_NORMAL; | |
121 | int numnotes = 0; /* how many notes groups, for esbm check */ | |
122 | short stemto = CS_SAME; /* to check for mixed CS_ABOVE/CS_BELOW */ | |
123 | struct GRPSYL *start_p = 0; /* first beamed group */ | |
124 | ||
125 | for ( ; grpsyl_p != (struct GRPSYL *) NULL; | |
126 | grpsyl_p = grpsyl_p->next) { | |
127 | ||
128 | if ((grpsyl_p->grpvalue != GV_ZERO) | |
129 | && (grpsyl_p->beamloc != NOITEM)) { | |
130 | /* have non-grace with beam info set */ | |
131 | has = YES; | |
132 | ||
133 | /* check for size or cross-staff stem mixtures */ | |
134 | if (grpsyl_p->beamloc == STARTITEM) { | |
135 | size = grpsyl_p->grpsize; | |
136 | numnotes = 0; | |
137 | stemto = CS_SAME; | |
138 | } | |
139 | /* check for size mixture. But only do non-cross-staff | |
140 | * beams here, because it's a lot easier to do the | |
141 | * cross-staff beam check in chk_crossbeams() */ | |
142 | else if (grpsyl_p->grpsize != size | |
143 | && grpsyl_p->beamto == CS_SAME) { | |
144 | l_yyerror(grpsyl_p->inputfile, | |
145 | grpsyl_p->inputlineno, | |
146 | "can't beam normal and cue notes together"); | |
147 | } | |
148 | ||
149 | if (grpsyl_p->grpcont != GC_NOTES) { | |
150 | if (grpsyl_p->grpcont == GC_REST) { | |
151 | if (grpsyl_p->basictime < 8) { | |
152 | l_yyerror(grpsyl_p->inputfile, | |
153 | grpsyl_p->inputlineno, | |
154 | "rests inside a beam must be less than quarter note duration"); | |
155 | } | |
156 | if (grpsyl_p->beamloc != INITEM) { | |
157 | l_yyerror(grpsyl_p->inputfile, | |
158 | grpsyl_p->inputlineno, | |
159 | "beam cannot %s with a rest", | |
160 | grpsyl_p->beamloc == STARTITEM ? | |
161 | "begin" : "end"); | |
162 | } | |
163 | } | |
164 | else if (grpsyl_p->beamto == CS_SAME) { | |
165 | if (grpsyl_p->beamloc != INITEM) { | |
166 | l_yyerror(grpsyl_p->inputfile, | |
167 | grpsyl_p->inputlineno, | |
168 | "beam cannot begin or end with a space"); | |
169 | } | |
170 | if (grpsyl_p->basictime < 8) { | |
171 | l_yyerror(grpsyl_p->inputfile, | |
172 | grpsyl_p->inputlineno, | |
173 | "spaces inside a beam must be less than quarter note duration"); | |
174 | } | |
175 | } | |
176 | } | |
177 | else if (grpsyl_p->grpvalue != GV_ZERO) { | |
178 | numnotes++; | |
179 | } | |
180 | ||
181 | if (grpsyl_p->stemto != CS_SAME) { | |
182 | if (stemto != CS_SAME && grpsyl_p->stemto | |
183 | != stemto) { | |
184 | l_yyerror(grpsyl_p->inputfile, | |
185 | grpsyl_p->inputlineno, | |
186 | "beam cannot include chords with stems to both above and below staffs"); | |
187 | } | |
188 | stemto = grpsyl_p->stemto; | |
189 | } | |
190 | ||
191 | if (grpsyl_p->beamloc == INITEM && | |
192 | IS_STEMLEN_KNOWN(grpsyl_p->stemlen)) { | |
193 | l_yyerror(grpsyl_p->inputfile, | |
194 | grpsyl_p->inputlineno, | |
195 | "stem len specification not allowed inside a beam"); | |
196 | } | |
197 | ||
198 | if (grpsyl_p->beamloc == STARTITEM) { | |
199 | start_p = grpsyl_p; | |
200 | } | |
201 | else if (grpsyl_p->beamloc == ENDITEM) { | |
202 | if (start_p != 0) { | |
203 | slopelencheck(start_p, grpsyl_p, "beam"); | |
204 | start_p = 0; | |
205 | } | |
206 | } | |
207 | ||
208 | if (grpsyl_p->breakbeam == YES | |
209 | && grpsyl_p->beamto == CS_SAME) { | |
210 | if (numnotes < 2) { | |
211 | l_warning(grpsyl_p->inputfile, | |
212 | grpsyl_p->inputlineno, | |
213 | "esbm must be preceeded by at least 2 beamed notes"); | |
214 | grpsyl_p->breakbeam = NO; | |
215 | } | |
216 | else { | |
217 | struct GRPSYL *g_p; | |
218 | ||
219 | /* Check that there are | |
220 | * at least 2 following beamed notes */ | |
221 | numnotes = 0; | |
222 | for (g_p = grpsyl_p->next; g_p != 0; | |
223 | g_p = g_p->next) { | |
224 | if (g_p->grpcont == GC_NOTES && | |
225 | g_p->grpvalue != GV_ZERO) { | |
226 | numnotes++; | |
227 | } | |
228 | if (g_p->breakbeam == YES) { | |
229 | break; | |
230 | } | |
231 | if (g_p->beamloc == ENDITEM) { | |
232 | break; | |
233 | } | |
234 | } | |
235 | if (numnotes < 2) { | |
236 | l_warning(grpsyl_p->inputfile, | |
237 | grpsyl_p->inputlineno, | |
238 | "esbm must be followed by at least 2 beamed notes"); | |
239 | grpsyl_p->breakbeam = NO; | |
240 | } | |
241 | else if (grpsyl_p->grpcont != GC_NOTES) { | |
242 | /* User really should have put | |
243 | * the esbm on the preceeding | |
244 | * NOTES group. We'll be nice | |
245 | * and move it for them. | |
246 | */ | |
247 | grpsyl_p->breakbeam = NO; | |
248 | for (g_p = grpsyl_p->prev; | |
249 | g_p->grpcont != GC_NOTES; | |
250 | g_p = g_p->prev) { | |
251 | ; | |
252 | } | |
253 | g_p->breakbeam = YES; | |
254 | } | |
255 | } | |
256 | numnotes = 0; | |
257 | } | |
258 | } | |
259 | } | |
260 | ||
261 | return(has); | |
262 | } | |
263 | \f | |
264 | ||
265 | /* beam notes together according to user-specified default beaming style */ | |
266 | ||
267 | void | |
268 | do_beaming(gs_p, grpsize, staffno, vno) | |
269 | ||
270 | struct GRPSYL *gs_p; /* list of GRPSYLs to do beaming on */ | |
271 | int grpsize; /* GS_NORMAL or GS_SMALL | |
272 | * (grace are handled separately) */ | |
273 | int staffno; | |
274 | int vno; /* voice number */ | |
275 | ||
276 | { | |
277 | struct SSV *ssv_beaminfo_p; /* ssv having relevent beam info */ | |
278 | register int n; /* index into beamstyle list */ | |
279 | RATIONAL styletime; /* accumulated time to beam together */ | |
280 | RATIONAL tot_time; /* cumulative grpsyl time */ | |
281 | struct GRPSYL *first_p; /* first in beam group */ | |
282 | struct GRPSYL *last_p; /* last in beam group */ | |
283 | int stop; /* YES if need to stop beaming */ | |
284 | int beamrests; /* if to include rests inside beams */ | |
285 | int beamspaces; /* if to include spaces inside beams */ | |
286 | short stemto = CS_SAME; /* check for mixed CS_ABOVE/CS_BELOW */ | |
287 | short restart = NO; /* YES if could start another beam | |
288 | * with current group, even though | |
289 | * it can go with previous */ | |
290 | ||
291 | ||
292 | ||
293 | debug(4, "do_beaming file=%s line=%d grpsize=%d staff=%d voice=%d", | |
294 | gs_p->inputfile, gs_p->inputlineno, grpsize, staffno, vno); | |
295 | ||
296 | /* if no default beaming scheme for this voice, then nothing to do-- | |
297 | * any custom beaming would have already been done */ | |
298 | ssv_beaminfo_p = vvpath(staffno, vno, BEAMSTLIST); | |
299 | if (ssv_beaminfo_p->nbeam == 0) { | |
300 | return; | |
301 | } | |
302 | ||
303 | /* ok. We may need to do some beaming. Go through the beamstlist and | |
304 | * see if there are any groups to beam together */ | |
305 | ||
306 | /* initialize */ | |
307 | /* point to first non-grace group */ | |
308 | for ( ; gs_p != (struct GRPSYL *) 0 && gs_p->grpcont == GC_NOTES | |
309 | && gs_p->grpvalue == GV_ZERO; gs_p = gs_p->next) { | |
310 | ; | |
311 | } | |
312 | ||
313 | /* if no groups, nothing to do */ | |
314 | if (gs_p == (struct GRPSYL *) 0) { | |
315 | return; | |
316 | } | |
317 | ||
318 | beamrests = vvpath(staffno, vno, BEAMSTLIST)->beamrests; | |
319 | beamspaces = vvpath(staffno, vno, BEAMSTLIST)->beamspaces; | |
320 | styletime = tot_time = Zero; | |
321 | for (n = 0; n < ssv_beaminfo_p->nbeam; n++) { | |
322 | styletime = radd(styletime, ssv_beaminfo_p->beamstlist[n]); | |
323 | ||
324 | if (GE(tot_time, styletime)) { | |
325 | /* we're already past this beamstyle segment */ | |
326 | continue; | |
327 | } | |
328 | ||
329 | for (first_p = last_p = 0, stop = NO; | |
330 | LT(tot_time, styletime); gs_p = gs_p->next) { | |
331 | ||
332 | if (gs_p == 0) { | |
333 | /* Must be too few groups in measure. | |
334 | * This error will already have been | |
335 | * reported elsewhere. | |
336 | */ | |
337 | return; | |
338 | } | |
339 | ||
340 | /* ignore grace */ | |
341 | while (gs_p->grpvalue == GV_ZERO) { | |
342 | gs_p = gs_p->next; | |
343 | if (gs_p == 0) { | |
344 | /* Must have tried to end a measure | |
345 | * with grace. Already reported. */ | |
346 | return; | |
347 | } | |
348 | } | |
349 | ||
350 | tot_time = radd(tot_time, gs_p->fulltime); | |
351 | if (GE(tot_time, styletime)) { | |
352 | /* This group puts us at or past | |
353 | * the current beamstyle segment */ | |
354 | stop = YES; | |
355 | } | |
356 | ||
357 | /* only 8th and shorter get beamed */ | |
358 | if (gs_p->basictime < 8 || (gs_p->grpcont == GC_SPACE && | |
359 | beamspaces == NO) ) { | |
360 | stop = YES; | |
361 | } | |
362 | else if (gs_p->grpcont == GC_REST && beamrests == NO) { | |
363 | stop = YES; | |
364 | } | |
365 | else if (gs_p->grpsize != grpsize) { | |
366 | /* Wrong size to beam on this call */ | |
367 | stop = YES; | |
368 | } | |
369 | else if (gs_p->stemto != CS_SAME && stemto != CS_SAME | |
370 | && gs_p->stemto != stemto) { | |
371 | /* We don't allow beaming across three staffs, | |
372 | * so have to stop current beam, but could | |
373 | * possibly beam this group with following | |
374 | * groups, as long as they don't have a | |
375 | * conflicting stemto */ | |
376 | stop = YES; | |
377 | restart = YES; | |
378 | } | |
379 | else if (gs_p->grpcont == GC_NOTES && | |
380 | LE(tot_time, styletime)) { | |
381 | /* found something beam-able */ | |
382 | if (first_p == 0) { | |
383 | first_p = gs_p; | |
384 | } | |
385 | last_p = gs_p; | |
386 | } | |
387 | if (gs_p->stemto != CS_SAME) { | |
388 | stemto = gs_p->stemto; | |
389 | } | |
390 | ||
391 | if (stop == YES) { | |
392 | if (first_p != 0 && last_p != 0 | |
393 | && first_p != last_p) { | |
394 | /* Disallow illegal combinations of | |
395 | * slope and stem length */ | |
396 | slopelencheck(first_p, last_p, "beam"); | |
397 | ||
398 | /* If there are subbeam groupings, | |
399 | * do those. */ | |
400 | subbeam(ssv_beaminfo_p, | |
401 | rsub(styletime, ssv_beaminfo_p->beamstlist[n]), | |
402 | n, first_p, last_p); | |
403 | ||
404 | /* mark beginning of beam group */ | |
405 | first_p->beamloc = STARTITEM; | |
406 | ||
407 | /* mark all intermediate groups, | |
408 | * skipping grace */ | |
409 | for (first_p = first_p->next; | |
410 | first_p != last_p; | |
411 | first_p = first_p->next) { | |
412 | ||
413 | if (first_p->grpvalue | |
414 | == GV_ZERO) { | |
415 | continue; | |
416 | } | |
417 | ||
418 | first_p->beamloc = INITEM; | |
419 | ||
420 | if (IS_STEMLEN_KNOWN(first_p->stemlen)) { | |
421 | l_yyerror(first_p->inputfile, first_p->inputlineno, | |
422 | "stem len specification not allowed inside a beam"); | |
423 | } | |
424 | } | |
425 | ||
426 | /* mark the end of the beam group */ | |
427 | last_p->beamloc = ENDITEM; | |
428 | } | |
429 | ||
430 | /* Re-init for any more bunches to beam */ | |
431 | first_p = last_p = 0; | |
432 | stop = NO; | |
433 | stemto = CS_SAME; | |
434 | if (restart == YES) { | |
435 | if (gs_p->grpcont == GC_NOTES) { | |
436 | first_p = gs_p; | |
437 | } | |
438 | restart = NO; | |
439 | } | |
440 | } | |
441 | } | |
442 | } | |
443 | } | |
444 | \f | |
445 | ||
446 | /* Once a STARTITEM and ENDITEM groups of the regular beamstyle | |
447 | * have been identified, go through them to see if there should | |
448 | * be subgroups. If so, mark breakbeam = YES on the last group of | |
449 | * each subgroup. | |
450 | */ | |
451 | ||
452 | static void | |
453 | subbeam(ssv_p, outer_time, obi, begin_gs_p, end_gs_p) | |
454 | ||
455 | struct SSV *ssv_p; /* to get beamstlist and subbeamstlist */ | |
456 | RATIONAL outer_time; /* Time in measure when outer beam begins */ | |
457 | int obi; /* outer beam index, subscript into ssv_p->beamstlist */ | |
458 | struct GRPSYL *begin_gs_p; | |
459 | struct GRPSYL *end_gs_p; | |
460 | ||
461 | { | |
462 | int sbi; /* sub beam index, subscript of ssv_p->subbeamstlist */ | |
463 | RATIONAL subgroup_time; /* duration of items in subbeamstlist */ | |
464 | RATIONAL tot_time; /* sum of note groups in subbeaming */ | |
465 | struct GRPSYL *gs_p; /* walk through groups */ | |
466 | struct GRPSYL *last_notegroup_p;/* Most recent GC_NOTES GRPSYL */ | |
467 | ||
468 | ||
469 | /* Check if more than one beam subgroup | |
470 | * makes up the outer beam grouping. */ | |
471 | if (ssv_p->nbeam == ssv_p->nsubbeam) { | |
472 | /* There are no sub-beam groupings anywhere in the measure */ | |
473 | return; | |
474 | } | |
475 | ||
476 | /* Find the subbeamlist entry that matches with the outer beam entry */ | |
477 | subgroup_time = Zero; | |
478 | for (sbi = 0; LT(subgroup_time, outer_time); sbi++) { | |
479 | subgroup_time = radd(subgroup_time, ssv_p->subbeamstlist[sbi]); | |
480 | } | |
481 | ||
482 | if ( EQ(ssv_p->beamstlist[obi], ssv_p->subbeamstlist[sbi]) ) { | |
483 | /* Outer and subbeam have the same time duration, | |
484 | * so there aren't any subgroups in this outer beam grouping. */ | |
485 | return; | |
486 | } | |
487 | ||
488 | /* There are subgroups inside the outer beam grouping, | |
489 | * so we may need to set one or more breakbeams. */ | |
490 | subgroup_time = ssv_p->subbeamstlist[sbi]; | |
491 | ||
492 | /* If beam starts later than the outer beamstyle item begins, | |
493 | * (e.g., if there was a rest at the beginning of the beam time), | |
494 | * we have to count that time as already taken up from the subbeam. | |
495 | */ | |
496 | for (tot_time = Zero, gs_p = begin_gs_p->prev; gs_p != 0; | |
497 | gs_p = gs_p->prev) { | |
498 | tot_time = radd(tot_time, gs_p->fulltime); | |
499 | } | |
500 | tot_time = rsub(tot_time, outer_time); | |
501 | last_notegroup_p = 0; | |
502 | for (gs_p = begin_gs_p; gs_p != end_gs_p; gs_p = gs_p->next) { | |
503 | ||
504 | /* Grace notes are irrelevant */ | |
505 | if (gs_p->grpvalue == GV_ZERO) { | |
506 | continue; | |
507 | } | |
508 | ||
509 | /* Remember where last note group is, in case we | |
510 | * need to set breakbeam on it. */ | |
511 | if (gs_p->grpcont == GC_NOTES) { | |
512 | last_notegroup_p = gs_p; | |
513 | } | |
514 | ||
515 | /* Add up group time values until the total equals | |
516 | * or exceeds that of the subgroup. */ | |
517 | tot_time = radd(tot_time, gs_p->fulltime); | |
518 | if (LT(tot_time, subgroup_time)) { | |
519 | /* not far enough yet */ | |
520 | continue; | |
521 | } | |
522 | ||
523 | /* If the value exceeds, there is a note spanning the | |
524 | * subgroup boundary, so just ignore the subgrouping. */ | |
525 | if (GT(tot_time, subgroup_time)) { | |
526 | tot_time = rsub(tot_time, subgroup_time); | |
527 | subgroup_time = ssv_p->subbeamstlist[++sbi]; | |
528 | if (gs_p->grpcont != GC_NOTES) { | |
529 | last_notegroup_p = 0; | |
530 | } | |
531 | } | |
532 | ||
533 | else { | |
534 | /* A group ends right at the subbeam boundary. | |
535 | * Set breakbeam on last group, if there was one. | |
536 | */ | |
537 | if (last_notegroup_p != 0) { | |
538 | last_notegroup_p->breakbeam = YES; | |
539 | } | |
540 | ||
541 | /* The current subbeam is finished. | |
542 | * Move on to the next subbeam, if there is one. */ | |
543 | if (++sbi < ssv_p->nsubbeam) { | |
544 | subgroup_time = ssv_p->subbeamstlist[sbi]; | |
545 | /* Since we know the subbeam we just | |
546 | * finished ended exactly | |
547 | * at the subbeam boundary, | |
548 | * we set to time taken up so far | |
549 | * by the new subbeam to zero. | |
550 | */ | |
551 | tot_time = Zero; | |
552 | last_notegroup_p = 0; | |
553 | } | |
554 | } | |
555 | } | |
556 | } | |
557 | \f | |
558 | ||
559 | /* alt groups must always have beamloc set, so fix them */ | |
560 | ||
561 | void | |
562 | set_alt_beams(gs_p) | |
563 | ||
564 | struct GRPSYL *gs_p; /* a measure's worth of GRPSYLs for a voice */ | |
565 | ||
566 | { | |
567 | struct GRPSYL *other_gs_p; /* group on other end of alt pair */ | |
568 | ||
569 | ||
570 | debug(4, "set_alt_beams file=%s line=%d", | |
571 | gs_p->inputfile, gs_p->inputlineno); | |
572 | ||
573 | /* walk through the list, fixing any alt groups */ | |
574 | for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) { | |
575 | ||
576 | /* check if is an alt pair */ | |
577 | if (gs_p->slash_alt < 0) { | |
578 | ||
579 | if (gs_p->next == (struct GRPSYL *) 0) { | |
580 | /* no second group in alt, will be flagged | |
581 | * elsewhere */ | |
582 | continue; | |
583 | } | |
584 | ||
585 | /* set the pair as a beam group */ | |
586 | gs_p->beamloc = STARTITEM; | |
587 | gs_p->next->beamloc = ENDITEM; | |
588 | ||
589 | slopelencheck(gs_p, gs_p->next, "alt"); | |
590 | ||
591 | /* middle phase wants to have both notes in an alt group | |
592 | * have their alt field set, so do that */ | |
593 | gs_p->next->slash_alt = gs_p->slash_alt; | |
594 | ||
595 | /* adjust preceeding and following groups if necessary. | |
596 | * If was already in a beam group, split off the other | |
597 | * parts into their own groups or put flags on the | |
598 | * extras if they are down to one group */ | |
599 | ||
600 | /* find previous normal group if any and adjust */ | |
601 | for (other_gs_p = gs_p->prev; | |
602 | other_gs_p != (struct GRPSYL *) 0; | |
603 | other_gs_p = other_gs_p->prev) { | |
604 | ||
605 | if (other_gs_p->grpvalue != GV_ZERO) { | |
606 | if (other_gs_p->grpcont == GC_REST) { | |
607 | other_gs_p->beamloc = NOITEM; | |
608 | } | |
609 | else { | |
610 | break; | |
611 | } | |
612 | } | |
613 | } | |
614 | ||
615 | if (other_gs_p != (struct GRPSYL *) 0) { | |
616 | ||
617 | switch (other_gs_p->beamloc) { | |
618 | ||
619 | case INITEM: | |
620 | other_gs_p->beamloc = ENDITEM; | |
621 | break; | |
622 | ||
623 | case STARTITEM: | |
624 | other_gs_p->beamloc = NOITEM; | |
625 | break; | |
626 | ||
627 | default: | |
628 | break; | |
629 | } | |
630 | } | |
631 | ||
632 | /* now do the same for the following group */ | |
633 | for (other_gs_p = gs_p->next->next; | |
634 | other_gs_p != (struct GRPSYL *) 0; | |
635 | other_gs_p = other_gs_p->next) { | |
636 | ||
637 | if (other_gs_p->grpvalue != GV_ZERO) { | |
638 | if (other_gs_p->grpcont == GC_REST) { | |
639 | other_gs_p->beamloc = NOITEM; | |
640 | } | |
641 | else { | |
642 | break; | |
643 | } | |
644 | } | |
645 | } | |
646 | ||
647 | if (other_gs_p != (struct GRPSYL *) 0) { | |
648 | ||
649 | switch (other_gs_p->beamloc) { | |
650 | ||
651 | case INITEM: | |
652 | other_gs_p->beamloc = STARTITEM; | |
653 | break; | |
654 | ||
655 | case ENDITEM: | |
656 | other_gs_p->beamloc = NOITEM; | |
657 | break; | |
658 | ||
659 | default: | |
660 | break; | |
661 | } | |
662 | } | |
663 | ||
664 | /* skip over the second in the pair */ | |
665 | gs_p = gs_p->next; | |
666 | } | |
667 | } | |
668 | } | |
669 | \f | |
670 | ||
671 | /* Given a list of GRPSYLs on a visible voice | |
672 | * having "bm with staff below" do all the error checking. | |
673 | * This list of groups has to be for the bottom visible voice | |
674 | * for the duration of the beam. | |
675 | * There has to be a set of groups on the top non-space visible | |
676 | * voice of the first visible staff below, | |
677 | * which starts a "bm with staff above" at exactly | |
678 | * the same time value. The ebm values also have to match. At every | |
679 | * point inside the beam, one voice must have notes and the other voice | |
680 | * must have spaces. | |
681 | * | |
682 | * Have to be careful in this function, | |
683 | * because the gs_p->staffno and gs_p->vno may not be filled in yet, | |
684 | * so have to use the staffno from the STAFF struct, and get vno from the | |
685 | * first gs_p, which the caller is supposed to have filled in correctly. | |
686 | * | |
687 | * Returns the staff number of the staff containing the matching | |
688 | * "bm with staff above" or -1 if no such staff was found. | |
689 | */ | |
690 | ||
691 | int | |
692 | chk_crossbeam(gs_p, mll_p) | |
693 | ||
694 | struct GRPSYL *gs_p; /* first group in above voice of cross staff beam */ | |
695 | struct MAINLL *mll_p; /* gs_p hangs off of here */ | |
696 | ||
697 | { | |
698 | struct GRPSYL *g_p; /* for walking through group list */ | |
699 | struct GRPSYL *other_p; /* group on other staff */ | |
700 | struct MAINLL *assoc_mll_p; /* other staff hangs off of here */ | |
701 | struct GRPSYL *assoc_grps_p; /* the measure-worth of groups in | |
702 | * the voice being beamed to */ | |
703 | RATIONAL start_time, end_time; /* of the above voice */ | |
704 | RATIONAL other_start, other_end;/* time of groups on below staff */ | |
705 | int user_specified_stem_len; /* YES or NO */ | |
706 | int size = GS_NORMAL; | |
707 | int assoc_vno; /* voice number on below staff */ | |
708 | struct STAFF *staff_p; | |
709 | int staffno; | |
710 | int vno; | |
711 | ||
712 | ||
713 | /* only the first gs_p is guaranteed to have the right vno at this | |
714 | * point, so save that. */ | |
715 | vno = gs_p->vno; | |
716 | staff_p = mll_p->u.staff_p; | |
717 | staffno = staff_p->staffno; | |
718 | ||
719 | /* find where in the measure the beam begins, by adding up the | |
720 | * time values of all the groups prior to the first beamed group */ | |
721 | for (start_time = Zero, g_p = gs_p->prev; g_p != (struct GRPSYL *) 0; | |
722 | g_p = g_p->prev) { | |
723 | start_time = radd(start_time, g_p->fulltime); | |
724 | } | |
725 | ||
726 | /* find how long the beam lasts. Also see if there are any small | |
727 | * groups */ | |
728 | for (end_time = start_time, g_p = gs_p; g_p != (struct GRPSYL *) 0; | |
729 | g_p = g_p->next) { | |
730 | /* accumulate the time */ | |
731 | end_time = radd(end_time, g_p->fulltime); | |
732 | ||
733 | /* check for small groups */ | |
734 | if (g_p->grpcont == GC_NOTES && g_p->grpsize == GS_SMALL | |
735 | && g_p->grpvalue != GV_ZERO) { | |
736 | size= GS_SMALL; | |
737 | } | |
738 | ||
739 | /* end of the beam? */ | |
740 | if (g_p->beamloc == ENDITEM && g_p->grpvalue != GV_ZERO) { | |
741 | break; | |
742 | } | |
743 | } | |
744 | if (g_p == (struct GRPSYL *) 0) { | |
745 | /* maybe this should be silent, since another error message | |
746 | * should already be printed, but this will point out that | |
747 | * the problem was on a cross-staff beam */ | |
748 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
749 | "can't find end of cross-staff beam"); | |
750 | return(-1); | |
751 | } | |
752 | ||
753 | /* Make sure this is the bottom voice of the above staff. | |
754 | * If it's voice 2 (subscript 1) then it is for sure. | |
755 | * Otherwise, have to make sure the second and third voices, if any, | |
756 | * have all spaces for the duration of the cross-staff beam */ | |
757 | if (vno != 1) { | |
758 | /* If voice 2 is visible and not all spaces, | |
759 | * or if we are working on voice 1 while voice 3 is | |
760 | * visible and not all space, there is a problem. */ | |
761 | if (vvpath(staffno, 2, VISIBLE)->visible == YES && | |
762 | hasspace(staff_p->groups_p[1], start_time, | |
763 | end_time) == NO || (vno == 0 && | |
764 | vvpath(staffno, 3, VISIBLE)->visible == YES && | |
765 | hasspace(mll_p->u.staff_p->groups_p[2], | |
766 | start_time, end_time) == NO)) { | |
767 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
768 | "cross-staff beam must be from bottom voice of staff %d", | |
769 | mll_p->u.staff_p->staffno); | |
770 | return(-1); | |
771 | } | |
772 | } | |
773 | ||
774 | /* Find the associated voice, and the associated bm group | |
775 | * in that voice. First find the next visible staff */ | |
776 | for (assoc_mll_p = mll_p->next; ; assoc_mll_p = assoc_mll_p->next) { | |
777 | if (assoc_mll_p == (struct MAINLL *) 0 || | |
778 | assoc_mll_p->str != S_STAFF) { | |
779 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
780 | "no visible staff below to beam with"); | |
781 | return(-1); | |
782 | } | |
783 | ||
784 | if (svpath(assoc_mll_p->u.staff_p->staffno, VISIBLE)->visible | |
785 | == YES) { | |
786 | /* found the right staff */ | |
787 | break; | |
788 | } | |
789 | } | |
790 | ||
791 | /* Associated voice is probably voice 1 of the below staff. | |
792 | * But there is a slight possibility it is voice 2, or even voice 3. | |
793 | * Skip over voices that are all spaces for the duration of the beam. | |
794 | * Since voice 3 is the "middle" voice, we check 1, then 3, then 2. | |
795 | */ | |
796 | if (vvpath(staffno, 1, VISIBLE)->visible == YES && | |
797 | hasspace(assoc_mll_p->u.staff_p->groups_p[0], | |
798 | start_time, end_time) == NO) { | |
799 | assoc_grps_p = assoc_mll_p->u.staff_p->groups_p[0]; | |
800 | assoc_vno = 0; | |
801 | } | |
802 | else if (vvpath(staffno, 3, VISIBLE)->visible == YES && | |
803 | hasspace(assoc_mll_p->u.staff_p->groups_p[2], | |
804 | start_time, end_time) == NO) { | |
805 | assoc_grps_p = assoc_mll_p->u.staff_p->groups_p[2]; | |
806 | assoc_vno = 2; | |
807 | } | |
808 | else if (vvpath(staffno, 2, VISIBLE)->visible == YES && | |
809 | hasspace(assoc_mll_p->u.staff_p->groups_p[1], | |
810 | start_time, end_time) == NO) { | |
811 | assoc_grps_p = assoc_mll_p->u.staff_p->groups_p[1]; | |
812 | assoc_vno = 1; | |
813 | } | |
814 | else { | |
815 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
816 | "cross-staff beam has no notes on staff %d", | |
817 | assoc_mll_p->u.staff_p->staffno); | |
818 | return(-1); | |
819 | } | |
820 | ||
821 | /* Tab staffs can't be involved in cross-staff beaming */ | |
822 | if (is_tab_staff(mll_p->u.staff_p->staffno) || | |
823 | is_tab_staff(assoc_mll_p->u.staff_p->staffno)) { | |
824 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
825 | "cross-staff beaming not allowed on tab staff"); | |
826 | return(-1); | |
827 | } | |
828 | ||
829 | /* We don't allow the different staffs to have different staffscale | |
830 | * values: it doesn't really make much sense to allow it, and avoids | |
831 | * all the issues like how wide to make the beams. | |
832 | */ | |
833 | if (svpath(mll_p->u.staff_p->staffno, STAFFSCALE)->staffscale != | |
834 | svpath(assoc_mll_p->u.staff_p->staffno, | |
835 | STAFFSCALE)->staffscale) { | |
836 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
837 | "staffs involved with cross-staff beams must have identical staffscale values"); | |
838 | /* We did find which to associate with, even though | |
839 | * its staffscale was wrong. */ | |
840 | return(assoc_mll_p->u.staff_p->staffno); | |
841 | } | |
842 | ||
843 | /* find the group that ought to be the "bm with staff above" group, by | |
844 | * going that far time-wise into the measure on the associated voice */ | |
845 | for (other_start = Zero, other_p = assoc_grps_p; | |
846 | other_p != (struct GRPSYL *) 0; | |
847 | other_p = other_p->next) { | |
848 | ||
849 | if (GT(other_start, start_time)) { | |
850 | /* too far. pretend to be at end of list so we | |
851 | * and fall out of loop to print the error message | |
852 | * for this case */ | |
853 | other_p = (struct GRPSYL *) 0; | |
854 | break; | |
855 | } | |
856 | ||
857 | if (EQ(other_start, start_time)) { | |
858 | /* found it! */ | |
859 | break; | |
860 | } | |
861 | ||
862 | if (other_p->grpvalue == GV_ZERO) { | |
863 | continue; | |
864 | } | |
865 | ||
866 | /* have to keep going. Keep track of how far we are in time */ | |
867 | other_start = radd(other_start, other_p->fulltime); | |
868 | } | |
869 | ||
870 | /* skip past any grace groups */ | |
871 | while (other_p != 0 && other_p->grpvalue == GV_ZERO) { | |
872 | other_p = other_p->next; | |
873 | } | |
874 | ||
875 | /* If we didn't find a voice below, or that voice's group | |
876 | * isn't the start of a beam with above, there is a problem. | |
877 | * In the second case, maybe user really meant to beam with some | |
878 | * lower voice, but that would collide, which we don't allow. | |
879 | */ | |
880 | if (other_p == (struct GRPSYL *) 0 || other_p->beamloc != STARTITEM | |
881 | || other_p->beamto != CS_ABOVE) { | |
882 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
883 | "'bm with staff below' has no matching 'bm with staff above' (may be missing, invisible, or on wrong voice)"); | |
884 | return(assoc_mll_p->u.staff_p->staffno); | |
885 | } | |
886 | ||
887 | /* check if user specified a stem length on the first group */ | |
888 | if (IS_STEMLEN_KNOWN(gs_p->stemlen) | |
889 | || IS_STEMLEN_KNOWN(other_p->stemlen)) { | |
890 | user_specified_stem_len = YES; | |
891 | } | |
892 | else { | |
893 | user_specified_stem_len = NO; | |
894 | } | |
895 | ||
896 | /* go through the two voices. For each note group, verify that | |
897 | * the other voice has space during that time period. Do the "other" | |
898 | * staff first, because in a previous version of this function it | |
899 | * had to be done in that order to avoid possible null pointer | |
900 | * dereference. Now things have changed, so that doesn't matter | |
901 | * any more, but I don't want to change the order, to make sure I | |
902 | * don't break something. | |
903 | */ | |
904 | other_p = verify_crossbeam(other_p, | |
905 | mll_p->u.staff_p->groups_p[vno], start_time, | |
906 | &other_end, assoc_mll_p->u.staff_p->staffno, size); | |
907 | gs_p = verify_crossbeam(gs_p, assoc_grps_p, start_time, &end_time, | |
908 | mll_p->u.staff_p->staffno, size); | |
909 | ||
910 | /* we should be pointing to the ebm group for each staff, | |
911 | * unless of course, something went wrong, like user didn't | |
912 | * specify an ebm */ | |
913 | if (gs_p == (struct GRPSYL *) 0 || other_p == (struct GRPSYL *) 0) { | |
914 | /* maybe this should be silent, since another error message | |
915 | * should already be printed, but this will point out that | |
916 | * the problem was on a cross-staff beam */ | |
917 | l_yyerror(assoc_grps_p->inputfile, assoc_grps_p->inputlineno, | |
918 | "failed to find ebm for cross-staff beam"); | |
919 | return(assoc_mll_p->u.staff_p->staffno); | |
920 | } | |
921 | ||
922 | if (NE(end_time, other_end)) { | |
923 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
924 | "ebm not at same time in measure for both voices of cross-staff beam"); | |
925 | } | |
926 | ||
927 | /* Disallow illegal combinations of slope and stem length */ | |
928 | slopelencheck(gs_p, other_p, "beam"); | |
929 | ||
930 | return(assoc_mll_p->u.staff_p->staffno); | |
931 | } | |
932 | \f | |
933 | ||
934 | /* Given the first group of a cross-staff beam, and the beginning of the | |
935 | * list of GRPSYLs in the associated voice (the voice beamed to), and the | |
936 | * time into the measure where the beam starts, check each group. Verify | |
937 | * that each GC_NOTES group has GC_SPACE in the other voice and vice-versa. | |
938 | * Also check that all note groups are the same size, and mark the spaces | |
939 | * as the correct size so that everything in the beam has the same size. | |
940 | * Return a pointer to the last group in the beam (null if something goes | |
941 | * wrong). Also, return the time into the measure of the end of the beam, | |
942 | * via the end_time_p pointer. | |
943 | */ | |
944 | ||
945 | static struct GRPSYL * | |
946 | verify_crossbeam(gs_p, other_gs_p, start_time, end_time_p, staffno, size) | |
947 | ||
948 | struct GRPSYL *gs_p; /* first group in list to be checked */ | |
949 | struct GRPSYL *other_gs_p; /* the groups_p of the associated voice */ | |
950 | RATIONAL start_time; /* when the beam begins */ | |
951 | RATIONAL *end_time_p; /* time through end of beam will be returned here */ | |
952 | int staffno; | |
953 | int size; /* GS_NORMAL or GS_SMALL */ | |
954 | ||
955 | { | |
956 | RATIONAL end_time; | |
957 | int has_at_least_1_note_group = NO; | |
958 | ||
959 | ||
960 | /* go through each group in the beam */ | |
961 | for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) { | |
962 | ||
963 | /* skip over any grace groups */ | |
964 | if (gs_p->grpvalue == GV_ZERO) { | |
965 | continue; | |
966 | } | |
967 | ||
968 | /* find the end time of the group, for passing to hasspace() */ | |
969 | end_time = radd(start_time, gs_p->fulltime); | |
970 | ||
971 | /* if notes, other voice must have space */ | |
972 | if (gs_p->grpcont == GC_NOTES) { | |
973 | if (hasspace(other_gs_p, start_time, end_time) == NO) { | |
974 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
975 | "cross-staff beam must always have notes in one voice and space in the other voice"); | |
976 | return (struct GRPSYL *) 0; | |
977 | } | |
978 | has_at_least_1_note_group = YES; | |
979 | if (gs_p->grpsize != size) { | |
980 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
981 | "can't mix normal and cue size chords in cross-staff beam"); | |
982 | } | |
983 | } | |
984 | ||
985 | /* conversely, if space, other voice must not have space */ | |
986 | else if (gs_p->grpcont == GC_SPACE) { | |
987 | struct GRPSYL *g_p; | |
988 | RATIONAL t; | |
989 | int oldcont = GC_SPACE; | |
990 | ||
991 | /* This is somewhat like hasspace() except that checks | |
992 | * that the entire duration is space. Here we need | |
993 | * to check if there is space at least somewhere during | |
994 | * the time period. If so, user error. | |
995 | */ | |
996 | for (g_p = other_gs_p, t = Zero; LT(t, start_time); | |
997 | g_p = g_p->next) { | |
998 | if (g_p->grpvalue == GV_ZERO) { | |
999 | continue; | |
1000 | } | |
1001 | t = radd(t, g_p->fulltime); | |
1002 | oldcont = g_p->grpcont; | |
1003 | } | |
1004 | if (GT(t, start_time) && oldcont == GC_SPACE) { | |
1005 | /* group spilling into this time is space */ | |
1006 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
1007 | "cross-staff beam must always have notes in one voice and space in the other voice"); | |
1008 | return (struct GRPSYL *) 0; | |
1009 | } | |
1010 | for ( ; g_p != 0 && LT(t, end_time); g_p = g_p->next) { | |
1011 | if (g_p->grpvalue == GV_ZERO) { | |
1012 | continue; | |
1013 | } | |
1014 | if (g_p->grpcont == GC_SPACE) { | |
1015 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
1016 | "cross-staff beam must always have notes in one voice and space in the other voice"); | |
1017 | return (struct GRPSYL *) 0; | |
1018 | } | |
1019 | t = radd(t, g_p->fulltime); | |
1020 | } | |
1021 | ||
1022 | /* mark size of spaces. Normally space can't be cue * size, but in this case, it makes it easier for later | |
1023 | * code (in print phrase at least) if everything in the | |
1024 | * beam--even spaces--is marked as cue size */ | |
1025 | gs_p->grpsize = size; | |
1026 | } | |
1027 | ||
1028 | /* esbm is not currently allowed on cross-staff beams. | |
1029 | * It would much more complicated than normal beams, | |
1030 | * because the "primary" beam might perhaps best be the top, | |
1031 | * the bottom, or the middle, depending on where the notes are. | |
1032 | * Placement and print phase would have to know about that, | |
1033 | * so that stems could be adjusted properly, | |
1034 | * and beams drawn in the right places. | |
1035 | */ | |
1036 | if (gs_p->breakbeam == YES) { | |
1037 | l_warning(gs_p->inputfile, gs_p->inputlineno, | |
1038 | "esbm is not supported on cross-staff beams; being ignored"); | |
1039 | gs_p->breakbeam = NO; | |
1040 | } | |
1041 | ||
1042 | /* see if we reached the end of the beam */ | |
1043 | if (gs_p->beamloc == ENDITEM) { | |
1044 | *end_time_p = end_time; | |
1045 | if (has_at_least_1_note_group == NO) { | |
1046 | l_yyerror(gs_p->inputfile, gs_p->inputlineno, | |
1047 | "cross-staff beam has no notes on staff %d", | |
1048 | staffno); | |
1049 | } | |
1050 | ||
1051 | return(gs_p); | |
1052 | } | |
1053 | ||
1054 | /* arrange for next time through the loop, by moving the | |
1055 | * start_time to the next group */ | |
1056 | start_time = end_time; | |
1057 | } | |
1058 | ||
1059 | /* failed to find an ebm */ | |
1060 | return (struct GRPSYL *) 0; | |
1061 | } | |
1062 | \f | |
1063 | ||
1064 | /* User is not allowed to specify length on both ends of a beam along with | |
1065 | * a slope, because they could becontradictory. */ | |
1066 | ||
1067 | static void | |
1068 | slopelencheck(first_p, last_p, bmtype) | |
1069 | ||
1070 | struct GRPSYL *first_p; /* first beamed group */ | |
1071 | struct GRPSYL *last_p; /* last beamed group */ | |
1072 | char *bmtype; /* "beam" or "alt" */ | |
1073 | ||
1074 | { | |
1075 | if (IS_STEMLEN_KNOWN(first_p->stemlen) == YES && | |
1076 | IS_STEMLEN_KNOWN(last_p->stemlen) == YES && | |
1077 | fabs(first_p->beamslope - NOBEAMANGLE) > 0.001) { | |
1078 | l_yyerror(last_p->inputfile, last_p->inputlineno, | |
1079 | "can't specify both end stem lengths and slope for %s", | |
1080 | bmtype); | |
1081 | } | |
1082 | } |