Add UI for choosing between DirectSound / XAudio2.

Always use new sound output buffering (remove option).
This commit is contained in:
jdpurcell 2015-01-31 06:40:14 +00:00
parent 83e8abc963
commit 5eee1cd306
5 changed files with 186 additions and 59 deletions

View File

@ -111,6 +111,8 @@ namespace BizHawk.Client.Common
public enum EDispMethod { OpenGL, GdiPlus, SlimDX9 };
public enum ESoundOutputMethod { DirectSound, XAudio2, Dummy };
public enum EDispManagerAR { None, System, Custom };
public enum SaveStateTypeE { Default, Binary, Text };
@ -224,13 +226,13 @@ namespace BizHawk.Client.Common
public int DispCustomUserARHeight = 1;
// Sound options
public ESoundOutputMethod SoundOutputMethod = ESoundOutputMethod.DirectSound;
public bool SoundEnabled = true;
public bool MuteFrameAdvance = true;
public int SoundVolume = 100; // Range 0-100
public bool SoundThrottle = false;
public string SoundDevice = "";
public int SoundBufferSizeMs = 100;
public bool UseNewOutputBuffer = false;
// Log Window
public bool LogWindowSaveWindowPosition = true;

View File

@ -281,7 +281,13 @@ namespace BizHawk.Client.EmuHawk
try { GlobalWin.Sound = new Sound(Handle); }
catch
{
MessageBox.Show("Couldn't initialize DirectSound! Things may go poorly for you. Try changing your sound driver to 41khz instead of 48khz in mmsys.cpl.", "Initialization Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
string message = "Couldn't initialize sound device! Try changing the output method in Sound config.";
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound)
message = "Couldn't initialize DirectSound! Things may go poorly for you. Try changing your sound driver to 44.1khz instead of 48khz in mmsys.cpl.";
MessageBox.Show(message, "Initialization Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
Global.Config.SoundOutputMethod = Config.ESoundOutputMethod.Dummy;
GlobalWin.Sound = new Sound(Handle);
}
#else
Global.Sound = new Sound();
@ -1506,19 +1512,10 @@ namespace BizHawk.Client.EmuHawk
// note that the avi dumper has already rewired the emulator itself in this case.
GlobalWin.Sound.SetAsyncInputPin(_dumpProxy);
}
else if (Global.Config.SoundThrottle || Global.Config.UseNewOutputBuffer)
{
// for sound throttle and new output buffer, use sync mode
Global.Emulator.EndAsyncSound();
GlobalWin.Sound.SetSyncInputPin(Global.Emulator.SyncSoundProvider);
}
else
{
// for vsync\clock throttle modes through old output buffer, use async
GlobalWin.Sound.SetAsyncInputPin(
!Global.Emulator.StartAsyncSound()
? new MetaspuAsync(Global.Emulator.SyncSoundProvider, ESynchMethod.ESynchMethod_V)
: Global.Emulator.SoundProvider);
Global.Emulator.EndAsyncSound();
GlobalWin.Sound.SetSyncInputPin(Global.Emulator.SyncSoundProvider);
}
}

View File

@ -43,8 +43,12 @@ namespace BizHawk.Client.EmuHawk
public Sound(IntPtr mainWindowHandle)
{
_soundOutput = new DirectSoundSoundOutput(this, mainWindowHandle);
//_soundOutput = new XAudio2SoundOutput(this);
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound)
_soundOutput = new DirectSoundSoundOutput(this, mainWindowHandle);
else if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.XAudio2)
_soundOutput = new XAudio2SoundOutput(this);
else
_soundOutput = new DummySoundOutput(this);
}
public void Dispose()
@ -267,7 +271,7 @@ namespace BizHawk.Client.EmuHawk
public static IEnumerable<string> GetDeviceNames()
{
return DirectSound.GetDevices().Select(d => d.Description);
return DirectSound.GetDevices().Select(d => d.Description).ToList();
}
private int BufferSizeSamples { get; set; }
@ -429,7 +433,7 @@ namespace BizHawk.Client.EmuHawk
{
using (XAudio2 device = new XAudio2())
{
return Enumerable.Range(0, device.DeviceCount).Select(n => device.GetDeviceDetails(n).DisplayName);
return Enumerable.Range(0, device.DeviceCount).Select(n => device.GetDeviceDetails(n).DisplayName).ToList();
}
}
@ -475,13 +479,13 @@ namespace BizHawk.Client.EmuHawk
public int CalculateSamplesNeeded()
{
long samplesAwaitingPlayback = _runningSamplesQueued - _sourceVoice.State.SamplesPlayed;
bool isInitializing = _runningSamplesQueued == 0;
bool detectedUnderrun = !isInitializing && _sourceVoice.State.BuffersQueued == 0;
if (detectedUnderrun)
{
_sound.OnUnderrun();
}
long samplesAwaitingPlayback = _runningSamplesQueued - _sourceVoice.State.SamplesPlayed;
int samplesNeeded = (int)Math.Max(BufferSizeSamples - samplesAwaitingPlayback, 0);
if ((isInitializing && _sound.InitializeBufferWithSilence) || (detectedUnderrun && _sound.RecoverFromUnderrunsWithSilence))
{
@ -499,13 +503,78 @@ namespace BizHawk.Client.EmuHawk
// TODO: Re-use these buffers
byte[] bytes = new byte[sampleCount * Sound.BlockAlign];
Buffer.BlockCopy(samples, 0, bytes, 0, bytes.Length);
_sourceVoice.SubmitSourceBuffer(new AudioBuffer {
_sourceVoice.SubmitSourceBuffer(new AudioBuffer
{
AudioBytes = bytes.Length,
AudioData = new DataStream(bytes, true, false)
});
_runningSamplesQueued += sampleCount;
}
}
public class DummySoundOutput : ISoundOutput
{
private Sound _sound;
private int _remainingSamples;
private long _lastWriteTime;
public DummySoundOutput(Sound sound)
{
_sound = sound;
}
public void Dispose()
{
}
private int BufferSizeSamples { get; set; }
public int MaxSamplesDeficit { get; private set; }
public void ApplyVolumeSettings(double volume)
{
}
public void StartSound()
{
BufferSizeSamples = Sound.MillisecondsToSamples(Global.Config.SoundBufferSizeMs);
MaxSamplesDeficit = BufferSizeSamples;
_lastWriteTime = 0;
}
public void StopSound()
{
BufferSizeSamples = 0;
}
public int CalculateSamplesNeeded()
{
Start:
long currentWriteTime = Stopwatch.GetTimestamp();
if (_lastWriteTime != 0)
{
double elapsedSeconds = (currentWriteTime - _lastWriteTime) / (double)Stopwatch.Frequency;
if (elapsedSeconds < 0.001)
{
// Due to rounding errors this doesn't work too well in audio throttle mode unless we let more time pass
Thread.Sleep(1);
goto Start;
}
_remainingSamples -= (int)Math.Round(elapsedSeconds * Sound.SampleRate);
if (_remainingSamples < 0) _remainingSamples = 0;
}
int samplesNeeded = BufferSizeSamples - _remainingSamples;
_lastWriteTime = currentWriteTime;
return samplesNeeded;
}
public void WriteSamples(short[] samples, int sampleCount)
{
if (sampleCount == 0) return;
_remainingSamples += sampleCount;
}
}
#else
// Dummy implementation for non-Windows platforms for now.
public class Sound

View File

@ -35,16 +35,19 @@
this.SoundVolGroup = new System.Windows.Forms.GroupBox();
this.SoundVolBar = new System.Windows.Forms.TrackBar();
this.SoundVolNumeric = new System.Windows.Forms.NumericUpDown();
this.UseNewOutputBuffer = new System.Windows.Forms.CheckBox();
this.listBoxSoundDevices = new System.Windows.Forms.ListBox();
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.grpOutputMethod = new System.Windows.Forms.GroupBox();
this.rbOutputMethodXAudio2 = new System.Windows.Forms.RadioButton();
this.rbOutputMethodDirectSound = new System.Windows.Forms.RadioButton();
this.SoundVolGroup.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.SoundVolBar)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.SoundVolNumeric)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.BufferSizeNumeric)).BeginInit();
this.grpOutputMethod.SuspendLayout();
this.SuspendLayout();
//
// Cancel
@ -62,7 +65,6 @@
// OK
//
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, 244);
this.OK.Name = "OK";
this.OK.Size = new System.Drawing.Size(75, 23);
@ -74,7 +76,7 @@
// SoundOnCheckBox
//
this.SoundOnCheckBox.AutoSize = true;
this.SoundOnCheckBox.Location = new System.Drawing.Point(147, 12);
this.SoundOnCheckBox.Location = new System.Drawing.Point(108, 31);
this.SoundOnCheckBox.Name = "SoundOnCheckBox";
this.SoundOnCheckBox.Size = new System.Drawing.Size(74, 17);
this.SoundOnCheckBox.TabIndex = 3;
@ -85,7 +87,7 @@
// MuteFrameAdvance
//
this.MuteFrameAdvance.AutoSize = true;
this.MuteFrameAdvance.Location = new System.Drawing.Point(147, 35);
this.MuteFrameAdvance.Location = new System.Drawing.Point(108, 54);
this.MuteFrameAdvance.Name = "MuteFrameAdvance";
this.MuteFrameAdvance.Size = new System.Drawing.Size(128, 17);
this.MuteFrameAdvance.TabIndex = 4;
@ -123,23 +125,13 @@
this.SoundVolNumeric.TabIndex = 1;
this.SoundVolNumeric.ValueChanged += new System.EventHandler(this.SoundVolNumeric_ValueChanged);
//
// UseNewOutputBuffer
//
this.UseNewOutputBuffer.AutoSize = true;
this.UseNewOutputBuffer.Location = new System.Drawing.Point(147, 58);
this.UseNewOutputBuffer.Name = "UseNewOutputBuffer";
this.UseNewOutputBuffer.Size = new System.Drawing.Size(205, 17);
this.UseNewOutputBuffer.TabIndex = 5;
this.UseNewOutputBuffer.Text = "Use New Output Buffer (Experimental)";
this.UseNewOutputBuffer.UseVisualStyleBackColor = true;
//
// listBoxSoundDevices
//
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.Right)));
this.listBoxSoundDevices.FormattingEnabled = true;
this.listBoxSoundDevices.Location = new System.Drawing.Point(108, 102);
this.listBoxSoundDevices.Location = new System.Drawing.Point(108, 108);
this.listBoxSoundDevices.Name = "listBoxSoundDevices";
this.listBoxSoundDevices.Size = new System.Drawing.Size(284, 95);
this.listBoxSoundDevices.TabIndex = 7;
@ -147,7 +139,7 @@
// SoundDeviceLabel
//
this.SoundDeviceLabel.AutoSize = true;
this.SoundDeviceLabel.Location = new System.Drawing.Point(108, 86);
this.SoundDeviceLabel.Location = new System.Drawing.Point(105, 92);
this.SoundDeviceLabel.Name = "SoundDeviceLabel";
this.SoundDeviceLabel.Size = new System.Drawing.Size(78, 13);
this.SoundDeviceLabel.TabIndex = 6;
@ -173,7 +165,7 @@
0,
0});
this.BufferSizeNumeric.Minimum = new decimal(new int[] {
60,
30,
0,
0,
0});
@ -196,6 +188,41 @@
this.BufferSizeUnitsLabel.TabIndex = 10;
this.BufferSizeUnitsLabel.Text = "milliseconds";
//
// grpOutputMethod
//
this.grpOutputMethod.Controls.Add(this.rbOutputMethodXAudio2);
this.grpOutputMethod.Controls.Add(this.rbOutputMethodDirectSound);
this.grpOutputMethod.Location = new System.Drawing.Point(292, 12);
this.grpOutputMethod.Name = "grpOutputMethod";
this.grpOutputMethod.Size = new System.Drawing.Size(100, 68);
this.grpOutputMethod.TabIndex = 5;
this.grpOutputMethod.TabStop = false;
this.grpOutputMethod.Text = "Output Method";
//
// rbOutputMethodXAudio2
//
this.rbOutputMethodXAudio2.AutoSize = true;
this.rbOutputMethodXAudio2.Location = new System.Drawing.Point(6, 42);
this.rbOutputMethodXAudio2.Name = "rbOutputMethodXAudio2";
this.rbOutputMethodXAudio2.Size = new System.Drawing.Size(65, 17);
this.rbOutputMethodXAudio2.TabIndex = 1;
this.rbOutputMethodXAudio2.TabStop = true;
this.rbOutputMethodXAudio2.Text = "XAudio2";
this.rbOutputMethodXAudio2.UseVisualStyleBackColor = true;
this.rbOutputMethodXAudio2.CheckedChanged += new System.EventHandler(this.OutputMethodRadioButtons_CheckedChanged);
//
// rbOutputMethodDirectSound
//
this.rbOutputMethodDirectSound.AutoSize = true;
this.rbOutputMethodDirectSound.Location = new System.Drawing.Point(6, 19);
this.rbOutputMethodDirectSound.Name = "rbOutputMethodDirectSound";
this.rbOutputMethodDirectSound.Size = new System.Drawing.Size(84, 17);
this.rbOutputMethodDirectSound.TabIndex = 0;
this.rbOutputMethodDirectSound.TabStop = true;
this.rbOutputMethodDirectSound.Text = "DirectSound";
this.rbOutputMethodDirectSound.UseVisualStyleBackColor = true;
this.rbOutputMethodDirectSound.CheckedChanged += new System.EventHandler(this.OutputMethodRadioButtons_CheckedChanged);
//
// SoundConfig
//
this.AcceptButton = this.OK;
@ -203,12 +230,12 @@
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.CancelButton = this.Cancel;
this.ClientSize = new System.Drawing.Size(404, 279);
this.Controls.Add(this.grpOutputMethod);
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);
this.Controls.Add(this.MuteFrameAdvance);
this.Controls.Add(this.SoundOnCheckBox);
@ -225,6 +252,8 @@
((System.ComponentModel.ISupportInitialize)(this.SoundVolBar)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.SoundVolNumeric)).EndInit();
((System.ComponentModel.ISupportInitialize)(this.BufferSizeNumeric)).EndInit();
this.grpOutputMethod.ResumeLayout(false);
this.grpOutputMethod.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
@ -239,11 +268,13 @@
private System.Windows.Forms.GroupBox SoundVolGroup;
private System.Windows.Forms.NumericUpDown SoundVolNumeric;
private System.Windows.Forms.TrackBar SoundVolBar;
private System.Windows.Forms.CheckBox UseNewOutputBuffer;
private System.Windows.Forms.ListBox listBoxSoundDevices;
private System.Windows.Forms.Label SoundDeviceLabel;
private System.Windows.Forms.Label BufferSizeLabel;
private System.Windows.Forms.NumericUpDown BufferSizeNumeric;
private System.Windows.Forms.Label BufferSizeUnitsLabel;
private System.Windows.Forms.GroupBox grpOutputMethod;
private System.Windows.Forms.RadioButton rbOutputMethodXAudio2;
private System.Windows.Forms.RadioButton rbOutputMethodDirectSound;
}
}

