You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

MacOS下切换音频设备后AVAudioEngine配置变更通知未触发问题

问题描述

我想用AVAudioEngine将包含麦克风输入在内的多个音频输入混合到文件中,但切换输入/输出设备后,有时AVAudioEngineConfigurationChangeNotification不会触发,导致引擎停止工作。

我在AVAudioEngine的输入节点上安装了tap,当默认输入或输出设备变更时,这个tap会停止工作。原本我通过接收AVAudioEngineConfigurationChangeNotification后重启引擎并重新连接节点来修复,但有时该通知不会触发——此时tap已经失效,但AVAudioEngine.isRunning仍返回true。

根据苹果文档描述:

当音频引擎的I/O单元检测到音频输入或输出硬件的通道数或采样率发生变化时

这是不是意味着当新旧设备格式相同时,该通知不会触发?且此时引擎应该无缝切换设备?

我尝试连接sink节点并打印接收内容,同样出现失效问题。基本上任何设备变更(无论输入还是输出)都会中断引擎,但并非所有变更都会触发通知,即便引擎已经失效。请问有什么解决办法?

基础配置代码

audioEngine.inputNode
    .installTap(
        onBus: 0,
        bufferSize: 1024,
        format: audioEngine.inputNode.inputFormat(forBus: 0)
     ) { buffer, _ in
         do {
             let sampleBuffer = try buffer.convertToCMSampleBuffer()
         
             self.didOutputSampleBuffer.send((self.id, sampleBuffer))
         } catch {
             print(error)
         }
     }

let notification = NSNotification.Name.AVAudioEngineConfigurationChange

NotificationCenter.default.addObserver(
      self,
      selector: #selector(onInputChanged),
      name: notification,
      object: nil
)

audioEngine.prepare()

audioEngine.start()

设备变更处理代码

// 重新创建tap以应对格式变化
audioEngine.start()
解决办法
  • 补充监听音频会话路由变更通知
    仅依赖AVAudioEngineConfigurationChangeNotification并不够,同时监听AVAudioSessionRouteChangeNotification。这个通知会在音频路由(如设备切换)发生任何变化时触发,不受设备格式是否相同的影响。在回调中检查AVAudioSessionRouteChangeReason,只要是与设备切换相关的类型(比如newDeviceAvailable、oldDeviceUnavailable),就执行引擎重启逻辑。

  • 主动检测tap存活状态
    在tap的回调中记录最后一次接收音频数据的时间,设置一个定时检查任务(比如每2秒检查一次),如果超过设定阈值(比如3秒)未收到数据,则判定tap已失效,主动触发引擎重启流程。

  • 标准化引擎重启流程
    不要直接调用start(),需遵循完整的重启步骤:

    @objc func handleDeviceChange() {
        // 停止引擎并清理资源
        audioEngine.stop()
        audioEngine.inputNode.removeTap(onBus: 0)
        
        // 重新配置输入节点tap
        audioEngine.inputNode.installTap(
            onBus: 0,
            bufferSize: 1024,
            format: audioEngine.inputNode.inputFormat(forBus: 0)
        ) { buffer, time in
            do {
                let sampleBuffer = try buffer.convertToCMSampleBuffer()
                self.didOutputSampleBuffer.send((self.id, sampleBuffer))
            } catch {
                print(error)
            }
        }
        
        // 重新准备并启动引擎
        audioEngine.prepare()
        try? audioEngine.start()
    }
    
  • 确保音频会话配置正确
    启动引擎前,先配置并激活AVAudioSession,确保会话类别与你的使用场景匹配(比如混合输入输出需用.playAndRecord):

    let audioSession = AVAudioSession.sharedInstance()
    do {
        try audioSession.setCategory(.playAndRecord, mode: .default)
        try audioSession.setActive(true)
    } catch {
        print("音频会话配置失败:\(error)")
    }
    

内容的提问来源于stack exchange,提问作者Homid Way

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:53:12