diff --git a/naff/api/voice/audio.py b/naff/api/voice/audio.py index 7c55a4cf9..b2319e8a9 100644 --- a/naff/api/voice/audio.py +++ b/naff/api/voice/audio.py @@ -134,7 +134,7 @@ def audio_complete(self) -> bool: return False return True - def _create_process(self) -> None: + def _create_process(self, *, block: bool = True) -> None: before = ( self.ffmpeg_before_args if isinstance(self.ffmpeg_before_args, list) else self.ffmpeg_before_args.split() ) @@ -162,8 +162,9 @@ def _create_process(self) -> None: ) self.read_ahead_task.start() - # block until some data is in the buffer - self.buffer.initialised.wait() + if block: + # block until some data is in the buffer + self.buffer.initialised.wait() def _read_ahead(self) -> None: while self.process: @@ -181,6 +182,24 @@ def _read_ahead(self) -> None: self.buffer.initialised.set() time.sleep(0.1) + def pre_buffer(self, duration: None | float = None) -> None: + """ + Start pre-buffering the audio. + + Args: + duration: The duration of audio to pre-buffer. + """ + if duration: + self.buffer_seconds = duration + + if self.process and self.process.poll() is None: + raise RuntimeError("Cannot pre-buffer an already running process") + # sanity value enforcement to prevent audio weirdness + self.buffer = AudioBuffer() + self.buffer.initialised.clear() + + self._create_process(block=False) + def read(self, frame_size: int) -> bytes: """ Reads frame_size bytes of audio from the buffer. @@ -190,6 +209,9 @@ def read(self, frame_size: int) -> bytes: """ if not self.process: self._create_process() + if not self.buffer.initialised.is_set(): + # we cannot start playing until the buffer is initialised + self.buffer.initialised.wait() data = self.buffer.read(frame_size) diff --git a/naff/api/voice/player.py b/naff/api/voice/player.py index 0ee449795..17a454a9b 100644 --- a/naff/api/voice/player.py +++ b/naff/api/voice/player.py @@ -84,10 +84,7 @@ def play(self) -> None: """Start playing.""" self._stop_event.clear() self._resume.set() - try: - self.start() - finally: - self.current_audio.cleanup() + self.start() def run(self) -> None: """The main player loop to send audio to the voice websocket."""