use super::SECONDS_PER_FRAME;
use super::decode;
use super::state::WordInfo;
use super::tokenizer::{self, Tokenizer};
pub(crate) fn stitch_chunk_words(
mut merged: Vec<WordInfo>,
next: Vec<WordInfo>,
seam_s: f64,
) -> Vec<WordInfo> {
if merged.is_empty() {
return next;
}
merged.truncate(merged.partition_point(|w| w.start <= seam_s));
merged.extend(next.into_iter().filter(|w| w.start > seam_s));
merged
}
pub(crate) struct TokenFormatter;
impl TokenFormatter {
pub(crate) fn tokens_to_words(
tokenizer: &Tokenizer,
tokens: &[decode::TokenInfo],
frame_offset: usize,
) -> Vec<WordInfo> {
if tokens.is_empty() {
return Vec::new();
}
let mut words = Vec::new();
let mut current_word = String::new();
let mut word_start_frame: Option<usize> = None;
let mut word_end_frame: usize = 0;
let mut word_confidences: Vec<f32> = Vec::new();
for token in tokens {
let token_text = tokenizer.token_text(token.token_id);
let is_word_boundary = token_text.starts_with(tokenizer::WORD_BOUNDARY);
if is_word_boundary && !current_word.is_empty() {
let avg_conf: f32 = if word_confidences.is_empty() {
1.0
} else {
word_confidences.iter().sum::<f32>() / word_confidences.len() as f32
};
words.push(WordInfo {
word: std::mem::take(&mut current_word),
start: (word_start_frame.unwrap_or(0) + frame_offset) as f64
* SECONDS_PER_FRAME,
end: (word_end_frame + frame_offset) as f64 * SECONDS_PER_FRAME,
confidence: avg_conf,
speaker: None,
});
current_word.clear();
word_confidences.clear();
word_start_frame = None;
}
let clean = if let Some(stripped) = token_text.strip_prefix(tokenizer::WORD_BOUNDARY) {
stripped
} else {
token_text
};
if !clean.is_empty() {
current_word.push_str(clean);
if word_start_frame.is_none() {
word_start_frame = Some(token.frame_index);
}
word_end_frame = token.frame_index;
word_confidences.push(token.confidence);
}
}
if !current_word.is_empty() {
let avg_conf: f32 = if word_confidences.is_empty() {
1.0
} else {
word_confidences.iter().sum::<f32>() / word_confidences.len() as f32
};
words.push(WordInfo {
word: current_word,
start: (word_start_frame.unwrap_or(0) + frame_offset) as f64 * SECONDS_PER_FRAME,
end: (word_end_frame + frame_offset) as f64 * SECONDS_PER_FRAME,
confidence: avg_conf,
speaker: None,
});
}
words
}
}
#[cfg(test)]
mod tests;