use std::{fs, sync::LazyLock};
use symphonia::{
core::{
audio::{SampleBuffer, SignalSpec},
codecs::{CODEC_TYPE_NULL, CodecRegistry, Decoder, DecoderOptions},
errors::Error as SymphoniaError,
formats::{FormatOptions, FormatReader},
io::{MediaSourceStream, MediaSourceStreamOptions},
probe::Probe,
},
default::{
codecs::{FlacDecoder, MpaDecoder, PcmDecoder, VorbisDecoder},
formats::{FlacReader, MpaReader, OggReader, WavReader},
},
};
use thiserror::Error;
use crate::media_container::{ContainerFormat, MediaContainer};
pub static SYMPHONIA_CODEC_REGISTRY: LazyLock<CodecRegistry> = LazyLock::new(|| {
let mut codecs = CodecRegistry::new();
codecs.register_all::<FlacDecoder>();
codecs.register_all::<MpaDecoder>();
codecs.register_all::<VorbisDecoder>();
codecs.register_all::<PcmDecoder>();
codecs
});
pub static SYMPHONIA_PROBE: LazyLock<Probe> = LazyLock::new(|| {
let mut probe = Probe::default();
probe.register_all::<FlacReader>();
probe.register_all::<OggReader>();
probe.register_all::<MpaReader>();
probe.register_all::<WavReader>();
probe
});
#[derive(Debug, Error)]
pub enum DecodingError {
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Symphonia(#[from] SymphoniaError),
#[error("Player attempted to play an unsupported container: '{0:?}'")]
UnsupportedContainer(ContainerFormat),
}
pub struct RawDecoder {
pub stream: symphonia::core::formats::Track,
pub format_reader: Box<dyn FormatReader>,
pub decoder: Box<dyn Decoder>,
}
pub struct DecodedPacket {
pub sample_buf: SampleBuffer<f32>,
pub frame_count: usize,
pub spec: SignalSpec,
}
impl RawDecoder {
pub fn from_container(
container: &MediaContainer,
buffer_len: usize,
) -> Result<Self, DecodingError> {
let file = fs::File::open(container.path())?;
let options = MediaSourceStreamOptions { buffer_len };
let mss = MediaSourceStream::new(Box::new(file), options);
let fmt_opts = FormatOptions {
enable_gapless: true,
..Default::default()
};
let format_reader: Box<dyn FormatReader> = match container.container() {
ContainerFormat::Flac => Box::new(FlacReader::try_new(mss, &fmt_opts)?),
ContainerFormat::Mpa => Box::new(MpaReader::try_new(mss, &fmt_opts)?),
ContainerFormat::Ogg => Box::new(OggReader::try_new(mss, &fmt_opts)?),
ContainerFormat::Wav => Box::new(WavReader::try_new(mss, &fmt_opts)?),
other => return Err(DecodingError::UnsupportedContainer(*other)),
};
let track = format_reader
.tracks()
.iter()
.find(|t| t.codec_params.codec != CODEC_TYPE_NULL)
.expect("Playable file does not have any supported codecs");
let dec_opts: DecoderOptions = Default::default();
let decoder = SYMPHONIA_CODEC_REGISTRY.make(&track.codec_params, &dec_opts)?;
Ok(Self {
stream: track.clone(),
format_reader,
decoder,
})
}
pub fn decode_next_packet(&mut self) -> Result<Option<DecodedPacket>, DecodingError> {
loop {
let packet = match self.format_reader.next_packet() {
Ok(packet) => packet,
Err(SymphoniaError::ResetRequired) => {
self.decoder.reset();
continue;
}
Err(SymphoniaError::IoError(err))
if matches!(err.kind(), std::io::ErrorKind::UnexpectedEof) =>
{
return Ok(None);
}
Err(err) => return Err(DecodingError::Symphonia(err)),
};
while !self.format_reader.metadata().is_latest() {
self.format_reader.metadata().pop();
}
if packet.track_id() != self.stream.id {
continue;
}
let decoded = match self.decoder.decode(&packet) {
Ok(decoded) => decoded,
Err(SymphoniaError::DecodeError(_)) => continue,
Err(SymphoniaError::IoError(_)) => continue,
Err(err) => return Err(DecodingError::Symphonia(err)),
};
let frame_count = decoded.frames();
let spec = *decoded.spec();
let mut sample_buf = SampleBuffer::<f32>::new(frame_count as u64, spec);
sample_buf.copy_interleaved_ref(decoded);
return Ok(Some(DecodedPacket {
sample_buf,
frame_count,
spec,
}));
}
}
}