use crate::filterbank::{imdct, long_sequence_window_n, short_window_n};
use crate::ics_info::{IcsInfo, WindowSequence, WindowShape};
use crate::ipqf::NUM_BANDS;
use crate::Error;
type Result<T> = core::result::Result<T, Error>;
pub const SSR_LONG_TRANSFORM: usize = 512;
pub const SSR_SHORT_TRANSFORM: usize = 64;
pub const BAND_LINES_LONG: usize = SSR_LONG_TRANSFORM / 2; pub const BAND_LINES_SHORT: usize = SSR_SHORT_TRANSFORM / 2; const NUM_SHORT_WINDOWS: usize = 8;
pub const BAND_SAMPLES_PER_FRAME: usize = SSR_LONG_TRANSFORM;
pub fn split_bands(spec: &[f64], seq: WindowSequence) -> Result<[Vec<f64>; NUM_BANDS]> {
if spec.len() != NUM_BANDS * BAND_LINES_LONG {
return Err(Error::FilterbankInvalid);
}
let mut bands: [Vec<f64>; NUM_BANDS] =
core::array::from_fn(|_| Vec::with_capacity(BAND_LINES_LONG));
match seq {
WindowSequence::EightShort => {
for w in 0..NUM_SHORT_WINDOWS {
let win =
&spec[w * (NUM_BANDS * BAND_LINES_SHORT)..][..NUM_BANDS * BAND_LINES_SHORT];
for (b, band) in bands.iter_mut().enumerate() {
let col = &win[b * BAND_LINES_SHORT..][..BAND_LINES_SHORT];
if b % 2 == 1 {
band.extend(col.iter().rev());
} else {
band.extend_from_slice(col);
}
}
}
}
_ => {
for (b, band) in bands.iter_mut().enumerate() {
let col = &spec[b * BAND_LINES_LONG..][..BAND_LINES_LONG];
if b % 2 == 1 {
band.extend(col.iter().rev());
} else {
band.extend_from_slice(col);
}
}
}
}
Ok(bands)
}
#[derive(Debug, Clone, Default)]
pub struct SsrSynthesis {
prev_shape: Option<WindowShape>,
}
impl SsrSynthesis {
#[must_use]
pub fn new() -> Self {
SsrSynthesis::default()
}
pub fn windowed_bands(
&mut self,
spec: &[f64],
ics_info: &IcsInfo,
) -> Result<[Vec<f64>; NUM_BANDS]> {
let left_shape = self.prev_shape.unwrap_or(ics_info.window_shape);
let right_shape = ics_info.window_shape;
let seq = ics_info.window_sequence;
let cols = split_bands(spec, seq)?;
let mut out: [Vec<f64>; NUM_BANDS] = core::array::from_fn(|_| Vec::new());
match seq {
WindowSequence::EightShort => {
for (band, col) in out.iter_mut().zip(cols.iter()) {
let mut u = Vec::with_capacity(BAND_SAMPLES_PER_FRAME);
for w in 0..NUM_SHORT_WINDOWS {
let lines = &col[w * BAND_LINES_SHORT..][..BAND_LINES_SHORT];
let x = imdct(lines, SSR_SHORT_TRANSFORM);
let win = short_window_n(SSR_SHORT_TRANSFORM, w, left_shape, right_shape);
u.extend(x.iter().zip(win.iter()).map(|(&xv, &wv)| xv * wv));
}
band.extend_from_slice(&u);
}
}
_ => {
let win = long_sequence_window_n(
SSR_LONG_TRANSFORM,
SSR_SHORT_TRANSFORM,
seq,
left_shape,
right_shape,
)?;
for (band, col) in out.iter_mut().zip(cols.iter()) {
let x = imdct(col, SSR_LONG_TRANSFORM);
band.extend(x.iter().zip(win.iter()).map(|(&xv, &wv)| xv * wv));
}
}
}
self.prev_shape = Some(right_shape);
Ok(out)
}
}
#[cfg(test)]
pub(crate) mod pqf_test_support {
use super::NUM_BANDS;
use crate::ipqf::{prototype, PROTO_LEN};
use core::f64::consts::PI;
pub(crate) fn analysis_coefs() -> [[f64; PROTO_LEN]; NUM_BANDS] {
let q = prototype();
core::array::from_fn(|i| {
core::array::from_fn(|n| {
0.25 * ((2.0 * i as f64 + 1.0) * (2.0 * n as f64 + 5.0) * PI / 16.0).cos() * q[n]
})
})
}
pub(crate) fn pqf_analysis(x: &[f64]) -> [Vec<f64>; NUM_BANDS] {
let h = analysis_coefs();
let m_len = x.len() / NUM_BANDS;
core::array::from_fn(|b| {
(0..m_len)
.map(|m| {
let mut acc = 0.0f64;
for (n, &hn) in h[b].iter().enumerate() {
let idx = 4 * m as isize + 3 - n as isize;
if idx >= 0 {
if let Some(&xv) = x.get(idx as usize) {
acc += hn * xv;
}
}
}
acc
})
.collect()
})
}
pub(crate) const PQF_CASCADE_DELAY: usize = 92;
}
#[cfg(test)]
mod tests {
use super::pqf_test_support::{pqf_analysis, PQF_CASCADE_DELAY};
use super::*;
use crate::filterbank::forward_mdct;
use crate::ipqf::Ipqf;
use core::f64::consts::PI;
#[test]
fn pqf_ipqf_cascade_is_near_perfect_reconstruction() {
let mut state = 0x1234_5678u32;
let mut rnd = || {
state = state.wrapping_mul(1664525).wrapping_add(1013904223);
(state >> 8) as f64 / (1u32 << 24) as f64 - 0.5
};
let x: Vec<f64> = (0..4000).map(|_| rnd()).collect();
let bands = pqf_analysis(&x);
let refs: [&[f64]; NUM_BANDS] = core::array::from_fn(|b| bands[b].as_slice());
let mut ipqf = Ipqf::new();
let y = ipqf.synthesize(&refs, bands[0].len());
let (mut err, mut sig) = (0.0f64, 0.0f64);
for n in 500..2500 {
let d = y[n + PQF_CASCADE_DELAY] - x[n];
err += d * d;
sig += x[n] * x[n];
}
let ratio = (err / sig).sqrt();
assert!(ratio < 1e-3, "cascade err/sig = {ratio}");
let mut err_bad = 0.0f64;
for n in 500..2500 {
let d = y[n + PQF_CASCADE_DELAY + 4] - x[n];
err_bad += d * d;
}
assert!((err_bad / sig).sqrt() > 0.1);
}
#[test]
fn tone_lands_at_its_spectral_bin() {
let win: Vec<f64> = (0..SSR_LONG_TRANSFORM)
.map(|n| (PI / SSR_LONG_TRANSFORM as f64 * (n as f64 + 0.5)).sin())
.collect();
for &k_target in &[100usize, 300, 550, 800] {
let f = (k_target as f64 + 0.5) * PI / 1024.0;
let x: Vec<f64> = (0..8192).map(|n| (f * n as f64).sin()).collect();
let bands = pqf_analysis(&x);
let mut spec = vec![0.0f64; 1024];
let mut spec_unreversed = vec![0.0f64; 1024];
for b in 0..NUM_BANDS {
let z: Vec<f64> = (0..SSR_LONG_TRANSFORM)
.map(|n| bands[b][768 + n] * win[n])
.collect();
let mut coeffs = forward_mdct(&z, SSR_LONG_TRANSFORM);
spec_unreversed[256 * b..256 * b + 256].copy_from_slice(&coeffs);
if b % 2 == 1 {
coeffs.reverse();
}
spec[256 * b..256 * b + 256].copy_from_slice(&coeffs);
}
let peak = |s: &[f64]| {
(0..s.len())
.max_by(|&a, &b| s[a].abs().partial_cmp(&s[b].abs()).unwrap())
.unwrap()
};
let got = peak(&spec);
assert!(
got.abs_diff(k_target) <= 2,
"tone k={k_target} peaked at {got}"
);
let got_unrev = peak(&spec_unreversed);
if k_target / 256 % 2 == 1 {
let band = k_target / 256;
let mirrored = 256 * band + (255 - (k_target - 256 * band));
assert!(
got_unrev.abs_diff(mirrored) <= 2,
"unreversed tone k={k_target} peaked at {got_unrev}, expected ≈{mirrored}"
);
}
}
}
#[test]
fn split_bands_long_layout() {
let spec: Vec<f64> = (0..1024).map(|i| i as f64).collect();
let bands = split_bands(&spec, WindowSequence::OnlyLong).unwrap();
for (b, band) in bands.iter().enumerate() {
assert_eq!(band.len(), 256);
if b % 2 == 0 {
assert_eq!(band[0], (256 * b) as f64);
assert_eq!(band[255], (256 * b + 255) as f64);
} else {
assert_eq!(band[0], (256 * b + 255) as f64);
assert_eq!(band[255], (256 * b) as f64);
}
}
}
#[test]
fn split_bands_short_layout() {
let spec: Vec<f64> = (0..1024).map(|i| i as f64).collect();
let bands = split_bands(&spec, WindowSequence::EightShort).unwrap();
for (b, band) in bands.iter().enumerate() {
assert_eq!(band.len(), 256);
for w in 0..8 {
let base = (128 * w + 32 * b) as f64;
if b % 2 == 0 {
assert_eq!(band[32 * w], base);
assert_eq!(band[32 * w + 31], base + 31.0);
} else {
assert_eq!(band[32 * w], base + 31.0);
assert_eq!(band[32 * w + 31], base);
}
}
}
}
#[test]
fn split_bands_rejects_bad_length() {
assert!(split_bands(&[0.0; 512], WindowSequence::OnlyLong).is_err());
}
fn test_ics_info(shape: WindowShape, seq: WindowSequence) -> IcsInfo {
let short = seq == WindowSequence::EightShort;
IcsInfo {
family: crate::swb_offset::FrameFamily::Lc1024,
ics_reserved_bit: false,
window_sequence: seq,
window_shape: shape,
max_sfb: 0,
scale_factor_grouping: if short { Some(0) } else { None },
predictor_data_present: false,
predictor_data: None,
ltp_data_present: false,
ltp_data: None,
ltp_data_present_pair: None,
ltp_data_pair: None,
num_windows: if short { 8 } else { 1 },
num_window_groups: if short { 8 } else { 1 },
window_group_length: if short { vec![1; 8] } else { vec![1] },
num_swb: 0,
}
}
#[test]
fn windowed_bands_geometry() {
let spec = vec![1.0f64; 1024];
for seq in [
WindowSequence::OnlyLong,
WindowSequence::LongStart,
WindowSequence::EightShort,
WindowSequence::LongStop,
] {
let mut synth = SsrSynthesis::new();
let info = test_ics_info(WindowShape::Sine, seq);
let u = synth.windowed_bands(&spec, &info).unwrap();
for band in &u {
assert_eq!(band.len(), BAND_SAMPLES_PER_FRAME);
assert!(band.iter().all(|v| v.is_finite()));
}
if seq == WindowSequence::LongStart {
for band in &u {
for &v in &band[400..] {
assert_eq!(v, 0.0, "LONG_START zero region");
}
}
}
}
}
}