From eb72ff9053bedcbd0b85fec8cce32e129e04c180 Mon Sep 17 00:00:00 2001 From: nattthebear Date: Sun, 11 Dec 2016 14:07:12 -0500 Subject: [PATCH] Swag up NullSound --- BizHawk.Client.EmuHawk/AVOut/AVSync.cs | 2 +- BizHawk.Client.EmuHawk/MainForm.cs | 11 +--- .../Base Implementations/NullSound.cs | 59 +++++++++++++++---- BizHawk.Emulation.Common/Extensions.cs | 14 +++++ 4 files changed, 62 insertions(+), 24 deletions(-) diff --git a/BizHawk.Client.EmuHawk/AVOut/AVSync.cs b/BizHawk.Client.EmuHawk/AVOut/AVSync.cs index 9f14c85ada..d9139e276f 100644 --- a/BizHawk.Client.EmuHawk/AVOut/AVSync.cs +++ b/BizHawk.Client.EmuHawk/AVOut/AVSync.cs @@ -22,7 +22,7 @@ namespace BizHawk.Client.EmuHawk // Sound refactor TODO: we could try set it here, but we want the client to be responsible for mode switching? There may be non-trivial complications with when to switch modes that we don't want this object worrying about if (asyncSoundProvider.SyncMode != SyncSoundMode.Async) { - throw new InvalidOperationException("Only sync mode is supported, set sync mode before passing in the sound provider"); + throw new InvalidOperationException("Only async mode is supported, set async mode before passing in the sound provider"); } if (!aset || !vset) diff --git a/BizHawk.Client.EmuHawk/MainForm.cs b/BizHawk.Client.EmuHawk/MainForm.cs index 589b5d946d..7a06c23288 100644 --- a/BizHawk.Client.EmuHawk/MainForm.cs +++ b/BizHawk.Client.EmuHawk/MainForm.cs @@ -734,16 +734,7 @@ namespace BizHawk.Client.EmuHawk _currentVideoProvider = NullVideo.Instance; } - if (Global.Emulator.HasSoundProvider()) - { - _currentSoundProvider = Global.Emulator.AsSoundProvider(); - } - else - { - // Set the samples per frame based on VSync rate (which is 60 unless the core states otherwise) - // Use 44.1 khz because we like to do that - _currentSoundProvider = new NullSound((int)(44100 / Global.Emulator.CoreComm.VsyncRate)); - } + _currentSoundProvider = Global.Emulator.AsSoundProviderOrDefault(); } } diff --git a/BizHawk.Emulation.Common/Base Implementations/NullSound.cs b/BizHawk.Emulation.Common/Base Implementations/NullSound.cs index b785b1f52c..8d34d1bb04 100644 --- a/BizHawk.Emulation.Common/Base Implementations/NullSound.cs +++ b/BizHawk.Emulation.Common/Base Implementations/NullSound.cs @@ -4,43 +4,76 @@ namespace BizHawk.Emulation.Common { public class NullSound : ISoundProvider { - private readonly int _spf; + private readonly long _spfNumerator; + private readonly long _spfDenominator; + private long _remainder = 0; + private short[] _buff = new short[0]; - public NullSound(int spf) + private NullSound() { - _spf = spf; + SyncMode = SyncSoundMode.Sync; + } + + /// + /// create a NullSound that provides an exact number of audio samples per call when in sync mode + /// + /// + public NullSound(int spf) + :this() + { + _spfNumerator = spf; + _spfDenominator = 1; + } + + /// + /// create a NullSound that exactly matches a given framerate when in sync mode + /// + /// + /// + public NullSound(long fpsNum, long fpsDen) + { + _spfNumerator = fpsDen * 44100; + _spfDenominator = fpsNum; } public bool CanProvideAsync { - get { return false; } + get { return true; } } public SyncSoundMode SyncMode { - get { return SyncSoundMode.Sync; } + get; + private set; } public void GetSamplesSync(out short[] samples, out int nsamp) { - short[] ret = new short[_spf * 2]; - samples = ret; - nsamp = _spf; + if (SyncMode != SyncSoundMode.Sync) + throw new InvalidOperationException("Wrong sound mode"); + + int s = (int)((_spfNumerator + _remainder) / _spfDenominator); + _remainder = (_spfNumerator + _remainder) % _spfDenominator; + + if (_buff.Length < s * 2) + _buff = new short[s * 2]; + + samples = _buff; + nsamp = s; } public void DiscardSamples() { } public void SetSyncMode(SyncSoundMode mode) { - if (mode == SyncSoundMode.Async) - { - throw new NotSupportedException("Async mode is not supported."); - } + SyncMode = mode; } public void GetSamplesAsync(short[] samples) { - throw new InvalidOperationException("Async mode is not supported."); + if (SyncMode != SyncSoundMode.Async) + throw new InvalidOperationException("Wrong sound mode"); + Array.Clear(samples, 0, samples.Length); } } } diff --git a/BizHawk.Emulation.Common/Extensions.cs b/BizHawk.Emulation.Common/Extensions.cs index 83b97d3342..3ffa04bdaf 100644 --- a/BizHawk.Emulation.Common/Extensions.cs +++ b/BizHawk.Emulation.Common/Extensions.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Reflection; using BizHawk.Common.ReflectionExtensions; +using System.Runtime.CompilerServices; namespace BizHawk.Emulation.Common.IEmulatorExtensions { @@ -48,6 +49,19 @@ namespace BizHawk.Emulation.Common.IEmulatorExtensions return core.ServiceProvider.GetService(); } + private static readonly ConditionalWeakTable CachedNullSoundProviders = new ConditionalWeakTable(); + + /// + /// returns the core's SoundProvider, or a suitable dummy provider + /// + public static ISoundProvider AsSoundProviderOrDefault(this IEmulator core) + { + var ret = core.ServiceProvider.GetService(); + if (ret == null) + ret = CachedNullSoundProviders.GetValue(core, e => new NullSound(e.CoreComm.VsyncNum, e.CoreComm.VsyncDen)); + return ret; + } + public static bool HasMemoryDomains(this IEmulator core) { if (core == null)