From 139eee8bd1bfece4f0ab8e9c41f329db1bd187a1 Mon Sep 17 00:00:00 2001 From: pstruebi Date: Mon, 24 Feb 2025 09:01:29 +0100 Subject: [PATCH] Improve the commandline ui --- auracast/multicast.py | 43 +++++++++++++++++++++++++++++++++++ auracast/multicast_control.py | 28 ++++++++++++++--------- 2 files changed, 60 insertions(+), 11 deletions(-) diff --git a/auracast/multicast.py b/auracast/multicast.py index 3ab3eb7..5c43f4b 100644 --- a/auracast/multicast.py +++ b/auracast/multicast.py @@ -50,6 +50,49 @@ from bumble.audio import io as audio_io import auracast_config +# modified from bumble +class ModWaveAudioInput(audio_io.ThreadedAudioInput): + """Audio input that reads PCM samples from a .wav file.""" + + def __init__(self, filename: str) -> None: + super().__init__() + self._filename = filename + self._wav: wave.Wave_read | None = None + self._bytes_read = 0 + self.rewind=True + + def _open(self) -> audio_io.PcmFormat: + self._wav = wave.open(self._filename, 'rb') + if self._wav.getsampwidth() != 2: + raise ValueError('sample width not supported') + return audio_io.PcmFormat( + audio_io.PcmFormat.Endianness.LITTLE, + audio_io.PcmFormat.SampleType.INT16, + self._wav.getframerate(), + self._wav.getnchannels(), + ) + + def _read(self, frame_size: int) -> bytes: + if not self._wav: + return b'' + + pcm_samples = self._wav.readframes(frame_size) + if not pcm_samples and self._bytes_read: + if not self.rewind: + return None + # Loop around. + self._wav.rewind() + self._bytes_read = 0 + pcm_samples = self._wav.readframes(frame_size) + + self._bytes_read += len(pcm_samples) + return pcm_samples + + def _close(self) -> None: + if self._wav: + self._wav.close() + +audio_io.WaveAudioInput = ModWaveAudioInput # ----------------------------------------------------------------------------- # Logging diff --git a/auracast/multicast_control.py b/auracast/multicast_control.py index 5d93f13..fc1417f 100644 --- a/auracast/multicast_control.py +++ b/auracast/multicast_control.py @@ -12,9 +12,15 @@ import multicast import auracast_config class Multicaster: + """ + A class responsible for managing the multicasting and audio streaming process. + + It provides methods to initialize and shutdown the broadcasting, as well as start and stop the streaming. + The class also manages the underlying device and advertising sets. + """ def __init__( - self, - global_conf: auracast_config.AuracastGlobalConfig, + self, + global_conf: auracast_config.AuracastGlobalConfig, big_conf: List[auracast_config.AuracastBigConfig] ): self.is_auracast_init = False @@ -61,9 +67,14 @@ class Multicaster: self.__init__(self.global_conf, self.big_conf) async def shutdown(self): - await self.device.stop_advertising() - for big in self.bigs.values(): - await big['advertising_set'].stop() + self.is_auracast_init = False + self. is_audio_init = False + if self.device: + await self.device.stop_advertising() + if self.big: + for big in self.bigs.values(): + if big['advertising_set']: + await big['advertising_set'].stop() await self.device_acm.__aexit__(None, None, None) # Manually triggering teardown @@ -71,7 +82,7 @@ class Multicaster: # example commandline ui async def command_line_ui(caster: Multicaster): while True: - command = await aioconsole.ainput("\nCommands: [start|stop|init|quit] > ") + command = await aioconsole.ainput("\nCommands: [start_audio|stop_audio|stop|init|init_audio|quit] > ") if command.strip().lower() == "start_audio": caster.start_streaming() @@ -94,11 +105,6 @@ async def command_line_ui(caster: Multicaster): elif command.strip().lower() == "init_audio": await caster.init_audio() - #elif command.strip().lower().startswith("set "): - #_, new_message = command.split(" ", 1) - #multicaster.set_message(new_message) - #print(f"✏️ Message updated to: {new_message}") - elif command.strip().lower() == "quit": print("👋 Exiting...") if caster.device: