Major sound provider refactor. Create a unified interface ISoundProvider, that is an IEmulator service and break it off of IEmulator. IAsyncSoundProvider was kept as a shim for now, for legacy cores that were inherintly async.

This commit is contained in:
adelikat 2016-12-11 11:14:42 -06:00
parent 5ac6746fb9
commit b73a500d6b
72 changed files with 3016 additions and 2452 deletions

View File

@ -17,8 +17,14 @@ namespace BizHawk.Client.EmuHawk
private long _soundRemainder; // audio timekeeping for video dumping
public void DumpAV(IVideoProvider v, IAsyncSoundProvider s, out short[] samples, out int samplesprovided)
public void DumpAV(IVideoProvider v, ISoundProvider asyncSoundProvider, out short[] samples, out int samplesprovided)
{
// 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");
}
if (!aset || !vset)
throw new InvalidOperationException("Must set params first!");
@ -29,7 +35,7 @@ namespace BizHawk.Client.EmuHawk
_soundRemainder = nsampnum % fpsnum;
samples = new short[nsamp * channels];
s.GetSamples(samples);
asyncSoundProvider.GetSamplesAsync(samples);
samplesprovided = (int)nsamp;
w.AddFrame(v);
@ -73,10 +79,16 @@ namespace BizHawk.Client.EmuHawk
}
}
public void DumpAV(IVideoProvider v, ISyncSoundProvider s, out short[] samples, out int samplesprovided)
public void DumpAV(IVideoProvider v, ISoundProvider syncSoundProvider, out short[] samples, out int samplesprovided)
{
// Sound refactor TODO: we could just 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 (syncSoundProvider.SyncMode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only sync mode is supported, set sync mode before passing in the sound provider");
}
VerifyParams();
s.GetSamples(out samples, out samplesprovided);
syncSoundProvider.GetSamplesSync(out samples, out samplesprovided);
exaudio_num += samplesprovided * (long)fpsnum;
// todo: scan for duplicate frames (ie, video content exactly matches previous frame) and for them, skip the threshone step

View File

@ -733,11 +733,24 @@ 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));
}
}
}
private IVideoProvider _currentVideoProvider = NullVideo.Instance;
private ISoundProvider _currentSoundProvider = new NullSound(44100 / 60); // Reasonable default until we have a core instance
protected override void OnActivated(EventArgs e)
{
base.OnActivated(e);
@ -1345,7 +1358,10 @@ namespace BizHawk.Client.EmuHawk
// AVI/WAV state
private IVideoWriter _currAviWriter;
private HashSet<int> _currAviWriterFrameList;
private IAsyncSoundProvider _aviSoundInput;
// Sound refator TODO: we can enforce async mode here with a property that gets/sets this but does an async check
private ISoundProvider _aviSoundInputAsync; // Note: This sound provider must be in async mode!
private MetaspuSoundProvider _dumpProxy; // an audio proxy used for dumping
private bool _dumpaudiosync; // set true to for experimental AV dumping
private int _avwriterResizew;
@ -1620,8 +1636,7 @@ namespace BizHawk.Client.EmuHawk
}
else
{
Emulator.EndAsyncSound();
GlobalWin.Sound.SetSyncInputPin(Emulator.SyncSoundProvider);
GlobalWin.Sound.SetSyncInputPin(_currentSoundProvider);
}
}
@ -3096,13 +3111,14 @@ namespace BizHawk.Client.EmuHawk
if (_dumpaudiosync)
{
Emulator.EndAsyncSound();
_currentSoundProvider.SetSyncMode(SyncSoundMode.Sync);
}
else
{
_aviSoundInput = !Emulator.StartAsyncSound()
? new MetaspuAsync(Emulator.SyncSoundProvider, ESynchMethod.ESynchMethod_V)
: Emulator.SoundProvider;
_aviSoundInputAsync = _currentSoundProvider.CanProvideAsync
? _currentSoundProvider
: new MetaspuAsync(_currentSoundProvider, ESynchMethod.ESynchMethod_V);
}
_dumpProxy = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
RewireSound();
@ -3123,7 +3139,7 @@ namespace BizHawk.Client.EmuHawk
AVIStatusLabel.Image = Properties.Resources.Blank;
AVIStatusLabel.ToolTipText = string.Empty;
AVIStatusLabel.Visible = false;
_aviSoundInput = null;
_aviSoundInputAsync = null;
_dumpProxy = null; // return to normal sound output
RewireSound();
}
@ -3144,7 +3160,7 @@ namespace BizHawk.Client.EmuHawk
AVIStatusLabel.Image = Properties.Resources.Blank;
AVIStatusLabel.ToolTipText = string.Empty;
AVIStatusLabel.Visible = false;
_aviSoundInput = null;
_aviSoundInputAsync = null;
_dumpProxy = null; // return to normal sound output
RewireSound();
}
@ -3226,11 +3242,11 @@ namespace BizHawk.Client.EmuHawk
int nsamp;
if (_dumpaudiosync)
{
(_currAviWriter as VideoStretcher).DumpAV(output, Emulator.SyncSoundProvider, out samp, out nsamp);
(_currAviWriter as VideoStretcher).DumpAV(output, _currentSoundProvider, out samp, out nsamp);
}
else
{
(_currAviWriter as AudioStretcher).DumpAV(output, _aviSoundInput, out samp, out nsamp);
(_currAviWriter as AudioStretcher).DumpAV(output, _aviSoundInputAsync, out samp, out nsamp);
}
if (disposableOutput != null)
@ -3238,7 +3254,7 @@ namespace BizHawk.Client.EmuHawk
disposableOutput.Dispose();
}
_dumpProxy.buffer.enqueue_samples(samp, nsamp);
_dumpProxy.Buffer.enqueue_samples(samp, nsamp);
}
catch (Exception e)
{

View File

@ -15,8 +15,9 @@ namespace BizHawk.Client.EmuHawk
private bool _disposed;
private ISoundOutput _soundOutput;
private ISyncSoundProvider _syncSoundProvider;
private IAsyncSoundProvider _asyncSoundProvider;
private ISoundProvider _soundProvider;
private SoundOutputProvider _outputProvider;
private readonly BufferedAsync _semiSync = new BufferedAsync();
@ -61,16 +62,11 @@ namespace BizHawk.Client.EmuHawk
_outputProvider = new SoundOutputProvider();
_outputProvider.MaxSamplesDeficit = _soundOutput.MaxSamplesDeficit;
_outputProvider.BaseSoundProvider = _syncSoundProvider;
_outputProvider.BaseSoundProvider = _soundProvider;
Global.SoundMaxBufferDeficitMs = (int)Math.Ceiling(SamplesToMilliseconds(_soundOutput.MaxSamplesDeficit));
IsStarted = true;
//ApplyVolumeSettings();
//LogUnderruns = true;
//_outputProvider.LogDebug = true;
}
public void StopSound()
@ -86,46 +82,28 @@ namespace BizHawk.Client.EmuHawk
IsStarted = false;
}
//public void ApplyVolumeSettings()
//{
// if (!IsStarted) return;
// double volume = Global.Config.SoundVolume / 100.0;
// if (volume < 0.0) volume = 0.0;
// if (volume > 1.0) volume = 1.0;
// _soundOutput.ApplyVolumeSettings(volume);
//}
public void SetSyncInputPin(ISyncSoundProvider source)
// Sound refactor TODO: combine these methods, and check the SyncMode for behavior
public void SetSyncInputPin(ISoundProvider source)
{
if (_asyncSoundProvider != null)
{
_asyncSoundProvider.DiscardSamples();
_asyncSoundProvider = null;
}
_semiSync.DiscardSamples();
_semiSync.BaseSoundProvider = null;
_syncSoundProvider = source;
_semiSync.ClearSoundProvider();
_soundProvider = source;
if (_outputProvider != null)
{
_outputProvider.BaseSoundProvider = source;
}
}
public void SetAsyncInputPin(IAsyncSoundProvider source)
public void SetAsyncInputPin(ISoundProvider source)
{
if (_syncSoundProvider != null)
{
_syncSoundProvider.DiscardSamples();
_syncSoundProvider = null;
}
if (_outputProvider != null)
{
_outputProvider.DiscardSamples();
_outputProvider.BaseSoundProvider = null;
}
_asyncSoundProvider = source;
_semiSync.BaseSoundProvider = source;
_soundProvider = source;
_semiSync.SetAsyncSoundProvider(source);
_semiSync.RecalculateMagic(Global.Emulator.CoreComm.VsyncRate);
}
@ -169,8 +147,10 @@ namespace BizHawk.Client.EmuHawk
{
if (!Global.Config.SoundEnabled || !IsStarted || _disposed)
{
if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples();
if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples();
if (_soundProvider != null) _soundProvider.DiscardSamples();
// Sound refactor TODO: delete me
//if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples();
//if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples();
if (_outputProvider != null) _outputProvider.DiscardSamples();
return;
}
@ -188,15 +168,18 @@ namespace BizHawk.Client.EmuHawk
samples = new short[samplesNeeded * ChannelCount];
samplesProvided = samplesNeeded;
if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples();
if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples();
if (_soundProvider != null) _soundProvider.DiscardSamples();
// Sound refactor TODO: delete me
//if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples();
//if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples();
if (_outputProvider != null) _outputProvider.DiscardSamples();
}
else if (_syncSoundProvider != null)
else if ( _soundProvider.SyncMode == SyncSoundMode.Sync)
//else if (_syncSoundProvider != null) // Sound refactor TODO: delete me
{
if (Global.Config.SoundThrottle)
{
_syncSoundProvider.GetSamples(out samples, out samplesProvided);
_soundProvider.GetSamplesSync(out samples, out samplesProvided);
while (samplesNeeded < samplesProvided && !Global.DisableSecondaryThrottling)
{
@ -213,11 +196,12 @@ namespace BizHawk.Client.EmuHawk
_outputProvider.GetSamples(samplesNeeded, out samples, out samplesProvided);
}
}
else if (_asyncSoundProvider != null)
else if (_soundProvider != null && _soundProvider.SyncMode == SyncSoundMode.Sync)
//else if (_asyncSoundProvider != null) // Sound refactor TODO: delete me
{
samples = new short[samplesNeeded * ChannelCount];
_semiSync.GetSamples(samples);
_semiSync.GetSamplesAsync(samples);
samplesProvided = samplesNeeded;
}

View File

@ -54,7 +54,21 @@ namespace BizHawk.Client.EmuHawk
public int MaxSamplesDeficit { get; set; }
public ISyncSoundProvider BaseSoundProvider { 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 void DiscardSamples()
{
@ -164,7 +178,7 @@ namespace BizHawk.Client.EmuHawk
short[] samples;
int count;
BaseSoundProvider.GetSamples(out samples, out count);
BaseSoundProvider.GetSamplesSync(out samples, out count);
bool correctedEmptyFrame = false;
if (count == 0)

View File

@ -180,8 +180,13 @@ namespace BizHawk.Client.EmuHawk
int nsamp;
short[] samp;
emu.FrameAdvance(true, true);
// some cores really really really like it if you drain their audio every frame
emu.SyncSoundProvider.GetSamples(out samp, out nsamp);
if (emu.HasSoundProvider())
{
emu.AsSoundProvider().GetSamplesSync(out samp, out nsamp);
}
current.Frames++;
if (emu.CanPollInput() && emu.AsInputPollable().IsLagFrame)
current.LaggedFrames++;

View File

@ -9,10 +9,11 @@ namespace BizHawk.Emulation.Common
[CoreAttributes("NullHawk", "", false, true)]
[ServiceNotApplicable(typeof(IStatable), typeof(ISaveRam), typeof(IDriveLight), typeof(ICodeDataLogger), typeof(IMemoryDomains),
typeof(IDebuggable), typeof(IDisassemblable), typeof(IInputPollable), typeof(IRegionable), typeof(ITraceable))]
public class NullEmulator : IEmulator, IVideoProvider, ISyncSoundProvider, IAsyncSoundProvider, ISettable<NullEmulator.NullEmulatorSettings, object>
public class NullEmulator : IEmulator, IVideoProvider, ISoundProvider, ISettable<NullEmulator.NullEmulatorSettings, object>
{
public NullEmulator(CoreComm comm, object settings)
{
SyncMode = SyncSoundMode.Sync;
ServiceProvider = new BasicServiceProvider(this);
CoreComm = comm;
_settings = (NullEmulatorSettings)settings ?? new NullEmulatorSettings();
@ -34,10 +35,6 @@ namespace BizHawk.Emulation.Common
private readonly int[] frameBuffer = new int[256 * 192];
private readonly Random rand = new Random();
public CoreComm CoreComm { get; private set; }
public IAsyncSoundProvider SoundProvider { get { return this; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return true; }
public void EndAsyncSound() { }
public void ResetCounters()
{
@ -94,8 +91,15 @@ namespace BizHawk.Emulation.Common
private short[] sampbuff = new short[735 * 2];
public void GetSamples(out short[] samples, out int nsamp)
#region ISoundProvider
public void GetSamplesSync(out short[] samples, out int nsamp)
{
if (SyncMode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Attempt to call a Sync method in async mode");
}
nsamp = 735;
samples = sampbuff;
if (!_settings.SnowyDisplay)
@ -108,20 +112,33 @@ namespace BizHawk.Emulation.Common
{
}
public void GetSamples(short[] samples)
public void GetSamplesAsync(short[] samples)
{
if (SyncMode != SyncSoundMode.Async)
{
throw new InvalidOperationException("Attempt to call an Async method in sync mode");
}
if (!_settings.SnowyDisplay)
return;
if (xmas)
pleg.Generate(samples);
}
public int MaxVolume
public bool CanProvideAsync
{
get;
set;
get { return true; }
}
public SyncSoundMode SyncMode { get; private set; }
public void SetSyncMode(SyncSoundMode mode)
{
SyncMode = mode;
}
#endregion
private NullEmulatorSettings _settings;
public class NullEmulatorSettings

View File

@ -1,10 +1,46 @@
namespace BizHawk.Emulation.Common
{
public class NullSound : IAsyncSoundProvider
{
public static readonly NullSound SilenceProvider = new NullSound();
using System;
namespace BizHawk.Emulation.Common
{
public class NullSound : ISoundProvider
{
private readonly int _spf;
public NullSound(int spf)
{
_spf = spf;
}
public bool CanProvideAsync
{
get { return false; }
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
short[] ret = new short[_spf * 2];
samples = ret;
nsamp = _spf;
}
public void GetSamples(short[] samples) { }
public void DiscardSamples() { }
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
}
}

View File

@ -123,8 +123,8 @@
<Compile Include="Interfaces\IInputCallbackSystem.cs" />
<Compile Include="Interfaces\IMemoryCallbackSystem.cs" />
<Compile Include="Interfaces\IEmulatorServiceProvider.cs" />
<Compile Include="Interfaces\ISoundProvider.cs" />
<Compile Include="Interfaces\ISyncSoundProvider.cs" />
<Compile Include="Interfaces\Services\ISoundProvider.cs" />
<Compile Include="Interfaces\IAsyncSoundProvider.cs" />
<Compile Include="Interfaces\Services\ICodeDataLogger.cs" />
<Compile Include="Interfaces\Services\IDebuggable.cs" />
<Compile Include="Interfaces\Services\IDisassemblable.cs" />

View File

@ -33,6 +33,21 @@ namespace BizHawk.Emulation.Common.IEmulatorExtensions
return core.ServiceProvider.GetService<IVideoProvider>();
}
public static bool HasSoundProvider(this IEmulator core)
{
if (core == null)
{
return false;
}
return core.ServiceProvider.HasService<ISoundProvider>();
}
public static ISoundProvider AsSoundProvider(this IEmulator core)
{
return core.ServiceProvider.GetService<ISoundProvider>();
}
public static bool HasMemoryDomains(this IEmulator core)
{
if (core == null)

View File

@ -0,0 +1,74 @@
using System;
namespace BizHawk.Emulation.Common
{
public interface IAsyncSoundProvider
{
void GetSamples(short[] samples);
void DiscardSamples();
}
/// <summary>
/// TODO: this is a shim for now
/// turns an IAsyncSoundPRovider into a full ISoundProvider
/// This is used in cores that have an async only sound implementation
/// better is impleemnt a sync sound option in those cores without the need for
/// this class or an IAsyncSoundPRovider interface
/// </summary>
public class FakeSyncSound : ISoundProvider
{
private readonly IAsyncSoundProvider source;
private readonly int spf;
/// <summary>
///
/// </summary>
/// <param name="source"></param>
/// <param name="spf">number of sample pairs to request and provide on each GetSamples() call</param>
public FakeSyncSound(IAsyncSoundProvider source, int spf)
{
this.source = source;
this.spf = spf;
SyncMode = SyncSoundMode.Sync;
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
if (SyncMode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Must be in sync mode to call a sync method");
}
short[] ret = new short[spf * 2];
source.GetSamples(ret);
samples = ret;
nsamp = spf;
}
public void DiscardSamples()
{
source.DiscardSamples();
}
public void GetSamplesAsync(short[] samples)
{
if (SyncMode != SyncSoundMode.Async)
{
throw new InvalidOperationException("Must be in async mode to call an async method");
}
source.GetSamples(samples);
}
public bool CanProvideAsync
{
get { return true; }
}
public SyncSoundMode SyncMode { get; private set; }
public void SetSyncMode(SyncSoundMode mode)
{
SyncMode = mode;
}
}
}

View File

@ -12,24 +12,6 @@ namespace BizHawk.Emulation.Common
/// <returns></returns>
IEmulatorServiceProvider ServiceProvider { get; }
/// <summary>
/// Sound provider for async operation. this is optional, and is only required after StartAsyncSound() is called and returns true
/// </summary>
IAsyncSoundProvider SoundProvider { get; }
/// <summary>
/// sound provider for sync operation. this is manditory
/// </summary>
ISyncSoundProvider SyncSoundProvider { get; }
/// <summary>start async operation. (on construct, sync operation is assumed).</summary>
/// <returns>false if core doesn't support async sound; SyncSoundProvider will continue to be used in that case</returns>
bool StartAsyncSound();
/// <summary>
/// end async operation, returning to sync operation. after this, all sound requests will go to the SyncSoundProvider
/// </summary>
void EndAsyncSound();
/// <summary>
/// Defines all the possible inputs and types that the core can receive
/// </summary>

View File

@ -1,8 +0,0 @@
namespace BizHawk.Emulation.Common
{
public interface IAsyncSoundProvider
{
void GetSamples(short[] samples);
void DiscardSamples();
}
}

View File

@ -1,43 +0,0 @@
namespace BizHawk.Emulation.Common
{
public interface ISyncSoundProvider
{
/// <summary>
/// </summary>
/// <param name="nsamp">number of sample PAIRS available</param>
void GetSamples(out short[] samples, out int nsamp);
void DiscardSamples();
}
/// <summary>
/// wraps an ISyncSoundProvider around an ISoundProvider
/// </summary>
public class FakeSyncSound : ISyncSoundProvider
{
private readonly IAsyncSoundProvider source;
private readonly int spf;
/// <summary>
/// </summary>
/// <param name="spf">number of sample pairs to request and provide on each GetSamples() call</param>
public FakeSyncSound(IAsyncSoundProvider source, int spf)
{
this.source = source;
this.spf = spf;
}
public void GetSamples(out short[] samples, out int nsamp)
{
short[] ret = new short[spf * 2];
source.GetSamples(ret);
samples = ret;
nsamp = spf;
}
public void DiscardSamples()
{
source.DiscardSamples();
}
}
}

View File

@ -0,0 +1,44 @@
namespace BizHawk.Emulation.Common
{
public enum SyncSoundMode { Sync, Async };
public interface ISoundProvider : IEmulatorService
{
/// <summary>
/// Returns true if a core can provide Async sound
/// </summary>
bool CanProvideAsync { get; }
/// <summary>
/// Sets sync or async sound mode,
/// Sync should be the default mode if not set
/// All implementations must provide sync
/// If a core can not provide async sound and the mode is set to sync,
/// an NotSupportedException should be thrown
/// </summary>
void SetSyncMode(SyncSoundMode mode);
/// <summary>
/// Reports which mode the sound provider is currently in
/// </summary>
SyncSoundMode SyncMode { get; }
/// <summary>
/// Provides samples in syncmode
/// If the core is not in sync mode, this should throw an InvalidOperationException
/// </summary>
void GetSamplesSync(out short[] samples, out int nsamp);
/// <summary>
/// Provides samples in async mode
/// If the core is not in async mode, this shoudl throw an InvalidOperationException
/// </summary>
/// <param name="samples"></param>
void GetSamplesAsync(short[] samples);
/// <summary>
/// Discards stuff, is there anything more to say here?
/// </summary>
void DiscardSamples();
}
}

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using System;
using System.Collections.Generic;
namespace BizHawk.Emulation.Common
{
@ -27,10 +28,24 @@ 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 : IAsyncSoundProvider
public sealed class BufferedAsync : ISoundProvider
{
public IAsyncSoundProvider BaseSoundProvider;
private ISoundProvider _baseSoundProvider;
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);
@ -50,11 +65,11 @@ namespace BizHawk.Emulation.Common
public void DiscardSamples()
{
if (BaseSoundProvider != null)
BaseSoundProvider.DiscardSamples();
if (_baseSoundProvider != null)
_baseSoundProvider.DiscardSamples();
}
public void GetSamples(short[] samples)
public void GetSamplesAsync(short[] samples)
{
int samplesToGenerate = SamplesInOneFrame;
if (buffer.Count > samples.Length + MaxExcessSamples)
@ -66,7 +81,7 @@ namespace BizHawk.Emulation.Common
var mySamples = new short[samplesToGenerate];
BaseSoundProvider.GetSamples(mySamples);
_baseSoundProvider.GetSamplesAsync(mySamples);
foreach (short s in mySamples)
{
@ -78,5 +93,28 @@ namespace BizHawk.Emulation.Common
samples[i] = buffer.Dequeue();
}
}
public bool CanProvideAsync
{
get { return true; }
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Async; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
throw new InvalidOperationException("Async mode is not supported.");
}
}
}

View File

@ -5,24 +5,18 @@ namespace BizHawk.Emulation.Common
/// <summary>
/// implements a DC block filter on top of an ISoundProvider. rather simple.
/// </summary>
sealed public class DCFilter : IAsyncSoundProvider, ISyncSoundProvider
sealed public class DCFilter : ISoundProvider
{
/*
* A note about accuracy:
*
* DCFilter can be added to the final output of any console, and this change will be faithful to the original hardware.
* Analog output hardware ALWAYS has dc blocking caps.
*/
private ISoundProvider _soundProvider;
IAsyncSoundProvider input;
ISyncSoundProvider syncinput;
private int latchL = 0;
private int latchR = 0;
private int accumL = 0;
private int accumR = 0;
int latchL = 0;
int latchR = 0;
int accumL = 0;
int accumR = 0;
private int depth;
static int DepthFromFilterwidth(int filterwidth)
private static int DepthFromFilterwidth(int filterwidth)
{
int ret = -2;
while (filterwidth > 0)
@ -33,34 +27,34 @@ namespace BizHawk.Emulation.Common
return ret;
}
int depth;
public static DCFilter AsISoundProvider(IAsyncSoundProvider input, int filterwidth)
public DCFilter(ISoundProvider input, int filterwidth)
{
if (input == null)
{
throw new ArgumentNullException();
return new DCFilter(input, null, filterwidth);
}
if (filterwidth < 8 || filterwidth > 65536)
{
throw new ArgumentOutOfRangeException();
}
depth = DepthFromFilterwidth(filterwidth);
_soundProvider = input;
}
public static DCFilter AsISyncSoundProvider(ISyncSoundProvider syncinput, int filterwidth)
{
if (syncinput == null)
throw new ArgumentNullException();
return new DCFilter(null, syncinput, filterwidth);
}
public static DCFilter DetatchedMode(int filterwidth)
{
return new DCFilter(null, null, filterwidth);
}
DCFilter(IAsyncSoundProvider input, ISyncSoundProvider syncinput, int filterwidth)
// Detached mode
public DCFilter(int filterwidth)
{
if (filterwidth < 8 || filterwidth > 65536)
{
throw new ArgumentOutOfRangeException();
this.input = input;
this.syncinput = syncinput;
}
depth = DepthFromFilterwidth(filterwidth);
_soundProvider = null;
}
/// <summary>
@ -104,31 +98,43 @@ namespace BizHawk.Emulation.Common
}
}
void IAsyncSoundProvider.GetSamples(short[] samples)
public void GetSamplesAsync(short[] samples)
{
input.GetSamples(samples);
_soundProvider.GetSamplesAsync(samples);
PushThroughSamples(samples, samples.Length);
}
void IAsyncSoundProvider.DiscardSamples()
public void DiscardSamples()
{
input.DiscardSamples();
_soundProvider.DiscardSamples();
}
void ISyncSoundProvider.GetSamples(out short[] samples, out int nsamp)
public void GetSamplesSync(out short[] samples, out int nsamp)
{
short[] sampin;
int nsampin;
syncinput.GetSamples(out sampin, out nsampin);
_soundProvider.GetSamplesSync(out sampin, out nsampin);
short[] ret = new short[nsampin * 2];
PushThroughSamples(sampin, ret, nsampin * 2);
samples = ret;
nsamp = nsampin;
}
void ISyncSoundProvider.DiscardSamples()
public SyncSoundMode SyncMode
{
syncinput.DiscardSamples();
get { return _soundProvider.SyncMode; }
}
public bool CanProvideAsync
{
get { return _soundProvider.CanProvideAsync; }
}
public void SetSyncMode(SyncSoundMode mode)
{
_soundProvider.SetSyncMode(mode);
}
}
}

View File

@ -4,23 +4,26 @@ using System.Collections.Generic;
namespace BizHawk.Emulation.Common
{
/// <summary>
/// uses Metaspu to have an ISyncSoundProvider input to a ISoundProvider
/// uses Metaspu to provide async sound to an ISoundProvider that does not provide its own async implementation
/// </summary>
public class MetaspuAsync : IAsyncSoundProvider
// Sound Refactor TODO: rename me to MetaspuAsyncSoundProvider
public class MetaspuAsync : ISoundProvider
{
private readonly ISynchronizingAudioBuffer buffer;
private readonly ISyncSoundProvider input;
public MetaspuAsync(ISyncSoundProvider input, ESynchMethod method)
private readonly ISoundProvider input;
public MetaspuAsync(ISoundProvider input, ESynchMethod method)
{
input.SetSyncMode(SyncSoundMode.Sync);
buffer = Metaspu.metaspu_construct(method);
this.input = input;
}
public void GetSamples(short[] samples)
public void GetSamplesAsync(short[] samples)
{
short[] sampin;
int numsamp;
input.GetSamples(out sampin, out numsamp);
input.GetSamplesSync(out sampin, out numsamp);
buffer.enqueue_samples(sampin, numsamp);
buffer.output_samples(samples, samples.Length / 2);
}
@ -30,37 +33,86 @@ namespace BizHawk.Emulation.Common
input.DiscardSamples();
buffer.clear();
}
public bool CanProvideAsync
{
get { return true; }
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Async; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Async)
{
throw new NotSupportedException("Only Async mode is supported");
}
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
throw new InvalidOperationException("Sync mode not supported");
}
}
public class MetaspuSoundProvider : IAsyncSoundProvider
// An async sound provider
// Sound refactor TODO: can this be combined with the other Metaspu?
public class MetaspuSoundProvider : ISoundProvider
{
public ISynchronizingAudioBuffer buffer;
public MetaspuSoundProvider(ESynchMethod method)
{
buffer = Metaspu.metaspu_construct(method);
Buffer = Metaspu.metaspu_construct(method);
}
public ISynchronizingAudioBuffer Buffer { get; set; }
private readonly short[] pullBuffer = new short[1470];
public MetaspuSoundProvider()
: this(ESynchMethod.ESynchMethod_V)
{
}
private readonly short[] pullBuffer = new short[1470];
public void PullSamples(IAsyncSoundProvider source)
{
Array.Clear(pullBuffer, 0, 1470);
source.GetSamples(pullBuffer);
buffer.enqueue_samples(pullBuffer, 735);
Buffer.enqueue_samples(pullBuffer, 735);
}
public void GetSamples(short[] samples)
public bool CanProvideAsync
{
buffer.output_samples(samples, samples.Length / 2);
get { return true; }
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Async; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Async)
{
throw new NotSupportedException("Only Async mode is supported.");
}
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
throw new InvalidOperationException("Sync mode is not supported.");
}
public void GetSamplesAsync(short[] samples)
{
Buffer.output_samples(samples, samples.Length / 2);
}
public void DiscardSamples()
{
buffer.clear();
Buffer.clear();
}
}
@ -104,7 +156,6 @@ namespace BizHawk.Emulation.Common
}
}
class ZeromusSynchronizer : ISynchronizingAudioBuffer
{
public ZeromusSynchronizer()

View File

@ -33,13 +33,17 @@ namespace BizHawk.Emulation.Common
public void DiscardSamples()
{
foreach (var soundSource in SoundProviders)
{
soundSource.DiscardSamples();
}
}
public void GetSamples(short[] samples)
{
foreach (var soundSource in SoundProviders)
{
soundSource.GetSamples(samples);
}
}
// Splits the volume space equally between available sources.

View File

@ -6,7 +6,7 @@ namespace BizHawk.Emulation.Common
/// <summary>
/// junk wrapper around LibSpeexDSP. quite inefficient. will be replaced
/// </summary>
public class SpeexResampler : IDisposable, ISyncSoundProvider
public class SpeexResampler : IDisposable, ISoundProvider
{
static class LibSpeexDSP
{
@ -250,7 +250,6 @@ namespace BizHawk.Emulation.Common
public static extern string speex_resampler_strerror(RESAMPLER_ERR err);
}
/// <summary>
/// opaque pointer to state
/// </summary>
@ -266,12 +265,12 @@ namespace BizHawk.Emulation.Common
private short[] outbuf;
// for ISyncSoundProvider
// for sync
private short[] outbuf2 = new short[16];
private int outbuf2pos = 0;
// to accept an ISyncSoundProvder input
private readonly ISyncSoundProvider input;
private readonly ISoundProvider input;
/// <summary>
/// in buffer position in samples (not sample pairs)
@ -309,7 +308,7 @@ namespace BizHawk.Emulation.Common
/// <param name="srateout">sampling rate out, rounded to nearest hz</param>
/// <param name="drainer">function which accepts output as produced. if null, act as an ISyncSoundProvider</param>
/// <param name="input">source to take input from when output is requested. if null, no autofetching</param>
public SpeexResampler(int quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action<short[], int> drainer = null, ISyncSoundProvider input = null)
public SpeexResampler(int quality, uint rationum, uint ratioden, uint sratein, uint srateout, Action<short[], int> drainer = null, ISoundProvider input = null)
{
if (drainer != null && input != null)
throw new ArgumentException("Can't autofetch without being an ISyncSoundProvider?");
@ -417,13 +416,13 @@ namespace BizHawk.Emulation.Common
outbuf2pos += nsamp * 2;
}
public void GetSamples(out short[] samples, out int nsamp)
public void GetSamplesSync(out short[] samples, out int nsamp)
{
if (input != null)
{
short[] sampin;
int nsampin;
input.GetSamples(out sampin, out nsampin);
input.GetSamplesSync(out sampin, out nsampin);
EnqueueSamples(sampin, nsampin);
}
Flush();
@ -436,6 +435,29 @@ namespace BizHawk.Emulation.Common
{
outbuf2pos = 0;
}
public bool CanProvideAsync
{
get { return false; }
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
}
}

View File

@ -160,9 +160,6 @@
</Compile>
<Compile Include="Calculator\TI83LinkPort.cs" />
<Compile Include="Computers\AppleII\AppleII.cs" />
<Compile Include="Computers\AppleII\AppleII.IAudioProvider.cs">
<DependentUpon>AppleII.cs</DependentUpon>
</Compile>
<Compile Include="Computers\AppleII\AppleII.IDebuggable.cs">
<DependentUpon>AppleII.cs</DependentUpon>
</Compile>
@ -181,6 +178,9 @@
<Compile Include="Computers\AppleII\AppleII.ISettable.cs">
<DependentUpon>AppleII.cs</DependentUpon>
</Compile>
<Compile Include="Computers\AppleII\AppleII.ISoundProvider.cs">
<DependentUpon>AppleII.cs</DependentUpon>
</Compile>
<Compile Include="Computers\AppleII\AppleII.IStatable.cs">
<DependentUpon>AppleII.cs</DependentUpon>
</Compile>
@ -375,6 +375,9 @@
<Compile Include="Consoles\Atari\lynx\Lynx.ISaveRam.cs">
<DependentUpon>Lynx.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\lynx\Lynx.ISoundProvider.cs">
<DependentUpon>Lynx.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Atari\lynx\Lynx.IStatable.cs">
<DependentUpon>Lynx.cs</DependentUpon>
</Compile>
@ -397,6 +400,9 @@
<Compile Include="Consoles\Coleco\ColecoVision.IStatable.cs">
<DependentUpon>ColecoVision.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Coleco\ColecoVision.ISoundProvider.cs">
<DependentUpon>ColecoVision.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Coleco\Input.cs" />
<Compile Include="Consoles\Coleco\MemoryMap.cs" />
<Compile Include="Consoles\Coleco\TMS9918A.cs" />
@ -439,6 +445,9 @@
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.ISettable.cs">
<DependentUpon>Gambatte.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.ISoundProvider.cs">
<DependentUpon>Gambatte.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\Gameboy\Gambatte.IStatable.cs">
<DependentUpon>Gambatte.cs</DependentUpon>
</Compile>
@ -514,6 +523,9 @@
<Compile Include="Consoles\Nintendo\GBA\VBANext.ISettings.cs">
<DependentUpon>VBANext.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\VBANext.ISoundProvider.cs">
<DependentUpon>VBANext.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\GBA\VBANext.IStatable.cs">
<DependentUpon>VBANext.cs</DependentUpon>
</Compile>
@ -871,6 +883,9 @@
<Compile Include="Consoles\Nintendo\QuickNES\QuickNES.ISettable.cs">
<DependentUpon>QuickNES.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\QuickNES\QuickNES.ISoundProvider.cs">
<DependentUpon>QuickNES.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Nintendo\QuickNES\QuickNES.IStatable.cs">
<DependentUpon>QuickNES.cs</DependentUpon>
</Compile>
@ -905,6 +920,9 @@
<Compile Include="Consoles\Sega\Genesis\Genesis.Input.cs" />
<Compile Include="Consoles\Sega\Genesis\Genesis.IO.cs" />
<Compile Include="Consoles\Sega\Genesis\Native68000\Musashi.cs" />
<Compile Include="Consoles\Sega\gpgx64\GPGX.ISoundProvider.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx\GenDbgHlp.cs" />
<Compile Include="Consoles\Sega\gpgx\GPGX.cs" />
<Compile Include="Consoles\Sega\gpgx\GPGX.ICodeDataLogger.cs">
@ -934,6 +952,9 @@
<Compile Include="Consoles\Sega\gpgx\GPGX.ISettable.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx\GPGX.ISoundProvider.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\gpgx\GPGX.IStatable.cs">
<DependentUpon>GPGX.cs</DependentUpon>
</Compile>
@ -1034,6 +1055,9 @@
<Compile Include="Consoles\Sega\SMS\SMS.ISettable.cs">
<DependentUpon>SMS.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\SMS\SMS.ISoundProvider.cs">
<DependentUpon>SMS.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\Sega\SMS\SMS.IStatable.cs">
<DependentUpon>SMS.cs</DependentUpon>
</Compile>
@ -1077,6 +1101,9 @@
<Compile Include="Consoles\WonderSwan\WonderSwan.ISettable.cs">
<DependentUpon>WonderSwan.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\WonderSwan\WonderSwan.ISoundProvider.cs">
<DependentUpon>WonderSwan.cs</DependentUpon>
</Compile>
<Compile Include="Consoles\WonderSwan\WonderSwan.IStatable.cs">
<DependentUpon>WonderSwan.cs</DependentUpon>
</Compile>

View File

@ -6,23 +6,6 @@ namespace BizHawk.Emulation.Cores.Calculators
{
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public IAsyncSoundProvider SoundProvider
{
get { return NullSound.SilenceProvider; }
}
public ISyncSoundProvider SyncSoundProvider
{
get { return new FakeSyncSound(NullSound.SilenceProvider, 735); }
}
public bool StartAsyncSound()
{
return true;
}
public void EndAsyncSound() { }
public ControllerDefinition ControllerDefinition
{
get { return TI83Controller; }

View File

@ -1,10 +1,5 @@
using System;
using System.Globalization;
using System.IO;
using System.Collections.Generic;
using BizHawk.Common;
using BizHawk.Common.NumberExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components.Z80;
@ -19,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Calculators
isPorted: false,
isReleased: true
)]
[ServiceNotApplicable(typeof(ISaveRam), typeof(IRegionable), typeof(IDriveLight))]
[ServiceNotApplicable(typeof(ISoundProvider), typeof(ISaveRam), typeof(IRegionable), typeof(IDriveLight))]
public partial class TI83 : IEmulator, IVideoProvider, IStatable, IDebuggable, IInputPollable, ISettable<TI83.TI83Settings, object>
{
[CoreConstructor("TI83")]

View File

@ -1,21 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AppleII
{
partial class AppleII : ISyncSoundProvider
{
void ISyncSoundProvider.GetSamples(out short[] samples, out int nsamp)
{
_machine.Speaker.AudioService.GetSamples(out samples, out nsamp);
}
void ISyncSoundProvider.DiscardSamples()
{
_machine.Speaker.AudioService.Clear();
}
}
}

View File

@ -6,27 +6,6 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
{
public IEmulatorServiceProvider ServiceProvider { get; private set; }
[FeatureNotImplemented]
public IAsyncSoundProvider SoundProvider
{
get { return null; }
}
[FeatureNotImplemented]
public ISyncSoundProvider SyncSoundProvider
{
get { return this; }
}
[FeatureNotImplemented]
public bool StartAsyncSound()
{
return false;
}
[FeatureNotImplemented]
public void EndAsyncSound() { }
public ControllerDefinition ControllerDefinition
{
get { return AppleIIController; }
@ -36,9 +15,15 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
public int Frame { get; set; }
public string SystemId { get { return "AppleII"; } }
public string SystemId
{
get { return "AppleII"; }
}
public bool DeterministicEmulation { get { return true; } }
public bool DeterministicEmulation
{
get { return true; }
}
public void FrameAdvance(bool render, bool rendersound)
{

View File

@ -0,0 +1,41 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.AppleII
{
public partial class AppleII : ISoundProvider
{
public bool CanProvideAsync
{
get { return false; }
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
_machine.Speaker.AudioService.GetSamples(out samples, out nsamp);
}
public void DiscardSamples()
{
_machine.Speaker.AudioService.Clear();
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
}
}

View File

@ -13,7 +13,7 @@ namespace BizHawk.Emulation.Cores.Computers.AppleII
isReleased: true
)]
[ServiceNotApplicable(typeof(ISaveRam), typeof(IRegionable))]
public partial class AppleII : IEmulator, IDriveLight
public partial class AppleII : IEmulator, ISoundProvider, IVideoProvider, IStatable, IDriveLight
{
public AppleII(CoreComm comm, IEnumerable<GameInfo> gameInfoSet, IEnumerable<byte[]> romSet, Settings settings)
: this(comm, gameInfoSet.First(), romSet.First(), settings)

View File

@ -83,8 +83,10 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
if (_board.Sid != null)
{
SyncSoundProvider = DCFilter.AsISyncSoundProvider(_board.Sid, 512);
}
_soundProvider = new DCFilter(_board.Sid, 512);
((BasicServiceProvider)ServiceProvider).Register<ISoundProvider>(_soundProvider);
}
DeterministicEmulation = true;
((BasicServiceProvider) ServiceProvider).Register<IVideoProvider>(_board.Vic);
@ -136,13 +138,8 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
public bool DeterministicEmulation { get; set; }
[SaveState.SaveWithName("Frame")]
public int Frame { get; set; }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
[SaveState.DoNotSave]
public IAsyncSoundProvider SoundProvider { get { return null; } }
[SaveState.DoNotSave]
public ISyncSoundProvider SyncSoundProvider { get; private set; }
[SaveState.DoNotSave]
[SaveState.DoNotSave]
public ControllerDefinition ControllerDefinition { get { return C64ControllerDefinition; } }
[SaveState.DoNotSave]
public IController Controller { get { return _board.Controller; } set { _board.Controller = value; } }
@ -167,9 +164,16 @@ namespace BizHawk.Emulation.Cores.Computers.Commodore64
while (_frameCycles != 0);
}
#endregion
#endregion
private void DoCycle()
#region ISoundProvider
[SaveState.DoNotSave]
public ISoundProvider _soundProvider { get; private set; }
#endregion
private void DoCycle()
{
if (_frameCycles == 0) {
_board.InputRead = false;

View File

@ -1,41 +1,57 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Computers.Commodore64.MOS
{
public sealed partial class Sid : IAsyncSoundProvider, ISyncSoundProvider
{
public int MaxVolume
{
get { return short.MaxValue; }
set { }
}
public sealed partial class Sid : ISoundProvider
{
public bool CanProvideAsync
{
get { return false; }
}
public void DiscardSamples()
{
_outputBufferIndex = 0;
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamples(short[] samples)
{
Flush();
var length = Math.Min(samples.Length, _outputBufferIndex);
for (var i = 0; i < length; i++)
{
samples[i] = _outputBuffer[i];
}
_outputBufferIndex = 0;
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new InvalidOperationException("Only Sync mode is supported.");
}
}
public void GetSamples(out short[] samples, out int nsamp)
{
Flush();
samples = _outputBuffer;
nsamp = _outputBufferIndex >> 1;
_outputBufferIndex = 0;
}
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async is not available");
}
public void DiscardSamples()
{
_outputBufferIndex = 0;
}
// Expose this as GetSamplesAsync to support async sound
// There's not need to do this though unless this core wants to handle async in its own way (the client can handle these situations if not available from the core)
private void GetSamples(short[] samples)
{
Flush();
var length = Math.Min(samples.Length, _outputBufferIndex);
for (var i = 0; i < length; i++)
{
samples[i] = _outputBuffer[i];
}
_outputBufferIndex = 0;
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
Flush();
samples = _outputBuffer;
nsamp = _outputBufferIndex >> 1;
_outputBufferIndex = 0;
}
}
}

View File

@ -310,11 +310,11 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
_pal = DetectPal(_game, Rom);
}
_tia = new TIA(this, _pal, Settings.SECAMColors);
// dcfilter coefficent is from real observed hardware behavior: a latched "1" will fully decay by ~170 or so tia sound cycles
_tia = new TIA(this, _pal, Settings.SECAMColors, CoreComm.VsyncRate > 55.0 ? 735 : 882);
_tia.GetFrameRate(out CoreComm.VsyncNum, out CoreComm.VsyncDen);
// dcfilter coefficent is from real observed hardware behavior: a latched "1" will fully decay by ~170 or so tia sound cycles
_dcfilter = DCFilter.AsISoundProvider(_tia, 256);
_dcfilter = new DCFilter(_tia, 256);
M6532 = new M6532(this);

View File

@ -65,6 +65,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
ser.Register<IDisassemblable>(Cpu);
ser.Register<ITraceable>(Tracer);
ser.Register<IVideoProvider>(_tia);
ser.Register<ISoundProvider>(_dcfilter);
}
public IEmulatorServiceProvider ServiceProvider { get; private set; }
@ -80,17 +81,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
public CoreComm CoreComm { get; private set; }
public IAsyncSoundProvider SoundProvider { get { return _dcfilter; } }
// todo: make this not so ugly
public ISyncSoundProvider SyncSoundProvider
{
get
{
return new FakeSyncSound(_dcfilter, CoreComm.VsyncRate > 55.0 ? 735 : 882);
}
}
public ControllerDefinition ControllerDefinition { get { return Atari2600ControllerDefinition; } }
public IController Controller { get; set; }
@ -123,10 +113,6 @@ namespace BizHawk.Emulation.Cores.Atari.Atari2600
};
}
public bool StartAsyncSound() { return true; }
public void EndAsyncSound() { }
public void ResetCounters()
{
_frame = 0;

File diff suppressed because it is too large Load Diff

View File

@ -35,6 +35,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari7800
{
ServiceProvider = new BasicServiceProvider(this);
(ServiceProvider as BasicServiceProvider).Register<IVideoProvider>(avProvider);
(ServiceProvider as BasicServiceProvider).Register<ISoundProvider>(avProvider);
InputCallbacks = new InputCallbackSystem();
CoreComm = comm;
byte[] highscoreBIOS = comm.CoreFileProvider.GetFirmware("A78", "Bios_HSC", false, "Some functions may not work without the high score BIOS.");
@ -206,14 +207,9 @@ namespace BizHawk.Emulation.Cores.Atari.Atari7800
#region audio\video
public ISyncSoundProvider SyncSoundProvider { get { return avProvider; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public IAsyncSoundProvider SoundProvider { get { return null; } }
MyAVProvider avProvider = new MyAVProvider();
class MyAVProvider : IVideoProvider, ISyncSoundProvider, IDisposable
class MyAVProvider : IVideoProvider, ISoundProvider, IDisposable
{
public FrameBuffer framebuffer { get; private set; }
public void ConnectToMachine(MachineBase m, EMU7800.Win.GameProgram g)
@ -231,7 +227,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari7800
resampler.Dispose();
resampler = new SpeexResampler(3, newsamplerate, 44100, newsamplerate, 44100, null, null);
samplerate = newsamplerate;
dcfilter = DCFilter.DetatchedMode(256);
dcfilter = new DCFilter(256);
}
if (g.MachineType == MachineType.A7800PAL || g.MachineType == MachineType.A2600PAL)
palette = TIATables.PALPalette;
@ -274,7 +270,14 @@ namespace BizHawk.Emulation.Cores.Atari.Atari7800
public int BufferHeight { get; private set; }
public int BackgroundColor { get { return unchecked((int)0xff000000); } }
public void GetSamples(out short[] samples, out int nsamp)
#region ISoundProvider
public bool CanProvideAsync
{
get { return false; }
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
int nsampin = framebuffer.SoundBufferByteLength;
unsafe
@ -291,16 +294,36 @@ namespace BizHawk.Emulation.Cores.Atari.Atari7800
}
}
resampler.GetSamples(out samples, out nsamp);
resampler.GetSamplesSync(out samples, out nsamp);
dcfilter.PushThroughSamples(samples, nsamp * 2);
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
public void DiscardSamples()
{
if (resampler != null)
resampler.DiscardSamples();
}
#endregion
public void Dispose()
{
if (resampler != null)
@ -310,6 +333,7 @@ namespace BizHawk.Emulation.Cores.Atari.Atari7800
}
}
}
#endregion
}
}

View File

@ -0,0 +1,45 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Atari.Lynx
{
public partial class Lynx : ISoundProvider
{
private short[] soundbuff = new short[2048];
private int numsamp;
public bool CanProvideAsync
{
get { return false; }
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = soundbuff;
nsamp = numsamp;
}
public void DiscardSamples()
{
// Nothing to do
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
}
}

View File

@ -13,7 +13,7 @@ namespace BizHawk.Emulation.Cores.Atari.Lynx
{
[CoreAttributes("Handy", "K. Wilkins", true, true, "mednafen 0-9-34-1", "http://mednafen.sourceforge.net/")]
[ServiceNotApplicable(typeof(ISettable<,>), typeof(IDriveLight), typeof(IRegionable))]
public partial class Lynx : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IStatable, IInputPollable
public partial class Lynx : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IStatable, IInputPollable
{
IntPtr Core;
@ -192,27 +192,5 @@ namespace BizHawk.Emulation.Cores.Atari.Lynx
}
#endregion
#region SoundProvider
short[] soundbuff = new short[2048];
int numsamp;
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public void GetSamples(out short[] samples, out int nsamp)
{
samples = soundbuff;
nsamp = numsamp;
}
public void DiscardSamples()
{
}
#endregion
}
}

View File

@ -0,0 +1,15 @@
using System;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Components;
namespace BizHawk.Emulation.Cores.ColecoVision
{
// Sound refactor TODO: Implement ISoundProvider here and sort this mess out
public partial class ColecoVision
{
public SN76489 PSG;
public IAsyncSoundProvider SoundProvider { get { return PSG; } }
//public ISyncSoundProvider SyncSoundProvider { get { return new FakeSyncSound(SoundProvider, 735); } }
}
}

View File

@ -16,13 +16,12 @@ namespace BizHawk.Emulation.Cores.ColecoVision
// ROM
public byte[] RomData;
public int RomLength;
public byte[] BiosRom;
// Machine
public Z80A Cpu;
public TMS9918A VDP;
public SN76489 PSG;
public byte[] Ram = new byte[1024];
private readonly TraceBuffer Tracer = new TraceBuffer();
@ -167,12 +166,6 @@ namespace BizHawk.Emulation.Cores.ColecoVision
public string SystemId { get { return "Coleco"; } }
public GameInfo game;
public CoreComm CoreComm { get; private set; }
public IAsyncSoundProvider SoundProvider { get { return PSG; } }
public string BoardName { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return new FakeSyncSound(SoundProvider, 735); } }
public bool StartAsyncSound() { return true; }
public void EndAsyncSound() { }
}
}

View File

@ -7,28 +7,6 @@ namespace BizHawk.Emulation.Cores.Intellivision
{
public IEmulatorServiceProvider ServiceProvider { get; private set; }
private DCFilter _dcfilter;
public IAsyncSoundProvider SoundProvider
{
get { return _dcfilter; }
}
public ISyncSoundProvider SyncSoundProvider
{
get { return new FakeSyncSound(_dcfilter, 735); }
}
public bool StartAsyncSound()
{
return true;
}
public void EndAsyncSound()
{
}
public ControllerDefinition ControllerDefinition
{
get { return ControllerDeck.Definition; }

View File

@ -64,8 +64,6 @@ namespace BizHawk.Emulation.Cores.Intellivision
(ServiceProvider as BasicServiceProvider).Register<ITraceable>(Tracer);
SetupMemoryDomains();
_dcfilter = DCFilter.AsISoundProvider(_psg, 256); // not sure how the 256 will change for intellivision I just copied it from Atari for now
}
public IntellivisionControllerDeck ControllerDeck { get; private set; }

View File

@ -5,7 +5,8 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Intellivision
{
public sealed class PSG : IAsyncSoundProvider
// Sound refactor todo: Implement ISoundProvider, and register _psg in the Intellivision core
public sealed class PSG
{
public ushort[] Register = new ushort[16];

View File

@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
[CoreAttributes("mGBA", "endrift", true, true, "0.5.0", "https://mgba.io/", false)]
[ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))]
public class MGBAHawk : IEmulator, IVideoProvider, ISyncSoundProvider, IGBAGPUViewable,
public class MGBAHawk : IEmulator, IVideoProvider, ISoundProvider, IGBAGPUViewable,
ISaveRam, IStatable, IInputPollable, ISettable<MGBAHawk.Settings, MGBAHawk.SyncSettings>
{
private IntPtr _core;
@ -192,9 +192,10 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
#endregion
#region ISoundProvider
private readonly short[] soundbuff = new short[2048];
private int nsamp;
public void GetSamples(out short[] samples, out int nsamp)
public void GetSamplesSync(out short[] samples, out int nsamp)
{
nsamp = this.nsamp;
samples = soundbuff;
@ -204,10 +205,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
nsamp = 0;
}
public IAsyncSoundProvider SoundProvider { get { throw new InvalidOperationException(); } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public bool CanProvideAsync
{
get { return false; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
#endregion
#region IMemoryDomains

View File

@ -16,7 +16,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
singleInstance: true
)]
[ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))]
public partial class GBA : IEmulator, IVideoProvider, ISyncSoundProvider, IGBAGPUViewable, ISaveRam, IStatable, IInputPollable
public partial class GBA : IEmulator, IVideoProvider, ISoundProvider, IGBAGPUViewable, ISaveRam, IStatable, IInputPollable
{
[CoreConstructor("GBA")]
public GBA(CoreComm comm, byte[] file)
@ -293,12 +293,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
short[] soundbuffer;
GCHandle soundhandle;
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public void GetSamples(out short[] samples, out int nsamp)
public void GetSamplesSync(out short[] samples, out int nsamp)
{
uint nbytes = LibMeteor.libmeteor_emptysound();
samples = soundbuffer;
@ -313,6 +308,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
LibMeteor.libmeteor_emptysound();
}
public bool CanProvideAsync
{
get { return false; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
#endregion
}
}

View File

@ -0,0 +1,44 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
public partial class VBANext : ISoundProvider
{
private short[] soundbuff = new short[2048];
private int numsamp;
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = soundbuff;
nsamp = numsamp;
}
public void DiscardSamples()
{
}
public bool CanProvideAsync
{
get { return false; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
}
}

View File

@ -1,22 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using Newtonsoft.Json;
using System.ComponentModel;
using BizHawk.Common;
using BizHawk.Emulation.Cores.Components.ARM;
namespace BizHawk.Emulation.Cores.Nintendo.GBA
{
[CoreAttributes("VBA-Next", "many authors", true, true, "cd508312a29ed8c29dacac1b11c2dce56c338a54", "https://github.com/libretro/vba-next")]
[ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))]
public partial class VBANext : IEmulator, IVideoProvider, ISyncSoundProvider, IInputPollable,
public partial class VBANext : IEmulator, IVideoProvider, ISoundProvider, IInputPollable,
IGBAGPUViewable, ISaveRam, IStatable, IDebuggable, ISettable<object, VBANext.SyncSettings>
{
IntPtr Core;
@ -226,27 +219,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.GBA
}
#endregion
#region SoundProvider
short[] soundbuff = new short[2048];
int numsamp;
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public void GetSamples(out short[] samples, out int nsamp)
{
samples = soundbuff;
nsamp = numsamp;
}
public void DiscardSamples()
{
}
#endregion
}
}

