cpal/host/alsa/
enumerate.rs1use std::collections::HashSet;
2
3use super::{alsa, Device, Host};
4use crate::{BackendSpecificError, DeviceDirection, DevicesError};
5
6const HW_PREFIX: &str = "hw";
7const PLUGHW_PREFIX: &str = "plughw";
8
9struct PhysicalDevice {
11 card_index: u32,
12 card_name: Option<String>,
13 device_index: u32,
14 device_name: Option<String>,
15 direction: DeviceDirection,
16}
17
18pub type Devices = std::vec::IntoIter<Device>;
20
21impl Host {
22 pub(super) fn enumerate_devices(&self) -> Result<Devices, DevicesError> {
28 let mut devices = Vec::new();
29 let mut seen_pcm_ids = HashSet::new();
30
31 let physical_devices = physical_devices();
32
33 if let Ok(hints) = alsa::device_name::HintIter::new_str(None, "pcm") {
35 for hint in hints {
36 if let Some(pcm_id) = hint.name {
37 let direction = hint.direction.map_or(DeviceDirection::Duplex, Into::into);
41 let device = Device {
42 pcm_id,
43 desc: hint.desc,
44 direction,
45 _context: self.inner.clone(),
46 };
47
48 seen_pcm_ids.insert(device.pcm_id.clone());
49 devices.push(device);
50 }
51 }
52 }
53
54 for phys_dev in physical_devices {
56 for prefix in [HW_PREFIX, PLUGHW_PREFIX] {
57 let pcm_id = format!(
58 "{}:CARD={},DEV={}",
59 prefix, phys_dev.card_index, phys_dev.device_index
60 );
61
62 if seen_pcm_ids.insert(pcm_id.clone()) {
63 devices.push(Device {
64 pcm_id,
65 desc: Some(format_device_description(&phys_dev, prefix)),
66 direction: phys_dev.direction,
67 _context: self.inner.clone(),
68 });
69 }
70 }
71 }
72
73 Ok(devices.into_iter())
74 }
75}
76
77fn format_device_description(phys_dev: &PhysicalDevice, prefix: &str) -> String {
79 let first_line = match (&phys_dev.card_name, &phys_dev.device_name) {
81 (Some(card), Some(device)) => format!("{}, {}", card, device),
82 (Some(card), None) => card.clone(),
83 (None, Some(device)) => device.clone(),
84 (None, None) => format!("Card {}", phys_dev.card_index),
85 };
86
87 let second_line = match prefix {
89 HW_PREFIX => "Direct hardware device without any conversions",
90 PLUGHW_PREFIX => "Hardware device with all software conversions",
91 _ => "",
92 };
93
94 format!("{}\n{}", first_line, second_line)
95}
96
97fn physical_devices() -> Vec<PhysicalDevice> {
98 let mut devices = Vec::new();
99 for card in alsa::card::Iter::new().filter_map(Result::ok) {
100 let card_index = card.get_index() as u32;
101 let ctl = match alsa::Ctl::new(&format!("{}:{}", HW_PREFIX, card_index), false) {
102 Ok(ctl) => ctl,
103 Err(_) => continue,
104 };
105 let card_name = ctl
106 .card_info()
107 .ok()
108 .and_then(|info| info.get_name().ok().map(|s| s.to_string()));
109
110 for device_index in alsa::ctl::DeviceIter::new(&ctl) {
111 let device_index = device_index as u32;
112 let playback_info = ctl
113 .pcm_info(device_index, 0, alsa::Direction::Playback)
114 .ok();
115 let capture_info = ctl.pcm_info(device_index, 0, alsa::Direction::Capture).ok();
116
117 let (direction, device_name) = match (&playback_info, &capture_info) {
118 (Some(p_info), Some(_c_info)) => (
119 DeviceDirection::Duplex,
120 p_info.get_name().ok().map(|s| s.to_string()),
121 ),
122 (Some(p_info), None) => (
123 DeviceDirection::Output,
124 p_info.get_name().ok().map(|s| s.to_string()),
125 ),
126 (None, Some(c_info)) => (
127 DeviceDirection::Input,
128 c_info.get_name().ok().map(|s| s.to_string()),
129 ),
130 (None, None) => {
131 continue;
133 }
134 };
135
136 let device_name = device_name.unwrap_or_else(|| format!("Device {}", device_index));
137 devices.push(PhysicalDevice {
138 card_index,
139 card_name: card_name.clone(),
140 device_index,
141 device_name: Some(device_name),
142 direction,
143 });
144 }
145 }
146
147 devices
148}
149
150impl From<alsa::Error> for DevicesError {
151 fn from(err: alsa::Error) -> Self {
152 let err: BackendSpecificError = err.into();
153 err.into()
154 }
155}
156
157impl From<alsa::Direction> for DeviceDirection {
158 fn from(direction: alsa::Direction) -> Self {
159 match direction {
160 alsa::Direction::Playback => DeviceDirection::Output,
161 alsa::Direction::Capture => DeviceDirection::Input,
162 }
163 }
164}