gonk_player/
lib.rs

1#![recursion_limit = "2048"]
2#![allow(clippy::should_implement_trait, clippy::unnecessary_to_owned)]
3use cpal::{
4    traits::{HostTrait, StreamTrait},
5    BuildStreamError, Stream, StreamConfig,
6};
7use gonk_database::{Index, Song};
8use std::{
9    fs::File,
10    io::ErrorKind,
11    sync::{Arc, RwLock},
12    time::Duration,
13    vec::IntoIter,
14};
15use symphonia::{
16    core::{
17        audio::SampleBuffer,
18        codecs::{Decoder, DecoderOptions},
19        errors::{Error, SeekErrorKind},
20        formats::{FormatOptions, SeekMode, SeekTo},
21        io::MediaSourceStream,
22        meta::MetadataOptions,
23        probe::{Hint, ProbeResult},
24        units::{Time, TimeBase},
25    },
26    default::get_probe,
27};
28
29pub use cpal::{traits::DeviceTrait, Device};
30
31mod cpal;
32
33#[inline]
34const fn gcd(a: usize, b: usize) -> usize {
35    if b == 0 {
36        a
37    } else {
38        gcd(b, a % b)
39    }
40}
41
42#[inline]
43fn lerp(a: f32, b: f32, t: f32) -> f32 {
44    a + t * (b - a)
45}
46
47const VOLUME_STEP: u8 = 5;
48const VOLUME_REDUCTION: f32 = 500.0;
49const MAX_DECODE_ERRORS: usize = 3;
50
51pub struct Resampler {
52    probed: ProbeResult,
53    decoder: Box<dyn Decoder>,
54
55    input_real: usize,
56    input: usize,
57    output: usize,
58
59    buffer: IntoIter<f32>,
60
61    current_frame: Vec<f32>,
62    current_frame_pos: usize,
63
64    next_frame: Vec<f32>,
65    next_frame_pos: usize,
66
67    output_buffer: Option<f32>,
68
69    time_base: TimeBase,
70
71    gain: f32,
72
73    pub volume: f32,
74    pub duration: Duration,
75    pub finished: bool,
76    pub elapsed: Duration,
77}
78
79impl Resampler {
80    pub fn new(output: usize, file: File, volume: u8, gain: f32) -> Self {
81        let mss = MediaSourceStream::new(Box::new(file), Default::default());
82
83        let mut probed = get_probe()
84            .format(
85                &Hint::default(),
86                mss,
87                &FormatOptions {
88                    prebuild_seek_index: true,
89                    seek_index_fill_rate: 1,
90                    enable_gapless: false,
91                },
92                &MetadataOptions::default(),
93            )
94            .unwrap();
95
96        let track = probed.format.default_track().unwrap();
97        let input = track.codec_params.sample_rate.unwrap() as usize;
98        let time_base = track.codec_params.time_base.unwrap();
99
100        let n_frames = track.codec_params.n_frames.unwrap();
101        let time = track.codec_params.time_base.unwrap().calc_time(n_frames);
102        let duration = Duration::from_secs(time.seconds) + Duration::from_secs_f64(time.frac);
103
104        let mut decoder = symphonia::default::get_codecs()
105            .make(&track.codec_params, &DecoderOptions::default())
106            .unwrap();
107
108        let next_packet = probed.format.next_packet().unwrap();
109        let decoded = decoder.decode(&next_packet).unwrap();
110        let mut buffer = SampleBuffer::<f32>::new(decoded.capacity() as u64, *decoded.spec());
111        buffer.copy_interleaved_ref(decoded);
112        let mut buffer = buffer.samples().to_vec().into_iter();
113
114        let ts = next_packet.ts();
115        let t = time_base.calc_time(ts);
116        let elapsed = Duration::from_secs(t.seconds) + Duration::from_secs_f64(t.frac);
117
118        let gcd = gcd(input, output);
119
120        let (current_frame, next_frame) = if input == output {
121            (Vec::new(), Vec::new())
122        } else {
123            (
124                vec![buffer.next().unwrap(), buffer.next().unwrap()],
125                vec![buffer.next().unwrap(), buffer.next().unwrap()],
126            )
127        };
128
129        Self {
130            probed,
131            decoder,
132            buffer,
133            input_real: input,
134            input: input / gcd,
135            output: output / gcd,
136            current_frame_pos: 0,
137            next_frame_pos: 0,
138            current_frame,
139            next_frame,
140            output_buffer: None,
141            volume: volume as f32 / VOLUME_REDUCTION,
142            duration,
143            elapsed,
144            time_base,
145            finished: false,
146            gain,
147        }
148    }
149
150    pub fn update_sample_rate(&mut self, output: usize) {
151        let gcd = gcd(self.input_real, output);
152        self.input = self.input_real / gcd;
153        self.output = output / gcd;
154
155        //TODO: There might be more buffers that should be cleared
156        //when changing the sample rate. It's hard to test.
157        self.output_buffer = None;
158    }
159
160    pub fn next(&mut self) -> f32 {
161        if self.finished {
162            0.0
163        } else if let Some(smp) = self.next_sample() {
164            if self.gain == 0.0 {
165                //Reduce the volume a little to match
166                //songs with replay gain information.
167                smp * self.volume * 0.75
168            } else {
169                smp * self.volume * self.gain
170            }
171        } else {
172            let mut decode_errors: usize = 0;
173            loop {
174                if self.finished {
175                    return 0.0;
176                }
177
178                match self.probed.format.next_packet() {
179                    Ok(next_packet) => {
180                        let decoded = self.decoder.decode(&next_packet).unwrap();
181                        let mut buffer =
182                            SampleBuffer::<f32>::new(decoded.capacity() as u64, *decoded.spec());
183                        buffer.copy_interleaved_ref(decoded);
184                        self.buffer = buffer.samples().to_vec().into_iter();
185
186                        let ts = next_packet.ts();
187                        let t = self.time_base.calc_time(ts);
188                        self.elapsed =
189                            Duration::from_secs(t.seconds) + Duration::from_secs_f64(t.frac);
190
191                        if self.input == self.output {
192                            self.current_frame = Vec::new();
193                            self.next_frame = Vec::new();
194                        } else {
195                            self.current_frame =
196                                vec![self.buffer.next().unwrap(), self.buffer.next().unwrap()];
197                            self.next_frame =
198                                vec![self.buffer.next().unwrap(), self.buffer.next().unwrap()];
199                        }
200
201                        self.current_frame_pos = 0;
202                        self.next_frame_pos = 0;
203
204                        debug_assert!(self.output_buffer.is_none());
205
206                        return self.next();
207                    }
208                    Err(err) => match err {
209                        Error::IoError(err) => match err.kind() {
210                            ErrorKind::UnexpectedEof => {
211                                self.finished = true;
212                                return 0.0;
213                            }
214                            _ => continue,
215                        },
216                        Error::DecodeError(_) => {
217                            decode_errors += 1;
218                            if decode_errors > MAX_DECODE_ERRORS {
219                                panic!("{:?}", err);
220                            }
221                            continue;
222                        }
223                        _ => panic!("{}", err),
224                    },
225                }
226            }
227        }
228    }
229
230    fn next_input_frame(&mut self) {
231        self.current_frame = std::mem::take(&mut self.next_frame);
232
233        if let Some(sample) = self.buffer.next() {
234            self.next_frame.push(sample);
235        }
236
237        if let Some(sample) = self.buffer.next() {
238            self.next_frame.push(sample);
239        }
240
241        self.current_frame_pos += 1;
242    }
243
244    fn next_sample(&mut self) -> Option<f32> {
245        if self.input == self.output {
246            return self.buffer.next();
247        } else if let Some(sample) = self.output_buffer.take() {
248            return Some(sample);
249        }
250
251        if self.next_frame_pos == self.output {
252            self.next_frame_pos = 0;
253
254            self.next_input_frame();
255            while self.current_frame_pos != self.input {
256                self.next_input_frame();
257            }
258            self.current_frame_pos = 0;
259        } else {
260            let req_left_sample = (self.input * self.next_frame_pos / self.output) % self.input;
261
262            while self.current_frame_pos != req_left_sample {
263                self.next_input_frame();
264                debug_assert!(self.current_frame_pos < self.input);
265            }
266        }
267
268        let numerator = (self.input * self.next_frame_pos) % self.output;
269
270        self.next_frame_pos += 1;
271
272        if self.current_frame.is_empty() && self.next_frame.is_empty() {
273            return None;
274        }
275
276        if self.next_frame.is_empty() {
277            let r = self.current_frame.remove(0);
278            self.output_buffer = self.current_frame.first().cloned();
279            self.current_frame.clear();
280            Some(r)
281        } else {
282            let ratio = numerator as f32 / self.output as f32;
283            self.output_buffer = Some(lerp(self.current_frame[1], self.next_frame[1], ratio));
284            Some(lerp(self.current_frame[0], self.next_frame[0], ratio))
285        }
286    }
287
288    pub fn set_volume(&mut self, volume: u8) {
289        self.volume = volume as f32 / VOLUME_REDUCTION;
290    }
291    pub fn seek(&mut self, time: Duration) -> Result<(), String> {
292        match self.probed.format.seek(
293            SeekMode::Coarse,
294            SeekTo::Time {
295                time: Time::new(time.as_secs(), time.subsec_nanos() as f64 / 1_000_000_000.0),
296                track_id: None,
297            },
298        ) {
299            Ok(_) => Ok(()),
300            Err(e) => match e {
301                Error::SeekError(e) => match e {
302                    SeekErrorKind::OutOfRange => Err(String::from("Seek out of range!")),
303                    _ => panic!("{:?}", e),
304                },
305                _ => panic!("{}", e),
306            },
307        }
308    }
309}
310
311#[derive(Debug, PartialEq, Eq)]
312pub enum State {
313    Playing,
314    Paused,
315    Stopped,
316}
317
318pub struct Player {
319    pub stream: Stream,
320    pub resampler: Arc<RwLock<Option<Resampler>>>,
321    pub sample_rate: usize,
322    pub state: State,
323    pub songs: Index<Song>,
324    pub volume: u8,
325}
326
327impl Player {
328    pub fn new(wanted_device: &str, volume: u8, songs: Index<Song>, elapsed: f32) -> Self {
329        #[cfg(unix)]
330        let _gag = gag::Gag::stderr().unwrap();
331
332        let mut device = None;
333
334        for d in audio_devices() {
335            if d.name().unwrap() == wanted_device {
336                device = Some(d);
337            }
338        }
339
340        let device = if let Some(device) = device {
341            device
342        } else {
343            default_device()
344        };
345
346        let config = device.default_output_config().unwrap().config();
347        if config.channels != 2 {
348            panic!("TODO: Support downmixing multiple channels")
349        }
350
351        let resampler = Arc::new(RwLock::new(None));
352        let stream = create_output_stream(&device, &config, resampler.clone()).unwrap();
353
354        let mut state = State::Stopped;
355        let sample_rate = config.sample_rate.0 as usize;
356
357        //Force update the playback position when restoring the queue.
358        if let Some(song) = songs.selected() {
359            let file = match File::open(&song.path) {
360                Ok(file) => file,
361                Err(_) => {
362                    return Self {
363                        stream,
364                        resampler,
365                        sample_rate,
366                        state,
367                        songs,
368                        volume,
369                    }
370                }
371            };
372            let elapsed = Duration::from_secs_f32(elapsed);
373            let mut r = Resampler::new(sample_rate, file, volume, song.gain as f32);
374            //Elapsed will not update while paused so force update it.
375            r.elapsed = elapsed;
376            r.seek(elapsed).unwrap();
377            state = State::Paused;
378            *resampler.write().unwrap() = Some(r);
379        };
380
381        Self {
382            resampler,
383            sample_rate: config.sample_rate.0 as usize,
384            stream,
385            volume,
386            state,
387            songs,
388        }
389    }
390
391    pub fn set_output_device(&mut self, device: &Device) -> Result<(), String> {
392        //TODO: Pausing the stream and hanging the thread for 500ms
393        //gives the device enough time to release it's lock.
394        //My interface has two outputs on the same device and
395        //is unable to grab handles for each other while
396        //in use.
397
398        match device.default_output_config() {
399            Ok(supported_stream) => {
400                match create_output_stream(
401                    device,
402                    &supported_stream.config(),
403                    self.resampler.clone(),
404                ) {
405                    Ok(stream) => {
406                        self.stream = stream;
407                        self.sample_rate = supported_stream.sample_rate().0 as usize;
408
409                        if let Some(resampler) = self.resampler.write().unwrap().as_mut() {
410                            resampler.update_sample_rate(self.sample_rate);
411                        }
412
413                        self.stream.play().unwrap();
414                        Ok(())
415                    }
416                    Err(e) => match e {
417                        BuildStreamError::BackendSpecific { err } => Err(err.description),
418                        _ => Err(format!("{}", e)),
419                    },
420                }
421            }
422            Err(e) => Err(format!("{}", e)),
423        }
424    }
425
426    pub fn update(&mut self) -> Result<(), String> {
427        let mut next = false;
428        if let Some(resampler) = self.resampler.read().unwrap().as_ref() {
429            if resampler.finished {
430                next = true;
431            }
432        }
433
434        if next {
435            self.next()?;
436        }
437
438        Ok(())
439    }
440
441    pub fn add_songs(&mut self, songs: &[Song]) -> Result<(), String> {
442        self.songs.data.extend(songs.to_vec());
443        if self.songs.selected().is_none() {
444            self.songs.select(Some(0));
445            self.play_selected()
446        } else {
447            Ok(())
448        }
449    }
450
451    pub fn previous(&mut self) -> Result<(), String> {
452        self.songs.up();
453        self.play_selected()
454    }
455
456    pub fn next(&mut self) -> Result<(), String> {
457        self.songs.down();
458        self.play_selected()
459    }
460
461    fn play_selected(&mut self) -> Result<(), String> {
462        if let Some(song) = self.songs.selected() {
463            let file = match File::open(&song.path) {
464                Ok(file) => file,
465                //TODO: Error might be too vague.
466                Err(_) => return Err(format!("Could not open file: {:?}", song.path)),
467            };
468            if let Some(resampler) = self.resampler.write().unwrap().as_mut() {
469                resampler.finished = true;
470            }
471            *self.resampler.write().unwrap() = Some(Resampler::new(
472                self.sample_rate,
473                file,
474                self.volume,
475                song.gain as f32,
476            ));
477            self.play();
478        }
479        Ok(())
480    }
481
482    pub fn play_index(&mut self, i: usize) -> Result<(), String> {
483        self.songs.select(Some(i));
484        self.play_selected()
485    }
486
487    pub fn delete_index(&mut self, i: usize) -> Result<(), String> {
488        if self.songs.is_empty() {
489            return Ok(());
490        }
491        self.songs.data.remove(i);
492
493        if let Some(playing) = self.songs.index() {
494            let len = self.songs.len();
495
496            if len == 0 {
497                self.clear();
498            } else if i == playing && i == 0 {
499                if i == 0 {
500                    self.songs.select(Some(0));
501                }
502                return self.play_selected();
503            } else if i == playing && i == len {
504                self.songs.select(Some(len - 1));
505            } else if i < playing {
506                self.songs.select(Some(playing - 1));
507            }
508        };
509        Ok(())
510    }
511
512    pub fn clear(&mut self) {
513        self.songs = Index::default();
514        self.state = State::Stopped;
515        *self.resampler.write().unwrap() = None;
516    }
517
518    pub fn clear_except_playing(&mut self) {
519        if let Some(index) = self.songs.index() {
520            let playing = self.songs.data.remove(index);
521            self.songs = Index::new(vec![playing], Some(0));
522        }
523    }
524
525    pub fn volume_up(&mut self) {
526        self.volume += VOLUME_STEP;
527        if self.volume > 100 {
528            self.volume = 100;
529        }
530
531        if let Some(resampler) = self.resampler.write().unwrap().as_mut() {
532            resampler.set_volume(self.volume);
533        }
534    }
535
536    pub fn volume_down(&mut self) {
537        if self.volume != 0 {
538            self.volume -= VOLUME_STEP;
539        }
540
541        if let Some(resampler) = self.resampler.write().unwrap().as_mut() {
542            resampler.set_volume(self.volume);
543        }
544    }
545
546    pub fn duration(&self) -> Duration {
547        if let Some(resampler) = self.resampler.read().unwrap().as_ref() {
548            resampler.duration
549        } else {
550            Duration::default()
551        }
552    }
553
554    pub fn elapsed(&self) -> Duration {
555        if let Some(resampler) = self.resampler.read().unwrap().as_ref() {
556            resampler.elapsed
557        } else {
558            Duration::default()
559        }
560    }
561
562    pub fn toggle_playback(&mut self) -> Result<(), String> {
563        if self.resampler.read().unwrap().is_none() {
564            self.play_selected()
565        } else {
566            match self.state {
567                State::Playing => self.pause(),
568                State::Paused => self.play(),
569                State::Stopped => (),
570            };
571            Ok(())
572        }
573    }
574
575    pub fn play(&mut self) {
576        self.stream.play().unwrap();
577        self.state = State::Playing;
578    }
579
580    pub fn pause(&mut self) {
581        self.stream.pause().unwrap();
582        self.state = State::Paused;
583    }
584
585    pub fn seek_by(&mut self, time: f32) -> Result<(), String> {
586        let time = if let Some(resampler) = self.resampler.read().unwrap().as_ref() {
587            resampler.elapsed.as_secs_f32() + time
588        } else {
589            return Ok(());
590        };
591
592        if time > self.duration().as_secs_f32() {
593            self.next()?;
594        } else {
595            self.seek_to(time)?;
596            self.play();
597        }
598
599        Ok(())
600    }
601
602    pub fn seek_to(&mut self, time: f32) -> Result<(), String> {
603        //Seeking at under 0.5 seconds causes an unexpected EOF.
604        //Could be because of the coarse seek.
605        let time = Duration::from_secs_f32(time.clamp(0.5, f32::MAX));
606        if time > self.duration() {
607            self.next()?;
608        } else {
609            if let Some(resampler) = self.resampler.write().unwrap().as_mut() {
610                resampler.seek(time)?;
611            }
612            self.play();
613        }
614        Ok(())
615    }
616
617    pub fn is_playing(&self) -> bool {
618        State::Playing == self.state
619    }
620}
621
622unsafe impl Send for Player {}
623
624fn create_output_stream(
625    device: &Device,
626    config: &StreamConfig,
627    resampler: Arc<RwLock<Option<Resampler>>>,
628) -> Result<Stream, BuildStreamError> {
629    device.build_output_stream(
630        config,
631        move |data: &mut [f32], _: &cpal::OutputCallbackInfo| {
632            for frame in data.chunks_mut(2) {
633                for sample in frame.iter_mut() {
634                    let smp = if let Some(resampler) = resampler.write().unwrap().as_mut() {
635                        resampler.next()
636                    } else {
637                        0.0
638                    };
639                    *sample = smp;
640                }
641            }
642        },
643        |err| panic!("{}", err),
644    )
645}
646
647pub fn audio_devices() -> Vec<Device> {
648    let host_id = cpal::default_host().id();
649    let host = cpal::host_from_id(host_id).unwrap();
650
651    //FIXME: Getting just the output devies was too slow(150ms).
652    //Collecting every device is still slow but it's not as bad.
653    host.devices().unwrap().collect()
654}
655
656pub fn default_device() -> Device {
657    cpal::default_host().default_output_device().unwrap()
658}