Capitolo 10.5
Script
Gli script fanno in un colpo quello che a mano richiederebbe ore: mettere un accento su ogni tempo forte, togliere le dinamiche ripetute, trasporre un pentagramma, rinominare gli strumenti di cento partiture, controllare le estensioni. Si scrivono in Lua 5.4, il linguaggio dei plug-in JW Lua e RGP Lua di Finale.
Uno script vede tutta la partitura (pentagrammi, misure, note, segni, testi, impaginazione) e può cambiarla. Lavora su una copia: se finisce bene, tutte le sue modifiche diventano un solo passo di Annulla; se si ferma per un errore, la partitura resta com’era. Gli script girano in un ambiente chiuso: non possono leggere o scrivere file, usare la rete o avviare programmi.
Il menu Script
Ogni file .lua della cartella degli script è una voce del menu Script; le sottocartelle sono sottomenu. La prima volta Maestro ci copia alcuni esempi, nella cartella Esempi: aprili con un editor di testo per vedere come sono fatti. Alcuni rifanno gli script più usati della raccolta di RGP Lua per Finale (tenere una nota degli accordi, raddoppiare all’ottava, scambiare due livelli, togliere le articolazioni dalle pause, contare le durate).
- Script → Apri la cartella degli script la mostra nel Finder o in Esplora file (sul Mac è
~/Library/Application Support/Maestro/Script, su Windows%LOCALAPPDATA%\Maestro\Script). Un file nuovo o modificato vale subito, senza riavviare. - Uno script lavora sulla partitura in primo piano: sulla selezione, se lo script la usa, o su tutto.
- Esegui di nuovo (⌥⌘U, su Windows Ctrl+Alt+U) ripete l’ultimo script con le stesse risposte ai parametri.
- Interrompi lo script ferma uno script che ci mette troppo; dopo due minuti si ferma comunque.
- Esegui uno script… apre un file
.luaqualsiasi, anche fuori dalla cartella. - Mentre lo script gira la partitura resta libera; se però la cambi nel frattempo, le modifiche dello script non si applicano (la console lo dice), così non perdi quello che hai scritto.
Sull’iPad il pulsante Script del leggio esegue gli script della cartella Script di Maestro (nell’app File: Sull’iPad → Maestro → Script), per esempio per trasporre un brano mentre lo leggi; le modifiche valgono per la lettura e il file non cambia.
La console degli script
Script → Console degli script mostra quello che gli script scrivono con print, i messaggi di Maestro e gli errori, con il file e la riga («Accenti.lua:12: Durata non valida…»). Dopo un errore si apre da sola.
Nella riga in basso puoi scrivere Lua sulla partitura in primo piano e premere Invio: un’espressione mostra il suo valore (score.title, #score.staves), un’istruzione la esegue (score.title = "Sonata"), con un passo di Annulla. Le frecce ↑ e ↓ richiamano le righe di prima.
Scrivere uno script
-- Un accento sulla prima nota di ogni misura del flauto,
-- e le note sopra il Do6 un'ottava sotto
local flauto = score.staves:find("Flauto")
for _, misura in ipairs(flauto.measures) do
local prima = misura:layer(1)[1]
if prima and prima.isNote then prima:add("accent") end
end
for e in flauto:entries() do
for _, nota in ipairs(e.notes) do
if nota.midi > 84 then nota:transpose("-P8") end
end
end
- I numeri partono da 1, come in Lua e come i numeri di misura:
score.staves[1],misura:layer(1). - Le proprietà si leggono e scrivono con il punto (
e.duration = "8"), i metodi si chiamano con i due punti (e:add("accent")). - Gli elenchi (
score.staves,pentagramma.measures,misura:layer(1),e.notes,misura.dynamics…) si contano con#e si percorrono conipairs; gli iteratoriscore:entries(),pentagramma:entries(),misura:entries()danno tutte le note, accordi e pause.
| Oggetto | Che cos’è | Alcune proprietà e metodi |
|---|---|---|
score |
la partitura | title, composer, tempo, staves, measures, pageFormat; insertMeasures, deleteMeasures, setKey, setTime, transpose, addStaff |
| pentagramma | score.staves[n] |
name, abbreviatedName, clef, measures; setClef, move, delete |
| misura | pentagramma.measures[n] |
number, clef, dynamics, hairpins, texts, chords; layer(n), append, clear, fillWithRests |
| misura della partitura | score.measures[n] |
time, key; setTime, setKey |
| nota, accordo o pausa | misura:layer(1)[n] |
duration, isRest, isChord, tick, tied, articulations, notes; add, remove, transpose, setNotes, delete |
| altezza | e.notes[n] |
name, step, alter, octave, midi; transpose, delete |
| segni | misura.dynamics[n], score.slurs[n]… |
i campi del segno (marking, tick…); delete; negli elenchi add({ … }) |
Le durate si scrivono "w", "h", "q", "8", "16", "32" con i punti ("q."); le altezze "C4" (Do centrale), "F#5", "Bb3" o anche "Sol4"; gli intervalli "P8", "-m3", "M2", "A4"; le articolazioni "accent", "staccato", "tenuto", "fermata"….
Oltre alle proprietà della tabella, ogni oggetto arriva a ogni campo della partitura con il nome che ha nel file .maestro: e.hidden = true, e:set("pitches.1.notehead", "diamond"), pentagramma:raw() (tutto l’oggetto come tabella) e :replace(tabella), :fields(). Un nome sbagliato dà errore invece di passare in silenzio. L’elenco completo lo dà lo script stesso: maestro.schema() e maestro.schema("entry").
Scorrere la partitura
Le funzioni eachentry, eachcell, eachstaff ed each (le stesse di RGP Lua) percorrono una regione: senza argomenti la selezione o, se non c’è, tutta la partitura.
-- Uno staccato su tutte le crome della selezione (o della partitura)
for e in eachentry() do
if e.isNote and e.duration == "8" then e:add("staccato") end
end
| Funzione | Che cosa dà |
|---|---|
eachentry(regione, livello) |
note, accordi e pause, pentagramma per pentagramma e misura per misura; livello da 1 a 4 (di serie tutti) |
eachcell(regione) |
ogni misura di ogni pentagramma: for misura, pentagramma in eachcell() do … end |
eachstaff(regione) |
i pentagrammi |
each(elenco), eachbackwards(elenco) |
gli elementi di un elenco, o le note di un accordo (each(e)); all’indietro si può cancellare mentre si scorre |
pairsbykeys(tabella) |
le coppie di una tabella in ordine di chiave |
La regione può essere selection (solo la selezione: niente se non c’è), score, un pentagramma, una misura (score.staves[1].measures[4] o score.measures[4]) o una tabella come { staves = {1, 2}, measures = {5, 8} }: un numero, oppure il primo e l’ultimo; quello che manca vale tutto. C’è anche eachentrysaved, per chi viene da Finale, ma è uguale a eachentry: in Maestro le modifiche valgono subito.
I comandi dell’editor
editor.perform fa quello che faresti a mano con le palette di Articolazioni, Espressioni, Linee, Gruppi irregolari, Ritornelli e Impaginazione: legature, forcelle, linee d’ottava, testi, volte, interruzioni di sistema… Agisce su una regione, come una selezione: di serie la selezione, oppure un’entry, un elenco di entry, una misura, un pentagramma o { staves = …, measures = … }. Le domande della finestra del comando si rispondono con le opzioni.
editor.perform("slur", { staves = 1, measures = {1, 2} }) -- legatura dalla prima all'ultima nota
editor.perform("crescendo", score.staves[1].measures[5])
editor.perform("ending", { measures = 8, numbers = {1} }) -- prima volta
editor.perform("tuplet", e, { ratio = "5:4" }) -- quintina dall'entry e
editor.perform("text", misura, { text = "dolce", kind = "expression" })
editor.perform("systemBreak", { measures = 9 }) -- nuovo sistema dalla misura 9
editor.commands() elenca i comandi con le loro opzioni. Le articolazioni si mettono o si tolgono, come nella palette. Se l’editor rifiuta il comando («Seleziona almeno due note…») lo script si ferma con un errore alla sua riga, e anche questi comandi restano nell’unico passo di Annulla dello script. Ogni comando reimpagina la partitura: nei cicli lunghi conviene un comando su una regione grande piuttosto che uno per nota.
Nome, scorciatoia e parametri
In testa allo script un blocco di metadati, facoltativo, dà il nome nel menu (in più lingue), una descrizione, una scorciatoia e i parametri da chiedere prima di partire:
--[[ maestro
name = { it = "Trasponi", en = "Transpose" }
description = { it = "Traspone la selezione dell'intervallo scelto." }
shortcut = "mod+alt+shift+Y" -- mod = ⌘ sul Mac, Ctrl su Windows; oppure "F9"
needsSelection = true
params = {
{ name = "interval", type = "text", default = "M2", label = { it = "Intervallo" } },
{ name = "volte", type = "number", default = 1, min = 1, max = 4 },
}
]]
for e in selection.entries() do e:transpose(params.interval) end
Maestro mostra un modulo con i parametri (la volta dopo propone le risposte di prima) e lo script li trova in params. I tipi sono number, text, boolean e choice (con choices = { … }). Una scorciatoia già usata da Maestro non si assegna. require("nome") carica i moduli dalla cartella lib della cartella degli script.
Su una cartella intera
Script → Esegui su una cartella… applica uno script a tutte le partiture di una cartella (anche nelle sottocartelle): Maestro, Finale, MusicXML, MuseScore e MIDI. Nel modulo scegli i parametri dello script e il risultato: solo il resoconto, oppure i file salvati in un’altra cartella come documenti Maestro, MusicXML, MusicXML compresso, Finale (.musx) o MIDI. I file originali non cambiano mai. La console mostra l’esito file per file.
Dal terminale: maestro-script
Il comando maestro-script fa lo stesso senza finestre, per elaborazioni in blocco e per altri programmi:
maestro-script run Accenti.lua Brani/ --recursive --out Convertiti/ --format musicxml
maestro-script eval 'score.title' Brani/
maestro-script run Trasponi.lua brano.musx -p interval=-M2 --out Uscita/ --json
maestro-script help
Sul Mac è dentro Maestro: Script → Installa il comando maestro-script… lo rende disponibile nel Terminale. Su Windows è maestro-script.exe nella cartella di Maestro (Script → Il comando maestro-script… dice dove). Non c’è nella versione del Mac App Store.
Con un assistente (Claude e altri)
maestro-script mcp è un server MCP: un assistente come Claude può aprire le tue partiture, scrivere ed eseguire script, guardare le pagine e salvare il risultato. Legge e scrive solo nelle cartelle che gli indichi con --root; non sovrascrive mai l’originale né, senza --allow-overwrite, un file che esiste già.
Con Claude Code, sul Mac:
claude mcp add maestro -- /Applications/Maestro.app/Contents/MacOS/maestro-script mcp --root ~/Partiture
Con Claude Desktop, nel file di configurazione dei server MCP:
{
"mcpServers": {
"maestro": {
"command": "/Applications/Maestro.app/Contents/MacOS/maestro-script",
"args": ["mcp", "--root", "/Users/io/Partiture"]
}
}
}
Su Windows il comando è %LOCALAPPDATA%\Programs\Maestro\maestro-script.exe. Poi basta chiedere: «apri Minuetto.musx, metti un accento su ogni tempo forte del flauto, fammi vedere la prima pagina e salvalo come Minuetto-accenti.maestro».
Sulle partiture aperte in Maestro
Un assistente può lavorare anche sulle partiture aperte nell’app, mentre le guardi. È spento di serie: accendilo con Script → Permetti agli assistenti di modificare le partiture aperte e collega l’assistente con maestro-script mcp --app; con Claude Code, sul Mac:
claude mcp add maestro-app -- /Applications/Maestro.app/Contents/MacOS/maestro-script mcp --app
L’assistente vede le partiture aperte e la tua selezione; ogni suo script è un passo di Annulla chiamato «Assistente» e compare nella console degli script; i file li salvi tu. Il collegamento passa solo dal tuo computer e dal tuo utente, senza rete.
Scrivere script con un editor
La cartella degli script contiene anche le descrizioni dell’interfaccia per gli editor (.maestro/maestro.d.lua e .luarc.json): aprendola in Visual Studio Code con l’estensione Lua hai l’autocompletamento di proprietà e metodi e i controlli mentre scrivi.
I dati originali di Finale
Una partitura aperta da un file di Finale (.musx o .mus) porta con sé, finché resta aperta, i dati originali del file: gli script li leggono con finale, con i nomi dell’EnigmaXML (gli stessi di musxdom e del PDK di Finale). Servono a recuperare quello che l’importazione non porta in Maestro, o a confrontare.
local ms = finale.others("measSpec", 1)[1] -- la misura 1 come la scrive Finale
print(ms.beats, ms.divbeat, ms.width)
for e in score:entries() do
local n = finale.entnum(e) -- l'entry di Finale da cui viene
if n then
local originale = finale.entry(n)
-- … per esempio riportare un'informazione che l'importazione non legge
end
end
finale.others(tag, cmper, inci),finale.details(tag, cmper1, cmper2, inci),finale.texts(tag, numero): i record (gli argomenti dopo il nome sono facoltativi e filtrano);finale.entry(entnum),finale.header(),finale.options(tag),finale.tags()(i nomi presenti).- Un record è una tabella:
_tagè il nome, gli attributi e gli elementi sono campi; un elemento vuoto valetrue, uno ripetuto è una sequenza (finale.list(x)dà sempre una sequenza). finale.entryFor(entnum)efinale.entnum(entry),finale.staffFor(cmper)efinale.staffCmper(pentagramma)collegano gli oggetti di Finale e quelli di Maestro.- È sola lettura e non si salva: dopo Salva in
.maestroe una nuova apertura,finalenon c’è più. Per i.musci sono i record che Maestro sa leggere.
Per chi viene da RGP Lua
Il linguaggio è lo stesso, gli oggetti sono quelli di Maestro:
| RGP Lua (Finale) | Maestro |
|---|---|
plugindef() |
il blocco --[[ maestro … ]] in testa allo script |
finenv.Region() |
selection; le funzioni di scorrimento senza regione usano la selezione o tutta la partitura |
for e in eachentrysaved(finenv.Region()) |
for e in eachentry() (anche eachentrysaved, eachcell, eachstaff, each, pairsbykeys) |
finale.FCStaves, FCStaff |
score.staves, score.staves[n] |
finale.FCMeasures, FCMeasure |
score.measures[n] (tempo e armatura), pentagramma.measures[n] (la musica) |
FCNoteEntryLayer, FCNoteEntry |
misura:layer(n), misura:layer(n)[i] |
FCNote |
e.notes[i] |
entry.Duration (EDU, semiminima 1024) |
e.duration ("q") o e.ticks (semiminima 960) |
FCExpression, FCSmartShape |
misura.dynamics, misura.texts, score.slurs, misura.hairpins… |
entry:Save(), measure:Save() |
non serve: le modifiche valgono subito (e si annullano tutte insieme) |