azalea_shell/service/
audio.rs

1use 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    /// Default sink volume
30    SystemVolume(f64),
31}
32
33#[derive(Clone, Debug)]
34pub enum Output {
35    /// Default sink volume
36    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    // TODO: Fix audio polling
46    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}