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