PyAudio - 同步播放和录制

时间:2014-04-05 09:35:02

标签: python audio pyaudio

我目前正在使用PyAudio处理轻量级录音工具,以满足我计划的应用程序的特定需求。我正在使用ASIO音频接口。我编写程序要做的是通过界面播放wav文件,同时记录界面的输出。该接口实时处理板载信号并改变音频。由于我打算将此渲染输出导入DAW,我需要将输出与输入音频完美同步。使用DAW我可以同时播放音频到我的界面并记录输出。当我这样做时,它在DAW中完全同步。我的实用程序的目的是能够从python脚本中触发它。

通过蛮力的方法,我提出了一个有效的解决方案,但我现在仍然坚持使用一个神奇的数字,而且我不确定这是否是某种常数或我可以计算的东西。如果它是一个我可以计算出来的数字,那将是理想的,但我仍然想知道它来自哪种方式。

我的回调如下:

def testCallback(in_data, frame_count, time_info, status):
    #read data from wave file
    data = wave_file.readframes(frame_count)
    #calculate number of latency frames for playback and recording
    #1060 is my magic number
    latencyCalc = math.ceil((stream.get_output_latency() + stream.get_input_latency()) * wave_file.getframerate()) + 1060

    #no more data in playback file
    if data == "":
        #this is the number of times we must keep the loop alive to capture all playback
        recordEndBuffer = latencyCalc / frame_count
        if lastCt < recordEndBuffer:
            #return 0-byte data to keep callback alive
            data = b"0"*wave_file.getsampwidth()*frame_count
            lastCt += 1
    #we start recording before playback, so this accounts for the initial "pre-playback" data in the output file
    if firstCt > (latencyCalc/frame_count):
        wave_out.writeframes(in_data)
    else:
       firstCt += 1
    return (data, pyaudio.paContinue)

我关注的是功能:

latencyCalc = math.ceil((stream.get_output_latency() + stream.get_input_latency()) * wave_file.getframerate()) + 1060

通过观察输出文件与原始播放文件的偏移量,我将这个计算放在一起。发生了两件事,我的输出文件在同时播放时比原始文件开始的时间晚了,它也会提前结束。通过反复试验,我确定它在开始时是额外的特定帧数,最后是缺失的。这会计算这些帧数。我理解第一部分,它是使用采样率转换为帧的输入/输出延迟(以秒/亚秒精度提供)。但是我不太确定如何填写1060的值,因为我不知道它来自何处。

我发现通过播放ASIO驱动程序上的延迟设置,我的应用程序继续正确同步录制的文件,即使上面的输出/输入延迟因调整而改变(输入/输出延迟总是相同的值),所以1060似乎在我的机器上是一致的。但是,我根本不知道这是否是可以计算的值。或者,如果它是一个特定的常数,我不确定它究竟代表什么。

任何有助于更好地理解这些价值观的人都将不胜感激。我很高兴我的实用工具现在正常工作,但我想完全理解这里发生了什么,因为我怀疑可能使用不同的界面可能不再正常工作(我想支持这个在路上几个原因)。

EDIT 4/8/2014 以回应Roberto: 我收到的价值     latencyCalc = math.ceil((stream.get_output_latency()+ stream.get_input_latency())* wave_file.getframerate())+ 1060 是8576,额外的1060使总延迟达到9636帧。你在为什么我添加了1060帧的假设是正确的。我正在通过外部ASIO接口播放文件,我希望在我的录制文件中捕获的处理是接口上发生的处理的结果(不是我编码的东西)。为了比较输出,我只是简单地播放了测试文件并记录了界面的输出,而没有任何处理效果。然后我检查了Audacity中的两个轨道,并通过反复试验确定1060是最接近我可以让两个轨道对齐。我已经意识到它仍然不是很完美,但是当它同时播放时它非常接近并且听不到(当1060偏移被移除时,这是不正确的,有明显的延迟)。与1060相比,添加/删除附加帧的补偿太多了。

我相信你是正确的,额外的延迟来自外部接口。我最初想知道这是否可以用我手边的数字信息来计算,但我总结它只是界面中的一个常数。我觉得这是真的,因为我已经确定如果我删除1060,文件的偏移与执行相同的测试完全相同,但在Reaper中手动(这正是我自动化的过程)。我的新暴力抵消让我的潜伏期远远超过收割机,所以我打算称之为胜利。在我的应用程序中,目标是用新处理的文件完全替换原始文件,因此需要两者之间的绝对最小延迟。

回答你在PyAudio中关于ASIO的问题,答案是幸运的是。您必须使用ASIO SDK for PortAudio编译PortAudio以使用ASIO,然后更新PyAudio设置以便以这种方式进行编译。幸运的是,我正在使用内置ASIO支持的Windows http://www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio,然后可以通过ASIO访问这些设备。

1 个答案:

答案 0 :(得分:1)

由于我不允许发表评论,我会在此问你:stream.get_output_latency() + stream.get_input_latency()) * wave_file.getframerate()的价值是多少?你是如何在第一时间得到这个数字1060的呢? 使用您标记的代码行:
latencyCalc = math.ceil((stream.get_output_latency() + stream.get_input_latency()) * wave_file.getframerate()) + 1060,您只需为总延迟添加额外的1060帧。我从你的描述中不清楚,为什么你这样做,但我认为你已经测量了生成的文件中的总延迟,并且除了输入延迟+输出延迟之和外,总是有恒定数量的额外帧。那么,你是否认为这个额外的延迟可能是由于处理?你说你对输入音频信号进行了一些处理;和处理肯定需要一些时间。尝试对未改变的输入信号执行相同操作,并查看是否减少/移除了额外的延迟。甚至是应用程序的其他部分,例如如果应用程序具有GUI,那么所有这些都会减慢记录速度。您没有完全描述您的应用程序,但我猜测额外延迟是由您的代码以及代码执行的操作引起的。为什么“神奇数字”总是一样的?因为你的代码总是一样的。

简历:
'神奇数字'代表什么?
显然,除了你的总轮次延迟之外,它还代表了一些额外的延迟 造成这种额外延迟的原因是什么?
原因很可能是代码中的某个地方。您的应用程序正在执行需要一些额外时间的操作,因此会产生一些额外的延迟。我想到的唯一其他可能的事情是,你在设置的某个地方添加了一些额外的“静音期”,所以你也可以检查一下。