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