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

时间:2021-04-02 17:52:10

标签: swift macos avfoundation avaudioengine

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

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

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

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

r

1 个答案:

答案 0 :(得分:1)

我收到了 Apple 技术支持的回复,正如爸爸所说的 here,当音频引擎配置发生变化时,会发送 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()
    }
}

此代码适用于 Big Sur。