Audio: per-game persisted format overrides + auto-learn

Persist audio overrides keyed by game image name (coop_audio_overrides.ini
next to the exe) so a known-bad game is auto-corrected on its next launch:
on attach to a guessed stream the host applies any saved override, and a
manual Override now saves too. A format the hook catches exactly at
IAudioClient::Initialize is auto-saved as that game's override (ground truth);
if it overwrites a differing stored value, a warning is logged. Host-originated
log lines now reach the Log window via IpcServer::host_log (color-coded).

Validated live (harness): a pre-seeded override for coop_tone is auto-applied
over the guess (state -> manual override, 48000/2/16). audio_overrides_test
covers persist/reload/case-insensitive lookup/differing-overwrite. Trim README.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 01:59:11 +02:00
parent fe6f8462ab
commit 32028cb286
12 changed files with 483 additions and 12 deletions

View File

@@ -79,8 +79,10 @@ and covers anything the hooked path doesn't (Vulkan, D3D9 — see Roadmap).
loopback fallback is always format-correct. The Audio panel shows each stream's
format provenance (*known* / *measuring* / *measured rate* / *low-confidence* /
*override*) so the assumption is visible, and (under Debug details) lets the operator
**re-measure** the rate or **override** the format when the guess is wrong. Streams
created *after* injection are captured exactly.
**re-measure** the rate or **override** the format when the guess is wrong. Overrides
are **remembered per game** (and a format caught exactly at `Initialize` is auto-saved
as that game's override), so a known-bad game is corrected automatically next launch.
Streams created *after* injection are captured exactly.
- **Debug-oriented UI:** the ImGui overlay is laid out for diagnosing the
pipeline, not for end use. F1 hides it entirely so the window is a clean mirror
for RPT; F2 frees the operator cursor; **F10 saves a PNG screenshot** (back buffer,
@@ -90,14 +92,6 @@ and covers anything the hooked path doesn't (Vulkan, D3D9 — see Roadmap).
### Planned (next up)
- **Per-game persisted audio overrides + auto-learn.** The Audio panel already lets
the operator re-measure or override a stream's format (Debug details → *Fix the
primary stream*), but the override is session-only. Persist overrides **keyed by game
image name** to a small store next to the exe so a known-bad game is auto-corrected on
its next launch, and **auto-save a format caught exactly at `IAudioClient::Initialize`
as that game's override** (ground truth) so a later late-attach is corrected
automatically. If a caught format overwrites a stored override that *differs*, log a
warning.
- **Auto re-attach the same game on relaunch (session-only).** A checkbox on the
attached (or just-terminated) target, default off, *not* persisted, **always visible**
(it's the recommended recovery path, not a diagnostic). While on and the target has
@@ -191,6 +185,9 @@ ctest --test-dir build -C Debug --output-on-failure
converges to the right standard rate, rejects burst windows (never commits to a wrong
neighbour, incl. the real 46205 misread), flags a genuinely non-standard rate
low-confidence instead of spinning, and ignores idle windows. Pure logic, no device.
- **`audio_overrides_test`** — unit test of the per-game audio override store
(persist/reload, case-insensitive lookup by image name, and the differing-overwrite
detection that drives the warning). No device.
- **`audio_hook_test`** — in-process self-test of the WASAPI render-hook's **format
detection**, the part that gets pitch right. Using a shared configurable
`ToneSource` (the same render helper `coop_tone` uses), it renders tones at a matrix