#![allow(unused)]
use std::num::NonZeroU32;
use non_empty_slice::{NonEmptySlice, NonEmptyVec, non_empty_vec};
#[cfg(feature = "simd")]
use wide::f32x8;
use crate::{AudioSample, AudioSampleError, AudioSampleResult, ConvertTo, ParameterError};
#[cfg(feature = "simd")]
#[inline]
pub fn convert_f32_to_i16_simd(input: &[f32], output: &mut [i16]) -> AudioSampleResult<()> {
if input.len() != output.len() {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"slice_lengths",
"Input and output slices must have same length",
)));
}
let chunks = input.len() / 8;
for i in 0..chunks {
let start_idx = i * 8;
let chunk: [f32; 8] = input[start_idx..start_idx + 8].try_into().unwrap();
let f32_vec = f32x8::from(chunk);
let clamped = f32_vec.max(f32x8::splat(-1.0)).min(f32x8::splat(1.0));
let scaled = clamped * f32x8::splat(i16::MAX as f32);
let ints = scaled.trunc_int().to_array();
for (j, &v) in ints.iter().enumerate() {
output[start_idx + j] = v as i16;
}
}
let start_remainder = chunks * 8;
for i in start_remainder..input.len() {
output[i] = input[i].convert_to();
}
Ok(())
}
#[cfg(feature = "simd")]
#[inline]
pub fn convert_i16_to_f32_simd(input: &[i16], output: &mut [f32]) -> AudioSampleResult<()> {
if input.len() != output.len() {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"slice_lengths",
"Input and output slices must have same length",
)));
}
let chunks = input.len() / 8;
let neg_div = f32x8::splat(-(i16::MIN as f32)); let pos_div = f32x8::splat(i16::MAX as f32); for i in 0..chunks {
let start_idx = i * 8;
let f32_values = f32x8::from([
input[start_idx] as f32,
input[start_idx + 1] as f32,
input[start_idx + 2] as f32,
input[start_idx + 3] as f32,
input[start_idx + 4] as f32,
input[start_idx + 5] as f32,
input[start_idx + 6] as f32,
input[start_idx + 7] as f32,
]);
let neg = f32_values / neg_div;
let pos = f32_values / pos_div;
let mask = f32_values.simd_lt(f32x8::splat(0.0));
let result = mask.blend(neg, pos).to_array();
output[start_idx..start_idx + 8].copy_from_slice(&result);
}
let start_remainder = chunks * 8;
for i in start_remainder..input.len() {
output[i] = input[i].convert_to();
}
Ok(())
}
#[cfg(feature = "simd")]
#[inline]
pub fn convert_f32_to_i32_simd(input: &[f32], output: &mut [i32]) -> AudioSampleResult<()> {
if input.len() != output.len() {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"slice_lengths",
"Input and output slices must have same length",
)));
}
let chunks = input.len() / 8;
for i in 0..chunks {
let start_idx = i * 8;
let chunk: [f32; 8] = input[start_idx..start_idx + 8].try_into().unwrap();
let f32_vec = f32x8::from(chunk);
let clamped = f32_vec.max(f32x8::splat(-1.0)).min(f32x8::splat(1.0));
let scaled = clamped * f32x8::splat(i32::MAX as f32);
let ints = scaled.trunc_int().to_array();
output[start_idx..start_idx + 8].copy_from_slice(&ints);
}
let start_remainder = chunks * 8;
for i in start_remainder..input.len() {
output[i] = input[i].convert_to();
}
Ok(())
}
#[cfg(feature = "simd")]
#[inline]
pub fn convert_simd<T, U>(input: &[T], output: &mut [U]) -> AudioSampleResult<()>
where
T: AudioSample + ConvertTo<U>,
U: AudioSample,
{
use std::any::TypeId;
if TypeId::of::<T>() == TypeId::of::<f32>() && TypeId::of::<U>() == TypeId::of::<i16>() {
let input =
unsafe { std::slice::from_raw_parts(input.as_ptr() as *const f32, input.len()) };
let output = unsafe {
std::slice::from_raw_parts_mut(output.as_mut_ptr() as *mut i16, output.len())
};
return convert_f32_to_i16_simd(input, output);
}
if TypeId::of::<T>() == TypeId::of::<i16>() && TypeId::of::<U>() == TypeId::of::<f32>() {
let input =
unsafe { std::slice::from_raw_parts(input.as_ptr() as *const i16, input.len()) };
let output = unsafe {
std::slice::from_raw_parts_mut(output.as_mut_ptr() as *mut f32, output.len())
};
return convert_i16_to_f32_simd(input, output);
}
if TypeId::of::<T>() == TypeId::of::<f32>() && TypeId::of::<U>() == TypeId::of::<i32>() {
let input =
unsafe { std::slice::from_raw_parts(input.as_ptr() as *const f32, input.len()) };
let output = unsafe {
std::slice::from_raw_parts_mut(output.as_mut_ptr() as *mut i32, output.len())
};
return convert_f32_to_i32_simd(input, output);
}
for (i, o) in input.iter().zip(output.iter_mut()) {
*o = (*i).convert_to();
}
Ok(())
}
#[inline]
pub fn convert_scalar_unrolled<T, U>(input: &[T], output: &mut [U]) -> AudioSampleResult<()>
where
T: AudioSample + ConvertTo<U>,
U: AudioSample,
{
if input.len() != output.len() {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"slice_lengths",
"Input and output slices must have same length",
)));
}
let chunks = input.len() / 4;
let remainder = input.len() % 4;
for i in 0..chunks {
let base = i * 4;
output[base] = input[base].convert_to();
output[base + 1] = input[base + 1].convert_to();
output[base + 2] = input[base + 2].convert_to();
output[base + 3] = input[base + 3].convert_to();
}
let start_remainder = chunks * 4;
for i in 0..remainder {
output[start_remainder + i] = input[start_remainder + i].convert_to();
}
Ok(())
}
#[inline]
pub fn convert<T, U>(input: &[T], output: &mut [U]) -> AudioSampleResult<()>
where
T: AudioSample + ConvertTo<U>,
U: AudioSample,
{
#[cfg(feature = "simd")]
{
convert_simd(input, output)
}
#[cfg(not(feature = "simd"))]
{
convert_scalar_unrolled(input, output)
}
}
#[inline]
pub fn deinterleave_stereo<T: AudioSample>(
interleaved: &[T],
output: &mut [T],
) -> AudioSampleResult<()> {
if interleaved.len() != output.len() {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"slice_lengths",
"Input and output slices must have same length",
)));
}
if !interleaved.len().is_multiple_of(2) {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"interleaved_length",
"Interleaved stereo data must have even length",
)));
}
#[cfg(feature = "simd")]
{
Ok(deinterleave_stereo_simd(interleaved, output))
}
#[cfg(not(feature = "simd"))]
{
deinterleave_stereo_scalar(interleaved, output);
Ok(())
}
}
#[inline]
fn deinterleave_stereo_scalar<T: AudioSample>(interleaved: &[T], output: &mut [T]) {
let frames = interleaved.len() / 2;
let (left_out, right_out) = output.split_at_mut(frames);
let chunks = frames / 4;
let remainder = frames % 4;
for i in 0..chunks {
let base_frame = i * 4;
let base_interleaved = base_frame * 2;
left_out[base_frame] = interleaved[base_interleaved];
right_out[base_frame] = interleaved[base_interleaved + 1];
left_out[base_frame + 1] = interleaved[base_interleaved + 2];
right_out[base_frame + 1] = interleaved[base_interleaved + 3];
left_out[base_frame + 2] = interleaved[base_interleaved + 4];
right_out[base_frame + 2] = interleaved[base_interleaved + 5];
left_out[base_frame + 3] = interleaved[base_interleaved + 6];
right_out[base_frame + 3] = interleaved[base_interleaved + 7];
}
let start = chunks * 4;
for i in 0..remainder {
let frame_idx = start + i;
let interleaved_idx = frame_idx * 2;
left_out[frame_idx] = interleaved[interleaved_idx];
right_out[frame_idx] = interleaved[interleaved_idx + 1];
}
}
#[cfg(feature = "simd")]
#[inline]
fn deinterleave_stereo_simd<T: AudioSample>(interleaved: &[T], output: &mut [T]) {
deinterleave_stereo_scalar(interleaved, output)
}
#[inline]
pub fn deinterleave_multi<T: AudioSample>(
interleaved: &NonEmptySlice<T>,
output: &mut NonEmptySlice<T>,
num_channels: NonZeroU32,
) -> AudioSampleResult<()> {
if interleaved.len() != output.len() {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"slice_lengths",
"Input and output slices must have same length",
)));
}
if !interleaved
.len()
.get()
.is_multiple_of(num_channels.get() as usize)
{
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"interleaved_length",
"Interleaved data length must be divisible by channel count",
)));
}
if num_channels.get() == 2 {
return deinterleave_stereo(interleaved, output);
}
deinterleave_multi_scalar(interleaved, output, num_channels);
Ok(())
}
fn deinterleave_multi_scalar<T: AudioSample>(
interleaved: &NonEmptySlice<T>,
output: &mut NonEmptySlice<T>,
num_channels: NonZeroU32,
) {
let frames = interleaved.len().get() / num_channels.get() as usize;
for ch in 0..num_channels.get() as usize {
let out_start = ch * frames;
let out_slice = &mut output[out_start..out_start + frames];
for frame in 0..frames {
out_slice[frame] = interleaved[frame * num_channels.get() as usize + ch];
}
}
}
#[inline]
pub fn interleave_stereo<T: AudioSample>(planar: &[T], output: &mut [T]) -> AudioSampleResult<()> {
if planar.len() != output.len() {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"slice_lengths",
"Input and output slices must have same length",
)));
}
if !planar.len().is_multiple_of(2) {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"planar_length",
"Planar stereo data must have even length",
)));
}
#[cfg(feature = "simd")]
{
Ok(interleave_stereo_simd(planar, output))
}
#[cfg(not(feature = "simd"))]
{
interleave_stereo_scalar(planar, output);
Ok(())
}
}
#[inline]
fn interleave_stereo_scalar<T: AudioSample>(planar: &[T], output: &mut [T]) {
let frames = planar.len() / 2;
let (left_in, right_in) = planar.split_at(frames);
let chunks = frames / 4;
let remainder = frames % 4;
for i in 0..chunks {
let base_frame = i * 4;
let base_interleaved = base_frame * 2;
output[base_interleaved] = left_in[base_frame];
output[base_interleaved + 1] = right_in[base_frame];
output[base_interleaved + 2] = left_in[base_frame + 1];
output[base_interleaved + 3] = right_in[base_frame + 1];
output[base_interleaved + 4] = left_in[base_frame + 2];
output[base_interleaved + 5] = right_in[base_frame + 2];
output[base_interleaved + 6] = left_in[base_frame + 3];
output[base_interleaved + 7] = right_in[base_frame + 3];
}
let start = chunks * 4;
for i in 0..remainder {
let frame_idx = start + i;
let interleaved_idx = frame_idx * 2;
output[interleaved_idx] = left_in[frame_idx];
output[interleaved_idx + 1] = right_in[frame_idx];
}
}
#[cfg(feature = "simd")]
#[inline]
fn interleave_stereo_simd<T: AudioSample>(planar: &[T], output: &mut [T]) {
interleave_stereo_scalar(planar, output)
}
#[inline]
pub fn interleave_multi<T: AudioSample>(
planar: &NonEmptySlice<T>,
output: &mut NonEmptySlice<T>,
num_channels: NonZeroU32,
) -> AudioSampleResult<()> {
if planar.len() != output.len() {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"slice_lengths",
"Input and output slices must have same length",
)));
}
if !planar
.len()
.get()
.is_multiple_of(num_channels.get() as usize)
{
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"planar_length",
"Planar data length must be divisible by channel count",
)));
}
if num_channels.get() == 2 {
return interleave_stereo(planar, output);
}
interleave_multi_scalar(planar, output, num_channels);
Ok(())
}
fn interleave_multi_scalar<T: AudioSample>(
planar: &NonEmptySlice<T>,
output: &mut NonEmptySlice<T>,
num_channels: NonZeroU32,
) {
let frames = planar.len().get() / num_channels.get() as usize;
for frame in 0..frames {
let out_base = frame * num_channels.get() as usize;
for ch in 0..num_channels.get() as usize {
let in_idx = ch * frames + frame;
output[out_base + ch] = planar[in_idx];
}
}
}
#[inline]
pub fn deinterleave_stereo_vec<T: AudioSample>(
interleaved: &NonEmptyVec<T>,
) -> AudioSampleResult<NonEmptyVec<T>> {
if !interleaved.len().get().is_multiple_of(2) {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"interleaved_length",
"Interleaved stereo data must have even length",
)));
}
let mut output = non_empty_vec![T::default(); interleaved.len()];
deinterleave_stereo(interleaved, &mut output)?;
Ok(output)
}
#[inline]
pub fn deinterleave_multi_vec<T: AudioSample>(
interleaved: &NonEmptySlice<T>,
num_channels: NonZeroU32,
) -> AudioSampleResult<NonEmptyVec<T>> {
if !interleaved
.len()
.get()
.is_multiple_of(num_channels.get() as usize)
{
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"interleaved_length",
"Interleaved data length must be divisible by channel count",
)));
}
let mut output = non_empty_vec![T::default(); interleaved.len()];
deinterleave_multi(interleaved, &mut output, num_channels)?;
Ok(output)
}
#[inline]
pub fn interleave_stereo_vec<T: AudioSample>(
planar: &NonEmptyVec<T>,
) -> AudioSampleResult<NonEmptyVec<T>> {
if !planar.len().get().is_multiple_of(2) {
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"planar_length",
"Planar stereo data must have even length",
)));
}
let mut output = non_empty_vec![T::default(); planar.len()];
interleave_stereo(planar, &mut output)?;
Ok(output)
}
#[inline]
pub fn interleave_multi_vec<T: AudioSample>(
planar: &NonEmptyVec<T>,
num_channels: NonZeroU32,
) -> AudioSampleResult<NonEmptyVec<T>> {
if !planar
.len()
.get()
.is_multiple_of(num_channels.get() as usize)
{
return Err(AudioSampleError::Parameter(ParameterError::invalid_value(
"planar_length",
"Planar data length must be divisible by channel count",
)));
}
let mut output = non_empty_vec![T::default(); planar.len()];
interleave_multi(planar, &mut output, num_channels)?;
Ok(output)
}
#[cfg(test)]
mod tests {
use non_empty_iter::TryIntoNonEmptyIterator;
use super::*;
#[test]
fn test_scalar_conversion() {
let _input = non_empty_vec![0.5f32, -0.3, 0.8, 1.0, -1.0];
let mut _output = non_empty_vec![0i16; crate::nzu!(5)];
#[cfg(not(feature = "simd"))]
{
convert_scalar_unrolled(&_input, &mut _output).unwrap();
let expected0: i16 = 0.5f32.convert_to();
let expected1: i16 = (-0.3f32).convert_to();
let expected4: i16 = (-1.0f32).convert_to();
assert_eq!(_output[0], expected0);
assert_eq!(_output[1], expected1);
assert_eq!(_output[4], expected4);
}
}
#[cfg(feature = "simd")]
#[test]
fn test_simd_f32_to_i16_conversion() {
let input = non_empty_vec![0.5f32, -0.3, 0.8, 1.0, -1.0, 0.0, 0.1, -0.1, 0.25];
let mut output = non_empty_vec![0i16; crate::nzu!(9)];
convert_f32_to_i16_simd(&input, &mut output).unwrap();
let expected0: i16 = input[0].convert_to();
let expected1: i16 = input[1].convert_to();
let expected8: i16 = input[8].convert_to();
assert_eq!(output[0], expected0);
assert_eq!(output[1], expected1);
assert_eq!(output[8], expected8); }
#[cfg(feature = "simd")]
#[test]
fn test_simd_i16_to_f32_conversion() {
let input = non_empty_vec![16383i16, -9830, 26214, 32767, -32768, 0, 3276, -3276];
let mut output = non_empty_vec![0.0f32; crate::nzu!(8)];
convert_i16_to_f32_simd(&input, &mut output).unwrap();
use approx_eq::assert_approx_eq;
let expected0: f32 = input[0].convert_to();
let expected1: f32 = input[1].convert_to();
let expected3: f32 = input[3].convert_to();
assert_approx_eq!(output[0] as f64, expected0 as f64, 1e-5);
assert_approx_eq!(output[1] as f64, expected1 as f64, 1e-5);
assert_approx_eq!(output[3] as f64, expected3 as f64, 1e-5);
}
#[test]
fn test_optimized_conversion_dispatch() {
let input = non_empty_vec![0.5f32, -0.3, 0.8];
let mut output = non_empty_vec![0i16; crate::nzu!(3)];
convert(&input, &mut output).unwrap();
assert_eq!(output.len().get(), 3);
let expected0: i16 = input[0].convert_to();
assert_eq!(output[0], expected0);
}
#[test]
fn test_deinterleave_stereo_f32() {
let interleaved = vec![0.1f32, 0.5, 0.2, 0.6, 0.3, 0.7, 0.4, 0.8];
let mut output = vec![0.0f32; 8];
deinterleave_stereo(&interleaved, &mut output).unwrap();
assert_eq!(output[0], 0.1); assert_eq!(output[1], 0.2); assert_eq!(output[2], 0.3); assert_eq!(output[3], 0.4); assert_eq!(output[4], 0.5); assert_eq!(output[5], 0.6); assert_eq!(output[6], 0.7); assert_eq!(output[7], 0.8); }
#[test]
fn test_deinterleave_stereo_i16() {
let interleaved = vec![100i16, 200, 300, 400, 500, 600];
let mut output = vec![0i16; 6];
deinterleave_stereo(&interleaved, &mut output).unwrap();
assert_eq!(output[0], 100); assert_eq!(output[1], 300); assert_eq!(output[2], 500); assert_eq!(output[3], 200); assert_eq!(output[4], 400); assert_eq!(output[5], 600); }
#[test]
fn test_deinterleave_stereo_remainder() {
let interleaved = vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0];
let mut output = vec![0.0f32; 10];
deinterleave_stereo(&interleaved, &mut output).unwrap();
assert_eq!(&output[0..5], &[1.0, 3.0, 5.0, 7.0, 9.0]);
assert_eq!(&output[5..10], &[2.0, 4.0, 6.0, 8.0, 10.0]);
}
#[test]
fn test_deinterleave_multi_3ch() {
let interleaved = non_empty_vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0];
let mut output = non_empty_vec![0.0f32; crate::nzu!(6)];
deinterleave_multi(&interleaved, &mut output, NonZeroU32::new(3).unwrap());
assert_eq!(output[0], 1.0); assert_eq!(output[1], 4.0); assert_eq!(output[2], 2.0); assert_eq!(output[3], 5.0); assert_eq!(output[4], 3.0); assert_eq!(output[5], 6.0); }
#[test]
fn test_deinterleave_multi_6ch() {
let interleaved: Vec<f32> = (1..=12).map(|x| x as f32).collect();
let interleaved = NonEmptyVec::new(interleaved).unwrap();
let mut output = non_empty_vec![0.0f32; crate::nzu!(12)];
deinterleave_multi(&interleaved, &mut output, NonZeroU32::new(6).unwrap()).unwrap();
assert_eq!(&output[0..2], &[1.0, 7.0]); assert_eq!(&output[2..4], &[2.0, 8.0]); assert_eq!(&output[4..6], &[3.0, 9.0]); assert_eq!(&output[6..8], &[4.0, 10.0]); assert_eq!(&output[8..10], &[5.0, 11.0]); assert_eq!(&output[10..12], &[6.0, 12.0]); }
#[test]
fn test_interleave_stereo_f32() {
let planar = non_empty_vec![0.1f32, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8];
let mut output = non_empty_vec![0.0f32; crate::nzu!(8)];
interleave_stereo(&planar, &mut output).unwrap();
assert_eq!(output[0], 0.1); assert_eq!(output[1], 0.5); assert_eq!(output[2], 0.2); assert_eq!(output[3], 0.6); assert_eq!(output[4], 0.3); assert_eq!(output[5], 0.7); assert_eq!(output[6], 0.4); assert_eq!(output[7], 0.8); }
#[test]
fn test_interleave_stereo_i16() {
let planar = non_empty_vec![100i16, 300, 500, 200, 400, 600];
let mut output = non_empty_vec![0i16; crate::nzu!(6)];
interleave_stereo(&planar, &mut output).unwrap();
assert_eq!(output[0], 100); assert_eq!(output[1], 200); assert_eq!(output[2], 300); assert_eq!(output[3], 400); assert_eq!(output[4], 500); assert_eq!(output[5], 600); }
#[test]
fn test_interleave_multi_3ch() {
let planar = non_empty_vec![1.0f32, 4.0, 2.0, 5.0, 3.0, 6.0];
let mut output = non_empty_vec![0.0f32; crate::nzu!(6)];
interleave_multi(&planar, &mut output, NonZeroU32::new(3).unwrap()).unwrap();
assert_eq!(output[0], 1.0); assert_eq!(output[1], 2.0); assert_eq!(output[2], 3.0); assert_eq!(output[3], 4.0); assert_eq!(output[4], 5.0); assert_eq!(output[5], 6.0); }
#[test]
fn test_stereo_roundtrip_f32() {
let original = vec![1.0f32, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0];
let mut deinterleaved = vec![0.0f32; 8];
let mut reinterleaved = vec![0.0f32; 8];
deinterleave_stereo(&original, &mut deinterleaved).unwrap();
interleave_stereo(&deinterleaved, &mut reinterleaved).unwrap();
assert_eq!(original, reinterleaved);
}
#[test]
fn test_stereo_roundtrip_i16() {
let original: Vec<i16> = (1..=16).map(|x| x as i16 * 100).collect();
let mut deinterleaved = vec![0i16; 16];
let mut reinterleaved = vec![0i16; 16];
deinterleave_stereo(&original, &mut deinterleaved).unwrap();
interleave_stereo(&deinterleaved, &mut reinterleaved).unwrap();
assert_eq!(original, reinterleaved);
}
#[test]
fn test_multi_roundtrip_6ch() {
let original: Vec<f32> = (1..=60).map(|x| x as f32).collect(); let original = NonEmptyVec::new(original).unwrap();
let mut deinterleaved = non_empty_vec![0.0f32; crate::nzu!(60)];
let mut reinterleaved = non_empty_vec![0.0f32; crate::nzu!(60)];
let num_channels = NonZeroU32::new(6).unwrap();
deinterleave_multi(&original, &mut deinterleaved, num_channels).unwrap();
interleave_multi(&deinterleaved, &mut reinterleaved, num_channels).unwrap();
assert_eq!(original, reinterleaved);
}
#[test]
fn test_deinterleave_stereo_vec_fn() {
let interleaved = non_empty_vec![1.0f32, 2.0, 3.0, 4.0];
let planar = deinterleave_stereo_vec(&interleaved).unwrap();
assert_eq!(planar, non_empty_vec![1.0, 3.0, 2.0, 4.0]);
}
#[test]
fn test_interleave_stereo_vec_fn() {
let planar = non_empty_vec![1.0f32, 3.0, 2.0, 4.0];
let interleaved = interleave_stereo_vec(&planar).unwrap();
assert_eq!(interleaved, non_empty_vec![1.0, 2.0, 3.0, 4.0]);
}
#[test]
fn test_deinterleave_stereo_length_mismatch() {
let interleaved = non_empty_vec![1.0f32, 2.0, 3.0, 4.0];
let mut output = non_empty_vec![0.0f32; crate::nzu!(2)];
let result = deinterleave_stereo(&interleaved, &mut output);
assert!(result.is_err());
}
#[test]
fn test_deinterleave_stereo_odd_length() {
let interleaved = non_empty_vec![1.0f32, 2.0, 3.0]; let mut output = non_empty_vec![0.0f32; crate::nzu!(3)];
let result = deinterleave_stereo(&interleaved, &mut output);
assert!(result.is_err());
}
#[test]
fn test_deinterleave_multi_not_divisible() {
let interleaved = non_empty_vec![1.0f32, 2.0, 3.0, 4.0, 5.0]; let mut output = non_empty_vec![0.0f32; crate::nzu!(5)];
let result = deinterleave_multi(&interleaved, &mut output, NonZeroU32::new(3).unwrap());
assert!(result.is_err());
}
}
#[cfg(all(test, feature = "simd"))]
mod simd_parity_tests {
use super::*;
fn scalar_f32_to_i16(source: f32) -> i16 {
let v = source.max(-1.0).min(1.0);
let scaled = v * (i16::MAX as f32);
let as_i32: i32 = unsafe { scaled.to_int_unchecked() };
as_i32 as i16
}
fn scalar_f32_to_i32(source: f32) -> i32 {
let v = source.max(-1.0).min(1.0);
let scaled = v * (i32::MAX as f32);
scaled.clamp(i32::MIN as f32, i32::MAX as f32) as i32
}
fn scalar_i16_to_f32(v: i16) -> f32 {
if v < 0 {
(v as f32) / (-(i16::MIN as f32))
} else {
(v as f32) / (i16::MAX as f32)
}
}
fn f32_probe_inputs() -> Vec<f32> {
let mut v = vec![
0.0f32,
-0.0,
1.0,
-1.0,
0.5,
-0.5,
0.25,
-0.25,
0.99999994, -0.99999994,
2.0, -2.0, f32::NAN,
f32::INFINITY,
f32::NEG_INFINITY,
];
for k in 0..200i32 {
let x = (k as f32) / 199.0; v.push(x);
v.push(-x);
v.push(x + 0.4999999 / (i16::MAX as f32));
v.push(-(x + 0.5000001 / (i16::MAX as f32)));
}
v
}
#[test]
fn parity_f32_to_i16() {
let input = f32_probe_inputs();
let mut simd_out = vec![0i16; input.len()];
convert_f32_to_i16_simd(&input, &mut simd_out).unwrap();
for (i, &x) in input.iter().enumerate() {
assert_eq!(
simd_out[i],
scalar_f32_to_i16(x),
"f32->i16 mismatch at idx {i} for input {x} (bits {:08x})",
x.to_bits()
);
let canonical: i16 = x.convert_to();
assert_eq!(simd_out[i], canonical, "f32->i16 vs ConvertTo at idx {i}");
}
}
#[test]
fn parity_f32_to_i32() {
let input = f32_probe_inputs();
let mut simd_out = vec![0i32; input.len()];
convert_f32_to_i32_simd(&input, &mut simd_out).unwrap();
for (i, &x) in input.iter().enumerate() {
assert_eq!(
simd_out[i],
scalar_f32_to_i32(x),
"f32->i32 mismatch at idx {i} for input {x} (bits {:08x})",
x.to_bits()
);
let canonical: i32 = x.convert_to();
assert_eq!(simd_out[i], canonical, "f32->i32 vs ConvertTo at idx {i}");
}
}
#[test]
fn parity_i16_to_f32() {
let input: Vec<i16> = (i16::MIN..=i16::MAX).collect();
let mut simd_out = vec![0.0f32; input.len()];
convert_i16_to_f32_simd(&input, &mut simd_out).unwrap();
for (i, &x) in input.iter().enumerate() {
assert_eq!(
simd_out[i].to_bits(),
scalar_i16_to_f32(x).to_bits(),
"i16->f32 mismatch at idx {i} for input {x}"
);
let canonical: f32 = x.convert_to();
assert_eq!(
simd_out[i].to_bits(),
canonical.to_bits(),
"i16->f32 vs ConvertTo at idx {i}"
);
}
}
#[test]
fn parity_convert_dispatch() {
let f32_in: Vec<f32> = f32_probe_inputs();
let mut simd_i16 = vec![0i16; f32_in.len()];
let mut scal_i16 = vec![0i16; f32_in.len()];
convert(&f32_in, &mut simd_i16).unwrap();
convert_scalar_unrolled(&f32_in, &mut scal_i16).unwrap();
assert_eq!(simd_i16, scal_i16, "convert f32->i16 vs scalar unrolled");
let mut simd_i32 = vec![0i32; f32_in.len()];
let mut scal_i32 = vec![0i32; f32_in.len()];
convert(&f32_in, &mut simd_i32).unwrap();
convert_scalar_unrolled(&f32_in, &mut scal_i32).unwrap();
assert_eq!(simd_i32, scal_i32, "convert f32->i32 vs scalar unrolled");
let i16_in: Vec<i16> = (i16::MIN..=i16::MAX).step_by(7).collect();
let mut simd_f32 = vec![0.0f32; i16_in.len()];
let mut scal_f32 = vec![0.0f32; i16_in.len()];
convert(&i16_in, &mut simd_f32).unwrap();
convert_scalar_unrolled(&i16_in, &mut scal_f32).unwrap();
for i in 0..i16_in.len() {
assert_eq!(
simd_f32[i].to_bits(),
scal_f32[i].to_bits(),
"convert i16->f32 vs scalar unrolled at idx {i}"
);
}
}
#[test]
fn parity_interleave_deinterleave_stereo() {
for &frames in &[1usize, 3, 4, 5, 8, 9, 17, 33] {
let n = frames * 2;
let planar_f32: Vec<f32> = (0..n).map(|i| (i as f32) * 0.013 - 0.7).collect();
let mut simd_out = vec![0.0f32; n];
let mut scal_out = vec![0.0f32; n];
interleave_stereo(&planar_f32, &mut simd_out).unwrap();
interleave_stereo_scalar(&planar_f32, &mut scal_out);
assert_eq!(simd_out, scal_out, "interleave_stereo f32 frames={frames}");
let inter_f32 = simd_out.clone();
let mut d_simd = vec![0.0f32; n];
let mut d_scal = vec![0.0f32; n];
deinterleave_stereo(&inter_f32, &mut d_simd).unwrap();
deinterleave_stereo_scalar(&inter_f32, &mut d_scal);
assert_eq!(d_simd, d_scal, "deinterleave_stereo f32 frames={frames}");
let planar_i16: Vec<i16> = (0..n).map(|i| (i as i16) * 37 - 100).collect();
let mut s16 = vec![0i16; n];
let mut c16 = vec![0i16; n];
interleave_stereo(&planar_i16, &mut s16).unwrap();
interleave_stereo_scalar(&planar_i16, &mut c16);
assert_eq!(s16, c16, "interleave_stereo i16 frames={frames}");
}
}
#[test]
fn parity_interleave_channels_entrypoint() {
use crate::AudioSamples;
use crate::operations::traits::AudioChannelOps;
use crate::sample_rate;
use ndarray::Array1;
use non_empty_slice::NonEmptySlice;
macro_rules! check_type {
($t:ty, $gen:expr) => {{
for &nch in &[2usize, 3, 4] {
let len = 37usize; let chans: Vec<AudioSamples<'static, $t>> = (0..nch)
.map(|c| {
let data: Vec<$t> = (0..len).map(|i| $gen(c, i)).collect();
AudioSamples::new_mono(Array1::from(data), sample_rate!(44100)).unwrap()
})
.collect();
let slice = NonEmptySlice::new(&chans).unwrap();
let via_simd =
<AudioSamples<'_, $t> as AudioChannelOps>::interleave_channels(slice)
.unwrap();
let a = via_simd.as_multi_channel().unwrap();
assert_eq!(a.shape(), &[nch, len]);
let mut expected: Vec<$t> = Vec::with_capacity(nch * len);
for i in 0..len {
for c in 0..nch {
expected.push(chans[c].as_slice().unwrap()[i]);
}
}
let mut k = 0usize;
for ch in 0..nch {
for s in 0..len {
assert_eq!(
a[(ch, s)],
expected[k],
"interleave_channels {} nch={} at ({},{})",
stringify!($t),
nch,
ch,
s
);
k += 1;
}
}
}
}};
}
check_type!(i16, |c: usize, i: usize| (c as i16) * 1000 + i as i16);
check_type!(i32, |c: usize, i: usize| (c as i32) * 100_000 + i as i32);
check_type!(f32, |c: usize, i: usize| (c as f32) + (i as f32) * 0.01);
check_type!(f64, |c: usize, i: usize| (c as f64) + (i as f64) * 0.001);
}
}