use {
crate::OperationError,
rodio::{
Decoder, Source,
buffer::SamplesBuffer,
source::{Spatial, UniformSourceIterator},
},
std::{io::Cursor, path::Path},
tokio::fs::read,
};
pub async fn load_audio<const SR: usize, P>(
audio_path: P,
mono: bool,
) -> Result<(Vec<f32>, usize), OperationError>
where
P: AsRef<Path>,
{
let file = Cursor::new(read(audio_path).await?);
let decoder = Decoder::new(file)?;
let channels = if mono { 1 } else { decoder.channels() } as usize;
let samples = UniformSourceIterator::new(decoder, channels as _, SR as _).collect::<Vec<f32>>();
if samples.is_empty() {
return Err(OperationError::InputInvalid("Audio is empty.".to_owned()));
}
Ok((samples, channels))
}
pub fn resample<const SSR: usize, const TSR: usize>(
samples: &[f32],
src_channels: usize,
tgt_channels: usize,
) -> Vec<f32> {
if samples.is_empty() || src_channels == 0 || tgt_channels == 0 {
return Vec::new();
}
UniformSourceIterator::new(
SamplesBuffer::new(src_channels as _, SSR as _, samples),
tgt_channels as _,
TSR as _,
)
.collect()
}
pub fn spatial_audio<const SR: usize>(
audio: &[f32],
channels: usize,
emitter_position: [f32; 3],
left_ear: [f32; 3],
right_ear: [f32; 3],
) -> Vec<f32> {
Spatial::new(
SamplesBuffer::new(channels as _, SR as _, audio),
emitter_position,
left_ear,
right_ear,
)
.collect()
}