Saggio sull'incisione musicale automatizzata: 1.4 Costruzione del software

## 1.4 Costruzione del software

Questa sezione descrive alcune decisioni di programmazione prese durante la progettazione di LilyPond.

### Rappresentazione della musica

Idealmente, il formato di input per qualsiasi sistema di formattazione ad alto livello consiste in una descrizione astratta del contenuto da formattare, che nel nostro caso è la musica stessa. La questione solleva un problema formidabile: in che modo possiamo definire che cos’è davvero la musica? Anziché tentare di trovare una risposta, abbiamo rovesciato la domanda. Scriviamo un programma in grado di produrre spartiti e rendiamone il formato il più snello possibile: quando non potremo ridurlo ulteriormente, ci ritroveremo per definizione con il contenuto in sé. Il nostro programma, dunque, serve a definire la forma di un contenuto musicale.

Anche la sintassi è un aspetto dell’interfaccia utente di LilyPond, quindi è semplice da scrivere:

>     {
>       c'4 d'8
>     }

crea un do centrale da un quarto (C1) e un re da un’ottavo immediatamente sopra il do centrale (D1).

A scala microscopica, una sintassi simile si adopera facilmente. A una scala più larga, però, anche la sintassi ha bisogno di una struttura. In quale altro modo, altrimenti, sarebbe possibile scrivere brani complessi come sinfonie e opere liriche? La struttura è formata dal concetto di espressioni musicali: combinando minuscoli frammenti di musica in frammenti più consistenti, possiamo esprimere musica più complessa. Per esempio

>     f'4

Possiamo costruire note simultanee racchiudendole tra `<<` e `>>`:

>     <<c4 d4 e4>>

Possiamo mettere in sequenza queste due espressioni racchiudendole tra parentesi graffe `{ … }`:

>     { f4 <<c4 d4 e4>> }

Anche quella qui sopra è un’espressione, perciò può essere ulteriormente combinata con un’altra espressione simultanea (una minima, in questo caso): `<<`, `\\`, e `>>`:

>     << g2 \\ { f4 <<c4 d4 e4>> } >>

Strutture ricorsive come quelle appena esaminate possono essere specificate in modo pulito e formale in una grammatica indipendente dal contesto, la quale genera anche il codice di analisi. In altre parole, la sintassi di LilyPond è definita in modo chiaro e privo di ambiguità

Interfacce utente e sintassi sono gli elementi immediatamente visibili agli utenti e quelli con cui essi hanno più a che fare. Un po’ sono una questione di gusto, un po’ l’argomento di molte discussioni: non troppo produttive, però, nonostante qualche pregio ce l’abbiano. Nel quadro più ampio di LilyPond, la sintassi di input non è molto importante: inventare una sintassi pulita è facile; molto più difficile, invece, è scrivere un codice di formattazione decente. Contiamo le righe di codice destinate ai due componenti e troveremo la conferma di quanto abbiamo appena affermato: analisi e rappresentazione occupano meno del 10% del codice sorgente.

Nel progettare le strutture usate in LilyPond, abbiamo preso decisioni diverse da quelle che appaiono in altri programmi. Considerate la natura gerarchica della notazione musicale:

In questo caso, abbiamo altezze raggruppate in accordi che appartengono alle misure, le quali appartengono ai righi. La cosa assomiglia a una struttura ordinata di scatole annidate:

Purtroppo, la struttura è ordinata perché si basa su alcuni presupposti troppo restrittivi, il che diventa evidente considerando un esempio musicale più complesso:

In quest’esempio, i righi si interrompono arbitrariamente, le voci saltano dall’uno all’altro e su ciascun pentagramma c’è un’indicazione di tempo diversa. Molti programmi avrebbero del filo da torcere per riprodurlo, proprio perché costruiti con una struttura a scatole annidate. Con LilyPond, d’altronde, abbiamo cercato di mantenere formato di input e struttura il più possibile flessibili.

### Quali simboli incidere?

Il processo di formattazione decide dove mettere i simboli, tuttavia ciò può essere fatto solo dopo aver deciso quali simboli vanno stampati: in altre parole, dopo aver deciso quale notazione adoperare.

La comune notazione musicale è un sistema di registrazione della musica evolutosi nel corso degli ultimi mille anni. La forma normalmente adoperata oggi risale agli inizi del Rinascimento, e anche se nei princìpi di base è rimasta praticamente immutata (cioè testa delle note su un pentagramma di cinque linee), continua a modificarsi nei dettagli per permettere di esprimere le innovazioni introdotte dalla notazione contemporanea. Dunque, la comune notazione musicale comprende circa cinque secoli di musica, con applicazioni che spaziano dalle melodie monofoniche ai mostruosi contrappunti di una grande orchestra.

