python-sounddevice
python-sounddevice copied to clipboard
Advice on detecting when Sounddevice has stopped playing audio (and using multiple OutputStreams)
I am writing the audio component for a digital assistant. An important feature is the ability to pause a "thread" producing audio output, (let's call this T_1), start a new one T_2, then when T_2 ends, resume T_1.
Right now, each "thread" has an associated outputStream (and callback). This seems to work but PortAudio literature states that opening multiple OutputStreams is not a good idea. I also do too many things in the callback.
I am looking at "Proposed Enhancements to PortAudio API" 019 []https://www.portaudio.com/docs/proposals/019-NotifyClientWhenAllBuffersHavePlayed.html
I have tried polling with active(). This doesn't work nor do I understand why it should work. As I understand it, a stream is active if it is not stopped. I don't want to open and close the outputStream.
I've reading PortAudio []https://www.portaudio.com/docs/v19-doxydocs/start_stop_abort.html , I've tried , after the callback has finished outputting, putting a thread to sleep for a time based on the estimated duration of the audio .... and a fudge_factor.
audio_context is shared between callback and the producing thread
def callback(outdata, frames, time, status):
assert frames == audio_context.block_size
if status.output_underflow:
print("Output underflow: increase blocksize?", file=sys.stderr)
raise sd.CallbackAbort
assert not status
try:
# reading messages from a queue
message = audio_context.input_queue.get()
if message is None:
raise sd.CallbackStop
data = message.data
except queue.Empty as e:
print("Buffer is empty: increase buffersize?", file=sys.stderr)
raise sd.CallbackAbort from e
if len(data) < len(outdata):
outdata[: len(data)] = data
outdata[len(data) :].fill(0)
# this should be the last data written by the callback
audio_context.event.set()
else:
outdata[:] = data
in producing thread
# wait until callback is finished
audio_context.event.wait()
# let the audio finish. The a_fudge_factor is < 1.0
time.sleep(duration * a_fudge_factor)
This sort of works. I can't seem to find a more acceptable way of doing this? Also is using multiple outputStreams that bad if one has the resources? Any advice would be appreciated.
This seems to work but PortAudio literature states that opening multiple OutputStreams is not a good idea.
Yes, this is normally not recommended.
I would try to use a single stream that has a callback function that communicates with the main thread and produces blocks of audio containing parts of T_1 and T_2 as appropriate. If silence is needed in between, the audio signal should contain zero values for as long as desired.