JamesRyanATX/fcbnerd: Do things with a Behringer FCB1010 MIDI pedalboard in Mac OS · GitHub


Use a MIDI foot controller as an additional keyboard for yourMac fcbnerd
links to your MIDI sources and either runs a shell command when a.
footswitch or pedal sends out a message you have actually bound, or prints one JSON item.
per line for every single message so another program can choose what a stomp.
methods.

$  fcbnerd -q-- bind ' 1:20:127= open ~/Downloads'-- bind ' pc:1:0= state hey there'

Or stream whatever for another program to manage:

$  fcbnerd
 {"type":" linked"," source":" UM-ONE"," time":" 2026-09-14T20:01:00.120 Z"} 
 {"type":" pc"," channel":1," program":0," source":" UM-ONE"," time":" 2026-09-14T20:01:02.345 Z"} 
 {"type":" cc"," channel":1," controller":30," worth":84," source":" UM-ONE"," time":" 2026-09-14T20:01:03.910 Z"} 

Built for the Behringer FCB1010, however absolutely nothing in it is FCB1010-specific: any.
CoreMIDI source works.

Why a command-line tool rather of an app

Anything that acts upon your Mac, like pushing secrets or running scripts, requirements.
authorizations that sandboxed apps can’t get, and every user desires a various.
set of actions anyhow. fcbnerd just checks out MIDI, which requires no authorizations.
The actions come from your shell, or to a tool that currently has the gain access to,.
such as Hammerspoon or Keyboard Maestro.

 brew trust-- tap jamesryanatx/tap  # Homebrew 7+ will not pack third-party taps up until you trust them
brew set up JamesRyanATX/tap/fcbnerd

Or from source without Homebrew (Xcode or the Swift toolchain, macOS 13+):

 speedy construct -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 replicate.
  • listen (default) links to every MIDI source, or just those whose.
    name includes -- source, and streams occasions up until interrupted. It follows.
    hotplug: disconnect the user interface mid-set and plug it back in, and the stream.
    continues with detached/ linked lines.
  • list prints the sources readily available today.
  • replicate releases a virtual MIDI source called fcbnerd simulator
    that plays artificial presses, a pedal sweep and a sysex message on a loop.
    Run it in one terminal and fcbnerd in another to construct a customer without any.
    pedal connected.
  • -- format text prints lined up columns for eyeballing, consisting of a.
    bind = pattern for each message you can bind. Scripts ought to utilize the.
    default JSON; the text design might alter.
  • -- bind runs a command when a message matches; see listed below.
  • -- peaceful stops printing occasions, leaving just the bound commands.
  • -- shell course selects the shell that runs bound commands (default.
    / bin/sh).

Status messages go to stderr; stdout brings just occasions. Each line is.
flushed as quickly as it’s composed, so pipelines see occasions instantly.

First discover what your pedal sends out. Run fcbnerd -f text and push the.
switch:

$  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 worth= 127 bind= 1:20:127 [USB MIDI Interface]

Then bind a command to that pattern:

 fcbnerd-- bind ' 1:20:127= open ~/Downloads'

A binding is PATTERN= COMMAND Everything after the very first = is the command,.
so it can consist of = and : itself. Use -- bind as lot of times as you like.
Every binding that matches a message begins, in the order offered, and they run.
at the exact same time.

Pattern Matches
CHANNEL: CONTROLLER: WORTH Control modification, e.g. 1:20:127 cc:1:20:127 likewise works.
pc: CHANNEL: PROGRAM Program modification, e.g. pc:1:7

Any number can be *: 1:30:* is every worth of controller 30 on channel 1,.
which is how you bind an expression pedal.

Commands run in the background through / bin/sh -c, or the shell you offer.
with -- shell Their stdin is / dev/null Their stdout goes to fcbnerd’s.
stderr, so it can’t corrupt the occasion stream; with -- peaceful it goes to.
stdout. They see these environment variables:

Variable
MIDI_TYPE cc or pc
MIDI_CHANNEL 1– 16
MIDI_CONTROLLER, MIDI_VALUE For cc
MIDI_PROGRAM For pc
MIDI_SOURCE MIDI source name

 # Expression pedal sets output volume
fcbnerd -q-- bind ' 1:30: *= osascript -e "set volume output volume $(( MIDI_VALUE * 100/ 127))"'

Every stomp runs the command, so 2 fast presses run it two times even if the.
initially run hasn’t ended up. That likewise implies every matching message begins a.
shell. Keep broad patterns like *: *:127 or pc: *:* far from loud gadgets.

Pedal sweeps are the exception. A sweep sends out lots of worths a 2nd, so.
for a binding with a * worth, just one copy of the command performs at a time.
for each control (channel and controller). While it runs, fcbnerd keeps just.
that control’s most recent worth and runs it next, which keeps the shell count down.
and still ends on the pedal’s last position. If a command is still running.
after 5 seconds, fcbnerd states so on stderr.

A command that exits non-zero gets its binding and exit status printed to.
stderr. Stopping fcbnerd (Ctrl+ C, eliminate, closing the terminal, or a closed.
stdout) sends out SIGTERM to any command still running, consisting of procedures it.
begun.

Functions and aliases from your interactive shell aren’t filled in sh -c
In celebration, export a function to make it noticeable (macOS’s / bin/sh is celebration, so.
the default shell sees it):

 greet() {state " predetermined $ MIDI_PROGRAM";}
 export -f welcome.
fcbnerd -q-- bind ' pc:1: *= greet'

