/**************************************************************************** * * * Project64 - A Nintendo 64 emulator. * * http://www.pj64-emu.com/ * * Copyright (C) 2016 Project64. All rights reserved. * * Copyright (C) 2015 Gilles Siberlin * * Copyright (C) 2007-2009 Richard Goedeken * * Copyright (C) 2007-2008 Ebenblues * * Copyright (C) 2003 JttL * * Copyright (C) 2002 Hacktarux * * * * License: * * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * * version 2 of the License, or (at your option) any later version. * * * ****************************************************************************/ #ifdef ANDROID #include #include #endif #include "audio_1.1.h" #include "Version.h" #include #include #include "AudioSettings.h" #include "trace.h" #include "main.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; void PluginInit(void) { if (g_PluginInit) { return; } SetupTrace(); SetupAudioSettings(); 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(); WriteTrace(TraceAudioInterface, TraceDebug, "Called"); } EXPORT void CALL AiDacrateChanged(int SystemType) { WriteTrace(TraceAudioInterface, TraceDebug, "Start (SystemType: %d)", SystemType); if (!g_PluginInit) { WriteTrace(TraceAudioInterface, TraceNotice, "Plugin has not been initilized"); WriteTrace(TraceAudioInterface, TraceDebug, "Done"); return; } int f = g_GameFreq != 0 ? g_GameFreq : DEFAULT_FREQUENCY; switch (SystemType) { 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); 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) { return; } uint32_t LenReg = *g_AudioInfo.AI__LEN_REG; uint8_t * p = g_AudioInfo.RDRAM + (*g_AudioInfo.AI__DRAM_ADDR_REG & 0xFFFFFF); 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; } } WriteTrace(TraceAudioInterface, TraceDebug, "Done"); } EXPORT uint32_t CALL AiReadLength(void) { WriteTrace(TraceAudioInterface, TraceDebug, "Called"); return 0; } EXPORT void CALL AiUpdate(int32_t /*Wait*/) { WriteTrace(TraceAudioInterface, TraceDebug, "Called"); } EXPORT void CALL CloseDLL(void) { WriteTrace(TraceAudioInterface, TraceDebug, "Called"); } EXPORT void CALL DllAbout(void * /*hParent*/) { WriteTrace(TraceAudioInterface, TraceDebug, "Called"); } EXPORT void CALL DllConfig(void * /*hParent*/) { WriteTrace(TraceAudioInterface, TraceDebug, "Called"); } EXPORT void CALL DllTest(void * /*hParent*/) { WriteTrace(TraceAudioInterface, TraceDebug, "Called"); } EXPORT void CALL GetDllInfo(PLUGIN_INFO * PluginInfo) { PluginInfo->Version = 0x0101; PluginInfo->Type = PLUGIN_TYPE_AUDIO; #ifdef _DEBUG sprintf(PluginInfo->Name, "Android Audio Debug Plugin %s", VER_FILE_VERSION_STR); #else sprintf(PluginInfo->Name, "Android Audio Plugin %s", VER_FILE_VERSION_STR); #endif PluginInfo->MemoryBswaped = true; PluginInfo->NormalMemory = false; } EXPORT int32_t CALL InitiateAudio(AUDIO_INFO Audio_Info) { WriteTrace(TraceAudioInterface, TraceDebug, "Start"); g_AudioInfo = Audio_Info; WriteTrace(TraceAudioInterface, TraceDebug, "Done (res: true)"); return true; } EXPORT void CALL RomOpen() { WriteTrace(TraceAudioInterface, TraceDebug, "Start"); InitializeAudio(DEFAULT_FREQUENCY); WriteTrace(TraceAudioInterface, TraceDebug, "Done"); } EXPORT void CALL RomClosed(void) { WriteTrace(TraceAudioInterface, TraceDebug, "Start"); CloseAudio(); WriteTrace(TraceAudioInterface, TraceDebug, "Done"); } EXPORT void CALL ProcessAList(void) { WriteTrace(TraceAudioInterface, TraceDebug, "Called"); } extern "C" void UseUnregisteredSetting(int /*SettingID*/) { WriteTrace(TraceAudioInterface, TraceDebug, "Called"); #ifdef _WIN32 DebugBreak(); #endif }