Skip to content

Datapack

1. The datapack concept

A datapack is the fundamental structural unit of musical content in neumaRk.

It represents a vertical block of musical information synchronized in time, typically corresponding to one or more aligned staves:

  • markers
  • chords
  • notes
  • fingering
  • articulations
  • dynamics
  • lyrics
  • formatting

Each datapack describes a coherent temporal sequence (one or more consecutive measures). Musical datapacks are separated from one another by one or more blank lines, which act as structural delimiters. The presence of a blank line implies the conclusion of the preceding datapack.


2. Line types

Each line of the datapack has a semantic type.

The type may be:

  • explicit, via a marker
  • implicit, deduced from the content and position

2.1 Explicit line markers

Explicit markers are:

  • one, two, or three uppercase letters
  • followed by )
  • followed by a space
Marker Type
M) Markers
C) Chords
$) Fingering
A) Articulations
N) Notes
D) Dynamics
L) Lyrics
F) Format

The markers are optional, except $), which is never deduced (§3.bis.1).

$) (fingering, neumaRk_fingering.md) is the only marker whose first character is not an uppercase letter: it stays 2 characters wide like the others, preserving the vertical-alignment invariant.

The N) marker admits two morphological 2-character variants, explicitly provided for by the specification:

  • N+ — new staff introduced in this datapack (§4.5);
  • N2 — second voice on the same staff (see neumaRk_voices.md).

The N2 and N+ tokens stay 2 characters wide (the ) is absent), preserving the vertical-alignment invariant.

Likewise, the C) marker admits a variant:

  • C+ — line of alternate chords above the base line (neumaRk_chords.md §7; comment-label and line scope in §8).

C+ too stays 2 characters wide (the ) is absent), like N+, preserving the same invariant.

2.2 Complete inventory of explicit lines

The table in §2.1 lists the markers used inside a datapack. For completeness, this is the inventory of all the lines the language recognizes by marker or by structure (independently of the implicit deduction of §3.bis, which applies only to lines without a marker).

Datapack line markers (inside a datapack; order in §3):

Marker Type Ref.
M) Markers neumaRk_markers.md
C) / C+ Chords / alternate chords neumaRk_chords.md §7
$) Fingering (fingers, strings, fixed position) neumaRk_fingering.md
A) Articulations neumaRk_articulations.md
N) / N+ / N2 Notes / new staff / voice 2 §4.5, neumaRk_voices.md
D) Dynamics neumaRk_dynamics.md
L) Lyrics neumaRk_lyrics.md
F) Format §4

Block markers (between datapacks, global):

Marker Type Ref.
PLAY) Imperative execution sub-program neumaRk_play_and_form.md
FORM) Descriptive form overview neumaRk_play_and_form.md
LYRICS) Per-section sung text neumaRk_lyrics.md §8
FOOT) Footnote definitions neumaRk_footnotes.md
TEXT) Prose positioned in the flow neumaRk_text_line.md
%%NAME … %%end Version/arrangement blocks neumaRk_versions.md

Structural lines (carry no musical content, not deducible):

Line Role Ref.
nrk:MAJOR.MINOR Version line (line 1; mandatory in the exchanged file) neumaRk_specification.md §8, neumaRk_header.md §2
H…) Header markers (HT / HCM / … / HV) neumaRk_header.md
blank line Structural separator (end of header / between datapacks) §3.bis.1
// … Comment (whole line or trailing) §11
line starting with -, then only - / spaces / % Vertical margin between datapacks; % = possible page break (-%) §10

3. Logical order of lines (implicit deduction)

In the absence of explicit markers, the type of the lines is deduced following the logical order:

  1. Markers (at most one line, optional)
  2. Chords
  3. one base C) line + up to 2 alternative C+ lines (§3.bis.7, limit E127)
  4. a second base C) line is error E129
  5. Note groups, each composed of:
  6. one Fingering line $) (optional, first; explicit only, never deduced — see neumaRk_fingering.md)
  7. one Articulations line (optional)
  8. one Notes line (mandatory in the absence of chords)
  9. one Dynamics line (optional, after — see neumaRk_dynamics.md)
  10. one Lyrics line (optional, last)

A datapack may contain from 1 to 4 note groups: each group corresponds to a staff of the system (see §4). 4. Format (at most one line, optional, always final)


3.bis Line-type deduction (normative algorithm)

§3 gives the logical order of the types; this section formalizes its deduction algorithm: how, in the absence of an explicit marker, the type of a line is determined from content + position. The rule written here is normative — it is the language, not an implementation detail. An explicit marker (§2.1) always wins: the algorithm that follows applies only to lines without a marker.

3.bis.1 Datapack structure

Only the Markers block and the chord block are unique per datapack (shared by the whole system, §4.1); everything else is a repeated note group, once per (staff × voice). The chord block is unique but positionable: its position among the note groups determines the staff above which it is rendered (cardinal rule in §4.1).

datapack   ::= [Markers]?  Item{1..}  [Format]?

Item       ::= NoteGroup | ChordBlock       // ChordBlock at most ONCE
NoteGroup  ::= [$]? [A]? N [D]? [L]?  ( [$]? [A]? N2 [D]? [L]? )?   // one staff (voice 1 [+ voice 2])
ChordBlock ::= ( [A]? C+ ){0,2}  [A]?  C)    // chord band (base + up to 2 alt.)
  • Markers: unique, always the first line (§8), shared.
  • ChordBlock (chord band): unique per datapack and shared by all staves (§4.1). It contains a single base C) line and up to 2 alternative C+ lines immediately above (§3.bis.7, limit E127); each line may be preceded by its own A) articulating its chord-rhythm (neumaRk_chords.md §4) — for this reason A) is admitted before C)/C+. The block appears at most once: two base C) lines are error E129.
  • NoteGroup (one staff): N mandatory, with optional $)/A)/D)/L) and an optional voice 2 N2 (with its own retinue, including its own $)). It repeats up to 4 staves (§4); with voice 2 it reaches 8 voice-blocks overall (§4.6). Voice 2 is not implicitly deducible — there is no way to distinguish, from content alone, "voice 2 of the same staff" from "new staff": it must always be declared with the explicit marker N2.
  • $) (fingering): never deduced — the cascade of §3.bis.4 never produces the Fingering type; the line exists only with the explicit $) marker. It fingers the first notes line below it (N)/N+/N2), with the group's A), if any, in between (neumaRk_fingering.md §2).
  • Format: unique, always final (§9).

