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