Skip to content

Articulations (A) line)

STATUS: SPEC. §1-§7 sealed 2026-05-23 (current A) deduction, already in the parser); §8-§12 sealed 2026-05-24 (extensions: token model, glissando gl, slur/wave spans). Implemented end-to-end (parser Phase 3.1 + rendering 3.1b); status/details in neumaRk_changelog.md §0.6. Known limitation: the vertical wave↔slur collision is a layout follow-up. Binding and order are not redefined here: see neumaRk_datapack.md §2.1 and §3. Sealing decisions in §7 (base) and §12 (extensions).

1. Definition

The A) (Articulations) line carries the articulations and ornaments to apply, position by position, to the events of the music line it is bound to. It is a parallel layer: it does not alter rhythm, durations or flow, only the notational attributes of the events.

2. Structural position and binding

Marker, optionality and logical order are defined in neumaRk_datapack.md §2.1 (marker table) and §3 (implicit order: A) is optional and precedes the N) line of its own note group). In operational summary:

  • A) binds to the line below ("model §4.A.5"): its tokens are held pending and drained onto the first subsequent music target in the same group/staff.
  • Drain target, in priority order:
  • the chord-rhythm of the C) line immediately below — only for stave 0 (the chord-rhythm lives in MeasureShared), if the C) has rhythm;
  • otherwise the N) line of the same stave.
  • In multi-stave each staff has its own pending bucket; voice 2 (N2) has an independent A) bucket (see neumaRk_voices.md §3.1).

3. Vocabulary

Each token is a symbol (or combination) that maps to an articulation or ornament. Tokens are separated by spaces; multiple articulations on the same note are written as a single token without spaces (e.g. >!).

Token Meaning Type
- tenuto articulation
> accent articulation
! staccato articulation
^ marcato articulation
+ pizzicato (left hand) articulation
o fermata (above) articulation
os short fermata articulation
ol long fermata articulation
-! portato (tenuto+staccato) combination
>! staccato accent combination
tr trill ornament
m mordent ornament
M inverted mordent ornament
t turn ornament
T inverted turn ornament
, breath mark ornament
h harmonic articulation (extension §8+)
v up bow articulation (extension §8+)
n down bow articulation (extension §8+)
. placeholder: no articulation on that position

h for the harmonic (the symbol ° is not ASCII and o is already fermata); v/n for the bows (n recalls the ⊓). Added to these are a per-pair token (glissando gl, §9) and two spans (slur ( ), wave ~, §10), introduced by the decomposable token model in §8.

The vocabulary is the closed set of the table above (plus the placeholder ., the per-pair/spans of the §8+ extensions). Only these tokens are valid articulations; the parser classification charset derives from this set (see §6 and §11). A token outside the vocabulary is not a valid articulation.

The markers > and ^ make a line uniquely articulations during classification (articulations_specific_marker): no other music line uses them in isolation.

4. Count-based alignment

The correspondence with the events of the target line is strictly positional by count (as in neumaRk_dynamics.md §4.1), left-to-right:

N) | a8 b  c  d |
A) | >  .  !  ^ |
     ↑      ↑  ↑
   accent staccato marcato (b: none)
  • The token . or an empty token = no articulation on that note.
  • If there are fewer tokens than notes, the remaining notes stay without articulation (no-op).
  • If there are more tokens than events, the extras are silently dropped (auto-fix philosophy; note: unlike D) no warning is emitted — see Open decisions).
  • The alignment counts rests too (unlike L), which skips them): a position above a rest is an event in every respect. This is what allows a span (§10) to cover a rest (you write ~ over it and the span continues).

5. Examples

A) | >    !  |
N) | c4 d e f |     accent on c, staccato on e

A) | >!         |
C) | C7  F7     |     staccato accent on the first chord-rhythm event (stave 0)

6. The vocabulary is part of deduction (single source)

The parser is not agnostic with respect to this vocabulary: to deduce that a prefix-less line is A) it must recognize its tokens. Historically the vocabulary was split and duplicated — half syntactic in the parser (charset of articulations_line / articulations_specific_marker, used for classification) and half semantic in the renderer (nrkArticToModifiers, NRK-token → VexFlow code) — and the two halves diverged (the charset admitted _ and unmapped isolated letters).

Normative model (decided 2026-05-23): this document is the single source. The spec defines NRK-token → musical meaning (e.g. !→staccato); the meaning is normative, the VexFlow codes are not (another renderer would use others). From this table derive: the parser's classification charset and tokenization; the renderer's meaning → glyph map.

7. Decisions (closed 2026-05-23)

  1. Single source ✓ — the spec is normative; parser-charset and renderer-map derive from it. Eliminates the drift.
  2. Vocabulary = closed set ✓ — symbols outside the table (e.g. _, isolated letters) are not valid articulations; the charset must be restricted to the vocabulary.
  3. Token excess ✓ — A) emits W131 like D) (was a silent drop).
  4. Normative meaning, VexFlow implementational ✓ — the "meaning" column is binding; the codes a-/a>/… are an implementation note.

Code-side follow-up (D(L)→D(P), tracked in docs/feature-deduction-spec-audit.md), all ✅ done (Phase 3.1): classification charset derived from the closed vocabulary; W131 on token excess; W139 (lexical validation) on any token with an out-of-vocabulary sequence, including the isolated letter-fragment (s of os, r of tr).


A) Extensions (sealed 2026-05-24)

Sections §8-§12 are normative and implemented (parser Phase 3.1 + render 3.1b). They extend the per-position vocabulary of §3 with: a decomposable token model (§8), a per-pair token (glissando gl, §9) and two spans (§10).

8. Token model: decomposition and co-locations

The base vocabulary (§3) uses, in the current parser, a switch over whole tokens (nrkArticToModifiers, with case ">!"). The extensions require a true per-position decomposer, analogous to parseDynamicsToken of D).

Each position (space-separated token) is an order-free concatenation of elements, decomposed with greedy longest-match over the multi-char atoms (first os/ol before o, first tr before t):

element :=
  | wave            ~[1-4]? ("…")?      # §10.2 (digit/label only at opening)
  | slur-open       (                    # §10.1
  | slur-close      )                    # §10.1
  | bracket-open    [ ("…")?             # §10.3 (label only at opening)
  | bracket-close   ]                    # §10.3
  | octave-open     8u | 8d              # §10.4 (up / down)
  | octave-close    8.                   # §10.4 (8 + dot)
  | glissando       gl                   # §9 (towards the next note)
  | fermata         o | os | ol          # greedy: os/ol before o
  | trill           tr                   # greedy: tr before t
  | turn            t | T
  | mordent         m | M
  | per-note 1ch    - > ! ^ + , h v n
  | placeholder     .                    # no element on that position

Consequence: -! and >! are no longer special tokens but co-locations (-+! = portato; >+! = staccato accent). All combinations emerge from concatenation: (! = slur-open + staccato; >gl = accent + glissando. The "combination" entry of the §3 table remains valid as a meaning, not as an atomic token.

The decomposition (tokenize) is separate from the span resolution (post-pass), just as D) separates parseDynamicsToken from extract_hairpins (§10).

9. Note-to-note glissando (gl)

gl on the position of a note = that note glides towards the next. The direction (up/down) is implicit from the two pitches. It is per-pair, not a span.

A) .  gl .  .
N) r4 a  b  r       # a glides towards b

Distinct from the single-note glissando (scoop/fall, entry/exit from one note), which lives in N) as a modifier between parentheses — see neumaRk_notes_and_durations.md (N extensions).

10. Spans

Two spans, borrowed from the D) model (open/close resolved in a post-pass).

10.1 Slur ( ) (delimited span)

LilyPond style: ( opens on the note of its position, ) closes on the note of its position; the curve covers from opening to closing.

A) . ( . . )
N) c d e f g      # slur from d to g
  • Cross-barline: yes.
  • Confined to the staff: a slur left open at the end of the staff closes there with a warning; an orphan )warning, ignored.
  • No overlap/nesting (v1): at most one slur open at a time; a second ( while one is open → warning. (Nesting deferred.)
  • Open+close on the same note (()): degenerate → ignored/warning.
  • Co-locatable with the per-note ones: (! = slur-open + staccato.

10.2 Wave ~[1-4] (span by repetition)

A wavy line always above the staff. It unifies vibrato / shake / long-vibrato / laid-back: the amplitude is the visual width, the label gives its name.

Amplitude — optional digit 14 (default ~ = level 1):

  • ~N (with digit) opens a new span of amplitude N, closing the current one;
  • ~ (bare) extends the current span; if none is open, opens one of level 1.
A) ~3 ~ ~ ~1 ~ ~      # large wave over 3 notes, then small wave over 3

Extension and rests: consecutive ~ (even cross-barline) = a single wave; a position without ~ (including an empty measure) closes the span. Rests do not interrupt (the alignment counts rests, §4).

A) ~2 ~ ~ ~ |   | ~3 ~ ~ ~
N) a4 b r c | d | a4 b r c
#  └─ amplitude-2 over a b r(rest) c ─┘  (meas.2 empty → no wave)  └─ amplitude-3 ─┘

Label (optional, only on the opening token): "…" immediately after the wave, rendered above its start. The label is a text markup container (neumaRk_text_markup.md §2.2): it accepts the #/##/###, */**, __ markers; the default is upright (italic/bold/underline/size only via the markers). Amplitude and label are independent.

A) ~3"shake" ~ ~ ~
A) ~"vibrato" ~ ~
A) ~4"laid back" ~ ~ ~ ~

The label gives the semantics (vibrato vs shake vs laid-back); the glyph ~ alone is only "wave of amplitude N". For a transcription tool the page is the truth; an eventual playback will infer from label/amplitude.

10.3 Analysis bracket [ ] (delimited span)

A grouping bracket above the staff, like the analytical brackets of formal analysis. [ opens on the note of its position, ] closes on the note of its position (LilyPond style, exactly like the slur §10.1).

A) . . ["DO triad" . . ] |
N) a a c        e | g e c a
  • Label (optional): a string "…" immediately after [ (reuses the same opaque token as the wave). Rendered left-aligned above the opening and is a text markup container (neumaRk_text_markup.md §2.2: #/*/__), upright by default. A bracket without label ([ … ]) is allowed.
  • Rendering: square bracket always above the staff, with the two ends turned downward (toward the notes).
  • Cross-barline / cross-system: yes.
  • No overlap/nesting (v1): at most one bracket open at a time; a second [ while one is open → warning (W144.bracket_open_overlap); an orphan ]warning (W144.bracket_close_unmatched); a degenerate [] (open+close on the same note) → warning (W144.bracket_degenerate); a bracket left open at the end of the row → implicit close + warning (W144.bracket_unclosed_eol).
  • Purely graphic: no effect on playback (it is an analytical annotation).
  • Syntax note: in A) rows [ … | … ] is a bracket spanning a barline, so the inner | is a real barline — NOT a polychord [X|Y] (§chords) nor a volta [1.. The map suppresses polychord detection on Articulations rows.

10.4 Octave 8u / 8d8 (delimited span)

An ottava line. 8u opens an 8va alta (octave up), 8d opens an 8vb bassa (octave down); 8. closes the span (inclusive on the last covered note). Delimited like the slur (§10.1) — deliberately NOT the extension model of the wave (§10.2): the ottava line is sparse, so repeating a token on every note would be cumbersome. A bare 8 (without u/d/.) is out of vocabulary (likely a typo for 8.) → W139.

A) . . . . . . . 8u . . . | . . . 8. |
N) c d e f g a b c  d e f | a g f e d c b a g
#                  └─ 8va from the 8th note, across the barline, to the close ─┘
  • Rendering: the standard SMuFL ottava sign — 8va (alta, glyph U+E511) / 8ba (bassa, glyph U+E513) — followed by a dashed horizontal line and an end hook toward the staff; above for 8u, below for 8d. (Both glyphs are in the BravuraLS/PetalumaLS subset.)
  • Playback: the covered notes are transposed ±12 semitones (8u = +12, 8d = −12). The written pitch is unchanged — the noteheads stay where they are; only the playback shifts.
  • Cross-barline / cross-system: yes.
  • No overlap/nesting (v1): a second open while one is open → warning (W144.octave_open_overlap); an orphan 8. close → warning (W144.octave_close_unmatched); a degenerate 8u8.warning (W144.octave_degenerate); open at end of row → implicit close + warning (W144.octave_unclosed_eol).

Span convention (decided for A), to be reused by the D) hairpins/ cresc.): every long span is delimited — a (possibly parametrized) opening token + a closing token; intermediate positions are placeholders .. Slur ( ), bracket [ ] and octave 8u…8 follow it. The wave ~ keeps the extension model as the single documented exception, because its glyph is a continuous wavy line drawn over every covered note.

11. Impact on deduction (extended charset)

The extensions broaden the line classification surface (neumaRk_datapack.md §3.bis):

  • Charset A): includes the new symbols ( ) ~ h v n g and the digits 1-4, plus [ ] 8 u d (§10.3 bracket, §10.4 octave), in addition to the base vocabulary §6 (from which the charset is derived — single source).
  • ~ is a specific marker: a line that contains ~ is classified uniquely as A), despite the digits that would otherwise bring it closer to chords/notes.
  • [ ] 8 d are NOT specific markers: they are heavily overloaded ([ = section [NAME], alternate-chord label, grace/annotation in N); 8/d = duration/note). They only widen the A) charset; the notes-line priority (TB4) protects N) rows, and an implicit bracket/octave row needs the explicit A) prefix to be classified as articulations.
  • g/gl vs note G — TB4: g (head of the gl glissando token, §9) is also the note G, and with the durations 14 it makes lines like g, g4, g g g ambiguous. The TB4 rule (neumaRk_datapack.md §3.bis.5) gives priority to notes: a line that is a valid notes-line (every token matches the parse_notes grammar + at least one real pitch) is N), not A). The token gl is not a valid note, so an articulations line with gl stays A).
  • Quote-aware: the content of a label "…" (e.g. shake) is excluded from the classification match — out-of-charset letters or a ~ inside the text neither break nor falsely mark the line.

12. Decisions (extensions, closed 2026-05-24)

  1. Note-to-note glissando (gl, §9) ≠ single-note glissando (scoop/fall, in N)). ✓
  2. Extended vocabulary = closed set; normative meaning, implementational VexFlow codes (inherits §6/§7). ✓
  3. Unified token model (decomposer + co-locations, §8). ✓
  4. Slur ( ) delimited, no overlap v1 (§10.1). ✓
  5. Up bow v, down bow n; harmonic h (§3). ✓
  6. Wave ~: levels 1–4; digit = new span, bare = extends; always above; covers rests; label "…" at opening, italic (§10.2). ✓
  7. Shake / long-vibrato / laid-back unified in the wave ~N + label. ✓
  8. Analysis bracket [ ] delimited, label "…" optional, always above, purely graphic; no overlap v1 (§10.3). ✓ (added 2026-05-31)
  9. Octave 8u/8d8. delimited (8. closes), dashed line above/ below, transposes playback ±12; no overlap v1 (§10.4). ✓ (2026-05-31)
  10. Span convention: long spans are delimited (open + close); the wave is the single exception (extension), its glyph being a continuous line. ✓