From 7204270277a16eb76e645b534638a24ccd14f960 Mon Sep 17 00:00:00 2001 From: hheik <4469778+hheik@users.noreply.github.com> Date: Sun, 10 Mar 2024 01:25:46 +0200 Subject: [PATCH] Implemented support for custom decoders --- src/client.rs | 11 ++++- src/client/app.rs | 18 ++++--- src/main.rs | 4 ++ src/server.rs | 3 +- src/server/decoder.rs | 96 ++++++++++++++++++++++++++++++++++++++ src/server/decoder/opus.rs | 12 +++++ src/server/playback.rs | 9 ++-- 7 files changed, 138 insertions(+), 15 deletions(-) create mode 100644 src/server/decoder.rs create mode 100644 src/server/decoder/opus.rs diff --git a/src/client.rs b/src/client.rs index 22ac1e2..4163740 100644 --- a/src/client.rs +++ b/src/client.rs @@ -6,7 +6,10 @@ use std::{ use crate::CliArgs; -use self::{app::App, request_queue::request_queue_cleaner}; +use self::{ + app::{App, AppOptions}, + request_queue::request_queue_cleaner, +}; pub mod app; pub mod crossterm; @@ -16,9 +19,13 @@ pub mod ui; pub fn run(args: CliArgs) -> Result<(), Box> { let message_queue = Arc::new(Mutex::new(vec![])); let server_state = Arc::new(Mutex::new(None)); - let app = App { + let options = AppOptions { title: "rmp - Rust Music Player".into(), enhanced_graphics: args.enhanced_graphics, + sync_state: args.sync_state, + }; + let app = App { + options, should_quit: false, state: server_state.clone(), message_queue: message_queue.clone(), diff --git a/src/client/app.rs b/src/client/app.rs index 5d27ae9..9168229 100644 --- a/src/client/app.rs +++ b/src/client/app.rs @@ -9,19 +9,23 @@ use rmp::{ }; pub struct App { - pub title: String, + pub options: AppOptions, pub should_quit: bool, - pub enhanced_graphics: bool, pub message_queue: Arc>>, pub state: Arc>>, } +pub struct AppOptions { + pub title: String, + pub sync_state: bool, + pub enhanced_graphics: bool, +} + impl App { - pub fn new(title: &str, enhanced_graphics: bool) -> Self { + pub fn new(options: AppOptions) -> Self { Self { - title: title.to_string(), + options, should_quit: false, - enhanced_graphics, message_queue: Arc::new(Mutex::new(vec![])), state: Arc::new(Mutex::new(None)), } @@ -85,6 +89,8 @@ impl App { pub fn on_tab(&mut self) {} pub fn on_tick(&mut self, _duration: Duration) { - self.push_message(Message::new(MessageType::StateFetch, None)); + if self.options.sync_state { + self.push_message(Message::new(MessageType::StateFetch, None)); + } } } diff --git a/src/main.rs b/src/main.rs index e779914..c81022e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -38,6 +38,10 @@ pub struct CliArgs { #[argh(option, default = "100")] message_rate: u64, + /// should client automatically sync the server state? (used mainly for debugging) + #[argh(option, default = "true")] + sync_state: bool, + /// whether unicode symbols are used to improve the overall look of the app #[argh(option, default = "true")] enhanced_graphics: bool, diff --git a/src/server.rs b/src/server.rs index b055491..8fab1c2 100644 --- a/src/server.rs +++ b/src/server.rs @@ -16,6 +16,7 @@ use crate::CliArgs; use self::playback::{playback_manager, Playback}; pub mod audio_backend; +pub mod decoder; pub mod playback; pub struct Server { @@ -188,5 +189,5 @@ fn route_request(request: &Message, server: &mut Server) -> Result return Ok(Message::new(MessageType::NotImplementedAck, None)), } - return Message::state_response(&server.state); + Message::state_response(&server.state) } diff --git a/src/server/decoder.rs b/src/server/decoder.rs new file mode 100644 index 0000000..d55bffe --- /dev/null +++ b/src/server/decoder.rs @@ -0,0 +1,96 @@ +// Implement decoders missing from rodio. +// At the moment this means opus, mod and xm. + +use std::io::{Read, Seek}; + +pub mod opus; + +pub enum SourceImpl +where + R: Read + Seek, +{ + Rodio(rodio::Decoder), + // Custom(Decoder), +} + +impl Iterator for SourceImpl +where + R: Read + Seek, +{ + type Item = i16; + + #[inline] + fn next(&mut self) -> Option { + match self { + Self::Rodio(source) => source.next(), + } + } +} + +impl rodio::Source for SourceImpl +where + R: Read + Seek, +{ + #[inline] + fn current_frame_len(&self) -> Option { + match self { + Self::Rodio(source) => source.current_frame_len(), + } + } + + #[inline] + fn channels(&self) -> u16 { + match self { + Self::Rodio(source) => source.channels(), + } + } + + #[inline] + fn sample_rate(&self) -> u32 { + match self { + Self::Rodio(source) => source.sample_rate(), + } + } + + #[inline] + fn total_duration(&self) -> Option { + match self { + Self::Rodio(source) => source.total_duration(), + } + } +} + +pub enum Decoder +where + R: Read + Seek, +{ + Opus(opus::OpusDecoder), +} + +impl Decoder +where + R: Read + Seek, +{ + pub fn new(data: R) -> Result, ()> + where + R: Read + Seek + Send + Sync + 'static, + { + match rodio::Decoder::new(data) { + Ok(source) => return Ok(SourceImpl::Rodio(source)), + Err(err) => { + eprintln!( + "Rodio decode error:\n\terror: {err:?}\n\tcontinuing with custom decoder" + ); + } + }; + + // match Decoder::new(data) { + // Ok(source) => return Ok(SourceImpl::Custom(source)), + // Err(err) => { + // eprintln!("Custom decode error:\n\terror: {err:?}\n\tPlayback failed"); + // } + // } + + Err(()) + } +} diff --git a/src/server/decoder/opus.rs b/src/server/decoder/opus.rs new file mode 100644 index 0000000..ccd3f7d --- /dev/null +++ b/src/server/decoder/opus.rs @@ -0,0 +1,12 @@ +use std::{ + io::{Read, Seek}, + marker::PhantomData, +}; + +/// Not implemented +pub struct OpusDecoder +where + R: Read + Seek, +{ + _data: PhantomData, +} diff --git a/src/server/playback.rs b/src/server/playback.rs index b00992c..f6a2aa0 100644 --- a/src/server/playback.rs +++ b/src/server/playback.rs @@ -5,9 +5,9 @@ use std::{ sync::{Arc, Mutex}, }; -use rodio::{Decoder, OutputStream, Sink}; +use rodio::{OutputStream, Sink}; -use super::Server; +use super::{decoder::Decoder, Server}; // HACK: hard-coded path to track static TRACK: &str = ""; @@ -49,10 +49,7 @@ pub fn playback_manager(server: Arc>) { let file = BufReader::new(File::open(&track).unwrap()); let source = match Decoder::new(file) { Ok(source) => source, - Err(err) => { - eprintln!("Decode error:\n\ttrack: {track:?}\n\tinfo: {err:?}"); - continue; - } + Err(_) => continue, }; sink.clear(); sink.append(source);