Show HN: Behringer FCB1010 MIDIペダルボードとmacOSのマクロ機能
Show HN: Macros with a Behringer FCB1010 MIDI Pedalboard in macOS (github.com)
要約
fcbnerdは、MIDIフットコントローラーをmacOSの追加キーボードとして利用できるコマンドラインツールです。MIDIソースに接続し、指定したフットスイッチやペダルのメッセージに応じてシェルコマンドを実行したり、他のプログラムが処理できる形式でMIDIメッセージをストリーム出力したりできます。macOSのサンドボックス制限を回避し、柔軟な自動化を実現します。
全文翻訳
fcbnerd
[画像: マンガ: 開発者がコーヒーを片手に、MIDIペダルボードのフットスイッチを踏みながら、犬が近くで眠っている広いモニターから身を乗り出している。]
MIDIフットコントローラーをMacの追加キーボードとして使用します。fcbnerdはMIDIソースに接続し、フットスイッチやペダルがバインドしたメッセージを送信したときにシェルコマンドを実行するか、またはすべてのメッセージに対して1行に1つのJSONオブジェクトを出力して、別のプログラムがそのストンプの意味を決定できるようにします。
$ fcbnerd -q --bind '1:20:127=open ~/Downloads' --bind 'pc:1:0=say hello'
または、別のプログラムが処理できるようにすべてをストリームします。
$ fcbnerd
{"type":"connected","source":"UM-ONE","time":"2026-09-14T20:01:00.120Z"}
{"type":"pc","channel":1,"program":0,"source":"UM-ONE","time":"2026-09-14T20:01:02.345Z"}
{"type":"cc","channel":1,"controller":30,"value":84,"source":"UM-ONE","time":"2026-09-14T20:01:03.910Z"}
Behringer FCB1010向けに構築されていますが、FCB1010固有のものは何もありません。あらゆるCoreMIDIソースが機能します。
アプリではなくコマンドラインツールである理由
キーを押したりスクリプトを実行したりするなど、Mac上で動作するものは、サンドボックス化されたアプリでは取得できないパーミッションを必要とします。また、ユーザーごとに異なるアクションセットを望むものです。fcbnerdはMIDIを読み取るだけで、パーミッションは一切必要ありません。アクションはシェル、または既にアクセス権を持つツール(HammerspoonやKeyboard Maestroなど)に属します。
インストール
brew trust --tap jamesryanatx/tap # Homebrew 7+ は、サードパーティのタップを信頼するまでロードしません
brew install JamesRyanATX/tap/fcbnerd
またはHomebrewなしでソースから(XcodeまたはSwiftツールチェーン、macOS 13+):
swift build -c release
cp .build/release/fcbnerd /usr/local/bin/
使用法
fcbnerd [listen] [--source NAME] [--format json|text] [--bind BINDING]... [--quiet] [--shell PATH]
fcbnerd list [--format json|text]
fcbnerd simulate
listen(デフォルト)は、すべてのMIDIソースに接続するか、--sourceを含む名前のソースのみに接続し、中断されるまでイベントをストリームします。ホットプラグに対応しています。セットの途中でインターフェースを抜き差ししても、disconnected/connectedの行とともにストリームは継続します。listは、現在利用可能なソースを表示します。simulateは、fcbnerd simulatorという名前の仮想MIDIソースを発行し、合成されたプレス、ペダルスイープ、およびsysexメッセージをループで再生します。これを1つのターミナルで実行し、別のターミナルでfcbnerdを実行して、ペダルなしでコンシューマーを構築します。--format textは、視覚的に確認しやすいように整列された列を表示し、バインドできる各メッセージのbind=パターンを含みます。スクリプトはデフォルトのJSONを使用すべきです。テキストレイアウトは変更される可能性があります。--bindは、メッセージが一致した場合にコマンドを実行します。下記参照。--quietはイベントの表示を停止し、バインドされたコマンドのみを残します。--shell PATHは、バインドされたコマンドを実行するシェルを選択します(デフォルトは/bin/sh)。
ステータスメッセージはstderrに出力され、stdoutはイベントのみを運びます。各行は書き込まれるとすぐにフラッシュされるため、パイプはイベントを即座に確認できます。
コマンドのバインド
まず、ペダルが何を送信するかを調べます。fcbnerd -f textを実行してスイッチを押してください。
$ fcbnerd -f text
16:30:41.115 pc channel=1 program=7 bind=pc:1:7 [USB MIDI Interface]
16:30:41.115 cc channel=1 controller=20 value=127 bind=1:20:127 [USB MIDI Interface]
次に、そのパターンにコマンドをバインドします。
fcbnerd --bind '1:20:127=open ~/Downloads'
バインディングはPATTERN=COMMANDです。最初の=以降はすべてコマンドなので、=や:自体を含めることができます。--bindは何度でも使用できます。一致するすべてのバインディングは、指定された順序で開始され、同時に実行されます。
| パターン | マッチする |
|---|---|
CHANNEL:CONTROLLER:VALUE | コントロールチェンジ、例: 1:20:127。cc:1:20:127も機能します。 |
pc:CHANNEL:PROGRAM | プログラムチェンジ、例: pc:1:7。 |
任意の数値を*にできます: 1:30:*はチャンネル1のコントローラー30のすべての値にマッチし、これはエクスプレッションペダルをバインドする方法です。
コマンドは/bin/sh -c、または--shellで指定したシェルを通じてバックグラウンドで実行されます。標準入力は/dev/nullです。標準出力はfcbnerdのstderrに送られるため、イベントストリームを破損させることはありません。--quietを使用すると、標準出力に送られます。これらの環境変数がコマンドから参照できます。
| 変数 | |
|---|---|
MIDI_TYPE | cc または pc |
MIDI_CHANNEL | 1–16 |
MIDI_CONTROLLER, MIDI_VALUE | cc の場合 |
MIDI_PROGRAM | pc の場合 |
MIDI_SOURCE | MIDIソース名 |
# エクスプレッションペダルで出力ボリュームを設定
fcbnerd -q --bind '1:30:*=osascript -e "set volume output volume $((MIDI_VALUE * 100 / 127))"'
すべてのストンプはコマンドを実行するため、最初の実行が終了していなくても、2回の素早いプレスは2回実行されます。これは、一致するすべてのメッセージがシェルを開始することも意味します。*:*:127やpc:*:*のような広範なパターンは、ノイズの多いデバイスから遠ざけてください。
ペダルスイープは例外です。スイープは1秒間に数十個の値を発信するため、値が*のバインディングでは、各コントロール(チャンネルとコントローラー)につき1つのコマンドコピーのみが同時に実行されます。コマンド実行中、fcbnerdはそのコントロールの最新の値のみを保持し、次に実行する際に使用します。これによりシェル数が抑えられ、最終的にはペダルの最終位置で終了します。コマンドが5秒経過しても実行中の場合、fcbnerdはstderrにその旨を表示します。
ゼロ以外の終了ステータスを返すコマンドは、そのバインディングと終了ステータスがstderrに表示されます。fcbnerdの停止(Ctrl+C、kill、ターミナルのクローズ、またはstdoutのクローズ)は、実行中のコマンド(開始したプロセスを含む)にSIGTERMを送信します。
シェル関数
インタラクティブシェルからの関数やエイリアスは、sh -cではロードされません。bashでは、関数をエクスポートすると可視になります(macOSの/bin/shはbashなので、デフォルトシェルはそれを見ます)。
greet() { say "preset $MIDI_PROGRAM"; }
export -f greet
fcbnerd -q --bind 'pc:1:*=greet'
zshは関数をエクスポートできません。それらをファイルに配置し、zshでソースします: --shell /bin/zsh --bind 'pc:1:*=source ~/.fcbnerd.zsh && greet'。
オン/オフスイッチ
FCB1010はスイッチを離したときに何も送信しないため(FCB1010ノート参照)、バインディングはプレス時のみ発火します。オン/オフ動作(最初のプレスで「オン」、2回目で「オフ」)は、コンシューマーが状態を追跡する必要があります。例えば/tmpのファイルをトグルすることで実現できます。
出力
fcbnerd listenは1行に1つのJSONオブジェクトを出力します。すべてのオブジェクトにはtype、source(MIDIソースの表示名)、およびtime(fcbnerdがメッセージを受信した時刻: ISO 8601、UTC、ミリ秒)が含まれます。チャンネルは1~16です。ノート、コントローラー、プログラム、ベロシティ、プレッシャーの値は、生の0~127のMIDI値です。
type | 追加フィールド | ノート |
|---|---|---|
pc | channel, program | プログラムチェンジ。programはワイヤー上では0ベースです。 |
cc | channel, controller, value | コントロールチェンジ: スイッチとエクスプレッションペダル。 |
note_on | channel, note, velocity | |
note_off | channel, note, velocity | ベロシティ0のノートオンでも発行されます。 |
poly_pressure | channel, note, pressure | |
channel_pressure | channel, pressure | |
pitch_bend | channel, value | 0~16383、中央値8192。 |
sysex | length, data | dataはf0…f7のフレーミングを含む小文字の16進数です。lengthはそれらのバイト数をカウントします。 |
connected | ソースが出現し、リッスンされていることを示します。常にそのソースのイベントの前に来ます。 | |
disconnected | ソースが消滅しました。処理中のメッセージがそれに続く場合があります。 |
システムリアルタイムメッセージ(MIDIクロックなど)およびシステムコモンメッセージ(ソングポジション、MTC)は発行されません。将来のバージョンで新しいイベントタイプやフィールドが追加される可能性があります。既存のものは意味を変えません。コンシューマーは、認識しないタイプやフィールドを無視すべきです。
ストリームを速やかに読み取ってください。コンシューマーが読み取りを停止した場合、fcbnerdはメモリ内にイベントをキューイングし、読み取りが再開されたときにすべて配信します。したがって、遅延したコンシューマーは、古いプレスのバーストに対してアクションを実行することになります。
fcbnerd list --format jsonは異なる形状で、ソースごとに1行を出力します: {"type":"source","name":"UM-ONE","id":-1234567}。idはCoreMIDIの一意IDです。
例
examples/developer.shは、ソフトウェアエンジニア向けの完全なコメント付きセットアップです。最初にDRY_RUN=1を付けて実行すると、各スイッチが何をするかを確認できます。
| コントロール | アクション |
|---|---|
| スイッチ1 | ホームフォルダをFinderで開く |
| スイッチ2 | メールを開く |
| スイッチ3 | iTermを開く |
| スイッチ4 | Chromeのプロファイルピッカーを開始点とする新しいウィンドウを開く |
| スイッチ5 | Claudeを開く |
| スイッチ6 | マイクのミュートまたはミュート解除 |
| スイッチ7 | クリップボードに領域またはウィンドウのスクリーンショットを撮る |
| スイッチ8 | アクティブなChromeタブを閉じる |
| スイッチ9 | アクティブなアプリケーションを終了する |
| スイッチ10 | スクリーンをロックする |
| エクスプレッションペダルA | 出力ボリューム |
| エクスプレッションペダルB | SpotifyまたはMusicのボリューム |
シェルとjq
プログラム0は次のスペースに切り替え、プログラム1は前のスペースに切り替えます。これには複数のスペースが必要で、システム設定→キーボード→キーボードショートカット→ミッションコントロールで「スペースを左右に移動」ショートカットが有効になっていること(デフォルト)、およびターミナルアプリがシステムイベントを制御するためのアクセシビリティパーミッションと自動化パーミッションを持っていることが必要です。macOSは、自動化パーミッションを最初に要求します。
fcbnerd | jq --unbuffered -r 'select(.type == "pc") | .program' |
while read -r program; do
case "$program" in
0) osascript -e 'tell application "System Events" to key code 124 using control down' ;;
1) osascript -e 'tell application "System Events" to key code 123 using control down' ;;
esac
done
Hammerspoon
プログラム0は再生/一時停止を切り替え、CC 30のエクスプレッションペダルが出力ボリュームを設定します。出力は部分的なチャンクで到着する可能性があるため、改行までバッファリングします。パスはApple Silicon用です。Intel上のHomebrewは/usr/local/binにインストールします。
local buffer = ""
fcbnerd = hs.task.new("/opt/homebrew/bin/fcbnerd", nil, function(_, stdout, _)
buffer = buffer .. stdout
for line in buffer:gmatch("([^
]*)\n") do
local event = hs.json.decode(line)
if event and event.type == "pc" and event.program == 0 then
hs.eventtap.event.newSystemKeyEvent("PLAY", true):post()
hs.eventtap.event.newSystemKeyEvent("PLAY", false):post()
elseif event and event.type == "cc" and event.controller == 30 then
hs.audiodevice.defaultOutputDevice():setVolume(event.value / 127 * 100)
end
end
buffer = buffer:match("[^
]*$")
return true
end)
fcbnerd:start()
FCB1010ノート
コンシューマーが処理する必要があるペダルに関する事項:
- スイッチのプレスは1つのメッセージを送信し、離しても何も送信しません。オン/オフ動作(最初のプレスで「オン」、2回目で「オフ」)は、コンシューマーが追跡する必要があります。
- 工場出荷時のプリセットでは、アクティブなプリセットによって同じスイッチから異なるCC番号が送信されます。使用する予定の各スイッチを押して、送信されるものをメモしてください。
- スイッチを押すと、そのプリセットのエクスプレッションペダルの値も再送信されるため、ペダルのコントローラーへのすべての
ccがペダルが動いたことを意味するわけではありません。 - エクスプレッションペダルは、完全な0~127の範囲に達しません。トラベルの一部は何も送信せず、スイープは約3分の2の値範囲をカバーするため、実際に表示される範囲にスケーリングしてください。
- ペダルは5ピンDIN MIDIのみです。USB MIDIインターフェースが必要で、これは
source名として表示されます。
開発
swift build
swift test # デコーダー、フォーマッター、バインディングのテスト
.build/debug/fcbnerd simulate & # フェイクペダル
.build/debug/fcbnerd --format text # 監視
Sources/FCBNerdCoreはCoreMIDIのUniversal MIDI Packetsをデコードし、出力をフォーマットし、バインディングを解析します。CoreMIDIへの依存関係がないため、ハードウェアなしでテストを実行できます。Sources/fcbnerdはCLIです。CoreMIDI接続、ホットプラグ、およびシミュレーターが含まれます。
リリースするには、Sources/fcbnerd/main.swiftのversionをインクリメントし、コミットし、対応するタグをプッシュします。
git tag -a v1.2.3 -m "fcbnerd 1.2.3" && git push origin v1.2.3
リリースワークフローは、テストを実行し、ユニバーサルバイナリを含むGitHubリリースを発行し、JamesRyanATX/homebrew-tapのフォーミュラを更新します。
ライセンス
MIT