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.
// note that the avi dumper has already rewired the emulator itself in this case.
GlobalWin.Sound.SetSyncInputPin(_dumpProxy);
}
else if (Global.Config.SoundThrottle || !_currentSoundProvider.CanProvideAsync)
{
_currentSoundProvider.SetSyncMode(SyncSoundMode.Sync);
GlobalWin.Sound.SetSyncInputPin(_currentSoundProvider);
GlobalWin.Sound.SetInputPin(_dumpProxy);
}
else
{
_currentSoundProvider.SetSyncMode(SyncSoundMode.Async);
GlobalWin.Sound.SetAsyncInputPin(_currentSoundProvider);
bool useAsyncMode = _currentSoundProvider.CanProvideAsync && !Global.Config.SoundThrottle;
_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 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();
public Sound(IntPtr mainWindowHandle)
@ -60,9 +61,7 @@ namespace BizHawk.Client.EmuHawk
_soundOutput.StartSound();
_outputProvider = new SoundOutputProvider();
_outputProvider.MaxSamplesDeficit = _soundOutput.MaxSamplesDeficit;
_outputProvider.BaseSoundProvider = _soundProvider;
Global.SoundMaxBufferDeficitMs = (int)Math.Ceiling(SamplesToMilliseconds(_soundOutput.MaxSamplesDeficit));
@ -75,48 +74,43 @@ namespace BizHawk.Client.EmuHawk
_soundOutput.StopSound();
_outputProvider = null;
_outputProvider.DiscardSamples();
_semiSync.DiscardSamples();
Global.SoundMaxBufferDeficitMs = 0;
IsStarted = false;
}
// Sound refactor TODO: combine these methods, and check the SyncMode for behavior
/// <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>
public void SetSyncInputPin(ISoundProvider source)
public void SetInputPin(ISoundProvider source)
{
if (source.SyncMode != SyncSoundMode.Sync)
throw new InvalidOperationException("SetSyncInputPin() must be called with a sync input");
_outputProvider.DiscardSamples();
_outputProvider.BaseSoundProvider = null;
_semiSync.DiscardSamples();
_semiSync.ClearSoundProvider();
_soundProvider = source;
if (_outputProvider != null)
{
_outputProvider.BaseSoundProvider = source;
}
}
_semiSync.BaseSoundProvider = null;
/// <summary>
/// attach a new input pin, of the `async` variety
/// </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)
_sourceProvider = source;
if (_sourceProvider == null)
{
_outputProvider.DiscardSamples();
_outputProvider.BaseSoundProvider = null;
return;
}
_soundProvider = source;
_semiSync.SetAsyncSoundProvider(source);
_semiSync.RecalculateMagic(Global.Emulator.CoreComm.VsyncRate);
_syncMode = _sourceProvider.SyncMode;
if (_syncMode == SyncSoundMode.Sync)
{
_outputProvider.BaseSoundProvider = _sourceProvider;
}
else
{
_semiSync.BaseSoundProvider = _sourceProvider;
_semiSync.RecalculateMagic(Global.Emulator.CoreComm.VsyncRate);
}
}
public bool LogUnderruns { get; set; }
@ -140,7 +134,7 @@ namespace BizHawk.Client.EmuHawk
{
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);
_soundOutput.WriteSamples(new short[silenceSamples * 2], silenceSamples);
samplesNeeded -= silenceSamples;
@ -157,13 +151,18 @@ namespace BizHawk.Client.EmuHawk
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 (_outputProvider != null) _outputProvider.DiscardSamples();
if (_sourceProvider != null) _sourceProvider.DiscardSamples();
_outputProvider.DiscardSamples();
return;
}
if (_sourceProvider.SyncMode != _syncMode)
{
throw new Exception("Sync mode changed unexpectedly.");
}
if (atten < 0) atten = 0;
if (atten > 1) atten = 1;
_soundOutput.ApplyVolumeSettings(atten);
@ -177,14 +176,14 @@ namespace BizHawk.Client.EmuHawk
samples = new short[samplesNeeded * ChannelCount];
samplesProvided = samplesNeeded;
if (_soundProvider != null) _soundProvider.DiscardSamples();
if (_outputProvider != null) _outputProvider.DiscardSamples();
_sourceProvider.DiscardSamples();
_outputProvider.DiscardSamples();
}
else if (_soundProvider != null && _soundProvider.SyncMode == SyncSoundMode.Sync)
else if (_syncMode == SyncSoundMode.Sync)
{
if (Global.Config.SoundThrottle)
{
_soundProvider.GetSamplesSync(out samples, out samplesProvided);
_sourceProvider.GetSamplesSync(out samples, out samplesProvided);
while (samplesNeeded < samplesProvided && !Global.DisableSecondaryThrottling)
{
@ -201,7 +200,7 @@ namespace BizHawk.Client.EmuHawk
_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];

View File

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

View File

@ -30,28 +30,13 @@ namespace BizHawk.Emulation.Common
*/
public sealed class BufferedAsync : ISoundProvider
{
private ISoundProvider _baseSoundProvider;
public ISoundProvider BaseSoundProvider { get; set; }
public void SetAsyncSoundProvider(ISoundProvider provider)
{
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 readonly Queue<short> buffer = new Queue<short>(MaxExcessSamples);
private int SamplesInOneFrame = 1470;
private int TargetExtraSamples = 882;
const int MaxExcessSamples = 4096;
private const int MaxExcessSamples = 4096;
/// <summary>
/// recalculates some internal parameters based on the IEmulator's framerate
@ -65,8 +50,10 @@ namespace BizHawk.Emulation.Common
public void DiscardSamples()
{
if (_baseSoundProvider != null)
_baseSoundProvider.DiscardSamples();
buffer.Clear();
if (BaseSoundProvider != null)
BaseSoundProvider.DiscardSamples();
}
public void GetSamplesAsync(short[] samples)
@ -81,7 +68,11 @@ namespace BizHawk.Emulation.Common
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)
{
@ -108,13 +99,13 @@ namespace BizHawk.Emulation.Common
{
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)
{
throw new InvalidOperationException("Async mode is not supported.");
throw new InvalidOperationException("Sync mode is not supported.");
}
}
}