SUPERB
Flows

session reference

Generated from the source by scripts/reference.py. Do not edit this file. The short page, with the examples, is [session](session.md).

Reference

Every function of the session flow: what it takes, what it returns, what it refuses, and how it undoes. A Result<T> is Ok(T) or a SuperbError; the errors listed are the ones you can cause.

create

pub fn create( folder: impl AsRef<Path>, sample_rate: SampleRate, block_size: usize, ) -> Result<Session>

Starts a new, empty session at 120 bpm in 4/4 in folder, which is created with the folders of its Layout (audio/, bounces/, stems/) and given its memory.json at once, so it can be opened from here on. The session is named after the folder. Under the default Root that is session::create(Root::default().folder("My Show"), ..), which makes ~/superb/My Show/.

Takes

  • folder: impl AsRef<Path> — where the session lives, usually Root::folder(name); created now with audio/, bounces/ and stems/ inside; the session is named after it
  • sample_rate: SampleRate — the one sample rate everything in the session runs at
  • block_size: usize — frames per engine block, the largest block any node sees

Returns Result<Session> — the new Session, running an offline engine; nothing plays until devices::output or session::steps::render_blocks

Errors the folder cannot be created or memory.json cannot be written; a block size of zero.

Undo none: not an edit.

Made of create_dir_all per folder of the Layout → Memory::new → Session::new → Memory::save.

Example

let mut session = session::create(Root::default().folder("My Show"), SampleRate::HZ_48K, 256)?;   // ~/superb/My Show/

open

pub fn open(folder: impl AsRef<Path>, block_size: usize) -> Result<Session>

Opens a session folder written by save: reads memory.json and loads every WAV file the clips refer to into Session::audio. A file that is missing or at another sample rate is left out, and its clip plays as silence. The undo history starts empty and clean. Recordings a crash left unfinished are noted in the log, not placed: record::recover does that.

Takes

  • folder: impl AsRef<Path> — a session folder, one create made
  • block_size: usize — frames per engine block

Returns Result<Session> — the Session as saved: memory, routing, tempo, and the audio its clips refer to, read from audio/; state comes back only once state_file is named

Errors no memory.json in the folder, a file written by a newer version, or malformed JSON; a missing audio file leaves its clip silent rather than failing.

Undo none: not an edit.

Made of Memory::load → Session::new → wav::read per audio clip → record::unfinished.

Example

let mut session = session::open("~/songs/demo", 256)?;
session::state_file(&mut session, Some(session.folder.join("state.json")))?;

layout

pub fn layout(session: &Session) -> Layout

Where each part of the session lives inside its folder: memory.json, state.json, audio/, bounces/, stems/<name>/.

Takes

  • session: &Session — the open session, borrowed

Returns Layout — the session folder's Layout: memory(), state(), audio(), bounces(), bounce(name), stems(), stem_set(name)

Errors never; a query.

Undo none.

Made of Layout::new on Session::folder.

Example

let layout = session::layout(&session);
export::bounce(&mut session, layout.bounce("rough mix"), Bounce::default())?;   // bounces/rough mix.wav
session::state_file(&mut session, Some(layout.state()))?;

save

pub fn save(session: &mut Session) -> Result<()>

Saves the session into its folder: the memory as memory.json, after writing any audio a clip refers to that is not on disk yet (recordings already are). The history remembers this state as saved (session.history.is_clean()).

Takes

  • session: &mut Session — the open session, borrowed mutably

Returns Result<()> — nothing; session.history.is_clean() is true afterwards

Errors the folder or memory.json cannot be written; audio a clip refers to cannot be written.

Undo none: not an edit.

Made of wav::write per audio clip not yet on disk → Memory::save → UndoStack::mark_clean.

Example

session::save(&mut session)?;

import_audio

pub fn import_audio(session: &mut Session, file: impl AsRef<Path>) -> Result<Imported>

Brings an audio file into the session and says what it was. With the files feature any format symphonia decodes is read (WAV, AIFF, FLAC, ALAC, MP3, AAC in MP4 or M4A, Ogg Vorbis, CAF, Matroska) and resampled to the session's rate when it differs; without it, a WAV already at the session's rate. The audio is written into the folder as 32-bit float at the session's rate, audio/<name>.wav (<name> 2.wav when that is taken), and kept in memory; clip::add places it by the returned key. Not an edit: the file joins the folder and stays, placing it is the undo step.

Takes

  • session: &mut Session — the open session, borrowed mutably
  • file: impl AsRef<Path> — the audio file to bring in; WAV, AIFF, FLAC, ALAC, MP3, AAC (MP4/M4A), Ogg Vorbis, CAF or Matroska with the files feature

