pub use crate::callback::AirAbsorptionCallback;
use crate::context::Context;
use crate::{geometry, Equalizer};
#[derive(Debug, Default)]
pub enum AirAbsorptionModel {
#[default]
Default,
Exponential {
coefficients: [f32; 3],
},
Callback {
callback: AirAbsorptionCallback,
dirty: bool,
},
}
impl From<&AirAbsorptionModel> for audionimbus_sys::IPLAirAbsorptionModel {
fn from(air_absorption_model: &AirAbsorptionModel) -> Self {
let (type_, coefficients, callback, user_data, dirty) = match air_absorption_model {
AirAbsorptionModel::Default => (
audionimbus_sys::IPLAirAbsorptionModelType::IPL_AIRABSORPTIONTYPE_DEFAULT,
<[f32; 3]>::default(),
None,
std::ptr::null_mut(),
bool::default(),
),
AirAbsorptionModel::Exponential { coefficients } => (
audionimbus_sys::IPLAirAbsorptionModelType::IPL_AIRABSORPTIONTYPE_EXPONENTIAL,
*coefficients,
None,
std::ptr::null_mut(),
bool::default(),
),
AirAbsorptionModel::Callback { callback, dirty } => {
let (callback_fn, user_data) = callback.as_raw_parts();
(
audionimbus_sys::IPLAirAbsorptionModelType::IPL_AIRABSORPTIONTYPE_CALLBACK,
<[f32; 3]>::default(),
Some(callback_fn),
user_data,
*dirty,
)
}
};
Self {
type_,
coefficients,
callback,
userData: user_data,
dirty: if dirty {
audionimbus_sys::IPLbool::IPL_TRUE
} else {
audionimbus_sys::IPLbool::IPL_FALSE
},
}
}
}
pub unsafe fn air_absorption(
context: &Context,
source: geometry::Point,
listener: geometry::Point,
model: &AirAbsorptionModel,
) -> Equalizer<3> {
let mut air_absorption = Equalizer([0.0; 3]);
unsafe {
audionimbus_sys::iplAirAbsorptionCalculate(
context.raw_ptr(),
source.into(),
listener.into(),
&mut model.into(),
air_absorption.0.as_mut_ptr(),
);
}
air_absorption
}
#[cfg(test)]
mod tests {
use super::*;
use crate::Point;
#[test]
fn test_default_model() {
let context = Context::default();
let source = Point::new(10.0, 0.0, 0.0);
let listener = Point::new(0.0, 0.0, 0.0);
let model = AirAbsorptionModel::default();
let absorption = unsafe { air_absorption(&context, source, listener, &model) };
for &band in &absorption.0 {
assert!(band > 0.0 && band < 1.0);
}
}
#[test]
fn test_exponential_model() {
let context = Context::default();
let source = Point::new(5.0, 0.0, 0.0);
let listener = Point::new(0.0, 0.0, 0.0);
let model = AirAbsorptionModel::Exponential {
coefficients: [0.01, 0.02, 0.03],
};
let absorption = unsafe { air_absorption(&context, source, listener, &model) };
assert!(absorption.0[2] <= absorption.0[1]);
assert!(absorption.0[1] <= absorption.0[0]);
}
#[test]
fn test_zero_distance() {
let context = Context::default();
let source = Point::new(0.0, 0.0, 0.0);
let listener = Point::new(0.0, 0.0, 0.0);
let model = AirAbsorptionModel::default();
let absorption = unsafe { air_absorption(&context, source, listener, &model) };
for &band in &absorption.0 {
assert_eq!(band, 1.0);
}
}
}