use crate::ics_info::{IcsInfo, WindowSequence, WindowShape};
use crate::swb_offset::{FrameFamily, LONG_WINDOW_LEN, SHORT_WINDOW_LEN};
use crate::Error;
const LONG_TRANSFORM_LEN: usize = 2 * LONG_WINDOW_LEN as usize; const SHORT_TRANSFORM_LEN: usize = 2 * SHORT_WINDOW_LEN as usize; const NUM_SHORT_WINDOWS: usize = 8;
const N_L: usize = LONG_TRANSFORM_LEN; const N_S: usize = SHORT_TRANSFORM_LEN;
type Result<T> = core::result::Result<T, Error>;
pub(crate) fn imdct(spec: &[f64], n_transform: usize) -> Vec<f64> {
let half = n_transform / 2;
debug_assert_eq!(spec.len(), half);
let n0 = (half + 1) as f64 / 2.0;
let scale = 2.0 / n_transform as f64;
let phase_step = 2.0 * core::f64::consts::PI / n_transform as f64;
let mut out = vec![0.0f64; n_transform];
for (n, slot) in out.iter_mut().enumerate() {
let np = n as f64 + n0;
let mut acc = 0.0f64;
for (k, &c) in spec.iter().enumerate() {
acc += c * (phase_step * np * (k as f64 + 0.5)).cos();
}
*slot = scale * acc;
}
out
}
pub(crate) fn forward_mdct(time: &[f64], n_transform: usize) -> Vec<f64> {
let half = n_transform / 2;
debug_assert_eq!(time.len(), n_transform);
let n0 = (half + 1) as f64 / 2.0;
let step = 2.0 * core::f64::consts::PI / n_transform as f64;
(0..half)
.map(|k| {
2.0 * time
.iter()
.enumerate()
.map(|(n, &t)| t * (step * (n as f64 + n0) * (k as f64 + 0.5)).cos())
.sum::<f64>()
})
.collect()
}
pub(crate) fn long_only_window_family(
family: FrameFamily,
left_shape: WindowShape,
right_shape: WindowShape,
) -> Vec<f64> {
let n_l = family.long_transform_len();
let halves = window_halves_style(
n_l,
left_shape,
right_shape,
WindowStyle::for_family(family),
);
let half_l = n_l / 2;
let mut w = vec![0.0f64; n_l];
w[..half_l].copy_from_slice(&halves.left);
for (m, &rv) in halves.right.iter().enumerate() {
w[half_l + m] = rv;
}
w
}
pub(crate) fn long_sequence_window(
sequence: WindowSequence,
left_shape: WindowShape,
right_shape: WindowShape,
) -> Result<Vec<f64>> {
long_sequence_window_n(N_L, N_S, sequence, left_shape, right_shape)
}
pub(crate) fn long_sequence_window_n(
n_l: usize,
n_s: usize,
sequence: WindowSequence,
left_shape: WindowShape,
right_shape: WindowShape,
) -> Result<Vec<f64>> {
let kind = match sequence {
WindowSequence::OnlyLong => LongKind::OnlyLong,
WindowSequence::LongStart => LongKind::Start,
WindowSequence::LongStop => LongKind::Stop,
WindowSequence::EightShort => return Err(Error::FilterbankInvalid),
};
Ok(build_long_window_n(n_l, n_s, left_shape, right_shape, kind))
}
pub(crate) fn short_window_j(
j: usize,
left_shape: WindowShape,
right_shape: WindowShape,
) -> Vec<f64> {
short_window_n(N_S, j, left_shape, right_shape)
}
pub(crate) fn short_window_n(
n_s: usize,
j: usize,
left_shape: WindowShape,
right_shape: WindowShape,
) -> Vec<f64> {
let this_left = if j == 0 { left_shape } else { right_shape };
let halves = window_halves(n_s, this_left, right_shape);
let mut w = vec![0.0f64; n_s];
w[..n_s / 2].copy_from_slice(&halves.left);
for (m, &rv) in halves.right.iter().enumerate() {
w[n_s / 2 + m] = rv;
}
w
}
pub(crate) const SHORT_SEQ_START: usize = (N_L - N_S) / 4;
pub(crate) const SHORT_SEQ_HOP: usize = N_S / 2;
fn bessel_i0(x: f64) -> f64 {
let half_x = x / 2.0;
let mut term = 1.0f64; let mut sum = 1.0f64;
let mut k = 1.0f64;
loop {
term *= (half_x / k) * (half_x / k);
sum += term;
if term <= sum * 1e-18 {
break;
}
k += 1.0;
if k > 256.0 {
break;
}
}
sum
}
fn kbd_kernel(half: usize, alpha: f64) -> Vec<f64> {
let quarter = half as f64 / 2.0; let denom = bessel_i0(core::f64::consts::PI * alpha);
(0..=half)
.map(|n| {
let t = (n as f64 - quarter) / quarter;
let radicand = (1.0 - t * t).max(0.0);
bessel_i0(core::f64::consts::PI * alpha * radicand.sqrt()) / denom
})
.collect()
}
fn kbd_left(half: usize, alpha: f64) -> Vec<f64> {
let kernel = kbd_kernel(half, alpha);
let total: f64 = kernel.iter().sum();
let mut running = 0.0f64;
let mut out = Vec::with_capacity(half);
for &w in kernel.iter().take(half) {
running += w;
out.push((running / total).sqrt());
}
out
}
fn sine_left(half: usize) -> Vec<f64> {
let n_transform = (2 * half) as f64;
(0..half)
.map(|n| (core::f64::consts::PI / n_transform * (n as f64 + 0.5)).sin())
.collect()
}
struct WindowHalves {
left: Vec<f64>,
right: Vec<f64>,
}
fn low_overlap_left(half: usize) -> Vec<f64> {
let n = 2 * half; let rise_start = 3 * n / 16;
let rise_end = 5 * n / 16;
let quarter = n as f64 / 4.0;
(0..half)
.map(|i| {
if i < rise_start {
0.0
} else if i < rise_end {
(core::f64::consts::PI * (i as f64 - rise_start as f64 + 0.5) / quarter).sin()
} else {
1.0
}
})
.collect()
}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
pub(crate) enum WindowStyle {
Standard,
LowDelay,
}
impl WindowStyle {
pub(crate) fn for_family(family: FrameFamily) -> Self {
if family.is_ld() {
WindowStyle::LowDelay
} else {
WindowStyle::Standard
}
}
}
fn half_window_style(n_transform: usize, shape: WindowShape, style: WindowStyle) -> Vec<f64> {
let half = n_transform / 2;
match (shape, style) {
(WindowShape::Sine, _) => sine_left(half),
(WindowShape::Kbd, WindowStyle::LowDelay) => low_overlap_left(half),
(WindowShape::Kbd, WindowStyle::Standard) => {
let alpha = match n_transform {
2048 | 1920 | 512 => 4.0,
_ => 6.0,
};
kbd_left(half, alpha)
}
}
}
fn window_halves(
n_transform: usize,
left_shape: WindowShape,
right_shape: WindowShape,
) -> WindowHalves {
window_halves_style(n_transform, left_shape, right_shape, WindowStyle::Standard)
}
fn window_halves_style(
n_transform: usize,
left_shape: WindowShape,
right_shape: WindowShape,
style: WindowStyle,
) -> WindowHalves {
let left = half_window_style(n_transform, left_shape, style);
let mut right = half_window_style(n_transform, right_shape, style);
right.reverse();
WindowHalves { left, right }
}
#[derive(Clone, Debug)]
pub struct Filterbank {
family: FrameFamily,
overlap: Vec<f64>,
prev_shape: Option<WindowShape>,
}
impl Default for Filterbank {
fn default() -> Self {
Self::new()
}
}
impl Filterbank {
pub fn new() -> Self {
Self::new_family(FrameFamily::Lc1024)
}
pub fn new_family(family: FrameFamily) -> Self {
Filterbank {
family,
overlap: vec![0.0f64; family.frame_len()],
prev_shape: None,
}
}
pub fn family(&self) -> FrameFamily {
self.family
}
pub fn aliased_tail(&self) -> &[f64] {
&self.overlap
}
pub fn prev_shape(&self) -> Option<WindowShape> {
self.prev_shape
}
pub fn synthesize(&mut self, spec: &[f64], ics_info: &IcsInfo) -> Result<Vec<f64>> {
if ics_info.family != self.family {
return Err(Error::FilterbankInvalid);
}
let z = self.windowed_signal(spec, ics_info)?;
debug_assert_eq!(z.len(), self.family.long_transform_len());
let half = self.family.frame_len();
let out: Vec<f64> = z[..half]
.iter()
.zip(self.overlap.iter())
.map(|(&zn, &on)| zn + on)
.collect();
self.overlap.clear();
self.overlap.extend_from_slice(&z[half..]);
self.prev_shape = Some(ics_info.window_shape);
Ok(out)
}
fn windowed_signal(&self, spec: &[f64], ics_info: &IcsInfo) -> Result<Vec<f64>> {
let left_shape = self.prev_shape.unwrap_or(ics_info.window_shape);
let right_shape = ics_info.window_shape;
match ics_info.window_sequence {
WindowSequence::OnlyLong => {
self.long_windowed(spec, left_shape, right_shape, LongKind::OnlyLong)
}
WindowSequence::LongStart => {
self.long_windowed(spec, left_shape, right_shape, LongKind::Start)
}
WindowSequence::LongStop => {
self.long_windowed(spec, left_shape, right_shape, LongKind::Stop)
}
WindowSequence::EightShort => self.short_windowed(spec, left_shape, right_shape),
}
}
fn long_windowed(
&self,
spec: &[f64],
left_shape: WindowShape,
right_shape: WindowShape,
kind: LongKind,
) -> Result<Vec<f64>> {
if spec.len() != self.family.frame_len() {
return Err(Error::FilterbankInvalid);
}
let x = imdct(spec, self.family.long_transform_len());
let w = self.long_window(left_shape, right_shape, kind)?;
let z: Vec<f64> = x.iter().zip(w.iter()).map(|(&xv, &wv)| xv * wv).collect();
Ok(z)
}
fn long_window(
&self,
left_shape: WindowShape,
right_shape: WindowShape,
kind: LongKind,
) -> Result<Vec<f64>> {
let n_l = self.family.long_transform_len();
match self.family.short_transform_len() {
Some(n_s) => Ok(build_long_window_style(
n_l,
n_s,
left_shape,
right_shape,
kind,
WindowStyle::for_family(self.family),
)),
None => match kind {
LongKind::OnlyLong => {
let halves =
window_halves_style(n_l, left_shape, right_shape, WindowStyle::LowDelay);
let half_l = n_l / 2;
let mut w = vec![0.0f64; n_l];
w[..half_l].copy_from_slice(&halves.left);
for (m, &rv) in halves.right.iter().enumerate() {
w[half_l + m] = rv;
}
Ok(w)
}
_ => Err(Error::LdShortWindow),
},
}
}
}
fn build_long_window_n(
n_l: usize,
n_s: usize,
left_shape: WindowShape,
right_shape: WindowShape,
kind: LongKind,
) -> Vec<f64> {
build_long_window_style(
n_l,
n_s,
left_shape,
right_shape,
kind,
WindowStyle::Standard,
)
}
fn build_long_window_style(
n_l: usize,
n_s: usize,
left_shape: WindowShape,
right_shape: WindowShape,
kind: LongKind,
style: WindowStyle,
) -> Vec<f64> {
let long = window_halves_style(n_l, left_shape, right_shape, style);
let short = window_halves_style(n_s, left_shape, right_shape, style);
let half_l = n_l / 2;
let mut w = vec![0.0f64; n_l];
match kind {
LongKind::OnlyLong | LongKind::Start => {
w[..half_l].copy_from_slice(&long.left);
}
LongKind::Stop => {
let a = (n_l - n_s) / 4;
for (m, &sv) in short.left.iter().enumerate() {
w[a + m] = sv;
}
for slot in w.iter_mut().take(half_l).skip(a + n_s / 2) {
*slot = 1.0;
}
}
}
match kind {
LongKind::OnlyLong => {
for (m, &rv) in long.right.iter().enumerate() {
w[half_l + m] = rv;
}
}
LongKind::Start => {
let b = (3 * n_l - n_s) / 4;
for slot in w.iter_mut().take(b).skip(half_l) {
*slot = 1.0;
}
for (m, &rv) in short.right.iter().enumerate() {
w[b + m] = rv;
}
}
LongKind::Stop => {
for (m, &rv) in long.right.iter().enumerate() {
w[half_l + m] = rv;
}
}
}
w
}
impl Filterbank {
fn short_windowed(
&self,
spec: &[f64],
left_shape: WindowShape,
right_shape: WindowShape,
) -> Result<Vec<f64>> {
let n_s = self
.family
.short_transform_len()
.ok_or(Error::LdShortWindow)?;
let n_l = self.family.long_transform_len();
let short_len = n_s / 2; if spec.len() != NUM_SHORT_WINDOWS * short_len {
return Err(Error::FilterbankInvalid);
}
let mut windowed: Vec<Vec<f64>> = Vec::with_capacity(NUM_SHORT_WINDOWS);
for j in 0..NUM_SHORT_WINDOWS {
let coeffs = &spec[j * short_len..(j + 1) * short_len];
let x = imdct(coeffs, n_s);
let this_left = if j == 0 { left_shape } else { right_shape };
let halves = window_halves(n_s, this_left, right_shape);
let mut z = vec![0.0f64; n_s];
for n in 0..n_s / 2 {
z[n] = x[n] * halves.left[n];
}
for n in n_s / 2..n_s {
z[n] = x[n] * halves.right[n - n_s / 2];
}
windowed.push(z);
}
let mut z = vec![0.0f64; n_l];
let start = (n_l - n_s) / 4; let hop = n_s / 2; for (j, win) in windowed.iter().enumerate() {
let base = start + j * hop;
for (n, &v) in win.iter().enumerate() {
z[base + n] += v;
}
}
Ok(z)
}
}
#[derive(Clone, Copy)]
enum LongKind {
OnlyLong,
Start,
Stop,
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ics_info::IcsInfo;
fn long_info(shape: WindowShape, seq: WindowSequence) -> IcsInfo {
IcsInfo {
family: crate::swb_offset::FrameFamily::Lc1024,
ics_reserved_bit: false,
window_sequence: seq,
window_shape: shape,
max_sfb: 49,
scale_factor_grouping: 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: 1,
num_window_groups: 1,
window_group_length: vec![1],
num_swb: 49,
}
}
fn short_info(shape: WindowShape) -> IcsInfo {
IcsInfo {
family: crate::swb_offset::FrameFamily::Lc1024,
ics_reserved_bit: false,
window_sequence: WindowSequence::EightShort,
window_shape: shape,
max_sfb: 14,
scale_factor_grouping: Some(0),
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: 8,
num_window_groups: 8,
window_group_length: vec![1; 8],
num_swb: 14,
}
}
#[test]
fn sine_window_endpoints() {
let left = sine_left(1024);
assert_eq!(left.len(), 1024);
let expect0 = (core::f64::consts::PI * 0.5 / 2048.0).sin();
assert!((left[0] - expect0).abs() < 1e-15);
assert!(left[1023] > 0.9999 && left[1023] <= 1.0);
for w in 1..1024 {
assert!(left[w] > left[w - 1]);
}
}
#[test]
fn sine_window_unit_power_overlap() {
let half = sine_left(1024);
for n in 0..1024 {
let wl = half[n];
let wr = half[1023 - n]; let s = wl * wl + wr * wr;
assert!((s - 1.0).abs() < 1e-12, "n={n} sum={s}");
}
}
#[test]
fn kbd_window_unit_power_overlap() {
let left = kbd_left(1024, 4.0);
assert_eq!(left.len(), 1024);
for n in 0..1024 {
let wl = left[n];
let wr = left[1023 - n];
let s = wl * wl + wr * wr;
assert!((s - 1.0).abs() < 1e-12, "n={n} sum={s}");
}
for n in 1..1024 {
assert!(left[n] >= left[n - 1]);
}
}
#[test]
fn bessel_i0_known_values() {
assert!((bessel_i0(0.0) - 1.0).abs() < 1e-15);
assert!((bessel_i0(1.0) - 1.266_065_877_752_008_4).abs() < 1e-12);
assert!((bessel_i0(2.0) - 2.279_585_302_336_067_3).abs() < 1e-12);
}
#[test]
fn imdct_dc_coefficient() {
let n = 8usize;
let spec = [1.0, 0.0, 0.0, 0.0];
let x = imdct(&spec, n);
let scale = 2.0 / 8.0;
let n0 = 2.5;
for (idx, &xv) in x.iter().enumerate() {
let expect =
scale * (2.0 * core::f64::consts::PI / 8.0 * (idx as f64 + n0) * 0.5).cos();
assert!((xv - expect).abs() < 1e-15, "n={idx}");
}
}
use super::forward_mdct;
fn long_sine_window() -> Vec<f64> {
let left = sine_left(1024);
let mut w = vec![0.0; LONG_TRANSFORM_LEN];
w[..1024].copy_from_slice(&left);
for m in 0..1024 {
w[1024 + m] = left[1023 - m];
}
w
}
#[test]
fn tdac_perfect_reconstruction_sine_long() {
let n = LONG_TRANSFORM_LEN; let hop = n / 2; let win = long_sine_window();
let total = 5 * hop;
let input: Vec<f64> = (0..total)
.map(|i| (0.013 * i as f64).sin() + 0.5 * (0.07 * i as f64).cos())
.collect();
let info = long_info(WindowShape::Sine, WindowSequence::OnlyLong);
let mut fb = Filterbank::new();
let mut outputs = Vec::new();
for f in 0..4 {
let base = f * hop;
let frame: Vec<f64> = (0..n)
.map(|m| {
let idx = base + m;
if idx < total {
input[idx] * win[m]
} else {
0.0
}
})
.collect();
let spec = forward_mdct(&frame, n);
outputs.push(fb.synthesize(&spec, &info).unwrap());
}
for (f, out) in outputs.iter().enumerate().take(3).skip(1) {
let base = f * hop;
for k in 0..hop {
let recon = out[k];
let expect = input[base + k];
assert!(
(recon - expect).abs() < 1e-9,
"frame={f} k={k} recon={recon} expect={expect}"
);
}
}
}
fn tdac_long_family(family: crate::swb_offset::FrameFamily, shape: WindowShape) {
let n = family.long_transform_len();
let hop = n / 2;
let style = WindowStyle::for_family(family);
let win = {
let left = half_window_style(n, shape, style);
let mut w = vec![0.0; n];
w[..hop].copy_from_slice(&left);
for m in 0..hop {
w[hop + m] = left[hop - 1 - m];
}
w
};
let total = 5 * hop;
let input: Vec<f64> = (0..total)
.map(|i| (0.017 * i as f64).sin() + 0.4 * (0.043 * i as f64).cos())
.collect();
let mut info = long_info(shape, WindowSequence::OnlyLong);
info.family = family;
info.num_swb = 40; let mut fb = Filterbank::new_family(family);
let mut outputs = Vec::new();
for f in 0..4 {
let base = f * hop;
let frame: Vec<f64> = (0..n)
.map(|m| {
let idx = base + m;
if idx < total {
input[idx] * win[m]
} else {
0.0
}
})
.collect();
let spec = forward_mdct(&frame, n);
let out = fb.synthesize(&spec, &info).unwrap();
assert_eq!(out.len(), family.frame_len());
outputs.push(out);
}
for (f, out) in outputs.iter().enumerate().take(3).skip(1) {
let base = f * hop;
for k in 0..hop {
assert!(
(out[k] - input[base + k]).abs() < 1e-9,
"{:?} {:?} frame={f} k={k}",
family,
shape
);
}
}
}
#[test]
fn tdac_lc960_sine_and_kbd() {
tdac_long_family(crate::swb_offset::FrameFamily::Lc960, WindowShape::Sine);
tdac_long_family(crate::swb_offset::FrameFamily::Lc960, WindowShape::Kbd);
}
#[test]
fn tdac_ld512_sine_and_low_overlap() {
tdac_long_family(crate::swb_offset::FrameFamily::Ld512, WindowShape::Sine);
tdac_long_family(crate::swb_offset::FrameFamily::Ld512, WindowShape::Kbd);
}
#[test]
fn tdac_ld480_sine_and_low_overlap() {
tdac_long_family(crate::swb_offset::FrameFamily::Ld480, WindowShape::Sine);
tdac_long_family(crate::swb_offset::FrameFamily::Ld480, WindowShape::Kbd);
}
#[test]
fn low_overlap_window_regions_and_pr() {
for n in [1024usize, 960] {
let half = n / 2;
let left = low_overlap_left(half);
assert_eq!(left.len(), half);
for (i, &v) in left.iter().enumerate().take(3 * n / 16) {
assert_eq!(v, 0.0, "N={n} i={i}");
}
for (i, &v) in left.iter().enumerate().take(half).skip(5 * n / 16) {
assert_eq!(v, 1.0, "N={n} i={i}");
}
for i in 3 * n / 16 + 1..5 * n / 16 {
assert!(left[i] > left[i - 1], "N={n} i={i}");
}
for i in 0..half {
let s = left[i] * left[i] + left[half - 1 - i] * left[half - 1 - i];
assert!((s - 1.0).abs() < 1e-12, "N={n} i={i} s={s}");
}
}
}
#[test]
fn ld_filterbank_rejects_non_only_long() {
use crate::swb_offset::FrameFamily;
let mut fb = Filterbank::new_family(FrameFamily::Ld512);
let mut info = long_info(WindowShape::Sine, WindowSequence::LongStart);
info.family = FrameFamily::Ld512;
let spec = vec![0.0; 512];
assert!(matches!(
fb.synthesize(&spec, &info),
Err(Error::LdShortWindow)
));
}
#[test]
fn family_mismatch_rejected() {
use crate::swb_offset::FrameFamily;
let mut fb = Filterbank::new_family(FrameFamily::Lc1024);
let mut info = long_info(WindowShape::Sine, WindowSequence::OnlyLong);
info.family = FrameFamily::Lc960;
let spec = vec![0.0; 960];
assert!(matches!(
fb.synthesize(&spec, &info),
Err(Error::FilterbankInvalid)
));
}
#[test]
fn eight_short_lc960_tdac() {
use crate::swb_offset::FrameFamily;
let family = FrameFamily::Lc960;
let n_s = 240usize;
let hop = 120usize;
let start = 420usize;
let win = {
let left = half_window_style(n_s, WindowShape::Sine, WindowStyle::Standard);
let mut w = vec![0.0; n_s];
w[..hop].copy_from_slice(&left);
for m in 0..hop {
w[hop + m] = left[hop - 1 - m];
}
w
};
let input: Vec<f64> = (0..1920).map(|i| (0.05 * i as f64).sin() * 0.7).collect();
let mut spec = Vec::with_capacity(8 * hop);
for j in 0..8 {
let base = start + j * hop;
let frame: Vec<f64> = (0..n_s).map(|m| input[base + m] * win[m]).collect();
spec.extend(forward_mdct(&frame, n_s));
}
let mut info = short_info(WindowShape::Sine);
info.family = family;
info.num_swb = 14;
let mut fb = Filterbank::new_family(family);
let out = fb.synthesize(&spec, &info).unwrap();
assert_eq!(out.len(), 960);
for k in 540..960 {
assert!(
(out[k] - input[k]).abs() < 1e-9,
"k={k} out={} in={}",
out[k],
input[k]
);
}
}
#[test]
fn tdac_perfect_reconstruction_kbd_long() {
let n = LONG_TRANSFORM_LEN;
let hop = n / 2;
let win = {
let left = kbd_left(1024, 4.0);
let mut w = vec![0.0; n];
w[..1024].copy_from_slice(&left);
for m in 0..1024 {
w[1024 + m] = left[1023 - m];
}
w
};
let total = 5 * hop;
let input: Vec<f64> = (0..total)
.map(|i| 0.3 * (0.02 * i as f64).cos() - 0.6 * (0.05 * i as f64).sin())
.collect();
let info = long_info(WindowShape::Kbd, WindowSequence::OnlyLong);
let mut fb = Filterbank::new();
let mut outputs = Vec::new();
for f in 0..4 {
let base = f * hop;
let frame: Vec<f64> = (0..n)
.map(|m| {
let idx = base + m;
if idx < total {
input[idx] * win[m]
} else {
0.0
}
})
.collect();
let spec = forward_mdct(&frame, n);
outputs.push(fb.synthesize(&spec, &info).unwrap());
}
for (f, out) in outputs.iter().enumerate().take(3).skip(1) {
let base = f * hop;
for k in 0..hop {
assert!((out[k] - input[base + k]).abs() < 1e-9, "frame={f} k={k}");
}
}
}
#[test]
fn eight_short_internal_tdac() {
let n_s = SHORT_TRANSFORM_LEN; let hop = n_s / 2; let sine_short = {
let left = sine_left(hop);
let mut w = vec![0.0; n_s];
w[..hop].copy_from_slice(&left);
for m in 0..hop {
w[hop + m] = left[hop - 1 - m];
}
w
};
let total = N_L;
let input: Vec<f64> = (0..total)
.map(|i| (0.05 * i as f64).sin() + 0.4 * (0.11 * i as f64).cos())
.collect();
let start = (N_L - N_S) / 4;
let mut spec = Vec::with_capacity(NUM_SHORT_WINDOWS * SHORT_WINDOW_LEN as usize);
for j in 0..NUM_SHORT_WINDOWS {
let base = start + j * hop;
let seg: Vec<f64> = (0..n_s).map(|m| input[base + m] * sine_short[m]).collect();
let s = forward_mdct(&seg, n_s);
spec.extend_from_slice(&s);
}
let info = short_info(WindowShape::Sine);
let mut fb = Filterbank::new();
let out = fb.synthesize(&spec, &info).unwrap();
for idx in (start + hop)..1024 {
assert!(
(out[idx] - input[idx]).abs() < 1e-9,
"idx={idx} out={} input={}",
out[idx],
input[idx]
);
}
}
#[test]
fn synthesize_long_length_and_shape() {
let info = long_info(WindowShape::Sine, WindowSequence::OnlyLong);
let mut fb = Filterbank::new();
let spec = vec![0.25f64; LONG_WINDOW_LEN as usize];
let out = fb.synthesize(&spec, &info).unwrap();
assert_eq!(out.len(), LONG_WINDOW_LEN as usize);
assert!(out.iter().all(|v| v.is_finite()));
}
#[test]
fn synthesize_eight_short_length() {
let info = short_info(WindowShape::Sine);
let mut fb = Filterbank::new();
let spec = vec![0.1f64; NUM_SHORT_WINDOWS * SHORT_WINDOW_LEN as usize];
let out = fb.synthesize(&spec, &info).unwrap();
assert_eq!(out.len(), LONG_WINDOW_LEN as usize);
assert!(out.iter().all(|v| v.is_finite()));
}
#[test]
fn synthesize_rejects_wrong_length() {
let info = long_info(WindowShape::Sine, WindowSequence::OnlyLong);
let mut fb = Filterbank::new();
let spec = vec![0.0f64; 512];
assert!(matches!(
fb.synthesize(&spec, &info),
Err(Error::FilterbankInvalid)
));
let sinfo = short_info(WindowShape::Sine);
let mut fb2 = Filterbank::new();
let bad = vec![0.0f64; 1000];
assert!(matches!(
fb2.synthesize(&bad, &sinfo),
Err(Error::FilterbankInvalid)
));
}
#[test]
fn start_window_plateau_and_zero_regions() {
let fb = Filterbank::new();
let w = fb
.long_window(WindowShape::Sine, WindowShape::Sine, LongKind::Start)
.unwrap();
assert_eq!(w.len(), N_L);
for v in w.iter().take(1472).skip(1024) {
assert!((*v - 1.0).abs() < 1e-15);
}
for v in w.iter().take(N_L).skip(1600) {
assert_eq!(*v, 0.0);
}
assert!(w[1472] > w[1599]);
}
#[test]
fn stop_window_zero_and_plateau_regions() {
let fb = Filterbank::new();
let w = fb
.long_window(WindowShape::Sine, WindowShape::Sine, LongKind::Stop)
.unwrap();
assert_eq!(w.len(), N_L);
for v in w.iter().take(448) {
assert_eq!(*v, 0.0);
}
for v in w.iter().take(1024).skip(576) {
assert!((*v - 1.0).abs() < 1e-15);
}
assert!(w[448] < w[575]);
}
#[test]
fn first_frame_uses_own_shape_for_left_half() {
let info = long_info(WindowShape::Kbd, WindowSequence::OnlyLong);
let fb = Filterbank::new();
let w = fb
.windowed_signal(&vec![0.0; LONG_WINDOW_LEN as usize], &info)
.unwrap();
let _ = w;
let win = fb
.long_window(WindowShape::Kbd, WindowShape::Kbd, LongKind::OnlyLong)
.unwrap();
let kbd = kbd_left(1024, 4.0);
for n in 0..1024 {
assert!((win[n] - kbd[n]).abs() < 1e-15);
}
}
#[test]
fn ssr_kbd_short_window_matches_table_4_a_13() {
#[allow(clippy::excessive_precision)]
const TABLE_4_A_13: [(usize, f64); 32] = [
(0, 0.0000875914060105),
(1, 0.0009321760265333),
(2, 0.0032114611466596),
(3, 0.0081009893216786),
(4, 0.0171240286619181),
(5, 0.0320720743527833),
(6, 0.0548307856028528),
(7, 0.0871361822564870),
(8, 0.1302923415174603),
(9, 0.1848955425508276),
(10, 0.2506163195331889),
(11, 0.3260874142923209),
(12, 0.4089316830907141),
(13, 0.4959414909423747),
(14, 0.5833939894958904),
(15, 0.6674601983218376),
(16, 0.7446454751465113),
(17, 0.8121892962974020),
(18, 0.8683559394406505),
(19, 0.9125649996381605),
(20, 0.9453396205809574),
(21, 0.9680864942677585),
(22, 0.9827581789763112),
(23, 0.9914756203467121),
(24, 0.9961964092194694),
(25, 0.9984956609571091),
(26, 0.9994855586984285),
(27, 0.9998533730714648),
(28, 0.9999671864476404),
(29, 0.9999948432453556),
(30, 0.9999995655238333),
(31, 0.9999999961638728),
];
let left = half_window_style(64, WindowShape::Kbd, WindowStyle::Standard);
assert_eq!(left.len(), 32);
for &(i, expect) in &TABLE_4_A_13 {
assert!(
(left[i] - expect).abs() < 1e-8,
"Table 4.A.13 w({i}): got {} expect {expect}",
left[i]
);
}
let alt = kbd_left(32, 4.0);
assert!((alt[0] - TABLE_4_A_13[0].1).abs() > 1e-4);
}
#[test]
fn ssr_kbd_long_window_matches_table_4_a_14() {
#[allow(clippy::excessive_precision)]
const TABLE_4_A_14_SPREAD: [(usize, f64); 15] = [
(0, 0.0005851230124487),
(1, 0.0009642149851497),
(2, 0.0013558207534965),
(16, 0.0116765080854300),
(32, 0.0405466983507029),
(64, 0.1811734433685097),
(96, 0.4325622561631607),
(128, 0.7110428359000029),
(160, 0.9058173183656508),
(192, 0.9845850806232530),
(224, 0.9992757396582338),
(240, 0.9999442511639580),
(250, 0.9999962619864214),
(254, 0.9999995351446231),
(255, 0.9999998288155155),
];
let left = half_window_style(512, WindowShape::Kbd, WindowStyle::Standard);
assert_eq!(left.len(), 256);
for &(i, expect) in &TABLE_4_A_14_SPREAD {
assert!(
(left[i] - expect).abs() < 1e-8,
"Table 4.A.14 w({i}): got {} expect {expect}",
left[i]
);
}
let alt = kbd_left(256, 6.0);
assert!((alt[0] - TABLE_4_A_14_SPREAD[0].1).abs() > 1e-4);
}
#[test]
fn generalized_long_window_matches_standard_and_scales() {
for kind in [LongKind::OnlyLong, LongKind::Start, LongKind::Stop] {
let std = Filterbank::new()
.long_window(WindowShape::Sine, WindowShape::Sine, kind)
.unwrap();
let gen = build_long_window_n(2048, 256, WindowShape::Sine, WindowShape::Sine, kind);
assert_eq!(std, gen);
}
let w = build_long_window_n(
512,
64,
WindowShape::Sine,
WindowShape::Sine,
LongKind::Start,
);
assert_eq!(w.len(), 512);
for v in w.iter().take(368).skip(256) {
assert!((*v - 1.0).abs() < 1e-15);
}
assert!(w[368] < 1.0 && w[368] > w[399]);
for v in w.iter().skip(400) {
assert_eq!(*v, 0.0);
}
let w = build_long_window_n(
512,
64,
WindowShape::Sine,
WindowShape::Sine,
LongKind::Stop,
);
for v in w.iter().take(112) {
assert_eq!(*v, 0.0);
}
for v in w.iter().take(256).skip(144) {
assert!((*v - 1.0).abs() < 1e-15);
}
}
#[test]
fn ssr_only_long_window_is_power_complementary() {
for shape in [WindowShape::Sine, WindowShape::Kbd] {
let w = build_long_window_n(512, 64, shape, shape, LongKind::OnlyLong);
for n in 0..256 {
let s = w[n] * w[n] + w[n + 256] * w[n + 256];
assert!(
(s - 1.0).abs() < 1e-10,
"{shape:?} w²({n}) + w²({}) = {s}",
n + 256
);
}
}
}
}