I'm trying to create a low-level Stream that will allow me to output a WAVE file, while simultaneously recording an input on the same audio device. My audio device is setup so that the output WAVE file will be played through the output and this runs through a system that then goes to an input on the device. Using the convenience function playrec() from python-sounddevice gives me a full recording of the what's seen at the input, however with my code using the lower-level Stream() function the recording starts late and the last tiny bit of the audio isn't recorded. The reason I want to use the lower-level Stream() function is to test whether or not I can decrease the overall delay in that system compared to playrec(). I tried changing the blocksize and buffersize to no avail.
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