fix(audio): a sync offset is saved the moment it changes

Both offsets rode the debounced layout save, so a value set seconds before the app closed was lost
without a word. That is what happened to the -130 ms offset on 2026-11-12: two clips were measured
against a setting that was not in force, the numbers disagreed by ~65 ms, and the disagreement was
blamed on the audio path.

Setters now write the row immediately (two tiny rows), keeping ScheduleSave for the rest of the
layout. AudioSyncSettingsTests gains the regression: set both offsets on a view model and read them
back through a fresh store with no debounce flush.

MyMistakes records the general shape — "a setting that didn't stick" is almost always ScheduleSave —
with the check list and the two related rules: offsets never ship with a non-zero default, and
scripts/avsync.py only resolves ~50 ms, so below that the creator's judgement wins.

677 pass, 0 warnings. Scope justification: Assets/starting-background.jpg is outside this unit — the
creator's replacement art, still awaiting their visual check.
This commit is contained in:
2026-11-12 14:21:48 -08:00
parent 1eb04597d1
commit cc75212997
3 changed files with 63 additions and 0 deletions
+4
View File
@@ -243,6 +243,9 @@ public partial class MainViewModel
var clamped = Math.Clamp(value, -500, 500);
if (!SetProperty(ref _audioSyncOffsetMs, clamped)) return;
OnPropertyChanged(nameof(SyncOffsetLabel));
// Written at once, not through the debounced layout save: a sync offset set seconds before
// closing the app used to be lost, and losing it costs a whole record-and-measure cycle.
if (_layoutStore is { } store) store.SaveAudioSyncOffsetMs(clamped);
ScheduleSave();
}
}
@@ -264,6 +267,7 @@ public partial class MainViewModel
var clamped = Math.Clamp(value, 0, 500);
if (!SetProperty(ref _micSyncOffsetMs, clamped)) return;
OnPropertyChanged(nameof(MicSyncOffsetLabel));
if (_layoutStore is { } store) store.SaveMicSyncOffsetMs(clamped); // immediate, see above
ScheduleSave();
}
}