@maruaudio/dsp/spectral/frequency-shifter moves every partial of a mono signal by the same signed number of hertz (single-sideband modulation). Two fixed four-section Niemitalo IIR allpass banks form an approximate analytic pair; the first bank's output is delayed one sample as the real part and the second is the imaginary part. A phase-locked quadrature carrier then gives real × cos + imaginary × sin for an upward shift and real × cos − imaginary × sin for a downward one. Each allpass section uses the squared pole as its coefficient, with f32 state.
Parameters
| Path | Setter | Range as coded | Default | Unit |
|---|---|---|---|---|
frequencyHz | setFrequencyShifterFrequency | −1000 to +1000 | 0 | Hz |
Shifts with magnitude below FREQUENCY_SHIFTER_UNISON_BAND_HZ (0.5 Hz) pass the input through unchanged; withinFrequencyShifterUnisonBand reports that band.
Lifecycle
initFrequencyShifterNode(sampleRate) preallocates both banks, with the rate bounded to the Processor envelope (8 to 192 kHz) → setFrequencyShifterFrequency → frequencyShifterNodeTick(state, x) per sample → resetFrequencyShifterNode, which clears the banks, the delay and the carrier phase. prepareFrequencyShifterNodeSampleRate re-stamps the bounded rate: the carrier advances by |frequencyHz| / sampleRate, so the shift stays fixed in hertz, and the allpass poles are normalized and need no change.
When to use it
When every partial should move by the same offset in hertz rather than by a ratio, so the harmonics of a pitched source no longer stay whole multiples of its fundamental. For a ratio see @maru/spectral-pitch-shifter.
Limits
- Mono sample tick. A frequency change applies at the next sample with no smoothing; the carrier phase is kept.
- The allpass pair is approximate, so some opposite-sideband energy remains (the leaf playground demo notes this).
- The banks keep running inside the unison band, so leaving it continues from their history.
- The carrier calls
Math.cosandMath.sinevery sample outside the unison band.