#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Gender {
Male,
Female,
Unknown,
}
#[must_use]
pub fn detect_gender(f0: f32, formants: &[f32]) -> Gender {
let gender_from_f0 = if f0 < 140.0 {
Gender::Male
} else if f0 > 200.0 {
Gender::Female
} else {
Gender::Unknown
};
if formants.len() >= 2 {
let f1 = formants[0];
let f2 = formants[1];
let male_score = formant_similarity(f1, f2, 500.0, 1500.0);
let female_score = formant_similarity(f1, f2, 600.0, 1800.0);
let gender_from_formants = if male_score > female_score {
Gender::Male
} else if female_score > male_score {
Gender::Female
} else {
Gender::Unknown
};
match (gender_from_f0, gender_from_formants) {
(Gender::Male, Gender::Male) => Gender::Male,
(Gender::Female, Gender::Female) => Gender::Female,
(Gender::Unknown, g) | (g, Gender::Unknown) => g,
_ => Gender::Unknown, }
} else {
gender_from_f0
}
}
fn formant_similarity(f1: f32, f2: f32, expected_f1: f32, expected_f2: f32) -> f32 {
let f1_diff = (f1 - expected_f1).abs() / expected_f1;
let f2_diff = (f2 - expected_f2).abs() / expected_f2;
1.0 / (1.0 + f1_diff + f2_diff)
}
#[must_use]
pub fn detect_gender_with_confidence(f0: f32, formants: &[f32]) -> (Gender, f32) {
let gender = detect_gender(f0, formants);
let confidence = if formants.len() >= 2 {
let f1 = formants[0];
let f2 = formants[1];
let male_score = formant_similarity(f1, f2, 500.0, 1500.0);
let female_score = formant_similarity(f1, f2, 600.0, 1800.0);
let max_score = male_score.max(female_score);
let diff = (male_score - female_score).abs();
(max_score * diff).min(1.0)
} else {
if (120.0..=220.0).contains(&f0) {
0.3
} else {
0.7
}
};
(gender, confidence)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_gender_detection() {
let male_f0 = 120.0;
let male_formants = vec![500.0, 1500.0, 2500.0];
assert_eq!(detect_gender(male_f0, &male_formants), Gender::Male);
let female_f0 = 220.0;
let female_formants = vec![600.0, 1800.0, 2800.0];
assert_eq!(detect_gender(female_f0, &female_formants), Gender::Female);
let ambiguous_f0 = 170.0;
let result = detect_gender(ambiguous_f0, &[]);
assert_eq!(result, Gender::Unknown);
}
#[test]
fn test_gender_with_confidence() {
let f0 = 100.0; let formants = vec![500.0, 1500.0];
let (gender, confidence) = detect_gender_with_confidence(f0, &formants);
assert_eq!(gender, Gender::Male);
assert!(confidence >= 0.0 && confidence <= 1.0); }
}