190 lines
5.3 KiB
C
190 lines
5.3 KiB
C
|
#include "quakedef.h"
|
||
|
|
||
|
#include <SLES/OpenSLES.h>
|
||
|
|
||
|
#define AUDIODRIVERNAME "OpenSLES"
|
||
|
|
||
|
static void QDECL OSL_RegisterCvars(void)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void *OSL_Lock(soundcardinfo_t *sc, unsigned int *startoffset)
|
||
|
{
|
||
|
return sc->sn.buffer;
|
||
|
}
|
||
|
static void OSL_Unlock(soundcardinfo_t *sc, void *buffer)
|
||
|
{
|
||
|
//no need to do anything
|
||
|
}
|
||
|
static void OSL_Submit(soundcardinfo_t *sc, int start, int end)
|
||
|
{
|
||
|
//submit happens outside the mixer
|
||
|
}
|
||
|
static unsigned int OSL_GetDMAPos(soundcardinfo_t *sc)
|
||
|
{
|
||
|
sc->sn.samplepos = sc->snd_sent;
|
||
|
return sc->sn.samplepos;
|
||
|
}
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
SLObjectItf sl;
|
||
|
SLEngineItf engine;
|
||
|
SLPlayItf play;
|
||
|
SLObjectItf player;
|
||
|
SLObjectItf output;
|
||
|
SLBufferQueueItf bufferqueue;
|
||
|
|
||
|
|
||
|
unsigned char *buffer;
|
||
|
size_t buffersegmentsize;
|
||
|
size_t buffersegments;
|
||
|
size_t buffersegment;
|
||
|
} osl_data_t;
|
||
|
|
||
|
//assumption: that each buffer is released in sequence.
|
||
|
static void buffercallback(SLBufferQueueItf queue, void *ctx)
|
||
|
{
|
||
|
soundcardinfo_t *sc = ctx;
|
||
|
osl_data_t *p = sc->handle;
|
||
|
//we got the buffer back.
|
||
|
if (sc->Shutdown)
|
||
|
{ //we're not shutting down yet, so paint more stuff into the buffer and throw it back.
|
||
|
sc->sn.buffer = p->buffer + (p->buffersegment%p->buffersegments)*p->buffersegmentsize;
|
||
|
sc->sn.samples = p->buffersegmentsize/sc->sn.samplebytes;//numFramesAvailable * sc->sn.numchannels;
|
||
|
sc->samplequeue = sc->sn.samples;
|
||
|
S_MixerThread(sc);
|
||
|
sc->snd_sent = p->buffersegment++*p->buffersegmentsize;
|
||
|
|
||
|
(*queue)->Enqueue(queue, sc->sn.buffer, p->buffersegmentsize);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void OSL_Shutdown(soundcardinfo_t *sc)
|
||
|
{
|
||
|
osl_data_t *p = sc->handle;
|
||
|
sc->Shutdown = NULL; //stop posting new buffers
|
||
|
if (p)
|
||
|
{
|
||
|
if (p->play)
|
||
|
(*p->play)->SetPlayState(p->play, SL_PLAYSTATE_STOPPED);
|
||
|
if (p->player)
|
||
|
(*p->player)->Destroy(p->player);
|
||
|
if (p->output)
|
||
|
(*p->output)->Destroy(p->output);
|
||
|
if (p->sl)
|
||
|
(*p->sl)->Destroy(p->sl);
|
||
|
sc->handle = NULL;
|
||
|
Z_Free(p);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static qboolean QDECL OSL_InitCard (soundcardinfo_t *sc, const char *cardname)
|
||
|
{
|
||
|
osl_data_t *p;
|
||
|
size_t segments = 4; //lets cycle through 4 segments in our single logical buffer
|
||
|
size_t segmentframes = 256; //with X frames per segment
|
||
|
//FIXME: mixahead
|
||
|
|
||
|
sc->sn.numchannels = 2;
|
||
|
sc->sn.sampleformat = QSF_S16;
|
||
|
switch(sc->sn.sampleformat)
|
||
|
{
|
||
|
case QSF_U8:
|
||
|
//case QSF_S8;
|
||
|
sc->sn.samplebytes = 1;
|
||
|
break;
|
||
|
case QSF_S16:
|
||
|
sc->sn.samplebytes = 2;
|
||
|
break;
|
||
|
//case QSF_F32:
|
||
|
//sc->sn.samplebytes = 4;
|
||
|
//break;
|
||
|
default:
|
||
|
return false;
|
||
|
// sc->sn.sampleformat = QSF_INVALID;
|
||
|
// sc->sn.samplebytes = 0;
|
||
|
// break;
|
||
|
}
|
||
|
|
||
|
Con_DPrintf("Opening OpenSLES, %.1fkhz\n", sc->sn.speed/1000.0);
|
||
|
|
||
|
sc->Shutdown = OSL_Shutdown;
|
||
|
sc->Lock = OSL_Lock;
|
||
|
sc->Unlock = OSL_Unlock;
|
||
|
sc->Submit = OSL_Submit;
|
||
|
sc->GetDMAPos = OSL_GetDMAPos;
|
||
|
sc->handle = p = Z_Malloc(sizeof(*p) + segmentframes*segments*sc->sn.samplebytes*sc->sn.numchannels);
|
||
|
if (p)
|
||
|
{
|
||
|
p->buffer = (unsigned char*)(p+1);
|
||
|
p->buffersegments = segments;
|
||
|
p->buffersegmentsize = segmentframes*sc->sn.samplebytes*sc->sn.numchannels;
|
||
|
sc->selfpainting = true;
|
||
|
Q_strncpyz(sc->name, cardname?cardname:"", sizeof(sc->name));
|
||
|
|
||
|
SLEngineOption options[] =
|
||
|
{
|
||
|
{(SLuint32) SL_ENGINEOPTION_THREADSAFE, (SLuint32)SL_BOOLEAN_TRUE},
|
||
|
// {(SLuint32) SL_ENGINEOPTION_MAJORVERSION, (SLuint32)1},
|
||
|
// {(SLuint32) SL_ENGINEOPTION_MINORVERSION, (SLuint32)1},
|
||
|
};
|
||
|
slCreateEngine(&p->sl, countof(options), options, 0, NULL, NULL);
|
||
|
if (p->sl)
|
||
|
{
|
||
|
(*p->sl)->Realize(p->sl, SL_BOOLEAN_FALSE);
|
||
|
(*p->sl)->GetInterface(p->sl, SL_IID_ENGINE, &p->engine);
|
||
|
if (p->engine)
|
||
|
{
|
||
|
(*p->engine)->CreateOutputMix(p->engine, &p->output, 0, NULL, NULL);
|
||
|
if (p->output)
|
||
|
{
|
||
|
SLboolean TRUE_ = true;
|
||
|
SLDataLocator_BufferQueue loc_bufferqueue = {SL_DATALOCATOR_BUFFERQUEUE, segments};
|
||
|
SLDataFormat_PCM pcmformat = {SL_DATAFORMAT_PCM, sc->sn.numchannels, sc->sn.speed*1000, sc->sn.samplebytes*8, sc->sn.samplebytes*8/*+pad*/, SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN};
|
||
|
SLDataSource audiosource = {&loc_bufferqueue, &pcmformat};
|
||
|
SLDataLocator_OutputMix loc_outputmix = {SL_DATALOCATOR_OUTPUTMIX, p->output};
|
||
|
SLDataSink audiosink = {&loc_outputmix, NULL};
|
||
|
(*p->output)->Realize(p->output, false);
|
||
|
(*p->engine)->CreateAudioPlayer(p->engine, &p->player, &audiosource, &audiosink, 1, &SL_IID_BUFFERQUEUE, &TRUE_);
|
||
|
if (p->player)
|
||
|
{
|
||
|
(*p->player)->Realize(p->player, false);
|
||
|
(*p->player)->GetInterface(p->player, SL_IID_PLAY, &p->play);
|
||
|
(*p->player)->GetInterface(p->player, SL_IID_BUFFERQUEUE, &p->bufferqueue);
|
||
|
if (p->bufferqueue)
|
||
|
{
|
||
|
(*p->bufferqueue)->RegisterCallback(p->bufferqueue, buffercallback, sc);
|
||
|
buffercallback(p->bufferqueue, sc);
|
||
|
buffercallback(p->bufferqueue, sc);
|
||
|
buffercallback(p->bufferqueue, sc);
|
||
|
if (SL_RESULT_SUCCESS == (*p->play)->SetPlayState(p->play, SL_PLAYSTATE_PLAYING))
|
||
|
{
|
||
|
//should now be playing our buffers, and recycling them when one terminates.i
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
OSL_Shutdown(sc);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
static qboolean QDECL OSL_Enumerate (void (QDECL *callback) (const char *drivername, const char *devicecode, const char *readablename))
|
||
|
{
|
||
|
//callback(AUDIODRIVERNAME, internalname, nicename);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
sounddriver_t OSL_Output =
|
||
|
{
|
||
|
AUDIODRIVERNAME,
|
||
|
OSL_InitCard,
|
||
|
OSL_Enumerate,
|
||
|
OSL_RegisterCvars
|
||
|
};
|
||
|
|