You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
305 lines
8.5 KiB
305 lines
8.5 KiB
/* |
|
Copyright (C) 2015 SiPlus, Chasseur de bots |
|
|
|
This program is free software; you can redistribute it and/or |
|
modify it under the terms of the GNU General Public License |
|
as published by the Free Software Foundation; either version 2 |
|
of the License, or (at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. |
|
|
|
See the GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program; if not, write to the Free Software |
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
|
|
|
*/ |
|
|
|
#include "common.h" |
|
#include "platform/platform.h" |
|
#if XASH_SOUND == SOUND_OPENSLES |
|
#include <SLES/OpenSLES.h> |
|
#include "pthread.h" |
|
#include "sound.h" |
|
|
|
extern convar_t *s_primary; |
|
extern dma_t dma; |
|
|
|
static SLObjectItf snddma_android_engine = NULL; |
|
static SLObjectItf snddma_android_outputMix = NULL; |
|
static SLObjectItf snddma_android_player = NULL; |
|
static SLBufferQueueItf snddma_android_bufferQueue; |
|
static SLPlayItf snddma_android_play; |
|
|
|
static pthread_mutex_t snddma_android_mutex = PTHREAD_MUTEX_INITIALIZER; |
|
|
|
static int snddma_android_pos; |
|
static int snddma_android_size; |
|
|
|
static const SLInterfaceID *pSL_IID_ENGINE; |
|
static const SLInterfaceID *pSL_IID_BUFFERQUEUE; |
|
static const SLInterfaceID *pSL_IID_PLAY; |
|
static SLresult SLAPIENTRY (*pslCreateEngine)( |
|
SLObjectItf *pEngine, |
|
SLuint32 numOptions, |
|
const SLEngineOption *pEngineOptions, |
|
SLuint32 numInterfaces, |
|
const SLInterfaceID *pInterfaceIds, |
|
const SLboolean * pInterfaceRequired |
|
); |
|
|
|
void SNDDMA_Activate( qboolean active ) |
|
{ |
|
if( !dma.initialized ) |
|
return; |
|
|
|
if( active ) |
|
{ |
|
memset( dma.buffer, 0, snddma_android_size * 2 ); |
|
(*snddma_android_bufferQueue)->Enqueue( snddma_android_bufferQueue, dma.buffer, snddma_android_size ); |
|
(*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_PLAYING ); |
|
} |
|
else |
|
{ |
|
//if( s_globalfocus->integer ) |
|
//return; |
|
(*snddma_android_play)->SetPlayState( snddma_android_play, SL_PLAYSTATE_STOPPED ); |
|
(*snddma_android_bufferQueue)->Clear( snddma_android_bufferQueue ); |
|
} |
|
} |
|
|
|
static void SNDDMA_Android_Callback( SLBufferQueueItf bq, void *context ) |
|
{ |
|
uint8_t *buffer2; |
|
|
|
pthread_mutex_lock( &snddma_android_mutex ); |
|
|
|
buffer2 = ( uint8_t * )dma.buffer + snddma_android_size; |
|
(*bq)->Enqueue( bq, buffer2, snddma_android_size ); |
|
memcpy( buffer2, dma.buffer, snddma_android_size ); |
|
memset( dma.buffer, 0, snddma_android_size ); |
|
snddma_android_pos += dma.samples; |
|
|
|
pthread_mutex_unlock( &snddma_android_mutex ); |
|
} |
|
|
|
static const char *SNDDMA_Android_Init( void ) |
|
{ |
|
SLresult result; |
|
|
|
SLEngineItf engine; |
|
|
|
int freq; |
|
|
|
SLDataLocator_BufferQueue sourceLocator; |
|
SLDataFormat_PCM sourceFormat; |
|
SLDataSource source; |
|
|
|
SLDataLocator_OutputMix sinkLocator; |
|
SLDataSink sink; |
|
|
|
SLInterfaceID interfaceID; |
|
SLboolean interfaceRequired; |
|
|
|
int samples; |
|
void *handle = dlopen( "libOpenSLES.so", RTLD_LAZY ); |
|
|
|
if( !handle ) |
|
return "dlopen for libOpenSLES.so"; |
|
|
|
pslCreateEngine = dlsym( handle, "slCreateEngine" ); |
|
|
|
if( !pslCreateEngine ) |
|
return "resolve slCreateEngine"; |
|
|
|
pSL_IID_ENGINE = dlsym( handle, "SL_IID_ENGINE" ); |
|
|
|
if( !pSL_IID_ENGINE ) |
|
return "resolve SL_IID_ENGINE"; |
|
|
|
pSL_IID_PLAY = dlsym( handle, "SL_IID_PLAY" ); |
|
|
|
if( !pSL_IID_PLAY ) |
|
return "resolve SL_IID_PLAY"; |
|
|
|
pSL_IID_BUFFERQUEUE = dlsym( handle, "SL_IID_BUFFERQUEUE" ); |
|
|
|
if( !pSL_IID_BUFFERQUEUE ) |
|
return "resolve SL_IID_BUFFERQUEUE"; |
|
|
|
|
|
result = pslCreateEngine( &snddma_android_engine, 0, NULL, 0, NULL, NULL ); |
|
if( result != SL_RESULT_SUCCESS ) return "slCreateEngine"; |
|
result = (*snddma_android_engine)->Realize( snddma_android_engine, SL_BOOLEAN_FALSE ); |
|
if( result != SL_RESULT_SUCCESS ) return "engine->Realize"; |
|
result = (*snddma_android_engine)->GetInterface( snddma_android_engine, *pSL_IID_ENGINE, &engine ); |
|
if( result != SL_RESULT_SUCCESS ) return "engine->GetInterface(ENGINE)"; |
|
|
|
result = (*engine)->CreateOutputMix( engine, &snddma_android_outputMix, 0, NULL, NULL ); |
|
if( result != SL_RESULT_SUCCESS ) return "engine->CreateOutputMix"; |
|
result = (*snddma_android_outputMix)->Realize( snddma_android_outputMix, SL_BOOLEAN_FALSE ); |
|
if( result != SL_RESULT_SUCCESS ) return "outputMix->Realize"; |
|
|
|
freq = SOUND_DMA_SPEED; |
|
sourceLocator.locatorType = SL_DATALOCATOR_BUFFERQUEUE; |
|
sourceLocator.numBuffers = 2; |
|
sourceFormat.formatType = SL_DATAFORMAT_PCM; |
|
sourceFormat.numChannels = 2; // always stereo, because engine supports only stereo |
|
sourceFormat.samplesPerSec = freq * 1000; |
|
sourceFormat.bitsPerSample = 16; // always 16 bit audio |
|
sourceFormat.containerSize = sourceFormat.bitsPerSample; |
|
sourceFormat.channelMask = SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT; |
|
sourceFormat.endianness = SL_BYTEORDER_LITTLEENDIAN; |
|
source.pLocator = &sourceLocator; |
|
source.pFormat = &sourceFormat; |
|
|
|
sinkLocator.locatorType = SL_DATALOCATOR_OUTPUTMIX; |
|
sinkLocator.outputMix = snddma_android_outputMix; |
|
sink.pLocator = &sinkLocator; |
|
sink.pFormat = NULL; |
|
|
|
interfaceID = *pSL_IID_BUFFERQUEUE; |
|
interfaceRequired = SL_BOOLEAN_TRUE; |
|
|
|
result = (*engine)->CreateAudioPlayer( engine, &snddma_android_player, &source, &sink, 1, &interfaceID, &interfaceRequired ); |
|
if( result != SL_RESULT_SUCCESS ) return "engine->CreateAudioPlayer"; |
|
result = (*snddma_android_player)->Realize( snddma_android_player, SL_BOOLEAN_FALSE ); |
|
if( result != SL_RESULT_SUCCESS ) return "player->Realize"; |
|
result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_BUFFERQUEUE, &snddma_android_bufferQueue ); |
|
if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(BUFFERQUEUE)"; |
|
result = (*snddma_android_player)->GetInterface( snddma_android_player, *pSL_IID_PLAY, &snddma_android_play ); |
|
if( result != SL_RESULT_SUCCESS ) return "player->GetInterface(PLAY)"; |
|
result = (*snddma_android_bufferQueue)->RegisterCallback( snddma_android_bufferQueue, SNDDMA_Android_Callback, NULL ); |
|
if( result != SL_RESULT_SUCCESS ) return "bufferQueue->RegisterCallback"; |
|
|
|
samples = s_samplecount->value; |
|
if( !samples ) |
|
samples = 4096; |
|
|
|
dma.format.channels = sourceFormat.numChannels; |
|
dma.samples = samples * sourceFormat.numChannels; |
|
dma.format.speed = freq; |
|
snddma_android_size = dma.samples * ( sourceFormat.bitsPerSample >> 3 ); |
|
dma.buffer = Z_Malloc( snddma_android_size * 2 ); |
|
dma.samplepos = 0; |
|
// dma.sampleframes = dma.samples / dma.format.channels; |
|
dma.format.width = 2; |
|
if( !dma.buffer ) return "malloc"; |
|
|
|
//snddma_android_mutex = trap_Mutex_Create(); |
|
|
|
snddma_android_pos = 0; |
|
dma.initialized = true; |
|
|
|
SNDDMA_Activate( true ); |
|
|
|
return NULL; |
|
} |
|
|
|
qboolean SNDDMA_Init( void ) |
|
{ |
|
const char *initError; |
|
|
|
Msg( "OpenSL ES audio device initializing...\n" ); |
|
|
|
initError = SNDDMA_Android_Init(); |
|
if( initError ) |
|
{ |
|
Msg( S_ERROR "SNDDMA_Init: %s failed.\n", initError ); |
|
SNDDMA_Shutdown(); |
|
return false; |
|
} |
|
|
|
Msg( "OpenSL ES audio initialized.\n" ); |
|
|
|
return true; |
|
} |
|
|
|
int SNDDMA_GetDMAPos( void ) |
|
{ |
|
return snddma_android_pos; |
|
} |
|
|
|
void SNDDMA_Shutdown( void ) |
|
{ |
|
Msg( "Closing OpenSL ES audio device...\n" ); |
|
|
|
if( snddma_android_player ) |
|
{ |
|
(*snddma_android_player)->Destroy( snddma_android_player ); |
|
snddma_android_player = NULL; |
|
} |
|
if( snddma_android_outputMix ) |
|
{ |
|
(*snddma_android_outputMix)->Destroy( snddma_android_outputMix ); |
|
snddma_android_outputMix = NULL; |
|
} |
|
if( snddma_android_engine ) |
|
{ |
|
(*snddma_android_engine)->Destroy( snddma_android_engine ); |
|
snddma_android_engine = NULL; |
|
} |
|
|
|
if( dma.buffer ) |
|
{ |
|
Z_Free( dma.buffer ); |
|
dma.buffer = NULL; |
|
} |
|
|
|
//if( snddma_android_mutex ) |
|
//trap_Mutex_Destroy( &snddma_android_mutex ); |
|
|
|
Msg( "OpenSL ES audio device shut down.\n" ); |
|
} |
|
|
|
void SNDDMA_Submit( void ) |
|
{ |
|
pthread_mutex_unlock( &snddma_android_mutex ); |
|
} |
|
|
|
void SNDDMA_BeginPainting( void ) |
|
{ |
|
pthread_mutex_lock( &snddma_android_mutex ); |
|
} |
|
|
|
|
|
/* |
|
============== |
|
SNDDMA_GetSoundtime |
|
|
|
update global soundtime |
|
=============== |
|
*/ |
|
int SNDDMA_GetSoundtime( void ) |
|
{ |
|
static int buffers, oldsamplepos; |
|
int samplepos, fullsamples; |
|
|
|
fullsamples = dma.samples / 2; |
|
|
|
// it is possible to miscount buffers |
|
// if it has wrapped twice between |
|
// calls to S_Update. Oh well. |
|
samplepos = SNDDMA_GetDMAPos(); |
|
|
|
if( samplepos < oldsamplepos ) |
|
{ |
|
buffers++; // buffer wrapped |
|
|
|
if( paintedtime > 0x40000000 ) |
|
{ |
|
// time to chop things off to avoid 32 bit limits |
|
buffers = 0; |
|
paintedtime = fullsamples; |
|
S_StopAllSounds( true ); |
|
} |
|
} |
|
|
|
oldsamplepos = samplepos; |
|
|
|
return (buffers * fullsamples + samplepos / 2); |
|
} |
|
#endif
|
|
|