2012-10-18 03:45:21 +00:00
|
|
|
/* RetroArch - A frontend for libretro.
|
|
|
|
* Copyright (C) 2010-2012 - Hans-Kristian Arntzen
|
2012-10-18 04:06:55 +00:00
|
|
|
* Copyright (C) 2011-2012 - Daniel De Matteis
|
2012-10-18 03:45:21 +00:00
|
|
|
*
|
|
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "../driver.h"
|
|
|
|
#include "../general.h"
|
|
|
|
|
|
|
|
#include <SLES/OpenSLES.h>
|
|
|
|
#ifdef ANDROID
|
|
|
|
#include <SLES/OpenSLES_Android.h>
|
|
|
|
#define ISLDataLocator_BufferQueue SLDataLocator_AndroidSimpleBufferQueue
|
|
|
|
#define ISL_DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
|
2012-10-18 05:28:56 +00:00
|
|
|
#define ISL_IID_BUFFERQUEUE SL_IID_ANDROIDSIMPLEBUFFERQUEUE
|
2012-10-18 03:45:21 +00:00
|
|
|
#else
|
|
|
|
#define ISLDataLocator_BufferQueue SLDataLocator_BufferQueue
|
|
|
|
#define ISL_DATALOCATOR_BUFFERQUEUE SL_DATALOCATOR_BUFFERQUEUE
|
2012-10-18 05:28:56 +00:00
|
|
|
#define ISL_IID_BUFFERQUEUE SL_IID_BUFFERQUEUE
|
2012-10-18 03:45:21 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
#include <string.h>
|
|
|
|
|
|
|
|
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;
|
|
|
|
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];
|
|
|
|
|
|
|
|
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)
|
2012-10-18 05:33:45 +00:00
|
|
|
RARCH_WARN("queueItf->Enqueue() encountered a problem.\n");
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
pCntxt->pData += AUDIO_DATA_BUFFER_SIZE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
static void *sl_init(const char *device, unsigned rate, unsigned latency)
|
2012-10-18 03:45:21 +00:00
|
|
|
{
|
2012-10-18 05:28:56 +00:00
|
|
|
(void)device;
|
|
|
|
sl_t *sl = (sl_t*)calloc(1, sizeof(sl_t));
|
|
|
|
if (!sl)
|
|
|
|
goto error;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
SLEngineOption EngineOption[] =
|
|
|
|
{
|
|
|
|
(SLuint32) SL_ENGINEOPTION_THREADSAFE,
|
|
|
|
(SLuint32) SL_BOOLEAN_TRUE
|
|
|
|
};
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->res_ptr = slCreateEngine(&sl->sl, 1, EngineOption, 0, NULL, NULL);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
/* 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)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* Get interface for IID_ENGINE */
|
|
|
|
|
|
|
|
sl->res_ptr = (*sl->sl)->GetInterface(sl->sl, SL_IID_ENGINE, (void*)&sl->EngineItf);
|
|
|
|
|
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
/* VOLUME interface */
|
|
|
|
|
|
|
|
/* NOTES by Google:
|
|
|
|
* Please see section "Supported features from OpenSL ES 1.0.1",
|
|
|
|
* subsection "Objects and interfaces" in <NDKroot>/docs/opensles/index.html
|
|
|
|
* You'll see that the cell for object Output mix interface Volume is white.
|
|
|
|
* This means it is not a supported combination.
|
|
|
|
* As a workaround, use the Volume interface on the Audio player object. */
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
/* This seems to fail right now:
|
|
|
|
* libOpenSLES(19315): class OutputMix interface 0 requested but unavailable MPH=43
|
|
|
|
*/
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
const SLInterfaceID ids[] = {SL_IID_VOLUME};
|
|
|
|
const SLboolean req[] = {SL_BOOLEAN_FALSE};
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
/* Create Output Mix object to be used by player */
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->res_ptr = (*sl->EngineItf)->CreateOutputMix(sl->EngineItf, &sl->OutputMix, 1,
|
|
|
|
ids, req);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
/* Realizing the Output Mix object in synchonous mode */
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->res_ptr = (*sl->OutputMix)->Realize(sl->OutputMix, SL_BOOLEAN_FALSE);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:51:57 +00:00
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
|
|
|
#if 0
|
|
|
|
/* Get interface for IID_VOLUME */
|
|
|
|
sl->res_ptr = (*sl->OutputMix)->GetInterface(sl->OutputMix, SL_IID_VOLUME,
|
|
|
|
(void*)&sl->volumeItf);
|
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/* TODO: hardcode for now, make this use the parameters in some way later */
|
|
|
|
SLuint32 rate_sl = SL_SAMPLINGRATE_48;
|
|
|
|
SLuint8 bits_sl = 16;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
/* Setup the data source structure for the buffer queue */
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->bufferQueue.locatorType = ISL_DATALOCATOR_BUFFERQUEUE;
|
|
|
|
sl->bufferQueue.numBuffers = 2;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
/* Setup the format of the content in the buffer queue */
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->pcm.formatType = SL_DATAFORMAT_PCM;
|
|
|
|
sl->pcm.numChannels = 2;
|
|
|
|
sl->pcm.samplesPerSec = rate_sl;
|
|
|
|
sl->pcm.bitsPerSample = bits_sl;
|
|
|
|
sl->pcm.containerSize = bits_sl;
|
|
|
|
sl->pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
|
|
|
|
sl->pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->audioSource.pFormat = (void*)&sl->pcm;
|
|
|
|
sl->audioSource.pLocator = (void*)&sl->bufferQueue;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
/* Setup the data sink structure */
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
|
|
|
|
sl->locator_outputmix.outputMix = sl->OutputMix;
|
|
|
|
sl->audioSink.pLocator = (void*)&sl->locator_outputmix;
|
|
|
|
sl->audioSink.pFormat = NULL;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
/* Initialize the context for buffer queue callbacks */
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->cntxt.pDataBase = (void*)&pcmData;
|
|
|
|
sl->cntxt.pData = sl->cntxt.pDataBase;
|
|
|
|
sl->cntxt.size = sizeof(pcmData);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:51:57 +00:00
|
|
|
/* Initialize audio data to silence */
|
|
|
|
memset(pcmData, 0, sizeof(pcmData));
|
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
const SLInterfaceID ids1[] = {ISL_IID_BUFFERQUEUE};
|
|
|
|
const SLboolean req1[] = {SL_BOOLEAN_TRUE};
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
/* Create the music player */
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->res_ptr = (*sl->EngineItf)->CreateAudioPlayer(sl->EngineItf, &sl->player,
|
|
|
|
&sl->audioSource, &sl->audioSink, 1, ids1, req1);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
/* Realizing the player in synchronous mode. */
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->res_ptr = (*sl->player)->Realize(sl->player, SL_BOOLEAN_FALSE);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
/* Get interface for IID_PLAY */
|
|
|
|
sl->res_ptr = (*sl->player)->GetInterface(sl->player, SL_IID_PLAY, (void*)&sl->playItf);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
/* Get interface for IID_BUFFERQUEUE */
|
|
|
|
sl->res_ptr = (*sl->player)->GetInterface(sl->player, ISL_IID_BUFFERQUEUE,
|
|
|
|
(void*)&sl->bufferQueueItf);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
/* Setup to receive buffer queue event callbacks */
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->res_ptr = (*sl->bufferQueueItf)->RegisterCallback(sl->bufferQueueItf,
|
2012-10-18 03:45:21 +00:00
|
|
|
BufferQueueCallback, NULL);
|
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
2012-10-18 03:45:21 +00:00
|
|
|
goto error;
|
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
#if 0
|
|
|
|
/* Before we start set volume to -3dB (-300mB) */
|
|
|
|
/* TODO: Necessary or not? Let's not do this for now */
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->res_ptr = (*sl->volumeItf)->SetVolumeLevel(sl->volumeItf, -300);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
2012-10-18 05:28:56 +00:00
|
|
|
#endif
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
/* Setup for audio playback */
|
|
|
|
|
|
|
|
sl->res_ptr = (*sl->playItf)->SetPlayState(sl->playItf, SL_PLAYSTATE_PLAYING);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
goto error;
|
|
|
|
|
|
|
|
return sl;
|
|
|
|
|
|
|
|
error:
|
2012-10-18 05:28:56 +00:00
|
|
|
RARCH_ERR("Couldn't initialize OpenSL ES driver, error code: [%d].\n", sl->res_ptr);
|
2012-10-18 03:45:21 +00:00
|
|
|
if (sl)
|
|
|
|
{
|
|
|
|
(*sl->sl)->Destroy(sl->sl);
|
|
|
|
free(sl);
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool sl_stop(void *data)
|
|
|
|
{
|
2012-10-18 05:28:56 +00:00
|
|
|
sl_t *sl = (sl_t*)data;
|
|
|
|
|
|
|
|
/* Stop player */
|
|
|
|
sl->res_ptr = (*sl->playItf)->SetPlayState(sl->playItf, SL_PLAYSTATE_STOPPED);
|
|
|
|
|
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
RARCH_ERR("SetPlayState (SL_PLAYSTATE_STOPPED) failed.\n");
|
|
|
|
|
2012-10-18 03:45:21 +00:00
|
|
|
(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)
|
|
|
|
{
|
2012-10-18 05:28:56 +00:00
|
|
|
sl_t *sl = (sl_t*)data;
|
|
|
|
|
|
|
|
sl->res_ptr = (*sl->playItf)->SetPlayState(sl->playItf, SL_PLAYSTATE_PLAYING);
|
|
|
|
|
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
RARCH_ERR("SetPlayState (SL_PLAYSTATE_PLAYING) failed.\n");
|
|
|
|
|
2012-10-18 03:45:21 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void sl_free(void *data)
|
|
|
|
{
|
|
|
|
sl_t *sl = (sl_t*)data;
|
|
|
|
if (sl)
|
|
|
|
{
|
2012-10-18 05:28:56 +00:00
|
|
|
sl->res_ptr = (*sl->playItf)->SetPlayState(sl->playItf, SL_PLAYSTATE_STOPPED);
|
2012-10-18 03:45:21 +00:00
|
|
|
(*sl->sl)->Destroy(sl->sl);
|
|
|
|
}
|
|
|
|
free(sl);
|
|
|
|
}
|
|
|
|
|
|
|
|
static ssize_t sl_write(void *data, const void *buf, size_t size)
|
|
|
|
{
|
2012-10-18 05:28:56 +00:00
|
|
|
/* not sure about where to use buf, what to return */
|
|
|
|
sl_t *sl = (sl_t*)data;
|
|
|
|
|
|
|
|
sl->res_ptr = (*sl->bufferQueueItf)->GetState(sl->bufferQueueItf, &sl->state);
|
|
|
|
|
|
|
|
if(sl->res_ptr != SL_RESULT_SUCCESS)
|
|
|
|
RARCH_ERR("GetState() failed.\n");
|
|
|
|
|
|
|
|
while(sl->state.count)
|
|
|
|
(*sl->bufferQueueItf)->GetState(sl->bufferQueueItf, &sl->state);
|
2012-10-18 03:45:21 +00:00
|
|
|
|
2012-10-18 05:28:56 +00:00
|
|
|
return size;
|
2012-10-18 03:45:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const audio_driver_t audio_opensl = {
|
|
|
|
sl_init,
|
|
|
|
sl_write,
|
|
|
|
sl_stop,
|
|
|
|
sl_start,
|
|
|
|
sl_set_nonblock_state,
|
|
|
|
sl_free,
|
|
|
|
NULL,
|
|
|
|
"opensl"
|
|
|
|
};
|