use crate::timestamp_parse::TIMESTAMP_QUANTUM_SEC;
pub fn slice_encoder_hidden(
enc: &[f32],
enc_seq: usize,
d_model: usize,
start_sec: f32,
end_sec: f32,
) -> (Vec<f32>, usize) {
if enc_seq == 0 || enc.is_empty() {
return (Vec::new(), 0);
}
let mut f0 = (start_sec / TIMESTAMP_QUANTUM_SEC).floor() as isize;
let mut f1 = (end_sec / TIMESTAMP_QUANTUM_SEC).ceil() as isize;
if f1 <= f0 {
f0 = 0;
f1 = enc_seq as isize;
}
let f0 = f0.clamp(0, enc_seq as isize) as usize;
let f1 = f1.clamp(f0 as isize + 1, enc_seq as isize) as usize;
let out_seq = f1 - f0;
let mut out = vec![0f32; out_seq * d_model];
for f in f0..f1 {
let dst = (f - f0) * d_model;
let src = f * d_model;
out[dst..dst + d_model].copy_from_slice(&enc[src..src + d_model]);
}
(out, out_seq)
}
pub fn median_filter_1d(x: &[f32], width: usize) -> Vec<f32> {
if width <= 1 || x.is_empty() {
return x.to_vec();
}
let pad = width / 2;
let mut out = vec![0f32; x.len()];
for i in 0..x.len() {
let lo = i.saturating_sub(pad);
let hi = (i + pad + 1).min(x.len());
let mut window: Vec<f32> = x[lo..hi].to_vec();
window.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
out[i] = window[window.len() / 2];
}
out
}
pub fn dtw(x: &[f32], n_tokens: usize, n_frames: usize) -> (Vec<usize>, Vec<usize>) {
if n_tokens == 0 || n_frames == 0 {
return (Vec::new(), Vec::new());
}
let mut cost = vec![f32::INFINITY; (n_tokens + 1) * (n_frames + 1)];
let mut trace = vec![-1i8; (n_tokens + 1) * (n_frames + 1)];
cost[0] = 0.0;
let idx = |i: usize, j: usize| i * (n_frames + 1) + j;
for j in 1..=n_frames {
for i in 1..=n_tokens {
let c0 = cost[idx(i - 1, j - 1)];
let c1 = cost[idx(i - 1, j)];
let c2 = cost[idx(i, j - 1)];
let (c, t) = if c0 <= c1 && c0 <= c2 {
(c0, 0i8)
} else if c1 <= c0 && c1 <= c2 {
(c1, 1i8)
} else {
(c2, 2i8)
};
cost[idx(i, j)] = x[(i - 1) * n_frames + (j - 1)] + c;
trace[idx(i, j)] = t;
}
}
let mut i = n_tokens;
let mut j = n_frames;
let mut text_indices = Vec::new();
let mut time_indices = Vec::new();
while i > 0 || j > 0 {
text_indices.push(i.saturating_sub(1));
time_indices.push(j.saturating_sub(1));
if i == 0 {
j -= 1;
continue;
}
if j == 0 {
i -= 1;
continue;
}
match trace[idx(i, j)] {
0 => {
i -= 1;
j -= 1;
}
1 => i -= 1,
_ => j -= 1,
}
}
text_indices.reverse();
time_indices.reverse();
(text_indices, time_indices)
}
pub fn split_to_word_tokens(
tokenizer: &tokenizers::Tokenizer,
text_tokens: &[u32],
) -> (Vec<String>, Vec<Vec<u32>>) {
if text_tokens.is_empty() {
return (Vec::new(), Vec::new());
}
let mut words = Vec::new();
let mut word_tokens: Vec<Vec<u32>> = Vec::new();
let mut current = Vec::new();
for &tok in text_tokens {
current.push(tok);
let piece = tokenizer.decode(&[tok], false).unwrap_or_default();
if (piece.starts_with(' ') || piece.starts_with('\u{3000}')) && current.len() > 1 {
let prev: Vec<u32> = current[..current.len() - 1].to_vec();
let w = tokenizer.decode(&prev, false).unwrap_or_default();
if !w.trim().is_empty() {
words.push(w);
word_tokens.push(prev);
}
current = vec![tok];
}
}
if !current.is_empty() {
let w = tokenizer.decode(¤t, false).unwrap_or_default();
if !w.trim().is_empty() {
words.push(w);
word_tokens.push(current);
}
}
if words.is_empty() {
let w = tokenizer.decode(text_tokens, false).unwrap_or_default();
words.push(w);
word_tokens.push(text_tokens.to_vec());
}
(words, word_tokens)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn slice_encoder_hidden_covers_range() {
let enc_seq = 10;
let d = 4;
let enc: Vec<f32> = (0..enc_seq * d).map(|x| x as f32).collect();
let (sl, n) = slice_encoder_hidden(&enc, enc_seq, d, 0.04, 0.10);
assert_eq!(n, 3); assert_eq!(sl.len(), n * d);
}
#[test]
fn dtw_toy_path() {
let costs = vec![
1.0, 2.0, 3.0, 2.0, 1.0, 2.0,
];
let (ti, fi) = dtw(&costs, 2, 3);
assert_eq!(ti.len(), fi.len());
assert!(!ti.is_empty());
}
}