Skip to content

Repository files navigation

Fluxion

Differentiable, cross-vendor, framework-agnostic audio DSP — a functional graph API, a modern SoX-substitute CLI, and a hard-real-time engine. Written in Rust, bound to anything.

CI MSRV 1.85 License: MIT

Fluxion owns the DSP-specific pieces that determine correctness and deployability — closed-form filter design, forward kernels, analytic adjoints for exact gradients, stability certificates — and rents the general-purpose infrastructure that ML systems already provide: the autodiff tape, portable GPU code generation, array interchange. One graph algebra lowers to two engines that never mix their rules: a batched differentiable training engine (CPU SIMD / CUDA) and an allocation-free, lock-free real-time engine. A certified freeze boundary connects them: a trained graph becomes a versioned .fxg artifact that an edge device re-certifies before playback and can hot-swap live with a click-free crossfade.

This repository is the software companion to the paper Fluxion: A Differentiable DSP Runtime from Training GPU to Real-Time Edge (IS² 2026) — see Citation and Reproducing the paper.

Status: pre-1.0 (0.x). The core is implemented and tested: the graph algebra, the DSP op set with hand-derived analytic gradients, Burn-based whole-graph autodiff, CPU SIMD + CUDA batch kernels, the allocation-free realtime engine, audio IO, the CLI, and the Python package. The public API and the .fxg on-disk format may still change before 1.0.

Roadmap: the host-engine push — WebAssembly first, then streaming resampling, the mastering set, sidechain routing, analysis taps, the timeline helpers, and one simple API across CLI, Python, C and the browser — is planned task-by-task, with dependencies and verifiable milestones, in ROADMAP.md.

One codebase, five ways in:

  • Rust library — fluxion on crates.io: compose effects with | (series) and + (parallel), run them batched on CPU/GPU, differentiate them, or freeze them for realtime.
  • CLI — fluxion, a SoX substitute with named effects and long flags (not SoX's interface, most of SoX's jobs).
  • Python — fluxion on PyPI: a torchfx-style API (Wave, effect classes, | and +), zero-copy DLPack interop, torch/JAX autograd adapters, batched data augmentation.
  • C ABI — a small panic-safe surface (fluxion.h) for C/C++/Swift consumers.
  • Browser — npm install fluxion: the same engine as WebAssembly, offline render today, AudioWorklet playback next. Its output is checked against the native library in CI.

All of them build the same graph and share one text form for it — "highpass(80, 4) | gain(-3dB)" means the same thing everywhere. See docs/interfaces.md for the contract between them and docs/ops.md for every op's name on every interface.

Install

# Rust library
cargo add fluxion                 # batch/CPU core (pure Rust, builds offline)
cargo add fluxion -F autodiff     # + whole-graph differentiation through Burn
cargo add fluxion -F realtime     # + the realtime engine re-exports

# CLI
cargo install fluxion-cli                          # file processing, no audio-device deps
cargo install fluxion-cli --features realtime      # + play/record via CPAL

# Python (wheels: Linux x86_64/aarch64, macOS Intel/AS, Windows; numpy is the only hard dep)
pip install fluxion               # extras: [torch] [jax] [interop] (safetensors) [dataset] (parquet)

Dependencies are deliberately thin: the default build is pure Rust and compiles offline; heavy optional stacks (Burn/CubeCL for autodiff/GPU, CPAL for audio devices, PyO3 for Python) only enter behind the feature flags that need them.

The graph in 30 seconds

use fluxion::prelude::*;

// `|` = series, `+` = parallel (outputs summed) — the same algebra everywhere.
let chain = (lowpass(800.0, 2) + highpass(4000.0, 2)) | compand(0.01, 0.1, -20.0, 4.0, 6.0, 0.0) | gain(0.5);

// The same chain, written in the text syntax the CLI, Python, C and JS all share.
let same: Graph = "(lowpass(800, 2) + highpass(4000, 2)) | compand(0.01, 0.1, -20, 4, 6, 0) | gain(0.5)".parse()?;
assert_eq!(same, chain);

let wet = process(&chain, &signal);              // batch: any channels, allocates freely

