#
Audio Filters
Comprehensive guide to Sonora's audio filtering system.
#
Overview
Sonora provides real-time audio filtering capabilities to enhance or modify audio playback. Filters can be applied individually or stacked for complex effects.
#
Built-in Filters
#
BassBoost
Enhances low-frequency audio for a richer bass experience.
# Apply bass boost
await player.set_filter("bassboost", gain=0.5, frequency=100)
# Parameters:
# - gain: Boost amount (0.0 to 1.0, default: 0.2)
# - frequency: Cutoff frequency in Hz (default: 100)
#
Nightcore
Increases playback speed and pitch for a high-energy effect.
# Apply nightcore effect
await player.set_filter("nightcore", speed=1.2, pitch=1.1)
# Parameters:
# - speed: Speed multiplier (0.5 to 2.0, default: 1.2)
# - pitch: Pitch multiplier (0.5 to 2.0, default: 1.1)
#
Reverb
Adds echo and ambiance effects.
# Apply reverb
await player.set_filter("reverb", room_size=0.8, damping=0.5, wet=0.3)
# Parameters:
# - room_size: Room size (0.0 to 1.0, default: 0.5)
# - damping: High frequency damping (0.0 to 1.0, default: 0.5)
# - wet: Wet signal mix (0.0 to 1.0, default: 0.33)
#
Equalizer
15-band equalizer for precise frequency control.
# Apply equalizer preset
await player.set_filter("equalizer", preset="rock")
# Available presets: rock, pop, edm, classical, jazz, hiphop, metal
# Custom equalizer
await player.set_filter("equalizer", bands=[
{"band": 0, "gain": 0.5}, # 25 Hz
{"band": 1, "gain": 0.3}, # 40 Hz
{"band": 2, "gain": 0.1}, # 63 Hz
{"band": 3, "gain": 0.0}, # 100 Hz
{"band": 4, "gain": -0.2}, # 160 Hz
{"band": 5, "gain": -0.3}, # 250 Hz
{"band": 6, "gain": 0.0}, # 400 Hz
{"band": 7, "gain": 0.2}, # 630 Hz
{"band": 8, "gain": 0.4}, # 1000 Hz
{"band": 9, "gain": 0.3}, # 1600 Hz
{"band": 10, "gain": 0.2}, # 2500 Hz
{"band": 11, "gain": 0.1}, # 4000 Hz
{"band": 12, "gain": 0.0}, # 6300 Hz
{"band": 13, "gain": -0.1}, # 10000 Hz
{"band": 14, "gain": -0.2} # 16000 Hz
])
# Parameters:
# - preset: Preset name (optional)
# - bands: List of band adjustments (optional)
# - band: Band index (0-14)
# - gain: Gain in dB (-0.25 to 1.0)
#
Filter Management
#
Applying Filters
# Single filter
await player.set_filter("bassboost", gain=0.3)
# Multiple filters (stacking)
await player.set_filter("bassboost", gain=0.2)
await player.set_filter("reverb", room_size=0.6)
# Update existing filter
await player.set_filter("bassboost", gain=0.5) # Updates gain
#
Removing Filters
# Remove specific filter
await player.clear_filter("bassboost")
# Remove all filters
await player.clear_filters()
# Reset to defaults
await player.reset_filters()
#
Checking Active Filters
# Get active filters
active_filters = player.filters.active_filters
print(f"Active: {list(active_filters.keys())}")
# Check if filter is active
if player.filters.has_filter("bassboost"):
print("Bass boost is active")
#
Advanced Usage
#
Filter Chaining
# Create a complex effect chain
await player.set_filter("equalizer", preset="rock")
await player.set_filter("bassboost", gain=0.3)
await player.set_filter("reverb", room_size=0.4, damping=0.3)
#
Dynamic Filter Control
# Gradually increase bass during playback
import asyncio
async def dynamic_bass():
for gain in [0.0, 0.1, 0.2, 0.3, 0.4, 0.5]:
await player.set_filter("bassboost", gain=gain)
await asyncio.sleep(2) # Change every 2 seconds
asyncio.create_task(dynamic_bass())
#
Context-Aware Filtering
# Adjust filters based on track genre
@event_manager.on(EventType.TRACK_START)
async def adjust_filters(event):
track = event.data['track']
# Simple genre detection based on title
title_lower = track.title.lower()
if 'rock' in title_lower or 'metal' in title_lower:
await player.set_filter("equalizer", preset="rock")
elif 'pop' in title_lower or 'dance' in title_lower:
await player.set_filter("equalizer", preset="pop")
elif 'classical' in title_lower:
await player.set_filter("equalizer", preset="classical")
else:
await player.clear_filters()
#
Custom Filters
#
Creating Custom Filters
from sonora.filters import AudioFilter, register_filter
@register_filter("my_custom_filter")
class MyCustomFilter(AudioFilter):
def __init__(self, intensity=1.0):
self.intensity = intensity
def apply(self, audio_data):
# Apply your custom audio processing
# audio_data is raw PCM audio
processed = self.process_audio(audio_data, self.intensity)
return processed
def process_audio(self, data, intensity):
# Your DSP logic here
# This is a simplified example
return data * intensity # Simple amplification
#
Filter Parameters
class ParametricFilter(AudioFilter):
def __init__(self, frequency=1000, q=1.0, gain=0.0):
self.frequency = frequency
self.q = q # Quality factor
self.gain = gain
def apply(self, audio_data):
# Implement parametric EQ
return self.parametric_eq(audio_data, self.frequency, self.q, self.gain)
#
Performance Considerations
#
CPU Usage
- Filters increase CPU usage, especially when stacked
- Complex filters like reverb use more processing power
- Monitor performance with
sonoractl performance
#
Memory Usage
- Filters may require additional memory for processing buffers
- Large audio buffers can impact memory usage
#
Best Practices
- Use Sparingly: Only apply necessary filters
- Monitor Performance: Check CPU usage during playback
- Test Combinations: Ensure filter combinations sound good
- Provide Controls: Allow users to adjust filter parameters
- Save Presets: Store user filter preferences
#
Troubleshooting
#
Common Issues
Filters Not Applying
- Check if Lavalink supports the filter
- Verify filter parameters are valid
- Ensure player is connected
Audio Quality Issues
- Some filters may introduce artifacts at extreme settings
- Reduce intensity if audio sounds distorted
- Try different parameter combinations
Performance Problems
- Reduce number of active filters
- Use simpler filters for resource-constrained environments
- Monitor with performance diagnostics
#
Debug Information
# Get filter debug info
debug_info = player.filters.get_debug_info()
print(f"Active filters: {debug_info['active_filters']}")
print(f"CPU usage: {debug_info['cpu_usage']}%")
print(f"Memory usage: {debug_info['memory_usage']}MB")
#
Examples
#
DJ-Style Filter Control
class DJController:
def __init__(self, player):
self.player = player
self.presets = {
'chill': {'reverb': {'room_size': 0.8, 'damping': 0.3}},
'party': {'bassboost': {'gain': 0.5}, 'nightcore': {'speed': 1.1}},
'focus': {'equalizer': {'preset': 'classical'}}
}
async def apply_preset(self, name):
if name not in self.presets:
return
await self.player.clear_filters()
preset = self.presets[name]
for filter_name, params in preset.items():
await self.player.set_filter(filter_name, **params)
async def fade_filter(self, filter_name, param_name, start_val, end_val, duration=5.0):
steps = 20
step_duration = duration / steps
step_size = (end_val - start_val) / steps
current_val = start_val
for _ in range(steps):
await self.player.set_filter(filter_name, **{param_name: current_val})
await asyncio.sleep(step_duration)
current_val += step_size
# Usage
dj = DJController(player)
await dj.apply_preset('party')
await dj.fade_filter('bassboost', 'gain', 0.0, 0.5, 3.0)
#
Adaptive Filtering
class AdaptiveFilters:
def __init__(self, player):
self.player = player
self.track_history = []
async def analyze_and_apply(self, track):
# Simple analysis based on track metadata
genre = self.detect_genre(track)
energy = self.estimate_energy(track)
filters = {}
if genre == 'electronic':
filters['bassboost'] = {'gain': 0.4}
if energy > 0.7:
filters['nightcore'] = {'speed': 1.05}
elif genre == 'classical':
filters['reverb'] = {'room_size': 0.9, 'damping': 0.2}
elif genre == 'rock':
filters['equalizer'] = {'preset': 'rock'}
# Apply filters
await self.player.clear_filters()
for filter_name, params in filters.items():
await self.player.set_filter(filter_name, **params)
self.track_history.append((track, filters))
def detect_genre(self, track):
# Simplified genre detection
title = track.title.lower()
if any(word in title for word in ['electronic', 'edm', 'dance']):
return 'electronic'
elif any(word in title for word in ['classical', 'orchestra']):
return 'classical'
elif any(word in title for word in ['rock', 'metal']):
return 'rock'
return 'unknown'
def estimate_energy(self, track):
# Simplified energy estimation
# In practice, you'd analyze audio features
return 0.5 # Placeholder
This adaptive system can automatically apply appropriate filters based on track characteristics, creating a more immersive listening experience.