use crate::ssf::{SsfBlock, SsfData, SsfGranule, StrideFlag};
use crate::ssf_ac::{idx_to_reconstruction, SsfRandGenState};
use crate::ssf_pred_coeff::{ssf_pred_coeff_mat, SSF_PRED_MAT_DIMS};
use crate::ssf_tables::{
OFFSETS_DB_TO_LIN, OFFSETS_LIN_TO_DB, POST_GAIN_LUT, PRED_GAIN_QUANT_TAB, PRED_RFS_TABLE,
PRED_RTS_TABLE, SLOPES_DB_TO_LIN, SLOPES_LIN_TO_DB, STEP_SIZES_Q4_15,
};
pub const NUM_SPEC_BUF: usize = 5;
pub const NUM_ENV_BUF: usize = 4;
pub const SSF_HIGH_FREQ_GAIN_THRESHOLD: usize = 2;
pub const ENV_MIN: i32 = -64;
pub const ENV_MAX: i32 = 63;
pub const MIN_ALLOC_OFFSET: i32 = -21;
pub const ENV_MAX_2_MIN_OFFSET: i32 = 20;
pub const RFU_THRESHOLD: f32 = 0.75;
pub const ALLOC_DITHERING_THRESHOLD_SMALL: i32 = 3;
pub const ALLOC_DITHERING_THRESHOLD_LARGE: i32 = 5;
pub fn decode_envelope(env_curr_offset: &[i32]) -> Vec<i32> {
const ENV_DELTA_MIN: i32 = -16;
let n = env_curr_offset.len();
let mut env = vec![0i32; n];
if n == 0 {
return env;
}
env[0] = env_curr_offset[0];
for band in 1..n {
let delta = env_curr_offset[band] + ENV_DELTA_MIN;
env[band] = env[band - 1] + delta;
}
env
}
pub fn interpolate_envelope(env: &[i32], env_prev: &[i32], num_blocks: usize) -> Vec<Vec<i32>> {
let n = env.len();
let mut env_interp = vec![vec![0i32; n]; num_blocks];
if num_blocks == 0 || n == 0 {
return env_interp;
}
let inv_num_blocks_q10: i32 = 256; for band in 0..n {
let prev = if band < env_prev.len() {
env_prev[band]
} else {
0
};
let left_delta_q10 = (env[band] - prev) * 1024;
let left_slope_q10 = ((left_delta_q10 as i64 * inv_num_blocks_q10 as i64) >> 10) as i32;
for (block, row) in env_interp.iter_mut().enumerate() {
let interp_q10 = (1 + block as i32) * left_slope_q10 + prev * 1024;
let rounded = if interp_q10 > 0 {
(interp_q10 + 512) >> 10
} else {
-(((-interp_q10) + 512) >> 10)
};
row[band] = rounded;
}
}
env_interp
}
pub fn decode_gains(blocks: &[SsfBlock], stride_flag: StrideFlag) -> Vec<f32> {
let num_blocks = blocks.len();
let mut gain = vec![1.0_f32; num_blocks];
if matches!(stride_flag, StrideFlag::LongStride) {
return gain;
}
for (block, slot) in gain.iter_mut().enumerate() {
let idx = blocks[block].gain_idx() as f32;
*slot = (10.0_f32).powf(idx * 0.1);
}
gain
}
pub fn refine_envelope(
env_interp: &[Vec<i32>],
gain: &[f32],
blocks: &[SsfBlock],
) -> (Vec<Vec<i32>>, Vec<Vec<f32>>) {
let num_blocks = env_interp.len();
let num_bands = env_interp.first().map(Vec::len).unwrap_or(0);
let mut f_env_signal = vec![vec![0.0_f32; num_bands]; num_blocks];
let mut env_alloc = vec![vec![0i32; num_bands]; num_blocks];
for block in 0..num_blocks {
for band in 0..num_bands {
let interp = env_interp[block][band];
let mut sig = (2.0_f32).powf(0.5 * interp as f32);
if band >= SSF_HIGH_FREQ_GAIN_THRESHOLD && block < gain.len() {
sig *= gain[block];
}
f_env_signal[block][band] = sig;
let mut alloc = interp;
if band >= SSF_HIGH_FREQ_GAIN_THRESHOLD && block < blocks.len() {
let gain_idx = blocks[block].gain_idx();
let two = 2 * gain_idx;
let q = if two >= 0 {
(two + 1) / 3
} else {
-((-two + 1) / 3)
};
alloc += q;
alloc = alloc.clamp(ENV_MIN, ENV_MAX);
}
env_alloc[block][band] = alloc;
}
}
(env_alloc, f_env_signal)
}
pub fn decode_predictor(
block: &SsfBlock,
block_idx: u32,
start_block: u32,
end_block: u32,
i_prev_pred_lag_idx: &mut i32,
n_mdct: u32,
) -> (f32, f32) {
const PRED_LAG_DELTA_MIN: i32 = -8;
let mut i_pred_lag_idx: i32 = 0;
let mut f_pred_gain: f32 = 0.0;
if block_idx >= start_block && block_idx < end_block && block.predictor_presence {
let i_pred_gain_idx = block.pred_gain_idx.unwrap_or(0).clamp(0, 31);
f_pred_gain = PRED_GAIN_QUANT_TAB[i_pred_gain_idx as usize];
if block.delta_flag {
i_pred_lag_idx = i32::from(block.predictor_lag_delta_bits)
+ *i_prev_pred_lag_idx
+ PRED_LAG_DELTA_MIN;
} else {
i_pred_lag_idx = i32::from(block.predictor_lag_bits);
}
}
*i_prev_pred_lag_idx = i_pred_lag_idx;
let f_pred_lag = 640.0_f32 * (2.0_f32).powf((i_pred_lag_idx as f32 - 509.0) / 170.0);
let _ = n_mdct;
(f_pred_gain, f_pred_lag)
}
#[derive(Debug, Clone, Copy)]
pub struct SpectrumHelpers {
pub f_rfu: f32,
pub i_alloc_dithering_threshold: i32,
pub f_adaptive_noise_gain: f32,
pub f_adaptive_noise_gain_var_pres: f32,
}
pub fn compute_helpers(f_pred_gain: f32, variance_preserving: bool) -> SpectrumHelpers {
let g = f_pred_gain;
let f_rfu = if g < -1.0 {
1.0
} else if g < 0.0 {
-g
} else if g < 1.0 {
g
} else if g < 2.0 {
2.0 - g
} else {
0.0
};
let mut i_alloc_dithering_threshold = if f_rfu > RFU_THRESHOLD {
ALLOC_DITHERING_THRESHOLD_SMALL
} else {
ALLOC_DITHERING_THRESHOLD_LARGE
};
if variance_preserving {
i_alloc_dithering_threshold = ALLOC_DITHERING_THRESHOLD_LARGE;
}
let f_adaptive_noise_gain_var_pres = (1.0 - f_rfu * f_rfu).max(0.0).sqrt();
let f_adaptive_noise_gain = 1.0 - f_rfu;
SpectrumHelpers {
f_rfu,
i_alloc_dithering_threshold,
f_adaptive_noise_gain,
f_adaptive_noise_gain_var_pres,
}
}
pub fn map_db_to_lin_q10(i_input_q10: i32) -> i32 {
let i_input_q4 = i_input_q10 >> 6;
let i_index = i_input_q4 >> 6;
if i_index >= 0 && i_index < SLOPES_DB_TO_LIN.len() as i32 {
let slope = SLOPES_DB_TO_LIN[i_index as usize];
let mut i_res = slope * i_input_q4; i_res >>= 4; i_res += OFFSETS_DB_TO_LIN[i_index as usize]; i_res << 6 } else {
100i32 << 10
}
}
pub fn map_lin_to_db_q10(i_input_q10: i32) -> i32 {
let i_input_q8 = i_input_q10 >> 2;
let i_quant_in = i_input_q8 >> 1;
let i_index = i_quant_in >> 8;
let i_int = i_index << (8 + 1); let i_fract = i_input_q8 - i_int;
if i_index >= 0 && i_index < SLOPES_LIN_TO_DB.len() as i32 {
let slope = SLOPES_LIN_TO_DB[i_index as usize];
let i_tmp2_a = i_index << 1; let i_tmp1 = slope * i_tmp2_a; let i_tmp2_b_q16 = slope * i_fract; let i_tmp2_b = i_tmp2_b_q16 >> 8; let mut i_res = i_tmp1 + i_tmp2_b;
i_res += OFFSETS_LIN_TO_DB[i_index as usize]; i_res << 2 } else {
40i32 << 10
}
}
pub fn heuristic_scaling(
i_rfu_q10: i32,
env_in_q0: &[i32],
band_widths_qx0: &[i32],
num_bins: i32,
) -> Vec<i32> {
let num_bands = env_in_q0.len();
if num_bands == 0 {
return Vec::new();
}
const I_DYN_THRESHOLD_Q10: i32 = 40 << 10; const I_MAXI_W_DB_Q10: i32 = 15 << 10; const I_INV_THREE_Q10: i32 = 341;
let i_max_env: i32 = *env_in_q0.iter().max().unwrap_or(&0);
let i_min_env: i32 = *env_in_q0.iter().min().unwrap_or(&0);
let i_dyn_unscaled = i_max_env - i_min_env;
let i_dyn_q10 = i_dyn_unscaled << 10;
let mut env_local_q10 = vec![0i32; num_bands];
if i_dyn_q10 > I_DYN_THRESHOLD_Q10 && i_dyn_unscaled > 0 {
let i_cmp_fact_q10 = I_DYN_THRESHOLD_Q10 / i_dyn_unscaled;
for band in 0..num_bands {
let v = env_in_q0[band] - i_min_env;
env_local_q10[band] = v * i_cmp_fact_q10;
}
} else {
for band in 0..num_bands {
let v = env_in_q0[band] - i_min_env;
env_local_q10[band] = v << 10;
}
}
let mut env_indices: Vec<usize> = (0..num_bands).collect();
env_indices.sort_by(|&a, &b| env_local_q10[b].cmp(&env_local_q10[a]));
let env_local_sorted_q10: Vec<i32> = env_indices.iter().map(|&i| env_local_q10[i]).collect();
let weights_lin_q10: Vec<i32> = env_local_sorted_q10
.iter()
.map(|&v| map_db_to_lin_q10(v))
.collect();
let mut i_mtr: i64 = 0;
for band in 0..num_bands {
let bw = band_widths_qx0[env_indices[band]] as i64;
i_mtr += weights_lin_q10[band] as i64 * bw; }
i_mtr >>= 10; i_mtr *= i_rfu_q10 as i64; i_mtr >>= 7;
i_mtr *= i_rfu_q10 as i64;
i_mtr >>= 3;
let i_mtr = i_mtr as i32;
let mut i_mnt: i64 = 0;
let mut i_bsum: i64 = 0;
let mut band: usize = 0;
while i_mnt < i_mtr as i64 && band < num_bands - 1 {
let i_t_curr_lev = weights_lin_q10[band];
while band < num_bands - 1 && weights_lin_q10[band] == i_t_curr_lev {
i_bsum += band_widths_qx0[env_indices[band]] as i64;
band += 1;
}
let i_tmp2 = (i_t_curr_lev as i64) - (weights_lin_q10[band] as i64); let contribution = i_tmp2 * i_bsum; i_mnt += contribution;
}
if i_mnt < i_mtr as i64 {
i_bsum = num_bins as i64;
}
if i_bsum == 0 {
i_bsum = 1; }
let i_tmp = (i_mnt - i_mtr as i64) << 4; let i_tmp2 = i_tmp / i_bsum; let i_tmp2 = i_tmp2 >> 4; let i_t_curr_lev_final = (weights_lin_q10[band] as i64 + i_tmp2) as i32;
let mut int_weights_db_q10 = vec![0i32; num_bands];
let i_tmp2_db = map_lin_to_db_q10(i_t_curr_lev_final); for band in 0..num_bands {
let i_tmp = env_local_q10[band] - i_tmp2_db;
let i_tmp_q20 = (i_tmp as i64) * (I_INV_THREE_Q10 as i64);
let i_tmp_q10 = ((i_tmp_q20 >> 10) as i32).clamp(0, I_MAXI_W_DB_Q10);
int_weights_db_q10[band] = i_tmp_q10;
}
int_weights_db_q10
}
pub fn apply_heuristic_scaling(
env_alloc: &[i32],
band_widths: &[u8],
num_bins: u32,
f_rfu: f32,
) -> (Vec<i32>, Vec<f32>) {
let num_bands = env_alloc.len();
if num_bands == 0 {
return (Vec::new(), Vec::new());
}
let env_in_q0: Vec<i32> = env_alloc.iter().map(|&v| 3 * v).collect();
let band_widths_qx0: Vec<i32> = band_widths
.iter()
.take(num_bands)
.map(|&v| i32::from(v))
.collect();
let i_rfu_q10 = (f_rfu * 1024.0).round().clamp(0.0, i32::MAX as f32) as i32;
let int_weights_db_q10 =
heuristic_scaling(i_rfu_q10, &env_in_q0, &band_widths_qx0, num_bins as i32);
let mut i_w_db = vec![0i32; num_bands];
for band in 0..num_bands {
i_w_db[band] = (int_weights_db_q10[band] / 2) >> 10;
}
const LF_BOOST_THRESHOLD: i32 = 3;
if i_w_db[0] > LF_BOOST_THRESHOLD {
i_w_db[0] -= LF_BOOST_THRESHOLD;
} else {
i_w_db[0] = 0;
}
let mut f_gain_q = vec![1.0_f32; num_bands];
let mut env_alloc_mod = vec![0i32; num_bands];
for band in 0..num_bands {
let f_w_db = int_weights_db_q10[band] as f32 / 1024.0;
f_gain_q[band] = (10.0_f32).powf(1.5 / 20.0 * f_w_db);
env_alloc_mod[band] = (env_alloc[band] - i_w_db[band]).clamp(ENV_MIN, ENV_MAX);
}
(env_alloc_mod, f_gain_q)
}
pub fn build_alloc_table(env_alloc_mod: &[i32], alloc_offset_bits: u8) -> Vec<u32> {
let n = env_alloc_mod.len();
let i_alloc_offset = i32::from(alloc_offset_bits) + MIN_ALLOC_OFFSET;
let mut i_max = env_alloc_mod.first().copied().unwrap_or(0);
for &v in env_alloc_mod.iter().skip(1) {
if v > i_max {
i_max = v;
}
}
i_max -= ENV_MAX_2_MIN_OFFSET;
let mut tab = vec![0u32; n];
for band in 0..n {
let v = env_alloc_mod[band] - i_max + i_alloc_offset;
let v = v.clamp(0, 20);
tab[band] = v as u32;
}
tab
}
pub fn mmse_laplace(f_mid_point: f32, f_step_size: f32) -> f32 {
let s2 = std::f32::consts::SQRT_2;
let f_upper = f_mid_point + f_step_size / 2.0;
let f_lower = f_mid_point - f_step_size / 2.0;
let f_pdf_lower = (s2 / 2.0) * (-f_lower.abs() * s2).exp();
let f_pdf_upper = (s2 / 2.0) * (-f_upper.abs() * s2).exp();
let f_pdf_lower = f_pdf_lower.max(0.0);
let f_pdf_upper = f_pdf_upper.max(0.0);
if f_lower > 0.0 {
let num = f_pdf_upper * (s2 * f_upper + 1.0) - f_pdf_lower * (s2 * f_lower + 1.0);
let den = s2 * (f_pdf_upper - f_pdf_lower);
if den.abs() < 1e-12 {
f_mid_point
} else {
num / den
}
} else if f_upper < 0.0 {
let num = f_pdf_upper * (s2 * f_upper - 1.0) - f_pdf_lower * (s2 * f_lower - 1.0);
let den = s2 * (f_pdf_upper - f_pdf_lower);
if den.abs() < 1e-12 {
f_mid_point
} else {
num / den
}
} else {
let num = f_pdf_lower * (s2 * f_lower - 1.0) + f_pdf_upper * (s2 * f_upper + 1.0);
let den = s2 * (f_pdf_lower + f_pdf_upper) - 2.0;
if den.abs() < 1e-12 {
f_mid_point
} else {
num / den
}
}
}
#[allow(clippy::too_many_arguments)]
pub fn inverse_quantize_block(
i_alloc_table: &[u32],
bands: &[(usize, usize)],
i_quant_idx: &[i32],
helpers: &SpectrumHelpers,
variance_preserving: bool,
noise_rng: &mut SsfRandGenState,
dither_rng: &mut SsfRandGenState,
out: &mut [f32],
) {
const I_MODEL_UNIT: f32 = (1u32 << 15) as f32;
for (band_idx, &i_alloc) in i_alloc_table.iter().enumerate() {
let (start_bin, end_bin) = bands[band_idx];
let i_step_size = STEP_SIZES_Q4_15[i_alloc as usize];
for bin in start_bin..=end_bin {
if i_alloc == 0 {
let mut s = noise_rng.random_noise_value();
if variance_preserving && band_idx > 1 {
s *= helpers.f_adaptive_noise_gain_var_pres;
} else {
s *= helpers.f_adaptive_noise_gain;
}
out[bin] = s;
} else if (i_alloc as i32) < helpers.i_alloc_dithering_threshold {
let i_dither = dither_rng.dither_value();
let i_mid = idx_to_reconstruction(i_quant_idx[bin], i_dither, i_step_size);
let f_mid = i_mid as f32 / I_MODEL_UNIT;
let mut f_post_gain = POST_GAIN_LUT[(i_alloc - 1) as usize];
if variance_preserving && band_idx > 1 {
let var_pres = f_post_gain.sqrt() * helpers.f_adaptive_noise_gain_var_pres;
if var_pres > f_post_gain {
f_post_gain = var_pres;
}
}
out[bin] = f_mid * f_post_gain;
} else {
let i_mid = idx_to_reconstruction(i_quant_idx[bin], 0, i_step_size);
let f_mid = i_mid as f32 / I_MODEL_UNIT;
let f_step = i_step_size as f32 / I_MODEL_UNIT;
out[bin] = mmse_laplace(f_mid, f_step);
}
}
}
}
pub fn inverse_heuristic_scale(spec_invq: &mut [f32], bands: &[(usize, usize)], f_gain_q: &[f32]) {
for (band_idx, &(start_bin, end_bin)) in bands.iter().enumerate() {
let g = if band_idx < f_gain_q.len() {
f_gain_q[band_idx]
} else {
1.0
};
if (g - 1.0).abs() < 1e-9 {
continue;
}
let inv = 1.0 / g;
for slot in spec_invq.iter_mut().take(end_bin + 1).skip(start_bin) {
*slot *= inv;
}
}
}
pub fn build_c_matrix(tab_idx: usize) -> Option<Vec<Vec<Vec<f32>>>> {
if tab_idx >= 37 {
return None;
}
let rt = PRED_RTS_TABLE[tab_idx] as usize;
let rf = PRED_RFS_TABLE[tab_idx] as usize;
let two_rf_p1 = 2 * rf + 1;
let raw = ssf_pred_coeff_mat(tab_idx)?;
let (rows, cols) = SSF_PRED_MAT_DIMS[tab_idx];
if rows != two_rf_p1 || cols != 33 * rt {
return None;
}
let mut c = vec![vec![vec![0.0_f32; rt]; 65]; two_rf_p1];
for (nu_idx, c_row) in c.iter_mut().enumerate().take(two_rf_p1) {
for k in 0..rt {
for eta in 0..33usize {
let byte = raw[nu_idx * cols + k * 33 + eta] as i32;
let recon = 1.1787855_f32 * (byte as f32 - 146.0) / 128.0;
c_row[eta + 32][k] = recon;
}
}
}
#[allow(clippy::needless_range_loop)]
for k in 0..rt {
let s = if (k & 1) == 0 { -1.0_f32 } else { 1.0_f32 };
for eta in -32_i32..0 {
for nu in -(rf as i32)..=(rf as i32) {
let nu_idx = (nu + rf as i32) as usize;
let neg_nu_idx = (-nu + rf as i32) as usize;
let eta_idx = (eta + 32) as usize;
let neg_eta_idx = (-eta + 32) as usize;
c[nu_idx][eta_idx][k] = s * c[neg_nu_idx][neg_eta_idx][k];
}
}
}
Some(c)
}
#[derive(Debug, Clone, Default)]
pub struct SubbandPredictorState {
pub f_spec_buffer: Vec<Vec<f32>>, pub f_env_buffer: Vec<Vec<f32>>, }
impl SubbandPredictorState {
pub fn new() -> Self {
Self::default()
}
pub fn clear(&mut self) {
self.f_spec_buffer.clear();
self.f_env_buffer.clear();
}
fn push_history(&mut self, f_spec_prev: &[f32], f_env_signal: &[f32]) {
if self.f_spec_buffer.len() < NUM_SPEC_BUF {
self.f_spec_buffer
.resize(NUM_SPEC_BUF, vec![0.0_f32; f_spec_prev.len()]);
}
for i in (1..NUM_SPEC_BUF).rev() {
self.f_spec_buffer[i] = self.f_spec_buffer[i - 1].clone();
}
self.f_spec_buffer[0] = f_spec_prev.to_vec();
if self.f_env_buffer.len() < NUM_ENV_BUF {
self.f_env_buffer
.resize(NUM_ENV_BUF, vec![0.0_f32; f_env_signal.len()]);
}
for i in (1..NUM_ENV_BUF).rev() {
self.f_env_buffer[i] = self.f_env_buffer[i - 1].clone();
}
self.f_env_buffer[0] = f_env_signal.to_vec();
}
#[allow(clippy::too_many_arguments)]
pub fn run(
&mut self,
f_spec_prev: &[f32],
f_env_signal: &[f32],
f_pred_gain: f32,
f_pred_lag: f32,
n_mdct: u32,
bands: &[(usize, usize)],
b_iframe: bool,
) -> Vec<f32> {
let num_bins = f_spec_prev.len();
self.push_history(f_spec_prev, f_env_signal);
if f_pred_gain == 0.0 || num_bins == 0 || n_mdct == 0 {
return vec![0.0; num_bins];
}
let mut f_period = f_pred_lag / n_mdct as f32;
let k_s = if f_period > 81.0 / 32.0 {
f_period -= 1.0;
1usize
} else {
0usize
};
let tab_idx = if f_period <= 9.0 / 32.0 {
0
} else {
((16.0 * f_period + 0.5).floor() as i32 - 4).clamp(0, 36) as usize
};
let rt = PRED_RTS_TABLE[tab_idx] as usize;
let rf = PRED_RFS_TABLE[tab_idx] as usize;
let c = match build_c_matrix(tab_idx) {
Some(c) => c,
None => return vec![0.0; num_bins],
};
let z_len = num_bins + 2 * rf;
let mut z = vec![vec![0.0_f32; rt]; z_len];
#[allow(clippy::needless_range_loop)]
for k in 0..rt {
let buf_idx = k + k_s;
if buf_idx < self.f_spec_buffer.len() && self.f_spec_buffer[buf_idx].len() == num_bins {
let copy: Vec<f32> = self.f_spec_buffer[buf_idx][..num_bins].to_vec();
for (n, v) in copy.into_iter().enumerate() {
z[n + rf][k] = v;
}
}
for n in (-(rf as i32))..0 {
let neg = -n - 1; z[(n + rf as i32) as usize][k] = z[(neg + rf as i32) as usize][k];
}
}
let mut f_spec_extract = vec![0.0_f32; num_bins];
for bin in 0..num_bins {
let mut tmp = 0.0_f32;
for nu in -(rf as i32)..=(rf as i32) {
let mu = (2 * bin as i32 + nu + 1) as f32;
let phi_unrounded = (f_period / 4.0) * mu;
let phi = phi_unrounded.round() - phi_unrounded;
let f_int: i32 = if phi > f_period {
32
} else if phi < -f_period {
-32
} else {
let two_t = 2.0 * f_period;
let min_2t = if two_t < 1.0 { two_t } else { 1.0 };
if min_2t.abs() < 1e-12 {
0
} else {
(64.0 * phi / min_2t).round() as i32
}
};
let f_idx = (f_int + 32).clamp(0, 64) as usize;
let nu_idx = (nu + rf as i32) as usize;
if bin % 2 == 0 {
for k in 0..rt {
let z_val = z[(bin as i32 + nu + rf as i32) as usize][k];
tmp += c[nu_idx][f_idx][k] * z_val;
}
} else {
let mut s = 1.0_f32;
for k in 0..rt {
s *= -1.0;
let z_val = z[(bin as i32 + nu + rf as i32) as usize][k];
tmp += s * c[nu_idx][f_idx][k] * z_val;
}
}
}
f_spec_extract[bin] = tmp;
}
let integer_lag_raw = (f_pred_lag / n_mdct as f32).round() as i32;
let integer_lag = if b_iframe && integer_lag_raw > 0 {
0usize
} else {
integer_lag_raw.max(0) as usize
};
let mut f_spec_pred = vec![0.0_f32; num_bins];
let env_buf_len = self.f_env_buffer.len();
let env_idx = integer_lag.min(env_buf_len.saturating_sub(1));
for (band_idx, &(start_bin, end_bin)) in bands.iter().enumerate() {
let env_v = self
.f_env_buffer
.get(env_idx)
.and_then(|row| row.get(band_idx).copied())
.unwrap_or(1.0);
if env_v.abs() < 1e-12 {
continue;
}
let f_envelope = 1.0 / env_v;
for bin in start_bin..=end_bin {
f_spec_pred[bin] = f_spec_extract[bin] * f_envelope * f_pred_gain;
}
}
f_spec_pred
}
}
pub fn inverse_flatten(
f_spec_res: &[f32],
f_spec_pred: &[f32],
f_env_signal: &[f32],
bands: &[(usize, usize)],
) -> Vec<f32> {
let num_bins = f_spec_res.len();
let mut f_spec = vec![0.0_f32; num_bins];
for bin in 0..num_bins {
let p = if bin < f_spec_pred.len() {
f_spec_pred[bin]
} else {
0.0
};
f_spec[bin] = f_spec_res[bin] + p;
}
for (band_idx, &(start_bin, end_bin)) in bands.iter().enumerate() {
let g = if band_idx < f_env_signal.len() {
f_env_signal[band_idx]
} else {
1.0
};
for slot in f_spec.iter_mut().take(end_bin + 1).skip(start_bin) {
*slot *= g;
}
}
f_spec
}
#[derive(Debug, Clone, Default)]
pub struct SsfSynthState {
pub dither_rng: SsfRandGenState,
pub noise_rng: SsfRandGenState,
pub prev_pred_lag_idx: i32,
pub pred_state: SubbandPredictorState,
pub f_spec_prev: Vec<f32>,
pub env_prev: Vec<i32>,
}
impl SsfSynthState {
pub fn new() -> Self {
Self::default()
}
pub fn reset_iframe(&mut self) {
self.dither_rng.reset();
self.noise_rng.reset();
self.pred_state.clear();
self.f_spec_prev.clear();
self.prev_pred_lag_idx = 0;
}
}
pub fn synthesize_granule(
granule: &SsfGranule,
env_prev: &[i32],
state: &mut SsfSynthState,
) -> Vec<f32> {
let num_blocks = granule.num_blocks as usize;
let n_mdct = granule.n_mdct as usize;
let num_bins = granule.num_bins as usize;
let num_bands = granule.num_bands as usize;
if granule.b_iframe {
state.reset_iframe();
}
let env = decode_envelope(&granule.env_curr);
let env_interp: Vec<Vec<i32>> = match granule.stride_flag {
StrideFlag::LongStride => vec![env.clone()],
StrideFlag::ShortStride => {
let prev_for_interp = if granule.b_iframe {
granule
.env_startup
.as_deref()
.map(decode_envelope)
.unwrap_or_else(|| vec![0i32; num_bands])
} else if !env_prev.is_empty() {
env_prev.to_vec()
} else if !state.env_prev.is_empty() {
let mut e = state.env_prev.clone();
e.resize(num_bands, 0);
e
} else {
vec![0i32; num_bands]
};
interpolate_envelope(&env, &prev_for_interp, num_blocks)
}
};
let gain = decode_gains(&granule.blocks, granule.stride_flag);
let (env_alloc, f_env_signal) = refine_envelope(&env_interp, &gain, &granule.blocks);
let layout = match crate::ssf::SsfBinLayout::build(num_bands, granule.n_mdct) {
Some(l) => l,
None => return vec![0.0; num_blocks * n_mdct],
};
let bands: Vec<(usize, usize)> = (0..num_bands)
.map(|b| (layout.start_bin[b] as usize, layout.end_bin[b] as usize))
.collect();
let band_widths_full = crate::ssf::ssf_band_widths_for(granule.n_mdct);
let mut out = vec![0.0_f32; num_blocks * n_mdct];
for block_idx in 0..num_blocks {
let blk = &granule.blocks[block_idx];
let (f_pred_gain, f_pred_lag) = decode_predictor(
blk,
block_idx as u32,
granule.start_block,
granule.end_block,
&mut state.prev_pred_lag_idx,
granule.n_mdct,
);
let helpers = compute_helpers(f_pred_gain, blk.variance_preserving);
let (env_alloc_mod, f_gain_q) = match band_widths_full.as_ref() {
Some(bw) if helpers.f_rfu > 0.0 && !blk.variance_preserving => apply_heuristic_scaling(
&env_alloc[block_idx],
&bw[..num_bands],
num_bins as u32,
helpers.f_rfu,
),
_ => (env_alloc[block_idx].clone(), vec![1.0_f32; num_bands]),
};
let i_alloc_table = build_alloc_table(&env_alloc_mod, blk.alloc_offset_bits);
let mut f_spec_invq = vec![0.0_f32; num_bins];
inverse_quantize_block(
&i_alloc_table,
&bands,
&blk.quant_idx,
&helpers,
blk.variance_preserving,
&mut state.noise_rng,
&mut state.dither_rng,
&mut f_spec_invq,
);
if !blk.variance_preserving {
inverse_heuristic_scale(&mut f_spec_invq, &bands, &f_gain_q);
}
let f_spec_res = f_spec_invq;
let f_spec_prev_input = if state.f_spec_prev.len() == num_bins {
state.f_spec_prev.clone()
} else {
vec![0.0_f32; num_bins]
};
let f_spec_pred = state.pred_state.run(
&f_spec_prev_input,
&f_env_signal[block_idx],
f_pred_gain,
f_pred_lag,
granule.n_mdct,
&bands,
granule.b_iframe,
);
let f_spec = inverse_flatten(&f_spec_res, &f_spec_pred, &f_env_signal[block_idx], &bands);
state.f_spec_prev = f_spec.clone();
let off = block_idx * n_mdct;
let copy = f_spec.len().min(n_mdct);
out[off..off + copy].copy_from_slice(&f_spec[..copy]);
}
state.env_prev = env;
out
}
pub fn synthesize_ssf_data(data: &SsfData, state: &mut SsfSynthState) -> Vec<f32> {
let mut out: Vec<f32> = Vec::new();
for g in &data.granules {
let spec = synthesize_granule(g, &[], state);
out.extend_from_slice(&spec);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn decode_envelope_handles_empty() {
let env = decode_envelope(&[]);
assert!(env.is_empty());
}
#[test]
fn decode_envelope_chains_deltas() {
let env = decode_envelope(&[-28, 16, 16, 17]);
assert_eq!(env, vec![-28, -28, -28, -27]);
}
#[test]
fn refine_envelope_low_bands_no_gain() {
let env_interp = vec![vec![0i32, 0, 4, 4]];
let gain = vec![2.0_f32];
let blocks = vec![SsfBlock {
gain_bits: 8 + 3,
..SsfBlock::default()
}]; let (alloc, sig) = refine_envelope(&env_interp, &gain, &blocks);
assert!((sig[0][0] - 1.0).abs() < 1e-5);
assert!((sig[0][1] - 1.0).abs() < 1e-5);
assert!((sig[0][2] - 8.0).abs() < 1e-3);
assert_eq!(alloc[0][0], 0);
assert_eq!(alloc[0][1], 0);
assert_eq!(alloc[0][2], 6);
}
#[test]
fn build_alloc_table_clamps_to_0_20() {
let tab = build_alloc_table(&[0, 5, 10, 15, 20], 21);
assert_eq!(tab, vec![0, 5, 10, 15, 20]);
}
#[test]
fn build_alloc_table_negative_clips_to_zero() {
let tab = build_alloc_table(&[-50, -50], 0);
assert_eq!(tab, vec![0, 0]);
}
#[test]
fn helpers_no_predictor_gives_zero_rfu() {
let h = compute_helpers(0.0, false);
assert_eq!(h.f_rfu, 0.0);
assert_eq!(h.f_adaptive_noise_gain, 1.0);
assert!((h.f_adaptive_noise_gain_var_pres - 1.0).abs() < 1e-6);
assert_eq!(
h.i_alloc_dithering_threshold,
ALLOC_DITHERING_THRESHOLD_LARGE
);
}
#[test]
fn helpers_pred_gain_in_unit_window() {
let h = compute_helpers(0.5, false);
assert!((h.f_rfu - 0.5).abs() < 1e-6);
assert!((h.f_adaptive_noise_gain - 0.5).abs() < 1e-6);
}
#[test]
fn predictor_off_returns_zero_gain_and_zero_lag_idx() {
let mut prev = 100;
let blk = SsfBlock {
predictor_presence: false,
..SsfBlock::default()
};
let (g, _lag) = decode_predictor(&blk, 0, 0, 1, &mut prev, 960);
assert_eq!(g, 0.0);
assert_eq!(prev, 0);
}
#[test]
fn predictor_with_lag_idx_resolves_to_pred_gain_quant() {
let mut prev = 0;
let blk = SsfBlock {
predictor_presence: true,
delta_flag: false,
predictor_lag_bits: 200,
pred_gain_idx: Some(0),
..SsfBlock::default()
};
let (g, _lag) = decode_predictor(&blk, 0, 0, 1, &mut prev, 960);
assert!((g - PRED_GAIN_QUANT_TAB[0]).abs() < 1e-6);
assert_eq!(prev, 200);
}
#[test]
fn c_matrix_dimensions() {
for tab_idx in 0..37 {
let c = build_c_matrix(tab_idx).unwrap_or_else(|| panic!("tab_idx={tab_idx}"));
let rt = PRED_RTS_TABLE[tab_idx] as usize;
let rf = PRED_RFS_TABLE[tab_idx] as usize;
assert_eq!(c.len(), 2 * rf + 1, "outer dim wrong for tab_idx={tab_idx}");
assert_eq!(c[0].len(), 65, "mid dim wrong for tab_idx={tab_idx}");
assert_eq!(c[0][0].len(), rt, "inner dim wrong for tab_idx={tab_idx}");
}
}
#[test]
fn c_matrix_negative_eta_mirror() {
let c = build_c_matrix(0).expect("mat0");
let rf = PRED_RFS_TABLE[0] as usize;
let rt = PRED_RTS_TABLE[0] as usize;
#[allow(clippy::needless_range_loop)]
for k in 0..rt {
let s = if k % 2 == 0 { -1.0_f32 } else { 1.0 };
for eta in 1..=32_i32 {
for nu in -(rf as i32)..=(rf as i32) {
let pos_eta = eta;
let neg_eta = -eta;
let pos_nu_idx = (nu + rf as i32) as usize;
let neg_nu_idx = (-nu + rf as i32) as usize;
let pos = c[pos_nu_idx][(pos_eta + 32) as usize][k];
let neg = c[neg_nu_idx][(neg_eta + 32) as usize][k];
let expected = s * pos;
assert!(
(neg - expected).abs() < 1e-5,
"mirror failure k={k} nu={nu} eta={eta}: got {neg} expected {expected}"
);
}
}
}
}
#[test]
fn subband_predictor_passes_zero_pred_gain_through() {
let mut s = SubbandPredictorState::new();
let bands = vec![(0usize, 11usize)];
let prev = vec![0.5_f32; 12];
let env = vec![1.0_f32; 1];
let out = s.run(&prev, &env, 0.0, 640.0, 960, &bands, true);
assert_eq!(out.len(), 12);
assert!(out.iter().all(|&v| v == 0.0));
}
#[test]
fn subband_predictor_extraction_runs_without_panic() {
let mut s = SubbandPredictorState::new();
let bands = vec![(0usize, 11usize)];
let prev = vec![0.1_f32; 12];
let env = vec![1.0_f32; 1];
s.run(&prev, &env, 0.0, 640.0, 960, &bands, true);
let out = s.run(&prev, &env, 0.5, 640.0, 960, &bands, false);
assert_eq!(out.len(), 12);
for (i, v) in out.iter().enumerate() {
assert!(v.is_finite(), "bin {i} not finite: {v}");
}
}
#[test]
fn inverse_flatten_band_envelope_gain() {
let res = vec![0.5_f32; 4];
let pred = vec![0.0_f32; 4];
let env = vec![2.0_f32];
let bands = vec![(0usize, 3usize)];
let f = inverse_flatten(&res, &pred, &env, &bands);
for &v in &f {
assert!((v - 1.0).abs() < 1e-6);
}
}
#[test]
fn synthesize_granule_long_stride_zero_blocks_runs() {
let granule = SsfGranule {
b_iframe: true,
stride_flag: StrideFlag::LongStride,
num_bands: 12,
start_block: 0,
end_block: 0,
num_blocks: 1,
n_mdct: 960,
num_bins: 140,
env_curr_band0_bits: 0,
env_startup_band0_bits: None,
env_curr: vec![-28; 12],
env_startup: None,
blocks: vec![SsfBlock::default()],
ac_bits_used: 30,
};
let mut state = SsfSynthState::new();
let pcm_spec = synthesize_granule(&granule, &[], &mut state);
assert_eq!(pcm_spec.len(), 960);
for &v in &pcm_spec {
assert!(v.is_finite());
}
}
#[test]
fn synthesize_granule_latches_env_prev() {
let env_curr_raw = vec![-28, 16, 16, 17, 16, 16, 16, 16, 16, 16, 16, 16];
let granule = SsfGranule {
b_iframe: true,
stride_flag: StrideFlag::LongStride,
num_bands: 12,
start_block: 0,
end_block: 0,
num_blocks: 1,
n_mdct: 960,
num_bins: 140,
env_curr_band0_bits: 0,
env_startup_band0_bits: None,
env_curr: env_curr_raw.clone(),
env_startup: None,
blocks: vec![SsfBlock::default()],
ac_bits_used: 30,
};
let mut state = SsfSynthState::new();
assert!(state.env_prev.is_empty());
let _ = synthesize_granule(&granule, &[], &mut state);
assert_eq!(state.env_prev, decode_envelope(&env_curr_raw));
}
#[test]
fn short_stride_p_frame_uses_state_env_prev() {
let i_env_raw = vec![-20i32, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20];
let i_granule = SsfGranule {
b_iframe: true,
stride_flag: StrideFlag::LongStride,
num_bands: 12,
start_block: 0,
end_block: 0,
num_blocks: 1,
n_mdct: 960,
num_bins: 140,
env_curr_band0_bits: 0,
env_startup_band0_bits: None,
env_curr: i_env_raw.clone(),
env_startup: None,
blocks: vec![SsfBlock::default()],
ac_bits_used: 30,
};
let mut state = SsfSynthState::new();
let _ = synthesize_granule(&i_granule, &[], &mut state);
let i_resolved = decode_envelope(&i_env_raw);
assert_eq!(state.env_prev, i_resolved);
let p_env_raw = vec![-10i32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16];
let p_resolved = decode_envelope(&p_env_raw);
assert_eq!(p_resolved[0], -10);
let expected_interp = interpolate_envelope(&p_resolved, &i_resolved, 4);
let layout = crate::ssf::SsfBinLayout::build(12, 240).expect("layout");
let p_granule = SsfGranule {
b_iframe: false,
stride_flag: StrideFlag::ShortStride,
num_bands: 12,
start_block: 0,
end_block: 4,
num_blocks: 4,
n_mdct: 240,
num_bins: layout.num_bins,
env_curr_band0_bits: 18, env_startup_band0_bits: None,
env_curr: p_env_raw,
env_startup: None,
blocks: vec![SsfBlock::default(); 4],
ac_bits_used: 30,
};
let spec = synthesize_granule(&p_granule, &[], &mut state);
assert_eq!(spec.len(), 4 * 240);
for &v in &spec {
assert!(v.is_finite());
}
assert!(
expected_interp[0][0] > -20 && expected_interp[0][0] < -10,
"interp[0][0] = {}, expected strictly between -20 and -10",
expected_interp[0][0]
);
}
#[test]
fn synthesize_ssf_data_chains_env_prev_across_granules() {
let g0_env = vec![-25i32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16];
let g1_env = vec![-15i32, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16];
let mk_g = |env: Vec<i32>, b_iframe: bool| SsfGranule {
b_iframe,
stride_flag: StrideFlag::LongStride,
num_bands: 12,
start_block: 0,
end_block: 0,
num_blocks: 1,
n_mdct: 960,
num_bins: 140,
env_curr_band0_bits: 0,
env_startup_band0_bits: None,
env_curr: env,
env_startup: None,
blocks: vec![SsfBlock::default()],
ac_bits_used: 30,
};
let data = SsfData {
granules: vec![mk_g(g0_env.clone(), true), mk_g(g1_env.clone(), false)],
};
let mut state = SsfSynthState::new();
let _ = synthesize_ssf_data(&data, &mut state);
assert_eq!(state.env_prev, decode_envelope(&g1_env));
}
#[test]
fn map_db_to_lin_zero_input() {
let r = map_db_to_lin_q10(0);
assert_eq!(r, 16 << 6);
}
#[test]
fn map_db_to_lin_out_of_range_clamps() {
let r = map_db_to_lin_q10(40 << 10); assert_eq!(r, 100i32 << 10);
let r = map_db_to_lin_q10(60 << 10); assert_eq!(r, 100i32 << 10);
}
#[test]
fn map_db_to_lin_monotone_within_table() {
let mut prev = i32::MIN;
for q10 in (0i32..(4 << 10)).step_by(64) {
let r = map_db_to_lin_q10(q10);
assert!(r >= prev, "non-monotone at q10={q10}: {r} < {prev}");
prev = r;
}
}
#[test]
fn map_lin_to_db_zero_input() {
let r = map_lin_to_db_q10(0);
assert_eq!(r, OFFSETS_LIN_TO_DB[0] << 2);
}
#[test]
fn map_lin_to_db_out_of_range_clamps() {
let r = map_lin_to_db_q10(i32::MAX / 4); assert_eq!(r, 40i32 << 10);
}
#[test]
fn heuristic_scaling_zero_envelope_yields_zero_weights() {
let env_in = vec![0i32; 12];
let bw = vec![15i32; 12];
let res = heuristic_scaling(0, &env_in, &bw, 180);
assert_eq!(res.len(), 12);
assert!(res.iter().all(|&v| v == 0), "got {res:?}");
}
#[test]
fn heuristic_scaling_clamps_to_max() {
let env_in = vec![0i32, 100, 200, 300, 400, 500];
let bw = vec![10i32; 6];
let res = heuristic_scaling(1024, &env_in, &bw, 60);
assert_eq!(res.len(), 6);
for &v in &res {
assert!((0..=15360).contains(&v), "weight out of range: {v}");
}
}
#[test]
fn apply_heuristic_scaling_short_circuits_on_empty() {
let (mod_, gq) = apply_heuristic_scaling(&[], &[], 0, 0.5);
assert!(mod_.is_empty());
assert!(gq.is_empty());
}
#[test]
fn apply_heuristic_scaling_clamps_env_alloc_mod() {
let env_alloc = vec![ENV_MAX, ENV_MIN, 0, 10, -10, 30];
let bw = vec![15u8; 6];
let (mod_, gq) = apply_heuristic_scaling(&env_alloc, &bw, 90, 0.6);
assert_eq!(mod_.len(), 6);
assert_eq!(gq.len(), 6);
for &v in &mod_ {
assert!(
(ENV_MIN..=ENV_MAX).contains(&v),
"env_alloc_mod out of range: {v}"
);
}
for &g in &gq {
assert!(g.is_finite() && g > 0.0, "f_gain_q invalid: {g}");
}
}
#[test]
fn synthesize_granule_runs_with_heuristic_scaling_branch() {
let blk = SsfBlock {
predictor_presence: true,
delta_flag: false,
predictor_lag_bits: 200,
pred_gain_idx: Some(15),
variance_preserving: false,
..SsfBlock::default()
};
let env_curr = vec![-28i32, 16, 16, 17, 16, 16, 16, 16, 16, 16, 16, 16];
let granule = SsfGranule {
b_iframe: true,
stride_flag: StrideFlag::LongStride,
num_bands: 12,
start_block: 0,
end_block: 1, num_blocks: 1,
n_mdct: 960,
num_bins: 140,
env_curr_band0_bits: 0,
env_startup_band0_bits: None,
env_curr,
env_startup: None,
blocks: vec![blk],
ac_bits_used: 30,
};
let mut state = SsfSynthState::new();
let pcm_spec = synthesize_granule(&granule, &[], &mut state);
assert_eq!(pcm_spec.len(), 960);
for &v in &pcm_spec {
assert!(v.is_finite(), "non-finite output: {v}");
}
}
#[test]
fn synthesize_granule_variance_preserving_skips_heuristic() {
let blk = SsfBlock {
predictor_presence: true,
delta_flag: false,
predictor_lag_bits: 200,
pred_gain_idx: Some(15),
variance_preserving: true,
..SsfBlock::default()
};
let env_curr = vec![-28i32, 16, 16, 17, 16, 16, 16, 16, 16, 16, 16, 16];
let granule = SsfGranule {
b_iframe: true,
stride_flag: StrideFlag::LongStride,
num_bands: 12,
start_block: 0,
end_block: 1,
num_blocks: 1,
n_mdct: 960,
num_bins: 140,
env_curr_band0_bits: 0,
env_startup_band0_bits: None,
env_curr,
env_startup: None,
blocks: vec![blk],
ac_bits_used: 30,
};
let mut state = SsfSynthState::new();
let pcm_spec = synthesize_granule(&granule, &[], &mut state);
assert_eq!(pcm_spec.len(), 960);
for &v in &pcm_spec {
assert!(v.is_finite(), "non-finite output: {v}");
}
}
}