/************************************************* * The PMW Music Typesetter - 3rd incarnation * *************************************************/ /* Copyright (c) Philip Hazel, 1991 - 2020 */ /* Written by Philip Hazel, starting November 1991 */ /* This file last modified: August 2020 */ /* This file contains part V of the code for reading in a PMW score file - code for reading one note. */ #include "pmwhdr.h" #include "readhdr.h" #define ornset_trill 1 #define ornset_trem 2 #define ornset_mordturn 4 /* This table is used to convert from an accidental-less note in absolute units to a stave-relative note. Only "white" notes are ever used to index into this table. */ static uschar pitch_stave[] = { 4, 0, 6, 0, 8, 10, 0, 12, 0, 14, 0, 16, 18, 0, 20, 0, 22, 24, 0, 26, 0, 28, 0, 30, 32, 0, 34, 0, 36, 38, 0, 40, 0, 42, 0, 44, 46, 0, 48, 0, 50, 52, 0, 54, 0, 56, 0, 58, 60, 0, 62, 0, 64, 66, 0, 68, 0, 70, 0, 72, 74, 0, 76, 0, 78, 80, 0, 82, 0, 84, 0, 86, 88, 0, 90, 0, 92, 94, 0, 96, 0, 98, 0,100, 102, 0,104, 0,106,108, 0,110, 0,112, 0,114, 116, 0,118, 0,120,122, 0,124, 0,126, 0,128 }; /* This table is used to convert a pitch obtained fom the above table into a pitch relative to a stave, where 128 is the bottom line on the stave. This table is for "real" clefs only. */ static uschar pitch_clef[] = { 64, 68, 72, 76, 80, 84, 84, 88, 92 }; /* Tr S M A Te cBa Ba B DB */ /* These tables give the extra "accidental left" amounts for accidentals in brackets */ /* - ## $ $$ % # */ static int rbra_left[] = { 5800, 5800, 5300, 5300, 5800, 6800 }; static int sbra_left[] = { 5800, 6800, 6300, 6300, 6800, 6800 }; /* Static variables used for communication between read_note() and post_note() */ static int maxpitch, minpitch, chordcount; static int notetype, length, stemforce; static BOOL seconds; static tiedata this_tiedata[MAX_CHORDSIZE]; /* This variable is set true when the only notes in a bar so far are notated using "p" and "x" and are tied to their predecessors. It is used to determine whether a following "p" or "x" should have its accidentals printed or not. */ static BOOL onlytieddup; /* Used for copying ornaments for 'x' notes */ static b_ornamentstr *last_note_ornament; /************************************************* * Read pitch for [printpitch] * *************************************************/ /* This function reads a stave-relative pitch for use by the [printpitch] directive. It is placed here because it uses the tables in this module. The character is already current on entry. Arguments: none Returns: the pitch */ int read_stavepitch(void) { int pitch; read_ch = tolower(read_ch); if ('a' > read_ch || read_ch > 'g') { error_moan(ERR10, "Note letter"); return 128; } pitch = stave_octave + 36 + read_basicpitch[read_ch - 'a']; next_ch(); while (read_ch == '\'') { pitch += 12; next_ch(); } while (read_ch == '`' ) { pitch -= 12; next_ch(); } return pitch_stave[pitch] + pitch_clef[stave_clef]; } /************************************************* * Read optional move (or bracket) for ornament * *************************************************/ /* This is also used for dynamics. Arguments: ax address of the x movement, or NULL if not allowed ay address of the y movement af address of the bflags field, or NULL if not allowed Returns: nothing */ static void ornmove(int *ax, int *ay, uschar *af) { *ay = 0; if (ax != NULL) *ax = 0; if (af != NULL) *af = 0; while (read_ch == '/') { next_ch(); switch (read_ch) { case 'u': *ay += read_movevalue(); break; case 'd': *ay -= read_movevalue(); break; case 'l': case 'r': if (ax == NULL) error_moan(ERR10, "/u or /d"); else *ax += ((read_ch == 'r')?(+1):(-1)) * read_movevalue(); break; default: if (af == NULL) error_moan(ERR10, "/u, /d, /l, or /r"); else switch (read_ch) { case 'b': *af &= ~(DO_SBRA|DO_SKET); *af |= DO_RBRA | DO_RKET; break; case 'B': *af &= ~(DO_RBRA|DO_RKET); *af |= DO_SBRA | DO_SKET; break; case '(': *af &= ~DO_SBRA; *af |= DO_RBRA; break; case '[': *af &= ~DO_RBRA; *af |= DO_SBRA; break; case ')': *af &= ~DO_SKET; *af |= DO_RKET; break; case ']': *af &= ~DO_RKET; *af |= DO_SKET; break; default: error_moan(ERR10, "/u, /d, /l, /r, /b, /B, /(, /), /[, or /]"); break; } if (read_ch != '\\') next_ch(); /* \ means there was an error */ break; } } } /************************************************* * Read optional move or bracket for dynamic * *************************************************/ /* This function inserts a dynmove item into the data list. Argument: the dynamic Returns: nothing */ static void dynmove(int dynamic) { b_dynmovestr *d; if (read_ch != '/') return; d = store_getitem(b_dynmove); d->dynamic = dynamic; ornmove(&(d->x), &(d->y), &(d->bflags)); } /************************************************* * Output underlay text for one note * *************************************************/ /* This function processes underlay strings that have been saved up, inserting an appropriate b_text item into the data, with text(s) for one note. Arguments: none Returns: nothing */ static void do_underlay(void) { ulaypend **pp = &stave_pendulay; ulaypend *p = stave_pendulay; /* Loop for each "verse" */ while (p != NULL) { int endfont; uschar *s = p->string; b_textstr *q; if (p->halfway != 0 || p->offset != 0) { b_textXstr *xx = store_getitem(b_textX); xx->rotate = 0; xx->halfway = p->halfway; xx->offset = p->offset; p->halfway = 0; p->offset = 0; } q = store_getitem(b_text); q->font = endfont = p->font; q->size = p->size; q->htype = p->htype; q->ulevel = p->level; q->flags = p->flags; q->string = s; q->x = p->x; q->y = p->y; /* If we are at an equals sign, just output the one character; otherwise search for the end of the syllable. We must correctly skip over escapes, and also find the final font, to set for the next syllable. */ if (*s == '=') s++; else { int font, revert; uschar ss[20]; for (;;) { while (*s != '\\' && *s != '-' && *s != ' ' && *s != '=' && *s != 0) s++; if (*s != '\\') break; s = string_escape(++s, ss, &font, &revert); if (font >= 0 && !revert) endfont = font; } } /* Set string count - don't include a minus sign, but skip over it */ q->ulen = s - p->string; if (*s == '-') s++; /* Skip spaces between syllables */ while (*s == ' ') s++; /* Advance to next verse, freeing the current control block if reached the end of the string. */ if (*s == 0) { *pp = p->next; store_free(p); } else { p->string = s; p->font = endfont; pp = &(p->next); } p = *pp; } } /************************************************* * Processing after reading a note * *************************************************/ /* This is called after reading an original note, or after copying a previous note using 'x'. The flag is TRUE if called after an original note or after the final copied note, in which case things that can follow a note are processed. We first have to choose a stem direction, which is tied in with the beaming and which we can't always complete at this point. Argument: TRUE for original note or last of some copies Returns: nothing */ static void post_note(BOOL final_copy) { int i; int pletending = FALSE; int stempitch = (maxpitch + minpitch)/2; /* zero for rests */ if (!stave_lastwastied) onlytieddup = FALSE; /* Set up the tie data for this note before any chord sorting, because it has to be sorted along with the chord. */ for (i = 0; i < chordcount; i++) stave_tiedata[i] = this_tiedata[i]; /* A note or rest longer than a quaver terminates a beam, unless it is a grace note */ if (notetype < quaver && stave_beaming && length > 0) read_setbeamstems(); /* Deal with non-rests and non-grace notes */ if (stempitch > 0 && length != 0) { stave_lastgracestem = 0; /* unset grace note forcing */ /* If already beaming, count notes in the beam */ if (stave_beaming) stave_beamcount++; /* Else a note shorter than a crotchet starts a beam */ else if (notetype > crotchet) { stave_beaming = TRUE; stave_beamfirstnote = stave_firstnoteptr; /* remember first note */ stave_beamcount = 1; if (stemforce == 0) stemforce = stave_stemforce; if (stemforce != 0) { stave_beamstemforce = stemforce; mac_setstackedstems((stave_beamstemforce > 0)? nf_stemup : 0); } else { stave_beamstemforce = 0; stave_maxaway = stempitch; } } /* Deal with beamed and non-beamed notes which have their stem direction forced. Note that we must call setstemflag even for down stems, because it does other work for chords. */ if (stemforce != 0 || (!stave_beaming && stave_stemforce != 0)) { int flag; if (stemforce == 0) stemforce = stave_stemforce; flag = (stemforce > 0)? nf_stemup : 0; mac_setstemflag(stave_firstnoteptr, flag); /* For non-beamed notes, set the flag for any pending queued notes, and remember the direction. We don't remember the direction for forced notes in the middle of beams -- these are usually eccentric. */ if (!stave_beaming) { mac_setstackedstems(flag); stave_laststemup = flag != 0; } } /* Deal with beamed note that does not have a forced stem - if the beam's stem direction was forced, set this note's direction. Otherwise use its pitch in computing the maxaway value, and add it to the beam stack. */ else if (stave_beaming) { /* If the previous note was tied and we are at the start of a beam, copy the stem direction of the previous note, if known. */ if (stave_lastwastied && stave_beamcount == 1 && stave_stemstackptr == 0) stave_beamstemforce = stave_laststemup? 1 : -1; if (stave_beamstemforce != 0) { /* NB */ mac_setstemflag(stave_firstnoteptr, (stave_beamstemforce > 0)? nf_stemup : 0); } else { if (abs(stempitch-P_3L) > abs(stave_maxaway-P_3L)) stave_maxaway = stempitch; stave_beamstack[stave_beamstackptr++] = stave_firstnoteptr; } } /* Deal with non-beamed note that does not have a forced stem - if the stem direction is immediately decidable, use it and empty the stack of any pending notes awaiting a decision. Otherwise add this note to the stack. Note that we must call setstemflag, even with a zero flag, because it also sorts chords and deals with inverted notes. */ else if (stave_lastwastied && stave_stemstackptr == 0 && (chordcount > 1 || stave_lasttiepitch == stave_firstnoteptr->spitch)) { /* NB */ mac_setstemflag(stave_firstnoteptr, stave_laststemup? nf_stemup : 0); } else if (stempitch != stave_stemswaplevel[curstave]) { int flag; stave_laststemup = stempitch < stave_stemswaplevel[curstave]; flag = (stave_laststemup)? nf_stemup : 0; mac_setstemflag(stave_firstnoteptr, flag); mac_setstackedstems(flag); } /* What happens to notes that are on the stemswap level depends on the type of stemswapping specified. */ else switch (curmovt->stemswaptype) { case stemswap_default: if (stave_firstinbar || stave_stemstackptr > 0) stave_stemstack[stave_stemstackptr++] = stave_firstnoteptr; else { /* NB */ mac_setstemflag(stave_firstnoteptr, stave_laststemup? nf_stemup : 0); } break; case stemswap_up: mac_setstemflag(stave_firstnoteptr, nf_stemup); break; case stemswap_down: mac_setstemflag(stave_firstnoteptr, 0); break; case stemswap_left: mac_setstemflag(stave_firstnoteptr, stave_laststemup? nf_stemup : 0); break; case stemswap_right: stave_stemstack[stave_stemstackptr++] = stave_firstnoteptr; break; } /* Subsequent notes are no longer the first in the bar */ stave_firstinbar = FALSE; } /* Grace notes are always stem up unless explicitly marked, but a single forced grace note forces all immediately following. */ else if (length == 0) { if (stemforce == 0) stemforce = stave_lastgracestem; mac_setstemflag(stave_firstnoteptr, (stemforce >= 0)? nf_stemup : 0); stave_lastgracestem = stemforce; } /* For a rest, unset grace stem forcing */ else { stave_lastgracestem = 0; } /* We now need to deal with ties, glissando marks, beam breaks, and the ends of plet groups. We permit the plet group ending to come before any of the other items if no space intervenes. Skip this, though, for intermediate notes of a copied set. */ if (!final_copy) return; if (read_ch == '}') { pletending = TRUE; next_ch(); } /* Deal with ties and glissandos */ if (read_ch == '_') { int acount = 0; int bcount = 0; int flags = tief_default; if (stave_ties > 0) acount = 255; else if (stave_ties < 0) bcount = 255; next_ch(); while (read_ch == '/') { next_ch(); if (read_ch == 'g') { flags &= ~tief_default; flags |= tief_gliss; } else if (read_ch == 's' || read_ch == 't') flags |= tief_slur; else if (read_ch == 'e') flags |= tief_editorial; else if (read_ch == 'i') { if (*read_chptr == 'p') { flags |= tief_dotted; next_ch(); } else flags |= tief_dashed; } else { int count = 255; if (isdigit(read_ch)) count = read_integer(FALSE); if (read_ch == 'b') { bcount = count; acount = 0; } else if (read_ch == 'a') { acount = count; bcount = 0; } else error_moan(ERR37, "/a /b /e /g /i /p /s or /t"); flags |= tief_slur; } next_ch(); } if ((flags & tief_editorial) != 0) { if ((flags & (tief_dotted | tief_dashed)) != 0) error_moan(ERR94); } if (minpitch == 0) error_moan(ERR51); else { b_tiestr *p = store_getitem(b_tie); p->flags = flags; p->abovecount = acount; p->belowcount = bcount; p->note = stave_firstnoteptr; stave_lastwastied = TRUE; stave_lasttiepitch = stave_firstnoteptr->spitch; } stave_resetOK = FALSE; } else { stave_lastwastied = FALSE; stave_resetOK = TRUE; } /* If a relevant note is followed by a comma, set for a secondary beam break. If followed by a semicolon, set for a primary beam break. We used to give an error when either of these characters did not follow a quaver or shorter note. This makes it annoying to use doublenotes or halvenotes to set a piece in different ways. We now allow comma and semicolon after any note at this point, only generating the relevant break for short enough notes. Any other occurrences still give an error. COMPATIBILITY FEATURE: For compatibility with the original program, if the "oldbeambreak" option is set, use space or ! for a primary break. Semicolon will be treated (elsewhere) as a general separator and ignored. */ if (opt_oldbeambreak) { if (read_ch == ' ' || read_ch == '!') { next_ch(); if (notetype >= quaver) { (void)store_getitem(b_beambreak); if (stave_beaming) read_setbeamstems(); } } else if (read_ch == ',') goto SECBEAMBREAK; } else /* Not oldbeambreak */ { if (read_ch == ';') { next_ch(); if (notetype >= quaver) { (void)store_getitem(b_beambreak); if (stave_beaming) read_setbeamstems(); } } else if (read_ch == ',') { usint v; SECBEAMBREAK: next_ch(); if (isdigit(read_ch)) { v = read_ch - '0'; next_ch(); } else v = 1; if (notetype >= quaver) { b_beambreak2str *b = store_getitem(b_beambreak2); b->value = v; } } } /* If we encountered '}' previously or here, end plet group */ sigch(); if (read_ch == '}') { next_ch(); pletending = TRUE; } if (pletending) { if (stave_pletlen == 0) error_moan(ERR52); else { stave_pletlen = 0; (void)store_getitem(b_endplet); } } /* If there was a [smove] before the note, insert the appropriate space directive. */ if (stave_smove != 0) { b_spacestr *s = store_getitem(b_space); s->value = stave_smove; s->relative = stave_smove_relative; stave_smove = 0; } /* If we had a tied chord containing seconds, generate an implicit [ensure] */ if (seconds && stave_lastwastied) { b_ensurestr *pe = store_getitem(b_ensure); pe->value = 20000; read_lastensuredtie = pe; } else read_lastensuredtie = NULL; /* Finally, update the count of notes in this chord */ stave_chordcount = chordcount; } /************************************************* * Read one note or chord * *************************************************/ /* This function is called if the stave scanner cannot interpret the current character as the start of a directive or any other non-note construction. Arguments: none Returns: nothing */ void read_note(void) { BOOL nopack = FALSE; int inchord = 0; /* Contains nf_chord when in a chord */ int item = b_note; int ornament = stave_ornament; int ornset = 0; int prevpitch = 0; int yextra; /* restlevel or stemlength */ int chordlength = 0; stave_firstnoteptr = NULL; maxpitch = 0; minpitch = 256; stemforce = 0; /* stem not forced */ seconds = FALSE; /* If we are at the start of a bar, behave for "p" and "x" as if all previous notes were tied duplicated (i.e. show accidentials unless this note is tied to its predecessor). */ if (stave_firstinbar) onlytieddup = TRUE; /* Handle exact duplication of the previous note or chord. Duplication of a previous note's pitch(es) only is handled by the 'p' letter, and is mixed up with other interpretation below. */ if (read_ch == 'x') { int count = 1; next_sigch(); if (isdigit(read_ch)) count = read_integer(FALSE); if (read_ch == '\\') /* No options are allowed */ { next_ch(); error_skip = skip_BACKSLASH; error_moan(ERR135); error_skip = skip_EOL; next_ch(); } if (stave_lastbasenoteptr == NULL) { error_moan(ERR115); return; } /* Duplicate a note or chord */ for (; count > 0; count--) { b_notestr *old = stave_lastnoteptr; b_notestr *oldbase = stave_lastbasenoteptr; b_notestr *new; bstr *p = (bstr *)last_note_ornament; do_underlay(); chordcount = 0; if (p != NULL) while (p != (bstr *)old) { int type = p->type; if (type == b_Jump) p = (bstr *)(((b_Jumpstr *)p)->next); else if (type == b_ornament && ((b_ornamentstr *)p)->ornament < or_dsharp) { b_ornamentstr *pp = store_getitem(b_ornament); *pp = *((b_ornamentstr *)p); } p = (bstr *)((uschar *)p + length_table[type]); } stave_firstnoteptr = new = store_getitem(b_note); length = old->length; notetype = old->notetype; *new = *old; /* At bar start, or after only tied duplicates, retain the accidental; otherwise don't. The note itself is taken from the previous note (to get the length the same), but the accidentals are taken from the base note, because intermediate notes may have their accidentals disabled. */ if (onlytieddup && !stave_lastwastied) { new->acc = oldbase->acc; new->accleft = oldbase->accleft; new->flags |= nf_accleft; /* Spacing already set */ } else { new->acc = 0; new->accleft = 0; } stave_pitchtotal += new->truepitch; stave_pitchcount++; chordcount++; /* This error should never occur because the original chord should be diagnosed. Paranoia. */ if (chordcount >= MAX_CHORDSIZE) error_moan(ERR132, MAX_CHORDSIZE); /* Hard */ if (new->spitch > maxpitch) maxpitch = new->spitch; if (new->spitch < minpitch) minpitch = new->spitch; if ((old->flags & nf_chord) != 0) { mac_advancechord(old); mac_advancechord(oldbase); while (old->type == b_chord) { new = store_getitem(b_chord); *new = *old; /* At bar start, or after only tied duplicates, retain the accidental from the base note; otherwise don't. */ if (onlytieddup && !stave_lastwastied) { new->acc = oldbase->acc; new->accleft = oldbase->accleft; } else { new->acc = 0; new->accleft = 0; } if (new->spitch > maxpitch) maxpitch = new->spitch; if (new->spitch < minpitch) minpitch = new->spitch; stave_pitchtotal += new->truepitch; stave_pitchcount++; chordcount++; mac_advancechord(old); mac_advancechord(oldbase); } } new[1].type = b_End; /* Mark end for sorting */ stave_barlength += length; post_note(count <= 1); } return; } /* Not an exact repetion: handle the reading of a new note or chord. */ last_note_ornament = NULL; /* For remembering ornaments for 'x' */ /* Deal with the start of a chord */ if (read_ch == '(') { inchord = nf_chord; next_sigch(); } /* Loop to read all the notes of a chord */ chordcount = 0; for (;;) { b_notestr *noteptr; BOOL acc_invis = FALSE; BOOL acc_onenote = FALSE; BOOL duplicating = FALSE; uschar *acc_above = NULL; usint acflags = 0; int flags = stave_noteflags | stave_stemflag | inchord; int transposedaccforce = stave_transposedaccforce; int acc = 0; int accleft = 0; int masq = -1; int tiedcount = -1; int transposeacc = 0; int explicit_couple = 0; int dup_octave = 0; int pitch, abspitch, orig; if (chordcount == 0) acflags |= stave_accentflags; /* Deal with accidentals */ if (read_ch == '#') { next_ch(); switch(read_ch) { case '#': acc = ac_dsharp; next_ch(); break; case '-': flags |= nf_halfacc; next_ch(); /* Fall through */ default: acc = ac_sharp; break; } } else if (read_ch == '$') { next_ch(); switch (read_ch) { case '$': acc = ac_dflat; next_ch(); break; case '-': flags |= nf_halfacc; next_ch(); /* Fall through */ default: acc = ac_flat; } } else if (read_ch == '%') { acc = ac_natural; next_ch(); } /* Save original accidental (prior to transposition) for use if this note has to be re-created for 'p'. The note letter is also saved later. */ orig = acc; /* Deal with requests for transposed accidentals */ if (read_ch == '^') { next_ch(); if (read_ch == '-') { transposedaccforce = FALSE; next_ch(); } else if (read_ch == '+') { transposedaccforce = TRUE; next_ch(); } if (read_ch == '#') { next_ch(); if (read_ch == '#') { transposeacc = ac_dsharp; next_ch(); } else transposeacc = ac_sharp; } else if (read_ch == '$') { next_ch(); if (read_ch == '$') { transposeacc = ac_dflat; next_ch(); } else transposeacc = ac_flat; } else if (read_ch == '%') { transposeacc = ac_natural; next_ch(); } } /* Deal with special forms of accidental: invisible, above/below, bracketed, and moved. */ if (acc != 0) { /* An invisible accidental may have no further options */ if (read_ch == '?') { acc_invis = TRUE; next_ch(); } else { /* Deal with a request to print the accidental above or below the note. We do this by setting it invisible, and generating a suitable ornament. This is only allowed on the first note of a chord. */ if (read_ch == 'o' || read_ch == 'u') { if (chordcount == 0) { b_ornamentstr *p = store_getitem(b_ornament); int value = ((read_ch == 'o')? or_dsharp : or_accbelow); next_ch(); ornmove(&(p->x), &(p->y), NULL); p->bflags = 0; if (read_ch == ')') { value += 1; next_ch(); } else if (read_ch == ']') { value += 2; next_ch(); } p->ornament = value; acc_above = &(p->ornament); /* To fill in the acc later */ acc_invis = TRUE; acc_onenote = TRUE; } else error_moan(ERR96); } /* Deal with visible, normally placed, accidentals */ else { if (read_ch == ')') { flags |= nf_accrbra; next_ch(); } else if (read_ch == ']') { flags |= nf_accsbra; next_ch(); } while (read_ch == '<') { next_ch(); if (isdigit(read_ch)) accleft += read_integer(TRUE); else accleft += 5000; flags |= nf_accleft; } } } } /* Now read note letter. Non-letters will be assigned as crotchets. The only relevant one currently is '/'. */ if (isupper(read_ch)) { notetype = minim; length = len_minim; } else { notetype = crotchet; length = len_crotchet; } /* Now deal with the pitch. First of all get an absolute pitch which is independent of any clef. The units are semitones and the origin is such that middle C has the value 48, which should cope with any requirements. This pitch doesn't yet contain the accidental (if any). */ read_ch = tolower(read_ch); /* Deal with duplication of previous pitch(es). This must not be in a chord and there must be no accidentals. We regenerate the pitch letter from the previous note, and the accidental if this is the start of a bar, unless after a tie. */ if (read_ch == 'p') { if (acc != 0) error_moan(ERR114); if (inchord) { error_moan(ERR37, "Note letter"); read_ch = 'a'; } else if (stave_lastbasenoteptr == NULL) { error_moan(ERR115); read_ch = 'r'; } else { int x = stave_lastbasenoteptr->truepitch; if (stave_transpose != no_transpose) x -= stave_transpose; dup_octave = 12 - stave_octave; while (x < 48) { x += 12; dup_octave -= 12; } while (x > 59) { x -= 12; dup_octave += 12; } /* Set the accidental - needed for transposition, even if we aren't going to print it, for cases of forcing - and the original note letter. */ orig = stave_lastbasenoteptr->orig; acc = orig & 0x0f; transposeacc = stave_lastbasenoteptr->acc; read_ch = (orig >> 4) + 'a'; /* At bar start, or after only tied duplicates, retain the accidental; otherwise make it invisible. */ if (onlytieddup && !stave_lastwastied) { if (stave_lastbasenoteptr->acc == 0) acc_invis = TRUE; accleft = stave_lastbasenoteptr->accleft; flags |= nf_accleft | (stave_lastbasenoteptr->flags & (nf_accrbra | nf_accsbra)); } else acc_invis = TRUE; duplicating = TRUE; } } /* Deal with non-rests, i.e. notes */ if ('a' <= read_ch && read_ch <= 'g') { pitch = stave_octave + 36 + read_basicpitch[read_ch - 'a']; yextra = stave_stemlength; /* Default stem length */ orig |= (read_ch - 'a') << 4; if (!duplicating) onlytieddup = FALSE; } /* Deal with rests. R is a normal rest, Q is a quiet (invisible) rest, S is like R but whole bars do not pack up, and / is a "repeat beat" mark that is handle as a differently printed crotchet rest. */ else { pitch = 0; acflags &= ~af_dynamics; /* Ignore any default dynamics */ yextra = stave_restlevel; if (read_ch == 'q') flags |= nf_hidden; else if (read_ch == 's') nopack = TRUE; else if (read_ch == 't') flags |= nf_restrep; else if (read_ch != 'r') { error_moan(ERR37, inchord? "Note letter or end of chord" : "Note or rest letter"); pitch = 30; /* Take a random pitch */ inchord = FALSE; /* Terminate a chord, in case missing ')' */ stave_checklength = FALSE; /* Disable length check for this bar */ } if (inchord && pitch == 0) error_moan(ERR43); } /* If suspended and not a rest, resume automatically */ if (stave_suspended && pitch > 0) { (void)store_getitem(b_resume); stave_suspended = FALSE; } /* Pitch adjustment for octave indicators, transposition and clef, except for rests, of course. */ next_ch(); if (pitch == 0) abspitch = minpitch = 0; else /* min = max = 0 for rests */ { if (duplicating) pitch += dup_octave; else { while (read_ch == '\'') { pitch += 12; next_ch(); } while (read_ch == '`' ) { pitch -= 12; next_ch(); } } /* Get a true absolute pitch which includes the accidental. Update the table which keeps track of the current accidental state for a given input pitch. After a tie, take the default accidental from the previous note, which may have been in the previous bar. Save the information in case this note is tied. */ if (chordcount >= MAX_CHORDSIZE) error_moan(ERR132, MAX_CHORDSIZE); /* Hard */ this_tiedata[chordcount].pitch = pitch; if (acc != 0) { int x = read_accpitch[acc]; if (!acc_onenote) baraccs[pitch] = x; this_tiedata[chordcount].acc = x; abspitch = pitch + x - 2; } else { if (stave_lastwastied) { int i; for (i = 0; i < stave_chordcount; i++) { if (pitch == stave_tiedata[i].pitch) { int x = stave_tiedata[i].acc; abspitch = pitch + x - 2; this_tiedata[chordcount].acc = x; tiedcount = i; acc_invis = TRUE; /* Could come from transpose if previous is invisible */ goto TRANSPOSE; } } } /* Last not tied, or tie not matched up (slur) */ abspitch = pitch + baraccs[pitch] - 2; this_tiedata[chordcount].acc = baraccs[pitch]; } TRANSPOSE: /* Transpose the note and its accidental if required */ if (stave_transpose != no_transpose) { abspitch = transpose_note(abspitch, &pitch, &acc, transposeacc, transposedaccforce, acc_onenote, FALSE, tiedcount); this_tiedata[chordcount].acc_tp = baraccs_tp[pitch]; } /* If there was a half-accidental that is now a natural, change it to the opposite half-accidental, and play similar games with double sharps and flats. */ if ((flags & nf_halfacc) != 0) { if (acc == ac_none || acc == ac_natural) acc = ((orig & 0x0f) == ac_sharp)? ac_flat : ac_sharp; else if (acc == ac_dflat) { acc = ac_sharp; pitch -= 2; abspitch -= 2; } else if (acc == ac_dsharp) { acc = ac_flat; pitch += 2; abspitch += 2; } } /* Adjust absolute pitch for transposing clefs */ abspitch += stave_clef_octave; /* Keep track of the absolute pitch range, and maintain data for tessitura computation. */ if (abspitch > stave_maxpitch) stave_maxpitch = abspitch; if (abspitch < stave_minpitch) stave_minpitch = abspitch; stave_pitchtotal += abspitch; stave_pitchcount++; /* Now adjust the printing pitch to make it relative to the current clef, where a value of P_1L (=128) is the bottom line of the staff, and the note positions are 2 apart (because they are two points apart). However, the printing pitch may be explicitly forced (percussion staves), in which case the accidental must be cancelled. */ if (stave_printpitch) { pitch = stave_printpitch; acc = 0; } else pitch = pitch_stave[pitch] + pitch_clef[stave_clef]; /* Save max/min pitch per chord */ if (pitch > maxpitch) maxpitch = pitch; if (pitch < minpitch) minpitch = pitch; /* Set flags for potentially auto-coupled notes; these may be unset later by the \h\ option. */ if (pitch > P_6L) flags |= nf_coupleU; else if (pitch < P_0L) flags |= nf_coupleD; /* Flag chords containing seconds */ if (abs(pitch - prevpitch) == 2) seconds = TRUE; prevpitch = pitch; } /* If the accidental is to be printed above or below, add in its value, which is not known till after transposition. */ if (acc_above != NULL) { *acc_above += 3*(acc - 1); if ((flags & nf_halfacc) != 0) *acc_above += (acc == ac_sharp)? 6 : 12; } /* If the accidental is invisible, cancel it now that the true pitch has been determined. Don't cancel for a rest so that it gets an error below. */ if (acc_invis && pitch != 0) acc = 0; /* Note or rest length adjustment by modifiers. */ if (read_ch == '!') { if (notetype != minim) error_moan(ERR150); notetype = semibreve; length = stave_requiredbarlength; if (pitch == 0) { flags |= nf_centre; if ((flags & nf_restrep) != 0) nopack = TRUE; /* Was T! */ } next_ch(); } else { while (read_ch == '=' ) { next_ch(); notetype += 2; length /= 4; } while (read_ch == '-' ) { next_ch(); notetype += 1; length /= 2; } while (read_ch == '+') { next_ch(); notetype -= 1; length *= 2; } if (notetype != crotchet) flags &= ~nf_restrep; /* Ignore if not crotchet */ } /* Set left movement for any accidental. We can't do this earlier, because transposition can alter which accidental is printed, and we need to have notetype set correctly for accadjusts. If duplicating, the movement has already been copied. */ if (acc && !duplicating) { accleft += curmovt->accspacing[acc] - curmovt->accadjusts[notetype]; if ((flags & nf_accrbra) != 0) accleft += rbra_left[acc]; else if ((flags & nf_accsbra) != 0) accleft += sbra_left[acc]; } /* Deal with non-standard lengths. */ if (stave_pletlen != 0) length = (length*stave_pletsupnum)/(stave_pletlen*stave_pletsupden); /* Deal with dotted notes. For the first note of a chord, a movement of the dot is permitted. */ if ((read_ch == '>' && *read_chptr == '.') || isdigit(read_ch)) { if (chordcount == 0) { b_dotrightstr *d = store_getitem(b_dotright); if (read_ch == '>') { next_ch(); d->value = 5000; } else { d->value = read_integer(TRUE); if (read_ch != '>' || *read_chptr != '.') error_moan(ERR37, "\">.\" (augmentation dot movement)"); next_ch(); } } else error_moan(ERR48, "Augmentation dot movement"); } /* Lengthen the note according to .+ . or .. */ if (read_ch == '.' ) { next_ch(); if (read_ch == '+') { next_ch(); flags |= nf_plus; length = (length*5)/4; } else { flags |= nf_dot; length = (length*3)/2; if (read_ch == '.' ) { next_ch(); flags |= nf_dot2; length = (length*7)/6; } } } /* Handle doubled or halved note lengths - note that full bar rests must be skipped (the centre flag is an indicator). */ if ((stave_notenum != 1 || stave_noteden != 1) && (flags & nf_centre) == 0) { int i = stave_notenum; while (i > 1) { length += length; notetype--; i /= 2; } i = stave_noteden; while (i > 1) { length /= 2; notetype++; i /= 2; } } /* Can't handle anything longer than a breve or shorter than a hemidemisemiquaver. */ if (notetype < 0 || notetype > 7) error_moan(ERR103); /* Now we have the final length for the note, adjust it if necessary to allow for triplet vs duplet time signatures, etc. */ if (stave_matchnum > 0 && (flags & nf_centre) == 0) length = mac_muldiv(length, stave_matchnum, stave_matchden); /* Accents and ornaments etc. and local level for rests */ if (read_ch == '\\') { BOOL had_error = FALSE; uschar *end_options; error_skip = skip_BACKSLASH; next_sigch(); /* Search for the terminating backslash in the current line, to try to minimise error cascades when it is missing. */ for (end_options = read_chptr;; end_options++) { if (end_options >= read_endptr || (*end_options == '|' && end_options[-1] != '~' && end_options[-1] != 't')) { error_skip = skip_BAR; error_moan(ERR133); goto ENDACCENT; } if (*end_options == '\\') break; } /* Now process the options */ while (read_ch != '\\' && read_ch != EOF) { accent *ap; switch (read_ch) { /* ----- Handle things that are not accents or ornaments ----- */ case ':': next_ch(); if (read_ch == ':') { next_ch(); flags |= nf_highdot; } else flags ^= nf_lowdot; break; case ')': next_ch(); flags |= nf_headbra; break; case 'c': next_ch(); explicit_couple = nf_couple; break; /* This flag centres a note iff it is the first note in the bar and has the barlength. */ case 'C': next_ch(); if (stave_barlength == 0 && length == stave_requiredbarlength) flags ^= nf_centre; break; case 'g': length = 0; next_ch(); if (read_ch == '/') { flags |= nf_appogg; next_ch(); } break; case 'h': next_ch(); flags &= ~nf_couple; explicit_couple = 0; break; case 'l': /* local rest level */ next_ch(); { int y; if (read_expect_integer(&y, TRUE, TRUE)) { if (opt_oldrestlevel) y *= 2; yextra += y; } } break; case 'M': case 'm': masq = (read_ch == 'm')? crotchet : minim; flags &= ~(nf_dot|nf_dot2|nf_plus); for (;;) { next_ch(); if (read_ch == '-') masq++; else if (read_ch == '=') masq += 2; else if (read_ch == '+') masq--; else break; } if (masq < breve || masq > hdsquaver) { error_moan(ERR45); masq = -1; } if (read_ch == '.') { next_ch(); if (read_ch == '+') { next_ch(); flags |= nf_plus; } else { flags |= nf_dot; if (read_ch == '.') { next_ch(); flags |= nf_dot2; } } } break; case 'n': next_ch(); if (read_ch == 'h') { flags |= nf_nhharmonic; next_ch(); } else if (read_ch == 'x') { flags |= nf_nhcross; next_ch(); } else error_moan(ERR45); break; case 's': next_ch(); if (read_ch == 'u') { if (stemforce < 0) error_moan(ERR46, inchord? "in chord" : ""); stemforce = 1; next_ch(); } else if (read_ch == 'd') { if (stemforce > 0) error_moan(ERR46, inchord? "in chord" : ""); stemforce = -1; next_ch(); } else if (read_ch == 'w') { if (stave_beaming && notetype >= quaver && stave_beamstemforce != 0) { if (stemforce) error_moan(ERR46, inchord? "in chord" : ""); stave_beamstemforce = -stave_beamstemforce; if (abs(stave_beamstemforce) == 1) stave_beamstemforce *= 2; } else error_moan(ERR47); next_ch(); } else if (read_ch == 'l') { int y; next_ch(); if (read_expect_integer(&y, TRUE, TRUE)) { if (opt_oldstemlength) y *= 2; yextra += y; } } else if (read_ch == 'm') { next_ch(); flags |= nf_smallhead; } else if (read_ch == 'p') /* sp is an ornament */ { read_ch = 's'; read_chptr--; goto ACCORN; } else { error_moan(ERR10, "su, sd, sw, sl, sm, or sp"); /* skips to '\' or EOL */ if (read_ch != '\\') goto ENDACCENT; } break; case 'x': next_ch(); acflags &= ~stave_accentflags; ornament = -1; break; /* ----- Handle accents and ornaments ----- */ default: ACCORN: for (ap = accent_chars; ap->string != NULL; ap++) { int i; int ln = (int)Ustrlen(ap->string); for (i = 0; i < ln; i++) if (read_chptr[i-1] != ap->string[i]) break; if (i >= ln) { read_chptr += ln - 1; next_ch(); break; } } /* Found in the table */ if (ap->string != NULL) { if (ap->flag < 256) /* Ornaments */ { switch (ap->flag) { case or_mord: case or_dmord: case or_imord: case or_dimord: case or_turn: case or_iturn: if ((ornset & ornset_mordturn) != 0) error_moan(ERR45); /* But carry on */ else ornset |= ornset_mordturn; break; case or_trem1: case or_trem2: case or_trem3: if ((ornset & ornset_trem) != 0) error_moan(ERR45); /* But carry on */ else ornset |= ornset_trem; break; case or_tr: case or_trsh: case or_trfl: case or_trnat: if ((ornset & ornset_trill) != 0) error_moan(ERR45); /* But carry on */ else ornset |= ornset_trill; break; } if (chordcount == 0) { b_ornamentstr *p = store_getitem(b_ornament); if (last_note_ornament == NULL) last_note_ornament = p; p->ornament = ap->flag; p->bflags = 0; /* Tremolos have no horizontal movement or bracketing; arpeggios and spread have no bracketing, but can have arbitrary movement. Because tremelos use '/' as their character, the coding is messy. */ if (ap->flag >= or_trem1 && ap->flag <= or_trem3) { p->x = 0; /* No horizontal movement */ p->y = 0; /* Default no vertical movement */ p->bflags = 0; /* No bracketing */ ornament = -1; /* Cancel auto tremolo */ if (read_ch == '/') /* Move a trem3 */ ornmove(NULL, &(p->y), NULL); else if (ap->flag != or_trem1 && /* Move a trem1/trem2 */ (read_ch == 'u' || read_ch == 'd') && (read_chptr < read_endptr) && (isdigit(*read_chptr) || *read_chptr == '-')) { p->ornament -= 1; read_ch = '/'; read_chptr--; ornmove(NULL, &(p->y), NULL); } } else ornmove(&(p->x), &(p->y), (ap->flag >= or_arp)? NULL : &(p->bflags)); } else error_moan(ERR48, "Ornaments"); } else /* Accents */ { if (chordcount == 0) { int i; acflags |= ap->flag; for (i = 1; i <= dyn_max; i++) { if (ap->flag == accent_list[i]) { dynmove(i-1); break; } } } else error_moan(ERR48, "Accents"); } } /* Not found in the big table; check for a. */ else if (read_ch == 'a') { int x; next_ch(); error_skip = skip_OFF; /* Temporarily no skip */ if (read_expect_integer(&x, FALSE, FALSE)) { if (chordcount == 0) { if (x <= dyn_max) { acflags |= accent_list[x]; dynmove(x-1); } else error_moan(ERR128, x); } else error_moan(ERR48, "Accents"); } error_skip = skip_BACKSLASH; } /* Unknown ornament or expression. Give up after two successive errors. */ else { error_moan(ERR45); if (had_error) { fprintf(stderr, "** Assuming missing backslash after two " "expression/ornament errors\n\n"); goto ENDACCENT; } else { had_error = TRUE; next_sigch(); } } } if ((acflags & (af_staccato|af_staccatiss)) == (af_staccato|af_staccatiss)) error_moan(ERR129); sigch(); } /* end of loop */ next_ch(); /* moves past terminating '\' */ } ENDACCENT: /* some error states jump here */ error_skip = skip_EOL; /* default setting */ /* Rests should not have ornaments or accidentals or certain options. */ if (pitch == 0 && ( (ornset != 0 && (flags & nf_hidden) == 0) || (flags & nf_notrests) != 0 || acc != 0 || stemforce != 0 || (acflags & af_dynamics) != 0)) error_moan(ERR68); /* A small notehead may not be specified for a grace note. */ if (length == 0 && (flags & nf_smallhead) != 0) error_moan(ERR108); /* Accumulate bar length and check that chord notes are all same length. If not, and the current one is a crotchet (i.e. just a lower case letter) force it to be the same as the first note. Other lengths cause an error. Output an underlay syllable if one is pending. Can't do this earlier because of grace notes. */ if (chordcount == 0) /* First note of chord */ { stave_barlength += length; chordlength = length; if (length != 0 && stave_pendulay != NULL) { if (pitch != 0 || Ustrcmp(stave_pendulay->string, "#") == 0) do_underlay(); } } else if (length != chordlength) { if (notetype == crotchet && (flags & nf_dotted) == 0) { length = chordlength; notetype = stave_firstnoteptr->notetype; flags = (flags & ~nf_dotted) | (stave_firstnoteptr->flags & nf_dotted); } else error_moan(ERR44); } /* Allow automatic coupling flags through only if appropriately coupled, but allow explicit coupling at any time. */ if (stave_couplestate == 0) { flags = (flags & ~nf_couple) | explicit_couple; if (pitch >= 136) flags &= ~nf_coupleD; else flags &= ~nf_coupleU; } else { flags |= explicit_couple; if (stave_couplestate > 0) flags &= ~nf_coupleD; else if (stave_couplestate < 0) flags &= ~nf_coupleU; } /* Deal with never-to-be-packed-up rest bar */ if (nopack) (void) store_getitem(b_nopack); /* Output any masquerade instruction, and adjust stem flag */ if (masq >= 0) { if (masq < minim) flags &= ~nf_stem; if (chordcount == 0) { b_masqstr *p = store_getitem(b_masq); p->value = masq; } } else if (notetype < minim) flags &= ~nf_stem; /* Output any default ornament (can only happen on first note) */ if (ornament >= 0) { b_ornamentstr *p = store_getitem(b_ornament); if (last_note_ornament == NULL) last_note_ornament = p; p->ornament = ornament; p->x = p->y = 0; ornament = -1; } /* If a whole bar rest, flag it for centring unless we are in an unchecked bar. (If the rest was specified as R! then it has already been marked as a whole bar rest.) */ if (pitch == 0 && length == stave_requiredbarlength && stave_checklength) flags |= nf_centre; /* Now create a data block for the note or rest, saving the address of the first one in a chord for exact duplications ('x') and the first one in a non-p-duplicated chord for subsequent 'p' duplications, except in the case of above/below accidentals. */ noteptr = store_getitem(item); if (stave_firstnoteptr == NULL) /* Rest, single, or first note in a chord */ { stave_firstnoteptr = noteptr; if (pitch != 0) { stave_lastnoteptr = noteptr; if (!duplicating) stave_lastbasenoteptr = (acc_above != NULL)? NULL : noteptr; } } /* Insert the data for this note */ noteptr->notetype = notetype; noteptr->acc = acc; noteptr->orig = orig; noteptr->spitch = pitch; noteptr->flags = flags; noteptr->acflags = acflags; noteptr->length = length; noteptr->yextra = yextra; noteptr->accleft = accleft; noteptr->truepitch = abspitch; /* Count note, and set different item for remaining notes of chord */ chordcount++; item = b_chord; /* If we are duplicating, we must now copy the remaining notes of a chord, replacing the length and chopping off any accidentals where necessary. We must also set the relevant tiedata values. */ if (duplicating && (stave_lastbasenoteptr->flags & nf_chord) != 0) { int i = 0; b_notestr *old = stave_lastbasenoteptr; noteptr->flags |= nf_chord; mac_advancechord(old); while (i++, old->type == b_chord) { noteptr = store_getitem(item); *noteptr = *old; noteptr->notetype = notetype; /* At bar start, or after only tied duplicates, retain the accidental; otherwise don't. */ if (onlytieddup && !stave_lastwastied) { noteptr->acc = old->acc; noteptr->accleft = old->accleft; } else { noteptr->acc = 0; noteptr->accleft = 0; } noteptr->flags = (noteptr->flags & ~nf_dotted & ~nf_stem) | (flags & (nf_dotted | nf_stem)); noteptr->acflags &= ~af_dynamics; noteptr->length = length; if (noteptr->spitch > maxpitch) maxpitch = noteptr->spitch; if (noteptr->spitch < minpitch) minpitch = noteptr->spitch; stave_pitchtotal += noteptr->truepitch; stave_pitchcount++; chordcount++; mac_advancechord(old); this_tiedata[i] = stave_tiedata[i]; } } /* Insert a b_End item immediately after this note in case it is the last note of a chord, so that the function for sorting chords detects the end correctly. As there is always space for a b_Jump item after any item, this is safe to do. This is done unconditionally, because after an error inchord may not be set at this point. */ noteptr[1].type = b_End; /* Mark end for sorting */ /* Check for end of chord */ if (inchord) { sigch(); if (read_ch == ')' || read_ch == EOF) { next_ch(); break; } /* Continuing with chord */ } else break; } /* We have now reached the end of a chord, having output all the notes therein contiguously, with any ornaments or masquerade items preceding. The remaining code is put into a separate function so it can be called from the note-copying code too. */ post_note(TRUE); } /* End of read5.c */