New SoundOutputProvider class which takes the place of BufferedAsync. This should be far less prone to audible glitches and reduces latency.

This commit is contained in:
jdpurcell 2015-01-23 04:16:25 +00:00
parent 566107e12e
commit e3e5ae4934
7 changed files with 439 additions and 168 deletions

View File

@ -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 bool UseNewOutputBuffer = false;
// Log Window // Log Window
public bool LogWindowSaveWindowPosition = true; public bool LogWindowSaveWindowPosition = true;

View File

@ -616,6 +616,7 @@
<Compile Include="RomStatusPicker.Designer.cs"> <Compile Include="RomStatusPicker.Designer.cs">
<DependentUpon>RomStatusPicker.cs</DependentUpon> <DependentUpon>RomStatusPicker.cs</DependentUpon>
</Compile> </Compile>
<Compile Include="SoundOutputProvider.cs" />
<Compile Include="Throttle.cs" /> <Compile Include="Throttle.cs" />
<Compile Include="ToolAttributes.cs" /> <Compile Include="ToolAttributes.cs" />
<Compile Include="tools\BatchRun.cs"> <Compile Include="tools\BatchRun.cs">

View File

@ -1458,15 +1458,15 @@ namespace BizHawk.Client.EmuHawk
// note that the avi dumper has already rewired the emulator itself in this case. // note that the avi dumper has already rewired the emulator itself in this case.
GlobalWin.Sound.SetAsyncInputPin(_dumpProxy); GlobalWin.Sound.SetAsyncInputPin(_dumpProxy);
} }
else if (Global.Config.SoundThrottle) else if (Global.Config.SoundThrottle || Global.Config.UseNewOutputBuffer)
{ {
// for sound throttle, use sync mode // for sound throttle and new output buffer, use sync mode
Global.Emulator.EndAsyncSound(); Global.Emulator.EndAsyncSound();
GlobalWin.Sound.SetSyncInputPin(Global.Emulator.SyncSoundProvider); GlobalWin.Sound.SetSyncInputPin(Global.Emulator.SyncSoundProvider);
} }
else else
{ {
// for vsync\clock throttle modes, use async // for vsync\clock throttle modes through old output buffer, use async
GlobalWin.Sound.SetAsyncInputPin( GlobalWin.Sound.SetAsyncInputPin(
!Global.Emulator.StartAsyncSound() !Global.Emulator.StartAsyncSound()
? new MetaspuAsync(Global.Emulator.SyncSoundProvider, ESynchMethod.ESynchMethod_V) ? new MetaspuAsync(Global.Emulator.SyncSoundProvider, ESynchMethod.ESynchMethod_V)

View File

@ -47,11 +47,14 @@ namespace BizHawk.Client.EmuHawk
private const int BufferSizeSamples = SampleRate * BufferSizeMilliseconds / 1000; private const int BufferSizeSamples = SampleRate * BufferSizeMilliseconds / 1000;
private const int BufferSizeBytes = BufferSizeSamples * BlockAlign; private const int BufferSizeBytes = BufferSizeSamples * BlockAlign;
private const double BufferSizeSeconds = (double)(BufferSizeBytes / BlockAlign) / SampleRate; private const double BufferSizeSeconds = (double)(BufferSizeBytes / BlockAlign) / SampleRate;
private const int MinBufferFullnessMilliseconds = 60;
private const int MinBufferFullnessSamples = SampleRate * MinBufferFullnessMilliseconds / 1000;
private bool _muted; private bool _muted;
private bool _disposed; private bool _disposed;
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 ISoundProvider _asyncSoundProvider; private ISoundProvider _asyncSoundProvider;
private ISyncSoundProvider _syncSoundProvider; private ISyncSoundProvider _syncSoundProvider;
private int _actualWriteOffsetBytes = -1; private int _actualWriteOffsetBytes = -1;
@ -86,6 +89,9 @@ namespace BizHawk.Client.EmuHawk
_deviceBuffer = new SecondarySoundBuffer(device, desc); _deviceBuffer = new SecondarySoundBuffer(device, desc);
ChangeVolume(Global.Config.SoundVolume); ChangeVolume(Global.Config.SoundVolume);
//LogUnderruns = true;
//_outputProvider.LogDebug = true;
} }
public void StartSound() public void StartSound()
@ -98,6 +104,11 @@ namespace BizHawk.Client.EmuHawk
_deviceBuffer.Write(new byte[BufferSizeBytes], 0, LockFlags.EntireBuffer); _deviceBuffer.Write(new byte[BufferSizeBytes], 0, LockFlags.EntireBuffer);
_deviceBuffer.CurrentPlayPosition = 0; _deviceBuffer.CurrentPlayPosition = 0;
_deviceBuffer.Play(0, PlayFlags.Looping); _deviceBuffer.Play(0, PlayFlags.Looping);
_actualWriteOffsetBytes = -1;
_filledBufferSizeBytes = 0;
_lastWriteTime = 0;
_lastWriteCursor = 0;
} }
bool IsPlaying bool IsPlaying
@ -131,14 +142,26 @@ namespace BizHawk.Client.EmuHawk
public void SetSyncInputPin(ISyncSoundProvider source) public void SetSyncInputPin(ISyncSoundProvider source)
{ {
_syncSoundProvider = source; if (_asyncSoundProvider != null)
_asyncSoundProvider = null; {
_asyncSoundProvider.DiscardSamples();
_asyncSoundProvider = null;
}
_semiSync.DiscardSamples(); _semiSync.DiscardSamples();
_semiSync.BaseSoundProvider = null;
_syncSoundProvider = source;
_outputProvider.BaseSoundProvider = source;
} }
public void SetAsyncInputPin(ISoundProvider source) public void SetAsyncInputPin(ISoundProvider source)
{ {
_syncSoundProvider = null; if (_syncSoundProvider != null)
{
_syncSoundProvider.DiscardSamples();
_syncSoundProvider = 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);
@ -225,6 +248,7 @@ namespace BizHawk.Client.EmuHawk
{ {
if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples(); if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples();
if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples(); if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples();
if (_outputProvider != null) _outputProvider.DiscardSamples();
return; return;
} }
@ -239,15 +263,25 @@ namespace BizHawk.Client.EmuHawk
if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples(); if (_asyncSoundProvider != null) _asyncSoundProvider.DiscardSamples();
if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples(); if (_syncSoundProvider != null) _syncSoundProvider.DiscardSamples();
if (_outputProvider != null) _outputProvider.DiscardSamples();
} }
else if (_syncSoundProvider != null) else if (_syncSoundProvider != null)
{ {
_syncSoundProvider.GetSamples(out samples, out samplesProvided); if (Global.Config.SoundThrottle)
while (samplesNeeded < samplesProvided && !Global.DisableSecondaryThrottling)
{ {
Thread.Sleep((samplesProvided - samplesNeeded) / (SampleRate / 1000)); // let audio clock control sleep time _syncSoundProvider.GetSamples(out samples, out samplesProvided);
samplesNeeded = CalculateSamplesNeeded();
while (samplesNeeded < samplesProvided && !Global.DisableSecondaryThrottling)
{
Thread.Sleep((samplesProvided - samplesNeeded) / (SampleRate / 1000)); // let audio clock control sleep time
samplesNeeded = CalculateSamplesNeeded();
}
}
else
{
samples = new short[samplesNeeded * ChannelCount];
samplesProvided = _outputProvider.GetSamples(samples, samplesNeeded, samplesNeeded - (BufferSizeSamples - MinBufferFullnessSamples));
} }
} }
else if (_asyncSoundProvider != null) else if (_asyncSoundProvider != null)

View File

@ -0,0 +1,241 @@
using System;
using System.Collections.Generic;
using System.Linq;
using BizHawk.Emulation.Common;
using BizHawk.Client.Common;
namespace BizHawk.Client.EmuHawk
{
// This is intended to be a buffer between a synchronous sound provider and the
// output device (e.g. DirectSound). The idea is to take advantage of the samples
// buffered up in the output device so that we don't need to keep a bunch buffered
// up here. This will keep the latency at a minimum. The goal is to keep zero extra
// 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 (-40 ms) and depleting the output
// device's buffer, or too high (+40 ms), 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 = 40;
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 UsableHistoryLength = 20;
private const int MaxHistoryLength = 60;
private const int SoftCorrectionLength = 240;
private Queue<short> _buffer = new Queue<short>(MaxExtraSamples * ChannelCount);
private Queue<int> _extraCountHistory = new Queue<int>();
private Queue<int> _outputCountHistory = new Queue<int>();
private Queue<bool> _hardCorrectionHistory = new Queue<bool>();
private short[] _resampleBuffer = new short[0];
private double _resampleLengthRoundingError;
public ISyncSoundProvider BaseSoundProvider { get; set; }
public SoundOutputProvider()
{
}
public void DiscardSamples()
{
_buffer.Clear();
_extraCountHistory.Clear();
_outputCountHistory.Clear();
_hardCorrectionHistory.Clear();
_resampleBuffer = new short[0];
_resampleLengthRoundingError = 0.0;
if (BaseSoundProvider != null)
{
BaseSoundProvider.DiscardSamples();
}
}
public bool LogDebug { get; set; }
private double SamplesPerFrame
{
get { return SampleRate / Global.Emulator.CoreComm.VsyncRate; }
}
public int GetSamples(short[] samples, int idealSampleCount, int minSampleCount)
{
double scaleFactor = 1.0;
if (_extraCountHistory.Count >= UsableHistoryLength && !_hardCorrectionHistory.Any(c => c))
{
double offsetFromTarget = _extraCountHistory.Average();
if (Math.Abs(offsetFromTarget) > MaxTargetOffsetSamples)
{
double correctionSpan = _outputCountHistory.Average() * SoftCorrectionLength;
scaleFactor *= correctionSpan / (correctionSpan + offsetFromTarget);
}
}
GetSamplesFromBase(scaleFactor);
int bufferSampleCount = _buffer.Count / ChannelCount;
int extraSampleCount = bufferSampleCount - idealSampleCount;
bool hardCorrected = false;
if (bufferSampleCount < minSampleCount)
{
int generateSampleCount = (minSampleCount - bufferSampleCount) + HardCorrectionSamples;
if (LogDebug) Console.WriteLine("Generating " + generateSampleCount + " samples");
for (int i = 0; i < generateSampleCount * ChannelCount; i++)
{
_buffer.Enqueue(0);
}
hardCorrected = true;
}
else if (extraSampleCount > MaxExtraSamples)
{
int discardSampleCount = (extraSampleCount - MaxExtraSamples) + HardCorrectionSamples;
if (LogDebug) Console.WriteLine("Discarding " + discardSampleCount + " samples");
for (int i = 0; i < discardSampleCount * ChannelCount; i++)
{
_buffer.Dequeue();
}
hardCorrected = true;
}
bufferSampleCount = _buffer.Count / ChannelCount;
extraSampleCount = bufferSampleCount - idealSampleCount;
int outputSampleCount = Math.Min(idealSampleCount, bufferSampleCount);
UpdateHistory(_extraCountHistory, extraSampleCount);
UpdateHistory(_outputCountHistory, outputSampleCount);
UpdateHistory(_hardCorrectionHistory, hardCorrected);
GetSamplesFromBuffer(samples, outputSampleCount);
if (LogDebug)
{
Console.WriteLine("Avg: {0:0.0} ms, Min: {1:0.0} ms, Max: {2:0.0} ms, Scale: {3:0.0000}",
_extraCountHistory.Average() * 1000.0 / SampleRate,
_extraCountHistory.Min() * 1000.0 / SampleRate,
_extraCountHistory.Max() * 1000.0 / SampleRate,
scaleFactor);
}
return outputSampleCount;
}
private void GetSamplesFromBase(double scaleFactor)
{
short[] samples;
int count;
BaseSoundProvider.GetSamples(out samples, out count);
scaleFactor *= SamplesPerFrame / count;
double newCountTarget = count * scaleFactor;
int newCount = (int)Math.Round(newCountTarget + _resampleLengthRoundingError);
// Do not resample for one-sample differences. With NTSC @ 59.94 FPS, for example,
// there are ~735.7 samples per frame so the source will oscillate between 735 and
// 736 samples. Our calculated number of samples will also oscillate between 735
// and 736, but likely out of phase. There's no point resampling to make up for
// something that will average out over time, so don't resample for these
// differences. We will, however, keep track of them as part of the rounding error
// in case they end up not averaging out as expected.
if (Math.Abs(newCount - count) > 1)
{
samples = Resample(samples, count, newCount);
count = newCount;
}
// Although the rounding error may seem insignificant, it definitely matters over
// time so we need to keep track of it. With NTSC @ 59.94 FPS, for example, if we
// were to always round to 736 samples per frame ignoring the rounding error, we
// would drift by ~22 milliseconds per minute.
_resampleLengthRoundingError += newCountTarget - count;
AddSamplesToBuffer(samples, count);
}
private void UpdateHistory<T>(Queue<T> queue, T value)
{
queue.Enqueue(value);
while (queue.Count > MaxHistoryLength)
{
queue.Dequeue();
}
}
private void GetSamplesFromBuffer(short[] samples, int count)
{
for (int i = 0; i < count * ChannelCount; i++)
{
samples[i] = _buffer.Dequeue();
}
}
private void AddSamplesToBuffer(short[] samples, int count)
{
for (int i = 0; i < count * ChannelCount; i++)
{
_buffer.Enqueue(samples[i]);
}
}
private short[] GetResampleBuffer(int count)
{
if (_resampleBuffer.Length < count * ChannelCount)
{
_resampleBuffer = new short[count * ChannelCount];
}
return _resampleBuffer;
}
// This uses simple linear interpolation which is supposedly not a great idea for
// resampling audio, but it sounds surprisingly good to me. Maybe it works well
// because we are typically stretching by very small amounts.
private short[] Resample(short[] input, int inputCount, int outputCount)
{
if (inputCount == outputCount)
{
return input;
}
short[] output = GetResampleBuffer(outputCount);
if (inputCount == 0 || outputCount == 0)
{
Array.Clear(output, 0, outputCount * ChannelCount);
return output;
}
for (int iOutput = 0; iOutput < outputCount; iOutput++)
{
double iInput = ((double)iOutput / (outputCount - 1)) * (inputCount - 1);
int iInput0 = (int)iInput;
int iInput1 = iInput0 + 1;
double input0Weight = iInput1 - iInput;
double input1Weight = iInput - iInput0;
if (iInput1 == inputCount)
iInput1 = inputCount - 1;
for (int iChannel = 0; iChannel < ChannelCount; iChannel++)
{
output[iOutput * ChannelCount + iChannel] = (short)
(input[iInput0 * ChannelCount + iChannel] * input0Weight +
input[iInput1 * ChannelCount + iChannel] * input1Weight);
}
}
return output;
}
}
}

