Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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
  1. Layer 1 — Compile-time type checking with zero-cost abstractions
  2. Layer 2 — Hardware-inspired signal conventions
  3. 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:

The Name

In category theory, a quiver is a directed graph: objects connected by morphisms. In our world:

Category TheoryQuiver Audio
ObjectsModules
Morphisms (Arrows)Patch Cables
CompositionSignal Flow
IdentityPass-through

The math isn’t just decoration—it guides the API design and ensures compositions are well-typed.


Ready to patch? Start with Installation.