APU fully implemented and working but approx 0.25s latency to fix

This commit is contained in:
2026-05-14 22:31:23 +01:00
parent cafcf625ad
commit 780f8ee790
7 changed files with 160 additions and 18 deletions

View File

@@ -58,6 +58,12 @@
richTextBox1 = new RichTextBox();
button1 = new Button();
CpuRun = new Button();
groupBox1 = new GroupBox();
lblTone0 = new Label();
lblNoise = new Label();
lblTone2 = new Label();
lblTone1 = new Label();
groupBox1.SuspendLayout();
SuspendLayout();
//
// lblAF
@@ -164,10 +170,10 @@
//
// txtMemoryView
//
txtMemoryView.Location = new Point(110, 100);
txtMemoryView.Location = new Point(110, 101);
txtMemoryView.Margin = new Padding(2);
txtMemoryView.Name = "txtMemoryView";
txtMemoryView.Size = new Size(553, 1118);
txtMemoryView.Size = new Size(553, 543);
txtMemoryView.TabIndex = 15;
txtMemoryView.Text = "Memory View Window";
//
@@ -242,7 +248,7 @@
// lblIff1
//
lblIff1.AutoSize = true;
lblIff1.Location = new Point(709, 514);
lblIff1.Location = new Point(11, 533);
lblIff1.Margin = new Padding(4, 0, 4, 0);
lblIff1.Name = "lblIff1";
lblIff1.Size = new Size(45, 25);
@@ -252,7 +258,7 @@
// lblIff2
//
lblIff2.AutoSize = true;
lblIff2.Location = new Point(709, 572);
lblIff2.Location = new Point(11, 586);
lblIff2.Margin = new Padding(4, 0, 4, 0);
lblIff2.Name = "lblIff2";
lblIff2.Size = new Size(45, 25);
@@ -262,12 +268,12 @@
// lblIE
//
lblIE.AutoSize = true;
lblIE.Location = new Point(710, 462);
lblIE.Location = new Point(11, 486);
lblIE.Margin = new Padding(4, 0, 4, 0);
lblIE.Name = "lblIE";
lblIE.Size = new Size(133, 25);
lblIE.Size = new Size(33, 25);
lblIE.TabIndex = 26;
lblIE.Text = "Interrupt Mode";
lblIE.Text = "IM";
//
// btnReset
//
@@ -289,7 +295,7 @@
// lblFrames
//
lblFrames.AutoSize = true;
lblFrames.Location = new Point(709, 852);
lblFrames.Location = new Point(14, 660);
lblFrames.Margin = new Padding(2, 0, 2, 0);
lblFrames.Name = "lblFrames";
lblFrames.Size = new Size(149, 25);
@@ -299,7 +305,7 @@
// lblFPS
//
lblFPS.AutoSize = true;
lblFPS.Location = new Point(824, 949);
lblFPS.Location = new Point(129, 757);
lblFPS.Margin = new Padding(2, 0, 2, 0);
lblFPS.Name = "lblFPS";
lblFPS.Size = new Size(41, 25);
@@ -309,7 +315,7 @@
// lblFrameTime
//
lblFrameTime.AutoSize = true;
lblFrameTime.Location = new Point(755, 898);
lblFrameTime.Location = new Point(60, 706);
lblFrameTime.Margin = new Padding(2, 0, 2, 0);
lblFrameTime.Name = "lblFrameTime";
lblFrameTime.Size = new Size(104, 25);
@@ -319,7 +325,7 @@
// richTextBox1
//
richTextBox1.Enabled = false;
richTextBox1.Location = new Point(709, 636);
richTextBox1.Location = new Point(682, 474);
richTextBox1.Margin = new Padding(4);
richTextBox1.Name = "richTextBox1";
richTextBox1.ReadOnly = true;
@@ -329,7 +335,7 @@
//
// button1
//
button1.Location = new Point(694, 1030);
button1.Location = new Point(1075, 756);
button1.Margin = new Padding(4);
button1.Name = "button1";
button1.Size = new Size(118, 36);
@@ -340,7 +346,7 @@
//
// CpuRun
//
CpuRun.Location = new Point(694, 1104);
CpuRun.Location = new Point(943, 757);
CpuRun.Margin = new Padding(4);
CpuRun.Name = "CpuRun";
CpuRun.Size = new Size(118, 36);
@@ -349,11 +355,61 @@
CpuRun.UseVisualStyleBackColor = true;
CpuRun.Click += CpuRun_Click;
//
// groupBox1
//
groupBox1.Controls.Add(lblTone1);
groupBox1.Controls.Add(lblTone2);
groupBox1.Controls.Add(lblNoise);
groupBox1.Controls.Add(lblTone0);
groupBox1.Location = new Point(266, 658);
groupBox1.Name = "groupBox1";
groupBox1.Size = new Size(397, 222);
groupBox1.TabIndex = 35;
groupBox1.TabStop = false;
groupBox1.Text = "Audio (SN76489)";
//
// lblTone0
//
lblTone0.AutoSize = true;
lblTone0.Location = new Point(25, 48);
lblTone0.Name = "lblTone0";
lblTone0.Size = new Size(59, 25);
lblTone0.TabIndex = 0;
lblTone0.Text = "Tone0";
//
// lblNoise
//
lblNoise.AutoSize = true;
lblNoise.Location = new Point(25, 160);
lblNoise.Name = "lblNoise";
lblNoise.Size = new Size(57, 25);
lblNoise.TabIndex = 1;
lblNoise.Text = "Noise";
//
// lblTone2
//
lblTone2.AutoSize = true;
lblTone2.Location = new Point(25, 122);
lblTone2.Name = "lblTone2";
lblTone2.Size = new Size(59, 25);
lblTone2.TabIndex = 2;
lblTone2.Text = "Tone2";
//
// lblTone1
//
lblTone1.AutoSize = true;
lblTone1.Location = new Point(25, 83);
lblTone1.Name = "lblTone1";
lblTone1.Size = new Size(59, 25);
lblTone1.TabIndex = 3;
lblTone1.Text = "Tone1";
//
// DebuggerForm
//
AutoScaleDimensions = new SizeF(10F, 25F);
AutoScaleMode = AutoScaleMode.Font;
ClientSize = new Size(1206, 1238);
ClientSize = new Size(1206, 892);
Controls.Add(groupBox1);
Controls.Add(CpuRun);
Controls.Add(button1);
Controls.Add(richTextBox1);
@@ -385,6 +441,8 @@
Margin = new Padding(2);
Name = "DebuggerForm";
Text = "Debugger";
groupBox1.ResumeLayout(false);
groupBox1.PerformLayout();
ResumeLayout(false);
PerformLayout();
}
@@ -420,6 +478,11 @@
private Button button1;
private Button CpuRun;
public System.Windows.Forms.Timer uiUpdateTimer;
private GroupBox groupBox1;
private Label lblTone1;
private Label lblTone2;
private Label lblNoise;
private Label lblTone0;
//private TextBox textBox4;
}
}

View File

@@ -1,6 +1,8 @@
using System;
using System.Reflection.PortableExecutable;
using System.Text;
using System.Windows.Forms;
using Core.Audio;
using Core.Cpu;
using Core.Memory;
@@ -100,7 +102,7 @@ namespace Desktop
lblIY.Text = $"IY: {_cpu.IY.Word:X4}";
lblIff1.Text = $"IFF1: {_cpu.IFF1}";
lblIff2.Text = $"IFF2: {_cpu.IFF2}";
lblIE.Text = $"Interrupt Mode: {_cpu.InterruptMode}";
lblIE.Text = $"IM: {_cpu.InterruptMode}";
lblFlags.Text = $"Flags: {_cpu.GetFlagsString()}";
lblTStates.Text = $"T-States: {_cpu.TotalTStates}";
lblFrames.Text = $"Frames Rendered: {_mainForm.TotalFrameCount}";
@@ -109,11 +111,23 @@ namespace Desktop
UpdateMemoryView();
UpdateStackView();
UpdateDisassemblyView();
// --- AUDIO REGISTERS ---
// Use _mainForm to access the Machine, and then grab the AudioProcessor!
if (_mainForm._machine != null && _mainForm._machine.AudioProcessor != null)
{
ushort[] apuRegs = _mainForm._machine.AudioProcessor.Registers;
lblTone0.Text = $"Tone 0: 0x{apuRegs[0]:X4} (Vol: 0x{apuRegs[1]:X1})";
lblTone1.Text = $"Tone 1: 0x{apuRegs[2]:X4} (Vol: 0x{apuRegs[3]:X1})";
lblTone2.Text = $"Tone 2: 0x{apuRegs[4]:X4} (Vol: 0x{apuRegs[5]:X1})";
lblNoise.Text = $"Noise : 0x{apuRegs[6]:X1} (Vol: 0x{apuRegs[7]:X1})";
}
}
private void UpdateMemoryView()
{
int count = 40;
int count = 20;
// Try to parse the hex string the user typed in
if (!ushort.TryParse(txtMemoryStart.Text, System.Globalization.NumberStyles.HexNumber, null, out ushort startAddress))
{

View File

@@ -120,4 +120,7 @@
<metadata name="uiUpdateTimer.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value>
</metadata>
<metadata name="$this.TrayHeight" type="System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>47</value>
</metadata>
</root>

View File

@@ -343,6 +343,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="NAudio" Version="2.3.0" />
<PackageReference Include="Vortice.XInput" Version="3.8.3" />
</ItemGroup>

View File

@@ -139,6 +139,7 @@
Margin = new Padding(4);
Name = "ParsonsForm1";
Text = "Form1";
FormClosing += ParsonsForm1_FormClosing;
menuStrip1.ResumeLayout(false);
menuStrip1.PerformLayout();
ResumeLayout(false);

View File

@@ -6,14 +6,16 @@ using System.Runtime.InteropServices;
using System.Threading.Tasks;
using System.Windows.Forms;
using Core;
using Desktop.Audio;
namespace Desktop
{
public partial class ParsonsForm1 : Form
{
private SmsMachine _machine = null!;
public SmsMachine _machine = null!;
private DebuggerForm _debugger;
private Bitmap _screenBitmap = new Bitmap(256, 192, PixelFormat.Format32bppArgb);
private NAudioPlayer _audioPlayer;
private Task _emulatorTask;
private double TargetFrameTime = 16.667f;
public int TotalFrameCount = 0;
@@ -34,6 +36,8 @@ namespace Desktop
InitializeComponent();
this.Text = $"Parsons Master System 2026 - {_currentRomName}";
_machine = new SmsMachine();
_audioPlayer = new NAudioPlayer();
_machine.AudioProcessor.AudioDevice = _audioPlayer;
PopulateIncludedRomsMenu();
@@ -157,7 +161,8 @@ namespace Desktop
framesThisSecond = 0;
lastFpsUpdate = currentTime;
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate {
BeginInvoke((System.Windows.Forms.MethodInvoker)delegate
{
this.Text = $"Parsons Master System - {_currentRomName} [FPS/FT: {FramesPerSecond:F0}/{FrameTime:F1}]";
});
}
@@ -299,5 +304,11 @@ namespace Desktop
_machine.IoBus.Joypad1Keyboard |= bitMask; // Target Keyboard!
}
}
private void ParsonsForm1_FormClosing(object sender, FormClosingEventArgs e)
{
IsRunning = false;
_audioPlayer?.Stop();
}
}
}

