[Audio] Move OpenSLES code in to Driver/OpenSLES.cpp

This commit is contained in:
zilmar 2017-09-13 17:41:52 +10:00
parent 78d89cde5a
commit c36d905bcb
7 changed files with 715 additions and 681 deletions

View File

@ -13,10 +13,12 @@ LOCAL_ARM_MODE := arm
LOCAL_C_INCLUDES :=
LOCAL_SRC_FILES := \
$(SRCDIR)/AudioSettings.cpp \
$(SRCDIR)/AudioMain.cpp \
$(SRCDIR)/trace.cpp \
LOCAL_SRC_FILES := \
$(SRCDIR)/Driver/OpenSLES.cpp \
$(SRCDIR)/Driver/SoundBase.cpp \
$(SRCDIR)/AudioMain.cpp \
$(SRCDIR)/AudioSettings.cpp \
$(SRCDIR)/trace.cpp \
LOCAL_CFLAGS := $(COMMON_CFLAGS)

View File

@ -13,10 +13,6 @@
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* *
****************************************************************************/
#ifdef ANDROID
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#endif
#include <Project64-audio/Driver/OpenSLES.h>
#include "audio_1.1.h"
#include "Version.h"
@ -27,83 +23,13 @@
#include "AudioMain.h"
#include "SettingsID.h"
#ifdef ANDROID
typedef struct threadLock_
{
pthread_mutex_t mutex;
pthread_cond_t cond;
volatile unsigned char value;
volatile unsigned char limit;
} threadLock;
#endif
bool g_AudioEnabled = true;
/* Read header for type definition */
AUDIO_INFO g_AudioInfo;
/* Pointer to the primary audio buffer */
uint8_t * g_primaryBuffer = NULL;
/* Size of the primary buffer */
uint32_t g_primaryBufferBytes = 0;
/* Size of the primary audio buffer in equivalent output samples */
unsigned int g_PrimaryBufferSize = PRIMARY_BUFFER_SIZE;
/* Pointer to secondary buffers */
uint8_t ** g_secondaryBuffers = NULL;
/* Size of a single secondary buffer */
uint32_t g_secondaryBufferBytes = 0;
/* Size of a single secondary audio buffer in output samples */
uint32_t g_SecondaryBufferSize = SECONDARY_BUFFER_SIZE;
/* Position in the primary buffer where next audio chunk should be placed */
uint32_t g_primaryBufferPos = 0;
/* Index of the next secondary buffer available */
uint32_t g_secondaryBufferIndex = 0;
/* Number of secondary buffers */
uint32_t g_SecondaryBufferNbr = SECONDARY_BUFFER_NBR;
/* Audio frequency, this is usually obtained from the game, but for compatibility we set default value */
uint32_t g_GameFreq = DEFAULT_FREQUENCY;
/* SpeedFactor is used to increase/decrease game playback speed */
uint32_t g_speed_factor = 100;
/* If this is true then left and right channels are swapped */
bool g_SwapChannels = false;
/* Output Audio frequency */
int g_OutputFreq = 44100;
/* Indicate that the audio plugin failed to initialize, so the emulator can keep running without sound */
bool g_critical_failure = false;
#ifdef ANDROID
/* Thread Lock */
threadLock g_lock;
/* Engine interfaces */
SLObjectItf g_engineObject = NULL;
SLEngineItf g_engineEngine = NULL;
/* Output mix interfaces */
SLObjectItf g_outputMixObject = NULL;
/* Player interfaces */
SLObjectItf g_playerObject = NULL;
SLPlayItf g_playerPlay = NULL;
/* Buffer queue interfaces */
SLAndroidSimpleBufferQueueItf g_bufferQueue = NULL;
#endif
bool g_PluginInit = false;
uint32_t g_Dacrate = 0;
OpenSLESDriver * g_SoundDriver = NULL;
@ -119,491 +45,6 @@ void PluginInit(void)
g_PluginInit = true;
}
/* This callback handler is called every time a buffer finishes playing */
#ifdef ANDROID
void queueCallback(SLAndroidSimpleBufferQueueItf caller, void *context)
{
threadLock *plock = (threadLock *)context;
pthread_mutex_lock(&(plock->mutex));
if (plock->value < plock->limit)
plock->value++;
pthread_cond_signal(&(plock->cond));
pthread_mutex_unlock(&(plock->mutex));
}
#endif
static void CloseAudio(void)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Start");
g_primaryBufferPos = 0;
g_secondaryBufferIndex = 0;
/* Delete Primary buffer */
if (g_primaryBuffer != NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Delete g_primaryBuffer (%p)", g_primaryBuffer);
g_primaryBufferBytes = 0;
delete[] g_primaryBuffer;
g_primaryBuffer = NULL;
}
/* Delete Secondary buffers */
if (g_secondaryBuffers != NULL)
{
for (uint32_t i = 0; i < g_SecondaryBufferNbr; i++)
{
if (g_secondaryBuffers[i] != NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Delete g_secondaryBuffers[%d] (%p)", i, g_secondaryBuffers[i]);
delete[] g_secondaryBuffers[i];
g_secondaryBuffers[i] = NULL;
}
}
g_secondaryBufferBytes = 0;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Delete g_secondaryBuffers (%p)", g_secondaryBuffers);
delete[] g_secondaryBuffers;
g_secondaryBuffers = NULL;
}
#ifdef ANDROID
/* Destroy buffer queue audio player object, and invalidate all associated interfaces */
if (g_playerObject != NULL)
{
SLuint32 state = SL_PLAYSTATE_PLAYING;
(*g_playerPlay)->SetPlayState(g_playerPlay, SL_PLAYSTATE_STOPPED);
while (state != SL_PLAYSTATE_STOPPED)
{
(*g_playerPlay)->GetPlayState(g_playerPlay, &state);
}
(*g_playerObject)->Destroy(g_playerObject);
g_playerObject = NULL;
g_playerPlay = NULL;
g_bufferQueue = NULL;
}
/* Destroy output mix object, and invalidate all associated interfaces */
if (g_outputMixObject != NULL)
{
(*g_outputMixObject)->Destroy(g_outputMixObject);
g_outputMixObject = NULL;
}
/* Destroy engine object, and invalidate all associated interfaces */
if (g_engineObject != NULL)
{
(*g_engineObject)->Destroy(g_engineObject);
g_engineObject = NULL;
g_engineEngine = NULL;
}
/* Destroy thread Locks */
pthread_cond_signal(&(g_lock.cond));
pthread_mutex_unlock(&(g_lock.mutex));
pthread_cond_destroy(&(g_lock.cond));
pthread_mutex_destroy(&(g_lock.mutex));
#endif
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
}
static bool CreatePrimaryBuffer(void)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Start");
unsigned int primaryBytes = (unsigned int)(g_PrimaryBufferSize * N64_SAMPLE_BYTES);
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Allocating memory for primary audio buffer: %i bytes.", primaryBytes);
g_primaryBuffer = new uint8_t[primaryBytes];
if (g_primaryBuffer == NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_primaryBuffer == NULL");
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done (res: false)");
return false;
}
memset(g_primaryBuffer, 0, primaryBytes);
g_primaryBufferBytes = primaryBytes;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done (res: True)");
return true;
}
static bool CreateSecondaryBuffers(void)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Start");
bool status = true;
unsigned int secondaryBytes = (unsigned int)(g_SecondaryBufferSize * SLES_SAMPLE_BYTES);
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Allocating memory for %d secondary audio buffers: %i bytes.", g_SecondaryBufferNbr, secondaryBytes);
/* Allocate number of secondary buffers */
g_secondaryBuffers = new uint8_t *[g_SecondaryBufferNbr];
if (g_secondaryBuffers == NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_secondaryBuffers == NULL");
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done (res: false)");
return false;
}
/* Allocate size of each secondary buffers */
for (uint32_t i = 0; i < g_SecondaryBufferNbr; i++)
{
g_secondaryBuffers[i] = new uint8_t[secondaryBytes];
if (g_secondaryBuffers[i] == NULL)
{
status = false;
break;
}
memset(g_secondaryBuffers[i], 0, secondaryBytes);
}
g_secondaryBufferBytes = secondaryBytes;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done (res: %s)", status ? "True" : "False");
return status;
}
static void InitializeAudio(uint32_t freq)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Start (freq: %d)", freq);
if (freq < 4000)
{
WriteTrace(TraceAudioInitShutdown, TraceInfo, "Sometimes a bad frequency is requested so ignore it (freq: %d)", freq);
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
if (g_GameFreq == freq && g_primaryBuffer != NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceInfo, "we are already using this frequency, so ignore it (freq: %d)", freq);
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
if (g_critical_failure)
{
WriteTrace(TraceAudioInitShutdown, TraceInfo, "had a critical failure in setting up plugin, so ignore init");
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
/* This is important for the sync */
g_GameFreq = freq;
#ifdef ANDROID
SLuint32 sample_rate;
if ((freq / 1000) <= 11)
{
g_OutputFreq = 11025;
sample_rate = SL_SAMPLINGRATE_11_025;
}
else if ((freq / 1000) <= 22)
{
g_OutputFreq = 22050;
sample_rate = SL_SAMPLINGRATE_22_05;
}
else if ((freq / 1000) <= 32)
{
g_OutputFreq = 32000;
sample_rate = SL_SAMPLINGRATE_32;
}
else
{
g_OutputFreq = 44100;
sample_rate = SL_SAMPLINGRATE_44_1;
}
#endif
WriteTrace(TraceAudioInitShutdown, TraceInfo, "Requesting frequency: %iHz.", g_OutputFreq);
/* reload these because they gets re-assigned from data below, and InitializeAudio can be called more than once */
g_PrimaryBufferSize = GetSetting(Buffer_PrimarySize);
g_SecondaryBufferSize = GetSetting(Buffer_SecondarySize);
g_SecondaryBufferNbr = GetSetting(Buffer_SecondaryNbr);
/* Close everything because InitializeAudio can be called more than once */
CloseAudio();
/* Create primary buffer */
if (!CreatePrimaryBuffer())
{
WriteTrace(TraceAudioInitShutdown, TraceError, "CreatePrimaryBuffer failed");
CloseAudio();
g_critical_failure = true;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
/* Create secondary buffers */
if (!CreateSecondaryBuffers())
{
WriteTrace(TraceAudioInitShutdown, TraceError, "CreateSecondaryBuffers failed");
CloseAudio();
g_critical_failure = true;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
#ifdef ANDROID
/* Create thread Locks to ensure synchronization between callback and processing code */
if (pthread_mutex_init(&(g_lock.mutex), (pthread_mutexattr_t*)NULL) != 0)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "pthread_mutex_init failed");
CloseAudio();
g_critical_failure = true;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
if (pthread_cond_init(&(g_lock.cond), (pthread_condattr_t*)NULL) != 0)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "pthread_cond_init failed");
CloseAudio();
g_critical_failure = true;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
pthread_mutex_lock(&(g_lock.mutex));
g_lock.value = g_lock.limit = g_SecondaryBufferNbr;
pthread_mutex_unlock(&(g_lock.mutex));
/* Engine object */
SLresult result = slCreateEngine(&g_engineObject, 0, NULL, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "slCreateEngine failed (result: %d)", result);
}
if (result == SL_RESULT_SUCCESS)
{
result = (*g_engineObject)->Realize(g_engineObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "slCreateEngine->Realize failed (result: %d)", result);
}
}
if (result == SL_RESULT_SUCCESS)
{
result = (*g_engineObject)->GetInterface(g_engineObject, SL_IID_ENGINE, &g_engineEngine);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "slCreateEngine->GetInterface failed (result: %d)", result);
}
}
if (result == SL_RESULT_SUCCESS)
{
/* Output mix object */
result = (*g_engineEngine)->CreateOutputMix(g_engineEngine, &g_outputMixObject, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "slCreateEngine->CreateOutputMix failed (result: %d)", result);
}
}
if (result == SL_RESULT_SUCCESS)
{
result = (*g_outputMixObject)->Realize(g_outputMixObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_outputMixObject->Realize failed (result: %d)", result);
}
}
if (result == SL_RESULT_SUCCESS)
{
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, g_SecondaryBufferNbr };
SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM,2, sample_rate, 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, g_outputMixObject };
SLDataSink audioSnk = { &loc_outmix, NULL };
/* Create audio player */
const SLInterfaceID ids1[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
const SLboolean req1[] = { SL_BOOLEAN_TRUE };
result = (*g_engineEngine)->CreateAudioPlayer(g_engineEngine, &(g_playerObject), &audioSrc, &audioSnk, 1, ids1, req1);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_engineEngine->CreateAudioPlayer failed (result: %d)", result);
}
}
/* Realize the player */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_playerObject)->Realize(g_playerObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_playerObject->Realize failed (result: %d)", result);
}
}
/* Get the play interface */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_playerObject)->GetInterface(g_playerObject, SL_IID_PLAY, &(g_playerPlay));
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_playerObject->GetInterface(SL_IID_PLAY) failed (result: %d)", result);
}
}
/* Get the buffer queue interface */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_playerObject)->GetInterface(g_playerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(g_bufferQueue));
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_playerObject->GetInterface(SL_IID_ANDROIDSIMPLEBUFFERQUEUE) failed (result: %d)", result);
}
}
/* register callback on the buffer queue */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_bufferQueue)->RegisterCallback(g_bufferQueue, queueCallback, &g_lock);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "bufferQueue->RegisterCallback() failed (result: %d)", result);
}
}
/* set the player's state to playing */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_playerPlay)->SetPlayState(g_playerPlay, SL_PLAYSTATE_PLAYING);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_playerPlay->SetPlayState(SL_PLAYSTATE_PLAYING) failed (result: %d)", result);
}
}
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceNotice, "Couldn't open OpenSLES audio");
CloseAudio();
g_critical_failure = true;
}
#endif
WriteTrace(TraceAudioInitShutdown, TraceNotice, "Done");
}
static int resample(unsigned char *input, int /*input_avail*/, int oldsamplerate, unsigned char *output, int output_needed, int newsamplerate)
{
int *psrc = (int*)input;
int *pdest = (int*)output;
int i = 0, j = 0;
#ifdef USE_SPEEX
spx_uint32_t in_len, out_len;
if (Resample == RESAMPLER_SPEEX)
{
if (spx_state == NULL)
{
spx_state = speex_resampler_init(2, oldsamplerate, newsamplerate, ResampleQuality, &error);
if (spx_state == NULL)
{
memset(output, 0, output_needed);
return 0;
}
}
speex_resampler_set_rate(spx_state, oldsamplerate, newsamplerate);
in_len = input_avail / 4;
out_len = output_needed / 4;
if ((error = speex_resampler_process_interleaved_int(spx_state, (const spx_int16_t *)input, &in_len, (spx_int16_t *)output, &out_len)))
{
memset(output, 0, output_needed);
return input_avail; // number of bytes consumed
}
return in_len * 4;
}
#endif
#ifdef USE_SRC
if (Resample == RESAMPLER_SRC)
{
// the high quality resampler needs more input than the samplerate ratio would indicate to work properly
if (input_avail > output_needed * 3 / 2)
input_avail = output_needed * 3 / 2; // just to avoid too much short-float-short conversion time
if (_src_len < input_avail * 2 && input_avail > 0)
{
if (_src) free(_src);
_src_len = input_avail * 2;
_src = malloc(_src_len);
}
if (_dest_len < output_needed * 2 && output_needed > 0)
{
if (_dest) free(_dest);
_dest_len = output_needed * 2;
_dest = malloc(_dest_len);
}
memset(_src, 0, _src_len);
memset(_dest, 0, _dest_len);
if (src_state == NULL)
{
src_state = src_new(ResampleQuality, 2, &error);
if (src_state == NULL)
{
memset(output, 0, output_needed);
return 0;
}
}
src_short_to_float_array((short *)input, _src, input_avail / 2);
src_data.end_of_input = 0;
src_data.data_in = _src;
src_data.input_frames = input_avail / 4;
src_data.src_ratio = (float)newsamplerate / oldsamplerate;
src_data.data_out = _dest;
src_data.output_frames = output_needed / 4;
if ((error = src_process(src_state, &src_data)))
{
memset(output, 0, output_needed);
return input_avail; // number of bytes consumed
}
src_float_to_short_array(_dest, (short *)output, output_needed / 2);
return src_data.input_frames_used * 4;
}
#endif
// RESAMPLE == TRIVIAL
if (newsamplerate >= oldsamplerate)
{
int sldf = oldsamplerate;
int const2 = 2 * sldf;
int dldf = newsamplerate;
int const1 = const2 - 2 * dldf;
int criteria = const2 - dldf;
for (i = 0; i < output_needed / 4; i++)
{
pdest[i] = psrc[j];
if (criteria >= 0)
{
++j;
criteria += const1;
}
else criteria += const2;
}
return j * 4; //number of bytes consumed
}
// newsamplerate < oldsamplerate, this only happens when speed_factor > 1
for (i = 0; i < output_needed / 4; i++)
{
j = i * oldsamplerate / newsamplerate;
pdest[i] = psrc[j];
}
return j * 4; //number of bytes consumed
}
EXPORT void CALL PluginLoaded(void)
{
PluginInit();
@ -620,109 +61,36 @@ EXPORT void CALL AiDacrateChanged(int SystemType)
return;
}
int f = g_GameFreq != 0 ? g_GameFreq : DEFAULT_FREQUENCY;
switch (SystemType)
if (g_SoundDriver && g_Dacrate != *g_AudioInfo.AI_DACRATE_REG)
{
case SYSTEM_NTSC:
f = 48681812 / (*g_AudioInfo.AI__DACRATE_REG + 1);
break;
case SYSTEM_PAL:
f = 49656530 / (*g_AudioInfo.AI__DACRATE_REG + 1);
break;
case SYSTEM_MPAL:
f = 48628316 / (*g_AudioInfo.AI__DACRATE_REG + 1);
break;
}
InitializeAudio(f);
g_Dacrate = *g_AudioInfo.AI_DACRATE_REG & 0x00003FFF;
if (g_Dacrate != *g_AudioInfo.AI_DACRATE_REG)
{
WriteTrace(TraceAudioInterface, TraceNotice, "Unknown/reserved bits in AI_DACRATE_REG set. 0x%08X", *g_AudioInfo.AI_DACRATE_REG);
}
uint32_t video_clock = 0;
switch (SystemType)
{
case SYSTEM_NTSC: video_clock = 48681812; break;
case SYSTEM_PAL: video_clock = 49656530; break;
case SYSTEM_MPAL: video_clock = 48628316; break;
}
uint32_t Frequency = video_clock / (g_Dacrate + 1);
g_SoundDriver->AI_SetFrequency(Frequency);
}
WriteTrace(TraceAudioInterface, TraceDebug, "Done");
}
EXPORT void CALL AiLenChanged(void)
{
WriteTrace(TraceAudioInterface, TraceDebug, "Start (DRAM_ADDR = 0x%X LenReg = 0x%X)", *g_AudioInfo.AI__LEN_REG, *g_AudioInfo.AI__DRAM_ADDR_REG);
if (!g_AudioEnabled)
WriteTrace(TraceAudioInterface, TraceDebug, "Start (DRAM_ADDR = 0x%X Len = 0x%X)", *g_AudioInfo.AI_DRAM_ADDR_REG, *g_AudioInfo.AI_LEN_REG);
if (g_SoundDriver && g_AudioEnabled)
{
return;
}
uint32_t LenReg = *g_AudioInfo.AI__LEN_REG;
uint8_t * p = g_AudioInfo.RDRAM + (*g_AudioInfo.AI__DRAM_ADDR_REG & 0xFFFFFF);
uint32_t Len = *g_AudioInfo.AI_LEN_REG & 0x3FFF8;
uint8_t * Buffer = (g_AudioInfo.RDRAM + (*g_AudioInfo.AI_DRAM_ADDR_REG & 0x00FFFFF8));
WriteTrace(TraceAudioInterface, TraceDebug, "g_primaryBufferPos = 0x%X LenReg = 0x%X g_primaryBufferBytes = %X", g_primaryBufferPos, LenReg, g_primaryBufferBytes);
if (g_primaryBufferPos + LenReg < g_primaryBufferBytes)
{
unsigned int i;
for (i = 0; i < LenReg; i += 4)
{
if (g_SwapChannels == 0)
{
/* Left channel */
g_primaryBuffer[g_primaryBufferPos + i] = p[i + 2];
g_primaryBuffer[g_primaryBufferPos + i + 1] = p[i + 3];
/* Right channel */
g_primaryBuffer[g_primaryBufferPos + i + 2] = p[i];
g_primaryBuffer[g_primaryBufferPos + i + 3] = p[i + 1];
}
else
{
/* Left channel */
g_primaryBuffer[g_primaryBufferPos + i] = p[i];
g_primaryBuffer[g_primaryBufferPos + i + 1] = p[i + 1];
/* Right channel */
g_primaryBuffer[g_primaryBufferPos + i + 2] = p[i + 2];
g_primaryBuffer[g_primaryBufferPos + i + 3] = p[i + 3];
}
}
g_primaryBufferPos += i;
}
else
{
WriteTrace(TraceAudioInterface, TraceDebug, "Audio primary buffer overflow. (g_primaryBufferPos: %d LenReg: %d g_primaryBufferBytes: %d)", g_primaryBufferPos, LenReg, g_primaryBufferBytes);
}
uint32_t newsamplerate = g_OutputFreq * 100 / g_speed_factor;
uint32_t oldsamplerate = g_GameFreq != 0 ? g_GameFreq : DEFAULT_FREQUENCY;
while (g_primaryBufferPos >= ((g_secondaryBufferBytes * oldsamplerate) / newsamplerate))
{
WriteTrace(TraceAudioInterface, TraceDebug, "g_secondaryBufferBytes = %d", g_secondaryBufferBytes);
WriteTrace(TraceAudioInterface, TraceDebug, "oldsamplerate = %d", oldsamplerate);
WriteTrace(TraceAudioInterface, TraceDebug, "newsamplerate = %d", newsamplerate);
WriteTrace(TraceAudioInterface, TraceDebug, "((g_secondaryBufferBytes * oldsamplerate) / newsamplerate) = %d", ((g_secondaryBufferBytes * oldsamplerate) / newsamplerate));
WriteTrace(TraceAudioInterface, TraceDebug, "g_primaryBufferPos= %d", g_primaryBufferPos);
#ifdef ANDROID
pthread_mutex_lock(&(g_lock.mutex));
/* Wait for the next callback if no more output buffers available */
while (g_lock.value == 0)
{
pthread_cond_wait(&(g_lock.cond), &(g_lock.mutex));
}
g_lock.value--;
pthread_mutex_unlock(&(g_lock.mutex));
#endif
WriteTrace(TraceAudioInterface, TraceDebug, "Finished with lock");
// TODO: don't resample if speed_factor = 100 and newsamplerate ~= oldsamplerate
int input_used = resample(g_primaryBuffer, g_primaryBufferPos, oldsamplerate, g_secondaryBuffers[g_secondaryBufferIndex], g_secondaryBufferBytes, newsamplerate);
#ifdef ANDROID
(*g_bufferQueue)->Enqueue(g_bufferQueue, g_secondaryBuffers[g_secondaryBufferIndex], g_secondaryBufferBytes);
#endif
memmove(g_primaryBuffer, &g_primaryBuffer[input_used], g_primaryBufferPos - input_used);
g_primaryBufferPos -= input_used;
g_secondaryBufferIndex++;
if (g_secondaryBufferIndex > (g_SecondaryBufferNbr - 1))
{
g_secondaryBufferIndex = 0;
}
g_SoundDriver->AI_LenChanged(Buffer, Len);
}
WriteTrace(TraceAudioInterface, TraceDebug, "Done");
}
@ -789,14 +157,21 @@ EXPORT int32_t CALL InitiateAudio(AUDIO_INFO Audio_Info)
EXPORT void CALL RomOpen()
{
WriteTrace(TraceAudioInterface, TraceDebug, "Start");
InitializeAudio(DEFAULT_FREQUENCY);
if (g_SoundDriver)
{
g_SoundDriver->AI_SetFrequency(DEFAULT_FREQUENCY);
}
WriteTrace(TraceAudioInterface, TraceDebug, "Done");
}
EXPORT void CALL RomClosed(void)
{
WriteTrace(TraceAudioInterface, TraceDebug, "Start");
CloseAudio();
g_Dacrate = 0;
if (g_SoundDriver)
{
g_SoundDriver->AI_Shutdown();
}
WriteTrace(TraceAudioInterface, TraceDebug, "Done");
}

