From 6715d2a07ce02c1b15ff22606d3ff1f48ffae7f4 Mon Sep 17 00:00:00 2001 From: Metroid_Skittles Date: Sat, 7 Feb 2026 07:49:36 +0100 Subject: [PATCH] Update torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py --- .../Torchlight/FFmpegAudioPlayer.py | 101 +++++++++++------- 1 file changed, 60 insertions(+), 41 deletions(-) diff --git a/torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py b/torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py index 18e86bd..bc0baf6 100755 --- a/torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py +++ b/torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py @@ -60,6 +60,9 @@ class FFmpegAudioPlayer(): def PlayURI(self, uri, position, rubberband = None, dec_params = None, bitrate = None, backwards = None, *args): + if self.Playing: + self.Stop() + if position: PosStr = str(datetime.timedelta(seconds = position)) #Command = ["/usr/bin/ffmpeg", "-ss", PosStr, "-i", uri, "-acodec", "pcm_s16le", "-ac", "1", "-ar", str(int(self.SampleRate)), "-f", "s16le", "-vn", *args] @@ -71,7 +74,13 @@ class FFmpegAudioPlayer(): if args: Command += list(args) + # Reset per-play counters/state in case this instance is reused. + self.Seconds = 0.0 + self.StartedPlaying = None + self.StoppedPlaying = None + self._byte_remainder = 0 self.Playing = True + if dec_params: Command += dec_params @@ -172,7 +181,7 @@ class FFmpegAudioPlayer(): if SecondsElapsed >= self.Seconds: if not self.StoppedPlaying: - print("BUFFER UNDERRUN!") + self.Master.Logger.warning("BUFFER UNDERRUN") self.Stop(False) return @@ -180,55 +189,65 @@ class FFmpegAudioPlayer(): async def _read_stream(self, stream, writer): Started = False + try: + while stream and self.Playing: + Data = await stream.read(65536) - while stream and self.Playing: - Data = await stream.read(65536) + if Data: + try: + writer.write(Data) + await writer.drain() + except (ConnectionError, BrokenPipeError, OSError) as ex: + self.Master.Logger.warning("Voice socket write failed: %s", ex) + break - if Data: - writer.write(Data) - await writer.drain() + Bytes = len(Data) + frame_size = SAMPLEBYTES * self.Channels - Bytes = len(Data) - frame_size = SAMPLEBYTES * self.Channels + # Guard against invalid configuration and use integral frame counting + if frame_size <= 0 or self.SampleRate <= 0: + self.Master.Logger.error( + "Invalid audio configuration: Channels=%r, SampleRate=%r", + self.Channels, + self.SampleRate, + ) + else: + # Accumulate remainder bytes to avoid systematic undercounting + total_bytes = getattr(self, "_byte_remainder", 0) + Bytes + Frames = total_bytes // frame_size + self._byte_remainder = total_bytes % frame_size + Seconds = Frames / self.SampleRate + self.Seconds += Seconds - # Guard against invalid configuration and use integral frame counting - if frame_size <= 0 or self.SampleRate <= 0: - self.Master.Logger.error( - "Invalid audio configuration: Channels=%r, SampleRate=%r", - self.Channels, - self.SampleRate, - ) + if not Started: + Started = True + self.Callback("Play") + self.StartedPlaying = time.time() + asyncio.ensure_future(self._updater()) else: - # Accumulate remainder bytes to avoid systematic undercounting - total_bytes = getattr(self, "_byte_remainder", 0) + Bytes - Frames = total_bytes // frame_size - self._byte_remainder = total_bytes % frame_size - Seconds = Frames / self.SampleRate - self.Seconds += Seconds - - if not Started: - Started = True - self.Callback("Play") - self.StartedPlaying = time.time() - asyncio.ensure_future(self._updater()) - else: - self.Process = None - break - - self.StoppedPlaying = time.time() + self.Process = None + break + finally: + self.StoppedPlaying = time.time() async def _stream_subprocess(self, cmd): if not self.Playing: return + try: + _, self.Writer = await asyncio.open_connection(self.Host[0], self.Host[1]) - _, self.Writer = await asyncio.open_connection(self.Host[0], self.Host[1]) + Process = await asyncio.create_subprocess_exec(*cmd, + stdout = asyncio.subprocess.PIPE, stderr = asyncio.subprocess.DEVNULL) + self.Process = Process - Process = await asyncio.create_subprocess_exec(*cmd, - stdout = asyncio.subprocess.PIPE, stderr = asyncio.subprocess.DEVNULL) - self.Process = Process + await self._read_stream(Process.stdout, self.Writer) + await Process.wait() - await self._read_stream(Process.stdout, self.Writer) - await Process.wait() - - if self.Seconds == 0.0: - self.Stop() + if self.Playing: + self.Stop(False) + except asyncio.CancelledError: + raise + except Exception: + self.Master.Logger.error(traceback.format_exc()) + if self.Playing: + self.Stop()