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