Metadata-Version: 2.4
Name: gen-dsp
Version: 0.1.19
Summary: Generate multiple dsp plugin formats from Max gen~ exports
Keywords: puredata,pd,max,msp,gen~,dsp,audio,external
Author: Michael Spears, Shakeeb Alireza
Author-email: Shakeeb Alireza <shakfu@users.noreply.github.com>
License-Expression: MIT
License-File: LICENSE
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Other Audience
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Software Development :: Code Generators
Requires-Dist: pydantic>=2.0 ; extra == 'graph'
Requires-Dist: pydantic>=2.0 ; extra == 'sim'
Requires-Dist: numpy>=1.24 ; extra == 'sim'
Requires-Python: >=3.10
Project-URL: Homepage, https://github.com/shakfu/gen-dsp
Project-URL: Repository, https://github.com/shakfu/gen-dsp
Project-URL: Changelog, https://github.com/shakfu/gen-dsp/blob/master/CHANGELOG.md
Project-URL: Issues, https://github.com/shakfu/gen-dsp/issues
Provides-Extra: graph
Provides-Extra: sim
Description-Content-Type: text/markdown

# gen-dsp

**[Documentation](https://shakfu.github.io/gen-dsp/)** | **[API Reference](https://shakfu.github.io/gen-dsp/api/)** | **[Changelog](https://github.com/shakfu/gen-dsp/blob/master/CHANGELOG.md)**

gen-dsp is a zero-dependency pure Python package that generates buildable audio plugin projects from Max/MSP gen~ code exports, targeting 15 platforms: PureData, Max/MSP, ChucK, AudioUnit (AUv2), AUv3, CLAP, VST3, LV2, SuperCollider, VCV Rack, Daisy, Circle, Web Audio (WASM), Standalone (miniaudio), and Csound. It handles project scaffolding, I/O and buffer detection, parameter metadata extraction, and platform-specific patching.

gen-dsp also includes an optional **graph** frontend (`pip install gen-dsp[graph]`) that provides a way to test gen-dsp's platform backends without needing to create and export gen~ patches. It defines DSP graphs in Python, JSON, or the purpose-built **GDSP DSL** (`.gdsp` files) and compiles them to the same plugin targets. While not intended to replace gen~, it may evolve into a useful frontend in its own right. The companion [dsp-graph](https://github.com/shakfu/dsp-graph) project provides a web-based visual graph editor and debugger (React + FastAPI) built on top of gen-dsp's `graph` backend.

This project is a friendly fork of Michael Spears' [gen_ext](https://github.com/samesimilar/gen_ext) which was originally created to "compile code exported from a Max gen~ object into an "external" object that can be loaded into a PureData patch."

## Cross-Platform Support

gen-dsp builds on macOS, Linux, and Windows. All platforms are tested in CI via GitHub Actions.

| Platform | macOS | Linux | Windows | Build System | Output |
|----------|:-----:|:-----:|:-------:|--------------|--------|
| PureData | yes | yes | -- | make (pd-lib-builder) | `.pd_darwin` / `.pd_linux` |
| Max/MSP | yes | -- | -- | CMake (max-sdk-base) | `.mxo` / `.mxe64` |
| ChucK | yes | yes | -- | make | `.chug` |
| AudioUnit (AUv2) | yes | -- | -- | CMake | `.component` |
| AUv3 | yes | -- | -- | CMake (Xcode) | `.app` + `.appex` |
| CLAP | yes | yes | yes | CMake (FetchContent) | `.clap` |
| VST3 | yes | yes | yes | CMake (FetchContent) | `.vst3` |
| LV2 | yes | yes | -- | CMake (FetchContent) | `.lv2` |
| SuperCollider | yes | yes | yes | CMake (FetchContent) | `.scx` / `.so` |
| VCV Rack | yes | yes | -- | make (Rack SDK) | `plugin.dylib` / `.so` / `.dll` |
| Daisy | -- | yes | -- | make (libDaisy) | `.bin` (firmware) |
| Circle | -- | yes | -- | make (Circle SDK) | `.img` (kernel image) |
| Web Audio | yes | yes | yes | make (Emscripten) | `.wasm` + `processor.js` |
| Standalone | yes | yes | yes | make (miniaudio) | native executable |
| Csound | yes | yes | -- | make | `.dylib` / `.so` opcode |

Each platform has a detailed guide covering prerequisites, build details, SDK configuration, install paths, and troubleshooting:

| Platform | Guide |
|----------|-------|
| PureData | [docs/backends/puredata.md](docs/backends/puredata.md) |
| Max/MSP | [docs/backends/max.md](docs/backends/max.md) |
| ChucK | [docs/backends/chuck.md](docs/backends/chuck.md) |
| AudioUnit (AUv2) | [docs/backends/audiounit.md](docs/backends/audiounit.md) |
| AUv3 | [docs/backends/auv3.md](docs/backends/auv3.md) |
| CLAP | [docs/backends/clap.md](docs/backends/clap.md) |
| VST3 | [docs/backends/vst3.md](docs/backends/vst3.md) |
| LV2 | [docs/backends/lv2.md](docs/backends/lv2.md) |
| SuperCollider | [docs/backends/supercollider.md](docs/backends/supercollider.md) |
| VCV Rack | [docs/backends/vcvrack.md](docs/backends/vcvrack.md) |
| Daisy | [docs/backends/daisy.md](docs/backends/daisy.md) |
| Circle | [docs/backends/circle.md](docs/backends/circle.md) |
| Web Audio | [docs/backends/webaudio.md](docs/backends/webaudio.md) |
| Standalone | [docs/backends/standalone.md](docs/backends/standalone.md) |
| Csound | [docs/backends/csound.md](docs/backends/csound.md) |

## Key Improvements and Features

- **Python package**: gen-dsp is a pip installable zero-dependency python package with a cli which embeds all templates and related code.

- **Automated project scaffolding**: `gen-dsp <source> -p <platform>` creates a complete, buildable project from a gen~ export or graph file in one command, versus manually copying files and editing Makefiles.

- **Automatic buffer detection**: Scans exported code for buffer usage patterns and configures them without manual intervention.

- **Max/MSP support**: Generates CMake-based Max externals with proper 64-bit signal handling and buffer lock/unlock API.

- **ChucK support**: Generates chugins (.chug) with multi-channel I/O and runtime parameter control.

- **AudioUnit support**: Generates macOS AUv2 plugins (.component) using the raw C API -- no Apple SDK dependency, just system frameworks.

- **CLAP support**: Generates cross-platform CLAP plugins (`.clap`) with zero-copy audio processing -- CLAP headers fetched via CMake FetchContent.

- **VST3 support**: Generates cross-platform VST3 plugins (`.vst3`) with zero-copy audio processing -- Steinberg VST3 SDK fetched via CMake FetchContent.

- **LV2 support**: Generates cross-platform LV2 plugins (`.lv2` bundles) with TTL metadata containing real parameter names/ranges parsed from gen~ exports -- LV2 headers fetched via CMake FetchContent.

- **SuperCollider support**: Generates cross-platform SC UGens (`.scx`/`.so`) with `.sc` class files containing parameter names/defaults parsed from gen~ exports -- SC plugin headers fetched via CMake FetchContent.

- **VCV Rack support**: Generates VCV Rack modules with per-sample processing, auto-generated `plugin.json` manifest and panel SVG -- Rack SDK auto-downloaded and cached on first build.

- **Daisy support**: Generates Daisy Seed firmware (.bin) with custom genlib runtime (bump allocator for SRAM/SDRAM) -- first embedded/cross-compilation target, requires `arm-none-eabi-gcc`.

- **Circle support**: Generates bare-metal Raspberry Pi kernel images (.img) for Pi Zero through Pi 5 using the [Circle](https://github.com/rsta2/circle) framework -- 14 board variants covering I2S, PWM, HDMI, and USB audio outputs. Supports multi-plugin mode via `--graph` with USB MIDI CC parameter control: linear chains use an optimized ping-pong buffer codegen path, while arbitrary DAGs (fan-out, fan-in via mixer nodes) use topological sort with per-edge buffer allocation.

- **Web Audio support**: Generates browser-ready AudioWorklet + WASM modules from gen~ exports or graph sources. Emscripten compiles C++ to WebAssembly; the generated `processor.js` runs DSP in a real-time audio thread via `AudioWorkletProcessor`. Includes a demo page (`index.html`) with parameter sliders and a `make serve` target for local testing.

- **Standalone support**: Generates self-contained CLI audio applications using [miniaudio](https://miniaud.io/) (public domain, single-header, zero-dependency). Processes real-time audio from system default I/O devices with CLI parameter control. Cross-platform: macOS, Linux, Windows.

- **AUv3 support**: Generates macOS AUv3 plugins as App Extensions (`.appex` inside `.app`) using `cmake -G Xcode`. Implements `AUAudioUnit` subclass with `AUParameterTree` and realtime-safe `internalRenderBlock`.

- **Csound support**: Generates Csound opcode plugins via the `csdl.h` C API. Audio inputs map to a-rate args, parameters to k-rate args. Handles float-to-MYFLT conversion with sample-accurate timing.

- **Platform-specific patches**: Automatically fixes compatibility issues like the `exp2f -> exp2` problem in Max 9 exports on macOS.

- **Analysis tools**: `gen-dsp detect` inspects exports to show I/O counts, parameters, and buffers before committing to a build.

- **Dry-run mode**: Preview what changes will be made before applying them.

- **Platform registry**: To make it easy to discover new backends

- **graph frontend** (optional): Define DSP signal-processing graphs in the [GDSP DSL](docs/graph/dsl.md), Python, or JSON using 54 built-in node types (oscillators, filters, delays, buffers, math ops, etc.), then compile to C++ and generate buildable plugin projects. Primary purpose is to enable testing gen-dsp's platform backends without gen~ exports. Includes graph validation, optimization (dead-code elimination, constant folding), FAUST-style algebra combinators (`series`, `parallel`, `split`, `merge`), Graphviz visualization, and numpy-based simulation. Covers ~89% of gen~ operators.

## Installation

```bash
pip install gen-dsp
```

With graph frontend support (requires pydantic):

```bash
pip install gen-dsp[graph]
```

With graph simulation (requires pydantic + numpy):

```bash
pip install gen-dsp[sim]
```

Or install from source:

```bash
git clone https://github.com/shakfu/gen-dsp.git
cd gen-dsp
pip install -e .          # core only
pip install -e ".[graph]" # with graph frontend
```

## Quick Start

```bash
# 1. Export your gen~ code in Max (send 'exportcode' to gen~ object)

# 2. Create and build a PureData external
gen-dsp ./path/to/export -p pd

# 3. Use in PureData as myeffect~
```

## Commands

### Default command

Generate a plugin project (and build it) from a source:

```bash
gen-dsp <source> -p <platform> [-n <name>] [-o <output>] [--no-build]
```

The source type is auto-detected:

- **Directory** -- treated as a gen~ export
- **`.gdsp` file** -- parsed as GDSP DSL (requires `gen-dsp[graph]`)
- **`.json` file** -- parsed as graph JSON (requires `gen-dsp[graph]`)

Options:

- `-p, --platform` - Target platform (required): `pd`, `max`, `chuck`, `au`, `auv3`, `clap`, `vst3`, `lv2`, `sc`, `vcvrack`, `daisy`, `circle`, `webaudio`, `standalone`, `csound`
- `-n, --name` - Name for the plugin (default: inferred from source)
- `-o, --output` - Output directory (default: `./<name>_<platform>`)
- `--no-build` - Skip building after project creation
- `--buffers` - Explicit buffer names (overrides auto-detection)
- `--no-shared-cache` - Disable shared OS cache for FetchContent downloads (clap, vst3, lv2, sc; shared cache is enabled by default)
- `--board` - Board variant for embedded platforms (Daisy: `seed`, `pod`, etc.; Circle: `pi3-i2s`, `pi4-usb`, etc.)
- `--no-patch` - Skip automatic exp2f fix
- `--no-midi` - Disable MIDI note handling
- `--midi-gate NAME` - MIDI gate parameter name
- `--midi-freq NAME` - MIDI frequency parameter name
- `--midi-vel NAME` - MIDI velocity parameter name
- `--midi-freq-unit {hz,midi}` - Unit for MIDI frequency parameter
- `--voices N` - Polyphony voices (default: 1)
- `--inputs-as-params [NAME ...]` - Remap signal inputs to parameters. No names = remap all; with names = remap only those inputs (see [docs/inputs_as_params.md](docs/inputs_as_params.md))
- `--dry-run` - Preview without creating files

### build

Build an existing project:

```bash
gen-dsp build [project-path] [-p <platform>] [--clean] [-v]
```

### manifest

Emit a JSON manifest describing a gen~ export (I/O counts, parameters with ranges, buffers):

```bash
gen-dsp manifest <export-path> [--buffers sample envelope]
```

The same manifest is also written as `manifest.json` to the project root during project generation.

### detect

Analyze a gen~ export:

```bash
gen-dsp detect <export-path> [--json]
```

Shows: export name, signal I/O counts, parameters, detected buffers, and needed patches.

### patch

Apply platform-specific fixes:

```bash
gen-dsp patch <target-path> [--dry-run]
```

Currently applies the `exp2f -> exp2` fix for macOS compatibility with Max 9 exports.

### list

List available platforms:

```bash
gen-dsp list
```

### cache

Show cached SDKs:

```bash
gen-dsp cache
```

### Graph subcommands (requires `gen-dsp[graph]`)

Work with DSP signal graphs defined as `.gdsp` or JSON. These are top-level subcommands:

```bash
gen-dsp compile <file> [-o DIR] [--optimize]
gen-dsp validate <file>
gen-dsp dot <file> [-o DIR]
gen-dsp sim <file> [-i INPUT] [-o DIR] [-n SAMPLES] [--param K=V]
```

All subcommands accept both `.gdsp` and `.json` files (auto-detected by extension).

To generate a platform project from a graph, use the default command: `gen-dsp my.gdsp -p clap`.

## Graph Frontend: Define DSP Graphs in GDSP/Python/JSON

The optional graph subpackage (`pip install gen-dsp[graph]`) provides a way to test gen-dsp's platform backends without needing to create and export gen~ patches. It defines DSP graphs in the GDSP DSL, Python, or JSON and compiles them to C++, generating buildable plugin projects through the same platform backends. While not intended to replace gen~, it may evolve into a useful frontend in its own right. For a visual editing experience, see the companion [dsp-graph](https://github.com/shakfu/dsp-graph) project -- a web-based graph editor and debugger built on top of this subpackage.

### Quick Start (GDSP)

The GDSP DSL is a concise, line-oriented language for defining DSP graphs. It borrows idioms from Gen~ codebox, Faust, and SuperCollider. See the [full specification](docs/graph/dsl.md) for details.

```gdsp
graph fbdelay (sr=48000) {
    in input
    out output = wet_mix

    param time     1..2000  = 500    # delay time in ms
    param feedback 0..0.99  = 0.6
    param tone     0..1     = 0.3    # lowpass on feedback
    param mix      0..1     = 0.5

    delay dly 96000
    history fb_state = 0.0

    time_samps  = mstosamps(time)
    tap         = delay_read dly(time_samps, interp=linear)
    fb_filtered = onepole(tap, tone)
    delay_write dly(input + fb_filtered * feedback)

    dry     = input * (1 - mix)
    wet     = tap * mix
    wet_mix = dry + wet
}
```

Generate a plugin project directly from `.gdsp`:

```bash
gen-dsp fbdelay.gdsp -p clap
```

Or compile, validate, and visualize:

```bash
gen-dsp compile fbdelay.gdsp              # emit C++ to stdout
gen-dsp validate fbdelay.gdsp             # check graph connectivity
gen-dsp dot fbdelay.gdsp -o ./output/     # Graphviz DOT visualization
```

More examples in [`examples/dsl/`](examples/dsl/).

### Quick Start (JSON)

Define a graph as JSON:

```json
{
  "name": "gain",
  "inputs": [{"id": "in1"}],
  "outputs": [{"id": "out1", "source": "mul1"}],
  "params": [{"name": "volume", "min": 0.0, "max": 1.0, "default": 0.5}],
  "nodes": [{"op": "mul", "id": "mul1", "a": "in1", "b": "volume"}]
}
```

Then generate a plugin project:

```bash
gen-dsp gain.json -p clap
```

### Quick Start (Python)

```python
from gen_dsp.graph import (
    Graph, AudioInput, AudioOutput, Param, BinOp, OnePole,
    compile_graph, validate_graph
)

graph = Graph(
    name="lowpass",
    inputs=[AudioInput(id="in1")],
    outputs=[AudioOutput(id="out1", source="lp")],
    params=[Param(name="cutoff", min=0.0, max=0.999, default=0.5)],
    nodes=[OnePole(id="lp", a="in1", coeff="cutoff")],
)

errors = validate_graph(graph)
assert not errors

cpp = compile_graph(graph)
print(cpp)  # standalone C++ -- no genlib dependency
```

### Graph Algebra

Compose graphs using FAUST-style combinators:

```python
from gen_dsp.graph import series, parallel

chain = lowpass >> highpass    # series (>> operator)
stack = lowpass // highpass    # parallel (// operator)
```

### Simulation

Run graphs in Python with numpy (requires `pip install gen-dsp[sim]`):

```bash
gen-dsp sim lowpass.json -i in1=input.wav -o ./output/ --param cutoff=0.8
```

### Available Node Types

54 built-in node types covering ~89% of gen~ operators: `BinOp` (add/sub/mul/div/pow/mod/min/max/rsub/rdiv/rmod/absdiff/hypot/atan2/step/and/or/xor/gtp/ltp/gtep/ltep/eqp/neqp/fastpow), `UnaryOp` (abs/neg/sqrt/exp/log/sin/cos/tan/tanh/floor/ceil/round/sign/atan/asin/acos/not/bool/exp2/log2/log10/sinh/cosh/asinh/acosh/atanh/trunc/fract/atodb/dbtoa/ftom/mtof/phasewrap/degrees/radians/mstosamps/sampstoms/t60/t60time/fixdenorm/fixnan/isdenorm/isnan/fastsin/fastcos/fasttan/fastexp), `SinOsc`, `SawOsc`, `PulseOsc`, `TriOsc`, `Phasor`, `Noise`, `OnePole`, `Biquad`, `SVF`, `Allpass`, `DCBlock`, `History`, `DelayLine`/`DelayRead`/`DelayWrite`, `Buffer`/`BufRead`/`BufWrite`/`BufSize`/`Splat`/`Peek`, `Cycle`, `Wave`, `Lookup`, `ADSR`, `Accum`, `Counter`, `MulAccum`, `Clamp`, `Wrap`, `Fold`, `Scale`, `Mix`, `Smoothstep`, `Select`, `Compare`, `GateRoute`/`GateOut`, `Selector`, `Change`, `Delta`, `Latch`, `SampleHold`, `Slide`, `Elapsed`, `RateDiv`, `SmoothParam`, `Constant`, `NamedConstant`, `SampleRate`, `Pass`, `Subgraph`.

## Features

### Input-to-Parameter Remapping

In gen~, all external inputs are signal-rate `in` objects -- there's no distinction between audio and control data. When a patch uses signal inputs for control values (pitch, gate, etc.), the resulting plugin is misclassified as an effect. `--inputs-as-params` remaps those inputs to host-visible parameters:

```bash
# Remap all signal inputs to parameters
gen-dsp ./fm_bells -p clap --inputs-as-params

# Remap only specific inputs by name
gen-dsp ./fm_bells -p clap --inputs-as-params carrier "c/m ratio"
```

This turns a 2-input effect into a 0-input generator/instrument with 2 additional parameters. Works on all 15 platforms. See [docs/inputs_as_params.md](docs/inputs_as_params.md) for details.

### Automatic Buffer Detection

gen-dsp scans your gen~ export for buffer usage patterns and configures them automatically:

```bash
$ gen-dsp detect ./my_sampler_export
Gen~ Export: my_sampler
  Signal inputs: 1
  Signal outputs: 2
  Parameters: 3
  Buffers: ['sample', 'envelope']
```

Buffer names must be valid C identifiers (alphanumeric, starting with a letter).

### Platform Patches

Max 9 exports include `exp2f` which fails on macOS. gen-dsp automatically patches this to `exp2` during project creation, or you can apply it manually:

```bash
gen-dsp patch ./my_project --dry-run  # Preview
gen-dsp patch ./my_project            # Apply
```

## PureData

See the [PureData guide](docs/backends/puredata.md) for full details.

```bash
gen-dsp ./my_export -p pd --no-build
cd myeffect_pd && make all
```

Parameters: send `<name> <value>` messages to the first inlet. Send `bang` to list all parameters. Buffers connect to PureData arrays by name; use `pdset` to remap.

## Max/MSP

See the [Max/MSP guide](docs/backends/max.md) for full details.

```bash
gen-dsp ./my_export -p max
# Output: externals/myeffect~.mxo (macOS) or myeffect~.mxe64 (Windows)
```

Max is the only platform using 64-bit double signals. The SDK (max-sdk-base) is auto-cloned on first build.

## ChucK

See the [ChucK guide](docs/backends/chuck.md) for full details.

```bash
gen-dsp ./my_export -p chuck --no-build
cd my_export_chuck && make mac  # or make linux
```

Class names are auto-capitalized (`myeffect` -> `Myeffect`). Parameters are controlled via `eff.param("name", value)`. Buffer-based chugins can load WAV files at runtime via `eff.loadBuffer("sample", "amen.wav")`.

## AudioUnit (AUv2)

See the [AudioUnit guide](docs/backends/audiounit.md) for full details.

```bash
gen-dsp ./my_export -p au
# Output: build/myeffect.component
```

macOS only. Uses the raw AUv2 C API -- no external SDK needed, just system frameworks. Auto-detects `aufx` (effect) vs `augn` (generator). Passes Apple's `auval` validation.

## CLAP

See the [CLAP guide](docs/backends/clap.md) for full details.

```bash
gen-dsp ./my_export -p clap
# Output: build/myeffect.clap
```

Cross-platform (macOS, Linux, Windows). Zero-copy audio. Passes [clap-validator](https://github.com/free-audio/clap-validator) conformance tests. CLAP headers fetched via CMake FetchContent (tag 1.2.2, MIT licensed).

## VST3

See the [VST3 guide](docs/backends/vst3.md) for full details.

```bash
gen-dsp ./my_export -p vst3
# Output: build/VST3/Release/myeffect.vst3/
```

Cross-platform (macOS, Linux, Windows). Zero-copy audio. Passes Steinberg's SDK validator (47/47 tests). VST3 SDK fetched via CMake FetchContent (tag v3.7.9_build_61, GPL3/proprietary dual licensed).

## LV2

See the [LV2 guide](docs/backends/lv2.md) for full details.

```bash
gen-dsp ./my_export -p lv2
# Output: build/myeffect.lv2/
```

macOS and Linux. Passes lilv-based instantiation and audio processing validation. LV2 headers fetched via CMake FetchContent (tag v1.18.10, ISC licensed). TTL metadata with real parameter names/ranges generated at project creation time.

## SuperCollider

See the [SuperCollider guide](docs/backends/supercollider.md) for full details.

```bash
gen-dsp ./my_export -p sc
# Output: build/myeffect.scx (macOS) or build/myeffect.so (Linux)
```

Cross-platform (macOS, Linux, Windows). Passes sclang class compilation and scsynth NRT audio rendering validation. SC plugin headers fetched via CMake FetchContent (~80MB tarball). Generates `.sc` class file with parameter names/defaults. UGen name is auto-capitalized.

## VCV Rack

See the [VCV Rack guide](docs/backends/vcvrack.md) for full details.

```bash
gen-dsp ./my_export -p vcvrack
# Output: plugin.dylib (macOS), plugin.so (Linux), or plugin.dll (Windows)
```

Per-sample processing via `perform(n=1)`. Auto-generates `plugin.json` manifest and dark panel SVG. Passes headless Rack runtime validation (plugin loading + module instantiation). Rack SDK v2.6.1 auto-downloaded and cached.

## Daisy (Electrosmith)

See the [Daisy guide](docs/backends/daisy.md) for full details.

```bash
gen-dsp ./my_export -p daisy
# Output: build/myeffect.bin
```

Cross-compilation target for STM32H750. Requires `arm-none-eabi-gcc`. libDaisy (v7.1.0) auto-cloned on first build. Supports 8 board variants via `--board` flag (seed, pod, patch, patch_sm, field, petal, legio, versio).

## Circle (Raspberry Pi bare metal)

See the [Circle guide](docs/backends/circle.md) for full details.

```bash
gen-dsp ./my_export -p circle --board pi3-i2s
# Output: kernel8.img (copy to SD card boot partition)
```

Bare-metal kernel images for Raspberry Pi using the [Circle](https://github.com/rsta2/circle) framework (no OS). Requires `aarch64-none-elf-gcc` (64-bit) or `arm-none-eabi-gcc` (32-bit Pi Zero). Circle SDK auto-cloned on first build. Supports 14 board variants via `--board` flag:

| Audio | Boards |
|-------|--------|
| I2S (external DAC) | `pi0-i2s`, `pi0w2-i2s`, `pi3-i2s` (default), `pi4-i2s`, `pi5-i2s` |
| PWM (3.5mm jack) | `pi0-pwm`, `pi0w2-pwm`, `pi3-pwm`, `pi4-pwm` |
| HDMI | `pi3-hdmi`, `pi4-hdmi`, `pi5-hdmi` |
| USB (USB DAC) | `pi4-usb`, `pi5-usb` |

### Circle Multi-Plugin Mode

Multi-plugin mode lets you run multiple gen~ plugins on a single Circle kernel image, with USB MIDI CC parameter control at runtime. Use the `chain` subcommand with a JSON graph file:

```bash
gen-dsp chain ./exports --graph chain.json -n mychain --board pi4-i2s
# Output: kernel8-rpi4.img
```

#### Linear chain (auto-detected)

When connections form a simple series, gen-dsp uses an optimized ping-pong buffer codegen path:

```json
{
  "nodes": {
    "reverb": { "export": "gigaverb", "midi_channel": 1 },
    "comp":   { "export": "compressor", "midi_channel": 2,
                "cc": { "21": "threshold", "22": "ratio" } }
  },
  "connections": [
    ["audio_in", "reverb"],
    ["reverb",   "comp"],
    ["comp",     "audio_out"]
  ]
}
```

#### DAG with fan-out and mixer

For non-linear topologies, use fan-out (one output feeding multiple nodes) and built-in mixer nodes for fan-in:

```json
{
  "nodes": {
    "reverb":  { "export": "gigaverb" },
    "delay":   { "export": "spectraldelayfb" },
    "mix":     { "type": "mixer", "inputs": 2 }
  },
  "connections": [
    ["audio_in", "reverb"],
    ["audio_in", "delay"],
    ["reverb",   "mix:0"],
    ["delay",    "mix:1"],
    ["mix",      "audio_out"]
  ]
}
```

Mixer nodes combine inputs via weighted sum with per-input gain parameters (default 1.0, range 0.0-2.0), controllable via MIDI CC like any other parameter. The `"mix:0"` syntax routes to a specific mixer input index.

#### Graph reference

- **`nodes`**: dict of `id -> config`. Gen~ nodes require `"export"` (references a gen~ export directory name under the base `export_path`). Mixer nodes require `"type": "mixer"` and `"inputs"` count. `midi_channel` defaults to position + 1. `cc` is optional (default: CC-by-param-index).
- **`connections`**: list of `[from, to]` pairs. `audio_in` and `audio_out` are reserved endpoints. Use `"node:N"` to target a specific input index on mixer nodes.

The positional `export_path` argument is the base directory; each node's `export` field is resolved as a subdirectory (e.g. `./exports/gigaverb/gen/`). Use `--export /path/to/export` to provide explicit paths for individual nodes.

At runtime, connect a USB MIDI controller. Each node listens on its assigned MIDI channel for CC messages. With the default CC-by-index mapping, CC 0 controls parameter 0, CC 1 controls parameter 1, etc. Explicit `cc` mappings let you assign specific CC numbers to named parameters.

## Web Audio

See the [Web Audio guide](docs/backends/webaudio.md) for full details.

```bash
gen-dsp ./my_export -p webaudio
# Output: build/myeffect.wasm + processor.js + index.html
cd myeffect_webaudio && make serve
# Open http://localhost:8080
```

Compiles gen~ exports or graph sources to WebAssembly via Emscripten. The generated project includes an `AudioWorkletProcessor` (`processor.js`) for real-time browser audio and a demo page (`index.html`) with parameter sliders. Works with any graph source -- generators (e.g. FM synth) produce audio directly; effects process noise input by default.

```bash
# From a graph source (standalone generator)
gen-dsp fm_synth.gdsp -p webaudio
cd fm_synth_webaudio && make serve
```

## Standalone

See the [Standalone guide](docs/backends/standalone.md) for full details.

```bash
gen-dsp ./my_export -p standalone
cd myeffect_standalone && make all
./myeffect -l              # list parameters
./myeffect -p revtime 0.8  # run with parameter
```

Cross-platform CLI audio application using [miniaudio](https://miniaud.io/). Processes real-time audio from the system default input/output. Parameters set via `-p <name> <value>` flags. miniaudio.h downloaded at build time (no bundled dependency).

## AUv3

See the [AUv3 guide](docs/backends/auv3.md) for full details.

```bash
gen-dsp ./my_export -p auv3
cd myeffect_auv3
cmake -G Xcode -B build && cmake --build build --config Release
```

macOS only. Generates an App Extension (`.appex`) inside a host app (`.app`). Requires Xcode (for the CMake Xcode generator). Run the host app once to register the AUv3 with PluginKit for discovery by DAWs.

## Csound

See the [Csound guide](docs/backends/csound.md) for full details.

```bash
gen-dsp ./my_export -p csound
cd myeffect_csound && make all
# Usage in .csd: aout1, aout2 myeffect ain1, ain2, kparam1, kparam2, ...
```

Generates Csound opcode plugins. Audio inputs map to a-rate args, parameters to k-rate args. Install the built `lib*.dylib`/`.so` to `OPCODE6DIR64` for Csound to discover it.

## Shared FetchContent Cache

CLAP, VST3, LV2, and SC backends use CMake FetchContent to download their SDKs/headers at configure time. By default, gen-dsp bakes an OS-appropriate shared cache path into the generated CMakeLists.txt so that multiple projects share a single SDK download. Pass `--no-shared-cache` to disable this and use CMake's default project-local `build/_deps/` instead.

The default shared cache resolves to:

| OS | Cache path |
|----|------------|
| macOS | `~/Library/Caches/gen-dsp/fetchcontent/` |
| Linux | `$XDG_CACHE_HOME/gen-dsp/fetchcontent/` (defaults to `~/.cache/`) |
| Windows | `%LOCALAPPDATA%/gen-dsp/fetchcontent/` |

### `GEN_DSP_CACHE_DIR` environment variable

Set this at cmake configure time to override the default shared cache path:

```bash
GEN_DSP_CACHE_DIR=/path/to/cache cmake ..
```

The env var takes highest priority, followed by the default shared cache path, followed by CMake's default (project-local `build/_deps/` when `--no-shared-cache` is used).

The development Makefile exports `GEN_DSP_CACHE_DIR=build/.fetchcontent_cache` automatically, so `make example-clap`, `make example-vst3`, `make example-lv2`, and `make example-sc` share the same SDK cache used by tests.

## Limitations

- Maximum of 8 buffers per external
- Buffers are single-channel only. Use multiple buffers for multi-channel audio.
- Max/MSP: Windows builds require Visual Studio or equivalent MSVC toolchain
- AudioUnit: macOS only
- CLAP: first CMake configure requires network access to fetch CLAP headers (cached afterward)
- VST3: first CMake configure requires network access to fetch VST3 SDK (~50MB, cached afterward); GPL3/proprietary dual license
- LV2: first CMake configure requires network access to fetch LV2 headers (cached afterward)
- SuperCollider: first CMake configure requires network access to fetch SC headers (~80MB tarball, cached afterward)
- VCV Rack: first build requires network access to fetch Rack SDK (cached afterward); `RACK_DIR` env var can override auto-download; per-sample `perform(n=1)` has higher CPU overhead than block-based processing
- Daisy: requires `arm-none-eabi-gcc` cross-compiler; first clone of libDaisy requires network access and `git`; v1 targets Daisy Seed only (no board-specific knob/CV mapping)
- Circle: requires `aarch64-none-elf-gcc` (or `arm-none-eabi-gcc` for Pi Zero) cross-compiler; first clone of Circle SDK requires network access and `git`; output-only (no audio input capture); single-plugin mode requires manual GPIO/ADC code for parameter control; multi-plugin mode (`--graph`) supports linear chains and arbitrary DAGs (fan-out, fan-in via mixer nodes) but no buffers
- Web Audio: requires Emscripten SDK (`emcc` on PATH); buffer loading not yet supported (browser file I/O is async/platform-specific)
- Standalone: requires curl (for miniaudio.h download on first build)
- AUv3: macOS only; requires full Xcode (not just Command Line Tools) for the CMake Xcode generator; host app must be run once to register the extension with PluginKit
- Csound: requires Csound headers (`csdl.h`); MYFLT is double in Csound 6/7, so there is a float-to-double conversion cost per sample
- Graph frontend: requires pydantic >= 2.0; simulation additionally requires numpy >= 1.24; Daisy, Circle, and VCV Rack platforms not yet supported for graph sources

## Requirements

### Runtime

- Python >= 3.10
- C/C++ compiler (gcc, clang)

### Graph frontend (optional)

- pydantic >= 2.0 (`pip install gen-dsp[graph]`)
- numpy >= 1.24 for simulation (`pip install gen-dsp[sim]`)

### PureData builds

- make
- PureData headers (typically installed with PureData)

### Max/MSP builds

- CMake >= 3.19
- git (for cloning max-sdk-base)

### ChucK builds

- make
- C/C++ compiler (clang on macOS, gcc on Linux)
- ChucK (for running the chugin)

### AudioUnit builds

- macOS (AudioUnit is macOS-only)
- CMake >= 3.19
- C/C++ compiler (clang via Xcode or Command Line Tools)

### CLAP builds

- CMake >= 3.19
- C/C++ compiler (clang, gcc)
- Network access on first configure (to fetch CLAP headers)

### VST3 builds

- CMake >= 3.19
- C/C++ compiler (clang, gcc, MSVC)
- Network access on first configure (to fetch VST3 SDK, ~50MB)

### LV2 builds

- CMake >= 3.19
- C/C++ compiler (clang, gcc)
- Network access on first configure (to fetch LV2 headers)

### SuperCollider builds

- CMake >= 3.19
- C/C++ compiler (clang, gcc)
- Network access on first configure (to fetch SC plugin headers)

### VCV Rack builds

- make
- C/C++ compiler (clang, gcc)
- Network access on first build (Rack SDK auto-downloaded and cached; override with `RACK_DIR` env var)

### Daisy builds

- make
- `arm-none-eabi-gcc` ([ARM GNU Toolchain Downloads](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) -- select `arm-none-eabi`)
- git (for cloning libDaisy on first build)
- Network access on first build (to clone libDaisy + submodules)

### Circle builds

- make
- `aarch64-none-elf-gcc` ([ARM GNU Toolchain Downloads](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) -- select `aarch64-none-elf`) or `arm-none-eabi-gcc` (for Pi Zero)
- git (for cloning Circle SDK on first build)
- Network access on first build (to clone Circle)

### Web Audio builds

- make
- Emscripten SDK ([emsdk](https://emscripten.org/docs/getting_started/downloads.html) -- `emcc` on PATH)
- Python 3 (for `make serve` demo server)

### Standalone builds

- make
- C/C++ compiler
- curl (for downloading miniaudio.h on first build)

### AUv3 builds

- macOS
- Xcode (full IDE, not just Command Line Tools)
- CMake >= 3.19

### Csound builds

- make
- C/C++ compiler
- Csound headers (`csdl.h`) -- from CsoundLib64.framework (macOS) or `libcsound64-dev` (Linux)

### macOS

Install Xcode or Command Line Tools:

```bash
xcode-select --install
```

### Linux / Organelle

Standard build tools (gcc, make) are typically pre-installed.

## Cross-Compilation Note

Build artifacts are platform-specific. When moving projects between macOS and Linux/Organelle:

```bash
make clean
make all
```

## Development

```bash
git clone https://github.com/shakfu/gen-dsp.git
cd gen-dsp
uv venv && uv pip install -e ".[dev]"
source .venv/bin/activate
make test
```

### Building Example Plugins

The Makefile includes targets for generating and building example plugins from the test fixtures:

```bash
# gen~ export examples (from test fixtures)
make example-pd       # PureData external
make example-max      # Max/MSP external
make example-chuck    # ChucK chugin
make example-au       # AudioUnit plugin (macOS only)
make example-clap     # CLAP plugin
make example-vst3     # VST3 plugin
make example-lv2      # LV2 plugin
make example-sc       # SuperCollider UGen
make example-vcvrack  # VCV Rack module (auto-downloads Rack SDK)
make example-daisy    # Daisy firmware (requires arm-none-eabi-gcc)
make example-circle   # Circle kernel image (requires aarch64-none-elf-gcc)
make examples         # All of the above

# Graph examples (from .gdsp files)
make graph-example-clap     # CLAP from graph
make graph-example-webaudio # Web Audio WASM + demo page (defaults to fm_synth)
make graph-examples         # All graph examples
```

Override the fixture, name, or buffers:

```bash
make example-chuck FIXTURE=RamplePlayer NAME=rampleplayer BUFFERS="--buffers sample"
```

Available fixtures: `gigaverb` (default, no buffers), `RamplePlayer` (has buffers), `spectraldelayfb`.

Output goes to `build/examples/`. Web Audio examples include `make serve` in the generated project for local browser testing.

### Adding New Backends

gen-dsp uses a platform registry system that makes it straightforward to add support for new audio platforms (Bela, Daisy, etc.). See [docs/new_backends.md](docs/new_backends.md) for a complete guide.

## Attribution

The gen~ language was created by [Graham Wakefield](https://github.com/grrrwaaa) at Cycling '74.

This project builds on the original idea and work of [gen_ext](https://github.com/samesimilar/gen_ext) by Michael Spears.

Test fixtures include code exported from examples bundled with Max:

- gigaverb: ported from Juhana Sadeharju's implementation
- spectraldelayfb: from gen~.spectraldelay_feedback

The Daisy backend was informed by techniques from [oopsy](https://github.com/electro-smith/oopsy) by Electrosmith and contributors, including Graham Wakefield.

The Circle backend uses [Circle](https://github.com/rsta2/circle) by Rene Stange, a C++ bare metal programming environment for the Raspberry Pi.

## License

MIT License. See [LICENSE](LICENSE) for details.

Note: Generated gen~ code is subject to Cycling '74's license terms.
