Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/filter values #326

Merged
merged 2 commits into from
Sep 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "wavelink"
version = "3.4.1"
version = "3.4.2"
authors = [
{ name="PythonistaGuild, EvieePy", email="[email protected]" },
]
Expand Down
2 changes: 1 addition & 1 deletion wavelink/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
__author__ = "PythonistaGuild, EvieePy"
__license__ = "MIT"
__copyright__ = "Copyright 2019-Present (c) PythonistaGuild, EvieePy"
__version__ = "3.4.1"
__version__ = "3.4.2"


from .enums import *
Expand Down
87 changes: 56 additions & 31 deletions wavelink/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,27 @@

from __future__ import annotations

from typing import TYPE_CHECKING, Any, TypedDict
from typing import TYPE_CHECKING, Any, Generic, TypedDict, TypeVar

from .types.filters import (
ChannelMix as ChannelMixPayload,
Distortion as DistortionPayload,
Equalizer as EqualizerPayload,
FilterPayload,
Karaoke as KaraokePayload,
LowPass as LowPassPayload,
Rotation as RotationPayload,
Timescale as TimescalePayload,
Tremolo as TremoloPayload,
Vibrato as VibratoPayload,
)


if TYPE_CHECKING:
from typing_extensions import Self, Unpack

from .types.filters import (
ChannelMix as ChannelMixPayload,
Distortion as DistortionPayload,
Equalizer as EqualizerPayload,
FilterPayload,
Karaoke as KaraokePayload,
LowPass as LowPassPayload,
Rotation as RotationPayload,
Timescale as TimescalePayload,
Tremolo as TremoloPayload,
Vibrato as VibratoPayload,
)

FT = TypeVar("FT")


__all__ = (
Expand Down Expand Up @@ -108,6 +111,19 @@ class ChannelMixOptions(TypedDict):
right_to_right: float | None


class _BaseFilter(Generic[FT]):
_payload: FT

def __init__(self, payload: FT) -> None:
self._payload = payload
self._remove_none()

def _remove_none(self) -> None:
# Lavalink doesn't allow nullable types in any filters, but they are still not required...
# Passing None makes it easier for the user to remove a field...
self._payload = {k: v for k, v in self._payload.items() if v is not None} # type: ignore


class Equalizer:
"""Equalizer Filter Class.

Expand Down Expand Up @@ -182,14 +198,14 @@ def __repr__(self) -> str:
return f"<Equalizer: {self._payload}>"


class Karaoke:
class Karaoke(_BaseFilter[KaraokePayload]):
"""Karaoke Filter class.

Uses equalization to eliminate part of a band, usually targeting vocals.
"""

def __init__(self, payload: KaraokePayload) -> None:
self._payload = payload
super().__init__(payload=payload)

def set(self, **options: Unpack[KaraokeOptions]) -> Self:
"""Set the properties of the this filter.
Expand All @@ -214,6 +230,7 @@ def set(self, **options: Unpack[KaraokeOptions]) -> Self:
"filterBand": options.get("filter_band", self._payload.get("filterBand")),
"filterWidth": options.get("filter_width", self._payload.get("filterWidth")),
}
self._remove_none()
return self

def reset(self) -> Self:
Expand All @@ -236,14 +253,14 @@ def __repr__(self) -> str:
return f"<Karaoke: {self._payload}>"


class Timescale:
class Timescale(_BaseFilter[TimescalePayload]):
"""Timescale Filter class.

Changes the speed, pitch, and rate.
"""

def __init__(self, payload: TimescalePayload) -> None:
self._payload = payload
super().__init__(payload=payload)

def set(self, **options: Unpack[TimescalePayload]) -> Self:
"""Set the properties of the this filter.
Expand All @@ -261,6 +278,7 @@ def set(self, **options: Unpack[TimescalePayload]) -> Self:
The rate.
"""
self._payload.update(options)
self._remove_none()
return self

def reset(self) -> Self:
Expand All @@ -283,15 +301,15 @@ def __repr__(self) -> str:
return f"<Timescale: {self._payload}>"


class Tremolo:
class Tremolo(_BaseFilter[TremoloPayload]):
"""The Tremolo Filter class.

Uses amplification to create a shuddering effect, where the volume quickly oscillates.
Demo: https://en.wikipedia.org/wiki/File:Fuse_Electronics_Tremolo_MK-III_Quick_Demo.ogv
"""

def __init__(self, payload: TremoloPayload) -> None:
self._payload = payload
super().__init__(payload=payload)

def set(self, **options: Unpack[TremoloPayload]) -> Self:
"""Set the properties of the this filter.
Expand All @@ -307,6 +325,7 @@ def set(self, **options: Unpack[TremoloPayload]) -> Self:
The tremolo depth.
"""
self._payload.update(options)
self._remove_none()
return self

def reset(self) -> Self:
Expand All @@ -329,14 +348,14 @@ def __repr__(self) -> str:
return f"<Tremolo: {self._payload}>"


class Vibrato:
class Vibrato(_BaseFilter[VibratoPayload]):
"""The Vibrato Filter class.

Similar to tremolo. While tremolo oscillates the volume, vibrato oscillates the pitch.
"""