Implicit chord-detection = head-only. The implicit deduction of the Chords type stays head-only (§3.bis.4, "open head" guard): without a marker, a chord-shaped line is read as chords only before the first notes line. A ChordBlock placed after the notes (between staves or below the last one) requires the explicit marker C) — which always wins the deduction (§2.1) and is rendered at the position where it is written (§4.1). This keeps implicit classification unchanged (no existing piece reclassified, §3.bis.8), opening anchoring only on explicit request.

3.bis.2 Computation model

Deduction is a single-pass, per-datapack automaton that scans the lines in source order while maintaining two pieces of state:

  • the last-row type — the type of the last line (initially Empty);
  • the head-closed flag — false until the first Notes line of the datapack appears, then true forever. It marks the boundary between Head and Groups.

The last-row-type guards are the transition function of the automaton: they encode both the order within a group, and the loop-back that opens the next group (re-entry on A or on N). The head-closed flag prevents Chords from reappearing once the staves have been entered.

3.bis.3 Pre-filter: decorative lines

Before the cascade, a purely decorative line — composed only of simple barlines, :, compact repeat forms (neumaRk_flow_and_repeats.md §6.1), dots, spaces, and tabs — is skipped and is not interpreted as musical content (it remains structural; its barline decorations belong semantically to the other lines). Exception: the >/^ rescue in §3.bis.6.

3.bis.4 Classification cascade (normative precedence)

On non-decorative lines without a marker, a first-match-wins cascade is applied. The order is binding: the content predicates are deliberately permissive and do not over-match only thanks to (a) this order and (b) the position guard. The triad that defines each type is predicate + guard + exclusions.

Scope. This cascade classifies only marker-less lines that carry musical content, and produces exactly the 6 types below. Explicit markers (§2.1) and structural lines — version line, header, blank lines, comments, margins (§10) — are determined by marker or position, not by this cascade: the complete inventory is in §2.2.

# Type Predicate (content) Guard (position) Exclusions
1 Markers only […] / barline / spaces is the first line of the datapack —
2 Chords ≥1 valid chord (neumaRk_chords.md §3) + barline / . / % / comment-label / spaces — or only % (TB1bis) head open (head not yet closed) not slash-only (TB2); not rest-only r/! (TB1)
3 Articulations charset of the A) vocabulary (neumaRk_articulations.md) last-row type ≠ Articulations not ^-only (TB3); not a valid notes-line (TB4)
4 Dynamics dynamics charset (< > c d f m p s z - . \| :) last-row type = Notes —
5 Lyrics word-char + . - _ ' \| : (the most permissive) last-row type ∈ —
6 Notes default / sink any —

Notes on the guards:

  • Markers (line 1): the implicit deduction of Markers occurs only on the first line of the datapack. A markers-shaped line further down is not Markers (an explicit M) remains possible wherever the spec allows it).
  • Chords (line 2): the normative guard for implicit deduction is head open. Without a marker, a chord-shaped line is read as chords only before the first notes line: for implicit deduction alone there is no chord-row after a notes-row. This condition holds only for implicit deduction, though: an explicit C) (§2.1) is admitted also after the notes and is rendered as a chord band at the position where it is written (§4.1). In either case the chords stay unique and shared by the system (§4.1); the chord block is unique per datapack (§3.bis.1) and a second base C) line is error E129.
  • Lyrics (line 5): the predicate is the most permissive (it matches almost any text). This is intentional: the permissiveness is contained by the position guard, and in any case the default type is Notes. It is not narrowed (that would be non-monotonic and would risk changing the classification of existing songs).
  • Notes default: line 6 is the sink — every line that reaches the bottom of the cascade is a notes line. It is also the loop-back mechanism: two consecutive N) (both sink) are two distinct staves. In the implicit form the second notes line does not reach the sink: after a Notes line, a line of words and barlines is Lyrics (line 5), so c d e f | under N) c4 d e f | is a text line. For two staves, write the marker (N) or N+).

3.bis.5 Tiebreakers

Three disambiguations resolve cases in which a permissive predicate would match the wrong type:

  • TB1 — rest-only → Notes. A line composed only of rest-tokens (r / !), barline, ., %, and spaces belongs to the stave (it is a notes line of rests only), not to an empty chord-row. It applies after Chords/Alt and after Articulations:
A7        ← Chords          | > .       ← Articulations
r         ← Notes (TB1)     r           ← Notes (TB1)

A single rule — "rest-only forces Notes": the line is intercepted before Chords and Articulations, regardless of the preceding row type. A guard requires a real rest-token r/! or %, so | > | (anacrusis) remains handled by the rescue (§3.bis.6).

! counts here as a token of a notes line, but it is not a rest: it is the repetition of the preceding note (neumaRk_notes_and_durations.md §5). A line of only ! with no note before (! ! under C) A7) is therefore Notes and gives E018.

  • TB1bis — %-only without a chord-row → Chords. A line composed only of % (plus barline, ., spaces — no rest-token r/!) with head open and in the absence of a real chord-row in the datapack is a chord-row of measure repeats only: each % repeats the last chord-measure (even from the preceding datapack — the measure-repeat lookback is cross-datapack). It is the exception to TB1: % without a rest-token is not forced to Notes, but falls into the Chords branch (which already accepts %).
| C7 | F7 |     ← Chords
| a b c | …     ← Notes (closes the head)

| % | % |       ← Chords (TB1bis): repeats C7, F7 from the datapack above
| d e f | …     ← Notes

