Decompiled source of MicGate v0.3.1

MicGate.dll

Decompiled 6 hours ago
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Voice;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("MicGate")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("MicGate")]
[assembly: AssemblyTitle("MicGate")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[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]
	[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;
		}
	}
}
namespace MicGate
{
	[BepInPlugin("micgate", "Mic Gate", "0.3.1")]
	public class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		private ConfigEntry<bool> gateEnabled;

		private ConfigEntry<float> thresholdDb;

		private ConfigEntry<int> releaseMs;

		private ConfigEntry<float> testVolume;

		private ConfigEntry<KeyCode> menuKey;

		private const float MinDb = -80f;

		private const float MaxDb = -10f;

		private const int MinDelay = 0;

		private const int MaxDelay = 2000;

		private Type recorderType;

		private Object recorder;

		private PropertyInfo pVoiceDetection;

		private PropertyInfo pThreshold;

		private PropertyInfo pLevelMeter;

		private PropertyInfo pTransmitting;

		private PropertyInfo pRecording;

		private PropertyInfo pDelay;

		private PropertyInfo pLevel;

		private Type levelType;

		private bool origSaved;

		private bool origVoiceDetection;

		private float origThreshold;

		private int origDelay;

		private bool warnedNoType;

		private bool open;

		private bool dirty;

		private bool testing;

		private object tappedVoice;

		private TapPlayer tap;

		private string testError;

		private float tapCheck;

		private float findTimer;

		private float applyTimer;

		private float levelDb = -100f;

		private bool transmitting;

		private Rect win = new Rect(20f, 20f, 390f, 375f);

