use crate::core::{AudioFrame, AudioProfile, CaptureDiagnostics};
use crate::processors::{analyze_frame, classify_frame, FrameClass};
#[derive(Debug)]
pub struct RuntimeStats {
profile: AudioProfile,
noise_floor: f32,
analyzed_frames: u64,
noise_only_frames: u64,
transitional_frames: u64,
speech_like_frames: u64,
}
impl RuntimeStats {
pub fn new(profile: AudioProfile) -> Self {
Self {
profile,
noise_floor: 0.01,
analyzed_frames: 0,
noise_only_frames: 0,
transitional_frames: 0,
speech_like_frames: 0,
}
}
pub fn observe(&mut self, frame: &AudioFrame) {
if frame.samples.is_empty() {
return;
}
let features = analyze_frame(&frame.samples, self.noise_floor);
let frame_class = classify_frame(self.profile, features, self.noise_floor);
self.analyzed_frames += 1;
match frame_class {
FrameClass::NoiseOnly => {
self.noise_only_frames += 1;
self.noise_floor = self.noise_floor * 0.92 + features.rms.max(0.0005) * 0.08;
}
FrameClass::Transitional => {
self.transitional_frames += 1;
self.noise_floor = self.noise_floor * 0.992 + features.rms.max(0.0005) * 0.008;
}
FrameClass::SpeechLike => {
self.speech_like_frames += 1;
self.noise_floor = self.noise_floor * 0.997 + features.rms.max(0.0005) * 0.003;
}
}
}
pub fn apply(&self, diagnostics: &mut CaptureDiagnostics) {
diagnostics.analyzed_frames = self.analyzed_frames;
diagnostics.noise_only_frames = self.noise_only_frames;
diagnostics.transitional_frames = self.transitional_frames;
diagnostics.speech_like_frames = self.speech_like_frames;
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::core::{AudioProfile, AudioSpec};
fn frame(samples: Vec<f32>) -> AudioFrame {
AudioFrame {
samples,
spec: AudioSpec {
sample_rate: 44_100,
channels: 1,
},
}
}
#[test]
fn runtime_stats_classify_frames_into_buckets() {
let mut stats = RuntimeStats::new(AudioProfile::VoiceHvac);
stats.observe(&frame(vec![
0.02, -0.02, 0.02, -0.02, 0.02, -0.02, 0.02, -0.02,
]));
stats.observe(&frame(vec![
0.0, 0.18, 0.29, 0.12, -0.04, -0.16, -0.22, -0.09,
]));
let mut diagnostics = CaptureDiagnostics::default();
stats.apply(&mut diagnostics);
assert_eq!(diagnostics.analyzed_frames, 2);
assert_eq!(diagnostics.noise_only_frames, 1);
assert_eq!(diagnostics.speech_like_frames, 1);
}
}