makepad_platform/os/linux/
alsa_audio.rs1use {
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 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 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 audio_inputs.iter_mut().for_each( | v | {
179 if !devices.contains(&v.device_id) {
180 v.is_terminated = true;
181 }
182 });
183 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 audio_outputs.iter_mut().for_each( | v | {
241 if !devices.contains(&v.device_id) {
242 v.is_terminated = true;
243 }
244 });
245 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 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 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 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 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 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 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 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