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: