Audio: Added option to select sample rate

This commit is contained in:
Souryo 2016-01-14 19:33:16 -05:00
parent 6c12c9257d
commit f8a0277c2e
16 changed files with 176 additions and 91 deletions

View file

@ -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;
}

View file

@ -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);
};

View file

@ -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();

View file

@ -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) {

View file

@ -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;

View file

@ -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;

View file

@ -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;
};

View file

@ -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));
}
}

View file

@ -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);
};

View file

@ -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);
}
}
}

View file

@ -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;
}
}

View file

@ -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)

View file

@ -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);

View file

@ -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); }

View file

@ -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);
}
}
}

View file

@ -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;