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