View File

@ -0,0 +1,124 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
public partial class Gameboy : ISoundProvider
{
public bool CanProvideAsync
{
get { return false; }
}
public void DiscardSamples()
{
soundoutbuffcontains = 0;
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = soundoutbuff;
nsamp = soundoutbuffcontains;
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
internal bool Muted
{
get { return _settings.Muted; }
}
// sample pairs before resampling
private short[] soundbuff = new short[(35112 + 2064) * 2];
private int soundoutbuffcontains = 0;
private short[] soundoutbuff = new short[2048];
private int latchL = 0;
private int latchR = 0;
private BlipBuffer blipL, blipR;
private uint blipAccumulate;
private void ProcessSound(int nsamp)
{
for (uint i = 0; i < nsamp; i++)
{
int curr = soundbuff[i * 2];
if (curr != latchL)
{
int diff = latchL - curr;
latchL = curr;
blipL.AddDelta(blipAccumulate, diff);
}
curr = soundbuff[i * 2 + 1];
if (curr != latchR)
{
int diff = latchR - curr;
latchR = curr;
blipR.AddDelta(blipAccumulate, diff);
}
blipAccumulate++;
}
}
private void ProcessSoundEnd()
{
blipL.EndFrame(blipAccumulate);
blipR.EndFrame(blipAccumulate);
blipAccumulate = 0;
soundoutbuffcontains = blipL.SamplesAvailable();
if (soundoutbuffcontains != blipR.SamplesAvailable())
{
throw new InvalidOperationException("Audio processing error");
}
blipL.ReadSamplesLeft(soundoutbuff, soundoutbuffcontains);
blipR.ReadSamplesRight(soundoutbuff, soundoutbuffcontains);
}
private void InitSound()
{
blipL = new BlipBuffer(1024);
blipL.SetRates(TICKSPERSECOND, 44100);
blipR = new BlipBuffer(1024);
blipR.SetRates(TICKSPERSECOND, 44100);
}
private void DisposeSound()
{
if (blipL != null)
{
blipL.Dispose();
blipL = null;
}
if (blipR != null)
{
blipR.Dispose();
blipR = null;
}
}
}
}

