src/audio/alsa/SDL_alsa_audio.c
author Sam Lantinga
Tue, 13 Oct 2009 09:59:20 +0000
branchSDL-1.2
changeset 4334 afadcd7d2319
parent 4333 55717a755897
child 4336 77b20871fd44
permissions -rw-r--r--
Use the plug: ALSA devices for surround sound formats, which have automatic sample format/rate conversion.
Based on feedback from the ALSA development mailing list:
http://mailman.alsa-project.org/pipermail/alsa-devel/2009-October/022267.html
     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 /* Whether we should set the buffer size or the period size */
    47 /*#define SET_PERIOD_SIZE*/
    48 /*#define DEBUG_PERIOD_SIZE*/
    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_NAME(snd_pcm_open))(snd_pcm_t **pcm, const char *name, snd_pcm_stream_t stream, int mode);
    64 static int (*SDL_NAME(snd_pcm_close))(snd_pcm_t *pcm);
    65 static snd_pcm_sframes_t (*SDL_NAME(snd_pcm_writei))(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size);
    66 static int (*SDL_NAME(snd_pcm_resume))(snd_pcm_t *pcm);
    67 static int (*SDL_NAME(snd_pcm_prepare))(snd_pcm_t *pcm);
    68 static int (*SDL_NAME(snd_pcm_drain))(snd_pcm_t *pcm);
    69 static const char *(*SDL_NAME(snd_strerror))(int errnum);
    70 static size_t (*SDL_NAME(snd_pcm_hw_params_sizeof))(void);
    71 static size_t (*SDL_NAME(snd_pcm_sw_params_sizeof))(void);
    72 static int (*SDL_NAME(snd_pcm_hw_params_any))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
    73 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);
    74 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);
    75 static int (*SDL_NAME(snd_pcm_hw_params_set_channels))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params, unsigned int val);
    76 static int (*SDL_NAME(snd_pcm_hw_params_get_channels))(const snd_pcm_hw_params_t *params, unsigned int *val);
    77 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);
    78 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);
    79 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);
    80 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);
    81 static int (*SDL_NAME(snd_pcm_hw_params_get_periods))(const snd_pcm_hw_params_t *params, unsigned int *val, int *dir);
    82 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);
    83 static int (*SDL_NAME(snd_pcm_hw_params_get_buffer_size))(const snd_pcm_hw_params_t *params, snd_pcm_uframes_t *val);
    84 static int (*SDL_NAME(snd_pcm_hw_params))(snd_pcm_t *pcm, snd_pcm_hw_params_t *params);
    85 /*
    86 */
    87 static int (*SDL_NAME(snd_pcm_sw_params_current))(snd_pcm_t *pcm, snd_pcm_sw_params_t *swparams);
    88 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);
    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_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_current",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params_current)	},
   122 	{ "snd_pcm_sw_params_set_start_threshold",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params_set_start_threshold)	},
   123 	{ "snd_pcm_sw_params",	(void**)(char*)&SDL_NAME(snd_pcm_sw_params)	},
   124 	{ "snd_pcm_nonblock",	(void**)(char*)&SDL_NAME(snd_pcm_nonblock)	},
   125 };
   126 
   127 static void UnloadALSALibrary(void) {
   128 	if (alsa_loaded) {
   129 		SDL_UnloadObject(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 	if (alsa_handle) {
   140 		alsa_loaded = 1;
   141 		retval = 0;
   142 		for (i = 0; i < SDL_arraysize(alsa_functions); i++) {
   143 			*alsa_functions[i].func = SDL_LoadFunction(alsa_handle,alsa_functions[i].name);
   144 			if (!*alsa_functions[i].func) {
   145 				retval = -1;
   146 				UnloadALSALibrary();
   147 				break;
   148 			}
   149 		}
   150 	}
   151 	return retval;
   152 }
   153 
   154 #else
   155 
   156 static void UnloadALSALibrary(void) {
   157 	return;
   158 }
   159 
   160 static int LoadALSALibrary(void) {
   161 	return 0;
   162 }
   163 
   164 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */
   165 
   166 static const char *get_audio_device(int channels)
   167 {
   168 	const char *device;
   169 	
   170 	device = SDL_getenv("AUDIODEV");	/* Is there a standard variable name? */
   171 	if ( device == NULL ) {
   172 		switch (channels) {
   173 		case 6:
   174 			device = "plug:surround51";
   175 			break;
   176 		case 4:
   177 			device = "plug:surround40";
   178 			break;
   179 		default:
   180 			device = "default";
   181 			break;
   182 		}
   183 	}
   184 	return device;
   185 }
   186 
   187 /* Audio driver bootstrap functions */
   188 
   189 static int Audio_Available(void)
   190 {
   191 	int available;
   192 	int status;
   193 	snd_pcm_t *handle;
   194 
   195 	available = 0;
   196 	if (LoadALSALibrary() < 0) {
   197 		return available;
   198 	}
   199 	status = SDL_NAME(snd_pcm_open)(&handle, get_audio_device(2), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   200 	if ( status >= 0 ) {
   201 		available = 1;
   202         	SDL_NAME(snd_pcm_close)(handle);
   203 	}
   204 	UnloadALSALibrary();
   205 	return(available);
   206 }
   207 
   208 static void Audio_DeleteDevice(SDL_AudioDevice *device)
   209 {
   210 	SDL_free(device->hidden);
   211 	SDL_free(device);
   212 	UnloadALSALibrary();
   213 }
   214 
   215 static SDL_AudioDevice *Audio_CreateDevice(int devindex)
   216 {
   217 	SDL_AudioDevice *this;
   218 
   219 	/* Initialize all variables that we clean on shutdown */
   220 	LoadALSALibrary();
   221 	this = (SDL_AudioDevice *)SDL_malloc(sizeof(SDL_AudioDevice));
   222 	if ( this ) {
   223 		SDL_memset(this, 0, (sizeof *this));
   224 		this->hidden = (struct SDL_PrivateAudioData *)
   225 				SDL_malloc((sizeof *this->hidden));
   226 	}
   227 	if ( (this == NULL) || (this->hidden == NULL) ) {
   228 		SDL_OutOfMemory();
   229 		if ( this ) {
   230 			SDL_free(this);
   231 		}
   232 		return(0);
   233 	}
   234 	SDL_memset(this->hidden, 0, (sizeof *this->hidden));
   235 
   236 	/* Set the function pointers */
   237 	this->OpenAudio = ALSA_OpenAudio;
   238 	this->WaitAudio = ALSA_WaitAudio;
   239 	this->PlayAudio = ALSA_PlayAudio;
   240 	this->GetAudioBuf = ALSA_GetAudioBuf;
   241 	this->CloseAudio = ALSA_CloseAudio;
   242 
   243 	this->free = Audio_DeleteDevice;
   244 
   245 	return this;
   246 }
   247 
   248 AudioBootStrap ALSA_bootstrap = {
   249 	DRIVER_NAME, "ALSA 0.9 PCM audio",
   250 	Audio_Available, Audio_CreateDevice
   251 };
   252 
   253 /* This function waits until it is possible to write a full sound buffer */
   254 static void ALSA_WaitAudio(_THIS)
   255 {
   256 	/* Check to see if the thread-parent process is still alive */
   257 	{ static int cnt = 0;
   258 		/* Note that this only works with thread implementations 
   259 		   that use a different process id for each thread.
   260 		*/
   261 		if (parent && (((++cnt)%10) == 0)) { /* Check every 10 loops */
   262 			if ( kill(parent, 0) < 0 ) {
   263 				this->enabled = 0;
   264 			}
   265 		}
   266 	}
   267 }
   268 
   269 
   270 /*
   271  * http://bugzilla.libsdl.org/show_bug.cgi?id=110
   272  * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE
   273  *  and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR"
   274  */
   275 #define SWIZ6(T) \
   276     T *ptr = (T *) mixbuf; \
   277     const Uint32 count = (this->spec.samples / 6); \
   278     Uint32 i; \
   279     for (i = 0; i < count; i++, ptr += 6) { \
   280         T tmp; \
   281         tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \
   282         tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \
   283     }
   284 
   285 static __inline__ void swizzle_alsa_channels_6_64bit(_THIS) { SWIZ6(Uint64); }
   286 static __inline__ void swizzle_alsa_channels_6_32bit(_THIS) { SWIZ6(Uint32); }
   287 static __inline__ void swizzle_alsa_channels_6_16bit(_THIS) { SWIZ6(Uint16); }
   288 static __inline__ void swizzle_alsa_channels_6_8bit(_THIS) { SWIZ6(Uint8); }
   289 
   290 #undef SWIZ6
   291 
   292 
   293 /*
   294  * Called right before feeding this->mixbuf to the hardware. Swizzle channels
   295  *  from Windows/Mac order to the format alsalib will want.
   296  */
   297 static __inline__ void swizzle_alsa_channels(_THIS)
   298 {
   299     if (this->spec.channels == 6) {
   300         const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */
   301         if (fmtsize == 16)
   302             swizzle_alsa_channels_6_16bit(this);
   303         else if (fmtsize == 8)
   304             swizzle_alsa_channels_6_8bit(this);
   305         else if (fmtsize == 32)
   306             swizzle_alsa_channels_6_32bit(this);
   307         else if (fmtsize == 64)
   308             swizzle_alsa_channels_6_64bit(this);
   309     }
   310 
   311     /* !!! FIXME: update this for 7.1 if needed, later. */
   312 }
   313 
   314 
   315 static void ALSA_PlayAudio(_THIS)
   316 {
   317 	int status;
   318 	snd_pcm_uframes_t frames_left;
   319 	const Uint8 *sample_buf = (const Uint8 *) mixbuf;
   320 	const int sample_size = ((int) (this->spec.format & 0xFF)) / 8;
   321 
   322 	swizzle_alsa_channels(this);
   323 
   324 	frames_left = ((snd_pcm_uframes_t) this->spec.samples);
   325 
   326 	while ( frames_left > 0 ) {
   327 		status = SDL_NAME(snd_pcm_writei)(pcm_handle, sample_buf, frames_left);
   328 		if ( status < 0 ) {
   329 			if ( status == -EAGAIN ) {
   330 				SDL_Delay(1);
   331 				continue;
   332 			}
   333 			if ( status == -ESTRPIPE ) {
   334 				do {
   335 					SDL_Delay(1);
   336 					status = SDL_NAME(snd_pcm_resume)(pcm_handle);
   337 				} while ( status == -EAGAIN );
   338 			}
   339 			if ( status < 0 ) {
   340 				status = SDL_NAME(snd_pcm_prepare)(pcm_handle);
   341 			}
   342 			if ( status < 0 ) {
   343 				/* Hmm, not much we can do - abort */
   344 				this->enabled = 0;
   345 				return;
   346 			}
   347 			continue;
   348 		}
   349 		sample_buf += status * sample_size;
   350 		frames_left -= status;
   351 	}
   352 }
   353 
   354 static Uint8 *ALSA_GetAudioBuf(_THIS)
   355 {
   356 	return(mixbuf);
   357 }
   358 
   359 static void ALSA_CloseAudio(_THIS)
   360 {
   361 	if ( mixbuf != NULL ) {
   362 		SDL_FreeAudioMem(mixbuf);
   363 		mixbuf = NULL;
   364 	}
   365 	if ( pcm_handle ) {
   366 		SDL_NAME(snd_pcm_drain)(pcm_handle);
   367 		SDL_NAME(snd_pcm_close)(pcm_handle);
   368 		pcm_handle = NULL;
   369 	}
   370 }
   371 
   372 static int ALSA_OpenAudio(_THIS, SDL_AudioSpec *spec)
   373 {
   374 	int                  status;
   375 	snd_pcm_hw_params_t *hwparams;
   376 	snd_pcm_sw_params_t *swparams;
   377 	snd_pcm_format_t     format;
   378 	snd_pcm_uframes_t    frames;
   379 	unsigned int         rate;
   380 #ifdef SET_PERIOD_SIZE
   381 	unsigned int         periods;
   382 #endif
   383 	unsigned int 	     channels;
   384 	Uint16               test_format;
   385 
   386 	/* Open the audio device */
   387 	/* Name of device should depend on # channels in spec */
   388 	status = SDL_NAME(snd_pcm_open)(&pcm_handle, get_audio_device(spec->channels), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
   389 
   390 	if ( status < 0 ) {
   391 		SDL_SetError("Couldn't open audio device: %s", SDL_NAME(snd_strerror)(status));
   392 		return(-1);
   393 	}
   394 
   395 	/* Figure out what the hardware is capable of */
   396 	snd_pcm_hw_params_alloca(&hwparams);
   397 	status = SDL_NAME(snd_pcm_hw_params_any)(pcm_handle, hwparams);
   398 	if ( status < 0 ) {
   399 		SDL_SetError("Couldn't get hardware config: %s", SDL_NAME(snd_strerror)(status));
   400 		ALSA_CloseAudio(this);
   401 		return(-1);
   402 	}
   403 
   404 	/* SDL only uses interleaved sample output */
   405 	status = SDL_NAME(snd_pcm_hw_params_set_access)(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED);
   406 	if ( status < 0 ) {
   407 		SDL_SetError("Couldn't set interleaved access: %s", SDL_NAME(snd_strerror)(status));
   408 		ALSA_CloseAudio(this);
   409 		return(-1);
   410 	}
   411 
   412 	/* Try for a closest match on audio format */
   413 	status = -1;
   414 	for ( test_format = SDL_FirstAudioFormat(spec->format);
   415 	      test_format && (status < 0); ) {
   416 		switch ( test_format ) {
   417 			case AUDIO_U8:
   418 				format = SND_PCM_FORMAT_U8;
   419 				break;
   420 			case AUDIO_S8:
   421 				format = SND_PCM_FORMAT_S8;
   422 				break;
   423 			case AUDIO_S16LSB:
   424 				format = SND_PCM_FORMAT_S16_LE;
   425 				break;
   426 			case AUDIO_S16MSB:
   427 				format = SND_PCM_FORMAT_S16_BE;
   428 				break;
   429 			case AUDIO_U16LSB:
   430 				format = SND_PCM_FORMAT_U16_LE;
   431 				break;
   432 			case AUDIO_U16MSB:
   433 				format = SND_PCM_FORMAT_U16_BE;
   434 				break;
   435 			default:
   436 				format = 0;
   437 				break;
   438 		}
   439 		if ( format != 0 ) {
   440 			status = SDL_NAME(snd_pcm_hw_params_set_format)(pcm_handle, hwparams, format);
   441 		}
   442 		if ( status < 0 ) {
   443 			test_format = SDL_NextAudioFormat();
   444 		}
   445 	}
   446 	if ( status < 0 ) {
   447 		SDL_SetError("Couldn't find any hardware audio formats");
   448 		ALSA_CloseAudio(this);
   449 		return(-1);
   450 	}
   451 	spec->format = test_format;
   452 
   453 	/* Set the number of channels */
   454 	status = SDL_NAME(snd_pcm_hw_params_set_channels)(pcm_handle, hwparams, spec->channels);
   455 	channels = spec->channels;
   456 	if ( status < 0 ) {
   457 		status = SDL_NAME(snd_pcm_hw_params_get_channels)(hwparams, &channels);
   458 		if ( status < 0 ) {
   459 			SDL_SetError("Couldn't set audio channels");
   460 			ALSA_CloseAudio(this);
   461 			return(-1);
   462 		}
   463 		spec->channels = channels;
   464 	}
   465 
   466 	/* Set the audio rate */
   467 	rate = spec->freq;
   468 
   469 	status = SDL_NAME(snd_pcm_hw_params_set_rate_near)(pcm_handle, hwparams, &rate, NULL);
   470 	if ( status < 0 ) {
   471 		SDL_SetError("Couldn't set audio frequency: %s", SDL_NAME(snd_strerror)(status));
   472 		ALSA_CloseAudio(this);
   473 		return(-1);
   474 	}
   475 	spec->freq = rate;
   476 
   477 	/* Set the buffer size, in samples */
   478 #ifdef SET_PERIOD_SIZE
   479 	frames = spec->samples;
   480 	status = SDL_NAME(snd_pcm_hw_params_set_period_size_near)(pcm_handle, hwparams, &frames, NULL);
   481 	if ( status < 0 ) {
   482 		SDL_SetError("Couldn't set period size: %s", SDL_NAME(snd_strerror)(status));
   483 		ALSA_CloseAudio(this);
   484 		return(-1);
   485 	}
   486 
   487 	spec->samples = frames;
   488 
   489 	periods = 2;
   490 	status = SDL_NAME(snd_pcm_hw_params_set_periods_near)(pcm_handle, hwparams, &periods, NULL);
   491 	if ( status < 0 ) {
   492 		SDL_SetError("Couldn't set period count: %s", SDL_NAME(snd_strerror)(status));
   493 		ALSA_CloseAudio(this);
   494 		return(-1);
   495 	}
   496 #else
   497 	frames = spec->samples * 2;
   498 	status = SDL_NAME(snd_pcm_hw_params_set_buffer_size_near)(pcm_handle, hwparams, &frames);
   499 #endif
   500 
   501 	/* "set" the hardware with the desired parameters */
   502 	status = SDL_NAME(snd_pcm_hw_params)(pcm_handle, hwparams);
   503 	if ( status < 0 ) {
   504 		SDL_SetError("Couldn't set hardware audio parameters: %s", SDL_NAME(snd_strerror)(status));
   505 		ALSA_CloseAudio(this);
   506 		return(-1);
   507 	}
   508 
   509 /* This is useful for debugging... */
   510 #ifdef DEBUG_PERIOD_SIZE
   511 { snd_pcm_uframes_t bufsize; snd_pcm_sframes_t persize; unsigned int periods; int dir;
   512    SDL_NAME(snd_pcm_hw_params_get_buffer_size)(hwparams, &bufsize);
   513    SDL_NAME(snd_pcm_hw_params_get_period_size)(hwparams, &persize, &dir);
   514    SDL_NAME(snd_pcm_hw_params_get_periods)(hwparams, &periods, &dir);
   515 
   516    fprintf(stderr, "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", persize, periods, bufsize);
   517 }
   518 #endif
   519 
   520 	/* Set the software parameters */
   521 	snd_pcm_sw_params_alloca(&swparams);
   522 	status = SDL_NAME(snd_pcm_sw_params_current)(pcm_handle, swparams);
   523 	if ( status < 0 ) {
   524 		SDL_SetError("Couldn't get software config: %s", SDL_NAME(snd_strerror)(status));
   525 		ALSA_CloseAudio(this);
   526 		return(-1);
   527 	}
   528 	status = SDL_NAME(snd_pcm_sw_params_set_start_threshold)(pcm_handle, swparams, 1);
   529 	if ( status < 0 ) {
   530 		SDL_SetError("Couldn't set start threshold: %s", SDL_NAME(snd_strerror)(status));
   531 		ALSA_CloseAudio(this);
   532 		return(-1);
   533 	}
   534 	status = SDL_NAME(snd_pcm_sw_params)(pcm_handle, swparams);
   535 	if ( status < 0 ) {
   536 		SDL_SetError("Couldn't set software audio parameters: %s", SDL_NAME(snd_strerror)(status));
   537 		ALSA_CloseAudio(this);
   538 		return(-1);
   539 	}
   540 
   541 	/* Calculate the final parameters for this audio specification */
   542 	SDL_CalculateAudioSpec(spec);
   543 
   544 	/* Allocate mixing buffer */
   545 	mixlen = spec->size;
   546 	mixbuf = (Uint8 *)SDL_AllocAudioMem(mixlen);
   547 	if ( mixbuf == NULL ) {
   548 		ALSA_CloseAudio(this);
   549 		return(-1);
   550 	}
   551 	SDL_memset(mixbuf, spec->silence, spec->size);
   552 
   553 	/* Get the parent process id (we're the parent of the audio thread) */
   554 	parent = getpid();
   555 
   556 	/* Switch to blocking mode for playback */
   557 	SDL_NAME(snd_pcm_nonblock)(pcm_handle, 0);
   558 
   559 	/* We're ready to rock and roll. :-) */
   560 	return(0);
   561 }