Files
zyrarbot/tests/test_effects.py
T

111 lines
3.8 KiB
Python

from __future__ import annotations
import struct
import unittest
from zyrarbot.audio.effects import (
AudioEffectsPipeline,
DistortionEffect,
FourthDensityEffect,
MuffledEffect,
RobotEffect,
ToasterEffect,
TremoloEffect,
create_default_effects,
)
class TestAudioEffects(unittest.TestCase):
def test_distortion_clips_pcm(self):
fx = DistortionEffect(multiplier=10.0)
pcm = struct.pack("<hh", 10000, -10000)
out = fx.process(pcm)
left, right = struct.unpack("<hh", out)
self.assertEqual(left, 32767)
self.assertEqual(right, -32768)
def test_muffled_filters_pcm(self):
fx = MuffledEffect(alpha=0.1)
# Step input
pcm = struct.pack("<hh", 10000, 10000)
out = fx.process(pcm)
left, right = struct.unpack("<hh", out)
# With alpha=0.1, output sample should be smoothed to 1000
self.assertEqual(left, 1000)
self.assertEqual(right, 1000)
def test_tremolo_modulates_amplitude(self):
fx = TremoloEffect(freq_hz=4.0, depth=0.6)
pcm = struct.pack("<hh", 10000, 10000)
out = fx.process(pcm)
left, right = struct.unpack("<hh", out)
# At phase 0, sin(0) = 0 -> lfo = 0.5 -> factor = 1.0 - 0.6*0.5 = 0.7
self.assertAlmostEqual(left, 7000, delta=100)
self.assertAlmostEqual(right, 7000, delta=100)
def test_fourth_density_processes_without_error(self):
fx = FourthDensityEffect()
pcm = (struct.pack("<hh", 1000, -1000)) * 960
out = fx.process(pcm)
self.assertEqual(len(out), len(pcm))
def test_robot_modulates_ring(self):
fx = RobotEffect(freq_hz=65.0, mix=0.85)
pcm = (struct.pack("<hh", 10000, 10000)) * 960
out = fx.process(pcm)
self.assertEqual(len(out), len(pcm))
self.assertNotEqual(out, pcm)
def test_toaster_bitcrush_pcm(self):
fx = ToasterEffect(bit_depth=5, downsample=6)
pcm = struct.pack("<hh", 12345, -23456)
out = fx.process(pcm)
l, r = struct.unpack("<hh", out)
# Low 11 bits are zeroed due to 5-bit quantization
self.assertEqual(l & 0x07FF, 0)
self.assertEqual(r & 0x07FF, 0)
def test_pipeline_chains_multiple_effects_and_applies_volume(self):
pipeline = AudioEffectsPipeline(create_default_effects())
pcm = (struct.pack("<hh", 1000, -1000)) * 960
# When no effects active and volume=1.0, data is unchanged
out = pipeline.process(pcm, volume=1.0)
self.assertEqual(out, pcm)
# Activate fourth_density and distortion simultaneously
pipeline.set_active("fourth_density", True)
pipeline.set_active("distortion", True)
self.assertTrue(pipeline.is_active("fourth_density"))
self.assertTrue(pipeline.is_active("distortion"))
out_mult = pipeline.process(pcm, volume=0.5)
self.assertEqual(len(out_mult), len(pcm))
self.assertNotEqual(out_mult, pcm)
# Toggle off
toggled = pipeline.toggle("distortion")
self.assertFalse(toggled)
self.assertFalse(pipeline.is_active("distortion"))
def test_effects_metadata_does_not_contain_badge(self):
from zyrarbot.audio.effects.registry import get_available_effects_metadata
metadata = get_available_effects_metadata()
self.assertGreater(len(metadata), 0)
for entry in metadata:
self.assertIn("id", entry)
self.assertIn("name", entry)
self.assertIn("description", entry)
self.assertIn("icon", entry)
self.assertNotIn("badge", entry)
def test_effect_instances_have_no_badge_attribute(self):
for effect in create_default_effects():
self.assertFalse(hasattr(effect, "badge"))
if __name__ == "__main__":
unittest.main()