add ReverbEffect

pull/47/head
cosmonaut 2023-03-01 17:47:09 -08:00
parent 472da0edd2
commit f8b14ea94f
5 changed files with 226 additions and 172 deletions

View File

@ -10,7 +10,6 @@ namespace MoonWorks.Audio
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 float CurveDistanceScalar = 1f; public float CurveDistanceScalar = 1f;
public float DopplerScale = 1f; public float DopplerScale = 1f;
@ -27,8 +26,6 @@ namespace MoonWorks.Audio
} }
} }
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>>();
@ -108,104 +105,6 @@ namespace MoonWorks.Audio
SpeedOfSound, SpeedOfSound,
Handle3D Handle3D
); );
/* Init reverb */
IntPtr reverbVoice;
IntPtr reverb;
FAudio.FAudioCreateReverb(out reverb, 0);
IntPtr chainPtr;
chainPtr = Marshal.AllocHGlobal(
Marshal.SizeOf<FAudio.FAudioEffectChain>()
);
FAudio.FAudioEffectChain* reverbChain = (FAudio.FAudioEffectChain*) chainPtr;
reverbChain->EffectCount = 1;
reverbChain->pEffectDescriptors = Marshal.AllocHGlobal(
Marshal.SizeOf<FAudio.FAudioEffectDescriptor>()
);
FAudio.FAudioEffectDescriptor* reverbDescriptor =
(FAudio.FAudioEffectDescriptor*) reverbChain->pEffectDescriptors;
reverbDescriptor->InitialState = 1;
reverbDescriptor->OutputChannels = (uint) (
(DeviceDetails.OutputFormat.Format.nChannels == 6) ? 6 : 1
);
reverbDescriptor->pEffect = reverb;
FAudio.FAudio_CreateSubmixVoice(
Handle,
out reverbVoice,
1, /* omnidirectional reverb */
DeviceDetails.OutputFormat.Format.nSamplesPerSec,
0,
0,
IntPtr.Zero,
chainPtr
);
FAudio.FAPOBase_Release(reverb);
Marshal.FreeHGlobal(reverbChain->pEffectDescriptors);
Marshal.FreeHGlobal(chainPtr);
ReverbVoice = reverbVoice;
/* Init reverb params */
// Defaults based on FAUDIOFX_I3DL2_PRESET_GENERIC
IntPtr reverbParamsPtr = Marshal.AllocHGlobal(
Marshal.SizeOf<FAudio.FAudioFXReverbParameters>()
);
FAudio.FAudioFXReverbParameters* reverbParams = (FAudio.FAudioFXReverbParameters*) reverbParamsPtr;
reverbParams->WetDryMix = 100.0f;
reverbParams->ReflectionsDelay = 7;
reverbParams->ReverbDelay = 11;
reverbParams->RearDelay = FAudio.FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
reverbParams->PositionLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION;
reverbParams->PositionRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION;
reverbParams->PositionMatrixLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
reverbParams->PositionMatrixRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
reverbParams->EarlyDiffusion = 15;
reverbParams->LateDiffusion = 15;
reverbParams->LowEQGain = 8;
reverbParams->LowEQCutoff = 4;
reverbParams->HighEQGain = 8;
reverbParams->HighEQCutoff = 6;
reverbParams->RoomFilterFreq = 5000f;
reverbParams->RoomFilterMain = -10f;
reverbParams->RoomFilterHF = -1f;
reverbParams->ReflectionsGain = -26.0200005f;
reverbParams->ReverbGain = 10.0f;
reverbParams->DecayTime = 1.49000001f;
reverbParams->Density = 100.0f;
reverbParams->RoomSize = FAudio.FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
FAudio.FAudioVoice_SetEffectParameters(
ReverbVoice,
0,
reverbParamsPtr,
(uint) Marshal.SizeOf<FAudio.FAudioFXReverbParameters>(),
0
);
Marshal.FreeHGlobal(reverbParamsPtr);
/* Init reverb sends */
ReverbSends = new FAudio.FAudioVoiceSends
{
SendCount = 2,
pSends = Marshal.AllocHGlobal(
2 * Marshal.SizeOf<FAudio.FAudioSendDescriptor>()
)
};
FAudio.FAudioSendDescriptor* sendDesc = (FAudio.FAudioSendDescriptor*) ReverbSends.pSends;
sendDesc[0].Flags = 0;
sendDesc[0].pOutputVoice = MasteringVoice;
sendDesc[1].Flags = 0;
sendDesc[1].pOutputVoice = ReverbVoice;
} }
internal void Update() internal void Update()
@ -268,7 +167,6 @@ namespace MoonWorks.Audio
resources.Clear(); resources.Clear();
} }
FAudio.FAudioVoice_DestroyVoice(ReverbVoice);
FAudio.FAudioVoice_DestroyVoice(MasteringVoice); FAudio.FAudioVoice_DestroyVoice(MasteringVoice);
FAudio.FAudio_Release(Handle); FAudio.FAudio_Release(Handle);

128
src/Audio/ReverbEffect.cs Normal file
View File

@ -0,0 +1,128 @@
using System;
using System.Runtime.InteropServices;
namespace MoonWorks.Audio
{
// sound instances can send their audio to this voice to add reverb
public unsafe class ReverbEffect : IDisposable
{
private IntPtr voice;
public IntPtr Voice => voice;
private bool disposedValue;
public ReverbEffect(AudioDevice audioDevice)
{
/* Init reverb */
IntPtr reverb;
FAudio.FAudioCreateReverb(out reverb, 0);
IntPtr chainPtr;
chainPtr = (nint) NativeMemory.Alloc(
(nuint) Marshal.SizeOf<FAudio.FAudioEffectChain>()
);
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);
NativeMemory.Free((void*) reverbChain->pEffectDescriptors);
NativeMemory.Free((void*) chainPtr);
/* Init reverb params */
// Defaults based on FAUDIOFX_I3DL2_PRESET_GENERIC
FAudio.FAudioFXReverbParameters reverbParams;
reverbParams.WetDryMix = 100.0f;
reverbParams.ReflectionsDelay = 7;
reverbParams.ReverbDelay = 11;
reverbParams.RearDelay = FAudio.FAUDIOFX_REVERB_DEFAULT_REAR_DELAY;
reverbParams.PositionLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION;
reverbParams.PositionRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION;
reverbParams.PositionMatrixLeft = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
reverbParams.PositionMatrixRight = FAudio.FAUDIOFX_REVERB_DEFAULT_POSITION_MATRIX;
reverbParams.EarlyDiffusion = 15;
reverbParams.LateDiffusion = 15;
reverbParams.LowEQGain = 8;
reverbParams.LowEQCutoff = 4;
reverbParams.HighEQGain = 8;
reverbParams.HighEQCutoff = 6;
reverbParams.RoomFilterFreq = 5000f;
reverbParams.RoomFilterMain = -10f;
reverbParams.RoomFilterHF = -1f;
reverbParams.ReflectionsGain = -26.0200005f;
reverbParams.ReverbGain = 10.0f;
reverbParams.DecayTime = 1.49000001f;
reverbParams.Density = 100.0f;
reverbParams.RoomSize = FAudio.FAUDIOFX_REVERB_DEFAULT_ROOM_SIZE;
SetParams(reverbParams);
}
public void SetParams(in FAudio.FAudioFXReverbParameters reverbParams)
{
fixed (FAudio.FAudioFXReverbParameters* reverbParamsPtr = &reverbParams)
{
FAudio.FAudioVoice_SetEffectParameters(
voice,
0,
(nint) reverbParamsPtr,
(uint) Marshal.SizeOf<FAudio.FAudioFXReverbParameters>(),
0
);
}
}
protected virtual void Dispose(bool disposing)
{
if (!disposedValue)
{
if (disposing)
{
// TODO: dispose managed state (managed objects)
}
FAudio.FAudioVoice_DestroyVoice(voice);
disposedValue = true;
}
}
~ReverbEffect()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: false);
}
public void Dispose()
{
// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}
}

View File

