chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / lyrics.c
CommitLineData
69695f33
MW
1
2/* Copyright (c) 1995, 1997, 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006 by Arkkra Enterprises */
3/* All rights reserved */
4
5/* functions called by the parse phase to deal with lyrics */
6
7#include "defines.h"
8#include "structs.h"
9#include "globals.h"
10
11
12/* information about a measure-worth of lyrics */
13struct LYRINFO {
14 short place; /* PL_ABOVE, etc */
15 short vno; /* verse number */
16 struct GRPSYL *gs_p; /* the lyrics themselves */
17 struct LYRINFO *next; /* for linked list of other verses on the
18 * same staff */
19};
20
21/* current font/size for each staff/verse/place combination */
22struct LYRFONTSIZE {
23 short staffno;
24 short verse;
25 char place; /* PL_ABOVE, etc */
26 char font;
27 char size;
28 char all; /* YES if "above all" or "below all".
29 * In that case, the staffno is
30 * irrelevant */
31 struct LYRFONTSIZE *next; /* linked list */
32};
33
34/* we keep the font/size info independently for each staff/verse/place combo
35 * and carry it forward from measure to measure. Info is stored here */
36struct LYRFONTSIZE *Lyrfontsizeinfo_p [MAXSTAFFS + 1];
37
38/* for each staff, keep a list of all the GRPSYL lyric lists that will be
39 * associated with that staff. */
40static struct LYRINFO *Lyr_tbl[MAXSTAFFS + 1];
41
42/* need to keep a list of all verse numbers ever used, for setting Maxverses */
43static struct RANGELIST *Above_vnumbers_used;
44static struct RANGELIST *Below_vnumbers_used;
45static struct RANGELIST *Between_vnumbers_used;
46
47/* for auto-numbering verses, keep track of the last verse number used
48 * for each staff/place */
49static short Prev_verse_num[MAXSTAFFS+1][NUM_PLACE];
50
51/* static function declarations */
52static void free_lyrinfo P((struct LYRINFO *lyrinfo_p));
53static void assoc_lyr2staff P((int staffno, int verse, struct GRPSYL *gs_p,
54 char *lyrstring, int all, int place));
55static struct LYRFONTSIZE *getlyrfontsize P((int staffno, int verse,
56 int place, int all));
57static void updlyrfontsize P((struct LYRFONTSIZE *lyrfontsize_p,
58 int font, int size));
59static char *next_syl P((char **sylstring_p, int *font_p, int *size_p,
60 short *position_p, int staffno));
61static void do_sylwidth P((char *lyrstring, float *wid_b4_syl_p,
62 float *wid_real_syl_p, float *wid_after_syl_p,
63 int compensating));
64static void count_verses P((struct RANGELIST *used_p));
65static void vno_used P((int vno));
66\f
67
68/* given a range of verses, save the range for later use */
69
70void
71lyr_verse(begin, end)
72
73int begin; /* first verse in range */
74int end; /* last verse number in range */
75
76{
77 /* error check */
78 /* Note that -1 is a special value meaning "one more than previous."
79 * Zero is also special, meaning "centered," but that doesn't
80 * get handled by this function, since that doesn't need the error
81 * checks that are done here, since the parser guarantees good values.
82 * If a zero does get passed in, the user must have explicitly
83 * specified it, which is a user error.
84 */
85 if (begin < 1 && begin != -1) {
86 yyerror("verse number must be >= 1");
87 return;
88 }
89
90 if (end < begin) {
91 yyerror("end of verse range smaller than beginning");
92 return;
93 }
94
95 save_vno_range(begin, end);
96}
97\f
98
99/* once all the information about a measure-worth of lyrics has been
100 * gathered, process it. Break up the lyrics string into syllables and
101 * store them in the appropriate GRPSYL structs. Then clone the list as
102 * many times as necessary for each staff/verse, and associate with
103 * appropriate staffs.
104 */
105
106void
107proc_lyrics(grpsyl_p, lyrstring)
108
109struct GRPSYL *grpsyl_p; /* list of GRPSYLs with time values */
110char *lyrstring; /* string containing the lyrics */
111
112{
113 struct RANGELIST *slist_p; /* walk through list of staffs */
114 struct RANGELIST *vlist_p; /* walk through list of verses */
115 int staffno;
116 int verse;
117 int place;
118
119
120 if (grpsyl_p == (struct GRPSYL *) 0 || lyrstring == (char *) 0) {
121 return;
122 }
123
124 debug(2, "proc_lyrics file=%s lineno=%d", grpsyl_p->inputfile,
125 grpsyl_p->inputlineno);
126
127 /* for each staff and verse being defined, clone the GRPSYL
128 * list and associate with appropriate staff */
129 /* do each appropriate staff */
130 for (slist_p = Staffrange_p; slist_p != (struct RANGELIST *) 0;
131 slist_p = slist_p->next) {
132 place = (slist_p->place == PL_UNKNOWN ? PL_BELOW : slist_p->place);
133 for (staffno = slist_p->begin; staffno <= slist_p->end;
134 staffno++) {
135
136 /* do each appropriate verse */
137 for (vlist_p = Vnorange_p;
138 vlist_p != (struct RANGELIST *) 0;
139 vlist_p = vlist_p->next) {
140
141 for (verse = vlist_p->begin;
142 verse <= vlist_p->end;
143 verse++) {
144
145 /* -1 means one more than previous */
146 if (verse == -1) {
147
148 verse = Prev_verse_num[staffno][place] + 1;
149 }
150 Prev_verse_num[staffno][place] = verse;
151 /* connect to proper staff */
152 assoc_lyr2staff(staffno, verse,
153 grpsyl_p, lyrstring,
154 slist_p->all, place);
155
156 /* mark that we've use this verse # */
157 vno_used(verse);
158 }
159 }
160 }
161 }
162
163 /* free the vno rangelist. Don't free the staff info yet, because
164 * there may be more verses */
165 free_vnorange();
166
167 /* the lyrics string has been broken into syllables. Don't need
168 * the original string anymore */
169 FREE(lyrstring);
170}
171\f
172
173/* connect a list of syllable to specified STAFF */
174
175static void
176assoc_lyr2staff(staffno, verse, gs_p, lyrstring, all, place)
177
178int staffno; /* which staff lyrics belong to */
179int verse; /* verse number of lyrics */
180struct GRPSYL *gs_p; /* list of GRPSYLs with time information */
181char *lyrstring; /* string of lyrics syllables in this measure/verse */
182int all; /* YES if associated with "all" */
183int place; /* PL_* */
184
185{
186 struct LYRINFO *new_p; /* to store info about
187 * current staff/verse lyrics */
188 struct LYRINFO *lyrinfo_p; /* used when searching for
189 * where to insert this verse */
190 struct LYRINFO **insert_place_p_p; /* address of where to add
191 * lyric info when doing
192 * insertion sort */
193 char *lyr_copy; /* copy of lyrics */
194 int font;
195 int size;
196 struct LYRFONTSIZE *lyrfontsize_p; /* info about font/size for
197 * current staff/verse/place */
198
199
200 debug(4, "assoc_lyr2staff file=%s lineno=%d staffno=%d, verse=%d, all=%d, place=%d",
201 gs_p->inputfile, gs_p->inputlineno, staffno, verse,
202 all, place);
203
204 if (staffno > Score.staffs) {
205 l_yyerror(gs_p->inputfile, gs_p->inputlineno,
206 "can't have lyrics for staff %d when there aren't that many staffs",
207 staffno);
208 return;
209 }
210
211 /* make a copy of the grpsyl list for this staff/verse */
212 gs_p = clone_gs_list(gs_p, NO);
213
214 /* allocate space to save info, and fill it in */
215 CALLOC(LYRINFO, new_p, 1);
216 new_p->vno = (short) verse;
217 new_p->place = place;
218 new_p->gs_p = gs_p;
219
220 /* insertion sort into list of lyrics for this staff */
221 for (insert_place_p_p = & (Lyr_tbl[staffno]);
222 *insert_place_p_p != (struct LYRINFO *) 0;
223 insert_place_p_p = & ((*insert_place_p_p)->next) ) {
224
225 lyrinfo_p = (*insert_place_p_p);
226
227 if ( lyrinfo_p->vno > verse) {
228 /* need to insert new one right before this one */
229 break;
230 }
231
232 if ( (lyrinfo_p->vno == verse)
233 && (lyrinfo_p->place == new_p->place)) {
234 l_yyerror(Curr_filename, yylineno,
235 "verse %d, staff %d multiply defined",
236 verse, staffno);
237 return;
238 }
239 }
240 new_p->next = *insert_place_p_p;
241 *insert_place_p_p = new_p;
242
243 /* we need to set font/size on a per-staff basis. That means we
244 * have to make a temporary copy for each instance and get the
245 * syllables each time */
246 lyrfontsize_p = getlyrfontsize(staffno, verse, Place, all);
247 font = lyrfontsize_p->font;
248 size = lyrfontsize_p->size;
249
250 lyr_copy = lyrstring = copy_string(lyrstring + 2, font, size);
251
252 /* get into internal format */
253 fix_string(lyrstring, font, size, Curr_filename, yylineno);
254
255 /* skip past the font/size */
256 lyrstring += 2;
257
258 /* fill in vno, staffno, and syllables */
259 for ( ; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
260
261 gs_p->staffno = (short) staffno;
262 gs_p->vno = (short) verse;
263
264 /* if this GRPSYL is a space, don't fill in a syllable */
265 if (gs_p->grpcont == GC_SPACE) {
266 continue;
267 }
268
269 /* otherwise, find the next syllable, and store it in
270 * the GRPSYL */
271 gs_p->syl = next_syl(&lyrstring, &font, &size,
272 &(gs_p->sylposition), staffno);
273 }
274
275 /* keep track of font/size at end of measure in case we have more
276 * lyrics in this same staff/verse/place on a later measure */
277 updlyrfontsize(lyrfontsize_p, font, size);
278
279 /* skip over any trailing white space */
280 while (*lyrstring == ' ' || *lyrstring == '\t') {
281 lyrstring++;
282 }
283
284 if (*lyrstring != '\0') {
285 yyerror("too many syllables in string");
286 }
287
288 /* the copy has been broken into syllables. Free the copy */
289 FREE(lyr_copy);
290}
291\f
292
293/* given a staffno, verse, and place, return (via pointer to struct of info)
294 * the font and size that should be used */
295
296static struct LYRFONTSIZE *
297getlyrfontsize(staffno, verse, place, all)
298
299int staffno;
300int verse;
301int place;
302int all; /* YES if associated with "all" */
303
304{
305 struct LYRFONTSIZE *lfs_p; /* walk through linked list */
306
307
308 /* if this staff/verse/place is on the list, use the values stored
309 * there. */
310 for (lfs_p = Lyrfontsizeinfo_p [staffno];
311 lfs_p != (struct LYRFONTSIZE *) 0;
312 lfs_p = lfs_p->next) {
313
314 if (lfs_p->staffno == staffno && lfs_p->verse == verse
315 && lfs_p->place == place && lfs_p->all == all) {
316 return(lfs_p);
317 }
318 }
319
320 /* wasn't on list yet. Add to list, using default values from SSV */
321 MALLOC(LYRFONTSIZE, lfs_p, 1);
322 lfs_p->staffno = (short) staffno;
323 lfs_p->verse = (short) verse;
324 lfs_p->place = (char) place;
325 lfs_p->all = (char) all;
326 if (all == YES) {
327 lfs_p->font = (char) (Score.lyricsfamily + Score.lyricsfont);
328 lfs_p->size = (char) Score.lyricssize;
329 }
330 else {
331 lfs_p->font = svpath(staffno, LYRICSFAMILY)->lyricsfamily
332 + svpath(staffno, LYRICSFONT)->lyricsfont;
333 lfs_p->size = svpath(staffno, LYRICSFONT)->lyricssize;
334 }
335 lfs_p->next = Lyrfontsizeinfo_p [ staffno ];
336 Lyrfontsizeinfo_p [ staffno ] = lfs_p;
337
338 return(lfs_p);
339}
340\f
341
342/* update values of font/size for a given LYRFONTSIZE struct */
343
344static void
345updlyrfontsize(lyrfontsize_p, font, size)
346
347struct LYRFONTSIZE *lyrfontsize_p;
348int font;
349int size;
350
351{
352 lyrfontsize_p->font = (char) font;
353 lyrfontsize_p->size = (char) size;
354}
355\f
356
357/* when user sets lyricsfont via an SSV, that overrides whatever fonts are
358 * currently in effect, so change them. */
359
360void
361setlyrfont(staffno, font)
362
363int staffno;
364int font;
365
366{
367 int s; /* index through staffs */
368 struct LYRFONTSIZE *lfs_p; /* info about font/size */
369
370
371 if (Context == C_SCORE) {
372 Context = C_STAFF;
373 /* reset all staffs */
374 for (s = 1; s <= Score.staffs; s++) {
375 setlyrfont(s, font);
376 }
377 Context = C_SCORE;
378 }
379 else {
380 /* set all place/verse combinations of given staff */
381 for (lfs_p = Lyrfontsizeinfo_p [staffno];
382 lfs_p != (struct LYRFONTSIZE *) 0;
383 lfs_p = lfs_p->next) {
384 lfs_p->font = (lfs_p->all == YES ? Score.lyricsfamily
385 + Score.lyricsfont
386 : svpath(staffno, LYRICSFAMILY)->lyricsfamily
387 + font);
388 }
389 }
390}
391\f
392
393/* when user sets lyricsize via an SSV, that overrides whatever fonts are
394 * currently in effect, so change them. */
395
396void
397setlyrsize(staffno, size)
398
399int staffno;
400int size;
401
402{
403 int s; /* index through staffs */
404 struct LYRFONTSIZE *lfs_p; /* font/size info */
405
406
407 if (Context == C_SCORE) {
408 /* reset all staffs */
409 Context = C_STAFF;
410 for (s = 1; s <= Score.staffs; s++) {
411 setlyrsize(s, size);
412 }
413 Context = C_SCORE;
414 }
415 else {
416 /* set all place/verse combinations of given staff */
417 for (lfs_p = Lyrfontsizeinfo_p [staffno];
418 lfs_p != (struct LYRFONTSIZE *) 0;
419 lfs_p = lfs_p->next) {
420 lfs_p->size = (char) (lfs_p->all == YES
421 ? Score.lyricssize : size);
422 }
423 }
424}
425\f
426
427/* This function is called when an entire measure has been collected, to
428 * take care of all the lyrics.
429 * For each staff, allocate the proper number of elements for the syls_p
430 * list and fill them in.
431 * Then free all the LYRINFO structs and clean out the Lyr_tbl.
432 */
433
434void
435attach_lyrics2staffs(mll_staffs_p)
436
437struct MAINLL *mll_staffs_p; /* the list of staffs to attch to */
438
439{
440 struct STAFF *staff_p; /* the staff we are currently dealing
441 * with */
442 int verses; /* how many verses for staff */
443 struct LYRINFO *lyrinfo_p; /* where lyrics info is stored */
444
445
446 debug(4, "attach_lyrics2staffs");
447
448 /* do each staff */
449 for ( ; mll_staffs_p != (struct MAINLL *) 0;
450 mll_staffs_p = mll_staffs_p->next) {
451
452 if (mll_staffs_p->str != S_STAFF) {
453 /* must have gotten to end of staffs; done */
454 break;
455 }
456
457 /* need staff pointer a lot. Get shorter name for it */
458 staff_p = mll_staffs_p->u.staff_p;
459
460 /* count up how many verses there are associated with this
461 * staff */
462 for ( verses = 0, lyrinfo_p = Lyr_tbl[staff_p->staffno];
463 lyrinfo_p != (struct LYRINFO *) 0;
464 lyrinfo_p = lyrinfo_p->next) {
465 verses++;
466 }
467
468 /* if no verses, nothing to do */
469 if (verses == 0) {
470 continue;
471 }
472
473 /* alloc space for the appropriate number of verses */
474 MALLOC(GRPSYL *, staff_p->syls_p, verses);
475 if ((staff_p->sylplace = (short *) malloc
476 (sizeof(short) * verses)) == (short *) 0) {
477 l_no_mem(__FILE__, __LINE__);
478 }
479 staff_p->nsyllists = (short) verses;
480
481 /* now attach the GRPSYLs and set the places for each */
482 for (verses = 0, lyrinfo_p = Lyr_tbl[staff_p->staffno];
483 lyrinfo_p != (struct LYRINFO *) 0;
484 lyrinfo_p = lyrinfo_p->next, verses++) {
485
486 staff_p->syls_p[verses] = lyrinfo_p->gs_p;
487 staff_p->sylplace[verses] = lyrinfo_p->place;
488 }
489
490 free_lyrinfo(Lyr_tbl[staff_p->staffno]);
491 Lyr_tbl[staff_p->staffno] = (struct LYRINFO *) 0;
492 }
493}
494\f
495
496/* recursively free list of LYRINFO structs */
497
498static void
499free_lyrinfo(lyrinfo_p)
500
501struct LYRINFO *lyrinfo_p; /* the list to free */
502
503{
504 if (lyrinfo_p == (struct LYRINFO *) 0) {
505 return;
506 }
507
508 free_lyrinfo(lyrinfo_p->next);
509 FREE(lyrinfo_p);
510}
511\f
512
513/* At each bar line, reset previous verse number for auto-verse-numbering */
514
515void
516lyr_new_bar()
517{
518 int staff_index;
519 int place_index;
520
521 for (staff_index = 1; staff_index <= MAXSTAFFS; staff_index++) {
522 for (place_index = 0; place_index < NUM_PLACE; place_index++) {
523 Prev_verse_num[staff_index][place_index] = 0;
524 }
525 }
526}
527
528\f
529
530/* in several places we need to copy part of a lyric from one string to
531 * another while keeping track of the font and size changes along the way.
532 * Doing this with a function gets hard to think about because you'd
533 * have to pass around a bunch of pointers to pointers and keep all the
534 * levels of indirection straight, so I opted for a macro.
535 * destbuff is the char array into which to copy. destindex is the subscript
536 * into that array. src_p is a char * from which to copy. delimiter is the
537 * character at which to stop copying. fnt_p and sz_p are pointer to font
538 * and size which need to be updated if the font or size change.
539 */
540#define COPY_LYR(destbuff, destindex, src_p, delimiter, fnt_p, sz_p) \
541 for ( ; *src_p != '\0' && *src_p != delimiter; src_p++) { \
542 destbuff[destindex++] = *src_p; \
543 \
544 switch ( (unsigned) *src_p & 0xff) { \
545 case STR_FONT: \
546 *fnt_p = *++src_p; \
547 destbuff[destindex++] = *src_p; \
548 break; \
549 case STR_SIZE: \
550 *sz_p = *++src_p; \
551 destbuff[destindex++] = *src_p; \
552 break; \
553 case STR_MUS_CHAR: \
554 destbuff[destindex++] = *++src_p; \
555 destbuff[destindex++] = *++src_p; \
556 break; \
557 case STR_BACKSPACE: \
558 destbuff[destindex++] = *++src_p; \
559 break; \
560 default: \
561 break; \
562 } \
563 }
564\f
565
566
567/* break a string of lyrics into individual syllables. The following
568 * characters are special:
569 * <space> marks end of syllable
570 * <tab> same as space
571 * - end of syllable
572 * _ end of syllable
573 * '<' beginning of non-lyric item
574 * '>' end of non-lyric item
575 */
576/* Each syllable will be returned as a string of the form:
577 * <font> <size> [<STR_PRE> pre-item <STR_PRE_END>] syllable [<STR_PST> post-item <STR_PST_END>]
578 * where
579 * <font> is a 1-byte font identifier, FONT_TR, FONT_TI etc
580 * <size> is a 1-byte size, in points
581 * if there is non-lyrics stuff to be put before the lyric syllable, it
582 * will be surrounded by <STR_PRE> and <STR_PRE_END> characters.
583 * It may contain other commands for font changes, etc.
584 * This field is optional. If present it may be of arbitrary
585 * length, including zero length
586 * If this text is to be used for syllable
587 * placement, the header will be <STR_U_PRE> instead
588 * The syllable follows. It may contain any of the commands found in
589 * any other string. It can be of zero length.
590 * Optionally, there may there something to print after the syllable
591 * that isn't part of the syllable. If there is such a thing,
592 * it will be surrounded by <STR_PST> and <STR_PST_END> characters.
593 * If this text is to be used for syllable placement, the
594 * header will be <STR_U_PST> instead.
595 * Any of the fields can be of zero length, but at least one of them
596 * must be of non-zero length for each syllable to be sensible.
597 * This function should be called after the string has been transformed
598 * to internal format, with font/size changes stored as commands.
599 * If a syllable starts with a positioning value, that is updated.
600 */
601
602static char *
603next_syl(sylstring_p, font_p, size_p, position_p, staffno)
604
605char **sylstring_p; /* address of pointer to current spot in lyric string
606 * of syllables. At entry, its contents should be
607 * the address of the first character of the syllable
608 * to be returned. On return, its contents will be
609 * updated to point to the first character of the
610 * following syllable, to be ready for the next call
611 * to this function.
612 */
613int *font_p; /* pointer to current font value, will be updated
614 * with new font if it changes. */
615int *size_p; /* pointer to current size value; will be updated
616 * with new size if it changes */
617short *position_p; /* pointer to sylposition field, will be updated
618 * with the user-specified position value, if
619 * they specified one, otherwise will be left as is. */
620int staffno; /* which staff this syllable is for */
621
622{
623 char *p; /* pointer into lyrics string */
624 int done; /* boolean to keep track of if we are done */
625 int font, size; /* original font and size */
626 int i; /* index into sylbuff */
627 char sylbuff[BUFSIZ]; /* temporary storage for a syllable */
628
629
630 /* initialize */
631 sylbuff[0] = '\0';
632 i = 0;
633 font = *font_p;
634 size = *size_p;
635
636 /* skip any leading white space */
637 for (p = *sylstring_p; *p != '\0'; p++) {
638 if (*p != ' ') {
639 break;
640 }
641 }
642
643 /* see if we have a non-lyrics before the lyric */
644 if ( *p == '<') {
645
646 /* we do have a non-lyric. Put in the header, and copy up
647 * to the '>', keeping track of any font/size changes
648 * along the way */
649 if (*(p+1) == '^') {
650 sylbuff[i++] = (char) STR_U_PRE;
651 p++;
652 }
653 else {
654 sylbuff[i++] = (char) STR_PRE;
655 }
656 p++;
657 COPY_LYR(sylbuff, i, p, '>', font_p, size_p);
658
659 /* add in the > marker */
660 sylbuff[i++] = (char) STR_PRE_END;
661 if (*p == '\0') {
662 yyerror("missing '>' in lyric");
663 }
664 else {
665 p++;
666 }
667 }
668
669 /* see if user gave a position value */
670 if (*p == '|') {
671 /* use default value from parameter */
672 *position_p = svpath(staffno, SYLPOSITION)->sylposition;
673 p++;
674 }
675 else if ( isdigit(*p) ||
676 ( (*p == '-' || *p == '+') && isdigit(*(p+1)) ) ) {
677 char *after_num_p;
678 int value;
679
680 /* if there is a pipe after the number, it's a position */
681 value = strtol(p, &after_num_p, 10);
682 if ( *after_num_p == '|') {
683 *position_p = value;
684 p = after_num_p + 1;
685 }
686 }
687
688 /* now collect the lyric syllable itself */
689 for ( done = NO; *p != '\0' && i < BUFSIZ; p++) {
690
691 switch ( (unsigned) *p & 0xff) {
692
693 case ' ':
694 case '\t':
695 /* definitely end of this lyric syllable */
696 done = YES;
697 break;
698
699
700 case '~':
701 /* two syllables joined into one. Replace the ~
702 * by a space */
703 sylbuff[i++] = ' ';
704 p++;
705 break;
706
707 case '-':
708 case '_':
709 /* end of syllable */
710 /* need to peek ahead to check for non-lyric following */
711 sylbuff[i++] = *p++;
712 if ( *p != '<') {
713 done = YES;
714 break;
715 }
716 /*FALLTHRU*/
717
718 case '<':
719 if (*(p+1) == '^') {
720 sylbuff[i++] = (char) STR_U_PST;
721 p++;
722 }
723 else {
724 sylbuff[i++] = (char) STR_PST;
725 }
726 p++;
727 COPY_LYR(sylbuff, i, p, '>', font_p, size_p);
728
729 sylbuff[i++] = (char) STR_PST_END;
730 p++;
731 /* A ~ joins this syllable with the next to make
732 * them effectively one, but anything else ends
733 * the syllable. But copy - or _ if they are there */
734 if ( *p != '~') {
735 if (*p == '-' || *p == '_') {
736 sylbuff[i++] = *p++;
737 }
738 done = YES;
739 }
740 break;
741
742 case STR_FONT:
743 sylbuff[i++] = *p++;
744 *font_p = *p;
745#ifdef EXTCHAR
746 if (*font_p >= FONT_XTR) {
747 sylbuff[i++] = *p++;
748 }
749#endif
750 break;
751
752 case STR_SIZE:
753 sylbuff[i++] = *p++;
754 *size_p = *p;
755 break;
756
757 case STR_BACKSPACE:
758 sylbuff[i++] = *p++;
759 break;
760
761 case STR_MUS_CHAR:
762 sylbuff[i++] = *p++;
763 sylbuff[i++] = *p++;
764 break;
765
766 case STR_BOX:
767 yyerror("boxed text not allowed in lyrics");
768 break;
769
770 case STR_BOX_END:
771 break;
772
773 case STR_CIR:
774 yyerror("circled text not allowed in lyrics");
775 break;
776
777 case STR_CIR_END:
778 break;
779
780 case STR_PILE:
781 yyerror("\\: not allowed in lyric syllable");
782 break;
783
784 case STR_C_ALIGN:
785 case STR_L_ALIGN:
786 yyerror("alignment not allowed in lyric syllable");
787 break;
788
789 default:
790 break;
791 }
792
793 if (done == NO) {
794 sylbuff[i++] = *p;
795 }
796 else {
797 break;
798 }
799 }
800
801 sylbuff[i] = '\0';
802
803 if (i == 0) {
804 yyerror("too few syllables in string");
805 }
806
807 /* prepare for next call to this function */
808 *sylstring_p = p;
809
810 /* return a copy of the extracted syllable */
811 return(copy_string(sylbuff, font, size));
812}
813\f
814
815/* given a syllable string, return the width (in inches) of the non-lyric
816 * parts and of the actual syllable */
817
818void
819sylwidth(lyrstring, wid_b4_syl_p, wid_real_syl_p, wid_after_syl_p)
820
821char *lyrstring;
822float *wid_b4_syl_p; /* width of leading non-lyrics will be put here */
823float *wid_real_syl_p; /* width of actual syllable will be put here */
824float *wid_after_syl_p; /* width of trailing non-lyric will be put here */
825
826{
827 do_sylwidth(lyrstring, wid_b4_syl_p, wid_real_syl_p, wid_after_syl_p,
828 NO);
829}
830\f
831
832/* find width of syllable and <...> things. When called in placement phase
833 * we count the <^...> and don't count <...>. When called in print phase
834 * we do the opposite to compensate.
835 */
836
837static void
838do_sylwidth(lyrstring, wid_b4_syl_p, wid_real_syl_p, wid_after_syl_p,
839 compensating)
840
841char *lyrstring;
842float *wid_b4_syl_p; /* width of leading non-lyrics will be put here */
843float *wid_real_syl_p; /* width of actual syllable will be put here */
844float *wid_after_syl_p; /* width of trailing non-lyric will be put here */
845int compensating; /* YES if being called from print phrase when we
846 * have to compensate for the fact that <...> may
847 * not have been used in placement. */
848
849{
850 int pre_counts = NO; /* if pre width should be included */
851 int post_counts = NO; /* if post width should be included */
852 int had_post = NO; /* if had post <...> */
853 float w1 = 0.0; /* width of pre */
854 float w2 = 0.0; /* width of pre + syllable */
855 float w3; /* width of entire string */
856 char *p;
857 char save; /* temp storage */
858
859
860 if (lyrstring == (char *) 0) {
861 *wid_b4_syl_p = *wid_real_syl_p = *wid_after_syl_p = 0.0;
862 return;
863 }
864
865 for (p = lyrstring; *p != '\0'; p++) {
866 switch ( (unsigned) *p & 0xff) {
867 case STR_PRE:
868 if (compensating == YES) {
869 pre_counts = YES;
870 }
871 break;
872 case STR_U_PRE:
873 if (compensating == NO) {
874 pre_counts = YES;
875 }
876 break;
877 case STR_PRE_END:
878 /* get width of string so far. Temporarily shorten
879 * string to this point and get its length. This
880 * will be the length of the preceeding <...> */
881 *p = '\0';
882 w1 = strwidth(lyrstring);
883 *p = (char) STR_PRE_END;
884 break;
885 case STR_U_PST:
886 /* get width of string so far. Temporarily shorten
887 * string to this point and get its length. This
888 * will be the length of the syllable and its
889 * preceeding <...> */
890 if (compensating == NO) {
891 post_counts = YES;
892 }
893 save = *p;
894 *p = '\0';
895 w2 = strwidth(lyrstring);
896 *p = save;
897 had_post = YES;
898 break;
899 case STR_PST:
900 if (compensating == YES) {
901 post_counts = YES;
902 }
903 save = *p;
904 *p = '\0';
905 w2 = strwidth(lyrstring);
906 *p = save;
907 had_post = YES;
908 break;
909 default:
910 break;
911 }
912 }
913
914 /* get length of entire string */
915 w3 = strwidth(lyrstring);
916
917 /* if there wasn't a post <...> we didn't yet get the width of
918 * the syllable plus preceeding <...> */
919 if (had_post == NO) {
920 w2 = w3;
921 }
922
923 /* now calculate and return the widths */
924 *wid_b4_syl_p = (pre_counts == YES ? w1 : 0.0);
925 *wid_real_syl_p = w2 - w1;
926 *wid_after_syl_p = (post_counts == YES ? w3 - w2 : 0.0);
927}
928\f
929
930/* during placement phase, <...> things were used or not used as appropriate
931 * for setting placement. During print phrase, we do the opposite to
932 * compensate, and update the group east and west boundaries as necessary */
933void
934lyr_compensate(gs_p)
935
936struct GRPSYL *gs_p;
937
938{
939 float pre_wid, syl_wid, post_wid;
940
941 do_sylwidth(gs_p->syl, &pre_wid, &syl_wid, &post_wid, YES);
942 gs_p->c[AW] -= pre_wid;
943 gs_p->c[AE] += post_wid;
944}
945\f
946
947/* every time a verse number is used, see whether we've already had that
948 * verse number before. if not, add it to the list of verse numbers used. */
949/* We could keep each range of defined verses in a RANGELIST, so that,
950 * for example, 1-3 could be keep in a single struct. However, trying to
951 * coalese the list could get very hairy, so do the simple-minded way of
952 * saving each unique verse number in its own struct. We could also
953 * insertion sort the list, but rarely will there be more than a couple
954 * verses, and inserting into a singly linked list takes a slight amount
955 * of work, so be very lazy and just search the whole list each time and
956 * insert at beginning if need to add to list. */
957
958static void
959vno_used(vno)
960
961int vno; /* the verse number to check */
962
963{
964 struct RANGELIST *v_p; /* to walk through list of verse nums used */
965 struct RANGELIST **list_p_p; /* which list to check */
966
967
968 switch (Place) {
969 case PL_ABOVE:
970 list_p_p = &Above_vnumbers_used;
971 break;
972 case PL_BETWEEN:
973 list_p_p = &Between_vnumbers_used;
974 break;
975 case PL_BELOW:
976 case PL_UNKNOWN:
977 list_p_p = &Below_vnumbers_used;
978 break;
979 default:
980 pfatal("illegal place in vno_used");
981 /*NOTREACHED*/
982 return;
983 }
984
985 /* see if this verse is already on the list */
986 for (v_p = *list_p_p; v_p != (struct RANGELIST *) 0; v_p = v_p->next) {
987 if (v_p->begin == vno) {
988 /* already had this verse before */
989 return;
990 }
991 }
992
993 /* must not have seen this verse number before, so add it */
994 CALLOC(RANGELIST, v_p, 1);
995 v_p->begin = (short) vno;
996 /* ->end is not used in this list */
997 v_p->next = *list_p_p;
998 *list_p_p = v_p;
999}
1000\f
1001
1002/* when parsing is complete, we can find out how many unique verse numbers
1003 * there were in the input */
1004
1005void
1006set_maxverses()
1007{
1008 debug(4, "set_maxverses");
1009
1010 count_verses(Above_vnumbers_used);
1011 count_verses(Below_vnumbers_used);
1012 count_verses(Between_vnumbers_used);
1013}
1014
1015/* recursively walk down list of verse numbers used. Count them up and
1016 * free them while unwinding */
1017
1018static void
1019count_verses(used_p)
1020
1021struct RANGELIST *used_p;
1022
1023{
1024 if (used_p == (struct RANGELIST *) 0) {
1025 return;
1026 }
1027
1028 count_verses(used_p->next);
1029 Maxverses++;
1030 FREE(used_p);
1031}
1032\f
1033
1034/* Return pointer to the SSV that contains the default timeunit information
1035 * for verses being defined. Make sure if there are multiple staffs
1036 * being defined, they all have the same default timeunit. If there is only
1037 * one voice on the staff, use that for default. If there are 2 voices, if the
1038 * place is PL_ABOVE use voice 1, otherwise use voice 2. */
1039
1040struct SSV *
1041get_lyr_dflt_timeunit_ssv()
1042
1043{
1044 struct RANGELIST *staffrange_p; /* to index through list of staffs */
1045 struct SSV *ssv_p; /* containing relevent timeunit */
1046 struct SSV *ref_ssv_p; /* to ensure consistency */
1047 RATIONAL timeunit; /* timeunit for current staff */
1048 RATIONAL reference_timeunit; /* the timeunit found on previous
1049 * staff, for comparison to make sure
1050 * they are the same */
1051 int staff;
1052
1053
1054 reference_timeunit = Zero;
1055 /* if all else fails, use Score value */
1056 ssv_p = ref_ssv_p = &Score;
1057
1058 /* go down the list of staffs, get timeunit for each */
1059 for (staffrange_p = Staffrange_p;
1060 staffrange_p != (struct RANGELIST *) 0;
1061 staffrange_p = staffrange_p->next) {
1062
1063 for (staff = staffrange_p->begin; staff <= staffrange_p->end;
1064 staff++) {
1065
1066 /* get appropriate default timeunit value */
1067 if (svpath(staff, VSCHEME)->vscheme == V_1 ||
1068 Place == PL_ABOVE) {
1069 ssv_p = vvpath(staff, 1, TIMEUNIT);
1070 }
1071 else {
1072 ssv_p = vvpath(staff, 2, TIMEUNIT);
1073 }
1074 timeunit = ssv_p->timeunit;
1075
1076 /* check for consistency */
1077 if ( NE(reference_timeunit, Zero) ) {
1078 if ( NE(timeunit, reference_timeunit) ||
1079 timelists_equal(
1080 ssv_p->timelist_p,
1081 ref_ssv_p->timelist_p) == NO) {
1082 /* have a mis-match. Give error message,
1083 * and return the first timeunit we got.
1084 * No reason to check any more, because
1085 * all that may happen is that we'll
1086 * print lots more error messages for
1087 * the same problem. */
1088 yyerror("timeunit value must be the same for all lyric staffs being defined on the same input line");
1089 return(ssv_p);
1090 }
1091 }
1092 else {
1093 /* first time through. Now we have a timeunit
1094 * to use for default */
1095 reference_timeunit = timeunit;
1096 ref_ssv_p = ssv_p;
1097 }
1098 }
1099 }
1100
1101 /* Used to pfatal if couldn't find default value. However, that can
1102 * happen if user specifies an invalid staff number, so that isn't
1103 * a program bug, but a user error. The user error would already have
1104 * been flagged, so no reason to complain at all here */
1105
1106 return(ssv_p);
1107}
1108\f
1109
1110/* user wants us to derive the lyrics time values from corresponding music
1111 * time values. Find the proper list of GRPSYLs to copy time values from,
1112 * and make a copy of that list, with field set properly for lyrics. */
1113
1114struct GRPSYL *
1115derive_lyrtime()
1116
1117{
1118 struct MAINLL *mll_p; /* to find STAFF to derive from */
1119 struct GRPSYL *new_list_p; /* the list of derived time values */
1120 struct GRPSYL *gs_p; /* to walk through new_list_p */
1121 struct GRPSYL *ref_gs_p; /* to walk through the reference list--
1122 * the list we are deriving from */
1123 int staff, voice; /* which list of GRPSYLs to clone */
1124
1125
1126 /* If there are no staffs associated with these lyrics, then nothing
1127 * to do here. We should have already printed an error elsewhere */
1128 if (Staffrange_p == (struct RANGELIST *) 0) {
1129 return (struct GRPSYL *) 0;
1130 }
1131
1132 /* find the proper music GRPSYL from which to derive time values */
1133 staff = leadstaff();
1134 for (mll_p = Mainlltc_p; mll_p != (struct MAINLL *) 0;
1135 mll_p = mll_p->prev) {
1136
1137 /* if back up all the way to a bar, user hasn't defined the
1138 * right staff's music input yet */
1139 if (mll_p->str == S_BAR) {
1140 /* pretend we fell off top of list and break out.
1141 * That way we only need one copy of error message code */
1142 mll_p = (struct MAINLL *) 0;
1143 break;
1144 }
1145 if (mll_p->str == S_STAFF && mll_p->u.staff_p->staffno == staff) {
1146 /* found the right staff data */
1147 break;
1148 }
1149 }
1150
1151 if (mll_p == (struct MAINLL *) 0) {
1152 l_yyerror(Curr_filename, yylineno,
1153 "can't derive lyrics time values: staff %d music not defined yet", staff);
1154 return (struct GRPSYL *) 0;
1155 }
1156
1157 /* usually use voice 1 of first staff specified. Only exceptions
1158 * are if voice 1 is invisible or nonexistent or if explicitly below,
1159 * and there is a visible voice 2, in which case voice 2 is used */
1160 voice = 0;
1161 if ((vvpath(staff, 1, VISIBLE)->visible == NO ||
1162 mll_p->u.staff_p->groups_p[0] == (struct GRPSYL *) 0
1163 || Place == PL_BELOW)
1164 && mll_p->u.staff_p->groups_p[1] != (struct GRPSYL *) 0
1165 && vvpath(staff, 2, VISIBLE)->visible == YES) {
1166 voice = 1;
1167 }
1168 /* One more exception. If both voices are invisible, but we would
1169 * have used voice 2 if it was visible, use voice 2. Otherwise
1170 * if times would normally be derived from voice 2, but the
1171 * whole staff is invisible, we could derive the wrong values. */
1172 if (vvpath(staff, 1, VISIBLE)->visible == NO &&
1173 vvpath(staff, 2, VISIBLE)->visible == NO &&
1174 mll_p->u.staff_p->groups_p[1] != (struct GRPSYL *) 0 &&
1175 Place == PL_BELOW) {
1176 voice = 1;
1177 }
1178
1179 if (mll_p->u.staff_p->groups_p[voice] == (struct GRPSYL *) 0) {
1180 l_yyerror(Curr_filename, yylineno,
1181 "can't derive lyrics time values: staff %d voice %d music not defined yet", staff, voice + 1);
1182 return (struct GRPSYL *) 0;
1183 }
1184
1185 new_list_p = clone_gs_list(mll_p->u.staff_p->groups_p[voice], NO);
1186
1187 /* grace notes don't count in the time derivation, so remove
1188 * them from the cloned list */
1189 for (gs_p = new_list_p; gs_p != (struct GRPSYL *) 0; gs_p = gs_p->next) {
1190 if (gs_p->grpvalue == GV_ZERO) {
1191 struct GRPSYL *to_free_p;
1192 to_free_p = gs_p;
1193 if (gs_p->prev == 0) {
1194 new_list_p = gs_p->next;
1195 }
1196 else {
1197 gs_p->prev->next = gs_p->next;
1198 }
1199 /* Note that there should always be a 'next' after
1200 * a grace note, so this 'if' shouldn't really be
1201 * necessary, but it is here as defensive code. */
1202 if (gs_p->next != 0) {
1203 gs_p->next->prev = gs_p->prev;
1204 }
1205 FREE(to_free_p);
1206 }
1207 }
1208
1209 /* go through cloned list, changing type to lyrics and changing rests
1210 * in the music to spaces for the lyrics. Also deal with ties and slurs,
1211 * since notes that are tied or slurred to will be treated like a
1212 * space, since there is probably only a single syllable wanted. */
1213 for (gs_p = new_list_p, ref_gs_p = mll_p->u.staff_p->groups_p[voice];
1214 gs_p != (struct GRPSYL *) 0;
1215 gs_p = gs_p->next, ref_gs_p = ref_gs_p->next) {
1216 /* Skip graces in reference list. (There aren't any in
1217 * the new list since we removed them above.) */
1218 while (ref_gs_p->grpvalue == GV_ZERO) {
1219 ref_gs_p = ref_gs_p->next;
1220 if (ref_gs_p == 0) {
1221 /* should be impossible to get here */
1222 pfatal("unexpected null ref_gs_p in derive_lyrtime");
1223 }
1224 }
1225
1226 gs_p->grpsyl = GS_SYLLABLE;
1227 if (ref_gs_p->grpcont == GC_REST) {
1228 gs_p->grpcont = GC_SPACE;
1229 }
1230 else if (ref_gs_p->grpcont == GC_NOTES && ref_gs_p->nnotes > 0) {
1231 /* if the top note of the chord is tied or slurred,
1232 * or the entire chord is tied,
1233 * make the following lyrics GRPSYL, if any, a space. */
1234 if ((ref_gs_p->notelist[0].tie == YES ||
1235 ref_gs_p->tie == YES ||
1236 ref_gs_p->notelist[0].nslurto > 0) &&
1237 gs_p->next != (struct GRPSYL *) 0) {
1238 gs_p->next->grpcont = GC_SPACE;
1239 }
1240 }
1241 }
1242
1243 /* One more detail: if the first note was tied-to or slurred-to from
1244 * the previous measure, we have to change that to a space. To do that,
1245 * we call prevgrpsyl, but it expects the staffno and vno to be filled
1246 * in on the GRPSYL passed to it, and we're so early in parsing that
1247 * that hasn't happened yet. So first have to patch that up. Later on,
1248 * all the GRPSYLs will get the staffno and vno filled in, but it won't
1249 * hurt anything that we've already done this one here; they will
1250 * just be overwritten with the same values. */
1251 ref_gs_p = mll_p->u.staff_p->groups_p[voice];
1252 ref_gs_p->staffno = mll_p->u.staff_p->staffno;
1253 ref_gs_p->vno = voice + 1;
1254 if ((gs_p = prevgrpsyl(ref_gs_p, &mll_p)) != (struct GRPSYL *) 0) {
1255 if (gs_p->grpcont == GC_NOTES && gs_p->nnotes > 0) {
1256 if (gs_p->notelist[0].tie == YES ||
1257 gs_p->tie == YES ||
1258 gs_p->notelist[0].nslurto > 0) {
1259 new_list_p->grpcont = GC_SPACE;
1260 }
1261 }
1262 }
1263
1264 return (new_list_p);
1265}
1266