Skip to main content

quiver/
lib.rs

1//! # Quiver: Modular Audio Synthesis Library
2//!
3//! > *"A quiver is a directed graph—nodes connected by arrows. In audio, our nodes are
4//! > modules, our arrows are patch cables, and signal flows through their composition."*
5//!
6//! `quiver` is a Rust library for building modular audio synthesis systems. It combines
7//! the mathematical elegance of category theory with the tactile joy of patching a
8//! hardware modular synthesizer.
9//!
10//! ## Quick Start
11//!
12//! ```
13//! use quiver::prelude::*;
14//!
15//! // Build a patch at CD-quality sample rate.
16//! let sr = 44_100.0;
17//! let mut patch = Patch::new(sr);
18//!
19//! // Add modules — each `add` returns a `NodeHandle` used to reference its ports.
20//! let vco = patch.add("vco", Vco::new(sr));
21//! let vcf = patch.add("vcf", Svf::new(sr));
22//! let vca = patch.add("vca", Vca::new());
23//! let env = patch.add("env", Adsr::new(sr));
24//! let out = patch.add("out", StereoOutput::new());
25//!
26//! // Patch cables: VCO -> VCF -> VCA -> stereo out.
27//! patch.connect(vco.out("saw"), vcf.in_("in")).unwrap();
28//! patch.connect(vcf.out("lp"), vca.in_("in")).unwrap();
29//! patch.connect(vca.out("out"), out.in_("left")).unwrap();
30//! patch.connect(vca.out("out"), out.in_("right")).unwrap();
31//!
32//! // The envelope shapes both the filter cutoff and the amplitude.
33//! patch.connect(env.out("env"), vcf.in_("cutoff")).unwrap();
34//! patch.connect(env.out("env"), vca.in_("cv")).unwrap();
35//!
36//! // Choose the output module, then compile before processing.
37//! patch.set_output(out.id());
38//! patch.compile().unwrap();
39//!
40//! // Advance the patch by one sample of stereo audio.
41//! let (_left, _right) = patch.tick();
42//! ```
43//!
44//! ## Feature Flags
45//!
46//! - `std` (default): Full standard library support including OSC, plugin wrappers,
47//!   visualization tools, and module development kit. Implies `alloc`.
48//! - `alloc`: Enables serialization (JSON save/load), presets, and basic I/O modules
49//!   for `no_std` environments with heap allocation (e.g., WASM).
50//! - `simd`: Enables SIMD vectorization for block processing (works with any tier).
51//! - `wasm`: WebAssembly bindings via `wasm-bindgen` and TypeScript types via `tsify`
52//!   (implies `alloc`).
53//!
54//! Without any features, the library operates in `no_std` mode with `alloc`,
55//! providing core DSP modules for embedded systems and WebAssembly targets.
56
57#![cfg_attr(not(feature = "std"), no_std)]
58// Enables the (nightly-only) `doc(cfg(...))` feature-badge attribute, but only
59// when building under the `docsrs` cfg that docs.rs sets (and that this
60// crate's own `#[cfg_attr(docsrs, doc(cfg(feature = "...")))]` annotations key
61// off of, see [package.metadata.docs.rs] in Cargo.toml). On an ordinary stable
62// build this line expands to nothing, so it does not require nightly locally.
63#![cfg_attr(docsrs, feature(doc_cfg))]
64
65extern crate alloc;
66
67// Conditional collection types: HashMap in std mode, BTreeMap in no_std
68#[cfg(feature = "std")]
69pub(crate) type StdMap<K, V> = std::collections::HashMap<K, V>;
70#[cfg(not(feature = "std"))]
71pub(crate) type StdMap<K, V> = alloc::collections::BTreeMap<K, V>;
72
73pub mod analog;
74pub mod combinator;
75pub mod graph;
76pub mod modules;
77pub mod polyphony;
78pub mod port;
79pub mod rng;
80pub mod simd;
81
82// Alloc-tier modules (work with no_std + alloc)
83#[cfg(feature = "alloc")]
84#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
85pub mod introspection;
86#[cfg(feature = "alloc")]
87mod introspection_impls; // ModuleIntrospection implementations for all modules
88#[cfg(feature = "alloc")]
89#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
90pub mod io;
91#[cfg(feature = "alloc")]
92#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
93pub mod observer;
94#[cfg(feature = "alloc")]
95#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
96pub mod presets;
97#[cfg(feature = "alloc")]
98#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
99pub mod scala;
100#[cfg(feature = "alloc")]
101#[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
102pub mod serialize;
103
104// Std-only offline rendering (WAV export).
105#[cfg(feature = "std")]
106#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
107pub mod render;
108
109// Std-only modules (require full std for network, plugins, etc.)
110#[cfg(feature = "std")]
111#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
112pub mod extended_io;
113#[cfg(feature = "std")]
114#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
115pub mod mdk;
116#[cfg(feature = "std")]
117#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
118pub mod visual;
119
120// WASM bindings (requires wasm feature)
121#[cfg(feature = "wasm")]
122#[cfg_attr(docsrs, doc(cfg(feature = "wasm")))]
123pub mod wasm;
124
125/// Prelude module for convenient imports
126pub mod prelude {
127    // Layer 1: Combinators
128    pub use crate::combinator::{
129        Chain, Constant, Contramap, Fanout, Feedback, First, Identity, Map, Merge, Module,
130        ModuleExt, Parallel, Second, Split, Swap,
131    };
132
133    // Layer 2: Port System
134    pub use crate::port::{
135        ports_compatible, BlockPortValues, Compatibility, GraphModule, ModulatedParam, ParamDef,
136        ParamId, ParamRange, PortDef, PortId, PortInfo, PortSpec, PortValues, SignalColors,
137        SignalKind,
138    };
139
140    // Layer 3: Patch Graph
141    pub use crate::graph::{
142        Cable, CableId, CompatibilityResult, NodeHandle, NodeId, Patch, PatchError, PatchMeta,
143        PortRef, ValidationMode,
144    };
145
146    // Core DSP Modules
147    pub use crate::modules::{
148        Adsr, Attenuverter, Clock, Lfo, Mixer, Multiple, NoiseGenerator, Offset, Quantizer,
149        SampleAndHold, Scale, SlewLimiter, StepSequencer, StereoOutput, Svf, UnitDelay, Vca, Vco,
150    };
151
152    // Phase 2 Modules
153    pub use crate::modules::{
154        BernoulliGate, Comparator, Crossfader, LogicAnd, LogicNot, LogicOr, LogicXor, Max, Min,
155        PrecisionAdder, Rectifier, RingModulator, VcSwitch,
156    };
157
158    // Phase 3 Modules
159    pub use crate::modules::{Crosstalk, DiodeLadderFilter, GroundLoop};
160
161    // Phase 4 Modules: Advanced DSP
162    pub use crate::modules::{
163        ArpPattern, Arpeggiator, ChordMemory, ChordType, FormantOsc, Granular, ParametricEq,
164        PitchShifter, Reverb, Vocoder, Wavetable, WavetableType,
165    };
166
167    // Remediation wave modules: sample playback (Q142), opt-in oversampling for
168    // nonlinear stages (Q143), mid/side utilities (Q150), sidechain ducker (Q148),
169    // and the canonical `Wavefolder` export (Q149).
170    pub use crate::modules::{
171        Ducker, MidSideDecode, MidSideEncode, Oversample, Oversampler, SamplePlayer, Wavefolder,
172    };
173
174    // Analog Modeling
175    pub use crate::analog::{noise, saturation, AnalogVco, ComponentModel, ThermalModel};
176
177    // Phase 3: Enhanced Analog Modeling
178    pub use crate::analog::{HighFrequencyRolloff, VoctTrackingModel};
179
180    // Phase 4: Polyphony Support
181    pub use crate::polyphony::{
182        AllocationMode, PolyPatch, UnisonConfig, Voice, VoiceAllocator, VoiceControl, VoiceInput,
183        VoiceMixer, VoiceState,
184    };
185
186    // Phase 4: SIMD and Block Processing
187    pub use crate::simd::{
188        AudioBlock, BlockProcessor, LazyBlock, LazySignal, ProcessContext, RingBuffer, StereoBlock,
189        DEFAULT_BLOCK_SIZE, SIMD_BLOCK_SIZE,
190    };
191
192    // RNG (no_std compatible)
193    pub use crate::rng::{Rng, SeedableRng};
194
195    // ========================================================================
196    // Alloc-tier exports (work with no_std + alloc)
197    // ========================================================================
198
199    // External I/O (works with alloc via core::sync::atomic + alloc::sync::Arc)
200    #[cfg(feature = "alloc")]
201    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
202    pub use crate::io::{AtomicF64, ExternalInput, ExternalOutput, MidiState};
203
204    // Introspection API (GUI parameter discovery)
205    #[cfg(feature = "alloc")]
206    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
207    pub use crate::introspection::{
208        ControlType, ModuleIntrospection, ParamCurve, ParamInfo, ValueFormat,
209    };
210
211    // Real-Time State Bridge (GUI live value streaming)
212    #[cfg(feature = "alloc")]
213    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
214    pub use crate::observer::{
215        calculate_peak_db, calculate_rms_db, GateDetector, LevelMeterState, ObservableValue,
216        ObserverConfig, StateObserver, SubscriptionTarget,
217    };
218
219    // Serialization (works with alloc via serde_json alloc feature)
220    #[cfg(feature = "alloc")]
221    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
222    pub use crate::serialize::{
223        CableDef, CatalogResponse, ModuleCatalogEntry, ModuleDef, ModuleMetadata, ModuleRegistry,
224        PatchDef, PortSummary, ValidationError, ValidationResult, CURRENT_PATCH_VERSION,
225    };
226
227    // Preset Library (works with alloc - just data structures)
228    #[cfg(feature = "alloc")]
229    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
230    pub use crate::presets::{
231        ClassicPresets, PresetCategory, PresetInfo, PresetLibrary, SoundDesignPresets,
232        TutorialPresets,
233    };
234
235    // Scala (.scl) microtuning parser (Q146)
236    #[cfg(feature = "alloc")]
237    #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))]
238    pub use crate::scala::{ScalaError, ScalaScale};
239
240    // ========================================================================
241    // Std-only exports (require full std)
242    // ========================================================================
243
244    // Extended I/O (requires std for network, plugins, etc.)
245    #[cfg(feature = "std")]
246    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
247    pub use crate::extended_io::{
248        AudioBusConfig, OscBinding, OscInput, OscMessage, OscPattern, OscReceiver, OscValue,
249        PluginCategory, PluginInfo, PluginParameter, PluginWrapper, WebAudioConfig,
250        WebAudioProcessor, WebAudioWorklet,
251    };
252
253    // Module Development Kit (requires std)
254    #[cfg(feature = "std")]
255    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
256    pub use crate::mdk::{
257        AudioAnalysis, DocFormat, DocGenerator, ModuleCategory, ModulePresets, ModuleTemplate,
258        ModuleTestHarness, PortTemplate, StateFieldTemplate, TestResult, TestSuiteResult,
259    };
260
261    // Visual Tools (requires std)
262    #[cfg(feature = "std")]
263    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
264    pub use crate::visual::{
265        AutomationData, AutomationPoint, AutomationRecorder, AutomationTrack, DotExporter,
266        DotStyle, LevelMeter, Scope, SpectrumAnalyzer, TriggerMode,
267    };
268
269    // Offline rendering / WAV export (requires std) (Q145)
270    #[cfg(feature = "std")]
271    #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
272    pub use crate::render::{render, render_to_wav};
273
274    // WASM bindings (requires wasm feature)
275    #[cfg(feature = "wasm")]
276    #[cfg_attr(docsrs, doc(cfg(feature = "wasm")))]
277    pub use crate::wasm::{QuiverEngine, QuiverError};
278}
279
280// Re-export the curated [`prelude`] at the crate root for convenience, so `use quiver::*`
281// (or `use quiver::Vco`) works for quick starts and examples — a common pattern for
282// batteries-included crates.
283//
284// This is intentional and additive: it does not hide the granular paths. If you prefer
285// tight imports and want to avoid pulling ~150 names into scope, keep importing directly
286// from the submodules instead — e.g. `use quiver::modules::Vco;`,
287// `use quiver::graph::Patch;`, `use quiver::port::SignalKind;`. Both styles remain
288// available; pick per call site.
289pub use prelude::*;