summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/audio/openslES
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/audio/openslES')
-rw-r--r--contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.c807
-rw-r--r--contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.h38
2 files changed, 845 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.c b/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.c
new file mode 100644
index 0000000..4d5b3bd
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.c
@@ -0,0 +1,807 @@
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_OPENSLES
24
25// For more discussion of low latency audio on Android, see this:
26// https://googlesamples.github.io/android-audio-high-performance/guides/opensl_es.html
27
28#include "../SDL_sysaudio.h"
29#include "SDL_openslES.h"
30
31#include "../../core/android/SDL_android.h"
32#include <SLES/OpenSLES.h>
33#include <SLES/OpenSLES_Android.h>
34#include <android/log.h>
35
36
37#define NUM_BUFFERS 2 // -- Don't lower this!
38
39struct SDL_PrivateAudioData
40{
41 Uint8 *mixbuff;
42 int next_buffer;
43 Uint8 *pmixbuff[NUM_BUFFERS];
44 SDL_Semaphore *playsem;
45};
46
47#if 0
48#define LOG_TAG "SDL_openslES"
49#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
50#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
51//#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
52#define LOGV(...)
53#else
54#define LOGE(...)
55#define LOGI(...)
56#define LOGV(...)
57#endif
58
59/*
60#define SL_SPEAKER_FRONT_LEFT ((SLuint32) 0x00000001)
61#define SL_SPEAKER_FRONT_RIGHT ((SLuint32) 0x00000002)
62#define SL_SPEAKER_FRONT_CENTER ((SLuint32) 0x00000004)
63#define SL_SPEAKER_LOW_FREQUENCY ((SLuint32) 0x00000008)
64#define SL_SPEAKER_BACK_LEFT ((SLuint32) 0x00000010)
65#define SL_SPEAKER_BACK_RIGHT ((SLuint32) 0x00000020)
66#define SL_SPEAKER_FRONT_LEFT_OF_CENTER ((SLuint32) 0x00000040)
67#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080)
68#define SL_SPEAKER_BACK_CENTER ((SLuint32) 0x00000100)
69#define SL_SPEAKER_SIDE_LEFT ((SLuint32) 0x00000200)
70#define SL_SPEAKER_SIDE_RIGHT ((SLuint32) 0x00000400)
71#define SL_SPEAKER_TOP_CENTER ((SLuint32) 0x00000800)
72#define SL_SPEAKER_TOP_FRONT_LEFT ((SLuint32) 0x00001000)
73#define SL_SPEAKER_TOP_FRONT_CENTER ((SLuint32) 0x00002000)
74#define SL_SPEAKER_TOP_FRONT_RIGHT ((SLuint32) 0x00004000)
75#define SL_SPEAKER_TOP_BACK_LEFT ((SLuint32) 0x00008000)
76#define SL_SPEAKER_TOP_BACK_CENTER ((SLuint32) 0x00010000)
77#define SL_SPEAKER_TOP_BACK_RIGHT ((SLuint32) 0x00020000)
78*/
79#define SL_ANDROID_SPEAKER_STEREO (SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT)
80#define SL_ANDROID_SPEAKER_QUAD (SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT)
81#define SL_ANDROID_SPEAKER_5DOT1 (SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER | SL_SPEAKER_LOW_FREQUENCY)
82#define SL_ANDROID_SPEAKER_7DOT1 (SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_SIDE_LEFT | SL_SPEAKER_SIDE_RIGHT)
83
84// engine interfaces
85static SLObjectItf engineObject = NULL;
86static SLEngineItf engineEngine = NULL;
87
88// output mix interfaces
89static SLObjectItf outputMixObject = NULL;
90
91// buffer queue player interfaces
92static SLObjectItf bqPlayerObject = NULL;
93static SLPlayItf bqPlayerPlay = NULL;
94static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
95#if 0
96static SLVolumeItf bqPlayerVolume;
97#endif
98
99// recorder interfaces
100static SLObjectItf recorderObject = NULL;
101static SLRecordItf recorderRecord = NULL;
102static SLAndroidSimpleBufferQueueItf recorderBufferQueue = NULL;
103
104#if 0
105static const char *sldevaudiorecorderstr = "SLES Audio Recorder";
106static const char *sldevaudioplayerstr = "SLES Audio Player";
107
108#define SLES_DEV_AUDIO_RECORDER sldevaudiorecorderstr
109#define SLES_DEV_AUDIO_PLAYER sldevaudioplayerstr
110static void OPENSLES_DetectDevices( int recording )
111{
112 LOGI( "openSLES_DetectDevices()" );
113 if ( recording )
114 addfn( SLES_DEV_AUDIO_RECORDER );
115 else
116 addfn( SLES_DEV_AUDIO_PLAYER );
117}
118#endif
119
120static void OPENSLES_DestroyEngine(void)
121{
122 LOGI("OPENSLES_DestroyEngine()");
123
124 // destroy output mix object, and invalidate all associated interfaces
125 if (outputMixObject != NULL) {
126 (*outputMixObject)->Destroy(outputMixObject);
127 outputMixObject = NULL;
128 }
129
130 // destroy engine object, and invalidate all associated interfaces
131 if (engineObject != NULL) {
132 (*engineObject)->Destroy(engineObject);
133 engineObject = NULL;
134 engineEngine = NULL;
135 }
136}
137
138static bool OPENSLES_CreateEngine(void)
139{
140 const SLInterfaceID ids[1] = { SL_IID_VOLUME };
141 const SLboolean req[1] = { SL_BOOLEAN_FALSE };
142 SLresult result;
143
144 LOGI("openSLES_CreateEngine()");
145
146 // create engine
147 result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
148 if (SL_RESULT_SUCCESS != result) {
149 LOGE("slCreateEngine failed: %d", result);
150 goto error;
151 }
152 LOGI("slCreateEngine OK");
153
154 // realize the engine
155 result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
156 if (SL_RESULT_SUCCESS != result) {
157 LOGE("RealizeEngine failed: %d", result);
158 goto error;
159 }
160 LOGI("RealizeEngine OK");
161
162 // get the engine interface, which is needed in order to create other objects
163 result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
164 if (SL_RESULT_SUCCESS != result) {
165 LOGE("EngineGetInterface failed: %d", result);
166 goto error;
167 }
168 LOGI("EngineGetInterface OK");
169
170 // create output mix
171 result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
172 if (SL_RESULT_SUCCESS != result) {
173 LOGE("CreateOutputMix failed: %d", result);
174 goto error;
175 }
176 LOGI("CreateOutputMix OK");
177
178 // realize the output mix
179 result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
180 if (SL_RESULT_SUCCESS != result) {
181 LOGE("RealizeOutputMix failed: %d", result);
182 goto error;
183 }
184 return true;
185
186error:
187 OPENSLES_DestroyEngine();
188 return false;
189}
190
191// this callback handler is called every time a buffer finishes recording
192static void bqRecorderCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
193{
194 struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
195
196 LOGV("SLES: Recording Callback");
197 SDL_SignalSemaphore(audiodata->playsem);
198}
199
200static void OPENSLES_DestroyPCMRecorder(SDL_AudioDevice *device)
201{
202 struct SDL_PrivateAudioData *audiodata = device->hidden;
203 SLresult result;
204
205 // stop recording
206 if (recorderRecord != NULL) {
207 result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
208 if (SL_RESULT_SUCCESS != result) {
209 LOGE("SetRecordState stopped: %d", result);
210 }
211 }
212
213 // destroy audio recorder object, and invalidate all associated interfaces
214 if (recorderObject != NULL) {
215 (*recorderObject)->Destroy(recorderObject);
216 recorderObject = NULL;
217 recorderRecord = NULL;
218 recorderBufferQueue = NULL;
219 }
220
221 if (audiodata->playsem) {
222 SDL_DestroySemaphore(audiodata->playsem);
223 audiodata->playsem = NULL;
224 }
225
226 if (audiodata->mixbuff) {
227 SDL_free(audiodata->mixbuff);
228 }
229}
230
231// !!! FIXME: make this non-blocking!
232static void SDLCALL RequestAndroidPermissionBlockingCallback(void *userdata, const char *permission, bool granted)
233{
234 SDL_SetAtomicInt((SDL_AtomicInt *) userdata, granted ? 1 : -1);
235}
236
237static bool OPENSLES_CreatePCMRecorder(SDL_AudioDevice *device)
238{
239 struct SDL_PrivateAudioData *audiodata = device->hidden;
240 SLDataFormat_PCM format_pcm;
241 SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
242 SLDataSink audioSnk;
243 SLDataLocator_IODevice loc_dev;
244 SLDataSource audioSrc;
245 const SLInterfaceID ids[1] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
246 const SLboolean req[1] = { SL_BOOLEAN_TRUE };
247 SLresult result;
248 int i;
249
250 // !!! FIXME: make this non-blocking!
251 {
252 SDL_AtomicInt permission_response;
253 SDL_SetAtomicInt(&permission_response, 0);
254 if (!SDL_RequestAndroidPermission("android.permission.RECORD_AUDIO", RequestAndroidPermissionBlockingCallback, &permission_response)) {
255 return false;
256 }
257
258 while (SDL_GetAtomicInt(&permission_response) == 0) {
259 SDL_Delay(10);
260 }
261
262 if (SDL_GetAtomicInt(&permission_response) < 0) {
263 LOGE("This app doesn't have RECORD_AUDIO permission");
264 return SDL_SetError("This app doesn't have RECORD_AUDIO permission");
265 }
266 }
267
268 // Just go with signed 16-bit audio as it's the most compatible
269 device->spec.format = SDL_AUDIO_S16;
270 device->spec.channels = 1;
271 //device->spec.freq = SL_SAMPLINGRATE_16 / 1000;*/
272
273 // Update the fragment size as size in bytes
274 SDL_UpdatedAudioDeviceFormat(device);
275
276 LOGI("Try to open %u hz %u bit %u channels %s samples %u",
277 device->spec.freq, SDL_AUDIO_BITSIZE(device->spec.format),
278 device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
279
280 // configure audio source
281 loc_dev.locatorType = SL_DATALOCATOR_IODEVICE;
282 loc_dev.deviceType = SL_IODEVICE_AUDIOINPUT;
283 loc_dev.deviceID = SL_DEFAULTDEVICEID_AUDIOINPUT;
284 loc_dev.device = NULL;
285 audioSrc.pLocator = &loc_dev;
286 audioSrc.pFormat = NULL;
287
288 // configure audio sink
289 loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
290 loc_bufq.numBuffers = NUM_BUFFERS;
291
292 format_pcm.formatType = SL_DATAFORMAT_PCM;
293 format_pcm.numChannels = device->spec.channels;
294 format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
295 format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
296 format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
297 format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
298 format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
299
300 audioSnk.pLocator = &loc_bufq;
301 audioSnk.pFormat = &format_pcm;
302
303 // create audio recorder
304 // (requires the RECORD_AUDIO permission)
305 result = (*engineEngine)->CreateAudioRecorder(engineEngine, &recorderObject, &audioSrc, &audioSnk, 1, ids, req);
306 if (SL_RESULT_SUCCESS != result) {
307 LOGE("CreateAudioRecorder failed: %d", result);
308 goto failed;
309 }
310
311 // realize the recorder
312 result = (*recorderObject)->Realize(recorderObject, SL_BOOLEAN_FALSE);
313 if (SL_RESULT_SUCCESS != result) {
314 LOGE("RealizeAudioPlayer failed: %d", result);
315 goto failed;
316 }
317
318 // get the record interface
319 result = (*recorderObject)->GetInterface(recorderObject, SL_IID_RECORD, &recorderRecord);
320 if (SL_RESULT_SUCCESS != result) {
321 LOGE("SL_IID_RECORD interface get failed: %d", result);
322 goto failed;
323 }
324
325 // get the buffer queue interface
326 result = (*recorderObject)->GetInterface(recorderObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &recorderBufferQueue);
327 if (SL_RESULT_SUCCESS != result) {
328 LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
329 goto failed;
330 }
331
332 // register callback on the buffer queue
333 // context is '(SDL_PrivateAudioData *)device->hidden'
334 result = (*recorderBufferQueue)->RegisterCallback(recorderBufferQueue, bqRecorderCallback, device->hidden);
335 if (SL_RESULT_SUCCESS != result) {
336 LOGE("RegisterCallback failed: %d", result);
337 goto failed;
338 }
339
340 // Create the audio buffer semaphore
341 audiodata->playsem = SDL_CreateSemaphore(0);
342 if (!audiodata->playsem) {
343 LOGE("cannot create Semaphore!");
344 goto failed;
345 }
346
347 // Create the sound buffers
348 audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
349 if (!audiodata->mixbuff) {
350 LOGE("mixbuffer allocate - out of memory");
351 goto failed;
352 }
353
354 for (i = 0; i < NUM_BUFFERS; i++) {
355 audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size;
356 }
357
358 // in case already recording, stop recording and clear buffer queue
359 result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_STOPPED);
360 if (SL_RESULT_SUCCESS != result) {
361 LOGE("Record set state failed: %d", result);
362 goto failed;
363 }
364
365 // enqueue empty buffers to be filled by the recorder
366 for (i = 0; i < NUM_BUFFERS; i++) {
367 result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[i], device->buffer_size);
368 if (SL_RESULT_SUCCESS != result) {
369 LOGE("Record enqueue buffers failed: %d", result);
370 goto failed;
371 }
372 }
373
374 // start recording
375 result = (*recorderRecord)->SetRecordState(recorderRecord, SL_RECORDSTATE_RECORDING);
376 if (SL_RESULT_SUCCESS != result) {
377 LOGE("Record set state failed: %d", result);
378 goto failed;
379 }
380
381 return true;
382
383failed:
384 return SDL_SetError("Open device failed!");
385}
386
387// this callback handler is called every time a buffer finishes playing
388static void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
389{
390 struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *)context;
391
392 LOGV("SLES: Playback Callback");
393 SDL_SignalSemaphore(audiodata->playsem);
394}
395
396static void OPENSLES_DestroyPCMPlayer(SDL_AudioDevice *device)
397{
398 struct SDL_PrivateAudioData *audiodata = device->hidden;
399
400 // set the player's state to 'stopped'
401 if (bqPlayerPlay != NULL) {
402 const SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
403 if (SL_RESULT_SUCCESS != result) {
404 LOGE("SetPlayState stopped failed: %d", result);
405 }
406 }
407
408 // destroy buffer queue audio player object, and invalidate all associated interfaces
409 if (bqPlayerObject != NULL) {
410 (*bqPlayerObject)->Destroy(bqPlayerObject);
411
412 bqPlayerObject = NULL;
413 bqPlayerPlay = NULL;
414 bqPlayerBufferQueue = NULL;
415 }
416
417 if (audiodata->playsem) {
418 SDL_DestroySemaphore(audiodata->playsem);
419 audiodata->playsem = NULL;
420 }
421
422 if (audiodata->mixbuff) {
423 SDL_free(audiodata->mixbuff);
424 }
425}
426
427static bool OPENSLES_CreatePCMPlayer(SDL_AudioDevice *device)
428{
429 /* If we want to add floating point audio support (requires API level 21)
430 it can be done as described here:
431 https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point
432 */
433 if (SDL_GetAndroidSDKVersion() >= 21) {
434 const SDL_AudioFormat *closefmts = SDL_ClosestAudioFormats(device->spec.format);
435 SDL_AudioFormat test_format;
436 while ((test_format = *(closefmts++)) != 0) {
437 if (SDL_AUDIO_ISSIGNED(test_format)) {
438 break;
439 }
440 }
441
442 if (!test_format) {
443 // Didn't find a compatible format :
444 LOGI("No compatible audio format, using signed 16-bit audio");
445 test_format = SDL_AUDIO_S16;
446 }
447 device->spec.format = test_format;
448 } else {
449 // Just go with signed 16-bit audio as it's the most compatible
450 device->spec.format = SDL_AUDIO_S16;
451 }
452
453 // Update the fragment size as size in bytes
454 SDL_UpdatedAudioDeviceFormat(device);
455
456 LOGI("Try to open %u hz %s %u bit %u channels %s samples %u",
457 device->spec.freq, SDL_AUDIO_ISFLOAT(device->spec.format) ? "float" : "pcm", SDL_AUDIO_BITSIZE(device->spec.format),
458 device->spec.channels, (device->spec.format & 0x1000) ? "BE" : "LE", device->sample_frames);
459
460 // configure audio source
461 SLDataLocator_AndroidSimpleBufferQueue loc_bufq;
462 loc_bufq.locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE;
463 loc_bufq.numBuffers = NUM_BUFFERS;
464
465 SLDataFormat_PCM format_pcm;
466 format_pcm.formatType = SL_DATAFORMAT_PCM;
467 format_pcm.numChannels = device->spec.channels;
468 format_pcm.samplesPerSec = device->spec.freq * 1000; // / kilo Hz to milli Hz
469 format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(device->spec.format);
470 format_pcm.containerSize = SDL_AUDIO_BITSIZE(device->spec.format);
471
472 if (SDL_AUDIO_ISBIGENDIAN(device->spec.format)) {
473 format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
474 } else {
475 format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
476 }
477
478 switch (device->spec.channels) {
479 case 1:
480 format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT;
481 break;
482 case 2:
483 format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO;
484 break;
485 case 3:
486 format_pcm.channelMask = SL_ANDROID_SPEAKER_STEREO | SL_SPEAKER_FRONT_CENTER;
487 break;
488 case 4:
489 format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD;
490 break;
491 case 5:
492 format_pcm.channelMask = SL_ANDROID_SPEAKER_QUAD | SL_SPEAKER_FRONT_CENTER;
493 break;
494 case 6:
495 format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1;
496 break;
497 case 7:
498 format_pcm.channelMask = SL_ANDROID_SPEAKER_5DOT1 | SL_SPEAKER_BACK_CENTER;
499 break;
500 case 8:
501 format_pcm.channelMask = SL_ANDROID_SPEAKER_7DOT1;
502 break;
503 default:
504 // Unknown number of channels, fall back to stereo
505 device->spec.channels = 2;
506 format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
507 break;
508 }
509
510 SLDataSink audioSnk;
511 SLDataSource audioSrc;
512 audioSrc.pFormat = (void *)&format_pcm;
513
514 SLAndroidDataFormat_PCM_EX format_pcm_ex;
515 if (SDL_AUDIO_ISFLOAT(device->spec.format)) {
516 // Copy all setup into PCM EX structure
517 format_pcm_ex.formatType = SL_ANDROID_DATAFORMAT_PCM_EX;
518 format_pcm_ex.endianness = format_pcm.endianness;
519 format_pcm_ex.channelMask = format_pcm.channelMask;
520 format_pcm_ex.numChannels = format_pcm.numChannels;
521 format_pcm_ex.sampleRate = format_pcm.samplesPerSec;
522 format_pcm_ex.bitsPerSample = format_pcm.bitsPerSample;
523 format_pcm_ex.containerSize = format_pcm.containerSize;
524 format_pcm_ex.representation = SL_ANDROID_PCM_REPRESENTATION_FLOAT;
525 audioSrc.pFormat = (void *)&format_pcm_ex;
526 }
527
528 audioSrc.pLocator = &loc_bufq;
529
530 // configure audio sink
531 SLDataLocator_OutputMix loc_outmix;
532 loc_outmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
533 loc_outmix.outputMix = outputMixObject;
534 audioSnk.pLocator = &loc_outmix;
535 audioSnk.pFormat = NULL;
536
537 // create audio player
538 const SLInterfaceID ids[2] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE, SL_IID_VOLUME };
539 const SLboolean req[2] = { SL_BOOLEAN_TRUE, SL_BOOLEAN_FALSE };
540 SLresult result;
541 result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
542 if (SL_RESULT_SUCCESS != result) {
543 LOGE("CreateAudioPlayer failed: %d", result);
544 goto failed;
545 }
546
547 // realize the player
548 result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
549 if (SL_RESULT_SUCCESS != result) {
550 LOGE("RealizeAudioPlayer failed: %d", result);
551 goto failed;
552 }
553
554 // get the play interface
555 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
556 if (SL_RESULT_SUCCESS != result) {
557 LOGE("SL_IID_PLAY interface get failed: %d", result);
558 goto failed;
559 }
560
561 // get the buffer queue interface
562 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
563 if (SL_RESULT_SUCCESS != result) {
564 LOGE("SL_IID_BUFFERQUEUE interface get failed: %d", result);
565 goto failed;
566 }
567
568 // register callback on the buffer queue
569 // context is '(SDL_PrivateAudioData *)device->hidden'
570 result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, device->hidden);
571 if (SL_RESULT_SUCCESS != result) {
572 LOGE("RegisterCallback failed: %d", result);
573 goto failed;
574 }
575
576#if 0
577 // get the volume interface
578 result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
579 if (SL_RESULT_SUCCESS != result) {
580 LOGE("SL_IID_VOLUME interface get failed: %d", result);
581 // goto failed;
582 }
583#endif
584
585 struct SDL_PrivateAudioData *audiodata = device->hidden;
586
587 // Create the audio buffer semaphore
588 audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1);
589 if (!audiodata->playsem) {
590 LOGE("cannot create Semaphore!");
591 goto failed;
592 }
593
594 // Create the sound buffers
595 audiodata->mixbuff = (Uint8 *)SDL_malloc(NUM_BUFFERS * device->buffer_size);
596 if (!audiodata->mixbuff) {
597 LOGE("mixbuffer allocate - out of memory");
598 goto failed;
599 }
600
601 for (int i = 0; i < NUM_BUFFERS; i++) {
602 audiodata->pmixbuff[i] = audiodata->mixbuff + i * device->buffer_size;
603 }
604
605 // set the player's state to playing
606 result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
607 if (SL_RESULT_SUCCESS != result) {
608 LOGE("Play set state failed: %d", result);
609 goto failed;
610 }
611
612 return true;
613
614failed:
615 return false;
616}
617
618static bool OPENSLES_OpenDevice(SDL_AudioDevice *device)
619{
620 device->hidden = (struct SDL_PrivateAudioData *)SDL_calloc(1, sizeof(*device->hidden));
621 if (!device->hidden) {
622 return false;
623 }
624
625 if (device->recording) {
626 LOGI("OPENSLES_OpenDevice() for recording");
627 return OPENSLES_CreatePCMRecorder(device);
628 } else {
629 bool ret;
630 LOGI("OPENSLES_OpenDevice() for playback");
631 ret = OPENSLES_CreatePCMPlayer(device);
632 if (!ret) {
633 // Another attempt to open the device with a lower frequency
634 if (device->spec.freq > 48000) {
635 OPENSLES_DestroyPCMPlayer(device);
636 device->spec.freq = 48000;
637 ret = OPENSLES_CreatePCMPlayer(device);
638 }
639 }
640
641 if (!ret) {
642 return SDL_SetError("Open device failed!");
643 }
644 }
645
646 return true;
647}
648
649static bool OPENSLES_WaitDevice(SDL_AudioDevice *device)
650{
651 struct SDL_PrivateAudioData *audiodata = device->hidden;
652
653 LOGV("OPENSLES_WaitDevice()");
654
655 while (!SDL_GetAtomicInt(&device->shutdown)) {
656 // this semaphore won't fire when the app is in the background (OPENSLES_PauseDevices was called).
657 if (SDL_WaitSemaphoreTimeout(audiodata->playsem, 100)) {
658 return true; // semaphore was signaled, let's go!
659 }
660 // Still waiting on the semaphore (or the system), check other things then wait again.
661 }
662 return true;
663}
664
665static bool OPENSLES_PlayDevice(SDL_AudioDevice *device, const Uint8 *buffer, int buflen)
666{
667 struct SDL_PrivateAudioData *audiodata = device->hidden;
668
669 LOGV("======OPENSLES_PlayDevice()======");
670
671 // Queue it up
672 const SLresult result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, buffer, buflen);
673
674 audiodata->next_buffer++;
675 if (audiodata->next_buffer >= NUM_BUFFERS) {
676 audiodata->next_buffer = 0;
677 }
678
679 // If Enqueue fails, callback won't be called.
680 // Post the semaphore, not to run out of buffer
681 if (SL_RESULT_SUCCESS != result) {
682 SDL_SignalSemaphore(audiodata->playsem);
683 }
684
685 return true;
686}
687
688/// n playn sem
689// getbuf 0 - 1
690// fill buff 0 - 1
691// play 0 - 0 1
692// wait 1 0 0
693// getbuf 1 0 0
694// fill buff 1 0 0
695// play 0 0 0
696// wait
697//
698// okay..
699
700static Uint8 *OPENSLES_GetDeviceBuf(SDL_AudioDevice *device, int *bufsize)
701{
702 struct SDL_PrivateAudioData *audiodata = device->hidden;
703
704 LOGV("OPENSLES_GetDeviceBuf()");
705 return audiodata->pmixbuff[audiodata->next_buffer];
706}
707
708static int OPENSLES_RecordDevice(SDL_AudioDevice *device, void *buffer, int buflen)
709{
710 struct SDL_PrivateAudioData *audiodata = device->hidden;
711
712 // Copy it to the output buffer
713 SDL_assert(buflen == device->buffer_size);
714 SDL_memcpy(buffer, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
715
716 // Re-enqueue the buffer
717 const SLresult result = (*recorderBufferQueue)->Enqueue(recorderBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], device->buffer_size);
718 if (SL_RESULT_SUCCESS != result) {
719 LOGE("Record enqueue buffers failed: %d", result);
720 return -1;
721 }
722
723 audiodata->next_buffer++;
724 if (audiodata->next_buffer >= NUM_BUFFERS) {
725 audiodata->next_buffer = 0;
726 }
727
728 return device->buffer_size;
729}
730
731static void OPENSLES_CloseDevice(SDL_AudioDevice *device)
732{
733 // struct SDL_PrivateAudioData *audiodata = device->hidden;
734 if (device->hidden) {
735 if (device->recording) {
736 LOGI("OPENSLES_CloseDevice() for recording");
737 OPENSLES_DestroyPCMRecorder(device);
738 } else {
739 LOGI("OPENSLES_CloseDevice() for playing");
740 OPENSLES_DestroyPCMPlayer(device);
741 }
742
743 SDL_free(device->hidden);
744 device->hidden = NULL;
745 }
746}
747
748static bool OPENSLES_Init(SDL_AudioDriverImpl *impl)
749{
750 LOGI("OPENSLES_Init() called");
751
752 if (!OPENSLES_CreateEngine()) {
753 return false;
754 }
755
756 LOGI("OPENSLES_Init() - set pointers");
757
758 // Set the function pointers
759 // impl->DetectDevices = OPENSLES_DetectDevices;
760 impl->ThreadInit = Android_AudioThreadInit;
761 impl->OpenDevice = OPENSLES_OpenDevice;
762 impl->WaitDevice = OPENSLES_WaitDevice;
763 impl->PlayDevice = OPENSLES_PlayDevice;
764 impl->GetDeviceBuf = OPENSLES_GetDeviceBuf;
765 impl->WaitRecordingDevice = OPENSLES_WaitDevice;
766 impl->RecordDevice = OPENSLES_RecordDevice;
767 impl->CloseDevice = OPENSLES_CloseDevice;
768 impl->Deinitialize = OPENSLES_DestroyEngine;
769
770 // and the capabilities
771 impl->HasRecordingSupport = true;
772 impl->OnlyHasDefaultPlaybackDevice = true;
773 impl->OnlyHasDefaultRecordingDevice = true;
774
775 LOGI("OPENSLES_Init() - success");
776
777 // this audio target is available.
778 return true;
779}
780
781AudioBootStrap OPENSLES_bootstrap = {
782 "openslES", "OpenSL ES audio driver", OPENSLES_Init, false, false
783};
784
785void OPENSLES_ResumeDevices(void)
786{
787 if (bqPlayerPlay != NULL) {
788 // set the player's state to 'playing'
789 SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
790 if (SL_RESULT_SUCCESS != result) {
791 LOGE("OPENSLES_ResumeDevices failed: %d", result);
792 }
793 }
794}
795
796void OPENSLES_PauseDevices(void)
797{
798 if (bqPlayerPlay != NULL) {
799 // set the player's state to 'paused'
800 SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
801 if (SL_RESULT_SUCCESS != result) {
802 LOGE("OPENSLES_PauseDevices failed: %d", result);
803 }
804 }
805}
806
807#endif // SDL_AUDIO_DRIVER_OPENSLES
diff --git a/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.h b/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.h
new file mode 100644
index 0000000..0ae2664
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/audio/openslES/SDL_openslES.h
@@ -0,0 +1,38 @@
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#ifndef SDL_openslesaudio_h_
24#define SDL_openslesaudio_h_
25
26#ifdef SDL_AUDIO_DRIVER_OPENSLES
27
28extern void OPENSLES_ResumeDevices(void);
29extern void OPENSLES_PauseDevices(void);
30
31#else
32
33#define OPENSLES_ResumeDevices()
34#define OPENSLES_PauseDevices()
35
36#endif
37
38#endif // SDL_openslesaudio_h_