Atlas - SDL_alsa_audio.c

Home / ext / SDL / src / audio / alsa Lines: 1 | Size: 58904 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 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#ifdef SDL_AUDIO_DRIVER_ALSA 24 25#ifndef SDL_ALSA_NON_BLOCKING 26#define SDL_ALSA_NON_BLOCKING 0 27#endif 28 29// without the thread, you will detect devices on startup, but will not get further hotplug events. But that might be okay. 30#ifndef SDL_ALSA_HOTPLUG_THREAD 31#define SDL_ALSA_HOTPLUG_THREAD 1 32#endif 33 34// this turns off debug logging completely (but by default this goes to the bitbucket). 35#ifndef SDL_ALSA_DEBUG 36#define SDL_ALSA_DEBUG 1 37#endif 38 39#include "../SDL_sysaudio.h" 40#include "SDL_alsa_audio.h" 41#include "../../core/linux/SDL_udev.h" 42 43#if SDL_ALSA_DEBUG 44#define LOGDEBUG(...) SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, "ALSA: " __VA_ARGS__) 45#else 46#define LOGDEBUG(...) 47#endif 48 49//TODO: cleanup once the code settled down 50 51static int (*ALSA_snd_pcm_open)(snd_pcm_t **, const char *, snd_pcm_stream_t, int); 52static int (*ALSA_snd_pcm_close)(snd_pcm_t *pcm); 53static int (*ALSA_snd_pcm_start)(snd_pcm_t *pcm); 54static snd_pcm_sframes_t (*ALSA_snd_pcm_writei)(snd_pcm_t *, const void *, snd_pcm_uframes_t); 55static snd_pcm_sframes_t (*ALSA_snd_pcm_readi)(snd_pcm_t *, void *, snd_pcm_uframes_t); 56static int (*ALSA_snd_pcm_recover)(snd_pcm_t *, int, int); 57static int (*ALSA_snd_pcm_prepare)(snd_pcm_t *); 58static int (*ALSA_snd_pcm_drain)(snd_pcm_t *); 59static const char *(*ALSA_snd_strerror)(int); 60static size_t (*ALSA_snd_pcm_hw_params_sizeof)(void); 61static size_t (*ALSA_snd_pcm_sw_params_sizeof)(void); 62static void (*ALSA_snd_pcm_hw_params_copy)(snd_pcm_hw_params_t *, const snd_pcm_hw_params_t *); 63static int (*ALSA_snd_pcm_hw_params_any)(snd_pcm_t *, snd_pcm_hw_params_t *); 64static int (*ALSA_snd_pcm_hw_params_set_access)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_access_t); 65static int (*ALSA_snd_pcm_hw_params_set_format)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_format_t); 66static int (*ALSA_snd_pcm_hw_params_set_channels)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int); 67static int (*ALSA_snd_pcm_hw_params_get_channels)(const snd_pcm_hw_params_t *, unsigned int *); 68static int (*ALSA_snd_pcm_hw_params_set_rate_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); 69static int (*ALSA_snd_pcm_hw_params_set_period_size_near)(snd_pcm_t *, snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *); 70static int (*ALSA_snd_pcm_hw_params_get_period_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *, int *); 71static int (*ALSA_snd_pcm_hw_params_set_periods_min)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); 72static int (*ALSA_snd_pcm_hw_params_set_periods_first)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *, int *); 73static int (*ALSA_snd_pcm_hw_params_get_periods)(const snd_pcm_hw_params_t *, unsigned int *, int *); 74static int (*ALSA_snd_pcm_hw_params_set_buffer_size_near)(snd_pcm_t *pcm, snd_pcm_hw_params_t *, snd_pcm_uframes_t *); 75static int (*ALSA_snd_pcm_hw_params_get_buffer_size)(const snd_pcm_hw_params_t *, snd_pcm_uframes_t *); 76static int (*ALSA_snd_pcm_hw_params)(snd_pcm_t *, snd_pcm_hw_params_t *); 77static int (*ALSA_snd_pcm_sw_params_current)(snd_pcm_t *, 78 snd_pcm_sw_params_t *); 79static int (*ALSA_snd_pcm_sw_params_set_start_threshold)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); 80static int (*ALSA_snd_pcm_sw_params)(snd_pcm_t *, snd_pcm_sw_params_t *); 81static int (*ALSA_snd_pcm_nonblock)(snd_pcm_t *, int); 82static int (*ALSA_snd_pcm_wait)(snd_pcm_t *, int); 83static int (*ALSA_snd_pcm_sw_params_set_avail_min)(snd_pcm_t *, snd_pcm_sw_params_t *, snd_pcm_uframes_t); 84static int (*ALSA_snd_pcm_reset)(snd_pcm_t *); 85static snd_pcm_state_t (*ALSA_snd_pcm_state)(snd_pcm_t *); 86static int (*ALSA_snd_device_name_hint)(int, const char *, void ***); 87static char *(*ALSA_snd_device_name_get_hint)(const void *, const char *); 88static int (*ALSA_snd_device_name_free_hint)(void **); 89static snd_pcm_sframes_t (*ALSA_snd_pcm_avail)(snd_pcm_t *); 90static size_t (*ALSA_snd_ctl_card_info_sizeof)(void); 91static size_t (*ALSA_snd_pcm_info_sizeof)(void); 92static int (*ALSA_snd_card_next)(int *); 93static int (*ALSA_snd_ctl_open)(snd_ctl_t **,const char *,int); 94static int (*ALSA_snd_ctl_close)(snd_ctl_t *); 95static int (*ALSA_snd_ctl_card_info)(snd_ctl_t *, snd_ctl_card_info_t *); 96static int (*ALSA_snd_ctl_pcm_next_device)(snd_ctl_t *, int *); 97static unsigned int (*ALSA_snd_pcm_info_get_subdevices_count)(const snd_pcm_info_t *); 98static void (*ALSA_snd_pcm_info_set_device)(snd_pcm_info_t *, unsigned int); 99static void (*ALSA_snd_pcm_info_set_subdevice)(snd_pcm_info_t *, unsigned int); 100static void (*ALSA_snd_pcm_info_set_stream)(snd_pcm_info_t *, snd_pcm_stream_t); 101static int (*ALSA_snd_ctl_pcm_info)(snd_ctl_t *, snd_pcm_info_t *); 102static const char *(*ALSA_snd_ctl_card_info_get_id)(const snd_ctl_card_info_t *); 103static const char *(*ALSA_snd_pcm_info_get_name)(const snd_pcm_info_t *); 104static const char *(*ALSA_snd_pcm_info_get_subdevice_name)(const snd_pcm_info_t *); 105static const char *(*ALSA_snd_ctl_card_info_get_name)(const snd_ctl_card_info_t *); 106static void (*ALSA_snd_ctl_card_info_clear)(snd_ctl_card_info_t *); 107static int (*ALSA_snd_pcm_hw_free)(snd_pcm_t *); 108static int (*ALSA_snd_pcm_hw_params_set_channels_near)(snd_pcm_t *, snd_pcm_hw_params_t *, unsigned int *); 109static snd_pcm_chmap_query_t **(*ALSA_snd_pcm_query_chmaps)(snd_pcm_t *pcm); 110static void (*ALSA_snd_pcm_free_chmaps)(snd_pcm_chmap_query_t **maps); 111static int (*ALSA_snd_pcm_set_chmap)(snd_pcm_t *, const snd_pcm_chmap_t *); 112static int (*ALSA_snd_pcm_chmap_print)(const snd_pcm_chmap_t *, size_t, char *); 113 114#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC 115#define snd_pcm_hw_params_sizeof ALSA_snd_pcm_hw_params_sizeof 116#define snd_pcm_sw_params_sizeof ALSA_snd_pcm_sw_params_sizeof 117 118static const char *alsa_library = SDL_AUDIO_DRIVER_ALSA_DYNAMIC; 119static SDL_SharedObject *alsa_handle = NULL; 120 121static bool load_alsa_sym(const char *fn, void **addr) 122{ 123 *addr = SDL_LoadFunction(alsa_handle, fn); 124 if (!*addr) { 125 // Don't call SDL_SetError(): SDL_LoadFunction already did. 126 return false; 127 } 128 129 return true; 130} 131 132// cast funcs to char* first, to please GCC's strict aliasing rules. 133#define SDL_ALSA_SYM(x) \ 134 if (!load_alsa_sym(#x, (void **)(char *)&ALSA_##x)) \ 135 return false 136#else 137#define SDL_ALSA_SYM(x) ALSA_##x = x 138#endif 139 140static bool load_alsa_syms(void) 141{ 142 SDL_ALSA_SYM(snd_pcm_open); 143 SDL_ALSA_SYM(snd_pcm_close); 144 SDL_ALSA_SYM(snd_pcm_start); 145 SDL_ALSA_SYM(snd_pcm_writei); 146 SDL_ALSA_SYM(snd_pcm_readi); 147 SDL_ALSA_SYM(snd_pcm_recover); 148 SDL_ALSA_SYM(snd_pcm_prepare); 149 SDL_ALSA_SYM(snd_pcm_drain); 150 SDL_ALSA_SYM(snd_strerror); 151 SDL_ALSA_SYM(snd_pcm_hw_params_sizeof); 152 SDL_ALSA_SYM(snd_pcm_sw_params_sizeof); 153 SDL_ALSA_SYM(snd_pcm_hw_params_copy); 154 SDL_ALSA_SYM(snd_pcm_hw_params_any); 155 SDL_ALSA_SYM(snd_pcm_hw_params_set_access); 156 SDL_ALSA_SYM(snd_pcm_hw_params_set_format); 157 SDL_ALSA_SYM(snd_pcm_hw_params_set_channels); 158 SDL_ALSA_SYM(snd_pcm_hw_params_get_channels); 159 SDL_ALSA_SYM(snd_pcm_hw_params_set_rate_near); 160 SDL_ALSA_SYM(snd_pcm_hw_params_set_period_size_near); 161 SDL_ALSA_SYM(snd_pcm_hw_params_get_period_size); 162 SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_min); 163 SDL_ALSA_SYM(snd_pcm_hw_params_set_periods_first); 164 SDL_ALSA_SYM(snd_pcm_hw_params_get_periods); 165 SDL_ALSA_SYM(snd_pcm_hw_params_set_buffer_size_near); 166 SDL_ALSA_SYM(snd_pcm_hw_params_get_buffer_size); 167 SDL_ALSA_SYM(snd_pcm_hw_params); 168 SDL_ALSA_SYM(snd_pcm_sw_params_current); 169 SDL_ALSA_SYM(snd_pcm_sw_params_set_start_threshold); 170 SDL_ALSA_SYM(snd_pcm_sw_params); 171 SDL_ALSA_SYM(snd_pcm_nonblock); 172 SDL_ALSA_SYM(snd_pcm_wait); 173 SDL_ALSA_SYM(snd_pcm_sw_params_set_avail_min); 174 SDL_ALSA_SYM(snd_pcm_reset); 175 SDL_ALSA_SYM(snd_pcm_state); 176 SDL_ALSA_SYM(snd_device_name_hint); 177 SDL_ALSA_SYM(snd_device_name_get_hint); 178 SDL_ALSA_SYM(snd_device_name_free_hint); 179 SDL_ALSA_SYM(snd_pcm_avail); 180 SDL_ALSA_SYM(snd_ctl_card_info_sizeof); 181 SDL_ALSA_SYM(snd_pcm_info_sizeof); 182 SDL_ALSA_SYM(snd_card_next); 183 SDL_ALSA_SYM(snd_ctl_open); 184 SDL_ALSA_SYM(snd_ctl_close); 185 SDL_ALSA_SYM(snd_ctl_card_info); 186 SDL_ALSA_SYM(snd_ctl_pcm_next_device); 187 SDL_ALSA_SYM(snd_pcm_info_get_subdevices_count); 188 SDL_ALSA_SYM(snd_pcm_info_set_device); 189 SDL_ALSA_SYM(snd_pcm_info_set_subdevice); 190 SDL_ALSA_SYM(snd_pcm_info_set_stream); 191 SDL_ALSA_SYM(snd_ctl_pcm_info); 192 SDL_ALSA_SYM(snd_pcm_info_get_subdevices_count); 193 SDL_ALSA_SYM(snd_ctl_card_info_get_id); 194 SDL_ALSA_SYM(snd_pcm_info_get_name); 195 SDL_ALSA_SYM(snd_pcm_info_get_subdevice_name); 196 SDL_ALSA_SYM(snd_ctl_card_info_get_name); 197 SDL_ALSA_SYM(snd_ctl_card_info_clear); 198 SDL_ALSA_SYM(snd_pcm_hw_free); 199 SDL_ALSA_SYM(snd_pcm_hw_params_set_channels_near); 200 SDL_ALSA_SYM(snd_pcm_query_chmaps); 201 SDL_ALSA_SYM(snd_pcm_free_chmaps); 202 SDL_ALSA_SYM(snd_pcm_set_chmap); 203 SDL_ALSA_SYM(snd_pcm_chmap_print); 204 205 return true; 206} 207 208#undef SDL_ALSA_SYM 209 210#ifdef SDL_AUDIO_DRIVER_ALSA_DYNAMIC 211 212SDL_ELF_NOTE_DLOPEN( 213 "audio-libalsa", 214 "Support for audio through libalsa", 215 SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, 216 SDL_AUDIO_DRIVER_ALSA_DYNAMIC 217) 218 219static void UnloadALSALibrary(void) 220{ 221 if (alsa_handle) { 222 SDL_UnloadObject(alsa_handle); 223 alsa_handle = NULL; 224 } 225} 226 227static bool LoadALSALibrary(void) 228{ 229 bool retval = true; 230 if (!alsa_handle) { 231 alsa_handle = SDL_LoadObject(alsa_library); 232 if (!alsa_handle) { 233 retval = false; 234 // Don't call SDL_SetError(): SDL_LoadObject already did. 235 } else { 236 retval = load_alsa_syms(); 237 if (!retval) { 238 UnloadALSALibrary(); 239 } 240 } 241 } 242 return retval; 243} 244 245#else 246 247static void UnloadALSALibrary(void) 248{ 249} 250 251static bool LoadALSALibrary(void) 252{ 253 load_alsa_syms(); 254 return true; 255} 256 257#endif // SDL_AUDIO_DRIVER_ALSA_DYNAMIC 258 259static const char *ALSA_device_prefix = NULL; 260static void ALSA_guess_device_prefix(void) 261{ 262 if (ALSA_device_prefix) { 263 return; // already calculated. 264 } 265 266 // Apparently there are several different ways that ALSA lists 267 // actual hardware. It could be prefixed with "hw:" or "default:" 268 // or "sysdefault:" and maybe others. Go through the list and see 269 // if we can find a preferred prefix for the system. 270 271 static const char *const prefixes[] = { 272 "hw:", "sysdefault:", "default:" 273 }; 274 275 void **hints = NULL; 276 if (ALSA_snd_device_name_hint(-1, "pcm", &hints) == 0) { 277 for (int i = 0; hints[i]; i++) { 278 char *name = ALSA_snd_device_name_get_hint(hints[i], "NAME"); 279 if (name) { 280 for (int j = 0; j < SDL_arraysize(prefixes); j++) { 281 const char *prefix = prefixes[j]; 282 const size_t prefixlen = SDL_strlen(prefix); 283 if (SDL_strncmp(name, prefix, prefixlen) == 0) { 284 ALSA_device_prefix = prefix; 285 break; 286 } 287 } 288 free(name); // This should NOT be SDL_free() 289 290 if (ALSA_device_prefix) { 291 break; 292 } 293 } 294 } 295 } 296 297 if (!ALSA_device_prefix) { 298 ALSA_device_prefix = prefixes[0]; // oh well. 299 } 300 301 LOGDEBUG("device prefix is probably '%s'", ALSA_device_prefix); 302} 303 304typedef struct ALSA_Device 305{ 306 // the unicity key is the couple (id,recording) 307 char *id; // empty means canonical default 308 char *name; 309 bool recording; 310 struct ALSA_Device *next; 311} ALSA_Device; 312 313static const ALSA_Device default_playback_handle = { 314 "", 315 "default", 316 false, 317 NULL 318}; 319 320static const ALSA_Device default_recording_handle = { 321 "", 322 "default", 323 true, 324 NULL 325}; 326 327// TODO: Figure out the "right"(TM) way. For the moment we presume that if a system is using a 328// software mixer for application audio sharing which is not the linux native alsa[dmix], for 329// instance jack/pulseaudio2[pipewire]/pulseaudio1/esound/etc, we expect the system integrators did 330// configure the canonical default to the right alsa PCM plugin for their software mixer. 331// 332// All the above may be completely wrong. 333static char *get_pcm_str(void *handle) 334{ 335 SDL_assert(handle != NULL); // SDL2 used NULL to mean "default" but that's not true in SDL3. 336 ALSA_Device *dev = (ALSA_Device *)handle; 337 char *pcm_str = NULL; 338 339 if (SDL_strlen(dev->id) == 0) { 340 // If the user does not want to go thru the default PCM or the canonical default, the 341 // the configuration space being _massive_, give the user the ability to specify 342 // its own PCMs using environment variables. It will have to fit SDL constraints though. 343 const char *devname = SDL_GetHint(dev->recording ? SDL_HINT_AUDIO_ALSA_DEFAULT_RECORDING_DEVICE : SDL_HINT_AUDIO_ALSA_DEFAULT_PLAYBACK_DEVICE); 344 if (!devname) { 345 devname = SDL_GetHint(SDL_HINT_AUDIO_ALSA_DEFAULT_DEVICE); 346 if (!devname) { 347 devname = "default"; 348 } 349 } 350 pcm_str = SDL_strdup(devname); 351 } else { 352 SDL_asprintf(&pcm_str, "%sCARD=%s", ALSA_device_prefix, dev->id); 353 } 354 return pcm_str; 355} 356 357static int RecoverALSADevice(snd_pcm_t *pcm, int errnum) 358{ 359 const snd_pcm_state_t prerecovery = ALSA_snd_pcm_state(pcm); 360 const int status = ALSA_snd_pcm_recover(pcm, errnum, 0); // !!! FIXME: third parameter is non-zero to prevent libasound from printing error messages. Should we do that? 361 if (status == 0) { 362 const snd_pcm_state_t postrecovery = ALSA_snd_pcm_state(pcm); 363 if ((prerecovery == SND_PCM_STATE_XRUN) && (postrecovery == SND_PCM_STATE_PREPARED)) { 364 ALSA_snd_pcm_start(pcm); // restart the device if it stopped due to an overrun or underrun. 365 } 366 } 367 return status; 368} 369 370 371// This function waits until it is possible to write a full sound buffer 372static bool ALSA_WaitDevice(SDL_AudioDevice *device) 373{ 374 const int sample_frames = device->sample_frames; 375 const int fulldelay = (int) ((((Uint64) sample_frames) * 1000) / device->spec.freq); 376 const int delay = SDL_clamp(fulldelay, 1, 5); 377 378 while (!SDL_GetAtomicInt(&device->shutdown)) { 379 const int rc = ALSA_snd_pcm_avail(device->hidden->pcm); 380 if (rc < 0) { 381 const int status = RecoverALSADevice(device->hidden->pcm, rc); 382 if (status < 0) { 383 // Hmm, not much we can do - abort 384 SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA wait failed (unrecoverable): %s", ALSA_snd_strerror(rc)); 385 return false; 386 } 387 } 388 if (rc >= sample_frames) { 389 break; 390 } 391 SDL_Delay(delay); 392 } 393 return true; 394} 395 396static bool ALSA_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) 397{ 398 SDL_assert(buffer == device->hidden->mixbuf); 399 Uint8 *sample_buf = (Uint8 *) buffer; // !!! FIXME: deal with this without casting away constness 400 const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); 401 snd_pcm_uframes_t frames_left = (snd_pcm_uframes_t) (buflen / frame_size); 402 403 while ((frames_left > 0) && !SDL_GetAtomicInt(&device->shutdown)) { 404 const int rc = ALSA_snd_pcm_writei(device->hidden->pcm, sample_buf, frames_left); 405 //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA PLAYDEVICE: WROTE %d of %d bytes", (rc >= 0) ? ((int) (rc * frame_size)) : rc, (int) (frames_left * frame_size)); 406 SDL_assert(rc != 0); // assuming this can't happen if we used snd_pcm_wait and queried for available space. 407 if (rc < 0) { 408 SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it! 409 const int status = RecoverALSADevice(device->hidden->pcm, rc); 410 if (status < 0) { 411 // Hmm, not much we can do - abort 412 SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA write failed (unrecoverable): %s", ALSA_snd_strerror(rc)); 413 return false; 414 } 415 continue; 416 } 417 418 sample_buf += rc * frame_size; 419 frames_left -= rc; 420 } 421 422 return true; 423} 424 425static Uint8 *ALSA_GetDeviceBuf(SDL_AudioDevice *device, int *buffer_size) 426{ 427 snd_pcm_sframes_t rc = ALSA_snd_pcm_avail(device->hidden->pcm); 428 if (rc <= 0) { 429 // Wait a bit and try again, maybe the hardware isn't quite ready yet? 430 SDL_Delay(1); 431 432 rc = ALSA_snd_pcm_avail(device->hidden->pcm); 433 if (rc <= 0) { 434 // We'll catch it next time 435 *buffer_size = 0; 436 return NULL; 437 } 438 } 439 440 const int requested_frames = SDL_min(device->sample_frames, rc); 441 const int requested_bytes = requested_frames * SDL_AUDIO_FRAMESIZE(device->spec); 442 SDL_assert(requested_bytes <= *buffer_size); 443 //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA GETDEVICEBUF: NEED %d BYTES", requested_bytes); 444 *buffer_size = requested_bytes; 445 return device->hidden->mixbuf; 446} 447 448static int ALSA_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) 449{ 450 const int frame_size = SDL_AUDIO_FRAMESIZE(device->spec); 451 SDL_assert((buflen % frame_size) == 0); 452 453 const snd_pcm_sframes_t total_available = ALSA_snd_pcm_avail(device->hidden->pcm); 454 if (total_available == 0) { 455 return 0; // go back to WaitDevice and try again. 456 } 457 458 const int total_frames = SDL_min(buflen / frame_size, total_available); 459 const int rc = ALSA_snd_pcm_readi(device->hidden->pcm, buffer, total_frames); 460 461 SDL_assert(rc != -EAGAIN); // assuming this can't happen if we used snd_pcm_wait and queried for available space. snd_pcm_recover won't handle it! 462 463 if (rc < 0) { 464 const int status = RecoverALSADevice(device->hidden->pcm, rc); 465 if (status < 0) { 466 // Hmm, not much we can do - abort 467 SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "ALSA read failed (unrecoverable): %s", ALSA_snd_strerror(rc)); 468 return -1; 469 } 470 return 0; // go back to WaitDevice and try again. 471 } 472 473 //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: recorded %d bytes", rc * frame_size); 474 475 return rc * frame_size; 476} 477 478static void ALSA_FlushRecording(SDL_AudioDevice *device) 479{ 480 ALSA_snd_pcm_reset(device->hidden->pcm); 481} 482 483static void ALSA_CloseDevice(SDL_AudioDevice *device) 484{ 485 if (device->hidden) { 486 if (device->hidden->pcm) { 487 ALSA_snd_pcm_close(device->hidden->pcm); 488 } 489 SDL_free(device->hidden->mixbuf); 490 SDL_free(device->hidden); 491 } 492} 493 494 495// To make easier to track parameters during the whole alsa pcm configuration: 496struct ALSA_pcm_cfg_ctx { 497 SDL_AudioDevice *device; 498 499 snd_pcm_hw_params_t *hwparams; 500 snd_pcm_sw_params_t *swparams; 501 502 SDL_AudioFormat matched_sdl_format; 503 unsigned int chans_n; 504 unsigned int target_chans_n; 505 unsigned int rate; 506 snd_pcm_uframes_t persize; // alsa period size, SDL audio device sample_frames 507 snd_pcm_chmap_query_t **chmap_queries; 508 unsigned int sdl_chmap[SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX]; 509 unsigned int alsa_chmap_installed[SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX]; 510 511 unsigned int periods; 512}; 513// The following are SDL channel maps with alsa position values, from 0 channels to 8 channels. 514// See SDL3/SDL_audio.h 515// Strictly speaking those are "parameters" of channel maps, like alsa hwparams and swparams, they 516// have to be "reduced/refined" until an exact channel map. Only the 6 channels map requires such 517// "reduction/refine". 518static enum snd_pcm_chmap_position sdl_channel_maps[SDL_AUDIO_ALSA__SDL_CHMAPS_N][SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX] = { 519 // 0 channels 520 { 521 0 522 }, 523 // 1 channel 524 { 525 SND_CHMAP_MONO, 526 }, 527 // 2 channels 528 { 529 SND_CHMAP_FL, 530 SND_CHMAP_FR, 531 }, 532 // 3 channels 533 { 534 SND_CHMAP_FL, 535 SND_CHMAP_FR, 536 SND_CHMAP_LFE, 537 }, 538 // 4 channels 539 { 540 SND_CHMAP_FL, 541 SND_CHMAP_FR, 542 SND_CHMAP_RL, 543 SND_CHMAP_RR, 544 }, 545 // 5 channels 546 { 547 SND_CHMAP_FL, 548 SND_CHMAP_FR, 549 SND_CHMAP_LFE, 550 SND_CHMAP_RL, 551 SND_CHMAP_RR, 552 }, 553 // 6 channels 554 // XXX: here we encode not a uniq channel map but a set of channel maps. We will reduce it each 555 // time we are going to work with an alsa 6 channels map. 556 { 557 SND_CHMAP_FL, 558 SND_CHMAP_FR, 559 SND_CHMAP_FC, 560 SND_CHMAP_LFE, 561 // The 2 following channel positions are (SND_CHMAP_SL,SND_CHMAP_SR) or 562 // (SND_CHMAP_RL,SND_CHMAP_RR) 563 SND_CHMAP_UNKNOWN, 564 SND_CHMAP_UNKNOWN, 565 }, 566 // 7 channels 567 { 568 SND_CHMAP_FL, 569 SND_CHMAP_FR, 570 SND_CHMAP_FC, 571 SND_CHMAP_LFE, 572 SND_CHMAP_RC, 573 SND_CHMAP_SL, 574 SND_CHMAP_SR, 575 }, 576 // 8 channels 577 { 578 SND_CHMAP_FL, 579 SND_CHMAP_FR, 580 SND_CHMAP_FC, 581 SND_CHMAP_LFE, 582 SND_CHMAP_RL, 583 SND_CHMAP_RR, 584 SND_CHMAP_SL, 585 SND_CHMAP_SR, 586 }, 587}; 588 589// Helper for the function right below. 590static bool has_pos(const unsigned int *chmap, unsigned int pos) 591{ 592 for (unsigned int chan_idx = 0; ; chan_idx++) { 593 if (chan_idx == 6) { 594 return false; 595 } 596 if (chmap[chan_idx] == pos) { 597 return true; 598 } 599 } 600 SDL_assert(!"Shouldn't hit this code."); 601 return false; 602} 603 604// XXX: Each time we are going to work on an alsa 6 channels map, we must reduce the set of channel 605// maps which is encoded in sdl_channel_maps[6] to a uniq one. 606#define HAVE_NONE 0 607#define HAVE_REAR 1 608#define HAVE_SIDE 2 609#define HAVE_BOTH 3 610static void sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(unsigned int *sdl_6chans, const unsigned int *alsa_6chans) 611{ 612 // For alsa channel maps with 6 channels and with SND_CHMAP_FL,SND_CHMAP_FR,SND_CHMAP_FC, 613 // SND_CHMAP_LFE, reduce our 6 channels maps to a uniq one. 614 if ( !has_pos(alsa_6chans, SND_CHMAP_FL) || 615 !has_pos(alsa_6chans, SND_CHMAP_FR) || 616 !has_pos(alsa_6chans, SND_CHMAP_FC) || 617 !has_pos(alsa_6chans, SND_CHMAP_LFE)) { 618 sdl_6chans[4] = SND_CHMAP_UNKNOWN; 619 sdl_6chans[5] = SND_CHMAP_UNKNOWN; 620 LOGDEBUG("6channels:unsupported channel map"); 621 return; 622 } 623 624 unsigned int state = HAVE_NONE; 625 for (unsigned int chan_idx = 0; chan_idx < 6; chan_idx++) { 626 if ((alsa_6chans[chan_idx] == SND_CHMAP_SL) || (alsa_6chans[chan_idx] == SND_CHMAP_SR)) { 627 if (state == HAVE_NONE) { 628 state = HAVE_SIDE; 629 } else if (state == HAVE_REAR) { 630 state = HAVE_BOTH; 631 break; 632 } 633 } else if ((alsa_6chans[chan_idx] == SND_CHMAP_RL) || (alsa_6chans[chan_idx] == SND_CHMAP_RR)) { 634 if (state == HAVE_NONE) { 635 state = HAVE_REAR; 636 } else if (state == HAVE_SIDE) { 637 state = HAVE_BOTH; 638 break; 639 } 640 } 641 } 642 643 if ((state == HAVE_BOTH) || (state == HAVE_NONE)) { 644 sdl_6chans[4] = SND_CHMAP_UNKNOWN; 645 sdl_6chans[5] = SND_CHMAP_UNKNOWN; 646 LOGDEBUG("6channels:unsupported channel map"); 647 } else if (state == HAVE_REAR) { 648 sdl_6chans[4] = SND_CHMAP_RL; 649 sdl_6chans[5] = SND_CHMAP_RR; 650 LOGDEBUG("6channels:sdl map set to rear"); 651 } else { // state == HAVE_SIDE 652 sdl_6chans[4] = SND_CHMAP_SL; 653 sdl_6chans[5] = SND_CHMAP_SR; 654 LOGDEBUG("6channels:sdl map set to side"); 655 } 656} 657#undef HAVE_NONE 658#undef HAVE_REAR 659#undef HAVE_SIDE 660#undef HAVE_BOTH 661 662static void swizzle_map_compute_alsa_subscan(const struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, unsigned int sdl_pos_idx) 663{ 664 swizzle_map[sdl_pos_idx] = -1; 665 for (unsigned int alsa_pos_idx = 0; ; alsa_pos_idx++) { 666 SDL_assert(alsa_pos_idx != ctx->chans_n); // no 0 channels or not found matching position should happen here (actually enforce playback/recording symmetry). 667 if (ctx->alsa_chmap_installed[alsa_pos_idx] == ctx->sdl_chmap[sdl_pos_idx]) { 668 LOGDEBUG("swizzle SDL %u <-> alsa %u", sdl_pos_idx,alsa_pos_idx); 669 swizzle_map[sdl_pos_idx] = (int) alsa_pos_idx; 670 return; 671 } 672 } 673} 674 675// XXX: this must stay playback/recording symmetric. 676static void swizzle_map_compute(const struct ALSA_pcm_cfg_ctx *ctx, int *swizzle_map, bool *needs_swizzle) 677{ 678 *needs_swizzle = false; 679 for (unsigned int sdl_pos_idx = 0; sdl_pos_idx != ctx->chans_n; sdl_pos_idx++) { 680 swizzle_map_compute_alsa_subscan(ctx, swizzle_map, sdl_pos_idx); 681 if (swizzle_map[sdl_pos_idx] != sdl_pos_idx) { 682 *needs_swizzle = true; 683 } 684 } 685} 686 687#define CHMAP_INSTALLED 0 688#define CHANS_N_NEXT 1 689#define CHMAP_NOT_FOUND 2 690// Should always be a queried alsa channel map unless the queried alsa channel map was of type VAR, 691// namely we can program the channel positions directly from the SDL channel map. 692static int alsa_chmap_install(struct ALSA_pcm_cfg_ctx *ctx, const unsigned int *chmap) 693{ 694 bool isstack; 695 snd_pcm_chmap_t *chmap_to_install = (snd_pcm_chmap_t *)SDL_small_alloc(unsigned int, 1 + ctx->chans_n, &isstack); 696 if (!chmap_to_install) { 697 return -1; 698 } 699 700 chmap_to_install->channels = ctx->chans_n; 701 SDL_memcpy(chmap_to_install->pos, chmap, sizeof (unsigned int) * ctx->chans_n); 702 703 #if SDL_ALSA_DEBUG 704 char logdebug_chmap_str[128]; 705 ALSA_snd_pcm_chmap_print(chmap_to_install,sizeof(logdebug_chmap_str),logdebug_chmap_str); 706 LOGDEBUG("channel map to install:%s",logdebug_chmap_str); 707 #endif 708 709 int status = ALSA_snd_pcm_set_chmap(ctx->device->hidden->pcm, chmap_to_install); 710 if (status < 0) { 711 SDL_SetError("ALSA: failed to install channel map: %s", ALSA_snd_strerror(status)); 712 return -1; 713 } 714 SDL_memcpy(ctx->alsa_chmap_installed, chmap, ctx->chans_n * sizeof (unsigned int)); 715 716 SDL_small_free(chmap_to_install, isstack); 717 return CHMAP_INSTALLED; 718} 719 720// We restrict the alsa channel maps because in the unordered matches we do only simple accounting. 721// In the end, this will handle mostly alsa channel maps with more than one SND_CHMAP_NA position fillers. 722static bool alsa_chmap_has_duplicate_position(const struct ALSA_pcm_cfg_ctx *ctx, const unsigned int *pos) 723{ 724 if (ctx->chans_n < 2) {// we need at least 2 positions 725 LOGDEBUG("channel map:no duplicate"); 726 return false; 727 } 728 729 for (unsigned int chan_idx = 1; chan_idx != ctx->chans_n; chan_idx++) { 730 for (unsigned int seen_idx = 0; seen_idx != chan_idx; seen_idx++) { 731 if (pos[seen_idx] == pos[chan_idx]) { 732 LOGDEBUG("channel map:have duplicate"); 733 return true; 734 } 735 } 736 } 737 738 LOGDEBUG("channel map:no duplicate"); 739 return false; 740} 741 742static int alsa_chmap_cfg_ordered_fixed_or_paired(struct ALSA_pcm_cfg_ctx *ctx) 743{ 744 for (snd_pcm_chmap_query_t **chmap_query = ctx->chmap_queries; *chmap_query; chmap_query++) { 745 if ( ((*chmap_query)->map.channels != ctx->chans_n) || 746 (((*chmap_query)->type != SND_CHMAP_TYPE_FIXED) && ((*chmap_query)->type != SND_CHMAP_TYPE_PAIRED)) ) { 747 continue; 748 } 749 750 #if SDL_ALSA_DEBUG 751 char logdebug_chmap_str[128]; 752 ALSA_snd_pcm_chmap_print(&(*chmap_query)->map,sizeof(logdebug_chmap_str),logdebug_chmap_str); 753 LOGDEBUG("channel map:ordered:fixed|paired:%s",logdebug_chmap_str); 754 #endif 755 756 for (int i = 0; i < ctx->chans_n; i++) { 757 ctx->sdl_chmap[i] = (unsigned int) sdl_channel_maps[ctx->chans_n][i]; 758 } 759 760 unsigned int *alsa_chmap = (*chmap_query)->map.pos; 761 if (ctx->chans_n == 6) { 762 sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(ctx->sdl_chmap, alsa_chmap); 763 } 764 if (alsa_chmap_has_duplicate_position(ctx, alsa_chmap)) { 765 continue; 766 } 767 768 for (unsigned int chan_idx = 0; ctx->sdl_chmap[chan_idx] == alsa_chmap[chan_idx]; chan_idx++) { 769 if (chan_idx == ctx->chans_n) { 770 return alsa_chmap_install(ctx, alsa_chmap); 771 } 772 } 773 } 774 return CHMAP_NOT_FOUND; 775} 776 777// Here, the alsa channel positions can be programmed in the alsa frame (cf HDMI). 778// If the alsa channel map is VAR, we only check we have the unordered set of channel positions we 779// are looking for. 780static int alsa_chmap_cfg_ordered_var(struct ALSA_pcm_cfg_ctx *ctx) 781{ 782 for (snd_pcm_chmap_query_t **chmap_query = ctx->chmap_queries; *chmap_query; chmap_query++) { 783 if (((*chmap_query)->map.channels != ctx->chans_n) || ((*chmap_query)->type != SND_CHMAP_TYPE_VAR)) { 784 continue; 785 } 786 787 #if SDL_ALSA_DEBUG 788 char logdebug_chmap_str[128]; 789 ALSA_snd_pcm_chmap_print(&(*chmap_query)->map,sizeof(logdebug_chmap_str),logdebug_chmap_str); 790 LOGDEBUG("channel map:ordered:var:%s",logdebug_chmap_str); 791 #endif 792 793 for (int i = 0; i < ctx->chans_n; i++) { 794 ctx->sdl_chmap[i] = (unsigned int) sdl_channel_maps[ctx->chans_n][i]; 795 } 796 797 unsigned int *alsa_chmap = (*chmap_query)->map.pos; 798 if (ctx->chans_n == 6) { 799 sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(ctx->sdl_chmap, alsa_chmap); 800 } 801 if (alsa_chmap_has_duplicate_position(ctx, alsa_chmap)) { 802 continue; 803 } 804 805 unsigned int pos_matches_n = 0; 806 for (unsigned int chan_idx = 0; chan_idx != ctx->chans_n; chan_idx++) { 807 for (unsigned int subscan_chan_idx = 0; subscan_chan_idx != ctx->chans_n; subscan_chan_idx++) { 808 if (ctx->sdl_chmap[chan_idx] == alsa_chmap[subscan_chan_idx]) { 809 pos_matches_n++; 810 break; 811 } 812 } 813 } 814 815 if (pos_matches_n == ctx->chans_n) { 816 return alsa_chmap_install(ctx, ctx->sdl_chmap); // XXX: we program the SDL chmap here 817 } 818 } 819 820 return CHMAP_NOT_FOUND; 821} 822 823static int alsa_chmap_cfg_ordered(struct ALSA_pcm_cfg_ctx *ctx) 824{ 825 const int status = alsa_chmap_cfg_ordered_fixed_or_paired(ctx); 826 return (status != CHMAP_NOT_FOUND) ? status : alsa_chmap_cfg_ordered_var(ctx); 827} 828 829// In the unordered case, we are just interested to get the same unordered set of alsa channel 830// positions than in the SDL channel map since we will swizzle (no duplicate channel position). 831static int alsa_chmap_cfg_unordered(struct ALSA_pcm_cfg_ctx *ctx) 832{ 833 for (snd_pcm_chmap_query_t **chmap_query = ctx->chmap_queries; *chmap_query; chmap_query++) { 834 if ( ((*chmap_query)->map.channels != ctx->chans_n) || 835 (((*chmap_query)->type != SND_CHMAP_TYPE_FIXED) && ((*chmap_query)->type != SND_CHMAP_TYPE_PAIRED)) ) { 836 continue; 837 } 838 839 #if SDL_ALSA_DEBUG 840 char logdebug_chmap_str[128]; 841 ALSA_snd_pcm_chmap_print(&(*chmap_query)->map,sizeof(logdebug_chmap_str),logdebug_chmap_str); 842 LOGDEBUG("channel map:unordered:fixed|paired:%s",logdebug_chmap_str); 843 #endif 844 845 for (int i = 0; i < ctx->chans_n; i++) { 846 ctx->sdl_chmap[i] = (unsigned int) sdl_channel_maps[ctx->chans_n][i]; 847 } 848 849 unsigned int *alsa_chmap = (*chmap_query)->map.pos; 850 if (ctx->chans_n == 6) { 851 sdl_6chans_set_rear_or_side_channels_from_alsa_6chans(ctx->sdl_chmap, alsa_chmap); 852 } 853 854 if (alsa_chmap_has_duplicate_position(ctx, alsa_chmap)) { 855 continue; 856 } 857 858 unsigned int pos_matches_n = 0; 859 for (unsigned int chan_idx = 0; chan_idx != ctx->chans_n; chan_idx++) { 860 for (unsigned int subscan_chan_idx = 0; subscan_chan_idx != ctx->chans_n; subscan_chan_idx++) { 861 if (ctx->sdl_chmap[chan_idx] == alsa_chmap[subscan_chan_idx]) { 862 pos_matches_n++; 863 break; 864 } 865 } 866 } 867 868 if (pos_matches_n == ctx->chans_n) { 869 return alsa_chmap_install(ctx, alsa_chmap); 870 } 871 } 872 873 return CHMAP_NOT_FOUND; 874} 875 876static int alsa_chmap_cfg(struct ALSA_pcm_cfg_ctx *ctx) 877{ 878 int status; 879 880 ctx->chmap_queries = ALSA_snd_pcm_query_chmaps(ctx->device->hidden->pcm); 881 if (ctx->chmap_queries == NULL) { 882 // We couldn't query the channel map, assume no swizzle necessary 883 LOGDEBUG("couldn't query channel map, swizzling off"); 884 return CHMAP_INSTALLED; 885 } 886 887 //---------------------------------------------------------------------------------------------- 888 status = alsa_chmap_cfg_ordered(ctx); // we prefer first channel maps we don't need to swizzle 889 if (status == CHMAP_INSTALLED) { 890 LOGDEBUG("swizzling off"); 891 return status; 892 } else if (status != CHMAP_NOT_FOUND) { 893 return status; // < 0 error code 894 } 895 896 // Fall-thru 897 //---------------------------------------------------------------------------------------------- 898 status = alsa_chmap_cfg_unordered(ctx); // those we will have to swizzle 899 if (status == CHMAP_INSTALLED) { 900 LOGDEBUG("swizzling on"); 901 902 bool isstack; 903 int *swizzle_map = SDL_small_alloc(int, ctx->chans_n, &isstack); 904 if (!swizzle_map) { 905 status = -1; 906 } else { 907 bool needs_swizzle; 908 swizzle_map_compute(ctx, swizzle_map, &needs_swizzle); // fine grained swizzle configuration 909 if (needs_swizzle) { 910 // let SDL's swizzler handle this one. 911 ctx->device->chmap = SDL_ChannelMapDup(swizzle_map, ctx->chans_n); 912 if (!ctx->device->chmap) { 913 status = -1; 914 } 915 } 916 SDL_small_free(swizzle_map, isstack); 917 } 918 } 919 920 if (status == CHMAP_NOT_FOUND) { 921 return CHANS_N_NEXT; 922 } 923 924 return status; // < 0 error code 925} 926 927#define CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N 0 // target more hardware pressure 928#define CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N 1 // target less hardware pressure 929#define CHANS_N_CONFIGURED 0 930#define CHANS_N_NOT_CONFIGURED 1 931static int ALSA_pcm_cfg_hw_chans_n_scan(struct ALSA_pcm_cfg_ctx *ctx, unsigned int mode) 932{ 933 unsigned int target_chans_n = ctx->device->spec.channels; // we start at what was specified 934 if (mode == CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N) { 935 target_chans_n--; 936 } 937 while (true) { 938 if (mode == CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N) { 939 if (target_chans_n > SDL_AUDIO_ALSA__CHMAP_CHANS_N_MAX) { 940 return CHANS_N_NOT_CONFIGURED; 941 } 942 // else: CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N 943 } else if (target_chans_n == 0) { 944 return CHANS_N_NOT_CONFIGURED; 945 } 946 947 LOGDEBUG("target chans_n is %u", target_chans_n); 948 949 int status = ALSA_snd_pcm_hw_params_any(ctx->device->hidden->pcm, ctx->hwparams); 950 if (status < 0) { 951 SDL_SetError("ALSA: Couldn't get hardware config: %s", ALSA_snd_strerror(status)); 952 return -1; 953 } 954 // SDL only uses interleaved sample output 955 status = ALSA_snd_pcm_hw_params_set_access(ctx->device->hidden->pcm, ctx->hwparams, SND_PCM_ACCESS_RW_INTERLEAVED); 956 if (status < 0) { 957 SDL_SetError("ALSA: Couldn't set interleaved access: %s", ALSA_snd_strerror(status)); 958 return -1; 959 } 960 // Try for a closest match on audio format 961 snd_pcm_format_t alsa_format = 0; 962 const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(ctx->device->spec.format); 963 ctx->matched_sdl_format = 0; 964 while ((ctx->matched_sdl_format = *(closefmts++)) != 0) { 965 // XXX: we are forcing the same endianness, namely we won't need byte swapping upon 966 // writing/reading to/from the SDL audio buffer. 967 switch (ctx->matched_sdl_format) { 968 case SDL_AUDIO_U8: 969 alsa_format = SND_PCM_FORMAT_U8; 970 break; 971 case SDL_AUDIO_S8: 972 alsa_format = SND_PCM_FORMAT_S8; 973 break; 974 case SDL_AUDIO_S16LE: 975 alsa_format = SND_PCM_FORMAT_S16_LE; 976 break; 977 case SDL_AUDIO_S16BE: 978 alsa_format = SND_PCM_FORMAT_S16_BE; 979 break; 980 case SDL_AUDIO_S32LE: 981 alsa_format = SND_PCM_FORMAT_S32_LE; 982 break; 983 case SDL_AUDIO_S32BE: 984 alsa_format = SND_PCM_FORMAT_S32_BE; 985 break; 986 case SDL_AUDIO_F32LE: 987 alsa_format = SND_PCM_FORMAT_FLOAT_LE; 988 break; 989 case SDL_AUDIO_F32BE: 990 alsa_format = SND_PCM_FORMAT_FLOAT_BE; 991 break; 992 default: 993 continue; 994 } 995 if (ALSA_snd_pcm_hw_params_set_format(ctx->device->hidden->pcm, ctx->hwparams, alsa_format) >= 0) { 996 break; 997 } 998 } 999 if (ctx->matched_sdl_format == 0) { 1000 SDL_SetError("ALSA: Unsupported audio format: %s", ALSA_snd_strerror(status)); 1001 return -1; 1002 } 1003 // let alsa approximate the number of channels 1004 ctx->chans_n = target_chans_n; 1005 status = ALSA_snd_pcm_hw_params_set_channels_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->chans_n)); 1006 if (status < 0) { 1007 SDL_SetError("ALSA: Couldn't set audio channels: %s", ALSA_snd_strerror(status)); 1008 return -1; 1009 } 1010 // let alsa approximate the audio rate 1011 ctx->rate = ctx->device->spec.freq; 1012 status = ALSA_snd_pcm_hw_params_set_rate_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->rate), NULL); 1013 if (status < 0) { 1014 SDL_SetError("ALSA: Couldn't set audio frequency: %s", ALSA_snd_strerror(status)); 1015 return -1; 1016 } 1017 // let approximate the period size to the requested buffer size 1018 ctx->persize = ctx->device->sample_frames; 1019 status = ALSA_snd_pcm_hw_params_set_period_size_near(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->persize), NULL); 1020 if (status < 0) { 1021 SDL_SetError("ALSA: Couldn't set the period size: %s", ALSA_snd_strerror(status)); 1022 return -1; 1023 } 1024 // let approximate the minimun number of periods per buffer (we target a double buffer) 1025 ctx->periods = 2; 1026 status = ALSA_snd_pcm_hw_params_set_periods_min(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->periods), NULL); 1027 if (status < 0) { 1028 SDL_SetError("ALSA: Couldn't set the minimum number of periods per buffer: %s", ALSA_snd_strerror(status)); 1029 return -1; 1030 } 1031 // restrict the number of periods per buffer to an approximation of the approximated minimum 1032 // number of periods per buffer done right above 1033 status = ALSA_snd_pcm_hw_params_set_periods_first(ctx->device->hidden->pcm, ctx->hwparams, &(ctx->periods), NULL); 1034 if (status < 0) { 1035 SDL_SetError("ALSA: Couldn't set the number of periods per buffer: %s", ALSA_snd_strerror(status)); 1036 return -1; 1037 } 1038 // install the hw parameters 1039 status = ALSA_snd_pcm_hw_params(ctx->device->hidden->pcm, ctx->hwparams); 1040 if (status < 0) { 1041 SDL_SetError("ALSA: installation of hardware parameter failed: %s", ALSA_snd_strerror(status)); 1042 return -1; 1043 } 1044 //========================================================================================== 1045 // Here the alsa pcm is in SND_PCM_STATE_PREPARED state, let's figure out a good fit for 1046 // SDL channel map, it may request to change the target number of channels though. 1047 status = alsa_chmap_cfg(ctx); 1048 if (status < 0) { 1049 return status; // we forward the SDL error 1050 } else if (status == CHMAP_INSTALLED) { 1051 return CHANS_N_CONFIGURED; // we are finished here 1052 } 1053 1054 // status == CHANS_N_NEXT 1055 ALSA_snd_pcm_free_chmaps(ctx->chmap_queries); 1056 ALSA_snd_pcm_hw_free(ctx->device->hidden->pcm); // uninstall those hw params 1057 1058 if (mode == CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N) { 1059 target_chans_n++; 1060 } else { // CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N 1061 target_chans_n--; 1062 } 1063 } 1064 1065 SDL_assert(!"Shouldn't reach this code."); 1066 return CHANS_N_NOT_CONFIGURED; 1067} 1068#undef CHMAP_INSTALLED 1069#undef CHANS_N_NEXT 1070#undef CHMAP_NOT_FOUND 1071 1072static bool ALSA_pcm_cfg_hw(struct ALSA_pcm_cfg_ctx *ctx) 1073{ 1074 LOGDEBUG("target chans_n, equal or above requested chans_n mode"); 1075 int status = ALSA_pcm_cfg_hw_chans_n_scan(ctx, CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N); 1076 if (status < 0) { // something went too wrong 1077 return false; 1078 } else if (status == CHANS_N_CONFIGURED) { 1079 return true; 1080 } 1081 1082 // Here, status == CHANS_N_NOT_CONFIGURED 1083 LOGDEBUG("target chans_n, below requested chans_n mode"); 1084 status = ALSA_pcm_cfg_hw_chans_n_scan(ctx, CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N); 1085 if (status < 0) { // something went too wrong 1086 return false; 1087 } else if (status == CHANS_N_CONFIGURED) { 1088 return true; 1089 } 1090 1091 // Here, status == CHANS_N_NOT_CONFIGURED 1092 return SDL_SetError("ALSA: Couldn't configure targeting any SDL supported channel number"); 1093} 1094#undef CHANS_N_SCAN_MODE__EQUAL_OR_ABOVE_REQUESTED_CHANS_N 1095#undef CHANS_N_SCAN_MODE__BELOW_REQUESTED_CHANS_N 1096#undef CHANS_N_CONFIGURED 1097#undef CHANS_N_NOT_CONFIGURED 1098 1099 1100static bool ALSA_pcm_cfg_sw(struct ALSA_pcm_cfg_ctx *ctx) 1101{ 1102 int status; 1103 1104 status = ALSA_snd_pcm_sw_params_current(ctx->device->hidden->pcm, ctx->swparams); 1105 if (status < 0) { 1106 return SDL_SetError("ALSA: Couldn't get software config: %s", ALSA_snd_strerror(status)); 1107 } 1108 1109 status = ALSA_snd_pcm_sw_params_set_avail_min(ctx->device->hidden->pcm, ctx->swparams, ctx->persize); // will become device->sample_frames if the alsa pcm configuration is successful 1110 if (status < 0) { 1111 return SDL_SetError("Couldn't set minimum available samples: %s", ALSA_snd_strerror(status)); 1112 } 1113 1114 status = ALSA_snd_pcm_sw_params_set_start_threshold(ctx->device->hidden->pcm, ctx->swparams, 1); 1115 if (status < 0) { 1116 return SDL_SetError("ALSA: Couldn't set start threshold: %s", ALSA_snd_strerror(status)); 1117 } 1118 status = ALSA_snd_pcm_sw_params(ctx->device->hidden->pcm, ctx->swparams); 1119 if (status < 0) { 1120 return SDL_SetError("Couldn't set software audio parameters: %s", ALSA_snd_strerror(status)); 1121 } 1122 return true; 1123} 1124 1125 1126static bool ALSA_OpenDevice(SDL_AudioDevice *device) 1127{ 1128 const bool recording = device->recording; 1129 struct ALSA_pcm_cfg_ctx cfg_ctx; // used to track everything here 1130 char *pcm_str; 1131 int status = 0; 1132 1133 //device->spec.channels = 8; 1134 //SDL_SetLogPriority(SDL_LOG_CATEGORY_AUDIO, SDL_LOG_PRIORITY_VERBOSE); 1135 LOGDEBUG("channels requested %u",device->spec.channels); 1136 // XXX: We do not use the SDL internal swizzler yet. 1137 device->chmap = NULL; 1138 1139 SDL_zero(cfg_ctx); 1140 cfg_ctx.device = device; 1141 1142 // Initialize all variables that we clean on shutdown 1143 cfg_ctx.device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*cfg_ctx.device->hidden)); 1144 if (!cfg_ctx.device->hidden) { 1145 return false; 1146 } 1147 1148 // Open the audio device 1149 pcm_str = get_pcm_str(cfg_ctx.device->handle); 1150 if (pcm_str == NULL) { 1151 goto err_free_device_hidden; 1152 } 1153 LOGDEBUG("PCM open '%s'", pcm_str); 1154 status = ALSA_snd_pcm_open(&cfg_ctx.device->hidden->pcm, 1155 pcm_str, 1156 recording ? SND_PCM_STREAM_CAPTURE : SND_PCM_STREAM_PLAYBACK, 1157 SND_PCM_NONBLOCK); 1158 SDL_free(pcm_str); 1159 if (status < 0) { 1160 SDL_SetError("ALSA: Couldn't open audio device: %s", ALSA_snd_strerror(status)); 1161 goto err_free_device_hidden; 1162 } 1163 1164 // Now we need to configure the opened pcm as close as possible from the requested parameters we 1165 // can reasonably deal with (and that could change) 1166 snd_pcm_hw_params_alloca(&(cfg_ctx.hwparams)); 1167 snd_pcm_sw_params_alloca(&(cfg_ctx.swparams)); 1168 1169 if (!ALSA_pcm_cfg_hw(&cfg_ctx)) { // alsa pcm "hardware" part of the pcm 1170 goto err_close_pcm; 1171 } 1172 1173 // from here, we get only the alsa chmap queries in cfg_ctx to explicitly clean, hwparams is 1174 // uninstalled upon pcm closing 1175 1176 // This is useful for debugging 1177 #if SDL_ALSA_DEBUG 1178 snd_pcm_uframes_t bufsize; 1179 ALSA_snd_pcm_hw_params_get_buffer_size(cfg_ctx.hwparams, &bufsize); 1180 SDL_LogDebug(SDL_LOG_CATEGORY_AUDIO, 1181 "ALSA: period size = %ld, periods = %u, buffer size = %lu", 1182 cfg_ctx.persize, cfg_ctx.periods, bufsize); 1183 #endif 1184 1185 if (!ALSA_pcm_cfg_sw(&cfg_ctx)) { // alsa pcm "software" part of the pcm 1186 goto err_cleanup_ctx; 1187 } 1188 1189 // Now we can update the following parameters in the spec: 1190 cfg_ctx.device->spec.format = cfg_ctx.matched_sdl_format; 1191 cfg_ctx.device->spec.channels = cfg_ctx.chans_n; 1192 cfg_ctx.device->spec.freq = cfg_ctx.rate; 1193 cfg_ctx.device->sample_frames = cfg_ctx.persize; 1194 // Calculate the final parameters for this audio specification 1195 SDL_UpdatedAudioDeviceFormat(cfg_ctx.device); 1196 1197 // Allocate mixing buffer 1198 if (!recording) { 1199 cfg_ctx.device->hidden->mixbuf = (Uint8 *)SDL_malloc(cfg_ctx.device->buffer_size); 1200 if (cfg_ctx.device->hidden->mixbuf == NULL) { 1201 goto err_cleanup_ctx; 1202 } 1203 SDL_memset(cfg_ctx.device->hidden->mixbuf, cfg_ctx.device->silence_value, cfg_ctx.device->buffer_size); 1204 } 1205 1206#if !SDL_ALSA_NON_BLOCKING 1207 if (!recording) { 1208 ALSA_snd_pcm_nonblock(cfg_ctx.device->hidden->pcm, 0); 1209 } 1210#endif 1211 return true; // We're ready to rock and roll. :-) 1212 1213err_cleanup_ctx: 1214 ALSA_snd_pcm_free_chmaps(cfg_ctx.chmap_queries); 1215err_close_pcm: 1216 ALSA_snd_pcm_close(cfg_ctx.device->hidden->pcm); 1217err_free_device_hidden: 1218 SDL_free(cfg_ctx.device->hidden); 1219 cfg_ctx.device->hidden = NULL; 1220 return false; 1221} 1222 1223static void ALSA_ThreadInit(SDL_AudioDevice *device) 1224{ 1225 SDL_SetCurrentThreadPriority(device->recording ? SDL_THREAD_PRIORITY_HIGH : SDL_THREAD_PRIORITY_TIME_CRITICAL); 1226 // do snd_pcm_start as close to the first time we PlayDevice as possible to prevent an underrun at startup. 1227 ALSA_snd_pcm_start(device->hidden->pcm); 1228} 1229 1230static ALSA_Device *hotplug_devices = NULL; 1231 1232static int hotplug_device_process(snd_ctl_t *ctl, snd_ctl_card_info_t *ctl_card_info, int dev_idx, 1233 snd_pcm_stream_t direction, ALSA_Device **unseen, ALSA_Device **seen) 1234{ 1235 unsigned int subdevs_n = 1; // we have at least one subdevice (substream since the direction is a stream in alsa terminology) 1236 unsigned int subdev_idx = 0; 1237 const bool recording = direction == SND_PCM_STREAM_CAPTURE ? true : false; // used for the unicity of the device 1238 bool isstack; 1239 snd_pcm_info_t *pcm_info = (snd_pcm_info_t *)SDL_small_alloc(Uint8, ALSA_snd_pcm_info_sizeof(), &isstack); 1240 SDL_memset(pcm_info, 0, ALSA_snd_pcm_info_sizeof()); 1241 1242 while (true) { 1243 ALSA_snd_pcm_info_set_stream(pcm_info, direction); 1244 ALSA_snd_pcm_info_set_device(pcm_info, dev_idx); 1245 ALSA_snd_pcm_info_set_subdevice(pcm_info, subdev_idx); // we have at least one subdevice (substream) of index 0 1246 1247 const int r = ALSA_snd_ctl_pcm_info(ctl, pcm_info); 1248 if (r < 0) { 1249 SDL_small_free(pcm_info, isstack); 1250 // first call to ALSA_snd_ctl_pcm_info 1251 if (subdev_idx == 0 && r == -ENOENT) { // no such direction/stream for this device 1252 return 0; 1253 } 1254 return -1; 1255 } 1256 1257 if (subdev_idx == 0) { 1258 subdevs_n = ALSA_snd_pcm_info_get_subdevices_count(pcm_info); 1259 } 1260 1261 // building the unseen list scanning the list of hotplug devices, if it is already there 1262 // using the id, move it to the seen list. 1263 ALSA_Device *unseen_prev_adev = NULL; 1264 ALSA_Device *adev; 1265 for (adev = *unseen; adev; adev = adev->next) { 1266 // the unicity key is the couple (id,recording) 1267 if ((SDL_strcmp(adev->id, ALSA_snd_ctl_card_info_get_id(ctl_card_info)) == 0) && (adev->recording == recording)) { 1268 // unchain from unseen 1269 if (*unseen == adev) { // head 1270 *unseen = adev->next; 1271 } else { 1272 unseen_prev_adev->next = adev->next; 1273 } 1274 // chain to seen 1275 adev->next = *seen; 1276 *seen = adev; 1277 break; 1278 } 1279 unseen_prev_adev = adev; 1280 } 1281 1282 if (adev == NULL) { // newly seen device 1283 adev = SDL_calloc(1, sizeof(*adev)); 1284 if (adev == NULL) { 1285 SDL_small_free(pcm_info, isstack); 1286 return -1; 1287 } 1288 1289 adev->id = SDL_strdup(ALSA_snd_ctl_card_info_get_id(ctl_card_info)); 1290 if (adev->id == NULL) { 1291 SDL_small_free(pcm_info, isstack); 1292 SDL_free(adev); 1293 return -1; 1294 } 1295 1296 if (SDL_asprintf(&adev->name, "%s:%s", ALSA_snd_ctl_card_info_get_name(ctl_card_info), ALSA_snd_pcm_info_get_name(pcm_info)) == -1) { 1297 SDL_small_free(pcm_info, isstack); 1298 SDL_free(adev->id); 1299 SDL_free(adev); 1300 return -1; 1301 } 1302 1303 if (direction == SND_PCM_STREAM_CAPTURE) { 1304 adev->recording = true; 1305 } else { 1306 adev->recording = false; 1307 } 1308 1309 if (SDL_AddAudioDevice(recording, adev->name, NULL, adev) == NULL) { 1310 SDL_small_free(pcm_info, isstack); 1311 SDL_free(adev->id); 1312 SDL_free(adev->name); 1313 SDL_free(adev); 1314 return -1; 1315 } 1316 1317 adev->next = *seen; 1318 *seen = adev; 1319 } 1320 1321 subdev_idx++; 1322 if (subdev_idx == subdevs_n) { 1323 SDL_small_free(pcm_info, isstack); 1324 return 0; 1325 } 1326 1327 SDL_memset(pcm_info, 0, ALSA_snd_pcm_info_sizeof()); 1328 } 1329 1330 SDL_small_free(pcm_info, isstack); 1331 SDL_assert(!"Shouldn't reach this code"); 1332 return -1; 1333} 1334 1335static void ALSA_HotplugIteration(bool *has_default_output, bool *has_default_recording) 1336{ 1337 if (has_default_output != NULL) { 1338 *has_default_output = true; 1339 } 1340 1341 if (has_default_recording != NULL) { 1342 *has_default_recording = true; 1343 } 1344 1345 bool isstack; 1346 snd_ctl_card_info_t *ctl_card_info = (snd_ctl_card_info_t *) SDL_small_alloc(Uint8, ALSA_snd_ctl_card_info_sizeof(), &isstack); 1347 if (!ctl_card_info) { 1348 return; // oh well. 1349 } 1350 1351 SDL_memset(ctl_card_info, 0, ALSA_snd_ctl_card_info_sizeof()); 1352 1353 snd_ctl_t *ctl = NULL; 1354 ALSA_Device *unseen = hotplug_devices; 1355 ALSA_Device *seen = NULL; 1356 int card_idx = -1; 1357 while (true) { 1358 int r = ALSA_snd_card_next(&card_idx); 1359 if (r < 0) { 1360 goto failed; 1361 } else if (card_idx == -1) { 1362 break; 1363 } 1364 1365 char ctl_name[64]; 1366 SDL_snprintf(ctl_name, sizeof (ctl_name), "%s%d", ALSA_device_prefix, card_idx); // card_idx >= 0 1367 LOGDEBUG("hotplug ctl_name = '%s'", ctl_name); 1368 1369 r = ALSA_snd_ctl_open(&ctl, ctl_name, 0); 1370 if (r < 0) { 1371 continue; 1372 } 1373 1374 r = ALSA_snd_ctl_card_info(ctl, ctl_card_info); 1375 if (r < 0) { 1376 goto failed; 1377 } 1378 1379 int dev_idx = -1; 1380 while (true) { 1381 r = ALSA_snd_ctl_pcm_next_device(ctl, &dev_idx); 1382 if (r < 0) { 1383 goto failed; 1384 } else if (dev_idx == -1) { 1385 break; 1386 } 1387 1388 r = hotplug_device_process(ctl, ctl_card_info, dev_idx, SND_PCM_STREAM_PLAYBACK, &unseen, &seen); 1389 if (r < 0) { 1390 goto failed; 1391 } 1392 1393 r = hotplug_device_process(ctl, ctl_card_info, dev_idx, SND_PCM_STREAM_CAPTURE, &unseen, &seen); 1394 if (r < 0) { 1395 goto failed; 1396 } 1397 } 1398 ALSA_snd_ctl_close(ctl); 1399 ALSA_snd_ctl_card_info_clear(ctl_card_info); 1400 } 1401 1402 // remove only the unseen devices 1403 while (unseen) { 1404 SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(unseen)); 1405 SDL_free(unseen->name); 1406 SDL_free(unseen->id); 1407 ALSA_Device *next = unseen->next; 1408 SDL_free(unseen); 1409 unseen = next; 1410 } 1411 1412 // update hotplug devices to be the seen devices 1413 hotplug_devices = seen; 1414 SDL_small_free(ctl_card_info, isstack); 1415 return; 1416 1417failed: 1418 if (ctl) { 1419 ALSA_snd_ctl_close(ctl); 1420 } 1421 1422 // remove the unseen 1423 while (unseen) { 1424 SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(unseen)); 1425 SDL_free(unseen->name); 1426 SDL_free(unseen->id); 1427 ALSA_Device *next = unseen->next; 1428 SDL_free(unseen); 1429 unseen = next; 1430 } 1431 1432 // remove the seen 1433 while (seen) { 1434 SDL_AudioDeviceDisconnected(SDL_FindPhysicalAudioDeviceByHandle(seen)); 1435 SDL_free(seen->name); 1436 SDL_free(seen->id); 1437 ALSA_Device *next = seen->next; 1438 SDL_free(seen); 1439 seen = next; 1440 } 1441 1442 hotplug_devices = NULL; 1443 SDL_small_free(ctl_card_info, isstack); 1444} 1445 1446 1447#if SDL_ALSA_HOTPLUG_THREAD 1448static SDL_AtomicInt ALSA_hotplug_shutdown; 1449static SDL_Thread *ALSA_hotplug_thread; 1450 1451static int SDLCALL ALSA_HotplugThread(void *arg) 1452{ 1453 SDL_SetCurrentThreadPriority(SDL_THREAD_PRIORITY_LOW); 1454 1455 while (!SDL_GetAtomicInt(&ALSA_hotplug_shutdown)) { 1456 // Block awhile before checking again, unless we're told to stop. 1457 const Uint64 ticks = SDL_GetTicks() + 5000; 1458 while (!SDL_GetAtomicInt(&ALSA_hotplug_shutdown) && (SDL_GetTicks() < ticks)) { 1459 SDL_Delay(100); 1460 } 1461 1462 ALSA_HotplugIteration(NULL, NULL); // run the check. 1463 } 1464 1465 return 0; 1466} 1467#endif 1468 1469#ifdef SDL_USE_LIBUDEV 1470 1471static bool udev_initialized; 1472 1473static void ALSA_udev_callback(SDL_UDEV_deviceevent udev_type, int udev_class, const char *devpath) 1474{ 1475 if (!devpath) { 1476 return; 1477 } 1478 1479 switch (udev_type) { 1480 case SDL_UDEV_DEVICEADDED: 1481 ALSA_HotplugIteration(NULL, NULL); 1482 break; 1483 1484 case SDL_UDEV_DEVICEREMOVED: 1485 ALSA_HotplugIteration(NULL, NULL); 1486 break; 1487 1488 default: 1489 break; 1490 } 1491} 1492 1493static bool ALSA_start_udev(void) 1494{ 1495 udev_initialized = SDL_UDEV_Init(); 1496 if (udev_initialized) { 1497 // Set up the udev callback 1498 if (!SDL_UDEV_AddCallback(ALSA_udev_callback)) { 1499 SDL_UDEV_Quit(); 1500 udev_initialized = false; 1501 } 1502 } 1503 return udev_initialized; 1504} 1505 1506static void ALSA_stop_udev(void) 1507{ 1508 if (udev_initialized) { 1509 SDL_UDEV_DelCallback(ALSA_udev_callback); 1510 SDL_UDEV_Quit(); 1511 udev_initialized = false; 1512 } 1513} 1514 1515#else 1516 1517static bool ALSA_start_udev(void) 1518{ 1519 return false; 1520} 1521 1522static void ALSA_stop_udev(void) 1523{ 1524} 1525 1526#endif // SDL_USE_LIBUDEV 1527 1528static void ALSA_DetectDevices(SDL_AudioDevice **default_playback, SDL_AudioDevice **default_recording) 1529{ 1530 ALSA_guess_device_prefix(); 1531 1532 // ALSA doesn't have a concept of a changeable default device, afaik, so we expose a generic default 1533 // device here. It's the best we can do at this level. 1534 bool has_default_playback = false, has_default_recording = false; 1535 ALSA_HotplugIteration(&has_default_playback, &has_default_recording); // run once now before a thread continues to check. 1536 if (has_default_playback) { 1537 *default_playback = SDL_AddAudioDevice(/*recording=*/false, "ALSA default playback device", NULL, (void *)&default_playback_handle); 1538 } 1539 if (has_default_recording) { 1540 *default_recording = SDL_AddAudioDevice(/*recording=*/true, "ALSA default recording device", NULL, (void *)&default_recording_handle); 1541 } 1542 1543 if (!ALSA_start_udev()) { 1544#if SDL_ALSA_HOTPLUG_THREAD 1545 SDL_SetAtomicInt(&ALSA_hotplug_shutdown, 0); 1546 ALSA_hotplug_thread = SDL_CreateThread(ALSA_HotplugThread, "SDLHotplugALSA", NULL); 1547 // if the thread doesn't spin, oh well, you just don't get further hotplug events. 1548#endif 1549 } 1550} 1551 1552static void ALSA_DeinitializeStart(void) 1553{ 1554 ALSA_Device *dev; 1555 ALSA_Device *next; 1556 1557#if SDL_ALSA_HOTPLUG_THREAD 1558 if (ALSA_hotplug_thread) { 1559 SDL_SetAtomicInt(&ALSA_hotplug_shutdown, 1); 1560 SDL_WaitThread(ALSA_hotplug_thread, NULL); 1561 ALSA_hotplug_thread = NULL; 1562 } 1563#endif 1564 ALSA_stop_udev(); 1565 1566 // Shutting down! Clean up any data we've gathered. 1567 for (dev = hotplug_devices; dev; dev = next) { 1568 //SDL_LogInfo(SDL_LOG_CATEGORY_AUDIO, "ALSA: at shutdown, removing %s device '%s'", dev->recording ? "recording" : "playback", dev->name); 1569 next = dev->next; 1570 SDL_free(dev->name); 1571 SDL_free(dev); 1572 } 1573 hotplug_devices = NULL; 1574} 1575 1576static void ALSA_Deinitialize(void) 1577{ 1578 UnloadALSALibrary(); 1579} 1580 1581static bool ALSA_Init(SDL_AudioDriverImpl *impl) 1582{ 1583 if (!LoadALSALibrary()) { 1584 return false; 1585 } 1586 1587 impl->DetectDevices = ALSA_DetectDevices; 1588 impl->OpenDevice = ALSA_OpenDevice; 1589 impl->ThreadInit = ALSA_ThreadInit; 1590 impl->WaitDevice = ALSA_WaitDevice; 1591 impl->GetDeviceBuf = ALSA_GetDeviceBuf; 1592 impl->PlayDevice = ALSA_PlayDevice; 1593 impl->CloseDevice = ALSA_CloseDevice; 1594 impl->DeinitializeStart = ALSA_DeinitializeStart; 1595 impl->Deinitialize = ALSA_Deinitialize; 1596 impl->WaitRecordingDevice = ALSA_WaitDevice; 1597 impl->RecordDevice = ALSA_RecordDevice; 1598 impl->FlushRecording = ALSA_FlushRecording; 1599 1600 impl->HasRecordingSupport = true; 1601 1602 return true; 1603} 1604 1605AudioBootStrap ALSA_bootstrap = { 1606 "alsa", "ALSA PCM audio", ALSA_Init, false, false 1607}; 1608 1609#endif // SDL_AUDIO_DRIVER_ALSA 1610
[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.