Try to fix background glitching over long periods of time
This commit is contained in:
@@ -40,7 +40,12 @@ internal sealed class IosAudioEngine
|
||||
private bool inputProvided;
|
||||
private bool tapInstalled;
|
||||
private long captureCallbacks, capturedFrames, convertedFrames, rejectedFeeds, converterFailures, stereoFrames, stereoDifferentFrames;
|
||||
private long renderCallbacks, lastRenderTimestamp;
|
||||
internal bool IsConnected { get; private set; }
|
||||
internal bool IsRunning => engine?.Running == true;
|
||||
// The watchdog compares this across ticks: a graph that stops calling back while it still
|
||||
// reports Running leaves the whole device-clocked pipeline frozen until it is rebuilt.
|
||||
internal long RenderCallbacks => Interlocked.Read(ref renderCallbacks);
|
||||
internal int BufferMilliseconds { get => playbackRing.BufferMilliseconds; set => playbackRing.BufferMilliseconds = value; }
|
||||
|
||||
internal void StartListening(VoiceCatClient owner)
|
||||
@@ -201,15 +206,24 @@ internal sealed class IosAudioEngine
|
||||
// This callback is the cadence iOS keeps exact while the app is backgrounded or the device
|
||||
// is locked, so it owns both managed 20 ms hands-offs: capture into the sender and one mix
|
||||
// cycle per 20 ms of render demand. Both run allocation-free and without locks or I/O.
|
||||
Interlocked.Increment(ref renderCallbacks);
|
||||
long now = System.Diagnostics.Stopwatch.GetTimestamp(), previous = lastRenderTimestamp;
|
||||
lastRenderTimestamp = now;
|
||||
VoiceCatClient? owner = Volatile.Read(ref client);
|
||||
if (owner is null || !IsConnected) microphoneCredit = 0;
|
||||
else
|
||||
{
|
||||
if (previous != 0 && (now - previous) * 1000.0 / System.Diagnostics.Stopwatch.Frequency > 100)
|
||||
{
|
||||
Volatile.Read(ref microphone)?.Ring.Resynchronize();
|
||||
playbackRing.Resynchronize(); owner.Audio.ResynchronizeInputs(); microphoneCredit = 0;
|
||||
}
|
||||
microphoneCredit += frames;
|
||||
while (microphoneCredit >= 960) { microphoneCredit -= 960; PumpMicrophoneChunk(owner); }
|
||||
// Top the mix ring up to cover this callback plus its configured target so the read
|
||||
// below never starves and the buffer keeps its chosen buffering latency.
|
||||
int deficit = frames + playbackRing.TargetFrames - playbackRing.CountFrames;
|
||||
// below never starves and the buffer keeps its chosen buffering latency. Catch-up is
|
||||
// capped at one extra cycle so a refill cannot overrun this callback's deadline.
|
||||
int deficit = Math.Min(frames + playbackRing.TargetFrames - playbackRing.CountFrames, frames + 960);
|
||||
for (int produced = 0; produced < deficit; produced += 960) owner.Audio.RunCycle();
|
||||
}
|
||||
Span<short> input = renderScratch.AsSpan(0, requested);
|
||||
@@ -245,7 +259,7 @@ internal sealed class IosAudioEngine
|
||||
if (tapInstalled) old.InputNode.RemoveTapOnBus(0);
|
||||
old.Stop(); if (source is not null) old.DetachNode(source); old.Dispose();
|
||||
}
|
||||
tapInstalled = false; pendingInput = null; inputProvider = null;
|
||||
tapInstalled = false; pendingInput = null; inputProvider = null; lastRenderTimestamp = 0; microphoneCredit = 0;
|
||||
convertedMicrophone?.Dispose(); convertedMicrophone = null;
|
||||
microphoneConverter?.Dispose(); microphoneConverter = null;
|
||||
microphoneFormat?.Dispose(); microphoneFormat = null;
|
||||
|
||||
Reference in New Issue
Block a user