		private void Awake()
		{
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Expected O, but got Unknown
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dd: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			gateEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enabled", true, "Noise gate: only send voice when the microphone level is above the threshold.");
			thresholdDb = ((BaseUnityPlugin)this).Config.Bind<float>("General", "ThresholdDb", -40f, new ConfigDescription("Send threshold in dB (about -80 = very sensitive, -10 = only loud sounds).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-80f, -10f), Array.Empty<object>()));
			releaseMs = ((BaseUnityPlugin)this).Config.Bind<int>("General", "ReleaseDelayMs", 250, new ConfigDescription("How long (in ms) the microphone stays open after your voice drops below the threshold. Prevents cut-off word endings.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2000), Array.Empty<object>()));
			testVolume = ((BaseUnityPlugin)this).Config.Bind<float>("Test", "TestVolume", 0.8f, new ConfigDescription("Volume of the microphone test playback (hear yourself).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			menuKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("General", "MenuKey", (KeyCode)289, "Key that opens the sensitivity window.");
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			recorderType = AccessTools.TypeByName("Photon.Voice.Unity.Recorder");
			Log.LogInfo((object)("Mic Gate 0.3.1 geladen. Recorder-Typ gefunden: " + (recorderType != null)));
		}

		private bool RecorderAlive()
		{
			return recorder != (Object)null;
		}

		private void FindRecorder()
		{
			if (recorderType == null)
			{
				if (!warnedNoType)
				{
					warnedNoType = true;
					Log.LogWarning((object)"Photon.Voice.Unity.Recorder nicht gefunden. F9 drücken für eine Diagnose.");
				}
				return;
			}
			Object[] array = Object.FindObjectsOfType(recorderType);
			if (array == null || array.Length == 0)
			{
				return;
			}
			pVoiceDetection = recorderType.GetProperty("VoiceDetection");
			pThreshold = recorderType.GetProperty("VoiceDetectionThreshold");
			pLevelMeter = recorderType.GetProperty("LevelMeter");
			pTransmitting = recorderType.GetProperty("IsCurrentlyTransmitting");
			pRecording = recorderType.GetProperty("IsRecording");
			pDelay = recorderType.GetProperty("VoiceDetectionDelayMs");
			Object val = array[0];
			Object[] array2 = array;
			bool flag = default(bool);
			foreach (Object val2 in array2)
			{
				int num;
				if (pRecording != null)
				{
					object value = pRecording.GetValue(val2, null);
					if (value is bool)
					{
						flag = (bool)value;
						num = 1;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					num = 0;
				}
				if (((uint)num & (flag ? 1u : 0u)) != 0)
				{
					val = val2;
					break;
				}
			}
			recorder = val;
			origSaved = false;
			Log.LogInfo((object)($"Recorder gefunden ({array.Length} Stück). VoiceDetection-Property: {pVoiceDetection != null}, " + $"Threshold-Property: {pThreshold != null}, LevelMeter-Property: {pLevelMeter != null}, " + $"Delay-Property: {pDelay != null}"));
		}

		private static float DbToAmp(float db)
		{
			return Mathf.Pow(10f, db / 20f);
		}

		private void ApplySettings()
		{
			if (!RecorderAlive() || pVoiceDetection == null || pThreshold == null)
			{
				return;
			}
			bool flag = (bool)pVoiceDetection.GetValue(recorder, null);
			float num = (float)pThreshold.GetValue(recorder, null);
			if (!origSaved)
			{
				origVoiceDetection = flag;
				origThreshold = num;
				origDelay = ((pDelay != null && pDelay.GetValue(recorder, null) is int num2) ? num2 : 500);
				origSaved = true;
				Log.LogInfo((object)$"Ursprüngliche Werte: VoiceDetection={flag}, Threshold={num}, DelayMs={origDelay}");
			}
			bool flag2 = gateEnabled.Value || origVoiceDetection;
			float num3 = (gateEnabled.Value ? DbToAmp(thresholdDb.Value) : origThreshold);
			if (flag != flag2)
			{
				pVoiceDetection.SetValue(recorder, flag2, null);
			}
			if (Mathf.Abs(num - num3) > num3 * 0.01f)
			{
				pThreshold.SetValue(recorder, num3, null);
			}
			if (pDelay != null && pDelay.GetValue(recorder, null) is int num4)
			{
				int num5 = (gateEnabled.Value ? releaseMs.Value : origDelay);
				if (num4 != num5)
				{
					pDelay.SetValue(recorder, num5, null);
				}
			}
		}

		private void RestoreOriginal()
		{
			if (RecorderAlive() && origSaved && !(pVoiceDetection == null) && !(pThreshold == null))
			{
				pVoiceDetection.SetValue(recorder, origVoiceDetection, null);
				pThreshold.SetValue(recorder, origThreshold, null);
				if (pDelay != null)
				{
					pDelay.SetValue(recorder, origDelay, null);
				}
			}
		}

		private void ReadLevel(float dt)
		{
			transmitting = false;
			if (!RecorderAlive())
			{
				levelDb = Mathf.Max(-100f, levelDb - 60f * dt);
				return;
			}
			if (pTransmitting != null && pTransmitting.GetValue(recorder, null) is bool flag)
			{
				transmitting = flag;
			}
			object obj = ((pLevelMeter != null) ? pLevelMeter.GetValue(recorder, null) : null);
			float num = 0f;
			if (obj != null)
			{
				if (levelType != obj.GetType())
				{
					levelType = obj.GetType();
					pLevel = levelType.GetProperty("CurrentAvgAmp") ?? levelType.GetProperty("CurrentPeakAmp");
				}
				if (pLevel != null && pLevel.GetValue(obj, null) is float num2)
				{
					num = num2;
				}
			}
			float num3 = ((num > 1E-05f) ? (20f * Mathf.Log10(num)) : (-100f));
			levelDb = Mathf.Max(num3, levelDb - 60f * dt);
		}

		private void Update()
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			float unscaledDeltaTime = Time.unscaledDeltaTime;
			if (Input.GetKeyDown(menuKey.Value))
			{
				open = !open;
				if (!open)
				{
					StopTest();
					((BaseUnityPlugin)this).Config.Save();
				}
			}
			if (Input.GetKeyDown((KeyCode)290))
			{
				try
				{
					Dump();
				}
				catch (Exception ex)
				{
					Log.LogError((object)("Dump fehlgeschlagen: " + ex));
				}
			}
			findTimer -= unscaledDeltaTime;
			if (!RecorderAlive() && findTimer <= 0f)
			{
				findTimer = 2f;
				FindRecorder();
			}
			applyTimer -= unscaledDeltaTime;
			if (applyTimer <= 0f)
			{
				applyTimer = 0.5f;
				try
				{
					ApplySettings();
				}
				catch (Exception ex2)
				{
					Log.LogError((object)("Einstellungen setzen fehlgeschlagen: " + ex2.Message));
				}
			}
			ReadLevel(unscaledDeltaTime);
			UpdateTest();
			if (open)
			{
				float num = ((Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303)) ? 5f : 1f);
				if (Input.GetKeyDown((KeyCode)276))
				{
					SetThreshold(thresholdDb.Value - num);
				}
				if (Input.GetKeyDown((KeyCode)275))
				{
					SetThreshold(thresholdDb.Value + num);
				}
				int num2 = ((Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303)) ? 200 : 50);
				if (Input.GetKeyDown((KeyCode)274))
				{
					SetDelay(releaseMs.Value - num2);
				}
				if (Input.GetKeyDown((KeyCode)273))
				{
					SetDelay(releaseMs.Value + num2);
				}
			}
			if (dirty && !Input.GetMouseButton(0))
			{
				dirty = false;
				((BaseUnityPlugin)this).Config.Save();
			}
		}

		private void LateUpdate()
		{
			if (open)
			{
				Cursor.lockState = (CursorLockMode)0;
				Cursor.visible = true;
			}
		}

		private void OnDestroy()
		{
			StopTest();
			RestoreOriginal();
		}

		private void SetDelay(int v)
		{
			v = Mathf.Clamp(v, 0, 2000);
			if (v != releaseMs.Value)
			{
				releaseMs.Value = v;
				dirty = true;
			}
		}

		private void SetThreshold(float v)
		{
			v = Mathf.Clamp(Mathf.Round(v), -80f, -10f);
			if (!Mathf.Approximately(v, thresholdDb.Value))
			{
				thresholdDb.Value = v;
				dirty = true;
			}
		}

		private object GetLocalVoice()
		{
			if (!RecorderAlive())
			{
				return null;
			}
			string[] array = new string[2] { "LocalVoice", "Voice" };
			foreach (string name in array)
			{
				PropertyInfo property = recorderType.GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property == null)
				{
					continue;
				}
				try
				{
					object value = property.GetValue(recorder, null);
					if (value != null)
					{
						return value;
					}
				}
				catch
				{
				}
			}
			array = new string[2] { "voice", "localVoice" };
			foreach (string text in array)
			{
				FieldInfo fieldInfo = AccessTools.Field(recorderType, text);
				if (!(fieldInfo == null))
				{
					object value2 = fieldInfo.GetValue(recorder);
					if (value2 != null)
					{
						return value2;
					}
				}
			}
			return null;
		}

		private static int ReadInt(object obj, string name, int fallback)
		{
			if (obj == null)
			{
				return fallback;
			}
			try
			{
				PropertyInfo property = obj.GetType().GetProperty(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (property != null)
				{
					return Convert.ToInt32(property.GetValue(obj, null));
				}
				FieldInfo field = obj.GetType().GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return Convert.ToInt32(field.GetValue(obj));
				}
			}
			catch
			{
			}
			return fallback;
		}

		private bool EnsureTap()
		{
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: Expected O, but got Unknown
			object localVoice = GetLocalVoice();
			if (localVoice == null)
			{
				testError = "No active voice yet (is the microphone recording?)";
				return false;
			}
			if ((Object)(object)tap != (Object)null && tappedVoice == localVoice)
			{
				return true;
			}
			MethodInfo method = localVoice.GetType().GetMethod("AddPostProcessor", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (method == null)
			{
				testError = "Test not supported (no AddPostProcessor on " + localVoice.GetType().Name + ")";
				Log.LogWarning((object)testError);
				return false;
			}
			ParameterInfo[] parameters = method.GetParameters();
			Type type = ((parameters.Length == 1) ? parameters[0].ParameterType : null);
			Type type2 = ((type != null && type.IsArray) ? type.GetElementType() : null);
			if ((Object)(object)tap == (Object)null)
			{
				GameObject val = new GameObject("MicGate_TestPlayback");
				Object.DontDestroyOnLoad((Object)(object)val);
				tap = val.AddComponent<TapPlayer>();
			}
			object obj = null;
			PropertyInfo property = localVoice.GetType().GetProperty("Info", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null)
			{
				try
				{
					obj = property.GetValue(localVoice, null);
				}
				catch
				{
				}
			}
			int num = ReadInt(obj, "SamplingRate", 24000);
			int num2 = ReadInt(obj, "Channels", 1);
			tap.Configure(num, num2);
			object value;
			if (type2 == typeof(IProcessor<float>))
			{
				value = new FloatTap(tap);
			}
			else
			{
				if (!(type2 == typeof(IProcessor<short>)))
				{
					testError = "Test not supported (unknown processor type " + ((type2 != null) ? type2.Name : "?") + ")";
					Log.LogWarning((object)testError);
					return false;
				}
				value = new ShortTap(tap);
			}
			Array array = Array.CreateInstance(type2, 1);
			array.SetValue(value, 0);
			method.Invoke(localVoice, new object[1] { array });
			tappedVoice = localVoice;
			Log.LogInfo((object)$"Mikrofon-Test: Abgriff installiert ({localVoice.GetType().Name}, {num} Hz, {num2} Kanal/Kanäle, {type2.Name}).");
			return true;
		}

		private void StartTest()
		{
			if (!testing)
			{
				testError = null;
				if (EnsureTap())
				{
					tap.ResetBuffer();
					tap.Volume = testVolume.Value;
					tap.Active = true;
					testing = true;
				}
			}
		}

		private void StopTest()
		{
			if (testing)
			{
				testing = false;
				if ((Object)(object)tap != (Object)null)
				{
					tap.Active = false;
				}
			}
		}

		private void UpdateTest()
		{
			if (!testing)
			{
				return;
			}
			if (!open)
			{
				StopTest();
				return;
			}
			tap.Volume = testVolume.Value;
			tapCheck -= Time.unscaledDeltaTime;
			if (tapCheck <= 0f)
			{
				tapCheck = 0.5f;
				if (GetLocalVoice() != tappedVoice)
				{
					EnsureTap();
				}
			}
		}

		private void OnGUI()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Expected O, but got Unknown
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (open)
			{
				GUI.depth = -500;
				win = GUI.Window(84210, win, new WindowFunction(DrawWindow), "Mic Gate - send sensitivity");
			}
		}

		private static void DrawRect(Rect r, Color c)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			GUI.color = c;
			GUI.DrawTexture(r, (Texture)(object)Texture2D.whiteTexture);
			GUI.color = Color.white;
		}

		private void DrawWindow(int id)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0158: Unknown result type (might be due to invalid IL or missing references)
			//IL_018f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0174: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_029f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_032e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0351: Unknown result type (might be due to invalid IL or missing references)
			//IL_0361: Unknown result type (might be due to invalid IL or missing references)
			//IL_0366: Unknown result type (might be due to invalid IL or missing references)
			//IL_0398: Unknown result type (might be due to invalid IL or missing references)
			//IL_03e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_042e: Unknown result type (might be due to invalid IL or missing references)
			//IL_04e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_0500: Unknown result type (might be due to invalid IL or missing references)
			bool flag = GUI.Toggle(new Rect(15f, 26f, 250f, 20f), gateEnabled.Value, " Enable noise gate");
			if (flag != gateEnabled.Value)
			{
				gateEnabled.Value = flag;
				dirty = true;
			}
			GUI.Label(new Rect(15f, 50f, 360f, 20f), "Send threshold: " + thresholdDb.Value.ToString("0") + " dB");
			float threshold = GUI.HorizontalSlider(new Rect(15f, 76f, 360f, 18f), thresholdDb.Value, -80f, -10f);
			SetThreshold(threshold);
			Rect r = default(Rect);
			((Rect)(ref r))..ctor(15f, 104f, 360f, 18f);
			DrawRect(r, new Color(0.12f, 0.12f, 0.12f, 1f));
			bool flag2 = levelDb >= thresholdDb.Value;
			float num = Mathf.InverseLerp(-80f, -10f, levelDb);
			DrawRect(new Rect(((Rect)(ref r)).x, ((Rect)(ref r)).y, ((Rect)(ref r)).width * num, ((Rect)(ref r)).height), flag2 ? new Color(0.25f, 0.85f, 0.4f, 1f) : new Color(0.55f, 0.55f, 0.55f, 1f));
			DrawRect(new Rect(((Rect)(ref r)).x + ((Rect)(ref r)).width * Mathf.InverseLerp(-80f, -10f, thresholdDb.Value) - 1f, ((Rect)(ref r)).y - 3f, 3f, ((Rect)(ref r)).height + 6f), new Color(1f, 0.8f, 0.1f, 1f));
			string text = ((!RecorderAlive()) ? "Microphone recorder not found" : (transmitting ? "SENDING" : "silent"));
			GUI.Label(new Rect(15f, 128f, 360f, 20f), "Level: " + levelDb.ToString("0") + " dB   -   " + text);
			string text2 = ((pDelay == null) ? "  (not supported)" : "");
			GUI.Label(new Rect(15f, 154f, 360f, 20f), "Release delay: " + releaseMs.Value + " ms" + text2);
			float num2 = GUI.HorizontalSlider(new Rect(15f, 180f, 360f, 18f), (float)releaseMs.Value, 0f, 2000f);
			SetDelay(Mathf.RoundToInt(num2 / 10f) * 10);
			GUI.Label(new Rect(15f, 206f, 360f, 20f), "Left/Right: threshold -/+ 1 dB   Up/Down: delay +/- 50 ms");
			GUI.Label(new Rect(15f, 226f, 360f, 20f), "Hold Shift for bigger steps   |   " + ((object)menuKey.Value/*cast due to .constrained prefix*/).ToString() + ": close and save");
			if (GUI.Button(new Rect(15f, 256f, 360f, 28f), testing ? "Stop microphone test" : "Test microphone (hear yourself)"))
			{
				if (testing)
				{
					StopTest();
				}
				else
				{
					StartTest();
				}
			}
			GUI.Label(new Rect(15f, 290f, 360f, 20f), "Test volume: " + Mathf.RoundToInt(testVolume.Value * 100f) + " %");
			float num3 = GUI.HorizontalSlider(new Rect(15f, 314f, 360f, 18f), testVolume.Value, 0f, 1f);
			if (!Mathf.Approximately(num3, testVolume.Value))
			{
				testVolume.Value = num3;
				dirty = true;
			}
			string text3 = (testing ? ("Listening to what is sent (frames: " + (((Object)(object)tap != (Object)null) ? tap.FramesReceived : 0) + "). Use headphones!") : ((testError != null) ? testError : "Use headphones to avoid feedback."));
			GUI.Label(new Rect(15f, 340f, 360f, 20f), text3);
			GUI.DragWindow(new Rect(0f, 0f, 10000f, 22f));
		}

		private void Dump()
		{
			StringBuilder stringBuilder = new StringBuilder();
			stringBuilder.AppendLine("== Status ==");
			stringBuilder.AppendLine("Recorder-Typ: " + (recorderType != null));
			stringBuilder.AppendLine("Recorder-Objekt gefunden: " + RecorderAlive());
			string[] array;
			if (RecorderAlive())
			{
				array = new string[8] { "VoiceDetection", "VoiceDetectionThreshold", "IsRecording", "VoiceDetectionDelayMs", "IsCurrentlyTransmitting", "TransmitEnabled", "RecordingEnabled", "MicrophoneType" };
				foreach (string text in array)
				{
					PropertyInfo property = recorderType.GetProperty(text);
					object obj = null;
					try
					{
						obj = ((property != null) ? property.GetValue(recorder, null) : "(Property fehlt)");
					}
					catch (Exception ex)
					{
						obj = "Fehler: " + ex.Message;
					}
					stringBuilder.AppendLine("  " + text + " = " + obj);
				}
			}
			stringBuilder.AppendLine("Pegel jetzt: " + levelDb.ToString("0.0") + " dB");
			object localVoice = GetLocalVoice();
			stringBuilder.AppendLine("LocalVoice-Typ: " + ((localVoice != null) ? localVoice.GetType().FullName : "(keine)"));
			stringBuilder.AppendLine();
			stringBuilder.AppendLine("== Typen mit Voice / Recorder / Microphone / Speaker / Menu im Namen ==");
			int num = 0;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				string name = assembly.GetName().Name;
				if (!name.StartsWith("Assembly-CSharp") && !name.StartsWith("Photon"))
				{
					continue;
				}
				Type[] array2;
				try
				{
					array2 = assembly.GetTypes();
				}
				catch (ReflectionTypeLoadException ex2)
				{
					array2 = Array.FindAll(ex2.Types, (Type x) => x != null);
				}
				Type[] array3 = array2;
				foreach (Type type in array3)
				{
					string name2 = type.Name;
					if (name2.IndexOf("Voice", StringComparison.OrdinalIgnoreCase) >= 0 || name2.IndexOf("Recorder", StringComparison.OrdinalIgnoreCase) >= 0 || name2.IndexOf("Microphone", StringComparison.OrdinalIgnoreCase) >= 0 || name2.IndexOf("Speaker", StringComparison.OrdinalIgnoreCase) >= 0 || name2.StartsWith("MenuPage", StringComparison.Ordinal) || name2.StartsWith("MenuSlider", StringComparison.Ordinal))
					{
						stringBuilder.AppendLine("  [" + name + "] " + type.FullName);
						if (++num > 400)
						{
							break;
						}
					}
				}
			}
			array = new string[4] { "PlayerVoiceChat", "Photon.Voice.Unity.Recorder", "Photon.Voice.LevelMeter", "Photon.Voice.PUN.PhotonVoiceNetwork" };
			foreach (string text2 in array)
			{
				DumpType(stringBuilder, AccessTools.TypeByName(text2));
			}
			if (localVoice != null)
			{
				DumpType(stringBuilder, localVoice.GetType());
			}
			string text3 = Path.Combine(Paths.BepInExRootPath, "MicGate_dump.txt");
			File.WriteAllText(text3, stringBuilder.ToString());
			Log.LogInfo((object)("Diagnose geschrieben: " + text3));
		}

