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:
parent
566107e12e
commit
e3e5ae4934
|
@ -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;
|
||||||
|
|
|
@ -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">
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue