一次播放 15 个音轨,延迟小于 50 毫秒?

Posted

技术标签:

【中文标题】一次播放 15 个音轨,延迟小于 50 毫秒?【英文标题】:Playing 15 audio tracks at once with <50ms latency? 【发布时间】:2015-11-06 16:03:11 【问题描述】:

总而言之,我的问题是:是否可以同时解码和播放 15 个有损压缩的音轨,延迟低于 50 毫秒且没有卡顿? p>

背景

我正在为我正在创建的游戏用纯 C 语言编写声音库。我希望一次播放多达 15 个音轨,延迟小于 50 毫秒。

截至目前,该库能够播放原始 PCM 文件(48000Hz 压缩的 16 位样本),并且可以轻松地以 45 毫秒的延迟一次播放 15 种声音,而不会出现卡顿且 CPU 使用率最低。这是在我相对较旧的 Intel Q9300 + SSD 机器上。

由于原始音频文件很大,我扩充了我的库以支持使用 opusfile (https://mf4.xiph.org/jenkins/view/opus/job/opusfile-unix/ws/doc/html/index.html) 播放 OPUS 文件。我希望我仍然能够一次播放 15 种声音,而音频文件不会占用 200MB 以上。我错了——我一次只能播放 3 或 4 首 OPUS 曲目,然后才能听到口吃和其他缓冲不足的症状。与原始 PCM 播放相比,CPU 使用率也大幅增加。

我还尝试使用 vorbisfile (http://www.xiph.org/vorbis/doc/vorbisfile/) 包含 VORBIS 支持。我想也许即时解码 VORBIS 不会占用大量 CPU。 VORBIS 比 OPUS 好一点——我可以在听到口吃之前一次播放 5 或 6 个声音(我猜 VORBIS 确实更容易解码)——但这仍然远不及播放原始 PCM 文件。

在我深入研究低级 libvorbis/libopus API 并研究其他音频压缩格式之前,以低于 50 毫秒的延迟同时解码和播放 15 个有损压缩的音轨实际上是否可行?在中低端台式电脑上不会卡顿?

如果有帮助,我的声音库目前大约每 15 毫秒调用一次函数,该函数基本上执行以下操作(为清楚起见,省略了错误处理和后处理):

void onBufferUpdateNeeded(int numSounds, struct Sound *sounds,
    uint16_t *bufferToUpdate, int numSamplesNeeded, uint16_t *tmpBuffer) 
    int i, j;
    memset(bufferToUpdate, 0, numSamplesNeeded * sizeof(uint16_t));
    for (i = 0; i < numSounds; ++i) 
        /* Seek to the specified sample number in the already-opened
        file handle. The implementation of this depends on the file
        type (vorbis, opus, raw PCM). */
        seekToSample(sounds[i].fileHandle, sounds[i].currentSample);

        /* Read numSamplesNeeded samples from the file handle into
        tmpBuffer. */
        readSamples(tmpBuffer, sounds[i].fileHandle, numSamplesNeeded);

        /* Add the samples into the buffer. */
        for (j = 0; j < numSamplesNeeded; ++j) 
            bufferToUpdate[j] += tmpBuffer[j];
        
    

提前感谢您的帮助!

【问题讨论】:

@MartinJames 音频处理目前在一个线程中完成,但游戏的其余部分使用其他线程进行网络和逻辑更新。即使我使用两个线程进行音频处理,我最多也可以将 vorbis 的同时声音数量增加一倍至 10-12,这仍然低于 15。此外,我使用静态变量仅用于说明。该库实际上将临时缓冲区作为变量传递。我将编辑原始帖子以显示此内容。 【参考方案1】:

听起来您已经知道自己问题的答案:否。 通常,我对此类问题(尤其是与性能相关的查询)的唯一建议是尝试并找到如果可能的话。但是您已经收集了这些数据。

确实,感知/有损音频编解码器往往需要大量计算来解码。听起来您想避免原始 PCM 的存储开销。在这种情况下,如果您可以安全地假设您将为您的应用程序保留足够的内存,您可以提前解码音频流,或者使用一些缓存机制来处理内存限制。也许这可以卸载到不同的线程(因为您问题中提到的 Q9300 CPU 是双核)。

否则,您将需要寻找计算要求较低的压缩器。您可能对FLAC 感兴趣,它由与 Vorbis 和 Opus 相同的组织赞助。它是无损的,因此它的压缩效果不如有损算法,但解码速度应该快得多。

如果这仍然不合适,请浏览this big list of ~150 audio codecs,直到找到符合您标准的。由于您控制客户端软件,因此您有很多选择(相对于流式传输到网络浏览器)。

【讨论】:

感谢您的回复。是的,我怀疑一次播放这么多曲目是不可能的,但我希望使用一些较低级别的 API 可能会显着提高性能。我考虑了一些无损算法,FLAC 就是其中之一,但我对压缩率并不满意。最终,如果这是一次播放 15 首曲目的唯一方法,那么我将不得不再次考虑。 另外,您是否知道这个大列表中的哪些有损编解码器的解码计算量不是很大? 也许可以试试 MPEG-1 第 2 层(MPEG-1 第 3 层的前身,更广为人知的是 MP3)。也许它已经足够老了,计算费用在你的预算之内。

以上是关于一次播放 15 个音轨,延迟小于 50 毫秒?的主要内容,如果未能解决你的问题,请参考以下文章

无延迟播放声音 iOS

在投币、杀戮和通电声音之间有 300 毫秒的延迟

定时器触发 Tick 事件,延迟 15 毫秒

如何在 iPhone 上实现音频流 <50 毫秒的延迟

每 10 毫秒记录 pc 音频数据而不播放

有没有办法指定同一播放列表上音轨之间的时间延迟?