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.
This commit is contained in:
2026-08-31 18:30:03 -07:00
parent 670fe3a10a
commit e8ff4df05f
12 changed files with 295 additions and 128 deletions
+9 -8
View File
@@ -1064,19 +1064,20 @@ The tasks below are ordered by dependency and risk. Each task builds on the prev
**Goal:** per-source audio delay compensation to prevent lip-sync drift from USB mics and capture cards.
### Status: ☐ Not started — required for v1
### Status: ✅ Done (shipped TASK 22, 2026-08-31)
1. ☐ `AudioSyncOffset` property on AudioSource models (default 0ms, range -500ms to +500ms)
2. ☐ Apply offset in `AudioMixer` — delay or advance audio samples relative to video
3. ☐ UI: offset slider per audio source (or global offset for simplicity)
4. ☐ Persist offset in `LayoutStore` (schema migration)
5. ☐ Tests: offset application, positive/negative delay, boundary values
1. ☑ `AudioSyncOffsetMs` on `MainViewModel` (global, default 0, range 0..500 ms) — positive-only: OBS's documented fix delays the audio so it lands on video when it runs ahead; advancing audio would need a video-side delay (out of the audio layer's scope, v1.1+).
2. ☑ Applied in `AudioMixer` — post-mix interleaved-stereo delay via the pure `AudioSyncDelay` line (`Services/Audio/AudioSyncDelay.cs`), fed through a `Func<int> syncOffsetMs` seam each mix tick.
3. ☑ UI: compact "SYNC" slider (0..500) on the mic bar with a status dot (green = no-op, amber = offset set) via `IntToSyncBrushConverter`.
4. ☑ Persisted in `LayoutStore.Settings` (`Audio.SyncOffsetMs`) via `LoadAudioSyncOffsetMs`/`SaveAudioSyncOffsetMs`; saved from `SaveLayoutNow`.
5. ☑ Tests: `AudioSyncDelayTests` — zero-delay identity, negative→0 clamp, >500 ms clamp to 500 ms, and 10 ms → 960 interleaved-sample shift.
### Design decisions
- **Global offset first** — one setting for all audio sources. Per-source is v1.1+.
- **Simple slider** — -500ms to +500ms, default 0. No numeric input needed.
- **Visual feedback** — show a "sync OK" indicator when offset is applied.
- **Positive-only (delay audio)** — the physically-correct direction (audio runs ahead of the video). True "advance" needs a video-side delay and is tracked as per-source/advance in v1.1 (line 1388).
- **Simple slider** — 0 to +500 ms, default 0. No numeric input needed.
- **Visual feedback** — "sync OK" status dot shows when an offset is dialled in.
---