47 lines
1.3 KiB
Python
47 lines
1.3 KiB
Python
from __future__ import annotations
|
|
|
|
import array
|
|
import math
|
|
|
|
from zyrarbot.audio.effects.base import AudioEffect
|
|
|
|
|
|
class RobotEffect(AudioEffect):
|
|
"""Efekt metalicznego, robotycznego głosu oparty na ring modulation (modulacja pierścieniowa ~65Hz)."""
|
|
|
|
id = "robot"
|
|
name = "Robot"
|
|
icon = "bot"
|
|
|
|
def __init__(self, freq_hz: float = 65.0, mix: float = 0.85) -> None:
|
|
self.freq_hz = freq_hz
|
|
self.mix = mix
|
|
self._phase = 0.0
|
|
self._phase_inc = 2.0 * math.pi * freq_hz / 48000.0
|
|
|
|
def process(self, pcm_data: bytes, sample_rate: int = 48000) -> bytes:
|
|
if not pcm_data:
|
|
return pcm_data
|
|
|
|
samples = array.array("h", pcm_data)
|
|
phase = self._phase
|
|
phase_inc = self._phase_inc
|
|
mix = self.mix
|
|
dry = 1.0 - mix
|
|
|
|
for i in range(0, len(samples), 2):
|
|
carrier = math.sin(phase)
|
|
phase += phase_inc
|
|
if phase >= 2.0 * math.pi:
|
|
phase -= 2.0 * math.pi
|
|
|
|
mod_factor = dry + mix * carrier
|
|
samples[i] = max(-32768, min(32767, int(samples[i] * mod_factor)))
|
|
samples[i + 1] = max(-32768, min(32767, int(samples[i + 1] * mod_factor)))
|
|
|
|
self._phase = phase
|
|
return samples.tobytes()
|
|
|
|
def reset(self) -> None:
|
|
self._phase = 0.0
|