2015-06-26 04:32:37 +00:00
/*
2015-10-22 05:13:26 +00:00
* Apple // emulator for *ix
2015-06-26 04:32:37 +00:00
*
* This software package is subject to the GNU General Public License
2015-10-22 05:13:26 +00:00
* version 3 or later ( your choice ) as published by the Free Software
2015-06-26 04:32:37 +00:00
* Foundation .
*
2015-10-22 05:13:26 +00:00
* Copyright 2013 - 2015 Aaron Culliney
2015-06-26 04:32:37 +00:00
*
*/
// soundcore OpenSLES backend -- streaming audio
# include "common.h"
# include <SLES/OpenSLES.h>
# if defined(ANDROID)
# include <SLES / OpenSLES_Android.h>
# else
# error FIXME TODO this currently uses Android BufferQueue extensions...
# endif
# define DEBUG_OPENSL 0
# if DEBUG_OPENSL
# define OPENSL_LOG(...) LOG(__VA_ARGS__)
# else
# define OPENSL_LOG(...)
# endif
2015-07-11 21:37:41 +00:00
# define NUM_CHANNELS 2
2015-06-26 04:32:37 +00:00
typedef struct SLVoice {
2015-07-11 21:37:41 +00:00
void * ctx ; // EngineContext_s
2015-06-26 04:32:37 +00:00
// working data buffer
2015-07-09 03:14:16 +00:00
uint8_t * ringBuffer ; // ringBuffer of total size : bufferSize+submitSize
2015-07-03 05:24:14 +00:00
unsigned long bufferSize ; // ringBuffer non-overflow size
2015-07-11 21:37:41 +00:00
ptrdiff_t writeHead ; // head of the writer of ringBuffer (speaker, mockingboard)
2015-07-03 05:24:14 +00:00
unsigned long writeWrapCount ; // count of buffer wraps for the writer
unsigned long spinLock ; // spinlock around reader variables
2015-07-11 21:37:41 +00:00
ptrdiff_t readHead ; // head of the reader of ringBuffer (OpenSLES callback)
2015-07-03 05:24:14 +00:00
unsigned long readWrapCount ; // count of buffer wraps for the reader
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
// next voice
struct SLVoice * next ;
2015-06-26 04:32:37 +00:00
} SLVoice ;
typedef struct EngineContext_s {
SLObjectItf engineObject ;
SLEngineItf engineEngine ;
SLObjectItf outputMixObject ;
2015-07-11 21:37:41 +00:00
SLObjectItf bqPlayerObject ;
SLPlayItf bqPlayerPlay ;
SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue ;
uint8_t * mixBuf ; // mix buffer submitted to OpenSLES
unsigned long submitSize ; // buffer size OpenSLES expects/wants
SLVoice * voices ;
SLVoice * recycledVoices ;
2015-06-26 04:32:37 +00:00
} EngineContext_s ;
static AudioBackend_s opensles_audio_backend = { 0 } ;
// ----------------------------------------------------------------------------
2015-07-03 05:24:14 +00:00
// AudioBuffer_s internal processing routines
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
// Check and resets underrun condition (readHead has advanced beyond writeHead)
static inline bool _underrun_check_and_manage ( SLVoice * voice , OUTPARM unsigned long * workingBytes ) {
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
SPINLOCK_ACQUIRE ( & voice - > spinLock ) ;
unsigned long readHead = voice - > readHead ;
unsigned long readWrapCount = voice - > readWrapCount ;
SPINLOCK_RELINQUISH ( & voice - > spinLock ) ;
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
assert ( readHead < voice - > bufferSize ) ;
assert ( voice - > writeHead < voice - > bufferSize ) ;
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
bool isUnder = false ;
if ( ( readWrapCount > voice - > writeWrapCount ) | |
( ( readHead > = voice - > writeHead ) & & ( readWrapCount = = voice - > writeWrapCount ) ) )
{
isUnder = true ;
2015-10-21 03:51:21 +00:00
LOG ( " Buffer underrun ... " ) ;
2015-07-03 05:24:14 +00:00
voice - > writeHead = readHead ;
voice - > writeWrapCount = readWrapCount ;
}
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
if ( readHead < = voice - > writeHead ) {
* workingBytes = voice - > writeHead - readHead ;
} else {
* workingBytes = voice - > writeHead + ( voice - > bufferSize - readHead ) ;
}
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
return isUnder ;
}
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
// This callback handler is called presumably every time (or just prior to when) a buffer finishes playing and the
// system needs moar data (of the correct buffer size)
static void bqPlayerCallback ( SLAndroidSimpleBufferQueueItf bq , void * context ) {
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
// invariant : can always read submitSize from position of readHead (bufferSize+submitSize)
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
EngineContext_s * ctx = ( EngineContext_s * ) context ;
2015-06-26 04:32:37 +00:00
2015-07-09 03:14:16 +00:00
SLresult result = SL_RESULT_SUCCESS ;
2015-07-11 21:37:41 +00:00
do {
// This is a very simple inline mixer so that we only need one BufferQueue (which works best on low-end Android
// devices
//
// HACK ASSUMPTIONS :
// * max of 2 voices/buffers
// * both buffers contain stereo signed 16bit samples with zero as mid point
// * absolute value of maximum amplitude is less than one half SHRT_MAX (to avoid clipping)
SLVoice * voice0 = ctx - > voices ;
if ( ! voice0 ) {
result = - 1 ;
break ;
}
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
// copy/mix data
2015-07-03 05:24:14 +00:00
2015-07-11 21:37:41 +00:00
memcpy ( ctx - > mixBuf , voice0 - > ringBuffer + voice0 - > readHead , ctx - > submitSize ) ;
2015-07-03 05:24:14 +00:00
2015-07-11 21:37:41 +00:00
SLVoice * voice1 = voice0 - > next ;
if ( voice1 ) {
// add second waveform into mix buffer
////if (SIMD_IS_AVAILABLE()) {
# if USE_SIMD
# warning FIXME TODO vectorial code here
# endif
////} else {
uint16_t * mixBuf = ( uint16_t * ) ctx - > mixBuf ;
unsigned long submitSize = ctx - > submitSize > > 1 ;
for ( unsigned long i = 0 ; i < submitSize ; i + + ) {
mixBuf [ i ] + = ( ( uint16_t * ) ( voice1 - > ringBuffer + voice1 - > readHead ) ) [ i ] ;
}
////}
2015-07-09 03:14:16 +00:00
}
2015-07-03 05:24:14 +00:00
2015-07-11 21:37:41 +00:00
// submit data to OpenSLES
result = ( * bq ) - > Enqueue ( bq , ctx - > mixBuf , ctx - > submitSize ) ;
// now manage quiet backfilling and overflow/wrapping ...
memset ( voice0 - > ringBuffer + voice0 - > readHead , 0x0 , ctx - > submitSize ) ; // backfill quiet samples
unsigned long newReadHead0 = voice0 - > readHead + ctx - > submitSize ;
unsigned long newReadWrapCount0 = voice0 - > readWrapCount ;
if ( newReadHead0 > = voice0 - > bufferSize ) {
newReadHead0 = newReadHead0 - voice0 - > bufferSize ;
memset ( voice0 - > ringBuffer + voice0 - > bufferSize , 0x0 , ctx - > submitSize ) ; // backfill quiet samples
memset ( voice0 - > ringBuffer , 0x0 , newReadHead0 ) ;
+ + newReadWrapCount0 ;
}
SPINLOCK_ACQUIRE ( & voice0 - > spinLock ) ;
voice0 - > readHead = newReadHead0 ;
voice0 - > readWrapCount = newReadWrapCount0 ;
SPINLOCK_RELINQUISH ( & voice0 - > spinLock ) ;
if ( voice1 ) {
memset ( voice1 - > ringBuffer + voice1 - > readHead , 0x0 , ctx - > submitSize ) ; // backfill quiet samples
unsigned long newReadHead1 = voice1 - > readHead + ctx - > submitSize ;
unsigned long newReadWrapCount1 = voice1 - > readWrapCount ;
if ( newReadHead1 > = voice1 - > bufferSize ) {
newReadHead1 = newReadHead1 - voice1 - > bufferSize ;
memset ( voice1 - > ringBuffer + voice1 - > bufferSize , 0x0 , ctx - > submitSize ) ; // backfill quiet samples
memset ( voice1 - > ringBuffer , 0x0 , newReadHead1 ) ;
+ + newReadWrapCount1 ;
}
SPINLOCK_ACQUIRE ( & voice1 - > spinLock ) ;
voice1 - > readHead = newReadHead1 ;
voice1 - > readWrapCount = newReadWrapCount1 ;
SPINLOCK_RELINQUISH ( & voice1 - > spinLock ) ;
}
} while ( 0 ) ;
2015-07-03 05:24:14 +00:00
if ( result ! = SL_RESULT_SUCCESS ) {
LOG ( " WARNING: could not enqueue data to OpenSLES! " ) ;
2015-07-11 21:37:41 +00:00
( * ( ctx - > bqPlayerPlay ) ) - > SetPlayState ( ctx - > bqPlayerPlay , SL_PLAYSTATE_STOPPED ) ;
2015-07-03 05:24:14 +00:00
}
2015-06-26 04:32:37 +00:00
}
2015-07-03 05:24:14 +00:00
static long _SLMaybeSubmitAndStart ( SLVoice * voice ) {
SLuint32 state = 0 ;
2015-07-11 21:37:41 +00:00
EngineContext_s * ctx = ( EngineContext_s * ) voice - > ctx ;
SLresult result = ( * ( ctx - > bqPlayerPlay ) ) - > GetPlayState ( ctx - > bqPlayerPlay , & state ) ;
2015-07-03 05:24:14 +00:00
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " OOPS, could not get source state : %lu " , ( unsigned long ) result ) ;
2015-07-03 05:24:14 +00:00
} else {
if ( ( state ! = SL_PLAYSTATE_PLAYING ) & & ( state ! = SL_PLAYSTATE_PAUSED ) ) {
LOG ( " FORCING restart audio buffer queue playback ... " ) ;
2015-07-11 21:37:41 +00:00
result = ( * ( ctx - > bqPlayerPlay ) ) - > SetPlayState ( ctx - > bqPlayerPlay , SL_PLAYSTATE_PLAYING ) ;
bqPlayerCallback ( ctx - > bqPlayerBufferQueue , ctx ) ;
2015-07-03 05:24:14 +00:00
}
}
return result ;
}
// ----------------------------------------------------------------------------
// AudioBuffer_s public API handlers
2015-06-26 04:32:37 +00:00
// returns queued+working sound buffer size in bytes
static long SLGetPosition ( AudioBuffer_s * _this , OUTPARM unsigned long * bytes_queued ) {
* bytes_queued = 0 ;
long err = 0 ;
do {
SLVoice * voice = ( SLVoice * ) _this - > _internal ;
2015-07-03 05:24:14 +00:00
unsigned long workingBytes = 0 ;
bool underrun = _underrun_check_and_manage ( voice , & workingBytes ) ;
//bool overrun = _overrun_check_and_manage(voice);
unsigned long queuedBytes = 0 ;
if ( ! underrun ) {
2015-07-11 21:37:41 +00:00
//queuedBytes = ctx->submitSize; // assume that there are always about this much actually queued
2015-06-26 04:32:37 +00:00
}
2015-07-03 05:24:14 +00:00
assert ( workingBytes < = voice - > bufferSize ) ;
2015-07-05 19:57:24 +00:00
* bytes_queued = workingBytes ;
2015-06-26 04:32:37 +00:00
} while ( 0 ) ;
return err ;
}
static long SLLockBuffer ( AudioBuffer_s * _this , unsigned long write_bytes , INOUT int16_t * * audio_ptr , OUTPARM unsigned long * audio_bytes ) {
* audio_bytes = 0 ;
* audio_ptr = NULL ;
long err = 0 ;
//OPENSL_LOG("SLLockBuffer request for %ld bytes", write_bytes);
do {
SLVoice * voice = ( SLVoice * ) _this - > _internal ;
2015-07-11 21:37:41 +00:00
EngineContext_s * ctx = ( EngineContext_s * ) voice - > ctx ;
2015-06-26 04:32:37 +00:00
if ( write_bytes = = 0 ) {
2015-07-03 05:24:14 +00:00
LOG ( " HMMM ... writing full buffer! " ) ;
write_bytes = voice - > bufferSize ;
2015-06-26 04:32:37 +00:00
}
2015-07-03 05:24:14 +00:00
unsigned long workingBytes = 0 ;
_underrun_check_and_manage ( voice , & workingBytes ) ;
unsigned long availableBytes = voice - > bufferSize - workingBytes ;
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
assert ( workingBytes < = voice - > bufferSize ) ;
assert ( voice - > writeHead < voice - > bufferSize ) ;
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
// TODO FIXME : maybe need to resurrect the 2 inner pointers and foist the responsibility onto the
// speaker/mockingboard modules so we can actually write moar here?
2015-07-11 21:37:41 +00:00
unsigned long writableBytes = MIN ( availableBytes , ( ( voice - > bufferSize + ctx - > submitSize ) - voice - > writeHead ) ) ;
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
if ( write_bytes > writableBytes ) {
2015-07-08 04:32:43 +00:00
OPENSL_LOG ( " NOTE truncating audio buffer (call again to write complete requested buffer) ... " ) ;
2015-07-03 05:24:14 +00:00
write_bytes = writableBytes ;
2015-06-26 04:32:37 +00:00
}
2015-07-03 05:24:14 +00:00
* audio_ptr = ( int16_t * ) ( voice - > ringBuffer + voice - > writeHead ) ;
* audio_bytes = write_bytes ;
2015-06-26 04:32:37 +00:00
} while ( 0 ) ;
return err ;
}
static long SLUnlockBuffer ( AudioBuffer_s * _this , unsigned long audio_bytes ) {
long err = 0 ;
do {
SLVoice * voice = ( SLVoice * ) _this - > _internal ;
2015-07-11 21:37:41 +00:00
EngineContext_s * ctx = ( EngineContext_s * ) voice - > ctx ;
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
unsigned long previousWriteHead = voice - > writeHead ;
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
voice - > writeHead + = audio_bytes ;
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
assert ( ( voice - > writeHead < = ( voice - > bufferSize + ctx - > submitSize ) ) & & " OOPS, real overflow in queued sound data! " ) ;
2015-06-26 04:32:37 +00:00
2015-07-03 05:24:14 +00:00
if ( voice - > writeHead > = voice - > bufferSize ) {
// copy data from overflow into beginning of buffer
voice - > writeHead = voice - > writeHead - voice - > bufferSize ;
+ + voice - > writeWrapCount ;
memcpy ( voice - > ringBuffer , voice - > ringBuffer + voice - > bufferSize , voice - > writeHead ) ;
2015-07-11 21:37:41 +00:00
} else if ( previousWriteHead < ctx - > submitSize ) {
2015-07-03 05:24:14 +00:00
// copy data in beginning of buffer into overflow position
2015-07-11 21:37:41 +00:00
unsigned long copyNumBytes = MIN ( audio_bytes , ctx - > submitSize - previousWriteHead ) ;
2015-07-03 05:24:14 +00:00
memcpy ( voice - > ringBuffer + voice - > bufferSize + previousWriteHead , voice - > ringBuffer + previousWriteHead , copyNumBytes ) ;
2015-06-26 04:32:37 +00:00
}
2015-07-03 05:24:14 +00:00
err = _SLMaybeSubmitAndStart ( voice ) ;
2015-06-26 04:32:37 +00:00
} while ( 0 ) ;
return err ;
}
2015-07-11 21:37:41 +00:00
#if 0
2015-06-26 04:32:37 +00:00
// HACK Part I : done once for mockingboard that has semiauto repeating phonemes ...
static long SLUnlockStaticBuffer ( AudioBuffer_s * _this , unsigned long audio_bytes ) {
SLVoice * voice = ( SLVoice * ) _this - > _internal ;
voice - > replay_index = audio_bytes ;
return 0 ;
}
// HACK Part II : replay mockingboard phoneme ...
static long SLReplay ( AudioBuffer_s * _this ) {
SLVoice * voice = ( SLVoice * ) _this - > _internal ;
2015-07-03 05:24:14 +00:00
SPINLOCK_ACQUIRE ( & voice - > spinLock ) ;
voice - > readHead = 0 ;
voice - > writeHead = voice - > replay_index ;
SPINLOCK_RELINQUISH ( & voice - > spinLock ) ;
long err = _SLMaybeSubmitAndStart ( voice ) ;
# warning FIXME TODO ... how do we handle mockingboard for new OpenSLES buffer queue codepath?
2015-06-26 04:32:37 +00:00
return err ;
}
2015-07-11 21:37:41 +00:00
# endif
2015-06-26 04:32:37 +00:00
static long SLGetStatus ( AudioBuffer_s * _this , OUTPARM unsigned long * status ) {
* status = - 1 ;
SLresult result = SL_RESULT_UNKNOWN_ERROR ;
do {
SLVoice * voice = ( SLVoice * ) _this - > _internal ;
2015-07-11 21:37:41 +00:00
EngineContext_s * ctx = ( EngineContext_s * ) voice - > ctx ;
2015-06-26 04:32:37 +00:00
SLuint32 state = 0 ;
2015-07-11 21:37:41 +00:00
result = ( * ( ctx - > bqPlayerPlay ) ) - > GetPlayState ( ctx - > bqPlayerPlay , & state ) ;
2015-06-26 04:32:37 +00:00
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " OOPS, could not get source state : %lu " , ( unsigned long ) result ) ;
2015-06-26 04:32:37 +00:00
break ;
}
if ( ( state = = SL_PLAYSTATE_PLAYING ) | | ( state = = SL_PLAYSTATE_PAUSED ) ) {
* status = AUDIO_STATUS_PLAYING ;
} else {
* status = AUDIO_STATUS_NOTPLAYING ;
}
} while ( 0 ) ;
return ( long ) result ;
}
// ----------------------------------------------------------------------------
// SLVoice is the AudioBuffer_s->_internal
2015-07-11 21:37:41 +00:00
static inline void _opensl_destroyVoice ( SLVoice * voice ) {
2015-07-03 05:24:14 +00:00
if ( voice - > ringBuffer ) {
FREE ( voice - > ringBuffer ) ;
2015-06-26 04:32:37 +00:00
}
memset ( voice , 0 , sizeof ( * voice ) ) ;
FREE ( voice ) ;
}
2015-07-11 21:37:41 +00:00
static long opensl_destroySoundBuffer ( const struct AudioContext_s * audio_context , INOUT AudioBuffer_s * * soundbuf_struct ) {
if ( ! * soundbuf_struct ) {
return 0 ;
}
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
LOG ( " opensl_destroySoundBuffer ... " ) ;
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
EngineContext_s * ctx = ( EngineContext_s * ) ( audio_context - > _internal ) ;
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
SLVoice * v = ( SLVoice * ) ( ( * soundbuf_struct ) - > _internal ) ;
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
SLVoice * vprev = NULL ;
SLVoice * voice = ctx - > voices ;
while ( voice ) {
if ( voice = = v ) {
if ( vprev ) {
vprev - > next = voice - > next ;
} else {
ctx - > voices = voice - > next ;
}
2015-06-26 04:32:37 +00:00
break ;
}
2015-07-11 21:37:41 +00:00
vprev = voice ;
voice = voice - > next ;
2015-06-26 04:32:37 +00:00
}
2015-07-11 21:37:41 +00:00
assert ( voice & & " voice should exist, or speaker, mockingboard, etc are not using this internal API correctly! " ) ;
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
// Do not actually destory the voice here since we could race with the buffer queue. purge these on complete sound
// system shutdown
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
voice - > next = ctx - > recycledVoices ;
ctx - > recycledVoices = voice ;
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
memset ( * soundbuf_struct , 0x0 , sizeof ( soundbuf_struct ) ) ;
2015-06-26 04:32:37 +00:00
FREE ( * soundbuf_struct ) ;
2015-07-11 21:37:41 +00:00
2015-06-26 04:32:37 +00:00
return 0 ;
}
2015-07-11 21:37:41 +00:00
static long opensl_createSoundBuffer ( const AudioContext_s * audio_context , INOUT AudioBuffer_s * * soundbuf_struct ) {
2015-06-26 04:32:37 +00:00
LOG ( " opensl_createSoundBuffer ... " ) ;
assert ( * soundbuf_struct = = NULL ) ;
SLVoice * voice = NULL ;
do {
EngineContext_s * ctx = ( EngineContext_s * ) ( audio_context - > _internal ) ;
assert ( ctx ! = NULL ) ;
2015-07-11 21:37:41 +00:00
unsigned long bufferSize = opensles_audio_backend . systemSettings . stereoBufferSizeSamples * opensles_audio_backend . systemSettings . bytesPerSample * NUM_CHANNELS ;
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
if ( ctx - > recycledVoices ) {
LOG ( " Recycling previously SLVoice ... " ) ;
voice = ctx - > recycledVoices ;
ctx - > recycledVoices = voice - > next ;
uint8_t * prevBuffer = voice - > ringBuffer ;
2015-09-07 18:07:49 +00:00
memset ( voice , 0x0 , sizeof ( * voice ) ) ;
2015-07-11 21:37:41 +00:00
voice - > bufferSize = bufferSize ;
voice - > ringBuffer = prevBuffer ;
} else {
LOG ( " Creating new SLVoice ... " ) ;
voice = calloc ( 1 , sizeof ( * voice ) ) ;
if ( voice = = NULL ) {
ERRLOG ( " OOPS, Out of memory! " ) ;
break ;
}
voice - > bufferSize = bufferSize ;
// Allocate enough space for the temp buffer (including a maximum allowed overflow)
2015-09-12 22:04:09 +00:00
voice - > ringBuffer = calloc ( 1 , voice - > bufferSize + ctx - > submitSize /*max overflow*/ ) ;
2015-07-11 21:37:41 +00:00
if ( voice - > ringBuffer = = NULL ) {
ERRLOG ( " OOPS, Error allocating %lu bytes " , ( unsigned long ) voice - > bufferSize + ctx - > submitSize ) ;
break ;
}
2015-06-26 04:32:37 +00:00
}
2015-07-11 21:37:41 +00:00
LOG ( " ideal stereo submission bufsize is %lu (bytes:%lu) " , ( unsigned long ) android_stereoBufferSubmitSizeSamples , ( unsigned long ) ctx - > submitSize ) ;
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
voice - > ctx = ctx ;
2015-06-26 04:32:37 +00:00
2015-09-12 22:04:09 +00:00
if ( ( * soundbuf_struct = calloc ( 1 , sizeof ( AudioBuffer_s ) ) ) = = NULL ) {
2015-06-26 04:32:37 +00:00
ERRLOG ( " OOPS, Not enough memory " ) ;
break ;
}
( * soundbuf_struct ) - > _internal = voice ;
( * soundbuf_struct ) - > GetCurrentPosition = & SLGetPosition ;
( * soundbuf_struct ) - > Lock = & SLLockBuffer ;
( * soundbuf_struct ) - > Unlock = & SLUnlockBuffer ;
( * soundbuf_struct ) - > GetStatus = & SLGetStatus ;
2015-07-11 21:37:41 +00:00
// mockingboard-specific (SSI263) hacks
//(*soundbuf_struct)->UnlockStaticBuffer = &SLUnlockStaticBuffer;
//(*soundbuf_struct)->Replay = &SLReplay;
voice - > next = ctx - > voices ;
ctx - > voices = voice ;
LOG ( " Successfully created SLVoice " ) ;
2015-06-26 04:32:37 +00:00
return 0 ;
} while ( 0 ) ;
if ( * soundbuf_struct ) {
2015-07-03 05:24:14 +00:00
opensl_destroySoundBuffer ( audio_context , soundbuf_struct ) ;
2015-06-26 04:32:37 +00:00
} else if ( voice ) {
_opensl_destroyVoice ( voice ) ;
}
return - 1 ;
}
// ----------------------------------------------------------------------------
static long opensles_systemShutdown ( AudioContext_s * * audio_context ) {
assert ( * audio_context ! = NULL ) ;
EngineContext_s * ctx = ( EngineContext_s * ) ( ( * audio_context ) - > _internal ) ;
assert ( ctx ! = NULL ) ;
2015-07-11 21:37:41 +00:00
// destroy buffer queue audio player object, and invalidate all associated interfaces
if ( ctx - > bqPlayerObject ! = NULL ) {
( * ( ctx - > bqPlayerObject ) ) - > Destroy ( ctx - > bqPlayerObject ) ;
ctx - > bqPlayerObject = NULL ;
ctx - > bqPlayerPlay = NULL ;
ctx - > bqPlayerBufferQueue = NULL ;
}
2015-06-26 04:32:37 +00:00
// destroy output mix object, and invalidate all associated interfaces
if ( ctx - > outputMixObject ! = NULL ) {
( * ( ctx - > outputMixObject ) ) - > Destroy ( ctx - > outputMixObject ) ;
ctx - > outputMixObject = NULL ;
}
// destroy engine object, and invalidate all associated interfaces
if ( ctx - > engineObject ! = NULL ) {
( * ( ctx - > engineObject ) ) - > Destroy ( ctx - > engineObject ) ;
ctx - > engineObject = NULL ;
ctx - > engineEngine = NULL ;
}
2015-07-11 21:37:41 +00:00
if ( ctx - > mixBuf ) {
FREE ( ctx - > mixBuf ) ;
}
assert ( ctx - > voices = = NULL & & " incorrect API usage " ) ;
SLVoice * voice = ctx - > recycledVoices ;
while ( voice ) {
SLVoice * vkill = voice ;
voice = voice - > next ;
_opensl_destroyVoice ( vkill ) ;
}
memset ( ctx , 0x0 , sizeof ( EngineContext_s ) ) ;
2015-06-26 04:32:37 +00:00
FREE ( ctx ) ;
2015-07-11 21:37:41 +00:00
memset ( * audio_context , 0x0 , sizeof ( AudioContext_s ) ) ;
2015-06-26 04:32:37 +00:00
FREE ( * audio_context ) ;
return 0 ;
}
static long opensles_systemSetup ( INOUT AudioContext_s * * audio_context ) {
assert ( * audio_context = = NULL ) ;
EngineContext_s * ctx = NULL ;
SLresult result = - 1 ;
2015-07-03 05:24:14 +00:00
opensles_audio_backend . systemSettings . sampleRateHz = android_deviceSampleRateHz ;
opensles_audio_backend . systemSettings . bytesPerSample = 2 ;
if ( android_deviceSampleRateHz < = 22050 /*sentinel in DevicePropertyCalculator.java*/ ) {
2015-07-25 19:41:50 +00:00
android_stereoBufferSubmitSizeSamples > > = 1 ; // value from Android/Java DevicePropertyCalculator.java seems to be pre-multiplied by channel size?
2015-07-03 05:24:14 +00:00
}
2015-07-11 21:37:41 +00:00
2015-07-25 19:41:50 +00:00
opensles_audio_backend . systemSettings . monoBufferSizeSamples = android_deviceSampleRateHz * audio_getLatency ( ) ;
opensles_audio_backend . systemSettings . stereoBufferSizeSamples = android_deviceSampleRateHz * audio_getLatency ( ) ;
2015-07-11 21:37:41 +00:00
if ( android_stereoBufferSubmitSizeSamples < < 2 > opensles_audio_backend . systemSettings . stereoBufferSizeSamples ) {
opensles_audio_backend . systemSettings . stereoBufferSizeSamples = android_stereoBufferSubmitSizeSamples < < 2 ;
2015-09-06 20:55:17 +00:00
LOG ( " Changing stereo buffer size to be %lu samples " , ( unsigned long ) opensles_audio_backend . systemSettings . stereoBufferSizeSamples ) ;
2015-07-11 21:37:41 +00:00
}
if ( android_monoBufferSubmitSizeSamples < < 2 > opensles_audio_backend . systemSettings . monoBufferSizeSamples ) {
opensles_audio_backend . systemSettings . monoBufferSizeSamples = android_monoBufferSubmitSizeSamples < < 2 ;
2015-09-06 20:55:17 +00:00
LOG ( " Changing mono buffer size to be %lu samples " , ( unsigned long ) opensles_audio_backend . systemSettings . monoBufferSizeSamples ) ;
2015-07-11 21:37:41 +00:00
}
2015-07-05 20:00:08 +00:00
# warning TODO FIXME ^^^^^ need a dynamic bufferSize calculation / calibration routine to determine optimal buffer size for device ... may also need a user-initiated calibration too
2015-07-03 05:24:14 +00:00
2015-06-26 04:32:37 +00:00
do {
//
// Engine creation ...
//
ctx = calloc ( 1 , sizeof ( EngineContext_s ) ) ;
if ( ! ctx ) {
result = - 1 ;
break ;
}
2015-07-11 21:37:41 +00:00
ctx - > submitSize = android_stereoBufferSubmitSizeSamples * opensles_audio_backend . systemSettings . bytesPerSample * NUM_CHANNELS ;
2015-09-12 22:04:09 +00:00
ctx - > mixBuf = calloc ( 1 , ctx - > submitSize ) ;
2015-07-11 21:37:41 +00:00
if ( ctx - > mixBuf = = NULL ) {
ERRLOG ( " OOPS, Error allocating %lu bytes " , ( unsigned long ) ctx - > submitSize ) ;
break ;
}
2015-06-26 04:32:37 +00:00
// create basic engine
result = slCreateEngine ( & ( ctx - > engineObject ) , 0 , NULL , /*engineMixIIDCount:*/ 0 , /*engineMixIIDs:*/ NULL , /*engineMixReqs:*/ NULL ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " Could not create OpenSLES Engine : %lu " , ( unsigned long ) result ) ;
2015-06-26 04:32:37 +00:00
break ;
}
// realize the engine
result = ( * ( ctx - > engineObject ) ) - > Realize ( ctx - > engineObject , /*asynchronous_realization:*/ SL_BOOLEAN_FALSE ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " Could not realize the OpenSLES Engine : %lu " , ( unsigned long ) result ) ;
2015-06-26 04:32:37 +00:00
break ;
}
// get the actual engine interface
result = ( * ( ctx - > engineObject ) ) - > GetInterface ( ctx - > engineObject , SL_IID_ENGINE , & ( ctx - > engineEngine ) ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " Could not get the OpenSLES Engine : %lu " , ( unsigned long ) result ) ;
2015-06-26 04:32:37 +00:00
break ;
}
//
// Output Mix ...
//
result = ( * ( ctx - > engineEngine ) ) - > CreateOutputMix ( ctx - > engineEngine , & ( ctx - > outputMixObject ) , 0 , NULL , NULL ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " Could not create output mix : %lu " , ( unsigned long ) result ) ;
2015-06-26 04:32:37 +00:00
break ;
}
// realize the output mix
result = ( * ( ctx - > outputMixObject ) ) - > Realize ( ctx - > outputMixObject , SL_BOOLEAN_FALSE ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " Could not realize the output mix : %lu " , ( unsigned long ) result ) ;
2015-06-26 04:32:37 +00:00
break ;
}
// create soundcore API wrapper
2015-09-12 22:04:09 +00:00
if ( ( * audio_context = calloc ( 1 , sizeof ( AudioContext_s ) ) ) = = NULL ) {
2015-06-26 04:32:37 +00:00
result = - 1 ;
ERRLOG ( " OOPS, Not enough memory " ) ;
break ;
}
2015-07-11 21:37:41 +00:00
//
// OpenSLES buffer queue player setup
//
// configure audio source
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {
. locatorType = SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE ,
. numBuffers = 2 ,
# warning FIXME TODO ... verify 2 numBuffers is best
} ;
SLDataFormat_PCM format_pcm = {
. formatType = SL_DATAFORMAT_PCM ,
. numChannels = 2 ,
. samplesPerSec = opensles_audio_backend . systemSettings . sampleRateHz * 1000 ,
. bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16 ,
. containerSize = SL_PCMSAMPLEFORMAT_FIXED_16 ,
. channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT ,
. endianness = SL_BYTEORDER_LITTLEENDIAN ,
} ;
SLDataSource audioSrc = {
. pLocator = & loc_bufq ,
. pFormat = & format_pcm ,
} ;
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {
. locatorType = SL_DATALOCATOR_OUTPUTMIX ,
. outputMix = ctx - > outputMixObject ,
} ;
SLDataSink audioSnk = {
. pLocator = & loc_outmix ,
. pFormat = NULL ,
} ;
// create audio player
# define _NUM_INTERFACES 3
const SLInterfaceID ids [ _NUM_INTERFACES ] = {
SL_IID_BUFFERQUEUE ,
SL_IID_EFFECTSEND ,
//SL_IID_MUTESOLO,
SL_IID_VOLUME ,
} ;
const SLboolean req [ _NUM_INTERFACES ] = {
SL_BOOLEAN_TRUE ,
SL_BOOLEAN_TRUE ,
//numChannels == 1 ? SL_BOOLEAN_FALSE : SL_BOOLEAN_TRUE,
SL_BOOLEAN_TRUE ,
} ;
result = ( * ( ctx - > engineEngine ) ) - > CreateAudioPlayer ( ctx - > engineEngine , & ( ctx - > bqPlayerObject ) , & audioSrc , & audioSnk , _NUM_INTERFACES , ids , req ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " OOPS, could not create the BufferQueue player object : %lu " , ( unsigned long ) result ) ;
2015-07-11 21:37:41 +00:00
break ;
}
// realize the player
result = ( * ( ctx - > bqPlayerObject ) ) - > Realize ( ctx - > bqPlayerObject , /*asynchronous_realization:*/ SL_BOOLEAN_FALSE ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " OOPS, could not realize the BufferQueue player object : %lu " , ( unsigned long ) result ) ;
2015-07-11 21:37:41 +00:00
break ;
}
// get the play interface
result = ( * ( ctx - > bqPlayerObject ) ) - > GetInterface ( ctx - > bqPlayerObject , SL_IID_PLAY , & ( ctx - > bqPlayerPlay ) ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " OOPS, could not get the play interface : %lu " , ( unsigned long ) result ) ;
2015-07-11 21:37:41 +00:00
break ;
}
// get the buffer queue interface
result = ( * ( ctx - > bqPlayerObject ) ) - > GetInterface ( ctx - > bqPlayerObject , SL_IID_BUFFERQUEUE , & ( ctx - > bqPlayerBufferQueue ) ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " OOPS, could not get the BufferQueue play interface : %lu " , ( unsigned long ) result ) ;
2015-07-11 21:37:41 +00:00
break ;
}
// register callback on the buffer queue
result = ( * ( ctx - > bqPlayerBufferQueue ) ) - > RegisterCallback ( ctx - > bqPlayerBufferQueue , bqPlayerCallback , ctx ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " OOPS, could not register BufferQueue callback : %lu " , ( unsigned long ) result ) ;
2015-07-11 21:37:41 +00:00
break ;
}
2015-06-26 04:32:37 +00:00
( * audio_context ) - > _internal = ctx ;
( * audio_context ) - > CreateSoundBuffer = & opensl_createSoundBuffer ;
( * audio_context ) - > DestroySoundBuffer = & opensl_destroySoundBuffer ;
2015-07-11 21:37:41 +00:00
LOG ( " Successfully created OpenSLES engine and buffer queue " ) ;
2015-06-26 04:32:37 +00:00
} while ( 0 ) ;
if ( result ! = SL_RESULT_SUCCESS ) {
if ( ctx ) {
2015-09-12 22:04:09 +00:00
AudioContext_s * ctxPtr = calloc ( 1 , sizeof ( AudioContext_s ) ) ;
2015-06-26 04:32:37 +00:00
ctxPtr - > _internal = ctx ;
opensles_systemShutdown ( & ctxPtr ) ;
}
assert ( * audio_context = = NULL ) ;
LOG ( " OpenSLES sound output disabled " ) ;
}
return result ;
}
// pause all audio
2015-07-11 21:37:41 +00:00
static long opensles_systemPause ( AudioContext_s * audio_context ) {
2015-07-08 04:32:43 +00:00
LOG ( " OpenSLES pausing play " ) ;
2015-06-26 04:32:37 +00:00
2015-07-11 21:37:41 +00:00
EngineContext_s * ctx = ( EngineContext_s * ) ( audio_context - > _internal ) ;
SLresult result = ( * ( ctx - > bqPlayerPlay ) ) - > SetPlayState ( ctx - > bqPlayerPlay , SL_PLAYSTATE_PAUSED ) ;
2015-06-26 04:32:37 +00:00
return 0 ;
}
2015-07-11 21:37:41 +00:00
static long opensles_systemResume ( AudioContext_s * audio_context ) {
2015-07-08 04:32:43 +00:00
LOG ( " OpenSLES resuming play " ) ;
2015-07-25 19:41:50 +00:00
SLuint32 state = 0 ;
2015-07-11 21:37:41 +00:00
EngineContext_s * ctx = ( EngineContext_s * ) ( audio_context - > _internal ) ;
2015-07-25 19:41:50 +00:00
SLresult result = ( * ( ctx - > bqPlayerPlay ) ) - > GetPlayState ( ctx - > bqPlayerPlay , & state ) ;
2015-06-26 04:32:37 +00:00
2015-07-25 19:41:50 +00:00
do {
if ( result ! = SL_RESULT_SUCCESS ) {
2015-09-06 20:55:17 +00:00
ERRLOG ( " OOPS, could not get source state when attempting to resume : %lu " , ( unsigned long ) result ) ;
2015-07-25 19:41:50 +00:00
break ;
}
2015-09-07 04:03:59 +00:00
# if !TESTING
2015-07-25 19:41:50 +00:00
assert ( state ! = SL_PLAYSTATE_PLAYING & & " mismatch between systemPause/systemResume " ) ;
2015-09-07 04:03:59 +00:00
# endif
2015-07-25 19:41:50 +00:00
if ( state = = SL_PLAYSTATE_PAUSED ) {
// Balanced resume OK here
SLresult result = ( * ( ctx - > bqPlayerPlay ) ) - > SetPlayState ( ctx - > bqPlayerPlay , SL_PLAYSTATE_PLAYING ) ;
2015-09-07 04:03:59 +00:00
# if !TESTING
2015-07-25 19:41:50 +00:00
} else {
// Do not resume for stopped state, let this get forced from CPU/speaker thread otherwise we starve. (The
// stopped state happens if user dynamically changed buffer parameters in menu settings which triggered an
// OpenSLES destroy/re-initialization ... e.g. audio_setLatency() was invoked)
assert ( state = = SL_PLAYSTATE_STOPPED ) ;
2015-09-07 04:03:59 +00:00
# endif
2015-07-25 19:41:50 +00:00
}
} while ( 0 ) ;
return result ;
2015-06-26 04:32:37 +00:00
}
__attribute__ ( ( constructor ( CTOR_PRIORITY_EARLY ) ) )
static void _init_opensl ( void ) {
LOG ( " Initializing OpenSLES sound system " ) ;
assert ( audio_backend = = NULL & & " there can only be one! " ) ;
opensles_audio_backend . setup = & opensles_systemSetup ;
opensles_audio_backend . shutdown = & opensles_systemShutdown ;
opensles_audio_backend . pause = & opensles_systemPause ;
opensles_audio_backend . resume = & opensles_systemResume ;
audio_backend = & opensles_audio_backend ;
}