#include "quakedef.h" #include #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 };