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* reverbChain = (FAudio.FAudioEffectChain*) chainPtr; reverbChain->EffectCount = 1; reverbChain->pEffectDescriptors = (nint) NativeMemory.Alloc( (nuint) Marshal.SizeOf() ); 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(), 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); } } }