You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
191 lines
7.3 KiB
191 lines
7.3 KiB
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
|
|
)
|
|
}
|
|
}
|