src/audio/alsa/SDL_alsa_audio.c
author Sam Lantinga
Sat, 17 Oct 2009 06:55:17 +0000
branchSDL-1.2
changeset 4347 38f22ed3a433
parent 4339 819270e2f893
child 4348 b312352d8c8d
permissions -rw-r--r--
Option to fix bug #851

For some people setting the period size works better (and is what SDL 1.2.13 did), but for most people it's the same or worse. You can use an environment variable to pick which one you want.
     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 #ifdef DEBUG_PERIOD_SIZE
   512 { snd_pcm_uframes_t bufsize; snd_pcm_sframes_t persize; unsigned int periods; int dir;
   513    SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
   514    SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, &dir);
   515    SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, &dir);
   516 
   517    fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
   518 }
   519 #endif
   520 
   521 	/* Set the software parameters */
   522 	snd_pcm_sw_params_alloca(&swparams);
   523 	status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);
   524 	if ( status < 0 ) {
   525 		SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status));
   526 		ALSA_CloseAudio(this);
   527 		return(-1);
   528 	}
   529 	status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 1);
   530 	if ( status < 0 ) {
   531 		SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status));
   532 		ALSA_CloseAudio(this);
   533 		return(-1);
   534 	}
   535 	status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams);
   536 	if ( status < 0 ) {
   537 		SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status));
   538 		ALSA_CloseAudio(this);
   539 		return(-1);
   540 	}
   541 
   542 	/* Calculate the final parameters for this audio specification */
   543 	SDL_CalculateAudioSpec(spec);
   544 
   545 	/* Allocate mixing buffer */
   546 	mixlen = spec->size;
   547 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   548 	if ( mixbuf == NULL ) {
   549 		ALSA_CloseAudio(this);
   550 		return(-1);
   551 	}
   552 	SDL_memset(mixbuf, spec->silence, spec->size);
   553 
   554 	/* Get the parent process id (we're the parent of the audio thread) */
   555 	parent = getpid();
   556 
   557 	/* Switch to blocking mode for playback */
   558 	SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
   559 
   560 	/* We're ready to rock and roll. :-) */
   561 	return(0);
   562 }