use crate::sbr_qmf::Complex;
use crate::{Error, Result};
pub const NUM_QMF_SLOTS: usize = 32;
pub const LOOKAHEAD: usize = 6;
const PROTO_LEN: usize = 13;
const G0_Q8: [f64; PROTO_LEN] = [
0.00746082949812,
0.02270420949825,
0.04546865930473,
0.07266113929591,
0.09885108575264,
0.11793710567217,
0.125,
0.11793710567217,
0.09885108575264,
0.07266113929591,
0.04546865930473,
0.02270420949825,
0.00746082949812,
];
const G12_Q2: [f64; PROTO_LEN] = [
0.0,
0.01899487526049,
0.0,
-0.07293139167538,
0.0,
0.30596630545168,
0.5,
0.30596630545168,
0.0,
-0.07293139167538,
0.0,
0.01899487526049,
0.0,
];
const G0_Q12: [f64; PROTO_LEN] = [
0.04081179924692,
0.03812810994926,
0.05144908135699,
0.06399831151592,
0.07428313801106,
0.08100347892914,
0.08333333333333,
0.08100347892914,
0.07428313801106,
0.06399831151592,
0.05144908135699,
0.03812810994926,
0.04081179924692,
];
const G1_Q8: [f64; PROTO_LEN] = [
0.01565675600122,
0.03752716391991,
0.05417891378782,
0.08417044116767,
0.10307344158036,
0.12222452249753,
0.125,
0.12222452249753,
0.10307344158036,
0.08417044116767,
0.05417891378782,
0.03752716391991,
0.01565675600122,
];
const G234_Q4: [f64; PROTO_LEN] = [
-0.05908211155639,
-0.04871498374946,
0.0,
0.07778723915851,
0.16486303567403,
0.23279856662996,
0.25,
0.23279856662996,
0.16486303567403,
0.07778723915851,
0.0,
-0.04871498374946,
-0.05908211155639,
];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HybridConfig {
Bands1020,
Bands34,
}
impl HybridConfig {
#[must_use]
pub fn nr_bands(&self) -> usize {
match self {
HybridConfig::Bands1020 => 71,
HybridConfig::Bands34 => 91,
}
}
fn split_bands(&self) -> usize {
match self {
HybridConfig::Bands1020 => 3,
HybridConfig::Bands34 => 5,
}
}
fn q(&self, p: usize) -> usize {
match self {
HybridConfig::Bands1020 => [8, 2, 2][p],
HybridConfig::Bands34 => [12, 8, 4, 4, 4][p],
}
}
fn proto(&self, p: usize) -> &'static [f64; PROTO_LEN] {
match self {
HybridConfig::Bands1020 => [&G0_Q8, &G12_Q2, &G12_Q2][p],
HybridConfig::Bands34 => [&G0_Q12, &G1_Q8, &G234_Q4, &G234_Q4, &G234_Q4][p],
}
}
fn type_a(&self, p: usize) -> bool {
match self {
HybridConfig::Bands1020 => p == 0,
HybridConfig::Bands34 => true,
}
}
}
#[derive(Debug, Clone)]
pub struct PsHybrid {
config: HybridConfig,
history: Vec<[Complex; LOOKAHEAD]>,
}
impl PsHybrid {
#[must_use]
pub fn new(config: HybridConfig) -> Self {
PsHybrid {
config,
history: vec![[Complex::default(); LOOKAHEAD]; config.split_bands()],
}
}
#[must_use]
pub fn config(&self) -> HybridConfig {
self.config
}
pub fn reset(&mut self, config: HybridConfig) {
self.config = config;
self.history = vec![[Complex::default(); LOOKAHEAD]; config.split_bands()];
}
pub fn analyze(&mut self, x: &[[Complex; 64]]) -> Result<Vec<Vec<Complex>>> {
if x.len() < NUM_QMF_SLOTS + LOOKAHEAD {
return Err(Error::PsDataInvalid);
}
let nb = self.config.nr_bands();
let split = self.config.split_bands();
let mut out = vec![vec![Complex::default(); nb]; NUM_QMF_SLOTS];
for p in 0..split {
let mut buf = [Complex::default(); LOOKAHEAD + NUM_QMF_SLOTS + LOOKAHEAD];
buf[..LOOKAHEAD].copy_from_slice(&self.history[p]);
for (j, slot) in x.iter().enumerate().take(NUM_QMF_SLOTS + LOOKAHEAD) {
buf[LOOKAHEAD + j] = slot[p];
}
let q_cnt = self.config.q(p);
let g = self.config.proto(p);
let type_a = self.config.type_a(p);
for q in 0..q_cnt {
let mut filt = [Complex::default(); PROTO_LEN];
for (m, f) in filt.iter_mut().enumerate() {
let arg = if type_a {
2.0 * core::f64::consts::PI / q_cnt as f64
* (q as f64 + 0.5)
* (m as f64 - 6.0)
} else {
2.0 * core::f64::consts::PI * q as f64 / q_cnt as f64 * (m as f64 - 6.0)
};
let (s, c) = arg.sin_cos();
*f = if type_a {
Complex::new(g[m] * c, g[m] * s)
} else {
Complex::new(g[m] * c, 0.0)
};
}
for (n, row) in out.iter_mut().enumerate() {
let mut acc = Complex::default();
for (m, &f) in filt.iter().enumerate() {
acc += f * buf[n + 12 - m];
}
accumulate_channel(&self.config, p, q, acc, row);
}
}
for j in 0..LOOKAHEAD {
self.history[p][j] = x[NUM_QMF_SLOTS - LOOKAHEAD + j][p];
}
}
for (n, row) in out.iter_mut().enumerate() {
for k in split..64 {
row[hybrid_offset(&self.config) + k - split] = x[n][k];
}
}
Ok(out)
}
}
fn hybrid_offset(config: &HybridConfig) -> usize {
match config {
HybridConfig::Bands1020 => 10,
HybridConfig::Bands34 => 32,
}
}
fn accumulate_channel(config: &HybridConfig, p: usize, q: usize, v: Complex, row: &mut [Complex]) {
match config {
HybridConfig::Bands1020 => match p {
0 => {
let k = match q {
6 => 0,
7 => 1,
0 => 2,
1 => 3,
2 | 5 => 4,
_ => 5, };
row[k] += v;
}
1 => {
row[if q == 0 { 7 } else { 6 }] += v;
}
_ => {
row[8 + q] += v;
}
},
HybridConfig::Bands34 => {
let base = [0usize, 12, 20, 24, 28][p];
row[base + q] += v;
}
}
}
#[must_use]
pub fn synthesize(config: HybridConfig, rows: &[Vec<Complex>]) -> Vec<[Complex; 64]> {
let split = config.split_bands();
let off = hybrid_offset(&config);
let mut out = Vec::with_capacity(rows.len());
for row in rows {
let mut slot = [Complex::default(); 64];
let spans: &[(usize, usize)] = match config {
HybridConfig::Bands1020 => &[(0, 6), (6, 8), (8, 10)],
HybridConfig::Bands34 => &[(0, 12), (12, 20), (20, 24), (24, 28), (28, 32)],
};
for (p, &(lo, hi)) in spans.iter().enumerate() {
for v in &row[lo..hi] {
slot[p] += *v;
}
}
for k in split..64 {
slot[k] = row[off + k - split];
}
out.push(slot);
}
out
}
#[cfg(test)]
mod tests {
use super::*;
fn frame_from(f: impl Fn(usize, usize) -> Complex) -> Vec<[Complex; 64]> {
(0..NUM_QMF_SLOTS + LOOKAHEAD)
.map(|n| {
let mut s = [Complex::default(); 64];
for (k, cell) in s.iter_mut().enumerate() {
*cell = f(n, k);
}
s
})
.collect()
}
fn noise(seed: u64) -> impl Fn(usize, usize) -> Complex {
move |n, k| {
let mut h = seed
.wrapping_mul(6364136223846793005)
.wrapping_add((n * 64 + k) as u64);
h ^= h >> 33;
h = h.wrapping_mul(0xff51afd7ed558ccd);
h ^= h >> 33;
let re = (h & 0xFFFF) as f64 / 65535.0 - 0.5;
let im = ((h >> 16) & 0xFFFF) as f64 / 65535.0 - 0.5;
Complex::new(re, im)
}
}
#[test]
fn perfect_reconstruction_both_configs() {
for config in [HybridConfig::Bands1020, HybridConfig::Bands34] {
let mut fb = PsHybrid::new(config);
for f in 0..3 {
let sig = noise(7);
let x = frame_from(|n, k| sig(32 * f + n, k));
let hyb = fb.analyze(&x).unwrap();
assert_eq!(hyb.len(), NUM_QMF_SLOTS);
assert_eq!(hyb[0].len(), config.nr_bands());
let back = synthesize(config, &hyb);
let start = if f == 0 { LOOKAHEAD } else { 0 };
for n in start..NUM_QMF_SLOTS {
for k in 0..64 {
let d = back[n][k] - x[n][k];
assert!(
d.norm_sqr() < 1e-24,
"cfg {config:?} frame {f} slot {n} band {k}: {d:?}"
);
}
}
}
}
}
#[test]
fn band0_positive_low_frequency_lands_on_s2() {
let mut fb = PsHybrid::new(HybridConfig::Bands1020);
let omega = core::f64::consts::PI / 16.0;
let x = frame_from(|n, k| {
if k == 0 {
let (s, c) = (omega * n as f64).sin_cos();
Complex::new(c, s)
} else {
Complex::default()
}
});
let hyb = fb.analyze(&x).unwrap();
let row = &hyb[20];
let energies: Vec<f64> = (0..10).map(|k| row[k].norm_sqr()).collect();
let max_k = (0..10)
.max_by(|&a, &b| energies[a].partial_cmp(&energies[b]).unwrap())
.unwrap();
assert_eq!(max_k, 2, "energies: {energies:?}");
}
#[test]
fn band0_negative_low_frequency_lands_on_s1() {
let mut fb = PsHybrid::new(HybridConfig::Bands1020);
let omega = -core::f64::consts::PI / 16.0;
let x = frame_from(|n, k| {
if k == 0 {
let (s, c) = (omega * n as f64).sin_cos();
Complex::new(c, s)
} else {
Complex::default()
}
});
let hyb = fb.analyze(&x).unwrap();
let row = &hyb[20];
let energies: Vec<f64> = (0..10).map(|k| row[k].norm_sqr()).collect();
let max_k = (0..10)
.max_by(|&a, &b| energies[a].partial_cmp(&energies[b]).unwrap())
.unwrap();
assert_eq!(max_k, 1, "energies: {energies:?}");
}
#[test]
fn unsplit_bands_pass_through() {
let mut fb = PsHybrid::new(HybridConfig::Bands1020);
let sig = noise(11);
let x = frame_from(&sig);
let hyb = fb.analyze(&x).unwrap();
for n in 0..NUM_QMF_SLOTS {
for k in 3..64 {
let d = hyb[n][10 + k - 3] - x[n][k];
assert!(d.norm_sqr() < 1e-30);
}
}
}
#[test]
fn short_input_rejected() {
let mut fb = PsHybrid::new(HybridConfig::Bands34);
let x = vec![[Complex::default(); 64]; NUM_QMF_SLOTS];
assert!(fb.analyze(&x).is_err());
}
}