Fix sound bugs + cleanup.

This commit is contained in:
J.D. Purcell 2016-12-14 21:19:46 -05:00
parent 206ea9887b
commit 84480e4235
4 changed files with 62 additions and 86 deletions

View File

@ -1616,17 +1616,13 @@ namespace BizHawk.Client.EmuHawk
{ {
// we're video dumping, so async mode only and use the DumpProxy. // we're video dumping, so async mode only and use the DumpProxy.
// note that the avi dumper has already rewired the emulator itself in this case. // note that the avi dumper has already rewired the emulator itself in this case.
GlobalWin.Sound.SetSyncInputPin(_dumpProxy); GlobalWin.Sound.SetInputPin(_dumpProxy);
}
else if (Global.Config.SoundThrottle || !_currentSoundProvider.CanProvideAsync)
{
_currentSoundProvider.SetSyncMode(SyncSoundMode.Sync);
GlobalWin.Sound.SetSyncInputPin(_currentSoundProvider);
} }
else else
{ {
_currentSoundProvider.SetSyncMode(SyncSoundMode.Async); bool useAsyncMode = _currentSoundProvider.CanProvideAsync && !Global.Config.SoundThrottle;
GlobalWin.Sound.SetAsyncInputPin(_currentSoundProvider); _currentSoundProvider.SetSyncMode(useAsyncMode ? SyncSoundMode.Async : SyncSoundMode.Sync);
GlobalWin.Sound.SetInputPin(_currentSoundProvider);
} }
} }

View File

