SDL_aaudio.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551
  1. /*
  2. Simple DirectMedia Layer
  3. Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
  4. This software is provided 'as-is', without any express or implied
  5. warranty. In no event will the authors be held liable for any damages
  6. arising from the use of this software.
  7. Permission is granted to anyone to use this software for any purpose,
  8. including commercial applications, and to alter it and redistribute it
  9. freely, subject to the following restrictions:
  10. 1. The origin of this software must not be misrepresented; you must not
  11. claim that you wrote the original software. If you use this software
  12. in a product, an acknowledgment in the product documentation would be
  13. appreciated but is not required.
  14. 2. Altered source versions must be plainly marked as such, and must not be
  15. misrepresented as being the original software.
  16. 3. This notice may not be removed or altered from any source distribution.
  17. */
  18. #include "SDL_internal.h"
  19. #ifdef SDL_AUDIO_DRIVER_AAUDIO
  20. #include "../SDL_sysaudio.h"
  21. #include "SDL_aaudio.h"
  22. #include "../../core/android/SDL_android.h"
  23. #include <aaudio/AAudio.h>
  24. #if __ANDROID_API__ < 31
  25. #define AAUDIO_FORMAT_PCM_I32 4
  26. #endif
  27. struct SDL_PrivateAudioData
  28. {
  29. AAudioStream *stream;
  30. int num_buffers;
  31. Uint8 *mixbuf; // Raw mixing buffer
  32. size_t mixbuf_bytes; // num_buffers * device->buffer_size
  33. size_t callback_bytes;
  34. size_t processed_bytes;
  35. SDL_Semaphore *semaphore;
  36. SDL_AtomicInt error_callback_triggered;
  37. };
  38. // Debug
  39. #if 0
  40. #define LOGI(...) SDL_Log(__VA_ARGS__);
  41. #else
  42. #define LOGI(...)
  43. #endif
  44. #define LIB_AAUDIO_SO "libaaudio.so"
  45. typedef struct AAUDIO_Data
  46. {
  47. SDL_SharedObject *handle;
  48. #define SDL_PROC(ret, func, params) ret (*func) params;
  49. #include "SDL_aaudiofuncs.h"
  50. } AAUDIO_Data;
  51. static AAUDIO_Data ctx;
  52. static bool AAUDIO_LoadFunctions(AAUDIO_Data *data)
  53. {
  54. #define SDL_PROC(ret, func, params) \
  55. do { \
  56. data->func = (ret (*) params)SDL_LoadFunction(data->handle, #func); \
  57. if (!data->func) { \
  58. return SDL_SetError("Couldn't load AAUDIO function %s: %s", #func, SDL_GetError()); \
  59. } \
  60. } while (0);
  61. #include "SDL_aaudiofuncs.h"
  62. return true;
  63. }
  64. static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error)
  65. {
  66. LOGI("SDL AAUDIO_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error));
  67. // You MUST NOT close the audio stream from this callback, so we cannot call SDL_AudioDeviceDisconnected here.
  68. // Just flag the device so we can kill it in PlayDevice instead.
  69. SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
  70. SDL_SetAtomicInt(&device->hidden->error_callback_triggered, (int) error); // AAUDIO_OK is zero, so !triggered means no error.
  71. SDL_SignalSemaphore(device->hidden->semaphore); // in case we're blocking in WaitDevice.
  72. }
  73. static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames)
  74. {
  75. SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
  76. struct SDL_PrivateAudioData *hidden = device->hidden;
  77. size_t framesize = SDL_AUDIO_FRAMESIZE(device->spec);
  78. size_t callback_bytes = numFrames * framesize;
  79. size_t old_buffer_index = hidden->callback_bytes / device->buffer_size;
  80. if (device->recording) {
  81. const Uint8 *input = (const Uint8 *)audioData;
  82. size_t available_bytes = hidden->mixbuf_bytes - (hidden->callback_bytes - hidden->processed_bytes);
  83. size_t size = SDL_min(available_bytes, callback_bytes);
  84. size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes;
  85. size_t end = (offset + size) % hidden->mixbuf_bytes;
  86. SDL_assert(size <= hidden->mixbuf_bytes);
  87. //LOGI("Recorded %zu frames, %zu available, %zu max (%zu written, %zu read)\n", callback_bytes / framesize, available_bytes / framesize, hidden->mixbuf_bytes / framesize, hidden->callback_bytes / framesize, hidden->processed_bytes / framesize);
  88. if (offset <= end) {
  89. SDL_memcpy(&hidden->mixbuf[offset], input, size);
  90. } else {
  91. size_t partial = (hidden->mixbuf_bytes - offset);
  92. SDL_memcpy(&hidden->mixbuf[offset], &input[0], partial);
  93. SDL_memcpy(&hidden->mixbuf[0], &input[partial], end);
  94. }
  95. SDL_MemoryBarrierRelease();
  96. hidden->callback_bytes += size;
  97. if (size < callback_bytes) {
  98. LOGI("Audio recording overflow, dropped %zu frames\n", (callback_bytes - size) / framesize);
  99. }
  100. } else {
  101. Uint8 *output = (Uint8 *)audioData;
  102. size_t available_bytes = (hidden->processed_bytes - hidden->callback_bytes);
  103. size_t size = SDL_min(available_bytes, callback_bytes);
  104. size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes;
  105. size_t end = (offset + size) % hidden->mixbuf_bytes;
  106. SDL_assert(size <= hidden->mixbuf_bytes);
  107. //LOGI("Playing %zu frames, %zu available, %zu max (%zu written, %zu read)\n", callback_bytes / framesize, available_bytes / framesize, hidden->mixbuf_bytes / framesize, hidden->processed_bytes / framesize, hidden->callback_bytes / framesize);
  108. SDL_MemoryBarrierAcquire();
  109. if (offset <= end) {
  110. SDL_memcpy(output, &hidden->mixbuf[offset], size);
  111. } else {
  112. size_t partial = (hidden->mixbuf_bytes - offset);
  113. SDL_memcpy(&output[0], &hidden->mixbuf[offset], partial);
  114. SDL_memcpy(&output[partial], &hidden->mixbuf[0], end);
  115. }
  116. hidden->callback_bytes += size;
  117. if (size < callback_bytes) {
  118. LOGI("Audio playback underflow, missed %zu frames\n", (callback_bytes - size) / framesize);
  119. SDL_memset(&output[size], device->silence_value, (callback_bytes - size));
  120. }
  121. }
  122. size_t new_buffer_index = hidden->callback_bytes / device->buffer_size;
  123. while (old_buffer_index < new_buffer_index) {
  124. // Trigger audio processing
  125. SDL_SignalSemaphore(hidden->semaphore);
  126. ++old_buffer_index;
  127. }
  128. return AAUDIO_CALLBACK_RESULT_CONTINUE;
  129. }
  130. static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
  131. {
  132. struct SDL_PrivateAudioData *hidden = device->hidden;
  133. size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes);
  134. return &hidden->mixbuf[offset];
  135. }
  136. static bool AAUDIO_WaitDevice(SDL_AudioDevice *device)
  137. {
  138. while (!SDL_GetAtomicInt(&device->shutdown)) {
  139. // this semaphore won't fire when the app is in the background (AAUDIO_PauseDevices was called).
  140. if (SDL_WaitSemaphoreTimeout(device->hidden->semaphore, 100)) {
  141. return true; // semaphore was signaled, let's go!
  142. }
  143. // Still waiting on the semaphore (or the system), check other things then wait again.
  144. }
  145. return true;
  146. }
  147. static bool BuildAAudioStream(SDL_AudioDevice *device);
  148. static bool RecoverAAudioDevice(SDL_AudioDevice *device)
  149. {
  150. struct SDL_PrivateAudioData *hidden = device->hidden;
  151. // attempt to build a new stream, in case there's a new default device.
  152. ctx.AAudioStream_requestStop(hidden->stream);
  153. ctx.AAudioStream_close(hidden->stream);
  154. hidden->stream = NULL;
  155. SDL_aligned_free(hidden->mixbuf);
  156. hidden->mixbuf = NULL;
  157. SDL_DestroySemaphore(hidden->semaphore);
  158. hidden->semaphore = NULL;
  159. const int prev_sample_frames = device->sample_frames;
  160. SDL_AudioSpec prevspec;
  161. SDL_copyp(&prevspec, &device->spec);
  162. if (!BuildAAudioStream(device)) {
  163. return false; // oh well, we tried.
  164. }
  165. // 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
  166. // so SDL_AudioDeviceFormatChanged can set up all the important state if necessary and then set it back to the new spec.
  167. const int new_sample_frames = device->sample_frames;
  168. SDL_AudioSpec newspec;
  169. SDL_copyp(&newspec, &device->spec);
  170. device->sample_frames = prev_sample_frames;
  171. SDL_copyp(&device->spec, &prevspec);
  172. if (!SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames)) {
  173. return false; // ugh
  174. }
  175. return true;
  176. }
  177. static bool AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
  178. {
  179. struct SDL_PrivateAudioData *hidden = device->hidden;
  180. // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
  181. const aaudio_result_t err = (aaudio_result_t) SDL_GetAtomicInt(&hidden->error_callback_triggered);
  182. if (err) {
  183. SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "aaudio: Audio device triggered error %d (%s)", (int) err, ctx.AAudio_convertResultToText(err));
  184. if (!RecoverAAudioDevice(device)) {
  185. return false; // oh well, we went down hard.
  186. }
  187. } else {
  188. SDL_MemoryBarrierRelease();
  189. hidden->processed_bytes += buflen;
  190. }
  191. return true;
  192. }
  193. static int AAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
  194. {
  195. struct SDL_PrivateAudioData *hidden = device->hidden;
  196. // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
  197. if (SDL_GetAtomicInt(&hidden->error_callback_triggered)) {
  198. SDL_SetAtomicInt(&hidden->error_callback_triggered, 0);
  199. return -1;
  200. }
  201. SDL_assert(buflen == device->buffer_size); // If this isn't true, we need to change semaphore trigger logic and account for wrapping copies here
  202. size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes);
  203. SDL_MemoryBarrierAcquire();
  204. SDL_memcpy(buffer, &hidden->mixbuf[offset], buflen);
  205. hidden->processed_bytes += buflen;
  206. return buflen;
  207. }
  208. static void AAUDIO_CloseDevice(SDL_AudioDevice *device)
  209. {
  210. struct SDL_PrivateAudioData *hidden = device->hidden;
  211. LOGI(__func__);
  212. if (hidden) {
  213. if (hidden->stream) {
  214. ctx.AAudioStream_requestStop(hidden->stream);
  215. // !!! 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)?
  216. // !!! FIXME: also, will this definitely wait for a running data callback to finish, and then stop the callback from firing again?
  217. ctx.AAudioStream_close(hidden->stream);
  218. }
  219. if (hidden->semaphore) {
  220. SDL_DestroySemaphore(hidden->semaphore);
  221. }
  222. SDL_aligned_free(hidden->mixbuf);
  223. SDL_free(hidden);
  224. device->hidden = NULL;
  225. }
  226. }
  227. static bool BuildAAudioStream(SDL_AudioDevice *device)
  228. {
  229. struct SDL_PrivateAudioData *hidden = device->hidden;
  230. const bool recording = device->recording;
  231. aaudio_result_t res;
  232. SDL_SetAtomicInt(&hidden->error_callback_triggered, 0);
  233. AAudioStreamBuilder *builder = NULL;
  234. res = ctx.AAudio_createStreamBuilder(&builder);
  235. if (res != AAUDIO_OK) {
  236. LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
  237. return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res);
  238. } else if (!builder) {
  239. LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
  240. return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL");
  241. }
  242. #if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
  243. const int aaudio_device_id = (int) ((size_t) device->handle);
  244. LOGI("Opening device id %d", aaudio_device_id);
  245. ctx.AAudioStreamBuilder_setDeviceId(builder, aaudio_device_id);
  246. #endif
  247. aaudio_format_t format;
  248. if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) {
  249. format = AAUDIO_FORMAT_PCM_I32;
  250. } else if (device->spec.format == SDL_AUDIO_F32) {
  251. format = AAUDIO_FORMAT_PCM_FLOAT;
  252. } else {
  253. format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else.
  254. }
  255. ctx.AAudioStreamBuilder_setFormat(builder, format);
  256. ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq);
  257. ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels);
  258. const aaudio_direction_t direction = (recording ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
  259. ctx.AAudioStreamBuilder_setDirection(builder, direction);
  260. ctx.AAudioStreamBuilder_setErrorCallback(builder, AAUDIO_errorCallback, device);
  261. ctx.AAudioStreamBuilder_setDataCallback(builder, AAUDIO_dataCallback, device);
  262. // Some devices have flat sounding audio when low latency mode is enabled, but this is a better experience for most people
  263. if (SDL_GetHintBoolean(SDL_HINT_ANDROID_LOW_LATENCY_AUDIO, true)) {
  264. SDL_Log("Low latency audio enabled");
  265. ctx.AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
  266. } else {
  267. SDL_Log("Low latency audio disabled");
  268. }
  269. LOGI("AAudio Try to open %u hz %s %u channels samples %u",
  270. device->spec.freq, SDL_GetAudioFormatName(device->spec.format),
  271. device->spec.channels, device->sample_frames);
  272. res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream);
  273. if (res != AAUDIO_OK) {
  274. LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
  275. ctx.AAudioStreamBuilder_delete(builder);
  276. return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
  277. }
  278. ctx.AAudioStreamBuilder_delete(builder);
  279. device->sample_frames = (int)ctx.AAudioStream_getFramesPerDataCallback(hidden->stream);
  280. if (device->sample_frames == AAUDIO_UNSPECIFIED) {
  281. // We'll get variable frames in the callback, make sure we have at least half a buffer available
  282. device->sample_frames = (int)ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 2;
  283. }
  284. device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream);
  285. device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream);
  286. format = ctx.AAudioStream_getFormat(hidden->stream);
  287. if (format == AAUDIO_FORMAT_PCM_I16) {
  288. device->spec.format = SDL_AUDIO_S16;
  289. } else if (format == AAUDIO_FORMAT_PCM_I32) {
  290. device->spec.format = SDL_AUDIO_S32;
  291. } else if (format == AAUDIO_FORMAT_PCM_FLOAT) {
  292. device->spec.format = SDL_AUDIO_F32;
  293. } else {
  294. return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format);
  295. }
  296. SDL_UpdatedAudioDeviceFormat(device);
  297. // Allocate a triple buffered mixing buffer
  298. // Two buffers can be in the process of being filled while the third is being read
  299. hidden->num_buffers = 3;
  300. hidden->mixbuf_bytes = (hidden->num_buffers * device->buffer_size);
  301. hidden->mixbuf = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), hidden->mixbuf_bytes);
  302. if (!hidden->mixbuf) {
  303. return false;
  304. }
  305. hidden->processed_bytes = 0;
  306. hidden->callback_bytes = 0;
  307. hidden->semaphore = SDL_CreateSemaphore(recording ? 0 : hidden->num_buffers);
  308. if (!hidden->semaphore) {
  309. LOGI("SDL Failed SDL_CreateSemaphore %s recording:%d", SDL_GetError(), recording);
  310. return false;
  311. }
  312. LOGI("AAudio Actually opened %u hz %s %u channels samples %u, buffers %d",
  313. device->spec.freq, SDL_GetAudioFormatName(device->spec.format),
  314. device->spec.channels, device->sample_frames, hidden->num_buffers);
  315. res = ctx.AAudioStream_requestStart(hidden->stream);
  316. if (res != AAUDIO_OK) {
  317. LOGI("SDL Failed AAudioStream_requestStart %d recording:%d", res, recording);
  318. return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
  319. }
  320. LOGI("SDL AAudioStream_requestStart OK");
  321. return true;
  322. }
  323. // !!! FIXME: make this non-blocking!
  324. static void SDLCALL RequestAndroidPermissionBlockingCallback(void *userdata, const char *permission, bool granted)
  325. {
  326. SDL_SetAtomicInt((SDL_AtomicInt *) userdata, granted ? 1 : -1);
  327. }
  328. static bool AAUDIO_OpenDevice(SDL_AudioDevice *device)
  329. {
  330. #if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
  331. SDL_assert(device->handle); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero.
  332. #endif
  333. LOGI(__func__);
  334. if (device->recording) {
  335. // !!! FIXME: make this non-blocking!
  336. SDL_AtomicInt permission_response;
  337. SDL_SetAtomicInt(&permission_response, 0);
  338. if (!SDL_RequestAndroidPermission("android.permission.RECORD_AUDIO", RequestAndroidPermissionBlockingCallback, &permission_response)) {
  339. return false;
  340. }
  341. while (SDL_GetAtomicInt(&permission_response) == 0) {
  342. SDL_Delay(10);
  343. }
  344. if (SDL_GetAtomicInt(&permission_response) < 0) {
  345. LOGI("This app doesn't have RECORD_AUDIO permission");
  346. return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
  347. }
  348. }
  349. device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
  350. if (!device->hidden) {
  351. return false;
  352. }
  353. return BuildAAudioStream(device);
  354. }
  355. static bool PauseOneDevice(SDL_AudioDevice *device, void *userdata)
  356. {
  357. struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden;
  358. if (hidden) {
  359. if (hidden->stream) {
  360. aaudio_result_t res;
  361. if (device->recording) {
  362. // Pause() isn't implemented for recording, use Stop()
  363. res = ctx.AAudioStream_requestStop(hidden->stream);
  364. } else {
  365. res = ctx.AAudioStream_requestPause(hidden->stream);
  366. }
  367. if (res != AAUDIO_OK) {
  368. LOGI("SDL Failed AAudioStream_requestPause %d", res);
  369. SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
  370. }
  371. }
  372. }
  373. return false; // keep enumerating.
  374. }
  375. // Pause (block) all non already paused audio devices by taking their mixer lock
  376. void AAUDIO_PauseDevices(void)
  377. {
  378. if (ctx.handle) { // AAUDIO driver is used?
  379. (void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL);
  380. }
  381. }
  382. // Resume (unblock) all non already paused audio devices by releasing their mixer lock
  383. static bool ResumeOneDevice(SDL_AudioDevice *device, void *userdata)
  384. {
  385. struct SDL_PrivateAudioData *hidden = device->hidden;
  386. if (hidden) {
  387. if (hidden->stream) {
  388. aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream);
  389. if (res != AAUDIO_OK) {
  390. LOGI("SDL Failed AAudioStream_requestStart %d", res);
  391. SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
  392. }
  393. }
  394. }
  395. return false; // keep enumerating.
  396. }
  397. void AAUDIO_ResumeDevices(void)
  398. {
  399. if (ctx.handle) { // AAUDIO driver is used?
  400. (void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL);
  401. }
  402. }
  403. static void AAUDIO_Deinitialize(void)
  404. {
  405. Android_StopAudioHotplug();
  406. LOGI(__func__);
  407. if (ctx.handle) {
  408. SDL_UnloadObject(ctx.handle);
  409. }
  410. SDL_zero(ctx);
  411. LOGI("End AAUDIO %s", SDL_GetError());
  412. }
  413. static bool AAUDIO_Init(SDL_AudioDriverImpl *impl)
  414. {
  415. LOGI(__func__);
  416. /* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release,
  417. * so don't use it until 8.1.
  418. *
  419. * See https://github.com/google/oboe/issues/40 for more information.
  420. */
  421. if (SDL_GetAndroidSDKVersion() < 27) {
  422. return false;
  423. }
  424. SDL_zero(ctx);
  425. ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO);
  426. if (!ctx.handle) {
  427. LOGI("SDL couldn't find " LIB_AAUDIO_SO);
  428. return false;
  429. }
  430. if (!AAUDIO_LoadFunctions(&ctx)) {
  431. SDL_UnloadObject(ctx.handle);
  432. SDL_zero(ctx);
  433. return false;
  434. }
  435. impl->ThreadInit = Android_AudioThreadInit;
  436. impl->Deinitialize = AAUDIO_Deinitialize;
  437. impl->OpenDevice = AAUDIO_OpenDevice;
  438. impl->CloseDevice = AAUDIO_CloseDevice;
  439. impl->WaitDevice = AAUDIO_WaitDevice;
  440. impl->PlayDevice = AAUDIO_PlayDevice;
  441. impl->GetDeviceBuf = AAUDIO_GetDeviceBuf;
  442. impl->WaitRecordingDevice = AAUDIO_WaitDevice;
  443. impl->RecordDevice = AAUDIO_RecordDevice;
  444. impl->HasRecordingSupport = true;
  445. #if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
  446. impl->DetectDevices = Android_StartAudioHotplug;
  447. #else
  448. impl->OnlyHasDefaultPlaybackDevice = true;
  449. impl->OnlyHasDefaultRecordingDevice = true;
  450. #endif
  451. LOGI("SDL AAUDIO_Init OK");
  452. return true;
  453. }
  454. AudioBootStrap AAUDIO_bootstrap = {
  455. "AAudio", "AAudio audio driver", AAUDIO_Init, false
  456. };
  457. #endif // SDL_AUDIO_DRIVER_AAUDIO