APU补全中

This commit is contained in:
ALIENJACK\alien 2024-08-06 19:50:33 +08:00
parent b8c116aa28
commit 8952b842b1
5 changed files with 483 additions and 20 deletions

View File

@ -60,7 +60,16 @@ namespace VirtualNes.Core
m_bMute[i] = true; m_bMute[i] = true;
} }
public void Dispose() { } public void Dispose()
{
@internal.Dispose();
vrc6.Dispose();
vrc7.Dispose();
mmc5.Dispose();
fds.Dispose();
n106.Dispose();
fme7.Dispose();
}
private int[] vol = new int[24]; private int[] vol = new int[24];
static double cutofftemp = (2.0 * 3.141592653579 * 40.0); static double cutofftemp = (2.0 * 3.141592653579 * 40.0);

View File

@ -1,36 +1,219 @@
using System; using Codice.CM.Client.Differences;
using System;
namespace VirtualNes.Core namespace VirtualNes.Core
{ {
public class APU_MMC5 : APU_INTERFACE public class APU_MMC5 : APU_INTERFACE
{ {
public const int RECTANGLE_VOL_SHIFT = 8;
public const int DAOUT_VOL_SHIFT = 6;
SYNCRECTANGLE sch0 = new SYNCRECTANGLE(); SYNCRECTANGLE sch0 = new SYNCRECTANGLE();
SYNCRECTANGLE sch1 = new SYNCRECTANGLE(); SYNCRECTANGLE sch1 = new SYNCRECTANGLE();
RECTANGLE ch0 = new RECTANGLE();
RECTANGLE ch1 = new RECTANGLE();
byte reg5010;
byte reg5011;
byte reg5015;
byte sync_reg5015;
int FrameCycle;
float cpu_clock;
int cycle_rate;
// Tables
static int[] vbl_length = new int[32];
static int[] duty_lut = new int[4];
static int[] decay_lut = new int[16];
static int[] vbl_lut = new int[32];
public APU_MMC5()
{
// 仮設定
Reset(APU_INTERFACE.APU_CLOCK, 22050);
}
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
//todo : 实现 sch0.ZeroMemory();
sch1.ZeroMemory();
reg5010 = reg5011 = reg5015 = 0;
sync_reg5015 = 0;
FrameCycle = 0;
Setup(fClock, nRate);
for (ushort addr = 0x5000; addr <= 0x5015; addr++)
{
Write(addr, 0);
}
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
//todo : 实现 cpu_clock = fClock;
cycle_rate = (int)(fClock * 65536.0f / nRate);
// Create Tables
int i;
int samples = (int)(nRate / 60.0f);
for (i = 0; i < 16; i++)
decay_lut[i] = (i + 1) * samples * 5;
for (i = 0; i < 32; i++)
vbl_lut[i] = vbl_length[i] * samples * 5;
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
//todo : 实现 switch (addr)
}
public override int Process(int channel)
{ {
//todo : 实现 // MMC5 CH0 rectangle
return 0; case 0x5000:
ch0.reg[0] = data;
ch0.volume = (byte)(data & 0x0F);
ch0.holdnote = (byte)(data & 0x20);
ch0.fixed_envelope = (byte)(data & 0x10);
ch0.env_decay = decay_lut[data & 0x0F];
ch0.duty_flip = duty_lut[data >> 6];
break;
case 0x5001:
ch0.reg[1] = data;
break;
case 0x5002:
ch0.reg[2] = data;
ch0.freq = INT2FIX(((ch0.reg[3] & 0x07) << 8) + data + 1);
break;
case 0x5003:
ch0.reg[3] = data;
ch0.vbl_length = vbl_lut[data >> 3];
ch0.env_vol = 0;
ch0.freq = INT2FIX(((data & 0x07) << 8) + ch0.reg[2] + 1);
if ((reg5015 & 0x01) != 0)
ch0.enable = 0xFF;
break;
// MMC5 CH1 rectangle
case 0x5004:
ch1.reg[0] = data;
ch1.volume = (byte)(data & 0x0F);
ch1.holdnote = (byte)(data & 0x20);
ch1.fixed_envelope = (byte)(data & 0x10);
ch1.env_decay = decay_lut[data & 0x0F];
ch1.duty_flip = duty_lut[data >> 6];
break;
case 0x5005:
ch1.reg[1] = data;
break;
case 0x5006:
ch1.reg[2] = data;
ch1.freq = INT2FIX(((ch1.reg[3] & 0x07) << 8) + data + 1);
break;
case 0x5007:
ch1.reg[3] = data;
ch1.vbl_length = vbl_lut[data >> 3];
ch1.env_vol = 0;
ch1.freq = INT2FIX(((data & 0x07) << 8) + ch1.reg[2] + 1);
if ((reg5015 & 0x02) != 0)
ch1.enable = 0xFF;
break;
case 0x5010:
reg5010 = data;
break;
case 0x5011:
reg5011 = data;
break;
case 0x5012:
case 0x5013:
case 0x5014:
break;
case 0x5015:
reg5015 = data;
if ((reg5015 & 0x01) != 0)
{
ch0.enable = 0xFF;
}
else
{
ch0.enable = 0;
ch0.vbl_length = 0;
}
if ((reg5015 & 0x02) != 0)
{
ch1.enable = 0xFF;
}
else
{
ch1.enable = 0;
ch1.vbl_length = 0;
}
break;
}
} }
internal void SyncWrite(ushort addr, byte data) internal void SyncWrite(ushort addr, byte data)
{ {
//todo : 实现 switch (addr)
{
// MMC5 CH0 rectangle
case 0x5000:
sch0.reg[0] = data;
sch0.holdnote = (byte)(data & 0x20);
break;
case 0x5001:
case 0x5002:
sch0.reg[addr & 3] = data;
break;
case 0x5003:
sch0.reg[3] = data;
sch0.vbl_length = vbl_length[data >> 3];
if ((sync_reg5015 & 0x01) != 0)
sch0.enable = 0xFF;
break;
// MMC5 CH1 rectangle
case 0x5004:
sch1.reg[0] = data;
sch1.holdnote = (byte)(data & 0x20);
break;
case 0x5005:
case 0x5006:
sch1.reg[addr & 3] = data;
break;
case 0x5007:
sch1.reg[3] = data;
sch1.vbl_length = vbl_length[data >> 3];
if ((sync_reg5015 & 0x02) != 0)
sch1.enable = 0xFF;
break;
case 0x5010:
case 0x5011:
case 0x5012:
case 0x5013:
case 0x5014:
break;
case 0x5015:
sync_reg5015 = data;
if ((sync_reg5015 & 0x01) != 0)
{
sch0.enable = 0xFF;
}
else
{
sch0.enable = 0;
sch0.vbl_length = 0;
}
if ((sync_reg5015 & 0x02) != 0)
{
sch1.enable = 0xFF;
}
else
{
sch1.enable = 0;
sch1.vbl_length = 0;
}
break;
}
} }
internal byte SyncRead(ushort addr) internal byte SyncRead(ushort addr)
@ -46,6 +229,118 @@ namespace VirtualNes.Core
return data; return data;
} }
public override bool Sync(int cycles)
{
FrameCycle += cycles;
if (FrameCycle >= 7457 * 5 / 2)
{
FrameCycle -= 7457 * 5 / 2;
if (sch0.enable != 0 && sch0.holdnote == 0)
{
if ((sch0.vbl_length) != 0)
{
sch0.vbl_length--;
}
}
if (sch1.enable != 0 && sch1.holdnote == 0)
{
if ((sch1.vbl_length) != 0)
{
sch1.vbl_length--;
}
}
}
return false;
}
public override int Process(int channel)
{
switch (channel)
{
case 0:
return RectangleRender(ch0);
case 1:
return RectangleRender(ch1);
case 2:
return reg5011 << DAOUT_VOL_SHIFT;
}
return 0;
}
private int RectangleRender(RECTANGLE ch)
{
if (ch.enable == 0 || ch.vbl_length <= 0)
return 0;
// vbl length counter
if (ch.holdnote == 0)
ch.vbl_length -= 5;
// envelope unit
ch.env_phase -= 5 * 4;
while (ch.env_phase < 0)
{
ch.env_phase += ch.env_decay;
if ((ch.holdnote) != 0)
ch.env_vol = (byte)((ch.env_vol + 1) & 0x0F);
else if (ch.env_vol < 0x0F)
ch.env_vol++;
}
if (ch.freq < INT2FIX(8))
return 0;
int volume;
if ((ch.fixed_envelope) != 0)
volume = ch.volume;
else
volume = (0x0F - ch.env_vol);
int output = volume << RECTANGLE_VOL_SHIFT;
ch.phaseacc -= cycle_rate;
if (ch.phaseacc >= 0)
{
if (ch.adder < ch.duty_flip)
ch.output_vol = output;
else
ch.output_vol = -output;
return ch.output_vol;
}
if (ch.freq > cycle_rate)
{
ch.phaseacc += ch.freq;
ch.adder = (ch.adder + 1) & 0x0F;
if (ch.adder < ch.duty_flip)
ch.output_vol = output;
else
ch.output_vol = -output;
}
else
{
// 加重平均
int num_times, total;
num_times = total = 0;
while (ch.phaseacc < 0)
{
ch.phaseacc += ch.freq;
ch.adder = (ch.adder + 1) & 0x0F;
if (ch.adder < ch.duty_flip)
total += output;
else
total -= output;
num_times++;
}
ch.output_vol = total / num_times;
}
return ch.output_vol;
}
public class SYNCRECTANGLE public class SYNCRECTANGLE
{ {
// For sync // For sync
@ -54,6 +349,38 @@ namespace VirtualNes.Core
public byte holdnote; public byte holdnote;
public byte[] dummy = new byte[2]; public byte[] dummy = new byte[2];
public int vbl_length; public int vbl_length;
public void ZeroMemory()
{
Array.Clear(reg, 0, reg.Length);
enable = 0;
holdnote = 0;
Array.Clear(dummy, 0, dummy.Length);
vbl_length = 0;
}
}
public class RECTANGLE
{
public byte[] reg = new byte[4];
public byte enable;
public int vbl_length;
public int phaseacc;
public int freq;
public int output_vol;
public byte fixed_envelope;
public byte holdnote;
public byte volume;
public byte env_vol;
public int env_phase;
public int env_decay;
public int adder;
public int duty_flip;
} }
} }
} }

