use crate::silk::SilkChannelState;
const REWHITEN_HISTORY: usize = 320;
const LTP_HISTORY: usize = 480;
pub fn synthesize(
excitation: &[f32],
lpc_per_sf: &[Vec<f32>],
gains_q16: &[i32],
pitch_lags: &[i32],
ltp_filter: &[[f32; 5]],
ltp_scale_q14: i32,
subframe_len: usize,
n_subframes: usize,
lpc_order: usize,
voiced: bool,
interp_coef: u8,
state: &mut SilkChannelState,
) -> Vec<f32> {
let frame_len = excitation.len();
let mut out = vec![0f32; frame_len];
if state.lpc_history.len() < lpc_order {
state.lpc_history.resize(lpc_order, 0.0);
}
if state.ltp_history.len() < LTP_HISTORY {
state.ltp_history.resize(LTP_HISTORY, 0.0);
}
if state.out_history.len() < REWHITEN_HISTORY {
state.out_history.resize(REWHITEN_HISTORY, 0.0);
}
let ltp_scale = ltp_scale_q14 as f32 / 16384.0;
let mut res_ring = vec![0.0f32; frame_len];
let mut lpc_ring = vec![0.0f32; frame_len];
for sf in 0..n_subframes {
let sf_start = sf * subframe_len;
let sf_end = sf_start + subframe_len;
let lpc = &lpc_per_sf[sf];
let g = gains_q16[sf].max(1) as f32 / 65536.0;
let taps = <p_filter[sf];
let lag = pitch_lags[sf];
let _ = interp_coef;
for n in sf_start..sf_end {
let mut res = g * excitation[n];
if voiced && lag > 0 {
for k in 0..5usize {
let idx = n as i32 - lag + 2 - k as i32;
let past_res = if idx >= 0 && (idx as usize) < n {
res_ring[idx as usize]
} else if idx >= 0 {
0.0
} else {
let abs_j = state.ltp_history.len() as i32 + idx;
if abs_j >= 0 && (abs_j as usize) < state.ltp_history.len() {
state.ltp_history[abs_j as usize]
} else {
0.0
}
};
res += taps[k] * ltp_scale * past_res;
}
}
res_ring[n] = res;
let mut s = res;
for k in 1..=lpc_order {
let idx = n as i32 - k as i32;
let past_out = if idx >= 0 {
lpc_ring[idx as usize]
} else {
let h_idx = (state.lpc_history.len() as i32 + idx) as usize;
state.lpc_history.get(h_idx).copied().unwrap_or(0.0)
};
s += lpc[k - 1] * past_out;
}
lpc_ring[n] = s;
out[n] = s.clamp(-1.0, 1.0);
}
}
let lpc_keep = lpc_order.min(lpc_ring.len());
state.lpc_history = lpc_ring[lpc_ring.len() - lpc_keep..].to_vec();
shift_ring(&mut state.ltp_history, &res_ring, LTP_HISTORY);
shift_ring(&mut state.out_history, &out, REWHITEN_HISTORY);
out
}
fn sample_history(i: i32, cur: &[f32], history: &[f32]) -> f32 {
if i >= 0 && (i as usize) < cur.len() {
cur[i as usize]
} else if i < 0 {
let abs = history.len() as i32 + i;
if abs >= 0 && (abs as usize) < history.len() {
history[abs as usize]
} else {
0.0
}
} else {
0.0
}
}
fn shift_ring(ring: &mut Vec<f32>, frame: &[f32], cap: usize) {
if frame.len() >= cap {
*ring = frame[frame.len() - cap..].to_vec();
return;
}
let keep = cap - frame.len();
let mut new_ring = Vec::with_capacity(cap);
if ring.len() >= keep {
new_ring.extend_from_slice(&ring[ring.len() - keep..]);
} else {
new_ring.resize(keep, 0.0);
}
new_ring.extend_from_slice(frame);
if new_ring.len() > cap {
let drop = new_ring.len() - cap;
new_ring.drain(0..drop);
}
*ring = new_ring;
}
pub fn upsample_to_48k(samples: &[f32], src_rate: u32) -> Vec<f32> {
match src_rate {
8_000 => upsample(samples, 6),
12_000 => upsample(samples, 4),
16_000 => upsample(samples, 3),
24_000 => upsample(samples, 2),
48_000 => samples.to_vec(),
_ => upsample(samples, 48_000 / src_rate),
}
}
fn upsample(samples: &[f32], factor: u32) -> Vec<f32> {
let f = factor as usize;
if f <= 1 {
return samples.to_vec();
}
let mut upsampled = vec![0f32; samples.len() * f];
for (i, &s) in samples.iter().enumerate() {
upsampled[i * f] = s * (f as f32);
}
let win_len = 2 * f + 1;
let mut win = vec![0f32; win_len];
for k in 0..win_len {
let phase = (k as f32 - f as f32) * core::f32::consts::PI / (f as f32);
let sinc = if phase.abs() < 1e-6 {
1.0
} else {
phase.sin() / phase
};
let hann =
0.5 - 0.5 * (2.0 * core::f32::consts::PI * k as f32 / (win_len as f32 - 1.0)).cos();
win[k] = sinc * hann;
}
let gain: f32 = win.iter().sum();
for w in win.iter_mut() {
*w /= gain;
}
let mut out = vec![0f32; upsampled.len()];
for n in 0..upsampled.len() {
let mut acc = 0f32;
for k in 0..win_len {
let idx = n as i32 + k as i32 - f as i32;
if idx >= 0 && (idx as usize) < upsampled.len() {
acc += win[k] * upsampled[idx as usize];
}
}
out[n] = acc;
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn upsample_length() {
let input = vec![0.0; 160];
let out = upsample_to_48k(&input, 8_000);
assert_eq!(out.len(), 960);
}
#[test]
fn upsample_factor_1() {
let input = vec![1.0, 2.0, 3.0];
let out = upsample_to_48k(&input, 48_000);
assert_eq!(out, input);
}
}