Improve iOS voice stability and user audio controls
This commit is contained in:
@@ -24,9 +24,10 @@ internal sealed class IosAudioEngine
|
||||
{
|
||||
internal readonly uint StreamId = streamId;
|
||||
internal readonly int Channels = channels;
|
||||
internal readonly PcmRing Ring = new(131_072);
|
||||
internal bool Primed;
|
||||
internal int TargetFrames = 3;
|
||||
// Capture hardware and the managed 20 ms sender have independent clocks. Correct
|
||||
// their small rate difference before the queue eventually reaches its hard edge.
|
||||
// RemoteIO can deliver capture in 100 ms bursts, so retain one burst of headroom.
|
||||
internal readonly AdaptivePcmBuffer Ring = new(channels, 120, 65_536);
|
||||
}
|
||||
private MicrophoneRoute? microphone;
|
||||
private readonly short[] microphoneFrame = new short[960 * 2];
|
||||
@@ -77,13 +78,7 @@ internal sealed class IosAudioEngine
|
||||
|
||||
private static MicrophoneRoute CreateMicrophoneRoute(uint streamId, int channels)
|
||||
{
|
||||
var route = new MicrophoneRoute(streamId, Math.Clamp(channels, 1, 2));
|
||||
// Physical RemoteIO capture is commonly delivered in 100 ms bursts. Starting its 20 ms
|
||||
// pacer with only the VPIO-oriented 60 ms cushion guarantees several underruns after every
|
||||
// mono/stereo rebuild before the adaptive path catches up. Prime one full burst plus one
|
||||
// frame for non-VPIO routes; VPIO remains at the proven low-latency three-frame cushion.
|
||||
route.TargetFrames = IosAudioRouter.Shared.UsesVoiceProcessing ? 3 : 6;
|
||||
return route;
|
||||
return new MicrophoneRoute(streamId, Math.Clamp(channels, 1, 2));
|
||||
}
|
||||
internal bool EnsureRunning()
|
||||
{
|
||||
@@ -198,23 +193,10 @@ internal sealed class IosAudioEngine
|
||||
if (route is not null && owner is not null)
|
||||
{
|
||||
int required = 960 * route.Channels;
|
||||
int completeFrames = route.Ring.Count / required;
|
||||
if (!route.Primed)
|
||||
{
|
||||
if (completeFrames >= route.TargetFrames) route.Primed = true;
|
||||
}
|
||||
if (route.Primed)
|
||||
{
|
||||
if (completeFrames == 0)
|
||||
{
|
||||
route.Primed = false;
|
||||
route.TargetFrames = Math.Min(route.TargetFrames + 1, 6);
|
||||
}
|
||||
else if (route.Ring.Read(microphoneFrame.AsSpan(0, required)) == required &&
|
||||
ReferenceEquals(route, Volatile.Read(ref microphone)) &&
|
||||
!owner.Audio.FeedPcm(route.StreamId, microphoneFrame.AsSpan(0, required), route.Channels))
|
||||
Interlocked.Increment(ref rejectedFeeds);
|
||||
}
|
||||
if (route.Ring.Read(microphoneFrame.AsSpan(0, required)) == required &&
|
||||
ReferenceEquals(route, Volatile.Read(ref microphone)) &&
|
||||
!owner.Audio.FeedPcm(route.StreamId, microphoneFrame.AsSpan(0, required), route.Channels))
|
||||
Interlocked.Increment(ref rejectedFeeds);
|
||||
}
|
||||
deadline += System.Diagnostics.Stopwatch.Frequency / 50;
|
||||
while (true)
|
||||
|
||||
Reference in New Issue
Block a user