Vai al contenuto

Datapack

1. Concetto di datapack

Un datapack è l’unità strutturale fondamentale del contenuto musicale in neumaRk.

Rappresenta un blocco verticale di informazione musicale sincronizzata nel tempo, tipicamente corrispondente a uno o più righi allineati:

  • markers
  • accordi
  • note
  • articolazioni
  • dinamiche
  • lyrics
  • formattazione

Ogni datapack descrive una sequenza temporale coerente (una o più misure consecutive). I datapack musicali sono separati tra loro da uno o più righi vuoti, che hanno valore di delimitazione strutturale. La presenza di una riga vuota implica la conlusione del datapack precedente.


2. Tipi di righe

Ogni riga del datapack ha un tipo semantico.

Il tipo può essere:

  • esplicito, tramite marcatore
  • implicito, dedotto dal contenuto e dalla posizione

2.1 Marcatori espliciti di riga

I marcatori espliciti sono:

  • una, due o tre lettere maiuscole
  • seguite da )
  • seguite da uno spazio
Marcatore Tipo
M) Markers
C) Chords
A) Articulations
N) Notes
D) Dynamics
L) Lyrics
F) Format

I marcatori sono opzionali.

Il marker N) ammette due varianti morfologiche di 2 caratteri, previste esplicitamente dalla specifica:

  • N+ — nuovo rigo introdotto in questo datapack (§4.5);
  • N2 — seconda voce sullo stesso rigo (vedi neumaRk_voices.md).

La cifra 2 e il segno + sostituiscono la ), preservando l'invariante di larghezza a 2 caratteri necessaria all'allineamento verticale.

Analogamente, il marker C) ammette una variante:

  • C+ — riga di accordi alternativi sopra la riga base (comment-label e scope di riga in neumaRk_chords.md §7).

Il segno + sostituisce la ) come in N+, preservando la stessa invariante di larghezza.


3. Ordine logico delle righe (deduzione implicita)

In assenza di marcatori espliciti, il tipo delle righe viene dedotto seguendo l’ordine logico:

  1. Markers (al massimo una riga, opzionale)
  2. Chords
  3. zero o più righe
  4. l’ultima è considerata la riga principale
  5. Gruppi di note, ciascuno composto da:
  6. una riga di Articulations (opzionale, prima)
  7. una riga di Notes (obbligatoria in assenza di chords)
  8. una riga di Dynamics (opzionale, dopo — vedi neumaRk_dynamics.md)
  9. una riga di Lyrics (opzionale, ultima)

Un datapack può contenere da 1 a 4 gruppi di note: ogni gruppo corrisponde a un rigo del sistema (vedi §4). 4. Format (al massimo una riga, opzionale, sempre finale)


3.bis Deduzione del tipo di riga (algoritmo normativo)

Il §3 dà l'ordine logico dei tipi; questa sezione ne formalizza l'algoritmo di deduzione: come, in assenza di marcatore esplicito, il tipo di una riga è determinato da contenuto + posizione. La regola qui scritta è normativa — è la lingua, non un dettaglio d'implementazione. Un marcatore esplicito (§2.1) vince sempre: l'algoritmo che segue si applica solo alle righe senza marcatore.

3.bis.1 Struttura del datapack

Solo Markers e Chords sono unici per datapack (sono condivisi dall'intero sistema, §4.1); tutto il resto è un gruppo di note ripetuto, una volta per (rigo × voce):

datapack ::= [Markers]?  Testa  Gruppo{1..8}  [Format]?

Testa    ::= ( [A]? Chord )*        // 0+ chord-row, ognuna eventualmente
                                    //   decorata da un'A) sopra
Gruppo   ::= [A]? N [D]? [L]?       // un (rigo × voce)
  • Testa (Markers + Chords): unica, condivisa. La riga di articolazioni può precedere una chord-row per decorarla (una chord-row può portare ritmo proprio, neumaRk_chords.md §4); per questo A) è ammessa prima di C).
  • Gruppo: si ripete fino a 8 volte = max 4 righi (§4) × 2 voci per rigo (§4.6). La voce 2 non è deducibile implicitamente — non esiste modo di distinguere dal solo contenuto "voce 2 dello stesso rigo" da "nuovo rigo": va sempre dichiarata col marcatore esplicito N2. Quindi la deduzione implicita da sola produce fino a 4 gruppi (i righi); gli 8 si raggiungono solo con N2.
  • Format: unica, sempre finale (§9).

