feat: 支持设置音量。
This commit is contained in:
@ -5,8 +5,9 @@ mod ambient_light;
|
||||
mod display;
|
||||
mod led_color;
|
||||
mod rpc;
|
||||
pub mod screenshot;
|
||||
mod screenshot;
|
||||
mod screenshot_manager;
|
||||
mod volume;
|
||||
|
||||
use ambient_light::{Border, ColorCalibration, LedStripConfig, LedStripConfigGroup};
|
||||
use display::{DisplayManager, DisplayState};
|
||||
@ -18,6 +19,7 @@ use screenshot_manager::ScreenshotManager;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::to_string;
|
||||
use tauri::{http::ResponseBuilder, regex, Manager};
|
||||
use volume::VolumeManager;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[serde(remote = "DisplayInfo")]
|
||||
@ -223,6 +225,8 @@ async fn main() {
|
||||
|
||||
let _mqtt = MqttRpc::global().await;
|
||||
|
||||
let _volume = VolumeManager::global().await;
|
||||
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
greet,
|
||||
|
@ -45,6 +45,8 @@ impl Board {
|
||||
let display_setting_request_sender = board_message_channels
|
||||
.display_setting_request_sender
|
||||
.clone();
|
||||
let volume_setting_request_sender =
|
||||
board_message_channels.volume_setting_request_sender.clone();
|
||||
|
||||
loop {
|
||||
match socket.try_recv(&mut buf) {
|
||||
@ -60,6 +62,8 @@ impl Board {
|
||||
if let Err(err) = result {
|
||||
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 => {
|
||||
|
@ -6,6 +6,7 @@ use super::DisplaySettingRequest;
|
||||
|
||||
pub struct BoardMessageChannels {
|
||||
pub display_setting_request_sender: Arc<broadcast::Sender<DisplaySettingRequest>>,
|
||||
pub volume_setting_request_sender: Arc<broadcast::Sender<f32>>,
|
||||
}
|
||||
|
||||
impl BoardMessageChannels {
|
||||
@ -19,8 +20,12 @@ impl BoardMessageChannels {
|
||||
let (display_setting_request_sender, _) = broadcast::channel(16);
|
||||
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 {
|
||||
display_setting_request_sender,
|
||||
volume_setting_request_sender,
|
||||
}
|
||||
}
|
||||
}
|
@ -3,9 +3,9 @@ use std::{collections::HashMap, sync::Arc, time::Duration};
|
||||
use futures::future::join_all;
|
||||
use mdns_sd::{ServiceDaemon, ServiceEvent};
|
||||
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)]
|
||||
pub struct UdpRpc {
|
||||
|
101
src-tauri/src/volume/manager.rs
Normal file
101
src-tauri/src/volume/manager.rs
Normal 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(())
|
||||
}
|
||||
}
|
3
src-tauri/src/volume/mod.rs
Normal file
3
src-tauri/src/volume/mod.rs
Normal file
@ -0,0 +1,3 @@
|
||||
mod manager;
|
||||
|
||||
pub use manager::*;
|
Reference in New Issue
Block a user