src/audio/alsa/SDL_alsa_audio.c
author Sam Lantinga
Mon, 19 Oct 2009 05:24:48 +0000
branchSDL-1.2
changeset 4362 e48a66bed093
parent 4361 991e4bcfc7a8
child 4363 028d26915548
permissions -rw-r--r--
ALSA write failed (unrecoverable): Resource temporarily unavailable
     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 			if ( status == -EAGAIN ) {
   318 				/* Apparently snd_pcm_recover() doesn't handle this. Foo. */
   319 				SDL_Delay(1);
   320 				continue;
   321 			}
   322 			status = SDL_NAME(snd_pcm_recover)(pcm_handle, status, 0);
   323 			if ( status < 0 ) {
   324 				/* Hmm, not much we can do - abort */
   325 				fprintf(stderr, "ALSA write failed (unrecoverable): %s", SDL_NAME(snd_strerror)(status));
   326 				this->enabled = 0;
   327 				return;
   328 			}
   329 			continue;
   330 		}
   331 		sample_buf += status * frame_size;
   332 		frames_left -= status;
   333 	}
   334 }
   335 
   336 static Uint8 *ALSA_GetAudioBuf(_THIS)
   337 {
   338 	return(mixbuf);
   339 }
   340 
   341 static void ALSA_CloseAudio(_THIS)
   342 {
   343 	if ( mixbuf != NULL ) {
   344 		SDL_FreeAudioMem(mixbuf);
   345 		mixbuf = NULL;
   346 	}
   347 	if ( pcm_handle ) {
   348 		SDL_NAME(snd_pcm_drain)(pcm_handle);
   349 		SDL_NAME(snd_pcm_close)(pcm_handle);
   350 		pcm_handle = NULL;
   351 	}
   352 }
   353 
   354 static int ALSA_finalize_hardware(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *hwparams, int override)
   355 {
   356 	int status;
   357 	snd_pcm_uframes_t bufsize;
   358 
   359 	/* "set" the hardware with the desired parameters */
   360 	status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
   361 	if ( status < 0 ) {
   362 		return(-1);
   363 	}
   364 
   365 	/* Get samples for the actual buffer size */
   366 	status = SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
   367 	if ( status < 0 ) {
   368 		return(-1);
   369 	}
   370 	if ( !override && bufsize != spec->samples * 2 ) {
   371 		return(-1);
   372 	}
   373 
   374 	/* FIXME: Is this safe to do? */
   375 	spec->samples = bufsize / 2;
   376 
   377 	/* This is useful for debugging */
   378 	if ( getenv("SDL_AUDIO_ALSA_DEBUG") ) {
   379 		snd_pcm_sframes_t persize = 0;
   380 		unsigned int periods = 0;
   381 
   382 		SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, NULL);
   383 		SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, NULL);
   384 
   385 		fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
   386 	}
   387 	return(0);
   388 }
   389 
   390 static int ALSA_set_period_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override)
   391 {
   392 	const char *env;
   393 	int status;
   394 	snd_pcm_hw_params_t *hwparams;
   395 	snd_pcm_uframes_t frames;
   396 	unsigned int periods;
   397 
   398 	/* Copy the hardware parameters for this setup */
   399 	snd_pcm_hw_params_alloca(&hwparams);
   400 	SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
   401 
   402 	if ( !override ) {
   403 		env = getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE");
   404 		if ( env ) {
   405 			override = SDL_atoi(env);
   406 			if ( override == 0 ) {
   407 				return(-1);
   408 			}
   409 		}
   410 	}
   411 
   412 	frames = spec->samples;
   413 	status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
   414 	if ( status < 0 ) {
   415 		return(-1);
   416 	}
   417 
   418 	periods = 2;
   419 	status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
   420 	if ( status < 0 ) {
   421 		return(-1);
   422 	}
   423 
   424 	return ALSA_finalize_hardware(this, spec, hwparams, override);
   425 }
   426 
   427 static int ALSA_set_buffer_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override)
   428 {
   429 	const char *env;
   430 	int status;
   431 	snd_pcm_hw_params_t *hwparams;
   432 	snd_pcm_uframes_t frames;
   433 
   434 	/* Copy the hardware parameters for this setup */
   435 	snd_pcm_hw_params_alloca(&hwparams);
   436 	SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
   437 
   438 	if ( !override ) {
   439 		env = getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
   440 		if ( env ) {
   441 			override = SDL_atoi(env);
   442 			if ( override == 0 ) {
   443 				return(-1);
   444 			}
   445 		}
   446 	}
   447 
   448 	frames = spec->samples * 2;
   449 	status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
   450 	if ( status < 0 ) {
   451 		return(-1);
   452 	}
   453 
   454 	return ALSA_finalize_hardware(this, spec, hwparams, override);
   455 }
   456 
   457 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
   458 {
   459 	int                  status;
   460 	snd_pcm_hw_params_t *hwparams;
   461 	snd_pcm_sw_params_t *swparams;
   462 	snd_pcm_format_t     format;
   463 	unsigned int         rate;
   464 	unsigned int 	     channels;
   465 	snd_pcm_uframes_t    bufsize;
   466 	Uint16               test_format;
   467 
   468 	/* Open the audio device */
   469 	/* Name of device should depend on # channels in spec */
   470 	status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   471 
   472 	if ( status < 0 ) {
   473 		SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
   474 		return(-1);
   475 	}
   476 
   477 	/* Figure out what the hardware is capable of */
   478 	snd_pcm_hw_params_alloca(&hwparams);
   479 	status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, hwparams);
   480 	if ( status < 0 ) {
   481 		SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
   482 		ALSA_CloseAudio(this);
   483 		return(-1);
   484 	}
   485 
   486 	/* SDL only uses interleaved sample output */
   487 	status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
   488 	if ( status < 0 ) {
   489 		SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
   490 		ALSA_CloseAudio(this);
   491 		return(-1);
   492 	}
   493 
   494 	/* Try for a closest match on audio format */
   495 	status = -1;
   496 	for ( test_format = SDL_FirstAudioFormat(spec->format);
   497 	      test_format && (status < 0); ) {
   498 		switch ( test_format ) {
   499 			case AUDIO_U8:
   500 				format = SND_PCM_FORMAT_U8;
   501 				break;
   502 			case AUDIO_S8:
   503 				format = SND_PCM_FORMAT_S8;
   504 				break;
   505 			case AUDIO_S16LSB:
   506 				format = SND_PCM_FORMAT_S16_LE;
   507 				break;
   508 			case AUDIO_S16MSB:
   509 				format = SND_PCM_FORMAT_S16_BE;
   510 				break;
   511 			case AUDIO_U16LSB:
   512 				format = SND_PCM_FORMAT_U16_LE;
   513 				break;
   514 			case AUDIO_U16MSB:
   515 				format = SND_PCM_FORMAT_U16_BE;
   516 				break;
   517 			default:
   518 				format = 0;
   519 				break;
   520 		}
   521 		if ( format != 0 ) {
   522 			status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, hwparams, format);
   523 		}
   524 		if ( status < 0 ) {
   525 			test_format = SDL_NextAudioFormat();
   526 		}
   527 	}
   528 	if ( status < 0 ) {
   529 		SDL_SetError("Couldn't find any hardware audio formats");
   530 		ALSA_CloseAudio(this);
   531 		return(-1);
   532 	}
   533 	spec->format = test_format;
   534 
   535 	/* Set the number of channels */
   536 	status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, hwparams, spec->channels);
   537 	channels = spec->channels;
   538 	if ( status < 0 ) {
   539 		status = SDL_NAME(snd_pcm_hw_params_get_channels)(hwparams, &channels);
   540 		if ( status < 0 ) {
   541 			SDL_SetError("Couldn't set audio channels");
   542 			ALSA_CloseAudio(this);
   543 			return(-1);
   544 		}
   545 		spec->channels = channels;
   546 	}
   547 
   548 	/* Set the audio rate */
   549 	rate = spec->freq;
   550 
   551 	status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, hwparams, &rate, NULL);
   552 	if ( status < 0 ) {
   553 		SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
   554 		ALSA_CloseAudio(this);
   555 		return(-1);
   556 	}
   557 	spec->freq = rate;
   558 
   559 	/* Set the buffer size, in samples */
   560 	if ( ALSA_set_period_size(this, spec, hwparams, 0) < 0 &&
   561 	     ALSA_set_buffer_size(this, spec, hwparams, 0) < 0 ) {
   562 		/* Failed to set desired buffer size, do the best you can... */
   563 		if ( ALSA_set_period_size(this, spec, hwparams, 1) < 0 ) {
   564 			SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
   565 			ALSA_CloseAudio(this);
   566 			return(-1);
   567 		}
   568 	}
   569 
   570 	/* Set the software parameters */
   571 	snd_pcm_sw_params_alloca(&swparams);
   572 	status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);
   573 	if ( status < 0 ) {
   574 		SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status));
   575 		ALSA_CloseAudio(this);
   576 		return(-1);
   577 	}
   578 	status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 1);
   579 	if ( status < 0 ) {
   580 		SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status));
   581 		ALSA_CloseAudio(this);
   582 		return(-1);
   583 	}
   584 	status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams);
   585 	if ( status < 0 ) {
   586 		SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status));
   587 		ALSA_CloseAudio(this);
   588 		return(-1);
   589 	}
   590 
   591 	/* Calculate the final parameters for this audio specification */
   592 	SDL_CalculateAudioSpec(spec);
   593 
   594 	/* Allocate mixing buffer */
   595 	mixlen = spec->size;
   596 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   597 	if ( mixbuf == NULL ) {
   598 		ALSA_CloseAudio(this);
   599 		return(-1);
   600 	}
   601 	SDL_memset(mixbuf, spec->silence, spec->size);
   602 
   603 	/* Switch to blocking mode for playback */
   604 	SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
   605 
   606 	/* We're ready to rock and roll. :-) */
   607 	return(0);
   608 }