@ -5,11 +5,14 @@ namespace MoonWorks.Audio
{ {
public abstract class SoundInstance : AudioResource public abstract class SoundInstance : AudioResource
{ {
internal IntPtr Handle; internal IntPtr Voice;
internal FAudio.FAudioWaveFormatEx Format; internal FAudio.FAudioWaveFormatEx Format;
protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings; protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings;
private ReverbEffect ReverbEffect;
private FAudio.FAudioVoiceSends ReverbSends;
public bool Is3D { get; protected set; } public bool Is3D { get; protected set; }
public virtual SoundState State { get; protected set; } public virtual SoundState State { get; protected set; }
@ -35,7 +38,7 @@ namespace MoonWorks.Audio
SetPanMatrixCoefficients(); SetPanMatrixCoefficients();
FAudio.FAudioVoice_SetOutputMatrix( FAudio.FAudioVoice_SetOutputMatrix(
Handle, Voice,
Device.MasteringVoice, Device.MasteringVoice,
dspSettings.SrcChannelCount, dspSettings.SrcChannelCount,
dspSettings.DstChannelCount, dspSettings.DstChannelCount,
@ -63,33 +66,7 @@ namespace MoonWorks.Audio
set set
{ {
volume = value; volume = value;
FAudio.FAudioVoice_SetVolume(Handle, volume, 0); FAudio.FAudioVoice_SetVolume(Voice, volume, 0);
}
}
private float reverb;
public unsafe float Reverb
{
get => reverb;
set
{
reverb = value;
float* outputMatrix = (float*) dspSettings.pMatrixCoefficients;
outputMatrix[0] = reverb;
if (dspSettings.SrcChannelCount == 2)
{
outputMatrix[1] = reverb;
}
FAudio.FAudioVoice_SetOutputMatrix(
Handle,
Device.ReverbVoice,
dspSettings.SrcChannelCount,
1,
dspSettings.pMatrixCoefficients,
0
);
} }
} }
@ -112,7 +89,7 @@ namespace MoonWorks.Audio
filterParameters.Frequency = value; filterParameters.Frequency = value;
FAudio.FAudioVoice_SetFilterParameters( FAudio.FAudioVoice_SetFilterParameters(
Handle, Voice,
ref filterParameters, ref filterParameters,
0 0
); );
@ -128,7 +105,7 @@ namespace MoonWorks.Audio
filterParameters.OneOverQ = value; filterParameters.OneOverQ = value;
FAudio.FAudioVoice_SetFilterParameters( FAudio.FAudioVoice_SetFilterParameters(
Handle, Voice,
ref filterParameters, ref filterParameters,
0 0
); );
@ -168,13 +145,49 @@ namespace MoonWorks.Audio
} }
FAudio.FAudioVoice_SetFilterParameters( FAudio.FAudioVoice_SetFilterParameters(
Handle, Voice,
ref filterParameters, ref filterParameters,
0 0
); );
} }
} }
private float reverb;
public unsafe float Reverb
{
get => reverb;
set
{
if (ReverbEffect != null)
{
reverb = value;
float* outputMatrix = (float*) dspSettings.pMatrixCoefficients;
outputMatrix[0] = reverb;
if (dspSettings.SrcChannelCount == 2)
{
outputMatrix[1] = reverb;
}
FAudio.FAudioVoice_SetOutputMatrix(
Voice,
ReverbEffect.Voice,
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 SoundInstance( public SoundInstance(
AudioDevice device, AudioDevice device,
ushort formatTag, ushort formatTag,
@ -198,7 +211,7 @@ namespace MoonWorks.Audio
FAudio.FAudio_CreateSourceVoice( FAudio.FAudio_CreateSourceVoice(
Device.Handle, Device.Handle,
out Handle, out Voice,
ref Format, ref Format,
FAudio.FAUDIO_VOICE_USEFILTER, FAudio.FAUDIO_VOICE_USEFILTER,
FAudio.FAUDIO_DEFAULT_FREQ_RATIO, FAudio.FAUDIO_DEFAULT_FREQ_RATIO,
@ -207,7 +220,7 @@ namespace MoonWorks.Audio
IntPtr.Zero IntPtr.Zero
); );
if (Handle == IntPtr.Zero) if (Voice == IntPtr.Zero)
{ {
Logger.LogError("SoundInstance failed to initialize!"); Logger.LogError("SoundInstance failed to initialize!");
return; return;
@ -215,14 +228,6 @@ namespace MoonWorks.Audio
InitDSPSettings(Format.nChannels); InitDSPSettings(Format.nChannels);
// FIXME: not everything should be running through reverb...
/*
FAudio.FAudioVoice_SetOutputVoices(
Handle,
ref Device.ReverbSends
);
*/
State = SoundState.Stopped; State = SoundState.Stopped;
} }
@ -243,7 +248,7 @@ namespace MoonWorks.Audio
UpdatePitch(); UpdatePitch();
FAudio.FAudioVoice_SetOutputMatrix( FAudio.FAudioVoice_SetOutputMatrix(
Handle, Voice,
Device.MasteringVoice, Device.MasteringVoice,
dspSettings.SrcChannelCount, dspSettings.SrcChannelCount,
dspSettings.DstChannelCount, dspSettings.DstChannelCount,
@ -252,30 +257,50 @@ namespace MoonWorks.Audio
); );
} }
public unsafe void ApplyReverb(ReverbEffect reverbEffect)
{
ReverbSends = new FAudio.FAudioVoiceSends();
ReverbSends.SendCount = 2;
ReverbSends.pSends = (nint) NativeMemory.Alloc((nuint) (2 * Marshal.SizeOf<FAudio.FAudioSendDescriptor>()));
FAudio.FAudioSendDescriptor* sendDesc = (FAudio.FAudioSendDescriptor*) ReverbSends.pSends;
sendDesc[0].Flags = 0;
sendDesc[0].pOutputVoice = Device.MasteringVoice;
sendDesc[1].Flags = 0;
sendDesc[1].pOutputVoice = reverbEffect.Voice;
FAudio.FAudioVoice_SetOutputVoices(
Voice,
ref ReverbSends
);
ReverbEffect = reverbEffect;
}
public abstract void Play(); public abstract void Play();
public abstract void QueueSyncPlay(); public abstract void QueueSyncPlay();
public abstract void Pause(); public abstract void Pause();
public abstract void Stop(); public abstract void Stop();
public abstract void StopImmediate(); public abstract void StopImmediate();
private void InitDSPSettings(uint srcChannels) private unsafe 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 = ( nuint memsize = (
4 * 4 *
(int) dspSettings.SrcChannelCount * dspSettings.SrcChannelCount *
(int) dspSettings.DstChannelCount dspSettings.DstChannelCount
); );
dspSettings.pMatrixCoefficients = Marshal.AllocHGlobal(memsize); dspSettings.pMatrixCoefficients = (nint) NativeMemory.Alloc(memsize);
unsafe unsafe
{ {
byte* memPtr = (byte*) dspSettings.pMatrixCoefficients; byte* memPtr = (byte*) dspSettings.pMatrixCoefficients;
for (int i = 0; i < memsize; i += 1) for (uint i = 0; i < memsize; i += 1)
{ {
memPtr[i] = 0; memPtr[i] = 0;
} }
@ -297,7 +322,7 @@ namespace MoonWorks.Audio
} }
FAudio.FAudioSourceVoice_SetFrequencyRatio( FAudio.FAudioSourceVoice_SetFrequencyRatio(
Handle, Voice,
(float) System.Math.Pow(2.0, pitch) * doppler, (float) System.Math.Pow(2.0, pitch) * doppler,
0 0
); );
@ -357,11 +382,16 @@ namespace MoonWorks.Audio
} }
} }
protected override void Destroy() protected unsafe override void Destroy()
{ {
StopImmediate(); StopImmediate();
FAudio.FAudioVoice_DestroyVoice(Handle); FAudio.FAudioVoice_DestroyVoice(Voice);
Marshal.FreeHGlobal(dspSettings.pMatrixCoefficients); NativeMemory.Free((void*) dspSettings.pMatrixCoefficients);
if (ReverbEffect != null)
{
NativeMemory.Free((void*) ReverbSends.pSends);
}
} }
} }
} }

