SUPERB
Primitives

primitives::automation

Parameter envelopes and DAW-style automation modes.

Purpose

primitives::automation depends only on the primitives root and primitives::realtime, and drives the same Arc<AtomicF32> handles that strip/node parameters already expose — no new parameter system required.

Key types

  • Envelope / EnvelopePoint / CurveShape::{Hold, Linear, Curve} — sorted breakpoints; sample-accurate evaluate / fill_block.
  • AutomationMode::{Off, Read, Touch, Latch, Write} plus AutomationState — touch/latch state machine.
  • AutomationLane — one param: envelope + mode + bound Arc<AtomicF32>. Call apply_block(block_start, frames, playing, scratch) once per audio block.
  • AutomationBank — several lanes applied together.
  • ParamTarget — implemented for AtomicF32 and Arc<AtomicF32>.

Usage

use std::sync::Arc;
use superb::primitives::automation::{AutomationLane, AutomationMode, EnvelopePoint};
use superb::primitives::{IdGenerator, TimePosition};
use superb::primitives::realtime::AtomicF32;

let ids = IdGenerator::new();
let gain = Arc::new(AtomicF32::new(1.0));
let mut lane = AutomationLane::new(ids.next(), Arc::clone(&gain));
lane.envelope_mut()
    .upsert(EnvelopePoint::linear(TimePosition::ZERO, 0.0));
lane.envelope_mut()
    .upsert(EnvelopePoint::linear(TimePosition::from_samples(100), 1.0));
lane.set_mode(AutomationMode::Read);

lane.apply_block(TimePosition::from_samples(50), 16, true, None);
assert!((gain.get() - 0.5).abs() < 1e-5);

Write / touch / latch:

# use std::sync::Arc;
# use superb::primitives::automation::{AutomationLane, AutomationMode};
# use superb::primitives::{IdGenerator, TimePosition};
# use superb::primitives::realtime::AtomicF32;
# let ids = IdGenerator::new();
# let gain = Arc::new(AtomicF32::new(1.0));
# let mut lane = AutomationLane::new(ids.next(), Arc::clone(&gain));
lane.set_mode(AutomationMode::Touch);
lane.begin_touch();          // UI grabbed the fader
gain.set(0.25);              // UI writes the live value
lane.apply_block(TimePosition::from_samples(0), 64, true, None);
lane.end_touch();            // back to reading the envelope

Real-time notes

  • Envelope edits (upsert, clear, record inserts) allocate — keep them off the audio thread, or throttle write-mode with set_write_interval_samples and accept that recording happens on a non-RT apply path / control thread.
  • The read path of apply_block only evaluates math and stores into atomics / a caller-provided scratch slice — no heap traffic.
  • Nodes that read their param once per block see the value at block_start. For true per-sample automation, pass a scratch buffer and have the node consume it.

Modes (while playing)

ModeEnvelope → paramParam → envelope
Offnono
Readyesno
Touchyes until touched; write while heldwhile touched
Latchyes until first touch; then write until stopafter first touch
Writenocontinuously

When not playing, every mode is a no-op for both directions.

See examples/automation_gain_ramp for an offline ramp printed to stdout.

On this page