Fix audio clock drift and adaptive jitter buffering
.NET port / test (macos-latest) (push) Canceled after 0s
.NET port / test (ubuntu-24.04) (push) Canceled after 0s
.NET port / test (windows-latest) (push) Canceled after 0s
.NET port / apple-client (push) Canceled after 0s

This commit is contained in:
2026-09-20 16:26:03 +02:00
parent 3ee0df11a9
commit dd811a0bb8
23 changed files with 401 additions and 61 deletions
@@ -14,23 +14,23 @@ internal sealed class SettingsController : UITableViewController
public override void ViewDidLoad() { base.ViewDidLoad(); TableView.RegisterClassForCellReuse(typeof(UITableViewCell), "setting"); }
private void Reload() => TableView.ReloadData();
public override nint NumberOfSections(UITableView tableView) => 5;
public override nint RowsInSection(UITableView tableView, nint section) => section switch { 0 => 4, 1 => 4, 2 => 5, 3 => model.Client?.Permissions is { } p && (p.IsAdmin || p.CanAdminAccounts) ? 1 : 0, _ => 2 };
public override nint RowsInSection(UITableView tableView, nint section) => section switch { 0 => 5, 1 => 4, 2 => 5, 3 => model.Client?.Permissions is { } p && (p.IsAdmin || p.CanAdminAccounts) ? 1 : 0, _ => 2 };
public override string? TitleForHeader(UITableView tableView, nint section) => section switch { 0 => "Audio", 1 => "Voice", 2 => "Notifications", 3 => "Administration", _ => "Server" };
public override UITableViewCell GetCell(UITableView tableView, NSIndexPath path)
{
UITableViewCell cell = tableView.DequeueReusableCell("setting", path); cell.AccessoryView = null; cell.Accessory = UITableViewCellAccessory.None;
string title = path.Section switch
{
0 => path.Row switch { 0 => "Audio preset", 1 => "Speaker output", 2 => "Advanced audio", _ => model.ScreenSharing ? "Stop sharing screen audio" : "Share screen audio" },
0 => path.Row switch { 0 => "Audio preset", 1 => "Speaker output", 2 => "Advanced audio", 3 => "Audio buffering", _ => model.ScreenSharing ? "Stop sharing screen audio" : "Share screen audio" },
1 => path.Row switch { 0 => "Input mode", 1 => $"VAD threshold: {model.Settings.VadThreshold:0.000}", 2 => $"Microphone volume: {model.Settings.InputGain:P0}", _ => "Microphone noise reduction" },
2 => path.Row switch { 0 => "Event sounds", 1 => $"Sound volume: {model.Settings.EventVolume:P0}", 2 => "Speak events", 3 => "Own voice activity sounds", _ => "Push-to-talk cue" },
3 => "Manage accounts", _ => path.Row == 0 ? "Disconnect" : "VoiceCat 0.0.1"
};
var content = cell.DefaultContentConfiguration; content.Text = title; content.SecondaryText = path.Section == 0 && path.Row == 0 ? IosAudioRouter.Shared.Preset.ToString() : path.Section == 1 && path.Row == 0 ? model.Settings.InputMode.ToString() : path.Section == 0 && path.Row == 3 && model.ScreenSharing ? "Sharing" : null; cell.ContentConfiguration = content; cell.AccessibilityLabel = title;
var content = cell.DefaultContentConfiguration; content.Text = title; content.SecondaryText = path.Section == 0 && path.Row == 0 ? IosAudioRouter.Shared.Preset.ToString() : path.Section == 1 && path.Row == 0 ? model.Settings.InputMode.ToString() : path.Section == 0 && path.Row == 3 ? $"{model.Settings.AudioBufferMilliseconds} ms" : path.Section == 0 && path.Row == 4 && model.ScreenSharing ? "Sharing" : null; cell.ContentConfiguration = content; cell.AccessibilityLabel = title + (content.SecondaryText is null ? "" : ", " + content.SecondaryText);
if (path.Section == 0 && path.Row == 1) cell.AccessoryView = Toggle(IosAudioRouter.Shared.ForceSpeaker, "Speaker output", (_, _) => IosAudioRouter.Shared.SetForceSpeaker(((UISwitch)cell.AccessoryView!).On));
else if (path.Section == 1 && path.Row == 3) cell.AccessoryView = Toggle(model.Settings.InputNoiseReduction, title, (_, _) => { model.Settings.InputNoiseReduction = ((UISwitch)cell.AccessoryView!).On; model.ApplyVoiceSettings(); });
else if (path.Section == 2 && path.Row is 0 or 2 or 3 or 4) { bool value = path.Row switch { 0 => model.Settings.EventSounds, 2 => model.Settings.SpokenEvents, 3 => model.Settings.SelfTalkSounds, _ => model.Settings.PushToTalkSound }; int row = path.Row; cell.AccessoryView = Toggle(value, title, (_, _) => { bool on = ((UISwitch)cell.AccessoryView!).On; if (row == 0) model.Settings.EventSounds = on; else if (row == 2) model.Settings.SpokenEvents = on; else if (row == 3) model.Settings.SelfTalkSounds = on; else model.Settings.PushToTalkSound = on; model.Save(); }); }
else if (path.Section == 0 && path.Row == 2 || path.Section == 3) cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
else if (path.Section == 0 && path.Row is 2 or 3 || path.Section == 3) cell.Accessory = UITableViewCellAccessory.DisclosureIndicator;
return cell;
}
public override async void RowSelected(UITableView tableView, NSIndexPath path)
@@ -39,7 +39,8 @@ internal sealed class SettingsController : UITableViewController
{
if (path.Section == 0 && path.Row == 0) Choice("Audio preset", Enum.GetValues<IosAudioPreset>().Select(value => value.ToString()).ToArray(), index => IosAudioRouter.Shared.SelectPreset(Enum.GetValues<IosAudioPreset>()[index]));
else if (path.Section == 0 && path.Row == 2) NavigationController?.PushViewController(new AdvancedAudioController(), true);
else if (path.Section == 0 && path.Row == 3) ShowScreenSharing();
else if (path.Section == 0 && path.Row == 3) Choice("Audio buffering", ["Low latency (20 ms)", "Balanced (40 ms)", "Stable (60 ms)"], index => { model.Settings.AudioBufferMilliseconds = index switch { 0 => 20, 2 => 60, _ => 40 }; model.ApplyVoiceSettings(); });
else if (path.Section == 0 && path.Row == 4) ShowScreenSharing();
else if (path.Section == 1 && path.Row == 0) Choice("Input mode", ["Voice activation", "Push to talk", "Always on"], index => { model.Settings.InputMode = (AudioInputMode)index; model.ApplyVoiceSettings(); });
else if (path.Section == 1 && path.Row == 1) Slider("Voice activation threshold", 0.001f, 0.1f, model.Settings.VadThreshold, value => { model.Settings.VadThreshold = value; model.ApplyVoiceSettings(); });
else if (path.Section == 1 && path.Row == 2) Slider("Microphone volume", 0, 4, model.Settings.InputGain, value => { model.Settings.InputGain = value; model.ApplyVoiceSettings(); });