def __init__(self, payload: VibratoPayload) -> None:
self._payload = payload
super().__init__(payload=payload)

def set(self, **options: Unpack[VibratoPayload]) -> Self:
"""Set the properties of the this filter.
Expand All @@ -352,6 +371,7 @@ def set(self, **options: Unpack[VibratoPayload]) -> Self:
The vibrato depth.
"""
self._payload.update(options)
self._remove_none()
return self

def reset(self) -> Self:
Expand All @@ -374,15 +394,15 @@ def __repr__(self) -> str:
return f"<Vibrato: {self._payload}>"


class Rotation:
class Rotation(_BaseFilter[RotationPayload]):
"""The Rotation Filter class.

Rotates the sound around the stereo channels/user headphones (aka Audio Panning).
It can produce an effect similar to https://youtu.be/QB9EB8mTKcc (without the reverb).
"""

def __init__(self, payload: RotationPayload) -> None:
self._payload = payload
super().__init__(payload=payload)

def set(self, **options: Unpack[RotationOptions]) -> Self:
"""Set the properties of the this filter.
Expand All @@ -396,6 +416,7 @@ def set(self, **options: Unpack[RotationOptions]) -> Self:
The frequency of the audio rotating around the listener in Hz. ``0.2`` is similar to the example video.
"""
self._payload: RotationPayload = {"rotationHz": options.get("rotation_hz", self._payload.get("rotationHz"))}
self._remove_none()
return self

def reset(self) -> Self:
Expand All @@ -418,14 +439,14 @@ def __repr__(self) -> str:
return f"<Rotation: {self._payload}>"


class Distortion:
class Distortion(_BaseFilter[DistortionPayload]):
"""The Distortion Filter class.

According to Lavalink "It can generate some pretty unique audio effects."
"""

def __init__(self, payload: DistortionPayload) -> None:
self._payload = payload
super().__init__(payload=payload)

def set(self, **options: Unpack[DistortionOptions]) -> Self:
"""Set the properties of the this filter.
Expand Down Expand Up @@ -462,6 +483,7 @@ def set(self, **options: Unpack[DistortionOptions]) -> Self:
"offset": options.get("offset", self._payload.get("offset")),
"scale": options.get("scale", self._payload.get("scale")),
}
self._remove_none()
return self

def reset(self) -> Self:
Expand All @@ -484,7 +506,7 @@ def __repr__(self) -> str:
return f"<Distortion: {self._payload}>"


class ChannelMix:
class ChannelMix(_BaseFilter[ChannelMixPayload]):
"""The ChannelMix Filter class.

Mixes both channels (left and right), with a configurable factor on how much each channel affects the other.
Expand All @@ -494,7 +516,7 @@ class ChannelMix:
"""

def __init__(self, payload: ChannelMixPayload) -> None:
self._payload = payload
super().__init__(payload=payload)

def set(self, **options: Unpack[ChannelMixOptions]) -> Self:
"""Set the properties of the this filter.
Expand All @@ -519,6 +541,7 @@ def set(self, **options: Unpack[ChannelMixOptions]) -> Self:
"rightToLeft": options.get("right_to_left", self._payload.get("rightToLeft")),
"rightToRight": options.get("right_to_right", self._payload.get("rightToRight")),
}
self._remove_none()
return self

def reset(self) -> Self:
Expand All @@ -541,15 +564,15 @@ def __repr__(self) -> str:
return f"<ChannelMix: {self._payload}>"


class LowPass:
class LowPass(_BaseFilter[LowPassPayload]):
"""The LowPass Filter class.

Higher frequencies get suppressed, while lower frequencies pass through this filter, thus the name low pass.
Any smoothing values equal to or less than ``1.0`` will disable the filter.
"""

def __init__(self, payload: LowPassPayload) -> None:
self._payload = payload
super().__init__(payload=payload)

def set(self, **options: Unpack[LowPassPayload]) -> Self:
"""Set the properties of the this filter.
Expand All @@ -563,6 +586,7 @@ def set(self, **options: Unpack[LowPassPayload]) -> Self:
The smoothing factor.
"""
self._payload.update(options)
self._remove_none()
return self

def reset(self) -> Self:
Expand All @@ -585,7 +609,7 @@ def __repr__(self) -> str:
return f"<LowPass: {self._payload}>"


class PluginFilters:
class PluginFilters(_BaseFilter[dict[str, Any]]):
"""The PluginFilters class.

This class handles setting filters on plugins that support setting filter values.
Expand All @@ -604,7 +628,7 @@ class PluginFilters:
"""

def __init__(self, payload: dict[str, Any]) -> None:
self._payload = payload
super().__init__(payload=payload)

def set(self, **options: dict[str, Any]) -> Self:
"""Set the properties of this filter.
Expand All @@ -625,6 +649,7 @@ def set(self, **options: dict[str, Any]) -> Self:
plugin_filters.set(**{"pluginName": {"filterKey": "filterValue", ...}})
"""
self._payload.update(options)
self._remove_none()
return self

def reset(self) -> Self:
Expand Down
Loading