feat: add player audio effects module with real-time pipeline, distortion, fourth density, and UI controls
This commit is contained in:
20 files changed
+698
-70
No files matched your search
@@ -21,6 +21,7 @@ EXPECTED_ENDPOINTS = {
|
||||
("post", "/api/stop"),
|
||||
("post", "/api/volume"),
|
||||
("post", "/api/volume_boost"),
|
||||
("post", "/api/effects/toggle"),
|
||||
("post", "/api/loop"),
|
||||
("post", "/api/loop_playlist"),
|
||||
("post", "/api/loop_cycle"),
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import struct
|
||||
import unittest
|
||||
|
||||
from zyrarbot.audio.effects import (
|
||||
AudioEffectsPipeline,
|
||||
DistortionEffect,
|
||||
FourthDensityEffect,
|
||||
MuffledEffect,
|
||||
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_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"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user