nandi/jolt-nativepublic Fork 0
2ab40bf193b6a39bc8d1397cc36d17572bbf59cd
Commits
Clone
git clone https://git.rickub.com/nandi/jolt-native.git
git clone ssh://git@rickub.com/nandi/jolt-native.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

device_description.rs · 400 lines · 11.4 KBRust Blame HistoryRaw
Lift freeq's AV media plane out of sleek 90f8b89 nandi 19d ago1//! Device metadata and description types.
2//!
3//! This module provides structured information about audio devices including manufacturer,
4//! device type, interface type, and connection details. Not all backends provide complete
5//! information - availability depends on platform capabilities.
6
7use std::fmt;
8
9use crate::ChannelCount;
10
11/// Describes an audio device with structured metadata.
12///
13/// This type provides structured information about an audio device beyond just its name.
14/// Availability depends on the host implementation and platform capabilities.
15#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct DeviceDescription {
17 /// Human-readable device name
18 name: String,
19
20 /// Device manufacturer or vendor name
21 manufacturer: Option<String>,
22
23 /// Driver name
24 driver: Option<String>,
25
26 /// Categorization of device type
27 device_type: DeviceType,
28
29 /// Connection/interface type
30 interface_type: InterfaceType,
31
32 /// Direction: input, output, or duplex
33 direction: DeviceDirection,
34
35 /// Physical address or connection identifier
36 address: Option<String>,
37
38 /// Additional description lines with non-structured, detailed information.
39 extended: Vec<String>,
40}
41
42/// Categorization of audio device types.
43///
44/// This describes the kind of audio device (speaker, microphone, headset, etc.)
45/// regardless of how it connects to the system.
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
47#[non_exhaustive]
48pub enum DeviceType {
49 /// Speaker (built-in or external)
50 Speaker,
51
52 /// Microphone (built-in or external)
53 Microphone,
54
55 /// Headphones (audio output only)
56 Headphones,
57
58 /// Headset (combined headphones + microphone)
59 Headset,
60
61 /// Earpiece (phone-style speaker, typically for voice calls)
62 Earpiece,
63
64 /// Handset (telephone-style handset with speaker and microphone)
65 Handset,
66
67 /// Hearing aid device
68 HearingAid,
69
70 /// Docking station audio
71 Dock,
72
73 /// Radio/TV tuner
74 Tuner,
75
76 /// Virtual/loopback device (software audio routing)
77 Virtual,
78
79 /// Unknown or unclassified device type
80 #[default]
81 Unknown,
82}
83
84/// How the device connects to the system (interface/connection type).
85///
86/// This describes the physical or logical connection between the audio device
87/// and the computer system.
88#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
89#[non_exhaustive]
90pub enum InterfaceType {
91 /// Built-in to the system (integrated audio chipset)
92 BuiltIn,
93
94 /// USB connection
95 Usb,
96
97 /// Bluetooth wireless connection
98 Bluetooth,
99
100 /// PCI or PCIe card (internal sound card)
101 Pci,
102
103 /// FireWire connection (IEEE 1394)
104 FireWire,
105
106 /// Thunderbolt connection
107 Thunderbolt,
108
109 /// HDMI connection
110 Hdmi,
111
112 /// Line-level analog connection (line in/out, aux)
113 Line,
114
115 /// S/PDIF digital audio interface
116 Spdif,
117
118 /// Network connection (Dante, AVB, AirPlay, IP audio, etc.)
119 Network,
120
121 /// Virtual/loopback connection (software audio routing, not physical hardware)
122 Virtual,
123
124 /// DisplayPort audio
125 DisplayPort,
126
127 /// Aggregate device (combines multiple devices)
128 Aggregate,
129
130 /// Unknown connection type
131 #[default]
132 Unknown,
133}
134
135/// The direction(s) that a device supports.
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
137#[non_exhaustive]
138pub enum DeviceDirection {
139 /// Input only (capture/recording)
140 Input,
141
142 /// Output only (playback/rendering)
143 Output,
144
145 /// Both input and output
146 Duplex,
147
148 /// Direction unknown or not yet determined
149 #[default]
150 Unknown,
151}
152
153impl DeviceDescription {
154 /// Returns the human-readable device name.
155 ///
156 /// This is always available and is the primary user-facing identifier.
157 pub fn name(&self) -> &str {
158 &self.name
159 }
160
161 /// Returns the manufacturer/vendor name if available.
162 pub fn manufacturer(&self) -> Option<&str> {
163 self.manufacturer.as_deref()
164 }
165
166 /// Returns the driver name if available.
167 pub fn driver(&self) -> Option<&str> {
168 self.driver.as_deref()
169 }
170
171 /// Returns the device type categorization.
172 pub fn device_type(&self) -> DeviceType {
173 self.device_type
174 }
175
176 /// Returns the interface/connection type.
177 pub fn interface_type(&self) -> InterfaceType {
178 self.interface_type
179 }
180
181 /// Returns the device direction.
182 pub fn direction(&self) -> DeviceDirection {
183 self.direction
184 }
185
186 /// Returns whether this device supports audio input (capture).
187 ///
188 /// This is a convenience method that checks if direction is `Input` or `Duplex`.
189 pub fn supports_input(&self) -> bool {
190 matches!(
191 self.direction,
192 DeviceDirection::Input | DeviceDirection::Duplex
193 )
194 }
195
196 /// Returns whether this device supports audio output (playback).
197 ///
198 /// This is a convenience method that checks if direction is `Output` or `Duplex`.
199 pub fn supports_output(&self) -> bool {
200 matches!(
201 self.direction,
202 DeviceDirection::Output | DeviceDirection::Duplex
203 )
204 }
205
206 /// Returns the physical address or connection identifier if available.
207 pub fn address(&self) -> Option<&str> {
208 self.address.as_deref()
209 }
210
211 /// Returns additional description lines with detailed information.
212 pub fn extended(&self) -> &[String] {
213 &self.extended
214 }
215}
216
217impl fmt::Display for DeviceDescription {
218 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219 write!(f, "{}", self.name)?;
220
221 if let Some(mfr) = &self.manufacturer {
222 write!(f, " ({})", mfr)?;
223 }
224
225 if self.device_type != DeviceType::Unknown {
226 write!(f, " [{}]", self.device_type)?;
227 }
228
229 if self.interface_type != InterfaceType::Unknown {
230 write!(f, " via {}", self.interface_type)?;
231 }
232
233 Ok(())
234 }
235}
236
237impl fmt::Display for DeviceType {
238 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
239 match self {
240 DeviceType::Speaker => write!(f, "Speaker"),
241 DeviceType::Microphone => write!(f, "Microphone"),
242 DeviceType::Headphones => write!(f, "Headphones"),
243 DeviceType::Headset => write!(f, "Headset"),
244 DeviceType::Earpiece => write!(f, "Earpiece"),
245 DeviceType::Handset => write!(f, "Handset"),
246 DeviceType::HearingAid => write!(f, "Hearing Aid"),
247 DeviceType::Dock => write!(f, "Dock"),
248 DeviceType::Tuner => write!(f, "Tuner"),
249 DeviceType::Virtual => write!(f, "Virtual"),
250 _ => write!(f, "Unknown"),
251 }
252 }
253}
254
255impl fmt::Display for InterfaceType {
256 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
257 match self {
258 InterfaceType::BuiltIn => write!(f, "Built-in"),
259 InterfaceType::Usb => write!(f, "USB"),
260 InterfaceType::Bluetooth => write!(f, "Bluetooth"),
261 InterfaceType::Pci => write!(f, "PCI"),
262 InterfaceType::FireWire => write!(f, "FireWire"),
263 InterfaceType::Thunderbolt => write!(f, "Thunderbolt"),
264 InterfaceType::Hdmi => write!(f, "HDMI"),
265 InterfaceType::Line => write!(f, "Line"),
266 InterfaceType::Spdif => write!(f, "S/PDIF"),
267 InterfaceType::Network => write!(f, "Network"),
268 InterfaceType::Virtual => write!(f, "Virtual"),
269 InterfaceType::DisplayPort => write!(f, "DisplayPort"),
270 InterfaceType::Aggregate => write!(f, "Aggregate"),
271 _ => write!(f, "Unknown"),
272 }
273 }
274}
275
276impl fmt::Display for DeviceDirection {
277 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
278 match self {
279 DeviceDirection::Input => write!(f, "Input"),
280 DeviceDirection::Output => write!(f, "Output"),
281 DeviceDirection::Duplex => write!(f, "Duplex"),
282 _ => write!(f, "Unknown"),
283 }
284 }
285}
286
287/// Builder for constructing a `DeviceDescription`.
288///
289/// This is primarily used by host implementations and custom hosts
290/// to gradually build up device descriptions with available metadata.
291#[derive(Debug, Clone)]
292pub struct DeviceDescriptionBuilder {
293 name: String,
294 manufacturer: Option<String>,
295 driver: Option<String>,
296 device_type: DeviceType,
297 interface_type: InterfaceType,
298 direction: DeviceDirection,
299 address: Option<String>,
300 extended: Vec<String>,
301}
302
303impl DeviceDescriptionBuilder {
304 /// Creates a new builder with the device name (required).
305 pub fn new(name: impl Into<String>) -> Self {
306 Self {
307 name: name.into(),
308 manufacturer: None,
309 driver: None,
310 device_type: DeviceType::default(),
311 interface_type: InterfaceType::default(),
312 direction: DeviceDirection::default(),
313 address: None,
314 extended: Vec::new(),
315 }
316 }
317
318 /// Sets the manufacturer name.
319 pub fn manufacturer(mut self, manufacturer: impl Into<String>) -> Self {
320 self.manufacturer = Some(manufacturer.into());
321 self
322 }
323
324 /// Sets the driver name.
325 pub fn driver(mut self, driver: impl Into<String>) -> Self {
326 self.driver = Some(driver.into());
327 self
328 }
329
330 /// Sets the device type.
331 pub fn device_type(mut self, device_type: DeviceType) -> Self {
332 self.device_type = device_type;
333 self
334 }
335
336 /// Sets the interface type.
337 pub fn interface_type(mut self, interface_type: InterfaceType) -> Self {
338 self.interface_type = interface_type;
339 self
340 }
341
342 /// Sets the device direction.
343 pub fn direction(mut self, direction: DeviceDirection) -> Self {
344 self.direction = direction;
345 self
346 }
347
348 /// Sets the physical address.
349 pub fn address(mut self, address: impl Into<String>) -> Self {
350 self.address = Some(address.into());
351 self
352 }
353
354 /// Sets the description lines.
355 pub fn extended(mut self, lines: Vec<String>) -> Self {
356 self.extended = lines;
357 self
358 }
359
360 /// Adds a single description line.
361 pub fn add_extended_line(mut self, line: impl Into<String>) -> Self {
362 self.extended.push(line.into());
363 self
364 }
365
366 /// Builds the [`DeviceDescription`].
367 pub fn build(self) -> DeviceDescription {
368 DeviceDescription {
369 name: self.name,
370 manufacturer: self.manufacturer,
371 driver: self.driver,
372 device_type: self.device_type,
373 interface_type: self.interface_type,
374 direction: self.direction,
375 address: self.address,
376 extended: self.extended,
377 }
378 }
379}
380
381/// Determines device direction from input/output capabilities.
382pub(crate) fn direction_from_caps(has_input: bool, has_output: bool) -> DeviceDirection {
383 match (has_input, has_output) {
384 (true, true) => DeviceDirection::Duplex,
385 (true, false) => DeviceDirection::Input,
386 (false, true) => DeviceDirection::Output,
387 (false, false) => DeviceDirection::Unknown,
388 }
389}
390
391/// Determines device direction from input/output channel counts.
392#[allow(dead_code)]
393pub(crate) fn direction_from_counts(
394 input_channels: Option<ChannelCount>,
395 output_channels: Option<ChannelCount>,
396) -> DeviceDirection {
397 let has_input = input_channels.map(|n| n > 0).unwrap_or(false);
398 let has_output = output_channels.map(|n| n > 0).unwrap_or(false);
399 direction_from_caps(has_input, has_output)
400}