Build custom BlackHole-based virtual audio drivers that mimic a specific real audio interface: same name, same exact per-direction channel count, same per-channel labels (e.g. "ADAT 3", "Headphones L").
A placeholder stands in for a real interface inside a CoreAudio aggregate device while the hardware is disconnected. Without one, an aggregate missing a member loses its channel routing, so a DAW routed to specific channels loses or misaligns them. A placeholder with the exact same name, channel count, and channel labels keeps that routing valid whether or not the real hardware is plugged in.
- Full Xcode.app (not just the Command Line Tools).
xcodebuildneeds the full toolchain. Install from the App Store, then:sudo xcode-select -s /Applications/Xcode.app sudo xcodebuild -license sudo xcodebuild -runFirstLaunch
git,python3(both ship with macOS / Xcode CLT).standinis a single stdlib-only Python script, no pip installs.
Tested on macOS 26 (Tahoe) with Python 3.14.
standin runs straight from the repo, no build step:
git clone https://github.com/VincentCusson/standin
cd standin
./standin listTo call it as standin from anywhere instead of ./standin, symlink it onto your PATH:
sudo ln -s "$PWD/standin" /usr/local/bin/standin./standin list # see available presets
./standin build --preset solo # build a placeholder for a Focusrite Scarlett Solo (4th Gen)
./standin activate solo # build + install it (asks before running sudo)
./standin status # confirm it's installed
./standin deactivate solo # uninstall itOr clone a device that's connected right now. It reads the real name, channel counts, sample rates, and channel labels straight from CoreAudio and turns them into a preset:
./standin clone # list connected devices, numbered
./standin clone 2 # inspect one by number...
./standin clone "Scarlett 4i4" # ...or by name, and offer to save it as a presetOr build fully manually, for any device:
./standin build \
--name "Focusrite Placeholder" \
--channels 18 \
--bundle-id com.you.focusriteplaceholder \
[--input-channels 4] [--output-channels 2] \
[--input-channel-names "Analog 1,Analog 2,..."] \
[--output-channel-names "Analog 1,Analog 2,..."]standin build never runs sudo. It builds the driver and prints the install commands. standin activate/standin deactivate are the only commands here that touch sudo, and both print the exact commands and confirm before doing so.
standin is the single entry point: one stdlib-only Python script, six subcommands.
| Subcommand | What it does |
|---|---|
standin list [-v] |
List known presets from presets.json |
standin clone [device] |
Read a connected device's real config via CoreAudio into a preset |
standin status |
Show which presets are currently installed |
standin build |
Build one driver from a preset or manual flags (no install) |
standin activate <key> |
Build + install a preset (sudo, confirms first) |
standin deactivate <key> |
Uninstall a preset (sudo, confirms first) |
Full guide: GUIDE.md.
presets.json is a small catalog of example devices, currently the Focusrite Scarlett 4th Gen lineup (solo, 2i2, 4i4, 16i16, 18i16). These aren't meant to be exact, just close enough to the real hardware to be handy stand-ins or a quick starting point. Each entry looks like:
"solo": {
"device_name": "Fakecusrite Solo",
"bundle_id": "com.standin.fakecusritesolo",
"input_channels": 4,
"output_channels": 2,
"input_channel_names": "Instrument/Line Input,Microphone Input,Loopback 1,Loopback 2",
"output_channel_names": "Output Left (Headphones Left),Output Right (Headphones Right)"
}notes and sample_rates are optional. standin clone adds new entries from live hardware automatically.
Got hardware that isn't here? PRs adding presets are welcome. standin clone <device> gives you most of the entry already.
blackhole-channel-naming.patch: per-channel labels (kAudioObjectPropertyElementName).blackhole-asymmetric-channels.patch: independent input/output channel counts, so an asymmetric device (e.g. 4 in / 2 out) doesn't need phantom channels on the smaller side.
Both patches are fully independent of each other and apply to vanilla BlackHole. They're applied fresh to a cached checkout (blackhole-src-cache/, gitignored) on every build.
You can also find these changes in my BlackHole fork.
standin itself (the CLI, docs, presets.json) is MIT-licensed. It builds against BlackHole, which is GPLv3: a .driver binary built by standin build is a derivative of BlackHole and is GPLv3 if you distribute it. See LICENSE for details.