cpal/host/alsa/
enumerate.rs

1use super::alsa;
2use super::{Device, DeviceHandles};
3use crate::{BackendSpecificError, DevicesError};
4use std::sync::{Arc, Mutex};
5
6/// ALSA's implementation for `Devices`.
7pub struct Devices {
8    builtin_pos: usize,
9    card_iter: alsa::card::Iter,
10}
11
12impl Devices {
13    pub fn new() -> Result<Self, DevicesError> {
14        Ok(Devices {
15            builtin_pos: 0,
16            card_iter: alsa::card::Iter::new(),
17        })
18    }
19}
20
21unsafe impl Send for Devices {}
22unsafe impl Sync for Devices {}
23
24const BUILTINS: [&str; 5] = ["default", "pipewire", "pulse", "jack", "oss"];
25
26impl Iterator for Devices {
27    type Item = Device;
28
29    fn next(&mut self) -> Option<Device> {
30        while self.builtin_pos < BUILTINS.len() {
31            let pos = self.builtin_pos;
32            self.builtin_pos += 1;
33            let name = BUILTINS[pos];
34
35            if let Ok(handles) = DeviceHandles::open(name) {
36                return Some(Device {
37                    name: name.to_string(),
38                    pcm_id: name.to_string(),
39                    handles: Arc::new(Mutex::new(handles)),
40                });
41            }
42        }
43
44        loop {
45            let res = self.card_iter.next()?;
46            let Ok(card) = res else { continue };
47
48            let ctl_id = format!("hw:{}", card.get_index());
49            let Ok(ctl) = alsa::Ctl::new(&ctl_id, false) else {
50                continue;
51            };
52            let Ok(cardinfo) = ctl.card_info() else {
53                continue;
54            };
55            let Ok(card_name) = cardinfo.get_name() else {
56                continue;
57            };
58
59            // Using plughw adds the ALSA plug layer, which can do sample type conversion,
60            // sample rate convertion, ...
61            // It is convenient, but at the same time not suitable for pro-audio as it hides
62            // the actual device capabilities and perform audio manipulation under your feet,
63            // for example sample rate conversion, sample format conversion, adds dummy channels,
64            // ...
65            // For now, many hardware only support 24bit / 3 bytes, which isn't yet supported by
66            // cpal. So we have to enable plughw (unfortunately) for maximum compatibility.
67            const USE_PLUGHW: bool = true;
68            let pcm_id = if USE_PLUGHW {
69                format!("plughw:{}", card.get_index())
70            } else {
71                ctl_id
72            };
73            if let Ok(handles) = DeviceHandles::open(&pcm_id) {
74                return Some(Device {
75                    name: card_name.to_string(),
76                    pcm_id: pcm_id.to_string(),
77                    handles: Arc::new(Mutex::new(handles)),
78                });
79            }
80        }
81    }
82}
83
84#[inline]
85pub fn default_input_device() -> Option<Device> {
86    Some(Device {
87        name: "default".to_owned(),
88        pcm_id: "default".to_owned(),
89        handles: Arc::new(Mutex::new(Default::default())),
90    })
91}
92
93#[inline]
94pub fn default_output_device() -> Option<Device> {
95    Some(Device {
96        name: "default".to_owned(),
97        pcm_id: "default".to_owned(),
98        handles: Arc::new(Mutex::new(Default::default())),
99    })
100}
101
102impl From<alsa::Error> for DevicesError {
103    fn from(err: alsa::Error) -> Self {
104        let err: BackendSpecificError = err.into();
105        err.into()
106    }
107}