The live indicator compared frames_rendered to the previous UI frame's value, but
audio buffers release in bursts so most frames saw no change -- the cell flickered
between a green dot and grey "idle". Now each stream remembers when it last advanced
and reads "live" for a short window (0.4 s) afterwards, with a ~2 Hz frames/s
estimate next to it; otherwise "idle". Steady and readable for multi-stream games.
Verified: x64 build green. Full visual confirmation needs an injected, audio-playing
game with the per-stream table open (Debug details); logic reviewed.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rework the spike-era debug readouts into general-purpose status, with the
verbose diagnostics gated behind the menu bar's "Debug details" switch:
- Controllers (was "Phase 0 spike"): always shows slot/source + live
buttons; the raw stick/trigger numbers are debug-only.
- Injection hook status: general view is attached + focus spoof + a single
"Game reading controller: N polls/s" summary; the per-slot poll table,
focus-API counts, and input-path detection are debug-only.
- Audio: general view adds a "Buffered: N ms" health/latency proxy
(AudioMirror now tracks buffered audio in both render paths) and keeps the
render-stream count; the per-stream table is debug-only.
Default overlay is now clean general status; flip Debug details for the full
diagnostics. Completes the "generalize the UI" roadmap task. All tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Surfaces the render-hook diagnostics in the Audio panel.
- HookStatusView / IpcServer::hook_status(): carry audio_streams_seen +
audio_streams[] from the hook's back-channel.
- InjectionPanel: expose hook_status() so the audio panel can read the counts.
- AudioPanel::draw_ui(HookStatusView): show the active Source (green Hooked
vs amber Loopback); only warn about the local echo on the loopback path. Add
the required render-stream table: one row per stream with format, cumulative
frames, the primary tagged, and a live/idle dot derived from frame-count
deltas. Notes overflow when the game exceeds kMaxAudioStreams slots.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirror the real game's audio so Steam Remote Play Together (which streams the
host's own audio session) carries it to the guest. The host captures the game
by PID via WASAPI process loopback and re-renders it on the default endpoint;
the game still plays locally too (accepted "double audio" for now).
- ProcessLoopbackCapture: process-loopback capture client, frame-sink + stats.
The completion handler must be agile (IAgileObject) or
ActivateAudioInterfaceAsync rejects every call with E_ILLEGAL_METHOD_CALL.
- AudioMirror: wraps capture with an event-driven render client and a primed
ring buffer; AudioPanel drives it from the injected game's window/PID.
- coop_tone: standalone WASAPI sine-wave process used as a known audio source.
- audio_loopback_test (CTest): captures coop_tone by PID and asserts non-silent
audio arrives, so the path is verifiable without a second Steam account.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>