3.bis.2 Modello di calcolo

La deduzione è un automa a passo singolo, per-datapack, che scorre le righe in ordine di sorgente mantenendo due elementi di stato:

  • lastType — il tipo dell'ultima riga (init Empty);
  • headClosedfalse finché non compare la prima riga Notes del datapack, poi true per sempre. Marca il confine fra Testa e Gruppi.

I guard lastType sono la funzione di transizione dell'automa: codificano sia l'ordine dentro un gruppo, sia il loop-back che apre il gruppo successivo (rientro su A o su N). headClosed impedisce a Chords di ricomparire una volta entrati nei righi.

3.bis.3 Pre-filtro: righe decorative

Prima della cascata, una riga puramente decorativa — composta solo da segni di battuta semplici, :, forme compatte di ritornello (neumaRk_flow_and_repeats.md §6.1), punti, spazi e tab — viene saltata e non riceve un tipo musicale (resta strutturale, le sue decorazioni di battuta appartengono semanticamente alle altre righe). Eccezione: il rescue >/^ in §3.bis.6.

3.bis.4 Cascata di classificazione (precedenza normativa)

Sulle righe non-decorative senza marcatore, si applica una cascata first-match-wins. L'ordine è vincolante: i predicati di contenuto sono volutamente permissivi e non sovra-matchano solo grazie a (a) questo ordine e (b) la guardia di posizione. Il trittico che definisce ogni tipo è predicato + guardia + esclusioni.

# Tipo Predicato (contenuto) Guardia (posizione) Esclusioni
1 Markers solo […] / barline / spazi è la prima riga del datapack
2 Chords ≥1 accordo valido (neumaRk_chords.md §3) + barline / . / % / comment-label / spazi — oppure soli % (TB1bis) testa aperta (!headClosed) non slash-only (TB2); non rest-only r/! (TB1)
3 Articulations charset del vocabolario A) (neumaRk_articulations.md) lastType ≠ Articulations non ^-only (TB3); non notes-line valida (TB4)
4 Dynamics charset dinamiche (< > c d f m p s z - . \| :) lastType == Notes
5 Lyrics word-char + . - _ ' \| : (il più permissivo) lastType ∈ {Notes, Dynamics, Lyrics}
6 Notes default / sink qualsiasi

Note sulle guardie:

  • Markers (riga 1): la deduzione implicita di Markers avviene solo sulla prima riga del datapack. Una riga markers-shaped più in basso non è Markers (un M) esplicito resta possibile ovunque la spec lo consenta).
  • Chords (riga 2): la condizione normativa è testa aperta. Equivale a dire che gli accordi vivono solo prima della prima riga di note: gli accordi sono unici e condivisi (§4.1), nessun datapack valido ha una chord-row dopo una notes-row. Il parser realizza il vincolo col flag posizionale headClosed (chiuso alla prima riga Notes del datapack) — allineato a §3.bis dal 2026-05-25 (FU.3a).
  • Lyrics (riga 5): il predicato è il più permissivo (matcha quasi ogni testo). È intenzionale: la permissività è contenuta dalla guardia di posizione, e in ogni caso il tipo di default è Notes. Non si restringe (sarebbe non-monotòno e rischierebbe di cambiare la classificazione di brani esistenti).
  • Default Notes: la riga 6 è il sink — ogni riga che arriva in fondo alla cascata è una riga di note. È anche il meccanismo del loop-back: due N) consecutive (entrambe sink) sono due righi distinti.

3.bis.5 Tiebreaker

Tre disambiguazioni risolvono casi in cui un predicato permissivo matcherebbe il tipo sbagliato:

  • TB1 — rest-only → Notes. Una riga composta solo da rest-token (r / !), barline, ., % e spazi appartiene allo stave (è una riga di note di soli silenzi), non a una chord-row vuota. Vale dopo Chords/Alt e dopo Articulations:
A7        ← Chords          | > .       ← Articulations
r         ← Notes (TB1)     r           ← Notes (TB1)

