NES - start VRC6 sound, currently sounds terrible

This commit is contained in:
adelikat 2012-08-03 02:08:42 +00:00
parent cc82bfbefc
commit 70f07346b1
3 changed files with 367 additions and 3 deletions

View File

@ -351,6 +351,7 @@
<Compile Include="Sound\CDAudio.cs" />
<Compile Include="Sound\Utilities\DualSound.cs" />
<Compile Include="Sound\Utilities\Equalizer.cs" />
<Compile Include="Sound\VRC6.cs" />
<Compile Include="Sound\YM2612.IO.cs" />
<Compile Include="Sound\YM2612.Channel.cs" />
<Compile Include="Sound\YM2612.Operator.cs" />

View File

@ -12,6 +12,8 @@ namespace BizHawk.Emulation.Consoles.Nintendo
int prg_bank_mask_8k, chr_bank_mask_1k;
bool newer_variant;
Sound.VRC6 VRC6Sound = new Sound.VRC6();
//state
int prg_bank_16k, prg_bank_8k;
ByteBuffer prg_banks_8k = new ByteBuffer(4);
@ -32,6 +34,7 @@ namespace BizHawk.Emulation.Consoles.Nintendo
public override void SyncState(Serializer ser)
{
base.SyncState(ser);
VRC6Sound.SyncState(ser);
ser.Sync("prg_bank_16k", ref prg_bank_16k);
ser.Sync("prg_bank_8k", ref prg_bank_8k);
ser.Sync("chr_banks_1k", ref chr_banks_1k);
@ -132,21 +135,33 @@ namespace BizHawk.Emulation.Consoles.Nintendo
break;
case 0x1000: //$9000
VRC6Sound.Write9000(value);
break;
case 0x1001: //$9001
VRC6Sound.Write9000(value);
break;
case 0x1002: //$9002
//TODO pulse 1
VRC6Sound.Write9002(value);
break;
case 0x2000: //$A000
VRC6Sound.WriteA000(value);
break;
case 0x2001: //$A001
VRC6Sound.WriteA001(value);
break;
case 0x2002: //$A002
//TODO pulse 2
VRC6Sound.WriteA002(value);
break;
case 0x3000: //$B000
VRC6Sound.WriteB000(value);
break;
case 0x3001: //$B001
VRC6Sound.WriteB001(value);
break;
case 0x3002: //$B002
//TODO sawtooth
VRC6Sound.WriteB002(value);
break;
case 0x3003: //$B003
@ -247,5 +262,16 @@ namespace BizHawk.Emulation.Consoles.Nintendo
}
}
public override void ApplyCustomAudio(short[] samples)
{
short[] fmsamples = new short[samples.Length];
VRC6Sound.GetSamples(fmsamples);
int len = samples.Length;
for (int i = 0; i < len; i++)
{
samples[i] = (short)((samples[i] >> 1) + (fmsamples[i] >> 1));
}
}
}
}

View File

