sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_alsa_audio.c (32583B)


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