More audio

This commit is contained in:
Asnivor 2024-11-05 16:49:57 +00:00
parent 85061362ae
commit 58473484a0
3 changed files with 95 additions and 39 deletions

View File

@ -8,9 +8,30 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
{ {
public partial class ASIC : ISoundProvider public partial class ASIC : ISoundProvider
{ {
/// <summary>
/// Output sample rate
/// </summary>
public const double SAMPLE_RATE = 44100; public const double SAMPLE_RATE = 44100;
public double FRAME_RATE => _sv.CpuFreq / _sv.CpuClocksPerFrame;
public double samplesPerFrame => SAMPLE_RATE / FRAME_RATE; /// <summary>
/// Total number of audio samples in a frame
/// </summary>
public double SamplesPerFrame => SAMPLE_RATE / _sv.RefreshRate;
/// <summary>
/// Total CPU cycles in a frame
/// </summary>
public int CpuTicksPerFrame => (int)_sv.CpuTicksPerFrame;
/// <summary>
/// The calculated CPU tick position within the frame
/// </summary>
public int TickPos;
/// <summary>
/// Keeps track of additional ticks outside of the frame window that need to be processed as a part of the next frame
/// </summary>
public int TicksOverrun;
/// <summary> /// <summary>
/// CH1: the right square wave channel /// CH1: the right square wave channel
@ -25,7 +46,9 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
/// <summary> /// <summary>
/// CH4: the noise channel /// CH4: the noise channel
/// </summary> /// </summary>
private CH_Noise _ch4; public CH_Noise _ch4;
public void InitAudio() public void InitAudio()
{ {
@ -33,18 +56,41 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
_ch2 = new CH_Square(this, 2); _ch2 = new CH_Square(this, 2);
_ch4 = new CH_Noise(this); _ch4 = new CH_Noise(this);
ch1Buff = new short[(int)samplesPerFrame]; ch1Buff = new short[(int)SamplesPerFrame];
ch2Buff = new short[(int)samplesPerFrame]; ch2Buff = new short[(int)SamplesPerFrame];
ch4LBuff = new short[(int)samplesPerFrame];
ch4RBuff = new short[(int)samplesPerFrame]; ch4LBuff = new short[(int)SamplesPerFrame];
ch4RBuff = new short[(int)SamplesPerFrame];
} }
public void AudioClock(int ticks) public void AudioClock(int incomingTicks)
{ {
int ticks = incomingTicks + TicksOverrun;
// determine frame boundaries
if (TickPos + ticks >= CpuTicksPerFrame)
{
// we are about to overrun the frame
TicksOverrun = TickPos + ticks - CpuTicksPerFrame;
// set ticks to run only up to the frame boundary
ticks -= TicksOverrun;
}
else
{
// still within frame boundaries
TicksOverrun = 0;
}
_ch1.Clock(ticks); _ch1.Clock(ticks);
_ch2.Clock(ticks); _ch2.Clock(ticks);
_ch4.Clock(ticks); _ch4.Clock(ticks);
TickPos += ticks;
if (TickPos == CpuTicksPerFrame)
{
TickPos = 0;
}
} }
public void SyncAudioState(Serializer ser) public void SyncAudioState(Serializer ser)
@ -151,7 +197,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
_chIndex = chIndex; _chIndex = chIndex;
_blip = new BlipBuffer((int)(_asic.samplesPerFrame * 2)); _blip = new BlipBuffer((int)(_asic.SamplesPerFrame));
_blip.SetRates(_cpuFreq, SAMPLE_RATE); _blip.SetRates(_cpuFreq, SAMPLE_RATE);
} }
@ -177,7 +223,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
if (_tickBuffer >= ticksPerFreq) if (_tickBuffer >= ticksPerFreq)
{ {
_tickBuffer -= ticksPerFreq; _tickBuffer -= ticksPerFreq;
int sample = GetSquareWaveSample(_asic._sv.FrameClock, FreqActual, DutyCycle) * Volume / 15; int sample = GetSquareWaveSample(_asic.TickPos + t, FreqActual, DutyCycle);
_blip.AddDelta((uint)_asic._sv.FrameClock, (short)sample); _blip.AddDelta((uint)_asic._sv.FrameClock, (short)sample);
} }
} }
@ -190,7 +236,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
public void LengthChanged() => _lenCounter = Length; public void LengthChanged() => _lenCounter = Length;
private static int GetSquareWaveSample(double time, double freq, int dutyCycle) private int GetSquareWaveSample(double time, double freq, int dutyCycle)
{ {
double sTime = time / SAMPLE_RATE; double sTime = time / SAMPLE_RATE;
double period = 1.0 / freq; double period = 1.0 / freq;
@ -203,12 +249,12 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
_ => 0.50 _ => 0.50
}; };
double currentTimeInPeriod = sTime % period; double currentTimeInPeriod = sTime % period;
return currentTimeInPeriod < period * dutyCycleFraction ? short.MaxValue / 32 : short.MinValue / 32; return currentTimeInPeriod < period * dutyCycleFraction ? Volume * 8 : Volume * -8;
} }
public virtual void SyncState(Serializer ser) public virtual void SyncState(Serializer ser)
{ {
ser.BeginSection($"AUDIO_CH{_chIndex}_NOISE"); ser.BeginSection($"AUDIO_CH{_chIndex}_SQUARE");
ser.Sync(nameof(LenPrescaler), ref _lenPrescaler); ser.Sync(nameof(LenPrescaler), ref _lenPrescaler);
ser.Sync(nameof(_lenCounter), ref _lenCounter); ser.Sync(nameof(_lenCounter), ref _lenCounter);
ser.Sync(nameof(_tickBuffer), ref _tickBuffer); ser.Sync(nameof(_tickBuffer), ref _tickBuffer);
@ -311,10 +357,10 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
_asic = asic; _asic = asic;
_noiseFreqs = NoiseFreqEntry.Init(_cpuFreq); _noiseFreqs = NoiseFreqEntry.Init(_cpuFreq);
_blipL = new BlipBuffer((int) (_asic.samplesPerFrame * 2)); _blipL = new BlipBuffer((int) (_asic.SamplesPerFrame));
_blipL.SetRates(_cpuFreq, SAMPLE_RATE); _blipL.SetRates(_cpuFreq, SAMPLE_RATE);
_blipR = new BlipBuffer((int) (_asic.samplesPerFrame * 2)); _blipR = new BlipBuffer((int) (_asic.SamplesPerFrame));
_blipR.SetRates(_cpuFreq, SAMPLE_RATE); _blipR.SetRates(_cpuFreq, SAMPLE_RATE);
} }
@ -341,8 +387,8 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
{ {
_tickBuffer -= _currNoiseFreq.TicksPerFreq; _tickBuffer -= _currNoiseFreq.TicksPerFreq;
ClockLFSR(); ClockLFSR();
_blipL.AddDelta((uint)_asic._sv.FrameClock, LeftChannelOutput ? (short)LFSR : 0); _blipL.AddDelta((uint)(_asic.TickPos + t), LeftChannelOutput ? (short)LFSR : 0);
_blipR.AddDelta((uint)_asic._sv.FrameClock, RightChannelOutput ? (short)LFSR : 0); _blipR.AddDelta((uint)(_asic.TickPos + t), RightChannelOutput ? (short)LFSR : 0);
} }
} }
} }
@ -480,10 +526,10 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
public void GetSamplesSync(out short[] samples, out int nsamp) public void GetSamplesSync(out short[] samples, out int nsamp)
{ {
nsamp = (int)samplesPerFrame; nsamp = (int)SamplesPerFrame;
// L // L
_ch2.Blip.EndFrame((uint) _sv.CpuClocksPerFrame); _ch2.Blip.EndFrame((uint)CpuTicksPerFrame);
int ch2Nsamp = _ch2.Blip.SamplesAvailable(); int ch2Nsamp = _ch2.Blip.SamplesAvailable();
if (ch2Nsamp != nsamp) if (ch2Nsamp != nsamp)
{ {
@ -491,7 +537,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
} }
_ch2.Blip.ReadSamples(ch2Buff, nsamp, false); _ch2.Blip.ReadSamples(ch2Buff, nsamp, false);
_ch4.BlipL.EndFrame((uint) _sv.CpuClocksPerFrame); _ch4.BlipL.EndFrame((uint)CpuTicksPerFrame);
int ch4LNsamp = _ch4.BlipL.SamplesAvailable(); int ch4LNsamp = _ch4.BlipL.SamplesAvailable();
if (ch4LNsamp != nsamp) if (ch4LNsamp != nsamp)
{ {
@ -500,7 +546,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
_ch4.BlipL.ReadSamples(ch4LBuff, nsamp, false); _ch4.BlipL.ReadSamples(ch4LBuff, nsamp, false);
// R // R
_ch1.Blip.EndFrame((uint)_sv.CpuClocksPerFrame); _ch1.Blip.EndFrame((uint)CpuTicksPerFrame);
int ch1Nsamp = _ch1.Blip.SamplesAvailable(); int ch1Nsamp = _ch1.Blip.SamplesAvailable();
if (ch1Nsamp != nsamp) if (ch1Nsamp != nsamp)
{ {
@ -508,7 +554,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
} }
_ch1.Blip.ReadSamples(ch1Buff, nsamp, false); _ch1.Blip.ReadSamples(ch1Buff, nsamp, false);
_ch4.BlipR.EndFrame((uint) _sv.CpuClocksPerFrame); _ch4.BlipR.EndFrame((uint)CpuTicksPerFrame);
int ch4RNsamp = _ch4.BlipR.SamplesAvailable(); int ch4RNsamp = _ch4.BlipR.SamplesAvailable();
if (ch4RNsamp != nsamp) if (ch4RNsamp != nsamp)
{ {
@ -531,6 +577,8 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
samples[o] = (short)ClampSample(left); samples[o] = (short)ClampSample(left);
samples[o + 1] = (short)ClampSample(right); samples[o + 1] = (short)ClampSample(right);
} }
DiscardSamples();
} }
private static int ClampSample(int sample) private static int ClampSample(int sample)

View File

@ -23,9 +23,6 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
} }
private int _bankSelect; private int _bankSelect;
private int _bankOffset => BankSelect << 14;
/// <summary> /// <summary>
/// True when the CPU is accessing memory /// True when the CPU is accessing memory
/// </summary> /// </summary>
@ -72,7 +69,8 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
// 1: 2nd 16k // 1: 2nd 16k
// 2: 3rd 16k // 2: 3rd 16k
// etc.. // etc..
result = _cartridge.ReadByte((ushort) ((address % 0x4000) + (_bankOffset % _cartridge.CartROMSize))); //result = _cartridge.ReadByte((ushort) ((address % 0x4000) + (_bankOffset % _cartridge.CartROMSize)));
result = _cartridge.ReadByte((ushort) ((address % 0x4000) + ((BankSelect * 0x4000) % _cartridge.CartROMSize)));
break; break;
// 0xC000 - 0xFFFF // 0xC000 - 0xFFFF
@ -133,7 +131,7 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
case 6: case 6:
case 7: case 7:
// fixed to the last 16K in the cart address space // fixed to the last 16K in the cart address space
_cartridge.WriteByte((ushort) ((address % 0x4000) + (_bankOffset % _cartridge.CartROMSize)), value); _cartridge.WriteByte((ushort) ((address % 0x4000) + ((BankSelect * 0x4000) % _cartridge.CartROMSize)), value);
break; break;
} }
} }

