MoonWorks/src/Audio/AudioDevice.cs

294 lines
6.4 KiB
C#
Raw Normal View History

2022-02-23 05:14:32 +00:00
using System;
using System.Collections.Generic;
2023-05-12 00:56:40 +00:00
using System.Runtime.InteropServices;
using System.Threading;
2021-01-20 02:06:10 +00:00
namespace MoonWorks.Audio
{
2022-02-23 05:14:32 +00:00
public class AudioDevice : IDisposable
{
public IntPtr Handle { get; }
public byte[] Handle3D { get; }
public IntPtr MasteringVoice { get; }
public FAudio.FAudioDeviceDetails DeviceDetails { get; }
public float CurveDistanceScalar = 1f;
public float DopplerScale = 1f;
public float SpeedOfSound = 343.5f;
private float masteringVolume = 1f;
public float MasteringVolume
{
get => masteringVolume;
set
{
masteringVolume = value;
FAudio.FAudioVoice_SetVolume(MasteringVoice, masteringVolume, 0);
}
}
private readonly HashSet<WeakReference> resources = new HashSet<WeakReference>();
private readonly List<StreamingSound> autoUpdateStreamingSoundReferences = new List<StreamingSound>();
private readonly List<StaticSoundInstance> autoFreeStaticSoundInstanceReferences = new List<StaticSoundInstance>();
private AudioTweenManager AudioTweenManager;
private const int Step = 200;
private TimeSpan UpdateInterval;
private System.Diagnostics.Stopwatch TickStopwatch = new System.Diagnostics.Stopwatch();
private long previousTickTime;
private Thread Thread;
private AutoResetEvent WakeSignal;
internal readonly object StateLock = new object();
2022-02-23 05:14:32 +00:00
private bool IsDisposed;
public unsafe AudioDevice()
{
UpdateInterval = TimeSpan.FromTicks(TimeSpan.TicksPerSecond / Step);
FAudio.FAudioCreate(out var handle, 0, FAudio.FAUDIO_DEFAULT_PROCESSOR);
2022-02-23 05:14:32 +00:00
Handle = handle;
/* Find a suitable device */
FAudio.FAudio_GetDeviceCount(Handle, out var devices);
if (devices == 0)
{
Logger.LogError("No audio devices found!");
FAudio.FAudio_Release(Handle);
2022-11-30 17:43:49 +00:00
Handle = IntPtr.Zero;
2022-02-23 05:14:32 +00:00
return;
}
FAudio.FAudioDeviceDetails deviceDetails;
uint i = 0;
for (i = 0; i < devices; i++)
{
FAudio.FAudio_GetDeviceDetails(
Handle,
i,
out deviceDetails
);
if ((deviceDetails.Role & FAudio.FAudioDeviceRole.FAudioDefaultGameDevice) == FAudio.FAudioDeviceRole.FAudioDefaultGameDevice)
{
DeviceDetails = deviceDetails;
break;
}
}
if (i == devices)
{
i = 0; /* whatever we'll just use the first one I guess */
FAudio.FAudio_GetDeviceDetails(
Handle,
i,
out deviceDetails
);
DeviceDetails = deviceDetails;
}
/* Init Mastering Voice */
IntPtr masteringVoice;
if (FAudio.FAudio_CreateMasteringVoice(
Handle,
out masteringVoice,
FAudio.FAUDIO_DEFAULT_CHANNELS,
FAudio.FAUDIO_DEFAULT_SAMPLERATE,
0,
i,
IntPtr.Zero
) != 0)
{
Logger.LogError("No mastering voice found!");
FAudio.FAudio_Release(Handle);
Handle = IntPtr.Zero;
2022-02-23 05:14:32 +00:00
return;
}
MasteringVoice = masteringVoice;
/* Init 3D Audio */
Handle3D = new byte[FAudio.F3DAUDIO_HANDLE_BYTESIZE];
FAudio.F3DAudioInitialize(
DeviceDetails.OutputFormat.dwChannelMask,
SpeedOfSound,
Handle3D
);
AudioTweenManager = new AudioTweenManager();
Logger.LogInfo("Setting up audio thread...");
WakeSignal = new AutoResetEvent(true);
Thread = new Thread(ThreadMain);
Thread.IsBackground = true;
Thread.Start();
TickStopwatch.Start();
previousTickTime = 0;
2022-02-23 05:14:32 +00:00
}
private void ThreadMain()
2022-02-23 05:14:32 +00:00
{
while (!IsDisposed)
2022-02-23 05:14:32 +00:00
{
lock (StateLock)
{
ThreadMainTick();
}
WakeSignal.WaitOne(UpdateInterval);
}
}
private void ThreadMainTick()
{
long tickDelta = TickStopwatch.Elapsed.Ticks - previousTickTime;
previousTickTime = TickStopwatch.Elapsed.Ticks;
float elapsedSeconds = (float) tickDelta / System.TimeSpan.TicksPerSecond;
2023-05-12 00:56:40 +00:00
for (var i = autoUpdateStreamingSoundReferences.Count - 1; i >= 0; i -= 1)
{
var streamingSound = autoUpdateStreamingSoundReferences[i];
2023-05-12 00:56:40 +00:00
if (streamingSound.Loaded)
2022-02-23 05:14:32 +00:00
{
streamingSound.Update();
}
else
{
2023-05-12 00:56:40 +00:00
autoUpdateStreamingSoundReferences.RemoveAt(i);
}
}
for (var i = autoFreeStaticSoundInstanceReferences.Count - 1; i >= 0; i -= 1)
{
var staticSoundInstance = autoFreeStaticSoundInstanceReferences[i];
2023-05-12 00:56:40 +00:00
if (staticSoundInstance.State == SoundState.Stopped)
2023-05-12 00:56:40 +00:00
{
staticSoundInstance.Free();
2023-05-12 00:56:40 +00:00
autoFreeStaticSoundInstanceReferences.RemoveAt(i);
2022-02-23 05:14:32 +00:00
}
}
AudioTweenManager.Update(elapsedSeconds);
2022-02-23 05:14:32 +00:00
}
public void SyncPlay()
{
FAudio.FAudio_CommitChanges(Handle, 1);
}
internal void CreateTween(
SoundInstance soundInstance,
AudioTweenProperty property,
System.Func<float, float> easingFunction,
float start,
float end,
float duration,
float delayTime
) {
lock (StateLock)
{
AudioTweenManager.CreateTween(
soundInstance,
property,
easingFunction,
start,
end,
duration,
delayTime
);
}
}
internal void ClearTweens(
SoundInstance soundReference,
AudioTweenProperty property
) {
lock (StateLock)
{
AudioTweenManager.ClearTweens(soundReference, property);
}
}
internal void WakeThread()
2022-02-23 05:14:32 +00:00
{
WakeSignal.Set();
2022-02-23 05:14:32 +00:00
}
internal void AddResourceReference(AudioResource resource)
2022-02-23 05:14:32 +00:00
{
lock (StateLock)
2022-02-23 05:14:32 +00:00
{
resources.Add(resource.weakReference);
2022-02-23 05:14:32 +00:00
}
}
internal void RemoveResourceReference(AudioResource resource)
2022-02-23 05:14:32 +00:00
{
lock (StateLock)
2022-02-23 05:14:32 +00:00
{
resources.Remove(resource.weakReference);
2022-02-23 05:14:32 +00:00
}
}
2023-05-12 00:56:40 +00:00
internal void AddAutoUpdateStreamingSoundInstance(StreamingSound instance)
{
autoUpdateStreamingSoundReferences.Add(instance);
}
2023-05-12 00:56:40 +00:00
internal void AddAutoFreeStaticSoundInstance(StaticSoundInstance instance)
{
autoFreeStaticSoundInstanceReferences.Add(instance);
}
2022-02-23 05:14:32 +00:00
protected virtual void Dispose(bool disposing)
{
if (!IsDisposed)
{
lock (StateLock)
2022-02-23 05:14:32 +00:00
{
if (disposing)
2022-02-23 00:44:39 +00:00
{
foreach (var weakReference in resources)
2022-02-23 05:14:32 +00:00
{
var target = weakReference.Target;
if (target != null)
{
(target as IDisposable).Dispose();
}
2022-02-23 05:14:32 +00:00
}
resources.Clear();
2022-02-23 00:44:39 +00:00
}
2021-01-29 02:01:42 +00:00
FAudio.FAudioVoice_DestroyVoice(MasteringVoice);
FAudio.FAudio_Release(Handle);
2022-02-23 05:14:32 +00:00
IsDisposed = true;
}
2022-02-23 05:14:32 +00:00
}
}
~AudioDevice()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: false);
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
2021-01-20 02:06:10 +00:00
}