Added basic audio settings

This commit is contained in:
Souryo 2015-07-17 20:58:57 -04:00
parent d70010f324
commit 266ca82a06
38 changed files with 1074 additions and 114 deletions

View file

@ -7,6 +7,7 @@
#include "NoiseChannel.h"
#include "DeltaModulationChannel.h"
#include "ApuFrameCounter.h"
#include "EmulationSettings.h"
APU* APU::Instance = nullptr;
IAudioDevice* APU::AudioDevice = nullptr;
@ -16,17 +17,17 @@ APU::APU(MemoryManager* memoryManager)
APU::Instance = this;
_memoryManager = memoryManager;
_blipBuffer = new Blip_Buffer();
_blipBuffer.reset(new Blip_Buffer());
_blipBuffer->sample_rate(APU::SampleRate);
_blipBuffer->clock_rate(CPU::ClockRate);
_outputBuffer = new int16_t[APU::SamplesPerFrame];
_squareChannel.push_back(unique_ptr<SquareChannel>(new SquareChannel(_blipBuffer, true)));
_squareChannel.push_back(unique_ptr<SquareChannel>(new SquareChannel(_blipBuffer, false)));
_triangleChannel.reset(new TriangleChannel(_blipBuffer));
_noiseChannel.reset(new NoiseChannel(_blipBuffer));
_deltaModulationChannel.reset(new DeltaModulationChannel(_blipBuffer, _memoryManager));
_squareChannel.push_back(unique_ptr<SquareChannel>(new SquareChannel(AudioChannel::Square1, _blipBuffer.get(), true)));
_squareChannel.push_back(unique_ptr<SquareChannel>(new SquareChannel(AudioChannel::Square2, _blipBuffer.get(), false)));
_triangleChannel.reset(new TriangleChannel(AudioChannel::Triangle, _blipBuffer.get()));
_noiseChannel.reset(new NoiseChannel(AudioChannel::Noise, _blipBuffer.get()));
_deltaModulationChannel.reset(new DeltaModulationChannel(AudioChannel::DMC, _blipBuffer.get(), _memoryManager));
_frameCounter.reset(new ApuFrameCounter(&APU::FrameCounterTick));
_memoryManager->RegisterIODevice(_squareChannel[0].get());
@ -146,7 +147,7 @@ void APU::ExecStatic()
void APU::Exec()
{
_currentCycle++;
if(_currentCycle == 20000) {
if(_currentCycle == 10000) {
Run();
_squareChannel[0]->EndFrame();
@ -158,7 +159,6 @@ void APU::Exec()
_blipBuffer->end_frame(_currentCycle);
// Read some samples out of Blip_Buffer if there are enough to fill our output buffer
uint32_t availableSampleCount = _blipBuffer->samples_avail();
size_t sampleCount = _blipBuffer->read_samples(_outputBuffer, APU::SamplesPerFrame);
if(APU::AudioDevice) {
APU::AudioDevice->PlayBuffer(_outputBuffer, (uint32_t)(sampleCount * BitsPerSample / 8));

View file

@ -30,7 +30,7 @@ class APU : public Snapshotable, public IMemoryHandler
unique_ptr<ApuFrameCounter> _frameCounter;
Blip_Buffer* _blipBuffer;
unique_ptr<Blip_Buffer> _blipBuffer;
int16_t* _outputBuffer;
MemoryManager* _memoryManager;

View file

@ -15,7 +15,7 @@ private:
uint8_t _counter = 0;
protected:
ApuEnvelope(Blip_Buffer* buffer) : ApuLengthCounter(buffer)
ApuEnvelope(AudioChannel channel, Blip_Buffer* buffer) : ApuLengthCounter(channel, buffer)
{
}

View file

@ -25,7 +25,7 @@ protected:
}
public:
ApuLengthCounter(Blip_Buffer* buffer) : BaseApuChannel(buffer)
ApuLengthCounter(AudioChannel channel, Blip_Buffer* buffer) : BaseApuChannel(channel, buffer)
{
}

View file

@ -2,6 +2,7 @@
#include "stdafx.h"
#include "IMemoryHandler.h"
#include "../BlipBuffer/Blip_Buffer.h"
#include "EmulationSettings.h"
template<int range>
class BaseApuChannel : public IMemoryHandler, public Snapshotable
@ -11,18 +12,32 @@ private:
uint16_t _lastOutput = 0;
uint32_t _previousCycle = 0;
Blip_Buffer *_buffer;
AudioChannel _channel;
double _baseVolume;
protected:
uint16_t _timer = 0;
uint16_t _period = 0;
uint32_t _clockDivider = 2; //All channels except triangle clock overy other cpu clock
void SetVolume(double volume)
{
_baseVolume = volume;
UpdateSynthVolume();
}
void UpdateSynthVolume()
{
_synth->volume(_baseVolume * EmulationSettings::GetChannelVolume(_channel) * 2);
}
public:
virtual void Clock() = 0;
virtual bool GetStatus() = 0;
BaseApuChannel(Blip_Buffer *buffer)
BaseApuChannel(AudioChannel channel, Blip_Buffer *buffer)
{
_channel = channel;
_buffer = buffer;
_synth.reset(new Blip_Synth<blip_good_quality, range>());
@ -50,11 +65,6 @@ public:
}
}
void SetVolume(double volume)
{
_synth->volume(volume);
}
virtual void Run(uint32_t targetCycle)
{
while(_previousCycle < targetCycle) {
@ -91,5 +101,8 @@ public:
void EndFrame()
{
_previousCycle = 0;
//Update options at the end of the cycle
UpdateSynthVolume();
}
};

View file

@ -4,12 +4,12 @@
#include "BaseMapper.h"
#include "MapperFactory.h"
#include "Debugger.h"
#include "MessageManager.h"
#include "EmulationSettings.h"
#include "../Utilities/Timer.h"
#include "../Utilities/FolderUtilities.h"
#include "../Core/MessageManager.h"
shared_ptr<Console> Console::Instance(new Console());
uint32_t Console::Flags = 0;
Console::Console()
{
@ -127,7 +127,7 @@ void Console::ResetComponents(bool softReset)
void Console::Stop()
{
_stop = true;
Console::ClearFlags(EmulationFlags::Paused);
EmulationSettings::ClearFlags(EmulationFlags::Paused);
_stopLock.Acquire();
_stopLock.Release();
}
@ -145,26 +145,11 @@ void Console::Resume()
Console::Instance->_pauseLock.Release();
}
void Console::SetFlags(int flags)
{
Console::Flags |= flags;
}
void Console::ClearFlags(int flags)
{
Console::Flags &= ~flags;
}
bool Console::CheckFlag(int flag)
{
return (Console::Flags & flag) == flag;
}
void Console::Run()
{
Timer clockTimer;
double elapsedTime = 0;
double targetTime = 16.6666666666666666;
double targetTime = 16.63926405550947; //~60.0988fps
_runLock.Acquire();
_stopLock.Acquire();
@ -177,7 +162,7 @@ void Console::Run()
lastFrameNumber = currentFrameNumber;
_cpu->EndFrame();
if(CheckFlag(EmulationFlags::LimitFPS)) {
if(EmulationSettings::CheckFlag(EmulationFlags::LimitFPS)) {
elapsedTime = clockTimer.GetElapsedMS();
while(targetTime > elapsedTime) {
if(targetTime - elapsedTime > 2) {
@ -197,14 +182,14 @@ void Console::Run()
_runLock.Acquire();
}
if(CheckFlag(EmulationFlags::Paused) && !_stop) {
if(EmulationSettings::CheckFlag(EmulationFlags::Paused) && !_stop) {
MessageManager::SendNotification(ConsoleNotificationType::GamePaused);
_runLock.Release();
//Prevent audio from looping endlessly while game is paused
_apu->StopAudio();
while(CheckFlag(EmulationFlags::Paused)) {
while(EmulationSettings::CheckFlag(EmulationFlags::Paused)) {
//Sleep until emulation is resumed
std::this_thread::sleep_for(std::chrono::duration<int, std::milli>(100));
}

View file

@ -8,12 +8,6 @@
#include "ControlManager.h"
#include "../Utilities/SimpleLock.h"
enum EmulationFlags
{
LimitFPS = 0x01,
Paused = 0x02,
};
class Debugger;
class BaseMapper;
@ -21,7 +15,6 @@ class Console
{
private:
static shared_ptr<Console> Instance;
static uint32_t Flags;
SimpleLock _pauseLock;
SimpleLock _runLock;
SimpleLock _stopLock;
@ -67,9 +60,5 @@ class Console
static string FindMatchingRomInFolder(string folder, string romFilename, uint32_t crc32Hash);
static string GetROMPath();
static bool CheckFlag(int flag);
static void SetFlags(int flags);
static void ClearFlags(int flags);
static shared_ptr<Console> GetInstance();
};

View file

@ -282,6 +282,7 @@
<ClInclude Include="Debugger.h" />
<ClInclude Include="Disassembler.h" />
<ClInclude Include="DisassemblyInfo.h" />
<ClInclude Include="EmulationSettings.h" />
<ClInclude Include="GameClient.h" />
<ClInclude Include="GameClientConnection.h" />
<ClInclude Include="GameConnection.h" />
@ -336,6 +337,7 @@
<ClCompile Include="Debugger.cpp" />
<ClCompile Include="Disassembler.cpp" />
<ClCompile Include="DisassemblyInfo.cpp" />
<ClCompile Include="EmulationSettings.cpp" />
<ClCompile Include="GameClient.cpp" />
<ClCompile Include="GameClientConnection.cpp" />
<ClCompile Include="GameConnection.cpp" />

View file

@ -218,6 +218,9 @@
<ClInclude Include="DeltaModulationChannel.h">
<Filter>Header Files\APU</Filter>
</ClInclude>
<ClInclude Include="EmulationSettings.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="CPU.cpp">
@ -289,5 +292,8 @@
<ClCompile Include="APU.cpp">
<Filter>Source Files\APU</Filter>
</ClCompile>
<ClCompile Include="EmulationSettings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View file

@ -86,7 +86,7 @@ protected:
}
public:
DeltaModulationChannel(Blip_Buffer *buffer, MemoryManager* memoryManager) : BaseApuChannel(buffer)
DeltaModulationChannel(AudioChannel channel, Blip_Buffer *buffer, MemoryManager* memoryManager) : BaseApuChannel(channel, buffer)
{
_memoryManager = memoryManager;
_clockDivider = 1;

View file

@ -0,0 +1,6 @@
#include "stdafx.h"
#include "EmulationSettings.h"
uint32_t EmulationSettings::Flags = 0;
uint32_t EmulationSettings::AudioLatency = 20000;
double EmulationSettings::ChannelVolume[5] = { 0.5f, 0.5f, 0.5f, 0.5f, 0.5f };

63
Core/EmulationSettings.h Normal file
View file

@ -0,0 +1,63 @@
#pragma once
#include "stdafx.h"
enum EmulationFlags
{
LimitFPS = 0x01,
Paused = 0x02,
};
enum class AudioChannel
{
Square1 = 0,
Square2 = 1,
Triangle = 2,
Noise = 3,
DMC = 4
};
class EmulationSettings
{
private:
static uint32_t Flags;
static uint32_t AudioLatency;
static double ChannelVolume[5];
public:
static void SetFlags(uint32_t flags)
{
Flags |= flags;
}
static void ClearFlags(uint32_t flags)
{
Flags &= ~flags;
}
static bool CheckFlag(uint32_t flag)
{
return (Flags & flag) == flag;
}
//0: Muted, 0.5: Default, 1.0: Max volume
static void SetChannelVolume(AudioChannel channel, double volume)
{
ChannelVolume[(int)channel] = volume;
}
static void SetAudioLatency(uint32_t msLatency)
{
AudioLatency = msLatency;
}
static double GetChannelVolume(AudioChannel channel)
{
return ChannelVolume[(int)channel];
}
static uint32_t GetAudioLatency()
{
return AudioLatency;
}
};

View file

@ -7,6 +7,7 @@
#include "GameInformationMessage.h"
#include "SaveStateMessage.h"
#include "Console.h"
#include "EmulationSettings.h"
#include "ControlManager.h"
#include "VirtualController.h"
#include "ClientConnectionData.h"
@ -84,9 +85,9 @@ void GameClientConnection::ProcessMessage(NetMessage* message)
_gameLoaded = gameInfo->AttemptLoadGame();
if(gameInfo->IsPaused()) {
Console::SetFlags(EmulationFlags::Paused);
EmulationSettings::SetFlags(EmulationFlags::Paused);
} else {
Console::ClearFlags(EmulationFlags::Paused);
EmulationSettings::ClearFlags(EmulationFlags::Paused);
}
break;

View file

@ -10,6 +10,7 @@
#include "Console.h"
#include "ControlManager.h"
#include "ClientConnectionData.h"
#include "EmulationSettings.h"
GameServerConnection::GameServerConnection(shared_ptr<Socket> socket, int controllerPort, IGameBroadcaster* gameBroadcaster) : GameConnection(socket, nullptr)
{
@ -54,7 +55,7 @@ void GameServerConnection::SendGameState()
void GameServerConnection::SendGameInformation()
{
SendNetMessage(GameInformationMessage(Console::GetROMPath(), _controllerPort, Console::CheckFlag(EmulationFlags::Paused)));
SendNetMessage(GameInformationMessage(Console::GetROMPath(), _controllerPort, EmulationSettings::CheckFlag(EmulationFlags::Paused)));
}
void GameServerConnection::SendMovieData(uint8_t state, uint8_t port)

View file

@ -37,7 +37,7 @@ protected:
}
public:
NoiseChannel(Blip_Buffer* buffer) : ApuEnvelope(buffer)
NoiseChannel(AudioChannel channel, Blip_Buffer* buffer) : ApuEnvelope(channel, buffer)
{
SetVolume(0.0741);
}

View file

@ -77,7 +77,7 @@ protected:
}
public:
SquareChannel(Blip_Buffer *buffer, bool isChannel1) : ApuEnvelope(buffer)
SquareChannel(AudioChannel channel, Blip_Buffer *buffer, bool isChannel1) : ApuEnvelope(channel, buffer)
{
SetVolume(0.1128);
_isChannel1 = isChannel1;

View file

@ -29,7 +29,7 @@ protected:
}
public:
TriangleChannel(Blip_Buffer* buffer) : ApuLengthCounter(buffer)
TriangleChannel(AudioChannel channel, Blip_Buffer* buffer) : ApuLengthCounter(channel, buffer)
{
_clockDivider = 1; //Triangle clocks at the same speed as the cpu
SetVolume(0.12765);

View file

@ -3,6 +3,7 @@
#include "VirtualController.h"
#include "ControlManager.h"
#include "Console.h"
#include "EmulationSettings.h"
VirtualController::VirtualController(uint8_t port)
{
@ -44,9 +45,9 @@ ButtonState VirtualController::GetButtonState()
_queueSize--;
if(_queueSize.load() > _minimumBuffer) {
Console::ClearFlags(EmulationFlags::LimitFPS);
EmulationSettings::ClearFlags(EmulationFlags::LimitFPS);
} else {
Console::SetFlags(EmulationFlags::LimitFPS);
EmulationSettings::SetFlags(EmulationFlags::LimitFPS);
}
_writeLock.Release();

View file

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Mesen.GUI.Config
{
public class AudioInfo
{
public bool EnableAudio = true;
public UInt32 AudioLatency = 150;
public UInt32 MasterVolume = 50;
public UInt32 Square1Volume = 50;
public UInt32 Square2Volume = 50;
public UInt32 TriangleVolume = 50;
public UInt32 NoiseVolume = 50;
public UInt32 DmcVolume = 50;
public AudioInfo()
{
}
static private double ConvertVolume(UInt32 volume)
{
if(ConfigManager.Config.AudioInfo.EnableAudio) {
return ((double)volume / 100d) * (double)ConfigManager.Config.AudioInfo.MasterVolume * 2 / 100d;
} else {
return 0;
}
}
static public void ApplyConfig()
{
AudioInfo audioInfo = ConfigManager.Config.AudioInfo;
InteropEmu.SetAudioLatency(audioInfo.AudioLatency);
InteropEmu.SetChannelVolume(0, ConvertVolume(audioInfo.Square1Volume));
InteropEmu.SetChannelVolume(1, ConvertVolume(audioInfo.Square2Volume));
InteropEmu.SetChannelVolume(2, ConvertVolume(audioInfo.TriangleVolume));
InteropEmu.SetChannelVolume(3, ConvertVolume(audioInfo.NoiseVolume));
InteropEmu.SetChannelVolume(4, ConvertVolume(audioInfo.DmcVolume));
}
}
}

View file

@ -15,6 +15,7 @@ namespace Mesen.GUI.Config
public PlayerProfile Profile;
public ClientConnectionInfo ClientConnectionInfo;
public ServerInfo ServerInfo;
public AudioInfo AudioInfo;
public List<string> RecentFiles;
public List<CheatInfo> Cheats;
public List<ControllerInfo> Controllers;
@ -25,6 +26,7 @@ namespace Mesen.GUI.Config
Profile = new PlayerProfile();
ClientConnectionInfo = new ClientConnectionInfo();
ServerInfo = new ServerInfo();
AudioInfo = new AudioInfo();
RecentFiles = new List<string>();
Controllers = new List<ControllerInfo>();
}

117
GUI.NET/Controls/ctrlTrackbar.Designer.cs generated Normal file
View file

@ -0,0 +1,117 @@
namespace Mesen.GUI.Controls
{
partial class ctrlTrackbar
{
/// <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 Component 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.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.lblText = new System.Windows.Forms.Label();
this.trackBar = new System.Windows.Forms.TrackBar();
this.txtValue = new System.Windows.Forms.TextBox();
this.tableLayoutPanel1.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBar)).BeginInit();
this.SuspendLayout();
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 1;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.Controls.Add(this.lblText, 0, 2);
this.tableLayoutPanel1.Controls.Add(this.trackBar, 0, 0);
this.tableLayoutPanel1.Controls.Add(this.txtValue, 0, 1);
this.tableLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel1.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 3;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel1.Size = new System.Drawing.Size(62, 160);
this.tableLayoutPanel1.TabIndex = 0;
//
// lblText
//
this.lblText.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right)));
this.lblText.AutoSize = true;
this.lblText.Location = new System.Drawing.Point(3, 147);
this.lblText.Name = "lblText";
this.lblText.Size = new System.Drawing.Size(56, 13);
this.lblText.TabIndex = 14;
this.lblText.Text = "Text";
this.lblText.TextAlign = System.Drawing.ContentAlignment.MiddleCenter;
//
// trackBar
//
this.trackBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| System.Windows.Forms.AnchorStyles.Right)));
this.trackBar.Location = new System.Drawing.Point(17, 0);
this.trackBar.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0);
this.trackBar.Maximum = 100;
this.trackBar.Name = "trackBar";
this.trackBar.Orientation = System.Windows.Forms.Orientation.Vertical;
this.trackBar.Size = new System.Drawing.Size(45, 124);
this.trackBar.TabIndex = 13;
this.trackBar.TickFrequency = 10;
this.trackBar.Value = 50;
this.trackBar.ValueChanged += new System.EventHandler(this.trackBar_ValueChanged);
//
// txtValue
//
this.txtValue.Anchor = System.Windows.Forms.AnchorStyles.None;
this.txtValue.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.txtValue.Location = new System.Drawing.Point(15, 127);
this.txtValue.Multiline = true;
this.txtValue.Name = "txtValue";
this.txtValue.Size = new System.Drawing.Size(31, 17);
this.txtValue.TabIndex = 15;
this.txtValue.Text = "100";
this.txtValue.TextAlign = System.Windows.Forms.HorizontalAlignment.Center;
//
// ctrlTrackbar
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.Controls.Add(this.tableLayoutPanel1);
this.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0);
this.MaximumSize = new System.Drawing.Size(63, 160);
this.MinimumSize = new System.Drawing.Size(63, 160);
this.Name = "ctrlTrackbar";
this.Size = new System.Drawing.Size(63, 160);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel1.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.trackBar)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.Label lblText;
private System.Windows.Forms.TrackBar trackBar;
private System.Windows.Forms.TextBox txtValue;
}
}