		private static void DumpType(StringBuilder sb, Type t)
		{
			sb.AppendLine();
			if (t == null)
			{
				sb.AppendLine("== (Typ nicht gefunden) ==");
				return;
			}
			sb.AppendLine("== " + t.FullName + " : " + ((t.BaseType != null) ? t.BaseType.Name : "-") + " ==");
			sb.AppendLine("-- Felder --");
			FieldInfo[] fields = t.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (FieldInfo fieldInfo in fields)
			{
				sb.AppendLine("  " + fieldInfo.FieldType.Name + " " + fieldInfo.Name);
			}
			sb.AppendLine("-- Properties --");
			PropertyInfo[] properties = t.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (PropertyInfo propertyInfo in properties)
			{
				sb.AppendLine("  " + propertyInfo.PropertyType.Name + " " + propertyInfo.Name + (propertyInfo.CanWrite ? " {get;set;}" : " {get;}"));
			}
			sb.AppendLine("-- Methoden --");
			MethodInfo[] methods = t.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			foreach (MethodInfo methodInfo in methods)
			{
				if (!methodInfo.IsSpecialName)
				{
					sb.AppendLine("  " + methodInfo.ReturnType.Name + " " + methodInfo.Name + "(" + string.Join(", ", Array.ConvertAll(methodInfo.GetParameters(), (ParameterInfo p) => p.ParameterType.Name + " " + p.Name)) + ")");
				}
			}
		}
	}
	internal sealed class FloatTap : IProcessor<float>, IDisposable
	{
		private readonly TapPlayer tap;

		public FloatTap(TapPlayer t)
		{
			tap = t;
		}

		public float[] Process(float[] buf)
		{
			tap.WriteFloat(buf);
			return buf;
		}

		public void Dispose()
		{
		}
	}
	internal sealed class ShortTap : IProcessor<short>, IDisposable
	{
		private readonly TapPlayer tap;

		public ShortTap(TapPlayer t)
		{
			tap = t;
		}

		public short[] Process(short[] buf)
		{
			tap.WriteShort(buf);
			return buf;
		}

		public void Dispose()
		{
		}
	}
	internal sealed class TapPlayer : MonoBehaviour
	{
		private readonly object sync = new object();

		private readonly float[] ring = new float[192000];

		private long writePos;

		private double readPos;

		private bool playing;

		private int outRate = 48000;

		private int sourceRate = 24000;

		private int channels = 1;

		public volatile bool Active;

		public float Volume = 1f;

		public int FramesReceived;

		private void Awake()
		{
			outRate = AudioSettings.outputSampleRate;
			AudioSource obj = ((Component)this).gameObject.AddComponent<AudioSource>();
			obj.clip = AudioClip.Create("MicGate_silence", 4096, 1, outRate, false);
			obj.loop = true;
			obj.spatialBlend = 0f;
			obj.playOnAwake = false;
			obj.Play();
		}

		public void Configure(int rate, int ch)
		{
			lock (sync)
			{
				sourceRate = Mathf.Max(8000, rate);
				channels = Mathf.Max(1, ch);
			}
			ResetBuffer();
		}

		public void ResetBuffer()
		{
			lock (sync)
			{
				writePos = 0L;
				readPos = 0.0;
				playing = false;
				FramesReceived = 0;
			}
		}

		private void Push(float s)
		{
			ring[(int)(writePos % ring.Length)] = s;
			writePos++;
		}

		public void WriteFloat(float[] buf)
		{
			if (!Active || buf == null)
			{
				return;
			}
			lock (sync)
			{
				FramesReceived++;
				for (int i = 0; i + channels <= buf.Length; i += channels)
				{
					float num = 0f;
					for (int j = 0; j < channels; j++)
					{
						num += buf[i + j];
					}
					Push(num / (float)channels);
				}
				if ((double)writePos - readPos > (double)(ring.Length - 8))
				{
					readPos = writePos - ring.Length / 2;
				}
			}
		}

		public void WriteShort(short[] buf)
		{
			if (!Active || buf == null)
			{
				return;
			}
			lock (sync)
			{
				FramesReceived++;
				for (int i = 0; i + channels <= buf.Length; i += channels)
				{
					float num = 0f;
					for (int j = 0; j < channels; j++)
					{
						num += (float)buf[i + j] / 32768f;
					}
					Push(num / (float)channels);
				}
				if ((double)writePos - readPos > (double)(ring.Length - 8))
				{
					readPos = writePos - ring.Length / 2;
				}
			}
		}

		private void OnAudioFilterRead(float[] data, int ch)
		{
			if (!Active)
			{
				Array.Clear(data, 0, data.Length);
				return;
			}
			float volume = Volume;
			int num = data.Length / ch;
			lock (sync)
			{
				double num2 = (double)sourceRate / (double)outRate;
				int num3 = sourceRate / 10;
				for (int i = 0; i < num; i++)
				{
					float num4 = 0f;
					if (!playing && (double)writePos - readPos >= (double)num3)
					{
						playing = true;
					}
					if (playing)
					{
						long num5 = (long)readPos;
						if (num5 + 1 >= writePos)
						{
							playing = false;
						}
						else
						{
							float num6 = ring[(int)(num5 % ring.Length)];
							float num7 = ring[(int)((num5 + 1) % ring.Length)];
							num4 = (num6 + (num7 - num6) * (float)(readPos - (double)num5)) * volume;
							readPos += num2;
						}
					}
					for (int j = 0; j < ch; j++)
					{
						data[i * ch + j] = num4;
					}
				}
			}
		}
	}
}