VOLTAreference

Reference / Design a sound

Draw a waveform with a function

Design a sound

Open in the workbench →
On this page · 3 sections

Define a waveform mathematically when a standard oscillator shape does not describe the sound you want.

What it is#

shape f is a user-defined waveshaper: a per-voice audio effect whose transfer function is any scalar lambda you can write —

cheby = \x -> 1.6*x - 1.2*x*x*x            -- 0.7·T₁ − 0.3·T₃
bass  = patch { out = sine (freq pitch) |> shape cheby }

-- inline, asymmetric (even harmonics):
lead = patch { out = saw (freq pitch) |> shape (\x -> if x > 0.0 then x else x * 0.3) }

The lambda is evaluated at compile time — by the Generate and transform musical structures evaluator — at 2049 points uniformly over [-1, 1], and the results are baked into a lookup table carried in the IR. The runtime never sees the lambda; it plays one waveshaper node. Arbitrary transfer laws at exactly the runtime cost of the stock drive.

Surface#

Signal-first / pipeable, like every per-voice effect (§11 of Language reference):

shape : Sig Audio Bipolar -> (Float -> Float) -> Sig Audio Bipolar

The function argument is either an inline \x -> … or a reference to a top-level lambda binding. Its body may use: numeric literals, arithmetic (+ - * /), comparisons, if/else if/case, let, tuples, and calls to other compile-time-evaluable lambdas — the evaluator's envelope (§14 of Language reference). The kernel math builtins fold here too: abs min max tanh sin cos floor sqrt exp ln pow (pinned by shape_accepts_math_builtins).

Errors#

  • Referencing anything dynamic (a signal, a macro, a note param) is a compile error with a span and, best-effort, the culprit's name: shape's function must be compile-time evaluable (numbers, arithmetic,if/case); it sawpitch``.
  • A non-function second argument: shape's second argument must be a function of one number.
  • A non-finite result at any bake point: shape's function must return a finite number; at x = ….

Gotchas#

  • Integer division truncates: 4/3 is 1 (language-wide semantics). Write float literals in transfer math: 4.0/3.0.
  • The bake domain is [-1, 1]; the runtime clamps input to that range before the table read (WaveShaperNode semantics). A hot signal (post-gain

    1. hits the table's endpoints — build the headroom into the curve, or

    keep the shaper early in the chain.

  • Outputs are clamped to [-4, 4] at bake time.
  • An asymmetric transfer (even harmonics) produces signal-dependent DC, same as drive with bias: the per-voice DC blocker absorbs it.

Aliasing note#

shape uses second-order MinusX ADAA in the native engine (2026-08 audit #1; originally first-order in circuit-fidelity Stage F), just like the stock saturators. The earlier claim — that a user table "has no closed-form antiderivative to lean on" — was simply wrong: the baked table is a piecewise-linear transfer, so its first antiderivative is an exact piecewise quadratic and its second an exact piecewise cubic. The engine bakes both node tables (table_ad, table_ad2) once at lower time, then at eval runs the three-point divided differences on the RESIDUAL f(x) − s·x (s = the transfer's measured slope at 0) and adds s·x[n−1] back — so the linear part passes as a pure one-sample delay with no HF droop (plain ADAA's boxcar response cost −6 dB at 16 kHz), the output is clamped to the table's value range, and the nonlinear residual keeps full alias suppression. Out of the [-1, 1] domain the LUT clamps f to a constant edge value, so F1/F2 extend linearly/quadratically there and the ADAA stays exact off-domain too. Cost: one sample of latency on the node (chowdsp documents the same for its ADAAWaveshaper).

Measured: on a hot 3.05 kHz sine through a hard-clip table, the 9th-harmonic alias drops well below the plain per-sample LUT read; on a slow ramp the ADAA output tracks the (one-sample-delayed) direct transfer to <1e-3. The suppression is comparable to ~4× oversampling for first order; a further 2×/4× oversample (upsample → table → halfband down) is the escalation if a very high-order transfer on a high lead still bites.