View File

@ -12,28 +12,21 @@
#pragma once
/* Default start-time size of primary buffer (in equivalent output samples).
This is the buffer where audio is loaded after it's extracted from n64's memory. */
This is the buffer where audio is loaded after it's extracted from n64's memory. */
enum { PRIMARY_BUFFER_SIZE = 16384 };
/* Size of a single secondary buffer, in output samples. This is the requested size of OpenSLES's
hardware buffer, this should be a power of two. */
hardware buffer, this should be a power of two. */
enum { SECONDARY_BUFFER_SIZE = 1024 };
/* This is the requested number of OpenSLES's hardware buffers */
enum { SECONDARY_BUFFER_NBR = 2 };
/* This sets default frequency what is used if rom doesn't want to change it.
Probably only game that needs this is Zelda: Ocarina Of Time Master Quest
*NOTICE* We should try to find out why Demos' frequencies are always wrong
They tend to rely on a default frequency, apparently, never the same one ;) */
enum { DEFAULT_FREQUENCY = 33600 };
/* number of bytes per sample */
enum
{
N64_SAMPLE_BYTES = 4,
SLES_SAMPLE_BYTES = 4,
};
Probably only game that needs this is Zelda: Ocarina Of Time Master Quest
*NOTICE* We should try to find out why Demos' frequencies are always wrong
They tend to rely on a default frequency, apparently, never the same one ;) */
enum { DEFAULT_FREQUENCY = 33600 };
extern bool g_SwapChannels;
extern uint32_t g_GameFreq;

