chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mup / parstssv.c
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 }