fix(host/audio): the mic pump's first resolve waits for the minted endpoints

Measured on the target box: the pump wired 2 s before the provisioning
worker latched, took the cable as its write target, and the next wiring
pass would then have paired the default recording with the minted
microphone — which nothing writes into: dead mic-air until a pump reopen.
resolve_target now provisions synchronously (instant once latched; the
opt-out env is honoured), so the pump's held device and the plan's verdict
can never disagree.
This commit is contained in:
2026-08-07 12:02:48 +02:00
parent 15dddf7ec0
commit 99a59fb5c7
2 changed files with 15 additions and 4 deletions
@@ -353,11 +353,17 @@ pub(crate) fn discover_driver(needle: &str, inf_name: &str) -> Result<(String, S
}
/// `audio-probe mint` devtest body: one synchronous provisioning pass, results printed.
/// Synchronous provisioning for devtests: a fresh CLI process has no startup worker to have
/// finished yet, so `audio-probe plan` would otherwise RACE the background thread and print
/// the name ladder instead of tier-0. Existing marker devnodes re-resolve in milliseconds.
/// Synchronous provisioning for the mic pump's resolve and the devtests.
///
/// The pump's FIRST open must not race the startup worker: measured on the target box, the
/// pump wired 2 s before the worker latched, took the cable as its write target, and the next
/// wiring pass would then have pointed the default recording at the minted microphone —
/// which nothing writes into: dead mic-air until a pump reopen. Blocking the first resolve
/// (existing marker devnodes re-resolve in milliseconds; a cold boot pays the one-time mint)
/// keeps the pump's target and the plan's verdict the same thing. Latched calls return
/// immediately; the opt-out env is honoured like everywhere else.
pub(crate) fn ensure_blocking() {
if PROVISIONED.get().is_some() {
if std::env::var_os("PUNKTFUNK_NO_AUDIO_MINT").is_some() || PROVISIONED.get().is_some() {
return;
}
if let Ok(m) = ensure_all() {
@@ -217,6 +217,11 @@ impl VirtualMic for WasapiVirtualMic {
/// Resolve the mic inject target from the wiring plan, auto-installing the Steam Streaming pair
/// when nothing usable exists (then re-planning). Runs on the COM-initialized render thread.
fn resolve_target() -> Result<(wasapi::Device, String)> {
// The minted endpoints must exist BEFORE this open resolves its write target: the pump
// holds one device for its lifetime, so racing the provisioning worker here left the pump
// on the cable while later plans paired the default recording with the minted microphone
// nothing wrote into (see `minted::ensure_blocking`). Instant once latched.
super::minted::ensure_blocking();
// set_playback=false: the mic pump runs while the host is idle — only the desktop-audio
// capture may park the playback default (on the silent sink) for a stream's lifetime.
let mut wiring = audio_control::wire_now(false);