Commit | Line | Data |
---|---|---|
69695f33 MW |
1 | /* Copyright (c) 2003 by Arkkra Enterprises. */ |
2 | /* All rights reserved. */ | |
3 | ||
4 | /* This file contains parse-time functions related to TIMEDSSVs. | |
5 | * These are used to specify mid-measure parameter changes, | |
6 | * like change of clef. | |
7 | */ | |
8 | ||
9 | #include "defines.h" | |
10 | #include "structs.h" | |
11 | #include "globals.h" | |
12 | ||
13 | /* Mid-measure SSV's eventually get attached to the BAR, but we need | |
14 | * to point to the list temporarily while the BAR doesn't exist yet. */ | |
15 | static struct TIMEDSSV *Timedssv_p; | |
16 | ||
17 | /* When there is more than one TIMEDSSV at the same moment in time, | |
18 | * we use user input order, so we add items to the end of the list. | |
19 | * This keeps track of where to put the next one. | |
20 | */ | |
21 | static struct TIMEDSSV **Timed_tail_p_p = &Timedssv_p; | |
22 | ||
23 | static struct TIMEDSSV *tssv_new P((int context)); | |
24 | \f | |
25 | ||
26 | /* Allocate and initalize a TIMEDSSV for each staff/voice being defined, | |
27 | * and return pointer to the first one. | |
28 | */ | |
29 | ||
30 | struct TIMEDSSV * | |
31 | tssv_create(context) | |
32 | ||
33 | int context; | |
34 | ||
35 | { | |
36 | struct SVRANGELIST *svr_p; /* list of staffs/voices being entered */ | |
37 | struct RANGELIST *srange_p; /* list of staffs being entered */ | |
38 | struct RANGELIST *vrange_p; /* list of voices being entered */ | |
39 | struct TIMEDSSV *new_p; | |
40 | struct TIMEDSSV *first_p = 0; /* first one created */ | |
41 | int s; /* staff number */ | |
42 | int v; /* voice number */ | |
43 | ||
44 | /* Create as many new TIMEDSSVs as needed, based on the current list | |
45 | * of staffs/voices being defined, | |
46 | * link them onto the temporary list for the current measure. | |
47 | */ | |
48 | ||
49 | switch (context) { | |
50 | case C_SCORE: | |
51 | first_p = new_p = tssv_new(context); | |
52 | break; | |
53 | case C_STAFF: | |
54 | case C_VOICE: | |
55 | for (svr_p = Svrangelist_p; svr_p != 0; svr_p = svr_p->next) { | |
56 | for (srange_p = svr_p->stafflist_p; srange_p != 0; | |
57 | srange_p = srange_p->next) { | |
58 | for (s = srange_p->begin; s <= srange_p->end; s++) { | |
59 | if (context == C_STAFF) { | |
60 | new_p = tssv_new(context); | |
61 | if (first_p == 0) { | |
62 | first_p = new_p; | |
63 | } | |
64 | new_p->ssv.staffno = s; | |
65 | } | |
66 | else { | |
67 | for (vrange_p = svr_p->vnolist_p; | |
68 | vrange_p != 0; | |
69 | vrange_p = vrange_p->next) { | |
70 | for (v = vrange_p->begin; | |
71 | v <= vrange_p->end; | |
72 | v++) { | |
73 | new_p = tssv_new(context); | |
74 | if (first_p == 0) { | |
75 | first_p = new_p; | |
76 | } | |
77 | new_p->ssv.staffno = s; | |
78 | new_p->ssv.voiceno = v; | |
79 | } | |
80 | } | |
81 | } | |
82 | } | |
83 | } | |
84 | } | |
85 | break; | |
86 | default: | |
87 | pfatal("invalid context %d when creating TIMEDSSV", context); | |
88 | /*NOTREACHED*/ | |
89 | break; | |
90 | } | |
91 | return(first_p); | |
92 | } | |
93 | \f | |
94 | ||
95 | /* Create a single TIMEDSSV and link it onto the list */ | |
96 | ||
97 | static struct TIMEDSSV * | |
98 | tssv_new(context) | |
99 | ||
100 | int context; | |
101 | ||
102 | { | |
103 | struct TIMEDSSV *curr_tssv_p; | |
104 | ||
105 | CALLOC(TIMEDSSV, curr_tssv_p, 1); | |
106 | zapssv( & (curr_tssv_p->ssv) ); | |
107 | curr_tssv_p->ssv.context = context; | |
108 | curr_tssv_p->grpsyl_p = 0; | |
109 | curr_tssv_p->time_off.n = -1; | |
110 | curr_tssv_p->time_off.d = 1; | |
111 | ||
112 | curr_tssv_p->next = 0; | |
113 | *Timed_tail_p_p = curr_tssv_p; | |
114 | Timed_tail_p_p = &(curr_tssv_p->next); | |
115 | ||
116 | return(curr_tssv_p); | |
117 | } | |
118 | \f | |
119 | ||
120 | /* Save a parameter setting in the given TIMEDSSV. We only support a very | |
121 | * limited list of parameters that can be changed mid-measure, | |
122 | * so this checks for valid ones. | |
123 | */ | |
124 | ||
125 | void | |
126 | tssv_update(timedssv_p, param, value) | |
127 | ||
128 | struct TIMEDSSV *timedssv_p; | |
129 | int param; | |
130 | int value; | |
131 | ||
132 | { | |
133 | /* Could be multiple staffs/voices, so do them all */ | |
134 | for ( ; timedssv_p != 0; timedssv_p = timedssv_p->next) { | |
135 | switch (param) { | |
136 | case CLEF: | |
137 | timedssv_p->ssv.clef = value; | |
138 | break; | |
139 | case RELEASE: | |
140 | if (rangecheck(value, MINRELEASE, MAXRELEASE, | |
141 | "mid-measure release change") == YES) { | |
142 | timedssv_p->ssv.release = value; | |
143 | } | |
144 | break; | |
145 | case DEFOCT: | |
146 | if (rangecheck(value, MINOCTAVE, MAXOCTAVE, | |
147 | "mid-measure defoct change") == YES) { | |
148 | timedssv_p->ssv.defoct = value; | |
149 | } | |
150 | break; | |
151 | ||
152 | default: | |
153 | yyerror("only clef, defoct, and release parameters can be changed mid-measure"); | |
154 | return; | |
155 | } | |
156 | if (timedssv_p->ssv.used[param] == YES) { | |
157 | warning("multiple changes of the same parameter; last used"); | |
158 | } | |
159 | timedssv_p->ssv.used[param] = YES; | |
160 | } | |
161 | } | |
162 | \f | |
163 | ||
164 | /* Associate grpsyl with TIMEDSSV. This should be called at the end of | |
165 | * parsing of a grpsyl, in case it has at least one timed ssv. */ | |
166 | ||
167 | void | |
168 | tssv_setgrpsyl(gs_p) | |
169 | ||
170 | struct GRPSYL *gs_p; | |
171 | ||
172 | { | |
173 | struct TIMEDSSV *tssv_p; | |
174 | ||
175 | /* User could input multiple << >> things, and each gets their | |
176 | * own TIMEDSSV, so we need to associate this grpsyl with | |
177 | * all that don't yet have one. */ | |
178 | for (tssv_p = Timedssv_p; tssv_p != 0; tssv_p = tssv_p->next) { | |
179 | if (tssv_p->grpsyl_p == 0) { | |
180 | tssv_p->grpsyl_p = gs_p; | |
181 | ||
182 | /* Do some error checks */ | |
183 | if (tssv_p->ssv.used[CLEF] == YES) { | |
184 | if (tssv_p->ssv.context == C_STAFF | |
185 | && is_tab_staff(tssv_p->ssv.staffno)) { | |
186 | yyerror("can't change clef of tab staff"); | |
187 | } | |
188 | if (tssv_p->ssv.context == C_VOICE) { | |
189 | yyerror("can't change clef in voice context"); | |
190 | } | |
191 | } | |
192 | } | |
193 | } | |
194 | } | |
195 | ||
196 | \f | |
197 | ||
198 | /* Do processing on one input line worth of TIMEDSSVs. | |
199 | */ | |
200 | ||
201 | void | |
202 | tssv_line() | |
203 | { | |
204 | struct TIMEDSSV *ts_p; | |
205 | struct GRPSYL *gs_p; | |
206 | ||
207 | /* First we have to find the time offsets of each TIMEDSSV. | |
208 | * We can't necessarily calculate them at the time | |
209 | * they were added to the list, since for tuplets | |
210 | * we don't know fulltimes till we reach the end | |
211 | * of the tuplet, and know how to adjust. | |
212 | * So we just save the GRPSYL* at that point, | |
213 | * and now we go through and find all the actual times. | |
214 | */ | |
215 | for (ts_p = Timedssv_p; ts_p != 0; ts_p = ts_p->next) { | |
216 | if (GE(ts_p->time_off, Zero)) { | |
217 | /* already set from some previous line */ | |
218 | continue; | |
219 | } | |
220 | ||
221 | if (ts_p->grpsyl_p == 0) { | |
222 | /* This could happen if there was a user input | |
223 | * error, because we could have set up the TIMEDSSV | |
224 | * and then not been able to parse the GRPSYL that | |
225 | * was intended to go with it. Set time offset to | |
226 | * a safe value, and skip the rest of the loop, | |
227 | * so we don't try to dereference the null ptr. */ | |
228 | ts_p->time_off = Zero; | |
229 | continue; | |
230 | } | |
231 | ||
232 | /* Count up the time before the group where the timed | |
233 | * SSV was specified. */ | |
234 | for (ts_p->time_off = Zero, gs_p = ts_p->grpsyl_p->prev; | |
235 | gs_p != 0; gs_p = gs_p->prev) { | |
236 | /* Alt groups have not yet had their time adjusted, | |
237 | * so we have to compensate for that. */ | |
238 | if (gs_p->slash_alt < 0 || (gs_p->prev != 0 | |
239 | && gs_p->prev->slash_alt < 0) ) { | |
240 | ts_p->time_off = radd(ts_p->time_off, | |
241 | rdiv(gs_p->fulltime, Two)); | |
242 | } | |
243 | else if (gs_p->grpvalue != GV_ZERO) { | |
244 | ts_p->time_off = radd(ts_p->time_off, | |
245 | gs_p->fulltime); | |
246 | } | |
247 | } | |
248 | } | |
249 | } | |
250 | \f | |
251 | ||
252 | /* Sort the current TIMEDSSV list by time and return pointer to the head | |
253 | * of the sorted list. The sorting is done by time. When there is a tie | |
254 | * things are put in user input order. Also re-inits for the next measure. | |
255 | */ | |
256 | ||
257 | struct TIMEDSSV * | |
258 | tssv_sort() | |
259 | ||
260 | { | |
261 | struct TIMEDSSV *ret; /* return value is pointer to sorted list */ | |
262 | short moved; /* YES if something was moved during sort */ | |
263 | ||
264 | /* Most of the time, the list will be empty. */ | |
265 | if (Timedssv_p == 0) { | |
266 | return(0); | |
267 | } | |
268 | ||
269 | /* Sort in time order. | |
270 | * The list is almost certain to be very short, | |
271 | * so sort needn't be very efficient. So we check pairs | |
272 | * and swap ones that are backwards. */ | |
273 | do { | |
274 | struct TIMEDSSV **ts_p_p; | |
275 | struct TIMEDSSV *tmp_ts_p; | |
276 | ||
277 | moved = NO; | |
278 | for (ts_p_p = &Timedssv_p; (*ts_p_p)->next != 0; | |
279 | ts_p_p = &((*ts_p_p)->next) ) { | |
280 | if ( GT( (*ts_p_p)->time_off, (*ts_p_p)->next->time_off ) ) { | |
281 | /* Wrong order. Swap them */ | |
282 | tmp_ts_p = (*ts_p_p)->next; | |
283 | (*ts_p_p)->next = (*ts_p_p)->next->next; | |
284 | tmp_ts_p->next = *ts_p_p; | |
285 | *ts_p_p = tmp_ts_p; | |
286 | moved = YES; | |
287 | break; | |
288 | } | |
289 | } | |
290 | } while (moved == YES); | |
291 | ret = Timedssv_p; | |
292 | ||
293 | /* re-init for next measure */ | |
294 | Timedssv_p = 0; | |
295 | Timed_tail_p_p = &(Timedssv_p); | |
296 | return(ret); | |
297 | } |