Add OpenAL sound output.
XAudio2: Some stuff I forgot to dispose.
This commit is contained in:
parent
822d42048e
commit
c56edd6e93
|
@ -111,7 +111,7 @@ namespace BizHawk.Client.Common
|
||||||
|
|
||||||
public enum EDispMethod { OpenGL, GdiPlus, SlimDX9 };
|
public enum EDispMethod { OpenGL, GdiPlus, SlimDX9 };
|
||||||
|
|
||||||
public enum ESoundOutputMethod { DirectSound, XAudio2, Dummy };
|
public enum ESoundOutputMethod { DirectSound, XAudio2, OpenAL, Dummy };
|
||||||
|
|
||||||
public enum EDispManagerAR { None, System, Custom };
|
public enum EDispManagerAR { None, System, Custom };
|
||||||
|
|
||||||
|
|
|
@ -625,6 +625,7 @@
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="Sound\Output\DirectSoundSoundOutput.cs" />
|
<Compile Include="Sound\Output\DirectSoundSoundOutput.cs" />
|
||||||
<Compile Include="Sound\Output\DummySoundOutput.cs" />
|
<Compile Include="Sound\Output\DummySoundOutput.cs" />
|
||||||
|
<Compile Include="Sound\Output\OpenALSoundOutput.cs" />
|
||||||
<Compile Include="Sound\Output\XAudio2SoundOutput.cs" />
|
<Compile Include="Sound\Output\XAudio2SoundOutput.cs" />
|
||||||
<Compile Include="Sound\Sound.cs" />
|
<Compile Include="Sound\Sound.cs" />
|
||||||
<Compile Include="Sound\Utilities\SoundOutputProvider.cs" />
|
<Compile Include="Sound\Utilities\SoundOutputProvider.cs" />
|
||||||
|
|
|
@ -276,6 +276,9 @@ namespace BizHawk.Client.EmuHawk
|
||||||
Global.ActiveController = Global.NullControls;
|
Global.ActiveController = Global.NullControls;
|
||||||
Global.AutoFireController = Global.AutofireNullControls;
|
Global.AutoFireController = Global.AutofireNullControls;
|
||||||
Global.AutofireStickyXORAdapter.SetOnOffPatternFromConfig();
|
Global.AutofireStickyXORAdapter.SetOnOffPatternFromConfig();
|
||||||
|
#if !WINDOWS
|
||||||
|
Global.Config.SoundOutputMethod = Config.ESoundOutputMethod.OpenAL;
|
||||||
|
#endif
|
||||||
try { GlobalWin.Sound = new Sound(Handle); }
|
try { GlobalWin.Sound = new Sound(Handle); }
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
using OpenTK.Audio;
|
||||||
|
using OpenTK.Audio.OpenAL;
|
||||||
|
|
||||||
|
using BizHawk.Client.Common;
|
||||||
|
|
||||||
|
namespace BizHawk.Client.EmuHawk
|
||||||
|
{
|
||||||
|
public class OpenALSoundOutput : ISoundOutput
|
||||||
|
{
|
||||||
|
private bool _disposed;
|
||||||
|
private Sound _sound;
|
||||||
|
private AudioContext _context;
|
||||||
|
private int _sourceID;
|
||||||
|
private BufferPool _bufferPool;
|
||||||
|
private long _runningSamplesPlayed;
|
||||||
|
private long _runningSamplesQueued;
|
||||||
|
|
||||||
|
public OpenALSoundOutput(Sound sound)
|
||||||
|
{
|
||||||
|
_sound = sound;
|
||||||
|
_context = new AudioContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (_disposed) return;
|
||||||
|
|
||||||
|
_context.Dispose();
|
||||||
|
_context = null;
|
||||||
|
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> GetDeviceNames()
|
||||||
|
{
|
||||||
|
return Enumerable.Empty<string>();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int BufferSizeSamples { get; set; }
|
||||||
|
|
||||||
|
public int MaxSamplesDeficit { get; private set; }
|
||||||
|
|
||||||
|
public void ApplyVolumeSettings(double volume)
|
||||||
|
{
|
||||||
|
AL.Source(_sourceID, ALSourcef.Gain, (float)volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StartSound()
|
||||||
|
{
|
||||||
|
BufferSizeSamples = Sound.MillisecondsToSamples(Global.Config.SoundBufferSizeMs);
|
||||||
|
MaxSamplesDeficit = BufferSizeSamples;
|
||||||
|
|
||||||
|
_sourceID = AL.GenSource();
|
||||||
|
|
||||||
|
_bufferPool = new BufferPool();
|
||||||
|
_runningSamplesQueued = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopSound()
|
||||||
|
{
|
||||||
|
AL.SourceStop(_sourceID);
|
||||||
|
|
||||||
|
AL.DeleteSource(_sourceID);
|
||||||
|
|
||||||
|
_bufferPool.Dispose();
|
||||||
|
_bufferPool = null;
|
||||||
|
|
||||||
|
BufferSizeSamples = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CalculateSamplesNeeded()
|
||||||
|
{
|
||||||
|
bool isInitializing = _runningSamplesQueued == 0;
|
||||||
|
bool detectedUnderrun = !isInitializing && GetSource(ALGetSourcei.BuffersProcessed) == GetSource(ALGetSourcei.BuffersQueued);
|
||||||
|
if (detectedUnderrun)
|
||||||
|
{
|
||||||
|
_sound.OnUnderrun();
|
||||||
|
}
|
||||||
|
UnqueueProcessedBuffers();
|
||||||
|
long samplesAwaitingPlayback = _runningSamplesQueued - (_runningSamplesPlayed + GetSource(ALGetSourcei.SampleOffset));
|
||||||
|
int samplesNeeded = (int)Math.Max(BufferSizeSamples - samplesAwaitingPlayback, 0);
|
||||||
|
if (isInitializing || detectedUnderrun)
|
||||||
|
{
|
||||||
|
_sound.HandleInitializationOrUnderrun(detectedUnderrun, ref samplesNeeded);
|
||||||
|
}
|
||||||
|
return samplesNeeded;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void WriteSamples(short[] samples, int sampleCount)
|
||||||
|
{
|
||||||
|
if (sampleCount == 0) return;
|
||||||
|
UnqueueProcessedBuffers();
|
||||||
|
int byteCount = sampleCount * Sound.BlockAlign;
|
||||||
|
var buffer = _bufferPool.Obtain(byteCount);
|
||||||
|
AL.BufferData(buffer.BufferID, ALFormat.Stereo16, samples, byteCount, Sound.SampleRate);
|
||||||
|
AL.SourceQueueBuffer(_sourceID, buffer.BufferID);
|
||||||
|
_runningSamplesQueued += sampleCount;
|
||||||
|
if (AL.GetSourceState(_sourceID) != ALSourceState.Playing)
|
||||||
|
{
|
||||||
|
AL.SourcePlay(_sourceID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UnqueueProcessedBuffers()
|
||||||
|
{
|
||||||
|
int releaseCount = GetSource(ALGetSourcei.BuffersProcessed);
|
||||||
|
while (releaseCount > 0)
|
||||||
|
{
|
||||||
|
AL.SourceUnqueueBuffer(_sourceID);
|
||||||
|
var releasedBuffer = _bufferPool.ReleaseOne();
|
||||||
|
_runningSamplesPlayed += releasedBuffer.Length / Sound.BlockAlign;
|
||||||
|
releaseCount--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetSource(ALGetSourcei param)
|
||||||
|
{
|
||||||
|
int value;
|
||||||
|
AL.GetSource(_sourceID, param, out value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private class BufferPool : IDisposable
|
||||||
|
{
|
||||||
|
private List<BufferPoolItem> _availableItems = new List<BufferPoolItem>();
|
||||||
|
private Queue<BufferPoolItem> _obtainedItems = new Queue<BufferPoolItem>();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (BufferPoolItem item in _availableItems.Concat(_obtainedItems))
|
||||||
|
{
|
||||||
|
AL.DeleteBuffer(item.BufferID);
|
||||||
|
}
|
||||||
|
_availableItems.Clear();
|
||||||
|
_obtainedItems.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferPoolItem Obtain(int length)
|
||||||
|
{
|
||||||
|
BufferPoolItem item = GetAvailableItem() ?? new BufferPoolItem();
|
||||||
|
item.Length = length;
|
||||||
|
_obtainedItems.Enqueue(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BufferPoolItem GetAvailableItem()
|
||||||
|
{
|
||||||
|
if (_availableItems.Count == 0) return null;
|
||||||
|
BufferPoolItem item = _availableItems[0];
|
||||||
|
_availableItems.RemoveAt(0);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BufferPoolItem ReleaseOne()
|
||||||
|
{
|
||||||
|
BufferPoolItem item = _obtainedItems.Dequeue();
|
||||||
|
_availableItems.Add(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BufferPoolItem
|
||||||
|
{
|
||||||
|
public int BufferID { get; private set; }
|
||||||
|
public int Length { get; set; }
|
||||||
|
|
||||||
|
public BufferPoolItem()
|
||||||
|
{
|
||||||
|
BufferID = AL.GenBuffer();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -92,6 +92,7 @@ namespace BizHawk.Client.EmuHawk
|
||||||
_sourceVoice.Dispose();
|
_sourceVoice.Dispose();
|
||||||
_sourceVoice = null;
|
_sourceVoice = null;
|
||||||
|
|
||||||
|
_bufferPool.Dispose();
|
||||||
_bufferPool = null;
|
_bufferPool = null;
|
||||||
|
|
||||||
BufferSizeSamples = 0;
|
BufferSizeSamples = 0;
|
||||||
|
@ -129,11 +130,21 @@ namespace BizHawk.Client.EmuHawk
|
||||||
_runningSamplesQueued += sampleCount;
|
_runningSamplesQueued += sampleCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
private class BufferPool
|
private class BufferPool : IDisposable
|
||||||
{
|
{
|
||||||
private List<BufferPoolItem> _availableItems = new List<BufferPoolItem>();
|
private List<BufferPoolItem> _availableItems = new List<BufferPoolItem>();
|
||||||
private Queue<BufferPoolItem> _obtainedItems = new Queue<BufferPoolItem>();
|
private Queue<BufferPoolItem> _obtainedItems = new Queue<BufferPoolItem>();
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
foreach (BufferPoolItem item in _availableItems.Concat(_obtainedItems))
|
||||||
|
{
|
||||||
|
item.DataStream.Dispose();
|
||||||
|
}
|
||||||
|
_availableItems.Clear();
|
||||||
|
_obtainedItems.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
public BufferPoolItem Obtain(int length)
|
public BufferPoolItem Obtain(int length)
|
||||||
{
|
{
|
||||||
BufferPoolItem item = GetAvailableItem(length) ?? new BufferPoolItem(length);
|
BufferPoolItem item = GetAvailableItem(length) ?? new BufferPoolItem(length);
|
||||||
|
|
|
@ -25,13 +25,16 @@ namespace BizHawk.Client.EmuHawk
|
||||||
#if WINDOWS
|
#if WINDOWS
|
||||||
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound)
|
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.DirectSound)
|
||||||
_soundOutput = new DirectSoundSoundOutput(this, mainWindowHandle);
|
_soundOutput = new DirectSoundSoundOutput(this, mainWindowHandle);
|
||||||
else if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.XAudio2)
|
|
||||||
|
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.XAudio2)
|
||||||
_soundOutput = new XAudio2SoundOutput(this);
|
_soundOutput = new XAudio2SoundOutput(this);
|
||||||
else
|
|
||||||
_soundOutput = new DummySoundOutput(this);
|
|
||||||
#else
|
|
||||||
_soundOutput = new DummySoundOutput(this);
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (Global.Config.SoundOutputMethod == Config.ESoundOutputMethod.OpenAL)
|
||||||
|
_soundOutput = new OpenALSoundOutput(this);
|
||||||
|
|
||||||
|
if (_soundOutput == null)
|
||||||
|
_soundOutput = new DummySoundOutput(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
|
|
|
@ -67,8 +67,10 @@ namespace BizHawk.Client.EmuHawk
|
||||||
private void PopulateDeviceList()
|
private void PopulateDeviceList()
|
||||||
{
|
{
|
||||||
IEnumerable<string> deviceNames = Enumerable.Empty<string>();
|
IEnumerable<string> deviceNames = Enumerable.Empty<string>();
|
||||||
|
#if WINDOWS
|
||||||
if (rbOutputMethodDirectSound.Checked) deviceNames = DirectSoundSoundOutput.GetDeviceNames();
|
if (rbOutputMethodDirectSound.Checked) deviceNames = DirectSoundSoundOutput.GetDeviceNames();
|
||||||
if (rbOutputMethodXAudio2.Checked) deviceNames = XAudio2SoundOutput.GetDeviceNames();
|
if (rbOutputMethodXAudio2.Checked) deviceNames = XAudio2SoundOutput.GetDeviceNames();
|
||||||
|
#endif
|
||||||
listBoxSoundDevices.Items.Clear();
|
listBoxSoundDevices.Items.Clear();
|
||||||
listBoxSoundDevices.Items.Add("<default>");
|
listBoxSoundDevices.Items.Add("<default>");
|
||||||
listBoxSoundDevices.SelectedIndex = 0;
|
listBoxSoundDevices.SelectedIndex = 0;
|
||||||
|
|
Loading…
Reference in New Issue