2022-01-04 02:15:28 +00:00
|
|
|
use std::sync::mpsc::{Receiver, Sender};
|
2021-12-10 21:53:22 +00:00
|
|
|
use std::{thread, thread::JoinHandle};
|
|
|
|
|
|
|
|
use mps_interpreter::tokens::MpsTokenReader;
|
2022-01-21 00:52:03 +00:00
|
|
|
use mps_interpreter::MpsItem;
|
2021-12-10 21:53:22 +00:00
|
|
|
|
|
|
|
use super::MpsPlayer;
|
|
|
|
use super::PlaybackError;
|
|
|
|
|
2022-01-04 02:15:28 +00:00
|
|
|
/// A wrapper around MpsPlayer so that playback can occur on a different thread.
|
2022-01-05 22:37:13 +00:00
|
|
|
/// This allows for message passing between the player and controller.
|
2022-01-04 02:15:28 +00:00
|
|
|
///
|
|
|
|
/// You will probably never directly interact with this, instead using MpsController to communicate.
|
2021-12-10 21:53:22 +00:00
|
|
|
pub struct MpsPlayerServer<T: MpsTokenReader> {
|
|
|
|
player: MpsPlayer<T>,
|
|
|
|
control: Receiver<ControlAction>,
|
|
|
|
event: Sender<PlayerAction>,
|
2022-01-05 22:37:13 +00:00
|
|
|
playback: Sender<PlaybackAction>,
|
2022-01-03 22:53:57 +00:00
|
|
|
keep_alive: bool,
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<T: MpsTokenReader> MpsPlayerServer<T> {
|
2022-01-04 02:15:28 +00:00
|
|
|
pub fn new(
|
|
|
|
player: MpsPlayer<T>,
|
|
|
|
ctrl: Receiver<ControlAction>,
|
|
|
|
event: Sender<PlayerAction>,
|
2022-01-05 22:37:13 +00:00
|
|
|
playback: Sender<PlaybackAction>,
|
2022-01-04 02:15:28 +00:00
|
|
|
keep_alive: bool,
|
|
|
|
) -> Self {
|
2021-12-10 21:53:22 +00:00
|
|
|
Self {
|
|
|
|
player: player,
|
|
|
|
control: ctrl,
|
|
|
|
event: event,
|
2022-01-05 22:37:13 +00:00
|
|
|
playback: playback,
|
2022-01-03 22:53:57 +00:00
|
|
|
keep_alive: keep_alive,
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-05 22:37:13 +00:00
|
|
|
fn enqeue_some(&mut self, count: usize) {
|
|
|
|
//println!("Enqueuing up to {} items", count);
|
|
|
|
match self.player.enqueue(count) {
|
|
|
|
Err(e) => self.event.send(PlayerAction::Exception(e)).unwrap(),
|
2022-01-24 21:12:29 +00:00
|
|
|
Ok(items) => {
|
|
|
|
for item in items {
|
|
|
|
// notify of new items that have been enqueued
|
|
|
|
self.playback.send(PlaybackAction::Enqueued(item)).unwrap();
|
|
|
|
}
|
|
|
|
}
|
2022-01-05 22:37:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn on_empty(&self) {
|
|
|
|
self.event.send(PlayerAction::Empty).unwrap();
|
|
|
|
self.playback.send(PlaybackAction::Empty).unwrap();
|
|
|
|
}
|
|
|
|
|
|
|
|
fn on_end(&self) {
|
|
|
|
self.event.send(PlayerAction::End).unwrap();
|
|
|
|
self.playback.send(PlaybackAction::Exit).unwrap();
|
|
|
|
}
|
|
|
|
|
2021-12-10 21:53:22 +00:00
|
|
|
fn run_loop(&mut self) {
|
|
|
|
// this can panic since it's not on the main thread
|
2022-01-05 22:37:13 +00:00
|
|
|
// initial queue fill
|
|
|
|
self.enqeue_some(1);
|
2022-01-03 22:53:57 +00:00
|
|
|
let mut is_empty = self.player.queue_len() == 0;
|
2021-12-10 21:53:22 +00:00
|
|
|
loop {
|
|
|
|
let command = self.control.recv().unwrap();
|
|
|
|
|
2021-12-12 20:08:49 +00:00
|
|
|
let mut is_exiting = false;
|
2021-12-10 21:53:22 +00:00
|
|
|
|
2022-01-03 22:53:57 +00:00
|
|
|
let mut check_empty = false;
|
|
|
|
|
2021-12-10 21:53:22 +00:00
|
|
|
// process command
|
|
|
|
match command {
|
2022-01-04 02:15:28 +00:00
|
|
|
ControlAction::Next { .. } => {
|
2021-12-12 20:08:49 +00:00
|
|
|
//println!("Executing next command (queue_len: {})", self.player.queue_len());
|
|
|
|
if let Err(e) = self.player.new_sink() {
|
|
|
|
self.event.send(PlayerAction::Exception(e)).unwrap();
|
|
|
|
}
|
|
|
|
if !self.player.is_paused() {
|
2022-01-05 22:37:13 +00:00
|
|
|
self.enqeue_some(1);
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
2022-01-04 02:15:28 +00:00
|
|
|
}
|
|
|
|
ControlAction::Previous { .. } => {} // TODO
|
|
|
|
ControlAction::Play { .. } => self.player.resume(),
|
|
|
|
ControlAction::Pause { .. } => self.player.pause(),
|
|
|
|
ControlAction::PlayPause { .. } => {
|
2021-12-10 21:53:22 +00:00
|
|
|
if self.player.is_paused() {
|
|
|
|
self.player.resume();
|
|
|
|
} else {
|
|
|
|
self.player.pause();
|
|
|
|
}
|
2022-01-04 02:15:28 +00:00
|
|
|
}
|
|
|
|
ControlAction::Stop { .. } => self.player.stop(),
|
|
|
|
ControlAction::Exit { .. } => {
|
2021-12-10 21:53:22 +00:00
|
|
|
self.player.stop();
|
|
|
|
is_exiting = true;
|
2022-01-04 02:15:28 +00:00
|
|
|
}
|
|
|
|
ControlAction::Enqueue { amount, .. } => {
|
2022-01-05 22:37:13 +00:00
|
|
|
self.enqeue_some(amount);
|
2022-01-04 02:15:28 +00:00
|
|
|
}
|
|
|
|
ControlAction::NoOp { .. } => {} // empty by design
|
|
|
|
ControlAction::SetVolume { volume, .. } => {
|
2021-12-10 21:53:22 +00:00
|
|
|
self.player.set_volume((volume as f32) / (u32::MAX as f32));
|
2022-01-04 02:15:28 +00:00
|
|
|
}
|
|
|
|
ControlAction::CheckEmpty { .. } => {
|
2022-01-03 22:53:57 +00:00
|
|
|
check_empty = true;
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
2021-12-12 20:08:49 +00:00
|
|
|
|
|
|
|
// keep queue full (while playing music)
|
|
|
|
if self.player.queue_len() == 0 && !self.player.is_paused() && !is_exiting {
|
2022-01-05 22:37:13 +00:00
|
|
|
self.enqeue_some(1);
|
2022-01-04 02:15:28 +00:00
|
|
|
if self.player.queue_len() == 0 {
|
|
|
|
// no more music to add
|
2022-01-03 22:53:57 +00:00
|
|
|
is_exiting = !self.keep_alive || is_exiting;
|
2021-12-12 20:08:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-10 21:53:22 +00:00
|
|
|
if command.needs_ack() {
|
|
|
|
self.event.send(PlayerAction::Acknowledge(command)).unwrap();
|
|
|
|
}
|
|
|
|
|
2022-01-03 22:53:57 +00:00
|
|
|
// always check for empty state change
|
2022-01-04 02:15:28 +00:00
|
|
|
if self.player.queue_len() == 0 && !is_empty {
|
|
|
|
// just became empty
|
2022-01-03 22:53:57 +00:00
|
|
|
is_empty = true;
|
2022-01-05 22:37:13 +00:00
|
|
|
self.on_empty();
|
2022-01-04 02:15:28 +00:00
|
|
|
} else if self.player.queue_len() != 0 && is_empty {
|
|
|
|
// just got filled
|
2022-01-03 22:53:57 +00:00
|
|
|
is_empty = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if is_empty && check_empty {
|
2022-01-05 22:37:13 +00:00
|
|
|
self.on_empty();
|
2022-01-03 22:53:57 +00:00
|
|
|
}
|
|
|
|
|
2022-01-04 02:15:28 +00:00
|
|
|
if is_exiting {
|
|
|
|
break;
|
|
|
|
}
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
2022-01-05 22:37:13 +00:00
|
|
|
//println!("Exiting playback server");
|
|
|
|
self.on_end();
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn spawn<F: FnOnce() -> MpsPlayer<T> + Send + 'static>(
|
|
|
|
factory: F,
|
|
|
|
ctrl_tx: Sender<ControlAction>,
|
|
|
|
ctrl_rx: Receiver<ControlAction>,
|
2022-01-03 22:53:57 +00:00
|
|
|
event: Sender<PlayerAction>,
|
2022-01-05 22:37:13 +00:00
|
|
|
playback: Sender<PlaybackAction>,
|
2022-01-04 02:15:28 +00:00
|
|
|
keep_alive: bool,
|
2021-12-10 21:53:22 +00:00
|
|
|
) -> JoinHandle<()> {
|
|
|
|
thread::spawn(move || Self::unblocking_timer_loop(ctrl_tx, 50));
|
|
|
|
thread::spawn(move || {
|
|
|
|
let player = factory();
|
2022-01-05 22:37:13 +00:00
|
|
|
let mut server_obj = Self::new(player, ctrl_rx, event, playback, keep_alive);
|
2021-12-10 21:53:22 +00:00
|
|
|
server_obj.run_loop();
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn unblocking_timer_loop(ctrl_tx: Sender<ControlAction>, sleep_ms: u64) {
|
|
|
|
let dur = std::time::Duration::from_millis(sleep_ms);
|
|
|
|
loop {
|
2022-01-04 02:15:28 +00:00
|
|
|
if let Err(_) = ctrl_tx.send(ControlAction::NoOp { ack: false }) {
|
2021-12-10 21:53:22 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
thread::sleep(dur);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Action the controller wants the player to perform
|
|
|
|
#[allow(dead_code)]
|
2021-12-12 20:08:49 +00:00
|
|
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
2021-12-10 21:53:22 +00:00
|
|
|
pub enum ControlAction {
|
2022-01-04 02:15:28 +00:00
|
|
|
Next { ack: bool },
|
|
|
|
Previous { ack: bool },
|
|
|
|
Play { ack: bool },
|
|
|
|
Pause { ack: bool },
|
|
|
|
PlayPause { ack: bool },
|
|
|
|
Stop { ack: bool },
|
|
|
|
Exit { ack: bool },
|
|
|
|
Enqueue { amount: usize, ack: bool },
|
|
|
|
NoOp { ack: bool },
|
|
|
|
SetVolume { ack: bool, volume: u32 },
|
|
|
|
CheckEmpty { ack: bool },
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl ControlAction {
|
|
|
|
fn needs_ack(&self) -> bool {
|
|
|
|
*match self {
|
2022-01-04 02:15:28 +00:00
|
|
|
Self::Next { ack } => ack,
|
|
|
|
Self::Previous { ack } => ack,
|
|
|
|
Self::Play { ack } => ack,
|
|
|
|
Self::Pause { ack } => ack,
|
|
|
|
Self::PlayPause { ack } => ack,
|
|
|
|
Self::Stop { ack } => ack,
|
|
|
|
Self::Exit { ack } => ack,
|
|
|
|
Self::Enqueue { ack, .. } => ack,
|
|
|
|
Self::NoOp { ack, .. } => ack,
|
|
|
|
Self::SetVolume { ack, .. } => ack,
|
|
|
|
Self::CheckEmpty { ack } => ack,
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Action the player has performed/encountered
|
2021-12-12 20:08:49 +00:00
|
|
|
#[derive(Clone, Debug)]
|
2021-12-10 21:53:22 +00:00
|
|
|
pub enum PlayerAction {
|
|
|
|
Acknowledge(ControlAction),
|
|
|
|
Exception(PlaybackError),
|
|
|
|
End,
|
2022-01-03 22:53:57 +00:00
|
|
|
Empty,
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
|
|
|
|
2022-01-05 22:37:13 +00:00
|
|
|
#[derive(Clone, Debug)]
|
|
|
|
pub enum PlaybackAction {
|
|
|
|
Empty,
|
2022-01-21 00:52:03 +00:00
|
|
|
Enqueued(MpsItem),
|
2022-01-05 22:37:13 +00:00
|
|
|
Exit,
|
|
|
|
}
|
|
|
|
|
2021-12-10 21:53:22 +00:00
|
|
|
impl PlayerAction {
|
|
|
|
pub fn is_acknowledgement(&self) -> bool {
|
|
|
|
match self {
|
|
|
|
Self::Acknowledge(_) => true,
|
2022-01-04 02:15:28 +00:00
|
|
|
_ => false,
|
2021-12-10 21:53:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|