From 8f3f028ac94f22af106c47fb6f80a5506654e557 Mon Sep 17 00:00:00 2001 From: Aurora Date: Fri, 5 Aug 2022 17:02:26 +0200 Subject: [PATCH] Add main volume + check battery level in bg task --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/gui.rs | 105 +++++++++++++++++++++++++----------------------- src/settings.rs | 9 +++++ 4 files changed, 66 insertions(+), 52 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 540ee70..0c0ada9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1643,7 +1643,7 @@ checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" [[package]] name = "music-vibes" -version = "0.1.2" +version = "0.1.3" dependencies = [ "audio-capture", "buttplug", diff --git a/Cargo.toml b/Cargo.toml index 51fa3e2..58b6486 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "music-vibes" -version = "0.1.2" +version = "0.1.3" edition = "2021" license = "MIT" diff --git a/src/gui.rs b/src/gui.rs index e0aebf0..5d1ace2 100644 --- a/src/gui.rs +++ b/src/gui.rs @@ -2,7 +2,7 @@ use std::{ collections::{HashMap, VecDeque}, sync::Arc, thread::JoinHandle, - time::{Duration, Instant}, + time::Duration, }; use audio_capture::win::capture::AudioCapture; @@ -43,7 +43,6 @@ struct GuiApp { devices: HashMap, current_sound_power: SharedF32, _capture_thread: JoinHandle<()>, - // volatile info is_scanning: bool, show_settings: bool, // persistent settings @@ -52,35 +51,57 @@ struct GuiApp { struct DeviceProps { is_enabled: bool, - last_battery_readout: ReadoutState, + battery_state: BatteryState, multiplier: f32, min: f32, max: f32, } -enum ReadoutState { - Uninit, - Init(BatteryReadout), - Error, -} +// TEMP: if readout returned an error, SharedF32 will be set to NaN +struct BatteryState(SharedF32, tokio::task::JoinHandle<()>); -struct BatteryReadout(f64, Instant); +impl BatteryState { + pub fn new(runtime: &Runtime, device: Arc) -> Self { + let shared_level = SharedF32::new(0.0); + let task = { + let shared_level = shared_level.clone(); + runtime.spawn(battery_check_bg_task(device, shared_level)) + }; + Self(shared_level, task) + } -impl BatteryReadout { - pub fn now(bat: f64) -> Self { - Self(bat, Instant::now()) + pub fn get_level(&self) -> Option { + let value = self.0.load(); + if value.is_nan() { + None + } else { + Some(value) + } } +} - pub fn is_older_than(&self, dur: Duration) -> bool { - self.1.elapsed() > dur +async fn battery_check_bg_task( + device: Arc, + shared_level: SharedF32, +) { + let mut interval = tokio::time::interval(Duration::from_secs(5)); + loop { + interval.tick().await; + match device.battery_level().await { + Ok(level) => shared_level.store(level as f32), + Err(_) => { + shared_level.store(f32::NAN); + break; + } + } } } -impl Default for DeviceProps { - fn default() -> Self { +impl DeviceProps { + fn new(runtime: &Runtime, device: Arc) -> Self { Self { is_enabled: false, - last_battery_readout: ReadoutState::Uninit, + battery_state: BatteryState::new(runtime, device), multiplier: 1.0, min: 0.0, max: 1.0, @@ -222,7 +243,9 @@ impl eframe::App for GuiApp { } }); ui.separator(); - let sound_power = self.current_sound_power.load(); + let main_mul = self.settings.main_volume.powi(2); + let sound_power = + (self.current_sound_power.load() * main_mul).clamp(0.0, 1.0); ui.horizontal(|ui| { ui.label(format!( "Current volume: {:.2}%", @@ -232,6 +255,14 @@ impl eframe::App for GuiApp { }); ui.horizontal(|ui| { + ui.label("Main volume: "); + let mut volume_as_percent = self.settings.main_volume * 100.0; + ui.add( + Slider::new(&mut volume_as_percent, 0.0..=500.0) + .suffix("%"), + ); + self.settings.main_volume = volume_as_percent / 100.0; + let mut low_pass_freq = self.settings.low_pass_freq.load(); ui.label("Low pass freq.: "); ui.add( @@ -245,7 +276,10 @@ impl eframe::App for GuiApp { ui.heading("Devices"); for device in self.client.devices() { - let props = self.devices.entry(device.index()).or_default(); + let props = + self.devices.entry(device.index()).or_insert_with(|| { + DeviceProps::new(&self.runtime, device.clone()) + }); device_widget(ui, device, props, sound_power, &self.runtime); } }); @@ -274,22 +308,8 @@ fn device_widget( ui.label(&device.name); } - let state = &mut props.last_battery_readout; - let bat: Option = match state { - ReadoutState::Uninit => { - update_battery_readout(runtime, &device, state) - } - ReadoutState::Init(readout) => { - if readout.is_older_than(Duration::from_secs(5)) { - update_battery_readout(runtime, &device, state) - } else { - Some(readout.0) - } - } - ReadoutState::Error => None, - }; - if let Some(bat) = bat { - ui.label(format!("Battery: {}", bat)); + if let Some(bat) = props.battery_state.get_level() { + ui.label(format!("Battery: {}%", bat * 100.0)); } let (speed, cutoff) = props.calculate_visual_output(sound_power); @@ -324,18 +344,3 @@ fn device_widget( } }); } - -// TEMP: currently, getting error from battery_level will mark it -// as unusable, even if error was spurious -fn update_battery_readout( - runtime: &Runtime, - device: &Arc, - state: &mut ReadoutState, -) -> Option { - let ret; - (*state, ret) = match runtime.block_on(device.battery_level()) { - Ok(bat) => (ReadoutState::Init(BatteryReadout::now(bat)), Some(bat)), - Err(_) => (ReadoutState::Error, None), - }; - ret -} diff --git a/src/settings.rs b/src/settings.rs index d513614..0f039a6 100644 --- a/src/settings.rs +++ b/src/settings.rs @@ -2,7 +2,9 @@ use eframe::{get_value, set_value, Storage}; use crate::util::SharedF32; +// TODO: Add derive macro pub struct Settings { + pub main_volume: f32, pub low_pass_freq: SharedF32, pub use_dark_mode: bool, } @@ -10,6 +12,7 @@ pub struct Settings { impl Default for Settings { fn default() -> Self { Self { + main_volume: defaults::MAIN_VOLUME, low_pass_freq: SharedF32::new(defaults::LOW_PASS_FREQ), use_dark_mode: defaults::DARK_MODE, } @@ -17,27 +20,33 @@ impl Default for Settings { } mod names { + pub const MAIN_VOLUME: &str = "main_volume"; pub const LOW_PASS_FREQ: &str = "low_pass_freq"; pub const DARK_MODE: &str = "dark_mode"; } mod defaults { + pub const MAIN_VOLUME: f32 = 1.0; pub const LOW_PASS_FREQ: f32 = 20_000.0; pub const DARK_MODE: bool = true; } impl Settings { pub fn load(storage: &dyn Storage) -> Self { + let main_volume = get_value(storage, names::MAIN_VOLUME) + .unwrap_or(defaults::MAIN_VOLUME); let low_pass_freq = get_value(storage, names::LOW_PASS_FREQ) .unwrap_or(defaults::LOW_PASS_FREQ); let use_dark_mode = get_value(storage, names::DARK_MODE).unwrap_or(defaults::DARK_MODE); Self { + main_volume, low_pass_freq: SharedF32::new(low_pass_freq), use_dark_mode, } } pub fn save(&self, storage: &mut dyn Storage) { + set_value(storage, names::MAIN_VOLUME, &self.main_volume); set_value(storage, names::LOW_PASS_FREQ, &self.low_pass_freq.load()); set_value(storage, names::DARK_MODE, &self.use_dark_mode); }