View File

@ -1,13 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.IO;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Common;
using Newtonsoft.Json;
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
@ -23,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
portedUrl: "http://gambatte.sourceforge.net/"
)]
[ServiceNotApplicable(typeof(IDriveLight), typeof(IDriveLight))]
public partial class Gameboy : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IStatable, IInputPollable, ICodeDataLogger,
public partial class Gameboy : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IStatable, IInputPollable, ICodeDataLogger,
IDebuggable, ISettable<Gameboy.GambatteSettings, Gameboy.GambatteSyncSettings>
{
/// <summary>
@ -566,103 +560,5 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
}
#endregion
#region ISoundProvider
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
/// <summary>
/// sample pairs before resampling
/// </summary>
short[] soundbuff = new short[(35112 + 2064) * 2];
int soundoutbuffcontains = 0;
short[] soundoutbuff = new short[2048];
int latchL = 0;
int latchR = 0;
BlipBuffer blipL, blipR;
uint blipAccumulate;
private void ProcessSound(int nsamp)
{
for (uint i = 0; i < nsamp; i++)
{
int curr = soundbuff[i * 2];
if (curr != latchL)
{
int diff = latchL - curr;
latchL = curr;
blipL.AddDelta(blipAccumulate, diff);
}
curr = soundbuff[i * 2 + 1];
if (curr != latchR)
{
int diff = latchR - curr;
latchR = curr;
blipR.AddDelta(blipAccumulate, diff);
}
blipAccumulate++;
}
}
private void ProcessSoundEnd()
{
blipL.EndFrame(blipAccumulate);
blipR.EndFrame(blipAccumulate);
blipAccumulate = 0;
soundoutbuffcontains = blipL.SamplesAvailable();
if (soundoutbuffcontains != blipR.SamplesAvailable())
throw new InvalidOperationException("Audio processing error");
blipL.ReadSamplesLeft(soundoutbuff, soundoutbuffcontains);
blipR.ReadSamplesRight(soundoutbuff, soundoutbuffcontains);
}
void InitSound()
{
blipL = new BlipBuffer(1024);
blipL.SetRates(TICKSPERSECOND, 44100);
blipR = new BlipBuffer(1024);
blipR.SetRates(TICKSPERSECOND, 44100);
}
void DisposeSound()
{
if (blipL != null)
{
blipL.Dispose();
blipL = null;
}
if (blipR != null)
{
blipR.Dispose();
blipR = null;
}
}
public void DiscardSamples()
{
soundoutbuffcontains = 0;
}
public void GetSamples(out short[] samples, out int nsamp)
{
samples = soundoutbuff;
nsamp = soundoutbuffcontains;
}
public bool Muted { get { return _settings.Muted; } }
#endregion
}
}

