callback is not called in RawOutputStream when using some devices
Hi, I have a simple application playing a stream (see code below). That works well when I use my laptop's speakers but when I switch to a bluetooth headset, the callback function is never called (I checked by using print statements) - this consequently leads to a Queue.Full exception.
The headset works well in all other cases, even in the same application. I use sd.default.device['output'] as device ID, so I expect sounddevice to detect the correct device.
Could it be that the sd.default has to be updated when I start playing a new file/stream??
Thanks for your time and your great library btw :-)
def _play_stream(self, source):
_q = Queue(maxsize=20)
def _callback_stream(outdata, frames, time, status):
if status.output_underflow:
raise sd.CallbackAbort
assert not status
try:
data = _q.get_nowait()
#data = data
except queue.Empty as e:
raise sd.CallbackAbort from e
assert len(data) == len(outdata)
outdata[:] = data
try:
info = ffmpeg.probe(source)
except ffmpeg.Error as e:
logger.error(e)
streams = info.get('streams', [])
if len(streams) != 1:
logger.error('There must be exactly one stream available')
stream = streams[0]
if stream.get('codec_type') != 'audio':
logger.error('The stream must be an audio stream')
channels = stream['channels']
samplerate = float(stream['sample_rate'])
try:
process = ffmpeg.input(source).filter('volume', self._volume).output(
'pipe:',
format='f32le',
acodec='pcm_f32le',
ac=channels,
ar=samplerate,
loglevel='quiet',
).run_async(pipe_stdout=True)
stream = sd.RawOutputStream(
samplerate=samplerate, blocksize=1024,
device=sd.default.device['output'], channels=channels, dtype='float32',
callback=_callback_stream)
read_size = 1024 * channels * stream.samplesize
for _ in range(20):
_q.put_nowait(process.stdout.read(read_size))
logger.info("Starte Stream ...")
with stream:
timeout = 1024 * 20 / samplerate
while True:
_q.put(process.stdout.read(read_size), timeout=timeout)
#except KeyboardInterrupt:
# logger.error('\nInterrupted by user')
except queue.Full as e:
# A timeout occurred, i.e. there was an error in the callback
logger.error("Queue ist voll: {}", e)
except Exception as e:
logger.error(e)
I use
sd.default.device['output']as device ID, so I expect sounddevice to detect the correct device.
The selection of the default device, and in fact any device handling is done by the underlying PortAudio library.
Could it be that the
sd.defaulthas to be updated when I start playing a new file/stream??
I don't know, probably.
One thing you could try is to call sd._terminate(); sd._initialize() after switching the device. See also https://python-sounddevice.readthedocs.io/en/0.4.1/api/expert-mode.html.
If this doesn't work, it might be a limitation of the PortAudio library. You could ask in their mailing list about it: http://portaudio.com/contacts.html