我在尝试同步播放设备上的音频和从设备(在本例中为我的笔记本电脑扬声器和麦克风)录制音频时遇到了一些问题。
问题
我尝试使用Python模块来实现这个:“sounddevice”和“pyaudio”;但这两种实现都有一个奇怪的问题,即录制的音频的前几帧始终为零。还有其他人遇到过此类问题吗?这个问题似乎与所使用的块大小无关(即,其样本数量始终为零)。
我能做些什么来防止这种情况发生吗?
代码
import queue
import matplotlib.pyplot as plt
import numpy as np
import pyaudio
import soundfile as sf
FRAME_SIZE = 512
excitation, fs = sf.read("excitation.wav", dtype=np.float32)
# Instantiate PyAudio
p = pyaudio.PyAudio()
q = queue.Queue()
output_idx = 0
mic_buffer = np.zeros((excitation.shape[0] + FRAME_SIZE
- (excitation.shape[0] % FRAME_SIZE), 1))
def rec_play_callback(in_data, framelength, time_info, status):
global output_idx
# print status of playback in case of event
if status:
print(f"status: {status}")
chunksize = min(excitation.shape[0] - output_idx, framelength)
# write data to output buffer
out_data = excitation[output_idx:output_idx + chunksize]
# write input data to input buffer
inputsamples = np.frombuffer(in_data, dtype=np.float32)
if not np.sum(inputsamples):
print("Empty frame detected")
# send input data to buffer for main thread
q.put(inputsamples)
if chunksize < framelength:
out_data[chunksize:] = 0
return (out_data.tobytes(), pyaudio.paComplete)
output_idx += chunksize
return (out_data.tobytes(), pyaudio.paContinue)
# Define playback and record stream
stream = p.open(rate=fs,
channels=1,
frames_per_buffer=FRAME_SIZE,
format=pyaudio.paFloat32,
input=True,
output=True,
input_device_index=1, # Macbook Pro microphone
output_device_index=2, # Macbook Pro speakers
stream_callback=rec_play_callback)
stream.start_stream()
input_idx = 0
while stream.is_active():
data = q.get(timeout=1)
mic_buffer[input_idx:input_idx + FRAME_SIZE, 0] = data
input_idx += FRAME_SIZE
stream.stop_stream()
stream.close()
p.terminate()
# Plot captured microphone signal
plt.plot(mic_buffer)
plt.show()
输出
Empty frame detected
编辑:使用 CoreAudio 在 MacOS 上运行它。正如@2e0byo 所指出的,这可能是相关的。
这是一个普遍问题,我们缺少对您的架构的完整了解。 所以我们能做的就是指出一些一般概念。
在数字信号处理系统中,处理后的信号中经常存在前导空白和恒定延迟。 这通常与缓冲区的大小和采样率有关。 在某些系统中,您甚至可能不知道缓冲区的存在,例如作为用户级 API 无法访问的设备驱动程序的一部分。
要减少缓冲引起的偏移,您必须减小缓冲区或加快采样速度。 然后,您的系统必须更频繁地处理较小的数据包,并且数据包大小或采样时钟的变化可能会影响您的信号处理,具体取决于您的信号内容和您正在进行的信号处理类型。 因此,进行这些更改中的任何一个都会增加系统处理的每个数据的开销,并且还可能以其他方式影响性能。
我用于调试此类问题的一种方法是尝试找到正在设置偏移量的缓冲区,如果需要则跟踪源代码,然后查看是否可以调整大小或采样率并仍然达到您想要的性能吞吐量和准确性方面的需求。
为了未来的自己。
找到了解决方法。在
stream.start_stream()
之后,添加
while not any(stream.read(1)):
pass
如果您重视终止循环的一个非零块,请将其存储:
while not any(v := stream.read(1)):
pass
之后,
v
是bytes
的实例,长度取决于流格式。由于问题中它是 32 位浮点数,因此 len(v)
是 4。