Vai al contenuto

Articolazioni (riga A))

STATO: SPEC. §1-§7 sigillate 2026-05-23 (deduzione A) attuale, già nel parser); §8-§12 sigillate 2026-05-24 (estensioni: modello token, glissato gl, span legatura/onda). Implementate end-to-end (parser Fase 3.1 + rendering 3.1b); stato/dettagli in neumaRk_changelog.md §0.6. Limite noto: la collisione verticale onda↔legatura è un follow-up di layout. Binding e ordine non sono ridefiniti qui: vedi neumaRk_datapack.md §2.1 e §3. Decisioni di sigillo in §7 (base) e §12 (estensioni).

1. Definizione

La riga A) (Articulations) porta le articolazioni e gli ornamenti da applicare, posizione per posizione, agli eventi della riga musicale a cui è legata. È un layer parallelo: non altera ritmo, durate o flusso, solo gli attributi notazionali degli eventi.

2. Posizione strutturale e binding

Marcatore, opzionalità e ordine logico sono definiti in neumaRk_datapack.md §2.1 (tabella marcatori) e §3 (ordine implicito: A) è opzionale e precede la riga N) del proprio gruppo di note). In sintesi operativa:

  • A) lega alla riga sotto ("modello §4.A.5"): i suoi token sono messi in attesa (pending) e drenati sul primo target musicale successivo nello stesso gruppo/rigo.
  • Target del drain, in ordine di priorità:
  • la chord-rhythm della riga C) immediatamente sotto — solo per lo stave 0 (il chord-rhythm vive in MeasureShared), se la C) ha ritmo;
  • altrimenti la riga N) dello stesso stave.
  • In multi-stave ogni rigo ha il proprio bucket di pending; la voce 2 (N2) ha un bucket A) indipendente (vedi neumaRk_voices.md §3.1).

3. Vocabolario

Ogni token è un simbolo (o combinazione) che mappa a un'articolazione o ornamento. I token sono separati da spazio; più articolazioni sulla stessa nota si scrivono come token unico senza spazi (es. >!).

Token Significato Tipo
- tenuto articolazione
> accento articolazione
! staccato articolazione
^ marcato articolazione
+ pizzicato (mano sx) articolazione
o fermata (sopra) articolazione
os fermata breve articolazione
ol fermata lunga articolazione
-! portato (tenuto+staccato) combinazione
>! accento staccato combinazione
tr trillo ornamento
m mordente ornamento
M mordente inverso ornamento
t gruppetto ornamento
T gruppetto inverso ornamento
, respiro ornamento
h armonico articolazione (estensione §8+)
v arco in su articolazione (estensione §8+)
n arco in giù articolazione (estensione §8+)
. placeholder: nessuna articolazione su quella posizione

h per l'armonico (il simbolo ° non è ASCII e o è già fermata); v/n per gli archi (n ricorda il ⊓). A questi si aggiungono un token per-coppia (glissato gl, §9) e due span (legatura ( ), onda ~, §10), introdotti dal modello di token decomponibile in §8.

Il vocabolario è l'insieme chiuso della tabella sopra (più il placeholder ., i per-coppia/span delle estensioni §8+). Solo questi token sono articolazioni valide; il charset di classificazione del parser deriva da questo insieme (vedi §6 e §11). Un token fuori dal vocabolario non è un'articolazione valida.

I marcatori > e ^ rendono una riga univocamente articulations in fase di classificazione (articulations_specific_marker): nessun'altra riga musicale li usa isolati.

4. Allineamento count-based

La corrispondenza con gli eventi della riga target è strettamente posizionale per conteggio (come neumaRk_dynamics.md §4.1), left-to-right:

N) | a8 b  c  d |
A) | >  .  !  ^ |
     ↑      ↑  ↑
   accento staccato marcato (b: nessuna)
  • Il token . o un token vuoto = nessuna articolazione su quella nota.
  • Se i token sono meno delle note, le note residue restano senza articolazione (no-op).
  • Se i token sono più degli eventi, gli extra sono scartati silenziosamente (filosofia auto-fix; nota: a differenza di D) non viene emesso un warning — vedi Decisioni aperte).
  • L'allineamento conta anche i rest (a differenza di L), che li salta): una posizione sopra un rest è un evento a tutti gli effetti. È ciò che permette a uno span (§10) di coprire un rest (vi si scrive ~ e lo span prosegue).

5. Esempi

A) | >    !  |
N) | c4 d e f |     accento su c, staccato su e

A) | >!         |
C) | C7  F7     |     accento-staccato sul primo evento di chord-rhythm (stave 0)

