VOLTAreference

Reference / Design a sound

Vowels, voice and pitch effects

Design a sound

Open in the workbench →
On this page · 6 sections

Shape vocal-like resonances and process pitched material with Volta's voice and pitch toolkit.

What it is#

The pro vocal chain as track effects: monophonic autotune (tune), a formant knob that works on any bus including samples (formantShift), a scale-aware harmonizer (harmonize), a de-esser (deess) — plus pitch-mapped chops (B4): pitched composes with chop/slice, so a chopped vocal spells a per-slice melody from its note pattern.

Surface#

key = "c minor"
snap = macro "snap" 0.3

lead = play vox mel
  |> tune snap 12            -- strength (Param) [+ retune speed, ms]
  |> harmonize [2, 4] 0.4    -- 1..4 diatonic degrees (const) + wet mix (Param)

texture = play pad chords |> formantShift (0 - 4)   -- ±12 semis (Param)

chops = patch { source = sample "ade" |> slice 8 |> pitched ~C4,
                ampEnv = adsr 1ms 300ms 0.6 200ms, gain = vel * 0.6 }
line  = play chops !"c4 eb4 g4 c5"   -- slices advance per note, grains repitch

mixer { master = { inserts = [deess 0.5] } }   -- amount [+ crossover Hz]

All four are legal on tracks AND as bus/master inserts (the mixer grammar's harmonize takes a single degree — it has no list literal).

  • tune strength [speedMs] — snaps the tracked f0 to the module key (chromatic when unkeyed), formant-preserved. strength ∈ [0, 1] is macro-ridable (autotune as a performance knob); speed defaults 40 ms, small = the zipper. Speed accepts composed controls in milliseconds, read at the block endpoint without rebuilding tracker or shifter history.
  • formantShift semis — envelope-only warp, ±12. A macro's [0, 1] travel maps across ±12 (0.5 = no shift, the tilt convention).
  • harmonize [degrees] [mix] — up to 4 voices at scale-step offsets from the tracked f0 (semitones when unkeyed), formant-preserved, alternately panned (odd degrees slightly L, even slightly R), 1/√n headroom on the wet sum. Dry always passes; mix defaults 0.7.
  • deess amount [freqHz] — program-relative, band-limited de-esser. freqHz (default 6 kHz, clamped 2–12 kHz) is the BOTTOM of the sibilance region; the detector and the cut sit on the band centred at freq · 1.118 (≈ 5–9 kHz at the default). amount is macro-ridable and moves the threshold/ratio/range only — never a filter coefficient. See The de-esser, in detail below.

The de-esser crossover accepts composed controls. A 5 ms smoother feeds a persistent 16-sample coefficient clock; body, sibilance and gain histories stay running. These settings use the same track/return/master control graph contract as other inserts.

The de-esser, in detail#

Rebuilt 2026-08-03 against findings C1 + C6 of Dsp Quality Audit 2026 08 03, and repaired the same day against the adversarial review of that rebuild (D1–D5 — the gain computer's body-presence crossfade, the detector's published numbers and the cold start). Four properties define it. Every number below was measured through the real engine with deess on the master, at program levels clear of the master safety limiter's knee.

1. The threshold comes from the program, not from full scale. A slow follower on the low band gives the body's level and the sibilance threshold sits 6 + a·24 dB under it. The pre-audit curve was an absolute −12 − a·24 dBFS on the high band, so the ess band alone had to peak within 24 dB of full scale before anything happened — at any realistic level the effect was a no-op. Measured (1 kHz body, 7 kHz ess 12 dB under it, deess on the master, ess attenuation in dB):

bodyamount0.250.50.81.0
−16 dBFSbefore the audit0.000.001.423.80
−16 dBFSafter0.304.3110.5814.40
−26 dBFSbefore the audit0.000.000.000.00
−26 dBFSafter0.304.3110.5814.40

The two "after" rows are identical to the last digit: the knob now does the same thing at any program level, and stays identical down to a −46 dBFS body (see Level floor below). It still discriminates by ratio — at amount 0.5 an ess 12 dB under the body gets 4.31 dB and one 20 or 30 dB under gets 0.00 dB. (The rebuild's first cut published 0.59 / 5.01 / 11.24 / 14.95 on this row; the review's repair holds the program reference at the body's peak rather than letting it release between crests, which raises the threshold by about 0.6 dB. Same targets, ~0.6 dB gentler.)

2. The reduction is monotone in the ess. At a fixed body, a louder ess is never reduced less — the property a de-esser needs to be a dynamics processor at all. The rebuild's first cut crossfaded to its no-body fallback on the relative program − sibilance, which put the ess on both sides of the threshold: raising the ess raised its own threshold faster than it raised the detector, so the reduction peaked 6–12 dB under the body and fell away above that — a 1.5:1 upward expander over the loud half of the range. The crossfade is now keyed on the body alone (§ No body, no problem). Measured, ess attenuation in dB against how far the ess sits under a −34 dBFS body:

ess is … under the body−12−60+6+12+18+24+30
deess 0.5, before3.933.934.237.055.010.630.000.00
deess 0.5, after16.2016.2013.689.214.310.320.000.00
deess 1.0, before5.505.506.0813.2214.9510.455.310.66
deess 1.0, after18.9318.9318.9317.7514.409.774.580.34

The plateaus at the hot end are the 12 + a·12 dB range cap, not the gain computer giving up. And because the reference is held across a phrase, a constant "sss" gets a constant reduction: 1.2 s of steady 7 kHz after a 0.6 s vowel measures 4.91 / 4.91 / 4.91 / 4.91 / 4.92 / 5.61 dB at 0.2 s intervals (before: 6.13 → 7.00 → 4.04, sliding as the body decayed).

3. The detector hears only the sibilance region. The sidechain is a bandpass cascade (Q 1.08, applied twice ⇒ 12 dB/oct skirts) at the band centre, normalized to unity there — not "everything above freq". Measured off the shipped coefficients, the cascade's half-power span is [0.858, 1.431]·freq (5.1–8.6 kHz at the default; 44.1 kHz gives [0.862, 1.421]), and through the whole HP4 · BP² detector path an 18 kHz partial arrives −29.1 dB down at 48 kHz / −33.4 dB at 44.1 kHz — 11 kHz −9.5 dB, 12 kHz −12.2, 14 kHz −17.6. (A single bandpass section would manage only −14.7 dB at 18 kHz; that single-section figure was what this page published as "16 dB down" before the review.) Because the depth of the cut is decided by that band-limited detector alone, an out-of-band partial can never duck the sibilance band harder than an in-band one — measured with a quiet 6708 Hz probe plus one equal-level partial at deess 1:

partial6 kHz8 kHz11 kHz14 kHz18 kHz
band ducked, before6.256.8415.4013.407.63
band ducked, after24.0024.0018.7312.726.90

4. The reduction is a bell, not a shelf. The gain is applied to the high band as 1 + (g−1)·BP(z) — algebraically RBJ's peaking EQ with a fixed pole Q (|H|² = ((1−w²)² + (g·w/Q)²)/((1−w²)² + (w/Q)²)), so the coefficients never change and only one scalar moves per sample. Measured with deess 0.8 on a 1 kHz body plus ONE equal-amplitude HF partial (an "ess" as loud as the body — the range cap's own worst case):

partial7 kHz10 kHz14 kHz18 kHz
before the audit−4.80−9.62−11.51−11.85
after−18.05−2.83−0.52−0.00

Before, the further a partial sat from the sibilance region the deeper it was cut — a loud hi-hat took 12 dB of air out of the mix while the ess it exists for was the least attenuated thing in the band. After, the cut peaks where the ess is and lets the air through.

Supporting details:

  • The LR4 split stays, moved down to freq · 0.5. The low band is the body's own untouched path — the 1 kHz body measures 0.00 dB in every configuration including full reduction — and the split sits far enough below the bell that its leak-through (−30.12 dB at the band centre, 48 kHz) cannot cap the cut. That residual is compensated, so the dialled reduction is what the recombined output shows. The body tracker reads the low band through one more LP4 (an 8th-order body reading, detector-only): the split's own skirt passes a 7 kHz tone at −31.8 dB, which would read as a −48 dBFS "body" under a −16 dBFS hi-hat and defeat the no-body fallback; at −63.5 dB it cannot.
  • Ballistics. Detector 1 ms attack / 60 ms release through the shared log-domain GrSmoother (The effects reference § The detector shared by compress / mbcomp / fet / deess); the program tracker integrates the squared low band with a 10 ms attack, a 1.5 s hold and a 1 s release, which parks it at the band's peak level with the waveform ripple 27 dB down. The hold is re-armed by every crest that exceeds the tracked level, so "the body is still there" needs no separate detector — and a body dropout mid-phrase can no longer slide the threshold out from under a steady ess. Both asymmetries are the safe ones: a stale-high reference *under*-de-esses, and a rising body is caught in 10 ms. Measured ess-band ripple on a steady body: 0.008 dB at 60 Hz, 0.008 at 100 Hz, 0.006 at 220 Hz — no pumping.
  • Cold start. reset() (bypass-engage, chain rebuild, the top of a take) leaves the program trackers empty, and a threshold read off an empty tracker is a threshold at the floor. The de-esser is therefore transparent for 24 ms after a reset while the trackers peak-hold the program — 3 ms of that window is spent letting the crossover settle, so the edge at the top of a take is not mistaken for a body. Onset reduction now measures 4.94 dB peak against 4.94 dB steady (before: 11.34 dB peak against 5.16 steady, a ~40 ms swell-in).
  • No body, no problem. On HF-only material (hats alone, a cymbal tail) the low band vanishes and a purely program-relative threshold would collapse to the floor. The threshold therefore crossfades to the sibilance band's own slow average, margin = 3 + a·3 dB under it, as the body itself falls through a 12 dB window above the −60 dBFS reference floor — never as a comparison against the sibilance level, which is what made the reduction non-monotone. A 7 kHz tone alone gets 3.13 dB at deess 0.5 at −16, −26 and −36 dBFS alike — gentle, bounded and level-independent — instead of annihilation. The reduction is additionally capped at 12 + a·12 dB.
  • Level floor. "Level-independent" holds while the body sits above −48 dBFS (the top of that window). At a −56 dBFS body the crossfade has started and the table collapses to 0.24 / 2.53 / 6.89 / 9.80 dB. That is the reference floor doing its job, not a bug: below it there is no program left to be relative to.
  • Stereo-linked (detect on the louder channel, one gain to both) and latency-free (biquads only: crossover, sidechain, bell).
  • The bench dynamics meter now reads deess's reduction too (at the bell centre), alongside compress/optical/fet/mbcomp/ott/limiter.

IR#

TrackEffect::Tune { strength: Param, speed_ms }, FormantShift { semis: Param }, Harmonize { degrees: Vec<i32>, mix: Param }, Deess { amount: Param, freq } (ir/src/ir.rs). Chop-pitch is not new IR — NodeKind::Sample already carries root_midi + slices; the lowering simply lets pitched fold onto a chopped/sliced sample (the stretch family stays mutually exclusive with both).

How it's implemented#

  • Compiler: compiler/src/builtins.rs (tune/formantShift/harmonize/deess) and compiler/src/lower/track.rs's lower_vocal_fx — the one lowerer track pipes, buses and master inserts all reach (harmonize's const degree list is validated 1..=4 entries, each in −14..=14).
  • Engine: the DSP cores live in engine/src/vocal.rs — the per-block-variable-ratio PitchShifter (1024-point Hann analysis, envelope preserve/shift via a log-domain true-envelope estimator) and the scale-snapping Tuner (shared McLeod tracker). The shifter is resampling + time-scale modification, not spectral translation: the phase vocoder only re-times the signal and a fractional read head does the pitch move (see Shifting core under Notes & limits) — assembled per effect in engine/src/effect.rs (TuneFx, FormantShiftFx, HarmonizeFx, Deess; the de-esser reuses the in-file Lr4 crossover and drives a GrSmoother off its own band-limited sidechain — it no longer wraps a Compressor, whose gain computer knows nothing about a moving threshold). The module key reaches the chains via CompiledModule.key → TrackFx::build.
  • Browser: nothing separate to port. The legacy Web Audio DSP was deleted with the compat path — the workbench runs this same Rust engine through engine-wasm/ in an AudioWorklet, so every effect here is the code above on every target by construction.
  • Chop-pitch: engine/src/voice/eval_sampler.rs multiplies the slice/grain read rate by 2^((note − root_midi)/12) when root_midi is set, exactly like the grains path.

