Audio: Improved dynamic sample rate logic and added an option to disable it completely

This commit is contained in:
Sour 2018-08-02 22:09:55 -04:00
parent 1a7f07cff5
commit 115f46e05f
16 changed files with 96 additions and 21 deletions

View file

@ -1398,8 +1398,8 @@ void Console::DisplayDebugInformation(Timer &clockTimer, Timer &lastFrameTimer,
int startFrame = _ppu->GetFrameCount();
_debugHud->DrawRectangle(8, 8, 115, 40, 0x40000000, true, 1, startFrame);
_debugHud->DrawRectangle(8, 8, 115, 40, 0xFFFFFF, false, 1, startFrame);
_debugHud->DrawRectangle(8, 8, 115, 49, 0x40000000, true, 1, startFrame);
_debugHud->DrawRectangle(8, 8, 115, 49, 0xFFFFFF, false, 1, startFrame);
_debugHud->DrawString(10, 10, "Audio Stats", 0xFFFFFF, 0xFF000000, 1, startFrame);
_debugHud->DrawString(10, 21, "Latency: ", 0xFFFFFF, 0xFF000000, 1, startFrame);
@ -1411,6 +1411,7 @@ void Console::DisplayDebugInformation(Timer &clockTimer, Timer &lastFrameTimer,
_debugHud->DrawString(10, 30, "Underruns: " + std::to_string(stats.BufferUnderrunEventCount), 0xFFFFFF, 0xFF000000, 1, startFrame);
_debugHud->DrawString(10, 39, "Buffer Size: " + std::to_string(stats.BufferSize / 1024) + "kb", 0xFFFFFF, 0xFF000000, 1, startFrame);
_debugHud->DrawString(10, 48, "Rate: " + std::to_string((uint32_t)(_settings->GetSampleRate() * _soundMixer->GetRateAdjustment())) + "Hz", 0xFFFFFF, 0xFF000000, 1, startFrame);
_debugHud->DrawRectangle(136, 8, 115, 58, 0x40000000, true, 1, startFrame);
_debugHud->DrawRectangle(136, 8, 115, 58, 0xFFFFFF, false, 1, startFrame);

View file

@ -19,6 +19,8 @@ enum EmulationFlags : uint64_t
UseHdPacks = 0x20,
HasFourScore = 0x40,
DisableDynamicSampleRate = 0x80,
PauseOnMovieEnd = 0x0100,
PauseWhenInBackground = 0x0200,
AllowBackgroundInput = 0x0400,

View file

@ -420,26 +420,57 @@ void SoundMixer::ProcessEndOfFrame()
}
}
double SoundMixer::GetRateAdjustment()
{
return _rateAdjustment;
}
double SoundMixer::GetTargetRateAdjustment()
{
bool isRecording = _waveRecorder || _console->GetVideoRenderer()->IsRecording();
if(!isRecording) {
if(!isRecording && !_settings->CheckFlag(EmulationFlags::DisableDynamicSampleRate)) {
//Don't deviate from selected sample rate while recording
//TODO: Have 2 output streams (one for recording, one for the speakers)
AudioStatistics stats = GetStatistics();
if(stats.AverageLatency > 0 && _settings->GetEmulationSpeed() == 100) {
int32_t requestedLatency = (int32_t)_settings->GetAudioLatency();
//Try to stay within +/- 3ms of requested latency
constexpr int32_t maxGap = 3;
constexpr int32_t maxSubAdjustment = 3600;
//Try to stay within +/- 2ms of requested latency
if(stats.AverageLatency > requestedLatency + 2) {
return 0.995;
} else if(stats.AverageLatency < requestedLatency - 2) {
return 1.005;
int32_t requestedLatency = (int32_t)_settings->GetAudioLatency();
double latencyGap = stats.AverageLatency - requestedLatency;
double adjustment = std::min(0.0025, (std::ceil((std::abs(latencyGap) - maxGap) * 8)) * 0.00003125);
if(latencyGap < 0 && _underTarget < maxSubAdjustment) {
_underTarget++;
} else if(latencyGap > 0 && _underTarget > -maxSubAdjustment) {
_underTarget--;
}
//For every ~1 second spent under/over target latency, further adjust rate (GetTargetRate is called approx. 3x per frame)
//This should slowly get us closer to the actual output rate of the sound card
double subAdjustment = 0.00003125 * _underTarget / 180;
if(adjustment > 0) {
if(latencyGap > maxGap) {
_rateAdjustment = 1 - adjustment + subAdjustment;
} else if(latencyGap < -maxGap) {
_rateAdjustment = 1 + adjustment + subAdjustment;
}
} else if(std::abs(latencyGap) < 1) {
//Restore normal rate once we get within +/- 1ms
_rateAdjustment = 1.0 + subAdjustment;
}
} else {
_underTarget = 0;
_rateAdjustment = 1.0;
}
} else {
_underTarget = 0;
_rateAdjustment = 1.0;
}
return 1.0;
return _rateAdjustment;
}
void SoundMixer::UpdateTargetSampleRate()

View file

@ -52,6 +52,9 @@ private:
int16_t _previousOutputLeft = 0;
int16_t _previousOutputRight = 0;
double _rateAdjustment = 1.0;
int32_t _underTarget = 0;
vector<uint32_t> _timestamps;
int16_t _channelOutput[MaxChannelCount][CycleLength];
int16_t _currentOutput[MaxChannelCount];
@ -111,4 +114,5 @@ public:
AudioStatistics GetStatistics();
void ProcessEndOfFrame();
double GetRateAdjustment();
};

View file

@ -7,6 +7,8 @@ namespace Mesen.GUI.Config
public string AudioDevice = "";
public bool EnableAudio = true;
public bool DisableDynamicSampleRate = false;
[MinMax(15, 300)] public UInt32 AudioLatency = 60;
[MinMax(0, 100)] public UInt32 MasterVolume = 25;
@ -175,6 +177,7 @@ namespace Mesen.GUI.Config
InteropEmu.SetFlag(EmulationFlags.ReduceSoundInBackground, audioInfo.ReduceSoundInBackground);
InteropEmu.SetFlag(EmulationFlags.ReduceSoundInFastForward, audioInfo.ReduceSoundInFastForward);
InteropEmu.SetFlag(EmulationFlags.DisableDynamicSampleRate, audioInfo.DisableDynamicSampleRate);
InteropEmu.SetFlag(EmulationFlags.SwapDutyCycles, audioInfo.SwapDutyCycles);
InteropEmu.SetFlag(EmulationFlags.SilenceTriangleHighFreq, audioInfo.SilenceTriangleHighFreq);
InteropEmu.SetFlag(EmulationFlags.ReduceDmcPopping, audioInfo.ReduceDmcPopping);
@ -204,4 +207,12 @@ namespace Mesen.GUI.Config
TwinFamicom,
TwinFamicom60
}
public enum DynamicRateAdjustmentType
{
None = 0,
Low = 1,
Medium = 2,
High = 3
}
}