View file

@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Mesen.GUI.Controls
{
public partial class ctrlTrackbar : UserControl
{
public event EventHandler ValueChanged
{
add { trackBar.ValueChanged += value; }
remove { trackBar.ValueChanged -= value; }
}
public ctrlTrackbar()
{
InitializeComponent();
}
public string Caption
{
get { return lblText.Text; }
set { lblText.Text = value; }
}
public int Value
{
get { return trackBar.Value; }
set
{
trackBar.Value = value;
txtValue.Text = trackBar.Value.ToString() + "%";
}
}
private void trackBar_ValueChanged(object sender, EventArgs e)
{
txtValue.Text = trackBar.Value.ToString() + "%";
}
}
}

View file

@ -0,0 +1,120 @@
<?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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -37,39 +37,19 @@ namespace Mesen.GUI.Forms
/// </summary>
private void InitializeComponent()
{
this.panel1 = new System.Windows.Forms.Panel();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.btnCancel = new System.Windows.Forms.Button();
this.btnOK = new System.Windows.Forms.Button();
this.panel1.SuspendLayout();
this.baseConfigPanel = new System.Windows.Forms.Panel();
this.flowLayoutPanel1 = new System.Windows.Forms.FlowLayoutPanel();
this.baseConfigPanel.SuspendLayout();
this.flowLayoutPanel1.SuspendLayout();
this.SuspendLayout();
//
// panel1
//
this.panel1.Controls.Add(this.flowLayoutPanel1);
this.panel1.Dock = System.Windows.Forms.DockStyle.Bottom;
this.panel1.Location = new System.Drawing.Point(0, 232);
this.panel1.Name = "panel1";
this.panel1.Size = new System.Drawing.Size(284, 30);
this.panel1.TabIndex = 0;
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.Controls.Add(this.btnCancel);
this.flowLayoutPanel1.Controls.Add(this.btnOK);
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel1.FlowDirection = System.Windows.Forms.FlowDirection.RightToLeft;
this.flowLayoutPanel1.Location = new System.Drawing.Point(0, 0);
this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(284, 30);
this.flowLayoutPanel1.TabIndex = 4;
//
// btnCancel
//
this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnCancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
this.btnCancel.Location = new System.Drawing.Point(206, 3);
this.btnCancel.Location = new System.Drawing.Point(84, 3);
this.btnCancel.Name = "btnCancel";
this.btnCancel.Size = new System.Drawing.Size(75, 23);
this.btnCancel.TabIndex = 0;
@ -79,8 +59,9 @@ namespace Mesen.GUI.Forms
//
// btnOK
//
this.btnOK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right)));
this.btnOK.DialogResult = System.Windows.Forms.DialogResult.OK;
this.btnOK.Location = new System.Drawing.Point(125, 3);
this.btnOK.Location = new System.Drawing.Point(3, 3);
this.btnOK.Name = "btnOK";
this.btnOK.Size = new System.Drawing.Size(75, 23);
this.btnOK.TabIndex = 1;
@ -88,14 +69,34 @@ namespace Mesen.GUI.Forms
this.btnOK.UseVisualStyleBackColor = true;
this.btnOK.Click += new System.EventHandler(this.btnOK_Click);
//
// baseConfigPanel
//
this.baseConfigPanel.Controls.Add(this.flowLayoutPanel1);
this.baseConfigPanel.Dock = System.Windows.Forms.DockStyle.Bottom;
this.baseConfigPanel.Location = new System.Drawing.Point(0, 233);
this.baseConfigPanel.Name = "baseConfigPanel";
this.baseConfigPanel.Size = new System.Drawing.Size(327, 29);
this.baseConfigPanel.TabIndex = 1;
//
// flowLayoutPanel1
//
this.flowLayoutPanel1.Controls.Add(this.btnOK);
this.flowLayoutPanel1.Controls.Add(this.btnCancel);
this.flowLayoutPanel1.Dock = System.Windows.Forms.DockStyle.Right;
this.flowLayoutPanel1.Location = new System.Drawing.Point(162, 0);
this.flowLayoutPanel1.Margin = new System.Windows.Forms.Padding(0, 0, 0, 0);
this.flowLayoutPanel1.Name = "flowLayoutPanel1";
this.flowLayoutPanel1.Size = new System.Drawing.Size(165, 29);
this.flowLayoutPanel1.TabIndex = 2;
//
// BaseConfigForm
//
this.AcceptButton = this.btnOK;
this.CancelButton = this.btnCancel;
this.ClientSize = new System.Drawing.Size(284, 262);
this.Controls.Add(this.panel1);
this.ClientSize = new System.Drawing.Size(327, 262);
this.Controls.Add(this.baseConfigPanel);
this.Name = "BaseConfigForm";
this.panel1.ResumeLayout(false);
this.baseConfigPanel.ResumeLayout(false);
this.flowLayoutPanel1.ResumeLayout(false);
this.ResumeLayout(false);
@ -103,9 +104,9 @@ namespace Mesen.GUI.Forms
#endregion
private System.Windows.Forms.Panel panel1;
protected System.Windows.Forms.FlowLayoutPanel flowLayoutPanel1;
protected Button btnCancel;
protected Button btnOK;
protected Panel baseConfigPanel;
private FlowLayoutPanel flowLayoutPanel1;
}
}