Realizzato dal 2026-05-25 come regola unica «rest-only forza Notes»: la riga è intercettata prima di Chords e Articulations, a prescindere da lastType (FU.3b dopo Articulations + FU.3c dopo Chords — entrambi davano il tipo sbagliato, rispettivamente chord-row vuota e Articolazioni). Una guardia richiede un rest-token r/! o % reale, così | > | (anacrusi) resta gestito dal rescue (§3.bis.6).

  • TB1bis — %-only senza chord-row → Chords. Una riga composta solo da % (più barline, ., spazi — nessun rest-token r/!) in testa aperta (!headClosed) e in assenza di una chord-row reale nel datapack è una chord-row di sole ripetizioni di misura: ogni % ripete l'ultima misura-accordo (anche dal datapack precedente — il lookback dei measure-repeat è cross-datapack). È l'eccezione a TB1: i % senza rest-token non vengono forzati a Notes, ma cadono nel ramo Chords (la cui isChordLine accetta già %).
| C7 | F7 |     ← Chords
| a b c | …     ← Notes (chiude la testa)

| % | % |       ← Chords (TB1bis): ripete C7, F7 dal datapack sopra
| d e f | …     ← Notes

Decisione 2026-05-26, supera il principio precedente «se vuoi una chord-row di soli % devi dichiararla con C)». Motivazione: «accordi che si ripetono mentre la melodia cambia» è il pattern di lead-sheet più comune; forzare C) su ogni continuazione era attrito gratuito. Due guardie tengono il caso stretto:

  1. niente rest-token — una riga con r/! resta sempre Notes (TB1): i silenzi sono inequivocabilmente note;
  2. !sawRealChordRow — se la testa ha già una chord-row con accordo reale, una riga %-only successiva resta Notes (è una notes-row di ripetizioni), perché la chord-row del datapack è già definita.

Escape-hatch per la lettura opposta (un secondo rigo di note di soli %): marcatore esplicito N+ / N). Limite noto: un datapack di soli % (senza alcuna notes-row) viene letto come chord-row e viola la grammatica (A?ND?L?)+ (serve ≥1 N); dichiararlo con N) se l'intento è una notes-row standalone di ripetizioni.

  • TB2 — slash-only → Notes. Una riga di soli / standalone (più barline, ., spazi) è slash rhythm (neumaRk_notes_and_durations.md §10), mai una chord-row: / da solo è sintatticamente quasi-accordo ma semanticamente un evento di nota.
| / / | / / |     ← Notes (TB2)
  • TB3 — ^-only → Notes. Una riga di soli ^ (più | e spazi) è una riga di note con sole legature di valore (neumaRk_notes_and_durations.md §6), non Articulations.
| ^ |     ← Notes (TB3)
  • TB4 — notes-line valida → Notes. Una riga che è una notes-line valida ha priorità sulle Articulations, anche se cade interamente nel charset del vocabolario A). Necessario perché alcune lettere del vocabolario sono pure note-name/durate: in particolare g (compone gl glissato, §9) è anche la nota G, e le durate 14 stanno nel charset → g, g4, g g g matchavano articulations_line ed erano rubati ad A).
A7        ← Chords
g         ← Notes (TB4), non Articulations

Predicato: ogni token (split su spazi; le barline |/: ai bordi sono spogliate) è un note-token valido secondo la grammatica canonica parse_notes (fonte unica → niente divergenza), e almeno un token porta un pitch reale [a-g]/r. I token di sola articolazione (>, tr, -, o, gl, ~…) non matchano parse_notes → la riga resta Articulations; una riga di soli ./,/^ (placeholder, nessun pitch reale) → resta Articulations. Realizzato 2026-05-26 (isNotesLineForTiebreak in map_functions.cpp), gate anche sul rescue >/^/~ (§3.bis.6). Principio: in caso di ambiguità A)↔N), vince la nota.

3.bis.6 Rescue > / ^

