Show More
Commit Description:
Add timers for Simulation and various engines...
Commit Description:
Add timers for Simulation and various engines Starting to add additional timers for different stages of the process of updating in order to get more insight into what is slowing it down. The update takes 9ms, which is much longer than it used to. Engine-specific timers are coming later.
File last commit:
Show/Diff file:
Action:
FNA/src/Audio/AudioEngine.cs
509 lines | 10.5 KiB | text/x-csharp | CSharpLexer
#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.Collections.ObjectModel;
using System.Runtime.InteropServices;
#endregion
namespace Microsoft.Xna.Framework.Audio
{
// http://msdn.microsoft.com/en-us/library/dd940262.aspx
public class AudioEngine : IDisposable
{
#region Public Constants
public const int ContentVersion = 46;
#endregion
#region Public Properties
public ReadOnlyCollection<RendererDetail> RendererDetails
{
get
{
return new ReadOnlyCollection<RendererDetail>(
rendererDetails
);
}
}
public bool IsDisposed
{
get;
private set;
}
#endregion
#region Internal Variables
internal readonly IntPtr handle;
internal readonly byte[] handle3D;
internal readonly ushort channels;
internal readonly object gcSync = new object();
#endregion
#region Private Variables
private byte[] buffer;
private GCHandle pin;
private RendererDetail[] rendererDetails;
private readonly FAudio.FACTNotificationCallback xactNotificationFunc;
private FAudio.FACTNotificationDescription notificationDesc;
private class IntPtrComparer : IEqualityComparer<IntPtr>
{
public bool Equals(IntPtr x, IntPtr y)
{
return x == y;
}
public int GetHashCode(IntPtr obj)
{
return obj.GetHashCode();
}
}
private static readonly IntPtrComparer comparer = new IntPtrComparer();
// If this isn't static, destructors gets confused like idiots
private static readonly Dictionary<IntPtr, WeakReference> xactPtrs = new Dictionary<IntPtr, WeakReference>(comparer);
#endregion
#region Public Static Variables
// STOP LEAKING YOUR XACT DATA, GOOD GRIEF PEOPLE
internal static bool ProgramExiting = false;
#endregion
#region Disposing Event
public event EventHandler<EventArgs> Disposing;
#endregion
#region Public Constructors
public AudioEngine(
string settingsFile
) : this(
settingsFile,
new TimeSpan(
0, 0, 0, 0,
(int) FAudio.FACT_ENGINE_LOOKAHEAD_DEFAULT
),
null
) {
}
public AudioEngine(
string settingsFile,
TimeSpan lookAheadTime,
string rendererId
) {
if (String.IsNullOrEmpty(settingsFile))
{
throw new ArgumentNullException("settingsFile");
}
// Read entire file into memory, pin buffer
buffer = TitleContainer.ReadAllBytes(settingsFile);
pin = GCHandle.Alloc(buffer, GCHandleType.Pinned);
// Generate engine parameters
FAudio.FACTRuntimeParameters settings = new FAudio.FACTRuntimeParameters();
settings.pGlobalSettingsBuffer = pin.AddrOfPinnedObject();
settings.globalSettingsBufferSize = (uint) buffer.Length;
xactNotificationFunc = OnXACTNotification;
settings.fnNotificationCallback = Marshal.GetFunctionPointerForDelegate(
xactNotificationFunc
);
// Special parameters from constructor
settings.lookAheadTime = (uint) lookAheadTime.Milliseconds;
if (!string.IsNullOrEmpty(rendererId))
{
// FIXME: wchar_t? -flibit
settings.pRendererID = Marshal.StringToHGlobalAuto(rendererId);
}
// Init engine, finally
FAudio.FACTCreateEngine(0, out handle);
if (FAudio.FACTAudioEngine_Initialize(handle, ref settings) != 0)
{
throw new InvalidOperationException(
"Engine initialization failed!"
);
}
// Free the settings strings
if (settings.pRendererID != IntPtr.Zero)
{
Marshal.FreeHGlobal(settings.pRendererID);
}
// Grab RendererDetails
ushort rendererCount;
FAudio.FACTAudioEngine_GetRendererCount(
handle,
out rendererCount
);
if (rendererCount == 0)
{
Dispose();
throw new NoAudioHardwareException();
}
rendererDetails = new RendererDetail[rendererCount];
char[] displayName = new char[0xFF];
char[] rendererID = new char[0xFF];
for (ushort i = 0; i < rendererCount; i += 1)
{
FAudio.FACTRendererDetails details;
FAudio.FACTAudioEngine_GetRendererDetails(
handle,
i,
out details
);
unsafe
{
for (int j = 0; j < 0xFF; j += 1)
{
displayName[j] = (char) details.displayName[j];
rendererID[j] = (char) details.rendererID[j];
}
}
rendererDetails[i] = new RendererDetail(
new string(displayName),
new string(rendererID)
);
}
// Init 3D audio
handle3D = new byte[FAudio.F3DAUDIO_HANDLE_BYTESIZE];
FAudio.FACT3DInitialize(
handle,
handle3D
);
// Grab channel count for DSP_SETTINGS
FAudio.FAudioWaveFormatExtensible mixFormat;
FAudio.FACTAudioEngine_GetFinalMixFormat(
handle,
out mixFormat
);
channels = mixFormat.Format.nChannels;
// All XACT references have to go through here...
notificationDesc = new FAudio.FACTNotificationDescription();
}
#endregion
#region Destructor
~AudioEngine()
{
Dispose(false);
}
#endregion
#region Public Dispose Methods
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
#endregion
#region Public Methods
public AudioCategory GetCategory(string name)
{
if (String.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
ushort category = FAudio.FACTAudioEngine_GetCategory(
handle,
name
);
if (category == FAudio.FACTCATEGORY_INVALID)
{
throw new InvalidOperationException(
"Invalid category name!"
);
}
return new AudioCategory(this, category, name);
}
public float GetGlobalVariable(string name)
{
if (String.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
ushort variable = FAudio.FACTAudioEngine_GetGlobalVariableIndex(
handle,
name
);
if (variable == FAudio.FACTVARIABLEINDEX_INVALID)
{
throw new InvalidOperationException(
"Invalid variable name!"
);
}
float result;
FAudio.FACTAudioEngine_GetGlobalVariable(
handle,
variable,
out result
);
return result;
}
public void SetGlobalVariable(string name, float value)
{
if (String.IsNullOrEmpty(name))
{
throw new ArgumentNullException("name");
}
ushort variable = FAudio.FACTAudioEngine_GetGlobalVariableIndex(
handle,
name
);
if (variable == FAudio.FACTVARIABLEINDEX_INVALID)
{
throw new InvalidOperationException(
"Invalid variable name!"
);
}
FAudio.FACTAudioEngine_SetGlobalVariable(
handle,
variable,
value
);
}
public void Update()
{
FAudio.FACTAudioEngine_DoWork(handle);
}
#endregion
#region Protected Methods
protected virtual void Dispose(bool disposing)
{
lock (gcSync)
{
if (!IsDisposed)
{
if (Disposing != null)
{
Disposing.Invoke(this, null);
}
FAudio.FACTAudioEngine_ShutDown(handle);
pin.Free();
buffer = null;
rendererDetails = null;
IsDisposed = true;
}
}
}
#endregion
#region Internal Methods
internal void RegisterWaveBank(
IntPtr ptr,
WeakReference reference
) {
notificationDesc.type = FAudio.FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED;
notificationDesc.pWaveBank = ptr;
FAudio.FACTAudioEngine_RegisterNotification(
handle,
ref notificationDesc
);
lock (xactPtrs)
{
xactPtrs.Add(ptr, reference);
}
}
internal void RegisterSoundBank(
IntPtr ptr,
WeakReference reference
) {
notificationDesc.type = FAudio.FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED;
notificationDesc.pSoundBank = ptr;
FAudio.FACTAudioEngine_RegisterNotification(
handle,
ref notificationDesc
);
lock (xactPtrs)
{
xactPtrs.Add(ptr, reference);
}
}
internal void RegisterCue(
IntPtr ptr,
WeakReference reference
) {
notificationDesc.type = FAudio.FACTNOTIFICATIONTYPE_CUEDESTROYED;
notificationDesc.pCue = ptr;
FAudio.FACTAudioEngine_RegisterNotification(
handle,
ref notificationDesc
);
lock (xactPtrs)
{
xactPtrs.Add(ptr, reference);
}
}
internal void UnregisterWaveBank(IntPtr ptr)
{
lock (xactPtrs)
{
if (!xactPtrs.ContainsKey(ptr))
{
return;
}
notificationDesc.type = FAudio.FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED;
notificationDesc.pWaveBank = ptr;
FAudio.FACTAudioEngine_UnRegisterNotification(
handle,
ref notificationDesc
);
xactPtrs.Remove(ptr);
}
}
internal void UnregisterSoundBank(IntPtr ptr)
{
lock (xactPtrs)
{
if (!xactPtrs.ContainsKey(ptr))
{
return;
}
notificationDesc.type = FAudio.FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED;
notificationDesc.pSoundBank = ptr;
FAudio.FACTAudioEngine_UnRegisterNotification(
handle,
ref notificationDesc
);
xactPtrs.Remove(ptr);
}
}
internal void UnregisterCue(IntPtr ptr)
{
lock (xactPtrs)
{
if (!xactPtrs.ContainsKey(ptr))
{
return;
}
notificationDesc.type = FAudio.FACTNOTIFICATIONTYPE_CUEDESTROYED;
notificationDesc.pCue = ptr;
FAudio.FACTAudioEngine_UnRegisterNotification(
handle,
ref notificationDesc
);
xactPtrs.Remove(ptr);
}
}
#endregion
#region Private Methods
[ObjCRuntime.MonoPInvokeCallback(typeof(FAudio.FACTNotificationCallback))]
private static unsafe void OnXACTNotification(IntPtr notification)
{
WeakReference reference;
FAudio.FACTNotification* not = (FAudio.FACTNotification*) notification;
if (not->type == FAudio.FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED)
{
IntPtr target = not->anon.waveBank.pWaveBank;
lock (xactPtrs)
{
if (xactPtrs.TryGetValue(target, out reference))
{
if (reference.IsAlive)
{
(reference.Target as WaveBank).OnWaveBankDestroyed();
}
}
xactPtrs.Remove(target);
}
}
else if (not->type == FAudio.FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED)
{
IntPtr target = not->anon.soundBank.pSoundBank;
lock (xactPtrs)
{
if (xactPtrs.TryGetValue(target, out reference))
{
if (reference.IsAlive)
{
(reference.Target as SoundBank).OnSoundBankDestroyed();
}
}
xactPtrs.Remove(target);
}
}
else if (not->type == FAudio.FACTNOTIFICATIONTYPE_CUEDESTROYED)
{
IntPtr target = not->anon.cue.pCue;
lock (xactPtrs)
{
if (xactPtrs.TryGetValue(target, out reference))
{
if (reference.IsAlive)
{
(reference.Target as Cue).OnCueDestroyed();
}
}
xactPtrs.Remove(target);
}
}
}
#endregion
}
}