makepad_platform/os/linux/
alsa_audio.rs

1use {
2    std::collections::HashSet,
3    std::sync::{Arc, Mutex},
4    std::os::raw::{c_void, c_char},
5    std::ffi::CStr,
6    crate::{
7        makepad_live_id::*,
8        thread::SignalToUI,
9        audio::*,
10        os::linux::libc_sys,
11        os::linux::alsa_sys::*
12    }
13};
14 
15struct AlsaAudioDesc {
16    name: String,
17    desc: AudioDeviceDesc,
18}
19
20struct AlsaAudioDevice {
21    device_handle: *mut snd_pcm_t,
22    channel_count: usize,
23    frame_count: usize,
24    interleaved: Vec<f32>,
25    _buffer_size: usize,
26}
27
28struct AlsaAudioDeviceRef {
29    device_id: AudioDeviceId,
30    is_terminated: bool,
31}
32
33pub struct AlsaAudioAccess {
34    pub audio_input_cb: [Arc<Mutex<Option<AudioInputFn> > >; MAX_AUDIO_DEVICE_INDEX],
35    pub audio_output_cb: [Arc<Mutex<Option<AudioOutputFn> > >; MAX_AUDIO_DEVICE_INDEX],
36    audio_outputs: Arc<Mutex<Vec<AlsaAudioDeviceRef >> >,
37    audio_inputs: Arc<Mutex<Vec<AlsaAudioDeviceRef >> >,
38    device_descs: Vec<AlsaAudioDesc>,
39    failed_devices: Arc<Mutex<HashSet<AudioDeviceId>>>,
40    change_signal: SignalToUI
41}
42
43#[derive(Debug)]
44pub struct AlsaError(String);
45
46macro_rules!alsa_error {
47    ( $ call: expr) => {
48        AlsaError::from(stringify!( $ call), $ call)
49    }
50}
51
52impl AlsaAudioAccess {
53    pub fn new(change_signal: SignalToUI) -> Arc<Mutex<Self >> {
54        let change_signal_inner = change_signal.clone();
55        std::thread::spawn(move || {
56            let mut last_card_count = 0;
57            loop {
58                let mut card_count = 0;
59                let mut card_num = -1;
60                loop {
61                    unsafe{snd_card_next(&mut card_num);}
62                    if card_num <0 {
63                        break;
64                    }
65                    card_count += 1;
66                }
67                if card_count != last_card_count{
68                    last_card_count = card_count;
69                    change_signal_inner.set();
70                }
71                let _ = std::thread::sleep(std::time::Duration::new(1, 0));
72            }
73        });
74        
75        Arc::new(Mutex::new(
76            AlsaAudioAccess {
77                change_signal,
78                failed_devices: Default::default(),
79                audio_input_cb: Default::default(),
80                audio_output_cb: Default::default(),
81                device_descs: Default::default(),
82                audio_inputs: Default::default(),
83                audio_outputs: Default::default(),
84            }
85        ))
86    }
87    
88    pub fn get_updated_descs(&mut self)-> Vec<AudioDeviceDesc> {
89        // alright lets do it
90        fn inner(alsa:&AlsaAudioAccess) -> Result<Vec<AlsaAudioDesc>, AlsaError> {
91            let mut device_descs = Vec::new();
92            let mut card_num = -1;
93            unsafe {
94                loop {
95                    alsa_error!(snd_card_next(&mut card_num)) ?;
96                    if card_num <0 {
97                        break;
98                    }
99                    
100                    let mut hints: *mut *mut c_void = 0 as *mut _;
101                    alsa_error!(snd_device_name_hint(card_num, "pcm\0".as_ptr(), &mut hints)) ?;
102                    
103                    
104                    let mut index = 0;
105                    while *hints.offset(index) != std::ptr::null_mut() {
106                        let hint_ptr = *hints.offset(index);
107                        let name_str = from_alsa_string(snd_device_name_get_hint(hint_ptr, "NAME\0".as_ptr())).unwrap_or("".into());
108                        let desc_str = from_alsa_string(snd_device_name_get_hint(hint_ptr, "DESC\0".as_ptr())).unwrap_or("".into()).replace("\n", " ");
109                        let ioid = from_alsa_string(snd_device_name_get_hint(hint_ptr, "IOID\0".as_ptr())).unwrap_or("".into());
110                        let device_id = AudioDeviceId(LiveId::from_str(&name_str));
111                        let desc = AudioDeviceDesc {
112                            has_failed: alsa.failed_devices.lock().unwrap().contains(&device_id),
113                            device_id,
114                            device_type: AudioDeviceType::Input,
115                            is_default: false,
116                            channel_count: 2,
117                            name: format!("[ALSA] {}",desc_str)
118                        };
119                        if ioid == "" || ioid == "Input" {
120                            device_descs.push(AlsaAudioDesc {
121                                name: name_str.clone(),
122                                desc: desc.clone()
123                            });
124                        }
125                        if ioid == "" || ioid == "Output" {
126                            device_descs.push(AlsaAudioDesc {
127                                name: name_str,
128                                desc: AudioDeviceDesc {device_type: AudioDeviceType::Output, ..desc}
129                            });
130                        }
131                        index += 1;
132                    }
133                }
134            }
135            Ok(device_descs)
136        }
137        self.device_descs.clear();
138        match inner(self) {
139            Err(e) => {
140                println!("ALSA ERROR {}", e.0)
141            }
142            Ok(mut descs) => {
143                // pick a single default device
144                if let Some(descs) = descs.iter_mut().find( | v | v.desc.device_type.is_output() && v.name.starts_with("plughw:")) {
145                    descs.desc.is_default = true;
146                }
147                else if let Some(descs) = descs.iter_mut().find( | v | v.desc.device_type.is_output() && v.name.starts_with("dmix:")) {
148                    descs.desc.is_default = true;
149                }
150                else if let Some(descs) = descs.iter_mut().find( | v | v.desc.device_type.is_output()) {
151                    descs.desc.is_default = true;
152                }
153                if let Some(descs) = descs.iter_mut().find( | v | v.desc.device_type.is_input() && v.name.starts_with("plughw:")) {
154                    descs.desc.is_default = true;
155                }
156                else if let Some(descs) = descs.iter_mut().find( | v | v.desc.device_type.is_input() && v.name.starts_with("dmix:")) {
157                    descs.desc.is_default = true;
158                }
159                else if let Some(descs) = descs.iter_mut().find( | v | v.desc.device_type.is_input()) {
160                    descs.desc.is_default = true;
161                }
162                
163                self.device_descs = descs;
164            }
165        }
166        let mut out = Vec::new();
167        for dev in &self.device_descs {
168            out.push(dev.desc.clone());
169        }
170        out
171    }
172    
173    
174    pub fn use_audio_inputs(&mut self, devices: &[AudioDeviceId]) {
175        let new = {
176            let mut audio_inputs = self.audio_inputs.lock().unwrap();
177            // lets shut down the ones we dont use
178            audio_inputs.iter_mut().for_each( | v | {
179                if !devices.contains(&v.device_id) {
180                    v.is_terminated = true;
181                }
182            });
183            // create the new ones
184            let mut new = Vec::new();
185            for (index, device_id) in devices.iter().enumerate() {
186                if audio_inputs.iter().find( | v | v.device_id == *device_id).is_none() {
187                    if let Some(v) = self.device_descs.iter().find( | v | v.desc.device_id == *device_id){
188                        new.push((index, *device_id, v.name.clone()))
189                    }
190                }
191            }
192            new
193        };
194        for (index, device_id, name) in new {
195            let audio_input_cb = self.audio_input_cb[index].clone();
196            let audio_inputs = self.audio_inputs.clone();
197            let failed_devices = self.failed_devices.clone();
198            let change_signal = self.change_signal.clone();
199            std::thread::spawn(move || {
200                if let Ok((mut device, device_ref)) = AlsaAudioDevice::new(&name, device_id, SND_PCM_STREAM_CAPTURE){
201                    audio_inputs.lock().unwrap().push(device_ref);
202                    let mut audio_buffer = device.allocate_matching_buffer();
203                    loop {
204                        if audio_inputs.lock().unwrap().iter().find( | v | v.device_id == device_id && v.is_terminated).is_some() {
205                            break;
206                        }
207                        match device.read_input_buffer(&mut audio_buffer) {
208                            Err(e) => {
209                                println!("Write output buffer error {}", e.0);
210                                break;
211                            }
212                            Ok(_) => ()
213                        }
214                        if let Some(fbox) = &mut *audio_input_cb.lock().unwrap() {
215                            fbox(
216                                AudioInfo {
217                                    device_id,
218                                    time: None,
219                                },
220                                &audio_buffer
221                            );
222                        }
223                    }
224                    let mut audio_inputs = audio_inputs.lock().unwrap();
225                    audio_inputs.retain( | v | v.device_id != device_id);
226                }
227                else{
228                    println!("Failed to open ALSA audio device, trying something else");
229                    failed_devices.lock().unwrap().insert(device_id);
230                    change_signal.set();
231                }
232            });
233        }
234    }
235    
236    pub fn use_audio_outputs(&mut self, devices: &[AudioDeviceId]) {
237        let new = {
238            let mut audio_outputs = self.audio_outputs.lock().unwrap();
239            // lets shut down the ones we dont use
240            audio_outputs.iter_mut().for_each( | v | {
241                if !devices.contains(&v.device_id) {
242                    v.is_terminated = true;
243                }
244            });
245            // create the new ones
246            let mut new = Vec::new();
247            for (index, device_id) in devices.iter().enumerate() {
248                if audio_outputs.iter().find( | v | v.device_id == *device_id).is_none() {
249                    if let Some(v) = self.device_descs.iter().find( | v | v.desc.device_id == *device_id){
250                        new.push((index, *device_id, v.name.clone()))
251                    }
252                }
253            }
254            new
255            
256        };
257        for (index, device_id, name) in new {
258            let audio_output_cb = self.audio_output_cb[index].clone();
259            let audio_outputs = self.audio_outputs.clone();
260            let failed_devices = self.failed_devices.clone();
261            let change_signal = self.change_signal.clone();
262            std::thread::spawn(move || {
263                // this thing fails here. so how would we then drop down to a secondary
264                // we could simply switch default
265                if let Ok((mut device, device_ref)) = AlsaAudioDevice::new(&name, device_id, SND_PCM_STREAM_PLAYBACK){
266                    audio_outputs.lock().unwrap().push(device_ref);
267                    // lets allocate an output buffer
268                    let mut audio_buffer = device.allocate_matching_buffer();
269                    loop {
270                        if audio_outputs.lock().unwrap().iter().find( | v | v.device_id == device_id && v.is_terminated).is_some() {
271                            break;
272                        }
273                        if let Some(fbox) = &mut *audio_output_cb.lock().unwrap() {
274                            fbox(
275                                AudioInfo {
276                                    device_id,
277                                    time: None,
278                                },
279                                &mut audio_buffer
280                            );
281                        }
282                        match device.write_output_buffer(&audio_buffer) {
283                            Err(e) => {
284                                println!("Write output buffer error {}", e.0);
285                                break;
286                            }
287                            Ok(_) => ()
288                        }
289                    }
290                    audio_outputs.lock().unwrap().retain( | v | v.device_id != device_id);
291                }
292                else{
293                    println!("Failed to open ALSA audio device, trying something else");
294                    failed_devices.lock().unwrap().insert(device_id);
295                    change_signal.set();
296                }
297            });
298        }
299    }
300}
301
302
303impl AlsaAudioDevice {
304    fn new(device_name: &str, device_id: AudioDeviceId, direction: snd_pcm_stream_t) -> Result<(AlsaAudioDevice, AlsaAudioDeviceRef),
305        AlsaError> {
306        unsafe {
307            let mut handle: *mut snd_pcm_t = 0 as *mut _;
308            let mut hw_params: *mut snd_pcm_hw_params_t = 0 as *mut _;
309            let name0 = format!("{}\0", device_name);
310            let mut rate = 48000;
311            alsa_error!(snd_pcm_open(&mut handle, name0.as_ptr(), direction, 0)) ?;
312            alsa_error!(snd_pcm_hw_params_malloc(&mut hw_params)) ?;
313            alsa_error!(snd_pcm_hw_params_any(handle, hw_params)) ?;
314            alsa_error!(snd_pcm_hw_params_set_access(handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) ?;
315            alsa_error!(snd_pcm_hw_params_set_format(handle, hw_params, SND_PCM_FORMAT_FLOAT_LE)) ?;
316            alsa_error!(snd_pcm_hw_params_set_rate_near(handle, hw_params, &mut rate, 0 as *mut _)) ?;
317            alsa_error!(snd_pcm_hw_params_set_channels(handle, hw_params, 2)) ?;
318            let mut periods = 2;
319            let mut dir = 0;
320            alsa_error!(snd_pcm_hw_params_set_periods_near(handle, hw_params, &mut periods, &mut dir)) ?;
321            let mut buffer_size = 512;
322            alsa_error!(snd_pcm_hw_params_set_buffer_size_near(handle, hw_params, &mut buffer_size)) ?;
323            alsa_error!(snd_pcm_hw_params(handle, hw_params)) ?;
324            alsa_error!(snd_pcm_hw_params_set_rate_resample(handle, hw_params, 1)) ?;
325            let mut buffer_size = 0;
326            alsa_error!(snd_pcm_hw_params_get_buffer_size(hw_params, &mut buffer_size)) ?;
327            let mut channel_count = 0;
328            alsa_error!(snd_pcm_hw_params_get_channels(hw_params, &mut channel_count)) ?;
329            let mut frame_count = 0;
330            alsa_error!(snd_pcm_hw_params_get_period_size(hw_params, &mut frame_count, 0 as *mut _)) ?;
331            snd_pcm_hw_params_free(hw_params);
332            
333            // alright device is prepared.
334            Ok((Self {
335                interleaved: {let mut n = Vec::new(); n.resize(frame_count as usize * channel_count as usize, 0.0); n},
336                device_handle: handle,
337                channel_count: channel_count as usize,
338                frame_count: frame_count as usize,
339                _buffer_size: buffer_size as usize,
340            }, AlsaAudioDeviceRef {
341                device_id,
342                is_terminated: false,
343            }))
344        }
345    }
346    
347    fn allocate_matching_buffer(&self) -> AudioBuffer {
348        AudioBuffer::new_with_size(self.frame_count, self.channel_count)
349    }
350    
351    fn write_output_buffer(&mut self, buffer: &AudioBuffer) -> Result<i32, AlsaError> {
352        unsafe {
353            // interleave the audio buffer
354            buffer.copy_to_interleaved(&mut self.interleaved);
355            let result = snd_pcm_writei(self.device_handle, self.interleaved.as_ptr() as *mut _, self.frame_count as _);
356            if result == -libc_sys::EPIPE as _ {
357                snd_pcm_prepare(self.device_handle);
358                return Ok(0)
359            }
360            //println!("buffer {:?}", buffer.data.as_ptr());
361            AlsaError::from("snd_pcm_writei", result as _)
362        }
363    }
364    
365    fn read_input_buffer(&mut self, buffer: &mut AudioBuffer) -> Result<i32, AlsaError> {
366        unsafe {
367            // interleave the audio buffer
368            let result = snd_pcm_readi(self.device_handle, self.interleaved.as_ptr() as *mut _, self.frame_count as _);
369            if result == -libc_sys::EPIPE as _ {
370                snd_pcm_prepare(self.device_handle);
371                return Ok(0)
372            }
373            buffer.copy_from_interleaved(self.channel_count, &self.interleaved);
374            //println!("buffer {:?}", buffer.data.as_ptr());
375            AlsaError::from("snd_pcm_writei", result as _)
376        }
377    }
378}
379
380
381impl AlsaError {
382    pub fn from(prefix: &str, err: i32) -> Result<i32, Self> {
383        if err < 0 {
384            Err(AlsaError(format!("{} - {}", prefix, unsafe {CStr::from_ptr(snd_strerror(err)).to_str().unwrap().to_string()})))
385        }
386        else {
387            Ok(err)
388        }
389    }
390}
391
392fn from_alsa_string(s: *mut c_char) -> Option<String> {
393    if s.is_null() {return None};
394    unsafe {
395        let c = CStr::from_ptr(s).to_str().unwrap().to_string();
396        libc_sys::free(s as *mut c_void);
397        Some(c)
398    }
399}
400