-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpitch.rs
67 lines (57 loc) · 2.5 KB
/
pitch.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
//! The recommended algorithm is the [HannedFftDetector](crate::pitch::hanned_fft::HannedFftDetector),
//! which is the most versatile. [PowerCepstrum](crate::pitch::cepstrum::PowerCepstrum) on the other hand, is less versatile,
//! but it is able to detect a fundamental from a sample that includes many harmonics. This means that [PowerCepstrum](crate::pitch::cepstrum::PowerCepstrum)
//! is good for detecting sounds that are rich in harmonics, as well as low pitched sounds, but bad at detecting samples
//! with fewer partials.
//!
mod cepstrum;
mod core;
mod hanned_fft;
pub use cepstrum::PowerCepstrum;
pub use hanned_fft::HannedFftDetector;
// autocorrelation doesn't work well enough yet.
// pub mod autocorrelation;
use std::ops::Range;
use crate::core::{utils::interpolated_peak_at, FftPoint};
pub trait PitchDetector: SignalToSpectrum {
/// The default implementation will detect within a conventional range of frequencies (20Hz to nyquist).
/// If you want to detect a pitch in a specific range, use the [detect_pitch_in_range](Self::detect_pitch_in_range) method
fn detect_pitch(&mut self, signal: &[f64], sample_rate: f64) -> Option<f64> {
let nyquist_freq = sample_rate / 2.;
let min_freq = 20.; // Conventional minimum frequency for human hearing
self.detect_pitch_in_range(signal, sample_rate, min_freq..nyquist_freq)
}
/// Default implementation to detect a pitch within the specified frequency range.
fn detect_pitch_in_range(
&mut self,
signal: &[f64],
sample_rate: f64,
freq_range: Range<f64>,
) -> Option<f64> {
let (start_bin, spectrum) =
self.signal_to_spectrum(signal, Some((freq_range, sample_rate)));
let max_bin =
spectrum.iter().enumerate().reduce(
|accum, item| {
if item.1 > accum.1 {
item
} else {
accum
}
},
)?;
let FftPoint { x: bin, .. } = interpolated_peak_at(&spectrum, max_bin.0)?;
Some(self.bin_to_freq(bin + start_bin as f64, sample_rate))
}
}
pub trait SignalToSpectrum {
fn signal_to_spectrum(
&mut self,
signal: &[f64],
freq_range: Option<(Range<f64>, f64)>,
) -> (usize, Vec<f64>);
// Bin may be float resolution
fn bin_to_freq(&self, bin: f64, sample_rate: f64) -> f64;
fn freq_to_bin(&self, freq: f64, sample_rate: f64) -> f64;
fn name(&self) -> &'static str;
}