49
Desktop/NAudioPlayer.cs Normal file
View File

@@ -0,0 +1,49 @@
using System;
using Core.Interfaces;
using Microsoft.VisualBasic;
using NAudio.Wave;
namespace Desktop.Audio // Change this to match your project's namespace
{
public class NAudioPlayer : IAudioDevice
{
private WaveOutEvent _waveOut;
private BufferedWaveProvider _buffer;
public NAudioPlayer()
{
// Set up the audio format: 44100 Hz, 32-bit IEEE float, 1 channel (Mono)
WaveFormat format = WaveFormat.CreateIeeeFloatWaveFormat(44100, 1);
_buffer = new BufferedWaveProvider(format);
// CRITICAL FOR EMULATORS:
// If the emulator runs slightly too fast, the audio buffer will fill up
// and the sound will lag behind the gameplay. Discarding overflow keeps it synced!
_buffer.DiscardOnBufferOverflow = true;
_waveOut = new WaveOutEvent();
// 100ms latency is a great sweet spot for WinForms emulators.
// Too low = crackling audio. Too high = delayed sound effects.
_waveOut.DesiredLatency = 100;
_waveOut.Init(_buffer);
_waveOut.Play();
}
public void AddSample(float sample)
{
// Convert the float (-1.0f to 1.0f) into 4 raw bytes
byte[] sampleBytes = BitConverter.GetBytes(sample);
// Push the 4 bytes to the sound card buffer!
_buffer.AddSamples(sampleBytes, 0, 4);
}
public void Stop()
{
_waveOut?.Stop();
_waveOut?.Dispose();
}
}
}