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