use crate::context::Context;
use crate::effect::ambisonics::AmbisonicsType;
use crate::ffi_wrapper::FFIWrapper;
pub trait ChannelPointers {
fn as_slice(&self) -> &[*mut Sample];
fn as_mut_slice(&mut self) -> &mut [*mut Sample];
}
impl<T> ChannelPointers for T
where
T: AsRef<[*mut Sample]> + AsMut<[*mut Sample]>,
{
fn as_slice(&self) -> &[*mut Sample] {
self.as_ref()
}
fn as_mut_slice(&mut self) -> &mut [*mut Sample] {
self.as_mut()
}
}
#[derive(Debug)]
pub struct AudioBuffer<T, P: ChannelPointers = Vec<*mut Sample>> {
num_samples: u32,
channel_ptrs: P,
_marker: std::marker::PhantomData<T>,
}
impl<T, P: ChannelPointers> AudioBuffer<T, P> {
pub unsafe fn try_new(channel_ptrs: P, num_samples: u32) -> Result<Self, AudioBufferError> {
if channel_ptrs.as_slice().is_empty() {
return Err(AudioBufferError::InvalidNumChannels { num_channels: 0 });
}
if num_samples == 0 {
return Err(AudioBufferError::InvalidNumSamples { num_samples });
}
debug_assert!(
channel_ptrs.as_slice().iter().all(|&ptr| !ptr.is_null()),
"some channel pointers are null"
);
Ok(Self {
num_samples,
channel_ptrs,
_marker: std::marker::PhantomData,
})
}
pub fn num_channels(&self) -> u32 {
self.channel_ptrs.as_slice().len() as u32
}
pub const fn num_samples(&self) -> u32 {
self.num_samples
}
pub fn interleave(
&self,
context: &Context,
dst: &mut [Sample],
) -> Result<(), AudioBufferOperationError> {
let expected_len = self.num_channels() * self.num_samples();
if dst.len() as u32 != expected_len {
return Err(AudioBufferOperationError::InterleaveLengthMismatch {
dst_len: dst.len(),
expected_len,
});
}
let mut audio_buffer_ffi = self.as_ffi();
unsafe {
audionimbus_sys::iplAudioBufferInterleave(
context.raw_ptr(),
&raw mut *audio_buffer_ffi,
dst.as_mut_ptr(),
);
}
Ok(())
}
pub fn deinterleave(
&mut self,
context: &Context,
src: &[Sample],
) -> Result<(), AudioBufferOperationError> {
let expected_len = self.num_channels() * self.num_samples();
if src.len() as u32 != expected_len {
return Err(AudioBufferOperationError::DeinterleaveLengthMismatch {
src_len: src.len(),
expected_len,
});
}
let mut audio_buffer_ffi = self.as_ffi();
unsafe {
audionimbus_sys::iplAudioBufferDeinterleave(
context.raw_ptr(),
src.as_ptr().cast_mut(),
&raw mut *audio_buffer_ffi,
);
};
Ok(())
}
pub fn mix<T2, P2: ChannelPointers>(
&mut self,
context: &Context,
source: &AudioBuffer<T2, P2>,
) -> Result<(), AudioBufferOperationError> {
let self_num_channels = self.num_channels();
let other_num_channels = source.num_channels();
if self_num_channels != other_num_channels {
return Err(AudioBufferOperationError::ChannelCountMismatch {
self_num_channels,
other_num_channels,
});
}
let self_num_samples = self.num_samples();
let other_num_samples = source.num_samples();
if self_num_samples != other_num_samples {
return Err(AudioBufferOperationError::SampleCountMismatch {
self_num_samples,
other_num_samples,
});
}
unsafe {
audionimbus_sys::iplAudioBufferMix(
context.raw_ptr(),
&raw mut *source.as_ffi(),
&raw mut *self.as_ffi(),
);
}
Ok(())
}
pub fn downmix<T2, P2: ChannelPointers>(
&mut self,
context: &Context,
source: &AudioBuffer<T2, P2>,
) -> Result<(), AudioBufferOperationError> {
let self_num_samples = self.num_samples();
let other_num_samples = source.num_samples();
if self_num_samples != other_num_samples {
return Err(AudioBufferOperationError::SampleCountMismatch {
self_num_samples,
other_num_samples,
});
}
unsafe {
audionimbus_sys::iplAudioBufferDownmix(
context.raw_ptr(),
&raw mut *source.as_ffi(),
&raw mut *self.as_ffi(),
);
}
Ok(())
}
pub fn channels(&self) -> impl Iterator<Item = &[Sample]> + '_ {
self.channel_ptrs.as_slice().iter().map(|&ptr|
unsafe { std::slice::from_raw_parts(ptr, self.num_samples() as usize) })
}
pub fn channels_mut(&mut self) -> impl Iterator<Item = &mut [Sample]> + '_ {
let num_samples = self.num_samples as usize;
self.channel_ptrs.as_mut_slice().iter_mut().map(move |ptr|
unsafe { std::slice::from_raw_parts_mut(*ptr, num_samples) })
}
pub fn convert_ambisonics(
&mut self,
context: &Context,
in_type: AmbisonicsType,
out_type: AmbisonicsType,
) {
unsafe {
audionimbus_sys::iplAudioBufferConvertAmbisonics(
context.raw_ptr(),
in_type.into(),
out_type.into(),
&raw mut *self.as_ffi(),
&raw mut *self.as_ffi(),
);
}
}
pub fn convert_ambisonics_into<T2, P2: ChannelPointers>(
&mut self,
context: &Context,
in_type: AmbisonicsType,
out_type: AmbisonicsType,
out: &mut AudioBuffer<T2, P2>,
) -> Result<(), AudioBufferOperationError> {
let self_count = self.num_channels() * self.num_samples();
let other_count = out.num_channels() * out.num_samples();
if self_count != other_count {
return Err(AudioBufferOperationError::TotalSampleMismatch {
self_count,
other_count,
});
}
unsafe {
audionimbus_sys::iplAudioBufferConvertAmbisonics(
context.raw_ptr(),
in_type.into(),
out_type.into(),
&raw mut *self.as_ffi(),
&raw mut *out.as_ffi(),
);
}
Ok(())
}
pub(crate) fn as_ffi(&self) -> FFIWrapper<'_, audionimbus_sys::IPLAudioBuffer, Self> {
let audio_buffer = audionimbus_sys::IPLAudioBuffer {
numChannels: self.num_channels() as i32,
numSamples: self.num_samples() as i32,
data: self.channel_ptrs.as_slice().as_ptr().cast_mut(),
};
FFIWrapper::new(audio_buffer)
}
}
impl<T: AsRef<[Sample]>> AudioBuffer<T, Vec<*mut Sample>> {
pub fn try_with_data(data: T) -> Result<Self, AudioBufferError> {
Self::try_with_data_and_settings(data, AudioBufferSettings::default())
}
pub fn try_with_data_and_settings(
data: T,
settings: AudioBufferSettings,
) -> Result<Self, AudioBufferError> {
let data = data.as_ref();
if data.is_empty() {
return Err(AudioBufferError::EmptyData);
}
let (num_channels, num_samples) = settings.num_channels_and_samples(data)?;
let frame_size = settings.frame_size.unwrap_or(num_samples);
let frame_index = settings.frame_index;
if (frame_index + 1) * frame_size > num_samples {
return Err(AudioBufferError::FrameOutOfBounds {
frame_size,
frame_index,
});
}
let channel_ptrs = (0..num_channels)
.map(|channel| {
let index = (channel * num_samples + frame_index * frame_size) as usize;
data[index..].as_ptr().cast_mut()
})
.collect();
Ok(Self {
num_samples: frame_size,
channel_ptrs,
_marker: std::marker::PhantomData,
})
}
}
impl<'a, T: AsRef<[Sample]>> AudioBuffer<T, &'a mut [*mut Sample]> {
pub fn try_borrowed_with_data(
data: T,
null_channel_ptrs: &'a mut [*mut Sample],
) -> Result<Self, AudioBufferError> {
Self::try_borrowed_with_data_and_settings(
data,
null_channel_ptrs,
AudioBufferSettings::default(),
)
}
pub fn try_borrowed_with_data_and_settings(
data: T,
null_channel_ptrs: &'a mut [*mut Sample],
settings: AudioBufferSettings,
) -> Result<Self, AudioBufferError> {
let data = data.as_ref();
if data.is_empty() {
return Err(AudioBufferError::EmptyData);
}
let (num_channels, num_samples) = settings.num_channels_and_samples(data)?;
let frame_size = settings.frame_size.unwrap_or(num_samples);
let frame_index = settings.frame_index;
if (frame_index + 1) * frame_size > num_samples {
return Err(AudioBufferError::FrameOutOfBounds {
frame_size,
frame_index,
});
}
if null_channel_ptrs.len() as u32 != num_channels {
return Err(AudioBufferError::InvalidChannelPtrs {
actual: null_channel_ptrs.len() as u32,
expected: num_channels,
});
}
null_channel_ptrs
.iter_mut()
.enumerate()
.for_each(|(i, channel)| {
let index = i as u32 * num_samples + frame_index * frame_size;
*channel = data[index as usize..].as_ptr().cast_mut();
});
let channel_ptrs = null_channel_ptrs;
Ok(AudioBuffer {
num_samples: frame_size,
channel_ptrs,
_marker: std::marker::PhantomData,
})
}
}
impl<'a> AudioBuffer<(), &'a mut [*mut Sample]> {
pub fn try_from_slices(
channels: &[&'a [Sample]],
null_channel_ptrs: &'a mut [*mut Sample],
) -> Result<Self, AudioBufferError> {
if channels.is_empty() {
return Err(AudioBufferError::InvalidNumChannels { num_channels: 0 });
}
let num_samples = channels[0].len();
if num_samples == 0 {
return Err(AudioBufferError::InvalidNumSamples { num_samples: 0 });
}
if null_channel_ptrs.len() != channels.len() {
return Err(AudioBufferError::InvalidChannelPtrs {
actual: null_channel_ptrs.len() as u32,
expected: channels.len() as u32,
});
}
for (ptr, channel) in null_channel_ptrs.iter_mut().zip(channels.iter()) {
*ptr = channel.as_ptr().cast_mut();
}
Ok(AudioBuffer {
num_samples: num_samples as u32,
channel_ptrs: null_channel_ptrs,
_marker: std::marker::PhantomData,
})
}
}
pub type Sample = f32;
#[derive(Default, Copy, Clone, Debug)]
pub struct AudioBufferSettings {
pub num_channels: Option<u32>,
pub num_samples: Option<u32>,
pub frame_size: Option<u32>,
pub frame_index: u32,
}
impl AudioBufferSettings {
pub fn with_num_channels(num_channels: u32) -> Self {
Self {
num_channels: Some(num_channels),
..Default::default()
}
}
pub fn with_num_samples(num_samples: u32) -> Self {
Self {
num_samples: Some(num_samples),
..Default::default()
}
}
pub fn with_num_channels_and_num_samples(num_channels: u32, num_samples: u32) -> Self {
Self {
num_channels: Some(num_channels),
num_samples: Some(num_samples),
..Default::default()
}
}
pub fn num_channels_and_samples<T: AsRef<[Sample]>>(
&self,
data: T,
) -> Result<(u32, u32), AudioBufferError> {
let data = data.as_ref();
let (num_channels, num_samples) = match (self.num_channels, self.num_samples) {
(None, None) => (1, data.len() as u32),
(Some(num_channels), Some(num_samples)) => {
if num_channels == 0 {
return Err(AudioBufferError::InvalidNumChannels { num_channels });
}
if num_samples == 0 || num_channels * num_samples != data.len() as u32 {
return Err(AudioBufferError::InvalidNumSamples { num_samples });
}
(num_channels, num_samples)
}
(Some(num_channels), None) => {
if num_channels == 0 || !(data.len() as u32).is_multiple_of(num_channels) {
return Err(AudioBufferError::InvalidNumChannels { num_channels });
}
let num_samples = data.len() as u32 / num_channels;
(num_channels, num_samples)
}
(None, Some(num_samples)) => {
if num_samples == 0 || !(data.len() as u32).is_multiple_of(num_samples) {
return Err(AudioBufferError::InvalidNumSamples { num_samples });
}
let num_channels = data.len() as u32 / num_samples;
(num_channels, num_samples)
}
};
Ok((num_channels, num_samples))
}
}
pub fn allocate_channel_ptrs<T: AsRef<[Sample]>>(
data: T,
settings: AudioBufferSettings,
) -> Result<Vec<*mut Sample>, AudioBufferError> {
let (num_channels, _) = settings.num_channels_and_samples(data)?;
let channel_ptrs = vec![std::ptr::null_mut(); num_channels as usize];
Ok(channel_ptrs)
}
#[derive(Debug, PartialEq, Eq)]
pub enum AudioBufferError {
EmptyData,
InvalidNumSamples { num_samples: u32 },
InvalidNumChannels { num_channels: u32 },
InvalidChannelPtrs { actual: u32, expected: u32 },
FrameOutOfBounds { frame_size: u32, frame_index: u32 },
}
impl std::error::Error for AudioBufferError {}
impl std::fmt::Display for AudioBufferError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match &self {
Self::EmptyData => write!(f, "empty audio buffer data",),
Self::InvalidNumSamples { num_samples } => {
write!(f, "invalid number of samples per channel: {num_samples}")
}
Self::InvalidNumChannels { num_channels } => {
write!(f, "invalid number of channels: {num_channels}")
}
Self::InvalidChannelPtrs { actual, expected } => {
write!(
f,
"invalid length of channel pointers: expected {expected}, got {actual}"
)
}
Self::FrameOutOfBounds {
frame_size,
frame_index,
} => {
write!(
f,
"frame with index {frame_index} of size {frame_size} out of channel bounds"
)
}
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum AudioBufferOperationError {
InterleaveLengthMismatch { dst_len: usize, expected_len: u32 },
DeinterleaveLengthMismatch { src_len: usize, expected_len: u32 },
ChannelCountMismatch {
self_num_channels: u32,
other_num_channels: u32,
},
SampleCountMismatch {
self_num_samples: u32,
other_num_samples: u32,
},
TotalSampleMismatch { self_count: u32, other_count: u32 },
}
impl std::error::Error for AudioBufferOperationError {}
impl std::fmt::Display for AudioBufferOperationError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::InterleaveLengthMismatch {
dst_len,
expected_len,
} => write!(
f,
"destination slice length {dst_len} does not match expected length {expected_len}"
),
Self::DeinterleaveLengthMismatch {
src_len,
expected_len,
} => write!(
f,
"source slice length {src_len} does not match expected length {expected_len}"
),
Self::ChannelCountMismatch {
self_num_channels,
other_num_channels,
} => write!(
f,
"channel count mismatch: buffer has {self_num_channels} channels, other has {other_num_channels}"
),
Self::SampleCountMismatch {
self_num_samples,
other_num_samples,
} => write!(
f,
"sample count mismatch: buffer has {self_num_samples} samples, other has {other_num_samples}"
),
Self::TotalSampleMismatch {
self_count,
other_count,
} => write!(
f,
"total sample count mismatch: buffer has {self_count} samples, other has {other_count}"
),
}
}
}
pub const fn num_ambisonics_channels(order: u32) -> u32 {
(order + 1) * (order + 1)
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum ChannelRequirement {
Exactly(u32),
AtLeast(u32),
Range { min: u32, max: u32 },
}
impl ChannelRequirement {
pub fn is_satisfied_by(&self, actual: u32) -> bool {
match *self {
Self::Exactly(num_channels) => actual == num_channels,
Self::AtLeast(num_channels) => actual >= num_channels,
Self::Range { min, max } => (min..=max).contains(&actual),
}
}
}
impl std::fmt::Display for ChannelRequirement {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::Exactly(num_channels) => {
write!(f, "exactly {num_channels}")
}
Self::AtLeast(num_channels) => {
write!(f, "at least {num_channels}")
}
Self::Range { min, max } => {
write!(f, "between {min} and {max} (inclusive)")
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
mod try_new {
use super::*;
#[test]
fn test_valid() {
let mut data = vec![0.5f32; 2048];
let (left, right) = data.split_at_mut(1024);
let mut channel_ptrs: Vec<*mut f32> = vec![left.as_mut_ptr(), right.as_mut_ptr()];
let result =
unsafe { AudioBuffer::<f32, &mut Vec<*mut f32>>::try_new(&mut channel_ptrs, 1024) };
assert!(result.is_ok());
}
#[test]
fn test_invalid_num_samples() {
let channel_ptrs = vec![std::ptr::null_mut(); 2];
let result = unsafe { AudioBuffer::<(), _>::try_new(channel_ptrs, 0) };
assert!(matches!(
result,
Err(AudioBufferError::InvalidNumSamples { num_samples: 0 }),
));
}
#[test]
fn test_invalid_num_channels() {
let channel_ptrs: Vec<*mut f32> = vec![];
let result = unsafe { AudioBuffer::<(), _>::try_new(channel_ptrs, 1024) };
assert!(matches!(
result,
Err(AudioBufferError::InvalidNumChannels { num_channels: 0 }),
));
}
}
mod try_with_data {
use super::*;
#[test]
fn test_valid() {
let empty_data: Vec<f32> = vec![0.5; 1024];
assert!(AudioBuffer::try_with_data(&empty_data).is_ok());
}
#[test]
fn test_empty_data() {
let empty_data: Vec<f32> = vec![];
assert!(matches!(
AudioBuffer::try_with_data(&empty_data),
Err(AudioBufferError::EmptyData),
));
}
}
mod try_with_data_and_settings {
use super::*;
#[test]
fn test_valid_default_settings() {
let data: Vec<Sample> = vec![0.0; 10];
let settings = AudioBufferSettings::default();
let result = AudioBuffer::try_with_data_and_settings(&data, settings);
assert!(result.is_ok());
}
#[test]
fn test_valid_settings() {
let data: Vec<Sample> = vec![0.0; 6];
let settings = AudioBufferSettings {
num_channels: Some(2),
num_samples: Some(3),
..Default::default()
};
let result = AudioBuffer::try_with_data_and_settings(&data, settings);
assert!(result.is_ok());
}
#[test]
fn test_valid_settings_with_frame_size() {
let data: Vec<Sample> = vec![0.0; 10];
let settings = AudioBufferSettings {
num_channels: Some(2),
num_samples: Some(5),
frame_size: Some(3),
frame_index: 0,
};
let result = AudioBuffer::try_with_data_and_settings(&data, settings);
assert!(result.is_ok());
}
#[test]
fn test_valid_multiple_channels_and_samples() {
let data: Vec<Sample> = vec![0.0; 12];
let settings = AudioBufferSettings {
num_channels: Some(3),
num_samples: Some(4),
..Default::default()
};
let result = AudioBuffer::try_with_data_and_settings(&data, settings);
assert!(result.is_ok());
}
#[test]
fn test_empty_data() {
let data: Vec<Sample> = vec![];
let settings = AudioBufferSettings::default();
let result = AudioBuffer::try_with_data_and_settings(&data, settings);
assert!(matches!(result, Err(AudioBufferError::EmptyData)));
}
#[test]
fn test_invalid_num_channels_zero() {
let data: Vec<Sample> = vec![0.0; 10];
let settings = AudioBufferSettings {
num_channels: Some(0),
num_samples: Some(5),
frame_size: None,
frame_index: 0,
};
let result = AudioBuffer::try_with_data_and_settings(&data, settings);
assert!(matches!(
result,
Err(AudioBufferError::InvalidNumChannels { num_channels: 0 })
));
}
#[test]
fn test_invalid_num_samples_zero() {
let data: Vec<Sample> = vec![0.0; 10];
let settings = AudioBufferSettings {
num_channels: Some(2),
num_samples: Some(0),
frame_size: None,
frame_index: 0,
};
let result = AudioBuffer::try_with_data_and_settings(&data, settings);
assert!(matches!(
result,
Err(AudioBufferError::InvalidNumSamples { num_samples: 0 })
));
}
#[test]
fn test_invalid_num_samples_not_divisible() {
let data: Vec<Sample> = vec![0.0; 10];
let settings = AudioBufferSettings {
num_channels: Some(3),
num_samples: Some(3),
frame_size: None,
frame_index: 0,
};
let result = AudioBuffer::try_with_data_and_settings(&data, settings);
assert!(matches!(
result,
Err(AudioBufferError::InvalidNumSamples { num_samples: 3 })
));
}
#[test]
fn test_frame_out_of_bounds() {
let data: Vec<Sample> = vec![0.0; 10];
let settings = AudioBufferSettings {
num_channels: Some(2),
num_samples: Some(5),
frame_size: Some(3),
frame_index: 1,
};
let result = AudioBuffer::try_with_data_and_settings(&data, settings);
assert!(matches!(
result,
Err(AudioBufferError::FrameOutOfBounds {
frame_size: 3,
frame_index: 1
})
));
}
}
mod try_new_borrowed {
use super::*;
#[test]
fn test_valid_construction() {
let mut channel1 = vec![1.0, 2.0, 3.0];
let mut channel2 = vec![4.0, 5.0, 6.0];
let mut ptrs = vec![channel1.as_mut_ptr(), channel2.as_mut_ptr()];
let buffer = unsafe { AudioBuffer::<&[Sample], _>::try_new(&mut ptrs, 3) }.unwrap();
assert_eq!(buffer.num_channels(), 2);
assert_eq!(buffer.num_samples(), 3);
let channels: Vec<&[Sample]> = buffer.channels().collect();
assert_eq!(channels[0], &[1.0, 2.0, 3.0]);
assert_eq!(channels[1], &[4.0, 5.0, 6.0]);
}
#[test]
fn test_empty_channel_ptrs() {
let mut ptrs: Vec<*mut Sample> = vec![];
let result = unsafe { AudioBuffer::<&[Sample], _>::try_new(&mut ptrs, 100) };
assert!(matches!(
result,
Err(AudioBufferError::InvalidNumChannels { num_channels: 0 })
));
}
#[test]
fn test_zero_num_samples() {
let mut data = vec![1.0, 2.0, 3.0];
let mut ptrs = vec![data.as_mut_ptr()];
let result = unsafe { AudioBuffer::<&[Sample], _>::try_new(&mut ptrs, 0) };
assert!(matches!(
result,
Err(AudioBufferError::InvalidNumSamples { num_samples: 0 })
));
}
}
mod try_borrowed_with_data_and_settings {
use super::*;
#[test]
fn test_valid_construction() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let settings = AudioBufferSettings::with_num_channels(2);
let mut channel_ptrs = allocate_channel_ptrs(&data, settings).unwrap();
let buffer = AudioBuffer::try_borrowed_with_data_and_settings(
&data,
&mut channel_ptrs,
settings,
)
.unwrap();
assert_eq!(buffer.num_channels(), 2);
assert_eq!(buffer.num_samples(), 3);
let channels: Vec<&[Sample]> = buffer.channels().collect();
assert_eq!(channels[0], &[1.0, 2.0, 3.0]);
assert_eq!(channels[1], &[4.0, 5.0, 6.0]);
}
#[test]
fn test_invalid_channel_ptrs() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let settings = AudioBufferSettings::with_num_channels(2);
let mut channel_ptrs = [std::ptr::null_mut(); 3];
let result = AudioBuffer::try_borrowed_with_data_and_settings(
&data,
&mut channel_ptrs,
settings,
);
assert!(matches!(
result,
Err(AudioBufferError::InvalidChannelPtrs {
actual: 3,
expected: 2
})
));
}
}
mod try_from_slices {
use super::*;
#[test]
fn test_valid_construction() {
let channel_0 = vec![1.0, 2.0, 3.0, 4.0];
let channel_1 = vec![5.0, 6.0, 7.0, 8.0];
let channels: &[&[Sample]] = &[&channel_0, &channel_1];
let mut channel_ptrs = vec![std::ptr::null_mut(); 2];
let audio_buffer = AudioBuffer::try_from_slices(channels, &mut channel_ptrs).unwrap();
assert_eq!(audio_buffer.num_channels(), 2);
assert_eq!(audio_buffer.num_samples(), 4);
let mut iter = audio_buffer.channels();
assert_eq!(iter.next().unwrap(), &[1.0, 2.0, 3.0, 4.0]);
assert_eq!(iter.next().unwrap(), &[5.0, 6.0, 7.0, 8.0]);
assert!(iter.next().is_none());
}
#[test]
fn test_empty_channels() {
let empty_channels: &[&[Sample]] = &[];
let mut channel_ptrs = vec![];
let result = AudioBuffer::try_from_slices(empty_channels, &mut channel_ptrs);
assert!(matches!(
result,
Err(AudioBufferError::InvalidNumChannels { num_channels: 0 })
));
}
#[test]
fn test_mismatched_channel_ptrs_length() {
let channel_0 = vec![1.0, 2.0, 3.0];
let channel_1 = vec![4.0, 5.0, 6.0];
let channels: &[&[Sample]] = &[&channel_0, &channel_1];
let mut channel_ptrs_wrong_size = vec![std::ptr::null_mut(); 1];
let result = AudioBuffer::try_from_slices(channels, &mut channel_ptrs_wrong_size);
assert!(matches!(
result,
Err(AudioBufferError::InvalidChannelPtrs {
actual: 1,
expected: 2
})
));
}
#[test]
fn test_empty_channel_data() {
let empty_channel = vec![];
let channels_with_empty: &[&[Sample]] = &[&empty_channel];
let mut channel_ptrs = vec![std::ptr::null_mut(); 1];
let result = AudioBuffer::try_from_slices(channels_with_empty, &mut channel_ptrs);
assert!(matches!(
result,
Err(AudioBufferError::InvalidNumSamples { num_samples: 0 })
));
}
}
mod channels_iteration {
use super::*;
#[test]
fn test_channels_iter() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let buffer = AudioBuffer::try_with_data_and_settings(
&data,
AudioBufferSettings::with_num_channels(2),
)
.unwrap();
let channels: Vec<&[Sample]> = buffer.channels().collect();
assert_eq!(channels.len(), 2);
assert_eq!(channels[0], &[1.0, 2.0, 3.0]);
assert_eq!(channels[1], &[4.0, 5.0, 6.0]);
}
#[test]
fn test_channels_mut_iter() {
let mut data = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let mut buffer = AudioBuffer::try_with_data_and_settings(
&mut data,
AudioBufferSettings::with_num_channels(2),
)
.unwrap();
for channel in buffer.channels_mut() {
for sample in channel.iter_mut() {
*sample *= 2.0;
}
}
assert_eq!(data, vec![2.0, 4.0, 6.0, 8.0, 10.0, 12.0]);
}
}
mod audio_buffer_settings {
use super::*;
#[test]
fn test_with_num_channels() {
let settings = AudioBufferSettings::with_num_channels(4);
assert_eq!(settings.num_channels, Some(4));
assert_eq!(settings.num_samples, None);
}
#[test]
fn test_with_num_samples() {
let settings = AudioBufferSettings::with_num_samples(1024);
assert_eq!(settings.num_channels, None);
assert_eq!(settings.num_samples, Some(1024));
}
#[test]
fn test_with_num_channels_and_num_samples() {
let settings = AudioBufferSettings::with_num_channels_and_num_samples(2, 512);
assert_eq!(settings.num_channels, Some(2));
assert_eq!(settings.num_samples, Some(512));
}
#[test]
fn test_num_channels_and_samples_inference() {
let data = vec![0.0; 12];
let settings = AudioBufferSettings::default();
let (channels, samples) = settings.num_channels_and_samples(&data).unwrap();
assert_eq!(channels, 1);
assert_eq!(samples, 12);
let settings = AudioBufferSettings::with_num_channels(3);
let (channels, samples) = settings.num_channels_and_samples(&data).unwrap();
assert_eq!(channels, 3);
assert_eq!(samples, 4);
let settings = AudioBufferSettings::with_num_samples(4);
let (channels, samples) = settings.num_channels_and_samples(&data).unwrap();
assert_eq!(channels, 3);
assert_eq!(samples, 4);
}
}
mod allocate_channel_ptrs {
use super::*;
#[test]
fn test_valid() {
let data = vec![0.0; 12];
let settings = AudioBufferSettings::with_num_channels(3);
let ptrs = allocate_channel_ptrs(&data, settings).unwrap();
assert_eq!(ptrs.len(), 3);
assert!(ptrs.iter().all(|&ptr| ptr.is_null()));
}
#[test]
fn test_invalid() {
let data = vec![0.0; 10];
let settings = AudioBufferSettings {
num_channels: Some(3),
num_samples: Some(3),
..Default::default()
};
let result = allocate_channel_ptrs(&data, settings);
assert!(result.is_err());
}
}
mod channel_requirement {
use super::*;
#[test]
fn test_is_satisfied_by() {
assert!(ChannelRequirement::Exactly(2).is_satisfied_by(2));
assert!(!ChannelRequirement::Exactly(2).is_satisfied_by(1));
assert!(ChannelRequirement::AtLeast(2).is_satisfied_by(3));
assert!(!ChannelRequirement::AtLeast(2).is_satisfied_by(1));
assert!(ChannelRequirement::Range { min: 1, max: 4 }.is_satisfied_by(3));
assert!(!ChannelRequirement::Range { min: 1, max: 4 }.is_satisfied_by(5));
}
}
mod mix {
use super::*;
#[test]
fn test_valid() {
let context = Context::default();
let source = vec![0.5; 100];
let source_buffer = AudioBuffer::try_with_data(&source).unwrap();
let mut mix = vec![0.5; 100];
let mut mix_buffer = AudioBuffer::try_with_data(&mut mix).unwrap();
assert!(mix_buffer.mix(&context, &source_buffer).is_ok());
}
#[test]
fn test_mismatched_channels() {
let context = Context::default();
let source = vec![0.5; 100];
let source_buffer = AudioBuffer::try_with_data(&source).unwrap();
let mut mix = vec![0.5; 200];
let mut mix_buffer = AudioBuffer::try_with_data_and_settings(
&mut mix,
AudioBufferSettings::with_num_channels(2),
)
.unwrap();
assert_eq!(
mix_buffer.mix(&context, &source_buffer),
Err(AudioBufferOperationError::ChannelCountMismatch {
self_num_channels: 2,
other_num_channels: 1
}),
);
}
#[test]
fn test_sample_count_mismatch() {
let context = Context::default();
let source = vec![0.0; 512];
let source_buffer = AudioBuffer::try_with_data(&source).unwrap();
let mix = vec![0.0; 1024];
let mut mix_buffer = AudioBuffer::try_with_data(&mix).unwrap();
assert_eq!(
mix_buffer.mix(&context, &source_buffer),
Err(AudioBufferOperationError::SampleCountMismatch {
self_num_samples: 1024,
other_num_samples: 512,
}),
);
}
}
mod downmix {
use super::*;
#[test]
fn test_valid() {
let context = Context::default();
let input = vec![0.5; 200];
let input_buffer = AudioBuffer::try_with_data(&input).unwrap();
let mut output = vec![0.5; 200];
let mut output_buffer = AudioBuffer::try_with_data(&mut output).unwrap();
assert!(output_buffer.downmix(&context, &input_buffer).is_ok());
}
#[test]
fn test_mismatched_samples() {
let context = Context::default();
let input = vec![0.5; 200];
let input_buffer = AudioBuffer::try_with_data_and_settings(
&input,
AudioBufferSettings::with_num_channels(2),
)
.unwrap();
let mut output = vec![0.5; 50];
let mut output_buffer = AudioBuffer::try_with_data(&mut output).unwrap();
assert_eq!(
output_buffer.downmix(&context, &input_buffer),
Err(AudioBufferOperationError::SampleCountMismatch {
self_num_samples: 50,
other_num_samples: 100
}),
);
}
}
mod interleave {
use super::*;
#[test]
fn test_valid() {
let context = Context::default();
let samples = vec![0.0; 1024];
let buffer = AudioBuffer::try_with_data(&samples).unwrap();
let mut dst = vec![0.0; 1024];
assert!(buffer.interleave(&context, &mut dst).is_ok());
}
#[test]
fn test_length_mismatch() {
let context = Context::default();
let samples = vec![0.0; 1024];
let buffer = AudioBuffer::try_with_data(&samples).unwrap();
let mut dst = vec![0.0; 512];
assert_eq!(
buffer.interleave(&context, &mut dst),
Err(AudioBufferOperationError::InterleaveLengthMismatch {
dst_len: 512,
expected_len: 1024,
}),
);
}
}
mod deinterleave {
use super::*;
#[test]
fn test_valid() {
let context = Context::default();
let samples = vec![0.0; 1024];
let mut buffer = AudioBuffer::try_with_data(&samples).unwrap();
let src = vec![0.0; 1024];
assert!(buffer.deinterleave(&context, &src).is_ok());
}
#[test]
fn test_length_mismatch() {
let context = Context::default();
let samples = vec![0.0; 1024];
let mut buffer = AudioBuffer::try_with_data(&samples).unwrap();
let src = vec![0.0; 2048];
assert_eq!(
buffer.deinterleave(&context, &src),
Err(AudioBufferOperationError::DeinterleaveLengthMismatch {
src_len: 2048,
expected_len: 1024,
}),
);
}
}
mod convert_ambisonics {
use super::*;
#[test]
fn test_valid() {
let context = Context::default();
let samples1 = vec![0.0; 1024];
let mut buffer1 = AudioBuffer::try_with_data_and_settings(
&samples1,
AudioBufferSettings::with_num_channels(4),
)
.unwrap();
let samples2 = vec![0.0; 1024];
let mut buffer2 = AudioBuffer::try_with_data_and_settings(
&samples2,
AudioBufferSettings::with_num_channels(4),
)
.unwrap();
assert!(buffer1
.convert_ambisonics_into(
&context,
AmbisonicsType::N3D,
AmbisonicsType::FuMa,
&mut buffer2,
)
.is_ok());
}
#[test]
fn test_total_sample_mismatch() {
let context = Context::default();
let samples1 = vec![0.0; 1024];
let mut buffer1 = AudioBuffer::try_with_data_and_settings(
&samples1,
AudioBufferSettings::with_num_channels(4),
)
.unwrap();
let samples2 = vec![0.0; 512];
let mut buffer2 = AudioBuffer::try_with_data_and_settings(
&samples2,
AudioBufferSettings::with_num_channels(4),
)
.unwrap();
assert_eq!(
buffer1.convert_ambisonics_into(
&context,
AmbisonicsType::N3D,
AmbisonicsType::FuMa,
&mut buffer2,
),
Err(AudioBufferOperationError::TotalSampleMismatch {
self_count: 1024,
other_count: 512,
}),
);
}
}
}