Configurable DirectSound buffer size.
A bunch of changes in Sound / SoundOutputProvider to support this and behave nicely.
This commit is contained in:
parent
a04982bc84
commit
69c66d12b1
|
@ -228,6 +228,7 @@ namespace BizHawk.Client.Common
|
||||||
public int SoundVolume = 100; // Range 0-100
|
public int SoundVolume = 100; // Range 0-100
|
||||||
public bool SoundThrottle = false;
|
public bool SoundThrottle = false;
|
||||||
public string SoundDevice = "";
|
public string SoundDevice = "";
|
||||||
|
public int SoundBufferSizeMs = 100;
|
||||||
public bool UseNewOutputBuffer = false;
|
public bool UseNewOutputBuffer = false;
|
||||||
|
|
||||||
// Log Window
|
// Log Window
|
||||||
|
|
|
@ -2093,6 +2093,13 @@ namespace BizHawk.Client.EmuHawk
|
||||||
Global.Config.DisplayInput ^= true;
|
Global.Config.DisplayInput ^= true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ToggleSound()
|
||||||
|
{
|
||||||
|
Global.Config.SoundEnabled ^= true;
|
||||||
|
GlobalWin.Sound.StopSound();
|
||||||
|
GlobalWin.Sound.StartSound();
|
||||||
|
}
|
||||||
|
|
||||||
private static void VolumeUp()
|
private static void VolumeUp()
|
||||||
{
|
{
|
||||||
Global.Config.SoundVolume += 10;
|
Global.Config.SoundVolume += 10;
|
||||||
|
@ -2101,17 +2108,10 @@ namespace BizHawk.Client.EmuHawk
|
||||||
Global.Config.SoundVolume = 100;
|
Global.Config.SoundVolume = 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalWin.Sound.ChangeVolume(Global.Config.SoundVolume);
|
GlobalWin.Sound.ApplyVolumeSettings();
|
||||||
GlobalWin.OSD.AddMessage("Volume " + Global.Config.SoundVolume);
|
GlobalWin.OSD.AddMessage("Volume " + Global.Config.SoundVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void ToggleSound()
|
|
||||||
{
|
|
||||||
Global.Config.SoundEnabled ^= true;
|
|
||||||
GlobalWin.Sound.UpdateSoundSettings();
|
|
||||||
GlobalWin.Sound.StartSound();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void VolumeDown()
|
private static void VolumeDown()
|
||||||
{
|
{
|
||||||
Global.Config.SoundVolume -= 10;
|
Global.Config.SoundVolume -= 10;
|
||||||
|
@ -2120,7 +2120,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
Global.Config.SoundVolume = 0;
|
Global.Config.SoundVolume = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalWin.Sound.ChangeVolume(Global.Config.SoundVolume);
|
GlobalWin.Sound.ApplyVolumeSettings();
|
||||||
GlobalWin.OSD.AddMessage("Volume " + Global.Config.SoundVolume);
|
GlobalWin.OSD.AddMessage("Volume " + Global.Config.SoundVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -43,18 +43,14 @@ namespace BizHawk.Client.EmuHawk
|
||||||
private const int BytesPerSample = 2;
|
private const int BytesPerSample = 2;
|
||||||
private const int ChannelCount = 2;
|
private const int ChannelCount = 2;
|
||||||
private const int BlockAlign = BytesPerSample * ChannelCount;
|
private const int BlockAlign = BytesPerSample * ChannelCount;
|
||||||
private const int BufferSizeMilliseconds = 100;
|
private const int MinBufferFullnessSamples = 55 * SampleRate / 1000;
|
||||||
private const int BufferSizeSamples = SampleRate * BufferSizeMilliseconds / 1000;
|
|
||||||
private const int BufferSizeBytes = BufferSizeSamples * BlockAlign;
|
|
||||||
private const double BufferSizeSeconds = (double)(BufferSizeBytes / BlockAlign) / SampleRate;
|
|
||||||
private const int MinBufferFullnessMilliseconds = 55;
|
|
||||||
private const int MinBufferFullnessSamples = SampleRate * MinBufferFullnessMilliseconds / 1000;
|
|
||||||
|
|
||||||
private bool _muted;
|
private bool _muted;
|
||||||
private bool _disposed;
|
private bool _disposed;
|
||||||
|
private DirectSound _device;
|
||||||
private SecondarySoundBuffer _deviceBuffer;
|
private SecondarySoundBuffer _deviceBuffer;
|
||||||
private readonly BufferedAsync _semiSync = new BufferedAsync();
|
private readonly BufferedAsync _semiSync = new BufferedAsync();
|
||||||
private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider();
|
private SoundOutputProvider _outputProvider;
|
||||||
private ISoundProvider _asyncSoundProvider;
|
private ISoundProvider _asyncSoundProvider;
|
||||||
private ISyncSoundProvider _syncSoundProvider;
|
private ISyncSoundProvider _syncSoundProvider;
|
||||||
private int _actualWriteOffsetBytes = -1;
|
private int _actualWriteOffsetBytes = -1;
|
||||||
|
@ -67,6 +63,24 @@ namespace BizHawk.Client.EmuHawk
|
||||||
if (device == null) return;
|
if (device == null) return;
|
||||||
|
|
||||||
device.SetCooperativeLevel(handle, CooperativeLevel.Priority);
|
device.SetCooperativeLevel(handle, CooperativeLevel.Priority);
|
||||||
|
_device = device;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int BufferSizeMilliseconds { get; set; }
|
||||||
|
|
||||||
|
private int BufferSizeSamples
|
||||||
|
{
|
||||||
|
get { return SampleRate * BufferSizeMilliseconds / 1000; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private int BufferSizeBytes
|
||||||
|
{
|
||||||
|
get { return BufferSizeSamples * BlockAlign; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateDeviceBuffer()
|
||||||
|
{
|
||||||
|
BufferSizeMilliseconds = Global.Config.SoundBufferSizeMs;
|
||||||
|
|
||||||
var format = new WaveFormat
|
var format = new WaveFormat
|
||||||
{
|
{
|
||||||
|
@ -81,25 +95,31 @@ namespace BizHawk.Client.EmuHawk
|
||||||
var desc = new SoundBufferDescription
|
var desc = new SoundBufferDescription
|
||||||
{
|
{
|
||||||
Format = format,
|
Format = format,
|
||||||
Flags =
|
Flags = BufferFlags.GlobalFocus | BufferFlags.Software | BufferFlags.GetCurrentPosition2 | BufferFlags.ControlVolume,
|
||||||
BufferFlags.GlobalFocus | BufferFlags.Software | BufferFlags.GetCurrentPosition2 | BufferFlags.ControlVolume,
|
|
||||||
SizeInBytes = BufferSizeBytes
|
SizeInBytes = BufferSizeBytes
|
||||||
};
|
};
|
||||||
|
|
||||||
_deviceBuffer = new SecondarySoundBuffer(device, desc);
|
_deviceBuffer = new SecondarySoundBuffer(_device, desc);
|
||||||
|
}
|
||||||
|
|
||||||
ChangeVolume(Global.Config.SoundVolume);
|
public void ApplyVolumeSettings()
|
||||||
|
{
|
||||||
|
if (_deviceBuffer == null) return;
|
||||||
|
|
||||||
//LogUnderruns = true;
|
if (Global.Config.SoundVolume == 0)
|
||||||
//_outputProvider.LogDebug = true;
|
_deviceBuffer.Volume = -5000;
|
||||||
|
else
|
||||||
|
_deviceBuffer.Volume = 0 - ((100 - Global.Config.SoundVolume) * 45);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StartSound()
|
public void StartSound()
|
||||||
{
|
{
|
||||||
if (_disposed) throw new ObjectDisposedException("Sound");
|
if (_disposed) throw new ObjectDisposedException("Sound");
|
||||||
if (Global.Config.SoundEnabled == false) return;
|
if (!Global.Config.SoundEnabled) return;
|
||||||
if (_deviceBuffer == null) return;
|
if (_deviceBuffer != null) return;
|
||||||
if (IsPlaying) return;
|
|
||||||
|
CreateDeviceBuffer();
|
||||||
|
ApplyVolumeSettings();
|
||||||
|
|
||||||
_deviceBuffer.Write(new byte[BufferSizeBytes], 0, LockFlags.EntireBuffer);
|
_deviceBuffer.Write(new byte[BufferSizeBytes], 0, LockFlags.EntireBuffer);
|
||||||
_deviceBuffer.CurrentPlayPosition = 0;
|
_deviceBuffer.CurrentPlayPosition = 0;
|
||||||
|
@ -109,34 +129,33 @@ namespace BizHawk.Client.EmuHawk
|
||||||
_filledBufferSizeBytes = 0;
|
_filledBufferSizeBytes = 0;
|
||||||
_lastWriteTime = 0;
|
_lastWriteTime = 0;
|
||||||
_lastWriteCursor = 0;
|
_lastWriteCursor = 0;
|
||||||
}
|
|
||||||
|
|
||||||
bool IsPlaying
|
_outputProvider = new SoundOutputProvider();
|
||||||
{
|
_outputProvider.HardCorrectionThresholdSamples = BufferSizeSamples - MinBufferFullnessSamples;
|
||||||
get
|
_outputProvider.BaseSoundProvider = _syncSoundProvider;
|
||||||
{
|
|
||||||
if (_deviceBuffer == null) return false;
|
//LogUnderruns = true;
|
||||||
if ((_deviceBuffer.Status & BufferStatus.Playing) != 0) return true;
|
//_outputProvider.LogDebug = true;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void StopSound()
|
public void StopSound()
|
||||||
{
|
{
|
||||||
if (!IsPlaying) return;
|
if (_deviceBuffer == null) return;
|
||||||
|
|
||||||
_deviceBuffer.Write(new byte[BufferSizeBytes], 0, LockFlags.EntireBuffer);
|
_deviceBuffer.Write(new byte[BufferSizeBytes], 0, LockFlags.EntireBuffer);
|
||||||
_deviceBuffer.Stop();
|
_deviceBuffer.Stop();
|
||||||
|
_deviceBuffer.Dispose();
|
||||||
|
_deviceBuffer = null;
|
||||||
|
|
||||||
|
_outputProvider = null;
|
||||||
|
|
||||||
|
BufferSizeMilliseconds = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
if (_disposed) return;
|
if (_disposed) return;
|
||||||
if (_deviceBuffer != null && _deviceBuffer.Disposed == false)
|
StopSound();
|
||||||
{
|
|
||||||
_deviceBuffer.Dispose();
|
|
||||||
_deviceBuffer = null;
|
|
||||||
}
|
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,7 +169,10 @@ namespace BizHawk.Client.EmuHawk
|
||||||
_semiSync.DiscardSamples();
|
_semiSync.DiscardSamples();
|
||||||
_semiSync.BaseSoundProvider = null;
|
_semiSync.BaseSoundProvider = null;
|
||||||
_syncSoundProvider = source;
|
_syncSoundProvider = source;
|
||||||
_outputProvider.BaseSoundProvider = source;
|
if (_outputProvider != null)
|
||||||
|
{
|
||||||
|
_outputProvider.BaseSoundProvider = source;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetAsyncInputPin(ISoundProvider source)
|
public void SetAsyncInputPin(ISoundProvider source)
|
||||||
|
@ -160,21 +182,29 @@ namespace BizHawk.Client.EmuHawk
|
||||||
_syncSoundProvider.DiscardSamples();
|
_syncSoundProvider.DiscardSamples();
|
||||||
_syncSoundProvider = null;
|
_syncSoundProvider = null;
|
||||||
}
|
}
|
||||||
_outputProvider.DiscardSamples();
|
if (_outputProvider != null)
|
||||||
_outputProvider.BaseSoundProvider = null;
|
{
|
||||||
|
_outputProvider.DiscardSamples();
|
||||||
|
_outputProvider.BaseSoundProvider = null;
|
||||||
|
}
|
||||||
_asyncSoundProvider = source;
|
_asyncSoundProvider = source;
|
||||||
_semiSync.BaseSoundProvider = source;
|
_semiSync.BaseSoundProvider = source;
|
||||||
_semiSync.RecalculateMagic(Global.CoreComm.VsyncRate);
|
_semiSync.RecalculateMagic(Global.CoreComm.VsyncRate);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool InitializeBufferWithSilence
|
private bool InitializeBufferWithSilence
|
||||||
{
|
{
|
||||||
get { return Global.Config.SoundThrottle; }
|
get { return true; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool RecoverFromUnderrunsWithSilence
|
private bool RecoverFromUnderrunsWithSilence
|
||||||
{
|
{
|
||||||
get { return Global.Config.SoundThrottle; }
|
get { return true; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private int SilenceLeaveRoomForFrameCount
|
||||||
|
{
|
||||||
|
get { return Global.Config.SoundThrottle ? 1 : 2; } // Why 2? I don't know, but it seems to work well with the clock throttle's behavior.
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool LogUnderruns { get; set; }
|
public bool LogUnderruns { get; set; }
|
||||||
|
@ -188,8 +218,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
if (_actualWriteOffsetBytes != -1)
|
if (_actualWriteOffsetBytes != -1)
|
||||||
{
|
{
|
||||||
double elapsedSeconds = (currentWriteTime - _lastWriteTime) / (double)Stopwatch.Frequency;
|
double elapsedSeconds = (currentWriteTime - _lastWriteTime) / (double)Stopwatch.Frequency;
|
||||||
|
double bufferSizeSeconds = (double)BufferSizeSamples / SampleRate;
|
||||||
int cursorDelta = CircularDistance(_lastWriteCursor, writeCursor, BufferSizeBytes);
|
int cursorDelta = CircularDistance(_lastWriteCursor, writeCursor, BufferSizeBytes);
|
||||||
cursorDelta += BufferSizeBytes * (int)Math.Round((elapsedSeconds - (cursorDelta / (double)(SampleRate * BlockAlign))) / BufferSizeSeconds);
|
cursorDelta += BufferSizeBytes * (int)Math.Round((elapsedSeconds - (cursorDelta / (double)(SampleRate * BlockAlign))) / bufferSizeSeconds);
|
||||||
_filledBufferSizeBytes -= cursorDelta;
|
_filledBufferSizeBytes -= cursorDelta;
|
||||||
if (_filledBufferSizeBytes < 0)
|
if (_filledBufferSizeBytes < 0)
|
||||||
{
|
{
|
||||||
|
@ -206,9 +237,8 @@ namespace BizHawk.Client.EmuHawk
|
||||||
int samplesNeeded = CircularDistance(_actualWriteOffsetBytes, playCursor, BufferSizeBytes) / BlockAlign;
|
int samplesNeeded = CircularDistance(_actualWriteOffsetBytes, playCursor, BufferSizeBytes) / BlockAlign;
|
||||||
if ((isInitializing && InitializeBufferWithSilence) || (detectedUnderrun && RecoverFromUnderrunsWithSilence))
|
if ((isInitializing && InitializeBufferWithSilence) || (detectedUnderrun && RecoverFromUnderrunsWithSilence))
|
||||||
{
|
{
|
||||||
// Fill the buffer with silence but leave enough empty for one frame's audio
|
|
||||||
int samplesPerFrame = (int)Math.Round(SampleRate / Global.Emulator.CoreComm.VsyncRate);
|
int samplesPerFrame = (int)Math.Round(SampleRate / Global.Emulator.CoreComm.VsyncRate);
|
||||||
int silenceSamples = Math.Max(samplesNeeded - samplesPerFrame, 0);
|
int silenceSamples = Math.Max(samplesNeeded - (SilenceLeaveRoomForFrameCount * samplesPerFrame), 0);
|
||||||
WriteSamples(new short[silenceSamples * 2], silenceSamples);
|
WriteSamples(new short[silenceSamples * 2], silenceSamples);
|
||||||
samplesNeeded -= silenceSamples;
|
samplesNeeded -= silenceSamples;
|
||||||
}
|
}
|
||||||
|
@ -244,7 +274,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
public void UpdateSound()
|
public void UpdateSound()
|
||||||
{
|
{
|
||||||
if (Global.Config.SoundEnabled == false || _disposed)
|
if (!Global.Config.SoundEnabled || _deviceBuffer == null || _disposed)
|
||||||
{
|
{
|
||||||
if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples();
|
if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples();
|
||||||
if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples();
|
if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples();
|
||||||
|
@ -281,7 +311,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
{
|
{
|
||||||
samples = new short[samplesNeeded * ChannelCount];
|
samples = new short[samplesNeeded * ChannelCount];
|
||||||
|
|
||||||
samplesProvided = _outputProvider.GetSamples(samples, samplesNeeded, samplesNeeded - (BufferSizeSamples - MinBufferFullnessSamples));
|
samplesProvided = _outputProvider.GetSamples(samples, samplesNeeded);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_asyncSoundProvider != null)
|
else if (_asyncSoundProvider != null)
|
||||||
|
@ -297,31 +327,6 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
WriteSamples(samples, samplesProvided);
|
WriteSamples(samples, samplesProvided);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Range: 0-100
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="vol"></param>
|
|
||||||
public void ChangeVolume(int vol)
|
|
||||||
{
|
|
||||||
if (vol > 100)
|
|
||||||
vol = 100;
|
|
||||||
if (vol < 0)
|
|
||||||
vol = 0;
|
|
||||||
Global.Config.SoundVolume = vol;
|
|
||||||
UpdateSoundSettings();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Uses Global.Config.SoundEnabled, this just notifies the object to read it
|
|
||||||
/// </summary>
|
|
||||||
public void UpdateSoundSettings()
|
|
||||||
{
|
|
||||||
if (!Global.Config.SoundEnabled || Global.Config.SoundVolume == 0)
|
|
||||||
_deviceBuffer.Volume = -5000;
|
|
||||||
else
|
|
||||||
_deviceBuffer.Volume = 0 - ((100 - Global.Config.SoundVolume) * 45);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
// Dummy implementation for non-Windows platforms for now.
|
// Dummy implementation for non-Windows platforms for now.
|
||||||
|
|
|
@ -14,19 +14,14 @@ namespace BizHawk.Client.EmuHawk
|
||||||
// samples here on average. As long as we're within +/-5 milliseconds we don't need
|
// samples here on average. As long as we're within +/-5 milliseconds we don't need
|
||||||
// to touch the source audio. Once it goes outside of that window, we'll start to
|
// to touch the source audio. Once it goes outside of that window, we'll start to
|
||||||
// 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 (-45 ms) and depleting the output
|
// window shortly. If it ends up going too low or too high, we will perform a
|
||||||
// device's buffer, or too high (+45 ms), we will perform a "hard" correction by
|
// "hard" correction by generating silence or discarding samples.
|
||||||
// generating silence or discarding samples.
|
|
||||||
public class SoundOutputProvider
|
public class SoundOutputProvider
|
||||||
{
|
{
|
||||||
private const int SampleRate = 44100;
|
private const int SampleRate = 44100;
|
||||||
private const int ChannelCount = 2;
|
private const int ChannelCount = 2;
|
||||||
private const int MaxExtraMilliseconds = 45;
|
private const int SoftCorrectionThresholdSamples = 5 * SampleRate / 1000;
|
||||||
private const int MaxExtraSamples = SampleRate * MaxExtraMilliseconds / 1000;
|
private const int StartupHardCorrectionThresholdSamples = 10 * SampleRate / 1000;
|
||||||
private const int MaxTargetOffsetMilliseconds = 5;
|
|
||||||
private const int MaxTargetOffsetSamples = SampleRate * MaxTargetOffsetMilliseconds / 1000;
|
|
||||||
private const int HardCorrectionMilliseconds = 20;
|
|
||||||
private const int HardCorrectionSamples = SampleRate * HardCorrectionMilliseconds / 1000;
|
|
||||||
private const int UsableHistoryLength = 20;
|
private const int UsableHistoryLength = 20;
|
||||||
private const int MaxHistoryLength = 60;
|
private const int MaxHistoryLength = 60;
|
||||||
private const int SoftCorrectionLength = 240;
|
private const int SoftCorrectionLength = 240;
|
||||||
|
@ -34,7 +29,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
private const int BaseSampleRateMaxHistoryLength = 300;
|
private const int BaseSampleRateMaxHistoryLength = 300;
|
||||||
private const int MinResamplingDistanceSamples = 3;
|
private const int MinResamplingDistanceSamples = 3;
|
||||||
|
|
||||||
private Queue<short> _buffer = new Queue<short>(MaxExtraSamples * ChannelCount);
|
private Queue<short> _buffer = new Queue<short>();
|
||||||
|
|
||||||
private Queue<int> _extraCountHistory = new Queue<int>();
|
private Queue<int> _extraCountHistory = new Queue<int>();
|
||||||
private Queue<int> _outputCountHistory = new Queue<int>();
|
private Queue<int> _outputCountHistory = new Queue<int>();
|
||||||
|
@ -46,12 +41,14 @@ namespace BizHawk.Client.EmuHawk
|
||||||
private short[] _resampleBuffer = new short[0];
|
private short[] _resampleBuffer = new short[0];
|
||||||
private double _resampleLengthRoundingError;
|
private double _resampleLengthRoundingError;
|
||||||
|
|
||||||
public ISyncSoundProvider BaseSoundProvider { get; set; }
|
|
||||||
|
|
||||||
public SoundOutputProvider()
|
public SoundOutputProvider()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int HardCorrectionThresholdSamples { get; set; }
|
||||||
|
|
||||||
|
public ISyncSoundProvider BaseSoundProvider { get; set; }
|
||||||
|
|
||||||
public void DiscardSamples()
|
public void DiscardSamples()
|
||||||
{
|
{
|
||||||
_buffer.Clear();
|
_buffer.Clear();
|
||||||
|
@ -76,14 +73,14 @@ namespace BizHawk.Client.EmuHawk
|
||||||
get { return SampleRate / Global.Emulator.CoreComm.VsyncRate; }
|
get { return SampleRate / Global.Emulator.CoreComm.VsyncRate; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetSamples(short[] samples, int idealSampleCount, int minSampleCount)
|
public int GetSamples(short[] samples, int idealSampleCount)
|
||||||
{
|
{
|
||||||
double scaleFactor = 1.0;
|
double scaleFactor = 1.0;
|
||||||
|
|
||||||
if (_extraCountHistory.Count >= UsableHistoryLength && !_hardCorrectionHistory.Any(c => c))
|
if (_extraCountHistory.Count >= UsableHistoryLength && !_hardCorrectionHistory.Any(c => c))
|
||||||
{
|
{
|
||||||
double offsetFromTarget = _extraCountHistory.Average();
|
double offsetFromTarget = _extraCountHistory.Average();
|
||||||
if (Math.Abs(offsetFromTarget) > MaxTargetOffsetSamples)
|
if (Math.Abs(offsetFromTarget) > SoftCorrectionThresholdSamples)
|
||||||
{
|
{
|
||||||
double correctionSpan = _outputCountHistory.Average() * SoftCorrectionLength;
|
double correctionSpan = _outputCountHistory.Average() * SoftCorrectionLength;
|
||||||
scaleFactor *= correctionSpan / (correctionSpan + offsetFromTarget);
|
scaleFactor *= correctionSpan / (correctionSpan + offsetFromTarget);
|
||||||
|
@ -94,11 +91,13 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
int bufferSampleCount = _buffer.Count / ChannelCount;
|
int bufferSampleCount = _buffer.Count / ChannelCount;
|
||||||
int extraSampleCount = bufferSampleCount - idealSampleCount;
|
int extraSampleCount = bufferSampleCount - idealSampleCount;
|
||||||
|
int hardCorrectionThresholdSamples = _extraCountHistory.Count >= UsableHistoryLength ? HardCorrectionThresholdSamples :
|
||||||
|
Math.Min(StartupHardCorrectionThresholdSamples, HardCorrectionThresholdSamples);
|
||||||
bool hardCorrected = false;
|
bool hardCorrected = false;
|
||||||
|
|
||||||
if (bufferSampleCount < minSampleCount)
|
if (extraSampleCount < -hardCorrectionThresholdSamples)
|
||||||
{
|
{
|
||||||
int generateSampleCount = (minSampleCount - bufferSampleCount) + HardCorrectionSamples;
|
int generateSampleCount = -extraSampleCount;
|
||||||
if (LogDebug) Console.WriteLine("Generating " + generateSampleCount + " samples");
|
if (LogDebug) Console.WriteLine("Generating " + generateSampleCount + " samples");
|
||||||
for (int i = 0; i < generateSampleCount * ChannelCount; i++)
|
for (int i = 0; i < generateSampleCount * ChannelCount; i++)
|
||||||
{
|
{
|
||||||
|
@ -106,9 +105,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
}
|
}
|
||||||
hardCorrected = true;
|
hardCorrected = true;
|
||||||
}
|
}
|
||||||
else if (extraSampleCount > MaxExtraSamples)
|
else if (extraSampleCount > hardCorrectionThresholdSamples)
|
||||||
{
|
{
|
||||||
int discardSampleCount = (extraSampleCount - MaxExtraSamples) + HardCorrectionSamples;
|
int discardSampleCount = extraSampleCount;
|
||||||
if (LogDebug) Console.WriteLine("Discarding " + discardSampleCount + " samples");
|
if (LogDebug) Console.WriteLine("Discarding " + discardSampleCount + " samples");
|
||||||
for (int i = 0; i < discardSampleCount * ChannelCount; i++)
|
for (int i = 0; i < discardSampleCount * ChannelCount; i++)
|
||||||
{
|
{
|
||||||
|
@ -236,7 +235,6 @@ namespace BizHawk.Client.EmuHawk
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
double roundingError = 0.0;
|
|
||||||
for (int iOutput = 0; iOutput < outputCount; iOutput++)
|
for (int iOutput = 0; iOutput < outputCount; iOutput++)
|
||||||
{
|
{
|
||||||
double iInput = ((double)iOutput / (outputCount - 1)) * (inputCount - 1);
|
double iInput = ((double)iOutput / (outputCount - 1)) * (inputCount - 1);
|
||||||
|
@ -250,17 +248,11 @@ namespace BizHawk.Client.EmuHawk
|
||||||
|
|
||||||
for (int iChannel = 0; iChannel < ChannelCount; iChannel++)
|
for (int iChannel = 0; iChannel < ChannelCount; iChannel++)
|
||||||
{
|
{
|
||||||
double valueExact =
|
double value =
|
||||||
input[iInput0 * ChannelCount + iChannel] * input0Weight +
|
input[iInput0 * ChannelCount + iChannel] * input0Weight +
|
||||||
input[iInput1 * ChannelCount + iChannel] * input1Weight +
|
input[iInput1 * ChannelCount + iChannel] * input1Weight;
|
||||||
roundingError;
|
|
||||||
|
|
||||||
if (valueExact < -32768.0) valueExact = -32768.0;
|
output[iOutput * ChannelCount + iChannel] = (short)((int)(value + 32768.5) - 32768);
|
||||||
if (valueExact > 32767.0) valueExact = 32767.0;
|
|
||||||
|
|
||||||
short value = (short)((int)(valueExact + 32768.5) - 32768);
|
|
||||||
output[iOutput * ChannelCount + iChannel] = value;
|
|
||||||
roundingError = valueExact - value;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -37,17 +37,21 @@
|
||||||
this.SoundVolNumeric = new System.Windows.Forms.NumericUpDown();
|
this.SoundVolNumeric = new System.Windows.Forms.NumericUpDown();
|
||||||
this.UseNewOutputBuffer = new System.Windows.Forms.CheckBox();
|
this.UseNewOutputBuffer = new System.Windows.Forms.CheckBox();
|
||||||
this.listBoxSoundDevices = new System.Windows.Forms.ListBox();
|
this.listBoxSoundDevices = new System.Windows.Forms.ListBox();
|
||||||
this.label1 = new System.Windows.Forms.Label();
|
this.SoundDeviceLabel = new System.Windows.Forms.Label();
|
||||||
|
this.BufferSizeLabel = new System.Windows.Forms.Label();
|
||||||
|
this.BufferSizeNumeric = new System.Windows.Forms.NumericUpDown();
|
||||||
|
this.BufferSizeUnitsLabel = new System.Windows.Forms.Label();
|
||||||
this.SoundVolGroup.SuspendLayout();
|
this.SoundVolGroup.SuspendLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.SoundVolBar)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.SoundVolBar)).BeginInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.SoundVolNumeric)).BeginInit();
|
((System.ComponentModel.ISupportInitialize)(this.SoundVolNumeric)).BeginInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.BufferSizeNumeric)).BeginInit();
|
||||||
this.SuspendLayout();
|
this.SuspendLayout();
|
||||||
//
|
//
|
||||||
// Cancel
|
// Cancel
|
||||||
//
|
//
|
||||||
this.Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.Cancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
this.Cancel.DialogResult = System.Windows.Forms.DialogResult.Cancel;
|
||||||
this.Cancel.Location = new System.Drawing.Point(317, 209);
|
this.Cancel.Location = new System.Drawing.Point(317, 244);
|
||||||
this.Cancel.Name = "Cancel";
|
this.Cancel.Name = "Cancel";
|
||||||
this.Cancel.Size = new System.Drawing.Size(75, 23);
|
this.Cancel.Size = new System.Drawing.Size(75, 23);
|
||||||
this.Cancel.TabIndex = 0;
|
this.Cancel.TabIndex = 0;
|
||||||
|
@ -59,7 +63,7 @@
|
||||||
//
|
//
|
||||||
this.OK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
this.OK.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.OK.DialogResult = System.Windows.Forms.DialogResult.OK;
|
this.OK.DialogResult = System.Windows.Forms.DialogResult.OK;
|
||||||
this.OK.Location = new System.Drawing.Point(236, 209);
|
this.OK.Location = new System.Drawing.Point(236, 244);
|
||||||
this.OK.Name = "OK";
|
this.OK.Name = "OK";
|
||||||
this.OK.Size = new System.Drawing.Size(75, 23);
|
this.OK.Size = new System.Drawing.Size(75, 23);
|
||||||
this.OK.TabIndex = 1;
|
this.OK.TabIndex = 1;
|
||||||
|
@ -135,19 +139,62 @@
|
||||||
| System.Windows.Forms.AnchorStyles.Left)
|
| System.Windows.Forms.AnchorStyles.Left)
|
||||||
| System.Windows.Forms.AnchorStyles.Right)));
|
| System.Windows.Forms.AnchorStyles.Right)));
|
||||||
this.listBoxSoundDevices.FormattingEnabled = true;
|
this.listBoxSoundDevices.FormattingEnabled = true;
|
||||||
this.listBoxSoundDevices.Location = new System.Drawing.Point(108, 108);
|
this.listBoxSoundDevices.Location = new System.Drawing.Point(108, 102);
|
||||||
this.listBoxSoundDevices.Name = "listBoxSoundDevices";
|
this.listBoxSoundDevices.Name = "listBoxSoundDevices";
|
||||||
this.listBoxSoundDevices.Size = new System.Drawing.Size(284, 95);
|
this.listBoxSoundDevices.Size = new System.Drawing.Size(284, 95);
|
||||||
this.listBoxSoundDevices.TabIndex = 6;
|
this.listBoxSoundDevices.TabIndex = 6;
|
||||||
//
|
//
|
||||||
// label1
|
// SoundDeviceLabel
|
||||||
//
|
//
|
||||||
this.label1.AutoSize = true;
|
this.SoundDeviceLabel.AutoSize = true;
|
||||||
this.label1.Location = new System.Drawing.Point(108, 92);
|
this.SoundDeviceLabel.Location = new System.Drawing.Point(108, 86);
|
||||||
this.label1.Name = "label1";
|
this.SoundDeviceLabel.Name = "SoundDeviceLabel";
|
||||||
this.label1.Size = new System.Drawing.Size(78, 13);
|
this.SoundDeviceLabel.Size = new System.Drawing.Size(78, 13);
|
||||||
this.label1.TabIndex = 7;
|
this.SoundDeviceLabel.TabIndex = 7;
|
||||||
this.label1.Text = "Sound Device:";
|
this.SoundDeviceLabel.Text = "Sound Device:";
|
||||||
|
//
|
||||||
|
// BufferSizeLabel
|
||||||
|
//
|
||||||
|
this.BufferSizeLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.BufferSizeLabel.AutoSize = true;
|
||||||
|
this.BufferSizeLabel.Location = new System.Drawing.Point(105, 213);
|
||||||
|
this.BufferSizeLabel.Name = "BufferSizeLabel";
|
||||||
|
this.BufferSizeLabel.Size = new System.Drawing.Size(61, 13);
|
||||||
|
this.BufferSizeLabel.TabIndex = 8;
|
||||||
|
this.BufferSizeLabel.Text = "Buffer Size:";
|
||||||
|
//
|
||||||
|
// BufferSizeNumeric
|
||||||
|
//
|
||||||
|
this.BufferSizeNumeric.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.BufferSizeNumeric.Location = new System.Drawing.Point(172, 211);
|
||||||
|
this.BufferSizeNumeric.Maximum = new decimal(new int[] {
|
||||||
|
250,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.BufferSizeNumeric.Minimum = new decimal(new int[] {
|
||||||
|
80,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
this.BufferSizeNumeric.Name = "BufferSizeNumeric";
|
||||||
|
this.BufferSizeNumeric.Size = new System.Drawing.Size(59, 20);
|
||||||
|
this.BufferSizeNumeric.TabIndex = 9;
|
||||||
|
this.BufferSizeNumeric.Value = new decimal(new int[] {
|
||||||
|
100,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
0});
|
||||||
|
//
|
||||||
|
// BufferSizeUnitsLabel
|
||||||
|
//
|
||||||
|
this.BufferSizeUnitsLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)));
|
||||||
|
this.BufferSizeUnitsLabel.AutoSize = true;
|
||||||
|
this.BufferSizeUnitsLabel.Location = new System.Drawing.Point(237, 213);
|
||||||
|
this.BufferSizeUnitsLabel.Name = "BufferSizeUnitsLabel";
|
||||||
|
this.BufferSizeUnitsLabel.Size = new System.Drawing.Size(63, 13);
|
||||||
|
this.BufferSizeUnitsLabel.TabIndex = 10;
|
||||||
|
this.BufferSizeUnitsLabel.Text = "milliseconds";
|
||||||
//
|
//
|
||||||
// SoundConfig
|
// SoundConfig
|
||||||
//
|
//
|
||||||
|
@ -155,8 +202,11 @@
|
||||||
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
|
||||||
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
|
||||||
this.CancelButton = this.Cancel;
|
this.CancelButton = this.Cancel;
|
||||||
this.ClientSize = new System.Drawing.Size(404, 244);
|
this.ClientSize = new System.Drawing.Size(404, 279);
|
||||||
this.Controls.Add(this.label1);
|
this.Controls.Add(this.BufferSizeUnitsLabel);
|
||||||
|
this.Controls.Add(this.BufferSizeNumeric);
|
||||||
|
this.Controls.Add(this.BufferSizeLabel);
|
||||||
|
this.Controls.Add(this.SoundDeviceLabel);
|
||||||
this.Controls.Add(this.listBoxSoundDevices);
|
this.Controls.Add(this.listBoxSoundDevices);
|
||||||
this.Controls.Add(this.UseNewOutputBuffer);
|
this.Controls.Add(this.UseNewOutputBuffer);
|
||||||
this.Controls.Add(this.SoundVolGroup);
|
this.Controls.Add(this.SoundVolGroup);
|
||||||
|
@ -174,6 +224,7 @@
|
||||||
this.SoundVolGroup.PerformLayout();
|
this.SoundVolGroup.PerformLayout();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.SoundVolBar)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.SoundVolBar)).EndInit();
|
||||||
((System.ComponentModel.ISupportInitialize)(this.SoundVolNumeric)).EndInit();
|
((System.ComponentModel.ISupportInitialize)(this.SoundVolNumeric)).EndInit();
|
||||||
|
((System.ComponentModel.ISupportInitialize)(this.BufferSizeNumeric)).EndInit();
|
||||||
this.ResumeLayout(false);
|
this.ResumeLayout(false);
|
||||||
this.PerformLayout();
|
this.PerformLayout();
|
||||||
|
|
||||||
|
@ -190,6 +241,9 @@
|
||||||
private System.Windows.Forms.TrackBar SoundVolBar;
|
private System.Windows.Forms.TrackBar SoundVolBar;
|
||||||
private System.Windows.Forms.CheckBox UseNewOutputBuffer;
|
private System.Windows.Forms.CheckBox UseNewOutputBuffer;
|
||||||
private System.Windows.Forms.ListBox listBoxSoundDevices;
|
private System.Windows.Forms.ListBox listBoxSoundDevices;
|
||||||
private System.Windows.Forms.Label label1;
|
private System.Windows.Forms.Label SoundDeviceLabel;
|
||||||
|
private System.Windows.Forms.Label BufferSizeLabel;
|
||||||
|
private System.Windows.Forms.NumericUpDown BufferSizeNumeric;
|
||||||
|
private System.Windows.Forms.Label BufferSizeUnitsLabel;
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -21,6 +21,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
SoundOnCheckBox.Checked = Global.Config.SoundEnabled;
|
SoundOnCheckBox.Checked = Global.Config.SoundEnabled;
|
||||||
MuteFrameAdvance.Checked = Global.Config.MuteFrameAdvance;
|
MuteFrameAdvance.Checked = Global.Config.MuteFrameAdvance;
|
||||||
UseNewOutputBuffer.Checked = Global.Config.UseNewOutputBuffer;
|
UseNewOutputBuffer.Checked = Global.Config.UseNewOutputBuffer;
|
||||||
|
BufferSizeNumeric.Value = Global.Config.SoundBufferSizeMs;
|
||||||
SoundVolBar.Value = Global.Config.SoundVolume;
|
SoundVolBar.Value = Global.Config.SoundVolume;
|
||||||
SoundVolNumeric.Value = Global.Config.SoundVolume;
|
SoundVolNumeric.Value = Global.Config.SoundVolume;
|
||||||
UpdateSoundDialog();
|
UpdateSoundDialog();
|
||||||
|
@ -45,10 +46,10 @@ namespace BizHawk.Client.EmuHawk
|
||||||
Global.Config.SoundEnabled = SoundOnCheckBox.Checked;
|
Global.Config.SoundEnabled = SoundOnCheckBox.Checked;
|
||||||
Global.Config.MuteFrameAdvance = MuteFrameAdvance.Checked;
|
Global.Config.MuteFrameAdvance = MuteFrameAdvance.Checked;
|
||||||
Global.Config.UseNewOutputBuffer = UseNewOutputBuffer.Checked;
|
Global.Config.UseNewOutputBuffer = UseNewOutputBuffer.Checked;
|
||||||
|
Global.Config.SoundBufferSizeMs = (int)BufferSizeNumeric.Value;
|
||||||
Global.Config.SoundVolume = SoundVolBar.Value;
|
Global.Config.SoundVolume = SoundVolBar.Value;
|
||||||
Global.Config.SoundDevice = (string)listBoxSoundDevices.SelectedItem ?? "<default>";
|
Global.Config.SoundDevice = (string)listBoxSoundDevices.SelectedItem ?? "<default>";
|
||||||
GlobalWin.Sound.ChangeVolume(Global.Config.SoundVolume);
|
GlobalWin.Sound.StopSound();
|
||||||
GlobalWin.Sound.UpdateSoundSettings();
|
|
||||||
GlobalWin.Sound.StartSound();
|
GlobalWin.Sound.StartSound();
|
||||||
GlobalWin.OSD.AddMessage("Sound settings saved");
|
GlobalWin.OSD.AddMessage("Sound settings saved");
|
||||||
Close();
|
Close();
|
||||||
|
|
Loading…
Reference in New Issue