6. Il vocabolario è parte della deduzione (fonte unica)

Il parser non è agnostico rispetto a questo vocabolario: per dedurre che una riga prefix-less è A) deve riconoscerne i token. Storicamente il vocabolario era diviso e duplicato — metà sintattica nel parser (charset di articulations_line / articulations_specific_marker, usato per la classificazione) e metà semantica nel renderer (nrkArticToModifiers, token-NRK → codice VexFlow) — e le due metà divergevano (il charset ammetteva _ e lettere isolate non mappate).

Modello normativo (deciso 2026-05-23): questo documento è la fonte unica. La spec definisce token-NRK → significato musicale (es. !→staccato); il significato è normativo, i codici VexFlow no (un altro renderer ne userebbe altri). Da questa tabella derivano: il charset di classificazione e la tokenizzazione del parser; la mappa significato → glifo del renderer.

7. Decisioni (chiuse 2026-05-23)

  1. Fonte unica ✓ — la spec è normativa; charset-parser e mappa-renderer ne derivano. Elimina il drift.
  2. Vocabolario = insieme chiuso ✓ — i simboli fuori tabella (es. _, lettere isolate) non sono articolazioni valide; il charset va ristretto al vocabolario.
  3. Eccesso di token ✓ — A) emette W131 come D) (era silent drop).
  4. Significato normativo, VexFlow implementativo ✓ — la colonna "significato" è vincolante; i codici a-/a>/… sono nota d'implementazione.

Follow-up lato codice (D(L)→D(P), tracciato in docs/feature-deduction-spec-audit.md), tutti ✅ fatti (Fase 3.1): charset di classificazione derivato dal vocabolario chiuso; W131 su eccesso token; W139 (validazione lessicale) su qualsiasi token con sequenza fuori-vocabolario, inclusa la lettera-frammento isolata (s di os, r di tr).


Estensioni A) (sigillate 2026-05-24)

Le sezioni §8-§12 sono normative e implementate (parser Fase 3.1 + render 3.1b). Estendono il vocabolario per-posizione di §3 con: un modello di token decomponibile (§8), un token per-coppia (glissato gl, §9) e due span (§10).

8. Modello del token: decomposizione e co-locazioni

Il vocabolario base (§3) usa, nel parser attuale, uno switch su token interi (nrkArticToModifiers, con case ">!"). Le estensioni richiedono un vero decompositore per-posizione, analogo a parseDynamicsToken di D).

Ogni posizione (token separato da spazi) è una concatenazione order-free di elementi, decomposta con greedy longest-match sugli atomi multi-char (prima os/ol di o, prima tr di t):

elemento :=
  | onda            ~[1-4]? ("…")?      # §10.2 (cifra/etichetta solo in apertura)
  | legatura-apre   (                    # §10.1
  | legatura-chiude )                    # §10.1
  | staffa-apre     [ ("…")?             # §10.3 (etichetta solo in apertura)
  | staffa-chiude   ]                    # §10.3
  | ottava-apre     8u | 8d              # §10.4 (alta / bassa)
  | ottava-chiude   8.                   # §10.4 (8 + punto)
  | glissato        gl                   # §9 (verso la nota successiva)
  | fermata         o | os | ol          # greedy: os/ol prima di o
  | trillo          tr                   # greedy: tr prima di t
  | gruppetto       t | T
  | mordente        m | M
  | per-nota 1ch    - > ! ^ + , h v n
  | placeholder     .                    # nessun elemento su quella posizione

Conseguenza: -! e >! non sono più token speciali ma co-locazioni (-+! = portato; >+! = accento-staccato). Tutte le combinazioni emergono dalla concatenazione: (! = legatura-apre + staccato; >gl = accento + glissato. La voce "combinazione" della tabella §3 resta valida come significato, non come token atomico.

La decomposizione (tokenize) è separata dalla risoluzione degli span (post-pass), come D) separa parseDynamicsToken da extract_hairpins (§10).

9. Glissato fra note (gl)

gl sulla posizione di una nota = quella nota glissa verso la successiva. La direzione (su/giù) è implicita dalle due altezze. È per-coppia, non uno span.

A) .  gl .  .
N) r4 a  b  r       # a glissa verso b

Distinto dal glissato di singola nota (scoop/fall, entrata/uscita da una nota), che vive in N) come modificatore fra parentesi — vedi neumaRk_notes_and_durations.md (estensioni N).

10. Span

Due span, mutuati dal modello di D) (apertura/chiusura risolte in un post-pass).

10.1 Legatura di portamento ( ) (span delimitato)

