Files
zyrarbot/tests/test_effects.py
T
mmacierzanka dadb5f8b0e
Backend / Lint i formatowanie (pull_request) Successful in 15s
Backend / Testy i kontrakt API (pull_request) Successful in 26s
Panel / Lint, typy i build (pull_request) Failing after 36s
Backend / Testowy build obrazu (pull_request) Successful in 1m34s
refactor: track background tasks in application lifecycle and clean up codebase
2026-09-13 16:58:46 +02:00

102 lines
3.5 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)
left, right = struct.unpack("<hh", out)
# Low 11 bits are zeroed due to 5-bit quantization
self.assertEqual(left & 0x07FF, 0)
self.assertEqual(right & 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_structure(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.assertEqual(set(entry.keys()), {"id", "name", "icon"})
if __name__ == "__main__":
unittest.main()