Skip to content

Pitch Shifter

Tier: Primitives | ComponentType: 15 | Params: 4

Granular time-domain pitch shifter using Hann-windowed dual-grain crossfade.

Overview

PitchShifter changes the pitch of incoming audio without changing its speed, using a granular time-domain approach. Two overlapping grains, each windowed with a Hann envelope, read from a circular buffer at a rate determined by the pitch shift amount. The half-grain offset between the two grains ensures a smooth crossfade with no discontinuities.

Shift is specified in semitones with an additional fine detune in cents. Grain size controls the trade-off between tonal quality (larger grains, better for sustained notes) and transient response (smaller grains, better for drums).

File Locations

Path
Header Sources/FolioDSP/include/FolioDSP/Primitives/PitchShifter.h
Implementation Sources/FolioDSP/src/Primitives/PitchShifter.cpp
Tests Tests/FolioDSPTests/PitchShifterTests.swift
Bridge Sources/FolioDSPBridge/src/FolioDSPBridge.mm (PitchShifterBridge)

Parameters

Index Name Description Min Max Default Min Default Max Default Unit
0 Shift Pitch shift amount in semitones -24.0 24.0 -12.0 12.0 0.0 st
1 Grain Size Window size for each grain 1.0 200.0 5.0 50.0 20.0 ms
2 Mix Dry/wet blend 0.0 100.0 0.0 100.0 100.0 %
3 Detune Fine pitch offset in cents -100.0 100.0 -50.0 50.0 0.0 ct

Processing Algorithm

1. Playback Rate

\[r = 2^{(s + d/100) / 12}\]

where \(s\) = shift (semitones), \(d\) = detune (cents). +12 st → rate 2.0 (octave up), -12 st → rate 0.5 (octave down).

2. Write to Circular Buffer

Input is written to an 88200-sample circular buffer at the write pointer.

3. Dual Grain Readout

Two grains read from the buffer, offset by half a grain period:

\[\text{pos}_0 = \text{write} - G + \phi \cdot G \cdot r\]
\[\text{pos}_1 = \text{write} - G + \phi_1 \cdot G \cdot r, \quad \phi_1 = \phi + 0.5\]

where \(G\) = grain size in samples, \(\phi\) = grain phase (0–1).

4. Hann Window Crossfade

\[w(\phi) = \frac{1}{2}\left(1 - \cos(2\pi\phi)\right)\]
\[y_{\text{wet}} = s_0 \cdot w(\phi) + s_1 \cdot w(\phi_1)\]

5. Dry/Wet Mix

\[y = x \cdot (1 - m) + y_{\text{wet}} \cdot m\]

Snapshot Fields

Field Type Range Unit Description
Shift Float -24–24 st Current pitch shift
Detune Float -100–100 ct Fine detune
Grain Size Float 1–200 ms Current grain window
Input Float 0–1 Input level
Output Float 0–1 Output level
Rate Float 0.25–4 x Playback rate multiplier

Implementation Notes

  • 88200-sample circular buffer (2 seconds at 44.1 kHz)
  • Two overlapping grains with half-period offset eliminate crossfade artifacts
  • Hann window provides smooth amplitude envelope (zero at edges, peak at center)
  • Minimum grain size clamped to 64 samples to prevent aliasing
  • Linear interpolation on buffer reads for sub-sample accuracy

Equation Summary

y = Σ grain[pos·2^(st/12)] · hann(env)