View file

@ -6,6 +6,7 @@ using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Config;
using Mesen.GUI.Controls;
namespace Mesen.GUI.Forms
{
@ -15,7 +16,7 @@ namespace Mesen.GUI.Forms
private Dictionary<string, FieldInfo> _fieldInfo = null;
private object _entity;
private Timer _validateTimer;
public BaseConfigForm()
{
InitializeComponent();
@ -26,6 +27,13 @@ namespace Mesen.GUI.Forms
_validateTimer.Start();
}
protected override void OnLoad(EventArgs e)
{
base.OnLoad(e);
UpdateUI();
}
private void OnValidateInput(object sender, EventArgs e)
{
btnOK.Enabled = ValidateInput();
@ -47,6 +55,7 @@ namespace Mesen.GUI.Forms
protected override void OnFormClosed(FormClosedEventArgs e)
{
if(this.DialogResult == System.Windows.Forms.DialogResult.OK) {
UpdateObject();
UpdateConfig();
if(ApplyChangesOnOK) {
ConfigManager.ApplyChanges();
@ -66,6 +75,12 @@ namespace Mesen.GUI.Forms
{
}
protected bool Updating
{
get;
private set;
}
protected object Entity
{
get { return _entity; }
@ -99,6 +114,7 @@ namespace Mesen.GUI.Forms
protected void UpdateUI()
{
this.Updating = true;
foreach(KeyValuePair<string, Control> kvp in _bindings) {
if(!_fieldInfo.ContainsKey(kvp.Key)) {
throw new Exception("Invalid binding key");
@ -122,9 +138,17 @@ namespace Mesen.GUI.Forms
} else {
throw new Exception("No radio button matching value found");
}
} else if(kvp.Value is ctrlTrackbar) {
((ctrlTrackbar)kvp.Value).Value = (int)(uint)value;
} else if(kvp.Value is NumericUpDown) {
NumericUpDown nud = kvp.Value as NumericUpDown;
decimal val = (decimal)(uint)value;
val = Math.Min(Math.Max(val, nud.Minimum), nud.Maximum);
nud.Value = val;
}
}
}
}
this.Updating = false;
}
protected void UpdateObject()
@ -146,9 +170,13 @@ namespace Mesen.GUI.Forms
field.SetValue(Entity, ((CheckBox)kvp.Value).Checked);
} else if(kvp.Value is Panel) {
field.SetValue(Entity, kvp.Value.Controls.OfType<RadioButton>().FirstOrDefault(r => r.Checked).Tag);
} else if(kvp.Value is ctrlTrackbar) {
field.SetValue(Entity, (UInt32)((ctrlTrackbar)kvp.Value).Value);
} else if(kvp.Value is NumericUpDown) {
field.SetValue(Entity, (UInt32)((NumericUpDown)kvp.Value).Value);
}
}
}
}
}
private void btnOK_Click(object sender, EventArgs e)

