Atlas - SDL_aaudio.c

Home / ext / SDL / src / audio / aaudio Lines: 1 | Size: 23561 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2026 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_AAUDIO 24 25#include "../SDL_sysaudio.h" 26#include "SDL_aaudio.h" 27 28#include "../../core/android/SDL_android.h" 29#include <aaudio/AAudio.h> 30 31#if __ANDROID_API__ < 31 32#define AAUDIO_FORMAT_PCM_I32 4 33#endif 34 35struct SDL_PrivateAudioData 36{ 37 AAudioStream *stream; 38 int num_buffers; 39 Uint8 *mixbuf; // Raw mixing buffer 40 size_t mixbuf_bytes; // num_buffers * device->buffer_size 41 size_t callback_bytes; 42 size_t processed_bytes; 43 SDL_Semaphore *semaphore; 44 SDL_AtomicInt error_callback_triggered; 45}; 46 47// Debug 48#if 0 49#define LOGI(...) SDL_Log(__VA_ARGS__); 50#else 51#define LOGI(...) 52#endif 53 54#define LIB_AAUDIO_SO "libaaudio.so" 55 56SDL_ELF_NOTE_DLOPEN( 57 "audio-aaudio", 58 "Support for audio through AAudio", 59 SDL_ELF_NOTE_DLOPEN_PRIORITY_SUGGESTED, 60 LIB_AAUDIO_SO 61) 62 63typedef struct AAUDIO_Data 64{ 65 SDL_SharedObject *handle; 66#define SDL_PROC(ret, func, params) ret (*func) params; 67#include "SDL_aaudiofuncs.h" 68} AAUDIO_Data; 69static AAUDIO_Data ctx; 70 71static bool AAUDIO_LoadFunctions(AAUDIO_Data *data) 72{ 73#define SDL_PROC(ret, func, params) \ 74 do { \ 75 data->func = (ret (*) params)SDL_LoadFunction(data->handle, #func); \ 76 if (!data->func) { \ 77 return SDL_SetError("Couldn't load AAUDIO function %s: %s", #func, SDL_GetError()); \ 78 } \ 79 } while (0); 80 81#define SDL_PROC_OPTIONAL(ret, func, params) \ 82 do { \ 83 data->func = (ret (*) params)SDL_LoadFunction(data->handle, #func); /* if it fails, okay. */ \ 84 } while (0); 85#include "SDL_aaudiofuncs.h" 86 return true; 87} 88 89 90static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error) 91{ 92 LOGI("SDL AAUDIO_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error)); 93 94 // You MUST NOT close the audio stream from this callback, so we cannot call SDL_AudioDeviceDisconnected here. 95 // Just flag the device so we can kill it in PlayDevice instead. 96 SDL_AudioDevice *device = (SDL_AudioDevice *) userData; 97 SDL_SetAtomicInt(&device->hidden->error_callback_triggered, (int) error); // AAUDIO_OK is zero, so !triggered means no error. 98 SDL_SignalSemaphore(device->hidden->semaphore); // in case we're blocking in WaitDevice. 99} 100 101static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames) 102{ 103 SDL_AudioDevice *device = (SDL_AudioDevice *) userData; 104 struct SDL_PrivateAudioData *hidden = device->hidden; 105 size_t framesize = SDL_AUDIO_FRAMESIZE(device->spec); 106 size_t callback_bytes = numFrames * framesize; 107 size_t old_buffer_index = hidden->callback_bytes / device->buffer_size; 108 109 if (device->recording) { 110 const Uint8 *input = (const Uint8 *)audioData; 111 size_t available_bytes = hidden->mixbuf_bytes - (hidden->callback_bytes - hidden->processed_bytes); 112 size_t size = SDL_min(available_bytes, callback_bytes); 113 size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes; 114 size_t end = (offset + size) % hidden->mixbuf_bytes; 115 SDL_assert(size <= hidden->mixbuf_bytes); 116 117//LOGI("Recorded %zu frames, %zu available, %zu max (%zu written, %zu read)", callback_bytes / framesize, available_bytes / framesize, hidden->mixbuf_bytes / framesize, hidden->callback_bytes / framesize, hidden->processed_bytes / framesize); 118 119 if (offset <= end) { 120 SDL_memcpy(&hidden->mixbuf[offset], input, size); 121 } else { 122 size_t partial = (hidden->mixbuf_bytes - offset); 123 SDL_memcpy(&hidden->mixbuf[offset], &input[0], partial); 124 SDL_memcpy(&hidden->mixbuf[0], &input[partial], end); 125 } 126 127 SDL_MemoryBarrierRelease(); 128 hidden->callback_bytes += size; 129 130 if (size < callback_bytes) { 131 LOGI("Audio recording overflow, dropped %zu frames", (callback_bytes - size) / framesize); 132 } 133 } else { 134 Uint8 *output = (Uint8 *)audioData; 135 size_t available_bytes = (hidden->processed_bytes - hidden->callback_bytes); 136 size_t size = SDL_min(available_bytes, callback_bytes); 137 size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes; 138 size_t end = (offset + size) % hidden->mixbuf_bytes; 139 SDL_assert(size <= hidden->mixbuf_bytes); 140 141//LOGI("Playing %zu frames, %zu available, %zu max (%zu written, %zu read)", callback_bytes / framesize, available_bytes / framesize, hidden->mixbuf_bytes / framesize, hidden->processed_bytes / framesize, hidden->callback_bytes / framesize); 142 143 SDL_MemoryBarrierAcquire(); 144 if (offset <= end) { 145 SDL_memcpy(output, &hidden->mixbuf[offset], size); 146 } else { 147 size_t partial = (hidden->mixbuf_bytes - offset); 148 SDL_memcpy(&output[0], &hidden->mixbuf[offset], partial); 149 SDL_memcpy(&output[partial], &hidden->mixbuf[0], end); 150 } 151 hidden->callback_bytes += size; 152 153 if (size < callback_bytes) { 154 LOGI("Audio playback underflow, missed %zu frames", (callback_bytes - size) / framesize); 155 SDL_memset(&output[size], device->silence_value, (callback_bytes - size)); 156 } 157 } 158 159 size_t new_buffer_index = hidden->callback_bytes / device->buffer_size; 160 while (old_buffer_index < new_buffer_index) { 161 // Trigger audio processing 162 SDL_SignalSemaphore(hidden->semaphore); 163 ++old_buffer_index; 164 } 165 166 return AAUDIO_CALLBACK_RESULT_CONTINUE; 167} 168 169static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize) 170{ 171 struct SDL_PrivateAudioData *hidden = device->hidden; 172 size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes); 173 return &hidden->mixbuf[offset]; 174} 175 176static bool AAUDIO_WaitDevice(SDL_AudioDevice *device) 177{ 178 while (!SDL_GetAtomicInt(&device->shutdown)) { 179 // this semaphore won't fire when the app is in the background (AAUDIO_PauseDevices was called). 180 if (SDL_WaitSemaphoreTimeout(device->hidden->semaphore, 100)) { 181 return true; // semaphore was signaled, let's go! 182 } 183 // Still waiting on the semaphore (or the system), check other things then wait again. 184 } 185 return true; 186} 187 188static bool BuildAAudioStream(SDL_AudioDevice *device); 189 190static bool RecoverAAudioDevice(SDL_AudioDevice *device) 191{ 192 struct SDL_PrivateAudioData *hidden = device->hidden; 193 194 // attempt to build a new stream, in case there's a new default device. 195 ctx.AAudioStream_requestStop(hidden->stream); 196 ctx.AAudioStream_close(hidden->stream); 197 hidden->stream = NULL; 198 199 SDL_aligned_free(hidden->mixbuf); 200 hidden->mixbuf = NULL; 201 202 SDL_DestroySemaphore(hidden->semaphore); 203 hidden->semaphore = NULL; 204 205 const int prev_sample_frames = device->sample_frames; 206 SDL_AudioSpec prevspec; 207 SDL_copyp(&prevspec, &device->spec); 208 209 if (!BuildAAudioStream(device)) { 210 return false; // oh well, we tried. 211 } 212 213 // we don't know the new device spec until we open the new device, so we saved off the old one and force it back 214 // so SDL_AudioDeviceFormatChanged can set up all the important state if necessary and then set it back to the new spec. 215 const int new_sample_frames = device->sample_frames; 216 SDL_AudioSpec newspec; 217 SDL_copyp(&newspec, &device->spec); 218 219 device->sample_frames = prev_sample_frames; 220 SDL_copyp(&device->spec, &prevspec); 221 if (!SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames)) { 222 return false; // ugh 223 } 224 return true; 225} 226 227 228static bool AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen) 229{ 230 struct SDL_PrivateAudioData *hidden = device->hidden; 231 232 // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here. 233 const aaudio_result_t err = (aaudio_result_t) SDL_GetAtomicInt(&hidden->error_callback_triggered); 234 if (err) { 235 SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "aaudio: Audio device triggered error %d (%s)", (int) err, ctx.AAudio_convertResultToText(err)); 236 237 if (!RecoverAAudioDevice(device)) { 238 return false; // oh well, we went down hard. 239 } 240 } else { 241 SDL_MemoryBarrierRelease(); 242 hidden->processed_bytes += buflen; 243 } 244 return true; 245} 246 247static int AAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen) 248{ 249 struct SDL_PrivateAudioData *hidden = device->hidden; 250 251 // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here. 252 if (SDL_GetAtomicInt(&hidden->error_callback_triggered)) { 253 SDL_SetAtomicInt(&hidden->error_callback_triggered, 0); 254 return -1; 255 } 256 257 SDL_assert(buflen == device->buffer_size); // If this isn't true, we need to change semaphore trigger logic and account for wrapping copies here 258 size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes); 259 SDL_MemoryBarrierAcquire(); 260 SDL_memcpy(buffer, &hidden->mixbuf[offset], buflen); 261 hidden->processed_bytes += buflen; 262 return buflen; 263} 264 265static void AAUDIO_CloseDevice(SDL_AudioDevice *device) 266{ 267 struct SDL_PrivateAudioData *hidden = device->hidden; 268 LOGI(SDL_FUNCTION); 269 270 if (hidden) { 271 if (hidden->stream) { 272 ctx.AAudioStream_requestStop(hidden->stream); 273 // !!! FIXME: do we have to wait for the state to change to make sure all buffered audio has played, or will close do this (or will the system do this after the close)? 274 // !!! FIXME: also, will this definitely wait for a running data callback to finish, and then stop the callback from firing again? 275 ctx.AAudioStream_close(hidden->stream); 276 } 277 278 if (hidden->semaphore) { 279 SDL_DestroySemaphore(hidden->semaphore); 280 } 281 282 SDL_aligned_free(hidden->mixbuf); 283 SDL_free(hidden); 284 device->hidden = NULL; 285 } 286} 287 288static void SetOptionalStreamUsage(AAudioStreamBuilder *builder) 289{ 290 if (ctx.AAudioStreamBuilder_setUsage) { // optional API: requires Android 28 291 const char *hint = SDL_GetHint(SDL_HINT_AUDIO_DEVICE_STREAM_ROLE); 292 if (hint) { 293 aaudio_usage_t usage = AAUDIO_USAGE_MEDIA; // covers most things, and is the system default. 294 if ((SDL_strcasecmp(hint, "Communications") == 0) || (SDL_strcasecmp(hint, "GameChat") == 0)) { 295 usage = AAUDIO_USAGE_VOICE_COMMUNICATION; 296 } else if (SDL_strcasecmp(hint, "Game") == 0) { 297 usage = AAUDIO_USAGE_GAME; 298 } 299 ctx.AAudioStreamBuilder_setUsage(builder, usage); 300 301 // !!! FIXME: I _think_ this is okay with the current set of usages we support, but the docs 302 // !!! FIXME: say you need to dip down into Java to call android.app.Activity.setVolumeControlStream(usage) 303 // !!! FIXME: so the physical volume buttons control this stream, but that might be more for special cases 304 // !!! FIXME: like notification sounds, etc, and it's possible you _don't_ want to override this for those 305 // !!! FIXME: special cases, too! We'll revisit if there are bug reports. 306 } 307 } 308} 309 310static bool BuildAAudioStream(SDL_AudioDevice *device) 311{ 312 struct SDL_PrivateAudioData *hidden = device->hidden; 313 const bool recording = device->recording; 314 aaudio_result_t res; 315 316 SDL_SetAtomicInt(&hidden->error_callback_triggered, 0); 317 318 AAudioStreamBuilder *builder = NULL; 319 res = ctx.AAudio_createStreamBuilder(&builder); 320 if (res != AAUDIO_OK) { 321 LOGI("SDL Failed AAudio_createStreamBuilder %d", res); 322 return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res); 323 } else if (!builder) { 324 LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL"); 325 return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL"); 326 } 327 328#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES 329 const int aaudio_device_id = (int) ((size_t) device->handle); 330 LOGI("Opening device id %d", aaudio_device_id); 331 ctx.AAudioStreamBuilder_setDeviceId(builder, aaudio_device_id); 332#endif 333 334 aaudio_format_t format; 335 if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) { 336 format = AAUDIO_FORMAT_PCM_I32; 337 } else if (device->spec.format == SDL_AUDIO_F32) { 338 format = AAUDIO_FORMAT_PCM_FLOAT; 339 } else { 340 format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else. 341 } 342 ctx.AAudioStreamBuilder_setFormat(builder, format); 343 ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq); 344 ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels); 345 346 int32_t sample_frames; 347 if (SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES)) { 348 sample_frames = device->sample_frames; 349 } else { 350 // Use 20 ms for the default audio buffer size 351 sample_frames = (device->spec.freq / 50); 352 } 353 ctx.AAudioStreamBuilder_setBufferCapacityInFrames(builder, 2 * sample_frames); // AAudio requires that the buffer capacity is at least 354 ctx.AAudioStreamBuilder_setFramesPerDataCallback(builder, sample_frames); // twice the size of the data callback buffer size 355 356 const aaudio_direction_t direction = (recording ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT); 357 ctx.AAudioStreamBuilder_setDirection(builder, direction); 358 ctx.AAudioStreamBuilder_setErrorCallback(builder, AAUDIO_errorCallback, device); 359 ctx.AAudioStreamBuilder_setDataCallback(builder, AAUDIO_dataCallback, device); 360 // Some devices have flat sounding audio when low latency mode is enabled, but this is a better experience for most people 361 if (SDL_GetHintBoolean(SDL_HINT_ANDROID_LOW_LATENCY_AUDIO, true)) { 362 SDL_Log("Low latency audio enabled"); 363 ctx.AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); 364 } else { 365 SDL_Log("Low latency audio disabled"); 366 } 367 368 SetOptionalStreamUsage(builder); 369 370 if (recording && ctx.AAudioStreamBuilder_setInputPreset) { // optional API: requires Android 28 371 // try to use a microphone that is for recording external audio. Otherwise Android might choose the mic used for talking 372 // on the telephone when held to the user's ear, which is often not useful at any distance from the device. 373 ctx.AAudioStreamBuilder_setInputPreset(builder, AAUDIO_INPUT_PRESET_CAMCORDER); 374 } 375 376 LOGI("AAudio Try to open %u hz %s %u channels samples %u", 377 device->spec.freq, SDL_GetAudioFormatName(device->spec.format), 378 device->spec.channels, device->sample_frames); 379 380 res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream); 381 if (res != AAUDIO_OK) { 382 LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res); 383 ctx.AAudioStreamBuilder_delete(builder); 384 return SDL_SetError("%s : %s", SDL_FUNCTION, ctx.AAudio_convertResultToText(res)); 385 } 386 ctx.AAudioStreamBuilder_delete(builder); 387 388 device->sample_frames = (int)ctx.AAudioStream_getFramesPerDataCallback(hidden->stream); 389 if (device->sample_frames == AAUDIO_UNSPECIFIED) { 390 // We'll get variable frames in the callback, make sure we have at least half a buffer available 391 device->sample_frames = (int)ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 2; 392 } 393 394 device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream); 395 device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream); 396 397 format = ctx.AAudioStream_getFormat(hidden->stream); 398 if (format == AAUDIO_FORMAT_PCM_I16) { 399 device->spec.format = SDL_AUDIO_S16; 400 } else if (format == AAUDIO_FORMAT_PCM_I32) { 401 device->spec.format = SDL_AUDIO_S32; 402 } else if (format == AAUDIO_FORMAT_PCM_FLOAT) { 403 device->spec.format = SDL_AUDIO_F32; 404 } else { 405 return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format); 406 } 407 408 SDL_UpdatedAudioDeviceFormat(device); 409 410 // Allocate a triple buffered mixing buffer 411 // Two buffers can be in the process of being filled while the third is being read 412 hidden->num_buffers = 3; 413 hidden->mixbuf_bytes = (hidden->num_buffers * device->buffer_size); 414 hidden->mixbuf = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), hidden->mixbuf_bytes); 415 if (!hidden->mixbuf) { 416 return false; 417 } 418 hidden->processed_bytes = 0; 419 hidden->callback_bytes = 0; 420 421 hidden->semaphore = SDL_CreateSemaphore(recording ? 0 : hidden->num_buffers - 1); 422 if (!hidden->semaphore) { 423 LOGI("SDL Failed SDL_CreateSemaphore %s recording:%d", SDL_GetError(), recording); 424 return false; 425 } 426 427 LOGI("AAudio Actually opened %u hz %s %u channels samples %u, buffers %d", 428 device->spec.freq, SDL_GetAudioFormatName(device->spec.format), 429 device->spec.channels, device->sample_frames, hidden->num_buffers); 430 431 res = ctx.AAudioStream_requestStart(hidden->stream); 432 if (res != AAUDIO_OK) { 433 LOGI("SDL Failed AAudioStream_requestStart %d recording:%d", res, recording); 434 return SDL_SetError("%s : %s", SDL_FUNCTION, ctx.AAudio_convertResultToText(res)); 435 } 436 437 LOGI("SDL AAudioStream_requestStart OK"); 438 439 return true; 440} 441 442// !!! FIXME: make this non-blocking! 443static void SDLCALL RequestAndroidPermissionBlockingCallback(void *userdata, const char *permission, bool granted) 444{ 445 SDL_SetAtomicInt((SDL_AtomicInt *) userdata, granted ? 1 : -1); 446} 447 448static bool AAUDIO_OpenDevice(SDL_AudioDevice *device) 449{ 450#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES 451 SDL_assert(device->handle); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero. 452#endif 453 454 LOGI(SDL_FUNCTION); 455 456 if (device->recording) { 457 // !!! FIXME: make this non-blocking! 458 SDL_AtomicInt permission_response; 459 SDL_SetAtomicInt(&permission_response, 0); 460 if (!SDL_RequestAndroidPermission("android.permission.RECORD_AUDIO", RequestAndroidPermissionBlockingCallback, &permission_response)) { 461 return false; 462 } 463 464 while (SDL_GetAtomicInt(&permission_response) == 0) { 465 SDL_Delay(10); 466 } 467 468 if (SDL_GetAtomicInt(&permission_response) < 0) { 469 LOGI("This app doesn't have RECORD_AUDIO permission"); 470 return SDL_SetError("This app doesn't have RECORD_AUDIO permission"); 471 } 472 } 473 474 device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); 475 if (!device->hidden) { 476 return false; 477 } 478 479 return BuildAAudioStream(device); 480} 481 482static bool PauseOneDevice(SDL_AudioDevice *device, void *userdata) 483{ 484 struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden; 485 if (hidden) { 486 if (hidden->stream) { 487 aaudio_result_t res; 488 489 if (device->recording) { 490 // Pause() isn't implemented for recording, use Stop() 491 res = ctx.AAudioStream_requestStop(hidden->stream); 492 } else { 493 res = ctx.AAudioStream_requestPause(hidden->stream); 494 } 495 496 if (res != AAUDIO_OK) { 497 LOGI("SDL Failed AAudioStream_requestPause %d", res); 498 SDL_SetError("%s : %s", SDL_FUNCTION, ctx.AAudio_convertResultToText(res)); 499 } 500 } 501 } 502 return false; // keep enumerating. 503} 504 505// Pause (block) all non already paused audio devices by taking their mixer lock 506void AAUDIO_PauseDevices(void) 507{ 508 if (ctx.handle) { // AAUDIO driver is used? 509 (void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL); 510 } 511} 512 513// Resume (unblock) all non already paused audio devices by releasing their mixer lock 514static bool ResumeOneDevice(SDL_AudioDevice *device, void *userdata) 515{ 516 struct SDL_PrivateAudioData *hidden = device->hidden; 517 if (hidden) { 518 if (hidden->stream) { 519 aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream); 520 if (res != AAUDIO_OK) { 521 LOGI("SDL Failed AAudioStream_requestStart %d", res); 522 SDL_SetError("%s : %s", SDL_FUNCTION, ctx.AAudio_convertResultToText(res)); 523 } 524 } 525 } 526 return false; // keep enumerating. 527} 528 529void AAUDIO_ResumeDevices(void) 530{ 531 if (ctx.handle) { // AAUDIO driver is used? 532 (void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL); 533 } 534} 535 536static void AAUDIO_Deinitialize(void) 537{ 538 Android_StopAudioHotplug(); 539 540 LOGI(SDL_FUNCTION); 541 if (ctx.handle) { 542 SDL_UnloadObject(ctx.handle); 543 } 544 SDL_zero(ctx); 545 LOGI("End AAUDIO %s", SDL_GetError()); 546} 547 548 549static bool AAUDIO_Init(SDL_AudioDriverImpl *impl) 550{ 551 LOGI(SDL_FUNCTION); 552 553 /* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release, 554 * so don't use it until 8.1. 555 * 556 * See https://github.com/google/oboe/issues/40 for more information. 557 */ 558 if (SDL_GetAndroidSDKVersion() < 27) { 559 return false; 560 } 561 562 SDL_zero(ctx); 563 564 ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO); 565 if (!ctx.handle) { 566 LOGI("SDL couldn't find " LIB_AAUDIO_SO); 567 return false; 568 } 569 570 if (!AAUDIO_LoadFunctions(&ctx)) { 571 SDL_UnloadObject(ctx.handle); 572 SDL_zero(ctx); 573 return false; 574 } 575 576 impl->ThreadInit = Android_AudioThreadInit; 577 impl->Deinitialize = AAUDIO_Deinitialize; 578 impl->OpenDevice = AAUDIO_OpenDevice; 579 impl->CloseDevice = AAUDIO_CloseDevice; 580 impl->WaitDevice = AAUDIO_WaitDevice; 581 impl->PlayDevice = AAUDIO_PlayDevice; 582 impl->GetDeviceBuf = AAUDIO_GetDeviceBuf; 583 impl->WaitRecordingDevice = AAUDIO_WaitDevice; 584 impl->RecordDevice = AAUDIO_RecordDevice; 585 586 impl->HasRecordingSupport = true; 587 588#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES 589 impl->DetectDevices = Android_StartAudioHotplug; 590#else 591 impl->OnlyHasDefaultPlaybackDevice = true; 592 impl->OnlyHasDefaultRecordingDevice = true; 593#endif 594 595 LOGI("SDL AAUDIO_Init OK"); 596 return true; 597} 598 599AudioBootStrap AAUDIO_bootstrap = { 600 "AAudio", "AAudio audio driver", AAUDIO_Init, false, false 601}; 602 603#endif // SDL_AUDIO_DRIVER_AAUDIO 604
[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.