#![allow(unused_imports)]
#[cfg(feature = "std")]
use std::f32::consts::PI;
#[cfg(not(feature = "std"))]
use core::f32::consts::PI;
use num_traits::{Float, FromPrimitive};
pub struct Hadamard<T: Float> {
size: usize,
_marker: core::marker::PhantomData<T>,
}
impl<T: Float + FromPrimitive> Hadamard<T> {
pub fn new(size: usize) -> Self {
Self {
size,
_marker: core::marker::PhantomData,
}
}
pub fn in_place(&self, data: &mut [T]) {
self.unscaled_in_place(data);
let factor = self.scaling_factor();
for c in 0..self.size {
data[c] = data[c] * factor;
}
}
pub fn scaling_factor(&self) -> T {
if self.size == 0 {
T::one()
} else {
T::from_f32(1.0 / (self.size as f32).sqrt()).unwrap()
}
}
pub fn unscaled_in_place(&self, data: &mut [T]) {
if self.size <= 1 {
return;
}
let mut h_size = 1;
while h_size < self.size {
for start_index in (0..self.size).step_by(h_size * 2) {
for i in start_index..(start_index + h_size).min(self.size) {
if i + h_size < self.size {
let a = data[i];
let b = data[i + h_size];
data[i] = a + b;
data[i + h_size] = a - b;
}
}
}
h_size *= 2;
}
}
}
pub struct Householder<T: Float> {
size: usize,
_marker: core::marker::PhantomData<T>,
}
impl<T: Float + FromPrimitive> Householder<T> {
pub fn new(size: usize) -> Self {
Self {
size,
_marker: core::marker::PhantomData,
}
}
pub fn in_place(&self, data: &mut [T]) {
if self.size < 1 {
return;
}
let factor = T::from_f32(-2.0 / self.size as f32).unwrap();
let mut sum = data[0];
for i in 1..self.size {
sum = sum + data[i];
}
sum = sum * factor;
for i in 0..self.size {
data[i] = data[i] + sum;
}
}
pub fn scaling_factor(&self) -> T {
T::one()
}
}
pub struct StereoMultiMixer<T: Float> {
channels: usize,
coeffs: Vec<T>,
}
impl<T: Float + FromPrimitive> StereoMultiMixer<T> {
pub fn new(channels: usize) -> Self {
assert!(channels > 0, "StereoMultiMixer must have a positive number of channels");
assert_eq!(channels % 2, 0, "StereoMultiMixer must have an even number of channels");
let h_channels = channels / 2;
let mut coeffs = vec![T::zero(); channels];
coeffs[0] = T::one();
coeffs[1] = T::zero();
for i in 1..h_channels {
let phase = PI * i as f32 / channels as f32;
coeffs[2 * i] = T::from_f32(phase.cos()).unwrap();
coeffs[2 * i + 1] = T::from_f32(phase.sin()).unwrap();
}
Self { channels, coeffs }
}
pub fn stereo_to_multi(&self, input: &[T; 2], output: &mut [T]) {
let scale = T::from_f32((2.0 / self.channels as f32).sqrt()).unwrap();
output[0] = input[0] * scale;
output[1] = input[1] * scale;
for i in (2..self.channels).step_by(2) {
output[i] = (input[0] * self.coeffs[i] + input[1] * self.coeffs[i + 1]) * scale;
output[i + 1] = (input[1] * self.coeffs[i] - input[0] * self.coeffs[i + 1]) * scale;
}
}
pub fn multi_to_stereo(&self, input: &[T], output: &mut [T; 2]) {
output[0] = input[0];
output[1] = input[1];
for i in (2..self.channels).step_by(2) {
output[0] = output[0] + input[i] * self.coeffs[i] - input[i + 1] * self.coeffs[i + 1];
output[1] = output[1] + input[i + 1] * self.coeffs[i] + input[i] * self.coeffs[i + 1];
}
}
pub fn scaling_factor1(&self) -> T {
T::from_f32(2.0 / self.channels as f32).unwrap()
}
pub fn scaling_factor2(&self) -> T {
T::from_f32((2.0 / self.channels as f32).sqrt()).unwrap()
}
}
pub fn cheap_energy_crossfade<T: Float + FromPrimitive>(x: T, to_coeff: &mut T, from_coeff: &mut T) {
*to_coeff = x;
*from_coeff = T::one() - x;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_householder() {
let householder = Householder::<f32>::new(4);
let mut data = vec![1.0, 2.0, 3.0, 4.0];
householder.in_place(&mut data);
let mean = (1.0 + 2.0 + 3.0 + 4.0) / 4.0;
let expected = vec![
1.0 - 2.0 * mean,
2.0 - 2.0 * mean,
3.0 - 2.0 * mean,
4.0 - 2.0 * mean,
];
for i in 0..4 {
assert!((data[i] - expected[i]).abs() < 1e-10);
}
}
#[test]
fn test_hadamard() {
let hadamard = Hadamard::<f32>::new(4);
let mut data = vec![1.0, 2.0, 3.0, 4.0];
hadamard.in_place(&mut data);
let expected = vec![5.0, -1.0, -2.0, 0.0];
for i in 0..4 {
assert!((data[i] - expected[i]).abs() < 1e-6);
}
}
#[test]
fn test_stereo_multi_mixer() {
let mixer = StereoMultiMixer::<f32>::new(4);
let input = [1.0, 2.0];
let mut output = vec![0.0; 4];
mixer.stereo_to_multi(&input, &mut output);
let input_energy = input[0] * input[0] + input[1] * input[1];
let output_energy = output.iter().map(|&x| x * x).sum::<f32>();
assert!((input_energy - output_energy).abs() < 1e-6);
let mut round_trip = [0.0, 0.0];
mixer.multi_to_stereo(&output, &mut round_trip);
let scale = mixer.scaling_factor2();
assert!((round_trip[0] * scale - input[0]).abs() < 1e-6);
assert!((round_trip[1] * scale - input[1]).abs() < 1e-6);
}
#[test]
fn test_cheap_energy_crossfade() {
let mut to_coeff = 0.0;
let mut from_coeff = 0.0;
cheap_energy_crossfade(0.5, &mut to_coeff, &mut from_coeff);
assert!((to_coeff - from_coeff).abs() < 1e-6);
assert!((to_coeff + from_coeff - 1.0).abs() < 0.03);
cheap_energy_crossfade(0.0, &mut to_coeff, &mut from_coeff);
assert!(to_coeff < 0.01);
assert!((from_coeff - 1.0).abs() < 0.01);
cheap_energy_crossfade(1.0, &mut to_coeff, &mut from_coeff);
assert!((to_coeff - 1.0).abs() < 0.01);
assert!(from_coeff < 0.01);
}
}