src/audio/alsa/SDL_alsa_audio.c
author Sam Lantinga <slouken@libsdl.org>
Tue, 28 Feb 2012 21:58:36 -0500
changeset 6303 6bb657898f55
parent 6046 3b33b82cdbac
child 6885 700f1b25f77f
permissions -rwxr-xr-x
Fixed bug 1429 - Compiling static library with -arch fails when linking showimage

We no longer need this ancient hack and it's causing problems when building shared libraries against SDL.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2012 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_config.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 __inline__ void
   245 swizzle_alsa_channels_6_64bit(_THIS)
   246 {
   247     SWIZ6(Uint64);
   248 }
   249 
   250 static __inline__ void
   251 swizzle_alsa_channels_6_32bit(_THIS)
   252 {
   253     SWIZ6(Uint32);
   254 }
   255 
   256 static __inline__ void
   257 swizzle_alsa_channels_6_16bit(_THIS)
   258 {
   259     SWIZ6(Uint16);
   260 }
   261 
   262 static __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 __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         if (this->hidden->mixbuf != NULL) {
   344             SDL_FreeAudioMem(this->hidden->mixbuf);
   345             this->hidden->mixbuf = NULL;
   346         }
   347         if (this->hidden->pcm_handle) {
   348             ALSA_snd_pcm_drain(this->hidden->pcm_handle);
   349             ALSA_snd_pcm_close(this->hidden->pcm_handle);
   350             this->hidden->pcm_handle = NULL;
   351         }
   352         SDL_free(this->hidden);
   353         this->hidden = NULL;
   354     }
   355 }
   356 
   357 static int
   358 ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override)
   359 {
   360     int status;
   361     snd_pcm_uframes_t bufsize;
   362 
   363     /* "set" the hardware with the desired parameters */
   364     status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams);
   365     if ( status < 0 ) {
   366         return(-1);
   367     }
   368 
   369     /* Get samples for the actual buffer size */
   370     status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize);
   371     if ( status < 0 ) {
   372         return(-1);
   373     }
   374     if ( !override && bufsize != this->spec.samples * 2 ) {
   375         return(-1);
   376     }
   377 
   378     /* !!! FIXME: Is this safe to do? */
   379     this->spec.samples = bufsize / 2;
   380 
   381     /* This is useful for debugging */
   382     if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) {
   383         snd_pcm_uframes_t persize = 0;
   384         unsigned int periods = 0;
   385 
   386         ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL);
   387         ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL);
   388 
   389         fprintf(stderr,
   390             "ALSA: period size = %ld, periods = %u, buffer size = %lu\n",
   391             persize, periods, bufsize);
   392     }
   393 
   394     return(0);
   395 }
   396 
   397 static int
   398 ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override)
   399 {
   400     const char *env;
   401     int status;
   402     snd_pcm_hw_params_t *hwparams;
   403     snd_pcm_uframes_t frames;
   404     unsigned int periods;
   405 
   406     /* Copy the hardware parameters for this setup */
   407     snd_pcm_hw_params_alloca(&hwparams);
   408     ALSA_snd_pcm_hw_params_copy(hwparams, params);
   409 
   410     if ( !override ) {
   411         env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
   412         if ( env ) {
   413             override = SDL_atoi(env);
   414             if ( override == 0 ) {
   415                 return(-1);
   416             }
   417         }
   418     }
   419 
   420     frames = this->spec.samples;
   421     status = ALSA_snd_pcm_hw_params_set_period_size_near(
   422                 this->hidden->pcm_handle, hwparams, &frames, NULL);
   423     if ( status < 0 ) {
   424         return(-1);
   425     }
   426 
   427     periods = 2;
   428     status = ALSA_snd_pcm_hw_params_set_periods_near(
   429                 this->hidden->pcm_handle, hwparams, &periods, NULL);
   430     if ( status < 0 ) {
   431         return(-1);
   432     }
   433 
   434     return ALSA_finalize_hardware(this, hwparams, override);
   435 }
   436 
   437 static int
   438 ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override)
   439 {
   440     const char *env;
   441     int status;
   442     snd_pcm_hw_params_t *hwparams;
   443     snd_pcm_uframes_t frames;
   444 
   445     /* Copy the hardware parameters for this setup */
   446     snd_pcm_hw_params_alloca(&hwparams);
   447     ALSA_snd_pcm_hw_params_copy(hwparams, params);
   448 
   449     if ( !override ) {
   450         env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
   451         if ( env ) {
   452             override = SDL_atoi(env);
   453             if ( override == 0 ) {
   454                 return(-1);
   455             }
   456         }
   457     }
   458 
   459     frames = this->spec.samples * 2;
   460     status = ALSA_snd_pcm_hw_params_set_buffer_size_near(
   461                     this->hidden->pcm_handle, hwparams, &frames);
   462     if ( status < 0 ) {
   463         return(-1);
   464     }
   465 
   466     return ALSA_finalize_hardware(this, hwparams, override);
   467 }
   468 
   469 static int
   470 ALSA_OpenDevice(_THIS, const char *devname, int iscapture)
   471 {
   472     int status = 0;
   473     snd_pcm_t *pcm_handle = NULL;
   474     snd_pcm_hw_params_t *hwparams = NULL;
   475     snd_pcm_sw_params_t *swparams = NULL;
   476     snd_pcm_format_t format = 0;
   477     SDL_AudioFormat test_format = 0;
   478     unsigned int rate = 0;
   479     unsigned int channels = 0;
   480 
   481     /* Initialize all variables that we clean on shutdown */
   482     this->hidden = (struct SDL_PrivateAudioData *)
   483         SDL_malloc((sizeof *this->hidden));
   484     if (this->hidden == NULL) {
   485         SDL_OutOfMemory();
   486         return 0;
   487     }
   488     SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   489 
   490     /* Open the audio device */
   491     /* Name of device should depend on # channels in spec */
   492     status = ALSA_snd_pcm_open(&pcm_handle,
   493                                get_audio_device(this->spec.channels),
   494                                SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   495 
   496     if (status < 0) {
   497         ALSA_CloseDevice(this);
   498         SDL_SetError("ALSA: Couldn't open audio device: %s",
   499                      ALSA_snd_strerror(status));
   500         return 0;
   501     }
   502 
   503     this->hidden->pcm_handle = pcm_handle;
   504 
   505     /* Figure out what the hardware is capable of */
   506     snd_pcm_hw_params_alloca(&hwparams);
   507     status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams);
   508     if (status < 0) {
   509         ALSA_CloseDevice(this);
   510         SDL_SetError("ALSA: Couldn't get hardware config: %s",
   511                      ALSA_snd_strerror(status));
   512         return 0;
   513     }
   514 
   515     /* SDL only uses interleaved sample output */
   516     status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams,
   517                                                SND_PCM_ACCESS_RW_INTERLEAVED);
   518     if (status < 0) {
   519         ALSA_CloseDevice(this);
   520         SDL_SetError("ALSA: Couldn't set interleaved access: %s",
   521                      ALSA_snd_strerror(status));
   522         return 0;
   523     }
   524 
   525     /* Try for a closest match on audio format */
   526     status = -1;
   527     for (test_format = SDL_FirstAudioFormat(this->spec.format);
   528          test_format && (status < 0);) {
   529         status = 0;             /* if we can't support a format, it'll become -1. */
   530         switch (test_format) {
   531         case AUDIO_U8:
   532             format = SND_PCM_FORMAT_U8;
   533             break;
   534         case AUDIO_S8:
   535             format = SND_PCM_FORMAT_S8;
   536             break;
   537         case AUDIO_S16LSB:
   538             format = SND_PCM_FORMAT_S16_LE;
   539             break;
   540         case AUDIO_S16MSB:
   541             format = SND_PCM_FORMAT_S16_BE;
   542             break;
   543         case AUDIO_U16LSB:
   544             format = SND_PCM_FORMAT_U16_LE;
   545             break;
   546         case AUDIO_U16MSB:
   547             format = SND_PCM_FORMAT_U16_BE;
   548             break;
   549         case AUDIO_S32LSB:
   550             format = SND_PCM_FORMAT_S32_LE;
   551             break;
   552         case AUDIO_S32MSB:
   553             format = SND_PCM_FORMAT_S32_BE;
   554             break;
   555         case AUDIO_F32LSB:
   556             format = SND_PCM_FORMAT_FLOAT_LE;
   557             break;
   558         case AUDIO_F32MSB:
   559             format = SND_PCM_FORMAT_FLOAT_BE;
   560             break;
   561         default:
   562             status = -1;
   563             break;
   564         }
   565         if (status >= 0) {
   566             status = ALSA_snd_pcm_hw_params_set_format(pcm_handle,
   567                                                        hwparams, format);
   568         }
   569         if (status < 0) {
   570             test_format = SDL_NextAudioFormat();
   571         }
   572     }
   573     if (status < 0) {
   574         ALSA_CloseDevice(this);
   575         SDL_SetError("ALSA: Couldn't find any hardware audio formats");
   576         return 0;
   577     }
   578     this->spec.format = test_format;
   579 
   580     /* Set the number of channels */
   581     status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams,
   582                                                  this->spec.channels);
   583     channels = this->spec.channels;
   584     if (status < 0) {
   585         status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels);
   586         if (status < 0) {
   587             ALSA_CloseDevice(this);
   588             SDL_SetError("ALSA: Couldn't set audio channels");
   589             return 0;
   590         }
   591         this->spec.channels = channels;
   592     }
   593 
   594     /* Set the audio rate */
   595     rate = this->spec.freq;
   596     status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams,
   597                                                   &rate, NULL);
   598     if (status < 0) {
   599         ALSA_CloseDevice(this);
   600         SDL_SetError("ALSA: Couldn't set audio frequency: %s",
   601                      ALSA_snd_strerror(status));
   602         return 0;
   603     }
   604     this->spec.freq = rate;
   605 
   606     /* Set the buffer size, in samples */
   607     if ( ALSA_set_period_size(this, hwparams, 0) < 0 &&
   608          ALSA_set_buffer_size(this, hwparams, 0) < 0 ) {
   609         /* Failed to set desired buffer size, do the best you can... */
   610         if ( ALSA_set_period_size(this, hwparams, 1) < 0 ) {
   611             ALSA_CloseDevice(this);
   612             SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status));
   613             return(-1);
   614         }
   615     }
   616     /* Set the software parameters */
   617     snd_pcm_sw_params_alloca(&swparams);
   618     status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams);
   619     if (status < 0) {
   620         ALSA_CloseDevice(this);
   621         SDL_SetError("ALSA: Couldn't get software config: %s",
   622                      ALSA_snd_strerror(status));
   623         return 0;
   624     }
   625     status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, this->spec.samples);
   626     if (status < 0) {
   627         ALSA_CloseDevice(this);
   628         SDL_SetError("Couldn't set minimum available samples: %s",
   629                      ALSA_snd_strerror(status));
   630         return 0;
   631     }
   632     status =
   633         ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1);
   634     if (status < 0) {
   635         ALSA_CloseDevice(this);
   636         SDL_SetError("ALSA: Couldn't set start threshold: %s",
   637                      ALSA_snd_strerror(status));
   638         return 0;
   639     }
   640     status = ALSA_snd_pcm_sw_params(pcm_handle, swparams);
   641     if (status < 0) {
   642         ALSA_CloseDevice(this);
   643         SDL_SetError("Couldn't set software audio parameters: %s",
   644                      ALSA_snd_strerror(status));
   645         return 0;
   646     }
   647 
   648     /* Calculate the final parameters for this audio specification */
   649     SDL_CalculateAudioSpec(&this->spec);
   650 
   651     /* Allocate mixing buffer */
   652     this->hidden->mixlen = this->spec.size;
   653     this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen);
   654     if (this->hidden->mixbuf == NULL) {
   655         ALSA_CloseDevice(this);
   656         SDL_OutOfMemory();
   657         return 0;
   658     }
   659     SDL_memset(this->hidden->mixbuf, this->spec.silence, this->spec.size);
   660 
   661     /* Switch to blocking mode for playback */
   662     ALSA_snd_pcm_nonblock(pcm_handle, 0);
   663 
   664     /* We're ready to rock and roll. :-) */
   665     return 1;
   666 }
   667 
   668 static void
   669 ALSA_Deinitialize(void)
   670 {
   671     UnloadALSALibrary();
   672 }
   673 
   674 static int
   675 ALSA_Init(SDL_AudioDriverImpl * impl)
   676 {
   677     if (LoadALSALibrary() < 0) {
   678         return 0;
   679     }
   680 
   681     /* Set the function pointers */
   682     impl->OpenDevice = ALSA_OpenDevice;
   683     impl->WaitDevice = ALSA_WaitDevice;
   684     impl->GetDeviceBuf = ALSA_GetDeviceBuf;
   685     impl->PlayDevice = ALSA_PlayDevice;
   686     impl->CloseDevice = ALSA_CloseDevice;
   687     impl->Deinitialize = ALSA_Deinitialize;
   688     impl->OnlyHasDefaultOutputDevice = 1;       /* !!! FIXME: Add device enum! */
   689 
   690     return 1;   /* this audio target is available. */
   691 }
   692 
   693 
   694 AudioBootStrap ALSA_bootstrap = {
   695     "alsa", "ALSA PCM audio", ALSA_Init, 0
   696 };
   697 
   698 #endif /* SDL_AUDIO_DRIVER_ALSA */
   699 
   700 /* vi: set ts=4 sw=4 expandtab: */