Audio: Improved dynamic sample rate logic and added an option to disable it completely
This commit is contained in:
parent
1a7f07cff5
commit
115f46e05f
16 changed files with 96 additions and 21 deletions
|
@ -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);
|
||||
|
|
|
@ -19,6 +19,8 @@ enum EmulationFlags : uint64_t
|
|||
UseHdPacks = 0x20,
|
||||
HasFourScore = 0x40,
|
||||
|
||||
DisableDynamicSampleRate = 0x80,
|
||||
|
||||
PauseOnMovieEnd = 0x0100,
|
||||
PauseWhenInBackground = 0x0200,
|
||||
AllowBackgroundInput = 0x0400,
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
|
|
36
GUI.NET/Forms/Config/frmAudioConfig.Designer.cs
generated
36
GUI.NET/Forms/Config/frmAudioConfig.Designer.cs
generated
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -1512,6 +1512,8 @@ namespace Mesen.GUI
|
|||
UseHdPacks = 0x20,
|
||||
HasFourScore = 0x40,
|
||||
|
||||
DisableDynamicSampleRate = 0x80,
|
||||
|
||||
PauseOnMovieEnd = 0x0100,
|
||||
PauseWhenInBackground = 0x0200,
|
||||
AllowBackgroundInput = 0x0400,
|
||||
|
|
Loading…
Add table
Reference in a new issue