quiver-dsp 0.1.0

A modular audio synthesis library using Arrow-style combinators and graph-based patching
Documentation

🏹 Quiver

CI Documentation Coverage License: MIT Rust

A modular audio synthesis library using Arrow-style combinators and graph-based patching.

Table of Contents

🚧 Project Status

Quiver is pre-1.0 and under active development. There are no published releases yet β€” depend on it via git if you want to experiment. The three-layer architecture is in place and the module set is broad, but the public API is still evolving and may change between commits without notice. Expect rough edges; feedback and contributions are very welcome.

πŸ€” Why Quiver?

Traditional audio synthesis libraries often force you to choose between:

  • Low-level control with verbose, error-prone code
  • High-level convenience that hides the signal flow

Quiver gives you both. Inspired by category theory and modular synthesizers, it provides:

Approach Benefit
πŸ”— Arrow Combinators Compose modules like functions with type-safe operators
πŸŽ›οΈ Patch Graph Visual, intuitive signal routing like hardware modular synths
🎚️ Analog Modeling Authentic warmth with component drift and saturation
⚑ Zero Allocation Real-time safe with predictable performance
// Compose modules functionally with Arrow-style combinators
let synth = oscillator >>> filter >>> amplifier;

// Or patch them like hardware, jack to jack
patch.connect(vco.out("saw"), vcf.in_("in")).unwrap();

How Quiver Compares

Quiver's niche is an embeddable, Rust-first modular-synth graph with hardware-style semantics β€” a no_std core plus first-class WebAssembly bindings.

Quiver FunDSP VCV Rack Glicol / Web Audio
Form Rust library Rust library Desktop application DSL / browser graph
Model Modular graph + Arrow combinators Combinator DSP graphs Patchable rack of plugins Live-coding / node graph
Semantics Hardware-style ports (V/Oct, gate, CV, normalled jacks) Signal-flow operators Eurorack-style modules Audio nodes / DSL
no_std core βœ… partial ❌ ❌
WASM bindings βœ… (first-class) via wasm-pack ❌ βœ… (browser-native)
Zero-alloc audio path βœ… βœ… n/a n/a

If you want a signal-flow combinator DSP library, FunDSP is excellent and an inspiration. If you want a finished desktop instrument, use VCV Rack. Quiver is for building modular instruments and tools β€” in native apps, plugins, or the browser β€” from Rust.

✨ Features

  • πŸ”— Typed Combinators: Compose audio modules using category-theory-inspired operators (>>>, ***, &&&)
  • πŸŽ›οΈ Graph-Based Patching: Build complex synthesizer patches with a flexible node/cable system
  • 🎚️ Analog Modeling: Realistic VCO drift, filter saturation, and component tolerances
  • 🎹 Polyphony: Built-in voice allocation with multiple algorithms
  • ⚑ SIMD Optimization: Optional vectorized processing for performance-critical applications
  • πŸ’Ύ Serialization: Save and load patches as JSON
  • πŸ”§ no_std Support: Run on embedded systems and WebAssembly targets

πŸ—οΈ Architecture

Quiver is built in three composable layers:

graph TD
    subgraph "Layer 3: Patch Graph"
        VCO[VCO] --> VCF[VCF]
        VCF --> VCA[VCA]
        VCA --> Output[Output]
        VCO --> LFO[LFO]
        LFO --> VCF
        ADSR[ADSR] --> VCA
    end

    subgraph "Layer 2: Port System"
        Ports["SignalKind: Audio | V/Oct | Gate | Trigger | CV<br/>ModulatedParam: Linear | Exponential | V/Oct ranges"]
    end

    subgraph "Layer 1: Typed Combinators"
        Combinators["Chain (>>>) | Parallel (***) | Fanout (&&&) | Feedback"]
    end

    Output ~~~ Ports
    Ports ~~~ Combinators

Layer 1 - Combinators: Functional composition with type-safe signal flow Layer 2 - Ports: Rich metadata for inputs/outputs with modulation support Layer 3 - Graph: Visual patching with cables, mixing, and normalled connections

πŸš€ Quick Start

Add Quiver to your Cargo.toml (the package is published as quiver-dsp because the bare name quiver was taken on crates.io; the library name is still quiver, so imports are use quiver::...):

[dependencies]
quiver-dsp = "0.1"

Feature Flags

Feature Default Description
std Yes Full functionality including OSC, plugins, visualization (implies alloc)
alloc No Serialization, presets, and I/O for no_std + heap environments
simd No SIMD vectorization for block processing (works with any tier)
wasm No WebAssembly bindings (wasm-bindgen) + TypeScript types (tsify); implies alloc. Powers packages/@quiver/wasm and the browser synth demo

