Implemented support for custom decoders

master
hheik 2024-03-10 01:25:46 +02:00
parent 6d673b5d9c
commit 7204270277
7 changed files with 138 additions and 15 deletions

View File

@ -6,7 +6,10 @@ use std::{
use crate::CliArgs; 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 app;
pub mod crossterm; pub mod crossterm;
@ -16,9 +19,13 @@ pub mod ui;
pub fn run(args: CliArgs) -> Result<(), Box<dyn Error>> { pub fn run(args: CliArgs) -> Result<(), Box<dyn Error>> {
let message_queue = Arc::new(Mutex::new(vec![])); let message_queue = Arc::new(Mutex::new(vec![]));
let server_state = Arc::new(Mutex::new(None)); let server_state = Arc::new(Mutex::new(None));
let app = App { let options = AppOptions {
title: "rmp - Rust Music Player".into(), title: "rmp - Rust Music Player".into(),
enhanced_graphics: args.enhanced_graphics, enhanced_graphics: args.enhanced_graphics,
sync_state: args.sync_state,
};
let app = App {
options,
should_quit: false, should_quit: false,
state: server_state.clone(), state: server_state.clone(),
message_queue: message_queue.clone(), message_queue: message_queue.clone(),

View File

@ -9,19 +9,23 @@ use rmp::{
}; };
pub struct App { pub struct App {
pub title: String, pub options: AppOptions,
pub should_quit: bool, pub should_quit: bool,
pub enhanced_graphics: bool,
pub message_queue: Arc<Mutex<Vec<Message>>>, pub message_queue: Arc<Mutex<Vec<Message>>>,
pub state: Arc<Mutex<Option<ServerState>>>, pub state: Arc<Mutex<Option<ServerState>>>,
} }
pub struct AppOptions {
pub title: String,
pub sync_state: bool,
pub enhanced_graphics: bool,
}
impl App { impl App {
pub fn new(title: &str, enhanced_graphics: bool) -> Self { pub fn new(options: AppOptions) -> Self {
Self { Self {
title: title.to_string(), options,
should_quit: false, should_quit: false,
enhanced_graphics,
message_queue: Arc::new(Mutex::new(vec![])), message_queue: Arc::new(Mutex::new(vec![])),
state: Arc::new(Mutex::new(None)), state: Arc::new(Mutex::new(None)),
} }
@ -85,6 +89,8 @@ impl App {
pub fn on_tab(&mut self) {} pub fn on_tab(&mut self) {}
pub fn on_tick(&mut self, _duration: Duration) { 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));
}
} }
} }

View File

@ -38,6 +38,10 @@ pub struct CliArgs {
#[argh(option, default = "100")] #[argh(option, default = "100")]
message_rate: u64, 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 /// whether unicode symbols are used to improve the overall look of the app
#[argh(option, default = "true")] #[argh(option, default = "true")]
enhanced_graphics: bool, enhanced_graphics: bool,

View File

@ -16,6 +16,7 @@ use crate::CliArgs;
use self::playback::{playback_manager, Playback}; use self::playback::{playback_manager, Playback};
pub mod audio_backend; pub mod audio_backend;
pub mod decoder;
pub mod playback; pub mod playback;
pub struct Server { pub struct Server {
@ -188,5 +189,5 @@ fn route_request(request: &Message, server: &mut Server) -> Result<Message, Stri
} }
_ => return Ok(Message::new(MessageType::NotImplementedAck, None)), _ => return Ok(Message::new(MessageType::NotImplementedAck, None)),
} }
return Message::state_response(&server.state); Message::state_response(&server.state)
} }

96
src/server/decoder.rs Normal file
View File

@ -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<R>
where
R: Read + Seek,
{
Rodio(rodio::Decoder<R>),
// Custom(Decoder<R>),
}
impl<R> Iterator for SourceImpl<R>
where
R: Read + Seek,
{
type Item = i16;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
match self {
Self::Rodio(source) => source.next(),
}
}
}
impl<R> rodio::Source for SourceImpl<R>
where
R: Read + Seek,
{
#[inline]
fn current_frame_len(&self) -> Option<usize> {
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<std::time::Duration> {
match self {
Self::Rodio(source) => source.total_duration(),
}
}
}
pub enum Decoder<R>
where
R: Read + Seek,
{
Opus(opus::OpusDecoder<R>),
}
impl<R> Decoder<R>
where
R: Read + Seek,
{
pub fn new(data: R) -> Result<SourceImpl<R>, ()>
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(())
}
}

View File

@ -0,0 +1,12 @@
use std::{
io::{Read, Seek},
marker::PhantomData,
};
/// Not implemented
pub struct OpusDecoder<R>
where
R: Read + Seek,
{
_data: PhantomData<R>,
}

View File

@ -5,9 +5,9 @@ use std::{
sync::{Arc, Mutex}, 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 // HACK: hard-coded path to track
static TRACK: &str = ""; static TRACK: &str = "";
@ -49,10 +49,7 @@ pub fn playback_manager(server: Arc<Mutex<Server>>) {
let file = BufReader::new(File::open(&track).unwrap()); let file = BufReader::new(File::open(&track).unwrap());
let source = match Decoder::new(file) { let source = match Decoder::new(file) {
Ok(source) => source, Ok(source) => source,
Err(err) => { Err(_) => continue,
eprintln!("Decode error:\n\ttrack: {track:?}\n\tinfo: {err:?}");
continue;
}
}; };
sink.clear(); sink.clear();
sink.append(source); sink.append(source);