feat(clients): unify volume boost cap to 4x across all clients
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>
This commit is contained in:
@@ -20,7 +20,7 @@ struct PerUserTuningView: View {
|
|||||||
Section("Volume") {
|
Section("Volume") {
|
||||||
HStack {
|
HStack {
|
||||||
Text("Gain")
|
Text("Gain")
|
||||||
Slider(value: $gain, in: 0...2, step: 0.05) { _ in
|
Slider(value: $gain, in: 0...4, step: 0.05) { _ in
|
||||||
applyToAllStreams()
|
applyToAllStreams()
|
||||||
}
|
}
|
||||||
.accessibilityLabel("Volume gain for \(user.nickname)")
|
.accessibilityLabel("Volume gain for \(user.nickname)")
|
||||||
|
|||||||
@@ -246,7 +246,7 @@ struct SettingsView: View {
|
|||||||
get: { Double(session.voiceState.inputGain) },
|
get: { Double(session.voiceState.inputGain) },
|
||||||
set: { session.setInputGain(Float($0)) }
|
set: { session.setInputGain(Float($0)) }
|
||||||
),
|
),
|
||||||
in: 0...3, step: 0.05
|
in: 0...4, step: 0.05
|
||||||
)
|
)
|
||||||
.accessibilityLabel("Microphone volume")
|
.accessibilityLabel("Microphone volume")
|
||||||
.accessibilityValue("\(Int((session.voiceState.inputGain * 100).rounded())) percent")
|
.accessibilityValue("\(Int((session.voiceState.inputGain * 100).rounded())) percent")
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ final class PerUserTuningSheet: NSViewController {
|
|||||||
let muted = state?.muted ?? false
|
let muted = state?.muted ?? false
|
||||||
let nr = state?.noiseReduction ?? false
|
let nr = state?.noiseReduction ?? false
|
||||||
|
|
||||||
let gainSlider = NSSlider(value: Double(gain * 100), minValue: 0, maxValue: 200, target: self, action: #selector(sliderChanged))
|
let gainSlider = NSSlider(value: Double(gain * 100), minValue: 0, maxValue: 400, target: self, action: #selector(sliderChanged))
|
||||||
gainSlider.tag = Int(stream.id)
|
gainSlider.tag = Int(stream.id)
|
||||||
gainSlider.numberOfTickMarks = 0
|
gainSlider.numberOfTickMarks = 0
|
||||||
gainSlider.setAccessibilityLabel("Volume for \(stream.label): \(Int(gain * 100)) percent")
|
gainSlider.setAccessibilityLabel("Volume for \(stream.label): \(Int(gain * 100)) percent")
|
||||||
|
|||||||
@@ -50,9 +50,9 @@ final class SettingsWindowController: NSWindowController, NSWindowDelegate {
|
|||||||
// Cached VAD slider position so we can restore it when the window reopens.
|
// Cached VAD slider position so we can restore it when the window reopens.
|
||||||
private var vadSliderValue: Double = 50
|
private var vadSliderValue: Double = 50
|
||||||
|
|
||||||
// Mic input gain: 0–300 % (100 = unity). Persisted via MainWindowController.inputGain.
|
// Mic input gain: 0–400 % (100 = unity). Persisted via MainWindowController.inputGain.
|
||||||
private let inputGainSlider: NSSlider = {
|
private let inputGainSlider: NSSlider = {
|
||||||
let s = NSSlider(value: 100, minValue: 0, maxValue: 300, target: nil, action: nil)
|
let s = NSSlider(value: 100, minValue: 0, maxValue: 400, target: nil, action: nil)
|
||||||
s.numberOfTickMarks = 0
|
s.numberOfTickMarks = 0
|
||||||
return s
|
return s
|
||||||
}()
|
}()
|
||||||
@@ -77,7 +77,7 @@ final class SettingsWindowController: NSWindowController, NSWindowDelegate {
|
|||||||
private let auxDevicePicker = NSPopUpButton()
|
private let auxDevicePicker = NSPopUpButton()
|
||||||
private let auxRefreshButton = NSButton()
|
private let auxRefreshButton = NSButton()
|
||||||
private let auxGainSlider: NSSlider = {
|
private let auxGainSlider: NSSlider = {
|
||||||
let s = NSSlider(value: 100, minValue: 0, maxValue: 300, target: nil, action: nil)
|
let s = NSSlider(value: 100, minValue: 0, maxValue: 400, target: nil, action: nil)
|
||||||
s.numberOfTickMarks = 0
|
s.numberOfTickMarks = 0
|
||||||
return s
|
return s
|
||||||
}()
|
}()
|
||||||
|
|||||||
@@ -194,15 +194,15 @@ public sealed class AudioSettingsForm : Form
|
|||||||
Location = new Point(12, 288),
|
Location = new Point(12, 288),
|
||||||
Size = new Size(200, 45),
|
Size = new Size(200, 45),
|
||||||
Minimum = 0,
|
Minimum = 0,
|
||||||
Maximum = 300,
|
Maximum = 400,
|
||||||
TickFrequency = 25,
|
TickFrequency = 25,
|
||||||
SmallChange = 5,
|
SmallChange = 5,
|
||||||
LargeChange = 25,
|
LargeChange = 25,
|
||||||
Value = Math.Clamp(settings.MicGain, 0, 300),
|
Value = Math.Clamp(settings.MicGain, 0, 400),
|
||||||
};
|
};
|
||||||
_trkGain.AccessibleName = "Microphone volume";
|
_trkGain.AccessibleName = "Microphone volume";
|
||||||
_trkGain.AccessibleDescription =
|
_trkGain.AccessibleDescription =
|
||||||
"Boost a quiet microphone. 100 is unity gain; range 0–300 percent.";
|
"Boost a quiet microphone. 100 is unity gain; range 0–400 percent.";
|
||||||
_trkGain.Scroll += TrkGain_Scroll;
|
_trkGain.Scroll += TrkGain_Scroll;
|
||||||
|
|
||||||
// ── Mic noise reduction ───────────────────────────────────────────────
|
// ── Mic noise reduction ───────────────────────────────────────────────
|
||||||
@@ -290,15 +290,15 @@ public sealed class AudioSettingsForm : Form
|
|||||||
Location = new Point(12, 510),
|
Location = new Point(12, 510),
|
||||||
Size = new Size(200, 45),
|
Size = new Size(200, 45),
|
||||||
Minimum = 0,
|
Minimum = 0,
|
||||||
Maximum = 300,
|
Maximum = 400,
|
||||||
TickFrequency = 25,
|
TickFrequency = 25,
|
||||||
SmallChange = 5,
|
SmallChange = 5,
|
||||||
LargeChange = 25,
|
LargeChange = 25,
|
||||||
Value = Math.Clamp(settings.AuxGain, 0, 300),
|
Value = Math.Clamp(settings.AuxGain, 0, 400),
|
||||||
};
|
};
|
||||||
_trkAuxGain.AccessibleName = "Aux volume";
|
_trkAuxGain.AccessibleName = "Aux volume";
|
||||||
_trkAuxGain.AccessibleDescription =
|
_trkAuxGain.AccessibleDescription =
|
||||||
"Volume of the aux input stream. 100 is unity gain; range 0–300 percent.";
|
"Volume of the aux input stream. 100 is unity gain; range 0–400 percent.";
|
||||||
_trkAuxGain.Scroll += TrkAuxGain_Scroll;
|
_trkAuxGain.Scroll += TrkAuxGain_Scroll;
|
||||||
|
|
||||||
// ── OK / Cancel ───────────────────────────────────────────────────────
|
// ── OK / Cancel ───────────────────────────────────────────────────────
|
||||||
|
|||||||
@@ -117,11 +117,11 @@ public sealed class PerUserTuningDialog : Form
|
|||||||
var trkGain = new TrackBar
|
var trkGain = new TrackBar
|
||||||
{
|
{
|
||||||
AccessibleName = $"Gain for {s.Label}",
|
AccessibleName = $"Gain for {s.Label}",
|
||||||
AccessibleDescription = "Volume level for this stream. 100 is normal (1.0×), 200 is double.",
|
AccessibleDescription = "Volume level for this stream. 100 is normal (1.0×); range 0–400 percent.",
|
||||||
Location = new Point(12, y + 20),
|
Location = new Point(12, y + 20),
|
||||||
Size = new Size(260, 45),
|
Size = new Size(260, 45),
|
||||||
Minimum = 0,
|
Minimum = 0,
|
||||||
Maximum = 200,
|
Maximum = 400,
|
||||||
Value = ClampToTrack(gain0),
|
Value = ClampToTrack(gain0),
|
||||||
TickFrequency = 25,
|
TickFrequency = 25,
|
||||||
SmallChange = 5,
|
SmallChange = 5,
|
||||||
@@ -186,7 +186,7 @@ public sealed class PerUserTuningDialog : Form
|
|||||||
private static int ClampToTrack(float gain)
|
private static int ClampToTrack(float gain)
|
||||||
{
|
{
|
||||||
int v = (int)Math.Round(gain * 100f);
|
int v = (int)Math.Round(gain * 100f);
|
||||||
return Math.Max(0, Math.Min(200, v));
|
return Math.Max(0, Math.Min(400, v));
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class StreamRow
|
private sealed class StreamRow
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ public sealed class VoiceSettings
|
|||||||
/// <summary>VAD sensitivity slider position, 1–100 (default mirrors the designer's 76).</summary>
|
/// <summary>VAD sensitivity slider position, 1–100 (default mirrors the designer's 76).</summary>
|
||||||
public int VadThresholdSlider { get; set; } = 76;
|
public int VadThresholdSlider { get; set; } = 76;
|
||||||
|
|
||||||
/// <summary>Microphone input gain slider position, 0–300 percent (100 = unity).</summary>
|
/// <summary>Microphone input gain slider position, 0–400 percent (100 = unity).</summary>
|
||||||
public int MicGain { get; set; } = 100;
|
public int MicGain { get; set; } = 100;
|
||||||
|
|
||||||
/// <summary>Send-side mic noise reduction (RNNoise) toggle. MIC stream only, mono only;
|
/// <summary>Send-side mic noise reduction (RNNoise) toggle. MIC stream only, mono only;
|
||||||
@@ -48,7 +48,7 @@ public sealed class VoiceSettings
|
|||||||
/// NOT a core/miniaudio id — the aux device is opened client-side, so the two id spaces differ.</summary>
|
/// NOT a core/miniaudio id — the aux device is opened client-side, so the two id spaces differ.</summary>
|
||||||
public string? AuxDeviceId { get; set; } = null;
|
public string? AuxDeviceId { get; set; } = null;
|
||||||
|
|
||||||
/// <summary>Aux input volume slider position, 0–300 percent (100 = unity). Applied client-side
|
/// <summary>Aux input volume slider position, 0–400 percent (100 = unity). Applied client-side
|
||||||
/// to the captured PCM before feeding (the core's input gain is mic-only and global).</summary>
|
/// to the captured PCM before feeding (the core's input gain is mic-only and global).</summary>
|
||||||
public int AuxGain { get; set; } = 100;
|
public int AuxGain { get; set; } = 100;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user