use std::{fmt::Display, fs::File, io::{self, BufReader}, path::Path};
use std::error::Error;
use self::raw::RawDecoder;
mod raw;
#[cfg(feature = "wav")] mod wav;
#[cfg(feature = "vorbis")] mod vorbis;
#[cfg(feature = "mp3")] mod mp3;
#[cfg(feature = "flac")] mod flac;
pub type Sample = f32;
pub struct Decoder {
decoder: FormatDecoder
}
#[derive(Debug, Clone)]
pub struct RawAudioSpec {
pub sample_rate: u32,
pub channels: usize,
pub sample_format: RawSampleFormat,
pub endianness: Endian,
pub start_offset: usize,
pub max_frames: Option<usize>,
}
#[derive(Debug, Copy, Clone)]
pub enum Endian {
Big,
Little,
}
#[derive(Debug, Copy, Clone)]
pub enum RawSampleFormat {
Float32,
Float64,
Unsigned8,
Signed8,
Unsigned16,
Signed16,
Unsigned24,
Signed24,
Unsigned32,
Signed32,
Unsigned64,
Signed64
}
#[derive(Debug, Clone)]
pub struct AudioInfo {
sample_rate: u32,
channels: usize,
format: AudioFormat,
}
impl AudioInfo {
#[inline]
pub fn sample_rate(&self) -> u32 {
self.sample_rate
}
#[inline]
pub fn channels(&self) -> usize {
self.channels
}
#[inline]
pub fn format(&self) -> AudioFormat {
self.format
}
}
#[derive(Debug, Copy, Clone)]
pub enum AudioFormat {
Wav,
Vorbis,
Mp3,
Flac,
Raw,
}
impl Display for AudioFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
AudioFormat::Wav => write!(f, "WAV"),
AudioFormat::Vorbis => write!(f, "Vorbis"),
AudioFormat::Mp3 => write!(f, "MP3"),
AudioFormat::Flac => write!(f, "FLAC"),
AudioFormat::Raw => write!(f, "Raw"),
}
}
}
impl Decoder {
#[inline]
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, DecoderError> {
Ok(Self {
decoder: FormatDecoder::open(path)?
})
}
#[inline]
pub fn open_raw<P: AsRef<Path>>(path: P, spec: RawAudioSpec) -> Result<Self, DecoderError> {
let f = File::open(path).map_err(DecoderError::IOError)?;
Ok(Self {
decoder: FormatDecoder::Raw(RawDecoder::new(BufReader::new(f), spec)?)
})
}
}
impl Decoder {
#[inline]
pub fn info(&self) -> AudioInfo {
self.decoder.info()
}
#[inline]
pub fn into_samples(self) -> Result<SampleIterator, DecoderError> {
self.decoder.into_samples()
}
}
pub struct SampleIterator(Box<dyn Iterator<Item = Result<Sample, DecoderError>>>);
impl Iterator for SampleIterator {
type Item = Result<Sample, DecoderError>;
#[inline(always)]
fn next(&mut self) -> Option<Self::Item> {
self.0.next()
}
}
pub(crate) enum FormatDecoder {
Raw(self::raw::RawDecoder<BufReader<File>>),
#[cfg(feature = "wav")]
Wav(self::wav::WavDecoder),
#[cfg(feature = "vorbis")]
Vorbis(self::vorbis::VorbisDecoder),
#[cfg(feature = "mp3")]
Mp3(self::mp3::Mp3Decoder),
#[cfg(feature = "flac")]
Flac(self::flac::FlacDecoder),
}
impl FormatDecoder {
#[inline]
pub fn open<P: AsRef<Path>>(path: P) -> Result<Self, DecoderError> {
macro_rules! get_decoder {
($in_ext:expr, $($ext:literal => requires $feature:literal for $init:expr),*) => {
match $in_ext {
$(
#[cfg(feature = $feature)]
$ext => { return Ok($init) }
#[cfg(not(feature = $feature))]
$ext => { return Err(DecoderError::DisabledExtension { feature: $feature, extension: $ext }) }
)*
other => return Err(DecoderError::UnsupportedExtension(other.to_owned()))
}
}
}
if let Some(ext) = path.as_ref().extension().and_then(|ext| ext.to_str()) {
get_decoder!(ext,
"wav" => requires "wav" for FormatDecoder::Wav(self::wav::WavDecoder::open(path)?),
"ogg" => requires "vorbis" for FormatDecoder::Vorbis(self::vorbis::VorbisDecoder::open(path)?),
"mp3" => requires "mp3" for FormatDecoder::Mp3(self::mp3::Mp3Decoder::open(path)?),
"flac" => requires "flac" for FormatDecoder::Flac(self::flac::FlacDecoder::open(path)?)
)
}
Err(DecoderError::NoExtension)
}
#[inline]
pub fn into_samples(self) -> Result<SampleIterator, DecoderError> {
match self {
FormatDecoder::Raw(d) => Ok(SampleIterator(d.into_samples()?)),
#[cfg(feature = "wav")]
FormatDecoder::Wav(d) => Ok(SampleIterator(d.into_samples()?)),
#[cfg(feature = "vorbis")]
FormatDecoder::Vorbis(d) => Ok(SampleIterator(d.into_samples()?)),
#[cfg(feature = "mp3")]
FormatDecoder::Mp3(d) => Ok(SampleIterator(d.into_samples()?)),
#[cfg(feature = "flac")]
FormatDecoder::Flac(d) => Ok(SampleIterator(d.into_samples()?))
}
}
#[inline]
pub fn info(&self) -> AudioInfo {
match self {
FormatDecoder::Raw(d) => d.info(),
#[cfg(feature = "wav")]
FormatDecoder::Wav(d) => d.info(),
#[cfg(feature = "vorbis")]
FormatDecoder::Vorbis(d) => d.info(),
#[cfg(feature = "mp3")]
FormatDecoder::Mp3(d) => d.info(),
#[cfg(feature = "flac")]
FormatDecoder::Flac(d) => d.info(),
}
}
}
#[derive(Debug)]
pub enum DecoderError {
IOError(io::Error),
FormatError(String),
NoExtension,
UnsupportedExtension(String),
IncompleteData,
DisabledExtension {
extension: &'static str,
feature: &'static str,
},
}
impl Error for DecoderError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
None
}
fn cause(&self) -> Option<&dyn Error> {
self.source()
}
}
impl Display for DecoderError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
DecoderError::IOError(err) => write!(f, "IO error: {}", err),
DecoderError::FormatError(err) => write!(f, "format error: {}", err),
DecoderError::NoExtension => write!(f, "file has no extension"),
DecoderError::UnsupportedExtension(ext) => write!(f, "extension '{}' is not supported", ext),
DecoderError::DisabledExtension { extension, feature } => write!(f, "feature '{}' is required to read '{}' files, but is not enabled", feature, extension),
DecoderError::IncompleteData => write!(f, "incomplete data"),
}
}
}