VOLTAreference

Reference / Samples and recording

Pin a print: keep a bounce as a song-local sample

Samples and recording

Open in the workbench →
On this page · 8 sections

Keep a bounceOf or sampleOf render you like as a file beside the song, so it plays exactly as it is however the source above it changes.

What a pin is#

A bounceOf / bouncesOf / sampleOf binding is a recipe: every load renders it again (Resample your own sounds, Create samples from synthesis). Pinning writes the installed render to the song's prints folder; from then on every host installs that file under the binding's bank key instead of rendering it. The source is never rewritten and never names the pin — the recipe stays authoritative for what the print is, and a pin is an override the bake consults.

  • Where. <song dir>/prints/<song stem>/<binding>.wav (32-bit float, stereo when the print is — bit-exact with the render, and authored-level peaks past ±1 survive) plus <binding>.print.json. The stem level keeps songs that share a folder apart. A bouncesOf generation is <binding>#<k>. prints/ is not a sample-bank root: a pin never registers as an ordinary !"name" sample.
  • The sidecar records the binding, kind (bounce / master / sample), the recipe key it was pinned against, the engine version, rate, frames, channels, the WAV's SHA-256, the binding's source excerpt, macros held (a take), and the time. A WAV whose digest no longer matches its sidecar is refused (the def renders from its recipe) rather than played under a stale identity.
  • A missing folder is today's behavior. A song shared without prints/ renders every recipe.

Current, stale, unknown#

Each def has a durable print key (offline::print_keys, and every bake_prints report): SHA-256 over the same typed recipe the bounce cache hashes, without the cache's version salt and without the sample rate, with every buffer read entering by name (a pinned upstream by its WAV digest, an unpinned one by its own key). So:

  • a pin taken against today's recipe is current; edit the recipe (or an unpinned upstream of it) and it is stale — and it keeps playing: a pin never silently re-renders;
  • a pinned upstream shields its descendants: pinning gen1 and editing the synth behind it leaves a pinned gen2 current;
  • an engine DSP change does not mark pins stale (the point of keeping audio), and the key is the same on every machine and in the browser;
  • unknown when a recipe cannot be identified: live input, hosted plugin state, or embedded song sources.

Limits of the key, by construction: a bank FILE replaced under the same name is not detected (buffers enter by name), and a change to the engine's typed plan encoding re-keys bounce defs (sampleOf keys hash only the kernel). Either way the pin keeps playing and Keep re-stamps it. PRINT_KEY_VERSION leads every key; engine/tests/pinned_prints.rs::a_print_key_is_a_fixed_value pins one.

The verbs#

VerbWhat happens
Pinwrites the installed print + sidecar (no second render)
Unpindeletes both (and an emptied folder); the recipe renders again
Keepre-stamps a stale pin's key to the current recipe; audio unchanged
Re-printrenders the current recipe and replaces the pin (old files stay until the render lands)
Pin while playingrenders the binding with every macro held where the knobs are now and pins that — a take; its sidecar lists the macros

A take is the one print a recipe cannot reproduce (bakes otherwise hold macros at their initials); Re-print would lose it. sampleOf reads no macros, so it has no take.

Where pins apply#

  • Studio (desktop). app-native/src/prints.rs::PrintHost loads the open song's folder on open and re-reads it when the folder changes (one stat per bake — a pin written by MCP or another window is picked up on the next Run). Every bake job takes the pin map (EngineHandle::bounce_bake_pinned), and the handle keeps it too (set_print_pins), because every load re-bakes sampleOf defs and a pinned one must install its pin there as well. The boot bake, device restarts and the Run bake all honor pins.
  • Export, measure, MCP renders. app-native/src/export.rs takes the pins (ExportSnapshot::pins, run_measure, render_span — the span cache key folds pin digests in). One-shot renders bake internally from the bank they are handed, so a pin also rides in the bank as pinned-print:<binding>@<sha> (prints::bank_entries); every bake merges those (prints::merge_bank_pins). Never installed in a live bank.
  • CLI. volta export stems and volta export video use the song's pins and say so; --no-prints renders the recipes.
  • MCP. list_prints (each def: pinned, current / recipe changed / unknown, take) and pin_print (pin / unpin / keep / reprint / take with explicit macros).
  • Sharing. The community feed and registry store text: the publish dialog and volta publish say how many pins stay on this machine; listeners hear the recipes.

In the editor#

Every bounceOf / sampleOf binding gets a chip at the end of its first line (ui/src/app/printchips.rs, painted by ui/src/editor/linechips.rs):

  • live — the print's length, ▶ (auditions the installed bank entry), pin, and pin playing (bounces only, and only where the shell can take);
  • pinned — ▶, unpin;
  • pinned · recipe changed — amber, with a gutter note that needs no hover ("playing pinned print — the recipe changed"), and re-print / keep / unpin; on a take, re-print is greyed and its tooltip says why;
  • unchecked (no key), rendering…, pin not loaded (unpin only).

A bouncesOf family shows one summary chip ("3 prints · 1 pinned ▾") whose menu lists each generation with its own verbs.

Finding prints across songs#

Sounds → Samples → Prints lists every pin in the workspace and the open song's folder (app-native/src/print_index.rs: a background walk, cached per folder by mtime). Search matches song, binding and the pinned source excerpt. A print from another song offers Preview, Use in this song (copies the WAV into this song's samples/ as <song>-<binding>.wav, suffixing on a collision, with the sidecar beside it as provenance, and inserts !"<name>") and Open origin (opens that song at the binding). A print from the open song offers Preview and Jump to binding. The bridge seam is separate from the bank listing (EngineBridge::print_index, audition_print, use_print) because a print is not a bank entry.

In the browser#

Pins live in IndexedDB (volta-song-samples v2, store prints, keyed scope\nbinding with the same sidecar JSON and float WAV bytes; runtime/public/studio-print-store.js), scoped like imports (liveDoc ?? songPath). loadIr runs module → assets → pins → wavetables → bake → report; a pin whose WAV digest no longer matches is refused. The four verbs run page-side with the desktop's semantics; an unsaved session cannot pin. Not yet: the Prints section (the store lists across scopes, nothing renders it), takes, and prints for bundled songs.

Where things live#

  • engine/src/prints.rs — print key hasher, sidecar + folder I/O, pin map, bank-carried pins, PinState.
  • engine/src/offline.rs — bake_prints, bake_prints_with_macros, print_keys, print_key_for; recipe/resources.rs::hash_resource_names.
  • engine/src/lib.rs — BounceBake::{run_report, macros}, EngineHandle::{bounce_bake_pinned, bounce_take, set_print_pins, print_keys}.
  • app-native/src/prints.rs (host state and verbs), app-native/src/main.rs (wiring, start_take/take_poll), app-native/src/mcp/prints.rs (tools), app-native/src/print_index.rs (the cross-song walk).
  • Tests: engine/tests/pinned_prints.rs, app-native/src/mcp/mod.rs::pin_print_*, cli/src/interchange.rs::export_stems_plays_pinned_prints_unless_told_not_to.