use crate::saf_raw;
use libc::c_void;
use std::ptr::{addr_of_mut, null, null_mut};
const SAMP_RATE: usize = 44100;
const NUM_OUT_CHANNELS: usize = 2;
pub const FRAME_SIZE: usize = 128;
const RAD_TO_DEGREE: f32 = 180.0 / std::f32::consts::PI;
pub trait Binauraliser {
fn process_frame(&mut self, buffers: &[(BufferMetadata, &[f32])]) -> (Vec<f32>, Vec<f32>);
fn process(&mut self, buffers: &[(BufferMetadata, &[f32])]) -> (Vec<f32>, Vec<f32>) {
let len = buffers
.iter()
.map(|(_tag, samples)| samples.len())
.max()
.unwrap_or(0);
for (_tag, samples) in buffers.iter() {
debug_assert_eq!(0, samples.len() % FRAME_SIZE);
}
let mut final_left_vec = Vec::with_capacity(len);
let mut final_right_vec = Vec::with_capacity(len);
for i in (0..len).step_by(FRAME_SIZE) {
let buf_lo = i;
let buf_hi = i + FRAME_SIZE;
let frame = buffers
.iter()
.map(|(metadata, samples)| (*metadata, &samples[buf_lo..buf_hi]))
.collect::<Vec<_>>();
let (mut left_vec, mut right_vec) = self.process_frame(&frame);
final_left_vec.append(&mut left_vec);
final_right_vec.append(&mut right_vec);
}
(final_left_vec, final_right_vec)
}
}
#[derive(Clone, Copy, Debug)]
pub struct BufferMetadata {
pub azimuth: f32,
pub elevation: f32,
pub range: f32,
pub gain: f32,
}
pub struct BinauraliserNF {
h_bin: *mut c_void,
}
impl BinauraliserNF {
pub fn new() -> Self {
let mut h_bin = null_mut();
unsafe {
saf_raw::binauraliserNF_create(addr_of_mut!(h_bin));
saf_raw::binauraliserNF_init(h_bin, SAMP_RATE as i32);
saf_raw::binauraliser_setUseDefaultHRIRsflag(h_bin, 1);
}
BinauraliserNF { h_bin }
}
}
impl Binauraliser for BinauraliserNF {
fn process_frame(&mut self, buffers: &[(BufferMetadata, &[f32])]) -> (Vec<f32>, Vec<f32>) {
for (_, b) in buffers {
debug_assert_eq!(b.len(), FRAME_SIZE);
}
let num_channels: usize = buffers.len();
let mut raw_input_ptrs: Vec<*const f32> = vec![null(); num_channels];
let mut output_vec_1 = vec![0.0; FRAME_SIZE];
let mut output_vec_2 = vec![0.0; FRAME_SIZE];
let mut raw_output_ptrs: Vec<*mut f32> = vec![null_mut(); NUM_OUT_CHANNELS];
raw_output_ptrs[0] = output_vec_1.as_mut_ptr();
raw_output_ptrs[1] = output_vec_2.as_mut_ptr();
unsafe {
saf_raw::binauraliser_setNumSources(self.h_bin, num_channels as i32);
for (i, &(metadata, audio_data)) in buffers.iter().enumerate() {
raw_input_ptrs[i] = audio_data.as_ptr();
saf_raw::binauraliserNF_setSourceDist_m(self.h_bin, i as i32, metadata.range);
saf_raw::binauraliser_setSourceAzi_deg(
self.h_bin,
i as i32,
metadata.azimuth * RAD_TO_DEGREE,
);
saf_raw::binauraliser_setSourceElev_deg(
self.h_bin,
i as i32,
metadata.elevation * RAD_TO_DEGREE,
);
saf_raw::binauraliser_setSourceGain(self.h_bin, i as i32, metadata.gain);
}
saf_raw::binauraliserNF_initCodec(self.h_bin);
saf_raw::binauraliserNF_process(
self.h_bin,
raw_input_ptrs.as_ptr(), raw_output_ptrs.as_ptr(), num_channels as i32, NUM_OUT_CHANNELS as i32, FRAME_SIZE as i32, );
}
(output_vec_1, output_vec_2)
}
}
impl Default for BinauraliserNF {
fn default() -> Self {
Self::new()
}
}
impl Drop for BinauraliserNF {
fn drop(&mut self) {
unsafe {
saf_raw::binauraliserNF_destroy(addr_of_mut!(self.h_bin));
}
}
}
#[allow(dead_code)]
struct DummyBinauraliser;
impl Binauraliser for DummyBinauraliser {
fn process_frame(&mut self, buffers: &[(BufferMetadata, &[f32])]) -> (Vec<f32>, Vec<f32>) {
for (_, b) in buffers {
debug_assert_eq!(b.len(), FRAME_SIZE);
}
assert!(buffers.len() == 2);
(buffers[0].1.to_vec(), buffers[1].1.to_vec())
}
}
#[cfg(test)]
mod tests {
use super::*;
use hound::{SampleFormat, WavSpec, WavWriter};
use std::f32::consts::PI;
const MOCK_METADATA: BufferMetadata = BufferMetadata {
azimuth: 0.0,
elevation: 0.0,
range: 1.0,
gain: 1.0,
};
const LEFT_METADATA: BufferMetadata = BufferMetadata {
azimuth: 90.0,
elevation: 10.0,
range: 1.0,
gain: 1.0,
};
const RIGHT_METADATA: BufferMetadata = BufferMetadata {
azimuth: 90.0,
elevation: 0.0,
range: 1.0,
gain: 1.0,
};
const C: f32 = 261.61;
const G: f32 = 392.00;
fn create_sine_wave(len: usize, note: f32) -> Vec<f32> {
let snapped_len = len.div_ceil(FRAME_SIZE) * FRAME_SIZE;
(0..snapped_len)
.map(|x| (x % 44100) as f32 / 44100.0)
.map(|t| (t * note * 2.0 * PI).sin() * (i16::MAX as f32))
.collect()
}
#[allow(unused)]
fn write_stereo_output(left_samps: Vec<f32>, right_samps: Vec<f32>, out_file: &'static str) {
let spec = WavSpec {
channels: 2,
sample_rate: 44100,
bits_per_sample: 16,
sample_format: SampleFormat::Int,
};
let mut writer = WavWriter::create(out_file, spec).unwrap();
for (left, right) in std::iter::zip(left_samps, right_samps) {
writer.write_sample(left as i16).unwrap();
writer.write_sample(right as i16).unwrap();
}
writer.finalize().unwrap();
}
#[test]
fn test_mono_single_frame() {
let mut binauraliser_nf = BinauraliserNF::new();
let c_note_vec: Vec<f32> = create_sine_wave(FRAME_SIZE, C);
let frame_slice = [(MOCK_METADATA, &c_note_vec[0..FRAME_SIZE])];
let (left_samps, right_samps) = binauraliser_nf.process_frame(frame_slice.as_ref());
assert!(left_samps.into_iter().all(|x| x != 0.0));
assert!(right_samps.into_iter().all(|x| x != 0.0));
}
#[test]
fn test_stereo_single_frame() {
let mut binauraliser_nf = BinauraliserNF::new();
let c_note_vec: Vec<f32> = create_sine_wave(FRAME_SIZE, C);
let g_note_vec: Vec<f32> = create_sine_wave(FRAME_SIZE, G);
let frame_slice = [
(LEFT_METADATA, c_note_vec.as_slice()),
(RIGHT_METADATA, g_note_vec.as_slice()),
];
let (left_samps, right_samps) = binauraliser_nf.process_frame(frame_slice.as_ref());
assert!(left_samps.into_iter().all(|x| x != 0.0));
assert!(right_samps.into_iter().all(|x| x != 0.0));
}
#[test]
fn test_stereo_multi_frame() {
let mut binauraliser_nf = BinauraliserNF::new();
const THREE_SEC: usize = SAMP_RATE * 3;
let c_note_vec: Vec<f32> = create_sine_wave(THREE_SEC, C);
let g_note_vec: Vec<f32> = create_sine_wave(THREE_SEC, G);
assert_eq!(0, c_note_vec.len() % FRAME_SIZE);
let frame_slice = [
(LEFT_METADATA, c_note_vec.as_slice()),
(RIGHT_METADATA, g_note_vec.as_slice()),
];
let (left_samps, right_samps) = binauraliser_nf.process(frame_slice.as_ref());
assert!(left_samps.clone().into_iter().all(|x| x != 0.0));
assert!(right_samps.clone().into_iter().all(|x| x != 0.0));
}
}