summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c
diff options
context:
space:
mode:
author3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
committer3gg <3gg@shellblade.net>2025-12-27 12:03:39 -0800
commit5a079a2d114f96d4847d1ee305d5b7c16eeec50e (patch)
tree8926ab44f168acf787d8e19608857b3af0f82758 /contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c
Initial commit
Diffstat (limited to 'contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c')
-rw-r--r--contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c551
1 files changed, 551 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c b/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c
new file mode 100644
index 0000000..3360bec
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/audio/aaudio/SDL_aaudio.c
@@ -0,0 +1,551 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
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
56typedef struct AAUDIO_Data
57{
58 SDL_SharedObject *handle;
59#define SDL_PROC(ret, func, params) ret (*func) params;
60#include "SDL_aaudiofuncs.h"
61} AAUDIO_Data;
62static AAUDIO_Data ctx;
63
64static bool AAUDIO_LoadFunctions(AAUDIO_Data *data)
65{
66#define SDL_PROC(ret, func, params) \
67 do { \
68 data->func = (ret (*) params)SDL_LoadFunction(data->handle, #func); \
69 if (!data->func) { \
70 return SDL_SetError("Couldn't load AAUDIO function %s: %s", #func, SDL_GetError()); \
71 } \
72 } while (0);
73#include "SDL_aaudiofuncs.h"
74 return true;
75}
76
77
78static void AAUDIO_errorCallback(AAudioStream *stream, void *userData, aaudio_result_t error)
79{
80 LOGI("SDL AAUDIO_errorCallback: %d - %s", error, ctx.AAudio_convertResultToText(error));
81
82 // You MUST NOT close the audio stream from this callback, so we cannot call SDL_AudioDeviceDisconnected here.
83 // Just flag the device so we can kill it in PlayDevice instead.
84 SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
85 SDL_SetAtomicInt(&device->hidden->error_callback_triggered, (int) error); // AAUDIO_OK is zero, so !triggered means no error.
86 SDL_SignalSemaphore(device->hidden->semaphore); // in case we're blocking in WaitDevice.
87}
88
89static aaudio_data_callback_result_t AAUDIO_dataCallback(AAudioStream *stream, void *userData, void *audioData, int32_t numFrames)
90{
91 SDL_AudioDevice *device = (SDL_AudioDevice *) userData;
92 struct SDL_PrivateAudioData *hidden = device->hidden;
93 size_t framesize = SDL_AUDIO_FRAMESIZE(device->spec);
94 size_t callback_bytes = numFrames * framesize;
95 size_t old_buffer_index = hidden->callback_bytes / device->buffer_size;
96
97 if (device->recording) {
98 const Uint8 *input = (const Uint8 *)audioData;
99 size_t available_bytes = hidden->mixbuf_bytes - (hidden->callback_bytes - hidden->processed_bytes);
100 size_t size = SDL_min(available_bytes, callback_bytes);
101 size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes;
102 size_t end = (offset + size) % hidden->mixbuf_bytes;
103 SDL_assert(size <= hidden->mixbuf_bytes);
104
105//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);
106
107 if (offset <= end) {
108 SDL_memcpy(&hidden->mixbuf[offset], input, size);
109 } else {
110 size_t partial = (hidden->mixbuf_bytes - offset);
111 SDL_memcpy(&hidden->mixbuf[offset], &input[0], partial);
112 SDL_memcpy(&hidden->mixbuf[0], &input[partial], end);
113 }
114
115 SDL_MemoryBarrierRelease();
116 hidden->callback_bytes += size;
117
118 if (size < callback_bytes) {
119 LOGI("Audio recording overflow, dropped %zu frames", (callback_bytes - size) / framesize);
120 }
121 } else {
122 Uint8 *output = (Uint8 *)audioData;
123 size_t available_bytes = (hidden->processed_bytes - hidden->callback_bytes);
124 size_t size = SDL_min(available_bytes, callback_bytes);
125 size_t offset = hidden->callback_bytes % hidden->mixbuf_bytes;
126 size_t end = (offset + size) % hidden->mixbuf_bytes;
127 SDL_assert(size <= hidden->mixbuf_bytes);
128
129//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);
130
131 SDL_MemoryBarrierAcquire();
132 if (offset <= end) {
133 SDL_memcpy(output, &hidden->mixbuf[offset], size);
134 } else {
135 size_t partial = (hidden->mixbuf_bytes - offset);
136 SDL_memcpy(&output[0], &hidden->mixbuf[offset], partial);
137 SDL_memcpy(&output[partial], &hidden->mixbuf[0], end);
138 }
139 hidden->callback_bytes += size;
140
141 if (size < callback_bytes) {
142 LOGI("Audio playback underflow, missed %zu frames", (callback_bytes - size) / framesize);
143 SDL_memset(&output[size], device->silence_value, (callback_bytes - size));
144 }
145 }
146
147 size_t new_buffer_index = hidden->callback_bytes / device->buffer_size;
148 while (old_buffer_index < new_buffer_index) {
149 // Trigger audio processing
150 SDL_SignalSemaphore(hidden->semaphore);
151 ++old_buffer_index;
152 }
153
154 return AAUDIO_CALLBACK_RESULT_CONTINUE;
155}
156
157static Uint8 *AAUDIO_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
158{
159 struct SDL_PrivateAudioData *hidden = device->hidden;
160 size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes);
161 return &hidden->mixbuf[offset];
162}
163
164static bool AAUDIO_WaitDevice(SDL_AudioDevice *device)
165{
166 while (!SDL_GetAtomicInt(&device->shutdown)) {
167 // this semaphore won't fire when the app is in the background (AAUDIO_PauseDevices was called).
168 if (SDL_WaitSemaphoreTimeout(device->hidden->semaphore, 100)) {
169 return true; // semaphore was signaled, let's go!
170 }
171 // Still waiting on the semaphore (or the system), check other things then wait again.
172 }
173 return true;
174}
175
176static bool BuildAAudioStream(SDL_AudioDevice *device);
177
178static bool RecoverAAudioDevice(SDL_AudioDevice *device)
179{
180 struct SDL_PrivateAudioData *hidden = device->hidden;
181
182 // attempt to build a new stream, in case there's a new default device.
183 ctx.AAudioStream_requestStop(hidden->stream);
184 ctx.AAudioStream_close(hidden->stream);
185 hidden->stream = NULL;
186
187 SDL_aligned_free(hidden->mixbuf);
188 hidden->mixbuf = NULL;
189
190 SDL_DestroySemaphore(hidden->semaphore);
191 hidden->semaphore = NULL;
192
193 const int prev_sample_frames = device->sample_frames;
194 SDL_AudioSpec prevspec;
195 SDL_copyp(&prevspec, &device->spec);
196
197 if (!BuildAAudioStream(device)) {
198 return false; // oh well, we tried.
199 }
200
201 // 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
202 // so SDL_AudioDeviceFormatChanged can set up all the important state if necessary and then set it back to the new spec.
203 const int new_sample_frames = device->sample_frames;
204 SDL_AudioSpec newspec;
205 SDL_copyp(&newspec, &device->spec);
206
207 device->sample_frames = prev_sample_frames;
208 SDL_copyp(&device->spec, &prevspec);
209 if (!SDL_AudioDeviceFormatChangedAlreadyLocked(device, &newspec, new_sample_frames)) {
210 return false; // ugh
211 }
212 return true;
213}
214
215
216static bool AAUDIO_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
217{
218 struct SDL_PrivateAudioData *hidden = device->hidden;
219
220 // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
221 const aaudio_result_t err = (aaudio_result_t) SDL_GetAtomicInt(&hidden->error_callback_triggered);
222 if (err) {
223 SDL_LogError(SDL_LOG_CATEGORY_AUDIO, "aaudio: Audio device triggered error %d (%s)", (int) err, ctx.AAudio_convertResultToText(err));
224
225 if (!RecoverAAudioDevice(device)) {
226 return false; // oh well, we went down hard.
227 }
228 } else {
229 SDL_MemoryBarrierRelease();
230 hidden->processed_bytes += buflen;
231 }
232 return true;
233}
234
235static int AAUDIO_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
236{
237 struct SDL_PrivateAudioData *hidden = device->hidden;
238
239 // AAUDIO_dataCallback picks up our work and unblocks AAUDIO_WaitDevice. But make sure we didn't fail here.
240 if (SDL_GetAtomicInt(&hidden->error_callback_triggered)) {
241 SDL_SetAtomicInt(&hidden->error_callback_triggered, 0);
242 return -1;
243 }
244
245 SDL_assert(buflen == device->buffer_size); // If this isn't true, we need to change semaphore trigger logic and account for wrapping copies here
246 size_t offset = (hidden->processed_bytes % hidden->mixbuf_bytes);
247 SDL_MemoryBarrierAcquire();
248 SDL_memcpy(buffer, &hidden->mixbuf[offset], buflen);
249 hidden->processed_bytes += buflen;
250 return buflen;
251}
252
253static void AAUDIO_CloseDevice(SDL_AudioDevice *device)
254{
255 struct SDL_PrivateAudioData *hidden = device->hidden;
256 LOGI(__func__);
257
258 if (hidden) {
259 if (hidden->stream) {
260 ctx.AAudioStream_requestStop(hidden->stream);
261 // !!! 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)?
262 // !!! FIXME: also, will this definitely wait for a running data callback to finish, and then stop the callback from firing again?
263 ctx.AAudioStream_close(hidden->stream);
264 }
265
266 if (hidden->semaphore) {
267 SDL_DestroySemaphore(hidden->semaphore);
268 }
269
270 SDL_aligned_free(hidden->mixbuf);
271 SDL_free(hidden);
272 device->hidden = NULL;
273 }
274}
275
276static bool BuildAAudioStream(SDL_AudioDevice *device)
277{
278 struct SDL_PrivateAudioData *hidden = device->hidden;
279 const bool recording = device->recording;
280 aaudio_result_t res;
281
282 SDL_SetAtomicInt(&hidden->error_callback_triggered, 0);
283
284 AAudioStreamBuilder *builder = NULL;
285 res = ctx.AAudio_createStreamBuilder(&builder);
286 if (res != AAUDIO_OK) {
287 LOGI("SDL Failed AAudio_createStreamBuilder %d", res);
288 return SDL_SetError("SDL Failed AAudio_createStreamBuilder %d", res);
289 } else if (!builder) {
290 LOGI("SDL Failed AAudio_createStreamBuilder - builder NULL");
291 return SDL_SetError("SDL Failed AAudio_createStreamBuilder - builder NULL");
292 }
293
294#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
295 const int aaudio_device_id = (int) ((size_t) device->handle);
296 LOGI("Opening device id %d", aaudio_device_id);
297 ctx.AAudioStreamBuilder_setDeviceId(builder, aaudio_device_id);
298#endif
299
300 aaudio_format_t format;
301 if ((device->spec.format == SDL_AUDIO_S32) && (SDL_GetAndroidSDKVersion() >= 31)) {
302 format = AAUDIO_FORMAT_PCM_I32;
303 } else if (device->spec.format == SDL_AUDIO_F32) {
304 format = AAUDIO_FORMAT_PCM_FLOAT;
305 } else {
306 format = AAUDIO_FORMAT_PCM_I16; // sint16 is a safe bet for everything else.
307 }
308 ctx.AAudioStreamBuilder_setFormat(builder, format);
309 ctx.AAudioStreamBuilder_setSampleRate(builder, device->spec.freq);
310 ctx.AAudioStreamBuilder_setChannelCount(builder, device->spec.channels);
311
312 const aaudio_direction_t direction = (recording ? AAUDIO_DIRECTION_INPUT : AAUDIO_DIRECTION_OUTPUT);
313 ctx.AAudioStreamBuilder_setDirection(builder, direction);
314 ctx.AAudioStreamBuilder_setErrorCallback(builder, AAUDIO_errorCallback, device);
315 ctx.AAudioStreamBuilder_setDataCallback(builder, AAUDIO_dataCallback, device);
316 // Some devices have flat sounding audio when low latency mode is enabled, but this is a better experience for most people
317 if (SDL_GetHintBoolean(SDL_HINT_ANDROID_LOW_LATENCY_AUDIO, true)) {
318 SDL_Log("Low latency audio enabled");
319 ctx.AAudioStreamBuilder_setPerformanceMode(builder, AAUDIO_PERFORMANCE_MODE_LOW_LATENCY);
320 } else {
321 SDL_Log("Low latency audio disabled");
322 }
323
324 LOGI("AAudio Try to open %u hz %s %u channels samples %u",
325 device->spec.freq, SDL_GetAudioFormatName(device->spec.format),
326 device->spec.channels, device->sample_frames);
327
328 res = ctx.AAudioStreamBuilder_openStream(builder, &hidden->stream);
329 if (res != AAUDIO_OK) {
330 LOGI("SDL Failed AAudioStreamBuilder_openStream %d", res);
331 ctx.AAudioStreamBuilder_delete(builder);
332 return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
333 }
334 ctx.AAudioStreamBuilder_delete(builder);
335
336 device->sample_frames = (int)ctx.AAudioStream_getFramesPerDataCallback(hidden->stream);
337 if (device->sample_frames == AAUDIO_UNSPECIFIED) {
338 // We'll get variable frames in the callback, make sure we have at least half a buffer available
339 device->sample_frames = (int)ctx.AAudioStream_getBufferCapacityInFrames(hidden->stream) / 2;
340 }
341
342 device->spec.freq = ctx.AAudioStream_getSampleRate(hidden->stream);
343 device->spec.channels = ctx.AAudioStream_getChannelCount(hidden->stream);
344
345 format = ctx.AAudioStream_getFormat(hidden->stream);
346 if (format == AAUDIO_FORMAT_PCM_I16) {
347 device->spec.format = SDL_AUDIO_S16;
348 } else if (format == AAUDIO_FORMAT_PCM_I32) {
349 device->spec.format = SDL_AUDIO_S32;
350 } else if (format == AAUDIO_FORMAT_PCM_FLOAT) {
351 device->spec.format = SDL_AUDIO_F32;
352 } else {
353 return SDL_SetError("Got unexpected audio format %d from AAudioStream_getFormat", (int) format);
354 }
355
356 SDL_UpdatedAudioDeviceFormat(device);
357
358 // Allocate a triple buffered mixing buffer
359 // Two buffers can be in the process of being filled while the third is being read
360 hidden->num_buffers = 3;
361 hidden->mixbuf_bytes = (hidden->num_buffers * device->buffer_size);
362 hidden->mixbuf = (Uint8 *)SDL_aligned_alloc(SDL_GetSIMDAlignment(), hidden->mixbuf_bytes);
363 if (!hidden->mixbuf) {
364 return false;
365 }
366 hidden->processed_bytes = 0;
367 hidden->callback_bytes = 0;
368
369 hidden->semaphore = SDL_CreateSemaphore(recording ? 0 : hidden->num_buffers);
370 if (!hidden->semaphore) {
371 LOGI("SDL Failed SDL_CreateSemaphore %s recording:%d", SDL_GetError(), recording);
372 return false;
373 }
374
375 LOGI("AAudio Actually opened %u hz %s %u channels samples %u, buffers %d",
376 device->spec.freq, SDL_GetAudioFormatName(device->spec.format),
377 device->spec.channels, device->sample_frames, hidden->num_buffers);
378
379 res = ctx.AAudioStream_requestStart(hidden->stream);
380 if (res != AAUDIO_OK) {
381 LOGI("SDL Failed AAudioStream_requestStart %d recording:%d", res, recording);
382 return SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
383 }
384
385 LOGI("SDL AAudioStream_requestStart OK");
386
387 return true;
388}
389
390// !!! FIXME: make this non-blocking!
391static void SDLCALL RequestAndroidPermissionBlockingCallback(void *userdata, const char *permission, bool granted)
392{
393 SDL_SetAtomicInt((SDL_AtomicInt *) userdata, granted ? 1 : -1);
394}
395
396static bool AAUDIO_OpenDevice(SDL_AudioDevice *device)
397{
398#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
399 SDL_assert(device->handle); // AAUDIO_UNSPECIFIED is zero, so legit devices should all be non-zero.
400#endif
401
402 LOGI(__func__);
403
404 if (device->recording) {
405 // !!! FIXME: make this non-blocking!
406 SDL_AtomicInt permission_response;
407 SDL_SetAtomicInt(&permission_response, 0);
408 if (!SDL_RequestAndroidPermission("android.permission.RECORD_AUDIO", RequestAndroidPermissionBlockingCallback, &permission_response)) {
409 return false;
410 }
411
412 while (SDL_GetAtomicInt(&permission_response) == 0) {
413 SDL_Delay(10);
414 }
415
416 if (SDL_GetAtomicInt(&permission_response) < 0) {
417 LOGI("This app doesn't have RECORD_AUDIO permission");
418 return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
419 }
420 }
421
422 device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
423 if (!device->hidden) {
424 return false;
425 }
426
427 return BuildAAudioStream(device);
428}
429
430static bool PauseOneDevice(SDL_AudioDevice *device, void *userdata)
431{
432 struct SDL_PrivateAudioData *hidden = (struct SDL_PrivateAudioData *)device->hidden;
433 if (hidden) {
434 if (hidden->stream) {
435 aaudio_result_t res;
436
437 if (device->recording) {
438 // Pause() isn't implemented for recording, use Stop()
439 res = ctx.AAudioStream_requestStop(hidden->stream);
440 } else {
441 res = ctx.AAudioStream_requestPause(hidden->stream);
442 }
443
444 if (res != AAUDIO_OK) {
445 LOGI("SDL Failed AAudioStream_requestPause %d", res);
446 SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
447 }
448 }
449 }
450 return false; // keep enumerating.
451}
452
453// Pause (block) all non already paused audio devices by taking their mixer lock
454void AAUDIO_PauseDevices(void)
455{
456 if (ctx.handle) { // AAUDIO driver is used?
457 (void) SDL_FindPhysicalAudioDeviceByCallback(PauseOneDevice, NULL);
458 }
459}
460
461// Resume (unblock) all non already paused audio devices by releasing their mixer lock
462static bool ResumeOneDevice(SDL_AudioDevice *device, void *userdata)
463{
464 struct SDL_PrivateAudioData *hidden = device->hidden;
465 if (hidden) {
466 if (hidden->stream) {
467 aaudio_result_t res = ctx.AAudioStream_requestStart(hidden->stream);
468 if (res != AAUDIO_OK) {
469 LOGI("SDL Failed AAudioStream_requestStart %d", res);
470 SDL_SetError("%s : %s", __func__, ctx.AAudio_convertResultToText(res));
471 }
472 }
473 }
474 return false; // keep enumerating.
475}
476
477void AAUDIO_ResumeDevices(void)
478{
479 if (ctx.handle) { // AAUDIO driver is used?
480 (void) SDL_FindPhysicalAudioDeviceByCallback(ResumeOneDevice, NULL);
481 }
482}
483
484static void AAUDIO_Deinitialize(void)
485{
486 Android_StopAudioHotplug();
487
488 LOGI(__func__);
489 if (ctx.handle) {
490 SDL_UnloadObject(ctx.handle);
491 }
492 SDL_zero(ctx);
493 LOGI("End AAUDIO %s", SDL_GetError());
494}
495
496
497static bool AAUDIO_Init(SDL_AudioDriverImpl *impl)
498{
499 LOGI(__func__);
500
501 /* AAudio was introduced in Android 8.0, but has reference counting crash issues in that release,
502 * so don't use it until 8.1.
503 *
504 * See https://github.com/google/oboe/issues/40 for more information.
505 */
506 if (SDL_GetAndroidSDKVersion() < 27) {
507 return false;
508 }
509
510 SDL_zero(ctx);
511
512 ctx.handle = SDL_LoadObject(LIB_AAUDIO_SO);
513 if (!ctx.handle) {
514 LOGI("SDL couldn't find " LIB_AAUDIO_SO);
515 return false;
516 }
517
518 if (!AAUDIO_LoadFunctions(&ctx)) {
519 SDL_UnloadObject(ctx.handle);
520 SDL_zero(ctx);
521 return false;
522 }
523
524 impl->ThreadInit = Android_AudioThreadInit;
525 impl->Deinitialize = AAUDIO_Deinitialize;
526 impl->OpenDevice = AAUDIO_OpenDevice;
527 impl->CloseDevice = AAUDIO_CloseDevice;
528 impl->WaitDevice = AAUDIO_WaitDevice;
529 impl->PlayDevice = AAUDIO_PlayDevice;
530 impl->GetDeviceBuf = AAUDIO_GetDeviceBuf;
531 impl->WaitRecordingDevice = AAUDIO_WaitDevice;
532 impl->RecordDevice = AAUDIO_RecordDevice;
533
534 impl->HasRecordingSupport = true;
535
536#if ALLOW_MULTIPLE_ANDROID_AUDIO_DEVICES
537 impl->DetectDevices = Android_StartAudioHotplug;
538#else
539 impl->OnlyHasDefaultPlaybackDevice = true;
540 impl->OnlyHasDefaultRecordingDevice = true;
541#endif
542
543 LOGI("SDL AAUDIO_Init OK");
544 return true;
545}
546
547AudioBootStrap AAUDIO_bootstrap = {
548 "AAudio", "AAudio audio driver", AAUDIO_Init, false, false
549};
550
551#endif // SDL_AUDIO_DRIVER_AAUDIO