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: build a patch, run it,
//! and this time actually *hear* it — the render finishes by writing a real
//! `.wav` file to disk.
//!
//! Run with: cargo run --example quick_taste
use quiver::prelude::*;
use quiver::render::write_wav;
use std::path::Path;
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. `render` (from `quiver::render`, re-exported
// in the prelude) just repeatedly calls `patch.tick()` for you — it's the
// same thing as a manual loop, but it's also what writes WAV files below.
let (left, right) = render(&mut patch, 1.0);
// Report the results
let peak = left.iter().map(|s| s.abs()).fold(0.0_f64, f64::max);
println!("Generated {} samples", left.len());
println!("Peak amplitude: {:.2}V", peak);
// --- Hear it! ---
// Quiver's `Audio` ports use a modular-synth convention of +-5V, but a
// `.wav` file's samples are full-scale +-1.0, so we divide by 5 before
// writing (see the `# Sample scale` note on `quiver::render`).
let to_full_scale = |buf: &[f64]| -> Vec<f64> { buf.iter().map(|s| s / 5.0).collect() };
let wav_path = Path::new("target/quick_taste.wav");
write_wav(
wav_path,
44100,
&to_full_scale(&left),
&to_full_scale(&right),
)
.expect("failed to write WAV file");
println!(
"\nWrote {} - play it in any audio player to hear Quiver's output!",
wav_path.display()
);
}
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.