#![forbid(unsafe_code)]
use std::f32::consts::PI;
#[derive(Debug, Clone)]
pub struct VorbisMdct {
n: usize,
window: Vec<f32>,
cos_table: Vec<f32>,
}
impl VorbisMdct {
#[must_use]
#[allow(clippy::cast_precision_loss)]
pub fn new(n: usize) -> Self {
let window = Self::compute_vorbis_window(n);
let cos_table = Self::compute_cos_table(n);
Self {
n,
window,
cos_table,
}
}
#[allow(clippy::cast_precision_loss)]
fn compute_vorbis_window(n: usize) -> Vec<f32> {
let mut window = Vec::with_capacity(n);
for i in 0..n {
let x = ((i as f32 + 0.5) / n as f32 * PI).sin();
let w = (x * x * PI / 2.0).sin();
window.push(w);
}
window
}
#[allow(clippy::cast_precision_loss)]
fn compute_cos_table(n: usize) -> Vec<f32> {
let n2 = n / 2;
let mut table = Vec::with_capacity(n * n2);
for k in 0..n2 {
for i in 0..n {
let angle = PI / n as f32 * (i as f32 + 0.5 + n as f32 / 2.0) * (k as f32 + 0.5);
table.push(angle.cos());
}
}
table
}
#[allow(clippy::cast_precision_loss)]
pub fn forward(&self, input: &[f32], output: &mut [f32]) {
let n = self.n;
let n2 = n / 2;
assert_eq!(input.len(), n);
assert_eq!(output.len(), n);
let mut windowed = vec![0.0; n];
for i in 0..n {
windowed[i] = input[i] * self.window[i];
}
for k in 0..n2 {
let mut sum = 0.0;
for i in 0..n {
let angle = PI / n as f32 * (i as f32 + 0.5 + n as f32 / 2.0) * (k as f32 + 0.5);
sum += windowed[i] * angle.cos();
}
output[k] = sum;
}
for i in n2..n {
output[i] = 0.0;
}
}
#[allow(clippy::cast_precision_loss)]
pub fn inverse(&self, input: &[f32], output: &mut [f32]) {
let n = self.n;
let n2 = n / 2;
assert_eq!(input.len(), n2);
assert_eq!(output.len(), n);
for i in 0..n {
let mut sum = 0.0;
for k in 0..n2 {
let angle = PI / n as f32 * (i as f32 + 0.5 + n as f32 / 2.0) * (k as f32 + 0.5);
sum += input[k] * angle.cos();
}
output[i] = sum * 2.0 / n as f32;
}
for i in 0..n {
output[i] *= self.window[i];
}
}
#[must_use]
pub const fn size(&self) -> usize {
self.n
}
#[must_use]
pub fn window(&self) -> &[f32] {
&self.window
}
}
#[derive(Debug, Clone)]
pub struct OverlapAdd {
size: usize,
buffer: Vec<f32>,
}
impl OverlapAdd {
#[must_use]
pub fn new(size: usize) -> Self {
Self {
size,
buffer: vec![0.0; size],
}
}
pub fn process(&mut self, input: &[f32], output: &mut [f32]) {
assert_eq!(input.len(), 2 * self.size);
assert_eq!(output.len(), self.size);
for i in 0..self.size {
output[i] = self.buffer[i] + input[i];
}
self.buffer.copy_from_slice(&input[self.size..]);
}
pub fn reset(&mut self) {
self.buffer.fill(0.0);
}
#[must_use]
pub const fn size(&self) -> usize {
self.size
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_mdct_creation() {
let mdct = VorbisMdct::new(256);
assert_eq!(mdct.size(), 256);
assert_eq!(mdct.window().len(), 256);
}
#[test]
fn test_vorbis_window() {
let window = VorbisMdct::compute_vorbis_window(256);
assert_eq!(window.len(), 256);
for i in 0..128 {
let w1 = window[i];
let w2 = window[255 - i];
assert!((w1 - w2).abs() < 1e-6);
}
for &w in &window {
assert!(w >= 0.0 && w <= 1.0);
}
}
#[test]
fn test_mdct_forward() {
let mdct = VorbisMdct::new(256);
let input = vec![1.0; 256];
let mut output = vec![0.0; 256];
mdct.forward(&input, &mut output);
assert!(output[..128].iter().any(|&x| x.abs() > 1e-6));
assert!(output[128..].iter().all(|&x| x == 0.0));
}
#[test]
fn test_mdct_inverse() {
let mdct = VorbisMdct::new(256);
let coeffs = vec![1.0; 128];
let mut output = vec![0.0; 256];
mdct.inverse(&coeffs, &mut output);
assert!(output.iter().any(|&x| x.abs() > 1e-6));
}
#[test]
fn test_mdct_roundtrip() {
let mdct = VorbisMdct::new(128);
let input = vec![1.0; 128];
let mut coeffs = vec![0.0; 128];
let mut output = vec![0.0; 128];
mdct.forward(&input, &mut coeffs);
mdct.inverse(&coeffs[..64], &mut output);
let input_energy: f32 = input.iter().map(|x| x * x).sum();
let output_energy: f32 = output.iter().map(|x| x * x).sum();
assert!(input_energy > 0.0);
assert!(output_energy > 0.0);
}
#[test]
fn test_overlap_add_creation() {
let ola = OverlapAdd::new(128);
assert_eq!(ola.size(), 128);
}
#[test]
fn test_overlap_add_process() {
let mut ola = OverlapAdd::new(128);
let input = vec![1.0; 256];
let mut output = vec![0.0; 128];
ola.process(&input, &mut output);
assert_eq!(output[0], 1.0);
ola.process(&input, &mut output);
assert_eq!(output[0], 2.0);
}
#[test]
fn test_overlap_add_reset() {
let mut ola = OverlapAdd::new(128);
let input = vec![1.0; 256];
let mut output = vec![0.0; 128];
ola.process(&input, &mut output);
ola.reset();
ola.process(&input, &mut output);
assert_eq!(output[0], 1.0);
}
}