Sindbad~EG File Manager
use std::f64;
use rustfft::Length;
use crate::common::mdct_error_inplace;
use crate::RequiredScratch;
use crate::{mdct::Mdct, DctNum};
/// Naive O(n^2 ) MDCT implementation
///
/// This implementation is primarily used to test other MDCT algorithms.
///
/// ~~~
/// // Computes a naive MDCT of output size 124, using the MP3 window function
/// use rustdct::mdct::{Mdct, MdctNaive, window_fn};
/// use rustdct::RequiredScratch;
///
/// let len = 124;
///
/// let dct = MdctNaive::new(len, window_fn::mp3);
///
/// let input = vec![0f32; len * 2];
/// let (input_a, input_b) = input.split_at(len);
/// let mut output = vec![0f32; len];
/// let mut scratch = vec![0f32; dct.get_scratch_len()];
///
/// dct.process_mdct_with_scratch(input_a, input_b, &mut output, &mut scratch);
/// ~~~
pub struct MdctNaive<T> {
twiddles: Box<[T]>,
window: Box<[T]>,
}
impl<T: DctNum> MdctNaive<T> {
/// Creates a new MDCT context that will process inputs of length `output_len * 2` and produce
/// outputs of length `output_len`
///
/// `output_len` must be even.
///
/// `window_fn` is a function that takes a `size` and returns a `Vec` containing `size` window values.
/// See the [`window_fn`](mdct/window_fn/index.html) module for provided window functions.
pub fn new<F>(output_len: usize, window_fn: F) -> Self
where
F: FnOnce(usize) -> Vec<T>,
{
assert!(
output_len % 2 == 0,
"The MDCT len must be even. Got {}",
output_len
);
let constant_factor = 0.5f64 * f64::consts::PI / (output_len as f64);
let twiddles: Vec<T> = (0..output_len * 4)
.map(|i| (constant_factor * (i as f64 + 0.5_f64)).cos())
.map(|c| T::from_f64(c).unwrap())
.collect();
let window = window_fn(output_len * 2);
assert_eq!(
window.len(),
output_len * 2,
"Window function returned incorrect number of values"
);
Self {
twiddles: twiddles.into_boxed_slice(),
window: window.into_boxed_slice(),
}
}
}
impl<T: DctNum> Mdct<T> for MdctNaive<T> {
fn process_mdct_with_scratch(
&self,
input_a: &[T],
input_b: &[T],
output: &mut [T],
scratch: &mut [T],
) {
validate_buffers_mdct!(
input_a,
input_b,
output,
scratch,
self.len(),
self.get_scratch_len()
);
let output_len = output.len();
let half_output = output.len() / 2;
for k in 0..output_len {
let output_cell = output.get_mut(k).unwrap();
*output_cell = T::zero();
let mut twiddle_index = (half_output + k * (output_len + 1)) % self.twiddles.len();
let twiddle_stride = k * 2 + 1;
for i in 0..input_a.len() {
let twiddle = self.twiddles[twiddle_index];
*output_cell = *output_cell + input_a[i] * self.window[i] * twiddle;
twiddle_index += twiddle_stride;
if twiddle_index >= self.twiddles.len() {
twiddle_index -= self.twiddles.len();
}
}
for i in 0..input_b.len() {
let twiddle = self.twiddles[twiddle_index];
*output_cell = *output_cell + input_b[i] * self.window[i + output_len] * twiddle;
twiddle_index += twiddle_stride;
if twiddle_index >= self.twiddles.len() {
twiddle_index -= self.twiddles.len();
}
}
}
}
fn process_imdct_with_scratch(
&self,
input: &[T],
output_a: &mut [T],
output_b: &mut [T],
scratch: &mut [T],
) {
validate_buffers_mdct!(
input,
output_a,
output_b,
scratch,
self.len(),
self.get_scratch_len()
);
let input_len = input.len();
let half_input = input_len / 2;
for k in 0..input_len {
let mut output_val = T::zero();
let mut twiddle_index = half_input + k;
let twiddle_stride = input_len + k * 2 + 1;
for i in 0..input.len() {
let twiddle = self.twiddles[twiddle_index];
output_val = output_val + input[i] * twiddle;
twiddle_index += twiddle_stride;
if twiddle_index >= self.twiddles.len() {
twiddle_index -= self.twiddles.len();
}
}
output_a[k] = output_a[k] + output_val * self.window[k];
}
for k in 0..input_len {
let mut output_val = T::zero();
let twiddle_k = if k < input_len / 2 {
k
} else {
input_len - k - 1
};
let mut twiddle_index =
self.twiddles.len() - input_len * 2 + half_input - twiddle_k - 1;
let twiddle_stride = input_len - 1 - twiddle_k * 2;
for i in 0..input.len() {
let twiddle = self.twiddles[twiddle_index];
output_val = output_val + input[i] * twiddle;
twiddle_index += twiddle_stride;
if twiddle_index >= self.twiddles.len() {
twiddle_index -= self.twiddles.len();
}
}
output_b[k] = output_b[k] + output_val * self.window[k + input_len];
}
}
}
impl<T> Length for MdctNaive<T> {
fn len(&self) -> usize {
self.twiddles.len() / 4
}
}
impl<T> RequiredScratch for MdctNaive<T> {
fn get_scratch_len(&self) -> usize {
0
}
}
#[cfg(test)]
mod unit_tests {
use super::*;
use crate::mdct::window_fn;
use std::f32;
use crate::test_utils::{compare_float_vectors, random_signal};
/// Verify our naive implementation against some known values
#[test]
fn test_known_values_mdct() {
let input_list = vec![
vec![0_f32, 0_f32, 0_f32, 0_f32],
vec![1_f32, 1_f32, -5_f32, 5_f32],
vec![7_f32, 3_f32, 8_f32, 4_f32, -1_f32, 3_f32, 0_f32, 4_f32],
vec![
7_f32, 3_f32, 8_f32, 4_f32, -1_f32, 3_f32, 0_f32, 4_f32, 1f32, 1f32, 1f32, 1f32,
],
];
let expected_list = vec![
vec![0_f32, 0_f32],
vec![0_f32, 0_f32],
vec![-4.7455063, -2.073643, -2.2964284, 8.479767],
vec![
-2.90775651,
-12.30026278,
6.92661442,
2.79403335,
3.56420194,
-2.40007133,
],
];
for (input, expected) in input_list.iter().zip(expected_list.iter()) {
let output = slow_mdct(&input, window_fn::one);
assert!(compare_float_vectors(&expected, &output));
}
}
/// Verify our naive windowed implementation against some known values
#[test]
fn test_known_values_windowed_mdct() {
let input_list = vec![
vec![0_f32, 0_f32, 0_f32, 0_f32],
vec![1_f32, 1_f32, -5_f32, 5_f32],
vec![7_f32, 3_f32, 8_f32, 4_f32, -1_f32, 3_f32, 0_f32, 4_f32],
vec![
7_f32, 3_f32, 8_f32, 4_f32, -1_f32, 3_f32, 0_f32, 4_f32, 1f32, 1f32, 1f32, 1f32,
],
];
let expected_list = vec![
vec![0_f32, 0_f32],
vec![2.29289322, 1.53553391],
vec![-4.67324308, 3.1647844, -6.22625186, 2.1647844],
vec![
-5.50153067,
-3.46580575,
3.79375195,
-1.25072987,
4.6738204,
3.16506351,
],
];
for (input, expected) in input_list.iter().zip(expected_list.iter()) {
let output = slow_mdct(&input, window_fn::mp3);
assert!(compare_float_vectors(&expected, &output));
}
}
/// Verify that our fast implementation of the MDCT gives the same output as the slow version, for many different inputs
#[test]
fn test_matches_mdct() {
for current_window_fn in &[window_fn::one, window_fn::mp3, window_fn::vorbis] {
for i in 1..10 {
let input_len = i * 4;
let output_len = i * 2;
let input = random_signal(input_len);
let (input_a, input_b) = input.split_at(output_len);
let slow_output = slow_mdct(&input, current_window_fn);
let mut fast_output = vec![0f32; output_len];
let dct = MdctNaive::new(output_len, current_window_fn);
dct.process_mdct_with_scratch(&input_a, &input_b, &mut fast_output, &mut []);
println!("{}", output_len);
println!("expected: {:?}", slow_output);
println!("actual: {:?}", fast_output);
assert!(
compare_float_vectors(&slow_output, &fast_output),
"i = {}",
i
);
}
}
}
fn slow_mdct<F>(input: &[f32], window_fn: F) -> Vec<f32>
where
F: Fn(usize) -> Vec<f32>,
{
let mut output = vec![0f32; input.len() / 2];
let size_float = output.len() as f32;
let window = window_fn(input.len());
let windowed_input: Vec<f32> = input.iter().zip(window).map(|(i, w)| i * w).collect();
for k in 0..output.len() {
let mut current_value = 0_f32;
let k_float = k as f32;
for n in 0..input.len() {
let n_float = n as f32;
let twiddle = (f32::consts::PI
* (n_float + 0.5_f32 + size_float * 0.5)
* (k_float + 0.5_f32)
/ size_float)
.cos();
current_value += windowed_input[n] * twiddle;
}
output[k] = current_value;
}
output
}
/// Verify our naive implementation against some known values
#[test]
fn test_known_values_imdct() {
let input_list = vec![
vec![0f32, 0f32],
vec![1f32, 5f32],
vec![7f32, 3f32, 8f32, 4f32],
vec![7f32, 3f32, 8f32, 4f32, -1f32, 3f32],
];
let expected_list = vec![
vec![0f32, 0f32, 0f32, 0f32],
vec![-4.2367144, 4.2367153, -2.837299, -2.8372989],
vec![
5.833236, 2.4275358, -2.4275393, -5.833232, 4.8335495, -14.584825, -14.584811,
4.8335423,
],
vec![
2.4138875,
8.921771,
-2.4359043,
2.4359055,
-8.921769,
-2.4138737,
3.1458342,
-0.63405657,
-18.502512,
-18.502502,
-0.6340414,
3.1458292,
],
];
for (input, expected) in input_list.iter().zip(expected_list.iter()) {
let slow_output = slow_imdct(&input, window_fn::one);
let mut fast_output = vec![0f32; input.len() * 2];
let (fast_output_a, fast_output_b) = fast_output.split_at_mut(input.len());
let dct = MdctNaive::new(input.len(), window_fn::one);
dct.process_imdct_with_scratch(&input, fast_output_a, fast_output_b, &mut []);
assert!(compare_float_vectors(&expected, &slow_output));
assert!(compare_float_vectors(&expected, &fast_output));
}
}
/// Verify our naive windowed implementation against some known values
#[test]
fn test_known_values_windowed_imdct() {
let input_list = vec![
vec![0f32, 0f32],
vec![1_f32, 5_f32],
vec![7_f32, 3_f32, 8_f32, 4_f32],
vec![7_f32, 3_f32, 8_f32, 4_f32, -1_f32, 3_f32],
];
let expected_list = vec![
vec![0f32, 0f32, 0f32, 0f32],
vec![
-1.6213203435596431,
3.9142135623730936,
-2.6213203435596433,
-1.0857864376269069,
],
vec![
1.1380080486867217,
1.3486674811260955,
-2.0184235241728627,
-5.7211528055198331,
4.7406716077536428,
-12.126842074178105,
-8.1028968193867765,
0.94297821246780911,
],
vec![
0.3150751815802082,
3.4142135623730949,
-1.4828837895525038,
1.9325317795197492,
-8.2426406871192732,
-2.3932336063055089,
3.1189227588735786,
-0.58578643762689731,
-14.679036212259122,
-11.263620643186901,
-0.24264068711929426,
0.41061397098787894,
],
];
for (input, expected) in input_list.iter().zip(expected_list.iter()) {
let slow_output = slow_imdct(&input, window_fn::mp3);
let mut fast_output = vec![0f32; input.len() * 2];
let (fast_output_a, fast_output_b) = fast_output.split_at_mut(input.len());
let dct = MdctNaive::new(input.len(), window_fn::mp3);
dct.process_imdct_with_scratch(&input, fast_output_a, fast_output_b, &mut []);
assert!(compare_float_vectors(&expected, &slow_output));
assert!(compare_float_vectors(&expected, &fast_output));
}
}
/// Verify that our fast implementation of the MDCT gives the same output as the slow version, for many different inputs
#[test]
fn test_matches_imdct() {
for current_window_fn in &[window_fn::one, window_fn::mp3, window_fn::vorbis] {
for i in 1..10 {
let input_len = i * 2;
let output_len = i * 4;
let input = random_signal(input_len);
let slow_output = slow_imdct(&input, current_window_fn);
let mut fast_output = vec![0f32; output_len];
let (fast_output_a, fast_output_b) = fast_output.split_at_mut(input_len);
let dct = MdctNaive::new(input_len, current_window_fn);
dct.process_imdct_with_scratch(&input, fast_output_a, fast_output_b, &mut []);
assert!(
compare_float_vectors(&slow_output, &fast_output),
"i = {}",
i
);
}
}
}
fn slow_imdct<F>(input: &[f32], window_fn: F) -> Vec<f32>
where
F: Fn(usize) -> Vec<f32>,
{
let mut output = vec![0f32; input.len() * 2];
let size_float = input.len() as f32;
for n in 0..output.len() {
let mut current_value = 0_f32;
let n_float = n as f32;
for k in 0..input.len() {
let k_float = k as f32;
let twiddle = (f32::consts::PI
* (n_float + 0.5_f32 + size_float * 0.5)
* (k_float + 0.5_f32)
/ size_float)
.cos();
current_value += input[k] * twiddle;
}
output[n] = current_value;
}
let window = window_fn(output.len());
output.iter().zip(window).map(|(e, w)| e * w).collect()
}
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists