Home | History | Annotate | Download | only in alsa
      1 /*
      2     SDL - Simple DirectMedia Layer
      3     Copyright (C) 1997-2004 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 <dlfcn.h>
     37 #include "SDL_name.h"
     38 #include "SDL_loadso.h"
     39 #else
     40 #define SDL_NAME(X)	X
     41 #endif
     42 
     43 
     44 /* The tag name used by ALSA audio */
     45 #define DRIVER_NAME         "alsa"
     46 
     47 /* The default ALSA audio driver */
     48 #define DEFAULT_DEVICE	"default"
     49 
     50 /* Audio driver functions */
     51 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec);
     52 static void ALSA_WaitAudio(_THIS);
     53 static void ALSA_PlayAudio(_THIS);
     54 static Uint8 *ALSA_GetAudioBuf(_THIS);
     55 static void ALSA_CloseAudio(_THIS);
     56 
     57 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
     58 
     59 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
     60 static void *alsa_handle = NULL;
     61 static int alsa_loaded = 0;
     62 
     63 static int (*SDL_snd_pcm_open)(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
     64 static int (*SDL_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
     65 static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm);
     66 static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
     67 static int (*SDL_NAME(snd_pcm_resume))(snd_pcm_t *pcm);
     68 static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);
     69 static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);
     70 static const char *(*SDL_NAME(snd_strerror))(int errnum);
     71 static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
     72 static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void);
     73 static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
     74 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);
     75 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);
     76 static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
     77 static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params);
     78 static unsigned 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);
     79 static snd_pcm_uframes_t (*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);
     80 static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_hw_params_get_period_size))(const snd_pcm_hw_params_t *params);
     81 static unsigned 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);
     82 static int (*SDL_NAME(snd_pcm_hw_params_get_periods))(snd_pcm_hw_params_t *params);
     83 static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
     84 /*
     85 */
     86 static int (*SDL_NAME(snd_pcm_sw_params_current))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams);
     87 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);
     88 static int (*SDL_NAME(snd_pcm_sw_params_set_avail_min))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params, snd_pcm_uframes_t val);
     89 static int (*SDL_NAME(snd_pcm_sw_params))(snd_pcm_t *pcm, snd_pcm_sw_params_t *params);
     90 static int (*SDL_NAME(snd_pcm_nonblock))(snd_pcm_t *pcm, int nonblock);
     91 #define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof)
     92 #define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof)
     93 
     94 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
     95 static struct {
     96 	const char *name;
     97 	void **func;
     98 } alsa_functions[] = {
     99 	{ "snd_pcm_open",	(void**)(char*)&SDL_NAME(snd_pcm_open)		},
    100 	{ "snd_pcm_close",	(void**)(char*)&SDL_NAME(snd_pcm_close)	},
    101 	{ "snd_pcm_writei",	(void**)(char*)&SDL_NAME(snd_pcm_writei)	},
    102 	{ "snd_pcm_resume",	(void**)(char*)&SDL_NAME(snd_pcm_resume)	},
    103 	{ "snd_pcm_prepare",	(void**)(char*)&SDL_NAME(snd_pcm_prepare)	},
    104 	{ "snd_pcm_drain",	(void**)(char*)&SDL_NAME(snd_pcm_drain)	},
    105 	{ "snd_strerror",	(void**)(char*)&SDL_NAME(snd_strerror)		},
    106 	{ "snd_pcm_hw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof)		},
    107 	{ "snd_pcm_sw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof)		},
    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",	(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_set_avail_min",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_avail_min)	},
    122 	{ "snd_pcm_sw_params",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params)	},
    123 	{ "snd_pcm_nonblock",	(void**)(char*)&SDL_NAME(snd_pcm_nonblock)	},
    124 };
    125 
    126 static void UnloadALSALibrary(void) {
    127 	if (alsa_loaded) {
    128 /*		SDL_UnloadObject(alsa_handle);*/
    129 		dlclose(alsa_handle);
    130 		alsa_handle = NULL;
    131 		alsa_loaded = 0;
    132 	}
    133 }
    134 
    135 static int LoadALSALibrary(void) {
    136 	int i, retval = -1;
    137 
    138 /*	alsa_handle = SDL_LoadObject(alsa_library);*/
    139 	alsa_handle = dlopen(alsa_library,RTLD_NOW);
    140 	if (alsa_handle) {
    141 		alsa_loaded = 1;
    142 		retval = 0;
    143 		for (i = 0; i < SDL_arraysize(alsa_functions); i++) {
    144 /*			*alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);*/
    145 #if HAVE_DLVSYM
    146 			*alsa_functions[i].func = dlvsym(alsa_handle,alsa_functions[i].name,"ALSA_0.9");
    147 			if (!*alsa_functions[i].func)
    148 #endif
    149 				*alsa_functions[i].func = dlsym(alsa_handle,alsa_functions[i].name);
    150 			if (!*alsa_functions[i].func) {
    151 				retval = -1;
    152 				UnloadALSALibrary();
    153 				break;
    154 			}
    155 		}
    156 	}
    157 	return retval;
    158 }
    159 
    160 #else
    161 
    162 static void UnloadALSALibrary(void) {
    163 	return;
    164 }
    165 
    166 static int LoadALSALibrary(void) {
    167 	return 0;
    168 }
    169 
    170 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
    171 
    172 static const char *get_audio_device(int channels)
    173 {
    174 	const char *device;
    175 
    176 	device = SDL_getenv("AUDIODEV");	/* Is there a standard variable name? */
    177 	if ( device == NULL ) {
    178 		if (channels == 6) device = "surround51";
    179 		else if (channels == 4) device = "surround40";
    180 		else device = DEFAULT_DEVICE;
    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 0.9 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 	/* Check to see if the thread-parent process is still alive */
    255 	{ static int cnt = 0;
    256 		/* Note that this only works with thread implementations
    257 		   that use a different process id for each thread.
    258 		*/
    259 		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
    260 			if ( kill(parent, 0) < 0 ) {
    261 				this->enabled = 0;
    262 			}
    263 		}
    264 	}
    265 }
    266 
    267 
    268 /*
    269  * http://bugzilla.libsdl.org/show_bug.cgi?id=110
    270  * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
    271  *  and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
    272  */
    273 #define SWIZ6(T) \
    274     T *ptr = (T *) mixbuf; \
    275     const Uint32 count = (this->spec.samples / 6); \
    276     Uint32 i; \
    277     for (i = 0; i < count; i++, ptr += 6) { \
    278         T tmp; \
    279         tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
    280         tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
    281     }
    282 
    283 static __inline__ void swizzle_alsa_channels_6_64bit(_THIS) { SWIZ6(Uint64); }
    284 static __inline__ void swizzle_alsa_channels_6_32bit(_THIS) { SWIZ6(Uint32); }
    285 static __inline__ void swizzle_alsa_channels_6_16bit(_THIS) { SWIZ6(Uint16); }
    286 static __inline__ void swizzle_alsa_channels_6_8bit(_THIS) { SWIZ6(Uint8); }
    287 
    288 #undef SWIZ6
    289 
    290 
    291 /*
    292  * Called right before feeding this->mixbuf to the hardware. Swizzle channels
    293  *  from Windows/Mac order to the format alsalib will want.
    294  */
    295 static __inline__ void swizzle_alsa_channels(_THIS)
    296 {
    297     if (this->spec.channels == 6) {
    298         const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */
    299         if (fmtsize == 16)
    300             swizzle_alsa_channels_6_16bit(this);
    301         else if (fmtsize == 8)
    302             swizzle_alsa_channels_6_8bit(this);
    303         else if (fmtsize == 32)
    304             swizzle_alsa_channels_6_32bit(this);
    305         else if (fmtsize == 64)
    306             swizzle_alsa_channels_6_64bit(this);
    307     }
    308 
    309     /* !!! FIXME: update this for 7.1 if needed, later. */
    310 }
    311 
    312 
    313 static void ALSA_PlayAudio(_THIS)
    314 {
    315 	int           status;
    316 	int           sample_len;
    317 	signed short *sample_buf;
    318 
    319 	swizzle_alsa_channels(this);
    320 
    321 	sample_len = this->spec.samples;
    322 	sample_buf = (signed short *)mixbuf;
    323 
    324 	while ( sample_len > 0 ) {
    325 		status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, sample_len);
    326 		if ( status < 0 ) {
    327 			if ( status == -EAGAIN ) {
    328 				SDL_Delay(1);
    329 				continue;
    330 			}
    331 			if ( status == -ESTRPIPE ) {
    332 				do {
    333 					SDL_Delay(1);
    334 					status = SDL_NAME(snd_pcm_resume)(pcm_handle);
    335 				} while ( status == -EAGAIN );
    336 			}
    337 			if ( status < 0 ) {
    338 				status = SDL_NAME(snd_pcm_prepare)(pcm_handle);
    339 			}
    340 			if ( status < 0 ) {
    341 				/* Hmm, not much we can do - abort */
    342 				this->enabled = 0;
    343 				return;
    344 			}
    345 			continue;
    346 		}
    347 		sample_buf += status * this->spec.channels;
    348 		sample_len -= status;
    349 	}
    350 }
    351 
    352 static Uint8 *ALSA_GetAudioBuf(_THIS)
    353 {
    354 	return(mixbuf);
    355 }
    356 
    357 static void ALSA_CloseAudio(_THIS)
    358 {
    359 	if ( mixbuf != NULL ) {
    360 		SDL_FreeAudioMem(mixbuf);
    361 		mixbuf = NULL;
    362 	}
    363 	if ( pcm_handle ) {
    364 		SDL_NAME(snd_pcm_drain)(pcm_handle);
    365 		SDL_NAME(snd_pcm_close)(pcm_handle);
    366 		pcm_handle = NULL;
    367 	}
    368 }
    369 
    370 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
    371 {
    372 	int                  status;
    373 	snd_pcm_hw_params_t *hwparams;
    374 	snd_pcm_sw_params_t *swparams;
    375 	snd_pcm_format_t     format;
    376 	snd_pcm_uframes_t    frames;
    377 	Uint16               test_format;
    378 
    379 	/* Open the audio device */
    380 	/* Name of device should depend on # channels in spec */
    381 	status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
    382 
    383 	if ( status < 0 ) {
    384 		SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
    385 		return(-1);
    386 	}
    387 
    388 	/* Figure out what the hardware is capable of */
    389 	snd_pcm_hw_params_alloca(&hwparams);
    390 	status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, hwparams);
    391 	if ( status < 0 ) {
    392 		SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
    393 		ALSA_CloseAudio(this);
    394 		return(-1);
    395 	}
    396 
    397 	/* SDL only uses interleaved sample output */
    398 	status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
    399 	if ( status < 0 ) {
    400 		SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
    401 		ALSA_CloseAudio(this);
    402 		return(-1);
    403 	}
    404 
    405 	/* Try for a closest match on audio format */
    406 	status = -1;
    407 	for ( test_format = SDL_FirstAudioFormat(spec->format);
    408 	      test_format && (status < 0); ) {
    409 		switch ( test_format ) {
    410 			case AUDIO_U8:
    411 				format = SND_PCM_FORMAT_U8;
    412 				break;
    413 			case AUDIO_S8:
    414 				format = SND_PCM_FORMAT_S8;
    415 				break;
    416 			case AUDIO_S16LSB:
    417 				format = SND_PCM_FORMAT_S16_LE;
    418 				break;
    419 			case AUDIO_S16MSB:
    420 				format = SND_PCM_FORMAT_S16_BE;
    421 				break;
    422 			case AUDIO_U16LSB:
    423 				format = SND_PCM_FORMAT_U16_LE;
    424 				break;
    425 			case AUDIO_U16MSB:
    426 				format = SND_PCM_FORMAT_U16_BE;
    427 				break;
    428 			default:
    429 				format = 0;
    430 				break;
    431 		}
    432 		if ( format != 0 ) {
    433 			status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, hwparams, format);
    434 		}
    435 		if ( status < 0 ) {
    436 			test_format = SDL_NextAudioFormat();
    437 		}
    438 	}
    439 	if ( status < 0 ) {
    440 		SDL_SetError("Couldn't find any hardware audio formats");
    441 		ALSA_CloseAudio(this);
    442 		return(-1);
    443 	}
    444 	spec->format = test_format;
    445 
    446 	/* Set the number of channels */
    447 	status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, hwparams, spec->channels);
    448 	if ( status < 0 ) {
    449 		status = SDL_NAME(snd_pcm_hw_params_get_channels)(hwparams);
    450 		if ( (status <= 0) || (status > 2) ) {
    451 			SDL_SetError("Couldn't set audio channels");
    452 			ALSA_CloseAudio(this);
    453 			return(-1);
    454 		}
    455 		spec->channels = status;
    456 	}
    457 
    458 	/* Set the audio rate */
    459 	status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, hwparams, spec->freq, NULL);
    460 	if ( status < 0 ) {
    461 		SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
    462 		ALSA_CloseAudio(this);
    463 		return(-1);
    464 	}
    465 	spec->freq = status;
    466 
    467 	/* Set the buffer size, in samples */
    468 	frames = spec->samples;
    469 	frames = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, frames, NULL);
    470 	spec->samples = frames;
    471 	SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, 2, NULL);
    472 
    473 	/* "set" the hardware with the desired parameters */
    474 	status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
    475 	if ( status < 0 ) {
    476 		SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
    477 		ALSA_CloseAudio(this);
    478 		return(-1);
    479 	}
    480 
    481 /* This is useful for debugging... */
    482 /*
    483 { snd_pcm_sframes_t bufsize; int fragments;
    484    bufsize = SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams);
    485    fragments = SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams);
    486 
    487    fprintf(stderr, "ALSA: bufsize = %ld, fragments = %d\n", bufsize, fragments);
    488 }
    489 */
    490 
    491 	/* Set the software parameters */
    492 	snd_pcm_sw_params_alloca(&swparams);
    493 	status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);
    494 	if ( status < 0 ) {
    495 		SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status));
    496 		ALSA_CloseAudio(this);
    497 		return(-1);
    498 	}
    499 	status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 0);
    500 	if ( status < 0 ) {
    501 		SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status));
    502 		ALSA_CloseAudio(this);
    503 		return(-1);
    504 	}
    505 	status = SDL_NAME(snd_pcm_sw_params_set_avail_min)(pcm_handle, swparams, frames);
    506 	if ( status < 0 ) {
    507 		SDL_SetError("Couldn't set avail min: %s", SDL_NAME(snd_strerror)(status));
    508 		ALSA_CloseAudio(this);
    509 		return(-1);
    510 	}
    511 	status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams);
    512 	if ( status < 0 ) {
    513 		SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status));
    514 		ALSA_CloseAudio(this);
    515 		return(-1);
    516 	}
    517 
    518 	/* Calculate the final parameters for this audio specification */
    519 	SDL_CalculateAudioSpec(spec);
    520 
    521 	/* Allocate mixing buffer */
    522 	mixlen = spec->size;
    523 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
    524 	if ( mixbuf == NULL ) {
    525 		ALSA_CloseAudio(this);
    526 		return(-1);
    527 	}
    528 	SDL_memset(mixbuf, spec->silence, spec->size);
    529 
    530 	/* Get the parent process id (we're the parent of the audio thread) */
    531 	parent = getpid();
    532 
    533 	/* Switch to blocking mode for playback */
    534 	SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
    535 
    536 	/* We're ready to rock and roll. :-) */
    537 	return(0);
    538 }
    539