View File

@ -28,154 +28,154 @@
/// </summary> /// </summary>
private void InitializeComponent() private void InitializeComponent()
{ {
this.Cancel = new System.Windows.Forms.Button(); this.Cancel = new System.Windows.Forms.Button();
this.OK = new System.Windows.Forms.Button(); this.OK = new System.Windows.Forms.Button();
this.SoundOnCheckBox = new System.Windows.Forms.CheckBox(); this.SoundOnCheckBox = new System.Windows.Forms.CheckBox();
this.MuteFrameAdvance = new System.Windows.Forms.CheckBox(); this.MuteFrameAdvance = new System.Windows.Forms.CheckBox();
this.SoundVolGroup = new System.Windows.Forms.GroupBox(); this.SoundVolGroup = new System.Windows.Forms.GroupBox();
this.SoundVolBar = new System.Windows.Forms.TrackBar(); this.SoundVolBar = new System.Windows.Forms.TrackBar();
this.SoundVolNumeric = new System.Windows.Forms.NumericUpDown(); this.SoundVolNumeric = new System.Windows.Forms.NumericUpDown();
this.ThrottlecheckBox = 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.label1 = 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();
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, 209);
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;
this.Cancel.Text = "&Cancel"; this.Cancel.Text = "&Cancel";
this.Cancel.UseVisualStyleBackColor = true; this.Cancel.UseVisualStyleBackColor = true;
this.Cancel.Click += new System.EventHandler(this.Cancel_Click); this.Cancel.Click += new System.EventHandler(this.Cancel_Click);
// //
// OK // OK
// //
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, 209);
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;
this.OK.Text = "&Ok"; this.OK.Text = "&Ok";
this.OK.UseVisualStyleBackColor = true; this.OK.UseVisualStyleBackColor = true;
this.OK.Click += new System.EventHandler(this.OK_Click); this.OK.Click += new System.EventHandler(this.OK_Click);
// //
// SoundOnCheckBox // SoundOnCheckBox
// //
this.SoundOnCheckBox.AutoSize = true; this.SoundOnCheckBox.AutoSize = true;
this.SoundOnCheckBox.Location = new System.Drawing.Point(147, 12); this.SoundOnCheckBox.Location = new System.Drawing.Point(147, 12);
this.SoundOnCheckBox.Name = "SoundOnCheckBox"; this.SoundOnCheckBox.Name = "SoundOnCheckBox";
this.SoundOnCheckBox.Size = new System.Drawing.Size(74, 17); this.SoundOnCheckBox.Size = new System.Drawing.Size(74, 17);
this.SoundOnCheckBox.TabIndex = 2; this.SoundOnCheckBox.TabIndex = 2;
this.SoundOnCheckBox.Text = "Sound On"; this.SoundOnCheckBox.Text = "Sound On";
this.SoundOnCheckBox.UseVisualStyleBackColor = true; this.SoundOnCheckBox.UseVisualStyleBackColor = true;
this.SoundOnCheckBox.CheckedChanged += new System.EventHandler(this.SoundOnCheckBox_CheckedChanged); this.SoundOnCheckBox.CheckedChanged += new System.EventHandler(this.SoundOnCheckBox_CheckedChanged);
// //
// MuteFrameAdvance // MuteFrameAdvance
// //
this.MuteFrameAdvance.AutoSize = true; this.MuteFrameAdvance.AutoSize = true;
this.MuteFrameAdvance.Location = new System.Drawing.Point(147, 35); this.MuteFrameAdvance.Location = new System.Drawing.Point(147, 35);
this.MuteFrameAdvance.Name = "MuteFrameAdvance"; this.MuteFrameAdvance.Name = "MuteFrameAdvance";
this.MuteFrameAdvance.Size = new System.Drawing.Size(128, 17); this.MuteFrameAdvance.Size = new System.Drawing.Size(128, 17);
this.MuteFrameAdvance.TabIndex = 3; this.MuteFrameAdvance.TabIndex = 3;
this.MuteFrameAdvance.Text = "Mute Frame Advance"; this.MuteFrameAdvance.Text = "Mute Frame Advance";
this.MuteFrameAdvance.UseVisualStyleBackColor = true; this.MuteFrameAdvance.UseVisualStyleBackColor = true;
// //
// SoundVolGroup // SoundVolGroup
// //
this.SoundVolGroup.Controls.Add(this.SoundVolBar); this.SoundVolGroup.Controls.Add(this.SoundVolBar);
this.SoundVolGroup.Controls.Add(this.SoundVolNumeric); this.SoundVolGroup.Controls.Add(this.SoundVolNumeric);
this.SoundVolGroup.Location = new System.Drawing.Point(12, 12); this.SoundVolGroup.Location = new System.Drawing.Point(12, 12);
this.SoundVolGroup.Name = "SoundVolGroup"; this.SoundVolGroup.Name = "SoundVolGroup";
this.SoundVolGroup.Size = new System.Drawing.Size(90, 219); this.SoundVolGroup.Size = new System.Drawing.Size(90, 219);
this.SoundVolGroup.TabIndex = 4; this.SoundVolGroup.TabIndex = 4;
this.SoundVolGroup.TabStop = false; this.SoundVolGroup.TabStop = false;
this.SoundVolGroup.Text = "Volume"; this.SoundVolGroup.Text = "Volume";
// //
// SoundVolBar // SoundVolBar
// //
this.SoundVolBar.LargeChange = 10; this.SoundVolBar.LargeChange = 10;
this.SoundVolBar.Location = new System.Drawing.Point(23, 23); this.SoundVolBar.Location = new System.Drawing.Point(23, 23);
this.SoundVolBar.Maximum = 100; this.SoundVolBar.Maximum = 100;
this.SoundVolBar.Name = "SoundVolBar"; this.SoundVolBar.Name = "SoundVolBar";
this.SoundVolBar.Orientation = System.Windows.Forms.Orientation.Vertical; this.SoundVolBar.Orientation = System.Windows.Forms.Orientation.Vertical;
this.SoundVolBar.Size = new System.Drawing.Size(45, 164); this.SoundVolBar.Size = new System.Drawing.Size(45, 164);
this.SoundVolBar.TabIndex = 1; this.SoundVolBar.TabIndex = 1;
this.SoundVolBar.TickFrequency = 10; this.SoundVolBar.TickFrequency = 10;
this.SoundVolBar.Scroll += new System.EventHandler(this.trackBar1_Scroll); this.SoundVolBar.Scroll += new System.EventHandler(this.trackBar1_Scroll);
// //
// SoundVolNumeric // SoundVolNumeric
// //
this.SoundVolNumeric.Location = new System.Drawing.Point(16, 190); this.SoundVolNumeric.Location = new System.Drawing.Point(16, 190);
this.SoundVolNumeric.Name = "SoundVolNumeric"; this.SoundVolNumeric.Name = "SoundVolNumeric";
this.SoundVolNumeric.Size = new System.Drawing.Size(59, 20); this.SoundVolNumeric.Size = new System.Drawing.Size(59, 20);
this.SoundVolNumeric.TabIndex = 0; this.SoundVolNumeric.TabIndex = 0;
this.SoundVolNumeric.ValueChanged += new System.EventHandler(this.SoundVolNumeric_ValueChanged); this.SoundVolNumeric.ValueChanged += new System.EventHandler(this.SoundVolNumeric_ValueChanged);
// //
// ThrottlecheckBox // UseNewOutputBuffer
// //
this.ThrottlecheckBox.AutoSize = true; this.UseNewOutputBuffer.AutoSize = true;
this.ThrottlecheckBox.Location = new System.Drawing.Point(147, 58); this.UseNewOutputBuffer.Location = new System.Drawing.Point(147, 58);
this.ThrottlecheckBox.Name = "ThrottlecheckBox"; this.UseNewOutputBuffer.Name = "UseNewOutputBuffer";
this.ThrottlecheckBox.Size = new System.Drawing.Size(96, 17); this.UseNewOutputBuffer.Size = new System.Drawing.Size(205, 17);
this.ThrottlecheckBox.TabIndex = 5; this.UseNewOutputBuffer.TabIndex = 5;
this.ThrottlecheckBox.Text = "Sound Throttle"; this.UseNewOutputBuffer.Text = "Use New Output Buffer (Experimental)";
this.ThrottlecheckBox.UseVisualStyleBackColor = true; this.UseNewOutputBuffer.UseVisualStyleBackColor = true;
// //
// listBoxSoundDevices // listBoxSoundDevices
// //
this.listBoxSoundDevices.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) this.listBoxSoundDevices.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
| 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, 108);
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 // label1
// //
this.label1.AutoSize = true; this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(108, 92); this.label1.Location = new System.Drawing.Point(108, 92);
this.label1.Name = "label1"; this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(78, 13); this.label1.Size = new System.Drawing.Size(78, 13);
this.label1.TabIndex = 7; this.label1.TabIndex = 7;
this.label1.Text = "Sound Device:"; this.label1.Text = "Sound Device:";
// //
// SoundConfig // SoundConfig
// //
this.AcceptButton = this.OK; this.AcceptButton = this.OK;
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, 244);
this.Controls.Add(this.label1); this.Controls.Add(this.label1);
this.Controls.Add(this.listBoxSoundDevices); this.Controls.Add(this.listBoxSoundDevices);
this.Controls.Add(this.ThrottlecheckBox); this.Controls.Add(this.UseNewOutputBuffer);
this.Controls.Add(this.SoundVolGroup); this.Controls.Add(this.SoundVolGroup);
this.Controls.Add(this.MuteFrameAdvance); this.Controls.Add(this.MuteFrameAdvance);
this.Controls.Add(this.SoundOnCheckBox); this.Controls.Add(this.SoundOnCheckBox);
this.Controls.Add(this.OK); this.Controls.Add(this.OK);
this.Controls.Add(this.Cancel); this.Controls.Add(this.Cancel);
this.MinimumSize = new System.Drawing.Size(279, 259); this.MinimumSize = new System.Drawing.Size(279, 259);
this.Name = "SoundConfig"; this.Name = "SoundConfig";
this.ShowIcon = false; this.ShowIcon = false;
this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent;
this.Text = "Sound Configuration"; this.Text = "Sound Configuration";
this.Load += new System.EventHandler(this.SoundConfig_Load); this.Load += new System.EventHandler(this.SoundConfig_Load);
this.SoundVolGroup.ResumeLayout(false); this.SoundVolGroup.ResumeLayout(false);
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();
this.ResumeLayout(false); this.ResumeLayout(false);
this.PerformLayout(); this.PerformLayout();
} }
@ -188,7 +188,7 @@
private System.Windows.Forms.GroupBox SoundVolGroup; private System.Windows.Forms.GroupBox SoundVolGroup;
private System.Windows.Forms.NumericUpDown SoundVolNumeric; private System.Windows.Forms.NumericUpDown SoundVolNumeric;
private System.Windows.Forms.TrackBar SoundVolBar; private System.Windows.Forms.TrackBar SoundVolBar;
private System.Windows.Forms.CheckBox ThrottlecheckBox; 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 label1;
} }

