From 3bac7c74721d57205ebb0506ba62f32a34e97691 Mon Sep 17 00:00:00 2001 From: Overlord Date: Tue, 11 Aug 2026 22:08:56 +0200 Subject: [PATCH] Expand and clarify `README.md` with updated workflows, examples, and testing instructions. --- README.md | 74 +++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 58 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 7fbc7b7..d564962 100644 --- a/README.md +++ b/README.md @@ -2,22 +2,40 @@ Create and manage PipeWire virtual microphones easily, from the command line. +--- + ## What it does -A vmic is two chained loopback stages: +A vmic starts as a single loopback stage: ``` -apps -> vmic_X_sink =(stage 1)=> vmic_X_mid -> vmic_X_mix =(stage 2)=> vmic_X_mic -> recorders - ^ hardware mic joins here (route -s) +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 downstream, and optionally hear your -own output via a self-monitor loopback (which never carries the mixed-in mic). +`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`, `pw-link` on `PATH`) +- PipeWire + pipewire-pulse (`pactl`, `pw-loopback` on `PATH`) - Rust (edition 2021) ## Build @@ -45,18 +63,22 @@ LIBCLANG_PATH = "/home//.local/share/vmic-build/libclang-venv/lib/python3.* ## 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 -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 and their status -vmic wipe # tear down every vmic, tracked or not +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 `. @@ -75,6 +97,26 @@ vmic route podcast -s "USB Microphone" 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