From: ptitSeb Date: Tue, 24 Sep 2013 20:09:25 +0000 (+0200) Subject: Audio Notaz (from mupen64plus v1.5) plugin. Compile and run (very well) on the OpenPa... X-Git-Url: https://notaz.gp2x.de/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a84c12f48765dbe29d5acffb243169126173bad9;p=mupen64plus-pandora.git Audio Notaz (from mupen64plus v1.5) plugin. Compile and run (very well) on the OpenPandora --- diff --git a/source/notaz_audio/Audio_1.2.h b/source/notaz_audio/Audio_1.2.h new file mode 100644 index 0000000..0decf07 --- /dev/null +++ b/source/notaz_audio/Audio_1.2.h @@ -0,0 +1,285 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - Audio_1.2.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2007-2008 Richard42 Ebenblues * + * Copyright (C) 2002 Zilmar * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +/********************************************************************************** +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. + +**********************************************************************************/ +#ifndef _AUDIO_H_INCLUDED__ +#define _AUDIO_H_INCLUDED__ + +#if defined(__cplusplus) +extern "C" { +#endif + +/* Note: BOOL, BYTE, WORD, DWORD, TRUE, FALSE are defined in windows.h */ + +#define PLUGIN_TYPE_AUDIO 3 + +#define EXPORT __declspec(dllexport) +#define CALL _cdecl + +#define SYSTEM_NTSC 0 +#define SYSTEM_PAL 1 +#define SYSTEM_MPAL 2 + +#ifndef __PLUGIN_INFO__ +#define __PLUGIN_INFO__ +/***** Structures *****/ +typedef struct { + WORD Version; /* Should be set to 0x0101 */ + WORD Type; /* Set to PLUGIN_TYPE_AUDIO */ + char Name[100]; /* Name of the DLL */ + + /* If DLL supports memory these memory options then set them to TRUE or FALSE + if it does not support it */ + BOOL NormalMemory; /* a normal BYTE array */ + BOOL MemoryBswaped; /* a normal BYTE array where the memory has been pre + bswap on a dword (32 bits) boundry */ +} PLUGIN_INFO; +#endif //__PLUGIN_INFO__ + +typedef struct { + HWND hwnd; + HINSTANCE hinst; + + BOOL 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 + BYTE * 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. + BYTE * RDRAM; + BYTE * DMEM; + BYTE * IMEM; + + DWORD * MI_INTR_REG; + + DWORD * AI_DRAM_ADDR_REG; + DWORD * AI_LEN_REG; + DWORD * AI_CONTROL_REG; + DWORD * AI_STATUS_REG; + DWORD * AI_DACRATE_REG; + DWORD * AI_BITRATE_REG; + + void (*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 (int 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 DWORD 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 (BOOL 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 ( HWND 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 ( HWND 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 ( HWND 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 BOOL 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); + +/****************************************************************** + Function: PauseAudio + Purpose: This function is called when plugin should pause or + re-enable sound. Audio should be enabled by default. + input: TRUE when audio should be paused. + FALSE when audio sould continue playing. + output: TRUE when audio is paused. + FALSE when audio plays. +*******************************************************************/ +EXPORT BOOL CALL PauseAudio (BOOL Pause); + +/****************************************************************** + NOTE: THIS HAS BEEN ADDED FOR MUPEN64PLUS AND IS NOT PART OF THE + ORIGINAL SPEC + Function: SetConfigDir + Purpose: To pass the location where config files should be read/ + written to. + input: path to config directory + output: none +*******************************************************************/ +EXPORT void CALL SetConfigDir( char *configDir ); + +/****************************************************************** + NOTE: THIS HAS BEEN ADDED FOR MUPEN64PLUS AND IS NOT PART OF THE + ORIGINAL SPEC + Function: VolumeUp + Purpose: To increase the audio volume + input: none + output: none +*******************************************************************/ +EXPORT void CALL VolumeUp(void); + +/****************************************************************** + NOTE: THIS HAS BEEN ADDED FOR MUPEN64PLUS AND IS NOT PART OF THE + ORIGINAL SPEC + Function: VolumeDown + Purpose: To decrease the audio volume + input: none + output: none +*******************************************************************/ +EXPORT void CALL VolumeDown(void); + +/****************************************************************** + NOTE: THIS HAS BEEN ADDED FOR MUPEN64PLUS AND IS NOT PART OF THE + ORIGINAL SPEC + Function: VolumeMute + Purpose: Toggles between muted and not muted + input: none + output: none +*******************************************************************/ +EXPORT void CALL VolumeMute(void); + +/****************************************************************** + NOTE: THIS HAS BEEN ADDED FOR MUPEN64PLUS AND IS NOT PART OF THE + ORIGINAL SPEC + Function: VolumeGet + Purpose: Return the current volume level + input: none + output: string containing volume level state (percentage or mute) +*******************************************************************/ +EXPORT const char * CALL VolumeGetString(void); + + +#if defined(__cplusplus) +} +#endif + +#endif + diff --git a/source/notaz_audio/Makefile b/source/notaz_audio/Makefile new file mode 100755 index 0000000..1c2d3f4 --- /dev/null +++ b/source/notaz_audio/Makefile @@ -0,0 +1,51 @@ +#/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * +# * Mupen64plus - Makefile * +# * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * +# * Copyright (C) * +# * * +# * 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 pre-make file with a bunch of definitions +#USES_GTK2 = true +#include ../pre.mk +CFLAGS += -I ../mupen64plus-core/src/api/ + +# local CFLAGS, LIBS, and LDFLAGS +ifneq "$(CPU)" "ARM" +CFLAGS += -fpic -DPIC +endif +CFLAGS += -Wall -ggdb -gdwarf-2 + +LDFLAGS += -ldl + +TARGET = notaz_audio.so +OBJECTS = main.o osal_dynamiclib_unix.o + +# build targets +all: $(TARGET) + +clean: + rm -f *.o *.so + +# build rules +.c.o: + $(CC) $(CFLAGS) $(SRC_FLAGS) -c -o $@ $< + +$(TARGET): $(OBJECTS) + $(CC) -shared $^ $(LDFLAGS) $(PLUGIN_LDFLAGS) $(SRC_LIBS) -o $@ +# $(STRIP) $@ + diff --git a/source/notaz_audio/main.c b/source/notaz_audio/main.c new file mode 100755 index 0000000..7c70d44 --- /dev/null +++ b/source/notaz_audio/main.c @@ -0,0 +1,642 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus - main.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * notaz_audio: (c) notaz, 2010 * + * * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +/* +#include "../main/winlnxdefs.h" + +#include "Audio_1.2.h" +*/ +#define M64P_PLUGIN_PROTOTYPES 1 +#include "m64p_types.h" +#include "m64p_plugin.h" +#include "m64p_common.h" +#include "m64p_config.h" + +#include "osal_dynamiclib.h" + +/* version info */ +#define NOTAZ_AUDIO_PLUGIN_VERSION 0x020000 +#define AUDIO_PLUGIN_API_VERSION 0x020000 +#define CONFIG_API_VERSION 0x020100 +#define CONFIG_PARAM_VERSION 1.00 + +#define VERSION_PRINTF_SPLIT(x) (((x) >> 16) & 0xffff), (((x) >> 8) & 0xff), ((x) & 0xff) + +/* declarations of pointers to Core config functions */ +extern ptr_ConfigListSections ConfigListSections; +extern ptr_ConfigOpenSection ConfigOpenSection; +extern ptr_ConfigDeleteSection ConfigDeleteSection; +extern ptr_ConfigSaveSection ConfigSaveSection; +extern ptr_ConfigListParameters ConfigListParameters; +extern ptr_ConfigSaveFile ConfigSaveFile; +extern ptr_ConfigSetParameter ConfigSetParameter; +extern ptr_ConfigGetParameter ConfigGetParameter; +extern ptr_ConfigGetParameterHelp ConfigGetParameterHelp; +extern ptr_ConfigSetDefaultInt ConfigSetDefaultInt; +extern ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat; +extern ptr_ConfigSetDefaultBool ConfigSetDefaultBool; +extern ptr_ConfigSetDefaultString ConfigSetDefaultString; +extern ptr_ConfigGetParamInt ConfigGetParamInt; +extern ptr_ConfigGetParamFloat ConfigGetParamFloat; +extern ptr_ConfigGetParamBool ConfigGetParamBool; +extern ptr_ConfigGetParamString ConfigGetParamString; + +/* definitions of pointers to Core config functions */ +ptr_ConfigOpenSection ConfigOpenSection = NULL; +ptr_ConfigDeleteSection ConfigDeleteSection = NULL; +ptr_ConfigSaveSection ConfigSaveSection = NULL; +ptr_ConfigSetParameter ConfigSetParameter = NULL; +ptr_ConfigGetParameter ConfigGetParameter = NULL; +ptr_ConfigGetParameterHelp ConfigGetParameterHelp = NULL; +ptr_ConfigSetDefaultInt ConfigSetDefaultInt = NULL; +ptr_ConfigSetDefaultFloat ConfigSetDefaultFloat = NULL; +ptr_ConfigSetDefaultBool ConfigSetDefaultBool = NULL; +ptr_ConfigSetDefaultString ConfigSetDefaultString = NULL; +ptr_ConfigGetParamInt ConfigGetParamInt = NULL; +ptr_ConfigGetParamFloat ConfigGetParamFloat = NULL; +ptr_ConfigGetParamBool ConfigGetParamBool = NULL; +ptr_ConfigGetParamString ConfigGetParamString = NULL; + +/* local variables */ +static void (*l_DebugCallback)(void *, int, const char *) = NULL; +static void *l_DebugCallContext = NULL; +static int l_PluginInit = 0; +static int l_PausedForSync = 1; /* Audio is started in paused state after SDL initialization */ +static m64p_handle l_ConfigAudio; + +#undef PLUGIN_VERSION +#define PLUGIN_VERSION "r2" + +#define PREFIX "[audio] " +#define log(f, ...) printf(PREFIX f, ##__VA_ARGS__) + +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +/* comment from jttl_audio: + * This sets default frequency what is used if rom doesn't want to change it. + * Popably 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 OSS_FRAGMENT_COUNT 5 + +/* Read header for type definition */ +static AUDIO_INFO audio_info; +/* Audio frequency, this is usually obtained from the game, + * but for compatibility we set default value */ +static int input_freq = DEFAULT_FREQUENCY; + +/* config stuff */ +static int minimum_rate = 8000; +static int pich_percent = 100; + +static unsigned int sound_out_buff[48000 * 4]; // ~4 sec, enough? +static unsigned int sound_silence_buff[48000 / 10]; + +static int sound_dev = -1; +static int output_freq; +static int resample_step; +static int silence_bytes; +static int fade_len; +static int fade_step; + +/* rates Pandora supports natively, we'll need to do + * less resampling if we stick close to these */ +static const int supported_rates[] = { + 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000, +}; + +static int init(int freq) +{ + static int output_freq_old; + int stereo, bits, rate; + int bsize, frag; + int ret, i; + + input_freq = freq; + + minimum_rate = ConfigGetParamInt(l_ConfigAudio, "MINIMUM_RATE" ); + output_freq = minimum_rate; + pich_percent = ConfigGetParamInt(l_ConfigAudio, "PITCH_PERCENT" ); + + // find lowest alowed rate that is higher than game's output + for (i = 0; i < ARRAY_SIZE(supported_rates); i++) { + int rate = supported_rates[i]; + if (freq <= rate + 100 && rate >= minimum_rate) { + output_freq = rate; + break; + } + } + + if (sound_dev >= 0) { + if (output_freq == output_freq_old) + goto finish; + close(sound_dev); + } + + sound_dev = open("/dev/dsp", O_WRONLY); + if (sound_dev == -1) { + perror(PREFIX "open(\"/dev/dsp\")"); + sound_dev = open("/dev/dsp1", O_WRONLY); + if (sound_dev == -1) { + perror(PREFIX "open(\"/dev/dsp1\")"); + return -1; + } + } + + bsize = output_freq / 20 * 4; // ~50ms + for (frag = 0; bsize; bsize >>= 1, frag++) + ; + + frag |= OSS_FRAGMENT_COUNT << 16; // fragment count + ret = ioctl(sound_dev, SNDCTL_DSP_SETFRAGMENT, &frag); + if (ret < 0) + perror(PREFIX "SNDCTL_DSP_SETFRAGMENT failed"); + + silence_bytes = output_freq / 30 * 4; // ~ 25ms + memset(sound_silence_buff, 0, sizeof(sound_silence_buff)); + + stereo = 1; + bits = 16; + rate = output_freq; + ret = ioctl(sound_dev, SNDCTL_DSP_STEREO, &stereo); + if (ret == 0) + ret = ioctl(sound_dev, SNDCTL_DSP_SETFMT, &bits); + if (ret == 0) + ret = ioctl(sound_dev, SNDCTL_DSP_SPEED, &rate); + if (ret < 0) + perror(PREFIX "failed to set audio format"); + + if (rate != output_freq) + log("warning: output rate %d differs from desired %d\n", rate, output_freq); + +finish: + resample_step = freq * 1024 / output_freq; + if (pich_percent != 100) + resample_step = resample_step * pich_percent / 100; + fade_len = output_freq / 1000; + fade_step = 64 * 1024 / fade_len; + + log("(re)started, rates: %d -> %d\n", freq, output_freq); + + return 0; +} + +static unsigned int resample(unsigned int *input, int in_size, unsigned int *output, int out_size) +{ + unsigned int i, j, t; + int step = resample_step; + int count = 0; + + t = input[0]; + t = (t << 16) | (t >> 16); + + for (i = j = 0; i < out_size / 4;) + { + output[i++] = t; + + count += step; + while (count >= 1024) { + count -= 1024; + j++; + if (j >= in_size / 4) + goto out; + t = input[j]; + t = (t << 16) | (t >> 16); + } + } + +out: + return i * 4; // number of bytes to output +} + +static void fade(unsigned int *buf, int buf_len, int up) +{ + signed short *sb = (void *)buf; + int len = fade_len; + int step = fade_step; + int counter = 0, mult, mult_step; + int i; + + if (up) { + mult = 0; + mult_step = 1; + } else { + sb += buf_len / 2 - len * 2; + if (sb < (signed short *)buf) + sb = (void *)buf; + mult = 63; + mult_step = -1; + } + + for (i = 0; i < len; i++) { + counter += step; + while (counter >= 1024) { + counter -= 1024; + mult += mult_step; + } + sb[i * 2] = sb[i * 2] / 64 * mult; + sb[i * 2 + 1] = sb[i * 2] / 64 * mult; + } +} + +EXPORT void CALL AiDacrateChanged( int SystemType ) +{ + int f = input_freq; + switch (SystemType) + { + case SYSTEM_NTSC: + f = 48681812 / (*audio_info.AI_DACRATE_REG + 1); + break; + case SYSTEM_PAL: + f = 49656530 / (*audio_info.AI_DACRATE_REG + 1); + break; + case SYSTEM_MPAL: + f = 48628316 / (*audio_info.AI_DACRATE_REG + 1); + break; + } + init(f); +} + +EXPORT void CALL AiLenChanged(void) +{ + static int had_uflow; + unsigned int ret, len, *in, *part2, part2_len; + audio_buf_info bi; + + if (sound_dev < 0) + return; + + // XXX: what about alignment? len limit? rdram overflow? + in = (unsigned int *)(audio_info.RDRAM + (*audio_info.AI_DRAM_ADDR_REG & 0xFFFFFC)); + len = *audio_info.AI_LEN_REG; + + //log("AiLenChanged: %u\n", len); + + ret = resample(in, len, sound_out_buff, sizeof(sound_out_buff)); + if (ret >= sizeof(sound_out_buff)) + log("overflow, in_len=%d\n", len); + + if (had_uflow) + fade(sound_out_buff, ret / 2, 1); + + write(sound_dev, sound_out_buff, ret / 2); + part2 = sound_out_buff + (ret / 4) / 2; + part2_len = ret - ret / 2; + + // try to keep at most 2 free fragments to avoid + // hardware underflows that cause crackling on pandora + // XXX: for some reason GETOSPACE only works after write? + // XXX: .fragments sometimes overflows? ALSA OSS emu bugs? + ret = ioctl(sound_dev, SNDCTL_DSP_GETOSPACE, &bi); + if (ret == 0 && 2 < bi.fragments && bi.fragments <= OSS_FRAGMENT_COUNT) { + fade(part2, part2_len, 0); + write(sound_dev, part2, part2_len); + write(sound_dev, sound_silence_buff, silence_bytes); + if (bi.fragments == 4) + write(sound_dev, sound_silence_buff, silence_bytes); + had_uflow = 1; + } + else { + write(sound_dev, part2, part2_len); + had_uflow = 0; + } +} + +/* +EXPORT DWORD CALL AiReadLength(void) +{ + return 0; +} +EXPORT void CALL CloseDLL(void) +{ + if (sound_dev >= 0) + close(sound_dev); + sound_dev = -1; +} +*/ +static char config_file[512]; + +static void read_config(void) +{ + char cfg[512], *p; + int val; + FILE *f; + + f = fopen(config_file, "r"); + if (f == NULL) { + perror(PREFIX "can't open config"); + return; + } + + while (1) { + p = fgets(cfg, sizeof(cfg), f); + if (p == NULL) + break; + if (p[0] == '#') + continue; + + if (sscanf(p, "pich_percent = %d", &val) == 1 && 10 <= val && val <= 1000) { + pich_percent = val; + break; + } + if (sscanf(p, "minimum_rate = %d", &val) == 1) { + minimum_rate = val; + break; + } + } + fclose(f); +} +/* +EXPORT void CALL SetConfigDir(char *configDir) +{ + snprintf(config_file, sizeof(config_file), "%snotaz_audio.conf", configDir); + read_config(); +} + +EXPORT void CALL DllTest(HWND hParent) +{ +} + +EXPORT void CALL GetDllInfo(PLUGIN_INFO *PluginInfo) +{ + PluginInfo->Version = 0x0101; + PluginInfo->Type = PLUGIN_TYPE_AUDIO; + strcpy(PluginInfo->Name, "notaz's OSS audio " PLUGIN_VERSION); + PluginInfo->NormalMemory = TRUE; + PluginInfo->MemoryBswaped = TRUE; +} + +EXPORT void CALL DllAbout(HWND hParent) +{ +} + +EXPORT void CALL DllConfig(HWND hParent) +{ + char cmd[512]; + FILE *f; + + f = fopen(config_file, "r"); + if (f != NULL) + fclose(f); + else { + f = fopen(config_file, "w"); + if (f != NULL) { + fprintf(f, "# minimum sample rate to use. Higher values sound better on Pandora's DAC.\n"); + fprintf(f, "minimum_rate = %d\n\n", minimum_rate); + fprintf(f, "# sound playback speed compared to normal (10-200%%)\n"); + fprintf(f, "# this will affect gameplay speed and sound pitch\n"); + fprintf(f, "pich_percent = %d\n\n", pich_percent); + fclose(f); + } + } + + snprintf(cmd, sizeof(cmd), "mousepad \"%s\"", config_file); + system(cmd); + + read_config(); +} +*/ +EXPORT int CALL InitiateAudio(AUDIO_INFO Audio_Info) +{ + if (!l_PluginInit) + return 0; + + audio_info = Audio_Info; + return 1; +} + +EXPORT int CALL RomOpen(void) +{ + if (!l_PluginInit) + return 0; + + /* This function is for compatibility with Mupen64. */ + init(input_freq); + return 1; +} + +EXPORT void CALL RomClosed(void) +{ +} + +EXPORT void CALL ProcessAList(void) +{ +} + +EXPORT void CALL SetSpeedFactor(int percentage) +{ +} + +EXPORT void CALL VolumeUp(void) +{ +} + +EXPORT void CALL VolumeDown(void) +{ +} + +EXPORT void CALL VolumeMute(void) +{ +} +EXPORT int CALL VolumeGetLevel(void) +{ + return 255; +} + +EXPORT void CALL VolumeSetLevel(int level) +{ +} + +EXPORT const char * CALL VolumeGetString(void) +{ + return "100%%"; +} + +/* 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); +} + +/* 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, bSaveConfig; + float fConfigParamsVersion = 0.0f; + + 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; + } + + /* Get the core config function pointers from the library handle */ + ConfigOpenSection = (ptr_ConfigOpenSection) osal_dynlib_getproc(CoreLibHandle, "ConfigOpenSection"); + ConfigDeleteSection = (ptr_ConfigDeleteSection) osal_dynlib_getproc(CoreLibHandle, "ConfigDeleteSection"); + ConfigSaveSection = (ptr_ConfigSaveSection) osal_dynlib_getproc(CoreLibHandle, "ConfigSaveSection"); + ConfigSetParameter = (ptr_ConfigSetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigSetParameter"); + ConfigGetParameter = (ptr_ConfigGetParameter) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParameter"); + ConfigSetDefaultInt = (ptr_ConfigSetDefaultInt) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultInt"); + ConfigSetDefaultFloat = (ptr_ConfigSetDefaultFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultFloat"); + ConfigSetDefaultBool = (ptr_ConfigSetDefaultBool) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultBool"); + ConfigSetDefaultString = (ptr_ConfigSetDefaultString) osal_dynlib_getproc(CoreLibHandle, "ConfigSetDefaultString"); + ConfigGetParamInt = (ptr_ConfigGetParamInt) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamInt"); + ConfigGetParamFloat = (ptr_ConfigGetParamFloat) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamFloat"); + ConfigGetParamBool = (ptr_ConfigGetParamBool) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamBool"); + ConfigGetParamString = (ptr_ConfigGetParamString) osal_dynlib_getproc(CoreLibHandle, "ConfigGetParamString"); + + if (!ConfigOpenSection || !ConfigDeleteSection || !ConfigSetParameter || !ConfigGetParameter || + !ConfigSetDefaultInt || !ConfigSetDefaultFloat || !ConfigSetDefaultBool || !ConfigSetDefaultString || + !ConfigGetParamInt || !ConfigGetParamFloat || !ConfigGetParamBool || !ConfigGetParamString) + return M64ERR_INCOMPATIBLE; + + /* ConfigSaveSection was added in Config API v2.1.0 */ + if (ConfigAPIVersion >= 0x020100 && !ConfigSaveSection) + return M64ERR_INCOMPATIBLE; + + /* get a configuration section handle */ + if (ConfigOpenSection("Audio-Notaz", &l_ConfigAudio) != M64ERR_SUCCESS) + { + DebugMessage(M64MSG_ERROR, "Couldn't open config section 'Audio-Notaz'"); + return M64ERR_INPUT_NOT_FOUND; + } + + /* check the section version number */ + bSaveConfig = 0; + if (ConfigGetParameter(l_ConfigAudio, "Version", M64TYPE_FLOAT, &fConfigParamsVersion, sizeof(float)) != M64ERR_SUCCESS) + { + DebugMessage(M64MSG_WARNING, "No version number in 'Audio-Notaz' config section. Setting defaults."); + ConfigDeleteSection("Audio-Notaz"); + ConfigOpenSection("Audio-Notaz", &l_ConfigAudio); + bSaveConfig = 1; + } + else if (((int) fConfigParamsVersion) != ((int) CONFIG_PARAM_VERSION)) + { + DebugMessage(M64MSG_WARNING, "Incompatible version %.2f in 'Audio-Notaz' config section: current is %.2f. Setting defaults.", fConfigParamsVersion, (float) CONFIG_PARAM_VERSION); + ConfigDeleteSection("Audio-Notaz"); + ConfigOpenSection("Audio-Notaz", &l_ConfigAudio); + bSaveConfig = 1; + } + else if ((CONFIG_PARAM_VERSION - fConfigParamsVersion) >= 0.0001f) + { + /* handle upgrades */ + float fVersion = CONFIG_PARAM_VERSION; + ConfigSetParameter(l_ConfigAudio, "Version", M64TYPE_FLOAT, &fVersion); + DebugMessage(M64MSG_INFO, "Updating parameter set version in 'Audio-Notaz' config section to %.2f", fVersion); + bSaveConfig = 1; + } + + /* set the default values for this plugin */ + ConfigSetDefaultFloat(l_ConfigAudio, "Version", CONFIG_PARAM_VERSION, "Mupen64Plus Notaz Audio Plugin config parameter version number"); + ConfigSetDefaultInt(l_ConfigAudio, "MINIMUM_RATE", 44100, "Minimum sample rate to use. Higher values sound better on Pandora's DAC."); + ConfigSetDefaultInt(l_ConfigAudio, "PITCH_PERCENT", 100, "sound playback speed compared to normal (10-200%%). this will affect gameplay speed and sound pitch"); + + if (bSaveConfig && ConfigAPIVersion >= 0x020100) + ConfigSaveSection("Audio-SDL"); + + 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; + + if (sound_dev >= 0) + close(sound_dev); + sound_dev = -1; + + l_PluginInit = 0; + log("Notaz-audio Plugin shutdown\n"); + return M64ERR_SUCCESS; +} + + +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 = NOTAZ_AUDIO_PLUGIN_VERSION; + + if (APIVersion != NULL) + *APIVersion = AUDIO_PLUGIN_API_VERSION; + + if (PluginNamePtr != NULL) + *PluginNamePtr = "Mupen64Plus Notaz Audio Plugin"; + + if (Capabilities != NULL) + { + *Capabilities = 0; + } + + return M64ERR_SUCCESS; +} diff --git a/source/notaz_audio/osal_dynamiclib.h b/source/notaz_audio/osal_dynamiclib.h new file mode 100644 index 0000000..daef154 --- /dev/null +++ b/source/notaz_audio/osal_dynamiclib.h @@ -0,0 +1,30 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-core - osal/dynamiclib.h * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * 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. * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#if !defined(OSAL_DYNAMICLIB_H) +#define OSAL_DYNAMICLIB_H + +#include "m64p_types.h" + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName); + +#endif /* #define OSAL_DYNAMICLIB_H */ + diff --git a/source/notaz_audio/osal_dynamiclib_unix.c b/source/notaz_audio/osal_dynamiclib_unix.c new file mode 100644 index 0000000..b3b7ba5 --- /dev/null +++ b/source/notaz_audio/osal_dynamiclib_unix.c @@ -0,0 +1,37 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-core - osal/dynamiclib_unix.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * 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 +#include +#include + +#include "m64p_types.h" +#include "osal_dynamiclib.h" + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName) +{ + if (pccProcedureName == NULL) + return NULL; + + return dlsym(LibHandle, pccProcedureName); +} + + diff --git a/source/notaz_audio/osal_dynamiclib_win32.c b/source/notaz_audio/osal_dynamiclib_win32.c new file mode 100644 index 0000000..685d717 --- /dev/null +++ b/source/notaz_audio/osal_dynamiclib_win32.c @@ -0,0 +1,74 @@ +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * Mupen64plus-ui-console - osal_dynamiclib_win32.c * + * Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ * + * Copyright (C) 2009 Richard Goedeken * + * * + * 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 +#include +#include + +#include "m64p_types.h" +#include "osal_dynamiclib.h" + +m64p_error osal_dynlib_open(m64p_dynlib_handle *pLibHandle, const char *pccLibraryPath) +{ + if (pLibHandle == NULL || pccLibraryPath == NULL) + return M64ERR_INPUT_ASSERT; + + *pLibHandle = LoadLibrary(pccLibraryPath); + + if (*pLibHandle == NULL) + { + char *pchErrMsg; + DWORD dwErr = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL); + fprintf(stderr, "LoadLibrary('%s') error: %s\n", pccLibraryPath, pchErrMsg); + LocalFree(pchErrMsg); + return M64ERR_INPUT_NOT_FOUND; + } + + return M64ERR_SUCCESS; +} + +void * osal_dynlib_getproc(m64p_dynlib_handle LibHandle, const char *pccProcedureName) +{ + if (pccProcedureName == NULL) + return NULL; + + return GetProcAddress(LibHandle, pccProcedureName); +} + +m64p_error osal_dynlib_close(m64p_dynlib_handle LibHandle) +{ + int rval = FreeLibrary(LibHandle); + + if (rval == 0) + { + char *pchErrMsg; + DWORD dwErr = GetLastError(); + FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwErr, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &pchErrMsg, 0, NULL); + fprintf(stderr, "FreeLibrary() error: %s\n", pchErrMsg); + LocalFree(pchErrMsg); + return M64ERR_INTERNAL; + } + + return M64ERR_SUCCESS; +}