切换音频输出设备时AVAudioEngine音频停止

AVAudioEngine audio stops when switching the audio output device

我有这个简单的 Swift 代码,它使用 AVAudioEngine + AVAudioPlayerNode 循环播放音频文件。

当我启动应用程序时,音频会在笔记本电脑扬声器上播放。如果我将计算机的输出切换到 HomePod mini,笔记本电脑上的音频会停止但不会在 HomePod mini 上播放(mini 也不会亮起)。

如果我停止该应用并再次 运行 - 音频会在 HomePod mini 上播放。如果我切换回笔记本电脑 - 音频会停止,直到我重新启动应用程序,等等。

看来问题出在播放期间切换输出设备,但我不明白如何解决这个问题。下面是我的代码:

class AppDelegate: NSObject, NSApplicationDelegate {
    
    var engine = AVAudioEngine()
    var player = AVAudioPlayerNode()
    
    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Attach audio player
        engine.attach(player)
        
        // Load audio file
        let filePath = Bundle.main.path(forResource: "sheep1.m4a", ofType: nil)!
        let fileURL = URL(fileURLWithPath: filePath)
        let file = try! AVAudioFile(forReading: fileURL)
        
        // Load the audio buffer
        let fileFormat = file.processingFormat
        let fileFrameCount = UInt32(file.length)
        let buffer = AVAudioPCMBuffer(pcmFormat: fileFormat, frameCapacity: fileFrameCount)
        try! file.read(into: buffer!, frameCount: fileFrameCount)
        
        // Connect to the mixer
        let mainMixer = engine.mainMixerNode
        engine.connect(player, to: mainMixer, format: file.processingFormat)

        // Start the engine
        try! engine.start()
        
        // Play the audio
        player.scheduleBuffer(buffer!, at: nil, options: .loops, completionHandler: nil)
        player.play()
    }
}

我收到了 Apple 技术支持的回复,正如爸爸所说 当音频引擎配置更改时,会发送 AVAudioEngineConfigurationChange 通知。

此时节点已分离,需要重新构建音频设置并重新启动引擎以开始在新输出设备上播放音频。

我在下面包含了完整的 AppDelegate,它测试了问题的原始前提。在启动应用程序时,我同时调用 setupAudio() 加载音频和 playAudio() 开始播放。

此外,每次音频引擎配置更改时,我都会调用 playAudio() 重新开始播放:

@main
class AppDelegate: NSObject, NSApplicationDelegate {
    
    var engine = AVAudioEngine()
    var player = AVAudioPlayerNode()

    // Load audio file
    let file = try! AVAudioFile(forReading: URL(fileURLWithPath: Bundle.main.path(forResource: "sheep1.m4a", ofType: nil)!))
    var buffer: AVAudioPCMBuffer!

    func applicationDidFinishLaunching(_ aNotification: Notification) {
        // Attach audio player
        setupAudio()
        playAudio()
    }
    
    /// Call this only ONCE
    func setupAudio() {
        engine.attach(player)

        // Load the audio buffer
        let fileFormat = file.processingFormat
        let fileFrameCount = UInt32(file.length)
        buffer = AVAudioPCMBuffer(pcmFormat: fileFormat, frameCapacity: fileFrameCount)
        file.framePosition = .zero
        try! file.read(into: buffer!, frameCount: fileFrameCount)

        // Observe for changes in the audio engine configuration
        NotificationCenter.default.addObserver(self,
           selector: #selector(handleInterruption),
           name: NSNotification.Name.AVAudioEngineConfigurationChange,
           object: nil
        )
    }
    
    /// Call this every time you want to restart audio
    func playAudio() {
        // Connect to the mixer
        let mainMixer = engine.mainMixerNode
        engine.connect(player, to: mainMixer, format: file.processingFormat)

        // Start the engine
        try! engine.start()
        
        // Play the audio
        player.scheduleBuffer(buffer, at: nil, options: .loops, completionHandler: nil)
        player.play()
    }
    
    @objc func handleInterruption(notification: Notification) {
        playAudio()
    }
}

此代码在大苏尔适用于我。