chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / prntdata.c
CommitLineData
69695f33
MW
1
2/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006 by Arkkra Enterprises */
3/* All rights reserved */
4
5/* functions for printing things off of STAFF structs: notes, stems,
6 * rests, flags, beams, etc */
7
8#include "defines.h"
9#include "structs.h"
10#include "globals.h"
11
12
13/* This struct is used to build up a mesh that represents cross staff beams.
14 * This is used to figure out how far from the stem end to offset
15 * the end of a beam.
16 * There are a row of these linked horizontally via "next" for each beam.
17 * The stems are linked vertically via the above_p and below_p pointers.
18 * To get the stem offset for a given beam,
19 * the code finds the desired basictime on the appropriate stem,
20 * and counts how many beams that is from the end of the stem.
21 */
22struct CSBINFO {
23 struct CSBINFO *next; /* for next group in same beam */
24 struct CSBINFO *above_p; /* beams above this beam */
25 struct CSBINFO *below_p; /* beams below this beam */
26 struct GRPSYL *gs_p; /* group this info is associated with.
27 * This is only used on the 8th beam,
28 * and is just for convenience,
29 * to save us from having to figure
30 * it out again later.
31 */
32 int basictime; /* 8, 16, 32, etc represented by beam */
33};
34
35/* static functions */
36static void do_syl_joins P((char *syl, double west, double y));
37static void pr_stuff P((struct STUFF *stufflist_p, int staffno,
38 struct MAINLL *mll_p));
39static int pr_grid P((struct STUFF *stuff_p, int staffnum));
40static void pr_tieslur P((struct STUFF *stuff_p, struct MAINLL *mll_p,
41 int staffno));
42static int get_ts_style P((struct STUFF *stuff_p, struct MAINLL *mll_p));
43static void pr_rest P((struct GRPSYL *gs_p, struct STAFF *staff_p));
44static double mr_y_loc P((int staffno));
45static void pr_note_dots P((struct NOTE *noteinfo_p, int numdots,
46 double xdotr, double group_x, double group_y));
47static void pr_parens P((struct NOTE *note_p, struct GRPSYL *gs_p));
48static void pr_stems P((struct GRPSYL *grpsyl_p));
49static double slash_xlen P((struct GRPSYL *grpsyl_p));
50static void pr_flags P((struct GRPSYL *grpsyl_p, double x, double y));
51static void pr_accidental P((struct NOTE *noteinfo_p, struct GRPSYL *grpsyl_p));
52static void pr_leger P((struct NOTE *noteinfo_p, struct GRPSYL *gs_p,
53 int staffno));
54static int numlegers P((struct NOTE *noteinfo_p));
55static double leger_length P((struct NOTE *noteinfo_p, struct GRPSYL *othergs_p,
56 int lines, int other_is_prev, int is_intermediate));
57static void pr_tupnums P((struct GRPSYL *gs_p, struct STAFF *staff_p));
58static void pr_beams P((struct GRPSYL *grpsyl_p, int grpvalue, int grpsize));
59static struct CSBINFO *mkcsbmesh P((struct GRPSYL *begin_p,
60 struct GRPSYL *end_p));
61static int draw_beams P((struct GRPSYL *gs_p, struct GRPSYL *endbeam_p,
62 int basictime, int grpsize, int grpvalue));
63static double beam_offset P((int nbeams, int gsize, int stemdir));
64static struct GRPSYL *neighboring_note_beam_group P((struct GRPSYL *gs_p,
65 struct GRPSYL *first_p, int backwards) );
66static int chkgroupings P((int *side_p, struct GRPSYL *thisgs_p));
67static void do_beam P((double x1, double y1, double x2, double y2,
68 double halfwidth));
69static void pr_cresc P((struct STUFF *stuff_p));
70static void extend P((struct STUFF *stuff_p));
71static int tupdir1voice P((struct GRPSYL *gs_p));
72static int mirror P((char *str, int ch, int font));
73\f
74
75/* print things off of STAFF struct */
76
77void
78pr_staff(mll_p)
79
80struct MAINLL *mll_p; /* which main list struct holds the STAFF struct */
81
82{
83 struct STAFF *staff_p; /* mll_p->u.staff_p */
84 struct GRPSYL *grpsyl_p;/* current grpsyl */
85 struct MAINLL *barmll_p;/* to find TIMEDSSVs */
86 struct TIMEDSSV *tssv_p;/* for mid-measure parameter changes */
87 struct TIMEDSSV *t_p; /* walk through the mid-measure changes */
88 RATIONAL now; /* how far we are into measure */
89 char *savedlyr; /* saved copy of lyric syllable */
90 register int n; /* index thru notes in a group */
91 struct NOTE *noteinfo_p;/* current note */
92 int otherstaff; /* staff number for cross-staff stems */
93 int v; /* walk through voices or verses on the staff */
94 int size;
95
96
97 debug(512, "pr_staff file=%s lineno=%d staff=%d", mll_p->inputfile,
98 mll_p->inputlineno, mll_p->u.staff_p->staffno);
99
100 staff_p = mll_p->u.staff_p;
101
102 if ( svpath(staff_p->staffno, VISIBLE)->visible == NO) {
103 /* invisible staffs are easy to print... */
104 return;
105 }
106
107 /* do any syllables */
108 for (v = 0; v < staff_p->nsyllists; v++) {
109
110 /* if bottom staff of "between" lyric is invisible,
111 * the lyric silently disappears from output */
112 if (staff_p->sylplace[v] == PL_BETWEEN &&
113 svpath(staff_p->staffno + 1, VISIBLE)->visible
114 == NO) {
115 continue;
116 }
117
118 if (staff_p->syls_p[v] != (struct GRPSYL *) 0 &&
119 staff_p->syls_p[v]->inputlineno > 0) {
120 /* tell PostScript about user input line reference */
121 pr_linenum(staff_p->syls_p[v]->inputfile,
122 staff_p->syls_p[v]->inputlineno);
123 }
124
125 /* do all syllables for current verse/place */
126 for (grpsyl_p = staff_p->syls_p[v];
127 grpsyl_p != (struct GRPSYL *) 0;
128 grpsyl_p = grpsyl_p->next) {
129
130 if ( grpsyl_p->syl != (char *) 0) {
131
132 /* if <...> before or after syllable that
133 * were not used for placement, need to
134 * compensate for that */
135 lyr_compensate(grpsyl_p);
136
137 /* Extender printing can alter the lyrics
138 * string to get rid of the extender so it
139 * won't print with the syllable. But if we
140 * are printing pages using -o option we
141 * may need to have the original
142 * string preserved, because we may do this
143 * page again. So make a copy.
144 */
145 if ((savedlyr = malloc(strlen(grpsyl_p->syl) + 1))
146 == 0) {
147 l_no_mem(__FILE__, __LINE__);
148 }
149 strcpy(savedlyr, grpsyl_p->syl);
150
151 /* if syllable ends with a dash or underscore,
152 * they have to be spread between this syllable
153 * and the next */
154 (void) spread_extender(grpsyl_p, mll_p,
155 grpsyl_p->vno,
156 staff_p->sylplace[v], YES);
157
158 /* now print the syllable itself */
159 pr_string(grpsyl_p->c[AW], grpsyl_p->c[AY],
160 grpsyl_p->syl, J_LEFT,
161 grpsyl_p->inputfile,
162 grpsyl_p->inputlineno);
163
164 /* handle multiple syllables on one chord */
165 do_syl_joins(grpsyl_p->syl,
166 (double) grpsyl_p->c[AW],
167 (double) grpsyl_p->c[AY]);
168 /* if string was altered, put original back */
169 if (strcmp(grpsyl_p->syl, savedlyr) != 0) {
170 FREE(grpsyl_p->syl);
171 grpsyl_p->syl = savedlyr;
172 }
173 else {
174 FREE(savedlyr);
175 }
176 }
177 }
178 }
179
180 /* Find the BAR that would point to any TIMEDSSVs for this measure. */
181 for (barmll_p = mll_p->next; barmll_p->str != S_BAR; barmll_p = barmll_p->next) {
182 ;
183 }
184 t_p = tssv_p = barmll_p->u.bar_p->timedssv_p;
185
186 /* do notes, etc for each voice on the staff */
187 for (v = 0; v < MAXVOICES; v++) {
188
189 if (staff_p->groups_p[v] == 0) {
190 continue;
191 }
192
193 /* tab staff notes are handled differently */
194 if (is_tab_staff(staff_p->staffno) == YES) {
195 pr_tab_groups(staff_p->groups_p[v], mll_p);
196 continue;
197 }
198
199 /* Set up to handle mid-measure changes, if any */
200 if (tssv_p != 0) {
201 setssvstate(mll_p);
202 }
203 t_p = tssv_p;
204 now = Zero;
205
206 /* for each GRPSYL in the list for current voice */
207 for ( grpsyl_p = staff_p->groups_p[v];
208 grpsyl_p != (struct GRPSYL *) 0;
209 grpsyl_p = grpsyl_p->next) {
210
211 /* Apply any timed SSVs */
212 while (t_p != 0 && LE(t_p->time_off, now) ) {
213 asgnssv(&t_p->ssv);
214 t_p = t_p->next;
215 }
216 now = radd(now, grpsyl_p->fulltime);
217
218 if (grpsyl_p->clef != NOCLEF) {
219 float widthclef;
220 int clefsize;
221 clefsize = (3 * DFLT_SIZE) / 4;
222 widthclef = width(FONT_MUSIC, clefsize,
223 clefchar(grpsyl_p->clef));
224 pr_clef(grpsyl_p->staffno,
225 grpsyl_p->c[AW] -
226 (widthclef + CLEFPAD) * Staffscale,
227 YES, clefsize);
228 }
229 if (grpsyl_p->grpcont == GC_SPACE) {
230 /* very easy to print a space -- do nothing! */
231 continue;
232 }
233
234 if (grpsyl_p->grpcont == GC_REST) {
235 pr_rest(grpsyl_p, staff_p);
236 continue;
237 }
238
239 if (is_mrpt(grpsyl_p) == YES) {
240 pr_mrpt(grpsyl_p, staff_p);
241 continue;
242 }
243
244 /* If group has a cross-staff stem,
245 * figure out which is the other staff */
246 if (grpsyl_p->stemto == CS_ABOVE) {
247 for (otherstaff = grpsyl_p->staffno - 1;
248 otherstaff >= 1; otherstaff--) {
249 if (svpath(otherstaff, VISIBLE)->visible
250 == YES) {
251 break;
252 }
253 }
254 }
255 else if (grpsyl_p->stemto == CS_BELOW) {
256 for (otherstaff = grpsyl_p->staffno + 1;
257 otherstaff <= Score.staffs;
258 otherstaff++) {
259 if (svpath(otherstaff, VISIBLE)->visible
260 == YES) {
261 break;
262 }
263 }
264 }
265 else {
266 otherstaff = grpsyl_p->staffno;
267 }
268 if (otherstaff < 1 || otherstaff > Score.staffs) {
269 pfatal("failed to find other score for cross-staff stems for leger lines");
270 }
271
272 /* do each note in the group */
273 for (n = 0; n < grpsyl_p->nnotes; n++) {
274
275 size = (grpsyl_p->notelist[n].notesize ==
276 GS_NORMAL ? DFLT_SIZE :
277 SMALLSIZE);
278
279 /* we're going to need the NOTE info a lot;
280 * get its address */
281 noteinfo_p = &(grpsyl_p->notelist[n]);
282
283 /* do the note head */
284 pr_muschar(noteinfo_p->c[AX],
285 noteinfo_p->c[AY],
286 noteinfo_p->headchar,
287 size,
288 noteinfo_p->headfont);
289
290 /* do any accidental */
291 pr_accidental(noteinfo_p, grpsyl_p);
292
293 /* do any dots */
294 pr_note_dots(noteinfo_p, grpsyl_p->dots,
295 grpsyl_p->xdotr,
296 (double) grpsyl_p->c[AX],
297 (double) grpsyl_p->c[AY]);
298
299 /* print parentheses around note if any*/
300 if (noteinfo_p->note_has_paren == YES) {
301 pr_parens(noteinfo_p, grpsyl_p);
302 }
303
304 /* print small curve for 1/4 bends */
305 if (noteinfo_p->smallbend == YES) {
306 float adjust;
307
308 /* may have to move slightly to avoid
309 * flag. This is true if group is an
310 * unbeamed, stem-up group of 8th note
311 * or shorter duration */
312 if (grpsyl_p->basictime >= 8 &&
313 grpsyl_p->stemdir == UP
314 && grpsyl_p->beamloc
315 == NOITEM) {
316 adjust = 2.0 * STEPSIZE;
317 }
318 else {
319 adjust = STEPSIZE;
320 }
321 pr_sm_bend( (double)
322 noteinfo_p->c[AE] + adjust,
323 (double)
324 noteinfo_p->c[AY] + 0.5 * STEPSIZE);
325 }
326
327 /* do any leger lines */
328 if (grpsyl_p->stemto == CS_SAME ||
329 (n >= FNNI(grpsyl_p) &&
330 n <= LNNI(grpsyl_p) )) {
331 pr_leger(noteinfo_p, grpsyl_p,
332 grpsyl_p->staffno);
333 }
334 else {
335 /* notes are on a different staff */
336 pr_leger(noteinfo_p, grpsyl_p,
337 otherstaff);
338 }
339 }
340
341 /* do "with" lists */
342 pr_withlist(grpsyl_p);
343
344 /* do stems, flags, slash, and alt */
345 pr_stems(grpsyl_p);
346
347 /* print rolls */
348 if (gets_roll(grpsyl_p, staff_p, v) == YES) {
349 print_roll(grpsyl_p);
350 }
351 }
352
353 /* assign anything that happened after start of last group */
354 while (t_p != 0) {
355 asgnssv(&t_p->ssv);
356 t_p = t_p->next;
357 }
358
359 /* print tuplet numbers if any */
360 pr_tupnums(staff_p->groups_p[v], staff_p);
361
362 /* draw beams */
363 pr_beams(staff_p->groups_p[v], GV_NORMAL, GS_NORMAL);
364 pr_beams(staff_p->groups_p[v], GV_ZERO, GS_SMALL);
365 pr_beams(staff_p->groups_p[v], GV_NORMAL, GS_SMALL);
366 }
367
368 /* now do any associated STUFFs */
369 pr_stuff(staff_p->stuff_p, staff_p->staffno, mll_p);
370}
371\f
372
373/* if two syllables are to be joined, draw a little curved line between them */
374
375static void
376do_syl_joins (syl, west, y)
377
378char *syl; /* syllable string */
379double west; /* where syllable was printed */
380double y; /* where syllable was printed */
381
382{
383 int font, size;
384 char *p; /* pointer into syllable string */
385 float wid; /* of syllable up to space */
386 double x, east; /* of curved line */
387 double xinc, yinc; /* increment to move when doing curve */
388 double spacewid; /* width of ' ' */
389
390
391 int skipover = NO;
392
393 /* skip past any <...> */
394 font = syl[0];
395 size = syl[1];
396 for (p = syl + 2; *p != '\0'; p++) {
397 switch ( (unsigned) *p & 0xff) {
398 case STR_PRE:
399 case STR_U_PRE:
400 case STR_PST:
401 case STR_U_PST:
402 skipover = YES;
403 break;
404 case STR_PRE_END:
405 case STR_PST_END:
406 skipover = NO;
407 break;
408 case STR_MUS_CHAR:
409 p += 2;
410 break;
411 case STR_FONT:
412 font = *(p+1);
413 /*FALLTHRU*/
414 case STR_SIZE:
415 case STR_BACKSPACE:
416 case STR_PAGENUM:
417 case STR_NUMPAGES:
418 p++;
419 break;
420 case ' ':
421 if (skipover == NO && font <= EXT_FONT_OFFSET) {
422 /* temporarily shorten string to just before
423 * the space to get width of string up to
424 * that point */
425 *p = '\0';
426 wid = strwidth(syl);
427 *p = ' ';
428
429 /* Calculate dimensions
430 * and location of curve to be drawn. */
431 spacewid = width(font, size, ' ');
432 xinc = spacewid * 0.3;
433 yinc = spacewid * 0.15;
434 x = west + wid - STDPAD;
435 east = x + spacewid;
436
437 do_linetype(L_NORMAL);
438 do_moveto(x, y);
439 do_curveto(x + xinc, y - yinc,
440 east - xinc, y - yinc, east, y);
441 }
442 break;
443 default:
444 break;
445 }
446 }
447}
448\f
449
450/* print things in STUFF list */
451
452static void
453pr_stuff (stufflist_p, staffno, mll_p)
454
455struct STUFF *stufflist_p; /* which list of STUFF */
456int staffno; /* which staff the stuff is for */
457struct MAINLL *mll_p;
458
459{
460 char lch; /* last character in string */
461
462
463 /* do each item in stuff list */
464 for ( ; stufflist_p != (struct STUFF *) 0;
465 stufflist_p = stufflist_p->next) {
466
467 set_staffscale( (stufflist_p->all == YES) ? 0 : staffno);
468
469 switch (stufflist_p->stuff_type) {
470
471 case ST_MUSSYM:
472 case ST_OCTAVE:
473 case ST_ROM:
474 case ST_BOLD:
475 case ST_ITAL:
476 case ST_BOLDITAL:
477 /* do 'til' clause if any */
478 extend(stufflist_p);
479
480 /* if special case of ending in ~ or _, don't print the
481 * ~ or _ itself */
482 if ((lch = last_char(stufflist_p->string)) == '~' ||
483 lch == '_') {
484 stufflist_p->string[strlen(stufflist_p->string)
485 -1] = '\0';
486 }
487
488 /* print the string at specified place */
489 if (stufflist_p->string != (char *) 0) {
490
491
492 /* print grid if appropriate,
493 * otherwise just the string. */
494 if (stufflist_p->modifier != TM_CHORD ||
495 svpath(staffno, GRIDSWHEREUSED)
496 ->gridswhereused == NO ||
497 pr_grid(stufflist_p,
498 (stufflist_p->all == YES ?
499 0 : staffno))
500 == NO) {
501 pr_string (stufflist_p->c[AW],
502 stufflist_p->c[AY],
503 stufflist_p->string, J_LEFT,
504 stufflist_p->inputfile,
505 stufflist_p->inputlineno);
506 }
507 }
508
509 break;
510
511 case ST_CRESC:
512 case ST_DECRESC:
513 pr_cresc(stufflist_p);
514 break;
515
516 case ST_PEDAL:
517 pr_ped_char(stufflist_p, staffno);
518 break;
519
520 case ST_PHRASE:
521 pr_phrase(stufflist_p->crvlist_p, stufflist_p->modifier,
522 (stufflist_p->modifier == L_NORMAL ? YES : NO),
523 staffno);
524 break;
525
526 case ST_TIESLUR:
527 pr_tieslur(stufflist_p, mll_p, staffno);
528 break;
529
530 case ST_BEND:
531 pr_bend(stufflist_p->crvlist_p);
532 break;
533
534 case ST_TABSLUR:
535 pr_tabslur(stufflist_p->crvlist_p,
536 get_ts_style(stufflist_p, mll_p));
537 break;
538
539 case ST_MIDI:
540 break;
541
542 default:
543 pfatal("unknown stuff type");
544 break;
545 }
546 }
547}
548\f
549
550/* Print a guitar grid. Return YES if grid was found and printed, else NO. */
551
552static int
553pr_grid(stuff_p, staffnum)
554
555struct STUFF *stuff_p;
556int staffnum;
557
558{
559 struct GRID *grid_p;
560 double space;
561 float north, south;
562
563
564 if ((grid_p = findgrid(stuff_p->string)) == 0) {
565 /* placement phase should have printed a warning already */
566 return(NO);
567 }
568
569 /* print the grid name */
570 pr_string(stuff_p->c[AX] - strwidth(grid_p->name) / 2.0,
571 stuff_p->c[AY], grid_p->name, J_LEFT,
572 stuff_p->inputfile, stuff_p->inputlineno);
573
574 space = gridspace(staffnum);
575 gridsize(grid_p, staffnum, &north, &south, (float *) 0, (float *) 0);
576
577 do_grid(stuff_p->c[AX] - space * (grid_p->numstr - 1) / 2.0,
578 stuff_p->c[AS] - south,
579 space, grid_p, staffnum);
580 return(YES);
581}
582\f
583
584/* print ties and slurs */
585
586static void
587pr_tieslur(stuff_p, mll_p, staffno)
588
589struct STUFF *stuff_p;
590struct MAINLL *mll_p;
591int staffno;
592
593{
594 int ts_style; /* tie/slur style (L_DOTTED or L_DASHED) */
595
596
597 ts_style = get_ts_style(stuff_p, mll_p);
598
599 /* If tabslur, do that */
600 if ( stuff_p->curveno >= 0 && stuff_p->begnote_p->nslurto > 0
601 && IS_NOWHERE(stuff_p-> begnote_p->slurtolist
602 [stuff_p->curveno].octave)) {
603 pr_tabslur(stuff_p->crvlist_p, ts_style);
604 return;
605 }
606
607 /* print a regular tie/slur curve */
608 pr_phrase(stuff_p->crvlist_p, ts_style,
609 (ts_style == L_NORMAL ? YES : NO), staffno );
610}
611\f
612
613/* given a TIESLUR STUFF, return the line type to use for it */
614
615static int
616get_ts_style(stuff_p, mll_p)
617
618struct STUFF *stuff_p;
619struct MAINLL *mll_p;
620
621{
622 struct GRPSYL *prevgrp_p; /* for carryins */
623 int n; /* notelist index */
624
625
626 if (stuff_p->carryin == YES) {
627 prevgrp_p = prevgrpsyl(stuff_p->beggrp_p, &mll_p);
628 if (stuff_p->curveno >= 0) {
629 /* a carried-in slur. Need to find a note
630 * in previous group that is slurred to this one,
631 * and use its slurstyle. There is some chance
632 * that there could be more than one slur to this
633 * note from the same curveno
634 * and each slur could have a different style,
635 * in which case we no longer have enough information
636 * to know which to use, so we just use the first
637 * we find. */
638 for (n = 0; n < prevgrp_p->nnotes; n++) {
639
640 if (prevgrp_p->notelist[n].nslurto
641 <= stuff_p->curveno) {
642 /* couldn't have come from this grp */
643 continue;
644 }
645
646 if (prevgrp_p->notelist[n].slurtolist
647 [stuff_p->curveno].letter
648 == stuff_p->begnote_p->letter
649 && prevgrp_p->notelist[n]
650 .slurtolist[stuff_p->curveno].octave
651 == stuff_p->begnote_p->octave) {
652
653 return (prevgrp_p->notelist[n].
654 slurtolist[stuff_p->curveno]
655 .slurstyle);
656 }
657 }
658 }
659 else {
660 /* a carried-in tie. Need to find matching note
661 * in previous group, and use its tiestyle. */
662 for (n = 0; n < prevgrp_p->nnotes; n++) {
663 if (prevgrp_p->notelist[n].letter ==
664 stuff_p->begnote_p->letter &&
665 prevgrp_p->notelist[n].octave
666 == stuff_p->begnote_p->octave) {
667 return(prevgrp_p->notelist[n].tiestyle);
668 }
669 }
670 }
671 }
672
673 else {
674 if (stuff_p->curveno >= 0) {
675 /* a non-carried-in slur, use slurstyle */
676 return(stuff_p->begnote_p->slurtolist
677 [stuff_p->curveno].slurstyle);
678 }
679 else {
680 /* a non-carried-in tie, use tiestyle */
681 return(stuff_p->begnote_p->tiestyle);
682 }
683 }
684
685 /* if none of those cases applied, use normal */
686 return(L_NORMAL);
687}
688\f
689
690/* print a rest symbol */
691
692static void
693pr_rest(gs_p, staff_p)
694
695struct GRPSYL *gs_p; /* information about the rest to be printed */
696struct STAFF *staff_p;
697
698{
699 int muschar; /* which type of rest character to print */
700 int d; /* number of dots */
701 float adjust; /* to space dots properly */
702 float y; /* vertical location of rest */
703 int size;
704
705
706 if (gs_p->basictime < -1) {
707 /* multirest are a special case */
708 pr_multirest(gs_p, staff_p);
709 return;
710 }
711
712 /* draw the rest */
713 muschar = restchar(gs_p->basictime);
714 /* Half and whole rests outside the staff need to use the version
715 * that includes a ledger line. So check for that case.
716 * We used to use characters with ledgers all the time,
717 * but Ghostscript then sometimes seemed to misplace them
718 * by one pixel at certain magnifications, which looked bad. */
719 if (muschar == C_LL1REST || muschar == C_LL2REST) {
720 double halfst;
721 if (svpath(staff_p->staffno, STAFFLINES)->stafflines > 1) {
722 halfst = halfstaffhi(staff_p->staffno);
723 }
724 else {
725 halfst = 0.0;
726 }
727 /* The adjustments to halfst are chosen so that both half
728 * and whole rests will properly get leger lines when they
729 * are outside the staff, but not when inside.
730 */
731 if ( (gs_p->c[AN] > (staff_p->c[AY] + halfst + 1.7 * Stepsize)) ||
732 (gs_p->c[AN] < (staff_p->c[AY] - halfst - Stdpad)) ) {
733 muschar = (muschar == C_LL1REST ? C_1REST : C_2REST);
734 }
735 }
736 size = (gs_p->grpsize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE);
737 if (gs_p->is_meas == YES) {
738 /* measure rest is special case, have to move to middle */
739 pr_muschar( (gs_p->c[AW] + gs_p->c[AE]) / 2.0,
740 gs_p->c[AY], muschar, size, FONT_MUSIC);
741 }
742 else {
743 pr_muschar(gs_p->c[AX], gs_p->c[AY], muschar, size, FONT_MUSIC);
744 }
745
746 /* get ready to print any dots */
747 adjust = width(FONT_MUSIC, adj_size(size, Staffscale, (char *) 0,
748 -1), C_DOT) / 2.0;
749 y = _Cur[AY] + Stepsize;
750
751 /* print any dots after the rest */
752 for (d = 0; d < gs_p->dots; d++) {
753 /* each time we print a dot, the current location will get
754 * moved to just beyond that one */
755 pr_muschar(_Cur[AX] + adjust + (2.0 * Stdpad), y, C_DOT, size,
756 FONT_MUSIC);
757 }
758}
759\f
760
761/* print a measure repeat */
762
763void
764pr_mrpt(gs_p, staff_p)
765
766struct GRPSYL *gs_p;
767struct STAFF *staff_p;
768
769{
770 double x; /* horizontal position of number string */
771 double y, y_offset; /* vertical location */
772 double height, width; /* of meas num string */
773 char *numstr; /* ASCII version of numbers of measures */
774
775
776 /* measure repeat has to be moved to the middle of the measure */
777 pr_muschar( (gs_p->c[AW] + gs_p->c[AE]) / 2.0,
778 mr_y_loc(gs_p->staffno), C_MEASRPT, DFLT_SIZE, FONT_MUSIC);
779
780 if (svpath(gs_p->staffno, NUMBERMRPT)->numbermrpt == YES) {
781 /* print number above the staff */
782 y = Staffs_y[gs_p->staffno];
783 numstr = mrnum(staff_p, &x, &y_offset, &height, &width);
784 pr_string(x, y + y_offset, numstr, J_LEFT, (char *) 0, -1);
785 }
786}
787\f
788
789/* given a staff number, return the y at which to print the measure repeat
790 * or multirest symbols. If the number of staff lines is odd, this is the
791 * middle line, otherwise the line just above the middle. */
792
793static double
794mr_y_loc(staffno)
795
796int staffno;
797
798{
799 double y;
800
801 y = Staffs_y[staffno];
802 /* if even number of staff lines, move up a stepsize */
803 if ( (svpath(staffno, STAFFLINES)->stafflines & 1) == 0) {
804 y += Stepsize * (is_tab_staff(staffno) ? TABRATIO : 1.0);
805 }
806 return(y);
807}
808\f
809
810/* print the dots for dotted notes */
811
812static void
813pr_note_dots(noteinfo_p, numdots, xdotr, group_x, group_y)
814
815struct NOTE *noteinfo_p; /* which note to dot */
816int numdots; /* how many dots to print */
817double xdotr; /* relative x distance from note to print the dots */
818double group_x;
819double group_y; /* coord of group, dots are relative to this */
820
821{
822 float adjust; /* to place dots with proper spacing */
823
824
825 /* if note isn't dotted, nothing to do */
826 if (numdots <= 0) {
827 return;
828 }
829
830 adjust = width(FONT_MUSIC, adj_size(DFLT_SIZE, Staffscale,
831 (char *) 0, -1), C_DOT) / 2.0;
832
833 /* go to where first dot belongs */
834 set_cur(group_x + xdotr - adjust, group_y + noteinfo_p->ydotr);
835
836 /* print as many dots as necessary */
837 for ( ; numdots > 0; numdots--) {
838 pr_muschar(_Cur[AX] + adjust + (2.0 * Stdpad),
839 _Cur[AY], C_DOT, DFLT_SIZE, FONT_MUSIC);
840 }
841}
842\f
843
844/* print parentheses around a note. Should only be called if note_has_paren
845 * is YES */
846
847static void
848pr_parens(note_p, gs_p)
849
850struct NOTE *note_p;
851struct GRPSYL * gs_p;
852
853{
854 char paren_string[4];
855 double y;
856
857
858 /* make a parentheses string of proper size in internal string format */
859 (void) sprintf(paren_string, "%c%c(", FONT_TR,
860 adj_size((note_p->notesize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE),
861 Staffscale, (char *) 0, -1));
862
863 /* center the parentheses vertically on the Y on the note */
864 y = note_p->c[AY] - (strascent(paren_string)
865 - (strheight(paren_string) / 2.0));
866
867 /* print the left parenthesis */
868 pr_string(gs_p->c[AX] + note_p->wlparen, y,
869 paren_string, J_LEFT, (char *) 0, -1);
870
871 /* now do the right parenthesis */
872 paren_string[2] = ')';
873 pr_string(gs_p->c[AX] + note_p->erparen - strwidth(paren_string), y,
874 paren_string, J_LEFT, (char *) 0, -1);
875}
876\f
877
878/* print "with" lists */
879
880void
881pr_withlist(gs_p)
882
883struct GRPSYL *gs_p; /* GRPSYL that might have with lists */
884
885{
886 float y; /* where to start from */
887 float x;
888 float y_offset, sign;
889 float x_offset; /* to center first character of item on note */
890 float yposition; /* y coordinate at which to print */
891 float item_height; /* height of with list item */
892 int first_char; /* first char of string to print */
893 char *str_p; /* pointer into string to print */
894 int font, size;
895 int index; /* offset into with list */
896 int alternate; /* upside version of music symbol */
897 float ystaff; /* y of middle of staff */
898 float yline; /* y value of staff line */
899 float top, bot; /* top and bottom of item to be printed */
900 float pad; /* vertical padding around short items */
901 int sl; /* staff line index */
902 float adjusted_stepsize; /* STEPSIZE or STEPSIZE * TABRATIO
903 * depending on whether tab staff or not */
904 int stafflines; /* how many lines in current staff */
905 float minwithheight; /* MINWITHHEIGHT * Staffscale */
906
907
908 if (gs_p->nnotes == 0) {
909 return;
910 }
911
912 /* with goes forward from note opposite stem */
913 if (gs_p->normwith == YES) {
914 if (gs_p->stemdir == UP) {
915 y = gs_p->notelist [gs_p->nnotes - 1] .c[AS];
916 x = gs_p->notelist [gs_p->nnotes - 1] .c[AX];
917 sign = -1.0;
918 }
919 else {
920 y = gs_p->notelist[0].c[AN];
921 x = gs_p->notelist[0].c[AX];
922 sign = 1.0;
923 }
924 }
925 else {
926 /* with goes on opposite side than normal */
927 y = find_y_stem(gs_p);
928 if (gs_p->stemdir == DOWN) {
929 sign = -1.0;
930 /* whole notes /double wholes may have
931 * zero length stems so have to adjust */
932 if (gs_p->stemlen <= 0.0) {
933 y = gs_p->notelist[gs_p->nnotes - 1] .c[AS];
934 }
935 /* beamed notes stems effective stick out a little
936 * farther, so compensate for that */
937 if (gs_p->beamloc != NOITEM) {
938 y -= POINT;
939 }
940 }
941 else {
942 sign = 1.0;
943 if (gs_p->stemlen <= 0.0) {
944 y = gs_p->notelist[0].c[AN];
945 }
946 if (gs_p->beamloc != NOITEM) {
947 y += POINT;
948 }
949 }
950 x = gs_p->c[AX];
951 }
952
953 /* If a dot, wedge, and uwedge is the only item in the list,
954 * and it's on the stem side of a group with a stem, it is supposed
955 * to be aligned with the stem. */
956 if (gs_p->normwith == NO && gs_p->nwith == 1 &&
957 gs_p->basictime > 1 && gs_p->stemlen > 0.0 &&
958 is_music_symbol(gs_p->withlist[0]) == YES) {
959 font = gs_p->withlist[0][0];
960 size = gs_p->withlist[0][1];
961 str_p = gs_p->withlist[0] + 2;
962 first_char = next_str_char(&str_p, &font, &size);
963 if (first_char == C_DOT || first_char == C_WEDGE ||
964 first_char == C_UWEDGE) {
965 x = find_x_stem(gs_p);
966 }
967 }
968
969 y_offset = 0.0;
970 minwithheight = MINWITHHEIGHT * Staffscale;
971
972 /* do each item in with list */
973 for (index = 0; index < gs_p->nwith; index++) {
974
975 /* should center first character on x */
976 font = gs_p->withlist[index][0];
977 size = gs_p->withlist[index][1];
978 str_p = gs_p->withlist[index] + 2;
979 first_char = next_str_char(&str_p, &font, &size);
980
981 /* get upside down version if necessary */
982 if (sign == -1.0 && IS_MUSIC_FONT(font)) {
983 if ((alternate = mirror(gs_p->withlist[index],
984 first_char, font)) != first_char) {
985 *(str_p - 1) = (char) alternate;
986 }
987 }
988
989 x_offset = left_width( &(gs_p->withlist[index][0]) );
990
991 /* get height of item to print */
992 item_height = strheight(gs_p->withlist[index]);
993
994 /* if string is so short vertically
995 * it could get swallowed up in a staff
996 * line, adjust to fall in a space. Placement phase will have
997 * allowed MINWITHHEIGHT, so put in middle of that area unless
998 * that would fall on a line, in which case move somewhat */
999 if (item_height < minwithheight) {
1000 /* need to adjust this one. Start out by putting in
1001 * middle vertically of reserved area */
1002 yposition = y + y_offset + sign * minwithheight / 2.0;
1003
1004 /* no reason to adjust further for 1-line staffs */
1005 if ((stafflines = svpath(gs_p->staffno,
1006 STAFFLINES)->stafflines) > 1) {
1007
1008 /* get stepsize distance based on whether it
1009 * is a tab staff or not */
1010 adjusted_stepsize = (is_tab_staff(gs_p->staffno)
1011 == YES ? Stepsize * TABRATIO : Stepsize);
1012
1013 /* find y of middle of staff */
1014 ystaff = gs_p->notelist[0].c[AY]
1015 - (gs_p->notelist[0].stepsup
1016 * adjusted_stepsize);
1017
1018 /* take the extra vertical space alloted to this
1019 * with list item, and add 1/4 of it on top
1020 * and bottom as padding. If no staff line is
1021 * in between the boundaries of the item after
1022 * adding that padding, it's good enough where
1023 * it is. Otherwise, if a staff line falls above
1024 * the middle of the item, move the item
1025 * down into space. Otherwise move it
1026 * up into space.
1027 */
1028 pad = (minwithheight - item_height) / 4.0;
1029 top = yposition + (item_height / 2.0) + pad;
1030 bot = yposition - (item_height / 2.0) - pad;
1031
1032 /* check each staff line for collisions, from
1033 * bottom to top */
1034 for (sl = -(stafflines - 1);
1035 sl <= (stafflines - 1);
1036 sl += 2) {
1037
1038 /* find y of current staff line */
1039 yline = ystaff + (sl * adjusted_stepsize);
1040
1041 /* check if current staff line goes
1042 * through the item
1043 * as currently placed */
1044 if (yline < top && yline > bot) {
1045 /* collides--need to move */
1046
1047 if ((top - yline) >
1048 (yline - bot)) {
1049 /* move up to area
1050 * above the line */
1051 yposition += 2.0 * pad;
1052 /* if overdid the move,
1053 * move back a bit */
1054 if (yposition - yline -
1055 (item_height / 2.0)
1056 > 0.7 * adjusted_stepsize) {
1057 yposition -=
1058 0.4 * adjusted_stepsize;
1059 }
1060 }
1061 else {
1062 /* move down to area
1063 * below the line */
1064 yposition -= 2.0 * pad;
1065 if (yline - yposition -
1066 (item_height / 2.0)
1067 > 0.7 * adjusted_stepsize) {
1068 yposition +=
1069 0.4 * adjusted_stepsize;
1070 }
1071 }
1072
1073 /* only 1 staff line can
1074 * possibly interfere,
1075 * and we've found that one, so
1076 * can jump out of loop */
1077 break;
1078 }
1079 }
1080 }
1081
1082 /* adjust y_offset to include the area taken by item */
1083 y_offset += minwithheight * sign;
1084
1085 /* up to now, we've been using the center of the item,
1086 * so now adjust to baseline */
1087 if (sign > 0.0) {
1088 yposition += (item_height / 2.0)
1089 - strascent(gs_p->withlist[index]);
1090 }
1091 else {
1092 yposition -= (item_height / 2.0)
1093 - strdescent(gs_p->withlist[index]);
1094 }
1095 }
1096 else {
1097 /* not too short, handle normally */
1098 y_offset += item_height * sign;
1099 yposition = y + y_offset;
1100
1101 /* adjust to get to baseline of string from top or
1102 * bottom that we've used up to this point */
1103 if (sign > 0.0) {
1104 yposition -= strascent(gs_p->withlist[index]);
1105 }
1106 else {
1107 yposition += strdescent(gs_p->withlist[index]);
1108 }
1109 }
1110
1111 pr_string(x - x_offset, yposition, gs_p->withlist[index],
1112 J_CENTER, gs_p->inputfile, gs_p->inputlineno);
1113 }
1114}
1115\f
1116
1117/* print note stems and flags. Also print any slashes and alt lines */
1118
1119static void
1120pr_stems(grpsyl_p)
1121
1122struct GRPSYL *grpsyl_p; /* which group's stem to print */
1123
1124{
1125 float x, y1, y2;
1126 float sign; /* 1 or -1 direction for moving to draw slashes */
1127 float y_offset, offset, spacing; /* for where to draw slashes */
1128 float y_tilt; /* how much to move in y direction to get
1129 * proper tilt on slashes */
1130 float halfwidth; /* half width of slash or alt line */
1131 struct GRPSYL *first_p, *last_p; /* beginning and ending group
1132 * of beam group */
1133 int grpsize; /* grpsize field of grpsyl_p */
1134 int grpvalue; /* grpvalue field of grpsyl_p */
1135 int slash; /* to count number of slashes drawn */
1136 struct NOTE *note_p;
1137
1138
1139 /* if no stem, nothing to do */
1140 if ( grpsyl_p->stemlen <= 0 && grpsyl_p->slash_alt == 0) {
1141 return;
1142 }
1143
1144 /* figure out x coordinate of stem */
1145 x = find_x_stem(grpsyl_p);
1146
1147 /* if stem is up, start at bottom note, if down, at top */
1148 if (grpsyl_p->stemdir == UP) {
1149 note_p = &(grpsyl_p->notelist[ grpsyl_p->nnotes - 1]);
1150 y1 = note_p->c[AY];
1151 y2 = find_y_stem(grpsyl_p);
1152 sign = -1;
1153 }
1154 else {
1155 note_p = &(grpsyl_p->notelist [0]);
1156 y1 = note_p->c[AY];
1157 y2 = find_y_stem(grpsyl_p);
1158 sign = 1;
1159 }
1160
1161 if (note_p->headchar != 0) {
1162 y1 += stem_yoff(note_p->headchar, note_p->headfont,
1163 grpsyl_p->stemdir)
1164 * (note_p->notesize == GS_NORMAL
1165 ? Stepsize : Stepsize * SM_FACTOR);
1166 }
1167
1168 if (grpsyl_p->basictime >= 2) {
1169 /* print the stem */
1170 do_linetype(L_NORMAL);
1171
1172 draw_line(x, y1, x, y2);
1173
1174 /* attach any flags as appropriate */
1175 pr_flags(grpsyl_p, (double) x, (double) y2);
1176 }
1177
1178 /* print any slashes */
1179 if (grpsyl_p->slash_alt > 0) {
1180
1181 /* adjust for flags or beams. */
1182 if (grpsyl_p->basictime >= 8) {
1183 offset = (numbeams(grpsyl_p->basictime) - 1) *
1184 (grpsyl_p->grpsize == GS_NORMAL ? 5.0 : 4.0)
1185 * Stdpad;
1186 if (grpsyl_p->beamloc == NOITEM) {
1187 if (grpsyl_p->grpsize == GS_NORMAL) {
1188 offset += 8.0 * Stdpad;
1189 }
1190 else if (grpsyl_p->basictime != 16) {
1191 /* 16th small notes don't have any extra
1192 * stem to account for extra flag */
1193 offset += 3.0 * Stdpad;
1194 }
1195 }
1196 }
1197 else {
1198 offset = 0.0;
1199 }
1200
1201 if ( grpsyl_p->beamloc == NOITEM) {
1202 /* unbeamed things get hard-coded tilt value */
1203 if (grpsyl_p->grpvalue == GV_ZERO) {
1204 y_tilt = (grpsyl_p->stemdir == UP ? 3.5 : -3.5)
1205 * Stdpad;
1206 }
1207 else {
1208 y_tilt = 2.2 * Stdpad;
1209 }
1210 }
1211
1212 else {
1213 /* beamed. Need to slant slashes the same as beam */
1214
1215 grpsize = grpsyl_p->grpsize;
1216 grpvalue = grpsyl_p->grpvalue;
1217
1218 /* find beginning and ending stems */
1219 for (first_p = grpsyl_p; (first_p->beamloc != STARTITEM)
1220 || (first_p->grpsize != grpsize)
1221 || (first_p->grpvalue != grpvalue);
1222 first_p = first_p->prev) {
1223 ;
1224 }
1225
1226 for (last_p = grpsyl_p; (last_p->beamloc != ENDITEM)
1227 || (last_p->grpsize != grpsize)
1228 || (last_p->grpvalue != grpvalue);
1229 last_p = last_p->next) {
1230 ;
1231 }
1232
1233 /* calculate slope from them. We find the ratio of
1234 * y to x of the beam and apply that proportion to
1235 * the known x length of the slash to get the y height
1236 * of the slash, then divide by 2 to get the y distance
1237 * on either side of the stem. */
1238 y_tilt = (((find_y_stem(last_p) - find_y_stem(first_p))
1239 * (2.0 * slash_xlen(grpsyl_p)))
1240 / (find_x_stem(last_p)
1241 - find_x_stem(first_p))) / 2.0;
1242 y1 = find_y_stem(first_p);
1243 }
1244
1245 /* draw the slashes */
1246 pr_slashes(grpsyl_p, (double) x, (double) y2, (double) sign,
1247 (double) offset, (double) y_tilt);
1248 }
1249
1250 /* print alt group lines if any */
1251 if (grpsyl_p->slash_alt < 0) {
1252 struct GRPSYL *grpsyl2_p;
1253 float grp2x, grp2y; /* stem of second group */
1254 float grp1y_offset, grp2y_offset;
1255
1256
1257 if (grpsyl_p->next == (struct GRPSYL *) 0) {
1258 pfatal("missing second group in alt pair");
1259 }
1260
1261 /* figure out how wide to draw the lines and how far apart
1262 * to make them */
1263 if (grpsyl_p->grpsize == GS_NORMAL) {
1264 halfwidth = W_WIDE * Staffscale / PPI / 2.0;
1265 spacing = 5.0 * Stdpad;
1266 }
1267 else {
1268 halfwidth = W_MEDIUM * Staffscale / PPI / 2.0;
1269 spacing = 3.0 * Stdpad;
1270 }
1271
1272 /* find the stem coordinates of the second group */
1273 grpsyl2_p = grpsyl_p->next;
1274 grp2x = find_x_stem(grpsyl2_p);
1275 grp2y = find_y_stem(grpsyl2_p);
1276
1277 /* on notes shorter than half note, the lines don't go all the
1278 * way to the stems */
1279 if ( grpsyl_p->basictime >= 4) {
1280 /* figure out where the y of the end of the line is
1281 * by multiplying the x value by the tangent of the
1282 * angle of the line that would go all the way
1283 * between the stems */
1284 grp2y_offset = (grp2x - x - (6.0 * Stdpad))
1285 * ((grp2y - y2) / (grp2x - x));
1286 grp1y_offset = (6.0 * Stdpad)
1287 * ((grp2y - y2) / (grp2x - x));
1288 /* if 8th notes or shorter, get out of way of beams */
1289 offset = numbeams(grpsyl_p->basictime) * spacing;
1290 x += (6.0 * Stdpad);
1291 grp2x -= (6.0 * Stdpad);
1292 }
1293 else {
1294 grp1y_offset = 0.0;
1295 grp2y_offset = grp2y - y2;
1296 offset = 0.0;
1297 }
1298
1299 /* draw the alt lines */
1300 for (slash = -(grpsyl_p->slash_alt) - 1; slash >= 0; slash--) {
1301 y_offset = sign * slash * spacing + (sign * offset);
1302 do_newpath();
1303 do_moveto(x, y2 + y_offset + grp1y_offset - halfwidth);
1304 do_line(x, y2 + y_offset + grp1y_offset + halfwidth);
1305 do_line(grp2x, y2 + y_offset + grp2y_offset
1306 + halfwidth);
1307 do_line(grp2x, y2 + y_offset + grp2y_offset
1308 - halfwidth);
1309 do_closepath();
1310 do_fill();
1311 }
1312
1313 /* earlier phase wanted both groups in alt pair to have
1314 * slash_alt set, but now we've printed this one, so clear
1315 * the one on the following group, so it won't try to
1316 * print another alt group */
1317 grpsyl2_p->slash_alt = 0;
1318 }
1319}
1320\f
1321
1322void
1323pr_slashes(grpsyl_p, x, y, sign, offset, y_tilt)
1324
1325struct GRPSYL *grpsyl_p;
1326double x;
1327double y;
1328double sign;
1329double offset;
1330double y_tilt;
1331
1332{
1333 int slash;
1334 double xlen;
1335 float y_offset;
1336 float spacing;
1337 float halfwidth;
1338
1339
1340 /* get length based on note head size */
1341 xlen = slash_xlen(grpsyl_p);
1342
1343 /* figure out how wide to make the slashes and how far apart
1344 * to space them */
1345 if (grpsyl_p->grpsize == GS_NORMAL) {
1346 halfwidth = W_WIDE * Staffscale / PPI / 2.0;
1347 spacing = 5 * Stdpad;
1348 }
1349 else {
1350 halfwidth = W_MEDIUM * Staffscale / PPI / 2.0;
1351 spacing = 4 * Stdpad;
1352 }
1353
1354 for (slash = grpsyl_p->slash_alt; slash > 0; slash--) {
1355 y_offset = y + sign * (offset + (spacing * slash));
1356
1357 /* draw filled parallelogram */
1358 do_newpath();
1359 do_moveto(x - xlen, y_offset - y_tilt - halfwidth);
1360 do_line(x - xlen, y_offset - y_tilt + halfwidth);
1361 do_line(x + xlen, y_offset + y_tilt + halfwidth);
1362 do_line(x + xlen, y_offset + y_tilt - halfwidth);
1363 do_closepath();
1364 do_fill();
1365 }
1366}
1367
1368static double
1369slash_xlen(grpsyl_p)
1370
1371struct GRPSYL *grpsyl_p;
1372
1373{
1374 return (SLASHHORZ * Stepsize *
1375 (grpsyl_p->grpsize == GS_NORMAL ? 1.0 : SM_FACTOR));
1376}
1377\f
1378
1379/* print flags on 8th and shorter notes */
1380
1381static void
1382pr_flags(grpsyl_p, x, y)
1383
1384struct GRPSYL *grpsyl_p; /* group for which to draw flags */
1385double x;
1386double y; /* coord of end of stem */
1387
1388{
1389 int muschar; /* what kind of flag to print */
1390 float y_offset; /* from end of stem */
1391 int f; /* how many flags */
1392 int size;
1393
1394
1395 /* only 8th and shorter notes might have flags */
1396 if (grpsyl_p->basictime < 8) {
1397 return;
1398 }
1399
1400 /* if not a note, no flag */
1401 if (grpsyl_p->grpcont != GC_NOTES) {
1402 return;
1403 }
1404
1405 /* if beamed, no flag */
1406 if (grpsyl_p->beamloc != NOITEM) {
1407 return;
1408 }
1409
1410 /* figure out if up/down and whether small/reg */
1411 muschar = (grpsyl_p->stemdir == UP ? C_DNFLAG : C_UPFLAG);
1412 size = (grpsyl_p->grpsize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE);
1413
1414 /* do for each flag. f == 1 less than the number of flags, and is
1415 * how much to multiply the y_offset by for each flag */
1416 for ( f = numbeams(grpsyl_p->basictime) - 1; f >= 0; f--) {
1417
1418 switch (muschar) {
1419
1420 case C_UPFLAG:
1421 y_offset = f * (grpsyl_p->grpsize == GS_NORMAL ?
1422 FLAGSEP : SMFLAGSEP);
1423 break;
1424 case C_DNFLAG:
1425 y_offset = -f * (grpsyl_p->grpsize == GS_NORMAL ?
1426 FLAGSEP : SMFLAGSEP);
1427 break;
1428 default:
1429 pfatal("bad flag type");
1430 /*NOTREACHED*/
1431 return; /* to shut up compiler warning about unused */
1432 }
1433
1434 y_offset *= Staffscale;
1435
1436 /* now that we know where to place the flag, print it */
1437 pr_muschar(x + width(FONT_MUSIC,
1438 adj_size(size, Staffscale, (char *) 0, -1),
1439 muschar) / 2.0,
1440 y + y_offset, muschar, size, FONT_MUSIC);
1441 }
1442}
1443\f
1444
1445/* print any accidental */
1446
1447static void
1448pr_accidental(noteinfo_p, grpsyl_p)
1449
1450struct NOTE *noteinfo_p; /* info about the note being printed */
1451struct GRPSYL *grpsyl_p; /* info about the group conatining the note */
1452
1453{
1454 int muschar; /* which accidental symbol to draw */
1455 int size;
1456 int a_size; /* size adjusted for Staffscale */
1457
1458
1459 /* figure out which accidental symbol to use */
1460 muschar = acc2char(noteinfo_p->accidental);
1461
1462 /* if there is an accidental, print it at specified place */
1463 if (muschar != '\0') {
1464 size = (noteinfo_p->notesize == GS_NORMAL
1465 ? DFLT_SIZE : SMALLSIZE);
1466 a_size = adj_size(size, Staffscale, (char *) 0, -1);
1467 if (noteinfo_p->acc_has_paren == NO) {
1468 pr_muschar(grpsyl_p->c[AX] + noteinfo_p->waccr
1469 + width(FONT_MUSIC, a_size, muschar) / 2.0,
1470 noteinfo_p->c[AY], muschar, size, FONT_MUSIC);
1471 }
1472 else {
1473 /* have to print parentheses in addition to the
1474 * symbol for the accidental */
1475 char paren_string[4]; /* "(" or ")" in internal format */
1476 double offset; /* y adjustment of ( ) */
1477
1478 /* create string for "(" */
1479 (void) sprintf(paren_string, "%c%c%c",
1480 FONT_TR, a_size, '(');
1481
1482 /* to center things vertically on the note, need to
1483 * adjust parentheses downward by difference between
1484 * the ascent and half the height of the parenthesis */
1485 offset = strascent(paren_string) -
1486 (strheight(paren_string) / 2.0);
1487
1488 /* print the '(', the accidental, and the ')' */
1489 pr_string(grpsyl_p->c[AX] + noteinfo_p->waccr,
1490 noteinfo_p->c[AY] - offset,
1491 paren_string, J_LEFT,
1492 grpsyl_p->inputfile,
1493 grpsyl_p->inputlineno);
1494
1495 pr_muschar(_Cur[AX] +
1496 width(FONT_MUSIC, a_size, muschar) / 2.0,
1497 noteinfo_p->c[AY], muschar, size,
1498 FONT_MUSIC);
1499
1500 (void) sprintf(paren_string, "%c%c%c",
1501 FONT_TR, a_size, ')');
1502 pr_string(_Cur[AX], noteinfo_p->c[AY] - offset,
1503 paren_string, J_LEFT,
1504 grpsyl_p->inputfile,
1505 grpsyl_p->inputlineno);
1506 }
1507 }
1508}
1509\f
1510
1511/* print appropriate number of leger lines */
1512
1513static void
1514pr_leger(noteinfo_p, gs_p, staffno)
1515
1516struct NOTE *noteinfo_p; /* info about current note */
1517struct GRPSYL *gs_p; /* which group contains the note */
1518int staffno; /* which staff to draw relative to */
1519
1520{
1521 register int lines2draw; /* how many leger lines are needed */
1522 float sign; /* 1 for above or -1 for below staff */
1523 float y; /* vertical position */
1524 float left_leger, right_leger; /* how far legers stick out from note */
1525 int is_intermediate; /* YES if inner, NO if outermost */
1526 int on_other_side; /* YES if on "wrong" side of stem */
1527
1528
1529 if ((lines2draw = numlegers(noteinfo_p)) < 1) {
1530 /* No legers needed for this note */
1531 return;
1532 }
1533
1534 /* Is note above or below the middle of the staff? */
1535 sign = noteinfo_p->stepsup > 0.0 ? 1.0 : -1.0;
1536
1537 /* For notes on the "wrong" side of the stem, we will only need
1538 * to draw the outermost leger. */
1539 if ( (gs_p->stemdir == DOWN && noteinfo_p->c[AE] < gs_p->c[AX]) ||
1540 (gs_p->stemdir == UP && noteinfo_p->c[AW] > gs_p->c[AX])) {
1541 on_other_side = YES;
1542 }
1543 else {
1544 on_other_side = NO;
1545 }
1546
1547 /* Draw the legers */
1548 do_linetype(L_NORMAL);
1549 is_intermediate = NO;
1550 for ( ; lines2draw > 0; lines2draw--) {
1551
1552 /* Find the y location for the leger line.
1553 * They are 2 Stepsizes apart,
1554 * beginning at the edge of the staff */
1555 y = Staffs_y[staffno]
1556 + (sign * (2 + lines2draw) * (2 * Stepsize));
1557
1558 /* If things are packed really close together, leger lines
1559 * could bleed into leger lines of the neighboring chord.
1560 * We need to see if there are any potentially
1561 * troublesome leger lines on either side, and shorten
1562 * this leger if necessary to avoid them.
1563 */
1564 left_leger = leger_length(noteinfo_p, gs_p->prev, lines2draw,
1565 YES, is_intermediate);
1566 right_leger = leger_length(noteinfo_p, gs_p->next, lines2draw,
1567 NO, is_intermediate);
1568
1569 draw_line( noteinfo_p->c[AW] - left_leger, y,
1570 noteinfo_p->c[AE] + right_leger, y);
1571 is_intermediate = YES;
1572
1573 /* For notes on the "wrong" side of the stem, we only need
1574 * to draw the outermost leger */
1575 if (on_other_side == YES) {
1576 break;
1577 }
1578 }
1579}
1580\f
1581
1582/* How many legers to draw is absolute value of stepsup divided
1583 * by 2 minus the 2 lines that are already in the staff.
1584 * Note that we only do legers on normal 5-line staffs. */
1585
1586static int
1587numlegers(noteinfo_p)
1588
1589struct NOTE *noteinfo_p;
1590
1591{
1592 return (abs(noteinfo_p->stepsup) / 2) - 2;
1593}
1594\f
1595
1596/* If things are packed really close together, leger lines
1597 * could bleed into leger lines of the neighboring chord.
1598 * This function will detect that and shorten them if necessary.
1599 * To be completely correct, it should check all the voices on the
1600 * staff, but that would be quite a bit more work, and chances of colliding
1601 * with another voice's notes is not very high, so we just check
1602 * the voice of the note in question.
1603 */
1604
1605static double
1606leger_length(noteinfo_p, othergs_p, lines, other_is_prev, is_intermediate)
1607
1608struct NOTE *noteinfo_p; /* we are finding leger length for this note */
1609struct GRPSYL *othergs_p; /* check this group for a too close note */
1610int lines; /* how many leger lines to draw */
1611int other_is_prev; /* YES if othergs_p is ->prev, NO if ->next */
1612int is_intermediate; /* YES if interior, NO is outermost leger */
1613
1614{
1615 int n; /* note index */
1616 double distance; /* between 2 notes */
1617 double length = 2.2 * Stdpad; /* length of leger. Init to default */
1618 double adjust; /* inners can be shortened extra */
1619
1620
1621 if (othergs_p == 0) {
1622 /* No group to collide with */
1623 return(length);
1624 }
1625 if (othergs_p->grpcont != GC_NOTES) {
1626 /* Can't have leger lines */
1627 return(length);
1628 }
1629
1630 /* Legers that are not through or right next to the note
1631 * can be shortened a bit more to make their gap show up better.
1632 */
1633 adjust = (is_intermediate ? 0.5 * Stdpad : 0.0);
1634
1635 /* See if othergs_p has any notes that are too close */
1636 for (n = 0; n < othergs_p->nnotes; n++) {
1637 if (numlegers( &(othergs_p->notelist[n]) ) < lines) {
1638 /* Neighboring note has fewer legers; not relevant */
1639 continue;
1640 }
1641 if (noteinfo_p->stepsup > 0 &&
1642 othergs_p->notelist[n].stepsup < 0) {
1643 /* Neighboring note's legers are below, ours above.
1644 * The remaining neighboring notes are irrelevant. */
1645 break;
1646 }
1647 if (noteinfo_p->stepsup < 0 &&
1648 othergs_p->notelist[n].stepsup > 0) {
1649 /* Neighboring note's legers are above, ours below.
1650 * Haven't gotten to any potentially relevant
1651 * notes yet. */
1652 continue;
1653 }
1654
1655 /* We have a pair of notes whose leger lines might collide.
1656 * See how far apart they are. */
1657 if (other_is_prev == YES) {
1658 distance = noteinfo_p->c[AW] - othergs_p->notelist[n].c[AE];
1659 }
1660 else {
1661 distance = othergs_p->notelist[n].c[AW] - noteinfo_p->c[AE];
1662 }
1663
1664 /* Ideally, we try to make leger lines 2.2 Stdpads on each side,
1665 * but if that leaves less than 2.0 Stdpads between them,
1666 * we shorten them until they get down to 0.7 Stdpads.
1667 * After that we let them join. That should only happen
1668 * if things are really tightly packed.
1669 * The 6.4 is from two legers of 2.2 each with 2.0 between.
1670 */
1671 if (distance < 6.4 * Stdpad) {
1672 /* Too close. Will have to shorten */
1673 length = (distance - (2.0 * Stdpad)) / 2.0 - adjust;
1674 if (length < 0.7 * Stdpad - adjust) {
1675 /* No shorter than minimum */
1676 length = 0.7 * Stdpad - adjust;
1677 }
1678 }
1679 }
1680 return (length);
1681}
1682\f
1683
1684/* given the first group of a tuplet, return, via pointers, the x coords of
1685 * the left and right boundaries of the tuplet number and its height.
1686 * Return pointer to static string containing the tuplet number itself in
1687 * internal string format */
1688
1689char *
1690tupnumsize(gs_p, west_p, east_p, height_p, staff_p)
1691
1692struct GRPSYL *gs_p;
1693float *west_p; /* west coord returned here */
1694float *east_p; /* east coord returned here */
1695float *height_p; /* string height returned here */
1696struct STAFF *staff_p; /* staff pointing at gs_p */
1697
1698{
1699 char *numstr; /* tuplet number as internal string */
1700 struct GRPSYL *last_gs_p; /* last group in tuplet */
1701 float num_x; /* x coord of number */
1702 float halfnumwidth; /* half the width of numstr */
1703 int tupside;
1704 int all_cue;
1705
1706
1707 /* assume all cue till proven otherwise */
1708 all_cue = YES;
1709
1710 /* find x of middle of tuplet number */
1711 if (gs_p->tuploc == LONEITEM) {
1712 if (gs_p->grpsize != GS_SMALL) {
1713 all_cue = NO;
1714 }
1715 num_x = gs_p->c[AX];
1716 }
1717 else {
1718 for (last_gs_p = gs_p->next; last_gs_p != (struct GRPSYL *) 0;
1719 last_gs_p = last_gs_p->next) {
1720 if (gs_p->grpsize != GS_SMALL) {
1721 all_cue = NO;
1722 }
1723 if (last_gs_p->tuploc == ENDITEM) {
1724 break;
1725 }
1726 }
1727 if (last_gs_p == (struct GRPSYL *) 0) {
1728 pfatal("missing end tuplet in tupnumsize");
1729 }
1730
1731 /* Usually, the x location of tuplet number is average of
1732 * beginning and end group x coords. But if there is a beam
1733 * and the number is being printed on the beam side,
1734 * and there is no bracket being printed,
1735 * it generally looks better to center between the stems.
1736 */
1737 tupside = tupdir(gs_p, staff_p);
1738 if (gs_p->beamloc == STARTITEM && last_gs_p->beamloc == ENDITEM
1739 && ((tupside == PL_ABOVE && gs_p->stemdir == UP)
1740 || (tupside == PL_BELOW && gs_p->stemdir == DOWN))
1741 && tupgetsbrack(gs_p) == NO) {
1742 num_x = (find_x_stem(last_gs_p) + find_x_stem(gs_p)) / 2.0;
1743 }
1744 else {
1745 num_x = (last_gs_p->c[AX] + gs_p->c[AX]) / 2.0;
1746 }
1747 }
1748
1749 /* prepare the string to print */
1750 numstr = num2str(gs_p->tupcont);
1751 /* force to 11-point newcentury bold-italics, unless all cue,
1752 * then smaller */
1753 numstr[0] = FONT_NX;
1754 numstr[1] = (char) adj_size((all_cue == YES ? 9 : 11), Staffscale,
1755 (char *) 0, -1);
1756 halfnumwidth = strwidth(numstr) / 2.0;
1757
1758 /* return the values */
1759 *west_p = num_x - halfnumwidth - Stdpad;
1760 *east_p = num_x + halfnumwidth + Stdpad;
1761 *height_p = strheight(numstr);
1762 return(numstr);
1763}
1764\f
1765
1766/* go through measure. If there are any tuplets, print a number by them,
1767 * along with bracket if appropriate. */
1768
1769static void
1770pr_tupnums(gs_p, staff_p)
1771
1772struct GRPSYL *gs_p; /* start from here to walk through list of groups */
1773struct STAFF *staff_p; /* staff pointing to gs_p */
1774
1775{
1776 struct GRPSYL *first_gs_p = 0; /* where to begin tuplet label.
1777 * Initialization is just to shut up bogus
1778 * compiler warning. */
1779 struct GRPSYL *g_p; /* to check for all spaces */
1780 float x1, x2; /* where tuplet bracket begins & ends */
1781 float num_y; /* y of tuplet number */
1782 float y1, y2; /* y coord of ends of bracket */
1783 char *numstr; /* ASCII version of tuplet number */
1784 float numeast, numwest; /* boundaries of tuplet number */
1785 float height; /* of tuplet number */
1786 float y_adjust; /* adjustment for space taken by number */
1787 float x_adjust; /* from group x to where bracket goes */
1788 int num_notes = 0; /* how many notes in tuplet */
1789 int need_brack = NO; /* set to YES if the beaming of the notes
1790 * doesn't match the tuplet boundaries */
1791 float brackdir; /* how far in y direction to draw bracket ends
1792 * (positive or negative depending on the
1793 * direction that the bracket points) */
1794 int size;
1795
1796
1797 /* go through all the groups */
1798 for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
1799
1800 switch (gs_p->tuploc) {
1801
1802 case NOITEM:
1803 break;
1804
1805 case STARTITEM:
1806 /* remember beginning for later use */
1807 first_gs_p = gs_p;
1808 num_notes = 1;
1809 break;
1810
1811 case INITEM:
1812 num_notes++;
1813 break;
1814
1815 case LONEITEM:
1816 first_gs_p = gs_p;
1817 /*FALLTHRU*/
1818
1819 case ENDITEM:
1820 num_notes++;
1821
1822 /* if not to be printed, nothing to do except reinit */
1823 if (gs_p->printtup == PT_NEITHER) {
1824 num_notes = 0;
1825 break;
1826 }
1827
1828 /* we don't do tuplet numbers on cross-staff beams--
1829 * it's virtually impossible to know where to put them
1830 */
1831 if (gs_p->beamto != CS_SAME) {
1832 num_notes = 0;
1833 break;
1834 }
1835
1836 /* If the tuplet is all spaces,
1837 * there is nothing to draw a bracket over,
1838 * and trying to do so causes problems,
1839 * so don't try. */
1840 for (g_p = first_gs_p; g_p->tuploc != NOITEM;
1841 g_p = g_p->next) {
1842 if (g_p->grpcont != GC_SPACE) {
1843 /* good--it has something
1844 * other than spaces */
1845 break;
1846 }
1847
1848 if (g_p->tuploc == ENDITEM
1849 || g_p->tuploc == LONEITEM) {
1850 /* reached end of all-space tuplet */
1851 break;
1852 }
1853 }
1854 if (g_p->grpcont == GC_SPACE) {
1855 /* must have been all spaces */
1856 num_notes = 0;
1857 break;
1858 }
1859
1860 /* if tuplet doesn't match beaming, need bracket */
1861 need_brack = tupgetsbrack(first_gs_p);
1862
1863 if (num_notes == 0) {
1864 pfatal("no notes in tuplet");
1865 }
1866
1867 numstr = tupnumsize(first_gs_p, &numwest, &numeast,
1868 &height, staff_p);
1869
1870 if (tupdir(first_gs_p, staff_p) == PL_ABOVE) {
1871 y_adjust = strascent(numstr);
1872 y1 = first_gs_p->c[AN] - y_adjust;
1873 y2 = gs_p->c[AN] - y_adjust;
1874 brackdir = -3.0 * Stdpad;
1875 }
1876 else {
1877 /* print below */
1878 y1 = first_gs_p->c[AS];
1879 y2 = gs_p->c[AS];
1880 brackdir = 3.0 * Stdpad;
1881 }
1882
1883 /* print tuplet number at correct place */
1884 y1 += first_gs_p->tupextend;
1885 y2 += gs_p->tupextend;
1886 num_y = (y1 + y2) / 2.0;
1887 pr_string(numwest + Stdpad, num_y, numstr, J_LEFT,
1888 gs_p->inputfile, gs_p->inputlineno);
1889
1890 /* add tuplet bracket if necessary */
1891 if (need_brack == YES) {
1892 do_linetype(L_NORMAL);
1893
1894 /* adjust to reach edge of note head */
1895 size = (first_gs_p->grpsize == GS_NORMAL ?
1896 DFLT_SIZE : SMALLSIZE)
1897 * Staffscale;
1898 if (first_gs_p->grpcont == GC_NOTES) {
1899 x_adjust = widest_head(first_gs_p)
1900 * Staffscale / 2.0;
1901 }
1902 else if (first_gs_p->grpcont == GC_REST) {
1903 x_adjust = width(FONT_MUSIC, size,
1904
1905 restchar(first_gs_p->basictime))
1906 / 2.0;
1907 }
1908 else {
1909 x_adjust = 0.0;
1910 }
1911 x1 = first_gs_p->c[AX] - x_adjust;
1912
1913 size = (gs_p->grpsize == GS_NORMAL ?
1914 DFLT_SIZE : SMALLSIZE)
1915 * Staffscale;
1916 if (gs_p->grpcont == GC_NOTES) {
1917 x_adjust = widest_head(gs_p)
1918 * Staffscale / 2.0;
1919 }
1920 else if (gs_p->grpcont == GC_REST) {
1921 x_adjust = width(FONT_MUSIC, size,
1922 restchar(gs_p->basictime))
1923 / 2.0;
1924 }
1925 else {
1926 x_adjust = 0.0;
1927 }
1928 x2 = gs_p->c[AX] + x_adjust;
1929
1930 /* move the bracket line up from the baseline
1931 * of the number */
1932 y1 += (4.0 * Stdpad);
1933 y2 += (4.0 * Stdpad);
1934 num_y += (4.0 * Stdpad);
1935
1936 /* figure out how much to adjust y from num_y
1937 * to account for the space taken up by the
1938 * number. Use ratio of similar triangles. */
1939 if (numwest - x1 == 0.0) {
1940 /* avoid any chance of divide by 0 */
1941 y_adjust = 0.0;
1942 }
1943 else {
1944 y_adjust = (((numeast - numwest
1945 + (Stdpad * 2.0)) *
1946 (num_y - y1)) / (numeast - x1))
1947 / 2.0;
1948 }
1949
1950 draw_line(x1, y1, numwest - Stdpad,
1951 num_y - y_adjust);
1952 draw_line(numeast + Stdpad,
1953 num_y + y_adjust, x2, y2);
1954 draw_line(x1, y1, x1, y1 + brackdir);
1955 draw_line(x2, y2, x2, y2 + brackdir);
1956 }
1957
1958 /* re-init in case other tuplets in same measure */
1959 num_notes = 0;
1960
1961 break;
1962
1963 default:
1964 pfatal("bad tuplet type");
1965 break;
1966 }
1967 }
1968}
1969\f
1970
1971/* utility function. Given the first group in a tuplet, return YES if it
1972 * is to have a bracket printed. It does if the tuplet itself is to be printed,
1973 * and if not a LONEITEM and if any of the beamlocs do not match the tuploc */
1974
1975int
1976tupgetsbrack(gs_p)
1977
1978struct GRPSYL *gs_p; /* first group of tuplet */
1979
1980{
1981 /* If nothing is to be printed or number only, no bracket */
1982 if (gs_p->printtup == PT_NEITHER || gs_p->printtup == PT_NUMBER) {
1983 return(NO);
1984 }
1985
1986 /* single chord tuplets never get a bracket -- not enough room
1987 * to draw one */
1988 if (gs_p->tuploc == LONEITEM) {
1989 return(NO);
1990 }
1991
1992 /* if user insists on a bracket, we oblige */
1993 if (gs_p->printtup == PT_BOTH) {
1994 return(YES);
1995 }
1996
1997 /* check for mismatches between beamloc and tuploc. */
1998 for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
1999 /* grace notes don't count */
2000 if (gs_p->grpvalue == GV_ZERO) {
2001 continue;
2002 }
2003
2004 if (gs_p->tuploc != gs_p->beamloc) {
2005 return(YES);
2006 }
2007 if (gs_p->tuploc == ENDITEM) {
2008 /* matched beam everywhere, so no bracket needed */
2009 return(NO);
2010 }
2011 }
2012 pfatal("missing end tuplet");
2013
2014 /*NOTREACHED*/
2015 return(NO);
2016}
2017\f
2018
2019/* utility function to return PL_ABOVE or PL_BELOW
2020 * depending on whether the number for
2021 * the given tuplet should get printed above or below the groups */
2022/* Can be passed any group in the tuplet. If not the first, it will find the
2023 * first and go from there */
2024
2025int
2026tupdir(gs_p, staff_p)
2027
2028struct GRPSYL *gs_p; /* group in tuplet */
2029struct STAFF *staff_p; /* staff pointing to gs_p */
2030
2031{
2032 RATIONAL starttime, endtime; /* begin & end time of tuplet */
2033 struct GRPSYL *save_gs_p; /* temporarily save value of gs_p */
2034 int othervoice; /* array subscript in staff_p->groups_p
2035 * of the other voice on this staff */
2036 int vscheme; /* V_* value */
2037 RATIONAL smalltime;
2038
2039
2040 smalltime.n = 1;
2041 smalltime.d = 2 * MAXBASICTIME;
2042
2043
2044 switch (gs_p->tuploc) {
2045
2046 case LONEITEM:
2047 case STARTITEM:
2048 /* this is the one we want */
2049 break;
2050
2051 case NOITEM:
2052 pfatal("arg of tupdir is not in a tuplet");
2053 /*NOTREACHED*/
2054 break;
2055 default:
2056 /* have to back up to beginning of tuplet first */
2057 for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->prev) {
2058 if (gs_p->tuploc == STARTITEM) {
2059 break;
2060 }
2061 }
2062 if (gs_p == (struct GRPSYL *) 0) {
2063 pfatal("can't find beginning of tuplet");
2064 }
2065 break;
2066 }
2067
2068 /* figure out which side. First determine vscheme */
2069
2070 /* there is a circumstance where we're looking at an entire score,
2071 * (in relvert), and if some of the score has V_1 and some of it
2072 * doesn't, it's possible for us to get confused and think something
2073 * isn't V_1 when it is. We would then try to look at the other
2074 * voice, which is null, and would blow up. To avoid this, if one
2075 * voice is null, treat measure as V_1 regardless of what vscheme
2076 * might lead us to believe.
2077 */
2078 if (staff_p->groups_p[1] == (struct GRPSYL *) 0) {
2079 return(tupdir1voice(gs_p));
2080 }
2081
2082 /* voice 3 pays no attention to any other voices. */
2083 if (gs_p->vno == 3) {
2084 return(tupdir1voice(gs_p));
2085 }
2086
2087 if ((vscheme = svpath(staff_p->staffno, VSCHEME)->vscheme) == V_1) {
2088 return(tupdir1voice(gs_p));
2089 }
2090 else if (vscheme == V_2OPSTEM) {
2091 /* 2 opposing stem voices, always put tuplet above voice 1 and
2092 * below voice 2 */
2093 if (gs_p->tupside != PL_UNKNOWN) {
2094 l_warning(gs_p->inputfile, gs_p->inputlineno,
2095 "tuplet side specification not valid when vscheme=2o");
2096 /* fix so we don't print error again if called
2097 * again on this tuplet */
2098 gs_p->tupside = PL_UNKNOWN;
2099 }
2100 return(gs_p->vno == 1 ? PL_ABOVE : PL_BELOW);
2101 }
2102 else {
2103 /* find the time period taken by tuplet */
2104 save_gs_p = gs_p;
2105 starttime = Zero;
2106 /* find time to where tuplet begins */
2107 for (gs_p = gs_p->prev; gs_p != (struct GRPSYL *) 0;
2108 gs_p = gs_p->prev) {
2109 starttime = radd(starttime, gs_p->fulltime);
2110 }
2111 /* find time up to last note of tuplet */
2112 endtime = starttime;
2113 for (gs_p = save_gs_p; gs_p->tuploc != ENDITEM
2114 && gs_p->tuploc != LONEITEM;
2115 gs_p = gs_p->next) {
2116 endtime = radd(endtime, gs_p->fulltime);
2117 }
2118 /* add on a little bit for the final group of the tuplet */
2119 endtime = radd(endtime, smalltime);
2120
2121 /* now check if other voice has space or not */
2122 othervoice = (gs_p->vno == 1 ? 1 : 0);
2123 if (hasspace(staff_p->groups_p [othervoice], starttime, endtime)
2124 == YES) {
2125 /* other voice is space: treat like V_1 */
2126 return(tupdir1voice(save_gs_p));
2127 }
2128 else {
2129 /* other voice not space: treat like V_2OPSTEM */
2130 if (gs_p->tupside != PL_UNKNOWN) {
2131 l_warning(gs_p->inputfile, gs_p->inputlineno,
2132 "tuplet side specification not valid when there are two voices");
2133 /* fix so we don't print error again if called
2134 * again on this tuplet */
2135 gs_p->tupside = PL_UNKNOWN;
2136 }
2137 return(gs_p->vno == 1 ? PL_ABOVE : PL_BELOW);
2138 }
2139 }
2140}
2141\f
2142
2143/* return PL_ABOVE or PL_BELOW for tup location assuming a single voice */
2144
2145static int
2146tupdir1voice(gs_p)
2147
2148struct GRPSYL *gs_p; /* first group of tuplet */
2149
2150{
2151 int stemdirsum; /* sum of stem directions to see if mostly up or down */
2152
2153
2154 /* if user specified a direction, the answer is easy */
2155 if (gs_p->tupside != PL_UNKNOWN) {
2156 return(gs_p->tupside);
2157 }
2158
2159 /* Count up stem directions. Whichever side
2160 * has more stems, put it on that side. In case of tie,
2161 * arbitrarily choose above. */
2162 stemdirsum = 0;
2163 for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
2164 if (gs_p->grpcont == GC_NOTES && gs_p->grpvalue != GV_ZERO) {
2165 stemdirsum += (gs_p->stemdir == UP ? 1 : -1);
2166 }
2167 if (gs_p->tuploc == LONEITEM || gs_p->tuploc == ENDITEM) {
2168 break;
2169 }
2170 }
2171
2172 return(stemdirsum >= 0 ? PL_ABOVE : PL_BELOW);
2173}
2174\f
2175
2176/* go through measure, printing any beams. Gets called once for normal sized
2177 * notes, once for cue notes, and once for grace note. */
2178
2179static void
2180pr_beams(gs_p, grpvalue, grpsize)
2181
2182struct GRPSYL *gs_p; /* list of grpsyls for current measure
2183 * of current voice */
2184int grpvalue; /* GV_NORMAL, GV_ZERO */
2185int grpsize; /* GS_NORMAL, GS_SMALL */
2186
2187{
2188 struct GRPSYL *startbeam_p; /* first in beam group */
2189 int t; /* 8, 16, etc for basictimes */
2190
2191
2192 /* go through all the grpsyls in measure */
2193 for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
2194
2195 /* skip until we find a STARTITEM
2196 * on the relevant kind of group */
2197 if (gs_p->beamloc != STARTITEM || gs_p->grpvalue != grpvalue
2198 || gs_p->grpsize != grpsize) {
2199 continue;
2200 }
2201
2202 /* when there are cross-staff beams, we will find the beam
2203 * on both staffs, but only need to draw it once. So skip
2204 * it the second time */
2205 if (gs_p->beamto == CS_ABOVE) {
2206 continue;
2207 }
2208
2209 /* find the matching ENDITEM */
2210 for (startbeam_p = gs_p; gs_p != 0 && (gs_p->beamloc != ENDITEM
2211 || gs_p->grpvalue != grpvalue
2212 || gs_p->grpsize != grpsize);
2213 gs_p = gs_p->next) {
2214
2215 }
2216 if (gs_p == 0) {
2217 pfatal("pr_beams couldn't find end of beam group");
2218 }
2219
2220 /* now go through beam group drawing beams for 8th notes,
2221 * then 16th, etc */
2222 for (t = 8; t <= MAXBASICTIME; t <<= 1) {
2223 if (draw_beams(startbeam_p, gs_p, t, grpsize, grpvalue)
2224 <= 0) {
2225 break;
2226 }
2227 }
2228 }
2229}
2230\f
2231
2232/* In the case of cross-staff beams with the above staff's stems down,
2233 * and the below staff's stems up, we need to do extra work.
2234 * This function builds up a mesh of structs that represent the beams,
2235 * with a row of CSBINFO structs linked horizontally for each beam,
2236 * and vertical links at each stem. end_bm_offset() then uses this information
2237 * to figure out where along the stem a beam ends.
2238 * This function returns a pointer to the beginning of the 8th note beam.
2239 *
2240 * As an example, consider this input:
2241 * 1: 8.c; 64f beam with staff below; 32.s; 16e; 8s; 8e; 16s; 32.f; 64s ebm;
2242 * 2: 8.e; 64s beam with staff above; 32.a; 16s; 8g; 8s; 16g; 32.s; 64a ebm;
2243 * The resulting mesh will look like this:
2244 * . . . .
2245 * . . . .
2246 * (64th) X . . .
2247 * | . . .
2248 * (32nd) X --> X . . .
2249 * | | . . .
2250 * (16th) X --> X --> X . X --> X (32nd)
2251 * | | | . | |
2252 * return_value --> X --> X --> X --> X --> X --> X --> X --> X (8th)
2253 * . . | | |
2254 * . . X --> X --> X (16th)
2255 * . . . |
2256 * . . . X (64th)
2257 * . . . .
2258 * . . . .
2259 *
2260 * Each X in the diagram represents a CSBINFO struct.
2261 * Each row represents a beam. The --> is the "next" field.
2262 * Each column represents a stem. It is a doubly-linked list,
2263 * using above_p and below_p fields.
2264 * The dots show the stem direction.
2265 */
2266
2267static struct CSBINFO *
2268mkcsbmesh(begin_p, end_p)
2269
2270struct GRPSYL *begin_p; /* first group of cross-staff beam on upper staff */
2271struct GRPSYL *end_p; /* 8th note beam goes from begin_p to end_p.
2272 * There may be zero or more additional beams
2273 * for shorter durations that span part or all
2274 * of this list.
2275 */
2276
2277{
2278 struct CSBINFO *csbi_list_p; /* this points to the 8th note beam
2279 * list, which is what will
2280 * ultimately be returned */
2281 struct CSBINFO *csbi_p; /* the current information */
2282 struct CSBINFO *csbi8_p; /* to walk through 8th list */
2283 struct CSBINFO *prevcsbi_p; /* previous in horizontal list */
2284 struct CSBINFO *c_p; /* for walking vertical lists */
2285 struct GRPSYL *gs_p; /* to walk through beamed groups */
2286 int basictime; /* 8, 16, 32, etc */
2287 int stemdir; /* stem direction where beam starts */
2288 int shortest; /* shortest basictime (8, 16, ...) */
2289
2290
2291 /* There is always at least an 8th note beam that goes the
2292 * entire length, so make a list for that. */
2293 csbi_list_p = prevcsbi_p = 0;
2294 shortest = 8;
2295 for (gs_p = begin_p; gs_p != end_p->next; gs_p = nxtbmgrp(gs_p,
2296 begin_p, end_p->next)) {
2297 MALLOC(CSBINFO, csbi_p, 1);
2298
2299 /* set horizontal list links */
2300 if (csbi_list_p == 0) {
2301 /* first item on the horizontal list */
2302 csbi_list_p = csbi_p;
2303 }
2304 else {
2305 /* link from previous horizontally */
2306 prevcsbi_p->next = csbi_p;
2307 }
2308 prevcsbi_p = csbi_p;
2309 csbi_p->next = 0;
2310
2311 /* init vertical list links */
2312 csbi_p->above_p = csbi_p->below_p = 0;
2313
2314 /* this is for the 8th note beam */
2315 csbi_p->basictime = 8;
2316 /* save what group this is for, for later convenience */
2317 csbi_p->gs_p = gs_p;
2318
2319 /* remember the shortest basictime anywhere in the beam */
2320 if (gs_p->basictime > shortest) {
2321 shortest = gs_p->basictime;
2322 }
2323 }
2324
2325 /* For each additional beam, build up a row of structs representing
2326 * that beam, and link it vertically to the row below or above it,
2327 * depending on whether the first group of the beam is on the staff
2328 * above or below the 8th beam.
2329 */
2330 for (basictime = 16; basictime <= shortest; basictime <<= 1) {
2331 stemdir = UNKNOWN; /* Init to keep lint happy;
2332 * this will get set to appropriate
2333 * value before it is actually used. */
2334 prevcsbi_p = 0; /* No run of groups found yet */
2335
2336 /* Walk through list, finding any runs of groups that are
2337 * at least as short in duration as the current basictime
2338 * we are looking for. Note this could be as little as a single
2339 * group in the case of a partial beam.
2340 * We walk through the GRPSYLs and their
2341 * corresponding CSBINFO structs in parallel.
2342 */
2343 for (gs_p = begin_p, csbi8_p = csbi_list_p;
2344 gs_p != end_p->next;
2345 gs_p = nxtbmgrp(gs_p, begin_p, end_p->next),
2346 csbi8_p = csbi8_p->next) {
2347
2348 if (gs_p->basictime >= basictime) {
2349 /* this group is part of a beam of at least
2350 * as short as the basictime of interest. */
2351 MALLOC(CSBINFO, csbi_p, 1);
2352 csbi_p->next = 0;
2353 csbi_p->basictime = basictime;
2354
2355 /* If not first group in this beam,
2356 * link from previous. If is first,
2357 * save its stem direction. That determines
2358 * which side of the 8th beam it goes on. */
2359 if (prevcsbi_p != 0) {
2360 prevcsbi_p->next = csbi_p;
2361 }
2362 else {
2363 stemdir = gs_p->stemdir;
2364 }
2365 /* Prepare to link more on horizonally,
2366 * if beam goes further. */
2367 prevcsbi_p = csbi_p;
2368
2369 /* set vertical links */
2370 if (stemdir == DOWN) {
2371 /* Must be from staff above.
2372 * Find current top, and add
2373 * above there */
2374 for (c_p = csbi8_p; c_p->above_p != 0;
2375 c_p = c_p->above_p) {
2376 ;
2377 }
2378 c_p->above_p = csbi_p;
2379 csbi_p->below_p = c_p;
2380 csbi_p->above_p = 0;
2381 }
2382 else {
2383 /* similar for from staff below */
2384 for (c_p = csbi8_p; c_p->below_p != 0;
2385 c_p = c_p->below_p) {
2386 ;
2387 }
2388 c_p->below_p = csbi_p;
2389 csbi_p->above_p = c_p;
2390 csbi_p->below_p = 0;
2391 }
2392 }
2393 else {
2394 /* If we were doing a beam before,
2395 * it's done now */
2396 prevcsbi_p = 0;
2397 }
2398 }
2399 }
2400 return(csbi_list_p);
2401}
2402\f
2403
2404/* draw beams in a beam group for a particular time value, 8th, 16th, etc */
2405/* this gets called repeatedly, first for 8th, then 16ths, etc, until
2406 * there are no more shorter notes.
2407 * It returns the number of beams drawn (including partials) */
2408
2409
2410static int
2411draw_beams(gs_p, endbeam_p, basictime, grpsize, grpvalue)
2412
2413struct GRPSYL *gs_p; /* start of beam group */
2414struct GRPSYL *endbeam_p; /* end of beam group */
2415int basictime; /* draw beam for this basic time:
2416 * 8, 16, 32, 64, etc */
2417int grpsize; /* GS_NORMAL, GS_SMALL */
2418int grpvalue; /* GV_NORMAL, GV_ZERO */
2419
2420{
2421 int found = 0; /* how many beams found to be drawn */
2422 int ngrps; /* how many groups to beam together */
2423 struct GRPSYL *first_p = 0;/* first group in beam (the one on the
2424 * above staff while doing cross-staff beams) */
2425 struct GRPSYL *begin_p = 0, *end_p; /* the initialization is
2426 * to shut up bogus compiler warning */
2427 struct GRPSYL *other_p; /* other note that must be used to calculate
2428 * slope of partial beam */
2429 float y_offset; /* from end of stem to draw beam */
2430 int side; /* left or right for partial beam */
2431 float x_begin, y_begin, x_other, y_other; /* partial beam
2432 * coordinates */
2433 double halfwidth; /* half width of a beam */
2434 double end_y_offset; /* to deal with cross staff beams */
2435 double slope; /* of partial beam */
2436 double halfstem;
2437 double stemdist; /* distance between stems */
2438 double pbeam_len; /* length of partial beam */
2439
2440
2441 /* get relevant group, accounting for cross-staff beams */
2442 first_p = gs_p;
2443 gs_p = neighboring_note_beam_group(gs_p, first_p, NO);
2444
2445 /* go through the list */
2446 while ( gs_p != endbeam_p->next) {
2447
2448 /* find however many in a row deserve to get another beam */
2449 for (end_p = (struct GRPSYL *) 0, ngrps = 0;
2450 (gs_p != endbeam_p->next);
2451 gs_p = nxtbmgrp(gs_p,
2452 first_p, endbeam_p->next)) {
2453
2454 /* if wrong type (e.g a grace inside of
2455 * a set of normal notes), skip over */
2456 if (gs_p->grpsize != grpsize
2457 || gs_p->grpvalue != grpvalue ) {
2458 continue;
2459 }
2460
2461 /* if not beamed, skip */
2462 if ( gs_p->beamloc == NOITEM) {
2463 pfatal("non-beam inside beam group\n");
2464 }
2465
2466 /* if this one deserves another beam,
2467 * keep track of that. If not, break out */
2468 if (gs_p->basictime >= basictime) {
2469 end_p = gs_p;
2470 found++;
2471 if (ngrps == 0) {
2472 begin_p = gs_p;
2473 }
2474 ngrps++;
2475 if (gs_p->breakbeam == YES && basictime > 8) {
2476 break;
2477 }
2478 }
2479 else {
2480 break;
2481 }
2482 }
2483
2484 /* prepare to do next one */
2485 if (gs_p != endbeam_p->next) {
2486 gs_p = nxtbmgrp(gs_p, first_p, endbeam_p->next);
2487 }
2488
2489 /* if none we looked at deserved a beam, keep looking */
2490 if (end_p == (struct GRPSYL *) 0) {
2491 continue;
2492 }
2493
2494 /* calculate where on stem the beam should start */
2495 y_offset = beam_offset(numbeams(basictime),
2496 begin_p->grpsize, begin_p->stemdir);
2497
2498 if (end_p->grpsize == GS_NORMAL) {
2499 halfwidth = W_WIDE * Staffscale / PPI / 2.0;
2500 halfstem = W_NORMAL * Staffscale / PPI / 2.0;
2501 }
2502 else {
2503 halfwidth = W_WIDE * Staffscale * SM_FACTOR / PPI / 2.0;
2504 halfstem = W_NORMAL * Staffscale * SM_FACTOR / PPI / 2.0;
2505 }
2506
2507 /* check if single group.
2508 * If so, need to do a partial beam, otherwise full beam */
2509 if (ngrps == 1) {
2510 /* rests and spaces don't get beams,
2511 * so don't get partial ones */
2512 if (end_p->grpcont != GC_NOTES) {
2513 continue;
2514 }
2515
2516 side = pbeamside(end_p, first_p);
2517
2518 /* Now that we decided where the
2519 * partial beam goes, we can draw it */
2520
2521 /* in order to figure out the end point of the partial
2522 * beam, we have to calculate the slope of the beam as
2523 * if it were a full beam and derive from that where
2524 * the partial beam will end. */
2525 /* determine whether to use prev or next note, and
2526 * skip any notes of the wrong type! */
2527 if (side == PB_LEFT) {
2528 other_p = prevbmgrp(end_p, first_p);
2529 }
2530 else {
2531 other_p = nxtbmgrp(end_p, first_p, end_p->next);
2532 }
2533
2534 /* the line then goes from the stem (at y_offset) to
2535 * a notehead width east or west of the stem,
2536 * with the y coordinate calculated from the slope
2537 * of what a full length beam would have been, unless
2538 * stems are too close, in which case shorten it
2539 * somewhat. */
2540 x_begin = find_x_stem(end_p);
2541 y_begin = find_y_stem(end_p);
2542 x_other = find_x_stem(other_p);
2543 y_other = find_y_stem(other_p);
2544
2545 /* if cross-staff and the two stems are in opposite
2546 * directions, have to compensate for that */
2547 if (end_p->stemdir == other_p->stemdir) {
2548 /* in same direction */
2549 slope = (y_other - y_begin)
2550 / (x_other - x_begin);
2551 }
2552 else {
2553 double opp_adj;
2554
2555 opp_adj = beam_offset(
2556 numbeams(other_p->basictime),
2557 other_p->grpsize, other_p->stemdir);
2558 slope = (y_other - (y_begin - opp_adj))
2559 / (x_other - x_begin);
2560 }
2561
2562 /* adjust to overlap stem */
2563 x_begin += halfstem * side;
2564
2565 /* determine partial beam length */
2566 /* find distance between stems */
2567 if (x_begin < x_other) {
2568 stemdist = x_other - x_begin;
2569 }
2570 else {
2571 stemdist = x_begin - x_other;
2572 }
2573 /* if wide enough, use note head width, else less */
2574 if (stemdist < 5.0 * Stepsize) {
2575 pbeam_len = 0.4 * stemdist;
2576 }
2577 else {
2578 pbeam_len = widest_head(end_p) * Staffscale;
2579 }
2580
2581 /* draw the partial beam */
2582 do_beam(x_begin, y_begin + y_offset,
2583 x_begin + pbeam_len * side,
2584 y_begin + y_offset + side *
2585 pbeam_len * slope, halfwidth);
2586 }
2587
2588 else {
2589 /* draw a normal beam */
2590
2591 /* For regular beams, can use y_offset directly,
2592 * but with cross-staff beam, stems may be in opposite
2593 * directions, so have to call a function to get
2594 * appropriate offset.
2595 */
2596 if (begin_p->beamto == CS_SAME) {
2597 end_y_offset = y_offset;
2598 }
2599 else {
2600 end_y_offset = end_bm_offset(first_p, end_p,
2601 basictime);
2602 }
2603
2604 /* If the stems on both ends of the beam
2605 * are zero length, don't draw any beams.
2606 * If user really wants the beams,
2607 * they can make one of the ends
2608 * barely longer than zero.
2609 */
2610 if (begin_p->stemlen <= 0.0 && end_p->stemlen <= 0.0) {
2611 continue;
2612 }
2613
2614 /* find end of first stem and last stem and draw
2615 * the beam at proper offset from there */
2616 do_beam(find_x_stem(begin_p) - halfstem,
2617 find_y_stem(begin_p) + y_offset,
2618 find_x_stem(end_p) + halfstem,
2619 find_y_stem(end_p) + end_y_offset,
2620 halfwidth);
2621 }
2622 }
2623 return(found);
2624}
2625\f
2626
2627/* Figure out how far from the end of a stem a beam should be in the
2628 * case of a cross-staff beam with opposite-direction stems at its ends.
2629 * Will return some multiple (possibly 0) of the distance between beams,
2630 * with the proper sign to account for stem direction.
2631 * Should only be called if the beam in question is a cross-staff beam.
2632 */
2633
2634double
2635end_bm_offset(top_first_p, end_p, basictime)
2636
2637struct GRPSYL *top_first_p; /* the group that has "bm with staff below" */
2638struct GRPSYL *end_p; /* the group where a beam ends. This could be
2639 * either a group with ebm or some intermediate
2640 * group that happens to end a beam segment
2641 * that is shorter. */
2642int basictime; /* the basictime of the beam currently under
2643 * consideration. The first beam drawn will
2644 * be 8, the next 16, then 32, etc. */
2645
2646{
2647 static struct CSBINFO *csbi_list_p = 0;/* Info about the cross beams */
2648 static struct GRPSYL *cached_gs_p = 0; /* Each time we get a different
2649 * top_first_p, we calculate its csbi_list
2650 * and cache it for future calls. This lets
2651 * us know if we can re-use the cached value. */
2652 struct CSBINFO *csbi_p; /* for walking 8th note beam ->next links */
2653 struct CSBINFO *c_p; /* for walking vertical links of mesh */
2654 int nbeams; /* how many beams from the stem end */
2655
2656 if (cached_gs_p != top_first_p) {
2657 /* Cached one is no good; need to recalculate */
2658 if (csbi_list_p != 0) {
2659 /* We had a list before; need to clean it up */
2660 struct CSBINFO *nextvert_p; /* to free vert list */
2661 struct CSBINFO *nexthor_p; /* to free hor list */
2662 /* walk horizontal list */
2663 for (csbi_p = csbi_list_p; csbi_p != 0;
2664 csbi_p = nexthor_p) {
2665 /* clean up vert list, both directions */
2666 for (c_p = csbi_p->above_p; c_p != 0;
2667 c_p = nextvert_p) {
2668 nextvert_p = c_p->above_p;
2669 FREE(c_p);
2670 }
2671 for (c_p = csbi_p->below_p; c_p != 0;
2672 c_p = nextvert_p) {
2673 nextvert_p = c_p->below_p;
2674 FREE(c_p);
2675 }
2676 nexthor_p = csbi_p->next;
2677 FREE(csbi_p);
2678 }
2679 }
2680 /* Calculate everything for current beam */
2681 csbi_list_p = mkcsbmesh(top_first_p, end_p);
2682 cached_gs_p = top_first_p;
2683 }
2684
2685 /* First follow the 8th note CSBINFO list across till we find
2686 * the one matching the end group. */
2687 for (csbi_p = csbi_list_p; csbi_p != 0 && csbi_p->gs_p != end_p;
2688 csbi_p = csbi_p->next) {
2689 ;
2690 }
2691 if (csbi_p == 0) {
2692 pfatal("couldn't find beam end group in end_bm_offset()");
2693 }
2694
2695 /* Now follow the vertical links until we find the right basic time.
2696 * It could be on either side of the 8th beam.
2697 * First we find the end of the stem, then count the number of
2698 * links we have to follow to get to the one with the right basictime.
2699 */
2700 if (end_p->stemdir == DOWN) {
2701 /* Must be from staff above, so end of stem is all the way * down the below_p list.
2702 */
2703 for (c_p = csbi_p; c_p->below_p != 0; c_p = c_p->below_p) {
2704 ;
2705 }
2706 /* Now count the number of beams till the one we want */
2707 for (nbeams = 1; c_p->basictime != basictime;
2708 c_p = c_p->above_p) {
2709 nbeams++;
2710 }
2711 if (c_p == 0) {
2712 pfatal("failed to find cross staff beam info go up");
2713 }
2714 }
2715 else {
2716 /* similar for staff below groups */
2717 for (c_p = csbi_p; c_p->above_p != 0; c_p = c_p->above_p) {
2718 ;
2719 }
2720 /* Now count the number of beams till the one we want */
2721 for (nbeams = 1; c_p->basictime != basictime;
2722 c_p = c_p->below_p) {
2723 nbeams++;
2724 }
2725 if (c_p == 0) {
2726 pfatal("failed to find cross staff beam info go up");
2727 }
2728 }
2729 return (beam_offset(nbeams, end_p->grpsize, end_p->stemdir));
2730}
2731\f
2732
2733/* find y offset on stem based on number of beams, whether normal or small
2734 * notes, and stem direction */
2735
2736static double
2737beam_offset(nbeams, gsize, stemdir)
2738
2739int nbeams; /* how many beams */
2740int gsize; /* GS_NORMAL or GS_SMALL */
2741int stemdir; /* UP or DOWN */
2742
2743{
2744 /* for consistency, it would be nice to use FLAGSEP and SMFLAGSEP
2745 * for beam separation too, but when we tried that, beams looked too
2746 * close together, especially on certain low-resolution devices,
2747 * so that's why we're using 5 and 4 stepsizes. */
2748 return ( (nbeams - 1) * (gsize == GS_NORMAL ? 5.0 : 4.0)
2749 * Staffscale
2750 * (stemdir == UP ? -POINT : POINT) );
2751}
2752\f
2753
2754/* Given a group inside a beam, return the next group. Usually this will
2755 * be gs_p->next, but in the case of a cross-staff beam, it might be a
2756 * group on the other staff */
2757
2758struct GRPSYL *
2759nxtbmgrp(gs_p, first_p, endnext_p)
2760
2761struct GRPSYL *gs_p; /* find the beam group after this one */
2762struct GRPSYL *first_p; /* The first group in the top staff of the
2763 * beam */
2764struct GRPSYL *endnext_p; /* what to return upon reaching the end of
2765 * the beam. This will be the ->next field of
2766 * the last group in the beam on the top staff
2767 * of a cross-staff beam. Returning this lets
2768 * legacy code (code before we supported
2769 * cross-staff beams) keep working with minimal
2770 * changes. */
2771
2772{
2773 int grpsize, grpvalue;
2774
2775 /* If we are passed the first group, it could be a space,
2776 * in which case we need to use the below staff's group instead.
2777 */
2778 if (gs_p->grpcont == GC_SPACE && gs_p->beamto != CS_SAME) {
2779 /* Need to hop to below staff. Go down the chord to find
2780 * the matching cross-staff beam group. */
2781 do {
2782 if ((gs_p = gs_p->gs_p) == (struct GRPSYL *) 0) {
2783 pfatal("can't find matching beam chord");
2784 }
2785
2786 /* skip any lyrics and such till we find the beamed-to group */
2787 } while (gs_p->beamto != CS_ABOVE);
2788 }
2789
2790 /* need to skip past any groups of the wrong kind */
2791 grpsize = first_p->grpsize;
2792 grpvalue = first_p->grpvalue;
2793 do {
2794 /* Move to next group. If that gets us to the end
2795 * of the measure, report that we're done. */
2796 if ((gs_p = gs_p->next) == (struct GRPSYL *) 0) {
2797 return(endnext_p);
2798 }
2799 } while (gs_p->grpsize != grpsize || gs_p->grpvalue != grpvalue);
2800
2801 /* if past end of beam group, report that we're done */
2802 if (gs_p->beamloc != INITEM && gs_p->beamloc != ENDITEM) {
2803 return(endnext_p);
2804 }
2805
2806 return(neighboring_note_beam_group(gs_p, first_p, NO));
2807}
2808\f
2809
2810/* Given a group inside a beam (not the first),
2811 * return the previous group. Usually this will
2812 * be gs_p->prev, but in the case of a cross-staff beam, it might be a
2813 * group on the other staff */
2814
2815struct GRPSYL *
2816prevbmgrp(gs_p, first_p)
2817
2818struct GRPSYL *gs_p; /* find the beam group after this one */
2819struct GRPSYL *first_p; /* The first group in the top staff of the
2820 * beam */
2821
2822{
2823 int grpsize, grpvalue;
2824 int staffno;
2825
2826 staffno = gs_p->staffno;
2827
2828 /* need to skip past any groups of the wrong kind */
2829 grpsize = first_p->grpsize;
2830 grpvalue = first_p->grpvalue;
2831 do {
2832 /* Move to prev group. */
2833 if ((gs_p = gs_p->prev) == (struct GRPSYL *) 0) {
2834 pfatal("prevbmgrp couldn't find prev group");
2835 }
2836 } while (gs_p->grpsize != grpsize || gs_p->grpvalue != grpvalue);
2837
2838 gs_p = neighboring_note_beam_group(gs_p, first_p, YES);
2839
2840 /* if we hopped staffs, then the space on the original staff might
2841 * have been a long note, in which case the group we have isn't
2842 * really the one we want. So we have to go forward on this new staff
2843 * until we find the space that corresponds to the groups we started
2844 * with, then back up one group from there. That's the one we want */
2845 if (staffno != gs_p->staffno) {
2846 /* we hopped staffs. Go forward to the next space */
2847 for (gs_p = gs_p->next; gs_p->grpcont != GC_SPACE;
2848 gs_p = gs_p->next) {
2849 ;
2850 }
2851 /* now take the group right before the space */
2852 gs_p = gs_p->prev;
2853 }
2854 return(gs_p);
2855}
2856\f
2857
2858/* Given a group in a beam, skip over any embedded rests.
2859 * Then if the group is not a space, return it as it is.
2860 * If it is a space, return the corresponding group on the staff
2861 * that this group is beamed to */
2862
2863static struct GRPSYL *
2864neighboring_note_beam_group(gs_p, first_p, backwards)
2865
2866struct GRPSYL *gs_p; /* find the beam group neighboring this one */
2867struct GRPSYL *first_p; /* The first group in the top staff of the
2868 * beam */
2869int backwards; /* if YES, go backwards (find the previous
2870 * group rather than the following) */
2871
2872{
2873 struct GRPSYL *tgs_p; /* as we walk down a chord to try to find
2874 * the group we're looking for, this keeps
2875 * track of where we are */
2876
2877
2878 /* skip over any embedded rests--they are not notes. */
2879 while (gs_p->grpcont == GC_REST) {
2880 if (backwards == YES) {
2881 gs_p = gs_p->prev;
2882 }
2883 else {
2884 gs_p = gs_p->next;
2885 }
2886 }
2887 if (gs_p == 0) {
2888 pfatal("neighboring_note_beam_group didn't find note group");
2889 }
2890
2891 /* If this is a cross-staff beam, we may need to hop from
2892 * staff to staff sometimes. If this group is a space
2893 * group, then we have to hop now. */
2894 if (gs_p->grpcont == GC_SPACE) {
2895 if (gs_p->beamto == CS_SAME) {
2896 do {
2897 if (backwards == YES) {
2898 gs_p = gs_p->prev;
2899 } else {
2900 gs_p = gs_p->next;
2901 }
2902 } while (gs_p != 0 && gs_p->grpcont != GC_NOTES);
2903 }
2904
2905 else if (gs_p->staffno == first_p->staffno) {
2906 /* Need to hop to below staff.
2907 * Go down the chord to find
2908 * the matching cross-staff beam group */
2909 do {
2910 if ((gs_p = gs_p->gs_p) ==
2911 (struct GRPSYL *) 0) {
2912 pfatal("can't find matching beam chord");
2913 }
2914
2915 /* skip any lyrics and such till we find the
2916 * group beamed to us */
2917 } while (gs_p->beamto != CS_ABOVE);
2918 }
2919 else {
2920 /* Need to jump back to staff above.
2921 * Since the chord linked list is only one way (down)
2922 * and we need to look up the chord, this is a
2923 * little harder. Start at the first_p group, which
2924 * is the first group in the beam on the above staff.
2925 * Keep going down that staff until we find a chord
2926 * linked down to gs_p. */
2927 for ( ; first_p != (struct GRPSYL *) 0;
2928 first_p = first_p->next) {
2929
2930 /* walk down the chord */
2931 for (tgs_p = first_p->gs_p;
2932 tgs_p != (struct GRPSYL *) 0;
2933 tgs_p = tgs_p->gs_p) {
2934
2935 if (tgs_p == gs_p) {
2936 /* Aha! We found it! */
2937 return(first_p);
2938 }
2939
2940 if (tgs_p->staffno > gs_p->staffno) {
2941 /* we're past the staff we care
2942 * about, so this chord can't
2943 * be the right one. */
2944 break;
2945 }
2946 }
2947 }
2948
2949 pfatal("failed to find group when jumping back to above staff");
2950 }
2951 }
2952
2953 return(gs_p);
2954}
2955\f
2956
2957/* given a GRPSYL that deserves a partial beam, return PB_LEFT if the beam
2958 * goes on the left or PB_RIGHT if is goes on the right. */
2959
2960int
2961pbeamside(gs_p, first_p)
2962
2963struct GRPSYL *gs_p;
2964struct GRPSYL *first_p;
2965
2966{
2967 int side;
2968 int beams2left, beams2right; /* how many beams or dots for notes on
2969 * either side of current group */
2970 struct GRPSYL *prevgs_p, *nextgs_p;
2971
2972
2973 /* need to figure out which side of stem to draw the
2974 * partial beam. First the easy cases: if is STARTITEM,
2975 * then it has to go on the right, if ENDITEM, it
2976 * has to go on the left */
2977 switch (gs_p->beamloc) {
2978 case STARTITEM:
2979 side = PB_RIGHT;
2980 break;
2981
2982 case ENDITEM:
2983 side = PB_LEFT;
2984 break;
2985
2986 case INITEM:
2987 /* Hmmm. Will have to be more clever. Check the
2988 * note on either side. If we're at a breakbeam,
2989 * it's easy to know. Otherwise, if one should have more
2990 * beams than the other, put the partial on that
2991 * side */
2992 prevgs_p = prevbmgrp(gs_p, first_p);
2993 nextgs_p = nxtbmgrp(gs_p, first_p, gs_p->next);
2994 beams2left = numbeams(prevgs_p->basictime);
2995 beams2right = numbeams(nextgs_p->basictime);
2996 if (gs_p->breakbeam == YES) {
2997 side = PB_LEFT;
2998 }
2999 else if (prevgs_p != 0 && prevgs_p->breakbeam == YES) {
3000 side = PB_RIGHT;
3001 }
3002 else if (beams2left > beams2right) {
3003 side = PB_LEFT;
3004 }
3005 else if (beams2right > beams2left) {
3006 side = PB_RIGHT;
3007 }
3008
3009 /* That was inconclusive. So now we're going to try to decide
3010 * based on logical groupings of notes; that is, notes grouped
3011 * according to what the accents should be. */
3012 else if (chkgroupings(&side, gs_p) == YES) {
3013 /* it found an answer and set "side" for us */
3014 ;
3015 }
3016 else {
3017 /* ok. that didn't help.
3018 * See if the notes on either side
3019 * have more dots than the other.
3020 * If so, put the partial towards
3021 * that one. If they are the same, then
3022 * throw in the towel and just stick it
3023 * on the left */
3024 beams2left = prevgs_p->dots;
3025 beams2right = nextgs_p->dots;
3026 if (beams2right > beams2left) {
3027 side = PB_RIGHT;
3028 }
3029 else {
3030 side = PB_LEFT;
3031 }
3032 }
3033 break;
3034
3035 default:
3036 pfatal("invalid beamloc passed to pbeamside");
3037 /*NOTREACHED*/
3038 return(PB_LEFT); /* to shut up bogus compiler warning */
3039 }
3040
3041 return(side);
3042}
3043\f
3044/*
3045 * Name: chkgroupings()
3046 *
3047 * Abstract: Decide partial beam side based on groupings of notes.
3048 *
3049 * Returns: YES if it found an answer (stored in *side_p), NO if not
3050 *
3051 * Description: This function breaks the measure down into successively
3052 * smaller pieces based on where the accents should be, trying to
3053 * find a piece where the current GRPSYL falls at the beginning or
3054 * end of the piece. If the GRPSYL falls at the start of a piece,
3055 * its partial beam should point right; if end, left. If we get
3056 * to the point where the pieces are shorter than the GRPSYL
3057 * itself, we have failed.
3058 */
3059
3060static int
3061chkgroupings(side_p, thisgs_p)
3062
3063int *side_p; /* where to put the answer, if found */
3064struct GRPSYL *thisgs_p; /* the GRPSYL we are working on */
3065
3066{
3067 struct GRPSYL *gs_p; /* point along GRPSYL list */
3068 short *factors; /* array to be malloc'ed */
3069 int n; /* loop variable */
3070 RATIONAL thisstart; /* time offset in measure of thisgs_p */
3071 RATIONAL nextstart; /* time offset in measure of next GRPSYL */
3072 RATIONAL quotient; /* temp variable for dividing */
3073 RATIONAL grouplen; /* time length of a grouping */
3074 RATIONAL tupstart; /* time offset where tuplet starts */
3075 RATIONAL tupdur; /* time length of a tuplet */
3076 int counts; /* count in the current grouplen */
3077 int fraction; /* is grouplen a fraction of a count? */
3078 int fact; /* a factor */
3079
3080
3081 /*
3082 * If we're doing grace beams, skip this whole thing, since we're
3083 * dealing with time values, and they are all zero.
3084 */
3085 if (thisgs_p->grpvalue == GV_ZERO) {
3086 return (NO);
3087 }
3088
3089 /* find the time offset of thisgs_p by adding up all previous GRPSYLs*/
3090 thisstart = Zero;
3091 for (gs_p = thisgs_p->prev; gs_p != 0; gs_p = gs_p->prev) {
3092 thisstart = radd(thisstart, gs_p->fulltime);
3093 }
3094
3095 /* find offset of GRPSYL following thisgs_p */
3096 nextstart = radd(thisstart, thisgs_p->fulltime);
3097
3098 /*
3099 * Interior notes of tuplets are dealt with in a special way.
3100 */
3101 if (thisgs_p->tuploc == INITEM) {
3102 /*
3103 * Find the duration of the tuplet by adding up all the
3104 * previous GRPSYLs in the tuplet and this GRPSYL and all the
3105 * later GRPSYLs. (The loops stop when they hit a NOITEM
3106 * that's not grace.)
3107 */
3108 tupdur = Zero;
3109 for (gs_p = thisgs_p->prev; gs_p != 0 &&
3110 (gs_p->grpvalue == GV_ZERO ||
3111 gs_p->tuploc != NOITEM); gs_p = gs_p->prev) {
3112 tupdur = radd(tupdur, gs_p->fulltime);
3113 }
3114 /* remember where tuplet starts */
3115 tupstart = rsub(thisstart, tupdur);
3116 for (gs_p = thisgs_p; gs_p != 0 &&
3117 (gs_p->grpvalue == GV_ZERO ||
3118 gs_p->tuploc != NOITEM); gs_p = gs_p->next) {
3119 tupdur = radd(tupdur, gs_p->fulltime);
3120 }
3121
3122 /*
3123 * If the starting point of this tuplet is not at a multiple of
3124 * its duration, we consider the tuplet synchopated. This is
3125 * pretty bizarre and not worth trying to deal with.
3126 */
3127 quotient = rdiv(tupstart, tupdur);
3128 if (quotient.d != 1) {
3129 return (NO);
3130 }
3131
3132 /* the first group length to consider is tupdur/tupcont */
3133 grouplen = tupdur;
3134 grouplen.d *= thisgs_p->tupcont;
3135 rred(&grouplen);
3136
3137 /* loop until an answer is found, or we give up */
3138 for (;;) {
3139 /*
3140 * If the group length is not longer than our note, it
3141 * makes no sense to try to see if our note is at the
3142 * start or end of such a group. Maybe we never hit a
3143 * match because our note is syncopated. Whatever the
3144 * reason, we have to give up.
3145 */
3146 if (LE(grouplen, thisgs_p->fulltime)) {
3147 return (NO);
3148 }
3149
3150 /*
3151 * If thisstart/grouplen is an integer, it means
3152 * thisgs_p is on a grouping boundary; that is, it is
3153 * the first GRPSYL in a grouping. So point right.
3154 */
3155 quotient = rdiv(thisstart, grouplen);
3156 if (quotient.d == 1) {
3157 *side_p = PB_RIGHT;
3158 return (YES);
3159 }
3160
3161 /*
3162 * If nextstart/grouplen is an integer, it means the
3163 * GRPSYL after thisgs_p is on a grouping boundary,
3164 * which means that thisgs_p is the last GRPSYL in a
3165 * grouping. So point left.
3166 */
3167 quotient = rdiv(nextstart, grouplen);
3168 if (quotient.d == 1) {
3169 *side_p = PB_LEFT;
3170 return (YES);
3171 }
3172
3173 /* divide grouplen by 2 and try again */
3174 grouplen = rdiv(grouplen, Two);
3175 }
3176 }
3177
3178 /*
3179 * This is the normal case, not the interior of a tuplet.
3180 */
3181
3182 /* get all the prime factors of the time sig's numerator */
3183 factors = factor(Score.timenum);
3184
3185 grouplen = Score.time; /* first group is the whole measure */
3186 counts = Score.timenum; /* number of counts in measure */
3187
3188 /*
3189 * Loop until we find an answer, or until we have to give up. Each
3190 * time through the loop, we reduce the grouping length. At first, we
3191 * divide out prime factors from the number of counts in the measure.
3192 * Once we get down to one count, we start dividing by 2 all the time.
3193 */
3194 for (;;) {
3195 fraction = YES; /* default to "fraction of a count" */
3196
3197 /* if there are still multiple counts, divide out a prime */
3198 if (counts > 1) {
3199 /*
3200 * See if there are any prime factors greater than 4.
3201 * This only happens with funny timesigs like 10/8 or
3202 * 7/4. We work down from the top, because the
3203 * likelyhood is that the highest level grouping is by
3204 * the biggest factor, when these funny numbers are
3205 * involved. At least 10/8, for example, is normally
3206 * 5 groups of 2, not 2 groups of 5.
3207 */
3208 for (n = Score.timenum; n > 4 && factors[n] == 0; n--)
3209 ;
3210 /* if we found a 5 or greater, use it */
3211 if (n > 4) {
3212 factors[n]--;
3213 fact = n;
3214 /*
3215 * There are no funny factors (5 or more) left. Next,
3216 * we need to look for 2s, not 3s yet, because, for
3217 * example, 6/8 is 2 groups of 3, not 3 groups of 2.
3218 */
3219 } else if (counts % 2 == 0) {
3220 factors[2]--;
3221 fact = 2;
3222 /* no 2s either, so look for 3s */
3223 } else if (counts % 3 == 0) {
3224 factors[3]--;
3225 fact = 3;
3226 } else {
3227 /* no factors left, so flag it by setting fact to 1 */
3228 fact = 1;
3229 }
3230
3231 /*
3232 * If a factor was found, divide it out, and remember
3233 * that we are not yet dealing with fractions of a
3234 * single count.
3235 */
3236 if (fact > 1) {
3237 counts /= fact;
3238 fraction = NO;
3239 }
3240 }
3241
3242 if (fraction == YES) {
3243 /*
3244 * We are dealing with fractions of a count, so divide
3245 * by 2 from now on.
3246 */
3247 grouplen = rdiv(grouplen, Two);
3248 } else {
3249 /*
3250 * Using the number of counts remaining, form the
3251 * length in lowest terms.
3252 */
3253 grouplen.n = counts;
3254 grouplen.d = Score.timeden;
3255 rred(&grouplen);
3256 }
3257
3258 /*
3259 * If the group length is not longer than our note, it makes no
3260 * sense to try to see if our note is at the start or end of
3261 * such a group. Maybe we never hit a match because our note
3262 * is syncopated. Whatever the reason, we have to give up.
3263 */
3264 if (LE(grouplen, thisgs_p->fulltime)) {
3265 return (NO);
3266 }
3267
3268 /*
3269 * If thisstart/grouplen is an integer, it means thisgs_p is on
3270 * a grouping boundary; that is, it is the first GRPSYL in a
3271 * grouping. So point right.
3272 */
3273 quotient = rdiv(thisstart, grouplen);
3274 if (quotient.d == 1) {
3275 *side_p = PB_RIGHT;
3276 return (YES);
3277 }
3278
3279 /*
3280 * If nextstart/grouplen is an integer, it means the GRPSYL
3281 * after thisgs_p is on a grouping boundary, which means that
3282 * thisgs_p is the last GRPSYL in a grouping. So point left.
3283 */
3284 quotient = rdiv(nextstart, grouplen);
3285 if (quotient.d == 1) {
3286 *side_p = PB_LEFT;
3287 return (YES);
3288 }
3289 }
3290
3291 return (NO); /* we can never get here; this is for lint */
3292}
3293\f
3294
3295/* actually draw a beam */
3296
3297static void
3298do_beam(x1, y1, x2, y2, halfwidth)
3299
3300double x1, y1; /* start beam here */
3301double x2, y2; /* end beam here */
3302double halfwidth; /* go this far up and down from y1 and y2 to get
3303 * corners of parallelogram that makes up the beam */
3304
3305{
3306 do_newpath();
3307 do_moveto(x1, y1 + halfwidth);
3308 do_line(x2, y2 + halfwidth);
3309 do_line(x2, y2 - halfwidth);
3310 do_line(x1, y1 - halfwidth);
3311 do_closepath();
3312 do_fill();
3313}
3314\f
3315
3316/* print a multirest */
3317
3318void
3319pr_multirest(gs_p, staff_p)
3320
3321struct GRPSYL *gs_p; /* info about the multirest */
3322struct STAFF *staff_p;
3323
3324{
3325 double x; /* horizontal position of number string */
3326 double y, y_offset; /* vertical location */
3327 double height, width; /* of meas num string */
3328 float east, west; /* edges of the multirest */
3329 char *numstr; /* ASCII version of numbers of measures */
3330
3331
3332 /* avoid core dumps */
3333 if (Score_location_p == (float *) 0) {
3334 pfatal("can't do multirest: no feed");
3335 return;
3336 }
3337
3338 /* determine where to place the multirest */
3339 y = mr_y_loc(gs_p->staffno);
3340 east = gs_p->c[AE];
3341 west = gs_p->c[AW];
3342
3343 /* If user wants us to use the alternative multirest style of using
3344 * rest symbols (often used in orchestral music), and the length of
3345 * the multirest is 8 or less, we use that alternate style,
3346 * otherwise draw horizontal line along middle staff and two
3347 * vertical lines near the bar lines. Note that the basictime is
3348 * the negative of the number of measures of multirest.
3349 * We have seen rare examples of using the alternate style for all the
3350 * way up to 11 measures, but consider normal usage only up to 8.
3351 */
3352 if (svpath(staff_p->staffno, RESTSYMMULT)->restsymmult == YES &&
3353 gs_p->basictime > -9) {
3354 double center; /* can't use AX, must use avg of AE and AW */
3355 int size; /* actually will always be normal size,
3356 * but may as well make code be able to handle
3357 * small size just in case... */
3358
3359 center = (gs_p->c[AE] + gs_p->c[AW]) / 2.0;
3360 size = (gs_p->grpsize == GS_NORMAL ? DFLT_SIZE : SMALLSIZE);
3361
3362 switch (gs_p->basictime) {
3363 case -2:
3364 pr_muschar(center, gs_p->c[AY], C_DWHREST, size, FONT_MUSIC);
3365 break;
3366 case -3:
3367 pr_muschar(gs_p->c[AW], gs_p->c[AY], C_DWHREST, size, FONT_MUSIC);
3368 pr_muschar(gs_p->c[AE], gs_p->c[AY], C_1REST, size, FONT_MUSIC);
3369 break;
3370 case -4:
3371 pr_muschar(center, gs_p->c[AY], C_QWHREST, size, FONT_MUSIC);
3372 break;
3373 case -5:
3374 pr_muschar(gs_p->c[AW], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC);
3375 pr_muschar(gs_p->c[AE], gs_p->c[AY], C_1REST, size, FONT_MUSIC);
3376 break;
3377 case -6:
3378 pr_muschar(gs_p->c[AW], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC);
3379 pr_muschar(gs_p->c[AE], gs_p->c[AY], C_DWHREST, size, FONT_MUSIC);
3380 break;
3381 case -7:
3382 pr_muschar(gs_p->c[AW], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC);
3383 pr_muschar(center, gs_p->c[AY], C_DWHREST, size, FONT_MUSIC);
3384 pr_muschar(gs_p->c[AE], gs_p->c[AY], C_1REST, size, FONT_MUSIC);
3385 break;
3386 case -8:
3387 pr_muschar(gs_p->c[AW], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC);
3388 pr_muschar(gs_p->c[AE], gs_p->c[AY], C_QWHREST, size, FONT_MUSIC);
3389 break;
3390 default:
3391 pfatal("restsymmult with illegal number of measures (%d)",
3392 -(gs_p->basictime) );
3393 break;
3394 }
3395 }
3396 else {
3397 /* draw vertical lines at each end */
3398 do_linetype(L_MEDIUM);
3399
3400 draw_line(west, y - (2.0 * Stepsize), west, y + (2.0 * Stepsize));
3401 draw_line(east, y - (2.0 * Stepsize), east, y + (2.0 * Stepsize));
3402
3403 /* draw heavy horizontal */
3404 do_linetype(L_WIDE);
3405 draw_line(west, y, east, y);
3406 }
3407
3408 if (svpath(staff_p->staffno, PRINTMULTNUM)->printmultnum == YES) {
3409 /* print number of measures */
3410 numstr = mrnum(staff_p, &x, &y_offset, &height, &width);
3411 pr_string(x, y + y_offset, numstr, J_LEFT, (char *) 0, -1);
3412 }
3413}
3414\f
3415
3416/* Given a STAFF pointing to a multirest or measure repeat GRPSYL,
3417 * return a string for its number of measures,
3418 * and return via pointers its x, relative y, height, and width */
3419
3420char *
3421mrnum(staff_p, x_p, y_offset_p, height_p, width_p)
3422
3423struct STAFF *staff_p;
3424double *x_p; /* return where number starts horizontally */
3425double *y_offset_p; /* return y relative to staff */
3426double *height_p; /* return height of string */
3427double *width_p; /* return width of string */
3428
3429{
3430 struct GRPSYL *gs_p = 0;/* initialize to avoid compiler warning */
3431 char *numstr; /* ASCII version of number of measures */
3432 int v; /* voice index */
3433
3434 /* skip over invisible voices */
3435 for (v = 0; v < MAXVOICES; v++) {
3436 if (vvpath(staff_p->staffno, v + 1, VISIBLE)->visible == YES) {
3437 gs_p = staff_p->groups_p[v];
3438 break;
3439 }
3440 }
3441 if (v == MAXVOICES) {
3442 pfatal("no visible voice found by mrnum");
3443 }
3444 if (gs_p->grpcont == GC_NOTES) {
3445 /* this is a measure repeat */
3446 numstr = num2str(staff_p->mrptnum);
3447 numstr[0] = FONT_TR;
3448 numstr[1] = 11;
3449 }
3450 else if (gs_p->grpcont == GC_REST) {
3451 /* this is a multi-rest */
3452 numstr = num2str( -(gs_p->basictime) );
3453 /* want these in bigger size */
3454 /* Essential Dictionary of Music Notation says this
3455 * should be in the same size and font as time signature. */
3456 numstr[0] = FONT_NB;
3457 numstr[1] = 16;
3458 }
3459 else {
3460 pfatal("wrong group type (%d) passed to mrnum, line %d, staff %d, voice %d", gs_p->grpcont, gs_p->inputlineno, gs_p->staffno, gs_p->vno);
3461 /*NOTREACHED*/
3462 return (char *) 0; /* to shut up bogus compiler warning */
3463 }
3464 numstr[1] = (char) adj_size((int) numstr[1], Staffscale, (char *) 0, -1);
3465
3466 *width_p = strwidth(numstr);
3467 *height_p = strheight(numstr);
3468
3469 /* x is middle of measure minus 1/2 of number string width */
3470 /* y offset is just above staff */
3471 *x_p = ((gs_p->c[AE] + gs_p->c[AW]) / 2.0) - (*width_p / 2.0);
3472 *y_offset_p = halfstaffhi(gs_p->staffno) + Stepsize;
3473 return(numstr);
3474}
3475\f
3476
3477/* given a number, return pointer to string version (with font/size in first
3478 * 2 bytes. Points to static area overwritten on each call, so if you need a
3479 * unique copy of it, use copy_string(). Always makes a string in Roman in
3480 * the default size. */
3481
3482char *
3483num2str(num)
3484
3485int num; /* the number to convert */
3486
3487{
3488 static char numstr[12];
3489
3490 /* get ASCII version of number */
3491 (void) sprintf(numstr, "%c%c%d", FONT_TR,
3492 adj_size(DFLT_SIZE, Staffscale, (char *) 0, -1), num);
3493 return(numstr);
3494}
3495\f
3496
3497/* print cresc or decresc */
3498
3499static void
3500pr_cresc(stuff_p)
3501
3502struct STUFF *stuff_p; /* info about what to print and where */
3503
3504{
3505 float x1, x2; /* x coords of west and east points */
3506 float line1y1, line2y1; /* y coords of west points */
3507 float line1y2, line2y2; /* y coords of east points */
3508
3509
3510 do_linetype(L_NORMAL);
3511
3512 /* get coords for point and midpoint of open end */
3513 x1 = stuff_p->c[AW];
3514 x2 = stuff_p->c[AE];
3515 if (stuff_p->stuff_type == ST_CRESC) {
3516 line1y1 = line2y1 = (stuff_p->c[AN] + stuff_p->c[AS]) / 2.0;
3517 /* adjust by 1 point to allow some vertical padding */
3518 line1y2 = stuff_p->c[AN] - (1.0 * Stdpad);
3519 line2y2 = stuff_p->c[AS] + (1.0 * Stdpad);
3520 }
3521 else if (stuff_p->stuff_type == ST_DECRESC) {
3522 line1y2 = line2y2 = (stuff_p->c[AN] + stuff_p->c[AS]) / 2.0;
3523 line1y1 = stuff_p->c[AN] - (1.0 * Stdpad);
3524 line2y1 = stuff_p->c[AS] + (1.0 * Stdpad);
3525 }
3526 else {
3527 pfatal("pr_cres called for something other than cresc/decresc");
3528 /*NOTREACHED*/
3529 return; /* to shut up bogus compiler warning about unused variables */
3530 }
3531
3532 /* draw the two sides of the hairpin */
3533 draw_line(x1, line1y1, x2, line1y2);
3534 draw_line(x1, line2y1, x2, line2y2);
3535}
3536\f
3537
3538/* if a STUFF has a til clause, may need to extend out. If a trill, extend
3539 * with wavy line. If octave, use dashed line. If strings ends with a ~,
3540 * use a wavy line. If ends with an underscore or is figbass, use
3541 * underline. For everything else, put out periodic dashed. */
3542
3543static void
3544extend(stuff_p)
3545
3546struct STUFF *stuff_p; /* a stuff which may have a til clause */
3547
3548{
3549 float extlen; /* length of extension */
3550 float y; /* vertical position */
3551 float x; /* horizontal position */
3552 float segment; /* length of dash + white space */
3553 char *dash; /* dash in proper font/size */
3554 char lch; /* last character of string */
3555
3556
3557 if (stuff_p->end.bars <= 0 && stuff_p->end.count <= 0.0) {
3558 /* no til clause, so nothing to do */
3559 return;
3560 }
3561
3562 /* figure out how much to extend */
3563 extlen = stuff_p->c[AE] - stuff_p->c[AW] - strwidth(stuff_p->string);
3564 y = stuff_p->c[AY];
3565
3566 if (string_is_sym(stuff_p->string, C_TR, FONT_MUSIC) == YES) {
3567 /* special case of a trill */
3568 if ( extlen < Stepsize) {
3569 /* too short to bother */
3570 return;
3571 }
3572
3573 y += (2.0 * Stepsize);
3574 draw_wavy(stuff_p->c[AE] - extlen, y, stuff_p->c[AE], y);
3575 return;
3576 }
3577
3578 else if ((lch = last_char(stuff_p->string)) == '~') {
3579 y += strascent(stuff_p->string) / 2.0;
3580 draw_wavy(stuff_p->c[AE] - extlen, y, stuff_p->c[AE], y);
3581 return;
3582 }
3583
3584 else if (lch == '_' || stuff_p->modifier == TM_FIGBASS) {
3585 do_linetype(L_NORMAL);
3586 draw_line(stuff_p->c[AE] - extlen, y, stuff_p->c[AE], y);
3587 return;
3588 }
3589
3590 else if (stuff_p->stuff_type == ST_OCTAVE) {
3591
3592 if ( extlen < (4.0 * Stepsize)) {
3593 /* too short to bother */
3594 return;
3595 }
3596
3597 y += (1.5 * Stepsize);
3598 do_linetype(L_DASHED);
3599 draw_line(stuff_p->c[AE] - extlen + (2.0 * Stepsize), y,
3600 stuff_p->c[AE], y);
3601
3602 /* vertical line at end unless carried to next score */
3603 if (stuff_p->carryout == NO) {
3604 draw_line(stuff_p->c[AE], y, stuff_p->c[AE],
3605 y + (3.0 * Stepsize *
3606 (stuff_p->place == PL_ABOVE ? -1.0 : 1.0)));
3607 }
3608
3609 /* put linetype back to solid so some other music character that
3610 * uses a line won't get messed up */
3611 do_linetype(L_NORMAL);
3612 return;
3613 }
3614
3615 dash = dashstr(stuff_p->string);
3616 segment = (3.0 * strwidth(dash));
3617 for ( x = stuff_p->c[AE] - extlen + (2.0 * segment) / 3.0;
3618 x < stuff_p->c[AE]; x += segment) {
3619 pr_string(x, y, dash, J_LEFT, (char *) 0, -1);
3620 }
3621 FREE(dash);
3622}
3623\f
3624
3625/* Some characters have upside-down versions that are used
3626 * if stem is down. This table maps such characters to their flips versions.
3627 */
3628
3629static struct MIRRCHAR {
3630 int font; /* Which music font. Note that both the
3631 * normal and inverted characters must be
3632 * in the same font. (We could relax
3633 * this constraint by storing a font for each,
3634 * and returning both character and font,
3635 * but there's no hardship in this simple way.)
3636 */
3637 char norm;
3638 char inverted;
3639} mirrtbl[] = {
3640 { FONT_MUSIC, C_FERM, C_UFERM },
3641 { FONT_MUSIC, C_ACC_HAT, C_ACC_UHAT },
3642 { FONT_MUSIC, C_WEDGE, C_UWEDGE },
3643 { 0, 0 }
3644};
3645
3646
3647/* Given a string and the first character in it, if it is a music symbol
3648 * that has a mirrored version, return that, otherwise, return
3649 * it as it was.
3650 */
3651
3652static int
3653mirror(str, ch, font)
3654
3655char *str; /* the string to check */
3656int ch; /* the first character (which better be a music character) */
3657int font; /* FONT_MUSIC or some other music font */
3658
3659{
3660 int i;
3661
3662 for (i = 0; mirrtbl[i].norm != '\0'; i++) {
3663 if (string_is_sym(str, mirrtbl[i].norm, mirrtbl[i].font) == YES) {
3664 return((int) mirrtbl[i].inverted);
3665 }
3666 }
3667 return(ch);
3668}