View file

@ -117,7 +117,4 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="panel1.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
</root>

View file

@ -46,7 +46,6 @@ namespace Mesen.GUI.Forms.Cheats
AddBinding("CompareValue", txtCompare);
AddBinding("IsRelativeAddress", radRelativeAddress.Parent);
UpdateUI();
UpdateOKButton();
}

View file

@ -0,0 +1,310 @@
namespace Mesen.GUI.Forms.Config
{
partial class frmAudioConfig
{
/// <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.grpVolume = new System.Windows.Forms.GroupBox();
this.tableLayoutPanel1 = new System.Windows.Forms.TableLayoutPanel();
this.trkDmcVol = new Mesen.GUI.Controls.ctrlTrackbar();
this.trkNoiseVol = new Mesen.GUI.Controls.ctrlTrackbar();
this.trkTriangleVol = new Mesen.GUI.Controls.ctrlTrackbar();
this.trkSquare2Vol = new Mesen.GUI.Controls.ctrlTrackbar();
this.trkSquare1Vol = new Mesen.GUI.Controls.ctrlTrackbar();
this.trkMaster = new Mesen.GUI.Controls.ctrlTrackbar();
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
this.chkEnableAudio = new System.Windows.Forms.CheckBox();
this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel();
this.lblAudioLatency = new System.Windows.Forms.Label();
this.nudLatency = new System.Windows.Forms.NumericUpDown();
this.lblLatencyMs = new System.Windows.Forms.Label();
this.btnReset = new System.Windows.Forms.Button();
this.baseConfigPanel.SuspendLayout();
this.grpVolume.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
this.tableLayoutPanel2.SuspendLayout();
this.flowLayoutPanel2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.nudLatency)).BeginInit();
this.SuspendLayout();
//
// baseConfigPanel
//
this.baseConfigPanel.Controls.Add(this.btnReset);
this.baseConfigPanel.Location = new System.Drawing.Point(0, 245);
this.baseConfigPanel.Size = new System.Drawing.Size(470, 29);
this.baseConfigPanel.Controls.SetChildIndex(this.btnReset, 0);
//
// grpVolume
//
this.grpVolume.Controls.Add(this.tableLayoutPanel1);
this.grpVolume.Location = new System.Drawing.Point(3, 29);
this.grpVolume.Name = "grpVolume";
this.grpVolume.Size = new System.Drawing.Size(462, 185);
this.grpVolume.TabIndex = 2;
this.grpVolume.TabStop = false;
this.grpVolume.Text = "Volume";
//
// tableLayoutPanel1
//
this.tableLayoutPanel1.ColumnCount = 6;
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F));
this.tableLayoutPanel1.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 16.66667F));
this.tableLayoutPanel1.Controls.Add(this.trkDmcVol, 5, 0);
this.tableLayoutPanel1.Controls.Add(this.trkNoiseVol, 4, 0);
this.tableLayoutPanel1.Controls.Add(this.trkTriangleVol, 3, 0);
this.tableLayoutPanel1.Controls.Add(this.trkSquare2Vol, 2, 0);
this.tableLayoutPanel1.Controls.Add(this.trkSquare1Vol, 1, 0);
this.tableLayoutPanel1.Controls.Add(this.trkMaster, 0, 0);
this.tableLayoutPanel1.Location = new System.Drawing.Point(6, 19);
this.tableLayoutPanel1.Name = "tableLayoutPanel1";
this.tableLayoutPanel1.RowCount = 1;
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel1.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 160F));
this.tableLayoutPanel1.Size = new System.Drawing.Size(451, 160);
this.tableLayoutPanel1.TabIndex = 2;
//
// trkDmcVol
//
this.trkDmcVol.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.trkDmcVol.Caption = "DMC";
this.trkDmcVol.Location = new System.Drawing.Point(381, 0);
this.trkDmcVol.Margin = new System.Windows.Forms.Padding(0);
this.trkDmcVol.MaximumSize = new System.Drawing.Size(63, 160);
this.trkDmcVol.MinimumSize = new System.Drawing.Size(63, 160);
this.trkDmcVol.Name = "trkDmcVol";
this.trkDmcVol.Size = new System.Drawing.Size(63, 160);
this.trkDmcVol.TabIndex = 16;
this.trkDmcVol.Value = 50;
//
// trkNoiseVol
//
this.trkNoiseVol.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.trkNoiseVol.Caption = "Noise";
this.trkNoiseVol.Location = new System.Drawing.Point(306, 0);
this.trkNoiseVol.Margin = new System.Windows.Forms.Padding(0);
this.trkNoiseVol.MaximumSize = new System.Drawing.Size(63, 160);
this.trkNoiseVol.MinimumSize = new System.Drawing.Size(63, 160);
this.trkNoiseVol.Name = "trkNoiseVol";
this.trkNoiseVol.Size = new System.Drawing.Size(63, 160);
this.trkNoiseVol.TabIndex = 15;
this.trkNoiseVol.Value = 50;
//
// trkTriangleVol
//
this.trkTriangleVol.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.trkTriangleVol.Caption = "Triangle";
this.trkTriangleVol.Location = new System.Drawing.Point(231, 0);
this.trkTriangleVol.Margin = new System.Windows.Forms.Padding(0);
this.trkTriangleVol.MaximumSize = new System.Drawing.Size(63, 160);
this.trkTriangleVol.MinimumSize = new System.Drawing.Size(63, 160);
this.trkTriangleVol.Name = "trkTriangleVol";
this.trkTriangleVol.Size = new System.Drawing.Size(63, 160);
this.trkTriangleVol.TabIndex = 14;
this.trkTriangleVol.Value = 50;
//
// trkSquare2Vol
//
this.trkSquare2Vol.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.trkSquare2Vol.Caption = "Square 2";
this.trkSquare2Vol.Location = new System.Drawing.Point(156, 0);
this.trkSquare2Vol.Margin = new System.Windows.Forms.Padding(0);
this.trkSquare2Vol.MaximumSize = new System.Drawing.Size(63, 160);
this.trkSquare2Vol.MinimumSize = new System.Drawing.Size(63, 160);
this.trkSquare2Vol.Name = "trkSquare2Vol";
this.trkSquare2Vol.Size = new System.Drawing.Size(63, 160);
this.trkSquare2Vol.TabIndex = 13;
this.trkSquare2Vol.Value = 50;
//
// trkSquare1Vol
//
this.trkSquare1Vol.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.trkSquare1Vol.Caption = "Square 1";
this.trkSquare1Vol.Location = new System.Drawing.Point(81, 0);
this.trkSquare1Vol.Margin = new System.Windows.Forms.Padding(0);
this.trkSquare1Vol.MaximumSize = new System.Drawing.Size(63, 160);
this.trkSquare1Vol.MinimumSize = new System.Drawing.Size(63, 160);
this.trkSquare1Vol.Name = "trkSquare1Vol";
this.trkSquare1Vol.Size = new System.Drawing.Size(63, 160);
this.trkSquare1Vol.TabIndex = 12;
this.trkSquare1Vol.Value = 50;
//
// trkMaster
//
this.trkMaster.Anchor = System.Windows.Forms.AnchorStyles.Top;
this.trkMaster.Caption = "Master";
this.trkMaster.Location = new System.Drawing.Point(6, 0);
this.trkMaster.Margin = new System.Windows.Forms.Padding(0);
this.trkMaster.MaximumSize = new System.Drawing.Size(63, 160);
this.trkMaster.MinimumSize = new System.Drawing.Size(63, 160);
this.trkMaster.Name = "trkMaster";
this.trkMaster.Size = new System.Drawing.Size(63, 160);
this.trkMaster.TabIndex = 11;
this.trkMaster.Value = 50;
//
// tableLayoutPanel2
//
this.tableLayoutPanel2.ColumnCount = 1;
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel2.Controls.Add(this.grpVolume, 0, 1);
this.tableLayoutPanel2.Controls.Add(this.chkEnableAudio, 0, 0);
this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel2, 0, 2);
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0);
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
this.tableLayoutPanel2.RowCount = 4;
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel2.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel2.Size = new System.Drawing.Size(470, 274);
this.tableLayoutPanel2.TabIndex = 3;
//
// chkEnableAudio
//
this.chkEnableAudio.AutoSize = true;
this.chkEnableAudio.Location = new System.Drawing.Point(6, 6);
this.chkEnableAudio.Margin = new System.Windows.Forms.Padding(6, 6, 6, 3);
this.chkEnableAudio.Name = "chkEnableAudio";
this.chkEnableAudio.Size = new System.Drawing.Size(89, 17);
this.chkEnableAudio.TabIndex = 3;
this.chkEnableAudio.Text = "Enable Audio";
this.chkEnableAudio.UseVisualStyleBackColor = true;
this.chkEnableAudio.CheckedChanged += new System.EventHandler(this.AudioConfig_ValueChanged);
//
// flowLayoutPanel2
//
this.flowLayoutPanel2.Controls.Add(this.lblAudioLatency);
this.flowLayoutPanel2.Controls.Add(this.nudLatency);
this.flowLayoutPanel2.Controls.Add(this.lblLatencyMs);
this.flowLayoutPanel2.Location = new System.Drawing.Point(3, 220);
this.flowLayoutPanel2.Name = "flowLayoutPanel2";
this.flowLayoutPanel2.Size = new System.Drawing.Size(200, 24);
this.flowLayoutPanel2.TabIndex = 4;
//
// lblAudioLatency
//
this.lblAudioLatency.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblAudioLatency.AutoSize = true;
this.lblAudioLatency.Location = new System.Drawing.Point(3, 6);
this.lblAudioLatency.Name = "lblAudioLatency";
this.lblAudioLatency.Size = new System.Drawing.Size(48, 13);
this.lblAudioLatency.TabIndex = 0;
this.lblAudioLatency.Text = "Latency:";
//
// nudLatency
//
this.nudLatency.Location = new System.Drawing.Point(57, 3);
this.nudLatency.Maximum = new decimal(new int[] {
300,
0,
0,
0});
this.nudLatency.Minimum = new decimal(new int[] {
100,
0,
0,
0});
this.nudLatency.Name = "nudLatency";
this.nudLatency.Size = new System.Drawing.Size(45, 20);
this.nudLatency.TabIndex = 1;
this.nudLatency.Value = new decimal(new int[] {
100,
0,
0,
0});
//
// lblLatencyMs
//
this.lblLatencyMs.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblLatencyMs.AutoSize = true;
this.lblLatencyMs.Location = new System.Drawing.Point(108, 6);
this.lblLatencyMs.Name = "lblLatencyMs";
this.lblLatencyMs.Size = new System.Drawing.Size(20, 13);
this.lblLatencyMs.TabIndex = 2;
this.lblLatencyMs.Text = "ms";
//
// btnReset
//
this.btnReset.AutoSize = true;
this.btnReset.Location = new System.Drawing.Point(6, 3);
this.btnReset.Name = "btnReset";
this.btnReset.Size = new System.Drawing.Size(99, 23);
this.btnReset.TabIndex = 3;
this.btnReset.Text = "Reset to Defaults";
this.btnReset.UseVisualStyleBackColor = true;
this.btnReset.Click += new System.EventHandler(this.btnReset_Click);
//
// frmAudioConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(470, 274);
this.Controls.Add(this.tableLayoutPanel2);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "frmAudioConfig";
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Audio Options";
this.Controls.SetChildIndex(this.tableLayoutPanel2, 0);
this.Controls.SetChildIndex(this.baseConfigPanel, 0);
this.baseConfigPanel.ResumeLayout(false);
this.baseConfigPanel.PerformLayout();
this.grpVolume.ResumeLayout(false);
this.tableLayoutPanel1.ResumeLayout(false);
this.tableLayoutPanel2.ResumeLayout(false);
this.tableLayoutPanel2.PerformLayout();
this.flowLayoutPanel2.ResumeLayout(false);
this.flowLayoutPanel2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.nudLatency)).EndInit();
this.ResumeLayout(false);
}
#endregion
private System.Windows.Forms.GroupBox grpVolume;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel2;
private System.Windows.Forms.CheckBox chkEnableAudio;
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2;
private System.Windows.Forms.Label lblAudioLatency;
private System.Windows.Forms.NumericUpDown nudLatency;
private System.Windows.Forms.Label lblLatencyMs;
private System.Windows.Forms.Button btnReset;
private Controls.ctrlTrackbar trkMaster;
private Controls.ctrlTrackbar trkDmcVol;
private Controls.ctrlTrackbar trkNoiseVol;
private Controls.ctrlTrackbar trkTriangleVol;
private Controls.ctrlTrackbar trkSquare2Vol;
private Controls.ctrlTrackbar trkSquare1Vol;
}
}

