Primitives
Foundation types shared by every module of the crate. No dependencies, no I/O.
Every public method is in rustdoc (cargo doc --no-deps). These pages are
the contracts: which thread a call runs on, what is state and what is an
edit, which feature flag it sits behind, and what is not built yet. Start
at quick-start.md to pick a function.
Purpose
The root of superb::primitives is the bottom of the engine: every other
module depends on it, and it depends on nothing else in the crate. Everything
here is Copy-friendly (except the rarely-used SuperbError::Message(String)
variant) and safe to touch on the audio thread — it defines the vocabulary
(time, sample rates, IDs, errors) that the rest of the SDK builds on, but does
no scheduling, buffering or I/O itself.
Key types
SampleRate— validated integer Hz (1_000..=1_536_000), withf64and seconds/samples conversion helpers.TimePosition/TimeDuration— signed, integer sample-accurate audio-domain position/duration (signed so pre-roll is representable).BeatPosition/BeatDuration— musical position/duration in quarter notes (f64); independent of sample rate.TimeSignature— validatednumerator/denominatormeter withbar_lengthandbeat_lengthhelpers.IdGenerator/TrackId,BusId,SendId,NodeId,PortId,ClipId,MarkerId,ParamId,InsertId— monotonicu64newtype IDs,0reserved as "invalid".RawIdis thatu64.SuperbError/Result<T>— the shared error enum used across the SDK.
Usage
use superb::primitives::{SampleRate, TimePosition, BeatPosition};
let rate = SampleRate::new(48_000).unwrap();
let half_second = TimePosition::from_seconds(0.5, rate);
assert_eq!(half_second.samples(), 24_000);
assert_eq!(half_second.to_seconds(rate), 0.5);
// Beats are independent of sample rate; the tempo map in
// `primitives::transport` converts between the two domains.
let two_bars_of_4_4 = BeatPosition::new(8.0);
assert_eq!(two_bars_of_4_4.beats(), 8.0);use superb::primitives::{IdGenerator, NodeId};
let gen = IdGenerator::new();
let a: NodeId = gen.next();
let b: NodeId = gen.next();
assert!(a.is_valid());
assert!(b > a);Real-time notes
Every type here is stack-only (Vec/String-free except
SuperbError::Message, which is only ever constructed off the audio thread)
so arithmetic on TimePosition, BeatPosition, SampleRate etc. never
allocates. IdGenerator::next_raw is a single lock-free AtomicU64::fetch_add
(src/primitives/ids.rs, proven single-threaded and cross-thread by
generator_is_monotonic_and_never_yields_invalid and
generator_is_unique_across_threads), so IDs may be minted from any thread,
including the audio thread. There is no allocator dependency to prove
no-alloc against here — that guarantee is established downstream in
the tests/graph_no_alloc.rs integration test, which exercises these types
as part of a real process call.
Limitations / next
TimePosition/TimeDurationarei64samples — no explicit overflow checking beyond whati64arithmetic gives you.BeatPosition/BeatDurationaref64; no rational/tick-exact representation.- Converting between the audio and musical domains requires a tempo map,
which lives in
primitives::transport, not here. IdGeneratornever reuses IDs and has no persistence story of its own;primitives::sessionkeeps its own ID counter and saves it with the memory.