View File

@ -32,7 +32,7 @@ void SetupAudioSettings(void)
RegisterSetting(Logging_LogAudioInitShutdown, Data_DWORD_General, "AudioInitShutdown", "Logging", g_ModuleLogLevel[TraceAudioInitShutdown], NULL);
RegisterSetting(Logging_LogAudioInterface, Data_DWORD_General, "AudioInterface", "Logging", g_ModuleLogLevel[TraceAudioInterface], NULL);
g_SwapChannels = GetSetting(Output_SwapChannels);
g_SwapChannels = GetSetting(Output_SwapChannels) != 0;
g_GameFreq = GetSetting(Output_DefaultFrequency);
g_ModuleLogLevel[TraceAudioInitShutdown] = GetSetting(Logging_LogAudioInitShutdown);

View File

@ -56,14 +56,14 @@ typedef struct
uint8_t * DMEM;
uint8_t * IMEM;
uint32_t * MI__INTR_REG;
uint32_t * MI_INTR_REG;
uint32_t * AI__DRAM_ADDR_REG;
uint32_t * AI__LEN_REG;
uint32_t * AI__CONTROL_REG;
uint32_t * AI__STATUS_REG;
uint32_t * AI__DACRATE_REG;
uint32_t * AI__BITRATE_REG;
uint32_t * AI_DRAM_ADDR_REG;
uint32_t * AI_LEN_REG;
uint32_t * AI_CONTROL_REG;
uint32_t * AI_STATUS_REG;
uint32_t * AI_DACRATE_REG;
uint32_t * AI_BITRATE_REG;
void(CALL *CheckInterrupts)(void);
} AUDIO_INFO;

View File

@ -13,3 +13,662 @@
* GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html *
* *
****************************************************************************/
#include "OpenSLES.h"
#include <Project64-audio/trace.h>
#include <Project64-audio/SettingsID.h>
#include <Project64-audio/AudioMain.h>
#ifdef ANDROID
#include <SLES/OpenSLES.h>
#include <SLES/OpenSLES_Android.h>
#endif
#ifdef ANDROID
typedef struct threadLock_
{
pthread_mutex_t mutex;
pthread_cond_t cond;
volatile unsigned char value;
volatile unsigned char limit;
} threadLock;
#endif
/* number of bytes per sample */
enum
{
N64_SAMPLE_BYTES = 4,
SLES_SAMPLE_BYTES = 4,
};
/* Pointer to the primary audio buffer */
uint8_t * g_primaryBuffer = NULL;
/* Size of the primary buffer */
uint32_t g_primaryBufferBytes = 0;
/* Size of the primary audio buffer in equivalent output samples */
unsigned int g_PrimaryBufferSize = PRIMARY_BUFFER_SIZE;
/* Pointer to secondary buffers */
uint8_t ** g_secondaryBuffers = NULL;
/* Size of a single secondary buffer */
uint32_t g_secondaryBufferBytes = 0;
/* Size of a single secondary audio buffer in output samples */
uint32_t g_SecondaryBufferSize = SECONDARY_BUFFER_SIZE;
/* Position in the primary buffer where next audio chunk should be placed */
uint32_t g_primaryBufferPos = 0;
/* Index of the next secondary buffer available */
uint32_t g_secondaryBufferIndex = 0;
/* Number of secondary buffers */
uint32_t g_SecondaryBufferNbr = SECONDARY_BUFFER_NBR;
/* Audio frequency, this is usually obtained from the game, but for compatibility we set default value */
uint32_t g_GameFreq = DEFAULT_FREQUENCY;
/* SpeedFactor is used to increase/decrease game playback speed */
uint32_t g_speed_factor = 100;
/* If this is true then left and right channels are swapped */
bool g_SwapChannels = false;
/* Output Audio frequency */
int g_OutputFreq = 44100;
/* Indicate that the audio plugin failed to initialize, so the emulator can keep running without sound */
bool g_critical_failure = false;
#ifdef ANDROID
/* Thread Lock */
threadLock g_lock;
/* Engine interfaces */
SLObjectItf g_engineObject = NULL;
SLEngineItf g_engineEngine = NULL;
/* Output mix interfaces */
SLObjectItf g_outputMixObject = NULL;
/* Player interfaces */
SLObjectItf g_playerObject = NULL;
SLPlayItf g_playerPlay = NULL;
/* Buffer queue interfaces */
SLAndroidSimpleBufferQueueItf g_bufferQueue = NULL;
#endif
static bool CreatePrimaryBuffer(void)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Start");
unsigned int primaryBytes = (unsigned int)(g_PrimaryBufferSize * N64_SAMPLE_BYTES);
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Allocating memory for primary audio buffer: %i bytes.", primaryBytes);
g_primaryBuffer = new uint8_t[primaryBytes];
if (g_primaryBuffer == NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_primaryBuffer == NULL");
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done (res: false)");
return false;
}
memset(g_primaryBuffer, 0, primaryBytes);
g_primaryBufferBytes = primaryBytes;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done (res: True)");
return true;
}
static void CloseAudio(void)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Start");
g_primaryBufferPos = 0;
g_secondaryBufferIndex = 0;
/* Delete Primary buffer */
if (g_primaryBuffer != NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Delete g_primaryBuffer (%p)", g_primaryBuffer);
g_primaryBufferBytes = 0;
delete[] g_primaryBuffer;
g_primaryBuffer = NULL;
}
/* Delete Secondary buffers */
if (g_secondaryBuffers != NULL)
{
for (uint32_t i = 0; i < g_SecondaryBufferNbr; i++)
{
if (g_secondaryBuffers[i] != NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Delete g_secondaryBuffers[%d] (%p)", i, g_secondaryBuffers[i]);
delete[] g_secondaryBuffers[i];
g_secondaryBuffers[i] = NULL;
}
}
g_secondaryBufferBytes = 0;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Delete g_secondaryBuffers (%p)", g_secondaryBuffers);
delete[] g_secondaryBuffers;
g_secondaryBuffers = NULL;
}
#ifdef ANDROID
/* Destroy buffer queue audio player object, and invalidate all associated interfaces */
if (g_playerObject != NULL)
{
SLuint32 state = SL_PLAYSTATE_PLAYING;
(*g_playerPlay)->SetPlayState(g_playerPlay, SL_PLAYSTATE_STOPPED);
while (state != SL_PLAYSTATE_STOPPED)
{
(*g_playerPlay)->GetPlayState(g_playerPlay, &state);
}
(*g_playerObject)->Destroy(g_playerObject);
g_playerObject = NULL;
g_playerPlay = NULL;
g_bufferQueue = NULL;
}
/* Destroy output mix object, and invalidate all associated interfaces */
if (g_outputMixObject != NULL)
{
(*g_outputMixObject)->Destroy(g_outputMixObject);
g_outputMixObject = NULL;
}
/* Destroy engine object, and invalidate all associated interfaces */
if (g_engineObject != NULL)
{
(*g_engineObject)->Destroy(g_engineObject);
g_engineObject = NULL;
g_engineEngine = NULL;
}
/* Destroy thread Locks */
pthread_cond_signal(&(g_lock.cond));
pthread_mutex_unlock(&(g_lock.mutex));
pthread_cond_destroy(&(g_lock.cond));
pthread_mutex_destroy(&(g_lock.mutex));
#endif
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
}
static bool CreateSecondaryBuffers(void)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Start");
bool status = true;
unsigned int secondaryBytes = (unsigned int)(g_SecondaryBufferSize * SLES_SAMPLE_BYTES);
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Allocating memory for %d secondary audio buffers: %i bytes.", g_SecondaryBufferNbr, secondaryBytes);
/* Allocate number of secondary buffers */
g_secondaryBuffers = new uint8_t *[g_SecondaryBufferNbr];
if (g_secondaryBuffers == NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_secondaryBuffers == NULL");
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done (res: false)");
return false;
}
/* Allocate size of each secondary buffers */
for (uint32_t i = 0; i < g_SecondaryBufferNbr; i++)
{
g_secondaryBuffers[i] = new uint8_t[secondaryBytes];
if (g_secondaryBuffers[i] == NULL)
{
status = false;
break;
}
memset(g_secondaryBuffers[i], 0, secondaryBytes);
}
g_secondaryBufferBytes = secondaryBytes;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done (res: %s)", status ? "True" : "False");
return status;
}
static int resample(unsigned char *input, int /*input_avail*/, int oldsamplerate, unsigned char *output, int output_needed, int newsamplerate)
{
int *psrc = (int*)input;
int *pdest = (int*)output;
int i = 0, j = 0;
#ifdef USE_SPEEX
spx_uint32_t in_len, out_len;
if (Resample == RESAMPLER_SPEEX)
{
if (spx_state == NULL)
{
spx_state = speex_resampler_init(2, oldsamplerate, newsamplerate, ResampleQuality, &error);
if (spx_state == NULL)
{
memset(output, 0, output_needed);
return 0;
}
}
speex_resampler_set_rate(spx_state, oldsamplerate, newsamplerate);
in_len = input_avail / 4;
out_len = output_needed / 4;
if ((error = speex_resampler_process_interleaved_int(spx_state, (const spx_int16_t *)input, &in_len, (spx_int16_t *)output, &out_len)))
{
memset(output, 0, output_needed);
return input_avail; // number of bytes consumed
}
return in_len * 4;
}
#endif
#ifdef USE_SRC
if (Resample == RESAMPLER_SRC)
{
// the high quality resampler needs more input than the samplerate ratio would indicate to work properly
if (input_avail > output_needed * 3 / 2)
input_avail = output_needed * 3 / 2; // just to avoid too much short-float-short conversion time
if (_src_len < input_avail * 2 && input_avail > 0)
{
if (_src) free(_src);
_src_len = input_avail * 2;
_src = malloc(_src_len);
}
if (_dest_len < output_needed * 2 && output_needed > 0)
{
if (_dest) free(_dest);
_dest_len = output_needed * 2;
_dest = malloc(_dest_len);
}
memset(_src, 0, _src_len);
memset(_dest, 0, _dest_len);
if (src_state == NULL)
{
src_state = src_new(ResampleQuality, 2, &error);
if (src_state == NULL)
{
memset(output, 0, output_needed);
return 0;
}
}
src_short_to_float_array((short *)input, _src, input_avail / 2);
src_data.end_of_input = 0;
src_data.data_in = _src;
src_data.input_frames = input_avail / 4;
src_data.src_ratio = (float)newsamplerate / oldsamplerate;
src_data.data_out = _dest;
src_data.output_frames = output_needed / 4;
if ((error = src_process(src_state, &src_data)))
{
memset(output, 0, output_needed);
return input_avail; // number of bytes consumed
}
src_float_to_short_array(_dest, (short *)output, output_needed / 2);
return src_data.input_frames_used * 4;
}
#endif
// RESAMPLE == TRIVIAL
if (newsamplerate >= oldsamplerate)
{
int sldf = oldsamplerate;
int const2 = 2 * sldf;
int dldf = newsamplerate;
int const1 = const2 - 2 * dldf;
int criteria = const2 - dldf;
for (i = 0; i < output_needed / 4; i++)
{
pdest[i] = psrc[j];
if (criteria >= 0)
{
++j;
criteria += const1;
}
else criteria += const2;
}
return j * 4; //number of bytes consumed
}
// newsamplerate < oldsamplerate, this only happens when speed_factor > 1
for (i = 0; i < output_needed / 4; i++)
{
j = i * oldsamplerate / newsamplerate;
pdest[i] = psrc[j];
}
return j * 4; //number of bytes consumed
}
/* This callback handler is called every time a buffer finishes playing */
#ifdef ANDROID
void queueCallback(SLAndroidSimpleBufferQueueItf caller, void *context)
{
threadLock *plock = (threadLock *)context;
pthread_mutex_lock(&(plock->mutex));
if (plock->value < plock->limit)
plock->value++;
pthread_cond_signal(&(plock->cond));
pthread_mutex_unlock(&(plock->mutex));
}
#endif
void OpenSLESDriver::AI_SetFrequency(uint32_t freq)
{
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Start (freq: %d)", freq);
if (freq < 4000)
{
WriteTrace(TraceAudioInitShutdown, TraceInfo, "Sometimes a bad frequency is requested so ignore it (freq: %d)", freq);
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
if (g_GameFreq == freq && g_primaryBuffer != NULL)
{
WriteTrace(TraceAudioInitShutdown, TraceInfo, "we are already using this frequency, so ignore it (freq: %d)", freq);
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
if (g_critical_failure)
{
WriteTrace(TraceAudioInitShutdown, TraceInfo, "had a critical failure in setting up plugin, so ignore init");
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
/* This is important for the sync */
g_GameFreq = freq;
#ifdef ANDROID
SLuint32 sample_rate;
if ((freq / 1000) <= 11)
{
g_OutputFreq = 11025;
sample_rate = SL_SAMPLINGRATE_11_025;
}
else if ((freq / 1000) <= 22)
{
g_OutputFreq = 22050;
sample_rate = SL_SAMPLINGRATE_22_05;
}
else if ((freq / 1000) <= 32)
{
g_OutputFreq = 32000;
sample_rate = SL_SAMPLINGRATE_32;
}
else
{
g_OutputFreq = 44100;
sample_rate = SL_SAMPLINGRATE_44_1;
}
#endif
WriteTrace(TraceAudioInitShutdown, TraceInfo, "Requesting frequency: %iHz.", g_OutputFreq);
/* reload these because they gets re-assigned from data below, and InitializeAudio can be called more than once */
g_PrimaryBufferSize = GetSetting(Buffer_PrimarySize);
g_SecondaryBufferSize = GetSetting(Buffer_SecondarySize);
g_SecondaryBufferNbr = GetSetting(Buffer_SecondaryNbr);
/* Close everything because InitializeAudio can be called more than once */
CloseAudio();
/* Create primary buffer */
if (!CreatePrimaryBuffer())
{
WriteTrace(TraceAudioInitShutdown, TraceError, "CreatePrimaryBuffer failed");
CloseAudio();
g_critical_failure = true;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
/* Create secondary buffers */
if (!CreateSecondaryBuffers())
{
WriteTrace(TraceAudioInitShutdown, TraceError, "CreateSecondaryBuffers failed");
CloseAudio();
g_critical_failure = true;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
#ifdef ANDROID
/* Create thread Locks to ensure synchronization between callback and processing code */
if (pthread_mutex_init(&(g_lock.mutex), (pthread_mutexattr_t*)NULL) != 0)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "pthread_mutex_init failed");
CloseAudio();
g_critical_failure = true;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
if (pthread_cond_init(&(g_lock.cond), (pthread_condattr_t*)NULL) != 0)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "pthread_cond_init failed");
CloseAudio();
g_critical_failure = true;
WriteTrace(TraceAudioInitShutdown, TraceDebug, "Done");
return;
}
pthread_mutex_lock(&(g_lock.mutex));
g_lock.value = g_lock.limit = g_SecondaryBufferNbr;
pthread_mutex_unlock(&(g_lock.mutex));
/* Engine object */
SLresult result = slCreateEngine(&g_engineObject, 0, NULL, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "slCreateEngine failed (result: %d)", result);
}
if (result == SL_RESULT_SUCCESS)
{
result = (*g_engineObject)->Realize(g_engineObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "slCreateEngine->Realize failed (result: %d)", result);
}
}
if (result == SL_RESULT_SUCCESS)
{
result = (*g_engineObject)->GetInterface(g_engineObject, SL_IID_ENGINE, &g_engineEngine);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "slCreateEngine->GetInterface failed (result: %d)", result);
}
}
if (result == SL_RESULT_SUCCESS)
{
/* Output mix object */
result = (*g_engineEngine)->CreateOutputMix(g_engineEngine, &g_outputMixObject, 0, NULL, NULL);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "slCreateEngine->CreateOutputMix failed (result: %d)", result);
}
}
if (result == SL_RESULT_SUCCESS)
{
result = (*g_outputMixObject)->Realize(g_outputMixObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_outputMixObject->Realize failed (result: %d)", result);
}
}
if (result == SL_RESULT_SUCCESS)
{
SLDataLocator_AndroidSimpleBufferQueue loc_bufq = { SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, g_SecondaryBufferNbr };
SLDataFormat_PCM format_pcm = { SL_DATAFORMAT_PCM,2, sample_rate, 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, g_outputMixObject };
SLDataSink audioSnk = { &loc_outmix, NULL };
/* Create audio player */
const SLInterfaceID ids1[] = { SL_IID_ANDROIDSIMPLEBUFFERQUEUE };
const SLboolean req1[] = { SL_BOOLEAN_TRUE };
result = (*g_engineEngine)->CreateAudioPlayer(g_engineEngine, &(g_playerObject), &audioSrc, &audioSnk, 1, ids1, req1);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_engineEngine->CreateAudioPlayer failed (result: %d)", result);
}
}
/* Realize the player */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_playerObject)->Realize(g_playerObject, SL_BOOLEAN_FALSE);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_playerObject->Realize failed (result: %d)", result);
}
}
/* Get the play interface */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_playerObject)->GetInterface(g_playerObject, SL_IID_PLAY, &(g_playerPlay));
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_playerObject->GetInterface(SL_IID_PLAY) failed (result: %d)", result);
}
}
/* Get the buffer queue interface */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_playerObject)->GetInterface(g_playerObject, SL_IID_ANDROIDSIMPLEBUFFERQUEUE, &(g_bufferQueue));
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_playerObject->GetInterface(SL_IID_ANDROIDSIMPLEBUFFERQUEUE) failed (result: %d)", result);
}
}
/* register callback on the buffer queue */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_bufferQueue)->RegisterCallback(g_bufferQueue, queueCallback, &g_lock);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "bufferQueue->RegisterCallback() failed (result: %d)", result);
}
}
/* set the player's state to playing */
if (result == SL_RESULT_SUCCESS)
{
result = (*g_playerPlay)->SetPlayState(g_playerPlay, SL_PLAYSTATE_PLAYING);
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceError, "g_playerPlay->SetPlayState(SL_PLAYSTATE_PLAYING) failed (result: %d)", result);
}
}
if (result != SL_RESULT_SUCCESS)
{
WriteTrace(TraceAudioInitShutdown, TraceNotice, "Couldn't open OpenSLES audio");
CloseAudio();
g_critical_failure = true;
}
#endif
WriteTrace(TraceAudioInitShutdown, TraceNotice, "Done");
}
void OpenSLESDriver::AI_Shutdown(void)
{
CloseAudio();
}
void OpenSLESDriver::AI_LenChanged(uint8_t *start, uint32_t length)
{
WriteTrace(TraceAudioInterface, TraceDebug, "Start");
WriteTrace(TraceAudioInterface, TraceDebug, "g_primaryBufferPos = 0x%X length = 0x%X g_primaryBufferBytes = %X", g_primaryBufferPos, length, g_primaryBufferBytes);
if (g_primaryBufferPos + length < g_primaryBufferBytes)
{
unsigned int i;
for (i = 0; i < length; i += 4)
{
if (g_SwapChannels == 0)
{
/* Left channel */
g_primaryBuffer[g_primaryBufferPos + i] = start[i + 2];
g_primaryBuffer[g_primaryBufferPos + i + 1] = start[i + 3];
/* Right channel */
g_primaryBuffer[g_primaryBufferPos + i + 2] = start[i];
g_primaryBuffer[g_primaryBufferPos + i + 3] = start[i + 1];
}
else
{
/* Left channel */
g_primaryBuffer[g_primaryBufferPos + i] = start[i];
g_primaryBuffer[g_primaryBufferPos + i + 1] = start[i + 1];
/* Right channel */
g_primaryBuffer[g_primaryBufferPos + i + 2] = start[i + 2];
g_primaryBuffer[g_primaryBufferPos + i + 3] = start[i + 3];
}
}
g_primaryBufferPos += i;
}
else
{
WriteTrace(TraceAudioInterface, TraceDebug, "Audio primary buffer overflow. (g_primaryBufferPos: %d LenReg: %d g_primaryBufferBytes: %d)", g_primaryBufferPos, length, g_primaryBufferBytes);
}
uint32_t newsamplerate = g_OutputFreq * 100 / g_speed_factor;
uint32_t oldsamplerate = g_GameFreq != 0 ? g_GameFreq : DEFAULT_FREQUENCY;
while (g_primaryBufferPos >= ((g_secondaryBufferBytes * oldsamplerate) / newsamplerate))
{
WriteTrace(TraceAudioInterface, TraceDebug, "g_secondaryBufferBytes = %d", g_secondaryBufferBytes);
WriteTrace(TraceAudioInterface, TraceDebug, "oldsamplerate = %d", oldsamplerate);
WriteTrace(TraceAudioInterface, TraceDebug, "newsamplerate = %d", newsamplerate);
WriteTrace(TraceAudioInterface, TraceDebug, "((g_secondaryBufferBytes * oldsamplerate) / newsamplerate) = %d", ((g_secondaryBufferBytes * oldsamplerate) / newsamplerate));
WriteTrace(TraceAudioInterface, TraceDebug, "g_primaryBufferPos= %d", g_primaryBufferPos);
#ifdef ANDROID
pthread_mutex_lock(&(g_lock.mutex));
/* Wait for the next callback if no more output buffers available */
while (g_lock.value == 0)
{
pthread_cond_wait(&(g_lock.cond), &(g_lock.mutex));
}
g_lock.value--;
pthread_mutex_unlock(&(g_lock.mutex));
#endif
WriteTrace(TraceAudioInterface, TraceDebug, "Finished with lock");
// TODO: don't resample if speed_factor = 100 and newsamplerate ~= oldsamplerate
int input_used = resample(g_primaryBuffer, g_primaryBufferPos, oldsamplerate, g_secondaryBuffers[g_secondaryBufferIndex], g_secondaryBufferBytes, newsamplerate);
#ifdef ANDROID
(*g_bufferQueue)->Enqueue(g_bufferQueue, g_secondaryBuffers[g_secondaryBufferIndex], g_secondaryBufferBytes);
#endif
memmove(g_primaryBuffer, &g_primaryBuffer[input_used], g_primaryBufferPos - input_used);
g_primaryBufferPos -= input_used;
g_secondaryBufferIndex++;
if (g_secondaryBufferIndex > (g_SecondaryBufferNbr - 1))
{
g_secondaryBufferIndex = 0;
}
}
WriteTrace(TraceAudioInterface, TraceDebug, "Done");
}

View File

@ -16,9 +16,14 @@
#pragma once
#pragma once
#include <Project64-audio/Audio_1.1.h>
#include "SoundBase.h"
class OpenSLESDriver :
public SoundDriverBase
{
public:
void AI_Shutdown(void);
void AI_SetFrequency(uint32_t freq);
void AI_LenChanged(uint8_t *start, uint32_t length);
};