use std::sync::OnceLock;
use crate::frame::{BlockType, GranuleSideInfo, GRANULE_LINES};
const CI: [f32; 8] = [
-0.6, -0.535, -0.33, -0.185, -0.095, -0.041, -0.0142, -0.0037,
];
fn weights() -> &'static ([f32; 8], [f32; 8]) {
static T: OnceLock<([f32; 8], [f32; 8])> = OnceLock::new();
T.get_or_init(|| {
let mut cs = [0f32; 8];
let mut ca = [0f32; 8];
for i in 0..8 {
let d = (1.0 + CI[i] * CI[i]).sqrt();
cs[i] = 1.0 / d;
ca[i] = CI[i] / d;
}
(cs, ca)
})
}
pub fn expand(gi: &GranuleSideInfo, lines: &mut [f32; GRANULE_LINES]) {
let is_short = gi.window_switching && gi.block_type == BlockType::Short;
let boundaries = if is_short {
usize::from(gi.mixed_block)
} else {
31
};
let (cs, ca) = weights();
for sb in 1..=boundaries {
let base = sb * 18;
let w = &mut lines[base - 8..base + 8];
for i in 0..8 {
let (lower, upper) = (7 - i, 8 + i);
let a = w[lower];
let b = w[upper];
w[lower] = a * cs[i] + b * ca[i];
w[upper] = -a * ca[i] + b * cs[i];
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::decode::antialias::reduce;
#[test]
fn expand_then_reduce_is_identity() {
let mut lines = [0f32; GRANULE_LINES];
for i in 0..GRANULE_LINES {
lines[i] = ((i % 17) as f32 - 8.0) * 0.1;
}
let original = lines;
expand(&GranuleSideInfo::default(), &mut lines); reduce(&GranuleSideInfo::default(), &mut lines);
for i in 0..GRANULE_LINES {
assert!(
(lines[i] - original[i]).abs() < 1e-5,
"line {i}: {} vs {}",
lines[i],
original[i]
);
}
}
}