use crate::{Error, Result};
#[inline]
fn nint(x: f64) -> i32 {
if x >= 0.0 {
(x + 0.5).floor() as i32
} else {
(x - 0.5).ceil() as i32
}
}
#[inline]
fn int_trunc(x: f64) -> i32 {
x.trunc() as i32
}
fn offset_row(fs_sbr: u32) -> Option<&'static [i32; 16]> {
const OFF_16: [i32; 16] = [-8, -7, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7];
const OFF_22: [i32; 16] = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13];
const OFF_24: [i32; 16] = [-5, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16];
const OFF_32: [i32; 16] = [-6, -4, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16];
const OFF_44: [i32; 16] = [-4, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16, 20];
const OFF_64: [i32; 16] = [-2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16, 20, 24];
match fs_sbr {
16000 => Some(&OFF_16),
22050 => Some(&OFF_22),
24000 => Some(&OFF_24),
32000 => Some(&OFF_32),
44100 | 48000 | 64000 => Some(&OFF_44),
88200 | 96000 | 128000 | 176400 | 192000 => Some(&OFF_64),
_ => None,
}
}
fn start_min(fs_sbr: u32) -> i32 {
let fs = fs_sbr as f64;
let c = if fs_sbr < 32000 {
3000.0
} else if fs_sbr < 64000 {
4000.0
} else {
5000.0
};
nint(c * 128.0 / fs)
}
fn stop_min(fs_sbr: u32) -> i32 {
let fs = fs_sbr as f64;
let c = if fs_sbr < 32000 {
6000.0
} else if fs_sbr < 64000 {
8000.0
} else {
10000.0
};
nint(c * 128.0 / fs)
}
pub fn k0(fs_sbr: u32, bs_start_freq: u8) -> Result<i32> {
let row = offset_row(fs_sbr).ok_or(Error::SbrFreqBandInvalid)?;
let idx = bs_start_freq as usize;
if idx >= row.len() {
return Err(Error::SbrFreqBandInvalid);
}
Ok(start_min(fs_sbr) + row[idx])
}
pub fn k2(fs_sbr: u32, bs_stop_freq: u8, k0_val: i32) -> Result<i32> {
if bs_stop_freq > 15 {
return Err(Error::SbrFreqBandInvalid);
}
let val = match bs_stop_freq {
14 => (2 * k0_val).min(64),
15 => (3 * k0_val).min(64),
_ => {
let stop_min_v = stop_min(fs_sbr);
if stop_min_v <= 0 {
return Err(Error::SbrFreqBandInvalid);
}
let ratio = 64.0 / stop_min_v as f64;
let mut stop_dk = [0i32; 13];
for (p, slot) in stop_dk.iter_mut().enumerate() {
let hi = nint(stop_min_v as f64 * ratio.powf((p as f64 + 1.0) / 13.0));
let lo = nint(stop_min_v as f64 * ratio.powf(p as f64 / 13.0));
*slot = hi - lo;
}
stop_dk.sort_unstable();
let mut acc = stop_min_v;
for &dk in stop_dk.iter().take(bs_stop_freq as usize) {
acc += dk;
}
acc.min(64)
}
};
Ok(val)
}
pub fn master_table(
k0_val: i32,
k2_val: i32,
bs_freq_scale: u8,
bs_alter_scale: bool,
) -> Result<Vec<i32>> {
if k2_val <= k0_val || bs_freq_scale > 3 {
return Err(Error::SbrFreqBandInvalid);
}
if bs_freq_scale == 0 {
master_linear(k0_val, k2_val, bs_alter_scale)
} else {
master_warped(k0_val, k2_val, bs_freq_scale, bs_alter_scale)
}
}
fn master_linear(k0_val: i32, k2_val: i32, bs_alter_scale: bool) -> Result<Vec<i32>> {
let (dk, num_bands) = if !bs_alter_scale {
let dk = 1;
(
dk,
2 * int_trunc((k2_val - k0_val) as f64 / (dk as f64 * 2.0)),
)
} else {
let dk = 2;
(dk, 2 * nint((k2_val - k0_val) as f64 / (dk as f64 * 2.0)))
};
if num_bands <= 0 {
return Err(Error::SbrFreqBandInvalid);
}
let num_bands = num_bands as usize;
let mut v_dk = vec![dk; num_bands];
let k2_achieved = k0_val + num_bands as i32 * dk;
let mut k2_diff = k2_val - k2_achieved;
if k2_diff != 0 {
let (incr, mut k): (i32, isize) = if k2_diff < 0 {
(1, 0)
} else {
(-1, num_bands as isize - 1)
};
while k2_diff != 0 {
v_dk[k as usize] -= incr;
k += incr as isize;
k2_diff += incr;
}
}
let mut f_master = Vec::with_capacity(num_bands + 1);
f_master.push(k0_val);
for &d in &v_dk {
if d <= 0 {
return Err(Error::SbrFreqBandInvalid);
}
let next = *f_master.last().unwrap() + d;
f_master.push(next);
}
Ok(f_master)
}
fn master_warped(
k0_val: i32,
k2_val: i32,
bs_freq_scale: u8,
bs_alter_scale: bool,
) -> Result<Vec<i32>> {
let bands = [12.0, 10.0, 8.0][(bs_freq_scale - 1) as usize];
let warp = if bs_alter_scale { 1.3 } else { 1.0 };
let (two_regions, k1) = if (k2_val as f64) / (k0_val as f64) > 2.2449 {
(true, 2 * k0_val)
} else {
(false, k2_val)
};
let v_k0 = warped_region(k0_val, k1, bands, 1.0)?;
let num_bands0 = v_k0.len() - 1;
if !two_regions {
return Ok(v_k0);
}
let max_v_dk0 = max_step(&v_k0);
let v_k1 = warped_region_upper(k1, k2_val, bands, warp, max_v_dk0)?;
let num_bands1 = v_k1.len() - 1;
let mut f_master = Vec::with_capacity(num_bands0 + num_bands1 + 1);
f_master.extend_from_slice(&v_k0);
f_master.extend_from_slice(&v_k1[1..]);
Ok(f_master)
}
fn max_step(v_k: &[i32]) -> i32 {
v_k.windows(2).map(|w| w[1] - w[0]).max().unwrap_or(0)
}
fn warped_region(k_lo: i32, k_hi: i32, bands: f64, warp: f64) -> Result<Vec<i32>> {
let ratio = k_hi as f64 / k_lo as f64;
let num_bands = 2 * nint(bands * ratio.ln() / (2.0 * 2.0_f64.ln() * warp));
if num_bands <= 0 {
return Err(Error::SbrFreqBandInvalid);
}
let num_bands = num_bands as usize;
let mut v_dk = vec![0i32; num_bands];
for (k, slot) in v_dk.iter_mut().enumerate() {
let hi = nint(k_lo as f64 * ratio.powf((k as f64 + 1.0) / num_bands as f64));
let lo = nint(k_lo as f64 * ratio.powf(k as f64 / num_bands as f64));
*slot = hi - lo;
}
v_dk.sort_unstable();
let mut v_k = Vec::with_capacity(num_bands + 1);
v_k.push(k_lo);
for &d in &v_dk {
if d <= 0 {
return Err(Error::SbrFreqBandInvalid);
}
let next = *v_k.last().unwrap() + d;
v_k.push(next);
}
Ok(v_k)
}
fn warped_region_upper(
k1: i32,
k2_val: i32,
bands: f64,
warp: f64,
max_v_dk0: i32,
) -> Result<Vec<i32>> {
let ratio = k2_val as f64 / k1 as f64;
let num_bands1 = 2 * nint(bands * ratio.ln() / (2.0 * 2.0_f64.ln() * warp));
if num_bands1 <= 0 {
return Err(Error::SbrFreqBandInvalid);
}
let num_bands1 = num_bands1 as usize;
let mut v_dk1 = vec![0i32; num_bands1];
for (k, slot) in v_dk1.iter_mut().enumerate() {
let hi = nint(k1 as f64 * ratio.powf((k as f64 + 1.0) / num_bands1 as f64));
let lo = nint(k1 as f64 * ratio.powf(k as f64 / num_bands1 as f64));
*slot = hi - lo;
}
if v_dk1.iter().copied().min().unwrap_or(0) < max_v_dk0 {
v_dk1.sort_unstable();
let mut change = max_v_dk0 - v_dk1[0];
let half = int_trunc((v_dk1[num_bands1 - 1] - v_dk1[0]) as f64 / 2.0);
if change > half {
change = half;
}
v_dk1[0] += change;
v_dk1[num_bands1 - 1] -= change;
}
v_dk1.sort_unstable();
let mut v_k1 = Vec::with_capacity(num_bands1 + 1);
v_k1.push(k1);
for &d in &v_dk1 {
if d <= 0 {
return Err(Error::SbrFreqBandInvalid);
}
let next = *v_k1.last().unwrap() + d;
v_k1.push(next);
}
Ok(v_k1)
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HiLoTables {
pub f_table_high: Vec<i32>,
pub f_table_low: Vec<i32>,
pub f_table_noise: Vec<i32>,
pub m: i32,
pub k_x: i32,
}
impl HiLoTables {
#[inline]
pub fn n_high(&self) -> usize {
self.f_table_high.len() - 1
}
#[inline]
pub fn n_low(&self) -> usize {
self.f_table_low.len() - 1
}
#[inline]
pub fn n_q(&self) -> usize {
self.f_table_noise.len() - 1
}
pub fn derive(f_master: &[i32], bs_xover_band: u8, bs_noise_bands: u8) -> Result<Self> {
if f_master.len() < 2 || bs_noise_bands > 3 {
return Err(Error::SbrFreqBandInvalid);
}
let n_master = f_master.len() - 1;
let xover = bs_xover_band as usize;
if xover >= n_master {
return Err(Error::SbrFreqBandInvalid);
}
let n_high = n_master - xover;
let f_table_high: Vec<i32> = f_master[xover..=n_master].to_vec();
debug_assert_eq!(f_table_high.len(), n_high + 1);
let k_x = f_table_high[0];
let m = f_table_high[n_high] - k_x;
let half = n_high / 2;
let n_low = half + (n_high - 2 * half);
let parity = (1 - if n_high % 2 == 0 { 1 } else { -1 }) / 2; let mut f_table_low = Vec::with_capacity(n_low + 1);
for k in 0..=n_low {
let i_k = if k == 0 {
0
} else {
(2 * k as isize - parity as isize) as usize
};
f_table_low.push(*f_table_high.get(i_k).ok_or(Error::SbrFreqBandInvalid)?);
}
let k2_range = f_table_low[n_low];
let n_q = if bs_noise_bands == 0 {
1usize
} else {
let val = nint(bs_noise_bands as f64 * ((k2_range as f64 / k_x as f64).log2()));
val.max(1) as usize
};
let mut f_table_noise = Vec::with_capacity(n_q + 1);
let mut i_prev: usize = 0;
f_table_noise.push(f_table_low[0]);
for k in 1..=n_q {
let denom = (n_q + 1 - k) as f64;
let step = int_trunc((n_low - i_prev) as f64 / denom);
i_prev += step as usize;
f_table_noise.push(*f_table_low.get(i_prev).ok_or(Error::SbrFreqBandInvalid)?);
}
Ok(HiLoTables {
f_table_high,
f_table_low,
f_table_noise,
m,
k_x,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn nint_rounds_half_away_from_zero() {
assert_eq!(nint(2.5), 3);
assert_eq!(nint(2.4), 2);
assert_eq!(nint(2.6), 3);
assert_eq!(nint(0.5), 1);
assert_eq!(nint(3.0), 3);
}
#[test]
fn start_stop_min_44100() {
assert_eq!(start_min(88200), 7);
assert_eq!(stop_min(88200), 15);
}
#[test]
fn start_min_band_thresholds() {
assert_eq!(start_min(24000), 16);
assert_eq!(start_min(44100), 12);
}
#[test]
fn k0_24khz_start_freq_5() {
assert_eq!(k0(24000, 5).unwrap(), 17);
assert_eq!(k0(24000, 0).unwrap(), 11);
}
#[test]
fn k0_rejects_bad_inputs() {
assert_eq!(k0(24000, 16), Err(Error::SbrFreqBandInvalid));
assert_eq!(k0(11025, 0), Err(Error::SbrFreqBandInvalid));
}
#[test]
fn k2_shortcuts() {
assert_eq!(k2(88200, 14, 10).unwrap(), 20);
assert_eq!(k2(88200, 14, 40).unwrap(), 64); assert_eq!(k2(88200, 15, 10).unwrap(), 30);
assert_eq!(k2(88200, 15, 30).unwrap(), 64); }
#[test]
fn k2_accumulation_bs_stop_freq_0() {
assert_eq!(k2(88200, 0, 7).unwrap(), 15);
}
#[test]
fn k2_accumulation_is_monotone() {
let mut prev = k2(88200, 0, 7).unwrap();
for bsf in 1..14 {
let cur = k2(88200, bsf, 7).unwrap();
assert!(cur >= prev, "k2 dropped at bs_stop_freq={bsf}");
assert!(cur <= 64);
prev = cur;
}
}
#[test]
fn master_linear_simple() {
let fm = master_table(5, 13, 0, false).unwrap();
assert_eq!(fm, vec![5, 6, 7, 8, 9, 10, 11, 12, 13]);
}
#[test]
fn master_linear_with_remainder() {
let fm = master_table(5, 12, 0, false).unwrap();
assert_eq!(*fm.first().unwrap(), 5);
assert_eq!(*fm.last().unwrap(), 12);
assert_eq!(fm.len(), 7); assert!(fm.windows(2).all(|w| w[1] > w[0]));
}
#[test]
fn master_linear_alter_scale_dk2() {
let fm = master_table(4, 16, 0, true).unwrap();
assert_eq!(fm, vec![4, 6, 8, 10, 12, 14, 16]);
}
#[test]
fn master_rejects_k2_le_k0() {
assert_eq!(
master_table(20, 20, 0, false),
Err(Error::SbrFreqBandInvalid)
);
assert_eq!(
master_table(20, 10, 1, false),
Err(Error::SbrFreqBandInvalid)
);
}
#[test]
fn master_warped_single_region_monotone() {
let fm = master_table(14, 28, 1, false).unwrap();
assert_eq!(*fm.first().unwrap(), 14);
assert_eq!(*fm.last().unwrap(), 28);
assert!(fm.windows(2).all(|w| w[1] > w[0]));
}
#[test]
fn master_warped_two_region_monotone() {
let fm = master_table(12, 32, 2, false).unwrap();
assert_eq!(*fm.first().unwrap(), 12);
assert_eq!(*fm.last().unwrap(), 32);
assert!(fm.windows(2).all(|w| w[1] > w[0]));
assert!(fm.contains(&24));
}
#[test]
fn derive_high_low_noise_geometry() {
let fm = master_table(5, 13, 0, false).unwrap();
let t = HiLoTables::derive(&fm, 2, 2).unwrap();
assert_eq!(t.n_high(), 6);
assert_eq!(t.f_table_high, vec![7, 8, 9, 10, 11, 12, 13]);
assert_eq!(t.k_x, 7);
assert_eq!(t.m, 6);
assert_eq!(t.n_low(), 3);
assert_eq!(t.f_table_low, vec![7, 9, 11, 13]);
assert_eq!(t.f_table_noise[0], 7);
assert_eq!(*t.f_table_noise.last().unwrap(), 13);
assert!(t.f_table_noise.windows(2).all(|w| w[1] > w[0]));
}
#[test]
fn derive_odd_nhigh_parity() {
let fm = master_table(5, 12, 0, false).unwrap();
let t = HiLoTables::derive(&fm, 1, 1).unwrap();
assert_eq!(t.n_high(), 5);
assert_eq!(t.n_low(), 3);
let h = &t.f_table_high;
assert_eq!(t.f_table_low, vec![h[0], h[1], h[3], h[5]]);
}
#[test]
fn derive_noise_bands_zero_single_band() {
let fm = master_table(5, 13, 0, false).unwrap();
let t = HiLoTables::derive(&fm, 2, 0).unwrap();
assert_eq!(t.n_q(), 1);
assert_eq!(t.f_table_noise.len(), 2);
assert_eq!(t.f_table_noise[0], t.f_table_low[0]);
assert_eq!(
*t.f_table_noise.last().unwrap(),
*t.f_table_low.last().unwrap()
);
}
#[test]
fn derive_rejects_xover_ge_nmaster() {
let fm = master_table(5, 13, 0, false).unwrap(); assert_eq!(
HiLoTables::derive(&fm, 8, 1),
Err(Error::SbrFreqBandInvalid)
);
assert_eq!(
HiLoTables::derive(&fm, 9, 1),
Err(Error::SbrFreqBandInvalid)
);
}
#[test]
fn end_to_end_44100_typical() {
let k0v = k0(88200, 5).unwrap();
let k2v = k2(88200, 5, k0v).unwrap();
assert!(k2v > k0v);
let fm = master_table(k0v, k2v, 0, false).unwrap();
let t = HiLoTables::derive(&fm, 1, 2).unwrap();
assert_eq!(t.k_x, fm[1]);
assert_eq!(t.m, fm[fm.len() - 1] - fm[1]);
assert!(t.k_x <= 32);
assert!(t.k_x + t.m <= 64);
assert!(t.f_table_high.windows(2).all(|w| w[1] > w[0]));
assert!(t.f_table_low.windows(2).all(|w| w[1] > w[0]));
assert!(t.f_table_noise.windows(2).all(|w| w[1] > w[0]));
}
}