View file

@ -123,6 +123,7 @@
<Form ID="frmAudioConfig" Title="Opcions de so">
<Control ID="tpgGeneral">General</Control>
<Control ID="tpgAdvanced">Avançat</Control>
<Control ID="chkDisableDynamicSampleRate">Disable dynamic sample rate</Control>
<Control ID="chkSwapDutyCycles">Inverteix els cicles dels canals quadrats (imita clons antics)</Control>
<Control ID="chkSilenceTriangleHighFreq">Silencia les freqüències ultrasòniques al canal de triangle (redueix els "esclafits")</Control>
<Control ID="chkReduceDmcPopping">Redueix els "esclafits" al canal DMC</Control>

View file

@ -123,6 +123,7 @@
<Form ID="frmAudioConfig" Title="Audio Options">
<Control ID="tpgGeneral">General</Control>
<Control ID="tpgAdvanced">Advanced</Control>
<Control ID="chkDisableDynamicSampleRate">Disable dynamic sample rate</Control>
<Control ID="chkSwapDutyCycles">Swap square channels duty cycles (Mimics old clones)</Control>
<Control ID="chkSilenceTriangleHighFreq">Mute ultrasonic frequencies on triangle channel (reduces popping)</Control>
<Control ID="chkReduceDmcPopping">Reduce popping sounds on the DMC channel</Control>

View file

@ -122,6 +122,7 @@
<Form ID="frmAudioConfig" Title="Opciones de audio">
<Control ID="tpgGeneral">General</Control>
<Control ID="tpgAdvanced">Avanzado</Control>
<Control ID="chkDisableDynamicSampleRate">Disable dynamic sample rate</Control>
<Control ID="chkSwapDutyCycles">Invertir ciclos de canales cuadrados (imita viejos clones)</Control>
<Control ID="chkSilenceTriangleHighFreq">Cortar el canal de triángulo a frecuencias ultrasónicas (reduce los "chasquidos")</Control>
<Control ID="chkReduceDmcPopping">Reducir los "chasquidos" en el canal DMC</Control>

View file

@ -123,6 +123,7 @@
<Form ID="frmAudioConfig" Title="Options audio">
<Control ID="tpgGeneral">Général</Control>
<Control ID="tpgAdvanced">Avancé</Control>
<Control ID="chkDisableDynamicSampleRate">Désactiver le taux d'échantillonnage dynamique</Control>
<Control ID="chkSwapDutyCycles">Inverser le rapport cyclique des canaux Square 1 et Square 2</Control>
<Control ID="chkSilenceTriangleHighFreq">Couper le canal triangle lors de fréquences ultrasoniques (réduit les "pops")</Control>
<Control ID="chkReduceDmcPopping">Réduire l'intensité des "pops" du canal DMC</Control>

