src/audio/alsa/SDL_alsa_audio.c
author Philipp Wiesemann <philipp.wiesemann@arcor.de>
Wed, 29 Oct 2014 20:20:47 +0100
changeset 9201 21d9f9babb30
parent 8149 681eb46b8ac4
child 9393 ed79a66e57e5
permissions -rw-r--r--
Fixed bug 2647 - Memory leak in SDL_AddHintCallback function - SDL_hints.c

Nitz

Variable entry going out of scope leaks the storage it points to, at:

/* Need to add a hint entry for this watcher */
hint = (SDL_Hint *)SDL_malloc(sizeof(*hint));
if (!hint) {
return;
}

Patch is attached.
     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 
    42 static int (*ALSA_snd_pcm_open)
    43   (snd_pcm_t **, const char *, snd_pcm_stream_t, int);
    44 static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm);
    45 static snd_pcm_sframes_t(*ALSA_snd_pcm_writei)
    46   (snd_pcm_t *, const void *, snd_pcm_uframes_t);
    47 static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int);
    48 static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *);
    49 static int (*ALSA_snd_pcm_drain) (snd_pcm_t *);
    50 static const char *(*ALSA_snd_strerror) (int);
    51 static size_t(*ALSA_snd_pcm_hw_params_sizeof) (void);
    52 static size_t(*ALSA_snd_pcm_sw_params_sizeof) (void);
    53 static void (*ALSA_snd_pcm_hw_params_copy)
    54   (snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *);
    55 static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *);
    56 static int (*ALSA_snd_pcm_hw_params_set_access)
    57   (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t);
    58 static int (*ALSA_snd_pcm_hw_params_set_format)
    59   (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t);
    60 static int (*ALSA_snd_pcm_hw_params_set_channels)
    61   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int);
    62 static int (*ALSA_snd_pcm_hw_params_get_channels)
    63   (const snd_pcm_hw_params_t *, unsigned int *);
    64 static int (*ALSA_snd_pcm_hw_params_set_rate_near)
    65   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
    66 static int (*ALSA_snd_pcm_hw_params_set_period_size_near)
    67   (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
    68 static int (*ALSA_snd_pcm_hw_params_get_period_size)
    69   (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *);
    70 static int (*ALSA_snd_pcm_hw_params_set_periods_near)
    71   (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *);
    72 static int (*ALSA_snd_pcm_hw_params_get_periods)
    73   (const snd_pcm_hw_params_t *, unsigned int *, int *);
    74 static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)
    75   (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
    76 static int (*ALSA_snd_pcm_hw_params_get_buffer_size)
    77   (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *);
    78 static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *);
    79 static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *,
    80                                               snd_pcm_sw_params_t *);
    81 static int (*ALSA_snd_pcm_sw_params_set_start_threshold)
    82   (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t);
    83 static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *);
    84 static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int);
    85 static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int);
    86 static 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 
    93 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
    94 static void *alsa_handle = NULL;
    95 
    96 static int
    97 load_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 
   115 static int
   116 load_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 
   154 static void
   155 UnloadALSALibrary(void)
   156 {
   157     if (alsa_handle != NULL) {
   158         SDL_UnloadObject(alsa_handle);
   159         alsa_handle = NULL;
   160     }
   161 }
   162 
   163 static int
   164 LoadALSALibrary(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 
   184 static void
   185 UnloadALSALibrary(void)
   186 {
   187 }
   188 
   189 static int
   190 LoadALSALibrary(void)
   191 {
   192     load_alsa_syms();
   193     return 0;
   194 }
   195 
   196 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
   197 
   198 static const char *
   199 get_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 */
   222 static void
   223 ALSA_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 
   244 static SDL_INLINE void
   245 swizzle_alsa_channels_6_64bit(_THIS)
   246 {
   247     SWIZ6(Uint64);
   248 }
   249 
   250 static SDL_INLINE void
   251 swizzle_alsa_channels_6_32bit(_THIS)
   252 {
   253     SWIZ6(Uint32);
   254 }
   255 
   256 static SDL_INLINE void
   257 swizzle_alsa_channels_6_16bit(_THIS)
   258 {
   259     SWIZ6(Uint16);
   260 }
   261 
   262 static SDL_INLINE void
   263 swizzle_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  */
   275 static SDL_INLINE void
   276 swizzle_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 
   294 static void
   295 ALSA_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 
   333 static Uint8 *
   334 ALSA_GetDeviceBuf(_THIS)
   335 {
   336     return (this->hidden->mixbuf);
   337 }
   338 
   339 static void
   340 ALSA_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 
   355 static int
   356 ALSA_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 
   395 static int
   396 ALSA_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 
   435 static int
   436 ALSA_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 
   467 static int
   468 ALSA_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 
   653 static void
   654 ALSA_Deinitialize(void)
   655 {
   656     UnloadALSALibrary();
   657 }
   658 
   659 static int
   660 ALSA_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 
   679 AudioBootStrap 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: */