From 010120e363647fe5c27b5c3d48ad7f9fa03f8415 Mon Sep 17 00:00:00 2001 From: Daniel Schulte Date: Thu, 9 Apr 2026 17:59:07 +0200 Subject: [PATCH] IVR: Add play_soundfiles_as_one Using play_soundfiles_as_one allows you to play multiple sound files while still being able to interrupt playback via DTMF tones. This is useful for e.g. menu applications where you want to play an announcement consisting of multiple files which is canceled should the user press a button (make a selection). This also supports the complete kwarg which ignores DTMF events during playback and returns only after all files have been played. --- yate/ivr.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/yate/ivr.py b/yate/ivr.py index cccb95d..01c758b 100644 --- a/yate/ivr.py +++ b/yate/ivr.py @@ -93,6 +93,48 @@ async def play_soundfile(self, path: str, repeat: bool = False, complete: bool = await self.playback_end_event.wait() return True + async def play_soundfiles_as_one(self, paths: list[str], complete: bool = False) -> bool: + """ + Play multiple audio files on this channel in a group. + + Playing sound files in a group means the files get played completely one after another without needing complete=True to force waiting and losing the possibility to respond to DTMF events. + + :param paths: list of paths to the audio file locations + :param complete: block coroutine until all audio playback has finished, not interrupted by DTMF events. + :return: True if the operation was successful, false otherwise + """ + + async def play_multisound(): + for path in paths: + msg_params = { + "source": f"wave/play/{path}", + "notify": self.call_id, + } + play_msg = MessageRequest("chan.attach", msg_params) + self.playback_end_event.clear() + await self.send_message_async(play_msg) + + cur_playback_done = False + while not cur_playback_done: + event_type = await self.wait_channel_event() + if event_type == ChannelEventType.DTMF: + if complete: + self.dtmf_event.clear() + continue # Ignore the event + break + elif event_type == ChannelEventType.PLAYBACK_END: + cur_playback_done = True + else: + break # Unknown event... + if not cur_playback_done: # Aborted by DTMF or unknown event + break + + multisound_task = asyncio.create_task(play_multisound()) + if complete: + # Block till all sounds have been played. + await multisound_task + return True + async def record_audio(self, path: str, time_limit_s: float = None) -> Optional[asyncio.Future]: """ Start audio recording on this channel