feat(audio): microphone-only sync delay, on by default at 175 ms
The webcam's frames reach the compositor later than the mic's samples reach the audio pipe, so the voice runs ahead of the lips. The existing lip-sync offset could not fix that: it delays the WHOLE mix, which drags desktop and alert audio — neither of which has a camera to follow — out of sync in the process. So the delay line learned channels (mono for a source, stereo for the mix, offset always in milliseconds so the frame count is what stays constant), and FillAndMix runs a second line over micChunk before the mix. Slider-side: presets off / 150 / 200 in the LIP-SYNC bay under the whole-mix control, persisted as Audio.MicSyncOffsetMs. Default 175 ms is the creator's own number for a C920 pipeline (they asked for +150..+200). It is a setting, not a constant, because the right figure belongs to the camera and the machine — a faster camera wants less, no camera wants none. Both delay lines are reconfigured every tick, so the 2026-09-12 trap still applies and is documented at both sites: a Configure that reallocates discards the buffered voice every 10 ms. 673 pass, 0 warnings. Ear check pending: this is an audio change.
This commit is contained in:
@@ -74,6 +74,7 @@ public partial class MainViewModel
|
||||
private bool _gameMuted;
|
||||
private double? _gameVolumeBeforeMute;
|
||||
private int _audioSyncOffsetMs;
|
||||
private int _micSyncOffsetMs = LayoutStore.DefaultMicSyncOffsetMs;
|
||||
private AudioGainProvider? _gainProvider;
|
||||
private readonly IMicrophoneEnumerator _microphoneEnumerator;
|
||||
private readonly AudioMixer _audioMixer;
|
||||
@@ -246,8 +247,30 @@ public partial class MainViewModel
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Master strip ────────────────────────────────────────────────────────
|
||||
/// <summary>
|
||||
/// Microphone-only sync delay in milliseconds (0..500, default 175). A camera's frames reach the
|
||||
/// compositor later than the mic's samples reach the audio pipe, so by default the voice runs
|
||||
/// AHEAD of the lips; delaying the mic alone closes the gap without touching desktop or alert audio,
|
||||
/// which have no camera to follow. The creator can dial it to taste in the audio dock.
|
||||
///
|
||||
/// <para>175 ms is the creator's own measurement for a C920 pipeline (they asked for +150..+200);
|
||||
/// it is a persisted setting, not a constant, because the right number is the machine's, not ours.</para>
|
||||
/// </summary>
|
||||
public int MicSyncOffsetMs
|
||||
{
|
||||
get => _micSyncOffsetMs;
|
||||
set
|
||||
{
|
||||
var clamped = Math.Clamp(value, 0, 500);
|
||||
if (!SetProperty(ref _micSyncOffsetMs, clamped)) return;
|
||||
OnPropertyChanged(nameof(MicSyncOffsetLabel));
|
||||
ScheduleSave();
|
||||
}
|
||||
}
|
||||
|
||||
public string MicSyncOffsetLabel => _micSyncOffsetMs == 0 ? "off" : $"+{_micSyncOffsetMs} ms";
|
||||
|
||||
// ─── Master strip ────────────────────────────────────────────────────────
|
||||
private bool _monoOutput;
|
||||
|
||||
/// <summary>Collapse the output to mono. Persisted, because a creator who needs it needs it on
|
||||
|
||||
Reference in New Issue
Block a user