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