Atlas - SDL_aaudio.c
Home / ext / SDL / src / audio / aaudio Lines: 1 | Size: 22127 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_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(__func__); 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 bool BuildAAudioStream(SDL_AudioDevice *device) 289{ 290 struct SDL_PrivateAudioData *hidden = device->hidden; 291 const bool recording = device->recording; 292 aaudio_result_t res; 293 294 SDL_SetAtomicInt(&hidden->error_callback_triggered, 0); 295 296 AAudioStreamBuilder *builder = NULL; 297 res = ctx.AAudio_createStreamBuilder(&builder); 298 if (res != AAUDIO_OK) { 299 LOGI("SDL Failed AAudio_createStreamBuilder %d", res); 300 return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res); 301 } else if (!builder) { 302 LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL"); 303 return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL"); 304 } 305 306#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES 307 const int aaudio_device_id = (int) ((size_t) device->handle); 308 LOGI("Opening device id %d", aaudio_device_id); 309 ctx.AAudioStreamBuilder_setDeviceId(builder, aaudio_device_id); 310#endif 311 312 aaudio_format_t format; 313 if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) { 314 format = AAUDIO_FORMAT_PCM_I32; 315 } else if (device->spec.format == SDL_AUDIO_F32) { 316 format = AAUDIO_FORMAT_PCM_FLOAT; 317 } else { 318 format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else. 319 } 320 ctx.AAudioStreamBuilder_setFormat(builder, format); 321 ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq); 322 ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels); 323 324 // If no specific buffer size has been requested, the device will pick the optimal 325 if(SDL_GetHint(SDL_HINT_AUDIO_DEVICE_SAMPLE_FRAMES)) { 326 ctx.AAudioStreamBuilder_setBufferCapacityInFrames(builder, 2 * device->sample_frames); // AAudio requires that the buffer capacity is at least 327 ctx.AAudioStreamBuilder_setFramesPerDataCallback(builder, device->sample_frames); // twice the size of the data callback buffer size 328 } 329 330 const aaudio_direction_t direction = (recording ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT); 331 ctx.AAudioStreamBuilder_setDirection(builder, direction); 332 ctx.AAudioStreamBuilder_setErrorCallback(builder, AAUDIO_errorCallback, device); 333 ctx.AAudioStreamBuilder_setDataCallback(builder, AAUDIO_dataCallback, device); 334 // Some devices have flat sounding audio when low latency mode is enabled, but this is a better experience for most people 335 if (SDL_GetHintBoolean(SDL_HINT_ANDROID_LOW_LATENCY_AUDIO, true)) { 336 SDL_Log("Low latency audio enabled"); 337 ctx.AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY); 338 } else { 339 SDL_Log("Low latency audio disabled"); 340 } 341 342 if (recording && ctx.AAudioStreamBuilder_setInputPreset) { // optional API: requires Android 28 343 // try to use a microphone that is for recording external audio. Otherwise Android might choose the mic used for talking 344 // on the telephone when held to the user's ear, which is often not useful at any distance from the device. 345 ctx.AAudioStreamBuilder_setInputPreset(builder, AAUDIO_INPUT_PRESET_CAMCORDER); 346 } 347 348 LOGI("AAudio Try to open %u hz %s %u channels samples %u", 349 device->spec.freq, SDL_GetAudioFormatName(device->spec.format), 350 device->spec.channels, device->sample_frames); 351 352 res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream); 353 if (res != AAUDIO_OK) { 354 LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res); 355 ctx.AAudioStreamBuilder_delete(builder); 356 return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); 357 } 358 ctx.AAudioStreamBuilder_delete(builder); 359 360 device->sample_frames = (int)ctx.AAudioStream_getFramesPerDataCallback(hidden->stream); 361 if (device->sample_frames == AAUDIO_UNSPECIFIED) { 362 // We'll get variable frames in the callback, make sure we have at least half a buffer available 363 device->sample_frames = (int)ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 2; 364 } 365 366 device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream); 367 device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream); 368 369 format = ctx.AAudioStream_getFormat(hidden->stream); 370 if (format == AAUDIO_FORMAT_PCM_I16) { 371 device->spec.format = SDL_AUDIO_S16; 372 } else if (format == AAUDIO_FORMAT_PCM_I32) { 373 device->spec.format = SDL_AUDIO_S32; 374 } else if (format == AAUDIO_FORMAT_PCM_FLOAT) { 375 device->spec.format = SDL_AUDIO_F32; 376 } else { 377 return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format); 378 } 379 380 SDL_UpdatedAudioDeviceFormat(device); 381 382 // Allocate a triple buffered mixing buffer 383 // Two buffers can be in the process of being filled while the third is being read 384 hidden->num_buffers = 3; 385 hidden->mixbuf_bytes = (hidden->num_buffers * device->buffer_size); 386 hidden->mixbuf = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), hidden->mixbuf_bytes); 387 if (!hidden->mixbuf) { 388 return false; 389 } 390 hidden->processed_bytes = 0; 391 hidden->callback_bytes = 0; 392 393 hidden->semaphore = SDL_CreateSemaphore(recording ? 0 : hidden->num_buffers - 1); 394 if (!hidden->semaphore) { 395 LOGI("SDL Failed SDL_CreateSemaphore %s recording:%d", SDL_GetError(), recording); 396 return false; 397 } 398 399 LOGI("AAudio Actually opened %u hz %s %u channels samples %u, buffers %d", 400 device->spec.freq, SDL_GetAudioFormatName(device->spec.format), 401 device->spec.channels, device->sample_frames, hidden->num_buffers); 402 403 res = ctx.AAudioStream_requestStart(hidden->stream); 404 if (res != AAUDIO_OK) { 405 LOGI("SDL Failed AAudioStream_requestStart %d recording:%d", res, recording); 406 return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); 407 } 408 409 LOGI("SDL AAudioStream_requestStart OK"); 410 411 return true; 412} 413 414// !!! FIXME: make this non-blocking! 415static void SDLCALL RequestAndroidPermissionBlockingCallback(void *userdata, const char *permission, bool granted) 416{ 417 SDL_SetAtomicInt((SDL_AtomicInt *) userdata, granted ? 1 : -1); 418} 419 420static bool AAUDIO_OpenDevice(SDL_AudioDevice *device) 421{ 422#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES 423 SDL_assert(device->handle); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero. 424#endif 425 426 LOGI(__func__); 427 428 if (device->recording) { 429 // !!! FIXME: make this non-blocking! 430 SDL_AtomicInt permission_response; 431 SDL_SetAtomicInt(&permission_response, 0); 432 if (!SDL_RequestAndroidPermission("android.permission.RECORD_AUDIO", RequestAndroidPermissionBlockingCallback, &permission_response)) { 433 return false; 434 } 435 436 while (SDL_GetAtomicInt(&permission_response) == 0) { 437 SDL_Delay(10); 438 } 439 440 if (SDL_GetAtomicInt(&permission_response) < 0) { 441 LOGI("This app doesn't have RECORD_AUDIO permission"); 442 return SDL_SetError("This app doesn't have RECORD_AUDIO permission"); 443 } 444 } 445 446 device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden)); 447 if (!device->hidden) { 448 return false; 449 } 450 451 return BuildAAudioStream(device); 452} 453 454static bool PauseOneDevice(SDL_AudioDevice *device, void *userdata) 455{ 456 struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden; 457 if (hidden) { 458 if (hidden->stream) { 459 aaudio_result_t res; 460 461 if (device->recording) { 462 // Pause() isn't implemented for recording, use Stop() 463 res = ctx.AAudioStream_requestStop(hidden->stream); 464 } else { 465 res = ctx.AAudioStream_requestPause(hidden->stream); 466 } 467 468 if (res != AAUDIO_OK) { 469 LOGI("SDL Failed AAudioStream_requestPause %d", res); 470 SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); 471 } 472 } 473 } 474 return false; // keep enumerating. 475} 476 477// Pause (block) all non already paused audio devices by taking their mixer lock 478void AAUDIO_PauseDevices(void) 479{ 480 if (ctx.handle) { // AAUDIO driver is used? 481 (void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL); 482 } 483} 484 485// Resume (unblock) all non already paused audio devices by releasing their mixer lock 486static bool ResumeOneDevice(SDL_AudioDevice *device, void *userdata) 487{ 488 struct SDL_PrivateAudioData *hidden = device->hidden; 489 if (hidden) { 490 if (hidden->stream) { 491 aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream); 492 if (res != AAUDIO_OK) { 493 LOGI("SDL Failed AAudioStream_requestStart %d", res); 494 SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res)); 495 } 496 } 497 } 498 return false; // keep enumerating. 499} 500 501void AAUDIO_ResumeDevices(void) 502{ 503 if (ctx.handle) { // AAUDIO driver is used? 504 (void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL); 505 } 506} 507 508static void AAUDIO_Deinitialize(void) 509{ 510 Android_StopAudioHotplug(); 511 512 LOGI(__func__); 513 if (ctx.handle) { 514 SDL_UnloadObject(ctx.handle); 515 } 516 SDL_zero(ctx); 517 LOGI("End AAUDIO %s", SDL_GetError()); 518} 519 520 521static bool AAUDIO_Init(SDL_AudioDriverImpl *impl) 522{ 523 LOGI(__func__); 524 525 /* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release, 526 * so don't use it until 8.1. 527 * 528 * See https://github.com/google/oboe/issues/40 for more information. 529 */ 530 if (SDL_GetAndroidSDKVersion() < 27) { 531 return false; 532 } 533 534 SDL_zero(ctx); 535 536 ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO); 537 if (!ctx.handle) { 538 LOGI("SDL couldn't find " LIB_AAUDIO_SO); 539 return false; 540 } 541 542 if (!AAUDIO_LoadFunctions(&ctx)) { 543 SDL_UnloadObject(ctx.handle); 544 SDL_zero(ctx); 545 return false; 546 } 547 548 impl->ThreadInit = Android_AudioThreadInit; 549 impl->Deinitialize = AAUDIO_Deinitialize; 550 impl->OpenDevice = AAUDIO_OpenDevice; 551 impl->CloseDevice = AAUDIO_CloseDevice; 552 impl->WaitDevice = AAUDIO_WaitDevice; 553 impl->PlayDevice = AAUDIO_PlayDevice; 554 impl->GetDeviceBuf = AAUDIO_GetDeviceBuf; 555 impl->WaitRecordingDevice = AAUDIO_WaitDevice; 556 impl->RecordDevice = AAUDIO_RecordDevice; 557 558 impl->HasRecordingSupport = true; 559 560#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES 561 impl->DetectDevices = Android_StartAudioHotplug; 562#else 563 impl->OnlyHasDefaultPlaybackDevice = true; 564 impl->OnlyHasDefaultRecordingDevice = true; 565#endif 566 567 LOGI("SDL AAUDIO_Init OK"); 568 return true; 569} 570 571AudioBootStrap AAUDIO_bootstrap = { 572 "AAudio", "AAudio audio driver", AAUDIO_Init, false, false 573}; 574 575#endif // SDL_AUDIO_DRIVER_AAUDIO 576[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.