Fix enqueuing and song skip playback issues
This commit is contained in:
parent
03318a0ef5
commit
9b6472b072
4 changed files with 59 additions and 27 deletions
4
Cargo.lock
generated
4
Cargo.lock
generated
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
if items_left == 0 { return Ok(()); }
|
||||
for item in &mut self.runner {
|
||||
if items_left == 0 { return Ok(()); }
|
||||
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)]
|
||||
|
|
|
@ -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),
|
||||
|
|
12
src/main.rs
12
src/main.rs
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue