use std::sync::{Arc, atomic::AtomicU64};
use arc_swap::ArcSwapOption;
#[derive(Debug, Clone)]
pub struct AudioSample {
chans: usize,
data: Vec<Vec<f32>>,
}
#[derive(Debug)]
pub struct AudioSampleHandle {
pub sample: ArcSwapOption<AudioSample>,
pub sample_version: AtomicU64,
}
pub struct AudioSampleRef {
pub sample: Arc<AudioSample>,
pub sample_version: AtomicU64,
}
impl AudioSampleHandle {
pub fn invalidate(&self, sample: AudioSample) {
self.sample.store(Some(Arc::new(sample)));
self.sample_version
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
}
impl AudioSample {
pub fn new(chans: usize, data: Vec<Vec<f32>>) -> Self {
Self { chans, data }
}
pub fn data(&self) -> &Vec<Vec<f32>> {
&self.data
}
pub fn chans(&self) -> usize {
self.chans
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum AudioSampleError {
PathNotFound,
FailedDecoding,
FrontendNotFound,
}
#[derive(Clone)]
pub struct AudioSampleFrontend {
handle: Arc<AudioSampleHandle>,
}
impl AudioSampleFrontend {
pub fn new(handle: Arc<AudioSampleHandle>) -> Self {
Self { handle }
}
pub fn load_file(&self, path: &str, chans: usize, sr: u32) -> Result<(), AudioSampleError> {
match decode_with_ffmpeg(path, chans, sr) {
Ok(decoded) => {
self.handle.invalidate(decoded);
Ok(())
}
Err(_) => Err(AudioSampleError::FailedDecoding), }
}
}
use std::{
io::{BufReader, Read},
process::{Command, Stdio},
};
pub fn decode_with_ffmpeg(path: &str, chans: usize, sr: u32) -> std::io::Result<AudioSample> {
let mut child = Command::new("ffmpeg")
.args([
"-i",
path, "-f",
"f32le", "-ac", &chans.to_string(),
"-ar", &sr.to_string(),
"-acodec",
"pcm_f32le",
"pipe:1",
])
.stdout(Stdio::piped())
.stderr(Stdio::null()) .spawn()?;
let stdout = child.stdout.take().unwrap();
let mut reader = BufReader::new(stdout);
let mut per_channel = vec![Vec::new(); chans];
let mut buf = [0u8; 4]; let mut channel_idx = 0;
while reader.read_exact(&mut buf).is_ok() {
let sample = f32::from_le_bytes(buf);
per_channel[channel_idx].push(sample);
channel_idx += 1;
if channel_idx == chans {
channel_idx = 0;
}
}
Ok(AudioSample::new(chans, per_channel))
}