View File

@ -1,25 +1,95 @@
namespace VirtualNes.Core using System;
using System.Net;
namespace VirtualNes.Core
{ {
public class APU_VRC7 : APU_INTERFACE public class APU_VRC7 : APU_INTERFACE
{ {
OPLL VRC7_OPLL;
byte address;
public APU_VRC7()
{
Emu2413API.OPLL_init(3579545, 22050); // 仮のサンプリングレート
VRC7_OPLL = Emu2413API.OPLL_new();
if (VRC7_OPLL != null)
{
Emu2413API.OPLL_reset(VRC7_OPLL);
Emu2413API.OPLL_reset_patch(VRC7_OPLL, Emu2413API.OPLL_VRC7_TONE);
VRC7_OPLL.masterVolume = 128;
}
// 仮設定
Reset(APU_CLOCK, 22050);
}
public override void Dispose()
{
if (VRC7_OPLL != null)
{
Emu2413API.OPLL_delete(VRC7_OPLL);
VRC7_OPLL = null;
// OPLL_close(); // 無くても良い(中身無し)
}
}
public override void Reset(float fClock, int nRate) public override void Reset(float fClock, int nRate)
{ {
//todo : 实现 if (VRC7_OPLL != null)
{
Emu2413API.OPLL_reset(VRC7_OPLL);
Emu2413API.OPLL_reset_patch(VRC7_OPLL, Emu2413API.OPLL_VRC7_TONE);
VRC7_OPLL.masterVolume = 128;
}
address = 0;
Setup(fClock, nRate);
} }
public override void Setup(float fClock, int nRate) public override void Setup(float fClock, int nRate)
{ {
//todo : 实现 Emu2413API.OPLL_setClock((UInt32)(fClock * 2.0f), (UInt32)nRate);
} }
public override void Write(ushort addr, byte data) public override void Write(ushort addr, byte data)
{ {
//todo : 实现 if (VRC7_OPLL != null)
{
if (addr == 0x9010)
{
address = data;
}
else if (addr == 0x9030)
{
Emu2413API.OPLL_writeReg(VRC7_OPLL, address, data);
}
}
} }
public override int Process(int channel) public override int Process(int channel)
{ {
//todo : 实现 if (VRC7_OPLL != null)
return Emu2413API.OPLL_calc(VRC7_OPLL);
return 0;
}
float[] blkmul = { 0.5f, 1.0f, 2.0f, 4.0f, 8.0f, 16.0f, 32.0f, 64.0f };
public override int GetFreq(int channel)
{
if (VRC7_OPLL != null && channel < 8)
{
int fno = ((VRC7_OPLL.reg[0x20 + channel] & 0x01) << 8) + VRC7_OPLL.reg[0x10 + channel];
int blk = (VRC7_OPLL.reg[0x20 + channel] >> 1) & 0x07;
if ((VRC7_OPLL.reg[0x20 + channel] & 0x10) != 0)
{
return (int)((256.0d * (double)fno * blkmul[blk]) / ((double)(1 << 18) / (3579545.0 / 72.0)));
}
}
return 0; return 0;
} }
} }

View File

@ -80,10 +80,36 @@ namespace VirtualNes.Core
public UInt32 noiseB_idx; public UInt32 noiseB_idx;
public UInt32 noiseA_dphase; public UInt32 noiseA_dphase;
public UInt32 noiseB_dphase; public UInt32 noiseB_dphase;
public int masterVolume; /* 0min -- 64 -- 127 max (Liner) */
} }
public static class Emu2413API public static class Emu2413API
{ {
/* Bits for Pitch and Amp modulator */
public const int PM_PG_BITS = 8;
public const int PM_PG_WIDTH = 1 << PM_PG_BITS;
public const int PM_DP_BITS = 16;
public const int PM_DP_WIDTH = (1 << PM_DP_BITS);
public const int AM_PG_BITS = 8;
public const int AM_PG_WIDTH = (1 << AM_PG_BITS);
public const int AM_DP_BITS = 16;
public const int AM_DP_WIDTH = (1 << AM_DP_BITS);
/* PM table is calcurated by PM_AMP * pow(2,PM_DEPTH*sin(x)/1200) */
public const int PM_AMP_BITS = 8;
public const int PM_AMP = (1 << PM_AMP_BITS);
/* PM speed(Hz) and depth(cent) */
public const double PM_SPEED = 6.4d;
public const double PM_DEPTH = 13.75d;
public const int OPLL_2413_TONE = 0;
public const int OPLL_VRC7_TONE = 1;
static int[] pmtable = new int[PM_PG_WIDTH];
static int[] amtable = new int[AM_PG_WIDTH];
public static void OPLL_init(UInt32 c, UInt32 r) public static void OPLL_init(UInt32 c, UInt32 r)
{ {
makePmTable(); makePmTable();
@ -97,7 +123,7 @@ namespace VirtualNes.Core
OPLL_setClock(c, r); OPLL_setClock(c, r);
} }
private static void OPLL_setClock(uint c, uint r) internal static void OPLL_setClock(uint c, uint r)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
@ -138,6 +164,39 @@ namespace VirtualNes.Core
} }
private static void makePmTable() private static void makePmTable()
{
int i;
for (i = 0; i < PM_PG_WIDTH; i++)
pmtable[i] = (int)(PM_AMP * Math.Pow(2, PM_DEPTH * Math.Sin(2.0 * Math.PI * i / PM_PG_WIDTH) / 1200));
}
internal static OPLL OPLL_new()
{
throw new NotImplementedException();
}
internal static void OPLL_reset(OPLL vRC7_OPLL)
{
throw new NotImplementedException();
}
internal static void OPLL_reset_patch(OPLL vRC7_OPLL, int oPLL_VRC7_TONE)
{
throw new NotImplementedException();
}
internal static void OPLL_delete(OPLL vRC7_OPLL)
{
throw new NotImplementedException();
}
internal static void OPLL_writeReg(OPLL opll, UInt32 reg, UInt32 data)
{
throw new NotImplementedException();
}
internal static int OPLL_calc(OPLL opll)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }

View File

@ -437,8 +437,6 @@ namespace VirtualNes.Core
} }
} }
private byte SyncSub(int no, ControllerState state) private byte SyncSub(int no, ControllerState state)
{ {
ushort bit = 0; ushort bit = 0;