录音开始时稍有延迟,结束时被 python-sounddevice 中的低电平流切断
Slight delay in beginning of recording, and end gets cut off with low level streams in python-sounddevice
我正在尝试创建一个低级流,它允许我输出 WAVE 文件,同时在同一音频设备上录制输入。我的音频设备已设置,因此输出的 WAVE 文件将通过输出播放,并通过系统运行,然后进入设备的输入。使用 python-sounddevice 中的便捷函数 playrec() 可以让我完整记录在输入端看到的内容,但是我的代码使用较低级别的 Stream() 函数,记录开始较晚,最后一点点的音频未录制。我想使用较低级别的 Stream() 函数的原因是为了测试与 playrec() 相比,我是否可以减少该系统的整体延迟。我尝试更改块大小和缓冲区大小无济于事。
def callback(indata, outdata, frames, time, status):
assert frames == args.blocksize
qr.put(indata.copy())
rec_file.write(qr.get())
if status.output_underflow:
print('Output underflow: increase blocksize?', file=sys.stderr)
raise sd.CallbackAbort
assert not status
try:
data = q.get_nowait()
except queue.Empty:
print('Buffer is empty: increase buffersize?', file=sys.stderr)
raise sd.CallbackAbort
if data.size < outdata.size:
outdata[:len(data),0] = data
outdata[len(data):] = 0
raise sd.CallbackStop
else:
outdata[:,0] = data
try:
with sf.SoundFile(args.filename) as f:
#queue for recording input
qr = queue.Queue(maxsize=args.buffersize)
#queue for output WAVE file
q = queue.Queue(maxsize=args.buffersize)
event = threading.Event()
for _ in range(args.buffersize):
data = f.read(frames=args.blocksize, dtype='float32')
if data.size == 0:
break
q.put_nowait(data) # Pre-fill queue
stream = sd.Stream(
samplerate=f.samplerate, blocksize=args.blocksize,
dtype='float32', callback=callback, finished_callback=event.set,
latency='low')
with sf.SoundFile('output'+str(itr)+'.wav', mode='x', samplerate=f.samplerate,
channels=1) as rec_file:
with stream:
timeout = args.blocksize * args.buffersize / f.samplerate
while data.size != 0:
data = f.read(args.blocksize, dtype='float32')
q.put(data, timeout=timeout)
event.wait() # Wait until playback is finished
如果您不介意一次将整个输入和输出信号存储在内存中,您可以随意使用 sd.playrec()
。您将无法使用 sd.Stream
使用您自己的代码来减少延迟。 sd.playrec()
内部使用 sd.Stream
并且不会增加延迟。
如果您想减少延迟,您应该尝试为 blocksize
and/or latency
参数使用较低的值。但是请注意,低值将更加不稳定,并可能导致 playback/recording.
中出现故障
如果您不想一次将所有数据都存储在内存中,则不能使用 sd.playrec()
,您可以尝试使用 sd.Stream
,就像上面的示例一样。
但是请注意,这两行中的队列充其量是无用的:
qr.put(indata.copy())
rec_file.write(qr.get())
你不妨写:
rec_file.write(indata)
但请不要!
写入文件可能会长时间阻塞音频回调,从而导致音频丢失。
因此,使用队列是一个好主意(使用 indata.copy()
也是一个好主意)。
但是您应该只在回调函数中将写入您的qr
。 阅读 应该发生在不同的时间点。
你应该在 q.put(...)
之前或之后的 while
循环中执行 非阻塞 qr.get_nowait()
并将数据写入文件那里。
在回调函数中,您不应该执行 阻塞 qr.put(indata.copy())
,因为这可能会再次阻塞您的音频回调,导致掉线。
相反,您应该使用 qr.put_nowait()
。为避免队列满,您应该从 qr
中删除 maxsize
参数(但将其保留在另一个队列中!)。
最后,在离开 with stream
上下文管理器后,qr
中可能仍有数据尚未写入文件。
所以在流关闭后,您应该确保清空“记录队列”并将剩余的块写入文件。
我正在尝试创建一个低级流,它允许我输出 WAVE 文件,同时在同一音频设备上录制输入。我的音频设备已设置,因此输出的 WAVE 文件将通过输出播放,并通过系统运行,然后进入设备的输入。使用 python-sounddevice 中的便捷函数 playrec() 可以让我完整记录在输入端看到的内容,但是我的代码使用较低级别的 Stream() 函数,记录开始较晚,最后一点点的音频未录制。我想使用较低级别的 Stream() 函数的原因是为了测试与 playrec() 相比,我是否可以减少该系统的整体延迟。我尝试更改块大小和缓冲区大小无济于事。
def callback(indata, outdata, frames, time, status):
assert frames == args.blocksize
qr.put(indata.copy())
rec_file.write(qr.get())
if status.output_underflow:
print('Output underflow: increase blocksize?', file=sys.stderr)
raise sd.CallbackAbort
assert not status
try:
data = q.get_nowait()
except queue.Empty:
print('Buffer is empty: increase buffersize?', file=sys.stderr)
raise sd.CallbackAbort
if data.size < outdata.size:
outdata[:len(data),0] = data
outdata[len(data):] = 0
raise sd.CallbackStop
else:
outdata[:,0] = data
try:
with sf.SoundFile(args.filename) as f:
#queue for recording input
qr = queue.Queue(maxsize=args.buffersize)
#queue for output WAVE file
q = queue.Queue(maxsize=args.buffersize)
event = threading.Event()
for _ in range(args.buffersize):
data = f.read(frames=args.blocksize, dtype='float32')
if data.size == 0:
break
q.put_nowait(data) # Pre-fill queue
stream = sd.Stream(
samplerate=f.samplerate, blocksize=args.blocksize,
dtype='float32', callback=callback, finished_callback=event.set,
latency='low')
with sf.SoundFile('output'+str(itr)+'.wav', mode='x', samplerate=f.samplerate,
channels=1) as rec_file:
with stream:
timeout = args.blocksize * args.buffersize / f.samplerate
while data.size != 0:
data = f.read(args.blocksize, dtype='float32')
q.put(data, timeout=timeout)
event.wait() # Wait until playback is finished
如果您不介意一次将整个输入和输出信号存储在内存中,您可以随意使用 sd.playrec()
。您将无法使用 sd.Stream
使用您自己的代码来减少延迟。 sd.playrec()
内部使用 sd.Stream
并且不会增加延迟。
如果您想减少延迟,您应该尝试为 blocksize
and/or latency
参数使用较低的值。但是请注意,低值将更加不稳定,并可能导致 playback/recording.
如果您不想一次将所有数据都存储在内存中,则不能使用 sd.playrec()
,您可以尝试使用 sd.Stream
,就像上面的示例一样。
但是请注意,这两行中的队列充其量是无用的:
qr.put(indata.copy())
rec_file.write(qr.get())
你不妨写:
rec_file.write(indata)
但请不要!
写入文件可能会长时间阻塞音频回调,从而导致音频丢失。
因此,使用队列是一个好主意(使用 indata.copy()
也是一个好主意)。
但是您应该只在回调函数中将写入您的qr
。 阅读 应该发生在不同的时间点。
你应该在 q.put(...)
之前或之后的 while
循环中执行 非阻塞 qr.get_nowait()
并将数据写入文件那里。
在回调函数中,您不应该执行 阻塞 qr.put(indata.copy())
,因为这可能会再次阻塞您的音频回调,导致掉线。
相反,您应该使用 qr.put_nowait()
。为避免队列满,您应该从 qr
中删除 maxsize
参数(但将其保留在另一个队列中!)。
最后,在离开 with stream
上下文管理器后,qr
中可能仍有数据尚未写入文件。
所以在流关闭后,您应该确保清空“记录队列”并将剩余的块写入文件。