chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / plutils.c
CommitLineData
69695f33
MW
1/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
2 * 2005, 2006 by Arkkra Enterprises */
3/* All rights reserved */
4/*
5 * Name: plutils.c
6 *
7 * Description: This file contains utility functions belonging to the placement
8 * phase. Some of them are also used by other phases.
9 */
10
11#include "defines.h"
12#include "structs.h"
13#include "globals.h"
14
15static int phrase_tieslur_note P((struct GRPSYL *gs_p, int nidx, int side,
16 int interfere));
17static int tied_to_nidx P((struct GRPSYL *gs_p, int nidx));
18static int slurred_to_nidx P((struct GRPSYL *gs_p, int nidx, int sidx));
19static RATIONAL lefttime P((double count, struct GRPSYL *firstgs_p,
20 int timeden));
21static RATIONAL righttime P((double count, struct GRPSYL *firstgs_p,
22 int timeden));
23
24/*
25 * Name: nextnongrace()
26 *
27 * Abstract: Return next nongrace group in a GRPSYL list.
28 *
29 * Returns: pointer to GRPSYL of next nongrace group, 0 if none
30 *
31 * Description: This function loops down the GRPSYL linked list from the given
32 * starting point. It returns the next nongrace GRPSYL, or 0
33 * if none.
34 */
35
36struct GRPSYL *
37nextnongrace(gs_p)
38
39struct GRPSYL *gs_p; /* current group */
40
41{
42 gs_p = gs_p->next;
43 while (gs_p != 0 && gs_p->grpvalue == GV_ZERO)
44 gs_p = gs_p->next;
45 return (gs_p);
46}
47\f
48/*
49 * Name: prevnongrace()
50 *
51 * Abstract: Return previous nongrace group in a GRPSYL list.
52 *
53 * Returns: pointer to GRPSYL of previous nongrace group, 0 if none
54 *
55 * Description: This function loop up the GRPSYL linked list from the given
56 * starting point. It returns the previous nongrace GRPSYL, or 0
57 * if none.
58 */
59
60struct GRPSYL *
61prevnongrace(gs_p)
62
63struct GRPSYL *gs_p; /* current group */
64
65{
66 gs_p = gs_p->prev;
67 while (gs_p != 0 && gs_p->grpvalue == GV_ZERO)
68 gs_p = gs_p->prev;
69 return (gs_p);
70}
71\f
72/*
73 * Name: nextglobnongrace()
74 *
75 * Abstract: Return next nongrace group in this voice.
76 *
77 * Returns: pointer to GRPSYL of next nongrace group, 0 if none
78 *
79 * Description: This function, given a nongrace and the MLL structure it hangs
80 * off of, returns the next nongrace in this voice, even if it's in
81 * the next measure. If it is in the next measure, *mll_p_p gets
82 * updated. But if that next measure is a second or later ending,
83 * it's not considered to be a "next" measure, so return 0.
84 */
85
86struct GRPSYL *
87nextglobnongrace(gs_p, mll_p_p)
88
89struct GRPSYL *gs_p; /* current group */
90struct MAINLL **mll_p_p; /* MLL structure it is hanging off of */
91
92{
93 do {
94 gs_p = nextgrpsyl(gs_p, mll_p_p);
95 } while (gs_p != 0 && gs_p->grpvalue == GV_ZERO);
96 return (gs_p);
97}
98\f
99/*
100 * Name: prevglobnongrace()
101 *
102 * Abstract: Return previous nongrace group in this voice.
103 *
104 * Returns: pointer to GRPSYL of previous nongrace group, 0 if none
105 *
106 * Description: This function, given a nongrace and the MLL structure it hangs
107 * off of, returns the prev nongrace in this voice, even if it's
108 * in an earlier measure. If we are at the start of an ending,
109 * it skips over any previous ending and goes to the measure
110 * preceding the first ending. If the resulting nongrace is in a
111 * previous measure, *mll_p_p gets updated.
112 */
113
114struct GRPSYL *
115prevglobnongrace(gs_p, mll_p_p)
116
117struct GRPSYL *gs_p; /* current group */
118struct MAINLL **mll_p_p; /* MLL structure it is hanging off of */
119
120{
121 do {
122 gs_p = prevgrpsyl(gs_p, mll_p_p);
123 } while (gs_p != 0 && gs_p->grpvalue == GV_ZERO);
124 return (gs_p);
125}
126\f
127/*
128 * Name: drmo()
129 *
130 * Abstract: Detect rightmost one.
131 *
132 * Returns: void
133 *
134 * Description: This function returns the bit position of the rightmost bit
135 * that is a 1 in the given number, the low order bit being
136 * bit 0. The given number must not be 0.
137 */
138
139int
140drmo(num)
141
142register int num;
143
144{
145 register int n;
146
147 for (n = 0; n < 8 * sizeof(int); n++) {
148 if ( (num & (1 << n)) != 0 )
149 return (n);
150 }
151 pfatal("0 was passed to drmo");
152 return (0); /* dead code, but keeps lint happy */
153}
154\f
155/*
156 * Name: tieslurpad()
157 *
158 * Abstract: How much tie/slur padding is needed after this group?
159 *
160 * Returns: Padding in inches.
161 *
162 * Description: This function returns the amount of padding needed after a
163 * group due to ties or slurs, if the given group is tied to the
164 * next group, or any note in it is tied or slurred to a note
165 * in the following group. Otherwise it returns zero.
166 * NOTE: This function ignores staffscale.
167 */
168
169double
170tieslurpad(staff_p, gs_p)
171
172struct STAFF *staff_p; /* the staff the group is connected to */
173struct GRPSYL *gs_p; /* the group after which padding may occur */
174
175{
176 struct NOTE *note_p; /* point at a note structure */
177 struct GRPSYL *gtemp_p; /* temp GRPSYL pointer */
178 struct GRPSYL *this_p; /* first GRPSYL in this voice */
179 struct GRPSYL *that_p; /* first GRPSYL in other voice */
180 RATIONAL starttime; /* time into measure where *gs_p starts */
181 float pad; /* how much padding is needed */
182 int interfere; /* does other voice have notes/rests here? */
183 int stepdiff; /* vertical reach of a curve */
184 int n; /* index into notes in group */
185 int s; /* index into notes slurred to */
186
187
188 /* syllables can't have ties or slurs */
189 if (gs_p->grpsyl != GS_GROUP)
190 return (0);
191
192 /* rests and spaces can't have ties or slurs */
193 if (gs_p->grpcont != GC_NOTES)
194 return (0);
195
196 /* if last group in measure, don't need any more space */
197 if (gs_p->next == 0)
198 return (0);
199
200 /*
201 * Find the first group in this measure, and total time preceding the
202 * group we were given. We need this to figure out which voice we are
203 * in, and, if there is another voice, whether it has only spaces
204 * during the time of our group, which affects how the curves should
205 * look.
206 */
207 starttime = Zero;
208 for (gtemp_p = gs_p->prev, this_p = gs_p; gtemp_p != 0;
209 this_p = gtemp_p, gtemp_p = gtemp_p->prev)
210 starttime = radd(starttime, gtemp_p->fulltime);
211
212 /* point at other voice, or null pointer if none */
213 if (staff_p->groups_p[0] == this_p)
214 that_p = staff_p->groups_p[1]; /* might be 0 */
215 else if (staff_p->groups_p[1] == this_p)
216 that_p = staff_p->groups_p[0]; /* might be 0 */
217 else
218 that_p = 0; /* we are voice 3, ignore other voices */
219
220 if (that_p == 0 || hasspace(that_p, starttime,
221 radd(starttime, gs_p->fulltime)))
222 interfere = NO;
223 else
224 interfere = YES;
225
226 pad = 0; /* start with no padding */
227
228 /*
229 * Loop through every note in this group. If it's tied, check each
230 * note to see if either it or the note it's tied to is ineligible for
231 * phrase-like curves. If so, there will be a horizontally aligned
232 * curve, and we need to pad. The note must be the same in both
233 * groups, so there's no need to consider vertical distances at this
234 * point. Then loop through the 0 or more slurs from this note to
235 * note(s) in the next group. For each one, find the vertical distance
236 * between the two notes. The padding it needs is based on this and on
237 * whether phrase-like curves can be drawn. Keep track of the maximum
238 * padding needed by any pair of notes.
239 * We also need to pad if the stems are UP-DOWN, because that leaves no
240 * room for the curve.
241 */
242 for (n = 0; n < gs_p->nnotes; n++) {
243 note_p = &gs_p->notelist[n];
244 if (note_p->tie == YES) {
245 if (gs_p->stemdir == UP && gs_p->next->stemdir == DOWN
246 || phrase_tieslur_note(gs_p, n, STARTITEM, interfere)
247 == NO || phrase_tieslur_note(gs_p->next, tied_to_nidx(
248 gs_p, n), ENDITEM, interfere) == NO)
249 pad = MAX(pad, TIESLURPAD);
250 }
251 for (s = 0; s < note_p->nslurto; s++) {
252 /*
253 * If it's a slur to/from nowhere, don't deal with it
254 * here. It is considered along with the width of the
255 * individual note.
256 */
257 if (IS_NOWHERE(note_p->slurtolist[s].octave))
258 continue; /* from nowhere */
259
260 stepdiff = abs(
261 ( note_p->octave * 7 +
262 Letshift[ note_p->letter - 'a' ] ) -
263 ( note_p->slurtolist[s].octave * 7 +
264 Letshift[ note_p->slurtolist[s].letter
265 - 'a' ] )
266 );
267 if (gs_p->stemdir == UP && gs_p->next->stemdir == DOWN
268 || phrase_tieslur_note(gs_p, n, STARTITEM, interfere)
269 == NO || phrase_tieslur_note(gs_p->next,
270 slurred_to_nidx(gs_p, n, s), ENDITEM, interfere) == NO){
271 pad = MAX(pad, stepdiff <= 3 ? TIESLURPAD :
272 TIESLURPAD + (stepdiff - 3) * STEPSIZE / 2);
273 } else {
274 pad = MAX(pad, stepdiff <= 3 ? 0 :
275 (stepdiff - 3) * STEPSIZE / 2);
276 }
277 }
278 }
279
280 return (pad); /* max padding needed by any pair of notes */
281}
282\f
283/*
284 * Name: phrase_tieslur_note()
285 *
286 * Abstract: Is the given note the end note and eligible for "new" tie/slur?
287 *
288 * Returns: YES or NO
289 *
290 * Description: This function determines whether a tie or slur to/from the
291 * given note is to be drawn like a phrase mark (as opposed to
292 * drawing it vertically aligned with the note).
293 */
294
295static int
296phrase_tieslur_note(gs_p, nidx, side, interfere)
297
298struct GRPSYL *gs_p; /* point at note's group */
299int nidx; /* index to this note in notelist */
300int side; /* STARTITEM (curve here to right) or ENDITEM */
301int interfere; /* does the other voice have notes/rests here?*/
302
303{
304 /* check for each bad condition, returning NO if it exists */
305
306 /* inner note of a group */
307 if (nidx != 0 && nidx != gs_p->nnotes - 1)
308 return (NO);
309
310 /* bottom note of voice 1 and other voice interferes */
311 if (gs_p->vno == 1 && nidx == gs_p->nnotes - 1 && interfere)
312 return (NO);
313
314 /* top note of voice 2 and other voice interferes */
315 if (gs_p->vno == 2 && nidx == 0 && interfere)
316 return (NO);
317
318 /* antistem note if "with" list is present */
319 /* (don't need to check normwith; if it weren't YES we would have */
320 /* returned above) */
321 if (gs_p->nwith != 0 && gs_p->stemdir == UP && nidx == gs_p->nnotes - 1)
322 return (NO);
323
324 /* antistem note if "with" list is present */
325 /* (don't need to check normwith; if it weren't YES we would have */
326 /* returned above) */
327 if (gs_p->nwith != 0 && gs_p->stemdir == DOWN && nidx == 0)
328 return (NO);
329
330 /* stem in the way of left end of curve */
331 if (side == STARTITEM && gs_p->basictime >= 2 && gs_p->stemdir == UP &&
332 nidx == 0 && gs_p->nnotes > 1)
333 return (NO);
334
335 /* stem in the way of right end of curve */
336 if (side == ENDITEM && gs_p->basictime >= 2 && gs_p->stemdir == DOWN &&
337 nidx == gs_p->nnotes - 1 && gs_p->nnotes > 1)
338 return (NO);
339
340 return (YES);
341}
342\f
343/*
344 * Name: tied_to_nidx()
345 *
346 * Abstract: Return the note index of the note the given note is tied to.
347 *
348 * Returns: index into gs_p->next->notelist
349 *
350 * Description: This function is given a valid group (not the last one in the
351 * measure) and an index into its notelist to a note that is tied
352 * to the next group. It returns the index into the next group's
353 * notelist to the note that the first group's note is tied to.
354 */
355
356static int
357tied_to_nidx(gs_p, nidx)
358
359struct GRPSYL *gs_p; /* point at note's group */
360int nidx; /* index to this note in notelist */
361
362{
363 struct NOTE *nl_ptr; /* ptr to next group's notelist */
364 int n;
365
366
367 nl_ptr = gs_p->next->notelist;
368
369 for (n = 0; n < gs_p->next->nnotes; n++) {
370 if (gs_p->notelist[nidx].letter == nl_ptr[n].letter &&
371 gs_p->notelist[nidx].octave == nl_ptr[n].octave)
372 return (n);
373 }
374
375 pfatal("tied_to_nidx: can't find note tied to");
376 return (0); /* to keep lint happy */
377}
378\f
379/*
380 * Name: slurred_to_nidx()
381 *
382 * Abstract: Return the note index of the note the given note is slurred to.
383 *
384 * Returns: index into gs_p->next->notelist
385 *
386 * Description: This function is given a valid group (not the last one in the
387 * measure) and an index into its notelist to a note that is tied
388 * to the next group. It returns the index into the next group's
389 * notelist to the note that the first group's note is tied to.
390 */
391
392static int
393slurred_to_nidx(gs_p, nidx, sidx)
394
395struct GRPSYL *gs_p; /* point at note's group */
396int nidx; /* index to this note in notelist */
397int sidx; /* index to slurred to note in slurto list */
398
399{
400 struct NOTE *nl_ptr; /* ptr to next group's notelist */
401 int n;
402
403
404 nl_ptr = gs_p->next->notelist;
405
406 for (n = 0; n < gs_p->next->nnotes; n++) {
407 if (gs_p->notelist[nidx].slurtolist[sidx].letter ==
408 nl_ptr[n].letter &&
409 gs_p->notelist[nidx].slurtolist[sidx].octave ==
410 nl_ptr[n].octave)
411 return (n);
412 }
413
414 pfatal("slurred_to_nidx: can't find note slurred to");
415 return (0); /* to keep lint happy */
416}
417\f
418/*
419 * Name: hasspace()
420 *
421 * Abstract: Finds out if the given voice has space during given time.
422 *
423 * Returns: YES or NO
424 *
425 * Description: This function is given a linked list of groups to check
426 * during a given time interval. If the list consists entirely
427 * of space(s) during the time interval, the function returns
428 * YES. Otherwise it returns NO. If vtime2 is greater than the
429 * length of a measure, the extra, nonexistent time is regarded
430 * as all spaces. If the linked list of groups doesn't exist
431 * (gs_p is a null pointer), the function returns YES, since
432 * there is nothing there but "space".
433 */
434
435int
436hasspace(gs_p, vtime, vtime2)
437
438register struct GRPSYL *gs_p; /* starts pointing at the first GRPSYL list */
439RATIONAL vtime, vtime2; /* time when to start and stop checking for space */
440
441{
442 RATIONAL t; /* accumulate time */
443 int oldcont; /* content of previous group */
444
445
446 /* "no linked list exists" counts as all spaces */
447 if (gs_p == 0)
448 return (YES);
449
450 oldcont = GC_SPACE; /* prevent useless 'used before set' warning */
451
452 /* accumulate time until crossing vtime boundary */
453 for (t = Zero; LT(t, vtime); gs_p = gs_p->next) {
454 if (gs_p->grpvalue == GV_ZERO)
455 continue;
456 t = radd(t, gs_p->fulltime);
457 oldcont = gs_p->grpcont;
458 }
459
460 if (GT(t, vtime) && oldcont != GC_SPACE)
461 return (NO);
462
463 for ( ; gs_p != 0 && LT(t, vtime2); gs_p = gs_p->next) {
464 if (gs_p->grpvalue == GV_ZERO)
465 continue;
466 if (gs_p->grpcont != GC_SPACE)
467 return (NO);
468 t = radd(t, gs_p->fulltime);
469 }
470
471 return (YES);
472}
473\f
474/*
475 * Name: closestgroup()
476 *
477 * Abstract: Find closest nongrace group in this voice to given time value.
478 *
479 * Returns: pointer to the closest nongrace GRPSYL
480 *
481 * Description: This function finds the GRPSYL in the given linked list that is
482 * closest, timewise, to the given count number, ignoring grace
483 * groups.
484 */
485
486struct GRPSYL *
487closestgroup(count, firstgs_p, timeden)
488
489double count; /* which count of the measure */
490struct GRPSYL *firstgs_p; /* first GRPSYL of relevant voice in measure */
491int timeden; /* denominator of current time signature */
492
493{
494 RATIONAL reqtime; /* time requested */
495 RATIONAL tottime; /* total time in measure so far */
496 RATIONAL otottime; /* old total time in measure so far */
497 struct GRPSYL *gs_p; /* point along group list */
498 struct GRPSYL *ogs_p; /* (old) point along group list */
499
500
501 /* skip over any initial grace groups */
502 if (firstgs_p->grpvalue == GV_ZERO)
503 firstgs_p = nextnongrace(firstgs_p);
504
505 /* if at or before the first count, it's closest to first group */
506 if (count <= 1)
507 return (firstgs_p);
508
509 /* get requested time to nearest tiny part of a count, in lowest terms*/
510 reqtime.n = 4 * MAXBASICTIME * (count - 1) + 0.5;
511 reqtime.d = 4 * MAXBASICTIME * timeden;
512 rred(&reqtime);
513
514 /*
515 * Loop through this voice accumulating time values. As soon as we
516 * equal or exceed the requested time value, check whether the
517 * requested time is closer to the new accumulated time, or that before
518 * this last group. Return the closest one.
519 */
520 tottime = Zero;
521 for (ogs_p = firstgs_p, gs_p = nextnongrace(ogs_p); gs_p != 0;
522 ogs_p = gs_p, gs_p = nextnongrace(gs_p)) {
523 otottime = tottime;
524 tottime = radd(tottime, ogs_p->fulltime);
525 if (GE(tottime, reqtime)) {
526 if (GT( rsub(reqtime,otottime), rsub(tottime,reqtime) ))
527 return (gs_p);
528 else
529 return (ogs_p);
530 }
531 }
532
533 /* requested time is after last group; return last group */
534 return (ogs_p);
535}
536\f
537/*
538 * Name: chkallspace()
539 *
540 * Abstract: Check if voice is all spaces for the voice this stuff is on.
541 *
542 * Returns: YES or NO
543 *
544 * Description: This function checks where one voice seems to be all spaces
545 * during the duration of a phrase mark, or other stuff which must
546 * be associated with a definite group. The tricky thing is that
547 * until we've decided which voice the stuff is intended to
548 * apply to, we don't exactly know what the endpoints of the
549 * stuff are going to be. All we know is the "count" values the
550 * user asked for, which may or may not equal the positions of
551 * GRPSYLs in the voices. So we look at voices 1 and 2 and take
552 * the worst (widest) case as the endpoints. (This is called only
553 * when both of these voices exist. We ignore any voice 3.)
554 */
555
556int
557chkallspace(msbeg_p, stuff_p, vno)
558
559struct MAINLL *msbeg_p; /* staff at beginning of the stuff */
560struct STUFF *stuff_p; /* the STUFF */
561int vno; /* voice being tested for being all spaces */
562
563{
564 static RATIONAL tiny = {1, 4 * MAXBASICTIME};
565 struct MAINLL *msend_p; /* staff at end of the phrase */
566 int timeden; /* denom of time sig at end of stuff */
567 RATIONAL begtime, endtime; /* time into measures of begin & end */
568 RATIONAL temptime; /* temp var for storing time */
569
570
571 /*
572 * Find what measure this stuff ends in. Along the way, keep
573 * track of the time signature denominator, in case it changes.
574 */
575 msend_p = getendstuff(msbeg_p, stuff_p, &timeden);
576
577 /*
578 * If we hit a multirest, bail out, arbitrarily returning NO. This
579 * stuff will be thrown away later anyway.
580 */
581 if (msend_p == 0)
582 return (NO);
583
584 /*
585 * If the second voice doesn't exist (because vscheme changed),
586 * it's like all spaces in that voice. So if we're asking about that
587 * voice, return YES. If asking about the first voice, return NO.
588 */
589 if (msend_p->u.staff_p->groups_p[1] == NULL) {
590 return (vno == 1 ? YES : NO);
591 }
592
593 /*
594 * Find time values that are sure to contain the stuff. Take the
595 * outermost values of the two voices.
596 */
597 begtime = lefttime(stuff_p->start.count,
598 msbeg_p->u.staff_p->groups_p[0], Score.timeden);
599 temptime = lefttime(stuff_p->start.count,
600 msbeg_p->u.staff_p->groups_p[1], Score.timeden);
601 if (LT(temptime, begtime))
602 begtime = temptime;
603 endtime = righttime(stuff_p->end.count,
604 msend_p->u.staff_p->groups_p[0], timeden);
605 temptime = righttime(stuff_p->end.count,
606 msend_p->u.staff_p->groups_p[1], timeden);
607 if (GT(temptime, endtime))
608 endtime = temptime;
609
610 /*
611 * If the beginning and end are in the same measure and at the same
612 * time, this phrase would normally be thrown away later, but we need
613 * to deal with it because the case of a phrase from a grace to its
614 * main note. It doesn't make sense to ask what a zero time contains,
615 * so to handle this, add a tiny time value to the end time.
616 */
617 if (msbeg_p == msend_p && EQ(begtime, endtime))
618 endtime = radd(endtime, tiny);
619
620 return (allspace(vno, msbeg_p, begtime, msend_p, endtime));
621}
622\f
623/*
624 * Name: allspace()
625 *
626 * Abstract: Finds out if the given voice has space for the given time.
627 *
628 * Returns: YES or NO
629 *
630 * Description: This function is a multi-measure version of hasspace(), and in
631 * fact works by calling hasspace() repeatedly. It is given the
632 * linked list of groups for the voice in the first measure in
633 * question. It checks whether the voice consists entirely of
634 * spaces from the duration point given for this first measure,
635 * until the endpoint, which may or may not be in the same measure.
636 */
637
638int
639allspace(vno, msbeg_p, begtime, msend_p, endtime)
640
641int vno; /* voice number, numbering from voice 1 == 0 */
642struct MAINLL *msbeg_p; /* point at MLL (staff) where duration begins */
643RATIONAL begtime; /* time where duration begins */
644struct MAINLL *msend_p; /* point at MLL (staff) where duration ends */
645RATIONAL endtime; /* time where duration ends */
646
647{
648 struct MAINLL *mainll_p; /* point along MLL */
649 int staffno;
650
651
652 /* if the time starts and ends in the same measure, let hasspace do it*/
653 if (msbeg_p == msend_p) {
654 return (hasspace(msbeg_p->u.staff_p->groups_p[vno],
655 begtime, endtime));
656 }
657
658 /*
659 * If the first measure contains non-spaces, return NO. Rather than
660 * keeping track of time signatures, we're going to pretend that we
661 * are in the longest possible time. This relies on the fact that
662 * hasspace() in effect assumes that any phony time past the end of
663 * the actual measure is spaces.
664 */
665 if (hasspace(msbeg_p->u.staff_p->groups_p[vno], begtime, Maxtime) == NO)
666 return (NO);
667
668 staffno = msbeg_p->u.staff_p->staffno;
669
670 /* if any intermediate measures contain non-spaces, return NO */
671 for (mainll_p = msbeg_p->next; mainll_p != 0 && mainll_p != msend_p;
672 mainll_p = mainll_p->next) {
673
674 /* skip everything but STAFFs for our staff number */
675 if (mainll_p->str != S_STAFF ||
676 mainll_p->u.staff_p->staffno != staffno)
677 continue;
678
679 if (hasspace(mainll_p->u.staff_p->groups_p[vno], Zero, Maxtime)
680 == NO)
681 return (NO);
682 }
683
684 if (mainll_p == 0)
685 pfatal("bug found in allspace");
686
687 /* the result is now determined by the last measure */
688 return (hasspace(msend_p->u.staff_p->groups_p[vno], Zero, endtime));
689}
690\f
691/*
692 * Name: getendstuff()
693 *
694 * Abstract: Find staff and time signature denominator for end of a stuff.
695 *
696 * Returns: pointer to MLL structure for staff containing end of stuff, or 0
697 *
698 * Description: This function finds the staff for the end of the given stuff.
699 * As a byproduct, it also finds the denominator of the time
700 * signature at that place. If a multirest is encountered, a null
701 * pointer is returned, and timeden is not guaranteed.
702 * If the end of the piece is encountered, it returns the last
703 * staff.
704 */
705
706struct MAINLL *
707getendstuff(mainll_p, stuff_p, timeden_p)
708
709struct MAINLL *mainll_p;/* staff at beginning of stuff, gets changed to end */
710struct STUFF *stuff_p; /* the STUFF */
711int *timeden_p; /* gets set to denom of time sig at end of stuff */
712
713{
714 int staffno; /* staff number where stuff is */
715 struct MAINLL *mst_p; /* point at the last staffno staff seen */
716 int timenum; /* remember the last time sig numerator */
717 int b; /* count bar lines */
718
719
720 /* bail out if multirest */
721 if (mainll_p->u.staff_p->groups_p[0]->basictime < -1)
722 return (0);
723
724 timenum = Score.timenum; /* init to current time sig numerator*/
725 *timeden_p = Score.timeden; /* init to current time sig denom */
726
727 /* if stuff doesn't cross any bar lines, we can return right away */
728 if (stuff_p->end.bars == 0)
729 return (mainll_p);
730
731 mst_p = mainll_p; /* remember last staff of this number */
732
733 staffno = mainll_p->u.staff_p->staffno;
734
735 /*
736 * Count past the right number of bar lines, keeping the time sig
737 * denominator up to date.
738 */
739 for (b = 0; b < stuff_p->end.bars; b++) {
740 for (mainll_p = mainll_p->next;
741 mainll_p != 0 && mainll_p->str != S_BAR;
742 mainll_p = mainll_p->next) {
743
744 if (mainll_p->str == S_SSV &&
745 mainll_p->u.ssv_p->used[TIME] == YES) {
746 timenum = mainll_p->u.ssv_p->timenum;
747 *timeden_p = mainll_p->u.ssv_p->timeden;
748 }
749
750 /* bail out if multirest encountered */
751 if (mainll_p->str == S_STAFF && mainll_p->u.staff_p->
752 groups_p[0]->basictime < -1)
753 return (0);
754
755 /* remember last staff of this number */
756 if (mainll_p->str == S_STAFF && mainll_p->u.staff_p->
757 staffno == staffno)
758 mst_p = mainll_p;
759 }
760 /* if end of song, set to last bar line and return this staff*/
761 if (mainll_p == 0) {
762 stuff_p->end.count = timenum + 1;
763 return (mst_p);
764 }
765 }
766
767 /*
768 * mainll_p points at the bar line preceding the place where the stuff
769 * ends. Continue forward to find the correct STAFF.
770 */
771 for (mainll_p = mainll_p->next ;
772 mainll_p != 0 && mainll_p->str != S_BAR;
773 mainll_p = mainll_p->next) {
774
775 if (mainll_p->str == S_SSV &&
776 mainll_p->u.ssv_p->used[TIME] == YES)
777 *timeden_p = mainll_p->u.ssv_p->timeden;
778
779 if (mainll_p->str == S_STAFF &&
780 mainll_p->u.staff_p->staffno == staffno)
781 break;
782 }
783
784 /* if end of song, set to last bar line and return this staff */
785 if (mainll_p == 0) {
786 stuff_p->end.count = timenum + 1;
787 return (mst_p); /* hit end of song, return last meas */
788 }
789 if (mainll_p->str == S_BAR)
790 pfatal("stuff crosses FEED where number of staffs changes");
791 if (mainll_p->u.staff_p->groups_p[0]->basictime < -1)
792 return (0);
793
794 return (mainll_p);
795}
796\f
797/*
798 * Name: lefttime()
799 *
800 * Abstract: Find time value of the nongrace group left of the given count.
801 *
802 * Returns: time value into measure
803 *
804 * Description: This function finds the nongrace GRPSYL in the given linked
805 * list that is at or left of the given count number. If the
806 * count is less 1, we return time zero, even though technically
807 * time zero is to the right of the given count number.
808 */
809
810static RATIONAL
811lefttime(count, firstgs_p, timeden)
812
813double count; /* which count of the measure */
814struct GRPSYL *firstgs_p; /* first GRPSYL of relevant voice in measure */
815int timeden; /* denominator of current time signature */
816
817{
818 RATIONAL reqtime; /* time requested */
819 RATIONAL tottime; /* total time in measure so far */
820 RATIONAL otottime; /* old total time in measure so far */
821 struct GRPSYL *gs_p; /* point along group list */
822 struct GRPSYL *ogs_p; /* (old) point along group list */
823
824
825 /* skip over any initial grace groups */
826 if (firstgs_p->grpvalue == GV_ZERO)
827 firstgs_p = nextnongrace(firstgs_p);
828
829 /* if at or before the first count, have to use first group */
830 if (count <= 1)
831 return (Zero);
832
833 /*
834 * Get requested time to the nearest half of the smallest fraction of a
835 * count that a user can specify, +1, in lowest terms. The +1 is so
836 * that if the user isn't too accurate, we still land on the intended
837 * group.
838 */
839 reqtime.n = 2 * MAXBASICTIME * (count - 1) + 0.5 + 1.0;
840 reqtime.d = 2 * MAXBASICTIME * timeden;
841 rred(&reqtime);
842
843 /*
844 * Loop through this voice accumulating time values. As soon as we
845 * equal or exceed the requested time value, return the previous
846 * group's time.
847 */
848 otottime = tottime = Zero;
849 for (ogs_p = firstgs_p, gs_p = nextnongrace(ogs_p); gs_p != 0;
850 ogs_p = gs_p, gs_p = nextnongrace(gs_p)) {
851 otottime = tottime;
852 tottime = radd(tottime, ogs_p->fulltime);
853 if (GE(tottime, reqtime))
854 return (otottime);
855 }
856
857 /* requested time is after last group; return time of last group */
858 return (otottime);
859}
860\f
861/*
862 * Name: righttime()
863 *
864 * Abstract: Find time value of the nongrace group right of the given count.
865 *
866 * Returns: time value into measure
867 *
868 * Description: This function finds the nongrace GRPSYL in the given linked
869 * list that is at or right of the given count number. If the
870 * count is greater then the rightmost group in the measure, we
871 * return the time up to the rightmost group, even though
872 * technically that time is to the left of the given count number.
873 */
874
875static RATIONAL
876righttime(count, firstgs_p, timeden)
877
878double count; /* which count of the measure */
879struct GRPSYL *firstgs_p; /* first GRPSYL of relevant voice in measure */
880int timeden; /* denominator of current time signature */
881
882{
883 RATIONAL reqtime; /* time requested */
884 RATIONAL tottime; /* total time in measure so far */
885 struct GRPSYL *gs_p; /* point along group list */
886 struct GRPSYL *ogs_p; /* (old) point along group list */
887
888
889 /* skip over any initial grace groups */
890 if (firstgs_p->grpvalue == GV_ZERO)
891 firstgs_p = nextnongrace(firstgs_p);
892
893 /* if at or before the first count, use first group */
894 if (count <= 1)
895 return (Zero);
896
897 /*
898 * Get requested time to the nearest half of the smallest fraction of a
899 * count that a user can specify, -1, in lowest terms. The -1 is so
900 * that if the user isn't too accurate, we still land on the intended
901 * group.
902 */
903 reqtime.n = 2 * MAXBASICTIME * (count - 1) + 0.5 - 1.0;
904 reqtime.d = 2 * MAXBASICTIME * timeden;
905 rred(&reqtime);
906
907 /*
908 * Loop through this voice accumulating time values. As soon as we
909 * equal or exceed the requested time value, return that new time,
910 * although don't go beyond the last group's time value.
911 */
912 tottime = Zero;
913 for (ogs_p = firstgs_p, gs_p = nextnongrace(ogs_p); gs_p != 0;
914 ogs_p = gs_p, gs_p = nextnongrace(gs_p)) {
915 tottime = radd(tottime, ogs_p->fulltime);
916 if (GE(tottime, reqtime))
917 return (tottime);
918 }
919
920 /* requested time is after last group; but must return last group */
921 return (tottime);
922}
923\f
924/*
925 * Name: accdimen()
926 *
927 * Abstract: Find the dimensions of a note's accidental.
928 *
929 * Returns: void
930 *
931 * Description: This function finds the ascent, descent, and width of an
932 * accidental, and returns them through pointers. If a pointer
933 * is null, it doesn't try to fill it in. An accidental char of
934 * '\0' gives zero for each dimension. The function takes into
935 * account whether the accidental is normal or small size, and
936 * whether it has parentheses around it.
937 * NOTE: This function ignores staffscale.
938 */
939
940void
941accdimen(note_p, ascent_p, descent_p, width_p)
942
943struct NOTE *note_p; /* the note whose accidental we're working on*/
944float *ascent_p; /* ascent, to be filled in */
945float *descent_p; /* descent, to be filled in */
946float *width_p; /* width, to be filled in */
947
948{
949 char accchar; /* accidental character number */
950 int size; /* which size of character */
951 float halfhigh; /* half the height of a parenthesis */
952
953
954 if (note_p->accidental == '\0') {
955 if (ascent_p != 0) {
956 *ascent_p = 0.0;
957 }
958 if (descent_p != 0) {
959 *descent_p = 0.0;
960 }
961 if (width_p != 0) {
962 *width_p = 0.0;
963 }
964 return;
965 }
966
967 /* find character name and size of this accidental */
968 accchar = acc2char(note_p->accidental);
969 size = (note_p->notesize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE);
970
971 /* get the requested dimensions of this accidental */
972 if (ascent_p != 0) {
973 *ascent_p = ascent(FONT_MUSIC, size, accchar);
974 }
975 if (descent_p != 0) {
976 *descent_p = descent(FONT_MUSIC, size, accchar);
977 }
978 if (width_p != 0) {
979 *width_p = width(FONT_MUSIC, size, accchar);
980 }
981
982 /*
983 * If it has parentheses around it, account for that. Assume the left
984 * and right parens are symmetrical. They will be centered on the line
985 * or space of the note.
986 */
987 if (note_p->acc_has_paren) {
988 if (width_p != 0) {
989 *width_p += 2 * width(FONT_TR, size, '(');
990 }
991 halfhigh = height(FONT_TR, size, '(') / 2.0;
992 if (ascent_p != 0 && halfhigh > *ascent_p) {
993 *ascent_p = halfhigh;
994 }
995 if (descent_p != 0 && halfhigh > *descent_p) {
996 *descent_p = halfhigh;
997 }
998 }
999}
1000\f
1001/*
1002 * Name: staffvertspace()
1003 *
1004 * Abstract: Find the minimum amount of vertical space a staff should have.
1005 *
1006 * Returns: the amount of vertical distance in inches
1007 *
1008 * Description: This function finds the minimum amount of vertical space that
1009 * should be allocated for a staff, based on how many lines it has
1010 * and whether it is tablature. This does not take into account
1011 * the extra space required by things sticking out farther; it's
1012 * just for the staff itself, plus the extra white space required
1013 * by staffs that have few lines. The SSVs must be up to date.
1014 * NOTE: This function takes staffscale into account.
1015 */
1016
1017double
1018staffvertspace(s)
1019
1020int s; /* staff number */
1021
1022{
1023 float space; /* the answer */
1024
1025
1026 /*
1027 * Base space on number of steps between top and bottom lines. But for
1028 * tablature, it must be scaled because the lines are farther apart.
1029 */
1030 space = (svpath(s, STAFFLINES)->stafflines - 1) * 2 * STEPSIZE;
1031 if (is_tab_staff(s))
1032 space *= TABRATIO;
1033
1034 /* but don't ever return less than a (scaled) regular 5 line staff */
1035 return (svpath(s, STAFFSCALE)->staffscale * MAX(space, 8.0 * STEPSIZE));
1036}
1037\f
1038/*
1039 * Name: halfstaffhi()
1040 *
1041 * Abstract: Find half of the staff height.
1042 *
1043 * Returns: half the staff height in inches
1044 *
1045 * Description: This function finds half of the staff's height, based on how
1046 * many lines it has and whether it is tablature. This does not
1047 * take into account the extra space required by things sticking
1048 * out farther; it's just for the staff itself, except that one
1049 * line staffs are given a minimum instead of the zero you would
1050 * expect. The SSVs must be up to date.
1051 * NOTE: This function takes staffscale into account.
1052 */
1053
1054double
1055halfstaffhi(s)
1056
1057int s; /* staff number */
1058
1059{
1060 float space; /* the answer */
1061
1062
1063 /*
1064 * Base space on the number of steps between the top line and the
1065 * middle of the staff. But for tablature, it must be scaled because
1066 * the lines are farther apart.
1067 */
1068 space = (svpath(s, STAFFLINES)->stafflines - 1) * STEPSIZE;
1069 if (is_tab_staff(s))
1070 space *= TABRATIO;
1071
1072 /* but don't ever return less than one (scaled) stepsize */
1073 return (MAX(space, STEPSIZE) * svpath(s, STAFFSCALE)->staffscale);
1074}
1075\f
1076/*
1077 * Name: ratbend()
1078 *
1079 * Abstract: Convert a bend distance to rational.
1080 *
1081 * Returns: the rational number answer; 0/1 if null bend or no bend
1082 *
1083 * Description: This function, given a NOTE structure from a tab staff, returns
1084 * the amount of the bend (if any) as a rational number.
1085 */
1086
1087RATIONAL
1088ratbend(note_p)
1089
1090struct NOTE *note_p;
1091
1092{
1093 RATIONAL answer;
1094
1095
1096 if (note_p->BEND == 0)
1097 return (Zero);
1098
1099 answer.d = BENDDEN(*note_p);
1100 answer.n = BENDNUM(*note_p) + BENDINT(*note_p) * answer.d;
1101 rred(&answer);
1102
1103 return (answer);
1104}
1105\f
1106/*
1107 * Name: notehorz()
1108 *
1109 * Abstract: Find horizontal boundary of note and associated things.
1110 *
1111 * Returns: the RE or RW
1112 *
1113 * Description: This function finds the horizontal boundary of a note,
1114 * including accidentals, dots, etc., all the things that can be
1115 * on the note. The note's own RE and RW only tell about the note
1116 * head itself.
1117 * NOTE: This function takes staffscale into account. The SSVs
1118 * need not be up to date, but Staffscale and Stdpad must be set.
1119 */
1120
1121double
1122notehorz(gs_p, note_p, coord)
1123
1124struct GRPSYL *gs_p; /* the group the note is in */
1125struct NOTE *note_p; /* point at the note */
1126int coord; /* RE or RW */
1127
1128{
1129 int s; /* index into slurtolist */
1130 double h; /* the answer */
1131
1132
1133 if (coord == RE) {
1134 if (note_p->note_has_paren == YES &&
1135 ! is_tab_staff(gs_p->staffno)) {
1136 /*
1137 * If there are parens around the note, start there.
1138 * Note: this field does not apply on tab staff; it
1139 * is only there for carrying over to tabnote staff.
1140 * Tab staff uses FRET_HAS_PAREN, and this distance is
1141 * included in the size of the "note" (fret) itself.
1142 */
1143 h = note_p->erparen;
1144 } else {
1145 /*
1146 * If non-tablature and there are dots, start from the
1147 * first dot. Otherwise start from the note.
1148 */
1149 if (is_tab_staff(gs_p->staffno) == NO &&
1150 gs_p->dots > 0) {
1151 h = gs_p->xdotr + 6 * Stdpad;
1152 if (gs_p->dots > 1) {
1153 h += (gs_p->dots - 1) * (2 * Stdpad +
1154 width(FONT_MUSIC, DFLT_SIZE, C_DOT));
1155 }
1156 } else {
1157 h = note_p->c[RE] + Stdpad;
1158 }
1159 }
1160
1161 /*
1162 * If this note has a slur to nowhere (and there can be at most
1163 * one such), include its length.
1164 */
1165 for (s = 0; s < note_p->nslurto; s++) {
1166 if (note_p->slurtolist[s].octave == OUT_UPWARD ||
1167 note_p->slurtolist[s].octave == OUT_DOWNWARD) {
1168 h += Staffscale * (SLIDEXLEN + STDPAD);
1169 break;
1170 }
1171 }
1172
1173 } else { /* RW */
1174
1175 if (note_p->note_has_paren == YES &&
1176 ! is_tab_staff(gs_p->staffno)) {
1177 /* if parens around note, start there */
1178 h = note_p->wlparen;
1179 } else if (is_tab_staff(gs_p->staffno) == NO &&
1180 note_p->accidental != '\0') {
1181 /* if there's an accidental, start there */
1182 /* (this includes any parens around the accidental) */
1183 h = note_p->waccr;
1184 } else {
1185 /* the note head itself, with padding */
1186 h = note_p->c[RW] - Stdpad;
1187 }
1188
1189 /*
1190 * If this note has a slur from nowhere (and there can be at
1191 * most one such), include its length.
1192 */
1193 for (s = 0; s < note_p->nslurto; s++) {
1194 if (note_p->slurtolist[s].octave == IN_UPWARD ||
1195 note_p->slurtolist[s].octave == IN_DOWNWARD) {
1196 h -= Staffscale * (SLIDEXLEN + STDPAD);
1197 break;
1198 }
1199 }
1200 }
1201
1202 return (h);
1203}
1204\f
1205/*
1206 * Name: allsmall()
1207 *
1208 * Abstract: Do the given groups (of notes) consist entirely of small notes?
1209 *
1210 * Returns: YES or NO
1211 *
1212 * Description: This function is given pointer to two GRPSYLs in a linked list.
1213 * They may point to the same GRPSYL, or the second may point to a
1214 * later GRPSYL in the list. The function returns YES if all the
1215 * notes in these GRPSYLs and any intervening GRPSYLs are small.
1216 * Any GRPSYLs that are not for notes are ignored.
1217 */
1218
1219int
1220allsmall(gs1_p, gs2_p)
1221
1222struct GRPSYL *gs1_p; /* starting group */
1223struct GRPSYL *gs2_p; /* ending group (may be same as starting group) */
1224
1225{
1226 struct GRPSYL *gs_p; /* point along the list */
1227 int n; /* index into notelist */
1228
1229
1230 /* check every group, and return NO if anything is normal size */
1231 for (gs_p = gs1_p; gs_p != gs2_p->next; gs_p = gs_p->next) {
1232 if (gs_p->grpcont == GC_NOTES && gs_p->grpsize == GS_NORMAL) {
1233 for (n = 0; n < gs_p->nnotes; n++) {
1234 if (gs_p->notelist[n].notesize == GS_NORMAL)
1235 return (NO);
1236 }
1237 }
1238 }
1239
1240 return (YES); /* everything must have been small */
1241}
1242\f
1243/*
1244 * Name: finalstemadjust()
1245 *
1246 * Abstract: Make final adjustments to the stem length.
1247 *
1248 * Returns: void
1249 *
1250 * Description: This function makes final adjustments to the stem length that
1251 * all the cases have in common. Coming in, it is set to the
1252 * stem length not counting the part between notes of a multinote
1253 * group, and it doesn't account for the thickness of a beam.
1254 * The SSVs must be up to date.
1255 * NOTE: This function takes staffscale into account.
1256 */
1257
1258void
1259finalstemadjust(gs_p)
1260
1261struct GRPSYL *gs_p; /* group whose stemlen should be adjusted */
1262
1263{
1264 float stepdiff; /* distance between outer notes in steps */
1265
1266
1267 /* if it is negative (note on wrong side of beam), zap it */
1268 if (gs_p->stemlen < 0)
1269 gs_p->stemlen = 0;
1270
1271 /* add distance between outer notes of group */
1272 stepdiff = gs_p->notelist[0].c[RY] -
1273 gs_p->notelist[ gs_p->nnotes - 1 ].c[RY];
1274 gs_p->stemlen += stepdiff;
1275
1276 /*
1277 * Decr the length by half the thickness of the beam, but don't let it
1278 * get less than the distance between the outer notes of the group.
1279 */
1280 gs_p->stemlen -= (W_WIDE * POINT / 2.0) *
1281 (gs_p->grpsize == GS_NORMAL ? 1.0 : SM_FACTOR) *
1282 svpath(gs_p->staffno, STAFFSCALE)->staffscale;
1283 gs_p->stemlen = MAX(gs_p->stemlen, stepdiff);
1284}
1285\f
1286/*
1287 * Name: getstemshift()
1288 *
1289 * Abstract: Find how far a stem is from the group's X coordinate.
1290 *
1291 * Returns: the distance in inches
1292 *
1293 * Description: This function finds how far a group's stem is shifted
1294 * horizontally from the group's X coordinate.
1295 * NOTE: This function takes staffscale into account.
1296 */
1297
1298double
1299getstemshift(gs_p)
1300
1301struct GRPSYL *gs_p; /* group whose stemlen should be adjusted */
1302
1303{
1304 /* return half the width of the widest note in the group */
1305 return (svpath(gs_p->staffno, STAFFSCALE)->staffscale *
1306 widest_head(gs_p) / 2.0);
1307}
1308\f
1309/*
1310 * Name: vscheme_voices()
1311 *
1312 * Abstract: Given a vscheme, how many voices are in it?
1313 *
1314 * Returns: number of voices
1315 *
1316 * Description: This function is given one of the valid vschemes, and it
1317 * returns the number of voices that vscheme allows.
1318 */
1319
1320int
1321vscheme_voices(vscheme)
1322
1323int vscheme; /* the given vscheme */
1324
1325{
1326 switch (vscheme) {
1327 case V_1:
1328 return (1);
1329
1330 case V_2OPSTEM:
1331 case V_2FREESTEM:
1332 return (2);
1333
1334 case V_3OPSTEM:
1335 case V_3FREESTEM:
1336 return (3);
1337
1338 default:
1339 pfatal("invalid vscheme in vscheme_voices()");
1340 }
1341
1342 return (0);
1343}
1344\f
1345/*
1346 * Name: chmgrp2staffm()
1347 *
1348 * Abstract: Given a chord and group, find the group's staff.
1349 *
1350 * Returns: pointer to staff's MLL item
1351 *
1352 * Description: This function is given the MLL item for a chord, and a group
1353 * connected to the chord. It returns the MLL item for the staff
1354 * that the group is connected to. The group can belong to any of
1355 * the staff's voices.
1356 */
1357
1358struct MAINLL *
1359chmgrp2staffm(mll_p, gs_p)
1360
1361struct MAINLL *mll_p; /* starts as MLL for the chord */
1362struct GRPSYL *gs_p; /* starts as GRPSYL the given group */
1363
1364{
1365 /* find the first group in this measure */
1366 for ( ; gs_p->prev != 0; gs_p = gs_p->prev)
1367 ;
1368
1369 /* find the staff that it belongs to */
1370 for ( ; mll_p != 0; mll_p = mll_p->next) {
1371
1372 if (mll_p->str == S_STAFF &&
1373 (mll_p->u.staff_p->groups_p[0] == gs_p ||
1374 mll_p->u.staff_p->groups_p[1] == gs_p ||
1375 mll_p->u.staff_p->groups_p[2] == gs_p))
1376 break;
1377 }
1378 if (mll_p == 0)
1379 pfatal("can't find staff in chmgrp2staffm");
1380
1381 return (mll_p);
1382}
1383\f
1384/*
1385 * Name: shiftgs()
1386 *
1387 * Abstract: Shift a GRPSYL's relative horizontal coords.
1388 *
1389 * Returns: void
1390 *
1391 * Description: This function is a GRPSYL and a shift amount. It shifts the
1392 * GRPSYL's relative horizontal coords by that amount, also
1393 * including any preceding grace GRPSYLs.
1394 */
1395
1396void
1397shiftgs(gs_p, shift)
1398
1399struct GRPSYL *gs_p; /* the main GRPSYL */
1400double shift;
1401
1402{
1403 struct GRPSYL *ggs_p; /* point at a grace group */
1404
1405
1406 gs_p->c[RX] += shift;
1407 gs_p->c[RW] += shift;
1408 gs_p->c[RE] += shift;
1409
1410 /* apply shift to any preceding grace groups */
1411 for (ggs_p = gs_p->prev; ggs_p != 0 && ggs_p->grpvalue == GV_ZERO;
1412 ggs_p = ggs_p->prev) {
1413 ggs_p->c[RX] += shift;
1414 ggs_p->c[RW] += shift;
1415 ggs_p->c[RE] += shift;
1416 }
1417}
1418\f
1419/*
1420 * Name: nearestline()
1421 *
1422 * Abstract: Round a vertical offset to the nearest staff line.
1423 *
1424 * Returns: the resulting offset
1425 *
1426 * Description: This function rounds its parameter off to a multiple of 2
1427 * stepsizes.
1428 * NOTE: This function takes staffscale into account. The SSVs
1429 * need not be up to date, but Stepsize must be set.
1430 */
1431
1432double
1433nearestline(offset)
1434
1435double offset; /* offset from center staff line */
1436
1437{
1438 if (offset >= 0) {
1439 offset /= (2 * Stepsize);
1440 offset = (int)(offset + 0.5);
1441 offset *= (2 * Stepsize);
1442 } else {
1443 offset = -offset;
1444 offset /= (2 * Stepsize);
1445 offset = (int)(offset + 0.5);
1446 offset *= (2 * Stepsize);
1447 offset = -offset;
1448 }
1449
1450 return (offset);
1451}
1452\f
1453/*
1454 * Name: vfyoffset()
1455 *
1456 * Abstract: Verify horizontal offsets are not in conflict.
1457 *
1458 * Returns: void
1459 *
1460 * Description: This function prints a warning if the horizontal offsets of
1461 * voices 1 and 2 are in conflict. In that case it also zaps
1462 * the bad offsets.
1463 */
1464
1465void
1466vfyoffset(g_p)
1467
1468struct GRPSYL *g_p[]; /* array of pointers to two groups */
1469
1470{
1471 /* the only errors are cases where "+" and "-" are used */
1472 if (g_p[0]->ho_usage != HO_LEFT && g_p[0]->ho_usage != HO_RIGHT)
1473 return;
1474
1475 /* can't both be "+" or both be "-" */
1476 if (g_p[0]->ho_usage == g_p[1]->ho_usage) {
1477
1478 l_warning(
1479 g_p[1]->inputfile, g_p[1]->inputlineno,
1480 "voices 1 and 2 cannot both have horizontal offset '%c'; ignoring them",
1481 g_p[0]->ho_usage == HO_LEFT ? '-' :'+');
1482
1483 g_p[0]->ho_usage = HO_NONE;
1484 g_p[1]->ho_usage = HO_NONE;
1485 }
1486}
1487\f
1488/*
1489 * Name: adjslope()
1490 *
1491 * Abstract: Adjust the slope of a beam.
1492 *
1493 * Returns: the new slope
1494 *
1495 * Description: This function is given the slope of a beam as determined by
1496 * linear regression. It adjusts it according to the "beamslope"
1497 * parameter.
1498 */
1499
1500double
1501adjslope(g_p, oldslope, betweencsb)
1502
1503struct GRPSYL *g_p; /* pointer to GRPSYL to get staff and voice from */
1504double oldslope; /* the given slope */
1505int betweencsb; /* is this beam CSB and between the staffs? */
1506
1507{
1508 struct SSV *ssv_p; /* for getting fact and max */
1509 float beamfact; /* to multiply by */
1510 float beammax; /* max angle in degrees */
1511 float newangle; /* the adjusted angle */
1512
1513
1514 /* find parameter values */
1515 ssv_p = vvpath(g_p->staffno, g_p->vno, BEAMSLOPE);
1516 beamfact = ssv_p->beamfact;
1517 beammax = ssv_p->beammax;
1518
1519 /*
1520 * If cross staff beaming and the beam is between the staffs, we allow
1521 * a somewhat bigger angle, because it may be necessary to avoid
1522 * collisions.
1523 */
1524 if (betweencsb == YES)
1525 beammax *= 1.4;
1526
1527 /* new angle = old angle * beamfact */
1528 newangle = (atan(oldslope) * 180.0 / PI) * beamfact;
1529
1530 /* force it to stay within the limit */
1531 if (newangle > beammax)
1532 newangle = beammax;
1533 else if (newangle < -beammax)
1534 newangle = -beammax;
1535
1536 /* return as slope */
1537 return (tan(newangle * PI / 180.0));
1538}
1539\f
1540/*
1541 * Name: curve_y_at_x()
1542 *
1543 * Abstract: Given a curve and an X value, return the Y value there.
1544 *
1545 * Returns: the Y value
1546 *
1547 * Description: This function should only be called for curves where the X
1548 * value of each point in the curve list is greater than the
1549 * previous point's X value, although it's okay if the curve
1550 * itself is not strictly increasing in X value all the time as
1551 * you go from the start to the end.
1552 *
1553 * If the X value given is less than the first point's, it returns
1554 * the Y of the first point. If the X value is greater than the
1555 * last point's, it returns the Y of the last point. Otherwise,
1556 * it returns a Y value of the curve at that X value. I say "a"
1557 * Y value, because if the curve isn't strictly increasing, there
1558 * can be multiple answers, and it just returns one of them.
1559 *
1560 * The function assumes that the curve points will be connected by
1561 * cubic curves, according to the algorithm in calccurve() and
1562 * findcontrol().
1563 */
1564
1565double
1566curve_y_at_x(first_p, x)
1567
1568struct CRVLIST *first_p; /* left endpoint of curve */
1569double x; /* X coord at which we need Y */
1570
1571{
1572 float y; /* the answer */
1573 float a, b, c; /* coefficients for a cubic */
1574 struct CRVLIST *left_p, *right_p; /* endpoints of a cubic segment */
1575 float rotangle; /* rotate new system to get old (in radians) */
1576 float tranx, trany; /* a point translated to another coord system */
1577 float pointx, pointy; /* trans & rotated in another coord system */
1578 float lineslope, intercept; /* of a line through the given x */
1579 float cos_rotangle, sin_rotangle; /* for saving these values */
1580 float deltax, deltay; /* of endpoints of segment between 2 points */
1581 float len; /* length of segment between 2 points */
1582
1583
1584 /*
1585 * If the first point of the curve is at or already beyond the given x,
1586 * return the first point's y.
1587 */
1588 if (first_p->x >= x) {
1589 return (first_p->y);
1590 }
1591 right_p = 0; /* for lint */
1592 for (left_p = first_p; left_p->next != 0; left_p = left_p->next) {
1593 right_p = left_p->next;
1594 /* if x is right at this point, use this point's y */
1595 if (right_p->x == x) {
1596 return (right_p->y);
1597 }
1598 /* if x is within this interval, break out */
1599 if (left_p->x < x && x < right_p->x) {
1600 break;
1601 }
1602 }
1603 /* if this happens, x is beyond the last point, so use last point's y */
1604 if (left_p->next == 0) {
1605 return (left_p->y);
1606 }
1607
1608 /*
1609 * The given x is between the x coords of two of the points in the
1610 * curvelist. So we need to find the cubic arc that calccurve() and
1611 * findcontrol() would use, if this curve is going to be used. The
1612 * cubic arc is determined in a translated/rotated coordinate system
1613 * where left_p is (0,0) and right_p is on the positive X axix.
1614 * rotangle is the angle from the segment between left_p and right_p
1615 * to the real X axis. The cubic, in the translated/rotated system, is
1616 * y = a x^3 + b x^2 + c x. It turns out that the constant term is
1617 * always zero.
1618 */
1619 rotangle = findcubic(left_p, right_p, &a, &b, &c);
1620
1621 /*
1622 * If left_p->y == right_p->y, rotangle is zero, meaning no rotation was
1623 * necessary, only translation. In that case we can simply plug into
1624 * the cubic we found, adjusting for the translation. A fudge factor is
1625 * needed so that we don't take the tangent of almost 90 degrees below.
1626 */
1627 if (fabs(rotangle) < 0.001) {
1628 pointx = x - left_p->x;
1629 pointy = ((a * pointx + b) * pointx + c) * pointx;
1630 y = pointy + left_p->y;
1631 return (y);
1632 }
1633
1634 /*
1635 * Rotation was necessary. In the original coord system, picture a
1636 * vertical line at the given x value. It intersects the cubic,
1637 * possibly in more than one place. We want the y value at the
1638 * intersection. In the translated/rotated system, this line has a
1639 * slope as determine below.
1640 */
1641 if (rotangle < 0.0) {
1642 lineslope = tan(PI / 2.0 + rotangle);
1643 } else {
1644 lineslope = tan(-PI / 2.0 + rotangle);
1645 }
1646
1647 /*
1648 * In the real coord system, the vertical line hits (x, 0). Find this
1649 * point in the translated/rotated system.
1650 */
1651 /* first translate */
1652 tranx = x - left_p->x;
1653 trany = -left_p->y;
1654 /* then rotate */
1655 cos_rotangle = cos(rotangle); /* save to avoid recalculation */
1656 sin_rotangle = sin(rotangle);
1657 pointx = tranx * cos_rotangle - trany * sin_rotangle;
1658 pointy = trany * cos_rotangle + tranx * sin_rotangle;
1659
1660 /* find Y intercept in the translated/rotated system */
1661 intercept = pointy - lineslope * pointx;
1662
1663 /*
1664 * Now, in the tran/rot coord system, we need to find the intersection
1665 * of this line
1666 * y = lineslope * x + intercept
1667 * and the cubic
1668 * y = a * x^3 + b * x^2 + c * x
1669 * Setting the two values of y equal, we get
1670 * lineslope * x + intercept = a * x^3 + b * x^2 + c * x
1671 * or
1672 * a * x^3 + b * x^2 + (c - lineslope) * x - intercept = 0
1673 */
1674 /* find intersection point in the tran/rot coord system */
1675 deltax = right_p->x - left_p->x;
1676 deltay = right_p->y - left_p->y;
1677 len = sqrt(SQUARED(deltax) + SQUARED(deltay));
1678 pointx = solvecubic(a, b, c-lineslope, -intercept,
1679 0.0, len, Stdpad / 2.0);
1680 pointy = lineslope * pointx + intercept;
1681
1682 /* rotate backwards, getting Y value */
1683 trany = pointy * cos_rotangle - pointx * sin_rotangle;
1684
1685 /* translate back to the original coord system */
1686 y = trany + left_p->y;
1687
1688 return (y);
1689}
1690\f
1691/*
1692 * Name: findcubic()
1693 *
1694 * Abstract: Given neighboring curve points, find cubic and rotation angle.
1695 *
1696 * Returns: angle from new coord system's X axis to old system's (radians)
1697 *
1698 * Description: This function uses a new coordinate system, where the left
1699 * curve point is (0, 0), and the right curve point is on the
1700 * positive X axis. It finds the coefficients for the cubic arc
1701 * that will be put through these points. It returns the angle
1702 * that the old coord system needs to be rotated by to get to
1703 * the new system.
1704 */
1705
1706double
1707findcubic(left_p, right_p, a_p, b_p, c_p)
1708
1709struct CRVLIST *left_p; /* left endpoint of cubic arc */
1710struct CRVLIST *right_p; /* right endpoint of cubic arc */
1711float *a_p, *b_p, *c_p; /* return the answers here, the coefficients */
1712
1713{
1714 double langle; /* half angle from prev segment to this one */
1715 double rangle; /* half angle from this segment to next one */
1716 float deltax, deltay; /* for this segment */
1717 float len; /* of this segment */
1718 float lslope, rslope; /* slope of tangent line at left & right point*/
1719 float thisang, prevang, nextang; /* angle of segment with horizontal */
1720
1721
1722 langle = rangle = 0.0; /* for lint */
1723
1724 /* get angle of this segment */
1725 thisang = atan((right_p->y - left_p->y) / (right_p->x - left_p->x));
1726
1727 if (left_p->prev != 0) {
1728 /* there is a previous segment; find its angle */
1729 prevang = atan((left_p->y - left_p->prev->y) /
1730 (left_p->x - left_p->prev->x));
1731 /* half the change in angle */
1732 langle = (thisang - prevang) / 2.0;
1733 }
1734 if (right_p->next != 0) {
1735 /* there is a next segment; find its angle */
1736 nextang = atan((right_p->next->y - right_p->y) /
1737 (right_p->next->x - right_p->x));
1738 /* half the change in angle */
1739 rangle = (nextang - thisang) / 2.0;
1740 }
1741 if (left_p->prev == 0) {
1742 /* no previous segment; use same angle as on the right */
1743 langle = rangle;
1744 }
1745 if (right_p->next == 0) {
1746 /* no next segment; use same angle as on the left */
1747 rangle = langle;
1748 }
1749
1750 /* get lengths of this segment */
1751 deltax = right_p->x - left_p->x;
1752 deltay = right_p->y - left_p->y;
1753 len = sqrt(SQUARED(deltax) + SQUARED(deltay));
1754
1755 /*
1756 * Rotate and translate the axes so that the starting point (left_p)
1757 * is at the origin, and the ending point (right_p) is on the positive
1758 * x axis. Their coords are (0, 0) and (len, 0). We are going to
1759 * find a cubic equation that intersects the endpoints, and has the
1760 * required slope at those points. The equation is
1761 * y = a x^3 + b x^2 + c x + d
1762 * so the slope is
1763 * y' = 3 a x^2 + 2 b x + c
1764 * By plugging the two points into these, you get 4 equations in the 4
1765 * unknowns a, b, c, d.
1766 */
1767 /* find the slope of the tangent lines at the first & second points */
1768 lslope = -tan(langle);
1769 rslope = tan(rangle);
1770
1771 /* set values of a, b, c (d turns out to be always 0) */
1772 *a_p = (lslope + rslope) / SQUARED(len);
1773 *b_p = (-2.0 * lslope - rslope) / len;
1774 *c_p = lslope;
1775
1776 return (-thisang);
1777}
1778\f
1779/*
1780 * Name: solvecubic()
1781 *
1782 * Abstract: Find a solution to a cubic equation within a given interval.
1783 *
1784 * Returns: the solution
1785 *
1786 * Description: This function is given the coefficients of a cubic equation and
1787 * the boundaries of an interval. The function must be positive
1788 * at one end and negative at the other (or zero is okay at
1789 * either). It uses the "modified regula falsi" algorithm to find
1790 * a solution, meaning that it keeps narrowing down the interval.
1791 * It stops when the inverval size <= the threshhold given. But
1792 * in case something goes wrong, it also stops after 20 loops.
1793 */
1794
1795double
1796solvecubic(a, b, c, d, lo, hi, thresh)
1797
1798double a, b, c, d; /* in equation a x^3 + b x^2 + c x + d = 0 */
1799double lo, hi; /* low and high boundaries of interval to look in */
1800double thresh; /* how close must result be to the true answer */
1801
1802#define FUNC(x) (((a * x + b) * x + c) * x + d)
1803{
1804 float lovert, hivert; /* y values */
1805 float mid, midvert; /* a point in the middle and its y value */
1806 float oldmidvert; /* midvert in previous loop */
1807 int n;
1808
1809
1810 lovert = FUNC(lo);
1811 hivert = FUNC(hi);
1812
1813 /*
1814 * If the function is positive at both endpoints or negative at both
1815 * endpoints, it was called incorrectly. But we're going to allow for
1816 * a small violation of this due to presumed roundoff error. If one
1817 * endpoint if "very near" zero, we'll pretend it was zero and return
1818 * it as the solution.
1819 */
1820 if (lovert * hivert > 0.0) {
1821 if (fabs(lovert) < thresh)
1822 return (lo);
1823 if (fabs(hivert) < thresh)
1824 return (hi);
1825 pfatal("solvecubic was called with an invalid interval");
1826 }
1827
1828 mid = lo;
1829 midvert = lovert;
1830
1831 for (n = 0; n < 20 && hi - lo > thresh; n++) {
1832 oldmidvert = midvert;
1833
1834 /*
1835 * Find a point somewhere in the interval by passing a segment
1836 * through (lo, lovert) and (hi, hivert) and seeing where it
1837 * hits the X axis.
1838 */
1839 mid = (lovert * hi - hivert * lo) / (lovert - hivert);
1840 midvert = FUNC(mid);
1841
1842 /*
1843 * Set either the hi or the lo equal to the mid. If the value
1844 * at mid is the same sign as the previous one, divide the
1845 * vert value by 2, so we can eventually get the segment to
1846 * hit on the other side.
1847 */
1848 if ((lovert > 0.0) != (midvert > 0.0)) {
1849 hi = mid;
1850 hivert = midvert;
1851 if ((midvert > 0.0) == (oldmidvert > 0.0)) {
1852 lovert /= 2.0;
1853 }
1854 } else {
1855 lo = mid;
1856 lovert = midvert;
1857 if ((midvert > 0.0) == (oldmidvert > 0.0)) {
1858 hivert /= 2.0;
1859 }
1860 }
1861 }
1862
1863 return (mid);
1864}
1865\f
1866/*
1867 * Name: css_affects_stemtip()
1868 *
1869 * Abstract: Do CSS notes (if any) affect the position of the stem's tip?
1870 *
1871 * Returns: YES or NO
1872 *
1873 * Description: This function is given a pointer to a GRPSYL. It must be a
1874 * note group, but can be grace or nongrace. It may be a member
1875 * of a beamed set, or not a member of a beamed set. It decides
1876 * whether the position of the tip of the stem (or where the stem
1877 * would be for a non-stemmed note) is affected by CSS notes.
1878 */
1879
1880int
1881css_affects_stemtip(gs_p)
1882
1883struct GRPSYL *gs_p; /* starts at the given group */
1884
1885{
1886 /*
1887 * For the single (unbeamed) group case, the position of the tip of the
1888 * stem is affected if either the CSS notes are on the stem side, or if
1889 * all the notes are CSS.
1890 */
1891 if (gs_p->beamloc == NOITEM) {
1892 return (STEMSIDE_CSS(gs_p) || NNN(gs_p) == 0 ? YES : NO);
1893 }
1894
1895 /* CSB is never CSS */
1896 if (gs_p->beamto != CS_SAME) {
1897 return (NO);
1898 }
1899
1900 /*
1901 * For the beamed case, either all or none of the groups can have CSS
1902 * notes on the stem side, if there are any other-staff notes at all
1903 * in the group. This is because all the groups' stems go
1904 * the same direction, and we don't allow the beaming together of
1905 * groups where some have stemto == CS_ABOVE and others have CS_BELOW.
1906 * Theoretically a group with all CSS notes could affect the position
1907 * of the beam regardless of whether its CSS notes are stemside or not;
1908 * but we will pretend that it can't. We'll fake things out in
1909 * setbeam(). This way, we can handle beaming and set the beam
1910 * position and good group boundaries on the beamside during the
1911 * CSSpss==NO pass. Then the placement of "stuff" on that side will
1912 * be better.
1913 */
1914 /* if we're not at the start of the beamed set, go back to there */
1915 while (gs_p->beamloc != STARTITEM) {
1916 gs_p = prevsimilar(gs_p);
1917 }
1918 /* check each member to see if any have stemside CSS */
1919 while (gs_p != 0) {
1920 if (STEMSIDE_CSS(gs_p)) {
1921 return (YES);
1922 }
1923 if (gs_p->beamloc == ENDITEM) {
1924 break;
1925 }
1926 gs_p = nextsimilar(gs_p);
1927 }
1928 return (NO);
1929}
1930\f
1931/*
1932 * Name: css_affects_tieslurbend()
1933 *
1934 * Abstract: Do CSS notes (if any) affect the position of this tie/slur/bend?
1935 *
1936 * Returns: YES or NO
1937 *
1938 * Description: This function decides whether the given tie, slur, or bend is
1939 * affected by CSS notes in any of the groups it covers.
1940 */
1941
1942int
1943css_affects_tieslurbend(stuff_p, mll_p)
1944
1945struct STUFF *stuff_p; /* the tie, slur, or bend */
1946struct MAINLL *mll_p; /* MLL item where this tie/slur/bend starts */
1947
1948{
1949 struct GRPSYL *sg_p; /* starting group of the tie/slur/bend */
1950 struct GRPSYL *eg_p; /* starting group of the tie/slur/bend */
1951 struct NOTE *snote_p; /* starting note of the tie/slur/bend */
1952 struct NOTE *enote_p; /* ending note of the tie/slur/bend */
1953 int idx; /* index of note in the group */
1954
1955
1956 /* if not cross staff stemming, don't waste time checking */
1957 if (CSSused == NO) {
1958 return (NO);
1959 }
1960
1961 /* second half (after crossing scorefeed); was handled by first half */
1962 if (stuff_p->carryin == YES) {
1963 return (NO);
1964 }
1965
1966 sg_p = stuff_p->beggrp_p;
1967 snote_p = stuff_p->begnote_p;
1968
1969 /* find the index of the note in the group */
1970 for (idx = 0; idx < sg_p->nnotes; idx++) {
1971 if (&sg_p->notelist[idx] == snote_p) {
1972 break;
1973 }
1974 }
1975 if (idx == sg_p->nnotes) {
1976 pfatal("can't find tied/slurred/bent note in group");
1977 }
1978
1979 /* if this starting note is CSS, return YES */
1980 if (IS_CSS_NOTE(sg_p, idx)) {
1981 return (YES);
1982 }
1983
1984 /*
1985 * Find the end note of the tie/slur/bend. If none, we don't care
1986 * about the end note.
1987 */
1988 eg_p = nextgrpsyl(sg_p, &mll_p);
1989 if (eg_p == 0) {
1990 return (NO);
1991 }
1992
1993 /* find the note tied/slurred/bent to */
1994 if (stuff_p->curveno == -1) { /* this is a tie */
1995 enote_p = find_matching_note(eg_p, snote_p->letter,
1996 snote_p->octave, (char *)0);
1997 } else { /* this is a slur or bend */
1998 enote_p = find_matching_note(eg_p,
1999 snote_p->slurtolist[stuff_p->curveno].letter,
2000 snote_p->slurtolist[stuff_p->curveno].octave,
2001 (char *)0);
2002 }
2003
2004 if (enote_p == 0) {
2005 return (NO);
2006 }
2007
2008 /* find the index of the note in the group */
2009 for (idx = 0; idx < eg_p->nnotes; idx++) {
2010 if (&eg_p->notelist[idx] == enote_p) {
2011 break;
2012 }
2013 }
2014 if (idx == eg_p->nnotes) {
2015 pfatal("can't find tied/slurred/bent-to note in group");
2016 }
2017
2018 /* if this ending note is CSS, return YES */
2019 if (IS_CSS_NOTE(eg_p, idx)) {
2020 return (YES);
2021 }
2022
2023 return (NO);
2024}
2025/*
2026 * Name: css_affects_phrase()
2027 *
2028 * Abstract: Do CSS notes (if any) affect the position of this phrase mark?
2029 *
2030 * Returns: YES or NO
2031 *
2032 * Description: This function decides whether the given phrase mark is
2033 * affected by CSS notes in any of the groups it covers.
2034 */
2035
2036int
2037css_affects_phrase(stuff_p, mll_p)
2038
2039struct STUFF *stuff_p; /* the phrase */
2040struct MAINLL *mll_p; /* for the group at start of phrase */
2041
2042{
2043 struct GRPSYL *gs_p; /* point at end group covered by phrase */
2044 int place; /* PL_ABOVE or PL_BELOW */
2045 int staffno; /* staff number */
2046 int vidx; /* voice index */
2047
2048
2049 place = stuff_p->place;
2050 gs_p = stuff_p->beggrp_p;
2051 staffno = gs_p->staffno;
2052 vidx = gs_p->vno - 1;
2053
2054 /* loop once for each group covered by the phrase */
2055 while (gs_p != 0) {
2056 /* return YES right away if we find an affected group */
2057 switch (gs_p->stemto) {
2058 case CS_SAME:
2059 break;
2060 case CS_ABOVE:
2061 if (place == PL_ABOVE || NNN(gs_p) == 0) {
2062 return (YES);
2063 }
2064 break;
2065 case CS_BELOW:
2066 if (place == PL_BELOW || NNN(gs_p) == 0) {
2067 return (YES);
2068 }
2069 break;
2070 }
2071
2072 /* if we've seen the last group, we are done */
2073 if (gs_p == stuff_p->endgrp_p) {
2074 break;
2075 }
2076
2077 /* find the next note group in this voice */
2078 do {
2079 gs_p = gs_p->next;
2080 } while (gs_p != 0 && gs_p->grpcont != GC_NOTES);
2081
2082 /* if we hit the end of the measure, look for next measure */
2083 if (gs_p == 0) {
2084 /* find the bar line */
2085 while (mll_p != 0 && mll_p->str != S_BAR) {
2086 mll_p = mll_p->next;
2087 }
2088 /* find the matching staff in the next measure */
2089 while (mll_p != 0 && ! (mll_p->str == S_STAFF &&
2090 mll_p->u.staff_p->staffno == staffno)) {
2091 mll_p = mll_p->next;
2092 }
2093 /* defensive check, should not happen */
2094 if (mll_p == 0) {
2095 break;
2096 }
2097 /* point at the first group in new measure */
2098 gs_p = mll_p->u.staff_p->groups_p[vidx];
2099 }
2100 }
2101
2102 return (NO);
2103}
2104\f
2105/*
2106 * Name: nextsimilar()
2107 *
2108 * Abstract: Return next group in a GRPSYL list that is "like" the current.
2109 *
2110 * Returns: pointer to GRPSYL of next desired group, 0 if none
2111 *
2112 * Description: This function loop down the GRPSYL linked list from the given
2113 * starting point. It returns the next GRPSYL in the list that has
2114 * the same grpcont and grpvalue as the given one, or 0 if none.
2115 */
2116
2117struct GRPSYL *
2118nextsimilar(gs_p)
2119
2120struct GRPSYL *gs_p; /* current group */
2121
2122{
2123 int curvalue;
2124 int curcont;
2125
2126 curvalue = gs_p->grpvalue;
2127 curcont = gs_p->grpcont;
2128 gs_p = gs_p->next;
2129 while (gs_p != 0 &&
2130 (gs_p->grpvalue != curvalue || gs_p->grpcont != curcont)) {
2131 gs_p = gs_p->next;
2132 }
2133 return (gs_p);
2134}
2135\f
2136/*
2137 * Name: prevsimilar()
2138 *
2139 * Abstract: Return prev group in a GRPSYL list that is "like" the current.
2140 *
2141 * Returns: pointer to GRPSYL of previous desired group, 0 if none
2142 *
2143 * Description: This function loops down the GRPSYL linked list from the given
2144 * starting point. It returns the prev GRPSYL in the list that has
2145 * the same grpcont and grpvalue as the given one, or 0 if none.
2146 */
2147
2148struct GRPSYL *
2149prevsimilar(gs_p)
2150
2151struct GRPSYL *gs_p; /* current group */
2152
2153{
2154 int curvalue;
2155 int curcont;
2156
2157 curvalue = gs_p->grpvalue;
2158 curcont = gs_p->grpcont;
2159 gs_p = gs_p->prev;
2160 while (gs_p != 0 &&
2161 (gs_p->grpvalue != curvalue || gs_p->grpcont != curcont)) {
2162 gs_p = gs_p->prev;
2163 }
2164 return (gs_p);
2165}
2166\f
2167/*
2168 * Name: gs2ch()
2169 *
2170 * Abstract: Given a GRPSYL and its staff's MLL, find chord for that time.
2171 *
2172 * Returns: pointer to CHORD structure
2173 *
2174 * Description: This function is given a GRPSYL and the MLL structure for the
2175 * GRPSYL's staff. It finds the CHORD structure that heads the
2176 * list of GRPSYLs occurring at that time in that measure. Note
2177 * that if the given GRPSYL is grace, it won't actually occur in
2178 * that linked list of GRPSYLs; but in that case the following
2179 * non-grace GRPSYL will.
2180 */
2181
2182struct CHORD *
2183gs2ch(mll_p, gs_p)
2184
2185struct MAINLL *mll_p; /* the MLL for the given GRPSYL */
2186struct GRPSYL *gs_p; /* the given GRPSYL */
2187
2188{
2189 struct CHORD *ch_p; /* loop through chords */
2190 struct GRPSYL *gs2_p; /* point along a GRPSYL list */
2191 RATIONAL time; /* time offset where our group is */
2192
2193
2194 /* find chord headcell for this measure */
2195 while (mll_p != 0 && mll_p->str == S_STAFF) {
2196 mll_p = mll_p->prev;
2197 }
2198 if (mll_p == 0 || mll_p->str != S_CHHEAD) {
2199 pfatal("missing chord head cell in gs2ch");
2200 }
2201
2202 /* find time offset of our group by summing all previous groups */
2203 time = Zero;
2204 for (gs2_p = gs_p->prev; gs2_p != 0; gs2_p = gs2_p->prev) {
2205 time = radd(time, gs2_p->fulltime);
2206 }
2207
2208 /*
2209 * Find the chord that contains our group (or, if we are a grace group,
2210 * the following normal group).
2211 */
2212 for (ch_p = mll_p->u.chhead_p->ch_p;
2213 ch_p != 0 && NE(ch_p->starttime, time);
2214 ch_p = ch_p->ch_p) {
2215 ;
2216 }
2217 if (ch_p == 0) {
2218 pfatal("can't find chord in gs2ch");
2219 }
2220
2221 return (ch_p);
2222}
2223\f
2224/*
2225 * Name: stemroom()
2226 *
2227 * Abstract: Try to find how much room a "wrong way" stem needs.
2228 *
2229 * Returns: The room needed, measured in stepsizes.
2230 *
2231 * Description: This function is given a nongrace note group whose stem has
2232 * been forced the wrong way (down for the top group or up for the
2233 * bottom group) despite the other voice being nonspace. It tries
2234 * to find how long the stem will be so that we can decide whether
2235 * the groups need to be horizontally offset. It works well for
2236 * nonbeamed groups, but for beamed groups it can only guess. It
2237 * is to be used in places where we need to know the stem length
2238 * (to the extent possible) even though beamstem.c hasn't run yet.
2239 *
2240 * WARNING: This code is similar to the nongrace section of
2241 * proclist() in beamstem.c. If you change one, you probably
2242 * will need to change the other.
2243 */
2244
2245double
2246stemroom(gs_p)
2247
2248struct GRPSYL *gs_p; /* the group in question */
2249
2250{
2251 float room; /* the answer, in stepsizes */
2252 int bf; /* number of beams/flags */
2253
2254
2255 /*
2256 * If user specified stem length, use that.
2257 */
2258 if (IS_STEMLEN_KNOWN(gs_p->stemlen)) {
2259 return (gs_p->stemlen / STEPSIZE);
2260 }
2261
2262 /*
2263 * If this group is part of a beamed set, there is no way to know how
2264 * long the stem will be, since the beaming hasn't been done yet, and
2265 * can't be done until all horizontal placement has been done. So
2266 * return the default stem length and hope for the best.
2267 */
2268 if (gs_p->beamloc != NOITEM) {
2269 return (DEFSTEMLEN);
2270 }
2271
2272 /*
2273 * Only half notes and shorter have stems, but whole and double
2274 * whole notes still need to have a pseudo stem length set if
2275 * alternation beams are to be drawn between two neighboring
2276 * groups, or the group has slashes.
2277 */
2278 if (gs_p->basictime <= 1 && gs_p->slash_alt == 0) {
2279 /* no (pseudo)stem */
2280 return (0.0);
2281 }
2282
2283 /* find default stemlen for this voice */
2284 room = vvpath(gs_p->staffno, gs_p->vno, STEMLEN)->stemlen;
2285 if (room == 0.0) {
2286 return (0.0);
2287 }
2288
2289 /* if small notes, reduce this default */
2290 room *= (allsmall(gs_p, gs_p) == YES ? SM_STEMFACTOR : 1.0);
2291
2292 /* add more, if needed, for flags/beams/slashes/alternations */
2293 if (gs_p->basictime >= 8) {
2294 bf = drmo(gs_p->basictime) - 2; /* no. of beams/flags*/
2295 } else {
2296 bf = 0; /* none on quarter or longer */
2297 }
2298 bf += abs(gs_p->slash_alt); /* slashes or alternations */
2299 if (gs_p->slash_alt > 0 && gs_p->basictime >= 16) {
2300 bf++; /* slashes need an extra one if 16, 32, ... */
2301 }
2302 if (bf > 2) {
2303 room += (bf - 2) * FLAGSEP / STEPSIZE;
2304 }
2305
2306 /*
2307 * If the note may have flag(s), is stem up, and has dot(s), we must
2308 * prevent the flag(s) from hitting the dot(s), by lengthening the stem.
2309 */
2310 if (gs_p->basictime >= 8 && gs_p->stemdir == UP && gs_p->dots != 0) {
2311 if (gs_p->notelist[0].stepsup % 2 == 0) {
2312 /* note is on a line */
2313 if (gs_p->basictime == 8) {
2314 room += 1.0;
2315 } else {
2316 room += 2.0;
2317 }
2318 } else {
2319 /* note is on a space */
2320 if (gs_p->basictime > 8) {
2321 room += 1.0;
2322 }
2323 }
2324 }
2325
2326 return (room);
2327}