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]]
name = "rusqlite"
version = "0.26.1"
version = "0.26.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a82b0b91fad72160c56bf8da7a549b25d7c31109f52cc1437eac4c0ad2550a7"
checksum = "4ba4d3462c8b2e4d7f4fcfcf2b296dc6b65404fbbc7b63daa37fd485c149daf7"
dependencies = [
"bitflags",
"fallible-iterator",

View file

@ -1,7 +1,7 @@
use std::io;
use std::fs;
use rodio::{decoder::Decoder, OutputStream, Sink};
use rodio::{decoder::Decoder, OutputStream, Sink, OutputStreamHandle};
use m3u8_rs::{MediaPlaylist, MediaSegment};
@ -14,6 +14,7 @@ pub struct MpsPlayer<T: MpsTokenReader> {
sink: Sink,
#[allow(dead_code)]
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> {
@ -23,6 +24,7 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
runner: runner,
sink: Sink::try_new(&output_handle).map_err(PlaybackError::from_err)?,
output_stream: stream,
output_handle: output_handle
})
}
@ -53,7 +55,7 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
let stream = io::BufReader::new(file);
let source = Decoder::new(stream).map_err(PlaybackError::from_err)?;
self.sink.append(source);
self.sink.play(); // idk if this is necessary
//self.sink.play(); // idk if this is necessary
Ok(())
},
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> {
let mut items_left = count;
for item in &mut self.runner {
if items_left == 0 { return Ok(()); }
for item in &mut self.runner {
match item {
Ok(music) => {
//println!("Enqueuing {}", music.filename);
let file = fs::File::open(music.filename).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);
self.sink.play(); // idk if this is necessary
//self.sink.play(); // idk if this is necessary
Ok(())
},
Err(e) => Err(PlaybackError::from_err(e))
}?;
items_left -= 1;
if items_left == 0 { break; }
}
Ok(())
}
@ -102,6 +106,10 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
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> {
let mut playlist = MediaPlaylist {
version: 6,
@ -133,6 +141,20 @@ impl<T: MpsTokenReader> MpsPlayer<T> {
pub fn set_volume(&self, volume: f32) {
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)]

View file

@ -6,8 +6,6 @@ use mps_interpreter::tokens::MpsTokenReader;
use super::MpsPlayer;
use super::PlaybackError;
const DEFAULT_QUEUE_SIZE: usize = 1;
pub struct MpsPlayerServer<T: MpsTokenReader> {
player: MpsPlayer<T>,
control: Receiver<ControlAction>,
@ -25,29 +23,23 @@ impl<T: MpsTokenReader> MpsPlayerServer<T> {
fn run_loop(&mut self) {
// 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();
}
loop {
let command = self.control.recv().unwrap();
// keep queue full
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();
}
}
let mut is_exiting = false;
// process command
let mut is_exiting = false;
match command {
ControlAction::Next{..} => {
if self.player.queue_len() <= 1 {
self.player.stop();
//self.player.resume();
//println!("Executing next command (queue_len: {})", self.player.queue_len());
if let Err(e) = self.player.new_sink() {
self.event.send(PlayerAction::Exception(e)).unwrap();
}
if !self.player.is_paused() {
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));
}
}
// 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() {
self.event.send(PlayerAction::Acknowledge(command)).unwrap();
}
if is_exiting { break; }
}
self.event.send(PlayerAction::End).unwrap();
}
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
#[allow(dead_code)]
#[derive(Clone, PartialEq, Eq)]
#[derive(Clone, PartialEq, Eq, Debug)]
pub enum ControlAction {
Next{ack: bool},
Previous{ack: bool},
@ -143,7 +147,7 @@ impl ControlAction {
}
/// Action the player has performed/encountered
#[derive(Clone)]
#[derive(Clone, Debug)]
pub enum PlayerAction {
Acknowledge(ControlAction),
Exception(PlaybackError),

View file

@ -13,11 +13,17 @@ fn play_cursor() -> Result<(), PlaybackError> {
fn main() {
//play_cursor().unwrap();
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);
MpsPlayer::new(runner).unwrap()
let player = MpsPlayer::new(runner).unwrap();
player.set_volume(0.8);
player
});
ctrl.wait_for_done().unwrap();
ctrl.exit().unwrap();
//ctrl.exit().unwrap(); // don't use both
}