Rationale: "chords that repeat while the melody changes" is the most common lead-sheet pattern. Two guards keep the case narrow:

  1. no rest-token — a line with r/! always remains Notes (TB1): rests are unambiguously notes;
  2. no real chord-row seen yet — if the head already has a chord-row with a real chord, a subsequent %-only line remains Notes (it is a notes-row of repeats), because the datapack's chord-row is already defined.

Escape-hatch for the opposite reading (a second notes staff of only %): the explicit marker N+ / N). Note: a datapack of only % without a valid source measure (neither in this datapack nor in the previous one, cross-datapack) emits E300 (measure-repeat with no source); declare it with N) if the intent is a standalone notes-row of repeats.

  • TB2 — slash-only → Notes. A line of standalone / only (plus barline, ., spaces) is slash rhythm (neumaRk_notes_and_durations.md §10), never a chord-row: / on its own is syntactically a quasi-chord but semantically a note event.
| / | / |     ← Notes (TB2)
  • TB3 — ^-only → Notes. A line of ^ only (plus | and spaces) is a notes line with ties only (neumaRk_notes_and_durations.md §6), not Articulations.
| ^ |     ← Notes (TB3)
  • TB4 — valid notes-line → Notes. A line that is a valid notes-line has priority over Articulations, even if it falls entirely within the charset of the A) vocabulary. Necessary because some letters of the vocabulary are also note-names/durations: in particular g (composes gl glissando, §9) is also the note G, and the durations 1–4 are in the charset → g, g4, g g g would be read as articulations and stolen from A).
A7        ← Chords
g         ← Notes (TB4), not Articulations

Predicate: every token (split on spaces; the barlines |/: at the edges are stripped) is a valid note-token according to the canonical notes grammar (a single source, so there is no divergence), and at least one token carries a real pitch [a-g]/r. Articulation-only tokens (>, tr, -, o, gl, ~…) do not match that grammar → the line remains Articulations; a placeholder line with no real pitch that contains , (| . , ^ |) → remains Articulations. A line of only . is decorative instead and is skipped (§3.bis.3), and one of only ^ is Notes (TB3). The >/^/~ rescue (§3.bis.6) also applies. Principle: in case of A)↔N) ambiguity, the note wins.

3.bis.6 > / ^ rescue

A line that the pre-filter (§3.bis.3) would have discarded as decorative — because > is readable as an anacrusis attached to a barline — but which contains > or ^ and is entirely articulations-charset, is recovered as Articulations (here > is an accent, not an anacrusis):

| > |     ← Articulations (rescue): accent on the first event of the measure

The rescue is a branch outside the cascade and always produces Articulations ( articulations may open a group in any position); it is subject to the same constraint (last-row type ≠ Articulations) and the ^-only exclusion (TB3).

Line type ≠ coloring of the leading >. The rescue decides only the type (Articulations); the | > | example has the > between barlines, not leading, and it is an accent. When instead an A)/D)/L) line aligned to the C/N carries a leading > in the upbeat column, that > is colored as the upbeat marker (red), not as an accent — see neumaRk_notes_and_durations.md §7 (Upbeat column in column-aligned lines). They are two distinct levels: line classification here, positional semantics of > there.

First line of the datapack. The rescue presupposes a context above it. In first position (line_id == 0) a line | > | is read as Markers/anacrusis, not as an accent: the Markers pre-filter (cascade line 1) takes precedence, and > is ambiguous between accent and upbeat when there is no notes line below it to disambiguate. It is a deliberate choice: in the absence of context, the anacrusis reading prevails.

3.bis.7 Post-pass: AlternateChords promotion

The base classification assigns Chords to all implicit chord-rows. A subsequent pass relabels as AlternateChords (C+, alternate chords above the base) the consecutive chord-rows that precede the last, only if no chord-row of the datapack carries an explicit marker (if the user has chosen the markers, their choice is respected). Limit: max 2 alternate lines per datapack (E127). Full spec in neumaRk_chords.md §7.

3.bis.8 Derivations and invariant

  • The articulation classification charset (line 3) derives from the closed vocabulary of neumaRk_articulations.md (single source): deduction and rendering must not diverge.
  • Invariant: the line→type classification defined here is normative; every conforming implementation must produce it exactly.
  • Classification (TB1bis): a %-only line with no rest-token, in an open head and without a real chord-row, is a chord-row of measure repeats (§3.bis.5 TB1bis).
  • Classification (TB4): a line that is a valid notes-line but falls in the A) charset (typically note-only lines like g: g, g4, g g g) is a notes-line — the note wins (§3.bis.5 TB4).

4. Multiple staves per datapack (multi-stave)

A datapack may represent a system with multiple aligned staves (e.g. melody + bass). The number of staves is given by the number of note groups ($?A?ND?L?) present, up to a maximum of 4.

The number of staves may vary from one datapack to another in the same song: one datapack may contain a single staff and the next two or more, without prior declarations.

4.1 Elements shared by the system

The following elements are common to all staves of the datapack:

  • the Markers line
  • the chord block (base C) + optional alternatives C+)
  • barlines and measure decorators (voltas, change of meter/key, $, @, DC, FINE, etc.)
  • the key and meter of the system

The measure barlines cross all staves vertically.

Cardinal rule — chord-block position (anchor). The chord block stays a single harmony shared by the system, but it is rendered at the vertical position where it is written relative to the note staves. Let k be the number of note staves (N)) written before the chord block (N2 does not count: it shares its parent N)'s staff) and n the total number of staves in the system:

Position in source Rendering
block at the head (k = 0) chord band above the first staff (default)
block between two staves (0 < k < n) chord band in the interline, above staff k (2-staff lead sheet, symbols between melody and bass)
block after the last staff (k = n) chord band below the last staff

Position does not multiply or bind the chords to a staff: it only changes the vertical rendering, not the harmony. Markers (section letters, voltas) always stay at the top, independent of the anchor. Per-staff independent harmonies (a different C) for each N)) are outside the model: the system harmony is unique.

A) binding. An A) always articulates the line immediately below it: inside the chord block it articulates the following C+/C) (chord-rhythm, neumaRk_chords.md §4); above an N) it articulates that staff. So a chord's A) does not "slip" onto the notes staff below.

An A) without its row below — written under the datapack's last N), or followed by another A) — articulates nothing: it is ignored and the parser reports W174 (non-blocking). The exception is a datapack with no note rows, where the A) applies to the automatic rests.

Example (2-staff lead sheet, chords between melody and bass — Sher style):

N) c8 d e f | g a b c           // staff 0 (melody)
C) | CMA7 | DbMA7               // chord block: k=1, n=2 → interline, above the bass
N) (@F) c,4 e g e | c e g e     // staff 1 (bass)

4.2 Elements independent per staff

Each group ($?A?ND?L?) defines an independent staff with its own:

  • Notes (mandatory)
  • Fingering, Articulations, Dynamics, Lyrics (optional)
  • clef declared via the inline directive (@…) as the first token of the Notes line — see neumaRk_notes_and_durations.md §9. In the absence of a directive, the last clef defined in the context applies (default: treble clef).
  • ties, beams, and tuplets (all contained within the staff)

4.3 Horizontal alignment

The vertical alignment between staves is temporal: measure n of a stave sits above measure n of the others. If a stave (or a voice, neumaRk_voices.md) has fewer measures than the others, its missing measures are completed with measure rests, with no diagnostics: N) c d e f | g a b c | above N+ c d e f | gives the second stave a rest in the second measure.

4.4 Example

C) | Gm6 |
N) | g | a | bb | a |
N) | (@F) g,4 bb8 d e4 d | g,4 bb8 d e4 d | g,4 bb8 d e4 d | g,4 bb8 d e4 d |

A 2-staff system: the first in treble clef (default), the second in bass clef. The chord Gm6 is common to the system.


4.5 Staff continuity between datapacks (N+)

When a song has multiple datapacks, each staff (stave) has a persistent identity: the "first staff" of a datapack is the same "first staff" of the previous one — it inherits pitch context, the clef in effect, durCtx, and is rendered in the same vertical position.

The base identity is positional: the first N) of the datapack corresponds to the first N) of the previous datapack, the second to the second, and so on.

To add a new staff in a datapack without breaking its identity with the previous ones, the N+ marker is used in place of N).

N+   <content of the new staff>

The + replaces the ) (it does not add to it): the prefix remains 2 characters as with N), so the vertical alignment of measures remains identical between adjacent staves.

Rules

  • N) = continuation of the next staff of the previous datapack (FIFO, in source order). It inherits its context (pitch, clef, duration).
  • A datapack without note lines (an M) on its own, a chords-only C)) does not count: the "previous datapack" of an N) is the last one that had staves. The automatic rests or slashes of the note-less datapack sit on the first of those staves, in its clef.
  • N+ = new staff introduced in this datapack. It starts with fresh context (the orientation reference depends on the initial clef, see neumaRk_notes_and_durations.md §2.2).
  • Source order = visual order top→bottom. The N+ marker determines only the identity (new vs. continuation), not the position.
  • N+ in the first datapack of the song is admitted but redundant (in the absence of preceding datapacks all staves are necessarily new); it is treated as a normal N).
  • Unchanged limit: max 4 total staves per datapack (sum of N)
  • N+).

Errors

  • E122 — N) without match: the datapack has more N) than the number of staves the previous one had. N+ is missing (or there is one N) too many).
  • W168 (non-blocking) — N+ redeclared: the datapack has at least one N+ and some stave of the previous one is left without an N) continuation. Typically N+ was repeated in every datapack instead of being written only where the stave is born. N+ does not consume the FIFO queue: the previous stave is orphaned and the N) that follows binds to the identity of the stave above, inheriting its clef and octave reference (typical symptom: the rests of a bass-clef stave drawn at treble positions, above the staff). Fix: N+ only in the datapack that introduces the stave, N) in every later one.

Example

Datapack 1 (intro with 2 staves, treble + bass):

M) [intro]
C) G7
N) |: <d b>2 <e c>4 | <f d>2 <e c>4 :|
N) (@F) g,4. d'8 e d | f4. d8 e d

Datapack 2 (Theme): adds a third staff at the top (vocal melody), keeping the intro's treble and bass in the middle and at the bottom.

M) [Theme]
C) > | G7
N+ >  d8 | b'^ | b2 r8 d,
A) >    | . ! | . !
N) > |: <d b>2 <e c>4 | <f d>2 <e c>4 :|
N) > | g,4. d'8 e d   | f4. d8 e d

Resulting mapping (for the engine, not visible in source):

source row type identity
1 (N+) new new stave (top)
2 (N)) cont. = first intro staff (treble)
3 (N)) cont. = second intro staff (bass)

The context (last_pitch, clef, durCtx) of staves 2 and 3 of the Theme comes from staves 1 and 2 of the intro; staff 1 of the Theme starts fresh.

Cases not covered

The N)/N+ syntax expresses the common situation "I add a staff at the top/in the middle/at the bottom", but it does not cover:

  • selective drop (a datapack that keeps the 2nd staff of the previous one but drops the 1st);
  • reorder (swapping the visual order of existing staves while keeping their identity).

For these cases a future extension could introduce an explicit reference to the staff's internal ID (e.g. N<n>)). Not spec'd in this version.


4.6 Second voice per staff (N2)

Each staff may host an independent second voice, introduced by the N2 line. Stave and voice are distinct concepts: the staff is the pentagram, the voice is a musical stream within the staff.

Voice 2 shares the clef, key, meter, barlines, and chord line with voice 1, but maintains an independent persistent musical context. Voice 1 has stems up, voice 2 stems down.

The complete specification of syntax, context, binding, and diagnostics is in neumaRk_voices.md.


5. Datapack validity rules

A datapack is valid if:

  • it contains at least one Notes line or one Chords line
  • it respects the logical order of the lines
  • all musical lines are temporally alignable

It is invalid:

  • a datapack with only text lines

A misplaced or duplicated Format line does not invalidate the datapack: it is corrected with W182 (§9).


6. Barlines and measures

