src/audio/alsa/SDL_alsa_audio.c
author Ryan C. Gordon <icculus@icculus.org>
Mon, 21 May 2018 12:00:21 -0400
changeset 11994 8e094f91ddab
parent 11811 5d94cb6b24d3
child 12031 b6c4568cc10b
permissions -rw-r--r--
thread: fixed compiler warnings on non-Linux systems that use pthread.

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