use std::{
fmt::Display,
sync::{
Arc, Mutex, PoisonError,
atomic::{AtomicU32, Ordering},
mpsc::{Receiver, RecvError, SendError, Sender},
},
};
use bitflags::bitflags;
use blake3::Hash;
use lunar_lib::{
config::ConfigError,
database::{Database, DatabaseError},
};
use rubato::ResamplerConstructionError;
use selene_core::{
database::LibraryDb,
decoding::DecodingError,
library::{collection::Collectable, track::TrackId},
media_container::ContainerFormat,
};
use serde::{Deserialize, Serialize};
use symphonia::core::errors::Error as SymphoniaError;
use thiserror::Error;
use crate::{
PlayerEvent,
decoder::{DecoderCommand, DecoderTx},
event_handler::EventTx,
player::playback::{CpalError, DeviceConfig},
playlist::{
AtomicPlaybackStatus, LoopMode, Playable, PlayableTrack, PlaybackStatus, Playlist,
ResolvedTrack, ShuffleMode,
},
};
mod opened_decoder;
pub use opened_decoder::*;
pub mod playback;
mod ipc;
pub use ipc::*;
mod thread;
#[derive(Debug, Error)]
pub enum PlayerError {
#[error("{0}")]
Io(#[from] std::io::Error),
#[error("{0}")]
Config(#[from] ConfigError),
#[error("{0}")]
Cpal(#[from] CpalError),
#[error("{0}")]
Symphonia(#[from] SymphoniaError),
#[error("{0}")]
Database(#[from] DatabaseError),
#[error("{0}")]
Decoder(#[from] DecodingError),
#[error("{0}")]
Resampler(#[from] ResamplerConstructionError),
#[error("Player attempted to play an unsupported container: '{0:?}'")]
UnsupportedContainer(ContainerFormat),
#[error("A critical thread panicked holding an interior mutex")]
CriticalMutexPoisoned,
#[error("A sender or receiver to a critical thread disconnected")]
CriticalThreadDisconnected,
}
impl<T> From<PoisonError<T>> for PlayerError {
fn from(_value: PoisonError<T>) -> Self {
Self::CriticalMutexPoisoned
}
}
impl From<RecvError> for PlayerError {
fn from(_value: RecvError) -> Self {
Self::CriticalThreadDisconnected
}
}
impl<T> From<SendError<T>> for PlayerError {
fn from(_value: SendError<T>) -> Self {
Self::CriticalThreadDisconnected
}
}
pub struct Player {
rx: Receiver<PlayerRequest>,
decoder_tx: Sender<DecoderCommand>,
event_tx: Sender<PlayerEvent>,
playlist: Playlist,
device_config: Arc<Mutex<DeviceConfig>>,
playback_state: Arc<AtomicPlaybackStatus>,
volume: Arc<AtomicU32>,
}
impl Player {
fn set_volume(&mut self, volume: f32, increment: bool) -> f32 {
let volume = if increment {
f32::from_bits(self.volume.load(Ordering::Relaxed)) + volume
} else {
volume
}
.clamp(0.0, 1.0);
self.event_tx.event(PlayerEvent::VolumeChanged { volume });
self.volume.store(volume.to_bits(), Ordering::Relaxed);
volume
}
pub fn load(&self, playable: ResolvedTrack) -> Result<(), PlayerError> {
let output_sample_rate = self.device_config.lock()?.config.sample_rate() as usize;
let decoder = OpenedDecoder::new(playable, output_sample_rate)?;
self.decoder_tx.load(decoder)?;
Ok(())
}
pub fn preload(&self, playable: ResolvedTrack) -> Result<(), PlayerError> {
let output_sample_rate = self.device_config.lock()?.config.sample_rate() as usize;
let decoder = OpenedDecoder::new(playable, output_sample_rate)?;
self.decoder_tx.preload(decoder)?;
Ok(())
}
fn replace_decoders(&mut self, pop: bool) -> Result<bool, PlayerError> {
let db = LibraryDb::open().unwrap();
let load = if pop {
self.playlist.pop_next(&db)?
} else {
self.playlist.current(&db)?
};
let preload = self.playlist.peek_next(&db)?;
if let Some(load) = load {
let output_sample_rate = self.device_config.lock()?.config.sample_rate() as usize;
let load = OpenedDecoder::new(load, output_sample_rate)?;
if let Some(preload) = preload {
let preload = OpenedDecoder::new(preload, output_sample_rate)?;
self.decoder_tx.load_and_preload(load, preload)?;
return Ok(true);
}
self.decoder_tx.load(load)?;
Ok(true)
} else {
self.decoder_tx.stop()?;
Ok(false)
}
}
}
bitflags! {
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct PlayerQueryFlags: u16 {
const PLAYBACK_STATE = 1 << 0;
const CURRENTLY_PLAYING = 1 << 1;
const CURRENTLY_PLAYING_RAW = 1 << 2;
const VOLUME = 1 << 3;
const TIME = 1 << 4;
const QUEUE = 1 << 5;
const QUEUE_RAW = 1 << 6;
const QUEUE_STATE = 1 << 7;
const PLAYLIST = 1 << 8;
const PLAYLIST_RAW = 1 << 9;
const PLAYLIST_STATE = 1 << 10;
const TRACKLIST = 1 << 11;
const TRACKLIST_RAW = 1 << 12;
const TRACKLIST_POSITION = 1 << 13;
const SHUFFLE_MODE = 1 << 14;
const LOOP_MODE = 1 << 15;
const ALL_SIMPLE = Self::PLAYBACK_STATE.bits()
| Self::CURRENTLY_PLAYING_RAW.bits()
| Self::VOLUME.bits()
| Self::TIME.bits()
| Self::QUEUE.bits()
| Self::PLAYLIST.bits()
| Self::TRACKLIST.bits()
| Self::TRACKLIST_POSITION.bits()
| Self::SHUFFLE_MODE.bits()
| Self::LOOP_MODE.bits();
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum QueryWrapper<T> {
None,
Some(T),
}
impl<T> QueryWrapper<T> {
pub fn into_option(self) -> Option<T> {
match self {
QueryWrapper::None => None,
QueryWrapper::Some(t) => Some(t),
}
}
pub fn as_option(&self) -> Option<&T> {
match self {
QueryWrapper::None => None,
QueryWrapper::Some(t) => Some(t),
}
}
}
impl<T> From<Option<T>> for QueryWrapper<T> {
fn from(value: Option<T>) -> Self {
match value {
Some(t) => QueryWrapper::Some(t),
None => QueryWrapper::None,
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct QueryResult {
pub playback_state: Option<PlaybackStatus>,
pub currently_playing: Option<QueryWrapper<TrackId>>,
pub currently_playing_raw: Option<QueryWrapper<ResolvedTrack>>,
pub volume: Option<f32>,
pub time: Option<QueryWrapper<f64>>,
pub queue: Option<Vec<TrackId>>,
pub queue_raw: Option<Vec<PlayableTrack>>,
pub queue_state: Option<Hash>,
pub playlist: Option<Vec<Collectable>>,
pub playlist_raw: Option<Vec<Playable>>,
pub playlist_state: Option<Hash>,
pub tracklist: Option<Vec<TrackId>>,
pub tracklist_raw: Option<Vec<PlayableTrack>>,
pub tracklist_position: Option<(usize, usize)>,
pub shuffle_mode: Option<ShuffleMode>,
pub loop_mode: Option<LoopMode>,
}
impl Display for QueryResult {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if let Some(playback_state) = self.playback_state {
writeln!(f, "Playback State: {playback_state}")?;
}
if let Some(tracklist_track) = &self.currently_playing {
if let Some(tracklist_track) = tracklist_track.as_option() {
writeln!(f, "Currently Playing: {}", tracklist_track.to_selene_id())?;
} else {
f.write_str("Currently Playing: None\n")?;
}
}
if let Some(volume) = self.volume {
writeln!(f, "Volume: {volume}")?;
}
if let Some(time) = &self.time {
let time = time.as_option().unwrap_or(&0.0);
writeln!(f, "Time: {time}")?;
}
if let Some(queue) = &self.queue {
writeln!(
f,
"Queue: {}",
queue
.iter()
.map(TrackId::to_selene_id)
.collect::<Vec<_>>()
.join(";")
)?;
}
if let Some(state) = self.queue_state {
writeln!(f, "Queue State: {state}")?;
}
if let Some(playlist) = &self.playlist {
writeln!(
f,
"Playlist: {}",
playlist
.iter()
.map(Collectable::to_selene_id)
.collect::<Vec<_>>()
.join(";")
)?;
}
if let Some(state) = self.playlist_state {
writeln!(f, "State: {state}")?;
}
if let Some(shuffle_mode) = self.shuffle_mode {
writeln!(f, "Shuffle Mode: {shuffle_mode}")?;
}
if let Some(loop_mode) = self.loop_mode {
writeln!(f, "Loop Mode: {loop_mode}")?;
}
Ok(())
}
}