Sound cleanup.

This commit is contained in:
J.D. Purcell 2016-12-14 23:26:01 -05:00
parent a33a8252ff
commit d95edc273e
7 changed files with 56 additions and 45 deletions

View File

@ -717,6 +717,7 @@
<Compile Include="RomStatusPicker.Designer.cs"> <Compile Include="RomStatusPicker.Designer.cs">
<DependentUpon>RomStatusPicker.cs</DependentUpon> <DependentUpon>RomStatusPicker.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="Sound\Interfaces\ISoundOutput.cs" />
<Compile Include="Sound\Output\DirectSoundSoundOutput.cs" /> <Compile Include="Sound\Output\DirectSoundSoundOutput.cs" />
<Compile Include="Sound\Output\DummySoundOutput.cs" /> <Compile Include="Sound\Output\DummySoundOutput.cs" />
<Compile Include="Sound\Output\OpenALSoundOutput.cs" /> <Compile Include="Sound\Output\OpenALSoundOutput.cs" />

View File

@ -0,0 +1,14 @@
using System;
namespace BizHawk.Client.EmuHawk
{
public interface ISoundOutput : IDisposable
{
void StartSound();
void StopSound();
void ApplyVolumeSettings(double volume);
int MaxSamplesDeficit { get; }
int CalculateSamplesNeeded();
void WriteSamples(short[] samples, int sampleCount);
}
}

View File

@ -15,10 +15,9 @@ namespace BizHawk.Client.EmuHawk
private bool _disposed; private bool _disposed;
private readonly ISoundOutput _outputDevice; private readonly ISoundOutput _outputDevice;
private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider(); private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider(); // Buffer for Sync sources
private readonly BufferedAsync _bufferedAsync = new BufferedAsync(); private readonly BufferedAsync _bufferedAsync = new BufferedAsync(); // Buffer for Async sources
private ISoundProvider _sourceProvider; private IBufferedSoundProvider _bufferedProvider; // One of the preceding buffers, or null if no source is set
private SyncSoundMode _syncMode;
public Sound(IntPtr mainWindowHandle) public Sound(IntPtr mainWindowHandle)
{ {
@ -71,8 +70,7 @@ namespace BizHawk.Client.EmuHawk
_outputDevice.StopSound(); _outputDevice.StopSound();
_outputProvider.DiscardSamples(); if (_bufferedProvider != null) _bufferedProvider.DiscardSamples();
_bufferedAsync.DiscardSamples();
Global.SoundMaxBufferDeficitMs = 0; Global.SoundMaxBufferDeficitMs = 0;
@ -86,28 +84,27 @@ namespace BizHawk.Client.EmuHawk
/// </summary> /// </summary>
public void SetInputPin(ISoundProvider source) public void SetInputPin(ISoundProvider source)
{ {
_outputProvider.DiscardSamples(); if (_bufferedProvider != null)
_outputProvider.BaseSoundProvider = null;
_bufferedAsync.DiscardSamples();
_bufferedAsync.BaseSoundProvider = null;
_sourceProvider = source;
if (_sourceProvider == null)
{ {
return; _bufferedProvider.BaseSoundProvider = null;
_bufferedProvider.DiscardSamples();
_bufferedProvider = null;
} }
_syncMode = _sourceProvider.SyncMode; if (source == null) return;
if (_syncMode == SyncSoundMode.Sync)
if (source.SyncMode == SyncSoundMode.Sync)
{ {
_outputProvider.BaseSoundProvider = _sourceProvider; _bufferedProvider = _outputProvider;
} }
else else if (source.SyncMode == SyncSoundMode.Async)
{ {
_bufferedAsync.BaseSoundProvider = _sourceProvider;
_bufferedAsync.RecalculateMagic(Global.Emulator.CoreComm.VsyncRate); _bufferedAsync.RecalculateMagic(Global.Emulator.CoreComm.VsyncRate);
_bufferedProvider = _bufferedAsync;
} }
else throw new InvalidOperationException("Unsupported sync mode.");
_bufferedProvider.BaseSoundProvider = source;
} }
public bool LogUnderruns { get; set; } public bool LogUnderruns { get; set; }
@ -148,18 +145,12 @@ namespace BizHawk.Client.EmuHawk
public void UpdateSound(float atten) public void UpdateSound(float atten)
{ {
if (!Global.Config.SoundEnabled || !IsStarted || _sourceProvider == null || _disposed) if (!Global.Config.SoundEnabled || !IsStarted || _bufferedProvider == null || _disposed)
{ {
if (_sourceProvider != null) _sourceProvider.DiscardSamples(); if (_bufferedProvider != null) _bufferedProvider.DiscardSamples();
_outputProvider.DiscardSamples();
return; return;
} }
if (_sourceProvider.SyncMode != _syncMode)
{
throw new Exception("Sync mode changed unexpectedly.");
}
if (atten < 0) atten = 0; if (atten < 0) atten = 0;
if (atten > 1) atten = 1; if (atten > 1) atten = 1;
_outputDevice.ApplyVolumeSettings(atten); _outputDevice.ApplyVolumeSettings(atten);
@ -173,14 +164,13 @@ namespace BizHawk.Client.EmuHawk
samples = new short[samplesNeeded * ChannelCount]; samples = new short[samplesNeeded * ChannelCount];
samplesProvided = samplesNeeded; samplesProvided = samplesNeeded;
_sourceProvider.DiscardSamples(); _bufferedProvider.DiscardSamples();
_outputProvider.DiscardSamples();
} }
else if (_syncMode == SyncSoundMode.Sync) else if (_bufferedProvider == _outputProvider)
{ {
if (Global.Config.SoundThrottle) if (Global.Config.SoundThrottle)
{ {
_sourceProvider.GetSamplesSync(out samples, out samplesProvided); _outputProvider.BaseSoundProvider.GetSamplesSync(out samples, out samplesProvided);
while (samplesNeeded < samplesProvided && !Global.DisableSecondaryThrottling) while (samplesNeeded < samplesProvided && !Global.DisableSecondaryThrottling)
{ {
@ -197,7 +187,7 @@ namespace BizHawk.Client.EmuHawk
_outputProvider.GetSamples(samplesNeeded, out samples, out samplesProvided); _outputProvider.GetSamples(samplesNeeded, out samples, out samplesProvided);
} }
} }
else if (_syncMode == SyncSoundMode.Async) else if (_bufferedProvider == _bufferedAsync)
{ {
samples = new short[samplesNeeded * ChannelCount]; samples = new short[samplesNeeded * ChannelCount];
@ -223,14 +213,4 @@ namespace BizHawk.Client.EmuHawk
return samples * 1000.0 / SampleRate; return samples * 1000.0 / SampleRate;
} }
} }
public interface ISoundOutput : IDisposable
{
void StartSound();
void StopSound();
void ApplyVolumeSettings(double volume);
int MaxSamplesDeficit { get; }
int CalculateSamplesNeeded();
void WriteSamples(short[] samples, int sampleCount);
}
} }

