micpipe 0.1.0

Run a macOS microphone-to-BlackHole audio router as a background service.
micpipe-0.1.0 is not a library.

micpipe

micpipe is a lightweight macOS audio router service for sending your microphone into BlackHole 2ch or another CoreAudio output. It is built for the common workflow of using BlackHole as the microphone source in another app while keeping the route alive in the background.

It can also run in the foreground while you are testing.

What it does

  • Routes one input device to one output device.
  • Runs as a per-user launchd service.
  • Follows the system default input, or pins a specific input by name.
  • Restarts automatically when the default input disconnects.
  • Waits for a pinned input to reconnect, then restarts automatically.

What it is not

micpipe is not a full multi-source mixer. It does not currently combine multiple inputs, expose gain controls, or provide mute/solo controls. It is a small background audio router for keeping a microphone-to-output route alive.

Requirements

  • macOS
  • Rust and Cargo
  • BlackHole, or another CoreAudio output device to receive the microphone audio
  • Microphone permission for the terminal or installed binary that runs micpipe

Install

Install from crates.io:

cargo install micpipe

Install from a source checkout:

cargo install --path .

Quick start

Run in the foreground with the default route:

micpipe run

By default, micpipe run follows the system default input device and routes it to the first output device whose description contains BlackHole 2ch.

Install and start the background service:

micpipe install --output "BlackHole 2ch"

Pin explicit devices when you want stable matching by device-name substring:

micpipe install \
  --input "MacBook Pro Microphone" \
  --output "BlackHole 2ch"

Use run instead of install for foreground testing:

micpipe run \
  --input "MacBook Pro Microphone" \
  --output "BlackHole 2ch"

Add --debug to log buffer occupancy once per second:

micpipe run --debug

Common use cases

  • Send your mic into BlackHole so another app can select BlackHole as its input.
  • Keep that route alive as a background service.
  • Follow whichever microphone is currently the macOS default.
  • Pin a USB or external microphone and restart only after that same device reconnects.

macOS setup notes

Install BlackHole separately before using the default output route.

Run micpipe run once from the terminal if macOS needs to prompt for microphone permission. The permission belongs to the app or binary that starts micpipe, so the foreground test is the easiest way to confirm capture works before installing the service.

CLI reference

micpipe run [--input <name>] [--output <name>] [--debug]
micpipe install [--input <name>] [--output <name>] [--debug]
micpipe status
micpipe restart
micpipe stop
micpipe start
micpipe uninstall

--output is a case-insensitive substring match and defaults to BlackHole 2ch.

--input is optional:

  • Without --input, micpipe follows the system default input device. If that input disconnects, micpipe logs that the input device was disconnected and immediately asks the installed service to restart.
  • With --input, micpipe pins that input by case-insensitive substring. If the pinned input disconnects, micpipe logs the disconnected device, polls every 5 seconds until that input appears again, then asks the installed service to restart.

Output stream errors are logged, but do not currently trigger a restart.

Service

micpipe install records the current executable path in the plist. When you install from crates.io, that is usually the micpipe binary under Cargo's bin directory.

Useful service commands:

micpipe status
micpipe restart
micpipe stop
micpipe start
micpipe uninstall

The service label is com.markarranz.micpipe, and the generated plist lives at:

~/Library/LaunchAgents/com.markarranz.micpipe.plist

Re-run micpipe uninstall followed by micpipe install ... when you want to change the installed route arguments.

Logs

The service writes logs under:

~/.local/share/micpipe/out.log
~/.local/share/micpipe/err.log

Watch both logs with:

tail -f ~/.local/share/micpipe/out.log ~/.local/share/micpipe/err.log

Log lines are timestamped in the user's local timezone with a numeric offset, for example:

[2026-06-29T13:04:05-07:00] Mic -> BlackHole running...

Human-facing lifecycle and recovery messages go to out.log. Raw stream errors and restart-command failures go to err.log.

When the default input disconnects, out.log includes a recovery message like:

[2026-06-29T13:04:05-07:00] input device disconnected: MacBook Pro Microphone; attempting micpipe restart

When a pinned input disconnects, out.log records that micpipe is waiting for that device before restarting:

[2026-06-29T13:04:05-07:00] input device disconnected: USB Microphone; waiting for pinned input device 'USB Microphone' to reconnect before restarting
[2026-06-29T13:04:10-07:00] pinned input device reconnected: USB Microphone; attempting micpipe restart

Troubleshooting

No output device matching BlackHole

Install BlackHole, confirm it appears in macOS audio devices, or pass a different output substring:

micpipe run --output "Your Output Device"

No default input device

Select a default microphone in macOS Sound settings, or pass an explicit input:

micpipe run --input "Your Microphone"

Service installed but not loaded

Start it again and then inspect status:

micpipe start
micpipe status

Logs show input device disconnected

If you are following the default input, micpipe attempts an immediate service restart. If you pinned an input with --input, reconnect that same device; micpipe polls every 5 seconds and restarts after it appears again.

Route arguments changed

Uninstall and reinstall the service so the plist is regenerated with the new arguments:

micpipe uninstall
micpipe install --input "Your Microphone" --output "BlackHole 2ch"

Development

Run the checks before committing changes:

cargo fmt
cargo test
cargo clippy --all-targets --all-features
git diff --check

After changing the binary for an installed service:

cargo build --release --bin micpipe
cp target/release/micpipe ~/.local/bin/micpipe
micpipe restart
tail -f ~/.local/share/micpipe/out.log ~/.local/share/micpipe/err.log

See DESIGN.md for implementation details.