import Foundation import AVFoundation /// 统一音频会话管理类 /// 用于统一管理所有组件的音频会话配置 @objc class AudioSessionManager: NSObject { // MARK: - 单例实现 @objc static let shared = AudioSessionManager() // 音频会话 private let audioSession = AVAudioSession.sharedInstance() // 是否已经配置过音频会话 private var isConfigured = false // 当前配置类型 private var currentCategory: AVAudioSession.Category = .playAndRecord private var currentMode: AVAudioSession.Mode = .default private var currentOptions: AVAudioSession.CategoryOptions = [.allowBluetooth, .allowBluetoothA2DP] // 私有初始化方法,确保只能通过shared访问 private override init() { super.init() NSLog("[AudioSessionManager] 音频会话管理器已初始化") } // MARK: - 音频会话配置 /// 在应用启动时调用一次性配置方法 /// - Parameters: /// - category: 音频会话类别 /// - mode: 音频会话模式 /// - options: 音频会话选项 /// - Returns: 是否配置成功 @objc func initialConfiguration(category: AVAudioSession.Category = .playAndRecord, mode: AVAudioSession.Mode = .default, options: AVAudioSession.CategoryOptions = [.allowBluetooth, .allowBluetoothA2DP]) -> Bool { if isConfigured { NSLog("[AudioSessionManager] 音频会话已经配置过,忽略初始化请求") return true } do { NSLog("[AudioSessionManager] 执行一次性音频会话配置: \(category)") try audioSession.setCategory(category, mode: mode, options: options) try audioSession.setActive(true) // 记录当前配置 currentCategory = category currentMode = mode currentOptions = options isConfigured = true return true } catch { NSLog("[AudioSessionManager] 一次性配置音频会话失败: \(error.localizedDescription)") return false } } /// 配置音频会话,适用于语音识别和合成 @objc func configureForVoiceProcessing() throws { if !applyConfigurationIfNeeded(category: .playAndRecord, mode: .spokenAudio, options: [.allowBluetooth, .allowBluetoothA2DP, .duckOthers]) { NSLog("[AudioSessionManager] 使用已存在的音频会话配置,跳过语音处理模式配置") } } /// 配置音频会话,适用于蓝牙耳机连接和监听 @objc func configureForBluetoothHeadset() throws { if !applyConfigurationIfNeeded(category: .playAndRecord, mode: .default, options: [.allowBluetooth, .allowBluetoothA2DP]) { NSLog("[AudioSessionManager] 使用已存在的音频会话配置,跳过蓝牙耳机模式配置") } } /// 配置音频会话,适用于媒体播放和蓝牙媒体按钮 @objc func configureForMediaPlayback() throws { if !applyConfigurationIfNeeded(category: .playback, mode: .default, options: [.allowBluetooth, .allowBluetoothA2DP]) { NSLog("[AudioSessionManager] 使用已存在的音频会话配置,跳过媒体播放模式配置") } } /// 配置自定义音频会话 @objc func configure(category: AVAudioSession.Category, mode: AVAudioSession.Mode, options: AVAudioSession.CategoryOptions) throws { if !applyConfigurationIfNeeded(category: category, mode: mode, options: options) { NSLog("[AudioSessionManager] 使用已存在的音频会话配置,跳过自定义模式配置") } } /// 内部方法:如果尚未配置,应用新的配置 private func applyConfigurationIfNeeded(category: AVAudioSession.Category, mode: AVAudioSession.Mode, options: AVAudioSession.CategoryOptions) -> Bool { // 如果已经配置过,不再重复配置 if isConfigured { return false } do { NSLog("[AudioSessionManager] 应用音频会话配置: \(category)") try audioSession.setCategory(category, mode: mode, options: options) try audioSession.setActive(true) // 记录当前配置 currentCategory = category currentMode = mode currentOptions = options isConfigured = true return true } catch { NSLog("[AudioSessionManager] 配置音频会话失败: \(error.localizedDescription)") return false } } /// 获取当前音频会话的配置信息 @objc func getCurrentConfiguration() -> [String: Any] { return [ "isConfigured": isConfigured, "category": "\(currentCategory)", "mode": "\(currentMode)" ] } /// 仅在特殊情况下使用:强制重新配置音频会话 @objc func forceReconfigure() -> Bool { isConfigured = false do { NSLog("[AudioSessionManager] 强制重新配置音频会话") try audioSession.setCategory(currentCategory, mode: currentMode, options: currentOptions) try audioSession.setActive(true) isConfigured = true return true } catch { NSLog("[AudioSessionManager] 强制重新配置失败: \(error.localizedDescription)") return false } } /// 激活音频会话(如果已配置) @objc func activateSession() throws { if isConfigured { try audioSession.setActive(true) } else { NSLog("[AudioSessionManager] 警告: 尝试激活未配置的音频会话") // 应用默认配置并激活 try applyConfigurationIfNeeded(category: .playAndRecord, mode: .default, options: [.allowBluetooth, .allowBluetoothA2DP]) } } /// 停用音频会话(保留配置) @objc func deactivateSession() throws { try audioSession.setActive(false, options: .notifyOthersOnDeactivation) } /// 获取当前音频会话路由描述 @objc func getCurrentRoute() -> AVAudioSessionRouteDescription { return audioSession.currentRoute } /// 添加路由变化监听器 @objc func addRouteChangeListener(_ observer: Any, selector: Selector) { NotificationCenter.default.addObserver( observer, selector: selector, name: AVAudioSession.routeChangeNotification, object: nil ) } /// 移除路由变化监听器 @objc func removeRouteChangeListener(_ observer: Any) { NotificationCenter.default.removeObserver( observer, name: AVAudioSession.routeChangeNotification, object: nil ) } }