using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace BizHawk.Emulation.Sound { /* * old VRC6 audio. * not quite a drop in replacement for the new one. * doesn't sound right. */ 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 = (short)(Pulse1.RenderSample() << 4); val += (short)(Pulse2.RenderSample() << 7); val += (short)(Sawtooth.RenderSample() << 7); samples[i++] = val; samples[i++] = val; } } public VRC6() { MaxVolume = (short.MaxValue / 3); } 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 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 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 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 void SyncState(Serializer ser) { ser.Sync("AccumRate", ref AccumRate); ser.Sync("AccumStep", ref AccumStep); ser.Sync("Accum", ref Accum); 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("OUT", ref OUT); } 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; } } } }