Una riga che il pre-filtro (§3.bis.3) avrebbe scartato come decorativa — perché > è leggibile come anacrusi attaccata a una barline — ma che contiene > oppure ^ ed è interamente charset-articolazioni, viene recuperata come Articulations (qui > è un accento, non un'anacrusi):

| > |     ← Articulations (rescue): accento sul primo evento della misura

Il rescue è un ramo fuori cascata e produce sempre Articulations (le articolazioni possono aprire un gruppo in qualsiasi posizione); è soggetto allo stesso vincolo lastType ≠ Articulations e all'esclusione ^-only (TB3).

Prima riga del datapack. Il rescue presuppone un contesto a monte. In prima posizione (line_id == 0) una riga | > | è letta come Markers/anacrusi, non come accento: il pre-filtro Markers (cascata riga 1) ha precedenza e > è ambiguo fra accento e levare quando non c'è una riga di note sotto a disambiguarlo. È una scelta consapevole (decisione 2026-05-25): in assenza di contesto la lettura anacrusi prevale.

3.bis.7 Post-pass: promozione AlternateChords

La classificazione di base assegna Chords a tutte le chord-row implicite. Una pass successiva rietichetta come AlternateChords (C+, accordi alternativi sopra la base) le chord-row consecutive che precedono l'ultima, solo se nessuna chord-row del datapack porta un marcatore esplicito (se l'utente ha scelto i marcatori, la sua scelta è rispettata). Limite: max 2 righe alternative per datapack (E127). Spec completa in docs/feature-alternate-chords.md e neumaRk_chords.md §7.

3.bis.8 Derivazioni e vincolo

  • Il charset di articulations_line (riga 3) deriva dal vocabolario chiuso di neumaRk_articulations.md (fonte unica): la deduzione e la semantica del renderer non devono divergere.
  • Invariante: l'algoritmo può solo essere esteso verso quanto qui scritto, mai cambiare in silenzio una mappatura riga→tipo esistente. Le divergenze parser↔spec sulla classificazione (FU.3a testa chiusa, FU.3b rest-only dopo A), FU.3c rest-only dopo Chords) sono state chiuse il 2026-05-25, verificate per differenza sulla batteria "classification golden" (wasm/tests/fixtures/classify_*). Resta aperto FU.1 (charset articolazioni dal vocabolario chiuso), che non cambia la mappatura riga→tipo.
  • Cambio di mappatura 2026-05-26 (TB1bis): la riga %-only senza rest-token in testa aperta e senza chord-row reale passa da Notes a Chords (§3.bis.5 TB1bis). È un cambio deliberato e documentato della mappatura, non silenzioso — ammesso perché il linguaggio non è ancora diffuso (nessun bump di versione). Coperto dai golden classify_pct_only_chords (promozione) e classify_pct_after_real_chords (guardia sawRealChordRow).
  • Correzione 2026-05-26 (TB4): la riga che è una notes-line valida ma cade nel charset A) (tipicamente le righe di sola nota g: g, g4, g g g) passa da Articulations a Notes (§3.bis.5 TB4). È un bug-fix verso il comportamento corretto («vince la nota»), non un'estensione controversa. Coperto dai golden classify_note_g_after_chords e classify_note_g_vs_artic (con negativo > . . . → resta Articulations).

4. Più righi per datapack (multi-stave)

Un datapack può rappresentare un sistema con più righi allineati (es. melodia + basso). Il numero di righi è dato dal numero di gruppi di note (A?ND?L?) presenti, fino a un massimo di 4.

Il numero di righi può variare da un datapack all’altro nello stesso brano: un datapack può contenere un solo rigo e quello successivo due o più, senza dichiarazioni preventive.

4.1 Elementi condivisi dal sistema

I seguenti elementi sono comuni a tutti i righi del datapack:

  • riga di Markers
  • riga(he) di Chords
  • segni di battuta e decoratori di misura (volte, cambio di metro/tonalità, $, @, DC, FINE, ecc.)
  • tonalità e metro del sistema

Le stanghette di misura attraversano verticalmente tutti i righi.

4.2 Elementi indipendenti per rigo

Ogni gruppo (A?ND?L?) definisce un rigo indipendente con propri:

  • Notes (obbligatorie)
  • Articulations, Dynamics, Lyrics (opzionali)
  • chiave dichiarata via direttiva inline (@…) come primo token della riga di Notes — vedi neumaRk_notes_and_durations.md §9. In assenza di direttiva, vale l’ultima chiave definita nel contesto (default: chiave di violino).
  • legature, travi e gruppi irregolari (tutti contenuti nel rigo)

4.3 Allineamento orizzontale

Tutti i righi del sistema devono contenere lo stesso numero di misure: l’allineamento verticale fra righi è temporale.

