2012-09-06 08:32:25 +00:00
|
|
|
|
//http://wiki.superfamicom.org/snes/show/Backgrounds
|
|
|
|
|
|
|
|
|
|
//TODO
|
2012-09-04 19:12:16 +00:00
|
|
|
|
//libsnes needs to be modified to support multiple instances - THIS IS NECESSARY - or else loading one game and then another breaks things
|
2012-09-22 05:03:52 +00:00
|
|
|
|
// edit - this is a lot of work
|
2012-09-04 07:09:00 +00:00
|
|
|
|
//wrap dll code around some kind of library-accessing interface so that it doesnt malfunction if the dll is unavailable
|
|
|
|
|
|
|
|
|
|
using System;
|
2012-09-04 00:20:36 +00:00
|
|
|
|
using System.Linq;
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
|
|
|
|
namespace BizHawk.Emulation.Consoles.Nintendo.SNES
|
|
|
|
|
{
|
|
|
|
|
public unsafe static class LibsnesDll
|
|
|
|
|
{
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public static extern string snes_library_id();
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 18:04:06 +00:00
|
|
|
|
public static extern int snes_library_revision_major();
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 18:04:06 +00:00
|
|
|
|
public static extern int snes_library_revision_minor();
|
2012-09-04 00:20:36 +00:00
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public static extern void snes_init();
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public static extern void snes_power();
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-16 17:15:53 +00:00
|
|
|
|
public static extern void snes_reset();
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public static extern void snes_run();
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public static extern void snes_term();
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-09 21:19:54 +00:00
|
|
|
|
public static extern void snes_unload_cartridge();
|
2012-09-04 00:20:36 +00:00
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public static extern void snes_load_cartridge_normal(
|
|
|
|
|
[MarshalAs(UnmanagedType.LPStr)]
|
|
|
|
|
string rom_xml,
|
|
|
|
|
[MarshalAs(UnmanagedType.LPArray)]
|
|
|
|
|
byte[] rom_data,
|
|
|
|
|
int rom_size);
|
|
|
|
|
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2012-09-04 19:25:09 +00:00
|
|
|
|
public delegate void snes_video_refresh_t(int *data, int width, int height);
|
2012-09-04 00:20:36 +00:00
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2012-09-04 01:21:14 +00:00
|
|
|
|
public delegate void snes_input_poll_t();
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public delegate ushort snes_input_state_t(int port, int device, int index, int id);
|
2012-09-04 01:21:14 +00:00
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2012-09-23 15:57:01 +00:00
|
|
|
|
public delegate void snes_input_notify_t(int index);
|
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
2012-09-04 01:21:14 +00:00
|
|
|
|
public delegate void snes_audio_sample_t(ushort left, ushort right);
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
|
|
|
|
|
public delegate void snes_scanlineStart_t(int line);
|
2012-09-04 00:20:36 +00:00
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public static extern void snes_set_video_refresh(snes_video_refresh_t video_refresh);
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public static extern void snes_set_input_poll(snes_input_poll_t input_poll);
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public static extern void snes_set_input_state(snes_input_state_t input_state);
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-23 15:57:01 +00:00
|
|
|
|
public static extern void snes_set_input_notify(snes_input_notify_t input_notify);
|
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 01:21:14 +00:00
|
|
|
|
public static extern void snes_set_audio_sample(snes_audio_sample_t audio_sample);
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
|
|
|
|
public static extern void snes_set_scanlineStart(snes_scanlineStart_t scanlineStart);
|
2012-09-04 00:20:36 +00:00
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 06:08:46 +00:00
|
|
|
|
[return: MarshalAs(UnmanagedType.U1)]
|
|
|
|
|
public static extern bool snes_check_cartridge(
|
|
|
|
|
[MarshalAs(UnmanagedType.LPArray)] byte[] rom_data,
|
|
|
|
|
int rom_size);
|
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 07:09:00 +00:00
|
|
|
|
[return: MarshalAs(UnmanagedType.U1)]
|
|
|
|
|
public static extern SNES_REGION snes_get_region();
|
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 17:29:20 +00:00
|
|
|
|
public static extern int snes_get_memory_size(SNES_MEMORY id);
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 07:09:00 +00:00
|
|
|
|
public static extern IntPtr snes_get_memory_data(SNES_MEMORY id);
|
2012-09-04 08:21:01 +00:00
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-05 01:30:09 +00:00
|
|
|
|
public static extern int snes_serialize_size();
|
|
|
|
|
|
|
|
|
|
[return: MarshalAs(UnmanagedType.U1)]
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-05 01:30:09 +00:00
|
|
|
|
public static extern bool snes_serialize(IntPtr data, int size);
|
2012-09-04 08:21:01 +00:00
|
|
|
|
|
|
|
|
|
[return: MarshalAs(UnmanagedType.U1)]
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 08:21:01 +00:00
|
|
|
|
public static extern bool snes_unserialize(IntPtr data, int size);
|
2012-09-04 19:12:16 +00:00
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-04 19:12:16 +00:00
|
|
|
|
public static extern void snes_set_layer_enable(int layer, int priority,
|
|
|
|
|
[MarshalAs(UnmanagedType.U1)]
|
|
|
|
|
bool enable
|
|
|
|
|
);
|
2012-09-06 08:32:25 +00:00
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
[DllImport("libsneshawk.dll", CallingConvention = CallingConvention.Cdecl)]
|
2012-09-06 08:32:25 +00:00
|
|
|
|
public static extern int snes_peek_logical_register(SNES_REG reg);
|
|
|
|
|
|
|
|
|
|
public enum SNES_REG : int
|
|
|
|
|
{
|
|
|
|
|
//$2105
|
|
|
|
|
BG_MODE = 0,
|
|
|
|
|
BG3_PRIORITY = 1,
|
|
|
|
|
BG1_TILESIZE = 2,
|
|
|
|
|
BG2_TILESIZE = 3,
|
|
|
|
|
BG3_TILESIZE = 4,
|
|
|
|
|
BG4_TILESIZE = 5,
|
|
|
|
|
//$2107
|
|
|
|
|
BG1_SCADDR = 10,
|
|
|
|
|
BG1_SCSIZE = 11,
|
|
|
|
|
//$2108
|
|
|
|
|
BG2_SCADDR = 12,
|
|
|
|
|
BG2_SCSIZE = 13,
|
|
|
|
|
//$2109
|
|
|
|
|
BG3_SCADDR = 14,
|
|
|
|
|
BG3_SCSIZE = 15,
|
|
|
|
|
//$210A
|
|
|
|
|
BG4_SCADDR = 16,
|
|
|
|
|
BG4_SCSIZE = 17,
|
|
|
|
|
//$210B
|
|
|
|
|
BG1_TDADDR = 20,
|
|
|
|
|
BG2_TDADDR = 21,
|
|
|
|
|
//$210C
|
|
|
|
|
BG3_TDADDR = 22,
|
2012-09-24 06:47:34 +00:00
|
|
|
|
BG4_TDADDR = 23,
|
|
|
|
|
//$2133 SETINI
|
|
|
|
|
SETINI_MODE7_EXTBG = 30,
|
|
|
|
|
SETINI_HIRES = 31,
|
|
|
|
|
SETINI_OVERSCAN = 32,
|
|
|
|
|
SETINI_OBJ_INTERLACE = 33,
|
2012-09-24 07:46:54 +00:00
|
|
|
|
SETINI_SCREEN_INTERLACE = 34,
|
|
|
|
|
//$2130 CGWSEL
|
|
|
|
|
CGWSEL_COLORMASK = 40,
|
|
|
|
|
CGWSEL_COLORSUBMASK = 41,
|
|
|
|
|
CGWSEL_ADDSUBMODE = 42,
|
|
|
|
|
CGWSEL_DIRECTCOLOR = 43,
|
2012-09-06 08:32:25 +00:00
|
|
|
|
}
|
2012-09-04 07:09:00 +00:00
|
|
|
|
|
|
|
|
|
public enum SNES_MEMORY : uint
|
|
|
|
|
{
|
|
|
|
|
CARTRIDGE_RAM = 0,
|
|
|
|
|
CARTRIDGE_RTC = 1,
|
|
|
|
|
BSX_RAM = 2,
|
|
|
|
|
BSX_PRAM = 3,
|
|
|
|
|
SUFAMI_TURBO_A_RAM = 4,
|
|
|
|
|
SUFAMI_TURBO_B_RAM = 5,
|
|
|
|
|
GAME_BOY_RAM = 6,
|
|
|
|
|
GAME_BOY_RTC = 7,
|
|
|
|
|
|
|
|
|
|
WRAM = 100,
|
|
|
|
|
APURAM = 101,
|
|
|
|
|
VRAM = 102,
|
|
|
|
|
OAM = 103,
|
|
|
|
|
CGRAM = 104,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum SNES_REGION : uint
|
2012-09-04 00:20:36 +00:00
|
|
|
|
{
|
2012-09-04 07:09:00 +00:00
|
|
|
|
NTSC = 0,
|
|
|
|
|
PAL = 1,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum SNES_DEVICE : uint
|
|
|
|
|
{
|
|
|
|
|
NONE = 0,
|
|
|
|
|
JOYPAD = 1,
|
|
|
|
|
MULTITAP = 2,
|
|
|
|
|
MOUSE = 3,
|
|
|
|
|
SUPER_SCOPE = 4,
|
|
|
|
|
JUSTIFIER = 5,
|
|
|
|
|
JUSTIFIERS = 6,
|
|
|
|
|
SERIAL_CABLE = 7
|
2012-09-04 00:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public enum SNES_DEVICE_ID : uint
|
|
|
|
|
{
|
|
|
|
|
JOYPAD_B = 0,
|
|
|
|
|
JOYPAD_Y = 1,
|
|
|
|
|
JOYPAD_SELECT = 2,
|
|
|
|
|
JOYPAD_START = 3,
|
|
|
|
|
JOYPAD_UP = 4,
|
|
|
|
|
JOYPAD_DOWN = 5,
|
|
|
|
|
JOYPAD_LEFT = 6,
|
|
|
|
|
JOYPAD_RIGHT = 7,
|
|
|
|
|
JOYPAD_A = 8,
|
|
|
|
|
JOYPAD_X = 9,
|
|
|
|
|
JOYPAD_L = 10,
|
|
|
|
|
JOYPAD_R = 11
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
public class ScanlineHookManager
|
|
|
|
|
{
|
|
|
|
|
public void Register(object tag, Action<int> callback)
|
|
|
|
|
{
|
|
|
|
|
var rr = new RegistrationRecord();
|
|
|
|
|
rr.tag = tag;
|
|
|
|
|
rr.callback = callback;
|
|
|
|
|
|
|
|
|
|
Unregister(tag);
|
|
|
|
|
records.Add(rr);
|
|
|
|
|
OnHooksChanged();
|
|
|
|
|
}
|
2012-09-06 08:32:25 +00:00
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
public int HookCount { get { return records.Count; } }
|
|
|
|
|
|
|
|
|
|
public virtual void OnHooksChanged() { }
|
|
|
|
|
|
|
|
|
|
public void Unregister(object tag)
|
|
|
|
|
{
|
|
|
|
|
records.RemoveAll((r) => r.tag == tag);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void HandleScanline(int scanline)
|
|
|
|
|
{
|
|
|
|
|
foreach (var rr in records) rr.callback(scanline);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<RegistrationRecord> records = new List<RegistrationRecord>();
|
|
|
|
|
|
|
|
|
|
class RegistrationRecord
|
|
|
|
|
{
|
|
|
|
|
public object tag;
|
|
|
|
|
public int scanline;
|
|
|
|
|
public Action<int> callback;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-04 01:21:14 +00:00
|
|
|
|
public unsafe class LibsnesCore : IEmulator, IVideoProvider, ISoundProvider
|
2012-09-04 00:20:36 +00:00
|
|
|
|
{
|
2012-09-05 23:16:08 +00:00
|
|
|
|
bool disposed = false;
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public void Dispose()
|
|
|
|
|
{
|
2012-09-05 23:16:08 +00:00
|
|
|
|
if (disposed) return;
|
|
|
|
|
disposed = true;
|
|
|
|
|
|
2012-09-16 17:15:53 +00:00
|
|
|
|
disposedSaveRam = ReadSaveRam();
|
|
|
|
|
|
2012-09-05 23:16:08 +00:00
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_video_refresh(null);
|
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_poll(null);
|
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_state(null);
|
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_audio_sample(null);
|
2012-09-22 05:03:52 +00:00
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_scanlineStart(null);
|
2012-09-05 23:16:08 +00:00
|
|
|
|
|
2012-09-09 21:19:54 +00:00
|
|
|
|
LibsnesDll.snes_unload_cartridge();
|
2012-09-04 00:20:36 +00:00
|
|
|
|
LibsnesDll.snes_term();
|
2012-09-08 01:15:16 +00:00
|
|
|
|
|
|
|
|
|
resampler.Dispose();
|
2012-09-04 00:20:36 +00:00
|
|
|
|
}
|
2012-09-16 17:15:53 +00:00
|
|
|
|
//save the save memory before disposing the core, so we can pull from it in the future after the core is terminated
|
|
|
|
|
//that will be necessary to get it saving to disk
|
|
|
|
|
byte[] disposedSaveRam;
|
|
|
|
|
|
2012-09-05 23:16:08 +00:00
|
|
|
|
//we can only have one active snes core at a time, due to libsnes being so static.
|
|
|
|
|
//so we'll track the current one here and detach the previous one whenever a new one is booted up.
|
|
|
|
|
static LibsnesCore CurrLibsnesCore;
|
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
public class MyScanlineHookManager : ScanlineHookManager
|
|
|
|
|
{
|
|
|
|
|
public MyScanlineHookManager(LibsnesCore core)
|
|
|
|
|
{
|
|
|
|
|
this.core = core;
|
|
|
|
|
}
|
|
|
|
|
LibsnesCore core;
|
|
|
|
|
|
|
|
|
|
public override void OnHooksChanged()
|
|
|
|
|
{
|
|
|
|
|
core.OnScanlineHooksChanged();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
public MyScanlineHookManager ScanlineHookManager;
|
|
|
|
|
void OnScanlineHooksChanged()
|
|
|
|
|
{
|
|
|
|
|
if (disposed) return;
|
|
|
|
|
if (ScanlineHookManager.HookCount == 0) LibsnesDll.snes_set_scanlineStart(null);
|
|
|
|
|
else LibsnesDll.snes_set_scanlineStart(scanlineStart_cb);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void snes_scanlineStart(int line)
|
|
|
|
|
{
|
|
|
|
|
ScanlineHookManager.HandleScanline(line);
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public LibsnesCore(byte[] romData)
|
|
|
|
|
{
|
2012-09-05 23:16:08 +00:00
|
|
|
|
//attach this core as the current
|
|
|
|
|
if(CurrLibsnesCore != null)
|
|
|
|
|
CurrLibsnesCore.Dispose();
|
|
|
|
|
CurrLibsnesCore = this;
|
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
ScanlineHookManager = new MyScanlineHookManager(this);
|
|
|
|
|
|
2012-09-04 18:04:06 +00:00
|
|
|
|
LibsnesDll.snes_init();
|
|
|
|
|
|
2012-09-05 23:16:08 +00:00
|
|
|
|
vidcb = new LibsnesDll.snes_video_refresh_t(snes_video_refresh);
|
2012-09-04 00:20:36 +00:00
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_video_refresh(vidcb);
|
|
|
|
|
|
2012-09-05 23:16:08 +00:00
|
|
|
|
pollcb = new LibsnesDll.snes_input_poll_t(snes_input_poll);
|
2012-09-04 00:20:36 +00:00
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_poll(pollcb);
|
|
|
|
|
|
2012-09-05 23:16:08 +00:00
|
|
|
|
inputcb = new LibsnesDll.snes_input_state_t(snes_input_state);
|
2012-09-04 00:20:36 +00:00
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_state(inputcb);
|
2012-09-04 01:21:14 +00:00
|
|
|
|
|
2012-09-23 15:57:01 +00:00
|
|
|
|
notifycb = new LibsnesDll.snes_input_notify_t(snes_input_notify);
|
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_input_notify(notifycb);
|
|
|
|
|
|
2012-09-05 23:16:08 +00:00
|
|
|
|
soundcb = new LibsnesDll.snes_audio_sample_t(snes_audio_sample);
|
2012-09-04 01:21:14 +00:00
|
|
|
|
BizHawk.Emulation.Consoles.Nintendo.SNES.LibsnesDll.snes_set_audio_sample(soundcb);
|
|
|
|
|
|
2012-09-22 05:03:52 +00:00
|
|
|
|
scanlineStart_cb = new LibsnesDll.snes_scanlineStart_t(snes_scanlineStart);
|
|
|
|
|
|
2012-09-05 21:21:35 +00:00
|
|
|
|
// start up audio resampler
|
2012-09-07 20:12:47 +00:00
|
|
|
|
InitAudio();
|
2012-09-05 21:21:35 +00:00
|
|
|
|
|
2012-09-04 17:29:20 +00:00
|
|
|
|
//strip header
|
|
|
|
|
if ((romData.Length & 0x7FFF) == 512)
|
|
|
|
|
{
|
|
|
|
|
var newData = new byte[romData.Length - 512];
|
|
|
|
|
Array.Copy(romData, 512, newData, 0, newData.Length);
|
|
|
|
|
romData = newData;
|
|
|
|
|
}
|
|
|
|
|
LibsnesDll.snes_load_cartridge_normal(null, romData, romData.Length);
|
|
|
|
|
|
2012-09-09 21:19:54 +00:00
|
|
|
|
LibsnesDll.snes_power();
|
|
|
|
|
|
2012-09-04 17:29:20 +00:00
|
|
|
|
SetupMemoryDomains(romData);
|
2012-09-04 00:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-05 23:16:08 +00:00
|
|
|
|
//must keep references to these so that they wont get garbage collected
|
|
|
|
|
LibsnesDll.snes_video_refresh_t vidcb;
|
|
|
|
|
LibsnesDll.snes_input_poll_t pollcb;
|
|
|
|
|
LibsnesDll.snes_input_state_t inputcb;
|
2012-09-23 15:57:01 +00:00
|
|
|
|
LibsnesDll.snes_input_notify_t notifycb;
|
2012-09-05 23:16:08 +00:00
|
|
|
|
LibsnesDll.snes_audio_sample_t soundcb;
|
2012-09-22 05:03:52 +00:00
|
|
|
|
LibsnesDll.snes_scanlineStart_t scanlineStart_cb;
|
2012-09-05 23:16:08 +00:00
|
|
|
|
|
2012-09-04 00:20:36 +00:00
|
|
|
|
ushort snes_input_state(int port, int device, int index, int id)
|
|
|
|
|
{
|
|
|
|
|
//Console.WriteLine("{0} {1} {2} {3}", port, device, index, id);
|
|
|
|
|
|
|
|
|
|
string key = "P" + (1 + port) + " ";
|
2012-09-04 07:09:00 +00:00
|
|
|
|
if ((LibsnesDll.SNES_DEVICE)device == LibsnesDll.SNES_DEVICE.JOYPAD)
|
2012-09-04 00:20:36 +00:00
|
|
|
|
{
|
2012-09-05 18:52:17 +00:00
|
|
|
|
switch ((LibsnesDll.SNES_DEVICE_ID)id)
|
2012-09-04 00:20:36 +00:00
|
|
|
|
{
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_A: key += "A"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_B: key += "B"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_X: key += "X"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_Y: key += "Y"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_UP: key += "Up"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_DOWN: key += "Down"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_LEFT: key += "Left"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_RIGHT: key += "Right"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_L: key += "L"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_R: key += "R"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_SELECT: key += "Select"; break;
|
|
|
|
|
case LibsnesDll.SNES_DEVICE_ID.JOYPAD_START: key += "Start"; break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (ushort)(Controller[key] ? 1 : 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void snes_input_poll()
|
2012-09-23 15:57:01 +00:00
|
|
|
|
{
|
|
|
|
|
// libsnes.cpp calls this on every video refresh regardless of any underlying anything, so...
|
|
|
|
|
//IsLagFrame = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void snes_input_notify(int index)
|
2012-09-04 00:20:36 +00:00
|
|
|
|
{
|
2012-09-11 01:36:12 +00:00
|
|
|
|
IsLagFrame = false;
|
2012-09-04 00:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-04 19:25:09 +00:00
|
|
|
|
void snes_video_refresh(int* data, int width, int height)
|
2012-09-04 00:20:36 +00:00
|
|
|
|
{
|
|
|
|
|
vidWidth = width;
|
|
|
|
|
vidHeight = height;
|
|
|
|
|
int size = vidWidth * vidHeight;
|
|
|
|
|
if (vidBuffer.Length != size)
|
|
|
|
|
vidBuffer = new int[size];
|
|
|
|
|
for (int y = 0; y < height; y++)
|
|
|
|
|
for (int x = 0; x < width; x++)
|
|
|
|
|
{
|
|
|
|
|
int si = y * 1024 + x;
|
|
|
|
|
int di = y * vidWidth + x;
|
2012-09-04 19:25:09 +00:00
|
|
|
|
int rgb = data[si];
|
|
|
|
|
vidBuffer[di] = rgb;
|
2012-09-04 00:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-20 19:52:47 +00:00
|
|
|
|
public void FrameAdvance(bool render, bool rendersound)
|
2012-09-04 00:20:36 +00:00
|
|
|
|
{
|
2012-09-20 20:25:40 +00:00
|
|
|
|
// speedup when sound rendering is not needed
|
2012-09-20 20:36:44 +00:00
|
|
|
|
if (!rendersound)
|
|
|
|
|
LibsnesDll.snes_set_audio_sample(null);
|
|
|
|
|
else
|
|
|
|
|
LibsnesDll.snes_set_audio_sample(soundcb);
|
2012-09-20 20:25:40 +00:00
|
|
|
|
|
2012-09-16 17:15:53 +00:00
|
|
|
|
bool resetSignal = Controller["Reset"];
|
|
|
|
|
if (resetSignal) LibsnesDll.snes_reset();
|
|
|
|
|
|
|
|
|
|
bool powerSignal = Controller["Power"];
|
|
|
|
|
if (powerSignal) LibsnesDll.snes_power();
|
|
|
|
|
|
2012-09-04 19:12:16 +00:00
|
|
|
|
LibsnesDll.snes_set_layer_enable(0, 0, CoreInputComm.SNES_ShowBG1_0);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(0, 1, CoreInputComm.SNES_ShowBG1_1);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(1, 0, CoreInputComm.SNES_ShowBG2_0);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(1, 1, CoreInputComm.SNES_ShowBG2_1);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(2, 0, CoreInputComm.SNES_ShowBG3_0);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(2, 1, CoreInputComm.SNES_ShowBG3_1);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(3, 0, CoreInputComm.SNES_ShowBG4_0);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(3, 1, CoreInputComm.SNES_ShowBG4_1);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(4, 0, CoreInputComm.SNES_ShowOBJ_0);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(4, 1, CoreInputComm.SNES_ShowOBJ_1);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(4, 2, CoreInputComm.SNES_ShowOBJ_2);
|
|
|
|
|
LibsnesDll.snes_set_layer_enable(4, 3, CoreInputComm.SNES_ShowOBJ_3);
|
|
|
|
|
|
2012-09-11 01:36:12 +00:00
|
|
|
|
// if the input poll callback is called, it will set this to false
|
|
|
|
|
IsLagFrame = true;
|
|
|
|
|
|
2012-09-04 00:20:36 +00:00
|
|
|
|
//apparently this is one frame?
|
2012-09-07 20:06:57 +00:00
|
|
|
|
timeFrameCounter++;
|
2012-09-04 00:20:36 +00:00
|
|
|
|
LibsnesDll.snes_run();
|
2012-09-08 20:03:04 +00:00
|
|
|
|
|
2012-09-11 01:36:12 +00:00
|
|
|
|
if (IsLagFrame)
|
|
|
|
|
LagCount++;
|
2012-09-04 00:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//video provider
|
|
|
|
|
int IVideoProvider.BackgroundColor { get { return 0; } }
|
|
|
|
|
int[] IVideoProvider.GetVideoBuffer() { return vidBuffer; }
|
|
|
|
|
int IVideoProvider.VirtualWidth { get { return vidWidth; } }
|
2012-09-05 18:52:17 +00:00
|
|
|
|
int IVideoProvider.BufferWidth { get { return vidWidth; } }
|
2012-09-04 00:20:36 +00:00
|
|
|
|
int IVideoProvider.BufferHeight { get { return vidHeight; } }
|
|
|
|
|
|
2012-09-05 18:52:17 +00:00
|
|
|
|
int[] vidBuffer = new int[256 * 256];
|
|
|
|
|
int vidWidth = 256, vidHeight = 256;
|
2012-09-04 00:20:36 +00:00
|
|
|
|
|
|
|
|
|
public IVideoProvider VideoProvider { get { return this; } }
|
2012-09-04 01:21:14 +00:00
|
|
|
|
public ISoundProvider SoundProvider { get { return this; } }
|
2012-09-04 00:20:36 +00:00
|
|
|
|
|
|
|
|
|
public ControllerDefinition ControllerDefinition { get { return SNESController; } }
|
|
|
|
|
IController controller;
|
|
|
|
|
public IController Controller
|
|
|
|
|
{
|
|
|
|
|
get { return controller; }
|
|
|
|
|
set { controller = value; }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public static readonly ControllerDefinition SNESController =
|
|
|
|
|
new ControllerDefinition
|
|
|
|
|
{
|
|
|
|
|
Name = "SNES Controller",
|
|
|
|
|
BoolButtons = {
|
2012-09-16 17:15:53 +00:00
|
|
|
|
"P1 Up", "P1 Down", "P1 Left", "P1 Right", "P1 Select", "P1 Start", "P1 B", "P1 A", "P1 X", "P1 Y", "P1 L", "P1 R", "Reset", "Power",
|
2012-09-10 18:40:39 +00:00
|
|
|
|
"P2 Up", "P2 Down", "P2 Left", "P2 Right", "P2 Select", "P2 Start", "P2 B", "P2 A", "P2 X", "P2 Y", "P2 L", "P2 R",
|
|
|
|
|
"P3 Up", "P3 Down", "P3 Left", "P3 Right", "P3 Select", "P3 Start", "P3 B", "P3 A", "P3 X", "P3 Y", "P3 L", "P3 R",
|
|
|
|
|
"P4 Up", "P4 Down", "P4 Left", "P4 Right", "P4 Select", "P4 Start", "P4 B", "P4 A", "P4 X", "P4 Y", "P4 L", "P4 R",
|
2012-09-04 00:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
};
|
2012-09-05 18:52:17 +00:00
|
|
|
|
|
2012-09-04 00:20:36 +00:00
|
|
|
|
int timeFrameCounter;
|
2012-09-07 20:31:05 +00:00
|
|
|
|
public int Frame { get { return timeFrameCounter; } set { timeFrameCounter = value; } }
|
2012-09-04 00:20:36 +00:00
|
|
|
|
public int LagCount { get; set; }
|
|
|
|
|
public bool IsLagFrame { get; private set; }
|
|
|
|
|
public string SystemId { get { return "SNES"; } }
|
|
|
|
|
public bool DeterministicEmulation { get; set; }
|
2012-09-04 07:09:00 +00:00
|
|
|
|
public bool SaveRamModified
|
|
|
|
|
{
|
|
|
|
|
set { }
|
|
|
|
|
get
|
|
|
|
|
{
|
|
|
|
|
return LibsnesDll.snes_get_memory_size(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM) != 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
2012-09-05 18:52:17 +00:00
|
|
|
|
|
2012-09-14 22:28:38 +00:00
|
|
|
|
public byte[] ReadSaveRam()
|
2012-09-16 17:15:53 +00:00
|
|
|
|
{
|
|
|
|
|
if (disposedSaveRam != null) return disposedSaveRam;
|
2012-09-14 22:28:38 +00:00
|
|
|
|
return snes_get_memory_data_read(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM);
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-04 07:09:00 +00:00
|
|
|
|
public static byte[] snes_get_memory_data_read(LibsnesDll.SNES_MEMORY id)
|
|
|
|
|
{
|
|
|
|
|
var size = (int)LibsnesDll.snes_get_memory_size(id);
|
2012-09-04 08:21:01 +00:00
|
|
|
|
if (size == 0) return new byte[0];
|
2012-09-04 07:09:00 +00:00
|
|
|
|
var data = LibsnesDll.snes_get_memory_data(id);
|
|
|
|
|
var ret = new byte[size];
|
2012-09-05 18:52:17 +00:00
|
|
|
|
Marshal.Copy(data, ret, 0, size);
|
2012-09-04 07:09:00 +00:00
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void StoreSaveRam(byte[] data)
|
|
|
|
|
{
|
|
|
|
|
var size = (int)LibsnesDll.snes_get_memory_size(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM);
|
2012-09-04 08:21:01 +00:00
|
|
|
|
if (size == 0) return;
|
2012-09-04 07:09:00 +00:00
|
|
|
|
var emudata = LibsnesDll.snes_get_memory_data(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM);
|
|
|
|
|
Marshal.Copy(data, 0, emudata, size);
|
|
|
|
|
}
|
2012-09-04 00:20:36 +00:00
|
|
|
|
|
2012-09-14 22:28:38 +00:00
|
|
|
|
public void ClearSaveRam()
|
|
|
|
|
{
|
|
|
|
|
byte[] cleardata = new byte[(int)LibsnesDll.snes_get_memory_size(LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM)];
|
|
|
|
|
StoreSaveRam(cleardata);
|
|
|
|
|
}
|
|
|
|
|
|
2012-09-07 20:06:57 +00:00
|
|
|
|
public void ResetFrameCounter() { timeFrameCounter = 0; }
|
2012-09-04 08:21:01 +00:00
|
|
|
|
public void SaveStateText(TextWriter writer)
|
|
|
|
|
{
|
|
|
|
|
var temp = SaveStateBinary();
|
|
|
|
|
temp.SaveAsHex(writer);
|
2012-09-23 16:59:44 +00:00
|
|
|
|
// write extra copy of stuff we don't use
|
|
|
|
|
writer.WriteLine("Frame {0}", Frame);
|
2012-09-04 08:21:01 +00:00
|
|
|
|
}
|
|
|
|
|
public void LoadStateText(TextReader reader)
|
|
|
|
|
{
|
|
|
|
|
string hex = reader.ReadLine();
|
|
|
|
|
byte[] state = new byte[hex.Length / 2];
|
|
|
|
|
state.ReadFromHex(hex);
|
|
|
|
|
LoadStateBinary(new BinaryReader(new MemoryStream(state)));
|
|
|
|
|
}
|
2012-09-05 18:52:17 +00:00
|
|
|
|
|
2012-09-04 08:21:01 +00:00
|
|
|
|
public void SaveStateBinary(BinaryWriter writer)
|
|
|
|
|
{
|
|
|
|
|
int size = LibsnesDll.snes_serialize_size();
|
|
|
|
|
byte[] buf = new byte[size];
|
|
|
|
|
fixed (byte* pbuf = &buf[0])
|
|
|
|
|
LibsnesDll.snes_serialize(new IntPtr(pbuf), size);
|
|
|
|
|
writer.Write(buf);
|
2012-09-11 01:50:55 +00:00
|
|
|
|
|
|
|
|
|
// other variables
|
|
|
|
|
writer.Write(IsLagFrame);
|
|
|
|
|
writer.Write(LagCount);
|
|
|
|
|
writer.Write(Frame);
|
|
|
|
|
|
2012-09-04 08:21:01 +00:00
|
|
|
|
writer.Flush();
|
|
|
|
|
}
|
|
|
|
|
public void LoadStateBinary(BinaryReader reader)
|
|
|
|
|
{
|
|
|
|
|
int size = LibsnesDll.snes_serialize_size();
|
2012-09-11 01:50:55 +00:00
|
|
|
|
|
|
|
|
|
byte[] buf = reader.ReadBytes(size);
|
2012-09-04 08:21:01 +00:00
|
|
|
|
fixed (byte* pbuf = &buf[0])
|
|
|
|
|
LibsnesDll.snes_unserialize(new IntPtr(pbuf), size);
|
2012-09-11 01:50:55 +00:00
|
|
|
|
|
|
|
|
|
// other variables
|
|
|
|
|
IsLagFrame = reader.ReadBoolean();
|
|
|
|
|
LagCount = reader.ReadInt32();
|
|
|
|
|
Frame = reader.ReadInt32();
|
2012-09-04 08:21:01 +00:00
|
|
|
|
}
|
|
|
|
|
public byte[] SaveStateBinary()
|
|
|
|
|
{
|
|
|
|
|
MemoryStream ms = new MemoryStream();
|
|
|
|
|
BinaryWriter bw = new BinaryWriter(ms);
|
|
|
|
|
SaveStateBinary(bw);
|
|
|
|
|
bw.Flush();
|
|
|
|
|
return ms.ToArray();
|
|
|
|
|
}
|
2012-09-04 00:20:36 +00:00
|
|
|
|
|
|
|
|
|
// Arbitrary extensible core comm mechanism
|
2012-09-04 19:12:16 +00:00
|
|
|
|
public CoreInputComm CoreInputComm { get; set; }
|
|
|
|
|
public CoreOutputComm CoreOutputComm { get { return _CoreOutputComm; } }
|
|
|
|
|
CoreOutputComm _CoreOutputComm = new CoreOutputComm();
|
2012-09-04 00:20:36 +00:00
|
|
|
|
|
|
|
|
|
// ----- Client Debugging API stuff -----
|
2012-09-04 17:29:20 +00:00
|
|
|
|
unsafe MemoryDomain MakeMemoryDomain(string name, LibsnesDll.SNES_MEMORY id, Endian endian)
|
|
|
|
|
{
|
2012-09-21 06:03:27 +00:00
|
|
|
|
IntPtr block = LibsnesDll.snes_get_memory_data(id);
|
2012-09-04 17:29:20 +00:00
|
|
|
|
int size = LibsnesDll.snes_get_memory_size(id);
|
|
|
|
|
int mask = size - 1;
|
|
|
|
|
byte* blockptr = (byte*)block.ToPointer();
|
|
|
|
|
MemoryDomain md;
|
2012-09-05 18:52:17 +00:00
|
|
|
|
|
2012-09-04 17:29:20 +00:00
|
|
|
|
//have to bitmask these somehow because it's unmanaged memory and we would hate to clobber things or make them nondeterministic
|
|
|
|
|
if (Util.IsPowerOfTwo(size))
|
|
|
|
|
{
|
|
|
|
|
//can &mask for speed
|
|
|
|
|
md = new MemoryDomain(name, size, endian,
|
|
|
|
|
(addr) => blockptr[addr & mask],
|
|
|
|
|
(addr, value) => blockptr[addr & mask] = value);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
//have to use % (only OAM needs this, it seems)
|
|
|
|
|
md = new MemoryDomain(name, size, endian,
|
|
|
|
|
(addr) => blockptr[addr % size],
|
|
|
|
|
(addr, value) => blockptr[addr % size] = value);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MemoryDomains.Add(md);
|
|
|
|
|
|
|
|
|
|
return md;
|
|
|
|
|
|
|
|
|
|
//doesnt cache the addresses. safer. slower. necessary? don't know
|
|
|
|
|
//return new MemoryDomain(name, size, endian,
|
|
|
|
|
// (addr) => Peek(LibsnesDll.SNES_MEMORY.WRAM, addr & mask),
|
|
|
|
|
// (addr, value) => Poke(LibsnesDll.SNES_MEMORY.WRAM, addr & mask, value));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SetupMemoryDomains(byte[] romData)
|
|
|
|
|
{
|
|
|
|
|
MemoryDomains = new List<MemoryDomain>();
|
2012-09-05 18:52:17 +00:00
|
|
|
|
|
2012-09-04 19:12:16 +00:00
|
|
|
|
var romDomain = new MemoryDomain("CARTROM", romData.Length, Endian.Little,
|
2012-09-04 17:29:20 +00:00
|
|
|
|
(addr) => romData[addr],
|
|
|
|
|
(addr, value) => romData[addr] = value);
|
|
|
|
|
|
|
|
|
|
MainMemory = MakeMemoryDomain("WRAM", LibsnesDll.SNES_MEMORY.WRAM, Endian.Little);
|
|
|
|
|
MemoryDomains.Add(romDomain);
|
|
|
|
|
MakeMemoryDomain("CARTRAM", LibsnesDll.SNES_MEMORY.CARTRIDGE_RAM, Endian.Little);
|
|
|
|
|
MakeMemoryDomain("VRAM", LibsnesDll.SNES_MEMORY.VRAM, Endian.Little);
|
|
|
|
|
MakeMemoryDomain("OAM", LibsnesDll.SNES_MEMORY.OAM, Endian.Little);
|
|
|
|
|
MakeMemoryDomain("CGRAM", LibsnesDll.SNES_MEMORY.CGRAM, Endian.Little);
|
|
|
|
|
MakeMemoryDomain("APURAM", LibsnesDll.SNES_MEMORY.APURAM, Endian.Little);
|
|
|
|
|
}
|
|
|
|
|
public IList<MemoryDomain> MemoryDomains { get; private set; }
|
|
|
|
|
public MemoryDomain MainMemory { get; private set; }
|
2012-09-04 01:21:14 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2012-09-05 18:52:17 +00:00
|
|
|
|
#region audio stuff
|
2012-09-04 01:21:14 +00:00
|
|
|
|
|
2012-09-07 20:12:47 +00:00
|
|
|
|
void InitAudio()
|
|
|
|
|
{
|
|
|
|
|
metaspu = new Sound.MetaspuSoundProvider(Sound.ESynchMethod.ESynchMethod_V);
|
|
|
|
|
resampler = new Sound.Utilities.SpeexResampler(6, 64081, 88200, 32041, 44100, new Action<short[], int>(metaspu.buffer.enqueue_samples));
|
2012-09-06 08:32:25 +00:00
|
|
|
|
|
2012-09-07 20:12:47 +00:00
|
|
|
|
}
|
2012-09-04 01:21:14 +00:00
|
|
|
|
|
2012-09-07 20:12:47 +00:00
|
|
|
|
Sound.Utilities.SpeexResampler resampler;
|
2012-09-04 01:21:14 +00:00
|
|
|
|
|
2012-09-07 20:12:47 +00:00
|
|
|
|
Sound.MetaspuSoundProvider metaspu;
|
2012-09-04 01:21:14 +00:00
|
|
|
|
|
2012-09-05 18:52:17 +00:00
|
|
|
|
void snes_audio_sample(ushort left, ushort right)
|
|
|
|
|
{
|
2012-09-20 20:36:44 +00:00
|
|
|
|
resampler.EnqueueSample((short)left, (short)right);
|
2012-09-05 18:52:17 +00:00
|
|
|
|
}
|
2012-09-04 01:21:14 +00:00
|
|
|
|
|
|
|
|
|
|
2012-09-05 18:52:17 +00:00
|
|
|
|
//BinaryWriter dbgs = new BinaryWriter(File.Open("dbgwav.raw", FileMode.Create, FileAccess.Write));
|
2012-09-04 01:21:14 +00:00
|
|
|
|
|
2012-09-05 18:52:17 +00:00
|
|
|
|
public void GetSamples(short[] samples)
|
2012-09-04 01:21:14 +00:00
|
|
|
|
{
|
2012-09-07 20:12:47 +00:00
|
|
|
|
resampler.Flush();
|
2012-09-05 18:52:17 +00:00
|
|
|
|
metaspu.GetSamples(samples);
|
2012-09-04 01:21:14 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-09-05 18:52:17 +00:00
|
|
|
|
public void DiscardSamples()
|
2012-09-04 01:21:14 +00:00
|
|
|
|
{
|
2012-09-05 18:52:17 +00:00
|
|
|
|
metaspu.DiscardSamples();
|
2012-09-04 01:21:14 +00:00
|
|
|
|
}
|
2012-09-05 18:52:17 +00:00
|
|
|
|
|
|
|
|
|
public int MaxVolume { get; set; }
|
|
|
|
|
|
|
|
|
|
#endregion audio stuff
|
|
|
|
|
|
2012-09-04 00:20:36 +00:00
|
|
|
|
}
|
|
|
|
|
}
|