Spaces:
Sleeping
Sleeping
Nick Martins
Demo safety: curated examples only (no upload), 0.3s RIR + iter cap → bounded, no stall
41bf0d3 Download dfdn/delay.py from nicktup/reverb-extractor: direct link, hf CLI and curl.
- Browser
- Download file 2.9 kB
-
https://huggingface.co/spaces/nicktup/reverb-extractor/resolve/main/dfdn/delay.py
- Command line
-
hf download hf://spaces/nicktup/reverb-extractor/dfdn/delay.py
-
curl -L -o delay.py https://huggingface.co/spaces/nicktup/reverb-extractor/resolve/main/dfdn/delay.py
2.9 kB
| """Differentiable fractional delay lines (the novel ingredient of the paper). | |
| Integer delays implemented by reading a circular buffer are *not* | |
| differentiable w.r.t. the delay length. The paper makes them differentiable by | |
| realising each delay in the frequency domain with the Pei--Lai closed-form | |
| variable fractional-delay filter, whose frequency response is a smooth function | |
| of the (real-valued) delay ``m``. Gradients therefore flow through the FFT to | |
| ``m`` itself. | |
| For a K-point DFT the delay ``z^{-m}`` has the frequency response (Eq. in | |
| Sec. 3 of the paper):: | |
| D[0] = 1 | |
| D[k] = exp(-j m (2*pi/K) k) for k = 1 .. K/2 - 1 | |
| D[K/2] = cos(m * pi) (Nyquist bin, forced real) | |
| D[K-k] = conj(D[k]) (Hermitian symmetry) | |
| The Nyquist bin is forced real so that the inverse transform of a real signal | |
| stays real; in the time domain this corresponds to a windowed-sinc FIR | |
| interpolator. We only build the non-redundant half spectrum (k = 0 .. K/2) and | |
| rely on ``torch.fft.irfft`` to supply the Hermitian mirror automatically. | |
| """ | |
| from __future__ import annotations | |
| import math | |
| import torch | |
| def fractional_delay_frequency_response( | |
| m: torch.Tensor, n_fft: int | |
| ) -> torch.Tensor: | |
| """Half-spectrum (rfft layout) frequency response of ``z^{-m}``. | |
| Parameters | |
| ---------- | |
| m | |
| Delay lengths in samples, shape ``(N,)`` (may be fractional / real). | |
| n_fft | |
| FFT length ``K``. Must be even. | |
| Returns | |
| ------- | |
| torch.Tensor | |
| Complex tensor of shape ``(N, K // 2 + 1)`` giving ``D_i[k]`` for | |
| ``k = 0 .. K/2``. Differentiable w.r.t. ``m``. | |
| """ | |
| if n_fft % 2 != 0: | |
| raise ValueError("n_fft must be even") | |
| m = torch.as_tensor(m) | |
| n_bins = n_fft // 2 + 1 | |
| k = torch.arange(n_bins, device=m.device, dtype=m.dtype) # 0 .. K/2 | |
| # Phase of a pure delay: -m * (2*pi/K) * k, broadcast over lines. | |
| phase = -m.unsqueeze(-1) * (2.0 * math.pi / n_fft) * k.unsqueeze(0) # (N, n_bins) | |
| resp = torch.polar(torch.ones_like(phase), phase) # unit-modulus complex | |
| # Force the Nyquist bin (k = K/2) to be real = cos(m*pi) so that irfft of a | |
| # real spectrum is real. This is the Pei-Lai construction. | |
| nyq = torch.cos(m * math.pi).to(resp.dtype) # (N,) | |
| resp = resp.clone() | |
| resp[:, -1] = nyq | |
| # k = 0 bin is exactly 1 by construction (phase 0), left as is. | |
| return resp | |
| def apply_fractional_delay(x: torch.Tensor, m: torch.Tensor) -> torch.Tensor: | |
| """Delay each row of ``x`` by ``m`` samples via the frequency domain. | |
| Provided for testing / standalone use. ``x`` has shape ``(N, L)`` and ``m`` | |
| shape ``(N,)``. Returns a tensor of the same shape as ``x``. | |
| """ | |
| N, L = x.shape | |
| X = torch.fft.rfft(x, n=L, dim=-1) | |
| D = fractional_delay_frequency_response(m, L) | |
| return torch.fft.irfft(X * D, n=L, dim=-1) | |