A robust and powerful, fully asynchronous Lavalink wrapper built for discord.py in Python.
Comprehensive audio filter system including equalizer, distortion, karaoke, timescale, and plugin filters for advanced audio processing and effects. The filter system allows real-time audio manipulation to enhance playback quality and create creative audio effects.
The main Filters class that manages all individual filter types and applies them to players.
class Filters:
def __init__(self, *, data: dict | None = None):
"""
Initialize a new Filters container.
Parameters:
- data: Optional filter data to initialize from
"""
@property
def volume(self) -> float | None:
"""
Player volume multiplier (0.0-5.0, where 1.0 is 100%).
Values >1.0 may cause clipping.
"""
@volume.setter
def volume(self, value: float) -> None:
"""Set the volume multiplier."""
@property
def equalizer(self) -> Equalizer:
"""15-band equalizer filter."""
@property
def karaoke(self) -> Karaoke:
"""Karaoke filter for vocal elimination."""
@property
def timescale(self) -> Timescale:
"""Timescale filter for speed/pitch/rate adjustment."""
@property
def tremolo(self) -> Tremolo:
"""Tremolo filter for volume oscillation."""
@property
def vibrato(self) -> Vibrato:
"""Vibrato filter for pitch oscillation."""
@property
def rotation(self) -> Rotation:
"""Rotation filter for audio panning effects."""
@property
def distortion(self) -> Distortion:
"""Distortion filter for audio distortion effects."""
@property
def channel_mix(self) -> ChannelMix:
"""Channel mix filter for stereo channel manipulation."""
@property
def low_pass(self) -> LowPass:
"""Low pass filter for high frequency suppression."""
@property
def plugin_filters(self) -> PluginFilters:
"""Custom plugin filters for Lavalink plugins."""
def set_filters(self, **filters) -> None:
"""
Set multiple filters at once.
Parameters:
- volume: float - Volume multiplier
- equalizer: Equalizer - Equalizer filter
- karaoke: Karaoke - Karaoke filter
- timescale: Timescale - Timescale filter
- tremolo: Tremolo - Tremolo filter
- vibrato: Vibrato - Vibrato filter
- rotation: Rotation - Rotation filter
- distortion: Distortion - Distortion filter
- channel_mix: ChannelMix - Channel mix filter
- low_pass: LowPass - Low pass filter
- plugin_filters: PluginFilters - Plugin filters
- reset: bool - Whether to reset unspecified filters
"""
def reset(self) -> None:
"""Reset all filters to their default states."""
@classmethod
def from_filters(cls, **filters) -> Self:
"""
Create a new Filters instance with specified filters.
Parameters:
- **filters: Filter parameters (same as set_filters)
Returns:
Filters: New Filters instance
"""
def __call__(self) -> dict:
"""
Get the raw filter payload for Lavalink.
Returns:
dict: Filter payload data
"""15-band equalizer for frequency-specific volume adjustment.
class Equalizer:
def __init__(self, payload: list[dict] | None = None):
"""
Initialize equalizer with optional band data.
Parameters:
- payload: List of band configurations
"""
def set(self, *, bands: list[dict] | None = None) -> Self:
"""
Set equalizer bands.
Parameters:
- bands: List of dicts with 'band' (0-14) and 'gain' (-0.25 to 1.0) keys
Returns:
Self: This equalizer instance for chaining
Note:
- Band 0-14 represent different frequency ranges
- Gain -0.25 = muted, 0.0 = unchanged, 1.0 = doubled
- This method resets ALL bands, use payload property for selective changes
"""
def reset(self) -> Self:
"""
Reset all bands to 0.0 gain.
Returns:
Self: This equalizer instance for chaining
"""
@property
def payload(self) -> dict[int, dict]:
"""
Raw equalizer band data (copy).
Returns:
dict: Band data keyed by band number (0-14)
"""Vocal elimination filter using equalization techniques.
class Karaoke:
def __init__(self, payload: dict):
"""
Initialize karaoke filter.
Parameters:
- payload: Karaoke configuration data
"""
def set(
self,
*,
level: float | None = None,
mono_level: float | None = None,
filter_band: float | None = None,
filter_width: float | None = None
) -> Self:
"""
Configure karaoke filter parameters.
Parameters:
- level: Effect level (0.0-1.0)
- mono_level: Mono level (0.0-1.0)
- filter_band: Filter band frequency in Hz
- filter_width: Filter width
Returns:
Self: This karaoke instance for chaining
"""
def reset(self) -> Self:
"""
Reset karaoke filter to defaults.
Returns:
Self: This karaoke instance for chaining
"""
@property
def payload(self) -> dict:
"""Raw karaoke filter data (copy)."""Speed, pitch, and rate modification filter.
class Timescale:
def __init__(self, payload: dict):
"""
Initialize timescale filter.
Parameters:
- payload: Timescale configuration data
"""
def set(
self,
*,
speed: float | None = None,
pitch: float | None = None,
rate: float | None = None
) -> Self:
"""
Configure timescale parameters.
Parameters:
- speed: Playback speed multiplier
- pitch: Pitch adjustment multiplier
- rate: Rate adjustment multiplier
Returns:
Self: This timescale instance for chaining
"""
def reset(self) -> Self:
"""
Reset timescale filter to defaults.
Returns:
Self: This timescale instance for chaining
"""
@property
def payload(self) -> dict:
"""Raw timescale filter data (copy)."""Volume oscillation effect filter.
class Tremolo:
def __init__(self, payload: dict):
"""
Initialize tremolo filter.
Parameters:
- payload: Tremolo configuration data
"""
def set(
self,
*,
frequency: float | None = None,
depth: float | None = None
) -> Self:
"""
Configure tremolo parameters.
Parameters:
- frequency: Oscillation frequency in Hz
- depth: Effect depth (0.0-1.0)
Returns:
Self: This tremolo instance for chaining
"""
def reset(self) -> Self:
"""
Reset tremolo filter to defaults.
Returns:
Self: This tremolo instance for chaining
"""
@property
def payload(self) -> dict:
"""Raw tremolo filter data (copy)."""Pitch oscillation effect filter.
class Vibrato:
def __init__(self, payload: dict):
"""
Initialize vibrato filter.
Parameters:
- payload: Vibrato configuration data
"""
def set(
self,
*,
frequency: float | None = None,
depth: float | None = None
) -> Self:
"""
Configure vibrato parameters.
Parameters:
- frequency: Oscillation frequency in Hz
- depth: Effect depth (0.0-1.0)
Returns:
Self: This vibrato instance for chaining
"""
def reset(self) -> Self:
"""
Reset vibrato filter to defaults.
Returns:
Self: This vibrato instance for chaining
"""
@property
def payload(self) -> dict:
"""Raw vibrato filter data (copy)."""Audio panning and stereo rotation effects.
class Rotation:
def __init__(self, payload: dict):
"""
Initialize rotation filter.
Parameters:
- payload: Rotation configuration data
"""
def set(self, *, rotation_hz: float | None = None) -> Self:
"""
Configure rotation frequency.
Parameters:
- rotation_hz: Rotation frequency in Hz (0.2 is similar to example effects)
Returns:
Self: This rotation instance for chaining
"""
def reset(self) -> Self:
"""
Reset rotation filter to defaults.
Returns:
Self: This rotation instance for chaining
"""
@property
def payload(self) -> dict:
"""Raw rotation filter data (copy)."""Audio distortion effects filter.
class Distortion:
def __init__(self, payload: dict):
"""
Initialize distortion filter.
Parameters:
- payload: Distortion configuration data
"""
def set(
self,
*,
sin_offset: float | None = None,
sin_scale: float | None = None,
cos_offset: float | None = None,
cos_scale: float | None = None,
tan_offset: float | None = None,
tan_scale: float | None = None,
offset: float | None = None,
scale: float | None = None
) -> Self:
"""
Configure distortion parameters.
Parameters:
- sin_offset: Sine offset value
- sin_scale: Sine scale multiplier
- cos_offset: Cosine offset value
- cos_scale: Cosine scale multiplier
- tan_offset: Tangent offset value
- tan_scale: Tangent scale multiplier
- offset: General offset value
- scale: General scale multiplier
Returns:
Self: This distortion instance for chaining
"""
def reset(self) -> Self:
"""
Reset distortion filter to defaults.
Returns:
Self: This distortion instance for chaining
"""
@property
def payload(self) -> dict:
"""Raw distortion filter data (copy)."""Left/right channel mixing and manipulation.
class ChannelMix:
def __init__(self, payload: dict):
"""
Initialize channel mix filter.
Parameters:
- payload: Channel mix configuration data
"""
def set(
self,
*,
left_to_left: float | None = None,
left_to_right: float | None = None,
right_to_left: float | None = None,
right_to_right: float | None = None
) -> Self:
"""
Configure channel mixing factors.
Parameters:
- left_to_left: Left channel to left output (0.0-1.0)
- left_to_right: Left channel to right output (0.0-1.0)
- right_to_left: Right channel to left output (0.0-1.0)
- right_to_right: Right channel to right output (0.0-1.0)
Returns:
Self: This channel mix instance for chaining
Note:
- Default values keep channels independent
- Setting all to 0.5 creates mono output
"""
def reset(self) -> Self:
"""
Reset channel mix filter to defaults.
Returns:
Self: This channel mix instance for chaining
"""
@property
def payload(self) -> dict:
"""Raw channel mix filter data (copy)."""High frequency suppression filter.
class LowPass:
def __init__(self, payload: dict):
"""
Initialize low pass filter.
Parameters:
- payload: Low pass configuration data
"""
def set(self, *, smoothing: float | None = None) -> Self:
"""
Configure low pass smoothing.
Parameters:
- smoothing: Smoothing factor (>1.0 to enable, <=1.0 disables)
Returns:
Self: This low pass instance for chaining
"""
def reset(self) -> Self:
"""
Reset low pass filter to defaults.
Returns:
Self: This low pass instance for chaining
"""
@property
def payload(self) -> dict:
"""Raw low pass filter data (copy)."""Custom filters for Lavalink plugins.
class PluginFilters:
def __init__(self, payload: dict[str, Any]):
"""
Initialize plugin filters.
Parameters:
- payload: Plugin filter configuration data
"""
def set(self, **options: dict[str, Any]) -> Self:
"""
Set plugin-specific filter values.
Parameters:
- **options: Plugin filter options in format:
pluginName={"filterKey": "filterValue", ...}
Returns:
Self: This plugin filters instance for chaining
Example:
plugin_filters.set(pluginName={"filterKey": "filterValue"})
"""
def reset(self) -> Self:
"""
Reset all plugin filters to defaults.
Returns:
Self: This plugin filters instance for chaining
"""
@property
def payload(self) -> dict[str, Any]:
"""Raw plugin filter data (copy)."""import wavelink
@bot.command()
async def bass_boost(ctx, level: int = 3):
"""Apply bass boost using equalizer."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
# Get current filters
filters = player.filters
# Configure bass boost (boost low frequencies)
bass_bands = [
{"band": 0, "gain": level * 0.05}, # 25Hz
{"band": 1, "gain": level * 0.04}, # 40Hz
{"band": 2, "gain": level * 0.03}, # 63Hz
{"band": 3, "gain": level * 0.02}, # 100Hz
{"band": 4, "gain": level * 0.01}, # 160Hz
]
filters.equalizer.set(bands=bass_bands)
await player.set_filters(filters)
await ctx.send(f"Applied bass boost level {level}")
@bot.command()
async def nightcore(ctx):
"""Apply nightcore effect (higher pitch and speed)."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
# Get current filters
filters = player.filters
# Configure nightcore timescale
filters.timescale.set(speed=1.3, pitch=1.3, rate=1.0)
await player.set_filters(filters)
await ctx.send("Applied nightcore effect!")
@bot.command()
async def vaporwave(ctx):
"""Apply vaporwave effect (lower pitch and speed)."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
filters = player.filters
# Configure vaporwave timescale
filters.timescale.set(speed=0.8, pitch=0.8, rate=1.0)
await player.set_filters(filters)
await ctx.send("Applied vaporwave effect!")@bot.command()
async def party_mode(ctx):
"""Apply multiple filters for party effect."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
filters = player.filters
# Bass boost with equalizer
bass_bands = [
{"band": 0, "gain": 0.15},
{"band": 1, "gain": 0.12},
{"band": 2, "gain": 0.09},
{"band": 3, "gain": 0.06},
]
filters.equalizer.set(bands=bass_bands)
# Add tremolo for volume oscillation
filters.tremolo.set(frequency=2.0, depth=0.3)
# Add rotation for stereo effects
filters.rotation.set(rotation_hz=0.1)
# Slight volume boost
filters.volume = 1.2
await player.set_filters(filters)
await ctx.send("🎉 Party mode activated!")
@bot.command()
async def robot_voice(ctx):
"""Apply robot voice effect."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
filters = player.filters
# Distortion for robotic sound
filters.distortion.set(
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
)
# Channel mix for stereo effect
filters.channel_mix.set(
left_to_left=0.8,
left_to_right=0.2,
right_to_left=0.2,
right_to_right=0.8
)
await player.set_filters(filters)
await ctx.send("🤖 Robot voice effect applied!")@bot.command()
async def filters(ctx):
"""Show current filter status."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
filters = player.filters
embed = discord.Embed(title="Current Filters", color=discord.Color.blue())
# Volume
if filters.volume is not None:
embed.add_field(name="Volume", value=f"{filters.volume:.2f}x", inline=True)
# Check each filter type
filter_info = []
# Equalizer
eq_active = any(band["gain"] != 0.0 for band in filters.equalizer.payload.values())
if eq_active:
filter_info.append("Equalizer")
# Timescale
ts_payload = filters.timescale.payload
if ts_payload.get("speed") or ts_payload.get("pitch") or ts_payload.get("rate"):
speed = ts_payload.get("speed", 1.0)
pitch = ts_payload.get("pitch", 1.0)
filter_info.append(f"Timescale (Speed: {speed:.2f}, Pitch: {pitch:.2f})")
# Other filters
if filters.karaoke.payload:
filter_info.append("Karaoke")
if filters.tremolo.payload:
filter_info.append("Tremolo")
if filters.vibrato.payload:
filter_info.append("Vibrato")
if filters.rotation.payload:
filter_info.append("Rotation")
if filters.distortion.payload:
filter_info.append("Distortion")
if filters.channel_mix.payload:
filter_info.append("Channel Mix")
if filters.low_pass.payload:
filter_info.append("Low Pass")
if filters.plugin_filters.payload:
filter_info.append("Plugin Filters")
if filter_info:
embed.add_field(
name="Active Filters",
value="\n".join(filter_info),
inline=False
)
else:
embed.add_field(name="Status", value="No filters active", inline=False)
await ctx.send(embed=embed)
@bot.command()
async def reset_filters(ctx):
"""Reset all filters to default."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
# Reset all filters
await player.set_filters() # Passing None resets all filters
await ctx.send("✅ All filters reset to default!")
@bot.command()
async def save_filters(ctx, name: str):
"""Save current filter preset."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
# In a real bot, you'd save this to a database
# This is a simple example using a dict
if not hasattr(bot, 'filter_presets'):
bot.filter_presets = {}
# Get current filter payload
filter_data = player.filters()
bot.filter_presets[name.lower()] = filter_data
await ctx.send(f"💾 Saved current filters as preset '{name}'")
@bot.command()
async def load_filters(ctx, name: str):
"""Load a saved filter preset."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
if not hasattr(bot, 'filter_presets'):
return await ctx.send("No filter presets saved!")
preset_name = name.lower()
if preset_name not in bot.filter_presets:
available = ", ".join(bot.filter_presets.keys())
return await ctx.send(f"Preset '{name}' not found! Available: {available}")
# Create filters from saved data
filter_data = bot.filter_presets[preset_name]
filters = wavelink.Filters(data=filter_data)
await player.set_filters(filters)
await ctx.send(f"📂 Loaded filter preset '{name}'")# Predefined EQ presets
EQ_PRESETS = {
'flat': [], # No changes
'bass_boost': [
{"band": 0, "gain": 0.2},
{"band": 1, "gain": 0.15},
{"band": 2, "gain": 0.1},
{"band": 3, "gain": 0.05},
],
'treble_boost': [
{"band": 11, "gain": 0.1},
{"band": 12, "gain": 0.15},
{"band": 13, "gain": 0.2},
{"band": 14, "gain": 0.25},
],
'vocal_boost': [
{"band": 6, "gain": 0.1},
{"band": 7, "gain": 0.15},
{"band": 8, "gain": 0.2},
{"band": 9, "gain": 0.15},
{"band": 10, "gain": 0.1},
],
'pop': [
{"band": 0, "gain": 0.1},
{"band": 1, "gain": 0.05},
{"band": 7, "gain": 0.1},
{"band": 8, "gain": 0.15},
{"band": 9, "gain": 0.1},
{"band": 13, "gain": 0.1},
{"band": 14, "gain": 0.15},
]
}
@bot.command()
async def eq_preset(ctx, preset: str = None):
"""Apply or list equalizer presets."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
if preset is None:
available = ", ".join(EQ_PRESETS.keys())
return await ctx.send(f"Available EQ presets: {available}")
preset_name = preset.lower()
if preset_name not in EQ_PRESETS:
available = ", ".join(EQ_PRESETS.keys())
return await ctx.send(f"Unknown preset! Available: {available}")
filters = player.filters
filters.equalizer.set(bands=EQ_PRESETS[preset_name])
await player.set_filters(filters)
await ctx.send(f"🎵 Applied '{preset}' EQ preset")
@bot.command()
async def custom_eq(ctx, *band_gains):
"""Set custom equalizer bands. Usage: !custom_eq 0.1 0.2 0.0 ..."""
player = ctx.voice_client
if not player:
return await ctx.send("Not connected to a voice channel!")
if len(band_gains) > 15:
return await ctx.send("Maximum 15 bands (0-14)!")
try:
# Parse gain values
gains = [float(gain) for gain in band_gains]
# Validate gain range
for gain in gains:
if not -0.25 <= gain <= 1.0:
return await ctx.send("Gain values must be between -0.25 and 1.0!")
# Create band configuration
bands = [{"band": i, "gain": gain} for i, gain in enumerate(gains)]
filters = player.filters
filters.equalizer.set(bands=bands)
await player.set_filters(filters)
await ctx.send(f"🎛️ Applied custom EQ with {len(bands)} bands")
except ValueError:
await ctx.send("Invalid gain values! Use numbers between -0.25 and 1.0")Install with Tessl CLI
npx tessl i tessl/pypi-wavelink