Render a defined sound into a sample value and reuse that material in a different musical role.
Bake an audio buffer from a pure function of time at module load. The third member of the bake family: Resample your own sounds renders tracks, Wavetable synthesis builds tables, sampleOf renders functions. Design spec: Create samples from synthesis.
kickBuf = sampleOf 0.6s (\t -> sin (t * 6.283 * 55.0) * exp (0.0 - t * 9.0))
kick = patch { source = sample kickBuf, gain = vel }
ghost = patch { source = sample kickBuf |> pitched ~C2, gain = vel * 0.4 }
Surface#
sampleOf dur f : Str — a top-level binding only. The binding is the bank handle (the bounceOf trick): sample kickBuf and sampleAt [a, b] ix accept it directly; internally it names the reserved bank key bake:<binding>. The handle is an ordinary value from there on — rename it, import it, hold it in a record or a list, return it from a function, and every receiver that names a buffer still resolves the same entry (compiler/src/lower/handles.rs; the one exception is a comp slice's source, which resolves before the evaluator exists — see Recording and takes). Every sample verb works on the baked buffer — pitched, speed, loop, trim, reverse, chop/chopAt, slice/sliceAt, granular — and a bounceOf track may sample it (the compiler/hosts bake sampleOf defs first, so the composition is ordered).
dur— compile-time constant:0.6s,250ms, or cycles (1cfolds through the module tempo); a bare number is seconds. Must be positive; capped at 30 s (for long material,bounceOfa track).f— a one-parameter scalar lambda;tis seconds (a plain number — unit literals are rejected inside kernel math). The expression envelope isprocess's (Write a custom audio processor): arithmetic, comparisons +and,if(branchless select — both arms always run),let, the math builtins (abs min max tanh sin cos floor sqrt exp ln pow), and calls to other scalar lambdas (inlined). No macros — a baked buffer is static; referencing one is a compile error naming it. No state, no signals, no note params.- Output is NaN-flushed to 0 and clamped to [-1, 1] (the sample-bank contract — unlike
process's ±2 soft-limit).
A sampleOf buffer can be pinned like a bounce (Pin a print: keep a bounce as a song-local sample); it has no take, since its kernel reads no macros.
Recipes#
Phase-exact pitch sweep (kick) — integrate the frequency law, don't multiply it by t (that's FM warble):
-- f(t) = 48 + 332·e^(−t/τ) ⇒ φ(t) = 2π·(48·t + 332·τ·(1 − e^(−t/τ)))
kickBuf = sampleOf 0.5s (\t ->
let tau = 0.022 in
let phi = 6.2831853 * (48.0 * t + 332.0 * tau * (1.0 - exp (0.0 - t / tau))) in
tanh (sin phi * 2.2) * exp (0.0 - t * 11.0))
Deterministic noise — the shader hash:
snareBuf = sampleOf 0.35s (\t ->
let h = sin (t * 127843.0) * 43758.5453 in
((h - floor h) * 2.0 - 1.0) * exp (0.0 - t * 22.0))
Inharmonic partial sum (bells/rides): sum sin partials, each with its own decay. See examples/songs/rendered.volta (the rendered preset) for the full kit — kick, ghost (same buffer pitched down), snare, hat, ride, 4-second riser.
Under the hood#
Compiles to the process bytecode (ir::KernelOp) in an ir::SampleSynthDef { name, seconds, ops } (Module.sampleSynthDefs). One implementation bakes round(seconds · sr) frames at the host's own rate at load, before bounce renders: engine/src/offline.rs::bake_sample_synths (f32), called inside bake_bounces — so volta-studio, engine-wasm (the browser), libvolta and render_export all inherit it. The browser reaches it through WasmEngine::bake_bounces (engine-wasm/src/lib.rs); the second, f64 TypeScript bake was deleted with the TypeScript workbench on 2026-08-09.
Determinism: same rate → bit-identical everywhere.
Tests: compiler/src/lower/tests.rs (sample_of_*), engine/tests/sample_synth.rs; golden snapshot compiler/tests/snapshots/rendered.json.
Deferred (spec §5): stereo bakes, a seeded hash op, normalized-time binder, wavetableOf over bake: handles.