View File

@ -1,14 +1,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores.Nintendo.SNES;
using Newtonsoft.Json;
namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
{
[CoreAttributes(
@ -18,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
isReleased: true
)]
[ServiceNotApplicable(typeof(IDriveLight))]
public partial class GambatteLink : IEmulator, IVideoProvider, ISyncSoundProvider, IInputPollable, ISaveRam, IStatable, ILinkable,
public partial class GambatteLink : IEmulator, IVideoProvider, ISoundProvider, IInputPollable, ISaveRam, IStatable, ILinkable,
IDebuggable, ISettable<GambatteLink.GambatteLinkSettings, GambatteLink.GambatteLinkSyncSettings>, ICodeDataLogger
{
public GambatteLink(CoreComm comm, GameInfo leftinfo, byte[] leftrom, GameInfo rightinfo, byte[] rightrom, object Settings, object SyncSettings, bool deterministic)
@ -89,11 +83,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public static readonly ControllerDefinition DualGbController = new ControllerDefinition
{
Name = "Dual Gameboy Controller",
@ -327,7 +316,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
SampleBufferContains = count;
}
public void GetSamples(out short[] samples, out int nsamp)
public void GetSamplesSync(out short[] samples, out int nsamp)
{
nsamp = SampleBufferContains;
samples = SampleBuffer;
@ -338,6 +327,29 @@ namespace BizHawk.Emulation.Cores.Nintendo.Gameboy
SampleBufferContains = 0;
}
public bool CanProvideAsync
{
get { return false; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
#endregion
}
}

View File

@ -130,6 +130,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
_audioProvider = new N64Audio(api);
_inputProvider = new N64Input(this.AsInputPollable(), api, comm, this._syncSettings.Controllers);
(ServiceProvider as BasicServiceProvider).Register<IVideoProvider>(_videoProvider);
(ServiceProvider as BasicServiceProvider).Register<ISoundProvider>(_audioProvider.Resampler);
string rsp;
switch (_syncSettings.Rsp)
@ -268,14 +269,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.N64
public DisplayType Region { get { return _display_type; } }
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return _audioProvider.Resampler; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public ControllerDefinition ControllerDefinition
{
get { return _inputProvider.ControllerDefinition; }

View File

@ -79,7 +79,9 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
magicSoundProvider = null;
}
class MagicSoundProvider : IAsyncSoundProvider, ISyncSoundProvider, IDisposable
// Sound refactor todo: do we want to support async?
class MagicSoundProvider
: ISoundProvider, IDisposable
{
BlipBuffer blip;
NES nes;
@ -99,30 +101,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
//output = new Sound.Utilities.DCFilter(actualMetaspu);
}
public void GetSamples(short[] samples)
public bool CanProvideAsync
{
//Console.WriteLine("Sync: {0}", nes.apu.dlist.Count);
int nsamp = samples.Length / 2;
if (nsamp > blipbuffsize) // oh well.
nsamp = blipbuffsize;
uint targetclock = (uint)blip.ClocksNeeded(nsamp);
uint actualclock = nes.apu.sampleclock;
foreach (var d in nes.apu.dlist)
blip.AddDelta(d.time * targetclock / actualclock, d.value);
nes.apu.dlist.Clear();
blip.EndFrame(targetclock);
nes.apu.sampleclock = 0;
blip.ReadSamples(samples, nsamp, true);
// duplicate to stereo
for (int i = 0; i < nsamp * 2; i += 2)
samples[i + 1] = samples[i];
//mix in the cart's extra sound circuit
nes.Board.ApplyCustomAudio(samples);
get { return false; }
}
public void GetSamples(out short[] samples, out int nsamp)
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode != SyncSoundMode.Sync)
{
throw new NotSupportedException("Only sync mode is supported");
}
}
public void GetSamplesAsync(short[] samples)
{
throw new NotSupportedException("Async not supported");
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
//Console.WriteLine("ASync: {0}", nes.apu.dlist.Count);
foreach (var d in nes.apu.dlist)

View File

@ -70,6 +70,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
Tracer = new TraceBuffer { Header = cpu.TraceHeader };
ser.Register<ITraceable>(Tracer);
ser.Register<IVideoProvider>(videoProvider);
ser.Register<ISoundProvider>(magicSoundProvider);
if (Board is BANDAI_FCG_1)
{
@ -334,10 +335,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.NES
}
MyVideoProvider videoProvider;
public IAsyncSoundProvider SoundProvider { get { return magicSoundProvider; } }
public ISyncSoundProvider SyncSoundProvider { get { return magicSoundProvider; } }
public bool StartAsyncSound() { return true; }
public void EndAsyncSound() { }
[Obsolete] // with the changes to both nes and quicknes cores, nothing uses this anymore
public static readonly ControllerDefinition NESController =

View File

@ -0,0 +1,69 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
{
public partial class QuickNES : ISoundProvider
{
private short[] MonoBuff = new short[1024];
private short[] StereoBuff = new short[2048];
private int NumSamples = 0;
public bool CanProvideAsync
{
get { return false; }
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = StereoBuff;
nsamp = NumSamples;
}
public void DiscardSamples()
{
// Nothing to do
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
private void InitAudio()
{
LibQuickNES.ThrowStringError(QN.qn_set_sample_rate(Context, 44100));
}
private void DrainAudio()
{
NumSamples = QN.qn_read_audio(Context, MonoBuff, MonoBuff.Length);
unsafe
{
fixed (short* _src = &MonoBuff[0], _dst = &StereoBuff[0])
{
short* src = _src;
short* dst = _dst;
for (int i = 0; i < NumSamples; i++)
{
*dst++ = *src;
*dst++ = *src++;
}
}
}
}
}
}

View File

@ -1,16 +1,9 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Newtonsoft.Json;
using BizHawk.Common.BufferExtensions;
using BizHawk.Emulation.Common;
using BizHawk.Common;
using BizHawk.Common.CollectionExtensions;
using BizHawk.Emulation.Common.BizInvoke;
namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
@ -24,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
portedUrl: "https://github.com/kode54/QuickNES"
)]
[ServiceNotApplicable(typeof(IDriveLight))]
public partial class QuickNES : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IInputPollable,
public partial class QuickNES : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IInputPollable,
IStatable, IDebuggable, ISettable<QuickNES.QuickNESSettings, QuickNES.QuickNESSyncSettings>, Cores.Nintendo.NES.INESPPUViewable
{
static readonly LibQuickNES QN;
@ -320,52 +313,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Nintendo.QuickNES
throw new ObjectDisposedException(GetType().Name);
}
#region SoundProvider
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
void InitAudio()
{
LibQuickNES.ThrowStringError(QN.qn_set_sample_rate(Context, 44100));
}
void DrainAudio()
{
NumSamples = QN.qn_read_audio(Context, MonoBuff, MonoBuff.Length);
unsafe
{
fixed (short* _src = &MonoBuff[0], _dst = &StereoBuff[0])
{
short* src = _src;
short* dst = _dst;
for (int i = 0; i < NumSamples; i++)
{
*dst++ = *src;
*dst++ = *src++;
}
}
}
}
short[] MonoBuff = new short[1024];
short[] StereoBuff = new short[2048];
int NumSamples = 0;
public void GetSamples(out short[] samples, out int nsamp)
{
samples = StereoBuff;
nsamp = NumSamples;
}
public void DiscardSamples()
{
}
#endregion
#region Blacklist
// These games are known to not work in quicknes but quicknes thinks it can run them, bail out if one of these is loaded

