Collezioni (book / playlist)¶
Questo documento definisce il formato testuale con cui un insieme ordinato di brani — un book o una playlist — è rappresentato in un unico file neumaRk.
Una collezione non introduce un nuovo linguaggio: è un sottile strato contenitore attorno a una sequenza di brani, ciascuno dei quali è un documento neumaRk standalone già definito dal resto della specifica. Lo stesso parser legge brani singoli e collezioni; la distinzione avviene sulla prima riga del file.
1. Scopo e ruolo¶
Il formato collezione serve a esportare, condividere e re-importare
un book o una playlist come singolo file .nrk, autonomo e leggibile.
Il principio guida è la portabilità: il file è una foto autosufficiente della collezione. Tutti i brani sono incorporati come snapshot (copie congelate), così che il file possa essere aperto e importato da chiunque, anche senza accesso ai brani originali e anche se gli originali cambiano o spariscono.
Tutto ciò che è specifico del prodotto (e non del linguaggio musicale) — copertina, tag curati, condivisione, ruoli, identificatori di database — non fa parte del file (vedi §7).
2. Collezione e brano singolo¶
Un file neumaRk può rappresentare:
- un brano singolo, la cui prima riga è la dichiarazione di versione
nrk:<major>.<minor>(vedineumaRk_header.md§2); - una collezione, la cui prima riga è
nrk-book:<v>oppurenrk-playlist:<v>(§3).
La distinzione è data dal solo contenuto della prima riga, mai
dall'estensione del file: un .nrk può contenere l'uno o l'altra.
Un file che inizia con nrk: è un brano singolo come sempre: il formato
collezione è retro-compatibile e non altera la lettura dei file
esistenti.
Il termine collezione è un'ombrello concettuale usato in questa specifica e nel tooling; non compare mai come token nel file. Il tipo (book o playlist) è scritto direttamente nel marcatore di riga 1.
3. Riga di apertura e tipo¶
La prima riga di una collezione è una dichiarazione di versione che incorpora il tipo:
nrk-book:0.6
oppure
nrk-playlist:0.6
Caratteristiche:
- è obbligatoria e deve essere la prima riga del file;
- il tipo (
book/playlist) è parte del marcatore: leggendo la prima riga si conosce subito sia che il file è una collezione sia di quale tipo; - la versione segue la stessa numerazione
nrk:del linguaggio.
Differenza funzionale fra i due tipi:
- un book è una raccolta curata di brani, senza override per-brano;
- una playlist è una sequenza eseguibile in cui ogni brano può portare un override per-occorrenza (§6).
4. Header di collezione¶
Dopo la riga di apertura, ed eventuali righe vuote, può comparire un
header di collezione: un blocco di direttive nella forma
chiave: valore.
nrk-playlist:0.6
name: My Setlist
desc: live al Blue Note
| Direttiva | Significato | Obbligatoria |
|---|---|---|
name: |
nome della collezione | no (consigliata) |
desc: |
descrizione libera | no |
Le direttive di header:
- usano
:come separatore (stessa famiglia sintattica dinrk:); - precedono qualunque blocco-brano;
- sono parte di neumaRk (come il titolo
HT)lo è per il brano): identità testuale della collezione.
Una direttiva di header che compaia dopo il primo blocco-brano è fuori posizione (W155).
5. Blocchi-brano¶
Il corpo della collezione è una sequenza di blocchi-brano. Ogni blocco:
- è un documento neumaRk standalone, che inizia con la propria riga
nrk:<v>e prosegue con header e datapack come di consueto; - è una snapshot autosufficiente (il contenuto musicale è incorporato, non riferito);
- è copia-incollabile come file
.nrkvalido a sé.
L'ordine dei brani è l'ordine dei blocchi nel file: non esiste una chiave d'ordinamento numerica.
nrk-playlist:0.6
name: My Setlist
nrk:0.6
First Tune (Author)
F 120bpm
F| Bb| C7| F|
nrk:0.6
Second Tune (Author)
Bb 90bpm
Bb| Eb| F7| Bb|
Una collezione priva di blocchi-brano è degenere (W156).
6. Override per-item (solo playlist)¶
In una playlist, ciascun brano può essere preceduto da una riga
item: che ne dichiara gli override per quell'occorrenza nella
scaletta. Gli override non sono proprietà del brano (lo stesso
brano in due playlist può averne di diversi): sono uno strato della
voce di scaletta.
item: transpose=+2 notes="capo 3" form=[Intro] [A|8]x2 [B|8] [A|8]
nrk:0.6
My Tune (Author)
…
Regole:
- la riga
item:si applica al blocco-brano che la segue immediatamente (il prossimonrk:); - compare solo se c'è almeno un override; la sua assenza significa "nessun overlay";
- è ammessa solo nelle playlist. Una riga
item:in unnrk-book:è fuori contesto (W152).
6.1 Sintassi dei parametri¶
I parametri sono coppie chiave=valore separate da spazi. Il segno =
distingue il parametro inline dalla direttiva di header (:), così
nessun simbolo ha doppio ruolo.
Un valore:
- è un token semplice (senza spazi), es.
transpose=+2; oppure - è quotato
"…"quando contiene spazi (testo letterale, quote-aware come nel resto di neumaRk), es.notes="capo 3".
Chiavi ammesse: transpose, notes, form. Una chiave sconosciuta è
ignorata con diagnostica W153.
| Chiave | Tipo | Significato |
|---|---|---|
transpose |
intero con segno | trasposizione di esecuzione (overlay, ±semitoni) |
notes |
testo | annotazione personale per questa voce |
form |
grammatica FORM | forma di esecuzione per questa occorrenza (§8) |
6.2 form= consuma il resto della riga¶
Poiché la grammatica FORM usa essa stessa i container "…" per le
label (vedi §8), il parametro form= non è quotato: consuma
tutto ciò che segue fino a fine riga, verbatim. Per questo form=,
se presente, è l'ultimo parametro della riga item:.
item: transpose=-1 form="Tema"[A|8]ff [B|4]x2 "Coda"[A|8]&fermata
Le virgolette qui sono interne alla grammatica FORM (label), non delimitatori del parametro. (Una forma su più righe è fuori scope in questa versione.)
Un transpose nullo (+0, no-op) e un notes vuoto non sono errori.
7. Modalità portabilità: dentro e fuori dal file¶
La collezione porta solo musica e struttura minima. Tutto ciò che è prodotto applicativo viene ricostruito all'import dai default, come quando si crea una collezione nuova.
Nel file (è neumaRk):
- il tipo (
nrk-book:/nrk-playlist:); name:/desc:;- l'ordine dei brani (= ordine dei blocchi);
- per le playlist: gli override
transpose/notes/formsuitem:; - ogni brano per intero, come snapshot.
Fuori dal file (non è neumaRk):
- copertina, tag curati, conteggio iscritti;
- condivisione, ruoli, permessi;
- identificatori di database dei brani;
- la distinzione dynamic vs snapshot delle voci playlist (in export ogni voce è risolta a snapshot).
7.1 Il file è intenzionalmente anonimo¶
Per coerenza con il principio sopra, un file di collezione non porta alcuna identità d'origine: né identificatori di database, né chiavi di deduplicazione, né tag dell'host. È, di proposito, anonimo.
Conseguenza: l'identità di un brano (decidere se due brani sono "lo stesso") non è una proprietà del file — è una questione dell'host, e va derivata semmai dalla musica (vedi §7.2), mai da un'etichetta opaca incollata al file. Questa è una scelta deliberata: il formato non trasporta dati che esso stesso non comprende.
7.2 Semantica di import (comportamento dell'host)¶
Come un host re-importa una collezione non è definito da neumaRk — è comportamento applicativo. In leadsheet.app, dato che il file è anonimo (§7.1):
- l'import di un book crea copie dei brani nella libreria dell'utente; l'import non è idempotente — reimportare lo stesso book ricrea le copie;
- la deduplica ("ho già questo brano?") è rimandata e, quando arriverà, sarà basata sull'impronta melodica (proprietà della musica, condivisa con la ricerca) come advisory — non su una chiave esatta incollata al file. Una identità esatta e durevole, con un formato volutamente puro, non è ottenibile dal file; perciò la dedup nasce dalla musica, ed è per natura un suggerimento, non un automatismo.
7.3 Assorbimento dell'header all'import — modulo header-probe (lossless)¶
All'import, ogni brano viene separato in header (→ prop) e datapack
(→ cont pulito), così il brano importato è coerente con quelli salvati
da WRITE (header nelle property, corpo nel cont) e il round-trip
export→import è lossless.
La separazione usa il vero parser (parseText, text→Music) esposto da
un modulo WASM dedicato (createModuleHeader, sorgente
wasm/src/neumark/header_probe.cpp → parseHeaderOnly), servito sotto
assets/wasm/header/ e caricato lazy solo all'import. Il bundle WASM
dell'app library resta volutamente snello (non include il parser nel boot
normale); il costo del parser si paga una sola volta, alla prima importazione.
In view, che il parser lo bundla già, parseHeaderOnly è esposto sullo
stesso modulo (createModuleView) e usato dal salvataggio persistito
(collection-view.service), senza caricare un modulo separato.
Da parseHeaderOnly si ricavano:
prop: header completo nello shape DB (tit,crd{mus,lyr,arr,trs},key,bpm,mtr,sty,sub,yr,alt[],lng[]) — valori dalMusicparsato, presenza dai range dimap.header;cont: il datapack puro (righe dopo l'header, blank iniziali/finali rimossi).
Fallback: se il modulo non si carica, l'import ripiega sullo scan
leggero (parse_block_header in collections.cpp, sottoinsieme dei campi)
con l'header lasciato dentro cont, che WRITE normalizza al primo
ri-salvataggio. Lo scan leggero resta quindi come rete di sicurezza, non più
come comportamento primario.
Limite noto: la separazione richiede la riga vuota fra header e datapack
(che generate_nrk emette sempre in export); su file scritti a mano senza
quel separatore parseText non distingue header e corpo — stesso comportamento
dell'import in WRITE.
8. FORM a livello di collezione¶
Il parametro form di una voce playlist (§6) usa la grammatica
FORM) definita in neumaRk_play_and_form.md (box di sezione,
durate informative |N, label, dinamiche, keyword, prosa libera).
8.1 Semantica: sostituzione¶
Se una voce playlist porta un form, esso sostituisce la sezione
FORM) eventualmente contenuta nello snapshot del brano, per quella
occorrenza.
Serve a dichiarare: "la forma con cui suoniamo questo brano stasera è questa, a prescindere dalla forma originale del brano."
- override presente → l'item-
formvince sullaFORM)del brano; - override assente → resta la
FORM)del brano (se presente).
È una sostituzione, non una fusione.
8.2 Perché FORM e non PLAY¶
FORM) è posizione-indipendente (una sola, descrittiva, in fondo
al documento; il player la ignora): si presta a essere dichiarata a
livello di scaletta, dove esiste solo il riferimento a un brano.
PLAY) invece è posizionale/inline: il suo effetto dipende da
dove è collocata fra i datapack (vedi neumaRk_play_and_form.md
§2.1). A livello di scaletta non esiste una posizione interna al brano
in cui iniettarla, quindi PLAY) non è esprimibile come override di
voce. L'override di forma è perciò sempre descrittivo (FORM), mai
eseguito: non altera il playback, ma ciò che i musicisti leggono come
forma.
8.3 Risoluzione dei riferimenti¶
I box [NAME] dell'item-form si risolvono sui marker M) del
brano della voce (lo snapshot che segue), con lo stesso modello di
neumaRk_play_and_form.md §3.1. Un box che non corrisponde a nessun
marker è reference broken: renderizzato come testo non eseguito,
senza errore o warning (neumaRk_play_and_form.md §7.3). Questo
consente forme di scaletta informali.
Le diagnostiche di box (W141 durata, W142 repeat, W143 keyword)
restano applicabili; quelle di posizionamento del documento (W140 più
FORM), W145 FORM) non in fondo) non si applicano all'item-form.
9. Regole di parsing¶
9.1 Split prima del parsing musicale¶
Il file di collezione viene suddiviso in blocchi alle righe nrk:
prima di qualunque analisi musicale. Il parser di brano riceve
solo il testo da una riga nrk: alla successiva.
Conseguenza: i token di collezione (nrk-book: / nrk-playlist:,
name: / desc:, item:) vivono nei segmenti esterni ai blocchi e
non possono collidere con il contenuto musicale.
9.2 Riconoscimento¶
- prima riga
nrk-book:→ collezione book; - prima riga
nrk-playlist:→ collezione playlist; - prima riga
nrk:→ brano singolo.
L'entry-point del parser, che per i brani impone "nrk: deve essere la
prima riga", accetta nrk-book: / nrk-playlist: come token di
riga 1 alternativi. Sono prefissi letterali distinti: il
riconoscimento è additivo e non ambiguo.
9.3 Righe vuote e versioni¶
- Le righe vuote separano l'header di collezione dai blocchi e i blocchi
tra loro, coerentemente con la delimitazione dei datapack
(
neumaRk_datapack.md§1). - Il blocco
%%NAME … %%end(versioni del brano,neumaRk_versions.md) resta interno al singolo blocco-brano: è testo che il parser di brano vede, non un token di collezione. Lo strato collezione non usa%%.
10. Diagnostica¶
| Codice | Descrizione |
|---|---|
| W152 | Riga item: in una collezione nrk-book: (i book non hanno override per-item) |
| W153 | Chiave override sconosciuta in item: (ammessi transpose / notes / form) |
| W154 | Riga item: non seguita da un blocco-brano (nrk:) |
| W155 | Direttiva di header collezione (name: / desc:) dopo il primo blocco-brano |
| W156 | Collezione priva di blocchi-brano |
10.1 Non-errori¶
- un blocco snapshot la cui
FORM)è sostituita dall'item-form(§8.1); - un box
[NAME]dell'item-formsenza marker omonimo (reference broken, §8.3); transpose=+0(no-op) enotesvuoto;- righe vuote supplementari nell'header o fra i blocchi.
11. Esempi¶
11.1 Playlist con override¶
nrk-playlist:0.6
name: Friday Gig
desc: trio set
item: transpose=+2
nrk:0.6
Blue Bossa (Kenny Dorham)
Cm 140bpm
Cm| Fm| Dm7b5| G7| Cm|
item: form=[Intro|4] [A|16]x2 [Solos] [A|16] [Outro]&fermata
nrk:0.6
So What (Miles Davis)
M) [A] [Solos] [Outro]
…datapack…
La prima voce suona Blue Bossa trasposto +2; la seconda dichiara una forma di esecuzione che sostituisce quella del brano.
11.2 Book (nessun override)¶
nrk-book:0.6
name: My Real Book — Vol. 1
nrk:0.6
Autumn Leaves (J. Kosma)
…
nrk:0.6
All The Things You Are (J. Kern)
…
11.3 Collezione a un solo brano¶
nrk-playlist:0.6
name: Solo Spot
nrk:0.6
Naima (J. Coltrane)
…
Degrada in modo trasparente: una collezione con un solo blocco è valida.
12. Riassunto¶
| Concetto | Sintassi | Sezione |
|---|---|---|
| Apertura book | nrk-book:<v> |
§3 |
| Apertura playlist | nrk-playlist:<v> |
§3 |
| Header collezione | name: / desc: |
§4 |
| Blocco-brano | nrk:<v> … (snapshot) |
§5 |
| Ordine | ordine dei blocchi nel file | §5 |
| Override voce | item: chiave=valore … (solo playlist) |
§6 |
| Parametri voce | transpose / notes / form |
§6.1 |
| Forma di voce | form= (grammatica FORM, rest-of-line) |
§6.2, §8 |
| Sostituzione forma | item-form sostituisce la FORM) del brano |
§8.1 |
| Split | alle righe nrk:, prima del parsing musicale |
§9.1 |
| Portabilità | tutto snapshot; copertina/tag/ruoli fuori | §7 |
Questo documento definisce le collezioni (book e playlist) come
strato contenitore portabile di neumaRk, costruito per riuso attorno ai
documenti-brano e alla forma del brano (neumaRk_play_and_form.md).