View file

@ -124,6 +124,7 @@
<Form ID="frmAudioConfig" Title="音声設定">
<Control ID="tpgGeneral">全般</Control>
<Control ID="tpgAdvanced">詳細設定</Control>
<Control ID="chkDisableDynamicSampleRate">サンプルレートの自動調整を無効にする</Control>
<Control ID="chkSwapDutyCycles">Squareチャンネルのデューティサイクルをスワップする</Control>
<Control ID="chkSilenceTriangleHighFreq">Triangleチャンネルが超音波を出す場合、ミュートする ポップイズを軽減する</Control>
<Control ID="chkReduceDmcPopping">DMCチャンネルのポップイズを軽減する</Control>

View file

@ -122,6 +122,7 @@
<Form ID="frmAudioConfig" Title="Opções de áudio">
<Control ID="tpgGeneral">Geral</Control>
<Control ID="tpgAdvanced">Avançado</Control>
<Control ID="chkDisableDynamicSampleRate">Disable dynamic sample rate</Control>
<Control ID="chkSwapDutyCycles">Inverter ciclos de canais quadrados (Imita velhos clones)</Control>
<Control ID="chkSilenceTriangleHighFreq">Cortar o canal de triângulo em frequências ultrassônicas (Reduz os "pops")</Control>
<Control ID="chkReduceDmcPopping">Reduzir a intensidade dos "pops" no canal DMC</Control>

View file

@ -122,6 +122,7 @@
<Form ID="frmAudioConfig" Title="Звук">
<Control ID="tpgGeneral">Общие</Control>
<Control ID="tpgAdvanced">Расширенные</Control>
<Control ID="chkDisableDynamicSampleRate">Disable dynamic sample rate</Control>
<Control ID="chkSwapDutyCycles">Перепутанная скважность (как на старых клонах)</Control>
<Control ID="chkSilenceTriangleHighFreq">Заглушать ультразвуковые частоты на канале triangle (уменьшает "треск")</Control>
<Control ID="chkReduceDmcPopping">Reduce popping sounds on the DMC channel</Control>

View file

@ -122,6 +122,7 @@
<Form ID="frmAudioConfig" Title="Звук">
<Control ID="tpgGeneral">Загальнi</Control>
<Control ID="tpgAdvanced">Розширені</Control>
<Control ID="chkDisableDynamicSampleRate">Disable dynamic sample rate</Control>
<Control ID="chkSwapDutyCycles">Переплутана шпаруватість (як на старих клонах)</Control>
<Control ID="chkSilenceTriangleHighFreq">Заглушати ультразвукові частоти на каналі triangle (зменшує "трiск")</Control>
<Control ID="chkReduceDmcPopping">Зменшити клацання на каналі DMC</Control>

View file

