From c7ec2b515962eed725969a6d686b28f0c7a438e3 Mon Sep 17 00:00:00 2001 From: Choco <94597336+ChocoMeow@users.noreply.github.com> Date: Sat, 6 Jul 2024 15:27:19 +0800 Subject: [PATCH] Rewrote filters --- cogs/effect.py | 22 ++-- ipc/methods.py | 6 +- voicelink/filters.py | 248 ++++++++++++++++++++----------------------- voicelink/player.py | 28 ++++- 4 files changed, 157 insertions(+), 147 deletions(-) diff --git a/cogs/effect.py b/cogs/effect.py index cd4a2b1..835d536 100644 --- a/cogs/effect.py +++ b/cogs/effect.py @@ -69,7 +69,7 @@ class Effect(commands.Cog): if player.filters.has_filter(filter_tag="speed"): player.filters.remove_filter(filter_tag="speed") - await player.add_filter(voicelink.Timescale(tag="speed", speed=value)) + await player.add_filter(voicelink.Timescale(tag="speed", speed=value), ctx.author) await ctx.send(f"You set the speed to **{value}**.") @commands.hybrid_command(name="karaoke", aliases=get_aliases("karaoke")) @@ -86,7 +86,7 @@ class Effect(commands.Cog): if player.filters.has_filter(filter_tag="karaoke"): player.filters.remove_filter(filter_tag="karaoke") - await player.add_filter(voicelink.Karaoke(tag="karaoke", level=level, mono_level=monolevel, filter_band=filterband, filter_width=filterwidth)) + await player.add_filter(voicelink.Karaoke(tag="karaoke", level=level, mono_level=monolevel, filter_band=filterband, filter_width=filterwidth), ctx.author) await send(ctx, "karaoke", level, monolevel, filterband, filterwidth) @commands.hybrid_command(name="tremolo", aliases=get_aliases("tremolo")) @@ -101,7 +101,7 @@ class Effect(commands.Cog): if player.filters.has_filter(filter_tag="tremolo"): player.filters.remove_filter(filter_tag="tremolo") - await player.add_filter(voicelink.Tremolo(tag="tremolo", frequency=frequency, depth=depth)) + await player.add_filter(voicelink.Tremolo(tag="tremolo", frequency=frequency, depth=depth), ctx.author) await send(ctx, "tremolo&vibrato", frequency, depth) @commands.hybrid_command(name="vibrato", aliases=get_aliases("vibrato")) @@ -116,7 +116,7 @@ class Effect(commands.Cog): if player.filters.has_filter(filter_tag="vibrato"): player.filters.remove_filter(filter_tag="vibrato") - await player.add_filter(voicelink.Vibrato(tag="vibrato", frequency=frequency, depth=depth)) + await player.add_filter(voicelink.Vibrato(tag="vibrato", frequency=frequency, depth=depth), ctx.author) await send(ctx, "tremolo&vibrato", frequency, depth) @commands.hybrid_command(name="rotation", aliases=get_aliases("rotation")) @@ -128,7 +128,7 @@ class Effect(commands.Cog): if player.filters.has_filter(filter_tag="rotation"): player.filters.remove_filter(filter_tag="rotation") - await player.add_filter(voicelink.Rotation(tag="rotation", rotation_hertz=hertz)) + await player.add_filter(voicelink.Rotation(tag="rotation", rotation_hertz=hertz), ctx.author) await send(ctx, "rotation", hertz) @commands.hybrid_command(name="distortion", aliases=get_aliases("distortion")) @@ -139,7 +139,7 @@ class Effect(commands.Cog): if player.filters.has_filter(filter_tag="distortion"): player.filters.remove_filter(filter_tag="distortion") - await player.add_filter(voicelink.Distortion(tag="distortion", sin_offset=0.0, sin_scale=1.0, cos_offset=0.0, cos_scale=1.0, tan_offset=0.0, tan_scale=1.0, offset=0.0, scale=1.0)) + await player.add_filter(voicelink.Distortion(tag="distortion", sin_offset=0.0, sin_scale=1.0, cos_offset=0.0, cos_scale=1.0, tan_offset=0.0, tan_scale=1.0, offset=0.0, scale=1.0), ctx.author) await send(ctx, "distortion") @commands.hybrid_command(name="lowpass", aliases=get_aliases("lowpass")) @@ -151,7 +151,7 @@ class Effect(commands.Cog): if player.filters.has_filter(filter_tag="lowpass"): player.filters.remove_filter(filter_tag="lowpass") - await player.add_filter(voicelink.LowPass(tag="lowpass", smoothing=smoothing)) + await player.add_filter(voicelink.LowPass(tag="lowpass", smoothing=smoothing), ctx.author) await send(ctx, "lowpass", smoothing) @commands.hybrid_command(name="channelmix", aliases=get_aliases("channelmix")) @@ -168,7 +168,7 @@ class Effect(commands.Cog): if player.filters.has_filter(filter_tag="channelmix"): player.filters.remove_filter(filter_tag="channelmix") - await player.add_filter(voicelink.ChannelMix(tag="channelmix", left_to_left=left_to_left, right_to_right=right_to_right, left_to_right=left_to_right, right_to_left=right_to_left)) + await player.add_filter(voicelink.ChannelMix(tag="channelmix", left_to_left=left_to_left, right_to_right=right_to_right, left_to_right=left_to_right, right_to_left=right_to_left), ctx.author) await send(ctx, "channelmix", left_to_left, right_to_right, left_to_right, right_to_left) @commands.hybrid_command(name="nightcore", aliases=get_aliases("nightcore")) @@ -177,7 +177,7 @@ class Effect(commands.Cog): "Add nightcore filter into your player." player = await check_access(ctx) - await player.add_filter(voicelink.Timescale.nightcore()) + await player.add_filter(voicelink.Timescale.nightcore(), ctx.author) await send(ctx, "nightcore") @commands.hybrid_command(name="8d", aliases=get_aliases("8d")) @@ -186,7 +186,7 @@ class Effect(commands.Cog): "Add 8D filter into your player." player = await check_access(ctx) - await player.add_filter(voicelink.Rotation.nightD()) + await player.add_filter(voicelink.Rotation.nightD(), ctx.author) await send(ctx, "8d") @commands.hybrid_command(name="vaporwave", aliases=get_aliases("vaporwave")) @@ -195,7 +195,7 @@ class Effect(commands.Cog): "Add vaporwave filter into your player." player = await check_access(ctx) - await player.add_filter(voicelink.Timescale.vaporwave()) + await player.add_filter(voicelink.Timescale.vaporwave(), ctx.author) await send(ctx, "vaporwave") @commands.hybrid_command(name="cleareffect", aliases=get_aliases("cleareffect")) diff --git a/ipc/methods.py b/ipc/methods.py index c64abf6..29e52f4 100644 --- a/ipc/methods.py +++ b/ipc/methods.py @@ -5,7 +5,7 @@ from typing import List, Dict, Union, Optional from discord import User, Member, VoiceChannel from discord.ext import commands -from voicelink import Player, Track, Playlist, NodePool, decode, LoopType +from voicelink import Player, Track, Playlist, NodePool, decode, LoopType, Filters from addons import lyricsPlatform RATELIMIT_COUNTER: Dict[int, Dict[str, float]] = {} @@ -111,7 +111,9 @@ async def initPlayer(player: Player, member: Member, data: Dict) -> Dict: "is_paused": player.is_paused, "is_dj": player.is_privileged(member, check_user_join=False), "autoplay": player.settings.get("autoplay", False), - "volume": player.volume + "volume": player.volume, + "filters": [{"tag": filter.tag, "scope": filter.scope, "payload": filter.payload} for filter in player.filters.get_filters()], + "available_filters": [{"tag": name, "scope": filter.scope, "payload": filter.payload} for name, filter in Filters.get_available_filters().items()] } async def closeConnection(bot: commands.Bot, data: Dict) -> None: diff --git a/voicelink/filters.py b/voicelink/filters.py index 7751793..0829e14 100644 --- a/voicelink/filters.py +++ b/voicelink/filters.py @@ -38,6 +38,7 @@ class Filter: """ def __init__(self): self.payload: Dict[str, List] = None + self.scope: Dict[str, List] = None self.tag: str = None class Filters: @@ -71,6 +72,20 @@ class Filters: def get_filters(self) -> List[Filter]: return self._filters + + @classmethod + def get_available_filters(cls) -> Dict[str, Filter]: + return { + "karaoke": Karaoke(), + "tremolo": Tremolo(), + "vibrato": Vibrato(), + "rotation": Rotation(), + "distortion": Distortion(), + "lowpass": LowPass(), + "nightcore": Timescale.nightcore(), + "vaporwave": Timescale.vaporwave(), + "8d": Rotation.nightD(), + } class Equalizer(Filter): """ @@ -80,7 +95,7 @@ class Equalizer(Filter): The format for the levels is: List[Tuple[int, float]] """ - def __init__(self, *, tag: str, levels: list): + def __init__(self, *, tag: str = "equalizer", levels: list): super().__init__() self.eq = self._factory(levels) @@ -156,7 +171,6 @@ class Equalizer(Filter): ] return cls(tag="piano", levels=levels) - class Timescale(Filter): """Filter which changes the speed and pitch of a track. You can make some very nice effects with this filter, @@ -167,32 +181,24 @@ class Timescale(Filter): def __init__( self, *, - tag: str, + tag: str = "timescale", speed: float = 1.0, pitch: float = 1.0, rate: float = 1.0 ): super().__init__() - if speed < 0: - raise FilterInvalidArgument("Timescale speed must be more than 0.") - if pitch < 0: - raise FilterInvalidArgument("Timescale pitch must be more than 0.") - if rate < 0: - raise FilterInvalidArgument("Timescale rate must be more than 0.") - + self.scope = {"speed": [0, 5], "pitch": [0, 5], "rate": [0, 5]} self.speed = speed self.pitch = pitch - self.rate = rate - self.tag = tag + self.rate = rate - self.payload = { - "timescale": { - "speed": self.speed, - "pitch": self.pitch, - "rate": self.rate - } - } + for prop, (min_val, max_val) in self.scope.items(): + if not min_val <= getattr(self, prop) <= max_val: + raise FilterInvalidArgument(f"Timescale {prop} must be between {min_val} and {max_val}.") + + self.tag = tag + self.payload = {"timescale": {prop: getattr(self, prop) for prop in self.scope}} @classmethod def vaporwave(cls): @@ -215,8 +221,7 @@ class Timescale(Filter): return cls(tag="nightcore", speed=1.25, pitch=1.3) def __repr__(self): - return f"" - + return f"" class Karaoke(Filter): """Filter which filters the vocal track from any song and leaves the instrumental. @@ -226,7 +231,7 @@ class Karaoke(Filter): def __init__( self, *, - tag: str, + tag: str = "karaoke", level: float = 1.0, mono_level: float = 1.0, filter_band: float = 220.0, @@ -234,27 +239,21 @@ class Karaoke(Filter): ): super().__init__() + self.scope = {"level": [0, 5], "monoLevel": [0, 5], "filterBand": [0, 500], "filterWidth": [0, 300]} self.level = level - self.mono_level = mono_level - self.filter_band = filter_band - self.filter_width = filter_width - self.tag = tag + self.monoLevel = mono_level + self.filterBand = filter_band + self.filterWidth = filter_width - self.payload = { - "karaoke": { - "level": self.level, - "monoLevel": self.mono_level, - "filterBand": self.filter_band, - "filterWidth": self.filter_width - } - } + for prop, (min_val, max_val) in self.scope.items(): + if not min_val <= getattr(self, prop) <= max_val: + raise FilterInvalidArgument(f"Karaoke {prop} must be between {min_val} and {max_val}.") + + self.tag = tag + self.payload = {"karaoke": {prop: getattr(self, prop) for prop in self.scope}} def __repr__(self): - return ( - f"" - ) - + return (f" 1: - raise FilterInvalidArgument("Tremolo depth must be between 0 and 1.") - + self.scope = {"frequency": [0, 5], "depth": [0, 1]} self.frequency = frequency self.depth = depth - self.tag = tag - self.payload = { - "tremolo": { - "frequency": self.frequency, - "depth": self.depth - } - } + for prop, (min_val, max_val) in self.scope.items(): + if not min_val <= getattr(self, prop) <= max_val: + raise FilterInvalidArgument(f"Tremolo {prop} must be between {min_val} and {max_val}.") + + self.tag = tag + self.payload = {"tremolo": {prop: getattr(self, prop) for prop in self.scope}} def __repr__(self): - return f"" - + return f" 14: - raise FilterInvalidArgument("Vibrato frequency must be between 0 and 14.") - if depth < 0 or depth > 1: - raise FilterInvalidArgument("Vibrato depth must be between 0 and 1.") + self.scope = {"frequency": [0, 14], "depth": [0, 1]} self.frequency = frequency self.depth = depth - self.tag = tag - self.payload = { - "vibrato": { - "frequency": self.frequency, - "depth": self.depth - } - } + for prop, (min_val, max_val) in self.scope.items(): + if not min_val <= getattr(self, prop) <= max_val: + raise FilterInvalidArgument(f"Vibrato {prop} must be between {min_val} and {max_val}.") + + self.tag = tag + self.payload = {"vibrato": {prop: getattr(self, prop) for prop in self.scope}} def __repr__(self): - return f"" - + return f" str: - return f"" + return f" left_to_left > 1: - raise ValueError("'left_to_left' value must be more than or equal to 0 or less than or equal to 1.") - if 0 > right_to_right > 1: - raise ValueError("'right_to_right' value must be more than or equal to 0 or less than or equal to 1.") - if 0 > left_to_right > 1: - raise ValueError("'left_to_right' value must be more than or equal to 0 or less than or equal to 1.") - if 0 > right_to_left > 1: - raise ValueError("'right_to_left' value must be more than or equal to 0 or less than or equal to 1.") - - self.left_to_left = left_to_left - self.left_to_right = left_to_right - self.right_to_left = right_to_left - self.right_to_right = right_to_right - self.tag = tag - - self.payload = { - "channelMix": { - "leftToLeft": self.left_to_left, - "leftToRight": self.left_to_right, - "rightToLeft": self.right_to_left, - "rightToRight": self.right_to_right - } + self.scope = { + "leftToLeft": [0, 1], + "leftToRight": [0, 1], + "rightToLeft": [0, 1], + "rightToRight": [0, 1] } + self.leftToLeft = left_to_left + self.leftToRight = right_to_right + self.rightToLeft = left_to_right + self.rightToRight = right_to_left + + for prop, (min_val, max_val) in self.scope.items(): + if not min_val <= getattr(self, prop) <= max_val: + raise FilterInvalidArgument(f"ChannelMix {prop} must be between {min_val} and {max_val}.") + + self.tag = tag + self.payload = {"channelMix": {prop: getattr(self, prop) for prop in self.scope}} def __repr__(self) -> str: - return ( - f"" - ) + return (f" str: - return ( - f" " - f"cos_offset={self.cos_offset} cos_scale={self.cos_scale} tan_offset={self.tan_offset} " - f"tan_scale={self.tan_scale} offset={self.offset} scale={self.scale}" - ) - + return (f" str: - return f"" - - + return f"" \ No newline at end of file diff --git a/voicelink/player.py b/voicelink/player.py index d61f701..2bbbc73 100644 --- a/voicelink/player.py +++ b/voicelink/player.py @@ -684,37 +684,59 @@ class Player(VoiceProtocol): self._logger.debug(f"Player in {self.guild.name}({self.guild.id}) has been update the repeat mode to {mode}.") return mode - async def add_filter(self, filter: Filter, fast_apply=False) -> Filters: + async def add_filter(self, filter: Filter, requester: Member = None, fast_apply: bool = False) -> Filters: try: self._filters.add_filter(filter=filter) except FilterTagAlreadyInUse: raise FilterTagAlreadyInUse(self.get_msg("FilterTagAlreadyInUse")) + payload = self._filters.get_all_payloads() await self.send(method=RequestMethod.patch, data={"filters": payload}) if fast_apply: await self.seek(self.position) + if self.is_ipc_connected: + await self.send_ws({ + "op": "updateFilter", + "filter": {"tag": filter.tag, "scope": filter.scope, "payload": filter.payload}, + "type": "add" + }, requester) + self._logger.debug(f"Player in {self.guild.name}({self.guild.id}) has been applied a {filter.tag} filter.") return self._filters - async def remove_filter(self, filter_tag: str, fast_apply=False) -> Filters: + async def remove_filter(self, filter_tag: str, requester: Member = None, fast_apply: bool = False) -> Filters: self._filters.remove_filter(filter_tag=filter_tag) payload = self._filters.get_all_payloads() await self.send(method=RequestMethod.patch, data={"filters": payload}) if fast_apply: await self.seek(self.position) + if self.is_ipc_connected: + await self.send_ws({ + "op": "updateFilter", + "filter": {"tag": filter_tag}, + "type": "remove" + }, requester) + self._logger.debug(f"Player in {self.guild.name}({self.guild.id}) has been removed a {filter_tag} filter.") return self._filters - async def reset_filter(self, *, fast_apply=False) -> None: + async def reset_filter(self, *, requester: Member = None, fast_apply=False) -> None: if not self._filters: raise FilterInvalidArgument("You must have filters applied first in order to use this method.") + self._filters.reset_filters() await self.send(method=RequestMethod.patch, data={"filters": {}}) if fast_apply: await self.seek(self.position) + if self.is_ipc_connected: + await self.send_ws({ + "op": "updateFilter", + "type": "reset" + }, requester) + self._logger.debug(f"Player in {self.guild.name}({self.guild.id}) has been removed all filters.") async def change_node(self, identifier: str = None) -> None: