@@ -5,42 +5,79 @@ use std::sync::{Arc, Mutex};
55
66/// ALSA's implementation for `Devices`.
77pub struct Devices {
8- hint_iter : alsa:: device_name:: HintIter ,
8+ builtin_pos : usize ,
9+ card_iter : alsa:: card:: Iter ,
910}
1011
1112impl Devices {
1213 pub fn new ( ) -> Result < Self , DevicesError > {
1314 Ok ( Devices {
14- hint_iter : alsa:: device_name:: HintIter :: new_str ( None , "pcm" ) ?,
15+ builtin_pos : 0 ,
16+ card_iter : alsa:: card:: Iter :: new ( ) ,
1517 } )
1618 }
1719}
1820
1921unsafe impl Send for Devices { }
2022unsafe impl Sync for Devices { }
2123
24+ const BUILTINS : [ & ' static str ; 5 ] = [ "default" , "pipewire" , "pulse" , "jack" , "oss" ] ;
25+
2226impl Iterator for Devices {
2327 type Item = Device ;
2428
2529 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+
2644 loop {
27- match self . hint_iter . next ( ) {
28- None => return None ,
29- Some ( hint) => {
30- let name = match hint. name {
31- None => continue ,
32- // Ignoring the `null` device.
33- Some ( name) if name == "null" => continue ,
34- Some ( name) => name,
35- } ;
45+ let Some ( res) = self . card_iter . next ( ) else {
46+ return None ;
47+ } ;
48+ let Ok ( card) = res else { continue } ;
49+
50+ let ctl_id = format ! ( "hw:{}" , card. get_index( ) ) ;
51+ let Ok ( ctl) = alsa:: Ctl :: new ( & ctl_id, false ) else {
52+ continue ;
53+ } ;
54+ let Ok ( cardinfo) = ctl. card_info ( ) else {
55+ continue ;
56+ } ;
57+ let Ok ( card_name) = cardinfo. get_name ( ) else {
58+ continue ;
59+ } ;
3660
37- if let Ok ( handles) = DeviceHandles :: open ( & name) {
38- return Some ( Device {
39- name,
40- handles : Arc :: new ( Mutex :: new ( handles) ) ,
41- } ) ;
42- }
43- }
61+ // Using plughw adds the ALSA plug layer, which can do sample type conversion,
62+ // sample rate convertion, ...
63+ // It is convenient, but at the same time not suitable for pro-audio as it hides
64+ // the actual device capabilities and perform audio manipulation under your feet,
65+ // for example sample rate conversion, sample format conversion, adds dummy channels,
66+ // ...
67+ // In the mean time, CPAL doesn't perform implicit sample format conversion,
68+ // so just enable plughw for now.
69+ const USE_PLUGHW : bool = true ;
70+ let pcm_id = if USE_PLUGHW {
71+ format ! ( "plughw:{}" , card. get_index( ) )
72+ } else {
73+ ctl_id
74+ } ;
75+ if let Ok ( handles) = DeviceHandles :: open ( & pcm_id) {
76+ return Some ( Device {
77+ name : card_name. to_string ( ) ,
78+ pcm_id : pcm_id. to_string ( ) ,
79+ handles : Arc :: new ( Mutex :: new ( handles) ) ,
80+ } ) ;
4481 }
4582 }
4683 }
@@ -50,6 +87,7 @@ impl Iterator for Devices {
5087pub fn default_input_device ( ) -> Option < Device > {
5188 Some ( Device {
5289 name : "default" . to_owned ( ) ,
90+ pcm_id : "default" . to_owned ( ) ,
5391 handles : Arc :: new ( Mutex :: new ( Default :: default ( ) ) ) ,
5492 } )
5593}
@@ -58,6 +96,7 @@ pub fn default_input_device() -> Option<Device> {
5896pub fn default_output_device ( ) -> Option < Device > {
5997 Some ( Device {
6098 name : "default" . to_owned ( ) ,
99+ pcm_id : "default" . to_owned ( ) ,
61100 handles : Arc :: new ( Mutex :: new ( Default :: default ( ) ) ) ,
62101 } )
63102}
0 commit comments