View File

@ -14,7 +14,7 @@ namespace MoonWorks.Audio
get get
{ {
FAudio.FAudioSourceVoice_GetState( FAudio.FAudioSourceVoice_GetState(
Handle, Voice,
out var state, out var state,
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
); );
@ -71,12 +71,12 @@ namespace MoonWorks.Audio
} }
FAudio.FAudioSourceVoice_SubmitSourceBuffer( FAudio.FAudioSourceVoice_SubmitSourceBuffer(
Handle, Voice,
ref Parent.Handle, ref Parent.Handle,
IntPtr.Zero IntPtr.Zero
); );
FAudio.FAudioSourceVoice_Start(Handle, 0, operationSet); FAudio.FAudioSourceVoice_Start(Voice, 0, operationSet);
State = SoundState.Playing; State = SoundState.Playing;
} }
@ -84,21 +84,21 @@ namespace MoonWorks.Audio
{ {
if (State == SoundState.Paused) if (State == SoundState.Paused)
{ {
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0); FAudio.FAudioSourceVoice_Stop(Voice, 0, 0);
State = SoundState.Paused; State = SoundState.Paused;
} }
} }
public override void Stop() public override void Stop()
{ {
FAudio.FAudioSourceVoice_ExitLoop(Handle, 0); FAudio.FAudioSourceVoice_ExitLoop(Voice, 0);
State = SoundState.Stopped; State = SoundState.Stopped;
} }
public override void StopImmediate() public override void StopImmediate()
{ {
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0); FAudio.FAudioSourceVoice_Stop(Voice, 0, 0);
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle); FAudio.FAudioSourceVoice_FlushSourceBuffers(Voice);
State = SoundState.Stopped; State = SoundState.Stopped;
} }
@ -106,8 +106,8 @@ namespace MoonWorks.Audio
{ {
if (State == SoundState.Playing) if (State == SoundState.Playing)
{ {
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0); FAudio.FAudioSourceVoice_Stop(Voice, 0, 0);
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle); FAudio.FAudioSourceVoice_FlushSourceBuffers(Voice);
} }
Parent.Handle.PlayBegin = sampleFrame; Parent.Handle.PlayBegin = sampleFrame;
@ -123,11 +123,9 @@ namespace MoonWorks.Audio
Pan = 0; Pan = 0;
Pitch = 0; Pitch = 0;
Volume = 1; Volume = 1;
Reverb = 0;
Loop = false; Loop = false;
Is3D = false; Is3D = false;
FilterType = FilterType.None; FilterType = FilterType.None;
Reverb = 0;
} }
} }
} }

