BlitzFFT is a Rust CLI for audio Fourier analysis and exact whole-signal FFT benchmarking, with a native Rust CPU engine, 64-bit CPU processing, and an opt-in experimental 128-bit binary128 CPU mode for framed analysis on nightly Rust.
It has three complementary jobs:
- framed analysis, similar to an STFT pipeline, with backend auto-selection
CUDA -> Metal -> CPU - with frequency estimation errors of ±0.0000028 Hz (vs ±0.023 Hz for 32-bit) on an hour-long window, provide >180 dB SNR for scientific instrumentation applications
- exact whole-file FFT benchmarking on one long real-valued waveform, with a pure-Rust default comparison set and optional foreign-library comparisons
This repository is partly a tool and partly a notebook on FFT implementation tradeoffs. The code is useful on its own, but it is also a compact place to compare:
- framed versus whole-signal analysis
- real-input FFTs versus complex-input FFTs
- stable
64-bitprocessing versus experimental128-bitprocessing - GPU throughput versus CPU planning and execution costs
- simple libraries versus highly tuned planners
- power-of-two framing constraints versus arbitrary-length exact transforms
- Why this repo exists
- What changed recently
- How To Make It Better
- What an FFT is
- Math conventions used in this repo
- Why real-input FFTs matter
- How BlitzFFT uses FFTs
- Project architecture
- Install
- Build
- CLI
- Examples
- Whole-file benchmark methodology
- Simulated 60-minute whole-file FFT at 384 kHz
- Estimated scaling to multi-day FFTs at 384 kHz
- Roadmap
- Repository layout
- Reference map
- License
There are many excellent FFT libraries already. BlitzFFT is not trying to replace them all with a single universal implementation. Instead, the repo tries to answer a more practical question:
For audio work on a real machine, what actually changes when we switch the transform shape, the backend, the planning model, and the library?
That leads to two different workflows:
- Framed analysis for ordinary audio inspection.
- Exact whole-file transforms for benchmark and algorithm comparisons.
The framed path is the one most people expect from "audio FFT" software: split a waveform into overlapping windows, apply a tapering function, and transform each frame independently. That is what you want for time-local spectral analysis, spectrogram generation, transient inspection, and quick peak summaries.
The whole-file path is different. It treats the entire waveform as one signal and computes one exact forward FFT over the full sample count. That is useful when you care about:
- exact bin spacing for a long observation interval
- arbitrary, non-power-of-two signal lengths
- comparing library setup cost versus execution cost
- measuring how much work is saved by exploiting real-valued input structure
- The CPU framed backend now uses the native BlitzFFT engine instead of materializing a full complex
RustFFTinput buffer for every frame. - Framed results no longer allocate an unused full complex spectrum for each frame.
- The framed
f32CPU path can batch pairs of real frames by packing one frame into the complex real lane and the next frame into the imaginary lane, then recovering both real spectra from one complex FFT. - The native
f64power-of-two CPU FFT path now uses SIMD butterflies on supportedx86_64andaarch64machines. - The repo now has an exact whole-file benchmark path for long real-valued signals, including non-power-of-two lengths.
- The default whole-file benchmark compares three native-Rust paths side by side:
BlitzFFT native,RealFFT, andRustFFT complex. - Optional
FFTW3f,KissFFT, andPocketFFTcomparisons can be enabled explicitly with Cargo features. - The benchmark docs now center a simulated one-hour
384kHz scenario using a439.997Hz sine and a full-signal Hann window.
If we want BlitzFFT to become meaningfully better, the next work should stay focused on a few concrete themes.
The biggest remaining speed opportunity is the native CPU FFT core, especially the f64 whole-file path and the arbitrary-length Bluestein path. That means:
- add stronger SIMD coverage for
f64 - keep reducing copies and scratch churn in the real-input kernels
- improve the scalar Bluestein inner FFT path
- stop doing full-spectrum work when the caller only asked for summary peaks or top bins
This repo gets better when optimizations come with tests, not just faster tables. The native engine needs broader randomized checks, more cross-validation against rustfft and realfft, and clearer documented tolerances for f32 and f64.
The default story should remain: stable Rust, native CPU path, no required foreign FFT libraries. Optional GPU and comparison backends are valuable, but they should stay optional enough that the core tool remains easy to build and trust.
Some workflows only need a peak summary, a few top bins, or a machine-readable benchmark row. Those paths should not have to pay for full magnitude storage and formatting when the user did not ask for it.
The benchmark presentation is much stronger now than it used to be, but the repo still improves when it makes measured versus simulated results unmistakable, records feature flags and machine details, and ships a reproducible benchmark workflow.
The longer-form plan lives in ROADMAP.md.
An FFT is a fast algorithm for computing the discrete Fourier transform, or DFT.
The DFT takes a finite list of samples and rewrites it as a sum of discrete complex sinusoids. Instead of asking "what is the sample value at time index
For a length-$N$ signal
and the inverse DFT is
If you implement that definition directly, it costs
That is the whole reason FFTs matter. They do not change the transform being computed. They change the cost of computing it.
Some quick intuition helps:
- the time-domain signal is a sequence of samples
- the frequency-domain signal is a sequence of complex coefficients
- each coefficient describes the amplitude and phase of one discrete frequency bin
- a longer observation window gives finer frequency spacing
- a shorter observation window gives better time localization
The bin frequencies are
where
This is why a one-hour signal at
The most famous FFT family is the Cooley-Tukey decomposition. When
- radix-2 and radix-4 decompositions
- mixed-radix decompositions
- split-radix variants
- prime-factor algorithms
- Bluestein and Rader methods for awkward lengths, especially primes
Different libraries package those ideas differently. Some are tiny and simple. Others, like FFTW, search over many possible plans and choose the fastest measured one for the host machine.
BlitzFFT follows the standard forward-transform sign convention with a negative exponential:
Some practical conventions matter when you compare FFT libraries:
- Most FFT libraries return an unnormalized forward transform.
- Normalization, if desired, is usually applied separately.
- For real-valued input, the negative-frequency half of the spectrum is redundant.
For a real signal,
so a real-input forward FFT only needs to return the nonnegative-frequency half-spectrum:
That is why the positive-frequency output length is
for even
Audio samples are real-valued. That sounds obvious, but it changes the implementation story a lot.
If you feed a real signal into a generic complex FFT, you usually have to:
- allocate a complex buffer of length
$N$ - copy each real sample into the real part
- set all imaginary parts to zero
- compute a full complex FFT
- ignore half the result because it is conjugate-redundant
A real-input FFT avoids most of that waste. In practical terms, that can mean:
- less input marshaling
- less memory traffic
- less output storage
- faster execution
- a cleaner API for audio workloads
RealFFT in Rust builds on RustFFT but exposes the real-to-complex path directly. FFTW3f, KissFFT, and PocketFFT also provide real-input transforms. This repo makes that comparison explicit.
For framed f32 CPU analysis, BlitzFFT also uses a related two-real-FFTs-in-one-complex-FFT trick. Given two real frames a[n] and b[n], the CPU backend packs them as:
It then computes one complex FFT and recovers the two real spectra with conjugate symmetry:
This does not make the whole pipeline twice as fast, because packing, unpacking, magnitudes, memory traffic, and output still cost time. It does reduce transform scheduling overhead for frame-heavy workloads and keeps the all-real signal structure explicit in the native CPU backend.
There are two very different transform modes in this repository.
The framed path is an STFT-style workflow:
- load or synthesize audio
- downmix to mono if needed
- split the signal into overlapping frames
- apply a Hann window per frame
- compute one FFT per frame
- emit magnitudes, summaries, CSV, or binary output
For frame index
where
The Hann window used in this repo is
In the CPU framed backend, the transform work is done with cached native BlitzFFT plans plus thread-local work buffers. On Apple hardware, the Metal backend applies the frame window on-device. On NVIDIA systems, the CUDA backend is preferred when enabled and available.
The whole-file mode computes one forward FFT over the entire signal:
This is not an STFT. There is no hop size and no time-local frame index. You get one spectrum covering the full observation interval.
That is why the reported frequency resolution for the one-hour example is
This mode exists to compare transform engines and data-motion costs, not to provide time-local spectral evolution.
The value above is a linear frequency-bin spacing in hertz. If, separately, you want to think about a similarly sized interval in logarithmic pitch space, take
interpreted as a base-2 log-frequency interval. The corresponding frequency ratio is
A cent, written ¢, is one hundredth of a semitone, or 1/1200 of an octave. The equivalent size here is
So that target interval is approximately:
0.3333 ¢1.0001925as a frequency ratio
It is also exactly one step of 3600-EDO, since
per equal division of the octave.
This is smaller than the usual named commas in tuning theory. A useful nearby reference is one sixth of a schisma. Using
gives
so
which is very close to 0.3333 ¢, with an error of about 0.008 ¢.
At a high level, the project is organized like this:
src/main.rsCLI parsing, input selection, framing, backend selection, and dispatchsrc/audio.rsWAV loading, mono downmixing, Hann windows, framing, sine synthesis, and bin-to-Hz conversionsrc/backends/framed FFT backends for CPU, Metal, and CUDAsrc/benchmark.rsframed benchmark harness comparing the selected backend against the CPU baselinesrc/whole_fft.rsexact whole-signal benchmark harness across multiple FFT librariessrc/native/pocketfft_bridge.ccthin C++ bridge exposing vendored PocketFFT to Rustvendor/kissfft/vendored KISS FFT sourcesvendor/pocketfft/vendored PocketFFT header-only implementation
Automatic backend selection is:
CUDA -> Metal -> CPU
If you force a backend from the CLI, that explicit choice wins.
The benchmark table compares closely related but not identical implementation styles:
BlitzFFT nativeThe repo's own native Rust real-input path with reusable plans, scratch buffers, and output buffers.RealFFTA directrealfftcrate benchmark path that still uses a real-input transform, but with less aggressive buffer reuse in the harness.RustFFT complexA baseline that converts the real signal into a full complex buffer and runs a standard complex FFT.FFTW3fAn optional single-precision real-to-complex FFTW path discovered from the local machine when thefftwfeature is enabled.KissFFTAn optional real FFT path from the vendored KISS FFT C implementation.PocketFFTAn optional vendored C++ header-only path accessed throughsrc/native/pocketfft_bridge.cc.
That means the benchmark is not just "algorithm A versus algorithm B". It is also measuring planning policy, data layout policy, and buffer-management style.
When --precision 64 is selected, the whole-file benchmark currently uses the CPU-side RealFFT and RustFFT double-precision paths plus the repo's reusable native real-input path. The optional PocketFFT, KissFFT, and FFTW3f comparisons remain single-precision-only in this codebase today.
BlitzFFT ships a head-only Homebrew formula in this repository. Until the project has tagged release tarballs, install the current main branch with:
brew install --HEAD ./Formula/blitzfft.rbFrom outside a clone, you can install directly from the public formula URL:
brew install --HEAD https://raw.githubusercontent.com/TheColby/BlitzFFT/main/Formula/blitzfft.rbAfter installation:
blitzfft --list-backends
blitzfft --generate-sine 440,48000,0.1 --summary -f noneThe Homebrew formula builds the default native Rust CPU configuration. Optional comparison backends such as FFTW, KISS FFT, PocketFFT, CUDA, and Metal remain Cargo feature builds for now.
# Native Rust CPU build
cargo build --release
# Enable the optional foreign-library whole-file comparisons
cargo build --release --features foreign-fft
# Apple GPU backend
cargo build --release --features metal
# NVIDIA GPU backend
cargo build --release --features cuda
# Everything enabled, including optional comparison backends
cargo build --release --features "cuda metal foreign-fft"- The default build is native Rust and does not require FFTW, KISS FFT, or PocketFFT.
--features foreign-fftenables the optional comparison backends used in the larger whole-file benchmark tables.FFTW3fis only needed when thefftwfeature is enabled.build.rstriespkg-config --libs --cflags fftw3ffirst, then checks/opt/homebrew/liband/usr/local/libforlibfftw3f.dylib.KissFFTis only compiled when thekissfftfeature is enabled.PocketFFTis only compiled when thepocketfftfeature is enabled through a small C++ bridge against the vendored header invendor/pocketfft/.- The Metal shader is built by
build.rswhen themetalfeature is enabled. - The CPU path always remains available.
- GitHub Actions now checks the default stable build on Linux and macOS, plus a Linux
foreign-fftjob with FFTW installed.
The project name and binary name are both blitzfft.
Usage: blitzfft [OPTIONS] [INPUT]
Arguments:
[INPUT] Input WAV file (16/24/32-bit PCM or f32)
Options:
--list-backends List backend choices and whether they are compiled into this build
--list-precisions List precision modes and their build/runtime requirements
--list-formats List output formats supported by this build
-n, --fft-size <FFT_SIZE> FFT frame size (must be a power of two) [default: 2048]
--hop <HOP> Hop size in samples (default = fft_size/2)
--batch-size <BATCH_SIZE> Number of frames to process per GPU batch (default = all frames) [default: 0]
-b, --backend <BACKEND> Force a specific backend [default: auto]
--channel <avg|left|right|N> Input channel selection [default: avg]
--window <WINDOW> Window applied to each analysis frame [default: hann] [possible values: rect, hann, hamming, blackman]
--precision <PRECISION> Internal processing precision in bits [default: 64] [possible values: 32, 64, 128]
-o, --output <PATH> Output file (optional; stdout if omitted for text/csv)
-f, --format <FORMAT> Output format [default: text]
--min-hz <MIN_HZ> Only emit bins at or above this frequency
--max-hz <MAX_HZ> Only emit bins at or below this frequency
--top-bins <TOP_BINS> Only emit the N loudest bins per frame (0 = all bins) [default: 0]
--benchmark Run framed benchmark comparing selected backend against CPU baseline
--bench-repeats <BENCH_REPEATS> Number of benchmark repeats [default: 5]
--whole-file-benchmark Run one exact FFT over the entire signal
--generate-sine <Hz,SR,Secs> Synthesize a sine wave in memory
--write-generated-wav <PATH> Persist the generated/loaded mono signal as 32-bit float WAV
--full-window <FULL_WINDOW> Apply a window across the entire loaded/generated signal before whole-file FFT
--apply-full-hann Apply a Hann window across the entire loaded/generated signal
--summary Print per-frame peak-frequency summary to stdout
- WAV input supports 16-bit PCM, 24-bit PCM, 32-bit PCM, and 32-bit float.
- Multi-channel input can be averaged to mono or a single channel can be selected with
--channel. - Framed output can be emitted as
text,csv,json,bin, ornone. --min-hzand--max-hzlimit emittedtext,csv, andjsonbins plus summary peaks to a frequency band.--windowcontrols framed analysis windows, while--full-windowapplies a whole-signal window before an exact whole-file FFT.--precision 64enables double-precision CPU processing for framed analysis and a reduced whole-file benchmark set.--precision 128enables an experimental truebinary128CPU path for framed analysis when the crate is built with--features binary128on nightly Rust.--precision 128currently does not support--whole-file-benchmark, and it does not use GPU backends.- Whole-file benchmark mode prints a comparison table and exits.
--list-backends,--list-precisions, and--list-formatswork without an input file and report what this build supports.
blitzfft --list-backends
blitzfft --list-precisions
blitzfft --list-formatspython3 scripts/blitzfft_parallel.py --generate-sine 440,48000,0.05 --summary --format noneThe Python script in scripts/blitzfft_parallel.py is a dependency-light framed-analysis reference path. It uses a pure-Python radix-2 FFT and prefers process parallelism, but it automatically falls back to a thread executor on restricted systems where process pools are unavailable.
cargo run --release -- input.wav --backend cpu --summary -f nonecargo run --release -- input.wav --channel left --min-hz 80 --max-hz 5000 --summary -f nonecargo run --release -- input.wav --precision 64 --backend cpu --summary -f nonecargo +nightly run --release --features binary128 -- input.wav --precision 128 --backend cpu --summary -f noneThe current 128-bit path is intentionally opt-in. It uses Rust's unstable f128 type behind the binary128 Cargo feature, with crate-local math helpers for trig and square root. The default build stays on stable Rust; if you pass --precision 128 without that feature, the CLI exits with a clear error.
cargo run --release -- input.wav --benchmark --bench-repeats 5 -f nonecargo run --release -- input.wav --full-window hann --whole-file-benchmark --bench-repeats 1 -f nonecargo run --release -- input.wav --precision 64 --backend cpu --full-window hann --whole-file-benchmark --bench-repeats 1 -f nonetarget/release/blitzfft \
--generate-sine 439.997,384000,3600 \
--apply-full-hann \
--write-generated-wav data/sine_439p997hz_60min_384khz_f32_hann.wav \
--whole-file-benchmark \
--bench-repeats 1 \
-f nonecargo run --release -- input.wav --fft-size 4096 --hop 1024 --format csv --output spectrum.csvcargo run --release -- input.wav --window blackman --min-hz 20 --max-hz 2000 --format json --output spectrum.jsonThis section matters because FFT benchmarks are easy to misread.
The whole-file table reports two times:
Setupplanner creation, config creation, allocation, or equivalent transform preparationExecone measured transform call, excluding WAV load time and excluding repeat-to-repeat input restoration performed by the benchmark harness
That distinction is important. A planner-heavy library can look slower in one-shot runs even if it is excellent when a plan is reused many times. Conversely, a lightweight library can look great in a single transform but lose ground when the workload changes shape or dimensionality.
The table is useful, but it is not a universal ranking of FFT libraries.
It does not mean:
- the fastest library here is always fastest for every size and workload
- the slowest library here is poorly engineered
- framed STFT workloads will behave the same way as one giant transform
- setup and execution costs will scale identically on every machine
It does show:
- how these exact implementations behave in this repository
- how much real-input optimization changes the picture
- how much data conversion overhead hurts the full complex baseline
- how sharp the frequency resolution becomes when
$N$ is very large
The benchmark asset is windowed across the entire one-hour signal before the exact whole-file FFT. That is unusual compared with frame-by-frame STFT work, but it makes the benchmarked signal handling explicit and reproducible.
The full-signal Hann is
applied for
Updated on 2026-04-01 for a mono 32-bit float, one-hour sine-wave scenario with:
- frequency:
439.997 Hz - duration:
3600 s - samples:
1,382,400,000 - frequency resolution:
- nearest-bin frequency to the target sine:
- window: full-signal Hann window applied before the FFT
- command: the reproduction command shown above
The corresponding WAV would be about 5.15 GiB on disk. In this workspace, the one-hour 384 kHz benchmark is treated as a simulation anchored to the measured 48 kHz run, because a direct six-library exact rerun at 1.3824 billion samples would exceed practical local memory and disk limits.
The table below therefore reports a simulated one-hour anchor. Exec is projected with the same Peak est. (Hz) is a quadratic sub-bin estimate around the loudest FFT bin and is printed to a conservative 12 decimal places to avoid implying more certainty than the f64 estimate can support. The simulated table below still shows the common nearest-bin center because this 384 kHz one-hour case is modeled rather than freshly rerun.
| Algorithm | Setup (s) | Exec (s) | Peak bin | Peak est. (Hz) |
|---|---|---|---|---|
| PocketFFT | simulated | 10.776736 | 1,583,989 | 439.996944444444 |
| RealFFT | simulated | 20.514858 | 1,583,989 | 439.996944444444 |
| BlitzFFT native | simulated | 22.263636 | 1,583,989 | 439.996944444444 |
| FFTW3f | simulated | 36.360388 | 1,583,989 | 439.996944444444 |
| RustFFT complex | simulated | 38.872592 | 1,583,989 | 439.996944444444 |
| KissFFT | simulated | 46.515731 | 1,583,989 | 439.996944444444 |
The same numbers are also recorded in benchmarks/60min_whole_file_fft.md.
On a measured shorter run, the extra resolution does expose tiny implementation differences. For example, with
cargo run --release -- --generate-sine 439.997,48000,10 --precision 32 --apply-full-hann --whole-file-benchmark --bench-repeats 1 -f nonethe interpolated peak estimates come out as:
| Algorithm | Peak bin | Peak est. (Hz) |
|---|---|---|
| BlitzFFT native | 4,400 | 439.997757311980877 |
| RealFFT | 4,400 | 439.997757311980877 |
| RustFFT complex | 4,400 | 439.997757318645654 |
| FFTW3f | 4,400 | 439.997757317381456 |
| KissFFT | 4,400 | 439.997757313537420 |
| PocketFFT | 4,400 | 439.997757311980877 |
The simulated table above gives one exact-size anchor point at
samples, or one hour of mono audio at
To visualize how the whole-file execution time should grow as the FFT gets longer, the graph below extrapolates each algorithm's simulated Exec time with an
This is an execution-time scaling estimate, not a fresh measured benchmark at every duration. The one-hour 384 kHz anchor is itself simulated from the measured 48 kHz benchmark by the same
To regenerate the chart after updating the anchor timings, run:
python3 scripts/generate_whole_fft_scaling_svg.pyAt the multi-day end of that estimate, the projected execution times at
| Duration | Samples | PocketFFT | RealFFT | BlitzFFT native | FFTW3f | RustFFT complex | KissFFT |
|---|---|---|---|---|---|---|---|
| 24 hr | 33,177,600,000 | 297.696 s | 566.701 s | 615.009 s | 1004.417 s | 1073.814 s | 1284.948 s |
| 48 hr | 66,355,200,000 | 612.428 s | 1165.832 s | 1265.213 s | 2066.312 s | 2209.077 s | 2643.427 s |
| 72 hr | 99,532,800,000 | 933.589 s | 1777.203 s | 1928.700 s | 3149.902 s | 3367.535 s | 4029.660 s |
BlitzFFT/
|- Formula/
| `- blitzfft.rb
|- data/
| `- generated benchmark WAVs (for example, a 384 kHz 60-minute sine asset; not checked in)
|- src/
| |- main.rs
| |- audio.rs
| |- benchmark.rs
| |- output.rs
| |- whole_fft.rs
| |- native/
| | `- pocketfft_bridge.cc
| `- backends/
| |- mod.rs
| |- cpu.rs
| |- cuda.rs
| `- metal.rs
|- vendor/
| |- kissfft/
| `- pocketfft/
`- shaders/
`- fft.metal
This section is intentionally broad and repo-focused. It is not literally the entire FFT literature, but it does collect the main sources relevant to the theory, libraries, and design choices that appear in BlitzFFT.
- J. W. Cooley and J. W. Tukey, "An algorithm for the machine calculation of complex Fourier series," Mathematics of Computation, 1965. DOI: 10.1090/S0025-5718-1965-0178586-1
- Matteo Frigo and Steven G. Johnson, "The Design and Implementation of FFTW3," Proceedings of the IEEE, 2005. PDF: fftw-paper-ieee.pdf
- FFTW manual, especially "What FFTW Really Computes": https://www.fftw.org/doc/
- FFTPACK at Netlib, important because PocketFFT is a heavily modified FFTPACK descendant: https://www.netlib.org/fftpack/
- Bluestein-style handling of difficult lengths is summarized in the PocketFFT README and commonly associated with the chirp z-transform family: https://en.wikipedia.org/wiki/Chirp_Z-transform
- F. J. Harris, "On the Use of Windows for Harmonic Analysis with the Discrete Fourier Transform," Proceedings of the IEEE, 1978. DOI: 10.1109/PROC.1978.10837
- Julius O. Smith III, online DSP references on spectral analysis and windows: https://ccrma.stanford.edu/~jos/
- FFTW home page: https://www.fftw.org/
- FFTW manual top page: https://www.fftw.org/doc/
- FFTW paper: https://www.fftw.org/fftw-paper-ieee.pdf
- Relevant manual topics for this repo:
- planner and wisdom
- real-data DFTs
- "What FFTW Really Computes"
- Upstream repository: https://github.com/mborgerding/kissfft
- Upstream README: https://github.com/mborgerding/kissfft/blob/master/README.md
- Vendored copy in this repo:
vendor/kissfft/README.md - SIMD notes:
vendor/kissfft/README.simd
Why it matters here: KISS FFT is a good contrast case against FFTW. It aims to stay simple and reasonably efficient rather than planner-heavy and maximally optimized for every machine.
- Original upstream noted by the GitHub mirror: https://gitlab.mpcdf.mpg.de/mtr/pocketfft
- GitHub mirror used as a convenient public reference: https://github.com/mreineck/pocketfft
- Vendored README in this repo:
vendor/pocketfft/README.md - PocketFFT README highlights relevant to this repo:
- real-to-complex and complex-to-real transforms
- mixed support for awkward sizes
- Bluestein handling for large prime factors
- plan caching for repeated sizes
Why it matters here: PocketFFT is a strong exact-whole-signal comparison point because it is compact, modern C++, and explicitly optimized for more than just textbook radix-2 cases.
rustfftcrate docs: https://docs.rs/crate/rustfft/latestrealfftcrate docs: https://docs.rs/crate/realfft/latestrealfftcrate summary: it wrapsrustfftto expose real-to-complex and complex-to-real transforms directly, avoiding manual real-to-complex expansion in user code
Why they matter here:
- the framed CPU backend uses the native BlitzFFT engine
- the whole-file benchmark compares
RealFFTandRustFFT complexdirectly - the repo's native whole-file path is mostly about planner and buffer reuse around a real-input transform, not about inventing a brand-new Fourier algorithm
src/audio.rsWAV loading, mono downmixing, Hann window generation, and framingsrc/backends/cpu.rsnative BlitzFFT CPU execution paths plus reusable work bufferssrc/whole_fft.rsexact whole-signal benchmark harness and per-library bridge codesrc/native/pocketfft_bridge.ccRust-to-PocketFFT bridgebuild.rsoptional foreign-backend compilation/linking plus Metal shader compilation
MIT