• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
UnloadALSALibrary(void)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 
LoadALSALibrary(void)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 
UnloadALSALibrary(void)162 static void UnloadALSALibrary(void) {
163 	return;
164 }
165 
LoadALSALibrary(void)166 static int LoadALSALibrary(void) {
167 	return 0;
168 }
169 
170 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
171 
get_audio_device(int channels)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 
Audio_Available(void)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 
Audio_DeleteDevice(SDL_AudioDevice * device)206 static void Audio_DeleteDevice(SDL_AudioDevice *device)
207 {
208 	SDL_free(device->hidden);
209 	SDL_free(device);
210 	UnloadALSALibrary();
211 }
212 
Audio_CreateDevice(int devindex)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 */
ALSA_WaitAudio(_THIS)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 
swizzle_alsa_channels_6_64bit(_THIS)283 static __inline__ void swizzle_alsa_channels_6_64bit(_THIS) { SWIZ6(Uint64); }
swizzle_alsa_channels_6_32bit(_THIS)284 static __inline__ void swizzle_alsa_channels_6_32bit(_THIS) { SWIZ6(Uint32); }
swizzle_alsa_channels_6_16bit(_THIS)285 static __inline__ void swizzle_alsa_channels_6_16bit(_THIS) { SWIZ6(Uint16); }
swizzle_alsa_channels_6_8bit(_THIS)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  */
swizzle_alsa_channels(_THIS)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 
ALSA_PlayAudio(_THIS)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 
ALSA_GetAudioBuf(_THIS)352 static Uint8 *ALSA_GetAudioBuf(_THIS)
353 {
354 	return(mixbuf);
355 }
356 
ALSA_CloseAudio(_THIS)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 
ALSA_OpenAudio(_THIS,SDL_AudioSpec * spec)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