All musical lines (Markers, Chords, Articulations, Notes, Dynamics, Lyrics):

  • contain barlines — simple and compound — and any measure decorators (in the absence of barlines all content belongs to the first measure of the staff)
  • implicitly define the division into measures

The Format line is the only exception: it does not admit barlines (neither simple nor compound) and no measure decorators (§9).

The first barline. The opening barline of a row is optional when only the row identifier and context objects come before it (§7.3: meter, key, clef, harmony in effect, pitch reference, also in a mixed group). Context does not create a measure: it holds for the measure that follows, as if it were glued to the entry barline. Anything else — a label, a note, a chord symbol, a text — makes the segment a measure, and the barline that closes it is a real barline.

C) (Bb) | Eb7 F |        // one measure in B-flat: (Bb) is the key
N) | g2 a |

C) [Intro] | Eb7 F |     // two measures: the first carries only the label
N) | r1 | g2 a |

N) (3/4) | c d e |       // one 3/4 measure, like N) |(3/4) c d e |

6.1 Supported barlines

The admitted barlines are:

  • | simple barline
  • || double barline
  • |. or .| end
  • |: repeat start
  • :| repeat end

The final barline is written preceded by a space (c d e f |); the glued form c d e f| is tolerated and read the same way.

6.2 What sits between quotes is text

A | between double quotes "…" is not a barline, and a > between quotes is not a pickup: they are characters of the text. This holds for every quoted container — chord comment-label, note annotation, M)/A)/D) label — so a label may contain those signs without splitting the measure:

C) | C-7"a | b" | F7 |        // two measures, the chord keeps its label
M) | "a > b" |                // one measure, no pickup

Conditions, the same as for comments (§11):

  • \" is a literal quote and does not delimit;
  • an unclosed quote protects nothing: a stray quote must not swallow the barlines to the end of the line;
  • exception: on sung L) rows and inside LYRICS) blocks the quotes are word characters, so they do not protect there and a | splits the measure as usual.

7. Measure decorators

Measure decorators are elements adjacent to a barline and are divided into:

  • BEGIN decorators (to the right of the barline)
  • END decorators (to the left of the barline)

7.1 BEGIN decorators

Positioned immediately to the right of the barline.

Possible decorators:

  • Measure-context object — see §7.3. Within parentheses, it declares meter, key, clef, the harmony in effect and a pitch reference:
|(3/4,Dm)
|([3+3+2]/8)
|(@F)
|(3/4,Ab,@F)
  • Volta endings

  • text within square brackets

  • optional +n for the duration in measures

Example:

|[1.]+4

In the absence of +n, the volta closes automatically at the first :| encountered within 4 measures (the typical case of [1.] endings). If no :| appears within the following 4 measures, the volta indicates an exit — use +n for non-standard cases.

  • $ segno
  • @ coda

Order within the BEGIN group is free: |(3/4)[1] and |[1](3/4) are equivalent. The canonical form — the one serialization writes back — is |[1](3/4): flow touches the barline, context touches the music.


7.2 END decorators

Positioned immediately to the left of the barline.

Supported decorators:

  • DC
  • DCal@
  • DCalFINE
  • D$
  • D$al@
  • D$alFINE
  • FINE
  • al@
  • free text within square brackets (graphic annotation)

The $ and @ signs are BEGIN decorators (see §7.1): they identify the point where the segno or coda is located, not a jump toward them.


7.3 Measure-context object

The context that "runs" between measures — meter, key, clef in effect, the harmony that persists, the pitch reference and the current duration — is writable. You declare it with a parenthesised group at a measure boundary.

C) A7 |[1](@F7) !(2.) | G7 (@D7):open|
N) a  |[1](3/4,F,@F,f@4_8) b c d | g2. (5/4,D,@G,d@2_2):open|

(:open| is the "open" repeat end, neumaRk_flow_and_repeats.md §6.1.)

Components

@ means set; what it sets is decided by the row it is written on.

form meaning level
3/4, [3+3+2]/8 meter (allowed forms in neumaRk_header.md §10.4) measure (shared across rows)
Ab, Dm, X key measure (shared across rows)
@F, @G8vb, @C3 on an N) row clef stave
@F13, @NC on a C) row harmony in effect stave (chord block)
f@4_8 on an N) row pitch + duration reference stave

Separators: comma, space or both. Components may appear in any order; the canonical one is the order of the table (meter, key, clef, reference), and that is what serialization writes back.

f@4_8 is not new syntax: it is the absolute octave of neumaRk_notes_and_durations.md §2.3 (@<n>_ plus a mandatory duration), used here as a reference rather than as a note. It fixes pitch and duration for the music that follows, without writing any note.

A clef is not allowed on a C) row (the clef belongs to the stave, and the stave is declared by N)): there @ always means harmony. That is why (@F), (@G) and (@C) on C) are chord symbols (F, G, C major), with no diagnostics; a form that can only be a clef ((@F8vb), (@G8va), (@G8vb), (@F8)) gives E135; (@C3) or (@F4) are read as chord symbols with an unknown suffix (W103, neumaRk_chords.md). The pitch reference f@4_8 belongs to the stave and is written on N): it is not a component of the chord row. On C) and C+, glued to the barline or before the first barline, it gives E135 (E135.pitch_ref_on_secondary_row), as on the other rows that do not carry the stave (below).

Also on C) the position decides, because the parenthesis slot inside the measure is already taken by the optional chord (F13) and by chord rhythm, and a bare key would be indistinguishable from a chord symbol:

  • a group glued to the barline (C) |(Bb) Eb7) or written before the first barline (C) (Bb) | Eb7, §6) is context: (Bb) is the key;
  • a group detached after the barline or inside the measure (C) | (Bb) Eb7, C) | Eb7 (Bb) F) is an optional chord;
  • a detached meter at a measure boundary is context on C) too (C) | (3/4) G |, C) | G (3/4) | G |): a chord symbol never has the shape of a meter.
C) |(Bb) Eb7 F | (Bb) Eb7 F |    // 1st measure: key of B-flat; 2nd: optional chord Bb
N) | g2 a | g2 a |

X as a context key means "no key signature", like C.

Position

Measure boundaries only. The barline may be explicit or implicit: N) (@F) >g8 | … is valid — it is the entry boundary of the first measure of a row that does not open with |.

The segment before the first barline of a row becomes a measure only if it contains something that is not context (§6): N) | a is a single measure, and so is N) (@F) | a — the (@F) is the entry context of the a measure, exactly as in N) |(@F) a or N) (@F) a. In this position the group (or the sequence of groups) is read as glued to the entry barline, with the same vocabulary, on every measure row (N), N2, N+, C), C+, M), A), D), L), $)). On a C) row (Bb) | Eb7 F | is therefore one measure in B-flat and (@F13) | G one measure with the declared harmony, while (F13) | G — an optional chord, i.e. music — stays two measures. A label makes the barline real: C) [Intro] | Eb7 F | has two measures, the first with the label alone.

Free position. A detached group at a measure boundary, in a measure with no notes (the M), A), D), L), $) rows, or a C) measure), follows the adjacency rule below: before the content it is an entry, after it an exit that holds from the next measure. A label counts as content: M) (3/4) [Intro] | puts 3/4 on the label's measure, M) [Intro] (3/4) | from the measure after. On the M), A), D), L), $) rows, only the meter is context in free position: a detached key or pitch reference is not context, and the row reads it with its own grammar (in L) | la (E) | (E) is a syllable). The M) grammar has no place for them (neumaRk_markers.md §7): M) | (Bb) | gives W186 (W186.key_free_position) and the group is discarded; to change key, glue it to the barline, M) |(Bb) |.

Rows that do not carry the stave. Clef and pitch reference belong to the stave and are written on N) (the clef not on N2, neumaRk_voices.md). On M), A), D), L) and $), glued to the barline or before the first barline, also in a mixed group, they give E135 (E135.clef_on_secondary_row, E135.pitch_ref_on_secondary_row): the component is discarded and the N) stave stays as it was; the other components of the group hold (A) |(3/4,c@3_8) . declares 3/4). The pitch reference gives the same E135 on C) and C+ too, with the chord row above or below N) (C) |(3/4,c@3_8) G . . | declares 3/4, the N) notes do not change). Meter and key belong to the measure and may be written on any row.

The adjacency rule, in one sentence: context touches the music, flow touches the barline.

  • entry (music to the right): |[1](3/4,@F) c4
  • exit (music to the left): d (@G,a@4_2):open|

The space sits on the music side only, and it is mandatory: glued to a note, a parenthesis is a note decoration (notehead, glissando, chord rhythm). A lone group in a measure with no music is an entry declaration: it has no music to its left to touch.

Mid-measure, only a clef change ((@F)) is allowed: that is not measure context, it is a notational event falling on a note. Other components outside a measure boundary are rejected → E136.

An invalid meter ((1/1), (5/3), (0/4), (3/32): the denominator must be 2, 4, 8 or 16 and the numerator at least 1) is rejected with E136 (E136.context_meter_invalid): the component is discarded and the previous meter stays. In a group with several components the others hold ((3/4,1/1) means 3/4). The same criterion applies to the header meter (neumaRk_header.md §10.4, E303).

The decorator may also be adjacent to an anacrusis barline > (besides |): >(@F), >(3/4,Ab,@F). Glued >( … ) is disambiguated from the reprise pickup (neumaRk_notes_and_durations.md §7.2) by the first character after >(: a context-starter (@, digit, [, uppercase A–G) → context object; a lowercase note / r / s → reprise pickup. (Keys use uppercase, pickup notes lowercase: no collision.)

Semantics

  • Measure-level components (meter, key) stay shared across the rows of the datapack: the first row that writes wins. If a later row declares a different meter or key for the same measure, its value is ignored with W159 (W159.context_conflict for the meter, W159.context_key_conflict for the key), and the rewritten document gives it the first row's value: C) |(3/4) G . . | above N) |(2/4) a4 b | gives a 3/4 measure. The same rule applies to flow decorators that differ between two rows of the same measure (segno, coda, jumps: W159, the row written first wins; neumaRk_markers.md §2.1).
  • Stave-level components (clef, harmony, reference) belong to the row they are written on: N+ and N) in the same datapack each declare their own reference, with no conflict.
  • A component produces no sign by itself. Meter, key and clef are drawn because they change something the reader must see; a reference and a harmony have nothing to show. A clef is printed before the music that consumes it; if the change falls on a system break, at the start of the new system — and, when the courtesy announcement is on, also at the end of the previous system (a renderer preference: where you write the object changes nothing). Only the initial clef of the stave sets the octave reference (neumaRk_notes_and_durations.md §9); later changes are graphical only.
  • The object does not count a position for A) and D) alignment: adding one does not shift the articulations of the measure (like the slash /, which counts as a single position).
  • Announced twice at the same instant (…(5/4,D)|(3/4,F)…, also across a double barline (5/4,D)||(3/4,F)): W169, and the entry wins — it is the one closest to the music it affects. Two barlines separated by a space ((5/4,D)| |(3/4,F)) enclose an empty measure instead: they are two different instants, no warning.
  • An exit context on the last measure of the piece has no music to apply to: it has no effect, and the rewritten document does not keep it.

Context-only row and datapack

An empty measure carrying a declaration is still a measure: N) c d e f | (@F) | c d e f | has three measures, exactly like N) c d e f | | c d e f |. The clef change is printed at the start of the second one, above its whole-measure rest.

What may go unprinted is the row:

  • a row whose content is nothing but context objects (N) (@F) |) is not printed;
  • a datapack whose rows are all like that takes no space in the score: no staff, no default rest, no bar number, no duration in the flow. What it declares applies to the music that follows, and is drawn there (clef, meter, key);
  • in a datapack that does print, a context-only stave row (N)/N+) does not appear in that system, in any position — N) c d e f | above N+ (@F) | gives a one-stave system, and the second stave appears from the datapack where it has music; the same with N) (@G) | above N) (@F) c e g e |. Chord symbols and markings of the other staves stay with their staves. If removing the context-only staves would leave none (an M) [A] above N) (@F) |), they print normally.

This is how you declare context without writing music: at a precise point in the piece, or inside a %%NAME block that may not be rendered.

A context-only datapack that declares no staves (C) |(@F13) | on its own) does not break stave continuity either: the N) of the next datapack continues the staves from before.

A flow sign (repeat, volta, decorator), a marker or written harmony make a system exist even without notes: a datapack carrying them prints.


8. Markers line

The Markers line:

  • contains markers enclosed within square brackets
  • the markers refer to the start of the measure

If a marker is preceded by a barline:

  • there must be a space between the barline and the marker
  • to avoid ambiguity with the volta decorators: a glued |[A] is read as a volta labelled A, not as a section

It is best practice to place the flow decorators (DC, D$, coda, etc.) on this line.


9. Format line

The Format line is always declared with the explicit marker F): there is no implicit form, and a line of alignment marks only without F) (|*|) is a notes line (invalid).

The Format line, if present, must be:

  • unique per datapack;
  • always the last line of the datapack;
  • made of one alignment mark, with optional side content (§9.1) before and/or after it.

It contains alignment indications:

Symbol Alignment
\|* LEFT
*\| RIGHT
\|*\| CENTER
\|**\| JUSTIFIED (default)

A mark counts only as a whole token, separated by spaces or by the edge of the line: [A|8], **x**| or \|* are not marks. If the line carries more than one, the first one holds.

9.1 Content beside the system

A system aligned left, right or center leaves free space. The F) line can fill it: the content goes on the side of the mark where it is written.

F) |* On cue, D.S. al @              // system on the left, text on the right
F) Dal $ al @ poi [Coda]&fermata *|  // text on the left, system on the right
F) Solo: |*| [B]x2                   // centered, content on both sides

The content of each side is a FORM) body (neumaRk_play_and_form.md §3–§6), with no quotes around it: markup-aware prose, section boxes with their labels and postfixes, the $ @ &fermata tokens, ; to break the line, footnote references [^…] and links [text=>url]. As in FORM), nothing is executed: a $ or @ written here is not a flow sign, and a box that matches no M) section is drawn as an unresolved reference, with no diagnostics. To write an alignment mark as text, prefix it with \ (\|*). A // opens the trailing comment, as on every line (§11).

The content is laid out in the free space, at a fixed distance from the system, and vertically centered on the system's staves. If it does not fit on one line, the system narrows (up to one fifth of its width, never below the minimum space of its measures) and then the content wraps.

The free space depends on the alignment: on the right with |*, on the left with *|, on both sides with |*|; with |**| there is none.

9.2 Diagnostics

An F) line that breaks these rules emits W182 and is corrected:

  • it is not the last: it is moved to the end (W182.not_last);
  • there is more than one: the last one holds (W182.duplicate);
  • it carries no mark as a whole token, or more than one: the line is ignored, or everything from the second mark on (W182.extra_content);
  • it carries content on the side the system occupies (F) text |*, F) *| text) or with |**|: that content is ignored and the alignment holds (W182.no_room).

Boxes in side content have the same diagnostics as FORM) (W141, W142, W143, E302).


10. Margins between datapacks

A line:

  • that follows a blank line
  • that begins with -
  • that contains only -, spaces, tabs, or %

indicates a vertical margin between datapacks.

The depth of the margin is given by the maximum number of consecutive -:

  • - - - → margin 1
  • - -- - → margin 2
  • --- - → margin 3

The % symbol indicates a possible page break.

A line of only %, with no leading -, is not a margin: it is a chord row of measure repeats only (§3.bis.5, TB1bis). The page break is written -%.

11. Comments

Single-line comments are permitted in the form // comment. The scope is the line: everything that follows // up to the newline is ignored by the parser, except in the two cases below. Comments may stand on their own line (above/below a datapack, or between the lines of a datapack) or trail any line of code, including the header lines of the free-text blocks (TEXT), INFO), PLAY), FORM), LYRICS), FOOT)).

The discriminant between the two forms is positional: if there is at least one non-whitespace character before //, the comment is trailing (the part of code before // remains valid); otherwise the entire line is a comment.

Two contexts protect a //, that is, make it text rather than the start of a comment:

  1. Inside a link [text=>url] — including the escaped form \[text=>url] — the // is part of the address (https://…, see neumaRk_text_markup.md §3quater). A // after the closing ] is a comment again.
  2. Between double quotes "…" — chord comment-label, note annotation, M)/A)/D) label, INFO) label and body: what sits between quotes is text, as already holds for barlines (§6.2: a | between quotes does not split a measure). \" is a literal quote and does not delimit, and an unclosed quote protects nothing (a stray quote must not swallow the comment to the end of the line). Exception: on sung L) rows and inside LYRICS) blocks the quotes are word characters, so they do not protect there and the // opens a comment.

A comment is preserved where it is written: neither its position nor its form changes when the document is rewritten (a change of format, neumaRk_formats.md, or a rewrite by an editor). A comment on its own line inside a free-text block stays inside that block, on its line.

Multi-line comments (/* … */) are not supported: NRK is a column-sensitive language (the barline | align the lines of the datapack vertically) and block comments would break the alignment.

The sequence %% at the start of a line is not a comment: it is reserved for the song version blocks (see neumaRk_versions.md).

11.1 INFO) — a comment meant for the reader

// comment lives in the source only. INFO) is its second-level twin: it carries text meant for the reader but not for engraving.

INFO) "Transcriber's note" Transcribed from the 1961 record;
the chord symbols follow the original lead sheet.

The body is not part of the score: it is not drawn among the signs and enters no export (PDF, MusicXML, LilyPond). An interactive rendering may make it available on request at the point it is anchored to; a static one omits it. The text has no musical value: no construct references it, no execution reads it. It does travel with the document, always: every .nrk file saved, exported or shared keeps it.

