VOLTAreference

Reference / Work in Studio

Measure a kernel case by case

Work in Studio

Open in the workbench →
On this page · 7 sections

The kernel lab shows why a filter responds the way it does, one case at a time. Feed a DSP subject of your song an impulse, a step or a seeded noise burst, see what comes out, and click any output sample to see the filter's state just before and just after it, next to the line of your code that computed it. Sweep a parameter to see one measurement per value, or measure two parameters over a grid (the atlas). Every number is measured by running the engine's real insert path offline; the lab never plays a sound unless you press Audition.

Open the lab#

Put the caret on a process2/processS call or an effect declaration. The DSP PROBE dock appears in the right column; press open in kernel lab in it. The 05 · KERNEL LAB dock opens above the probe on that subject and stays open wherever the caret goes. Its head bar collapses it.

The subject row lists every DSP subject of the song: each effect declaration whole, and every kernel call of every other declaration, in source order. Pick one to measure it.

import "dsplib" (tpt1Law)

low = patch { source = saw (freq pitch) |> process2 (tpt1Law 0.0 800.0) { s = 0.0 } }
t = play low !"c3"

Try it. Open the lab on the process2 call, press run, then click the output a few samples in. Look for the state cell s before and after that sample, and lp + v (the expression that writes it) lit in dsplib's tab. Make it yours: choose step and primed, run again, and watch the state start from the primed level instead of 0.

One case#

Choose a stimulus (impulse, step or burst, a seeded uniform burst), its level (−18, −12 or −6 dBFS) and a reset policy:

  • fresh starts from the kernel's declared initial state;
  • primed starts from the state after 8,192 frames of a constant 0.5 input, replayed from the beginning every time (never a saved snapshot).

run measures 2,048 frames at 48 kHz off the audio thread (a worker on the desktop and in the browser), and cancel stops it at the next 4,096-frame step. The line above the plot also says what rate each kernel runs at, read from the compiled subject: runs at 1× (48 kHz), runs at 4× (192 kHz) for a kernel the song wraps in oversample 4, or runs once per block (375 Hz control rate) for a processC kernel. A kernel call written inside oversample N (…) is measured inside that wrapper, at the rate it plays. The plot draws the input faintly and the output over it. The line above it says measured, the whole recipe (stimulus, level, reset, parameter values, seed), the case's short digest and whether the response is level-bound: a nonlinear subject, or any primed case, behaves this way at this level and operating point only. A fresh case is called linear only when a half-level run scales exactly.

Trace a sample to its state and source#

Click the plot, or focus it and use ←/→ (Shift moves 16 frames) and ↵. The lab replays the case with one admitted trace point and shows, for that frame:

  • where it is in the run (run frames [n, n+1), in ms from the origin, so a primed case counts its priming);
  • the input and output;
  • each state cell before → after, beside its binder name and the next-state expression that writes it;
  • each kernel's rate; for an oversampled kernel one frame is N sub-samples and the state shown is after all of them, and a processC kernel's state moves only at a block's first frame;
  • each delay line (dline) as a strip of its most recent samples, newest at the left: before the frame as faint ticks, after it as solid bars. The line is named by its binder (buf, or st.line in a record), with the expression pushed into it, its write head before → after and how much of it is shown (showing 64 of 960);
  • the law's output expression, and the let equations between (named, not captured).

The same spans light in Source: the hot cell's binder and next-state expression at full strength, the rest outlined. Click a cell to jump to its expression; show jumps to the law. A law imported from a library opens in that library's tab. Spans light only in a buffer whose text is still the one the case was measured from.

A trace counts only when the traced replay reproduces the recording bit for bit. A subject whose result depends on where its blocks fall is refused as untraceable rather than shown with a guessed state.

A law that keeps its state as a record ({ env = 0.0, gain = 1.0 }) is traced the same way. Each cell is named by its field (st.env), in the order the initial state writes them, beside the expression the law gives that field. A field that st with { … } carries unchanged points at the with expression.

