VOLTAreference

Reference / For contributors

Understand audio processing cost

For contributors

Open in the workbench →
On this page · 3 sections

Use cost N to compare patches before playback. Click the DSP cell of the transport readout (dsp N%) while playing to investigate measured engine load.

Static estimates#

The Bench and Patch Bay show each patch's relative per-voice cost, with a baseline oscillator worth about 1. This is not CPU percent per patch. A voice cap is the maximum allowed, not the number currently sounding.

One unit is calibrated against native render time: about 6.7 ns per output sample at 48 kHz on an Apple-silicon performance core (an optimized build; eight held saw voices, 1.8 units each, measure about 97 ns). A 48 kHz sample lasts 20.8 µs, so about 3,000 units fill one core. A busy machine measures about 1.5× slower, and the wasm engine about 1.5× slower than native on bowed voices (measured under Node, not in a browser).

The module budget bar adds up the voices at the song's busiest moment plus every track, return and master insert:

  • Voices per track are the worst overlap of its note windows (each note's length plus the synth's release, at the fastest tempo), capped by voices = N; a mono or glide synth is one voice. A synth whose release is not known statically (a plain sample) counts its sounding events.
  • Across tracks, the voices are priced at the onset where the most cost sounds together, not as if every track's busiest moment coincided — a fugue whose voices enter one by one is priced at its densest bar.
  • Scenes: tracks in mutually exclusive scenes never sound together, so an arranged song is priced by its busiest co-active scene.

Heavy modules receive an advisory DspLoad diagnostic; it never fails compilation, even under @strict. It fires past 1,500 units (about half a native core — the headroom a slower or busy machine, or the browser, needs) and says what share of one core the estimate amounts to; past 3,000 it says the module will likely glitch when played live, which is the point to freeze heavy tracks with bounceOf or render offline.

Typical weights (measured, not guessed): saw/sine/biquad ≈ 1; SVF ≈ 2; korg35 ≈ 10; the oversampled diode ≈ 35 and ladder ≈ 50; a plucked string ≈ 3; modal ≈ 0.5 per mode; a 2-D mesh ≈ 0.155 per junction; stringBody ≈ 2 (default tier) or ≈ 12 (…Fine, …Stereo), +4 morphing (to / morph / size, at the larger tier); bowed ≈ 43 for a bare bow, plus its options. reverb/plate ≈ 85, spring ≈ 55, kPlate ≈ 36, shimmer ≈ 10, galactic ≈ 8; spectral effects ≈ 60; convolution ≈ 80.

Bowed strings#

A bowed voice is the dearest voice in the palette: its junction runs sub-steps at about 192 kHz (4 per sample at 44.1/48 kHz, 2 at 96 kHz) with torsion. Measured at 48 kHz, in units:

units
bare bowed43
+ stiffness+3
+ sympathy (coupled bridge)+7
+ touch (finger)+11
+ fingerNoise+2.5
+ strings (string choice, crossed strings ringing)+4 (from wall-clock deltas, not a dedicated bench)
+ noise, mute+0.5 each
+ body default tier / …Fine+7 / +14
+ a morphing or resized body (bodyTo / bodyMorph / bodySize)+5, the body priced at the larger of the two tiers
+ pickup = "magnetic" / "piezo" (replaces the body)+8 / +0.5
+ scale = "giant" (about 1 MB of delay line per voice)+0.5
bowstrings standard voice (e.g. violin)≈ 89
deluxe voice (violinDeluxe)≈ 165
…DeluxeTutti (three players)≈ 270

A section's players step together in lanes (see the physical-modeling reference, "Section players run as lanes"): each uncoupled bow in a voice with three or more of them is priced 12 units less (measured ≈ 86 ns per player per sample saved at 48 kHz). Bows on a shared bridge step together only when three or more of the synth's voices sound, so they keep their full price. Sums and products evaluate input by input over the block and cost a quarter of another scalar node (0.05).

At 96 kHz the bow costs about half as much per output sample (the same per second), while everything else costs the same per sample — twice as much per second. Estimates are priced at 44.1/48 kHz.

How close it lands: across the four bowed songs the worst second of the arranged render measured the same time per estimated unit within ±5 %, the estimate erring about a third high; across other songs that ratio ranged about 2× either way (filters, effects and patterns vary more). The weights live in compiler/src/cost.rs; estimator_cost_table and estimator_song_table in engine/tests/bowstrings_library.rs re-measure them (--ignored, --profile fast).

Measured DSP load#

The hero readout's DSP cell (a meter plus the percentage, announced as dsp N%) measures the whole loaded engine against its audio deadline. At 48 kHz, a 128-frame block has about 2,667 µs. Sustained 100% leaves no time to meet that deadline. The tooltip includes a two-second peak of the displayed rolling average; this is not a worst-callback spike detector.

Click the readout in desktop Studio to open DSP breakdown, sorted by cost:

  • Voices: synthesis and scheduling, per track on routed songs.
  • Controls: shared expressions, macros and analysis such as pitch tracking.
  • Inserts: each routed track insert, with its owner and slot number.
  • Returns / Master / Mix: return effects, master processing and channel strips.
  • Other engine work: total time outside the reported stages.

Filter by a track name such as airT, or a category such as Returns. The whole-engine total stays visible. Each row reports rolling µs/block and percent of the deadline; shared controls count once in their own rows. The Bench's live track percentage remains channel strip + inserts, excluding voice synthesis.

Playing one track does not unload the rest of the song. Shared controls, returns, master processing and other tracks' effects can still run. A stopped track label means its note scheduling is stopped; effect tails or remaining voices can still consume processing. Lowering its voice cap may have little effect on that baseline.

Songs without staged routing report aggregate voices and per-track mix/inserts. Individual voice nodes are not timed. Detail is bounded to 512 stages; any remaining work stays in the total and the Other row. The browser audio clock currently has millisecond resolution, so its popup explains why reliable small stage timings require desktop Studio.

Implementation#

compiler/src/cost.rs annotates Synth.cost and Module.cost in IR; ui/src/intel.rs joins them to the authoring views. The native engine times render work with Instant, smooths it with an exponential moving average, and publishes fixed arrays through a triple buffer. Labels and sorting happen on the control/UI threads. Module identity fences prevent old timings being shown under a newly loaded song's names. The shared EngineBridge::perf_frame passes this data to Studio, the headless harness, browser and plugin shell.

Further calibration, per-node attribution and worst-case measurements remain in Understand audio processing cost.