no_std Support

Quiver supports three tiers for different environments. All tiers are no_std but require a global allocator β€” the core graph uses Box/Vec/String, so there is no allocator-free tier:

# Tier 1: Core DSP only (no_std, heap required β€” bring your own #[global_allocator])
quiver-dsp = { version = "0.1", default-features = false }

# Tier 2: With serialization & presets (WASM web apps)
quiver-dsp = { version = "0.1", default-features = false, features = ["alloc"] }

# Tier 3: Full std (desktop apps, default)
quiver-dsp = "0.1"
Tier DSP Serialize Presets I/O OSC/Plugins Visual
Core βœ“
alloc βœ“ βœ“ βœ“ βœ“
std βœ“ βœ“ βœ“ βœ“ βœ“ βœ“

Build a subtractive synthesizer voice β€” VCO β†’ VCF β†’ VCA, shaped by an ADSR envelope:

use quiver::prelude::*;

fn main() {
    // Create a patch at CD-quality sample rate.
    let sr = 44_100.0;
    let mut patch = Patch::new(sr);

    // Add modules β€” each `add` takes a name and returns a `NodeHandle`
    // used to reference that module's ports.
    let vco = patch.add("vco", Vco::new(sr));
    let vcf = patch.add("vcf", Svf::new(sr));
    let vca = patch.add("vca", Vca::new());
    let env = patch.add("env", Adsr::new(sr));
    let out = patch.add("out", StereoOutput::new());

    // Patch cables jack-to-jack: VCO β†’ VCF β†’ VCA β†’ stereo out.
    // `in_()` is spelled with a trailing underscore because `in` is a Rust keyword.
    patch.connect(vco.out("saw"), vcf.in_("in")).unwrap();
    patch.connect(vcf.out("lp"), vca.in_("in")).unwrap();
    patch.connect(vca.out("out"), out.in_("left")).unwrap();
    patch.connect(vca.out("out"), out.in_("right")).unwrap();

    // The envelope modulates both the filter cutoff and the amplitude.
    patch.connect(env.out("env"), vcf.in_("cutoff")).unwrap();
    patch.connect(env.out("env"), vca.in_("cv")).unwrap();

    // Select the output module, then compile before processing (required).
    patch.set_output(out.id());
    patch.compile().unwrap();

    // Advance the patch one sample at a time; each `tick()` returns stereo audio.
    let (left, right) = patch.tick();
    println!("first sample: {left}, {right}");
}

πŸ“š Documentation

Resource Description
πŸ“– User Guide Comprehensive tutorials and concepts
πŸ“‹ API Reference Rustdoc documentation
πŸ’‘ Examples Runnable example patches
πŸ—ΊοΈ DEVELOPMENT.md Architecture decisions and roadmap

🎡 Examples

Run the examples to hear Quiver in action:

# Simple patch demo
cargo run --example simple_patch

# FM synthesis tutorial
cargo run --example tutorial_fm

# Polyphonic synth
cargo run --example tutorial_polyphony

# See all examples
cargo run --example

Example Patches

Example Description
simple_patch Basic VCO β†’ VCF β†’ VCA signal chain
tutorial_fm FM synthesis with modulator/carrier
tutorial_polyphony Polyphonic voice allocation
tutorial_subtractive Classic subtractive synthesis
howto_midi MIDI input handling

πŸ› οΈ Development

# Setup development environment (installs tools and git hooks)
make setup

# Run all checks (format, lint, test)
make check

# Run tests with coverage (80% threshold)
make coverage

# Format and lint
make fmt lint

# Generate changelog
make changelog

# See all available commands
make help

See DEVELOPMENT.md for the development roadmap and architecture decisions.

🀝 Contributing

Contributions are welcome! Please read our Contributing Guidelines before submitting a PR.

Areas Where Help is Appreciated

Area Examples
πŸ”Š DSP Algorithms Filter models, oscillator antialiasing, effects
πŸ§ͺ Testing Audio comparison tests, performance benchmarks
πŸ“ Documentation Tutorials, examples, API docs
πŸŽ›οΈ Modules Classic hardware module implementations

Quick Contribution Guide

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-filter)
  3. Make your changes
  4. Run checks (make check)
  5. Commit with conventional commits
  6. Open a Pull Request

Look for issues labeled good first issue to get started!

πŸ“„ License

This project is licensed under the MIT License - see the LICENSE file for details.