mirror of
https://github.com/librespot-org/librespot.git
synced 2024-12-18 17:11:53 +00:00
Cache volume across restarts (#220)
* create Volume struct for use with Cache * add "volume" file to Cache * load cached volume on start, intial overrides cached overrides default * amend volume_to_mixer function to cache the volume on every change * pass cache to Spirc and SpircTask so volume_to_mixer has access * rustfmt changes * revert volume_to_mixer function and Spirc/SpircTask cache variable * Volume implements Copy, pass by value instead of reference * clamp volume to 100 if cached value exceeds limit * convert Volume to u16 internally, use float and round to convert hex->dec * convert initial_volume and ConnectConfig.volume to u16 as well * add cache_volume function to SpircTask * remove conversion to/from percentage on cached volume * consolidate device.set_volume, mixer.set_volume, and caching * streamline intial volume logic
This commit is contained in:
parent
21f1ccfb5a
commit
d40c0f50db
8 changed files with 84 additions and 26 deletions
|
@ -9,6 +9,7 @@ use core::session::Session;
|
|||
use core::spotify_id::SpotifyId;
|
||||
use core::util::SeqGenerator;
|
||||
use core::version;
|
||||
use core::volume::Volume;
|
||||
|
||||
use protocol;
|
||||
use protocol::spirc::{DeviceState, Frame, MessageType, PlayStatus, State};
|
||||
|
@ -74,13 +75,13 @@ fn initial_state() -> State {
|
|||
frame
|
||||
}
|
||||
|
||||
fn initial_device_state(config: ConnectConfig, volume: u16) -> DeviceState {
|
||||
fn initial_device_state(config: ConnectConfig) -> DeviceState {
|
||||
{
|
||||
let mut msg = DeviceState::new();
|
||||
msg.set_sw_version(version::version_string());
|
||||
msg.set_is_active(false);
|
||||
msg.set_can_play(true);
|
||||
msg.set_volume(volume as u32);
|
||||
msg.set_volume(0);
|
||||
msg.set_name(config.name);
|
||||
{
|
||||
let repeated = msg.mut_capabilities();
|
||||
|
@ -233,11 +234,10 @@ impl Spirc {
|
|||
|
||||
let (cmd_tx, cmd_rx) = mpsc::unbounded();
|
||||
|
||||
let volume = config.volume as u16;
|
||||
let volume = config.volume;
|
||||
let linear_volume = config.linear_volume;
|
||||
|
||||
let device = initial_device_state(config, volume);
|
||||
mixer.set_volume(volume_to_mixer(volume as u16, linear_volume));
|
||||
let device = initial_device_state(config);
|
||||
|
||||
let mut task = SpircTask {
|
||||
player: player,
|
||||
|
@ -260,6 +260,8 @@ impl Spirc {
|
|||
session: session.clone(),
|
||||
};
|
||||
|
||||
task.set_volume(volume);
|
||||
|
||||
let spirc = Spirc { commands: cmd_tx };
|
||||
|
||||
task.hello();
|
||||
|
@ -532,9 +534,7 @@ impl SpircTask {
|
|||
}
|
||||
|
||||
MessageType::kMessageTypeVolume => {
|
||||
self.device.set_volume(frame.get_volume());
|
||||
self.mixer
|
||||
.set_volume(volume_to_mixer(frame.get_volume() as u16, self.linear_volume));
|
||||
self.set_volume(frame.get_volume() as u16);
|
||||
self.notify(None);
|
||||
}
|
||||
|
||||
|
@ -657,9 +657,7 @@ impl SpircTask {
|
|||
if volume > 0xFFFF {
|
||||
volume = 0xFFFF;
|
||||
}
|
||||
self.device.set_volume(volume);
|
||||
self.mixer
|
||||
.set_volume(volume_to_mixer(volume as u16, self.linear_volume));
|
||||
self.set_volume(volume as u16);
|
||||
}
|
||||
|
||||
fn handle_volume_down(&mut self) {
|
||||
|
@ -667,9 +665,7 @@ impl SpircTask {
|
|||
if volume < 0 {
|
||||
volume = 0;
|
||||
}
|
||||
self.device.set_volume(volume as u32);
|
||||
self.mixer
|
||||
.set_volume(volume_to_mixer(volume as u16, self.linear_volume));
|
||||
self.set_volume(volume as u16);
|
||||
}
|
||||
|
||||
fn handle_end_of_track(&mut self) {
|
||||
|
@ -724,6 +720,14 @@ impl SpircTask {
|
|||
}
|
||||
cs.send();
|
||||
}
|
||||
|
||||
fn set_volume(&mut self, volume: u16) {
|
||||
self.device.set_volume(volume as u32);
|
||||
self.mixer.set_volume(volume_to_mixer(volume, self.linear_volume));
|
||||
if let Some(cache) = self.session.cache() {
|
||||
cache.save_volume(Volume { volume })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SpircTask {
|
||||
|
|
|
@ -7,6 +7,7 @@ use std::path::PathBuf;
|
|||
|
||||
use authentication::Credentials;
|
||||
use spotify_id::FileId;
|
||||
use volume::Volume;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Cache {
|
||||
|
@ -52,6 +53,23 @@ impl Cache {
|
|||
}
|
||||
}
|
||||
|
||||
// cache volume to root/volume
|
||||
impl Cache {
|
||||
fn volume_path(&self) -> PathBuf {
|
||||
self.root.join("volume")
|
||||
}
|
||||
|
||||
pub fn volume(&self) -> Option<u16> {
|
||||
let path = self.volume_path();
|
||||
Volume::from_file(path)
|
||||
}
|
||||
|
||||
pub fn save_volume(&self, volume: Volume) {
|
||||
let path = self.volume_path();
|
||||
volume.save_to_file(&path);
|
||||
}
|
||||
}
|
||||
|
||||
impl Cache {
|
||||
fn file_path(&self, file: FileId) -> PathBuf {
|
||||
let name = file.to_base16();
|
||||
|
|
|
@ -81,6 +81,6 @@ impl Default for DeviceType {
|
|||
pub struct ConnectConfig {
|
||||
pub name: String,
|
||||
pub device_type: DeviceType,
|
||||
pub volume: i32,
|
||||
pub volume: u16,
|
||||
pub linear_volume: bool,
|
||||
}
|
||||
|
|
|
@ -52,3 +52,4 @@ pub mod session;
|
|||
pub mod spotify_id;
|
||||
pub mod util;
|
||||
pub mod version;
|
||||
pub mod volume;
|
||||
|
|
31
core/src/volume.rs
Normal file
31
core/src/volume.rs
Normal file
|
@ -0,0 +1,31 @@
|
|||
use std::fs::File;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::Path;
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct Volume {
|
||||
pub volume: u16,
|
||||
}
|
||||
|
||||
impl Volume {
|
||||
// read volume from file
|
||||
fn from_reader<R: Read>(mut reader: R) -> u16 {
|
||||
let mut contents = String::new();
|
||||
reader.read_to_string(&mut contents).unwrap();
|
||||
contents.trim().parse::<u16>().unwrap()
|
||||
}
|
||||
|
||||
pub(crate) fn from_file<P: AsRef<Path>>(path: P) -> Option<u16> {
|
||||
File::open(path).ok().map(Volume::from_reader)
|
||||
}
|
||||
|
||||
// write volume to file
|
||||
fn save_to_writer<W: Write>(&self, writer: &mut W) {
|
||||
writer.write_all(self.volume.to_string().as_bytes()).unwrap();
|
||||
}
|
||||
|
||||
pub(crate) fn save_to_file<P: AsRef<Path>>(&self, path: P) {
|
||||
let mut file = File::create(path).unwrap();
|
||||
self.save_to_writer(&mut file)
|
||||
}
|
||||
}
|
|
@ -36,7 +36,7 @@ fn main() {
|
|||
let session = core.run(Session::connect(session_config, credentials, None, handle))
|
||||
.unwrap();
|
||||
|
||||
let (player, _)= Player::new(player_config, session.clone(), None, move || (backend)(None));
|
||||
let (player, _) = Player::new(player_config, session.clone(), None, move || (backend)(None));
|
||||
|
||||
println!("Playing...");
|
||||
core.run(player.load(track, true, 0)).unwrap();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
use super::{Open, Sink};
|
||||
use jack::prelude::{client_options, AsyncClient, AudioOutPort, AudioOutSpec, Client, JackControl, Port,
|
||||
ProcessHandler, ProcessScope};
|
||||
use jack::prelude::{
|
||||
client_options, AsyncClient, AudioOutPort, AudioOutSpec, Client, JackControl, Port, ProcessHandler,
|
||||
ProcessScope,
|
||||
};
|
||||
use std::io;
|
||||
use std::sync::mpsc::{sync_channel, Receiver, SyncSender};
|
||||
|
||||
|
|
18
src/main.rs
18
src/main.rs
|
@ -204,15 +204,22 @@ fn setup(args: &[String]) -> Setup {
|
|||
let mixer_name = matches.opt_str("mixer");
|
||||
let mixer = mixer::find(mixer_name.as_ref()).expect("Invalid mixer");
|
||||
|
||||
let use_audio_cache = !matches.opt_present("disable-audio-cache");
|
||||
|
||||
let cache = matches
|
||||
.opt_str("c")
|
||||
.map(|cache_location| Cache::new(PathBuf::from(cache_location), use_audio_cache));
|
||||
|
||||
let initial_volume = matches
|
||||
.opt_str("initial-volume")
|
||||
.map(|volume| {
|
||||
let volume = volume.parse::<i32>().unwrap();
|
||||
if volume < 0 || volume > 100 {
|
||||
let volume = volume.parse::<u16>().unwrap();
|
||||
if volume > 100 {
|
||||
panic!("Initial volume must be in the range 0-100");
|
||||
}
|
||||
volume * 0xFFFF / 100
|
||||
(volume as i32 * 0xFFFF / 100) as u16
|
||||
})
|
||||
.or_else(|| cache.as_ref().and_then(Cache::volume))
|
||||
.unwrap_or(0x8000);
|
||||
|
||||
let zeroconf_port = matches
|
||||
|
@ -221,11 +228,6 @@ fn setup(args: &[String]) -> Setup {
|
|||
.unwrap_or(0);
|
||||
|
||||
let name = matches.opt_str("name").unwrap();
|
||||
let use_audio_cache = !matches.opt_present("disable-audio-cache");
|
||||
|
||||
let cache = matches
|
||||
.opt_str("c")
|
||||
.map(|cache_location| Cache::new(PathBuf::from(cache_location), use_audio_cache));
|
||||
|
||||
let credentials = {
|
||||
let cached_credentials = cache.as_ref().and_then(Cache::credentials);
|
||||
|
|
Loading…
Reference in a new issue