@ -16,9 +16,10 @@ namespace BizHawk.Client.EmuHawk
private bool _disposed; private bool _disposed;
private ISoundOutput _soundOutput; private ISoundOutput _soundOutput;
private ISoundProvider _soundProvider; private ISoundProvider _sourceProvider;
private SyncSoundMode _syncMode;
private SoundOutputProvider _outputProvider; private SoundOutputProvider _outputProvider = new SoundOutputProvider();
private readonly BufferedAsync _semiSync = new BufferedAsync(); private readonly BufferedAsync _semiSync = new BufferedAsync();
public Sound(IntPtr mainWindowHandle) public Sound(IntPtr mainWindowHandle)
@ -60,9 +61,7 @@ namespace BizHawk.Client.EmuHawk
_soundOutput.StartSound(); _soundOutput.StartSound();
_outputProvider = new SoundOutputProvider();
_outputProvider.MaxSamplesDeficit = _soundOutput.MaxSamplesDeficit; _outputProvider.MaxSamplesDeficit = _soundOutput.MaxSamplesDeficit;
_outputProvider.BaseSoundProvider = _soundProvider;
Global.SoundMaxBufferDeficitMs = (int)Math.Ceiling(SamplesToMilliseconds(_soundOutput.MaxSamplesDeficit)); Global.SoundMaxBufferDeficitMs = (int)Math.Ceiling(SamplesToMilliseconds(_soundOutput.MaxSamplesDeficit));
@ -75,48 +74,43 @@ namespace BizHawk.Client.EmuHawk
_soundOutput.StopSound(); _soundOutput.StopSound();
_outputProvider = null; _outputProvider.DiscardSamples();
_semiSync.DiscardSamples();
Global.SoundMaxBufferDeficitMs = 0; Global.SoundMaxBufferDeficitMs = 0;
IsStarted = false; IsStarted = false;
} }
// Sound refactor TODO: combine these methods, and check the SyncMode for behavior
/// <summary> /// <summary>
/// attach a new input pin, of the `sync` variety /// Attaches a new input pin which will run either in sync or async mode depending
/// on its SyncMode property. Once attached, the sync mode must not change unless
/// the pin is re-attached.
/// </summary> /// </summary>
public void SetSyncInputPin(ISoundProvider source) public void SetInputPin(ISoundProvider source)
{ {
if (source.SyncMode != SyncSoundMode.Sync) _outputProvider.DiscardSamples();
throw new InvalidOperationException("SetSyncInputPin() must be called with a sync input"); _outputProvider.BaseSoundProvider = null;
_semiSync.DiscardSamples(); _semiSync.DiscardSamples();
_semiSync.ClearSoundProvider(); _semiSync.BaseSoundProvider = null;
_soundProvider = source;
if (_outputProvider != null)
{
_outputProvider.BaseSoundProvider = source;
}
}
/// <summary> _sourceProvider = source;
/// attach a new input pin, of the `async` variety if (_sourceProvider == null)
/// </summary>
public void SetAsyncInputPin(ISoundProvider source)
{
if (source.SyncMode != SyncSoundMode.Async)
throw new InvalidOperationException("SetAsyncInputPin() must be called with a async input");
if (_outputProvider != null)
{ {
_outputProvider.DiscardSamples(); return;
_outputProvider.BaseSoundProvider = null;
} }
_soundProvider = source; _syncMode = _sourceProvider.SyncMode;
_semiSync.SetAsyncSoundProvider(source); if (_syncMode == SyncSoundMode.Sync)
_semiSync.RecalculateMagic(Global.Emulator.CoreComm.VsyncRate); {
_outputProvider.BaseSoundProvider = _sourceProvider;
}
else
{
_semiSync.BaseSoundProvider = _sourceProvider;
_semiSync.RecalculateMagic(Global.Emulator.CoreComm.VsyncRate);
}
} }
public bool LogUnderruns { get; set; } public bool LogUnderruns { get; set; }
@ -140,7 +134,7 @@ namespace BizHawk.Client.EmuHawk
{ {
if ((!isUnderrun && InitializeBufferWithSilence) || (isUnderrun && RecoverFromUnderrunsWithSilence)) if ((!isUnderrun && InitializeBufferWithSilence) || (isUnderrun && RecoverFromUnderrunsWithSilence))
{ {
int samplesPerFrame = (int)Math.Round(Sound.SampleRate / Global.Emulator.CoreComm.VsyncRate); int samplesPerFrame = (int)Math.Round(SampleRate / Global.Emulator.CoreComm.VsyncRate);
int silenceSamples = Math.Max(samplesNeeded - (SilenceLeaveRoomForFrameCount * samplesPerFrame), 0); int silenceSamples = Math.Max(samplesNeeded - (SilenceLeaveRoomForFrameCount * samplesPerFrame), 0);
_soundOutput.WriteSamples(new short[silenceSamples * 2], silenceSamples); _soundOutput.WriteSamples(new short[silenceSamples * 2], silenceSamples);
samplesNeeded -= silenceSamples; samplesNeeded -= silenceSamples;
@ -157,13 +151,18 @@ namespace BizHawk.Client.EmuHawk
public void UpdateSound(float atten) public void UpdateSound(float atten)
{ {
if (!Global.Config.SoundEnabled || !IsStarted || _disposed) if (!Global.Config.SoundEnabled || !IsStarted || _sourceProvider == null || _disposed)
{ {
if (_soundProvider != null) _soundProvider.DiscardSamples(); if (_sourceProvider != null) _sourceProvider.DiscardSamples();
if (_outputProvider != null) _outputProvider.DiscardSamples(); _outputProvider.DiscardSamples();
return; return;
} }
if (_sourceProvider.SyncMode != _syncMode)
{
throw new Exception("Sync mode changed unexpectedly.");
}
if (atten < 0) atten = 0; if (atten < 0) atten = 0;
if (atten > 1) atten = 1; if (atten > 1) atten = 1;
_soundOutput.ApplyVolumeSettings(atten); _soundOutput.ApplyVolumeSettings(atten);
@ -177,14 +176,14 @@ namespace BizHawk.Client.EmuHawk
samples = new short[samplesNeeded * ChannelCount]; samples = new short[samplesNeeded * ChannelCount];
samplesProvided = samplesNeeded; samplesProvided = samplesNeeded;
if (_soundProvider != null) _soundProvider.DiscardSamples(); _sourceProvider.DiscardSamples();
if (_outputProvider != null) _outputProvider.DiscardSamples(); _outputProvider.DiscardSamples();
} }
else if (_soundProvider != null && _soundProvider.SyncMode == SyncSoundMode.Sync) else if (_syncMode == SyncSoundMode.Sync)
{ {
if (Global.Config.SoundThrottle) if (Global.Config.SoundThrottle)
{ {
_soundProvider.GetSamplesSync(out samples, out samplesProvided); _sourceProvider.GetSamplesSync(out samples, out samplesProvided);
while (samplesNeeded < samplesProvided && !Global.DisableSecondaryThrottling) while (samplesNeeded < samplesProvided && !Global.DisableSecondaryThrottling)
{ {
@ -201,7 +200,7 @@ namespace BizHawk.Client.EmuHawk
_outputProvider.GetSamples(samplesNeeded, out samples, out samplesProvided); _outputProvider.GetSamples(samplesNeeded, out samples, out samplesProvided);
} }
} }
else if (_soundProvider != null && _soundProvider.SyncMode == SyncSoundMode.Async) else if (_syncMode == SyncSoundMode.Async)
{ {
samples = new short[samplesNeeded * ChannelCount]; samples = new short[samplesNeeded * ChannelCount];

View File

@ -54,21 +54,7 @@ namespace BizHawk.Client.EmuHawk
public int MaxSamplesDeficit { get; set; } public int MaxSamplesDeficit { get; set; }
// Sound Refactor TODO: is this sync mode check necessary? public ISoundProvider BaseSoundProvider { get; set; }
private ISoundProvider _baseSoundProvider;
public ISoundProvider BaseSoundProvider
{
get { return _baseSoundProvider; }
set
{
if (value != null && value.SyncMode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Sync mode is required");
}
_baseSoundProvider = value;
}
}
public void DiscardSamples() public void DiscardSamples()
{ {
@ -178,6 +164,10 @@ namespace BizHawk.Client.EmuHawk
short[] samples; short[] samples;
int count; int count;
if (BaseSoundProvider.SyncMode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Base sound provider must be in sync mode.");
}
BaseSoundProvider.GetSamplesSync(out samples, out count); BaseSoundProvider.GetSamplesSync(out samples, out count);
bool correctedEmptyFrame = false; bool correctedEmptyFrame = false;

View File

@ -30,28 +30,13 @@ namespace BizHawk.Emulation.Common
*/ */
public sealed class BufferedAsync : ISoundProvider public sealed class BufferedAsync : ISoundProvider
{ {
private ISoundProvider _baseSoundProvider; public ISoundProvider BaseSoundProvider { get; set; }
public void SetAsyncSoundProvider(ISoundProvider provider) private readonly Queue<short> buffer = new Queue<short>(MaxExcessSamples);
{
if (provider.SyncMode != SyncSoundMode.Async)
{
throw new InvalidOperationException("a sound provider in async mode is required");
}
_baseSoundProvider = provider;
}
public void ClearSoundProvider()
{
_baseSoundProvider = null;
}
readonly Queue<short> buffer = new Queue<short>(4096);
private int SamplesInOneFrame = 1470; private int SamplesInOneFrame = 1470;
private int TargetExtraSamples = 882; private int TargetExtraSamples = 882;
const int MaxExcessSamples = 4096; private const int MaxExcessSamples = 4096;
/// <summary> /// <summary>
/// recalculates some internal parameters based on the IEmulator's framerate /// recalculates some internal parameters based on the IEmulator's framerate
@ -65,8 +50,10 @@ namespace BizHawk.Emulation.Common
public void DiscardSamples() public void DiscardSamples()
{ {
if (_baseSoundProvider != null) buffer.Clear();
_baseSoundProvider.DiscardSamples();
if (BaseSoundProvider != null)
BaseSoundProvider.DiscardSamples();
} }
public void GetSamplesAsync(short[] samples) public void GetSamplesAsync(short[] samples)
@ -81,7 +68,11 @@ namespace BizHawk.Emulation.Common
var mySamples = new short[samplesToGenerate]; var mySamples = new short[samplesToGenerate];
_baseSoundProvider.GetSamplesAsync(mySamples); if (BaseSoundProvider.SyncMode != SyncSoundMode.Async)
{
throw new InvalidOperationException("Base sound provider must be in async mode.");
}
BaseSoundProvider.GetSamplesAsync(mySamples);
foreach (short s in mySamples) foreach (short s in mySamples)
{ {
@ -108,13 +99,13 @@ namespace BizHawk.Emulation.Common
{ {
if (mode != SyncSoundMode.Async) if (mode != SyncSoundMode.Async)
{ {
throw new NotSupportedException("Async mode is not supported."); throw new NotSupportedException("Sync mode is not supported.");
} }
} }
public void GetSamplesSync(out short[] samples, out int nsamp) public void GetSamplesSync(out short[] samples, out int nsamp)
{ {
throw new InvalidOperationException("Async mode is not supported."); throw new InvalidOperationException("Sync mode is not supported.");
} }
} }
} }