use std::path::Path;
use crate::sofa::{
Hrtf, InterpolatedFilter, Lookup, Neighborhood, get_filter_nointerp, interpolate, normalize,
resample, validate,
};
const DEFAULT_NORMALIZED: bool = true;
const DEFAULT_SAMPLE_RATE: f32 = 48000.0;
const DEFAULT_NEIGHBOR_ANGLE_STEP: f32 = 0.5;
const DEFAULT_NEIGHBOR_RADIUS_STEP: f32 = 0.01;
#[derive(thiserror::Error, Debug)]
#[non_exhaustive]
pub enum Error {
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error: {0}")]
Parse(#[from] crate::sofa::Error),
#[error("Invalid format")]
InvalidFormat,
#[error("Failed to build spatial lookup")]
LookupBuildFailed,
#[error("Resampling failed: {0}")]
ResampleFailed(String),
}
#[derive(Clone, Debug)]
pub struct OpenOptions {
sample_rate: f32,
neighbor_angle_step: f32,
neighbor_radius_step: f32,
normalized: bool,
}
impl OpenOptions {
pub fn new() -> Self {
Default::default()
}
pub fn sample_rate(&mut self, sample_rate: f32) -> &mut Self {
self.sample_rate = sample_rate;
self
}
pub fn neighbor_angle_step(&mut self, neighbor_angle_step: f32) -> &mut Self {
self.neighbor_angle_step = neighbor_angle_step;
self
}
pub fn neighbor_radius_step(&mut self, neighbor_radius_step: f32) -> &mut Self {
self.neighbor_radius_step = neighbor_radius_step;
self
}
pub fn normalized(&mut self, normalized: bool) -> &mut Self {
self.normalized = normalized;
self
}
pub fn open<P: AsRef<Path>>(&self, path: P) -> Result<Sofar, Error> {
let data = std::fs::read(path)?;
self.open_data(&data)
}
pub fn open_data<B: AsRef<[u8]>>(&self, bytes: B) -> Result<Sofar, Error> {
let mut hrtf = Hrtf::from_bytes(bytes.as_ref())?;
hrtf.convert_to_cartesian();
if let Err(e) = validate(&hrtf) {
log::warn!("SOFA validation: {}", e);
}
let original_rate = hrtf.sample_rate();
if (original_rate - self.sample_rate).abs() > 0.1 {
resample(&mut hrtf, self.sample_rate).map_err(Error::ResampleFailed)?;
}
if self.normalized {
let _ = normalize(&mut hrtf);
}
let lookup = Lookup::new(&hrtf).ok_or(Error::LookupBuildFailed)?;
let neighborhood = Neighborhood::new(
&hrtf,
&lookup,
self.neighbor_angle_step,
self.neighbor_radius_step,
);
let filter_len = hrtf.filter_len();
Ok(Sofar {
hrtf,
lookup,
neighborhood,
filter_len,
})
}
}
impl Default for OpenOptions {
fn default() -> Self {
OpenOptions {
sample_rate: DEFAULT_SAMPLE_RATE,
neighbor_angle_step: DEFAULT_NEIGHBOR_ANGLE_STEP,
neighbor_radius_step: DEFAULT_NEIGHBOR_RADIUS_STEP,
normalized: DEFAULT_NORMALIZED,
}
}
}
pub use crate::filter::Filter;
pub struct Sofar {
hrtf: Hrtf,
lookup: Lookup,
neighborhood: Neighborhood,
filter_len: usize,
}
impl Sofar {
pub fn open<P: AsRef<Path>>(path: P) -> Result<Sofar, Error> {
OpenOptions::new().open(path)
}
pub fn open_data<B: AsRef<[u8]>>(bytes: B) -> Result<Sofar, Error> {
OpenOptions::new().open_data(bytes)
}
pub fn filter_len(&self) -> usize {
self.filter_len
}
pub fn filter(&self, x: f32, y: f32, z: f32, filter: &mut Filter) {
let position = [x, y, z];
if let Some(nearest_idx) = self.lookup.find(&position)
&& let Some(interp) =
interpolate(&self.hrtf, &self.neighborhood, nearest_idx, &position)
{
self.fill_filter(filter, &interp);
return;
}
filter.left.iter_mut().for_each(|s| *s = 0.0);
filter.right.iter_mut().for_each(|s| *s = 0.0);
filter.ldelay = 0.0;
filter.rdelay = 0.0;
}
pub fn filter_nointerp(&self, x: f32, y: f32, z: f32, filter: &mut Filter) {
let position = [x, y, z];
if let Some(nearest_idx) = self.lookup.find(&position)
&& let Some(interp) = get_filter_nointerp(&self.hrtf, nearest_idx)
{
self.fill_filter(filter, &interp);
return;
}
filter.left.iter_mut().for_each(|s| *s = 0.0);
filter.right.iter_mut().for_each(|s| *s = 0.0);
filter.ldelay = 0.0;
filter.rdelay = 0.0;
}
fn fill_filter(&self, filter: &mut Filter, interp: &InterpolatedFilter) {
let copy_len = interp.left.len().min(filter.left.len());
filter.left[..copy_len].copy_from_slice(&interp.left[..copy_len]);
if copy_len < filter.left.len() {
filter.left[copy_len..].iter_mut().for_each(|s| *s = 0.0);
}
let copy_len = interp.right.len().min(filter.right.len());
filter.right[..copy_len].copy_from_slice(&interp.right[..copy_len]);
if copy_len < filter.right.len() {
filter.right[copy_len..].iter_mut().for_each(|s| *s = 0.0);
}
let sample_rate = self.hrtf.sample_rate();
filter.ldelay = interp.delay_left / sample_rate;
filter.rdelay = interp.delay_right / sample_rate;
}
pub fn hrtf(&self) -> &Hrtf {
&self.hrtf
}
pub fn lookup(&self) -> &Lookup {
&self.lookup
}
pub fn sample_rate(&self) -> f32 {
self.hrtf.sample_rate()
}
pub fn num_measurements(&self) -> u32 {
self.hrtf.m()
}
}