Returns Result<Imported> — an Imported: the key (audio/<name>.wav) a clip refers to the audio by, the file's own sample rate and bit depth, channels, frames, and whether it was resampled

Errors the file cannot be read or decoded; without the files feature, anything but a WAV at the session's rate.

Undo none: not an edit; placing the clip with clip::add is the undo step.

Made of io::files::decode → AudioFile::resampled_to (or io::wav::read_with_format) → folder::free_key → folder::write_audio → AudioCache::insert.

Example

let take = session::import_audio(&mut session, "/music/take.flac")?;
println!("{take}");   // audio/take.wav: 44100 Hz, 24-bit, 2 channel(s), resampled to 48000 Hz, stored as 32-bit float
clip::add(&mut session, vox, take.key, TimePosition::ZERO)?;

state_file

pub fn state_file(session: &mut Session, path: Option<PathBuf>) -> Result<()>

Says where state is kept, or that it is not. State (a track's mute, solo, arm, monitor and input, a clip's mute, a send's on/off, looping, the click and its count-in, the playhead) is never written into memory.json: until an application names a file here, a session opens with all of it off. With a path, the file is read now if it exists (its state lands on the memory, the mixer and the engine) and rewritten after every state change and every save, with the playhead. None stops writing; the file stays.

Takes

  • session: &mut Session — the open session, borrowed mutably
  • path: Option<PathBuf> — Some(path): keep state there, reading it now if it exists; None: stop keeping state

Returns Result<()> — nothing

Errors the file exists but cannot be read, is malformed or was written by a newer version; the file cannot be written.

Undo none: not an edit.

Made of Memory::load_state → Memory::apply_state → EngineHandle::send(SetLoopEnabled, Seek) → session::changed_structure; then Memory::save_state from session::set_state and save.

Example

session::state_file(&mut session, Some(session.folder.join("state.json")))?;   // read now, written from here on
session::state_file(&mut session, None)?;                                            // stop writing; the file stays

set_tempo

pub fn set_tempo(session: &mut Session, bpm: f64) -> Result<()>

Sets the memory's tempo at the start. Consecutive changes are one undo step. A ramp that begins at the start (tempo_change at beat zero with TempoChange::Ramp) stays a ramp: this moves where it begins. MIDI clips are scheduled through the tempo map, so the mixer rebuilds, and the rebuild hands the engine the new map.

Takes

  • session: &mut Session — the open session, borrowed mutably
  • bpm: f64 — beats (quarter notes) per minute

Returns Result<()> — nothing

Errors a tempo at or below zero.

Undo one undo step per drag: calls within Session::COALESCE_WINDOW (half a second) merge; rebuilds the mixer (at once while a device plays, otherwise at the next start).

Made of SetTempo at beat 0 (ramped when the start already ramps) through session::structural.

Example

session::set_tempo(&mut session, 100.0)?;

tempo_change

pub fn tempo_change( session: &mut Session, beat: BeatPosition, change: Option<TempoChange>, ) -> Result<()>

Adds a tempo change at beat, or removes the one there (None). TempoChange::Step(bpm) holds that tempo until the next change; TempoChange::Ramp(bpm) starts there and glides to the tempo of the next change, so a ramp is two changes: where it begins and where it arrives. Beat zero takes a change like any other beat, which is how a session begins with a ramp; it cannot be removed. Changes to the same beat coalesce. The click, MIDI clips and the bar and beat grid all follow. Rebuilds the mixer, which hands the engine the new map.

Takes

  • session: &mut Session — the open session, borrowed mutably
  • beat: BeatPosition — where the change takes effect, in quarter notes from the start
  • change: Option<TempoChange> — Some(TempoChange::Step(bpm)) holds that tempo until the next change; Some(TempoChange::Ramp(bpm)) starts there and glides to the next change's tempo; None removes the change

Returns Result<()> — nothing

Errors a beat before zero; a tempo at or below zero; removing the change at beat zero; removing a change that is not there.

Undo one step per beat; changes to the same beat coalesce; rebuilds the mixer (at once while a device plays, otherwise at the next start).

Made of SetTempo (ramped for a ramp) or RemoveTempo through session::structural.

Example

session::tempo_change(&mut session, BeatPosition::new(16.0), Some(TempoChange::Step(140.0)))?;   // faster from bar 5
session::tempo_change(&mut session, BeatPosition::new(16.0), None)?;                             // and back
// A ramp is two changes: where it begins and where it arrives.
session::tempo_change(&mut session, BeatPosition::ZERO, Some(TempoChange::Ramp(100.0)))?;        // from 100 at the start
session::tempo_change(&mut session, BeatPosition::new(32.0), Some(TempoChange::Step(140.0)))?;   // gliding to 140 at bar 9

meter_change

pub fn meter_change( session: &mut Session, bar: u32, signature: Option<TimeSignature>, ) -> Result<()>

Sets the meter (time signature) from bar on (Some), or removes the change at that bar (None); bar 1 always has a meter. Beats stay quarter notes whatever the signature, as in Logic and Cubase; the click ticks on the notated beat. Rebuilds the mixer, which hands the engine the new map.

Takes

  • session: &mut Session — the open session, borrowed mutably
  • bar: u32 — the 1-based bar the new meter starts at
  • signature: Option<TimeSignature> — Some(signature) sets the meter from that bar on; None removes the change there

Returns Result<()> — nothing

Errors bar 0; removing the meter at bar 1; removing a change that is not there.

Undo one undo step; rebuilds the mixer (at once while a device plays, otherwise at the next start).

Made of SetMeter through session::structural.

Example

session::meter_change(&mut session, 9, Some(TimeSignature::new(6, 8)?))?;

set_loop

pub fn set_loop(session: &mut Session, range: Option<LoopRange>) -> Result<()>

Sets or clears the loop range in the memory and on the running engine. Turn looping on with transport::looping.

Takes

  • session: &mut Session — the open session, borrowed mutably
  • range: Option<LoopRange> — Some(range) sets the loop range, told to the engine at once; None clears it

Returns Result<()> — nothing

Errors a range whose end is not after its start.

Undo one undo step per drag: calls within Session::COALESCE_WINDOW (half a second) merge.

Made of push SetLoopRange → EngineHandle::send(SetLoop).

Example

session::set_loop(&mut session, Some(LoopRange::new(TimePosition::ZERO, TimePosition::from_seconds(8.0, rate))?))?;
transport::looping(&mut session, true)?;

undo

pub fn undo(session: &mut Session) -> Result<bool>

Reverts the last edit. Returns false if there was nothing to undo.

Takes

  • session: &mut Session — the open session, borrowed mutably

Returns Result<bool> — true when a step was undone, false when there was nothing to undo

Errors a command fails to revert (should not happen).

Undo this is the undo.

Made of UndoStack::undo; the mixer is marked for a rebuild because the undone edit may have been structural.

Example

if session::undo(&mut session)? { println!("undid {:?}", session.history.redo_label()); }

redo

pub fn redo(session: &mut Session) -> Result<bool>

Re-applies the last undone edit.

Takes

  • session: &mut Session — the open session, borrowed mutably

Returns Result<bool> — true when a step was redone, false when there was nothing to redo

Errors a command fails to apply again.

Undo this is the redo.

Made of UndoStack::redo, then the same rebuild marking as undo.

Example

session::redo(&mut session)?;

Types

Session

A session: the memory you edit plus everything needed to play it. Plain record, public fields, constructors only. Every flow function takes &mut Session; reach into any field and call a primitive directly when a flow does not do exactly what you want.

Fields

  • memory: Memory — The memory: tracks, clips, markers, routing, tempo.
  • history: UndoStack<Memory> — Undo history for memory. Every flow edit pushes through this stack, so every edit is reversible. Consecutive edits of the same control (a fader, a knob, a clip being dragged) merge into one step while they arrive within Session::COALESCE_WINDOW of each other; a pause starts a new step. Call history.seal() to end a step sooner (on mouse up, say).
  • audio: AudioCache — Audio held in memory, keyed by the path clips refer to, relative to the session folder (audio/vox 1.wav).
  • folder: PathBuf — The folder the session lives in: memory.json and the audio/ folder of recordings are inside. Set when the session is created or opened; recordings are written there as they stop.
  • engine: EngineHandle — Control handle to the engine: commands, graph swaps, position.
  • audio_thread: Option<AudioThread> — The engine's audio side. Present until a device takes it (devices::output, feature devices); while present, steps::render_blocks ticks it by hand.
  • mixer: MixerHandles — The live controls of the mixer built by the last steps::rebuild: one per strip and send, keyed by the memory's ids.
  • recording: Option<crate::record::Recording> — A take being recorded, if any.
  • channels: usize — Channels per strip and at the output: 2, stereo. Change it before the first rebuild if you need another width.
  • output: Option<crate::primitives::io::LiveOutput> — The device output, once devices::output started one: the audio thread lives inside it until devices::close_output.
  • input: Option<crate::devices::AudioInput> — The audio input, once devices::input opened it: what monitoring audio tracks hear and device recordings capture.
  • midi_inputs: Vec<crate::primitives::midi::input::MidiInputBridge> — Hardware MIDI inputs connected so far. Dropping one disconnects it.
  • state_file: Option<PathBuf> — Where state is kept, once state_file named a file; None means state is not saved.

Methods

  • const COALESCE_WINDOW: core::time::Duration — How long a pause between two edits of the same control turns them into separate undo steps.
  • fn new(memory: Memory, folder: impl Into<PathBuf>, block_size: usize) -> Result<Session> — A stereo session around memory living in folder, with an engine that renders blocks of at most block_size frames at the memory's sample rate. Records the folder and touches nothing on disk; create and open do.

MixerHandles

The live controls of the mixer the engine is running, keyed by the track or send each was built for. Filled by every steps::rebuild. The Mixer itself becomes the graph the engine runs; what the control side keeps are these handles, whose atomics the audio thread reads every block, so a change made through them is heard at the next block. Handles from before a rebuild are stale: look them up again.

Fields

  • strips: Vec<(TrackId, Arc<StripParams>)> — A track or bus in the memory → its strip: volume, pan, mute, solo, pan law and the peak meter.
  • sends: Vec<(SendId, Arc<SendParams>)> — A send in the memory → its level and on/off.
  • click: Arc<ClickParams> — The metronome's click on the master: on/off, level, count-in.

Methods

  • fn strip(&self, track: TrackId) -> Option<&Arc<StripParams>> — The live strip of a track or bus.
  • fn send(&self, send: SendId) -> Option<&Arc<SendParams>> — The live controls of a send.

Imported

What import_audio brought in: the key a clip refers to the audio by, and what the file was, so an application can say "44.1 kHz, 24-bit, stereo, resampled". Whatever the file was, the session stores it as 32-bit float at its own rate.

Fields

  • key: PathBuf — audio/<name>.wav: the key a clip refers to the audio by.
  • source_rate: SampleRate — The file's own sample rate.
  • source_bits: Option<u32> — The file's own bits per sample when it has a fixed depth (16, 24, 32); None for lossy codecs such as MP3 and AAC, which have none.
  • channels: usize — Channels.
  • frames: usize — Frames as stored, at the session's rate.
  • stored_rate: SampleRate — The session's rate, which the stored audio is at.
  • resampled: bool — Whether the audio was resampled from source_rate to stored_rate.

Root

Where sessions live: one folder per session, side by side. ~/superb by default (Root::default); an application that keeps sessions elsewhere (another disk, a folder the user picked) names its own with Root::new. A root is only a path: nothing is made until a session is created in it.

Methods

  • fn new(path: impl Into<PathBuf>) -> Self — A root at path.
  • fn path(&self) -> &Path — The folder sessions live in.
  • fn folder(&self, name: &str) -> PathBuf — The folder a session called name has under this root, to hand to session::create or session::open. Characters a file name cannot hold (/ \ : * ? " < > |) become -.
  • fn sessions(&self) -> Result<Vec<PathBuf>> — The sessions under this root: every folder directly inside it that holds a memory.json, in name order. Empty when the root does not exist yet. # Errors The root exists but cannot be read.

Layout

Where each part of a session lives inside its folder. Every path is worked out from the folder; nothing here touches the disk. session::layout gives the one for an open session. use std::path::Path; use superb::session::Layout; let layout = Layout::new("/music/My Show"); assert_eq!(layout.folder(), Path::new("/music/My Show")); assert_eq!(layout.memory(), Path::new("/music/My Show/memory.json")); assert_eq!(layout.state(), Path::new("/music/My Show/state.json")); assert_eq!(layout.audio(), Path::new("/music/My Show/audio")); assert_eq!(layout.bounces(), Path::new("/music/My Show/bounces")); assert_eq!( layout.bounce("rough mix"), Path::new("/music/My Show/bounces/rough mix.wav") ); assert_eq!(layout.stems(), Path::new("/music/My Show/stems")); assert_eq!( layout.stem_set("final"), Path::new("/music/My Show/stems/final") );

Methods

  • fn new(folder: impl Into<PathBuf>) -> Self — The layout of the session folder folder.
  • fn folder(&self) -> &Path — The session folder.
  • fn memory(&self) -> PathBuf — memory.json: the session's edits.
  • fn state(&self) -> PathBuf — state.json: where the session's state goes once session::state_file names this file. Nothing writes it before.
  • fn audio(&self) -> PathBuf — audio/: what clips play, recordings and imports.
  • fn bounces(&self) -> PathBuf — bounces/: mixes.
  • fn bounce(&self, name: &str) -> PathBuf — bounces/<name>.wav: a bounce called name, for export::bounce.
  • fn stems(&self) -> PathBuf — stems/: stem sets, one folder each.
  • fn stem_set(&self, name: &str) -> PathBuf — stems/<name>/: the stem set called name, as export::stems writes it.

On this page