Quiver: Modular Audio Synthesis
“A quiver is a directed graph — nodes connected by arrows. In audio, our nodes are modules, our arrows are patch cables, and signal flows through their composition.”
Quiver is a Rust library for building modular audio synthesis systems. It combines the mathematical elegance of category theory with the tactile joy of patching a hardware modular synthesizer.
flowchart LR
subgraph "Oscillator"
VCO[🎵 VCO]
end
subgraph "Filter"
VCF[📊 VCF]
end
subgraph "Amplifier"
VCA[🔊 VCA]
end
subgraph "Envelope"
ADSR[📈 ADSR]
end
VCO -->|saw| VCF
VCF -->|lowpass| VCA
ADSR -->|env| VCF
ADSR -->|env| VCA
VCA --> Output[🔈]
Why Quiver?
Type-Safe Patching
Quiver catches connection errors at compile time. Connect a gate to a V/Oct input? The type system prevents it before you hear a single pop.
Hardware-Inspired Semantics
Voltages follow real modular conventions:
- ±5V for audio signals
- 1V/octave for pitch (0V = C4)
- 0-5V for gates and triggers
- 0-10V for unipolar CV
Mathematical Foundations
Built on Arrow-style functional combinators, Quiver lets you compose DSP operations like mathematical functions:
$$f \ggg g = g \circ f$$
Chain two modules and their types compose automatically.
Three-Layer Architecture
graph TB
subgraph "Layer 3: Patch Graph"
G[Runtime Topology]
end
subgraph "Layer 2: Port System"
P[Signal Conventions]
end
subgraph "Layer 1: Typed Combinators"
C[Arrow Composition]
end
C --> P --> G
style C fill:#4a9eff,color:#fff
style P fill:#f9a826,color:#fff
style G fill:#50c878,color:#fff
- Layer 1 — Compile-time type checking with zero-cost abstractions
- Layer 2 — Hardware-inspired signal conventions
- Layer 3 — Runtime-configurable patching like a real modular
Quick Taste
//! Quick Taste Example
//!
//! A minimal example showing the core Quiver workflow.
//!
//! Run with: cargo run --example quick_taste
use quiver::prelude::*;
fn main() {
// Create a patch at CD-quality sample rate
let mut patch = Patch::new(44100.0);
// Add an oscillator and output
let vco = patch.add("vco", Vco::new(44100.0));
let output = patch.add("out", StereoOutput::new());
// Connect the sawtooth wave to both channels
patch.connect(vco.out("saw"), output.in_("left")).unwrap();
patch.connect(vco.out("saw"), output.in_("right")).unwrap();
// Compile the patch for processing
patch.set_output(output.id());
patch.compile().unwrap();
// Generate one second of audio
let mut samples = Vec::new();
for _ in 0..44100 {
let (left, _right) = patch.tick();
samples.push(left);
}
// Report the results
let peak = samples.iter().map(|s| s.abs()).fold(0.0_f64, f64::max);
println!("Generated {} samples", samples.len());
println!("Peak amplitude: {:.2}V", peak);
}
What You’ll Learn
This documentation guides you from first patch to advanced synthesis:
- Getting Started — Install and build your first sound
- Tutorials — Progressive lessons in synthesis
- How-To Guides — Task-focused recipes
- Concepts — Deep dives into theory
- Reference — Complete module documentation
The Name
In category theory, a quiver is a directed graph: objects connected by morphisms. In our world:
| Category Theory | Quiver Audio |
|---|---|
| Objects | Modules |
| Morphisms (Arrows) | Patch Cables |
| Composition | Signal Flow |
| Identity | Pass-through |
The math isn’t just decoration—it guides the API design and ensures compositions are well-typed.
Ready to patch? Start with Installation.