Lyrics (L) line)¶
STATUS: SPEC. §1–§7 cover the deduction semantics of the inline
L)line; §8 covers the section lyrics blockLYRICS). Binding and order ofL)are not redefined here: seeneumaRk_datapack.md §2.1and §3. Residual notes in §7.The Italian version (
../it/neumaRk_lyrics.md) is the source of truth; this English version mirrors it.
1. Definition¶
The L) (Lyrics) line carries the lyrics, syllable by syllable, aligned
to the events of the N) line it is bound to. It is a parallel layer: it does
not alter rhythm or flow. Multiple consecutive L) lines of the same staff
form distinct verses (multi-verse).
2. Structural position and binding¶
Marker, optionality and logical order are defined in neumaRk_datapack.md
§2.1 and §3 (implicit order: L) is optional and last of the note group).
The line binds to the N) of its own group/staff; voice 2 (N2) has its
own L) lines (see neumaRk_voices.md).
3. Count-based alignment (notes only, not rests)¶
The correspondence is positional by count, left-to-right, but computed only on note-events: rests do not receive syllables.
N) | c4 d r e |
L) | la la sol |
// ↑ ↑ ↑ ↑
// la la (rest: none) sol
- The syllable counter advances only on actual notes; on a rest an empty padding is emitted (preserving positional alignment).
- Token
.= placeholder: no syllable on that note (the note stays without text, but the position is consumed). - Tokens in excess beyond the number of notes: emits W131 (like
D)).
4. Multiple verses (multi-verse)¶
Each consecutive L) line of the same staff is a successive verse:
N) | c d e f |
L) | first verse here | // verse 1
L) | sec-ond one now | // verse 2
- Limit: 10 verses per measure/staff; lines beyond the tenth are silently dropped.
- Padding between verses: for each verse, the notes without a token receive an empty syllable, so all notes have the same number of verses (vertical alignment between verses).
5. Syllable mechanics¶
Each text token is a word, optionally hyphenated with -:
| Form | Effect |
|---|---|
parola |
one syllable (whole word) on the current note |
pa-ro-la |
three syllables on three consecutive notes; the non-final syllables are marked word_continues (hyphenation dash in render) |
pa- |
trailing hyphen: the syllable is word_continues and the word goes on with the next syllable, also across the barline and in the next datapack (line break): L) \| au- gu- ri a- \| mi \| |
_ |
melisma: extends the previous syllable onto the current note (no new syllable) |
su_un, o_e |
elision: a _ inside a syllable joins two vowels (or two syllables) sung on the same note; the token counts as one syllable |
. |
placeholder: no syllable (see §3) |
-coda |
token with leading -: continuation of the previous word — back-marks the last emitted syllable as word_continues and adds coda as the next syllable (per-syllable form produced by the serializer) |
Robustness details (as per parser):
- the empty syllables generated by spurious hyphens (
-mar,mar--ti, isolated-) are discarded; - a syllable is
word_continuesif there exists a non-empty syllable after it within the same token, if the token ends with-(pa-, also as the last token of the measure or of the row), or if a continuation token-Xfollows.
Reference rendering (informative). The elision
_is not printed: a space takes its place, and under the space an elision arc (‿) joins the two vowels. The_alone (melisma) and one at the edges of the token are not elisions.Reference rendering of the melisma (informative). The rendering depends on the syllable the
_extends. If the syllable ends the word (or is a one-syllable word:ciao _ _), an extender line runs under the melisma notes. If the word continues (aword_continuessyllable:A- _ _ ve), there is no line: only hyphens up to the next syllable, repeated at regular intervals when the distance is long. This is engraving practice: the hyphen already says the word goes on. In both cases the melisma may cross the barline.
6. Examples¶
N) | c4 d e8 f g4 |
L) | Ave- _ ma- ri-a |
Ave (with hyphenation dash) → _ extends Ave onto the 2nd note →
ma/ri continue; a closes the word. Since Ave continues, the melisma is
rendered with hyphens up to ma, no line (§5).
7. Decisions and residual notes¶
Closed:
- Syllable excess ✓ — emits W131 like
D). - §5 mechanics normative ✓ — hyphenation, melisma
_, continuation-as described.
Residual notes (current behavior documented as normative pending revision, non-blocking):
- Cap of 10 verses: beyond the tenth, verses are dropped with no diagnostics.
- Rests: they always interrupt the syllable count — the rest receives no
text and the melisma
_does not "skip over" it. Normative. - Hyphenation form in the rewritten document: the parser accepts both
pa-ro-laand the per-syllable formpa- -ro- -la; the rewritten document uses one syllable per token, with the leading continuation-(pa -ro -la), which reads back identically. A trailing hyphen is rewritten as a continuation-Xon the next syllable only when that syllable comes right after, also in the next measure or datapack (a- | mi→a | -mi); with a_, a.or a rest in between, or on the last syllable of the piece, it stayspa-.
8. Section lyrics block (LYRICS) line)¶
8.1 Purpose and relationship to L)¶
The LYRICS) block is an alternative way to write the same verses that
L) writes inline. Instead of aligning syllables to the notes of a single
datapack, it carries the text per section and lets the engine
distribute the syllables onto the notes of that section automatically.
LYRICS) does not introduce a different lyrics model: it feeds the very
same per-note Lyric slots that L) feeds (§3, §5). The inline L) line
remains fully valid; the two compose (§8.5).
The equivalence with L) holds only for rendering, though: the block
carries information the inline cannot express — the text edition
(language + author, §8.8). For this reason the round-trip serializer
preserves the form the author chose: a LYRICS) block re-emits LYRICS)
(verbatim, language included), an L) line re-emits L). The serializer does
NOT automatically convert one into the other; the conversion, when needed, is
an explicit action of the writer. Verses that
round-trip as a block are not also re-emitted inline.
8.2 Syntax¶
LYRICS)
[A] sillabe del verso…
[B] sillabe del verso…
[A] (a second [A] line = a second verse on A)
- The block opens with a line whose first non-blank token is the marker
LYRICS)(optionally followed by an edition tag[language] [<author>], §8.8). - Each entry starts with a section reference
[NAME](§8.3). The text after the]on the same line, and/or the following non-blank lines that do not start with[, are the syllables of that entry. - The entry text may optionally begin with a pickup group
<…>— syllables that bind to the notes preceding the section (anacrusi). See §8.4.1. - Blank lines inside the block are allowed (visual separators): they do not close the block.
- The block extends until the first unescaped explicit line marker (the
single end rule of free-text blocks,
neumaRk_datapack.md§13). A[NAME]alone on a line remains an entry of the block (expanded form, below). A syllable line that starts with a marker is written with a leading\(\N) …); the\is not part of the syllables.
Both compact and expanded forms are accepted and equivalent:
LYRICS) LYRICS)
[A] this is a text ≡ [A]
this is a text
The syllable grammar is identical to L) (§5): pa-ro-la,
melisma _, placeholder ., leading-- continuation.
8.3 Section reference and structural position¶
[NAME] is the same reference-by-name used by PLAY) / FORM)
(neumaRk_play_and_form.md §3.1): matching is by text-equality on NAME
with markup stripped (neumaRk_text_markup.md §7.1). A LYRICS) block may
appear anywhere between datapacks; position in the file does not affect
which section an entry binds to (binding is by name, like %%versions).
A reference to a non-existent section is reference-broken: warning W157 (non-blocking, §8.9).
8.4 Cross-datapack section aggregation¶
This is the defining behaviour: the syllables of [A] are spread over
all the notes of section [A], concatenated in flow order, crossing
datapack / system / measure boundaries. (Inline L) is by contrast local
to one datapack.)
A measure belongs to the section whose marker is the most recent
[…] section marker at or before that measure (annotations "…" do not
open a section, neumaRk_markers.md §3.1, §4.1). A maximal run of
consecutive measures with the same current section is an occurrence of
that section.
M) [A]
N) a b c d
N) e f g a // same [A], next datapack, no marker
LYRICS)
[A] mol-te sil-la-be su_un ri-go
// └─ distributed over a b c d e f g a (8 notes, one occurrence, cross-datapack) ─┘
Within an occurrence, the notes (first staff, voice 1 — §8.7) are collected
in flow order and the syllables are bound exactly as L) (§3): positional
by count, rests receive no syllable, . placeholder, _ melisma.
Multiple occurrences (template). If [A] occurs more than once
(e.g. AABA), the entry is a template applied to each occurrence: the
same syllables are bound, independently, to every occurrence. The syllables
are not spread across occurrences. (Per-occurrence different text — e.g.
verse 1 / verse 2 across repeats — is out of scope; use distinct names
[A1]/[A2], §8.10.)
8.4.1 Pickup syllables (anacrusi) — <…>¶
Covers the case where lyrics need to start before a section boundary (anacrusi / pickup).
An entry may begin with a pickup group delimited by <…>. The
syllables inside the group bind to the last N notes that precede the
section's occurrence in flow order, where N is the syllable count of
the group (after grammar expansion: pa-ro-la counts 3, _ and . count
1 each). Syllables after > continue normally on the first note of the
section's occurrence (§8.4).
M) [intro]
N) | a4 a a a | b2 r4 c8 d |
M) [A]
N) | e1 |
LYRICS)
[A] <do re> mi
// │ └─ first note of [A] occurrence
// └──────── last 2 notes of the previous section ([intro]: c8 d)
Where the pickup lands. For each occurrence of [NAME], the pickup
scans backward from the first note of that occurrence over the
contiguous run of notes whose current section is not [NAME]. The N
last notes of that run receive the pickup syllables (rests are skipped,
exactly as in §3 / §8.6). The scan stops at:
- the first note belonging to
[NAME](i.e. it never crosses into the occurrence itself or into an earlier occurrence of[NAME]); - the start of the piece (no notes before
[NAME]'s first occurrence).
Voltas. When the preceding run contains voltas
(|[1.] … |[2.] …, neumaRk_flow_and_repeats.md §6), only the
last volta contributes notes to the pickup; measures inside earlier
voltas are skipped by the backward scan. The intuition is that the
anacrusi is the run-up into [NAME] along the path the music actually
takes the last time, i.e. the path that ends in [NAME]. Non-volta
measures contribute normally.
Multiple occurrences (template). As with the post-> syllables, the
pickup group is a template: it is applied independently to each
occurrence of [NAME], each looking back into its own preceding run of
non-[NAME] notes. In AABA, every [A] gets its own pickup from the
notes just before it.
Overflow / no preceding notes.
- If the preceding run has zero notes (e.g. the occurrence is at the
very start of the piece, or the anacrusis falls inside the section
because the marker is not on the downbeat), the pickup has no notes to
bind to: its syllables are dropped and W158 is emitted once per
affected occurrence (subcode
W158.pickup_no_prenotes, non-blocking, §8.9). The post->syllables are bound normally. - If
0 < available < N(partial overflow: a run exists but is shorter than the pickup), the available notes receive the trailing pickup syllables, the leading excess is dropped, and W158 is emitted once per affected occurrence (non-blocking, §8.9). The post->syllables are bound normally regardless.
Verse accumulation (same slot, OVERRIDE). A pickup occupies the same verse slot as its own body on the pre-notes, replacing any syllables already present on that slot:
- The body occupies the section's natural slot,
verseCount[NAME]— the first free row of the section, like an entry without a pickup (§8.5). - The pickup occupies the same slot as its own body on the
pre-notes:
pickupSlot = bodySlot. If the pre-notes already carry syllables on that slot (e.g. the preceding section's body singing those notes), the pickup overwrites them. Otherwise the pickup writes directly (with implicit padding).
The musical effect matches traditional notation convention: the verse
of [NAME] literally "steals" the last N notes of its predecessor from
the predecessor's same-position verse (same language / same slot), and
sings the anacrusis there.
Multilingual example (Portuguese = slot 0, English = slot 1): the last
2 notes of [B] carry two pickup syllables — <E vol> of [A2]
Portuguese replaces the last 2 syllables of [B] Portuguese body
(slot 0); <So I> of [A2] English replaces the last 2 of [B]
English body (slot 1). The body of [A2] (both languages) stays on its
natural slots (0 and 1), in continuity with [A1] and [B]. To
achieve this replacement, the engine processes all bodies first,
then all pickups (so each pickup sees and overwrites the body
already written on the pre-notes).
Constraints.
- Only one pickup group per entry, and it must come immediately
after
[NAME](and optional whitespace), before any post->syllable. - Empty
<>is a silent no-op: it is consumed without effect, no warning. The post->syllables bind as if the group were absent. <and>are reserved insideLYRICS)entries only; they are not syllable-grammar tokens elsewhere (L),D)).- Inside
<…>the syllable grammar is identical toL)(§5):pa-ro-la,_,., leading--continuation. A leading--continuation as the first token of the pickup group is dropped (there is no prior syllable in the same entry to continue). - Staff/voice binding is the same as the entry body: first staff, voice 1 (§8.7).
8.5 Verse accumulation (L) inline ++ LYRICS) blocks)¶
For each section, the verses stack top-to-bottom in this order:
verses(section) = [ inline L) verses, in order ] ++ [ [NAME] entries from LYRICS) blocks, in file order ]
Each [NAME] entry (one line) adds one verse. Two [A] entries (same
block or different blocks) add two verses, in order of appearance.
To keep verses vertically aligned across a section that spans several
datapacks (whose inline verse counts may differ), the engine first pads
every note of the section to n_inline = max(note.lyrics.size()) over the
whole section, then appends each block verse at a uniform index across all
occurrences.
The three fragments below produce the same two-verse result on [A]:
// (a) all inline // (b) mixed // (c) all block
N) ... N) ... N) ...
L) tes-to u-no L) tes-to u-no LYRICS)
L) tes-to du-e LYRICS) [A] tes-to u-no
[A] tes-to du-e [A] tes-to du-e
8.6 Alignment rules (reused from L))¶
Identical to §3 and §5: positional-by-count on notes, rests receive no
syllable, pa-ro-la hyphenation (word_continues), _ melisma,
. placeholder, leading-- continuation. Syllables in excess of the notes
of an occurrence emit W131 (as L) / D)); notes in excess of the
syllables receive an empty Lyric{} (padding).
8.7 Staff / voice binding¶
A LYRICS) entry always binds to the first staff, voice 1 (the main
vocal line). Lyrics for other staves or for voice 2
(N2) use the inline L) line, which already has that granularity
(neumaRk_voices.md). The block trades per-voice granularity for
per-section convenience.
8.8 Text editions (LYRICS) [language] [<author>])¶
"Language" is a special case of a text edition: multilingual lyrics generalize to editions.
An edition is a version of the sung text; a song may have more than one.
The LYRICS) header may carry two optional elements, on distinct axes that
are also syntactically separate:
- a language token (
LYRICS) pt-br): a code (it,en,pt-br, …; form[a-z]{2,3}(-region)?, case-insensitive, lowercased) — a translation; - an author in
<…>(LYRICS) en <Frank Sinatra>): who wrote that text. Free text, may contain spaces (the brackets delimit it: no disambiguation heuristics).
| Form | Nature |
|---|---|
bare LYRICS) / inline L) |
neutral (default, no language/author) |
LYRICS) pt-br |
language |
LYRICS) en <Frank Sinatra> |
language + author |
LYRICS) <Al Jarreau> |
author-only (no language) |
An edition is identified by the pair (language, author). Multiple
editions in the same language with different authors are allowed (vocalese /
competing translations: en <Jon Hendricks> + en <Eddie Jefferson>).
Default — Rule A¶
- neutral (no language, no author) → default text;
- language (has language, ± author) → intrinsic, contributes to the default;
- author-only (author without language) → opt-in, never the default.
The default text is the neutral one (inline L) or bare LYRICS)) if
present, otherwise the first language edition in file order. If no
intrinsic edition exists (only <author>-only) the song opens
instrumental: no default text, editions are activated from the selector.
Selection¶
The active edition is a scalar preference of the reader (one per
song, key = the language+author pair) — same pattern as
%%versions but scalar. It selects which edition feeds the per-note
Lyric slots; entries of the other editions are not bound. If the active
pair does not exist among the editions, the default is used (Rule A); if
there is no default (instrumental), no block lyric is bound.
The inline L) line is the neutral default edition: it accumulates
per §8.5 when the neutral edition is the shown text.
Metadata (title / author)¶
- Title per language: one title per language (shared by all editions of
that language — it is the translation of the song's name, it does not change
with the lyricist), declared with the
[language]tag onHT)titles (neumaRk_header.md§7.1). It lives in the metadata, not in the block text. - Text author: for the default → global
lyricsBy(HCL)); for a tagged edition → the<…>group on theLYRICS)block. For an author-only edition the name IS that edition's lyricsBy.
The edition axis is orthogonal to the %%versions axis: two independent
reading choices that compose. The version choice rewrites the flow
first; the active edition's lyrics then bind by-name to the
remaining notes. Available languages are deduced from the LYRICS)
blocks + the [language] tags on titles; there is no declaration header
(HL) stays out of scope, like the optional HV)).
8.8.1 Title ↔ language bridge¶
A title carrying a language tag (neumaRk_header.md §7.1, e.g.
HT) No More Blues [EN]) is bound to that code. Opening the song as that
title (e.g. from a search result) preselects the language for the session: the
shown title and the lyrics language follow that choice. This is a session
override (like a URL parameter): it does not overwrite the kept
preference, which the reader can always change.
8.9 Diagnostics¶
| Code | Severity | Description |
|---|---|---|
| W157 | WARNING | a [NAME] in the body of LYRICS) references a non-existent section (reference-broken). The [language] on the LYRICS) line is the edition tag (§8.8), not a reference |
| W158 | WARNING | LYRICS) pickup group <…> with no notes to bind to: empty preceding run (W158.pickup_no_prenotes, pickup syllables dropped) or shorter than N (W158.pickup_overflow, leading excess dropped) — one per affected occurrence (§8.4.1) |
| W131 | WARNING | syllables in excess of the notes of an occurrence (shared with L) / D)) |
Both non-blocking. (Cap of 10 verses per note, §4, applies to the combined inline+block total.)
8.10 Out of scope (MVP)¶
- Per-occurrence text (AABA singing different words each pass of the
same
[A]): not covered — verses stack vertically, they are not per-occurrence. Workaround: distinct section names[A1]/[A2]. - Voltas
|[1.]/|[2.]with different text per ending. - Multilingual binding to alternative titles (§8.8).
8.11 Examples¶
Two verses on [A], one verse on [B], [Instrumental] left untexted:
M) [A]
N) | c4 d e f |
M) [Instrumental]
N) | g a b c |
M) [B]
N) | c b a g |
LYRICS)
[A] here's the first verse
[A] and the sec-ond
[B] the bridge's own words
Cross-datapack occurrence (one [A] over two datapacks):
M) [A]
N) c d e f
N) g a b c
LYRICS)
[A] eight syl-la-bles a-cross two rows
Pickup with anacrusis (§8.4.1):
M) [intro]
N) | a4 a a a | b2 r4 c8 d |
M) [A]
N) | e1 |
LYRICS)
[A] <do re> mi