View file

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using Mesen.GUI.Config;
namespace Mesen.GUI.Forms.Config
{
public partial class frmAudioConfig : BaseConfigForm
{
public frmAudioConfig()
{
InitializeComponent();
Entity = ConfigManager.Config.AudioInfo;
AddBinding("EnableAudio", chkEnableAudio);
AddBinding("MasterVolume", trkMaster);
AddBinding("Square1Volume", trkSquare1Vol);
AddBinding("Square2Volume", trkSquare2Vol);
AddBinding("TriangleVolume", trkTriangleVol);
AddBinding("NoiseVolume", trkNoiseVol);
AddBinding("DmcVolume", trkDmcVol);
AddBinding("AudioLatency", nudLatency);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
AudioInfo.ApplyConfig();
}
private void AudioConfig_ValueChanged(object sender, EventArgs e)
{
if(!this.Updating) {
UpdateObject();
AudioInfo.ApplyConfig();
}
}
private void btnReset_Click(object sender, EventArgs e)
{
AudioInfo config = Entity as AudioInfo;
config.EnableAudio = true;
config.AudioLatency = 100;
config.MasterVolume = 50;
config.Square2Volume = 50;
config.Square1Volume = 50;
config.TriangleVolume = 50;
config.NoiseVolume = 50;
config.DmcVolume = 50;
UpdateUI();
AudioInfo.ApplyConfig();
}
}
}

