SoundOutputProvider: Standalone mode to eliminate the need for an external buffer when used with SyncToAsyncProvider.
This commit is contained in:
parent
a7f5bafb72
commit
c58d2929f8
|
@ -3142,7 +3142,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_currentSoundProvider.SetSyncMode(SyncSoundMode.Sync);
|
_currentSoundProvider.SetSyncMode(SyncSoundMode.Sync);
|
||||||
_aviSoundInputAsync = new SyncToAsyncProvider(_currentSoundProvider, 10.0);
|
_aviSoundInputAsync = new SyncToAsyncProvider(_currentSoundProvider);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_dumpProxy = new SimpleSyncSoundProvider();
|
_dumpProxy = new SimpleSyncSoundProvider();
|
||||||
|
|
|
@ -32,6 +32,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
private const int MinResamplingDistanceSamples = 3;
|
private const int MinResamplingDistanceSamples = 3;
|
||||||
|
|
||||||
private Queue<short> _buffer = new Queue<short>();
|
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> _extraCountHistory = new Queue<int>();
|
||||||
private Queue<int> _outputCountHistory = 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 short[] _resampleBuffer = new short[0];
|
||||||
private double _resampleLengthRoundingError;
|
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 ISoundProvider BaseSoundProvider { get; set; }
|
||||||
|
|
||||||
public void DiscardSamples()
|
public void DiscardSamples()
|
||||||
{
|
{
|
||||||
_buffer.Clear();
|
ResetBuffer();
|
||||||
_extraCountHistory.Clear();
|
_extraCountHistory.Clear();
|
||||||
_outputCountHistory.Clear();
|
_outputCountHistory.Clear();
|
||||||
_hardCorrectionHistory.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.
|
// To let us know about buffer underruns, rewinding, fast-forwarding, etc.
|
||||||
public void OnVolatility()
|
public void OnVolatility()
|
||||||
{
|
{
|
||||||
|
@ -91,7 +123,23 @@ namespace BizHawk.Client.EmuHawk
|
||||||
get { return SampleRate / Global.Emulator.CoreComm.VsyncRate; }
|
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)
|
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;
|
double scaleFactor = 1.0;
|
||||||
|
|
||||||
|
@ -108,9 +156,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
GetSamplesFromBase(ref scaleFactor);
|
GetSamplesFromBase(ref scaleFactor);
|
||||||
|
|
||||||
int bufferSampleCount = _buffer.Count / ChannelCount;
|
int bufferSampleCount = _buffer.Count / ChannelCount;
|
||||||
int extraSampleCount = bufferSampleCount - idealSampleCount;
|
int extraSampleCount = bufferSampleCount - _targetExtraSamples - idealSampleCount;
|
||||||
int maxSamplesDeficit = _extraCountHistory.Count >= UsableHistoryLength ?
|
int maxSamplesDeficit = _extraCountHistory.Count >= UsableHistoryLength ?
|
||||||
MaxSamplesDeficit : Math.Min(StartupMaxSamplesSurplusDeficit, MaxSamplesDeficit);
|
EffectiveMaxSamplesDeficit : Math.Min(StartupMaxSamplesSurplusDeficit, EffectiveMaxSamplesDeficit);
|
||||||
int maxSamplesSurplus = _extraCountHistory.Count >= UsableHistoryLength ?
|
int maxSamplesSurplus = _extraCountHistory.Count >= UsableHistoryLength ?
|
||||||
MaxSamplesSurplus : Math.Min(StartupMaxSamplesSurplusDeficit, MaxSamplesSurplus);
|
MaxSamplesSurplus : Math.Min(StartupMaxSamplesSurplusDeficit, MaxSamplesSurplus);
|
||||||
bool hardCorrected = false;
|
bool hardCorrected = false;
|
||||||
|
@ -137,7 +185,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
|
|
||||||
bufferSampleCount = _buffer.Count / ChannelCount;
|
bufferSampleCount = _buffer.Count / ChannelCount;
|
||||||
extraSampleCount = bufferSampleCount - idealSampleCount;
|
extraSampleCount = bufferSampleCount - _targetExtraSamples - idealSampleCount;
|
||||||
|
|
||||||
int outputSampleCount = Math.Min(idealSampleCount, bufferSampleCount);
|
int outputSampleCount = Math.Min(idealSampleCount, bufferSampleCount);
|
||||||
|
|
||||||
|
@ -154,9 +202,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
scaleFactor);
|
scaleFactor);
|
||||||
}
|
}
|
||||||
|
|
||||||
sampleCount = outputSampleCount;
|
return outputSampleCount;
|
||||||
samples = GetOutputBuffer(sampleCount);
|
|
||||||
GetSamplesFromBuffer(samples, sampleCount);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GetSamplesFromBase(ref double scaleFactor)
|
private void GetSamplesFromBase(ref double scaleFactor)
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
using BizHawk.Emulation.Common;
|
using BizHawk.Emulation.Common;
|
||||||
|
|
||||||
|
@ -7,41 +6,16 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
public class SyncToAsyncProvider : ISoundProvider
|
public class SyncToAsyncProvider : ISoundProvider
|
||||||
{
|
{
|
||||||
private const int SampleRate = 44100;
|
private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider(standaloneMode: true);
|
||||||
private const int ChannelCount = 2;
|
|
||||||
private const double MinExpectedFrameRate = 50;
|
|
||||||
|
|
||||||
private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider();
|
public SyncToAsyncProvider(ISoundProvider baseProvider)
|
||||||
private readonly int _bufferSizeSamples;
|
|
||||||
private readonly Queue<short> _buffer;
|
|
||||||
|
|
||||||
public SyncToAsyncProvider(ISoundProvider baseProvider, double bufferSizeMs)
|
|
||||||
{
|
{
|
||||||
_outputProvider.BaseSoundProvider = 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()
|
public void DiscardSamples()
|
||||||
{
|
{
|
||||||
_buffer.Clear();
|
|
||||||
_outputProvider.DiscardSamples();
|
_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
|
public bool CanProvideAsync
|
||||||
|
@ -67,17 +41,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
throw new InvalidOperationException("Sync mode is not supported.");
|
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);
|
_outputProvider.GetSamples(samples);
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue