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 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#[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}