Source code for pyFDN.graphicEQ.absorption_geq

"""Absorption filter design via graphic EQ for FDN delay lines.

Translation of absorptionGEQ.m from fdnToolbox.

Reference:
    Schlecht and Habets, "Accurate reverberation time control in feedback
    delay networks," Proc. DAFx, 2017.
"""

from __future__ import annotations

import numpy as np

from ..auxiliary.acoustics import rt_to_slope
from .design_geq import design_geq


[docs] def absorption_geq( rt: np.ndarray, delays: np.ndarray, fs: float, ) -> np.ndarray: """Design per-delay GEQ absorption filters matching target reverberation times. Each delay line gets its own cascade of biquad sections whose combined attenuation per round trip matches the desired RT at each frequency band. Args: rt: Target reverberation time in seconds at 10 frequency bands, shape ``(10,)`` or broadcastable. delays: Delay lengths in samples, shape ``(num_delays,)``. fs: Sampling frequency in Hz. Returns: Per-channel SOS bank of shape ``(num_bands, 6, num_delays)`` (the canonical SOS bank layout) where ``num_bands = 11`` (flat + low-shelf + 8 bandpass + high-shelf). All sections are normalised so ``a[0] = 1``. """ rt = np.asarray(rt, dtype=float).ravel() delays = np.asarray(delays, dtype=float).ravel() target_g = rt_to_slope(rt, fs) # dB / sample (negative) num_delays = len(delays) prototype_sos, _ = design_geq(target_g * delays[0], fs=fs) num_bands = prototype_sos.shape[0] sos_out = np.zeros((num_bands, 6, num_delays)) for i, delay in enumerate(delays): opt_sos, _ = design_geq(target_g * delay, fs=fs) opt_sos = opt_sos / opt_sos[:, 3:4] # normalise a0 = 1 sos_out[:, :, i] = opt_sos return sos_out