Source code for voicebox.effects.flanger

__all__ = ["Flanger"]

import time
from typing import Callable, Optional

import numpy as np

from voicebox.audio import Audio
from voicebox.effects.effect import EffectWithDryWet


[docs] class Flanger(EffectWithDryWet): """ Flanger effect with a very metallic sound. Args: rate: LFO rate in Hz. Default is .15. min_delay: Minimum delay time in seconds. Default is .0025. max_delay: Maximum delay time in seconds. Default is .0035. feedback: Delay feedback. Should be in range (0, 1). Default is .9. t_offset: Offset time to add to the LFO time, in seconds. Default is 0. t_offset_func: Optional function that returns a time by which to offset the LFO time (in addition to ``t_offset``). Defaults to a function that returns the current time, which makes the LFO phase consistent over time between runs. dry: Dry (input) signal level. 0 is none, 1 is unity. Default is .5. wet: Wet (affected) signal level. 0 is none, 1 is unity. Default is .5. """ rate: float min_delay: float max_delay: float feedback: float t_offset: float t_offset_func: Optional[Callable[[], float]] def __init__( self, rate: float = 0.15, min_delay: float = 0.0025, max_delay: float = 0.0035, feedback: float = 0.9, t_offset: float = 0.0, t_offset_func: Optional[Callable[[], float]] = time.monotonic, dry: float = 0.5, wet: float = 0.5, ): super().__init__(dry, wet) self.rate = rate self.min_delay = min_delay self.max_delay = max_delay self.feedback = feedback self.t_offset = t_offset self.t_offset_func = t_offset_func
[docs] def get_wet_signal(self, audio: Audio) -> np.ndarray: delay_offsets = self._get_delay_offsets(audio) wet = np.zeros_like(audio.signal) for i, (in_sample, delay_offset) in enumerate(zip(audio.signal, delay_offsets)): i_delay = i - delay_offset delay_sample = wet[i_delay] if i_delay >= 0 else 0 wet[i] = in_sample + self.feedback * delay_sample return wet
def _get_delay_offsets(self, audio: Audio) -> np.ndarray: t = self._get_time_from_lfo(audio) delay_times = np.cos(2 * np.pi * self.rate * t) delay_times = (delay_times + 1) / 2 delay_times = self.min_delay + (self.max_delay - self.min_delay) * delay_times return np.round(delay_times * audio.sample_rate).astype(int) def _get_time_from_lfo(self, audio: Audio) -> np.ndarray: t = np.arange(len(audio)) * audio.sample_period + self.t_offset if self.t_offset_func: t += self.t_offset_func() % (1 / self.rate) return t