In che modo possiamo noi imbrigliare un simile mostro a sette teste e costringerlo entro i limiti di un programma per computer? La nostra soluzione è stata quella di spezzare il problema della notazione (al contrario dell’incisione, che riguarda la tipografia) in ‘bocconi’ digeribili e programmabili: ciascun tipo di simbolo è gestito da un modulo a sé, un cosiddetto plug-in, completamente modulare e indipendente, così da poter essere sviluppato e migliorato separatamente. Tali plug-in sono chiamati incisori per analogia con l’artigiano che traduce le idee musicali in simboli grafici.

Nell’esempio seguente, cominciamo con il plug-in per le teste delle note, il `Note_heads_engraver`.

Poi il `Staff_symbol_engraver` aggiunge il rigo,

il `Clef_engraver` definisce un punto di riferimento per il rigo,

e lo `Stem_engraver` aggiunge i gambi.

Ogni volta che il programma incontra una testa di nota (o più d’una, se si tratta di un accordo) leggendo il file sorgente, avvisa lo `Stem_engraver`, il quale crea un gambo e glielo unisce. Aggiungendo gli incisori per travature, legature, accenti, accidenti, stanghette di battuta, indicazioni di tempo e armature di chiave, otteniamo la notazione completa.

Questo sistema funziona bene per la musica monofonica, ma che succede con la polifonia? Nella notazione polifonica, voci diverse possono condividere lo stesso rigo:

Nell’esempio qui sopra, armatura di chiave e rigo sono condivisi, ma gambi, legature, travature, eccetera, appartengono in modo esclusivo a ciascuna voce, perciò gli incisori devono essere raggruppati. Quelli per teste delle note, gambi, legature, eccetera, vanno in un gruppo chiamato ‘contesto Voice’, mentre quelli per chiave, accidenti, misure, eccetera, in un gruppo chiamato ‘contesto Staff’. Nel caso della polifonia, un singolo contesto Staff contiene più di un contesto Voice. Analogamente, contesti Staff multipli possono essere messi in un singolo contesto Score. Il contesto Score rappresenta il contesto notazionale di livello massimo.

#### Vedi anche

Internals Reference: Contesti.

### Architettura flessibile

Quando abbiamo cominciato, abbiamo scritto LilyPond interamente nel linguaggio di programmazione C++, scolpendo nella pietra le sue funzionalità. Per una serie di ragioni, però, la cosa si è rivelata insoddisfacente.

- Quando LilyPond commette degli errori, gli utenti devono sovrascrivere le decisioni di formattazione, il che significa, in altre parole, che devono poter accedere al motore di formattazione. Quindi, regole e impostazioni non possono essere stabilite da noi durante la composizione, ma devono essere accessibili agli utenti durante l’esecuzione del programma.
- La bontà di un’incisione viene giudicata dagli occhi, e in ultima analisi è questione di gusto. Per quanto esperti noi siamo, gli utenti possono essere in disaccordo con le nostre decisioni personali, perciò devono poter accedere anche alle definizioni dello stile tipografico.
- Infine, gli algoritmi di formattazione vengono rifiniti incessantemente da noi, il che ci richiede di dover contare su un approccio alle regole flessibile, e il modo con cui C++ impone di raggrupparle non può essere applicato facilmente alla formattazione della notazione musicale.

Abbiamo affrontato questi problemi integrando in LilyPond un interprete del linguaggio di programmazione Scheme e riscrivendoci parti del programma. Ora l’architettura di formattazione è costruita intorno alla nozione di oggetti grafici, descritti dalle variabili e dalle funzioni di Scheme, e comprende regole di formattazione, stile tipografico e decisioni di formattazione individuali. L’utente può accedere direttamente alla maggior parte di questi controlli.

Le variabili di Scheme controllano le decisioni in merito all’aspetto della pagina. Per esempio, molti oggetti grafici possidono una variabile di direzione che codifica la scelta tra su e giù (o tra destra e sinistra). Qui sotto vedete due accordi con accenti e arpeggi. Nel primo, tutti gli oggetti grafici hanno direzione giù (o sinistra), nel secondo, hanno direzione su (destra).

Il processo di formattazione di uno spartito consiste nel leggere e scrivere le variabili degli oggetti grafici, alcune delle quali hanno un valore predefinito. Per esempio, lo spessore di molte linee – una caratteristica dello stile tipografico – è una di queste. Siete liberi di modificare questo valore, donando al vostro spartito un’impressione tipografica diversa.

Anche le regole di formattazione sono variabili preimpostate: ogni oggetto possiede variabili contenenti procedure. Sono queste ultime a eseguire la formattazione vera e propria, e sostituendone di diverse, possiamo modificare l’aspetto degli oggetti. Nell’esempio seguente, la regola che governa quali oggetti sono adoperati per produrre il simbolo della testa di nota è cambiata nel corso del frammento.

#### LilyPond — Saggio sull’incisione musicale automatizzata v2.22.2 (ramo stabile).