started reworking audio around Voice API
parent
81cd397013
commit
b1c7740b86
|
@ -9,24 +9,15 @@ namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
public IntPtr Handle { get; }
|
public IntPtr Handle { get; }
|
||||||
public byte[] Handle3D { get; }
|
public byte[] Handle3D { get; }
|
||||||
public IntPtr MasteringVoice { get; }
|
|
||||||
public FAudio.FAudioDeviceDetails DeviceDetails { get; }
|
public FAudio.FAudioDeviceDetails DeviceDetails { get; }
|
||||||
|
|
||||||
|
private MasteringVoice masteringVoice;
|
||||||
|
public MasteringVoice MasteringVoice => masteringVoice;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
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 HashSet<WeakReference> resources = new HashSet<WeakReference>();
|
||||||
private readonly List<StreamingSound> autoUpdateStreamingSoundReferences = new List<StreamingSound>();
|
private readonly List<StreamingSound> autoUpdateStreamingSoundReferences = new List<StreamingSound>();
|
||||||
private readonly List<StaticSoundInstance> autoFreeStaticSoundInstanceReferences = new List<StaticSoundInstance>();
|
private readonly List<StaticSoundInstance> autoFreeStaticSoundInstanceReferences = new List<StaticSoundInstance>();
|
||||||
|
@ -93,26 +84,18 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Init Mastering Voice */
|
/* Init Mastering Voice */
|
||||||
IntPtr masteringVoice;
|
var result = MasteringVoice.Create(
|
||||||
|
this,
|
||||||
if (FAudio.FAudio_CreateMasteringVoice(
|
|
||||||
Handle,
|
|
||||||
out masteringVoice,
|
|
||||||
FAudio.FAUDIO_DEFAULT_CHANNELS,
|
|
||||||
FAudio.FAUDIO_DEFAULT_SAMPLERATE,
|
|
||||||
0,
|
|
||||||
i,
|
i,
|
||||||
IntPtr.Zero
|
out masteringVoice
|
||||||
) != 0)
|
);
|
||||||
|
|
||||||
|
if (!result)
|
||||||
{
|
{
|
||||||
Logger.LogError("No mastering voice found!");
|
Logger.LogError("Audio device creation failed!");
|
||||||
FAudio.FAudio_Release(Handle);
|
|
||||||
Handle = IntPtr.Zero;
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
MasteringVoice = masteringVoice;
|
|
||||||
|
|
||||||
/* Init 3D Audio */
|
/* Init 3D Audio */
|
||||||
|
|
||||||
Handle3D = new byte[FAudio.F3DAUDIO_HANDLE_BYTESIZE];
|
Handle3D = new byte[FAudio.F3DAUDIO_HANDLE_BYTESIZE];
|
||||||
|
@ -192,7 +175,7 @@ namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
if (soundSequenceReferences[i].TryGetTarget(out var soundSequence))
|
if (soundSequenceReferences[i].TryGetTarget(out var soundSequence))
|
||||||
{
|
{
|
||||||
soundSequence.Update();
|
soundSequence.OnUpdate();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -203,13 +186,13 @@ namespace MoonWorks.Audio
|
||||||
AudioTweenManager.Update(elapsedSeconds);
|
AudioTweenManager.Update(elapsedSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SyncPlay()
|
public void TriggerSyncGroup(uint syncGroup)
|
||||||
{
|
{
|
||||||
FAudio.FAudio_CommitChanges(Handle, 1);
|
FAudio.FAudio_CommitChanges(Handle, syncGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void CreateTween(
|
internal void CreateTween(
|
||||||
SoundInstance soundInstance,
|
Voice voice,
|
||||||
AudioTweenProperty property,
|
AudioTweenProperty property,
|
||||||
System.Func<float, float> easingFunction,
|
System.Func<float, float> easingFunction,
|
||||||
float start,
|
float start,
|
||||||
|
@ -220,7 +203,7 @@ namespace MoonWorks.Audio
|
||||||
lock (StateLock)
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
AudioTweenManager.CreateTween(
|
AudioTweenManager.CreateTween(
|
||||||
soundInstance,
|
voice,
|
||||||
property,
|
property,
|
||||||
easingFunction,
|
easingFunction,
|
||||||
start,
|
start,
|
||||||
|
@ -232,12 +215,12 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void ClearTweens(
|
internal void ClearTweens(
|
||||||
SoundInstance soundReference,
|
Voice voice,
|
||||||
AudioTweenProperty property
|
AudioTweenProperty property
|
||||||
) {
|
) {
|
||||||
lock (StateLock)
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
AudioTweenManager.ClearTweens(soundReference, property);
|
AudioTweenManager.ClearTweens(voice, property);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -286,6 +269,18 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
if (disposing)
|
if (disposing)
|
||||||
{
|
{
|
||||||
|
// stop all source voices
|
||||||
|
foreach (var weakReference in resources)
|
||||||
|
{
|
||||||
|
var target = weakReference.Target;
|
||||||
|
|
||||||
|
if (target != null && target is SourceVoice voice)
|
||||||
|
{
|
||||||
|
voice.Stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// destroy all audio resources
|
||||||
foreach (var weakReference in resources)
|
foreach (var weakReference in resources)
|
||||||
{
|
{
|
||||||
var target = weakReference.Target;
|
var target = weakReference.Target;
|
||||||
|
@ -295,10 +290,10 @@ namespace MoonWorks.Audio
|
||||||
(target as IDisposable).Dispose();
|
(target as IDisposable).Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
resources.Clear();
|
resources.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
FAudio.FAudioVoice_DestroyVoice(MasteringVoice);
|
|
||||||
FAudio.FAudio_Release(Handle);
|
FAudio.FAudio_Release(Handle);
|
||||||
|
|
||||||
IsDisposed = true;
|
IsDisposed = true;
|
||||||
|
|
|
@ -14,7 +14,7 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
internal class AudioTween
|
internal class AudioTween
|
||||||
{
|
{
|
||||||
public SoundInstance SoundInstance;
|
public Voice Voice;
|
||||||
public AudioTweenProperty Property;
|
public AudioTweenProperty Property;
|
||||||
public EasingFunction EasingFunction;
|
public EasingFunction EasingFunction;
|
||||||
public float Time;
|
public float Time;
|
||||||
|
@ -51,7 +51,7 @@ namespace MoonWorks.Audio
|
||||||
|
|
||||||
public void Free(AudioTween tween)
|
public void Free(AudioTween tween)
|
||||||
{
|
{
|
||||||
tween.SoundInstance = null;
|
tween.Voice = null;
|
||||||
Tweens.Enqueue(tween);
|
Tweens.Enqueue(tween);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace MoonWorks.Audio
|
||||||
internal class AudioTweenManager
|
internal class AudioTweenManager
|
||||||
{
|
{
|
||||||
private AudioTweenPool AudioTweenPool = new AudioTweenPool();
|
private AudioTweenPool AudioTweenPool = new AudioTweenPool();
|
||||||
private readonly Dictionary<(SoundInstance, AudioTweenProperty), AudioTween> AudioTweens = new Dictionary<(SoundInstance, AudioTweenProperty), AudioTween>();
|
private readonly Dictionary<(Voice, AudioTweenProperty), AudioTween> AudioTweens = new Dictionary<(Voice, AudioTweenProperty), AudioTween>();
|
||||||
private readonly List<AudioTween> DelayedAudioTweens = new List<AudioTween>();
|
private readonly List<AudioTween> DelayedAudioTweens = new List<AudioTween>();
|
||||||
|
|
||||||
public void Update(float elapsedSeconds)
|
public void Update(float elapsedSeconds)
|
||||||
|
@ -14,7 +14,7 @@ namespace MoonWorks.Audio
|
||||||
for (var i = DelayedAudioTweens.Count - 1; i >= 0; i--)
|
for (var i = DelayedAudioTweens.Count - 1; i >= 0; i--)
|
||||||
{
|
{
|
||||||
var audioTween = DelayedAudioTweens[i];
|
var audioTween = DelayedAudioTweens[i];
|
||||||
var soundInstance = audioTween.SoundInstance;
|
var voice = audioTween.Voice;
|
||||||
|
|
||||||
audioTween.Time += elapsedSeconds;
|
audioTween.Time += elapsedSeconds;
|
||||||
|
|
||||||
|
@ -24,23 +24,23 @@ namespace MoonWorks.Audio
|
||||||
switch (audioTween.Property)
|
switch (audioTween.Property)
|
||||||
{
|
{
|
||||||
case AudioTweenProperty.Pan:
|
case AudioTweenProperty.Pan:
|
||||||
audioTween.StartValue = soundInstance.Pan;
|
audioTween.StartValue = voice.Pan;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AudioTweenProperty.Pitch:
|
case AudioTweenProperty.Pitch:
|
||||||
audioTween.StartValue = soundInstance.Pitch;
|
audioTween.StartValue = voice.Pitch;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AudioTweenProperty.Volume:
|
case AudioTweenProperty.Volume:
|
||||||
audioTween.StartValue = soundInstance.Volume;
|
audioTween.StartValue = voice.Volume;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AudioTweenProperty.FilterFrequency:
|
case AudioTweenProperty.FilterFrequency:
|
||||||
audioTween.StartValue = soundInstance.FilterFrequency;
|
audioTween.StartValue = voice.FilterFrequency;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AudioTweenProperty.Reverb:
|
case AudioTweenProperty.Reverb:
|
||||||
audioTween.StartValue = soundInstance.Reverb;
|
audioTween.StartValue = voice.Reverb;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CreateTween(
|
public void CreateTween(
|
||||||
SoundInstance soundInstance,
|
Voice voice,
|
||||||
AudioTweenProperty property,
|
AudioTweenProperty property,
|
||||||
System.Func<float, float> easingFunction,
|
System.Func<float, float> easingFunction,
|
||||||
float start,
|
float start,
|
||||||
|
@ -73,7 +73,7 @@ namespace MoonWorks.Audio
|
||||||
float delayTime
|
float delayTime
|
||||||
) {
|
) {
|
||||||
var tween = AudioTweenPool.Obtain();
|
var tween = AudioTweenPool.Obtain();
|
||||||
tween.SoundInstance = soundInstance;
|
tween.Voice = voice;
|
||||||
tween.Property = property;
|
tween.Property = property;
|
||||||
tween.EasingFunction = easingFunction;
|
tween.EasingFunction = easingFunction;
|
||||||
tween.StartValue = start;
|
tween.StartValue = start;
|
||||||
|
@ -92,21 +92,21 @@ namespace MoonWorks.Audio
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ClearTweens(SoundInstance soundInstance, AudioTweenProperty property)
|
public void ClearTweens(Voice voice, AudioTweenProperty property)
|
||||||
{
|
{
|
||||||
AudioTweens.Remove((soundInstance, property));
|
AudioTweens.Remove((voice, property));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void AddTween(
|
private void AddTween(
|
||||||
AudioTween audioTween
|
AudioTween audioTween
|
||||||
) {
|
) {
|
||||||
// if a tween with the same sound and property already exists, get rid of it
|
// if a tween with the same sound and property already exists, get rid of it
|
||||||
if (AudioTweens.TryGetValue((audioTween.SoundInstance, audioTween.Property), out var currentTween))
|
if (AudioTweens.TryGetValue((audioTween.Voice, audioTween.Property), out var currentTween))
|
||||||
{
|
{
|
||||||
AudioTweenPool.Free(currentTween);
|
AudioTweenPool.Free(currentTween);
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioTweens[(audioTween.SoundInstance, audioTween.Property)] = audioTween;
|
AudioTweens[(audioTween.Voice, audioTween.Property)] = audioTween;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool UpdateAudioTween(AudioTween audioTween, float delta)
|
private static bool UpdateAudioTween(AudioTween audioTween, float delta)
|
||||||
|
@ -133,23 +133,23 @@ namespace MoonWorks.Audio
|
||||||
switch (audioTween.Property)
|
switch (audioTween.Property)
|
||||||
{
|
{
|
||||||
case AudioTweenProperty.Pan:
|
case AudioTweenProperty.Pan:
|
||||||
audioTween.SoundInstance.Pan = value;
|
audioTween.Voice.Pan = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AudioTweenProperty.Pitch:
|
case AudioTweenProperty.Pitch:
|
||||||
audioTween.SoundInstance.Pitch = value;
|
audioTween.Voice.Pitch = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AudioTweenProperty.Volume:
|
case AudioTweenProperty.Volume:
|
||||||
audioTween.SoundInstance.Volume = value;
|
audioTween.Voice.Volume = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AudioTweenProperty.FilterFrequency:
|
case AudioTweenProperty.FilterFrequency:
|
||||||
audioTween.SoundInstance.FilterFrequency = value;
|
audioTween.Voice.FilterFrequency = value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case AudioTweenProperty.Reverb:
|
case AudioTweenProperty.Reverb:
|
||||||
audioTween.SoundInstance.Reverb = value;
|
audioTween.Voice.Reverb = value;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MoonWorks.Audio
|
||||||
|
{
|
||||||
|
public class MasteringVoice : Voice
|
||||||
|
{
|
||||||
|
// mastering voice can't pan
|
||||||
|
public override float Pan => 0;
|
||||||
|
|
||||||
|
internal static bool Create(
|
||||||
|
AudioDevice device,
|
||||||
|
uint deviceIndex,
|
||||||
|
out MasteringVoice masteringVoice
|
||||||
|
) {
|
||||||
|
var result = FAudio.FAudio_CreateMasteringVoice(
|
||||||
|
device.Handle,
|
||||||
|
out var handle,
|
||||||
|
FAudio.FAUDIO_DEFAULT_CHANNELS,
|
||||||
|
FAudio.FAUDIO_DEFAULT_SAMPLERATE,
|
||||||
|
0,
|
||||||
|
deviceIndex,
|
||||||
|
IntPtr.Zero
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result == 0)
|
||||||
|
{
|
||||||
|
masteringVoice = new MasteringVoice(device, handle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Logger.LogError("Failed to create mastering voice!");
|
||||||
|
masteringVoice = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal MasteringVoice(
|
||||||
|
AudioDevice device,
|
||||||
|
IntPtr handle
|
||||||
|
) : base(device)
|
||||||
|
{
|
||||||
|
Handle = handle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,53 +4,31 @@ using System.Runtime.InteropServices;
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
// sound instances can send their audio to this voice to add reverb
|
// sound instances can send their audio to this voice to add reverb
|
||||||
public unsafe class ReverbEffect : AudioResource
|
public unsafe class ReverbEffect : SubmixVoice
|
||||||
{
|
{
|
||||||
private IntPtr voice;
|
public ReverbEffect(AudioDevice audioDevice) : base(audioDevice, 1, audioDevice.DeviceDetails.OutputFormat.Format.nSamplesPerSec)
|
||||||
public IntPtr Voice => voice;
|
|
||||||
|
|
||||||
public ReverbEffect(AudioDevice audioDevice) : base(audioDevice)
|
|
||||||
{
|
{
|
||||||
/* Init reverb */
|
/* Init reverb */
|
||||||
|
|
||||||
IntPtr reverb;
|
IntPtr reverb;
|
||||||
FAudio.FAudioCreateReverb(out reverb, 0);
|
FAudio.FAudioCreateReverb(out reverb, 0);
|
||||||
|
|
||||||
IntPtr chainPtr;
|
var chain = new FAudio.FAudioEffectChain();
|
||||||
chainPtr = (nint) NativeMemory.Alloc(
|
var descriptor = new FAudio.FAudioEffectDescriptor();
|
||||||
(nuint) Marshal.SizeOf<FAudio.FAudioEffectChain>()
|
|
||||||
|
descriptor.InitialState = 1;
|
||||||
|
descriptor.OutputChannels = Device.DeviceDetails.OutputFormat.Format.nChannels;
|
||||||
|
descriptor.pEffect = reverb;
|
||||||
|
|
||||||
|
chain.EffectCount = 1;
|
||||||
|
chain.pEffectDescriptors = (nint) (&descriptor);
|
||||||
|
|
||||||
|
FAudio.FAudioVoice_SetEffectChain(
|
||||||
|
Handle,
|
||||||
|
ref chain
|
||||||
);
|
);
|
||||||
|
|
||||||
FAudio.FAudioEffectChain* reverbChain = (FAudio.FAudioEffectChain*) chainPtr;
|
|
||||||
reverbChain->EffectCount = 1;
|
|
||||||
reverbChain->pEffectDescriptors = (nint) NativeMemory.Alloc(
|
|
||||||
(nuint) Marshal.SizeOf<FAudio.FAudioEffectDescriptor>()
|
|
||||||
);
|
|
||||||
|
|
||||||
FAudio.FAudioEffectDescriptor* reverbDescriptor =
|
|
||||||
(FAudio.FAudioEffectDescriptor*) reverbChain->pEffectDescriptors;
|
|
||||||
|
|
||||||
reverbDescriptor->InitialState = 1;
|
|
||||||
reverbDescriptor->OutputChannels = (uint) (
|
|
||||||
(audioDevice.DeviceDetails.OutputFormat.Format.nChannels == 6) ? 6 : 1
|
|
||||||
);
|
|
||||||
reverbDescriptor->pEffect = reverb;
|
|
||||||
|
|
||||||
FAudio.FAudio_CreateSubmixVoice(
|
|
||||||
audioDevice.Handle,
|
|
||||||
out voice,
|
|
||||||
1, /* omnidirectional reverb */
|
|
||||||
audioDevice.DeviceDetails.OutputFormat.Format.nSamplesPerSec,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
IntPtr.Zero,
|
|
||||||
chainPtr
|
|
||||||
);
|
|
||||||
FAudio.FAPOBase_Release(reverb);
|
FAudio.FAPOBase_Release(reverb);
|
||||||
|
|
||||||
NativeMemory.Free((void*) reverbChain->pEffectDescriptors);
|
|
||||||
NativeMemory.Free((void*) chainPtr);
|
|
||||||
|
|
||||||
/* Init reverb params */
|
/* Init reverb params */
|
||||||
// Defaults based on FAUDIOFX_I3DL2_PRESET_GENERIC
|
// Defaults based on FAUDIOFX_I3DL2_PRESET_GENERIC
|
||||||
|
|
||||||
|
@ -86,7 +64,7 @@ namespace MoonWorks.Audio
|
||||||
fixed (FAudio.FAudioFXReverbParameters* reverbParamsPtr = &reverbParams)
|
fixed (FAudio.FAudioFXReverbParameters* reverbParamsPtr = &reverbParams)
|
||||||
{
|
{
|
||||||
FAudio.FAudioVoice_SetEffectParameters(
|
FAudio.FAudioVoice_SetEffectParameters(
|
||||||
voice,
|
Handle,
|
||||||
0,
|
0,
|
||||||
(nint) reverbParamsPtr,
|
(nint) reverbParamsPtr,
|
||||||
(uint) Marshal.SizeOf<FAudio.FAudioFXReverbParameters>(),
|
(uint) Marshal.SizeOf<FAudio.FAudioFXReverbParameters>(),
|
||||||
|
@ -94,10 +72,5 @@ namespace MoonWorks.Audio
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Destroy()
|
|
||||||
{
|
|
||||||
FAudio.FAudioVoice_DestroyVoice(Voice);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,25 +3,25 @@ using System;
|
||||||
namespace MoonWorks.Audio
|
namespace MoonWorks.Audio
|
||||||
{
|
{
|
||||||
// NOTE: all sounds played with a SoundSequence must have the same audio format!
|
// NOTE: all sounds played with a SoundSequence must have the same audio format!
|
||||||
public class SoundSequence : SoundInstance
|
public class SoundSequence : SourceVoice
|
||||||
{
|
{
|
||||||
public int NeedSoundThreshold = 0;
|
public int NeedSoundThreshold = 0;
|
||||||
public delegate void OnSoundNeededFunc();
|
public delegate void OnSoundNeededFunc();
|
||||||
public OnSoundNeededFunc OnSoundNeeded;
|
public OnSoundNeededFunc OnSoundNeeded;
|
||||||
|
|
||||||
private object StateLock = new object();
|
|
||||||
|
|
||||||
public SoundSequence(AudioDevice device, ushort formatTag, ushort bitsPerSample, ushort blockAlign, ushort channels, uint samplesPerSecond) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond)
|
public SoundSequence(AudioDevice device, ushort formatTag, ushort bitsPerSample, ushort blockAlign, ushort channels, uint samplesPerSecond) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond)
|
||||||
{
|
{
|
||||||
device.AddSoundSequenceReference(this);
|
device.AddSoundSequenceReference(this);
|
||||||
|
OnUpdate += Update;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SoundSequence(AudioDevice device, StaticSound templateSound) : base(device, templateSound.FormatTag, templateSound.BitsPerSample, templateSound.BlockAlign, templateSound.Channels, templateSound.SamplesPerSecond)
|
public SoundSequence(AudioDevice device, StaticSound templateSound) : base(device, templateSound.FormatTag, templateSound.BitsPerSample, templateSound.BlockAlign, templateSound.Channels, templateSound.SamplesPerSecond)
|
||||||
{
|
{
|
||||||
device.AddSoundSequenceReference(this);
|
device.AddSoundSequenceReference(this);
|
||||||
|
OnUpdate += Update;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Update()
|
private void Update()
|
||||||
{
|
{
|
||||||
lock (StateLock)
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
|
@ -31,7 +31,7 @@ namespace MoonWorks.Audio
|
||||||
if (NeedSoundThreshold > 0)
|
if (NeedSoundThreshold > 0)
|
||||||
{
|
{
|
||||||
FAudio.FAudioSourceVoice_GetState(
|
FAudio.FAudioSourceVoice_GetState(
|
||||||
Voice,
|
Handle,
|
||||||
out var state,
|
out var state,
|
||||||
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
|
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
|
||||||
);
|
);
|
||||||
|
@ -65,66 +65,11 @@ namespace MoonWorks.Audio
|
||||||
lock (StateLock)
|
lock (StateLock)
|
||||||
{
|
{
|
||||||
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
|
FAudio.FAudioSourceVoice_SubmitSourceBuffer(
|
||||||
Voice,
|
Handle,
|
||||||
ref sound.Handle,
|
ref sound.Handle,
|
||||||
IntPtr.Zero
|
IntPtr.Zero
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Pause()
|
|
||||||
{
|
|
||||||
lock (StateLock)
|
|
||||||
{
|
|
||||||
if (State == SoundState.Playing)
|
|
||||||
{
|
|
||||||
FAudio.FAudioSourceVoice_Stop(Voice, 0, 0);
|
|
||||||
State = SoundState.Paused;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Play()
|
|
||||||
{
|
|
||||||
PlayUsingOperationSet(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void QueueSyncPlay()
|
|
||||||
{
|
|
||||||
PlayUsingOperationSet(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void PlayUsingOperationSet(uint operationSet)
|
|
||||||
{
|
|
||||||
lock (StateLock)
|
|
||||||
{
|
|
||||||
if (State == SoundState.Playing)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
FAudio.FAudioSourceVoice_Start(Voice, 0, operationSet);
|
|
||||||
State = SoundState.Playing;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Stop()
|
|
||||||
{
|
|
||||||
lock (StateLock)
|
|
||||||
{
|
|
||||||
FAudio.FAudioSourceVoice_ExitLoop(Voice, 0);
|
|
||||||
State = SoundState.Stopped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void StopImmediate()
|
|
||||||
{
|
|
||||||
lock (StateLock)
|
|
||||||
{
|
|
||||||
FAudio.FAudioSourceVoice_Stop(Voice, 0, 0);
|
|
||||||
FAudio.FAudioSourceVoice_FlushSourceBuffers(Voice);
|
|
||||||
State = SoundState.Stopped;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MoonWorks.Audio
|
||||||
|
{
|
||||||
|
public class SourceVoice : Voice
|
||||||
|
{
|
||||||
|
protected FAudio.FAudioWaveFormatEx Format;
|
||||||
|
|
||||||
|
protected object StateLock = new object();
|
||||||
|
|
||||||
|
public uint BuffersQueued
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
FAudio.FAudioSourceVoice_GetState(
|
||||||
|
Handle,
|
||||||
|
out var state,
|
||||||
|
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
|
||||||
|
);
|
||||||
|
|
||||||
|
return state.BuffersQueued;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private SoundState state;
|
||||||
|
public SoundState State
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (BuffersQueued == 0)
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
state = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public delegate void OnUpdateFunc();
|
||||||
|
public OnUpdateFunc OnUpdate; // called by AudioDevice thread
|
||||||
|
|
||||||
|
public SourceVoice(
|
||||||
|
AudioDevice device,
|
||||||
|
ushort formatTag,
|
||||||
|
ushort bitsPerSample,
|
||||||
|
ushort blockAlign,
|
||||||
|
ushort channels,
|
||||||
|
uint samplesPerSecond
|
||||||
|
) : base(device)
|
||||||
|
{
|
||||||
|
Format = new FAudio.FAudioWaveFormatEx
|
||||||
|
{
|
||||||
|
wFormatTag = formatTag,
|
||||||
|
wBitsPerSample = bitsPerSample,
|
||||||
|
nChannels = channels,
|
||||||
|
nBlockAlign = blockAlign,
|
||||||
|
nSamplesPerSec = samplesPerSecond,
|
||||||
|
nAvgBytesPerSec = blockAlign * samplesPerSecond
|
||||||
|
};
|
||||||
|
|
||||||
|
FAudio.FAudio_CreateSourceVoice(
|
||||||
|
device.Handle,
|
||||||
|
out var Handle,
|
||||||
|
ref Format,
|
||||||
|
FAudio.FAUDIO_VOICE_USEFILTER,
|
||||||
|
FAudio.FAUDIO_DEFAULT_FREQ_RATIO,
|
||||||
|
IntPtr.Zero,
|
||||||
|
IntPtr.Zero, // default sends to mastering voice!
|
||||||
|
IntPtr.Zero
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Play(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW)
|
||||||
|
{
|
||||||
|
lock (StateLock)
|
||||||
|
{
|
||||||
|
FAudio.FAudioSourceVoice_Start(Handle, 0, syncGroup);
|
||||||
|
|
||||||
|
State = SoundState.Playing;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Pause(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW)
|
||||||
|
{
|
||||||
|
lock (StateLock)
|
||||||
|
{
|
||||||
|
FAudio.FAudioSourceVoice_Stop(Handle, 0, syncGroup);
|
||||||
|
|
||||||
|
State = SoundState.Paused;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ExitLoop(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW)
|
||||||
|
{
|
||||||
|
lock (StateLock)
|
||||||
|
{
|
||||||
|
FAudio.FAudioSourceVoice_ExitLoop(Handle, syncGroup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Stop(uint syncGroup = FAudio.FAUDIO_COMMIT_NOW)
|
||||||
|
{
|
||||||
|
lock (StateLock)
|
||||||
|
{
|
||||||
|
FAudio.FAudioSourceVoice_Stop(Handle, 0, syncGroup);
|
||||||
|
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle);
|
||||||
|
|
||||||
|
State = SoundState.Stopped;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override unsafe void Destroy()
|
||||||
|
{
|
||||||
|
Stop();
|
||||||
|
base.Destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
namespace MoonWorks.Audio
|
||||||
|
{
|
||||||
|
public class StaticSourceVoice : SourceVoice
|
||||||
|
{
|
||||||
|
public StaticSourceVoice(AudioDevice device, ushort formatTag, ushort bitsPerSample, ushort blockAlign, ushort channels, uint samplesPerSecond) : base(device, formatTag, bitsPerSample, blockAlign, channels, samplesPerSecond)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
|
||||||
|
namespace MoonWorks.Audio
|
||||||
|
{
|
||||||
|
public class SubmixVoice : Voice
|
||||||
|
{
|
||||||
|
public SubmixVoice(
|
||||||
|
AudioDevice device,
|
||||||
|
uint inputChannels,
|
||||||
|
uint sampleRate
|
||||||
|
) : base(device)
|
||||||
|
{
|
||||||
|
FAudio.FAudio_CreateSubmixVoice(
|
||||||
|
device.Handle,
|
||||||
|
out Handle,
|
||||||
|
inputChannels,
|
||||||
|
sampleRate,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
IntPtr.Zero,
|
||||||
|
IntPtr.Zero
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,446 @@
|
||||||
|
using System;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using EasingFunction = System.Func<float, float>;
|
||||||
|
|
||||||
|
namespace MoonWorks.Audio
|
||||||
|
{
|
||||||
|
public abstract class Voice : AudioResource
|
||||||
|
{
|
||||||
|
protected IntPtr Handle;
|
||||||
|
|
||||||
|
protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
|
||||||
|
|
||||||
|
private ReverbEffect ReverbEffect;
|
||||||
|
|
||||||
|
public bool Is3D { get; protected set; }
|
||||||
|
|
||||||
|
private float pan = 0;
|
||||||
|
public virtual float Pan
|
||||||
|
{
|
||||||
|
get => pan;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
value = Math.MathHelper.Clamp(value, -1f, 1f);
|
||||||
|
if (pan != value)
|
||||||
|
{
|
||||||
|
pan = value;
|
||||||
|
|
||||||
|
if (pan < -1f)
|
||||||
|
{
|
||||||
|
pan = -1f;
|
||||||
|
}
|
||||||
|
if (pan > 1f)
|
||||||
|
{
|
||||||
|
pan = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Is3D) { return; }
|
||||||
|
|
||||||
|
SetPanMatrixCoefficients();
|
||||||
|
FAudio.FAudioVoice_SetOutputMatrix(
|
||||||
|
Handle,
|
||||||
|
Device.MasteringVoice.Handle,
|
||||||
|
dspSettings.SrcChannelCount,
|
||||||
|
dspSettings.DstChannelCount,
|
||||||
|
dspSettings.pMatrixCoefficients,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float pitch = 0;
|
||||||
|
public float Pitch
|
||||||
|
{
|
||||||
|
get => pitch;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
value = Math.MathHelper.Clamp(value, -1f, 1f);
|
||||||
|
if (pitch != value)
|
||||||
|
{
|
||||||
|
pitch = value;
|
||||||
|
UpdatePitch();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float volume = 1;
|
||||||
|
public float Volume
|
||||||
|
{
|
||||||
|
get => volume;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
value = Math.MathHelper.Max(0, value);
|
||||||
|
if (volume != value)
|
||||||
|
{
|
||||||
|
volume = value;
|
||||||
|
FAudio.FAudioVoice_SetVolume(Handle, volume, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private const float MAX_FILTER_FREQUENCY = 1f;
|
||||||
|
private const float MAX_FILTER_ONEOVERQ = 1.5f;
|
||||||
|
|
||||||
|
private FAudio.FAudioFilterParameters filterParameters = new FAudio.FAudioFilterParameters
|
||||||
|
{
|
||||||
|
Type = FAudio.FAudioFilterType.FAudioLowPassFilter,
|
||||||
|
Frequency = 1f,
|
||||||
|
OneOverQ = 1f
|
||||||
|
};
|
||||||
|
|
||||||
|
public float FilterFrequency
|
||||||
|
{
|
||||||
|
get => filterParameters.Frequency;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
value = System.Math.Clamp(value, 0.01f, MAX_FILTER_FREQUENCY);
|
||||||
|
if (filterParameters.Frequency != value)
|
||||||
|
{
|
||||||
|
filterParameters.Frequency = value;
|
||||||
|
|
||||||
|
FAudio.FAudioVoice_SetFilterParameters(
|
||||||
|
Handle,
|
||||||
|
ref filterParameters,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float FilterOneOverQ
|
||||||
|
{
|
||||||
|
get => filterParameters.OneOverQ;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
value = System.Math.Clamp(value, 0.01f, MAX_FILTER_ONEOVERQ);
|
||||||
|
if (filterParameters.OneOverQ != value)
|
||||||
|
{
|
||||||
|
filterParameters.OneOverQ = value;
|
||||||
|
|
||||||
|
FAudio.FAudioVoice_SetFilterParameters(
|
||||||
|
Handle,
|
||||||
|
ref filterParameters,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private FilterType filterType;
|
||||||
|
public FilterType FilterType
|
||||||
|
{
|
||||||
|
get => filterType;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (filterType != value)
|
||||||
|
{
|
||||||
|
filterType = value;
|
||||||
|
|
||||||
|
switch (filterType)
|
||||||
|
{
|
||||||
|
case FilterType.None:
|
||||||
|
filterParameters = new FAudio.FAudioFilterParameters
|
||||||
|
{
|
||||||
|
Type = FAudio.FAudioFilterType.FAudioLowPassFilter,
|
||||||
|
Frequency = 1f,
|
||||||
|
OneOverQ = 1f
|
||||||
|
};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FilterType.LowPass:
|
||||||
|
filterParameters.Type = FAudio.FAudioFilterType.FAudioLowPassFilter;
|
||||||
|
filterParameters.Frequency = 1f;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FilterType.BandPass:
|
||||||
|
filterParameters.Type = FAudio.FAudioFilterType.FAudioBandPassFilter;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case FilterType.HighPass:
|
||||||
|
filterParameters.Type = FAudio.FAudioFilterType.FAudioHighPassFilter;
|
||||||
|
filterParameters.Frequency = 0f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FAudio.FAudioVoice_SetFilterParameters(
|
||||||
|
Handle,
|
||||||
|
ref filterParameters,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float reverb;
|
||||||
|
public unsafe float Reverb
|
||||||
|
{
|
||||||
|
get => reverb;
|
||||||
|
internal set
|
||||||
|
{
|
||||||
|
if (ReverbEffect != null)
|
||||||
|
{
|
||||||
|
value = MathF.Max(0, value);
|
||||||
|
if (reverb != value)
|
||||||
|
{
|
||||||
|
reverb = value;
|
||||||
|
|
||||||
|
float* outputMatrix = (float*) dspSettings.pMatrixCoefficients;
|
||||||
|
outputMatrix[0] = reverb;
|
||||||
|
if (dspSettings.SrcChannelCount == 2)
|
||||||
|
{
|
||||||
|
outputMatrix[1] = reverb;
|
||||||
|
}
|
||||||
|
|
||||||
|
FAudio.FAudioVoice_SetOutputMatrix(
|
||||||
|
Handle,
|
||||||
|
ReverbEffect.Handle,
|
||||||
|
dspSettings.SrcChannelCount,
|
||||||
|
1,
|
||||||
|
dspSettings.pMatrixCoefficients,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
if (ReverbEffect == null)
|
||||||
|
{
|
||||||
|
Logger.LogWarn("Tried to set reverb value before applying a reverb effect");
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Voice(AudioDevice device) : base(device) { }
|
||||||
|
|
||||||
|
public void SetPan(float targetValue)
|
||||||
|
{
|
||||||
|
Pan = targetValue;
|
||||||
|
Device.ClearTweens(this, AudioTweenProperty.Pan);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPan(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Pan, easingFunction, Pan, targetValue, duration, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPan(float targetValue, float delayTime, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Pan, easingFunction, Pan, targetValue, duration, delayTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPitch(float targetValue)
|
||||||
|
{
|
||||||
|
Pitch = targetValue;
|
||||||
|
Device.ClearTweens(this, AudioTweenProperty.Pitch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPitch(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Pitch, easingFunction, Pitch, targetValue, duration, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetPitch(float targetValue, float delayTime, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Pitch, easingFunction, Pitch, targetValue, duration, delayTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float targetValue)
|
||||||
|
{
|
||||||
|
Volume = targetValue;
|
||||||
|
Device.ClearTweens(this, AudioTweenProperty.Volume);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Volume, easingFunction, Volume, targetValue, duration, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetVolume(float targetValue, float delayTime, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Volume, easingFunction, Volume, targetValue, duration, delayTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFilterFrequency(float targetValue)
|
||||||
|
{
|
||||||
|
FilterFrequency = targetValue;
|
||||||
|
Device.ClearTweens(this, AudioTweenProperty.FilterFrequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFilterFrequency(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.FilterFrequency, easingFunction, FilterFrequency, targetValue, duration, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFilterFrequency(float targetValue, float delayTime, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.FilterFrequency, easingFunction, FilterFrequency, targetValue, duration, delayTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetFilterOneOverQ(float targetValue)
|
||||||
|
{
|
||||||
|
FilterOneOverQ = targetValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetReverb(float targetValue)
|
||||||
|
{
|
||||||
|
Reverb = targetValue;
|
||||||
|
Device.ClearTweens(this, AudioTweenProperty.Reverb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetReverb(float targetValue, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Reverb, easingFunction, Volume, targetValue, duration, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetReverb(float targetValue, float delayTime, float duration, EasingFunction easingFunction)
|
||||||
|
{
|
||||||
|
Device.CreateTween(this, AudioTweenProperty.Reverb, easingFunction, Volume, targetValue, duration, delayTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void SetSends(Voice send)
|
||||||
|
{
|
||||||
|
FAudio.FAudioSendDescriptor* sendDesc = stackalloc FAudio.FAudioSendDescriptor[1];
|
||||||
|
sendDesc[0].Flags = 0;
|
||||||
|
sendDesc[0].pOutputVoice = send.Handle;
|
||||||
|
|
||||||
|
var sends = new FAudio.FAudioVoiceSends();
|
||||||
|
sends.SendCount = 1;
|
||||||
|
sends.pSends = (nint) sendDesc;
|
||||||
|
|
||||||
|
FAudio.FAudioVoice_SetOutputVoices(
|
||||||
|
Handle,
|
||||||
|
ref sends
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void SetSends(Voice sendOne, Voice sendTwo)
|
||||||
|
{
|
||||||
|
var sendDesc = stackalloc FAudio.FAudioSendDescriptor[2];
|
||||||
|
sendDesc[0].Flags = 0;
|
||||||
|
sendDesc[0].pOutputVoice = sendOne.Handle;
|
||||||
|
sendDesc[1].Flags = 0;
|
||||||
|
sendDesc[1].pOutputVoice = sendTwo.Handle;
|
||||||
|
|
||||||
|
var sends = new FAudio.FAudioVoiceSends();
|
||||||
|
sends.SendCount = 2;
|
||||||
|
sends.pSends = (nint) sendDesc;
|
||||||
|
|
||||||
|
FAudio.FAudioVoice_SetOutputVoices(
|
||||||
|
Handle,
|
||||||
|
ref sends
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public unsafe void SetReverbEffectChain(ReverbEffect reverbEffect)
|
||||||
|
{
|
||||||
|
SetSends(Device.MasteringVoice, reverbEffect);
|
||||||
|
ReverbEffect = reverbEffect;
|
||||||
|
}
|
||||||
|
|
||||||
|
private unsafe void InitDSPSettings(uint srcChannels)
|
||||||
|
{
|
||||||
|
dspSettings = new FAudio.F3DAUDIO_DSP_SETTINGS();
|
||||||
|
dspSettings.DopplerFactor = 1f;
|
||||||
|
dspSettings.SrcChannelCount = srcChannels;
|
||||||
|
dspSettings.DstChannelCount = Device.DeviceDetails.OutputFormat.Format.nChannels;
|
||||||
|
|
||||||
|
nuint memsize = (
|
||||||
|
4 *
|
||||||
|
dspSettings.SrcChannelCount *
|
||||||
|
dspSettings.DstChannelCount
|
||||||
|
);
|
||||||
|
|
||||||
|
dspSettings.pMatrixCoefficients = (nint) NativeMemory.Alloc(memsize);
|
||||||
|
byte* memPtr = (byte*) dspSettings.pMatrixCoefficients;
|
||||||
|
for (uint i = 0; i < memsize; i += 1)
|
||||||
|
{
|
||||||
|
memPtr[i] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetPanMatrixCoefficients();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdatePitch()
|
||||||
|
{
|
||||||
|
float doppler;
|
||||||
|
float dopplerScale = Device.DopplerScale;
|
||||||
|
if (!Is3D || dopplerScale == 0.0f)
|
||||||
|
{
|
||||||
|
doppler = 1.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
doppler = dspSettings.DopplerFactor * dopplerScale;
|
||||||
|
}
|
||||||
|
|
||||||
|
FAudio.FAudioSourceVoice_SetFrequencyRatio(
|
||||||
|
Handle,
|
||||||
|
(float) System.Math.Pow(2.0, pitch) * doppler,
|
||||||
|
0
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Taken from https://github.com/FNA-XNA/FNA/blob/master/src/Audio/SoundEffectInstance.cs
|
||||||
|
private unsafe void SetPanMatrixCoefficients()
|
||||||
|
{
|
||||||
|
/* Two major things to notice:
|
||||||
|
* 1. The spec assumes any speaker count >= 2 has Front Left/Right.
|
||||||
|
* 2. Stereo panning is WAY more complicated than you think.
|
||||||
|
* The main thing is that hard panning does NOT eliminate an
|
||||||
|
* entire channel; the two channels are blended on each side.
|
||||||
|
* -flibit
|
||||||
|
*/
|
||||||
|
float* outputMatrix = (float*) dspSettings.pMatrixCoefficients;
|
||||||
|
if (dspSettings.SrcChannelCount == 1)
|
||||||
|
{
|
||||||
|
if (dspSettings.DstChannelCount == 1)
|
||||||
|
{
|
||||||
|
outputMatrix[0] = 1.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
outputMatrix[0] = (pan > 0.0f) ? (1.0f - pan) : 1.0f;
|
||||||
|
outputMatrix[1] = (pan < 0.0f) ? (1.0f + pan) : 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dspSettings.DstChannelCount == 1)
|
||||||
|
{
|
||||||
|
outputMatrix[0] = 1.0f;
|
||||||
|
outputMatrix[1] = 1.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (pan <= 0.0f)
|
||||||
|
{
|
||||||
|
// Left speaker blends left/right channels
|
||||||
|
outputMatrix[0] = 0.5f * pan + 1.0f;
|
||||||
|
outputMatrix[1] = 0.5f * -pan;
|
||||||
|
// Right speaker gets less of the right channel
|
||||||
|
outputMatrix[2] = 0.0f;
|
||||||
|
outputMatrix[3] = pan + 1.0f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Left speaker gets less of the left channel
|
||||||
|
outputMatrix[0] = -pan + 1.0f;
|
||||||
|
outputMatrix[1] = 0.0f;
|
||||||
|
// Right speaker blends right/left channels
|
||||||
|
outputMatrix[2] = 0.5f * pan;
|
||||||
|
outputMatrix[3] = 0.5f * -pan + 1.0f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected unsafe override void Destroy()
|
||||||
|
{
|
||||||
|
FAudio.FAudioVoice_DestroyVoice(Handle);
|
||||||
|
|
||||||
|
NativeMemory.Free((void*) dspSettings.pMatrixCoefficients);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue