在 iOS 上一次可以播放多少声音 - AVAudioPlayer vs. AVAudioEngine & AVAudioPlayerNode

How many sounds can be played at a time on iOS - AVAudioPlayer vs. AVAudioEngine & AVAudioPlayerNode

我有一个应用程序,其中有一组大约 50 个声音,长度范围从大约 300 毫秒到大约 4 秒。需要在精确的时间播放各种声音组合(一次最多可以触发 10 个)。有些声音需要以短至 100 毫秒的间隔重复。

我将其实现为 AVAudioPlayer 的二维数组,所有这些都在应用程序启动时加载了声音。每个声音都有几个播放器,以适应快速重复的声音。特定声音的播放器在严格轮换中被重复使用。当安排新的声音时,该声音的最老播放器将停止,其当前时间设置为 0,因此声音将从头开始重复,下次使用 player.play(atTime:) 安排。有一个线程会在播放前约 300 毫秒安排新的声音集。

这一切都很好,直到某个点因设备而异。最终,随着声音播放速度的加快,and/or 安排了更多同时播放的声音,一些声音将拒绝播放。

我正在考虑使用混音器节点切换到 AVAudioEngine 和 AVAudioPlayerNodes。有谁知道这种方法是否可能处理更多的同步声音?我的猜测是这两种方法都转化为一组相当相似的 CoreAudio 函数,但我实际上并没有编写代码来检验该假设 - 在我这样做之前,我希望其他人可能已经在我之前探索了这个问题。之前深入研究过CoreAudio,希望能用这些好用的高级函数来代替!

另外,有谁知道在声音响起时触发关闭的方法吗?记录的功能允许回调关闭,但我能够在声音开始时触发事件的唯一方法是为 DispatchQueue 创建高质量的服务队列。不幸的是,根据系统负载,排队事件的执行时间可能与计划时间相差最多约 50 毫秒,这并不像我希望的那样精确。

将 AVAudioEngine 与 AVAudioPlayersNodes 一起使用可提供更好的性能,尽管以代码复杂性为代价。通过更好的缓冲控制,我能够轻松地将播放速率提高五倍。

改用这种方法的主要缺点是 Apple 的文档不够出色。对 Apple 文档的一些补充会使这项任务变得容易得多:

混合器节点被记录为能够转换采样率和通道数,因此我尝试配置 audioEngine.mainMixerNode 以将单声道缓冲区转换为输出节点的设置。将主混合器节点的输出设置为输出节点的格式似乎被接受,但在 运行 时间抛出了不透明的错误,抱怨通道计数不匹配。

看起来主混音器节点实际上并不是一个功能齐全的混音器节点。为了让它工作,我必须插入另一个执行通道转换的混音器节点,并将其连接到主混音器节点。如果 Apple 的文档真的提到了这一点,我就可以省去很多实验了。

此外,仅安排缓冲区不会导致播放任何内容。在任何事情发生之前,您需要在播放器节点上调用 play() 。 Apple 的文档在这里令人困惑 - 它说调用不带参数的 play() 将导致立即播放,这不是我想要的。进行了一些实验以确定 play() 只是告诉播放器节点醒来,并且预定的缓冲区实际上将在预定的时间播放,而不是立即播放。

如果 Apple 提供的不仅仅是自动生成的 class 文档,那将会非常有帮助。一些人工生成的文档可以让我避免很多令人沮丧的实验。

Chris Adamson 写得很好 "Learning Core Audio" 在我使用 Core Audio 时非常有帮助 - 遗憾的是新的 AVAudioEngine 功能几乎没有记录。