Add a SpircDelegate abstraction.

This commit is contained in:
Paul Lietar 2015-07-09 21:08:14 +01:00
parent cdd939e88e
commit 1264394838
4 changed files with 117 additions and 49 deletions

View file

@ -2,7 +2,7 @@
#![feature(plugin,scoped,zero_one,iter_arith,slice_position_elem,slice_bytes,bitset,arc_weak,append,future)] #![feature(plugin,scoped,zero_one,iter_arith,slice_position_elem,slice_bytes,bitset,arc_weak,append,future)]
#![allow(deprecated)] #![allow(deprecated)]
#![allow(unused_imports,dead_code)] //#![allow(unused_imports,dead_code)]
#![plugin(protobuf_macros)] #![plugin(protobuf_macros)]
#[macro_use] extern crate lazy_static; #[macro_use] extern crate lazy_static;

View file

@ -44,7 +44,7 @@ fn main() {
let player = Player::new(&session); let player = Player::new(&session);
let mut spirc_manager = SpircManager::new(&session, &player, username, name); let mut spirc_manager = SpircManager::new(&session, player, username, name);
spirc_manager.run(); spirc_manager.run();
poll_thread.join(); poll_thread.join();

View file

@ -1,28 +1,28 @@
use portaudio; use portaudio;
use vorbis; use vorbis;
use std::sync::{mpsc, Mutex, Arc, Condvar}; use std::sync::{mpsc, Mutex, Arc, Condvar, MutexGuard};
use std::thread; use std::thread;
use metadata::TrackRef; use metadata::TrackRef;
use session::Session; use session::Session;
use audio_file::AudioFile;
use audio_decrypt::AudioDecrypt; use audio_decrypt::AudioDecrypt;
use util::{self, SpotifyId, Subfile}; use util::{self, SpotifyId, Subfile};
use librespot_protocol::spirc::PlayStatus; use spirc::{SpircState, SpircDelegate, PlayStatus};
pub enum PlayerCommand { pub struct Player<'s> {
Load(SpotifyId, bool, u32), state: Arc<(Mutex<PlayerState>, Condvar)>,
Play,
Pause, commands: mpsc::Sender<PlayerCommand>,
Stop,
Seek(u32) #[allow(dead_code)]
thread: thread::JoinGuard<'s, ()>,
} }
pub struct PlayerState { pub struct PlayerState {
pub status: PlayStatus, status: PlayStatus,
pub position_ms: u32, position_ms: u32,
pub position_measured_at: i64, position_measured_at: i64,
pub update_time: i64 update_time: i64
} }
struct PlayerInternal<'s> { struct PlayerInternal<'s> {
@ -32,13 +32,12 @@ struct PlayerInternal<'s> {
commands: mpsc::Receiver<PlayerCommand>, commands: mpsc::Receiver<PlayerCommand>,
} }
pub struct Player<'s> { enum PlayerCommand {
pub state: Arc<(Mutex<PlayerState>, Condvar)>, Load(SpotifyId, bool, u32),
Play,
commands: mpsc::Sender<PlayerCommand>, Pause,
Stop,
#[allow(dead_code)] Seek(u32)
thread: thread::JoinGuard<'s, ()>,
} }
impl <'s> Player<'s> { impl <'s> Player<'s> {
@ -67,7 +66,7 @@ impl <'s> Player<'s> {
} }
} }
pub fn command(&self, cmd: PlayerCommand) { fn command(&self, cmd: PlayerCommand) {
self.commands.send(cmd).unwrap(); self.commands.send(cmd).unwrap();
} }
} }
@ -183,4 +182,51 @@ impl <'s> PlayerInternal<'s> {
} }
} }
impl <'s> SpircDelegate for Player<'s> {
type State = PlayerState;
fn load(&self, track: SpotifyId,
start_playing: bool, position_ms: u32) {
self.command(PlayerCommand::Load(track, start_playing, position_ms));
}
fn play(&self) {
self.command(PlayerCommand::Play)
}
fn pause(&self) {
self.command(PlayerCommand::Pause)
}
fn stop(&self) {
self.command(PlayerCommand::Stop)
}
fn seek(&self, position_ms: u32) {
self.command(PlayerCommand::Seek(position_ms));
}
fn state(&self) -> MutexGuard<Self::State> {
self.state.0.lock().unwrap()
}
fn wait_update<'a>(&'a self, guard: MutexGuard<'a, Self::State>)
-> MutexGuard<'a, Self::State> {
self.state.1.wait(guard).unwrap()
}
}
impl SpircState for PlayerState {
fn status(&self) -> PlayStatus {
return self.status;
}
fn position(&self) -> (u32, i64) {
return (self.position_ms, self.position_measured_at);
}
fn update_time(&self) -> i64 {
return self.update_time;
}
}

View file

