From a3226bb0fb9166b106f10513227ad8386ddc9367 Mon Sep 17 00:00:00 2001 From: Beingpax Date: Fri, 12 Dec 2025 09:19:29 +0545 Subject: [PATCH] Simplify audio input modes: keep Custom and Prioritized, remove System Default option --- VoiceInk/Services/AudioDeviceManager.swift | 75 ++++++++++++++----- .../OnboardingPermissionsView.swift | 6 +- .../Settings/AudioInputSettingsView.swift | 4 +- 3 files changed, 62 insertions(+), 23 deletions(-) diff --git a/VoiceInk/Services/AudioDeviceManager.swift b/VoiceInk/Services/AudioDeviceManager.swift index 0bfd5f9..c5f684d 100644 --- a/VoiceInk/Services/AudioDeviceManager.swift +++ b/VoiceInk/Services/AudioDeviceManager.swift @@ -10,7 +10,6 @@ struct PrioritizedDevice: Codable, Identifiable { } enum AudioInputMode: String, CaseIterable { - case systemDefault = "System Default" case custom = "Custom Device" case prioritized = "Prioritized" } @@ -19,12 +18,12 @@ class AudioDeviceManager: ObservableObject { private let logger = Logger(subsystem: "com.prakashjoshipax.voiceink", category: "AudioDeviceManager") @Published var availableDevices: [(id: AudioDeviceID, uid: String, name: String)] = [] @Published var selectedDeviceID: AudioDeviceID? - @Published var inputMode: AudioInputMode = .systemDefault + @Published var inputMode: AudioInputMode = .custom @Published var prioritizedDevices: [PrioritizedDevice] = [] var fallbackDeviceID: AudioDeviceID? - + var isRecordingActive: Bool = false - + static let shared = AudioDeviceManager() init() { @@ -33,14 +32,35 @@ class AudioDeviceManager: ObservableObject { loadAvailableDevices { [weak self] in self?.initializeSelectedDevice() } - + + migrateFromSystemDefaultIfNeeded() + if let savedMode = UserDefaults.standard.audioInputModeRawValue, let mode = AudioInputMode(rawValue: savedMode) { inputMode = mode + } else { + inputMode = .custom } - + setupDeviceChangeNotifications() } + + private func migrateFromSystemDefaultIfNeeded() { + if let savedModeRaw = UserDefaults.standard.audioInputModeRawValue, + savedModeRaw == "System Default" { + logger.info("Migrating from System Default mode to Custom mode") + + if let fallbackID = fallbackDeviceID { + selectedDeviceID = fallbackID + if let device = availableDevices.first(where: { $0.id == fallbackID }) { + UserDefaults.standard.selectedAudioDeviceUID = device.uid + logger.info("Migrated to Custom mode with device: \(device.name)") + } + } + + UserDefaults.standard.audioInputModeRawValue = AudioInputMode.custom.rawValue + } + } func setupFallbackDevice() { let deviceID: AudioDeviceID? = getDeviceProperty( @@ -216,6 +236,14 @@ class AudioDeviceManager: ObservableObject { self.selectedDeviceID = id UserDefaults.standard.selectedAudioDeviceUID = uid self.logger.info("Device selection saved with UID: \(uid)") + + do { + try AudioDeviceConfiguration.setDefaultInputDevice(id) + self.logger.info("✅ Set device as system default immediately") + } catch { + self.logger.error("Failed to set device as system default: \(error.localizedDescription)") + } + self.notifyDeviceChange() } } else { @@ -232,6 +260,14 @@ class AudioDeviceManager: ObservableObject { self.selectedDeviceID = id UserDefaults.standard.audioInputModeRawValue = AudioInputMode.custom.rawValue UserDefaults.standard.selectedAudioDeviceUID = uid + + do { + try AudioDeviceConfiguration.setDefaultInputDevice(id) + self.logger.info("✅ Set device as system default immediately") + } catch { + self.logger.error("Failed to set device as system default: \(error.localizedDescription)") + } + self.notifyDeviceChange() } } else { @@ -243,11 +279,8 @@ class AudioDeviceManager: ObservableObject { func selectInputMode(_ mode: AudioInputMode) { inputMode = mode UserDefaults.standard.audioInputModeRawValue = mode.rawValue - - if mode == .systemDefault { - selectedDeviceID = nil - UserDefaults.standard.removeObject(forKey: UserDefaults.Keys.selectedAudioDeviceUID) - } else if selectedDeviceID == nil { + + if selectedDeviceID == nil { if inputMode == .custom { if let firstDevice = availableDevices.first { selectDevice(id: firstDevice.id) @@ -255,15 +288,22 @@ class AudioDeviceManager: ObservableObject { } else if inputMode == .prioritized { selectHighestPriorityAvailableDevice() } + } else { + if let currentDeviceID = selectedDeviceID { + do { + try AudioDeviceConfiguration.setDefaultInputDevice(currentDeviceID) + logger.info("✅ Set current device as system default when mode changed") + } catch { + logger.error("Failed to set device as system default: \(error.localizedDescription)") + } + } } - + notifyDeviceChange() } func getCurrentDevice() -> AudioDeviceID { switch inputMode { - case .systemDefault: - return fallbackDeviceID ?? 0 case .custom: if let id = selectedDeviceID, isDeviceAvailable(id) { return id @@ -333,14 +373,15 @@ class AudioDeviceManager: ObservableObject { private func selectHighestPriorityAvailableDevice() { let sortedDevices = prioritizedDevices.sorted { $0.priority < $1.priority } - + for device in sortedDevices { if let availableDevice = availableDevices.first(where: { $0.uid == device.id }) { selectedDeviceID = availableDevice.id logger.info("Selected prioritized device: \(device.name) (Priority: \(device.priority))") - + do { try AudioDeviceConfiguration.setDefaultInputDevice(availableDevice.id) + logger.info("✅ Set prioritized device as system default immediately") } catch { logger.error("Failed to set prioritized device: \(error.localizedDescription)") continue @@ -349,7 +390,7 @@ class AudioDeviceManager: ObservableObject { return } } - + fallbackToDefaultDevice() } diff --git a/VoiceInk/Views/Onboarding/OnboardingPermissionsView.swift b/VoiceInk/Views/Onboarding/OnboardingPermissionsView.swift index 1415f03..f753e3c 100644 --- a/VoiceInk/Views/Onboarding/OnboardingPermissionsView.swift +++ b/VoiceInk/Views/Onboarding/OnboardingPermissionsView.swift @@ -278,8 +278,8 @@ struct OnboardingPermissionsView: View { // Check microphone permission permissionStates[0] = AVCaptureDevice.authorizationStatus(for: .audio) == .authorized - // Check if device is selected or system default mode is being used - permissionStates[1] = audioDeviceManager.selectedDeviceID != nil || audioDeviceManager.inputMode == .systemDefault + // Check if device is selected + permissionStates[1] = audioDeviceManager.selectedDeviceID != nil // Check accessibility permission permissionStates[2] = AXIsProcessTrusted() @@ -315,7 +315,7 @@ struct OnboardingPermissionsView: View { audioDeviceManager.loadAvailableDevices() if audioDeviceManager.availableDevices.isEmpty { - audioDeviceManager.selectInputMode(.systemDefault) + audioDeviceManager.selectInputMode(.custom) withAnimation { permissionStates[currentPermissionIndex] = true showAnimation = true diff --git a/VoiceInk/Views/Settings/AudioInputSettingsView.swift b/VoiceInk/Views/Settings/AudioInputSettingsView.swift index 226e307..ed3135c 100644 --- a/VoiceInk/Views/Settings/AudioInputSettingsView.swift +++ b/VoiceInk/Views/Settings/AudioInputSettingsView.swift @@ -258,15 +258,13 @@ struct InputModeCard: View { private var icon: String { switch mode { - case .systemDefault: return "macbook.and.iphone" case .custom: return "mic.circle.fill" case .prioritized: return "list.number" } } - + private var description: String { switch mode { - case .systemDefault: return "Use system's default input device" case .custom: return "Select a specific input device" case .prioritized: return "Set up device priority order" }