View File

@ -20,14 +20,11 @@ namespace BizHawk.Client.EmuHawk
SoundOnCheckBox.Checked = Global.Config.SoundEnabled; SoundOnCheckBox.Checked = Global.Config.SoundEnabled;
MuteFrameAdvance.Checked = Global.Config.MuteFrameAdvance; MuteFrameAdvance.Checked = Global.Config.MuteFrameAdvance;
ThrottlecheckBox.Checked = Global.Config.SoundThrottle; UseNewOutputBuffer.Checked = Global.Config.UseNewOutputBuffer;
SoundVolBar.Value = Global.Config.SoundVolume; SoundVolBar.Value = Global.Config.SoundVolume;
SoundVolNumeric.Value = Global.Config.SoundVolume; SoundVolNumeric.Value = Global.Config.SoundVolume;
UpdateSoundDialog(); UpdateSoundDialog();
// vestigal
ThrottlecheckBox.Visible = false;
var dd = SoundEnumeration.DeviceNames(); var dd = SoundEnumeration.DeviceNames();
listBoxSoundDevices.Items.Add("<default>"); listBoxSoundDevices.Items.Add("<default>");
listBoxSoundDevices.SelectedIndex = 0; listBoxSoundDevices.SelectedIndex = 0;
@ -47,8 +44,8 @@ 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.SoundVolume = SoundVolBar.Value; Global.Config.SoundVolume = SoundVolBar.Value;
Global.Config.SoundThrottle = ThrottlecheckBox.Checked;
Global.Config.SoundDevice = (string)listBoxSoundDevices.SelectedItem ?? "<default>"; Global.Config.SoundDevice = (string)listBoxSoundDevices.SelectedItem ?? "<default>";
GlobalWin.Sound.ChangeVolume(Global.Config.SoundVolume); GlobalWin.Sound.ChangeVolume(Global.Config.SoundVolume);
GlobalWin.Sound.UpdateSoundSettings(); GlobalWin.Sound.UpdateSoundSettings();
@ -81,13 +78,10 @@ namespace BizHawk.Client.EmuHawk
{ {
UpdateSoundDialog(); UpdateSoundDialog();
} }
private void UpdateSoundDialog() private void UpdateSoundDialog()
{ {
//Ocean Prince commented this out MuteFrameAdvance.Enabled = SoundOnCheckBox.Checked;
//SoundVolGroup.Enabled =
MuteFrameAdvance.Enabled =
ThrottlecheckBox.Enabled =
SoundOnCheckBox.Checked;
} }
} }
} }