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