Files
LlamaCasty/HANDOFF.md
T

5.4 KiB
Raw Blame History

HANDOFF — 2026-09-14 (signed audio sync SHIPPED + PUSHED: −500..+500, negative advances by eating the stream head; slider locked while live/recording)

Branch / Commit State

main HEAD = b22d08e (signed audio-sync, committed AND pushed to origin/main with the user's greenlight — 29 commits pushed in one go). Working tree clean.

DB now: Audio.SyncOffsetMs = 0 (confirmed via sqlite3 — the creator slid the sync to 0 and the write finally stuck; sound is correct at 0). The 0.54s residual in the 1128 take ≈ 300ms injected offset (leftover DB value) + ~240ms natural (partly webcam clap-quantization ±40–85ms, partly measurement).

⚠️ Branding (2026-09-14, creator-corrected): product = llamacasty, internals = ytLive

The product is llamacasty; the repo path, csproj AssemblyName/RootNamespace, DB/log paths (%APPDATA%\ytLlive\...), and most code names are the legacy ytLive/ytLlive. User-facing language must say "llamacasty"; code/assembly/repo names stay ytLive. Full detail in ai.md → Brand → "Product name vs repo/assembly branding".

✅ SHIPPED (this dirty tree) — signed audio sync, −500..+500

What: the audio-sync control is now a SIGNED offset. Positive = delay the mix (audio runs AHEAD of video — existing AudioSyncDelay behavior, unchanged and live-reactive). Negative = advance the audio (audio runs BEHIND video): OBS's "eat the head of the buffer" fix — the mixer drops the first |N| ms of the written stream at the pipe, re-anchoring the audio stream so every event lands |N| ms EARLIER relative to video.

Mechanism (AudioMixer): StartLive arms _advanceSamplesRemaining = |N| ms → samples at go-live (a negative offset can only eat the HEAD of the stream; it is armed once, not live-reactive). LiveLoopAsync skips min(budget, mixBuffer.Length) samples off each write head while the budget lasts — the pipe writer accepts a partial chunk via AsMemory(writeFrom). Positive path untouched (delay line still re-reads the Func every tick).

UI/plumbing:

  • MainViewModel.Audio.cs — clamp Math.Clamp(value, -500, 500), doc updated.
  • LayoutStore.Settings.cs — LoadAudioSyncOffsetMs/SaveAudioSyncOffsetMs clamp −500..500.
  • PreviewPane.xaml — label SYNC → "AUDIO SYNC", Minimum="-500", tooltip explains both directions (calibrate with a clap: clap late → negative; early → positive), and IsEnabled="{Binding IsEditMode}" — the slider locks during live AND recording (gun safety, same property IsRecording/StreamStatus already raise PropertyChanged for).
  • AudioSyncDelay unchanged (still clamps negative→0 internally; header doc updated to point at the mixer for the advance side).

Regression test (the ONE integration test for this change): StartLive_NegativeOffset_AdvancesAudio_ByDroppingTheStreamHead in AudioPipelineTests.cs — −40 ms advance (8-tick budget at 5ms interval), emits 6×0.9 fresh right after StartLive (≤ budget, so the head MUST be eaten) then a long 0.2 bed; asserts the wire max ≈ 0.2 (< 0.3). Fails loudly if −N no longer drops the head (0.9 leaks).

User directives (this session):

  • Signed −500..+500 with positive=delay / negative=advance, relabel "AUDIO SYNC", default 0.
  • Lock the sync control when live or recording (done — IsEditMode).
  • NOTE ONLY, no fix: the live recording is completely different from the "recording results" shown in Chat view (recorded in bugs.md — do not rediscover as a surprise).
  • Before 1.0: write a detailed USER-DOC tutorial on the audio-sync feature (see TASKS.md note; add it to the gold-pass/1.0 checklist). docs/ currently holds only the README image.

Take verification so far (ring-backlog fix)

Take ty-20260914-1128-0000-2.mp4 (1920×1080@60fps, 356 frames / 5.93s): audio clap RMS peak 3.260s; video diff-frame 163 → 2.717s; offset ≈ +0.54s — collapsed from +2.11/2.22s; the remaining ~300ms was the baked-in SyncOffsetMs=300 (now 0). Both streams start_time=0 — not an avoid_negative_ts artifact.

Open threads

  • Verify signed sync on device: re-run the clap take with a NEGATIVE offset to confirm the advance direction end-to-end (the regression test proves the mixer; a take proves the file).
  • Audio-silence verification — fixed code (724af14) confirmed; creator heard real audio.
  • Webcam MJPG missing / ~10–14Hz, layer SortOrder, truncation-with-dynamic-scenes — queued.
  • Sync control tutorial in user docs — REQUIRED before 1.0 (creator directive).

Landmines

  • testhost shares startup.log — filter by time.
  • cmd.exe /c "taskkill /F /IM ytLive.exe" (WSL double-slashes mangle) before rebuilds.
  • Build/tests: Windows dotnet host (/mnt/c/Program Files/dotnet/dotnet.exe). 0 warnings.
  • ffmpeg/ffprobe: /mnt/c/Program Files/Krita (x64)/bin/ with Windows paths.
  • MyMistakes.md has the audio/video sync measurement recipe (claps + cross-correlation) — grep it before re-deriving.
  • sqlite3 lives at /home/gramps/android-sdk/platform-tools/sqlite3 (WSL) for the DB at /mnt/c/Users/gramp/AppData/Roaming/ytLlive/ytLlive.db.

Next step

Open queue question: user-doc tutorial for the audio-sync feature (creator directive, REQUIRED before 1.0 — recorded in TASKS/task-22-audio-sync-offset.md). Pre-flight item to confirm next session: creator wanted to discuss whether other root-level md files (beyond TASKS.md) should be split one-topic-per-file like TASKS/.