unnieversal/crates/utils/src/pitch.rs

78 lines
2.1 KiB
Rust

use pitch_detection::detector::yin::YINDetector;
use pitch_detection::detector::PitchDetector;
pub fn generate_pitch_detector(size: usize) -> impl PitchDetector<f32> {
let padding = size / 2;
YINDetector::new(size, padding)
}
/// Returns an option with (Frequency, Clarity)
pub fn pitch_detect(
detector: &mut dyn PitchDetector<f32>,
signal: &[f32],
sample_rate: u32,
) -> Option<(f32, f32)> {
const POWER_THRESHOLD: f32 = 0.15;
const CLARITY_THRESHOLD: f32 = 0.5;
let pitch = detector.get_pitch(
&signal,
sample_rate as usize,
POWER_THRESHOLD,
CLARITY_THRESHOLD,
);
pitch.map(|a| (a.frequency, a.clarity))
}
pub fn generate_vocoder(sample_rate: u32) -> PhaseVocoder {
PhaseVocoder::new(1, sample_rate as f64, 256, 4)
}
// From https://github.com/nwoeanhinnogaehr/pvoc-plugins/blob/master/src/plugins/pitchshifter.rs
use pvoc::{Bin, PhaseVocoder};
pub fn pitch_shift<const LEN: usize>(
pvoc: &mut PhaseVocoder,
input: &[f32],
shift: f32,
) -> [f32; LEN] {
let shift = shift as f64;
let mut output = [0.0; LEN];
pvoc.process(
&[&input],
&mut [&mut output],
|channels: usize, bins: usize, input: &[Vec<Bin>], output: &mut [Vec<Bin>]| {
for i in 0..channels {
for j in 0..bins / 2 {
let index = ((j as f64) * shift) as usize;
if index < bins / 2 {
output[i][index].freq = input[i][j].freq * shift;
output[i][index].amp += input[i][j].amp;
}
}
}
},
);
output
}
/// Returns closest midi note to `pitch`
pub fn pitch_to_midi_note(pitch: f32) -> u8 {
// With the frequency, check the closest note
let note = 69 + (12. * (pitch / 440.).log2()).round() as i16;
note.clamp(0, 127) as u8
}
/// Returns a midi note's pitch
pub fn midi_note_to_pitch(note: u8) -> f32 {
440.0f32 * 2.0f32.powf((note as f32 - 69.) / 12.)
}
/// Returns the closest note frequency
pub fn closest_note_freq(freq: f32) -> f32 {
midi_note_to_pitch(pitch_to_midi_note(freq))
}