プログラミング
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フットコントローラーを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を含む名前のソースのみに接続し、中断されるまでイベントをストリームします。ホットプラグに対応しています。セットの途中でインターフェースを抜いて再度接続しても、ストリームは切断/接続の行で継続します。
list
現在利用可能なソースを表示します。
simulate
仮想MIDIソース named fcbnerd simulator を公開し、合成されたプレス、ペダルスイープ、および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の注記を参照)、バインディングはプレス時のみ発火します。オン/オフ動作のためには、コマンド内で状態を維持します。例えば、/tmpのファイルをトグルするなどです。
出力
fcbnerd listen は、1行あたり1つのJSONオブジェクトを出力します。各オブジェクトには、type、source(MIDIソースの表示名)、time(fcbnerdがメッセージを受信した時間:ISO 8601、UTC、ミリ秒)が含まれます。チャネルは1〜16です。note、controller、program、velocity、pressureの値は、生の0〜127のMIDI値です。
タイプ
追加フィールド
注記
pc
channel, program
プログラムチェンジ。programはワイヤー上で0ベースです。
cc
channel, controller, value
コントロールチェンジ:スイッチとエクスプレッションペダル。
note_on
channel, note, velocity
note_off
channel, note, velocity
velocityが0のnote-onでも送信されます。
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 は、ソフトウェアエンジニア向けの完全なコメント付きセットアップです。テストの実行、CIの待機、ブランチの同期、マイクのミュートなどに10個のスイッチがあり、さらに出力音量のエクスプレッションペダルもあります。最初にDRY_RUN=1で実行して、各スイッチが何をするかを確認してください。
シェルと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