Fix some REPL playback issues, including upgrading rodio to use more robust symphonia decoding

This commit is contained in:
NGnius (Graham) 2022-01-26 20:15:00 -05:00
parent 0c3f0fee9c
commit d3bb52d354
5 changed files with 35 additions and 61 deletions

5
Cargo.lock generated
View file

@ -1807,15 +1807,16 @@ dependencies = [
[[package]] [[package]]
name = "rodio" name = "rodio"
version = "0.14.0" version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4d98f5e557b61525057e2bc142c8cd7f0e70d75dc32852309bec440e6e046bf9" checksum = "ec0939e9f626e6c6f1989adb6226a039c855ca483053f0ee7c98b90e41cf731e"
dependencies = [ dependencies = [
"claxon", "claxon",
"cpal", "cpal",
"hound", "hound",
"lewton", "lewton",
"minimp3", "minimp3",
"symphonia",
] ]
[[package]] [[package]]

View file

@ -6,7 +6,7 @@ license = "LGPL-2.1-only OR GPL-2.0-or-later"
readme = "README.md" readme = "README.md"
[dependencies] [dependencies]
rodio = { version = "^0.14"} rodio = { version = "^0.15", features = ["symphonia"]}
m3u8-rs = { version = "^3.0.0" } m3u8-rs = { version = "^3.0.0" }
# local # local

View file

@ -1,6 +1,5 @@
use std::fs; use std::fs;
use std::io; use std::io;
use std::path::PathBuf;
use rodio::{decoder::Decoder, OutputStream, OutputStreamHandle, Sink}; use rodio::{decoder::Decoder, OutputStream, OutputStreamHandle, Sink};
@ -40,22 +39,11 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
if let Some(filename) = if let Some(filename) =
music.field("filename").and_then(|x| x.to_owned().to_str()) music.field("filename").and_then(|x| x.to_owned().to_str())
{ {
let path: PathBuf = filename.into(); // NOTE: Default rodio::Decoder hangs here when decoding large files, but symphonia does not
if let Some(ext) = path.extension().and_then(|x| x.to_str()) { let file = fs::File::open(filename).map_err(PlaybackError::from_err)?;
match ext { let stream = io::BufReader::new(file);
"mp3" let source = Decoder::new(stream).map_err(PlaybackError::from_err)?;
| "wav" self.sink.append(source);
| "flac"
| "aac"
| "ogg" => {
let file = fs::File::open(path).map_err(PlaybackError::from_err)?;
let stream = io::BufReader::new(file);
let source = Decoder::new(stream).map_err(PlaybackError::from_err)?;
self.sink.append(source);
},
_ => {}
}
}
Ok(()) Ok(())
} else { } else {
Err(PlaybackError::from_err( Err(PlaybackError::from_err(
@ -79,22 +67,11 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
if let Some(filename) = if let Some(filename) =
music.field("filename").and_then(|x| x.to_owned().to_str()) music.field("filename").and_then(|x| x.to_owned().to_str())
{ {
let path: PathBuf = filename.into(); // NOTE: Default rodio::Decoder hangs here when decoding large files, but symphonia does not
if let Some(ext) = path.extension().and_then(|x| x.to_str()) { let file = fs::File::open(filename).map_err(PlaybackError::from_err)?;
match ext { let stream = io::BufReader::new(file);
"mp3" let source = Decoder::new(stream).map_err(PlaybackError::from_err)?;
| "wav" self.sink.append(source);
| "flac"
| "aac"
| "ogg" => {
let file = fs::File::open(path).map_err(PlaybackError::from_err)?;
let stream = io::BufReader::new(file);
let source = Decoder::new(stream).map_err(PlaybackError::from_err)?;
self.sink.append(source);
},
_ => {}
}
}
Ok(()) Ok(())
} else { } else {
Err(PlaybackError::from_err( Err(PlaybackError::from_err(
@ -120,24 +97,13 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
if let Some(filename) = if let Some(filename) =
music.field("filename").and_then(|x| x.to_owned().to_str()) music.field("filename").and_then(|x| x.to_owned().to_str())
{ {
let path: PathBuf = filename.into(); enqueued.push(music.clone());
if let Some(ext) = path.extension().and_then(|x| x.to_str()) { // NOTE: Default rodio::Decoder hangs here when decoding large files, but symphonia does not
match ext { let file = fs::File::open(filename).map_err(PlaybackError::from_err)?;
"mp3" let stream = io::BufReader::new(file);
| "wav" let source = Decoder::new(stream).map_err(PlaybackError::from_err)?;
| "flac" self.sink.append(source);
| "aac" items_left -= 1;
| "ogg" => {
enqueued.push(music.clone());
let file = fs::File::open(path).map_err(PlaybackError::from_err)?;
let stream = io::BufReader::new(file);
let source = Decoder::new(stream).map_err(PlaybackError::from_err)?;
self.sink.append(source);
items_left -= 1;
},
_ => {}
}
}
Ok(()) Ok(())
} else { } else {
Err(PlaybackError::from_err( Err(PlaybackError::from_err(

View file

@ -39,7 +39,9 @@ impl<T: MpsTokenReader> MpsPlayerServer<T> {
fn enqeue_some(&mut self, count: usize) { fn enqeue_some(&mut self, count: usize) {
//println!("Enqueuing up to {} items", count); //println!("Enqueuing up to {} items", count);
match self.player.enqueue(count) { match self.player.enqueue(count) {
Err(e) => self.event.send(PlayerAction::Exception(e)).unwrap(), Err(e) => {
self.event.send(PlayerAction::Exception(e)).unwrap();
},
Ok(items) => { Ok(items) => {
for item in items { for item in items {
// notify of new items that have been enqueued // notify of new items that have been enqueued
@ -152,7 +154,7 @@ impl<T: MpsTokenReader> MpsPlayerServer<T> {
playback: Sender<PlaybackAction>, playback: Sender<PlaybackAction>,
keep_alive: bool, keep_alive: bool,
) -> JoinHandle<()> { ) -> JoinHandle<()> {
thread::spawn(move || Self::unblocking_timer_loop(ctrl_tx, 50)); thread::spawn(move || Self::unblocking_timer_loop(ctrl_tx, 100));
thread::spawn(move || { thread::spawn(move || {
let player = factory(); let player = factory();
let mut server_obj = Self::new(player, ctrl_rx, event, playback, keep_alive); let mut server_obj = Self::new(player, ctrl_rx, event, playback, keep_alive);

View file

@ -51,10 +51,10 @@ pub fn repl(args: CliArgs) {
match player.save_m3u8(&mut playlist_writer) { match player.save_m3u8(&mut playlist_writer) {
Ok(_) => {} Ok(_) => {}
Err(e) => { Err(e) => {
eprintln!("{}", e.message()); error_prompt(e, &args);
// consume any further errors (this shouldn't actually write anything) // consume any further errors (this shouldn't actually write anything)
while let Err(e) = player.save_m3u8(&mut playlist_writer) { while let Err(e) = player.save_m3u8(&mut playlist_writer) {
eprintln!("{}", e.message()); error_prompt(e, &args);
} }
} }
} }
@ -69,7 +69,7 @@ pub fn repl(args: CliArgs) {
if args.wait { if args.wait {
match ctrl.wait_for_empty() { match ctrl.wait_for_empty() {
Ok(_) => {} Ok(_) => {}
Err(e) => eprintln!("{}", e.message()), Err(e) => error_prompt(e, &args),
} }
} else { } else {
// consume all incoming errors // consume all incoming errors
@ -77,7 +77,7 @@ pub fn repl(args: CliArgs) {
while had_err { while had_err {
let mut new_had_err = false; let mut new_had_err = false;
for e in ctrl.check_ack() { for e in ctrl.check_ack() {
eprintln!("{}", e.message()); error_prompt(e, &args);
new_had_err = true; new_had_err = true;
} }
had_err = new_had_err; had_err = new_had_err;
@ -127,7 +127,7 @@ fn read_loop<F: FnMut()>(args: &CliArgs, state: &mut ReplState, mut execute: F)
'\n' => { '\n' => {
let statement_result = std::str::from_utf8(state.statement_buf.as_slice()); let statement_result = std::str::from_utf8(state.statement_buf.as_slice());
if statement_result.is_ok() && statement_result.unwrap().trim().starts_with("?") { if statement_result.is_ok() && statement_result.unwrap().trim().starts_with("?") {
println!("Got {}", statement_result.unwrap()); //println!("Got {}", statement_result.unwrap());
repl_commands(statement_result.unwrap().trim()); repl_commands(statement_result.unwrap().trim());
state.statement_buf.clear(); state.statement_buf.clear();
} else if state.bracket_depth == 0 && state.in_literal.is_none() { } else if state.bracket_depth == 0 && state.in_literal.is_none() {
@ -153,6 +153,11 @@ fn prompt(line: &mut usize, args: &CliArgs) {
std::io::stdout().flush().expect("Failed to flush stdout"); std::io::stdout().flush().expect("Failed to flush stdout");
} }
#[inline(always)]
fn error_prompt(error: mps_player::PlaybackError, args: &CliArgs) {
eprintln!("E{}{}", args.prompt, error.message());
}
fn repl_commands(command_str: &str) { fn repl_commands(command_str: &str) {
let words: Vec<&str> = command_str.split(" ").map(|s| s.trim()).collect(); let words: Vec<&str> = command_str.split(" ").map(|s| s.trim()).collect();
match words[0] { match words[0] {