https://github.com/cucuwritescode/adac
Automatic Differentiable Audio Compilation: differentiable audio graphs to real-time DSP
https://github.com/cucuwritescode/adac
audio-processing compilers ddsp
Last synced: 1 day ago
JSON representation
Automatic Differentiable Audio Compilation: differentiable audio graphs to real-time DSP
- Host: GitHub
- URL: https://github.com/cucuwritescode/adac
- Owner: cucuwritescode
- License: mit
- Created: 2026-03-20T15:34:05.000Z (3 months ago)
- Default Branch: main
- Last Pushed: 2026-06-14T12:31:12.000Z (2 days ago)
- Last Synced: 2026-06-14T16:22:00.871Z (2 days ago)
- Topics: audio-processing, compilers, ddsp
- Language: Python
- Homepage:
- Size: 553 KB
- Stars: 4
- Watchers: 0
- Forks: 1
- Open Issues: 12
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# ADAC
**Automatic Differentiable Audio Compilation**
[](https://www.python.org/downloads/)
[](LICENSE)
[](#testing)

*from differentiable audio research to efficient real-time DSP*
---
## the problem
researchers design and optimise audio processors in differentiable frameworks such as [FLAMO](https://github.com/gdalsanto/flamo), but deploying them as real-time plugins requires manual reimplementation. this is error-prone and creates a gap between research prototypes and usable tools. the worked example throughout is the feedback delay network (FDN), which exercises every part of the compiler.
```
before: FLAMO model (PyTorch) → ??? → real-time plugin
↑
manual rewrite
after: FLAMO model (PyTorch) → adac → FAUST → plugin
```
## how it works
```
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│ FLAMO │ ───▶ │ JSON │ ───▶ │ FAUST │
│ model │ ◀─── │ config │ │ code │
│ (PyTorch) │ │ │ │ (.dsp) │
└──────────────┘ └──────────────┘ └──────────────┘
flamo_to_json() ──▶ json_to_faust() ──▶
json_to_flamo() ◀──
╰───────────── flamo_to_faust() ──────────────╯
```
the pipeline traverses a FLAMO model graph, extracts all parameters (delays, gains, matrices, filters), serialises them to a JSON intermediate representation, and generates valid FAUST DSP code. extraction is map-aware: matrix types with non-identity maps (orthogonal, hadamard, householder) serialise the effective matrix the model applies, with the raw trainable weights preserved for round-tripping. `json_to_flamo` reconstructs the original model from the config.
on top of the codegen core:
- `HotReload` republishes the model to a running FAUST plugin during training, so you hear the optimisation while it runs
- macro-controls (`rt60`, `dry_wet`, `pre_delay`) add performance knobs to the generated plugin without touching the trained parameters
- `certify` computes a stability certificate for every feedback loop, written as `.cert.json` next to the `.dsp`
- `export_juce` turns a config into an installed VST3/AU plugin in one call
## installation
```bash
pip install -e .
```
for full FLAMO model support (requires PyTorch):
```bash
pip install -e ".[full]"
```
building plugins additionally requires the [FAUST](https://faust.grame.fr) distribution and [JUCE](https://juce.com).
## quick start
```python
import adac
#given a trained FLAMO model and sample rate
faust_code = adac.flamo_to_faust(model, fs=48000, name="MyReverb")
#write to file
with open("reverb.dsp", "w") as f:
f.write(faust_code)
```
or use the two-step pipeline for inspection:
```python
config = adac.flamo_to_json(model, fs=48000, name="MyReverb")
faust_code = adac.json_to_faust(config, controls={"rt60": True, "dry_wet": True})
```
### hear it while it trains
```python
live = adac.HotReload(fs=48000, name="MyReverb", controls={"rt60": True})
for step in range(n_steps):
loss = criterion(model(x), target)
loss.backward()
optimiser.step()
live.update(model)
live.update(model, force=True)
```
the hot-reload CLAP plugin (FAUST interpreter plus file watcher) lives in `faust/architecture/clap/`. reloads take about 100 ms and knob positions survive them. full script: `examples/live_training.py`.
### ship it
```python
adac.export_juce(
adac.flamo_to_json(model, fs=48000, name="MyReverb"),
"exported/", name="MyReverb",
controls={"rt60": True, "dry_wet": True, "pre_delay": True},
juce_modules="~/JUCE/modules",
build=True,
)
```
one call: FAUST generation, stability certificate, JUCE project, release build, install into the user plugin folders (macOS). the export refuses to build a model whose certificate says `unstable` or `not-certified`; pass `strict=False` to override. full script: `examples/export_plugin.py`.
### certify
```python
cert = adac.certify(config)
print(cert["verdict"])
```
the criterion is small-gain: the product of per-element spectral norms around each feedback loop must stay below one at every frequency, evaluated on the parameter values as emitted (single precision). verdicts are `certified-stable`, `marginally-stable`, `indeterminate`, `not-certified`, `unstable`. a lossless prototype is marginally stable; with the `rt60` control it is certified at any knob position.
## equivalence
generated FAUST matches FLAMO sample-exactly, direct paths included. the energy decay of the compiled plugin follows the FLAMO reference throughout, and the underlying impulse responses agree to within single-precision arithmetic noise. all four stereo paths match identically; the suite pins them.
the rt60 macro-control on the compiled plugin, measured by Schroeder integration, follows the ideal decay for the slider value:
regenerate the figures with `python examples/make_plots.py`.
## supported modules
| FLAMO module | FAUST output | description |
|---|---|---|
| `parallelDelay` | `@(n)` / `de.fdelay` | integer or fractional sample delays |
| `Gain` / `Matrix` | sum-of-products function | mixing matrices (hoisted, map-aware) |
| `HouseholderMatrix` | sum-of-products function | emitted as the effective matrix |
| `parallelGain` | `*(g)` | per-channel diagonal gains |
| `parallelSOSFilter` | `fi.tf2(...)` | cascaded biquad filters |
| `Series` | `:` | sequential composition |
| `Parallel` | `,` / `:>` | side-by-side or summing |
| `Recursion` | `~` | feedback loops (FDN core) |
| `Biquad` / `SVF` | `fi.tf2` / `fi.svf.*` | single-channel filters |
| `Shell` | *(unwrapped)* | FFT wrapper skipped |
## testing
```bash
#unit tests (no external dependencies)
pytest tests/ -q --ignore=tests/integration
#integration tests (requires flamo venv + faust compiler)
pytest tests/integration/ -v
```
200 unit tests validate the full pipeline: map-aware parameter extraction, delay quantisation, SOS normalisation, gain classification, graph traversal, code generation, macro-control wiring, multichannel arities, hot-reload publishing, certificate verdicts, and export orchestration.
integration tests compare impulse responses between FLAMO (frequency domain) and generated FAUST (time domain) sample-by-sample.
## project structure
```
src/adac/
codegen/
flamo_to_json.py parameter extraction and graph traversal
json_to_faust.py FAUST code generation and macro-controls
json_to_flamo.py model reconstruction from JSON config
flamo_to_faust.py convenience wrapper (both steps)
hotreload.py training-time live publishing
certificate.py small-gain stability certificate
export.py JUCE plugin export
examples/
live_training.py
export_plugin.py
make_plots.py
tests/
test_flamo_to_json.py
test_json_to_faust.py
test_flamo_to_faust.py
test_param_extraction.py
test_hotreload.py
test_certificate.py
test_export.py
integration/
test_ir_comparison.py
generate_flamo_ir.py
```
## related projects
- [FLAMO](https://github.com/gdalsanto/flamo) — differentiable audio processing framework
- [pyFDN](https://github.com/artificial-audio/pyFDN) — python feedback delay networks
- [FAUST](https://faust.grame.fr/) — functional audio stream
## licence
MIT — see [LICENSE](LICENSE) for details.