aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJack Palevich <jackpal@google.com>2011-05-12 15:57:05 +0800
committerJack Palevich <jackpal@google.com>2011-05-12 15:57:05 +0800
commitc0b4ff48355cee26a511b4fe783dfaa9e2710fe4 (patch)
tree798241ed2c22d119bf154e68de3ecce324ca9592
parent7ea20d0cc523eea9a2f849ab53f205b7de79ca85 (diff)
downloadquake-c0b4ff48355cee26a511b4fe783dfaa9e2710fe4.tar.gz
Make Quake use OpenSL ES audio.
Change-Id: I86dea625956385d14b36129437507f91dedfbf34
-rw-r--r--Android.mk7
-rw-r--r--quake/src/WinQuake/snd_android.cpp397
2 files changed, 397 insertions, 7 deletions
diff --git a/Android.mk b/Android.mk
index 1ae82df..85c284b 100644
--- a/Android.mk
+++ b/Android.mk
@@ -91,11 +91,14 @@ LOCAL_SRC_FILES:= \
world.cpp \
zone.cpp
+LOCAL_C_INCLUDES:= \
+ system/media/wilhelm/include
+
LOCAL_SHARED_LIBRARIES := \
libutils \
- libmedia \
libEGL \
- libGLESv1_CM
+ libGLESv1_CM \
+ libOpenSLES
LOCAL_MODULE := libquake
diff --git a/quake/src/WinQuake/snd_android.cpp b/quake/src/WinQuake/snd_android.cpp
index 7072b8d..1327023 100644
--- a/quake/src/WinQuake/snd_android.cpp
+++ b/quake/src/WinQuake/snd_android.cpp
@@ -10,14 +10,399 @@
#include <time.h>
#include <math.h>
#include <stdlib.h>
-#include <utils/Log.h>
-#include <media/AudioTrack.h>
+#include <unistd.h>
-#include <hardware/audio.h>
+#include <android/log.h>
+#include <SLES/OpenSLES.h>
-using namespace android;
+#define LOG_TAG "Quake snd_android"
+#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
+#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
-static AudioTrack gAudioTrack;
+const size_t SAMPLE_RATE = 11025;
+
+
+const size_t BYTES_PER_SAMPLE = 2;
+const size_t CHANNEL_COUNT = 2;
+const size_t BITS_PER_SAMPLE = 8 * BYTES_PER_SAMPLE;
+
+const size_t TOTAL_BUFFER_SIZE = 4 * 1024;
+
+#define MAX_NUMBER_INTERFACES 3
+
+/* Local storage for Audio data in 16 bit words */
+#define AUDIO_DATA_STORAGE_SIZE (TOTAL_BUFFER_SIZE / 2)
+/* Audio data buffer size in 16 bit words. 8 data segments are used in
+this simple example */
+#define AUDIO_DATA_BUFFER_SIZE (4096/8)
+
+const size_t NUMBER_OF_BUFFERS = AUDIO_DATA_STORAGE_SIZE / AUDIO_DATA_BUFFER_SIZE;
+
+/* Checks for error. If any errors exit the application! */
+void CheckErr( SLresult res )
+{
+ if ( res != SL_RESULT_SUCCESS )
+ {
+ fprintf(stdout, "%u SL failure, exiting\n", res);
+ exit(EXIT_FAILURE);
+ }
+ else {
+ //fprintf(stdout, "%d SL success, proceeding...\n", res);
+ }
+}
+
+/* Structure for passing information to callback function */
+typedef struct CallbackCntxt_ {
+ SLPlayItf playItf;
+ SLint16* pDataBase; // Base adress of local audio data storage
+ SLint16* pData; // Current adress of local audio data storage
+ SLuint32 size;
+} CallbackCntxt;
+
+/* Local storage for Audio data */
+SLint16 pcmData[AUDIO_DATA_STORAGE_SIZE];
+
+/* Callback for Buffer Queue events */
+void BufferQueueCallback(
+ SLBufferQueueItf queueItf,
+ void *pContext)
+{
+ //fprintf(stdout, "BufferQueueCallback called\n");
+ SLresult res;
+ //fprintf(stdout, " pContext=%p\n", pContext);
+ CallbackCntxt *pCntxt = (CallbackCntxt*)pContext;
+
+ if (pCntxt->pData >= (pCntxt->pDataBase + pCntxt->size)) {
+ pCntxt->pData = pCntxt->pDataBase;
+ }
+ {
+ //fprintf(stdout, "callback: before enqueue\n");
+ res = (*queueItf)->Enqueue(queueItf, (void*) pCntxt->pData,
+ 2 * AUDIO_DATA_BUFFER_SIZE); /* Size given in bytes. */
+ CheckErr(res);
+ /* Increase data pointer by buffer size */
+ pCntxt->pData += AUDIO_DATA_BUFFER_SIZE;
+ }
+ //fprintf(stdout, "end of BufferQueueCallback()\n");
+}
+
+SLEngineItf EngineItf;
+
+SLint32 numOutputs = 0;
+SLuint32 deviceID = 0;
+
+
+SLDataSource audioSource;
+SLDataLocator_BufferQueue bufferQueue;
+SLDataFormat_PCM pcm;
+
+SLDataSink audioSink;
+SLDataLocator_OutputMix locator_outputmix;
+
+
+SLVolumeItf volumeItf;
+
+
+SLboolean required[MAX_NUMBER_INTERFACES];
+SLInterfaceID iidArray[MAX_NUMBER_INTERFACES];
+
+/* Callback context for the buffer queue callback function */
+CallbackCntxt cntxt;
+
+static SLObjectItf OutputMix;
+static SLPlayItf playItf;
+static SLObjectItf player;
+static SLBufferQueueItf bufferQueueItf;
+static SLBufferQueueState state;
+
+/* Play some audio from a buffer queue */
+void TestPlaySawtoothBufferQueue( SLObjectItf sl )
+{
+ SLresult res;
+ int i;
+
+ /* Get the SL Engine Interface which is implicit */
+ res = (*sl)->GetInterface(sl, SL_IID_ENGINE, (void*)&EngineItf);
+ CheckErr(res);
+
+ /* Initialize arrays required[] and iidArray[] */
+ for (i=0;i<MAX_NUMBER_INTERFACES;i++)
+ {
+ required[i] = SL_BOOLEAN_FALSE;
+ iidArray[i] = SL_IID_NULL;
+ }
+
+ // Set arrays required[] and iidArray[] for VOLUME interface
+ required[0] = SL_BOOLEAN_TRUE;
+ iidArray[0] = SL_IID_VOLUME;
+ // Create Output Mix object to be used by player
+ res = (*EngineItf)->CreateOutputMix(EngineItf, &OutputMix, 0,
+ iidArray, required); CheckErr(res);
+
+ // Realizing the Output Mix object in synchronous mode.
+ res = (*OutputMix)->Realize(OutputMix, SL_BOOLEAN_FALSE);
+ CheckErr(res);
+
+#if 0
+ res = (*OutputMix)->GetInterface(OutputMix, SL_IID_VOLUME,
+ (void*)&volumeItf); CheckErr(res);
+#endif
+
+ /* Setup the data source structure for the buffer queue */
+ bufferQueue.locatorType = SL_DATALOCATOR_BUFFERQUEUE;
+ bufferQueue.numBuffers = 4; /* Four buffers in our buffer queue */
+
+ /* Setup the format of the content in the buffer queue */
+ pcm.formatType = SL_DATAFORMAT_PCM;
+ pcm.numChannels = 2;
+ pcm.samplesPerSec = SL_SAMPLINGRATE_11_025;
+ pcm.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16;
+ pcm.containerSize = 16;
+ pcm.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT;
+ pcm.endianness = SL_BYTEORDER_LITTLEENDIAN;
+
+ audioSource.pFormat = (void *)&pcm;
+ audioSource.pLocator = (void *)&bufferQueue;
+
+ /* Setup the data sink structure */
+ locator_outputmix.locatorType = SL_DATALOCATOR_OUTPUTMIX;
+ locator_outputmix.outputMix = OutputMix;
+ audioSink.pLocator = (void *)&locator_outputmix;
+ audioSink.pFormat = NULL;
+
+ /* Initialize the audio data to silence */
+ memset(pcmData, 0, sizeof(pcmData));
+
+ /* Initialize the context for Buffer queue callbacks */
+ cntxt.pDataBase = /*(void*)&*/pcmData;
+ cntxt.pData = cntxt.pDataBase;
+ cntxt.size = sizeof(pcmData) / 2;
+
+ /* Set arrays required[] and iidArray[] for SEEK interface
+ (PlayItf is implicit) */
+ required[0] = SL_BOOLEAN_TRUE;
+ iidArray[0] = SL_IID_BUFFERQUEUE;
+
+ /* Create the music player */
+ res = (*EngineItf)->CreateAudioPlayer(EngineItf, &player,
+ &audioSource, &audioSink, 1, iidArray, required); CheckErr(res);
+ fprintf(stdout, "bufferQueue example: after CreateAudioPlayer\n");
+
+ /* Realizing the player in synchronous mode. */
+ res = (*player)->Realize(player, SL_BOOLEAN_FALSE); CheckErr(res);
+ fprintf(stdout, "bufferQueue example: after Realize\n");
+
+ /* Get seek and play interfaces */
+ res = (*player)->GetInterface(player, SL_IID_PLAY, (void*)&playItf);
+ CheckErr(res);
+ fprintf(stdout, "bufferQueue example: after GetInterface(PLAY)\n");
+
+ res = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE,
+ (void*)&bufferQueueItf); CheckErr(res);
+
+ /* Setup to receive buffer queue event callbacks */
+ res = (*bufferQueueItf)->RegisterCallback(bufferQueueItf,
+ BufferQueueCallback, &cntxt); CheckErr(res);
+
+#if 0
+ /* Before we start set volume to -3dB (-300mB) */
+ res = (*volumeItf)->SetVolumeLevel(volumeItf, -300); CheckErr(res);
+#endif
+
+ /* Enqueue a few buffers to get the ball rolling */
+ res = (*bufferQueueItf)->Enqueue(bufferQueueItf, cntxt.pData,
+ 2 * AUDIO_DATA_BUFFER_SIZE); /* Size given in bytes. */
+ CheckErr(res);
+ cntxt.pData += AUDIO_DATA_BUFFER_SIZE;
+
+ res = (*bufferQueueItf)->Enqueue(bufferQueueItf, cntxt.pData,
+ 2 * AUDIO_DATA_BUFFER_SIZE); /* Size given in bytes. */
+ CheckErr(res);
+ cntxt.pData += AUDIO_DATA_BUFFER_SIZE;
+
+ res = (*bufferQueueItf)->Enqueue(bufferQueueItf, cntxt.pData,
+ 2 * AUDIO_DATA_BUFFER_SIZE); /* Size given in bytes. */
+ CheckErr(res);
+ cntxt.pData += AUDIO_DATA_BUFFER_SIZE;
+
+ /* Play the PCM samples using a buffer queue */
+ fprintf(stdout, "bufferQueue example: starting to play\n");
+ res = (*playItf)->SetPlayState( playItf, SL_PLAYSTATE_PLAYING );
+ CheckErr(res);
+
+ /* Wait until the PCM data is done playing, the buffer queue callback
+ will continue to queue buffers until the entire PCM data has been
+ played. This is indicated by waiting for the count member of the
+ SLBufferQueueState to go to zero.
+ */
+ res = (*bufferQueueItf)->GetState(bufferQueueItf, &state);
+ CheckErr(res);
+
+#if 0
+ // while (state.playIndex < 100) {
+ while (state.count) {
+ usleep(10000);
+ (*bufferQueueItf)->GetState(bufferQueueItf, &state);
+ }
+
+ #endif
+}
+
+SLObjectItf gSoundEngine;
+
+int startAndroidSound()
+{
+ SLresult res;
+
+ SLEngineOption EngineOption[] = {
+ {(SLuint32) SL_ENGINEOPTION_THREADSAFE,
+ (SLuint32) SL_BOOLEAN_TRUE}};
+
+ res = slCreateEngine( &gSoundEngine, 1, EngineOption, 0, NULL, NULL);
+ CheckErr(res);
+ /* Realizing the SL Engine in synchronous mode. */
+ res = (*gSoundEngine)->Realize(gSoundEngine, SL_BOOLEAN_FALSE); CheckErr(res);
+
+ /* Run the test */
+ TestPlaySawtoothBufferQueue(gSoundEngine);
+ return EXIT_SUCCESS;
+}
+
+void finishAndroidSound()
+{
+ SLresult res;
+
+ if (gSoundEngine == NULL) {
+ return;
+ }
+
+ /* Make sure player is stopped */
+ if (playItf != NULL) {
+ res = (*playItf)->SetPlayState(playItf, SL_PLAYSTATE_STOPPED);
+ CheckErr(res);
+ playItf = NULL;
+ }
+
+ if (player != NULL) {
+ /* Destroy the player */
+ (*player)->Destroy(player);
+ player = NULL;
+ }
+
+ if (OutputMix != NULL) {
+ /* Destroy Output Mix object */
+ (*OutputMix)->Destroy(OutputMix);
+ OutputMix = NULL;
+ }
+
+ /* Shutdown OpenSL ES */
+ (*gSoundEngine)->Destroy(gSoundEngine);
+ gSoundEngine = NULL;
+}
+
+#if 1
+
+/*
+==================
+SNDDMA_Init
+
+Try to find a sound device to mix for.
+Returns false if nothing is found.
+==================
+*/
+qboolean SNDDMA_Init(void)
+{
+ // Initialize Quake's idea of a DMA buffer.
+
+ shm = &sn;
+ memset((void*)&sn, 0, sizeof(sn));
+
+ shm->splitbuffer = false; // Not used.
+ shm->samplebits = 16;
+ shm->speed = 11025;
+ shm->channels = 2;
+ shm->samples = TOTAL_BUFFER_SIZE / BYTES_PER_SAMPLE;
+ shm->samplepos = 0; // Not used.
+ shm->buffer = (unsigned char*) pcmData;
+ shm->submission_chunk = 1; // Not used.
+
+ shm->soundalive = true;
+
+ if ( (shm->samples & 0x1ff) != 0 ) {
+ LOGE("SNDDDMA_Init: samples must be power of two.");
+ return false;
+ }
+
+ if ( shm->buffer == 0 ) {
+ LOGE("SNDDDMA_Init: Could not allocate sound buffer.");
+ return false;
+ }
+
+ int result = startAndroidSound();
+ return result == EXIT_SUCCESS;
+}
+
+/*
+==============
+SNDDMA_GetDMAPos
+
+return the current sample position (in mono samples read)
+inside the recirculating dma buffer, so the mixing code will know
+how many sample are required to fill it up.
+===============
+*/
+int SNDDMA_GetDMAPos(void)
+{
+ SLresult res;
+ if (bufferQueueItf != NULL) {
+ res = (*bufferQueueItf)->GetState(bufferQueueItf, &state);
+ CheckErr(res);
+ // Index of the currently playing or filling buffer.
+ SLuint32 playIndex = state.playIndex;
+ int ringIndex = playIndex % NUMBER_OF_BUFFERS;
+ return ringIndex * AUDIO_DATA_BUFFER_SIZE;
+ }
+ return 0;
+}
+
+/*
+===============
+SNDDMA_ReportWrite
+
+Report valid data being written into the DMA buffer by the sound mixing code.
+This is an Android specific API.
+================
+*/
+void SNDDMA_ReportWrite(size_t lengthBytes) {
+ // TODO: keep track of how much of the sound ring buffer has sound in it,
+ // detect starvation, and mix silence when we are starving.
+}
+
+/*
+==============
+SNDDMA_Submit
+
+Send sound to device if buffer isn't really the dma buffer
+===============
+*/
+void SNDDMA_Submit(void)
+{
+}
+
+/*
+==============
+SNDDMA_Shutdown
+
+Reset the sound device for exiting
+===============
+*/
+void SNDDMA_Shutdown(void)
+{
+ finishAndroidSound();
+}
+
+
+#else
// Written by the callback function running in an audio thread.
// index in bytes of where we last read.
@@ -349,3 +734,5 @@ void SNDDMA_Shutdown(void)
{
gAudioTrack.stop();
}
+
+#endif