Commit Graph
8 Commits
Author SHA1 Message Date
gramps ec01a7deb8 design: the last VM-side colours come from tokens (ThemeBrush)
The state colours the view-models hand to bindings — top bar, REC/ON-AIR dots, health banner,
meter bands, toast tints — were hex literals in C#. They now resolve from Themes/Tokens.xaml
through `Helpers/ThemeBrush`, so the values live in one place. Without an Application (unit tests)
the helper returns a neutral grey: visibly wrong rather than silently plausible.

Four tokens the code needed and the document lacked, added to DESIGN.md (so the doc-is-the-contract
guard demanded their brushes): info-container #3a3b52, success-container #1d5c38,
warning-container #b8860b, test-container #9c6f1c (the test-stream gold bar).

Also fixed by this: the top bar still painted the RETIRED brand red #e94560 when live — the
standard's #ff5451 now, and `OnAirSignTests` (which pinned the old value) says so. BarOn/BarOff
became properties rather than static fields, because a static field would resolve tokens before
the Application exists.

Verified: DesignTokensTests, GameMeterHonestyTests, NotificationAreaIntegrationTests,
OnAirSignTests, SessionTeardownTests — 38 tests green.
2026-10-30 09:58:52 -07:00
gramps c885bbd782 design: the code-set state colours now agree with the standard
Last inconsistency from the sweep: several VM-side state colours are still literals, and after
the palette moved they disagreed with everything around them — the top bar's LIVE state would
have painted the retired pink-red while the tile beside it used the standard red.

Values aligned (tokenisation of these sites is its own unit — they are strings bound to
Background/Foreground, so the fix is a VM->Brush refactor, not a find/replace):
- live / on-air / recorder dot: #e94560 -> #ff5451 (the standard red)
- error banner: #8f1f1f -> #93000a (error-container)
- audio meter bands follow the doc's stated thresholds: nominal cyan #38bdf8, caution amber
  #ffb95f, peak #ff5451 — they were green/yellow/red, which contradicted DESIGN.md's own table

GameMeterHonestyTests pinned the old band colours; its intent (the meter scales by volume) is
unchanged, so the two colour expectations now name the new bands.
2026-10-30 08:30:33 -07:00
gramps b140024ebf Audio: speech leveler (auto gain to -20 dBFS speech, -6..+20 dB, gated) between gate and compressor; honest mic meter (drop the +10 dB display boost and x1.2). Creator upload measured -28.5 LUFS; same audio through the new chain: -17.9 LUFS / -0.7 dBFS peak (Unit: audio loudness) 2026-10-09 13:27:53 -07:00
gramps b22d08eca6 feat: signed A/V sync offset (−500..+500), negative advances by eating live stream head (OBS eat-head semantics)
Positive offsets still delay the whole mix via the delay line (lip-sync fix);
negative offsets now ARM once at StartLive and drop |N| ms off the pipe's write
head so audio events land earlier when audio runs BEHIND video. Slider relabeled
AUDIO SYNC, Min −500, locked while live/recording (IsEditMode). LayoutStore and
VM clamp to −500..500.

OBS reference for eat-the-head negative sync: https://obsproject.com/kb/obs-studio/buffering-time (negative sync values pull audio earlier by discarding buffered player audio).

Test: StartLive_NegativeOffset_AdvancesAudio_ByDroppingTheStreamHead (6x0.9 head
must be eaten before 0.2 bed reaches the wire).
2026-09-14 12:30:44 -07:00
gramps 7f2bda8ed3 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.
2026-09-01 21:22:57 -07:00
gramps f89b9f9ffa docs: state the default-device audio assumption (no device probing, no OS-level audio debugging) 2026-08-31 18:52:01 -07:00
gramps e8ff4df05f TASK 22: global audio sync offset (positive delay at the mixer out)
Adds AudioSyncOffsetMs (0..500ms, default 0) that delays the whole
interleaved-stereo mix so audio lands on the video when it runs ahead —
OBS's documented lip-sync fix. Positive-only: advancing audio needs a
video-side delay and is out of the audio layer's scope.

- Services/Audio/AudioSyncDelay.cs: pure delay line, flushed on Configure
- AudioMixer: Func<int> syncOffsetMs seam + delay applied post-limiter
- LayoutStore.Settings + MainViewModel.Audio/VM: load/save + binding
- PreviewPane mic bar: SYNC slider + status dot (IntToSyncBrushConverter)
- AudioSyncDelayTests: identity, negative/beyond-500 clamps, 10ms→960 samples

Reference (external scan): https://obs-versions.com/blog/how-to-fix-audio-delay-on-obs
(audio ahead => positive delay). Verified: build 0 warnings; 3/3 delay tests pass.
2026-08-31 18:30:03 -07:00
gramps c1126a52b9 refactor: split Audio out of MainViewModel — Commit 6/12
MainViewModel.Audio.cs: mic bar (AudioLevel/MeterFillWidth/MeterBrush,
MicVolume/MicMuted, ToggleMicMute, PickMicrophone), mic status dot
(MicStatus/MicStatusBrush/MicStatusToolTip), game bar
(GameAudioLevel/GameMeterFillWidth/GameMeterBrush, GameAudioVolume,
GameMuted, ToggleGameMute), PushSystemVolume, mixer event handlers
(OnMicLevelChanged/OnLoopbackLevelChanged/OnMicConnected/OnMicFailed),
StartMicCaptureAsync, command properties, 18 fields.

Kept in core: TraxIdleBrush, TRAX code block, ctor wiring
(mixer init + event hookup), timer construction.

Verify: 0 warnings, 246 passed, 2 known failures only.
2026-08-30 08:17:55 -07:00