diff --git a/Nautilus/Patchers/CustomSoundPatcher.cs b/Nautilus/Patchers/CustomSoundPatcher.cs index 7d4a2de7..3f359ffe 100644 --- a/Nautilus/Patchers/CustomSoundPatcher.cs +++ b/Nautilus/Patchers/CustomSoundPatcher.cs @@ -1,24 +1,29 @@ +using System; using System.Collections.Generic; using FMOD; using FMOD.Studio; using FMODUnity; using HarmonyLib; +using Nautilus.Extensions; using Nautilus.FMod.Interfaces; using Nautilus.Handlers; using Nautilus.Utility; using UnityEngine; using UnityEngine.Playables; +using STOP_MODE = FMOD.Studio.STOP_MODE; namespace Nautilus.Patchers; internal class CustomSoundPatcher { internal record struct AttachedChannel(Channel Channel, Transform Transform); + internal record struct FadeInfo(Sound Sound, float Seconds); internal static readonly SelfCheckingDictionary CustomSounds = new("CustomSounds"); internal static readonly SelfCheckingDictionary CustomSoundBuses = new("CustomSoundBuses"); internal static readonly SelfCheckingDictionary CustomFModSounds = new("CustoomFModSounds"); internal static readonly Dictionary EmitterPlayedChannels = new(); + internal static readonly Dictionary FadeOuts = new(); internal static List AttachedChannels = new(); private static readonly Dictionary PlayedChannels = new(); @@ -159,10 +164,14 @@ public static bool FMODEventPlayableBehavior_OnExit_Prefix(FMODEventPlayableBeha return false; } - if (__instance.stopType != FMODUnity.STOP_MODE.None) + if (__instance.stopType == FMODUnity.STOP_MODE.Immediate) { channel.stop(); } + else if (__instance.stopType == FMODUnity.STOP_MODE.AllowFadeout) + { + TryFadeOutBeforeStop(channel); + } PlayableBehaviorChannels.Remove(__instance); __instance.isPlayheadInside = false; @@ -440,11 +449,18 @@ public static bool FMOD_CustomEmitter_Play_Prefix(FMOD_CustomEmitter __instance) [HarmonyPatch(typeof(FMOD_CustomEmitter), nameof(FMOD_CustomEmitter.Stop))] [HarmonyPrefix] - public static bool FMOD_CustomEmitter_Stop_Prefix(FMOD_CustomEmitter __instance) + public static bool FMOD_CustomEmitter_Stop_Prefix(FMOD_CustomEmitter __instance, STOP_MODE stopMode) { if (!EmitterPlayedChannels.TryGetValue(__instance.GetInstanceID(), out var channel)) return true; - channel.stop(); + if (stopMode == STOP_MODE.IMMEDIATE) + { + channel.stop(); + } + else + { + TryFadeOutBeforeStop(channel); + } __instance._playing = false; __instance.OnStop(); @@ -486,7 +502,8 @@ public static bool FMOD_CustomEmitter_ReleaseEvent_Prefix(FMOD_CustomEmitter __i if (__instance.asset == null || !CustomSounds.ContainsKey(__instance.asset.path) && !CustomFModSounds.ContainsKey(__instance.asset.path)) return true; if (!EmitterPlayedChannels.TryGetValue(__instance.GetInstanceID(), out var channel)) return false; // known sound but not played yet - channel.stop(); + !TryFadeOutBeforeStop(channel); + EmitterPlayedChannels.Remove(__instance.GetInstanceID()); return false; @@ -761,14 +778,22 @@ public static bool FMOD_CustomEmitter_Play_Prefix(FMOD_CustomEmitter __instance) [HarmonyPatch(typeof(FMOD_CustomEmitter), nameof(FMOD_CustomEmitter.Stop))] [HarmonyPrefix] - public static bool FMOD_CustomEmitter_Stop_Prefix(FMOD_CustomEmitter __instance) + public static bool FMOD_CustomEmitter_Stop_Prefix(FMOD_CustomEmitter __instance, STOP_MODE stopMode) { if (!EmitterPlayedChannels.TryGetValue(__instance.GetInstanceID(), out Channel channel)) { return true; } - channel.stop(); + if (stopMode == STOP_MODE.ALLOWFADEOUT) + { + TryFadeOutBeforeStop(channel); + } + else + { + channel.stop(); + } + __instance._playing = false; __instance.OnStop(); @@ -827,7 +852,9 @@ public static bool FMOD_CustomEmitter_ReleaseEvent_Prefix(FMOD_CustomEmitter __i return false; // known sound but not played yet } - channel.stop(); + TryFadeOutBeforeStop(channel); + + EmitterPlayedChannels.Remove(__instance.GetInstanceID()); return false; @@ -923,4 +950,18 @@ internal static void SetChannel3DAttributes(Channel channel, Vector3 position) ATTRIBUTES_3D attributes = position.To3DAttributes(); channel.set3DAttributes(ref attributes.position, ref attributes.velocity); } + + private static bool TryFadeOutBeforeStop(Channel channel) + { + if (channel.getCurrentSound(out var sound) != RESULT.OK || !FadeOuts.TryGetValue(sound.handle, out var fadeOut)) + { + channel.stop(); + return false; + } + + RuntimeManager.CoreSystem.getSoftwareFormat(out var samplesRate, out _, out _); + channel.AddFadeOut(fadeOut.Seconds, out var dspClock); + channel.setDelay(0, dspClock + (ulong)(samplesRate * fadeOut.Seconds)); + return true; + } } \ No newline at end of file