diff --git a/crates/punktfunk-host/src/audio/windows/minted.rs b/crates/punktfunk-host/src/audio/windows/minted.rs index c8fb5523..a34e6c9a 100644 --- a/crates/punktfunk-host/src/audio/windows/minted.rs +++ b/crates/punktfunk-host/src/audio/windows/minted.rs @@ -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() { diff --git a/crates/punktfunk-host/src/audio/windows/wasapi_mic.rs b/crates/punktfunk-host/src/audio/windows/wasapi_mic.rs index b27b9590..3339c27b 100644 --- a/crates/punktfunk-host/src/audio/windows/wasapi_mic.rs +++ b/crates/punktfunk-host/src/audio/windows/wasapi_mic.rs @@ -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);