cscg22-gearboy

CSCG 2022 Challenge 'Gearboy'
git clone https://git.sinitax.com/sinitax/cscg22-gearboy
Log | Files | Refs | sfeed.txt

SDL_alsa_audio.c (21384B)


      1/*
      2  Simple DirectMedia Layer
      3  Copyright (C) 1997-2014 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_timer.h"
     33#include "SDL_audio.h"
     34#include "../SDL_audiomem.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
     42static int (*ALSA_snd_pcm_open)
     43  (snd_pcm_t **, const char *, snd_pcm_stream_t, int);
     44static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm);
     45static snd_pcm_sframes_t(*ALSA_snd_pcm_writei)
     46  (snd_pcm_t *, const void *, snd_pcm_uframes_t);
     47static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int);
     48static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *);
     49static int (*ALSA_snd_pcm_drain) (snd_pcm_t *);
     50static const char *(*ALSA_snd_strerror) (int);
     51static size_t(*ALSA_snd_pcm_hw_params_sizeof) (void);
     52static size_t(*ALSA_snd_pcm_sw_params_sizeof) (void);
     53static void (*ALSA_snd_pcm_hw_params_copy)
     54  (snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
     55static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *);
     56static int (*ALSA_snd_pcm_hw_params_set_access)
     57  (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
     58static int (*ALSA_snd_pcm_hw_params_set_format)
     59  (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
     60static int (*ALSA_snd_pcm_hw_params_set_channels)
     61  (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
     62static int (*ALSA_snd_pcm_hw_params_get_channels)
     63  (const snd_pcm_hw_params_t *, unsigned int *);
     64static int (*ALSA_snd_pcm_hw_params_set_rate_near)
     65  (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
     66static int (*ALSA_snd_pcm_hw_params_set_period_size_near)
     67  (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
     68static int (*ALSA_snd_pcm_hw_params_get_period_size)
     69  (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
     70static int (*ALSA_snd_pcm_hw_params_set_periods_near)
     71  (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
     72static int (*ALSA_snd_pcm_hw_params_get_periods)
     73  (const snd_pcm_hw_params_t *, unsigned int *, int *);
     74static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)
     75  (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
     76static int (*ALSA_snd_pcm_hw_params_get_buffer_size)
     77  (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
     78static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *);
     79static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *,
     80                                              snd_pcm_sw_params_t *);
     81static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
     82  (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
     83static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *);
     84static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
     85static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
     86static int (*ALSA_snd_pcm_sw_params_set_avail_min)
     87  (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
     88
     89#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
     90#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof
     91#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof
     92
     93static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
     94static void *alsa_handle = NULL;
     95
     96static int
     97load_alsa_sym(const char *fn, void **addr)
     98{
     99    *addr = SDL_LoadFunction(alsa_handle, fn);
    100    if (*addr == NULL) {
    101        /* Don't call SDL_SetError(): SDL_LoadFunction already did. */
    102        return 0;
    103    }
    104
    105    return 1;
    106}
    107
    108/* cast funcs to char* first, to please GCC's strict aliasing rules. */
    109#define SDL_ALSA_SYM(x) \
    110    if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1
    111#else
    112#define SDL_ALSA_SYM(x) ALSA_##x = x
    113#endif
    114
    115static int
    116load_alsa_syms(void)
    117{
    118    SDL_ALSA_SYM(snd_pcm_open);
    119    SDL_ALSA_SYM(snd_pcm_close);
    120    SDL_ALSA_SYM(snd_pcm_writei);
    121    SDL_ALSA_SYM(snd_pcm_recover);
    122    SDL_ALSA_SYM(snd_pcm_prepare);
    123    SDL_ALSA_SYM(snd_pcm_drain);
    124    SDL_ALSA_SYM(snd_strerror);
    125    SDL_ALSA_SYM(snd_pcm_hw_params_sizeof);
    126    SDL_ALSA_SYM(snd_pcm_sw_params_sizeof);
    127    SDL_ALSA_SYM(snd_pcm_hw_params_copy);
    128    SDL_ALSA_SYM(snd_pcm_hw_params_any);
    129    SDL_ALSA_SYM(snd_pcm_hw_params_set_access);
    130    SDL_ALSA_SYM(snd_pcm_hw_params_set_format);
    131    SDL_ALSA_SYM(snd_pcm_hw_params_set_channels);
    132    SDL_ALSA_SYM(snd_pcm_hw_params_get_channels);
    133    SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near);
    134    SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near);
    135    SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size);
    136    SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_near);
    137    SDL_ALSA_SYM(snd_pcm_hw_params_get_periods);
    138    SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near);
    139    SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size);
    140    SDL_ALSA_SYM(snd_pcm_hw_params);
    141    SDL_ALSA_SYM(snd_pcm_sw_params_current);
    142    SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold);
    143    SDL_ALSA_SYM(snd_pcm_sw_params);
    144    SDL_ALSA_SYM(snd_pcm_nonblock);
    145    SDL_ALSA_SYM(snd_pcm_wait);
    146    SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min);
    147    return 0;
    148}
    149
    150#undef SDL_ALSA_SYM
    151
    152#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
    153
    154static void
    155UnloadALSALibrary(void)
    156{
    157    if (alsa_handle != NULL) {
    158        SDL_UnloadObject(alsa_handle);
    159        alsa_handle = NULL;
    160    }
    161}
    162
    163static int
    164LoadALSALibrary(void)
    165{
    166    int retval = 0;
    167    if (alsa_handle == NULL) {
    168        alsa_handle = SDL_LoadObject(alsa_library);
    169        if (alsa_handle == NULL) {
    170            retval = -1;
    171            /* Don't call SDL_SetError(): SDL_LoadObject already did. */
    172        } else {
    173            retval = load_alsa_syms();
    174            if (retval < 0) {
    175                UnloadALSALibrary();
    176            }
    177        }
    178    }
    179    return retval;
    180}
    181
    182#else
    183
    184static void
    185UnloadALSALibrary(void)
    186{
    187}
    188
    189static int
    190LoadALSALibrary(void)
    191{
    192    load_alsa_syms();
    193    return 0;
    194}
    195
    196#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
    197
    198static const char *
    199get_audio_device(int channels)
    200{
    201    const char *device;
    202
    203    device = SDL_getenv("AUDIODEV");    /* Is there a standard variable name? */
    204    if (device == NULL) {
    205        switch (channels) {
    206        case 6:
    207            device = "plug:surround51";
    208            break;
    209        case 4:
    210            device = "plug:surround40";
    211            break;
    212        default:
    213            device = "default";
    214            break;
    215        }
    216    }
    217    return device;
    218}
    219
    220
    221/* This function waits until it is possible to write a full sound buffer */
    222static void
    223ALSA_WaitDevice(_THIS)
    224{
    225    /* We're in blocking mode, so there's nothing to do here */
    226}
    227
    228
    229/* !!! FIXME: is there a channel swizzler in alsalib instead? */
    230/*
    231 * http://bugzilla.libsdl.org/show_bug.cgi?id=110
    232 * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
    233 *  and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
    234 */
    235#define SWIZ6(T) \
    236    T *ptr = (T *) this->hidden->mixbuf; \
    237    Uint32 i; \
    238    for (i = 0; i < this->spec.samples; i++, ptr += 6) { \
    239        T tmp; \
    240        tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
    241        tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
    242    }
    243
    244static SDL_INLINE void
    245swizzle_alsa_channels_6_64bit(_THIS)
    246{
    247    SWIZ6(Uint64);
    248}
    249
    250static SDL_INLINE void
    251swizzle_alsa_channels_6_32bit(_THIS)
    252{
    253    SWIZ6(Uint32);
    254}
    255
    256static SDL_INLINE void
    257swizzle_alsa_channels_6_16bit(_THIS)
    258{
    259    SWIZ6(Uint16);
    260}
    261
    262static SDL_INLINE void
    263swizzle_alsa_channels_6_8bit(_THIS)
    264{
    265    SWIZ6(Uint8);
    266}
    267
    268#undef SWIZ6
    269
    270
    271/*
    272 * Called right before feeding this->hidden->mixbuf to the hardware. Swizzle
    273 *  channels from Windows/Mac order to the format alsalib will want.
    274 */
    275static SDL_INLINE void
    276swizzle_alsa_channels(_THIS)
    277{
    278    if (this->spec.channels == 6) {
    279        const Uint16 fmtsize = (this->spec.format & 0xFF);      /* bits/channel. */
    280        if (fmtsize == 16)
    281            swizzle_alsa_channels_6_16bit(this);
    282        else if (fmtsize == 8)
    283            swizzle_alsa_channels_6_8bit(this);
    284        else if (fmtsize == 32)
    285            swizzle_alsa_channels_6_32bit(this);
    286        else if (fmtsize == 64)
    287            swizzle_alsa_channels_6_64bit(this);
    288    }
    289
    290    /* !!! FIXME: update this for 7.1 if needed, later. */
    291}
    292
    293
    294static void
    295ALSA_PlayDevice(_THIS)
    296{
    297    int status;
    298    const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf;
    299    const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) *
    300                                this->spec.channels;
    301    snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples);
    302
    303    swizzle_alsa_channels(this);
    304
    305    while ( frames_left > 0 && this->enabled ) {
    306        /* !!! FIXME: This works, but needs more testing before going live */
    307        /* ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1); */
    308        status = ALSA_snd_pcm_writei(this->hidden->pcm_handle,
    309                                     sample_buf, frames_left);
    310
    311        if (status < 0) {
    312            if (status == -EAGAIN) {
    313                /* Apparently snd_pcm_recover() doesn't handle this case -
    314                   does it assume snd_pcm_wait() above? */
    315                SDL_Delay(1);
    316                continue;
    317            }
    318            status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0);
    319            if (status < 0) {
    320                /* Hmm, not much we can do - abort */
    321                fprintf(stderr, "ALSA write failed (unrecoverable): %s\n",
    322                        ALSA_snd_strerror(status));
    323                this->enabled = 0;
    324                return;
    325            }
    326            continue;
    327        }
    328        sample_buf += status * frame_size;
    329        frames_left -= status;
    330    }
    331}
    332
    333static Uint8 *
    334ALSA_GetDeviceBuf(_THIS)
    335{
    336    return (this->hidden->mixbuf);
    337}
    338
    339static void
    340ALSA_CloseDevice(_THIS)
    341{
    342    if (this->hidden != NULL) {
    343        SDL_FreeAudioMem(this->hidden->mixbuf);
    344        this->hidden->mixbuf = NULL;
    345        if (this->hidden->pcm_handle) {
    346            ALSA_snd_pcm_drain(this->hidden->pcm_handle);
    347            ALSA_snd_pcm_close(this->hidden->pcm_handle);
    348            this->hidden->pcm_handle = NULL;
    349        }
    350        SDL_free(this->hidden);
    351        this->hidden = NULL;
    352    }
    353}
    354
    355static int
    356ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override)
    357{
    358    int status;
    359    snd_pcm_uframes_t bufsize;
    360
    361    /* "set" the hardware with the desired parameters */
    362    status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
    363    if ( status < 0 ) {
    364        return(-1);
    365    }
    366
    367    /* Get samples for the actual buffer size */
    368    status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
    369    if ( status < 0 ) {
    370        return(-1);
    371    }
    372    if ( !override && bufsize != this->spec.samples * 2 ) {
    373        return(-1);
    374    }
    375
    376    /* !!! FIXME: Is this safe to do? */
    377    this->spec.samples = bufsize / 2;
    378
    379    /* This is useful for debugging */
    380    if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
    381        snd_pcm_uframes_t persize = 0;
    382        unsigned int periods = 0;
    383
    384        ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL);
    385        ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL);
    386
    387        fprintf(stderr,
    388            "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
    389            persize, periods, bufsize);
    390    }
    391
    392    return(0);
    393}
    394
    395static int
    396ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override)
    397{
    398    const char *env;
    399    int status;
    400    snd_pcm_hw_params_t *hwparams;
    401    snd_pcm_uframes_t frames;
    402    unsigned int periods;
    403
    404    /* Copy the hardware parameters for this setup */
    405    snd_pcm_hw_params_alloca(&hwparams);
    406    ALSA_snd_pcm_hw_params_copy(hwparams, params);
    407
    408    if ( !override ) {
    409        env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
    410        if ( env ) {
    411            override = SDL_atoi(env);
    412            if ( override == 0 ) {
    413                return(-1);
    414            }
    415        }
    416    }
    417
    418    frames = this->spec.samples;
    419    status = ALSA_snd_pcm_hw_params_set_period_size_near(
    420                this->hidden->pcm_handle, hwparams, &frames, NULL);
    421    if ( status < 0 ) {
    422        return(-1);
    423    }
    424
    425    periods = 2;
    426    status = ALSA_snd_pcm_hw_params_set_periods_near(
    427                this->hidden->pcm_handle, hwparams, &periods, NULL);
    428    if ( status < 0 ) {
    429        return(-1);
    430    }
    431
    432    return ALSA_finalize_hardware(this, hwparams, override);
    433}
    434
    435static int
    436ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override)
    437{
    438    const char *env;
    439    int status;
    440    snd_pcm_hw_params_t *hwparams;
    441    snd_pcm_uframes_t frames;
    442
    443    /* Copy the hardware parameters for this setup */
    444    snd_pcm_hw_params_alloca(&hwparams);
    445    ALSA_snd_pcm_hw_params_copy(hwparams, params);
    446
    447    if ( !override ) {
    448        env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
    449        if ( env ) {
    450            override = SDL_atoi(env);
    451            if ( override == 0 ) {
    452                return(-1);
    453            }
    454        }
    455    }
    456
    457    frames = this->spec.samples * 2;
    458    status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
    459                    this->hidden->pcm_handle, hwparams, &frames);
    460    if ( status < 0 ) {
    461        return(-1);
    462    }
    463
    464    return ALSA_finalize_hardware(this, hwparams, override);
    465}
    466
    467static int
    468ALSA_OpenDevice(_THIS, const char *devname, int iscapture)
    469{
    470    int status = 0;
    471    snd_pcm_t *pcm_handle = NULL;
    472    snd_pcm_hw_params_t *hwparams = NULL;
    473    snd_pcm_sw_params_t *swparams = NULL;
    474    snd_pcm_format_t format = 0;
    475    SDL_AudioFormat test_format = 0;
    476    unsigned int rate = 0;
    477    unsigned int channels = 0;
    478
    479    /* Initialize all variables that we clean on shutdown */
    480    this->hidden = (struct SDL_PrivateAudioData *)
    481        SDL_malloc((sizeof *this->hidden));
    482    if (this->hidden == NULL) {
    483        return SDL_OutOfMemory();
    484    }
    485    SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    486
    487    /* Open the audio device */
    488    /* Name of device should depend on # channels in spec */
    489    status = ALSA_snd_pcm_open(&pcm_handle,
    490                               get_audio_device(this->spec.channels),
    491                               SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
    492
    493    if (status < 0) {
    494        ALSA_CloseDevice(this);
    495        return SDL_SetError("ALSA: Couldn't open audio device: %s",
    496                            ALSA_snd_strerror(status));
    497    }
    498
    499    this->hidden->pcm_handle = pcm_handle;
    500
    501    /* Figure out what the hardware is capable of */
    502    snd_pcm_hw_params_alloca(&hwparams);
    503    status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
    504    if (status < 0) {
    505        ALSA_CloseDevice(this);
    506        return SDL_SetError("ALSA: Couldn't get hardware config: %s",
    507                            ALSA_snd_strerror(status));
    508    }
    509
    510    /* SDL only uses interleaved sample output */
    511    status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
    512                                               SND_PCM_ACCESS_RW_INTERLEAVED);
    513    if (status < 0) {
    514        ALSA_CloseDevice(this);
    515        return SDL_SetError("ALSA: Couldn't set interleaved access: %s",
    516                     ALSA_snd_strerror(status));
    517    }
    518
    519    /* Try for a closest match on audio format */
    520    status = -1;
    521    for (test_format = SDL_FirstAudioFormat(this->spec.format);
    522         test_format && (status < 0);) {
    523        status = 0;             /* if we can't support a format, it'll become -1. */
    524        switch (test_format) {
    525        case AUDIO_U8:
    526            format = SND_PCM_FORMAT_U8;
    527            break;
    528        case AUDIO_S8:
    529            format = SND_PCM_FORMAT_S8;
    530            break;
    531        case AUDIO_S16LSB:
    532            format = SND_PCM_FORMAT_S16_LE;
    533            break;
    534        case AUDIO_S16MSB:
    535            format = SND_PCM_FORMAT_S16_BE;
    536            break;
    537        case AUDIO_U16LSB:
    538            format = SND_PCM_FORMAT_U16_LE;
    539            break;
    540        case AUDIO_U16MSB:
    541            format = SND_PCM_FORMAT_U16_BE;
    542            break;
    543        case AUDIO_S32LSB:
    544            format = SND_PCM_FORMAT_S32_LE;
    545            break;
    546        case AUDIO_S32MSB:
    547            format = SND_PCM_FORMAT_S32_BE;
    548            break;
    549        case AUDIO_F32LSB:
    550            format = SND_PCM_FORMAT_FLOAT_LE;
    551            break;
    552        case AUDIO_F32MSB:
    553            format = SND_PCM_FORMAT_FLOAT_BE;
    554            break;
    555        default:
    556            status = -1;
    557            break;
    558        }
    559        if (status >= 0) {
    560            status = ALSA_snd_pcm_hw_params_set_format(pcm_handle,
    561                                                       hwparams, format);
    562        }
    563        if (status < 0) {
    564            test_format = SDL_NextAudioFormat();
    565        }
    566    }
    567    if (status < 0) {
    568        ALSA_CloseDevice(this);
    569        return SDL_SetError("ALSA: Couldn't find any hardware audio formats");
    570    }
    571    this->spec.format = test_format;
    572
    573    /* Set the number of channels */
    574    status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
    575                                                 this->spec.channels);
    576    channels = this->spec.channels;
    577    if (status < 0) {
    578        status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
    579        if (status < 0) {
    580            ALSA_CloseDevice(this);
    581            return SDL_SetError("ALSA: Couldn't set audio channels");
    582        }
    583        this->spec.channels = channels;
    584    }
    585
    586    /* Set the audio rate */
    587    rate = this->spec.freq;
    588    status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
    589                                                  &rate, NULL);
    590    if (status < 0) {
    591        ALSA_CloseDevice(this);
    592        return SDL_SetError("ALSA: Couldn't set audio frequency: %s",
    593                            ALSA_snd_strerror(status));
    594    }
    595    this->spec.freq = rate;
    596
    597    /* Set the buffer size, in samples */
    598    if ( ALSA_set_period_size(this, hwparams, 0) < 0 &&
    599         ALSA_set_buffer_size(this, hwparams, 0) < 0 ) {
    600        /* Failed to set desired buffer size, do the best you can... */
    601        if ( ALSA_set_period_size(this, hwparams, 1) < 0 ) {
    602            ALSA_CloseDevice(this);
    603            return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
    604        }
    605    }
    606    /* Set the software parameters */
    607    snd_pcm_sw_params_alloca(&swparams);
    608    status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
    609    if (status < 0) {
    610        ALSA_CloseDevice(this);
    611        return SDL_SetError("ALSA: Couldn't get software config: %s",
    612                            ALSA_snd_strerror(status));
    613    }
    614    status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, this->spec.samples);
    615    if (status < 0) {
    616        ALSA_CloseDevice(this);
    617        return SDL_SetError("Couldn't set minimum available samples: %s",
    618                            ALSA_snd_strerror(status));
    619    }
    620    status =
    621        ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
    622    if (status < 0) {
    623        ALSA_CloseDevice(this);
    624        return SDL_SetError("ALSA: Couldn't set start threshold: %s",
    625                            ALSA_snd_strerror(status));
    626    }
    627    status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
    628    if (status < 0) {
    629        ALSA_CloseDevice(this);
    630        return SDL_SetError("Couldn't set software audio parameters: %s",
    631                            ALSA_snd_strerror(status));
    632    }
    633
    634    /* Calculate the final parameters for this audio specification */
    635    SDL_CalculateAudioSpec(&this->spec);
    636
    637    /* Allocate mixing buffer */
    638    this->hidden->mixlen = this->spec.size;
    639    this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
    640    if (this->hidden->mixbuf == NULL) {
    641        ALSA_CloseDevice(this);
    642        return SDL_OutOfMemory();
    643    }
    644    SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen);
    645
    646    /* Switch to blocking mode for playback */
    647    ALSA_snd_pcm_nonblock(pcm_handle, 0);
    648
    649    /* We're ready to rock and roll. :-) */
    650    return 0;
    651}
    652
    653static void
    654ALSA_Deinitialize(void)
    655{
    656    UnloadALSALibrary();
    657}
    658
    659static int
    660ALSA_Init(SDL_AudioDriverImpl * impl)
    661{
    662    if (LoadALSALibrary() < 0) {
    663        return 0;
    664    }
    665
    666    /* Set the function pointers */
    667    impl->OpenDevice = ALSA_OpenDevice;
    668    impl->WaitDevice = ALSA_WaitDevice;
    669    impl->GetDeviceBuf = ALSA_GetDeviceBuf;
    670    impl->PlayDevice = ALSA_PlayDevice;
    671    impl->CloseDevice = ALSA_CloseDevice;
    672    impl->Deinitialize = ALSA_Deinitialize;
    673    impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: Add device enum! */
    674
    675    return 1;   /* this audio target is available. */
    676}
    677
    678
    679AudioBootStrap ALSA_bootstrap = {
    680    "alsa", "ALSA PCM audio", ALSA_Init, 0
    681};
    682
    683#endif /* SDL_AUDIO_DRIVER_ALSA */
    684
    685/* vi: set ts=4 sw=4 expandtab: */