Kapitel 10.5
Skripte
Skripte erledigen in einem Zug, was von Hand Stunden dauern würde: ein Akzent auf jeder Zählzeit eins, wiederholte Dynamik entfernen, eine Notenzeile transponieren, die Instrumente von hundert Partituren umbenennen, Tonumfänge prüfen. Sie werden in Lua 5.4 geschrieben, der Sprache der Finale-Plug-ins JW Lua und RGP Lua.
Ein Skript sieht die ganze Partitur (Notenzeilen, Takte, Noten, Zeichen, Texte, Layout) und kann sie ändern. Es arbeitet an einer Kopie: Wenn es fertig wird, werden alle seine Änderungen ein einziger Rückgängig-Schritt; bricht es mit einem Fehler ab, bleibt die Partitur, wie sie war. Skripte laufen in einer geschlossenen Umgebung: Sie können keine Dateien lesen oder schreiben, das Netz nicht nutzen und keine Programme starten.
Das Menü Skripte
Jede .lua-Datei im Skriptordner ist ein Eintrag im Menü Skripte; Unterordner sind Untermenüs. Beim ersten Mal kopiert Maestro einige Beispiele hinein, in den Ordner Beispiele: Öffne sie in einem Texteditor, um zu sehen, wie sie gemacht sind. Einige bilden die meistgenutzten Skripte der RGP-Lua-Sammlung für Finale nach (eine Note der Akkorde behalten, in der Oktave verdoppeln, zwei Ebenen tauschen, Artikulationen von Pausen entfernen, Notenwerte zählen).
- Skripte → Skriptordner öffnen zeigt ihn im Finder oder im Explorer (auf dem Mac
~/Library/Application Support/Maestro/Script, unter Windows%LOCALAPPDATA%\Maestro\Script). Eine neue oder geänderte Datei gilt sofort, ohne Neustart. - Ein Skript arbeitet mit der vorderen Partitur: mit der Auswahl, wenn das Skript sie verwendet, oder mit allem.
- Erneut ausführen (⌥⌘U, unter Windows Strg+Alt+U) wiederholt das letzte Skript mit denselben Antworten auf die Parameter.
- Skript abbrechen hält ein Skript an, das zu lange braucht; nach zwei Minuten hält es ohnehin an.
- Skript ausführen… öffnet eine beliebige
.lua-Datei, auch außerhalb des Ordners. - Während das Skript läuft, bleibt die Partitur frei; änderst du sie aber in der Zwischenzeit, werden die Änderungen des Skripts nicht übernommen (die Konsole sagt es), damit nichts verloren geht, was du geschrieben hast.
Auf dem iPad führt die Taste Skripte des Notenpults die Skripte im Ordner Script von Maestro aus (in der App Dateien: Auf meinem iPad → Maestro → Script), etwa um ein Stück beim Lesen zu transponieren; die Änderungen gelten nur zum Lesen, die Datei ändert sich nicht.
Die Skriptkonsole
Skripte → Skriptkonsole zeigt, was Skripte mit print schreiben, die Meldungen von Maestro und die Fehler mit Datei und Zeile („Akzente.lua:12: Ungültiger Notenwert…“). Nach einem Fehler öffnet sie sich von selbst.
In der Zeile unten kannst du Lua für die vordere Partitur schreiben und Eingabe drücken: Ein Ausdruck zeigt seinen Wert (score.title, #score.staves), eine Anweisung wird ausgeführt (score.title = "Sonate"), mit einem Rückgängig-Schritt. Die Pfeile ↑ und ↓ holen frühere Zeilen zurück.
Ein Skript schreiben
-- Ein Akzent auf der ersten Note jedes Takts der Flöte,
-- und die Noten über C6 eine Oktave tiefer
local floete = score.staves:find("Flöte")
for _, takt in ipairs(floete.measures) do
local erste = takt:layer(1)[1]
if erste and erste.isNote then erste:add("accent") end
end
for e in floete:entries() do
for _, note in ipairs(e.notes) do
if note.midi > 84 then note:transpose("-P8") end
end
end
- Zahlen beginnen bei 1, wie in Lua und wie die Taktnummern:
score.staves[1],takt:layer(1). - Eigenschaften liest und schreibt man mit Punkt (
e.duration = "8"), Methoden ruft man mit Doppelpunkt auf (e:add("accent")). - Listen (
score.staves,notenzeile.measures,takt:layer(1),e.notes,takt.dynamics…) zählt man mit#und durchläuft sie mitipairs; die Iteratorenscore:entries(),notenzeile:entries(),takt:entries()liefern alle Noten, Akkorde und Pausen.
| Objekt | Was es ist | Einige Eigenschaften und Methoden |
|---|---|---|
score |
die Partitur | title, composer, tempo, staves, measures, pageFormat; insertMeasures, deleteMeasures, setKey, setTime, transpose, addStaff |
| Notenzeile | score.staves[n] |
name, abbreviatedName, clef, measures; setClef, move, delete |
| Takt | notenzeile.measures[n] |
number, clef, dynamics, hairpins, texts, chords; layer(n), append, clear, fillWithRests |
| Takt der Partitur | score.measures[n] |
time, key; setTime, setKey |
| Note, Akkord oder Pause | takt:layer(1)[n] |
duration, isRest, isChord, tick, tied, articulations, notes; add, remove, transpose, setNotes, delete |
| Tonhöhe | e.notes[n] |
name, step, alter, octave, midi; transpose, delete |
| Zeichen | takt.dynamics[n], score.slurs[n]… |
die Felder des Zeichens (marking, tick…); delete; in den Listen add({ … }) |
Notenwerte schreibt man "w", "h", "q", "8", "16", "32" mit Punkten ("q."); Tonhöhen "C4" (eingestrichenes C), "F#5", "Bb3" (B ist "Bb", H ist "B"); Intervalle "P8", "-m3", "M2", "A4"; Artikulationen "accent", "staccato", "tenuto", "fermata"….
Neben den Eigenschaften der Tabelle erreicht jedes Objekt jedes Feld der Partitur unter dem Namen, den es in der .maestro-Datei hat: e.hidden = true, e:set("pitches.1.notehead", "diamond"), notenzeile:raw() (das ganze Objekt als Tabelle) und :replace(tabelle), :fields(). Ein falscher Name gibt einen Fehler, statt still durchzugehen. Die vollständige Liste liefert das Skript selbst: maestro.schema() und maestro.schema("entry").
Die Partitur durchlaufen
Die Funktionen eachentry, eachcell, eachstaff und each (dieselben wie in RGP Lua) durchlaufen einen Bereich: ohne Argumente die Auswahl oder, wenn es keine gibt, die ganze Partitur.
-- Ein Staccato auf jeder Achtelnote der Auswahl (oder der Partitur)
for e in eachentry() do
if e.isNote and e.duration == "8" then e:add("staccato") end
end
| Funktion | Was sie liefert |
|---|---|
eachentry(bereich, ebene) |
Noten, Akkorde und Pausen, Notenzeile für Notenzeile und Takt für Takt; ebene von 1 bis 4 (standardmäßig alle) |
eachcell(bereich) |
jeden Takt jeder Notenzeile: for takt, notenzeile in eachcell() do … end |
eachstaff(bereich) |
die Notenzeilen |
each(liste), eachbackwards(liste) |
die Elemente einer Liste oder die Noten eines Akkords (each(e)); rückwärts kann man beim Durchlaufen löschen |
pairsbykeys(tabelle) |
die Paare einer Tabelle nach Schlüsseln sortiert |
Der Bereich kann selection sein (nur die Auswahl: nichts, wenn es keine gibt), score, eine Notenzeile, ein Takt (score.staves[1].measures[4] oder score.measures[4]) oder eine Tabelle wie { staves = {1, 2}, measures = {5, 8} }: eine Zahl oder die erste und die letzte; was fehlt, bedeutet alle. Es gibt auch eachentrysaved, für Umsteiger von Finale, aber es ist dasselbe wie eachentry: In Maestro gelten Änderungen sofort.
Die Befehle des Editors
editor.perform tut, was du von Hand mit den Paletten Artikulationen, Ausdrücke, Linien, N-tolen, Wiederholungen und Layout tun würdest: Bögen, Gabeln, Oktavlinien, Texte, Volten, Systemumbrüche… Der Befehl wirkt auf einen Bereich, wie eine Auswahl: standardmäßig die Auswahl, sonst ein Eintrag, eine Liste von Einträgen, ein Takt, ein System oder { staves = …, measures = … }. Die Fragen des Dialogs beantworten die Optionen.
editor.perform("slur", { staves = 1, measures = {1, 2} }) -- Bogen von der ersten bis zur letzten Note
editor.perform("crescendo", score.staves[1].measures[5])
editor.perform("ending", { measures = 8, numbers = {1} }) -- erste Volta
editor.perform("tuplet", e, { ratio = "5:4" }) -- Quintole ab dem Eintrag e
editor.perform("text", takt, { text = "dolce", kind = "expression" })
editor.perform("systemBreak", { measures = 9 }) -- neues System ab Takt 9
editor.commands() listet die Befehle mit ihren Optionen. Artikulationen werden gesetzt oder entfernt, wie in der Palette. Lehnt der Editor den Befehl ab („Wähle mindestens zwei Noten…“), hält das Skript mit einem Fehler in dieser Zeile an; auch diese Befehle bleiben im einzigen Rückgängig-Schritt des Skripts. Jeder Befehl setzt die Partitur neu: in langen Schleifen besser ein Befehl auf einen großen Bereich als einer pro Note.
Name, Tastenkürzel und Parameter
Ein optionaler Metadatenblock am Anfang des Skripts gibt den Namen im Menü (in mehreren Sprachen), eine Beschreibung, ein Tastenkürzel und die Parameter, die vor dem Start abgefragt werden:
--[[ maestro
name = { de = "Transponieren", en = "Transpose" }
description = { de = "Transponiert die Auswahl um das gewählte Intervall." }
shortcut = "mod+alt+shift+Y" -- mod = ⌘ auf dem Mac, Strg unter Windows; oder "F9"
needsSelection = true
params = {
{ name = "interval", type = "text", default = "M2", label = { de = "Intervall" } },
{ name = "mal", type = "number", default = 1, min = 1, max = 4 },
}
]]
for e in selection.entries() do e:transpose(params.interval) end
Maestro zeigt ein Formular mit den Parametern (beim nächsten Mal schlägt es die vorigen Antworten vor), und das Skript findet sie in params. Die Typen sind number, text, boolean und choice (mit choices = { … }). Ein Tastenkürzel, das Maestro schon benutzt, wird nicht vergeben. require("name") lädt Module aus dem Ordner lib des Skriptordners.
Für einen ganzen Ordner
Skripte → Auf einen Ordner anwenden… wendet ein Skript auf alle Partituren eines Ordners an (auch in Unterordnern): Maestro, Finale, MusicXML, MuseScore und MIDI. Im Formular wählst du die Parameter des Skripts und das Ergebnis: nur den Bericht oder die Dateien in einem anderen Ordner gespeichert, als Maestro-Dokumente, MusicXML, komprimiertes MusicXML, Finale (.musx) oder MIDI. Die Originaldateien ändern sich nie. Die Konsole zeigt das Ergebnis Datei für Datei.
Im Terminal: maestro-script
Der Befehl maestro-script macht dasselbe ohne Fenster, für die Stapelverarbeitung und für andere Programme:
maestro-script run Akzente.lua Partituren/ --recursive --out Konvertiert/ --format musicxml
maestro-script eval 'score.title' Partituren/
maestro-script run Transponieren.lua stueck.musx -p interval=-M2 --out Ausgabe/ --json
maestro-script help
Auf dem Mac steckt er in Maestro: Skripte → Befehl maestro-script installieren… macht ihn im Terminal verfügbar. Unter Windows ist es maestro-script.exe im Ordner von Maestro (Skripte → Der Befehl maestro-script… sagt, wo). In der Version aus dem Mac App Store gibt es ihn nicht.
Mit einem Assistenten (Claude und andere)
maestro-script mcp ist ein MCP-Server: Ein Assistent wie Claude kann deine Partituren öffnen, Skripte schreiben und ausführen, die Seiten ansehen und das Ergebnis speichern. Er liest und schreibt nur in den Ordnern, die du mit --root angibst; er überschreibt nie das Original und ohne --allow-overwrite keine vorhandene Datei.
Mit Claude Code auf dem Mac:
claude mcp add maestro -- /Applications/Maestro.app/Contents/MacOS/maestro-script mcp --root ~/Partituren
Mit Claude Desktop in der Konfigurationsdatei der MCP-Server:
{
"mcpServers": {
"maestro": {
"command": "/Applications/Maestro.app/Contents/MacOS/maestro-script",
"args": ["mcp", "--root", "/Users/ich/Partituren"]
}
}
}
Unter Windows lautet der Befehl %LOCALAPPDATA%\Programs\Maestro\maestro-script.exe. Dann genügt eine Bitte: „Öffne Menuett.musx, setze einen Akzent auf jede Zählzeit eins der Flöte, zeig mir die erste Seite und speichere es als Menuett-Akzente.maestro.“
An den in Maestro offenen Partituren
Ein Assistent kann auch an den in der App offenen Partituren arbeiten, während du sie ansiehst. Das ist standardmäßig aus: Schalte es mit Skripte → Assistenten die Bearbeitung offener Partituren erlauben ein und verbinde den Assistenten mit maestro-script mcp --app; mit Claude Code auf dem Mac:
claude mcp add maestro-app -- /Applications/Maestro.app/Contents/MacOS/maestro-script mcp --app
Der Assistent sieht die offenen Partituren und deine Auswahl; jedes seiner Skripte ist ein Rückgängig-Schritt namens „Assistent“ und erscheint in der Skriptkonsole; die Dateien sicherst du. Die Verbindung bleibt auf deinem Computer und bei deinem Benutzer, ohne Netzwerk.
Skripte mit einem Editor schreiben
Der Skriptordner enthält auch die Beschreibung der Schnittstelle für Editoren (.maestro/maestro.d.lua und .luarc.json): Öffne ihn in Visual Studio Code mit der Lua-Erweiterung, dann hast du Autovervollständigung für Eigenschaften und Methoden und Prüfungen beim Schreiben.
Die Originaldaten von Finale
Eine aus einer Finale-Datei (.musx oder .mus) geöffnete Partitur behält, solange sie offen ist, die Originaldaten der Datei: Skripte lesen sie über finale, mit den Namen des EnigmaXML (dieselben wie in musxdom und im PDK von Finale). Damit holst du zurück, was der Import nicht nach Maestro bringt, oder vergleichst.
local ms = finale.others("measSpec", 1)[1] -- Takt 1, wie Finale ihn schreibt
print(ms.beats, ms.divbeat, ms.width)
for e in score:entries() do
local n = finale.entnum(e) -- der Finale-Eintrag, aus dem er stammt
if n then
local original = finale.entry(n)
-- … etwa etwas übernehmen, das der Import nicht liest
end
end
finale.others(tag, cmper, inci),finale.details(tag, cmper1, cmper2, inci),finale.texts(tag, nummer): die Datensätze (die Argumente nach dem Namen sind optionale Filter);finale.entry(entnum),finale.header(),finale.options(tag),finale.tags()(die vorhandenen Namen).- Ein Datensatz ist eine Tabelle:
_tagist der Name, Attribute und Unterelemente sind Felder; ein leeres Element isttrue, ein wiederholtes eine Folge (finale.list(x)liefert immer eine Folge). finale.entryFor(entnum)undfinale.entnum(eintrag),finale.staffFor(cmper)undfinale.staffCmper(notenzeile)verbinden die Objekte von Finale und Maestro.- Nur lesbar und nicht gespeichert: Nach Sichern als
.maestround erneutem Öffnen gibt esfinalenicht mehr. Bei.mus-Dateien enthält es die Datensätze, die Maestro lesen kann.
Für Umsteiger von RGP Lua
Die Sprache ist dieselbe, die Objekte sind die von Maestro:
| RGP Lua (Finale) | Maestro |
|---|---|
plugindef() |
der Block --[[ maestro … ]] am Anfang des Skripts |
finenv.Region() |
selection; die Funktionen zum Durchlaufen nehmen ohne Bereich die Auswahl oder die ganze Partitur |
for e in eachentrysaved(finenv.Region()) |
for e in eachentry() (auch eachentrysaved, eachcell, eachstaff, each, pairsbykeys) |
finale.FCStaves, FCStaff |
score.staves, score.staves[n] |
finale.FCMeasures, FCMeasure |
score.measures[n] (Takt- und Tonartangabe), notenzeile.measures[n] (die Musik) |
FCNoteEntryLayer, FCNoteEntry |
takt:layer(n), takt:layer(n)[i] |
FCNote |
e.notes[i] |
entry.Duration (EDU, Viertel 1024) |
e.duration ("q") oder e.ticks (Viertel 960) |
FCExpression, FCSmartShape |
takt.dynamics, takt.texts, score.slurs, takt.hairpins… |
entry:Save(), measure:Save() |
nicht nötig: Änderungen gelten sofort (und werden gemeinsam rückgängig gemacht) |