Show More
Commit Description:
Various UI improvements.
Commit Description:
Various UI improvements.
References:
File last commit:
Show/Diff file:
Action:
FNA/src/Audio/DynamicSoundEffectInstance.cs
312 lines | 6.3 KiB | text/x-csharp | CSharpLexer
312 lines | 6.3 KiB | text/x-csharp | CSharpLexer
r0 | #region License | |||
/* FNA - XNA4 Reimplementation for Desktop Platforms | ||||
* Copyright 2009-2020 Ethan Lee and the MonoGame Team | ||||
* | ||||
* Released under the Microsoft Public License. | ||||
* See LICENSE for details. | ||||
*/ | ||||
#endregion | ||||
#region Using Statements | ||||
using System; | ||||
using System.Collections.Generic; | ||||
using System.Runtime.InteropServices; | ||||
#endregion | ||||
namespace Microsoft.Xna.Framework.Audio | ||||
{ | ||||
// http://msdn.microsoft.com/en-us/library/microsoft.xna.framework.audio.dynamicsoundeffectinstance.aspx | ||||
public sealed class DynamicSoundEffectInstance : SoundEffectInstance | ||||
{ | ||||
#region Public Properties | ||||
public int PendingBufferCount | ||||
{ | ||||
get | ||||
{ | ||||
return queuedBuffers.Count; | ||||
} | ||||
} | ||||
public override bool IsLooped | ||||
{ | ||||
get | ||||
{ | ||||
return false; | ||||
} | ||||
set | ||||
{ | ||||
// No-op, DynamicSoundEffectInstance cannot be looped! | ||||
} | ||||
} | ||||
#endregion | ||||
#region Internal Variables | ||||
internal FAudio.FAudioWaveFormatEx format; | ||||
#endregion | ||||
#region Private Variables | ||||
private int sampleRate; | ||||
private AudioChannels channels; | ||||
private List<IntPtr> queuedBuffers; | ||||
private List<uint> queuedSizes; | ||||
#endregion | ||||
#region Private Constants | ||||
private const int MINIMUM_BUFFER_CHECK = 3; | ||||
#endregion | ||||
#region BufferNeeded Event | ||||
public event EventHandler<EventArgs> BufferNeeded; | ||||
#endregion | ||||
#region Public Constructor | ||||
public DynamicSoundEffectInstance( | ||||
int sampleRate, | ||||
AudioChannels channels | ||||
) : base() { | ||||
this.sampleRate = sampleRate; | ||||
this.channels = channels; | ||||
isDynamic = true; | ||||
format = new FAudio.FAudioWaveFormatEx(); | ||||
format.wFormatTag = 1; | ||||
format.nChannels = (ushort) channels; | ||||
format.nSamplesPerSec = (uint) sampleRate; | ||||
format.wBitsPerSample = 16; | ||||
format.nBlockAlign = (ushort) (2 * format.nChannels); | ||||
format.nAvgBytesPerSec = format.nBlockAlign * format.nSamplesPerSec; | ||||
format.cbSize = 0; | ||||
queuedBuffers = new List<IntPtr>(); | ||||
queuedSizes = new List<uint>(); | ||||
InitDSPSettings(format.nChannels); | ||||
} | ||||
#endregion | ||||
#region Destructor | ||||
~DynamicSoundEffectInstance() | ||||
{ | ||||
// FIXME: ReRegisterForFinalize? -flibit | ||||
Dispose(); | ||||
} | ||||
#endregion | ||||
#region Public Methods | ||||
public TimeSpan GetSampleDuration(int sizeInBytes) | ||||
{ | ||||
return SoundEffect.GetSampleDuration( | ||||
sizeInBytes, | ||||
sampleRate, | ||||
channels | ||||
); | ||||
} | ||||
public int GetSampleSizeInBytes(TimeSpan duration) | ||||
{ | ||||
return SoundEffect.GetSampleSizeInBytes( | ||||
duration, | ||||
sampleRate, | ||||
channels | ||||
); | ||||
} | ||||
public override void Play() | ||||
{ | ||||
// Wait! What if we need moar buffers? | ||||
Update(); | ||||
// Okay we're good | ||||
base.Play(); | ||||
FrameworkDispatcher.Streams.Add(this); | ||||
} | ||||
public void SubmitBuffer(byte[] buffer) | ||||
{ | ||||
this.SubmitBuffer(buffer, 0, buffer.Length); | ||||
} | ||||
public void SubmitBuffer(byte[] buffer, int offset, int count) | ||||
{ | ||||
IntPtr next = Marshal.AllocHGlobal(count); | ||||
Marshal.Copy(buffer, offset, next, count); | ||||
lock (queuedBuffers) | ||||
{ | ||||
queuedBuffers.Add(next); | ||||
if (State != SoundState.Stopped) | ||||
{ | ||||
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer(); | ||||
buf.AudioBytes = (uint) count; | ||||
buf.pAudioData = next; | ||||
buf.PlayLength = ( | ||||
buf.AudioBytes / | ||||
(uint) channels / | ||||
(uint) (format.wBitsPerSample / 8) | ||||
); | ||||
FAudio.FAudioSourceVoice_SubmitSourceBuffer( | ||||
handle, | ||||
ref buf, | ||||
IntPtr.Zero | ||||
); | ||||
} | ||||
else | ||||
{ | ||||
queuedSizes.Add((uint) count); | ||||
} | ||||
} | ||||
} | ||||
public void SubmitFloatBufferEXT(float[] buffer) | ||||
{ | ||||
SubmitFloatBufferEXT(buffer, 0, buffer.Length); | ||||
} | ||||
public void SubmitFloatBufferEXT(float[] buffer, int offset, int count) | ||||
{ | ||||
/* Float samples are the typical format received from decoders. | ||||
* We currently use this for the VideoPlayer. | ||||
* -flibit | ||||
*/ | ||||
if (State != SoundState.Stopped && format.wFormatTag == 1) | ||||
{ | ||||
throw new InvalidOperationException( | ||||
"Submit a float buffer before Playing!" | ||||
); | ||||
} | ||||
format.wFormatTag = 3; | ||||
format.wBitsPerSample = 32; | ||||
format.nBlockAlign = (ushort) (4 * format.nChannels); | ||||
format.nAvgBytesPerSec = format.nBlockAlign * format.nSamplesPerSec; | ||||
IntPtr next = Marshal.AllocHGlobal(count * sizeof(float)); | ||||
Marshal.Copy(buffer, offset, next, count); | ||||
lock (queuedBuffers) | ||||
{ | ||||
queuedBuffers.Add(next); | ||||
if (State != SoundState.Stopped) | ||||
{ | ||||
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer(); | ||||
buf.AudioBytes = (uint) count * sizeof(float); | ||||
buf.pAudioData = next; | ||||
buf.PlayLength = ( | ||||
buf.AudioBytes / | ||||
(uint) channels / | ||||
(uint) (format.wBitsPerSample / 8) | ||||
); | ||||
FAudio.FAudioSourceVoice_SubmitSourceBuffer( | ||||
handle, | ||||
ref buf, | ||||
IntPtr.Zero | ||||
); | ||||
} | ||||
else | ||||
{ | ||||
queuedSizes.Add((uint) count * sizeof(float)); | ||||
} | ||||
} | ||||
} | ||||
#endregion | ||||
#region Protected Methods | ||||
protected override void Dispose(bool disposing) | ||||
{ | ||||
// Not much to see here... | ||||
base.Dispose(disposing); | ||||
} | ||||
#endregion | ||||
#region Internal Methods | ||||
internal void QueueInitialBuffers() | ||||
{ | ||||
FAudio.FAudioBuffer buffer = new FAudio.FAudioBuffer(); | ||||
lock (queuedBuffers) | ||||
{ | ||||
for (int i = 0; i < queuedBuffers.Count; i += 1) | ||||
{ | ||||
buffer.AudioBytes = queuedSizes[i]; | ||||
buffer.pAudioData = queuedBuffers[i]; | ||||
buffer.PlayLength = ( | ||||
buffer.AudioBytes / | ||||
(uint) channels / | ||||
(uint) (format.wBitsPerSample / 8) | ||||
); | ||||
FAudio.FAudioSourceVoice_SubmitSourceBuffer( | ||||
handle, | ||||
ref buffer, | ||||
IntPtr.Zero | ||||
); | ||||
} | ||||
queuedSizes.Clear(); | ||||
} | ||||
} | ||||
internal void ClearBuffers() | ||||
{ | ||||
lock (queuedBuffers) | ||||
{ | ||||
foreach (IntPtr buf in queuedBuffers) | ||||
{ | ||||
Marshal.FreeHGlobal(buf); | ||||
} | ||||
queuedBuffers.Clear(); | ||||
queuedSizes.Clear(); | ||||
} | ||||
} | ||||
internal void Update() | ||||
{ | ||||
if (State != SoundState.Playing) | ||||
{ | ||||
// Shh, we don't need you right now... | ||||
return; | ||||
} | ||||
if (handle != IntPtr.Zero) | ||||
{ | ||||
FAudio.FAudioVoiceState state; | ||||
FAudio.FAudioSourceVoice_GetState( | ||||
handle, | ||||
out state, | ||||
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED | ||||
); | ||||
while (PendingBufferCount > state.BuffersQueued) | ||||
lock (queuedBuffers) | ||||
{ | ||||
Marshal.FreeHGlobal(queuedBuffers[0]); | ||||
queuedBuffers.RemoveAt(0); | ||||
} | ||||
} | ||||
// Do we need even moar buffers? | ||||
for ( | ||||
int i = MINIMUM_BUFFER_CHECK - PendingBufferCount; | ||||
(i > 0) && BufferNeeded != null; | ||||
i -= 1 | ||||
) { | ||||
BufferNeeded(this, null); | ||||
} | ||||
} | ||||
#endregion | ||||
} | ||||
} | ||||