use crate::audio_buffer::Sample;
use crate::context::Context;
use crate::error::{to_option_error, SteamAudioError};
#[derive(Debug)]
pub struct ImpulseResponse(audionimbus_sys::IPLImpulseResponse);
impl ImpulseResponse {
pub fn try_new(
context: &Context,
impulse_response_settings: &ImpulseResponseSettings,
) -> Result<Self, SteamAudioError> {
let mut impulse_response = Self(std::ptr::null_mut());
let status = unsafe {
audionimbus_sys::iplImpulseResponseCreate(
context.raw_ptr(),
&mut audionimbus_sys::IPLImpulseResponseSettings::from(impulse_response_settings),
impulse_response.raw_ptr_mut(),
)
};
if let Some(error) = to_option_error(status) {
return Err(error);
}
Ok(impulse_response)
}
pub fn num_channels(&self) -> u32 {
unsafe { audionimbus_sys::iplImpulseResponseGetNumChannels(self.raw_ptr()) as u32 }
}
pub fn num_samples(&self) -> u32 {
unsafe { audionimbus_sys::iplImpulseResponseGetNumSamples(self.raw_ptr()) as u32 }
}
pub fn data(&self) -> &[Sample] {
let ptr = unsafe { audionimbus_sys::iplImpulseResponseGetData(self.raw_ptr()) };
let len = self.num_channels() * self.num_samples();
unsafe { std::slice::from_raw_parts(ptr, len as usize) }
}
pub fn channel(&self, channel_index: u32) -> Result<&[Sample], ImpulseResponseError> {
let num_channels = self.num_channels();
if channel_index >= num_channels {
return Err(ImpulseResponseError::ChannelIndexOutOfBounds {
channel_index,
num_channels,
});
}
let ptr = unsafe {
audionimbus_sys::iplImpulseResponseGetChannel(self.raw_ptr(), channel_index as i32)
};
let len = self.num_samples();
let data = unsafe { std::slice::from_raw_parts(ptr, len as usize) };
Ok(data)
}
pub fn reset(&mut self) {
unsafe { audionimbus_sys::iplImpulseResponseReset(self.raw_ptr()) }
}
pub fn copy_into(&self, dst: &mut Self) {
unsafe { audionimbus_sys::iplImpulseResponseCopy(self.raw_ptr(), dst.raw_ptr()) }
}
pub fn swap(&mut self, other: &mut Self) {
unsafe { audionimbus_sys::iplImpulseResponseSwap(self.raw_ptr(), other.raw_ptr()) }
}
pub fn add(&mut self, other: &Self) {
unsafe {
audionimbus_sys::iplImpulseResponseAdd(self.raw_ptr(), other.raw_ptr(), self.raw_ptr());
}
}
pub fn scale(&mut self, scalar: f32) {
unsafe { audionimbus_sys::iplImpulseResponseScale(self.raw_ptr(), scalar, self.raw_ptr()) }
}
pub const fn raw_ptr(&self) -> audionimbus_sys::IPLImpulseResponse {
self.0
}
pub const fn raw_ptr_mut(&mut self) -> &mut audionimbus_sys::IPLImpulseResponse {
&mut self.0
}
}
impl Drop for ImpulseResponse {
fn drop(&mut self) {
unsafe { audionimbus_sys::iplImpulseResponseRelease(&raw mut self.0) }
}
}
unsafe impl Send for ImpulseResponse {}
unsafe impl Sync for ImpulseResponse {}
impl Clone for ImpulseResponse {
fn clone(&self) -> Self {
Self(unsafe { audionimbus_sys::iplImpulseResponseRetain(self.0) })
}
}
#[derive(Debug)]
pub struct ImpulseResponseSettings {
pub duration: f32,
pub order: u32,
pub sampling_rate: u32,
}
impl From<&ImpulseResponseSettings> for audionimbus_sys::IPLImpulseResponseSettings {
fn from(settings: &ImpulseResponseSettings) -> Self {
Self {
duration: settings.duration,
order: settings.order as i32,
samplingRate: settings.sampling_rate as i32,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub enum ImpulseResponseError {
ChannelIndexOutOfBounds {
channel_index: u32,
num_channels: u32,
},
}
impl std::error::Error for ImpulseResponseError {}
impl std::fmt::Display for ImpulseResponseError {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
match self {
Self::ChannelIndexOutOfBounds {
channel_index,
num_channels,
} => write!(
f,
"channel index {channel_index} out of bounds (num_channels: {num_channels})"
),
}
}
}
pub fn add_impulse_responses(
in1: &ImpulseResponse,
in2: &ImpulseResponse,
out: &mut ImpulseResponse,
) {
unsafe { audionimbus_sys::iplImpulseResponseAdd(in1.raw_ptr(), in2.raw_ptr(), out.raw_ptr()) }
}
pub fn scale_impulse_response(
impulse_response: &ImpulseResponse,
scalar: f32,
out: &mut ImpulseResponse,
) {
unsafe {
audionimbus_sys::iplImpulseResponseScale(impulse_response.raw_ptr(), scalar, out.raw_ptr());
}
}
pub fn scale_accum_impulse_response(
impulse_response: &ImpulseResponse,
scalar: f32,
out: &mut ImpulseResponse,
) {
unsafe {
audionimbus_sys::iplImpulseResponseScaleAccum(
impulse_response.raw_ptr(),
scalar,
out.raw_ptr(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_try_new_impulse_response() {
let context = Context::default();
let settings = ImpulseResponseSettings {
duration: 1.0,
order: 1,
sampling_rate: 48000,
};
let impulse_response = ImpulseResponse::try_new(&context, &settings).unwrap();
assert_eq!(impulse_response.num_samples(), 48000);
assert_eq!(impulse_response.num_channels(), 4);
let data = impulse_response.data();
assert_eq!(
data.len() as u32,
impulse_response.num_channels() * impulse_response.num_samples()
);
}
#[test]
fn test_impulse_response_channel() {
let context = Context::default();
let settings = ImpulseResponseSettings {
duration: 0.1,
order: 1,
sampling_rate: 48000,
};
let impulse_response = ImpulseResponse::try_new(&context, &settings).unwrap();
let channel = impulse_response.channel(0).unwrap();
assert_eq!(channel.len() as u32, impulse_response.num_samples());
}
#[test]
fn test_impulse_response_channel_out_of_bounds() {
let context = Context::default();
let settings = ImpulseResponseSettings {
duration: 0.1,
order: 0,
sampling_rate: 48000,
};
let impulse_response = ImpulseResponse::try_new(&context, &settings).unwrap();
assert_eq!(
impulse_response.channel(10),
Err(ImpulseResponseError::ChannelIndexOutOfBounds {
channel_index: 10,
num_channels: 1,
})
);
}
#[test]
fn test_impulse_response_reset() {
let context = Context::default();
let settings = ImpulseResponseSettings {
duration: 0.1,
order: 0,
sampling_rate: 48000,
};
let mut impulse_response = ImpulseResponse::try_new(&context, &settings).unwrap();
impulse_response.reset();
let data = impulse_response.data();
assert!(data.iter().all(|&x| x == 0.0));
}
#[test]
fn test_impulse_response_scale() {
let context = Context::default();
let settings = ImpulseResponseSettings {
duration: 0.1,
order: 0,
sampling_rate: 48000,
};
let mut impulse_response = ImpulseResponse::try_new(&context, &settings).unwrap();
impulse_response.scale(2.0);
let data = impulse_response.data();
assert!(data.iter().all(|&x| x == 0.0));
}
#[test]
fn test_clone() {
let context = Context::default();
let settings = ImpulseResponseSettings {
duration: 1.0,
order: 1,
sampling_rate: 48000,
};
let impulse_response = ImpulseResponse::try_new(&context, &settings).unwrap();
let clone = impulse_response.clone();
assert_eq!(impulse_response.raw_ptr(), clone.raw_ptr());
drop(impulse_response);
assert!(!clone.raw_ptr().is_null());
}
}