sound_analyzation 0.1.0

fn analyze(&mut self, sample_rate: f32, threshold: f32) -> DetectionResult {frequency, gain, probability}
Documentation
pub trait Analyze {
    fn yin_pitch_detection(&mut self, sample_rate: f32, threshold: f32) -> (f32, f32);
    fn get_better_tau(&mut self, tau: usize, sample_rate: f32) -> f32;
    fn yin_difference(&mut self);
    fn yin_absolute_threshold(&mut self, threshold: f32) -> Option<usize>;
    fn yin_parabolic_interpolation(&mut self, tau_estimate: usize) -> f32;
    fn yin_cumulative_mean_normalized_difference(&mut self);
    fn gain(&mut self) -> f32;
    fn analyze(&mut self, sample_rate: f32, threshold: f32) -> DetectionResult;
}

pub struct DetectionResult {
    pub frequency: f32,
    pub probability: f32,
    pub gain: f32,
}

impl Analyze for Vec<f32> {
    fn analyze(&mut self, sample_rate: f32, threshold: f32) -> DetectionResult {
        let gain = self.gain();
        let (frequency, probability) = self.yin_pitch_detection(sample_rate, threshold);

        DetectionResult {
            frequency,
            probability,
            gain,
        }
    }

    fn gain(&mut self) -> f32 {
        let mean_squared: f32 = self.iter().cloned().fold(0.0, |mut sum, x: f32| {
            sum += x.powi(2);
            sum
        });

        let root_mean_squared = mean_squared.sqrt() / 100.0;
        if root_mean_squared < 1.0 {
            root_mean_squared
        } else {
            1.0
        }
    }

    fn yin_pitch_detection(&mut self, sample_rate: f32, threshold: f32) -> (f32, f32) {
        for sample in self.iter_mut() {
            *sample *= 1000.0;
        }

        self.yin_difference();
        self.yin_cumulative_mean_normalized_difference();
        let (probability, pitch_in_hertz) =
            if let Some(tau) = self.yin_absolute_threshold(threshold) {
                (1.0 - self[tau], self.get_better_tau(tau, sample_rate))
            } else {
                (-1.0, 0.0)
            };

        (pitch_in_hertz, probability)
    }

    fn get_better_tau(&mut self, tau: usize, sample_rate: f32) -> f32 {
        let better_tau = self.yin_parabolic_interpolation(tau);
        let pitch_in_hertz = sample_rate / better_tau;
        pitch_in_hertz
    }

    fn yin_difference(&mut self) {
        let buffer_clone = self.clone();
        let half_buffer_size = self.len() / 2;

        for tau in 0..half_buffer_size {
            for i in 0..half_buffer_size {
                let delta: f32 = buffer_clone[i] - buffer_clone[i + tau];
                self[tau] += delta * delta;
            }
        }

        self.resize(half_buffer_size, 0.0)
    }

    fn yin_cumulative_mean_normalized_difference(&mut self) {
        let buffer_size = self.len();
        let mut running_sum: f32 = 0.0;

        for tau in 1..buffer_size {
            running_sum += self[tau];
            self[tau] *= tau as f32 / running_sum;
        }
    }

    fn yin_absolute_threshold(&mut self, threshold: f32) -> Option<usize> {
        let mut iter = self.iter()
            .enumerate()
            .skip(2)
            .skip_while(|(_, &sample)| sample > threshold);
        let tripped_threshold = iter.next()?;

        let (_, mut previous_sample) = tripped_threshold;
        for (index, sample) in iter {
            if sample > previous_sample {
                return Some(index - 1);
            };
            previous_sample = sample;
        }

        Some(self.len() - 1)
    }

