Skip to main content

Generate multiple dsp plugin formats from Max gen~ exports

Project description

gen-dsp

Documentation | API Reference | Changelog

gen-dsp turns Max/MSP gen~ code exports into complete, buildable audio plugin projects. A gen~ export is just a bundle of C++ and the genlib runtime; turning it into a plugin a host can load still means writing host glue, build files, parameter wiring, and platform-specific fixes by hand -- once for every target you care about. gen-dsp automates that end to end: from a single export it scaffolds a ready-to-build project for any of 15 targets, handling I/O and buffer detection, parameter metadata extraction, and platform-specific patching. It is a zero-dependency pure Python package -- pip install gen-dsp, then one command per target.

The 15 targets are PureData, Max/MSP, ChucK, AudioUnit (AUv2), AUv3, CLAP, VST3, LV2, SuperCollider, VCV Rack, Daisy, Circle, Web Audio (WASM), Standalone (miniaudio), and Csound -- spanning desktop plugin formats, embedded firmware, and the browser.

gen-dsp also optionally provides its own alternative DSP DSL called GDSP, short for GRAPH DSP (pip install gen-dsp[graph]), another way to author DSP directly in gen-dsp as an alternative to gen~. You define a signal graph in purpose-built .gdsp files, and compile it to any of the 15 targets through the same pipeline -- no Max or gen~ export required. A graph can even be transpiled back to gen~ codebox source (experimental), so work started in GDSP can return to Max. The separate, optional dsp-graph package adds a web-based visual editor and debugger (React + FastAPI) for viewing and driving gen-dsp operations from a browser.

This project is a friendly fork of Michael Spears' 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
Max/MSP docs/backends/max.md
ChucK docs/backends/chuck.md
AudioUnit (AUv2) docs/backends/audiounit.md
AUv3 docs/backends/auv3.md
CLAP docs/backends/clap.md
VST3 docs/backends/vst3.md
LV2 docs/backends/lv2.md
SuperCollider docs/backends/supercollider.md
VCV Rack docs/backends/vcvrack.md
Daisy docs/backends/daisy.md
Circle docs/backends/circle.md
Web Audio docs/backends/webaudio.md
Standalone docs/backends/standalone.md
Csound 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 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 (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, 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

pip install gen-dsp

With graph frontend support (requires pydantic):

pip install gen-dsp[graph]

With graph simulation (requires pydantic + numpy):

pip install gen-dsp[sim]

Or install from source:

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

# 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:

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(s) (required): a single name, a comma-separated list, or all. Names: pd, max, chuck, au, auv3, clap, vst3, lv2, sc, vcvrack, daisy, circle, webaudio, standalone, csound. With multiple targets the source is parsed once and each is generated/built in turn (a per-target summary is printed); -o then acts as a parent directory, with each target in <output>/<name>_<platform>. Example: gen-dsp ./export -p clap,vst3,au
  • -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)
  • --dry-run - Preview without creating files

build

Build an existing project:

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):

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 or a graph file (auto-detected by path):

gen-dsp detect <export-path>        # gen~ export directory
gen-dsp detect my.gdsp              # graph file (.gdsp or .json)
gen-dsp detect my.json --json       # machine-readable

For a gen~ export: name, signal I/O counts, parameters, detected buffers, and needed patches. For a graph: name, I/O, parameters (with ranges), node-type breakdown, buffers, delay lines, and validity.

patch

Apply platform-specific fixes:

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

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

list

List available platforms:

gen-dsp list                  # names only
gen-dsp list -v               # with build system, extension, and description
gen-dsp list --json           # machine-readable metadata
gen-dsp list --boards daisy   # valid --board variants for a platform

cache

Show cached SDKs with their on-disk sizes, or prune them to reclaim space:

gen-dsp cache                  # list cached SDKs + sizes
gen-dsp cache --prune          # delete them (asks for confirmation)
gen-dsp cache --prune -y       # delete without prompting
gen-dsp cache --prune --dry-run  # show what would be removed

Config file (gen-dsp.toml)

Instead of long flag strings, put a gen-dsp.toml in your project directory and run gen-dsp with no arguments. Keys mirror the CLI options (hyphens or underscores both work); any CLI flag overrides the matching key. Use --config PATH to point at a specific file.

# gen-dsp.toml
source = "./my_export"
name = "myverb"
platform = ["clap", "vst3", "au"]   # one name, a list, or "all"
output = "build"
no-build = true
gen-dsp                  # reads ./gen-dsp.toml
gen-dsp --config ci.toml # explicit file
gen-dsp -p clap          # config provides source/name; CLI overrides platform

On Python 3.10, reading TOML needs the tomli package (pip install tomli); Python 3.11+ uses the standard-library tomllib. See examples/gen-dsp.toml.

doctor

Check, per platform, whether the host has the tools needed to build (compilers, CMake/Make, cross-toolchains, Emscripten, Xcode, git), with install hints for anything missing. Auto-downloaded SDKs are shown as notes. Exits non-zero when a requested platform is not ready, so it can be used as a CI gate:

gen-dsp doctor                 # all platforms
gen-dsp doctor -p daisy        # one platform
gen-dsp doctor --json          # machine-readable

Graph subcommands (requires gen-dsp[graph])

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

gen-dsp compile <file> [-o DIR] [--optimize]
gen-dsp validate <file>
gen-dsp viz <file> [-o DIR]
gen-dsp sim <file> [-i INPUT] [-o DIR] [-n SAMPLES] [--param K=V]
gen-dsp genexpr <file> [-o FILE]   # experimental: transpile to gen~ codebox

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 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 for details.

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:

gen-dsp fbdelay.gdsp -p clap

Or compile, validate, and visualize:

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

More examples in examples/dsl/.

Quick Start (JSON)

Define a graph as 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:

gen-dsp gain.json -p clap

Quick Start (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:

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]):

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

Transpile to gen~ codebox (experimental)

Re-emit a graph as gen~ codebox (GenExpr) source, to bring a graph back into Max/MSP for further patching, and as a differential-validation oracle for gen-dsp's own C++ compiler:

gen-dsp genexpr fbdelay.gdsp                 # prints codebox to stdout
gen-dsp genexpr fbdelay.gdsp -o fbdelay.genexpr
gen-dsp genexpr fbdelay.gdsp --rename-map    # JSON {original -> safe} substitutions

Emission is hybrid: a node maps to a native gen~ operator where its semantics match gen-dsp's C++ (clamp, wrap, scale, mix, delta, ...), and otherwise to faithful arithmetic mirroring the compiler -- single-sample state via History, ring buffers and tables via Data + peek/poke/dim. Most node types are covered; Noise emits gen~'s native noise() (valid for Max, but not deterministically comparable), while Buffer(fill="sine") and the fast* approximation ops are not expressible in a codebox and raise an error.

Param names and node ids that collide with a gen~ reserved word or operator (e.g. a param named mix) are auto-renamed to a safe identifier and recorded as comments in the emitted header. --rename-map emits those {original -> safe} substitutions as a JSON object instead of the codebox, so external editors and tools can correlate .gdsp identifiers with the generated names (also available programmatically as genexpr_rename_map(graph) in gen_dsp.graph.transpile).

This feature is experimental. The emitted GenExpr is checked in CI against the Python simulate() reference (bit-exact across a broad node corpus); validating gen-dsp's semantics against Cycling '74's actual gen~ still requires Max in the loop.

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:

# 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 for details.

Automatic Buffer Detection

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

$ 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:

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

PureData

See the PureData guide for full details.

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 for full details.

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 for full details.

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 for full details.

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 for full details.

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

Cross-platform (macOS, Linux, Windows). Zero-copy audio. Passes clap-validator conformance tests. CLAP headers fetched via CMake FetchContent (tag 1.2.2, MIT licensed).

VST3

See the VST3 guide for full details.

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 for full details.

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 for full details.

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 for full details.

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 for full details.

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 for full details.

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 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:

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:

{
  "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:

{
  "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 for full details.

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.

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

Standalone

See the Standalone guide for full details.

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. 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 for full details.

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 for full details.

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:

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 -- 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 -- 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 -- 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:

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:

make clean
make all

Development

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:

# 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:

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 for a complete guide.

Attribution

The gen~ language was created by Graham Wakefield at Cycling '74.

This project builds on the original idea and work of 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 by Electrosmith and contributors, including Graham Wakefield.

The Circle backend uses Circle by Rene Stange, a C++ bare metal programming environment for the Raspberry Pi.

License

MIT License. See LICENSE for details.

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gen_dsp-0.3.2.tar.gz (347.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gen_dsp-0.3.2-py3-none-any.whl (440.2 kB view details)

Uploaded Python 3

File details

Details for the file gen_dsp-0.3.2.tar.gz.

File metadata

  • Download URL: gen_dsp-0.3.2.tar.gz
  • Upload date:
  • Size: 347.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for gen_dsp-0.3.2.tar.gz
Algorithm Hash digest
SHA256 f58fa6c7ff6569dc1367e26b93005193d33a4154b81b1c465974e33edcedb30d
MD5 69bb3c86e4006bae852c6f1aa78ddf26
BLAKE2b-256 cd14827b2a8aadb5bfc4f01d2b9122f8933a3509d7cdfbde099f4a69daed8834

See more details on using hashes here.

File details

Details for the file gen_dsp-0.3.2-py3-none-any.whl.

File metadata

  • Download URL: gen_dsp-0.3.2-py3-none-any.whl
  • Upload date:
  • Size: 440.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.2

File hashes

Hashes for gen_dsp-0.3.2-py3-none-any.whl
Algorithm Hash digest
SHA256 847b9f9140b1ecac8bbbd94c47d5a242bc956360217f4e5803bc03970bf4e5bf
MD5 a5737b23d0cb68ddb118b54d6ea027cb
BLAKE2b-256 f775e5a9bac0cee13d02df5cc37e2e397cfa18e873fbe380b6da8e5d478f91ff

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page