diff --git a/README.md b/README.md index ef80a2a..e551c73 100644 --- a/README.md +++ b/README.md @@ -66,15 +66,34 @@ All phases below are implemented and verified. - **Phase 2 — audio mirror. ✅** The host captures the game's audio by PID via WASAPI process loopback and re-renders it on the default endpoint, so RPT carries game audio to guests. -- **Audio render-hook (echo fix). ✅ (pending manual end-to-end)** The injected - hook intercepts the game's WASAPI render path (`IAudioRenderClient`), copies - the frames into a shared audio ring for the host to re-render, and releases the - game's buffer silenced — so the operator no longer hears the audio twice. The - host owns an enable flag and re-renders the game's format via `AUTOCONVERTPCM`; - if the hook doesn't publish a format in time it reverts to process loopback. - The Audio panel shows the active source and a render-stream-count debug table. - Validated in-process by `audio_hook_test`; the real-game pass is the remaining - manual step. +- **Audio render-hook (echo fix). ✅ (capture proven on a real game; full RPT + pass pending)** The injected hook intercepts the game's WASAPI render path + (`IAudioRenderClient`), copies the frames into a shared audio ring for the host + to re-render, and releases the game's buffer silenced — so the operator no + longer hears the audio twice. It hooks the render vtables *proactively* so a + game that's already playing when injected is still captured. The host owns an + enable flag and re-renders the game's format via `AUTOCONVERTPCM`; if the hook + doesn't publish a format in time it reverts to process loopback. The Audio panel + shows the active source and a render-stream-count debug table. Validated + in-process by `audio_hook_test` and against a real already-playing game + (Phantom Brave) with `coop_audio_probe`: the pre-existing render client is + detected and real, non-silent audio reaches the ring with zero overruns. + +### Remaining verification + +Producer-side capture is proven; these still need a human / full setup to confirm: + +- **Hooked audio over RPT, end-to-end.** Run the host, inject, tick **Mirror game + audio**, and confirm **Source: Hooked (no echo)**, the game goes locally silent, + and a guest on Remote Play Together still hears it. +- **Format guess for non-mix-format games.** For a stream that already exists at + injection time the hook can't see the game's `Initialize`, so it assumes the + device **mix format**. Phantom Brave matched it exactly (48 kHz/2ch/float). A + game that initialized shared mode with a different format would come out + wrong-pitched/garbled and needs per-stream format detection — not yet handled. +- **Multi-stream games.** v1 captures only the first ("primary") render stream; + confirm the Audio panel's render-stream table makes a multi-stream game obvious + (secondary streams stay local until per-stream mixing is added). ## Roadmap