View File

@ -16,7 +16,7 @@ namespace BizHawk.Client.EmuHawk
// perform a "soft" correction by resampling it to hopefully get back inside our // perform a "soft" correction by resampling it to hopefully get back inside our
// window shortly. If it ends up going too low or too high, we will perform a // window shortly. If it ends up going too low or too high, we will perform a
// "hard" correction by generating silence or discarding samples. // "hard" correction by generating silence or discarding samples.
public class SoundOutputProvider public class SoundOutputProvider : IBufferedSoundProvider
{ {
private const int SampleRate = 44100; private const int SampleRate = 44100;
private const int ChannelCount = 2; private const int ChannelCount = 2;

View File

@ -117,6 +117,7 @@
<Compile Include="EmulationExceptions.cs" /> <Compile Include="EmulationExceptions.cs" />
<Compile Include="Enums.cs" /> <Compile Include="Enums.cs" />
<Compile Include="Extensions.cs" /> <Compile Include="Extensions.cs" />
<Compile Include="Interfaces\IBufferedSoundProvider.cs" />
<Compile Include="Interfaces\IController.cs" /> <Compile Include="Interfaces\IController.cs" />
<Compile Include="Interfaces\ICoreFileProvider.cs" /> <Compile Include="Interfaces\ICoreFileProvider.cs" />
<Compile Include="Interfaces\IEmulator.cs" /> <Compile Include="Interfaces\IEmulator.cs" />

View File

@ -0,0 +1,15 @@
namespace BizHawk.Emulation.Common
{
public interface IBufferedSoundProvider
{
/// <summary>
/// The source audio provider.
/// </summary>
ISoundProvider BaseSoundProvider { get; set; }
/// <summary>
/// Clears any internally buffered samples, and discards samples from the base provider (if set).
/// </summary>
void DiscardSamples();
}
}

View File

@ -28,7 +28,7 @@ namespace BizHawk.Emulation.Common
* TODO: For systems that _really_ don't need BufferedAsync (pce not turbocd, sms), make a way to signal * TODO: For systems that _really_ don't need BufferedAsync (pce not turbocd, sms), make a way to signal
* that and then bypass the BufferedAsync. * that and then bypass the BufferedAsync.
*/ */
public sealed class BufferedAsync : ISoundProvider public sealed class BufferedAsync : ISoundProvider, IBufferedSoundProvider
{ {
public ISoundProvider BaseSoundProvider { get; set; } public ISoundProvider BaseSoundProvider { get; set; }