第10.5章
スクリプト
スクリプトを使えば、手作業なら何時間もかかることを一度で行えます。すべての強拍へのアクセント、重複した強弱記号の削除、譜表の移調、百の楽譜の楽器名の変更、音域のチェックなどです。スクリプトは Lua 5.4 で書きます。Finale のプラグイン JW Lua や RGP Lua と同じ言語です。
スクリプトは楽譜全体(譜表、小節、音符、記号、テキスト、レイアウト)を参照し、変更できます。スクリプトはコピーに対して動作します。最後まで実行されると、すべての変更が 1 回の「取り消す」 にまとまります。エラーで止まった場合、楽譜は元のままです。スクリプトは閉じた環境で動作し、ファイルの読み書き、ネットワークの使用、プログラムの起動はできません。
スクリプトメニュー
スクリプトフォルダにある .lua ファイルが、それぞれ スクリプト メニューの項目になり、サブフォルダはサブメニューになります。初回には Maestro がいくつかのサンプルを サンプル フォルダにコピーします。テキストエディタで開くと、作り方がわかります。その一部は、Finale 用 RGP Lua のスクリプト集でよく使われるもの(和音の1音だけ残す、オクターブで重ねる、2つのレイヤーを入れ替える、休符のアーティキュレーションを削除する、音価を数える)を作り直したものです。
- スクリプト → スクリプトフォルダを開く で Finder またはエクスプローラーに表示します(Mac では
~/Library/Application Support/Maestro/Script、Windows では%LOCALAPPDATA%\Maestro\Script)。新しいファイルや変更したファイルは、再起動しなくてもすぐに使えます。 - スクリプトは前面の楽譜に対して動作します。スクリプトが選択範囲を使う場合は選択範囲に、そうでなければ全体に対して動作します。
- 再実行(⌥⌘U、Windows では Ctrl+Alt+U)は、最後のスクリプトを同じパラメータで繰り返します。
- スクリプトを中断 は時間がかかりすぎるスクリプトを止めます。2 分経つと自動的に止まります。
- スクリプトを実行… は、フォルダの外にあるものも含め、任意の
.luaファイルを開いて実行します。 - スクリプトの実行中も楽譜は操作できます。ただし途中で楽譜を変更した場合、スクリプトの変更は適用されません(コンソールに表示されます)。書いた内容が失われることはありません。
iPad では、譜面台の スクリプト ボタンで Maestro の Script フォルダ(ファイル App:このiPad内 → Maestro → Script)にあるスクリプトを実行できます。たとえば読みながら曲を移調できます。変更は表示用で、ファイルは変更されません。
スクリプトコンソール
スクリプト → スクリプトコンソール には、スクリプトが print で書いた内容、Maestro のメッセージ、ファイル名と行番号付きのエラー(「Accents.lua:12: 無効な音価です…」)が表示されます。エラーが起きると自動的に開きます。
下の行に前面の楽譜に対する Lua を書いて Enter を押すと、式ならその値が表示され(score.title、#score.staves)、文なら実行されます(score.title = "ソナタ")。変更は 1 回の「取り消す」にまとまります。↑ と ↓ で以前の行を呼び出せます。
スクリプトを書く
-- フルートの各小節の最初の音にアクセントを付け、
-- C6 より高い音を 1 オクターブ下げる
local flute = score.staves:find("フルート")
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
- 番号は Lua や小節番号と同じく 1 から始まります:
score.staves[1]、measure:layer(1)。 - プロパティはドットで読み書きし(
e.duration = "8")、メソッドはコロンで呼び出します(e:add("accent"))。 - リスト(
score.staves、staff.measures、measure:layer(1)、e.notes、measure.dynamicsなど)は#で数え、ipairsでたどります。イテレータscore:entries()、staff:entries()、measure:entries()はすべての音符、和音、休符を返します。
| オブジェクト | 内容 | 主なプロパティとメソッド |
|---|---|---|
score |
楽譜 | title、composer、tempo、staves、measures、pageFormat;insertMeasures、deleteMeasures、setKey、setTime、transpose、addStaff |
| 譜表 | score.staves[n] |
name、abbreviatedName、clef、measures;setClef、move、delete |
| 小節 | staff.measures[n] |
number、clef、dynamics、hairpins、texts、chords;layer(n)、append、clear、fillWithRests |
| 楽譜全体の小節 | score.measures[n] |
time、key;setTime、setKey |
| 音符・和音・休符 | measure:layer(1)[n] |
duration、isRest、isChord、tick、tied、articulations、notes;add、remove、transpose、setNotes、delete |
| 音高 | e.notes[n] |
name、step、alter、octave、midi;transpose、delete |
| 記号 | measure.dynamics[n]、score.slurs[n] など |
記号のフィールド(marking、tick など);delete;リストでは add({ … }) |
音価は "w"、"h"、"q"、"8"、"16"、"32" と書き、付点は "q." のように付けます。音高は "C4"(中央のド)、"F#5"、"Bb3"、音程は "P8"、"-m3"、"M2"、"A4"、アーティキュレーションは "accent"、"staccato"、"tenuto"、"fermata" などです。
表のプロパティのほかに、すべてのオブジェクトから .maestro ファイルでの名前を使って楽譜のすべてのフィールドにアクセスできます:e.hidden = true、e:set("pitches.1.notehead", "diamond")、staff:raw()(オブジェクト全体をテーブルとして取得)と :replace(テーブル)、:fields()。名前を間違えると、黙って無視されずにエラーになります。完全な一覧はスクリプト自身から取得できます:maestro.schema()、maestro.schema("entry")。
楽譜をたどる
関数 eachentry、eachcell、eachstaff、each(RGP Lua と同じもの)は範囲をたどります。引数がなければ選択範囲を、選択がなければ楽譜全体をたどります。
-- 選択範囲(または楽譜全体)のすべての八分音符にスタッカート
for e in eachentry() do
if e.isNote and e.duration == "8" then e:add("staccato") end
end
| 関数 | 返すもの |
|---|---|
eachentry(範囲, レイヤー) |
音符、和音、休符を譜表ごと、小節ごとに。レイヤー は 1〜4(標準ではすべて) |
eachcell(範囲) |
各譜表の各小節:for measure, staff in eachcell() do … end |
eachstaff(範囲) |
譜表 |
each(リスト)、eachbackwards(リスト) |
リストの要素、または和音の音(each(e))。後ろからたどれば、たどりながら削除できます |
pairsbykeys(テーブル) |
テーブルのペアをキーの順に |
範囲には selection(選択範囲のみ。選択がなければ何もなし)、score、譜表、小節(score.staves[1].measures[4] または score.measures[4])、または { staves = {1, 2}, measures = {5, 8} } のようなテーブルを指定できます。数を1つ、または最初と最後を書き、書かなかったものはすべてになります。Finale から移る方のために eachentrysaved もありますが、eachentry と同じです。Maestro では変更がすぐに反映されます。
エディタのコマンド
editor.perform は、アーティキュレーション、表情記号、ライン、連符、反復、レイアウトの各パレットで手作業で行うこと(スラー、ヘアピン、オクターブ線、テキスト、括弧、段の区切りなど)を実行します。対象は選択範囲と同じように範囲で指定します。省略すると選択範囲で、エントリ、エントリのリスト、小節、譜表、{ staves = …, measures = … } も使えます。コマンドのダイアログで聞かれることは、オプションで答えます。
editor.perform("slur", { staves = 1, measures = {1, 2} }) -- 最初の音から最後の音までスラー
editor.perform("crescendo", score.staves[1].measures[5])
editor.perform("ending", { measures = 8, numbers = {1} }) -- 1 番括弧
editor.perform("tuplet", e, { ratio = "5:4" }) -- エントリ e から 5 連符
editor.perform("text", measure, { text = "dolce", kind = "expression" })
editor.perform("systemBreak", { measures = 9 }) -- 9 小節目から新しい段
editor.commands() はコマンドとそのオプションの一覧を返します。アーティキュレーションはパレットと同じく、付けたり外したりします。エディタがコマンドを受け付けないとき(「同じ譜表の音符を 2 つ以上選択してください…」など)、スクリプトはその行でエラーになって止まります。これらのコマンドもスクリプト全体の 1 回の「取り消し」に含まれます。コマンドのたびに楽譜がレイアウトし直されるので、長いループでは音符ごとではなく大きな範囲に 1 回実行する方が速くなります。
名前、ショートカット、パラメータ
スクリプトの先頭に置く省略可能なメタデータのブロックで、メニューに表示する名前(複数の言語で)、説明、ショートカット、実行前に尋ねるパラメータを指定できます。
--[[ maestro
name = { ja = "移調", en = "Transpose" }
description = { ja = "選択範囲を指定の音程だけ移調します。" }
shortcut = "mod+alt+shift+Y" -- mod は Mac では ⌘、Windows では Ctrl;または "F9"
needsSelection = true
params = {
{ name = "interval", type = "text", default = "M2", label = { ja = "音程" } },
{ name = "times", type = "number", default = 1, min = 1, max = 4 },
}
]]
for e in selection.entries() do e:transpose(params.interval) end
Maestro はパラメータの入力フォームを表示し(次回は前回の回答を提案します)、スクリプトは params で値を受け取ります。型は number、text、boolean、choice(choices = { … } 付き)です。Maestro がすでに使っているショートカットは割り当てられません。require("名前") はスクリプトフォルダ内の lib フォルダからモジュールを読み込みます。
フォルダ全体に実行
スクリプト → フォルダに実行… は、フォルダ内(サブフォルダも含む)のすべての楽譜(Maestro、Finale、MusicXML、MuseScore、MIDI)にスクリプトを実行します。フォームでスクリプトのパラメータと結果を選びます。報告のみにするか、別のフォルダに Maestro 書類、MusicXML、圧縮 MusicXML、Finale(.musx)、MIDI として保存できます。元のファイルが変更されることはありません。 コンソールにファイルごとの結果が表示されます。
ターミナルから:maestro-script
maestro-script コマンドは、ウィンドウなしで同じことを行います。一括処理やほかのプログラムからの利用に向いています。
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
Mac では Maestro の中にあります。スクリプト → maestro-script コマンドをインストール… でターミナルから使えるようになります。Windows では Maestro のフォルダにある maestro-script.exe です(場所は スクリプト → maestro-script コマンド… で確認できます)。Mac App Store 版には含まれていません。
アシスタント(Claude など)から
maestro-script mcp は MCP サーバーです。Claude などのアシスタントが、楽譜を開き、スクリプトを書いて実行し、ページを見て、結果を保存できます。読み書きできるのは --root で指定したフォルダの中だけで、元のファイルは決して上書きせず、--allow-overwrite がなければ既存のファイルも上書きしません。
Mac の Claude Code では:
claude mcp add maestro -- /Applications/Maestro.app/Contents/MacOS/maestro-script mcp --root ~/Scores
Claude Desktop では、MCP サーバーの設定ファイルに次のように書きます:
{
"mcpServers": {
"maestro": {
"command": "/Applications/Maestro.app/Contents/MacOS/maestro-script",
"args": ["mcp", "--root", "/Users/me/Scores"]
}
}
}
Windows ではコマンドは %LOCALAPPDATA%\Programs\Maestro\maestro-script.exe です。あとは「Minuet.musx を開いて、フルートの各強拍にアクセントを付け、最初のページを見せてから Minuet-accents.maestro として保存して」のように頼むだけです。
Maestro で開いている楽譜に対して
アシスタントは、アプリで開いている楽譜を見ながら作業することもできます。この機能は標準ではオフです。スクリプト → アシスタントに開いている楽譜の編集を許可 でオンにし、maestro-script mcp --app でアシスタントを接続します。Mac の Claude Code では:
claude mcp add maestro-app -- /Applications/Maestro.app/Contents/MacOS/maestro-script mcp --app
アシスタントは開いている楽譜とあなたの選択範囲を参照できます。スクリプトごとの変更は「アシスタント」という名前の 1 回の「取り消す」になり、スクリプトコンソールに表示されます。ファイルの保存はご自身で行います。接続はあなたのコンピュータとユーザーの中だけで行われ、ネットワークは使いません。
エディタでスクリプトを書く
スクリプトフォルダには、エディタ用のインターフェースの説明(.maestro/maestro.d.lua と .luarc.json)も入っています。Visual Studio Code に Lua 拡張機能を入れてこのフォルダを開くと、プロパティやメソッドの補完と、入力中のチェックが使えます。
Finale の元のデータ
Finale のファイル(.musx または .mus)から開いた楽譜は、開いている間、ファイルの元のデータを保持しています。スクリプトは finale を通じて、EnigmaXML の名前(musxdom や Finale の PDK と同じ)でそれを読み取れます。読み込み時に Maestro に取り込まれなかった情報を取り戻したり、比較したりするのに使えます。
local ms = finale.others("measSpec", 1)[1] -- Finale が記録している小節 1
print(ms.beats, ms.divbeat, ms.width)
for e in score:entries() do
local n = finale.entnum(e) -- 元になった Finale のエントリ
if n then
local original = finale.entry(n)
-- … 例:読み込みで取り込まれなかった情報を反映する
end
end
finale.others(tag, cmper, inci)、finale.details(tag, cmper1, cmper2, inci)、finale.texts(tag, number):レコードを返します(名前の後の引数は省略可能な絞り込み)。finale.entry(entnum)、finale.header()、finale.options(tag)、finale.tags()(含まれている名前の一覧)。- レコードはテーブルです。
_tagが名前で、属性と子要素がフィールドになります。空の要素はtrue、繰り返される要素はシーケンスになります(finale.list(x)は常にシーケンスを返します)。 finale.entryFor(entnum)とfinale.entnum(entry)、finale.staffFor(cmper)とfinale.staffCmper(staff)で、Finale と Maestro のオブジェクトを対応づけられます。- 読み取り専用で、保存はされません。
.maestroとして 保存 して開き直すとfinaleはなくなります。.musの場合は、Maestro が読み取れるレコードが含まれます。
RGP Lua から移る方へ
言語は同じで、オブジェクトは Maestro のものです。
| RGP Lua(Finale) | Maestro |
|---|---|
plugindef() |
スクリプト先頭の --[[ maestro … ]] ブロック |
finenv.Region() |
selection。たどる関数に範囲を渡さなければ、選択範囲または楽譜全体 |
for e in eachentrysaved(finenv.Region()) |
for e in eachentry()(eachentrysaved、eachcell、eachstaff、each、pairsbykeys も) |
finale.FCStaves、FCStaff |
score.staves、score.staves[n] |
finale.FCMeasures、FCMeasure |
score.measures[n](拍子と調号)、staff.measures[n](音楽) |
FCNoteEntryLayer、FCNoteEntry |
measure:layer(n)、measure:layer(n)[i] |
FCNote |
e.notes[i] |
entry.Duration(EDU、4 分音符 1024) |
e.duration("q")または e.ticks(4 分音符 960) |
FCExpression、FCSmartShape |
measure.dynamics、measure.texts、score.slurs、measure.hairpins など |
entry:Save()、measure:Save() |
不要:変更はすぐに反映され、まとめて取り消せます |