Notes & limits#

  • Latency: tune/formantShift/harmonize carry the STFT family's group delay, and since the 2026-08-05 shifting-core rebuild that delay rides the pitch ratio — the phase vocoder's output stream is played back ratio times as fast, so the same stream position arrives sooner the further up you shift. Measured through the real 128-frame block path with a click: a smooth ramp across the ratio range (click peak, 128-frame blocks, 48 kHz):

    ratio0.50.751.01.061.191.261.502.03.0
    samples15441250107910421009934867757701
    ms32.226.022.521.721.019.518.115.814.6

    vocal::LATENCY (768) remains the published nominal. In practice this only matters for harmonize, which ADDS its shifted voices to the dry bus: the shipped harmonize [2, 4] in a minor key runs its two voices at ratios 1.1892 and 1.4983, i.e. 1009 and 867 samples — 2.9 ms apart, where before they were equal. (An earlier draft of this page said "up to ~5 ms"; that was an estimate, and it was roughly double.) It is not yet PDC-reported — deferred with the native-effect-latency item (TODO). deess is latency-free.

  • Realtime budget (post the 2026-07-20 static fix — callback starvation, not a DSP artifact): the tracker's NSDF runs on 2:1-decimated input every 4th block (~10 ms cadence, ~85 ms analysis window), and the shifter's input FIFO is an O(1) ring. cargo bench -p volta-engine --bench dsp -- vocal_ carries the sweep (vocal_shifter_1s_unity / _r119 / _r150 / _r200), because a single figure is not a description of this core: it is a time-scaler plus a resampler, so its cost RISES with |ratio − 1| while the spectral-translation core it replaced was flat.

    Measured min-of-40 and normalized by the tuner, which neither C2 nor the D1 band limit touched — the reference machine is shared, so absolute ms/s are not comparable across runs and a ratio to a fixed workload is:

    shifter cost, in "tuners"ratio 1.01.191.502.0
    translation core (pre-2026-08-05)0.0720.0740.0710.068
    resampling core0.0390.0560.0380.043
    + band-limited read0.0390.0650.0440.051

    So the resampling core is cheaper than the one it replaced at every ratio measured (FormantMode::Off no longer needs an envelope at all, and the OLA is normalized once per stream sample instead of four times per read), and the band limit costs 1.15–1.18× at ratios ≠ 1 and exactly 1.00× at unity (interleaved in one process, which is the only load-proof comparison — measured +2.4 to +2.7 ms/s on a loaded machine). The tuner itself is ~68 ms/s when quiet; a full tune |> harmonize [2,4] chain ≈ 0.15× realtime (was 0.84× before the 2026-07-20 static fix — audibly static under live load). A regression test (harmonize_stays_clean_under_vibrato) also bounds broadband hash and the rest-floor.

  • slice boundaries: slice/sliceAt ask for onset-detected boundaries, which reach the engine as a slice-bounds table (EngineHandle::resolve_slice_bounds → Command::InsertSliceBounds, consumed in engine/src/voice/eval_sampler.rs); with no table yet the slice falls back to the equal-division grid chop uses. The CLI stems exporter loads --samples flat (no bank subdirectories), so bank-nested samples don't reach it.
  • Shifting core (2026-08-05, DSP audit-3 §C2 — replaces the 2026-08-01 §E.4 peak-translation core): resampling + TSM, the DAFx-21 paper 11 §2.2 architecture. The phase vocoder no longer moves anything in frequency; it time-scales the signal by ratio (analysis hop ha, synthesis hop hs = ha·ratio, both riding √ratio so neither drops under 50 % overlap), and a fractional read head then plays that stream back ratio times as fast. Two consequences worth naming, because the spectral-translation core this replaced had the opposite ones (2026-08-03 audit C13/C15, re-derived 2026-08-05):
    • No destination bins. Nothing is moved in frequency, so there is no round(f·(ratio−1)/bin) to sit on a half-bin tie (the old core's residue went −47 → −12.4 dB at exactly 0.50) and nothing can be clamped onto the top bins. Pinned structurally by the_shifter_has_no_destination_bins.
    • The read head band-limits (added 2026-08-06, audit-3 review D1; the first cut of this core did not, and folded). A continuous stream read is wrong in BOTH directions: above unity content over sr/(2·ratio) folds back into the band, below unity the interpolator's images come back. The read is the engine's shared Kaiser sinc, resample::sinc_read_at_cutoff at 0.94/ratio of the stream's Nyquist — 8 taps a side at unity, 14 at ×2, 27 at the ×4 rail — with an identity arm at |ratio − 1| ≤ 1e-4 so a settled tune is still bit-transparent (a band-limited kernel is not: its zeros sit at multiples of 1/c samples, not at 1). Measured before → after, on a lone partial, the spur the read manufactured:

      partialratiospur atbeforeafter
      15 kHz×218 kHz+0.00 dBc−41.84
      18 kHz×212 kHz−0.00−108.03
      20 kHz×28 kHz+0.00−83.44
      18 kHz×0.515 kHz−12.88−42.55
      15 kHz×0.516.5 kHz−17.98−76.22

      On six partials all above 13 kHz — where at ×2 nothing can reach the audible band except by folding — the 2–16 kHz band goes −0.97 → −70.83 dB against the source's own top octave, and at ×0.5 the top octave goes −16.54 → −45.22 while the partials' real destination (6.5–11 kHz) arrives within 0.34 dB. Pinned by the_read_head_neither_folds_nor_images_the_top_octave, which measures BOTH columns in one run through a test-only bypass — and which exists because the vocal suite tested nothing above ~1 kHz, so a 0.94-Nyquist lowpass on every read left all seventeen of its tests green. That is what shifts the pitch, and resampling has no frequency-resolution floor. What survives from the peak-translation core is the phase treatment: peaks are still picked with the 5-point rule, each peak's region of influence still gets one phase advance with identity locking inside it, and a spectral-flux rise > 50 % still anchors the frame on the analysis phases (attack reset, as in stretch.rs). Locking is skipped below 4 bins of harmonic spacing, where a region would span several partials. The synthesis OLA is normalized by a parallel window-square accumulation, since a moving hop has no fixed Hann² constant. Still O(BINS) and allocation-free. Why it was replaced: the 5-point rule cannot admit two peaks closer than 3 bins (140.6 Hz at 1024/48 k), so below that a whole "region" spanned several harmonics and was translated by ONE integer bin offset — an additive move, not a ratio. On a 12-harmonic comb at +4 semitones the worst partial error went +169.4 → +1.7 cents at f0 110 Hz (with the measured comb spacing going 110.1 Hz — i.e. unshifted — → 138.6) and +171.0 → −2.2 at 130 Hz; the partial-level spread at 110 Hz 11.2 → 0.9 dB. Over f0 110–300 Hz at ratios 1.06 / 1.26 / 2.0 every partial now lands within 2.2 cents. A lone partial is exact down to 80 Hz (1.7 bins), which the window length, not the algorithm, sets. What it cost. (a) A time-scaler disperses a transient that a pure spectral translation did not: click concentration is 1.00× of dry at ratio 1 and 0.74–0.81× across ratios 1.03 … 2.0 (it does not worsen with the shift). About 0.5 dB of that is the fractional read's Catmull-Rom droop on a broadband impulse — the same droop every native fractional read has, −0.12 dB at 8 kHz, −0.55 at 12, −1.54 at 16. (b) Formant preservation lost 0.6 dB of F1 contrast on a 150–300 Hz glide (5.9 → 6.5 dB under the input's own contrast), because the envelope is now pre-warped by the ratio and read back through the resampler. (c) Group delay rides the ratio — see Latency.

  • Monophonic: the tracker follows one f0; polyphonic (Melodyne-class) retuning stays out of scope (production-gaps B5 ⛔ tier). Note that the low-f0 limit documented before this rebuild — nothing under ~140 Hz survived the correction — is gone; a male lead or a bass line retunes correctly now.
  • Bypass-engage resets shifter/tuner state (the set_bypass tail-reset contract), so a re-engage never replays stale OLA audio or rides a stale tracked pitch. deess resets its crossover, sidechain, bell, detector and both program trackers, so a re-engage never rides a stale threshold — and re-arms the 24 ms cold-start seed, so it never rides an empty one either.
  • Behavioral coverage: engine/src/vocal.rs unit tests pin the shifting core (low_f0_combs_land_on_the_shifted_lattice sweeps f0 110–300 Hz × ratio 1.06/1.26/2.0, a_lone_partial_shifts_exactly_into_the_bass, shifter_is_block_size_invariant, unity_ratio_is_transparent_and_the_level_holds_across_the_range), and engine/tests/vocal_fx.rs covers the surface (measurement-style: correction convergence, a male-range lead corrected across its whole comb, envelope-vs-comb, diatonic-third energy, chop repitch, and nine de-esser properties — program-relative threshold, ratio discrimination, ess-band-vs-air selectivity, untouched body, bounded no-body fallback, monotonicity in the ess level, a held reference through a sustained ess, out-of-band air never ducking harder than the band, and no onset overshoot) + compiler/src/lower/tests.rs + examples/songs/vocal.volta (golden-snapshotted).