Learnable time-frequency front-ends#

These modules turn a raw 1-D signal (real or complex) into a native complex time-frequency representation, with learnable parameters so the front-end trains end-to-end with the rest of the model. They complement complextorch.signal.pwelch() and the complextorch.nn.FFTBlock family.

Learnable STFT#

complextorch.nn.STFT frames the signal, applies a learnable window (initialised to Hann), and FFTs each frame, returning a complex spectrogram of shape (..., n_fft, n_frames). complextorch.nn.InverseSTFT inverts it with a window-squared overlap-add, so with matching windows the round-trip is exact on every sample covered by a non-zero window tap.

import torch
import complextorch as ctorch

stft  = ctorch.nn.STFT(n_fft=64, hop_length=16)
istft = ctorch.nn.InverseSTFT(n_fft=64, hop_length=16)
istft.window = stft.window   # tie the windows so the inverse stays exact once trained

x = torch.randn(2, 1024, dtype=torch.cfloat)   # complex baseband signal
spec = stft(x)                                  # (2, 64, n_frames), complex
recon = istft(spec)
print("spectrogram:", spec.shape, spec.dtype)
print("reconstruction error (interior):",
      (recon[..., 64:-64] - x[..., 64:-64]).abs().max().item())

Learnable complex filterbanks (Gabor / Morlet)#

complextorch.nn.ComplexGaborConv1d is a complex, wavelet-style analogue of SincNet: each output filter is a windowed complex exponential

\[ g_o(t) = e^{-t^2/(2\sigma_o^2)}\, e^{j 2\pi f_o t} \]

with a learnable centre frequency \(f_o\) and bandwidth \(\sigma_o\), applied with a complex 1-D convolution. complextorch.nn.MorletConv1d is the zero-mean (admissible) variant — it subtracts the envelope-weighted mean so the filter has no DC response.

gabor = ctorch.nn.ComplexGaborConv1d(in_channels=1, out_channels=32,
                                     kernel_size=63, padding=31)
y = gabor(x.unsqueeze(1))   # (2, 32, 1024), complex
print(y.shape, y.dtype)