Try to fix background glitching over long periods of time
Build and test / test (macos-latest) (push) Waiting to run
Build and test / test (ubuntu-24.04) (push) Waiting to run
Build and test / test (windows-latest) (push) Waiting to run
Build and test / apple-client (push) Waiting to run

This commit is contained in:
2026-09-24 13:31:16 +02:00
parent 186fe6dbb6
commit fb740bcfb2
7 changed files with 129 additions and 14 deletions
@@ -164,6 +164,38 @@ public class PublishServerScriptTests
Assert.Contains("deviceClockedAudio: true", model);
}
[Fact]
public async Task IosRecoversBackgroundAudioWithoutForegrounding()
{
string root = FindRoot();
string microphone = await File.ReadAllTextAsync(Path.Combine(
root, "clients", "apple", "VoiceCat.iOS", "IosAudioEngine.cs"));
string router = await File.ReadAllTextAsync(Path.Combine(
root, "clients", "apple", "VoiceCat.iOS", "IosAudioRouter.cs"));
// The render callback is the only clock for the device-clocked pipeline, so a graph that
// stops while backgrounded froze capture, mix and send until the app was foregrounded.
Assert.Contains("internal long RenderCallbacks", microphone);
Assert.Contains("internal bool IsRunning", microphone);
Assert.Contains("private void TickWatchdog()", router);
Assert.Contains("IosAudioEngine.Shared.RenderCallbacks", router);
Assert.Contains("IosAudioEngine.Shared.IsRunning", router);
Assert.Contains("IosAudioEngine.Shared.Reconfigure()", router);
// SetActive fails for as long as an interruption is in force, so Began suppresses
// recovery and the watchdog retries the rebuild that Ended asks for.
Assert.Contains("AVAudioSessionInterruptionType.Began", router);
Assert.Contains("interrupted = true", router);
Assert.Contains("internal void ResumeForeground()", router);
// A stalled render callback resynchronizes its own rings, and a refill is bounded so it
// cannot overrun the callback deadline that it is recovering from.
Assert.Contains("owner.Audio.ResynchronizeInputs()", microphone);
Assert.Contains("playbackRing.Resynchronize()", microphone);
Assert.Contains("Ring.Resynchronize()", microphone);
Assert.Contains("Math.Min(frames + playbackRing.TargetFrames - playbackRing.CountFrames, frames + 960)", microphone);
}
[Fact]
public async Task IosAudioCommitsInputRouteBeforeChannelsAndVoiceProcessingBeforeConnections()
{