Official Changelog¶
neumaRk 0.6.0 — Current version¶
Reference version declared by the examples in the specification
(nrk:0.6). It extends 0.5.0 with a broad wave of features, listed
below.
What's new in 0.6¶
0.6 extends 0.5.0 with the following features; each is specified in the referenced document.
| Feature | Document |
|---|---|
| Grace notes | neumaRk_grace_notes.md |
Second voice N2 |
neumaRk_voices.md |
Voice crossing X (two voices swap position; oblique "X") |
neumaRk_voices.md §7 |
Staff continuity N+ / multi-stave |
neumaRk_datapack.md §4 |
Measure repeat % |
neumaRk_flow_and_repeats.md §7 |
Polychords [top\|bottom] |
neumaRk_chords.md §6 |
Comment-label / alternate chords C+ |
neumaRk_chords.md §7 |
Harmony persistence (empty cell + leading . continuation) + no-chord NC + silent harmonic rest R |
neumaRk_chords.md §1.1-1.3 |
| Unified text markup | neumaRk_text_markup.md |
Collapsible sections [? …] / [!] |
neumaRk_markers.md §4 |
Extended dynamics D) |
neumaRk_dynamics.md |
Play directive (=Style,Tempo) |
neumaRk_play_directive.md |
PLAY) / FORM) |
neumaRk_play_and_form.md |
Versions %%NAME … %%end |
neumaRk_versions.md |
Articulations A) (base + extensions §8-12) |
neumaRk_articulations.md |
Lyrics L) + LYRICS) block |
neumaRk_lyrics.md |
Extended notes N) (noteheads, glissando, rhythm {}) |
neumaRk_notes_and_durations.md §11-12 |
Editorial group parentheses ( … ) in N) |
neumaRk_notes_and_durations.md §11.4 |
Unpitched notes = (staff position, exempt from transposition) |
neumaRk_notes_and_durations.md §11.5 |
Multi-measure rest RN |
neumaRk_notes_and_durations.md §13 |
Spacer s (invisible rest) |
neumaRk_notes_and_durations.md §14 |
Section anacrusis (>>> open measure + > mid-song upbeat) |
neumaRk_notes_and_durations.md §7.1 |
Authorial chromatic-display default chr (tonal read without a key signature) |
neumaRk_header.md §10.3 |
| Collections (book / playlist) | neumaRk_collections.md |
Reprise pickup (>( … ) out-of-time upbeat in parentheses) |
neumaRk_notes_and_durations.md §7.2 |
Footnotes (reference [^label] + FOOT) block) |
neumaRk_footnotes.md |
Text lines TEXT) (multi-line prose positioned in the flow: line breaks respected, empty lines, per-line inline alignment, justify with markup, vertical anchoring ^/_) |
neumaRk_text_line.md |
Caesura (Luftpause) V — note-anchored articulation on A) (uppercase V ≠ up-bow v) |
neumaRk_articulations.md §3 |
| Chromatic ornaments (mordents / turn / trill with an accidental on the auxiliary) | neumaRk_articulations.md §3.1 |
Boxless measure text decorator \| "text"\| (no-box twin of [text]) |
neumaRk_flow_and_repeats.md §4.3 |
Frame model for all text containers: "…", ["…"] (shorthand […]), ("…") on chord labels, A), D), end-decorators. New code W177 |
neumaRk_text_markup.md §2.1 |
Note-anchored FINE (end of the piece on a note, A) line) |
neumaRk_flow_and_repeats.md §4.2.1 |
Unified begin-barline decorator: clef combinable with meter/key in (…) (\|(3/4,Ab,@F)) + composition with the anacrusis (>(@F), canonical (@F) >g8); E125 extended to any clef on N2 |
neumaRk_datapack.md §7.1 |
Measure-context object (…) at both boundaries: the barline decorator extends its vocabulary to the harmony in effect (@F13, @NC on C)) and to a pitch+duration reference (f@4_8), opens the exit position (… (@G):open\|) and free order relative to flow decorators (canonical \|[1](3/4)). A context-only row is not printed, and a datapack of only such rows takes no space. New codes E135, E136, W169 |
neumaRk_datapack.md §7.3 |
Second-level comment INFO): text for the reader, never engraved nor exported, with an optional engraved label "…"; anchored by position (header, head/tail of a datapack, standalone, version block). New codes E137, W170, W171, W172 |
neumaRk_datapack.md §11.1 |
External links in markup: [text=>url] and the short form [=>url] (shows the domain); http/https only, address literal up to the ]; activatable on screen, plain text in print. New codes W175, W176 |
neumaRk_text_markup.md §3quater |
Persisting harmony joins the hermetic context of version blocks: every alternative starts from the harmony in force on entering the region, and after %%end an empty cell inherits the canonical block's, whichever version is selected. New code W178 |
neumaRk_versions.md §4.1.1 |
Fingering row $) (2026-10-01): pressing finger (0–5, substitution n-m), string (A–G with b/#, or 1–9), plucking finger (p i m a c), one token per event like A); <…> per chord pitch; position prefixes ' - +; fixed position [ … ] with optional label. New codes W180, W181; new subcodes W131.excess_fingering, W144.position_*, W174.fingering_orphan |
neumaRk_fingering.md |
Content beside a short system in the Format row F) (2026-10-08): written on the side of the alignment mark where it should appear (F) \|* On cue, D.S. al @, F) Dal $ al @ *\|, F) Solo: \|*\| [B]x2); each side is an unquoted FORM) body (prose, section boxes, $ @ &fermata, ;, footnotes, links), descriptive only. The alignment mark now counts only as a whole token. New subcode W182.no_room |
neumaRk_datapack.md §9.1 |
Notable normative changes in 0.6¶
Beyond the new features, 0.6 introduces a few changes to syntax or codes relative to earlier drafts:
;breaks the line in every text container (neumaRk_text_markup.md§3bis). It used to apply only to note annotations andFOOT)entries; it now applies to all containers, in both forms"…"and[…]:M)andD)annotations, chord comment-labels, labels ofA)and ofPLAY)/FORM)boxes, end-decorators. Excluded, with a literal;: the section nameM) [NAME], the version label and theINFO)label. All these containers draw;on multiple lines: the line closest to the staff stays where the single line would be and the stack grows away from the staff (upward above the staff, downward inD)and below aPLAY)/FORM)box); box labels also wrap to the box width, line by line. In the prose ofTEXT)and of theINFO)body;is literal: theINFO)body breaks lines only on the source newline, no longer on;. InPLAY)/FORM), where a source newline counts as a space,;breaks the line in prose too (neumaRk_play_and_form.md§6.3).\;writes a literal;in any text (§4). No migration: no known document used;in these containers.\"no longer closes a"…"container (neumaRk_text_markup.md§4). The escape was already in the spec, but the note annotationc4"the \"King\""(also("…"), and["…"]with an odd number of quotes), the chord comment-label and the end decorator stopped at the first": the note vanished withE001, the chord row gaveE102.\"and\\now work. The header stays plain text, with"and\literal (neumaRk_header.md§5). In theM)row a section["NAME"]means[NAME](neumaRk_markers.md§3.1): before, it became a section whose name kept the quotes and an annotation with the same text.- The version description leaves the language, absorbed by
INFO). The"…"container written below%%NAMEis no longer a description: that text is now written as anINFO)at the head of the block, which feeds the selector and, through its label, the score (neumaRk_versions.md§2.4.3). No migration: the earlier form was never published. - A single end rule for free-text blocks (
PLAY),FORM),LYRICS),FOOT),TEXT),INFO)): a block ends at the first unescaped explicit line marker (neumaRk_datapack.md§13). The variantsC+/N+/N2(followed by a space or the end of the line) and theH…)header lines now end a block too; previously they were absorbed as text. An implicit marker row ([Intro]) does not end a block: after prose, writeM) [Intro]. New line-start escape:\N) …is block text and the\is removed when rendered; it replaces the "Limitation" ofneumaRk_text_line.md§4. The blank-line criterion is unchanged (it endsPLAY)/FORM)/FOOT), notTEXT)/LYRICS)). - Two forms reserved by the link
[text=>url](neumaRk_text_markup.md§3quater). InPLAY)/FORM)a[…]containing=>is a link, not a section box; a//inside a link (escaped too,\[…=>…]) is part of the address and does not open a comment (neumaRk_datapack.md§11). - Comments
//: quotes, header lines, position (neumaRk_datapack.md§11). A//between double quotes"…"is text and does not open a comment — just as a|between quotes is not a barline (§6.2, rule now written down) — except on sungL)rows and insideLYRICS)blocks, where"is a word character. The header lines of the free-text blocks (TEXT) …,INFO),PLAY),FORM),LYRICS),FOOT)) take a trailing comment like any other line: it used to end up in the block content. A comment is preserved where it is written when the document is rewritten: a comment line inside a block stays in the block (§13.1), andPLAY)/FORM)blocks keep their multi-line layout. LYRICS)block codes renumbered toW157/W158. An earlier draft had theLYRICS)block shareW152/W153with Collections; every code now has a single meaning (Collections:W152–W156;LYRICS)block:W157= non-existent section,W158= pickup overflow).E001andW153split by cause.E001covered nine different errors (notes, grace, chord-stacks, rhythm notation): it keeps the generic case, the uninterpretable note token, and the other eight have their own code,E014–E021.W153keeps the unknown override key initem:; a token without=valueis nowW179.- Flow signs can be authored on the markers line
M). Segno$, coda@and jumps (DC/D$/DCal@/FINE/al@) may also be written onM)(the recommended location), including a flow-only barline at the start of a section (M) |@); the effect is identical to authoring onN)/C). WhenM)and the notes/chords line carry divergent decorators on the same measure, the row written first wins (normallyM), which heads the datapack) and the new warningW159is emitted (neumaRk_markers.md§2.1,neumaRk_flow_and_repeats.md§2; until 2026-10M)always won). - Optional-section close:
[!]replaces[?](neumaRk_markers.md§4.2).[!]is an anonymous non-optional section-open marker; it closes the preceding optional scope as a consequence of the general rule §4.1. - Measure repeat
%with span N>1: the token appears in each of the N consecutive measures in the run (e.g.| a | b | %2 | %2 |), preserving the "one cell = one measure" invariant (neumaRk_flow_and_repeats.md§7). - Grace duration is optional with a default of
8. The first event of a grace block no longer requires an explicit duration: if omitted it takes8(eighth note); subsequent events still inherit the block's value.E009remains only for a non-conforming explicit duration (allowed4/8/16) (neumaRk_grace_notes.md§3.1). - An empty chord cell now makes the harmony persist (previously:
no harmony). A
C)measure with no symbols/%/NCre-realizes the previous chord on the downbeat (neumaRk_chords.md§1.1). To explicitly declare the absence of harmony, use the newNCtoken (§1.2,N.C.glyph). The implicit slash-notation trigger changes accordingly: it now includes persisting harmony and excludesNC(neumaRk_notes_and_durations.md§10).NCcovers the whole measure as the sole content of its cell, but it can also sit next to other symbols: there it behaves like a chord (§1.2.1) — it takes its slot or the explicit durationNC(4), printsN.C.at that position and persists until a new harmony appears. The last harmonic event of the measure wins:C-7 NC | |leaves the empty cells silent,NC C-7 | |makesC-7persist. - New
Rtoken (silent harmonic rest) (§1.3): twin ofNC— it stops the harmony (no chord, no slashes, stops persistence) but draws no glyph. For when you need no printed symbol even though a line break falls on a persisting harmony. As the sole content of its cell it covers the whole measure; next to other tokens it covers a fraction and is a synonym of the chord restr(§1.3.1, §4.3) — bareRdraws nothing,R(…)draws a visible rest. Lossless round-trip (neumaRk_chords.md§1.3). - Chord-level comment-label with line extension (
label-) (§8.5): a-glued to the closing delimiter ("break"-/[break]-) moves the label to the side of the chord symbol and draws a dashed line up to the next printed chord, anNCorRcell, or end of line/piece, ending in a hook. Idiom forbreak/drum fill/solo. Distinguished by adjacency from the sustain-(§4.1.2, isolated token): the glued form is purely annotative (not a slot). Lossless round-trip (neumaRk_chords.md§8.5). A)line aboveC): text annotations on chords (neumaRk_articulations.md§2.1): anA)written above aC)anchors text annotations"…"/[…]to the printed chord tokens, one per column (marks./>stay on the rhythm heads). Rendered above the chord symbol, or above the repeat/struck head!(4)when the token has no glyph. Per-token alignment ⇒ lossless round-trip; for the 2nd note of a rhythm use the explicit formA(4) !(4). A text token beyond the printed columns drops out of flow and emits W131.A)line aboveC): dashed continuation line (neumaRk_articulations.md§2.1): an annotation with a glued-("vamp"-) opens a dashed line above the chord band; every covered column wants its own standalone-(consumption, no W131), the line skips the continuation columns and stops at the next printed chord symbol /NC/R/ end of line, with an always-present downward hook. Above-the-band twin of the CASO 2 inline extension (neumaRk_chords.md §7).A)line aboveC): barline-anchored extension (neumaRk_articulations.md§2.1): the dashed chord line now also anchors to the measure borders — begin-bar-"vamp"(label at the left border, opens the line) and terminator-|(closes on the end-bar, hook, no text). Twin of notes §13.4–13.5. A"vamp"-last token stays inline on the symbol (the textual closing end-bar is deferred).A)line aboveC):{ }analysis staff over chords (neumaRk_articulations.md§2.1):{ }brackets draw a square staff (bar + downward legs + optional label) over the chord span, twin of the staff over notes (§10.3). Intermediate columns use.to hold their position.- Tenuto/portato in
A): from-/-!to_/_!(neumaRk_articulations.md§3). The-character is freed inA)for the upcoming dashed text-line role (uniform withD)); staccato!and staccato-accent>!are unchanged. Sub-threshold impact (corpus census: 0 production songs, 3 dev test songs): legacy files with tenuto-inA)must be migrated to_. - Analysis staff
A): source delimiters[ ]→{ }(2026-08-20,neumaRk_articulations.md§10.3). The analysis staff now opens/closes with{/}, freeing[…]onA)rows for the upcoming box role["text"]. The glyph (square bracket above the staff) and the semantics are unchanged; the diagnostics are renamedW144.bracket_*→W144.brace_*(brace_open_overlap,brace_close_unmatched,brace_degenerate,brace_unclosed_eol). A)text-line unified withD)(neumaRk_articulations.md§13). TheA)line gains the free text with a dashed line thatD)already had below the staff, with an identical vocabulary ("text"/["text"]note-anchored,"text"-opens the line,-continues/crosses barlines,-"text"/"text"-begin/end-bar anchoring).A)above andD)below, mirror renderings (hook always toward the staff). The box["text"]is re-enabled inA)(square brackets freed after the analysis-staff →{ }swap).- Terminator
-|inA)andD)(neumaRk_articulations.md§13.5,neumaRk_dynamics.md§6.6). New token: a-glued to the composite border as the last token of the measure closes the line on the end-bar, with a hook and no text — filling the previously missing "close on the barline" case (idiomatic of rhythm tacet ----|). It is the prefix of the whole border (-|,-:|,-||,-|.,-DCal@|…), uniform; the glued/spaced discriminator distinguishes it from the continuation- |that crosses the barline. - Note annotation: round frame
("…")(neumaRk_notes_and_durations.md§8). The annotation model becomes symmetric: the text is always"…"and the frame is an optional wrapper —"…"(none),["…"](box),("…")(round, new;("sentinel, previouslyE001). The legacy[text]form (box without quotes) is read-tolerated and normalized to["text"]. The inline$F/$Sdirectives, then recognized only in the plain"…"container, were removed on 2026-10-01 (entry "Fingering and strings on the$)row" below). - Line break
;in text containers (neumaRk_text_markup.md§3bis). Inside a container ("…"/["…"]/("…")) an unescaped;is a line break (multi-line content on a single source line);\;= literal;(added to the escape set). Sub-threshold behavior change: a pre-existing literal;in an annotation is now reinterpreted as a line break — rare, recoverable with\;. - A single frame model for text containers
(
neumaRk_text_markup.md§2.1). The text sits between quotes, the frame is optional:"text"none,["text"]box (reference form),[text]its shorthand (same result),("text")round frame. It used to apply only to note annotations; it now applies also to chord comment-labels (chord, group and row level),A)texts (on the note, at the barline, aboveC)),D)texts (on the note, at the barline) and textual end-decorators. Left out:M)section and annotation, wave and bracket labels,PLAY)/FORM)box labels. Sub-threshold behavior changes, with no impact on existing sources (none used these forms outside notes):["text"]no longer shows the quotes inside the box;("text")used to be a degenerate slur inA)(W144) and a discarded parenthesis inC)andD); inD)a parenthesis outside("…")(e.g.(mf),(text)) used to vanish silently and now emits W177.["xN"]:|is equivalent to[xN]:|: in bothNis the number of repeats (neumaRk_flow_and_repeats.md§6.1.1, which previously treated[xN]:|as purely informative text). - Slur
(without)= two-note slur (2026-09-30,neumaRk_articulations.md§10.1). A(not followed by a)before the next(of the same voice, or before the end, is legal and reaches the next element written in the source. Previously it emittedW144.slur_unclosed_eoland stretched to the end of the system; a second(with one open emittedW144.slur_open_overlapand was ignored together with its). Behavior change:( ( . ) )used to be one outer slur, it is now two slurs plus an orphan). Next element a rest →W144.slur_unclosed_rest(new);W144.slur_open_overlapnow only covers((on the same note. Analysis staff and octave are unchanged. - Fingering and strings on the
$)row (2026-10-01,neumaRk_fingering.md,neumaRk_notes_and_durations.md§8). The inline$F(fingering) and$S(string number) directives of the note annotation (c4"$F1",d"$F3$S2") are removed: fingers, strings, plucking fingers and fixed positions are written on the new$)row. The note annotation has a single slot and it is always text:"$F1"is now annotation text, with no diagnostics. Behavior change; migration:c4"$F1$S2"→$) 12aboveN) c4.
Rules clarified and corrections of October 2026¶
A full review of the specification against the parser (2026-10) decided, case by case, which of the two to correct. The resulting rules:
- Shorthands repeat the notehead too (
neumaRk_notes_and_durations.md§5, §11.2).!, a bare duration,'/,and a bare^take the notehead of the previous note along with its pitch (and=, §11.5), across a rest; a head written on the token wins, an explicit pitch starts from the normal head. Behavior change:c8(x) !gave a normal head on the second note, it now gives a cross (no song in the libraries used this form, checked 2026-10-08). - Duration context: the last written event (
neumaRk_notes_and_durations.md§3.3). A note without a duration inherits that of the last written event, note or rest; the rests added by autofill to complete a measure do not count. Behavior change:| c4 d | e f |gavee2 f2(it inherited the completion rest), it now givese4 f4;a2 r8 | d egaved4. e8^e4, it now gives two eighth notes. - Opening upbeat without durations (§7):
> g a b | c1at the start of the piece lost the upbeat notes; they are now a quarter note each, with the new W005. An upbeat with inherited durations that overflows the measure is a real excess (E005). The upbeat may start with a rest or a spacer (> r8 g a,> s8 g a). An anacrusis>in the middle of a row emits the new W184. !,'and,after a rest at the start of the piece (§1.1, §5): they repeat the rest (or the spacer) with the new W012, instead of an invented note. With a note before,!keeps repeating the last note.- Header field by field (
neumaRk_header.md§3.2). The "all-or-nothing" criterion is retired: an invalid field is discarded with a diagnostic and the rest of the header holds. New codes E303 (invalid header meter), W304 (malformed or out-of-range BPM or year, subcodesbpm_invalid/bpm_range/year_invalid/year_range), W305 (repeated field), W306 (unrecognized token on the metadata line), W307 (malformednrk:line); E301 now actually fires. Values such asHB) 1000orHB) 120.5are no longer truncated. The rule "the implicit style starts with a lowercase letter" is removed: the parser never applied it. nrk:is a rule of the exchanged file (neumaRk_header.md§2): mandatory in the.nrkfile and in every song block of a collection; a song that reaches the parser withoutnrk:is valid. In a collection, a song withoutnrk:is merged into the previous one with the new W183.- Undeclared key and meter mean C and 4/4 in the parser too, and the
rewritten document writes
HK)andHM)only if they were declared (or if they differ from the default). - Allowed meters (
neumaRk_header.md§10.4): denominator 2, 4, 8 or 16, numerator from 1 up to two digits, or a sum[…]of parts from 2 to 5. An invalid meter in the context object(…)gives E136 (E136.context_meter_invalid) and the previous meter stays. - Conflicts between rows of the same measure (
neumaRk_datapack.md§7.3): for meter, key and flow decorators the row written first wins, and the losing row gets W159 (context_conflict,context_key_conflict,flow_decorator_conflict). For flow decoratorsM)used to always win; for meter and key the last row won, silently. - Diagnostics promised and now emitted: W133 (text container not closed
within the measure, on
N)/A)/D)/M)), W004 on a grace block glued to a rest or a spacer (W004.rest_main), W008 when another stave does not carry the sameRN(W008.other_stave; the degradation is one measure rest, not N), E126 on the second label of a group (E126.label_dup_group), W155 forname:/desc:after the first song or betweenitem:and its block, W182 for anF)row that is not last, duplicated or with extra content (since 2026-10-08 alsono_room, see the Format row below). W132 stays reserved, like W130. - Grace: a block glued on the left (
c4[d8/^]e4) is a grace on the note that follows (before, two notes were lost with E001); an invalid grace event is discarded with E004 (grace_pitch_invalid,grace_force_accidental) or E009 (neumaRk_grace_notes.md§2.1). - Withdrawn from the specification: the implicit Format row (
|*|withoutF)); E123 for rows interposed betweenN)andN2(E123 stays only without a parent stave); the requirement of equal measure counts across staves and voices (the shorter one is completed with rests); the lone.as a final barline (.is never a barline). - Constructs allowed and clarified:
>( … )with detached parentheses is the same as the glued form; the leading>ofD)under an upbeat is the placeholder, not a decrescendo, and anA)/D)/$)row without the placeholder starts from the first upbeat note;\]is a literal]in the abbreviated box[…], in anM)section name[…]and in the NAME ofPLAY)/FORM)boxes;["a|b"]on the chord row is a label; a label at the head of a measure with no chord after it stays in its measure, on the empty cell (onC+too), and two labels at the head of the same measure give E126; the octave nudge is written before@(c'@4_4); the chord tie is written inside the parentheses (C(2^)); a row of%only is a chord row (the margin with a page break is-%); a leading^on a note after a rest is an entry tie;%also works on voice 2;_inside anL)/LYRICS)syllable is an elision (neumaRk_lyrics.md§5); in a collection block, blank lines afternrk:and//comments in the header do not change the song title (neumaRk_collections.md§5). - The first barline (
neumaRk_datapack.md§6, §7.3). It is optional when only the row identifier and context objects come before it: the group before the first barline counts as glued to the entry barline, on every measure row, and does not create a measure. A label, a note, a chord symbol or a text do create the measure (C) [Intro] | Eb7 F |has two measures). Behavior change:C) (Bb) | Eb7 F |gave an empty measure with the optional chord Bb, it is now one measure in B-flat; onC)and on theM)/A)/D)/L)/$)rows a meter in that position gave E102 or was lost. The label-and-barline form ofneumaRk_chords.md§8.1 is one more measure, not a spelling equivalent to the form without barlines. - Key or optional chord on
C)(neumaRk_chords.md§5):(Bb)glued to the barline or before the first barline is the key; detached after the barline or inside the measure it is an optional chord. - Context in free position (
neumaRk_datapack.md§7.3): a detached meter at a measure boundary also holds onC)and on theM)/A)/D)/L)/$)rows, before a label as an entry, after it as an exit (it used to give E102 or be lost silently). On theM)/A)/D)/L)/$)rows a detached key or pitch reference is not context.(X)as a context key means no key signature, like(C). - Clef and pitch reference belong to the stave (
neumaRk_datapack.md§7.3): onM)/A)/D)/L)/$)they give E135 (E135.clef_on_secondary_row,E135.pitch_ref_on_secondary_row) and are discarded; the pitch reference gives the same E135 onC)andC+too. Behavior change: they used to change the clef, pitch and duration of theN)row silently (onC), with the chord row belowN), reading did not terminate). - Irregular groups and unknown durations (
neumaRk_notes_and_durations.md§3.2.1, §3.2.2, §3.3): a group with a different ratio closes the open one with W011; a group open at the barline whose unit does not fit in the space left is completed with rests of the same ratio and a smaller value (W002); unknown durations that cannot share the measure in writable values take a common binary value with the new subcode W005.uneven_split. These cases used to give internal errors (E90x) or inconsistent durations. - Non-integer BPM (
neumaRk_play_directive.md§2.3.1):(=120.5bpm),(=120,5bpm)and, after a Style,(=Swing, 92,5bpm)give W136 (W136.bpm_not_integer); they used to be ignored silently, and92,50bpmset 50 bpm. - New diagnostics for values ignored silently: W308 (unknown header
marker,
neumaRk_header.md§6.1: the line used to end up in the style), W185 (non-integertranspose=initem:,neumaRk_collections.md§6.1), E004 on a rest, spacer or non-pitch inside a chord-stack (E004.stack_rest,E004.stack_pitch_invalid,neumaRk_notes_and_durations.md§2.6: they used to become a duplicate of the first pitch), W186 (token outside theM)row vocabulary, e.g. a detached keyM) | (Bb) |or free textM) | foo |,neumaRk_markers.md§7: it used to disappear; the same W186 for the second section or annotation in the same measure,M) | [A] [B] |,M) | "a" "b" |,W186.section_extra,W186.annotation_extra, §3.2: the first counts). A[…]inside anM)annotation is text:M) | "a [b] c" |used to open the sectionbas well. - Trailing hyphen in syllables (
neumaRk_lyrics.md§5, §7):pa-says the word goes on with the next syllable, also across the barline and on a new line; the continuation used to be lost at the barline. The rewritten document keeps the hyphen. - Collection block headers (
neumaRk_collections.md§5):HC)carries the credits, and noH…)marker becomes the song's style. - Severity: an
E###error does not stop reading (§ Convention below). E007 (slash with less than one beat available) no longer leaves a zero-length rest. - Reading corrections, with no effect on the syntax: irregular groups with an
explicit ratio (
c8t5:3,c8t4:3,c4t2:3), eighth-note meters beyond15/8(16/8),R1read as a measure rest, final barline|., meters with a large numerator (66/2,99/4) completed with no internal errors or lost rests, measure rest in meters of two whole notes or more (4/2,8/4,16/8,32/16). - Corrections of the rewritten document (round-trip), with no effect on the
syntax: version blocks with the same label, editorial parentheses
( … )also inside>( … ), compound meter[3+3+2]/8(it used to become8/8), terminator-|., second flag on theTEXT)header (rewritten escaped), grace glued to a rest, entry tie after a rest or in the second ending, rests merged across the middle of the measure (also in chord rhythm), inline clef after a datapack of rests only,^mid-measure in the compact form, final barline.|not repeated in the next datapack, padding of header fields, distinct written rests that each fill a whole block of the meter (r1 r1in 4/2: no longer merged), trailing hyphen of syllables, row label of aC)with no chord symbols (C) [Intro] |,C) "solos": the row used to disappear). - Reference rendering (informative): a voice silent for the whole bar is
drawn as a centred whole rest, in every meter and per voice
(
neumaRk_notes_and_durations.md§3.3.1); a single note filling the bar is centred; the score uses a single ink, except links (neumaRk_specification.md§2). In the MusicXML and LilyPond exports the syllable elision_is an elision and the whole-bar rest a measure rest.
Diagnostic-code convention¶
E###— error: invalid construct. The parser repairs or discards it, as described by the document that covers it, and goes on; the measure involved may be marked invalid (LOG_ERROR).W###— warning: construct allowed but probably unintended, or an ignored value; parsing goes on (LOG_WARNING).
No diagnostic, error or warning, stops the reading of the document. The prefix
indicates the severity, not the category. The entire 0.6+ diagnostic family
(W130–W167: dynamics, articulations, play directive, PLAY) / FORM),
collapsible sections, versions, HV), collections, LYRICS) block) is emitted
as a warning and has been renamed from E13x/E14x/E15x to
W13x/W14x/W15x to align the prefix with the actual severity.
Families. The number after the letter indicates the area, in blocks:
| Range | Area |
|---|---|
E001–E022, W002–W013 |
Notes, durations, grace |
E101–E137, W103–W186 |
Chords, datapack, voices, measure context and all the 0.6+ families (dynamics, articulations, flow, markers, versions, collections, lyrics, footnotes, links, fingering) |
E300–E303, W301–W308 |
Flow and repeats (E300, E302), header (E301, E303, W301–W308) |
W400 |
Chord rhythm |
E901–E906, W999 |
Internal (see below) |
Missing numbers inside a range are not codes: they were never assigned, except the ones marked retired or reserved in the tables, which must not be reassigned.
The two tables below — base codes (pre-0.6 and fundamental) and
0.6+ codes — together form the complete, authoritative map of the
normative codes (code → meaning), aligned with the parser. Each code has a
single meaning. Some codes have subcodes (W131.excess_syllable,
E136.context_meter_invalid, …): they specify the cause, the meaning of the code
stays the one in the table.
Internal codes (dev-facing, not normative) are deliberately excluded from
these tables: E901–E906 — guards against impossible states (non-positive
duration, undeducible note values, navigation with an invalid line/measure id,
undetectable tuplet, non-representable note value) — and W999, a synthetic
log-truncation marker emitted when diagnostics exceed the buffer capacity. Also
outside the language vocabulary are E910/W910, which do not come from the
parser but from an application's renderer (a measure or a rendering step that
failed). Like W130, W132 and W161, they are not part of the language
vocabulary. W001 is retired (never emitted).
Base code allocation (pre-0.6 and fundamental)¶
| Code | Meaning | Family (doc) |
|---|---|---|
| E001 | Uninterpretable note token (no more specific code applies; e.g. a chord-stack glued to the next note <c e g>e4, %3) |
Notes & durations |
| E002 | A duration cannot follow . or ! |
Notes & durations |
| E003 | Invalid tuplet | Notes & durations |
| E004 | Cannot parse pitch · grace event that is not a pitch (E004.grace_pitch_invalid) or with the force-accidental ! (E004.grace_force_accidental): event discarded · rest or spacer (E004.stack_rest) or element that is not a pitch (E004.stack_pitch_invalid) inside a chord-stack <…>, grace ones included: element discarded |
Notes & durations · Grace notes |
| E005 | Note durations exceed the measure meter (also in the upbeat) | Notes & durations |
| E006 | More than one slash-rhythm / per measure |
Notes & durations |
| E007 | Slash / has no room: less than one beat available after beat alignment |
Notes & durations |
| E008 | Absolute octave @<n>_ requires an explicit duration |
Notes & durations |
| E009 | Explicit grace duration non-conforming (only 4/8/16 allowed), or disallowed elements after the pitch (?, t, ., "…", (…)); omitting it on the first event is legal (default 8) (E009.grace_duration_invalid) |
Grace notes |
| E010 | Modifier / or ^ allowed only on the block's last grace event |
Grace notes |
| E011 | Empty grace block [] |
Grace notes |
| E012 | Grace block with more than 4 events | Grace notes |
| E013 | Rest/spacer not allowed as a grace event | Grace notes |
| E014 | Rhythm-notation group {…} not closed |
Notes & durations |
| E015 | Grace block […] not closed |
Grace notes |
| E016 | Malformed chord-stack: < with no closing >, or <> |
Notes & durations |
| E017 | Chord-stack with no pitch (< >) |
Notes & durations |
| E018 | Prolongation/repeat/octave mark with no preceding note | Notes & durations |
| E019 | Grace event without pitch/duration | Grace notes |
| E020 | Grace chord-stack not closed (<…) |
Grace notes |
| E021 | Empty grace chord-stack <> |
Grace notes |
| E022 | Unpitched marker = misplaced: alone (unpitched_bare), on a rest or spacer (unpitched_rest), inside a chord-stack (unpitched_inside_stack), before a non-note token (unpitched_misplaced) |
Notes & durations (§11.5) |
| E101 | Compact rhythm list / polychord / chord expression unparsable (compact_rhythm_invalid, compact_rhythm_first_rest, polychord_unparsable, chord_unparsable) |
Chords |
| E102 | Unrecognized token in the chord line (also an unclosed container, a ^ outside the parentheses) |
Chords |
| E110 | Too many staves in datapack (max 4) | Datapack / multi-stave |
| ~~E111~~ | ~~Orphan D)/L)~~ — retired before implementation: the orphan row is tolerated (see W130). Code not reassigned |
— |
| E122 | N) without match: more continuations than staves in the previous datapack (use N+) |
Datapack (N+) |
| E123 | N2 without a parent stave: no N) or N+ before it in the datapack |
Voices |
| E124 | Duplicate N2 on the same stave |
Voices |
| E125 | Clef directive not allowed on an N2 row |
Voices |
| E126 | More than one comment-label on the same chord / group / row (label_dup_chord, label_dup_group, label_dup_row) |
Chords (alternate) |
| E127 | Max 2 alternative C+ rows per datapack |
Chords (alternate) |
| ~~E128~~ | ~~NC mixed with other content in the same measure~~ — retired: NC next to other symbols is the fractional form of the no-chord declaration (neumaRk_chords.md §1.2.1). Code not reassigned |
Chords |
| E129 | More than one base C) line (chord block) in the datapack |
Chords / datapack |
| E130 | Malformed voice/stave marker N2) / N+) (use N2 / N+) |
Voices |
| E131 | Two X crossing markers on the same column of the staff |
Voices (crossing) |
| E132 | X crossing marker without a pivot note |
Voices (crossing) |
| ~~E133~~ | ~~R (harmonic rest) mixed with other content in the same measure~~ — retired: R next to other tokens is the fractional form of the harmonic rest (neumaRk_chords.md §1.3.1). Code not reassigned |
Chords |
| E134 | Counted note after the reprise-pickup group >( … ) (must be trailing) (E134.reprise_note_after_group) |
Notes & durations |
| E135 | Clef on a chord row (C) \|(@F8vb), E135.clef_on_chord_row): on C) the @ declares the harmony in effect, and (@F)/(@G)/(@C) are chord symbols there · clef (E135.clef_on_secondary_row) or pitch reference (E135.pitch_ref_on_secondary_row) on M)/A)/D)/L)/$), pitch reference also on C)/C+: they belong to the stave and are written on N). Component discarded |
Measure context |
| E136 | Unrecognized measure-context (…) component (context_component_invalid), invalid meter (context_meter_invalid: the previous meter stays), or group away from a measure boundary (context_not_at_boundary) |
Measure context |
| E137 | INFO) between the musical rows of a datapack: it goes before the first row or after the last (E137.info_inside_datapack) |
Datapack / INFO) |
| E300 | Measure-repeat (% / RN) with no valid source (E300.mmr_no_source) |
Flow / MMR |
| E301 | Cannot parse the header key (HK) H): field discarded, the default applies |
Header / key |
| E302 | Repeat count x0 (contradictory: "execute 0 times" → the material is never performed); on :x0\|/:0x\|, [x0]:\| and PLAY/FORM box […]x0. x1/x100 stay W142 (E302.repeat_count_zero) |
Flow / repeats |
| E303 | Invalid header meter (HM) 5/3): field discarded, the starting meter stays |
Header |
| W002 | Tuplet not closed before measure end (auto-filled with tuplet-unit rests) | Notes & durations |
| W003 | Grace block not adjacent to the main note (ignored) | Grace notes |
| W004 | Grace block without an adjacent main note, or followed by a rest or a spacer (W004.rest_main): ignored |
Grace notes |
| W005 | Implicit duration with no duration to inherit: quarter notes in the opening upbeat; in a measure, unknown durations that cannot share the space in writable values (W005.uneven_split): the longest binary value that fits for all, at most a quarter note |
Notes & durations |
| W006 | Pitch outside the MIDI range [1..127] | Notes & durations |
| W007 | Invalid rhythm-notation token {…}: no duration (W007.rhythm_token_no_duration), invalid duration (W007.rhythm_duration_invalid) or invalid tuplet (W007.rhythm_tuplet_invalid) |
Notes & durations |
| W008 | Multi-measure rest RN over a span with content, or not shared by the other staves (W008.other_stave): degraded to one measure rest |
Notes & durations (MMR) |
| W009 | Modifier not allowed on spacer s (ignored) |
Notes & durations (spacer) |
| W010.* | Editorial group-parentheses span ( … ) malformed: paren_open_overlap (nesting), paren_close_unmatched (orphan )), paren_degenerate (empty ( )), paren_unclosed_eol (unclosed at end-of-line → implicit close) |
Notes & durations (§11.4) |
| W011 | Tuplet group closed before completing its cycle, by an explicit duration or by a group with a different ratio (completed with tuplet-unit rests; e.g. a8t b4., c16t5 c4t7) |
Notes and durations |
| W012 | ! after a rest with no preceding note to repeat: the rest or spacer is repeated · same case for '/, (W012.octave_mark) |
Notes & durations |
| W013 | Unpitched note =: accidental ignored (accidental), tie with a pitched note (tie_mixed), = on rhythm notation {…} (rhythm_redundant) |
Notes & durations (§11.5) |
| W103 | Unrecognized chord suffix | Chords |
| W301 | Could not deduce title or credits from the first header line | Header |
| W304 | Malformed or out-of-range header BPM or year (bpm_invalid, bpm_range, year_invalid, year_range): field ignored |
Header |
| W305 | Repeated header field: the first one holds | Header |
| W306 | Unrecognized token on the implicit metadata line: ignored | Header |
| W307 | Malformed nrk: line (not nrk:<major>.<minor>): ignored |
Header |
| W308 | Unknown header marker (HZ) foo): line ignored, stays header |
Header |
| W400 | Chord-rhythm note with non-positive duration (omitted) | Chords (rhythm) |
0.6+ code allocation (single source)¶
The $)/A)/D)/L) lines share W131 (excess tokens) by semantic
identity, not by collision.
| Code | Meaning | Family (doc) |
|---|---|---|
| W130 | (reserved — not emitted: an orphan D)/L) is tolerated, silently bound to stave 0) |
Dynamics |
| W131 | Tokens in excess of the events of the row below: syllables (excess_syllable), articulations (excess_articulation), dynamics (excess_dynamic), fingering (excess_fingering), LYRICS) syllables on the notes of a section (excess_section_lyric); on an A) above C), texts beyond the printed chord columns |
$)/A)/D)/L)/LYRICS) (shared) |
| W132 | (reserved — not emitted: a D) - not reducible to an extension/barline has no effect) |
Dynamics |
| W133 | Text container opened and not closed within the measure: the rest of the measure is read as text (N), A), D), M); on C) it is E102) |
Text markup |
| W134 | Empty play directive (=) |
Play directive |
| W135 | Reversed slot order (tempo before style) | Play directive |
| W136 | BPM out of range (10–999) or not an integer, with a decimal point or comma (W136.bpm_not_integer), also after a Style: the directive does not set the tempo |
Play directive |
| W137 | Invalid NRK figure in metric modulation | Play directive |
| W138 | Play directive in a line other than M) |
Play directive |
| W139 | A) token outside the vocabulary |
Articulations |
| W140 | More than one FORM) section |
PLAY/FORM |
| W141 | Invalid \|BARS duration (1–999) |
PLAY/FORM |
| W142 | Repeat count xN out of range (2–99), on PLAY)/FORM) boxes ([…]xN) and on the barline (:xN\|, W142.repeat_out_of_range) |
PLAY/FORM · Flow / repeats |
| W143 | Keyword &xxx not reserved |
PLAY/FORM |
| W144 | Malformed A) span: slur (slur_*), analysis bracket (brace_*), ottava (octave_*) — opening and closing on the same note, closing without opening, second opening while one is open, unclosed at end of row, slur arriving on a rest (slur_unclosed_rest) · malformed $) fixed position [ ]: position_degenerate, position_close_unmatched, position_open_overlap, position_unclosed_eol |
Articulations · Fingering |
| W145 | FORM) not at the end of the document |
PLAY/FORM |
| W146 | Redundant [!]: no optional section to close |
Markers |
| W147 | %%NAME block not closed: everything to the end of the file is treated as part of it |
Versions |
| W148 | Orphan %%end (no open block) |
Versions |
| W149 | (retired: a new %%NAME closes the open block implicitly — do not reassign) |
— |
| W150 | Malformed HV): missing NAME (hv_expected_name), list expected (hv_expected_list) or unclosed (hv_list_unclosed), trailing content after the list (hv_expected_default_or_eol), default= without a label (hv_default_no_label), or the old form HV) versions: […] (hv_legacy_form) |
Versions |
| W151 | Collapsible section not at start of line (§4.7): opening (W151.optional_open) or bounds (W151.optional_bounds) · default used as a version NAME or label |
Markers · Versions |
| W152 | item: line in an nrk-book: collection |
Collections |
| W153 | Unknown override key in item: |
Collections |
| W154 | item: line not followed by a song block |
Collections |
| W155 | Collection header directive after the first block, or between item: and its block: ignored |
Collections |
| W156 | Collection with no song blocks | Collections |
| W157 | [NAME] in the body of LYRICS) references a non-existent section (reference-broken) (W157.section_not_found) |
Lyrics (LYRICS) block) |
| W158 | LYRICS) pickup group <…> with no notes to bind to: empty preceding run (pickup_no_prenotes) or shorter than the group (pickup_overflow) |
Lyrics (LYRICS) block) |
| W159 | Meter (context_conflict), key (context_key_conflict) or flow decorators (flow_decorator_conflict) differing between rows of the same measure: the row written first wins |
Measure context / flow |
| W160 | More than one reprise-pickup group >( … ) in the same measure (W160.reprise_multiple_groups) |
Notes & durations |
| W161 | (reserved/deferred — reprise pickup, validation in parse_text) |
Notes & durations |
| W162 | FOOT) entry not starting with a [^…] label (ignored) (W162.footnote_entry_no_label) |
Footnotes |
| W163 | Duplicate explicit label in FOOT) (W163.footnote_label_duplicate) |
Footnotes |
| W164 | Reference [^…] with no matching FOOT) definition (W164.footnote_ref_no_def) |
Footnotes |
| W165 | FOOT) definition never referenced (W165.footnote_def_unreferenced) |
Footnotes |
| W166 | Mix of explicit and auto [^] labels in the same document (W166.footnote_label_mix) |
Footnotes |
| W167 | FINE on a barline and note-anchored FINE conflicting in the same measure (W167.fine_barline_note_conflict) |
Flow / MMR |
| W168 | N+ redeclared: a stave of the previous datapack is left without an N) continuation |
Datapack / staves |
| W169 | Context announced twice at the same point (end of a measure and start of the next) (W169.context_double_announce) |
Measure context |
| W170 | Second INFO) on the same anchor: the first one wins (W170.info_duplicate) |
Datapack / INFO) |
| W171 | Empty INFO) (no label, no body): ignored (W171.info_empty) |
Datapack / INFO) |
| W172 | INFO) label "…" not closed on the same line: the line is read as body (W172.info_label_unclosed) |
Datapack / INFO) |
| W173 | (retired before implementation — do not reassign) | — |
| W174 | A) row with no N) (nor C)/C+) directly below: ignored · $) row with no N) below (W174.fingering_orphan): ignored |
Datapack / articulations · fingering |
| W175 | Link whose address is not http/https or is malformed: the text stays, not activatable (W175.link_bad_url) |
Text markup (links) |
| W176 | Empty link [text=>] / [=>]: the text stays, if any (W176.link_empty) |
Text markup (links) |
| W177 | Parenthesis in D) outside a ("…") frame: ignored |
Dynamics / text markup |
| W178 | The blocks of a version end on different harmonies and the measure after %%end has an empty cell: it inherits the canonical block's |
Versions / harmony |
| W179 | Token without =value in item: |
Collections |
| W180 | Malformed $) token: the whole token is ignored |
Fingering |
| W181 | $) token that cannot apply: on a rest (fingering_rest), on a prolongation . (fingering_prolongation), <…> group on a single note (fingering_not_chord) or with more entries than pitches (fingering_chord_excess) |
Fingering |
| W182 | F) row not last (not_last: moved to the end), duplicated (duplicate: the last one holds), without an alignment mark written as a whole token or with a second one (extra_content: the row, or everything from the second mark on, is ignored), or with side content where the system leaves no room (no_room: content on the system's side of the mark, or with \|**\|; ignored) |
Datapack / Format |
| W183 | In a collection, song block without its nrk: line: merged into the previous song |
Collections |
| W184 | Upbeat mark > in the middle of a row: rendered as an empty measure followed by the upbeat |
Notes & durations |
| W185 | item: transpose= that is not a signed integer (transpose=abc): override ignored |
Collections |
| W186 | Token outside the M) row vocabulary (section, annotation, play directive, context at a measure boundary): discarded; W186.key_free_position for a detached key (M) \| (Bb) \|); W186.section_extra / W186.annotation_extra for the second section or annotation in the same measure (the first counts) |
Markers |
Historical note:
W144had also been drafted for "nestedPLAY)", a case structurally prevented by the parser (never emitted); the code therefore remains with the articulations. The collapsible-section-not-at-start-of-line case was moved fromW147(which collided with Versions) toW151(2026-05-26).
neumaRk 0.5.0 — First public release¶
Status¶
Public preview. The language is usable and coherent, but subject to short-term evolution.
Version meaning¶
This is the first public release of neumaRk. The number 0.5 reflects an already advanced conceptual maturity, while not guaranteeing definitive stability of the semantic interface.
Included features¶
- datapack-based structure
- header with conservative deduction
- notes with implicit and explicit durations
- chords with rhythmic semantics and implicit/explicit durations
- chords with a compact duration list
X(a,b,r16,c,...)(see §4.4) - repeat and flow management
- support for Compact / Human / Verbose formats
Normative clarifications¶
- persistent musical context
- contextual semantics of symbols
- priority of a single note in the measure
- deducible Format line (withdrawn in 0.6: the Format row is always declared with
F))
Not yet stabilized¶
Historical list as of 0.5.0; many of these items were introduced in 0.6 (see What's new in 0.6).
- articulations, dynamics, lyrics
- polychords (syntax
[top|bottom], seeneumaRk_chords.md§6) - multi-stave per datapack (up to 4 staves, see
neumaRk_datapack.md§4) - measure repeat (syntax
%/%!/%2/%!2/%4/%!4, seeneumaRk_flow_and_repeats.md§7) - possible syntactic extensions
- refinement of the deduction rules
- complete formal validation
Compatibility¶
There are no previous public versions.