SUPERB

Superb

An open-source Rust toolkit for building a digital audio workstation (DAW). One crate, superb, dual-licensed Apache-2.0 OR MIT.

Superb is the engine side of a DAW: sample-accurate time, audio buffers, a real-time-safe processing graph, a mixer with tracks, buses, sends and insert chains, MIDI, automation, the session's memory (tracks, clips, markers, routing, tempo) with undo, CLAP plugin hosting, recording, offline export, audio import, and audio device and file I/O. It has no user interface; you bring your own.

Two tiers

The crate is organised so you always know what a call will do.

Flows are top-level modules named after the things you work with and the tasks you do. Each one is a group of free functions that borrow a Session and do the whole job: session, track, clip, marker, transport, send, midi, record, metronome, export, devices. They are modules of functions, not classes: there is no object to construct and no method to look up, and the Session is a plain struct with public fields. Every flow function documents the primitive calls it is made of, so when one does not do exactly what you want, you call those primitives yourself.

Primitives live under superb::primitives and are the engine proper. They know nothing about flows or the Session.

flowwhat it doesdoc
sessioncreate, open and save a session; where files go (Root, Layout); import an audio file; the state file; tempo, meter and loop range; undo and redo; the engine side in session::stepssession.md
trackadd, archive, restore, rename, reorder, colour; mute, solo, arm, monitor, input, MIDI channel; volume, pan, route; meters; the mastertrack.md
clipaudio or MIDI on a track: add, move, split, trim, gain, fades, mute, rename, archive, restore; notes in a MIDI clipclip.md
markernamed places on the timeline: add, move, rename, removemarker.md
transportstart, stop, seek, rewind, loop; where the playhead istransport.md
senda copy of a track into a bus: add, remove, level, on/offsend.md
midiplay and release notes, send any message, connect a keyboardmidi.md
recordstart a recording, stop it, get a clip on every armed track, audio and MIDI; punch pointsrecord.md
metronomea click while the transport rolls, its level, bars of count-in before a recordingmetronome.md
exportbounce the session, or a range of it, to WAV, optionally resampled; every strip to its own file beside the mix, a stem set with a manifestexport.md
deviceslist audio and MIDI hardware; play through an output, switch outputs, open an inputdevices.md
modulein DAW termswhat it holdsdoc
primitives (root)shared vocabularysample-accurate time, beats, sample rate, IDs, errorsprimitives.md
primitives::buffersaudio buffersplanar and interleaved f32 blocks, gain and dB helpersbuffers.md
primitives::realtimereal-time helperswait-free queue, atomic floats, allocation counting, load meterrealtime.md
primitives::transporttransport barplay/stop/record state, playhead, tempo and meter map, loop rangetransport.md
primitives::midiMIDImessages, per-block event buffer, hardware input bridge (feature midi-io)midi.md
primitives::graphsignal paththe Node trait, the processing graph, silence propagation, latency compensation, live swappinggraph.md
primitives::graph::dynamicsdynamicscompressor, true-peak limiter with lookahead, gate and expander, de-esser, leveler, as nodesdynamics.md
primitives::mixermixertracks, buses, sends, insert chains, pan laws, mute/solo, peak metersmixer.md
primitives::automationautomationenvelopes, read/touch/latch/write modes, sample-accurate applyautomation.md
primitives::analysismeters, loudnesssample and true peak, RMS, LUFS (momentary, short-term, gated integrated), loudness normalisation, lining two recordings up, silenceanalysis.md
primitives::undoundo historycoalescing undo/redo stack with groups and clean-state trackingundo.md
primitives::sessionthe memoryMemory: tracks and their state, clips, markers, routing, inserts, tempo map, JSON save/load, the state file, undoable commandssession.md
primitives::pluginsplugin hostplugin instance model, PluginNode, CLAP host (VST3 is a stub)plugins.md
primitives::ioengine, devices, filesblock loop, live engine with control handle, offline render, capture queue, WAV, decode/resample (feature files), device streams (feature devices)io.md

docs/flows.md explains how the flows fit together (the Session, edits versus state, how the mixer follows the memory). docs/ARCHITECTURE.md covers the engine and the rules every module follows. If the DAW words are new to you, start with docs/TERMINOLOGY.md.

Reading the documentation

Each flow has one page under docs/flows/, and every page has the same shape:

  • Example: a full program using the flow. Start here.
  • Verbs: every function in one line each, with how it undoes.
  • Notes: the things that surprise people.

Beside it, docs/flows/<flow>.ref.md is generated from the source: every signature, what each argument is, what comes back, what fails, how it undoes, the primitive calls it is made of, and the flow's types.

A few conventions hold on every page, so they are stated here once:

  • A function that changes something takes &mut Session first and borrows the session for the call. A query takes &Session.
  • Tracks, clips, sends and markers are named by id (TrackId, ClipId, SendId, MarkerId). The id comes back from the function that made the thing, and the memory lists them (session.memory.tracks()).
  • A function that can fail returns Result<T>: Ok(T) or a SuperbError, so ? works. Its Errors line lists the mistakes you can make; a device or a file can always fail besides.
  • Edits (adding, moving, levels) enter the undo history. State (mute, solo, arm, monitor, input, looping, the click, the playhead) does not, and is kept across runs only once session::state_file has named a file.
  • Time on the timeline is in samples at the session's rate (TimePosition, TimeDuration); notes inside a MIDI clip are in beats from the clip's start.

cargo doc --no-deps --open builds the same text as rustdoc, with every signature linked. The same pages make up the documentation site that lives beside them in docs/: cd docs && bun install && bun dev serves it. docs/quick-start.md is the task index.

Every Rust example in the Markdown is a whole program, and cargo test --doc --features hardware,files compiles them all.

For an agent, /llms.txt is the index and /llms-full.txt is everything in one file, in reading order: the task index, each flow's page (a whole program) with its generated reference, the architecture, the terminology, the primitive contracts, the remaining work, and every example program in full.

Quick start

use superb::primitives::buffers::PlanarBuffer;
use superb::primitives::session::TrackKind;
use superb::primitives::{Result, SampleRate, TimePosition};
use superb::{clip, session, track, transport};

fn main() -> Result<()> {
    let folder = std::env::temp_dir().join("demo");                    // a session is a folder
    let mut session = session::create(&folder, SampleRate::HZ_48K, 256)?;
    let lead = track::add(&mut session, "lead", TrackKind::Audio)?;

    // Audio lives in memory: a decoded file, or a buffer you made.
    let mut tone = PlanarBuffer::new(2, 48_000);
    tone.fill(0.25);
    session.audio.insert("audio/tone.wav", tone);
    clip::add(&mut session, lead, "audio/tone.wav", TimePosition::ZERO)?;   // as long as the audio
    track::volume(&mut session, lead, -6.0)?;

    transport::start(&mut session)?;                                   // builds the mixer, starts playing
    let audio = session::steps::render_blocks(&mut session, 8)?;       // 8 blocks, no audio device needed
    println!("peak {:.3}", audio.peak());                              // 0.088: −6 dB, −3 dB at centre
    Ok(())
}

Every example is a short, commented program that runs without hardware unless hardware is the point:

cargo test                                     # the whole suite, no audio hardware needed
cargo run --example new_session                # session, track, clip: undo, save and open
cargo run --example play_session               # transport: start, seek, loop, offline blocks
cargo run --example mix_session                # track and send: faders, a bus, sends, meters
cargo run --example midi_keys                  # midi: notes into a monitoring track
cargo run --example record_take                # record: a recording from a synthetic microphone
cargo run --example metronome_count_in         # metronome: the click, and a count-in before recording
cargo run --example export_session             # export: bounce to WAV, whole or a range
cargo run --example bounce_stems               # export: where files go, a stem set with its manifest
cargo run --example list_devices --features devices

See examples/README.md for the full list, including one example per primitive.

Cargo features

Everything that touches hardware or a copyleft dependency is opt-in, so the default build and test run need nothing but Rust.

featureenables
clap (default)CLAP plugin hosting through clap-sys and libloading
devicesaudio input and output streams through cpal
midi-iohardware MIDI input through midir
hardwareboth of the above
filesaudio file decoding (WAV, AIFF, FLAC, ALAC, MP3, AAC in MP4/M4A, Ogg Vorbis, CAF, Matroska), resampling, import of any of them at the session's rate, and bounce helpers

Without files, session::import_audio reads WAV only, and only at the session's sample rate; anything else is an error that names the feature.

On Debian or Ubuntu the hardware features need pkg-config libasound2-dev.

Real-time discipline

Everything on the audio path is allocated in prepare and never afterwards. primitives::realtime ships a counting global allocator, and the integration tests under tests/ prove that process performs zero heap operations, including while parameters move and while a new graph is swapped in under a running engine. Mixer changes made through the flows are published as whole prepared graphs and swapped in between two blocks. docs/ARCHITECTURE.md spells out the rules.

Status

Working today, through the flows: the session's memory with tracks and their state, a master track that is always there, clips, markers and routing (tracks and clips are archived, never deleted), JSON files and undo; an opt-in state file for mute, solo, arm, monitor, inputs, looping, the click and the playhead; a mixer built from the memory with faders, buses and sends, swapped glitch-free into a running engine; tempo and meter changes the engine and the click follow; audio import in every format the decoder reads, resampled once to the session's rate, with a report of what came in; clip split, trim, gain and fades; notes added, changed and removed in MIDI clips; an opinionated folder layout under a root (~/superb by default); bouncing to WAV, and stem sets with a manifest; device output, output switching, and input on every channel the device has; recording audio and MIDI into clips, lined up by the device clock, streamed into the session folder as it happens (a crash costs a second, and what it left is recovered), with live counters, punch in and out and a metronome with count-in; hearing the input, and a synth on a monitoring MIDI track so incoming notes become audio; MIDI messages, a MIDI channel per track, and hardware input. In the primitives only, for now: insert chains and CLAP plugin hosting, automation envelopes and modes, analysis (true peak, LUFS, loudness normalisation, lining two recordings up by their audio, silence) and dynamics (compressor, limiter, gate, de-esser, leveler).

What is not built yet, and the order to build it in, is one list: DESIGN.md. The largest gaps are inserts and plugins through the flows, automation in the session, loop recording, and encoders other than WAV. ARA 2 plugin support is proposed in ARA2_DESIGN.md (shelved for now).

Licence

Apache-2.0 OR MIT, at your option. All code in this repository is original work written for Superb. Every dependency in the default build is Apache-2.0, MIT or ISC. The one MPL-2.0 dependency, the symphonia decoder, is opt-in behind the files feature and confined to a single source file; a test fails if it leaks anywhere else. See CONTRIBUTING.md for the policy.

On this page