Skip to main content

Crate pitch_estimate

Crate pitch_estimate 

Source
Expand description

Realtime monophonic pitch detection with the McLeod pitch method (MPM) and YIN.

Both detectors take one frame of f32 or f64 samples and return the fundamental frequency with a clarity score in [0, 1], or None when the frame has no pitch that clears the configured gates. Estimation is per frame and stateless: a detector keeps no history between calls and applies no smoothing across frames.

use pitch_estimate::{McLeodDetector, PitchDetector, YinDetector};

let sample_rate = 44_100;
let frame: Vec<f64> = (0..4096)
    .map(|i| (2.0 * std::f64::consts::PI * 440.0 * i as f64 / sample_rate as f64).sin())
    .collect();

let mut mpm = McLeodDetector::<f64>::new(4096, 2048)?;
let pitch = mpm.detect(&frame, sample_rate).expect("a clean sine has a pitch");
assert!((pitch.frequency - 440.0).abs() < 0.5);
assert!(pitch.clarity > 0.99);

let mut yin = YinDetector::<f64>::new(4096, 2048)?;
let pitch = yin.detect(&frame, sample_rate).expect("a clean sine has a pitch");
assert!((pitch.frequency - 440.0).abs() < 0.5);

§Contract

For every finite frame, detect returns either None or a Pitch whose frequency is finite and whose clarity is finite and in [0, 1]. That holds when the frame’s own arithmetic overflows inside the FFT: a non-finite autocorrelation, difference, or CMNDF entry is discarded before any candidate is chosen. Frames with a non-finite sample, an empty or too short frame, an all-zero frame, and a constant (DC) frame return None and never panic.

The detectors allocate their scratch buffers once, in the constructor. detect performs no heap allocation.

The crate uses the standard library (the FFT layer needs it) and contains no unsafe code.

§Algorithms

McLeodDetector computes the linear autocorrelation r(tau) through a zero-padded real FFT, divides the product by the transform length so the values are in time-domain units, seeds the two-segment squared-sum term m'(tau) from the time-domain sum of squares, and forms the NSDF n(tau) = 2 r(tau) / m'(tau), which lies in [-1, 1]. Key maxima between zero crossings are refined by parabolic interpolation. The first one at or above k times the largest is the period, and its height is the clarity.

YinDetector computes the difference function d(tau) over a fixed integration window through a cross-correlation FFT, normalizes it to the CMNDF d'(tau) = d(tau) * tau / (d(1) + ... + d(tau)) (so d'(1) = 1), takes the first lag at which d' drops below the absolute threshold, descends to the local minimum, refines it parabolically, and reports clarity = 1 - d'_min clamped to [0, 1].

Neither detector applies a window function. YIN subtracts the frame mean before its transform because its difference function is DC-invariant. Remove DC before using MPM. A constant offset pushes the NSDF toward 1 at every lag and hides the zero crossings MPM needs.

Structs§

McLeodDetector
Pitch detector using the McLeod pitch method: normalized square difference function (NSDF), key-maximum picking, parabolic refinement.
Pitch
Result of a detection pass over one frame.
YinDetector
Pitch detector using YIN: difference function over a fixed integration window, cumulative mean normalized difference function (CMNDF), absolute threshold with descent to the local minimum, parabolic refinement.

Enums§

ConfigError
Why a detector could not be constructed.

Constants§

MAX_FRAME_LEN
Largest accepted frame length, in samples.

Traits§

Float
Sample type accepted by the detectors: f32 or f64.
PitchDetector
A frame-by-frame pitch detector.