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