From 8313c103f6c42beae4012a7f78fad5a3202e64b3 Mon Sep 17 00:00:00 2001 From: goyuken Date: Tue, 30 Oct 2012 12:07:33 +0000 Subject: [PATCH] Namco163 Audio --- BizHawk.Emulation/BizHawk.Emulation.csproj | 1 + .../Nintendo/NES/Boards/NAMCOT_m19_m210.cs | 56 +++++- .../NES/Boards/Namcot1xx/Namco163Audio.cs | 179 ++++++++++++++++++ 3 files changed, 228 insertions(+), 8 deletions(-) create mode 100644 BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Namcot1xx/Namco163Audio.cs diff --git a/BizHawk.Emulation/BizHawk.Emulation.csproj b/BizHawk.Emulation/BizHawk.Emulation.csproj index 42098b7d69..43ffeb3499 100644 --- a/BizHawk.Emulation/BizHawk.Emulation.csproj +++ b/BizHawk.Emulation/BizHawk.Emulation.csproj @@ -302,6 +302,7 @@ + Code diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/NAMCOT_m19_m210.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/NAMCOT_m19_m210.cs index 6e40ace7db..f42b5b70be 100644 --- a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/NAMCOT_m19_m210.cs +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/NAMCOT_m19_m210.cs @@ -26,12 +26,20 @@ namespace BizHawk.Emulation.Consoles.Nintendo int irq_cycles; bool irq_pending; + Namco163Audio audio; + int audio_cycles; + public override void Dispose() { base.Dispose(); prg_banks_8k.Dispose(); chr_banks_1k.Dispose(); nt_banks_1k.Dispose(); + if (audio != null) + { + audio.Dispose(); + audio = null; + } } public override void SyncState(Serializer ser) @@ -40,12 +48,18 @@ namespace BizHawk.Emulation.Consoles.Nintendo ser.Sync("prg_banks_8k", ref prg_banks_8k); ser.Sync("chr_banks_1k", ref chr_banks_1k); ser.Sync("nt_banks_1k", ref nt_banks_1k); - for(int i=0;i<2;i++) ser.Sync("vram_enable_" + i, ref vram_enable[i]); + for (int i = 0; i < 2; i++) + ser.Sync("vram_enable_" + i, ref vram_enable[i]); ser.Sync("irq_counter", ref irq_counter); ser.Sync("irq_enabled", ref irq_enabled); ser.Sync("irq_cycles", ref irq_cycles); ser.Sync("irq_pending", ref irq_pending); SyncIRQ(); + if (audio != null) + { + ser.Sync("audio_cycles", ref audio_cycles); + audio.SyncState(ser); + } } public override bool Configure(NES.EDetectionOrigin origin) @@ -66,6 +80,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo //hydelide 3 *this is a good test of more advanced features Cart.vram_size = 8; //not many test cases of this, but hydelide 3 needs it. AssertPrg(128,256); AssertChr(128,256); AssertVram(8); AssertWram(0,8); + audio = new Namco163Audio(); break; //mapper 210: @@ -99,6 +114,11 @@ namespace BizHawk.Emulation.Consoles.Nintendo addr &= 0xF800; switch (addr) { + case 0x0800: + if (audio != null) + return audio.ReadData(); + else + break; case 0x1000: return (byte)(irq_counter & 0xFF); case 0x1800: @@ -113,7 +133,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo switch (addr) { case 0x0800: - //sound data port. not used. + if (audio != null) + audio.WriteData(value); break; case 0x1000: irq_counter = (irq_counter & 0xFF00) | value; @@ -175,6 +196,10 @@ namespace BizHawk.Emulation.Consoles.Nintendo case 0x7000: //$F000 prg_banks_8k[2] = (value & 0x3F) & prg_bank_mask_8k; break; + case 0x7800: //$F800 + if (audio != null) + audio.WriteAddr(value); + break; } } @@ -279,15 +304,30 @@ namespace BizHawk.Emulation.Consoles.Nintendo public override void ClockPPU() { - if (!irq_enabled) return; - - irq_cycles--; - if (irq_cycles == 0) + if (irq_enabled) { - irq_cycles += 3; - ClockIRQ(); + irq_cycles--; + if (irq_cycles == 0) + { + irq_cycles += 3; + ClockIRQ(); + } + } + if (audio != null) + { + audio_cycles++; + if (audio_cycles == 15 * 3) + { + audio_cycles = 0; + audio.Clock(); + } } } + public override void ApplyCustomAudio(short[] samples) + { + if (audio != null) + audio.ApplyCustomAudio(samples); + } } } \ No newline at end of file diff --git a/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Namcot1xx/Namco163Audio.cs b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Namcot1xx/Namco163Audio.cs new file mode 100644 index 0000000000..f1db069855 --- /dev/null +++ b/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Namcot1xx/Namco163Audio.cs @@ -0,0 +1,179 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace BizHawk.Emulation.Consoles.Nintendo +{ + // http://wiki.nesdev.com/w/index.php/Namco_163_audio + public class Namco163Audio : IDisposable + { + //ByteBuffer ram = new ByteBuffer(0x80); + byte[] ram = new byte[0x80]; + int addr; + bool autoincrement; + + public void Dispose() + { + //ram.Dispose(); + ram = null; + resampler.Dispose(); + resampler = null; + } + + /// + /// F800:FFFF + /// + /// + public void WriteAddr(byte value) + { + addr = value & 0x7f; + autoincrement = (value & 0x80) != 0; + } + + /// + /// 4800:4FFF + /// + /// + public void WriteData(byte value) + { + ram[addr] = value; + if (autoincrement) + { + addr++; + addr &= 0x7f; + } + } + + /// + /// 4800:4FFF + /// + /// + public byte ReadData() + { + byte ret = ram[addr]; + if (autoincrement) + { + addr++; + addr &= 0x7f; + } + return ret; + } + + /// + /// last channel clocked + /// + int ch; + + // output buffer; not savestated + //short[] samplebuff = new short[2048]; + //int samplebuffpos; + + /// + /// 119318hz (CPU / 15) + /// + public void Clock() + { + ch--; + int lastch = 8 - (ram[0x7f] >> 4 & 7); + if (ch < lastch) + ch = 8; + + byte samp = ClockChannel(ch); + + //samplebuff[samplebuffpos++] = samp; + //samplebuffpos &= 2047; + short ss = (short)(samp * 50 - 12096); + resampler.EnqueueSample(ss, ss); + } + + byte ClockChannel(int ch) + { + // channel regs are at [b..b+7] + int b = ch * 8 + 56; + + // not that bad really, and best to do exactly as specified because + // the results (phase) can be read back + int phase = ram[b + 1] | ram[b + 3] << 8 | ram[b + 5] << 16; + int freq = ram[b] | ram[b + 2] << 8 | ram[b + 4] << 16 & 0x030000; + int length = 256 - (ram[b + 4] & 0xfc); + phase = (phase + freq) % (length << 16); + + int pos = (phase >> 16) + ram[b + 6]; + pos &= 0xff; + int sample = ram[pos / 2] >> (pos & 1) * 4 & 0xf; + byte ret = (byte)(sample * (ram[b + 7] & 0x0f)); + // writeback phase + ram[b + 5] = (byte)(phase >> 16); + ram[b + 3] = (byte)(phase >> 8); + ram[b + 1] = (byte)phase; + return ret; + } + + public void SyncState(Serializer ser) + { + ser.Sync("ram", ref ram, false); + ser.Sync("addr", ref addr); + ser.Sync("autoincrement", ref autoincrement); + ser.Sync("ch", ref ch); + } + + Sound.Utilities.SpeexResampler resampler; + Sound.Utilities.DCFilter dc; + Sound.MetaspuAsync metaspu; + + public Namco163Audio() + { + resampler = new Sound.Utilities.SpeexResampler(2, 119318, 44100, 119318, 44100, null, null); + dc = new Sound.Utilities.DCFilter(4096); + metaspu = new Sound.MetaspuAsync(resampler, Sound.ESynchMethod.ESynchMethod_V); + } + + public void ApplyCustomAudio(short[] samples) + { + short[] tmp = new short[samples.Length]; + metaspu.GetSamples(tmp); + for (int i = 0; i < samples.Length; i++) + { + int samp = samples[i] + tmp[i]; + if (samp > 32767) + samples[i] = 32767; + else if (samp < -32768) + samples[i] = -32768; + else + samples[i] = (short)samp; + } + dc.PushThroughSamples(samples, samples.Length); + } + + // the same junk used in FDSAudio + // the problem here is, the raw 120khz output contains significant amounts of crap that gets + // massively garbaged up by this resampling + /* + public void ApplyCustomAudio(short[] samples) + { + for (int i = 0; i < samples.Length; i += 2) + { + // worst imaginable resampling + int pos = i * samplebuffpos / samples.Length; + int samp = samplebuff[pos] * 50 - 12096; + samp += samples[i]; + if (samp > 32767) + samples[i] = 32767; + else if (samp < -32768) + samples[i] = -32768; + else + samples[i] = (short)samp; + + // NES audio is mono, so this should be identical anyway + samples[i + 1] = samples[i]; + } + samplebuffpos = 0; + + dc.PushThroughSamples(samples, samples.Length); + } + */ + + + } +}