BizHawk/libmupen64plus/mupen64plus-audio-bkm/main.c

352 lines
9.6 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus-bkm-audio - main.c *
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
* Editet 2013 null_ptr Completely rewritten to suit custom needs *
* Copyright (C) 2007-2009 Richard Goedeken *
* Copyright (C) 2007-2008 Ebenblues *
* Copyright (C) 2003 JttL *
* Copyright (C) 2002 Hacktarux *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program; if not, write to the *
* Free Software Foundation, Inc., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define M64P_PLUGIN_PROTOTYPES 1
#include "m64p_types.h"
#include "m64p_plugin.h"
#include "m64p_common.h"
#include "m64p_config.h"
#include "main.h"
#include "osal_dynamiclib.h"
/* 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 ;)*/
#define DEFAULT_FREQUENCY 33600
#define DEFAULT_BUFFER_SIZE 12480
/* number of bytes per sample */
#define N64_SAMPLE_BYTES 4
/* local variables */
static void (*l_DebugCallback)(void *, int, const char *) = NULL;
static void *l_DebugCallContext = NULL;
static int l_PluginInit = 0;
/* Read header for type definition */
static AUDIO_INFO AudioInfo;
/* Audio frequency, this is usually obtained from the game, but for compatibility we set default value */
static int GameFreq = DEFAULT_FREQUENCY;
/* Audio buffer */
static char* audioBuffer = NULL;
static size_t bufferBack = 0;
static size_t bufferSize = 0;
// Prototype of local functions
static void SetSamplingRate(int freq);
static void SetBufferSize(size_t size);
static int critical_failure = 0;
/* Global functions */
static void DebugMessage(int level, const char *message, ...)
{
char msgbuf[1024];
va_list args;
if (l_DebugCallback == NULL)
return;
va_start(args, message);
vsprintf(msgbuf, message, args);
(*l_DebugCallback)(l_DebugCallContext, level, msgbuf);
va_end(args);
}
#pragma region (De-)Initialization
/* Mupen64Plus plugin functions */
EXPORT m64p_error CALL PluginStartup(m64p_dynlib_handle CoreLibHandle, void *Context,
void (*DebugCallback)(void *, int, const char *))
{
ptr_CoreGetAPIVersions CoreAPIVersionFunc;
int ConfigAPIVersion, DebugAPIVersion, VidextAPIVersion;
if (l_PluginInit)
return M64ERR_ALREADY_INIT;
/* first thing is to set the callback function for debug info */
l_DebugCallback = DebugCallback;
l_DebugCallContext = Context;
/* attach and call the CoreGetAPIVersions function, check Config API version for compatibility */
CoreAPIVersionFunc = (ptr_CoreGetAPIVersions) osal_dynlib_getproc(CoreLibHandle, "CoreGetAPIVersions");
if (CoreAPIVersionFunc == NULL)
{
DebugMessage(M64MSG_ERROR, "Core emulator broken; no CoreAPIVersionFunc() function found.");
return M64ERR_INCOMPATIBLE;
}
(*CoreAPIVersionFunc)(&ConfigAPIVersion, &DebugAPIVersion, &VidextAPIVersion, NULL);
if ((ConfigAPIVersion & 0xffff0000) != (CONFIG_API_VERSION & 0xffff0000))
{
DebugMessage(M64MSG_ERROR, "Emulator core Config API (v%i.%i.%i) incompatible with plugin (v%i.%i.%i)",
VERSION_PRINTF_SPLIT(ConfigAPIVersion), VERSION_PRINTF_SPLIT(CONFIG_API_VERSION));
return M64ERR_INCOMPATIBLE;
}
l_PluginInit = 1;
return M64ERR_SUCCESS;
}
EXPORT m64p_error CALL PluginShutdown(void)
{
if (!l_PluginInit)
return M64ERR_NOT_INIT;
/* reset some local variables */
l_DebugCallback = NULL;
l_DebugCallContext = NULL;
l_PluginInit = 0;
return M64ERR_SUCCESS;
}
EXPORT int CALL RomOpen(void)
{
if (!l_PluginInit)
return 0;
SetSamplingRate(GameFreq);
SetBufferSize(DEFAULT_BUFFER_SIZE);
return 1;
}
EXPORT void CALL RomClosed( void )
{
if (!l_PluginInit)
return;
if (critical_failure == 1)
return;
// Delete the buffer, as we are done producing sound
if (audioBuffer != NULL)
{
bufferSize = 0;
bufferBack = 0;
free(audioBuffer);
audioBuffer = NULL;
}
}
EXPORT int CALL InitiateAudio( AUDIO_INFO Audio_Info )
{
if (!l_PluginInit)
return 0;
AudioInfo = Audio_Info;
return 1;
}
static void SetBufferSize(size_t size)
{
char* oldBuffer = audioBuffer;
audioBuffer = (char*)malloc(size);
if(audioBuffer != NULL)
{
memcpy(audioBuffer, oldBuffer, min(size, bufferBack));
free(oldBuffer);
}
bufferSize = size;
bufferBack = min(size, bufferBack);
}
#pragma endregion
#pragma region Pluginversion
EXPORT m64p_error CALL PluginGetVersion(m64p_plugin_type *PluginType, int *PluginVersion, int *APIVersion, const char **PluginNamePtr, int *Capabilities)
{
/* set version info */
if (PluginType != NULL)
*PluginType = M64PLUGIN_AUDIO;
if (PluginVersion != NULL)
*PluginVersion = BKM_AUDIO_PLUGIN_VERSION;
if (APIVersion != NULL)
*APIVersion = AUDIO_PLUGIN_API_VERSION;
if (PluginNamePtr != NULL)
*PluginNamePtr = "Mupen64Plus BKM Audio Plugin for Bizhawk";
if (Capabilities != NULL)
{
*Capabilities = 0;
}
return M64ERR_SUCCESS;
}
#pragma endregion
#pragma region Handle audio
/* --- Called when sampling rate changes --- */
EXPORT void CALL AiDacrateChanged( int SystemType )
{
int f = GameFreq;
if (!l_PluginInit)
return;
if (*AudioInfo.AI_DACRATE_REG == 0)
{
f = DEFAULT_FREQUENCY;
}
else
{
switch (SystemType)
{
case SYSTEM_NTSC:
f = 48681812 / (*AudioInfo.AI_DACRATE_REG + 1);
break;
case SYSTEM_PAL:
f = 49656530 / (*AudioInfo.AI_DACRATE_REG + 1);
break;
case SYSTEM_MPAL:
f = 48628316 / (*AudioInfo.AI_DACRATE_REG + 1);
break;
}
}
SetSamplingRate(f);
}
/* --- Called when length of n64 audio buffer changes --- */
/* --- i.e. new audio data in buffer --- */
EXPORT void CALL AiLenChanged( void )
{
unsigned int LenReg;
unsigned char *p;
if (critical_failure == 1)
return;
if (!l_PluginInit)
return;
LenReg = *AudioInfo.AI_LEN_REG;
p = AudioInfo.RDRAM + (*AudioInfo.AI_DRAM_ADDR_REG & 0xFFFFFF);
if (bufferBack + LenReg < bufferSize)
{
unsigned int i;
for ( i = 0 ; i < LenReg ; i += 4 )
{
// Left channel
audioBuffer[ bufferBack + i ] = p[ i + 2 ];
audioBuffer[ bufferBack + i + 1 ] = p[ i + 3 ];
// Right channel
audioBuffer[ bufferBack + i + 2 ] = p[ i ];
audioBuffer[ bufferBack + i + 3 ] = p[ i + 1 ];
}
bufferBack += i;
}
else
{
DebugMessage(M64MSG_WARNING, "AiLenChanged(): Audio buffer overflow.");
}
}
static void SetSamplingRate(int freq)
{
GameFreq = freq; // This is important for the sync
}
#pragma endregion
#pragma region Unused methods
/* ----------------------------------------------------------------------
* ------------ STUBS. WE DO NOT USE THESE API FUNCTIONS ---------------
* -------- MUPEN EXPECTS THEM AND FAILS IF THEY DON'T EXIST ------------
* ---------------------------------------------------------------------- */
EXPORT void CALL ProcessAList(void)
{
}
EXPORT void CALL SetSpeedFactor(int percentage)
{
}
static int VolumeGetUnmutedLevel(void)
{
return 100;
}
EXPORT void CALL VolumeMute(void)
{
}
EXPORT void CALL VolumeUp(void)
{
}
EXPORT void CALL VolumeDown(void)
{
}
EXPORT int CALL VolumeGetLevel(void)
{
return 100;
}
EXPORT void CALL VolumeSetLevel(int level)
{
}
EXPORT const char * CALL VolumeGetString(void)
{
return "100%";
}
/* ----------------------------------------------------------------------
* --------------------------- STUBS END --------------------------------
* ---------------------------------------------------------------------- */
#pragma endregion
#pragma region Buffer export
/* --- Moves content of audio buffer to destination --- */
EXPORT void CALL ReadAudioBuffer(short* dest)
{
memcpy(dest, audioBuffer, bufferBack);
bufferBack = 0;
}
/* --- Returns number of shorts of internal data --- */
EXPORT int CALL GetBufferSize()
{
return max(bufferBack/2, 0);
}
/* --- Returns current sampling rate --- */
EXPORT int CALL GetAudioRate()
{
return GameFreq;
}
#pragma endregion