fft
stableFast Fourier Transform and spectral analysis utilities. Compute forward/inverse FFT, power spectra, windowing, STFT, convolution, and peak frequency detection on real-valued signal arrays.
use plugin fft::{fft, ifft, magnitude, …} Functions (19)
- fft Forward FFT of real+imaginary arrays
- ifft Inverse FFT back to time domain
- magnitude Per-bin magnitude from real+imag arrays
- phase Per-bin phase angle from real+imag arrays
- power_spectrum Power spectrum of a real signal
- next_power_of_2 Smallest power of 2 >= n
- windowed_fft FFT with a window function applied first
- stft Short-Time Fourier Transform (frame-based)
- peak_frequencies Find dominant frequency peaks
- bandpass Zero out bins outside a frequency range
- convolution Linear convolution via FFT
- cross_correlation Cross-correlation of two signals via FFT
- autocorrelation Autocorrelation of a signal via FFT
- spectral_centroid Frequency-weighted centre of the spectrum
- spectral_rolloff Frequency below which most energy lies
- frequency_bins Frequency value for each FFT bin
- apply_window Apply a window function to a signal
- generate_sine Generate a sine wave sample array
- db_scale Convert magnitude values to decibels
Overview
fft is a dependency-free signal-processing toolkit built around a Cooley-Tukey radix-2 FFT. Signals are plain Zolo arrays of numbers — there is no opaque handle or stateful context — so a waveform is just a table you can build, slice, and pass around freely. Inputs whose length is not a power of 2 are zero-padded automatically, and the complex spectrum is always returned as a {real, imag} table you can feed into the magnitude, phase, and spectral helpers.
The mental model is a pipeline: produce or load a real signal (for example with generate_sine), optionally taper it with apply_window, transform it with fft, then analyse the result with magnitude, peak_frequencies, spectral_centroid, or db_scale — and ifft back to the time domain when you need to reconstruct a filtered signal. Higher-level helpers such as convolution, cross_correlation, and stft wrap that pipeline for common tasks. Use it whenever you need spectral analysis, frequency-domain filtering, or fast convolution without pulling in a numeric library.
Common patterns
Generate a tone, transform it, and read back its magnitude spectrum:
use plugin fft::{generate_sine, fft, magnitude}
let signal = generate_sine(440.0, 44100.0, 1024)
let zeros = generate_sine(0.0, 44100.0, 1024)
let spec = fft(signal, zeros)
let mags = magnitude(spec["real"], spec["imag"])
print("bins: {mags}")
Find the dominant frequency of a noisy signal:
use plugin fft::{generate_sine, fft, peak_frequencies}
let sig = generate_sine(1000.0, 8000.0, 1024)
let zeros = generate_sine(0.0, 8000.0, 1024)
let spec = fft(sig, zeros)
let peaks = peak_frequencies(spec["real"], spec["imag"], 8000.0)
print("dominant: {peaks[1]["frequency"]} Hz")
Band-pass filter a signal in the frequency domain, then reconstruct it:
use plugin fft::{generate_sine, fft, bandpass, ifft}
let sig = generate_sine(440.0, 44100.0, 1024)
let zeros = generate_sine(0.0, 44100.0, 1024)
let spec = fft(sig, zeros)
let filtered = bandpass(spec["real"], spec["imag"], 400.0, 500.0, 44100.0)
let restored = ifft(filtered["real"], filtered["imag"])
print(restored["real"])
Forward FFT of real+imaginary arrays
Performs a forward Cooley-Tukey radix-2 FFT. Both arrays must be the same length; zero-padding to the next power of 2 is applied automatically. Returns {real, imag}.
use plugin fft::{fft, magnitude, generate_sine}
let signal = generate_sine(440.0, 44100.0, 1024)
let zeros = generate_sine(0.0, 44100.0, 1024)
let result = fft(signal, zeros)
let mags = magnitude(result["real"], result["imag"])
print(mags)
Inverse FFT back to time domain
Inverse FFT: converts frequency-domain arrays back to the time domain. The output is normalised by 1/N. Returns {real, imag}.
use plugin fft::{fft, ifft, generate_sine}
let sig = generate_sine(220.0, 44100.0, 512)
let zeros = generate_sine(0.0, 44100.0, 512)
let freq = fft(sig, zeros)
let back = ifft(freq["real"], freq["imag"])
print(back["real"])
A forward then inverse transform reconstructs the original signal (up to padding):
use plugin fft::{fft, ifft}
let sig = [1.0, 2.0, 3.0, 4.0]
let zeros = [0.0, 0.0, 0.0, 0.0]
let freq = fft(sig, zeros)
let round = ifft(freq["real"], freq["imag"])
print(round["real"]) // ~[1, 2, 3, 4]
Per-bin magnitude from real+imag arrays
Computes sqrt(r^2 + i^2) for each bin. Use this after fft to get the amplitude spectrum.
use plugin fft::{fft, magnitude, generate_sine}
let sig = generate_sine(1000.0, 8000.0, 256)
let im = generate_sine(0.0, 8000.0, 256)
let spec = fft(sig, im)
let mags = magnitude(spec["real"], spec["imag"])
print(mags)
Per-bin phase angle from real+imag arrays
Computes atan2(imag, real) for each bin, returning phase angles in radians.
use plugin fft::{fft, phase, generate_sine}
let sig = generate_sine(100.0, 8000.0, 256)
let im = generate_sine(0.0, 8000.0, 256)
let sp = fft(sig, im)
let ph = phase(sp["real"], sp["imag"])
print(ph)
Power spectrum of a real signal
Computes the power spectrum (r^2 + i^2) of a real-valued signal in one step. Takes only the real array; the imaginary part is assumed zero.
use plugin fft::{power_spectrum, generate_sine}
let sig = generate_sine(440.0, 44100.0, 1024)
let power = power_spectrum(sig)
print(power)
Smallest power of 2 >= n
Returns the smallest power of 2 that is greater than or equal to n. Useful for sizing FFT buffers.
use plugin fft::{next_power_of_2}
print(next_power_of_2(100)) // 128
print(next_power_of_2(1024)) // 1024
print(next_power_of_2(1025)) // 2048
FFT with a window function applied first
Applies a window function to the signal before computing the FFT. Supported window types: "hann", "hamming", "blackman". Returns {real, imag}.
use plugin fft::{windowed_fft, magnitude, generate_sine}
let sig = generate_sine(440.0, 44100.0, 1024)
let spec = windowed_fft(sig, "hann")
let mags = magnitude(spec["real"], spec["imag"])
print(mags)
Short-Time Fourier Transform (frame-based)
Computes the Short-Time Fourier Transform by sliding a Hann-windowed frame over the signal with the given hop size. Returns a table of frames, each containing {real, imag} for the positive-frequency bins.
use plugin fft::{stft, generate_sine}
let sig = generate_sine(440.0, 44100.0, 4096)
let frames = stft(sig, 512, 256)
print("frame count: {frames}")
Each frame is a {real, imag} table, so you can transform a single frame further:
use plugin fft::{stft, magnitude, generate_sine}
let sig = generate_sine(440.0, 44100.0, 4096)
let frames = stft(sig, 512, 256)
let first = frames[1]
let mags = magnitude(first["real"], first["imag"])
print(mags)
Find dominant frequency peaks
Finds local maxima in the magnitude spectrum and returns them sorted by magnitude descending. Each entry contains {frequency, magnitude}.
use plugin fft::{fft, peak_frequencies, generate_sine}
let sig = generate_sine(440.0, 44100.0, 1024)
let zeros = generate_sine(0.0, 44100.0, 1024)
let spec = fft(sig, zeros)
let peaks = peak_frequencies(spec["real"], spec["imag"], 44100.0)
print(peaks[1]["frequency"]) // ~440 Hz
Zero out bins outside a frequency range
Zeroes out all FFT bins outside the [low_freq, high_freq] range, effectively filtering the spectrum. Returns {real, imag}.
use plugin fft::{fft, bandpass, ifft, generate_sine}
let sig = generate_sine(440.0, 44100.0, 1024)
let zeros = generate_sine(0.0, 44100.0, 1024)
let spec = fft(sig, zeros)
let filtered = bandpass(spec["real"], spec["imag"], 400.0, 500.0, 44100.0)
let out = ifft(filtered["real"], filtered["imag"])
print(out["real"])
Linear convolution via FFT
Computes the linear convolution of two signals using FFT multiplication. Returns the result as a real-valued array of length len(s1) + len(s2) - 1.
use plugin fft::{convolution}
let a = [1.0, 2.0, 3.0]
let b = [1.0, 1.0]
let out = convolution(a, b)
print(out) // [1.0, 3.0, 5.0, 3.0]
Convolving a signal with a smoothing kernel applies a moving average filter:
use plugin fft::{convolution}
let signal = [0.0, 4.0, 0.0, 4.0, 0.0]
let kernel = [0.5, 0.5]
let smooth = convolution(signal, kernel)
print(smooth)
Cross-correlation of two signals via FFT
Computes the cross-correlation between two signals via FFT. Useful for detecting time delays between related signals.
use plugin fft::{cross_correlation, generate_sine}
let a = generate_sine(440.0, 44100.0, 512)
let b = generate_sine(440.0, 44100.0, 512)
let corr = cross_correlation(a, b)
print(corr)
Autocorrelation of a signal via FFT
Computes the autocorrelation of a signal with itself, returning a real-valued array of length 2*N - 1. Useful for pitch detection.
use plugin fft::{autocorrelation, generate_sine}
let sig = generate_sine(440.0, 44100.0, 512)
let auto = autocorrelation(sig)
print(auto)
Frequency-weighted centre of the spectrum
Returns the frequency-weighted mean of the magnitude spectrum — the "centre of mass" of the spectrum. Higher values indicate more high-frequency content.
use plugin fft::{fft, magnitude, spectral_centroid, generate_sine}
let sig = generate_sine(1000.0, 44100.0, 1024)
let im = generate_sine(0.0, 44100.0, 1024)
let spec = fft(sig, im)
let mags = magnitude(spec["real"], spec["imag"])
let sc = spectral_centroid(mags, 44100.0)
print("centroid: {sc} Hz")
Frequency below which most energy lies
Returns the frequency below which threshold fraction (default 0.85) of total spectral energy is contained. Useful for characterising timbral brightness.
use plugin fft::{fft, magnitude, spectral_rolloff, generate_sine}
let sig = generate_sine(2000.0, 44100.0, 1024)
let im = generate_sine(0.0, 44100.0, 1024)
let spec = fft(sig, im)
let mags = magnitude(spec["real"], spec["imag"])
let ro = spectral_rolloff(mags, 44100.0, 0.85)
print("rolloff: {ro} Hz")
Frequency value for each FFT bin
Returns a table of n frequency values (in Hz), one per FFT bin. Use this to label the output of magnitude or power_spectrum.
use plugin fft::{frequency_bins}
let bins = frequency_bins(8, 8000.0)
print(bins) // [0, 1000, 2000, 3000, 4000, 5000, 6000, 7000]
Apply a window function to a signal
Applies a named window function ("hann", "hamming", or "blackman") to a sample array and returns the windowed signal. Use before FFT to reduce spectral leakage.
use plugin fft::{apply_window, generate_sine}
let sig = generate_sine(440.0, 44100.0, 512)
let windowed = apply_window(sig, "hamming")
print(windowed)
Generate a sine wave sample array
Generates a table of num_samples sine wave samples at the given frequency and sample rate. Useful for testing FFT functions.
use plugin fft::{generate_sine}
let wave = generate_sine(440.0, 44100.0, 1024)
print(wave[1]) // first sample
Convert magnitude values to decibels
Converts magnitude values to decibels using 20 * log10(magnitude / reference). reference defaults to 1.0 when omitted. Values of 0 or below become -Infinity.
use plugin fft::{fft, magnitude, db_scale, generate_sine}
let sig = generate_sine(440.0, 44100.0, 1024)
let im = generate_sine(0.0, 44100.0, 1024)
let spec = fft(sig, im)
let mags = magnitude(spec["real"], spec["imag"])
let db = db_scale(mags, 1.0)
print(db)
When reference is omitted it defaults to 1.0, so raw magnitudes convert directly to dB:
use plugin fft::{db_scale}
let mags = [1.0, 0.1, 0.01]
print(db_scale(mags)) // [0, -20, -40]