Skip to main content

selene_daemon/player/
opened_decoder.rs

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