2022-02-23 05:14:32 +00:00
|
|
|
|
using System;
|
2021-01-20 05:33:25 +00:00
|
|
|
|
using System.Collections.Generic;
|
2021-01-20 02:06:10 +00:00
|
|
|
|
using System.Runtime.InteropServices;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
2022-10-20 22:00:25 +00:00
|
|
|
|
private float masteringVolume = 1f;
|
|
|
|
|
public float MasteringVolume
|
|
|
|
|
{
|
|
|
|
|
get => masteringVolume;
|
|
|
|
|
set
|
|
|
|
|
{
|
|
|
|
|
masteringVolume = value;
|
|
|
|
|
FAudio.FAudioVoice_SetVolume(MasteringVoice, masteringVolume, 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-23 05:14:32 +00:00
|
|
|
|
private readonly List<WeakReference<AudioResource>> resources = new List<WeakReference<AudioResource>>();
|
|
|
|
|
private readonly List<WeakReference<StreamingSound>> streamingSounds = new List<WeakReference<StreamingSound>>();
|
|
|
|
|
|
|
|
|
|
private bool IsDisposed;
|
|
|
|
|
|
|
|
|
|
public unsafe AudioDevice()
|
|
|
|
|
{
|
2022-08-30 17:09:32 +00:00
|
|
|
|
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!");
|
|
|
|
|
Handle = IntPtr.Zero;
|
|
|
|
|
FAudio.FAudio_Release(Handle);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MasteringVoice = masteringVoice;
|
|
|
|
|
|
|
|
|
|
/* Init 3D Audio */
|
|
|
|
|
|
|
|
|
|
Handle3D = new byte[FAudio.F3DAUDIO_HANDLE_BYTESIZE];
|
|
|
|
|
FAudio.F3DAudioInitialize(
|
|
|
|
|
DeviceDetails.OutputFormat.dwChannelMask,
|
|
|
|
|
SpeedOfSound,
|
|
|
|
|
Handle3D
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-24 02:11:48 +00:00
|
|
|
|
internal void Update()
|
2022-02-23 05:14:32 +00:00
|
|
|
|
{
|
|
|
|
|
for (var i = streamingSounds.Count - 1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
var weakReference = streamingSounds[i];
|
|
|
|
|
if (weakReference.TryGetTarget(out var streamingSound))
|
|
|
|
|
{
|
|
|
|
|
streamingSound.Update();
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
streamingSounds.RemoveAt(i);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-16 23:12:35 +00:00
|
|
|
|
public void SyncPlay()
|
|
|
|
|
{
|
|
|
|
|
FAudio.FAudio_CommitChanges(Handle, 1);
|
|
|
|
|
}
|
|
|
|
|
|
2022-02-23 05:14:32 +00:00
|
|
|
|
internal void AddDynamicSoundInstance(StreamingSound instance)
|
|
|
|
|
{
|
|
|
|
|
streamingSounds.Add(new WeakReference<StreamingSound>(instance));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void AddResourceReference(WeakReference<AudioResource> resourceReference)
|
|
|
|
|
{
|
|
|
|
|
lock (resources)
|
|
|
|
|
{
|
|
|
|
|
resources.Add(resourceReference);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
internal void RemoveResourceReference(WeakReference<AudioResource> resourceReference)
|
|
|
|
|
{
|
|
|
|
|
lock (resources)
|
|
|
|
|
{
|
|
|
|
|
resources.Remove(resourceReference);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
|
|
|
{
|
|
|
|
|
if (!IsDisposed)
|
|
|
|
|
{
|
|
|
|
|
if (disposing)
|
|
|
|
|
{
|
2022-04-20 21:57:24 +00:00
|
|
|
|
for (var i = resources.Count - 1; i >= 0; i--)
|
2022-02-23 00:44:39 +00:00
|
|
|
|
{
|
2022-04-20 21:57:24 +00:00
|
|
|
|
var weakReference = resources[i];
|
2022-02-23 00:44:39 +00:00
|
|
|
|
|
2022-04-20 21:57:24 +00:00
|
|
|
|
if (weakReference.TryGetTarget(out var resource))
|
2022-02-23 05:14:32 +00:00
|
|
|
|
{
|
2022-04-20 21:57:24 +00:00
|
|
|
|
resource.Dispose();
|
2022-02-23 05:14:32 +00:00
|
|
|
|
}
|
2022-02-23 00:44:39 +00:00
|
|
|
|
}
|
2022-04-20 21:57:24 +00:00
|
|
|
|
resources.Clear();
|
2022-02-23 05:14:32 +00:00
|
|
|
|
}
|
2021-01-29 02:01:42 +00:00
|
|
|
|
|
2022-02-23 00:44:39 +00:00
|
|
|
|
FAudio.FAudioVoice_DestroyVoice(MasteringVoice);
|
2022-02-23 05:14:32 +00:00
|
|
|
|
FAudio.FAudio_Release(Handle);
|
|
|
|
|
|
|
|
|
|
IsDisposed = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
|
|
|
|
~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
|
|
|
|
}
|