合成歌曲时音符之间的准确延迟

时间:2012-06-10 13:54:54

标签: c++ audio sleep midi

我正在编写同时播放数字音频(合成音乐)和MIDI音乐的C ++代码(使用RtMidi库。)数字化音乐将播放计算机的音频设备,但MIDI音乐可以播放超出外部合成器。我想播放使用数字化乐器和MIDI乐器的歌曲,我不确定同步这两个音频流的最佳方式:

  • 不可能使用像Sleep()这样的函数,因为延迟时间不均匀且太长,不能满足我的需要(大约为1毫秒)。如果Sleep()只需1ms就会等待5ms被要求,结果的歌曲节奏将被关闭,除非每次被调用时都是准确的,否则节奏将不均匀。
  • 计算放入音频缓冲区的样本数量可在数字音频的音符之间提供超精确的时序(一个样本的最小延迟 - 在48kHz时为0.02ms),但此时间不能用于MIDI。因为音频被缓冲,所以音符以突发形式合成(一次填充一个音频缓冲区,尽可能快),因此每次数字音频缓冲器需要时,这会导致一堆MIDI音符在它们之间没有延迟播放。补充。
  • 播放实时MIDI数据没有时间信息,因此一旦发送便会播放音符。因此,音符无法安排在以后播放,所以我需要在正确的时间自己准确发送MIDI事件。

目前我正在使用nanosleep() - 它只适用于Linux,而不是Windows - 等待笔记之间的正确时间。这允许数字音频和MIDI数据保持同步,但是nanosleep()不是非常一致,因此产生的节奏非常不均匀。

有人能想出一种方法来保持数字音频和MIDI数据的音符之间的准确时间吗?

3 个答案:

答案 0 :(得分:2)

如果您愿意使用Boost,则会有CPU-precision timers。如果没有,在Windows上有函数QueryPerformanceCounterQueryPerformanceFrequency,它们可以用于基于CPU的计时,这当然可以满足您的所有需求。 Web上有很多Timer类实现,其中一些在windows和* ix系统上都有效。

答案 1 :(得分:1)

第一个问题是您需要知道音频设备传递了多少音频。如果您的延迟足够低,您可能会从您推送的数据量中猜出,但是它与播放之间的延迟是一个移动目标,因此您应该尝试从音频中获取该信息硬件。这些信息是可用的,因此使用它是因为从延迟测量中的错误中获得的“抖动”会以音乐上明显的方式影响同步。

如果你必须使用睡眠进行计时,有两个问题会让它睡得更久:1。优先级(如果另一个进程/线程具有更高的优先级,如果计时器已经用完则会运行)和2.系统延迟(如果系统需要5毫秒来交换进程/线程,它可能会将其添加到您请求的延迟时间)。这些延迟在音乐上是相关的。大多数midi API都有一个“sequencer”api,可以让你提前排队数据,以避免使用系统定时器。

即使您没有将portaudio用于音频I / O,也可能会发现此文档很有用。

http://www.portaudio.com/docs/portaudio_sync_acmc2003.pdf

答案 2 :(得分:0)

答案不在于小缓冲区,而在于大缓冲区。

让我们举一首3分钟的歌曲。

首先渲染数字部分,然后用MIDI音符“标记”它。 然后开始播放并在时间触发MIDI音符,也许使用std :: vector来保持有序列表。 可以使用总时间偏移来更改同步:

HORRIBLE不完整但有希望在该主题上使用示范伪代码:

start_digital_playing_thread();
int midi_time_sync = 10; // ms
if (time >= (midi_note[50]->time + midi_time_sync)) // play note