Skip to content

Time Stretch and Pitch Shift

Speeding up a tape changes pitch and duration together. Modern editing DSP separates them.

OperationWhat changesWhat should stay stable
Time stretchLengthPitch
Pitch shiftPitchLength

This page explains the machinery behind that separation and grounds it in libsonare's stretch backend. For the vocabulary first, read Editing Basics.

The phase vocoder

The core tool is the phase vocoder.

At a high level, it does three things:

  1. Run an STFT (short-time Fourier transform) to split audio into short time-frequency frames.
  2. Resample those frames along the time axis, changing how quickly they advance.
  3. Rebuild phase so partials stay continuous instead of smearing.

Step 1 is the same framing every analysis feature uses; Spectrogram and STFT illustrates how those frames are cut, which is worth a look before reading the phase discussion below.

The hard part is phase coherence. The STFT splits each frame into frequency bins — one slot per narrow band of frequencies. When frames are spaced differently than they were analyzed, the phase in each bin has to be re-propagated so the individual frequency components (the partials that make up the sound) stay continuous. If that goes wrong, you hear the classic "phasey" or metallic artifact.

Intuitively, the audio is cut into thin slices of time and the slices are laid out again at a new spacing. Drop slices and the sound gets shorter; repeat them and it gets longer. Adjusting the phase so the waveform joins smoothly at every seam is the phase vocoder's real job.

Two operations, one backend

OperationChangesKeepsHow
Time stretchDurationPitchPhase vocoder rescales the time axis
Pitch shiftPitchDurationTime-stretch by a ratio, then resample back to the original length

This is why pitch shift and time stretch share a backend: a pitch shift is a time stretch followed by resampling. rate > 1.0 shortens a clip; semitones = 12 shifts up an octave.

To picture a shift up a semitone: stretch the audio slightly longer, then play it back slightly faster so it lands on its original length again.

PARAM SWEEP · TIME STRETCHIDLE
Time stretch — changing length, not pitch

Time stretching is pitch shift's exact opposite: it changes how long the audio lasts while leaving the pitch alone. Drag the rate and the drum hits spread out or bunch up — the waveform fills more or less of the panel — but the spectrum below barely moves. Below 1.0 the clip slows down and grows; above 1.0 it speeds up and shrinks. Every render is peak-normalized, so a fast rate does not simply arrive quieter than a slow one; the level you hear is set by the demo, not by the stretch. Press play to hear the groove change tempo with no chipmunk effect.

Rate
1 ×
PARAM SWEEP · PITCH SHIFTIDLE
Pitch shift — moving the whole harmonic comb

Pitch shifting transposes a sound without changing its length. Drag the semitones and watch the spectrum: every harmonic scales together and the fundamental marker tracks the new pitch. Because this shift is not formant-preserving, the formant bumps move too — the "chipmunk" effect. Press play to hear it.

Pitch
0 st

Why large moves create artifacts

Both operations invent or discard information.

EditWhat can go wrong
Stretch a sound to twice its lengthMuch of the new audio is synthesized from phase assumptions
Shift a voice up a fifthFormants move too unless corrected
Make a large transient editAttacks can soften or smear

Small moves stay transparent because the assumptions still hold. Large moves expose the assumptions as smearing, transient softening — a transient being the sharp attack at the start of a note or a drum hit — or a "chipmunk" timbre.

Practical rule: keep edits conservative for natural results, and treat big moves as deliberate creative effects.

How libsonare implements stretching

libsonare's timeStretch and pitchShift sit on a peak-locked phase-vocoder core (phase_vocoder_phaselocked, the NativeSpectral backend) combined with resampling for the pitch axis. Peak locking is what keeps the bins around each spectral peak phase-consistent, and it is the reason the "phasey" artifact above is muted rather than obvious. A plain, unlocked phase_vocoder exists as a lightweight C++-only fallback; the JavaScript and Python entry points always take the peak-locked path and expose no backend choice. A pitch shift is implemented as a time-stretch by the pitch ratio followed by a resample back to the original duration. The same core underlies noteStretch (region-bounded stretching) and the pitch path of voiceChange. All operate on decoded mono Float32Array / sample sequences.

Quality degrades gradually with the size of the move, so conservative ratios keep artifacts below the audible threshold on most material. They never reach zero: every call runs a full STFT round trip at a 2048-sample window with re-propagated phase, so a little transient softening and residual phasiness is there even at a ratio near 1. If a passage must stay bit-exact, do not send it through the stretcher at all.

Related: Editing Basics, Pitch Correction, Voice and Formant, Editing DSP