Formatting pass
parent
7a754e1c90
commit
26dc361d31
444
AudioDevice.cs
444
AudioDevice.cs
|
@ -1,277 +1,277 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public class AudioDevice : IDisposable
|
public class AudioDevice : IDisposable
|
||||||
{
|
{
|
||||||
public IntPtr Handle { get; }
|
public IntPtr Handle { get; }
|
||||||
public byte[] Handle3D { get; }
|
public byte[] Handle3D { get; }
|
||||||
public IntPtr MasteringVoice { get; }
|
public IntPtr MasteringVoice { get; }
|
||||||
public FAudio.FAudioDeviceDetails DeviceDetails { get; }
|
public FAudio.FAudioDeviceDetails DeviceDetails { get; }
|
||||||
public IntPtr ReverbVoice { get; }
|
public IntPtr ReverbVoice { get; }
|
||||||
|
|
||||||
public float CurveDistanceScalar = 1f;
|
public float CurveDistanceScalar = 1f;
|
||||||
public float DopplerScale = 1f;
|
public float DopplerScale = 1f;
|
||||||
public float SpeedOfSound = 343.5f;
|
public float SpeedOfSound = 343.5f;
|
||||||
|
|
||||||
internal FAudio.FAudioVoiceSends ReverbSends;
|
internal FAudio.FAudioVoiceSends ReverbSends;
|
||||||
|
|
||||||
private readonly List<WeakReference<AudioResource>> resources = new List<WeakReference<AudioResource>>();
|
private readonly List<WeakReference<AudioResource>> resources = new List<WeakReference<AudioResource>>();
|
||||||
private readonly List<WeakReference<StreamingSound>> streamingSounds = new List<WeakReference<StreamingSound>>();
|
private readonly List<WeakReference<StreamingSound>> streamingSounds = new List<WeakReference<StreamingSound>>();
|
||||||
|
|
||||||
private bool IsDisposed;
|
private bool IsDisposed;
|
||||||
|
|
||||||
public unsafe AudioDevice()
|
public unsafe AudioDevice()
|
||||||
{
|
{
|
||||||
FAudio.FAudioCreate(out var handle, 0, 0);
|
FAudio.FAudioCreate(out var handle, 0, 0);
|
||||||
Handle = handle;
|
Handle = handle;
|
||||||
|
|
||||||
/* Find a suitable device */
|
/* Find a suitable device */
|
||||||
|
|
||||||
FAudio.FAudio_GetDeviceCount(Handle, out var devices);
|
FAudio.FAudio_GetDeviceCount(Handle, out var devices);
|
||||||
|
|
||||||
if (devices == 0)
|
if (devices == 0)
|
||||||
{
|
{
|
||||||
Logger.LogError("No audio devices found!");
|
Logger.LogError("No audio devices found!");
|
||||||
Handle = IntPtr.Zero;
|
Handle = IntPtr.Zero;
|
||||||
FAudio.FAudio_Release(Handle);
|
FAudio.FAudio_Release(Handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudio.FAudioDeviceDetails deviceDetails;
|
FAudio.FAudioDeviceDetails deviceDetails;
|
||||||
|
|
||||||
uint i = 0;
|
uint i = 0;
|
||||||
for (i = 0; i < devices; i++)
|
for (i = 0; i < devices; i++)
|
||||||
{
|
{
|
||||||
FAudio.FAudio_GetDeviceDetails(
|
FAudio.FAudio_GetDeviceDetails(
|
||||||
Handle,
|
Handle,
|
||||||
i,
|
i,
|
||||||
out deviceDetails
|
out deviceDetails
|
||||||
);
|
);
|
||||||
if ((deviceDetails.Role & FAudio.FAudioDeviceRole.FAudioDefaultGameDevice) == FAudio.FAudioDeviceRole.FAudioDefaultGameDevice)
|
if ((deviceDetails.Role & FAudio.FAudioDeviceRole.FAudioDefaultGameDevice) == FAudio.FAudioDeviceRole.FAudioDefaultGameDevice)
|
||||||
{
|
{
|
||||||
DeviceDetails = deviceDetails;
|
DeviceDetails = deviceDetails;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (i == devices)
|
if (i == devices)
|
||||||
{
|
{
|
||||||
i = 0; /* whatever we'll just use the first one I guess */
|
i = 0; /* whatever we'll just use the first one I guess */
|
||||||
FAudio.FAudio_GetDeviceDetails(
|
FAudio.FAudio_GetDeviceDetails(
|
||||||
Handle,
|
Handle,
|
||||||
i,
|
i,
|
||||||
out deviceDetails
|
out deviceDetails
|
||||||
);
|
);
|
||||||
DeviceDetails = deviceDetails;
|
DeviceDetails = deviceDetails;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init Mastering Voice */
|
/* Init Mastering Voice */
|
||||||
IntPtr masteringVoice;
|
IntPtr masteringVoice;
|
||||||
|
|
||||||
if (FAudio.FAudio_CreateMasteringVoice(
|
if (FAudio.FAudio_CreateMasteringVoice(
|
||||||
Handle,
|
Handle,
|
||||||
out masteringVoice,
|
out masteringVoice,
|
||||||
FAudio.FAUDIO_DEFAULT_CHANNELS,
|
FAudio.FAUDIO_DEFAULT_CHANNELS,
|
||||||
FAudio.FAUDIO_DEFAULT_SAMPLERATE,
|
FAudio.FAUDIO_DEFAULT_SAMPLERATE,
|
||||||
0,
|
0,
|
||||||
i,
|
i,
|
||||||
IntPtr.Zero
|
IntPtr.Zero
|
||||||
) != 0)
|
) != 0)
|
||||||
{
|
{
|
||||||
Logger.LogError("No mastering voice found!");
|
Logger.LogError("No mastering voice found!");
|
||||||
Handle = IntPtr.Zero;
|
Handle = IntPtr.Zero;
|
||||||
FAudio.FAudio_Release(Handle);
|
FAudio.FAudio_Release(Handle);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MasteringVoice = masteringVoice;
|
MasteringVoice = masteringVoice;
|
||||||
|
|
||||||
/* Init 3D Audio */
|
/* Init 3D Audio */
|
||||||
|
|
||||||
Handle3D = new byte[FAudio.F3DAUDIO_HANDLE_BYTESIZE];
|
Handle3D = new byte[FAudio.F3DAUDIO_HANDLE_BYTESIZE];
|
||||||
FAudio.F3DAudioInitialize(
|
FAudio.F3DAudioInitialize(
|
||||||
DeviceDetails.OutputFormat.dwChannelMask,
|
DeviceDetails.OutputFormat.dwChannelMask,
|
||||||
SpeedOfSound,
|
SpeedOfSound,
|
||||||
Handle3D
|
Handle3D
|
||||||
);
|
);
|
||||||
|
|
||||||
/* Init reverb */
|
/* Init reverb */
|
||||||
|
|
||||||
IntPtr reverbVoice;
|
IntPtr reverbVoice;
|
||||||
|
|
||||||
IntPtr reverb;
|
IntPtr reverb;
|
||||||
FAudio.FAudioCreateReverb(out reverb, 0);
|
FAudio.FAudioCreateReverb(out reverb, 0);
|
||||||
|
|
||||||
IntPtr chainPtr;
|
IntPtr chainPtr;
|
||||||
chainPtr = Marshal.AllocHGlobal(
|
chainPtr = Marshal.AllocHGlobal(
|
||||||
Marshal.SizeOf<FAudio.FAudioEffectChain>()
|
Marshal.SizeOf<FAudio.FAudioEffectChain>()
|
||||||
);
|
);
|
||||||
|
|
||||||
FAudio.FAudioEffectChain* reverbChain = (FAudio.FAudioEffectChain*) chainPtr;
|
FAudio.FAudioEffectChain* reverbChain = (FAudio.FAudioEffectChain*) chainPtr;
|
||||||
reverbChain->EffectCount = 1;
|
reverbChain->EffectCount = 1;
|
||||||
reverbChain->pEffectDescriptors = Marshal.AllocHGlobal(
|
reverbChain->pEffectDescriptors = Marshal.AllocHGlobal(
|
||||||
Marshal.SizeOf<FAudio.FAudioEffectDescriptor>()
|
Marshal.SizeOf<FAudio.FAudioEffectDescriptor>()
|
||||||
);
|
);
|
||||||
|
|
||||||
FAudio.FAudioEffectDescriptor* reverbDescriptor =
|
FAudio.FAudioEffectDescriptor* reverbDescriptor =
|
||||||
(FAudio.FAudioEffectDescriptor*) reverbChain->pEffectDescriptors;
|
(FAudio.FAudioEffectDescriptor*) reverbChain->pEffectDescriptors;
|
||||||
|
|
||||||
reverbDescriptor->InitialState = 1;
|
reverbDescriptor->InitialState = 1;
|
||||||
reverbDescriptor->OutputChannels = (uint) (
|
reverbDescriptor->OutputChannels = (uint) (
|
||||||
(DeviceDetails.OutputFormat.Format.nChannels == 6) ? 6 : 1
|
(DeviceDetails.OutputFormat.Format.nChannels == 6) ? 6 : 1
|
||||||
);
|
);
|
||||||
reverbDescriptor->pEffect = reverb;
|
reverbDescriptor->pEffect = reverb;
|
||||||
|
|
||||||
FAudio.FAudio_CreateSubmixVoice(
|
FAudio.FAudio_CreateSubmixVoice(
|
||||||
Handle,
|
Handle,
|
||||||
out reverbVoice,
|
out reverbVoice,
|
||||||
1, /* omnidirectional reverb */
|
1, /* omnidirectional reverb */
|
||||||
DeviceDetails.OutputFormat.Format.nSamplesPerSec,
|
DeviceDetails.OutputFormat.Format.nSamplesPerSec,
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
IntPtr.Zero,
|
IntPtr.Zero,
|
||||||
chainPtr
|
chainPtr
|
||||||
);
|
);
|
||||||
FAudio.FAPOBase_Release(reverb);
|
FAudio.FAPOBase_Release(reverb);
|
||||||
|
|
||||||
Marshal.FreeHGlobal(reverbChain->pEffectDescriptors);
|
Marshal.FreeHGlobal(reverbChain->pEffectDescriptors);
|
||||||
Marshal.FreeHGlobal(chainPtr);
|
Marshal.FreeHGlobal(chainPtr);
|
||||||
|
|
||||||
ReverbVoice = reverbVoice;
|
ReverbVoice = reverbVoice;
|
||||||
|
|
||||||
/* Init reverb params */
|
/* Init reverb params */
|
||||||
// Defaults based on FAUDIOFX_I3DL2_PRESET_GENERIC
|
// Defaults based on FAUDIOFX_I3DL2_PRESET_GENERIC
|
||||||
|
|
||||||
IntPtr reverbParamsPtr = Marshal.AllocHGlobal(
|
IntPtr reverbParamsPtr = Marshal.AllocHGlobal(
|
||||||
Marshal.SizeOf<FAudio.FAudioFXReverbParameters>()
|
Marshal.SizeOf<FAudio.FAudioFXReverbParameters>()
|
||||||
);
|
);
|
||||||
|
|
||||||
FAudio.FAudioFXReverbParameters* reverbParams = (FAudio.FAudioFXReverbParameters*) reverbParamsPtr;
|
FAudio.FAudioFXReverbParameters* reverbParams = (FAudio.FAudioFXReverbParameters*) reverbParamsPtr;
|
||||||
reverbParams->WetDryMix = 100.0f;
|
reverbParams->WetDryMix = 100.0f;
|
||||||
reverbParams->ReflectionsDelay = 7;
|
reverbParams->ReflectionsDelay = 7;
|
||||||
reverbParams->ReverbDelay = 11;
|
reverbParams->ReverbDelay = 11;
|
||||||
reverbParams->RearDelay = FAudio.FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
|
reverbParams->RearDelay = FAudio.FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
|
||||||
reverbParams->PositionLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION;
|
reverbParams->PositionLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION;
|
||||||
reverbParams->PositionRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION;
|
reverbParams->PositionRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION;
|
||||||
reverbParams->PositionMatrixLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
|
reverbParams->PositionMatrixLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
|
||||||
reverbParams->PositionMatrixRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
|
reverbParams->PositionMatrixRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
|
||||||
reverbParams->EarlyDiffusion = 15;
|
reverbParams->EarlyDiffusion = 15;
|
||||||
reverbParams->LateDiffusion = 15;
|
reverbParams->LateDiffusion = 15;
|
||||||
reverbParams->LowEQGain = 8;
|
reverbParams->LowEQGain = 8;
|
||||||
reverbParams->LowEQCutoff = 4;
|
reverbParams->LowEQCutoff = 4;
|
||||||
reverbParams->HighEQGain = 8;
|
reverbParams->HighEQGain = 8;
|
||||||
reverbParams->HighEQCutoff = 6;
|
reverbParams->HighEQCutoff = 6;
|
||||||
reverbParams->RoomFilterFreq = 5000f;
|
reverbParams->RoomFilterFreq = 5000f;
|
||||||
reverbParams->RoomFilterMain = -10f;
|
reverbParams->RoomFilterMain = -10f;
|
||||||
reverbParams->RoomFilterHF = -1f;
|
reverbParams->RoomFilterHF = -1f;
|
||||||
reverbParams->ReflectionsGain = -26.0200005f;
|
reverbParams->ReflectionsGain = -26.0200005f;
|
||||||
reverbParams->ReverbGain = 10.0f;
|
reverbParams->ReverbGain = 10.0f;
|
||||||
reverbParams->DecayTime = 1.49000001f;
|
reverbParams->DecayTime = 1.49000001f;
|
||||||
reverbParams->Density = 100.0f;
|
reverbParams->Density = 100.0f;
|
||||||
reverbParams->RoomSize = FAudio.FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
|
reverbParams->RoomSize = FAudio.FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
|
||||||
FAudio.FAudioVoice_SetEffectParameters(
|
FAudio.FAudioVoice_SetEffectParameters(
|
||||||
ReverbVoice,
|
ReverbVoice,
|
||||||
0,
|
0,
|
||||||
reverbParamsPtr,
|
reverbParamsPtr,
|
||||||
(uint) Marshal.SizeOf<FAudio.FAudioFXReverbParameters>(),
|
(uint) Marshal.SizeOf<FAudio.FAudioFXReverbParameters>(),
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
Marshal.FreeHGlobal(reverbParamsPtr);
|
Marshal.FreeHGlobal(reverbParamsPtr);
|
||||||
|
|
||||||
/* Init reverb sends */
|
/* Init reverb sends */
|
||||||
|
|
||||||
ReverbSends = new FAudio.FAudioVoiceSends
|
ReverbSends = new FAudio.FAudioVoiceSends
|
||||||
{
|
{
|
||||||
SendCount = 2,
|
SendCount = 2,
|
||||||
pSends = Marshal.AllocHGlobal(
|
pSends = Marshal.AllocHGlobal(
|
||||||
2 * Marshal.SizeOf<FAudio.FAudioSendDescriptor>()
|
2 * Marshal.SizeOf<FAudio.FAudioSendDescriptor>()
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
FAudio.FAudioSendDescriptor* sendDesc = (FAudio.FAudioSendDescriptor*) ReverbSends.pSends;
|
FAudio.FAudioSendDescriptor* sendDesc = (FAudio.FAudioSendDescriptor*) ReverbSends.pSends;
|
||||||
sendDesc[0].Flags = 0;
|
sendDesc[0].Flags = 0;
|
||||||
sendDesc[0].pOutputVoice = MasteringVoice;
|
sendDesc[0].pOutputVoice = MasteringVoice;
|
||||||
sendDesc[1].Flags = 0;
|
sendDesc[1].Flags = 0;
|
||||||
sendDesc[1].pOutputVoice = ReverbVoice;
|
sendDesc[1].pOutputVoice = ReverbVoice;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
for (var i = streamingSounds.Count - 1; i >= 0; i--)
|
for (var i = streamingSounds.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var weakReference = streamingSounds[i];
|
var weakReference = streamingSounds[i];
|
||||||
if (weakReference.TryGetTarget(out var streamingSound))
|
if (weakReference.TryGetTarget(out var streamingSound))
|
||||||
{
|
{
|
||||||
streamingSound.Update();
|
streamingSound.Update();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
streamingSounds.RemoveAt(i);
|
streamingSounds.RemoveAt(i);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddDynamicSoundInstance(StreamingSound instance)
|
internal void AddDynamicSoundInstance(StreamingSound instance)
|
||||||
{
|
{
|
||||||
streamingSounds.Add(new WeakReference<StreamingSound>(instance));
|
streamingSounds.Add(new WeakReference<StreamingSound>(instance));
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void AddResourceReference(WeakReference<AudioResource> resourceReference)
|
internal void AddResourceReference(WeakReference<AudioResource> resourceReference)
|
||||||
{
|
{
|
||||||
lock (resources)
|
lock (resources)
|
||||||
{
|
{
|
||||||
resources.Add(resourceReference);
|
resources.Add(resourceReference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void RemoveResourceReference(WeakReference<AudioResource> resourceReference)
|
internal void RemoveResourceReference(WeakReference<AudioResource> resourceReference)
|
||||||
{
|
{
|
||||||
lock (resources)
|
lock (resources)
|
||||||
{
|
{
|
||||||
resources.Remove(resourceReference);
|
resources.Remove(resourceReference);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!IsDisposed)
|
if (!IsDisposed)
|
||||||
{
|
{
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
for (var i = streamingSounds.Count - 1; i >= 0; i--)
|
for (var i = streamingSounds.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var weakReference = streamingSounds[i];
|
var weakReference = streamingSounds[i];
|
||||||
|
|
||||||
if (weakReference.TryGetTarget(out var streamingSound))
|
if (weakReference.TryGetTarget(out var streamingSound))
|
||||||
{
|
{
|
||||||
streamingSound.Dispose();
|
streamingSound.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
streamingSounds.Clear();
|
streamingSounds.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudio.FAudioVoice_DestroyVoice(ReverbVoice);
|
FAudio.FAudioVoice_DestroyVoice(ReverbVoice);
|
||||||
FAudio.FAudioVoice_DestroyVoice(MasteringVoice);
|
FAudio.FAudioVoice_DestroyVoice(MasteringVoice);
|
||||||
FAudio.FAudio_Release(Handle);
|
FAudio.FAudio_Release(Handle);
|
||||||
|
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
// TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources
|
||||||
~AudioDevice()
|
~AudioDevice()
|
||||||
{
|
{
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// 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: true);
|
Dispose(disposing: true);
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using MoonWorks.Math;
|
using MoonWorks.Math;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public class AudioEmitter : AudioResource
|
public class AudioEmitter : AudioResource
|
||||||
{
|
{
|
||||||
internal FAudio.F3DAUDIO_EMITTER emitterData;
|
internal FAudio.F3DAUDIO_EMITTER emitterData;
|
||||||
|
|
||||||
public float DopplerScale
|
public float DopplerScale
|
||||||
{
|
{
|
||||||
|
@ -107,17 +107,17 @@ namespace MoonWorks.Audio
|
||||||
GCHandleType.Pinned
|
GCHandleType.Pinned
|
||||||
);
|
);
|
||||||
|
|
||||||
public AudioEmitter(AudioDevice device) : base(device)
|
public AudioEmitter(AudioDevice device) : base(device)
|
||||||
{
|
{
|
||||||
emitterData = new FAudio.F3DAUDIO_EMITTER();
|
emitterData = new FAudio.F3DAUDIO_EMITTER();
|
||||||
|
|
||||||
DopplerScale = 1f;
|
DopplerScale = 1f;
|
||||||
Forward = Vector3.Forward;
|
Forward = Vector3.Forward;
|
||||||
Position = Vector3.Zero;
|
Position = Vector3.Zero;
|
||||||
Up = Vector3.Up;
|
Up = Vector3.Up;
|
||||||
Velocity = Vector3.Zero;
|
Velocity = Vector3.Zero;
|
||||||
|
|
||||||
/* Unexposed variables, defaults based on XNA behavior */
|
/* Unexposed variables, defaults based on XNA behavior */
|
||||||
emitterData.pCone = IntPtr.Zero;
|
emitterData.pCone = IntPtr.Zero;
|
||||||
emitterData.ChannelCount = 1;
|
emitterData.ChannelCount = 1;
|
||||||
emitterData.ChannelRadius = 1.0f;
|
emitterData.ChannelRadius = 1.0f;
|
||||||
|
@ -128,8 +128,8 @@ namespace MoonWorks.Audio
|
||||||
emitterData.pLPFReverbCurve = IntPtr.Zero;
|
emitterData.pLPFReverbCurve = IntPtr.Zero;
|
||||||
emitterData.pReverbCurve = IntPtr.Zero;
|
emitterData.pReverbCurve = IntPtr.Zero;
|
||||||
emitterData.CurveDistanceScaler = 1.0f;
|
emitterData.CurveDistanceScaler = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Destroy() { }
|
protected override void Destroy() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
using System;
|
using System;
|
||||||
using MoonWorks.Math;
|
using MoonWorks.Math;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public class AudioListener : AudioResource
|
public class AudioListener : AudioResource
|
||||||
{
|
{
|
||||||
internal FAudio.F3DAUDIO_LISTENER listenerData;
|
internal FAudio.F3DAUDIO_LISTENER listenerData;
|
||||||
|
|
||||||
public Vector3 Forward
|
public Vector3 Forward
|
||||||
{
|
{
|
||||||
|
@ -80,18 +80,18 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AudioListener(AudioDevice device) : base(device)
|
public AudioListener(AudioDevice device) : base(device)
|
||||||
{
|
{
|
||||||
listenerData = new FAudio.F3DAUDIO_LISTENER();
|
listenerData = new FAudio.F3DAUDIO_LISTENER();
|
||||||
Forward = Vector3.Forward;
|
Forward = Vector3.Forward;
|
||||||
Position = Vector3.Zero;
|
Position = Vector3.Zero;
|
||||||
Up = Vector3.Up;
|
Up = Vector3.Up;
|
||||||
Velocity = Vector3.Zero;
|
Velocity = Vector3.Zero;
|
||||||
|
|
||||||
/* 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() { }
|
protected override void Destroy() { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,52 +1,52 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public abstract class AudioResource : IDisposable
|
public abstract class AudioResource : IDisposable
|
||||||
{
|
{
|
||||||
public AudioDevice Device { get; }
|
public AudioDevice Device { get; }
|
||||||
|
|
||||||
public bool IsDisposed { get; private set; }
|
public bool IsDisposed { get; private set; }
|
||||||
|
|
||||||
private WeakReference<AudioResource> selfReference;
|
private WeakReference<AudioResource> selfReference;
|
||||||
|
|
||||||
public AudioResource(AudioDevice device)
|
public AudioResource(AudioDevice device)
|
||||||
{
|
{
|
||||||
Device = device;
|
Device = device;
|
||||||
|
|
||||||
selfReference = new WeakReference<AudioResource>(this);
|
selfReference = new WeakReference<AudioResource>(this);
|
||||||
Device.AddResourceReference(selfReference);
|
Device.AddResourceReference(selfReference);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void Destroy();
|
protected abstract void Destroy();
|
||||||
|
|
||||||
protected virtual void Dispose(bool disposing)
|
protected virtual void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
if (!IsDisposed)
|
if (!IsDisposed)
|
||||||
{
|
{
|
||||||
Destroy();
|
Destroy();
|
||||||
|
|
||||||
if (selfReference != null)
|
if (selfReference != null)
|
||||||
{
|
{
|
||||||
Device.RemoveResourceReference(selfReference);
|
Device.RemoveResourceReference(selfReference);
|
||||||
selfReference = null;
|
selfReference = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
~AudioResource()
|
~AudioResource()
|
||||||
{
|
{
|
||||||
// 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
// 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: true);
|
Dispose(disposing: true);
|
||||||
GC.SuppressFinalize(this);
|
GC.SuppressFinalize(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
486
SoundInstance.cs
486
SoundInstance.cs
|
@ -1,271 +1,271 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using MoonWorks.Math;
|
using MoonWorks.Math;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public abstract class SoundInstance : AudioResource
|
public abstract class SoundInstance : AudioResource
|
||||||
{
|
{
|
||||||
internal IntPtr Handle { get; }
|
internal IntPtr Handle { get; }
|
||||||
internal FAudio.FAudioWaveFormatEx Format { get; }
|
internal FAudio.FAudioWaveFormatEx Format { get; }
|
||||||
public bool Loop { get; }
|
public bool Loop { get; }
|
||||||
|
|
||||||
protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
|
protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
|
||||||
|
|
||||||
protected bool is3D;
|
protected bool is3D;
|
||||||
|
|
||||||
public abstract SoundState State { get; protected set; }
|
public abstract SoundState State { get; protected set; }
|
||||||
|
|
||||||
private float _pan = 0;
|
private float _pan = 0;
|
||||||
public float Pan
|
public float Pan
|
||||||
{
|
{
|
||||||
get => _pan;
|
get => _pan;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_pan = value;
|
_pan = value;
|
||||||
|
|
||||||
if (_pan < -1f)
|
if (_pan < -1f)
|
||||||
{
|
{
|
||||||
_pan = -1f;
|
_pan = -1f;
|
||||||
}
|
}
|
||||||
if (_pan > 1f)
|
if (_pan > 1f)
|
||||||
{
|
{
|
||||||
_pan = 1f;
|
_pan = 1f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is3D) { return; }
|
if (is3D) { return; }
|
||||||
|
|
||||||
SetPanMatrixCoefficients();
|
SetPanMatrixCoefficients();
|
||||||
FAudio.FAudioVoice_SetOutputMatrix(
|
FAudio.FAudioVoice_SetOutputMatrix(
|
||||||
Handle,
|
Handle,
|
||||||
Device.MasteringVoice,
|
Device.MasteringVoice,
|
||||||
dspSettings.SrcChannelCount,
|
dspSettings.SrcChannelCount,
|
||||||
dspSettings.DstChannelCount,
|
dspSettings.DstChannelCount,
|
||||||
dspSettings.pMatrixCoefficients,
|
dspSettings.pMatrixCoefficients,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float _pitch = 1;
|
private float _pitch = 1;
|
||||||
public float Pitch
|
public float Pitch
|
||||||
{
|
{
|
||||||
get => _pitch;
|
get => _pitch;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_pitch = MathHelper.Clamp(value, -1f, 1f);
|
_pitch = MathHelper.Clamp(value, -1f, 1f);
|
||||||
UpdatePitch();
|
UpdatePitch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float _volume = 1;
|
private float _volume = 1;
|
||||||
public float Volume
|
public float Volume
|
||||||
{
|
{
|
||||||
get => _volume;
|
get => _volume;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_volume = value;
|
_volume = value;
|
||||||
FAudio.FAudioVoice_SetVolume(Handle, _volume, 0);
|
FAudio.FAudioVoice_SetVolume(Handle, _volume, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float _reverb;
|
private float _reverb;
|
||||||
public unsafe float Reverb
|
public unsafe float Reverb
|
||||||
{
|
{
|
||||||
get => _reverb;
|
get => _reverb;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_reverb = value;
|
_reverb = value;
|
||||||
|
|
||||||
float* outputMatrix = (float*) dspSettings.pMatrixCoefficients;
|
float* outputMatrix = (float*) dspSettings.pMatrixCoefficients;
|
||||||
outputMatrix[0] = _reverb;
|
outputMatrix[0] = _reverb;
|
||||||
if (dspSettings.SrcChannelCount == 2)
|
if (dspSettings.SrcChannelCount == 2)
|
||||||
{
|
{
|
||||||
outputMatrix[1] = _reverb;
|
outputMatrix[1] = _reverb;
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudio.FAudioVoice_SetOutputMatrix(
|
FAudio.FAudioVoice_SetOutputMatrix(
|
||||||
Handle,
|
Handle,
|
||||||
Device.ReverbVoice,
|
Device.ReverbVoice,
|
||||||
dspSettings.SrcChannelCount,
|
dspSettings.SrcChannelCount,
|
||||||
1,
|
1,
|
||||||
dspSettings.pMatrixCoefficients,
|
dspSettings.pMatrixCoefficients,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float _lowPassFilter;
|
private float _lowPassFilter;
|
||||||
public float LowPassFilter
|
public float LowPassFilter
|
||||||
{
|
{
|
||||||
get => _lowPassFilter;
|
get => _lowPassFilter;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_lowPassFilter = value;
|
_lowPassFilter = value;
|
||||||
|
|
||||||
FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters
|
FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters
|
||||||
{
|
{
|
||||||
Type = FAudio.FAudioFilterType.FAudioLowPassFilter,
|
Type = FAudio.FAudioFilterType.FAudioLowPassFilter,
|
||||||
Frequency = _lowPassFilter,
|
Frequency = _lowPassFilter,
|
||||||
OneOverQ = 1f
|
OneOverQ = 1f
|
||||||
};
|
};
|
||||||
FAudio.FAudioVoice_SetFilterParameters(
|
FAudio.FAudioVoice_SetFilterParameters(
|
||||||
Handle,
|
Handle,
|
||||||
ref p,
|
ref p,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float _highPassFilter;
|
private float _highPassFilter;
|
||||||
public float HighPassFilter
|
public float HighPassFilter
|
||||||
{
|
{
|
||||||
get => _highPassFilter;
|
get => _highPassFilter;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_highPassFilter = value;
|
_highPassFilter = value;
|
||||||
|
|
||||||
FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters
|
FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters
|
||||||
{
|
{
|
||||||
Type = FAudio.FAudioFilterType.FAudioHighPassFilter,
|
Type = FAudio.FAudioFilterType.FAudioHighPassFilter,
|
||||||
Frequency = _highPassFilter,
|
Frequency = _highPassFilter,
|
||||||
OneOverQ = 1f
|
OneOverQ = 1f
|
||||||
};
|
};
|
||||||
FAudio.FAudioVoice_SetFilterParameters(
|
FAudio.FAudioVoice_SetFilterParameters(
|
||||||
Handle,
|
Handle,
|
||||||
ref p,
|
ref p,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private float _bandPassFilter;
|
private float _bandPassFilter;
|
||||||
public float BandPassFilter
|
public float BandPassFilter
|
||||||
{
|
{
|
||||||
get => _bandPassFilter;
|
get => _bandPassFilter;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_bandPassFilter = value;
|
_bandPassFilter = value;
|
||||||
|
|
||||||
FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters
|
FAudio.FAudioFilterParameters p = new FAudio.FAudioFilterParameters
|
||||||
{
|
{
|
||||||
Type = FAudio.FAudioFilterType.FAudioBandPassFilter,
|
Type = FAudio.FAudioFilterType.FAudioBandPassFilter,
|
||||||
Frequency = _bandPassFilter,
|
Frequency = _bandPassFilter,
|
||||||
OneOverQ = 1f
|
OneOverQ = 1f
|
||||||
};
|
};
|
||||||
FAudio.FAudioVoice_SetFilterParameters(
|
FAudio.FAudioVoice_SetFilterParameters(
|
||||||
Handle,
|
Handle,
|
||||||
ref p,
|
ref p,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoundInstance(
|
public SoundInstance(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
ushort channels,
|
ushort channels,
|
||||||
uint samplesPerSecond,
|
uint samplesPerSecond,
|
||||||
bool is3D,
|
bool is3D,
|
||||||
bool loop
|
bool loop
|
||||||
) : base(device)
|
) : base(device)
|
||||||
{
|
{
|
||||||
var blockAlign = (ushort)(4 * channels);
|
var blockAlign = (ushort) (4 * channels);
|
||||||
var format = new FAudio.FAudioWaveFormatEx
|
var format = new FAudio.FAudioWaveFormatEx
|
||||||
{
|
{
|
||||||
wFormatTag = 3,
|
wFormatTag = 3,
|
||||||
wBitsPerSample = 32,
|
wBitsPerSample = 32,
|
||||||
nChannels = channels,
|
nChannels = channels,
|
||||||
nBlockAlign = blockAlign,
|
nBlockAlign = blockAlign,
|
||||||
nSamplesPerSec = samplesPerSecond,
|
nSamplesPerSec = samplesPerSecond,
|
||||||
nAvgBytesPerSec = blockAlign * samplesPerSecond
|
nAvgBytesPerSec = blockAlign * samplesPerSecond
|
||||||
};
|
};
|
||||||
|
|
||||||
Format = format;
|
Format = format;
|
||||||
|
|
||||||
FAudio.FAudio_CreateSourceVoice(
|
FAudio.FAudio_CreateSourceVoice(
|
||||||
Device.Handle,
|
Device.Handle,
|
||||||
out var handle,
|
out var handle,
|
||||||
ref format,
|
ref format,
|
||||||
FAudio.FAUDIO_VOICE_USEFILTER,
|
FAudio.FAUDIO_VOICE_USEFILTER,
|
||||||
FAudio.FAUDIO_DEFAULT_FREQ_RATIO,
|
FAudio.FAUDIO_DEFAULT_FREQ_RATIO,
|
||||||
IntPtr.Zero,
|
IntPtr.Zero,
|
||||||
IntPtr.Zero,
|
IntPtr.Zero,
|
||||||
IntPtr.Zero
|
IntPtr.Zero
|
||||||
);
|
);
|
||||||
|
|
||||||
if (handle == IntPtr.Zero)
|
if (handle == IntPtr.Zero)
|
||||||
{
|
{
|
||||||
Logger.LogError("SoundInstance failed to initialize!");
|
Logger.LogError("SoundInstance failed to initialize!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Handle = handle;
|
Handle = handle;
|
||||||
this.is3D = is3D;
|
this.is3D = is3D;
|
||||||
InitDSPSettings(Format.nChannels);
|
InitDSPSettings(Format.nChannels);
|
||||||
|
|
||||||
FAudio.FAudioVoice_SetOutputVoices(
|
FAudio.FAudioVoice_SetOutputVoices(
|
||||||
handle,
|
handle,
|
||||||
ref Device.ReverbSends
|
ref Device.ReverbSends
|
||||||
);
|
);
|
||||||
|
|
||||||
Loop = loop;
|
Loop = loop;
|
||||||
State = SoundState.Stopped;
|
State = SoundState.Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Apply3D(AudioListener listener, AudioEmitter emitter)
|
public void Apply3D(AudioListener listener, AudioEmitter emitter)
|
||||||
{
|
{
|
||||||
is3D = true;
|
is3D = true;
|
||||||
|
|
||||||
emitter.emitterData.CurveDistanceScaler = Device.CurveDistanceScalar;
|
emitter.emitterData.CurveDistanceScaler = Device.CurveDistanceScalar;
|
||||||
emitter.emitterData.ChannelCount = dspSettings.SrcChannelCount;
|
emitter.emitterData.ChannelCount = dspSettings.SrcChannelCount;
|
||||||
|
|
||||||
FAudio.F3DAudioCalculate(
|
FAudio.F3DAudioCalculate(
|
||||||
Device.Handle3D,
|
Device.Handle3D,
|
||||||
ref listener.listenerData,
|
ref listener.listenerData,
|
||||||
ref emitter.emitterData,
|
ref emitter.emitterData,
|
||||||
FAudio.F3DAUDIO_CALCULATE_MATRIX | FAudio.F3DAUDIO_CALCULATE_DOPPLER,
|
FAudio.F3DAUDIO_CALCULATE_MATRIX | FAudio.F3DAUDIO_CALCULATE_DOPPLER,
|
||||||
ref dspSettings
|
ref dspSettings
|
||||||
);
|
);
|
||||||
|
|
||||||
UpdatePitch();
|
UpdatePitch();
|
||||||
FAudio.FAudioVoice_SetOutputMatrix(
|
FAudio.FAudioVoice_SetOutputMatrix(
|
||||||
Handle,
|
Handle,
|
||||||
Device.MasteringVoice,
|
Device.MasteringVoice,
|
||||||
dspSettings.SrcChannelCount,
|
dspSettings.SrcChannelCount,
|
||||||
dspSettings.DstChannelCount,
|
dspSettings.DstChannelCount,
|
||||||
dspSettings.pMatrixCoefficients,
|
dspSettings.pMatrixCoefficients,
|
||||||
0
|
0
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract void Play();
|
public abstract void Play();
|
||||||
public abstract void Pause();
|
public abstract void Pause();
|
||||||
public abstract void Stop(bool immediate);
|
public abstract void Stop(bool immediate);
|
||||||
|
|
||||||
private void InitDSPSettings(uint srcChannels)
|
private void InitDSPSettings(uint srcChannels)
|
||||||
{
|
{
|
||||||
dspSettings = new FAudio.F3DAUDIO_DSP_SETTINGS();
|
dspSettings = new FAudio.F3DAUDIO_DSP_SETTINGS();
|
||||||
dspSettings.DopplerFactor = 1f;
|
dspSettings.DopplerFactor = 1f;
|
||||||
dspSettings.SrcChannelCount = srcChannels;
|
dspSettings.SrcChannelCount = srcChannels;
|
||||||
dspSettings.DstChannelCount = Device.DeviceDetails.OutputFormat.Format.nChannels;
|
dspSettings.DstChannelCount = Device.DeviceDetails.OutputFormat.Format.nChannels;
|
||||||
|
|
||||||
int memsize = (
|
int memsize = (
|
||||||
4 *
|
4 *
|
||||||
(int) dspSettings.SrcChannelCount *
|
(int) dspSettings.SrcChannelCount *
|
||||||
(int) dspSettings.DstChannelCount
|
(int) dspSettings.DstChannelCount
|
||||||
);
|
);
|
||||||
|
|
||||||
dspSettings.pMatrixCoefficients = Marshal.AllocHGlobal(memsize);
|
dspSettings.pMatrixCoefficients = Marshal.AllocHGlobal(memsize);
|
||||||
unsafe
|
unsafe
|
||||||
{
|
{
|
||||||
byte* memPtr = (byte*) dspSettings.pMatrixCoefficients;
|
byte* memPtr = (byte*) dspSettings.pMatrixCoefficients;
|
||||||
for (int i = 0; i < memsize; i += 1)
|
for (int i = 0; i < memsize; i += 1)
|
||||||
{
|
{
|
||||||
memPtr[i] = 0;
|
memPtr[i] = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SetPanMatrixCoefficients();
|
SetPanMatrixCoefficients();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdatePitch()
|
private void UpdatePitch()
|
||||||
{
|
{
|
||||||
|
@ -287,10 +287,10 @@ namespace MoonWorks.Audio
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Taken from https://github.com/FNA-XNA/FNA/blob/master/src/Audio/SoundEffectInstance.cs
|
// Taken from https://github.com/FNA-XNA/FNA/blob/master/src/Audio/SoundEffectInstance.cs
|
||||||
private unsafe void SetPanMatrixCoefficients()
|
private unsafe void SetPanMatrixCoefficients()
|
||||||
{
|
{
|
||||||
/* Two major things to notice:
|
/* Two major things to notice:
|
||||||
* 1. The spec assumes any speaker count >= 2 has Front Left/Right.
|
* 1. The spec assumes any speaker count >= 2 has Front Left/Right.
|
||||||
* 2. Stereo panning is WAY more complicated than you think.
|
* 2. Stereo panning is WAY more complicated than you think.
|
||||||
* The main thing is that hard panning does NOT eliminate an
|
* The main thing is that hard panning does NOT eliminate an
|
||||||
|
@ -307,7 +307,7 @@ namespace MoonWorks.Audio
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
outputMatrix[0] = (_pan > 0.0f) ? (1.0f - _pan) : 1.0f;
|
outputMatrix[0] = (_pan > 0.0f) ? (1.0f - _pan) : 1.0f;
|
||||||
outputMatrix[1] = (_pan < 0.0f) ? (1.0f + _pan) : 1.0f;
|
outputMatrix[1] = (_pan < 0.0f) ? (1.0f + _pan) : 1.0f;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -339,14 +339,14 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
Stop(true);
|
Stop(true);
|
||||||
|
|
||||||
FAudio.FAudioVoice_DestroyVoice(Handle);
|
FAudio.FAudioVoice_DestroyVoice(Handle);
|
||||||
Marshal.FreeHGlobal(dspSettings.pMatrixCoefficients);
|
Marshal.FreeHGlobal(dspSettings.pMatrixCoefficients);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public enum SoundState
|
public enum SoundState
|
||||||
{
|
{
|
||||||
Playing,
|
Playing,
|
||||||
Paused,
|
Paused,
|
||||||
Stopped
|
Stopped
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
132
StaticSound.cs
132
StaticSound.cs
|
@ -1,82 +1,82 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public class StaticSound : AudioResource
|
public class StaticSound : AudioResource
|
||||||
{
|
{
|
||||||
internal FAudio.FAudioBuffer Handle;
|
internal FAudio.FAudioBuffer Handle;
|
||||||
public ushort Channels { get; }
|
public ushort Channels { get; }
|
||||||
public uint SamplesPerSecond { get; }
|
public uint SamplesPerSecond { get; }
|
||||||
|
|
||||||
public uint LoopStart { get; set; } = 0;
|
public uint LoopStart { get; set; } = 0;
|
||||||
public uint LoopLength { get; set; } = 0;
|
public uint LoopLength { get; set; } = 0;
|
||||||
|
|
||||||
public static StaticSound LoadOgg(AudioDevice device, string filePath)
|
public static StaticSound LoadOgg(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);
|
||||||
|
|
||||||
if (error != 0)
|
if (error != 0)
|
||||||
{
|
{
|
||||||
throw new AudioLoadException("Error loading file!");
|
throw new AudioLoadException("Error loading file!");
|
||||||
}
|
}
|
||||||
var info = FAudio.stb_vorbis_get_info(filePointer);
|
var info = FAudio.stb_vorbis_get_info(filePointer);
|
||||||
var bufferSize = FAudio.stb_vorbis_stream_length_in_samples(filePointer) * info.channels;
|
var bufferSize = FAudio.stb_vorbis_stream_length_in_samples(filePointer) * info.channels;
|
||||||
var buffer = new float[bufferSize];
|
var buffer = new float[bufferSize];
|
||||||
|
|
||||||
FAudio.stb_vorbis_get_samples_float_interleaved(
|
FAudio.stb_vorbis_get_samples_float_interleaved(
|
||||||
filePointer,
|
filePointer,
|
||||||
info.channels,
|
info.channels,
|
||||||
buffer,
|
buffer,
|
||||||
(int) bufferSize
|
(int) bufferSize
|
||||||
);
|
);
|
||||||
|
|
||||||
FAudio.stb_vorbis_close(filePointer);
|
FAudio.stb_vorbis_close(filePointer);
|
||||||
|
|
||||||
return new StaticSound(
|
return new StaticSound(
|
||||||
device,
|
device,
|
||||||
(ushort) info.channels,
|
(ushort) info.channels,
|
||||||
info.sample_rate,
|
info.sample_rate,
|
||||||
buffer,
|
buffer,
|
||||||
0,
|
0,
|
||||||
(uint) buffer.Length
|
(uint) buffer.Length
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public StaticSound(
|
public StaticSound(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
ushort channels,
|
ushort channels,
|
||||||
uint samplesPerSecond,
|
uint samplesPerSecond,
|
||||||
float[] buffer,
|
float[] buffer,
|
||||||
uint bufferOffset, /* in floats */
|
uint bufferOffset, /* in floats */
|
||||||
uint bufferLength /* in floats */
|
uint bufferLength /* in floats */
|
||||||
) : base(device)
|
) : base(device)
|
||||||
{
|
{
|
||||||
Channels = channels;
|
Channels = channels;
|
||||||
SamplesPerSecond = samplesPerSecond;
|
SamplesPerSecond = samplesPerSecond;
|
||||||
|
|
||||||
var bufferLengthInBytes = (int) (bufferLength * sizeof(float));
|
var bufferLengthInBytes = (int) (bufferLength * sizeof(float));
|
||||||
Handle = new FAudio.FAudioBuffer();
|
Handle = new FAudio.FAudioBuffer();
|
||||||
Handle.Flags = FAudio.FAUDIO_END_OF_STREAM;
|
Handle.Flags = FAudio.FAUDIO_END_OF_STREAM;
|
||||||
Handle.pContext = IntPtr.Zero;
|
Handle.pContext = IntPtr.Zero;
|
||||||
Handle.AudioBytes = (uint) bufferLengthInBytes;
|
Handle.AudioBytes = (uint) bufferLengthInBytes;
|
||||||
Handle.pAudioData = Marshal.AllocHGlobal(bufferLengthInBytes);
|
Handle.pAudioData = Marshal.AllocHGlobal(bufferLengthInBytes);
|
||||||
Marshal.Copy(buffer, (int) bufferOffset, Handle.pAudioData, (int) bufferLength);
|
Marshal.Copy(buffer, (int) bufferOffset, Handle.pAudioData, (int) bufferLength);
|
||||||
Handle.PlayBegin = 0;
|
Handle.PlayBegin = 0;
|
||||||
Handle.PlayLength = 0;
|
Handle.PlayLength = 0;
|
||||||
|
|
||||||
LoopStart = 0;
|
LoopStart = 0;
|
||||||
LoopLength = 0;
|
LoopLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public StaticSoundInstance CreateInstance(bool loop = false)
|
public StaticSoundInstance CreateInstance(bool loop = false)
|
||||||
{
|
{
|
||||||
return new StaticSoundInstance(Device, this, false, loop);
|
return new StaticSoundInstance(Device, this, false, loop);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal(Handle.pAudioData);
|
Marshal.FreeHGlobal(Handle.pAudioData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,96 +1,96 @@
|
||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public class StaticSoundInstance : SoundInstance
|
public class StaticSoundInstance : SoundInstance
|
||||||
{
|
{
|
||||||
public StaticSound Parent { get; }
|
public StaticSound Parent { get; }
|
||||||
|
|
||||||
private SoundState _state = SoundState.Stopped;
|
private SoundState _state = SoundState.Stopped;
|
||||||
public override SoundState State
|
public override SoundState State
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
FAudio.FAudioSourceVoice_GetState(
|
FAudio.FAudioSourceVoice_GetState(
|
||||||
Handle,
|
Handle,
|
||||||
out var state,
|
out var state,
|
||||||
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
|
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
|
||||||
);
|
);
|
||||||
if (state.BuffersQueued == 0)
|
if (state.BuffersQueued == 0)
|
||||||
{
|
{
|
||||||
Stop(true);
|
Stop(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return _state;
|
return _state;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected set
|
protected set
|
||||||
{
|
{
|
||||||
_state = value;
|
_state = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal StaticSoundInstance(
|
internal StaticSoundInstance(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
StaticSound parent,
|
StaticSound parent,
|
||||||
bool is3D,
|
bool is3D,
|
||||||
bool loop
|
bool loop
|
||||||
) : base(device, parent.Channels, parent.SamplesPerSecond, is3D, loop)
|
) : base(device, parent.Channels, parent.SamplesPerSecond, is3D, loop)
|
||||||
{
|
{
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Play()
|
public override void Play()
|
||||||
{
|
{
|
||||||
if (State == SoundState.Playing)
|
if (State == SoundState.Playing)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Loop)
|
if (Loop)
|
||||||
{
|
{
|
||||||
Parent.Handle.LoopCount = 255;
|
Parent.Handle.LoopCount = 255;
|
||||||
Parent.Handle.LoopBegin = Parent.LoopStart;
|
Parent.Handle.LoopBegin = Parent.LoopStart;
|
||||||
Parent.Handle.LoopLength = Parent.LoopLength;
|
Parent.Handle.LoopLength = Parent.LoopLength;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Parent.Handle.LoopCount = 0;
|
Parent.Handle.LoopCount = 0;
|
||||||
Parent.Handle.LoopBegin = 0;
|
Parent.Handle.LoopBegin = 0;
|
||||||
Parent.Handle.LoopLength = 0;
|
Parent.Handle.LoopLength = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
|
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
|
||||||
Handle,
|
Handle,
|
||||||
ref Parent.Handle,
|
ref Parent.Handle,
|
||||||
IntPtr.Zero
|
IntPtr.Zero
|
||||||
);
|
);
|
||||||
|
|
||||||
FAudio.FAudioSourceVoice_Start(Handle, 0, 0);
|
FAudio.FAudioSourceVoice_Start(Handle, 0, 0);
|
||||||
State = SoundState.Playing;
|
State = SoundState.Playing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Pause()
|
public override void Pause()
|
||||||
{
|
{
|
||||||
if (State == SoundState.Paused)
|
if (State == SoundState.Paused)
|
||||||
{
|
{
|
||||||
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
||||||
State = SoundState.Paused;
|
State = SoundState.Paused;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Stop(bool immediate = true)
|
public override void Stop(bool immediate = true)
|
||||||
{
|
{
|
||||||
if (immediate)
|
if (immediate)
|
||||||
{
|
{
|
||||||
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
||||||
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle);
|
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle);
|
||||||
State = SoundState.Stopped;
|
State = SoundState.Stopped;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
FAudio.FAudioSourceVoice_ExitLoop(Handle, 0);
|
FAudio.FAudioSourceVoice_ExitLoop(Handle, 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,178 +1,178 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// For streaming long playback.
|
/// For streaming long playback.
|
||||||
/// Can be extended to support custom decoders.
|
/// Can be extended to support custom decoders.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class StreamingSound : SoundInstance
|
public abstract class StreamingSound : SoundInstance
|
||||||
{
|
{
|
||||||
private readonly List<IntPtr> queuedBuffers = new List<IntPtr>();
|
private readonly List<IntPtr> queuedBuffers = new List<IntPtr>();
|
||||||
private readonly List<uint> queuedSizes = new List<uint>();
|
private readonly List<uint> queuedSizes = new List<uint>();
|
||||||
private const int MINIMUM_BUFFER_CHECK = 3;
|
private const int MINIMUM_BUFFER_CHECK = 3;
|
||||||
|
|
||||||
public int PendingBufferCount => queuedBuffers.Count;
|
public int PendingBufferCount => queuedBuffers.Count;
|
||||||
|
|
||||||
public StreamingSound(
|
public StreamingSound(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
ushort channels,
|
ushort channels,
|
||||||
uint samplesPerSecond,
|
uint samplesPerSecond,
|
||||||
bool is3D,
|
bool is3D,
|
||||||
bool loop
|
bool loop
|
||||||
) : base(device, channels, samplesPerSecond, is3D, loop) { }
|
) : base(device, channels, samplesPerSecond, is3D, loop) { }
|
||||||
|
|
||||||
public override void Play()
|
public override void Play()
|
||||||
{
|
{
|
||||||
if (State == SoundState.Playing)
|
if (State == SoundState.Playing)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
State = SoundState.Playing;
|
State = SoundState.Playing;
|
||||||
Update();
|
Update();
|
||||||
FAudio.FAudioSourceVoice_Start(Handle, 0, 0);
|
FAudio.FAudioSourceVoice_Start(Handle, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Pause()
|
public override void Pause()
|
||||||
{
|
{
|
||||||
if (State == SoundState.Playing)
|
if (State == SoundState.Playing)
|
||||||
{
|
{
|
||||||
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
||||||
State = SoundState.Paused;
|
State = SoundState.Paused;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Stop(bool immediate = true)
|
public override void Stop(bool immediate = true)
|
||||||
{
|
{
|
||||||
if (immediate)
|
if (immediate)
|
||||||
{
|
{
|
||||||
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0);
|
||||||
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle);
|
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle);
|
||||||
ClearBuffers();
|
ClearBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
State = SoundState.Stopped;
|
State = SoundState.Stopped;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Update()
|
internal void Update()
|
||||||
{
|
{
|
||||||
if (State != SoundState.Playing)
|
if (State != SoundState.Playing)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudio.FAudioSourceVoice_GetState(
|
FAudio.FAudioSourceVoice_GetState(
|
||||||
Handle,
|
Handle,
|
||||||
out var state,
|
out var state,
|
||||||
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
|
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
|
||||||
);
|
);
|
||||||
|
|
||||||
while (PendingBufferCount > state.BuffersQueued)
|
while (PendingBufferCount > state.BuffersQueued)
|
||||||
lock (queuedBuffers)
|
lock (queuedBuffers)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal(queuedBuffers[0]);
|
Marshal.FreeHGlobal(queuedBuffers[0]);
|
||||||
queuedBuffers.RemoveAt(0);
|
queuedBuffers.RemoveAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
QueueBuffers();
|
QueueBuffers();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void QueueBuffers()
|
protected void QueueBuffers()
|
||||||
{
|
{
|
||||||
for (
|
for (
|
||||||
int i = MINIMUM_BUFFER_CHECK - PendingBufferCount;
|
int i = MINIMUM_BUFFER_CHECK - PendingBufferCount;
|
||||||
i > 0;
|
i > 0;
|
||||||
i -= 1
|
i -= 1
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
AddBuffer();
|
AddBuffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void ClearBuffers()
|
protected void ClearBuffers()
|
||||||
{
|
{
|
||||||
lock (queuedBuffers)
|
lock (queuedBuffers)
|
||||||
{
|
{
|
||||||
foreach (IntPtr buf in queuedBuffers)
|
foreach (IntPtr buf in queuedBuffers)
|
||||||
{
|
{
|
||||||
Marshal.FreeHGlobal(buf);
|
Marshal.FreeHGlobal(buf);
|
||||||
}
|
}
|
||||||
queuedBuffers.Clear();
|
queuedBuffers.Clear();
|
||||||
queuedSizes.Clear();
|
queuedSizes.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void AddBuffer()
|
protected void AddBuffer()
|
||||||
{
|
{
|
||||||
AddBuffer(
|
AddBuffer(
|
||||||
out var buffer,
|
out var buffer,
|
||||||
out var bufferOffset,
|
out var bufferOffset,
|
||||||
out var bufferLength,
|
out var bufferLength,
|
||||||
out var reachedEnd
|
out var reachedEnd
|
||||||
);
|
);
|
||||||
|
|
||||||
var lengthInBytes = bufferLength * sizeof(float);
|
var lengthInBytes = bufferLength * sizeof(float);
|
||||||
|
|
||||||
IntPtr next = Marshal.AllocHGlobal((int) lengthInBytes);
|
IntPtr next = Marshal.AllocHGlobal((int) lengthInBytes);
|
||||||
Marshal.Copy(buffer, (int) bufferOffset, next, (int) bufferLength);
|
Marshal.Copy(buffer, (int) bufferOffset, next, (int) bufferLength);
|
||||||
|
|
||||||
lock (queuedBuffers)
|
lock (queuedBuffers)
|
||||||
{
|
{
|
||||||
queuedBuffers.Add(next);
|
queuedBuffers.Add(next);
|
||||||
if (State != SoundState.Stopped)
|
if (State != SoundState.Stopped)
|
||||||
{
|
{
|
||||||
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer
|
FAudio.FAudioBuffer buf = new FAudio.FAudioBuffer
|
||||||
{
|
{
|
||||||
AudioBytes = lengthInBytes,
|
AudioBytes = lengthInBytes,
|
||||||
pAudioData = next,
|
pAudioData = next,
|
||||||
PlayLength = (
|
PlayLength = (
|
||||||
lengthInBytes /
|
lengthInBytes /
|
||||||
Format.nChannels /
|
Format.nChannels /
|
||||||
(uint)(Format.wBitsPerSample / 8)
|
(uint) (Format.wBitsPerSample / 8)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
|
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
|
||||||
Handle,
|
Handle,
|
||||||
ref buf,
|
ref buf,
|
||||||
IntPtr.Zero
|
IntPtr.Zero
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
queuedSizes.Add(lengthInBytes);
|
queuedSizes.Add(lengthInBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We have reached the end of the file, what do we do? */
|
/* We have reached the end of the file, what do we do? */
|
||||||
if (reachedEnd)
|
if (reachedEnd)
|
||||||
{
|
{
|
||||||
if (Loop)
|
if (Loop)
|
||||||
{
|
{
|
||||||
SeekStart();
|
SeekStart();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Stop(false);
|
Stop(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void AddBuffer(
|
protected abstract void AddBuffer(
|
||||||
out float[] buffer,
|
out float[] buffer,
|
||||||
out uint bufferOffset, /* in floats */
|
out uint bufferOffset, /* in floats */
|
||||||
out uint bufferLength, /* in floats */
|
out uint bufferLength, /* in floats */
|
||||||
out bool reachedEnd
|
out bool reachedEnd
|
||||||
);
|
);
|
||||||
|
|
||||||
protected abstract void SeekStart();
|
protected abstract void SeekStart();
|
||||||
|
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
Stop(true);
|
Stop(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,89 +1,91 @@
|
||||||
using System;
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public class StreamingSoundOgg : StreamingSound
|
public class StreamingSoundOgg : StreamingSound
|
||||||
{
|
{
|
||||||
// FIXME: what should this value be?
|
// FIXME: what should this value be?
|
||||||
public const int BUFFER_SIZE = 1024 * 128;
|
public const int BUFFER_SIZE = 1024 * 128;
|
||||||
|
|
||||||
internal IntPtr FileHandle { get; }
|
internal IntPtr FileHandle { get; }
|
||||||
internal FAudio.stb_vorbis_info Info { get; }
|
internal FAudio.stb_vorbis_info Info { get; }
|
||||||
|
|
||||||
private readonly float[] buffer;
|
private readonly float[] buffer;
|
||||||
|
|
||||||
public override SoundState State { get; protected set; }
|
public override SoundState State { get; protected set; }
|
||||||
|
|
||||||
public static StreamingSoundOgg Load(
|
public static StreamingSoundOgg Load(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
string filePath,
|
string filePath,
|
||||||
bool is3D = false,
|
bool is3D = false,
|
||||||
bool loop = false
|
bool loop = false
|
||||||
) {
|
)
|
||||||
var fileHandle = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
|
{
|
||||||
if (error != 0)
|
var fileHandle = FAudio.stb_vorbis_open_filename(filePath, out var error, IntPtr.Zero);
|
||||||
{
|
if (error != 0)
|
||||||
Logger.LogError("Error opening OGG file!");
|
{
|
||||||
throw new AudioLoadException("Error opening OGG file!");
|
Logger.LogError("Error opening OGG file!");
|
||||||
}
|
throw new AudioLoadException("Error opening OGG file!");
|
||||||
|
}
|
||||||
|
|
||||||
var info = FAudio.stb_vorbis_get_info(fileHandle);
|
var info = FAudio.stb_vorbis_get_info(fileHandle);
|
||||||
|
|
||||||
return new StreamingSoundOgg(
|
return new StreamingSoundOgg(
|
||||||
device,
|
device,
|
||||||
fileHandle,
|
fileHandle,
|
||||||
info,
|
info,
|
||||||
is3D,
|
is3D,
|
||||||
loop
|
loop
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal StreamingSoundOgg(
|
internal StreamingSoundOgg(
|
||||||
AudioDevice device,
|
AudioDevice device,
|
||||||
IntPtr fileHandle,
|
IntPtr fileHandle,
|
||||||
FAudio.stb_vorbis_info info,
|
FAudio.stb_vorbis_info info,
|
||||||
bool is3D,
|
bool is3D,
|
||||||
bool loop
|
bool loop
|
||||||
) : base(device, (ushort) info.channels, info.sample_rate, is3D, loop)
|
) : base(device, (ushort) info.channels, info.sample_rate, is3D, loop)
|
||||||
{
|
{
|
||||||
FileHandle = fileHandle;
|
FileHandle = fileHandle;
|
||||||
Info = info;
|
Info = info;
|
||||||
buffer = new float[BUFFER_SIZE];
|
buffer = new float[BUFFER_SIZE];
|
||||||
|
|
||||||
device.AddDynamicSoundInstance(this);
|
device.AddDynamicSoundInstance(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void AddBuffer(
|
protected override void AddBuffer(
|
||||||
out float[] buffer,
|
out float[] buffer,
|
||||||
out uint bufferOffset,
|
out uint bufferOffset,
|
||||||
out uint bufferLength,
|
out uint bufferLength,
|
||||||
out bool reachedEnd
|
out bool reachedEnd
|
||||||
) {
|
)
|
||||||
buffer = this.buffer;
|
{
|
||||||
|
buffer = this.buffer;
|
||||||
|
|
||||||
/* NOTE: this function returns samples per channel, not total samples */
|
/* NOTE: this function returns samples per channel, not total samples */
|
||||||
var samples = FAudio.stb_vorbis_get_samples_float_interleaved(
|
var samples = FAudio.stb_vorbis_get_samples_float_interleaved(
|
||||||
FileHandle,
|
FileHandle,
|
||||||
Info.channels,
|
Info.channels,
|
||||||
buffer,
|
buffer,
|
||||||
buffer.Length
|
buffer.Length
|
||||||
);
|
);
|
||||||
|
|
||||||
var sampleCount = samples * Info.channels;
|
var sampleCount = samples * Info.channels;
|
||||||
bufferOffset = 0;
|
bufferOffset = 0;
|
||||||
bufferLength = (uint) sampleCount;
|
bufferLength = (uint) sampleCount;
|
||||||
reachedEnd = sampleCount < buffer.Length;
|
reachedEnd = sampleCount < buffer.Length;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void SeekStart()
|
protected override void SeekStart()
|
||||||
{
|
{
|
||||||
FAudio.stb_vorbis_seek_start(FileHandle);
|
FAudio.stb_vorbis_seek_start(FileHandle);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Destroy()
|
protected override void Destroy()
|
||||||
{
|
{
|
||||||
FAudio.stb_vorbis_close(FileHandle);
|
FAudio.stb_vorbis_close(FileHandle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue