Commit | Line | Data |
---|---|---|
fac14bbe MW |
1 | |
2 | /* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2002, 2003, 2004 by Arkkra Enterprises */ | |
3 | /* All rights reserved */ | |
4 | ||
5 | /* parser functions related to STUFF */ | |
6 | ||
7 | ||
8 | #include <string.h> | |
9 | #include "defines.h" | |
10 | #include "structs.h" | |
11 | #include "globals.h" | |
12 | ||
13 | /* if user specifies a "til" clause on stuff with a number of measures > 0, | |
14 | * we need to save away info about where the til clause will end, to make sure | |
15 | * that it doesn't fall off the end of the measure or the piece. This is the | |
16 | * struct we use to save this info. */ | |
17 | static struct TIL_INFO { | |
18 | char *inputfile; /* where STUFF was defined */ | |
19 | int inputlineno; /* where STUFF was defined */ | |
20 | int measnum; /* number of measure in which til clause ends */ | |
21 | float count; /* count in measure where til clause ends */ | |
22 | struct TIL_INFO *next; /* for linked list */ | |
23 | } *Til_info_list_p; | |
24 | ||
25 | /* info about the STUFF currently being collected from input */ | |
26 | static int Curr_stuff_type; /* ST_* */ | |
27 | static int Stuff_size; /* point size of stuff text string */ | |
28 | static int Modifier; /* TM_* for text, L_* for phrase */ | |
29 | static int Measnum = 1; /* to check til clauses. Can't use Meas_num | |
30 | * global because it doesn't count invisible | |
31 | * bars but til clauses do */ | |
32 | static int Multi_adjust; /* adjustment to Measnum to account | |
33 | * for multirests */ | |
34 | ||
35 | /* head and tail of list of STUFF currently being collected from input */ | |
36 | static struct STUFF *Head_stufflist_p; | |
37 | static struct STUFF *Tail_stufflist_p; | |
38 | ||
39 | /* current pedal state for each staff. YES if in the middle of doing pedal, | |
40 | * NO if not. */ | |
41 | static short Pedal_state[MAXSTAFFS + 1]; | |
42 | static char *Ped_begin_str; /* will point to "\(begped)" */ | |
43 | static char *Ped_up_down_str; /* will point to "\(pedal)" */ | |
44 | ||
45 | /* static functions */ | |
46 | static struct STUFF *clone_stufflist P((struct STUFF *stufflist_p, | |
47 | int staffno, int all)); | |
48 | static void do_attach P((int staffno, int all, struct RANGELIST *vno_range_p)); | |
49 | static void midi_attach P((int staffno, struct STAFF *staff_p, | |
50 | struct RANGELIST *vno_range_p, int all)); | |
51 | static void free_stufflist P((struct STUFF *stuff_P)); | |
52 | static void free_tils P((struct TIL_INFO *til_p)); | |
53 | static void fix_pedal P((int staffno, struct STUFF *stuff_p)); | |
54 | static void ped_order_chk P((void)); | |
55 | \f | |
56 | ||
57 | /* save current stuff type value. Also check that we are in data (music) | |
58 | * context */ | |
59 | ||
60 | void | |
61 | set_stuff_type(stuff_type) | |
62 | ||
63 | int stuff_type; | |
64 | ||
65 | { | |
66 | Curr_stuff_type = stuff_type; | |
67 | ||
68 | (void) contextcheck(C_MUSIC, "statement"); | |
69 | } | |
70 | \f | |
71 | ||
72 | /* return current stuff type */ | |
73 | ||
74 | int | |
75 | get_stuff_type() | |
76 | ||
77 | { | |
78 | return(Curr_stuff_type); | |
79 | } | |
80 | \f | |
81 | ||
82 | /* check all the things in an input line of stuff, up to the colon, | |
83 | * for consistency, and save interesting info away for later use. */ | |
84 | ||
85 | void | |
86 | chk_stuff_header(size, modifier, place, dist_usage) | |
87 | ||
88 | int size; /* point size, or -1 if to use default */ | |
89 | int modifier; /* TM_* for text, L_* for phrase */ | |
90 | int place; /* PL_* */ | |
91 | int dist_usage; /* SD_* */ | |
92 | ||
93 | { | |
94 | ||
95 | debug(4, "chk_stuff_header"); | |
96 | ||
97 | switch (Curr_stuff_type) { | |
98 | case ST_ROM: | |
99 | case ST_BOLD: | |
100 | case ST_ITAL: | |
101 | case ST_BOLDITAL: | |
102 | case ST_MUSSYM: | |
103 | break; | |
104 | case ST_PEDAL: | |
105 | if (place != PL_BELOW && place != PL_UNKNOWN) { | |
106 | yyerror("pedal must be below"); | |
107 | } | |
108 | /*FALLTHRU*/ | |
109 | default: | |
110 | if (size != -1) { | |
111 | yyerror("can't specify size except with a font or mussym"); | |
112 | } | |
113 | if (modifier != TM_NONE && Curr_stuff_type != ST_PHRASE) { | |
114 | l_yyerror(Curr_filename, yylineno, | |
115 | "can't specify %s except with a font", | |
116 | stuff_modifier(modifier)); | |
117 | } | |
118 | if (Curr_stuff_type == ST_PHRASE && modifier != L_NORMAL && | |
119 | modifier != L_DOTTED && modifier != L_DASHED) { | |
120 | l_yyerror(Curr_filename, yylineno, | |
121 | "only dotted or dashed line type can be specified for phrase"); | |
122 | } | |
123 | break; | |
124 | } | |
125 | ||
126 | if (Curr_stuff_type == ST_OCTAVE) { | |
127 | if (is_tab_range() == YES) { | |
128 | yyerror("octave not allowed on tablature staff"); | |
129 | } | |
130 | else if(place == PL_BETWEEN) { | |
131 | yyerror("octave must be above or below"); | |
132 | place = PL_ABOVE; | |
133 | } | |
134 | } | |
135 | ||
136 | if (Curr_stuff_type == ST_PHRASE && place == PL_BETWEEN) { | |
137 | yyerror("phrase must be above, below, or omitted"); | |
138 | place = PL_ABOVE; | |
139 | } | |
140 | ||
141 | if (dist_usage != SD_NONE) { | |
142 | if (Curr_stuff_type == ST_PEDAL) { | |
143 | yyerror("dist not allowed on pedal"); | |
144 | } | |
145 | else if (Curr_stuff_type == ST_PHRASE) { | |
146 | yyerror("dist not allowed on phrase"); | |
147 | } | |
148 | else if (Curr_stuff_type == ST_MIDI) { | |
149 | yyerror("dist not allowed on midi"); | |
150 | } | |
151 | ||
152 | if (place == PL_BETWEEN) { | |
153 | yyerror("dist not allowed with 'between'"); | |
154 | } | |
155 | } | |
156 | ||
157 | /* Save the modifier value. | |
158 | * Have to set this before calling dflt_place() */ | |
159 | Modifier = modifier; | |
160 | ||
161 | /* fill in default values if user didn't specify */ | |
162 | if (place == PL_UNKNOWN) { | |
163 | place = dflt_place(); | |
164 | } | |
165 | ||
166 | Stuff_size = size; | |
167 | Place = (short) place; | |
168 | ||
169 | /* make sure current list of stuff is empty */ | |
170 | Head_stufflist_p = Tail_stufflist_p = (struct STUFF *) 0; | |
171 | } | |
172 | \f | |
173 | ||
174 | /* return default value for place depending on value of Curr_stuff_type */ | |
175 | ||
176 | int | |
177 | dflt_place() | |
178 | ||
179 | { | |
180 | switch (Curr_stuff_type) { | |
181 | ||
182 | case ST_PEDAL: | |
183 | return(PL_BELOW); | |
184 | ||
185 | case ST_OCTAVE: | |
186 | yyerror("must specify above or below with octave"); | |
187 | /* arbitrarily return above. If we leave it as unknown, | |
188 | * we can get double error messages in some cases */ | |
189 | return(PL_ABOVE); | |
190 | ||
191 | case ST_PHRASE: | |
192 | /* stays unknown at this point */ | |
193 | return(PL_UNKNOWN); | |
194 | ||
195 | default: | |
196 | if (Modifier == TM_ANALYSIS || Modifier == TM_FIGBASS) { | |
197 | return(PL_BELOW); | |
198 | } | |
199 | /* default for everything else is above */ | |
200 | return(PL_ABOVE); | |
201 | } | |
202 | } | |
203 | \f | |
204 | ||
205 | /* Add a space padding to a string (except if is it boxed). | |
206 | * If padding was added, free the passed-in string and return the padded string, | |
207 | * else return the string as is. The incoming string | |
208 | * is expected to already be converted to font/size/string | |
209 | * internal format by this time, although still in input ASCII form. | |
210 | */ | |
211 | ||
212 | char * | |
213 | pad_string(string, modifier) | |
214 | ||
215 | char *string; | |
216 | int modifier; /* TM_* */ | |
217 | ||
218 | { | |
219 | char *padded_string; /* string with 1-space padding at end */ | |
220 | char *str_p; /* walk through padded_string */ | |
221 | int len; /* length of string */ | |
222 | int last_was_backslash; /* YES/NO */ | |
223 | int count_backslashed; /* YES/NO if to count backslashed or | |
224 | * unbackslashed colons */ | |
225 | int colons; /* how many colons found */ | |
226 | int extra; /* how many extra bytes to malloc */ | |
227 | ||
228 | /* Boxed and circled strings don't get any extra padding, | |
229 | * so we can use what we have */ | |
230 | if (string[2] == '\\' && (string[3] == '[' || string[3] == '{')) { | |
231 | return(string); | |
232 | } | |
233 | ||
234 | /* Make a new copy with a space at the end. | |
235 | * But if the string ends in the middle of a pile, | |
236 | * we need to implicitly end the pile before adding the space. | |
237 | * Since the string is still in ASCII form, | |
238 | * we have to count up the number of colons | |
239 | * to see if we are mid-pile. In chord/analysis/figbass | |
240 | * we need to count unbackslashed colon, | |
241 | * otherwise backslashed.*/ | |
242 | count_backslashed = (IS_CHORDLIKE(modifier) ? NO : YES); | |
243 | /* figbass implicitly begins with a pile */ | |
244 | colons = (modifier == TM_FIGBASS ? 1 : 0); | |
245 | last_was_backslash = NO; | |
246 | for (str_p = string + 2; *str_p != '\0'; str_p++) { | |
247 | if (last_was_backslash == YES) { | |
248 | if (*str_p == ':' && count_backslashed == YES) { | |
249 | colons++; | |
250 | } | |
251 | last_was_backslash = NO; | |
252 | } | |
253 | else { | |
254 | if (*str_p == ':' && count_backslashed == NO) { | |
255 | colons++; | |
256 | } | |
257 | last_was_backslash = (*str_p == '\\' ? YES : NO); | |
258 | } | |
259 | } | |
260 | ||
261 | /* If odd number of colons, we are mid-pile. Will need | |
262 | * add extra byte to hold the colon to implicitly end the | |
263 | * pile, and if it needs to be a backslashed colon, | |
264 | * another extra byte for that. */ | |
265 | if (colons & 1) { | |
266 | extra = (count_backslashed == YES ? 2 : 1); | |
267 | } | |
268 | else { | |
269 | extra = 0; | |
270 | } | |
271 | ||
272 | len = strlen(string); | |
273 | ||
274 | /* +2 is for space/null at end */ | |
275 | MALLOCA(char, padded_string, len + 2 + extra); | |
276 | (void) memcpy(padded_string, string, len); | |
277 | str_p = padded_string + len; | |
278 | ||
279 | /* add implicit end-pile if needed */ | |
280 | if (extra == 2) { | |
281 | *str_p++ = '\\'; | |
282 | } | |
283 | if (extra > 0) { | |
284 | *str_p++ = ':'; | |
285 | } | |
286 | ||
287 | /* now add space padding */ | |
288 | *str_p++ = ' '; | |
289 | *str_p = '\0'; | |
290 | FREE(string); | |
291 | return(padded_string); | |
292 | } | |
293 | \f | |
294 | ||
295 | /* check a "stuff" item and add to list */ | |
296 | ||
297 | void | |
298 | add_stuff_item(start_count, start_steps, gracebackup, string, bars, count, | |
299 | dist, dist_usage) | |
300 | ||
301 | double start_count; /* where in measure to start this stuff */ | |
302 | double start_steps; /* offset by this many stepsizes */ | |
303 | int gracebackup; /* how many grace notes to back up from start */ | |
304 | char *string; /* what to print */ | |
305 | int bars; /* how many bar lines to cross with this stuff */ | |
306 | double count; /* how many beats into last measure */ | |
307 | int dist; /* dist for this specific STUFF, to override param */ | |
308 | int dist_usage; /* meaning of dist, SD_* */ | |
309 | ||
310 | { | |
311 | struct STUFF *new_p; /* where to store STUFF */ | |
312 | struct TIL_INFO *til_info_p; /* to save info about til clause */ | |
313 | int len; /* length of stuff text string */ | |
314 | char *padded_string; /* string with 1-space padding at end */ | |
315 | char lch; /* last character of string */ | |
316 | ||
317 | ||
318 | if (bars != 0 || count != 0.0) { | |
319 | /* has a "til" clause. Check if that is valid */ | |
320 | if (Curr_stuff_type == ST_MUSSYM) { | |
321 | if (string == (char *) 0) { | |
322 | yyerror("missing string"); | |
323 | return; | |
324 | } | |
325 | ||
326 | /* not yet changed to internal form, need to compare | |
327 | * in ASCII form */ | |
328 | if ((strcmp(string + 2, "tr") != 0) && | |
329 | (strcmp(string + 2, "\\(tr)") != 0)) { | |
330 | yyerror("til not allowed on mussym except on trills"); | |
331 | } | |
332 | } | |
333 | ||
334 | else if (Curr_stuff_type == ST_PEDAL) { | |
335 | yyerror("til not allowed on pedal"); | |
336 | } | |
337 | ||
338 | else if (Curr_stuff_type == ST_MIDI) { | |
339 | yyerror("til not allowed on midi"); | |
340 | } | |
341 | ||
342 | if (Curr_stuff_type != ST_PHRASE && | |
343 | (Modifier == TM_CHORD || Modifier == TM_ANALYSIS) ) { | |
344 | l_yyerror(Curr_filename, yylineno, | |
345 | "til not allowed with %s", | |
346 | stuff_modifier(Modifier)); | |
347 | } | |
348 | ||
349 | if (bars == 0) { | |
350 | if (count > Score.timenum + 1) { | |
351 | yyerror("'til' value must be <= numerator of time signature + 1"); | |
352 | } | |
353 | ||
354 | if (count < start_count) { | |
355 | yyerror("til value must be >= start value"); | |
356 | } | |
357 | } | |
358 | ||
359 | } | |
360 | else { | |
361 | /* doesn't have a "til" clause. Check if one is required */ | |
362 | if (Curr_stuff_type == ST_CRESC || | |
363 | Curr_stuff_type == ST_DECRESC) { | |
364 | yyerror("til required on cresc/decresc"); | |
365 | } | |
366 | } | |
367 | ||
368 | if (start_count > Score.timenum + 1) { | |
369 | yyerror("beat offset must be <= numerator of time signature + 1"); | |
370 | } | |
371 | ||
372 | if (Curr_stuff_type == ST_CRESC || Curr_stuff_type == ST_DECRESC) { | |
373 | if (string != (char *) 0) { | |
374 | yyerror("string not allowed with cresc/decresc"); | |
375 | } | |
376 | Modifier = TM_DYN; | |
377 | } | |
378 | ||
379 | else if (Curr_stuff_type == ST_PHRASE) { | |
380 | if (string != (char *) 0) { | |
381 | yyerror("string not allowed with phrase"); | |
382 | } | |
383 | } | |
384 | ||
385 | else if (Curr_stuff_type == ST_PEDAL) { | |
386 | if ( (string != (char *) 0) | |
387 | && (strcmp(string + 2, "\\(endped)") != 0) ) { | |
388 | yyerror("pedal string must be either blank or *"); | |
389 | } | |
390 | } | |
391 | ||
392 | else { | |
393 | if (string == (char *) 0) { | |
394 | yyerror("string is required"); | |
395 | return; | |
396 | } | |
397 | } | |
398 | ||
399 | if (gracebackup != 0 && Place == PL_BETWEEN) { | |
400 | yyerror("grace backup not allowed with 'between'"); | |
401 | } | |
402 | ||
403 | /* we can't deal with step offset on phrase marks very well, | |
404 | * so warn and ignore if we get one */ | |
405 | if (start_steps != 0.0 && Curr_stuff_type == ST_PHRASE) { | |
406 | l_warning(Curr_filename, yylineno, "step offset ignored on phrase mark"); | |
407 | start_steps = 0.0; | |
408 | } | |
409 | ||
410 | switch (Curr_stuff_type) { | |
411 | case ST_ROM: | |
412 | case ST_BOLD: | |
413 | case ST_ITAL: | |
414 | case ST_BOLDITAL: | |
415 | /* the text-type stuffs are supposed to have a 1-space padding | |
416 | * at the end of them */ | |
417 | if (bars != 0 || count != 0.0) { | |
418 | /* don't add padding if has wavy or solid line | |
419 | * til clause */ | |
420 | lch = last_char(string); | |
421 | if (lch == '~' || lch == '_') { | |
422 | break; | |
423 | } | |
424 | } | |
425 | string = pad_string(string, Modifier); | |
426 | break; | |
427 | ||
428 | case ST_MUSSYM: | |
429 | /* in mussym, user can specify things without the usual | |
430 | * \(---) convention. Change to include them */ | |
431 | if (string[2] == '\\' && string[3] == '(') { | |
432 | /* if user unnecessarily put in the \(--), leave it */ | |
433 | break; | |
434 | } | |
435 | ||
436 | len = strlen(string + 2); | |
437 | MALLOCA(char, padded_string, len + 6); | |
438 | (void) sprintf(padded_string, "%c%c\\(%s)", FONT_TR, DFLT_SIZE, | |
439 | string + 2); | |
440 | FREE(string); | |
441 | string = padded_string; | |
442 | break; | |
443 | ||
444 | default: | |
445 | break; | |
446 | } | |
447 | ||
448 | /* fill in a new STUFF struct with appropriate info */ | |
449 | new_p = newSTUFF(string, dist, dist_usage, start_count, start_steps, | |
450 | gracebackup, bars, count, | |
451 | Curr_stuff_type, Modifier, Place, Curr_filename, yylineno); | |
452 | ||
453 | /* if bars > 0, need to save away til info for later error | |
454 | * checking */ | |
455 | if (bars > 0) { | |
456 | CALLOC(TIL_INFO, til_info_p, 1); | |
457 | til_info_p->measnum = Measnum + bars; | |
458 | til_info_p->count = count; | |
459 | til_info_p->inputfile = new_p->inputfile; | |
460 | til_info_p->inputlineno = new_p->inputlineno; | |
461 | til_info_p->next = Til_info_list_p; | |
462 | Til_info_list_p = til_info_p; | |
463 | } | |
464 | ||
465 | /* above/between go on the head of the list, below goes on the | |
466 | * tail of the list, so that things come out in the right order. | |
467 | * Midi always goes at the end */ | |
468 | if (Place == PL_BELOW || Curr_stuff_type == ST_MIDI) { | |
469 | /* link onto list tail */ | |
470 | if ( Tail_stufflist_p == (struct STUFF *) 0) { | |
471 | Head_stufflist_p = new_p; | |
472 | } | |
473 | else { | |
474 | Tail_stufflist_p->next = new_p; | |
475 | } | |
476 | Tail_stufflist_p = new_p; | |
477 | } | |
478 | else { | |
479 | /* link onto head of list */ | |
480 | new_p->next = Head_stufflist_p; | |
481 | Head_stufflist_p = new_p; | |
482 | if (Tail_stufflist_p == (struct STUFF *) 0) { | |
483 | Tail_stufflist_p = new_p; | |
484 | } | |
485 | } | |
486 | } | |
487 | \f | |
488 | ||
489 | /* return YES if given string consists entirely of the specific music symbol */ | |
490 | /* the string should be in the internal format of font/size/string */ | |
491 | ||
492 | int | |
493 | string_is_sym(string, sym, symfont) | |
494 | ||
495 | char *string; /* which string to check */ | |
496 | int sym; /* check for this music symbol */ | |
497 | int symfont; /* FONT_MUSIC* */ | |
498 | ||
499 | { | |
500 | int font, size; | |
501 | ||
502 | ||
503 | if (string == (char *) 0) { | |
504 | return(NO); | |
505 | } | |
506 | ||
507 | font = *string++; | |
508 | size = *string++; | |
509 | if (next_str_char(&string, &font, &size) != sym) { | |
510 | return(NO); | |
511 | } | |
512 | if (font != symfont) { | |
513 | return(NO); | |
514 | } | |
515 | if (next_str_char(&string, &font, &size) == '\0') { | |
516 | return(YES); | |
517 | } | |
518 | return (NO); | |
519 | } | |
520 | \f | |
521 | ||
522 | /* connect a list of STUFF to a STAFF. If there is already something on | |
523 | * that STAFF's STUFF list, attach at the end or beginning as appropriate | |
524 | * depending on place. */ | |
525 | ||
526 | void | |
527 | attach_stuff() | |
528 | ||
529 | { | |
530 | struct SVRANGELIST *svr_p; /* to walk through Svrangelist */ | |
531 | struct RANGELIST *r_p; /* to walk through staff range list */ | |
532 | short staffno; | |
533 | ||
534 | ||
535 | debug(4, "attach_stuff"); | |
536 | ||
537 | /* make sure we've got STAFF structs for this measure */ | |
538 | create_staffs(); | |
539 | ||
540 | for (svr_p = Svrangelist_p; svr_p != (struct SVRANGELIST *) 0; | |
541 | svr_p = svr_p->next) { | |
542 | for (r_p = svr_p->stafflist_p; r_p != (struct RANGELIST *) 0; | |
543 | r_p = r_p->next) { | |
544 | ||
545 | for (staffno = r_p->begin; staffno <= r_p->end | |
546 | && staffno <= MAXSTAFFS; staffno++) { | |
547 | do_attach(staffno, r_p->all, svr_p->vnolist_p); | |
548 | ||
549 | if (Place == PL_BETWEEN) { | |
550 | /* between has 2 staffs in its range, | |
551 | * but stuff is only associated | |
552 | * with the top staff */ | |
553 | break; | |
554 | } | |
555 | } | |
556 | } | |
557 | } | |
558 | ||
559 | free_rlists(); | |
560 | ||
561 | /* have made copies of stuff for each staff that gets one, with | |
562 | * the proper font/size etc, so need to free master stufflist copy */ | |
563 | free_stufflist(Head_stufflist_p); | |
564 | } | |
565 | \f | |
566 | ||
567 | /* Attach STUFF for a specific staff. */ | |
568 | ||
569 | static void | |
570 | do_attach(staffno, all, vno_range_p) | |
571 | ||
572 | int staffno; | |
573 | int all; | |
574 | struct RANGELIST *vno_range_p; | |
575 | ||
576 | { | |
577 | struct STAFF *staff_p; /* where to attach STUFF */ | |
578 | struct STUFF *stufflist_p; /* current copy of STUFF list */ | |
579 | ||
580 | ||
581 | if (staffno > Score.staffs) { | |
582 | l_yyerror(Head_stufflist_p->inputfile, | |
583 | Head_stufflist_p->inputlineno, | |
584 | "staff number out of range"); | |
585 | return; | |
586 | } | |
587 | ||
588 | staff_p = Staffmap_p[staffno]->u.staff_p; | |
589 | ||
590 | if (Place == PL_BETWEEN) { | |
591 | if (staffno + 1 > Score.staffs) { | |
592 | /* will have already exclaimed about | |
593 | * this error before, so no need to print message, | |
594 | * but better skip next check */ | |
595 | return; | |
596 | } | |
597 | ||
598 | /* if either staff of a between is invisible, | |
599 | * throw this stuff away */ | |
600 | if (svpath(staffno, VISIBLE)->visible == NO || | |
601 | svpath(staffno + 1, | |
602 | VISIBLE)->visible == NO) { | |
603 | return; | |
604 | } | |
605 | } | |
606 | ||
607 | /* handle MIDI stuff specially */ | |
608 | if (Curr_stuff_type == ST_MIDI) { | |
609 | if (all == YES) { | |
610 | /* need to find top visible staff/voice to attach to */ | |
611 | int s; /* staff number */ | |
612 | int v; /* voice number */ | |
613 | struct RANGELIST range; | |
614 | ||
615 | v = 1; /* avoid bogus "used before set" warning */ | |
616 | for (s = 1; s <= MAXSTAFFS; s++) { | |
617 | if (svpath(s, VISIBLE)->visible == YES) { | |
618 | for (v = 1; v <= MAXVOICES; v++) { | |
619 | if (vvpath(s, v, VISIBLE)->visible == YES) { | |
620 | break; | |
621 | } | |
622 | } | |
623 | if (v <= MAXVOICES) { | |
624 | break; | |
625 | } | |
626 | } | |
627 | } | |
628 | if (s > MAXSTAFFS || v > MAXVOICES) { | |
629 | pfatal("failed to find top visible staff/voice"); | |
630 | } | |
631 | /* make a special RANGELIST for this */ | |
632 | range.begin = range.end = v; | |
633 | range.all = YES; | |
634 | range.next = 0; | |
635 | midi_attach(s, Staffmap_p[s]->u.staff_p, &range, all); | |
636 | } | |
637 | else { | |
638 | midi_attach(staffno, staff_p, vno_range_p, all); | |
639 | } | |
640 | } | |
641 | ||
642 | else { | |
643 | /* make the copy for this staff from master copy */ | |
644 | stufflist_p = clone_stufflist(Head_stufflist_p, staffno, all); | |
645 | ||
646 | if (Curr_stuff_type == ST_PEDAL) { | |
647 | fix_pedal(staffno, stufflist_p); | |
648 | } | |
649 | ||
650 | connect_stuff(staff_p, stufflist_p); | |
651 | } | |
652 | } | |
653 | \f | |
654 | ||
655 | /* attach MIDI stuff. This is slightly different than other stuff because | |
656 | * it can be applied to one or both voices. */ | |
657 | ||
658 | static void | |
659 | midi_attach(staffno, staff_p, vno_range_p, all) | |
660 | ||
661 | int staffno; /* attach to this staff number */ | |
662 | struct STAFF *staff_p; /* attach to this staff struct */ | |
663 | struct RANGELIST *vno_range_p; | |
664 | int all; /* if associated with "all" */ | |
665 | ||
666 | { | |
667 | struct RANGELIST *r_p; /* walk through vno_range_p */ | |
668 | int vno; /* voice number */ | |
669 | struct STUFF *stufflist_p; /* copy of stuff */ | |
670 | struct STUFF *st_p; /* walk through stufflist_p */ | |
671 | short place; | |
672 | ||
673 | ||
674 | /* do for each voice that MIDI stuff applies to */ | |
675 | for (r_p = vno_range_p; r_p != (struct RANGELIST *) 0; r_p = r_p->next) { | |
676 | for (vno = r_p->begin; vno <= r_p->end; vno++) { | |
677 | ||
678 | /* make the copy for this staff from master copy */ | |
679 | stufflist_p = clone_stufflist(Head_stufflist_p, | |
680 | staffno, all); | |
681 | ||
682 | /* fix up place based on voice number */ | |
683 | switch (vno) { | |
684 | case 1: | |
685 | place = PL_ABOVE; | |
686 | break; | |
687 | case 2: | |
688 | place = PL_BELOW; | |
689 | break; | |
690 | case 3: | |
691 | place = PL_BETWEEN; | |
692 | break; | |
693 | default: | |
694 | pfatal("illegal vno for midi"); | |
695 | /*NOTREACHED*/ | |
696 | place = PL_UNKNOWN; /* avoid "used before set" warning */ | |
697 | break; | |
698 | } | |
699 | for (st_p = stufflist_p; st_p != (struct STUFF *) 0; | |
700 | st_p = st_p->next) { | |
701 | st_p->place = place; | |
702 | } | |
703 | ||
704 | connect_stuff(staff_p, stufflist_p); | |
705 | } | |
706 | } | |
707 | } | |
708 | \f | |
709 | ||
710 | /* connect a new stuff list into an existing stuff list. Add below stuff and | |
711 | * MIDI stuff to the end of the list, | |
712 | * and others to beginning of list, but make sure any | |
713 | * "above all" comes after any above non-all, and that any below non-all | |
714 | * comes before any "below all." | |
715 | */ | |
716 | ||
717 | void | |
718 | connect_stuff(staff_p, stufflist_p) | |
719 | ||
720 | struct STAFF *staff_p; /* connect to stuff off of this staff */ | |
721 | struct STUFF *stufflist_p; /* connect this list of stuff */ | |
722 | ||
723 | { | |
724 | struct STUFF *st_p; /* to find link place in STUFF list */ | |
725 | struct STUFF *s_p; /* to find end of stufflist_p */ | |
726 | struct STUFF **ins_p_p; /* where to insert in list */ | |
727 | ||
728 | ||
729 | if (staff_p == (struct STAFF *) 0 || stufflist_p == (struct STUFF *) 0) { | |
730 | return; | |
731 | } | |
732 | ||
733 | if (staff_p->stuff_p == (struct STUFF *) 0) { | |
734 | /* no list before, so attach this one | |
735 | * directly to STAFF */ | |
736 | staff_p->stuff_p = stufflist_p; | |
737 | } | |
738 | ||
739 | else if (Place == PL_BELOW || stufflist_p->stuff_type == ST_MIDI) { | |
740 | /* if this set of stuff isn't associated with | |
741 | * "all", then it goes before any below "all" stuff */ | |
742 | if (stufflist_p->all == NO) { | |
743 | for (ins_p_p = &(staff_p->stuff_p); | |
744 | *ins_p_p != (struct STUFF *) 0; | |
745 | ins_p_p = &((*ins_p_p)->next)) { | |
746 | if ( (*ins_p_p)->place == PL_BELOW && | |
747 | (*ins_p_p)->all == YES) { | |
748 | break; | |
749 | } | |
750 | } | |
751 | /* find end of list to be inserted */ | |
752 | for (s_p = stufflist_p; s_p->next != (struct STUFF *) 0; | |
753 | s_p = s_p->next) { | |
754 | ; | |
755 | } | |
756 | ||
757 | /* insert */ | |
758 | s_p->next = *ins_p_p; | |
759 | *ins_p_p = stufflist_p; | |
760 | } | |
761 | ||
762 | else { | |
763 | /* goes at end of list. find the end */ | |
764 | for (st_p = staff_p->stuff_p; | |
765 | st_p->next != (struct STUFF *)0; | |
766 | st_p = st_p->next) { | |
767 | ; | |
768 | } | |
769 | ||
770 | /* connect in the new list */ | |
771 | st_p->next = stufflist_p; | |
772 | } | |
773 | } | |
774 | else { | |
775 | /* find end of new list */ | |
776 | for (s_p = stufflist_p; | |
777 | s_p->next != (struct STUFF *) 0; | |
778 | s_p = s_p->next) { | |
779 | ; | |
780 | } | |
781 | ||
782 | if (stufflist_p->all == NO) { | |
783 | /* goes at the head of the list */ | |
784 | s_p->next = staff_p->stuff_p; | |
785 | staff_p->stuff_p = stufflist_p; | |
786 | } | |
787 | else { | |
788 | /* goes before any existing above all */ | |
789 | for (ins_p_p = &(staff_p->stuff_p); | |
790 | *ins_p_p != (struct STUFF *) 0; | |
791 | ins_p_p = &((*ins_p_p)->next)) { | |
792 | if ( (*ins_p_p)->place == PL_ABOVE && | |
793 | (*ins_p_p)->all == YES) { | |
794 | break; | |
795 | } | |
796 | } | |
797 | /* find end of list to be inserted */ | |
798 | for (s_p = stufflist_p; s_p->next != (struct STUFF *) 0; | |
799 | s_p = s_p->next) { | |
800 | ; | |
801 | } | |
802 | ||
803 | /* insert */ | |
804 | s_p->next = *ins_p_p; | |
805 | *ins_p_p = stufflist_p; | |
806 | } | |
807 | } | |
808 | } | |
809 | \f | |
810 | ||
811 | /* given a list of STUFF, return a clone of the list */ | |
812 | ||
813 | static struct STUFF * | |
814 | clone_stufflist(stufflist_p, staffno, all) | |
815 | ||
816 | struct STUFF *stufflist_p; /* what stuff to clone */ | |
817 | int staffno; /* which staff, to get proper point size */ | |
818 | int all; /* YES if was "above all" or "below all" */ | |
819 | ||
820 | { | |
821 | struct STUFF *new_p; /* copy of STUFF */ | |
822 | char *newstring; /* copy of text string */ | |
823 | int font; | |
824 | int fontfamily; | |
825 | int size; | |
826 | ||
827 | ||
828 | if (stufflist_p == (struct STUFF *) 0) { | |
829 | return( (struct STUFF *) 0 ); | |
830 | } | |
831 | ||
832 | /* make copy of string with appropriate font and size */ | |
833 | if (stufflist_p->string != (char *) 0) { | |
834 | switch(stufflist_p->stuff_type) { | |
835 | case ST_BOLD: | |
836 | font = FONT_TB; | |
837 | break; | |
838 | case ST_OCTAVE: | |
839 | Stuff_size = DFLT_SIZE; | |
840 | font = FONT_TI; | |
841 | break; | |
842 | case ST_ITAL: | |
843 | font = FONT_TI; | |
844 | break; | |
845 | case ST_BOLDITAL: | |
846 | font = FONT_TX; | |
847 | break; | |
848 | default: | |
849 | font = FONT_TR; | |
850 | break; | |
851 | } | |
852 | ||
853 | /* figure out the proper size if not already determined */ | |
854 | if (Stuff_size < 0) { | |
855 | if (all == YES) { | |
856 | size = Score.size; | |
857 | } | |
858 | else { | |
859 | size = svpath(staffno, SIZE)->size; | |
860 | } | |
861 | } | |
862 | else { | |
863 | size = Stuff_size; | |
864 | } | |
865 | ||
866 | /* determine fontfamily and font if not already known */ | |
867 | if (Curr_family == FAMILY_DFLT) { | |
868 | if (all == YES) { | |
869 | fontfamily = Score.fontfamily; | |
870 | } | |
871 | else { | |
872 | fontfamily = svpath(staffno, FONTFAMILY)-> | |
873 | fontfamily; | |
874 | } | |
875 | } | |
876 | else { | |
877 | fontfamily = Curr_family; | |
878 | } | |
879 | ||
880 | /* clone text string */ | |
881 | newstring = copy_string(stufflist_p->string + 2, font, size); | |
882 | if (IS_CHORDLIKE(Modifier)) { | |
883 | newstring = modify_chstr(newstring, Modifier); | |
884 | } | |
885 | fix_string(newstring, fontfamily + font, size, | |
886 | stufflist_p->inputfile, stufflist_p->inputlineno); | |
887 | if (Modifier == TM_FIGBASS || Modifier == TM_ANALYSIS) { | |
888 | newstring = acc_trans(newstring); | |
889 | } | |
890 | } | |
891 | else { | |
892 | newstring = (char *) 0; | |
893 | } | |
894 | ||
895 | /* create and fill in clone of stuff, then return it */ | |
896 | new_p = newSTUFF(newstring, stufflist_p->dist, | |
897 | stufflist_p->dist_usage, | |
898 | stufflist_p->start.count, | |
899 | stufflist_p->start.steps, | |
900 | stufflist_p->gracebackup, | |
901 | stufflist_p->end.bars, stufflist_p->end.count, | |
902 | stufflist_p->stuff_type, stufflist_p->modifier, | |
903 | stufflist_p->place, stufflist_p->inputfile, | |
904 | stufflist_p->inputlineno); | |
905 | new_p->all = (short) all; | |
906 | new_p->next = clone_stufflist(stufflist_p->next, staffno, all); | |
907 | return(new_p); | |
908 | } | |
909 | \f | |
910 | ||
911 | /* allocate a STUFF and fill in all the values given. Initialize carry fields | |
912 | * and "all" to NO. Leave coordinates and next link as 0. | |
913 | * Note that the string pointer | |
914 | * is copied; it does not make a copy of the string itself, so never call this | |
915 | * function more than once with the same string--make a copy. */ | |
916 | ||
917 | struct STUFF * | |
918 | newSTUFF(string, dist, dist_usage, start_count, start_steps, gracebackup, bars, count, | |
919 | stuff_type, modifier, place, inputfile, inputlineno) | |
920 | ||
921 | char *string; /* text string of stuff */ | |
922 | int dist; /* dist for this STUFF to override dist parameter */ | |
923 | int dist_usage; /* meaning of dist, SD_* */ | |
924 | double start_count; /* count at which to begin stuff */ | |
925 | double start_steps; /* offset by this many steps */ | |
926 | int gracebackup; /* how many grace notes to back up from start */ | |
927 | int bars; /* bars in "til" clasue */ | |
928 | double count; /* counts in "til" clause */ | |
929 | int stuff_type; /* ST_* */ | |
930 | int modifier; /* TM_* */ | |
931 | int place; /* PL_* */ | |
932 | char *inputfile; /* which file stuff was defined in */ | |
933 | int inputlineno; /* where stuff was defined in input file */ | |
934 | ||
935 | { | |
936 | struct STUFF *new_p; /* the new STUFF to fill in */ | |
937 | ||
938 | ||
939 | CALLOC(STUFF, new_p, 1); | |
940 | new_p->string = string; | |
941 | new_p->start.count = start_count; | |
942 | new_p->start.steps = start_steps; | |
943 | new_p->gracebackup = (short) gracebackup; | |
944 | new_p->dist = (short) dist; | |
945 | new_p->dist_usage = (short) dist_usage; | |
946 | new_p->end.bars = (short) bars; | |
947 | new_p->end.count = count; | |
948 | new_p->stuff_type = (short) stuff_type; | |
949 | new_p->modifier = (short) modifier; | |
950 | new_p->place = (short) place; | |
951 | new_p->carryin = new_p->carryout = new_p->all = NO; | |
952 | new_p->costuff_p = 0; | |
953 | new_p->inputfile = inputfile; | |
954 | new_p->inputlineno = (short) inputlineno; | |
955 | ||
956 | return(new_p); | |
957 | } | |
958 | \f | |
959 | ||
960 | /* recursively free up a stufflist and any strings hanging off of it */ | |
961 | ||
962 | static void | |
963 | free_stufflist(stuff_p) | |
964 | ||
965 | struct STUFF *stuff_p; | |
966 | ||
967 | { | |
968 | if (stuff_p == (struct STUFF *) 0 ) { | |
969 | return; | |
970 | } | |
971 | ||
972 | free_stufflist(stuff_p->next); | |
973 | if (stuff_p->string != (char *) 0) { | |
974 | FREE(stuff_p->string); | |
975 | } | |
976 | FREE(stuff_p); | |
977 | } | |
978 | \f | |
979 | ||
980 | /* at each bar line, see if there are any "til" clauses that are supposed | |
981 | * to end in this measure. If so, make sure they end within the time | |
982 | * signature for this measure. */ | |
983 | ||
984 | void | |
985 | meas_stuff_chk() | |
986 | ||
987 | { | |
988 | struct TIL_INFO *til_info_p; /* to index thru list */ | |
989 | struct TIL_INFO **del_place_p_p; /* for deleting from list */ | |
990 | struct TIL_INFO *one2free_p; /* pointer to which element | |
991 | * to free */ | |
992 | ||
993 | debug(2, "meas_chk_stuff"); | |
994 | ||
995 | /* update measure number to conpensate for any multirests */ | |
996 | Measnum += Multi_adjust; | |
997 | Multi_adjust = 0; | |
998 | ||
999 | /* go through list of in-progress til clauses */ | |
1000 | for (til_info_p = Til_info_list_p, del_place_p_p = &Til_info_list_p; | |
1001 | til_info_p != (struct TIL_INFO *) 0; ) { | |
1002 | ||
1003 | if (til_info_p->measnum == Measnum) { | |
1004 | ||
1005 | /* at measure where this til clause ends */ | |
1006 | /* check if within time signature */ | |
1007 | if (til_info_p->count > Score.timenum + 1.0) { | |
1008 | l_yyerror(til_info_p->inputfile, | |
1009 | til_info_p->inputlineno, | |
1010 | "beats in 'til' clause must be <= numerator of time signature + 1 of the measure in which the 'til' clause ends (i.e., <= %d)", | |
1011 | Score.timenum); | |
1012 | } | |
1013 | ||
1014 | /* this one has been taken care of: delete from list */ | |
1015 | *del_place_p_p = til_info_p->next; | |
1016 | one2free_p = til_info_p; | |
1017 | } | |
1018 | else if (til_info_p->measnum < Measnum) { | |
1019 | /* must have ended inside a multirest, so delete | |
1020 | * from list */ | |
1021 | *del_place_p_p = til_info_p->next; | |
1022 | one2free_p = til_info_p; | |
1023 | } | |
1024 | else { | |
1025 | /* this one stays on the list for now, so move pointer | |
1026 | * to where to potentially delete to next element */ | |
1027 | del_place_p_p = &(til_info_p->next); | |
1028 | one2free_p = (struct TIL_INFO *) 0; | |
1029 | } | |
1030 | ||
1031 | /* have to move to next element | |
1032 | * before freeing the current one */ | |
1033 | til_info_p = til_info_p->next; | |
1034 | ||
1035 | if (one2free_p != (struct TIL_INFO *) 0) { | |
1036 | FREE(one2free_p); | |
1037 | } | |
1038 | } | |
1039 | ||
1040 | /* update number of measures. */ | |
1041 | Measnum++; | |
1042 | ||
1043 | /* make sure pedal marks are in proper order */ | |
1044 | ped_order_chk(); | |
1045 | } | |
1046 | \f | |
1047 | ||
1048 | /* adjust number of measures to account for multirests. Called when there is | |
1049 | * a multirest. Saved the number of measures in the multirest (minus 1 since | |
1050 | * the barline at the end will count for one measure) */ | |
1051 | ||
1052 | void | |
1053 | multi_stuff(nmeas) | |
1054 | ||
1055 | int nmeas; /* number of measures in multirest */ | |
1056 | ||
1057 | { | |
1058 | /* subtract 1 to account for the fact that at the bar line at the | |
1059 | * end of the multirest we will peg the measure counter */ | |
1060 | Multi_adjust = nmeas - 1; | |
1061 | } | |
1062 | \f | |
1063 | ||
1064 | /* handle pedal going into endings. When we hit a first ending, save the | |
1065 | * state of the pedal for all staffs. On subsequent endings in the set, | |
1066 | * reset the pedal state to what it was at the beginning of the first ending. | |
1067 | * At the endending, go back to normal operation. This is similar to | |
1068 | * the saveped() function used at print time. */ | |
1069 | ||
1070 | void | |
1071 | ped_endings(endingloc) | |
1072 | ||
1073 | int endingloc; /* STARTITEM, INITEM, etc */ | |
1074 | ||
1075 | { | |
1076 | register int s; /* staff index */ | |
1077 | ||
1078 | ||
1079 | if (endingloc == STARTITEM) { | |
1080 | if (Ped_snapshot[0] == YES) { | |
1081 | ||
1082 | /* starting 2nd ending: restore pedal state as it was | |
1083 | * at beginning of first ending */ | |
1084 | for (s = 1; s <= MAXSTAFFS; s++) { | |
1085 | Pedal_state[s] = Ped_snapshot[s]; | |
1086 | } | |
1087 | } | |
1088 | ||
1089 | else { | |
1090 | /* starting a set of endings, | |
1091 | * need to save pedal state at this | |
1092 | * point so we can carry it into subsequent endings */ | |
1093 | for (s = 1; s <= Score.staffs; s++) { | |
1094 | Ped_snapshot[s] = Pedal_state[s]; | |
1095 | } | |
1096 | /* make sure any remaining staffs are set to pedal off, | |
1097 | * in case user increases the number of staffs | |
1098 | * during the endings... */ | |
1099 | for ( ; s <= MAXSTAFFS; s++) { | |
1100 | Ped_snapshot[s] = NO; | |
1101 | } | |
1102 | ||
1103 | /* mark that we now have a snapshot */ | |
1104 | Ped_snapshot[0] = YES; | |
1105 | } | |
1106 | } | |
1107 | ||
1108 | else if (endingloc == ENDITEM) { | |
1109 | /* at end of endings, discard snapshot of pedal states */ | |
1110 | Ped_snapshot[0] = NO; | |
1111 | } | |
1112 | } | |
1113 | \f | |
1114 | ||
1115 | /* When all input has been processed, or when changing the number | |
1116 | * of staffs, we better not have any 'til' clauses | |
1117 | * still unfinished. If we do, print a warning message. */ | |
1118 | ||
1119 | void | |
1120 | chk4dangling_til_clauses(boundary_desc) | |
1121 | ||
1122 | char *boundary_desc; /* "the end of the song" or | |
1123 | * "a change in number of staffs" */ | |
1124 | ||
1125 | { | |
1126 | struct TIL_INFO *til_info_p; | |
1127 | ||
1128 | ||
1129 | debug(2, "chk4dangling_til_clauses"); | |
1130 | ||
1131 | /* Go through the whole list of remaining til clauses, | |
1132 | * and print a warning message for each. */ | |
1133 | for (til_info_p = Til_info_list_p; til_info_p != (struct TIL_INFO *) 0; | |
1134 | til_info_p = til_info_p->next) { | |
1135 | ||
1136 | /* If right on the boundary or spills over only a very tiny | |
1137 | * amount, don't bother to complain */ | |
1138 | if (til_info_p->measnum - Measnum == 0 | |
1139 | && til_info_p->count < .001) { | |
1140 | continue; | |
1141 | } | |
1142 | ||
1143 | l_warning(til_info_p->inputfile, til_info_p->inputlineno, | |
1144 | "'til' clause extends beyond %s by %dm + %.3f", | |
1145 | boundary_desc, til_info_p->measnum - Measnum, | |
1146 | til_info_p->count); | |
1147 | } | |
1148 | ||
1149 | /* mop up. */ | |
1150 | free_tils(Til_info_list_p); | |
1151 | Til_info_list_p = (struct TIL_INFO *) 0; | |
1152 | } | |
1153 | \f | |
1154 | ||
1155 | /* recursively free a list of TIL_INFO structs */ | |
1156 | ||
1157 | static void | |
1158 | free_tils(til_p) | |
1159 | ||
1160 | struct TIL_INFO *til_p; /* free this list */ | |
1161 | ||
1162 | { | |
1163 | if (til_p == (struct TIL_INFO *) 0) { | |
1164 | return; | |
1165 | } | |
1166 | ||
1167 | free_tils(til_p->next); | |
1168 | FREE(til_p); | |
1169 | } | |
1170 | \f | |
1171 | ||
1172 | /* user only has to specify when pedal marks end. We deduce from current | |
1173 | * pedal state whether a pedal mark is begin or up/down. This gets called | |
1174 | * whenever we have a list of pedal STUFFs. Later we enforce that pedal | |
1175 | * marks are put in in ascending order only, so that if user enters more | |
1176 | * than one pedal line for the same staff, that will be handled properly. */ | |
1177 | ||
1178 | static void | |
1179 | fix_pedal(staffno, stuff_p) | |
1180 | ||
1181 | int staffno; /* pedal is for this staff */ | |
1182 | struct STUFF *stuff_p; /* list of pedal mark info */ | |
1183 | ||
1184 | { | |
1185 | /* walk through list of pedal marks */ | |
1186 | for ( ; stuff_p != (struct STUFF *) 0; stuff_p = stuff_p->next) { | |
1187 | ||
1188 | if (stuff_p->string == (char *) 0) { | |
1189 | /* no star, so have to deduce state */ | |
1190 | ||
1191 | if (Pedal_state[staffno] == NO) { | |
1192 | /* pedal currently off, so begin pedal */ | |
1193 | Pedal_state[staffno] = YES; | |
1194 | stuff_p->string = copy_string(Ped_begin_str + 2, | |
1195 | (int) Ped_begin_str[0], | |
1196 | (int) Ped_begin_str[1]); | |
1197 | } | |
1198 | else { | |
1199 | /* pedal currently down, so pedal up/down */ | |
1200 | stuff_p->string = copy_string(Ped_up_down_str + 2, | |
1201 | (int) Ped_up_down_str[0], | |
1202 | (int) Ped_up_down_str[1]); | |
1203 | } | |
1204 | } | |
1205 | ||
1206 | else if (Pedal_state[staffno] == NO) { | |
1207 | yyerror("can't end pedal -- none in progress"); | |
1208 | } | |
1209 | ||
1210 | else { | |
1211 | /* user gave star, so end pedal */ | |
1212 | Pedal_state[staffno] = NO; | |
1213 | } | |
1214 | } | |
1215 | } | |
1216 | \f | |
1217 | ||
1218 | /* reset pedal states for all staffs. This should be called at init time | |
1219 | * and at any time when the number of staffs changes. This function also | |
1220 | * initializes the Ped_begin_str and Ped_up_down_str. */ | |
1221 | ||
1222 | void | |
1223 | reset_ped_state() | |
1224 | ||
1225 | { | |
1226 | static int first_time = YES; /* flag if function called before */ | |
1227 | register int s; /* index through staffs */ | |
1228 | ||
1229 | ||
1230 | /* mark pedal off for all staffs */ | |
1231 | for (s = 1; s <= Score.staffs; s++) { | |
1232 | Pedal_state[s] = NO; | |
1233 | } | |
1234 | Ped_snapshot[0] = NO; | |
1235 | ||
1236 | /* the first time this function is called, initialize the strings | |
1237 | * for pedal begin and pedal end. We just have one copy of these | |
1238 | * and then make as many copies from these as necessary */ | |
1239 | if (first_time == YES) { | |
1240 | first_time = NO; | |
1241 | Ped_begin_str = copy_string("\\(begped)", FONT_MUSIC, | |
1242 | DFLT_SIZE); | |
1243 | Ped_up_down_str = copy_string("\\(pedal)", FONT_MUSIC, | |
1244 | DFLT_SIZE); | |
1245 | fix_string(Ped_begin_str, FONT_MUSIC, DFLT_SIZE, | |
1246 | Curr_filename, -1); | |
1247 | fix_string(Ped_up_down_str, FONT_MUSIC, DFLT_SIZE, | |
1248 | Curr_filename, -1); | |
1249 | } | |
1250 | } | |
1251 | \f | |
1252 | ||
1253 | /* fill in rehearsal mark string. This doesn't go in a STUFF, but it's | |
1254 | * sort of like stuff and there didn't seem to be any more appropriate file for | |
1255 | * this function */ | |
1256 | ||
1257 | ||
1258 | static int Reh_let = 0; /* current value of rehearsal letter. 0 == "A", | |
1259 | * 25 == "Z", 26 == "AA", etc to 701 == "ZZ" */ | |
1260 | static int Reh_num = 1; /* current value of rehearsal number */ | |
1261 | ||
1262 | ||
1263 | void | |
1264 | set_reh_string(bar_p, fontfamily, font, size, string) | |
1265 | ||
1266 | struct BAR *bar_p; /* which bar gets the rehearsal mark */ | |
1267 | int fontfamily; /* what font family to use, or FAMILY_DFLT | |
1268 | * if to use current default */ | |
1269 | int font; /* what font to use, or FONT_UNKNOWN if to use the | |
1270 | * current default font */ | |
1271 | int size; /* font size to use, or -1 if to use current default */ | |
1272 | char *string; /* string for rehearsal mark */ | |
1273 | ||
1274 | { | |
1275 | char reh_str[12]; /* temporary buff for string version of | |
1276 | * rehearsal number or letter */ | |
1277 | static int reh_size = DFLT_SIZE; /* size to use for reh marks */ | |
1278 | static int reh_family = FAMILY_DFLT; /* font family to use */ | |
1279 | static int reh_font = FONT_TB; /* font to use */ | |
1280 | ||
1281 | ||
1282 | /* if first time through, init the font family to the score family */ | |
1283 | if (reh_family == FAMILY_DFLT) { | |
1284 | reh_family = Score.fontfamily; | |
1285 | } | |
1286 | ||
1287 | /* if user specified a new size, save that */ | |
1288 | if (size != -1) { | |
1289 | if (size > 100) { | |
1290 | yyerror("reh mark size too large"); | |
1291 | return; | |
1292 | } | |
1293 | else { | |
1294 | reh_size = size; | |
1295 | } | |
1296 | } | |
1297 | ||
1298 | /* if user specified new font or font family, save that */ | |
1299 | if (font != FONT_UNKNOWN) { | |
1300 | reh_font = font; | |
1301 | } | |
1302 | if (fontfamily != FAMILY_DFLT) { | |
1303 | reh_family = fontfamily; | |
1304 | } | |
1305 | ||
1306 | switch(bar_p->reh_type) { | |
1307 | ||
1308 | case REH_NUM: | |
1309 | /* get string version of current rehearsal number, and | |
1310 | * incrment it */ | |
1311 | bar_p->reh_string = copy_string(num2str(Reh_num++) + 2, | |
1312 | reh_family + reh_font, reh_size); | |
1313 | break; | |
1314 | ||
1315 | case REH_LET: | |
1316 | /* Get string version of current rehearsal letter. | |
1317 | * Start with A-Z, then AA, AB, AC, ... BA, BB, ... up to ZZ. | |
1318 | */ | |
1319 | if (Reh_let < 26) { | |
1320 | /* 1-letter long mark */ | |
1321 | (void) sprintf(reh_str, "%c", Reh_let + 'A'); | |
1322 | } | |
1323 | else if (Reh_let < 27 * 26) { | |
1324 | /* 2-letter long mark */ | |
1325 | (void) sprintf(reh_str, "%c%c", | |
1326 | (Reh_let / 26) + 'A' - 1, (Reh_let % 26) + 'A'); | |
1327 | } | |
1328 | else { | |
1329 | ufatal("too many rehearsal letters!"); | |
1330 | } | |
1331 | bar_p->reh_string = copy_string(reh_str, | |
1332 | reh_family + reh_font, reh_size); | |
1333 | /* increment for next time around */ | |
1334 | Reh_let++; | |
1335 | break; | |
1336 | ||
1337 | case REH_MNUM: | |
1338 | /* get string version of current measure number */ | |
1339 | bar_p->reh_string = copy_string(num2str(Meas_num) + 2, | |
1340 | reh_family + reh_font, reh_size); | |
1341 | break; | |
1342 | ||
1343 | case REH_STRING: | |
1344 | /* user-specified string */ | |
1345 | bar_p->reh_string = fix_string(string, | |
1346 | reh_family + reh_font, reh_size, | |
1347 | Curr_filename, yylineno); | |
1348 | break; | |
1349 | ||
1350 | case REH_NONE: | |
1351 | break; | |
1352 | ||
1353 | default: | |
1354 | pfatal("set_reh_string passed bad value"); | |
1355 | break; | |
1356 | } | |
1357 | } | |
1358 | \f | |
1359 | ||
1360 | /* Set rehearsal letter or number to user-specified value. | |
1361 | * If the current bar has a rehearsal mark of the type being changed, | |
1362 | * also replace its current mark with the changed one. This allows user | |
1363 | * to say either | |
1364 | * reh num num=5 | |
1365 | * or | |
1366 | * num=5 reh num | |
1367 | * and get the same results, which is consistent with how mnum= setting | |
1368 | * had worked. | |
1369 | */ | |
1370 | ||
1371 | void | |
1372 | init_reh(rehnumber, rehletter, mainbar_p) | |
1373 | ||
1374 | int rehnumber; /* New value for Reh_num or negative if setting Reh_let */ | |
1375 | char *rehletter; /* "A" to "ZZ" or null if setting number */ | |
1376 | struct MAINLL *mainbar_p; /* points to the current BAR */ | |
1377 | ||
1378 | { | |
1379 | struct BAR *bar_p; | |
1380 | char *oldstr; /* previous reh_string */ | |
1381 | ||
1382 | if (mainbar_p == 0 || mainbar_p->str != S_BAR) { | |
1383 | pfatal("bad mainbar_p passed to init_reh"); | |
1384 | } | |
1385 | bar_p = mainbar_p->u.bar_p; | |
1386 | oldstr = bar_p->reh_string; | |
1387 | ||
1388 | if (rehnumber >= 0) { | |
1389 | Reh_num = rehnumber; | |
1390 | /* If this bar has a rehearsal number on this bar, | |
1391 | * replace it, and free the old one. */ | |
1392 | if (bar_p->reh_type == REH_NUM) { | |
1393 | set_reh_string(bar_p, FAMILY_DFLT, FONT_UNKNOWN, -1, | |
1394 | (char *) 0); | |
1395 | FREE(oldstr); | |
1396 | } | |
1397 | } | |
1398 | ||
1399 | if (rehletter != 0) { | |
1400 | /* Letter is stored internally as a number, | |
1401 | * which is then converted, so we have to convert in reverse. | |
1402 | * We only allow "A" through "ZZ" */ | |
1403 | if (isupper(rehletter[0]) && rehletter[1] == '\0') { | |
1404 | Reh_let = rehletter[0] - 'A'; | |
1405 | } | |
1406 | else if (isupper(rehletter[0]) && isupper(rehletter[1]) | |
1407 | && rehletter[2] == '\0') { | |
1408 | Reh_let = 26 + (rehletter[1] - 'A') | |
1409 | + (rehletter[0] - 'A') * 26; | |
1410 | } | |
1411 | else { | |
1412 | yyerror("rehearsal letter setting must be \"A\" through \"ZZ\""); | |
1413 | return; | |
1414 | } | |
1415 | /* If this bar has a rehearsal letter on this bar, | |
1416 | * replace it, and free the old one. */ | |
1417 | if (bar_p->reh_type == REH_LET) { | |
1418 | set_reh_string(bar_p, FAMILY_DFLT, FONT_UNKNOWN, -1, | |
1419 | (char *) 0); | |
1420 | FREE(oldstr); | |
1421 | } | |
1422 | } | |
1423 | } | |
1424 | \f | |
1425 | ||
1426 | /* go through all stuff lists and verify that pedal marks are given in | |
1427 | * ascending order. If not, error. Some code in both parse and | |
1428 | * placement phases requires that pedal marks be in order. */ | |
1429 | ||
1430 | static void | |
1431 | ped_order_chk() | |
1432 | ||
1433 | { | |
1434 | int staffno; | |
1435 | struct STUFF *stuff_p; /* walk through stuff list */ | |
1436 | float last_ped_count; /* count where last pedal occurred */ | |
1437 | int last_backup; /* gracebackup of last pedal */ | |
1438 | ||
1439 | ||
1440 | /* check every staff */ | |
1441 | for (staffno = 1; staffno <= Score.staffs; staffno++) { | |
1442 | ||
1443 | /* initialize for current staff */ | |
1444 | last_ped_count = -1.0; | |
1445 | last_backup = 0; | |
1446 | ||
1447 | /* go through stuff list for current staff, looking for pedal */ | |
1448 | for (stuff_p = Staffmap_p[staffno]->u.staff_p->stuff_p; | |
1449 | stuff_p != (struct STUFF *) 0; | |
1450 | stuff_p = stuff_p->next) { | |
1451 | if (stuff_p->stuff_type == ST_PEDAL) { | |
1452 | ||
1453 | /* found a pedal. Make sure it is later than | |
1454 | * the previous pedal */ | |
1455 | if (stuff_p->start.count < last_ped_count || | |
1456 | (stuff_p->start.count | |
1457 | == last_ped_count | |
1458 | && stuff_p->gracebackup | |
1459 | > last_backup) ) { | |
1460 | l_yyerror(stuff_p->inputfile, | |
1461 | stuff_p->inputlineno, | |
1462 | "pedal must be specified in ascending order"); | |
1463 | /* no need to print error more than | |
1464 | * once if multiple errors */ | |
1465 | continue; | |
1466 | } | |
1467 | ||
1468 | /* keep track of where this pedal is, for | |
1469 | * comparing with the next one */ | |
1470 | last_ped_count = stuff_p->start.count; | |
1471 | last_backup = stuff_p->gracebackup; | |
1472 | } | |
1473 | } | |
1474 | } | |
1475 | } | |
1476 | \f | |
1477 | ||
1478 | /* Translate STUFF text modifier to a printable string. */ | |
1479 | ||
1480 | char * | |
1481 | stuff_modifier(modifier) | |
1482 | ||
1483 | int modifier; | |
1484 | ||
1485 | { | |
1486 | switch (modifier) { | |
1487 | ||
1488 | case TM_CHORD: | |
1489 | return("chord"); | |
1490 | case TM_ANALYSIS: | |
1491 | return("analysis"); | |
1492 | case TM_FIGBASS: | |
1493 | return("figbass"); | |
1494 | case TM_DYN: | |
1495 | return("dyn"); | |
1496 | case TM_NONE: | |
1497 | return("(no modifier)"); | |
1498 | default: | |
1499 | return("(invalid modifier)"); | |
1500 | } | |
1501 | } |