# 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

  1. Use Sparingly: Only apply necessary filters
  2. Monitor Performance: Check CPU usage during playback
  3. Test Combinations: Ensure filter combinations sound good
  4. Provide Controls: Allow users to adjust filter parameters
  5. 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.