src/audio/alsa/SDL_alsa_audio.c
author Sam Lantinga
Mon, 19 Oct 2009 05:15:21 +0000
branchSDL-1.2
changeset 4361 991e4bcfc7a8
parent 4360 be2dff2f79c0
child 4362 e48a66bed093
permissions -rw-r--r--
Added an error message for when ALSA audio fails
     1 /*
     2     SDL - Simple DirectMedia Layer
     3     Copyright (C) 1997-2009 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 #include "SDL_config.h"
    23 
    24 /* Allow access to a raw mixing buffer */
    25 
    26 #include <sys/types.h>
    27 #include <signal.h>	/* For kill() */
    28 
    29 #include "SDL_timer.h"
    30 #include "SDL_audio.h"
    31 #include "../SDL_audiomem.h"
    32 #include "../SDL_audio_c.h"
    33 #include "SDL_alsa_audio.h"
    34 
    35 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
    36 #include "SDL_name.h"
    37 #include "SDL_loadso.h"
    38 #else
    39 #define SDL_NAME(X)	X
    40 #endif
    41 
    42 
    43 /* The tag name used by ALSA audio */
    44 #define DRIVER_NAME         "alsa"
    45 
    46 /* Audio driver functions */
    47 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec);
    48 static void ALSA_WaitAudio(_THIS);
    49 static void ALSA_PlayAudio(_THIS);
    50 static Uint8 *ALSA_GetAudioBuf(_THIS);
    51 static void ALSA_CloseAudio(_THIS);
    52 
    53 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
    54 
    55 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
    56 static void *alsa_handle = NULL;
    57 static int alsa_loaded = 0;
    58 
    59 static int (*SDL_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
    60 static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm);
    61 static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
    62 static int (*SDL_NAME(snd_pcm_recover))(snd_pcm_t *pcm, int err, int silent);
    63 static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);
    64 static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);
    65 static const char *(*SDL_NAME(snd_strerror))(int errnum);
    66 static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
    67 static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void);
    68 static void (*SDL_NAME(snd_pcm_hw_params_copy))(snd_pcm_hw_params_t *dst, const snd_pcm_hw_params_t *src);
    69 static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
    70 static int (*SDL_NAME(snd_pcm_hw_params_set_access))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_access_t access);
    71 static int (*SDL_NAME(snd_pcm_hw_params_set_format))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_format_t val);
    72 static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
    73 static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params, unsigned int *val);
    74 static int (*SDL_NAME(snd_pcm_hw_params_set_rate_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
    75 static int (*SDL_NAME(snd_pcm_hw_params_set_period_size_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val, int *dir);
    76 static int (*SDL_NAME(snd_pcm_hw_params_get_period_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *frames, int *dir);
    77 static int (*SDL_NAME(snd_pcm_hw_params_set_periods_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
    78 static int (*SDL_NAME(snd_pcm_hw_params_get_periods))(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
    79 static int (*SDL_NAME(snd_pcm_hw_params_set_buffer_size_near))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
    80 static int (*SDL_NAME(snd_pcm_hw_params_get_buffer_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
    81 static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
    82 /*
    83 */
    84 static int (*SDL_NAME(snd_pcm_sw_params_current))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams);
    85 static int (*SDL_NAME(snd_pcm_sw_params_set_start_threshold))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val);
    86 static int (*SDL_NAME(snd_pcm_sw_params))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
    87 static int (*SDL_NAME(snd_pcm_nonblock))(snd_pcm_t *pcm, int nonblock);
    88 #define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof)
    89 #define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof)
    90 
    91 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
    92 static struct {
    93 	const char *name;
    94 	void **func;
    95 } alsa_functions[] = {
    96 	{ "snd_pcm_open",	(void**)(char*)&SDL_NAME(snd_pcm_open)		},
    97 	{ "snd_pcm_close",	(void**)(char*)&SDL_NAME(snd_pcm_close)	},
    98 	{ "snd_pcm_writei",	(void**)(char*)&SDL_NAME(snd_pcm_writei)	},
    99 	{ "snd_pcm_recover",	(void**)(char*)&SDL_NAME(snd_pcm_recover)	},
   100 	{ "snd_pcm_prepare",	(void**)(char*)&SDL_NAME(snd_pcm_prepare)	},
   101 	{ "snd_pcm_drain",	(void**)(char*)&SDL_NAME(snd_pcm_drain)	},
   102 	{ "snd_strerror",	(void**)(char*)&SDL_NAME(snd_strerror)		},
   103 	{ "snd_pcm_hw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof)		},
   104 	{ "snd_pcm_sw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof)		},
   105 	{ "snd_pcm_hw_params_copy",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_copy)		},
   106 	{ "snd_pcm_hw_params_any",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_any)		},
   107 	{ "snd_pcm_hw_params_set_access",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_access)		},
   108 	{ "snd_pcm_hw_params_set_format",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_format)		},
   109 	{ "snd_pcm_hw_params_set_channels",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_channels)	},
   110 	{ "snd_pcm_hw_params_get_channels",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_channels)	},
   111 	{ "snd_pcm_hw_params_set_rate_near",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_rate_near)	},
   112 	{ "snd_pcm_hw_params_set_period_size_near",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_period_size_near)	},
   113 	{ "snd_pcm_hw_params_get_period_size",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_period_size)	},
   114 	{ "snd_pcm_hw_params_set_periods_near",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_periods_near)	},
   115 	{ "snd_pcm_hw_params_get_periods",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_periods)	},
   116 	{ "snd_pcm_hw_params_set_buffer_size_near",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_buffer_size_near) },
   117 	{ "snd_pcm_hw_params_get_buffer_size",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_buffer_size) },
   118 	{ "snd_pcm_hw_params",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params)	},
   119 	{ "snd_pcm_sw_params_current",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params_current)	},
   120 	{ "snd_pcm_sw_params_set_start_threshold",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_start_threshold)	},
   121 	{ "snd_pcm_sw_params",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params)	},
   122 	{ "snd_pcm_nonblock",	(void**)(char*)&SDL_NAME(snd_pcm_nonblock)	},
   123 };
   124 
   125 static void UnloadALSALibrary(void) {
   126 	if (alsa_loaded) {
   127 		SDL_UnloadObject(alsa_handle);
   128 		alsa_handle = NULL;
   129 		alsa_loaded = 0;
   130 	}
   131 }
   132 
   133 static int LoadALSALibrary(void) {
   134 	int i, retval = -1;
   135 
   136 	alsa_handle = SDL_LoadObject(alsa_library);
   137 	if (alsa_handle) {
   138 		alsa_loaded = 1;
   139 		retval = 0;
   140 		for (i = 0; i < SDL_arraysize(alsa_functions); i++) {
   141 			*alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);
   142 			if (!*alsa_functions[i].func) {
   143 				retval = -1;
   144 				UnloadALSALibrary();
   145 				break;
   146 			}
   147 		}
   148 	}
   149 	return retval;
   150 }
   151 
   152 #else
   153 
   154 static void UnloadALSALibrary(void) {
   155 	return;
   156 }
   157 
   158 static int LoadALSALibrary(void) {
   159 	return 0;
   160 }
   161 
   162 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
   163 
   164 static const char *get_audio_device(int channels)
   165 {
   166 	const char *device;
   167 	
   168 	device = SDL_getenv("AUDIODEV");	/* Is there a standard variable name? */
   169 	if ( device == NULL ) {
   170 		switch (channels) {
   171 		case 6:
   172 			device = "plug:surround51";
   173 			break;
   174 		case 4:
   175 			device = "plug:surround40";
   176 			break;
   177 		default:
   178 			device = "default";
   179 			break;
   180 		}
   181 	}
   182 	return device;
   183 }
   184 
   185 /* Audio driver bootstrap functions */
   186 
   187 static int Audio_Available(void)
   188 {
   189 	int available;
   190 	int status;
   191 	snd_pcm_t *handle;
   192 
   193 	available = 0;
   194 	if (LoadALSALibrary() < 0) {
   195 		return available;
   196 	}
   197 	status = SDL_NAME(snd_pcm_open)(&handle, get_audio_device(2), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   198 	if ( status >= 0 ) {
   199 		available = 1;
   200         	SDL_NAME(snd_pcm_close)(handle);
   201 	}
   202 	UnloadALSALibrary();
   203 	return(available);
   204 }
   205 
   206 static void Audio_DeleteDevice(SDL_AudioDevice *device)
   207 {
   208 	SDL_free(device->hidden);
   209 	SDL_free(device);
   210 	UnloadALSALibrary();
   211 }
   212 
   213 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
   214 {
   215 	SDL_AudioDevice *this;
   216 
   217 	/* Initialize all variables that we clean on shutdown */
   218 	LoadALSALibrary();
   219 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
   220 	if ( this ) {
   221 		SDL_memset(this, 0, (sizeof *this));
   222 		this->hidden = (struct SDL_PrivateAudioData *)
   223 				SDL_malloc((sizeof *this->hidden));
   224 	}
   225 	if ( (this == NULL) || (this->hidden == NULL) ) {
   226 		SDL_OutOfMemory();
   227 		if ( this ) {
   228 			SDL_free(this);
   229 		}
   230 		return(0);
   231 	}
   232 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   233 
   234 	/* Set the function pointers */
   235 	this->OpenAudio = ALSA_OpenAudio;
   236 	this->WaitAudio = ALSA_WaitAudio;
   237 	this->PlayAudio = ALSA_PlayAudio;
   238 	this->GetAudioBuf = ALSA_GetAudioBuf;
   239 	this->CloseAudio = ALSA_CloseAudio;
   240 
   241 	this->free = Audio_DeleteDevice;
   242 
   243 	return this;
   244 }
   245 
   246 AudioBootStrap ALSA_bootstrap = {
   247 	DRIVER_NAME, "ALSA PCM audio",
   248 	Audio_Available, Audio_CreateDevice
   249 };
   250 
   251 /* This function waits until it is possible to write a full sound buffer */
   252 static void ALSA_WaitAudio(_THIS)
   253 {
   254 	/* We're in blocking mode, so there's nothing to do here */
   255 }
   256 
   257 
   258 /*
   259  * http://bugzilla.libsdl.org/show_bug.cgi?id=110
   260  * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
   261  *  and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
   262  */
   263 #define SWIZ6(T) \
   264     T *ptr = (T *) mixbuf; \
   265     const Uint32 count = (this->spec.samples / 6); \
   266     Uint32 i; \
   267     for (i = 0; i < count; i++, ptr += 6) { \
   268         T tmp; \
   269         tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
   270         tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
   271     }
   272 
   273 static __inline__ void swizzle_alsa_channels_6_64bit(_THIS) { SWIZ6(Uint64); }
   274 static __inline__ void swizzle_alsa_channels_6_32bit(_THIS) { SWIZ6(Uint32); }
   275 static __inline__ void swizzle_alsa_channels_6_16bit(_THIS) { SWIZ6(Uint16); }
   276 static __inline__ void swizzle_alsa_channels_6_8bit(_THIS) { SWIZ6(Uint8); }
   277 
   278 #undef SWIZ6
   279 
   280 
   281 /*
   282  * Called right before feeding this->mixbuf to the hardware. Swizzle channels
   283  *  from Windows/Mac order to the format alsalib will want.
   284  */
   285 static __inline__ void swizzle_alsa_channels(_THIS)
   286 {
   287     if (this->spec.channels == 6) {
   288         const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */
   289         if (fmtsize == 16)
   290             swizzle_alsa_channels_6_16bit(this);
   291         else if (fmtsize == 8)
   292             swizzle_alsa_channels_6_8bit(this);
   293         else if (fmtsize == 32)
   294             swizzle_alsa_channels_6_32bit(this);
   295         else if (fmtsize == 64)
   296             swizzle_alsa_channels_6_64bit(this);
   297     }
   298 
   299     /* !!! FIXME: update this for 7.1 if needed, later. */
   300 }
   301 
   302 
   303 static void ALSA_PlayAudio(_THIS)
   304 {
   305 	int status;
   306 	snd_pcm_uframes_t frames_left;
   307 	const Uint8 *sample_buf = (const Uint8 *) mixbuf;
   308 	const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) * this->spec.channels;
   309 
   310 	swizzle_alsa_channels(this);
   311 
   312 	frames_left = ((snd_pcm_uframes_t) this->spec.samples);
   313 
   314 	while ( frames_left > 0 && this->enabled ) {
   315 		status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, frames_left);
   316 		if ( status < 0 ) {
   317 			status = SDL_NAME(snd_pcm_recover)(pcm_handle, status, 0);
   318 			if ( status < 0 ) {
   319 				/* Hmm, not much we can do - abort */
   320 				fprintf(stderr, "ALSA write failed (unrecoverable): %s", SDL_NAME(snd_strerror)(status));
   321 				this->enabled = 0;
   322 				return;
   323 			}
   324 			continue;
   325 		}
   326 		sample_buf += status * frame_size;
   327 		frames_left -= status;
   328 	}
   329 }
   330 
   331 static Uint8 *ALSA_GetAudioBuf(_THIS)
   332 {
   333 	return(mixbuf);
   334 }
   335 
   336 static void ALSA_CloseAudio(_THIS)
   337 {
   338 	if ( mixbuf != NULL ) {
   339 		SDL_FreeAudioMem(mixbuf);
   340 		mixbuf = NULL;
   341 	}
   342 	if ( pcm_handle ) {
   343 		SDL_NAME(snd_pcm_drain)(pcm_handle);
   344 		SDL_NAME(snd_pcm_close)(pcm_handle);
   345 		pcm_handle = NULL;
   346 	}
   347 }
   348 
   349 static int ALSA_finalize_hardware(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *hwparams, int override)
   350 {
   351 	int status;
   352 	snd_pcm_uframes_t bufsize;
   353 
   354 	/* "set" the hardware with the desired parameters */
   355 	status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
   356 	if ( status < 0 ) {
   357 		return(-1);
   358 	}
   359 
   360 	/* Get samples for the actual buffer size */
   361 	status = SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
   362 	if ( status < 0 ) {
   363 		return(-1);
   364 	}
   365 	if ( !override && bufsize != spec->samples * 2 ) {
   366 		return(-1);
   367 	}
   368 
   369 	/* FIXME: Is this safe to do? */
   370 	spec->samples = bufsize / 2;
   371 
   372 	/* This is useful for debugging */
   373 	if ( getenv("SDL_AUDIO_ALSA_DEBUG") ) {
   374 		snd_pcm_sframes_t persize = 0;
   375 		unsigned int periods = 0;
   376 
   377 		SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, NULL);
   378 		SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, NULL);
   379 
   380 		fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
   381 	}
   382 	return(0);
   383 }
   384 
   385 static int ALSA_set_period_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override)
   386 {
   387 	const char *env;
   388 	int status;
   389 	snd_pcm_hw_params_t *hwparams;
   390 	snd_pcm_uframes_t frames;
   391 	unsigned int periods;
   392 
   393 	/* Copy the hardware parameters for this setup */
   394 	snd_pcm_hw_params_alloca(&hwparams);
   395 	SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
   396 
   397 	if ( !override ) {
   398 		env = getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
   399 		if ( env ) {
   400 			override = SDL_atoi(env);
   401 			if ( override == 0 ) {
   402 				return(-1);
   403 			}
   404 		}
   405 	}
   406 
   407 	frames = spec->samples;
   408 	status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
   409 	if ( status < 0 ) {
   410 		return(-1);
   411 	}
   412 
   413 	periods = 2;
   414 	status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
   415 	if ( status < 0 ) {
   416 		return(-1);
   417 	}
   418 
   419 	return ALSA_finalize_hardware(this, spec, hwparams, override);
   420 }
   421 
   422 static int ALSA_set_buffer_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override)
   423 {
   424 	const char *env;
   425 	int status;
   426 	snd_pcm_hw_params_t *hwparams;
   427 	snd_pcm_uframes_t frames;
   428 
   429 	/* Copy the hardware parameters for this setup */
   430 	snd_pcm_hw_params_alloca(&hwparams);
   431 	SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
   432 
   433 	if ( !override ) {
   434 		env = getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
   435 		if ( env ) {
   436 			override = SDL_atoi(env);
   437 			if ( override == 0 ) {
   438 				return(-1);
   439 			}
   440 		}
   441 	}
   442 
   443 	frames = spec->samples * 2;
   444 	status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
   445 	if ( status < 0 ) {
   446 		return(-1);
   447 	}
   448 
   449 	return ALSA_finalize_hardware(this, spec, hwparams, override);
   450 }
   451 
   452 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
   453 {
   454 	int                  status;
   455 	snd_pcm_hw_params_t *hwparams;
   456 	snd_pcm_sw_params_t *swparams;
   457 	snd_pcm_format_t     format;
   458 	unsigned int         rate;
   459 	unsigned int 	     channels;
   460 	snd_pcm_uframes_t    bufsize;
   461 	Uint16               test_format;
   462 
   463 	/* Open the audio device */
   464 	/* Name of device should depend on # channels in spec */
   465 	status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   466 
   467 	if ( status < 0 ) {
   468 		SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
   469 		return(-1);
   470 	}
   471 
   472 	/* Figure out what the hardware is capable of */
   473 	snd_pcm_hw_params_alloca(&hwparams);
   474 	status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, hwparams);
   475 	if ( status < 0 ) {
   476 		SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
   477 		ALSA_CloseAudio(this);
   478 		return(-1);
   479 	}
   480 
   481 	/* SDL only uses interleaved sample output */
   482 	status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
   483 	if ( status < 0 ) {
   484 		SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
   485 		ALSA_CloseAudio(this);
   486 		return(-1);
   487 	}
   488 
   489 	/* Try for a closest match on audio format */
   490 	status = -1;
   491 	for ( test_format = SDL_FirstAudioFormat(spec->format);
   492 	      test_format && (status < 0); ) {
   493 		switch ( test_format ) {
   494 			case AUDIO_U8:
   495 				format = SND_PCM_FORMAT_U8;
   496 				break;
   497 			case AUDIO_S8:
   498 				format = SND_PCM_FORMAT_S8;
   499 				break;
   500 			case AUDIO_S16LSB:
   501 				format = SND_PCM_FORMAT_S16_LE;
   502 				break;
   503 			case AUDIO_S16MSB:
   504 				format = SND_PCM_FORMAT_S16_BE;
   505 				break;
   506 			case AUDIO_U16LSB:
   507 				format = SND_PCM_FORMAT_U16_LE;
   508 				break;
   509 			case AUDIO_U16MSB:
   510 				format = SND_PCM_FORMAT_U16_BE;
   511 				break;
   512 			default:
   513 				format = 0;
   514 				break;
   515 		}
   516 		if ( format != 0 ) {
   517 			status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, hwparams, format);
   518 		}
   519 		if ( status < 0 ) {
   520 			test_format = SDL_NextAudioFormat();
   521 		}
   522 	}
   523 	if ( status < 0 ) {
   524 		SDL_SetError("Couldn't find any hardware audio formats");
   525 		ALSA_CloseAudio(this);
   526 		return(-1);
   527 	}
   528 	spec->format = test_format;
   529 
   530 	/* Set the number of channels */
   531 	status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, hwparams, spec->channels);
   532 	channels = spec->channels;
   533 	if ( status < 0 ) {
   534 		status = SDL_NAME(snd_pcm_hw_params_get_channels)(hwparams, &channels);
   535 		if ( status < 0 ) {
   536 			SDL_SetError("Couldn't set audio channels");
   537 			ALSA_CloseAudio(this);
   538 			return(-1);
   539 		}
   540 		spec->channels = channels;
   541 	}
   542 
   543 	/* Set the audio rate */
   544 	rate = spec->freq;
   545 
   546 	status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, hwparams, &rate, NULL);
   547 	if ( status < 0 ) {
   548 		SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
   549 		ALSA_CloseAudio(this);
   550 		return(-1);
   551 	}
   552 	spec->freq = rate;
   553 
   554 	/* Set the buffer size, in samples */
   555 	if ( ALSA_set_period_size(this, spec, hwparams, 0) < 0 &&
   556 	     ALSA_set_buffer_size(this, spec, hwparams, 0) < 0 ) {
   557 		/* Failed to set desired buffer size, do the best you can... */
   558 		if ( ALSA_set_period_size(this, spec, hwparams, 1) < 0 ) {
   559 			SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
   560 			ALSA_CloseAudio(this);
   561 			return(-1);
   562 		}
   563 	}
   564 
   565 	/* Set the software parameters */
   566 	snd_pcm_sw_params_alloca(&swparams);
   567 	status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);
   568 	if ( status < 0 ) {
   569 		SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status));
   570 		ALSA_CloseAudio(this);
   571 		return(-1);
   572 	}
   573 	status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 1);
   574 	if ( status < 0 ) {
   575 		SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status));
   576 		ALSA_CloseAudio(this);
   577 		return(-1);
   578 	}
   579 	status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams);
   580 	if ( status < 0 ) {
   581 		SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status));
   582 		ALSA_CloseAudio(this);
   583 		return(-1);
   584 	}
   585 
   586 	/* Calculate the final parameters for this audio specification */
   587 	SDL_CalculateAudioSpec(spec);
   588 
   589 	/* Allocate mixing buffer */
   590 	mixlen = spec->size;
   591 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   592 	if ( mixbuf == NULL ) {
   593 		ALSA_CloseAudio(this);
   594 		return(-1);
   595 	}
   596 	SDL_memset(mixbuf, spec->silence, spec->size);
   597 
   598 	/* Switch to blocking mode for playback */
   599 	SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
   600 
   601 	/* We're ready to rock and roll. :-) */
   602 	return(0);
   603 }