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

标签 swift macos avfoundation avaudioengine

我有这个简单的 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 技术支持的回复,正如爸爸评论的那样 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。

关于swift - 切换音频输出设备时AVAudioEngine音频停止,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/66923293/

相关文章:

ios - 在 Swift 2.0 中创建 CMSampleBuffer 的副本

ios - 获取当前设备,也在模拟器中

ios - swift : Redundant conformance of Viewcontroller to protocol UIGestureRecognizerDelegate

c - 如何获取文件的扩展属性(UNIX/C)?

swift - 如果我在 NSDocument 中取消保存操作,则无法退出应用程序

path - 如何使用 Swift 的文件路径 API 获取文档目录并创建文件名

objective-c - 我们可以在 AVFoundation 中合并两个视频来实现暂停和播放功能吗

swift - 如何在 swift 中向字典添加新键?

swift - 当按下开始按钮时,游戏分数会随着时间的推移而增加

java - 如何在 Intellij 中创建和部署 war