quakequest/Projects/Android/jni/darkplaces/snd_opensl.c
Simon 605d1edb6e Several Sweeeet fixes...
- Audio lag is now fixed - using the OpenSLES library (courtesy of Emile Belanger's Dark Places build for Android)
- 120hz mode can be used on the Quest 2 by adding the following to the command line:
``` -r 120 ```
2021-12-19 20:32:37 +00:00

399 lines
11 KiB
C

/*
Copyright (C) 2004 Andreas Kirsch
This program 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 Foundation; either version 2
of the License, or (at your option) any later version.
This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "quakedef.h"
#include <math.h>
#include "snd_main.h"
//Updated by Emile Belanger for OpenSL
// for native audio
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#include <pthread.h>
static unsigned int sdlaudiotime = 0;
// engine interfaces
static SLObjectItf engineObject = NULL;
static SLEngineItf engineEngine;
// output mix interfaces
static SLObjectItf outputMixObject = NULL;
// buffer queue player interfaces
static SLObjectItf bqPlayerObject = NULL;
static SLPlayItf bqPlayerPlay;
#ifdef ANDROID_NDK
static SLAndroidSimpleBufferQueueItf bqPlayerBufferQueue;
#else
static SLBufferQueueItf bqPlayerBufferQueue;
#endif
static SLEffectSendItf bqPlayerEffectSend;
static SLMuteSoloItf bqPlayerMuteSolo;
static SLVolumeItf bqPlayerVolume;
void assert(int v,const char * message)
{
// if (!v)
// LOGI("ASSERT: %s",message);
}
/*
// Note: SDL calls SDL_LockAudio() right before this function, so no need to lock the audio data here
static void Buffer_Callback (void *userdata, unsigned char *stream, int len)
{
unsigned int factor, RequestedFrames, MaxFrames, FrameCount;
unsigned int StartOffset, EndOffset;
factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
if ((unsigned int)len % factor != 0)
Sys_Error("SDL sound: invalid buffer length passed to Buffer_Callback (%d bytes)\n", len);
RequestedFrames = (unsigned int)len / factor;
if (SndSys_LockRenderBuffer())
{
if (snd_usethreadedmixing)
{
S_MixToBuffer(stream, RequestedFrames);
if (snd_blocked)
memset(stream, snd_renderbuffer->format.width == 1 ? 0x80 : 0, len);
SndSys_UnlockRenderBuffer();
return;
}
// Transfert up to a chunk of samples from snd_renderbuffer to stream
MaxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
if (MaxFrames > RequestedFrames)
FrameCount = RequestedFrames;
else
FrameCount = MaxFrames;
StartOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
EndOffset = (snd_renderbuffer->startframe + FrameCount) % snd_renderbuffer->maxframes;
if (StartOffset > EndOffset) // if the buffer wraps
{
unsigned int PartialLength1, PartialLength2;
PartialLength1 = (snd_renderbuffer->maxframes - StartOffset) * factor;
memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], PartialLength1);
PartialLength2 = FrameCount * factor - PartialLength1;
memcpy(&stream[PartialLength1], &snd_renderbuffer->ring[0], PartialLength2);
}
else
memcpy(stream, &snd_renderbuffer->ring[StartOffset * factor], FrameCount * factor);
snd_renderbuffer->startframe += FrameCount;
if (FrameCount < RequestedFrames && developer_insane.integer && vid_activewindow)
Con_DPrintf("SDL sound: %u sample frames missing\n", RequestedFrames - FrameCount);
sdlaudiotime += RequestedFrames;
SndSys_UnlockRenderBuffer();
}
}
*/
#define OPENSL_BUFF_LEN 1024
static unsigned char play_buffer[OPENSL_BUFF_LEN];
//NOTE!! There are definetly threading issues with this, but it appears to work for now...
void bqPlayerCallback(SLAndroidSimpleBufferQueueItf bq, void *context)
{
//LOGI("bqPlayerCallback");
unsigned int factor, RequestedFrames, MaxFrames, FrameCount;
unsigned int StartOffset, EndOffset;
factor = snd_renderbuffer->format.channels * snd_renderbuffer->format.width;
RequestedFrames = (unsigned int)OPENSL_BUFF_LEN / factor;
if (SndSys_LockRenderBuffer())
{
if (snd_usethreadedmixing)
{
S_MixToBuffer(play_buffer, RequestedFrames);
if (snd_blocked)
memset(play_buffer, snd_renderbuffer->format.width == 1 ? 0x80 : 0, OPENSL_BUFF_LEN);
(*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, play_buffer,OPENSL_BUFF_LEN);
SndSys_UnlockRenderBuffer();
return;
}
// Transfert up to a chunk of samples from snd_renderbuffer to stream
MaxFrames = snd_renderbuffer->endframe - snd_renderbuffer->startframe;
if (MaxFrames > RequestedFrames)
FrameCount = RequestedFrames;
else
FrameCount = MaxFrames;
StartOffset = snd_renderbuffer->startframe % snd_renderbuffer->maxframes;
EndOffset = (snd_renderbuffer->startframe + FrameCount) % snd_renderbuffer->maxframes;
if (StartOffset > EndOffset) // if the buffer wraps
{
unsigned int PartialLength1, PartialLength2;
PartialLength1 = (snd_renderbuffer->maxframes - StartOffset) * factor;
memcpy(play_buffer, &snd_renderbuffer->ring[StartOffset * factor], PartialLength1);
PartialLength2 = FrameCount * factor - PartialLength1;
memcpy(&play_buffer[PartialLength1], &snd_renderbuffer->ring[0], PartialLength2);
}
else
memcpy(play_buffer, &snd_renderbuffer->ring[StartOffset * factor], FrameCount * factor);
snd_renderbuffer->startframe += FrameCount;
if (FrameCount < RequestedFrames && developer_insane.integer && vid_activewindow)
Con_DPrintf("SDL sound: %u sample frames missing\n", RequestedFrames - FrameCount);
sdlaudiotime += RequestedFrames;
SndSys_UnlockRenderBuffer();
}
SLresult result;
//LOGI("Frame count = %d",FrameCount);
if (FrameCount == 0)
FrameCount = 1;
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, play_buffer,FrameCount * factor);
assert(SL_RESULT_SUCCESS == result,"Enqueue failed");
}
/*
====================
SndSys_Init
Create "snd_renderbuffer" with the proper sound format if the call is successful
May return a suggested format if the requested format isn't available
====================
*/
qboolean SndSys_Init (const snd_format_t* requested, snd_format_t* suggested)
{
unsigned int buffersize;
snd_threaded = false;
Con_DPrint ("SndSys_Init: using the SDL module\n");
buffersize = CeilPowerOf2((unsigned int)ceil((double)requested->speed / 25.0)); // 2048 bytes on 24kHz to 48kHz
Con_Printf("Wanted audio Specification:\n"
"\tChannels : %i\n"
"\tFormat : 0x%X\n"
"\tFrequency : %i\n"
"\tSamples : %i\n",
requested->channels, requested->width, requested->speed, buffersize);
//Opensl only does 44100
if (suggested != NULL)
{
suggested->channels = 2;
suggested->width = 2;
suggested->speed = 44100;
}
SLresult result;
// create engine
result = slCreateEngine(&engineObject, 0, NULL, 0, NULL, NULL);
assert(SL_RESULT_SUCCESS == result,"slCreateEngine");
// realize the engine
result = (*engineObject)->Realize(engineObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result,"Realize");
// get the engine interface, which is needed in order to create other objects
result = (*engineObject)->GetInterface(engineObject, SL_IID_ENGINE, &engineEngine);
assert(SL_RESULT_SUCCESS == result,"GetInterface");
// create output mix
result = (*engineEngine)->CreateOutputMix(engineEngine, &outputMixObject, 0, NULL, NULL);
assert(SL_RESULT_SUCCESS == result,"CreateOutputMix");
// realize the output mix
result = (*outputMixObject)->Realize(outputMixObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result,"Realize output mix");
//CREATE THE PLAYER
// configure audio source
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, 1};
SLDataFormat_PCM format_pcm = {SL_DATAFORMAT_PCM, 2, SL_SAMPLINGRATE_44_1,
SL_PCMSAMPLEFORMAT_FIXED_16, SL_PCMSAMPLEFORMAT_FIXED_16,
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT, SL_BYTEORDER_LITTLEENDIAN};
SLDataSource audioSrc = {&loc_bufq, &format_pcm};
// configure audio sink
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, outputMixObject};
SLDataSink audioSnk = {&loc_outmix, NULL};
// create audio player
// LOGI("create audio player");
const SLInterfaceID ids[1] = {SL_IID_ANDROIDSIMPLEBUFFERQUEUE};
const SLboolean req[1] = {SL_BOOLEAN_TRUE};
result = (*engineEngine)->CreateAudioPlayer(engineEngine, &bqPlayerObject, &audioSrc, &audioSnk,
1, ids, req);
assert(SL_RESULT_SUCCESS == result,"CreateAudioPlayer");
// realize the player
result = (*bqPlayerObject)->Realize(bqPlayerObject, SL_BOOLEAN_FALSE);
assert(SL_RESULT_SUCCESS == result,"Realize AudioPlayer");
// get the play interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_PLAY, &bqPlayerPlay);
assert(SL_RESULT_SUCCESS == result,"GetInterface AudioPlayer");
// get the buffer queue interface
result = (*bqPlayerObject)->GetInterface(bqPlayerObject, SL_IID_BUFFERQUEUE,
&bqPlayerBufferQueue);
assert(SL_RESULT_SUCCESS == result,"GetInterface buffer queue");
// register callback on the buffer queue
result = (*bqPlayerBufferQueue)->RegisterCallback(bqPlayerBufferQueue, bqPlayerCallback, NULL);
assert(SL_RESULT_SUCCESS == result,"RegisterCallback");
// set the player's state to playing
result = (*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_PLAYING);
assert(SL_RESULT_SUCCESS == result,"SetPlayState");
snd_threaded = true;
snd_renderbuffer = Snd_CreateRingBuffer(requested, 0, NULL);
if (snd_channellayout.integer == SND_CHANNELLAYOUT_AUTO)
Cvar_SetValueQuick (&snd_channellayout, SND_CHANNELLAYOUT_STANDARD);
sdlaudiotime = 0;
result = (*bqPlayerBufferQueue)->Enqueue(bqPlayerBufferQueue, "\0", 1);
assert(SL_RESULT_SUCCESS == result,"Enqueue first buffer");
return true;
}
/*
====================
SndSys_Shutdown
Stop the sound card, delete "snd_renderbuffer" and free its other resources
====================
*/
void SndSys_Shutdown(void)
{
/* Make sure player is stopped */
(*bqPlayerPlay)->SetPlayState(bqPlayerPlay, SL_PLAYSTATE_STOPPED);
/* Destroy the player */
(*engineObject)->Destroy(engineObject);
/* Destroy Output Mix object */
(*outputMixObject)->Destroy(outputMixObject);
if (snd_renderbuffer != NULL)
{
Mem_Free(snd_renderbuffer->ring);
Mem_Free(snd_renderbuffer);
snd_renderbuffer = NULL;
}
}
/*
====================
SndSys_Submit
Submit the contents of "snd_renderbuffer" to the sound card
====================
*/
void SndSys_Submit (void)
{
// Nothing to do here (this sound module is callback-based)
}
/*
====================
SndSys_GetSoundTime
Returns the number of sample frames consumed since the sound started
====================
*/
unsigned int SndSys_GetSoundTime (void)
{
return sdlaudiotime;
}
/*
====================
SndSys_LockRenderBuffer
Get the exclusive lock on "snd_renderbuffer"
====================
*/
qboolean SndSys_LockRenderBuffer (void)
{
//SDL_LockAudio();
return true;
}
/*
====================
SndSys_UnlockRenderBuffer
Release the exclusive lock on "snd_renderbuffer"
====================
*/
void SndSys_UnlockRenderBuffer (void)
{
//SDL_UnlockAudio();
}
/*
====================
SndSys_SendKeyEvents
Send keyboard events originating from the sound system (e.g. MIDI)
====================
*/
void SndSys_SendKeyEvents(void)
{
// not supported
}