Audio: Improve sound latency - can be set 4-5x lower than before without crackling sounds

This commit is contained in:
Souryo 2016-12-09 10:30:09 -05:00
parent cad8995696
commit 3f3b3ffb3c
10 changed files with 81 additions and 36 deletions

View file

@ -14,7 +14,7 @@
class SoundMixer : public Snapshotable
{
public:
static const uint32_t CycleLength = 10000;
static const uint32_t CycleLength = 1000;
static const uint32_t BitsPerSample = 16;
private:

View file

@ -123,6 +123,7 @@
<Control ID="chkEnableAudio">Activar el sonido</Control>
<Control ID="lblSampleRate">Frecuencia de muestreo:</Control>
<Control ID="lblLatencyMs">ms</Control>
<Control ID="lblLatencyWarning">Low values may cause sound problems</Control>
<Control ID="lblAudioLatency">Latencia:</Control>
<Control ID="lblAudioDevice">Dispositivo:</Control>
<Control ID="tpgVolume">Volumen</Control>

View file

@ -123,6 +123,7 @@
<Control ID="chkEnableAudio">Activer le son</Control>
<Control ID="lblSampleRate">Taux d'échantillonnage:</Control>
<Control ID="lblLatencyMs">ms</Control>
<Control ID="lblLatencyWarning">Une latence trop faible peut causer des problèmes</Control>
<Control ID="lblAudioLatency">Latence:</Control>
<Control ID="lblAudioDevice">Périphérique:</Control>
<Control ID="tpgVolume">Volume</Control>

View file

@ -123,6 +123,7 @@
<Control ID="chkEnableAudio">音声オン</Control>
<Control ID="lblSampleRate">サンプルレート:</Control>
<Control ID="lblLatencyMs">ミリ秒</Control>
<Control ID="lblLatencyWarning">低すぎる場合は、音声にノイズが発生することがある</Control>
<Control ID="lblAudioLatency">レイテンシ:</Control>
<Control ID="lblAudioDevice">デバイス:</Control>
<Control ID="tpgVolume">音量ミキサー</Control>

View file

@ -123,6 +123,7 @@
<Control ID="chkEnableAudio">Включить звук</Control>
<Control ID="lblSampleRate">Частота дискретизации:</Control>
<Control ID="lblLatencyMs">мс</Control>
<Control ID="lblLatencyWarning">Low values may cause sound problems</Control>
<Control ID="lblAudioLatency">Задержка:</Control>
<Control ID="lblAudioDevice">Устройство:</Control>
<Control ID="tpgVolume">Громкость</Control>

View file

@ -123,6 +123,7 @@
<Control ID="chkEnableAudio">Увімкнути звук</Control>
<Control ID="lblSampleRate">Частота дискретизації:</Control>
<Control ID="lblLatencyMs">мс</Control>
<Control ID="lblLatencyWarning">Low values may cause sound problems</Control>
<Control ID="lblAudioLatency">Затримка:</Control>
<Control ID="lblAudioDevice">Пристрій:</Control>
<Control ID="tpgVolume">Гучність</Control>

View file

@ -49,6 +49,8 @@
this.flowLayoutPanel2 = new System.Windows.Forms.FlowLayoutPanel();
this.nudLatency = new System.Windows.Forms.NumericUpDown();
this.lblLatencyMs = new System.Windows.Forms.Label();
this.picLatencyWarning = new System.Windows.Forms.PictureBox();
this.lblLatencyWarning = new System.Windows.Forms.Label();
this.lblAudioLatency = new System.Windows.Forms.Label();
this.cboSampleRate = new System.Windows.Forms.ComboBox();
this.lblAudioDevice = new System.Windows.Forms.Label();
@ -88,6 +90,7 @@
this.tableLayoutPanel2.SuspendLayout();
this.flowLayoutPanel2.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.nudLatency)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.picLatencyWarning)).BeginInit();
this.tabMain.SuspendLayout();
this.tpgGeneral.SuspendLayout();
this.tpgVolume.SuspendLayout();
@ -401,10 +404,13 @@
//
this.flowLayoutPanel2.Controls.Add(this.nudLatency);
this.flowLayoutPanel2.Controls.Add(this.lblLatencyMs);
this.flowLayoutPanel2.Controls.Add(this.picLatencyWarning);
this.flowLayoutPanel2.Controls.Add(this.lblLatencyWarning);
this.flowLayoutPanel2.Dock = System.Windows.Forms.DockStyle.Fill;
this.flowLayoutPanel2.Location = new System.Drawing.Point(77, 80);
this.flowLayoutPanel2.Margin = new System.Windows.Forms.Padding(0);
this.flowLayoutPanel2.Name = "flowLayoutPanel2";
this.flowLayoutPanel2.Size = new System.Drawing.Size(78, 24);
this.flowLayoutPanel2.Size = new System.Drawing.Size(386, 24);
this.flowLayoutPanel2.TabIndex = 4;
//
// nudLatency
@ -416,7 +422,7 @@
0,
0});
this.nudLatency.Minimum = new decimal(new int[] {
100,
15,
0,
0,
0});
@ -428,6 +434,7 @@
0,
0,
0});
this.nudLatency.ValueChanged += new System.EventHandler(this.nudLatency_ValueChanged);
//
// lblLatencyMs
//
@ -439,6 +446,28 @@
this.lblLatencyMs.TabIndex = 2;
this.lblLatencyMs.Text = "ms";
//
// picLatencyWarning
//
this.picLatencyWarning.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.picLatencyWarning.Image = global::Mesen.GUI.Properties.Resources.Warning;
this.picLatencyWarning.Location = new System.Drawing.Point(82, 5);
this.picLatencyWarning.Margin = new System.Windows.Forms.Padding(5, 3, 0, 3);
this.picLatencyWarning.Name = "picLatencyWarning";
this.picLatencyWarning.Size = new System.Drawing.Size(16, 16);
this.picLatencyWarning.TabIndex = 3;
this.picLatencyWarning.TabStop = false;
//
// lblLatencyWarning
//
this.lblLatencyWarning.Anchor = System.Windows.Forms.AnchorStyles.Left;
this.lblLatencyWarning.AutoSize = true;
this.lblLatencyWarning.Location = new System.Drawing.Point(98, 6);
this.lblLatencyWarning.Margin = new System.Windows.Forms.Padding(0, 0, 3, 0);
this.lblLatencyWarning.Name = "lblLatencyWarning";
this.lblLatencyWarning.Size = new System.Drawing.Size(192, 13);
this.lblLatencyWarning.TabIndex = 4;
this.lblLatencyWarning.Text = "Low values may cause sound problems";
//
// lblAudioLatency
//
this.lblAudioLatency.Anchor = System.Windows.Forms.AnchorStyles.Left;
@ -860,6 +889,7 @@
this.flowLayoutPanel2.ResumeLayout(false);
this.flowLayoutPanel2.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.nudLatency)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.picLatencyWarning)).EndInit();
this.tabMain.ResumeLayout(false);
this.tpgGeneral.ResumeLayout(false);
this.tpgVolume.ResumeLayout(false);
@ -943,5 +973,7 @@
private System.Windows.Forms.TrackBar trkReverbStrength;
private System.Windows.Forms.CheckBox chkSilenceTriangleHighFreq;
private System.Windows.Forms.CheckBox chkReduceDmcPopping;
private System.Windows.Forms.PictureBox picLatencyWarning;
private System.Windows.Forms.Label lblLatencyWarning;
}
}

