diff --git a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj index 77279a2c15..632a6603eb 100644 --- a/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj +++ b/BizHawk.Client.EmuHawk/BizHawk.Client.EmuHawk.csproj @@ -717,6 +717,7 @@ RomStatusPicker.cs + diff --git a/BizHawk.Client.EmuHawk/Sound/Interfaces/ISoundOutput.cs b/BizHawk.Client.EmuHawk/Sound/Interfaces/ISoundOutput.cs new file mode 100644 index 0000000000..f6dce3be8d --- /dev/null +++ b/BizHawk.Client.EmuHawk/Sound/Interfaces/ISoundOutput.cs @@ -0,0 +1,14 @@ +using System; + +namespace BizHawk.Client.EmuHawk +{ + public interface ISoundOutput : IDisposable + { + void StartSound(); + void StopSound(); + void ApplyVolumeSettings(double volume); + int MaxSamplesDeficit { get; } + int CalculateSamplesNeeded(); + void WriteSamples(short[] samples, int sampleCount); + } +} diff --git a/BizHawk.Client.EmuHawk/Sound/Sound.cs b/BizHawk.Client.EmuHawk/Sound/Sound.cs index e3135a3547..4c10058d60 100644 --- a/BizHawk.Client.EmuHawk/Sound/Sound.cs +++ b/BizHawk.Client.EmuHawk/Sound/Sound.cs @@ -15,10 +15,9 @@ namespace BizHawk.Client.EmuHawk private bool _disposed; private readonly ISoundOutput _outputDevice; - private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider(); - private readonly BufferedAsync _bufferedAsync = new BufferedAsync(); - private ISoundProvider _sourceProvider; - private SyncSoundMode _syncMode; + private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider(); // Buffer for Sync sources + private readonly BufferedAsync _bufferedAsync = new BufferedAsync(); // Buffer for Async sources + private IBufferedSoundProvider _bufferedProvider; // One of the preceding buffers, or null if no source is set public Sound(IntPtr mainWindowHandle) { @@ -71,8 +70,7 @@ namespace BizHawk.Client.EmuHawk _outputDevice.StopSound(); - _outputProvider.DiscardSamples(); - _bufferedAsync.DiscardSamples(); + if (_bufferedProvider != null) _bufferedProvider.DiscardSamples(); Global.SoundMaxBufferDeficitMs = 0; @@ -86,28 +84,27 @@ namespace BizHawk.Client.EmuHawk /// public void SetInputPin(ISoundProvider source) { - _outputProvider.DiscardSamples(); - _outputProvider.BaseSoundProvider = null; - - _bufferedAsync.DiscardSamples(); - _bufferedAsync.BaseSoundProvider = null; - - _sourceProvider = source; - if (_sourceProvider == null) + if (_bufferedProvider != null) { - return; + _bufferedProvider.BaseSoundProvider = null; + _bufferedProvider.DiscardSamples(); + _bufferedProvider = null; } - _syncMode = _sourceProvider.SyncMode; - if (_syncMode == SyncSoundMode.Sync) + if (source == null) return; + + if (source.SyncMode == SyncSoundMode.Sync) { - _outputProvider.BaseSoundProvider = _sourceProvider; + _bufferedProvider = _outputProvider; } - else + else if (source.SyncMode == SyncSoundMode.Async) { - _bufferedAsync.BaseSoundProvider = _sourceProvider; _bufferedAsync.RecalculateMagic(Global.Emulator.CoreComm.VsyncRate); + _bufferedProvider = _bufferedAsync; } + else throw new InvalidOperationException("Unsupported sync mode."); + + _bufferedProvider.BaseSoundProvider = source; } public bool LogUnderruns { get; set; } @@ -148,18 +145,12 @@ namespace BizHawk.Client.EmuHawk public void UpdateSound(float atten) { - if (!Global.Config.SoundEnabled || !IsStarted || _sourceProvider == null || _disposed) + if (!Global.Config.SoundEnabled || !IsStarted || _bufferedProvider == null || _disposed) { - if (_sourceProvider != null) _sourceProvider.DiscardSamples(); - _outputProvider.DiscardSamples(); + if (_bufferedProvider != null) _bufferedProvider.DiscardSamples(); return; } - if (_sourceProvider.SyncMode != _syncMode) - { - throw new Exception("Sync mode changed unexpectedly."); - } - if (atten < 0) atten = 0; if (atten > 1) atten = 1; _outputDevice.ApplyVolumeSettings(atten); @@ -173,14 +164,13 @@ namespace BizHawk.Client.EmuHawk samples = new short[samplesNeeded * ChannelCount]; samplesProvided = samplesNeeded; - _sourceProvider.DiscardSamples(); - _outputProvider.DiscardSamples(); + _bufferedProvider.DiscardSamples(); } - else if (_syncMode == SyncSoundMode.Sync) + else if (_bufferedProvider == _outputProvider) { if (Global.Config.SoundThrottle) { - _sourceProvider.GetSamplesSync(out samples, out samplesProvided); + _outputProvider.BaseSoundProvider.GetSamplesSync(out samples, out samplesProvided); while (samplesNeeded < samplesProvided && !Global.DisableSecondaryThrottling) { @@ -197,7 +187,7 @@ namespace BizHawk.Client.EmuHawk _outputProvider.GetSamples(samplesNeeded, out samples, out samplesProvided); } } - else if (_syncMode == SyncSoundMode.Async) + else if (_bufferedProvider == _bufferedAsync) { samples = new short[samplesNeeded * ChannelCount]; @@ -223,14 +213,4 @@ namespace BizHawk.Client.EmuHawk return samples * 1000.0 / SampleRate; } } - - public interface ISoundOutput : IDisposable - { - void StartSound(); - void StopSound(); - void ApplyVolumeSettings(double volume); - int MaxSamplesDeficit { get; } - int CalculateSamplesNeeded(); - void WriteSamples(short[] samples, int sampleCount); - } } diff --git a/BizHawk.Client.EmuHawk/Sound/Utilities/SoundOutputProvider.cs b/BizHawk.Client.EmuHawk/Sound/Utilities/SoundOutputProvider.cs index 0d03ce0e34..33ef5471a7 100644 --- a/BizHawk.Client.EmuHawk/Sound/Utilities/SoundOutputProvider.cs +++ b/BizHawk.Client.EmuHawk/Sound/Utilities/SoundOutputProvider.cs @@ -16,7 +16,7 @@ namespace BizHawk.Client.EmuHawk // perform a "soft" correction by resampling it to hopefully get back inside our // window shortly. If it ends up going too low or too high, we will perform a // "hard" correction by generating silence or discarding samples. - public class SoundOutputProvider + public class SoundOutputProvider : IBufferedSoundProvider { private const int SampleRate = 44100; private const int ChannelCount = 2; diff --git a/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj b/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj index 519f52259c..508fdec95e 100644 --- a/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj +++ b/BizHawk.Emulation.Common/BizHawk.Emulation.Common.csproj @@ -117,6 +117,7 @@ + diff --git a/BizHawk.Emulation.Common/Interfaces/IBufferedSoundProvider.cs b/BizHawk.Emulation.Common/Interfaces/IBufferedSoundProvider.cs new file mode 100644 index 0000000000..aec9efda87 --- /dev/null +++ b/BizHawk.Emulation.Common/Interfaces/IBufferedSoundProvider.cs @@ -0,0 +1,15 @@ +namespace BizHawk.Emulation.Common +{ + public interface IBufferedSoundProvider + { + /// + /// The source audio provider. + /// + ISoundProvider BaseSoundProvider { get; set; } + + /// + /// Clears any internally buffered samples, and discards samples from the base provider (if set). + /// + void DiscardSamples(); + } +} diff --git a/BizHawk.Emulation.Common/Sound/Utilities/BufferedAsync.cs b/BizHawk.Emulation.Common/Sound/Utilities/BufferedAsync.cs index b74965d313..aab88f280b 100644 --- a/BizHawk.Emulation.Common/Sound/Utilities/BufferedAsync.cs +++ b/BizHawk.Emulation.Common/Sound/Utilities/BufferedAsync.cs @@ -28,7 +28,7 @@ namespace BizHawk.Emulation.Common * TODO: For systems that _really_ don't need BufferedAsync (pce not turbocd, sms), make a way to signal * that and then bypass the BufferedAsync. */ - public sealed class BufferedAsync : ISoundProvider + public sealed class BufferedAsync : ISoundProvider, IBufferedSoundProvider { public ISoundProvider BaseSoundProvider { get; set; }