chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / lyrics.c
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 */
13 struct 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 */
22 struct 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 */
36 struct 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. */
40 static struct LYRINFO *Lyr_tbl[MAXSTAFFS + 1];
41
42 /* need to keep a list of all verse numbers ever used, for setting Maxverses */
43 static struct RANGELIST *Above_vnumbers_used;
44 static struct RANGELIST *Below_vnumbers_used;
45 static struct RANGELIST *Between_vnumbers_used;
46
47 /* for auto-numbering verses, keep track of the last verse number used
48  * for each staff/place */
49 static short Prev_verse_num[MAXSTAFFS+1][NUM_PLACE];
50
51 /* static function declarations */
52 static void free_lyrinfo P((struct LYRINFO *lyrinfo_p));
53 static void assoc_lyr2staff P((int staffno, int verse, struct GRPSYL *gs_p,
54                 char *lyrstring, int all, int place));
55 static struct LYRFONTSIZE *getlyrfontsize P((int staffno, int verse,
56                 int place, int all));
57 static void updlyrfontsize P((struct LYRFONTSIZE *lyrfontsize_p,
58                 int font, int size));
59 static char *next_syl P((char **sylstring_p, int *font_p, int *size_p,
60                         short *position_p, int staffno));
61 static 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));
64 static void count_verses P((struct RANGELIST *used_p));
65 static void vno_used P((int vno));
66 \f
67
68 /* given a range of verses, save the range for later use */
69
70 void
71 lyr_verse(begin, end)
72
73 int begin;      /* first verse in range */
74 int 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
106 void
107 proc_lyrics(grpsyl_p, lyrstring)
108
109 struct GRPSYL *grpsyl_p;        /* list of GRPSYLs with time values */
110 char *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
175 static void
176 assoc_lyr2staff(staffno, verse, gs_p, lyrstring, all, place)
177
178 int staffno;            /* which staff lyrics belong to */
179 int verse;              /* verse number of lyrics */
180 struct GRPSYL *gs_p;    /* list of GRPSYLs with time information */
181 char *lyrstring;        /* string of lyrics syllables in this measure/verse */
182 int all;                /* YES if associated with "all" */
183 int 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
296 static struct LYRFONTSIZE *
297 getlyrfontsize(staffno, verse, place, all)
298
299 int staffno;
300 int verse;
301 int place;
302 int 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
344 static void
345 updlyrfontsize(lyrfontsize_p, font, size)
346
347 struct LYRFONTSIZE *lyrfontsize_p;
348 int font;
349 int 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
360 void
361 setlyrfont(staffno, font)
362
363 int staffno;
364 int 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
396 void
397 setlyrsize(staffno, size)
398
399 int staffno;
400 int 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
434 void
435 attach_lyrics2staffs(mll_staffs_p)
436
437 struct 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
498 static void
499 free_lyrinfo(lyrinfo_p)
500
501 struct 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
515 void
516 lyr_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
602 static char *
603 next_syl(sylstring_p, font_p, size_p, position_p, staffno)
604
605 char **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                          */
613 int *font_p;            /* pointer to current font value, will be updated
614                          * with new font if it changes. */
615 int *size_p;            /* pointer to current size value; will be updated
616                          * with new size if it changes */
617 short *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. */
620 int 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
818 void
819 sylwidth(lyrstring, wid_b4_syl_p, wid_real_syl_p, wid_after_syl_p)
820
821 char *lyrstring;
822 float *wid_b4_syl_p;    /* width of leading non-lyrics will be put here */
823 float *wid_real_syl_p;  /* width of actual syllable will be put here */
824 float *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
837 static void
838 do_sylwidth(lyrstring, wid_b4_syl_p, wid_real_syl_p, wid_after_syl_p,
839                 compensating)
840
841 char *lyrstring;
842 float *wid_b4_syl_p;    /* width of leading non-lyrics will be put here */
843 float *wid_real_syl_p;  /* width of actual syllable will be put here */
844 float *wid_after_syl_p; /* width of trailing non-lyric will be put here */
845 int 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 */
933 void
934 lyr_compensate(gs_p)
935
936 struct 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
958 static void
959 vno_used(vno)
960
961 int 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
1005 void
1006 set_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
1018 static void
1019 count_verses(used_p)
1020
1021 struct 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
1040 struct SSV *
1041 get_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
1114 struct GRPSYL *
1115 derive_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