use cpal::Error;
use std::path::Path;
use crate::output::SampleType;
use super::{feature_required, no_decoder, AudioStream, Decoded, Encoding};
#[cfg(feature = "import")]
mod backend {
use std::fs::File;
use std::path::Path;
use std::sync::OnceLock;
use cpal::{Error, ErrorKind, SampleFormat, I24, U24};
use symphonia::core::audio::GenericAudioBufferRef;
use symphonia::core::codecs::audio::{AudioDecoder, AudioDecoderOptions};
use symphonia::core::codecs::registry::CodecRegistry;
use symphonia::core::errors::Error as SymphoniaError;
use symphonia::core::formats::probe::Hint;
use symphonia::core::formats::{FormatOptions, FormatReader, TrackType};
use symphonia::core::io::MediaSourceStream;
use symphonia::core::meta::MetadataOptions;
use crate::output::SampleType;
use crate::import::{AudioStream, Decoded, Samples};
fn codecs() -> &'static CodecRegistry {
static CODECS: OnceLock<CodecRegistry> = OnceLock::new();
CODECS.get_or_init(|| {
let mut registry = CodecRegistry::new();
symphonia::default::register_enabled_codecs(&mut registry);
#[cfg(feature = "import-he-aac")]
registry.register_audio_decoder::<symphonia_adapter_fdk_aac::AacDecoder>();
#[cfg(feature = "import-opus")]
registry.register_audio_decoder::<symphonia_adapter_libopus::OpusDecoder>();
registry
})
}
pub(super) fn open<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
Ok(Box::new(SymphoniaStream::open(path)?))
}
struct SymphoniaStream<S: SampleType> {
format: Box<dyn FormatReader>,
decoder: Box<dyn AudioDecoder>,
track_id: u32,
sample_rate: u32,
channels: u16,
total_frames: Option<u64>,
source_format: SampleFormat,
pending: Vec<S>,
scratch: Vec<f32>,
taken: usize,
emitted: u64,
limit: Option<u64>,
}
struct Opened {
format: Box<dyn FormatReader>,
decoder: Box<dyn AudioDecoder>,
track_id: u32,
sample_rate: u32,
channels: u16,
total_frames: Option<u64>,
bits: Option<u32>,
}
fn open_parts(path: &Path) -> Result<Opened, Error> {
let file = File::open(path).map_err(|error| {
let kind = match error.kind() {
std::io::ErrorKind::PermissionDenied => ErrorKind::PermissionDenied,
_ => ErrorKind::Other,
};
Error::with_message(kind, format!("could not read {}: {error}", path.display()))
})?;
let source = MediaSourceStream::new(Box::new(file), Default::default());
let format = symphonia::default::get_probe()
.probe(
&Hint::new(),
source,
FormatOptions::default(),
MetadataOptions::default(),
)
.map_err(|error| decode_failed(path, "container", error))?;
let track = format
.default_track(TrackType::Audio)
.ok_or_else(|| unreadable(path, "no audio track"))?;
let track_id = track.id;
let total_frames = track.num_frames;
let params = track
.codec_params
.as_ref()
.and_then(|params| params.audio())
.ok_or_else(|| unreadable(path, "audio track declares no codec"))?;
let decoder = codecs()
.make_audio_decoder(params, &AudioDecoderOptions::default())
.map_err(|error| decode_failed(path, "codec", error))?;
let decoded = decoder.codec_params();
let sample_rate = decoded
.sample_rate
.or(params.sample_rate)
.ok_or_else(|| unreadable(path, "no sample rate"))?;
let channels = decoded
.channels
.as_ref()
.or(params.channels.as_ref())
.map(|channels| channels.count())
.ok_or_else(|| unreadable(path, "no channel count"))?;
let bits = decoded.bits_per_sample.or(params.bits_per_sample);
Ok(Opened {
format,
decoder,
track_id,
sample_rate,
channels: channels as u16,
total_frames,
bits,
})
}
#[derive(Clone, Copy)]
enum Kind {
Unsigned,
Signed,
Float,
}
fn buffer_kind(buffer: &GenericAudioBufferRef<'_>) -> Kind {
match buffer {
GenericAudioBufferRef::U8(_)
| GenericAudioBufferRef::U16(_)
| GenericAudioBufferRef::U24(_)
| GenericAudioBufferRef::U32(_) => Kind::Unsigned,
GenericAudioBufferRef::S8(_)
| GenericAudioBufferRef::S16(_)
| GenericAudioBufferRef::S24(_)
| GenericAudioBufferRef::S32(_) => Kind::Signed,
GenericAudioBufferRef::F32(_) | GenericAudioBufferRef::F64(_) => Kind::Float,
}
}
fn native_format(bits: Option<u32>, kind: Option<Kind>) -> SampleFormat {
let kind = kind.unwrap_or(match bits {
Some(8) => Kind::Unsigned,
Some(_) => Kind::Signed,
None => Kind::Float,
});
match (kind, bits) {
(Kind::Float, Some(64)) => SampleFormat::F64,
(Kind::Float, _) => SampleFormat::F32,
(Kind::Unsigned, Some(..=8)) => SampleFormat::U8,
(Kind::Unsigned, Some(9..=16)) => SampleFormat::U16,
(Kind::Unsigned, Some(17..=24)) => SampleFormat::U24,
(Kind::Unsigned, _) => SampleFormat::U32,
(Kind::Signed, Some(..=8)) => SampleFormat::I8,
(Kind::Signed, Some(9..=16)) => SampleFormat::I16,
(Kind::Signed, Some(17..=24)) => SampleFormat::I24,
(Kind::Signed, _) => SampleFormat::I32,
}
}
pub(super) fn decode(path: &Path) -> Result<Decoded, Error> {
let Opened {
mut format,
mut decoder,
track_id,
sample_rate,
channels,
total_frames,
bits,
} = open_parts(path)?;
let limit = total_frames.map(|frames| frames.saturating_mul(channels.max(1) as u64));
let mut collected: Option<Samples> = None;
let mut emitted = 0u64;
loop {
if matches!(limit, Some(limit) if emitted >= limit) {
break;
}
let packet = match format.next_packet() {
Ok(Some(packet)) => packet,
Ok(None) | Err(SymphoniaError::ResetRequired) => break,
Err(error) => return Err(stream_failed("demuxing", error)),
};
if packet.track_id != track_id {
continue;
}
let buffer = match decoder.decode(&packet) {
Ok(buffer) => buffer,
Err(SymphoniaError::DecodeError(_)) => continue,
Err(SymphoniaError::ResetRequired) => break,
Err(error) => return Err(stream_failed("decoding", error)),
};
let samples = collected.get_or_insert_with(|| {
empty_for(native_format(bits, Some(buffer_kind(&buffer))))
});
let room = limit.map(|limit| (limit - emitted) as usize);
emitted += append(samples, &buffer, room) as u64;
}
Ok(Decoded {
samples: collected.unwrap_or_else(|| empty_for(native_format(bits, None))),
sample_rate,
channels,
})
}
fn empty_for(format: SampleFormat) -> Samples {
match format {
SampleFormat::U8 => Samples::U8(Vec::new()),
SampleFormat::I8 => Samples::I8(Vec::new()),
SampleFormat::U16 => Samples::U16(Vec::new()),
SampleFormat::I16 => Samples::I16(Vec::new()),
SampleFormat::U24 => Samples::U24(Vec::new()),
SampleFormat::I24 => Samples::I24(Vec::new()),
SampleFormat::U32 => Samples::U32(Vec::new()),
SampleFormat::I32 => Samples::I32(Vec::new()),
SampleFormat::F64 => Samples::F64(Vec::new()),
_ => Samples::F32(Vec::new()),
}
}
fn append(samples: &mut Samples, buffer: &GenericAudioBufferRef<'_>, room: Option<usize>) -> usize {
macro_rules! take {
($out:expr, $scratch_ty:ty, $convert:expr) => {{
let mut scratch: Vec<$scratch_ty> = Vec::new();
buffer.copy_to_vec_interleaved(&mut scratch);
let count = room.map_or(scratch.len(), |room| room.min(scratch.len()));
$out.extend(scratch[..count].iter().copied().map($convert));
count
}};
}
match samples {
Samples::U8(out) => take!(out, u8, |sample| sample),
Samples::I8(out) => take!(out, i8, |sample| sample),
Samples::U16(out) => take!(out, u16, |sample| sample),
Samples::I16(out) => take!(out, i16, |sample| sample),
Samples::U32(out) => take!(out, u32, |sample| sample),
Samples::I32(out) => take!(out, i32, |sample| sample),
Samples::F32(out) => take!(out, f32, |sample| sample),
Samples::F64(out) => take!(out, f64, |sample| sample),
Samples::U24(out) => take!(out, symphonia::core::audio::sample::u24, |sample| {
U24::new(sample.0 as i32).unwrap_or(<U24 as SampleType>::SILENCE)
}),
Samples::I24(out) => take!(out, symphonia::core::audio::sample::i24, |sample| {
I24::new(sample.0).unwrap_or(<I24 as SampleType>::SILENCE)
}),
}
}
impl<S: SampleType> SymphoniaStream<S> {
fn open(path: &Path) -> Result<Self, Error> {
let Opened {
format,
decoder,
track_id,
sample_rate,
channels,
total_frames,
bits,
} = open_parts(path)?;
Ok(SymphoniaStream {
format,
decoder,
track_id,
sample_rate,
channels,
total_frames,
source_format: native_format(bits, None),
pending: Vec::new(),
scratch: Vec::new(),
taken: 0,
emitted: 0,
limit: total_frames.map(|frames| frames.saturating_mul(channels.max(1) as u64)),
})
}
fn decode_next(&mut self) -> Result<bool, Error> {
loop {
let packet = match self.format.next_packet() {
Ok(Some(packet)) => packet,
Ok(None) => return Ok(false),
Err(SymphoniaError::ResetRequired) => return Ok(false),
Err(error) => return Err(stream_failed("demuxing", error)),
};
if packet.track_id != self.track_id {
continue;
}
match self.decoder.decode(&packet) {
Ok(decoded) => {
self.scratch.clear();
decoded.copy_to_vec_interleaved(&mut self.scratch);
self.pending.clear();
self.pending
.extend(self.scratch.iter().copied().map(S::from_f32));
self.taken = 0;
if !self.pending.is_empty() {
return Ok(true);
}
}
Err(SymphoniaError::DecodeError(_)) => continue,
Err(SymphoniaError::ResetRequired) => return Ok(false),
Err(error) => return Err(stream_failed("decoding", error)),
}
}
}
}
impl<S: SampleType> AudioStream<S> for SymphoniaStream<S> {
fn sample_rate(&self) -> u32 {
self.sample_rate
}
fn channels(&self) -> u16 {
self.channels
}
fn total_frames(&self) -> Option<u64> {
self.total_frames
}
fn source_format(&self) -> SampleFormat {
self.source_format
}
fn read(&mut self, out: &mut [S]) -> Result<usize, Error> {
let remaining = match self.limit {
Some(limit) if self.emitted >= limit => return Ok(0),
Some(limit) => Some(limit - self.emitted),
None => None,
};
if self.taken >= self.pending.len() && !self.decode_next()? {
return Ok(0);
}
let available = &self.pending[self.taken..];
let mut count = available.len().min(out.len());
if let Some(remaining) = remaining {
count = count.min(remaining as usize);
}
out[..count].copy_from_slice(&available[..count]);
self.taken += count;
self.emitted += count as u64;
Ok(count)
}
}
fn decode_failed(path: &Path, what: &str, error: SymphoniaError) -> Error {
Error::with_message(
ErrorKind::InvalidInput,
format!("could not read the {what} of {}: {error}", path.display()),
)
}
fn unreadable(path: &Path, what: &str) -> Error {
Error::with_message(
ErrorKind::InvalidInput,
format!("{}: {what}", path.display()),
)
}
fn stream_failed(what: &str, error: SymphoniaError) -> Error {
let kind = match error {
SymphoniaError::IoError(_) => ErrorKind::Other,
SymphoniaError::Unsupported(_) => ErrorKind::UnsupportedOperation,
_ => ErrorKind::InvalidInput,
};
Error::with_message(kind, format!("{what} failed: {error}"))
}
}
pub(super) fn read_pcm<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
#[cfg(feature = "import")]
{
backend::open(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("PCM decoding", "import"))
}
}
pub(super) fn decode_pcm(path: &Path) -> Result<Decoded, Error> {
#[cfg(feature = "import")]
{
backend::decode(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("PCM decoding", "import"))
}
}
pub(super) fn read_mp3<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
#[cfg(feature = "import")]
{
backend::open(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("MP3 decoding", "import"))
}
}
pub(super) fn decode_mp3(path: &Path) -> Result<Decoded, Error> {
#[cfg(feature = "import")]
{
backend::decode(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("MP3 decoding", "import"))
}
}
pub(super) fn read_aac<S: SampleType>(
path: &Path,
encoding: Encoding,
) -> Result<Box<dyn AudioStream<S>>, Error> {
if matches!(encoding, Encoding::AacHe | Encoding::AacHeV2) && !cfg!(feature = "import-he-aac") {
return Err(feature_required("HE-AAC decoding", "import-he-aac"));
}
#[cfg(feature = "import")]
{
backend::open(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("AAC decoding", "import"))
}
}
pub(super) fn decode_aac(path: &Path, encoding: Encoding) -> Result<Decoded, Error> {
if matches!(encoding, Encoding::AacHe | Encoding::AacHeV2) && !cfg!(feature = "import-he-aac") {
return Err(feature_required("HE-AAC decoding", "import-he-aac"));
}
#[cfg(feature = "import")]
{
backend::decode(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("AAC decoding", "import"))
}
}
pub(super) fn read_opus<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
#[cfg(feature = "import-opus")]
{
backend::open(path)
}
#[cfg(not(feature = "import-opus"))]
{
let _ = path;
Err(feature_required("Opus decoding", "import-opus"))
}
}
pub(super) fn decode_opus(path: &Path) -> Result<Decoded, Error> {
#[cfg(feature = "import-opus")]
{
backend::decode(path)
}
#[cfg(not(feature = "import-opus"))]
{
let _ = path;
Err(feature_required("Opus decoding", "import-opus"))
}
}
pub(super) fn read_vorbis<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
#[cfg(feature = "import")]
{
backend::open(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("Vorbis decoding", "import"))
}
}
pub(super) fn decode_vorbis(path: &Path) -> Result<Decoded, Error> {
#[cfg(feature = "import")]
{
backend::decode(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("Vorbis decoding", "import"))
}
}
pub(super) fn read_wma<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
let _ = path;
Err(no_decoder("WMA streaming"))
}
pub(super) fn decode_wma(path: &Path) -> Result<Decoded, Error> {
let _ = path;
Err(no_decoder("WMA decoding"))
}
pub(super) fn read_flac<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
#[cfg(feature = "import")]
{
backend::open(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("FLAC decoding", "import"))
}
}
pub(super) fn decode_flac(path: &Path) -> Result<Decoded, Error> {
#[cfg(feature = "import")]
{
backend::decode(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("FLAC decoding", "import"))
}
}
pub(super) fn read_alac<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
#[cfg(feature = "import")]
{
backend::open(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("ALAC decoding", "import"))
}
}
pub(super) fn decode_alac(path: &Path) -> Result<Decoded, Error> {
#[cfg(feature = "import")]
{
backend::decode(path)
}
#[cfg(not(feature = "import"))]
{
let _ = path;
Err(feature_required("ALAC decoding", "import"))
}
}
pub(super) fn read_truehd<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
let _ = path;
Err(no_decoder("Dolby TrueHD streaming"))
}
pub(super) fn decode_truehd(path: &Path) -> Result<Decoded, Error> {
let _ = path;
Err(no_decoder("Dolby TrueHD decoding"))
}
pub(super) fn read_ac3<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
let _ = path;
Err(no_decoder("AC-3 streaming"))
}
pub(super) fn decode_ac3(path: &Path) -> Result<Decoded, Error> {
let _ = path;
Err(no_decoder("AC-3 decoding"))
}
pub(super) fn read_eac3<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
let _ = path;
Err(no_decoder("E-AC-3 streaming"))
}
pub(super) fn decode_eac3(path: &Path) -> Result<Decoded, Error> {
let _ = path;
Err(no_decoder("E-AC-3 decoding"))
}
pub(super) fn read_dts<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
let _ = path;
Err(no_decoder("DTS streaming"))
}
pub(super) fn decode_dts(path: &Path) -> Result<Decoded, Error> {
let _ = path;
Err(no_decoder("DTS decoding"))
}
pub(super) fn read_dts_hd_ma<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
let _ = path;
Err(no_decoder("DTS-HD Master Audio streaming"))
}
pub(super) fn decode_dts_hd_ma(path: &Path) -> Result<Decoded, Error> {
let _ = path;
Err(no_decoder("DTS-HD Master Audio decoding"))
}
pub(super) fn read_amr_nb<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
let _ = path;
Err(no_decoder("AMR-NB streaming"))
}
pub(super) fn decode_amr_nb(path: &Path) -> Result<Decoded, Error> {
let _ = path;
Err(no_decoder("AMR-NB decoding"))
}
pub(super) fn read_amr_wb<S: SampleType>(path: &Path) -> Result<Box<dyn AudioStream<S>>, Error> {
let _ = path;
Err(no_decoder("AMR-WB streaming"))
}
pub(super) fn decode_amr_wb(path: &Path) -> Result<Decoded, Error> {
let _ = path;
Err(no_decoder("AMR-WB decoding"))
}