Skip to main content

wayle_audio/core/device/input/
mod.rs

1pub(crate) mod controls;
2pub(crate) mod monitoring;
3pub(crate) mod types;
4
5use std::{collections::HashMap, sync::Arc};
6
7use controls::InputDeviceController;
8use derive_more::Debug;
9use libpulse_binding::time::MicroSeconds;
10use tokio::sync::oneshot;
11use tokio_util::sync::CancellationToken;
12pub(crate) use types::{InputDeviceParams, LiveInputDeviceParams};
13use wayle_core::Property;
14use wayle_traits::{ModelMonitoring, Reactive};
15
16use crate::{
17    backend::{
18        commands::Command,
19        types::{CommandSender, EventSender},
20    },
21    error::Error,
22    types::{
23        device::{Device, DeviceKey, DevicePort, DeviceState, DeviceType, SourceInfo},
24        format::{AudioFormat, ChannelMap, SampleSpec},
25    },
26    volume::types::Volume,
27};
28
29/// PulseAudio source with reactive properties and control methods.
30///
31/// Instances from [`AudioService`] fields are live (properties auto-update).
32/// Instances from [`AudioService::input_device`] are snapshots (frozen state).
33///
34/// # Control Methods
35///
36/// - [`set_volume`](Self::set_volume) - Adjust input gain
37/// - [`set_mute`](Self::set_mute) - Mute or unmute
38/// - [`set_port`](Self::set_port) - Switch input port
39/// - [`set_as_default`](Self::set_as_default) - Make this the default input
40///
41/// [`AudioService`]: crate::AudioService
42/// [`AudioService::input_device`]: crate::AudioService::input_device
43#[derive(Clone, Debug)]
44pub struct InputDevice {
45    /// Command sender for backend operations
46    #[debug(skip)]
47    command_tx: CommandSender,
48
49    /// Event sender for monitoring (only for live instances)
50    #[debug(skip)]
51    event_tx: Option<EventSender>,
52
53    /// Cancellation token for monitoring (only for live instances)
54    #[debug(skip)]
55    cancellation_token: Option<CancellationToken>,
56
57    /// Device key for identification
58    pub key: DeviceKey,
59
60    /// Device name (internal identifier)
61    pub name: Property<String>,
62
63    /// Human-readable description
64    pub description: Property<String>,
65
66    /// Card index this device belongs to
67    pub card_index: Property<Option<u32>>,
68
69    /// Index of the owning module
70    pub owner_module: Property<Option<u32>>,
71
72    /// Driver name
73    pub driver: Property<String>,
74
75    /// Device state
76    pub state: Property<DeviceState>,
77
78    /// Current volume levels
79    pub volume: Property<Volume>,
80
81    /// Base volume (reference level)
82    pub base_volume: Property<Volume>,
83
84    /// Number of volume steps for devices which do not support arbitrary volumes
85    pub n_volume_steps: Property<u32>,
86
87    /// Whether device is muted
88    pub muted: Property<bool>,
89
90    /// Device properties from PulseAudio
91    pub properties: Property<HashMap<String, String>>,
92
93    /// Available ports
94    pub ports: Property<Vec<DevicePort>>,
95
96    /// Currently active port
97    pub active_port: Property<Option<String>>,
98
99    /// Supported audio formats
100    pub formats: Property<Vec<AudioFormat>>,
101
102    /// Sample specification
103    pub sample_spec: Property<SampleSpec>,
104
105    /// Channel map
106    pub channel_map: Property<ChannelMap>,
107
108    /// Index of the sink being monitored (if this is a monitor source)
109    pub monitor_of_sink: Property<Option<u32>>,
110
111    /// Name of the sink being monitored (if this is a monitor source)
112    pub monitor_of_sink_name: Property<Option<String>>,
113
114    /// Whether this is a monitor source
115    pub is_monitor: Property<bool>,
116
117    /// Latency in microseconds
118    pub latency: Property<MicroSeconds>,
119
120    /// Configured latency in microseconds
121    pub configured_latency: Property<MicroSeconds>,
122
123    /// Device flags (raw flags from PulseAudio)
124    pub flags: Property<u32>,
125}
126
127impl PartialEq for InputDevice {
128    fn eq(&self, other: &Self) -> bool {
129        self.key == other.key
130    }
131}
132
133impl Reactive for InputDevice {
134    type Context<'a> = InputDeviceParams<'a>;
135    type LiveContext<'a> = LiveInputDeviceParams<'a>;
136    type Error = Error;
137
138    async fn get(params: Self::Context<'_>) -> Result<Self, Self::Error> {
139        let (tx, rx) = oneshot::channel();
140        params
141            .command_tx
142            .send(Command::GetDevice {
143                device_key: params.device_key,
144                responder: tx,
145            })
146            .map_err(|_| Error::CommandChannelDisconnected)?;
147
148        let device = rx.await.map_err(|_| Error::CommandChannelDisconnected)??;
149
150        match device {
151            Device::Source(source) => Ok(Self::from_source(
152                &source,
153                params.command_tx.clone(),
154                None,
155                None,
156            )),
157            Device::Sink(_) => Err(Error::DeviceNotFound {
158                index: params.device_key.index,
159                device_type: DeviceType::Input,
160            }),
161        }
162    }
163
164    async fn get_live(params: Self::LiveContext<'_>) -> Result<Arc<Self>, Self::Error> {
165        let (tx, rx) = oneshot::channel();
166        params
167            .command_tx
168            .send(Command::GetDevice {
169                device_key: params.device_key,
170                responder: tx,
171            })
172            .map_err(|_| Error::CommandChannelDisconnected)?;
173
174        let device = rx.await.map_err(|_| Error::CommandChannelDisconnected)??;
175
176        let device = match device {
177            Device::Source(source) => Arc::new(Self::from_source(
178                &source,
179                params.command_tx.clone(),
180                Some(params.event_tx.clone()),
181                Some(params.cancellation_token.child_token()),
182            )),
183            Device::Sink(_) => {
184                return Err(Error::DeviceNotFound {
185                    index: params.device_key.index,
186                    device_type: DeviceType::Input,
187                });
188            }
189        };
190
191        device.clone().start_monitoring().await?;
192
193        Ok(device)
194    }
195}
196
197impl InputDevice {
198    pub(crate) fn from_source(
199        source: &SourceInfo,
200        command_tx: CommandSender,
201        event_tx: Option<EventSender>,
202        cancellation_token: Option<CancellationToken>,
203    ) -> Self {
204        Self {
205            command_tx,
206            event_tx,
207            cancellation_token,
208            key: source.key(),
209            name: Property::new(source.device.name.clone()),
210            description: Property::new(source.device.description.clone()),
211            card_index: Property::new(source.device.card_index),
212            owner_module: Property::new(source.device.owner_module),
213            driver: Property::new(source.device.driver.clone()),
214            state: Property::new(source.device.state),
215            volume: Property::new(source.device.volume.clone()),
216            base_volume: Property::new(source.device.base_volume.clone()),
217            n_volume_steps: Property::new(source.device.n_volume_steps),
218            muted: Property::new(source.device.muted),
219            properties: Property::new(source.device.properties.clone()),
220            ports: Property::new(source.device.ports.clone()),
221            active_port: Property::new(source.device.active_port.clone()),
222            formats: Property::new(source.device.formats.clone()),
223            sample_spec: Property::new(source.device.sample_spec.clone()),
224            channel_map: Property::new(source.device.channel_map.clone()),
225            monitor_of_sink: Property::new(source.monitor_of_sink),
226            monitor_of_sink_name: Property::new(source.monitor_of_sink_name.clone()),
227            is_monitor: Property::new(source.is_monitor),
228            latency: Property::new(source.device.latency),
229            configured_latency: Property::new(source.device.configured_latency),
230            flags: Property::new(source.device.flags),
231        }
232    }
233
234    pub(crate) fn update_from_source(&self, source: &SourceInfo) {
235        self.name.set(source.device.name.clone());
236        self.description.set(source.device.description.clone());
237        self.card_index.set(source.device.card_index);
238        self.owner_module.set(source.device.owner_module);
239        self.driver.set(source.device.driver.clone());
240        self.state.set(source.device.state);
241        self.volume.set(source.device.volume.clone());
242        self.base_volume.set(source.device.base_volume.clone());
243        self.n_volume_steps.set(source.device.n_volume_steps);
244        self.muted.set(source.device.muted);
245        self.properties.set(source.device.properties.clone());
246        self.ports.set(source.device.ports.clone());
247        self.active_port.set(source.device.active_port.clone());
248        self.formats.set(source.device.formats.clone());
249        self.sample_spec.set(source.device.sample_spec.clone());
250        self.channel_map.set(source.device.channel_map.clone());
251        self.monitor_of_sink.set(source.monitor_of_sink);
252        self.monitor_of_sink_name
253            .set(source.monitor_of_sink_name.clone());
254        self.is_monitor.set(source.is_monitor);
255        self.latency.set(source.device.latency);
256        self.configured_latency
257            .set(source.device.configured_latency);
258        self.flags.set(source.device.flags);
259    }
260
261    /// Set the volume for this input device.
262    ///
263    /// # Errors
264    /// Returns error if backend communication fails or device operation fails.
265    pub async fn set_volume(&self, volume: Volume) -> Result<(), Error> {
266        InputDeviceController::set_volume(&self.command_tx, self.key, volume).await
267    }
268
269    /// Set the mute state for this input device.
270    ///
271    /// # Errors
272    /// Returns error if backend communication fails or device operation fails.
273    pub async fn set_mute(&self, muted: bool) -> Result<(), Error> {
274        InputDeviceController::set_mute(&self.command_tx, self.key, muted).await
275    }
276
277    /// Set the active port for this input device.
278    ///
279    /// # Errors
280    /// Returns error if backend communication fails or device operation fails.
281    pub async fn set_port(&self, port: String) -> Result<(), Error> {
282        InputDeviceController::set_port(&self.command_tx, self.key, port).await
283    }
284
285    /// Set this device as the default input.
286    ///
287    /// # Errors
288    /// Returns error if backend communication fails or device operation fails.
289    pub async fn set_as_default(&self) -> Result<(), Error> {
290        InputDeviceController::set_as_default(&self.command_tx, self.key).await
291    }
292}