@audio/pitch
v2.0.3
Published
Pitch detection — umbrella for @audio/pitch-* atoms (YIN, pYIN, McLeod, HPS, cepstrum, SWIPE, autocorrelation, AMDF)
Readme
@audio/pitch

Pitch detection (F0 estimation). YIN, McLeod, pYIN, autocorrelation, AMDF, HPS, cepstrum, SWIPE.
Time-domain
YIN — cumulative mean normalized difference McLeod — normalized square difference (MPM) pYIN — probabilistic YIN with Beta prior Autocorrelation — normalized autocorrelation AMDF — average magnitude difference
Spectral
HPS — harmonic product spectrum Cepstrum — real cepstrum peak picking SWIPE — sawtooth waveform inspired estimator
Chroma, chord and key detection moved to @audio/mir (
mir-chroma,mir-chord,mir-key).
Install
npm install @audio/pitchUsage
import { yin, mcleod } from '@audio/pitch'
let fs = 44100
let frame = new Float32Array(2048) // fill from your audio source
let result = yin(frame, { fs })
// → { freq: 440.1, clarity: 0.97 } or nullWorks in Node.js and browser. No Web Audio API needed — operates on raw
Float32Arraysamples.
Sliding windows — call repeatedly as new samples arrive:
let hop = 512
for (let i = 0; i + 2048 <= samples.length; i += hop) {
let frame = samples.subarray(i, i + 2048)
let result = yin(frame, { fs })
if (result) console.log(i / fs, result.freq.toFixed(1))
}Harmony pipeline — chroma → chord → key lives in @audio/mir:
import { chroma, chord, smoothChords, key } from '@audio/mir'
let frames = []
for (let i = 0; i + 4096 <= samples.length; i += 2048) {
frames.push(chroma(samples.subarray(i, i + 4096), { fs, method: 'nnls' }))
}
let chords = smoothChords(frames, { selfProb: 0.5 })
let k = key(frames)API
All pitch algorithms return { freq, clarity } | null:
freq— fundamental frequency in Hzclarity— algorithm-specific confidence in[0, 1]null— no periodic structure found (silence, noise, polyphony)
Time-domain algorithms (YIN, McLeod, pYIN, autocorrelation, AMDF) accept any buffer length. Spectral algorithms (HPS, cepstrum, SWIPE) require power-of-2 length.
Each algorithm is also installable standalone: npm install @audio/pitch-yin etc.
YIN
de Cheveigné & Kawahara, 2002. The reference algorithm for monophonic pitch estimation. Most cited, most tested, most robust.
import yin from '@audio/pitch-yin'
let result = yin(samples, { fs: 44100 })| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate (Hz) |
| threshold | 0.15 | CMND threshold — lower = stricter, fewer detections |
Use when: General-purpose monophonic pitch tracking — speech, singing, solo instruments. The most reliable choice when in doubt. Not for: Polyphonic audio (returns dominant or null), real-time with hard latency budgets (needs full window). Ref: de Cheveigné & Kawahara, "YIN, a fundamental frequency estimator for speech and music", JASA 2002. Complexity: $O(N^2/4)$ — two nested passes over half the window.
McLeod
McLeod & Wyvill, 2005. Normalized square difference with smarter peak picking. Handles smaller windows — good for vibrato and fast pitch changes.
import mcleod from '@audio/pitch-mcleod'
let result = mcleod(samples, { fs: 44100 })| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate (Hz) |
| threshold | 0.9 | Peak selection threshold as fraction of global max |
Use when: Vibrato tracking, small hop sizes, singing voice where YIN occasionally double-triggers. Not for: Highly noisy signals (NSDF is less thresholded than YIN's CMND). Ref: McLeod & Wyvill, "A smarter way to find pitch", ICMC 2005. Complexity: $O(N^2/4)$ — same asymptotic cost as YIN.
pYIN
Mauch & Dixon, 2014. Probabilistic YIN — runs YIN at multiple thresholds weighted by a Beta(2, 18) prior, producing a distribution over candidate pitches instead of a single hard pick. More robust than YIN on ambiguous frames.
import pyin from '@audio/pitch-pyin'
let result = pyin(samples, { fs: 44100 })
// → { freq: 440.1, clarity: 0.92, candidates: [{ freq: 440.1, prob: 0.85 }, ...] }| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate (Hz) |
| minFreq | 50 | Minimum detectable frequency (Hz) |
| maxFreq | 2000 | Maximum detectable frequency (Hz) |
Use when: Ambiguous pitched content — breathy vocals, noisy recordings, or when you need a pitch posterior for downstream HMM tracking. Not for: Clean signals where YIN already works well (pYIN is ~10× slower due to multi-threshold sweep). Ref: Mauch & Dixon, "pYIN: A Fundamental Frequency Estimator Using Probabilistic Threshold Distributions", ICASSP 2014.
Autocorrelation
Normalized autocorrelation — the simplest pitch estimator. Educational baseline.
import autocorrelation from '@audio/pitch-autocorrelation'
let result = autocorrelation(samples, { fs: 44100 })| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate (Hz) |
| threshold | 0.5 | Minimum normalized autocorrelation value to accept |
Use when: Learning, quick prototypes, signals with strong dominant periodicity and low noise. Not for: Production — octave errors are common without additional heuristics. Ref: Rabiner, "Use of autocorrelation analysis for pitch detection", IEEE TASSP 1977. Complexity: $O(N^2/4)$.
AMDF
Ross et al., 1974. Average Magnitude Difference Function — the classical predecessor to YIN. Measures average absolute difference between a signal and its delayed copy; minima indicate periodicity.
import amdf from '@audio/pitch-amdf'
let result = amdf(samples, { fs: 44100 })| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate (Hz) |
| minFreq | 50 | Minimum detectable frequency (Hz) |
| maxFreq | 2000 | Maximum detectable frequency (Hz) |
| threshold | 0.3 | Normalized AMDF dip threshold |
Use when: Low-complexity environments, embedded systems. Simpler and cheaper than YIN (no squaring, no cumulative normalization). Not for: Noisy signals — lacks YIN's cumulative normalization that suppresses octave errors. Ref: Ross et al., "Average magnitude difference function pitch extractor", IEEE TASSP 1974. Complexity: $O(N^2/4)$.
HPS
Schroeder, 1968. Harmonic Product Spectrum — multiplies the spectrum by its downsampled copies so that harmonic peaks align at the fundamental. Robust to the missing-fundamental problem.
import hps from '@audio/pitch-hps'
let result = hps(samples, { fs: 44100 })| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate (Hz) |
| harmonics | 5 | Number of harmonic products |
| minFreq | 50 | Minimum detectable frequency (Hz) |
| maxFreq | 4000 | Maximum detectable frequency (Hz) |
| cents | 10 | Candidate spacing in cents |
| threshold | 0.1 | Minimum clarity to accept |
Use when: Harmonic-rich signals (guitar, piano, brass). Naturally handles missing fundamentals. Not for: Pure sinusoids (only one harmonic), very noisy signals. Ref: Schroeder, "Period histogram and product spectrum", JASA 1968. Requires: Power-of-2 window length.
Cepstrum
Noll, 1967. Real cepstrum — $c(\tau) = \text{IFFT}(\log |\text{FFT}(x)|)$. A peak at quefrency $\tau$ corresponds to period $\tau$ in the time domain.
import cepstrum from '@audio/pitch-cepstrum'
let result = cepstrum(samples, { fs: 44100 })| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate (Hz) |
| minFreq | 50 | Minimum detectable frequency (Hz) |
| maxFreq | 2000 | Maximum detectable frequency (Hz) |
| threshold | 0.3 | Minimum clarity to accept |
Use when: Harmonic signals where you want a clean spectral-domain method. Good pedagogical complement to time-domain algorithms. Not for: Low-pitched signals (quefrency resolution is limited by window length). Ref: Noll, "Cepstrum pitch determination", JASA 1967. Requires: Power-of-2 window length.
SWIPE
Camacho & Harris, 2008. SWIPE' (Sawtooth Waveform Inspired Pitch Estimator, prime harmonics). Measures spectral similarity between the window and a sawtooth template whose lobes sit at prime harmonics. More accurate than HPS on clean instrumental signals; robust against octave errors because only prime harmonics contribute.
Simplified single-window form: uses one FFT instead of the multi-resolution loudness pyramid of the original paper — sufficient for stationary windows.
import swipe from '@audio/pitch-swipe'
let result = swipe(samples, { fs: 44100 })| Param | Default | |
|---|---|---|
| fs | 44100 | Sample rate (Hz) |
| minFreq | 60 | Minimum detectable frequency (Hz) |
| maxFreq | 4000 | Maximum detectable frequency (Hz) |
| cents | 10 | Candidate spacing in cents |
| threshold | 0.15 | Minimum clarity to accept |
Use when: Clean instrumental signals, studio recordings, where sub-Hz accuracy matters. Not for: Very noisy or reverberant signals (single-window form lacks multi-resolution robustness of the full SWIPE'). Ref: Camacho & Harris, "A sawtooth waveform inspired pitch estimator for speech and music", JASA 2008. Requires: Power-of-2 window length.
Comparison
Pitch algorithms
| | YIN | McLeod | pYIN | AMDF | HPS | Cepstrum | SWIPE | |---|---|---|---|---|---|---|---| | Domain | time | time | time | time | spectral | spectral | spectral | | Accuracy | ★★★★★ | ★★★★ | ★★★★★ | ★★★ | ★★★★ | ★★★ | ★★★★★ | | Noise robustness | ★★★★★ | ★★★★ | ★★★★★ | ★★★ | ★★★ | ★★★ | ★★★★ | | Octave errors | rare | rare | rare | common | rare | occasional | rare | | Missing fundamental | no | no | no | no | yes | yes | yes | | Min window | ~4 periods | ~2 periods | ~4 periods | ~4 periods | power of 2 | power of 2 | power of 2 | | Best for | general | vibrato | ambiguous | embedded | harmonic-rich | pedagogical | studio |
See also
- fourier-transform — FFT used by spectral algorithms
- mir — chroma, chord, key, melody, structure, fingerprint
- beat — onset detection, tempo estimation, beat tracking
- digital-filter — filter design and processing
- stretch — time stretching and pitch shifting
- shift — pitch shifting algorithms