View file

@ -0,0 +1,120 @@
<?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=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View file

@ -248,10 +248,10 @@
//
// mnuAudioConfig
//
this.mnuAudioConfig.Enabled = false;
this.mnuAudioConfig.Name = "mnuAudioConfig";
this.mnuAudioConfig.Size = new System.Drawing.Size(152, 22);
this.mnuAudioConfig.Text = "Audio";
this.mnuAudioConfig.Click += new System.EventHandler(this.mnuAudioConfig_Click);
//
// mnuTools
//

View file

@ -56,6 +56,7 @@ namespace Mesen.GUI.Forms
}
ControllerInfo.ApplyConfig();
AudioInfo.ApplyConfig();
InteropEmu.SetFlags((int)EmulationFlags.LimitFPS);
}
@ -395,5 +396,11 @@ namespace Mesen.GUI.Forms
frmInputConfig frm = new frmInputConfig();
frm.ShowDialog();
}
private void mnuAudioConfig_Click(object sender, EventArgs e)
{
frmAudioConfig frm = new frmAudioConfig();
frm.ShowDialog();
}
}
}

View file

@ -79,9 +79,16 @@
<Compile Include="Config\ClientConnection.cs" />
<Compile Include="Config\Configuration.cs" />
<Compile Include="Config\ImageExtensions.cs" />
<Compile Include="Config\AudioInfo.cs" />
<Compile Include="Config\PlayerProfile.cs" />
<Compile Include="Config\ConfigManager.cs" />
<Compile Include="Config\ServerInfo.cs" />
<Compile Include="Controls\ctrlTrackbar.cs">
<SubType>UserControl</SubType>
</Compile>
<Compile Include="Controls\ctrlTrackbar.Designer.cs">
<DependentUpon>ctrlTrackbar.cs</DependentUpon>
</Compile>
<Compile Include="Controls\DXViewer.cs">
<SubType>UserControl</SubType>
</Compile>
@ -157,6 +164,12 @@
<Compile Include="Forms\Config\frmInputConfig.Designer.cs">
<DependentUpon>frmInputConfig.cs</DependentUpon>
</Compile>
<Compile Include="Forms\Config\frmAudioConfig.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="Forms\Config\frmAudioConfig.Designer.cs">
<DependentUpon>frmAudioConfig.cs</DependentUpon>
</Compile>
<Compile Include="Forms\Config\frmVideoConfig.cs">
<SubType>Form</SubType>
</Compile>
@ -196,6 +209,9 @@
<Compile Include="InteropEmu.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<EmbeddedResource Include="Controls\ctrlTrackbar.resx">
<DependentUpon>ctrlTrackbar.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Debugger\Controls\ctrlConsoleStatus.resx">
<DependentUpon>ctrlConsoleStatus.cs</DependentUpon>
</EmbeddedResource>
@ -226,6 +242,9 @@
<EmbeddedResource Include="Forms\Config\frmInputConfig.resx">
<DependentUpon>frmInputConfig.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\Config\frmAudioConfig.resx">
<DependentUpon>frmAudioConfig.cs</DependentUpon>
</EmbeddedResource>
<EmbeddedResource Include="Forms\Config\frmVideoConfig.resx">
<DependentUpon>frmVideoConfig.cs</DependentUpon>
</EmbeddedResource>

