selene_daemon/player/
opened_decoder.rs1use std::{collections::VecDeque, num::NonZero, time::SystemTime};
2
3use audioadapter_buffers::direct::InterleavedSlice;
4use rubato::{Fft, FixedSync, Resampler, ResamplerConstructionError};
5use selene_core::{
6 library::loudnorm::LoudnormAnalysis, symphonia_helpers::raw_decoder::RawDecoder,
7};
8
9use symphonia::core::{
10 audio::{Audio, AudioBuffer},
11 errors::{Error as SymphoniaError, SeekErrorKind},
12 formats::{SeekMode, SeekTo},
13 units::{Time, TimeBase},
14};
15
16use crate::{player::PlayerError, playlist::PlayingTrack};
17
18pub struct OpenedDecoder {
19 pub decoded_from: PlayingTrack,
20 pub started_at: Option<SystemTime>,
21 gain: f64,
22
23 inner: RawDecoder,
24
25 resampler: Fft<f32>,
26 resample_buffer: VecDeque<f32>,
27
28 pub current_frame: usize,
29 pub decoded_frames: usize,
30
31 pub at_eof: bool,
32 pub sent_scrobble: bool,
33}
34
35impl OpenedDecoder {
36 #[must_use]
37 pub fn time(&self) -> f64 {
39 let sample_rate = f64::from(self.inner.stream.codec_params.sample_rate);
40 self.current_frame as f64 / sample_rate
41 }
42
43 #[must_use]
44 pub fn decoded_time(&self) -> f64 {
46 let sample_rate = f64::from(self.inner.stream.codec_params.sample_rate);
47 self.decoded_frames as f64 / sample_rate
48 }
49
50 #[must_use]
51 pub fn start_time(&self) -> Option<u64> {
52 let time = self
53 .started_at?
54 .duration_since(SystemTime::UNIX_EPOCH)
55 .expect("Time went backwards")
56 .as_secs();
57
58 Some(time)
59 }
60
61 pub(crate) fn started(&mut self) -> bool {
62 if self.started_at.is_none() {
63 self.started_at = Some(SystemTime::now());
64 return true;
65 }
66
67 false
68 }
69
70 pub fn decode_next_packet(&mut self) -> Result<VecDeque<f32>, PlayerError> {
71 let channel_count = self.resampler.nbr_channels();
72
73 loop {
74 let next_frame_size = self.resampler.input_frames_next();
75 let chunk_size = next_frame_size * channel_count;
76
77 if self.resample_buffer.len() >= chunk_size {
78 let mut resampled = resample(
79 &mut self.resampler,
80 &self.resample_buffer.make_contiguous()[..chunk_size],
81 channel_count,
82 next_frame_size,
83 );
84
85 drop(self.resample_buffer.drain(..chunk_size));
86
87 for sample in &mut resampled {
88 *sample *= self.gain as f32;
89 }
90 return Ok(resampled.into());
91 }
92
93 let Some(packet) = self.inner.decode_next_packet()? else {
94 let mut samples = std::mem::take(&mut self.resample_buffer);
95 samples.resize(chunk_size, 0.0);
96
97 let mut resampled = resample(
98 &mut self.resampler,
99 samples.make_contiguous(),
100 channel_count,
101 next_frame_size,
102 );
103
104 self.at_eof = true;
105
106 for sample in &mut resampled {
107 *sample *= self.gain as f32;
108 }
109 return Ok(resampled.into());
110 };
111
112 let mut audio_buffer = AudioBuffer::<f32>::new(packet.spec().clone(), packet.frames());
113 audio_buffer.render_silence(Some(packet.frames()));
114 packet.copy_to(&mut audio_buffer);
115
116 self.current_frame += packet.frames();
117 self.decoded_frames += packet.frames();
118
119 self.resample_buffer.extend(audio_buffer.iter_interleaved());
120 }
121 }
122
123 pub fn seek(&mut self, seconds: f64, increment: bool) -> Result<f64, SymphoniaError> {
124 let seconds = if increment {
125 (self.time() + seconds).max(0.0)
126 } else {
127 seconds
128 }
129 .max(0.0);
130
131 let timestamp = Time::try_from_secs_f64(seconds).unwrap();
132
133 let seeked_to = match self.inner.format_reader.seek(
134 SeekMode::Accurate,
135 SeekTo::Time {
136 time: timestamp,
137 track_id: Some(self.inner.stream.id),
138 },
139 ) {
140 Ok(seeked_to) => seeked_to,
141 Err(SymphoniaError::SeekError(SeekErrorKind::OutOfRange)) => {
142 self.at_eof = true;
143 return Ok(self.inner.stream.duration());
144 }
145 Err(other) => return Err(other),
146 };
147
148 self.inner.decoder.reset();
149 self.at_eof = false;
150
151 let time_base = self
152 .inner
153 .stream
154 .time_base
155 .map(|(n, d)| TimeBase {
156 numer: NonZero::new(n).unwrap(),
157 denom: NonZero::new(d).unwrap(),
158 })
159 .unwrap();
160
161 let raw_time = time_base.calc_time(seeked_to.actual_ts).unwrap();
162 let time = raw_time.as_secs_f64();
163
164 let sample_rate = self.inner.stream.codec_params.sample_rate;
165 self.current_frame = (time * f64::from(sample_rate)) as usize;
166
167 Ok(time)
168 }
169}
170
171impl OpenedDecoder {
172 pub fn new(playable: PlayingTrack, output_sample_rate: usize) -> Result<Self, PlayerError> {
173 let container = playable.source.container();
174
175 let inner = RawDecoder::from_container(container, 64 * 1024)?;
176
177 let resampler = new_resampler(
178 inner.stream.codec_params.sample_rate as usize,
179 output_sample_rate,
180 inner.stream.codec_params.channels,
181 )?;
182
183 let resample_buffer =
184 VecDeque::with_capacity(resampler.input_frames_max() * resampler.nbr_channels());
185
186 let gain = playable
187 .source
188 .loudnorm_analysis()
189 .map_or(1.0, LoudnormAnalysis::calculated_gain);
190
191 Ok(OpenedDecoder {
192 inner,
193 gain,
194 current_frame: 0,
195 decoded_frames: 0,
196 started_at: None,
197 decoded_from: playable,
198 resampler,
199 resample_buffer,
200 at_eof: false,
201 sent_scrobble: false,
202 })
203 }
204}
205
206fn new_resampler(
207 input_sample_rate: usize,
208 output_sample_rate: usize,
209 channel_count: usize,
210) -> Result<Fft<f32>, ResamplerConstructionError> {
211 Fft::new(
212 input_sample_rate,
213 output_sample_rate,
214 1024,
215 1,
216 channel_count,
217 FixedSync::Both,
218 )
219}
220
221fn resample(
222 resampler: &mut Fft<f32>,
223 samples: &[f32],
224 channel_count: usize,
225 frame_size: usize,
226) -> Vec<f32> {
227 let buffer_in = InterleavedSlice::new(samples, channel_count, frame_size)
228 .expect("Sampler buffer contained less capacity then expected");
229
230 resampler
231 .process(&buffer_in, 0, None)
232 .expect("Resampler expects input and output to have the same number of channels")
233 .take_data()
234}