Stile LilyPond: ( apre sulla nota della sua posizione, ) chiude sulla nota della sua posizione; la curva copre dall'apertura alla chiusura.

A) . ( . . )
N) c d e f g      # legatura da d a g
  • Cross-barline: sì.
  • Confinata al rigo: una legatura aperta a fine rigo si chiude lì con warning; una ) orfana → warning, ignorata.
  • No sovrapposizione/annidamento (v1): al massimo una legatura aperta per volta; un secondo ( mentre una è aperta → warning. (Annidamento rinviato.)
  • Apertura+chiusura sulla stessa nota (()): degenere → ignorata/warning.
  • Co-locabile coi per-nota: (! = legatura-apre + staccato.

10.2 Onda ~[1-4] (span per ripetizione)

Una linea ondulata sempre sopra il rigo. Unifica vibrato / shake / long-vibrato / laid-back: l'ampiezza è la larghezza visiva, l'etichetta ne dà il nome.

Ampiezza — cifra opzionale 14 (default ~ = livello 1):

  • ~N (con cifra) apre un nuovo span di ampiezza N, chiudendo quello corrente;
  • ~ (nudo) estende lo span corrente; se nessuno è aperto, ne apre uno di livello 1.
A) ~3 ~ ~ ~1 ~ ~      # onda grande su 3 note, poi onda piccola su 3

Estensione e rest: ~ consecutivi (anche cross-barline) = un'unica onda; una posizione senza ~ (inclusa una misura vuota) chiude lo span. I rest non interrompono (l'allineamento conta i rest, §4).

A) ~2 ~ ~ ~ |   | ~3 ~ ~ ~
N) a4 b r c | d | a4 b r c
#  └─ ampiezza-2 su a b r(rest) c ─┘  (mis.2 vuota → niente onda)  └─ ampiezza-3 ─┘

Etichetta (opzionale, solo sul token di apertura): "…" subito dopo l'onda, resa sopra il suo inizio. L'etichetta è un container di text markup (neumaRk_text_markup.md §2.2): accetta i marcatori #/##/###, */**, __; il default è tondo (corsivo/grassetto/underline/size solo via i marcatori). Ampiezza ed etichetta sono indipendenti.

A) ~3"shake" ~ ~ ~
A) ~"vibrato" ~ ~
A) ~4"laid back" ~ ~ ~ ~

L'etichetta dà la semantica (vibrato vs shake vs laid-back); il glifo ~ da solo è solo "onda di ampiezza N". Per uno strumento di trascrizione la pagina è la verità; un eventuale playback inferirà da etichetta/ampiezza.

10.3 Staffa d'analisi [ ] (span delimitato)

Una staffa di raggruppamento sopra il rigo, come le parentesi dell'analisi formale. [ apre sulla nota della sua posizione, ] chiude su quella della sua (stile LilyPond, esattamente come la legatura §10.1).

