💾 Archived View for runjimmyrunrunyoufuckerrun.com › src › foreign › pmw › src › read5.c captured on 2021-12-17 at 13:26:06.
View Raw
More Information
-=-=-=-=-=-=-
/*************************************************
- 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)
{
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 == '