View File

@ -1,4 +1,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using BizHawk.Client.Common;
@ -20,15 +22,57 @@ namespace BizHawk.Client.EmuHawk
SoundOnCheckBox.Checked = Global.Config.SoundEnabled;
MuteFrameAdvance.Checked = Global.Config.MuteFrameAdvance;
UseNewOutputBuffer.Checked = Global.Config.UseNewOutputBuffer;
rbOutputMethodDirectSound.Checked = Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound;
rbOutputMethodXAudio2.Checked = Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.XAudio2;
BufferSizeNumeric.Value = Global.Config.SoundBufferSizeMs;
SoundVolBar.Value = Global.Config.SoundVolume;
SoundVolNumeric.Value = Global.Config.SoundVolume;
UpdateSoundDialog();
_programmaticallyChangingValue = false;
}
private void OK_Click(object sender, EventArgs e)
{
if (rbOutputMethodDirectSound.Checked && (int)BufferSizeNumeric.Value < 60)
{
MessageBox.Show("Buffer size must be at least 60 milliseconds for DirectSound.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
var oldOutputMethod = Global.Config.SoundOutputMethod;
Global.Config.SoundEnabled = SoundOnCheckBox.Checked;
Global.Config.MuteFrameAdvance = MuteFrameAdvance.Checked;
if (rbOutputMethodDirectSound.Checked) Global.Config.SoundOutputMethod = Config.ESoundOutputMethod.DirectSound;
if (rbOutputMethodXAudio2.Checked) Global.Config.SoundOutputMethod = Config.ESoundOutputMethod.XAudio2;
Global.Config.SoundBufferSizeMs = (int)BufferSizeNumeric.Value;
Global.Config.SoundVolume = SoundVolBar.Value;
Global.Config.SoundDevice = (string)listBoxSoundDevices.SelectedItem ?? "<default>";
GlobalWin.Sound.StopSound();
if (Global.Config.SoundOutputMethod != oldOutputMethod)
{
GlobalWin.Sound.Dispose();
GlobalWin.Sound = new Sound(GlobalWin.MainForm.Handle);
}
GlobalWin.Sound.StartSound();
GlobalWin.OSD.AddMessage("Sound settings saved");
DialogResult = DialogResult.OK;
}
private void Cancel_Click(object sender, EventArgs e)
{
GlobalWin.OSD.AddMessage("Sound config aborted");
Close();
}
private void PopulateDeviceList()
{
IEnumerable<string> deviceNames = Enumerable.Empty<string>();
if (rbOutputMethodDirectSound.Checked) deviceNames = DirectSoundSoundOutput.GetDeviceNames();
if (rbOutputMethodXAudio2.Checked) deviceNames = XAudio2SoundOutput.GetDeviceNames();
listBoxSoundDevices.Items.Clear();
listBoxSoundDevices.Items.Add("<default>");
listBoxSoundDevices.SelectedIndex = 0;
foreach (var name in DirectSoundSoundOutput.GetDeviceNames())
foreach (var name in deviceNames)
{
listBoxSoundDevices.Items.Add(name);
if (name == Global.Config.SoundDevice)
@ -36,28 +80,12 @@ namespace BizHawk.Client.EmuHawk
listBoxSoundDevices.SelectedItem = name;
}
}
_programmaticallyChangingValue = false;
}
private void OK_Click(object sender, EventArgs e)
private void OutputMethodRadioButtons_CheckedChanged(object sender, EventArgs e)
{
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.StopSound();
GlobalWin.Sound.StartSound();
GlobalWin.OSD.AddMessage("Sound settings saved");
Close();
}
private void Cancel_Click(object sender, EventArgs e)
{
GlobalWin.OSD.AddMessage("Sound config aborted");
Close();
if (!((RadioButton)sender).Checked) return;
PopulateDeviceList();
}
private void trackBar1_Scroll(object sender, EventArgs e)