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