X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~mdw/git/mup/blobdiff_plain/cdb3c0882392596f814cf939cbfbd38adc6f2bfe..ddf6330b56bcfb657e0186b24b9b1422c51d3424:/mup/mup/parstssv.c diff --git a/mup/mup/parstssv.c b/mup/mup/parstssv.c new file mode 100644 index 0000000..bf79df2 --- /dev/null +++ b/mup/mup/parstssv.c @@ -0,0 +1,297 @@ +/* Copyright (c) 2003 by Arkkra Enterprises. */ +/* All rights reserved. */ + +/* This file contains parse-time functions related to TIMEDSSVs. + * These are used to specify mid-measure parameter changes, + * like change of clef. + */ + +#include "defines.h" +#include "structs.h" +#include "globals.h" + +/* Mid-measure SSV's eventually get attached to the BAR, but we need + * to point to the list temporarily while the BAR doesn't exist yet. */ +static struct TIMEDSSV *Timedssv_p; + +/* When there is more than one TIMEDSSV at the same moment in time, + * we use user input order, so we add items to the end of the list. + * This keeps track of where to put the next one. + */ +static struct TIMEDSSV **Timed_tail_p_p = &Timedssv_p; + +static struct TIMEDSSV *tssv_new P((int context)); + + +/* Allocate and initalize a TIMEDSSV for each staff/voice being defined, + * and return pointer to the first one. + */ + +struct TIMEDSSV * +tssv_create(context) + +int context; + +{ + struct SVRANGELIST *svr_p; /* list of staffs/voices being entered */ + struct RANGELIST *srange_p; /* list of staffs being entered */ + struct RANGELIST *vrange_p; /* list of voices being entered */ + struct TIMEDSSV *new_p; + struct TIMEDSSV *first_p = 0; /* first one created */ + int s; /* staff number */ + int v; /* voice number */ + + /* Create as many new TIMEDSSVs as needed, based on the current list + * of staffs/voices being defined, + * link them onto the temporary list for the current measure. + */ + + switch (context) { + case C_SCORE: + first_p = new_p = tssv_new(context); + break; + case C_STAFF: + case C_VOICE: + for (svr_p = Svrangelist_p; svr_p != 0; svr_p = svr_p->next) { + for (srange_p = svr_p->stafflist_p; srange_p != 0; + srange_p = srange_p->next) { + for (s = srange_p->begin; s <= srange_p->end; s++) { + if (context == C_STAFF) { + new_p = tssv_new(context); + if (first_p == 0) { + first_p = new_p; + } + new_p->ssv.staffno = s; + } + else { + for (vrange_p = svr_p->vnolist_p; + vrange_p != 0; + vrange_p = vrange_p->next) { + for (v = vrange_p->begin; + v <= vrange_p->end; + v++) { + new_p = tssv_new(context); + if (first_p == 0) { + first_p = new_p; + } + new_p->ssv.staffno = s; + new_p->ssv.voiceno = v; + } + } + } + } + } + } + break; + default: + pfatal("invalid context %d when creating TIMEDSSV", context); + /*NOTREACHED*/ + break; + } + return(first_p); +} + + +/* Create a single TIMEDSSV and link it onto the list */ + +static struct TIMEDSSV * +tssv_new(context) + +int context; + +{ + struct TIMEDSSV *curr_tssv_p; + + CALLOC(TIMEDSSV, curr_tssv_p, 1); + zapssv( & (curr_tssv_p->ssv) ); + curr_tssv_p->ssv.context = context; + curr_tssv_p->grpsyl_p = 0; + curr_tssv_p->time_off.n = -1; + curr_tssv_p->time_off.d = 1; + + curr_tssv_p->next = 0; + *Timed_tail_p_p = curr_tssv_p; + Timed_tail_p_p = &(curr_tssv_p->next); + + return(curr_tssv_p); +} + + +/* Save a parameter setting in the given TIMEDSSV. We only support a very + * limited list of parameters that can be changed mid-measure, + * so this checks for valid ones. + */ + +void +tssv_update(timedssv_p, param, value) + +struct TIMEDSSV *timedssv_p; +int param; +int value; + +{ + /* Could be multiple staffs/voices, so do them all */ + for ( ; timedssv_p != 0; timedssv_p = timedssv_p->next) { + switch (param) { + case CLEF: + timedssv_p->ssv.clef = value; + break; + case RELEASE: + if (rangecheck(value, MINRELEASE, MAXRELEASE, + "mid-measure release change") == YES) { + timedssv_p->ssv.release = value; + } + break; + case DEFOCT: + if (rangecheck(value, MINOCTAVE, MAXOCTAVE, + "mid-measure defoct change") == YES) { + timedssv_p->ssv.defoct = value; + } + break; + + default: + yyerror("only clef, defoct, and release parameters can be changed mid-measure"); + return; + } + if (timedssv_p->ssv.used[param] == YES) { + warning("multiple changes of the same parameter; last used"); + } + timedssv_p->ssv.used[param] = YES; + } +} + + +/* Associate grpsyl with TIMEDSSV. This should be called at the end of + * parsing of a grpsyl, in case it has at least one timed ssv. */ + +void +tssv_setgrpsyl(gs_p) + +struct GRPSYL *gs_p; + +{ + struct TIMEDSSV *tssv_p; + + /* User could input multiple << >> things, and each gets their + * own TIMEDSSV, so we need to associate this grpsyl with + * all that don't yet have one. */ + for (tssv_p = Timedssv_p; tssv_p != 0; tssv_p = tssv_p->next) { + if (tssv_p->grpsyl_p == 0) { + tssv_p->grpsyl_p = gs_p; + + /* Do some error checks */ + if (tssv_p->ssv.used[CLEF] == YES) { + if (tssv_p->ssv.context == C_STAFF + && is_tab_staff(tssv_p->ssv.staffno)) { + yyerror("can't change clef of tab staff"); + } + if (tssv_p->ssv.context == C_VOICE) { + yyerror("can't change clef in voice context"); + } + } + } + } +} + + + +/* Do processing on one input line worth of TIMEDSSVs. + */ + +void +tssv_line() +{ + struct TIMEDSSV *ts_p; + struct GRPSYL *gs_p; + + /* First we have to find the time offsets of each TIMEDSSV. + * We can't necessarily calculate them at the time + * they were added to the list, since for tuplets + * we don't know fulltimes till we reach the end + * of the tuplet, and know how to adjust. + * So we just save the GRPSYL* at that point, + * and now we go through and find all the actual times. + */ + for (ts_p = Timedssv_p; ts_p != 0; ts_p = ts_p->next) { + if (GE(ts_p->time_off, Zero)) { + /* already set from some previous line */ + continue; + } + + if (ts_p->grpsyl_p == 0) { + /* This could happen if there was a user input + * error, because we could have set up the TIMEDSSV + * and then not been able to parse the GRPSYL that + * was intended to go with it. Set time offset to + * a safe value, and skip the rest of the loop, + * so we don't try to dereference the null ptr. */ + ts_p->time_off = Zero; + continue; + } + + /* Count up the time before the group where the timed + * SSV was specified. */ + for (ts_p->time_off = Zero, gs_p = ts_p->grpsyl_p->prev; + gs_p != 0; gs_p = gs_p->prev) { + /* Alt groups have not yet had their time adjusted, + * so we have to compensate for that. */ + if (gs_p->slash_alt < 0 || (gs_p->prev != 0 + && gs_p->prev->slash_alt < 0) ) { + ts_p->time_off = radd(ts_p->time_off, + rdiv(gs_p->fulltime, Two)); + } + else if (gs_p->grpvalue != GV_ZERO) { + ts_p->time_off = radd(ts_p->time_off, + gs_p->fulltime); + } + } + } +} + + +/* Sort the current TIMEDSSV list by time and return pointer to the head + * of the sorted list. The sorting is done by time. When there is a tie + * things are put in user input order. Also re-inits for the next measure. + */ + +struct TIMEDSSV * +tssv_sort() + +{ + struct TIMEDSSV *ret; /* return value is pointer to sorted list */ + short moved; /* YES if something was moved during sort */ + + /* Most of the time, the list will be empty. */ + if (Timedssv_p == 0) { + return(0); + } + + /* Sort in time order. + * The list is almost certain to be very short, + * so sort needn't be very efficient. So we check pairs + * and swap ones that are backwards. */ + do { + struct TIMEDSSV **ts_p_p; + struct TIMEDSSV *tmp_ts_p; + + moved = NO; + for (ts_p_p = &Timedssv_p; (*ts_p_p)->next != 0; + ts_p_p = &((*ts_p_p)->next) ) { + if ( GT( (*ts_p_p)->time_off, (*ts_p_p)->next->time_off ) ) { + /* Wrong order. Swap them */ + tmp_ts_p = (*ts_p_p)->next; + (*ts_p_p)->next = (*ts_p_p)->next->next; + tmp_ts_p->next = *ts_p_p; + *ts_p_p = tmp_ts_p; + moved = YES; + break; + } + } + } while (moved == YES); + ret = Timedssv_p; + + /* re-init for next measure */ + Timedssv_p = 0; + Timed_tail_p_p = &(Timedssv_p); + return(ret); +}