use crate::callback::DirectivityCallback;
use crate::context::Context;
use crate::geometry;
#[derive(Debug)]
pub enum Directivity {
WeightedDipole {
weight: f32,
power: f32,
},
Callback(DirectivityCallback),
}
impl Default for Directivity {
fn default() -> Self {
Self::WeightedDipole {
weight: 0.5,
power: 0.5,
}
}
}
impl From<&Directivity> for audionimbus_sys::IPLDirectivity {
fn from(directivity: &Directivity) -> Self {
let (dipole_weight, dipole_power, callback, user_data) = match directivity {
Directivity::WeightedDipole { weight, power } => {
(*weight, *power, None, std::ptr::null_mut())
}
Directivity::Callback(callback) => {
let (callback_fn, user_data) = callback.as_raw_parts();
(f32::default(), f32::default(), Some(callback_fn), user_data)
}
};
Self {
dipoleWeight: dipole_weight,
dipolePower: dipole_power,
callback,
userData: user_data,
}
}
}
pub fn directivity_attenuation(
context: &Context,
source: geometry::CoordinateSystem,
listener: geometry::Point,
directivity: &Directivity,
) -> f32 {
unsafe {
audionimbus_sys::iplDirectivityCalculate(
context.raw_ptr(),
source.into(),
listener.into(),
&mut directivity.into(),
)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::{CoordinateSystem, Point};
#[test]
fn test_default_model() {
let context = Context::default();
let source = CoordinateSystem::default();
let listener = Point::new(0.0, 0.0, 1.0);
let attenuation =
directivity_attenuation(&context, source, listener, &Directivity::default());
assert_eq!(attenuation, 1.0);
let listener = Point::new(0.0, 1.0, 0.0);
let attenuation =
directivity_attenuation(&context, source, listener, &Directivity::default());
assert_eq!(attenuation, 0.70710677);
}
#[test]
fn test_weighted_dipole() {
let context = Context::default();
let source = CoordinateSystem::default();
let listener_front = Point::new(0.0, 0.0, 1.0);
let listener_behind = Point::new(0.0, 0.0, -1.0);
let listener_side = Point::new(1.0, 0.0, 0.0);
let directivity = Directivity::WeightedDipole {
weight: 0.5, power: 1.0,
};
let attenuation_front =
directivity_attenuation(&context, source, listener_front, &directivity);
let attenuation_behind =
directivity_attenuation(&context, source, listener_behind, &directivity);
let attenuation_side =
directivity_attenuation(&context, source, listener_side, &directivity);
assert!(attenuation_side < attenuation_front);
assert_eq!(attenuation_behind, 0.0);
}
#[test]
fn test_omnidirectional() {
let context = Context::default();
let source = CoordinateSystem::default();
let directions = [
Point::new(1.0, 0.0, 0.0),
Point::new(0.0, 1.0, 0.0),
Point::new(0.0, 0.0, 1.0),
Point::new(-1.0, 0.0, 0.0),
];
let directivity = Directivity::WeightedDipole {
weight: 0.0, power: 1.0,
};
let mut attenuations = Vec::new();
for &listener in &directions {
let attn = directivity_attenuation(&context, source, listener, &directivity);
attenuations.push(attn);
}
let first = attenuations[0];
for &attenuation in &attenuations {
assert!((attenuation - first).abs() < 0.01);
}
}
#[test]
fn test_callback_model() {
let context = Context::default();
let source = CoordinateSystem::default();
let listener_front = Point::new(0.0, 0.0, 1.0);
let directivity = Directivity::Callback(DirectivityCallback::new(|_direction| 0.5));
let attenuation = directivity_attenuation(&context, source, listener_front, &directivity);
assert_eq!(attenuation, 0.5);
}
}