Skip to main content

selene_daemon/
player.rs

1use std::{
2    fmt::Display,
3    sync::{
4        Arc, Mutex, PoisonError,
5        atomic::{AtomicU32, Ordering},
6        mpsc::{Receiver, RecvError, SendError, Sender},
7    },
8};
9
10use bitflags::bitflags;
11use blake3::Hash;
12use lunar_lib::{
13    config::ConfigError,
14    database::{Database, DatabaseError},
15};
16use rubato::ResamplerConstructionError;
17use selene_core::{
18    database::LibraryDb,
19    decoding::DecodingError,
20    library::{collection::Collectable, track::TrackId},
21    media_container::ContainerFormat,
22};
23use serde::{Deserialize, Serialize};
24use symphonia::core::errors::Error as SymphoniaError;
25use thiserror::Error;
26
27use crate::{
28    PlayerEvent,
29    decoder::{DecoderCommand, DecoderTx},
30    event_handler::EventTx,
31    player::playback::{CpalError, DeviceConfig},
32    playlist::{
33        AtomicPlaybackStatus, LoopMode, Playable, PlayableTrack, PlaybackStatus, Playlist,
34        ResolvedTrack, ShuffleMode,
35    },
36};
37
38mod opened_decoder;
39pub use opened_decoder::*;
40
41pub mod playback;
42
43mod ipc;
44pub use ipc::*;
45
46mod thread;
47
48#[derive(Debug, Error)]
49pub enum PlayerError {
50    #[error("{0}")]
51    Io(#[from] std::io::Error),
52
53    #[error("{0}")]
54    Config(#[from] ConfigError),
55
56    #[error("{0}")]
57    Cpal(#[from] CpalError),
58
59    #[error("{0}")]
60    Symphonia(#[from] SymphoniaError),
61
62    #[error("{0}")]
63    Database(#[from] DatabaseError),
64
65    #[error("{0}")]
66    Decoder(#[from] DecodingError),
67
68    #[error("{0}")]
69    Resampler(#[from] ResamplerConstructionError),
70
71    #[error("Player attempted to play an unsupported container: '{0:?}'")]
72    UnsupportedContainer(ContainerFormat),
73
74    #[error("A critical thread panicked holding an interior mutex")]
75    CriticalMutexPoisoned,
76
77    #[error("A sender or receiver to a critical thread disconnected")]
78    CriticalThreadDisconnected,
79}
80
81impl<T> From<PoisonError<T>> for PlayerError {
82    fn from(_value: PoisonError<T>) -> Self {
83        Self::CriticalMutexPoisoned
84    }
85}
86
87impl From<RecvError> for PlayerError {
88    fn from(_value: RecvError) -> Self {
89        Self::CriticalThreadDisconnected
90    }
91}
92
93impl<T> From<SendError<T>> for PlayerError {
94    fn from(_value: SendError<T>) -> Self {
95        Self::CriticalThreadDisconnected
96    }
97}
98
99pub struct Player {
100    rx: Receiver<PlayerRequest>,
101    decoder_tx: Sender<DecoderCommand>,
102
103    event_tx: Sender<PlayerEvent>,
104
105    playlist: Playlist,
106
107    device_config: Arc<Mutex<DeviceConfig>>,
108    playback_state: Arc<AtomicPlaybackStatus>,
109    volume: Arc<AtomicU32>,
110}
111
112impl Player {
113    /// Sets or increments the volume to the input volume, clamps between `[0..1]`
114    fn set_volume(&mut self, volume: f32, increment: bool) -> f32 {
115        let volume = if increment {
116            f32::from_bits(self.volume.load(Ordering::Relaxed)) + volume
117        } else {
118            volume
119        }
120        .clamp(0.0, 1.0);
121
122        self.event_tx.event(PlayerEvent::VolumeChanged { volume });
123        self.volume.store(volume.to_bits(), Ordering::Relaxed);
124
125        volume
126    }
127
128    pub fn load(&self, playable: ResolvedTrack) -> Result<(), PlayerError> {
129        let output_sample_rate = self.device_config.lock()?.config.sample_rate() as usize;
130        let decoder = OpenedDecoder::new(playable, output_sample_rate)?;
131        self.decoder_tx.load(decoder)?;
132        Ok(())
133    }
134
135    pub fn preload(&self, playable: ResolvedTrack) -> Result<(), PlayerError> {
136        let output_sample_rate = self.device_config.lock()?.config.sample_rate() as usize;
137        let decoder = OpenedDecoder::new(playable, output_sample_rate)?;
138        self.decoder_tx.preload(decoder)?;
139        Ok(())
140    }
141
142    fn replace_decoders(&mut self, pop: bool) -> Result<bool, PlayerError> {
143        let db = LibraryDb::open().unwrap();
144        let load = if pop {
145            self.playlist.pop_next(&db)?
146        } else {
147            self.playlist.current(&db)?
148        };
149        let preload = self.playlist.peek_next(&db)?;
150
151        if let Some(load) = load {
152            let output_sample_rate = self.device_config.lock()?.config.sample_rate() as usize;
153            let load = OpenedDecoder::new(load, output_sample_rate)?;
154            if let Some(preload) = preload {
155                let preload = OpenedDecoder::new(preload, output_sample_rate)?;
156                self.decoder_tx.load_and_preload(load, preload)?;
157                return Ok(true);
158            }
159
160            self.decoder_tx.load(load)?;
161            Ok(true)
162        } else {
163            self.decoder_tx.stop()?;
164            Ok(false)
165        }
166    }
167}
168
169bitflags! {
170    #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
171    pub struct PlayerQueryFlags: u16 {
172        const PLAYBACK_STATE        = 1 << 0;
173
174        const CURRENTLY_PLAYING     = 1 << 1;
175        const CURRENTLY_PLAYING_RAW = 1 << 2;
176
177        const VOLUME                = 1 << 3;
178        const TIME                  = 1 << 4;
179
180        const QUEUE                 = 1 << 5;
181        const QUEUE_RAW             = 1 << 6;
182        const QUEUE_STATE           = 1 << 7;
183
184        const PLAYLIST              = 1 << 8;
185        const PLAYLIST_RAW          = 1 << 9;
186        const PLAYLIST_STATE        = 1 << 10;
187
188        const TRACKLIST             = 1 << 11;
189        const TRACKLIST_RAW         = 1 << 12;
190        const TRACKLIST_POSITION    = 1 << 13;
191
192        const SHUFFLE_MODE          = 1 << 14;
193        const LOOP_MODE             = 1 << 15;
194
195        const ALL_SIMPLE = Self::PLAYBACK_STATE.bits()
196            | Self::CURRENTLY_PLAYING_RAW.bits()
197            | Self::VOLUME.bits()
198            | Self::TIME.bits()
199            | Self::QUEUE.bits()
200            | Self::PLAYLIST.bits()
201            | Self::TRACKLIST.bits()
202            | Self::TRACKLIST_POSITION.bits()
203            | Self::SHUFFLE_MODE.bits()
204            | Self::LOOP_MODE.bits();
205    }
206}
207
208/// Wrapper around [`Option`] for ciborium ser/de workaround
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub enum QueryWrapper<T> {
211    None,
212    Some(T),
213}
214
215impl<T> QueryWrapper<T> {
216    pub fn into_option(self) -> Option<T> {
217        match self {
218            QueryWrapper::None => None,
219            QueryWrapper::Some(t) => Some(t),
220        }
221    }
222
223    pub fn as_option(&self) -> Option<&T> {
224        match self {
225            QueryWrapper::None => None,
226            QueryWrapper::Some(t) => Some(t),
227        }
228    }
229}
230
231impl<T> From<Option<T>> for QueryWrapper<T> {
232    fn from(value: Option<T>) -> Self {
233        match value {
234            Some(t) => QueryWrapper::Some(t),
235            None => QueryWrapper::None,
236        }
237    }
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize, Default)]
241pub struct QueryResult {
242    pub playback_state: Option<PlaybackStatus>,
243
244    pub currently_playing: Option<QueryWrapper<TrackId>>,
245    pub currently_playing_raw: Option<QueryWrapper<ResolvedTrack>>,
246
247    pub volume: Option<f32>,
248    pub time: Option<QueryWrapper<f64>>,
249
250    pub queue: Option<Vec<TrackId>>,
251    pub queue_raw: Option<Vec<PlayableTrack>>,
252    pub queue_state: Option<Hash>,
253
254    pub playlist: Option<Vec<Collectable>>,
255    pub playlist_raw: Option<Vec<Playable>>,
256    pub playlist_state: Option<Hash>,
257
258    pub tracklist: Option<Vec<TrackId>>,
259    pub tracklist_raw: Option<Vec<PlayableTrack>>,
260    pub tracklist_position: Option<(usize, usize)>,
261
262    pub shuffle_mode: Option<ShuffleMode>,
263    pub loop_mode: Option<LoopMode>,
264}
265
266impl Display for QueryResult {
267    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268        if let Some(playback_state) = self.playback_state {
269            writeln!(f, "Playback State: {playback_state}")?;
270        }
271
272        if let Some(tracklist_track) = &self.currently_playing {
273            if let Some(tracklist_track) = tracklist_track.as_option() {
274                writeln!(f, "Currently Playing: {}", tracklist_track.to_selene_id())?;
275            } else {
276                f.write_str("Currently Playing: None\n")?;
277            }
278        }
279
280        if let Some(volume) = self.volume {
281            writeln!(f, "Volume: {volume}")?;
282        }
283
284        if let Some(time) = &self.time {
285            let time = time.as_option().unwrap_or(&0.0);
286            writeln!(f, "Time: {time}")?;
287        }
288
289        if let Some(queue) = &self.queue {
290            writeln!(
291                f,
292                "Queue: {}",
293                queue
294                    .iter()
295                    .map(TrackId::to_selene_id)
296                    .collect::<Vec<_>>()
297                    .join(";")
298            )?;
299        }
300
301        if let Some(state) = self.queue_state {
302            writeln!(f, "Queue State: {state}")?;
303        }
304
305        if let Some(playlist) = &self.playlist {
306            writeln!(
307                f,
308                "Playlist: {}",
309                playlist
310                    .iter()
311                    .map(Collectable::to_selene_id)
312                    .collect::<Vec<_>>()
313                    .join(";")
314            )?;
315        }
316
317        if let Some(state) = self.playlist_state {
318            writeln!(f, "State: {state}")?;
319        }
320
321        if let Some(shuffle_mode) = self.shuffle_mode {
322            writeln!(f, "Shuffle Mode: {shuffle_mode}")?;
323        }
324
325        if let Some(loop_mode) = self.loop_mode {
326            writeln!(f, "Loop Mode: {loop_mode}")?;
327        }
328
329        Ok(())
330    }
331}