Home | History | Annotate | Download | only in alsa
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2012 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 (at) 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_set_avail_min))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams, snd_pcm_uframes_t val);
     85 static int (*SDL_NAME(snd_pcm_sw_params_current))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams);
     86 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);
     87 static int (*SDL_NAME(snd_pcm_sw_params))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
     88 static int (*SDL_NAME(snd_pcm_nonblock))(snd_pcm_t *pcm, int nonblock);
     89 static int (*SDL_NAME(snd_pcm_wait))(snd_pcm_t *pcm, int timeout);
     90 #define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof)
     91 #define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof)
     92 
     93 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
     94 static struct {
     95 	const char *name;
     96 	void **func;
     97 } alsa_functions[] = {
     98 	{ "snd_pcm_open",	(void**)(char*)&SDL_NAME(snd_pcm_open)		},
     99 	{ "snd_pcm_close",	(void**)(char*)&SDL_NAME(snd_pcm_close)	},
    100 	{ "snd_pcm_writei",	(void**)(char*)&SDL_NAME(snd_pcm_writei)	},
    101 	{ "snd_pcm_recover",	(void**)(char*)&SDL_NAME(snd_pcm_recover)	},
    102 	{ "snd_pcm_prepare",	(void**)(char*)&SDL_NAME(snd_pcm_prepare)	},
    103 	{ "snd_pcm_drain",	(void**)(char*)&SDL_NAME(snd_pcm_drain)	},
    104 	{ "snd_strerror",	(void**)(char*)&SDL_NAME(snd_strerror)		},
    105 	{ "snd_pcm_hw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof)		},
    106 	{ "snd_pcm_sw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof)		},
    107 	{ "snd_pcm_hw_params_copy",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_copy)		},
    108 	{ "snd_pcm_hw_params_any",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_any)		},
    109 	{ "snd_pcm_hw_params_set_access",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_access)		},
    110 	{ "snd_pcm_hw_params_set_format",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_format)		},
    111 	{ "snd_pcm_hw_params_set_channels",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_channels)	},
    112 	{ "snd_pcm_hw_params_get_channels",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_channels)	},
    113 	{ "snd_pcm_hw_params_set_rate_near",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_rate_near)	},
    114 	{ "snd_pcm_hw_params_set_period_size_near",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_period_size_near)	},
    115 	{ "snd_pcm_hw_params_get_period_size",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_period_size)	},
    116 	{ "snd_pcm_hw_params_set_periods_near",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_periods_near)	},
    117 	{ "snd_pcm_hw_params_get_periods",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_periods)	},
    118 	{ "snd_pcm_hw_params_set_buffer_size_near",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_set_buffer_size_near) },
    119 	{ "snd_pcm_hw_params_get_buffer_size",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params_get_buffer_size) },
    120 	{ "snd_pcm_hw_params",	(void**)(char*)&SDL_NAME(snd_pcm_hw_params)	},
    121 	{ "snd_pcm_sw_params_set_avail_min",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_avail_min) },
    122 	{ "snd_pcm_sw_params_current",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params_current)	},
    123 	{ "snd_pcm_sw_params_set_start_threshold",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_start_threshold)	},
    124 	{ "snd_pcm_sw_params",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params)	},
    125 	{ "snd_pcm_nonblock",	(void**)(char*)&SDL_NAME(snd_pcm_nonblock)	},
    126 	{ "snd_pcm_wait",	(void**)(char*)&SDL_NAME(snd_pcm_wait)	},
    127 };
    128 
    129 static void UnloadALSALibrary(void) {
    130 	if (alsa_loaded) {
    131 		SDL_UnloadObject(alsa_handle);
    132 		alsa_handle = NULL;
    133 		alsa_loaded = 0;
    134 	}
    135 }
    136 
    137 static int LoadALSALibrary(void) {
    138 	int i, retval = -1;
    139 
    140 	alsa_handle = SDL_LoadObject(alsa_library);
    141 	if (alsa_handle) {
    142 		alsa_loaded = 1;
    143 		retval = 0;
    144 		for (i = 0; i < SDL_arraysize(alsa_functions); i++) {
    145 			*alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);
    146 			if (!*alsa_functions[i].func) {
    147 				retval = -1;
    148 				UnloadALSALibrary();
    149 				break;
    150 			}
    151 		}
    152 	}
    153 	return retval;
    154 }
    155 
    156 #else
    157 
    158 static void UnloadALSALibrary(void) {
    159 	return;
    160 }
    161 
    162 static int LoadALSALibrary(void) {
    163 	return 0;
    164 }
    165 
    166 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
    167 
    168 static const char *get_audio_device(int channels)
    169 {
    170 	const char *device;
    171 
    172 	device = SDL_getenv("AUDIODEV");	/* Is there a standard variable name? */
    173 	if ( device == NULL ) {
    174 		switch (channels) {
    175 		case 6:
    176 			device = "plug:surround51";
    177 			break;
    178 		case 4:
    179 			device = "plug:surround40";
    180 			break;
    181 		default:
    182 			device = "default";
    183 			break;
    184 		}
    185 	}
    186 	return device;
    187 }
    188 
    189 /* Audio driver bootstrap functions */
    190 
    191 static int Audio_Available(void)
    192 {
    193 	int available;
    194 	int status;
    195 	snd_pcm_t *handle;
    196 
    197 	available = 0;
    198 	if (LoadALSALibrary() < 0) {
    199 		return available;
    200 	}
    201 	status = SDL_NAME(snd_pcm_open)(&handle, get_audio_device(2), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
    202 	if ( status >= 0 ) {
    203 		available = 1;
    204         	SDL_NAME(snd_pcm_close)(handle);
    205 	}
    206 	UnloadALSALibrary();
    207 	return(available);
    208 }
    209 
    210 static void Audio_DeleteDevice(SDL_AudioDevice *device)
    211 {
    212 	SDL_free(device->hidden);
    213 	SDL_free(device);
    214 	UnloadALSALibrary();
    215 }
    216 
    217 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
    218 {
    219 	SDL_AudioDevice *this;
    220 
    221 	/* Initialize all variables that we clean on shutdown */
    222 	LoadALSALibrary();
    223 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
    224 	if ( this ) {
    225 		SDL_memset(this, 0, (sizeof *this));
    226 		this->hidden = (struct SDL_PrivateAudioData *)
    227 				SDL_malloc((sizeof *this->hidden));
    228 	}
    229 	if ( (this == NULL) || (this->hidden == NULL) ) {
    230 		SDL_OutOfMemory();
    231 		if ( this ) {
    232 			SDL_free(this);
    233 		}
    234 		return(0);
    235 	}
    236 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
    237 
    238 	/* Set the function pointers */
    239 	this->OpenAudio = ALSA_OpenAudio;
    240 	this->WaitAudio = ALSA_WaitAudio;
    241 	this->PlayAudio = ALSA_PlayAudio;
    242 	this->GetAudioBuf = ALSA_GetAudioBuf;
    243 	this->CloseAudio = ALSA_CloseAudio;
    244 
    245 	this->free = Audio_DeleteDevice;
    246 
    247 	return this;
    248 }
    249 
    250 AudioBootStrap ALSA_bootstrap = {
    251 	DRIVER_NAME, "ALSA PCM audio",
    252 	Audio_Available, Audio_CreateDevice
    253 };
    254 
    255 /* This function waits until it is possible to write a full sound buffer */
    256 static void ALSA_WaitAudio(_THIS)
    257 {
    258 	/* We're in blocking mode, so there's nothing to do here */
    259 }
    260 
    261 
    262 /*
    263  * http://bugzilla.libsdl.org/show_bug.cgi?id=110
    264  * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
    265  *  and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
    266  */
    267 #define SWIZ6(T) \
    268     T *ptr = (T *) mixbuf; \
    269     Uint32 i; \
    270     for (i = 0; i < this->spec.samples; i++, ptr += 6) { \
    271         T tmp; \
    272         tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
    273         tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
    274     }
    275 
    276 static __inline__ void swizzle_alsa_channels_6_64bit(_THIS) { SWIZ6(Uint64); }
    277 static __inline__ void swizzle_alsa_channels_6_32bit(_THIS) { SWIZ6(Uint32); }
    278 static __inline__ void swizzle_alsa_channels_6_16bit(_THIS) { SWIZ6(Uint16); }
    279 static __inline__ void swizzle_alsa_channels_6_8bit(_THIS) { SWIZ6(Uint8); }
    280 
    281 #undef SWIZ6
    282 
    283 
    284 /*
    285  * Called right before feeding this->mixbuf to the hardware. Swizzle channels
    286  *  from Windows/Mac order to the format alsalib will want.
    287  */
    288 static __inline__ void swizzle_alsa_channels(_THIS)
    289 {
    290     if (this->spec.channels == 6) {
    291         const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */
    292         if (fmtsize == 16)
    293             swizzle_alsa_channels_6_16bit(this);
    294         else if (fmtsize == 8)
    295             swizzle_alsa_channels_6_8bit(this);
    296         else if (fmtsize == 32)
    297             swizzle_alsa_channels_6_32bit(this);
    298         else if (fmtsize == 64)
    299             swizzle_alsa_channels_6_64bit(this);
    300     }
    301 
    302     /* !!! FIXME: update this for 7.1 if needed, later. */
    303 }
    304 
    305 
    306 static void ALSA_PlayAudio(_THIS)
    307 {
    308 	int status;
    309 	snd_pcm_uframes_t frames_left;
    310 	const Uint8 *sample_buf = (const Uint8 *) mixbuf;
    311 	const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) * this->spec.channels;
    312 
    313 	swizzle_alsa_channels(this);
    314 
    315 	frames_left = ((snd_pcm_uframes_t) this->spec.samples);
    316 
    317 	while ( frames_left > 0 && this->enabled ) {
    318 		/* This works, but needs more testing before going live */
    319 		/*SDL_NAME(snd_pcm_wait)(pcm_handle, -1);*/
    320 
    321 		status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, frames_left);
    322 		if ( status < 0 ) {
    323 			if ( status == -EAGAIN ) {
    324 				/* Apparently snd_pcm_recover() doesn't handle this case - does it assume snd_pcm_wait() above? */
    325 				SDL_Delay(1);
    326 				continue;
    327 			}
    328 			status = SDL_NAME(snd_pcm_recover)(pcm_handle, status, 0);
    329 			if ( status < 0 ) {
    330 				/* Hmm, not much we can do - abort */
    331 				fprintf(stderr, "ALSA write failed (unrecoverable): %s\n", SDL_NAME(snd_strerror)(status));
    332 				this->enabled = 0;
    333 				return;
    334 			}
    335 			continue;
    336 		}
    337 		sample_buf += status * frame_size;
    338 		frames_left -= status;
    339 	}
    340 }
    341 
    342 static Uint8 *ALSA_GetAudioBuf(_THIS)
    343 {
    344 	return(mixbuf);
    345 }
    346 
    347 static void ALSA_CloseAudio(_THIS)
    348 {
    349 	if ( mixbuf != NULL ) {
    350 		SDL_FreeAudioMem(mixbuf);
    351 		mixbuf = NULL;
    352 	}
    353 	if ( pcm_handle ) {
    354 		SDL_NAME(snd_pcm_drain)(pcm_handle);
    355 		SDL_NAME(snd_pcm_close)(pcm_handle);
    356 		pcm_handle = NULL;
    357 	}
    358 }
    359 
    360 static int ALSA_finalize_hardware(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *hwparams, int override)
    361 {
    362 	int status;
    363 	snd_pcm_uframes_t bufsize;
    364 
    365 	/* "set" the hardware with the desired parameters */
    366 	status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
    367 	if ( status < 0 ) {
    368 		return(-1);
    369 	}
    370 
    371 	/* Get samples for the actual buffer size */
    372 	status = SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
    373 	if ( status < 0 ) {
    374 		return(-1);
    375 	}
    376 	if ( !override && bufsize != spec->samples * 2 ) {
    377 		return(-1);
    378 	}
    379 
    380 	/* FIXME: Is this safe to do? */
    381 	spec->samples = bufsize / 2;
    382 
    383 	/* This is useful for debugging */
    384 	if ( getenv("SDL_AUDIO_ALSA_DEBUG") ) {
    385 		snd_pcm_uframes_t persize = 0;
    386 		unsigned int periods = 0;
    387 
    388 		SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, NULL);
    389 		SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, NULL);
    390 
    391 		fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
    392 	}
    393 	return(0);
    394 }
    395 
    396 static int ALSA_set_period_size(_THIS, SDL_AudioSpec *spec, 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 	SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
    407 
    408 	if ( !override ) {
    409 		env = 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 = spec->samples;
    419 	status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
    420 	if ( status < 0 ) {
    421 		return(-1);
    422 	}
    423 
    424 	periods = 2;
    425 	status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
    426 	if ( status < 0 ) {
    427 		return(-1);
    428 	}
    429 
    430 	return ALSA_finalize_hardware(this, spec, hwparams, override);
    431 }
    432 
    433 static int ALSA_set_buffer_size(_THIS, SDL_AudioSpec *spec, snd_pcm_hw_params_t *params, int override)
    434 {
    435 	const char *env;
    436 	int status;
    437 	snd_pcm_hw_params_t *hwparams;
    438 	snd_pcm_uframes_t frames;
    439 
    440 	/* Copy the hardware parameters for this setup */
    441 	snd_pcm_hw_params_alloca(&hwparams);
    442 	SDL_NAME(snd_pcm_hw_params_copy)(hwparams, params);
    443 
    444 	if ( !override ) {
    445 		env = getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE");
    446 		if ( env ) {
    447 			override = SDL_atoi(env);
    448 			if ( override == 0 ) {
    449 				return(-1);
    450 			}
    451 		}
    452 	}
    453 
    454 	frames = spec->samples * 2;
    455 	status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
    456 	if ( status < 0 ) {
    457 		return(-1);
    458 	}
    459 
    460 	return ALSA_finalize_hardware(this, spec, hwparams, override);
    461 }
    462 
    463 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
    464 {
    465 	int                  status;
    466 	snd_pcm_hw_params_t *hwparams;
    467 	snd_pcm_sw_params_t *swparams;
    468 	snd_pcm_format_t     format;
    469 	unsigned int         rate;
    470 	unsigned int 	     channels;
    471 	Uint16               test_format;
    472 
    473 	/* Open the audio device */
    474 	/* Name of device should depend on # channels in spec */
    475 	status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
    476 
    477 	if ( status < 0 ) {
    478 		SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
    479 		return(-1);
    480 	}
    481 
    482 	/* Figure out what the hardware is capable of */
    483 	snd_pcm_hw_params_alloca(&hwparams);
    484 	status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, hwparams);
    485 	if ( status < 0 ) {
    486 		SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
    487 		ALSA_CloseAudio(this);
    488 		return(-1);
    489 	}
    490 
    491 	/* SDL only uses interleaved sample output */
    492 	status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
    493 	if ( status < 0 ) {
    494 		SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
    495 		ALSA_CloseAudio(this);
    496 		return(-1);
    497 	}
    498 
    499 	/* Try for a closest match on audio format */
    500 	status = -1;
    501 	for ( test_format = SDL_FirstAudioFormat(spec->format);
    502 	      test_format && (status < 0); ) {
    503 		switch ( test_format ) {
    504 			case AUDIO_U8:
    505 				format = SND_PCM_FORMAT_U8;
    506 				break;
    507 			case AUDIO_S8:
    508 				format = SND_PCM_FORMAT_S8;
    509 				break;
    510 			case AUDIO_S16LSB:
    511 				format = SND_PCM_FORMAT_S16_LE;
    512 				break;
    513 			case AUDIO_S16MSB:
    514 				format = SND_PCM_FORMAT_S16_BE;
    515 				break;
    516 			case AUDIO_U16LSB:
    517 				format = SND_PCM_FORMAT_U16_LE;
    518 				break;
    519 			case AUDIO_U16MSB:
    520 				format = SND_PCM_FORMAT_U16_BE;
    521 				break;
    522 			default:
    523 				format = 0;
    524 				break;
    525 		}
    526 		if ( format != 0 ) {
    527 			status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, hwparams, format);
    528 		}
    529 		if ( status < 0 ) {
    530 			test_format = SDL_NextAudioFormat();
    531 		}
    532 	}
    533 	if ( status < 0 ) {
    534 		SDL_SetError("Couldn't find any hardware audio formats");
    535 		ALSA_CloseAudio(this);
    536 		return(-1);
    537 	}
    538 	spec->format = test_format;
    539 
    540 	/* Set the number of channels */
    541 	status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, hwparams, spec->channels);
    542 	channels = spec->channels;
    543 	if ( status < 0 ) {
    544 		status = SDL_NAME(snd_pcm_hw_params_get_channels)(hwparams, &channels);
    545 		if ( status < 0 ) {
    546 			SDL_SetError("Couldn't set audio channels");
    547 			ALSA_CloseAudio(this);
    548 			return(-1);
    549 		}
    550 		spec->channels = channels;
    551 	}
    552 
    553 	/* Set the audio rate */
    554 	rate = spec->freq;
    555 
    556 	status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, hwparams, &rate, NULL);
    557 	if ( status < 0 ) {
    558 		SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
    559 		ALSA_CloseAudio(this);
    560 		return(-1);
    561 	}
    562 	spec->freq = rate;
    563 
    564 	/* Set the buffer size, in samples */
    565 	if ( ALSA_set_period_size(this, spec, hwparams, 0) < 0 &&
    566 	     ALSA_set_buffer_size(this, spec, hwparams, 0) < 0 ) {
    567 		/* Failed to set desired buffer size, do the best you can... */
    568 		if ( ALSA_set_period_size(this, spec, hwparams, 1) < 0 ) {
    569 			SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
    570 			ALSA_CloseAudio(this);
    571 			return(-1);
    572 		}
    573 	}
    574 
    575 	/* Set the software parameters */
    576 	snd_pcm_sw_params_alloca(&swparams);
    577 	status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);
    578 	if ( status < 0 ) {
    579 		SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status));
    580 		ALSA_CloseAudio(this);
    581 		return(-1);
    582 	}
    583 	status = SDL_NAME(snd_pcm_sw_params_set_avail_min)(pcm_handle, swparams, spec->samples);
    584 	if ( status < 0 ) {
    585 		SDL_SetError("Couldn't set minimum available samples: %s", SDL_NAME(snd_strerror)(status));
    586 		ALSA_CloseAudio(this);
    587 		return(-1);
    588 	}
    589 	status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 1);
    590 	if ( status < 0 ) {
    591 		SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status));
    592 		ALSA_CloseAudio(this);
    593 		return(-1);
    594 	}
    595 	status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams);
    596 	if ( status < 0 ) {
    597 		SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status));
    598 		ALSA_CloseAudio(this);
    599 		return(-1);
    600 	}
    601 
    602 	/* Calculate the final parameters for this audio specification */
    603 	SDL_CalculateAudioSpec(spec);
    604 
    605 	/* Allocate mixing buffer */
    606 	mixlen = spec->size;
    607 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
    608 	if ( mixbuf == NULL ) {
    609 		ALSA_CloseAudio(this);
    610 		return(-1);
    611 	}
    612 	SDL_memset(mixbuf, spec->silence, spec->size);
    613 
    614 	/* Switch to blocking mode for playback */
    615 	SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
    616 
    617 	/* We're ready to rock and roll. :-) */
    618 	return(0);
    619 }
    620