Show More
Commit Description:
Fix issue with simulation....
Commit Description:
Fix issue with simulation.
Previously, if there was more than one tick being advanced at once, it would
overshoot how many ticks it covered. So if it was covering 5 ticks and each
tick happens every 100 units, rather than recording that it had simulated
through t= 500, it would increase the cumulative time for each tick, recording
that it had simulated through t=2500.
Add error message, too.
References:
File last commit:
Show/Diff file:
Action:
FNA/src/Audio/AudioEngine.cs
427 lines | 9.2 KiB | text/x-csharp | CSharpLexer
427 lines | 9.2 KiB | text/x-csharp | CSharpLexer
r0 | #region License | |||
/* FNA - XNA4 Reimplementation for Desktop Platforms | ||||
r690 | * Copyright 2009-2022 Ethan Lee and the MonoGame Team | |||
r0 | * | |||
* 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 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"); | ||||
} | ||||
r690 | // Allocate (but don't initialize just yet!) | |||
FAudio.FACTCreateEngine(0, out handle); | ||||
// Grab RendererDetails | ||||
ushort rendererCount; | ||||
FAudio.FACTAudioEngine_GetRendererCount( | ||||
handle, | ||||
out rendererCount | ||||
); | ||||
if (rendererCount == 0) | ||||
{ | ||||
FAudio.FACTAudioEngine_Release(handle); | ||||
throw new NoAudioHardwareException(); | ||||
} | ||||
rendererDetails = new RendererDetail[rendererCount]; | ||||
byte[] converted = new byte[0xFF * sizeof(short)]; | ||||
for (ushort i = 0; i < rendererCount; i += 1) | ||||
{ | ||||
FAudio.FACTRendererDetails details; | ||||
FAudio.FACTAudioEngine_GetRendererDetails( | ||||
handle, | ||||
i, | ||||
out details | ||||
); | ||||
unsafe | ||||
{ | ||||
Marshal.Copy((IntPtr) details.displayName, converted, 0, converted.Length); | ||||
string name = System.Text.Encoding.Unicode.GetString(converted).TrimEnd('\0'); | ||||
Marshal.Copy((IntPtr) details.rendererID, converted, 0, converted.Length); | ||||
string id = System.Text.Encoding.Unicode.GetString(converted).TrimEnd('\0'); | ||||
rendererDetails[i] = new RendererDetail(name, id); | ||||
} | ||||
} | ||||
// Read entire file into memory, let FACT manage the pointer | ||||
IntPtr bufferLen; | ||||
IntPtr buffer = TitleContainer.ReadToPointer(settingsFile, out bufferLen); | ||||
r0 | ||||
// Generate engine parameters | ||||
FAudio.FACTRuntimeParameters settings = new FAudio.FACTRuntimeParameters(); | ||||
r690 | settings.pGlobalSettingsBuffer = buffer; | |||
settings.globalSettingsBufferSize = (uint) bufferLen; | ||||
settings.globalSettingsFlags = FAudio.FACT_FLAG_MANAGEDATA; | ||||
r0 | 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 | ||||
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); | ||||
} | ||||
// 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(); | ||||
r690 | notificationDesc.flags = FAudio.FACT_FLAG_NOTIFICATION_PERSIST; | |||
notificationDesc.type = FAudio.FACTNOTIFICATIONTYPE_WAVEBANKDESTROYED; | ||||
FAudio.FACTAudioEngine_RegisterNotification( | ||||
handle, | ||||
ref notificationDesc | ||||
); | ||||
notificationDesc.type = FAudio.FACTNOTIFICATIONTYPE_SOUNDBANKDESTROYED; | ||||
FAudio.FACTAudioEngine_RegisterNotification( | ||||
handle, | ||||
ref notificationDesc | ||||
); | ||||
notificationDesc.type = FAudio.FACTNOTIFICATIONTYPE_CUEDESTROYED; | ||||
FAudio.FACTAudioEngine_RegisterNotification( | ||||
handle, | ||||
ref notificationDesc | ||||
); | ||||
r0 | } | |||
#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); | ||||
r690 | FAudio.FACTAudioEngine_Release(handle); | |||
r0 | rendererDetails = null; | |||
IsDisposed = true; | ||||
} | ||||
} | ||||
} | ||||
#endregion | ||||
#region Internal Methods | ||||
r690 | internal void RegisterPointer( | |||
r0 | IntPtr ptr, | |||
WeakReference reference | ||||
) { | ||||
lock (xactPtrs) | ||||
{ | ||||
r690 | xactPtrs[ptr] = reference; | |||
r0 | } | |||
} | ||||
#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 | ||||
} | ||||
} | ||||