use std::{collections::VecDeque, num::NonZero, time::SystemTime};
use audioadapter_buffers::direct::InterleavedSlice;
use rubato::{Fft, FixedSync, Resampler, ResamplerConstructionError};
use selene_core::{
library::loudnorm::LoudnormAnalysis, symphonia_helpers::raw_decoder::RawDecoder,
};
use symphonia::core::{
audio::{Audio, AudioBuffer},
errors::{Error as SymphoniaError, SeekErrorKind},
formats::{SeekMode, SeekTo},
units::{Time, TimeBase},
};
use crate::{player::PlayerError, playlist::PlayingTrack};
pub struct OpenedDecoder {
pub decoded_from: PlayingTrack,
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
}
#[must_use]
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 chunk_size = next_frame_size * channel_count;
if self.resample_buffer.len() >= chunk_size {
let mut resampled = resample(
&mut self.resampler,
&self.resample_buffer.make_contiguous()[..chunk_size],
channel_count,
next_frame_size,
);
drop(self.resample_buffer.drain(..chunk_size));
for sample in &mut resampled {
*sample *= self.gain as f32;
}
return Ok(resampled.into());
}
let Some(packet) = self.inner.decode_next_packet()? else {
let mut samples = std::mem::take(&mut self.resample_buffer);
samples.resize(chunk_size, 0.0);
let mut resampled = resample(
&mut self.resampler,
samples.make_contiguous(),
channel_count,
next_frame_size,
);
self.at_eof = true;
for sample in &mut resampled {
*sample *= self.gain as f32;
}
return Ok(resampled.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());
}
}
pub fn seek(&mut self, seconds: f64, increment: bool) -> Result<f64, SymphoniaError> {
let seconds = if increment {
(self.time() + seconds).max(0.0)
} else {
seconds
}
.max(0.0);
let timestamp = Time::try_from_secs_f64(seconds).unwrap();
let seeked_to = match self.inner.format_reader.seek(
SeekMode::Accurate,
SeekTo::Time {
time: timestamp,
track_id: Some(self.inner.stream.id),
},
) {
Ok(seeked_to) => seeked_to,
Err(SymphoniaError::SeekError(SeekErrorKind::OutOfRange)) => {
self.at_eof = true;
return Ok(self.inner.stream.duration());
}
Err(other) => return Err(other),
};
self.inner.decoder.reset();
self.at_eof = false;
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: PlayingTrack, output_sample_rate: usize) -> Result<Self, PlayerError> {
let container = playable.source.container();
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 resample_buffer =
VecDeque::with_capacity(resampler.input_frames_max() * resampler.nbr_channels());
let gain = playable
.source
.loudnorm_analysis()
.map_or(1.0, LoudnormAnalysis::calculated_gain);
Ok(OpenedDecoder {
inner,
gain,
current_frame: 0,
decoded_frames: 0,
started_at: None,
decoded_from: playable,
resampler,
resample_buffer,
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,
)
}
fn resample(
resampler: &mut Fft<f32>,
samples: &[f32],
channel_count: usize,
frame_size: usize,
) -> Vec<f32> {
let buffer_in = InterleavedSlice::new(samples, channel_count, frame_size)
.expect("Sampler buffer contained less capacity then expected");
resampler
.process(&buffer_in, 0, None)
.expect("Resampler expects input and output to have the same number of channels")
.take_data()
}