View File

@ -13,14 +13,24 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
public bool DeterministicEmulation => true; public bool DeterministicEmulation => true;
private double _cpuClocksPerFrame; /// <summary>
private double _cpuClocksPerSecond; /// CPU frequency
/// </summary>
public double CpuFreq;
/// <summary>
/// Total number of CPU cycles in a frame
/// </summary>
public double CpuTicksPerFrame;
/// <summary>
/// Number of frames per second
/// </summary>
public double RefreshRate;
private int _frameClock; private int _frameClock;
private int _frame; private int _frame;
public double CpuFreq => _cpuClocksPerSecond;
public double CpuClocksPerFrame => _cpuClocksPerFrame;
public int FrameClock public int FrameClock
{ {
get => _frameClock; get => _frameClock;
@ -30,18 +40,18 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
private void CalcClock() private void CalcClock()
{ {
_cpuClocksPerSecond = 4_000_000.0; CpuFreq = 4_000_000.0;
_cpuClocksPerFrame = CpuTicksPerFrame =
(40 + 1) * // pixel clocks per scanline + 1 latch write pixel clock (40 + 1) * // pixel clocks per scanline + 1 latch write pixel clock
6 * // cpu clocks per pixel 6 * // cpu clocks per pixel
160 * // scanlines per field 160 * // scanlines per field
2; // fields per frame 2; // fields per frame
double refreshRate = _cpuClocksPerSecond / _cpuClocksPerFrame; // 50.8130081300813 RefreshRate = CpuFreq / CpuTicksPerFrame; // 50.8130081300813
_asic.Screen.SetRates( _asic.Screen.SetRates(
(int) _cpuClocksPerSecond, (int) CpuFreq,
(int) _cpuClocksPerFrame); (int) CpuTicksPerFrame);
_asic.InitAudio(); _asic.InitAudio();
} }
@ -66,14 +76,14 @@ namespace BizHawk.Emulation.Cores.Consoles.SuperVision
//_asic.FrameStart = true; //_asic.FrameStart = true;
while (_frameClock < _cpuClocksPerFrame) while (_frameClock < CpuTicksPerFrame)
{ {
int ticks = _cpu.ExecuteInstruction(); int ticks = _cpu.ExecuteInstruction();
_asic.Clock(ticks); _asic.Clock(ticks);
//_cpu.ExecuteOne(); //_cpu.ExecuteOne();
} }
_frameClock %= (int)_cpuClocksPerFrame; _frameClock %= (int)CpuTicksPerFrame;
_frame++; _frame++;
if (_isLag) if (_isLag)