From 06383ff6382806ca0e36e15dd320439b55987329 Mon Sep 17 00:00:00 2001 From: zilmar Date: Sun, 5 Jun 2016 11:33:35 +1000 Subject: [PATCH] [Project64] Add Audio plugin for android --- Source/Android/PluginAudio/AudioSettings.cpp | 32 + Source/Android/PluginAudio/AudioSettings.h | 26 + Source/Android/PluginAudio/Audio_1.1.h | 194 +++++ Source/Android/PluginAudio/Main.cpp | 799 ++++++++++++++++++ .../Android/PluginAudio/PluginAudio.vcxproj | 58 ++ .../PluginAudio/PluginAudio.vcxproj.filters | 45 + Source/Android/PluginAudio/Version.h | 42 + Source/Android/PluginAudio/main.h | 40 + Source/Android/PluginAudio/trace.cpp | 83 ++ Source/Android/PluginAudio/trace.h | 24 + 10 files changed, 1343 insertions(+) create mode 100644 Source/Android/PluginAudio/AudioSettings.cpp create mode 100644 Source/Android/PluginAudio/AudioSettings.h create mode 100644 Source/Android/PluginAudio/Audio_1.1.h create mode 100644 Source/Android/PluginAudio/Main.cpp create mode 100644 Source/Android/PluginAudio/PluginAudio.vcxproj create mode 100644 Source/Android/PluginAudio/PluginAudio.vcxproj.filters create mode 100644 Source/Android/PluginAudio/Version.h create mode 100644 Source/Android/PluginAudio/main.h create mode 100644 Source/Android/PluginAudio/trace.cpp create mode 100644 Source/Android/PluginAudio/trace.h diff --git a/Source/Android/PluginAudio/AudioSettings.cpp b/Source/Android/PluginAudio/AudioSettings.cpp new file mode 100644 index 000000000..dffe21214 --- /dev/null +++ b/Source/Android/PluginAudio/AudioSettings.cpp @@ -0,0 +1,32 @@ +/**************************************************************************** + * * + * Project64 - A Nintendo 64 emulator. * + * http://www.pj64-emu.com/ * + * Copyright (C) 2016 Project64. All rights reserved. * + * * + * License: * + * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * + * version 2 of the License, or (at your option) any later version. * + * * + ****************************************************************************/ +#include "AudioSettings.h" +#include "trace.h" +#include "main.h" + +void SetupAudioSettings (void) +{ + SetModuleName("AndroidAudio"); + RegisterSetting(Output_SwapChannels, Data_DWORD_General, "SwapChannels", "", 0, NULL); + RegisterSetting(Output_DefaultFrequency, Data_DWORD_General, "DefaultFrequency", "", DEFAULT_FREQUENCY, NULL); + RegisterSetting(Buffer_PrimarySize, Data_DWORD_General, "BufferPrimarySize", "", PRIMARY_BUFFER_SIZE, NULL); + RegisterSetting(Buffer_SecondarySize, Data_DWORD_General, "BufferSecondarySize", "", SECONDARY_BUFFER_SIZE, NULL); + RegisterSetting(Buffer_SecondaryNbr, Data_DWORD_General, "BufferSecondaryNbr", "", SECONDARY_BUFFER_NBR, NULL); + 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_GameFreq = GetSetting(Output_DefaultFrequency); + + g_ModuleLogLevel[TraceAudioInitShutdown] = GetSetting(Logging_LogAudioInitShutdown); + g_ModuleLogLevel[TraceAudioInterface] = GetSetting(Logging_LogAudioInterface); +} \ No newline at end of file diff --git a/Source/Android/PluginAudio/AudioSettings.h b/Source/Android/PluginAudio/AudioSettings.h new file mode 100644 index 000000000..04e424b3d --- /dev/null +++ b/Source/Android/PluginAudio/AudioSettings.h @@ -0,0 +1,26 @@ +/**************************************************************************** + * * + * Project64 - A Nintendo 64 emulator. * + * http://www.pj64-emu.com/ * + * Copyright (C) 2016 Project64. All rights reserved. * + * * + * License: * + * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * + * version 2 of the License, or (at your option) any later version. * + * * + ****************************************************************************/ +#pragma once +#include + +enum AudioSettingID +{ + Output_SwapChannels, + Output_DefaultFrequency, + Buffer_PrimarySize, + Buffer_SecondarySize, + Buffer_SecondaryNbr, + Logging_LogAudioInitShutdown, + Logging_LogAudioInterface, +}; + +void SetupAudioSettings ( void ); \ No newline at end of file diff --git a/Source/Android/PluginAudio/Audio_1.1.h b/Source/Android/PluginAudio/Audio_1.1.h new file mode 100644 index 000000000..05621c44b --- /dev/null +++ b/Source/Android/PluginAudio/Audio_1.1.h @@ -0,0 +1,194 @@ +/********************************************************************************** +Common Audio plugin spec, version #1.1 +********************************************************************************** +Notes: +------ + +Setting the approprate bits in the MI_INTR_REG and calling CheckInterrupts which +are both passed to the DLL in InitiateAudio will generate an Interrupt from with in +the plugin. + +**********************************************************************************/ +#pragma once + +#include + +enum { PLUGIN_TYPE_AUDIO = 3 }; + +#if defined(_WIN32) +#define EXPORT extern "C" __declspec(dllexport) +#define CALL __cdecl +#else +#define EXPORT extern "C" __attribute__((visibility("default"))) +#define CALL +#endif + +enum +{ + SYSTEM_NTSC = 0, + SYSTEM_PAL = 1, + SYSTEM_MPAL = 2, +}; + +/***** Structures *****/ +typedef struct +{ + uint16_t Version; /* Should be set to 0x0101 */ + uint16_t Type; /* Set to PLUGIN_TYPE_AUDIO */ + char Name[100]; /* Name of the DLL */ + int32_t NormalMemory; + int32_t MemoryBswaped; +} PLUGIN_INFO; + +typedef struct +{ + void * hwnd; + void * hinst; + + int32_t MemoryBswaped; // If this is set to TRUE, then the memory has been pre + + // bswap on a dword (32 bits) boundry + // eg. the first 8 bytes are stored like this: + // 4 3 2 1 8 7 6 5 + uint8_t * HEADER; // This is the rom header (first 40h bytes of the rom + // This will be in the same memory format as the rest of the memory. + uint8_t * RDRAM; + uint8_t * DMEM; + uint8_t * IMEM; + + 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; + + void(CALL *CheckInterrupts)(void); +} AUDIO_INFO; + +/****************************************************************** +Function: AiDacrateChanged +Purpose: This function is called to notify the dll that the +AiDacrate registers value has been changed. +input: The System type: +SYSTEM_NTSC 0 +SYSTEM_PAL 1 +SYSTEM_MPAL 2 +output: none +*******************************************************************/ +EXPORT void CALL AiDacrateChanged(int32_t SystemType); + +/****************************************************************** +Function: AiLenChanged +Purpose: This function is called to notify the dll that the +AiLen registers value has been changed. +input: none +output: none +*******************************************************************/ +EXPORT void CALL AiLenChanged(void); + +/****************************************************************** +Function: AiReadLength +Purpose: This function is called to allow the dll to return the +value that AI_LEN_REG should equal +input: none +output: The amount of bytes still left to play. +*******************************************************************/ +EXPORT uint32_t CALL AiReadLength(void); + +/****************************************************************** +Function: AiUpdate +Purpose: This function is called to allow the dll to update +things on a regular basis (check how long to sound to +go, copy more stuff to the buffer, anyhting you like). +The function is designed to go in to the message loop +of the main window ... but can be placed anywhere you +like. +input: if Wait is set to true, then this function should wait +till there is a messgae in the its message queue. +output: none +*******************************************************************/ +EXPORT void CALL AiUpdate(int32_t Wait); + +/****************************************************************** +Function: CloseDLL +Purpose: This function is called when the emulator is closing +down allowing the dll to de-initialise. +input: none +output: none +*******************************************************************/ +EXPORT void CALL CloseDLL(void); + +/****************************************************************** +Function: DllAbout +Purpose: This function is optional function that is provided +to give further information about the DLL. +input: a handle to the window that calls this function +output: none +*******************************************************************/ +EXPORT void CALL DllAbout(void * hParent); + +/****************************************************************** +Function: DllConfig +Purpose: This function is optional function that is provided +to allow the user to configure the dll +input: a handle to the window that calls this function +output: none +*******************************************************************/ +EXPORT void CALL DllConfig(void * hParent); + +/****************************************************************** +Function: DllTest +Purpose: This function is optional function that is provided +to allow the user to test the dll +input: a handle to the window that calls this function +output: none +*******************************************************************/ +EXPORT void CALL DllTest(void * hParent); + +/****************************************************************** +Function: GetDllInfo +Purpose: This function allows the emulator to gather information +about the dll by filling in the PluginInfo structure. +input: a pointer to a PLUGIN_INFO stucture that needs to be +filled by the function. (see def above) +output: none +*******************************************************************/ +EXPORT void CALL GetDllInfo(PLUGIN_INFO * PluginInfo); + +/****************************************************************** +Function: InitiateSound +Purpose: This function is called when the DLL is started to give +information from the emulator that the n64 audio +interface needs +Input: Audio_Info is passed to this function which is defined +above. +Output: TRUE on success +FALSE on failure to initialise + +** note on interrupts **: +To generate an interrupt set the appropriate bit in MI_INTR_REG +and then call the function CheckInterrupts to tell the emulator +that there is a waiting interrupt. +*******************************************************************/ +EXPORT int32_t CALL InitiateAudio(AUDIO_INFO Audio_Info); + +/****************************************************************** +Function: ProcessAList +Purpose: This function is called when there is a Alist to be +processed. The Dll will have to work out all the info +about the AList itself. +input: none +output: none +*******************************************************************/ +EXPORT void CALL ProcessAList(void); + +/****************************************************************** +Function: RomClosed +Purpose: This function is called when a rom is closed. +input: none +output: none +*******************************************************************/ +EXPORT void CALL RomClosed(void); diff --git a/Source/Android/PluginAudio/Main.cpp b/Source/Android/PluginAudio/Main.cpp new file mode 100644 index 000000000..ed95b88fe --- /dev/null +++ b/Source/Android/PluginAudio/Main.cpp @@ -0,0 +1,799 @@ +/**************************************************************************** + * * + * 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 + +/* 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); + + 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 +} diff --git a/Source/Android/PluginAudio/PluginAudio.vcxproj b/Source/Android/PluginAudio/PluginAudio.vcxproj new file mode 100644 index 000000000..aed6a6428 --- /dev/null +++ b/Source/Android/PluginAudio/PluginAudio.vcxproj @@ -0,0 +1,58 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {D233025A-231F-4A43-92B6-E87193C60ACC} + Win32Proj + PluginAudio + + + DynamicLibrary + + + + + + + + + AndroidAudio + AndroidAudio_d + $(SolutionDir)Plugin\Audio\ + $(SolutionDir)Plugin64\AndroidAudio\ + + + + NotUsing + + + + + + + + + + + + + + + + + {b4a4b994-9111-42b1-93c2-6f1ca8bc4421} + + + {8b9961b1-88d9-4ea3-a752-507a00dd9f3d} + + + \ No newline at end of file diff --git a/Source/Android/PluginAudio/PluginAudio.vcxproj.filters b/Source/Android/PluginAudio/PluginAudio.vcxproj.filters new file mode 100644 index 000000000..21ae25d27 --- /dev/null +++ b/Source/Android/PluginAudio/PluginAudio.vcxproj.filters @@ -0,0 +1,45 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/Source/Android/PluginAudio/Version.h b/Source/Android/PluginAudio/Version.h new file mode 100644 index 000000000..6e350772d --- /dev/null +++ b/Source/Android/PluginAudio/Version.h @@ -0,0 +1,42 @@ +/**************************************************************************** + * * + * Project64 - A Nintendo 64 emulator. * + * http://www.pj64-emu.com/ * + * Copyright (C) 2016 Project64. All rights reserved. * + * * + * License: * + * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * + * version 2 of the License, or (at your option) any later version. * + * * + ****************************************************************************/ +#define STRINGIZE2(s) #s +#define STRINGIZE(s) STRINGIZE2(s) + +#define VERSION_MAJOR 1 +#define VERSION_MINOR 0 +#define VERSION_REVISION 0 +#define VERSION_BUILD 9999 + +#define VER_FILE_DESCRIPTION_STR "Android Audio Plugin" +#define VER_FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD +#define VER_FILE_VERSION_STR STRINGIZE(VERSION_MAJOR) \ + "." STRINGIZE(VERSION_MINOR) \ + "." STRINGIZE(VERSION_REVISION) \ + "." STRINGIZE(VERSION_BUILD) \ + +#define VER_PRODUCTNAME_STR "Android-Audio" +#define VER_PRODUCT_VERSION VER_FILE_VERSION +#define VER_PRODUCT_VERSION_STR VER_FILE_VERSION_STR +#define VER_ORIGINAL_FILENAME_STR VER_PRODUCTNAME_STR ".dll" +#define VER_INTERNAL_NAME_STR VER_PRODUCTNAME_STR +#define VER_COPYRIGHT_STR "Copyright (C) 2016" + +#ifdef _DEBUG +#define VER_VER_DEBUG VS_FF_DEBUG +#else +#define VER_VER_DEBUG 0 +#endif + +#define VER_FILEOS VOS_NT_WINDOWS32 +#define VER_FILEFLAGS VER_VER_DEBUG +#define VER_FILETYPE VFT_DLL diff --git a/Source/Android/PluginAudio/main.h b/Source/Android/PluginAudio/main.h new file mode 100644 index 000000000..fa5235f9f --- /dev/null +++ b/Source/Android/PluginAudio/main.h @@ -0,0 +1,40 @@ +/**************************************************************************** + * * + * Project64 - A Nintendo 64 emulator. * + * http://www.pj64-emu.com/ * + * Copyright (C) 2016 Project64. All rights reserved. * + * Copyright (C) 2008-2012 Tillin9, Richard42 * + * * + * License: * + * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * + * version 2 of the License, or (at your option) any later version. * + * * + ****************************************************************************/ + #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. */ +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. */ +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, +}; + +extern bool g_SwapChannels; +extern uint32_t g_GameFreq; \ No newline at end of file diff --git a/Source/Android/PluginAudio/trace.cpp b/Source/Android/PluginAudio/trace.cpp new file mode 100644 index 000000000..2a63d0639 --- /dev/null +++ b/Source/Android/PluginAudio/trace.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** + * * + * Project64 - A Nintendo 64 emulator. * + * http://www.pj64-emu.com/ * + * Copyright (C) 2016 Project64. All rights reserved. * + * * + * License: * + * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * + * version 2 of the License, or (at your option) any later version. * + * * + ****************************************************************************/ + #include "trace.h" +#include +#include +#include + +#ifdef ANDROID +#include + +class AndroidLogger : public CTraceModule +{ + void Write(uint32_t module, uint8_t severity, const char * file, int line, const char * function, const char * Message) + { + switch (severity) + { + case TraceError: __android_log_print(ANDROID_LOG_ERROR, TraceModule(module), "%s: %s", function, Message); break; + case TraceWarning: __android_log_print(ANDROID_LOG_WARN, TraceModule(module), "%s: %s", function, Message); break; + case TraceNotice: __android_log_print(ANDROID_LOG_INFO, TraceModule(module), "%s: %s", function, Message); break; + case TraceInfo: __android_log_print(ANDROID_LOG_INFO, TraceModule(module), "%s: %s", function, Message); break; + case TraceDebug: __android_log_print(ANDROID_LOG_DEBUG, TraceModule(module), "%s: %s", function, Message); break; + case TraceVerbose: __android_log_print(ANDROID_LOG_VERBOSE, TraceModule(module), "%s: %s", function, Message); break; + default: __android_log_print(ANDROID_LOG_UNKNOWN, TraceModule(module), "%s: %s", function, Message); break; + } + } +}; +static AndroidLogger * g_AndroidLogger = NULL; +#endif +static CTraceFileLog * g_LogFile = NULL; + +void SetupTrace(void) +{ + if (g_LogFile != NULL) + { + return; + } + +#ifdef ANDROID + if (g_AndroidLogger == NULL) + { + g_AndroidLogger = new AndroidLogger(); + } + TraceAddModule(g_AndroidLogger); +#endif +#ifdef _DEBUG + TraceSetMaxModule(MaxTraceModulePluginAudio, TraceInfo); +#else + TraceSetMaxModule(MaxTraceModulePluginAudio, TraceError); +#endif + TraceSetModuleName(TraceAudioInitShutdown, "AudioInitShutdown"); + TraceSetModuleName(TraceAudioInterface, "AudioInterface"); + + char log_dir[260]; + memset(log_dir, 0, sizeof(log_dir)); + short logDirSetting = FindSystemSettingId("Dir:Log"); + short logFlushSetting = FindSystemSettingId("Log Auto Flush"); + if (logDirSetting != 0) + { + GetSystemSettingSz(logDirSetting, log_dir, sizeof(log_dir)); + } + + if (strlen(log_dir) == 0) + { + return; + } + + CPath LogFilePath(log_dir,"PluginAudio.log"); + if (!LogFilePath.DirectoryExists()) + { + LogFilePath.DirectoryCreate(); + } + g_LogFile = new CTraceFileLog(LogFilePath, GetSystemSetting(logFlushSetting) != 0, CLog::Log_New, 500); + TraceAddModule(g_LogFile); +} \ No newline at end of file diff --git a/Source/Android/PluginAudio/trace.h b/Source/Android/PluginAudio/trace.h new file mode 100644 index 000000000..73ffdbd63 --- /dev/null +++ b/Source/Android/PluginAudio/trace.h @@ -0,0 +1,24 @@ +/**************************************************************************** + * * + * Project64 - A Nintendo 64 emulator. * + * http://www.pj64-emu.com/ * + * Copyright (C) 2016 Project64. All rights reserved. * + * * + * License: * + * GNU/GPLv2 http://www.gnu.org/licenses/gpl-2.0.html * + * version 2 of the License, or (at your option) any later version. * + * * + ****************************************************************************/ +#pragma once +#include +#include + +enum TraceModuleAndroidAudio +{ + TraceSettings = MaxTraceModuleCommon, + TraceAudioInitShutdown, + TraceAudioInterface, + MaxTraceModulePluginAudio, +}; + +void SetupTrace(void); \ No newline at end of file