NES - start VRC6 sound, currently sounds terrible
This commit is contained in:
parent
cc82bfbefc
commit
70f07346b1
|
@ -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" />
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue