SoundOutputProvider: Standalone mode to eliminate the need for an external buffer when used with SyncToAsyncProvider.

This commit is contained in:
J.D. Purcell 2017-03-25 16:26:46 -04:00
parent a7f5bafb72
commit c58d2929f8
3 changed files with 60 additions and 48 deletions

View File

@ -3142,7 +3142,7 @@ namespace BizHawk.Client.EmuHawk
else
{
_currentSoundProvider.SetSyncMode(SyncSoundMode.Sync);
_aviSoundInputAsync = new SyncToAsyncProvider(_currentSoundProvider, 10.0);
_aviSoundInputAsync = new SyncToAsyncProvider(_currentSoundProvider);
}
}
_dumpProxy = new SimpleSyncSoundProvider();

View File

@ -32,6 +32,9 @@ namespace BizHawk.Client.EmuHawk
private const int MinResamplingDistanceSamples = 3;
private Queue<short> _buffer = new Queue<short>();
private bool _standaloneMode;
private int _targetExtraSamples;
private int _maxSamplesDeficit;
private Queue<int> _extraCountHistory = new Queue<int>();
private Queue<int> _outputCountHistory = new Queue<int>();
@ -48,17 +51,37 @@ namespace BizHawk.Client.EmuHawk
private short[] _resampleBuffer = new short[0];
private double _resampleLengthRoundingError;
public SoundOutputProvider()
public SoundOutputProvider(bool standaloneMode = false)
{
_standaloneMode = standaloneMode;
if (_standaloneMode)
{
const double targetExtraMs = 10.0;
_targetExtraSamples = (int)Math.Ceiling(targetExtraMs * SampleRate / 1000.0);
}
ResetBuffer();
}
public int MaxSamplesDeficit { get; set; }
public int MaxSamplesDeficit
{
get { return _maxSamplesDeficit; }
set
{
if (_standaloneMode) throw new InvalidOperationException();
_maxSamplesDeficit = value;
}
}
private int EffectiveMaxSamplesDeficit
{
get { return _maxSamplesDeficit + _targetExtraSamples; }
}
public ISoundProvider BaseSoundProvider { get; set; }
public void DiscardSamples()
{
_buffer.Clear();
ResetBuffer();
_extraCountHistory.Clear();
_outputCountHistory.Clear();
_hardCorrectionHistory.Clear();
@ -76,6 +99,15 @@ namespace BizHawk.Client.EmuHawk
}
}
private void ResetBuffer()
{
_buffer.Clear();
for (int i = 0; i < _targetExtraSamples * ChannelCount; i++)
{
_buffer.Enqueue(0);
}
}
// To let us know about buffer underruns, rewinding, fast-forwarding, etc.
public void OnVolatility()
{
@ -91,7 +123,23 @@ namespace BizHawk.Client.EmuHawk
get { return SampleRate / Global.Emulator.CoreComm.VsyncRate; }
}
public void GetSamples(short[] samples)
{
if (!_standaloneMode) throw new InvalidOperationException();
int returnSampleCount = samples.Length / ChannelCount;
GetSamples(returnSampleCount);
GetSamplesFromBuffer(samples, returnSampleCount);
}
public void GetSamples(int idealSampleCount, out short[] samples, out int sampleCount)
{
if (_standaloneMode) throw new InvalidOperationException();
sampleCount = GetSamples(idealSampleCount);
samples = GetOutputBuffer(sampleCount);
GetSamplesFromBuffer(samples, sampleCount);
}
private int GetSamples(int idealSampleCount)
{
double scaleFactor = 1.0;
@ -108,9 +156,9 @@ namespace BizHawk.Client.EmuHawk
GetSamplesFromBase(ref scaleFactor);
int bufferSampleCount = _buffer.Count / ChannelCount;
int extraSampleCount = bufferSampleCount - idealSampleCount;
int extraSampleCount = bufferSampleCount - _targetExtraSamples - idealSampleCount;
int maxSamplesDeficit = _extraCountHistory.Count >= UsableHistoryLength ?
MaxSamplesDeficit : Math.Min(StartupMaxSamplesSurplusDeficit, MaxSamplesDeficit);
EffectiveMaxSamplesDeficit : Math.Min(StartupMaxSamplesSurplusDeficit, EffectiveMaxSamplesDeficit);
int maxSamplesSurplus = _extraCountHistory.Count >= UsableHistoryLength ?
MaxSamplesSurplus : Math.Min(StartupMaxSamplesSurplusDeficit, MaxSamplesSurplus);
bool hardCorrected = false;
@ -137,7 +185,7 @@ namespace BizHawk.Client.EmuHawk
}
bufferSampleCount = _buffer.Count / ChannelCount;
extraSampleCount = bufferSampleCount - idealSampleCount;
extraSampleCount = bufferSampleCount - _targetExtraSamples - idealSampleCount;
int outputSampleCount = Math.Min(idealSampleCount, bufferSampleCount);
@ -154,9 +202,7 @@ namespace BizHawk.Client.EmuHawk
scaleFactor);
}
sampleCount = outputSampleCount;
samples = GetOutputBuffer(sampleCount);
GetSamplesFromBuffer(samples, sampleCount);
return outputSampleCount;
}
private void GetSamplesFromBase(ref double scaleFactor)

View File

@ -1,5 +1,4 @@
using System;
using System.Collections.Generic;
using BizHawk.Emulation.Common;
@ -7,41 +6,16 @@ namespace BizHawk.Client.EmuHawk
{
public class SyncToAsyncProvider : ISoundProvider
{
private const int SampleRate = 44100;
private const int ChannelCount = 2;
private const double MinExpectedFrameRate = 50;
private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider(standaloneMode: true);
private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider();
private readonly int _bufferSizeSamples;
private readonly Queue<short> _buffer;
public SyncToAsyncProvider(ISoundProvider baseProvider, double bufferSizeMs)
public SyncToAsyncProvider(ISoundProvider baseProvider)
{
_outputProvider.BaseSoundProvider = baseProvider;
_bufferSizeSamples = (int)Math.Ceiling(bufferSizeMs * SampleRate / 1000.0);
_buffer = new Queue<short>((_bufferSizeSamples + (int)Math.Ceiling(SampleRate / MinExpectedFrameRate)) * ChannelCount);
DiscardSamples();
}
public void DiscardSamples()
{
_buffer.Clear();
_outputProvider.DiscardSamples();
for (int i = 0; i < _bufferSizeSamples * ChannelCount; i++)
{
_buffer.Enqueue(0);
}
}
public void GetSamplesAsync(short[] samples)
{
GetSamplesFromBase(samples.Length / ChannelCount);
for (int i = 0; i < samples.Length; i++)
{
samples[i] = _buffer.Dequeue();
}
}
public bool CanProvideAsync
@ -67,17 +41,9 @@ namespace BizHawk.Client.EmuHawk
throw new InvalidOperationException("Sync mode is not supported.");
}
private void GetSamplesFromBase(int minResultingSampleCount)
public void GetSamplesAsync(short[] samples)
{
int idealSampleCount = Math.Max(_bufferSizeSamples + minResultingSampleCount - (_buffer.Count / ChannelCount), 0);
short[] samples;
int samplesProvided;
_outputProvider.MaxSamplesDeficit = _bufferSizeSamples;
_outputProvider.GetSamples(idealSampleCount, out samples, out samplesProvided);
for (int i = 0; i < samplesProvided * ChannelCount; i++)
{
_buffer.Enqueue(samples[i]);
}
_outputProvider.GetSamples(samples);
}
}
}