View file

@ -86,5 +86,11 @@ namespace Mesen.GUI.Forms.Config
{
chkReduceSoundInBackground.Enabled = !chkMuteSoundInBackground.Checked;
}
private void nudLatency_ValueChanged(object sender, EventArgs e)
{
picLatencyWarning.Visible = nudLatency.Value <= 30;
lblLatencyWarning.Visible = nudLatency.Value <= 30;
}
}
}

View file

@ -170,6 +170,15 @@ namespace Mesen.GUI.Forms
Version oldVersion = new Version(ConfigManager.Config.MesenVersion);
if(oldVersion < newVersion) {
//Upgrade
if(oldVersion <= new Version("5.3.0")) {
//Version 0.5.3-
//Reduce sound latency if still using default
if(ConfigManager.Config.AudioInfo.AudioLatency == 100) {
//50ms is a fairly safe number - seems to work fine as low as 20ms (varies by computer)
ConfigManager.Config.AudioInfo.AudioLatency = 50;
}
}
if(oldVersion <= new Version("0.4.1")) {
//Version 0.4.1-
//Remove all old cheats (Game matching/CRC logic has been changed and no longer compatible)

View file

@ -241,44 +241,37 @@ void SoundManager::PlayBuffer(int16_t *soundBuffer, uint32_t sampleCount, uint32
uint32_t soundBufferSize = sampleCount * bytesPerSample;
int32_t byteLatency = (int32_t)((float)(sampleRate * EmulationSettings::GetAudioLatency()) / 1000.0f * bytesPerSample);
DWORD currentPlayCursor;
DWORD safeWriteCursor;
_secondaryBuffer->GetCurrentPosition(&currentPlayCursor, &safeWriteCursor);
if(safeWriteCursor > _lastWriteOffset && safeWriteCursor - _lastWriteOffset < 10000) {
_lastWriteOffset = (uint16_t)safeWriteCursor;
}
int32_t playWriteByteLatency = (_lastWriteOffset - currentPlayCursor);
if(playWriteByteLatency < 0) {
playWriteByteLatency = 0xFFFF + playWriteByteLatency;
}
if(byteLatency != _previousLatency) {
Stop();
_previousLatency = byteLatency;
} else if(playWriteByteLatency > byteLatency * 3) {
_secondaryBuffer->SetCurrentPosition(_lastWriteOffset - byteLatency);
}
uint32_t targetRate = sampleRate;
if(EmulationSettings::GetEmulationSpeed() > 0 && EmulationSettings::GetEmulationSpeed() < 100) {
targetRate = (uint32_t)(targetRate * ((double)EmulationSettings::GetEmulationSpeed() / 100.0));
}
_secondaryBuffer->SetFrequency(targetRate);
CopyToSecondaryBuffer((uint8_t*)soundBuffer, soundBufferSize);
DWORD status;
_secondaryBuffer->GetStatus(&status);
if(!(status & DSBSTATUS_PLAYING)) {
CopyToSecondaryBuffer((uint8_t*)soundBuffer, soundBufferSize);
if(_lastWriteOffset >= byteLatency) {
Play();
}
} else {
CopyToSecondaryBuffer((uint8_t*)soundBuffer, soundBufferSize);
DWORD currentPlayCursor;
_secondaryBuffer->GetCurrentPosition(&currentPlayCursor, nullptr);
int32_t playWriteByteLatency = (_lastWriteOffset - currentPlayCursor);
if(playWriteByteLatency < 0) {
playWriteByteLatency = 0xFFFF - currentPlayCursor + _lastWriteOffset;
}
int32_t latencyGap = playWriteByteLatency - byteLatency;
int32_t tolerance = byteLatency / 35;
uint32_t targetRate = sampleRate;
if(EmulationSettings::GetEmulationSpeed() > 0 && EmulationSettings::GetEmulationSpeed() < 100) {
targetRate = (uint32_t)(targetRate * ((double)EmulationSettings::GetEmulationSpeed() / 100.0));
}
if(abs(latencyGap) > byteLatency / 2) {
//Out of sync, move back to where we should be (start of the latency buffer)
_secondaryBuffer->SetCurrentPosition(_lastWriteOffset - byteLatency);
} else if(latencyGap < -tolerance) {
//Playing too fast, slow down playing
targetRate = (uint32_t)(targetRate * 0.9975);
} else if(latencyGap > tolerance) {
//Playing too slow, speed up
targetRate = (uint32_t)(targetRate * 1.0025);
}
_secondaryBuffer->SetFrequency((DWORD)targetRate);
if(!(status & DSBSTATUS_PLAYING) && _lastWriteOffset >= byteLatency) {
Play();
}
}