diff --git a/android/native/jni/Android.mk b/android/native/jni/Android.mk index 4dd0206445..6dfcf89a97 100644 --- a/android/native/jni/Android.mk +++ b/android/native/jni/Android.mk @@ -4,6 +4,7 @@ LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := retroarch-activity +HAVE_OPENSL := 0 RARCH_PATH := ../../.. LOCAL_SRC_FILES = $(RARCH_PATH)/retroarch.c \ @@ -43,11 +44,19 @@ LOCAL_SRC_FILES = $(RARCH_PATH)/retroarch.c \ $(RARCH_PATH)/thread.c \ main.c + LOCAL_CFLAGS = -marm -DANDROID -DHAVE_DYNAMIC -DHAVE_OPENGL -DHAVE_OPENGLES -DHAVE_OPENGLES2 -DGLSL_DEBUG -DHAVE_GLSL -DHAVE_ZLIB -DINLINE=inline -DLSB_FIRST -D__LIBRETRO__ -DHAVE_CONFIGFILE=1 -DPACKAGE_VERSION=\"$(RARCH_VERSION)\" -std=gnu99 + LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -landroid -lEGL -lGLESv2 -llog -ldl -lz LOCAL_STATIC_LIBRARIES := android_native_app_glue +ifeq ($(HAVE_OPENSL), 1) +LOCAL_SRC_FILES += $(RARCH_PATH)/audio/opensl.c +LOCAL_CFLAGS += -DHAVE_SL +LOCAL_LDLIBS += -lOpenSLES +endif + include $(BUILD_SHARED_LIBRARY) $(call import-module,android/native_app_glue) diff --git a/audio/opensl.c b/audio/opensl.c new file mode 100644 index 0000000000..322e4b680f --- /dev/null +++ b/audio/opensl.c @@ -0,0 +1,321 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * + * RetroArch 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 Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch 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 RetroArch. + * If not, see . + */ + +#include "../driver.h" +#include "../general.h" + +#include +#ifdef ANDROID +#include +#define ISLDataLocator_BufferQueue SLDataLocator_AndroidSimpleBufferQueue +#define ISL_DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE +#else +#define ISLDataLocator_BufferQueue SLDataLocator_BufferQueue +#define ISL_DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_BUFFERQUEUE +#endif + +#define MAX_NUMBER_INTERFACES 3 + +#include +#include + +typedef struct CallbackCntxt_ +{ + SLPlayItf playItf; + SLint16* pDataBase; /* Base address of local audio data storage */ + SLint16* pData; /* Current address of local audio data storage */ + SLuint32 size; +} CallbackCntxt; + +typedef struct sl +{ + SLObjectItf sl; + SLresult res_ptr; + SLEngineItf EngineItf; + CallbackCntxt cntxt; + SLDataSource audioSource; + ISLDataLocator_BufferQueue bufferQueue; + SLDataFormat_PCM pcm; + SLDataSink audioSink; + SLDataLocator_OutputMix locator_outputmix; + SLObjectItf player; + SLPlayItf playItf; + SLBufferQueueItf bufferQueueItf; + SLBufferQueueState state; + SLObjectItf OutputMix; + SLVolumeItf volumeItf; + SLboolean required[MAX_NUMBER_INTERFACES]; + SLInterfaceID iidArray[MAX_NUMBER_INTERFACES]; + bool nonblock; +} sl_t; + +/* Local stoarge for audio data in 16 bit words */ +#define AUDIO_DATA_STORAGE_SIZE 4096 +/* Audio data buffer size in 16 bit words, 8 data segments are used in this example */ +#define AUDIO_DATA_BUFFER_SIZE 4096/8 + +/* Local storage for Audio data */ +SLint16 pcmData[AUDIO_DATA_STORAGE_SIZE]; + +static inline void sl_generate_error_msg(const char * funcname, SLresult res) +{ + RARCH_ERR("%s failed, error code: [%d].\n", funcname, res); +} + +void BufferQueueCallback(SLBufferQueueItf queueItf, void *pContext) +{ + SLresult res; + CallbackCntxt *pCntxt = (CallbackCntxt*)pContext; + + if(pCntxt->pData < (pCntxt->pDataBase + pCntxt->size)) + { + res = (*queueItf)->Enqueue(queueItf, (void*)pCntxt->pData, + 2 * AUDIO_DATA_BUFFER_SIZE); /* Size given in bytes */ + + if(res != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("queueItf->Enqueue()", res); + return; + } + + pCntxt->pData += AUDIO_DATA_BUFFER_SIZE; + } +} + +static void sl_init_extended(sl_t *sl_ctx) +{ + int i; + + /* Get the SL Engine Interface */ + + sl_ctx->res_ptr = (*sl_ctx->sl)->GetInterface(sl_ctx->sl, SL_IID_ENGINE, (void*)&sl_ctx->EngineItf); + + if(sl_ctx->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("sl_ctx->sl->GetInterface(SL_IID_ENGINE)", sl_ctx->res_ptr); + return; + } + + /* Initialize arrays required[] and iidArray[] */ + for (i = 0; i < MAX_NUMBER_INTERFACES; i++) + { + sl_ctx->required[i] = SL_BOOLEAN_FALSE; + sl_ctx->iidArray[i] = SL_IID_NULL; + } + + /* Unsupported feature on Android NDK it seems */ + + /* Set arrays required[] and iidArray[] for VOLUME interface */ + sl_ctx->required[0] = SL_BOOLEAN_TRUE; + sl_ctx->iidArray[0] = SL_IID_VOLUME; + + /* Create Output Mix object to be used by player */ + sl_ctx->res_ptr = (*sl_ctx->EngineItf)->CreateOutputMix(sl_ctx->EngineItf, &sl_ctx->OutputMix, 1, + sl_ctx->iidArray, sl_ctx->required); + + if(sl_ctx->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("EngineItf->CreateOutputMix", sl_ctx->res_ptr); + return; + } + + /* Realizing the Output Mix object in synchonous mode */ + sl_ctx->res_ptr = (*sl_ctx->OutputMix)->Realize(sl_ctx->OutputMix, SL_BOOLEAN_FALSE); + + if(sl_ctx->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("OutputMix->Realize(SL_BOOLEAN_FALSE)", sl_ctx->res_ptr); + return; + } + + /* Setup the data source structure for the buffer queue */ + sl_ctx->bufferQueue.locatorType = ISL_DATALOCATOR_BUFFERQUEUE; + sl_ctx->bufferQueue.numBuffers = 2; + + /* Setup the format of the content in the buffer queue */ + sl_ctx->pcm.formatType = SL_DATAFORMAT_PCM; + sl_ctx->pcm.numChannels = 2; + sl_ctx->pcm.samplesPerSec = SL_SAMPLINGRATE_44_1; + sl_ctx->pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; + sl_ctx->pcm.containerSize = 16; + sl_ctx->pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; + sl_ctx->pcm.endianness = SL_BYTEORDER_LITTLEENDIAN; + + sl_ctx->audioSource.pFormat = (void*)&sl_ctx->pcm; + sl_ctx->audioSource.pLocator = (void*)&sl_ctx->bufferQueue; + + /* Setup the data sink structure */ + sl_ctx->locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX; + sl_ctx->locator_outputmix.outputMix = sl_ctx->OutputMix; + sl_ctx->audioSink.pLocator = (void*)&sl_ctx->locator_outputmix; + sl_ctx->audioSink.pFormat = NULL; + + /* Initialize the context for buffer queue callbacks */ + sl_ctx->cntxt.pDataBase = (void*)&pcmData; + sl_ctx->cntxt.pData = sl_ctx->cntxt.pDataBase; + sl_ctx->cntxt.size = sizeof(pcmData); + + /* Set arrays required[] and iidArray[] for SEEK interface + * (PlayItf)is implicit) */ + sl_ctx->required[0] = SL_BOOLEAN_TRUE; + sl_ctx->iidArray[0] = SL_IID_BUFFERQUEUE; + + /* Create the music player */ + sl_ctx->res_ptr = (*sl_ctx->EngineItf)->CreateAudioPlayer(sl_ctx->EngineItf, &sl_ctx->player, + &sl_ctx->audioSource, &sl_ctx->audioSink, 1, sl_ctx->iidArray, sl_ctx->required); + + if(sl_ctx->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("sl_ctx->EngineItf->CreateAudioPlayer()", sl_ctx->res_ptr); + return; + } + + /* Realizing the player in synchronous mode. */ + sl_ctx->res_ptr = (*sl_ctx->player)->Realize(sl_ctx->player, SL_BOOLEAN_FALSE); + + if(sl_ctx->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("sl_ctx->player->Realize(SL_BOOLEAN_FALSE)", sl_ctx->res_ptr); + return; + } + + /* Get seek and play interfaces */ + sl_ctx->res_ptr = (*sl_ctx->player)->GetInterface(sl_ctx->player, SL_IID_PLAY, (void*)&sl_ctx->playItf); + + if(sl_ctx->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("sl_ctx->player->GetInterface(SL_IID_PLAY)", sl_ctx->res_ptr); + return; + } + + sl_ctx->res_ptr = (*sl_ctx->player)->GetInterface(sl_ctx->player, SL_IID_BUFFERQUEUE, + (void*)&sl_ctx->bufferQueueItf); + + if(sl_ctx->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("sl_ctx->player->GetInterface(SL_IID_BUFFERQUEUE)", sl_ctx->res_ptr); + return; + } + + /* Setup to receive buffer queue event callbacks */ + sl_ctx->res_ptr = (*sl_ctx->bufferQueueItf)->RegisterCallback(sl_ctx->bufferQueueItf, + BufferQueueCallback, NULL); + + if(sl_ctx->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("sl_ctx->bufferQueueItf->RegisterCallback(BufferQueueCallback)", sl_ctx->res_ptr); + return; + } + + /* Before we start set volume to -3dB (-300mB) */ + sl_ctx->res_ptr = (*sl_ctx->volumeItf)->SetVolumeLevel(sl_ctx->volumeItf, -300); + + if(sl_ctx->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("sl_ctx->volumeItf->SetVolumeLevel(-300)", sl_ctx->res_ptr); + return; + } +} + +static void *sl_init(const char *device, unsigned rate, unsigned latency) +{ + (void)device; + sl_t *sl = (sl_t*)calloc(1, sizeof(sl_t)); + if (!sl) + goto error; + + SLEngineOption EngineOption[] = + { + (SLuint32) SL_ENGINEOPTION_THREADSAFE, + (SLuint32) SL_BOOLEAN_TRUE + }; + + sl->res_ptr = slCreateEngine(&sl->sl, 1, EngineOption, 0, NULL, NULL); + + if(sl->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("sl_ctx->sl->GetInterface(SL_IID_ENGINE)", sl->res_ptr); + goto error; + } + + /* Realizing the SL Engine in synchronous mode */ + sl->res_ptr = (*sl->sl)->Realize(sl->sl, SL_BOOLEAN_FALSE); + + if(sl->res_ptr != SL_RESULT_SUCCESS) + { + sl_generate_error_msg("sl->sl->Realize(SL_BOOLEAN_FALSE)", sl->res_ptr); + goto error; + } + + sl_init_extended(sl); + + return sl; + +error: + if (sl) + { + (*sl->sl)->Destroy(sl->sl); + free(sl); + } + return NULL; +} + +static bool sl_stop(void *data) +{ + (void)data; + return true; +} + +static void sl_set_nonblock_state(void *data, bool state) +{ + sl_t *sl = (sl_t*)data; + sl->nonblock = state; +} + +static bool sl_start(void *data) +{ + (void)data; + return true; +} + +static void sl_free(void *data) +{ + sl_t *sl = (sl_t*)data; + if (sl) + { + (*sl->sl)->Destroy(sl->sl); + } + free(sl); +} + +static ssize_t sl_write(void *data, const void *buf, size_t size) +{ + (void)data; + (void)buf; + (void)size; + + return 0; +} + +const audio_driver_t audio_opensl = { + sl_init, + sl_write, + sl_stop, + sl_start, + sl_set_nonblock_state, + sl_free, + NULL, + "opensl" +}; diff --git a/config.def.h b/config.def.h index a35344f6b9..85b8d6b395 100644 --- a/config.def.h +++ b/config.def.h @@ -45,6 +45,7 @@ enum AUDIO_ALSA, AUDIO_ROAR, AUDIO_AL, + AUDIO_SL, AUDIO_JACK, AUDIO_SDL, AUDIO_XAUDIO, @@ -112,6 +113,8 @@ enum #define AUDIO_DEFAULT_DRIVER AUDIO_COREAUDIO #elif defined(HAVE_AL) #define AUDIO_DEFAULT_DRIVER AUDIO_AL +#elif defined(HAVE_SL) +#define AUDIO_DEFAULT_DRIVER AUDIO_SL #elif defined(HAVE_DSOUND) #define AUDIO_DEFAULT_DRIVER AUDIO_DSOUND #elif defined(HAVE_SDL) diff --git a/driver.c b/driver.c index 2a193697e4..000fa75350 100644 --- a/driver.c +++ b/driver.c @@ -46,6 +46,9 @@ static const audio_driver_t *audio_drivers[] = { #ifdef HAVE_AL &audio_openal, #endif +#ifdef HAVE_SL + &audio_opensl, +#endif #ifdef HAVE_ROAR &audio_roar, #endif diff --git a/driver.h b/driver.h index de1447e193..3061950af9 100644 --- a/driver.h +++ b/driver.h @@ -259,6 +259,7 @@ extern const audio_driver_t audio_oss; extern const audio_driver_t audio_alsa; extern const audio_driver_t audio_roar; extern const audio_driver_t audio_openal; +extern const audio_driver_t audio_opensl; extern const audio_driver_t audio_jack; extern const audio_driver_t audio_sdl; extern const audio_driver_t audio_xa; diff --git a/settings.c b/settings.c index 034f5eb8c6..54301bfebf 100644 --- a/settings.c +++ b/settings.c @@ -49,6 +49,8 @@ const char *config_get_default_audio(void) return "coreaudio"; case AUDIO_AL: return "openal"; + case AUDIO_SL: + return "opensl"; case AUDIO_SDL: return "sdl"; case AUDIO_DSOUND: