gen_dsp.graph API Reference¶
Public API for the gen_dsp.graph subpackage. Requires pip install gen-dsp[graph] (pydantic).
Simulation functions additionally require pip install gen-dsp[sim] (numpy).
All symbols are importable from gen_dsp.graph unless otherwise noted.
DSL Parsing¶
parse(source, *, filename="<string>") -> Graph¶
Parse a .gdsp source string and return a single Graph.
If source contains multiple graph blocks, returns the last one (which typically uses the
earlier ones as named subgraphs). Raises GDSPSyntaxError on tokenizer/parser errors and
GDSPCompileError on semantic errors (e.g. unresolved identifiers, recursive graph calls).
from gen_dsp.graph import parse
graph = parse("""
graph lowpass {
in x
out y = onepole(x, coeff=0.9)
}
""")
parse_multi(source, *, filename="<string>") -> dict[str, Graph]¶
Parse a .gdsp source string and return all graphs as {name: Graph}. Use this when you
need access to library subgraphs defined in the same file.
parse_file(path, *, multi=False) -> Graph | dict[str, Graph]¶
Parse a .gdsp file. Equivalent to parse(path.read_text()) or parse_multi(...) when
multi=True. Passes the filename to error messages.
class GDSPSyntaxError(Exception)¶
Raised by the tokenizer and parser for structural errors (unknown tokens, mismatched braces,
unexpected EOF). Attributes: line: int, col: int, filename: str. The str() representation
includes the source location: "<file>:<line>:<col>: <message>".
class GDSPCompileError(Exception)¶
Raised by the compiler for semantic errors (undefined ID, recursive graph reference, wrong number
of arguments, etc.). Same attributes as GDSPSyntaxError.
Validation¶
validate_graph(graph, *, warn_unmapped_params=False) -> list[GraphValidationError]¶
Validate a Graph and return a list of errors. An empty list means the graph is valid.
Before validation, subgraphs are expanded inline via expand_subgraphs().
Checks performed:
- Unique IDs -- no duplicate node IDs; no node ID collides with an audio input or param name.
- Reference resolution -- every string field that refers to another node resolves to a known ID.
- Output sources -- every
AudioOutput.sourcereferences an existing node. - Delay consistency --
DelayRead.delayandDelayWrite.delayreference an existingDelayLine. - Buffer consistency --
BufRead,BufWrite,BufSize,Splat,Cycle,Wave,Lookupreference an existingBuffer. - Gate consistency --
GateOut.gatereferences an existingGateRoute; channel is in range. - Control-rate consistency -- nodes listed in
control_nodesexist; they must not depend on audio inputs or audio-rate nodes. - No pure cycles -- topological sort on non-feedback edges must succeed.
When warn_unmapped_params=True, warnings for subgraph params that fall back to defaults are
appended after all errors.
class GraphValidationError(str)¶
A structured validation error that behaves as a plain string. Subclasses str so existing call
sites (errors == [], "; ".join(errors), print(err)) work unchanged.
Constructor:
GraphValidationError(
kind: str,
message: str,
*,
node_id: str | None = None,
field_name: str | None = None,
severity: str = "error", # "error" | "warning"
)
Attributes:
| Attribute | Type | Description |
|---|---|---|
kind |
str |
Machine-readable error category (see below) |
node_id |
str \| None |
ID of the offending node, if applicable |
field_name |
str \| None |
Name of the offending field, if applicable |
severity |
str |
"error" or "warning" |
kind values:
kind |
severity |
Meaning |
|---|---|---|
"duplicate_id" |
error | Two nodes share the same ID |
"id_collision" |
error | A node ID equals an audio input ID or param name |
"dangling_ref" |
error | A field references an ID that does not exist |
"bad_output_source" |
error | AudioOutput.source does not reference a node |
"missing_delay_line" |
error | DelayRead/DelayWrite references a non-existent DelayLine |
"missing_buffer" |
error | A buffer consumer references a non-existent Buffer |
"missing_gate_route" |
error | GateOut.gate references a non-existent GateRoute |
"gate_channel_range" |
error | GateOut.channel is outside [1, gate_route.count] |
"invalid_control_node" |
error | An ID in control_nodes is not a node ID |
"control_audio_dep" |
error | A control-rate node depends on an audio input |
"control_rate_dep" |
error | A control-rate node depends on an audio-rate node |
"cycle" |
error | Graph contains a pure cycle (not through History or delay) |
"expansion_error" |
error | expand_subgraphs() raised (malformed Subgraph node) |
"unmapped_param" |
warning | A subgraph param uses its default (only with warn_unmapped_params=True) |
Usage example (structured access for a UI):
errors = validate_graph(graph)
for err in errors:
if err.node_id:
highlight_node(err.node_id, severity=err.severity, kind=err.kind)
print(str(err)) # plain-text message with context
Compilation¶
compile_graph(graph) -> str¶
Compile a Graph to a standalone C++ source string. Raises ValueError if the graph fails
validation or contains IDs that are not valid C identifiers.
The output is a self-contained .cpp file (no genlib dependency) with:
- State struct
{Name}State create(sr)/destroy(self)/reset(self)lifecycle functionsperform(self, ins, outs, n)sample-processing loop- Param introspection:
num_params,param_name,param_min,param_max,set_param,get_param - Buffer introspection:
num_buffers,buffer_name,buffer_size,get_buffer,set_buffer - Peek introspection:
num_peeks,peek_name,get_peek
compile_graph_to_file(graph, output_dir) -> Path¶
Compile a Graph and write {name}.cpp to output_dir (created if absent). Returns the path
to the written file.
Optimization¶
optimize_graph(graph) -> OptimizeResult¶
Apply all optimization passes in sequence and return an OptimizeResult(graph, stats).
Passes applied:
- Constant folding (
constant_fold) -- pure nodes with all-constant inputs becomeConstant. - Common subexpression elimination (
eliminate_cse) -- duplicate pure nodes with identical inputs are merged into one. - Dead node elimination (
eliminate_dead_nodes) -- nodes not reachable from any output are removed. Respects side-effecting writers: when aDelayReadorBufRead/BufSizeis live, theDelayWrite/BufWrite/Splatnodes feeding the same resource are kept.
All passes return a new Graph (immutable). Stateful nodes (History, DelayLine, oscillators,
filters, etc.) are never constant-folded.
constant_fold(graph) -> Graph¶
Constant folding pass only. See optimize_graph for description.
eliminate_dead_nodes(graph) -> Graph¶
Dead node elimination pass only.
eliminate_cse(graph) -> Graph¶
Common subexpression elimination pass only.
promote_control_rate(graph) -> Graph¶
Promote audio-rate pure nodes to control-rate when all their dependencies are params, literals,
invariant nodes, or existing control-rate nodes. No-op when graph.control_interval <= 0.
class OptimizeStats(NamedTuple)¶
Statistics from one optimize_graph() run.
| Field | Type | Description |
|---|---|---|
constants_folded |
int |
Nodes replaced by Constant |
cse_merges |
int |
Duplicate nodes merged |
dead_nodes_removed |
int |
Unreachable nodes removed |
control_rate_promoted |
int |
Nodes promoted to control rate |
class OptimizeResult(NamedTuple)¶
| Field | Type | Description |
|---|---|---|
graph |
Graph |
The optimized graph |
stats |
OptimizeStats |
Per-pass statistics |
Simulation¶
Simulation requires numpy (pip install gen-dsp[sim]). Import directly from the submodule:
simulate(graph, inputs=None, n_samples=0, params=None, state=None, sample_rate=0.0) -> SimResult¶
Simulate a Graph in Python and return output arrays.
| Argument | Type | Description |
|---|---|---|
graph |
Graph |
The graph to simulate |
inputs |
dict[str, NDArray[float32]] \| None |
Audio input arrays keyed by input ID. None for generators. All arrays must have equal length. |
n_samples |
int |
Samples to process. Inferred from inputs if 0. Required for generators. |
params |
dict[str, float] \| None |
Parameter overrides applied before processing. |
state |
SimState \| None |
Reuse existing state for streaming. Created fresh if None. |
sample_rate |
float |
Override sample rate. Uses graph.sample_rate if 0. |
Returns a SimResult with outputs: dict[str, NDArray[float32]] and state: SimState.
class SimState¶
Mutable simulation state. Created automatically by simulate(), or explicitly for direct
parameter/buffer access.
Methods:
| Method | Description |
|---|---|
reset() |
Reset all state to initial values (params reset to defaults). |
set_param(name, value) |
Set a parameter. Raises KeyError if unknown. |
get_param(name) -> float |
Read a parameter. Raises KeyError if unknown. |
set_buffer(buffer_id, data) |
Set buffer contents. Data is truncated/zero-padded to buffer size. |
get_buffer(buffer_id) -> NDArray[float32] |
Get a copy of buffer contents. |
get_peek(peek_id) -> float |
Read the last value captured by a Peek node. |
class SimResult¶
| Field | Type | Description |
|---|---|---|
outputs |
dict[str, NDArray[float32]] |
Output arrays keyed by AudioOutput.id |
state |
SimState |
The (possibly new) simulation state |
Serialization¶
graph_to_gdsp(graph) -> str¶
Serialize a Graph back to .gdsp DSL source. The output round-trips through parse().
Constants are inlined as numeric literals. NamedConstant nodes become bare identifiers (pi,
e, etc.). History feedback writes use the <- operator. Nodes are emitted in topological
order.
Visualization¶
graph_to_dot(graph) -> str¶
Convert a Graph to a Graphviz DOT string. Nodes are colored by type, feedback edges are marked
[style=dashed].
graph_to_dot_file(graph, output_dir) -> Path¶
Write {name}.dot to output_dir. If dot (Graphviz) is on PATH, also renders {name}.pdf.
Returns the path to the .dot file.
Graph Algebra¶
FAUST-style block-diagram combinators. All return a new Graph; params are namespaced with the
subgraph ID prefix to avoid collisions.
series(a, b) -> Graph¶
Pipe a's outputs into b's inputs positionally. Requires len(a.outputs) == len(b.inputs).
Result: inputs=a.inputs, outputs=b.outputs.
Operator shorthand: a >> b.
parallel(a, b) -> Graph¶
Stack graphs side by side. Inputs and outputs are concatenated; params are namespaced.
Operator shorthand: a // b.
split(a, b) -> Graph¶
Fan-out: distribute a's outputs cyclically across b's inputs.
Requires len(b.inputs) % len(a.outputs) == 0.
merge(a, b) -> Graph¶
Fan-in: group a's outputs and sum them pairwise into b's inputs.
Requires len(a.outputs) % len(b.inputs) == 0.
Subgraph Expansion¶
expand_subgraphs(graph) -> Graph¶
Recursively inline all Subgraph nodes, rewriting IDs and param bindings to avoid collisions.
Returns a flat Graph with no Subgraph nodes.
Called automatically by compile_graph(), validate_graph(), optimize_graph(), and
simulate().
Topological Sort¶
toposort(graph) -> list[Node]¶
Return graph nodes in topological order using Kahn's algorithm with alphabetical tie-breaking for
determinism. Raises ValueError if the graph contains a pure cycle (cycles through History or
delay feedback edges are allowed and excluded from the sort).
Platform Adapter¶
For generating platform-specific plugin projects from a graph (without a gen~ export).
generate_adapter_cpp(graph, platform) -> str¶
Generate the _ext_{platform}.cpp adapter source that bridges the compiled graph to gen-dsp's
platform backend. platform must be one of SUPPORTED_PLATFORMS.
generate_manifest(graph) -> str¶
Generate a manifest.json string compatible with gen-dsp's Manifest dataclass. Contains
gen_name, num_inputs, num_outputs, params (with min/max/default), and buffers.
compile_for_gen_dsp(graph, output_dir, platform) -> Path¶
Convenience: compile graph and write three files to output_dir:
{name}.cpp-- compiled C++_ext_{platform}.cpp-- platform adaptermanifest.json-- gen-dsp manifest
Returns the output directory path.
SUPPORTED_PLATFORMS¶
set[str] -- the set of platform keys accepted by generate_adapter_cpp() and
compile_for_gen_dsp(). Currently: chuck, clap, au, vst3, lv2, sc, vcvrack,
daisy, circle, pd, max.
Generating Complete Plugin Projects¶
Use ProjectGenerator.from_graph() (from gen_dsp.core.project) to produce a fully buildable
project for any platform:
from gen_dsp.graph import Graph, AudioInput, AudioOutput, BinOp, Param
from gen_dsp.core.project import ProjectConfig, ProjectGenerator
graph = Graph(
name="gain",
inputs=[AudioInput(id="in1"), AudioInput(id="in2")],
outputs=[AudioOutput(id="out1", source="g1"), AudioOutput(id="out2", source="g2")],
params=[Param(name="gain", min=0.0, max=2.0, default=1.0)],
nodes=[
BinOp(id="g1", op="mul", a="in1", b="gain"),
BinOp(id="g2", op="mul", a="in2", b="gain"),
],
)
config = ProjectConfig(name=graph.name, platform="clap")
gen = ProjectGenerator.from_graph(graph, config)
project_dir = gen.generate("build/gain_clap")
This writes all source files (compiled C++, adapter, platform templates, build files) and runs the build by default.