Configurable DirectSound buffer size.

A bunch of changes in Sound / SoundOutputProvider to support this and behave nicely.
This commit is contained in:
jdpurcell 2015-01-25 03:33:45 +00:00
parent a04982bc84
commit 69c66d12b1
6 changed files with 174 additions and 121 deletions

View File

@ -228,6 +228,7 @@ namespace BizHawk.Client.Common
public int SoundVolume = 100; // Range 0-100
public bool SoundThrottle = false;
public string SoundDevice = "";
public int SoundBufferSizeMs = 100;
public bool UseNewOutputBuffer = false;
// Log Window

View File

@ -2093,6 +2093,13 @@ namespace BizHawk.Client.EmuHawk
Global.Config.DisplayInput ^= true;
}
public static void ToggleSound()
{
Global.Config.SoundEnabled ^= true;
GlobalWin.Sound.StopSound();
GlobalWin.Sound.StartSound();
}
private static void VolumeUp()
{
Global.Config.SoundVolume += 10;
@ -2101,17 +2108,10 @@ namespace BizHawk.Client.EmuHawk
Global.Config.SoundVolume = 100;
}
GlobalWin.Sound.ChangeVolume(Global.Config.SoundVolume);
GlobalWin.Sound.ApplyVolumeSettings();
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()
{
Global.Config.SoundVolume -= 10;
@ -2120,7 +2120,7 @@ namespace BizHawk.Client.EmuHawk
Global.Config.SoundVolume = 0;
}
GlobalWin.Sound.ChangeVolume(Global.Config.SoundVolume);
GlobalWin.Sound.ApplyVolumeSettings();
GlobalWin.OSD.AddMessage("Volume " + Global.Config.SoundVolume);
}

View File

