我在C ++中有一个跨平台项目,我实时混合音频。我有几个独立的轨道作为输入,我从磁盘上的单独文件中读取。然后我混合这些,应用一些处理,并用产生的音频吐出一个缓冲区。我遇到的问题是磁盘IO速度。对于我正在执行的当前测试,我有大约10个从磁盘同时读取的轨道。每个音轨采用原始PCM,48000 HZ 16位立体声。这意味着需要尽快读取大量数据。我通过Boost尝试了简单的fread调用和内存映射文件,但问题是一样的。首次打开文件时,通常会导致音频中断(可能是在操作系统将文件读入缓存时)。在那之后,一切运行顺利,没有故障。目前我在常见情况下每个文件使用一个线程,有时每个线程有两个文件。通常,当每个线程有两个文件时,会发生流的停顿/中断。请注意,我事先并不知道需要播放哪些输入文件,因为这是由用户控制的。所以我的问题是如何以这种方式读取这些初始块,以便我不会停滞/分手。此外,当加载新文件时,不一定必须开始读取。
我有几点想法:
我们可以通过在启动时读取所有文件但忽略数据来预取文件到缓存中吗?我无法将所有内容存储在内存中。但依赖操作系统读取内部行为似乎很糟糕,特别是因为这是跨平台的。
我们可以使用Ogg Vorbis这样的格式进行压缩,将压缩数据完全加载到内存中,然后即时解码吗?我认为解码10个或更多Vorbis流可能太CPU密集,但我还没有基准测试。至少以这种方式,我们将它从I / O绑定任务转换为CPU绑定任务。
我们可以采用其他任何一种聪明的缓冲方法来使大型读取更均匀地分布吗?我对如何实现这一点知之甚少。
我现在陷入困境,并希望任何可能提高吞吐量的建议。
答案 0 :(得分:1)
尝试使用事件处理来加载文件。
这是打开一堆文件描述符的地方,让操作系统在数据可用时通知您的程序。
使用“select”(http://linux.die.net/man/2/select)执行此操作的最广泛的api,wbut有更好的方法(poll,epoll,kqueue)。这些在任何地方都无法使用。
有些库可以为你抽象(libev和libevent)。
所以你这样做的方式是,一个线程打开你需要的所有文件并在它们上设置一个“观察者”。当数据可用时,观察者触发并呼叫回叫。
优点是你没有大量的线程等待和休眠检查所有打开的文件描述符。如果这不起作用那么你可能会过度饱和硬件的io带宽 - 在这种情况下你只需要等待。如果是这种情况,那么你需要做一些缓冲来避免口吃。
答案 1 :(得分:0)
根据经验,您需要在单独的线程中执行文件IO操作以进行实时操作。当用户想要混合第二个音频文件时,您只需打开一个新线程并读取该第二个音频文件的前N个字节,并将读取的数据返回到主线程中。这也会导致滞后,但不会破坏音频流。