1 /*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 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
45 static int (*ALSA_snd_pcm_open)
46 (snd_pcm_t **, const char *, snd_pcm_stream_t, int);
47 static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm);
48 static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)
49 (snd_pcm_t *, const void *, snd_pcm_uframes_t);
50 static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)
51 (snd_pcm_t *, void *, snd_pcm_uframes_t);
52 static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int);
53 static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *);
54 static int (*ALSA_snd_pcm_drain) (snd_pcm_t *);
55 static const char *(*ALSA_snd_strerror) (int);
56 static size_t(*ALSA_snd_pcm_hw_params_sizeof) (void);
57 static size_t(*ALSA_snd_pcm_sw_params_sizeof) (void);
58 static void (*ALSA_snd_pcm_hw_params_copy)
59 (snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
60 static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *);
61 static int (*ALSA_snd_pcm_hw_params_set_access)
62 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
63 static int (*ALSA_snd_pcm_hw_params_set_format)
64 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
65 static int (*ALSA_snd_pcm_hw_params_set_channels)
66 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
67 static int (*ALSA_snd_pcm_hw_params_get_channels)
68 (const snd_pcm_hw_params_t *, unsigned int *);
69 static int (*ALSA_snd_pcm_hw_params_set_rate_near)
70 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
71 static int (*ALSA_snd_pcm_hw_params_set_period_size_near)
72 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
73 static int (*ALSA_snd_pcm_hw_params_get_period_size)
74 (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
75 static int (*ALSA_snd_pcm_hw_params_set_periods_min)
76 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
77 static int (*ALSA_snd_pcm_hw_params_set_periods_first)
78 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
79 static int (*ALSA_snd_pcm_hw_params_get_periods)
80 (const snd_pcm_hw_params_t *, unsigned int *, int *);
81 static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)
82 (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
83 static int (*ALSA_snd_pcm_hw_params_get_buffer_size)
84 (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
85 static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *);
86 static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *,
87 snd_pcm_sw_params_t *);
88 static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
89 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
90 static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *);
91 static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
92 static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
93 static int (*ALSA_snd_pcm_sw_params_set_avail_min)
94 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
95 static int (*ALSA_snd_pcm_reset)(snd_pcm_t *);
96 static int (*ALSA_snd_device_name_hint) (int, const char *, void ***);
97 static char* (*ALSA_snd_device_name_get_hint) (const void *, const char *);
98 static int (*ALSA_snd_device_name_free_hint) (void **);
99 static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *);
100 #ifdef SND_CHMAP_API_VERSION
101 static snd_pcm_chmap_t* (*ALSA_snd_pcm_get_chmap) (snd_pcm_t *);
102 static 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
109 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
110 static void *alsa_handle = NULL;
111
112 static int
load_alsa_sym(const char * fn,void ** addr)113 load_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
131 static int
load_alsa_syms(void)132 load_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
182 static void
UnloadALSALibrary(void)183 UnloadALSALibrary(void)
184 {
185 if (alsa_handle != NULL) {
186 SDL_UnloadObject(alsa_handle);
187 alsa_handle = NULL;
188 }
189 }
190
191 static int
LoadALSALibrary(void)192 LoadALSALibrary(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
212 static void
UnloadALSALibrary(void)213 UnloadALSALibrary(void)
214 {
215 }
216
217 static int
LoadALSALibrary(void)218 LoadALSALibrary(void)
219 {
220 load_alsa_syms();
221 return 0;
222 }
223
224 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
225
226 static const char *
get_audio_device(void * handle,const int channels)227 get_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 */
252 static void
ALSA_WaitDevice(_THIS)253 ALSA_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));
263 SDL_OpenedAudioDeviceDisconnected(this);
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
291 static void
swizzle_alsa_channels_6_64bit(void * buffer,Uint32 bufferlen)292 swizzle_alsa_channels_6_64bit(void *buffer, Uint32 bufferlen)
293 {
294 SWIZ6(Uint64, buffer, bufferlen);
295 }
296
297 static void
swizzle_alsa_channels_6_32bit(void * buffer,Uint32 bufferlen)298 swizzle_alsa_channels_6_32bit(void *buffer, Uint32 bufferlen)
299 {
300 SWIZ6(Uint32, buffer, bufferlen);
301 }
302
303 static void
swizzle_alsa_channels_6_16bit(void * buffer,Uint32 bufferlen)304 swizzle_alsa_channels_6_16bit(void *buffer, Uint32 bufferlen)
305 {
306 SWIZ6(Uint16, buffer, bufferlen);
307 }
308
309 static void
swizzle_alsa_channels_6_8bit(void * buffer,Uint32 bufferlen)310 swizzle_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 */
322 static void
swizzle_alsa_channels(_THIS,void * buffer,Uint32 bufferlen)323 swizzle_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 */
340 static void
no_swizzle(_THIS,void * buffer,Uint32 bufferlen)341 no_swizzle(_THIS, void *buffer, Uint32 bufferlen)
342 {
343 }
344 #endif /* SND_CHMAP_API_VERSION */
345
346
347 static void
ALSA_PlayDevice(_THIS)348 ALSA_PlayDevice(_THIS)
349 {
350 const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf;
351 const int frame_size = ((SDL_AUDIO_BITSIZE(this->spec.format)) / 8) *
352 this->spec.channels;
353 snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
354
355 this->hidden->swizzle_func(this, this->hidden->mixbuf, frames_left);
356
357 while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
358 int status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
359 sample_buf, frames_left);
360
361 if (status < 0) {
362 if (status == -EAGAIN) {
363 /* Apparently snd_pcm_recover() doesn't handle this case -
364 does it assume snd_pcm_wait() above? */
365 SDL_Delay(1);
366 continue;
367 }
368 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
369 if (status < 0) {
370 /* Hmm, not much we can do - abort */
371 fprintf(stderr, "ALSA write failed (unrecoverable): %s\n",
372 ALSA_snd_strerror(status));
373 SDL_OpenedAudioDeviceDisconnected(this);
374 return;
375 }
376 continue;
377 }
378 else if (status == 0) {
379 /* No frames were written (no available space in pcm device).
380 Allow other threads to catch up. */
381 Uint32 delay = (frames_left / 2 * 1000) / this->spec.freq;
382 SDL_Delay(delay);
383 }
384
385 sample_buf += status * frame_size;
386 frames_left -= status;
387 }
388 }
389
390 static Uint8 *
ALSA_GetDeviceBuf(_THIS)391 ALSA_GetDeviceBuf(_THIS)
392 {
393 return (this->hidden->mixbuf);
394 }
395
396 static int
ALSA_CaptureFromDevice(_THIS,void * buffer,int buflen)397 ALSA_CaptureFromDevice(_THIS, void *buffer, int buflen)
398 {
399 Uint8 *sample_buf = (Uint8 *) buffer;
400 const int frame_size = ((SDL_AUDIO_BITSIZE(this->spec.format)) / 8) *
401 this->spec.channels;
402 const int total_frames = buflen / frame_size;
403 snd_pcm_uframes_t frames_left = total_frames;
404 snd_pcm_uframes_t wait_time = frame_size / 2;
405
406 SDL_assert((buflen % frame_size) == 0);
407
408 while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) {
409 int status;
410
411 status = ALSA_snd_pcm_readi(this->hidden->pcm_handle,
412 sample_buf, frames_left);
413
414 if (status == -EAGAIN) {
415 ALSA_snd_pcm_wait(this->hidden->pcm_handle, wait_time);
416 status = 0;
417 }
418 else if (status < 0) {
419 /*printf("ALSA: capture error %d\n", status);*/
420 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
421 if (status < 0) {
422 /* Hmm, not much we can do - abort */
423 fprintf(stderr, "ALSA read failed (unrecoverable): %s\n",
424 ALSA_snd_strerror(status));
425 return -1;
426 }
427 continue;
428 }
429
430 /*printf("ALSA: captured %d bytes\n", status * frame_size);*/
431 sample_buf += status * frame_size;
432 frames_left -= status;
433 }
434
435 this->hidden->swizzle_func(this, buffer, total_frames - frames_left);
436
437 return (total_frames - frames_left) * frame_size;
438 }
439
440 static void
ALSA_FlushCapture(_THIS)441 ALSA_FlushCapture(_THIS)
442 {
443 ALSA_snd_pcm_reset(this->hidden->pcm_handle);
444 }
445
446 static void
ALSA_CloseDevice(_THIS)447 ALSA_CloseDevice(_THIS)
448 {
449 if (this->hidden->pcm_handle) {
450 /* Wait for the submitted audio to drain
451 ALSA_snd_pcm_drop() can hang, so don't use that.
452 */
453 Uint32 delay = ((this->spec.samples * 1000) / this->spec.freq) * 2;
454 SDL_Delay(delay);
455
456 ALSA_snd_pcm_close(this->hidden->pcm_handle);
457 }
458 SDL_free(this->hidden->mixbuf);
459 SDL_free(this->hidden);
460 }
461
462 static int
ALSA_set_buffer_size(_THIS,snd_pcm_hw_params_t * params)463 ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params)
464 {
465 int status;
466 snd_pcm_hw_params_t *hwparams;
467 snd_pcm_uframes_t persize;
468 unsigned int periods;
469
470 /* Copy the hardware parameters for this setup */
471 snd_pcm_hw_params_alloca(&hwparams);
472 ALSA_snd_pcm_hw_params_copy(hwparams, params);
473
474 /* Attempt to match the period size to the requested buffer size */
475 persize = this->spec.samples;
476 status = ALSA_snd_pcm_hw_params_set_period_size_near(
477 this->hidden->pcm_handle, hwparams, &persize, NULL);
478 if ( status < 0 ) {
479 return(-1);
480 }
481
482 /* Need to at least double buffer */
483 periods = 2;
484 status = ALSA_snd_pcm_hw_params_set_periods_min(
485 this->hidden->pcm_handle, hwparams, &periods, NULL);
486 if ( status < 0 ) {
487 return(-1);
488 }
489
490 status = ALSA_snd_pcm_hw_params_set_periods_first(
491 this->hidden->pcm_handle, hwparams, &periods, NULL);
492 if ( status < 0 ) {
493 return(-1);
494 }
495
496 /* "set" the hardware with the desired parameters */
497 status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
498 if ( status < 0 ) {
499 return(-1);
500 }
501
502 this->spec.samples = persize;
503
504 /* This is useful for debugging */
505 if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
506 snd_pcm_uframes_t bufsize;
507
508 ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
509
510 fprintf(stderr,
511 "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
512 persize, periods, bufsize);
513 }
514
515 return(0);
516 }
517
518 static int
ALSA_OpenDevice(_THIS,void * handle,const char * devname,int iscapture)519 ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
520 {
521 int status = 0;
522 snd_pcm_t *pcm_handle = NULL;
523 snd_pcm_hw_params_t *hwparams = NULL;
524 snd_pcm_sw_params_t *swparams = NULL;
525 snd_pcm_format_t format = 0;
526 SDL_AudioFormat test_format = 0;
527 unsigned int rate = 0;
528 unsigned int channels = 0;
529 #ifdef SND_CHMAP_API_VERSION
530 snd_pcm_chmap_t *chmap;
531 char chmap_str[64];
532 #endif
533
534 /* Initialize all variables that we clean on shutdown */
535 this->hidden = (struct SDL_PrivateAudioData *)
536 SDL_malloc((sizeof *this->hidden));
537 if (this->hidden == NULL) {
538 return SDL_OutOfMemory();
539 }
540 SDL_zerop(this->hidden);
541
542 /* Open the audio device */
543 /* Name of device should depend on # channels in spec */
544 status = ALSA_snd_pcm_open(&pcm_handle,
545 get_audio_device(handle, this->spec.channels),
546 iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK,
547 SND_PCM_NONBLOCK);
548
549 if (status < 0) {
550 return SDL_SetError("ALSA: Couldn't open audio device: %s",
551 ALSA_snd_strerror(status));
552 }
553
554 this->hidden->pcm_handle = pcm_handle;
555
556 /* Figure out what the hardware is capable of */
557 snd_pcm_hw_params_alloca(&hwparams);
558 status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
559 if (status < 0) {
560 return SDL_SetError("ALSA: Couldn't get hardware config: %s",
561 ALSA_snd_strerror(status));
562 }
563
564 /* SDL only uses interleaved sample output */
565 status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
566 SND_PCM_ACCESS_RW_INTERLEAVED);
567 if (status < 0) {
568 return SDL_SetError("ALSA: Couldn't set interleaved access: %s",
569 ALSA_snd_strerror(status));
570 }
571
572 /* Try for a closest match on audio format */
573 status = -1;
574 for (test_format = SDL_FirstAudioFormat(this->spec.format);
575 test_format && (status < 0);) {
576 status = 0; /* if we can't support a format, it'll become -1. */
577 switch (test_format) {
578 case AUDIO_U8:
579 format = SND_PCM_FORMAT_U8;
580 break;
581 case AUDIO_S8:
582 format = SND_PCM_FORMAT_S8;
583 break;
584 case AUDIO_S16LSB:
585 format = SND_PCM_FORMAT_S16_LE;
586 break;
587 case AUDIO_S16MSB:
588 format = SND_PCM_FORMAT_S16_BE;
589 break;
590 case AUDIO_U16LSB:
591 format = SND_PCM_FORMAT_U16_LE;
592 break;
593 case AUDIO_U16MSB:
594 format = SND_PCM_FORMAT_U16_BE;
595 break;
596 case AUDIO_S32LSB:
597 format = SND_PCM_FORMAT_S32_LE;
598 break;
599 case AUDIO_S32MSB:
600 format = SND_PCM_FORMAT_S32_BE;
601 break;
602 case AUDIO_F32LSB:
603 format = SND_PCM_FORMAT_FLOAT_LE;
604 break;
605 case AUDIO_F32MSB:
606 format = SND_PCM_FORMAT_FLOAT_BE;
607 break;
608 default:
609 status = -1;
610 break;
611 }
612 if (status >= 0) {
613 status = ALSA_snd_pcm_hw_params_set_format(pcm_handle,
614 hwparams, format);
615 }
616 if (status < 0) {
617 test_format = SDL_NextAudioFormat();
618 }
619 }
620 if (status < 0) {
621 return SDL_SetError("ALSA: Couldn't find any hardware audio formats");
622 }
623 this->spec.format = test_format;
624
625 /* Validate number of channels and determine if swizzling is necessary
626 * Assume original swizzling, until proven otherwise.
627 */
628 this->hidden->swizzle_func = swizzle_alsa_channels;
629 #ifdef SND_CHMAP_API_VERSION
630 chmap = ALSA_snd_pcm_get_chmap(pcm_handle);
631 if (chmap) {
632 ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str);
633 if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 ||
634 SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) {
635 this->hidden->swizzle_func = no_swizzle;
636 }
637 free(chmap);
638 }
639 #endif /* SND_CHMAP_API_VERSION */
640
641 /* Set the number of channels */
642 status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
643 this->spec.channels);
644 channels = this->spec.channels;
645 if (status < 0) {
646 status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
647 if (status < 0) {
648 return SDL_SetError("ALSA: Couldn't set audio channels");
649 }
650 this->spec.channels = channels;
651 }
652
653 /* Set the audio rate */
654 rate = this->spec.freq;
655 status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
656 &rate, NULL);
657 if (status < 0) {
658 return SDL_SetError("ALSA: Couldn't set audio frequency: %s",
659 ALSA_snd_strerror(status));
660 }
661 this->spec.freq = rate;
662
663 /* Set the buffer size, in samples */
664 status = ALSA_set_buffer_size(this, hwparams);
665 if (status < 0) {
666 return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
667 }
668
669 /* Set the software parameters */
670 snd_pcm_sw_params_alloca(&swparams);
671 status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
672 if (status < 0) {
673 return SDL_SetError("ALSA: Couldn't get software config: %s",
674 ALSA_snd_strerror(status));
675 }
676 status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, this->spec.samples);
677 if (status < 0) {
678 return SDL_SetError("Couldn't set minimum available samples: %s",
679 ALSA_snd_strerror(status));
680 }
681 status =
682 ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
683 if (status < 0) {
684 return SDL_SetError("ALSA: Couldn't set start threshold: %s",
685 ALSA_snd_strerror(status));
686 }
687 status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
688 if (status < 0) {
689 return SDL_SetError("Couldn't set software audio parameters: %s",
690 ALSA_snd_strerror(status));
691 }
692
693 /* Calculate the final parameters for this audio specification */
694 SDL_CalculateAudioSpec(&this->spec);
695
696 /* Allocate mixing buffer */
697 if (!iscapture) {
698 this->hidden->mixlen = this->spec.size;
699 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen);
700 if (this->hidden->mixbuf == NULL) {
701 return SDL_OutOfMemory();
702 }
703 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
704 }
705
706 #if !SDL_ALSA_NON_BLOCKING
707 if (!iscapture) {
708 ALSA_snd_pcm_nonblock(pcm_handle, 0);
709 }
710 #endif
711
712 /* We're ready to rock and roll. :-) */
713 return 0;
714 }
715
716 typedef struct ALSA_Device
717 {
718 char *name;
719 SDL_bool iscapture;
720 struct ALSA_Device *next;
721 } ALSA_Device;
722
723 static void
add_device(const int iscapture,const char * name,void * hint,ALSA_Device ** pSeen)724 add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen)
725 {
726 ALSA_Device *dev = SDL_malloc(sizeof (ALSA_Device));
727 char *desc;
728 char *handle = NULL;
729 char *ptr;
730
731 if (!dev) {
732 return;
733 }
734
735 /* Not all alsa devices are enumerable via snd_device_name_get_hint
736 (i.e. bluetooth devices). Therefore if hint is passed in to this
737 function as NULL, assume name contains desc.
738 Make sure not to free the storage associated with desc in this case */
739 if (hint) {
740 desc = ALSA_snd_device_name_get_hint(hint, "DESC");
741 if (!desc) {
742 SDL_free(dev);
743 return;
744 }
745 } else {
746 desc = (char *) name;
747 }
748
749 SDL_assert(name != NULL);
750
751 /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output".
752 just chop the extra lines off, this seems to get a reasonable device
753 name without extra details. */
754 if ((ptr = strchr(desc, '\n')) != NULL) {
755 *ptr = '\0';
756 }
757
758 /*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/
759
760 handle = SDL_strdup(name);
761 if (!handle) {
762 if (hint) {
763 free(desc);
764 }
765 SDL_free(dev);
766 return;
767 }
768
769 SDL_AddAudioDevice(iscapture, desc, handle);
770 if (hint)
771 free(desc);
772 dev->name = handle;
773 dev->iscapture = iscapture;
774 dev->next = *pSeen;
775 *pSeen = dev;
776 }
777
778
779 static SDL_atomic_t ALSA_hotplug_shutdown;
780 static SDL_Thread *ALSA_hotplug_thread;
781
782 static int SDLCALL
ALSA_HotplugThread(void * arg)783 ALSA_HotplugThread(void *arg)
784 {
785 SDL_sem *first_run_semaphore = (SDL_sem *) arg;
786 ALSA_Device *devices = NULL;
787 ALSA_Device *next;
788 ALSA_Device *dev;
789 Uint32 ticks;
790
791 SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW);
792
793 while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) {
794 void **hints = NULL;
795 ALSA_Device *unseen;
796 ALSA_Device *seen;
797 ALSA_Device *prev;
798
799 if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) {
800 int i, j;
801 const char *match = NULL;
802 int bestmatch = 0xFFFF;
803 size_t match_len = 0;
804 int defaultdev = -1;
805 static const char * const prefixes[] = {
806 "hw:", "sysdefault:", "default:", NULL
807 };
808
809 unseen = devices;
810 seen = NULL;
811 /* Apparently there are several different ways that ALSA lists
812 actual hardware. It could be prefixed with "hw:" or "default:"
813 or "sysdefault:" and maybe others. Go through the list and see
814 if we can find a preferred prefix for the system. */
815 for (i = 0; hints[i]; i++) {
816 char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
817 if (!name) {
818 continue;
819 }
820
821 /* full name, not a prefix */
822 if ((defaultdev == -1) && (SDL_strcmp(name, "default") == 0)) {
823 defaultdev = i;
824 }
825
826 for (j = 0; prefixes[j]; j++) {
827 const char *prefix = prefixes[j];
828 const size_t prefixlen = SDL_strlen(prefix);
829 if (SDL_strncmp(name, prefix, prefixlen) == 0) {
830 if (j < bestmatch) {
831 bestmatch = j;
832 match = prefix;
833 match_len = prefixlen;
834 }
835 }
836 }
837
838 free(name);
839 }
840
841 /* look through the list of device names to find matches */
842 for (i = 0; hints[i]; i++) {
843 char *name;
844
845 /* if we didn't find a device name prefix we like at all... */
846 if ((!match) && (defaultdev != i)) {
847 continue; /* ...skip anything that isn't the default device. */
848 }
849
850 name = ALSA_snd_device_name_get_hint(hints[i], "NAME");
851 if (!name) {
852 continue;
853 }
854
855 /* only want physical hardware interfaces */
856 if (!match || (SDL_strncmp(name, match, match_len) == 0)) {
857 char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID");
858 const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0);
859 const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0);
860 SDL_bool have_output = SDL_FALSE;
861 SDL_bool have_input = SDL_FALSE;
862
863 free(ioid);
864
865 if (!isoutput && !isinput) {
866 free(name);
867 continue;
868 }
869
870 prev = NULL;
871 for (dev = unseen; dev; dev = next) {
872 next = dev->next;
873 if ( (SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) {
874 if (prev) {
875 prev->next = next;
876 } else {
877 unseen = next;
878 }
879 dev->next = seen;
880 seen = dev;
881 if (isinput) have_input = SDL_TRUE;
882 if (isoutput) have_output = SDL_TRUE;
883 } else {
884 prev = dev;
885 }
886 }
887
888 if (isinput && !have_input) {
889 add_device(SDL_TRUE, name, hints[i], &seen);
890 }
891 if (isoutput && !have_output) {
892 add_device(SDL_FALSE, name, hints[i], &seen);
893 }
894 }
895
896 free(name);
897 }
898
899 ALSA_snd_device_name_free_hint(hints);
900
901 devices = seen; /* now we have a known-good list of attached devices. */
902
903 /* report anything still in unseen as removed. */
904 for (dev = unseen; dev; dev = next) {
905 /*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
906 next = dev->next;
907 SDL_RemoveAudioDevice(dev->iscapture, dev->name);
908 SDL_free(dev->name);
909 SDL_free(dev);
910 }
911 }
912
913 /* On first run, tell ALSA_DetectDevices() that we have a complete device list so it can return. */
914 if (first_run_semaphore) {
915 SDL_SemPost(first_run_semaphore);
916 first_run_semaphore = NULL; /* let other thread clean it up. */
917 }
918
919 /* Block awhile before checking again, unless we're told to stop. */
920 ticks = SDL_GetTicks() + 5000;
921 while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) {
922 SDL_Delay(100);
923 }
924 }
925
926 /* Shutting down! Clean up any data we've gathered. */
927 for (dev = devices; dev; dev = next) {
928 /*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/
929 next = dev->next;
930 SDL_free(dev->name);
931 SDL_free(dev);
932 }
933
934 return 0;
935 }
936
937 static void
ALSA_DetectDevices(void)938 ALSA_DetectDevices(void)
939 {
940 /* Start the device detection thread here, wait for an initial iteration to complete. */
941 SDL_sem *semaphore = SDL_CreateSemaphore(0);
942 if (!semaphore) {
943 return; /* oh well. */
944 }
945
946 SDL_AtomicSet(&ALSA_hotplug_shutdown, 0);
947
948 ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore);
949 if (ALSA_hotplug_thread) {
950 SDL_SemWait(semaphore); /* wait for the first iteration to finish. */
951 }
952
953 SDL_DestroySemaphore(semaphore);
954 }
955
956 static void
ALSA_Deinitialize(void)957 ALSA_Deinitialize(void)
958 {
959 if (ALSA_hotplug_thread != NULL) {
960 SDL_AtomicSet(&ALSA_hotplug_shutdown, 1);
961 SDL_WaitThread(ALSA_hotplug_thread, NULL);
962 ALSA_hotplug_thread = NULL;
963 }
964
965 UnloadALSALibrary();
966 }
967
968 static int
ALSA_Init(SDL_AudioDriverImpl * impl)969 ALSA_Init(SDL_AudioDriverImpl * impl)
970 {
971 if (LoadALSALibrary() < 0) {
972 return 0;
973 }
974
975 /* Set the function pointers */
976 impl->DetectDevices = ALSA_DetectDevices;
977 impl->OpenDevice = ALSA_OpenDevice;
978 impl->WaitDevice = ALSA_WaitDevice;
979 impl->GetDeviceBuf = ALSA_GetDeviceBuf;
980 impl->PlayDevice = ALSA_PlayDevice;
981 impl->CloseDevice = ALSA_CloseDevice;
982 impl->Deinitialize = ALSA_Deinitialize;
983 impl->CaptureFromDevice = ALSA_CaptureFromDevice;
984 impl->FlushCapture = ALSA_FlushCapture;
985
986 impl->HasCaptureSupport = SDL_TRUE;
987
988 return 1; /* this audio target is available. */
989 }
990
991
992 AudioBootStrap ALSA_bootstrap = {
993 "alsa", "ALSA PCM audio", ALSA_Init, 0
994 };
995
996 #endif /* SDL_AUDIO_DRIVER_ALSA */
997
998 /* vi: set ts=4 sw=4 expandtab: */
999