azalea_shell/service/
audio.rs1use alsa::mixer::{SelemChannelId, SelemId};
2use tokio::sync::broadcast;
3
4#[derive(azalea_derive::StaticServiceManager)]
5pub struct Service {
6 mixer: alsa::Mixer,
7 selem_id: SelemId,
8 previous_volume: i64,
9 interval_duration: std::time::Duration,
10}
11
12#[derive(Clone)]
13pub struct Init {
14 selem_name: String,
15 interval_duration: std::time::Duration,
16}
17
18impl Default for Init {
19 fn default() -> Self {
20 Self {
21 selem_name: format!("Master"),
22 interval_duration: std::time::Duration::from_secs(1),
23 }
24 }
25}
26
27#[derive(Clone, Debug)]
28pub enum Input {
29 SystemVolume(f64),
31}
32
33#[derive(Clone, Debug)]
34pub enum Output {
35 SystemVolume(f64),
37}
38
39impl azalea_service::Service for Service {
40 type Init = Init;
41 type Input = Input;
42 type Event = ();
43 type Output = Output;
44
45 const DISABLE_EVENTS: bool = true;
47
48 async fn new(
49 init: Self::Init,
50 _: flume::Sender<Self::Input>,
51 output_sender: broadcast::Sender<Self::Output>,
52 ) -> Self {
53 let mixer = alsa::mixer::Mixer::new("default", false).unwrap();
54
55 let mut this = Self {
56 mixer,
57 selem_id: SelemId::new(&init.selem_name, 0),
58 interval_duration: init.interval_duration,
59 previous_volume: 0,
60 };
61
62 if let Some(volume) = this.get_system_volume() {
63 drop(output_sender.send(Output::SystemVolume(volume)));
64 }
65
66 this
67 }
68
69 async fn message(
70 &mut self,
71 input: Self::Input,
72 output_sender: &broadcast::Sender<Self::Output>,
73 ) {
74 match input {
75 Input::SystemVolume(volume_percent) => {
76 if let Some(selem) = self.mixer.find_selem(&self.selem_id) {
77 let (min_volume, max_volume) = selem.get_playback_volume_range();
78
79 let _ = selem.set_playback_volume_all(
80 (volume_percent * (max_volume - min_volume) as f64) as i64 + min_volume,
81 );
82
83 drop(output_sender.send(Output::SystemVolume(volume_percent)));
84 } else {
85 azalea_log::warning!("[AUDIO]: Failed to find Master selem");
86 }
87 }
88 }
89 }
90
91 async fn event_generator(&mut self) -> Self::Event {
92 tokio::time::sleep(self.interval_duration).await;
93 }
94
95 async fn event_handler(
96 &mut self,
97 _event: Self::Event,
98 output_sender: &tokio::sync::broadcast::Sender<Self::Output>,
99 ) -> azalea_service::Result<()> {
100 if let Some(volume) = self.get_system_volume() {
101 output_sender.send(Output::SystemVolume(volume))?;
102 }
103
104 Ok(())
105 }
106}
107
108impl Service {
109 fn get_system_volume(&mut self) -> Option<f64> {
110 if let Some(selem) = self.mixer.find_selem(&self.selem_id) {
111 let mut count = 0;
112 let mut acc_volume = 0;
113
114 for channel_id in SelemChannelId::all() {
115 if let Ok(channel_volume) = selem.get_playback_volume(*channel_id) {
116 acc_volume += channel_volume;
117 count += 1;
118 }
119 }
120
121 let volume_int = acc_volume / count;
122 let (min_volume, max_volume) = selem.get_playback_volume_range();
123
124 if self.previous_volume != volume_int {
125 let volume_percent = volume_int as f64 / ((max_volume - min_volume) as f64);
126 self.previous_volume = volume_int;
127 return Some(volume_percent);
128 }
129 }
130
131 None
132 }
133}