SDL_alsa_audio.c (32583B)
1 /* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2020 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 #ifndef SDL_ALSA_NON_BLOCKING 26 #define SDL_ALSA_NON_BLOCKING 0 27 #endif 28 29 /* Allow access to a raw mixing buffer */ 30 31 #include <sys/types.h> 32 #include <signal.h> /* For kill() */ 33 #include <string.h> 34 35 #include "SDL_timer.h" 36 #include "SDL_audio.h" 37 #include "../SDL_audio_c.h" 38 #include "SDL_alsa_audio.h" 39 40 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC 41 #include "SDL_loadso.h" 42 #endif 43 44 static int (*ALSA_snd_pcm_open) 45 (snd_pcm_t **, const char *, snd_pcm_stream_t, int); 46 static int (*ALSA_snd_pcm_close) (snd_pcm_t * pcm); 47 static snd_pcm_sframes_t (*ALSA_snd_pcm_writei) 48 (snd_pcm_t *, const void *, snd_pcm_uframes_t); 49 static snd_pcm_sframes_t (*ALSA_snd_pcm_readi) 50 (snd_pcm_t *, void *, snd_pcm_uframes_t); 51 static int (*ALSA_snd_pcm_recover) (snd_pcm_t *, int, int); 52 static int (*ALSA_snd_pcm_prepare) (snd_pcm_t *); 53 static int (*ALSA_snd_pcm_drain) (snd_pcm_t *); 54 static const char *(*ALSA_snd_strerror) (int); 55 static size_t(*ALSA_snd_pcm_hw_params_sizeof) (void); 56 static size_t(*ALSA_snd_pcm_sw_params_sizeof) (void); 57 static void (*ALSA_snd_pcm_hw_params_copy) 58 (snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *); 59 static int (*ALSA_snd_pcm_hw_params_any) (snd_pcm_t *, snd_pcm_hw_params_t *); 60 static int (*ALSA_snd_pcm_hw_params_set_access) 61 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t); 62 static int (*ALSA_snd_pcm_hw_params_set_format) 63 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t); 64 static int (*ALSA_snd_pcm_hw_params_set_channels) 65 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int); 66 static int (*ALSA_snd_pcm_hw_params_get_channels) 67 (const snd_pcm_hw_params_t *, unsigned int *); 68 static int (*ALSA_snd_pcm_hw_params_set_rate_near) 69 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); 70 static int (*ALSA_snd_pcm_hw_params_set_period_size_near) 71 (snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *); 72 static int (*ALSA_snd_pcm_hw_params_get_period_size) 73 (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *); 74 static int (*ALSA_snd_pcm_hw_params_set_periods_min) 75 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); 76 static int (*ALSA_snd_pcm_hw_params_set_periods_first) 77 (snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); 78 static int (*ALSA_snd_pcm_hw_params_get_periods) 79 (const snd_pcm_hw_params_t *, unsigned int *, int *); 80 static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near) 81 (snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *); 82 static int (*ALSA_snd_pcm_hw_params_get_buffer_size) 83 (const snd_pcm_hw_params_t *, snd_pcm_uframes_t *); 84 static int (*ALSA_snd_pcm_hw_params) (snd_pcm_t *, snd_pcm_hw_params_t *); 85 static int (*ALSA_snd_pcm_sw_params_current) (snd_pcm_t *, 86 snd_pcm_sw_params_t *); 87 static int (*ALSA_snd_pcm_sw_params_set_start_threshold) 88 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); 89 static int (*ALSA_snd_pcm_sw_params) (snd_pcm_t *, snd_pcm_sw_params_t *); 90 static int (*ALSA_snd_pcm_nonblock) (snd_pcm_t *, int); 91 static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int); 92 static int (*ALSA_snd_pcm_sw_params_set_avail_min) 93 (snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); 94 static int (*ALSA_snd_pcm_reset)(snd_pcm_t *); 95 static int (*ALSA_snd_device_name_hint) (int, const char *, void ***); 96 static char* (*ALSA_snd_device_name_get_hint) (const void *, const char *); 97 static int (*ALSA_snd_device_name_free_hint) (void **); 98 static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *); 99 #ifdef SND_CHMAP_API_VERSION 100 static snd_pcm_chmap_t* (*ALSA_snd_pcm_get_chmap) (snd_pcm_t *); 101 static int (*ALSA_snd_pcm_chmap_print) (const snd_pcm_chmap_t *map, size_t maxlen, char *buf); 102 #endif 103 104 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC 105 #define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof 106 #define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof 107 108 static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC; 109 static void *alsa_handle = NULL; 110 111 static int 112 load_alsa_sym(const char *fn, void **addr) 113 { 114 *addr = SDL_LoadFunction(alsa_handle, fn); 115 if (*addr == NULL) { 116 /* Don't call SDL_SetError(): SDL_LoadFunction already did. */ 117 return 0; 118 } 119 120 return 1; 121 } 122 123 /* cast funcs to char* first, to please GCC's strict aliasing rules. */ 124 #define SDL_ALSA_SYM(x) \ 125 if (!load_alsa_sym(#x, (void **) (char *) &ALSA_##x)) return -1 126 #else 127 #define SDL_ALSA_SYM(x) ALSA_##x = x 128 #endif 129 130 static int 131 load_alsa_syms(void) 132 { 133 SDL_ALSA_SYM(snd_pcm_open); 134 SDL_ALSA_SYM(snd_pcm_close); 135 SDL_ALSA_SYM(snd_pcm_writei); 136 SDL_ALSA_SYM(snd_pcm_readi); 137 SDL_ALSA_SYM(snd_pcm_recover); 138 SDL_ALSA_SYM(snd_pcm_prepare); 139 SDL_ALSA_SYM(snd_pcm_drain); 140 SDL_ALSA_SYM(snd_strerror); 141 SDL_ALSA_SYM(snd_pcm_hw_params_sizeof); 142 SDL_ALSA_SYM(snd_pcm_sw_params_sizeof); 143 SDL_ALSA_SYM(snd_pcm_hw_params_copy); 144 SDL_ALSA_SYM(snd_pcm_hw_params_any); 145 SDL_ALSA_SYM(snd_pcm_hw_params_set_access); 146 SDL_ALSA_SYM(snd_pcm_hw_params_set_format); 147 SDL_ALSA_SYM(snd_pcm_hw_params_set_channels); 148 SDL_ALSA_SYM(snd_pcm_hw_params_get_channels); 149 SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near); 150 SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near); 151 SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size); 152 SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_min); 153 SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_first); 154 SDL_ALSA_SYM(snd_pcm_hw_params_get_periods); 155 SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near); 156 SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size); 157 SDL_ALSA_SYM(snd_pcm_hw_params); 158 SDL_ALSA_SYM(snd_pcm_sw_params_current); 159 SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold); 160 SDL_ALSA_SYM(snd_pcm_sw_params); 161 SDL_ALSA_SYM(snd_pcm_nonblock); 162 SDL_ALSA_SYM(snd_pcm_wait); 163 SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min); 164 SDL_ALSA_SYM(snd_pcm_reset); 165 SDL_ALSA_SYM(snd_device_name_hint); 166 SDL_ALSA_SYM(snd_device_name_get_hint); 167 SDL_ALSA_SYM(snd_device_name_free_hint); 168 SDL_ALSA_SYM(snd_pcm_avail); 169 #ifdef SND_CHMAP_API_VERSION 170 SDL_ALSA_SYM(snd_pcm_get_chmap); 171 SDL_ALSA_SYM(snd_pcm_chmap_print); 172 #endif 173 174 return 0; 175 } 176 177 #undef SDL_ALSA_SYM 178 179 #ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC 180 181 static void 182 UnloadALSALibrary(void) 183 { 184 if (alsa_handle != NULL) { 185 SDL_UnloadObject(alsa_handle); 186 alsa_handle = NULL; 187 } 188 } 189 190 static int 191 LoadALSALibrary(void) 192 { 193 int retval = 0; 194 if (alsa_handle == NULL) { 195 alsa_handle = SDL_LoadObject(alsa_library); 196 if (alsa_handle == NULL) { 197 retval = -1; 198 /* Don't call SDL_SetError(): SDL_LoadObject already did. */ 199 } else { 200 retval = load_alsa_syms(); 201 if (retval < 0) { 202 UnloadALSALibrary(); 203 } 204 } 205 } 206 return retval; 207 } 208 209 #else 210 211 static void 212 UnloadALSALibrary(void) 213 { 214 } 215 216 static int 217 LoadALSALibrary(void) 218 { 219 load_alsa_syms(); 220 return 0; 221 } 222 223 #endif /* SDL_AUDIO_DRIVER_ALSA_DYNAMIC */ 224 225 static const char * 226 get_audio_device(void *handle, const int channels) 227 { 228 const char *device; 229 230 if (handle != NULL) { 231 return (const char *) handle; 232 } 233 234 /* !!! FIXME: we also check "SDL_AUDIO_DEVICE_NAME" at the higher level. */ 235 device = SDL_getenv("AUDIODEV"); /* Is there a standard variable name? */ 236 if (device != NULL) { 237 return device; 238 } 239 240 if (channels == 6) { 241 return "plug:surround51"; 242 } else if (channels == 4) { 243 return "plug:surround40"; 244 } 245 246 return "default"; 247 } 248 249 250 /* This function waits until it is possible to write a full sound buffer */ 251 static void 252 ALSA_WaitDevice(_THIS) 253 { 254 #if SDL_ALSA_NON_BLOCKING 255 const snd_pcm_sframes_t needed = (snd_pcm_sframes_t) this->spec.samples; 256 while (SDL_AtomicGet(&this->enabled)) { 257 const snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(this->hidden->pcm_handle); 258 if ((rc < 0) && (rc != -EAGAIN)) { 259 /* Hmm, not much we can do - abort */ 260 fprintf(stderr, "ALSA snd_pcm_avail failed (unrecoverable): %s\n", 261 ALSA_snd_strerror(rc)); 262 SDL_OpenedAudioDeviceDisconnected(this); 263 return; 264 } else if (rc < needed) { 265 const Uint32 delay = ((needed - (SDL_max(rc, 0))) * 1000) / this->spec.freq; 266 SDL_Delay(SDL_max(delay, 10)); 267 } else { 268 break; /* ready to go! */ 269 } 270 } 271 #endif 272 } 273 274 275 /* !!! FIXME: is there a channel swizzler in alsalib instead? */ 276 /* 277 * http://bugzilla.libsdl.org/show_bug.cgi?id=110 278 * "For Linux ALSA, this is FL-FR-RL-RR-C-LFE 279 * and for Windows DirectX [and CoreAudio], this is FL-FR-C-LFE-RL-RR" 280 */ 281 #define SWIZ6(T, buf, numframes) \ 282 T *ptr = (T *) buf; \ 283 Uint32 i; \ 284 for (i = 0; i < numframes; i++, ptr += 6) { \ 285 T tmp; \ 286 tmp = ptr[2]; ptr[2] = ptr[4]; ptr[4] = tmp; \ 287 tmp = ptr[3]; ptr[3] = ptr[5]; ptr[5] = tmp; \ 288 } 289 290 static void 291 swizzle_alsa_channels_6_64bit(void *buffer, Uint32 bufferlen) 292 { 293 SWIZ6(Uint64, buffer, bufferlen); 294 } 295 296 static void 297 swizzle_alsa_channels_6_32bit(void *buffer, Uint32 bufferlen) 298 { 299 SWIZ6(Uint32, buffer, bufferlen); 300 } 301 302 static void 303 swizzle_alsa_channels_6_16bit(void *buffer, Uint32 bufferlen) 304 { 305 SWIZ6(Uint16, buffer, bufferlen); 306 } 307 308 static void 309 swizzle_alsa_channels_6_8bit(void *buffer, Uint32 bufferlen) 310 { 311 SWIZ6(Uint8, buffer, bufferlen); 312 } 313 314 #undef SWIZ6 315 316 317 /* 318 * Called right before feeding this->hidden->mixbuf to the hardware. Swizzle 319 * channels from Windows/Mac order to the format alsalib will want. 320 */ 321 static void 322 swizzle_alsa_channels(_THIS, void *buffer, Uint32 bufferlen) 323 { 324 if (this->spec.channels == 6) { 325 switch (SDL_AUDIO_BITSIZE(this->spec.format)) { 326 case 8: swizzle_alsa_channels_6_8bit(buffer, bufferlen); break; 327 case 16: swizzle_alsa_channels_6_16bit(buffer, bufferlen); break; 328 case 32: swizzle_alsa_channels_6_32bit(buffer, bufferlen); break; 329 case 64: swizzle_alsa_channels_6_64bit(buffer, bufferlen); break; 330 default: SDL_assert(!"unhandled bitsize"); break; 331 } 332 } 333 334 /* !!! FIXME: update this for 7.1 if needed, later. */ 335 } 336 337 #ifdef SND_CHMAP_API_VERSION 338 /* Some devices have the right channel map, no swizzling necessary */ 339 static void 340 no_swizzle(_THIS, void *buffer, Uint32 bufferlen) 341 { 342 } 343 #endif /* SND_CHMAP_API_VERSION */ 344 345 346 static void 347 ALSA_PlayDevice(_THIS) 348 { 349 const Uint8 *sample_buf = (const Uint8 *) this->hidden->mixbuf; 350 const int frame_size = ((SDL_AUDIO_BITSIZE(this->spec.format)) / 8) * 351 this->spec.channels; 352 snd_pcm_uframes_t frames_left = ((snd_pcm_uframes_t) this->spec.samples); 353 354 this->hidden->swizzle_func(this, this->hidden->mixbuf, frames_left); 355 356 while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) { 357 int status = ALSA_snd_pcm_writei(this->hidden->pcm_handle, 358 sample_buf, frames_left); 359 360 if (status < 0) { 361 if (status == -EAGAIN) { 362 /* Apparently snd_pcm_recover() doesn't handle this case - 363 does it assume snd_pcm_wait() above? */ 364 SDL_Delay(1); 365 continue; 366 } 367 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0); 368 if (status < 0) { 369 /* Hmm, not much we can do - abort */ 370 fprintf(stderr, "ALSA write failed (unrecoverable): %s\n", 371 ALSA_snd_strerror(status)); 372 SDL_OpenedAudioDeviceDisconnected(this); 373 return; 374 } 375 continue; 376 } 377 else if (status == 0) { 378 /* No frames were written (no available space in pcm device). 379 Allow other threads to catch up. */ 380 Uint32 delay = (frames_left / 2 * 1000) / this->spec.freq; 381 SDL_Delay(delay); 382 } 383 384 sample_buf += status * frame_size; 385 frames_left -= status; 386 } 387 } 388 389 static Uint8 * 390 ALSA_GetDeviceBuf(_THIS) 391 { 392 return (this->hidden->mixbuf); 393 } 394 395 static int 396 ALSA_CaptureFromDevice(_THIS, void *buffer, int buflen) 397 { 398 Uint8 *sample_buf = (Uint8 *) buffer; 399 const int frame_size = ((SDL_AUDIO_BITSIZE(this->spec.format)) / 8) * 400 this->spec.channels; 401 const int total_frames = buflen / frame_size; 402 snd_pcm_uframes_t frames_left = total_frames; 403 snd_pcm_uframes_t wait_time = frame_size / 2; 404 405 SDL_assert((buflen % frame_size) == 0); 406 407 while ( frames_left > 0 && SDL_AtomicGet(&this->enabled) ) { 408 int status; 409 410 status = ALSA_snd_pcm_readi(this->hidden->pcm_handle, 411 sample_buf, frames_left); 412 413 if (status == -EAGAIN) { 414 ALSA_snd_pcm_wait(this->hidden->pcm_handle, wait_time); 415 status = 0; 416 } 417 else if (status < 0) { 418 /*printf("ALSA: capture error %d\n", status);*/ 419 status = ALSA_snd_pcm_recover(this->hidden->pcm_handle, status, 0); 420 if (status < 0) { 421 /* Hmm, not much we can do - abort */ 422 fprintf(stderr, "ALSA read failed (unrecoverable): %s\n", 423 ALSA_snd_strerror(status)); 424 return -1; 425 } 426 continue; 427 } 428 429 /*printf("ALSA: captured %d bytes\n", status * frame_size);*/ 430 sample_buf += status * frame_size; 431 frames_left -= status; 432 } 433 434 this->hidden->swizzle_func(this, buffer, total_frames - frames_left); 435 436 return (total_frames - frames_left) * frame_size; 437 } 438 439 static void 440 ALSA_FlushCapture(_THIS) 441 { 442 ALSA_snd_pcm_reset(this->hidden->pcm_handle); 443 } 444 445 static void 446 ALSA_CloseDevice(_THIS) 447 { 448 if (this->hidden->pcm_handle) { 449 /* Wait for the submitted audio to drain 450 ALSA_snd_pcm_drop() can hang, so don't use that. 451 */ 452 Uint32 delay = ((this->spec.samples * 1000) / this->spec.freq) * 2; 453 SDL_Delay(delay); 454 455 ALSA_snd_pcm_close(this->hidden->pcm_handle); 456 } 457 SDL_free(this->hidden->mixbuf); 458 SDL_free(this->hidden); 459 } 460 461 static int 462 ALSA_set_buffer_size(_THIS, snd_pcm_hw_params_t *params) 463 { 464 int status; 465 snd_pcm_hw_params_t *hwparams; 466 snd_pcm_uframes_t persize; 467 unsigned int periods; 468 469 /* Copy the hardware parameters for this setup */ 470 snd_pcm_hw_params_alloca(&hwparams); 471 ALSA_snd_pcm_hw_params_copy(hwparams, params); 472 473 /* Attempt to match the period size to the requested buffer size */ 474 persize = this->spec.samples; 475 status = ALSA_snd_pcm_hw_params_set_period_size_near( 476 this->hidden->pcm_handle, hwparams, &persize, NULL); 477 if ( status < 0 ) { 478 return(-1); 479 } 480 481 /* Need to at least double buffer */ 482 periods = 2; 483 status = ALSA_snd_pcm_hw_params_set_periods_min( 484 this->hidden->pcm_handle, hwparams, &periods, NULL); 485 if ( status < 0 ) { 486 return(-1); 487 } 488 489 status = ALSA_snd_pcm_hw_params_set_periods_first( 490 this->hidden->pcm_handle, hwparams, &periods, NULL); 491 if ( status < 0 ) { 492 return(-1); 493 } 494 495 /* "set" the hardware with the desired parameters */ 496 status = ALSA_snd_pcm_hw_params(this->hidden->pcm_handle, hwparams); 497 if ( status < 0 ) { 498 return(-1); 499 } 500 501 this->spec.samples = persize; 502 503 /* This is useful for debugging */ 504 if ( SDL_getenv("SDL_AUDIO_ALSA_DEBUG") ) { 505 snd_pcm_uframes_t bufsize; 506 507 ALSA_snd_pcm_hw_params_get_buffer_size(hwparams, &bufsize); 508 509 fprintf(stderr, 510 "ALSA: period size = %ld, periods = %u, buffer size = %lu\n", 511 persize, periods, bufsize); 512 } 513 514 return(0); 515 } 516 517 static int 518 ALSA_OpenDevice(_THIS, void *handle, const char *devname, int iscapture) 519 { 520 int status = 0; 521 snd_pcm_t *pcm_handle = NULL; 522 snd_pcm_hw_params_t *hwparams = NULL; 523 snd_pcm_sw_params_t *swparams = NULL; 524 snd_pcm_format_t format = 0; 525 SDL_AudioFormat test_format = 0; 526 unsigned int rate = 0; 527 unsigned int channels = 0; 528 #ifdef SND_CHMAP_API_VERSION 529 snd_pcm_chmap_t *chmap; 530 char chmap_str[64]; 531 #endif 532 533 /* Initialize all variables that we clean on shutdown */ 534 this->hidden = (struct SDL_PrivateAudioData *) 535 SDL_malloc((sizeof *this->hidden)); 536 if (this->hidden == NULL) { 537 return SDL_OutOfMemory(); 538 } 539 SDL_zerop(this->hidden); 540 541 /* Open the audio device */ 542 /* Name of device should depend on # channels in spec */ 543 status = ALSA_snd_pcm_open(&pcm_handle, 544 get_audio_device(handle, this->spec.channels), 545 iscapture ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 546 SND_PCM_NONBLOCK); 547 548 if (status < 0) { 549 return SDL_SetError("ALSA: Couldn't open audio device: %s", 550 ALSA_snd_strerror(status)); 551 } 552 553 this->hidden->pcm_handle = pcm_handle; 554 555 /* Figure out what the hardware is capable of */ 556 snd_pcm_hw_params_alloca(&hwparams); 557 status = ALSA_snd_pcm_hw_params_any(pcm_handle, hwparams); 558 if (status < 0) { 559 return SDL_SetError("ALSA: Couldn't get hardware config: %s", 560 ALSA_snd_strerror(status)); 561 } 562 563 /* SDL only uses interleaved sample output */ 564 status = ALSA_snd_pcm_hw_params_set_access(pcm_handle, hwparams, 565 SND_PCM_ACCESS_RW_INTERLEAVED); 566 if (status < 0) { 567 return SDL_SetError("ALSA: Couldn't set interleaved access: %s", 568 ALSA_snd_strerror(status)); 569 } 570 571 /* Try for a closest match on audio format */ 572 status = -1; 573 for (test_format = SDL_FirstAudioFormat(this->spec.format); 574 test_format && (status < 0);) { 575 status = 0; /* if we can't support a format, it'll become -1. */ 576 switch (test_format) { 577 case AUDIO_U8: 578 format = SND_PCM_FORMAT_U8; 579 break; 580 case AUDIO_S8: 581 format = SND_PCM_FORMAT_S8; 582 break; 583 case AUDIO_S16LSB: 584 format = SND_PCM_FORMAT_S16_LE; 585 break; 586 case AUDIO_S16MSB: 587 format = SND_PCM_FORMAT_S16_BE; 588 break; 589 case AUDIO_U16LSB: 590 format = SND_PCM_FORMAT_U16_LE; 591 break; 592 case AUDIO_U16MSB: 593 format = SND_PCM_FORMAT_U16_BE; 594 break; 595 case AUDIO_S32LSB: 596 format = SND_PCM_FORMAT_S32_LE; 597 break; 598 case AUDIO_S32MSB: 599 format = SND_PCM_FORMAT_S32_BE; 600 break; 601 case AUDIO_F32LSB: 602 format = SND_PCM_FORMAT_FLOAT_LE; 603 break; 604 case AUDIO_F32MSB: 605 format = SND_PCM_FORMAT_FLOAT_BE; 606 break; 607 default: 608 status = -1; 609 break; 610 } 611 if (status >= 0) { 612 status = ALSA_snd_pcm_hw_params_set_format(pcm_handle, 613 hwparams, format); 614 } 615 if (status < 0) { 616 test_format = SDL_NextAudioFormat(); 617 } 618 } 619 if (status < 0) { 620 return SDL_SetError("ALSA: Couldn't find any hardware audio formats"); 621 } 622 this->spec.format = test_format; 623 624 /* Validate number of channels and determine if swizzling is necessary 625 * Assume original swizzling, until proven otherwise. 626 */ 627 this->hidden->swizzle_func = swizzle_alsa_channels; 628 #ifdef SND_CHMAP_API_VERSION 629 chmap = ALSA_snd_pcm_get_chmap(pcm_handle); 630 if (chmap) { 631 ALSA_snd_pcm_chmap_print(chmap, sizeof(chmap_str), chmap_str); 632 if (SDL_strcmp("FL FR FC LFE RL RR", chmap_str) == 0 || 633 SDL_strcmp("FL FR FC LFE SL SR", chmap_str) == 0) { 634 this->hidden->swizzle_func = no_swizzle; 635 } 636 free(chmap); 637 } 638 #endif /* SND_CHMAP_API_VERSION */ 639 640 /* Set the number of channels */ 641 status = ALSA_snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 642 this->spec.channels); 643 channels = this->spec.channels; 644 if (status < 0) { 645 status = ALSA_snd_pcm_hw_params_get_channels(hwparams, &channels); 646 if (status < 0) { 647 return SDL_SetError("ALSA: Couldn't set audio channels"); 648 } 649 this->spec.channels = channels; 650 } 651 652 /* Set the audio rate */ 653 rate = this->spec.freq; 654 status = ALSA_snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, 655 &rate, NULL); 656 if (status < 0) { 657 return SDL_SetError("ALSA: Couldn't set audio frequency: %s", 658 ALSA_snd_strerror(status)); 659 } 660 this->spec.freq = rate; 661 662 /* Set the buffer size, in samples */ 663 status = ALSA_set_buffer_size(this, hwparams); 664 if (status < 0) { 665 return SDL_SetError("Couldn't set hardware audio parameters: %s", ALSA_snd_strerror(status)); 666 } 667 668 /* Set the software parameters */ 669 snd_pcm_sw_params_alloca(&swparams); 670 status = ALSA_snd_pcm_sw_params_current(pcm_handle, swparams); 671 if (status < 0) { 672 return SDL_SetError("ALSA: Couldn't get software config: %s", 673 ALSA_snd_strerror(status)); 674 } 675 status = ALSA_snd_pcm_sw_params_set_avail_min(pcm_handle, swparams, this->spec.samples); 676 if (status < 0) { 677 return SDL_SetError("Couldn't set minimum available samples: %s", 678 ALSA_snd_strerror(status)); 679 } 680 status = 681 ALSA_snd_pcm_sw_params_set_start_threshold(pcm_handle, swparams, 1); 682 if (status < 0) { 683 return SDL_SetError("ALSA: Couldn't set start threshold: %s", 684 ALSA_snd_strerror(status)); 685 } 686 status = ALSA_snd_pcm_sw_params(pcm_handle, swparams); 687 if (status < 0) { 688 return SDL_SetError("Couldn't set software audio parameters: %s", 689 ALSA_snd_strerror(status)); 690 } 691 692 /* Calculate the final parameters for this audio specification */ 693 SDL_CalculateAudioSpec(&this->spec); 694 695 /* Allocate mixing buffer */ 696 if (!iscapture) { 697 this->hidden->mixlen = this->spec.size; 698 this->hidden->mixbuf = (Uint8 *) SDL_malloc(this->hidden->mixlen); 699 if (this->hidden->mixbuf == NULL) { 700 return SDL_OutOfMemory(); 701 } 702 SDL_memset(this->hidden->mixbuf, this->spec.silence, this->hidden->mixlen); 703 } 704 705 #if !SDL_ALSA_NON_BLOCKING 706 if (!iscapture) { 707 ALSA_snd_pcm_nonblock(pcm_handle, 0); 708 } 709 #endif 710 711 /* We're ready to rock and roll. :-) */ 712 return 0; 713 } 714 715 typedef struct ALSA_Device 716 { 717 char *name; 718 SDL_bool iscapture; 719 struct ALSA_Device *next; 720 } ALSA_Device; 721 722 static void 723 add_device(const int iscapture, const char *name, void *hint, ALSA_Device **pSeen) 724 { 725 ALSA_Device *dev = SDL_malloc(sizeof (ALSA_Device)); 726 char *desc; 727 char *handle = NULL; 728 char *ptr; 729 730 if (!dev) { 731 return; 732 } 733 734 /* Not all alsa devices are enumerable via snd_device_name_get_hint 735 (i.e. bluetooth devices). Therefore if hint is passed in to this 736 function as NULL, assume name contains desc. 737 Make sure not to free the storage associated with desc in this case */ 738 if (hint) { 739 desc = ALSA_snd_device_name_get_hint(hint, "DESC"); 740 if (!desc) { 741 SDL_free(dev); 742 return; 743 } 744 } else { 745 desc = (char *) name; 746 } 747 748 SDL_assert(name != NULL); 749 750 /* some strings have newlines, like "HDA NVidia, HDMI 0\nHDMI Audio Output". 751 just chop the extra lines off, this seems to get a reasonable device 752 name without extra details. */ 753 if ((ptr = strchr(desc, '\n')) != NULL) { 754 *ptr = '\0'; 755 } 756 757 /*printf("ALSA: adding %s device '%s' (%s)\n", iscapture ? "capture" : "output", name, desc);*/ 758 759 handle = SDL_strdup(name); 760 if (!handle) { 761 if (hint) { 762 free(desc); 763 } 764 SDL_free(dev); 765 return; 766 } 767 768 SDL_AddAudioDevice(iscapture, desc, handle); 769 if (hint) 770 free(desc); 771 dev->name = handle; 772 dev->iscapture = iscapture; 773 dev->next = *pSeen; 774 *pSeen = dev; 775 } 776 777 778 static SDL_atomic_t ALSA_hotplug_shutdown; 779 static SDL_Thread *ALSA_hotplug_thread; 780 781 static int SDLCALL 782 ALSA_HotplugThread(void *arg) 783 { 784 SDL_sem *first_run_semaphore = (SDL_sem *) arg; 785 ALSA_Device *devices = NULL; 786 ALSA_Device *next; 787 ALSA_Device *dev; 788 Uint32 ticks; 789 790 SDL_SetThreadPriority(SDL_THREAD_PRIORITY_LOW); 791 792 while (!SDL_AtomicGet(&ALSA_hotplug_shutdown)) { 793 void **hints = NULL; 794 ALSA_Device *unseen; 795 ALSA_Device *seen; 796 ALSA_Device *prev; 797 798 if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) { 799 int i, j; 800 const char *match = NULL; 801 int bestmatch = 0xFFFF; 802 size_t match_len = 0; 803 int defaultdev = -1; 804 static const char * const prefixes[] = { 805 "hw:", "sysdefault:", "default:", NULL 806 }; 807 808 unseen = devices; 809 seen = NULL; 810 /* Apparently there are several different ways that ALSA lists 811 actual hardware. It could be prefixed with "hw:" or "default:" 812 or "sysdefault:" and maybe others. Go through the list and see 813 if we can find a preferred prefix for the system. */ 814 for (i = 0; hints[i]; i++) { 815 char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); 816 if (!name) { 817 continue; 818 } 819 820 /* full name, not a prefix */ 821 if ((defaultdev == -1) && (SDL_strcmp(name, "default") == 0)) { 822 defaultdev = i; 823 } 824 825 for (j = 0; prefixes[j]; j++) { 826 const char *prefix = prefixes[j]; 827 const size_t prefixlen = SDL_strlen(prefix); 828 if (SDL_strncmp(name, prefix, prefixlen) == 0) { 829 if (j < bestmatch) { 830 bestmatch = j; 831 match = prefix; 832 match_len = prefixlen; 833 } 834 } 835 } 836 837 free(name); 838 } 839 840 /* look through the list of device names to find matches */ 841 for (i = 0; hints[i]; i++) { 842 char *name; 843 844 /* if we didn't find a device name prefix we like at all... */ 845 if ((!match) && (defaultdev != i)) { 846 continue; /* ...skip anything that isn't the default device. */ 847 } 848 849 name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); 850 if (!name) { 851 continue; 852 } 853 854 /* only want physical hardware interfaces */ 855 if (!match || (SDL_strncmp(name, match, match_len) == 0)) { 856 char *ioid = ALSA_snd_device_name_get_hint(hints[i], "IOID"); 857 const SDL_bool isoutput = (ioid == NULL) || (SDL_strcmp(ioid, "Output") == 0); 858 const SDL_bool isinput = (ioid == NULL) || (SDL_strcmp(ioid, "Input") == 0); 859 SDL_bool have_output = SDL_FALSE; 860 SDL_bool have_input = SDL_FALSE; 861 862 free(ioid); 863 864 if (!isoutput && !isinput) { 865 free(name); 866 continue; 867 } 868 869 prev = NULL; 870 for (dev = unseen; dev; dev = next) { 871 next = dev->next; 872 if ( (SDL_strcmp(dev->name, name) == 0) && (((isinput) && dev->iscapture) || ((isoutput) && !dev->iscapture)) ) { 873 if (prev) { 874 prev->next = next; 875 } else { 876 unseen = next; 877 } 878 dev->next = seen; 879 seen = dev; 880 if (isinput) have_input = SDL_TRUE; 881 if (isoutput) have_output = SDL_TRUE; 882 } else { 883 prev = dev; 884 } 885 } 886 887 if (isinput && !have_input) { 888 add_device(SDL_TRUE, name, hints[i], &seen); 889 } 890 if (isoutput && !have_output) { 891 add_device(SDL_FALSE, name, hints[i], &seen); 892 } 893 } 894 895 free(name); 896 } 897 898 ALSA_snd_device_name_free_hint(hints); 899 900 devices = seen; /* now we have a known-good list of attached devices. */ 901 902 /* report anything still in unseen as removed. */ 903 for (dev = unseen; dev; dev = next) { 904 /*printf("ALSA: removing usb %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/ 905 next = dev->next; 906 SDL_RemoveAudioDevice(dev->iscapture, dev->name); 907 SDL_free(dev->name); 908 SDL_free(dev); 909 } 910 } 911 912 /* On first run, tell ALSA_DetectDevices() that we have a complete device list so it can return. */ 913 if (first_run_semaphore) { 914 SDL_SemPost(first_run_semaphore); 915 first_run_semaphore = NULL; /* let other thread clean it up. */ 916 } 917 918 /* Block awhile before checking again, unless we're told to stop. */ 919 ticks = SDL_GetTicks() + 5000; 920 while (!SDL_AtomicGet(&ALSA_hotplug_shutdown) && !SDL_TICKS_PASSED(SDL_GetTicks(), ticks)) { 921 SDL_Delay(100); 922 } 923 } 924 925 /* Shutting down! Clean up any data we've gathered. */ 926 for (dev = devices; dev; dev = next) { 927 /*printf("ALSA: at shutdown, removing %s device '%s'\n", dev->iscapture ? "capture" : "output", dev->name);*/ 928 next = dev->next; 929 SDL_free(dev->name); 930 SDL_free(dev); 931 } 932 933 return 0; 934 } 935 936 static void 937 ALSA_DetectDevices(void) 938 { 939 /* Start the device detection thread here, wait for an initial iteration to complete. */ 940 SDL_sem *semaphore = SDL_CreateSemaphore(0); 941 if (!semaphore) { 942 return; /* oh well. */ 943 } 944 945 SDL_AtomicSet(&ALSA_hotplug_shutdown, 0); 946 947 ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", semaphore); 948 if (ALSA_hotplug_thread) { 949 SDL_SemWait(semaphore); /* wait for the first iteration to finish. */ 950 } 951 952 SDL_DestroySemaphore(semaphore); 953 } 954 955 static void 956 ALSA_Deinitialize(void) 957 { 958 if (ALSA_hotplug_thread != NULL) { 959 SDL_AtomicSet(&ALSA_hotplug_shutdown, 1); 960 SDL_WaitThread(ALSA_hotplug_thread, NULL); 961 ALSA_hotplug_thread = NULL; 962 } 963 964 UnloadALSALibrary(); 965 } 966 967 static int 968 ALSA_Init(SDL_AudioDriverImpl * impl) 969 { 970 if (LoadALSALibrary() < 0) { 971 return 0; 972 } 973 974 /* Set the function pointers */ 975 impl->DetectDevices = ALSA_DetectDevices; 976 impl->OpenDevice = ALSA_OpenDevice; 977 impl->WaitDevice = ALSA_WaitDevice; 978 impl->GetDeviceBuf = ALSA_GetDeviceBuf; 979 impl->PlayDevice = ALSA_PlayDevice; 980 impl->CloseDevice = ALSA_CloseDevice; 981 impl->Deinitialize = ALSA_Deinitialize; 982 impl->CaptureFromDevice = ALSA_CaptureFromDevice; 983 impl->FlushCapture = ALSA_FlushCapture; 984 985 impl->HasCaptureSupport = SDL_TRUE; 986 987 return 1; /* this audio target is available. */ 988 } 989 990 991 AudioBootStrap ALSA_bootstrap = { 992 "alsa", "ALSA PCM audio", ALSA_Init, 0 993 }; 994 995 #endif /* SDL_AUDIO_DRIVER_ALSA */ 996 997 /* vi: set ts=4 sw=4 expandtab: */