feat: smooth volume ramping over 200ms in audio streaming
Some checks failed
CI / test (3.11) (push) Failing after 22s
CI / test (3.12) (push) Failing after 22s
CI / test (3.13) (push) Failing after 22s

Volume changes now ramp linearly per-sample via _scale_pcm_ramp instead
of jumping abruptly. Each frame steps _cur_vol toward target by at most
0.1, giving ~200ms for a full 0-to-1 sweep. Fast path unchanged when
volume is stable.
This commit is contained in:
user
2026-02-21 23:32:22 +01:00
parent c5c61e63cc
commit 6b7d733650
4 changed files with 115 additions and 9 deletions

View File

@@ -2,13 +2,14 @@
import asyncio
import struct
from unittest.mock import patch, MagicMock
from unittest.mock import patch
from derp.mumble import (
MumbleBot,
MumbleMessage,
_escape_html,
_scale_pcm,
_scale_pcm_ramp,
_shell_quote,
_strip_html,
)
@@ -583,3 +584,64 @@ class TestShellQuote:
quoted = _shell_quote(url)
assert quoted.startswith("'")
assert quoted.endswith("'")
# ---------------------------------------------------------------------------
# TestPcmRamping
# ---------------------------------------------------------------------------
class TestPcmRamping:
def test_flat_when_equal(self):
"""When vol_start == vol_end, behaves like _scale_pcm."""
pcm = struct.pack("<hh", 1000, -1000)
result = _scale_pcm_ramp(pcm, 0.5, 0.5)
expected = _scale_pcm(pcm, 0.5)
assert result == expected
def test_linear_interpolation(self):
"""Volume ramps linearly from start to end across samples."""
pcm = struct.pack("<hhhh", 10000, 10000, 10000, 10000)
result = _scale_pcm_ramp(pcm, 0.0, 1.0)
samples = struct.unpack("<hhhh", result)
# At i=0: vol=0.0, i=1: vol=0.25, i=2: vol=0.5, i=3: vol=0.75
assert samples[0] == 0
assert samples[1] == 2500
assert samples[2] == 5000
assert samples[3] == 7500
def test_clamp_positive(self):
"""Ramping up with loud samples clamps to 32767."""
pcm = struct.pack("<h", 32767)
result = _scale_pcm_ramp(pcm, 2.0, 2.0)
samples = struct.unpack("<h", result)
assert samples[0] == 32767
def test_clamp_negative(self):
"""Ramping up with negative samples clamps to -32768."""
pcm = struct.pack("<h", -32768)
result = _scale_pcm_ramp(pcm, 2.0, 2.0)
samples = struct.unpack("<h", result)
assert samples[0] == -32768
def test_preserves_length(self):
"""Output length equals input length."""
pcm = b"\x00" * 1920
result = _scale_pcm_ramp(pcm, 0.0, 1.0)
assert len(result) == 1920
def test_empty_data(self):
"""Empty input returns empty output."""
result = _scale_pcm_ramp(b"", 0.0, 1.0)
assert result == b""
def test_reverse_direction(self):
"""Volume ramps down from start to end."""
pcm = struct.pack("<hhhh", 10000, 10000, 10000, 10000)
result = _scale_pcm_ramp(pcm, 1.0, 0.0)
samples = struct.unpack("<hhhh", result)
# At i=0: vol=1.0, i=1: vol=0.75, i=2: vol=0.5, i=3: vol=0.25
assert samples[0] == 10000
assert samples[1] == 7500
assert samples[2] == 5000
assert samples[3] == 2500