use std::{collections::VecDeque, fs::File};
use audioadapter_buffers::direct::InterleavedSlice;
use lunar_lib::trace;
use rubato::{Fft, FixedSync, Resampler, ResamplerConstructionError};
use selene_core::container::ContainerFormat;
use symphonia::{
core::{
audio::SampleBuffer,
codecs::{CODEC_TYPE_NULL, Decoder, DecoderOptions},
errors::Error as SymphoniaError,
formats::{FormatOptions, FormatReader, SeekMode, SeekTo},
io::MediaSourceStream,
units::Time,
},
default::formats::{FlacReader, OggReader, WavReader},
};
use crate::{daemon::DaemonError, playlist::TracklistTrack};
pub struct OpenedDecoder {
pub decoded_from: TracklistTrack,
resampler: Fft<f32>,
resample_buffer: VecDeque<f32>,
pub for_stream: symphonia::core::formats::Track,
pub format_reader: Box<dyn FormatReader>,
pub decoder: Box<dyn Decoder>,
pub current_frame: usize,
pub at_eof: bool,
}
impl OpenedDecoder {
pub fn time(&self) -> f64 {
let sample_rate = f64::from(self.for_stream.codec_params.sample_rate.unwrap());
self.current_frame as f64 / sample_rate
}
pub fn decode_next_packet(&mut self) -> Result<VecDeque<f32>, DaemonError> {
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) =>
{
let mut samples = self.resample_buffer.drain(..).collect::<Vec<_>>();
let channel_count = self.resampler.nbr_channels();
let frame_count = self.resampler.input_frames_next();
let needed_samples = frame_count * channel_count;
samples.resize(needed_samples, 0.0);
let buffer_in = InterleavedSlice::new(&samples, channel_count, frame_count)
.expect("Sampler buffer contained less capacity then expected");
let resampled = self.resampler.process(&buffer_in, 0, None).expect(
"Resampler expects input and output to have the same number of channels",
);
self.at_eof = true;
return Ok(resampled.take_data().into());
}
Err(err) => return Err(DaemonError::Symphonia(err)),
};
while !self.format_reader.metadata().is_latest() {
self.format_reader.metadata().pop();
}
if packet.track_id() != self.for_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(DaemonError::Symphonia(err)),
};
let frame_count = decoded.frames();
let channel_count = decoded.spec().channels.count();
self.current_frame += frame_count;
let mut sample_buf = SampleBuffer::<f32>::new(frame_count as u64, *decoded.spec());
sample_buf.copy_interleaved_ref(decoded);
self.resample_buffer.extend(sample_buf.samples());
let mut output = Vec::new();
let chunk_frames = self.resampler.input_frames_next();
while self.resample_buffer.len() >= chunk_frames * channel_count {
let samples: Vec<f32> = self
.resample_buffer
.drain(..chunk_frames * channel_count)
.collect();
let buffer_in = InterleavedSlice::new(&samples, channel_count, chunk_frames)
.expect("Sampler buffer contained less capacity then expected");
let resampled = self.resampler.process(&buffer_in, 0, None).expect(
"Resampler expects input and output to have the same number of channels",
);
output.extend(resampled.take_data());
}
if !output.is_empty() {
return Ok(output.into());
}
continue;
}
}
pub fn seek(&mut self, seconds: f64, increment: bool) -> Result<f64, SymphoniaError> {
let seconds = if increment {
(self.time() + seconds).max(0.0)
} else {
seconds
};
let timestamp = Time::from(seconds);
let seeked_to = self.format_reader.seek(
SeekMode::Accurate,
SeekTo::Time {
time: timestamp,
track_id: Some(self.for_stream.id),
},
)?;
self.decoder.reset();
let time_base = self.for_stream.codec_params.time_base.unwrap();
let raw_time = time_base.calc_time(seeked_to.actual_ts);
let time = raw_time.seconds as f64 + raw_time.frac;
let sample_rate = self.for_stream.codec_params.sample_rate.unwrap();
self.current_frame = (time * sample_rate as f64) as usize;
Ok(time)
}
}
impl OpenedDecoder {
pub fn from_tracklist_track(
playable: TracklistTrack,
output_sample_rate: usize,
) -> Result<Self, DaemonError> {
let container = playable
.playable
.container()
.expect("Engine should handle tracks without containers");
trace!(
"Opening a new decoder from track: '{}'",
container.path().display()
);
let file = File::open(container.path())?;
let mss = MediaSourceStream::new(Box::new(file), Default::default());
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::Mp3 => {
todo!("Import mp3")
}
ContainerFormat::Ogg => Box::new(OggReader::try_new(mss, &fmt_opts)?),
ContainerFormat::Wav => Box::new(WavReader::try_new(mss, &fmt_opts)?),
};
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::default::get_codecs().make(&track.codec_params, &dec_opts)?;
let resampler = new_resampler(
track.codec_params.sample_rate.unwrap() as usize,
output_sample_rate,
track.codec_params.channels.unwrap().count(),
)?;
Ok(OpenedDecoder {
for_stream: track.clone(),
format_reader,
decoder,
current_frame: 0,
decoded_from: playable,
resampler,
resample_buffer: VecDeque::with_capacity(1024),
at_eof: false,
})
}
}
fn new_resampler(
input_sample_rate: usize,
output_sample_rate: usize,
channel_count: usize,
) -> Result<Fft<f32>, ResamplerConstructionError> {
Fft::new(
input_sample_rate,
output_sample_rate,
1024,
1,
channel_count,
FixedSync::Both,
)
}