1. Nick Massey
  2. Emu-o-Tron

Commits

Nick Massey  committed 896ba78

Fix bug on closing when using DX input without a joystick attached.
Run Memory Vis on nes frames instead of with timer.
Add Sound Visualizer.
Change square freq calculation.
Simulate reads and writes after NSFs return instead of just empty cycles for DMC fetch.

  • Participants
  • Parent commits 91abe6b
  • Branches default

Comments (0)

Files changed (17)

File DirectXEmu/DirectXEmu.csproj

View file
  • Ignore whitespace
       <DependentUpon>PollKey.cs</DependentUpon>
     </Compile>
     <Compile Include="RomPatching.cs" />
+    <Compile Include="SoundVis.cs">
+      <SubType>Form</SubType>
+    </Compile>
+    <Compile Include="SoundVis.Designer.cs">
+      <DependentUpon>SoundVis.cs</DependentUpon>
+    </Compile>
     <Compile Include="Video\DX10Renderer.cs" />
     <Compile Include="Video\DX9Renderer.cs" />
     <Compile Include="EmuoTron\APU.cs" />
     </Compile>
     <Compile Include="Video\Scalers\Scale2x.cs" />
     <Compile Include="Video\Scalers\IScaler.cs" />
+    <EmbeddedResource Include="SoundVis.resx">
+      <DependentUpon>SoundVis.cs</DependentUpon>
+    </EmbeddedResource>
     <EmbeddedResource Include="Video\Shaders\SimpleRender.fx" />
   </ItemGroup>
   <ItemGroup>

File DirectXEmu/EmuoTron/APU.cs

View file
  • Ignore whitespace
             output = new short[this.sampleRate]; //the buffers really don't need to be this large, but it should prevent overflows when the FPS is set exceptionally low.
             for (int i = 0; i < 32; i++)
             {
-                pulseTable[i] = ((95.52 / (8128.0 / i + 100)));
+                pulseTable[i] = ((95.52 / (8128.0 / i + 100.0)));
                 pulseTableShort[i] = (int)(pulseTable[i] * 0x7FFF); //Half the range for internal channels, half for external.
             }
             for (int i = 0; i < 204; i++)
             {
-                tndTable[i] = ((163.67 / (24329.0 / i + 100)));
+                tndTable[i] = ((163.67 / (24329.0 / i + 100.0)));
                 tndTableShort[i] = (int)(tndTable[i] * 0x7FFF);
             }
             volume.square1 = 1;
             outputPtr = 0;
             dmc.ptr = 0;
             dmcOutputPtr = 0;
