use std::{fs::File, path::Path, time::Duration};
use symphonia::core::{
audio::{AudioBufferRef, SampleBuffer},
codecs::Decoder,
errors::Error as SymphoniaError,
formats::{FormatReader, SeekMode, SeekTo},
io::MediaSourceStream,
probe::Hint,
units::Time,
};
use crate::SourceError;
pub struct Source {
format: Box<dyn FormatReader>,
decoder: Box<dyn Decoder>,
track_id: u32,
pub duration: Duration,
pub sample_rate: f64,
sample_buf: symphonia::core::audio::SampleBuffer<f32>,
i: u32,
buffering: bool,
}
impl Source {
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
let file = Box::new(File::open(path).unwrap());
let mss = MediaSourceStream::new(file, Default::default());
Self::from_mss(mss)
}
pub fn from_mss(mss: MediaSourceStream) -> Result<Self, anyhow::Error> {
let mut hint = Hint::new();
hint.with_extension("mp3");
let mut format = Box::new(symphonia::default::formats::Mp3Reader::try_new(
mss,
&Default::default(),
)?);
let track = format.default_track().unwrap().clone();
let mut decoder =
symphonia::default::get_codecs().make(&track.codec_params, &Default::default())?;
let codec_params = decoder.codec_params();
let duration = {
let time = codec_params
.time_base
.unwrap()
.calc_time(codec_params.n_frames.unwrap());
Duration::from_secs(time.seconds) + Duration::from_secs_f64(time.frac)
};
let audio_buf = get_next_audio_buffer(&mut *format, track.id, &mut *decoder)?;
let spec = *audio_buf.spec();
let sample_buf = {
let duration = audio_buf.capacity() as u64;
let mut sample_buf = SampleBuffer::new(duration, spec);
sample_buf.copy_interleaved_ref(audio_buf);
sample_buf
};
Ok(Self {
format,
decoder,
track_id: track.id,
duration,
sample_rate: spec.rate as f64,
sample_buf,
buffering: false,
i: 0,
})
}
fn decode_next(&mut self) -> Result<(), SourceError> {
let next = get_next_audio_buffer(&mut *self.format, self.track_id, &mut *self.decoder);
match next {
Ok(audio_buf) => {
self.sample_buf.copy_interleaved_ref(audio_buf);
self.i = 0;
self.buffering = false;
Ok(())
}
Err(SymphoniaError::IoError(e)) => match e.kind() {
std::io::ErrorKind::WouldBlock => {
self.sample_buf.clear();
self.buffering = true;
println!("error while decoding next packed {}", e);
Err(SourceError::Buffering)
}
std::io::ErrorKind::UnexpectedEof => Err(SourceError::EndOfStream),
_ => Err(SourceError::Buffering),
},
_ => Err(SourceError::Buffering),
}
}
}
fn get_next_audio_buffer<'a>(
format: &mut dyn FormatReader,
track_id: u32,
decoder: &'a mut dyn Decoder,
) -> Result<AudioBufferRef<'a>, SymphoniaError> {
let packet = loop {
let packet = format.next_packet()?;
if packet.track_id() == track_id {
break packet;
}
};
let audio_buf = decoder.decode(&packet)?;
Ok(audio_buf)
}
impl crate::Source for Source {
fn seek(&mut self, pos: Duration) -> Result<(), SourceError> {
if let Err(_) = self.format.seek(
SeekMode::Coarse,
SeekTo::Time {
time: Time {
seconds: pos.as_secs(),
frac: pos.as_secs_f64().fract(),
},
track_id: None,
},
) {
self.buffering = true;
return Err(SourceError::Buffering);
}
self.decode_next()?;
self.i = 0;
Ok(())
}
fn next(&mut self, buf: &mut [[f32; 2]]) -> Result<(), SourceError> {
for b in buf {
if self.i >= self.sample_buf.len() as u32 {
self.decode_next()?;
}
b[0] = self.sample_buf.samples()[(self.i + 0) as usize];
b[1] = self.sample_buf.samples()[(self.i + 1) as usize];
self.i += 2;
}
Ok(())
}
}