+/* 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 <stdio.h>
+#include <string.h>
+
+#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));
+\f
+/*
+ * 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 */
+ }
+}
+\f
+/*
+ * 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;
+ }
+}
+\f
+/*
+ * 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 */
+}
+\f
+/*
+ * 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;
+ }
+ }
+}
+\f
+/*
+ * 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);
+}
+\f
+/*
+ * 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);
+}
+\f
+/*
+ * 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;
+ }
+}
+\f
+/*
+ * 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;
+ }
+ }
+}
+\f
+/*
+ * 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;
+ }
+ }
+}