/
SDL_openslES.c
616 lines (503 loc) · 18.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
/*
Simple DirectMedia Layer
Copyright (C) 1997-2019 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_AUDIO_DRIVER_OPENSLES
#include "SDL_audio.h"
#include "../SDL_audio_c.h"
#include "SDL_openslES.h"
29
/* for native audio */
30
31
32
33
34
35
36
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <android/log.h>
#define LOG_TAG "SDL_openslES"
37
38
#if 0
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
39
40
41
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
//#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
#define LOGV(...)
42
#else
43
#define LOGE(...)
44
45
#define LOGI(...)
#define LOGV(...)
46
#endif
47
48
/* engine interfaces */
49
static SLObjectItf engineObject = NULL;
50
static SLEngineItf engineEngine = NULL;
51
52
/* output mix interfaces */
53
static SLObjectItf outputMixObject = NULL;
54
// static SLEnvironmentalReverbItf outputMixEnvironmentalReverb = NULL;
55
56
57
/* aux effect on the output mix, used by the buffer queue player */
/* static const SLEnvironmentalReverbSettings reverbSettings = SL_I3DL2_ENVIRONMENT_PRESET_STONECORRIDOR; */
58
59
/* buffer queue player interfaces */
60
61
62
static SLObjectItf bqPlayerObject = NULL;
static SLPlayItf bqPlayerPlay = NULL;
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue = NULL;
63
64
#if 0
static SLEffectSendItf bqPlayerEffectSend = NULL;
65
66
static SLMuteSoloItf bqPlayerMuteSolo = NULL;
static SLVolumeItf bqPlayerVolume = NULL;
67
#endif
68
69
#if 0
70
/* recorder interfaces TODO */
71
72
73
74
static SLObjectItf recorderObject = NULL;
static SLRecordItf recorderRecord;
static SLAndroidSimpleBufferQueueItf recorderBufferQueue;
#endif
75
76
/* pointer and size of the next player buffer to enqueue, and number of remaining buffers */
77
78
79
80
81
#if 0
static short *nextBuffer;
static unsigned nextSize;
static int nextCount;
#endif
82
83
// static SDL_AudioDevice* audioDevice = NULL;
84
85
#if 0
86
87
88
89
90
static const char *sldevaudiorecorderstr = "SLES Audio Recorder";
static const char *sldevaudioplayerstr = "SLES Audio Player";
#define SLES_DEV_AUDIO_RECORDER sldevaudiorecorderstr
#define SLES_DEV_AUDIO_PLAYER sldevaudioplayerstr
91
92
static void openslES_DetectDevices( int iscapture )
{
93
LOGI( "openSLES_DetectDevices()" );
94
95
if ( iscapture )
addfn( SLES_DEV_AUDIO_RECORDER );
96
else
97
addfn( SLES_DEV_AUDIO_PLAYER );
98
return;
99
100
101
}
#endif
102
static void openslES_DestroyEngine();
103
104
static int
105
openslES_CreateEngine()
106
{
107
SLresult result;
108
109
LOGI("openSLES_CreateEngine()");
110
111
/* create engine */
112
113
114
115
116
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
if (SL_RESULT_SUCCESS != result) {
LOGE("slCreateEngine failed");
goto error;
}
117
118
LOGI("slCreateEngine OK");
119
120
/* realize the engine */
121
122
123
124
125
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeEngine failed");
goto error;
}
126
127
LOGI("RealizeEngine OK");
128
129
/* get the engine interface, which is needed in order to create other objects */
130
131
132
133
134
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
if (SL_RESULT_SUCCESS != result) {
LOGE("EngineGetInterface failed");
goto error;
}
135
136
LOGI("EngineGetInterface OK");
137
138
139
140
/* create output mix, with environmental reverb specified as a non-required interface */
/* const SLInterfaceID ids[1] = { SL_IID_ENVIRONMENTALREVERB }; */
/* const SLboolean req[1] = { SL_BOOLEAN_FALSE }; */
141
142
143
144
const SLInterfaceID ids[1] = { SL_IID_VOLUME };
const SLboolean req[1] = { SL_BOOLEAN_FALSE };
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 1, ids, req);
145
146
147
148
149
150
if (SL_RESULT_SUCCESS != result) {
LOGE("CreateOutputMix failed");
goto error;
}
LOGI("CreateOutputMix OK");
151
152
/* realize the output mix */
153
154
155
156
157
158
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeOutputMix failed");
goto error;
}
return 1;
159
160
161
162
error:
openslES_DestroyEngine();
return 0;
163
164
}
165
166
static void openslES_DestroyPCMPlayer(_THIS);
static void openslES_DestroyPCMRecorder(_THIS);
167
168
static void openslES_DestroyEngine()
169
{
170
LOGI("openslES_DestroyEngine()");
171
172
173
// openslES_DestroyPCMPlayer(this);
// openslES_DestroyPCMRecorder(this);
174
175
/* destroy output mix object, and invalidate all associated interfaces */
176
177
178
if (outputMixObject != NULL) {
(*outputMixObject)->Destroy(outputMixObject);
outputMixObject = NULL;
179
/* outputMixEnvironmentalReverb = NULL; */
180
}
181
182
/* destroy engine object, and invalidate all associated interfaces */
183
184
185
186
187
if (engineObject != NULL) {
(*engineObject)->Destroy(engineObject);
engineObject = NULL;
engineEngine = NULL;
}
188
189
return;
190
191
}
192
/* this callback handler is called every time a buffer finishes playing */
193
194
static void
bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
195
{
196
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) context;
197
LOGV("SLES: Playback Callmeback");
198
SDL_SemPost(audiodata->playsem);
199
return;
200
201
}
202
203
static int
openslES_CreatePCMRecorder(_THIS)
204
{
205
206
/* struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */
207
208
LOGE("openslES_CreatePCMRecorder not implimented yet!");
return SDL_SetError("openslES_CreatePCMRecorder not implimented yet!");
209
210
}
211
static void
212
openslES_DestroyPCMRecorder(_THIS)
213
{
214
215
/* struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */
216
return;
217
218
}
219
static int
220
openslES_CreatePCMPlayer(_THIS)
221
{
222
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
223
224
225
SLDataFormat_PCM format_pcm;
SLresult result;
int i;
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/* If we want to add floating point audio support (requires API level 21)
it can be done as described here:
https://developer.android.com/ndk/guides/audio/opensl/android-extensions.html#floating-point
*/
#if 1
/* Just go with signed 16-bit audio as it's the most compatible */
this->spec.format = AUDIO_S16SYS;
#else
SDL_AudioFormat test_format = SDL_FirstAudioFormat(this->spec.format);
while (test_format != 0) {
if (SDL_AUDIO_ISSIGNED(test_format) && SDL_AUDIO_ISINT(test_format)) {
break;
}
test_format = SDL_NextAudioFormat();
}
242
243
244
245
246
247
248
if (test_format == 0) {
/* Didn't find a compatible format : */
LOGI( "No compatible audio format, using signed 16-bit audio" );
test_format = AUDIO_S16SYS;
}
this->spec.format = test_format;
249
#endif
250
251
/* Update the fragment size as size in bytes */
252
SDL_CalculateAudioSpec(&this->spec);
253
254
255
LOGI("Try to open %u hz %u bit chan %u %s samples %u",
this->spec.freq, SDL_AUDIO_BITSIZE(this->spec.format),
256
this->spec.channels, (this->spec.format & 0x1000) ? "BE" : "LE", this->spec.samples);
257
258
/* configure audio source */
259
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS };
260
261
262
format_pcm.formatType = SL_DATAFORMAT_PCM;
format_pcm.numChannels = this->spec.channels;
263
format_pcm.samplesPerSec = this->spec.freq * 1000; /* / kilo Hz to milli Hz */
264
265
format_pcm.bitsPerSample = SDL_AUDIO_BITSIZE(this->spec.format);
format_pcm.containerSize = SDL_AUDIO_BITSIZE(this->spec.format);
266
267
268
269
270
271
if (SDL_AUDIO_ISBIGENDIAN(this->spec.format)) {
format_pcm.endianness = SL_BYTEORDER_BIGENDIAN;
} else {
format_pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
}
272
273
/*
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
#define SL_SPEAKER_FRONT_LEFT ((SLuint32) 0x00000001)
#define SL_SPEAKER_FRONT_RIGHT ((SLuint32) 0x00000002)
#define SL_SPEAKER_FRONT_CENTER ((SLuint32) 0x00000004)
#define SL_SPEAKER_LOW_FREQUENCY ((SLuint32) 0x00000008)
#define SL_SPEAKER_BACK_LEFT ((SLuint32) 0x00000010)
#define SL_SPEAKER_BACK_RIGHT ((SLuint32) 0x00000020)
#define SL_SPEAKER_FRONT_LEFT_OF_CENTER ((SLuint32) 0x00000040)
#define SL_SPEAKER_FRONT_RIGHT_OF_CENTER ((SLuint32) 0x00000080)
#define SL_SPEAKER_BACK_CENTER ((SLuint32) 0x00000100)
#define SL_SPEAKER_SIDE_LEFT ((SLuint32) 0x00000200)
#define SL_SPEAKER_SIDE_RIGHT ((SLuint32) 0x00000400)
#define SL_SPEAKER_TOP_CENTER ((SLuint32) 0x00000800)
#define SL_SPEAKER_TOP_FRONT_LEFT ((SLuint32) 0x00001000)
#define SL_SPEAKER_TOP_FRONT_CENTER ((SLuint32) 0x00002000)
#define SL_SPEAKER_TOP_FRONT_RIGHT ((SLuint32) 0x00004000)
#define SL_SPEAKER_TOP_BACK_LEFT ((SLuint32) 0x00008000)
#define SL_SPEAKER_TOP_BACK_CENTER ((SLuint32) 0x00010000)
#define SL_SPEAKER_TOP_BACK_RIGHT ((SLuint32) 0x00020000)
292
293
*/
294
295
296
297
298
299
300
301
302
303
304
305
306
if (this->spec.channels == 1) {
format_pcm.channelMask = SL_SPEAKER_FRONT_CENTER;
} else if (this->spec.channels == 2) {
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
} else if (this->spec.channels == 3) {
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT | SL_SPEAKER_FRONT_CENTER;
} else if (this->spec.channels == 4) {
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT;
} else {
format_pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT |
SL_SPEAKER_BACK_LEFT | SL_SPEAKER_BACK_RIGHT | SL_SPEAKER_FRONT_CENTER;
}
307
308
SLDataSource audioSrc = { &loc_bufq, &format_pcm };
309
310
/* configure audio sink */
311
312
SLDataLocator_OutputMix loc_outmix = { SL_DATALOCATOR_OUTPUTMIX, outputMixObject };
SLDataSink audioSnk = { &loc_outmix, NULL };
313
314
/* create audio player */
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
const SLInterfaceID ids[2] = {
SL_IID_ANDROIDSIMPLEBUFFERQUEUE,
SL_IID_VOLUME
};
const SLboolean req[2] = {
SL_BOOLEAN_TRUE,
SL_BOOLEAN_FALSE,
};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk, 2, ids, req);
if (SL_RESULT_SUCCESS != result) {
LOGE("CreateAudioPlayer failed");
goto failed;
}
330
331
/* realize the player */
332
333
334
335
336
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
if (SL_RESULT_SUCCESS != result) {
LOGE("RealizeAudioPlayer failed");
goto failed;
}
337
338
/* get the play interface */
339
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
340
341
342
343
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_PLAY interface get failed");
goto failed;
}
344
345
/* get the buffer queue interface */
346
347
348
349
350
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &bqPlayerBufferQueue);
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_BUFFERQUEUE interface get failed");
goto failed;
}
351
352
353
354
/* register callback on the buffer queue */
/* context is '(SDL_PrivateAudioData *)this->hidden' */
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, this->hidden);
355
356
357
358
if (SL_RESULT_SUCCESS != result) {
LOGE("RegisterCallback failed");
goto failed;
}
359
360
#if 0
361
/* get the effect send interface */
362
363
364
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_EFFECTSEND, &bqPlayerEffectSend);
if (SL_RESULT_SUCCESS != result)
{
365
366
367
368
LOGE("SL_IID_EFFECTSEND interface get failed");
goto failed;
}
369
370
#endif
371
372
#if 0 /* mute/solo is not supported for sources that are known to be mono, as this is */
/* get the mute/solo interface */
373
374
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_MUTESOLO, &bqPlayerMuteSolo);
assert(SL_RESULT_SUCCESS == result);
375
(void) result;
376
377
#endif
378
#if 0
379
/* get the volume interface */
380
381
382
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_VOLUME, &bqPlayerVolume);
if (SL_RESULT_SUCCESS != result) {
LOGE("SL_IID_VOLUME interface get failed");
383
/* goto failed; */
384
}
385
#endif
386
387
/* Create the audio buffer semaphore */
388
389
audiodata->playsem = SDL_CreateSemaphore(NUM_BUFFERS - 1);
if (!audiodata->playsem) {
390
391
392
LOGE("cannot create Semaphore!");
goto failed;
}
393
394
/* Create the sound buffers */
395
396
audiodata->mixbuff = (Uint8 *) SDL_malloc(NUM_BUFFERS * this->spec.size);
if (audiodata->mixbuff == NULL) {
397
398
399
LOGE("mixbuffer allocate - out of memory");
goto failed;
}
400
401
for (i = 0; i < NUM_BUFFERS; i++) {
402
audiodata->pmixbuff[i] = audiodata->mixbuff + i * this->spec.size;
403
}
404
405
/* set the player's state to playing */
406
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
407
408
409
410
411
if (SL_RESULT_SUCCESS != result) {
LOGE("Play set state failed");
goto failed;
}
412
413
return 0;
414
failed:
415
416
openslES_DestroyPCMPlayer(this);
417
418
return SDL_SetError("Open device failed!");
419
420
}
421
static void
422
openslES_DestroyPCMPlayer(_THIS)
423
{
424
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
425
426
427
SLresult result;
/* set the player's state to 'stopped' */
428
429
430
431
432
if (bqPlayerPlay != NULL) {
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
if (SL_RESULT_SUCCESS != result) {
SDL_SetError("Stopped set state failed");
}
433
}
434
435
/* destroy buffer queue audio player object, and invalidate all associated interfaces */
436
if (bqPlayerObject != NULL) {
437
438
(*bqPlayerObject)->Destroy(bqPlayerObject);
439
440
bqPlayerObject = NULL;
441
bqPlayerPlay = NULL;
442
bqPlayerBufferQueue = NULL;
443
444
#if 0
bqPlayerEffectSend = NULL;
445
446
bqPlayerMuteSolo = NULL;
bqPlayerVolume = NULL;
447
#endif
448
449
}
450
451
452
if (audiodata->playsem) {
SDL_DestroySemaphore(audiodata->playsem);
audiodata->playsem = NULL;
453
}
454
455
456
if (audiodata->mixbuff) {
SDL_free(audiodata->mixbuff);
457
}
458
459
return;
460
461
}
462
463
static int
openslES_OpenDevice(_THIS, void *handle, const char *devname, int iscapture)
464
{
465
466
467
468
469
this->hidden = (struct SDL_PrivateAudioData *) SDL_calloc(1, (sizeof *this->hidden));
if (this->hidden == NULL) {
return SDL_OutOfMemory();
}
470
471
472
473
474
475
476
if (iscapture) {
LOGI("openslES_OpenDevice( ) %s for capture", devname);
return openslES_CreatePCMRecorder(this);
} else {
LOGI("openslES_OpenDevice( ) %s for playing", devname);
return openslES_CreatePCMPlayer(this);
}
477
478
}
479
480
static void
openslES_CloseDevice(_THIS)
481
{
482
483
/* struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden; */
484
485
if (this->iscapture) {
LOGI("openslES_CloseDevice( ) for capture");
486
openslES_DestroyPCMRecorder(this);
487
488
} else {
LOGI("openslES_CloseDevice( ) for playing");
489
openslES_DestroyPCMPlayer(this);
490
}
491
492
493
SDL_free(this->hidden);
494
return;
495
496
}
497
498
static void
openslES_WaitDevice(_THIS)
499
{
500
501
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
502
LOGV("openslES_WaitDevice( )");
503
504
/* Wait for an audio chunk to finish */
505
506
/* WaitForSingleObject(this->hidden->audio_sem, INFINITE); */
SDL_SemWait(audiodata->playsem);
507
508
return;
509
510
}
511
512
513
514
515
516
517
518
519
520
521
/*/ n playn sem */
/* getbuf 0 - 1 */
/* fill buff 0 - 1 */
/* play 0 - 0 1 */
/* wait 1 0 0 */
/* getbuf 1 0 0 */
/* fill buff 1 0 0 */
/* play 0 0 0 */
/* wait */
/* */
/* okay.. */
522
523
524
static Uint8 *
openslES_GetDeviceBuf(_THIS)
525
{
526
527
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
528
LOGV("openslES_GetDeviceBuf( )");
529
return audiodata->pmixbuff[audiodata->next_buffer];
530
531
}
532
533
static void
openslES_PlayDevice(_THIS)
534
{
535
struct SDL_PrivateAudioData *audiodata = (struct SDL_PrivateAudioData *) this->hidden;
536
SLresult result;
537
538
LOGV("======openslES_PlayDevice( )======");
539
540
/* Queue it up */
541
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, audiodata->pmixbuff[audiodata->next_buffer], this->spec.size);
542
543
544
545
audiodata->next_buffer++;
if (audiodata->next_buffer >= NUM_BUFFERS) {
audiodata->next_buffer = 0;
546
}
547
548
549
550
551
552
553
/* If Enqueue fails, callback won't be called.
* Post the semphore, not to run out of buffer */
if (SL_RESULT_SUCCESS != result) {
SDL_SemPost(audiodata->playsem);
}
554
return;
555
556
}
557
558
static int
openslES_Init(SDL_AudioDriverImpl * impl)
559
{
560
LOGI("openslES_Init() called");
561
562
563
564
if (!openslES_CreateEngine()) {
return 0;
}
565
566
LOGI("openslES_Init() - set pointers");
567
568
/* Set the function pointers */
569
/* impl->DetectDevices = openslES_DetectDevices; */
570
impl->OpenDevice = openslES_OpenDevice;
571
impl->CloseDevice = openslES_CloseDevice;
572
573
574
575
impl->PlayDevice = openslES_PlayDevice;
impl->GetDeviceBuf = openslES_GetDeviceBuf;
impl->Deinitialize = openslES_DestroyEngine;
impl->WaitDevice = openslES_WaitDevice;
576
577
578
579
/* and the capabilities */
impl->HasCaptureSupport = 0; /* TODO */
impl->OnlyHasDefaultOutputDevice = 1;
580
/* impl->OnlyHasDefaultInputDevice = 1; */
581
582
LOGI("openslES_Init() - succes");
583
584
585
/* this audio target is available. */
return 1;
586
587
588
}
AudioBootStrap openslES_bootstrap = {
589
"openslES", "opensl ES audio driver", openslES_Init, 0
590
591
};
592
593
void openslES_ResumeDevices()
{
594
595
596
597
598
599
if (bqPlayerPlay != NULL) {
/* set the player's state to 'playing' */
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
if (SL_RESULT_SUCCESS != result) {
SDL_SetError("openslES_ResumeDevices failed");
}
600
601
602
603
604
}
}
void openslES_PauseDevices()
{
605
606
607
608
609
610
if (bqPlayerPlay != NULL) {
/* set the player's state to 'paused' */
SLresult result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PAUSED);
if (SL_RESULT_SUCCESS != result) {
SDL_SetError("openslES_PauseDevices failed");
}
611
612
613
}
}
614
615
616
#endif /* SDL_AUDIO_DRIVER_OPENSLES */
/* vi: set ts=4 sw=4 expandtab: */