Rewrote filters

This commit is contained in:
Choco
2024-07-06 15:27:19 +08:00
parent ae1ff38761
commit c7ec2b5159
4 changed files with 157 additions and 147 deletions

View File

@@ -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"))

View File

@@ -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:

View File

@@ -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"<Voicelink.TimescaleFilter tag={self.tag} speed={self.speed} pitch={self.pitch} rate={self.rate}>"
return f"<Voicelink.TimescaleFilter tag={self.tag} payload={self.payload}>"
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"<Voicelink.KaraokeFilter tag={self.tag} level={self.level} mono_level={self.mono_level} "
f"filter_band={self.filter_band} filter_width={self.filter_width}>"
)
return (f"<Voicelink.KaraokeFilter tag={self.tag} payload={self.payload}")
class Tremolo(Filter):
"""Filter which produces a wavering tone in the music,
@@ -264,31 +263,25 @@ class Tremolo(Filter):
def __init__(
self,
*,
tag: str,
tag: str = "tremolo",
frequency: float = 2.0,
depth: float = 0.5
):
super().__init__()
if frequency < 0:
raise FilterInvalidArgument("Tremolo frequency must be more than 0.")
if depth < 0 or depth > 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"<Voicelink.TremoloFilter tag={self.tag} frequency={self.frequency} depth={self.depth}>"
return f"<Voicelink.TremoloFilter tag={self.tag} payload={self.payload}"
class Vibrato(Filter):
"""Filter which produces a wavering tone in the music, similar to the Tremolo filter,
@@ -298,50 +291,50 @@ class Vibrato(Filter):
def __init__(
self,
*,
tag: str,
tag: str = "vibrato",
frequency: float = 2.0,
depth: float = 0.5
):
super().__init__()
if frequency < 0 or frequency > 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"<Voicelink.VibratoFilter tag={self.tag} frequency={self.frequency} depth={self.depth}>"
return f"<Voicelink.VibratoFilter tag={self.tag} payload={self.payload}"
class Rotation(Filter):
"""Filter which produces a stereo-like panning effect, which sounds like
the audio is being rotated around the listener's head
"""
def __init__(self, *, tag: str, rotation_hertz: float = 5):
def __init__(self, *, tag: str = "rotation", rotation_hertz: float = 5):
super().__init__()
self.rotation_hertz = rotation_hertz
self.scope = {"rotationHz": [0, 10]}
self.rotationHz = rotation_hertz
for prop, (min_val, max_val) in self.scope.items():
if not min_val <= getattr(self, prop) <= max_val:
raise FilterInvalidArgument(f"Rotation {prop} must be between {min_val} and {max_val}.")
self.tag = tag
self.payload = {"rotation": {"rotationHz": self.rotation_hertz}}
self.payload = {"rotation": {prop: getattr(self, prop) for prop in self.scope}}
@classmethod
def nightD(cls):
return cls(tag="8d", rotation_hertz=0.2)
def __repr__(self) -> str:
return f"<Voicelink.RotationFilter tag={self.tag} rotation_hertz={self.rotation_hertz}>"
return f"<Voicelink.RotationFilter tag={self.tag} payload={self.payload}"
class ChannelMix(Filter):
@@ -352,7 +345,7 @@ class ChannelMix(Filter):
def __init__(
self,
*,
tag: str,
tag: str = "channelMix",
left_to_left: float = 1,
right_to_right: float = 1,
left_to_right: float = 0,
@@ -360,35 +353,26 @@ class ChannelMix(Filter):
):
super().__init__()
if 0 > 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"<Voicelink.ChannelMix tag={self.tag} left_to_left={self.left_to_left} left_to_right={self.left_to_right} "
f"right_to_left={self.right_to_left} right_to_right={self.right_to_right}>"
)
return (f"<Voicelink.ChannelMix tag={self.tag} payload={self.payload}")
class Distortion(Filter):
"""Filter which generates a distortion effect. Useful for certain filter implementations where
@@ -398,7 +382,7 @@ class Distortion(Filter):
def __init__(
self,
*,
tag: str,
tag: str = "distortion",
sin_offset: float = 0,
sin_scale: float = 1,
cos_offset: float = 0,
@@ -410,50 +394,52 @@ class Distortion(Filter):
):
super().__init__()
self.sin_offset = sin_offset
self.sin_scale = sin_scale
self.cos_offset = cos_offset
self.cos_scale = cos_scale
self.tan_offset = tan_offset
self.tan_scale = tan_scale
self.scope = {
"sinOffset": [0, 1],
"sinScale": [0, 1],
"cosOffset": [0, 1],
"cosScale": [0, 1],
"tanOffset": [0, 1],
"tanScale": [0, 1],
"offset": [0, 1],
"scale": [0, 1],
}
self.sinOffset = sin_offset
self.sinScale = sin_scale
self.cosOffset = cos_offset
self.cosScale = cos_scale
self.tanOffset = tan_offset
self.tanScale = tan_scale
self.offset = offset
self.scale = scale
self.tag = tag
for prop, (min_val, max_val) in self.scope.items():
if not min_val <= getattr(self, prop) <= max_val:
raise FilterInvalidArgument(f"Distortion {prop} must be between {min_val} and {max_val}.")
self.payload = {
"distortion": {
"sinOffset": self.sin_offset,
"sinScale": self.sin_scale,
"cosOffset": self.cos_offset,
"cosScale": self.cos_scale,
"tanOffset": self.tan_offset,
"tanScale": self.tan_scale,
"offset": self.offset,
"scale": self.scale
}
}
self.tag = tag
self.payload = {"distortion": {prop: getattr(self, prop) for prop in self.scope}}
def __repr__(self) -> str:
return (
f"<Voicelink.Distortion tag={self.tag} sin_offset={self.sin_offset} sin_scale={self.sin_scale}> "
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"<Voicelink.Distortion tag={self.tag} payload={self.payload}")
class LowPass(Filter):
"""Filter which supresses higher frequencies and allows lower frequencies to pass.
You can also do this with the Equalizer filter, but this is an easier way to do it.
"""
def __init__(self, *, tag: str, smoothing: float = 20):
def __init__(self, *, tag: str = "lowpass", smoothing: float = 20):
super().__init__()
self.scope = {"smoothing": [0, 100]}
self.smoothing = smoothing
for prop, (min_val, max_val) in self.scope.items():
if not min_val <= getattr(self, prop) <= max_val:
raise FilterInvalidArgument(f"LowPass {prop} must be between {min_val} and {max_val}.")
self.tag = tag
self.payload = {"lowPass": {"smoothing": self.smoothing}}
self.payload = {"lowPass": {prop: getattr(self, prop) for prop in self.scope}}
def __repr__(self) -> str:
return f"<Voicelink.LowPass tag={self.tag} smoothing={self.smoothing}>"
return f"<Voicelink.LowPass tag={self.tag} payload={self.payload}>"

View File

@@ -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: