1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
4 
5   This software is provided 'as-is', without any express or implied
6   warranty.  In no event will the authors be held liable for any damages
7   arising from the use of this software.
8 
9   Permission is granted to anyone to use this software for any purpose,
10   including commercial applications, and to alter it and redistribute it
11   freely, subject to the following restrictions:
12 
13   1. The origin of this software must not be misrepresented; you must not
14      claim that you wrote the original software. If you use this software
15      in a product, an acknowledgment in the product documentation would be
16      appreciated but is not required.
17   2. Altered source versions must be plainly marked as such, and must not be
18      misrepresented as being the original software.
19   3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #if SDL_AUDIO_DRIVER_ALSA
24 
25 #ifndef SDL_ALSA_NON_BLOCKING
26 #define SDL_ALSA_NON_BLOCKING 0
27 #endif
28 
29 /* Allow access to a raw mixing buffer */
30 
31 #include <sys/types.h>
32 #include <signal.h>             /* For kill() */
33 #include <string.h>
34 
35 #include "SDL_assert.h"
36 #include "SDL_timer.h"
37 #include "SDL_audio.h"
38 #include "../SDL_audio_c.h"
39 #include "SDL_alsa_audio.h"
40 
41 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
42 #include "SDL_loadso.h"
43 #endif
44 
45 static int (*ALSA_snd_pcm_open)
46   (snd_pcm_t **, const char *, snd_pcm_stream_t, int);
47 static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm);
48 static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)
49   (snd_pcm_t *, const void *, snd_pcm_uframes_t);
50 static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)
51   (snd_pcm_t *, void *, snd_pcm_uframes_t);
52 static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int);
53 static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *);
54 static int (*ALSA_snd_pcm_drain) (snd_pcm_t *);
55 static const char *(*ALSA_snd_strerror) (int);
56 static size_t(*ALSA_snd_pcm_hw_params_sizeof) (void);
57 static size_t(*ALSA_snd_pcm_sw_params_sizeof) (void);
58 static void (*ALSA_snd_pcm_hw_params_copy)
59   (snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
60 static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *);
61 static int (*ALSA_snd_pcm_hw_params_set_access)
62   (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
63 static int (*ALSA_snd_pcm_hw_params_set_format)
64   (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
65 static int (*ALSA_snd_pcm_hw_params_set_channels)
66   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
67 static int (*ALSA_snd_pcm_hw_params_get_channels)
68   (const snd_pcm_hw_params_t *, unsigned int *);
69 static int (*ALSA_snd_pcm_hw_params_set_rate_near)
70   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
71 static int (*ALSA_snd_pcm_hw_params_set_period_size_near)
72   (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
73 static int (*ALSA_snd_pcm_hw_params_get_period_size)
74   (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
75 static int (*ALSA_snd_pcm_hw_params_set_periods_min)
76   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
77 static int (*ALSA_snd_pcm_hw_params_set_periods_first)
78   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
79 static int (*ALSA_snd_pcm_hw_params_get_periods)
80   (const snd_pcm_hw_params_t *, unsigned int *, int *);
81 static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)
82   (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
83 static int (*ALSA_snd_pcm_hw_params_get_buffer_size)
84   (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
85 static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *);
86 static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *,
87                                               snd_pcm_sw_params_t *);
88 static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
89   (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
90 static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *);
91 static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
92 static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
93 static int (*ALSA_snd_pcm_sw_params_set_avail_min)
94   (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
95 static int (*ALSA_snd_pcm_reset)(snd_pcm_t *);
96 static int (*ALSA_snd_device_name_hint) (int, const char *, void ***);
97 static char* (*ALSA_snd_device_name_get_hint) (const void *, const char *);
98 static int (*ALSA_snd_device_name_free_hint) (void **);
99 static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
100 #ifdef SND_CHMAP_API_VERSION
101 static snd_pcm_chmap_t* (*ALSA_snd_pcm_get_chmap) (snd_pcm_t *);
102 static int (*ALSA_snd_pcm_chmap_print) (const snd_pcm_chmap_t *map, size_t maxlen, char *buf);
103 #endif
104 
105 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
106 #define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
107 #define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
108 
109 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
110 static void *alsa_handle = NULL;
111 
112 static int
load_alsa_sym(const char * fn,void ** addr)113 load_alsa_sym(const char *fn, void **addr)
114 {
115     *addr = SDL_LoadFunction(alsa_handle, fn);
116     if (*addr == NULL) {
117         /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
118         return 0;
119     }
120 
121     return 1;
122 }
123 
124 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
125 #define SDL_ALSA_SYM(x) \
126     if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1
127 #else
128 #define SDL_ALSA_SYM(x) ALSA_##x = x
129 #endif
130 
131 static int
load_alsa_syms(void)132 load_alsa_syms(void)
133 {
134     SDL_ALSA_SYM(snd_pcm_open);
135     SDL_ALSA_SYM(snd_pcm_close);
136     SDL_ALSA_SYM(snd_pcm_writei);
137     SDL_ALSA_SYM(snd_pcm_readi);
138     SDL_ALSA_SYM(snd_pcm_recover);
139     SDL_ALSA_SYM(snd_pcm_prepare);
140     SDL_ALSA_SYM(snd_pcm_drain);
141     SDL_ALSA_SYM(snd_strerror);
142     SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
143     SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
144     SDL_ALSA_SYM(snd_pcm_hw_params_copy);
145     SDL_ALSA_SYM(snd_pcm_hw_params_any);
146     SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
147     SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
148     SDL_ALSA_SYM(snd_pcm_hw_params_set_channels);
149     SDL_ALSA_SYM(snd_pcm_hw_params_get_channels);
150     SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
151     SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
152     SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
153     SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_min);
154     SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_first);
155     SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
156     SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
157     SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
158     SDL_ALSA_SYM(snd_pcm_hw_params);
159     SDL_ALSA_SYM(snd_pcm_sw_params_current);
160     SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
161     SDL_ALSA_SYM(snd_pcm_sw_params);
162     SDL_ALSA_SYM(snd_pcm_nonblock);
163     SDL_ALSA_SYM(snd_pcm_wait);
164     SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
165     SDL_ALSA_SYM(snd_pcm_reset);
166     SDL_ALSA_SYM(snd_device_name_hint);
167     SDL_ALSA_SYM(snd_device_name_get_hint);
168     SDL_ALSA_SYM(snd_device_name_free_hint);
169     SDL_ALSA_SYM(snd_pcm_avail);
170 #ifdef SND_CHMAP_API_VERSION
171     SDL_ALSA_SYM(snd_pcm_get_chmap);
172     SDL_ALSA_SYM(snd_pcm_chmap_print);
173 #endif
174 
175     return 0;
176 }
177 
178 #undef SDL_ALSA_SYM
179 
180 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
181 
182 static void
UnloadALSALibrary(void)183 UnloadALSALibrary(void)
184 {
185     if (alsa_handle != NULL) {
186         SDL_UnloadObject(alsa_handle);
187         alsa_handle = NULL;
188     }
189 }
190 
191 static int
LoadALSALibrary(void)192 LoadALSALibrary(void)
193 {
194     int retval = 0;
195     if (alsa_handle == NULL) {
196         alsa_handle = SDL_LoadObject(alsa_library);
197         if (alsa_handle == NULL) {
198             retval = -1;
199             /* Don't call SDL_SetError(): SDL_LoadObject already did. */
200         } else {
201             retval = load_alsa_syms();
202             if (retval < 0) {
203                 UnloadALSALibrary();
204             }
205         }
206     }
207     return retval;
208 }
209 
210 #else
211 
212 static void
UnloadALSALibrary(void)213 UnloadALSALibrary(void)
214 {
215 }
216 
217 static int
LoadALSALibrary(void)218 LoadALSALibrary(void)
219 {
220     load_alsa_syms();
221     return 0;
222 }
223 
224 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
225 
226 static const char *
get_audio_device(void * handle,const int channels)227 get_audio_device(void *handle, const int channels)
228 {
229     const char *device;
230 
231     if (handle != NULL) {
232         return (const char *) handle;
233     }
234 
235     /* !!! FIXME: we also check "SDL_AUDIO_DEVICE_NAME" at the higher level. */
236     device = SDL_getenv("AUDIODEV");    /* Is there a standard variable name? */
237     if (device != NULL) {
238         return device;
239     }
240 
241     if (channels == 6) {
242         return "plug:surround51";
243     } else if (channels == 4) {
244         return "plug:surround40";
245     }
246 
247     return "default";
248 }
249 
250 
251 /* This function waits until it is possible to write a full sound buffer */
252 static void
ALSA_WaitDevice(_THIS)253 ALSA_WaitDevice(_THIS)
254 {
255 #if SDL_ALSA_NON_BLOCKING
256     const snd_pcm_sframes_t needed = (snd_pcm_sframes_t) this->spec.samples;
257     while (SDL_AtomicGet(&this->enabled)) {
258         const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(this->hidden->pcm_handle);
259         if ((rc < 0) && (rc != -EAGAIN)) {
260             /* Hmm, not much we can do - abort */
261             fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n",
262                         ALSA_snd_strerror(rc));
263             SDL_OpenedAudioDeviceDisconnected(this);
264             return;
265         } else if (rc < needed) {
266             const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / this->spec.freq;
267             SDL_Delay(SDL_max(delay, 10));
268         } else {
269             break;  /* ready to go! */
270         }
271     }
272 #endif
273 }
274 
275 
276 /* !!! FIXME: is there a channel swizzler in alsalib instead? */
277 /*
278  * http://bugzilla.libsdl.org/show_bug.cgi?id=110
279  * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
280  *  and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
281  */
282 #define SWIZ6(T, buf, numframes) \
283     T *ptr = (T *) buf; \
284     Uint32 i; \
285     for (i = 0; i < numframes; i++, ptr += 6) { \
286         T tmp; \
287         tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
288         tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
289     }
290 
291 static void
swizzle_alsa_channels_6_64bit(void * buffer,Uint32 bufferlen)292 swizzle_alsa_channels_6_64bit(void *buffer, Uint32 bufferlen)
293 {
294     SWIZ6(Uint64, buffer, bufferlen);
295 }
296 
297 static void
swizzle_alsa_channels_6_32bit(void * buffer,Uint32 bufferlen)298 swizzle_alsa_channels_6_32bit(void *buffer, Uint32 bufferlen)
299 {
300     SWIZ6(Uint32, buffer, bufferlen);
301 }
302 
303 static void
swizzle_alsa_channels_6_16bit(void * buffer,Uint32 bufferlen)304 swizzle_alsa_channels_6_16bit(void *buffer, Uint32 bufferlen)
305 {
306     SWIZ6(Uint16, buffer, bufferlen);
307 }
308 
309 static void
swizzle_alsa_channels_6_8bit(void * buffer,Uint32 bufferlen)310 swizzle_alsa_channels_6_8bit(void *buffer, Uint32 bufferlen)
311 {
312     SWIZ6(Uint8, buffer, bufferlen);
313 }
314 
315 #undef SWIZ6
316 
317 
318 /*
319  * Called right before feeding this->hidden->mixbuf to the hardware. Swizzle
320  *  channels from Windows/Mac order to the format alsalib will want.
321  */
322 static void
swizzle_alsa_channels(_THIS,void * buffer,Uint32 bufferlen)323 swizzle_alsa_channels(_THIS, void *buffer, Uint32 bufferlen)
324 {
325     if (this->spec.channels == 6) {
326         switch (SDL_AUDIO_BITSIZE(this->spec.format)) {
327             case 8: swizzle_alsa_channels_6_8bit(buffer, bufferlen); break;
328             case 16: swizzle_alsa_channels_6_16bit(buffer, bufferlen); break;
329             case 32: swizzle_alsa_channels_6_32bit(buffer, bufferlen); break;
330             case 64: swizzle_alsa_channels_6_64bit(buffer, bufferlen); break;
331             default: SDL_assert(!"unhandled bitsize"); break;
332         }
333     }
334 
335     /* !!! FIXME: update this for 7.1 if needed, later. */
336 }
337 
338 #ifdef SND_CHMAP_API_VERSION
339 /* Some devices have the right channel map, no swizzling necessary */
340 static void
no_swizzle(_THIS,void * buffer,Uint32 bufferlen)341 no_swizzle(_THIS, void *buffer, Uint32 bufferlen)
342 {
343 }
344 #endif /* SND_CHMAP_API_VERSION */
345 
346 
347 static void
ALSA_PlayDevice(_THIS)348 ALSA_PlayDevice(_THIS)
349 {
350     const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf;
351     const int frame_size = ((SDL_AUDIO_BITSIZE(this->spec.format)) / 8) *
352                                 this->spec.channels;
353     snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
354 
355     this->hidden->swizzle_func(this, this->hidden->mixbuf, frames_left);
356 
357     while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
358         int status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
359                                          sample_buf, frames_left);
360 
361         if (status < 0) {
362             if (status == -EAGAIN) {
363                 /* Apparently snd_pcm_recover() doesn't handle this case -
364                    does it assume snd_pcm_wait() above? */
365                 SDL_Delay(1);
366                 continue;
367             }
368             status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
369             if (status < 0) {
370                 /* Hmm, not much we can do - abort */
371                 fprintf(stderr, "ALSA write failed (unrecoverable): %s\n",
372                         ALSA_snd_strerror(status));
373                 SDL_OpenedAudioDeviceDisconnected(this);
374                 return;
375             }
376             continue;
377         }
378         else if (status == 0) {
379             /* No frames were written (no available space in pcm device).
380                Allow other threads to catch up. */
381             Uint32 delay = (frames_left / 2 * 1000) / this->spec.freq;
382             SDL_Delay(delay);
383         }
384 
385         sample_buf += status * frame_size;
386         frames_left -= status;
387     }
388 }
389 
390 static Uint8 *
ALSA_GetDeviceBuf(_THIS)391 ALSA_GetDeviceBuf(_THIS)
392 {
393     return (this->hidden->mixbuf);
394 }
395 
396 static int
ALSA_CaptureFromDevice(_THIS,void * buffer,int buflen)397 ALSA_CaptureFromDevice(_THIS, void *buffer, int buflen)
398 {
399     Uint8 *sample_buf = (Uint8 *) buffer;
400     const int frame_size = ((SDL_AUDIO_BITSIZE(this->spec.format)) / 8) *
401                                 this->spec.channels;
402     const int total_frames = buflen / frame_size;
403     snd_pcm_uframes_t frames_left = total_frames;
404     snd_pcm_uframes_t wait_time = frame_size / 2;
405 
406     SDL_assert((buflen % frame_size) == 0);
407 
408     while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
409         int status;
410 
411         status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
412                                         sample_buf, frames_left);
413 
414         if (status == -EAGAIN) {
415             ALSA_snd_pcm_wait(this->hidden->pcm_handle, wait_time);
416             status = 0;
417         }
418         else if (status < 0) {
419             /*printf("ALSA: capture error %d\n", status);*/
420             status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
421             if (status < 0) {
422                 /* Hmm, not much we can do - abort */
423                 fprintf(stderr, "ALSA read failed (unrecoverable): %s\n",
424                         ALSA_snd_strerror(status));
425                 return -1;
426             }
427             continue;
428         }
429 
430         /*printf("ALSA: captured %d bytes\n", status * frame_size);*/
431         sample_buf += status * frame_size;
432         frames_left -= status;
433     }
434 
435     this->hidden->swizzle_func(this, buffer, total_frames - frames_left);
436 
437     return (total_frames - frames_left) * frame_size;
438 }
439 
440 static void
ALSA_FlushCapture(_THIS)441 ALSA_FlushCapture(_THIS)
442 {
443     ALSA_snd_pcm_reset(this->hidden->pcm_handle);
444 }
445 
446 static void
ALSA_CloseDevice(_THIS)447 ALSA_CloseDevice(_THIS)
448 {
449     if (this->hidden->pcm_handle) {
450         /* Wait for the submitted audio to drain
451            ALSA_snd_pcm_drop() can hang, so don't use that.
452          */
453         Uint32 delay = ((this->spec.samples * 1000) / this->spec.freq) * 2;
454         SDL_Delay(delay);
455 
456         ALSA_snd_pcm_close(this->hidden->pcm_handle);
457     }
458     SDL_free(this->hidden->mixbuf);
459     SDL_free(this->hidden);
460 }
461 
462 static int
ALSA_set_buffer_size(_THIS,snd_pcm_hw_params_t * params)463 ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params)
464 {
465     int status;
466     snd_pcm_hw_params_t *hwparams;
467     snd_pcm_uframes_t persize;
468     unsigned int periods;
469 
470     /* Copy the hardware parameters for this setup */
471     snd_pcm_hw_params_alloca(&hwparams);
472     ALSA_snd_pcm_hw_params_copy(hwparams, params);
473 
474     /* Attempt to match the period size to the requested buffer size */
475     persize = this->spec.samples;
476     status = ALSA_snd_pcm_hw_params_set_period_size_near(
477                 this->hidden->pcm_handle, hwparams, &persize, NULL);
478     if ( status < 0 ) {
479         return(-1);
480     }
481 
482     /* Need to at least double buffer */
483     periods = 2;
484     status = ALSA_snd_pcm_hw_params_set_periods_min(
485                 this->hidden->pcm_handle, hwparams, &periods, NULL);
486     if ( status < 0 ) {
487         return(-1);
488     }
489 
490     status = ALSA_snd_pcm_hw_params_set_periods_first(
491                 this->hidden->pcm_handle, hwparams, &periods, NULL);
492     if ( status < 0 ) {
493         return(-1);
494     }
495 
496     /* "set" the hardware with the desired parameters */
497     status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
498     if ( status < 0 ) {
499         return(-1);
500     }
501 
502     this->spec.samples = persize;
503 
504     /* This is useful for debugging */
505     if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
506         snd_pcm_uframes_t bufsize;
507 
508         ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
509 
510         fprintf(stderr,
511             "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
512             persize, periods, bufsize);
513     }
514 
515     return(0);
516 }
517 
518 static int
ALSA_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)519 ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
520 {
521     int status = 0;
522     snd_pcm_t *pcm_handle = NULL;
523     snd_pcm_hw_params_t *hwparams = NULL;
524     snd_pcm_sw_params_t *swparams = NULL;
525     snd_pcm_format_t format = 0;
526     SDL_AudioFormat test_format = 0;
527     unsigned int rate = 0;
528     unsigned int channels = 0;
529 #ifdef SND_CHMAP_API_VERSION
530     snd_pcm_chmap_t *chmap;
531     char chmap_str[64];
532 #endif
533 
534     /* Initialize all variables that we clean on shutdown */
535     this->hidden = (struct SDL_PrivateAudioData *)
536         SDL_malloc((sizeof *this->hidden));
537     if (this->hidden == NULL) {
538         return SDL_OutOfMemory();
539     }
540     SDL_zerop(this->hidden);
541 
542     /* Open the audio device */
543     /* Name of device should depend on # channels in spec */
544     status = ALSA_snd_pcm_open(&pcm_handle,
545                 get_audio_device(handle, this->spec.channels),
546                 iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
547                 SND_PCM_NONBLOCK);
548 
549     if (status < 0) {
550         return SDL_SetError("ALSA: Couldn't open audio device: %s",
551                             ALSA_snd_strerror(status));
552     }
553 
554     this->hidden->pcm_handle = pcm_handle;
555 
556     /* Figure out what the hardware is capable of */
557     snd_pcm_hw_params_alloca(&hwparams);
558     status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
559     if (status < 0) {
560         return SDL_SetError("ALSA: Couldn't get hardware config: %s",
561                             ALSA_snd_strerror(status));
562     }
563 
564     /* SDL only uses interleaved sample output */
565     status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
566                                                SND_PCM_ACCESS_RW_INTERLEAVED);
567     if (status < 0) {
568         return SDL_SetError("ALSA: Couldn't set interleaved access: %s",
569                      ALSA_snd_strerror(status));
570     }
571 
572     /* Try for a closest match on audio format */
573     status = -1;
574     for (test_format = SDL_FirstAudioFormat(this->spec.format);
575          test_format && (status < 0);) {
576         status = 0;             /* if we can't support a format, it'll become -1. */
577         switch (test_format) {
578         case AUDIO_U8:
579             format = SND_PCM_FORMAT_U8;
580             break;
581         case AUDIO_S8:
582             format = SND_PCM_FORMAT_S8;
583             break;
584         case AUDIO_S16LSB:
585             format = SND_PCM_FORMAT_S16_LE;
586             break;
587         case AUDIO_S16MSB:
588             format = SND_PCM_FORMAT_S16_BE;
589             break;
590         case AUDIO_U16LSB:
591             format = SND_PCM_FORMAT_U16_LE;
592             break;
593         case AUDIO_U16MSB:
594             format = SND_PCM_FORMAT_U16_BE;
595             break;
596         case AUDIO_S32LSB:
597             format = SND_PCM_FORMAT_S32_LE;
598             break;
599         case AUDIO_S32MSB:
600             format = SND_PCM_FORMAT_S32_BE;
601             break;
602         case AUDIO_F32LSB:
603             format = SND_PCM_FORMAT_FLOAT_LE;
604             break;
605         case AUDIO_F32MSB:
606             format = SND_PCM_FORMAT_FLOAT_BE;
607             break;
608         default:
609             status = -1;
610             break;
611         }
612         if (status >= 0) {
613             status = ALSA_snd_pcm_hw_params_set_format(pcm_handle,
614                                                        hwparams, format);
615         }
616         if (status < 0) {
617             test_format = SDL_NextAudioFormat();
618         }
619     }
620     if (status < 0) {
621         return SDL_SetError("ALSA: Couldn't find any hardware audio formats");
622     }
623     this->spec.format = test_format;
624 
625     /* Validate number of channels and determine if swizzling is necessary
626      * Assume original swizzling, until proven otherwise.
627      */
628     this->hidden->swizzle_func = swizzle_alsa_channels;
629 #ifdef SND_CHMAP_API_VERSION
630     chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
631     if (chmap) {
632         ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str);
633         if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 ||
634             SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) {
635             this->hidden->swizzle_func = no_swizzle;
636         }
637         free(chmap);
638     }
639 #endif /* SND_CHMAP_API_VERSION */
640 
641     /* Set the number of channels */
642     status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
643                                                  this->spec.channels);
644     channels = this->spec.channels;
645     if (status < 0) {
646         status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
647         if (status < 0) {
648             return SDL_SetError("ALSA: Couldn't set audio channels");
649         }
650         this->spec.channels = channels;
651     }
652 
653     /* Set the audio rate */
654     rate = this->spec.freq;
655     status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
656                                                   &rate, NULL);
657     if (status < 0) {
658         return SDL_SetError("ALSA: Couldn't set audio frequency: %s",
659                             ALSA_snd_strerror(status));
660     }
661     this->spec.freq = rate;
662 
663     /* Set the buffer size, in samples */
664     status = ALSA_set_buffer_size(this, hwparams);
665     if (status < 0) {
666         return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
667     }
668 
669     /* Set the software parameters */
670     snd_pcm_sw_params_alloca(&swparams);
671     status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
672     if (status < 0) {
673         return SDL_SetError("ALSA: Couldn't get software config: %s",
674                             ALSA_snd_strerror(status));
675     }
676     status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, this->spec.samples);
677     if (status < 0) {
678         return SDL_SetError("Couldn't set minimum available samples: %s",
679                             ALSA_snd_strerror(status));
680     }
681     status =
682         ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
683     if (status < 0) {
684         return SDL_SetError("ALSA: Couldn't set start threshold: %s",
685                             ALSA_snd_strerror(status));
686     }
687     status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
688     if (status < 0) {
689         return SDL_SetError("Couldn't set software audio parameters: %s",
690                             ALSA_snd_strerror(status));
691     }
692 
693     /* Calculate the final parameters for this audio specification */
694     SDL_CalculateAudioSpec(&this->spec);
695 
696     /* Allocate mixing buffer */
697     if (!iscapture) {
698         this->hidden->mixlen = this->spec.size;
699         this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
700         if (this->hidden->mixbuf == NULL) {
701             return SDL_OutOfMemory();
702         }
703         SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
704     }
705 
706     #if !SDL_ALSA_NON_BLOCKING
707     if (!iscapture) {
708         ALSA_snd_pcm_nonblock(pcm_handle, 0);
709     }
710     #endif
711 
712     /* We're ready to rock and roll. :-) */
713     return 0;
714 }
715 
716 typedef struct ALSA_Device
717 {
718     char *name;
719     SDL_bool iscapture;
720     struct ALSA_Device *next;
721 } ALSA_Device;
722 
723 static void
add_device(const int iscapture,const char * name,void * hint,ALSA_Device ** pSeen)724 add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
725 {
726     ALSA_Device *dev = SDL_malloc(sizeof (ALSA_Device));
727     char *desc;
728     char *handle = NULL;
729     char *ptr;
730 
731     if (!dev) {
732         return;
733     }
734 
735     /* Not all alsa devices are enumerable via snd_device_name_get_hint
736        (i.e. bluetooth devices).  Therefore if hint is passed in to this
737        function as  NULL, assume name contains desc.
738        Make sure not to free the storage associated with desc in this case */
739     if (hint) {
740         desc = ALSA_snd_device_name_get_hint(hint, "DESC");
741         if (!desc) {
742             SDL_free(dev);
743             return;
744         }
745     } else {
746         desc = (char *) name;
747     }
748 
749     SDL_assert(name != NULL);
750 
751     /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output".
752        just chop the extra lines off, this seems to get a reasonable device
753        name without extra details. */
754     if ((ptr = strchr(desc, '\n')) != NULL) {
755         *ptr = '\0';
756     }
757 
758     /*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/
759 
760     handle = SDL_strdup(name);
761     if (!handle) {
762         if (hint) {
763             free(desc);
764         }
765         SDL_free(dev);
766         return;
767     }
768 
769     SDL_AddAudioDevice(iscapture, desc, handle);
770     if (hint)
771         free(desc);
772     dev->name = handle;
773     dev->iscapture = iscapture;
774     dev->next = *pSeen;
775     *pSeen = dev;
776 }
777 
778 
779 static SDL_atomic_t ALSA_hotplug_shutdown;
780 static SDL_Thread *ALSA_hotplug_thread;
781 
782 static int SDLCALL
ALSA_HotplugThread(void * arg)783 ALSA_HotplugThread(void *arg)
784 {
785     SDL_sem *first_run_semaphore = (SDL_sem *) arg;
786     ALSA_Device *devices = NULL;
787     ALSA_Device *next;
788     ALSA_Device *dev;
789     Uint32 ticks;
790 
791     SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
792 
793     while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
794         void **hints = NULL;
795         ALSA_Device *unseen;
796         ALSA_Device *seen;
797         ALSA_Device *prev;
798 
799         if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
800             int i, j;
801             const char *match = NULL;
802             int bestmatch = 0xFFFF;
803             size_t match_len = 0;
804             int defaultdev = -1;
805             static const char * const prefixes[] = {
806                 "hw:", "sysdefault:", "default:", NULL
807             };
808 
809             unseen = devices;
810             seen = NULL;
811             /* Apparently there are several different ways that ALSA lists
812                actual hardware. It could be prefixed with "hw:" or "default:"
813                or "sysdefault:" and maybe others. Go through the list and see
814                if we can find a preferred prefix for the system. */
815             for (i = 0; hints[i]; i++) {
816                 char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
817                 if (!name) {
818                     continue;
819                 }
820 
821                 /* full name, not a prefix */
822                 if ((defaultdev == -1) && (SDL_strcmp(name, "default") == 0)) {
823                     defaultdev = i;
824                 }
825 
826                 for (j = 0; prefixes[j]; j++) {
827                     const char *prefix = prefixes[j];
828                     const size_t prefixlen = SDL_strlen(prefix);
829                     if (SDL_strncmp(name, prefix, prefixlen) == 0) {
830                         if (j < bestmatch) {
831                             bestmatch = j;
832                             match = prefix;
833                             match_len = prefixlen;
834                         }
835                     }
836                 }
837 
838                 free(name);
839             }
840 
841             /* look through the list of device names to find matches */
842             for (i = 0; hints[i]; i++) {
843                 char *name;
844 
845                 /* if we didn't find a device name prefix we like at all... */
846                 if ((!match) && (defaultdev != i)) {
847                     continue;  /* ...skip anything that isn't the default device. */
848                 }
849 
850                 name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
851                 if (!name) {
852                     continue;
853                 }
854 
855                 /* only want physical hardware interfaces */
856                 if (!match || (SDL_strncmp(name, match, match_len) == 0)) {
857                     char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
858                     const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
859                     const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
860                     SDL_bool have_output = SDL_FALSE;
861                     SDL_bool have_input = SDL_FALSE;
862 
863                     free(ioid);
864 
865                     if (!isoutput && !isinput) {
866                         free(name);
867                         continue;
868                     }
869 
870                     prev = NULL;
871                     for (dev = unseen; dev; dev = next) {
872                         next = dev->next;
873                         if ( (SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) {
874                             if (prev) {
875                                 prev->next = next;
876                             } else {
877                                 unseen = next;
878                             }
879                             dev->next = seen;
880                             seen = dev;
881                             if (isinput) have_input = SDL_TRUE;
882                             if (isoutput) have_output = SDL_TRUE;
883                         } else {
884                             prev = dev;
885                         }
886                     }
887 
888                     if (isinput && !have_input) {
889                         add_device(SDL_TRUE, name, hints[i], &seen);
890                     }
891                     if (isoutput && !have_output) {
892                         add_device(SDL_FALSE, name, hints[i], &seen);
893                     }
894                 }
895 
896                 free(name);
897             }
898 
899             ALSA_snd_device_name_free_hint(hints);
900 
901             devices = seen;   /* now we have a known-good list of attached devices. */
902 
903             /* report anything still in unseen as removed. */
904             for (dev = unseen; dev; dev = next) {
905                 /*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
906                 next = dev->next;
907                 SDL_RemoveAudioDevice(dev->iscapture, dev->name);
908                 SDL_free(dev->name);
909                 SDL_free(dev);
910             }
911         }
912 
913         /* On first run, tell ALSA_DetectDevices() that we have a complete device list so it can return. */
914         if (first_run_semaphore) {
915             SDL_SemPost(first_run_semaphore);
916             first_run_semaphore = NULL;  /* let other thread clean it up. */
917         }
918 
919         /* Block awhile before checking again, unless we're told to stop. */
920         ticks = SDL_GetTicks() + 5000;
921         while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) {
922             SDL_Delay(100);
923         }
924     }
925 
926     /* Shutting down! Clean up any data we've gathered. */
927     for (dev = devices; dev; dev = next) {
928         /*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
929         next = dev->next;
930         SDL_free(dev->name);
931         SDL_free(dev);
932     }
933 
934     return 0;
935 }
936 
937 static void
ALSA_DetectDevices(void)938 ALSA_DetectDevices(void)
939 {
940     /* Start the device detection thread here, wait for an initial iteration to complete. */
941     SDL_sem *semaphore = SDL_CreateSemaphore(0);
942     if (!semaphore) {
943         return;  /* oh well. */
944     }
945 
946     SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
947 
948     ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore);
949     if (ALSA_hotplug_thread) {
950         SDL_SemWait(semaphore);  /* wait for the first iteration to finish. */
951     }
952 
953     SDL_DestroySemaphore(semaphore);
954 }
955 
956 static void
ALSA_Deinitialize(void)957 ALSA_Deinitialize(void)
958 {
959     if (ALSA_hotplug_thread != NULL) {
960         SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
961         SDL_WaitThread(ALSA_hotplug_thread, NULL);
962         ALSA_hotplug_thread = NULL;
963     }
964 
965     UnloadALSALibrary();
966 }
967 
968 static int
ALSA_Init(SDL_AudioDriverImpl * impl)969 ALSA_Init(SDL_AudioDriverImpl * impl)
970 {
971     if (LoadALSALibrary() < 0) {
972         return 0;
973     }
974 
975     /* Set the function pointers */
976     impl->DetectDevices = ALSA_DetectDevices;
977     impl->OpenDevice = ALSA_OpenDevice;
978     impl->WaitDevice = ALSA_WaitDevice;
979     impl->GetDeviceBuf = ALSA_GetDeviceBuf;
980     impl->PlayDevice = ALSA_PlayDevice;
981     impl->CloseDevice = ALSA_CloseDevice;
982     impl->Deinitialize = ALSA_Deinitialize;
983     impl->CaptureFromDevice = ALSA_CaptureFromDevice;
984     impl->FlushCapture = ALSA_FlushCapture;
985 
986     impl->HasCaptureSupport = SDL_TRUE;
987 
988     return 1;   /* this audio target is available. */
989 }
990 
991 
992 AudioBootStrap ALSA_bootstrap = {
993     "alsa", "ALSA PCM audio", ALSA_Init, 0
994 };
995 
996 #endif /* SDL_AUDIO_DRIVER_ALSA */
997 
998 /* vi: set ts=4 sw=4 expandtab: */
999