@ -43,18 +43,14 @@ namespace BizHawk.Client.EmuHawk
private const int BytesPerSample = 2;
private const int ChannelCount = 2;
private const int BlockAlign = BytesPerSample * ChannelCount;
private const int BufferSizeMilliseconds = 100;
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 const int MinBufferFullnessSamples = 55 * SampleRate / 1000;
private bool _muted;
private bool _disposed;
private DirectSound _device;
private SecondarySoundBuffer _deviceBuffer;
private readonly BufferedAsync _semiSync = new BufferedAsync();
private readonly SoundOutputProvider _outputProvider = new SoundOutputProvider();
private SoundOutputProvider _outputProvider;
private ISoundProvider _asyncSoundProvider;
private ISyncSoundProvider _syncSoundProvider;
private int _actualWriteOffsetBytes = -1;
@ -67,6 +63,24 @@ namespace BizHawk.Client.EmuHawk
if (device == null) return;
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
{
@ -81,25 +95,31 @@ namespace BizHawk.Client.EmuHawk
var desc = new SoundBufferDescription
{
Format = format,
Flags =
BufferFlags.GlobalFocus | BufferFlags.Software | BufferFlags.GetCurrentPosition2 | BufferFlags.ControlVolume,
Flags = BufferFlags.GlobalFocus | BufferFlags.Software | BufferFlags.GetCurrentPosition2 | BufferFlags.ControlVolume,
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;
//_outputProvider.LogDebug = true;
if (Global.Config.SoundVolume == 0)
_deviceBuffer.Volume = -5000;
else
_deviceBuffer.Volume = 0 - ((100 - Global.Config.SoundVolume) * 45);
}
public void StartSound()
{
if (_disposed) throw new ObjectDisposedException("Sound");
if (Global.Config.SoundEnabled == false) return;
if (_deviceBuffer == null) return;
if (IsPlaying) return;
if (!Global.Config.SoundEnabled) return;
if (_deviceBuffer != null) return;
CreateDeviceBuffer();
ApplyVolumeSettings();
_deviceBuffer.Write(new byte[BufferSizeBytes], 0, LockFlags.EntireBuffer);
_deviceBuffer.CurrentPlayPosition = 0;
@ -109,34 +129,33 @@ namespace BizHawk.Client.EmuHawk
_filledBufferSizeBytes = 0;
_lastWriteTime = 0;
_lastWriteCursor = 0;
}
bool IsPlaying
{
get
{
if (_deviceBuffer == null) return false;
if ((_deviceBuffer.Status & BufferStatus.Playing) != 0) return true;
return false;
}
_outputProvider = new SoundOutputProvider();
_outputProvider.HardCorrectionThresholdSamples = BufferSizeSamples - MinBufferFullnessSamples;
_outputProvider.BaseSoundProvider = _syncSoundProvider;
//LogUnderruns = true;
//_outputProvider.LogDebug = true;
}
public void StopSound()
{
if (!IsPlaying) return;
if (_deviceBuffer == null) return;
_deviceBuffer.Write(new byte[BufferSizeBytes], 0, LockFlags.EntireBuffer);
_deviceBuffer.Stop();
_deviceBuffer.Dispose();
_deviceBuffer = null;
_outputProvider = null;
BufferSizeMilliseconds = 0;
}
public void Dispose()
{
if (_disposed) return;
if (_deviceBuffer != null && _deviceBuffer.Disposed == false)
{
_deviceBuffer.Dispose();
_deviceBuffer = null;
}
StopSound();
_disposed = true;
}
@ -150,7 +169,10 @@ namespace BizHawk.Client.EmuHawk
_semiSync.DiscardSamples();
_semiSync.BaseSoundProvider = null;
_syncSoundProvider = source;
_outputProvider.BaseSoundProvider = source;
if (_outputProvider != null)
{
_outputProvider.BaseSoundProvider = source;
}
}
public void SetAsyncInputPin(ISoundProvider source)
@ -160,21 +182,29 @@ namespace BizHawk.Client.EmuHawk
_syncSoundProvider.DiscardSamples();
_syncSoundProvider = null;
}
_outputProvider.DiscardSamples();
_outputProvider.BaseSoundProvider = null;
if (_outputProvider != null)
{
_outputProvider.DiscardSamples();
_outputProvider.BaseSoundProvider = null;
}
_asyncSoundProvider = source;
_semiSync.BaseSoundProvider = source;
_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; }
@ -188,8 +218,9 @@ namespace BizHawk.Client.EmuHawk
if (_actualWriteOffsetBytes != -1)
{
double elapsedSeconds = (currentWriteTime - _lastWriteTime) / (double)Stopwatch.Frequency;
double bufferSizeSeconds = (double)BufferSizeSamples / SampleRate;
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;
if (_filledBufferSizeBytes < 0)
{
@ -206,9 +237,8 @@ namespace BizHawk.Client.EmuHawk
int samplesNeeded = CircularDistance(_actualWriteOffsetBytes, playCursor, BufferSizeBytes) / BlockAlign;
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 silenceSamples = Math.Max(samplesNeeded - samplesPerFrame, 0);
int silenceSamples = Math.Max(samplesNeeded - (SilenceLeaveRoomForFrameCount * samplesPerFrame), 0);
WriteSamples(new short[silenceSamples * 2], silenceSamples);
samplesNeeded -= silenceSamples;
}
@ -244,7 +274,7 @@ namespace BizHawk.Client.EmuHawk
public void UpdateSound()
{
if (Global.Config.SoundEnabled == false || _disposed)
if (!Global.Config.SoundEnabled || _deviceBuffer == null || _disposed)
{
if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples();
if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples();
@ -281,7 +311,7 @@ namespace BizHawk.Client.EmuHawk
{
samples = new short[samplesNeeded * ChannelCount];
samplesProvided = _outputProvider.GetSamples(samples, samplesNeeded, samplesNeeded - (BufferSizeSamples - MinBufferFullnessSamples));
samplesProvided = _outputProvider.GetSamples(samples, samplesNeeded);
}
}
else if (_asyncSoundProvider != null)
@ -297,31 +327,6 @@ namespace BizHawk.Client.EmuHawk
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
// Dummy implementation for non-Windows platforms for now.

View File

@ -14,19 +14,14 @@ namespace BizHawk.Client.EmuHawk
// 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
// 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
// device's buffer, or too high (+45 ms), we will perform a "hard" correction by
// generating silence or discarding samples.
// window shortly. If it ends up going too low or too high, we will perform a
// "hard" correction by generating silence or discarding samples.
public class SoundOutputProvider
{
private const int SampleRate = 44100;
private const int ChannelCount = 2;
private const int MaxExtraMilliseconds = 45;
private const int MaxExtraSamples = SampleRate * MaxExtraMilliseconds / 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 SoftCorrectionThresholdSamples = 5 * SampleRate / 1000;
private const int StartupHardCorrectionThresholdSamples = 10 * SampleRate / 1000;
private const int UsableHistoryLength = 20;
private const int MaxHistoryLength = 60;
private const int SoftCorrectionLength = 240;
@ -34,7 +29,7 @@ namespace BizHawk.Client.EmuHawk
private const int BaseSampleRateMaxHistoryLength = 300;
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> _outputCountHistory = new Queue<int>();
@ -46,12 +41,14 @@ namespace BizHawk.Client.EmuHawk
private short[] _resampleBuffer = new short[0];
private double _resampleLengthRoundingError;
public ISyncSoundProvider BaseSoundProvider { get; set; }
public SoundOutputProvider()
{
}
public int HardCorrectionThresholdSamples { get; set; }
public ISyncSoundProvider BaseSoundProvider { get; set; }
public void DiscardSamples()
{
_buffer.Clear();
@ -76,14 +73,14 @@ namespace BizHawk.Client.EmuHawk
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;
if (_extraCountHistory.Count >= UsableHistoryLength && !_hardCorrectionHistory.Any(c => c))
{
double offsetFromTarget = _extraCountHistory.Average();
if (Math.Abs(offsetFromTarget) > MaxTargetOffsetSamples)
if (Math.Abs(offsetFromTarget) > SoftCorrectionThresholdSamples)
{
double correctionSpan = _outputCountHistory.Average() * SoftCorrectionLength;
scaleFactor *= correctionSpan / (correctionSpan + offsetFromTarget);
@ -94,11 +91,13 @@ namespace BizHawk.Client.EmuHawk
int bufferSampleCount = _buffer.Count / ChannelCount;
int extraSampleCount = bufferSampleCount - idealSampleCount;
int hardCorrectionThresholdSamples = _extraCountHistory.Count >= UsableHistoryLength ? HardCorrectionThresholdSamples :
Math.Min(StartupHardCorrectionThresholdSamples, HardCorrectionThresholdSamples);
bool hardCorrected = false;
if (bufferSampleCount < minSampleCount)
if (extraSampleCount < -hardCorrectionThresholdSamples)
{
int generateSampleCount = (minSampleCount - bufferSampleCount) + HardCorrectionSamples;
int generateSampleCount = -extraSampleCount;
if (LogDebug) Console.WriteLine("Generating " + generateSampleCount + " samples");
for (int i = 0; i < generateSampleCount * ChannelCount; i++)
{
@ -106,9 +105,9 @@ namespace BizHawk.Client.EmuHawk
}
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");
for (int i = 0; i < discardSampleCount * ChannelCount; i++)
{
@ -236,7 +235,6 @@ namespace BizHawk.Client.EmuHawk
return output;
}
double roundingError = 0.0;
for (int iOutput = 0; iOutput < outputCount; iOutput++)
{
double iInput = ((double)iOutput / (outputCount - 1)) * (inputCount - 1);
@ -250,17 +248,11 @@ namespace BizHawk.Client.EmuHawk
for (int iChannel = 0; iChannel < ChannelCount; iChannel++)
{
double valueExact =
double value =
input[iInput0 * ChannelCount + iChannel] * input0Weight +
input[iInput1 * ChannelCount + iChannel] * input1Weight +
roundingError;
input[iInput1 * ChannelCount + iChannel] * input1Weight;
if (valueExact < -32768.0) valueExact = -32768.0;
if (valueExact > 32767.0) valueExact = 32767.0;
short value = (short)((int)(valueExact + 32768.5) - 32768);
output[iOutput * ChannelCount + iChannel] = value;
roundingError = valueExact - value;
output[iOutput * ChannelCount + iChannel] = (short)((int)(value + 32768.5) - 32768);
}
}

View File

@ -37,17 +37,21 @@
this.SoundVolNumeric = new System.Windows.Forms.NumericUpDown();
this.UseNewOutputBuffer = new System.Windows.Forms.CheckBox();
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();
((System.ComponentModel.ISupportInitialize)(this.SoundVolBar)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.SoundVolNumeric)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.BufferSizeNumeric)).BeginInit();
this.SuspendLayout();
//
// Cancel
//
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.Location = new System.Drawing.Point(317, 209);
this.Cancel.Location = new System.Drawing.Point(317, 244);
this.Cancel.Name = "Cancel";
this.Cancel.Size = new System.Drawing.Size(75, 23);
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.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.Size = new System.Drawing.Size(75, 23);
this.OK.TabIndex = 1;
@ -135,19 +139,62 @@
| System.Windows.Forms.AnchorStyles.Left)
| System.Windows.Forms.AnchorStyles.Right)));
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.Size = new System.Drawing.Size(284, 95);
this.listBoxSoundDevices.TabIndex = 6;
//
// label1
// SoundDeviceLabel
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(108, 92);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(78, 13);
this.label1.TabIndex = 7;
this.label1.Text = "Sound Device:";
this.SoundDeviceLabel.AutoSize = true;
this.SoundDeviceLabel.Location = new System.Drawing.Point(108, 86);
this.SoundDeviceLabel.Name = "SoundDeviceLabel";
this.SoundDeviceLabel.Size = new System.Drawing.Size(78, 13);
this.SoundDeviceLabel.TabIndex = 7;
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
//
@ -155,8 +202,11 @@
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.Cancel;
this.ClientSize = new System.Drawing.Size(404, 244);
this.Controls.Add(this.label1);
this.ClientSize = new System.Drawing.Size(404, 279);
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.UseNewOutputBuffer);
this.Controls.Add(this.SoundVolGroup);
@ -174,6 +224,7 @@
this.SoundVolGroup.PerformLayout();
((System.ComponentModel.ISupportInitialize)(this.SoundVolBar)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.SoundVolNumeric)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.BufferSizeNumeric)).EndInit();
this.ResumeLayout(false);
this.PerformLayout();
@ -190,6 +241,9 @@
private System.Windows.Forms.TrackBar SoundVolBar;
private System.Windows.Forms.CheckBox UseNewOutputBuffer;
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;
}
}

View File

@ -21,6 +21,7 @@ namespace BizHawk.Client.EmuHawk
SoundOnCheckBox.Checked = Global.Config.SoundEnabled;
MuteFrameAdvance.Checked = Global.Config.MuteFrameAdvance;
UseNewOutputBuffer.Checked = Global.Config.UseNewOutputBuffer;
BufferSizeNumeric.Value = Global.Config.SoundBufferSizeMs;
SoundVolBar.Value = Global.Config.SoundVolume;
SoundVolNumeric.Value = Global.Config.SoundVolume;
UpdateSoundDialog();
@ -45,10 +46,10 @@ namespace BizHawk.Client.EmuHawk
Global.Config.SoundEnabled = SoundOnCheckBox.Checked;
Global.Config.MuteFrameAdvance = MuteFrameAdvance.Checked;
Global.Config.UseNewOutputBuffer = UseNewOutputBuffer.Checked;
Global.Config.SoundBufferSizeMs = (int)BufferSizeNumeric.Value;
Global.Config.SoundVolume = SoundVolBar.Value;
Global.Config.SoundDevice = (string)listBoxSoundDevices.SelectedItem ?? "<default>";
GlobalWin.Sound.ChangeVolume(Global.Config.SoundVolume);
GlobalWin.Sound.UpdateSoundSettings();
GlobalWin.Sound.StopSound();
GlobalWin.Sound.StartSound();
GlobalWin.OSD.AddMessage("Sound settings saved");
Close();