SDL_alsa_audio.c (21384B)
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org> 4 5 This software is provided 'as-is', without any express or implied 6 warranty. In no event will the authors be held liable for any damages 7 arising from the use of this software. 8 9 Permission is granted to anyone to use this software for any purpose, 10 including commercial applications, and to alter it and redistribute it 11 freely, subject to the following restrictions: 12 13 1. The origin of this software must not be misrepresented; you must not 14 claim that you wrote the original software. If you use this software 15 in a product, an acknowledgment in the product documentation would be 16 appreciated but is not required. 17 2. Altered source versions must be plainly marked as such, and must not be 18 misrepresented as being the original software. 19 3. This notice may not be removed or altered from any source distribution. 20*/ 21#include "../../SDL_internal.h" 22 23#if SDL_AUDIO_DRIVER_ALSA 24 25/* Allow access to a raw mixing buffer */ 26 27#include <sys/types.h> 28#include <signal.h> /* For kill() */ 29#include <errno.h> 30#include <string.h> 31 32#include "SDL_timer.h" 33#include "SDL_audio.h" 34#include "../SDL_audiomem.h" 35#include "../SDL_audio_c.h" 36#include "SDL_alsa_audio.h" 37 38#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC 39#include "SDL_loadso.h" 40#endif 41 42static int (*ALSA_snd_pcm_open) 43 (snd_pcm_t **, const char *, snd_pcm_stream_t, int); 44static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm); 45static snd_pcm_sframes_t(*ALSA_snd_pcm_writei) 46 (snd_pcm_t *, const void *, snd_pcm_uframes_t); 47static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int); 48static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *); 49static int (*ALSA_snd_pcm_drain) (snd_pcm_t *); 50static const char *(*ALSA_snd_strerror) (int); 51static size_t(*ALSA_snd_pcm_hw_params_sizeof) (void); 52static size_t(*ALSA_snd_pcm_sw_params_sizeof) (void); 53static void (*ALSA_snd_pcm_hw_params_copy) 54 (snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *); 55static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *); 56static int (*ALSA_snd_pcm_hw_params_set_access) 57 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t); 58static int (*ALSA_snd_pcm_hw_params_set_format) 59 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t); 60static int (*ALSA_snd_pcm_hw_params_set_channels) 61 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int); 62static int (*ALSA_snd_pcm_hw_params_get_channels) 63 (const snd_pcm_hw_params_t *, unsigned int *); 64static int (*ALSA_snd_pcm_hw_params_set_rate_near) 65 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); 66static int (*ALSA_snd_pcm_hw_params_set_period_size_near) 67 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *); 68static int (*ALSA_snd_pcm_hw_params_get_period_size) 69 (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *); 70static int (*ALSA_snd_pcm_hw_params_set_periods_near) 71 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); 72static int (*ALSA_snd_pcm_hw_params_get_periods) 73 (const snd_pcm_hw_params_t *, unsigned int *, int *); 74static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near) 75 (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *); 76static int (*ALSA_snd_pcm_hw_params_get_buffer_size) 77 (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *); 78static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *); 79static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *, 80 snd_pcm_sw_params_t *); 81static int (*ALSA_snd_pcm_sw_params_set_start_threshold) 82 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); 83static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *); 84static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int); 85static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int); 86static int (*ALSA_snd_pcm_sw_params_set_avail_min) 87 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); 88 89#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC 90#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof 91#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof 92 93static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC; 94static void *alsa_handle = NULL; 95 96static int 97load_alsa_sym(const char *fn, void **addr) 98{ 99 *addr = SDL_LoadFunction(alsa_handle, fn); 100 if (*addr == NULL) { 101 /* Don't call SDL_SetError(): SDL_LoadFunction already did. */ 102 return 0; 103 } 104 105 return 1; 106} 107 108/* cast funcs to char* first, to please GCC's strict aliasing rules. */ 109#define SDL_ALSA_SYM(x) \ 110 if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1 111#else 112#define SDL_ALSA_SYM(x) ALSA_##x = x 113#endif 114 115static int 116load_alsa_syms(void) 117{ 118 SDL_ALSA_SYM(snd_pcm_open); 119 SDL_ALSA_SYM(snd_pcm_close); 120 SDL_ALSA_SYM(snd_pcm_writei); 121 SDL_ALSA_SYM(snd_pcm_recover); 122 SDL_ALSA_SYM(snd_pcm_prepare); 123 SDL_ALSA_SYM(snd_pcm_drain); 124 SDL_ALSA_SYM(snd_strerror); 125 SDL_ALSA_SYM(snd_pcm_hw_params_sizeof); 126 SDL_ALSA_SYM(snd_pcm_sw_params_sizeof); 127 SDL_ALSA_SYM(snd_pcm_hw_params_copy); 128 SDL_ALSA_SYM(snd_pcm_hw_params_any); 129 SDL_ALSA_SYM(snd_pcm_hw_params_set_access); 130 SDL_ALSA_SYM(snd_pcm_hw_params_set_format); 131 SDL_ALSA_SYM(snd_pcm_hw_params_set_channels); 132 SDL_ALSA_SYM(snd_pcm_hw_params_get_channels); 133 SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near); 134 SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near); 135 SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size); 136 SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_near); 137 SDL_ALSA_SYM(snd_pcm_hw_params_get_periods); 138 SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near); 139 SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size); 140 SDL_ALSA_SYM(snd_pcm_hw_params); 141 SDL_ALSA_SYM(snd_pcm_sw_params_current); 142 SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold); 143 SDL_ALSA_SYM(snd_pcm_sw_params); 144 SDL_ALSA_SYM(snd_pcm_nonblock); 145 SDL_ALSA_SYM(snd_pcm_wait); 146 SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min); 147 return 0; 148} 149 150#undef SDL_ALSA_SYM 151 152#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC 153 154static void 155UnloadALSALibrary(void) 156{ 157 if (alsa_handle != NULL) { 158 SDL_UnloadObject(alsa_handle); 159 alsa_handle = NULL; 160 } 161} 162 163static int 164LoadALSALibrary(void) 165{ 166 int retval = 0; 167 if (alsa_handle == NULL) { 168 alsa_handle = SDL_LoadObject(alsa_library); 169 if (alsa_handle == NULL) { 170 retval = -1; 171 /* Don't call SDL_SetError(): SDL_LoadObject already did. */ 172 } else { 173 retval = load_alsa_syms(); 174 if (retval < 0) { 175 UnloadALSALibrary(); 176 } 177 } 178 } 179 return retval; 180} 181 182#else 183 184static void 185UnloadALSALibrary(void) 186{ 187} 188 189static int 190LoadALSALibrary(void) 191{ 192 load_alsa_syms(); 193 return 0; 194} 195 196#endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */ 197 198static const char * 199get_audio_device(int channels) 200{ 201 const char *device; 202 203 device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */ 204 if (device == NULL) { 205 switch (channels) { 206 case 6: 207 device = "plug:surround51"; 208 break; 209 case 4: 210 device = "plug:surround40"; 211 break; 212 default: 213 device = "default"; 214 break; 215 } 216 } 217 return device; 218} 219 220 221/* This function waits until it is possible to write a full sound buffer */ 222static void 223ALSA_WaitDevice(_THIS) 224{ 225 /* We're in blocking mode, so there's nothing to do here */ 226} 227 228 229/* !!! FIXME: is there a channel swizzler in alsalib instead? */ 230/* 231 * http://bugzilla.libsdl.org/show_bug.cgi?id=110 232 * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE 233 * and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR" 234 */ 235#define SWIZ6(T) \ 236 T *ptr = (T *) this->hidden->mixbuf; \ 237 Uint32 i; \ 238 for (i = 0; i < this->spec.samples; i++, ptr += 6) { \ 239 T tmp; \ 240 tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \ 241 tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \ 242 } 243 244static SDL_INLINE void 245swizzle_alsa_channels_6_64bit(_THIS) 246{ 247 SWIZ6(Uint64); 248} 249 250static SDL_INLINE void 251swizzle_alsa_channels_6_32bit(_THIS) 252{ 253 SWIZ6(Uint32); 254} 255 256static SDL_INLINE void 257swizzle_alsa_channels_6_16bit(_THIS) 258{ 259 SWIZ6(Uint16); 260} 261 262static SDL_INLINE void 263swizzle_alsa_channels_6_8bit(_THIS) 264{ 265 SWIZ6(Uint8); 266} 267 268#undef SWIZ6 269 270 271/* 272 * Called right before feeding this->hidden->mixbuf to the hardware. Swizzle 273 * channels from Windows/Mac order to the format alsalib will want. 274 */ 275static SDL_INLINE void 276swizzle_alsa_channels(_THIS) 277{ 278 if (this->spec.channels == 6) { 279 const Uint16 fmtsize = (this->spec.format & 0xFF); /* bits/channel. */ 280 if (fmtsize == 16) 281 swizzle_alsa_channels_6_16bit(this); 282 else if (fmtsize == 8) 283 swizzle_alsa_channels_6_8bit(this); 284 else if (fmtsize == 32) 285 swizzle_alsa_channels_6_32bit(this); 286 else if (fmtsize == 64) 287 swizzle_alsa_channels_6_64bit(this); 288 } 289 290 /* !!! FIXME: update this for 7.1 if needed, later. */ 291} 292 293 294static void 295ALSA_PlayDevice(_THIS) 296{ 297 int status; 298 const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf; 299 const int frame_size = (((int) (this->spec.format & 0xFF)) / 8) * 300 this->spec.channels; 301 snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples); 302 303 swizzle_alsa_channels(this); 304 305 while ( frames_left > 0 && this->enabled ) { 306 /* !!! FIXME: This works, but needs more testing before going live */ 307 /* ALSA_snd_pcm_wait(this->hidden->pcm_handle, -1); */ 308 status = ALSA_snd_pcm_writei(this->hidden->pcm_handle, 309 sample_buf, frames_left); 310 311 if (status < 0) { 312 if (status == -EAGAIN) { 313 /* Apparently snd_pcm_recover() doesn't handle this case - 314 does it assume snd_pcm_wait() above? */ 315 SDL_Delay(1); 316 continue; 317 } 318 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0); 319 if (status < 0) { 320 /* Hmm, not much we can do - abort */ 321 fprintf(stderr, "ALSA write failed (unrecoverable): %s\n", 322 ALSA_snd_strerror(status)); 323 this->enabled = 0; 324 return; 325 } 326 continue; 327 } 328 sample_buf += status * frame_size; 329 frames_left -= status; 330 } 331} 332 333static Uint8 * 334ALSA_GetDeviceBuf(_THIS) 335{ 336 return (this->hidden->mixbuf); 337} 338 339static void 340ALSA_CloseDevice(_THIS) 341{ 342 if (this->hidden != NULL) { 343 SDL_FreeAudioMem(this->hidden->mixbuf); 344 this->hidden->mixbuf = NULL; 345 if (this->hidden->pcm_handle) { 346 ALSA_snd_pcm_drain(this->hidden->pcm_handle); 347 ALSA_snd_pcm_close(this->hidden->pcm_handle); 348 this->hidden->pcm_handle = NULL; 349 } 350 SDL_free(this->hidden); 351 this->hidden = NULL; 352 } 353} 354 355static int 356ALSA_finalize_hardware(_THIS, snd_pcm_hw_params_t *hwparams, int override) 357{ 358 int status; 359 snd_pcm_uframes_t bufsize; 360 361 /* "set" the hardware with the desired parameters */ 362 status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams); 363 if ( status < 0 ) { 364 return(-1); 365 } 366 367 /* Get samples for the actual buffer size */ 368 status = ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize); 369 if ( status < 0 ) { 370 return(-1); 371 } 372 if ( !override && bufsize != this->spec.samples * 2 ) { 373 return(-1); 374 } 375 376 /* !!! FIXME: Is this safe to do? */ 377 this->spec.samples = bufsize / 2; 378 379 /* This is useful for debugging */ 380 if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) { 381 snd_pcm_uframes_t persize = 0; 382 unsigned int periods = 0; 383 384 ALSA_snd_pcm_hw_params_get_period_size(hwparams, &persize, NULL); 385 ALSA_snd_pcm_hw_params_get_periods(hwparams, &periods, NULL); 386 387 fprintf(stderr, 388 "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", 389 persize, periods, bufsize); 390 } 391 392 return(0); 393} 394 395static int 396ALSA_set_period_size(_THIS, snd_pcm_hw_params_t *params, int override) 397{ 398 const char *env; 399 int status; 400 snd_pcm_hw_params_t *hwparams; 401 snd_pcm_uframes_t frames; 402 unsigned int periods; 403 404 /* Copy the hardware parameters for this setup */ 405 snd_pcm_hw_params_alloca(&hwparams); 406 ALSA_snd_pcm_hw_params_copy(hwparams, params); 407 408 if ( !override ) { 409 env = SDL_getenv("SDL_AUDIO_ALSA_SET_PERIOD_SIZE"); 410 if ( env ) { 411 override = SDL_atoi(env); 412 if ( override == 0 ) { 413 return(-1); 414 } 415 } 416 } 417 418 frames = this->spec.samples; 419 status = ALSA_snd_pcm_hw_params_set_period_size_near( 420 this->hidden->pcm_handle, hwparams, &frames, NULL); 421 if ( status < 0 ) { 422 return(-1); 423 } 424 425 periods = 2; 426 status = ALSA_snd_pcm_hw_params_set_periods_near( 427 this->hidden->pcm_handle, hwparams, &periods, NULL); 428 if ( status < 0 ) { 429 return(-1); 430 } 431 432 return ALSA_finalize_hardware(this, hwparams, override); 433} 434 435static int 436ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params, int override) 437{ 438 const char *env; 439 int status; 440 snd_pcm_hw_params_t *hwparams; 441 snd_pcm_uframes_t frames; 442 443 /* Copy the hardware parameters for this setup */ 444 snd_pcm_hw_params_alloca(&hwparams); 445 ALSA_snd_pcm_hw_params_copy(hwparams, params); 446 447 if ( !override ) { 448 env = SDL_getenv("SDL_AUDIO_ALSA_SET_BUFFER_SIZE"); 449 if ( env ) { 450 override = SDL_atoi(env); 451 if ( override == 0 ) { 452 return(-1); 453 } 454 } 455 } 456 457 frames = this->spec.samples * 2; 458 status = ALSA_snd_pcm_hw_params_set_buffer_size_near( 459 this->hidden->pcm_handle, hwparams, &frames); 460 if ( status < 0 ) { 461 return(-1); 462 } 463 464 return ALSA_finalize_hardware(this, hwparams, override); 465} 466 467static int 468ALSA_OpenDevice(_THIS, const char *devname, int iscapture) 469{ 470 int status = 0; 471 snd_pcm_t *pcm_handle = NULL; 472 snd_pcm_hw_params_t *hwparams = NULL; 473 snd_pcm_sw_params_t *swparams = NULL; 474 snd_pcm_format_t format = 0; 475 SDL_AudioFormat test_format = 0; 476 unsigned int rate = 0; 477 unsigned int channels = 0; 478 479 /* Initialize all variables that we clean on shutdown */ 480 this->hidden = (struct SDL_PrivateAudioData *) 481 SDL_malloc((sizeof *this->hidden)); 482 if (this->hidden == NULL) { 483 return SDL_OutOfMemory(); 484 } 485 SDL_memset(this->hidden, 0, (sizeof *this->hidden)); 486 487 /* Open the audio device */ 488 /* Name of device should depend on # channels in spec */ 489 status = ALSA_snd_pcm_open(&pcm_handle, 490 get_audio_device(this->spec.channels), 491 SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK); 492 493 if (status < 0) { 494 ALSA_CloseDevice(this); 495 return SDL_SetError("ALSA: Couldn't open audio device: %s", 496 ALSA_snd_strerror(status)); 497 } 498 499 this->hidden->pcm_handle = pcm_handle; 500 501 /* Figure out what the hardware is capable of */ 502 snd_pcm_hw_params_alloca(&hwparams); 503 status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams); 504 if (status < 0) { 505 ALSA_CloseDevice(this); 506 return SDL_SetError("ALSA: Couldn't get hardware config: %s", 507 ALSA_snd_strerror(status)); 508 } 509 510 /* SDL only uses interleaved sample output */ 511 status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams, 512 SND_PCM_ACCESS_RW_INTERLEAVED); 513 if (status < 0) { 514 ALSA_CloseDevice(this); 515 return SDL_SetError("ALSA: Couldn't set interleaved access: %s", 516 ALSA_snd_strerror(status)); 517 } 518 519 /* Try for a closest match on audio format */ 520 status = -1; 521 for (test_format = SDL_FirstAudioFormat(this->spec.format); 522 test_format && (status < 0);) { 523 status = 0; /* if we can't support a format, it'll become -1. */ 524 switch (test_format) { 525 case AUDIO_U8: 526 format = SND_PCM_FORMAT_U8; 527 break; 528 case AUDIO_S8: 529 format = SND_PCM_FORMAT_S8; 530 break; 531 case AUDIO_S16LSB: 532 format = SND_PCM_FORMAT_S16_LE; 533 break; 534 case AUDIO_S16MSB: 535 format = SND_PCM_FORMAT_S16_BE; 536 break; 537 case AUDIO_U16LSB: 538 format = SND_PCM_FORMAT_U16_LE; 539 break; 540 case AUDIO_U16MSB: 541 format = SND_PCM_FORMAT_U16_BE; 542 break; 543 case AUDIO_S32LSB: 544 format = SND_PCM_FORMAT_S32_LE; 545 break; 546 case AUDIO_S32MSB: 547 format = SND_PCM_FORMAT_S32_BE; 548 break; 549 case AUDIO_F32LSB: 550 format = SND_PCM_FORMAT_FLOAT_LE; 551 break; 552 case AUDIO_F32MSB: 553 format = SND_PCM_FORMAT_FLOAT_BE; 554 break; 555 default: 556 status = -1; 557 break; 558 } 559 if (status >= 0) { 560 status = ALSA_snd_pcm_hw_params_set_format(pcm_handle, 561 hwparams, format); 562 } 563 if (status < 0) { 564 test_format = SDL_NextAudioFormat(); 565 } 566 } 567 if (status < 0) { 568 ALSA_CloseDevice(this); 569 return SDL_SetError("ALSA: Couldn't find any hardware audio formats"); 570 } 571 this->spec.format = test_format; 572 573 /* Set the number of channels */ 574 status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 575 this->spec.channels); 576 channels = this->spec.channels; 577 if (status < 0) { 578 status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels); 579 if (status < 0) { 580 ALSA_CloseDevice(this); 581 return SDL_SetError("ALSA: Couldn't set audio channels"); 582 } 583 this->spec.channels = channels; 584 } 585 586 /* Set the audio rate */ 587 rate = this->spec.freq; 588 status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, 589 &rate, NULL); 590 if (status < 0) { 591 ALSA_CloseDevice(this); 592 return SDL_SetError("ALSA: Couldn't set audio frequency: %s", 593 ALSA_snd_strerror(status)); 594 } 595 this->spec.freq = rate; 596 597 /* Set the buffer size, in samples */ 598 if ( ALSA_set_period_size(this, hwparams, 0) < 0 && 599 ALSA_set_buffer_size(this, hwparams, 0) < 0 ) { 600 /* Failed to set desired buffer size, do the best you can... */ 601 if ( ALSA_set_period_size(this, hwparams, 1) < 0 ) { 602 ALSA_CloseDevice(this); 603 return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status)); 604 } 605 } 606 /* Set the software parameters */ 607 snd_pcm_sw_params_alloca(&swparams); 608 status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams); 609 if (status < 0) { 610 ALSA_CloseDevice(this); 611 return SDL_SetError("ALSA: Couldn't get software config: %s", 612 ALSA_snd_strerror(status)); 613 } 614 status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, this->spec.samples); 615 if (status < 0) { 616 ALSA_CloseDevice(this); 617 return SDL_SetError("Couldn't set minimum available samples: %s", 618 ALSA_snd_strerror(status)); 619 } 620 status = 621 ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1); 622 if (status < 0) { 623 ALSA_CloseDevice(this); 624 return SDL_SetError("ALSA: Couldn't set start threshold: %s", 625 ALSA_snd_strerror(status)); 626 } 627 status = ALSA_snd_pcm_sw_params(pcm_handle, swparams); 628 if (status < 0) { 629 ALSA_CloseDevice(this); 630 return SDL_SetError("Couldn't set software audio parameters: %s", 631 ALSA_snd_strerror(status)); 632 } 633 634 /* Calculate the final parameters for this audio specification */ 635 SDL_CalculateAudioSpec(&this->spec); 636 637 /* Allocate mixing buffer */ 638 this->hidden->mixlen = this->spec.size; 639 this->hidden->mixbuf = (Uint8 *) SDL_AllocAudioMem(this->hidden->mixlen); 640 if (this->hidden->mixbuf == NULL) { 641 ALSA_CloseDevice(this); 642 return SDL_OutOfMemory(); 643 } 644 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen); 645 646 /* Switch to blocking mode for playback */ 647 ALSA_snd_pcm_nonblock(pcm_handle, 0); 648 649 /* We're ready to rock and roll. :-) */ 650 return 0; 651} 652 653static void 654ALSA_Deinitialize(void) 655{ 656 UnloadALSALibrary(); 657} 658 659static int 660ALSA_Init(SDL_AudioDriverImpl * impl) 661{ 662 if (LoadALSALibrary() < 0) { 663 return 0; 664 } 665 666 /* Set the function pointers */ 667 impl->OpenDevice = ALSA_OpenDevice; 668 impl->WaitDevice = ALSA_WaitDevice; 669 impl->GetDeviceBuf = ALSA_GetDeviceBuf; 670 impl->PlayDevice = ALSA_PlayDevice; 671 impl->CloseDevice = ALSA_CloseDevice; 672 impl->Deinitialize = ALSA_Deinitialize; 673 impl->OnlyHasDefaultOutputDevice = 1; /* !!! FIXME: Add device enum! */ 674 675 return 1; /* this audio target is available. */ 676} 677 678 679AudioBootStrap ALSA_bootstrap = { 680 "alsa", "ALSA PCM audio", ALSA_Init, 0 681}; 682 683#endif /* SDL_AUDIO_DRIVER_ALSA */ 684 685/* vi: set ts=4 sw=4 expandtab: */