feat: 支持设置音量。

This commit is contained in:
Ivan Li 2023-05-07 18:18:34 +08:00
parent 9109518822
commit 2c5ac11579
8 changed files with 238 additions and 9 deletions

123
src-tauri/Cargo.lock generated
View File

@ -109,6 +109,26 @@ version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a" checksum = "a4a4ddaa51a5bc52a6948f74c06d20aaaddb71924eab79b8c97a8c556e942d6a"
[[package]]
name = "bindgen"
version = "0.64.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"lazy_static",
"lazycell",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"syn 1.0.109",
]
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "1.3.2" version = "1.3.2"
@ -231,6 +251,15 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c"
[[package]]
name = "cexpr"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
dependencies = [
"nom 7.1.3",
]
[[package]] [[package]]
name = "cfb" name = "cfb"
version = "0.7.3" version = "0.7.3"
@ -280,6 +309,17 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "clang-sys"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c688fc74432808e3eb684cae8830a86be1d66a2bd58e1f248ed0960a590baf6f"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]] [[package]]
name = "cmake" name = "cmake"
version = "0.1.50" version = "0.1.50"
@ -414,6 +454,26 @@ dependencies = [
"libc", "libc",
] ]
[[package]]
name = "coreaudio-rs"
version = "0.11.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cb17e2d1795b1996419648915df94bc7103c28f7b48062d7acf4652fc371b2ff"
dependencies = [
"bitflags",
"core-foundation-sys 0.6.2",
"coreaudio-sys",
]
[[package]]
name = "coreaudio-sys"
version = "0.2.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f034b2258e6c4ade2f73bf87b21047567fb913ee9550837c2316d139b0262b24"
dependencies = [
"bindgen",
]
[[package]] [[package]]
name = "cpufeatures" name = "cpufeatures"
version = "0.2.7" version = "0.2.7"
@ -734,7 +794,7 @@ version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ce75530893d834dcfe3bb67ce0e7dec489484e7cb4423ca31618af4bab24fe" checksum = "24ce75530893d834dcfe3bb67ce0e7dec489484e7cb4423ca31618af4bab24fe"
dependencies = [ dependencies = [
"nom", "nom 3.2.1",
] ]
[[package]] [[package]]
@ -1674,10 +1734,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]] [[package]]
name = "libc" name = "lazycell"
version = "0.2.142" version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.143"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edc207893e85c5d6be840e969b496b53d94cec8be2d501b214f50daa97fa8024"
[[package]]
name = "libloading"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
dependencies = [
"cfg-if",
"winapi",
]
[[package]] [[package]]
name = "libudev-sys" name = "libudev-sys"
@ -1831,7 +1907,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8eb961d01a3bb07969cfa276be2ab88c31d0fefa77a872696832732d6e9ec094" checksum = "8eb961d01a3bb07969cfa276be2ab88c31d0fefa77a872696832732d6e9ec094"
dependencies = [ dependencies = [
"mccs", "mccs",
"nom", "nom 3.2.1",
] ]
[[package]] [[package]]
@ -1841,7 +1917,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cdaa8fe19a1a1918becc1b8cbbbdc1058bc71411dff4de0a6ec6b5269f49d38" checksum = "3cdaa8fe19a1a1918becc1b8cbbbdc1058bc71411dff4de0a6ec6b5269f49d38"
dependencies = [ dependencies = [
"mccs", "mccs",
"nom", "nom 3.2.1",
"serde", "serde",
"serde_derive", "serde_derive",
"serde_yaml", "serde_yaml",
@ -1884,6 +1960,12 @@ dependencies = [
"autocfg", "autocfg",
] ]
[[package]]
name = "minimal-lexical"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
[[package]] [[package]]
name = "miniz_oxide" name = "miniz_oxide"
version = "0.7.1" version = "0.7.1"
@ -1955,6 +2037,16 @@ dependencies = [
"memchr 1.0.2", "memchr 1.0.2",
] ]
[[package]]
name = "nom"
version = "7.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
dependencies = [
"memchr 2.5.0",
"minimal-lexical",
]
[[package]] [[package]]
name = "nu-ansi-term" name = "nu-ansi-term"
version = "0.46.0" version = "0.46.0"
@ -2202,6 +2294,12 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]] [[package]]
name = "percent-encoding" name = "percent-encoding"
version = "2.2.0" version = "2.2.0"
@ -2623,6 +2721,12 @@ dependencies = [
"uninitialized", "uninitialized",
] ]
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]] [[package]]
name = "rustc_version" name = "rustc_version"
version = "0.4.0" version = "0.4.0"
@ -2863,6 +2967,12 @@ dependencies = [
"lazy_static", "lazy_static",
] ]
[[package]]
name = "shlex"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3"
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.1" version = "1.4.1"
@ -3330,6 +3440,7 @@ dependencies = [
"color_space", "color_space",
"core-foundation", "core-foundation",
"core-graphics", "core-graphics",
"coreaudio-rs",
"ddc-hi", "ddc-hi",
"display-info", "display-info",
"env_logger", "env_logger",

View File

@ -36,6 +36,7 @@ tokio-stream = "0.1.14"
mdns-sd = "0.7.2" mdns-sd = "0.7.2"
futures = "0.3.28" futures = "0.3.28"
ddc-hi = "0.4.1" ddc-hi = "0.4.1"
coreaudio-rs = "0.11.2"
[features] [features]
# this feature is used for production builds or when `devPath` points to the filesystem # this feature is used for production builds or when `devPath` points to the filesystem

View File

@ -5,8 +5,9 @@ mod ambient_light;
mod display; mod display;
mod led_color; mod led_color;
mod rpc; mod rpc;
pub mod screenshot; mod screenshot;
mod screenshot_manager; mod screenshot_manager;
mod volume;
use ambient_light::{Border, ColorCalibration, LedStripConfig, LedStripConfigGroup}; use ambient_light::{Border, ColorCalibration, LedStripConfig, LedStripConfigGroup};
use display::{DisplayManager, DisplayState}; use display::{DisplayManager, DisplayState};
@ -18,6 +19,7 @@ use screenshot_manager::ScreenshotManager;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json::to_string; use serde_json::to_string;
use tauri::{http::ResponseBuilder, regex, Manager}; use tauri::{http::ResponseBuilder, regex, Manager};
use volume::VolumeManager;
#[derive(Serialize, Deserialize)] #[derive(Serialize, Deserialize)]
#[serde(remote = "DisplayInfo")] #[serde(remote = "DisplayInfo")]
@ -223,6 +225,8 @@ async fn main() {
let _mqtt = MqttRpc::global().await; let _mqtt = MqttRpc::global().await;
let _volume = VolumeManager::global().await;
tauri::Builder::default() tauri::Builder::default()
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
greet, greet,

View File

@ -45,6 +45,8 @@ impl Board {
let display_setting_request_sender = board_message_channels let display_setting_request_sender = board_message_channels
.display_setting_request_sender .display_setting_request_sender
.clone(); .clone();
let volume_setting_request_sender =
board_message_channels.volume_setting_request_sender.clone();
loop { loop {
match socket.try_recv(&mut buf) { match socket.try_recv(&mut buf) {
@ -60,6 +62,8 @@ impl Board {
if let Err(err) = result { if let Err(err) = result {
error!("send display setting request to channel failed: {:?}", err); error!("send display setting request to channel failed: {:?}", err);
} }
} else if buf[0] == 4 {
let result = volume_setting_request_sender.send(buf[1] as f32 / 100.0);
} }
} }
Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => { Err(ref e) if e.kind() == io::ErrorKind::WouldBlock => {

View File

@ -6,6 +6,7 @@ use super::DisplaySettingRequest;
pub struct BoardMessageChannels { pub struct BoardMessageChannels {
pub display_setting_request_sender: Arc<broadcast::Sender<DisplaySettingRequest>>, pub display_setting_request_sender: Arc<broadcast::Sender<DisplaySettingRequest>>,
pub volume_setting_request_sender: Arc<broadcast::Sender<f32>>,
} }
impl BoardMessageChannels { impl BoardMessageChannels {
@ -19,8 +20,12 @@ impl BoardMessageChannels {
let (display_setting_request_sender, _) = broadcast::channel(16); let (display_setting_request_sender, _) = broadcast::channel(16);
let display_setting_request_sender = Arc::new(display_setting_request_sender); let display_setting_request_sender = Arc::new(display_setting_request_sender);
let (volume_setting_request_sender, _) = broadcast::channel(16);
let volume_setting_request_sender = Arc::new(volume_setting_request_sender);
Self { Self {
display_setting_request_sender, display_setting_request_sender,
volume_setting_request_sender,
} }
} }
} }

View File

@ -3,9 +3,9 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
use futures::future::join_all; use futures::future::join_all;
use mdns_sd::{ServiceDaemon, ServiceEvent}; use mdns_sd::{ServiceDaemon, ServiceEvent};
use paris::{error, info, warn}; use paris::{error, info, warn};
use tokio::sync::{watch, OnceCell, RwLock, broadcast}; use tokio::sync::{watch, OnceCell, RwLock};
use super::{Board, BoardInfo, DisplaySettingRequest}; use super::{Board, BoardInfo};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub struct UdpRpc { pub struct UdpRpc {

View File

@ -0,0 +1,101 @@
use std::{
mem,
sync::{Arc, RwLock},
};
use coreaudio::{
audio_unit::macos_helpers::get_default_device_id,
sys::{
kAudioHardwareServiceDeviceProperty_VirtualMasterVolume, kAudioObjectPropertyScopeOutput,
AudioObjectHasProperty, AudioObjectPropertyAddress, AudioObjectSetPropertyData,
},
};
use paris::error;
use tokio::sync::OnceCell;
use crate::rpc::BoardMessageChannels;
pub struct VolumeManager {
current_volume: Arc<RwLock<f32>>,
handler: Option<tokio::task::JoinHandle<()>>,
}
impl VolumeManager {
pub async fn global() -> &'static Self {
static VOLUME_MANAGER: OnceCell<VolumeManager> = OnceCell::const_new();
VOLUME_MANAGER
.get_or_init(|| async { Self::create() })
.await
}
pub fn create() -> Self {
let mut instance = Self {
current_volume: Arc::new(RwLock::new(0.0)),
handler: None,
};
instance.subscribe_volume_setting_request();
instance
}
fn subscribe_volume_setting_request(&mut self) {
let handler = tokio::spawn(async {
let channels = BoardMessageChannels::global().await;
let mut request_rx = channels.volume_setting_request_sender.subscribe();
while let Ok(volume) = request_rx.recv().await {
if let Err(err) = Self::set_volume(volume) {
error!("failed to set volume: {}", err);
}
}
});
self.handler = Some(handler);
}
fn set_volume(volume: f32) -> anyhow::Result<()> {
log::debug!("set volume: {}", volume);
let device_id = get_default_device_id(false);
if device_id.is_none() {
anyhow::bail!("default audio output device is not found.");
}
let device_id = device_id.unwrap();
let address = AudioObjectPropertyAddress {
mSelector: kAudioHardwareServiceDeviceProperty_VirtualMasterVolume,
mScope: kAudioObjectPropertyScopeOutput,
mElement: 0,
};
log::debug!("device id: {}", device_id);
log::debug!("address: {:?}", address);
if 0 == unsafe { AudioObjectHasProperty(device_id, &address) } {
anyhow::bail!("Can not get audio property");
}
let size = mem::size_of::<f32>() as u32;
let result = unsafe {
AudioObjectSetPropertyData(
device_id,
&address,
0,
std::ptr::null(),
size,
&volume as *const f32 as *const std::ffi::c_void,
)
};
if result != 0 {
anyhow::bail!("Can not set audio property");
}
Ok(())
}
}

View File

@ -0,0 +1,3 @@
mod manager;
pub use manager::*;