use crate::config::VadConfig;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Segment {
pub start_sample: u64,
pub end_sample: u64,
}
impl Segment {
pub fn start_ms(&self, sample_rate: u32) -> u64 {
(self.start_sample * 1000) / u64::from(sample_rate.max(1))
}
pub fn end_ms(&self, sample_rate: u32) -> u64 {
(self.end_sample * 1000) / u64::from(sample_rate.max(1))
}
pub fn len_samples(&self) -> u64 {
self.end_sample.saturating_sub(self.start_sample)
}
}
#[derive(Debug, Clone)]
pub struct Segmenter {
threshold: f32,
neg_threshold: f32,
min_speech_samples: u64,
min_silence_samples: u64,
speech_pad_samples: u64,
max_speech_samples: u64, frame_size: u64,
triggered: bool,
current_start: u64,
temp_end: u64,
next_pos: u64,
prev_end: u64,
}
impl Segmenter {
pub fn new(config: &VadConfig) -> Self {
Segmenter {
threshold: config.threshold,
neg_threshold: config.resolved_neg_threshold(),
min_speech_samples: config.ms_to_samples(config.min_speech_ms),
min_silence_samples: config.ms_to_samples(config.min_silence_ms),
speech_pad_samples: config.ms_to_samples(config.speech_pad_ms),
max_speech_samples: config.ms_to_samples(config.max_speech_ms),
frame_size: config.frame_size() as u64,
triggered: false,
current_start: 0,
temp_end: 0,
next_pos: 0,
prev_end: 0,
}
}
pub fn reset(&mut self) {
self.triggered = false;
self.current_start = 0;
self.temp_end = 0;
self.next_pos = 0;
self.prev_end = 0;
}
pub fn feed(&mut self, prob: f32, out: &mut Vec<Segment>) {
let frame_start = self.next_pos;
let frame_end = frame_start + self.frame_size;
self.next_pos = frame_end;
if prob >= self.threshold {
if self.temp_end != 0 {
self.temp_end = 0;
}
if !self.triggered {
self.triggered = true;
self.current_start = frame_start;
}
self.check_max_speech(frame_end, out);
return;
}
if prob < self.neg_threshold && self.triggered {
if self.temp_end == 0 {
self.temp_end = frame_start;
}
if frame_start.saturating_sub(self.temp_end) >= self.min_silence_samples {
self.finalize_segment(self.current_start, self.temp_end, out);
self.triggered = false;
self.temp_end = 0;
return;
}
}
self.check_max_speech(frame_end, out);
}
fn check_max_speech(&mut self, frame_end: u64, out: &mut Vec<Segment>) {
if !self.triggered || self.max_speech_samples == 0 {
return;
}
let speech_len = frame_end.saturating_sub(self.current_start);
if speech_len <= self.max_speech_samples {
return;
}
if self.temp_end != 0 {
let split = self.temp_end;
self.finalize_segment(self.current_start, split, out);
self.current_start = split;
self.temp_end = 0;
} else {
self.finalize_segment(self.current_start, frame_end, out);
self.current_start = frame_end;
}
}
pub fn flush(&mut self, out: &mut Vec<Segment>) {
if self.triggered {
self.finalize_segment(self.current_start, self.next_pos, out);
self.triggered = false;
self.temp_end = 0;
}
}
fn finalize_segment(&mut self, raw_start: u64, raw_end: u64, out: &mut Vec<Segment>) {
if raw_end.saturating_sub(raw_start) < self.min_speech_samples {
return;
}
let mut start = raw_start.saturating_sub(self.speech_pad_samples);
let end = raw_end + self.speech_pad_samples;
if self.prev_end != 0 && start < self.prev_end {
start = self.prev_end;
}
self.prev_end = end;
out.push(Segment {
start_sample: start,
end_sample: end,
});
}
}