Improve song duration reporting, change to fork of playback library with duration fix

This commit is contained in:
NGnius (Graham) 2023-09-17 16:47:22 -04:00
parent a3a00543f4
commit 9f5f8959fc
8 changed files with 126 additions and 124 deletions

2
Cargo.lock generated
View file

@ -1898,8 +1898,6 @@ dependencies = [
[[package]] [[package]]
name = "rodio" name = "rodio"
version = "0.17.1" version = "0.17.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bdf1d4dea18dff2e9eb6dca123724f8b60ef44ad74a9ad283cdfe025df7e73fa"
dependencies = [ dependencies = [
"cpal", "cpal",
"symphonia", "symphonia",

View file

@ -10,9 +10,7 @@ rust-version = "1.59"
[dependencies] [dependencies]
rusqlite = { version = "0.27", features = ["bundled"], optional = true } rusqlite = { version = "0.27", features = ["bundled"], optional = true }
sqlparser = { version = "0.23", optional = true } sqlparser = { version = "0.23", optional = true }
symphonia = { version = "0.5", optional = true, features = [ symphonia = { version = "0.5", optional = true, features = ["all"] }
"aac", "alac", "flac", "mp3", "pcm", "vorbis", "isomp4", "ogg", "wav"
] }
dirs = { version = "4" } dirs = { version = "4" }
regex = { version = "1" } regex = { version = "1" }
rand = { version = "0.8" } rand = { version = "0.8" }

View file

@ -7,7 +7,7 @@ repository = "https://git.ngni.us/NGnius/muss"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
rodio = { version = "^0.17", features = ["symphonia-all"], default-features = false} rodio = { version = "0.17", features = ["symphonia-all"], path = "../../rodio", default-features = false}
m3u8-rs = { version = "^3.0" } m3u8-rs = { version = "^3.0" }
mpd = { version = "0.1", optional = true } mpd = { version = "0.1", optional = true }

View file

@ -49,7 +49,8 @@ impl SystemControlWrapper {
state: std::sync::Mutex::new( state: std::sync::Mutex::new(
State { State {
playback_time: 0, playback_time: 0,
duration_cache: None, duration_cache: std::collections::HashMap::new(),
current_item: None,
} }
), ),
} }
@ -164,7 +165,7 @@ impl SystemControlWrapper {
}*/ }*/
} }
fn build_metadata(item: Item) -> Metadata { fn build_metadata(item: Item, duration: Option<&std::time::Duration>) -> Metadata {
let file_uri = item.field("filename").and_then(|x| x.to_owned().to_str()); let file_uri = item.field("filename").and_then(|x| x.to_owned().to_str());
let cover_art = item.field("cover").and_then(|x| x.to_owned().to_str()); let cover_art = item.field("cover").and_then(|x| x.to_owned().to_str());
let cover_url = if let Some(art) = &cover_art { let cover_url = if let Some(art) = &cover_art {
@ -200,7 +201,7 @@ impl SystemControlWrapper {
None None
}; };
Metadata { Metadata {
length: None, // populated separately length: duration.map(|duration| duration.as_secs_f64().round() as i64 * 1_000_000),
art_url: cover_url, art_url: cover_url,
album: item.field("album").and_then(|x| x.to_owned().to_str()), album: item.field("album").and_then(|x| x.to_owned().to_str()),
album_artist: item album_artist: item
@ -232,10 +233,10 @@ impl SystemControlWrapper {
} }
} }
fn enqueued(item: Item, dbus_ctrl: &Sender<DbusControl>) { fn enqueued(item: Item, dbus_ctrl: &Sender<DbusControl>, duration: Option<&std::time::Duration>) {
//println!("Got enqueued item {}", &item.title); //println!("Got enqueued item {}", &item.title);
dbus_ctrl dbus_ctrl
.send(DbusControl::SetMetadata(Self::build_metadata(item))) .send(DbusControl::SetMetadata(Self::build_metadata(item, duration)))
.unwrap_or(()); .unwrap_or(());
} }
@ -257,16 +258,15 @@ impl SystemControlWrapper {
.unwrap_or(()); .unwrap_or(());
} }
fn time(item: Item, duration: std::time::Duration, dbus_ctrl: &Sender<DbusControl>) { fn time(item: Item, duration: &std::time::Duration, dbus_ctrl: &Sender<DbusControl>) {
let mut meta = Self::build_metadata(item); let meta = Self::build_metadata(item, Some(duration));
meta.length = Some(duration.as_secs_f64().round() as i64 * 1_000_000);
dbus_ctrl.send(DbusControl::SetMetadata(meta)).unwrap_or(()); dbus_ctrl.send(DbusControl::SetMetadata(meta)).unwrap_or(());
} }
fn time_update( fn time_update(
_item: Item, _item: Item,
new_time: i64, new_time: i64,
duration: &Option<std::time::Duration>, duration: Option<&std::time::Duration>,
dbus_ctrl: &Sender<DbusControl>, dbus_ctrl: &Sender<DbusControl>,
) { ) {
//println!("Position update tick"); //println!("Position update tick");
@ -286,7 +286,8 @@ impl SystemControlWrapper {
#[cfg(all(target_os = "linux", feature = "os-controls", feature = "mpris-player"))] #[cfg(all(target_os = "linux", feature = "os-controls", feature = "mpris-player"))]
struct State { struct State {
playback_time: i64, playback_time: i64,
duration_cache: Option<std::time::Duration>, duration_cache: std::collections::HashMap<Item, std::time::Duration>,
current_item: Option<Item>,
} }
#[cfg(all(target_os = "linux", feature = "os-controls", feature = "mpris-player"))] #[cfg(all(target_os = "linux", feature = "os-controls", feature = "mpris-player"))]
@ -299,24 +300,29 @@ impl super::EventTap for SystemControlWrapper {
PlaybackAction::Enqueued(item) => { PlaybackAction::Enqueued(item) => {
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
state.playback_time = 0; state.playback_time = 0;
state.duration_cache = None; state.current_item = Some(item.to_owned());
Self::enqueued(item.to_owned(), self.dbus_ctrl.as_ref().unwrap()); Self::enqueued(item.to_owned(), self.dbus_ctrl.as_ref().unwrap(), state.duration_cache.get(item));
},
PlaybackAction::Empty => {
Self::empty(self.dbus_ctrl.as_ref().unwrap());
let mut state = self.state.lock().unwrap();
state.playback_time = 0;
state.current_item = None;
}, },
PlaybackAction::Empty => Self::empty(self.dbus_ctrl.as_ref().unwrap()),
PlaybackAction::Time(item, duration) => { PlaybackAction::Time(item, duration) => {
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
state.duration_cache = Some(duration.to_owned()); state.duration_cache.insert(item.to_owned(), duration.to_owned());
Self::time(item.to_owned(), duration.to_owned(), self.dbus_ctrl.as_ref().unwrap()); Self::time(item.to_owned(), duration, self.dbus_ctrl.as_ref().unwrap());
} }
PlaybackAction::UpdateTick(item) => { PlaybackAction::UpdateTick(item, tick) => {
let mut state = self.state.lock().unwrap(); let mut state = self.state.lock().unwrap();
state.playback_time = *tick as i64;
Self::time_update( Self::time_update(
item.to_owned(), item.to_owned(),
state.playback_time, state.playback_time,
&state.duration_cache, state.duration_cache.get(item),
self.dbus_ctrl.as_ref().unwrap(), self.dbus_ctrl.as_ref().unwrap(),
); );
state.playback_time += 1;
} }
} }
None None

View file

@ -47,7 +47,9 @@ impl<I: std::iter::Iterator<Item = Result<Item, InterpreterError>>> PlayerServer
let event = std::sync::Arc::new(std::sync::Mutex::new(self.playback.clone())); let event = std::sync::Arc::new(std::sync::Mutex::new(self.playback.clone()));
move |source_in, item| { move |source_in, item| {
let event2 = event.clone(); let event2 = event.clone();
let mut update_tick_num = 0;
if let Some(duration) = source_in.total_duration() { if let Some(duration) = source_in.total_duration() {
//println!("Got duration {:?}", duration);
event event
.lock() .lock()
.map(|event| { .map(|event| {
@ -62,8 +64,9 @@ impl<I: std::iter::Iterator<Item = Result<Item, InterpreterError>>> PlayerServer
event2 event2
.lock() .lock()
.map(|x| { .map(|x| {
x.send(PlaybackAction::UpdateTick(item.clone())) x.send(PlaybackAction::UpdateTick(item.clone(), update_tick_num))
.unwrap_or(()) .unwrap_or(());
update_tick_num += 1;
}) })
.unwrap_or(()); .unwrap_or(());
}), }),
@ -96,8 +99,9 @@ impl<I: std::iter::Iterator<Item = Result<Item, InterpreterError>>> PlayerServer
event2 event2
.lock() .lock()
.map(|x| { .map(|x| {
x.send(PlaybackAction::UpdateTick(item.clone())) x.send(PlaybackAction::UpdateTick(item.clone(), update_tick_num))
.unwrap_or(()) .unwrap_or(());
update_tick_num += 1;
}) })
.unwrap_or(()); .unwrap_or(());
}), }),
@ -316,7 +320,7 @@ pub enum PlaybackAction {
Empty, Empty,
Enqueued(Item), Enqueued(Item),
Time(Item, std::time::Duration), Time(Item, std::time::Duration),
UpdateTick(Item), // tick sent once every second UpdateTick(Item, u64), // tick sent once every second
Exit, Exit,
} }

View file

@ -3,26 +3,46 @@ use muss_interpreter::Item;
use muss_player::EventTap; use muss_player::EventTap;
use muss_player::{PlaybackAction, PlayerAction, ControlAction}; use muss_player::{PlaybackAction, PlayerAction, ControlAction};
#[derive(Clone)]
pub struct InterpreterDebugState {
pub debug_flag: InterpreterDebugFlag,
pub verbose: bool,
}
#[derive(Copy, Clone)]
pub enum InterpreterDebugFlag {
Skip,
List,
Normal,
}
#[derive(Clone)]
pub struct DebugState { pub struct DebugState {
pub now_playing: Option<Item>, pub now_playing: std::sync::Arc<std::sync::RwLock<Option<Item>>>,
pub control_tx: Option<std::sync::Mutex<std::sync::mpsc::Sender<ControlAction>>>, pub control_tx: std::sync::Arc<std::sync::Mutex<Option<std::sync::mpsc::Sender<ControlAction>>>>,
pub interpreter: std::sync::Arc<std::sync::RwLock<InterpreterDebugState>>,
} }
impl DebugState { impl DebugState {
pub fn new() -> std::sync::Arc<std::sync::RwLock<Self>> { pub fn new() -> Self {
std::sync::Arc::new(std::sync::RwLock::new(Self { Self {
now_playing: None, now_playing: std::sync::Arc::new(std::sync::RwLock::new(None)),
control_tx: None, control_tx: std::sync::Arc::new(std::sync::Mutex::new(None)),
})) interpreter: std::sync::Arc::new(std::sync::RwLock::new(InterpreterDebugState {
debug_flag: InterpreterDebugFlag::Normal,
verbose: false,
})),
}
} }
} }
pub struct DebugEventHandler { pub struct DebugEventHandler {
state: std::sync::Arc<std::sync::RwLock<DebugState>>, state: DebugState,
} }
impl DebugEventHandler { impl DebugEventHandler {
pub fn new(debug_state: std::sync::Arc<std::sync::RwLock<DebugState>>) -> Self { pub fn new(debug_state: DebugState) -> Self {
Self { Self {
state: debug_state, state: debug_state,
} }
@ -33,15 +53,15 @@ impl EventTap for DebugEventHandler {
fn on_playback(&self, playback: &PlaybackAction) -> Option<ControlAction> { fn on_playback(&self, playback: &PlaybackAction) -> Option<ControlAction> {
match playback { match playback {
PlaybackAction::Enqueued(item) => { PlaybackAction::Enqueued(item) => {
let mut state = self.state.write().unwrap(); let mut now_playing = self.state.now_playing.write().unwrap();
state.now_playing = Some(item.to_owned()); *now_playing = Some(item.to_owned());
}, },
PlaybackAction::Empty | PlaybackAction::Exit => { PlaybackAction::Empty | PlaybackAction::Exit => {
let mut state = self.state.write().unwrap(); let mut now_playing = self.state.now_playing.write().unwrap();
state.now_playing = None; *now_playing = None;
}, },
PlaybackAction::Time(_item, _dur) => {}, PlaybackAction::Time(_item, _dur) => {},
PlaybackAction::UpdateTick(_item) => {}, PlaybackAction::UpdateTick(_item, _tick) => {},
} }
None None
} }
@ -51,6 +71,6 @@ impl EventTap for DebugEventHandler {
} }
fn init_control(&mut self, control: &std::sync::mpsc::Sender<ControlAction>) { fn init_control(&mut self, control: &std::sync::mpsc::Sender<ControlAction>) {
self.state.write().expect("Failed to get write lock on controller debug state").control_tx = Some(std::sync::Mutex::new(control.clone())); *self.state.control_tx.lock().expect("Failed to get write lock on controller debug state") = Some(control.clone());
} }
} }

View file

@ -84,6 +84,6 @@ fn music_filename(item: &Item) -> Option<String> {
} }
pub fn get_current_item(state: &mut ReplState) -> Option<muss_interpreter::Item> { pub fn get_current_item(state: &mut ReplState) -> Option<muss_interpreter::Item> {
let data = state.controller_debug.read().expect("Failed to get read lock for debug player data"); let now_playing = state.controller_debug.now_playing.read().expect("Failed to get read lock for debug now_playing data");
data.now_playing.clone() now_playing.clone()
} }

View file

@ -2,9 +2,6 @@
#![allow(clippy::single_match)] #![allow(clippy::single_match)]
use std::io::{self, Write}; use std::io::{self, Write};
use std::sync::mpsc::{self, Receiver}; use std::sync::mpsc::{self, Receiver};
use std::sync::RwLock;
use lazy_static::lazy_static;
use console::{Key, Term}; use console::{Key, Term};
@ -15,13 +12,6 @@ use muss_player::{Controller, Player};
use super::channel_io::{channel_io, ChannelWriter}; use super::channel_io::{channel_io, ChannelWriter};
use super::cli::CliArgs; use super::cli::CliArgs;
lazy_static! {
static ref DEBUG_STATE: RwLock<DebugState> = RwLock::new(DebugState {
debug_flag: DebugFlag::Normal,
verbose: false,
});
}
pub const TERMINAL_WRITE_ERROR: &str = "Failed to write to terminal output"; pub const TERMINAL_WRITE_ERROR: &str = "Failed to write to terminal output";
const INTERPRETER_WRITE_ERROR: &str = "Failed to write to interpreter"; const INTERPRETER_WRITE_ERROR: &str = "Failed to write to interpreter";
@ -41,20 +31,7 @@ pub struct ReplState {
cursor_rightward_position: usize, cursor_rightward_position: usize,
//debug: Arc<RwLock<DebugState>>, //debug: Arc<RwLock<DebugState>>,
list_rx: Receiver<DebugItem>, list_rx: Receiver<DebugItem>,
pub controller_debug: std::sync::Arc<std::sync::RwLock<crate::debug_state::DebugState>>, pub controller_debug: crate::debug_state::DebugState,
}
#[derive(Clone)]
struct DebugState {
debug_flag: DebugFlag,
verbose: bool,
}
#[derive(Copy, Clone)]
enum DebugFlag {
Skip,
List,
Normal,
} }
impl ReplState { impl ReplState {
@ -80,20 +57,23 @@ impl ReplState {
} }
} }
fn interpreter_event_callback<T: muss_interpreter::tokens::TokenReader>( fn interpreter_event_callback<'a, T: muss_interpreter::tokens::TokenReader>(
_interpreter: &mut Interpreter<'_, T>, debug_state: crate::debug_state::DebugState,
event: InterpreterEvent, ) -> impl Fn(
&mut Interpreter<'a, T>, InterpreterEvent,
) -> Result<(), InterpreterError> { ) -> Result<(), InterpreterError> {
move |_interpreter, event| {
match event { match event {
InterpreterEvent::StatementComplete => { InterpreterEvent::StatementComplete => {
if let Ok(mut d_state) = DEBUG_STATE.write() { if let Ok(mut d_state) = debug_state.interpreter.write() {
d_state.debug_flag = DebugFlag::Normal; d_state.debug_flag = crate::debug_state::InterpreterDebugFlag::Normal;
} }
} }
_ => {} _ => {}
} }
Ok(()) Ok(())
} }
}
#[inline] #[inline]
fn item_prompt(terminal: &mut Term, args: &CliArgs) { fn item_prompt(terminal: &mut Term, args: &CliArgs) {
@ -148,7 +128,7 @@ fn pretty_print_item(item: &Item, terminal: &mut Term, args: &CliArgs, verbose:
fn handle_list_rx(state: &mut ReplState, args: &CliArgs) { fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
//let items = state.list_rx.try_iter().collect::<Vec<_>>(); //let items = state.list_rx.try_iter().collect::<Vec<_>>();
let d_state = DEBUG_STATE let d_state = state.controller_debug.interpreter
.read() .read()
.expect("Failed to get read lock for debug state info") .expect("Failed to get read lock for debug state info")
.clone(); .clone();
@ -162,12 +142,14 @@ fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
} }
} }
let flag = d_state.debug_flag; let flag = d_state.debug_flag;
drop(d_state);
match flag { match flag {
DebugFlag::List => { crate::debug_state::InterpreterDebugFlag::List => {
while let Ok(item) = state.list_rx.recv() { while let Ok(item) = state.list_rx.recv() {
match item { match item {
Ok(item) => { Ok(item) => {
pretty_print_item(&item, &mut state.terminal, args, d_state.verbose) let verbose = state.controller_debug.interpreter.read().map(|x| x.verbose).unwrap_or(false);
pretty_print_item(&item, &mut state.terminal, args, verbose)
} }
Err(e) => error_prompt( Err(e) => error_prompt(
muss_player::PlayerError::Playback(muss_player::PlaybackError::from_err(e)), muss_player::PlayerError::Playback(muss_player::PlaybackError::from_err(e)),
@ -175,13 +157,13 @@ fn handle_list_rx(state: &mut ReplState, args: &CliArgs) {
), ),
} }
// stop listing if no longer in list mode // stop listing if no longer in list mode
let flag = if let Ok(d_state) = DEBUG_STATE.read() { let flag = if let Ok(d_state) = state.controller_debug.interpreter.read() {
d_state.debug_flag d_state.debug_flag
} else { } else {
DebugFlag::Normal crate::debug_state::InterpreterDebugFlag::Normal
}; };
match flag { match flag {
DebugFlag::List => {} crate::debug_state::InterpreterDebugFlag::List => {}
_ => break, _ => break,
} }
} }
@ -213,23 +195,27 @@ pub fn repl(args: CliArgs) {
}; };
let (list_tx, list_rx) = mpsc::channel(); let (list_tx, list_rx) = mpsc::channel();
let mut state = ReplState::new(writer, term, list_rx); let mut state = ReplState::new(writer, term, list_rx);
let controller_debug_clone = state.controller_debug.clone();
let player_builder = move || { let player_builder = move || {
let runner = Interpreter::with_stream_and_callback(reader, &interpreter_event_callback); let callback_handler = Box::new(interpreter_event_callback(controller_debug_clone.clone()));
// FIXME don't leak memory for callback handler
// (this is fine since it'll only get called once, it just looks bad)
let runner = Interpreter::with_stream_and_callback(reader, Box::leak(callback_handler));
let debugger = Debugger::new(runner, move |interpretor, item| { let debugger = Debugger::new(runner, move |interpretor, item| {
let flag = if let Ok(d_state) = DEBUG_STATE.read() { let flag = if let Ok(d_state) = controller_debug_clone.interpreter.read() {
d_state.debug_flag d_state.debug_flag
} else { } else {
DebugFlag::Normal crate::debug_state::InterpreterDebugFlag::Normal
}; };
match flag { match flag {
DebugFlag::Normal => item, crate::debug_state::InterpreterDebugFlag::Normal => item,
DebugFlag::Skip => { crate::debug_state::InterpreterDebugFlag::Skip => {
for _ in interpretor.by_ref() { for _ in interpretor.by_ref() {
// NOTE: recursion occurs here // NOTE: recursion occurs here
} }
None None
} }
DebugFlag::List => { crate::debug_state::InterpreterDebugFlag::List => {
if let Some(x) = item { if let Some(x) = item {
list_tx.send(x.map_err(|e| e.to_string())).unwrap_or(()); list_tx.send(x.map_err(|e| e.to_string())).unwrap_or(());
for x in interpretor.by_ref() { for x in interpretor.by_ref() {
@ -713,34 +699,34 @@ fn repl_commands(command_str: &str, state: &mut ReplState, args: &CliArgs) {
} }
"?list" => { "?list" => {
{ {
let mut debug_state = DEBUG_STATE let mut debug_state = state.controller_debug.interpreter
.write() .write()
.expect("Failed to get write lock for debug state"); .expect("Failed to get write lock for debug state");
debug_state.debug_flag = DebugFlag::List; debug_state.debug_flag = crate::debug_state::InterpreterDebugFlag::List;
} }
writeln!(state.terminal, "Listing upcoming items").expect(TERMINAL_WRITE_ERROR); writeln!(state.terminal, "Listing upcoming items").expect(TERMINAL_WRITE_ERROR);
} }
"?skip" => { "?skip" => {
{ {
let mut debug_state = DEBUG_STATE let mut debug_state = state.controller_debug.interpreter
.write() .write()
.expect("Failed to get write lock for debug state"); .expect("Failed to get write lock for debug state");
debug_state.debug_flag = DebugFlag::Skip; debug_state.debug_flag = crate::debug_state::InterpreterDebugFlag::Skip;
} }
writeln!(state.terminal, "Skipping upcoming items").expect(TERMINAL_WRITE_ERROR); writeln!(state.terminal, "Skipping upcoming items").expect(TERMINAL_WRITE_ERROR);
} }
"?normal" => { "?normal" => {
{ {
let mut debug_state = DEBUG_STATE let mut debug_state = state.controller_debug.interpreter
.write() .write()
.expect("Failed to get write lock for debug state"); .expect("Failed to get write lock for debug state");
debug_state.debug_flag = DebugFlag::Normal; debug_state.debug_flag = crate::debug_state::InterpreterDebugFlag::Normal;
} }
writeln!(state.terminal, "Resuming normal operation").expect(TERMINAL_WRITE_ERROR); writeln!(state.terminal, "Resuming normal operation").expect(TERMINAL_WRITE_ERROR);
} }
"?verbose" => { "?verbose" => {
let verbose = { let verbose = {
let mut debug_state = DEBUG_STATE let mut debug_state = state.controller_debug.interpreter
.write() .write()
.expect("Failed to get write lock for debug state"); .expect("Failed to get write lock for debug state");
debug_state.verbose = !debug_state.verbose; debug_state.verbose = !debug_state.verbose;
@ -754,7 +740,7 @@ fn repl_commands(command_str: &str, state: &mut ReplState, args: &CliArgs) {
} }
"?now" => { "?now" => {
if let Some(item) = crate::playlists::get_current_item(state) { if let Some(item) = crate::playlists::get_current_item(state) {
let verbose = DEBUG_STATE let verbose = state.controller_debug.interpreter
.read() .read()
.expect("Failed to get read lock for debug state") .expect("Failed to get read lock for debug state")
.verbose; .verbose;
@ -764,46 +750,38 @@ fn repl_commands(command_str: &str, state: &mut ReplState, args: &CliArgs) {
} }
} }
"?next" => { "?next" => {
state.controller_debug state.controller_debug.control_tx
.read()
.expect("Failed to get read lock for debug player data")
.control_tx.as_ref()
.expect("Control action sender shouldn't be None")
.lock() .lock()
.expect("Failed to get lock for control action sender") .expect("Failed to get lock for control action sender")
.as_ref()
.expect("Control action sender shouldn't be None")
.send(muss_player::ControlAction::Next { ack: false }) .send(muss_player::ControlAction::Next { ack: false })
.expect("Failed to send control action"); .expect("Failed to send control action");
}, },
"?previous" => { "?previous" => {
state.controller_debug state.controller_debug.control_tx
.read()
.expect("Failed to get read lock for debug player data")
.control_tx.as_ref()
.expect("Control action sender shouldn't be None")
.lock() .lock()
.expect("Failed to get lock for control action sender") .expect("Failed to get lock for control action sender")
.as_ref()
.expect("Control action sender shouldn't be None")
.send(muss_player::ControlAction::Previous { ack: false }) .send(muss_player::ControlAction::Previous { ack: false })
.expect("Failed to send control action"); .expect("Failed to send control action");
}, },
"?pause" => { "?pause" => {
state.controller_debug state.controller_debug.control_tx
.read()
.expect("Failed to get read lock for debug player data")
.control_tx.as_ref()
.expect("Control action sender shouldn't be None")
.lock() .lock()
.expect("Failed to get lock for control action sender") .expect("Failed to get lock for control action sender")
.as_ref()
.expect("Control action sender shouldn't be None")
.send(muss_player::ControlAction::Pause { ack: false }) .send(muss_player::ControlAction::Pause { ack: false })
.expect("Failed to send control action"); .expect("Failed to send control action");
} }
"?play" => { "?play" => {
state.controller_debug state.controller_debug.control_tx
.read()
.expect("Failed to get read lock for debug player data")
.control_tx.as_ref()
.expect("Control action sender shouldn't be None")
.lock() .lock()
.expect("Failed to get lock for control action sender") .expect("Failed to get lock for control action sender")
.as_ref()
.expect("Control action sender shouldn't be None")
.send(muss_player::ControlAction::Play { ack: false }) .send(muss_player::ControlAction::Play { ack: false })
.expect("Failed to send control action"); .expect("Failed to send control action");
} }
@ -817,13 +795,11 @@ fn volume_cmd(cmd_args: &[&str], state: &mut ReplState) {
if let Some(volume_arg) = cmd_args.get(1) { if let Some(volume_arg) = cmd_args.get(1) {
let volume_result: Result<f32, _> = volume_arg.parse(); let volume_result: Result<f32, _> = volume_arg.parse();
match volume_result { match volume_result {
Ok(vol) => state.controller_debug Ok(vol) => state.controller_debug.control_tx
.read()
.expect("Failed to get read lock for debug player data")
.control_tx.as_ref()
.expect("Control action sender shouldn't be None")
.lock() .lock()
.expect("Failed to get lock for control action sender") .expect("Failed to get lock for control action sender")
.as_ref()
.expect("Control action sender shouldn't be None")
.send(muss_player::ControlAction::SetVolume { ack: false, volume: (vol * 100.0).round() as _ }) .send(muss_player::ControlAction::SetVolume { ack: false, volume: (vol * 100.0).round() as _ })
.expect("Failed to send control action"), .expect("Failed to send control action"),
Err(e) => writeln!(state.terminal, "Error parsing ?volume number parameter: {}", e).expect(TERMINAL_WRITE_ERROR) Err(e) => writeln!(state.terminal, "Error parsing ?volume number parameter: {}", e).expect(TERMINAL_WRITE_ERROR)