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