X-Git-Url: https://git.distorted.org.uk/~mdw/mup/blobdiff_plain/cdb3c0882392596f814cf939cbfbd38adc6f2bfe..ddf6330b56bcfb657e0186b24b9b1422c51d3424:/mup/mup/ssv.c diff --git a/mup/mup/ssv.c b/mup/mup/ssv.c new file mode 100644 index 0000000..4ec37b2 --- /dev/null +++ b/mup/mup/ssv.c @@ -0,0 +1,1420 @@ +/* Copyright (c) 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2006 by Arkkra Enterprises */ +/* All rights reserved */ +/* + * Name: ssv.c + * + * Description: This file defines the instances of score, staff, and voice + * structures that are used for viewpathing. It also contains + * functions for accessing them. + */ + +#include +#include + +#include "defines.h" +#include "rational.h" +#include "structs.h" +#include "globals.h" + +/* define the default order of stacking for marks */ +static char Defmarkorder[] = { + 1, /* MK_MUSSYM */ + 2, /* MK_OCTAVE */ + 3, /* MK_DYN */ + 3, /* MK_OTHERTEXT */ + 3, /* MK_CHORD */ + 4, /* MK_LYRICS */ + 5, /* MK_ENDING */ + 6, /* MK_REHEARSAL */ + 7, /* MK_PEDAL */ +}; + +/* default timesig of 4/4 */ +static char Dflt_timerep[] = { 4, TSR_SLASH, 4, TSR_END }; + +static void freestaffset P((int num, struct STAFFSET *ss_p)); +static void asgnstaffset P((int num, struct STAFFSET **new_p_p, + struct STAFFSET *old_p)); +static void setorder P((int place, struct SSV *i_p, struct SSV *f_p)); + +/* + * Name: initstructs() + * + * Abstract: Init the fixed instances of the SSV structures. + * + * Returns: void + * + * Description: This function initializes all the fixed structures Score, + * Staff[s], and Voice[s][v]. This needs to be done before + * scanning through the main linked list of user input + * structures. + */ + +void +initstructs() + +{ + int s; /* staff number */ + int v; /* voice number */ + int n; /* another loop variable */ + int p; /* place (PL_*) */ + int m; /* mark (MK_*) */ + int hs; /* head shape number */ + + + /* + * Call zapssv() to release any malloc'ed memory, if any of the + * structures have been used before, and mark all fields unused. + */ + zapssv(&Score); + + for (s = 0; s < MAXSTAFFS; s++) { + zapssv(&Staff[s]); + for (v = 0; v < MAXVOICES; v++) + zapssv(&Voice[s][v]); + } + + /* + * Fill the Score structure with the proper default values, and + * mark all its fields as used. + */ + /* score context */ + Score.scale_factor = DEFSCALE; + Score.units = INCHES; + Score.pageheight = DEFPAGEHEIGHT; + Score.pagewidth = DEFPAGEWIDTH; + Score.panelsperpage = DEFPANELSPERPAGE; + Score.topmargin = DEFVMARGIN; + Score.botmargin = DEFVMARGIN; + Score.leftmargin = DEFHMARGIN; + Score.rightmargin = DEFHMARGIN; + Score.restcombine = NORESTCOMBINE; + Score.firstpage = NOFIRSTPAGE; + Score.staffs = DEFSTAFFS; + Score.minscsep = DEFMINSCSEP; + Score.maxscsep = DEFMAXSCSEP; + Score.minscpad = DEFMINSCPAD; + Score.maxscpad = DEFMAXSCPAD; + Score.nbrace = 0; + Score.nbrack = 0; + Score.nbarst = 0; + Score.timerep = Dflt_timerep; + Score.timenum = 4; + Score.timeden = 4; + Score.time.n = 1; + Score.time.d = 1; + Score.division = DEFDIVISION; + Score.endingstyle = ENDING_TOP; + Score.gridsatend = NO; + Score.measnum = NO; + Score.measnumfamily = BASE_TIMES; + Score.measnumfont = FONT_TR; + Score.measnumsize = MNUM_SIZE; + Score.packfact = DFLTPACKFACT; + Score.packexp = DFLTPACKEXP; + Score.warn = YES; + + /* score and staff context */ + Score.staffscale = DEFSTFSCALE; + Score.visible = YES; + Score.hidesilent = NO; + Score.stafflines = 5; + Score.strinfo = 0; + Score.printclef = SS_NORMAL; + Score.gridswhereused = NO; + Score.gridscale = DEFGRIDSCALE; + Score.gridfret = DEFGRIDFRET; + Score.numbermrpt = YES; + Score.printmultnum = YES; + Score.restsymmult = NO; + Score.vscheme = V_1; + for (v = 0; v < MAXVOICES; v++) { + Score.vcombine[v] = 0; + } + Score.vcombinequal = VC_NOOVERLAP; + Score.sharps = DEFSHARPS; + Score.is_minor = NO; + Score.cancelkey = NO; + Score.inttype = PERFECT; + Score.intnum = 1; + Score.addinttype = PERFECT; + Score.addintnum = 1; + Score.clef = TREBLE; + Score.rehstyle = RS_BOXED; + Score.fontfamily = BASE_TIMES; + Score.font = FONT_TR; + Score.size = DFLT_SIZE; + Score.lyricsfamily = BASE_TIMES; + Score.lyricsfont = FONT_TR; + Score.lyricssize = DFLT_SIZE; + Score.lyricsalign = DEFLYRICSALIGN; + Score.sylposition = DEFSYLPOSITION; + Score.minstsep = DEFMINSTSEP; + Score.staffpad = DEFSTPAD; + for (p = 0; p < NUM_PLACE; p++) { + for (m = 0; m < NUM_MARK; m++) { + Score.markorder[p][m] = Defmarkorder[m]; + } + } + Score.pedstyle = P_LINE; + Score.chorddist = DEFCHORDDIST; + Score.dist = DEFDIST; + Score.dyndist = DEFDYNDIST; + Score.label = 0; + Score.label2 = 0; + + /* score, staff, and voice context */ + Score.nbeam = 0; + Score.nsubbeam = 0; + Score.beamfact = DEFBEAMFACT; + Score.beammax = DEFBEAMMAX; + Score.pad = DEFPAD; + Score.stemlen = DEFSTEMLEN; + Score.stemshorten = DEFSTEMSHORTEN; + Score.defoct = DEFOCTAVE; + Score.timeunit.n = 1; + Score.timeunit.d = 4; + Score.timelist_p = 0; + Score.swingunit = Zero; + Score.release = DEFRELEASE; + Score.ontheline = YES; + Score.tabwhitebox = NO; + hs = get_shape_num("norm"); + for (n = 0; n < 7; n++) { + Score.noteheads[n] = hs; + } + + for (n = 0; n < NUMFLDS; n++) { + Score.used[n] = YES; /* all items will be set in Score */ + } +} + +/* + * Name: zapssv() + * + * Abstract: Init a fixed instance of the SSV structure to empty. + * + * Returns: void + * + * Description: This function initializes a fixed SSV structure to say that + * all fields are unused. + */ + +void +zapssv(s_p) + +struct SSV *s_p; /* pointer to the structure to be zapped */ + +{ + int n; /* loop variable */ + + + /* + * If the structure has been used before, we first have to free any + * memory that was malloc'ed. It's okay to check "used" the first + * time, because global variables are set to 0 by the compiler; + * thus, used[X] == NO. + */ + if (s_p->used[BRACELIST] == YES) { + freestaffset(s_p->nbrace, s_p->bracelist); + s_p->bracelist = 0; + } + if (s_p->used[BRACKLIST] == YES) { + freestaffset(s_p->nbrack, s_p->bracklist); + s_p->bracklist = 0; + } + if (s_p->used[BARSTLIST] == YES && s_p->barstlist != 0) { + FREE(s_p->barstlist); + s_p->barstlist = 0; + } + if (s_p->used[LABEL] == YES && s_p->label != 0) { + FREE(s_p->label); + s_p->label = 0; + } + if (s_p->used[LABEL2] == YES && s_p->label2 != 0) { + FREE(s_p->label2); + s_p->label2 = 0; + } + if (s_p->used[BEAMSTLIST] == YES && s_p->beamstlist != 0) { + FREE(s_p->beamstlist); + s_p->beamstlist = 0; + } + + /* + * Mark all fields unused. + */ + for (n = 0; n < NUMFLDS; n++) { + s_p->used[n] = NO; + } +} + +/* + * Name: freestaffset() + * + * Abstract: Free all malloc'ed memory associated with a staffset list. + * + * Returns: void + * + * Description: This function frees staffset structures, along with their + * labels if they exist. + */ + +static void +freestaffset(num, ss_p) + +int num; /* how many staffsets are in list? */ +struct STAFFSET *ss_p; /* pointer to first staffset */ + +{ + int n; /* loop variable */ + + + if (ss_p == 0) /* if there is no list, just return */ + return; + + /* for each staffset in the list, free labels if present */ + for (n = 0; n < num; n++) { + if (ss_p[n].label != 0) { /* if label, free it */ + FREE(ss_p[n].label); + ss_p[n].label = 0; + } + if (ss_p[n].label2 != 0) { /* if label2, free it */ + FREE(ss_p[n].label2); + ss_p[n].label2 = 0; + } + } + + FREE(ss_p); /* free the staffset list itself */ +} + +/* + * Name: asgnstaffset() + * + * Abstract: Set up a staffset list in another SSV structure. + * + * Returns: void + * + * Description: This function sets up a new staffset list identical to + * another one, including the labels if they exist. + */ + +static void +asgnstaffset(num, new_p_p, old_p) + +int num; /* how many staffsets are in list? */ +struct STAFFSET **new_p_p; /* ptr to ptr to first staffset of new list */ +struct STAFFSET *old_p; /* pointer to first staffset of old list */ + +{ + int n; /* loop variable */ + + + if (num == 0) /* if there's no old list, nothing to do */ + return; + + /* + * Allocate the new list and point at it. + */ + MALLOC(STAFFSET, *new_p_p, num); + + /* + * Loop through the old list. Wherever there is a label, allocate + * one for the new list. When there isn't, set that pointer to null. + */ + for (n = 0; n < num; n++) { + (*new_p_p)[n].topstaff = old_p[n].topstaff; + (*new_p_p)[n].botstaff = old_p[n].botstaff; + + if (old_p[n].label != 0) { + MALLOCA(char, (*new_p_p)[n].label, + strlen(old_p[n].label) + 1); + (void)strcpy((*new_p_p)[n].label, old_p[n].label); + } else { + (*new_p_p)[n].label = 0; + } + + if (old_p[n].label2 != 0) { + MALLOCA(char, (*new_p_p)[n].label2, + strlen(old_p[n].label2) + 1); + (void)strcpy((*new_p_p)[n].label2, old_p[n].label2); + } else { + (*new_p_p)[n].label2 = 0; + } + } +} + +/* + * Name: svpath() + * + * Abstract: Find a field for a staff, using the viewpath. + * + * Returns: pointer to structure containing correct field value + * + * Description: This function, given a staff number and a field number, looks + * down the viewpath to find the first structure where the field + * is set. It returns a pointer to that structure. (However, see + * below for a special kluge for the "visible" field.) + * Note: 0 is allowed for the staff number, and that means use + * the Score value. + */ + +struct SSV * +svpath(s, field) + +int s; /* staff number, 1 to MAXSTAFFS; or 0, meaning score */ +int field; /* the defined symbol for the field desired */ + +{ + static struct SSV phony; /* phony SSV; see below */ + + + if (s == 0) + return (&Score); + + /* + * For the "visible" field, special kluges are needed, for two reasons. + * One is that there is a command line option (-s) that overrides the + * visibility requests in the Mup input file. The other is that, even + * though it is a score/staff/voice parameter, it is easier to manage + * visibility at the staff level than at the voice level. Otherwise, + * to know if a staff should be drawn, we would have to check vscheme + * and the visibility state of each voice. + * + * The design is as follows: in mkchords.c, if a voice is to be + * invisible, a measure space is put in place of its original GRPSYL + * list. Users can use svpath() to check if a staff is visible + * instead of having to call vvpath() for its voice(s). The field + * staff_p->visible is set by calling here, so that will also be + * consistent. + * + * If the -s option was used on the command line, only staffs/voices + * listed there are ever allowed to be visible. So although SSVs are + * set as for other score/staff/voice parameters, the results from + * svpath() also consider what the -s option said. In vvpath() this + * is also done. + */ + if (field == VISIBLE) { + int num_voices; /* how many voices on this staff */ + + /* + * In case we are going to return the phony SSV, load the + * hidesilent field into it. Both visible and hidesilent are + * controlled by VISIBLE, but hidesilent is not to be affected + * by the kluges needed for "visible". + */ + /* if the field is set in the staff structure, use that */ + if (Staff[s-1].used[VISIBLE] == YES) { + phony.hidesilent = Staff[s-1].hidesilent; + } else { + /* use the score structure; it's always set there */ + phony.hidesilent = Score.hidesilent; + } + + /* always return NO if the command line says staff invisible */ + if (Staff_vis[s] == NO) { + /* return phony SSV with NO, ignore real SSV */ + phony.visible = NO; + return (&phony); + } + + num_voices = vscheme_voices(svpath(s, VSCHEME)->vscheme); + + /* + * If a voice that the command line is allowing to be visible + * was requested via an SSV to be visible, we must let the + * staff be visible. + */ + if ((Voice_vis[s][1] == YES && + Voice[s-1][0].used[VISIBLE] == YES && + Voice[s-1][0].visible == YES) || + + (num_voices >= 2 && + Voice_vis[s][2] == YES && + Voice[s-1][1].used[VISIBLE] == YES && + Voice[s-1][1].visible == YES) || + + (num_voices >= 3 && + Voice_vis[s][3] == YES && + Voice[s-1][2].used[VISIBLE] == YES && + Voice[s-1][2].visible == YES)) { + + /* return phony SSV with YES, ignore real SSV */ + phony.visible = YES; + return (&phony); + } + + /* + * If, for each voice that exists, either the command line is + * forcing it to be invisible or else it was requested via an + * SSV to be invisible, then the staff must be invisible. + */ + if ((Voice_vis[s][1] == NO || + Voice[s-1][0].used[VISIBLE] == YES && + Voice[s-1][0].visible == NO) && + + (num_voices < 2 || + (Voice_vis[s][2] == NO || + Voice[s-1][1].used[VISIBLE] == YES && + Voice[s-1][1].visible == NO)) && + + (num_voices < 3 || + (Voice_vis[s][3] == NO || + Voice[s-1][2].used[VISIBLE] == YES && + Voice[s-1][2].visible == NO))) { + + /* return phony SSV with NO, ignore real SSV */ + phony.visible = NO; + return (&phony); + } + + /* + * The command line and the voice(s) aren't forcing the issue. + * So fall through to determine the staff's visibility the + * normal way. + */ + } + + /* if the field is set in the staff structure, use that */ + if (Staff[s-1].used[field] == YES) + return (&Staff[s-1]); + + /* else use the score structure; it's always set there */ + return (&Score); +} + +/* + * Name: vvpath() + * + * Abstract: Find a field for a voice, using the viewpath. + * + * Returns: pointer to structure containing correct field value + * + * Description: This function, given a staff number, voice number on that + * staff, and a field number, looks down the viewpath to find + * the first structure where the field is set. It returns a + * pointer to that structure. (However, see below for a special + * kluge for the "visible" field.) + * Note: 0 is allowed for the voice number, and that means use + * the staff's value. If staff is 0, the Score is used, + * regardless of the voice number. + */ + +struct SSV * +vvpath(s, v, field) + +int s; /* staff number, 1 to MAXSTAFFS; or 0, meaning score */ +int v; /* voice number, 1 to MAXVOICES; or 0, meaning staff */ +int field; /* the defined symbol for the field desired */ + +{ + static struct SSV phony; /* phony SSV; see below */ + + + if (s == 0 || v == 0) + return (svpath(s, field)); + + /* + * See the comment in svpath() regarding the "visible" field. There's + * probably no need to call vvpath() for "visible" after mkchords.c has + * run, since voices that are to be invisible are changed to measure + * spaces there. But in mkchords.c itself, and earlier, there is + * sometimes a need. + * + * For the "visible" field, first check the command line to see if this + * voice or its staff must always be invisible. If so, return a phony + * SSV that says that. Otherwise fall through to handle the normal way. + */ + if (field == VISIBLE && (Staff_vis[s] == NO || Voice_vis[s][v] == NO)) { + /* + * Since we are going to force visible to NO, it's irrelevant + * what hidesilent is, so don't bother setting it, unlike what + * we had to do in svpath(). + */ + phony.visible = NO; + return (&phony); + } + + /* if the field is set in the voice structure, use that */ + if (Voice[s-1][v-1].used[field] == YES) + return (&Voice[s-1][v-1]); + + /* else if the field is set in the staff structure, use that */ + if (Staff[s-1].used[field] == YES) + return (&Staff[s-1]); + + /* else use the score structure; it's always set there */ + return (&Score); +} + +/* + * Name: asgnssv() + * + * Abstract: Assign fields from an input SSV to a fixed one. + * + * Returns: void + * + * Description: This function is passed an input SSV structure (from an input + * context). For each field where "used" is YES in the input SSV, + * it copies it to the appropriate fixed SSV and sets its "used" + * flag to YES. For each field where "used" is UNSET in the input + * SSV, it sets "used" to NO in the appropriate fixed SSV. + * In some cases, there are side effects, where it also + * alters a lower level structure. E.g., changing the number of + * voices of a staff inits its voice structures. In the case + * of stafflines, setting a staff to be a tablature staff + * automatically automatically forces some other fields to be set + * not only in the given staff, but also in the preceding tabnote + * staff. Note also that the Score "used" flags are already + * always set and don't need to be set here. And Score fields can + * never be unset. + */ + +void +asgnssv(i_p) + +struct SSV *i_p; /* input SSV structure to be copied from */ + +{ + struct SSV *f_p; /* ptr to fixed SSV structure to copy into */ + int s, v; /* used for looping through staffs & voices */ + int start, stop; /* loop limits */ + int n; /* another loop variable */ + + + f_p = 0; /* to prevent "uninitialized variable" warnings */ + + /* + * Using the selector fields in the input structure, set a pointer to + * the fixed structure that is to be populated. + */ + switch (i_p->context) { + case C_SCORE: + f_p = &Score; + break; + case C_STAFF: + /* silently ignore bogus staff no.; it is caught elsewhere */ + if (i_p->staffno < 1 || i_p->staffno > MAXSTAFFS) { + return; + } + f_p = &Staff[ i_p->staffno - 1 ]; + break; + case C_VOICE: + /* silently ignore bogus staff/voice; it is caught elsewhere */ + if (i_p->staffno < 1 || i_p->staffno > MAXSTAFFS || + i_p->voiceno < 1 || i_p->voiceno > MAXVOICES) { + return; + } + f_p = &Voice[ i_p->staffno - 1 ][ i_p->voiceno - 1 ]; + break; + } + + /* + * ========== ITEMS FOR SCORE CONTEXT ONLY =========== + * There's no need to set f_p->used[] = YES here; since this is the + * score, those bits are already always YES. + */ + if (i_p->used[SCALE_FACTOR] == YES) { + f_p->scale_factor = i_p->scale_factor; + } + + if (i_p->used[UNITS] == YES) { + f_p->units = i_p->units; + } + + /* + * PAGEHEIGHT, PAGEHEIGHT, and PANELSPERPAGE interact, because when the + * user sets PAGE*, they are referring to the paper, but internally we + * want page* to refer to one "panel" of music, which is a 90 degree + * rotated half of the sheet of paper when panelsperpage is 2. + */ + if (i_p->used[PAGEHEIGHT] == YES) { + if (f_p->panelsperpage == 1) { + f_p->pageheight = i_p->pageheight; + } else { + f_p->pagewidth = i_p->pageheight / 2.0; + } + } + + if (i_p->used[PAGEWIDTH] == YES) { + if (f_p->panelsperpage == 1) { + f_p->pagewidth = i_p->pagewidth; + } else { + f_p->pageheight = i_p->pagewidth; + } + } + + if (i_p->used[PANELSPERPAGE] == YES) { + /* depending on how this is changing, flip height and width */ + float oldwidth; + if (f_p->panelsperpage == 1 && i_p->panelsperpage == 2) { + oldwidth = f_p->pagewidth; + f_p->pagewidth = f_p->pageheight / 2.0; + f_p->pageheight = oldwidth; + } else if (f_p->panelsperpage == 2 && i_p->panelsperpage == 1) { + oldwidth = f_p->pagewidth; + f_p->pagewidth = f_p->pageheight; + f_p->pageheight = oldwidth * 2.0; + } + f_p->panelsperpage = i_p->panelsperpage; + } + + if (i_p->used[TOPMARGIN] == YES) { + f_p->topmargin = i_p->topmargin; + } + + if (i_p->used[BOTMARGIN] == YES) { + f_p->botmargin = i_p->botmargin; + } + + if (i_p->used[LEFTMARGIN] == YES) { + f_p->leftmargin = i_p->leftmargin; + } + + if (i_p->used[RIGHTMARGIN] == YES) { + f_p->rightmargin = i_p->rightmargin; + } + + if (i_p->used[RESTCOMBINE] == YES) { + f_p->restcombine = i_p->restcombine; + } + + if (i_p->used[FIRSTPAGE] == YES) { + f_p->firstpage = i_p->firstpage; + } + + if (i_p->used[NUMSTAFF] == YES) { + f_p->staffs = i_p->staffs; + + /* this destroys all staff and voice info */ + for (s = 0; s < MAXSTAFFS; s++) { + zapssv(&Staff[s]); + for (v = 0; v < MAXVOICES; v++) + zapssv(&Voice[s][v]); + } + } + + if (i_p->used[MINSCSEP] == YES) { + f_p->minscsep = i_p->minscsep; + } + + if (i_p->used[MAXSCSEP] == YES) { + f_p->maxscsep = i_p->maxscsep; + } + + if (i_p->used[MINSCPAD] == YES) { + f_p->minscpad = i_p->minscpad; + } + + if (i_p->used[MAXSCPAD] == YES) { + f_p->maxscpad = i_p->maxscpad; + } + + if (i_p->used[BRACELIST] == YES) { + /* if it was already used, free old list if present */ + if (f_p->used[BRACELIST] == YES) { + freestaffset(f_p->nbrace, f_p->bracelist); + f_p->bracelist = 0; + } + + /* set up new list */ + f_p->nbrace = i_p->nbrace; + asgnstaffset(f_p->nbrace, &f_p->bracelist, i_p->bracelist); + } + + if (i_p->used[BRACKLIST] == YES) { + /* if it was already used, free old list if present */ + if (f_p->used[BRACKLIST] == YES) { + freestaffset(f_p->nbrack, f_p->bracklist); + f_p->bracklist = 0; + } + + /* set up new list */ + f_p->nbrack = i_p->nbrack; + asgnstaffset(f_p->nbrack, &f_p->bracklist, i_p->bracklist); + } + + if (i_p->used[BARSTLIST] == YES) { + /* if it was already used, free old list if present */ + if (f_p->used[BARSTLIST] == YES && f_p->nbarst != 0) + FREE(f_p->barstlist); + + /* set up new list */ + f_p->nbarst = i_p->nbarst; + /* the +1 is to guard against allocating 0 */ + MALLOC(TOP_BOT, f_p->barstlist, f_p->nbarst + 1); + for (n = 0; n < f_p->nbarst; n++) { + f_p->barstlist[n] = i_p->barstlist[n]; + } + } + + if (i_p->used[TIME] == YES) { + f_p->timenum = i_p->timenum; + f_p->timeden = i_p->timeden; + f_p->timevis = i_p->timevis; + f_p->timerep = i_p->timerep; + f_p->time = i_p->time; + + /* + * Changing the time sig forces a change in default time unit. + * Set it to one "beat" for the score, and unset it for all + * staffs and voices. + */ + f_p->timeunit.n = 1; + f_p->timeunit.d = f_p->timeden; + f_p->timelist_p = 0; + for (s = 0; s < MAXSTAFFS; s++) { + Staff[s].used[TIMEUNIT] = NO; + for (v = 0; v < MAXVOICES; v++) + Voice[s][v].used[TIMEUNIT] = NO; + } + + /* + * Changing the time also destroys all beamstyle lists. + * However, the special empty beamstyle list that was set up + * for a tablature staff must be retained, so that it will + * continue to override any score beamstyle list that may be + * set up later on. + */ + if (Score.used[BEAMSTLIST] == YES) { + if (Score.nbeam != 0) { + FREE(Score.beamstlist); + Score.beamstlist = 0; + Score.nbeam = 0; + FREE(Score.subbeamstlist); + Score.subbeamstlist = 0; + Score.nsubbeam = 0; + } + } + for (s = 0; s < MAXSTAFFS; s++) { + if (Staff[s].used[BEAMSTLIST] == YES && + Staff[s].strinfo == 0) { /* not tablature */ + if (Staff[s].nbeam != 0) { + FREE(Staff[s].beamstlist); + Staff[s].beamstlist = 0; + Staff[s].nbeam = 0; + FREE(Staff[s].subbeamstlist); + Staff[s].subbeamstlist = 0; + Staff[s].nsubbeam = 0; + } + Staff[s].used[BEAMSTLIST] = NO; + } + for (v = 0; v < MAXVOICES; v++) { + if (Voice[s][v].used[BEAMSTLIST] == YES) { + if (Voice[s][v].nbeam != 0) { + FREE(Voice[s][v].beamstlist); + Voice[s][v].beamstlist = 0; + Voice[s][v].nbeam = 0; + FREE(Voice[s][v].subbeamstlist); + Voice[s][v].subbeamstlist = 0; + Voice[s][v].nsubbeam = 0; + } + Voice[s][v].used[BEAMSTLIST] = NO; + } + } + } + } + + if (i_p->used[DIVISION] == YES) { + f_p->division = i_p->division; + } + + if (i_p->used[ENDINGSTYLE] == YES) { + f_p->endingstyle = i_p->endingstyle; + } + + if (i_p->used[GRIDSATEND] == YES) { + f_p->gridsatend = i_p->gridsatend; + } + + if (i_p->used[MEASNUM] == YES) { + f_p->measnum = i_p->measnum; + } + + if (i_p->used[MEASNUMFAMILY] == YES) { + f_p->measnumfamily = i_p->measnumfamily; + } + + if (i_p->used[MEASNUMFONT] == YES) { + f_p->measnumfont = i_p->measnumfont; + } + + if (i_p->used[MEASNUMSIZE] == YES) { + f_p->measnumsize = i_p->measnumsize; + } + + if (i_p->used[PACKFACT] == YES) { + f_p->packfact = i_p->packfact; + } + + if (i_p->used[PACKEXP] == YES) { + f_p->packexp = i_p->packexp; + } + + if (i_p->used[WARN] == YES) { + f_p->warn = i_p->warn; + } + + /* + * ========== ITEMS FOR SCORE AND STAFF CONTEXT =========== + */ + /* + * Most parameters involve just a single field, and have no side + * effects. For this, we can use the following switch statement to + * do the work, for parameters that can exist on staff or voice. + * (Score-only ones don't need it, since that can't be unset.) + */ +#define SETPARM(name, NAME) \ + switch (i_p->used[NAME]) { \ + case YES: \ + f_p->name = i_p->name; \ + f_p->used[NAME] = YES; \ + break; \ + case UNSET: \ + f_p->used[NAME] = NO; \ + break; \ + /* default is NO; do nothing */ \ + } + + SETPARM(staffscale, STAFFSCALE) + + switch (i_p->used[STAFFLINES]) { + case YES: { + struct SSV *tabnote_p; /* ptr to tabnote fixed SSV */ + f_p->stafflines = i_p->stafflines; + + if (i_p->strinfo != 0) { /* tablature */ + struct SSV *voice_p; /* ptr to a voice's fixed SSV*/ + + /* + * This is a tablature staff. Set printclef to normal + * (even though tab isn't particularly "normal"). + * Point f_p->strinfo at the same array that + * i_p->strinfo points at. + */ + f_p->printclef = SS_NORMAL; + f_p->strinfo = i_p->strinfo; + + /* + * Force some other score/staff items to fixed values + * for tab. The parser blocks the user from setting + * these. We need to set them here in the staff SSV to + * override whatever may be in the score SSV. This + * will make it possible to avoid special checks for + * tablature in many places; the right values will be + * set for this staff. Also force score/staff/voice + * items here. + */ + f_p->sharps = 0; + f_p->is_minor = NO; + f_p->used[SHARPS] = YES; + + f_p->inttype = PERFECT; + f_p->intnum = 1; + f_p->used[TRANSPOSITION] = YES; + + f_p->addinttype = PERFECT; + f_p->addintnum = 1; + f_p->used[ADDTRANSPOSITION] = YES; + + f_p->clef = TABCLEF; + f_p->used[CLEF] = YES; + + if (f_p->used[BEAMSTLIST] == YES && f_p->nbeam != 0) { + /* if already used, free old list if present */ + FREE(f_p->beamstlist); + FREE(f_p->subbeamstlist); + } + f_p->nbeam = 0; + f_p->beamstlist = 0; + f_p->nsubbeam = 0; + f_p->subbeamstlist = 0; + f_p->used[BEAMSTLIST] = YES; + + f_p->defoct = 4; + f_p->used[DEFOCT] = YES; + + /* blow away the following in tab staff's voices */ + for (v = 0; v < MAXVOICES; v++) { + voice_p = &Voice[ i_p->staffno - 1][ v ]; + + if (voice_p->used[BEAMSTLIST] == YES) { + if (voice_p->nbeam != 0) { + FREE(f_p->beamstlist); + FREE(f_p->subbeamstlist); + } + voice_p->used[BEAMSTLIST] = NO; + } + voice_p->used[DEFOCT] = NO; + } + + /* + * Force fields on the tabnote staff above this tab + * staff. + */ + tabnote_p = &Staff[ i_p->staffno - 2 ]; + + /* + * The parse phase wouldn't let this be another tab + * staff, so we don't need to check for that. But it + * might be a 1-line staff. If so, override it to 5 + * line. If this parameter wasn't set, force printclef + * to normal, but if it was, keep the old value. (We + * might as well allow 5n and 5 drum as well as 5, + * though that would be a weird usage.) + */ + tabnote_p->stafflines = 5; + if (tabnote_p->used[STAFFLINES] == NO) + tabnote_p->printclef = SS_NORMAL; + tabnote_p->used[STAFFLINES] = YES; + + } else { /* not tablature */ + /* + * If this staff used to be tablature, we need to unset + * some "used" fields that were forced. + */ + if (f_p->strinfo != 0) { + f_p->used[SHARPS] = NO; + f_p->used[TRANSPOSITION] = NO; + f_p->used[ADDTRANSPOSITION] = NO; + f_p->used[CLEF] = NO; + f_p->used[BEAMSTLIST] = NO; + f_p->used[DEFOCT] = NO; + tabnote_p = &Staff[ i_p->staffno - 2 ]; + + /* make it non-tablature */ + f_p->strinfo = 0; + } + + f_p->printclef = i_p->printclef; + } + f_p->used[STAFFLINES] = YES; + } break; + case UNSET: + f_p->used[STAFFLINES] = NO; + break; + } + + SETPARM(gridswhereused, GRIDSWHEREUSED) + + SETPARM(gridscale, GRIDSCALE) + + SETPARM(gridfret, GRIDFRET) + + SETPARM(numbermrpt, NUMBERMRPT) + + SETPARM(printmultnum, PRINTMULTNUM) + + SETPARM(restsymmult, RESTSYMMULT) + + switch (i_p->used[VSCHEME]) { + case YES: + /* + * If the vscheme change changes the *number* of voices, we + * have to wipe out the voice information, but otherwise not. + */ + if (i_p->context == C_SCORE) { + start = 0; /* if score, do test for */ + stop = MAXSTAFFS - 1; /* all staffs */ + } else { /* C_STAFF */ + start = stop = i_p->staffno - 1; /* do just this one */ + } + + /* for each staff affected by this change . . . */ + for (n = start; n <= stop; n++) { + int oldvoices, newvoices; /* how many before & after */ + + oldvoices = vscheme_voices(svpath(n + 1, + VSCHEME)->vscheme); + newvoices = vscheme_voices(i_p->vscheme); + + if (oldvoices != newvoices) { + + for (v = 0; v < MAXVOICES; v++) + zapssv(&Voice[n][v]); + } + } + + f_p->vscheme = i_p->vscheme; + f_p->used[VSCHEME] = YES; + break; + case UNSET: + f_p->used[VSCHEME] = NO; + break; + } + + switch (i_p->used[VCOMBINE]) { + case YES: + for (v = 0; v < MAXVOICES; v++) { + f_p->vcombine[v] = i_p->vcombine[v]; + } + f_p->vcombinequal = i_p->vcombinequal; + f_p->used[VCOMBINE] = YES; + break; + case UNSET: + f_p->used[VCOMBINE] = NO; + break; + } + + switch (i_p->used[SHARPS]) { + case YES: + f_p->sharps = i_p->sharps; + f_p->is_minor = i_p->is_minor; + f_p->used[SHARPS] = YES; + break; + case UNSET: + f_p->used[SHARPS] = NO; + break; + } + + SETPARM(cancelkey, CANCELKEY) + + switch (i_p->used[TRANSPOSITION]) { + case YES: + f_p->inttype = i_p->inttype; + f_p->intnum = i_p->intnum; + f_p->used[TRANSPOSITION] = YES; + break; + case UNSET: + f_p->used[TRANSPOSITION] = NO; + break; + } + + switch (i_p->used[ADDTRANSPOSITION]) { + case YES: + f_p->addinttype = i_p->addinttype; + f_p->addintnum = i_p->addintnum; + f_p->used[ADDTRANSPOSITION] = YES; + break; + case UNSET: + f_p->used[ADDTRANSPOSITION] = NO; + break; + } + + switch (i_p->used[CLEF]) { + case YES: + f_p->clef = i_p->clef; + f_p->used[CLEF] = YES; + + /* + * Reset the default octave so that the middle line of the + * staff lies within it. If the user also set octave in + * this context, this will get changed again later in this + * function. + */ + if (f_p->clef > ALTO) /* lower than alto */ + f_p->defoct = 3; + else if (f_p->clef < TREBLE) /* higher than treble */ + f_p->defoct = 5; + else + f_p->defoct = 4; + f_p->used[DEFOCT] = YES; + break; + case UNSET: + f_p->used[DEFOCT] = NO; + break; + } + + SETPARM(rehstyle, REHSTYLE) + + SETPARM(fontfamily, FONTFAMILY) + + SETPARM(font, FONT) + + SETPARM(size, SIZE) + + SETPARM(lyricsfamily, LYRICSFAMILY) + + SETPARM(lyricsfont, LYRICSFONT) + + SETPARM(lyricssize, LYRICSSIZE) + + SETPARM(lyricsalign, LYRICSALIGN) + + SETPARM(sylposition, SYLPOSITION) + + SETPARM(minstsep, MINSTSEP) + + SETPARM(staffpad, STAFFPAD) + + switch (i_p->used[ABOVEORDER]) { + case YES: + setorder(PL_ABOVE, i_p, f_p); + f_p->used[ABOVEORDER] = YES; + break; + case UNSET: + f_p->used[ABOVEORDER] = NO; + break; + } + + switch (i_p->used[BELOWORDER]) { + case YES: + setorder(PL_BELOW, i_p, f_p); + f_p->used[BELOWORDER] = YES; + break; + case UNSET: + f_p->used[BELOWORDER] = NO; + break; + } + + switch (i_p->used[BETWEENORDER]) { + case YES: + setorder(PL_BETWEEN, i_p, f_p); + f_p->used[BETWEENORDER] = YES; + break; + case UNSET: + f_p->used[BETWEENORDER] = NO; + break; + } + + SETPARM(pedstyle, PEDSTYLE) + + SETPARM(chorddist, CHORDDIST) + + SETPARM(dist, DIST) + + SETPARM(dyndist, DYNDIST) + + switch (i_p->used[LABEL]) { + case YES: + /* if it was already used, free old label */ + if (f_p->used[LABEL] == YES && f_p->label != 0) { + FREE(f_p->label); + f_p->label = 0; + } + + /* set up new label */ + MALLOCA(char, f_p->label, strlen(i_p->label) + 1); + (void)strcpy(f_p->label, i_p->label); + + f_p->used[LABEL] = YES; + break; + case UNSET: + /* if it was already used, free old label */ + if (f_p->used[LABEL] == YES && f_p->label != 0) { + FREE(f_p->label); + f_p->label = 0; + } + f_p->used[LABEL] = NO; + break; + } + + switch (i_p->used[LABEL2]) { + case YES: + /* if it was already used, free old label2 */ + if (f_p->used[LABEL2] == YES && f_p->label2 != 0) { + FREE(f_p->label2); + f_p->label2 = 0; + } + + /* set up new label */ + MALLOCA(char, f_p->label2, strlen(i_p->label2) + 1); + (void)strcpy(f_p->label2, i_p->label2); + + f_p->used[LABEL2] = YES; + break; + case UNSET: + /* if it was already used, free old label2 */ + if (f_p->used[LABEL2] == YES && f_p->label2 != 0) { + FREE(f_p->label2); + f_p->label2 = 0; + } + f_p->used[LABEL2] = NO; + break; + } + + /* + * ========== ITEMS FOR SCORE, STAFF, AND VOICE CONTEXT =========== + */ + switch (i_p->used[VISIBLE]) { + case YES: + f_p->visible = i_p->visible; + f_p->hidesilent = i_p->hidesilent; + f_p->used[VISIBLE] = YES; + break; + case UNSET: + f_p->used[VISIBLE] = NO; + } + + switch (i_p->used[BEAMSTLIST]) { + case YES: + /* if it was already used, free old list if present */ + if (f_p->used[BEAMSTLIST] == YES && f_p->nbeam != 0) { + FREE(f_p->beamstlist); + FREE(f_p->subbeamstlist); + f_p->beamstlist = 0; + f_p->subbeamstlist = 0; + } + + /* set up new list */ + f_p->nbeam = i_p->nbeam; + f_p->beamrests = i_p->beamrests; + f_p->beamspaces = i_p->beamspaces; + f_p->nsubbeam = i_p->nsubbeam; + /* the +1 is to guard against allocating 0 */ + MALLOCA(RATIONAL, f_p->beamstlist, f_p->nbeam + 1); + MALLOCA(RATIONAL, f_p->subbeamstlist, f_p->nsubbeam + 1); + for (n = 0; n < f_p->nbeam; n++) { + f_p->beamstlist[n] = i_p->beamstlist[n]; + } + for (n = 0; n < f_p->nsubbeam; n++) { + f_p->subbeamstlist[n] = i_p->subbeamstlist[n]; + } + + f_p->used[BEAMSTLIST] = YES; + break; + case UNSET: + /* if it was already used, free old list if present */ + if (f_p->used[BEAMSTLIST] == YES && f_p->nbeam != 0) { + FREE(f_p->beamstlist); + FREE(f_p->subbeamstlist); + f_p->beamstlist = 0; + f_p->subbeamstlist = 0; + } + f_p->used[BEAMSTLIST] = NO; + break; + } + + switch (i_p->used[BEAMSLOPE]) { + case YES: + f_p->beamfact = i_p->beamfact; + f_p->beammax = i_p->beammax; + f_p->used[BEAMSLOPE] = YES; + break; + case UNSET: + f_p->used[BEAMSLOPE] = NO; + break; + } + + SETPARM(pad, PAD) + + SETPARM(stemlen, STEMLEN) + + SETPARM(stemshorten, STEMSHORTEN) + + SETPARM(defoct, DEFOCT) + + switch (i_p->used[TIMEUNIT]) { + case YES: + f_p->timeunit = i_p->timeunit; + f_p->timelist_p = i_p->timelist_p; + f_p->used[TIMEUNIT] = YES; + break; + case UNSET: + f_p->used[TIMEUNIT] = NO; + } + + SETPARM(swingunit, SWINGUNIT) + + SETPARM(release, RELEASE) + + SETPARM(ontheline, ONTHELINE) + + SETPARM(tabwhitebox, TABWHITEBOX) + + switch (i_p->used[NOTEHEADS]) { + case YES: + for (n = 0; n < 7; n++) { + f_p->noteheads[n] = i_p->noteheads[n]; + } + f_p->used[NOTEHEADS] = YES; + break; + case UNSET: + f_p->used[NOTEHEADS] = NO; + break; + } +} + +/* + * Name: setssvstate() + * + * Abstract: Set the static SSVs to the state for a given place in the MML. + * + * Returns: void + * + * Description: This function, given any structure from the main linked list, + * initializes the static SSVs, and then runs through the MLL up + * to just before that point, assigning SSVs. It assigns not only + * the SSVs in the MLL, but also the timed SSVs hanging off + * barlines. You can pass a null pointer, and then it will go + * through the whole MLL. + */ + +void +setssvstate(mainll_p) + +struct MAINLL *mainll_p; /* place in the MLL to stop */ + +{ + struct MAINLL *mll_p; /* for looping through MLL */ + struct TIMEDSSV *tssv_p; /* for looping through TIMEDSSV lists*/ + + + initstructs(); + for (mll_p = Mainllhc_p; mll_p != 0 && mll_p != mainll_p; + mll_p = mll_p->next) { + switch (mll_p->str) { + case S_SSV: + /* assign this normal input SSV */ + asgnssv(mll_p->u.ssv_p); + break; + case S_BAR: + /* assign each timed SSV, if any */ + for (tssv_p = mll_p->u.bar_p->timedssv_p; tssv_p != 0; + tssv_p = tssv_p->next) { + asgnssv(&tssv_p->ssv); + } + break; + } + } +} + +/* + * Name: setorder() + * + * Abstract: Assign an "order" field from an input SSV to a fixed one. + * + * Returns: void + * + * Description: This function is called by asgnssv() to assign to the + * appropriate part of the markorder array, based on above, below, + * or between. + */ + +static void +setorder(place, i_p, f_p) + +int place; /* PL_* */ +struct SSV *i_p; /* input SSV structure to be copied from */ +struct SSV *f_p; /* ptr to fixed SSV structure to copy into */ + +{ + int m; /* mark (MK_*) */ + int stk; /* stacking order */ + + + /* + * First assign all the marks' stacking orders as given. Keep track of + * the highest stacking order number found. + */ + stk = 0; + for (m = 0; m < NUM_MARK; m++) { + f_p->markorder[place][m] = i_p->markorder[place][m]; + + if (f_p->markorder[place][m] > stk) + stk = f_p->markorder[place][m]; + } + + /* + * For every mark type that the user didn't list, the stacking order is + * now 0. Set all these to default settings, higher than all the ones + * the user listed, but in the same order as Defmarkorder. They will + * all be separate numbers, none set equal, unlike Defmarkorder, where + * some are equal. + */ + for (m = 0; m < NUM_MARK; m++) { + if (f_p->markorder[place][m] == 0) { + f_p->markorder[place][m] = ++stk; + } + } +}