Fix enqueuing and song skip playback issues

This commit is contained in:
NGnius (Graham) 2021-12-12 15:08:49 -05:00
parent 03318a0ef5
commit 9b6472b072
4 changed files with 59 additions and 27 deletions

4
Cargo.lock generated
View file

@ -868,9 +868,9 @@ dependencies = [
[[package]] [[package]]
name = "rusqlite" name = "rusqlite"
version = "0.26.1" version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a82b0b91fad72160c56bf8da7a549b25d7c31109f52cc1437eac4c0ad2550a7" checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7"
dependencies = [ dependencies = [
"bitflags", "bitflags",
"fallible-iterator", "fallible-iterator",

View file

@ -1,7 +1,7 @@
use std::io; use std::io;
use std::fs; use std::fs;
use rodio::{decoder::Decoder, OutputStream, Sink}; use rodio::{decoder::Decoder, OutputStream, Sink, OutputStreamHandle};
use m3u8_rs::{MediaPlaylist, MediaSegment}; use m3u8_rs::{MediaPlaylist, MediaSegment};
@ -14,6 +14,7 @@ pub struct MpsPlayer<T: MpsTokenReader> {
sink: Sink, sink: Sink,
#[allow(dead_code)] #[allow(dead_code)]
output_stream: OutputStream, // this is required for playback, so it must live as long as this struct instance output_stream: OutputStream, // this is required for playback, so it must live as long as this struct instance
output_handle: OutputStreamHandle,
} }
impl<T: MpsTokenReader> MpsPlayer<T> { impl<T: MpsTokenReader> MpsPlayer<T> {
@ -23,6 +24,7 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
runner: runner, runner: runner,
sink: Sink::try_new(&output_handle).map_err(PlaybackError::from_err)?, sink: Sink::try_new(&output_handle).map_err(PlaybackError::from_err)?,
output_stream: stream, output_stream: stream,
output_handle: output_handle
}) })
} }
@ -53,7 +55,7 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
let stream = io::BufReader::new(file); let stream = io::BufReader::new(file);
let source = Decoder::new(stream).map_err(PlaybackError::from_err)?; let source = Decoder::new(stream).map_err(PlaybackError::from_err)?;
self.sink.append(source); self.sink.append(source);
self.sink.play(); // idk if this is necessary //self.sink.play(); // idk if this is necessary
Ok(()) Ok(())
}, },
Err(e) => Err(PlaybackError::from_err(e)) Err(e) => Err(PlaybackError::from_err(e))
@ -64,20 +66,22 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
pub fn enqueue(&mut self, count: usize) -> Result<(), PlaybackError> { pub fn enqueue(&mut self, count: usize) -> Result<(), PlaybackError> {
let mut items_left = count; let mut items_left = count;
if items_left == 0 { return Ok(()); }
for item in &mut self.runner { for item in &mut self.runner {
if items_left == 0 { return Ok(()); }
match item { match item {
Ok(music) => { Ok(music) => {
//println!("Enqueuing {}", music.filename);
let file = fs::File::open(music.filename).map_err(PlaybackError::from_err)?; let file = fs::File::open(music.filename).map_err(PlaybackError::from_err)?;
let stream = io::BufReader::new(file); let stream = io::BufReader::new(file);
let source = Decoder::new(stream).map_err(PlaybackError::from_err)?; let source = Decoder::new(stream).map_err(PlaybackError::from_err)?;
self.sink.append(source); self.sink.append(source);
self.sink.play(); // idk if this is necessary //self.sink.play(); // idk if this is necessary
Ok(()) Ok(())
}, },
Err(e) => Err(PlaybackError::from_err(e)) Err(e) => Err(PlaybackError::from_err(e))
}?; }?;
items_left -= 1; items_left -= 1;
if items_left == 0 { break; }
} }
Ok(()) Ok(())
} }
@ -102,6 +106,10 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
self.sink.len() self.sink.len()
} }
pub fn empty(&self) -> bool {
self.sink.empty()
}
pub fn save_m3u8<W: io::Write>(&mut self, w: &mut W) -> Result<(), PlaybackError> { pub fn save_m3u8<W: io::Write>(&mut self, w: &mut W) -> Result<(), PlaybackError> {
let mut playlist = MediaPlaylist { let mut playlist = MediaPlaylist {
version: 6, version: 6,
@ -133,6 +141,20 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
pub fn set_volume(&self, volume: f32) { pub fn set_volume(&self, volume: f32) {
self.sink.set_volume(volume); self.sink.set_volume(volume);
} }
pub fn new_sink(&mut self) -> Result<(), PlaybackError> {
let is_paused = self.sink.is_paused();
let volume = self.sink.volume();
self.stop();
self.sink = Sink::try_new(&self.output_handle).map_err(PlaybackError::from_err)?;
if is_paused {
self.sink.pause();
}
self.sink.set_volume(volume);
Ok(())
}
} }
#[cfg(test)] #[cfg(test)]

View file

@ -6,8 +6,6 @@ use mps_interpreter::tokens::MpsTokenReader;
use super::MpsPlayer; use super::MpsPlayer;
use super::PlaybackError; use super::PlaybackError;
const DEFAULT_QUEUE_SIZE: usize = 1;
pub struct MpsPlayerServer<T: MpsTokenReader> { pub struct MpsPlayerServer<T: MpsTokenReader> {
player: MpsPlayer<T>, player: MpsPlayer<T>,
control: Receiver<ControlAction>, control: Receiver<ControlAction>,
@ -25,29 +23,23 @@ impl<T: MpsTokenReader> MpsPlayerServer<T> {
fn run_loop(&mut self) { fn run_loop(&mut self) {
// this can panic since it's not on the main thread // this can panic since it's not on the main thread
if let Err(e) = self.player.enqueue_all() { // initial queue full
if let Err(e) = self.player.enqueue(1) {
self.event.send(PlayerAction::Exception(e)).unwrap(); self.event.send(PlayerAction::Exception(e)).unwrap();
} }
loop { loop {
let command = self.control.recv().unwrap(); let command = self.control.recv().unwrap();
// keep queue full let mut is_exiting = false;
if self.player.queue_len() < DEFAULT_QUEUE_SIZE {
if let Err(e) = self.player.enqueue(DEFAULT_QUEUE_SIZE - self.player.queue_len()) {
self.event.send(PlayerAction::Exception(e)).unwrap();
}
if self.player.queue_len() == 0 { // no more music to add
self.event.send(PlayerAction::End).unwrap();
}
}
// process command // process command
let mut is_exiting = false;
match command { match command {
ControlAction::Next{..} => { ControlAction::Next{..} => {
if self.player.queue_len() <= 1 { //println!("Executing next command (queue_len: {})", self.player.queue_len());
self.player.stop(); if let Err(e) = self.player.new_sink() {
//self.player.resume(); self.event.send(PlayerAction::Exception(e)).unwrap();
}
if !self.player.is_paused() {
self.player.enqueue(1).unwrap(); self.player.enqueue(1).unwrap();
} }
}, },
@ -76,12 +68,24 @@ impl<T: MpsTokenReader> MpsPlayerServer<T> {
self.player.set_volume((volume as f32) / (u32::MAX as f32)); self.player.set_volume((volume as f32) / (u32::MAX as f32));
} }
} }
// keep queue full (while playing music)
if self.player.queue_len() == 0 && !self.player.is_paused() && !is_exiting {
if let Err(e) = self.player.enqueue(1) {
self.event.send(PlayerAction::Exception(e)).unwrap();
}
if self.player.queue_len() == 0 { // no more music to add
is_exiting = true;
}
}
if command.needs_ack() { if command.needs_ack() {
self.event.send(PlayerAction::Acknowledge(command)).unwrap(); self.event.send(PlayerAction::Acknowledge(command)).unwrap();
} }
if is_exiting { break; } if is_exiting { break; }
} }
self.event.send(PlayerAction::End).unwrap();
} }
pub fn spawn<F: FnOnce() -> MpsPlayer<T> + Send + 'static>( pub fn spawn<F: FnOnce() -> MpsPlayer<T> + Send + 'static>(
@ -111,7 +115,7 @@ impl<T: MpsTokenReader> MpsPlayerServer<T> {
/// Action the controller wants the player to perform /// Action the controller wants the player to perform
#[allow(dead_code)] #[allow(dead_code)]
#[derive(Clone, PartialEq, Eq)] #[derive(Clone, PartialEq, Eq, Debug)]
pub enum ControlAction { pub enum ControlAction {
Next{ack: bool}, Next{ack: bool},
Previous{ack: bool}, Previous{ack: bool},
@ -143,7 +147,7 @@ impl ControlAction {
} }
/// Action the player has performed/encountered /// Action the player has performed/encountered
#[derive(Clone)] #[derive(Clone, Debug)]
pub enum PlayerAction { pub enum PlayerAction {
Acknowledge(ControlAction), Acknowledge(ControlAction),
Exception(PlaybackError), Exception(PlaybackError),

View file

@ -13,11 +13,17 @@ fn play_cursor() -> Result<(), PlaybackError> {
fn main() { fn main() {
//play_cursor().unwrap(); //play_cursor().unwrap();
let ctrl = MpsController::create(|| { let ctrl = MpsController::create(|| {
let cursor = io::Cursor::<&'static str>::new("sql(`SELECT * FROM songs JOIN artists ON songs.artist = artists.artist_id WHERE artists.name like 'thundercat'`);"); //let cursor = io::Cursor::<&'static str>::new("sql(`SELECT * FROM songs JOIN artists ON songs.artist = artists.artist_id WHERE artists.name like 'thundercat'`);");
let cursor = io::Cursor::<&'static str>::new(
"sql(`SELECT * FROM songs JOIN artists ON songs.artist = artists.artist_id WHERE artists.name like 'thundercat'`);
artist(`gwen`);"
);
let runner = MpsRunner::with_stream(cursor); let runner = MpsRunner::with_stream(cursor);
MpsPlayer::new(runner).unwrap() let player = MpsPlayer::new(runner).unwrap();
player.set_volume(0.8);
player
}); });
ctrl.wait_for_done().unwrap(); ctrl.wait_for_done().unwrap();
ctrl.exit().unwrap(); //ctrl.exit().unwrap(); // don't use both
} }