forked from MoonsideGames/MoonWorks
UpdatingSourceVoice + warn on audio leak
parent
772a0378bb
commit
e961a18a83
|
@ -64,7 +64,7 @@ namespace MoonWorks.Audio
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override unsafe void Destroy()
|
protected override unsafe void DisposeUnmanagedState()
|
||||||
{
|
{
|
||||||
if (OwnsBufferData)
|
if (OwnsBufferData)
|
||||||
{
|
{
|
||||||
|
|
|
@ -90,22 +90,15 @@ namespace MoonWorks.Audio
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unloads the Ogg data, freeing resources.
|
/// Unloads the Ogg data, freeing resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override unsafe void Unload()
|
public override void Unload()
|
||||||
{
|
{
|
||||||
if (Loaded)
|
DisposeUnmanagedState();
|
||||||
{
|
|
||||||
FAudio.stb_vorbis_close(VorbisHandle);
|
|
||||||
NativeMemory.Free((void*) FileDataPtr);
|
|
||||||
|
|
||||||
VorbisHandle = IntPtr.Zero;
|
|
||||||
FileDataPtr = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads an entire ogg file into an AudioBuffer. Useful for static audio.
|
/// Loads an entire ogg file into an AudioBuffer. Useful for static audio.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public unsafe static AudioBuffer CreateBuffer(AudioDevice device, string filePath)
|
public static unsafe AudioBuffer CreateBuffer(AudioDevice device, string filePath)
|
||||||
{
|
{
|
||||||
var filePointer = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
|
var filePointer = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
|
||||||
|
|
||||||
|
@ -143,5 +136,17 @@ namespace MoonWorks.Audio
|
||||||
(uint) lengthInBytes,
|
(uint) lengthInBytes,
|
||||||
true);
|
true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override unsafe void DisposeUnmanagedState()
|
||||||
|
{
|
||||||
|
if (Loaded)
|
||||||
|
{
|
||||||
|
FAudio.stb_vorbis_close(VorbisHandle);
|
||||||
|
NativeMemory.Free((void*) FileDataPtr);
|
||||||
|
|
||||||
|
VorbisHandle = IntPtr.Zero;
|
||||||
|
FileDataPtr = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,16 +99,9 @@ namespace MoonWorks.Audio
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unloads the qoa data, freeing resources.
|
/// Unloads the qoa data, freeing resources.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public override unsafe void Unload()
|
public override void Unload()
|
||||||
{
|
{
|
||||||
if (Loaded)
|
DisposeUnmanagedState();
|
||||||
{
|
|
||||||
FAudio.qoa_close(QoaHandle);
|
|
||||||
NativeMemory.Free((void*) FileDataPtr);
|
|
||||||
|
|
||||||
QoaHandle = IntPtr.Zero;
|
|
||||||
FileDataPtr = IntPtr.Zero;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -160,5 +153,17 @@ namespace MoonWorks.Audio
|
||||||
((UInt64)(bytes[4]) << 24) | ((UInt64)(bytes[5]) << 16) |
|
((UInt64)(bytes[4]) << 24) | ((UInt64)(bytes[5]) << 16) |
|
||||||
((UInt64)(bytes[6]) << 8) | ((UInt64)(bytes[7]) << 0);
|
((UInt64)(bytes[6]) << 8) | ((UInt64)(bytes[7]) << 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override unsafe void DisposeUnmanagedState()
|
||||||
|
{
|
||||||
|
if (Loaded)
|
||||||
|
{
|
||||||
|
FAudio.qoa_close(QoaHandle);
|
||||||
|
NativeMemory.Free((void*) FileDataPtr);
|
||||||
|
|
||||||
|
QoaHandle = IntPtr.Zero;
|
||||||
|
FileDataPtr = IntPtr.Zero;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,10 +36,5 @@ namespace MoonWorks.Audio
|
||||||
/// <param name="filledLengthInBytes">How much data was actually filled in by the decode.</param>
|
/// <param name="filledLengthInBytes">How much data was actually filled in by the decode.</param>
|
||||||
/// <param name="reachedEnd">Whether the end of the data was reached on this decode.</param>
|
/// <param name="reachedEnd">Whether the end of the data was reached on this decode.</param>
|
||||||
public abstract unsafe void Decode(void* buffer, int bufferLengthInBytes, out int filledLengthInBytes, out bool reachedEnd);
|
public abstract unsafe void Decode(void* buffer, int bufferLengthInBytes, out int filledLengthInBytes, out bool reachedEnd);
|
||||||
|
|
||||||
protected override void Destroy()
|
|
||||||
{
|
|
||||||
Unload();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ namespace MoonWorks.Audio
|
||||||
public float SpeedOfSound = 343.5f;
|
public float SpeedOfSound = 343.5f;
|
||||||
|
|
||||||
private readonly HashSet<GCHandle> resources = new HashSet<GCHandle>();
|
private readonly HashSet<GCHandle> resources = new HashSet<GCHandle>();
|
||||||
private readonly HashSet<SourceVoice> activeSourceVoices = new HashSet<SourceVoice>();
|
private readonly HashSet<UpdatingSourceVoice> updatingSourceVoices = new HashSet<UpdatingSourceVoice>();
|
||||||
|
|
||||||
private AudioTweenManager AudioTweenManager;
|
private AudioTweenManager AudioTweenManager;
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ namespace MoonWorks.Audio
|
||||||
internal readonly object StateLock = new object();
|
internal readonly object StateLock = new object();
|
||||||
|
|
||||||
private bool Running;
|
private bool Running;
|
||||||
private bool IsDisposed;
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
internal unsafe AudioDevice()
|
internal unsafe AudioDevice()
|
||||||
{
|
{
|
||||||
|
@ -164,15 +164,19 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
AudioTweenManager.Update(elapsedSeconds);
|
AudioTweenManager.Update(elapsedSeconds);
|
||||||
|
|
||||||
foreach (var voice in activeSourceVoices)
|
foreach (var voice in updatingSourceVoices)
|
||||||
{
|
{
|
||||||
voice.Update();
|
voice.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var voice in VoicesToReturn)
|
foreach (var voice in VoicesToReturn)
|
||||||
{
|
{
|
||||||
|
if (voice is UpdatingSourceVoice updatingSourceVoice)
|
||||||
|
{
|
||||||
|
updatingSourceVoices.Remove(updatingSourceVoice);
|
||||||
|
}
|
||||||
|
|
||||||
voice.Reset();
|
voice.Reset();
|
||||||
activeSourceVoices.Remove(voice);
|
|
||||||
VoicePool.Return(voice);
|
VoicePool.Return(voice);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +201,12 @@ namespace MoonWorks.Audio
|
||||||
lock (StateLock)
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
var voice = VoicePool.Obtain<T>(format);
|
var voice = VoicePool.Obtain<T>(format);
|
||||||
activeSourceVoices.Add(voice);
|
|
||||||
|
if (voice is UpdatingSourceVoice updatingSourceVoice)
|
||||||
|
{
|
||||||
|
updatingSourceVoices.Add(updatingSourceVoice);
|
||||||
|
}
|
||||||
|
|
||||||
return voice;
|
return voice;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -258,9 +267,9 @@ namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
resources.Add(resourceReference);
|
resources.Add(resourceReference);
|
||||||
|
|
||||||
if (resourceReference.Target is SourceVoice voice)
|
if (resourceReference.Target is UpdatingSourceVoice updatableVoice)
|
||||||
{
|
{
|
||||||
activeSourceVoices.Add(voice);
|
updatingSourceVoices.Add(updatableVoice);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -132,7 +132,5 @@ namespace MoonWorks.Audio
|
||||||
emitterData.pReverbCurve = IntPtr.Zero;
|
emitterData.pReverbCurve = IntPtr.Zero;
|
||||||
emitterData.CurveDistanceScaler = 1.0f;
|
emitterData.CurveDistanceScaler = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Destroy() { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,7 +94,5 @@ namespace MoonWorks.Audio
|
||||||
/* Unexposed variables, defaults based on XNA behavior */
|
/* Unexposed variables, defaults based on XNA behavior */
|
||||||
listenerData.pCone = IntPtr.Zero;
|
listenerData.pCone = IntPtr.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Destroy() { }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,16 +19,22 @@ namespace MoonWorks.Audio
|
||||||
Device.AddResourceReference(SelfReference);
|
Device.AddResourceReference(SelfReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void Destroy();
|
protected virtual void DisposeManagedState() { }
|
||||||
|
protected virtual void DisposeUnmanagedState() { }
|
||||||
|
|
||||||
protected void Dispose(bool disposing)
|
protected void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!IsDisposed)
|
if (!IsDisposed)
|
||||||
{
|
{
|
||||||
Destroy();
|
if (disposing)
|
||||||
|
{
|
||||||
|
DisposeManagedState();
|
||||||
|
|
||||||
Device.RemoveResourceReference(SelfReference);
|
Device.RemoveResourceReference(SelfReference);
|
||||||
SelfReference.Free();
|
SelfReference.Free();
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposeUnmanagedState();
|
||||||
|
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
}
|
}
|
||||||
|
@ -36,6 +42,17 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
~AudioResource()
|
~AudioResource()
|
||||||
{
|
{
|
||||||
|
#if DEBUG
|
||||||
|
// If the graphics device associated with this resource was already disposed, we assume
|
||||||
|
// that your game is in the middle of shutting down.
|
||||||
|
if (!IsDisposed && Device != null && !Device.IsDisposed)
|
||||||
|
{
|
||||||
|
// If you see this log message, you leaked a graphics resource without disposing it!
|
||||||
|
// This means your game may eventually run out of native memory for mysterious reasons.
|
||||||
|
Logger.LogWarn($"A resource of type {GetType().Name} was not Disposed.");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
|
||||||
Dispose(disposing: false);
|
Dispose(disposing: false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ namespace MoonWorks.Audio
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Plays back a series of AudioBuffers in sequence. Set the OnSoundNeeded callback to add AudioBuffers dynamically.
|
/// Plays back a series of AudioBuffers in sequence. Set the OnSoundNeeded callback to add AudioBuffers dynamically.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class SoundSequence : SourceVoice, IPoolable<SoundSequence>
|
public class SoundSequence : UpdatingSourceVoice, IPoolable<SoundSequence>
|
||||||
{
|
{
|
||||||
public int NeedSoundThreshold = 0;
|
public int NeedSoundThreshold = 0;
|
||||||
public delegate void OnSoundNeededFunc();
|
public delegate void OnSoundNeededFunc();
|
||||||
|
|
|
@ -191,12 +191,6 @@ namespace MoonWorks.Audio
|
||||||
Device.Return(this);
|
Device.Return(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Called automatically by AudioDevice in the audio thread.
|
|
||||||
/// Don't call this yourself! You might regret it!
|
|
||||||
/// </summary>
|
|
||||||
public virtual void Update() { }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds an FAudio buffer to the voice queue.
|
/// Adds an FAudio buffer to the voice queue.
|
||||||
/// The voice processes and plays back the buffers in its queue in the order that they were submitted.
|
/// The voice processes and plays back the buffers in its queue in the order that they were submitted.
|
||||||
|
@ -221,10 +215,9 @@ namespace MoonWorks.Audio
|
||||||
base.Reset();
|
base.Reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override unsafe void Destroy()
|
protected override void DisposeManagedState()
|
||||||
{
|
{
|
||||||
Stop();
|
Stop();
|
||||||
base.Destroy();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace MoonWorks.Audio
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Use in conjunction with an AudioDataStreamable object to play back streaming audio data.
|
/// Use in conjunction with an AudioDataStreamable object to play back streaming audio data.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class StreamingVoice : SourceVoice, IPoolable<StreamingVoice>
|
public class StreamingVoice : UpdatingSourceVoice, IPoolable<StreamingVoice>
|
||||||
{
|
{
|
||||||
private const int BUFFER_COUNT = 3;
|
private const int BUFFER_COUNT = 3;
|
||||||
private readonly IntPtr[] buffers;
|
private readonly IntPtr[] buffers;
|
||||||
|
|
|
@ -4,7 +4,7 @@ namespace MoonWorks.Audio
|
||||||
/// TransientVoice is intended for playing one-off sound effects that don't have a long term reference. <br/>
|
/// TransientVoice is intended for playing one-off sound effects that don't have a long term reference. <br/>
|
||||||
/// It will be automatically returned to the AudioDevice SourceVoice pool once it is done playing back.
|
/// It will be automatically returned to the AudioDevice SourceVoice pool once it is done playing back.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class TransientVoice : SourceVoice, IPoolable<TransientVoice>
|
public class TransientVoice : UpdatingSourceVoice, IPoolable<TransientVoice>
|
||||||
{
|
{
|
||||||
static TransientVoice IPoolable<TransientVoice>.Create(AudioDevice device, Format format)
|
static TransientVoice IPoolable<TransientVoice>.Create(AudioDevice device, Format format)
|
||||||
{
|
{
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace MoonWorks.Audio
|
||||||
|
{
|
||||||
|
public abstract class UpdatingSourceVoice : SourceVoice
|
||||||
|
{
|
||||||
|
protected UpdatingSourceVoice(AudioDevice device, Format format) : base(device, format)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void Update();
|
||||||
|
}
|
||||||
|
}
|
|
@ -565,7 +565,7 @@ namespace MoonWorks.Audio
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected unsafe override void Destroy()
|
protected override void DisposeUnmanagedState()
|
||||||
{
|
{
|
||||||
NativeMemory.Free(pMatrixCoefficients);
|
NativeMemory.Free(pMatrixCoefficients);
|
||||||
FAudio.FAudioVoice_DestroyVoice(Handle);
|
FAudio.FAudioVoice_DestroyVoice(Handle);
|
||||||
|
|
Loading…
Reference in New Issue