View file

@ -31,8 +31,6 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void Stop();
[DllImport(DLLPath, EntryPoint="GetROMPath")] private static extern IntPtr GetROMPathWrapper();
[DllImport(DLLPath)] public static extern void Reset();
[DllImport(DLLPath)] public static extern void SetFlags(int flags);
[DllImport(DLLPath)] public static extern void ClearFlags(int flags);
[DllImport(DLLPath)] public static extern void StartServer(UInt16 port);
[DllImport(DLLPath)] public static extern void StopServer();
[DllImport(DLLPath)] public static extern bool IsServerRunning();
@ -63,6 +61,11 @@ namespace Mesen.GUI
[DllImport(DLLPath)] public static extern void CheatAddProActionRocky(UInt32 code);
[DllImport(DLLPath)] public static extern void CheatClear();
[DllImport(DLLPath)] public static extern void SetFlags(UInt32 flags);
[DllImport(DLLPath)] public static extern void ClearFlags(UInt32 flags);
[DllImport(DLLPath)] public static extern void SetChannelVolume(UInt32 channel, double volume);
[DllImport(DLLPath)] public static extern void SetAudioLatency(UInt32 msLatency);
[DllImport(DLLPath)] public static extern void DebugInitialize();
[DllImport(DLLPath)] public static extern void DebugRelease();
[DllImport(DLLPath)] public static extern void DebugGetState(ref DebugState state);