A) . . ["triade di DO" . . ] |
N) a a c            e | g e c a
  • Etichetta (opzionale): stringa "…" subito dopo [ (riusa lo stesso token opaco dell'onda). Resa allineata a sinistra sopra l'apertura ed è un container di text markup (neumaRk_text_markup.md §2.2: #/*/__), tonda di default. Una staffa senza etichetta ([ … ]) è ammessa.
  • Resa: parentesi quadra sempre sopra il rigo, con i due montanti rivolti verso il basso (verso le note).
  • Cross-barline / cross-system: sì.
  • No overlap/annidamento (v1): al più una staffa aperta per volta; una seconda [ con una aperta → warning (W144.bracket_open_overlap); una ] orfana → warning (W144.bracket_close_unmatched); [] degenere (apertura+chiusura sulla stessa nota) → warning (W144.bracket_degenerate); staffa non chiusa a fine rigo → chiusura implicita + warning (W144.bracket_unclosed_eol).
  • Puramente grafica: nessun effetto sul playback (è un'annotazione analitica).
  • Nota di sintassi: nelle righe A) un [ … | … ] è una staffa che attraversa la barra, quindi il | interno è una barline reale — NON un polychord [X|Y] (§accordi) né una volta [1.. La mappa sopprime il rilevamento polychord sulle righe Articulations.

10.4 Ottava 8u / 8d8 (span delimitato)

Una linea di ottava. 8u apre un'8va alta, 8d apre un'8vb bassa; 8. chiude lo span (inclusivo sull'ultima nota coperta). Delimitata come la legatura (§10.1) — volutamente NON il modello a estensione dell'onda (§10.2): la linea d'ottava è rada, ripetere un token su ogni nota sarebbe macchinoso. Un 8 nudo (senza u/d/.) è fuori-vocabolario (probabile refuso di 8.) → W139.

A) . . . . . . . 8u . . . | . . . 8. |
N) c d e f g a b c  d e f | a g f e d c b a g
#                  └─ 8va dall'8ª nota, attraverso la barra, fino alla chiusura ─┘
  • Resa: la sigla SMuFL standard — 8va (alta, glifo U+E511) / 8ba (bassa, glifo U+E513) — seguita da una linea orizzontale tratteggiata e un gancio finale verso il rigo; sopra per 8u, sotto per 8d. (Entrambi i glifi sono nel subset BravuraLS/PetalumaLS.)
  • Playback: le note coperte sono trasposte di ±12 semitoni (8u = +12, 8d = −12). L'altezza scritta resta invariata — le teste-nota non si muovono; solo la riproduzione viene spostata.
  • Cross-barline / cross-system: sì.
  • No overlap/annidamento (v1): seconda apertura con una aperta → warning (W144.octave_open_overlap); 8. di chiusura orfano → warning (W144.octave_close_unmatched); 8u8. degenere → warning (W144.octave_degenerate); apertura non chiusa a fine rigo → chiusura implicita + warning (W144.octave_unclosed_eol).

Convenzione span (decisa per A), da riusare per le forcelle/cresc. di D)): ogni span lungo è delimitato — un token di apertura (eventualmente parametrizzato) + un token di chiusura; le posizioni intermedie sono placeholder .. Legatura ( ), staffa [ ] e ottava 8u…8 la seguono. L'onda ~ mantiene il modello a estensione come unica eccezione documentata, perché il suo glifo è una linea ondulata continua su ogni nota.

11. Impatto sulla deduzione (charset esteso)

Le estensioni allargano la superficie di classificazione delle righe (neumaRk_datapack.md §3.bis):

  • Charset A): include i nuovi simboli ( ) ~ h v n g e le cifre 1-4, più [ ] 8 u d (§10.3 staffa, §10.4 ottava), oltre al vocabolario base §6 (da cui il charset è derivato — fonte unica).
  • ~ è marker specifico: una riga che contiene ~ è classificata univocamente come A), malgrado le cifre che altrimenti la avvicinerebbero a chords/notes.
  • [ ] 8 d NON sono marker specifici: sono molto sovraccarichi ([ = sezione [NAME], etichetta alternate-chord, grace/annotation in N); 8/d = durata/nota). Allargano solo il charset A); la priorità notes-line (TB4) protegge le righe N), e una riga staffa/ottava implicita richiede il prefix esplicito A) per essere classificata come articolazioni.
  • g/gl vs nota G — TB4: g (testa del token gl glissato, §9) è anche la nota G, e con le durate 14 rende ambigue righe come g, g4, g g g. La regola TB4 (neumaRk_datapack.md §3.bis.5) dà priorità alle note: una riga che è una notes-line valida (ogni token matcha la grammatica parse_notes
  • almeno un pitch reale) è N), non A). Il token gl non è una nota valida, quindi una riga di articolazioni con gl resta A).
  • Quote-aware: il contenuto di un'etichetta "…" (es. shake) è escluso dal match di classificazione — lettere fuori-charset o un ~ dentro il testo non rompono né marcano falsamente la riga.

12. Decisioni (estensioni, chiuse 2026-05-24)

  1. Glissato fra-note (gl, §9) ≠ glissato singola-nota (scoop/fall, in N)). ✓
  2. Vocabolario esteso = insieme chiuso; significato normativo, codici VexFlow implementativi (eredita §6/§7). ✓
  3. Modello token unificato (decompositore + co-locazioni, §8). ✓
  4. Legatura ( ) delimitata, no overlap v1 (§10.1). ✓
  5. Arco su v, giù n; armonico h (§3). ✓
  6. Onda ~: livelli 1–4; cifra = nuovo span, nudo = estende; sempre sopra; copre i rest; etichetta "…" in apertura, corsivo (§10.2). ✓
  7. Shake / long-vibrato / laid-back unificati nell'onda ~N + etichetta. ✓
  8. Staffa d'analisi [ ] delimitata, etichetta "…" opzionale, sempre sopra, puramente grafica; no overlap v1 (§10.3). ✓ (aggiunta 2026-05-31)
  9. Ottava 8u/8d8. delimitata (8. chiude), tratteggiata sopra/ sotto, trasporta il playback ±12; no overlap v1 (§10.4). ✓ (2026-05-31)
  10. Convenzione span: gli span lunghi sono delimitati (apertura + chiusura); l'onda è l'unica eccezione (estensione), avendo un glifo a linea continua. ✓