using System; using System.Collections.Concurrent; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using PEAK_AutoMic.Patches; using Photon.Pun; using Photon.Realtime; using Photon.Voice; using Photon.Voice.Unity; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: IgnoresAccessChecksTo("Assembly-CSharp")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("PEAK_AutoMic")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.1.4.0")] [assembly: AssemblyInformationalVersion("0.1.4+a9dd7c0158b67173e7536647011eedb6081b59e7")] [assembly: AssemblyProduct("PEAK_AutoMic")] [assembly: AssemblyTitle("PEAK_AutoMic")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.1.4.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace BepInEx { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class BepInAutoPluginAttribute : Attribute { public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace BepInEx.Preloader.Core.Patching { [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] [Conditional("CodeGeneration")] internal sealed class PatcherAutoPluginAttribute : Attribute { public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null) { } } } namespace PEAK_AutoMic { public class BiQuadFilter { private double a0; private double a1; private double a2; private double a3; private double a4; private float x1; private float x2; private float y1; private float y2; public float Transform(float inSample) { double num = a0 * (double)inSample + a1 * (double)x1 + a2 * (double)x2 - a3 * (double)y1 - a4 * (double)y2; x2 = x1; x1 = inSample; y2 = y1; y1 = (float)num; return y1; } private void SetCoefficients(double aa0, double aa1, double aa2, double b0, double b1, double b2) { a0 = b0 / aa0; a1 = b1 / aa0; a2 = b2 / aa0; a3 = aa1 / aa0; a4 = aa2 / aa0; } public void SetLowPassFilter(float sampleRate, float cutoffFrequency, float q) { double num = Math.PI * 2.0 * (double)cutoffFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num) / (double)(2f * q); double b = (1.0 - num2) / 2.0; double b2 = 1.0 - num2; double b3 = (1.0 - num2) / 2.0; double aa = 1.0 + num3; double aa2 = -2.0 * num2; double aa3 = 1.0 - num3; SetCoefficients(aa, aa2, aa3, b, b2, b3); } public void SetPeakingEq(float sampleRate, float centreFrequency, float q, float dbGain) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = num3 / (double)(2f * q); double num5 = Math.Pow(10.0, dbGain / 40f); double b = 1.0 + num4 * num5; double b2 = -2.0 * num2; double b3 = 1.0 - num4 * num5; double aa = 1.0 + num4 / num5; double aa2 = -2.0 * num2; double aa3 = 1.0 - num4 / num5; SetCoefficients(aa, aa2, aa3, b, b2, b3); } public void SetHighPassFilter(float sampleRate, float cutoffFrequency, float q) { double num = Math.PI * 2.0 * (double)cutoffFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num) / (double)(2f * q); double b = (1.0 + num2) / 2.0; double b2 = 0.0 - (1.0 + num2); double b3 = (1.0 + num2) / 2.0; double aa = 1.0 + num3; double aa2 = -2.0 * num2; double aa3 = 1.0 - num3; SetCoefficients(aa, aa2, aa3, b, b2, b3); } public static BiQuadFilter LowPassFilter(float sampleRate, float cutoffFrequency, float q) { BiQuadFilter biQuadFilter = new BiQuadFilter(); biQuadFilter.SetLowPassFilter(sampleRate, cutoffFrequency, q); return biQuadFilter; } public static BiQuadFilter HighPassFilter(float sampleRate, float cutoffFrequency, float q) { BiQuadFilter biQuadFilter = new BiQuadFilter(); biQuadFilter.SetHighPassFilter(sampleRate, cutoffFrequency, q); return biQuadFilter; } public static BiQuadFilter BandPassFilterConstantSkirtGain(float sampleRate, float centreFrequency, float q) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = num3 / (double)(2f * q); double b = num3 / 2.0; int num5 = 0; double b2 = (0.0 - num3) / 2.0; double num6 = 1.0 + num4; double num7 = -2.0 * num2; double num8 = 1.0 - num4; return new BiQuadFilter(num6, num7, num8, b, num5, b2); } public static BiQuadFilter BandPassFilterConstantPeakGain(float sampleRate, float centreFrequency, float q) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = num3 / (double)(2f * q); double b = num4; int num5 = 0; double b2 = 0.0 - num4; double num6 = 1.0 + num4; double num7 = -2.0 * num2; double num8 = 1.0 - num4; return new BiQuadFilter(num6, num7, num8, b, num5, b2); } public static BiQuadFilter NotchFilter(float sampleRate, float centreFrequency, float q) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = num3 / (double)(2f * q); int num5 = 1; double b = -2.0 * num2; int num6 = 1; double num7 = 1.0 + num4; double num8 = -2.0 * num2; double num9 = 1.0 - num4; return new BiQuadFilter(num7, num8, num9, num5, b, num6); } public static BiQuadFilter AllPassFilter(float sampleRate, float centreFrequency, float q) { double num = Math.PI * 2.0 * (double)centreFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = num3 / (double)(2f * q); double b = 1.0 - num4; double b2 = -2.0 * num2; double b3 = 1.0 + num4; double num5 = 1.0 + num4; double num6 = -2.0 * num2; double num7 = 1.0 - num4; return new BiQuadFilter(num5, num6, num7, b, b2, b3); } public static BiQuadFilter PeakingEQ(float sampleRate, float centreFrequency, float q, float dbGain) { BiQuadFilter biQuadFilter = new BiQuadFilter(); biQuadFilter.SetPeakingEq(sampleRate, centreFrequency, q, dbGain); return biQuadFilter; } public static BiQuadFilter LowShelf(float sampleRate, float cutoffFrequency, float shelfSlope, float dbGain) { double num = Math.PI * 2.0 * (double)cutoffFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = Math.Pow(10.0, dbGain / 40f); double num5 = num3 / 2.0 * Math.Sqrt((num4 + 1.0 / num4) * (double)(1f / shelfSlope - 1f) + 2.0); double num6 = 2.0 * Math.Sqrt(num4) * num5; double b = num4 * (num4 + 1.0 - (num4 - 1.0) * num2 + num6); double b2 = 2.0 * num4 * (num4 - 1.0 - (num4 + 1.0) * num2); double b3 = num4 * (num4 + 1.0 - (num4 - 1.0) * num2 - num6); double num7 = num4 + 1.0 + (num4 - 1.0) * num2 + num6; double num8 = -2.0 * (num4 - 1.0 + (num4 + 1.0) * num2); double num9 = num4 + 1.0 + (num4 - 1.0) * num2 - num6; return new BiQuadFilter(num7, num8, num9, b, b2, b3); } public static BiQuadFilter HighShelf(float sampleRate, float cutoffFrequency, float shelfSlope, float dbGain) { double num = Math.PI * 2.0 * (double)cutoffFrequency / (double)sampleRate; double num2 = Math.Cos(num); double num3 = Math.Sin(num); double num4 = Math.Pow(10.0, dbGain / 40f); double num5 = num3 / 2.0 * Math.Sqrt((num4 + 1.0 / num4) * (double)(1f / shelfSlope - 1f) + 2.0); double num6 = 2.0 * Math.Sqrt(num4) * num5; double b = num4 * (num4 + 1.0 + (num4 - 1.0) * num2 + num6); double b2 = -2.0 * num4 * (num4 - 1.0 + (num4 + 1.0) * num2); double b3 = num4 * (num4 + 1.0 + (num4 - 1.0) * num2 - num6); double num7 = num4 + 1.0 - (num4 - 1.0) * num2 + num6; double num8 = 2.0 * (num4 - 1.0 - (num4 + 1.0) * num2); double num9 = num4 + 1.0 - (num4 - 1.0) * num2 - num6; return new BiQuadFilter(num7, num8, num9, b, b2, b3); } private BiQuadFilter() { x1 = (x2 = 0f); y1 = (y2 = 0f); } private BiQuadFilter(double a0, double a1, double a2, double b0, double b1, double b2) { SetCoefficients(a0, a1, a2, b0, b1, b2); x1 = (x2 = 0f); y1 = (y2 = 0f); } } internal class PlayerVoiceInfo { private float outputLevel; private readonly int processBuffer; private bool initialGate; private float MinLUFS; public readonly int voiceID; private BiQuadFilter preFilter; private BiQuadFilter rlbFilter; private const int TERM_MS = 800; private const float TARGET_LUFS = -27f; private const float MAX_DYNAMIC_RANGE = 6.5f; private const float INITIAL_LUFS_GATE = -47f; private const float NOISE_GATE = 4f; private const float MAX_DECAY = 0.04f; private const float LUFS_CEILING = -15f; private float[] squaredBuffer; private int bufferIndex; private float runningSum; private int sampleCount; private int falloffCount; public float MaxLUFS { get; set; } public PlayerVoiceInfo(int samplingRate, int voiceId = -1) { preFilter = BiQuadFilter.HighShelf(samplingRate, 1500f, 0.707f, 4f); rlbFilter = BiQuadFilter.HighPassFilter(samplingRate, 100f, 0.707f); MaxLUFS = -300f; MinLUFS = -20.5f; outputLevel = 1f; voiceID = voiceId; processBuffer = 800 * samplingRate / 1000; squaredBuffer = new float[processBuffer]; initialGate = false; } public void ProcessSamples(float[] samples) { foreach (float inSample in samples) { float inSample2 = preFilter.Transform(inSample); float num = rlbFilter.Transform(inSample2); float num2 = num * num; if (sampleCount >= processBuffer) { runningSum -= squaredBuffer[bufferIndex]; } squaredBuffer[bufferIndex] = num2; runningSum += num2; bufferIndex = (bufferIndex + 1) % processBuffer; if (sampleCount < processBuffer) { sampleCount++; } } falloffCount += samples.Length; } public float GetShortTermLUFS() { if (sampleCount < processBuffer) { return -300f; } double num = runningSum / (float)processBuffer; if (num <= 0.0) { return -300f; } return (float)(10.0 * Math.Log10(num) - 0.691); } public void RecordLUFS(float level) { if (sampleCount < processBuffer) { return; } if (falloffCount > processBuffer) { if (MaxLUFS > -27f) { MaxLUFS -= 0.04f; } falloffCount -= processBuffer; } MaxLUFS = Math.Max(level, MaxLUFS); MinLUFS = Math.Min(level, MinLUFS); if (!initialGate) { if (!(MaxLUFS > -47f)) { return; } initialGate = true; } float num = Math.Max(MaxLUFS - 6.5f, level); double num2 = Math.Min((level - MinLUFS) / 4f, 1.0); double num3 = Math.Sin(num2 * Math.PI * 0.5); num3 = num3 * num3 * num3 * num3; outputLevel = (float)Math.Pow(10.0, (double)(-27f - num) / 20.0) * (float)num3; } public float GetOutputLevel() { return outputLevel; } } [BepInPlugin("PEAK_AutoMic", "PEAK_AutoMic", "0.1.4")] public class Plugin : BaseUnityPlugin { internal static Harmony Harmony = null; internal static AudioLevels? LevelsReference = null; internal static bool _patched = false; public const string Id = "PEAK_AutoMic"; internal static ManualLogSource Log { get; private set; } = null; internal static ConcurrentDictionary PlayerVoices { get; private set; } = new ConcurrentDictionary(); public static string Name => "PEAK_AutoMic"; public static string Version => "0.1.4"; private void Awake() { //IL_0010: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; Harmony = new Harmony("PEAK_AutoMic"); Harmony.PatchAll(typeof(RemoteVoiceLinkPatches)); Log.LogInfo((object)("Plugin " + Name + " is loaded!")); } } } namespace PEAK_AutoMic.Patches { internal class RemoteVoiceLinkPatches { [HarmonyPatch(/*Could not decode attribute arguments.*/)] public static void Postfix(VoiceConnection __instance) { if ((Object)(object)__instance != (Object)null) { Plugin.Log.LogInfo((object)("Found VoiceConnection instance: " + ((Object)__instance).name)); __instance.RemoteVoiceAdded += __instance_RemoteVoiceAdded; } else { Plugin.Log.LogError((object)"VoiceConnection constructor returned NULL????????"); } } private static void __instance_RemoteVoiceAdded(RemoteVoiceLink link) { RemoteVoiceLink link2 = link; if (link2 == null) { Plugin.Log.LogError((object)"RemoteVoiceLink was NULL????????"); return; } Plugin.Log.LogInfo((object)$"Found RemoteVoiceLink for player #{link2.PlayerId}"); lock (PhotonNetwork.PlayerList) { Player[] playerList = PhotonNetwork.PlayerList; if (playerList != null) { for (int i = 0; i < playerList.Length; i++) { if (playerList[i] != null && playerList[i].ActorNumber == link2.PlayerId) { string userId = playerList[i].UserId; if (userId != null) { Plugin.PlayerVoices.TryRemove(userId, out PlayerVoiceInfo _); } } } } } link2.FloatFrameDecoded += delegate(FrameOut frame) { string text = null; lock (PhotonNetwork.PlayerList) { Player[] playerList2 = PhotonNetwork.PlayerList; if (playerList2 != null) { for (int j = 0; j < playerList2.Length; j++) { if (playerList2[j] != null && playerList2[j].ActorNumber == link2.PlayerId) { text = playerList2[j].UserId; if (text != null) { break; } } } } } if (text != null && frame != null && frame.Buf != null) { if (!Plugin.PlayerVoices.TryGetValue(text, out PlayerVoiceInfo value2)) { value2 = new PlayerVoiceInfo(((VoiceInfo)(ref link2.VoiceInfo)).SamplingRate, link2.VoiceId); Plugin.PlayerVoices.TryAdd(text, value2); } value2.ProcessSamples(frame.Buf); float shortTermLUFS = value2.GetShortTermLUFS(); value2.RecordLUFS(shortTermLUFS); float num = value2.GetOutputLevel() * 0.5f; float playerLevel = AudioLevels.GetPlayerLevel(text); if (Math.Abs(num - playerLevel) > 0.01f) { AudioLevels.SetPlayerLevel(text, Math.Clamp(num, 0f, 2f)); if ((Object)(object)Plugin.LevelsReference != (Object)null) { lock (Plugin.LevelsReference) { Plugin.LevelsReference._dirty = true; } } } } }; } [HarmonyPatch(typeof(AudioLevels), "InitNavigation")] [HarmonyPostfix] private static void Postfix(AudioLevels __instance) { if (Object.op_Implicit((Object)(object)__instance.mainPage)) { Plugin.LevelsReference = __instance; } } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { public IgnoresAccessChecksToAttribute(string assemblyName) { } } }