use super::{FormantAnalyzer, FormantResult};
use crate::{AnalysisConfig, Result};
pub struct FormantTracker {
analyzer: FormantAnalyzer,
hop_size: usize,
}
impl FormantTracker {
#[must_use]
pub fn new(config: AnalysisConfig) -> Self {
let hop_size = config.hop_size;
Self {
analyzer: FormantAnalyzer::new(config),
hop_size,
}
}
pub fn track(&self, samples: &[f32], sample_rate: f32) -> Result<Vec<FormantResult>> {
let window_size = 1024;
let mut results = Vec::new();
let num_frames = (samples.len() - window_size) / self.hop_size + 1;
for frame_idx in 0..num_frames {
let start = frame_idx * self.hop_size;
let end = (start + window_size).min(samples.len());
if end - start < window_size {
break;
}
let frame = &samples[start..end];
let result = self.analyzer.analyze(frame, sample_rate)?;
results.push(result);
}
Ok(results)
}
#[must_use]
pub fn get_contours(&self, results: &[FormantResult]) -> Vec<Vec<f32>> {
let num_formants = 4;
let mut contours = vec![Vec::new(); num_formants];
for result in results {
for (i, &formant) in result.formants.iter().enumerate() {
if i < num_formants {
contours[i].push(formant);
}
}
}
contours
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_formant_tracking() {
let config = AnalysisConfig::default();
let tracker = FormantTracker::new(config);
let sample_rate = 16000.0;
let samples = vec![0.1; 16000];
let _ = tracker.track(&samples, sample_rate);
}
}