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