@maruaudio/dsp/modulation/retune is a pitch-correction control. It moves a detected voice pitch towards a target note and outputs a pitch-shift ratio per sample (and the correction in cents) for a pitch shifter to apply; it never touches audio. The raw correction is amount × (target − detected) × 100 cents, followed per sample by a one-pole (@maru/modulation-slew) whose time constant is speedMs / 3, lengthened by humanize on notes held past 100 ms. Delayed vibrato from @maru/modulation-lfo is added, and the sum goes through centsToRatio (@maru/math-pitch).
Parameters
| Setter | Range the setter enforces | Default |
|---|---|---|
configureRetune(speedMs, amount, humanize, transitionMs) | 0 to 1000 ms (0 snaps on the next sample), 0 to 1, 0 to 1, 0 to 200 ms | 50 ms, 1, 0, 12 ms |
setRetuneTolerance(cents) | 0 to 100 cents left uncorrected | 0 |
setRetuneVibrato(rateHz, depthCents, delayMs) | 0.1 to 12 Hz, 0 to 200 cents, 0 to 2000 ms | 5.5 Hz, 0, 300 ms |
configureRetuneGuard(jump, confirmMs, onsetMs, leap) | 0 to 24 st (0 is off), 0 to 100 ms, 0 to 100 ms, 0 to 24 st | Off, 15 ms, 0, 0 |
Lifecycle
initRetune(rate) (rate ≤ 0 → 48 kHz) → configure → per detector hop, optionally retuneGuardPitch on the raw estimate, then updateRetune(state, detectedMidi, voiced, targetMidi, passThrough, elapsedSamples) with the target from a scale quantizer (@maru/math-scale) → retuneTick per sample, returning the ratio → resetRetune, which returns to unity. The state holds numbers and one LFO state; nothing allocates after init.
When to use it
Pulling a sung or played line onto a scale, from a hard snap to a slow, loose correction that leaves vibrato and scoops in place.
Limits
- A new note commits only after it persists for
transitionMs + min(speedMs/2, 120)ms, so a one-hop slip or vibrato across a scale boundary keeps the note. A pass-through hop gives zero correction and keeps the note state. - Unvoiced input holds the correction for 50 ms, then releases it (30 ms) and forgets the note. Vibrato starts after the delay, fades in over 150 ms and restarts its phase at a phrase onset, not on legato changes.
- The continuity guard holds estimates farther than
jumpsemitones from the last accepted pitch until a consistent run within 100 cents lastsconfirmMs; with the guard on, a note change ofleapor more commits at once. Jump 0 leaves the behaviour unchanged.