# vmic Create and manage PipeWire virtual microphones easily, from the command line. --- ## What it does A vmic starts as a single loopback stage: ``` apps -> vmic_X_sink =(pw-loopback)=> vmic_X_mic -> recorders ``` Point an app's output device at `vmic_X_sink`, and a recording app's input device at `vmic_X_mic`. Optionally mix in a real microphone (`route -s`), and optionally hear your own output via a self-monitor loopback (`edit -l`). If you enable both a self-monitor loopback *and* a mixed-in hardware source at the same time, vmic automatically splits into a second loopback stage so the hardware source never leaks into your self-monitor: ``` apps -> vmic_X_sink =(stage 1)=> vmic_X_mid -> vmic_X_mix =(stage 2)=> vmic_X_mic -> recorders ^ hardware mic joins here instead ``` It collapses back to the single-stage form the moment either the loopback or the mixed-in source goes away. `edit --loopback-no-mix true` opts a vmic out of this split entirely, keeping it at one stage even with both active - the mixed source just becomes audible in your self-monitor too, as an explicit tradeoff for staying simpler. Either transition recreates the vmic's underlying sink/mic nodes (same names, new ids); any stream that was routed into or out of them gets moved back automatically, and vmic reports what happened either way. ## Requirements - PipeWire + pipewire-pulse (`pactl`, `pw-loopback` on `PATH`) - Rust (edition 2021) ## Build ``` cargo build --release ``` If the build fails inside `libspa-sys` (e.g. `no field 'data' on type 'spa_pod_builder'`), your system clang is newer than the pinned `bindgen` version supports. Point `LIBCLANG_PATH` at an older libclang - a quick fix is a pip-vendored one: ``` python3 -m venv ~/.local/share/vmic-build/libclang-venv ~/.local/share/vmic-build/libclang-venv/bin/pip install libclang ``` then add to `.cargo/config.toml`: ```toml [env] LIBCLANG_PATH = "/home//.local/share/vmic-build/libclang-venv/lib/python3.*/site-packages/clang/native" ``` ## Usage ``` vmic create [-l] # create a vmic, optionally with self-monitor vmic route -i # move an app's playback into the vmic vmic route -o # move an app's recording off the vmic vmic route -s # mix a hardware mic in, or remove it vmic edit -l [] # toggle the self-monitor loopback vmic edit --loopback-no-mix [] # keep 1 stage even with loopback + a mixed source vmic edit -v <0.8|80> # self-monitor loopback volume vmic edit -sv <0.8|80> # mixed-in source volume vmic delete # tear down one vmic vmic list # show all vmics, their status, and current node shape vmic wipe # tear down every vmic, tracked or not ``` `-l` and `--loopback-no-mix` accept a bare form (means `true`) or an explicit `true`/`false`, e.g. `vmic edit podcast -l` and `vmic edit podcast -l false` both work. Aliases: `create` = `mk`/`make`, `delete` = `rm`/`remove`, `list` = `ls`, `wipe` = `reset`. Shell completions: `vmic completions `. ## Example ``` vmic create podcast -l # set your app's output device to vmic_podcast_sink # set your recorder's input device to vmic_podcast_mic vmic route podcast -s "USB Microphone" ``` ## State Tracked in a SQLite database at `~/.config/vmic/vmic.db` (override the directory with `VMIC_STATE_DIR_OVERRIDE`). ## Testing `cargo test` runs a handful of fast unit tests only - no PipeWire needed. Two live integration test suites exercise every command against a real PipeWire session: they spawn real `pw-loopback` processes, move real streams, and (guarded) run the destructive `wipe` command. Neither runs automatically; opt in explicitly: ``` tests/live_test.sh --input-app firefox --output-app chromium --hw-source fifine cargo test --test live_test -- --ignored --nocapture ``` Both take the same parameters - as flags for the shell version, or matching env vars for either (`VMIC_TEST_INPUT_APP`, `VMIC_TEST_OUTPUT_APP`, `VMIC_TEST_HW_SOURCE`, `VMIC_TEST_NAME`, `VMIC_TEST_SKIP_WIPE=1`) - so point them at whatever apps and hardware source you actually have available. They only ever touch vmics named ``/`_a`/`_b`, refuse to run the `wipe` phase if any other vmic exists at that point, and always clean up after themselves even on failure. ## License APGL-3