use std::{
io::{BufReader, Read},
process::{Command, Stdio},
sync::Arc,
};
#[derive(Clone, Default, Debug)]
pub struct ExternalBuffer {
pub data: Arc<[f32]>,
pub num_channels: usize,
}
impl ExternalBuffer {
#[inline(always)]
pub fn channel(&self, idx: usize) -> &[f32] {
let stride = self.data.len() / self.num_channels;
let start = idx * stride;
&self.data[start..start + stride]
}
}
pub fn decode_with_ffmpeg(path: &str, chans: usize, sr: u32) -> std::io::Result<ExternalBuffer> {
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;
}
}
let data: Arc<[f32]> = per_channel.into_iter().flatten().collect();
Ok(ExternalBuffer {
data,
num_channels: chans,
})
}
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum AudioSampleError {
PathNotFound,
FailedDecoding,
FrontendNotFound,
FailedToSendToRuntime,
}