diff --git a/src/Audio/AudioDevice.cs b/src/Audio/AudioDevice.cs index 6469955..aec8b72 100644 --- a/src/Audio/AudioDevice.cs +++ b/src/Audio/AudioDevice.cs @@ -10,7 +10,6 @@ namespace MoonWorks.Audio public byte[] Handle3D { get; } public IntPtr MasteringVoice { get; } public FAudio.FAudioDeviceDetails DeviceDetails { get; } - public IntPtr ReverbVoice { get; } public float CurveDistanceScalar = 1f; public float DopplerScale = 1f; @@ -27,8 +26,6 @@ namespace MoonWorks.Audio } } - internal FAudio.FAudioVoiceSends ReverbSends; - private readonly List> resources = new List>(); private readonly List> streamingSounds = new List>(); @@ -108,104 +105,6 @@ namespace MoonWorks.Audio SpeedOfSound, Handle3D ); - - /* Init reverb */ - - IntPtr reverbVoice; - - IntPtr reverb; - FAudio.FAudioCreateReverb(out reverb, 0); - - IntPtr chainPtr; - chainPtr = Marshal.AllocHGlobal( - Marshal.SizeOf() - ); - - FAudio.FAudioEffectChain* reverbChain = (FAudio.FAudioEffectChain*) chainPtr; - reverbChain->EffectCount = 1; - reverbChain->pEffectDescriptors = Marshal.AllocHGlobal( - Marshal.SizeOf() - ); - - 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* 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(), - 0 - ); - Marshal.FreeHGlobal(reverbParamsPtr); - - /* Init reverb sends */ - - ReverbSends = new FAudio.FAudioVoiceSends - { - SendCount = 2, - pSends = Marshal.AllocHGlobal( - 2 * Marshal.SizeOf() - ) - }; - 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() @@ -268,7 +167,6 @@ namespace MoonWorks.Audio resources.Clear(); } - FAudio.FAudioVoice_DestroyVoice(ReverbVoice); FAudio.FAudioVoice_DestroyVoice(MasteringVoice); FAudio.FAudio_Release(Handle); diff --git a/src/Audio/ReverbEffect.cs b/src/Audio/ReverbEffect.cs new file mode 100644 index 0000000..303bda7 --- /dev/null +++ b/src/Audio/ReverbEffect.cs @@ -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* 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); + } + } +} diff --git a/src/Audio/SoundInstance.cs b/src/Audio/SoundInstance.cs index 259df3b..b82fbf2 100644 --- a/src/Audio/SoundInstance.cs +++ b/src/Audio/SoundInstance.cs @@ -5,11 +5,14 @@ namespace MoonWorks.Audio { public abstract class SoundInstance : AudioResource { - internal IntPtr Handle; + internal IntPtr Voice; internal FAudio.FAudioWaveFormatEx Format; protected FAudio.F3DAUDIO_DSP_SETTINGS dspSettings; + private ReverbEffect ReverbEffect; + private FAudio.FAudioVoiceSends ReverbSends; + public bool Is3D { get; protected set; } public virtual SoundState State { get; protected set; } @@ -35,7 +38,7 @@ namespace MoonWorks.Audio SetPanMatrixCoefficients(); FAudio.FAudioVoice_SetOutputMatrix( - Handle, + Voice, Device.MasteringVoice, dspSettings.SrcChannelCount, dspSettings.DstChannelCount, @@ -63,33 +66,7 @@ namespace MoonWorks.Audio set { volume = value; - FAudio.FAudioVoice_SetVolume(Handle, 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 - ); + FAudio.FAudioVoice_SetVolume(Voice, volume, 0); } } @@ -112,7 +89,7 @@ namespace MoonWorks.Audio filterParameters.Frequency = value; FAudio.FAudioVoice_SetFilterParameters( - Handle, + Voice, ref filterParameters, 0 ); @@ -128,7 +105,7 @@ namespace MoonWorks.Audio filterParameters.OneOverQ = value; FAudio.FAudioVoice_SetFilterParameters( - Handle, + Voice, ref filterParameters, 0 ); @@ -168,13 +145,49 @@ namespace MoonWorks.Audio } FAudio.FAudioVoice_SetFilterParameters( - Handle, + Voice, ref filterParameters, 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( AudioDevice device, ushort formatTag, @@ -198,7 +211,7 @@ namespace MoonWorks.Audio FAudio.FAudio_CreateSourceVoice( Device.Handle, - out Handle, + out Voice, ref Format, FAudio.FAUDIO_VOICE_USEFILTER, FAudio.FAUDIO_DEFAULT_FREQ_RATIO, @@ -207,7 +220,7 @@ namespace MoonWorks.Audio IntPtr.Zero ); - if (Handle == IntPtr.Zero) + if (Voice == IntPtr.Zero) { Logger.LogError("SoundInstance failed to initialize!"); return; @@ -215,14 +228,6 @@ namespace MoonWorks.Audio InitDSPSettings(Format.nChannels); - // FIXME: not everything should be running through reverb... - /* - FAudio.FAudioVoice_SetOutputVoices( - Handle, - ref Device.ReverbSends - ); - */ - State = SoundState.Stopped; } @@ -243,7 +248,7 @@ namespace MoonWorks.Audio UpdatePitch(); FAudio.FAudioVoice_SetOutputMatrix( - Handle, + Voice, Device.MasteringVoice, dspSettings.SrcChannelCount, 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* 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 QueueSyncPlay(); public abstract void Pause(); public abstract void Stop(); public abstract void StopImmediate(); - private void InitDSPSettings(uint srcChannels) + 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; - int memsize = ( + nuint memsize = ( 4 * - (int) dspSettings.SrcChannelCount * - (int) dspSettings.DstChannelCount + dspSettings.SrcChannelCount * + dspSettings.DstChannelCount ); - dspSettings.pMatrixCoefficients = Marshal.AllocHGlobal(memsize); + dspSettings.pMatrixCoefficients = (nint) NativeMemory.Alloc(memsize); unsafe { byte* memPtr = (byte*) dspSettings.pMatrixCoefficients; - for (int i = 0; i < memsize; i += 1) + for (uint i = 0; i < memsize; i += 1) { memPtr[i] = 0; } @@ -297,7 +322,7 @@ namespace MoonWorks.Audio } FAudio.FAudioSourceVoice_SetFrequencyRatio( - Handle, + Voice, (float) System.Math.Pow(2.0, pitch) * doppler, 0 ); @@ -357,11 +382,16 @@ namespace MoonWorks.Audio } } - protected override void Destroy() + protected unsafe override void Destroy() { StopImmediate(); - FAudio.FAudioVoice_DestroyVoice(Handle); - Marshal.FreeHGlobal(dspSettings.pMatrixCoefficients); + FAudio.FAudioVoice_DestroyVoice(Voice); + NativeMemory.Free((void*) dspSettings.pMatrixCoefficients); + + if (ReverbEffect != null) + { + NativeMemory.Free((void*) ReverbSends.pSends); + } } } } diff --git a/src/Audio/StaticSoundInstance.cs b/src/Audio/StaticSoundInstance.cs index b497a96..9b0a445 100644 --- a/src/Audio/StaticSoundInstance.cs +++ b/src/Audio/StaticSoundInstance.cs @@ -14,7 +14,7 @@ namespace MoonWorks.Audio get { FAudio.FAudioSourceVoice_GetState( - Handle, + Voice, out var state, FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED ); @@ -71,12 +71,12 @@ namespace MoonWorks.Audio } FAudio.FAudioSourceVoice_SubmitSourceBuffer( - Handle, + Voice, ref Parent.Handle, IntPtr.Zero ); - FAudio.FAudioSourceVoice_Start(Handle, 0, operationSet); + FAudio.FAudioSourceVoice_Start(Voice, 0, operationSet); State = SoundState.Playing; } @@ -84,21 +84,21 @@ namespace MoonWorks.Audio { if (State == SoundState.Paused) { - FAudio.FAudioSourceVoice_Stop(Handle, 0, 0); + FAudio.FAudioSourceVoice_Stop(Voice, 0, 0); State = SoundState.Paused; } } public override void Stop() { - FAudio.FAudioSourceVoice_ExitLoop(Handle, 0); + FAudio.FAudioSourceVoice_ExitLoop(Voice, 0); State = SoundState.Stopped; } public override void StopImmediate() { - FAudio.FAudioSourceVoice_Stop(Handle, 0, 0); - FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle); + FAudio.FAudioSourceVoice_Stop(Voice, 0, 0); + FAudio.FAudioSourceVoice_FlushSourceBuffers(Voice); State = SoundState.Stopped; } @@ -106,8 +106,8 @@ namespace MoonWorks.Audio { if (State == SoundState.Playing) { - FAudio.FAudioSourceVoice_Stop(Handle, 0, 0); - FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle); + FAudio.FAudioSourceVoice_Stop(Voice, 0, 0); + FAudio.FAudioSourceVoice_FlushSourceBuffers(Voice); } Parent.Handle.PlayBegin = sampleFrame; @@ -123,11 +123,9 @@ namespace MoonWorks.Audio Pan = 0; Pitch = 0; Volume = 1; - Reverb = 0; Loop = false; Is3D = false; FilterType = FilterType.None; - Reverb = 0; } } } diff --git a/src/Audio/StreamingSound.cs b/src/Audio/StreamingSound.cs index e232fef..6d88f68 100644 --- a/src/Audio/StreamingSound.cs +++ b/src/Audio/StreamingSound.cs @@ -54,14 +54,14 @@ namespace MoonWorks.Audio State = SoundState.Playing; Update(); - FAudio.FAudioSourceVoice_Start(Handle, 0, operationSet); + FAudio.FAudioSourceVoice_Start(Voice, 0, operationSet); } public override void Pause() { if (State == SoundState.Playing) { - FAudio.FAudioSourceVoice_Stop(Handle, 0, 0); + FAudio.FAudioSourceVoice_Stop(Voice, 0, 0); State = SoundState.Paused; } } @@ -73,8 +73,8 @@ namespace MoonWorks.Audio public override void StopImmediate() { - FAudio.FAudioSourceVoice_Stop(Handle, 0, 0); - FAudio.FAudioSourceVoice_FlushSourceBuffers(Handle); + FAudio.FAudioSourceVoice_Stop(Voice, 0, 0); + FAudio.FAudioSourceVoice_FlushSourceBuffers(Voice); ClearBuffers(); State = SoundState.Stopped; @@ -88,7 +88,7 @@ namespace MoonWorks.Audio } FAudio.FAudioSourceVoice_GetState( - Handle, + Voice, out var state, FAudio.FAUDIO_VOICE_NOSAMPLESPLAYED ); @@ -136,7 +136,7 @@ namespace MoonWorks.Audio }; FAudio.FAudioSourceVoice_SubmitSourceBuffer( - Handle, + Voice, ref buf, IntPtr.Zero );