The same graph lowers to three executors, never mixing their rules:

// Differentiable (feature `autodiff`): loss.backward() flows through the whole chain —
// every op owns its analytic VJP; Burn provides the tape. Train coefficients or design
// parameters ("learn a cutoff") with stability guaranteed by construction.
let out = fluxion::diff_process::<B>(&chain, x, fs);

// Realtime (feature `realtime`): freeze designed coefficients, then run alloc-free,
// lock-free, bounded-time blocks in the audio callback — no autograd, no GPU, no locks.
let mut rt = fluxion::to_rt_graph(&chain, fs).expect("realtime-lowerable");
rt.prepare(128);
rt.process(&mut block);

Geometry operations that change length, channel count, or sample rate (trim, pad, resample, remix, …) are deliberately not graph ops — they live in fluxion::transform and run between graph passes.

CLI — a SoX substitute

Same philosophy, modern interface: named effects, long --flags, explicit units (Hz, seconds, dB), SI suffixes (--cutoff 1k), and a self-describing catalog (fluxion effects).

# Filter a file: adjacent effects fuse into one pass.
fluxion in.wav lowpass --cutoff 1k gain --db -6 out.wav

# The bread-and-butter SoX jobs: trim, resample, bit-depth conversion (TPDF-dithered).
fluxion --bits 16 in.flac trim --start 0.25 --len 30 rate --fs 44100 out.wav

# Multiple inputs: concatenate by default, sum with --mix.
fluxion --mix vocals.wav backing.wav mixed.wav

# Generate, analyze, inspect.
fluxion synth --wave sine --freq 440 --secs 1 fade --fadein 0.1 tone.wav
fluxion stat in.wav            # min/max, peak & RMS dBFS, DC offset, crest factor
fluxion info in.mp3            # metadata for WAV/FLAC/MP3/OGG/… (alias: soxi)

# Unix filter, null sink, dataset batch.
fluxion - reverse - < in.wav > out.wav
fluxion batch out/ 'data/*.wav' highpass --cutoff 80

# Freeze a chain to a portable artifact (stability-certified), play it live.
fluxion compile lowpass --cutoff 800 echo --time 0.3 chain.fxg
fluxion play in.wav chain.fxg          # --features realtime
fluxion record --secs 5 take.wav       # --features realtime

# Import a DDSP checkpoint trained elsewhere (FLAMO / torchfx), certified on the way in.
fluxion import ckpt.safetensors model.fxg

Effects (graph ops): gain, lowpass/highpass (Butterworth, any order), cheby1_lowpass/cheby1_highpass/cheby2_lowpass/cheby2_highpass, RBJ peaking/lowshelf/highshelf/ notch/bandpass/allpass, raw biquad, fir --taps …, normalize, delay, echo, reverb, fade, tremolo, overdrive, compand, reverse, chorus, flanger, phaser. Geometry stages: trim, pad, rate, speed, repeat, silence, channels, remix. Run fluxion effects [name] for every parameter, unit, and default.

Not ported from SoX (yet or ever): tempo/pitch (time-stretch), spectrogram (imaging dependency), noise reduction, and legacy niches (oops, riaa, earwax).

Python

import fluxion as fx
from fluxion import Compose, RandomChain

# Wave carries fs, so it never appears in the chain. `|` is series, `+` is parallel.
wave = fx.Wave.from_file("in.wav")
(wave | fx.filter.Highpass(80, order=4) | fx.effect.Gain(fx.db(-3))).save("out.wav")

chain = fx.filter.Lowpass(8000) | fx.effect.Gain(0.5)  # same algebra as Rust and the CLI
chain = fx.chain("lowpass(8000) | gain(0.5)")          # ...or the shared text form
y  = chain(x, fs=48_000)                               # (T,) or (C, T); numpy/torch/jax via DLPack
ys = chain.process_batch(batch, fs=48_000)             # (B, T) batched

# Data augmentation: stochastic chains, seeded.
aug = Compose([RandomChain(fx.filter.Lowpass, cutoff=(2_000, 16_000), p=0.8)])
x_aug = aug(x, fs=48_000)

