use std::{
sync::mpsc::{Receiver, Sender, TryRecvError, channel},
thread::{self},
};
use lunar_lib::{debug, error, trace};
use ringbuf::traits::Observer;
use crate::{
PlayerEvent,
daemon::DaemonError,
listener::{IpcListener, Listener},
player::playback::{CpalError, CpalHandle, DeviceConfig},
playlist::{Playlist, TracklistTrack},
wait,
};
#[cfg(feature = "mpris")]
mod mpris;
#[cfg(feature = "mpris")]
use mpris::{MprisConnector, MprisEvent};
mod opened_decoder;
pub use opened_decoder::*;
pub mod playback;
mod ipc;
pub use ipc::*;
pub struct Player {
rx: Receiver<PlayerRequest>,
listener_tx: Sender<PlayerEvent>,
#[cfg(feature = "mpris")]
mpris_tx: tokio::sync::mpsc::Sender<MprisEvent>,
playlist: Playlist,
cpal_handle: Option<CpalHandle>,
device_config: DeviceConfig,
decoder: Option<OpenedDecoder>,
preload: Option<OpenedDecoder>,
is_playing: bool,
volume: f32,
}
impl Player {
fn new() -> Result<(Sender<PlayerRequest>, Self), DaemonError> {
let (engine_tx, rx) = channel();
let listener_tx = Listener::open(engine_tx.clone())?;
#[cfg(feature = "mpris")]
let mpris_tx = MprisConnector::open(engine_tx.clone());
Ok((
engine_tx,
Self {
rx,
listener_tx,
playlist: Playlist::new(),
cpal_handle: None,
device_config: DeviceConfig::default_config()?,
decoder: None,
preload: None,
is_playing: false,
volume: 1.0,
#[cfg(feature = "mpris")]
mpris_tx,
},
))
}
pub fn open() -> Result<Sender<PlayerRequest>, DaemonError> {
let (player_tx, player) = Self::new()?;
thread::spawn(move || {
if let Err(err) = player.run() {
error!("Engine failed with error: {err}");
}
});
Ok(player_tx)
}
pub fn start() -> Result<(), DaemonError> {
let (_, player) = Self::new()?;
player.run()
}
fn run(mut self) -> Result<(), DaemonError> {
debug!("Engine thread started");
loop {
match self.rx.try_recv() {
Ok(PlayerRequest::Quit) => todo!(),
Ok(PlayerRequest::Ipc { command, callback }) => {
let response = self.run_command(*command)?;
callback.send(response)?;
}
Ok(PlayerRequest::Mpris { command }) => {
let _ = self.run_command(*command)?;
}
Err(TryRecvError::Empty) => (),
Err(TryRecvError::Disconnected) => {
return Ok(());
}
};
if !self.is_playing {
wait();
continue;
}
self.try_fill_decoders()?;
if self.decoder.is_none() {
wait();
continue;
}
self.ensure_cpal_handle()?;
if let Some(finished_consuming) = self
.cpal_handle
.as_mut()
.unwrap()
.consume_packet(self.volume)
&& !finished_consuming
{
wait();
continue;
}
if self.decoder.as_ref().unwrap().at_eof && !self.consume_preload()? {
if self.cpal_handle.as_mut().unwrap().audio_buf.is_empty() {
self.decoder = None;
self.event(PlayerEvent::CurrentlyPlayingChanged {
currently_playing: None,
});
self.set_is_playing(false);
self.is_playing = false;
self.cpal_handle = None;
} else {
wait();
}
continue;
}
let packet = self.decoder.as_mut().unwrap().decode_next_packet()?;
self.cpal_handle.as_mut().unwrap().pending_packet = packet;
}
}
fn event(&self, event: PlayerEvent) {
trace!("Sending event: {event}");
let _ = self.listener_tx.send(event.clone());
#[cfg(feature = "mpris")]
{
let mpris_event = match event {
PlayerEvent::CurrentlyPlayingChanged { currently_playing } => {
Some(MprisEvent::CurrentlyPlayingChanged { currently_playing })
}
PlayerEvent::IsPlayingChanged { is_playing } => {
let changed_at = self.decoder.as_ref().map(|d| d.time());
Some(MprisEvent::IsPlayingChanged {
is_playing,
changed_at,
})
}
PlayerEvent::ShuffleModeChanged { shuffle_mode } => {
Some(MprisEvent::ShuffleStatusChanged { shuffle_mode })
}
PlayerEvent::LoopModeChanged { loop_mode } => {
Some(MprisEvent::LoopStatusChanged { loop_mode })
}
PlayerEvent::VolumeChanged { volume } => Some(MprisEvent::VolumeChanged { volume }),
PlayerEvent::SeekOccured { time } => Some(MprisEvent::Seeked { time }),
PlayerEvent::Shutdown => Some(MprisEvent::Shutdown),
_ => None,
};
if let Some(mpris_event) = mpris_event {
let _ = self.mpris_tx.blocking_send(mpris_event);
}
}
}
}
impl Player {
pub fn ensure_cpal_handle(&mut self) -> Result<(), CpalError> {
if self.cpal_handle.is_none() {
self.cpal_handle = Some(CpalHandle::open(&self.device_config)?);
};
Ok(())
}
fn replace_decoders(&mut self) -> Result<(), DaemonError> {
if let Some(pop_next) = self.playlist.pop_next().cloned() {
self.load(pop_next)?;
} else {
self.unload();
}
if let Some(peek_next) = self.playlist.peek_next().cloned() {
self.preload(peek_next)?
} else {
self.preload = None;
}
Ok(())
}
fn try_fill_decoders(&mut self) -> Result<(), DaemonError> {
if self.decoder.is_none() && !self.consume_preload()? {
if let Some(pop_next) = self.playlist.pop_next().cloned() {
self.load(pop_next)?;
} else {
return Ok(());
}
}
if self.preload.is_none()
&& let Some(peek_next) = self.playlist.peek_next().cloned()
{
self.preload(peek_next)?;
}
Ok(())
}
pub fn load(&mut self, playable: TracklistTrack) -> Result<(), DaemonError> {
self.decoder = Some(OpenedDecoder::from_tracklist_track(
playable.clone(),
self.device_config.config.sample_rate() as usize,
)?);
self.event(PlayerEvent::CurrentlyPlayingChanged {
currently_playing: Some(playable),
});
Ok(())
}
pub fn unload(&mut self) {
self.decoder = None;
self.preload = None;
self.event(PlayerEvent::CurrentlyPlayingChanged {
currently_playing: None,
});
}
pub fn preload(&mut self, playable: TracklistTrack) -> Result<(), DaemonError> {
self.preload = Some(OpenedDecoder::from_tracklist_track(
playable,
self.device_config.config.sample_rate() as usize,
)?);
Ok(())
}
pub fn take_preload_or_load(&mut self) -> Result<Option<OpenedDecoder>, DaemonError> {
if let Some(preload) = self.preload.take() {
self.playlist.pop_next();
return Ok(Some(preload));
}
self.playlist
.pop_next()
.map(|p| {
OpenedDecoder::from_tracklist_track(
p.clone(),
self.device_config.config.sample_rate() as usize,
)
})
.transpose()
}
pub fn consume_preload(&mut self) -> Result<bool, DaemonError> {
let state = self.decoder.is_some();
self.decoder = self.take_preload_or_load()?;
let currently_playing = self.decoder.as_ref().map(|d| d.decoded_from.clone());
if self.decoder.is_some() || state {
self.event(PlayerEvent::CurrentlyPlayingChanged { currently_playing });
}
if let Some(peek_next) = self.playlist.peek_next().cloned() {
self.preload(peek_next)?
}
Ok(self.decoder.is_some())
}
pub fn clear_audio_buf(&self) {
if let Some(cpal_handle) = &self.cpal_handle {
cpal_handle.clear_buf().unwrap();
}
}
fn set_volume(&mut self, volume: f32, increment: bool) -> f32 {
let volume = if increment {
self.volume + volume
} else {
volume
};
self.volume = volume.clamp(0.0, 1.0);
self.event(PlayerEvent::VolumeChanged {
volume: self.volume,
});
self.volume
}
fn set_is_playing(&mut self, playing: bool) {
self.is_playing = playing;
if !playing {
self.clear_audio_buf();
}
self.event(PlayerEvent::IsPlayingChanged {
is_playing: playing,
});
}
}