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