use std::io::Read;
use std::marker::Send;
use std::marker::Sync;
#[cfg(feature = "enable-filesystem")]
use std::convert::AsRef;
#[cfg(feature = "enable-filesystem")]
use std::path::Path;
use symphonia::core::audio::SampleBuffer;
use symphonia::core::codecs::Decoder as SymphoniaDecoderTrait;
use symphonia::core::codecs::DecoderOptions;
use symphonia::core::formats::FormatOptions;
use symphonia::core::formats::{FormatReader, Track};
use symphonia::core::io::{MediaSource, MediaSourceStream, ReadOnlySource};
use symphonia::core::meta::MetadataOptions;
use symphonia::core::probe::Hint;
use crate::backend::constants::DEFAULT_FILE_EXTENSION;
use crate::backend::constants::DEFAULT_MIME_TYPE;
use crate::backend::display::est_num_frames_to_str;
use crate::backend::Error;
use crate::backend::Signal;
use crate::backend::Source;
pub struct SymphoniaDecoder {
decoder: Box<dyn SymphoniaDecoderTrait>,
reader: Box<dyn FormatReader>,
frame_rate_hz: u32,
num_channels: u16,
num_samples_remaining: Option<usize>,
current_packet_audio_buffer: Option<Vec<f32>>,
current_packet_sample_idx: usize,
}
impl std::fmt::Debug for SymphoniaDecoder {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(
f,
"SymphoniaDecoder {{ {} frames, {} channels, {} hz, {} }}",
est_num_frames_to_str(self.num_frames_estimate()),
self.num_channels(),
self.frame_rate_hz(),
self.duration_estimate_to_str(),
)
}
}
impl SymphoniaDecoder {
#[allow(clippy::missing_panics_doc)]
pub fn from_encoded_stream_with_hint<R: 'static + Read + Send + Sync>(
encoded_stream: R,
file_extension: &str,
mime_type: &str,
) -> Result<Self, Error> {
let metadata_opts: MetadataOptions = Default::default();
let format_opts: FormatOptions = FormatOptions {
enable_gapless: true,
..Default::default()
};
let mut hint = Hint::new();
if file_extension != DEFAULT_FILE_EXTENSION {
hint.with_extension(file_extension);
}
if mime_type != DEFAULT_MIME_TYPE {
hint.mime_type(mime_type);
}
let media_source: Box<dyn MediaSource> = Box::new(ReadOnlySource::new(encoded_stream));
let media_source_stream = MediaSourceStream::new(media_source, Default::default());
let probed = match symphonia::default::get_probe().format(
&hint,
media_source_stream,
&format_opts,
&metadata_opts,
) {
Ok(value) => value,
Err(symphonia::core::errors::Error::Unsupported { .. }) => {
return Err(Error::UnknownInputEncoding);
}
Err(err) => {
return Err(Error::UnknownDecodeErrorWithMessage(leak_str!(
err.to_string()
)))
}
};
let reader = probed.format;
let default_track: &Track = match reader.default_track() {
None => return Err(Error::NoSuitableAudioStreams(reader.tracks().len())),
Some(dt) => dt,
};
let frame_rate_hz = match default_track.codec_params.sample_rate {
None => return Err(Error::UnknownInputEncoding),
Some(fr) => fr,
};
#[allow(clippy::cast_possible_truncation)]
let num_channels = match default_track.codec_params.channels {
None => return Err(Error::UnknownInputEncoding),
Some(channels) => channels.count() as u16,
};
#[allow(clippy::cast_possible_truncation)]
let num_samples_remaining: Option<usize> = default_track
.codec_params
.n_frames
.map(|num_frames| num_frames as usize * num_channels as usize);
let decoder_opts: DecoderOptions = DecoderOptions { verify: false };
let default_track: &Track = match reader.default_track() {
None => return Err(Error::NoSuitableAudioStreams(reader.tracks().len())),
Some(dt) => dt,
};
let decoder = match symphonia::default::get_codecs()
.make(&default_track.codec_params, &decoder_opts)
{
Ok(value) => value,
Err(symphonia::core::errors::Error::Unsupported { .. }) => {
return Err(Error::UnknownInputEncoding);
}
Err(_) => {
return Err(Error::UnknownDecodeError);
}
};
let mut new_self = Self {
decoder,
reader,
frame_rate_hz,
num_channels,
num_samples_remaining,
current_packet_audio_buffer: None,
current_packet_sample_idx: 0,
};
new_self.current_packet_audio_buffer = new_self.next_packet_buffer();
Ok(new_self)
}
#[cfg(feature = "enable-filesystem")]
#[allow(clippy::missing_panics_doc)]
pub fn from_file<F: Clone + AsRef<Path>>(filename: F) -> Result<Self, Error> {
let filename_ref = filename.as_ref();
let file = match std::fs::File::open(filename_ref) {
Ok(f) => f,
Err(err) => match err.kind() {
std::io::ErrorKind::NotFound => {
return Err(Error::FileNotFound(Box::leak(
filename_ref.to_str().unwrap().to_owned().into_boxed_str(),
)));
}
_ => {
return Err(Error::UnknownIOError);
}
},
};
if let Ok(metadata) = file.metadata() {
if metadata.is_dir() {
return Err(Error::FilenameIsADirectory(Box::leak(
filename_ref.to_str().unwrap().to_owned().into_boxed_str(),
)));
}
}
let file_extension = match filename.as_ref().extension() {
Some(os_str) => match os_str.to_str() {
Some(str) => str,
None => DEFAULT_FILE_EXTENSION,
},
None => DEFAULT_FILE_EXTENSION,
};
Self::from_encoded_stream_with_hint(file, file_extension, DEFAULT_MIME_TYPE)
}
fn next_packet_buffer(&mut self) -> Option<Vec<f32>> {
while let Ok(packet) = self.reader.next_packet() {
match self.decoder.decode(&packet) {
Err(symphonia::core::errors::Error::DecodeError(..)) => {
continue;
}
Err(_) => return None,
Ok(decoded) => {
let spec = *decoded.spec();
let duration = decoded.capacity() as u64;
let mut buf = SampleBuffer::<f32>::new(duration, spec);
buf.copy_interleaved_ref(decoded);
let buf: Vec<f32> = buf.samples().to_owned();
return Some(buf);
}
}
}
None
}
}
impl Source for SymphoniaDecoder {}
impl Signal for SymphoniaDecoder {
#[inline]
fn frame_rate_hz(&self) -> u32 {
self.frame_rate_hz
}
#[inline]
fn num_channels(&self) -> u16 {
self.num_channels
}
#[inline]
fn num_frames_estimate(&self) -> Option<usize> {
let num_samples_remaining = self.num_samples_remaining?;
Some(num_samples_remaining / self.num_channels as usize)
}
}
impl Iterator for SymphoniaDecoder {
type Item = f32;
#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
match self.num_samples_remaining {
None => (0, None),
Some(nsr) => (nsr, None),
}
}
#[inline]
fn next(&mut self) -> Option<Self::Item> {
loop {
let buffer = self.current_packet_audio_buffer.as_ref()?;
if self.current_packet_sample_idx >= buffer.len() {
self.current_packet_audio_buffer = self.next_packet_buffer();
self.current_packet_sample_idx = 0;
continue;
}
let next_sample: f32 = buffer[self.current_packet_sample_idx];
self.num_samples_remaining =
self.num_samples_remaining.map(|nsr| nsr.saturating_sub(1));
self.current_packet_sample_idx += 1;
return Some(next_sample);
}
}
}
#[cfg(all(test, feature = "enable-ffmpeg"))]
mod test_symphonia_source {
use crate::decoder::SymphoniaDecoder;
use crate::Signal;
use crate::Source;
const COF_FILENAME: &str = "./audio-for-tests/circus-of-freaks/track.flac";
const COF_NUM_CHANNELS: u16 = 2;
const COF_NUM_FRAMES: usize = 2491247;
const COF_NUM_SAMPLES: usize = COF_NUM_FRAMES * COF_NUM_CHANNELS as usize;
const MONO_DTMF_FILENAME: &str = "./audio-for-tests/mono-dtmf-tones/track.flac";
const MONO_DTMF_NUM_CHANNELS: u16 = 1;
const MONO_DTMF_NUM_FRAMES: usize = 441000;
const MONO_DTMF_SAMPLES: usize = MONO_DTMF_NUM_FRAMES * MONO_DTMF_NUM_CHANNELS as usize;
#[test]
fn test_cof_size_hint_1() {
let mut decoder =
SymphoniaDecoder::from_file(COF_FILENAME).expect("Failed to decode circus-of-freaks");
assert_eq!(decoder.size_hint(), (COF_NUM_SAMPLES, None));
decoder.next();
assert_eq!(decoder.size_hint(), (COF_NUM_SAMPLES - 1, None));
decoder.next();
assert_eq!(decoder.size_hint(), (COF_NUM_SAMPLES - 2, None));
let decoder = decoder.skip(10);
assert_eq!(decoder.size_hint(), (COF_NUM_SAMPLES - 12, None));
let mut decoder = decoder.take(1000);
assert_eq!(decoder.size_hint(), (1000, Some(1000)));
decoder.next();
assert_eq!(decoder.size_hint(), (999, Some(999)));
}
#[test]
fn test_mono_dtmf_size_hint_1() {
let mut decoder = SymphoniaDecoder::from_file(MONO_DTMF_FILENAME)
.expect("Failed to decode mono-dtmf-tones");
assert_eq!(decoder.size_hint(), (MONO_DTMF_SAMPLES, None));
decoder.next();
assert_eq!(decoder.size_hint(), (MONO_DTMF_SAMPLES - 1, None));
decoder.next();
assert_eq!(decoder.size_hint(), (MONO_DTMF_SAMPLES - 2, None));
let decoder = decoder.skip(10);
assert_eq!(decoder.size_hint(), (MONO_DTMF_SAMPLES - 12, None));
let mut decoder = decoder.take(1000);
assert_eq!(decoder.size_hint(), (1000, Some(1000)));
decoder.next();
assert_eq!(decoder.size_hint(), (999, Some(999)));
}
#[test]
fn test_first_4_samples() {
let mut decoder =
SymphoniaDecoder::from_file("audio-for-tests/circus-of-freaks/track.flac")
.expect("decoding error");
let frame_rate_hz: u32 = 44100;
let num_channels: u16 = 2;
let num_frames_estimate: usize = 2491247;
let num_samples_estimate: usize = num_frames_estimate * num_channels as usize;
assert_eq!(decoder.frame_rate_hz(), frame_rate_hz);
assert_eq!(decoder.num_channels(), num_channels);
assert_eq!(decoder.num_frames_estimate(), Some(num_frames_estimate));
assert_eq!(decoder.size_hint(), (num_samples_estimate, None));
decoder.next();
assert_eq!(decoder.frame_rate_hz(), frame_rate_hz);
assert_eq!(decoder.num_channels(), num_channels);
assert_eq!(decoder.num_frames_estimate(), Some(num_frames_estimate - 1));
assert_eq!(decoder.size_hint(), (num_samples_estimate - 1, None));
decoder.next();
assert_eq!(decoder.frame_rate_hz(), frame_rate_hz);
assert_eq!(decoder.num_channels(), num_channels);
assert_eq!(decoder.num_frames_estimate(), Some(num_frames_estimate - 1));
assert_eq!(decoder.size_hint(), (num_samples_estimate - 2, None));
decoder.next();
assert_eq!(decoder.frame_rate_hz(), frame_rate_hz);
assert_eq!(decoder.num_channels(), num_channels);
assert_eq!(decoder.num_frames_estimate(), Some(num_frames_estimate - 2));
assert_eq!(decoder.size_hint(), (num_samples_estimate - 3, None));
decoder.next();
assert_eq!(decoder.frame_rate_hz(), frame_rate_hz);
assert_eq!(decoder.num_channels(), num_channels);
assert_eq!(decoder.num_frames_estimate(), Some(num_frames_estimate - 2));
assert_eq!(decoder.size_hint(), (num_samples_estimate - 4, None));
let remaining: Vec<f32> = decoder.collect_interleaved_samples();
assert_eq!(remaining.len(), num_samples_estimate - 4);
}
}