BizHawk/BizHawk.Emulation/Consoles/Nintendo/NES/Boards/Namcot1xx/Namco163Audio.cs

180 lines
4.2 KiB
C#

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);
}
*/
}
}