src/audio/alsa/SDL_alsa_audio.c
author Sam Lantinga
Sat, 17 Oct 2009 07:04:58 +0000
branchSDL-1.2
changeset 4348 b312352d8c8d
parent 4347 38f22ed3a433
child 4349 471dac4b41dd
permissions -rw-r--r--
Enable the ALSA debug code via environment variable
     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 /*#define DEBUG_PERIOD_SIZE*/
    47 
    48 /* Audio driver functions */
    49 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec);
    50 static void ALSA_WaitAudio(_THIS);
    51 static void ALSA_PlayAudio(_THIS);
    52 static Uint8 *ALSA_GetAudioBuf(_THIS);
    53 static void ALSA_CloseAudio(_THIS);
    54 
    55 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC
    56 
    57 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC;
    58 static void *alsa_handle = NULL;
    59 static int alsa_loaded = 0;
    60 
    61 static int (*SDL_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
    62 static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm);
    63 static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
    64 static int (*SDL_NAME(snd_pcm_resume))(snd_pcm_t *pcm);
    65 static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);
    66 static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);
    67 static const char *(*SDL_NAME(snd_strerror))(int errnum);
    68 static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
    69 static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void);
    70 static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
    71 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);
    72 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);
    73 static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
    74 static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params, unsigned int *val);
    75 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);
    76 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);
    77 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);
    78 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);
    79 static int (*SDL_NAME(snd_pcm_hw_params_get_periods))(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
    80 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);
    81 static int (*SDL_NAME(snd_pcm_hw_params_get_buffer_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
    82 static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
    83 /*
    84 */
    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 #define snd_pcm_hw_params_sizeof SDL_NAME(snd_pcm_hw_params_sizeof)
    90 #define snd_pcm_sw_params_sizeof SDL_NAME(snd_pcm_sw_params_sizeof)
    91 
    92 /* cast funcs to char* first, to please GCC's strict aliasing rules. */
    93 static struct {
    94 	const char *name;
    95 	void **func;
    96 } alsa_functions[] = {
    97 	{ "snd_pcm_open",	(void**)(char*)&SDL_NAME(snd_pcm_open)		},
    98 	{ "snd_pcm_close",	(void**)(char*)&SDL_NAME(snd_pcm_close)	},
    99 	{ "snd_pcm_writei",	(void**)(char*)&SDL_NAME(snd_pcm_writei)	},
   100 	{ "snd_pcm_resume",	(void**)(char*)&SDL_NAME(snd_pcm_resume)	},
   101 	{ "snd_pcm_prepare",	(void**)(char*)&SDL_NAME(snd_pcm_prepare)	},
   102 	{ "snd_pcm_drain",	(void**)(char*)&SDL_NAME(snd_pcm_drain)	},
   103 	{ "snd_strerror",	(void**)(char*)&SDL_NAME(snd_strerror)		},
   104 	{ "snd_pcm_hw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_hw_params_sizeof)		},
   105 	{ "snd_pcm_sw_params_sizeof",		(void**)(char*)&SDL_NAME(snd_pcm_sw_params_sizeof)		},
   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 	/* 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 	snd_pcm_uframes_t frames_left;
   317 	const Uint8 *sample_buf = (const Uint8 *) mixbuf;
   318 	const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) * this->spec.channels;
   319 
   320 	swizzle_alsa_channels(this);
   321 
   322 	frames_left = ((snd_pcm_uframes_t) this->spec.samples);
   323 
   324 	while ( frames_left > 0 && this->enabled ) {
   325 		status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, frames_left);
   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 * frame_size;
   348 		frames_left -= 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 	unsigned int         rate;
   378 	unsigned int         periods;
   379 	unsigned int 	     channels;
   380 	Uint16               test_format;
   381 
   382 	/* Open the audio device */
   383 	/* Name of device should depend on # channels in spec */
   384 	status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   385 
   386 	if ( status < 0 ) {
   387 		SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
   388 		return(-1);
   389 	}
   390 
   391 	/* Figure out what the hardware is capable of */
   392 	snd_pcm_hw_params_alloca(&hwparams);
   393 	status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, hwparams);
   394 	if ( status < 0 ) {
   395 		SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
   396 		ALSA_CloseAudio(this);
   397 		return(-1);
   398 	}
   399 
   400 	/* SDL only uses interleaved sample output */
   401 	status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
   402 	if ( status < 0 ) {
   403 		SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
   404 		ALSA_CloseAudio(this);
   405 		return(-1);
   406 	}
   407 
   408 	/* Try for a closest match on audio format */
   409 	status = -1;
   410 	for ( test_format = SDL_FirstAudioFormat(spec->format);
   411 	      test_format && (status < 0); ) {
   412 		switch ( test_format ) {
   413 			case AUDIO_U8:
   414 				format = SND_PCM_FORMAT_U8;
   415 				break;
   416 			case AUDIO_S8:
   417 				format = SND_PCM_FORMAT_S8;
   418 				break;
   419 			case AUDIO_S16LSB:
   420 				format = SND_PCM_FORMAT_S16_LE;
   421 				break;
   422 			case AUDIO_S16MSB:
   423 				format = SND_PCM_FORMAT_S16_BE;
   424 				break;
   425 			case AUDIO_U16LSB:
   426 				format = SND_PCM_FORMAT_U16_LE;
   427 				break;
   428 			case AUDIO_U16MSB:
   429 				format = SND_PCM_FORMAT_U16_BE;
   430 				break;
   431 			default:
   432 				format = 0;
   433 				break;
   434 		}
   435 		if ( format != 0 ) {
   436 			status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, hwparams, format);
   437 		}
   438 		if ( status < 0 ) {
   439 			test_format = SDL_NextAudioFormat();
   440 		}
   441 	}
   442 	if ( status < 0 ) {
   443 		SDL_SetError("Couldn't find any hardware audio formats");
   444 		ALSA_CloseAudio(this);
   445 		return(-1);
   446 	}
   447 	spec->format = test_format;
   448 
   449 	/* Set the number of channels */
   450 	status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, hwparams, spec->channels);
   451 	channels = spec->channels;
   452 	if ( status < 0 ) {
   453 		status = SDL_NAME(snd_pcm_hw_params_get_channels)(hwparams, &channels);
   454 		if ( status < 0 ) {
   455 			SDL_SetError("Couldn't set audio channels");
   456 			ALSA_CloseAudio(this);
   457 			return(-1);
   458 		}
   459 		spec->channels = channels;
   460 	}
   461 
   462 	/* Set the audio rate */
   463 	rate = spec->freq;
   464 
   465 	status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, hwparams, &rate, NULL);
   466 	if ( status < 0 ) {
   467 		SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
   468 		ALSA_CloseAudio(this);
   469 		return(-1);
   470 	}
   471 	spec->freq = rate;
   472 
   473 	/* Set the buffer size, in samples */
   474 	if (getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE")) {
   475 		frames = spec->samples;
   476 		status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
   477 		if ( status < 0 ) {
   478 			SDL_SetError("Couldn't set period size: %s", SDL_NAME(snd_strerror)(status));
   479 			ALSA_CloseAudio(this);
   480 			return(-1);
   481 		}
   482 
   483 		spec->samples = frames;
   484 
   485 		periods = 2;
   486 		status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
   487 		if ( status < 0 ) {
   488 			SDL_SetError("Couldn't set period count: %s", SDL_NAME(snd_strerror)(status));
   489 			ALSA_CloseAudio(this);
   490 			return(-1);
   491 		}
   492 	} else {
   493 		frames = spec->samples * 2;
   494 		status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
   495 		if ( status < 0 ) {
   496 			SDL_SetError("Couldn't set buffer size: %s", SDL_NAME(snd_strerror)(status));
   497 			ALSA_CloseAudio(this);
   498 			return(-1);
   499 		}
   500 	}
   501 
   502 	/* "set" the hardware with the desired parameters */
   503 	status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
   504 	if ( status < 0 ) {
   505 		SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
   506 		ALSA_CloseAudio(this);
   507 		return(-1);
   508 	}
   509 
   510 	/* This is useful for debugging */
   511 	if (getenv("SDL_AUDIO_ALSA_DEBUG_PERIOD_SIZE")) {
   512 		snd_pcm_uframes_t bufsize;
   513 		snd_pcm_sframes_t persize;
   514 		unsigned int periods; int dir;
   515 
   516 		SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
   517 		SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, &dir);
   518 		SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, &dir);
   519 
   520 		fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
   521 	}
   522 
   523 	/* Set the software parameters */
   524 	snd_pcm_sw_params_alloca(&swparams);
   525 	status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);
   526 	if ( status < 0 ) {
   527 		SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status));
   528 		ALSA_CloseAudio(this);
   529 		return(-1);
   530 	}
   531 	status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 1);
   532 	if ( status < 0 ) {
   533 		SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status));
   534 		ALSA_CloseAudio(this);
   535 		return(-1);
   536 	}
   537 	status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams);
   538 	if ( status < 0 ) {
   539 		SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status));
   540 		ALSA_CloseAudio(this);
   541 		return(-1);
   542 	}
   543 
   544 	/* Calculate the final parameters for this audio specification */
   545 	SDL_CalculateAudioSpec(spec);
   546 
   547 	/* Allocate mixing buffer */
   548 	mixlen = spec->size;
   549 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   550 	if ( mixbuf == NULL ) {
   551 		ALSA_CloseAudio(this);
   552 		return(-1);
   553 	}
   554 	SDL_memset(mixbuf, spec->silence, spec->size);
   555 
   556 	/* Get the parent process id (we're the parent of the audio thread) */
   557 	parent = getpid();
   558 
   559 	/* Switch to blocking mode for playback */
   560 	SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
   561 
   562 	/* We're ready to rock and roll. :-) */
   563 	return(0);
   564 }