chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / parstssv.c
CommitLineData
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. */
15static 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 */
21static struct TIMEDSSV **Timed_tail_p_p = &Timedssv_p;
22
23static 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
30struct TIMEDSSV *
31tssv_create(context)
32
33int 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
97static struct TIMEDSSV *
98tssv_new(context)
99
100int 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
125void
126tssv_update(timedssv_p, param, value)
127
128struct TIMEDSSV *timedssv_p;
129int param;
130int 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
167void
168tssv_setgrpsyl(gs_p)
169
170struct 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
201void
202tssv_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
257struct TIMEDSSV *
258tssv_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}