Namco163 Audio

This commit is contained in:
goyuken 2012-10-30 12:07:33 +00:00
parent ca86a8dff3
commit 8313c103f6
3 changed files with 228 additions and 8 deletions

View File

@ -302,6 +302,7 @@
<Compile Include="Consoles\Nintendo\NES\Boards\Namcot1xx\Mapper112.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Namcot1xx\Mapper154.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Namcot1xx\Mapper206.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Namcot1xx\Namco163Audio.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\Namcot1xx\Namcot1xx.cs" />
<Compile Include="Consoles\Nintendo\NES\Boards\NAMCOT_m19_m210.cs">
<SubType>Code</SubType>

View File

@ -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);
}
}
}

View File

@ -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;
}
/// <summary>
/// F800:FFFF
/// </summary>
/// <param name="value"></param>
public void WriteAddr(byte value)
{
addr = value & 0x7f;
autoincrement = (value & 0x80) != 0;
}
/// <summary>
/// 4800:4FFF
/// </summary>
/// <param name="value"></param>
public void WriteData(byte value)
{
ram[addr] = value;
if (autoincrement)
{
addr++;
addr &= 0x7f;
}
}
/// <summary>
/// 4800:4FFF
/// </summary>
/// <returns></returns>
public byte ReadData()
{
byte ret = ram[addr];
if (autoincrement)
{
addr++;
addr &= 0x7f;
}
return ret;
}
/// <summary>
/// last channel clocked
/// </summary>
int ch;
// output buffer; not savestated
//short[] samplebuff = new short[2048];
//int samplebuffpos;
/// <summary>
/// 119318hz (CPU / 15)
/// </summary>
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);
}
*/
}
}