#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Vowel {
I,
ISmall,
E,
Epsilon,
Ae,
Alpha,
OpenO,
O,
USmall,
U,
Caret,
Schwa,
Unknown,
}
#[must_use]
pub fn detect_vowel(formants: &[f32]) -> Vowel {
if formants.len() < 2 {
return Vowel::Unknown;
}
let f1 = formants[0];
let f2 = formants[1];
let vowels = [
(Vowel::I, 270.0, 2290.0),
(Vowel::ISmall, 390.0, 1990.0),
(Vowel::E, 530.0, 1840.0),
(Vowel::Epsilon, 610.0, 1900.0),
(Vowel::Ae, 660.0, 1720.0),
(Vowel::Alpha, 730.0, 1090.0),
(Vowel::OpenO, 570.0, 840.0),
(Vowel::O, 440.0, 1020.0),
(Vowel::USmall, 440.0, 1020.0),
(Vowel::U, 300.0, 870.0),
(Vowel::Caret, 640.0, 1190.0),
(Vowel::Schwa, 500.0, 1500.0),
];
let mut min_distance = f32::INFINITY;
let mut best_vowel = Vowel::Unknown;
for (vowel, f1_proto, f2_proto) in vowels {
let distance = ((f1 - f1_proto).powi(2) + (f2 - f2_proto).powi(2)).sqrt();
if distance < min_distance {
min_distance = distance;
best_vowel = vowel;
}
}
if min_distance < 400.0 {
best_vowel
} else {
Vowel::Unknown
}
}
#[must_use]
pub fn detect_vowel_with_confidence(formants: &[f32]) -> (Vowel, f32) {
let vowel = detect_vowel(formants);
if vowel == Vowel::Unknown || formants.len() < 2 {
return (Vowel::Unknown, 0.0);
}
let f1 = formants[0];
let f2 = formants[1];
let (proto_f1, proto_f2) = match vowel {
Vowel::I => (270.0, 2290.0),
Vowel::ISmall => (390.0, 1990.0),
Vowel::E => (530.0, 1840.0),
Vowel::Epsilon => (610.0, 1900.0),
Vowel::Ae => (660.0, 1720.0),
Vowel::Alpha => (730.0, 1090.0),
Vowel::OpenO => (570.0, 840.0),
Vowel::O | Vowel::USmall => (440.0, 1020.0),
Vowel::U => (300.0, 870.0),
Vowel::Caret => (640.0, 1190.0),
Vowel::Schwa => (500.0, 1500.0),
Vowel::Unknown => return (Vowel::Unknown, 0.0),
};
let distance = ((f1 - proto_f1).powi(2) + (f2 - proto_f2).powi(2)).sqrt();
let confidence = (1.0 - distance / 400.0).max(0.0);
(vowel, confidence)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_vowel_detection() {
let formants_i = vec![270.0, 2290.0, 3000.0, 3500.0];
assert_eq!(detect_vowel(&formants_i), Vowel::I);
let formants_ae = vec![660.0, 1720.0, 2500.0, 3500.0];
assert_eq!(detect_vowel(&formants_ae), Vowel::Ae);
let formants_u = vec![300.0, 870.0, 2500.0, 3500.0];
assert_eq!(detect_vowel(&formants_u), Vowel::U);
}
#[test]
fn test_vowel_with_confidence() {
let formants = vec![270.0, 2290.0];
let (vowel, confidence) = detect_vowel_with_confidence(&formants);
assert_eq!(vowel, Vowel::I);
assert!(confidence > 0.9);
}
#[test]
fn test_unknown_vowel() {
let formants = vec![100.0, 5000.0]; assert_eq!(detect_vowel(&formants), Vowel::Unknown);
}
}