Capítulo 10.5
Scripts
Os scripts fazem de uma vez o que levaria horas à mão: um acento em cada primeiro tempo, remover dinâmicas repetidas, transpor uma pauta, renomear os instrumentos de cem partituras, verificar as extensões. Eles são escritos em Lua 5.4, a linguagem dos plug-ins JW Lua e RGP Lua do Finale.
Um script vê a partitura inteira (pautas, compassos, notas, sinais, textos, diagramação) e pode alterá-la. Ele trabalha numa cópia: se termina, todas as suas alterações viram um único passo de Desfazer; se para com um erro, a partitura fica como estava. Os scripts rodam num ambiente fechado: não podem ler nem gravar arquivos, usar a rede ou abrir programas.
O menu Scripts
Cada arquivo .lua da pasta de scripts é um item do menu Scripts; as subpastas são submenus. Na primeira vez, o Maestro copia ali alguns exemplos, na pasta Exemplos: abra-os num editor de texto para ver como funcionam. Alguns refazem os scripts mais usados da coleção RGP Lua para o Finale (manter uma nota dos acordes, dobrar à oitava, trocar duas camadas, remover articulações das pausas, contar as durações).
- Scripts → Abrir a Pasta de Scripts a mostra no Finder ou no Explorador de Arquivos (no Mac é
~/Library/Application Support/Maestro/Script, no Windows%LOCALAPPDATA%\Maestro\Script). Um arquivo novo ou alterado funciona na hora, sem reiniciar. - Um script trabalha na partitura que está na frente: na Seleção, se o script a usa, ou em tudo.
- Executar Novamente (⌥⌘U, no Windows Ctrl+Alt+U) repete o último script com as mesmas respostas aos seus parâmetros.
- Interromper o Script para um script que demora demais; depois de dois minutos ele para de qualquer jeito.
- Executar um Script… abre qualquer arquivo
.lua, também fora da pasta. - Enquanto o script roda, a partitura fica livre; mas se você a alterar nesse meio-tempo, as alterações do script não são aplicadas (o console avisa), para você nunca perder o que escreveu.
No iPad, o botão Scripts da estante executa os scripts da pasta Script do Maestro (no app Arquivos: No Meu iPad → Maestro → Script), por exemplo para transpor uma peça enquanto você a lê; as alterações são só para a leitura e o arquivo não muda.
O console de scripts
Scripts → Console de Scripts mostra o que os scripts escrevem com print, as mensagens do Maestro e os erros, com arquivo e linha (“Accents.lua:12: Duração inválida…”). Ele abre sozinho depois de um erro.
Na linha de baixo você pode digitar Lua para a partitura que está na frente e pressionar Enter: uma expressão mostra o seu valor (score.title, #score.staves), uma instrução é executada (score.title = "Sonata"), com um passo de Desfazer. As setas ↑ e ↓ trazem de volta as linhas anteriores.
Escrever um script
-- Um acento na primeira nota de cada compasso da flauta,
-- e as notas acima do C6 uma oitava abaixo
local flute = score.staves:find("Flute")
for _, measure in ipairs(flute.measures) do
local first = measure:layer(1)[1]
if first and first.isNote then first:add("accent") end
end
for e in flute:entries() do
for _, note in ipairs(e.notes) do
if note.midi > 84 then note:transpose("-P8") end
end
end
- Os números começam em 1, como em Lua e como os números de compasso:
score.staves[1],measure:layer(1). - As propriedades são lidas e escritas com um ponto (
e.duration = "8"), os métodos são chamados com dois-pontos (e:add("accent")). - As listas (
score.staves,staff.measures,measure:layer(1),e.notes,measure.dynamics…) são contadas com#e percorridas comipairs; os iteradoresscore:entries(),staff:entries(),measure:entries()dão todas as notas, acordes e pausas.
| Objeto | O que é | Algumas propriedades e métodos |
|---|---|---|
score |
a partitura | title, composer, tempo, staves, measures, pageFormat; insertMeasures, deleteMeasures, setKey, setTime, transpose, addStaff |
| pauta | score.staves[n] |
name, abbreviatedName, clef, measures; setClef, move, delete |
| compasso | staff.measures[n] |
number, clef, dynamics, hairpins, texts, chords; layer(n), append, clear, fillWithRests |
| compasso da partitura | score.measures[n] |
time, key; setTime, setKey |
| nota, acorde ou pausa | measure:layer(1)[n] |
duration, isRest, isChord, tick, tied, articulations, notes; add, remove, transpose, setNotes, delete |
| altura | e.notes[n] |
name, step, alter, octave, midi; transpose, delete |
| sinais | measure.dynamics[n], score.slurs[n]… |
os campos do sinal (marking, tick…); delete; nas listas add({ … }) |
As durações se escrevem "w", "h", "q", "8", "16", "32" com pontos ("q."); as alturas "C4" (Dó central), "F#5", "Bb3"; os intervalos "P8", "-m3", "M2", "A4"; as articulações "accent", "staccato", "tenuto", "fermata"….
Além das propriedades da tabela, cada objeto alcança todos os campos da partitura pelo nome que eles têm no arquivo .maestro: e.hidden = true, e:set("pitches.1.notehead", "diamond"), staff:raw() (o objeto inteiro como tabela) e :replace(table), :fields(). Um nome errado dá um erro em vez de passar em silêncio. O próprio script dá a lista completa: maestro.schema() e maestro.schema("entry").
Percorrer a partitura
As funções eachentry, eachcell, eachstaff e each (as mesmas do RGP Lua) percorrem uma região: sem argumentos, a seleção ou, se não houver, a partitura inteira.
-- Um staccato em cada colcheia da seleção (ou da partitura)
for e in eachentry() do
if e.isNote and e.duration == "8" then e:add("staccato") end
end
| Função | O que dá |
|---|---|
eachentry(region, layer) |
notas, acordes e pausas, pauta por pauta e compasso por compasso; layer de 1 a 4 (todas, por padrão) |
eachcell(region) |
cada compasso de cada pauta: for measure, staff in eachcell() do … end |
eachstaff(region) |
as pautas |
each(list), eachbackwards(list) |
os elementos de uma lista, ou as notas de um acorde (each(e)); de trás para frente você pode apagar enquanto avança |
pairsbykeys(table) |
os pares de uma tabela na ordem das chaves |
A região pode ser selection (só a seleção: nada, se não houver), score, uma pauta, um compasso (score.staves[1].measures[4] ou score.measures[4]) ou uma tabela como { staves = {1, 2}, measures = {5, 8} }: um número, ou o primeiro e o último; o que falta quer dizer todos. Existe também eachentrysaved, para quem vem do Finale, mas é igual a eachentry: no Maestro as alterações valem na hora.
Os comandos do editor
editor.perform faz o que você faria à mão com as paletas Articulações, Expressões, Linhas, Quiálteras, Ritornelos e Diagramação: ligaduras de expressão, reguladores, linhas de oitava, textos, casas de repetição, quebras de sistema… Ele age sobre uma região, como uma seleção: por padrão a seleção, ou uma entrada, uma lista de entradas, um compasso, uma pauta ou { staves = …, measures = … }. As perguntas do diálogo do comando são respondidas com opções.
editor.perform("slur", { staves = 1, measures = {1, 2} }) -- ligadura da primeira nota à última
editor.perform("crescendo", score.staves[1].measures[5])
editor.perform("ending", { measures = 8, numbers = {1} }) -- primeira casa
editor.perform("tuplet", e, { ratio = "5:4" }) -- quintina a partir da entrada e
editor.perform("text", measure, { text = "dolce", kind = "expression" })
editor.perform("systemBreak", { measures = 9 }) -- novo sistema a partir do compasso 9
editor.commands() lista os comandos com as suas opções. As articulações são adicionadas ou removidas, como na paleta. Se o editor recusa o comando (“Selecione pelo menos duas notas…”), o script para com um erro naquela linha, e esses comandos também ficam no único passo de Desfazer do script. Cada comando diagrama a partitura de novo: em laços longos, um comando sobre uma região grande é melhor que um por nota.
Nome, atalho e parâmetros
Um bloco de metadados opcional no início do script dá o nome no menu (em vários idiomas), uma descrição, um atalho e os parâmetros a perguntar antes de começar:
--[[ maestro
name = { en = "Transpose", it = "Trasponi" }
description = { en = "Transposes the selection by the chosen interval." }
shortcut = "mod+alt+shift+Y" -- mod = ⌘ no Mac, Ctrl no Windows; ou "F9"
needsSelection = true
params = {
{ name = "interval", type = "text", default = "M2", label = { en = "Interval" } },
{ name = "times", type = "number", default = 1, min = 1, max = 4 },
}
]]
for e in selection.entries() do e:transpose(params.interval) end
O Maestro mostra um formulário com os parâmetros (na próxima vez ele propõe as respostas anteriores) e o script os encontra em params. Os tipos são number, text, boolean e choice (com choices = { … }). Um atalho já usado pelo Maestro não é atribuído. require("name") carrega módulos da pasta lib da pasta de scripts.
Numa pasta inteira
Scripts → Executar em uma Pasta… aplica um script a todas as partituras de uma pasta (também nas subpastas): Maestro, Finale, MusicXML, MuseScore, Guitar Pro e MIDI. No formulário você escolhe os parâmetros do script e o resultado: só o relatório, ou os arquivos salvos em outra pasta como documentos do Maestro, MusicXML, MusicXML comprimido, Finale (.musx) ou MIDI. Os arquivos originais nunca mudam. O console mostra o resultado arquivo por arquivo.
Pelo terminal: maestro-script
O comando maestro-script faz o mesmo sem janelas, para processamento em lote e para outros programas:
maestro-script run Accents.lua Scores/ --recursive --out Converted/ --format musicxml
maestro-script eval 'score.title' Scores/
maestro-script run Transpose.lua piece.musx -p interval=-M2 --out Output/ --json
maestro-script help
No Mac ele fica dentro do Maestro: Scripts → Instalar o comando maestro-script… o deixa disponível no Terminal. No Windows é o maestro-script.exe na pasta do Maestro (Scripts → O comando maestro-script… diz onde). Ele não está na versão da Mac App Store.
Com um assistente (Claude e outros)
maestro-script mcp é um servidor MCP: um assistente como o Claude pode abrir as suas partituras, escrever e executar scripts, olhar as páginas e salvar o resultado. Ele só lê e grava nas pastas que você lhe dá com --root; nunca sobrescreve o original nem, sem --allow-overwrite, um arquivo que já existe.
Com o Claude Code, no Mac:
claude mcp add maestro -- /Applications/Maestro.app/Contents/MacOS/maestro-script mcp --root ~/Scores
Com o Claude Desktop, no arquivo de configuração dos servidores MCP:
{
"mcpServers": {
"maestro": {
"command": "/Applications/Maestro.app/Contents/MacOS/maestro-script",
"args": ["mcp", "--root", "/Users/me/Scores"]
}
}
}
No Windows o comando é %LOCALAPPDATA%\Programs\Maestro\maestro-script.exe. Depois é só pedir: “abra Minuet.musx, coloque um acento em cada primeiro tempo da flauta, mostre a primeira página e salve como Minuet-accents.maestro”.
Nas partituras abertas no Maestro
Um assistente também pode trabalhar nas partituras abertas no aplicativo enquanto você as olha. Isso vem desativado: ative com Scripts → Permitir que os Assistentes Modifiquem as Partituras Abertas e conecte o assistente com maestro-script mcp --app; com o Claude Code, no Mac:
claude mcp add maestro-app -- /Applications/Maestro.app/Contents/MacOS/maestro-script mcp --app
O assistente vê as partituras abertas e a sua seleção; cada script dele é um passo de Desfazer chamado “Assistente” e aparece no console de scripts; quem salva os arquivos é você. A conexão fica no seu computador e na sua conta de usuário, sem rede.
Escrever scripts com um editor
A pasta de scripts também contém a descrição da interface para editores (.maestro/maestro.d.lua e .luarc.json): abra-a no Visual Studio Code com a extensão Lua para ter o preenchimento automático de propriedades e métodos e as verificações enquanto você digita.
Os dados originais do Finale
local ms = finale.others("measSpec", 1)[1] -- o compasso 1 como o Finale o escreve
print(ms.beats, ms.divbeat, ms.width)
for e in score:entries() do
local n = finale.entnum(e) -- a entrada do Finale de onde ela vem
if n then
local original = finale.entry(n)
-- … por exemplo trazer algo que a importação não lê
end
end
finale.others(tag, cmper, inci),finale.details(tag, cmper1, cmper2, inci),finale.texts(tag, number): os registros (os argumentos depois do nome são filtros opcionais);finale.entry(entnum),finale.header(),finale.options(tag),finale.tags()(os nomes presentes).- Um registro é uma tabela:
_tagé o nome, atributos e elementos filhos são campos; um elemento vazio étrue, um repetido é uma sequência (finale.list(x)sempre dá uma sequência). finale.entryFor(entnum)efinale.entnum(entry),finale.staffFor(cmper)efinale.staffCmper(staff)ligam os objetos do Finale aos do Maestro.- É só leitura e não é salvo: depois de Salvar como
.maestroe reabrir,finaledeixa de existir. Para os arquivos.mus, contém os registros que o Maestro consegue ler.
Vindo do RGP Lua
A linguagem é a mesma, os objetos são os do Maestro:
| RGP Lua (Finale) | Maestro |
|---|---|
plugindef() |
o bloco --[[ maestro … ]] no início do script |
finenv.Region() |
selection; as funções de percurso sem região usam a seleção ou a partitura inteira |
for e in eachentrysaved(finenv.Region()) |
for e in eachentry() (também eachentrysaved, eachcell, eachstaff, each, pairsbykeys) |
finale.FCStaves, FCStaff |
score.staves, score.staves[n] |
finale.FCMeasures, FCMeasure |
score.measures[n] (fórmula de compasso e tonalidade), staff.measures[n] (a música) |
FCNoteEntryLayer, FCNoteEntry |
measure:layer(n), measure:layer(n)[i] |
FCNote |
e.notes[i] |
entry.Duration (EDU, semínima 1024) |
e.duration ("q") ou e.ticks (semínima 960) |
FCExpression, FCSmartShape |
measure.dynamics, measure.texts, score.slurs, measure.hairpins… |
entry:Save(), measure:Save() |
não é necessário: as alterações valem na hora (e se desfazem todas juntas) |