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