    fn yin_parabolic_interpolation(&mut self, tau_estimate: usize) -> f32 {
        let better_tau: f32;

        let x0: usize = if tau_estimate < 1 {
            tau_estimate
        } else {
            tau_estimate - 1
        };

        let x2: usize = if tau_estimate + 1 < self.len() {
            tau_estimate + 1
        } else {
            tau_estimate
        };

        if x0 == tau_estimate {
            better_tau = if self[tau_estimate] <= self[x2] {
                tau_estimate as f32
            } else {
                x2 as f32
            }
        } else if x2 == tau_estimate {
            better_tau = if self[tau_estimate] <= self[x0] {
                tau_estimate as f32
            } else {
                x0 as f32
            }
        } else {
            let s0: f32 = self[x0];
            let s1: f32 = self[tau_estimate];
            let s2: f32 = self[x2];

            better_tau = tau_estimate as f32 + (s2 - s0) / (2.0 * (2.0 * s1 - s2 - s0));
        }

        better_tau
    }
}

#[cfg(test)]
mod tests {
    use analyze::*;
    #[test]
    fn gain_test() {
        let mut buffer = vec![
            0.0, 0.06279052, 0.12533323, 0.18738133, 0.2486899, 0.309017, 0.36812457, 0.4257793,
            0.4817537, 0.53582686,
        ];
        let gain = buffer.gain();
        let expected = 0.0102376845;
        assert_eq!(gain, expected);
    }

    #[test]
    fn difference_test() {
        let mut buffer = vec![
            0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5, 0.0, 0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0,
            -0.5, 0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5, 0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0,
        ];
        let expected = vec![
            0.0, 4.25, 11.5, 16.75, 22.0, 20.25, 13.5, 6.75, 2.0, 1.75, 7.75, 15.75, 21.75, 21.75,
            15.75, 7.75,
        ];
        buffer.yin_difference();
        assert_eq!(buffer, expected);
    }

    #[test]
    fn cumulative_mean_normalized_difference_test() {
        let mut buffer = vec![
            0.0, 4.25, 11.5, 16.75, 22.0, 20.25, 13.5, 6.75, 2.0, 1.75, 7.75, 15.75, 21.75, 21.75,
            15.75, 7.75,
        ];
        let expected = [
            0.0, 1.0, 1.4603175, 1.5461539, 1.6146789, 1.354515, 0.91784704, 0.4973684, 0.16494845,
            0.15949367, 0.7276996, 1.4171779, 1.8125, 1.7058824, 1.214876, 0.6142668,
        ];
        buffer.yin_cumulative_mean_normalized_difference();
        assert_eq!(buffer, expected);
    }

    #[test]
    fn absolute_threshold_test() {
        let mut buffer = vec![
            0.0, 1.0, 1.4603175, 1.5461539, 1.6146789, 1.354515, 0.91784704, 0.4973684, 0.16494845,
            0.15949367, 0.7276996, 1.4171779, 1.8125, 1.7058824, 1.214876, 0.614266,
        ];
        let threshold = 0.2;
        let expected = Some(9);
        assert_eq!(buffer.yin_absolute_threshold(threshold), expected);
    }

    #[test]
    fn parabolic_interpolation_test() {
        let mut buffer = vec![
            0.0, 1.0, 1.4603175, 1.5461539, 1.6146789, 1.354515, 0.91784704, 0.4973684, 0.16494845,
            0.15949367, 0.7276996, 1.4171779, 1.8125, 1.7058824, 1.214876, 0.6142668,
        ];
        let tau_estimate = 9;
        let expected = 8.509509;

        assert_eq!(buffer.yin_parabolic_interpolation(tau_estimate), expected);
    }

    #[test]
    fn yin_end_to_end() {
        let sample_rate = 44_100.00;
        let threshold = 0.20;

        let mut buffer = vec![
            0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5, 0.0, 0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0,
            -0.5, 0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0, -0.5, 0.0, 0.5, 1.0, 0.5, 0.0, -0.5, -1.0,
        ];
        let expected = (5181.9604, 0.8405063);

        assert_eq!(buffer.yin_pitch_detection(sample_rate, threshold), expected);
    }
}