View File

@ -84,6 +84,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
// start up audio resampler
InitAudio();
(ServiceProvider as BasicServiceProvider).Register<ISoundProvider>(resampler);
//strip header
if (romData != null)
@ -1300,10 +1301,6 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES
resampler.EnqueueSample((short)left, (short)right);
}
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return resampler; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
#endregion audio stuff

View File

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BizHawk.Common;
using BizHawk.Common.BufferExtensions;
@ -11,7 +10,7 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X
{
[CoreAttributes("Snes9x", "FIXME", true, false, "5e0319ab3ef9611250efb18255186d0dc0d7e125", "https://github.com/snes9xgit/snes9x", true)]
[ServiceNotApplicable(typeof(IDriveLight))]
public class Snes9x : IEmulator, IVideoProvider, ISyncSoundProvider
public class Snes9x : IEmulator, IVideoProvider, ISoundProvider
{
#region controller
@ -72,15 +71,11 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X
#endregion
#region ISyncSoundProvider
#region ISoundProvider
private short[] _sbuff = new short[2048];
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public void GetSamples(out short[] samples, out int nsamp)
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = _sbuff;
nsamp = 735;
@ -88,6 +83,30 @@ namespace BizHawk.Emulation.Cores.Nintendo.SNES9X
public void DiscardSamples()
{
// Nothing to do
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public bool CanProvideAsync
{
get { return false; }
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
#endregion

View File

@ -283,7 +283,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
void AdpcmEmitSample()
{
if (AdpcmIsPlaying == false)
SoundProvider.buffer.enqueue_sample(0, 0);
SoundProvider.Buffer.enqueue_sample(0, 0);
else
{
if (nextSampleTimer <= 0)
@ -303,13 +303,13 @@ namespace BizHawk.Emulation.Cores.PCEngine
}
short adjustedSample = (short)((playingSample - 2048) * MaxVolume / 2048);
SoundProvider.buffer.enqueue_sample(adjustedSample, adjustedSample);
SoundProvider.Buffer.enqueue_sample(adjustedSample, adjustedSample);
}
}
public void GetSamples(short[] samples)
{
SoundProvider.GetSamples(samples);
SoundProvider.GetSamplesAsync(samples);
}
public void DiscardSamples()

View File

@ -43,7 +43,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
public HuC6280PSG PSG;
public CDAudio CDAudio;
public SoundMixer SoundMixer;
public MetaspuSoundProvider SoundSynchronizer;
//public MetaspuSoundProvider SoundSynchronizer;
bool TurboGrafx { get { return Type == NecSystemType.TurboGrafx; } }
bool SuperGrafx { get { return Type == NecSystemType.SuperGrafx; } }
@ -171,7 +171,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
Cpu.ReadMemory21 = ReadMemory;
Cpu.WriteMemory21 = WriteMemory;
Cpu.WriteVDC = VDC1.WriteVDC;
soundProvider = PSG;
soundProvider = new FakeSyncSound(PSG, 735);
CDAudio = new CDAudio(null, 0);
}
@ -183,7 +183,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
Cpu.ReadMemory21 = ReadMemorySGX;
Cpu.WriteMemory21 = WriteMemorySGX;
Cpu.WriteVDC = VDC1.WriteVDC;
soundProvider = PSG;
soundProvider = new FakeSyncSound(PSG, 735);
CDAudio = new CDAudio(null, 0);
}
@ -199,8 +199,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
SetCDAudioCallback();
PSG.MaxVolume = short.MaxValue * 3 / 4;
SoundMixer = new SoundMixer(PSG, CDAudio, ADPCM);
SoundSynchronizer = new MetaspuSoundProvider(ESynchMethod.ESynchMethod_V);
soundProvider = SoundSynchronizer;
soundProvider = new FakeSyncSound(SoundMixer, 735);
Cpu.ThinkAction = (cycles) => { SCSI.Think(); ADPCM.Think(cycles); };
}
@ -305,6 +304,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
ser.Register<ITraceable>(Tracer);
ser.Register<IDisassemblable>(Cpu);
ser.Register<IVideoProvider>((IVideoProvider)VPC ?? VDC1);
ser.Register<ISoundProvider>(soundProvider);
SetupMemoryDomains();
}
@ -384,8 +384,6 @@ namespace BizHawk.Emulation.Cores.PCEngine
VDC1.ExecFrame(render);
PSG.EndFrame(Cpu.TotalExecutedCycles);
if (TurboCD)
SoundSynchronizer.PullSamples(SoundMixer);
if (lagged)
{
@ -406,14 +404,7 @@ namespace BizHawk.Emulation.Cores.PCEngine
public CoreComm CoreComm { get; private set; }
IAsyncSoundProvider soundProvider;
public IAsyncSoundProvider SoundProvider
{
get { return soundProvider; }
}
public ISyncSoundProvider SyncSoundProvider { get { return new FakeSyncSound(soundProvider, 735); } }
public bool StartAsyncSound() { return true; }
public void EndAsyncSound() { }
private ISoundProvider soundProvider;
public string SystemId { get { return systemid; } }
public string Region { get; set; }

View File

@ -301,13 +301,15 @@ namespace BizHawk.Emulation.Cores.Sega.Genesis
public CoreComm CoreComm { get; private set; }
// Sound refactor todo: Implement ISoundProvider
/*
public IAsyncSoundProvider SoundProvider
{
get { return SoundMixer; }
}
public ISyncSoundProvider SyncSoundProvider { get { return new FakeSyncSound(SoundMixer, 735); } }
public bool StartAsyncSound() { return true; }
public void EndAsyncSound() { }
*/
public int Frame { get; set; }
public int LagCount { get { return _lagcount; } set { _lagcount = value; } }

View File

@ -6,23 +6,6 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public IAsyncSoundProvider SoundProvider
{
get { return ActiveSoundProvider; }
}
public ISyncSoundProvider SyncSoundProvider
{
get { return new FakeSyncSound(ActiveSoundProvider, 735); }
}
public bool StartAsyncSound()
{
return true;
}
public void EndAsyncSound() { }
public ControllerDefinition ControllerDefinition
{
get

View File

@ -0,0 +1,13 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Sega.MasterSystem
{
// Sound refactor TODO: Implement ISoundProvider without the need for FakeSyncSound
public sealed partial class SMS
{
private FakeSyncSound _fakeSyncSound;
private IAsyncSoundProvider ActiveSoundProvider;
private SoundMixer SoundMixer;
}
}

View File

@ -42,7 +42,6 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
public VDP Vdp;
private SN76489 PSG;
private YM2413 YM2413;
private SoundMixer SoundMixer;
public bool IsGameGear { get; set; }
public bool IsSG1000 { get; set; }
@ -119,6 +118,8 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
if (HasYM2413 && game["WhenFMDisablePSG"])
SoundMixer.DisableSource(PSG);
ActiveSoundProvider = HasYM2413 ? (IAsyncSoundProvider)SoundMixer : PSG;
_fakeSyncSound = new FakeSyncSound(ActiveSoundProvider, 735);
(ServiceProvider as BasicServiceProvider).Register<ISoundProvider>(_fakeSyncSound);
SystemRam = new byte[0x2000];
@ -310,8 +311,6 @@ namespace BizHawk.Emulation.Cores.Sega.MasterSystem
else if (port == 0xF2 && HasYM2413) YM2413.DetectionValue = value;
}
private IAsyncSoundProvider ActiveSoundProvider;
private string _region;
private string RegionStr
{

View File

@ -24,7 +24,7 @@ namespace BizHawk.Emulation.Cores.Sega.Saturn
portedUrl: "http://yabause.org",
singleInstance: true
)]
public partial class Yabause : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IStatable, IInputPollable,
public partial class Yabause : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IStatable, IInputPollable,
ISettable<object, Yabause.SaturnSyncSettings>, IDriveLight
{
public static ControllerDefinition SaturnController = new ControllerDefinition
@ -354,20 +354,39 @@ namespace BizHawk.Emulation.Cores.Sega.Saturn
#region ISyncSoundProvider
short[] SoundBuffer = new short[44100 * 2];
int SoundNSamp = 0;
private short[] SoundBuffer = new short[44100 * 2];
private int SoundNSamp = 0;
public void GetSamples(out short[] samples, out int nsamp)
public void GetSamplesSync(out short[] samples, out int nsamp)
{
nsamp = SoundNSamp;
samples = SoundBuffer;
}
public void DiscardSamples() { }
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public bool CanProvideAsync
{
get { return false; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
#endregion

View File

@ -7,20 +7,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider
{
get { return this; }
}
public bool StartAsyncSound()
{
return false;
}
public void EndAsyncSound() { }
public ControllerDefinition ControllerDefinition { get; private set; }
public IController Controller { get; set; }

View File

@ -0,0 +1,57 @@
using System;
using BizHawk.Emulation.Common;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
{
public partial class GPGX : ISoundProvider
{
private short[] samples = new short[4096];
private int nsamp = 0;
public bool CanProvideAsync
{
get { return false; }
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
nsamp = this.nsamp;
samples = this.samples;
this.nsamp = 0;
}
public void DiscardSamples()
{
this.nsamp = 0;
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
private void update_audio()
{
IntPtr src = IntPtr.Zero;
LibGPGX.gpgx_get_audio(ref nsamp, ref src);
if (src != IntPtr.Zero)
{
Marshal.Copy(src, samples, 0, nsamp * 2);
}
}
}
}

View File

@ -14,7 +14,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
portedUrl: "https://code.google.com/p/genplus-gx/",
singleInstance: true
)]
public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable,
public partial class GPGX : IEmulator, IVideoProvider, ISaveRam, IStatable, IRegionable,
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
{
static GPGX AttachedCore = null;
@ -372,31 +372,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx
LibGPGX.gpgx_flush_vram(); // fully regenerate internal caches as needed
}
short[] samples = new short[4096];
int nsamp = 0;
public void GetSamples(out short[] samples, out int nsamp)
{
nsamp = this.nsamp;
samples = this.samples;
this.nsamp = 0;
}
public void DiscardSamples()
{
this.nsamp = 0;
}
void update_audio()
{
IntPtr src = IntPtr.Zero;
LibGPGX.gpgx_get_audio(ref nsamp, ref src);
if (src != IntPtr.Zero)
{
Marshal.Copy(src, samples, 0, nsamp * 2);
}
}
public DisplayType Region { get; private set; }
}
}

