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