2017-01-29 14:11:20 +00:00
|
|
|
use futures::future;
|
|
|
|
use futures::sync::{oneshot, mpsc};
|
2018-01-21 19:29:31 +00:00
|
|
|
use futures::{Future, Stream, Sink, Async, Poll};
|
2017-01-29 14:11:20 +00:00
|
|
|
use protobuf::{self, Message};
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2017-08-03 18:58:44 +00:00
|
|
|
use core::config::ConnectConfig;
|
|
|
|
use core::mercury::MercuryError;
|
|
|
|
use core::session::Session;
|
|
|
|
use core::util::{now_ms, SpotifyId, SeqGenerator};
|
|
|
|
use core::version;
|
2016-03-17 03:31:57 +00:00
|
|
|
|
2016-01-27 10:40:00 +00:00
|
|
|
use protocol;
|
2017-01-29 14:11:20 +00:00
|
|
|
use protocol::spirc::{PlayStatus, State, MessageType, Frame, DeviceState};
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2018-02-09 01:05:50 +00:00
|
|
|
use playback::mixer::Mixer;
|
|
|
|
use playback::player::Player;
|
2017-08-03 18:58:44 +00:00
|
|
|
|
2018-01-25 22:37:28 +00:00
|
|
|
use std;
|
2017-11-03 01:15:27 +00:00
|
|
|
use rand;
|
|
|
|
use rand::Rng;
|
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
pub struct SpircTask {
|
2016-01-20 14:11:49 +00:00
|
|
|
player: Player,
|
2017-02-22 04:17:04 +00:00
|
|
|
mixer: Box<Mixer>,
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
sequence: SeqGenerator<u32>,
|
2015-07-08 19:50:44 +00:00
|
|
|
|
|
|
|
ident: String,
|
2017-01-20 14:44:13 +00:00
|
|
|
device: DeviceState,
|
2017-01-29 14:11:20 +00:00
|
|
|
state: State,
|
2016-02-13 01:09:15 +00:00
|
|
|
|
2018-01-21 19:29:31 +00:00
|
|
|
subscription: Box<Stream<Item = Frame, Error = MercuryError>>,
|
|
|
|
sender: Box<Sink<SinkItem = Frame, SinkError = MercuryError>>,
|
2017-01-20 12:56:42 +00:00
|
|
|
commands: mpsc::UnboundedReceiver<SpircCommand>,
|
2018-01-21 19:29:31 +00:00
|
|
|
end_of_track: Box<Future<Item = (), Error = oneshot::Canceled>>,
|
2017-01-20 12:56:42 +00:00
|
|
|
|
|
|
|
shutdown: bool,
|
2017-02-22 04:17:04 +00:00
|
|
|
session: Session,
|
2017-01-20 12:56:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub enum SpircCommand {
|
2017-02-23 11:05:32 +00:00
|
|
|
Play,
|
|
|
|
PlayPause,
|
|
|
|
Pause,
|
|
|
|
Prev,
|
|
|
|
Next,
|
|
|
|
VolumeUp,
|
|
|
|
VolumeDown,
|
2017-01-20 12:56:42 +00:00
|
|
|
Shutdown
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct Spirc {
|
|
|
|
commands: mpsc::UnboundedSender<SpircCommand>,
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
fn initial_state() -> State {
|
|
|
|
protobuf_init!(protocol::spirc::State::new(), {
|
|
|
|
repeat: false,
|
|
|
|
shuffle: false,
|
|
|
|
status: PlayStatus::kPlayStatusStop,
|
|
|
|
position_ms: 0,
|
|
|
|
position_measured_at: 0,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2017-08-03 18:31:15 +00:00
|
|
|
fn initial_device_state(config: ConnectConfig, volume: u16) -> DeviceState {
|
2017-01-20 14:44:13 +00:00
|
|
|
protobuf_init!(DeviceState::new(), {
|
|
|
|
sw_version: version::version_string(),
|
|
|
|
is_active: false,
|
|
|
|
can_play: true,
|
|
|
|
volume: volume as u32,
|
2017-08-03 18:31:15 +00:00
|
|
|
name: config.name,
|
2017-01-20 14:44:13 +00:00
|
|
|
capabilities => [
|
|
|
|
@{
|
|
|
|
typ: protocol::spirc::CapabilityType::kCanBePlayer,
|
2017-01-29 14:11:20 +00:00
|
|
|
intValue => [1]
|
2017-01-20 14:44:13 +00:00
|
|
|
},
|
|
|
|
@{
|
|
|
|
typ: protocol::spirc::CapabilityType::kDeviceType,
|
2017-08-03 18:31:15 +00:00
|
|
|
intValue => [config.device_type as i64]
|
2017-01-20 14:44:13 +00:00
|
|
|
},
|
|
|
|
@{
|
|
|
|
typ: protocol::spirc::CapabilityType::kGaiaEqConnectId,
|
|
|
|
intValue => [1]
|
|
|
|
},
|
|
|
|
@{
|
|
|
|
typ: protocol::spirc::CapabilityType::kSupportsLogout,
|
|
|
|
intValue => [0]
|
|
|
|
},
|
|
|
|
@{
|
|
|
|
typ: protocol::spirc::CapabilityType::kIsObservable,
|
|
|
|
intValue => [1]
|
|
|
|
},
|
|
|
|
@{
|
|
|
|
typ: protocol::spirc::CapabilityType::kVolumeSteps,
|
2017-02-09 01:33:52 +00:00
|
|
|
intValue => [64]
|
2017-01-20 14:44:13 +00:00
|
|
|
},
|
|
|
|
@{
|
|
|
|
typ: protocol::spirc::CapabilityType::kSupportedContexts,
|
|
|
|
stringValue => [
|
|
|
|
"album",
|
|
|
|
"playlist",
|
|
|
|
"search",
|
|
|
|
"inbox",
|
|
|
|
"toplist",
|
|
|
|
"starred",
|
|
|
|
"publishedstarred",
|
|
|
|
"track",
|
|
|
|
]
|
|
|
|
},
|
|
|
|
@{
|
|
|
|
typ: protocol::spirc::CapabilityType::kSupportedTypes,
|
|
|
|
stringValue => [
|
|
|
|
"audio/local",
|
|
|
|
"audio/track",
|
|
|
|
"local",
|
|
|
|
"track",
|
|
|
|
]
|
|
|
|
}
|
|
|
|
],
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2018-01-29 22:37:30 +00:00
|
|
|
fn volume_to_mixer(volume: u16) -> u16 {
|
|
|
|
// Volume conversion taken from https://www.dr-lex.be/info-stuff/volumecontrols.html#ideal2
|
|
|
|
// Convert the given volume [0..0xffff] to a dB gain
|
|
|
|
// We assume a dB range of 60dB.
|
|
|
|
// Use the equatation: a * exp(b * x)
|
|
|
|
// in which a = IDEAL_FACTOR, b = 1/1000
|
|
|
|
const IDEAL_FACTOR: f64 = 6.908;
|
|
|
|
let normalized_volume = volume as f64 / std::u16::MAX as f64; // To get a value between 0 and 1
|
|
|
|
|
|
|
|
let mut val = std::u16::MAX;
|
|
|
|
// Prevent val > std::u16::MAX due to rounding errors
|
2018-02-09 01:05:50 +00:00
|
|
|
if normalized_volume < 0.999 {
|
2018-01-29 22:37:30 +00:00
|
|
|
let new_volume = (normalized_volume * IDEAL_FACTOR).exp() / 1000.0;
|
|
|
|
val = (new_volume * std::u16::MAX as f64) as u16;
|
2018-01-25 22:37:28 +00:00
|
|
|
}
|
2018-01-29 22:37:30 +00:00
|
|
|
|
2018-02-09 01:05:50 +00:00
|
|
|
debug!("input volume:{} to mixer: {}", volume, val);
|
2018-01-29 22:37:30 +00:00
|
|
|
|
|
|
|
// return the scale factor (0..0xffff) (equivalent to a voltage multiplier).
|
|
|
|
val
|
2018-01-25 22:37:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
impl Spirc {
|
2017-08-03 18:31:15 +00:00
|
|
|
pub fn new(config: ConnectConfig, session: Session, player: Player, mixer: Box<Mixer>)
|
2017-02-21 22:46:19 +00:00
|
|
|
-> (Spirc, SpircTask)
|
|
|
|
{
|
2017-02-22 04:17:04 +00:00
|
|
|
debug!("new Spirc[{}]", session.session_id());
|
|
|
|
|
2016-03-17 03:06:56 +00:00
|
|
|
let ident = session.device_id().to_owned();
|
2016-01-01 23:16:12 +00:00
|
|
|
|
2017-05-15 20:55:49 +00:00
|
|
|
let uri = format!("hm://remote/3/user/{}/", session.username());
|
2017-01-20 12:56:42 +00:00
|
|
|
|
|
|
|
let subscription = session.mercury().subscribe(&uri as &str);
|
|
|
|
let subscription = subscription.map(|stream| stream.map_err(|_| MercuryError)).flatten_stream();
|
2018-01-21 19:29:31 +00:00
|
|
|
let subscription = Box::new(subscription.map(|response| -> Frame {
|
2017-01-20 12:56:42 +00:00
|
|
|
let data = response.payload.first().unwrap();
|
|
|
|
protobuf::parse_from_bytes(data).unwrap()
|
2018-01-21 19:29:31 +00:00
|
|
|
}));
|
2017-01-20 12:56:42 +00:00
|
|
|
|
|
|
|
let sender = Box::new(session.mercury().sender(uri).with(|frame: Frame| {
|
|
|
|
Ok(frame.write_to_bytes().unwrap())
|
|
|
|
}));
|
|
|
|
|
|
|
|
let (cmd_tx, cmd_rx) = mpsc::unbounded();
|
|
|
|
|
2017-12-06 13:37:34 +00:00
|
|
|
let volume = config.volume as u16;
|
2017-08-03 18:31:15 +00:00
|
|
|
let device = initial_device_state(config, volume);
|
2018-01-29 22:37:30 +00:00
|
|
|
mixer.set_volume(volume_to_mixer(volume as u16));
|
2017-01-20 14:44:13 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
let mut task = SpircTask {
|
2016-01-20 14:11:49 +00:00
|
|
|
player: player,
|
2017-02-21 22:46:19 +00:00
|
|
|
mixer: mixer,
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
sequence: SeqGenerator::new(1),
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2016-01-01 23:16:12 +00:00
|
|
|
ident: ident,
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
device: device,
|
|
|
|
state: initial_state(),
|
2016-02-13 01:09:15 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
subscription: subscription,
|
|
|
|
sender: sender,
|
|
|
|
commands: cmd_rx,
|
2018-01-21 19:29:31 +00:00
|
|
|
end_of_track: Box::new(future::empty()),
|
2016-01-20 15:47:05 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
shutdown: false,
|
2017-02-22 04:17:04 +00:00
|
|
|
session: session.clone(),
|
2016-01-20 15:47:05 +00:00
|
|
|
};
|
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
let spirc = Spirc {
|
|
|
|
commands: cmd_tx,
|
|
|
|
};
|
2016-01-20 15:47:05 +00:00
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
task.hello();
|
2016-02-16 21:26:51 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
(spirc, task)
|
2016-02-16 21:26:51 +00:00
|
|
|
}
|
2016-02-16 21:52:55 +00:00
|
|
|
|
2017-02-23 11:05:32 +00:00
|
|
|
pub fn play(&self) {
|
2018-01-21 19:38:30 +00:00
|
|
|
let _ = self.commands.unbounded_send(SpircCommand::Play);
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
|
|
|
pub fn play_pause(&self) {
|
2018-01-21 19:38:30 +00:00
|
|
|
let _ = self.commands.unbounded_send(SpircCommand::PlayPause);
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
|
|
|
pub fn pause(&self) {
|
2018-01-21 19:38:30 +00:00
|
|
|
let _ = self.commands.unbounded_send(SpircCommand::Pause);
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
|
|
|
pub fn prev(&self) {
|
2018-01-21 19:38:30 +00:00
|
|
|
let _ = self.commands.unbounded_send(SpircCommand::Prev);
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
|
|
|
pub fn next(&self) {
|
2018-01-21 19:38:30 +00:00
|
|
|
let _ = self.commands.unbounded_send(SpircCommand::Next);
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
|
|
|
pub fn volume_up(&self) {
|
2018-01-21 19:38:30 +00:00
|
|
|
let _ = self.commands.unbounded_send(SpircCommand::VolumeUp);
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
|
|
|
pub fn volume_down(&self) {
|
2018-01-21 19:38:30 +00:00
|
|
|
let _ = self.commands.unbounded_send(SpircCommand::VolumeDown);
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
2017-01-29 16:25:09 +00:00
|
|
|
pub fn shutdown(&self) {
|
2018-01-21 19:38:30 +00:00
|
|
|
let _ = self.commands.unbounded_send(SpircCommand::Shutdown);
|
2016-02-16 21:52:55 +00:00
|
|
|
}
|
2017-01-20 12:56:42 +00:00
|
|
|
}
|
2016-02-16 21:52:55 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
impl Future for SpircTask {
|
|
|
|
type Item = ();
|
|
|
|
type Error = ();
|
|
|
|
|
|
|
|
fn poll(&mut self) -> Poll<(), ()> {
|
|
|
|
loop {
|
|
|
|
let mut progress = false;
|
|
|
|
|
|
|
|
if !self.shutdown {
|
|
|
|
match self.subscription.poll().unwrap() {
|
|
|
|
Async::Ready(Some(frame)) => {
|
|
|
|
progress = true;
|
|
|
|
self.handle_frame(frame);
|
|
|
|
}
|
|
|
|
Async::Ready(None) => panic!("subscription terminated"),
|
|
|
|
Async::NotReady => (),
|
|
|
|
}
|
2016-02-16 21:52:55 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
match self.commands.poll().unwrap() {
|
|
|
|
Async::Ready(Some(command)) => {
|
|
|
|
progress = true;
|
|
|
|
self.handle_command(command);
|
|
|
|
}
|
|
|
|
Async::Ready(None) => (),
|
|
|
|
Async::NotReady => (),
|
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
|
|
|
match self.end_of_track.poll() {
|
|
|
|
Ok(Async::Ready(())) => {
|
|
|
|
progress = true;
|
|
|
|
self.handle_end_of_track();
|
|
|
|
}
|
|
|
|
Ok(Async::NotReady) => (),
|
|
|
|
Err(oneshot::Canceled) => {
|
2018-01-21 19:29:31 +00:00
|
|
|
self.end_of_track = Box::new(future::empty())
|
2017-01-29 14:11:20 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-20 12:56:42 +00:00
|
|
|
}
|
2016-02-17 14:24:40 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
let poll_sender = self.sender.poll_complete().unwrap();
|
2016-02-17 19:35:31 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
// Only shutdown once we've flushed out all our messages
|
|
|
|
if self.shutdown && poll_sender.is_ready() {
|
|
|
|
return Ok(Async::Ready(()));
|
|
|
|
}
|
2016-02-17 19:35:52 +00:00
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
if !progress {
|
|
|
|
return Ok(Async::NotReady);
|
|
|
|
}
|
|
|
|
}
|
2016-02-17 19:35:52 +00:00
|
|
|
}
|
2016-01-20 15:47:05 +00:00
|
|
|
}
|
|
|
|
|
2017-01-20 12:56:42 +00:00
|
|
|
impl SpircTask {
|
|
|
|
fn handle_command(&mut self, cmd: SpircCommand) {
|
2017-02-23 11:05:32 +00:00
|
|
|
let active = self.device.get_is_active();
|
2017-01-20 12:56:42 +00:00
|
|
|
match cmd {
|
2017-02-23 11:05:32 +00:00
|
|
|
SpircCommand::Play => {
|
|
|
|
if active {
|
|
|
|
self.handle_play();
|
|
|
|
self.notify(None);
|
|
|
|
} else {
|
|
|
|
CommandSender::new(self, MessageType::kMessageTypePlay).send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SpircCommand::PlayPause => {
|
|
|
|
if active {
|
|
|
|
self.handle_play_pause();
|
|
|
|
self.notify(None);
|
|
|
|
} else {
|
|
|
|
CommandSender::new(self, MessageType::kMessageTypePlayPause).send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SpircCommand::Pause => {
|
|
|
|
if active {
|
|
|
|
self.handle_pause();
|
|
|
|
self.notify(None);
|
|
|
|
} else {
|
|
|
|
CommandSender::new(self, MessageType::kMessageTypePause).send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SpircCommand::Prev => {
|
|
|
|
if active {
|
|
|
|
self.handle_prev();
|
|
|
|
self.notify(None);
|
|
|
|
} else {
|
|
|
|
CommandSender::new(self, MessageType::kMessageTypePrev).send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SpircCommand::Next => {
|
|
|
|
if active {
|
|
|
|
self.handle_next();
|
|
|
|
self.notify(None);
|
|
|
|
} else {
|
|
|
|
CommandSender::new(self, MessageType::kMessageTypeNext).send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SpircCommand::VolumeUp => {
|
|
|
|
if active {
|
|
|
|
self.handle_volume_up();
|
|
|
|
self.notify(None);
|
|
|
|
} else {
|
|
|
|
CommandSender::new(self, MessageType::kMessageTypeVolumeUp).send();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
SpircCommand::VolumeDown => {
|
|
|
|
if active {
|
|
|
|
self.handle_volume_down();
|
|
|
|
self.notify(None);
|
|
|
|
} else {
|
|
|
|
CommandSender::new(self, MessageType::kMessageTypeVolumeDown).send();
|
|
|
|
}
|
|
|
|
}
|
2017-01-20 12:56:42 +00:00
|
|
|
SpircCommand::Shutdown => {
|
|
|
|
CommandSender::new(self, MessageType::kMessageTypeGoodbye).send();
|
|
|
|
self.shutdown = true;
|
|
|
|
self.commands.close();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_frame(&mut self, frame: Frame) {
|
|
|
|
debug!("{:?} {:?} {} {} {}",
|
|
|
|
frame.get_typ(),
|
|
|
|
frame.get_device_state().get_name(),
|
|
|
|
frame.get_ident(),
|
|
|
|
frame.get_seq_nr(),
|
|
|
|
frame.get_state_update_id());
|
|
|
|
|
2016-01-20 15:47:05 +00:00
|
|
|
if frame.get_ident() == self.ident ||
|
|
|
|
(frame.get_recipient().len() > 0 && !frame.get_recipient().contains(&self.ident)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-07-08 19:50:44 +00:00
|
|
|
match frame.get_typ() {
|
2016-02-18 23:02:41 +00:00
|
|
|
MessageType::kMessageTypeHello => {
|
2017-01-29 14:11:20 +00:00
|
|
|
self.notify(Some(frame.get_ident()));
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2016-02-18 23:02:41 +00:00
|
|
|
MessageType::kMessageTypeLoad => {
|
2017-01-20 14:44:13 +00:00
|
|
|
if !self.device.get_is_active() {
|
|
|
|
self.device.set_is_active(true);
|
|
|
|
self.device.set_became_active_at(now_ms());
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
self.update_tracks(&frame);
|
|
|
|
|
|
|
|
if self.state.get_track().len() > 0 {
|
|
|
|
self.state.set_position_ms(frame.get_state().get_position_ms());
|
|
|
|
self.state.set_position_measured_at(now_ms() as u64);
|
|
|
|
|
2016-08-23 21:43:55 +00:00
|
|
|
let play = frame.get_state().get_status() == PlayStatus::kPlayStatusPlay;
|
2017-01-29 14:11:20 +00:00
|
|
|
self.load_track(play);
|
2016-08-23 21:43:55 +00:00
|
|
|
} else {
|
2017-01-29 14:11:20 +00:00
|
|
|
self.state.set_status(PlayStatus::kPlayStatusStop);
|
2016-08-23 21:43:55 +00:00
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
|
|
|
self.notify(None);
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2016-02-18 23:02:41 +00:00
|
|
|
MessageType::kMessageTypePlay => {
|
2017-02-23 11:05:32 +00:00
|
|
|
self.handle_play();
|
|
|
|
self.notify(None);
|
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2017-02-23 11:05:32 +00:00
|
|
|
MessageType::kMessageTypePlayPause => {
|
|
|
|
self.handle_play_pause();
|
2017-01-29 14:11:20 +00:00
|
|
|
self.notify(None);
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2016-02-18 23:02:41 +00:00
|
|
|
MessageType::kMessageTypePause => {
|
2017-02-23 11:05:32 +00:00
|
|
|
self.handle_pause();
|
2017-01-29 14:11:20 +00:00
|
|
|
self.notify(None);
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2016-02-18 23:02:41 +00:00
|
|
|
MessageType::kMessageTypeNext => {
|
2017-02-23 11:05:32 +00:00
|
|
|
self.handle_next();
|
2017-01-29 14:11:20 +00:00
|
|
|
self.notify(None);
|
2016-01-14 03:27:34 +00:00
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2016-02-18 23:02:41 +00:00
|
|
|
MessageType::kMessageTypePrev => {
|
2017-02-23 11:05:32 +00:00
|
|
|
self.handle_prev();
|
|
|
|
self.notify(None);
|
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2017-02-23 11:05:32 +00:00
|
|
|
MessageType::kMessageTypeVolumeUp => {
|
|
|
|
self.handle_volume_up();
|
|
|
|
self.notify(None);
|
|
|
|
}
|
|
|
|
|
|
|
|
MessageType::kMessageTypeVolumeDown => {
|
|
|
|
self.handle_volume_down();
|
2017-01-29 14:11:20 +00:00
|
|
|
self.notify(None);
|
2016-01-14 03:27:34 +00:00
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2017-10-27 17:45:02 +00:00
|
|
|
MessageType::kMessageTypeRepeat => {
|
|
|
|
self.state.set_repeat(frame.get_state().get_repeat());
|
|
|
|
self.notify(None);
|
|
|
|
}
|
|
|
|
|
2017-11-03 01:15:27 +00:00
|
|
|
MessageType::kMessageTypeShuffle => {
|
|
|
|
self.state.set_shuffle(frame.get_state().get_shuffle());
|
|
|
|
if self.state.get_shuffle()
|
|
|
|
{
|
|
|
|
let current_index = self.state.get_playing_track_index();
|
|
|
|
{
|
|
|
|
let tracks = self.state.mut_track();
|
|
|
|
tracks.swap(0, current_index as usize);
|
|
|
|
if let Some((_, rest)) = tracks.split_first_mut() {
|
|
|
|
rand::thread_rng().shuffle(rest);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
self.state.set_playing_track_index(0);
|
|
|
|
} else {
|
|
|
|
let context = self.state.get_context_uri();
|
|
|
|
debug!("{:?}", context);
|
|
|
|
}
|
|
|
|
self.notify(None);
|
|
|
|
}
|
|
|
|
|
2016-02-18 23:02:41 +00:00
|
|
|
MessageType::kMessageTypeSeek => {
|
2017-01-29 14:11:20 +00:00
|
|
|
let position = frame.get_position();
|
|
|
|
|
|
|
|
self.state.set_position_ms(position);
|
|
|
|
self.state.set_position_measured_at(now_ms() as u64);
|
|
|
|
self.player.seek(position);
|
|
|
|
self.notify(None);
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2016-02-18 23:02:41 +00:00
|
|
|
MessageType::kMessageTypeReplace => {
|
2017-01-29 14:11:20 +00:00
|
|
|
self.update_tracks(&frame);
|
|
|
|
self.notify(None);
|
2016-01-16 01:15:24 +00:00
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
|
|
|
MessageType::kMessageTypeVolume => {
|
2018-01-25 22:37:28 +00:00
|
|
|
self.device.set_volume(frame.get_volume());
|
2018-01-29 22:37:30 +00:00
|
|
|
self.mixer.set_volume(volume_to_mixer(frame.get_volume() as u16));
|
2017-01-29 14:11:20 +00:00
|
|
|
self.notify(None);
|
|
|
|
}
|
|
|
|
|
2016-02-18 23:02:41 +00:00
|
|
|
MessageType::kMessageTypeNotify => {
|
2017-01-29 14:11:20 +00:00
|
|
|
if self.device.get_is_active() &&
|
|
|
|
frame.get_device_state().get_is_active()
|
|
|
|
{
|
2017-01-20 14:44:13 +00:00
|
|
|
self.device.set_is_active(false);
|
2017-01-29 14:11:20 +00:00
|
|
|
self.state.set_status(PlayStatus::kPlayStatusStop);
|
2016-01-20 14:11:49 +00:00
|
|
|
self.player.stop();
|
2017-02-21 22:46:19 +00:00
|
|
|
self.mixer.stop();
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2016-01-02 15:19:39 +00:00
|
|
|
_ => (),
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-23 11:05:32 +00:00
|
|
|
fn handle_play(&mut self) {
|
|
|
|
if self.state.get_status() == PlayStatus::kPlayStatusPause {
|
|
|
|
self.mixer.start();
|
|
|
|
self.player.play();
|
|
|
|
self.state.set_status(PlayStatus::kPlayStatusPlay);
|
|
|
|
self.state.set_position_measured_at(now_ms() as u64);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_play_pause(&mut self) {
|
|
|
|
match self.state.get_status() {
|
|
|
|
PlayStatus::kPlayStatusPlay => self.handle_pause(),
|
|
|
|
PlayStatus::kPlayStatusPause => self.handle_play(),
|
|
|
|
_ => (),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_pause(&mut self) {
|
|
|
|
if self.state.get_status() == PlayStatus::kPlayStatusPlay {
|
|
|
|
self.player.pause();
|
|
|
|
self.mixer.stop();
|
|
|
|
self.state.set_status(PlayStatus::kPlayStatusPause);
|
|
|
|
|
|
|
|
let now = now_ms() as u64;
|
|
|
|
let position = self.state.get_position_ms();
|
|
|
|
|
|
|
|
let diff = now - self.state.get_position_measured_at();
|
|
|
|
|
|
|
|
self.state.set_position_ms(position + diff as u32);
|
|
|
|
self.state.set_position_measured_at(now);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_next(&mut self) {
|
|
|
|
let current_index = self.state.get_playing_track_index();
|
2017-10-27 17:45:02 +00:00
|
|
|
let num_tracks = self.state.get_track().len() as u32;
|
|
|
|
let new_index = (current_index + 1) % num_tracks;
|
|
|
|
|
|
|
|
let mut was_last_track = (current_index + 1) >= num_tracks;
|
|
|
|
if self.state.get_repeat() {
|
|
|
|
was_last_track = false;
|
|
|
|
}
|
2017-02-23 11:05:32 +00:00
|
|
|
|
|
|
|
self.state.set_playing_track_index(new_index);
|
|
|
|
self.state.set_position_ms(0);
|
|
|
|
self.state.set_position_measured_at(now_ms() as u64);
|
|
|
|
|
2017-10-27 17:45:02 +00:00
|
|
|
self.load_track(!was_last_track);
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_prev(&mut self) {
|
|
|
|
// Previous behaves differently based on the position
|
|
|
|
// Under 3s it goes to the previous song
|
|
|
|
// Over 3s it seeks to zero
|
|
|
|
if self.position() < 3000 {
|
|
|
|
let current_index = self.state.get_playing_track_index();
|
|
|
|
|
|
|
|
let new_index = if current_index == 0 {
|
|
|
|
self.state.get_track().len() as u32 - 1
|
|
|
|
} else {
|
|
|
|
current_index - 1
|
|
|
|
};
|
|
|
|
|
|
|
|
self.state.set_playing_track_index(new_index);
|
|
|
|
self.state.set_position_ms(0);
|
|
|
|
self.state.set_position_measured_at(now_ms() as u64);
|
|
|
|
|
|
|
|
self.load_track(true);
|
|
|
|
} else {
|
|
|
|
self.state.set_position_ms(0);
|
|
|
|
self.state.set_position_measured_at(now_ms() as u64);
|
|
|
|
self.player.seek(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_volume_up(&mut self) {
|
2018-01-25 22:37:28 +00:00
|
|
|
let mut volume: u32 = self.device.get_volume() as u32 + 4096;
|
2017-02-23 11:05:32 +00:00
|
|
|
if volume > 0xFFFF {
|
|
|
|
volume = 0xFFFF;
|
|
|
|
}
|
|
|
|
self.device.set_volume(volume);
|
2018-01-29 22:37:30 +00:00
|
|
|
self.mixer.set_volume(volume_to_mixer(volume as u16));
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn handle_volume_down(&mut self) {
|
2018-01-25 22:37:28 +00:00
|
|
|
let mut volume: i32 = self.device.get_volume() as i32 - 4096;
|
2017-02-23 11:05:32 +00:00
|
|
|
if volume < 0 {
|
|
|
|
volume = 0;
|
|
|
|
}
|
|
|
|
self.device.set_volume(volume as u32);
|
2018-01-29 22:37:30 +00:00
|
|
|
self.mixer.set_volume(volume_to_mixer(volume as u16));
|
2017-02-23 11:05:32 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
fn handle_end_of_track(&mut self) {
|
2018-02-09 01:05:50 +00:00
|
|
|
self.handle_next();
|
2017-01-29 14:11:20 +00:00
|
|
|
self.notify(None);
|
2016-01-20 13:51:35 +00:00
|
|
|
}
|
2016-01-20 15:47:05 +00:00
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
fn position(&mut self) -> u32 {
|
|
|
|
let diff = now_ms() as u64 - self.state.get_position_measured_at();
|
|
|
|
self.state.get_position_ms() + diff as u32
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 16:25:09 +00:00
|
|
|
fn update_tracks(&mut self, frame: &protocol::spirc::Frame) {
|
2017-01-29 14:11:20 +00:00
|
|
|
let index = frame.get_state().get_playing_track_index();
|
|
|
|
let tracks = frame.get_state().get_track();
|
|
|
|
|
|
|
|
self.state.set_playing_track_index(index);
|
2017-01-29 16:25:09 +00:00
|
|
|
self.state.set_track(tracks.into_iter().cloned().collect());
|
2016-01-20 15:47:05 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
fn load_track(&mut self, play: bool) {
|
|
|
|
let index = self.state.get_playing_track_index();
|
|
|
|
let track = {
|
|
|
|
let gid = self.state.get_track()[index as usize].get_gid();
|
|
|
|
SpotifyId::from_raw(gid)
|
|
|
|
};
|
|
|
|
let position = self.state.get_position_ms();
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
let end_of_track = self.player.load(track, play, position);
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2017-01-29 16:25:09 +00:00
|
|
|
if play {
|
|
|
|
self.state.set_status(PlayStatus::kPlayStatusPlay);
|
|
|
|
} else {
|
|
|
|
self.state.set_status(PlayStatus::kPlayStatusPause);
|
|
|
|
}
|
|
|
|
|
2018-01-21 19:29:31 +00:00
|
|
|
self.end_of_track = Box::new(end_of_track);
|
2017-01-29 14:11:20 +00:00
|
|
|
}
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
fn hello(&mut self) {
|
|
|
|
CommandSender::new(self, MessageType::kMessageTypeHello).send();
|
|
|
|
}
|
2015-07-08 19:50:44 +00:00
|
|
|
|
2017-01-29 14:11:20 +00:00
|
|
|
fn notify(&mut self, recipient: Option<&str>) {
|
|
|
|
let mut cs = CommandSender::new(self, MessageType::kMessageTypeNotify);
|
|
|
|
if let Some(s) = recipient {
|
|
|
|
cs = cs.recipient(&s);
|
|
|
|
}
|
|
|
|
cs.send();
|
|
|
|
}
|
2015-07-08 19:50:44 +00:00
|
|
|
}
|
2016-02-16 21:25:55 +00:00
|
|
|
|
2017-02-22 04:17:04 +00:00
|
|
|
impl Drop for SpircTask {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
debug!("drop Spirc[{}]", self.session.session_id());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-17 14:24:40 +00:00
|
|
|
struct CommandSender<'a> {
|
2017-01-20 12:56:42 +00:00
|
|
|
spirc: &'a mut SpircTask,
|
2017-02-21 21:49:45 +00:00
|
|
|
frame: protocol::spirc::Frame,
|
2016-02-17 14:24:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> CommandSender<'a> {
|
2017-01-20 12:56:42 +00:00
|
|
|
fn new(spirc: &'a mut SpircTask, cmd: MessageType) -> CommandSender {
|
2017-02-21 21:49:45 +00:00
|
|
|
let frame = protobuf_init!(protocol::spirc::Frame::new(), {
|
|
|
|
version: 1,
|
|
|
|
protocol_version: "2.0.0",
|
|
|
|
ident: spirc.ident.clone(),
|
2017-02-21 22:46:19 +00:00
|
|
|
seq_nr: spirc.sequence.get(),
|
2017-02-21 21:49:45 +00:00
|
|
|
typ: cmd,
|
|
|
|
|
2017-02-21 22:46:19 +00:00
|
|
|
device_state: spirc.device.clone(),
|
|
|
|
state_update_id: now_ms(),
|
2017-02-21 21:49:45 +00:00
|
|
|
});
|
|
|
|
|
2016-02-17 14:24:40 +00:00
|
|
|
CommandSender {
|
2017-01-20 12:56:42 +00:00
|
|
|
spirc: spirc,
|
2017-02-21 21:49:45 +00:00
|
|
|
frame: frame,
|
2016-02-16 21:25:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-02-21 21:49:45 +00:00
|
|
|
fn recipient(mut self, recipient: &'a str) -> CommandSender {
|
|
|
|
self.frame.mut_recipient().push(recipient.to_owned());
|
2016-02-17 14:24:40 +00:00
|
|
|
self
|
|
|
|
}
|
|
|
|
|
2017-02-21 22:46:19 +00:00
|
|
|
#[allow(dead_code)]
|
2017-02-21 21:49:45 +00:00
|
|
|
fn state(mut self, state: protocol::spirc::State) -> CommandSender<'a> {
|
|
|
|
self.frame.set_state(state);
|
2016-02-17 14:24:40 +00:00
|
|
|
self
|
|
|
|
}
|
2017-01-29 14:11:20 +00:00
|
|
|
|
2017-02-21 21:49:45 +00:00
|
|
|
fn send(mut self) {
|
2017-02-21 22:46:19 +00:00
|
|
|
if !self.frame.has_state() && self.spirc.device.get_is_active() {
|
|
|
|
self.frame.set_state(self.spirc.state.clone());
|
2016-02-17 14:24:40 +00:00
|
|
|
}
|
|
|
|
|
2017-02-21 22:46:19 +00:00
|
|
|
let send = self.spirc.sender.start_send(self.frame).unwrap();
|
|
|
|
assert!(send.is_ready());
|
2016-02-17 14:24:40 +00:00
|
|
|
}
|
2016-02-16 21:25:55 +00:00
|
|
|
}
|