use alloc::vec::Vec;
#[cfg(not(feature = "std"))]
use num_traits::Float;
const TARGET_RATE: f64 = 12_000.0;
pub fn resample_to_12k(samples: &[i16], src_rate: u32) -> Vec<i16> {
let ratio = TARGET_RATE / src_rate as f64;
let out_len = (samples.len() as f64 * ratio).ceil() as usize;
let mut out = Vec::with_capacity(out_len);
for i in 0..out_len {
let src_pos = i as f64 / ratio;
let idx = src_pos as usize;
let frac = src_pos - idx as f64;
if idx + 1 < samples.len() {
let a = samples[idx] as f64;
let b = samples[idx + 1] as f64;
let v = a + (b - a) * frac;
out.push(v.round() as i16);
} else if idx < samples.len() {
out.push(samples[idx]);
}
}
out
}
pub fn resample_f32_to_12k(samples: &[f32], src_rate: u32) -> Vec<i16> {
const TARGET_PEAK: f64 = 0.8;
const SILENCE_FLOOR: f64 = 1e-6;
let peak = samples.iter().fold(0.0f64, |m, &s| m.max((s as f64).abs()));
let scale = if peak > SILENCE_FLOOR {
TARGET_PEAK / peak
} else {
1.0
};
let ratio = TARGET_RATE / src_rate as f64;
let out_len = (samples.len() as f64 * ratio).ceil() as usize;
let mut out = Vec::with_capacity(out_len);
for i in 0..out_len {
let src_pos = i as f64 / ratio;
let idx = src_pos as usize;
let frac = src_pos - idx as f64;
let v = if idx + 1 < samples.len() {
let a = samples[idx] as f64;
let b = samples[idx + 1] as f64;
a + (b - a) * frac
} else if idx < samples.len() {
samples[idx] as f64
} else {
continue;
};
let scaled = (v * scale * 32767.0).clamp(-32768.0, 32767.0);
out.push(scaled.round() as i16);
}
out
}
pub fn resample_f32_to_12k_f32(samples: &[f32], src_rate: u32) -> Vec<f32> {
if src_rate == 12_000 {
return samples.to_vec();
}
let ratio = TARGET_RATE / src_rate as f64;
let out_len = (samples.len() as f64 * ratio).ceil() as usize;
let mut out = Vec::with_capacity(out_len);
for i in 0..out_len {
let src_pos = i as f64 / ratio;
let idx = src_pos as usize;
let frac = src_pos - idx as f64;
let v = if idx + 1 < samples.len() {
let a = samples[idx] as f64;
let b = samples[idx + 1] as f64;
a + (b - a) * frac
} else if idx < samples.len() {
samples[idx] as f64
} else {
continue;
};
out.push(v as f32);
}
out
}
pub fn resample_i16_to_12k_f32(samples: &[i16], src_rate: u32) -> Vec<f32> {
if src_rate == 12_000 {
return samples.iter().map(|&s| s as f32 / 32768.0).collect();
}
resample_to_12k(samples, src_rate)
.into_iter()
.map(|s| s as f32 / 32768.0)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn passthrough_at_12k() {
let input: Vec<i16> = (0..100).collect();
let out = resample_to_12k(&input, 12000);
assert_eq!(out.len(), 100);
assert_eq!(out, input);
}
#[test]
fn downsample_from_48k() {
let input: Vec<i16> = (0..4800).map(|i| (i % 100) as i16).collect();
let out = resample_to_12k(&input, 48000);
assert_eq!(out.len(), 1200);
}
#[test]
fn downsample_from_44100() {
let input: Vec<i16> = vec![0i16; 44100];
let out = resample_to_12k(&input, 44100);
assert!((out.len() as i32 - 12000).abs() <= 1);
}
}