@ -1,18 +1,17 @@
use protobuf::{self, Message}; use protobuf::{self, Message};
use util; use util;
use session::{Config, Session}; use session::Session;
use util::SpotifyId; use util::SpotifyId;
use util::version::version_string; use util::version::version_string;
use player::{Player, PlayerCommand};
use mercury::{MercuryRequest, MercuryMethod}; use mercury::{MercuryRequest, MercuryMethod};
use std::sync::MutexGuard;
use librespot_protocol as protocol; use librespot_protocol as protocol;
use librespot_protocol::spirc::PlayStatus; pub use librespot_protocol::spirc::PlayStatus;
pub struct SpircManager<'s> { pub struct SpircManager<'s, D: SpircDelegate> {
player: &'s Player<'s>, delegate: D,
session: &'s Session, session: &'s Session,
username: String, username: String,
@ -37,11 +36,33 @@ pub struct SpircManager<'s> {
track: Option<SpotifyId> track: Option<SpotifyId>
} }
impl <'s> SpircManager<'s> { pub trait SpircDelegate {
pub fn new(session: &'s Session, player: &'s Player<'s>, username: String, name: String) -> SpircManager<'s> { type State : SpircState;
fn load(&self, track: SpotifyId,
start_playing: bool, position_ms: u32);
fn play(&self);
fn pause(&self);
fn seek(&self, position_ms: u32);
fn stop(&self);
fn state(&self) -> MutexGuard<Self::State>;
fn wait_update<'a>(&'a self, guard: MutexGuard<'a, Self::State>)
-> MutexGuard<'a, Self::State>;
}
pub trait SpircState {
fn status(&self) -> PlayStatus;
fn position(&self) -> (u32, i64);
fn update_time(&self) -> i64;
}
impl <'s, D: SpircDelegate> SpircManager<'s, D> {
pub fn new(session: &'s Session, delegate: D,
username: String, name: String) -> SpircManager<'s, D> {
SpircManager { SpircManager {
delegate: delegate,
session: &session, session: &session,
player: &player,
username: username.clone(), username: username.clone(),
state_update_id: 0, state_update_id: 0,
@ -88,10 +109,11 @@ impl <'s> SpircManager<'s> {
} }
} }
let h = self.player.state.0.lock().unwrap(); if {
if h.update_time > self.state_update_id { let state = self.delegate.state();
state.update_time() > self.state_update_id
} {
self.state_update_id = util::now_ms(); self.state_update_id = util::now_ms();
drop(h);
self.notify(None); self.notify(None);
} }
} }
@ -114,24 +136,23 @@ impl <'s> SpircManager<'s> {
let index = frame.get_state().get_playing_track_index() as usize; let index = frame.get_state().get_playing_track_index() as usize;
let track = SpotifyId::from_raw(frame.get_state().get_track()[index].get_gid()); let track = SpotifyId::from_raw(frame.get_state().get_track()[index].get_gid());
let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
self.track = Some(track); self.track = Some(track);
self.player.command(PlayerCommand::Load(track, self.delegate.load(track, play, frame.get_state().get_position_ms());
frame.get_state().get_status() == PlayStatus::kPlayStatusPlay,
frame.get_state().get_position_ms()));
} }
protocol::spirc::MessageType::kMessageTypePlay => { protocol::spirc::MessageType::kMessageTypePlay => {
self.player.command(PlayerCommand::Play); self.delegate.play();
} }
protocol::spirc::MessageType::kMessageTypePause => { protocol::spirc::MessageType::kMessageTypePause => {
self.player.command(PlayerCommand::Pause); self.delegate.pause();
} }
protocol::spirc::MessageType::kMessageTypeSeek => { protocol::spirc::MessageType::kMessageTypeSeek => {
self.player.command(PlayerCommand::Seek(frame.get_position())); self.delegate.seek(frame.get_position());
} }
protocol::spirc::MessageType::kMessageTypeNotify => { protocol::spirc::MessageType::kMessageTypeNotify => {
if self.is_active && frame.get_device_state().get_is_active() { if self.is_active && frame.get_device_state().get_is_active() {
self.is_active = false; self.is_active = false;
self.player.command(PlayerCommand::Stop); self.delegate.stop();
} }
} }
_ => () _ => ()
@ -153,7 +174,7 @@ impl <'s> SpircManager<'s> {
}); });
if self.is_active { if self.is_active {
pkt.set_state(self.state()); pkt.set_state(self.spirc_state());
} }
self.session.mercury(MercuryRequest{ self.session.mercury(MercuryRequest{
@ -164,13 +185,14 @@ impl <'s> SpircManager<'s> {
}); });
} }
fn state(&mut self) -> protocol::spirc::State { fn spirc_state(&self) -> protocol::spirc::State {
let state = self.player.state.0.lock().unwrap(); let state = self.delegate.state();
let (position_ms, position_measured_at) = state.position();
protobuf_init!(protocol::spirc::State::new(), { protobuf_init!(protocol::spirc::State::new(), {
status: state.status, status: state.status(),
position_ms: state.position_ms, position_ms: position_ms,
position_measured_at: state.position_measured_at as u64, position_measured_at: position_measured_at as u64,
playing_track_index: 0, playing_track_index: 0,
track => [ track => [
@ -189,7 +211,7 @@ impl <'s> SpircManager<'s> {
}) })
} }
fn device_state(&mut self) -> protocol::spirc::DeviceState { fn device_state(&self) -> protocol::spirc::DeviceState {
protobuf_init!(protocol::spirc::DeviceState::new(), { protobuf_init!(protocol::spirc::DeviceState::new(), {
sw_version: version_string(), sw_version: version_string(),
is_active: self.is_active, is_active: self.is_active,