@ -136,6 +136,7 @@ namespace Mesen.GUI.Forms.Config
this.chkSilenceTriangleHighFreq = new System.Windows.Forms.CheckBox();
this.chkSwapDutyCycles = new Mesen.GUI.Controls.ctrlRiskyOption();
this.chkReduceDmcPopping = new System.Windows.Forms.CheckBox();
this.chkDisableDynamicSampleRate = new Mesen.GUI.Controls.ctrlRiskyOption();
this.baseConfigPanel.SuspendLayout();
this.grpVolume.SuspendLayout();
this.tableLayoutPanel1.SuspendLayout();
@ -1739,18 +1740,20 @@ namespace Mesen.GUI.Forms.Config
//
this.tableLayoutPanel3.ColumnCount = 1;
this.tableLayoutPanel3.ColumnStyles.Add(new System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel3.Controls.Add(this.chkDisableNoiseModeFlag, 0, 3);
this.tableLayoutPanel3.Controls.Add(this.chkDisableDynamicSampleRate, 0, 2);
this.tableLayoutPanel3.Controls.Add(this.chkDisableNoiseModeFlag, 0, 4);
this.tableLayoutPanel3.Controls.Add(this.chkSilenceTriangleHighFreq, 0, 0);
this.tableLayoutPanel3.Controls.Add(this.chkSwapDutyCycles, 0, 2);
this.tableLayoutPanel3.Controls.Add(this.chkSwapDutyCycles, 0, 3);
this.tableLayoutPanel3.Controls.Add(this.chkReduceDmcPopping, 0, 1);
this.tableLayoutPanel3.Dock = System.Windows.Forms.DockStyle.Fill;
this.tableLayoutPanel3.Location = new System.Drawing.Point(3, 3);
this.tableLayoutPanel3.Name = "tableLayoutPanel3";
this.tableLayoutPanel3.RowCount = 5;
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle());
this.tableLayoutPanel3.RowCount = 6;
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 24F));
this.tableLayoutPanel3.RowStyles.Add(new System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100F));
this.tableLayoutPanel3.Size = new System.Drawing.Size(463, 341);
this.tableLayoutPanel3.TabIndex = 1;
@ -1759,9 +1762,9 @@ namespace Mesen.GUI.Forms.Config
//
this.chkDisableNoiseModeFlag.Checked = false;
this.chkDisableNoiseModeFlag.Dock = System.Windows.Forms.DockStyle.Fill;
this.chkDisableNoiseModeFlag.Location = new System.Drawing.Point(0, 70);
this.chkDisableNoiseModeFlag.Location = new System.Drawing.Point(0, 96);
this.chkDisableNoiseModeFlag.Name = "chkDisableNoiseModeFlag";
this.chkDisableNoiseModeFlag.Size = new System.Drawing.Size(463, 23);
this.chkDisableNoiseModeFlag.Size = new System.Drawing.Size(463, 24);
this.chkDisableNoiseModeFlag.TabIndex = 3;
this.chkDisableNoiseModeFlag.Text = "Disable noise channel mode flag";
//
@ -1778,7 +1781,7 @@ namespace Mesen.GUI.Forms.Config
//
this.chkSwapDutyCycles.Checked = false;
this.chkSwapDutyCycles.Dock = System.Windows.Forms.DockStyle.Fill;
this.chkSwapDutyCycles.Location = new System.Drawing.Point(0, 46);
this.chkSwapDutyCycles.Location = new System.Drawing.Point(0, 72);
this.chkSwapDutyCycles.Name = "chkSwapDutyCycles";
this.chkSwapDutyCycles.Size = new System.Drawing.Size(463, 24);
this.chkSwapDutyCycles.TabIndex = 0;
@ -1788,7 +1791,7 @@ namespace Mesen.GUI.Forms.Config
//
this.chkReduceDmcPopping.AutoSize = true;
this.chkReduceDmcPopping.CheckAlign = System.Drawing.ContentAlignment.TopLeft;
this.chkReduceDmcPopping.Location = new System.Drawing.Point(3, 26);
this.chkReduceDmcPopping.Location = new System.Drawing.Point(3, 27);
this.chkReduceDmcPopping.Name = "chkReduceDmcPopping";
this.chkReduceDmcPopping.Size = new System.Drawing.Size(243, 17);
this.chkReduceDmcPopping.TabIndex = 2;
@ -1796,6 +1799,16 @@ namespace Mesen.GUI.Forms.Config
this.chkReduceDmcPopping.TextAlign = System.Drawing.ContentAlignment.TopLeft;
this.chkReduceDmcPopping.UseVisualStyleBackColor = true;
//
// chkDisableDynamicSampleRate
//
this.chkDisableDynamicSampleRate.Checked = false;
this.chkDisableDynamicSampleRate.Dock = System.Windows.Forms.DockStyle.Fill;
this.chkDisableDynamicSampleRate.Location = new System.Drawing.Point(0, 48);
this.chkDisableDynamicSampleRate.Name = "chkDisableDynamicSampleRate";
this.chkDisableDynamicSampleRate.Size = new System.Drawing.Size(463, 24);
this.chkDisableDynamicSampleRate.TabIndex = 4;
this.chkDisableDynamicSampleRate.Text = "Disable dynamic sample rate";
//
// frmAudioConfig
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -1964,5 +1977,6 @@ namespace Mesen.GUI.Forms.Config
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel8;
private ctrlHorizontalTrackbar trkVolumeReduction;
private System.Windows.Forms.CheckBox chkMuteSoundInBackground;
private ctrlRiskyOption chkDisableDynamicSampleRate;
}
}

View file

@ -60,6 +60,8 @@ namespace Mesen.GUI.Forms.Config
AddBinding("SampleRate", cboSampleRate);
AddBinding("AudioDevice", cboAudioDevice);
AddBinding("DisableDynamicSampleRate", chkDisableDynamicSampleRate);
AddBinding("EnableEqualizer", chkEnableEqualizer);
//TODO: Uncomment when equalizer presets are implemented
//AddBinding("EqualizerPreset", cboEqualizerPreset);

View file

@ -1512,6 +1512,8 @@ namespace Mesen.GUI
UseHdPacks = 0x20,
HasFourScore = 0x40,
DisableDynamicSampleRate = 0x80,
PauseOnMovieEnd = 0x0100,
PauseWhenInBackground = 0x0200,
AllowBackgroundInput = 0x0400,