feat(clients): persist input settings, add mic input gain, fix iOS chat + VoiceOver
Input mode (VAD/PTT/Always-On), VAD threshold, and the new mic gain were
applied to the core + UI but never saved, so every relaunch reset to VAD
defaults. Each client now persists them and re-applies on connect:
- iOS: UserDefaults (SessionState.loadAndApplyVoiceSettings + setter writes)
- macOS: UserDefaults via MainWindowController didSet + loadPersistedAudioSettings
(settings window also restores the VAD slider from the stored threshold)
- Windows: new Models/VoiceSettings.cs (JSON at %AppData%\VoiceCat\voice.json,
mirrors FeedbackSettings) loaded/applied in MainForm
Add global send-side mic gain API vc_set_input_gain (applied to MIC PCM in
on_capture_frame before the VAD gate, clamped to int16) + Swift/C# bindings,
and a 0-300% (default 100%) mic-volume slider on all three clients.
Fix iOS chat: ChatView called sendText(scope:.channel) with no targetId (0),
so channel messages went nowhere; now passes session.currentChannelId.
Fix iOS per-user tuning for VoiceOver: the tuning sheet was long-press
.contextMenu only (invisible to VoiceOver); UserRow now also exposes the same
buttons via .accessibilityActions (no visual change).
Verified: core builds clean; ctest 24/27 (3 pre-existing teardown crashes,
reproduced with changes stashed); VoiceCatMac + VoiceCatiOS (arm64 sim) build
SUCCEEDED; VoiceCat.Interop dotnet build succeeded. Windows App not built
(WinForms can't build on macOS) — follows existing patterns.
This commit is contained in:
@@ -26,6 +26,7 @@ struct VoiceState {
|
||||
var serverDeafened = false
|
||||
var inputMode: VoiceCatInputMode = .voiceActivation
|
||||
var vadThreshold: Float = 0.025
|
||||
var inputGain: Float = 1.0
|
||||
var level: Float = 0.0
|
||||
var currentDeviceId: String?
|
||||
var localStreamId: UInt32 = 0
|
||||
@@ -59,6 +60,7 @@ final class SessionState {
|
||||
self.client = client
|
||||
self.selfUserId = selfUserId
|
||||
self.permissions = permissions
|
||||
loadAndApplyVoiceSettings()
|
||||
refreshChannels()
|
||||
refreshUsers()
|
||||
syncSelfChannel()
|
||||
@@ -336,11 +338,46 @@ final class SessionState {
|
||||
func setInputMode(_ mode: VoiceCatInputMode) {
|
||||
client.setInputMode(mode)
|
||||
voiceState.inputMode = mode
|
||||
UserDefaults.standard.set(Int(mode.rawValue), forKey: DefaultsKey.inputMode)
|
||||
}
|
||||
|
||||
func setVadThreshold(_ threshold: Float) {
|
||||
client.setVadThreshold(threshold)
|
||||
voiceState.vadThreshold = threshold
|
||||
UserDefaults.standard.set(threshold, forKey: DefaultsKey.vadThreshold)
|
||||
}
|
||||
|
||||
func setInputGain(_ gain: Float) {
|
||||
client.setInputGain(gain)
|
||||
voiceState.inputGain = gain
|
||||
UserDefaults.standard.set(gain, forKey: DefaultsKey.inputGain)
|
||||
}
|
||||
|
||||
// MARK: - Persisted input settings
|
||||
|
||||
private enum DefaultsKey {
|
||||
static let inputMode = "voice.inputMode"
|
||||
static let vadThreshold = "voice.vadThreshold"
|
||||
static let inputGain = "voice.inputGain"
|
||||
}
|
||||
|
||||
/// Restore the saved input mode / VAD threshold / mic gain and push them into the core so a
|
||||
/// relaunch keeps the user's transmission settings instead of resetting to VAD defaults.
|
||||
private func loadAndApplyVoiceSettings() {
|
||||
let d = UserDefaults.standard
|
||||
if d.object(forKey: DefaultsKey.inputMode) != nil {
|
||||
let raw = UInt32(d.integer(forKey: DefaultsKey.inputMode))
|
||||
voiceState.inputMode = VoiceCatInputMode(rawValue: raw) ?? .voiceActivation
|
||||
}
|
||||
if d.object(forKey: DefaultsKey.vadThreshold) != nil {
|
||||
voiceState.vadThreshold = d.float(forKey: DefaultsKey.vadThreshold)
|
||||
}
|
||||
if d.object(forKey: DefaultsKey.inputGain) != nil {
|
||||
voiceState.inputGain = d.float(forKey: DefaultsKey.inputGain)
|
||||
}
|
||||
client.setInputMode(voiceState.inputMode)
|
||||
client.setVadThreshold(voiceState.vadThreshold)
|
||||
client.setInputGain(voiceState.inputGain)
|
||||
}
|
||||
|
||||
private var pttEngaged = false
|
||||
|
||||
Reference in New Issue
Block a user