use crate::detector::internals::pitch_from_peaks;
use crate::detector::internals::Pitch;
use crate::detector::PitchDetector;
use crate::float::Float;
use crate::utils::buffer::square_sum;
use crate::utils::peak::PeakCorrection;
use super::internals::{windowed_square_error, yin_normalize_square_error, DetectorInternals};
pub struct YINDetector<T>
where
T: Float + std::iter::Sum,
{
internals: DetectorInternals<T>,
}
impl<T> YINDetector<T>
where
T: Float + std::iter::Sum,
{
pub fn new(size: usize, padding: usize) -> Self {
let internals = DetectorInternals::<T>::new(size, padding);
YINDetector { internals }
}
}
impl<T> PitchDetector<T> for YINDetector<T>
where
T: Float + std::iter::Sum,
{
fn get_pitch(
&mut self,
signal: &[T],
sample_rate: usize,
power_threshold: T,
clarity_threshold: T,
) -> Option<Pitch<T>> {
let threshold = T::one() - clarity_threshold;
let window_size = signal.len() / 2;
assert_eq!(signal.len(), self.internals.size);
if square_sum(signal) < power_threshold {
return None;
}
let result_ref = self.internals.buffers.get_real_buffer();
let result = &mut result_ref.borrow_mut()[..window_size];
windowed_square_error(signal, window_size, &mut self.internals.buffers, result);
yin_normalize_square_error(result);
result.iter_mut().for_each(|val| *val = threshold - *val);
pitch_from_peaks(result, sample_rate, T::zero(), PeakCorrection::Quadratic).map(|pitch| {
Pitch {
frequency: pitch.frequency,
clarity: T::one() - threshold + pitch.clarity / threshold,
}
})
}
}