Atlas - SDL_aaudio.c

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