View File

@ -3,24 +3,10 @@ using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : IEmulator
public partial class GPGX : IEmulator, ISoundProvider
{
public IEmulatorServiceProvider ServiceProvider { get; private set; }
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider
{
get { return this; }
}
public bool StartAsyncSound()
{
return false;
}
public void EndAsyncSound() { }
public ControllerDefinition ControllerDefinition { get; private set; }
public IController Controller { get; set; }

View File

@ -0,0 +1,57 @@
using System;
using BizHawk.Emulation.Common;
using System.Runtime.InteropServices;
namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
{
public partial class GPGX : ISoundProvider
{
private short[] samples = new short[4096];
private int nsamp = 0;
public bool CanProvideAsync
{
get { return false; }
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
nsamp = this.nsamp;
samples = this.samples;
this.nsamp = 0;
}
public void DiscardSamples()
{
this.nsamp = 0;
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
private void update_audio()
{
IntPtr src = IntPtr.Zero;
Core.gpgx_get_audio(ref nsamp, ref src);
if (src != IntPtr.Zero)
{
Marshal.Copy(src, samples, 0, nsamp * 2);
}
}
}
}

View File

@ -17,7 +17,7 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
portedUrl: "https://code.google.com/p/genplus-gx/",
singleInstance: false
)]
public partial class GPGX : IEmulator, ISyncSoundProvider, IVideoProvider, ISaveRam, IStatable, IRegionable,
public partial class GPGX : IEmulator, IVideoProvider, ISaveRam, IStatable, IRegionable,
IInputPollable, IDebuggable, IDriveLight, ICodeDataLogger, IDisassemblable
{
LibGPGX Core;
@ -374,31 +374,6 @@ namespace BizHawk.Emulation.Cores.Consoles.Sega.gpgx64
Core.gpgx_flush_vram(); // fully regenerate internal caches as needed
}
short[] samples = new short[4096];
int nsamp = 0;
public void GetSamples(out short[] samples, out int nsamp)
{
nsamp = this.nsamp;
samples = this.samples;
this.nsamp = 0;
}
public void DiscardSamples()
{
this.nsamp = 0;
}
void update_audio()
{
IntPtr src = IntPtr.Zero;
Core.gpgx_get_audio(ref nsamp, ref src);
if (src != IntPtr.Zero)
{
Marshal.Copy(src, samples, 0, nsamp * 2);
}
}
public DisplayType Region { get; private set; }
}
}

View File

@ -1,8 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using BizHawk.Emulation.Common;
@ -15,7 +12,7 @@ namespace BizHawk.Emulation.Cores.Sony.PSP
isReleased: false,
singleInstance: true
)]
public class PSP : IEmulator, IVideoProvider, ISyncSoundProvider
public class PSP : IEmulator, IVideoProvider, ISoundProvider
{
public static readonly ControllerDefinition PSPController = new ControllerDefinition
{
@ -41,10 +38,6 @@ namespace BizHawk.Emulation.Cores.Sony.PSP
}
};
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public ControllerDefinition ControllerDefinition { get { return PSPController; } }
public IController Controller { get; set; }
public bool DeterministicEmulation { get { return true; } }
@ -186,7 +179,7 @@ namespace BizHawk.Emulation.Cores.Sony.PSP
readonly short[] audiobuffer = new short[2048 * 2];
int nsampavail = 0;
public void GetSamples(out short[] samples, out int nsamp)
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = audiobuffer;
nsamp = nsampavail;
@ -195,5 +188,28 @@ namespace BizHawk.Emulation.Cores.Sony.PSP
public void DiscardSamples()
{
}
public bool CanProvideAsync
{
get { return false; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
}
}

View File

@ -31,7 +31,7 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
isPorted: true,
isReleased: true
)]
public unsafe partial class Octoshock : IEmulator, IVideoProvider, ISyncSoundProvider, ISaveRam, IStatable, IDriveLight, ISettable<Octoshock.Settings, Octoshock.SyncSettings>, IRegionable, IInputPollable
public unsafe partial class Octoshock : IEmulator, IVideoProvider, ISoundProvider, ISaveRam, IStatable, IDriveLight, ISettable<Octoshock.Settings, Octoshock.SyncSettings>, IRegionable, IInputPollable
{
public string SystemId { get { return "PSX"; } }
@ -861,12 +861,7 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
private short[] sbuff = new short[1611 * 2]; //need this for pal
private int sbuffcontains = 0;
public IAsyncSoundProvider SoundProvider { get { throw new InvalidOperationException(); } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public void GetSamples(out short[] samples, out int nsamp)
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = sbuff;
nsamp = sbuffcontains;
@ -877,6 +872,29 @@ namespace BizHawk.Emulation.Cores.Sony.PSX
sbuffcontains = 0;
}
public bool CanProvideAsync
{
get { return false; }
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
#endregion
#region ISaveRam

View File

@ -0,0 +1,45 @@
using System;
using BizHawk.Emulation.Common;
namespace BizHawk.Emulation.Cores.WonderSwan
{
public partial class WonderSwan : ISoundProvider
{
private short[] sbuff = new short[1536];
private int sbuffcontains = 0;
public bool CanProvideAsync
{
get { return false; }
}
public void GetSamplesSync(out short[] samples, out int nsamp)
{
samples = sbuff;
nsamp = sbuffcontains;
}
public void DiscardSamples()
{
sbuffcontains = 0;
}
public void SetSyncMode(SyncSoundMode mode)
{
if (mode == SyncSoundMode.Async)
{
throw new NotSupportedException("Async mode is not supported.");
}
}
public SyncSoundMode SyncMode
{
get { return SyncSoundMode.Sync; }
}
public void GetSamplesAsync(short[] samples)
{
throw new InvalidOperationException("Async mode is not supported.");
}
}
}

View File

@ -12,7 +12,7 @@ namespace BizHawk.Emulation.Cores.WonderSwan
{
[CoreAttributes("Cygne/Mednafen", "Dox", true, true, "0.9.36.5", "http://mednafen.sourceforge.net/")]
[ServiceNotApplicable(typeof(IDriveLight), typeof(IRegionable))]
public partial class WonderSwan : IEmulator, IVideoProvider, ISyncSoundProvider,
public partial class WonderSwan : IEmulator, IVideoProvider, ISoundProvider,
IInputPollable, IDebuggable
{
[CoreConstructor("WSWAN")]
@ -215,28 +215,5 @@ namespace BizHawk.Emulation.Cores.WonderSwan
public int BackgroundColor { get { return unchecked((int)0xff000000); } }
#endregion
#region ISoundProvider
private short[] sbuff = new short[1536];
private int sbuffcontains = 0;
public IAsyncSoundProvider SoundProvider { get { throw new InvalidOperationException(); } }
public ISyncSoundProvider SyncSoundProvider { get { return this; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
public void GetSamples(out short[] samples, out int nsamp)
{
samples = sbuff;
nsamp = sbuffcontains;
}
public void DiscardSamples()
{
sbuffcontains = 0;
}
#endregion
}
}

View File

@ -496,6 +496,7 @@ namespace BizHawk.Emulation.Cores
CoreComm.VsyncDen = 10000000;
SetupResampler(av.timing.fps, av.timing.sample_rate);
(ServiceProvider as BasicServiceProvider).Register<ISoundProvider>(resampler);
ControllerDefinition = CreateControllerDefinition(_SyncSettings);
@ -737,11 +738,6 @@ namespace BizHawk.Emulation.Cores
#region ISoundProvider
public IAsyncSoundProvider SoundProvider { get { return null; } }
public ISyncSoundProvider SyncSoundProvider { get { return resampler; } }
public bool StartAsyncSound() { return false; }
public void EndAsyncSound() { }
SpeexResampler resampler;
short[] sampbuff = new short[0];

View File

@ -10,6 +10,7 @@ namespace BizHawk.Emulation.Cores.Components
// Emulates PSG audio unit of a PC Engine / Turbografx-16 / SuperGrafx.
// It is embedded on the CPU and doesn't have its own part number. None the less, it is emulated separately from the 6280 CPU.
// Sound refactor TODO: IMixedSoundProvider must inherit ISoundProvider
public sealed class HuC6280PSG : IMixedSoundProvider
{
public class PSGChannel
@ -144,6 +145,7 @@ namespace BizHawk.Emulation.Cores.Components
}
public void DiscardSamples() { }
public void GetSamples(short[] samples)
{
int elapsedCycles = (int)(frameStopTime - frameStartTime);