zsh can’t export functions. Put them in a file and source it with zsh:.
-- shell/ bin/zsh-- bind 'pc:1: *= source ~/. fcbnerd.zsh && & greet'

The FCB1010 sends out absolutely nothing when you release a switch (see.
FCB1010 notes), so a binding fires on journalism just.For
on/off habits, keep the state in the command, for instance by toggling a.
file in / tmp

fcbnerd listen prints one JSON item per line. Every item has type,.
source (the MIDI source’s screen name) and time (when fcbnerd gotten.
the message: ISO 8601, UTC, milliseconds). Channels are 1– 16; note,.
controller, program, speed and pressure worths are the raw 0– 127 MIDI.
worths.

type Extra fields Notes
pc channel, program Program modification. program is 0-based on the wire.
cc channel, controller, worth Control modification: switches and expression pedals.
note_on channel, note, speed
note_off channel, note, speed Also produced for note-on with speed 0.
poly_pressure channel, note, pressure
channel_pressure channel, pressure
pitch_bend channel, worth 0– 16383, center 8192.
sysex length, information information is lowercase hex consisting of the f0 f7 framing; length counts those bytes.
linked A source appeared and is being listened to. Always precedes that source’s occasions.
detached A source disappeared. A message currently in flight might still follow it.

System real-time messages (MIDI clock and so on) and system typical messages.
( tune position, MTC) are not produced. New occasion types or fields might be included.
in future variations; existing ones will not alter significance. Consumers should.
neglect types and fields they do not acknowledge.

Read the stream without delay. If a customer stops reading, fcbnerd lines occasions.
in memory and provides them all when checking out resumes, so a stalled customer.
will act upon a burst of stagnant presses.

fcbnerd list-- format json prints a various shape, one line per source:.
{"type":" source"," name":" UM-ONE"," id": -1234567} id is the CoreMIDI.
special ID.

examples/developer.sh is a total, commented.
setup for software application engineers: 10 switches for running tests, waiting on CI,.
syncing the branch, silencing the mic and more, plus an expression pedal on.
output volume. Run it with DRY_RUN= 1 initially to see what each switch would do.

Program 0 switches to the next Space, and program 1 to the previous one.This
requirements more than one Space, the “Move left/right an area” faster ways.
made it possible for (the default) in System Settings → Keyboard → Keyboard Shortcuts →.
Mission Control, and for your terminal app both Accessibility consent and.
Automation consent to controlSystem Events macOS requests theAutomation
consent the very first time.

 fcbnerd | jq-- unbuffered -r ' choose(. type == "pc") |. program' |
 while  read -r program;  do
   case "$ program"  in
0) osascript -e ' inform application "System Events" to essential code 124 utilizing control down';;.
1) osascript -e ' inform application "System Events" to essential code 123 utilizing control down';;.
 esac
 done

Program 0 toggles play/pause, and an expression pedal on CC 30 sets the output.
volume. Output can get here in.
partial pieces, so buffer up until a newline. The course is for Apple Silicon;.
Homebrew on Intel sets up to / usr/local/bin

 regional  buffer  = ""
 fcbnerd  =  hs job brand-new("/ opt/homebrew/bin/ fcbnerd",  nil,  function( _,  stdout,  _).
 buffer  =  buffer   stdout
   for  line  in  buffer:  gmatch("([^n] *) n")  do
     regional  occasion  =  hs json translate( line).
 if  occasion  and  occasion type = = " pc"  and  occasion program = =  0  then
       hs eventtap occasion brand-newSystem KeyEvent(" PLAY",  real):  post().
 hs eventtap occasion brand-newSystem KeyEvent(" PLAY",  incorrect):  post().
 elseif  occasion  and  occasion type = = " cc"  and  occasion controller = =  30  then
       hs audiodevice defaultOutput Gadget():  setVolume( occasion worth /  127  *  100).
 end
   end
   buffer  =  buffer:  match("[^n] *$").
 return  real
 end).
 fcbnerd:  start()

Things about the pedal that customers require to manage:

  • A press sends out one message and releasing sends out absolutely nothing. On/ off habits.
    ( very first press “on”, 2nd “off”) needs to be tracked by the customer.
  • The factory presets send out various CC numbers from the exact same switch.
    depending upon which predetermined is active. Run fcbnerd -f text, press each.
    switch you prepare to utilize, and note what it sends out.
  • Pressing a switch likewise re-sends that predetermined’s expression-pedal worths, so.
    not every cc on a pedal’s controller implies the foot moved.
  • The expression pedals do not reach the complete 0– 127 variety. Part of the travel.
    sends out absolutely nothing and the sweep covers approximately two-thirds of the worths, so.
    rescale to the variety you in fact see.
  • The pedal has 5-pin DIN MIDI just. You require a USB MIDI user interface, which.
    appears as the source name.
 speedy construct.
swift  test                                  # decoder, formatter and binding tests
. build/debug/fcbnerd replicate &&             # phony pedal build/debug/fcbnerd-- format text #  see it

Sources/ FCBNerdCore deciphers CoreMIDI’s Universal MIDI Packets, formats.
output and parses bindings. It has no CoreMIDI reliance, so its trial run.
without hardware.
Sources/ fcbnerd is the CLI: CoreMIDI connections, hotplug and the.
simulator.

To release, bump variation in Sources/ fcbnerd/main. swift, dedicate, and push a.
matching tag:

 git tag -a v1.2.3 -m " fcbnerd 1.2.3" &&& & git push origin v1.2.3

The release workflow tests, releases a.
GitHub Release with a universal binary, and updates the formula in.
JamesRyanATX/homebrew-tap

MIT



Source link