View file

@ -11,6 +11,7 @@
#include "../Core/SaveStateManager.h"
#include "../Core/CheatManager.h"
#include "../Core/StandardController.h"
#include "../Core/EmulationSettings.h"
static NES::Renderer *_renderer = nullptr;
static SoundManager *_soundManager = nullptr;
@ -79,8 +80,8 @@ namespace InteropEmu {
}
}
DllExport void __stdcall Resume() { Console::ClearFlags(EmulationFlags::Paused); }
DllExport int __stdcall IsPaused() { return Console::CheckFlag(EmulationFlags::Paused); }
DllExport void __stdcall Resume() { EmulationSettings::ClearFlags(EmulationFlags::Paused); }
DllExport int __stdcall IsPaused() { return EmulationSettings::CheckFlag(EmulationFlags::Paused); }
DllExport void __stdcall Stop()
{
if(Console::GetInstance()) {
@ -94,8 +95,6 @@ namespace InteropEmu {
}
DllExport void __stdcall Reset() { Console::Reset(); }
DllExport void __stdcall SetFlags(int flags) { Console::SetFlags(flags); }
DllExport void __stdcall ClearFlags(int flags) { Console::ClearFlags(flags); }
DllExport void __stdcall StartServer(uint16_t port) { GameServer::StartServer(port); }
DllExport void __stdcall StopServer() { GameServer::StopServer(); }
@ -120,7 +119,7 @@ namespace InteropEmu {
DllExport void __stdcall Pause()
{
if(!IsConnected()) {
Console::SetFlags(EmulationFlags::Paused);
EmulationSettings::SetFlags(EmulationFlags::Paused);
}
}
@ -160,5 +159,11 @@ namespace InteropEmu {
DllExport void __stdcall CheatAddGameGenie(char* code) { CheatManager::AddGameGenieCode(code); }
DllExport void __stdcall CheatAddProActionRocky(uint32_t code) { CheatManager::AddProActionRockyCode(code); }
DllExport void __stdcall CheatClear() { CheatManager::ClearCodes(); }
DllExport void __stdcall SetFlags(uint32_t flags) { EmulationSettings::SetFlags(flags); }
DllExport void __stdcall ClearFlags(uint32_t flags) { EmulationSettings::ClearFlags(flags); }
DllExport void __stdcall SetChannelVolume(uint32_t channel, double volume) { EmulationSettings::SetChannelVolume((AudioChannel)channel, volume); }
DllExport void __stdcall SetAudioLatency(uint32_t msLatency) { EmulationSettings::SetAudioLatency(msLatency); }
}
}

View file

@ -5,7 +5,7 @@
#include "DirectXTK/DDSTextureLoader.h"
#include "DirectXTK/WICTextureLoader.h"
#include "../Core/PPU.h"
#include "../Core/Console.h"
#include "../Core/EmulationSettings.h"
#include "../Core/MessageManager.h"
#include "../Utilities/UTF8Util.h"
@ -369,6 +369,11 @@ namespace NES
sourceRect.top = 8;
sourceRect.bottom = _screenHeight - 8;
RECT destRect;
destRect.left = 0;
destRect.top = 0;
destRect.right = _screenWidth * 4;
destRect.bottom = (_screenHeight - 16) * 4;
XMVECTOR position{ { 0, 0 } };
D3D11_MAPPED_SUBRESOURCE dd;
@ -383,7 +388,7 @@ namespace NES
_pDeviceContext->Unmap(_pTexture, 0);
ID3D11ShaderResourceView *nesOutputBuffer = GetShaderResourceView(_pTexture);
_spriteBatch->Draw(nesOutputBuffer, position, &sourceRect, Colors::White, 0.0f, position, 4.0f);
_spriteBatch->Draw(nesOutputBuffer, destRect, &sourceRect);
nesOutputBuffer->Release();
}
@ -418,7 +423,7 @@ namespace NES
void Renderer::Render()
{
if(_frameChanged || Console::CheckFlag(EmulationFlags::Paused) || !_toasts.empty()) {
if(_frameChanged || EmulationSettings::CheckFlag(EmulationFlags::Paused) || !_toasts.empty()) {
_frameChanged = false;
// Clear the back buffer
_pDeviceContext->ClearRenderTargetView(_pRenderTargetView, Colors::Black);
@ -434,7 +439,7 @@ namespace NES
_spriteBatch->Begin(SpriteSortMode_Deferred, nullptr, _samplerState);*/
if(Console::CheckFlag(EmulationFlags::Paused)) {
if(EmulationSettings::CheckFlag(EmulationFlags::Paused)) {
DrawPauseScreen();
} else {
//Draw FPS counter

View file

@ -1,5 +1,6 @@
#include "stdafx.h"
#include "SoundManager.h"
#include "../Core/EmulationSettings.h"
SoundManager::SoundManager(HWND hwnd)
{
@ -165,7 +166,11 @@ void SoundManager::Reset()
void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t soundBufferSize)
{
static const int32_t byteLatency = _latency * (APU::BitsPerSample / 8);
int32_t byteLatency = (int32_t)((float)(APU::SampleRate * EmulationSettings::GetAudioLatency()) / 1000.0f * (APU::BitsPerSample / 8));
if(byteLatency != _previousLatency) {
Reset();
_previousLatency = byteLatency;
}
DWORD status;
_secondaryBuffer->GetStatus(&status);
@ -180,7 +185,7 @@ void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t soundBufferSize)
_secondaryBuffer->GetCurrentPosition(&currentPlayCursor, nullptr);
int32_t playWriteByteLatency = (_lastWriteOffset - currentPlayCursor);
if(playWriteByteLatency < -byteLatency * 2) {
if(playWriteByteLatency < 0) {
playWriteByteLatency = 0xFFFF - currentPlayCursor + _lastWriteOffset;
}
@ -189,10 +194,10 @@ void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t soundBufferSize)
//Out of sync, move back to where we should be (start of the latency buffer)
_secondaryBuffer->SetFrequency(44100);
_secondaryBuffer->SetCurrentPosition(_lastWriteOffset - byteLatency);
} else if(latencyGap < -200) {
} else if(latencyGap < -byteLatency/35) {
//Playing too fast, slow down playing
_secondaryBuffer->SetFrequency(43900);
} else if(latencyGap > 200) {
} else if(latencyGap > byteLatency/35) {
//Playing too slow, speed up
_secondaryBuffer->SetFrequency(44300);
} else {

View file

@ -23,7 +23,7 @@ private:
private:
uint16_t _lastWriteOffset = 0;
const uint16_t _latency = APU::SampleRate / (1000 / 150); // == 150ms latency
uint16_t _previousLatency = 0;
IDirectSound8* _directSound;
IDirectSoundBuffer* _primaryBuffer;