Show HN: Macros with a Behringer FCB1010 MIDI Pedalboard in macOS

fcbnerd lets a MIDI foot controller act like an extra keyboard on macOS. It listens to any CoreMIDI source, prints a JSON line for every message, or runs a shell command when a bound pattern matches. The tool works with the Behringer FCB1010, but it is not limited to that hardware.

Installation Options

The easiest path is Homebrew. First trust the third‑party tap, then install the binary:

Homebrew 7+ requires the trust step before loading external taps. For users who prefer a manual build, the Swift toolchain on macOS 13+ (or Xcode) can compile the project:

The resulting executable lives in /usr/local/bin/fcbnerd and can be invoked directly. No additional permissions are needed because the program only reads MIDI data, which macOS allows without sandbox escalation.

Core Commands and Modes

Running fcbnerd without arguments starts the default listen mode. It connects to every MIDI source, or only those whose name contains the optional --source filter, and streams events until interrupted. Hot‑plug support means unplugging a device mid‑set and plugging it back in will generate connected and disconnected status lines automatically.

The list sub‑command outputs a JSON line per source, for example `{"type":"source","name":"UM-ONE","id":-1234567}` where the id is the CoreMIDI unique identifier. The simulate mode publishes synthetic presses, a pedal sweep, and a SysEx message on a loop, useful for testing consumers without hardware.

Binding Syntax and Pattern Matching

A binding pairs a PATTERN with a COMMAND using the `=` sign. The pattern can describe a Control Change (`CHANNEL:CONTROLLER:VALUE`) or a Program Change (`pc:CHANNEL:PROGRAM`). Wildcards (``) replace any numeric field, enabling expressions such as `1:30:` to capture every value of controller 30 on channel 1.

Example binding that opens the Downloads folder when controller 20 on channel 1 reaches 127:

Multiple `--bind` options are allowed; all matching bindings launch concurrently in the order given. The command runs in the background via `/bin/sh -c` unless a different shell is supplied with `--shell`. Stdin is redirected to /dev/null, stdout goes to fcbnerd’s stderr, and with `--quiet` it is sent to stdout instead.

Environment variables are exported for each command:

• MIDI_TYPE (`cc` or `pc`)

• MIDI_CHANNEL (`1–16`)

• MIDI_CONTROLLER, MIDI_VALUE (for Control Change)

• MIDI_PROGRAM (for Program Change)

• MIDI_SOURCE (display name of the source)

These allow scripts to react dynamically to the incoming MIDI data.

JSON Event Stream and Message Types

When listen runs in its default JSON mode, each line contains a complete object with a type, source, and time stamp in ISO 8601 UTC format, e.g.:

Supported type values include:

• `pc` – Program Change (program numbers are zero‑based on the wire)

• `cc` – Control Change (switches and expression pedals)

• `note_on` / `note_off` – Note events with velocity

• `poly_pressure` – Polyphonic aftertouch

• `channel_pressure` – Channel aftertouch

• `pitch_bend` – 14‑bit bend value (0–16383, center 8192)

• `sysex` – System Exclusive with length and hex data

• `connected` / `disconnected` – Source hot‑plug notifications

System real‑time and common messages are omitted. Future versions may add new types, but existing fields retain their meaning, and consumers should ignore unknown entries.

Text Formatting and Quick Diagnostics

The `-f text` flag prints aligned columns for human inspection, showing the pattern that can be bound next to each message. For example:

Running this command while pressing a footswitch reveals the exact numbers to use in a binding. The text layout may change, so scripts should prefer the stable JSON output.

Advanced Integration with macOS Automation

Because fcbnerd only reads MIDI, any automation that needs to press keys or control the system must be delegated to a tool with the appropriate permissions. Hammerspoon, Keyboard Maestro, or plain osascript can be invoked from bindings.

A common pattern is to map Program Change 0 to “next Space” and Program Change 1 to “previous Space” using AppleScript:

The script relies on the Control → Move left/right a space shortcuts being enabled in System Settings → Keyboard → Keyboard Shortcuts → Mission Control, and the terminal app must have Accessibility and Automation permissions for System Events.

An expression pedal can control the output volume by scaling the MIDI value (0–127) to a percentage:

Because pedal sweeps generate dozens of values per second, fcbnerd throttles execution: only one instance of the command runs per controller, and the newest value is queued while the previous instance finishes. If a command exceeds 5 seconds, a warning appears on stderr.

Shell Function Exporting and Compatibility

The default shell is /bin/sh, which on macOS is a symlink to bash. To use a custom function inside a binding, export it first:

Zsh cannot export functions directly; instead, source a file containing the function and specify the shell explicitly:

When a binding’s command exits with a non‑zero status, fcbnerd prints the binding and exit code to stderr. Stopping fcbnerd (Ctrl‑C, kill, or closing the terminal) sends SIGTERM to any still‑running child processes, ensuring a clean shutdown.

Development Workflow and Release Process

The project is split into two Swift packages. Sources/FCBNerdCore handles CoreMIDI packet decoding, formatting, and binding parsing, and it has no CoreMIDI dependency, allowing unit tests to run without hardware. Sources/fcbnerd implements the command‑line interface, hot‑plug handling, and the simulator.

Typical development steps:

To publish a new version, bump the version string in Sources/fcbnerd/main.swift, commit, tag, and push:

The CI pipeline builds a universal binary, creates a GitHub Release, and updates the Homebrew formula in JamesRyanATX/homebrew-tap. The project is released under the MIT license.

Practical Tips for Pedal Consumers

• A switch sends a single message on press and nothing on release; consumers must maintain on/off state themselves.

• Factory presets may change the CC number for the same physical switch; use `fcbnerd -f text` to catalog each switch’s output before binding.

• Pressing a switch also re‑sends the current expression‑pedal values, so not every CC on the pedal indicates foot movement.

• Expression pedals rarely reach the full 0–127 range; rescale based on the observed minimum and maximum.

• The pedal uses a 5‑pin DIN MIDI connector; a USB‑MIDI interface (e.g., UM-ONE) appears as the source name in the event stream.

By combining fcbnerd’s lightweight MIDI‑only design with macOS automation tools, users can turn any MIDI foot controller into a versatile, programmable input device for productivity, media control, or custom development workflows.

Read original on Hacker News →

Discussion

Telegram comments — under this post

Comment in Telegram →