Helldivers 2 tanked on an IDLE host: the pad DualSense speaker stayed visible and the recording default was parked forever #217

Merged
enricobuehler merged 4 commits from worktree-hd2-idle-recording-default into main 2026-08-14 08:41:14 +00:00
Owner

Second Helldivers 2 field arc (the first led to #185's mint-retry fix). New report: even with the host idle — not streaming, no controller plugged in — a locally played HD2 drops to 2–5 FPS 1% lows. Two independent idle defects, both fixed here.

1. The pad-audio "Wireless Controller" speaker stayed visible while no pad existed — the reporter-confirmed main cause

The per-pad endpoint is a Steam-Streaming-Speakers instance stamped to be indistinguishable from a real DualSense speaker — the feature during a pad session (libScePad titles route haptics audio at it, cf. the GTA V Enhanced case), and a trap the rest of the time: it is pre-provisioned at every host start and stayed visible forever, so an idle HD2 (a libScePad port with native DualSense haptics on PC) found it by identity, engaged its haptics path against a device state no real hardware can produce (a DualSense audio device with no DualSense HID behind it), and tanked. The reporter isolated it live: disabling the device in mmsys.cpl restored full performance.

That manual remedy is now automatic: the endpoint parks hidden (DEVICE_STATE_DISABLED, via IPolicyConfig::SetEndpointVisibility — the exact call behind mmsys.cpl's Disable; vtable slot pinned next to the SetDefaultEndpoint we already bind) whenever no client pad is attached. Provisioning hides it at startup, a PUNKTFUNK_PAD_AUDIO=0 host hides leftovers from earlier runs, and the per-pad streamer shows it for exactly the pad's lifetime — to a game, a DualSense arriving and leaving. Devnode, driver binding and stamps stay put (registry-based resolution finds a disabled endpoint at the next boot), so the flips raise no PnP traffic — the #185 lesson holds. Devtest: pad-endpoint grew show/hide verbs; tone/capture need a show first.

⚠ Operator-visible: "Speakers (Wireless Controller)" now shows as disabled in the Sound settings while no client pad is connected — the parked state, not a defect.

2. The default recording device was parked on the virtual mic host-lifetime, with no restore path

wire_now_full asserted default recording = virtual mic capture on every wiring pass, including the mic pump's eager boot pass — and SetDefaultEndpoint covers eCommunications, so every game's voice input bound a virtual microphone whose feeder only runs during a stream. Nothing ever restored it: not session end, not service stop (a second field box showed "Punktfunk Microphone — default device" while idle, with mmsys.cpl's own Recording tab hanging on it).

The recording default is now session-scoped, exactly like the playback default has always been: parked only while a desktop-audio capture is open, the operator's device remembered (in-memory + crash marker audio-default-rec.prev), restored on capture close, recovered at next boot after a crash, unparked by the uninstaller. Boxes wedged by earlier builds (which recorded nothing to restore) heal at boot: an idle pass finding the default on the plan's mic capture moves it to the first real microphone (wiring_plan::real_capture, pure + unit-tested against the field box's exact recording-tab inventory). A game launched during a stream still records the client's mic; one launched before the stream keeps the operator's own microphone. The mid-idle drift re-assert (which stomped an operator's manual mic choice on mic-pump reopen) is gone with the gating.

Why streaming does not re-trigger any of this

The mechanism was "device advertising a role nothing fulfills" — states real hardware can never produce. A session recreates the complete real-hardware state (HID pad + container-matched, engine-drained endpoint; live mic pump), which HD2 handles like a physical DualSense.

Verification

.133 gate on proven shipped bytes: cargo fmt --all --check ✓, cargo clippy -p punktfunk-host --release --all-targets --locked -- -D warnings ✓, cargo test -p punktfunk-host --release --locked audio:: 84/84 ✓. On-glass HD2 validation (idle 1% lows + in-session with pad + long-muted client mic) is owed on the field boxes.

Second Helldivers 2 field arc (the first led to #185's mint-retry fix). New report: even with the host **idle** — not streaming, no controller plugged in — a locally played HD2 drops to 2–5 FPS 1% lows. Two independent idle defects, both fixed here. ## 1. The pad-audio "Wireless Controller" speaker stayed visible while no pad existed — **the reporter-confirmed main cause** The per-pad endpoint is a Steam-Streaming-Speakers instance stamped to be indistinguishable from a real DualSense speaker — the feature during a pad session (libScePad titles route haptics audio at it, cf. the GTA V Enhanced case), and a trap the rest of the time: it is **pre-provisioned at every host start** and stayed visible forever, so an idle HD2 (a libScePad port with native DualSense haptics on PC) found it by identity, engaged its haptics path against a device state no real hardware can produce (a DualSense audio device with no DualSense HID behind it), and tanked. The reporter isolated it live: disabling the device in mmsys.cpl restored full performance. That manual remedy is now automatic: the endpoint parks **hidden** (`DEVICE_STATE_DISABLED`, via `IPolicyConfig::SetEndpointVisibility` — the exact call behind mmsys.cpl's Disable; vtable slot pinned next to the `SetDefaultEndpoint` we already bind) whenever no client pad is attached. Provisioning hides it at startup, a `PUNKTFUNK_PAD_AUDIO=0` host hides leftovers from earlier runs, and the per-pad streamer shows it for exactly the pad's lifetime — to a game, a DualSense arriving and leaving. Devnode, driver binding and stamps stay put (registry-based resolution finds a disabled endpoint at the next boot), so the flips raise no PnP traffic — the #185 lesson holds. Devtest: `pad-endpoint` grew `show`/`hide` verbs; `tone`/`capture` need a `show` first. ⚠ Operator-visible: "Speakers (Wireless Controller)" now shows as *disabled* in the Sound settings while no client pad is connected — the parked state, not a defect. ## 2. The default recording device was parked on the virtual mic host-lifetime, with no restore path `wire_now_full` asserted *default recording = virtual mic capture* on **every** wiring pass, including the mic pump's eager boot pass — and `SetDefaultEndpoint` covers eCommunications, so every game's voice input bound a virtual microphone whose feeder only runs during a stream. Nothing ever restored it: not session end, not service stop (a second field box showed "Punktfunk Microphone — default device" while idle, with mmsys.cpl's own Recording tab hanging on it). The recording default is now **session-scoped**, exactly like the playback default has always been: parked only while a desktop-audio capture is open, the operator's device remembered (in-memory + crash marker `audio-default-rec.prev`), restored on capture close, recovered at next boot after a crash, unparked by the uninstaller. Boxes wedged by earlier builds (which recorded nothing to restore) heal at boot: an idle pass finding the default on the plan's mic capture moves it to the first real microphone (`wiring_plan::real_capture`, pure + unit-tested against the field box's exact recording-tab inventory). A game launched *during* a stream still records the client's mic; one launched before the stream keeps the operator's own microphone. The mid-idle drift re-assert (which stomped an operator's manual mic choice on mic-pump reopen) is gone with the gating. ## Why streaming does not re-trigger any of this The mechanism was "device advertising a role nothing fulfills" — states real hardware can never produce. A session recreates the complete real-hardware state (HID pad + container-matched, engine-drained endpoint; live mic pump), which HD2 handles like a physical DualSense. ## Verification .133 gate on proven shipped bytes: `cargo fmt --all --check` ✓, `cargo clippy -p punktfunk-host --release --all-targets --locked -- -D warnings` ✓, `cargo test -p punktfunk-host --release --locked audio::` 84/84 ✓. On-glass HD2 validation (idle 1% lows + in-session with pad + long-muted client mic) is owed on the field boxes.
enricobuehler added 4 commits 2026-08-14 08:40:52 +00:00
The wiring pass asserted 'default recording = virtual mic capture' on EVERY
pass — including the mic pump's eager boot pass — so an idle box permanently
held the Windows default recording device (and, since SetDefaultEndpoint
covers eCommunications, every game's voice input) on a virtual microphone
whose render feeder is idle-stopped, with no restore path at all: not at
session end, not at service stop. Field-measured 2026-08-14: Helldivers 2
(Wwise + always-on voice) played LOCALLY on an idle host tanks to 2–5 FPS 1%
lows, and mmsys.cpl's own Recording tab goes unresponsive polling the same
endpoint; the reporter's Sound settings showed 'Punktfunk Microphone —
Dispositivo predefinito' with the host idle.

The recording default now follows the exact discipline the playback default
has always had — parked only while a desktop-audio capture is open, with the
operator's device remembered (in memory + an on-disk crash marker,
audio-default-rec.prev), restored on capture close, recovered after a crash
on the next boot's first wiring pass, and unparked by the uninstaller. A
game launched during a stream still binds the client's mic (the park runs
before the session's game does); one launched before the stream keeps the
operator's own microphone — the honest answer.

Because earlier builds recorded nothing to restore, an upgraded box would
have stayed wedged on the virtual mic forever: an idle-pass hygiene now
moves a default found sitting on the plan's mic capture back to the first
REAL microphone (pure picker wiring_plan::real_capture, unit-tested against
the field box's exact recording-tab inventory). Session passes are exempt,
and a box with no real microphone is left alone.

Also folded in: the mid-idle drift re-assert is gone with the gating, so a
mic-pump reopen no longer stomps a recording device the operator chose
themselves.
The per-pad endpoint is stamped to be indistinguishable from a real
DualSense speaker — that is the feature during a pad session (libScePad
titles route haptics audio at it) and a trap the rest of the time: the
endpoint is pre-provisioned at EVERY host start and stayed visible
forever, so an idle Helldivers 2 found it by identity, engaged its
DualSense-haptics path against a device nothing services, and dropped to
2–5 FPS 1% lows — host idle, no controller plugged in, no session ever
run (field-confirmed 2026-08-14: the reporter isolated the 'DualSense
speaker' and disabling it in mmsys.cpl restored full performance).

That manual remedy is now automatic: the endpoint parks HIDDEN
(DEVICE_STATE_DISABLED, IPolicyConfig::SetEndpointVisibility — the call
behind mmsys.cpl's own Disable, vtable slot pinned next to the
SetDefaultEndpoint we already bind) whenever no client pad is attached.
Provisioning hides it at startup, a PUNKTFUNK_PAD_AUDIO=0 host hides
leftovers from earlier runs, and the per-pad streamer shows it for
exactly the pad's lifetime — to a game, a DualSense arriving and
leaving. The devnode, driver binding and stamps stay put (registry-based
resolution finds a disabled endpoint at the next boot), so the flips
raise no PnP traffic and the expensive provisioning still happens once
at boot — the #185 lesson holds.

Devtest: pad-endpoint grew show/hide verbs; tone/capture need a show
first on a parked box.
docs(changelog): the pad-audio DualSense speaker hides while no client pad is attached
ci / bun-nix (pull_request) Successful in 33s
ci / rust-arm64 (pull_request) Successful in 1m21s
ci / docs-site (pull_request) Successful in 1m14s
ci / web (pull_request) Successful in 6m32s
android / android (pull_request) Successful in 4m21s
ci / rust (pull_request) Successful in 8m45s
652de8b5e0
enricobuehler scheduled this pull request to auto merge when all checks succeed 2026-08-14 08:41:11 +00:00
enricobuehler merged commit 784f880fbf into main 2026-08-14 08:41:14 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: unom/punktfunk#217