4.4 Esempio

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 |

Sistema a 2 righi: il primo in chiave di violino (default), il secondo in chiave di basso. L’accordo Gm6 è comune al sistema.


4.5 Continuità degli stave fra datapack (N+)

Quando un brano ha più datapack, ogni rigo (stave) ha una identità persistente: il "primo rigo" di un datapack è lo stesso "primo rigo" del precedente — eredita pitch context, chiave in vigore, durCtx, e viene reso nella stessa posizione verticale.

L'identità di base è posizionale: la prima N) del datapack corrisponde alla prima N) del datapack precedente, la seconda alla seconda, e così via.

Per aggiungere un nuovo rigo in un datapack senza romperne l'identità con i precedenti, si usa il marker N+ al posto di N).

N+   <contenuto del rigo nuovo>

Il + sostituisce la ) (non la aggiunge): il prefisso resta a 2 caratteri come N), così l'allineamento verticale delle misure resta identico fra righi adiacenti.

Regole

  • N) = continuazione del prossimo rigo del datapack precedente (FIFO, in source order). Ne eredita il context (pitch, chiave, durata).
  • N+ = nuovo rigo introdotto in questo datapack. Parte con context fresh (riferimento di orientamento dipende dalla chiave iniziale, vedi neumaRk_notes_and_durations.md §2.2).
  • L'ordine in source = ordine visuale top→bottom. Il marker N+ determina solo l'identità (nuovo vs. continuazione), non la posizione.
  • N+ nel primo datapack del brano è ammesso ma ridondante (in assenza di precedenti tutti i righi sono per forza nuovi); viene trattato come un normale N).
  • Limite invariato: max 4 righi totali per datapack (somma di N)
  • N+).

Errori

  • E122N) senza match: il datapack ha più N) di quanti righi avesse il precedente. Mancano N+ (o un N) di troppo).

Esempio

Datapack 1 (intro a 2 righi, 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): aggiunge un terzo rigo in alto (melodia di voce), mantenendo treble e bass dell'intro al centro e in basso.

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

Mapping risultante (per l'engine, non visibile in source):

source row tipo identità
1 (N+) nuovo nuovo stave (top)
2 (N)) cont. = primo rigo intro (treble)
3 (N)) cont. = secondo rigo intro (bass)

Il context (last_pitch, chiave, durCtx) dei righi 2 e 3 del Theme viene dai righi 1 e 2 dell'intro; il rigo 1 del Theme parte fresh.

Casi non coperti

La sintassi N)/N+ esprime la situazione comune "aggiungo un rigo in cima/in mezzo/in fondo", ma non copre:

  • drop selettivo (datapack che mantiene il 2° rigo del precedente ma droppa il 1°);
  • reorder (scambiare l'ordine visuale di righi esistenti, mantenendone l'identità).

Per questi casi una futura estensione potrebbe introdurre un riferimento esplicito all'ID interno del rigo (es. N<n>)). Non spec'd in questa versione.


4.6 Seconda voce per rigo (N2)

Ogni rigo può ospitare una seconda voce indipendente, introdotta dalla riga N2. Stave e voce sono concetti distinti: il rigo è il pentagramma, la voce è uno stream musicale all'interno del rigo.

La voce 2 condivide chiave, tonalità, metro, stanghette e riga degli accordi con la voce 1, ma mantiene contesto musicale persistente indipendente. Voce 1 ha gambi in alto, voce 2 in basso.

La specifica completa di sintassi, contesto, binding e diagnostica è in neumaRk_voices.md.


5. Regole di validità del datapack

Un datapack è valido se:

  • contiene almeno una riga di Notes o una riga di Chords
  • rispetta l’ordine logico delle righe
  • tutte le righe musicali sono temporalmente allineabili

Non è valido:

  • un datapack con sole righe di testo
  • un datapack con una riga di Format non finale

6. Segni di battuta e misure

Tutte le righe musicali (Markers, Chords, Articulations, Notes, Dynamics, Lyrics):

  • contengono segni di battuta — semplici e composti — ed eventuali decoratori di misura (in assenza di barline tutto il contenuto apparterrà alla prima misura del rigo)
  • definiscono implicitamente la suddivisione in misure

