use std::{collections::VecDeque, num::NonZero, sync::Arc, time::SystemTime};
use audioadapter_buffers::direct::InterleavedSlice;
use lunar_lib::trace;
use rubato::{Fft, FixedSync, Resampler, ResamplerConstructionError};
use selene_core::{
config::common::common_config, library::track::ResolvedTrack,
symphonia_helpers::raw_decoder::RawDecoder,
};
use symphonia::core::{
audio::{Audio, AudioBuffer},
errors::Error as SymphoniaError,
formats::{SeekMode, SeekTo},
units::{Time, TimeBase},
};
use crate::player::PlayerError;
pub struct OpenedDecoder {
pub decoded_from: Arc<ResolvedTrack>,
pub started_at: Option<SystemTime>,
gain: f64,
inner: RawDecoder,
resampler: Fft<f32>,
resample_buffer: VecDeque<f32>,
pub current_frame: usize,
pub decoded_frames: usize,
pub at_eof: bool,
pub sent_scrobble: bool,
}
impl OpenedDecoder {
#[must_use]
pub fn time(&self) -> f64 {
let sample_rate = f64::from(self.inner.stream.codec_params.sample_rate);
self.current_frame as f64 / sample_rate
}
#[must_use]
pub fn decoded_time(&self) -> f64 {
let sample_rate = f64::from(self.inner.stream.codec_params.sample_rate);
self.decoded_frames as f64 / sample_rate
}
pub fn start_time(&self) -> Option<u64> {
let time = self
.started_at?
.duration_since(SystemTime::UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
Some(time)
}
pub(crate) fn started(&mut self) -> bool {
if self.started_at.is_none() {
self.started_at = Some(SystemTime::now());
return true;
}
false
}
pub fn decode_next_packet(&mut self) -> Result<VecDeque<f32>, PlayerError> {
let channel_count = self.resampler.nbr_channels();
loop {
let next_frame_size = self.resampler.input_frames_next();
let Some(packet) = self.inner.decode_next_packet()? else {
let mut samples = self.resample_buffer.drain(..).collect::<Vec<_>>();
let needed_samples = next_frame_size * channel_count;
samples.resize(needed_samples, 0.0);
let buffer_in = InterleavedSlice::new(&samples, channel_count, next_frame_size)
.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;
let mut data = resampled.take_data();
for sample in &mut data {
*sample *= self.gain as f32;
}
return Ok(data.into());
};
let mut audio_buffer = AudioBuffer::<f32>::new(packet.spec().clone(), packet.frames());
audio_buffer.render_silence(Some(packet.frames()));
packet.copy_to(&mut audio_buffer);
self.current_frame += packet.frames();
self.decoded_frames += packet.frames();
self.resample_buffer.extend(audio_buffer.iter_interleaved());
let mut output = Vec::new();
while self.resample_buffer.len() >= next_frame_size * channel_count {
let samples: Vec<f32> = self
.resample_buffer
.drain(..next_frame_size * channel_count)
.collect();
let buffer_in = InterleavedSlice::new(&samples, channel_count, next_frame_size)
.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() {
for sample in &mut output {
*sample *= self.gain as f32;
}
return Ok(output.into());
}
}
}
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::try_from_secs_f64(seconds).unwrap();
let seeked_to = self.inner.format_reader.seek(
SeekMode::Accurate,
SeekTo::Time {
time: timestamp,
track_id: Some(self.inner.stream.id),
},
)?;
self.inner.decoder.reset();
let time_base = self
.inner
.stream
.time_base
.map(|(n, d)| TimeBase {
numer: NonZero::new(n).unwrap(),
denom: NonZero::new(d).unwrap(),
})
.unwrap();
let raw_time = time_base.calc_time(seeked_to.actual_ts).unwrap();
let time = raw_time.as_secs_f64();
let sample_rate = self.inner.stream.codec_params.sample_rate;
self.current_frame = (time * f64::from(sample_rate)) as usize;
Ok(time)
}
}
impl OpenedDecoder {
pub fn new(playable: ResolvedTrack, output_sample_rate: usize) -> Result<Self, PlayerError> {
let container = playable.track.container();
trace!(
"Opening a new decoder from track: '{}'",
container.path().display()
);
let inner = RawDecoder::from_container(container, 64 * 1024)?;
let resampler = new_resampler(
inner.stream.codec_params.sample_rate as usize,
output_sample_rate,
inner.stream.codec_params.channels,
)?;
let gain = playable
.track
.loudnorm_analysis()
.map_or(1.0, |loudnorm_analysis| {
let config = common_config().loudnorm;
let gain_db =
(config.target_i - loudnorm_analysis.measured_i() - config.target_offset)
.min(config.target_tp - loudnorm_analysis.measured_tp());
10f64.powf(gain_db / 20.0)
});
trace!(
"Finished opening a decoder for: '{}'",
container.path().display()
);
Ok(OpenedDecoder {
inner,
gain,
current_frame: 0,
decoded_frames: 0,
started_at: None,
decoded_from: Arc::new(playable),
resampler,
resample_buffer: VecDeque::with_capacity(1024),
at_eof: false,
sent_scrobble: 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,
)
}