Initial support for playing multiple sounds to the same stream

This commit is contained in:
Fabio Manganiello 2018-12-24 18:58:44 +01:00
parent 3d2636b09c
commit 3baf0b1589

View file

@ -172,7 +172,8 @@ class SoundPlugin(Plugin):
@action @action
def play(self, file=None, sound=None, device=None, blocksize=None, def play(self, file=None, sound=None, device=None, blocksize=None,
bufsize=Sound._DEFAULT_BUFSIZE, samplerate=None, channels=None): bufsize=Sound._DEFAULT_BUFSIZE, samplerate=None, channels=None,
stream_index=None):
""" """
Plays a sound file (support formats: wav, raw) or a synthetic sound. Plays a sound file (support formats: wav, raw) or a synthetic sound.
@ -209,6 +210,11 @@ class SoundPlugin(Plugin):
:param channels: Number of audio channels. Default: number of channels :param channels: Number of audio channels. Default: number of channels
in the audio file in file mode, 1 if in synth mode in the audio file in file mode, 1 if in synth mode
:type channels: int :type channels: int
:param stream_index: If specified, play to an already active stream
index (you can get them through
:method:`platypush.plugins.sound.query_active_streams`). Default:
creates a new audio stream through PortAudio.
""" """
if not file and not sound: if not file and not sound:
@ -222,7 +228,6 @@ class SoundPlugin(Plugin):
self.playback_paused_changed.clear() self.playback_paused_changed.clear()
stream_index = None
q = queue.Queue(maxsize=bufsize) q = queue.Queue(maxsize=bufsize)
f = None f = None
t = 0. t = 0.
@ -273,17 +278,23 @@ class SoundPlugin(Plugin):
q.put_nowait(data) # Pre-fill the audio queue q.put_nowait(data) # Pre-fill the audio queue
streamtype = sd.RawOutputStream if file else sd.OutputStream
completed_callback_event = Event()
stream = streamtype(samplerate=samplerate, blocksize=blocksize,
device=device, channels=channels,
dtype='float32',
callback=self._play_audio_callback(
q=q, blocksize=blocksize,
streamtype=streamtype),
finished_callback=completed_callback_event.set)
stream_index = self.start_playback(stream, completed_callback_event) if stream_index is None:
completed_callback_event = Event()
streamtype = sd.RawOutputStream if file else sd.OutputStream
stream = streamtype(samplerate=samplerate, blocksize=blocksize,
device=device, channels=channels,
dtype='float32',
callback=self._play_audio_callback(
q=q, blocksize=blocksize,
streamtype=streamtype),
finished_callback=completed_callback_event.set)
stream_index = self.start_playback(stream,
completed_callback_event)
else:
stream = self.active_streams[stream_index]
completed_callback_event = self.completed_callback_events[stream_index]
with stream: with stream:
# Timeout set until we expect all the buffered blocks to # Timeout set until we expect all the buffered blocks to
@ -327,7 +338,7 @@ class SoundPlugin(Plugin):
f.close() f.close()
f = None f = None
self.stop_playback(stream_index) self.stop_playback([stream_index])
@action @action
@ -533,12 +544,12 @@ class SoundPlugin(Plugin):
except queue.Empty as e: except queue.Empty as e:
self.logger.warning('Recording timeout: audio callback failed?') self.logger.warning('Recording timeout: audio callback failed?')
finally: finally:
self.stop_playback(stream_index) self.stop_playback([stream_index])
self.stop_recording() self.stop_recording()
@action @action
def get_active_streams(self): def query_active_streams(self):
""" """
:returns: A list of active players :returns: A list of active players
""" """
@ -572,7 +583,12 @@ class SoundPlugin(Plugin):
return stream_index return stream_index
@action @action
def stop_playback(self, *streams): def stop_playback(self, streams=None):
"""
:param streams: Stream to stop by index (default: all)
:type streams: list[int]
"""
with self.playback_state_lock: with self.playback_state_lock:
if not streams: if not streams:
streams = self.active_streams.keys() streams = self.active_streams.keys()