Decompiled source of PEAK AutoMic v0.1.4

plugins/PEAK_AutoMic.dll

Decompiled a month ago
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<string, PlayerVoiceInfo> PlayerVoices { get; private set; } = new ConcurrentDictionary<string, PlayerVoiceInfo>();


		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<float> 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)
		{
		}
	}
}