我目前正在使用 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 在我的机器上似乎是一致的。但是,我根本不知道这是否是一个可以计算的值。或者如果它是一个特定的常数,我不确定它到底代表什么。
任何有助于更好地理解这些值的帮助将不胜感激。我很高兴我的实用程序现在可以正常工作,但想完全了解这里发生的事情,因为我怀疑可能使用不同的界面可能不再正常工作(出于几个原因,我想支持这一点)。
编辑 2014 年 4 月 8 日回应罗伯托:我收到的值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 的问题,幸运的是,答案是肯定的。您必须使用用于 PortAudio 的 ASIO SDK 编译 PortAudio 以使用 ASIO,然后更新 PyAudio 设置以这种方式编译。幸运的是,我正在使用 Windows,http: //www.lfd.uci.edu/~gohlke/pythonlibs/#pyaudio 内置了 ASIO 支持,然后可以通过 ASIO 访问这些设备。