Reference rendering (informative). When there is a body, the interactive rendering places a small ⓘ icon after the label (or on its own, if there is no label) that opens it; a label without a body has no icon. Static exports (PDF) drop the icon and keep the label.

Form. Marker at the start of the line; the prose may start on that line or below. Following lines are continuation, markup-aware prose (neumaRk_text_markup.md). The block ends at the first of the three things in §13: an unescaped line marker, a blank line, the end of the document.

Label. Right after INFO) there may be a "…" container, which must close on the same line: it is an engraved label, score in every respect — printed, exported, part of the fit and the pagination. Whatever follows the closing quote is already body; the two forms

INFO) "Studio version"
The studio take opens with …
INFO) "Studio version" The studio take opens with …

are equivalent. A body that must start with a quote writes it escaped (\", neumaRk_text_markup.md §4). A label without a body is a legitimate use: a short caption, with nothing to open.

Anchoring: the position in the source. There are no reference labels: where you write INFO) decides what it refers to.

Where INFO) sits Anchor
among the header lines (H…)), with no blank line in between the header
before the musical rows of a datapack the head of that datapack
after the musical rows of a datapack the tail of that datapack
alone, between two blank lines standalone, where it stands
at the head of a %%NAME block the version block (neumaRk_versions.md §2.4.3)

The blank line ends INFO) precisely because that is how the author says what it refers to: detached from the music below it stands alone, attached it is that music's head. The body is prose: it breaks lines on the source newline, and ; is a literal character in it (neumaRk_text_markup.md §3bis).

One per anchor: at most one INFO) in the header, one at the head and one at the tail of each datapack; two standalone blocks separated by a blank line are two distinct anchors. A second INFO) on the same anchor is ignored (W170, the first one wins). A block with neither label nor body is ignored (W171). A label not closed on the same line is not a label: the line is read as body (W172). An INFO) between the musical rows of a datapack is an error (E137): write it before the first row or after the last one.

Which one to use: an instruction about the music is an M) annotation; laid-out prose is TEXT); saying what you are reading and why is INFO). Not to be confused with a description the host keeps about the song: that is organisation of the host, while INFO) is content of the song and follows it everywhere.


12. Version blocks

Between one datapack and another, version blocks may appear, marked %%NAME … %%end, which enclose one or more alternate datapacks of the song. The blocks live standalone between datapacks, not inside them. See neumaRk_versions.md for the full spec.


13. End of free-text blocks

Free-text blocks — PLAY), FORM), LYRICS), FOOT), TEXT), INFO) — continue on the lines after the line that opens them. They all share a single end rule, defined here and referenced by their own specifications:

A free-text block ends at the first unescaped explicit line marker, or at the end of the document.

13.1 Line marker

A line is a line marker if, after leading whitespace, it does not start with \ and starts with one of:

Family Markers
datapack lines M) C) N) A) D) L) F)
2-character variants C+ N+ N2, followed by a space or the end of the line
block markers PLAY) FORM) LYRICS) FOOT) TEXT) INFO)
versions %%
header lines H…) (HT) HC) … HV), neumaRk_header.md)

A comment line (// …) is not a line marker: it does not end the block, and it is preserved inside the block, on the line where it is written (§11).

These are the lines of the inventories in §2.2. An implicit marker row (a line such as [Intro], without M)) does not end a block: inside PLAY), FORM) and LYRICS) a [NAME] alone on a line is content (a section box, an extended-form entry), and inside TEXT) it is prose. To end a block in front of a marker row, write the explicit marker:

TEXT) some background prose
M) [Intro]
C) | Cm11 |
N) | c,4 g' c r |

The same holds for INFO): below an INFO) the marker row must be written explicitly, M) [Intro], or [Intro] is absorbed as the body of the note.

N2O or C++ at the start of a prose line are not markers: the 2-character variants are markers only when followed by a space or the end of the line.

13.2 Escape at the start of a line

A \ at the start of a line makes the following marker literal: the line is not a marker and belongs to the block.

TEXT) The reprise is played
\N) as before, but softly.
  • When rendered, the leading \ is removed: the line reads "N) as before, but softly.".
  • The source keeps it: on round-trip the line is written back with the \.
  • If the character after \ is a markup metacharacter (\[, \*, \#, …), the markup escape applies (neumaRk_text_markup.md §4): the displayed result is the same, the character reads literally.

13.3 The blank line

A blank line ends A blank line does not end
PLAY) FORM) FOOT) INFO) TEXT) LYRICS)

A blank line does not end a block where it is content: in TEXT) and LYRICS) it separates paragraphs or verses and is printed as vertical space. It ends a block everywhere else, because there it is the separator between datapacks (§1).


14. Diagnostics

Code Condition
E110 More than 4 staves in the datapack (§4)
E122 N) with no stave to continue in the previous datapack: use N+ (§4.5)
E127 More than 2 alternate C+ rows (§3.bis.7)
E129 More than one base C) row (§3.bis.1)
E135 Clef on a C) row (E135.clef_on_chord_row), clef (E135.clef_on_secondary_row) on M)/A)/D)/L)/$), pitch reference (E135.pitch_ref_on_secondary_row) on M)/A)/D)/L)/$)/C)/C+: component discarded (§7.3)
E136 Unrecognized context-object component, outside a measure boundary, or invalid meter (E136.context_meter_invalid) (§7.3)
E137 INFO) among the musical rows of a datapack (§11.1)
E300 Measure repeat % without a source (§3.bis.5, TB1bis)
W159 Meter, key or flow decorators differing between rows of the same measure: the first row wins (§7.3)
W168 N+ redeclared: a stave of the previous datapack is left without continuation (§4.5)
W169 Context announced twice at the same instant (§7.3)
W170 Second INFO) on the same anchor (§11.1)
W171 Empty INFO) (§11.1)
W172 INFO) label not closed on the same line (§11.1)
W174 A) row without its row below: ignored (§4.1)
W182 F) row not last, duplicated, without a mark or with content where there is no room (§9.2)
W186 Key detached in free position on the M) row (W186.key_free_position): discarded (§7.3, neumaRk_markers.md §7)