/************************************************* * The PMW Music Typesetter - 3rd incarnation * *************************************************/ /* Copyright (c) Philip Hazel, 1991 - 2020 */ /* Written by Philip Hazel, starting November 1991 */ /* This file last modified: April 2020 */ /* This file contains code for miscellanous functions used when paginating, displaying and printing */ #include "pmwhdr.h" #include "outhdr.h" #include "pagehdr.h" /* Tables of accidental height/depths */ /* - ## $ $$ % # */ static int accdowntab[] = { 0, 0, 0, 0, 4000, 4000 }; static int accuptab[] = { 0, 0, 4000, 4000, 4000, 4000 }; /* Tables used by the ybound function */ static int accboundtable[] = { 4000, 0, 0, 4000, 0, -4000, -4000, 0 }; static int resttable[] = { 12000, 12000, 10000, 16000, 12000, 12000, 16000, 16000, 8000, 10000, 8000, 2000, 4000, 0, 0, -4000 }; /************************************************* * Convert character value to UTF-8 * *************************************************/ /* This function takes an integer value in the range 0 - 0x7fffffff and encodes it as a UTF-8 character in 0 to 6 bytes. Arguments: cvalue the character value buffer pointer to buffer for result - at least 6 bytes long Returns: number of characters placed in the buffer */ int misc_ord2utf8(int cvalue, uschar *buffer) { register int i, j; for (i = 0; i < 6; i++) if (cvalue <= utf8_table1[i]) break; buffer += i; for (j = i; j > 0; j--) { *buffer-- = 0x80 | (cvalue & 0x3f); cvalue >>= 6; } *buffer = utf8_table2[i] | cvalue; return i + 1; } /************************************************* * Return width of key signature * *************************************************/ /* This function checks for a special string for the key signature; otherwise it finds the number of accidentals in a key signature, and returns an appropriate printing width. In both cases this is the width for a 10-point stave (appropriate adjustment happens in the calling function when needed). Arguments: key the key signature clef the clef - used only to check for a special Returns: the printing width */ int misc_keywidth(int key, int clef) { pkeystr *pk; int width; int key63 = key & 63; DEBUG(("misc_keywidth() key=%d clef=%d\n", key, clef)); for (pk = main_printkey; pk != NULL; pk = pk->next) { if (key63 == pk->key && clef == pk->clef && pk->movt_number <= curmovt->number) break; } /* Printkey applies */ if (pk != NULL) { width = string_width((key>63)? pk->cstring : pk->string, font_mf, 10000); } /* Conventional key signature */ else if (key63 < X_key) { int n = main_keysigtable[key63]; width = abs(n) * (curmovt->accspacing)[(key>63)? ac_natural : (n>0)?ac_sharp : ac_flat]; } /* Custom key signature */ else { int i; uschar *p = main_xkeys[key63 - X_key]; width = 0; for (i = 0; i < 10; i++) { int ch = p[i] & 0x7f; if (ch == ac_none) continue; width += curmovt->accspacing[ch]; } } DEBUG(("misc_keywidth() = %d\n", width)); return width; } /************************************************* * Return width of time signature * *************************************************/ /* This function sets up the appropriate strings for a time signature and then returns the width of the longer one. Argument: the time signature Returns: the printing width */ int misc_timewidth(int ts) { ptimestr *pt = main_printtime; int *fontvector = (curmovt->fontsizes)->fontsize_text; int offsetn, offsetd, yieldn, yieldd; uschar vn[16]; uschar vd[16]; uschar *topstring = vn; uschar *botstring = vd; /* If not printing time signatures, return zero width */ if (!curmovt->showtime) return 0; /* First see if this time signature has special strings specified for its printing. The specification must have happened in this movement or earlier to be applicable. */ while (pt != NULL) { if (pt->time == ts && pt->movt_number <= curmovt->number) break; pt = pt->next; } /* If found special case, get strings and sizes from it */ if (pt != NULL) { offsetn = pt->offsettop; offsetd = pt->offsetbot; topstring = pt->top; botstring = pt->bot; } /* Default printing for this time signature. First mask off the multiplier, then check for the special cases of C and A. */ else { ts &= 0xFFFF; /* C and A have a known width */ if (ts == time_common || ts == time_cut) return 7500; /* Non-special case - set up numerator and denominator, in the time signature font. */ sprintf(CS vn, "%d", ts >> 8); sprintf(CS vd, "%d", ts & 255); offsetn = offsetd = ff_offset_ts; } /* We now have in topstring and botstring two strings to print. The yield is the greater of the two lengths, except when only the numerator is to be printed. */ yieldn = string_width(topstring, font_bf, fontvector[offsetn]); if (!curmovt->showtimebase) return yieldn; yieldd = string_width(botstring, font_bf, fontvector[offsetd]); return (yieldn > yieldd)? yieldn : yieldd; } /************************************************* * Find next note item in a bar * *************************************************/ /* If the second argument is not null, we use it to return any ornament value. We always advance by at least one item. The initial item need not be a note. Take care in case it is a Jump or End. Arguments: p pointer to the current item orn if not NULL, return the ornament that preceded the note Returns: pointer to the next note item or NULL if there isn't one */ b_notestr * misc_nextnote(b_notestr *p, int *orn) { int type = p->type; if (type > b_Jump) /* Advance only if not b_Jump or b_End */ { p = (b_notestr *)((uschar *)p + length_table[p->type]); type = p->type; } for (;;) { switch(type) { case b_End: /* In the special case of beaming over */ if (!beam_overbeam) return NULL; /* a barline, continue past the end. */ break; /* There will always be another bar. */ case b_note: return p; case b_ornament: if (orn != NULL) *orn = ((b_ornamentstr *)p)->ornament; break; case b_Jump: p = (b_notestr *)(((b_Jumpstr *)p)->next); break; } p = (b_notestr *)((uschar *)p + length_table[type]); type = p->type; } return p; /* keep compiler happy - never obeyed */ } /************************************************* * Make copy of continued data structure * *************************************************/ /* This is called at the start of outputting a system. It remembers the parameters that are current at that point. At the start of a new system, incslur is passed as TRUE, causing the section numbers of slurs to be increased. (At the start of the first system, it is FALSE.) The result of the function is a pointer to a vector of contstr structures, one for each stave. Arguments: p pointer to the current contstr count the highest stave number that we need to save (can be 0) incslur when TRUE, increment the section number for each slur Returns: pointer to a vector of contstrs */ contstr * misc_copycontstr(contstr *p, int count, BOOL incslur) { int i; int len = (count+1)*sizeof(contstr); contstr *yield = store_Xget(len); contstr *q = yield; /* Set up a copy of the vector */ memcpy(q, p, len); /* For each member of the vector, copy the chained-on data blocks */ for (i = 0; i <= count; i++) { hairpinstr *h = p->hairpin; nbarstr *nb = p->nbar; nbarstr **nbprev = &(q->nbar); slurstr *s = p->slurs; slurstr **sprev = &(q->slurs); ulaystr *u = p->ulay; ulaystr **uprev = &(q->ulay); obeamstr *o = p->overbeam; /* Copy the hairpin structure */ if (h != NULL) { hairpinstr *hh = store_Xget(sizeof(hairpinstr)); hh[0] = h[0]; q->hairpin = hh; } /* Copy the overbeam structure */ if (o != NULL) { obeamstr *oo = store_Xget(sizeof(obeamstr)); oo[0] = o[0]; q->overbeam = oo; } /* Copy the nth time bar structures */ while (nb != NULL) { nbarstr *nbb = store_Xget(sizeof(nbarstr)); nbb[0] = nb[0]; *nbprev = nbb; nbprev = &(nbb->next); nb = nb->next; } *nbprev = NULL; /* Copy the chain of slur structures. Each one may contain a sub-chain of gap structures. Increment the section number for each slur when requested - this happens when moving to a new system. */ while (s != NULL) { slurstr *ss = store_Xget(sizeof(slurstr)); gapstr *g = s->gaps; gapstr **gprev = &(ss->gaps); if (incslur) s->section += 1; ss[0] = s[0]; while (g != NULL) { gapstr *gg = store_Xget(sizeof(gapstr)); gg[0] = g[0]; *gprev = gg; gprev = &(gg->next); g = g->next; } *gprev = NULL; *sprev = ss; sprev = &(ss->next); s = s->next; } *sprev = NULL; /* Copy the chain of ulay structures */ while (u != NULL) { ulaystr *uu = store_Xget(sizeof(ulaystr)); uu[0] = u[0]; *uprev = uu; uprev = &(uu->next); u = u->next; } *uprev = NULL; /* Advance to the next pair of vector entries */ p++; q++; } return yield; } /************************************************* * Free a continued data structure * *************************************************/ /* This is called at the end of outputting a system Arguments: p pointer to the contstr vector count number of staves saved Returns: nothing */ void misc_freecontstr(contstr *p, int count) { int i; contstr *q = p; /* For each member of the vector, free the chained-on data blocks */ for (i = 0; i <= count; i++) { hairpinstr *h = q->hairpin; nbarstr *nb = q->nbar; slurstr *s = q->slurs; ulaystr *u = q->ulay; obeamstr *b = q->overbeam; if (h != NULL) store_free(h); if (b != NULL) store_free(b); while (nb != NULL) { nbarstr *nbb = nb->next; store_free(nb); nb = nbb; } while (s != NULL) { slurstr *ss = s->next; gapstr *g = s->gaps; while (g != NULL) { gapstr *gg = g->next; store_free(g); g = gg; } store_free(s); s = ss; } while (u != NULL) { ulaystr *uu = u->next; store_free(u); u = uu; } /* Advance to the next vector entry */ q++; } store_free(p); } /************************************************* * Common actions on cont data * *************************************************/ /* Certain items require the same actions when encountered in the advance scan of each system by page_setcont() as when encountered while actually outputting a system. These actions are handled in this function. Argument: pointer to the item Returns: nothing */ void misc_commoncont(bstr *p) { switch(p->type) { /* Adjust bowing flags */ case b_bowing: bar_cont->flags &= ~cf_bowingabove; if (((b_bowingstr *)p)->value) bar_cont->flags |= cf_bowingabove; break; /* Set note head style */ case b_noteheads: { int style = ((b_noteheadsstr *)p)->value; if (style >= nh_only) bar_cont->flags |= cf_noteheads; else bar_cont->flags &= ~cf_noteheads; if (style != nh_only) bar_cont->noteheadstyle = style; } break; /* Switch notes on/off */ case b_notes: bar_cont->flags &= ~cf_notes; if (((b_notesstr *)p)->value) bar_cont->flags |= cf_notes; break; /* Switch triplets on/off */ case b_tripsw: bar_cont->flags &= ~cf_triplets; if (((b_tripswstr *)p)->value) bar_cont->flags |= cf_triplets; break; /* Set slope for next beam */ case b_slope: beam_forceslope = ((b_slopestr *)p)->value; break; } } /************************************************* * See if there is pending {und,ov}erlay * *************************************************/ /* These functions are called by page_setcont() below. Argument: nothing Returns: TRUE if there is pending underlay */ static BOOL pendulay(void) { ulaystr *u = bar_cont->ulay; while (u != NULL) { if (u->level < olay_offset) return TRUE; u = u->next; } return FALSE; } /* Argument: nothing Returns: TRUE if there is pending overlay */ static BOOL pendolay(void) { ulaystr *u = bar_cont->ulay; while (u != NULL) { if (u->level >= olay_offset) return TRUE; u = u->next; } return FALSE; } /************************************************* * Shorten stem of note if appropriate * *************************************************/ /* This function is called from page_setcont(). We have to flag the note and not do it twice, to cope with repeated bars. Arguments: p the note upflag TRUE if the stem is up Returns: nothing */ static void shorten_stem(b_notestr *tp, int upflag) { int shorten = 0; if (upflag) { if (tp->spitch > 136) shorten = 250*(tp->spitch - 136); } else { if (tp->spitch < 136) shorten = 250*(136 - tp->spitch); } if (shorten != 0 && (tp->flags & nf_shortened) == 0) { tp->yextra -= (shorten <= curmovt->shorten)? shorten : curmovt->shorten; tp->flags |= nf_shortened; } } /************************************************* * Advance cont data to end of system * *************************************************/ /* This is called when paginating, when the end of a system has been chosen. The working cont structure is in page_cont. A "snapshot" copy has been taken which represents the state at the start of the system. We must now advance through the system to get ready for the next snapshot. The system we are dealing with is in page_sysblock. This pass also handles changes of stave and system spacing, setting up new data blocks as necessary. It also handles changes of page number. While we are doing this, we can compute the tiecount values to go in the tie control blocks. We cannot do this when reading in, because we don't know the stem direction at the time the tie block is created. The count value is the number of ties that take the opposite stemflag to the note. We also have to do a lot of work if we encounter a beamed-over bar line at the end of a system. Arguments: none Returns: nothing */ void page_setcont(void) { BOOL hadssnext = FALSE; BOOL hadsshere = FALSE; int bar = 0; int stave; for (stave = 0; stave <= page_lastwanted; stave++) { if (mac_teststave(curmovt->staves, stave)) { BOOL no_ulay, no_olay; bar_cont = page_cont + stave; no_ulay = !pendulay(); no_olay = !pendolay(); for (bar = page_sysblock->barstart; bar <= page_sysblock->barend; bar++) { bstr *p = ((curmovt->stavetable)[stave])->barindex[bar]; if (p != NULL && p->type != b_End) { int moff = 0; int beammoff = 0; int lastAlevel = 0; /* Above level */ int lastBlevel = 0; /* Below level */ int type = p->type; int chordcount = 0; BOOL hadulay = FALSE; BOOL hadolay = FALSE; BOOL upflag = FALSE; b_notestr *beamfirst = NULL; beam_forceslope = BIGNUMBER; for (;;) { switch(type) { case b_Jump: p = (bstr *)(((b_Jumpstr *)p)->next); break; case b_End: if (((b_Endstr *)p)->overbeam && bar == page_sysblock->barend && beamfirst != NULL) /* We have a beam that may be carried over the end of a bar. We have to fudge up various values that will be set when the beam is drawn, in order to run setupbeam() so that we can find the beam's slope. This is preserved in the cont structure for use at the start of the system. */ { barposstr *bp = curmovt->posvector + bar; out_postable = out_posptr = bp->vector; out_poslast = out_postable + bp->count - 1; out_bar = bar; out_stave = stave; out_gracenotes = FALSE; out_grace_fudge = 0; out_sysblock = page_sysblock; beam_offsetadjust = 0; n_upflag = (beamfirst->flags & nf_stemup) != 0; n_upfactor = n_upflag? (+1):(-1); (void)out_setupbeam(beamfirst, beammoff, TRUE, FALSE); } break; case b_footnote: { headstr *h = &(((b_footnotestr *)p)->h); headstr *hh = h; headstr *lh = NULL; page_justifyheading(h); while (hh != NULL) { lh = hh; page_newfootnotedepth += hh->space; hh = hh->next; } if (page_newfootnotes == NULL) page_newfootnotes = h; else { page_lastnewfootnote->next = h; h->spaceabove = curmovt->footnotesep; } page_lastnewfootnote = lh; } break; case b_slur: (void)misc_setstartslur(p); break; case b_endslur: { slurstr *s = misc_getendslur(p); if (s == NULL) error_moan(ERR62, ((b_endslurstr *)p)->id, bar, stave); else store_free(s); } break; case b_tie: { b_tiestr *pp = (b_tiestr *)p; bar_cont->tie = pp; if (pp->abovecount == 0 && pp->belowcount == 0) { if (upflag) pp->belowcount = (chordcount > 1)? chordcount/2 : 1; else pp->abovecount = (chordcount+1)/2; } if (pp->abovecount == 0 && pp->belowcount < chordcount) pp->abovecount = chordcount - pp->belowcount; else if (pp->belowcount == 0 && pp->abovecount < chordcount) pp->belowcount = chordcount - pp->abovecount; /* Allow for tie below on underlay level*/ if (lastBlevel != 0 && pp->belowcount > 0) { lastBlevel -= 3500; if (page_sysblock->ulevel[stave] > lastBlevel) page_sysblock->ulevel[stave] = lastBlevel; } /* Allow for tie above on overlay level*/ if (lastAlevel != 0 && pp->abovecount > 0) { lastAlevel += 3500; if (page_sysblock->olevel[stave] < lastAlevel) page_sysblock->olevel[stave] = lastAlevel; } } break; /* For notes/chords we calculate the {und,ov}erlay pitch, and also set flags for a possible subsequent tie. Skip rests, of course, and also skip the underlay calculation for notes that neither have their own text nor have an extender or hyphens under them (indicated by the existence of ulaystr data). Keep track of the last start-of-beam note, in case we are in an end-of-line bar with a continued beam. Handle automatic stem length adjustment. */ case b_note: bar_cont->tie = NULL; if (((b_notestr *)p)->spitch != 0) { b_notestr *tp = (b_notestr *)p; usint acflags = tp->acflags; int flags = tp->flags; int apitch = tp->spitch; int bpitch = tp->spitch; int acc = tp->acc; int stemlength; if (tp->notetype >= quaver) { if (beamfirst == NULL) { beamfirst = tp; beammoff = moff; } } else { beamfirst = NULL; beam_forceslope = BIGNUMBER; } upflag = (flags & nf_stemup) != 0; if (curmovt->shorten && tp->length != 0) shorten_stem(tp, upflag); stemlength = tp->yextra; chordcount = 1; mac_advancechord(tp); while (tp->type == b_chord) { chordcount++; flags |= tp->flags; acflags |= tp->acflags; if (tp->spitch > apitch) { apitch = tp->spitch; acc = tp->acc; } if (tp->spitch < bpitch) { bpitch = tp->spitch; acc = tp->acc; } if (curmovt->shorten && tp->length != 0) shorten_stem(tp, upflag); if (abs(tp->yextra) > abs(stemlength)) stemlength = tp->yextra; mac_advancechord(tp); } /* Now do the underlay/overlay stuff */ lastAlevel = (apitch - 124)*1000 + ((!upflag || (flags & nf_stem) == 0)? (5*accuptab[acc])/4 : (14000 + stemlength)); lastBlevel = (bpitch - 129)*1000 - ((hadulay || no_ulay)? page_ulaysize : 4000) - ((upflag || (flags & nf_stem) == 0)? accdowntab[acc]/2 : (13000 + stemlength)); /* Allow for dynamics */ if ((acflags & af_dynamics) != 0) { int dynextraA = accboundtable[ (((acflags & af_opposite) == 0)? 0:1) + (upflag? 2:0)]; int dynextraB = accboundtable[4 + (((acflags & af_opposite) == 0)? 0:1) + (upflag? 2:0)]; lastAlevel += dynextraA; lastBlevel += dynextraB; /* That's all if no relevant accent, or the accent falls inside the staff; otherwise make sure the level is suitably outside the staff. */ if (dynextraA != 0 && (acflags & af_dynoutside) != 0) lastAlevel += dynextraA; /* these are bigger accents */ if (dynextraB != 0 && (acflags & af_dynoutside) != 0) lastBlevel += dynextraB; /* these are bigger accents */ } /* Impose {min,max}imum level and keep {high,low}est level for the line if appropriate */ if (lastAlevel < 20000) lastAlevel = 20000; if (lastBlevel > -page_ulaysize - 1000) lastBlevel = -page_ulaysize - 1000; if (no_olay || hadolay || pendolay()) { if (page_sysblock->olevel[stave] < lastAlevel) page_sysblock->olevel[stave] = lastAlevel; } if (no_ulay || hadulay || pendulay()) { if (page_sysblock->ulevel[stave] > lastBlevel) page_sysblock->ulevel[stave] = lastBlevel; } /* Turn off value if don't want tie noticed */ if (upflag && (flags & nf_stem) != 0) lastAlevel = 0; if (!upflag && (flags & nf_stem) != 0) lastBlevel = 0; } /* Deal with rests - kill any outstanding underlay block for extensions, but not for hyphens. */ else { ulaystr **uu = &(bar_cont->ulay); ulaystr *u = *uu; while (u != NULL) { if (u->type == '=') { *uu = u->next; store_free(u); } else uu = &(u->next); u = *uu; } lastAlevel = lastBlevel = 0; if (((b_notestr *)p)->notetype < quaver) beamfirst = NULL; } /* Notes and rests dealt with */ moff += ((b_notestr *)p)->length; hadulay = hadolay = FALSE; break; /* Deal with beam breaks */ case b_beambreak: beamfirst = NULL; beam_forceslope = BIGNUMBER; break; /* Deal with resets */ case b_reset: moff = 0; break; /* Create or delete hairpin pending blocks */ case b_hairpin: { b_hairpinstr *h = (b_hairpinstr *)p; if (h->opt == 0) { if (bar_cont->hairpin != NULL) { store_free(bar_cont->hairpin); bar_cont->hairpin = NULL; } } else out_setstarthairpin(h, 0); } break; /* For nth time bars we need only keep one block, since continued cases won't be printing the numbers. */ case b_nbar: if (bar_cont->nbar == NULL) { b_nbarstr *b = (b_nbarstr *)p; out_setstartnbar(b, 0, 0); } break; case b_all: if (bar_cont->nbar != NULL) { store_free(bar_cont->nbar); bar_cont->nbar = NULL; } break; case b_setclef: bar_cont->clef = ((b_setclefstr *)p)->value; break; case b_clef: bar_cont->clef = ((b_clefstr *)p)->trueclef; break; case b_settime: bar_cont->time = ((b_settimestr *)p)->value; break; case b_time: bar_cont->time = ((b_timestr *)p)->time; break; case b_setkey: bar_cont->key = ((b_setkeystr *)p)->value; break; case b_key: bar_cont->key = ((b_keystr *)p)->key; break; case b_sgabove: /* retrospective change of system gap */ { b_sgstr *pp = (b_sgstr *)p; int v = pp->value; sysblock *pb = curpage->sysblocks; if (pb != NULL) /* There may be a previous system */ { while (pb->next != NULL) pb = pb->next; /* Find last block */ if(pb->type == sh_system) /* Ignore if heading */ { curpage->spaceleft += pb->systemgap; if (pp->opt == '+') pb->systemgap += v; else pb->systemgap = v; curpage->spaceleft -= pb->systemgap; } } } break; case b_sghere: /* immediate change of system gap */ { b_sgstr *pp = (b_sgstr *)p; int v = pp->value; if (pp->opt == '+') page_sysblock->systemgap += v; else page_sysblock->systemgap = v; } break; case b_sgnext: /* delayed change of system gap */ { b_sgstr *pp = (b_sgstr *)p; int v = pp->value; if (pp->opt == '+') page_sgnext += v; else page_sgnext = v; } break; case b_ssabove: /* change ensured stave spacing above */ { int i; b_ssstr *pp = (b_ssstr *)p; int s = pp->stave; int v = pp->value; if (page_ssehere == NULL) { page_ssehere = store_Xget((page_lastwanted+1)*sizeof(int)); for (i = 0; i <= page_lastwanted; i++) page_ssehere[i] = 0; } if (s == 0) /* Stave 0 => all staves */ { for (i = 1; i < page_lastwanted; i++) { if (pp->opt == '+') page_ssehere[i] += v; else page_ssehere[i] = v; } } else { if (pp->opt == '+') page_ssehere[s] += v; else page_ssehere[s] = v; } } break; case b_sshere: /* immediate change of stave spacing */ { b_ssstr *pp = (b_ssstr *)p; int s = pp->stave; int v = pp->value; if (!hadsshere) { page_sysblock->stavespacing = store_copy(page_sysblock->stavespacing); hadsshere = TRUE; } if (s == 0) /* Stave 0 => all staves */ { int i; for (i = 1; i < page_lastwanted; i++) { if (pp->opt == '+') page_sysblock->stavespacing[i] += v; else page_sysblock->stavespacing[i] = v; } } else if (pp->opt == '+') { page_sysblock->stavespacing[s] += v; if (page_ssehere != NULL) { int i; for (i = s+1; i <= curmovt->laststave; i++) { if (mac_teststave(page_sysblock->notsuspend, i)) { page_ssehere[i] += v; break; } } } } else page_sysblock->stavespacing[s] = v; } break; case b_ssnext: /* delayed change of stave spacing */ { b_ssstr *pp = (b_ssstr *)p; int s = pp->stave; int v = pp->value; if (!hadssnext) { page_ssnext = store_copy(page_ssnext); hadssnext = TRUE; } if (s == 0) { int i; for (i = 1; i < page_lastwanted; i++) { if (pp->opt == '+') page_ssnext[i] += v; else page_ssnext[i] = v; } } else if (pp->opt == '+') page_ssnext[s] += v; else page_ssnext[s] = v; } break; /* Changes to vertical justification and page positioning are put into system-specific variables, as it isn't known at the time to which page they will apply. Changes to horizontal justification are put directly into the justifyLR variable, as they apply to this system. */ case b_justify: { b_justifystr *pp = (b_justifystr *)p; int horiz = pp->side & (just_left|just_right); int vert = pp->side & (just_top|just_bottom); if (horiz) page_justifyLR = (pp->opt == '+')? (page_justifyLR | pp->side) : (page_justifyLR & ~pp->side); if (vert) { int oldjustify = (page_sys_justify == -1)? page_justify : page_sys_justify; page_sys_justify = (pp->opt == '+')? (oldjustify | pp->side) : (oldjustify & ~pp->side); } } break; case b_page: { b_pagestr *pg = (b_pagestr *)p; int value = pg->value; if (pg->relative == '+') value += curpage->number; if (value < curpage->number) error_moan(ERR112, value, curpage->number, bar, stave); else curpage->number = value; } break; case b_pagebots: page_sys_botmargin = ((b_pagebotsstr *)p)->value; break; case b_pagetops: page_sys_topmargin = ((b_pagetopsstr *)p)->value; break; /* The only text we are interested in here is underlay; set up or remove continuation control blocks. */ case b_text: { b_textstr *t = (b_textstr *)p; if ((t->flags & text_ul) != 0 && (t->ulen != 1 || t->string[0] != '=')) { int c = t->string[t->ulen]; ulaystr **uu = &bar_cont->ulay; ulaystr *u = *uu; /* On hitting any {und,ov}erlay, clear the field and flag so that only relevant notes are counted. Flag the type for the next note. */ if ((t->flags & text_above) == 0) { hadulay = TRUE; if (no_ulay) { page_sysblock->ulevel[stave] = 0; no_ulay = FALSE; } } else { hadolay = TRUE; if (no_olay) { page_sysblock->olevel[stave] = 0; no_olay = FALSE; } } /* Find existing control block for this level */ while (u != NULL && u->level != t->ulevel) { uu = &u->next; u = *uu; } /* If control block needed, either carry on with this one or get a new one. */ if (c == '=' || c == '-') { if (u == NULL) { u = store_Xget(sizeof(ulaystr)); u->next = NULL; u->x = u->y = 0; u->level = t->ulevel; *uu = u; } u->type = c; u->htype = t->htype; } /* Else free an existing one */ else if (u != NULL) { *uu = u->next; store_free(u); } } } break; /* Handle changes of underlay level */ case b_ulevel: { b_ulevelstr *pp = (b_ulevelstr *)p; page_ulevel[stave] = (pp->opt)? BIGNUMBER : pp->value; } break; case b_ulhere: page_ulhere[stave] = ((b_ulherestr *)p)->value; break; /* Handle changes of overlay level */ case b_olevel: { b_olevelstr *pp = (b_olevelstr *)p; page_olevel[stave] = (pp->opt)? BIGNUMBER : pp->value; } break; case b_olhere: page_olhere[stave] = ((b_olherestr *)p)->value; break; /* Suspend sets flag for start of next system */ case b_suspend: mac_clearstave(page_accepteddata->notsuspend, stave); break; /* We must cope with resume following suspend in the same system. */ case b_resume: mac_setstave(page_accepteddata->notsuspend, stave); break; /* Actions that are common to this scan and to the output scan are held in a separate function. */ default: misc_commoncont(p); break; } if (type == b_End) break; p = (bstr *)((uschar *)p + length_table[type]); type = p->type; } } } } } /* At the end of a system we must check on the {und,ov}erlay continuation control blocks. For each stave that has such a block (or blocks) we must look at the next bar. If it does not exist, is emtpy, or starts with a rest, we must kill the continuation block(s) for extender lines. Don't do this for hyphen blocks - another syllable is always expected and there are odd cases when these do go over rests, etc. We have to use a GOTO to get out of a switch within a loop. In the same loop we can deal with {und,ov}erlay levels. */ for (stave = 0; stave <= page_lastwanted; stave++) { ulaystr **uu; ulaystr *u; bar_cont = page_cont + stave; if (mac_teststave(curmovt->staves, stave) && bar_cont->ulay != NULL) { if (bar <= curmovt->barcount) { bstr *p = ((curmovt->stavetable)[stave])->barindex[bar]; if (p != NULL && p->type != b_End) { int type = p->type; while (type != b_End) { switch(type) { case b_Jump: p = (bstr *)(((b_Jumpstr *)p)->next); break; case b_note: if (((b_notestr *)p)->spitch != 0) goto NOREMOVE; else goto REMOVE; } p = (bstr *)((uschar *)p + length_table[type]); type = p->type; } } } /* Remove the underlay blocks for extender lines. */ REMOVE: uu = &(bar_cont->ulay); u = *uu; while (u != NULL) { if (u->type == '=') { *uu = u->next; store_free(u); } else uu = &(u->next); u = *uu; } } NOREMOVE: /* Handle changes to {und,ov}erlay levels */ if (page_ulevel[stave] != BIGNUMBER) page_sysblock->ulevel[stave] = page_ulevel[stave]; page_sysblock->ulevel[stave] += page_ulhere[stave]; if (page_olevel[stave] != BIGNUMBER) page_sysblock->olevel[stave] = page_olevel[stave]; page_sysblock->olevel[stave] += page_olhere[stave]; } } /************************************************** * Compute bounding y value, with accents and ties * **************************************************/ /* The parameters of the note are in the n_* variables. The yield is a y value relative to the staff base, with positive values going upwards. At the notehead end of a note, it is one point away from the notehead. We calculate in stave-points. The dynflag variable request inclusion of *all* dynamics; otherwise include only those that go inside the stave (staccato, ring, bar). This call is used when printing those that go outside the stave. Arguments: below TRUE for the bottom bound, FALSE for the top bound tie a tiestr for the note, or NULL if not tied accflag TRUE if there's an accidental dynflag TRUE if there are dynamics Returns: the y value */ int misc_ybound(BOOL below, b_tiestr *tie, BOOL accflag, BOOL dynflag) { int yield; int extra = 0; int accextra = 0; int flags = n_flags; usint acflags = n_acflags; /* If this is a rest, the only parameter of interest is the rest level */ if (n_pitch == 0) return n_restlevel + resttable[n_notetype + (below? 8:0)]; /* Deal with a note; first calculate additional length for stem, if any */ if ((flags & nf_stem) != 0) { extra = mac_muldiv(n_stemlength + 12000, n_fontsize, 10000); if (n_beamed) { extra += 1000; /* Extra for all but the first note of steep downward beams when stems are up. */ if (n_upflag && beam_slope < 0 && n_lastnote != beam_first) extra += 5*abs(beam_slope); } } /* The basic value takes account of the appropriate pitch and, if relevant, any accidental. We remember, in accextra, additional space added here. It will be taken away if there is subsequent space added for an accent. */ if (below) { if (n_upflag) { extra = accflag? accdowntab[n_lastacc] : 0; accextra = -extra; } else { if (accflag && extra == 0) { extra = accdowntab[n_firstacc]; accextra = -extra; } } if (extra == 0) { extra = 1000; accextra = -extra; } yield = (n_minpitch - 130)*1000 - extra; } else { if (n_upflag) { if (accflag && extra == 0) { extra = accuptab[n_firstacc]; accextra = extra; } } else { extra = accflag? accuptab[n_lastacc] : 0; accextra = extra; } if (extra == 0) { extra = 1000; accextra = extra; } yield = (n_maxpitch - 126)*1000 + extra; } /* Allow for ties */ if (tie != NULL) { if (below) { if (tie->belowcount > 0 && (n_upflag || (flags & nf_stem) == 0)) yield -= 4000; } else { if (tie->abovecount > 0 && (!n_upflag || (flags & nf_stem) == 0)) yield += 4000; } } /* Allow for dynamics. First of all, get rid of bowing marks if they are not relevant. */ if (((bar_cont->flags & cf_bowingabove) != 0) == below) acflags &= ~(af_up | af_down); if ((acflags & af_dynamics) != 0 && (dynflag || (acflags & af_dyninside) != 0)) { int oppflag, dynextra; if ((acflags & (af_dynamics - af_up - af_down)) != 0) { oppflag = ((acflags & af_opposite) == 0)? 0:1; } else { if ((bar_cont->flags & cf_bowingabove) == 0) oppflag = n_upflag? 0:1; else oppflag = n_upflag? 1:0; } dynextra = accboundtable[oppflag + (n_upflag? 2:0) + (below? 4:0)]; /* If adding space for an accent, retract space for an accidental */ if (dynextra) yield += dynextra - accextra; /* That's all if no relevant accent, or the accent falls inside the staff; otherwise make sure the pitch is suitably outside the staff. */ if (dynextra != 0 && dynflag && (acflags & af_dynoutside) != 0) { yield += dynextra; /* these are bigger accents */ if (below) { if (yield > -10000) yield = -10000; } else if (yield < 22000) yield = 22000; } } return yield; } /* End of misc.c */