@ -0,0 +1,337 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace BizHawk.Emulation.Sound
{
class VRC6 : ISoundProvider
{
public void DiscardSamples() { }
public int MaxVolume { get; set; }
public void GetSamples(short[] samples)
{
for (int i = 0; i < samples.Length; )
{
short val = 0;
val = Pulse1.RenderSample();
val *= Pulse2.RenderSample();
val *= Sawtooth.RenderSample();
val = (short)((val * MaxVolume) / short.MaxValue);
samples[i++] = val;
samples[i++] = val;
}
}
public VRC6()
{
MaxVolume = (short.MaxValue / 2);
}
public void SyncState(Serializer ser)
{
Pulse1.SyncState(ser);
Pulse2.SyncState(ser);
Sawtooth.SyncState(ser);
}
public void Write9000(byte value)
{
Pulse1.Write9000(value);
}
public void Write9001(byte value)
{
Pulse1.Write9001(value);
}
public void Write9002(byte value)
{
Pulse1.Write9002(value);
}
public void WriteA000(byte value)
{
Pulse2.WriteA000(value);
}
public void WriteA001(byte value)
{
Pulse2.WriteA001(value);
}
public void WriteA002(byte value)
{
Pulse2.WriteA002(value);
}
public void WriteB000(byte value)
{
Sawtooth.WriteB000(value);
}
public void WriteB001(byte value)
{
Sawtooth.WriteB001(value);
}
public void WriteB002(byte value)
{
Sawtooth.WriteB002(value);
}
private Chn_VRC6Pulse1 Pulse1 = new Chn_VRC6Pulse1();
private Chn_VRC6Pulse2 Pulse2 = new Chn_VRC6Pulse2();
private Chn_VRC6Sawtooth Sawtooth = new Chn_VRC6Sawtooth();
public class Chn_VRC6Pulse1
{
byte _Volume = 0;
double DutyPercentage = 0;
int _DutyCycle = 0;
int _FreqTimer = 0;
bool _Enabled = false;
double _Frequency = 0;
double _SampleCount = 0;
double _RenderedLength = 0;
bool WaveStatus = false;
short OUT = 0;
public void SyncState(Serializer ser)
{
ser.Sync("_Volume", ref _Volume);
//ser.Sync("DutyPercentage", ref DutyPercentage);
ser.Sync("_DutyCycle", ref _DutyCycle);
ser.Sync("_FreqTimer", ref _FreqTimer);
ser.Sync("_Enabled", ref _Enabled);
//ser.Sync("_Frequency", ref _Frequency);
//ser.Sync("_SampleCount", ref _SampleCount);
//ser.Sync("_RenderedLength", ref _RenderedLength);
//ser.Sync("WaveStatus", ref WaveStatus);
ser.Sync("OUT", ref OUT);
}
public short RenderSample()
{
if (_Enabled)
{
_SampleCount++;
if (WaveStatus && (_SampleCount > (_RenderedLength * DutyPercentage)))
{
_SampleCount -= _RenderedLength * DutyPercentage;
WaveStatus = !WaveStatus;
}
else if (!WaveStatus && (_SampleCount > (_RenderedLength * (1.0 - DutyPercentage))))
{
_SampleCount -= _RenderedLength * (1.0 - DutyPercentage);
WaveStatus = !WaveStatus;
}
if (WaveStatus)
OUT = (short)(-_Volume);
else
OUT = (short)(_Volume);
return OUT;
}
return 0;
}
public void Write9000(byte data)
{
_Volume = (byte)(data & 0x0F);//Bit 0 - 3
_DutyCycle = (data >> 4); //Bit 4 - 7
if (_DutyCycle == 0)
DutyPercentage = 0.6250;
else if (_DutyCycle == 1)
DutyPercentage = 0.1250;
else if (_DutyCycle == 2)
DutyPercentage = 0.1875;
else if (_DutyCycle == 3)
DutyPercentage = 0.2500;
else if (_DutyCycle == 4)
DutyPercentage = 0.3125;
else if (_DutyCycle == 5)
DutyPercentage = 0.3750;
else if (_DutyCycle == 6)
DutyPercentage = 0.4375;
else if (_DutyCycle == 7)
DutyPercentage = 0.5000;
else
DutyPercentage = 1.0;
}
public void Write9001(byte data)
{
_FreqTimer = (_FreqTimer & 0x0F00) | data;
//Update freq
_Frequency = 1790000 / 16 / (_FreqTimer + 1);
_RenderedLength = 44100 / _Frequency;
}
public void Write9002(byte data)
{
_FreqTimer = (_FreqTimer & 0x00FF) | ((data & 0x0F) << 8);
_Enabled = (data & 0x80) != 0;
//Update freq
_Frequency = 1790000 / 16 / (_FreqTimer + 1);
_RenderedLength = 44100 / _Frequency;
}
}
public class Chn_VRC6Pulse2
{
byte _Volume = 0;
double DutyPercentage = 0;
int _DutyCycle = 0;
int _FreqTimer = 0;
bool _Enabled = false;
double _Frequency = 0;
double _SampleCount = 0;
double _RenderedLength = 0;
bool WaveStatus = false;
short OUT = 0;
public short RenderSample()
{
if (_Enabled)
{
_SampleCount++;
if (WaveStatus && (_SampleCount > (_RenderedLength * DutyPercentage)))
{
_SampleCount -= _RenderedLength * DutyPercentage;
WaveStatus = !WaveStatus;
}
else if (!WaveStatus && (_SampleCount > (_RenderedLength * (1.0 - DutyPercentage))))
{
_SampleCount -= _RenderedLength * (1.0 - DutyPercentage);
WaveStatus = !WaveStatus;
}
if (WaveStatus)
OUT = (short)(-_Volume);
else
OUT = (short)(_Volume);
return OUT;
}
return 0;
}
public void WriteA000(byte data)
{
_Volume = (byte)(data & 0x0F);//Bit 0 - 3
_DutyCycle = (data >> 4); //Bit 4 - 7
if (_DutyCycle == 0)
DutyPercentage = 0.6250;
else if (_DutyCycle == 1)
DutyPercentage = 0.1250;
else if (_DutyCycle == 2)
DutyPercentage = 0.1875;
else if (_DutyCycle == 3)
DutyPercentage = 0.2500;
else if (_DutyCycle == 4)
DutyPercentage = 0.3125;
else if (_DutyCycle == 5)
DutyPercentage = 0.3750;
else if (_DutyCycle == 6)
DutyPercentage = 0.4375;
else if (_DutyCycle == 7)
DutyPercentage = 0.5000;
else
DutyPercentage = 1.0;
}
public void WriteA001(byte data)
{
_FreqTimer = (_FreqTimer & 0x0F00) | data;
//Update freq
_Frequency = 1790000 / 16 / (_FreqTimer + 1);
_RenderedLength = 44100 / _Frequency;
}
public void WriteA002(byte data)
{
_FreqTimer = (_FreqTimer & 0x00FF) | ((data & 0x0F) << 8);
_Enabled = (data & 0x80) != 0;
//Update freq
_Frequency = 1790000 / 16 / (_FreqTimer + 1);
_RenderedLength = 44100 / _Frequency;
}
public void SyncState(Serializer ser)
{
ser.Sync("_Volume", ref _Volume); //TODO
//st.VRC6Pulse2DutyPercentage = DutyPercentage;
//st.VRC6Pulse2_DutyCycle = _DutyCycle;
//st.VRC6Pulse2_FreqTimer = _FreqTimer;
//st.VRC6Pulse2_Enabled = _Enabled;
//st.VRC6Pulse2_Frequency = _Frequency;
//st.VRC6Pulse2_SampleCount = _SampleCount;
//st.VRC6Pulse2_RenderedLength = _RenderedLength;
//st.VRC6Pulse2WaveStatus = WaveStatus;
//st.VRC6Pulse2OUT = OUT;
}
}
public class Chn_VRC6Sawtooth
{
byte AccumRate = 0;
byte AccumStep = 0;
byte Accum = 0;
int _FreqTimer = 0;
bool _Enabled = false;
double _Frequency = 0;
double _SampleCount = 0;
double _RenderedLength = 0;
short OUT = 0;
public short RenderSample()
{
if (_Enabled)
{
_SampleCount++;
if (_SampleCount >= _RenderedLength)
{
_SampleCount -= _RenderedLength;
AccumStep++;
if ((AccumStep & 2) != 0)
Accum += AccumRate;
if (AccumStep >= 14)
AccumStep = Accum = 0;
OUT = (short)(Accum >> 3);
}
return (short)((OUT - 5));
}
return 0;
}
public void WriteB000(byte data)
{
AccumRate = (byte)(data & 0x3F);
}
public void WriteB001(byte data)
{
_FreqTimer = (_FreqTimer & 0x0F00) | data;
//Update freq
_Frequency = 1790000 / (_FreqTimer + 1);
_RenderedLength = 44100 / _Frequency;
}
public void WriteB002(byte data)
{
_FreqTimer = (_FreqTimer & 0x00FF) | ((data & 0x0F) << 8);
_Enabled = (data & 0x80) != 0;
//Update freq
_Frequency = 1790000 / (_FreqTimer + 1);
_RenderedLength = 44100 / _Frequency;
}
public void SyncState(Serializer ser)
{
//ser.Sync("_Volume", ref _Volume); //TODO
//st.VRC6Pulse2DutyPercentage = DutyPercentage;
//st.VRC6Pulse2_DutyCycle = _DutyCycle;
//st.VRC6Pulse2_FreqTimer = _FreqTimer;
//st.VRC6Pulse2_Enabled = _Enabled;
//st.VRC6Pulse2_Frequency = _Frequency;
//st.VRC6Pulse2_SampleCount = _SampleCount;
//st.VRC6Pulse2_RenderedLength = _RenderedLength;
//st.VRC6Pulse2WaveStatus = WaveStatus;
//st.VRC6Pulse2OUT = OUT;
}
}
}
}