+#if DEBUGGER
+            nes.debug.APUFrameReset();
+#endif
         }
         private void QuarterFrame()
         {
             }
             for (int updateCycle = lastUpdateCycle; updateCycle < currentTime; updateCycle++)
             {
-                byte square1Volume = (byte)(square1.Cycle() * volume.square1);
-                byte square2Volume = (byte)(square2.Cycle() * volume.square2);
-                byte triangleVolume = (byte)(triangle.Cycle() * volume.triangle);
-                byte noiseVolume = (byte)(noise.Cycle() * volume.noise);
-                byte dmcVolume = (byte)(dmc.buffer[dmcOutputPtr++] * volume.dmc);
-                byte externalVolume = (byte)(external.Cycle() * volume.external);//treating external volume as 0-255
+                byte square1Volume = square1.Cycle();
+                byte square2Volume = square2.Cycle();
+                byte triangleVolume = triangle.Cycle();
+                byte noiseVolume = noise.Cycle();
+                byte dmcVolume = dmc.buffer[dmcOutputPtr++];
+                byte externalVolume = external.Cycle();//treating external volume as 0-255
+#if DEBUGGER
+                nes.debug.APUCycle(square1Volume, square2Volume, triangleVolume, noiseVolume, dmcVolume, externalVolume);
+#endif
+                square1Volume = (byte)(square1Volume * volume.square1);
+                square2Volume = (byte)(square2Volume * volume.square2);
+                triangleVolume = (byte)(triangleVolume * volume.triangle);
+                noiseVolume = (byte)(noiseVolume * volume.noise);
+                dmcVolume = (byte)(dmcVolume * volume.dmc);
+                externalVolume = (byte)(externalVolume * volume.external);
                 sampleTotal += (short)((tndTableShort[(3 * triangleVolume) + (2 * noiseVolume) + dmcVolume] + pulseTableShort[square1Volume + square2Volume] + (externalVolume << 7)) ^ 0x8000);//just inserting external sound linearly.
                 sampleCount++;
                 sampleRateDivider--;

File DirectXEmu/EmuoTron/Channels/Noise.cs

View file
  • Ignore whitespace
                     volume = envelope;
                 else
                     volume = envelopeCounter;
-                byte feedback;
+                int feedback;
                 if (loop)
-                    feedback = (byte)((shiftReg & 1) ^ ((shiftReg >> 6) & 1));
+                    feedback = (shiftReg & 1) ^ ((shiftReg >> 6) & 1);
                 else
-                    feedback = (byte)((shiftReg & 1) ^ ((shiftReg >> 1) & 1));
+                    feedback = (shiftReg & 1) ^ ((shiftReg >> 1) & 1);
                 shiftReg >>= 1;
                 shiftReg = (ushort)(shiftReg | (feedback << 14));
                 divider = timer;

File DirectXEmu/EmuoTron/Channels/Square.cs

View file
  • Ignore whitespace
                     break;
                 case 2: //Low Timer
                     timer = (ushort)((timer & 0x700) | value);
-                    freq = (timer + 1) * 2;
+                    freq = (timer * 2) + 1;
                     divider = freq;
                     break;
                 case 3: //Length Counter and High Timer
                     if (enabled)
                         lengthCounter = nes.APU.lengthTable[value >> 3];
                     timer = (ushort)((timer & 0x00FF) | ((value & 0x7) << 8));
-                    freq = (timer + 1) * 2;
+                    freq = (timer * 2) + 1;
                     divider = freq;
                     dutySequencer = 0;
                     startFlag = true;

File DirectXEmu/EmuoTron/Channels/Triangle.cs

View file
  • Ignore whitespace
                     break;
                 case 2: //Low Timer
                     timer = (ushort)((timer & 0x0700) | value);
-                    freq = (timer + 1);
+                    freq = timer + 1;
                     divider = freq;
                     break;
                 case 3: //Length Counter and High Timer
                     if (enabled)
                         lengthCounter = nes.APU.lengthTable[(value >> 3)];
                     timer = (ushort)((timer & 0x00FF) | ((value & 0x7) << 8));
-                    freq = (timer + 1);
+                    freq = timer + 1;
                     divider = freq;
                     haltFlag = true;
                     break;
             divider--;
             if (divider == 0)
             {
-                if (timer <= 1) //Filter ultra-high freq.
+                if (timer <= 2) //Filter ultra-high freq.
                     volume = 7;
                 else
                     volume = sequence[sequenceCounter % 32];

File DirectXEmu/EmuoTron/Debug.cs

View file
  • Ignore whitespace
         public StringBuilder logBuilder = new StringBuilder();
         public bool logging = false;
 
+        public int historySamplesPerFrame = 128;
+        public int historyTimer;
+        public int historyCounter;
+
+        public byte[] square1History;
+        public byte[] square2History;
+        public byte[] triangleHistory;
+        public byte[] noiseHistory;
+        public byte[] dmcHistory;
+        public byte[] externalHistory;
+
         public int Scanline
         {
             get
         public Debug(NESCore nes)
         {
             this.nes = nes;
+            square1History = new byte[historySamplesPerFrame];
+            square2History = new byte[historySamplesPerFrame];
+            triangleHistory = new byte[historySamplesPerFrame];
+            noiseHistory = new byte[historySamplesPerFrame];
+            dmcHistory = new byte[historySamplesPerFrame];
+            externalHistory = new byte[historySamplesPerFrame];
+        }
+
+        public void APUCycle(byte square1, byte square2, byte triangle, byte noise, byte dmc, byte external)
+        {
+            if(historyTimer == 0)
+            {
+                historyTimer = nes.APU.CPUClock / historySamplesPerFrame / (int)(nes.APU.FPS);
+                if (historyCounter < historySamplesPerFrame)
+                {
+                    square1History[historyCounter] = square1;
+                    square2History[historyCounter] = square2;
+                    triangleHistory[historyCounter] = triangle;
+                    noiseHistory[historyCounter] = noise;
+                    dmcHistory[historyCounter] = dmc;
+                    externalHistory[historyCounter] = external;
+                }
+                historyCounter++;
+            }
+            historyTimer--;
+        }
+        
+        public void APUFrameReset()
+        {
+            historyCounter = 0;
+            historyTimer = 0;
         }
 
         public void Execute(ushort address)

File DirectXEmu/EmuoTron/Mappers/mNSF.cs

View file
  • Ignore whitespace
         public double speed;
         public double counter;
         public bool overTime;
+        public bool readOut;
 
         public mNSF(NESCore nes, byte[] banks, int PBRATE, int specialChip)
         {
         {
             if (!nes.PPU.frameComplete)
                 counter -= cycles;
-            if (counter <= 0)
+            if (counter <= 0 && !readOut)
             {
                 overTime = true;
                 counter = speed;

File DirectXEmu/EmuoTron/NESCore.cs

View file
  • Ignore whitespace
                         if(nsfPlayer && RegS == 0xFF)
                         {
                             PushWordStack(RegPC - 1);
-                            APU.AddCycles((int)((Mappers.mNSF) mapper).counter);
+                            ((Mappers.mNSF) mapper).readOut = true;
+                            int i = 0;
+                            while ((int)((Mappers.mNSF)mapper).counter > 0)
+                            {
+                                if(i % 3 == 0)
+                                    Write(0xFFFF,0xFF);
+                                else
+                                    Read(0x0000);
+                                i++;
+                            }
+                            ((Mappers.mNSF)mapper).readOut = false;
                             PPU.frameComplete = true;
                             ((Mappers.mNSF)mapper).counter = ((Mappers.mNSF)mapper).speed;
                         }

File DirectXEmu/Input/DXInput.cs

View file
  • Ignore whitespace
         {
             keyboard.Dispose();
             mouse.Dispose();
-            joystick.Dispose();
+            if(joystick != null)
+                joystick.Dispose();
             device.Dispose();
         }
 

File DirectXEmu/MemoryVis.Designer.cs

View file
  • Ignore whitespace
         /// </summary>
         private void InitializeComponent()
         {
-            this.components = new System.ComponentModel.Container();
             this.visPanel = new System.Windows.Forms.Panel();
-            this.tmrUpdate = new System.Windows.Forms.Timer(this.components);
             this.SuspendLayout();
             // 
             // visPanel
             this.visPanel.Size = new System.Drawing.Size(512, 512);
             this.visPanel.TabIndex = 0;
             // 
-            // tmrUpdate
-            // 
-            this.tmrUpdate.Tick += new System.EventHandler(this.tmrUpdate_Tick);
-            // 
             // MemoryVis
             // 
             this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
         #endregion
 
         private System.Windows.Forms.Panel visPanel;
-        private System.Windows.Forms.Timer tmrUpdate;
     }
 }

File DirectXEmu/MemoryVis.cs

View file
  • Ignore whitespace
 {
     public partial class MemoryVis : Form
     {
-        private Debug debug;
+        private NESCore nes;
         private Bitmap buffer = new Bitmap(512,512, PixelFormat.Format32bppArgb);
         private Graphics screenGfx;
-        public MemoryVis(Debug debug)
+        public MemoryVis()
         {
             InitializeComponent();
-            this.debug = debug;
-            tmrUpdate.Enabled = true;
-
             screenGfx = visPanel.CreateGraphics();
         }
 
-        private unsafe void tmrUpdate_Tick(object sender, EventArgs e)
+        private int timer;
+        public void Update(NESCore nes)
         {
+
+            this.nes = nes;
+            if(timer++ % 6 == 0)
+                Redraw();
+        }
+
+        private unsafe void Redraw()
+        {
+
             var bmd = buffer.LockBits(new Rectangle(0, 0, 512, 512), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
-            uint* ptr = (uint*) bmd.Scan0;
+            uint* ptr = (uint*)bmd.Scan0;
 
-            double time = debug.cpuTime;
-            for(int x = 0; x < 256; x++)
+            for (int x = 0; x < 256; x++)
             {
-                for(int y = 0; y < 256; y++)
+                for (int y = 0; y < 256; y++)
                 {
-                    int address = (y*256) + x;
-                    uint color = (uint)(0xFF000000) | (uint)TimeToColor(debug.memoryReads[address]) | (uint)(TimeToColor(debug.memoryWrites[address]) << 8) | (uint)(TimeToColor(debug.memoryExecutes[address]) << 16);
+                    int address = (y * 256) + x;
+                    uint color = (uint)(0xFF000000) | 
+                        (uint)TimeToColor(nes.debug.memoryReads[address]) | 
+                        (uint)(TimeToColor(nes.debug.memoryWrites[address]) << 8) | 
+                        (uint)(TimeToColor(nes.debug.memoryExecutes[address]) << 16);
                     ptr[(((y * 2) + 0) * 512) + ((x * 2) + 0)] = color;
                     ptr[(((y * 2) + 0) * 512) + ((x * 2) + 1)] = color;
                     ptr[(((y * 2) + 1) * 512) + ((x * 2) + 0)] = color;
 
         private byte TimeToColor(long time)
         {
-            long age = debug.cpuTime - time;
+            long age = nes.debug.cpuTime - time;
 
             if (time == 0)
                 return 0x00;

File DirectXEmu/MemoryVis.resx

View file
  • Ignore whitespace
   <resheader name="writer">
     <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
   </resheader>
-  <metadata name="tmrUpdate.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
-    <value>17, 17</value>
-  </metadata>
 </root>

File DirectXEmu/Program.Designer.cs

View file
  • Ignore whitespace
             this.testConsoleToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.enableLoggingToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.openLogToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.memoryVisualizerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.helpToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.helpToolStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem();
             this.romInfoToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.recordDialog = new System.Windows.Forms.SaveFileDialog();
             this.surfaceControl = new System.Windows.Forms.Panel();
             this.saveMovie = new System.Windows.Forms.SaveFileDialog();
-            this.memoryVisualizerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
+            this.soundVisualizerToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem();
             this.menuStrip.SuspendLayout();
             this.SuspendLayout();
             // 
             this.testConsoleToolStripMenuItem,
             this.enableLoggingToolStripMenuItem,
             this.openLogToolStripMenuItem,
-            this.memoryVisualizerToolStripMenuItem});
+            this.memoryVisualizerToolStripMenuItem,
+            this.soundVisualizerToolStripMenuItem});
             this.logToolStripMenuItem.Name = "logToolStripMenuItem";
             this.logToolStripMenuItem.Size = new System.Drawing.Size(54, 20);
             this.logToolStripMenuItem.Text = "Debug";
             this.openLogToolStripMenuItem.Text = "Open Log...";
             this.openLogToolStripMenuItem.Click += new System.EventHandler(this.openLogToolStripMenuItem_Click);
             // 
+            // memoryVisualizerToolStripMenuItem
+            // 
+            this.memoryVisualizerToolStripMenuItem.Name = "memoryVisualizerToolStripMenuItem";
+            this.memoryVisualizerToolStripMenuItem.Size = new System.Drawing.Size(191, 22);
+            this.memoryVisualizerToolStripMenuItem.Text = "Memory Visualizer...";
+            this.memoryVisualizerToolStripMenuItem.Click += new System.EventHandler(this.memoryVisualizerToolStripMenuItem_Click);
+            // 
             // helpToolStripMenuItem
             // 
             this.helpToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] {
             this.saveMovie.Filter = "FM2 files|*.fm2";
             this.saveMovie.Title = "Save Movie";
             // 
-            // memoryVisualizerToolStripMenuItem
+            // soundVisualizerToolStripMenuItem
             // 
-            this.memoryVisualizerToolStripMenuItem.Name = "memoryVisualizerToolStripMenuItem";
-            this.memoryVisualizerToolStripMenuItem.Size = new System.Drawing.Size(191, 22);
-            this.memoryVisualizerToolStripMenuItem.Text = "Memory Visualizer...";
-            this.memoryVisualizerToolStripMenuItem.Click += new System.EventHandler(this.memoryVisualizerToolStripMenuItem_Click);
+            this.soundVisualizerToolStripMenuItem.Name = "soundVisualizerToolStripMenuItem";
+            this.soundVisualizerToolStripMenuItem.Size = new System.Drawing.Size(191, 22);
+            this.soundVisualizerToolStripMenuItem.Text = "Sound Visualizer...";
+            this.soundVisualizerToolStripMenuItem.Click += new System.EventHandler(this.soundVisualizerToolStripMenuItem_Click);
             // 
             // Program
             // 
         private System.Windows.Forms.ToolStripMenuItem recordAVSToolStripMenuItem;
         private System.Windows.Forms.ToolStripMenuItem stopAVSToolStripMenuItem;
         private System.Windows.Forms.ToolStripMenuItem memoryVisualizerToolStripMenuItem;
+        private System.Windows.Forms.ToolStripMenuItem soundVisualizerToolStripMenuItem;
     }
 }

File DirectXEmu/Program.cs

View file
  • Ignore whitespace
         PatternTablePreview patternTablePreview;
         MemoryViewer memoryViewer;
         Debugger debugger;
-        private MemoryVis memoryVis;
+        SoundVis soundVis;
+        MemoryVis memoryVis;
         EmuConfig config;
 
         SoundVolume volume;
                 cpu.PPU.generateLine = this.nameTablePreview.UpdateNameTables(cpu.PPU.nameTables);
                 cpu.PPU.generateNameTables = true;
             }
+            if (soundVis != null && soundVis.Visible)
+                soundVis.Update(cpu);
+            if (memoryVis != null && memoryVis.Visible)
+                memoryVis.Update(cpu);
+        }
 
-        }
         public void Initialize()
         {
             this.appPath = Path.GetDirectoryName(Application.ExecutablePath);
                 debugger.Close();
             debugger = new Debugger(cpu.debug);
             debugger.UpdateDebug();
-            if (memoryVis != null && memoryVis.Visible)
-            {
-                Point loc = memoryVis.Location;
-                memoryVis.Close();
-                memoryVis = new MemoryVis(cpu.debug);
-                memoryVis.Show();
-                memoryVis.Location = loc;
-                this.BringToFront();
-            }
             ejectDiskToolStripMenuItem.DropDownItems.Clear();
             ejectDiskToolStripMenuItem.Text = "Eject Disk";
             ejectDiskToolStripMenuItem.Visible = (cpu.GetSideCount() != 0);
             input.InputEvent += new InputHandler(input_InputEvent);
             input.InputScalerEvent += new InputScalerHandler(input_InputScalerEvent);
 
-
         }
 
         void input_InputScalerEvent(EmuKeys key, int value)
 
         private void memoryVisualizerToolStripMenuItem_Click(object sender, EventArgs e)
         {
-            if(cpu != null)
+            if (memoryVis == null || memoryVis.Visible == false)
             {
-                if (memoryVis == null || !memoryVis.Visible)
-                    memoryVis = new MemoryVis(cpu.debug);
+                memoryVis = new MemoryVis();
                 memoryVis.Show();
             }
+            else
+                memoryVis.BringToFront();
+        }
+
+        private void soundVisualizerToolStripMenuItem_Click(object sender, EventArgs e)
+        {
+            if (soundVis == null || soundVis.Visible == false)
+            {
+                soundVis = new SoundVis();
+                soundVis.Show();
+            }
+            else
+                soundVis.BringToFront();
         }
 
     }

File DirectXEmu/SoundVis.Designer.cs

View file
  • Ignore whitespace
+namespace DirectXEmu
+{
+    partial class SoundVis
+    {
+        /// <summary>
+        /// Required designer variable.
+        /// </summary>
+        private System.ComponentModel.IContainer components = null;
+
+        /// <summary>
+        /// Clean up any resources being used.
+        /// </summary>
+        /// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
+        protected override void Dispose(bool disposing)
+        {
+            if (disposing && (components != null))
+            {
+                components.Dispose();
+            }
+            base.Dispose(disposing);
+        }
+
+        #region Windows Form Designer generated code
+
+        /// <summary>
+        /// Required method for Designer support - do not modify
+        /// the contents of this method with the code editor.
+        /// </summary>
+        private void InitializeComponent()
+        {
+            this.pnlScreen = new System.Windows.Forms.Panel();
+            this.SuspendLayout();
+            // 
+            // pnlScreen
+            // 
+            this.pnlScreen.Location = new System.Drawing.Point(12, 12);
+            this.pnlScreen.Name = "pnlScreen";
+            this.pnlScreen.Size = new System.Drawing.Size(384, 256);
+            this.pnlScreen.TabIndex = 0;
+            this.pnlScreen.MouseUp += new System.Windows.Forms.MouseEventHandler(this.pnlScreen_MouseUp);
+            // 
+            // SoundVis
+            // 
+            this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
+            this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
+            this.ClientSize = new System.Drawing.Size(407, 277);
+            this.Controls.Add(this.pnlScreen);
+            this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.Fixed3D;
+            this.MaximizeBox = false;
+            this.Name = "SoundVis";
+            this.ShowIcon = false;
+            this.Text = "Sound Visualizer";
+            this.ResumeLayout(false);
+
+        }
+
+        #endregion
+
+        private System.Windows.Forms.Panel pnlScreen;
+    }
+}

File DirectXEmu/SoundVis.cs

View file
  • Ignore whitespace
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.Linq;
+using System.Text;
+using System.Windows.Forms;
+using EmuoTron;
+
+namespace DirectXEmu
+{
+    public partial class SoundVis : Form
+    {
+        Bitmap screenBuffer = new Bitmap(384, 256, PixelFormat.Format32bppArgb);
+        private Graphics screenGfx;
+        private uint[] chanColors;
+
+        private const uint blue = 0xFF3333FF;
+        private const uint darkBlue = 0xFF3333FE;
+        private const uint grey = 0xFF333333;
+
+
+        private NESCore nes;
+
+        public SoundVis()
+        {
+            InitializeComponent();
+            screenGfx = pnlScreen.CreateGraphics();
+            chanColors = new uint[6];
+            for (int i = 0; i < 6; i++)
+                chanColors[i] = blue;
+        }
+
+        public unsafe void Update(NESCore nes)
+        {
+            this.nes = nes;
+            if (nes.APU.volume.square1 != 0)
+                chanColors[0] = blue;
+            else
+                chanColors[0] = grey;
+            if (nes.APU.volume.square2 != 0)
+                chanColors[1] = blue;
+            else
+                chanColors[1] = grey;
+            if (nes.APU.volume.triangle != 0)
+                chanColors[2] = blue;
+            else
+                chanColors[2] = grey;
+            if (nes.APU.volume.noise != 0)
+                chanColors[3] = blue;
+            else
+                chanColors[3] = grey;
+            if (nes.APU.volume.dmc != 0)
+                chanColors[4] = blue;
+            else
+                chanColors[4] = grey;
+            if (nes.APU.volume.external != 0)
+                chanColors[5] = blue;
+            else
+                chanColors[5] = grey;
+
+            int blueCount = 0;
+            int blueItem = -1;
+            for(int i = 0; i < 6; i++)
+                if(chanColors[i] == blue)
+                {
+                    blueItem = i;
+                    blueCount++;
+                }
+            if (blueCount == 1)
+                chanColors[blueItem] = darkBlue;
+
+            var bmd = screenBuffer.LockBits(new Rectangle(0, 0, 384, 256), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
+            uint* ptr = (uint*) bmd.Scan0;
+            for (int i = 0; i < 384 * 256; i++)
+                ptr[i] = 0xFF000000;
+            Point lastPoint;
+            for (int channel = 0; channel < 6; channel++)
+            {
+                switch (channel)
+                {
+                    case 0:
+                    case 1:
+                    case 2:
+                    case 3:
+                        byte[] volume;
+                        switch (channel)
+                        {
+                            default:
+                            case 0:
+                                volume = nes.debug.square1History;
+                                break;
+                            case 1:
+                                volume = nes.debug.square2History;
+                                break;
+                            case 2:
+                                volume = nes.debug.triangleHistory;
+                                break;
+                            case 3:
+                                volume = nes.debug.noiseHistory;
+                                break;
+                        }
+                        lastPoint = new Point(-1,0);
+                        for (int x = 0; x < 128; x++)
+                        {
+                            Point newPoint = new Point(x + ((channel % 3) * 128), ((channel / 3) * 128) + ((15 - volume[x]) * 8));
+
+                            if (channel != 2)
+                            {
+                                if (lastPoint.X != -1)
+                                {
+                                    DrawLine(lastPoint.X + 1, lastPoint.Y, newPoint.X, newPoint.Y, ptr,
+                                                chanColors[channel]);
+                                }
+                                else
+                                {
+                                    DrawPixel(ptr, newPoint.X, newPoint.Y, chanColors[channel]);
+                                }
+                            }
+                            else if (lastPoint.X != -1)
+                            {
+                                DrawLine(lastPoint.X, lastPoint.Y, newPoint.X, newPoint.Y, ptr,
+                                         chanColors[channel]);
+                            }
+                            lastPoint = newPoint;
+                        }
+                        break;
+                    case 4:
+                        lastPoint = new Point(-1, 0);
+                        for (int x = 0; x < 128; x++)
+                        {
+                            Point newPoint = new Point(x + ((channel % 3) * 128), ((channel / 3) * 128) + ((127 - nes.debug.dmcHistory[x])));
+                            if (lastPoint.X != -1)
+                            {
+                                DrawLine(lastPoint.X, lastPoint.Y, newPoint.X, newPoint.Y, ptr, chanColors[channel]);
+                            }
+                            lastPoint = newPoint;
+                        }
+                        break;
+                    case 5:
+                        lastPoint = new Point(-1, 0);
+                        for (int x = 0; x < 128; x++)
+                        {
+                            Point newPoint = new Point(x + ((channel % 3) * 128), ((channel / 3) * 128) + ((127 - (nes.debug.externalHistory[x] >> 1))));
+                            if (lastPoint.X != -1)
+                            {
+                                DrawLine(lastPoint.X, lastPoint.Y, newPoint.X, newPoint.Y, ptr, chanColors[channel]);
+                            }
+                            lastPoint = newPoint;
+                        }
+                        break;
+                }
+            }
+            screenBuffer.UnlockBits(bmd);
+            screenGfx.DrawImageUnscaled(screenBuffer, 0,0);
+        }
+
+        private int frame;
+        public void DumpImage()
+        {
+            screenBuffer.Save("soundDump"+ frame.ToString("D4")+".bmp", ImageFormat.Bmp);
+            frame++;
+        }
+
+        private static unsafe void DrawLine(int x0, int y0, int x1, int y1, uint* ptr, uint color)
+        {
+            var dx = Math.Abs(x1 - x0);
+            var dy = Math.Abs(y1 - y0);
+
+            int sx;
+            int sy;
+
+            if (x0 < x1)
+                sx = 1;
+            else
+                sx = -1;
+            if (y0 < y1)
+                sy = 1;
+            else
+                sy = -1;
+
+            var err = dx - dy;
+
+            while (true)
+            {
+                DrawPixel(ptr, x0, y0, color);
+                if (x0 == x1 && y0 == y1)
+                    break;
+                var e2 = err * 2;
+                if (e2 > dy * -1)
+                {
+                    err = err - dy;
+                    x0 += sx;
+                }
+                if (e2 >= dx) continue;
+                err = err + dx;
+                y0 += sy;
+            }
+        }
+        private static unsafe void DrawPixel(uint* ptr, int x, int y, uint color)
+        {
+            ptr[(y*384) + x] = color;
+        }
+
+        private void pnlScreen_MouseUp(object sender, MouseEventArgs e)
+        {
+            if(!this.Focused)
+                return;
+            int channel = e.X/128+ ((e.Y/128) * 3);
+            switch (chanColors[channel])
+            {
+                case darkBlue:
+                    for (int i = 0; i < 6; i++)
+                        chanColors[i] = blue;
+                    nes.APU.volume.square1 = 1;
+                    nes.APU.volume.square2 = 1;
+                    nes.APU.volume.triangle = 1;
+                    nes.APU.volume.noise = 1;
+                    nes.APU.volume.dmc = 1;
+                    nes.APU.volume.external = 1;
+                    break;
+                case grey:
+                case blue:
+                    for (int i = 0; i < 6; i++)
+                        chanColors[i] = grey;
+                    chanColors[channel] = darkBlue;
+                    nes.APU.volume.square1 = 0;
+                    nes.APU.volume.square2 = 0;
+                    nes.APU.volume.triangle = 0;
+                    nes.APU.volume.noise = 0;
+                    nes.APU.volume.dmc = 0;
+                    nes.APU.volume.external = 0;
+                    switch (channel)
+                    {
+                        case 0:
+                            nes.APU.volume.square1 = 1;
+                            break;
+                        case 1:
+                            nes.APU.volume.square2 = 1;
+                            break;
+                        case 2:
+                            nes.APU.volume.triangle = 1;
+                            break;
+                        case 3:
+                            nes.APU.volume.noise = 1;
+                            break;
+                        case 4:
+                            nes.APU.volume.dmc = 1;
+                            break;
+                        case 5:
+                            nes.APU.volume.external = 1;
+                            break;
+                    }
+                    break;
+
+            }
+        }
+    }
+}

File DirectXEmu/SoundVis.resx

View file
  • Ignore whitespace
+<?xml version="1.0" encoding="utf-8"?>
+<root>
+  <!-- 
+    Microsoft ResX Schema 
+    
+    Version 2.0
+    
+    The primary goals of this format is to allow a simple XML format 
+    that is mostly human readable. The generation and parsing of the 
+    various data types are done through the TypeConverter classes 
+    associated with the data types.
+    
+    Example:
+    
+    ... ado.net/XML headers & schema ...
+    <resheader name="resmimetype">text/microsoft-resx</resheader>
+    <resheader name="version">2.0</resheader>
+    <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
+    <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
+    <data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
+    <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
+    <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
+        <value>[base64 mime encoded serialized .NET Framework object]</value>
+    </data>
+    <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
+        <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
+        <comment>This is a comment</comment>
+    </data>
+                
+    There are any number of "resheader" rows that contain simple 
+    name/value pairs.
+    
+    Each data row contains a name, and value. The row also contains a 
+    type or mimetype. Type corresponds to a .NET class that support 
+    text/value conversion through the TypeConverter architecture. 
+    Classes that don't support this are serialized and stored with the 
+    mimetype set.
+    
+    The mimetype is used for serialized objects, and tells the 
+    ResXResourceReader how to depersist the object. This is currently not 
+    extensible. For a given mimetype the value must be set accordingly:
+    
+    Note - application/x-microsoft.net.object.binary.base64 is the format 
+    that the ResXResourceWriter will generate, however the reader can 
+    read any of the formats listed below.
+    
+    mimetype: application/x-microsoft.net.object.binary.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
+            : and then encoded with base64 encoding.
+    
+    mimetype: application/x-microsoft.net.object.soap.base64
+    value   : The object must be serialized with 
+            : System.Runtime.Serialization.Formatters.Soap.SoapFormatter
+            : and then encoded with base64 encoding.
+
+    mimetype: application/x-microsoft.net.object.bytearray.base64
+    value   : The object must be serialized into a byte array 
+            : using a System.ComponentModel.TypeConverter
+            : and then encoded with base64 encoding.
+    -->
+  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
+    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
+    <xsd:element name="root" msdata:IsDataSet="true">
+      <xsd:complexType>
+        <xsd:choice maxOccurs="unbounded">
+          <xsd:element name="metadata">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" />
+              </xsd:sequence>
+              <xsd:attribute name="name" use="required" type="xsd:string" />
+              <xsd:attribute name="type" type="xsd:string" />
+              <xsd:attribute name="mimetype" type="xsd:string" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="assembly">
+            <xsd:complexType>
+              <xsd:attribute name="alias" type="xsd:string" />
+              <xsd:attribute name="name" type="xsd:string" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="data">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
+              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
+              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
+              <xsd:attribute ref="xml:space" />
+            </xsd:complexType>
+          </xsd:element>
+          <xsd:element name="resheader">
+            <xsd:complexType>
+              <xsd:sequence>
+                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
+              </xsd:sequence>
+              <xsd:attribute name="name" type="xsd:string" use="required" />
+            </xsd:complexType>
+          </xsd:element>
+        </xsd:choice>
+      </xsd:complexType>
+    </xsd:element>
+  </xsd:schema>
+  <resheader name="resmimetype">
+    <value>text/microsoft-resx</value>
+  </resheader>
+  <resheader name="version">
+    <value>2.0</value>
+  </resheader>
+  <resheader name="reader">
+    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+  <resheader name="writer">
+    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
+  </resheader>
+</root>