diff --git a/torchlight_changes_unloze/torchlight3/Torchlight/AudioManager.py b/torchlight_changes_unloze/torchlight3/Torchlight/AudioManager.py index 71c6d4f5..e46df87d 100755 --- a/torchlight_changes_unloze/torchlight3/Torchlight/AudioManager.py +++ b/torchlight_changes_unloze/torchlight3/Torchlight/AudioManager.py @@ -303,8 +303,9 @@ class AudioClip(): def __del__(self): self.Logger.info("~AudioClip()") - def Play(self, seconds = None, rubberband = None, dec_params = None, *args): - return self.AudioPlayer.PlayURI(self.URI, position = seconds, rubberband = rubberband, dec_params = dec_params, *args) + def Play(self, seconds = None, rubberband = None, dec_params = None, bitrate = None, backwards = None, *args): + return self.AudioPlayer.PlayURI(self.URI, position = seconds, rubberband = rubberband, dec_params = dec_params, bitrate = bitrate, + backwards = backwards, *args) def Stop(self): return self.AudioPlayer.Stop() diff --git a/torchlight_changes_unloze/torchlight3/Torchlight/Commands.py b/torchlight_changes_unloze/torchlight3/Torchlight/Commands.py index c2ddfb1c..f2810cd7 100755 --- a/torchlight_changes_unloze/torchlight3/Torchlight/Commands.py +++ b/torchlight_changes_unloze/torchlight3/Torchlight/Commands.py @@ -8,6 +8,33 @@ import math from .Utils import Utils, DataHolder import traceback +def get_birtate(message): + bitrate = [] + try: + for msg in message[1].split(" "): #checking if + if "bitrate=" in msg: + bitrate = int(msg.split("bitrate=",1)[1]) + if bitrate < 0.0: + bitrate = 0.01 + if bitrate > 2000: + bitrate = 20 + bitrate.append(f"bitrate=tempo={bitrate}") + except Exception: + pass + return bitrate + +def get_backwards(message): + backwards = None + try: + for msg in message[1].split(" "): #checking if pitch= or tempo= is specified + if "backward=" in msg.lower(): + backwards = True + elif "backwards=" in msg.lower(): + backwards = True + except Exception: + pass + return backwards + def get_rubberBand(message): rubberband = [] try: @@ -548,6 +575,8 @@ class VoiceCommands(BaseCommand): return 0 rubberband = get_rubberBand(message) + backwards = get_backwards(message) + bitrate = get_birtate(message) if message[0] == "!random": Trigger = self.random.choice(list(self.VoiceTriggers.values())) @@ -614,7 +643,7 @@ class VoiceCommands(BaseCommand): if not AudioClip: return 1 - return AudioClip.Play(rubberband = rubberband) + return AudioClip.Play(rubberband = rubberband, bitrate = bitrate, backwards = backwards) class YouTube(BaseCommand): @@ -647,7 +676,9 @@ class YouTube(BaseCommand): #turning the string into a list where get_rubberband just picks the second element to search in dline = ['', line.split(" ", 1)[1]] rubberband = get_rubberBand(dline) - return AudioClip.Play(Time, rubberband = rubberband) + backwards = get_backwards(dline) + bitrate = get_birtate(dline) + return AudioClip.Play(Time, rubberband = rubberband, bitrate = bitrate, backwards = backwards) class YouTubeSearch(BaseCommand): import json @@ -693,7 +724,9 @@ class YouTubeSearch(BaseCommand): #turning the string into a list where get_rubberband just picks the second element to search in dline = ['', line.split(" ", 1)[1]] rubberband = get_rubberBand(dline) - return AudioClip.Play(Time, rubberband = rubberband) + backwards = get_backwards(dline) + bitrate = get_birtate(dline) + return AudioClip.Play(Time, rubberband = rubberband, bitrate = bitrate, backwards = backwards) class Say(BaseCommand): @@ -721,9 +754,17 @@ class Say(BaseCommand): try: dline = ['', message.split(" ", 1)[1]] rubberband = get_rubberBand(dline) + backwards = get_backwards(dline) except Exception: rubberband = None - if AudioClip.Play(rubberband = rubberband): + backwards = None + + try: + dline = ['', message.split(" ", 1)[1]] + bitrate = get_birtate(dline) + except Exception: + bitrate = None + if AudioClip.Play(rubberband = rubberband, bitrate = bitrate, backwards = backwards): AudioClip.AudioPlayer.AddCallback("Stop", lambda: os.unlink(TempFile.name)) return 0 else: diff --git a/torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py b/torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py index b0ff290b..2b8db621 100755 --- a/torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py +++ b/torchlight_changes_unloze/torchlight3/Torchlight/FFmpegAudioPlayer.py @@ -12,52 +12,53 @@ import sys SAMPLEBYTES = 2 class FFmpegAudioPlayerFactory(): - VALID_CALLBACKS = ["Play", "Stop", "Update"] + VALID_CALLBACKS = ["Play", "Stop", "Update"] - def __init__(self, master): - self.Logger = logging.getLogger(__class__.__name__) - self.Master = master - self.Torchlight = self.Master.Torchlight + def __init__(self, master): + self.Logger = logging.getLogger(__class__.__name__) + self.Master = master + self.Torchlight = self.Master.Torchlight - def __del__(self): - self.Master.Logger.info("~FFmpegAudioPlayerFactory()") - self.Quit() + def __del__(self): + self.Master.Logger.info("~FFmpegAudioPlayerFactory()") + self.Quit() - def NewPlayer(self): - self.Logger.debug(sys._getframe().f_code.co_name) - Player = FFmpegAudioPlayer(self) - return Player + def NewPlayer(self): + self.Logger.debug(sys._getframe().f_code.co_name) + Player = FFmpegAudioPlayer(self) + return Player - def Quit(self): - self.Master.Logger.info("FFmpegAudioPlayerFactory->Quit()") + def Quit(self): + self.Master.Logger.info("FFmpegAudioPlayerFactory->Quit()") class FFmpegAudioPlayer(): - def __init__(self, master): - self.Master = master - self.Torchlight = self.Master.Torchlight - self.Playing = False + def __init__(self, master): + self.Master = master + self.Torchlight = self.Master.Torchlight + self.Playing = False - self.Host = ( - self.Torchlight().Config["VoiceServer"]["Host"], - self.Torchlight().Config["VoiceServer"]["Port"] - ) - self.SampleRate = float(self.Torchlight().Config["VoiceServer"]["SampleRate"]) + self.Host = ( + self.Torchlight().Config["VoiceServer"]["Host"], + self.Torchlight().Config["VoiceServer"]["Port"] + ) + self.SampleRate = float(self.Torchlight().Config["VoiceServer"]["SampleRate"]) - self.StartedPlaying = None - self.StoppedPlaying = None - self.Seconds = 0.0 + self.StartedPlaying = None + self.StoppedPlaying = None + self.Seconds = 0.0 - self.Writer = None - self.Process = None + self.Writer = None + self.Process = None - self.Callbacks = [] + self.Callbacks = [] - def __del__(self): - self.Master.Logger.debug("~FFmpegAudioPlayer()") - self.Stop() + def __del__(self): + self.Master.Logger.debug("~FFmpegAudioPlayer()") + self.Stop() - def PlayURI(self, uri, position, rubberband = None, dec_params = None, *args): + def PlayURI(self, uri, position, rubberband = None, dec_params = None, bitrate = None, + backwards = None, *args): 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] @@ -68,122 +69,137 @@ class FFmpegAudioPlayer(): if dec_params: Command += dec_params - if rubberband: + if rubberband and backwards: Command += ["-filter:a"] rubberCommand = "" for rubber in rubberband: rubberCommand += rubber + ", " rubberCommand = rubberCommand[:-2] - Command += [rubberCommand] + Command += [rubberCommand + "[reversed];[reversed]areverse"] #[reversed] is intermediate stream label so reverse knows what stream label to reverse + else: + if rubberband: + Command += ["-filter:a"] + rubberCommand = "" + for rubber in rubberband: + rubberCommand += rubber + ", " + rubberCommand = rubberCommand[:-2] + Command += [rubberCommand] + if backwards: + Command += ["-af"] + Command += ["areverse"] + if bitrate: + Command += ["-ab ", str(bitrate), "k"] + self.Master.Logger.debug(f"command: {Command}") Command += ["-"] + #self.Master.Logger.debug(f"command: {Command}") asyncio.ensure_future(self._stream_subprocess(Command)) return True - def Stop(self, force = True): - if not self.Playing: - return False + def Stop(self, force = True): + if not self.Playing: + return False - if self.Process: - try: - self.Process.terminate() - self.Process.kill() - self.Process = None - except ProcessLookupError: - pass + if self.Process: + try: + self.Process.terminate() + self.Process.kill() + self.Process = None + except ProcessLookupError: + pass - if self.Writer: - if force: - Socket = self.Writer.transport.get_extra_info("socket") - if Socket: - Socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, - struct.pack("ii", 1, 0)) + if self.Writer: + if force: + Socket = self.Writer.transport.get_extra_info("socket") + if Socket: + Socket.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, + struct.pack("ii", 1, 0)) - self.Writer.transport.abort() + self.Writer.transport.abort() - self.Writer.close() + self.Writer.close() - self.Playing = False + self.Playing = False - self.Callback("Stop") - del self.Callbacks + self.Callback("Stop") + del self.Callbacks - return True + return True - def AddCallback(self, cbtype, cbfunc): - if not cbtype in FFmpegAudioPlayerFactory.VALID_CALLBACKS: - return False + def AddCallback(self, cbtype, cbfunc): + if not cbtype in FFmpegAudioPlayerFactory.VALID_CALLBACKS: + return False - self.Callbacks.append((cbtype, cbfunc)) - return True + self.Callbacks.append((cbtype, cbfunc)) + return True - def Callback(self, cbtype, *args, **kwargs): - for Callback in self.Callbacks: - if Callback[0] == cbtype: - try: - Callback[1](*args, **kwargs) - except Exception as e: - self.Master.Logger.error(traceback.format_exc()) + def Callback(self, cbtype, *args, **kwargs): + for Callback in self.Callbacks: + if Callback[0] == cbtype: + try: + Callback[1](*args, **kwargs) + except Exception as e: + self.Master.Logger.error(traceback.format_exc()) - async def _updater(self): - LastSecondsElapsed = 0.0 + async def _updater(self): + LastSecondsElapsed = 0.0 - while self.Playing: - SecondsElapsed = time.time() - self.StartedPlaying + while self.Playing: + SecondsElapsed = time.time() - self.StartedPlaying - if SecondsElapsed > self.Seconds: - SecondsElapsed = self.Seconds + if SecondsElapsed > self.Seconds: + SecondsElapsed = self.Seconds - self.Callback("Update", LastSecondsElapsed, SecondsElapsed) + self.Callback("Update", LastSecondsElapsed, SecondsElapsed) - if SecondsElapsed >= self.Seconds: - if not self.StoppedPlaying: - print("BUFFER UNDERRUN!") - self.Stop(False) - return + if SecondsElapsed >= self.Seconds: + if not self.StoppedPlaying: + print("BUFFER UNDERRUN!") + self.Stop(False) + return - LastSecondsElapsed = SecondsElapsed + LastSecondsElapsed = SecondsElapsed - await asyncio.sleep(0.1) + await asyncio.sleep(0.1) - async def _read_stream(self, stream, writer): - Started = False + async def _read_stream(self, stream, writer): + Started = False - while stream and self.Playing: - Data = await stream.read(65536) + while stream and self.Playing: + Data = await stream.read(65536) - if Data: - writer.write(Data) - await writer.drain() + if Data: + writer.write(Data) + await writer.drain() - Bytes = len(Data) - Samples = Bytes / SAMPLEBYTES - Seconds = Samples / self.SampleRate + Bytes = len(Data) + Samples = Bytes / SAMPLEBYTES + Seconds = Samples / self.SampleRate - self.Seconds += Seconds + 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 + 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.StoppedPlaying = time.time() - async def _stream_subprocess(self, cmd): - if not self.Playing: - return + async def _stream_subprocess(self, cmd): + if not self.Playing: + return - _, 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.Seconds == 0.0: + self.Stop()