Mic input gain (was 3x) and per-user receive gain (was 2x) had asymmetric boost ceilings. Raise both, plus the desktop aux input gain (was 3x), to a uniform 4x (400%) on macOS, iOS, and Windows. The master Output volume slider is unchanged (still 1x). No core changes needed: the C ABI only clamps negatives, so the ceilings live entirely in the client UI sliders. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
550 lines
21 KiB
C#
550 lines
21 KiB
C#
using System.ComponentModel;
|
||
using VoiceCat.App.Audio;
|
||
using VoiceCat.App.Models;
|
||
using VoiceCat.Interop;
|
||
|
||
namespace VoiceCat.App.Forms;
|
||
|
||
/// <summary>
|
||
/// Edits audio input settings: device selection, transmission mode (VAD/PTT/always-on),
|
||
/// VAD sensitivity, mic gain, and PTT key. Changes are applied live to the client for
|
||
/// immediate feedback; Cancel reverts them.
|
||
/// </summary>
|
||
public sealed class AudioSettingsForm : Form
|
||
{
|
||
private readonly VoiceCatClient _client;
|
||
private readonly VoiceSettings _settings;
|
||
private readonly uint _micStreamId;
|
||
|
||
// Aux-stream live-apply callbacks, supplied by MainForm (which owns the aux capture). Null
|
||
// when not connected — the controls still edit settings, just without live effect.
|
||
private readonly Action<bool>? _applyAuxEnabled;
|
||
private readonly Action<string?>? _applyAuxDevice;
|
||
private readonly Action<float>? _applyAuxGain;
|
||
|
||
// Snapshot of original values so Cancel can restore them
|
||
private readonly string? _origDeviceId;
|
||
private readonly VcInputMode _origMode;
|
||
private readonly int _origVadSlider;
|
||
private readonly int _origMicGain;
|
||
private readonly bool _origMicNoiseReduction;
|
||
private readonly bool _origStereoMic;
|
||
private readonly Keys _origPttKey;
|
||
private readonly bool _origAuxEnabled;
|
||
private readonly string? _origAuxDeviceId;
|
||
private readonly int _origAuxGain;
|
||
|
||
private readonly ComboBox _cboDevice;
|
||
private readonly Button _btnRefresh;
|
||
private readonly RadioButton _radioVad;
|
||
private readonly RadioButton _radioPtt;
|
||
private readonly RadioButton _radioAlwaysOn;
|
||
private readonly Label _lblPttKey;
|
||
private readonly Button _btnChangePtt;
|
||
private readonly Label _lblSensitivity;
|
||
private readonly TrackBar _trkVad;
|
||
private readonly TrackBar _trkGain;
|
||
private readonly CheckBox _chkNoiseReduction;
|
||
private readonly CheckBox _chkStereoMic;
|
||
private readonly CheckBox _chkAux;
|
||
private readonly Label _lblAuxDevice;
|
||
private readonly ComboBox _cboAuxDevice;
|
||
private readonly Button _btnAuxRefresh;
|
||
private readonly Label _lblAuxGain;
|
||
private readonly TrackBar _trkAuxGain;
|
||
|
||
private Keys _pttKey;
|
||
|
||
public AudioSettingsForm(VoiceCatClient client, VoiceSettings settings, uint micStreamId,
|
||
Action<bool>? applyAuxEnabled = null, Action<string?>? applyAuxDevice = null,
|
||
Action<float>? applyAuxGain = null)
|
||
{
|
||
_client = client;
|
||
_settings = settings;
|
||
_micStreamId = micStreamId;
|
||
_applyAuxEnabled = applyAuxEnabled;
|
||
_applyAuxDevice = applyAuxDevice;
|
||
_applyAuxGain = applyAuxGain;
|
||
_pttKey = (Keys)settings.PttKey;
|
||
|
||
_origDeviceId = settings.InputDeviceId;
|
||
_origMode = (VcInputMode)settings.InputMode;
|
||
_origVadSlider = settings.VadThresholdSlider;
|
||
_origMicGain = settings.MicGain;
|
||
_origMicNoiseReduction = settings.MicNoiseReduction;
|
||
_origStereoMic = settings.StereoMic;
|
||
_origPttKey = _pttKey;
|
||
_origAuxEnabled = settings.AuxEnabled;
|
||
_origAuxDeviceId = settings.AuxDeviceId;
|
||
_origAuxGain = settings.AuxGain;
|
||
|
||
Text = "Audio settings";
|
||
FormBorderStyle = FormBorderStyle.FixedDialog;
|
||
MaximizeBox = false;
|
||
MinimizeBox = false;
|
||
StartPosition = FormStartPosition.CenterParent;
|
||
AutoScaleMode = AutoScaleMode.Font;
|
||
ClientSize = new Size(420, 611);
|
||
|
||
// ── Device row ────────────────────────────────────────────────────────
|
||
var lblDevice = new Label
|
||
{
|
||
Text = "Input &device:",
|
||
Location = new Point(12, 16),
|
||
AutoSize = true,
|
||
};
|
||
|
||
_cboDevice = new ComboBox
|
||
{
|
||
Location = new Point(12, 36),
|
||
Width = 300,
|
||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||
DisplayMember = "Name",
|
||
ValueMember = "Id",
|
||
AccessibleName = "Input device",
|
||
AccessibleDescription = "Select which microphone or audio input device to use.",
|
||
};
|
||
_cboDevice.SelectedIndexChanged += CboDevice_SelectedIndexChanged;
|
||
|
||
_btnRefresh = new Button
|
||
{
|
||
Text = "&Refresh",
|
||
Location = new Point(320, 34),
|
||
Size = new Size(80, 26),
|
||
};
|
||
_btnRefresh.Click += (_, _) => LoadDevices();
|
||
|
||
// ── Transmission mode ─────────────────────────────────────────────────
|
||
var lblMode = new Label
|
||
{
|
||
Text = "Transmission mode:",
|
||
Location = new Point(12, 76),
|
||
AutoSize = true,
|
||
};
|
||
|
||
_radioVad = new RadioButton
|
||
{
|
||
Text = "&Voice activation",
|
||
Location = new Point(20, 96),
|
||
AutoSize = true,
|
||
};
|
||
_radioVad.CheckedChanged += RadioMode_CheckedChanged;
|
||
|
||
_lblSensitivity = new Label
|
||
{
|
||
Text = "Sensitivity:",
|
||
Location = new Point(36, 122),
|
||
AutoSize = true,
|
||
};
|
||
_trkVad = new TrackBar
|
||
{
|
||
Location = new Point(36, 140),
|
||
Size = new Size(200, 45),
|
||
Minimum = 1,
|
||
Maximum = 100,
|
||
TickFrequency = 10,
|
||
SmallChange = 1,
|
||
LargeChange = 10,
|
||
Value = Math.Clamp(settings.VadThresholdSlider, 1, 100),
|
||
};
|
||
_trkVad.AccessibleName = "VAD sensitivity";
|
||
_trkVad.AccessibleDescription =
|
||
"Voice detection sensitivity. Higher = more sensitive. Range 1–100.";
|
||
_trkVad.Scroll += TrkVad_Scroll;
|
||
|
||
_radioPtt = new RadioButton
|
||
{
|
||
Text = "&Push to talk",
|
||
Location = new Point(20, 192),
|
||
AutoSize = true,
|
||
};
|
||
_radioPtt.CheckedChanged += RadioMode_CheckedChanged;
|
||
|
||
_lblPttKey = new Label
|
||
{
|
||
Location = new Point(140, 194),
|
||
AutoSize = true,
|
||
};
|
||
|
||
_btnChangePtt = new Button
|
||
{
|
||
Text = "Change key...",
|
||
Location = new Point(200, 189),
|
||
Size = new Size(105, 26),
|
||
};
|
||
_btnChangePtt.Click += BtnChangePtt_Click;
|
||
|
||
_radioAlwaysOn = new RadioButton
|
||
{
|
||
Text = "A&lways on",
|
||
Location = new Point(20, 224),
|
||
AutoSize = true,
|
||
};
|
||
_radioAlwaysOn.CheckedChanged += RadioMode_CheckedChanged;
|
||
|
||
// ── Mic gain ──────────────────────────────────────────────────────────
|
||
var lblGain = new Label
|
||
{
|
||
Text = "Microphone &volume:",
|
||
Location = new Point(12, 268),
|
||
AutoSize = true,
|
||
};
|
||
_trkGain = new TrackBar
|
||
{
|
||
Location = new Point(12, 288),
|
||
Size = new Size(200, 45),
|
||
Minimum = 0,
|
||
Maximum = 400,
|
||
TickFrequency = 25,
|
||
SmallChange = 5,
|
||
LargeChange = 25,
|
||
Value = Math.Clamp(settings.MicGain, 0, 400),
|
||
};
|
||
_trkGain.AccessibleName = "Microphone volume";
|
||
_trkGain.AccessibleDescription =
|
||
"Boost a quiet microphone. 100 is unity gain; range 0–400 percent.";
|
||
_trkGain.Scroll += TrkGain_Scroll;
|
||
|
||
// ── Mic noise reduction ───────────────────────────────────────────────
|
||
// Send-side RNNoise denoise of the mic stream. MIC-only (the core's NR runs before
|
||
// the input gain and VAD/PTT gate; aux/screen are excluded). One pass for all listeners.
|
||
_chkNoiseReduction = new CheckBox
|
||
{
|
||
Text = "Noise &reduction (RNNoise)",
|
||
Location = new Point(12, 340),
|
||
AutoSize = true,
|
||
Checked = settings.MicNoiseReduction,
|
||
AccessibleName = "Microphone noise reduction",
|
||
AccessibleDescription =
|
||
"RNNoise denoising of your microphone. Cleans your signal for everyone listening.",
|
||
};
|
||
_chkNoiseReduction.CheckedChanged += ChkNoiseReduction_CheckedChanged;
|
||
|
||
// ── Stereo microphone ─────────────────────────────────────────────────
|
||
// Opens the mic capture device in stereo (interleaved L/R) instead of mono. Real stereo
|
||
// only reaches the wire on a stereo channel; the core folds a stereo mic to mono on a mono
|
||
// channel. Toggling while connected restarts the capture device (vc_audio_restart) so the
|
||
// new channel count takes effect immediately.
|
||
_chkStereoMic = new CheckBox
|
||
{
|
||
Text = "&Stereo microphone",
|
||
Location = new Point(12, 364),
|
||
AutoSize = true,
|
||
Checked = settings.StereoMic,
|
||
AccessibleName = "Stereo microphone",
|
||
AccessibleDescription =
|
||
"Capture your microphone in stereo. Only transmitted in stereo on a stereo channel.",
|
||
};
|
||
_chkStereoMic.CheckedChanged += ChkStereoMic_CheckedChanged;
|
||
|
||
// ── Aux input stream ──────────────────────────────────────────────────
|
||
// A second outgoing stream from another hardware input device (e.g. line-in / aux),
|
||
// captured client-side and fed to the core. Device + volume only — aux is always-on
|
||
// (the core never gates AUX_DEVICE on VAD/PTT).
|
||
_chkAux = new CheckBox
|
||
{
|
||
Text = "&Aux stream (second input device)",
|
||
Location = new Point(12, 404),
|
||
AutoSize = true,
|
||
Checked = settings.AuxEnabled,
|
||
AccessibleName = "Enable aux input stream",
|
||
AccessibleDescription =
|
||
"Transmit a second hardware input device alongside your microphone.",
|
||
};
|
||
_chkAux.CheckedChanged += ChkAux_CheckedChanged;
|
||
|
||
_lblAuxDevice = new Label
|
||
{
|
||
Text = "Aux d&evice:",
|
||
Location = new Point(12, 434),
|
||
AutoSize = true,
|
||
};
|
||
_cboAuxDevice = new ComboBox
|
||
{
|
||
Location = new Point(12, 454),
|
||
Width = 300,
|
||
DropDownStyle = ComboBoxStyle.DropDownList,
|
||
DisplayMember = "Name",
|
||
ValueMember = "Id",
|
||
AccessibleName = "Aux input device",
|
||
AccessibleDescription = "Select the second audio input device to transmit.",
|
||
};
|
||
_cboAuxDevice.SelectedIndexChanged += CboAuxDevice_SelectedIndexChanged;
|
||
|
||
_btnAuxRefresh = new Button
|
||
{
|
||
Text = "Re&fresh",
|
||
Location = new Point(320, 452),
|
||
Size = new Size(80, 26),
|
||
};
|
||
_btnAuxRefresh.Click += (_, _) => LoadAuxDevices();
|
||
|
||
_lblAuxGain = new Label
|
||
{
|
||
Text = "Aux vo&lume:",
|
||
Location = new Point(12, 490),
|
||
AutoSize = true,
|
||
};
|
||
_trkAuxGain = new TrackBar
|
||
{
|
||
Location = new Point(12, 510),
|
||
Size = new Size(200, 45),
|
||
Minimum = 0,
|
||
Maximum = 400,
|
||
TickFrequency = 25,
|
||
SmallChange = 5,
|
||
LargeChange = 25,
|
||
Value = Math.Clamp(settings.AuxGain, 0, 400),
|
||
};
|
||
_trkAuxGain.AccessibleName = "Aux volume";
|
||
_trkAuxGain.AccessibleDescription =
|
||
"Volume of the aux input stream. 100 is unity gain; range 0–400 percent.";
|
||
_trkAuxGain.Scroll += TrkAuxGain_Scroll;
|
||
|
||
// ── OK / Cancel ───────────────────────────────────────────────────────
|
||
var btnOk = new Button
|
||
{
|
||
Text = "&OK",
|
||
DialogResult = DialogResult.OK,
|
||
Location = new Point(228, 572),
|
||
Size = new Size(80, 27),
|
||
};
|
||
var btnCancel = new Button
|
||
{
|
||
Text = "&Cancel",
|
||
DialogResult = DialogResult.Cancel,
|
||
Location = new Point(316, 572),
|
||
Size = new Size(80, 27),
|
||
};
|
||
|
||
AcceptButton = btnOk;
|
||
CancelButton = btnCancel;
|
||
|
||
btnOk.Click += (_, _) =>
|
||
{
|
||
_settings.Save();
|
||
};
|
||
FormClosing += AudioSettingsForm_FormClosing;
|
||
|
||
Controls.AddRange([
|
||
lblDevice, _cboDevice, _btnRefresh,
|
||
lblMode, _radioVad, _lblSensitivity, _trkVad,
|
||
_radioPtt, _lblPttKey, _btnChangePtt, _radioAlwaysOn,
|
||
lblGain, _trkGain, _chkNoiseReduction, _chkStereoMic,
|
||
_chkAux, _lblAuxDevice, _cboAuxDevice, _btnAuxRefresh, _lblAuxGain, _trkAuxGain,
|
||
btnOk, btnCancel,
|
||
]);
|
||
|
||
// Apply saved mode (fires RadioMode_CheckedChanged which sets visibility)
|
||
switch ((VcInputMode)settings.InputMode)
|
||
{
|
||
case VcInputMode.PushToTalk: _radioPtt.Checked = true; break;
|
||
case VcInputMode.AlwaysOn: _radioAlwaysOn.Checked = true; break;
|
||
default: _radioVad.Checked = true; break;
|
||
}
|
||
|
||
LoadDevices();
|
||
LoadAuxDevices();
|
||
UpdateAuxControlsEnabled();
|
||
}
|
||
|
||
private void LoadDevices()
|
||
{
|
||
string? currentId = (_cboDevice.SelectedItem as DeviceInfo)?.Id ?? _settings.InputDeviceId;
|
||
|
||
var devices = _client.ListDevices(VcDeviceKind.Input);
|
||
_cboDevice.SelectedIndexChanged -= CboDevice_SelectedIndexChanged;
|
||
_cboDevice.DataSource = new BindingList<DeviceInfo>(devices);
|
||
_cboDevice.SelectedIndexChanged += CboDevice_SelectedIndexChanged;
|
||
|
||
// Try to restore previous selection
|
||
int idx = -1;
|
||
if (currentId is not null)
|
||
idx = devices.FindIndex(d => d.Id == currentId);
|
||
if (idx < 0)
|
||
idx = devices.FindIndex(d => d.IsDefault);
|
||
_cboDevice.SelectedIndex = idx >= 0 ? idx : (devices.Count > 0 ? 0 : -1);
|
||
}
|
||
|
||
private void CboDevice_SelectedIndexChanged(object? sender, EventArgs e)
|
||
{
|
||
if (_cboDevice.SelectedItem is not DeviceInfo dev) return;
|
||
_settings.InputDeviceId = dev.IsDefault ? null : dev.Id;
|
||
if (_micStreamId != 0)
|
||
_client.SetInputDevice(_micStreamId, dev.IsDefault ? null : dev.Id);
|
||
}
|
||
|
||
// ── Aux input stream ──────────────────────────────────────────────────────
|
||
|
||
private void LoadAuxDevices()
|
||
{
|
||
string? currentId = (_cboAuxDevice.SelectedItem as InputDeviceInfo)?.Id
|
||
?? _settings.AuxDeviceId;
|
||
|
||
var devices = InputDeviceEnumerator.List().ToList();
|
||
|
||
// Set the data source AND restore the selection while unsubscribed so neither the initial
|
||
// populate nor a Refresh fires a spurious device-change (which would restart the capture).
|
||
_cboAuxDevice.SelectedIndexChanged -= CboAuxDevice_SelectedIndexChanged;
|
||
_cboAuxDevice.DataSource = new BindingList<InputDeviceInfo>(devices);
|
||
|
||
int idx = -1;
|
||
if (currentId is not null)
|
||
idx = devices.FindIndex(d => d.Id == currentId);
|
||
if (idx < 0)
|
||
idx = devices.FindIndex(d => d.IsDefault);
|
||
_cboAuxDevice.SelectedIndex = idx >= 0 ? idx : (devices.Count > 0 ? 0 : -1);
|
||
_cboAuxDevice.SelectedIndexChanged += CboAuxDevice_SelectedIndexChanged;
|
||
|
||
// Persist the resolved selection (null = default) so Join Voice opens the same device.
|
||
if (_cboAuxDevice.SelectedItem is InputDeviceInfo dev)
|
||
_settings.AuxDeviceId = dev.IsDefault ? null : dev.Id;
|
||
}
|
||
|
||
private void ChkAux_CheckedChanged(object? sender, EventArgs e)
|
||
{
|
||
_settings.AuxEnabled = _chkAux.Checked;
|
||
UpdateAuxControlsEnabled();
|
||
_applyAuxEnabled?.Invoke(_chkAux.Checked);
|
||
}
|
||
|
||
private void CboAuxDevice_SelectedIndexChanged(object? sender, EventArgs e)
|
||
{
|
||
if (_cboAuxDevice.SelectedItem is not InputDeviceInfo dev) return;
|
||
_settings.AuxDeviceId = dev.IsDefault ? null : dev.Id;
|
||
if (_chkAux.Checked)
|
||
_applyAuxDevice?.Invoke(dev.IsDefault ? null : dev.Id);
|
||
}
|
||
|
||
private void TrkAuxGain_Scroll(object? sender, EventArgs e)
|
||
{
|
||
_settings.AuxGain = _trkAuxGain.Value;
|
||
if (_chkAux.Checked)
|
||
_applyAuxGain?.Invoke(_trkAuxGain.Value / 100f);
|
||
}
|
||
|
||
private void UpdateAuxControlsEnabled()
|
||
{
|
||
bool on = _chkAux.Checked;
|
||
_lblAuxDevice.Enabled = on;
|
||
_cboAuxDevice.Enabled = on;
|
||
_btnAuxRefresh.Enabled = on;
|
||
_lblAuxGain.Enabled = on;
|
||
_trkAuxGain.Enabled = on;
|
||
}
|
||
|
||
private void RadioMode_CheckedChanged(object? sender, EventArgs e)
|
||
{
|
||
var mode = CurrentMode();
|
||
bool isVad = mode == VcInputMode.VoiceActivation;
|
||
bool isPtt = mode == VcInputMode.PushToTalk;
|
||
|
||
_lblSensitivity.Visible = isVad;
|
||
_trkVad.Visible = isVad;
|
||
_lblPttKey.Visible = isPtt;
|
||
_btnChangePtt.Visible = isPtt;
|
||
UpdatePttKeyLabel();
|
||
|
||
_settings.InputMode = (int)mode;
|
||
if (_micStreamId != 0)
|
||
{
|
||
_client.SetInputMode(mode);
|
||
if (isVad) _client.SetVadThreshold(VadThresholdFromSlider());
|
||
if (isPtt) _client.SetPushToTalk(false);
|
||
}
|
||
}
|
||
|
||
private void TrkVad_Scroll(object? sender, EventArgs e)
|
||
{
|
||
_settings.VadThresholdSlider = _trkVad.Value;
|
||
if (_micStreamId != 0 && CurrentMode() == VcInputMode.VoiceActivation)
|
||
_client.SetVadThreshold(VadThresholdFromSlider());
|
||
}
|
||
|
||
private void TrkGain_Scroll(object? sender, EventArgs e)
|
||
{
|
||
_settings.MicGain = _trkGain.Value;
|
||
if (_micStreamId != 0)
|
||
_client.SetInputGain(_trkGain.Value / 100f);
|
||
}
|
||
|
||
private void ChkNoiseReduction_CheckedChanged(object? sender, EventArgs e)
|
||
{
|
||
_settings.MicNoiseReduction = _chkNoiseReduction.Checked;
|
||
if (_micStreamId != 0)
|
||
_client.SetInputNoiseReduction(_chkNoiseReduction.Checked);
|
||
}
|
||
|
||
private void ChkStereoMic_CheckedChanged(object? sender, EventArgs e)
|
||
{
|
||
_settings.StereoMic = _chkStereoMic.Checked;
|
||
// Channel count only takes effect when the capture device (re)starts, so restart it live.
|
||
if (_micStreamId != 0)
|
||
{
|
||
_client.SetCaptureChannels(_micStreamId, _chkStereoMic.Checked ? 2u : 1u);
|
||
_client.AudioRestart();
|
||
}
|
||
}
|
||
|
||
private void BtnChangePtt_Click(object? sender, EventArgs e)
|
||
{
|
||
using var dlg = new PttKeyCaptureDialog(_pttKey);
|
||
if (dlg.ShowDialog(this) == DialogResult.OK)
|
||
{
|
||
_pttKey = dlg.CapturedKey;
|
||
_settings.PttKey = (int)_pttKey;
|
||
UpdatePttKeyLabel();
|
||
}
|
||
}
|
||
|
||
private void AudioSettingsForm_FormClosing(object? sender, FormClosingEventArgs e)
|
||
{
|
||
if (DialogResult == DialogResult.OK) return;
|
||
|
||
// Cancel: restore originals to settings and live client
|
||
_settings.InputDeviceId = _origDeviceId;
|
||
_settings.InputMode = (int)_origMode;
|
||
_settings.VadThresholdSlider = _origVadSlider;
|
||
_settings.MicGain = _origMicGain;
|
||
_settings.MicNoiseReduction = _origMicNoiseReduction;
|
||
_settings.StereoMic = _origStereoMic;
|
||
_settings.PttKey = (int)_origPttKey;
|
||
|
||
if (_micStreamId != 0)
|
||
{
|
||
_client.SetInputDevice(_micStreamId, _origDeviceId);
|
||
_client.SetInputMode(_origMode);
|
||
if (_origMode == VcInputMode.VoiceActivation)
|
||
_client.SetVadThreshold(0.1f * (1f - (_origVadSlider - 1f) / 99f));
|
||
_client.SetInputGain(_origMicGain / 100f);
|
||
_client.SetInputNoiseReduction(_origMicNoiseReduction);
|
||
// Restore capture channel count; restart the device only if it actually changed.
|
||
if (_origStereoMic != _chkStereoMic.Checked)
|
||
{
|
||
_client.SetCaptureChannels(_micStreamId, _origStereoMic ? 2u : 1u);
|
||
_client.AudioRestart();
|
||
}
|
||
}
|
||
|
||
// Aux: restore originals to settings and re-apply live (order: device + gain first so a
|
||
// re-enable starts with the right configuration).
|
||
_settings.AuxEnabled = _origAuxEnabled;
|
||
_settings.AuxDeviceId = _origAuxDeviceId;
|
||
_settings.AuxGain = _origAuxGain;
|
||
_applyAuxDevice?.Invoke(_origAuxDeviceId);
|
||
_applyAuxGain?.Invoke(_origAuxGain / 100f);
|
||
_applyAuxEnabled?.Invoke(_origAuxEnabled);
|
||
}
|
||
|
||
private void UpdatePttKeyLabel() =>
|
||
_lblPttKey.Text = $"({_pttKey})";
|
||
|
||
private VcInputMode CurrentMode() =>
|
||
_radioPtt.Checked ? VcInputMode.PushToTalk :
|
||
_radioAlwaysOn.Checked ? VcInputMode.AlwaysOn :
|
||
VcInputMode.VoiceActivation;
|
||
|
||
private float VadThresholdFromSlider() =>
|
||
0.1f * (1f - (_trkVad.Value - 1f) / 99f);
|
||
}
|