Allow CPU throttle to catch up for more than 3 missed frames if the user has configured a large sound buffer size.
SoundOutputProvider: Better workaround for cores whose audio output goes dead (0 samples) at times. Better underrun handling. Different threshold for samples surplus vs. deficit.
This commit is contained in:
parent
09072acb3b
commit
58cd9796f2
|
@ -22,6 +22,11 @@ namespace BizHawk.Client.Common
|
|||
/// </summary>
|
||||
public static bool DisableSecondaryThrottling;
|
||||
|
||||
/// <summary>
|
||||
/// How far the sound output buffer can go below full before drastic corrective measures are taken.
|
||||
/// </summary>
|
||||
public static int SoundMaxBufferDeficitMs;
|
||||
|
||||
public static Controller NullControls;
|
||||
public static AutofireController AutofireNullControls;
|
||||
|
||||
|
|
|
@ -124,14 +124,17 @@ namespace BizHawk.Client.EmuHawk
|
|||
_lastWriteTime = 0;
|
||||
_lastWriteCursor = 0;
|
||||
|
||||
int minBufferFullnessSamples = MillisecondsToSamples(
|
||||
int minBufferFullnessMs =
|
||||
Global.Config.SoundBufferSizeMs < 80 ? 35 :
|
||||
Global.Config.SoundBufferSizeMs < 100 ? 45 :
|
||||
55);
|
||||
55;
|
||||
|
||||
_outputProvider = new SoundOutputProvider();
|
||||
_outputProvider.MaxSamplesDeficit = BufferSizeSamples - minBufferFullnessSamples;
|
||||
_outputProvider.MaxSamplesDeficit = BufferSizeSamples - MillisecondsToSamples(minBufferFullnessMs);
|
||||
_outputProvider.BaseSoundProvider = _syncSoundProvider;
|
||||
|
||||
Global.SoundMaxBufferDeficitMs = Global.Config.SoundBufferSizeMs - minBufferFullnessMs;
|
||||
|
||||
//LogUnderruns = true;
|
||||
//_outputProvider.LogDebug = true;
|
||||
}
|
||||
|
@ -147,6 +150,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
_outputProvider = null;
|
||||
|
||||
Global.SoundMaxBufferDeficitMs = 0;
|
||||
|
||||
BufferSizeSamples = 0;
|
||||
}
|
||||
|
||||
|
@ -224,6 +229,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
{
|
||||
if (LogUnderruns) Console.WriteLine("DirectSound underrun detected!");
|
||||
detectedUnderrun = true;
|
||||
_outputProvider.OnUnderrun();
|
||||
}
|
||||
}
|
||||
bool isInitializing = _actualWriteOffsetBytes == -1;
|
||||
|
|
|
@ -21,15 +21,12 @@ namespace BizHawk.Client.EmuHawk
|
|||
private const int SampleRate = 44100;
|
||||
private const int ChannelCount = 2;
|
||||
private const int SoftCorrectionThresholdSamples = 5 * SampleRate / 1000;
|
||||
private const int StartupHardCorrectionThresholdSamples = 10 * SampleRate / 1000;
|
||||
// Can't go more than 50 ms or so even if there's enough buffered up in the device
|
||||
// because the clock throttle loses its "rubber band" effect and won't replenish
|
||||
// our buffer. At that point it's better to hard correct than to let the soft
|
||||
// correction cause a major pitch shift.
|
||||
private const int NormalHardCorrectionThresholdSamples = 50 * SampleRate / 1000;
|
||||
private const int StartupMaxSamplesSurplusDeficit = 10 * SampleRate / 1000;
|
||||
private const int MaxSamplesSurplus = 50 * SampleRate / 1000;
|
||||
private const int UsableHistoryLength = 20;
|
||||
private const int MaxHistoryLength = 60;
|
||||
private const int SoftCorrectionLength = 240;
|
||||
private const int BaseMaxConsecutiveEmptyFrames = 1;
|
||||
private const int BaseSampleRateUsableHistoryLength = 60;
|
||||
private const int BaseSampleRateMaxHistoryLength = 300;
|
||||
private const int MinResamplingDistanceSamples = 3;
|
||||
|
@ -40,6 +37,9 @@ namespace BizHawk.Client.EmuHawk
|
|||
private Queue<int> _outputCountHistory = new Queue<int>();
|
||||
private Queue<bool> _hardCorrectionHistory = new Queue<bool>();
|
||||
|
||||
private int _baseConsecutiveEmptyFrames;
|
||||
private Queue<bool> _baseEmptyFrameCorrectionHistory = new Queue<bool>();
|
||||
|
||||
private double _lastAdvertisedSamplesPerFrame;
|
||||
private Queue<int> _baseSamplesPerFrame = new Queue<int>();
|
||||
|
||||
|
@ -62,6 +62,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
_extraCountHistory.Clear();
|
||||
_outputCountHistory.Clear();
|
||||
_hardCorrectionHistory.Clear();
|
||||
_baseConsecutiveEmptyFrames = 0;
|
||||
_baseEmptyFrameCorrectionHistory.Clear();
|
||||
_lastAdvertisedSamplesPerFrame = 0.0;
|
||||
_baseSamplesPerFrame.Clear();
|
||||
_outputBuffer = new short[0];
|
||||
|
@ -74,6 +76,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
}
|
||||
|
||||
public void OnUnderrun()
|
||||
{
|
||||
_extraCountHistory.Clear();
|
||||
_outputCountHistory.Clear();
|
||||
_hardCorrectionHistory.Clear();
|
||||
}
|
||||
|
||||
public bool LogDebug { get; set; }
|
||||
|
||||
private double AdvertisedSamplesPerFrame
|
||||
|
@ -87,7 +96,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
if (_extraCountHistory.Count >= UsableHistoryLength && !_hardCorrectionHistory.Any(c => c))
|
||||
{
|
||||
double offsetFromTarget = _extraCountHistory.Average();
|
||||
double offsetFromTarget = CalculatePowerMean(_extraCountHistory, 0.6);
|
||||
if (Math.Abs(offsetFromTarget) > SoftCorrectionThresholdSamples)
|
||||
{
|
||||
double correctionSpan = _outputCountHistory.Average() * SoftCorrectionLength;
|
||||
|
@ -99,12 +108,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
int bufferSampleCount = _buffer.Count / ChannelCount;
|
||||
int extraSampleCount = bufferSampleCount - idealSampleCount;
|
||||
int hardCorrectionThresholdSamples = _extraCountHistory.Count >= UsableHistoryLength ?
|
||||
Math.Min(NormalHardCorrectionThresholdSamples, MaxSamplesDeficit) :
|
||||
Math.Min(StartupHardCorrectionThresholdSamples, MaxSamplesDeficit);
|
||||
int maxSamplesDeficit = _extraCountHistory.Count >= UsableHistoryLength ?
|
||||
MaxSamplesDeficit : Math.Min(StartupMaxSamplesSurplusDeficit, MaxSamplesDeficit);
|
||||
int maxSamplesSurplus = _extraCountHistory.Count >= UsableHistoryLength ?
|
||||
MaxSamplesSurplus : Math.Min(StartupMaxSamplesSurplusDeficit, MaxSamplesSurplus);
|
||||
bool hardCorrected = false;
|
||||
|
||||
if (extraSampleCount < -hardCorrectionThresholdSamples)
|
||||
if (extraSampleCount < -maxSamplesDeficit)
|
||||
{
|
||||
int generateSampleCount = -extraSampleCount;
|
||||
if (LogDebug) Console.WriteLine("Generating " + generateSampleCount + " samples");
|
||||
|
@ -114,7 +124,7 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
hardCorrected = true;
|
||||
}
|
||||
else if (extraSampleCount > hardCorrectionThresholdSamples)
|
||||
else if (extraSampleCount > maxSamplesSurplus)
|
||||
{
|
||||
int discardSampleCount = extraSampleCount;
|
||||
if (LogDebug) Console.WriteLine("Discarding " + discardSampleCount + " samples");
|
||||
|
@ -136,8 +146,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
if (LogDebug)
|
||||
{
|
||||
Console.WriteLine("Avg: {0:0.0} ms, Min: {1:0.0} ms, Max: {2:0.0} ms, Scale: {3:0.0000}",
|
||||
_extraCountHistory.Average() * 1000.0 / SampleRate,
|
||||
Console.WriteLine("Avg: {0:0.0} ms, Min: {1:0.0}, Max: {2:0.0}, Scale: {3:0.0000}",
|
||||
CalculatePowerMean(_extraCountHistory, 0.6) * 1000.0 / SampleRate,
|
||||
_extraCountHistory.Min() * 1000.0 / SampleRate,
|
||||
_extraCountHistory.Max() * 1000.0 / SampleRate,
|
||||
scaleFactor);
|
||||
|
@ -155,6 +165,24 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
BaseSoundProvider.GetSamples(out samples, out count);
|
||||
|
||||
bool correctedEmptyFrame = false;
|
||||
if (count == 0)
|
||||
{
|
||||
_baseConsecutiveEmptyFrames++;
|
||||
if (_baseConsecutiveEmptyFrames > BaseMaxConsecutiveEmptyFrames)
|
||||
{
|
||||
int silenceCount = (int)Math.Round(AdvertisedSamplesPerFrame);
|
||||
samples = Resample(samples, count, silenceCount);
|
||||
count = silenceCount;
|
||||
correctedEmptyFrame = true;
|
||||
}
|
||||
}
|
||||
else if (_baseConsecutiveEmptyFrames != 0)
|
||||
{
|
||||
_baseConsecutiveEmptyFrames = 0;
|
||||
}
|
||||
UpdateHistory(_baseEmptyFrameCorrectionHistory, correctedEmptyFrame, MaxHistoryLength);
|
||||
|
||||
if (AdvertisedSamplesPerFrame != _lastAdvertisedSamplesPerFrame)
|
||||
{
|
||||
_baseSamplesPerFrame.Clear();
|
||||
|
@ -162,7 +190,8 @@ namespace BizHawk.Client.EmuHawk
|
|||
}
|
||||
UpdateHistory(_baseSamplesPerFrame, count, BaseSampleRateMaxHistoryLength);
|
||||
|
||||
if (_baseSamplesPerFrame.Count >= BaseSampleRateUsableHistoryLength && !_baseSamplesPerFrame.Any(n => n == 0))
|
||||
if (_baseSamplesPerFrame.Count >= BaseSampleRateUsableHistoryLength &&
|
||||
!_baseEmptyFrameCorrectionHistory.Any(n => n))
|
||||
{
|
||||
double baseAverageSamplesPerFrame = _baseSamplesPerFrame.Average();
|
||||
if (baseAverageSamplesPerFrame != 0.0)
|
||||
|
@ -192,7 +221,13 @@ namespace BizHawk.Client.EmuHawk
|
|||
AddSamplesToBuffer(samples, count);
|
||||
}
|
||||
|
||||
private void UpdateHistory<T>(Queue<T> queue, T value, int maxLength)
|
||||
private static double CalculatePowerMean(IEnumerable<int> values, double power)
|
||||
{
|
||||
double x = values.Average(n => Math.Pow(Math.Abs(n), power) * Math.Sign(n));
|
||||
return Math.Pow(Math.Abs(x), 1.0 / power) * Math.Sign(x);
|
||||
}
|
||||
|
||||
private static void UpdateHistory<T>(Queue<T> queue, T value, int maxLength)
|
||||
{
|
||||
queue.Enqueue(value);
|
||||
while (queue.Count > maxLength)
|
||||
|
|
|
@ -321,7 +321,11 @@ namespace BizHawk.Client.EmuHawk
|
|||
|
||||
if (elapsedTime >= timePerFrame)
|
||||
{
|
||||
if (elapsedTime >= timePerFrame * 4)
|
||||
int maxMissedFrames = (int)Math.Ceiling((Global.SoundMaxBufferDeficitMs / 1000.0) / ((double)timePerFrame / afsfreq));
|
||||
if (maxMissedFrames < 3)
|
||||
maxMissedFrames = 3;
|
||||
|
||||
if (elapsedTime > timePerFrame * (ulong)(1 + maxMissedFrames))
|
||||
ltime = ttime;
|
||||
else
|
||||
ltime += timePerFrame;
|
||||
|
|
Loading…
Reference in New Issue