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()
}
pub fn reverb<const SR: usize>(
audio: &[f32],
channels: usize,
room_size: f32,
damping: f32,
wet: f32,
) -> Vec<f32> {
if audio.is_empty() || channels == 0 {
return Vec::new();
}
let mut output = vec![0.0; audio.len()];
let mut delay_lines = vec![vec![0.0; (SR as f32 * 0.1) as usize]; 8]; let mut delay_pos = vec![0; 8];
let delay_lengths = [
(SR as f32 * 0.0297) as usize,
(SR as f32 * 0.0371) as usize,
(SR as f32 * 0.0411) as usize,
(SR as f32 * 0.0437) as usize,
(SR as f32 * 0.005) as usize,
(SR as f32 * 0.0157) as usize,
(SR as f32 * 0.0201) as usize,
(SR as f32 * 0.0263) as usize,
];
for i in 0..audio.len() {
let sample = audio[i];
let mut reverb_sample = 0.0;
for j in 0..8 {
let delayed = delay_lines[j][delay_pos[j]];
reverb_sample += delayed * 0.125; delay_lines[j][delay_pos[j]] = sample + delayed * damping;
delay_pos[j] = (delay_pos[j] + 1) % delay_lengths[j];
}
output[i] = sample * (1.0 - wet) + reverb_sample * wet * room_size;
}
output
}