use super::audio_effect_state::AudioEffectState;
use super::EffectError;
use crate::audio_buffer::{AudioBuffer, Sample};
use crate::audio_settings::AudioSettings;
use crate::context::Context;
use crate::error::{to_option_error, SteamAudioError};
use crate::ffi_wrapper::FFIWrapper;
use crate::geometry::Direction;
use crate::hrtf::{Hrtf, HrtfInterpolation};
use crate::{ChannelPointers, ChannelRequirement};
#[derive(Debug)]
pub struct BinauralEffect(audionimbus_sys::IPLBinauralEffect);
impl BinauralEffect {
pub fn try_new(
context: &Context,
audio_settings: &AudioSettings,
binaural_effect_settings: &BinauralEffectSettings,
) -> Result<Self, SteamAudioError> {
let mut binaural_effect = Self(std::ptr::null_mut());
let status = unsafe {
audionimbus_sys::iplBinauralEffectCreate(
context.raw_ptr(),
&mut audionimbus_sys::IPLAudioSettings::from(audio_settings),
&mut audionimbus_sys::IPLBinauralEffectSettings::from(binaural_effect_settings),
binaural_effect.raw_ptr_mut(),
)
};
if let Some(error) = to_option_error(status) {
return Err(error);
}
Ok(binaural_effect)
}
pub fn apply<I, O, PI: ChannelPointers, PO: ChannelPointers>(
&mut self,
binaural_effect_params: &BinauralEffectParams,
input_buffer: &AudioBuffer<I, PI>,
output_buffer: &AudioBuffer<O, PO>,
) -> Result<AudioEffectState, EffectError>
where
I: AsRef<[Sample]>,
O: AsRef<[Sample]> + AsMut<[Sample]>,
{
let num_input_channels = input_buffer.num_channels();
if !(1..=2).contains(&num_input_channels) {
return Err(EffectError::InvalidInputChannels {
expected: ChannelRequirement::Range { min: 1, max: 2 },
actual: num_input_channels,
});
}
let num_output_channels = output_buffer.num_channels();
if num_output_channels != 2 {
return Err(EffectError::InvalidOutputChannels {
expected: ChannelRequirement::Exactly(2),
actual: num_output_channels,
});
}
let state = unsafe {
audionimbus_sys::iplBinauralEffectApply(
self.raw_ptr(),
&raw mut *binaural_effect_params.as_ffi(),
&raw mut *input_buffer.as_ffi(),
&raw mut *output_buffer.as_ffi(),
)
}
.into();
Ok(state)
}
pub fn tail<O>(&self, output_buffer: &AudioBuffer<O>) -> Result<AudioEffectState, EffectError>
where
O: AsRef<[Sample]> + AsMut<[Sample]>,
{
let num_output_channels = output_buffer.num_channels();
if num_output_channels != 2 {
return Err(EffectError::InvalidOutputChannels {
expected: ChannelRequirement::Exactly(2),
actual: num_output_channels,
});
}
let state = unsafe {
audionimbus_sys::iplBinauralEffectGetTail(
self.raw_ptr(),
&raw mut *output_buffer.as_ffi(),
)
}
.into();
Ok(state)
}
pub fn tail_size(&self) -> usize {
unsafe { audionimbus_sys::iplBinauralEffectGetTailSize(self.raw_ptr()) as usize }
}
pub fn reset(&mut self) {
unsafe { audionimbus_sys::iplBinauralEffectReset(self.raw_ptr()) };
}
pub const fn raw_ptr(&self) -> audionimbus_sys::IPLBinauralEffect {
self.0
}
pub const fn raw_ptr_mut(&mut self) -> &mut audionimbus_sys::IPLBinauralEffect {
&mut self.0
}
}
impl Drop for BinauralEffect {
fn drop(&mut self) {
unsafe { audionimbus_sys::iplBinauralEffectRelease(&raw mut self.0) }
}
}
unsafe impl Send for BinauralEffect {}
unsafe impl Sync for BinauralEffect {}
impl Clone for BinauralEffect {
fn clone(&self) -> Self {
Self(unsafe { audionimbus_sys::iplBinauralEffectRetain(self.0) })
}
}
#[derive(Debug)]
pub struct BinauralEffectSettings<'a> {
pub hrtf: &'a Hrtf,
}
impl From<&BinauralEffectSettings<'_>> for audionimbus_sys::IPLBinauralEffectSettings {
fn from(settings: &BinauralEffectSettings) -> Self {
Self {
hrtf: settings.hrtf.raw_ptr(),
}
}
}
#[derive(Debug)]
pub struct BinauralEffectParams<'a> {
pub direction: Direction,
pub interpolation: HrtfInterpolation,
pub spatial_blend: f32,
pub hrtf: &'a Hrtf,
pub peak_delays: Option<[f32; 2]>,
}
impl BinauralEffectParams<'_> {
pub(crate) fn as_ffi(&self) -> FFIWrapper<'_, audionimbus_sys::IPLBinauralEffectParams, Self> {
let peak_delays_ptr = self
.peak_delays
.as_ref()
.map_or(std::ptr::null_mut(), |peak_delays| {
peak_delays.as_ptr().cast_mut()
});
let binaural_effect_params = audionimbus_sys::IPLBinauralEffectParams {
direction: self.direction.into(),
interpolation: self.interpolation.into(),
spatialBlend: self.spatial_blend,
hrtf: self.hrtf.raw_ptr(),
peakDelays: peak_delays_ptr,
};
FFIWrapper::new(binaural_effect_params)
}
}
#[cfg(test)]
mod tests {
use crate::*;
mod apply {
use super::*;
#[test]
fn test_valid_mono_input() {
let context = Context::default();
let audio_settings = AudioSettings::default();
let hrtf = Hrtf::try_new(&context, &audio_settings, &HrtfSettings::default()).unwrap();
let mut effect = BinauralEffect::try_new(
&context,
&audio_settings,
&BinauralEffectSettings { hrtf: &hrtf },
)
.unwrap();
let params = BinauralEffectParams {
direction: Direction::new(1.0, 0.0, 0.0),
interpolation: HrtfInterpolation::Nearest,
spatial_blend: 1.0,
hrtf: &hrtf,
peak_delays: None,
};
let input = vec![0.5; 1024];
let input_buffer = AudioBuffer::try_with_data(&input).unwrap();
let mut output = vec![0.0; 2 * 1024];
let output_buffer = AudioBuffer::try_with_data_and_settings(
&mut output,
AudioBufferSettings::with_num_channels(2),
)
.unwrap();
assert!(effect.apply(¶ms, &input_buffer, &output_buffer).is_ok());
}
#[test]
fn test_valid_stereo_input() {
let context = Context::default();
let audio_settings = AudioSettings::default();
let hrtf = Hrtf::try_new(&context, &audio_settings, &HrtfSettings::default()).unwrap();
let mut effect = BinauralEffect::try_new(
&context,
&audio_settings,
&BinauralEffectSettings { hrtf: &hrtf },
)
.unwrap();
let params = BinauralEffectParams {
direction: Direction::new(1.0, 0.0, 0.0),
interpolation: HrtfInterpolation::Nearest,
spatial_blend: 1.0,
hrtf: &hrtf,
peak_delays: None,
};
let input = vec![0.5; 2 * 1024];
let input_buffer = AudioBuffer::try_with_data_and_settings(
&input,
AudioBufferSettings::with_num_channels(2),
)
.unwrap();
let mut output = vec![0.0; 2 * 1024];
let output_buffer = AudioBuffer::try_with_data_and_settings(
&mut output,
AudioBufferSettings::with_num_channels(2),
)
.unwrap();
assert!(effect.apply(¶ms, &input_buffer, &output_buffer).is_ok());
}
#[test]
fn test_invalid_input_num_channels() {
let context = Context::default();
let audio_settings = AudioSettings::default();
let hrtf = Hrtf::try_new(&context, &audio_settings, &HrtfSettings::default()).unwrap();
let mut effect = BinauralEffect::try_new(
&context,
&audio_settings,
&BinauralEffectSettings { hrtf: &hrtf },
)
.unwrap();
let params = BinauralEffectParams {
direction: Direction::new(1.0, 0.0, 0.0),
interpolation: HrtfInterpolation::Nearest,
spatial_blend: 1.0,
hrtf: &hrtf,
peak_delays: None,
};
let input = vec![0.5; 4 * 1024];
let input_buffer = AudioBuffer::try_with_data_and_settings(
&input,
AudioBufferSettings::with_num_channels(4),
)
.unwrap();
let mut output = vec![0.0; 2 * 1024];
let output_buffer = AudioBuffer::try_with_data_and_settings(
&mut output,
AudioBufferSettings::with_num_channels(2),
)
.unwrap();
assert_eq!(
effect.apply(¶ms, &input_buffer, &output_buffer),
Err(EffectError::InvalidInputChannels {
expected: ChannelRequirement::Range { min: 1, max: 2 },
actual: 4
})
);
}
#[test]
fn test_invalid_output_num_channels() {
let context = Context::default();
let audio_settings = AudioSettings::default();
let hrtf = Hrtf::try_new(&context, &audio_settings, &HrtfSettings::default()).unwrap();
let mut effect = BinauralEffect::try_new(
&context,
&audio_settings,
&BinauralEffectSettings { hrtf: &hrtf },
)
.unwrap();
let params = BinauralEffectParams {
direction: Direction::new(1.0, 0.0, 0.0),
interpolation: HrtfInterpolation::Nearest,
spatial_blend: 1.0,
hrtf: &hrtf,
peak_delays: None,
};
let input = vec![0.5; 1024];
let input_buffer = AudioBuffer::try_with_data(&input).unwrap();
let mut output = vec![0.0; 4 * 1024];
let output_buffer = AudioBuffer::try_with_data_and_settings(
&mut output,
AudioBufferSettings::with_num_channels(4),
)
.unwrap();
assert_eq!(
effect.apply(¶ms, &input_buffer, &output_buffer),
Err(EffectError::InvalidOutputChannels {
expected: ChannelRequirement::Exactly(2),
actual: 4
})
);
}
}
mod tail {
use super::*;
#[test]
fn test_valid() {
let context = Context::default();
let audio_settings = AudioSettings::default();
let hrtf = Hrtf::try_new(&context, &audio_settings, &HrtfSettings::default()).unwrap();
let effect = BinauralEffect::try_new(
&context,
&audio_settings,
&BinauralEffectSettings { hrtf: &hrtf },
)
.unwrap();
let mut output = vec![0.0; 2 * 1024];
let output_buffer = AudioBuffer::try_with_data_and_settings(
&mut output,
AudioBufferSettings::with_num_channels(2),
)
.unwrap();
assert!(effect.tail(&output_buffer).is_ok());
}
#[test]
fn test_invalid_output_num_channels() {
let context = Context::default();
let audio_settings = AudioSettings::default();
let hrtf = Hrtf::try_new(&context, &audio_settings, &HrtfSettings::default()).unwrap();
let effect = BinauralEffect::try_new(
&context,
&audio_settings,
&BinauralEffectSettings { hrtf: &hrtf },
)
.unwrap();
let mut output = vec![0.0; 4 * 1024];
let output_buffer = AudioBuffer::try_with_data_and_settings(
&mut output,
AudioBufferSettings::with_num_channels(4),
)
.unwrap();
assert_eq!(
effect.tail(&output_buffer),
Err(EffectError::InvalidOutputChannels {
expected: ChannelRequirement::Exactly(2),
actual: 4
})
);
}
}
mod clone {
use super::*;
#[test]
fn test_clone() {
let context = Context::default();
let audio_settings = AudioSettings::default();
let hrtf = Hrtf::try_new(&context, &audio_settings, &HrtfSettings::default()).unwrap();
let effect = BinauralEffect::try_new(
&context,
&audio_settings,
&BinauralEffectSettings { hrtf: &hrtf },
)
.unwrap();
let clone = effect.clone();
assert_eq!(effect.raw_ptr(), clone.raw_ptr());
drop(effect);
assert!(!clone.raw_ptr().is_null());
}
}
}