# Dataset IO (extra: fluxion[dataset]) — Parquet, same schema as the Rust side, streamed.
from fluxion.dataset import iter_parquet, write_parquet
write_parquet("out.parquet",                                   # augment a whole dataset, lazily
              ((aug(x, fs), fs) for x, fs in iter_parquet("in.parquet")))

# Training: coefficients as nn.Parameter, analytic backward under torch autograd.
from fluxion.torch import SosModule
mod = SosModule.from_chain(chain, fs=48_000)

# Import a FLAMO-trained SISO biquad cascade (extra: fluxion[interop]).
coeffs = fx.interop.load_flamo_sos("checkpoint.safetensors")

Workspace

Crate Role
fluxion (crates/fluxion-facade) Facade + prelude; the crate users depend on (features: autodiff, realtime)
fluxion-core Graph algebra + IR (| series, + parallel, ~ feedback), typed op catalog, versioned .fxg
fluxion-ops DSP kernels + analytic VJPs, coefficient design, geometry transforms (SciPy golden-vector oracle tests)
fluxion-backend CPU executor (SIMD batch path), graph lowering, stability certification, CUDA kernels (feature cuda)
fluxion-autodiff Burn Autodiff integration: whole-graph diff_process, trainable coeffs/design params (feature-gated)
fluxion-rt Realtime engine: lock-free SPSC ring, alloc-free executor (alloc-asserted tests), CPAL backend (feature cpal)
fluxion-io WAV read/write (16/24/32-bit, TPDF dither), Symphonia decode/probe (FLAC/MP3/OGG/AAC/…), bounded-memory streaming, Arrow/Parquet dataset IO (feature parquet), FLAMO/torchfx DDSP checkpoint import (feature checkpoint)
fluxion-cli The fluxion binary (feature realtime for play/record)
fluxion-py PyO3/maturin package fluxion (abi3, numpy-only hard dep; extras: torch/jax/interop/dataset)
fluxion-ffi C ABI (include/fluxion.h, cbindgen), panic-safe; publish = false
fluxion-wasm wasm-bindgen browser bindings: chain from text + offline render (CPU). AudioWorklet and WebGPU deferred; publish = false

GPU status: CUDA forward + backward kernels are implemented and validated on NVIDIA hardware; Apple Metal / AMD ROCm validation via CubeCL is pending, so GPU stays behind the cuda feature.

Reproducing the paper

The measurement harnesses used in the paper's evaluation ship as ordinary cargo examples (release builds; each prints JSON lines or a summary):

Experiment Command
CPU batch throughput cargo run --release -p fluxion-backend --example paper_bench
Training-step throughput (analytic adjoint) cargo run --release -p fluxion-autodiff --example train_step_bench --features burn
EQ inversion: train → certify → freeze → play cargo run --release -p fluxion-autodiff --example eq_inversion --features burn
Real-time callback latency CCDF cargo run --release -p fluxion-rt --example latency_ccdf
1024-tap FIR per-block latency cargo run --release -p fluxion-rt --example fir_latency
Certified live hot-swap loop cargo run --release -p fluxion-backend --example hotswap_demo
Multichannel per-channel-chain case study cargo run --release -p fluxion-backend --example soundlamp_demo

Two didactic examples show the differentiable path in miniature: learn_cutoff (-p fluxion-autodiff --features burn) trains a low-pass cutoff by gradient descent, and fit_filter (-p fluxion-ops) fits biquad coefficients with the analytic gradient alone.

Develop

cargo build && cargo test        # whole workspace (offline, pure Rust)
cargo clippy --all-targets       # lints are CI-gated (-D warnings), incl. missing_docs
cargo bench -p fluxion-ops       # Criterion benchmarks
cargo run -p fluxion-cli -- effects
cd crates/fluxion-py && maturin develop && pytest tests/   # Python package

See CONTRIBUTING.md for the conventions CI enforces.

License

MIT. No GPL code from the reference projects (SoX, AudioNoise) — ideas and math only.

About

Differentiable framework-agnostic audio DSP with a functional graph API, a modern CLI, and a hard-real-time engine

Topics

Resources

Contributing

Stars

2 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages