Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of MicGate v0.3.1
MicGate.dll
Decompiled 6 hours agousing 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; } } } } } }