La riga di Format è l'unica eccezione: non ammette segni di battuta (né semplici né composti) e nessun decoratore di misura.

6.1 Segni di battuta supportati

I segni di battuta ammessi sono:

  • | battuta semplice
  • || doppia barra
  • |. o .| fine
  • |: inizio ripetizione
  • :| fine ripetizione

Il segno finale di battuta deve essere preceduto da uno spazio.


7. Decoratori di misura

I decoratori di misura sono elementi adiacenti a una barline e si dividono in:

  • BEGIN decorators (a destra della barline)
  • END decorators (a sinistra della barline)

7.1 BEGIN decorators

Posizionati immediatamente a destra della barline.

Possibili decoratori:

  • Cambio di metro e/o tonalità

  • fra parentesi tonde

  • separati da virgola
  • ordine arbitrario

Esempi:

|(3/4,Dm)
|([3+3+2]/8)
  • Volta endings

  • testo fra parentesi quadre

  • opzionale +n per la durata in misure

Esempio:

|[1.]+4

In assenza di +n, la volta si chiude automaticamente al primo :| incontrato entro 4 battute (caso tipico dei finali [1.]). Se nelle 4 battute successive non compare un :|, la volta indica un'uscita — usare +n per i casi non standard.

  • $ segno
  • @ coda

Se presenti, metro e tonalità devono precedere ogni altro decoratore BEGIN.


7.2 END decorators

Posizionati immediatamente a sinistra della barline.

Decoratori supportati:

  • DC
  • DCal@
  • DCalFINE
  • D$
  • D$al@
  • D$alFINE
  • FINE
  • al@
  • testo libero fra parentesi quadre (annotazione grafica)

I segni $ e @ sono BEGIN decorator (vedi §7.1): identificano il punto in cui il segno o la coda si trova, non un salto verso di essi.


8. Riga di Markers

La riga di Markers:

  • contiene marcatori racchiusi fra parentesi quadre
  • i marker si riferiscono all’inizio della battuta

Se un marker è preceduto da una barline:

  • deve esserci uno spazio fra barline e marker
  • per evitare ambiguità con i volta-decorators

È best practice collocare i decoratori di flusso (DC, D$, coda, ecc.) in questa riga.


9. Riga di Format

La riga di Format può essere dichiarata:

  • in forma esplicita, tramite il marcatore F);
  • in forma implicita, se il contenuto della riga è riconoscibile univocamente come riga di format.

La riga di Format, se presente, deve essere:

  • unica per datapack;
  • sempre l’ultima riga del datapack.

In caso di ambiguità con una riga musicale, prevale sempre l’interpretazione musicale e la riga non viene considerata Format.

Contiene indicazioni di allineamento:

Simbolo Allineamento
\|* LEFT
*\| RIGHT
\|*\| CENTER
\|**\| JUSTIFIED (default)

10. Margini fra datapack

Una riga:

  • che segue una riga vuota
  • che inizia con -
  • che contiene solo -, spazi, tab o %

indica un margine verticale fra datapack.

La profondità del margine è data dal massimo numero di - consecutivi:

  • - - - → margine 1
  • - -- - → margine 2
  • --- - → margine 3

Il simbolo % indica un possibile page break.

11. Commenti

Sono permessi commenti single-line nella forma // comment. Lo scope è la riga: tutto ciò che segue // fino al newline viene ignorato dal parser. I commenti possono stare su riga propria (sopra/sotto un datapack, o tra le righe di un datapack) oppure a fine riga di una riga di codice.

Il discriminante fra le due forme è posizionale: se prima di // c'è almeno un carattere non-whitespace, il commento è trailing (la parte di codice prima di // resta valida); altrimenti l'intera riga è commento.

I commenti multi-riga (/* … */) non sono supportati: NRK è un linguaggio column-sensitive (le | di battuta allineano verticalmente le righe del datapack) e i block comments romperebbero l'allineamento.

La sequenza %% ad inizio riga non è un commento: è riservata per i blocchi di versione del brano (vedi neumaRk_versions.md).


12. Blocchi di versione

Fra un datapack e l'altro possono comparire blocchi di versione, marcati %%NAME … %%end, che racchiudono uno o più datapack alternativi del brano. I blocchi vivono standalone fra datapack, non al loro interno. Vedi neumaRk_versions.md per la spec completa.