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