View File

@ -54,14 +54,14 @@ namespace MoonWorks.Audio
State = SoundState.Playing; State = SoundState.Playing;
Update(); Update();
FAudio.FAudioSourceVoice_Start(Handle, 0, operationSet); FAudio.FAudioSourceVoice_Start(Voice, 0, operationSet);
} }
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(Voice, 0, 0);
State = SoundState.Paused; State = SoundState.Paused;
} }
} }
@ -73,8 +73,8 @@ namespace MoonWorks.Audio
public override void StopImmediate() public override void StopImmediate()
{ {
FAudio.FAudioSourceVoice_Stop(Handle, 0, 0); FAudio.FAudioSourceVoice_Stop(Voice, 0, 0);
FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle); FAudio.FAudioSourceVoice_FlushSourceBuffers(Voice);
ClearBuffers(); ClearBuffers();
State = SoundState.Stopped; State = SoundState.Stopped;
@ -88,7 +88,7 @@ namespace MoonWorks.Audio
} }
FAudio.FAudioSourceVoice_GetState( FAudio.FAudioSourceVoice_GetState(
Handle, Voice,
out var state, out var state,
FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED
); );
@ -136,7 +136,7 @@ namespace MoonWorks.Audio
}; };
FAudio.FAudioSourceVoice_SubmitSourceBuffer( FAudio.FAudioSourceVoice_SubmitSourceBuffer(
Handle, Voice,
ref buf, ref buf,
IntPtr.Zero IntPtr.Zero
); );