fix(audio): game meter scales by GameAudioVolume — meter/stream/headphones all follow the one knob

Creator report 2026-09-01: with the desktop slider dragged to 20%, the game bar's
meter stayed pegged yellow/red. Root cause: the mic meter's formula (level x volume)
was cloned to the game bar WITHOUT the x-volume factor, and the clone inherited a
false premise — that WASAPI loopback capture tracks the endpoint volume. Tonight's
observation disproves it (the tap is pre-endpoint-volume), so both the meter (display)
and the mix (AudioGainProvider.LoopbackGain, fixed in 5ead064) must scale it themselves.
ai.md's two 'loopback scales with the slider' sentences corrected in the same pass.

Test seam: VolumePushOverride (internal static, mirrors LayoutPathOverride pattern) so
the test never hijacks the machine's real volume. GameMeterHonestyTests (RealApp +
temp DB): unity -> hot/red, 20% -> ~1/5 width/green, push observed.
This commit is contained in:
2026-09-01 21:22:57 -07:00
parent 5ead064d54
commit 7f2bda8ed3
3 changed files with 96 additions and 7 deletions
+9 -5
View File
@@ -625,8 +625,10 @@ devices, no timers).
and let tracks play once then stop). The desktop/game bar's mute toggles `LocalGain` (0 or 1;
`UpdateTraxLocalGain` runs on every `GameMuted` change and on TRAX load), so muting kills TRAX
in the headphones — matching the stream, where it rides the loopback channel. The volume slider
drives system volume (`PushSystemVolume`), which scales the entire loopback signal including
TRAX — one knob controls everything. No third mixer input. The footer
drives system volume (`PushSystemVolume`) — which scales the HEADPHONES; the loopback capture is
pre-endpoint-volume (disproven-then-fixed 2026-09-01), so the STREAM follows the knob via
`AudioGainProvider.LoopbackGain = GameMuted ? 0 : GameAudioVolume`, and the game meter multiplies
its display by the same. One knob, three honest paths. No third mixer input. The footer
**TRAX** button lives **inside the game audio bar** (in the preview overlay), **left of the
"Desktop Audio" label** (the creator's pick — it rides the desktop channel, so the music control
sits with the desktop audio controls): status dot (**red** no track / **yellow** loaded stopped /
@@ -645,9 +647,11 @@ devices, no timers).
silently falling back to the default endpoint.**
- **Game audio bar** (desktop/game, **always visible** — TASK 4's `IGameAudioDetector` show/hide gating was **removed 2026-08-15**: the bar used to appear only while a full-screen game with sound was up, which kept hiding the creator's desktop meter; the detector stack is gone, desktop audio is just automatic WASAPI loopback): overlaid **at the bottom of the preview window** (bottom-center, dark translucent
chip, a mirror of the mic bar: meter + mute + volume slider). It's monitoring UI **in the preview
grid** — the meter is display-only, but its **volume slider drives system volume** (`PushSystemVolume`
scales the entire WASAPI endpoint, so both the live headphones and the loopback capture reflect the
slider) **and mute kills TRAX** (`MusicPlayer.LocalGain` = 0). Relabelled
grid** — the meter is display-only (fill = `ToDisplay(GameAudioLevel) × GameAudioVolume` — the ×volume
term was missing until 2026-09-01: loopback capture does NOT shrink with the endpoint, so an
unscaled meter left it pegged at 20% volume; `GameMeterHonestyTests` guards it), but its **volume
slider drives system volume** (`PushSystemVolume` — live headphones follow the knob; the capture
side is scaled in the mix, see TRAX note above) **and mute kills TRAX** (`MusicPlayer.LocalGain` = 0). Relabelled
**"Desktop Audio"** (TASK 8) since TRAX rides the same channel.
- **`WaveToFloat`** (pure, shared): WASAPI mix formats → interleaved float — IEEE float 32-bit direct,
PCM 16-bit normalized to -1..1, `WaveFormatExtensible` with the IEEE-float subformat GUID