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