diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
index a16e29dc7f..3df7b8c189 100644
--- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
+++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj
@@ -225,7 +225,9 @@
+
+
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.cs
index 738e7c0a49..e8cf741c35 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64.cs
@@ -10,7 +10,7 @@ using BizHawk.Emulation.Cores.Consoles.Nintendo.N64;
namespace BizHawk.Emulation.Cores.Nintendo.N64
{
- public class N64 : IEmulator, IVideoProvider
+ public class N64 : IEmulator
{
public List> GetCpuFlagsAndRegisters()
{
@@ -64,24 +64,16 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
public string BoardName { get { return null; } }
public CoreComm CoreComm { get; private set; }
- public byte[] rom;
- public GameInfo game;
- public IVideoProvider VideoProvider { get { return this; } }
- public int[] frameBuffer;// = new int[800 * 600];
- public int[] GetVideoBuffer() { return frameBuffer; }
- public int VirtualWidth { get; set; }
- public int BufferWidth { get; set; }
- public int BufferHeight { get; set; }
- public int BackgroundColor { get { return 0; } }
+ private N64VideoProvider videoProvider;
+ public IVideoProvider VideoProvider { get { return videoProvider; } }
private DisplayType _display_type = DisplayType.NTSC;
public DisplayType DisplayType { get { return _display_type; } }
- public SpeexResampler resampler;
-
+ private N64Audio audioProvider;
public ISoundProvider SoundProvider { get { return null; } }
- public ISyncSoundProvider SyncSoundProvider { get { return resampler; } }
+ public ISyncSoundProvider SyncSoundProvider { get { return audioProvider.Resampler; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
@@ -118,18 +110,19 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
}
};
- public int Frame { get; set; }
+ public int Frame { get; private set; }
public int LagCount { get; set; }
- public bool IsLagFrame { get; set; }
+ public bool IsLagFrame { get; private set; }
public void ResetCounters()
{
Frame = 0;
LagCount = 0;
IsLagFrame = false;
}
+
public void FrameAdvance(bool render, bool rendersound)
{
- RefreshMemoryCallbacks();
+ audioProvider.RenderSound = rendersound;
if (Controller["Reset"])
{
@@ -142,10 +135,15 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
IsLagFrame = true;
api.frame_advance();
+
if (IsLagFrame) LagCount++;
- Frame++;
+ Frame++;
}
+ ///
+ /// Translates controller inputs from EmuHawk and
+ /// shoves them into mupen64plus
+ ///
public void setControllers()
{
CoreComm.InputCallback.Call();
@@ -171,6 +169,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
}
}
+ ///
+ /// Read all buttons from a controller and translate them
+ /// into a form the N64 understands
+ ///
+ /// Number of controller to translate
+ /// Bitlist of pressed buttons
public int ReadController(int num)
{
int buttons = 0;
@@ -193,7 +197,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
return buttons;
}
- public bool DeterministicEmulation { get; set; }
+ public bool DeterministicEmulation { get { return false; } }
public byte[] ReadSaveRam()
{
@@ -212,12 +216,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
public bool SaveRamModified { get { return true; } set { } }
- void SyncState(Serializer ser)
- {
- ser.BeginSection("N64");
- ser.EndSection();
- }
-
// these next 5 functions are all exact copy paste from gambatte.
// if something's wrong here, it's probably wrong there too
@@ -321,11 +319,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// we RefreshMemoryCallbacks() after the triggers in case the trigger turns itself off at that point
if (mcs.HasReads)
- readcb = delegate(uint addr) { mcs.CallRead(addr); RefreshMemoryCallbacks(); };
+ readcb = delegate(uint addr) { mcs.CallRead(addr); };
else
readcb = null;
if (mcs.HasWrites)
- writecb = delegate(uint addr) { mcs.CallWrite(addr); RefreshMemoryCallbacks(); };
+ writecb = delegate(uint addr) { mcs.CallWrite(addr); };
else
writecb = null;
@@ -394,11 +392,21 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
public void Dispose()
{
+ videoProvider.Dispose();
+ audioProvider.Dispose();
api.Dispose();
}
- mupen64plusApi api;
+ // mupen64plus DLL Api
+ private mupen64plusApi api;
+ ///
+ /// Create mupen64plus Emulator
+ ///
+ /// Core communication object
+ /// Game information of game to load
+ /// Rom that should be loaded
+ /// N64SyncSettings object
public N64(CoreComm comm, GameInfo game, byte[] rom, object SyncSettings)
{
int SaveType = 0;
@@ -406,8 +414,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
SaveType = 1;
CoreComm = comm;
- this.rom = rom;
- this.game = game;
this.SyncSettings = (N64SyncSettings)SyncSettings ?? new N64SyncSettings();
@@ -439,7 +445,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
api = new mupen64plusApi(this, rom, this.SyncSettings.GetVPS(game), SaveType);
api.SetM64PInputCallback(new mupen64plusApi.InputCallback(setControllers));
+ audioProvider = new N64Audio(api);
+ videoProvider = new N64VideoProvider(api);
+ api.FrameFinished += videoProvider.DoVideoFrame;
+ api.VInterrupt += audioProvider.DoAudioFrame;
+
InitMemoryDomains();
+ RefreshMemoryCallbacks();
}
N64SyncSettings SyncSettings;
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64Audio.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64Audio.cs
new file mode 100644
index 0000000000..69dfe5d20b
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64Audio.cs
@@ -0,0 +1,84 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.N64
+{
+ class N64Audio : IDisposable
+ {
+ ///
+ /// mupen64 DLL Api
+ ///
+ private mupen64plusApi api;
+ ///
+ /// Buffer for audio data
+ ///
+ private short[] audioBuffer = new short[0];
+ private uint _samplingRate = 0;
+ ///
+ /// Currently active sampling rate
+ ///
+ public uint SamplingRate
+ {
+ get
+ {
+ return _samplingRate;
+ }
+ private set
+ {
+ _samplingRate = value;
+ Resampler.ChangeRate(_samplingRate, 44100, _samplingRate, 44100);
+ }
+ }
+ ///
+ /// Resampler for audio output
+ ///
+ public SpeexResampler Resampler { get; private set; }
+ public bool RenderSound { get; set; }
+
+ ///
+ /// Creates a N64 Audio subsystem
+ ///
+ /// Mupen64 api which is used for fetching sound
+ public N64Audio(mupen64plusApi api)
+ {
+ this.api = api;
+ _samplingRate = api.GetSamplingRate();
+ Resampler = new SpeexResampler(6, SamplingRate, 44100,
+ SamplingRate, 44100);
+ }
+
+ ///
+ /// Fetches the audio buffer from mupen64plus and pushes it into the
+ /// Resampler for audio output
+ ///
+ public void DoAudioFrame()
+ {
+ uint m64pSamplingRate = api.GetSamplingRate();
+ if (m64pSamplingRate != SamplingRate)
+ SamplingRate = m64pSamplingRate;
+
+ int audioBufferSize = api.GetAudioBufferSize();
+ if (audioBuffer.Length < audioBufferSize)
+ audioBuffer = new short[audioBufferSize];
+
+ if (audioBufferSize > 0)
+ {
+ api.GetAudioBuffer(audioBuffer);
+ if (RenderSound)
+ Resampler.EnqueueSamples(audioBuffer, audioBufferSize / 2);
+ }
+ }
+
+ public void Dispose()
+ {
+ if(Resampler != null)
+ Resampler.Dispose();
+ Resampler = null;
+ // Api is disposed by N64
+ api = null;
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64VideoProvider.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64VideoProvider.cs
new file mode 100644
index 0000000000..d49a819c06
--- /dev/null
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/N64VideoProvider.cs
@@ -0,0 +1,70 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using BizHawk.Emulation.Common;
+
+namespace BizHawk.Emulation.Cores.Nintendo.N64
+{
+ class N64VideoProvider : IVideoProvider, IDisposable
+ {
+ private int[] frameBuffer;
+ private mupen64plusApi api;
+
+ ///
+ /// Creates N64 Video system with mupen64plus backend
+ ///
+ /// mupen64plus DLL that is used
+ public N64VideoProvider(mupen64plusApi api)
+ {
+ this.api = api;
+ int width = 0;
+ int height = 0;
+ api.GetScreenDimensions(ref width, ref height);
+ SetBufferSize(width, height);
+ }
+
+ public int[] GetVideoBuffer()
+ {
+ return frameBuffer;
+ }
+
+ public int VirtualWidth { get { return BufferWidth; } }
+ public int BufferWidth { get; private set; }
+ public int BufferHeight { get; private set; }
+ public int BackgroundColor { get { return 0; } }
+
+ ///
+ /// Fetches current frame buffer from mupen64
+ ///
+ public void DoVideoFrame()
+ {
+ int width = 0;
+ int height = 0;
+ api.GetScreenDimensions(ref width, ref height);
+ if (width != BufferWidth || height != BufferHeight)
+ {
+ SetBufferSize(width, height);
+ }
+ api.Getm64pFrameBuffer(frameBuffer, ref width, ref height);
+ }
+
+ ///
+ /// Sets a new width and height for frame buffer
+ ///
+ /// New width in pixels
+ /// New height in pixels
+ private void SetBufferSize(int width, int height)
+ {
+ BufferHeight = height;
+ BufferWidth = width;
+ frameBuffer = new int[width * height];
+ }
+
+ public void Dispose()
+ {
+ // api is disposed by N64
+ api = null;
+ }
+ }
+}
diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/mupen64plusApi.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/mupen64plusApi.cs
index 05eb26d55c..3efe85692c 100644
--- a/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/mupen64plusApi.cs
+++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/N64/mupen64plusApi.cs
@@ -12,14 +12,13 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
{
public class mupen64plusApi : IDisposable
{
+ // Only left in because api needs to know the number of frames passed
+ // because of a bug
private readonly N64 bizhawkCore;
static mupen64plusApi AttachedCore = null;
bool disposed = false;
- uint m64pSamplingRate;
- short[] m64pAudioBuffer = new short[2];
-
Thread m64pEmulator;
AutoResetEvent m64pFrameComplete = new AutoResetEvent(false);
@@ -439,12 +438,12 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
public mupen64plusApi(N64 bizhawkCore, byte[] rom, VideoPluginSettings video_settings, int SaveType)
{
+ // There can only be one core (otherwise breaks mupen64plus)
if (AttachedCore != null)
{
AttachedCore.Dispose();
AttachedCore = null;
}
-
this.bizhawkCore = bizhawkCore;
string VidDllName;
@@ -485,7 +484,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
connectFunctionPointers();
// Start up the core
- m64p_error result = m64pCoreStartup(0x20001, "", "", "Core", (IntPtr foo, int level, string Message) => { }, "", IntPtr.Zero);
+ m64p_error result = m64pCoreStartup(0x20001, "", "", "Core",
+ (IntPtr foo, int level, string Message) => { },
+ "", IntPtr.Zero);
// Set the savetype if needed
if (SaveType != 0)
@@ -498,9 +499,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// Pass the rom to the core
result = m64pCoreDoCommandByteArray(m64p_command.M64CMD_ROM_OPEN, rom.Length, rom);
- // Resize the video to the size in bizhawk's settings
- SetVideoSize(video_settings.Width, video_settings.Height);
-
// Open the general video settings section in the config system
IntPtr video_section = IntPtr.Zero;
m64pConfigOpenSection("Video-General", ref video_section);
@@ -511,6 +509,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
set_video_parameters(video_settings);
+ // Order of plugin loading is important, do not change!
// Set up and connect the graphics plugin
result = GfxPluginStartup(CoreDll, "Video", (IntPtr foo, int level, string Message) => { });
result = m64pCoreAttachPlugin(m64p_plugin_type.M64PLUGIN_GFX, GfxDll);
@@ -520,23 +519,22 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
result = m64pCoreAttachPlugin(m64p_plugin_type.M64PLUGIN_AUDIO, AudDll);
// Set up our input plugin
- result = AudPluginStartup(CoreDll, "Input", (IntPtr foo, int level, string Message) => { });
+ result = AudPluginStartup(CoreDll, "Input",
+ (IntPtr foo, int level, string Message) => { });
result = m64pCoreAttachPlugin(m64p_plugin_type.M64PLUGIN_INPUT, InpDll);
// Set up and connect the RSP plugin
result = RspPluginStartup(CoreDll, "RSP", (IntPtr foo, int level, string Message) => { });
result = m64pCoreAttachPlugin(m64p_plugin_type.M64PLUGIN_RSP, RspDll);
- // Set up the frame callback function
- m64pFrameCallback = new FrameCallback(Getm64pFrameBuffer);
- result = m64pCoreDoCommandFrameCallback(m64p_command.M64CMD_SET_FRAME_CALLBACK, 0, m64pFrameCallback);
-
- // Set up the VI callback function
- m64pVICallback = new VICallback(VI);
- result = m64pCoreDoCommandVICallback(m64p_command.M64CMD_SET_VI_CALLBACK, 0, m64pVICallback);
-
InitSaveram();
+ // Initialize event invoker
+ m64pFrameCallback = new FrameCallback(FireFrameFinishedEvent);
+ result = m64pCoreDoCommandFrameCallback(m64p_command.M64CMD_SET_FRAME_CALLBACK, 0, m64pFrameCallback);
+ m64pVICallback = new VICallback(FireVIEvent);
+ result = m64pCoreDoCommandVICallback(m64p_command.M64CMD_SET_VI_CALLBACK, 0, m64pVICallback);
+
// Start the emulator in another thread
m64pEmulator = new Thread(ExecuteEmulator);
m64pEmulator.Start();
@@ -544,14 +542,14 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// Wait for the core to boot up
m64pStartupComplete.WaitOne();
- // Set up the resampler
- m64pSamplingRate = (uint)AudGetAudioRate();
- bizhawkCore.resampler = new SpeexResampler(6, m64pSamplingRate, 44100, m64pSamplingRate, 44100, null, null);
-
AttachedCore = this;
}
volatile bool emulator_running = false;
+ ///
+ /// Starts execution of mupen64plus
+ /// Does not return until the emulator stops
+ ///
public void ExecuteEmulator()
{
emulator_running = true;
@@ -611,16 +609,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
RspPluginShutdown = (PluginShutdown)Marshal.GetDelegateForFunctionPointer(GetProcAddress(RspDll, "PluginShutdown"), typeof(PluginShutdown));
}
- public void SetVideoSize(int vidX, int vidY)
- {
- bizhawkCore.VirtualWidth = vidX;
- bizhawkCore.BufferWidth = vidX;
- bizhawkCore.BufferHeight = vidY;
-
- bizhawkCore.frameBuffer = new int[vidX * vidY];
- m64p_FrameBuffer = new int[vidX * vidY];
- }
-
+ ///
+ /// Puts plugin settings of EmuHawk into mupen64plus
+ ///
+ /// Settings to put into mupen64plus
public void set_video_parameters(VideoPluginSettings video_settings)
{
IntPtr video_plugin_section = IntPtr.Zero;
@@ -656,45 +648,34 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
}
}
-
- int[] m64p_FrameBuffer;
-
+ private int[] m64pBuffer = new int[0];
///
- /// This function will be used as the frame callback. It pulls the framebuffer from mupen64plus
+ /// This function copies the frame buffer from mupen64plus
///
- public void Getm64pFrameBuffer()
+ public void Getm64pFrameBuffer(int[] buffer, ref int width, ref int height)
{
- int width = 0;
- int height = 0;
-
- // Get the size of the frame buffer
- GFXReadScreen2Res(IntPtr.Zero, ref width, ref height, 0);
-
- // If it's not the same size as the current one, change the sizes
- if (width != bizhawkCore.BufferWidth || height != bizhawkCore.BufferHeight)
- {
- SetVideoSize(width, height);
- }
-
+ if(m64pBuffer.Length != width * height)
+ m64pBuffer = new int[width * height];
// Actually get the frame buffer
- GFXReadScreen2(m64p_FrameBuffer, ref width, ref height, 0);
+ GFXReadScreen2(m64pBuffer, ref width, ref height, 0);
// vflip
- int fromindex = bizhawkCore.BufferWidth * (bizhawkCore.BufferHeight - 1) * 4;
+ int fromindex = width * (height - 1) * 4;
int toindex = 0;
- for (int j = 0; j < bizhawkCore.BufferHeight; j++)
+
+ for (int j = 0; j < height; j++)
{
- Buffer.BlockCopy(m64p_FrameBuffer, fromindex, bizhawkCore.frameBuffer, toindex, bizhawkCore.BufferWidth * 4);
- fromindex -= bizhawkCore.BufferWidth * 4;
- toindex += bizhawkCore.BufferWidth * 4;
+ Buffer.BlockCopy(m64pBuffer, fromindex, buffer, toindex, width * 4);
+ fromindex -= width * 4;
+ toindex += width * 4;
}
// opaque
unsafe
{
- fixed (int* ptr = &bizhawkCore.frameBuffer[0])
+ fixed (int* ptr = &buffer[0])
{
- int l = bizhawkCore.frameBuffer.Length;
+ int l = buffer.Length;
for (int i = 0; i < l; i++)
{
ptr[i] |= unchecked((int)0xff000000);
@@ -703,32 +684,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
}
}
- ///
- /// This function will be used as the VI callback. It checks the audio rate and updates the resampler if necessary.
- /// It then polls the audio buffer for samples. It then marks the frame as complete.
- ///
- public void VI()
- {
- uint s = (uint)AudGetAudioRate();
- if (s != m64pSamplingRate)
- {
- m64pSamplingRate = s;
- bizhawkCore.resampler.ChangeRate(s, 44100, s, 44100);
- //Console.WriteLine("N64 ARate Change {0}", s);
- }
-
- int m64pAudioBufferSize = AudGetBufferSize();
- if (m64pAudioBuffer.Length < m64pAudioBufferSize)
- m64pAudioBuffer = new short[m64pAudioBufferSize];
-
- if (m64pAudioBufferSize > 0)
- {
- AudReadAudioBuffer(m64pAudioBuffer);
- bizhawkCore.resampler.EnqueueSamples(m64pAudioBuffer, m64pAudioBufferSize / 2);
- }
- m64pFrameComplete.Set();
- }
-
public int get_memory_size(N64_MEMORY id)
{
return m64pMemGetSize(id);
@@ -777,6 +732,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// When using the dynamic recompiler if a state is loaded too early some pointers are not set up yet, so mupen
// tries to access null pointers and the emulator crashes. It seems like it takes 2 frames to fully set up the recompiler,
// so if two frames haven't been run yet, run them, then load the state.
+
if (bizhawkCore.Frame < 2)
{
frame_advance();
@@ -851,9 +807,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
// Backup the saveram in case bizhawk wants to get at is after we've freed the libraries
saveram_backup = SaveSaveram();
- bizhawkCore.resampler.Dispose();
- bizhawkCore.resampler = null;
-
m64pCoreDetachPlugin(m64p_plugin_type.M64PLUGIN_GFX);
m64pCoreDetachPlugin(m64p_plugin_type.M64PLUGIN_AUDIO);
m64pCoreDetachPlugin(m64p_plugin_type.M64PLUGIN_INPUT);
@@ -878,6 +831,49 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
disposed = true;
}
}
+
+ public void GetScreenDimensions(ref int width, ref int height)
+ {
+ GFXReadScreen2Res(IntPtr.Zero, ref width, ref height, 0);
+ }
+
+ public uint GetSamplingRate()
+ {
+ return (uint)AudGetAudioRate();
+ }
+
+ public int GetAudioBufferSize()
+ {
+ return AudGetBufferSize();
+ }
+
+ public void GetAudioBuffer(short[] buffer)
+ {
+ AudReadAudioBuffer(buffer);
+ }
+
+ public event Action FrameFinished;
+ public event Action VInterrupt;
+
+ private void FireFrameFinishedEvent()
+ {
+ // Execute Frame Callback functions
+ if (FrameFinished != null)
+ FrameFinished();
+ }
+
+ private void FireVIEvent()
+ {
+ // Execute VI Callback functions
+ if (VInterrupt != null)
+ VInterrupt();
+ m64pFrameComplete.Set();
+ }
+
+ private void CompletedFrameCallback()
+ {
+ m64pFrameComplete.Set();
+ }
}
public class VideoPluginSettings
diff --git a/libmupen64plus/mupen64plus-core/projects/msvc11/mupen64plus-core.vcxproj b/libmupen64plus/mupen64plus-core/projects/msvc11/mupen64plus-core.vcxproj
index 232438811d..1af28c1434 100644
--- a/libmupen64plus/mupen64plus-core/projects/msvc11/mupen64plus-core.vcxproj
+++ b/libmupen64plus/mupen64plus-core/projects/msvc11/mupen64plus-core.vcxproj
@@ -106,7 +106,7 @@
MachineX86
- xcopy /y $(OutDir)mupen64plus.dll $(TargetDir)..\..\..\..\..\output\dll\
+ xcopy /y "$(OutDir)mupen64plus.dll" "$(TargetDir)..\..\..\..\..\output\dll\"