using System; using Xunit; using ytLive.Services.Audio; namespace ytLive.Tests; /// /// The mix's sample count comes from the clock, not from a tick count — the fix for the 2026-11-12 /// drift (video 9.888 s against audio 10.112 s on a 9.9 s recording: the audio ran 2.1% long and the /// sound slid ~200 ms behind the lips by the end of a ten-second clip). /// public class AudioClockTests { [Fact] public void The_mix_follows_the_wall_clock_even_when_the_tick_runs_short() { const int rate = 48000; const int maxChunk = rate / 5; // The measured scheduler: iterations land every 9.8 ms, not the requested 10 ms. var tickSeconds = 0.0098; long produced = 0; long naive = 0; var ticks = 0; var elapsed = 0.0; while (elapsed < 10.0) { var frames = AudioClock.FramesDue(elapsed, produced, rate, maxChunk); produced += frames; naive += (int)Math.Round(rate * 0.010); // what the old "480 per tick" loop wrote ticks++; elapsed += tickSeconds; } var expected = (long)Math.Round(elapsed * rate); // The old loop drifted by the tick-rate error — about 2% of ten seconds — and kept drifting. Assert.True(naive - expected > rate * 0.01, $"the fixed-chunk loop should reproduce the drift (naive {naive} vs expected {expected})"); // Asking the clock keeps the produced count on the clock, within one chunk of payment. Assert.True(Math.Abs(produced - expected) <= maxChunk, $"clock-driven count must track the clock (produced {produced} vs expected {expected})"); // And a stalled loop pays its debt off gradually rather than dumping it in one write. Assert.Equal(maxChunk, AudioClock.FramesDue(5.0, 0, rate, maxChunk)); Assert.Equal(0, AudioClock.FramesDue(5.0, rate * 5, rate, maxChunk)); // Audio that never reached the encoder stays owed. Counting it would shorten the track by the // start-up gap — the mirror image of the drift this clock exists to prevent (27 dropped start-up // writes made the first clock-driven recording 226 ms short). var produced2 = 0L; for (var i = 0; i < 27; i++) produced2 = AudioClock.Advance(produced2, 480, wrote: false); Assert.Equal(0, produced2); Assert.Equal(480, AudioClock.Advance(produced2, 480, wrote: true)); } }