C# Gameboy Emulator Sound Interference Noise - Naudio
Posted: 16 Mar 2025, 23:22
Wenn ich den Emulator mit einem ROM starte, spielt ein sehr lautes Interferenzgeräusch und der Emulator stürzt ab. Ich weiß nicht, ob es sich um ein Timing, ein Naudio- oder Spu -Emulationsfehler handelt. />
Der gesamte Quellcode kann hier gefunden werden>
Code: Select all
public sealed class GameBoyEmulator : NexusConsoleGame
{
private readonly Processor _cpu;
private readonly SoundProcessor _spu;
private readonly MemoryManagement _mmu;
private readonly PixelProcessor _ppu;
private readonly Timer _timer;
private readonly Joypad _joypad;
private double accumulatedTime;
private int cpuCycles;
private int cyclesThisUpdate;
public GameBoyEmulator(string rom)
{
_spu = new SoundProcessor();
_mmu = MemoryManagement.LoadGamePak(rom, _spu);
_cpu = new Processor(_mmu);
_ppu = new PixelProcessor(Graphic, _mmu);
_timer = new Timer(_mmu);
_joypad = new Joypad(_mmu);
}
protected override void Load()
{
Settings.ColorPalette = new GameBoyColorPalette();
Settings.Font = new NexusFont("Consolas", new NexusSize(8));
Settings.Title = "NexusGB";
Settings.StopGameKey = NexusKey.Escape;
}
protected override void Update()
{
accumulatedTime += DeltaTime * 1_000_000_000;
while (accumulatedTime >= 16740000)
{
accumulatedTime -= 16740000;
Input.UpdateGamepads();
Input.Update();
_joypad.HandleInputs(Input.Gamepad1, Input.Keys);
while (cyclesThisUpdate < GameBoySystem.CyclesPerUpdate)
{
cpuCycles = _cpu.Execute();
cyclesThisUpdate += cpuCycles;
_timer.Update(cpuCycles);
_ppu.Update(cpuCycles);
_spu.Update(cpuCycles);
_joypad.Update();
HandleInterrupts();
}
cyclesThisUpdate -= GameBoySystem.CyclesPerUpdate;
}
}
protected override void OnCrash(Exception exception)
=> Utility.ShowAlert("Error", $"An error occured:\n{exception}", NexusAlertIcon.Error);
protected override void CleanUp()
{
}
private void HandleInterrupts()
{
var interruptEnable = _mmu.InterruptEnable;
var interruptFlag = _mmu.InterruptFlag;
for (int i = 0; i < 5; i++)
{
if ((((interruptEnable & interruptFlag) >> i) & 0x01) == 1)
{
_cpu.ExecuteInterrupt(i);
}
}
_cpu.UpdateIme();
}
}
< /code>
Dies ist die Soundprozesssinginit: < /p>
public sealed class SoundProcessor
{
private readonly ImmutableArray _channels;
private readonly WaveSoundChannel _wave;
private byte number50;
private byte number51;
private byte number52;
private byte Sound1Volume => (byte)(number50 & 0x7);
public SoundProcessor()
{
_channels =
[
new SquareSweepChannel(this),
new SquareChannel(this),
_wave = new WaveSoundChannel(this),
new NoiseChannel(this)
];
_channels[0].WriteNumber(0, 0x80);
_channels[0].WriteNumber(1, 0xBF);
_channels[0].WriteNumber(2, 0xF3);
_channels[0].WriteNumber(4, 0xBF);
_channels[1].WriteNumber(1, 0x3F);
_channels[1].WriteNumber(2, 0x00);
_channels[1].WriteNumber(4, 0xBF);
_channels[2].WriteNumber(0, 0x7F);
_channels[2].WriteNumber(1, 0xFF);
_channels[2].WriteNumber(2, 0x9F);
_channels[2].WriteNumber(3, 0xBF);
_channels[3].WriteNumber(1, 0xFF);
_channels[3].WriteNumber(2, 0x00);
_channels[3].WriteNumber(3, 0x00);
_channels[3].WriteNumber(4, 0xBF);
number50 = 0x77;
number51 = 0xF3;
number52 = 0xF1;
}
public void Update(in int cycles)
{
if ((number52 & (1 = 0xFF27 and < 0xFF30: return 0x00;
case >= 0xFF30 and < 0xFF40: return _wave.ReadRam((ushort)(address - 0xFF30));
}
var relativeAddress = address - 0xFF10;
return _channels[relativeAddress / 5].ReadNumber(relativeAddress % 5);
}
public void WriteByte(in ushort address, in byte value)
{
switch (address)
{
case 0xFF24: number50 = value; return;
case 0xFF25: number51 = value; return;
case 0xFF26: number52 = value; return;
case >= 0xFF27 and < 0xFF30: return;
case >= 0xFF30 and < 0xFF40: _wave.WriteRam((ushort)(address - 0xFF30), value); return;
}
var relativeAddress = address - 0xFF10;
_channels[relativeAddress / 5].WriteNumber(relativeAddress % 5, value);
}
public void WriteToSoundBuffer(in int channel, in Span totalBuffer, in int index, float sample)
{
sample *= Sound1Volume / 7f;
if ((number51 & (1
public abstract class BaseSoundChannel
{
private readonly byte[] _numbers;
protected readonly int _channelNumber;
protected readonly SoundProcessor _spu;
protected readonly WindowsSoundOut _out;
private float volume;
public ref float ChannelVolume => ref volume;
protected BaseSoundChannel(SoundProcessor spu, in int channelNumber)
{
_numbers = new byte[5];
_channelNumber = channelNumber;
_spu = spu;
_out = new WindowsSoundOut();
ChannelVolume = 0.05f;
}
public abstract void Update(in int cycles);
public virtual byte ReadNumber(in int index) => _numbers[index];
public virtual void WriteNumber(in int index, in byte value) => _numbers[index] = value;
}
< /code>
Dies ist die tatsächliche Tonausgabe für ein Windows-Gerät: < /p>
public sealed class WindowsSoundOut
{
private readonly DirectSoundOut soundOut;
private readonly BufferedWaveProvider _provider;
private readonly byte[] _addSampleData;
public int SampleRate => _provider.WaveFormat.SampleRate;
public WindowsSoundOut()
{
_provider = new BufferedWaveProvider(new WaveFormat(44100, 16, 2));
soundOut = new DirectSoundOut(100);
soundOut.Init(_provider);
soundOut.Play();
_addSampleData = new byte[_provider.BufferLength];
}
public void BufferSoundSamples(in Span sampleData, in int length)
{
var index = 0;
foreach (var current in MemoryMarshal.Cast(sampleData))
{
_addSampleData[index++] = current;
}
_provider.AddSamples(_addSampleData, 0, length + sizeof(float)); //The `InvalidOperationException: "Buffer full"` from NAudio occurs here
}
}