use std::{
io::{BufReader, Read, Seek},
marker::PhantomData,
sync::Arc,
time::Duration,
};
#[allow(unused_imports)]
use std::io::SeekFrom;
use crate::{
common::{assert_error_traits, ChannelCount, SampleRate},
math::nz,
source::{SeekError, Source},
Sample,
};
pub mod builder;
pub use builder::{DecoderBuilder, Settings};
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
mod flac;
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
mod mp3;
#[cfg(feature = "symphonia")]
mod read_seek_source;
#[cfg(feature = "symphonia")]
pub mod symphonia;
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
mod vorbis;
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
mod wav;
pub struct Decoder<R: Read + Seek>(DecoderImpl<R>);
pub struct LoopedDecoder<R: Read + Seek> {
inner: Option<DecoderImpl<R>>,
settings: Settings,
}
#[allow(clippy::large_enum_variant)]
enum DecoderImpl<R: Read + Seek> {
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
Wav(wav::WavDecoder<R>),
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
Vorbis(vorbis::VorbisDecoder<R>),
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
Flac(flac::FlacDecoder<R>),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
Mp3(mp3::Mp3Decoder<R>),
#[cfg(feature = "symphonia")]
Symphonia(symphonia::SymphoniaDecoder, PhantomData<R>),
#[allow(dead_code)]
None(Unreachable, PhantomData<R>),
}
enum Unreachable {}
impl<R: Read + Seek> DecoderImpl<R> {
#[inline]
fn next(&mut self) -> Option<Sample> {
match self {
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.next(),
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.next(),
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.next(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.next(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source, PhantomData) => source.next(),
DecoderImpl::None(_, _) => unreachable!(),
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.size_hint(),
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.size_hint(),
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.size_hint(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.size_hint(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source, PhantomData) => source.size_hint(),
DecoderImpl::None(_, _) => unreachable!(),
}
}
#[inline]
fn current_span_len(&self) -> Option<usize> {
match self {
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.current_span_len(),
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.current_span_len(),
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.current_span_len(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.current_span_len(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source, PhantomData) => source.current_span_len(),
DecoderImpl::None(_, _) => unreachable!(),
}
}
#[inline]
fn channels(&self) -> ChannelCount {
match self {
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.channels(),
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.channels(),
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.channels(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.channels(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source, PhantomData) => source.channels(),
DecoderImpl::None(_, _) => unreachable!(),
}
}
#[inline]
fn sample_rate(&self) -> SampleRate {
match self {
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.sample_rate(),
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.sample_rate(),
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.sample_rate(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.sample_rate(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source, PhantomData) => source.sample_rate(),
DecoderImpl::None(_, _) => unreachable!(),
}
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
match self {
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.total_duration(),
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.total_duration(),
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.total_duration(),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.total_duration(),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source, PhantomData) => source.total_duration(),
DecoderImpl::None(_, _) => unreachable!(),
}
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
match self {
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => source.try_seek(pos),
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => source.try_seek(pos),
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => source.try_seek(pos),
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => source.try_seek(pos),
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source, PhantomData) => source.try_seek(pos),
DecoderImpl::None(_, _) => unreachable!(),
}
}
}
impl TryFrom<std::fs::File> for Decoder<BufReader<std::fs::File>> {
type Error = DecoderError;
fn try_from(file: std::fs::File) -> Result<Self, Self::Error> {
let len = file
.metadata()
.map_err(|e| Self::Error::IoError(e.to_string()))?
.len();
Self::builder()
.with_data(BufReader::new(file))
.with_byte_len(len)
.with_seekable(true)
.build()
}
}
impl<R> TryFrom<BufReader<R>> for Decoder<BufReader<R>>
where
R: Read + Seek + Send + Sync + 'static,
{
type Error = DecoderError;
fn try_from(data: BufReader<R>) -> Result<Self, Self::Error> {
Self::new(data)
}
}
impl<T> TryFrom<std::io::Cursor<T>> for Decoder<std::io::Cursor<T>>
where
T: AsRef<[u8]> + Send + Sync + 'static,
{
type Error = DecoderError;
fn try_from(data: std::io::Cursor<T>) -> Result<Self, Self::Error> {
Self::new(data)
}
}
impl<R: Read + Seek + Send + Sync + 'static> Decoder<R> {
pub fn builder() -> DecoderBuilder<R> {
DecoderBuilder::new()
}
pub fn new(data: R) -> Result<Self, DecoderError> {
DecoderBuilder::new().with_data(data).build()
}
pub fn new_looped(data: R) -> Result<LoopedDecoder<R>, DecoderError> {
DecoderBuilder::new().with_data(data).build_looped()
}
#[cfg(any(feature = "hound", feature = "symphonia-wav"))]
pub fn new_wav(data: R) -> Result<Self, DecoderError> {
DecoderBuilder::new()
.with_data(data)
.with_hint("wav")
.build()
}
#[cfg(any(feature = "claxon", feature = "symphonia-flac"))]
pub fn new_flac(data: R) -> Result<Self, DecoderError> {
DecoderBuilder::new()
.with_data(data)
.with_hint("flac")
.build()
}
#[cfg(any(feature = "lewton", feature = "symphonia-vorbis"))]
pub fn new_vorbis(data: R) -> Result<Self, DecoderError> {
DecoderBuilder::new()
.with_data(data)
.with_hint("ogg")
.build()
}
#[cfg(any(feature = "minimp3", feature = "symphonia-mp3"))]
pub fn new_mp3(data: R) -> Result<Self, DecoderError> {
DecoderBuilder::new()
.with_data(data)
.with_hint("mp3")
.build()
}
#[cfg(feature = "symphonia-aac")]
pub fn new_aac(data: R) -> Result<Self, DecoderError> {
DecoderBuilder::new()
.with_data(data)
.with_hint("aac")
.build()
}
#[cfg(feature = "symphonia-isomp4")]
pub fn new_mp4(data: R) -> Result<Self, DecoderError> {
DecoderBuilder::new()
.with_data(data)
.with_mime_type("audio/mp4")
.build()
}
}
impl<R> Iterator for Decoder<R>
where
R: Read + Seek,
{
type Item = Sample;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
self.0.size_hint()
}
}
impl<R> Source for Decoder<R>
where
R: Read + Seek,
{
#[inline]
fn current_span_len(&self) -> Option<usize> {
self.0.current_span_len()
}
#[inline]
fn channels(&self) -> ChannelCount {
self.0.channels()
}
fn sample_rate(&self) -> SampleRate {
self.0.sample_rate()
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
self.0.total_duration()
}
#[inline]
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
self.0.try_seek(pos)
}
}
impl<R> Iterator for LoopedDecoder<R>
where
R: Read + Seek,
{
type Item = Sample;
fn next(&mut self) -> Option<Self::Item> {
if let Some(inner) = &mut self.inner {
if let Some(sample) = inner.next() {
return Some(sample);
}
let decoder = self.inner.take()?;
let (new_decoder, sample) = match decoder {
#[cfg(all(feature = "hound", not(feature = "symphonia-wav")))]
DecoderImpl::Wav(source) => {
let mut reader = source.into_inner();
reader.seek(SeekFrom::Start(0)).ok()?;
let mut source = wav::WavDecoder::new(reader).ok()?;
let sample = source.next();
(DecoderImpl::Wav(source), sample)
}
#[cfg(all(feature = "lewton", not(feature = "symphonia-vorbis")))]
DecoderImpl::Vorbis(source) => {
use lewton::inside_ogg::OggStreamReader;
let mut reader = source.into_inner().into_inner();
reader.seek_bytes(SeekFrom::Start(0)).ok()?;
let mut source = vorbis::VorbisDecoder::from_stream_reader(
OggStreamReader::from_ogg_reader(reader).ok()?,
);
let sample = source.next();
(DecoderImpl::Vorbis(source), sample)
}
#[cfg(all(feature = "claxon", not(feature = "symphonia-flac")))]
DecoderImpl::Flac(source) => {
let mut reader = source.into_inner();
reader.seek(SeekFrom::Start(0)).ok()?;
let mut source = flac::FlacDecoder::new(reader).ok()?;
let sample = source.next();
(DecoderImpl::Flac(source), sample)
}
#[cfg(all(feature = "minimp3", not(feature = "symphonia-mp3")))]
DecoderImpl::Mp3(source) => {
let mut reader = source.into_inner();
reader.seek(SeekFrom::Start(0)).ok()?;
let mut source = mp3::Mp3Decoder::new(reader).ok()?;
let sample = source.next();
(DecoderImpl::Mp3(source), sample)
}
#[cfg(feature = "symphonia")]
DecoderImpl::Symphonia(source, PhantomData) => {
let mut reader = source.into_inner();
reader.seek(SeekFrom::Start(0)).ok()?;
let mut source =
symphonia::SymphoniaDecoder::new(reader, &self.settings).ok()?;
let sample = source.next();
(DecoderImpl::Symphonia(source, PhantomData), sample)
}
};
self.inner = Some(new_decoder);
sample
} else {
None
}
}
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(
self.inner.as_ref().map_or(0, |inner| inner.size_hint().0),
None,
)
}
}
impl<R> Source for LoopedDecoder<R>
where
R: Read + Seek,
{
#[inline]
fn current_span_len(&self) -> Option<usize> {
self.inner.as_ref()?.current_span_len()
}
#[inline]
fn channels(&self) -> ChannelCount {
self.inner.as_ref().map_or(nz!(1), |inner| inner.channels())
}
#[inline]
fn sample_rate(&self) -> SampleRate {
self.inner
.as_ref()
.map_or(nz!(44100), |inner| inner.sample_rate())
}
#[inline]
fn total_duration(&self) -> Option<Duration> {
None
}
fn try_seek(&mut self, pos: Duration) -> Result<(), SeekError> {
match &mut self.inner {
Some(inner) => inner.try_seek(pos),
None => Err(SeekError::Other(Arc::new(DecoderError::IoError(
"Looped source ended when it failed to loop back".to_string(),
)))),
}
}
}
#[derive(Debug, thiserror::Error, Clone)]
pub enum DecoderError {
#[error("The format of the data has not been recognized.")]
UnrecognizedFormat,
#[error("An IO error occurred while reading, writing, or seeking the stream.")]
IoError(String),
#[error("The stream contained malformed data and could not be decoded or demuxed: {0}")]
#[cfg(feature = "symphonia")]
DecodeError(&'static str),
#[error(
"A default or user-defined limit was reached while decoding or demuxing the stream: {0}"
)]
#[cfg(feature = "symphonia")]
LimitError(&'static str),
#[error("The demuxer or decoder needs to be reset before continuing.")]
#[cfg(feature = "symphonia")]
ResetRequired,
#[error("No streams were found by the decoder.")]
#[cfg(feature = "symphonia")]
NoStreams,
}
assert_error_traits!(DecoderError);