Sweep a parameter#

Pick a parameter (a literal argument of the law, an effect parameter or a macro the subject reads), a point count (5, 9 or 17) and a measurement (energy — output over stimulus energy, peak dBFS, or final value), then run sweep. The plot shows one measured case per point; ←/→ on the focused plot moves between them. The selected point shows its whole recipe:

  • replay case re-runs it, checks it reproduces, and opens it above;
  • audition renders one second of a 110 Hz saw through that case and plays it once, through the same one-shot path as the Studio's other auditions.

Atlas: two parameters over a grid#

The atlas row (shown when the case on screen has two or more parameters) measures every combination of two parameters. Pick x and y (two different parameters; choosing one for both swaps them), a size (3×3, 5×5 or 9×9, each axis spaced as a sweep spaces it — log for a frequency), and a measurement (by energy, by peak, by final), then run atlas. The stimulus, level and reset are the lab's current ones, and every other parameter keeps the value of the case on screen. Each cell is a complete case with its own recipe digest, measured exactly as run measures it; cancel stops between steps and keeps nothing.

The grid draws x across and y upward, one cell per case, shaded from faint (the grid's lowest value) to full (its highest); the two numbers are printed on the grid. The line above says measured, the measurement, the stimulus level and level-bound when any cell is. Click a cell, or focus the grid and use ←/→ (x) and ↑/↓ (y). The selected cell shows:

  • both parameter values, the measurement and the recipe digest, then the whole recipe;
  • its corner weights: how much of each corner of the grid the cell is, labelled with the corner's two values (20·0.1 0.56);
  • with a pin, the pinned cell's values and selected − pinned in dB.

pin holds the selected cell (unpin releases it). replay cell and audition cell are the sweep's replay case and audition. Selecting, pinning and comparing write nothing and play nothing.

adopt writes the selected cell's two values, and only when pressed:

  • both are literal arguments of the law (svfLaw 0.0 1000.0 2.0): one edit of the song buffer that rewrites both literals, one Undo step. It is refused when the song's text changed since the atlas ran (run it again) or when the edited song would not compile. The edit is a draft; run the song to hear it.
  • both are macros: both knobs move at once, as a live knob gesture; the source is not touched.
  • anything else (a literal with a macro, effect parameters, whose values are their declared defaults) is refused with the reason.

A host that measures fewer cases per job than the lab's cap cuts the grid as a prefix and says so: cut at the case cap: 5 of 9. No cell is ranked or chosen for you.

Stored cases and replay#

Every case you run is stored with the song as a recipe and its digests, not as samples, at most 64 per song. On the desktop it is .studio-lab/<song>.json beside the workspace (or ~/.volta/studio-lab/); in the browser it lives with the song's document and moves with it on rename.

replay re-runs a stored case on this build. It first compares what the case was measured with: the engine build, the subject's compile input (its code and every library it reads), the compiled subject, its state layout and any assets. If anything differs, it says what and runs nothing. If not, it runs and compares the output bit for bit: reproduced bit for bit, or not reproduced. An edit elsewhere in the song does not invalidate a case; an edit to the law or its library does. A case from another build is never called reproducible.

Limits#

  • Subjects that read samples, a live input or another track (a follower port) are refused: a case must be reproducible from its recipe alone. Oscillators and spectral kernels are measured by the probe dock instead.
  • 2,048 captured frames per case, 8,192 priming frames, 64 traced cells, 64 sweep points, 256 cases per atlas (a 9×9 grid is 81), one job at a time.
  • A delay line is shown as a window of its 64 most recent samples (at the kernel's own rate; an oversampled line holds sub-samples), never whole; a longer line says how much is shown. The left channel only, like cells.
  • Atlas cells, like sweep points, are not stored with the song.
  • Measurements are offline renders, not listening tests.