mirror of
https://github.com/SourMesen/Mesen.git
synced 2025-04-02 10:52:48 -04:00
Audio: Added option to select sample rate
This commit is contained in:
parent
6c12c9257d
commit
f8a0277c2e
16 changed files with 176 additions and 91 deletions
17
Core/APU.cpp
17
Core/APU.cpp
|
@ -10,7 +10,6 @@
|
|||
#include "SoundMixer.h"
|
||||
|
||||
APU* APU::Instance = nullptr;
|
||||
IAudioDevice* APU::AudioDevice = nullptr;
|
||||
|
||||
APU::APU(MemoryManager* memoryManager)
|
||||
{
|
||||
|
@ -199,17 +198,6 @@ void APU::Exec()
|
|||
}
|
||||
}
|
||||
|
||||
void APU::StopAudio(bool clearBuffer)
|
||||
{
|
||||
if(APU::AudioDevice) {
|
||||
if(clearBuffer) {
|
||||
APU::AudioDevice->Stop();
|
||||
} else {
|
||||
APU::AudioDevice->Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void APU::Reset(bool softReset)
|
||||
{
|
||||
_currentCycle = 0;
|
||||
|
@ -237,9 +225,4 @@ void APU::StreamState(bool saving)
|
|||
if(!saving) {
|
||||
SetNesModel(_nesModel, true);
|
||||
}
|
||||
}
|
||||
|
||||
IAudioDevice* APU::GetAudioDevice()
|
||||
{
|
||||
return APU::AudioDevice;
|
||||
}
|
14
Core/APU.h
14
Core/APU.h
|
@ -18,7 +18,6 @@ enum class NesModel;
|
|||
class APU : public Snapshotable, public IMemoryHandler
|
||||
{
|
||||
private:
|
||||
static IAudioDevice* AudioDevice;
|
||||
static APU* Instance;
|
||||
|
||||
uint32_t _previousCycle;
|
||||
|
@ -46,22 +45,11 @@ class APU : public Snapshotable, public IMemoryHandler
|
|||
protected:
|
||||
void StreamState(bool saving);
|
||||
|
||||
public:
|
||||
static const uint32_t SampleRate = 44100;
|
||||
static const uint32_t SamplesPerFrame = 44100 / 60;
|
||||
static const uint32_t BitsPerSample = 16;
|
||||
|
||||
public:
|
||||
APU(MemoryManager* memoryManager);
|
||||
~APU();
|
||||
|
||||
void Reset(bool softReset);
|
||||
|
||||
static void RegisterAudioDevice(IAudioDevice *audioDevice)
|
||||
{
|
||||
APU::AudioDevice = audioDevice;
|
||||
}
|
||||
|
||||
void SetNesModel(NesModel model, bool forceInit = false);
|
||||
|
||||
uint8_t ReadRAM(uint16_t addr);
|
||||
|
@ -71,7 +59,5 @@ class APU : public Snapshotable, public IMemoryHandler
|
|||
void Exec();
|
||||
static void ExecStatic();
|
||||
|
||||
static IAudioDevice* GetAudioDevice();
|
||||
static void StaticRun();
|
||||
static void StopAudio(bool clearBuffer = false);
|
||||
};
|
|
@ -9,6 +9,7 @@
|
|||
#include "../Utilities/Timer.h"
|
||||
#include "../Utilities/FolderUtilities.h"
|
||||
#include "HdPpu.h"
|
||||
#include "SoundMixer.h"
|
||||
|
||||
shared_ptr<Console> Console::Instance(new Console());
|
||||
|
||||
|
@ -151,7 +152,7 @@ void Console::ResetComponents(bool softReset)
|
|||
_cpu->Reset(softReset);
|
||||
_memoryManager->Reset(softReset);
|
||||
|
||||
_apu->StopAudio(true);
|
||||
SoundMixer::StopAudio(true);
|
||||
|
||||
if(softReset) {
|
||||
MessageManager::SendNotification(ConsoleNotificationType::GameReset);
|
||||
|
@ -226,7 +227,7 @@ void Console::Run()
|
|||
_runLock.Release();
|
||||
|
||||
//Prevent audio from looping endlessly while game is paused
|
||||
_apu->StopAudio();
|
||||
SoundMixer::StopAudio();
|
||||
|
||||
while(EmulationSettings::CheckFlag(EmulationFlags::Paused)) {
|
||||
//Sleep until emulation is resumed
|
||||
|
@ -251,7 +252,7 @@ void Console::Run()
|
|||
}
|
||||
}
|
||||
}
|
||||
_apu->StopAudio(true);
|
||||
SoundMixer::StopAudio();
|
||||
Movie::Stop();
|
||||
|
||||
VideoDecoder::GetInstance()->StopThread();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
#include "Disassembler.h"
|
||||
#include "VideoDecoder.h"
|
||||
#include "APU.h"
|
||||
#include "SoundMixer.h"
|
||||
#include "CodeDataLogger.h"
|
||||
#include "ExpressionEvaluator.h"
|
||||
|
||||
|
@ -283,7 +284,7 @@ bool Debugger::SleepUntilResume()
|
|||
|
||||
if(stepCount == 0) {
|
||||
//Break
|
||||
APU::StopAudio();
|
||||
SoundMixer::StopAudio();
|
||||
MessageManager::SendNotification(ConsoleNotificationType::CodeBreak);
|
||||
_stepOverAddr = -1;
|
||||
while(stepCount == 0) {
|
||||
|
|
|
@ -6,6 +6,7 @@ uint32_t EmulationSettings::_flags = EmulationFlags::LowLatency;
|
|||
uint32_t EmulationSettings::_audioLatency = 20000;
|
||||
double EmulationSettings::_channelVolume[5] = { 0.5f, 0.5f, 0.5f, 0.5f, 0.5f };
|
||||
double EmulationSettings::_masterVolume = 1.0;
|
||||
uint32_t EmulationSettings::_sampleRate = 44100;
|
||||
|
||||
NesModel EmulationSettings::_model = NesModel::Auto;
|
||||
|
||||
|
|
|
@ -67,6 +67,8 @@ private:
|
|||
static uint32_t _audioLatency;
|
||||
static double _channelVolume[5];
|
||||
static double _masterVolume;
|
||||
|
||||
static uint32_t _sampleRate;
|
||||
|
||||
static NesModel _model;
|
||||
|
||||
|
@ -113,6 +115,16 @@ public:
|
|||
_masterVolume = volume;
|
||||
}
|
||||
|
||||
static void SetSampleRate(uint32_t sampleRate)
|
||||
{
|
||||
_sampleRate = sampleRate;
|
||||
}
|
||||
|
||||
static uint32_t GetSampleRate()
|
||||
{
|
||||
return _sampleRate;
|
||||
}
|
||||
|
||||
static void SetAudioLatency(uint32_t msLatency)
|
||||
{
|
||||
_audioLatency = msLatency;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
class IAudioDevice
|
||||
{
|
||||
public:
|
||||
virtual void PlayBuffer(int16_t *soundBuffer, uint32_t bufferSize) = 0;
|
||||
virtual void PlayBuffer(int16_t *soundBuffer, uint32_t bufferSize, uint32_t sampleRate) = 0;
|
||||
virtual void Stop() = 0;
|
||||
virtual void Pause() = 0;
|
||||
};
|
|
@ -3,11 +3,13 @@
|
|||
#include "APU.h"
|
||||
#include "CPU.h"
|
||||
|
||||
IAudioDevice* SoundMixer::AudioDevice = nullptr;
|
||||
|
||||
SoundMixer::SoundMixer()
|
||||
{
|
||||
_outputBuffer = new int16_t[APU::SamplesPerFrame];
|
||||
_blipBuf = blip_new(APU::SamplesPerFrame);
|
||||
|
||||
_outputBuffer = new int16_t[SoundMixer::MaxSamplesPerFrame];
|
||||
_blipBuf = blip_new(SoundMixer::MaxSamplesPerFrame);
|
||||
_sampleRate = EmulationSettings::GetSampleRate();
|
||||
InitializeLookupTables();
|
||||
|
||||
Reset();
|
||||
|
@ -21,6 +23,22 @@ SoundMixer::~SoundMixer()
|
|||
blip_delete(_blipBuf);
|
||||
}
|
||||
|
||||
void SoundMixer::RegisterAudioDevice(IAudioDevice *audioDevice)
|
||||
{
|
||||
SoundMixer::AudioDevice = audioDevice;
|
||||
}
|
||||
|
||||
void SoundMixer::StopAudio(bool clearBuffer)
|
||||
{
|
||||
if(SoundMixer::AudioDevice) {
|
||||
if(clearBuffer) {
|
||||
SoundMixer::AudioDevice->Stop();
|
||||
} else {
|
||||
SoundMixer::AudioDevice->Pause();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMixer::Reset()
|
||||
{
|
||||
_previousOutput = 0;
|
||||
|
@ -36,16 +54,27 @@ void SoundMixer::Reset()
|
|||
void SoundMixer::PlayAudioBuffer(uint32_t time)
|
||||
{
|
||||
EndFrame(time);
|
||||
size_t sampleCount = blip_read_samples(_blipBuf, _outputBuffer, APU::SamplesPerFrame, 0);
|
||||
IAudioDevice *audioDevice = APU::GetAudioDevice();
|
||||
if(audioDevice) {
|
||||
audioDevice->PlayBuffer(_outputBuffer, (uint32_t)(sampleCount * APU::BitsPerSample / 8));
|
||||
size_t sampleCount = blip_read_samples(_blipBuf, _outputBuffer, SoundMixer::MaxSamplesPerFrame, 0);
|
||||
if(SoundMixer::AudioDevice) {
|
||||
SoundMixer::AudioDevice->PlayBuffer(_outputBuffer, (uint32_t)(sampleCount * SoundMixer::BitsPerSample / 8), _sampleRate);
|
||||
}
|
||||
|
||||
if(EmulationSettings::GetSampleRate() != _sampleRate) {
|
||||
//Update sample rate for next frame if setting changed
|
||||
_sampleRate = EmulationSettings::GetSampleRate();
|
||||
UpdateRates();
|
||||
}
|
||||
}
|
||||
|
||||
void SoundMixer::SetNesModel(NesModel model)
|
||||
{
|
||||
blip_set_rates(_blipBuf, model == NesModel::NTSC ? CPU::ClockRateNtsc : CPU::ClockRatePal, APU::SampleRate);
|
||||
_clockRate = model == NesModel::NTSC ? CPU::ClockRateNtsc : CPU::ClockRatePal;
|
||||
UpdateRates();
|
||||
}
|
||||
|
||||
void SoundMixer::UpdateRates()
|
||||
{
|
||||
blip_set_rates(_blipBuf, _clockRate, _sampleRate);
|
||||
}
|
||||
|
||||
void SoundMixer::InitializeLookupTables()
|
||||
|
@ -89,7 +118,7 @@ void SoundMixer::EndFrame(uint32_t time)
|
|||
_currentOutput[4] += _channelOutput[4][time];
|
||||
|
||||
int16_t currentOutput = GetOutputVolume();
|
||||
blip_add_delta(_blipBuf, time, (currentOutput - _previousOutput) * masterVolume);
|
||||
blip_add_delta(_blipBuf, time, (int)((currentOutput - _previousOutput) * masterVolume));
|
||||
_previousOutput = currentOutput;
|
||||
}
|
||||
blip_end_frame(_blipBuf, time);
|
||||
|
@ -101,4 +130,4 @@ void SoundMixer::EndFrame(uint32_t time)
|
|||
|
||||
_timestamps.clear();
|
||||
memset(_channelOutput, 0, sizeof(_channelOutput));
|
||||
}
|
||||
}
|
|
@ -2,10 +2,15 @@
|
|||
#include "stdafx.h"
|
||||
#include "EmulationSettings.h"
|
||||
#include "../BlipBuffer/blip_buf.h"
|
||||
#include "IAudioDevice.h"
|
||||
|
||||
class SoundMixer
|
||||
{
|
||||
private:
|
||||
static IAudioDevice* AudioDevice;
|
||||
static const uint32_t MaxSampleRate = 48000;
|
||||
static const uint32_t MaxSamplesPerFrame = MaxSampleRate / 60;
|
||||
|
||||
int16_t _previousOutput = 0;
|
||||
|
||||
vector<uint32_t> _timestamps;
|
||||
|
@ -19,11 +24,18 @@ private:
|
|||
int16_t *_outputBuffer;
|
||||
double _volumes[5];
|
||||
|
||||
uint32_t _sampleRate;
|
||||
uint32_t _clockRate;
|
||||
|
||||
void InitializeLookupTables();
|
||||
int16_t GetOutputVolume();
|
||||
void EndFrame(uint32_t time);
|
||||
|
||||
void UpdateRates();
|
||||
|
||||
public:
|
||||
static const uint32_t BitsPerSample = 16;
|
||||
|
||||
SoundMixer();
|
||||
~SoundMixer();
|
||||
|
||||
|
@ -32,4 +44,7 @@ public:
|
|||
|
||||
void PlayAudioBuffer(uint32_t cycle);
|
||||
void AddDelta(AudioChannel channel, uint32_t time, int8_t output);
|
||||
|
||||
static void StopAudio(bool clearBuffer = false);
|
||||
static void RegisterAudioDevice(IAudioDevice *audioDevice);
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ namespace Mesen.GUI.Config
|
|||
public UInt32 TriangleVolume = 100;
|
||||
public UInt32 NoiseVolume = 100;
|
||||
public UInt32 DmcVolume = 100;
|
||||
public UInt32 SampleRate = 44100;
|
||||
|
||||
public AudioInfo()
|
||||
{
|
||||
|
@ -43,6 +44,7 @@ namespace Mesen.GUI.Config
|
|||
InteropEmu.SetChannelVolume(2, ConvertVolume(audioInfo.TriangleVolume));
|
||||
InteropEmu.SetChannelVolume(3, ConvertVolume(audioInfo.NoiseVolume));
|
||||
InteropEmu.SetChannelVolume(4, ConvertVolume(audioInfo.DmcVolume));
|
||||
InteropEmu.SetSampleRate(audioInfo.SampleRate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
90
GUI.NET/Forms/Config/frmAudioConfig.Designer.cs
generated
90
GUI.NET/Forms/Config/frmAudioConfig.Designer.cs
generated
|
@ -37,10 +37,12 @@
|
|||
this.trkMaster = new Mesen.GUI.Controls.ctrlTrackbar();
|
||||
this.tableLayoutPanel2 = new System.Windows.Forms.TableLayoutPanel();
|
||||
this.chkEnableAudio = new System.Windows.Forms.CheckBox();
|
||||
this.lblSampleRate = new System.Windows.Forms.Label();
|
||||
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.lblAudioLatency = new System.Windows.Forms.Label();
|
||||
this.cboSampleRate = new System.Windows.Forms.ComboBox();
|
||||
this.btnReset = new System.Windows.Forms.Button();
|
||||
this.baseConfigPanel.SuspendLayout();
|
||||
this.grpVolume.SuspendLayout();
|
||||
|
@ -53,14 +55,15 @@
|
|||
// baseConfigPanel
|
||||
//
|
||||
this.baseConfigPanel.Controls.Add(this.btnReset);
|
||||
this.baseConfigPanel.Location = new System.Drawing.Point(0, 245);
|
||||
this.baseConfigPanel.Location = new System.Drawing.Point(0, 267);
|
||||
this.baseConfigPanel.Size = new System.Drawing.Size(470, 29);
|
||||
this.baseConfigPanel.Controls.SetChildIndex(this.btnReset, 0);
|
||||
//
|
||||
// grpVolume
|
||||
//
|
||||
this.tableLayoutPanel2.SetColumnSpan(this.grpVolume, 2);
|
||||
this.grpVolume.Controls.Add(this.tableLayoutPanel1);
|
||||
this.grpVolume.Location = new System.Drawing.Point(3, 29);
|
||||
this.grpVolume.Location = new System.Drawing.Point(3, 80);
|
||||
this.grpVolume.Name = "grpVolume";
|
||||
this.grpVolume.Size = new System.Drawing.Size(462, 185);
|
||||
this.grpVolume.TabIndex = 2;
|
||||
|
@ -96,6 +99,7 @@
|
|||
this.trkDmcVol.Caption = "DMC";
|
||||
this.trkDmcVol.Location = new System.Drawing.Point(381, 0);
|
||||
this.trkDmcVol.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.trkDmcVol.Maximum = 100;
|
||||
this.trkDmcVol.MaximumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkDmcVol.MinimumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkDmcVol.Name = "trkDmcVol";
|
||||
|
@ -110,6 +114,7 @@
|
|||
this.trkNoiseVol.Caption = "Noise";
|
||||
this.trkNoiseVol.Location = new System.Drawing.Point(306, 0);
|
||||
this.trkNoiseVol.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.trkNoiseVol.Maximum = 100;
|
||||
this.trkNoiseVol.MaximumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkNoiseVol.MinimumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkNoiseVol.Name = "trkNoiseVol";
|
||||
|
@ -124,6 +129,7 @@
|
|||
this.trkTriangleVol.Caption = "Triangle";
|
||||
this.trkTriangleVol.Location = new System.Drawing.Point(231, 0);
|
||||
this.trkTriangleVol.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.trkTriangleVol.Maximum = 100;
|
||||
this.trkTriangleVol.MaximumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkTriangleVol.MinimumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkTriangleVol.Name = "trkTriangleVol";
|
||||
|
@ -138,6 +144,7 @@
|
|||
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.Maximum = 100;
|
||||
this.trkSquare2Vol.MaximumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkSquare2Vol.MinimumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkSquare2Vol.Name = "trkSquare2Vol";
|
||||
|
@ -152,6 +159,7 @@
|
|||
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.Maximum = 100;
|
||||
this.trkSquare1Vol.MaximumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkSquare1Vol.MinimumSize = new System.Drawing.Size(63, 160);
|
||||
this.trkSquare1Vol.Name = "trkSquare1Vol";
|
||||
|
@ -177,11 +185,15 @@
|
|||
//
|
||||
// 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.ColumnCount = 2;
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel2.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle());
|
||||
this.tableLayoutPanel2.Controls.Add(this.chkEnableAudio, 0, 0);
|
||||
this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel2, 0, 2);
|
||||
this.tableLayoutPanel2.Controls.Add(this.lblSampleRate, 0, 1);
|
||||
this.tableLayoutPanel2.Controls.Add(this.flowLayoutPanel2, 1, 2);
|
||||
this.tableLayoutPanel2.Controls.Add(this.lblAudioLatency, 0, 2);
|
||||
this.tableLayoutPanel2.Controls.Add(this.cboSampleRate, 1, 1);
|
||||
this.tableLayoutPanel2.Controls.Add(this.grpVolume, 0, 3);
|
||||
this.tableLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
|
||||
this.tableLayoutPanel2.Location = new System.Drawing.Point(0, 0);
|
||||
this.tableLayoutPanel2.Name = "tableLayoutPanel2";
|
||||
|
@ -190,12 +202,14 @@
|
|||
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.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 20F));
|
||||
this.tableLayoutPanel2.Size = new System.Drawing.Size(470, 296);
|
||||
this.tableLayoutPanel2.TabIndex = 3;
|
||||
//
|
||||
// chkEnableAudio
|
||||
//
|
||||
this.chkEnableAudio.AutoSize = true;
|
||||
this.tableLayoutPanel2.SetColumnSpan(this.chkEnableAudio, 2);
|
||||
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";
|
||||
|
@ -205,29 +219,29 @@
|
|||
this.chkEnableAudio.UseVisualStyleBackColor = true;
|
||||
this.chkEnableAudio.CheckedChanged += new System.EventHandler(this.AudioConfig_ValueChanged);
|
||||
//
|
||||
// lblSampleRate
|
||||
//
|
||||
this.lblSampleRate.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.lblSampleRate.AutoSize = true;
|
||||
this.lblSampleRate.Location = new System.Drawing.Point(3, 33);
|
||||
this.lblSampleRate.Name = "lblSampleRate";
|
||||
this.lblSampleRate.Size = new System.Drawing.Size(71, 13);
|
||||
this.lblSampleRate.TabIndex = 0;
|
||||
this.lblSampleRate.Text = "Sample Rate:";
|
||||
//
|
||||
// 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.Location = new System.Drawing.Point(77, 53);
|
||||
this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0);
|
||||
this.flowLayoutPanel2.Name = "flowLayoutPanel2";
|
||||
this.flowLayoutPanel2.Size = new System.Drawing.Size(200, 24);
|
||||
this.flowLayoutPanel2.Size = new System.Drawing.Size(78, 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.Location = new System.Drawing.Point(3, 3);
|
||||
this.nudLatency.Maximum = new decimal(new int[] {
|
||||
300,
|
||||
0,
|
||||
|
@ -251,12 +265,36 @@
|
|||
//
|
||||
this.lblLatencyMs.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.lblLatencyMs.AutoSize = true;
|
||||
this.lblLatencyMs.Location = new System.Drawing.Point(108, 6);
|
||||
this.lblLatencyMs.Location = new System.Drawing.Point(54, 6);
|
||||
this.lblLatencyMs.Name = "lblLatencyMs";
|
||||
this.lblLatencyMs.Size = new System.Drawing.Size(20, 13);
|
||||
this.lblLatencyMs.TabIndex = 2;
|
||||
this.lblLatencyMs.Text = "ms";
|
||||
//
|
||||
// lblAudioLatency
|
||||
//
|
||||
this.lblAudioLatency.Anchor = System.Windows.Forms.AnchorStyles.Left;
|
||||
this.lblAudioLatency.AutoSize = true;
|
||||
this.lblAudioLatency.Location = new System.Drawing.Point(3, 58);
|
||||
this.lblAudioLatency.Name = "lblAudioLatency";
|
||||
this.lblAudioLatency.Size = new System.Drawing.Size(48, 13);
|
||||
this.lblAudioLatency.TabIndex = 0;
|
||||
this.lblAudioLatency.Text = "Latency:";
|
||||
//
|
||||
// cboSampleRate
|
||||
//
|
||||
this.cboSampleRate.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
|
||||
this.cboSampleRate.FormattingEnabled = true;
|
||||
this.cboSampleRate.Items.AddRange(new object[] {
|
||||
"11,025 Hz",
|
||||
"22,050 Hz",
|
||||
"44,100 Hz",
|
||||
"48,000 Hz"});
|
||||
this.cboSampleRate.Location = new System.Drawing.Point(80, 29);
|
||||
this.cboSampleRate.Name = "cboSampleRate";
|
||||
this.cboSampleRate.Size = new System.Drawing.Size(75, 21);
|
||||
this.cboSampleRate.TabIndex = 5;
|
||||
//
|
||||
// btnReset
|
||||
//
|
||||
this.btnReset.AutoSize = true;
|
||||
|
@ -272,7 +310,7 @@
|
|||
//
|
||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||
this.ClientSize = new System.Drawing.Size(470, 274);
|
||||
this.ClientSize = new System.Drawing.Size(470, 296);
|
||||
this.Controls.Add(this.tableLayoutPanel2);
|
||||
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
|
||||
this.MaximizeBox = false;
|
||||
|
@ -301,7 +339,6 @@
|
|||
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;
|
||||
|
@ -312,6 +349,9 @@
|
|||
private Controls.ctrlTrackbar trkTriangleVol;
|
||||
private Controls.ctrlTrackbar trkSquare2Vol;
|
||||
private Controls.ctrlTrackbar trkSquare1Vol;
|
||||
private System.Windows.Forms.Label lblSampleRate;
|
||||
private System.Windows.Forms.FlowLayoutPanel flowLayoutPanel2;
|
||||
private System.Windows.Forms.ComboBox cboSampleRate;
|
||||
|
||||
}
|
||||
}
|
|
@ -27,6 +27,7 @@ namespace Mesen.GUI.Forms.Config
|
|||
AddBinding("NoiseVolume", trkNoiseVol);
|
||||
AddBinding("DmcVolume", trkDmcVol);
|
||||
AddBinding("AudioLatency", nudLatency);
|
||||
AddBinding("SampleRate", cboSampleRate);
|
||||
}
|
||||
|
||||
protected override void OnFormClosed(FormClosedEventArgs e)
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace Mesen.GUI
|
|||
[DllImport(DLLPath)] private static extern void ClearFlags(EmulationFlags flags);
|
||||
[DllImport(DLLPath)] public static extern void SetMasterVolume(double volume);
|
||||
[DllImport(DLLPath)] public static extern void SetChannelVolume(UInt32 channel, double volume);
|
||||
[DllImport(DLLPath)] public static extern void SetSampleRate(UInt32 sampleRate);
|
||||
[DllImport(DLLPath)] public static extern void SetAudioLatency(UInt32 msLatency);
|
||||
[DllImport(DLLPath)] public static extern void SetNesModel(NesModel model);
|
||||
[DllImport(DLLPath)] public static extern void SetEmulationSpeed(UInt32 emulationSpeed);
|
||||
|
|
|
@ -205,6 +205,7 @@ namespace InteropEmu {
|
|||
DllExport void __stdcall ClearFlags(EmulationFlags flags) { EmulationSettings::ClearFlags(flags); }
|
||||
DllExport void __stdcall SetChannelVolume(uint32_t channel, double volume) { EmulationSettings::SetChannelVolume((AudioChannel)channel, volume); }
|
||||
DllExport void __stdcall SetMasterVolume(double volume) { EmulationSettings::SetMasterVolume(volume); }
|
||||
DllExport void __stdcall SetSampleRate(uint32_t sampleRate) { EmulationSettings::SetSampleRate(sampleRate); }
|
||||
DllExport void __stdcall SetAudioLatency(uint32_t msLatency) { EmulationSettings::SetAudioLatency(msLatency); }
|
||||
DllExport void __stdcall SetNesModel(uint32_t model) { EmulationSettings::SetNesModel((NesModel)model); }
|
||||
DllExport void __stdcall SetOverscanDimensions(uint32_t left, uint32_t right, uint32_t top, uint32_t bottom) { EmulationSettings::SetOverscanDimensions(left, right, top, bottom); }
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#include "stdafx.h"
|
||||
#include "SoundManager.h"
|
||||
#include "../Core/EmulationSettings.h"
|
||||
#include "../Core/SoundMixer.h"
|
||||
|
||||
SoundManager::SoundManager(HWND hwnd)
|
||||
{
|
||||
APU::RegisterAudioDevice(this);
|
||||
SoundMixer::RegisterAudioDevice(this);
|
||||
|
||||
_hWnd = hwnd;
|
||||
_directSound = 0;
|
||||
_primaryBuffer = 0;
|
||||
_secondaryBuffer = 0;
|
||||
|
||||
InitializeDirectSound(hwnd);
|
||||
}
|
||||
|
||||
SoundManager::~SoundManager()
|
||||
|
@ -18,13 +18,12 @@ SoundManager::~SoundManager()
|
|||
Release();
|
||||
}
|
||||
|
||||
bool SoundManager::InitializeDirectSound(HWND hwnd)
|
||||
bool SoundManager::InitializeDirectSound(uint32_t sampleRate)
|
||||
{
|
||||
HRESULT result;
|
||||
DSBUFFERDESC bufferDesc;
|
||||
WAVEFORMATEX waveFormat;
|
||||
|
||||
|
||||
|
||||
// Initialize the direct sound interface pointer for the default sound device.
|
||||
result = DirectSoundCreate8(NULL, &_directSound, NULL);
|
||||
if(FAILED(result)) {
|
||||
|
@ -32,7 +31,7 @@ bool SoundManager::InitializeDirectSound(HWND hwnd)
|
|||
}
|
||||
|
||||
// Set the cooperative level to priority so the format of the primary sound buffer can be modified.
|
||||
result = _directSound->SetCooperativeLevel(hwnd, DSSCL_PRIORITY);
|
||||
result = _directSound->SetCooperativeLevel(_hWnd, DSSCL_PRIORITY);
|
||||
if(FAILED(result)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -52,8 +51,10 @@ bool SoundManager::InitializeDirectSound(HWND hwnd)
|
|||
}
|
||||
|
||||
// Setup the format of the primary sound bufffer.
|
||||
_sampleRate = sampleRate;
|
||||
|
||||
waveFormat.wFormatTag = WAVE_FORMAT_PCM;
|
||||
waveFormat.nSamplesPerSec = 44100;
|
||||
waveFormat.nSamplesPerSec = _sampleRate;
|
||||
waveFormat.wBitsPerSample = 16;
|
||||
waveFormat.nChannels = 1;
|
||||
waveFormat.nBlockAlign = (waveFormat.wBitsPerSample / 8) * waveFormat.nChannels;
|
||||
|
@ -102,6 +103,8 @@ bool SoundManager::InitializeDirectSound(HWND hwnd)
|
|||
|
||||
void SoundManager::Release()
|
||||
{
|
||||
_lastWriteOffset = 0;
|
||||
|
||||
if(_secondaryBuffer) {
|
||||
_secondaryBuffer->Release();
|
||||
_secondaryBuffer = nullptr;
|
||||
|
@ -164,9 +167,14 @@ void SoundManager::Play()
|
|||
_secondaryBuffer->Play(0, 0, DSBPLAY_LOOPING);
|
||||
}
|
||||
|
||||
void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t soundBufferSize)
|
||||
void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t soundBufferSize, uint32_t sampleRate)
|
||||
{
|
||||
int32_t byteLatency = (int32_t)((float)(APU::SampleRate * EmulationSettings::GetAudioLatency()) / 1000.0f * (APU::BitsPerSample / 8));
|
||||
if(_sampleRate != sampleRate) {
|
||||
Release();
|
||||
InitializeDirectSound(sampleRate);
|
||||
}
|
||||
|
||||
int32_t byteLatency = (int32_t)((float)(sampleRate * EmulationSettings::GetAudioLatency()) / 1000.0f * (SoundMixer::BitsPerSample / 8));
|
||||
if(byteLatency != _previousLatency) {
|
||||
Stop();
|
||||
_previousLatency = byteLatency;
|
||||
|
@ -190,19 +198,20 @@ void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t soundBufferSize)
|
|||
}
|
||||
|
||||
int32_t latencyGap = playWriteByteLatency - byteLatency;
|
||||
if(abs(latencyGap) > 3000) {
|
||||
int32_t tolerance = byteLatency / 35;
|
||||
if(abs(latencyGap) > byteLatency / 2) {
|
||||
//Out of sync, move back to where we should be (start of the latency buffer)
|
||||
_secondaryBuffer->SetFrequency(44100);
|
||||
_secondaryBuffer->SetFrequency(sampleRate);
|
||||
_secondaryBuffer->SetCurrentPosition(_lastWriteOffset - byteLatency);
|
||||
} else if(latencyGap < -byteLatency/35) {
|
||||
} else if(latencyGap < -tolerance) {
|
||||
//Playing too fast, slow down playing
|
||||
_secondaryBuffer->SetFrequency(43900);
|
||||
} else if(latencyGap > byteLatency/35) {
|
||||
_secondaryBuffer->SetFrequency((DWORD)(sampleRate * 0.9975));
|
||||
} else if(latencyGap > tolerance) {
|
||||
//Playing too slow, speed up
|
||||
_secondaryBuffer->SetFrequency(44300);
|
||||
_secondaryBuffer->SetFrequency((DWORD)(sampleRate * 1.0025));
|
||||
} else {
|
||||
//Normal playback
|
||||
_secondaryBuffer->SetFrequency(44100);
|
||||
_secondaryBuffer->SetFrequency(sampleRate);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,7 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "../Core/APU.h"
|
||||
#include "../Core/IAudioDevice.h"
|
||||
|
||||
class SoundManager : public IAudioDevice
|
||||
{
|
||||
|
@ -10,20 +10,23 @@ public:
|
|||
~SoundManager();
|
||||
|
||||
void Release();
|
||||
void PlayBuffer(int16_t *soundBuffer, uint32_t bufferSize);
|
||||
void PlayBuffer(int16_t *soundBuffer, uint32_t bufferSize, uint32_t sampleRate);
|
||||
void Play();
|
||||
void Pause();
|
||||
void Stop();
|
||||
|
||||
private:
|
||||
bool InitializeDirectSound(HWND);
|
||||
bool InitializeDirectSound(uint32_t sampleRate);
|
||||
void ShutdownDirectSound();
|
||||
void ClearSecondaryBuffer();
|
||||
void CopyToSecondaryBuffer(uint8_t *data, uint32_t size);
|
||||
|
||||
private:
|
||||
HWND _hWnd;
|
||||
|
||||
uint16_t _lastWriteOffset = 0;
|
||||
uint16_t _previousLatency = 0;
|
||||
uint32_t _sampleRate = 0;
|
||||
|
||||
IDirectSound8* _directSound;
|
||||
IDirectSoundBuffer* _primaryBuffer;
|
||||
|
|
Loading…
Add table
Reference in a new issue