Decompiled source of LethalMic v1.5.3

LethalMic.dll

Decompiled 2 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using LethalCompanyInputUtils.Api;
using LethalCompanyInputUtils.BindingPathEnums;
using LethalMic.Patches;
using LethalMic.UI.Components;
using Microsoft.CodeAnalysis;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("LethalMic")]
[assembly: AssemblyDescription("LethalMic - Advanced audio processing for Lethal Company")]
[assembly: AssemblyCompany("xenoveni")]
[assembly: AssemblyProduct("LethalMic")]
[assembly: AssemblyCopyright("Copyright © xenoveni 2025")]
[assembly: AssemblyFileVersion("2.0.0")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.0.0.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.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace LethalMic
{
	public static class StaticAudioManager
	{
		private static ManualLogSource Logger;

		private static bool isInitialized;

		private static bool isRecording;

		private static float[] audioBuffer;

		private static float[] processedBuffer;

		private static int bufferSize = 1024;

		private static string selectedDevice;

		private static AudioClip microphoneClip;

		private static int sampleRate = 44100;

		private static int channels = 1;

		private static float currentMicLevel;

		private static float peakMicLevel;

		private static float noiseFloor;

		private static bool voiceDetected;

		private static float cpuUsage;

		private static int lastMicPosition = 0;

		private static AINoiseSuppressionProcessor noiseSuppressor;

		private static AdvancedEchoCanceller echoCanceller;

		private static SpectralSubtractionProcessor spectralProcessor;

		private static VoiceDuckingProcessor voiceDucker;

		private static AudioCompressorProcessor compressor;

		private static bool processorsInitialized = false;

		private static float[] tempBuffer;

		private static float[] outputBuffer;

		private static float lastGain = -1f;

		private static bool lastNoiseGate = false;

		private static bool lastCompression = false;

		private static float lastRatio = -1f;

		public static void Initialize(ManualLogSource logger)
		{
			if (!isInitialized)
			{
				Logger = logger;
				Logger.LogInfo((object)"Initializing StaticAudioManager...");
				audioBuffer = new float[bufferSize];
				processedBuffer = new float[bufferSize];
				tempBuffer = new float[bufferSize];
				outputBuffer = new float[bufferSize];
				Logger.LogInfo((object)$"Audio buffers initialized with size: {bufferSize}");
				InitializeProcessors();
				StartSpeakerCapture();
				Logger.LogInfo((object)"StaticAudioManager initialized with advanced processing pipeline");
				isInitialized = true;
			}
		}

		private static void InitializeProcessors()
		{
			try
			{
				noiseSuppressor = new AINoiseSuppressionProcessor(sampleRate, bufferSize);
				echoCanceller = new AdvancedEchoCanceller(sampleRate, bufferSize);
				spectralProcessor = new SpectralSubtractionProcessor(sampleRate, channels);
				voiceDucker = new VoiceDuckingProcessor(sampleRate, bufferSize);
				compressor = new AudioCompressorProcessor(sampleRate);
				processorsInitialized = true;
				Logger.LogInfo((object)"Audio processors initialized successfully");
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Failed to initialize audio processors: {arg}");
				processorsInitialized = false;
			}
		}

		public static void StartRecording()
		{
			if (!isInitialized || isRecording)
			{
				return;
			}
			try
			{
				string[] devices = Microphone.devices;
				if (devices.Length == 0)
				{
					Logger.LogWarning((object)"No microphone devices found");
					return;
				}
				string text = LethalMicStatic.GetInputDevice();
				if (string.IsNullOrEmpty(text))
				{
					text = devices[0];
				}
				microphoneClip = Microphone.Start(text, true, 1, sampleRate);
				if ((Object)(object)microphoneClip != (Object)null)
				{
					isRecording = true;
					lastMicPosition = 0;
					selectedDevice = text;
					Logger.LogInfo((object)("Started recording from device: " + text));
				}
				else
				{
					Logger.LogError((object)"Failed to start microphone recording");
				}
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Error starting microphone recording: {arg}");
			}
		}

		public static void StopRecording()
		{
			if (!isRecording)
			{
				return;
			}
			try
			{
				Microphone.End(selectedDevice);
				isRecording = false;
				Logger.LogInfo((object)"Stopped recording");
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Failed to stop recording: {arg}");
			}
		}

		public static void ProcessAudio()
		{
			if (!isInitialized || !isRecording || (Object)(object)microphoneClip == (Object)null)
			{
				return;
			}
			try
			{
				int samples = microphoneClip.samples;
				int position = Microphone.GetPosition(selectedDevice);
				if (position < 0 || position >= samples)
				{
					lastMicPosition = 0;
					return;
				}
				int num = position - lastMicPosition;
				if (num < 0)
				{
					num += samples;
				}
				if (num <= 0 || num > samples)
				{
					lastMicPosition = position;
					return;
				}
				float[] array = new float[num];
				if (!microphoneClip.GetData(array, lastMicPosition) || array.Length == 0)
				{
					lastMicPosition = position;
					return;
				}
				UpdateAudioLevels(ProcessAudioPipeline(array));
				UpdateUI();
				lastMicPosition = position;
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Error processing audio: {arg}");
			}
		}

		private static float[] ProcessAudioPipeline(float[] inputData)
		{
			if (!processorsInitialized || inputData == null || inputData.Length == 0)
			{
				return inputData;
			}
			try
			{
				Array.Copy(inputData, tempBuffer, Math.Min(inputData.Length, tempBuffer.Length));
				float microphoneGain = LethalMicStatic.GetMicrophoneGain();
				for (int i = 0; i < tempBuffer.Length; i++)
				{
					tempBuffer[i] *= microphoneGain;
				}
				if (LethalMicStatic.GetNoiseGateEnabled())
				{
					float noiseGateThreshold = LethalMicStatic.GetNoiseGateThreshold();
					float num = 0f;
					for (int j = 0; j < tempBuffer.Length; j++)
					{
						num += tempBuffer[j] * tempBuffer[j];
					}
					num = Mathf.Sqrt(num / (float)tempBuffer.Length);
					float num2 = noiseGateThreshold;
					if (num > noiseGateThreshold * 3f)
					{
						num2 = noiseGateThreshold * 5f;
						Logger.LogInfo((object)$"Echo detected! RMS: {num:F4}, using adaptive threshold: {num2:F4}");
					}
					for (int k = 0; k < tempBuffer.Length; k++)
					{
						if (Mathf.Abs(tempBuffer[k]) < num2)
						{
							tempBuffer[k] = 0f;
						}
					}
				}
				if (noiseSuppressor != null && noiseSuppressor.IsEnabled)
				{
					tempBuffer = noiseSuppressor.ProcessAudio(tempBuffer);
				}
				if (spectralProcessor != null)
				{
					spectralProcessor.ProcessAudio(tempBuffer, 0, tempBuffer.Length);
				}
				if (compressor != null && compressor.IsEnabled)
				{
					tempBuffer = compressor.ProcessAudio(tempBuffer);
				}
				Array.Copy(tempBuffer, outputBuffer, Math.Min(tempBuffer.Length, outputBuffer.Length));
				return outputBuffer;
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Error in audio processing pipeline: {arg}");
				return inputData;
			}
		}

		private static void UpdateAudioLevels(float[] audioData)
		{
			if (audioData != null && audioData.Length != 0)
			{
				float num = 0f;
				for (int i = 0; i < audioData.Length; i++)
				{
					num += audioData[i] * audioData[i];
				}
				currentMicLevel = Mathf.Sqrt(num / (float)audioData.Length);
				peakMicLevel = Mathf.Max(peakMicLevel * 0.95f, currentMicLevel);
				float noiseGateThreshold = LethalMicStatic.GetNoiseGateThreshold();
				voiceDetected = currentMicLevel > noiseGateThreshold;
				if (currentMicLevel < noiseFloor || noiseFloor == 0f)
				{
					noiseFloor = Mathf.Lerp(noiseFloor, currentMicLevel, 0.01f);
				}
				cpuUsage = Mathf.Lerp(cpuUsage, currentMicLevel * 100f, Time.deltaTime);
			}
		}

		private static void UpdateUI()
		{
			if (LethalMicStatic.IsUIVisible())
			{
				LethalMicUI uIIInstance = LethalMicStatic.GetUIIInstance();
				if ((Object)(object)uIIInstance != (Object)null)
				{
					uIIInstance.UpdateMicStatus(selectedDevice, "Connected", currentMicLevel);
					uIIInstance.UpdateCPUUsage(cpuUsage);
				}
			}
		}

		public static void Cleanup()
		{
			if (isRecording)
			{
				StopRecording();
			}
			if ((Object)(object)microphoneClip != (Object)null)
			{
				Object.Destroy((Object)(object)microphoneClip);
				microphoneClip = null;
			}
			if (noiseSuppressor != null)
			{
				noiseSuppressor.Dispose();
				noiseSuppressor = null;
			}
			if (echoCanceller != null)
			{
				echoCanceller.Dispose();
				echoCanceller = null;
			}
			if (spectralProcessor != null)
			{
				spectralProcessor.Dispose();
				spectralProcessor = null;
			}
			if (voiceDucker != null)
			{
				voiceDucker.Dispose();
				voiceDucker = null;
			}
			if (compressor != null)
			{
				compressor.Dispose();
				compressor = null;
			}
			isInitialized = false;
			processorsInitialized = false;
			Logger.LogInfo((object)"StaticAudioManager cleaned up");
		}

		public static float GetCurrentMicrophoneLevel()
		{
			return currentMicLevel;
		}

		public static float GetPeakMicrophoneLevel()
		{
			return peakMicLevel;
		}

		public static bool IsVoiceDetected()
		{
			return voiceDetected;
		}

		public static float GetNoiseFloor()
		{
			return noiseFloor;
		}

		public static void SetNoiseFloor(float value)
		{
			noiseFloor = value;
		}

		public static float GetCPUUsage()
		{
			return cpuUsage;
		}

		public static void UpdateProcessorSettings()
		{
			if (!processorsInitialized)
			{
				return;
			}
			try
			{
				bool flag = Math.Abs(LethalMicStatic.GetMicrophoneGain() - lastGain) > 0.1f || LethalMicStatic.GetNoiseGateEnabled() != lastNoiseGate || LethalMicStatic.GetCompressionEnabled() != lastCompression || Math.Abs(LethalMicStatic.GetCompressionRatio() - lastRatio) > 0.5f;
				if (flag)
				{
					Logger.LogInfo((object)"Updating audio processor settings...");
				}
				if (noiseSuppressor != null)
				{
					noiseSuppressor.NoiseReductionStrength = LethalMicStatic.GetNoiseGateThreshold();
					noiseSuppressor.IsEnabled = LethalMicStatic.GetNoiseGateEnabled();
					if (flag)
					{
						Logger.LogInfo((object)$"Noise suppressor: Enabled={noiseSuppressor.IsEnabled}, Strength={noiseSuppressor.NoiseReductionStrength:F3}");
					}
				}
				if (echoCanceller != null)
				{
					echoCanceller.EchoCancellationStrength = 0f;
					echoCanceller.IsEnabled = false;
					if (flag)
					{
						Logger.LogInfo((object)"Echo canceller: DISABLED (prevents echo loops)");
					}
				}
				if (voiceDucker != null)
				{
					voiceDucker.SetDuckingLevel(0.3f);
					if (flag)
					{
						Logger.LogInfo((object)"Voice ducker: Ducking level=0.3");
					}
				}
				if (compressor != null)
				{
					compressor.IsEnabled = LethalMicStatic.GetCompressionEnabled();
					compressor.UpdateSettings(-20f, LethalMicStatic.GetCompressionRatio(), LethalMicStatic.GetAttackTime(), LethalMicStatic.GetReleaseTime(), 0f);
					if (flag)
					{
						Logger.LogInfo((object)$"Compressor: Enabled={compressor.IsEnabled}, Ratio={LethalMicStatic.GetCompressionRatio()}, Attack={LethalMicStatic.GetAttackTime():F0}ms, Release={LethalMicStatic.GetReleaseTime():F0}ms");
					}
				}
				if (flag)
				{
					Logger.LogInfo((object)"Audio processor settings updated successfully");
					lastGain = LethalMicStatic.GetMicrophoneGain();
					lastNoiseGate = LethalMicStatic.GetNoiseGateEnabled();
					lastCompression = LethalMicStatic.GetCompressionEnabled();
					lastRatio = LethalMicStatic.GetCompressionRatio();
				}
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Error updating processor settings: {arg}");
			}
		}

		public static void SetInputDevice(string deviceName)
		{
			selectedDevice = deviceName;
			if (isRecording)
			{
				StopRecording();
				StartRecording();
			}
		}

		public static string GetInputDevice()
		{
			return selectedDevice;
		}

		private static void StartSpeakerCapture()
		{
			try
			{
				Logger.LogInfo((object)"Starting speaker audio capture for echo cancellation...");
				Logger.LogInfo((object)"Speaker capture initialized (simulated)");
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Failed to start speaker capture: {arg}");
			}
		}

		private static float[] GetSpeakerAudio(int requiredLength)
		{
			return null;
		}

		public static float[] ProcessAudioBuffer(float[] inputBuffer)
		{
			if (!processorsInitialized || inputBuffer == null || inputBuffer.Length == 0)
			{
				return inputBuffer;
			}
			try
			{
				return ProcessAudioPipeline(inputBuffer);
			}
			catch (Exception arg)
			{
				Logger.LogError((object)$"Error processing audio buffer: {arg}");
				return inputBuffer;
			}
		}

		public static void SetAggressiveSuppression()
		{
			LethalMicStatic.SetNoiseGateEnabled(value: true);
			LethalMicStatic.SetNoiseGateThreshold(0.05f);
			LethalMicStatic.SetCompressionEnabled(value: true);
			LethalMicStatic.SetCompressionRatio(10f);
			LethalMicStatic.SetAttackTime(2f);
			LethalMicStatic.SetReleaseTime(50f);
		}

		public static void SetStereoMixSuppression()
		{
			SetAggressiveSuppression();
		}

		public static void SetWasapiSuppression()
		{
			SetAggressiveSuppression();
		}
	}
	[Serializable]
	public class AudioPreset
	{
		public string Name { get; set; }

		public string Description { get; set; }

		public DateTime CreatedDate { get; set; }

		public bool NoiseSuppressionEnabled { get; set; } = true;


		public float NoiseSuppressionStrength { get; set; } = 0.8f;


		public bool RNNoiseEnabled { get; set; } = true;


		public bool VoiceEnhancementEnabled { get; set; } = true;


		public float VoiceGain { get; set; } = 1f;


		public bool AutoGainControlEnabled { get; set; } = true;


		public bool EchoCancellationEnabled { get; set; } = true;


		public float EchoCancellationStrength { get; set; } = 0.7f;


		public int EchoFilterLength { get; set; } = 256;


		public bool VoiceDuckingEnabled { get; set; } = true;


		public float DuckingLevel { get; set; } = 0.3f;


		public float DuckingAttackTime { get; set; } = 0.003f;


		public float DuckingReleaseTime { get; set; } = 0.1f;


		public int ProcessingQuality { get; set; } = 5;


		public bool SpectralSubtractionEnabled { get; set; }

		public bool LoopDetectionEnabled { get; set; } = true;


		public float LoopDetectionThreshold { get; set; } = 0.7f;


		public AudioPreset()
		{
			Name = "Default";
			Description = "Default audio processing settings";
			CreatedDate = DateTime.Now;
		}

		public AudioPreset(string name, string description)
			: this()
		{
			Name = name;
			Description = description;
		}
	}
	public static class AudioPresetManager
	{
		private static readonly string PresetsDirectory;

		private static readonly Dictionary<string, AudioPreset> _loadedPresets;

		private static AudioPreset _currentPreset;

		static AudioPresetManager()
		{
			PresetsDirectory = Path.Combine(Paths.ConfigPath, "LethalMic", "Presets");
			_loadedPresets = new Dictionary<string, AudioPreset>();
			InitializePresets();
		}

		public static void Initialize()
		{
			InitializePresets();
		}

		private static void InitializePresets()
		{
			try
			{
				if (!Directory.Exists(PresetsDirectory))
				{
					Directory.CreateDirectory(PresetsDirectory);
				}
				CreateDefaultPresets();
				LoadAllPresets();
				_currentPreset = GetPreset("Default") ?? CreateDefaultPreset();
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Failed to initialize audio presets: " + ex.Message));
				_currentPreset = CreateDefaultPreset();
			}
		}

		private static void CreateDefaultPresets()
		{
			SavePreset(CreateDefaultPreset());
			SavePreset(new AudioPreset("High Quality", "Maximum quality settings for best audio processing")
			{
				NoiseSuppressionStrength = 0.9f,
				ProcessingQuality = 10,
				EchoFilterLength = 512,
				VoiceGain = 1.2f,
				SpectralSubtractionEnabled = true
			});
			SavePreset(new AudioPreset("Performance", "Optimized for lower CPU usage")
			{
				NoiseSuppressionStrength = 0.6f,
				ProcessingQuality = 3,
				EchoFilterLength = 128,
				SpectralSubtractionEnabled = false,
				RNNoiseEnabled = false
			});
			SavePreset(new AudioPreset("Gaming", "Balanced settings for gaming with voice chat")
			{
				NoiseSuppressionStrength = 0.7f,
				VoiceDuckingEnabled = true,
				DuckingLevel = 0.4f,
				ProcessingQuality = 5,
				LoopDetectionEnabled = true
			});
			SavePreset(new AudioPreset("Streaming", "Professional settings for content creation")
			{
				NoiseSuppressionStrength = 0.85f,
				VoiceGain = 1.1f,
				ProcessingQuality = 8,
				EchoCancellationStrength = 0.8f,
				SpectralSubtractionEnabled = true,
				VoiceDuckingEnabled = false
			});
		}

		private static AudioPreset CreateDefaultPreset()
		{
			return new AudioPreset("Default", "Standard audio processing settings");
		}

		public static void SavePreset(AudioPreset preset)
		{
			try
			{
				string path = Path.Combine(PresetsDirectory, preset.Name + ".json");
				string contents = JsonUtility.ToJson((object)preset, true);
				File.WriteAllText(path, contents);
				_loadedPresets[preset.Name] = preset;
				Debug.Log((object)("Saved audio preset: " + preset.Name));
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Failed to save preset " + preset.Name + ": " + ex.Message));
			}
		}

		public static AudioPreset LoadPreset(string name)
		{
			try
			{
				string path = Path.Combine(PresetsDirectory, name + ".json");
				if (!File.Exists(path))
				{
					Debug.LogWarning((object)("Preset file not found: " + name));
					return null;
				}
				AudioPreset audioPreset = JsonUtility.FromJson<AudioPreset>(File.ReadAllText(path));
				_loadedPresets[name] = audioPreset;
				return audioPreset;
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Failed to load preset " + name + ": " + ex.Message));
				return null;
			}
		}

		private static void LoadAllPresets()
		{
			try
			{
				if (Directory.Exists(PresetsDirectory))
				{
					string[] files = Directory.GetFiles(PresetsDirectory, "*.json");
					for (int i = 0; i < files.Length; i++)
					{
						LoadPreset(Path.GetFileNameWithoutExtension(files[i]));
					}
				}
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Failed to load presets: " + ex.Message));
			}
		}

		public static AudioPreset GetPreset(string name)
		{
			_loadedPresets.TryGetValue(name, out var value);
			return value;
		}

		public static List<string> GetPresetNames()
		{
			return new List<string>(_loadedPresets.Keys);
		}

		public static AudioPreset GetCurrentPreset()
		{
			return _currentPreset;
		}

		public static void SetCurrentPreset(string name)
		{
			AudioPreset preset = GetPreset(name);
			if (preset != null)
			{
				_currentPreset = preset;
				Debug.Log((object)("Switched to audio preset: " + name));
			}
			else
			{
				Debug.LogWarning((object)("Preset not found: " + name));
			}
		}

		public static void SetCurrentPreset(AudioPreset preset)
		{
			if (preset != null)
			{
				_currentPreset = preset;
				Debug.Log((object)("Applied audio preset: " + preset.Name));
			}
		}

		public static void DeletePreset(string name)
		{
			try
			{
				if (name == "Default")
				{
					Debug.LogWarning((object)"Cannot delete the default preset");
					return;
				}
				string path = Path.Combine(PresetsDirectory, name + ".json");
				if (File.Exists(path))
				{
					File.Delete(path);
				}
				_loadedPresets.Remove(name);
				if (_currentPreset?.Name == name)
				{
					SetCurrentPreset("Default");
				}
				Debug.Log((object)("Deleted audio preset: " + name));
			}
			catch (Exception ex)
			{
				Debug.LogError((object)("Failed to delete preset " + name + ": " + ex.Message));
			}
		}

		public static AudioPreset CreatePresetFromCurrent(string name, string description)
		{
			AudioPreset audioPreset = new AudioPreset(name, description);
			if (_currentPreset != null)
			{
				audioPreset.NoiseSuppressionEnabled = _currentPreset.NoiseSuppressionEnabled;
				audioPreset.NoiseSuppressionStrength = _currentPreset.NoiseSuppressionStrength;
				audioPreset.RNNoiseEnabled = _currentPreset.RNNoiseEnabled;
				audioPreset.VoiceEnhancementEnabled = _currentPreset.VoiceEnhancementEnabled;
				audioPreset.VoiceGain = _currentPreset.VoiceGain;
				audioPreset.AutoGainControlEnabled = _currentPreset.AutoGainControlEnabled;
				audioPreset.EchoCancellationEnabled = _currentPreset.EchoCancellationEnabled;
				audioPreset.EchoCancellationStrength = _currentPreset.EchoCancellationStrength;
				audioPreset.EchoFilterLength = _currentPreset.EchoFilterLength;
				audioPreset.VoiceDuckingEnabled = _currentPreset.VoiceDuckingEnabled;
				audioPreset.DuckingLevel = _currentPreset.DuckingLevel;
				audioPreset.DuckingAttackTime = _currentPreset.DuckingAttackTime;
				audioPreset.DuckingReleaseTime = _currentPreset.DuckingReleaseTime;
				audioPreset.ProcessingQuality = _currentPreset.ProcessingQuality;
				audioPreset.SpectralSubtractionEnabled = _currentPreset.SpectralSubtractionEnabled;
				audioPreset.LoopDetectionEnabled = _currentPreset.LoopDetectionEnabled;
				audioPreset.LoopDetectionThreshold = _currentPreset.LoopDetectionThreshold;
			}
			SavePreset(audioPreset);
			return audioPreset;
		}
	}
	public class AdvancedEchoCanceller : IDisposable
	{
		private readonly int _sampleRate;

		private readonly int _frameSize;

		private readonly int _filterLength;

		private readonly float _stepSize;

		private readonly float _regularization;

		private readonly float[] _adaptiveFilter;

		private readonly float[] _referenceBuffer;

		private readonly float[] _errorBuffer;

		private readonly CircularBuffer _microphoneBuffer;

		private readonly CircularBuffer _speakerBuffer;

		private readonly int _maxDelay;

		private readonly float[] _echoPathEstimate;

		private float _echoPathStrength;

		private readonly float _echoPathAdaptationRate = 0.01f;

		private readonly float _nlpThreshold;

		private readonly float _nlpAttenuation;

		private float _nlpGain;

		private readonly float _nlpSmoothingCoeff;

		private readonly DoubleTalkDetector _doubleTalkDetector;

		private bool _isDoubleTalk;

		private readonly Complex[] _fftBuffer;

		private readonly Complex[] _referenceFFT;

		private readonly Complex[] _microphoneFFT;

		private readonly float[] _powerSpectralDensity;

		private readonly float[] _coherenceFunction;

		private readonly int _fftSize;

		private readonly float[] _windowFunction;

		private int _frameCounter;

		private readonly int _adaptationInterval = 4;

		private bool _disposed;

		public float EchoCancellationStrength { get; set; } = 1f;


		public bool IsEnabled { get; set; } = true;


		public AdvancedEchoCanceller(int sampleRate, int frameSize, int filterLength = 512, float stepSize = 0.01f, int maxDelay = 1024)
		{
			_sampleRate = sampleRate;
			_frameSize = frameSize;
			_filterLength = filterLength;
			_stepSize = stepSize;
			_regularization = 1E-06f;
			_maxDelay = maxDelay;
			_fftSize = NextPowerOfTwo(Math.Max(frameSize, filterLength) * 2);
			_adaptiveFilter = new float[_filterLength];
			_referenceBuffer = new float[_filterLength];
			_errorBuffer = new float[_frameSize];
			_echoPathEstimate = new float[_filterLength];
			_microphoneBuffer = new CircularBuffer(_maxDelay + _frameSize);
			_speakerBuffer = new CircularBuffer(_maxDelay + _frameSize);
			_nlpThreshold = 0.1f;
			_nlpAttenuation = 0.3f;
			_nlpGain = 1f;
			_nlpSmoothingCoeff = 0.95f;
			_doubleTalkDetector = new DoubleTalkDetector(sampleRate, frameSize);
			_fftBuffer = new Complex[_fftSize];
			_referenceFFT = new Complex[_fftSize];
			_microphoneFFT = new Complex[_fftSize];
			_powerSpectralDensity = new float[_fftSize / 2 + 1];
			_coherenceFunction = new float[_fftSize / 2 + 1];
			_windowFunction = CreateHannWindow(_frameSize);
			_frameCounter = 0;
		}

		public float[] ProcessAudio(float[] microphoneInput, float[] speakerReference)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("AdvancedEchoCanceller");
			}
			if (!IsEnabled || microphoneInput == null || speakerReference == null)
			{
				return microphoneInput ?? new float[0];
			}
			int num = Math.Min(microphoneInput.Length, speakerReference.Length);
			float[] array = new float[num];
			_microphoneBuffer.Write(microphoneInput);
			_speakerBuffer.Write(speakerReference);
			_isDoubleTalk = _doubleTalkDetector.DetectDoubleTalk(microphoneInput, speakerReference);
			for (int i = 0; i < num; i += _frameSize)
			{
				int num2 = Math.Min(_frameSize, num - i);
				float[] array2 = new float[num2];
				float[] array3 = new float[num2];
				Array.Copy(microphoneInput, i, array2, 0, num2);
				Array.Copy(speakerReference, i, array3, 0, num2);
				Array.Copy(ProcessBlock(array2, array3), 0, array, i, num2);
			}
			_frameCounter++;
			return array;
		}

		private float[] ProcessBlock(float[] microphoneBlock, float[] referenceBlock)
		{
			float[] array = EstimateEcho(referenceBlock);
			float[] array2 = new float[microphoneBlock.Length];
			for (int i = 0; i < microphoneBlock.Length; i++)
			{
				array2[i] = microphoneBlock[i] - array[i] * EchoCancellationStrength;
			}
			if (!_isDoubleTalk && _frameCounter % _adaptationInterval == 0)
			{
				UpdateAdaptiveFilter(referenceBlock, array2);
			}
			float[] signal = ApplyNonLinearProcessing(array2, microphoneBlock, referenceBlock);
			return ApplyFrequencyDomainProcessing(signal, referenceBlock);
		}

		private float[] EstimateEcho(float[] referenceBlock)
		{
			int num = Math.Min(referenceBlock.Length, _referenceBuffer.Length);
			Array.Copy(_referenceBuffer, num, _referenceBuffer, 0, _referenceBuffer.Length - num);
			Array.Copy(referenceBlock, 0, _referenceBuffer, _referenceBuffer.Length - num, num);
			float[] array = new float[referenceBlock.Length];
			for (int i = 0; i < referenceBlock.Length; i++)
			{
				float num2 = 0f;
				for (int j = 0; j < _filterLength && i + j < _referenceBuffer.Length; j++)
				{
					num2 += _adaptiveFilter[j] * _referenceBuffer[_referenceBuffer.Length - 1 - i - j];
				}
				array[i] = num2;
			}
			return array;
		}

		private void UpdateAdaptiveFilter(float[] referenceBlock, float[] errorSignal)
		{
			float num = 0f;
			for (int i = 0; i < _referenceBuffer.Length; i++)
			{
				num += _referenceBuffer[i] * _referenceBuffer[i];
			}
			num += _regularization;
			float num2 = _stepSize / num;
			for (int j = 0; j < Math.Min(errorSignal.Length, referenceBlock.Length); j++)
			{
				float num3 = errorSignal[j];
				for (int k = 0; k < _filterLength; k++)
				{
					int num4 = _referenceBuffer.Length - 1 - j - k;
					if (num4 >= 0 && num4 < _referenceBuffer.Length)
					{
						_adaptiveFilter[k] += num2 * num3 * _referenceBuffer[num4];
					}
				}
			}
			ApplyFilterConstraints();
		}

		private void ApplyFilterConstraints()
		{
			float num = 2f;
			for (int i = 0; i < _adaptiveFilter.Length; i++)
			{
				_adaptiveFilter[i] = Mathf.Clamp(_adaptiveFilter[i], 0f - num, num);
			}
		}

		private float[] ApplyNonLinearProcessing(float[] errorSignal, float[] microphoneSignal, float[] referenceSignal)
		{
			float[] array = new float[errorSignal.Length];
			float errorEnergy = CalculateEnergy(errorSignal);
			CalculateEnergy(microphoneSignal);
			float num = CalculateEnergy(referenceSignal);
			float num2 = EstimateResidualEchoLevel(errorEnergy, num);
			float num3 = 1f;
			if (num2 > _nlpThreshold && num > 0.001f)
			{
				float num4 = Mathf.Clamp01(num2 / _nlpThreshold);
				num3 = Mathf.Lerp(1f, _nlpAttenuation, num4);
			}
			_nlpGain = _nlpGain * _nlpSmoothingCoeff + num3 * (1f - _nlpSmoothingCoeff);
			for (int i = 0; i < errorSignal.Length; i++)
			{
				array[i] = errorSignal[i] * _nlpGain;
			}
			return array;
		}

		private float EstimateResidualEchoLevel(float errorEnergy, float referenceEnergy)
		{
			if (referenceEnergy < 0.001f)
			{
				return 0f;
			}
			float num = errorEnergy / (referenceEnergy + 0.001f);
			_echoPathStrength = _echoPathStrength * (1f - _echoPathAdaptationRate) + num * _echoPathAdaptationRate;
			return _echoPathStrength;
		}

		private float[] ApplyFrequencyDomainProcessing(float[] signal, float[] reference)
		{
			if (signal.Length < _frameSize)
			{
				return signal;
			}
			for (int i = 0; i < _frameSize; i++)
			{
				float num = ((i < signal.Length) ? (signal[i] * _windowFunction[i]) : 0f);
				float num2 = ((i < reference.Length) ? (reference[i] * _windowFunction[i]) : 0f);
				_microphoneFFT[i] = new Complex(num, 0.0);
				_referenceFFT[i] = new Complex(num2, 0.0);
			}
			for (int j = _frameSize; j < _fftSize; j++)
			{
				_microphoneFFT[j] = Complex.Zero;
				_referenceFFT[j] = Complex.Zero;
			}
			SimpleFFT(_microphoneFFT);
			SimpleFFT(_referenceFFT);
			ApplySpectralSuppression(_microphoneFFT, _referenceFFT);
			SimpleIFFT(_microphoneFFT);
			float[] array = new float[signal.Length];
			for (int k = 0; k < array.Length; k++)
			{
				array[k] = (float)_microphoneFFT[k].Real * _windowFunction[k];
			}
			return array;
		}

		private void ApplySpectralSuppression(Complex[] microphoneSpectrum, Complex[] referenceSpectrum)
		{
			int num = _fftSize / 2 + 1;
			for (int i = 0; i < num && i < microphoneSpectrum.Length; i++)
			{
				float num2 = (float)microphoneSpectrum[i].Magnitude;
				float num3 = (float)referenceSpectrum[i].Magnitude;
				float num4 = 0f;
				if (num3 > 0.001f)
				{
					num4 = num2 * num3 / (num3 * num3 + 0.001f);
				}
				float num5 = 1f - num4 * EchoCancellationStrength;
				num5 = Mathf.Clamp(num5, 0.1f, 1f);
				microphoneSpectrum[i] *= (Complex)num5;
			}
		}

		private float CalculateEnergy(float[] signal)
		{
			float num = 0f;
			for (int i = 0; i < signal.Length; i++)
			{
				num += signal[i] * signal[i];
			}
			return num / (float)signal.Length;
		}

		private float[] CreateHannWindow(int size)
		{
			float[] array = new float[size];
			for (int i = 0; i < size; i++)
			{
				array[i] = 0.5f * (1f - Mathf.Cos(MathF.PI * 2f * (float)i / (float)(size - 1)));
			}
			return array;
		}

		private static int NextPowerOfTwo(int value)
		{
			int num;
			for (num = 1; num < value; num <<= 1)
			{
			}
			return num;
		}

		private void SimpleFFT(Complex[] buffer)
		{
			int num = buffer.Length;
			if (num <= 1)
			{
				return;
			}
			int i = 1;
			int num2 = 0;
			for (; i < num; i++)
			{
				int num3 = num >> 1;
				while ((num2 & num3) != 0)
				{
					num2 ^= num3;
					num3 >>= 1;
				}
				num2 ^= num3;
				if (i < num2)
				{
					ref Complex reference = ref buffer[i];
					ref Complex reference2 = ref buffer[num2];
					Complex complex = buffer[num2];
					Complex complex2 = buffer[i];
					reference = complex;
					reference2 = complex2;
				}
			}
			for (int num4 = 2; num4 <= num; num4 <<= 1)
			{
				double num5 = Math.PI * 2.0 / (double)num4;
				Complex complex3 = new Complex(Math.Cos(num5), Math.Sin(num5));
				for (int j = 0; j < num; j += num4)
				{
					Complex one = Complex.One;
					for (int k = 0; k < num4 / 2; k++)
					{
						Complex complex4 = buffer[j + k];
						Complex complex5 = buffer[j + k + num4 / 2] * one;
						buffer[j + k] = complex4 + complex5;
						buffer[j + k + num4 / 2] = complex4 - complex5;
						one *= complex3;
					}
				}
			}
		}

		private void SimpleIFFT(Complex[] buffer)
		{
			for (int i = 0; i < buffer.Length; i++)
			{
				buffer[i] = Complex.Conjugate(buffer[i]);
			}
			SimpleFFT(buffer);
			for (int j = 0; j < buffer.Length; j++)
			{
				buffer[j] = Complex.Conjugate(buffer[j]) / (Complex)buffer.Length;
			}
		}

		public void Reset()
		{
			Array.Clear(_adaptiveFilter, 0, _adaptiveFilter.Length);
			Array.Clear(_referenceBuffer, 0, _referenceBuffer.Length);
			_microphoneBuffer.Clear();
			_speakerBuffer.Clear();
			_echoPathStrength = 0f;
			_nlpGain = 1f;
			_doubleTalkDetector?.Reset();
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_doubleTalkDetector?.Dispose();
				_disposed = true;
			}
		}
	}
	public class DoubleTalkDetector : IDisposable
	{
		private readonly float[] _micEnergyHistory;

		private readonly float[] _refEnergyHistory;

		private readonly int _historySize = 10;

		private int _historyIndex;

		private bool _disposed;

		public DoubleTalkDetector(int sampleRate, int frameSize)
		{
			_micEnergyHistory = new float[_historySize];
			_refEnergyHistory = new float[_historySize];
		}

		public bool DetectDoubleTalk(float[] microphoneSignal, float[] referenceSignal)
		{
			if (_disposed)
			{
				return false;
			}
			float num = CalculateEnergy(microphoneSignal);
			float num2 = CalculateEnergy(referenceSignal);
			_micEnergyHistory[_historyIndex] = num;
			_refEnergyHistory[_historyIndex] = num2;
			_historyIndex = (_historyIndex + 1) % _historySize;
			float num3 = _micEnergyHistory.Average();
			float num4 = _refEnergyHistory.Average();
			if (num4 < 0.001f)
			{
				return false;
			}
			return num3 / num4 > 0.5f;
		}

		private float CalculateEnergy(float[] signal)
		{
			float num = 0f;
			for (int i = 0; i < signal.Length; i++)
			{
				num += signal[i] * signal[i];
			}
			return num / (float)signal.Length;
		}

		public void Reset()
		{
			Array.Clear(_micEnergyHistory, 0, _micEnergyHistory.Length);
			Array.Clear(_refEnergyHistory, 0, _refEnergyHistory.Length);
			_historyIndex = 0;
		}

		public void Dispose()
		{
			_disposed = true;
		}
	}
	public class AINoiseSuppressionProcessor : IDisposable
	{
		private readonly int _sampleRate;

		private readonly int _frameSize;

		private readonly int _fftSize;

		private readonly float _noiseReductionStrength;

		private readonly NeuralNoiseReducer _neuralReducer;

		private readonly Complex[] _fftBuffer;

		private readonly Complex[] _noiseProfile;

		private readonly float[] _magnitudeSpectrum;

		private readonly float[] _phaseSpectrum;

		private readonly float[] _noiseMagnitude;

		private readonly float[] _cleanMagnitude;

		private readonly float[] _windowFunction;

		private readonly float[] _noiseFloorEstimate;

		private readonly float[] _signalToNoiseRatio;

		private readonly float _noiseAdaptationRate = 0.01f;

		private readonly float _signalAdaptationRate = 0.1f;

		private readonly int _numBands = 8;

		private float[][] _bandFilters;

		private float[] _bandGains;

		private float[] _bandNoiseFloors;

		private readonly VoiceActivityDetector _vadForNoise;

		private bool _isLearningNoise;

		private int _noiseLearnFrames;

		private readonly int _maxNoiseLearnFrames = 100;

		private readonly SpectralSubtractor _spectralSubtractor;

		private readonly WienerFilter _wienerFilter;

		private readonly ResidualNoiseReducer _residualReducer;

		private readonly float[] _overlapBuffer;

		private readonly int _hopSize;

		private int _frameCounter;

		private bool _disposed;

		public float NoiseReductionStrength { get; set; } = 1f;


		public bool IsEnabled { get; set; } = true;


		public bool IsLearningNoise => _isLearningNoise;

		public AINoiseSuppressionProcessor(int sampleRate, int frameSize, float noiseReductionStrength = 0.8f)
		{
			_sampleRate = sampleRate;
			_frameSize = frameSize;
			_fftSize = NextPowerOfTwo(frameSize * 2);
			_noiseReductionStrength = Mathf.Clamp01(noiseReductionStrength);
			_hopSize = frameSize / 2;
			_fftBuffer = new Complex[_fftSize];
			_noiseProfile = new Complex[_fftSize];
			_magnitudeSpectrum = new float[_fftSize / 2 + 1];
			_phaseSpectrum = new float[_fftSize / 2 + 1];
			_noiseMagnitude = new float[_fftSize / 2 + 1];
			_cleanMagnitude = new float[_fftSize / 2 + 1];
			_noiseFloorEstimate = new float[_fftSize / 2 + 1];
			_signalToNoiseRatio = new float[_fftSize / 2 + 1];
			_overlapBuffer = new float[_frameSize];
			_windowFunction = CreateHannWindow(_frameSize);
			InitializeMultiBandProcessing();
			_neuralReducer = new NeuralNoiseReducer(sampleRate, _fftSize / 2 + 1);
			_vadForNoise = new VoiceActivityDetector(sampleRate, frameSize);
			_spectralSubtractor = new SpectralSubtractor(_fftSize / 2 + 1);
			_wienerFilter = new WienerFilter(_fftSize / 2 + 1);
			_residualReducer = new ResidualNoiseReducer(_fftSize / 2 + 1);
			_isLearningNoise = true;
			_noiseLearnFrames = 0;
			for (int i = 0; i < _noiseFloorEstimate.Length; i++)
			{
				_noiseFloorEstimate[i] = 0.001f;
			}
		}

		private void InitializeMultiBandProcessing()
		{
			_bandFilters = new float[_numBands][];
			_bandGains = new float[_numBands];
			_bandNoiseFloors = new float[_numBands];
			for (int i = 0; i < _numBands; i++)
			{
				_bandFilters[i] = new float[_fftSize / 2 + 1];
				_bandGains[i] = 1f;
				_bandNoiseFloors[i] = 0.001f;
				float num = (float)(20.0 * Math.Pow(2.0, (double)i * 10.0 / (double)_numBands));
				float num2 = (float)(20.0 * Math.Pow(2.0, (double)(i + 1) * 10.0 / (double)_numBands));
				int num3 = (int)(num * (float)_fftSize / (float)_sampleRate);
				int num4 = (int)(num2 * (float)_fftSize / (float)_sampleRate);
				for (int j = 0; j < _bandFilters[i].Length; j++)
				{
					if (j >= num3 && j <= num4)
					{
						float num5 = (float)(j - num3) / (float)(num4 - num3);
						_bandFilters[i][j] = 0.5f * (1f - Mathf.Cos(MathF.PI * num5));
					}
					else
					{
						_bandFilters[i][j] = 0f;
					}
				}
			}
		}

		public float[] ProcessAudio(float[] inputAudio)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("AINoiseSuppressionProcessor");
			}
			if (!IsEnabled || inputAudio == null || inputAudio.Length == 0)
			{
				return inputAudio ?? new float[0];
			}
			float[] array = new float[inputAudio.Length];
			for (int i = 0; i < inputAudio.Length; i += _hopSize)
			{
				int length = Math.Min(_frameSize, inputAudio.Length - i);
				float[] frame = ExtractFrame(inputAudio, i, length);
				float[] frame2 = ProcessFrame(frame);
				OverlapAdd(array, frame2, i);
			}
			_frameCounter++;
			return array;
		}

		private float[] ExtractFrame(float[] input, int startIndex, int length)
		{
			float[] array = new float[_frameSize];
			for (int i = 0; i < length && startIndex + i < input.Length; i++)
			{
				array[i] = input[startIndex + i];
			}
			for (int j = 0; j < _frameSize; j++)
			{
				array[j] *= _windowFunction[j];
			}
			return array;
		}

		private float[] ProcessFrame(float[] frame)
		{
			PrepareFFTBuffer(frame);
			SimpleFFT(_fftBuffer);
			ExtractMagnitudeAndPhase();
			bool voiceActive = _vadForNoise.DetectVoiceActivity(frame);
			UpdateNoiseModel(voiceActive);
			float[] magnitude = ApplyMultiStageNoiseReduction(_magnitudeSpectrum);
			ReconstructSignal(magnitude, _phaseSpectrum);
			SimpleIFFT(_fftBuffer);
			return ExtractOutputFrame();
		}

		private void PrepareFFTBuffer(float[] frame)
		{
			for (int i = 0; i < _fftSize; i++)
			{
				if (i < frame.Length)
				{
					_fftBuffer[i] = new Complex(frame[i], 0.0);
				}
				else
				{
					_fftBuffer[i] = Complex.Zero;
				}
			}
		}

		private void ExtractMagnitudeAndPhase()
		{
			for (int i = 0; i < _magnitudeSpectrum.Length; i++)
			{
				_magnitudeSpectrum[i] = (float)_fftBuffer[i].Magnitude;
				_phaseSpectrum[i] = (float)_fftBuffer[i].Phase;
			}
		}

		private void UpdateNoiseModel(bool voiceActive)
		{
			if (_isLearningNoise && _noiseLearnFrames < _maxNoiseLearnFrames)
			{
				if (!voiceActive || _noiseLearnFrames < 20)
				{
					for (int i = 0; i < _noiseFloorEstimate.Length; i++)
					{
						_noiseFloorEstimate[i] = _noiseFloorEstimate[i] * (1f - _noiseAdaptationRate) + _magnitudeSpectrum[i] * _noiseAdaptationRate;
					}
				}
				_noiseLearnFrames++;
				if (_noiseLearnFrames >= _maxNoiseLearnFrames)
				{
					_isLearningNoise = false;
				}
			}
			else if (!voiceActive)
			{
				for (int j = 0; j < _noiseFloorEstimate.Length; j++)
				{
					float num = _noiseAdaptationRate * 0.1f;
					_noiseFloorEstimate[j] = _noiseFloorEstimate[j] * (1f - num) + _magnitudeSpectrum[j] * num;
				}
			}
			for (int k = 0; k < _signalToNoiseRatio.Length; k++)
			{
				float num2 = _magnitudeSpectrum[k] / (_noiseFloorEstimate[k] + 1E-06f);
				if (voiceActive)
				{
					_signalToNoiseRatio[k] = _signalToNoiseRatio[k] * (1f - _signalAdaptationRate) + num2 * _signalAdaptationRate;
				}
				else
				{
					_signalToNoiseRatio[k] = num2;
				}
			}
		}

		private float[] ApplyMultiStageNoiseReduction(float[] magnitude)
		{
			float[] magnitude2 = _spectralSubtractor.Process(magnitude, _noiseFloorEstimate, NoiseReductionStrength);
			float[] input = _wienerFilter.Process(magnitude2, _signalToNoiseRatio);
			float[] magnitude3 = _neuralReducer.Process(input, _noiseFloorEstimate);
			float[] processed = ApplyMultiBandProcessing(magnitude3);
			return _residualReducer.Process(processed, magnitude, _noiseFloorEstimate);
		}

		private float[] ApplyMultiBandProcessing(float[] magnitude)
		{
			float[] array = new float[magnitude.Length];
			for (int i = 0; i < _numBands; i++)
			{
				float num = 0f;
				float num2 = 0f;
				int num3 = 0;
				for (int j = 0; j < magnitude.Length; j++)
				{
					if (_bandFilters[i][j] > 0.1f)
					{
						num += magnitude[j] * _bandFilters[i][j];
						num2 += _noiseFloorEstimate[j] * _bandFilters[i][j];
						num3++;
					}
				}
				if (num3 > 0)
				{
					num /= (float)num3;
					num2 /= (float)num3;
					float snr = num / (num2 + 1E-06f);
					_bandGains[i] = CalculateBandGain(snr, i);
				}
			}
			for (int k = 0; k < magnitude.Length; k++)
			{
				float num4 = 0f;
				float num5 = 0f;
				for (int l = 0; l < _numBands; l++)
				{
					float num6 = _bandFilters[l][k];
					num4 += _bandGains[l] * num6;
					num5 += num6;
				}
				if (num5 > 0f)
				{
					array[k] = magnitude[k] * (num4 / num5);
				}
				else
				{
					array[k] = magnitude[k];
				}
			}
			return array;
		}

		private float CalculateBandGain(float snr, int bandIndex)
		{
			float num = 1f;
			num = ((snr < 1f) ? Mathf.Lerp(0.1f, 1f, snr) : ((!(snr > 10f)) ? Mathf.Lerp(0.5f, 1f, (snr - 1f) / 9f) : 1f));
			if (bandIndex < 2)
			{
				num = Mathf.Lerp(num, 1f, 0.3f);
			}
			else if (bandIndex > 5)
			{
				num *= 0.8f;
			}
			return Mathf.Clamp(num, 0.05f, 1f);
		}

		private void ReconstructSignal(float[] magnitude, float[] phase)
		{
			for (int i = 0; i < magnitude.Length && i < _fftBuffer.Length; i++)
			{
				float num = magnitude[i] * Mathf.Cos(phase[i]);
				float num2 = magnitude[i] * Mathf.Sin(phase[i]);
				_fftBuffer[i] = new Complex(num, num2);
			}
			for (int j = magnitude.Length; j < _fftSize; j++)
			{
				int num3 = _fftSize - j;
				if (num3 < magnitude.Length)
				{
					_fftBuffer[j] = Complex.Conjugate(_fftBuffer[num3]);
				}
				else
				{
					_fftBuffer[j] = Complex.Zero;
				}
			}
		}

		private float[] ExtractOutputFrame()
		{
			float[] array = new float[_frameSize];
			for (int i = 0; i < _frameSize; i++)
			{
				array[i] = (float)_fftBuffer[i].Real * _windowFunction[i];
			}
			return array;
		}

		private void OverlapAdd(float[] output, float[] frame, int startIndex)
		{
			for (int i = 0; i < frame.Length && startIndex + i < output.Length; i++)
			{
				output[startIndex + i] += frame[i];
			}
		}

		private float[] CreateHannWindow(int size)
		{
			float[] array = new float[size];
			for (int i = 0; i < size; i++)
			{
				array[i] = 0.5f * (1f - Mathf.Cos(MathF.PI * 2f * (float)i / (float)(size - 1)));
			}
			return array;
		}

		private static int NextPowerOfTwo(int value)
		{
			int num;
			for (num = 1; num < value; num <<= 1)
			{
			}
			return num;
		}

		private void SimpleFFT(Complex[] buffer)
		{
			int num = buffer.Length;
			if (num <= 1)
			{
				return;
			}
			int i = 1;
			int num2 = 0;
			for (; i < num; i++)
			{
				int num3 = num >> 1;
				while ((num2 & num3) != 0)
				{
					num2 ^= num3;
					num3 >>= 1;
				}
				num2 ^= num3;
				if (i < num2)
				{
					ref Complex reference = ref buffer[i];
					ref Complex reference2 = ref buffer[num2];
					Complex complex = buffer[num2];
					Complex complex2 = buffer[i];
					reference = complex;
					reference2 = complex2;
				}
			}
			for (int num4 = 2; num4 <= num; num4 <<= 1)
			{
				double num5 = Math.PI * 2.0 / (double)num4;
				Complex complex3 = new Complex(Math.Cos(num5), Math.Sin(num5));
				for (int j = 0; j < num; j += num4)
				{
					Complex one = Complex.One;
					for (int k = 0; k < num4 / 2; k++)
					{
						Complex complex4 = buffer[j + k];
						Complex complex5 = buffer[j + k + num4 / 2] * one;
						buffer[j + k] = complex4 + complex5;
						buffer[j + k + num4 / 2] = complex4 - complex5;
						one *= complex3;
					}
				}
			}
		}

		private void SimpleIFFT(Complex[] buffer)
		{
			for (int i = 0; i < buffer.Length; i++)
			{
				buffer[i] = Complex.Conjugate(buffer[i]);
			}
			SimpleFFT(buffer);
			for (int j = 0; j < buffer.Length; j++)
			{
				buffer[j] = Complex.Conjugate(buffer[j]) / (Complex)buffer.Length;
			}
		}

		public void Reset()
		{
			_isLearningNoise = true;
			_noiseLearnFrames = 0;
			Array.Clear(_noiseFloorEstimate, 0, _noiseFloorEstimate.Length);
			Array.Clear(_overlapBuffer, 0, _overlapBuffer.Length);
			for (int i = 0; i < _noiseFloorEstimate.Length; i++)
			{
				_noiseFloorEstimate[i] = 0.001f;
			}
			_neuralReducer?.Reset();
			_vadForNoise?.Reset();
			_spectralSubtractor?.Reset();
			_wienerFilter?.Reset();
			_residualReducer?.Reset();
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_neuralReducer?.Dispose();
				_vadForNoise?.Dispose();
				_spectralSubtractor?.Dispose();
				_wienerFilter?.Dispose();
				_residualReducer?.Dispose();
				_disposed = true;
			}
		}
	}
	public class NeuralNoiseReducer : IDisposable
	{
		private readonly int _inputSize;

		private readonly float[] _weights;

		private readonly float[] _biases;

		private bool _disposed;

		public NeuralNoiseReducer(int sampleRate, int inputSize)
		{
			_inputSize = inputSize;
			_weights = new float[inputSize];
			_biases = new float[inputSize];
			InitializeWeights();
		}

		private void InitializeWeights()
		{
			for (int i = 0; i < _inputSize; i++)
			{
				float num = (float)i / (float)_inputSize;
				if (num > 0.1f && num < 0.7f)
				{
					_weights[i] = 1.2f;
				}
				else
				{
					_weights[i] = 0.8f;
				}
				_biases[i] = 0.1f;
			}
		}

		public float[] Process(float[] input, float[] noiseFloor)
		{
			if (_disposed)
			{
				return input;
			}
			float[] array = new float[input.Length];
			for (int i = 0; i < Math.Min(input.Length, _inputSize); i++)
			{
				float val = (float)Math.Tanh(input[i] / (noiseFloor[i] + 1E-06f) * _weights[i] + _biases[i]);
				array[i] = input[i] * Math.Max(0f, Math.Min(1f, val));
			}
			return array;
		}

		public void Reset()
		{
		}

		public void Dispose()
		{
			_disposed = true;
		}
	}
	public class SpectralSubtractor : IDisposable
	{
		private readonly int _size;

		private bool _disposed;

		public SpectralSubtractor(int size)
		{
			_size = size;
		}

		public float[] Process(float[] magnitude, float[] noiseFloor, float strength)
		{
			if (_disposed)
			{
				return magnitude;
			}
			float[] array = new float[magnitude.Length];
			for (int i = 0; i < magnitude.Length; i++)
			{
				float num = magnitude[i] - noiseFloor[i] * strength;
				array[i] = Mathf.Max(num, magnitude[i] * 0.1f);
			}
			return array;
		}

		public void Reset()
		{
		}

		public void Dispose()
		{
			_disposed = true;
		}
	}
	public class WienerFilter : IDisposable
	{
		private readonly int _size;

		private bool _disposed;

		public WienerFilter(int size)
		{
			_size = size;
		}

		public float[] Process(float[] magnitude, float[] snr)
		{
			if (_disposed)
			{
				return magnitude;
			}
			float[] array = new float[magnitude.Length];
			for (int i = 0; i < magnitude.Length; i++)
			{
				float num = snr[i] / (snr[i] + 1f);
				array[i] = magnitude[i] * num;
			}
			return array;
		}

		public void Reset()
		{
		}

		public void Dispose()
		{
			_disposed = true;
		}
	}
	public class ResidualNoiseReducer : IDisposable
	{
		private readonly int _size;

		private bool _disposed;

		public ResidualNoiseReducer(int size)
		{
			_size = size;
		}

		public float[] Process(float[] processed, float[] original, float[] noiseFloor)
		{
			if (_disposed)
			{
				return processed;
			}
			float[] array = new float[processed.Length];
			for (int i = 0; i < processed.Length; i++)
			{
				if (processed[i] / (original[i] + 1E-06f) < 0.3f && processed[i] < noiseFloor[i] * 2f)
				{
					array[i] = processed[i] * 0.5f;
				}
				else
				{
					array[i] = processed[i];
				}
			}
			return array;
		}

		public void Reset()
		{
		}

		public void Dispose()
		{
			_disposed = true;
		}
	}
	public class VoiceActivityDetector : IDisposable
	{
		private readonly int _sampleRate;

		private readonly int _frameSize;

		private readonly float[] _energyHistory;

		private readonly int _historySize = 10;

		private int _historyIndex;

		private float _noiseFloor;

		private bool _disposed;

		public VoiceActivityDetector(int sampleRate, int frameSize)
		{
			_sampleRate = sampleRate;
			_frameSize = frameSize;
			_energyHistory = new float[_historySize];
			_noiseFloor = 0.001f;
		}

		public bool DetectVoiceActivity(float[] frame)
		{
			if (_disposed)
			{
				return false;
			}
			float num = 0f;
			float num2 = 0f;
			float num3 = 0f;
			for (int i = 0; i < frame.Length; i++)
			{
				float num4 = Mathf.Abs(frame[i]);
				num += frame[i] * frame[i];
				num2 += (float)i * num4;
				num3 += num4;
			}
			num /= (float)frame.Length;
			if (num3 > 0f)
			{
				num2 /= num3;
			}
			_energyHistory[_historyIndex] = num;
			_historyIndex = (_historyIndex + 1) % _historySize;
			float num5 = _energyHistory.Min();
			_noiseFloor = _noiseFloor * 0.995f + num5 * 0.005f;
			bool flag = num > _noiseFloor * 8f;
			float num6 = 150f * (float)frame.Length / (float)_sampleRate;
			float num7 = 3400f * (float)frame.Length / (float)_sampleRate;
			bool flag2 = num2 >= num6 && num2 <= num7;
			int num8 = 0;
			for (int j = 1; j < frame.Length; j++)
			{
				if (frame[j] >= 0f != frame[j - 1] >= 0f)
				{
					num8++;
				}
			}
			float num9 = (float)num8 / (float)frame.Length;
			bool flag3 = num9 > 0.02f && num9 < 0.3f;
			return flag && flag2 && flag3;
		}

		public void Reset()
		{
			Array.Clear(_energyHistory, 0, _energyHistory.Length);
			_historyIndex = 0;
			_noiseFloor = 0.001f;
		}

		public void Dispose()
		{
			_disposed = true;
		}
	}
	public class AudioCompressorProcessor : IDisposable
	{
		private readonly int _sampleRate;

		private float _attackTime;

		private float _releaseTime;

		private float _threshold;

		private float _ratio;

		private float _makeupGain;

		private float _envelope;

		private float _attackCoeff;

		private float _releaseCoeff;

		private bool _isEnabled;

		private bool _disposed;

		public bool IsEnabled
		{
			get
			{
				return _isEnabled;
			}
			set
			{
				_isEnabled = value;
			}
		}

		public float Threshold { get; set; } = -20f;


		public float Ratio { get; set; } = 4f;


		public float AttackTime { get; set; } = 10f;


		public float ReleaseTime { get; set; } = 100f;


		public float MakeupGain { get; set; }

		public AudioCompressorProcessor(int sampleRate, float threshold = -20f, float ratio = 4f, float attackTime = 10f, float releaseTime = 100f, float makeupGain = 0f)
		{
			_sampleRate = sampleRate;
			_threshold = DecibelToLinear(threshold);
			_ratio = ratio;
			_attackTime = attackTime;
			_releaseTime = releaseTime;
			_makeupGain = DecibelToLinear(makeupGain);
			CalculateCoefficients();
			_envelope = 0f;
			_isEnabled = true;
		}

		private void CalculateCoefficients()
		{
			_attackCoeff = Mathf.Exp(-1f / (_attackTime * 0.001f * (float)_sampleRate));
			_releaseCoeff = Mathf.Exp(-1f / (_releaseTime * 0.001f * (float)_sampleRate));
		}

		public float[] ProcessAudio(float[] inputAudio)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("AudioCompressorProcessor");
			}
			if (!_isEnabled || inputAudio == null || inputAudio.Length == 0)
			{
				return inputAudio ?? new float[0];
			}
			float[] array = new float[inputAudio.Length];
			for (int i = 0; i < inputAudio.Length; i++)
			{
				float num = Mathf.Abs(inputAudio[i]);
				float num2 = ((num > _envelope) ? _attackCoeff : _releaseCoeff);
				_envelope = _envelope * num2 + num * (1f - num2);
				float num3 = CalculateCompressionGain(_envelope);
				array[i] = inputAudio[i] * num3 * _makeupGain;
			}
			return array;
		}

		private float CalculateCompressionGain(float inputLevel)
		{
			if (inputLevel <= _threshold)
			{
				return 1f;
			}
			float num = (inputLevel - _threshold) * (1f - 1f / _ratio);
			return Mathf.Clamp((inputLevel - num) / inputLevel, 0.001f, 1f);
		}

		public void UpdateSettings(float threshold, float ratio, float attackTime, float releaseTime, float makeupGain)
		{
			Threshold = threshold;
			Ratio = ratio;
			AttackTime = attackTime;
			ReleaseTime = releaseTime;
			MakeupGain = makeupGain;
			_threshold = DecibelToLinear(threshold);
			_ratio = ratio;
			_attackTime = attackTime;
			_releaseTime = releaseTime;
			_makeupGain = DecibelToLinear(makeupGain);
			CalculateCoefficients();
		}

		public void Reset()
		{
			_envelope = 0f;
		}

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

		private static float LinearToDecibel(float linear)
		{
			return 20f * Mathf.Log10(Mathf.Max(linear, 0.0001f));
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
			}
		}
	}
	public class SpectralSubtractionProcessor : IDisposable
	{
		private readonly int _sampleRate;

		private readonly int _channels;

		private readonly int _fftSize;

		private readonly float[] _noiseSpectrum;

		private readonly Complex[] _fftBuffer;

		private readonly float[] _magnitudeBuffer;

		private readonly float[] _phaseBuffer;

		private readonly float[] _windowFunction;

		private readonly float _alpha = 2f;

		private readonly float _beta = 0.01f;

		private bool _noiseEstimated;

		private int _frameCount;

		private bool _disposed;

		public SpectralSubtractionProcessor(int sampleRate, int channels, int fftSize = 1024)
		{
			_sampleRate = sampleRate;
			_channels = channels;
			_fftSize = fftSize;
			_noiseSpectrum = new float[_fftSize / 2 + 1];
			_fftBuffer = new Complex[_fftSize];
			_magnitudeBuffer = new float[_fftSize / 2 + 1];
			_phaseBuffer = new float[_fftSize / 2 + 1];
			_windowFunction = CreateHannWindow(_fftSize);
		}

		public void ProcessAudio(float[] data, int offset, int length)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("SpectralSubtractionProcessor");
			}
			int num = _fftSize / 4;
			for (int i = 0; i < length - _fftSize; i += num)
			{
				ProcessFrame(data, offset + i);
			}
		}

		private void ProcessFrame(float[] data, int offset)
		{
			for (int i = 0; i < _fftSize; i++)
			{
				if (offset + i < data.Length)
				{
					_fftBuffer[i] = new Complex(data[offset + i] * _windowFunction[i], 0.0);
				}
				else
				{
					_fftBuffer[i] = Complex.Zero;
				}
			}
			SimpleFFT(_fftBuffer);
			for (int j = 0; j < _fftSize / 2 + 1; j++)
			{
				_magnitudeBuffer[j] = (float)_fftBuffer[j].Magnitude;
				_phaseBuffer[j] = (float)Math.Atan2(_fftBuffer[j].Imaginary, _fftBuffer[j].Real);
			}
			if (!_noiseEstimated && _frameCount < 10)
			{
				for (int k = 0; k < _magnitudeBuffer.Length; k++)
				{
					_noiseSpectrum[k] = (_noiseSpectrum[k] * (float)_frameCount + _magnitudeBuffer[k]) / (float)(_frameCount + 1);
				}
				_frameCount++;
				if (_frameCount >= 10)
				{
					_noiseEstimated = true;
				}
			}
			if (_noiseEstimated)
			{
				for (int l = 0; l < _magnitudeBuffer.Length; l++)
				{
					float val = _magnitudeBuffer[l] - _alpha * _noiseSpectrum[l];
					float val2 = _beta * _magnitudeBuffer[l];
					_magnitudeBuffer[l] = Math.Max(val, val2);
				}
			}
			for (int m = 0; m < _fftSize / 2 + 1; m++)
			{
				_fftBuffer[m] = Complex.FromPolarCoordinates(_magnitudeBuffer[m], _phaseBuffer[m]);
				if (m > 0 && m < _fftSize / 2)
				{
					_fftBuffer[_fftSize - m] = Complex.Conjugate(_fftBuffer[m]);
				}
			}
			SimpleIFFT(_fftBuffer);
			for (int n = 0; n < _fftSize && offset + n < data.Length; n++)
			{
				data[offset + n] = (float)_fftBuffer[n].Real * _windowFunction[n] * 0.5f;
			}
		}

		private float[] CreateHannWindow(int size)
		{
			float[] array = new float[size];
			for (int i = 0; i < size; i++)
			{
				array[i] = 0.5f * (1f - Mathf.Cos(MathF.PI * 2f * (float)i / (float)(size - 1)));
			}
			return array;
		}

		private void SimpleFFT(Complex[] buffer)
		{
			int num = buffer.Length;
			if (num <= 1)
			{
				return;
			}
			int i = 1;
			int num2 = 0;
			for (; i < num; i++)
			{
				int num3 = num >> 1;
				while ((num2 & num3) != 0)
				{
					num2 ^= num3;
					num3 >>= 1;
				}
				num2 ^= num3;
				if (i < num2)
				{
					ref Complex reference = ref buffer[i];
					ref Complex reference2 = ref buffer[num2];
					Complex complex = buffer[num2];
					Complex complex2 = buffer[i];
					reference = complex;
					reference2 = complex2;
				}
			}
			for (int num4 = 2; num4 <= num; num4 <<= 1)
			{
				double num5 = Math.PI * 2.0 / (double)num4;
				Complex complex3 = new Complex(Math.Cos(num5), Math.Sin(num5));
				for (int j = 0; j < num; j += num4)
				{
					Complex one = Complex.One;
					for (int k = 0; k < num4 / 2; k++)
					{
						Complex complex4 = buffer[j + k];
						Complex complex5 = buffer[j + k + num4 / 2] * one;
						buffer[j + k] = complex4 + complex5;
						buffer[j + k + num4 / 2] = complex4 - complex5;
						one *= complex3;
					}
				}
			}
		}

		private void SimpleIFFT(Complex[] buffer)
		{
			for (int i = 0; i < buffer.Length; i++)
			{
				buffer[i] = Complex.Conjugate(buffer[i]);
			}
			SimpleFFT(buffer);
			for (int j = 0; j < buffer.Length; j++)
			{
				buffer[j] = Complex.Conjugate(buffer[j]) / (Complex)buffer.Length;
			}
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
			}
		}
	}
	public class VoiceDuckingProcessor : IDisposable
	{
		private readonly int _sampleRate;

		private readonly int _frameSize;

		private readonly float _attackTime;

		private readonly float _releaseTime;

		private readonly float _threshold;

		private readonly float _ratio;

		private readonly float _makeupGain;

		private float _envelope;

		private readonly float _attackCoeff;

		private readonly float _releaseCoeff;

		private readonly float[] _energyHistory;

		private readonly int _energyHistorySize = 10;

		private int _energyHistoryIndex;

		private float _noiseFloor;

		private readonly float _noiseFloorAdaptationRate = 0.001f;

		private readonly float[] _spectralCentroidHistory;

		private readonly int _spectralHistorySize = 5;

		private int _spectralHistoryIndex;

		private bool _isDucking;

		private float _duckingGain;

		private readonly float _duckingLevel;

		private readonly float _duckingSmoothingTime;

		private readonly float _duckingSmoothingCoeff;

		private readonly float _voiceFreqMin = 300f;

		private readonly float _voiceFreqMax = 3400f;

		private readonly float[] _frequencyBins;

		private readonly int _fftSize = 512;

		private bool _disposed;

		public bool IsDucking => _isDucking;

		public float CurrentDuckingGain => _duckingGain;

		public float NoiseFloor => _noiseFloor;

		public VoiceDuckingProcessor(int sampleRate, int frameSize, float duckingLevel = 0.3f, float threshold = -30f, float ratio = 4f, float attackTime = 0.003f, float releaseTime = 0.1f)
		{
			_sampleRate = sampleRate;
			_frameSize = frameSize;
			_threshold = DecibelToLinear(threshold);
			_ratio = ratio;
			_attackTime = attackTime;
			_releaseTime = releaseTime;
			_makeupGain = 1f;
			_duckingLevel = Mathf.Clamp01(duckingLevel);
			_duckingSmoothingTime = 0.05f;
			_attackCoeff = Mathf.Exp(-1f / (_attackTime * (float)_sampleRate));
			_releaseCoeff = Mathf.Exp(-1f / (_releaseTime * (float)_sampleRate));
			_duckingSmoothingCoeff = Mathf.Exp(-1f / (_duckingSmoothingTime * (float)_sampleRate));
			_energyHistory = new float[_energyHistorySize];
			_spectralCentroidHistory = new float[_spectralHistorySize];
			_noiseFloor = 0.001f;
			_envelope = 0f;
			_duckingGain = 1f;
			_isDucking = false;
			_frequencyBins = new float[_fftSize / 2 + 1];
		}

		public void ProcessGameAudio(bool voiceDetected)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("VoiceDuckingProcessor");
			}
			UpdateDuckingState(voiceDetected);
		}

		public float[] ProcessAudio(float[] inputAudio, float[] gameAudio, bool voiceDetected)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("VoiceDuckingProcessor");
			}
			if (inputAudio == null || gameAudio == null)
			{
				return gameAudio ?? new float[0];
			}
			int num = Math.Min(inputAudio.Length, gameAudio.Length);
			float[] array = new float[num];
			bool flag = AnalyzeVoiceActivity(inputAudio);
			UpdateDuckingState(flag || voiceDetected);
			for (int i = 0; i < num; i++)
			{
				float num2 = (_isDucking ? _duckingLevel : 1f);
				float num3 = ((num2 < _envelope) ? _attackCoeff : _releaseCoeff);
				_envelope = _envelope * num3 + num2 * (1f - num3);
				array[i] = gameAudio[i] * _envelope * _makeupGain;
			}
			return array;
		}

		private bool AnalyzeVoiceActivity(float[] audioData)
		{
			if (audioData == null || audioData.Length == 0)
			{
				return false;
			}
			float num = CalculateRMSEnergy(audioData);
			UpdateNoiseFloor(num);
			_energyHistory[_energyHistoryIndex] = num;
			_energyHistoryIndex = (_energyHistoryIndex + 1) % _energyHistorySize;
			float num2 = CalculateSpectralCentroid(audioData);
			_spectralCentroidHistory[_spectralHistoryIndex] = num2;
			_spectralHistoryIndex = (_spectralHistoryIndex + 1) % _spectralHistorySize;
			bool num3 = num > _noiseFloor * 3f;
			bool flag = IsVoiceLikeSpectrum(num2);
			bool flag2 = IsSustainedActivity();
			return num3 && flag && flag2;
		}

		private float CalculateRMSEnergy(float[] audioData)
		{
			float num = 0f;
			for (int i = 0; i < audioData.Length; i++)
			{
				num += audioData[i] * audioData[i];
			}
			return Mathf.Sqrt(num / (float)audioData.Length);
		}

		private void UpdateNoiseFloor(float currentEnergy)
		{
			if (currentEnergy < _noiseFloor * 2f || _noiseFloor == 0f)
			{
				_noiseFloor = _noiseFloor * (1f - _noiseFloorAdaptationRate) + currentEnergy * _noiseFloorAdaptationRate;
			}
			_noiseFloor = Mathf.Max(_noiseFloor, 0.0001f);
		}

		private float CalculateSpectralCentroid(float[] audioData)
		{
			if (audioData.Length < _fftSize)
			{
				return 0f;
			}
			float num = 0f;
			float num2 = 0f;
			int num3 = (int)(_voiceFreqMin * (float)audioData.Length / (float)_sampleRate);
			int val = (int)(_voiceFreqMax * (float)audioData.Length / (float)_sampleRate);
			for (int i = num3; i < Math.Min(val, audioData.Length); i++)
			{
				float num4 = Mathf.Abs(audioData[i]);
				float num5 = (float)i * (float)_sampleRate / (float)audioData.Length;
				num += num5 * num4;
				num2 += num4;
			}
			if (!(num2 > 0f))
			{
				return 0f;
			}
			return num / num2;
		}

		private bool IsVoiceLikeSpectrum(float spectralCentroid)
		{
			if (spectralCentroid >= _voiceFreqMin)
			{
				return spectralCentroid <= _voiceFreqMax;
			}
			return false;
		}

		private bool IsSustainedActivity()
		{
			int num = 0;
			_energyHistory.Average();
			for (int i = 0; i < _energyHistorySize; i++)
			{
				if (_energyHistory[i] > _noiseFloor * 2f)
				{
					num++;
				}
			}
			return (float)num >= (float)_energyHistorySize * 0.3f;
		}

		private void UpdateDuckingState(bool voiceActive)
		{
			if (voiceActive && !_isDucking)
			{
				_isDucking = true;
			}
			else if (!voiceActive && _isDucking && !HasRecentVoiceActivity())
			{
				_isDucking = false;
			}
		}

		private bool HasRecentVoiceActivity()
		{
			int num = 0;
			int num2 = Math.Min(3, _energyHistorySize);
			for (int i = 0; i < num2; i++)
			{
				int num3 = (_energyHistoryIndex - 1 - i + _energyHistorySize) % _energyHistorySize;
				if (_energyHistory[num3] > _noiseFloor * 3f)
				{
					num++;
				}
			}
			return num > 0;
		}

		public float ProcessSample(float inputSample, float gameAudioSample, bool voiceDetected)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("VoiceDuckingProcessor");
			}
			float num = ((_isDucking || voiceDetected) ? _duckingLevel : 1f);
			_duckingGain = _duckingGain * _duckingSmoothingCoeff + num * (1f - _duckingSmoothingCoeff);
			return gameAudioSample * _duckingGain;
		}

		public void SetDuckingLevel(float level)
		{
			Mathf.Clamp01(level);
		}

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

		private static float LinearToDecibel(float linear)
		{
			return 20f * Mathf.Log10(Mathf.Max(linear, 0.0001f));
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
			}
		}
	}
	public class AdaptiveNoiseFloorEstimator : IDisposable
	{
		private readonly int _sampleRate;

		private readonly float _adaptationRate;

		private readonly float _minNoiseFloor;

		private readonly float _maxNoiseFloor;

		private readonly int _windowSize;

		private readonly float[] _energyHistory;

		private int _historyIndex;

		private float _currentNoiseFloor;

		private float _longTermAverage;

		private float _shortTermAverage;

		private bool _disposed;

		private int _frameCount;

		public AdaptiveNoiseFloorEstimator(int sampleRate, float adaptationRate = 0.01f)
		{
			_sampleRate = sampleRate;
			_adaptationRate = adaptationRate;
			_minNoiseFloor = -60f;
			_maxNoiseFloor = -20f;
			_windowSize = Math.Max(1, sampleRate / 100);
			_energyHistory = new float[100];
			_historyIndex = 0;
			_currentNoiseFloor = -40f;
			_longTermAverage = 0f;
			_shortTermAverage = 0f;
		}

		public float EstimateNoiseFloor(float[] data, int offset, int length)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("AdaptiveNoiseFloorEstimator");
			}
			float num = CalculateRMSEnergy(data, offset, length);
			float num2 = 20f * Mathf.Log10(Mathf.Max(num, 1E-10f));
			_energyHistory[_historyIndex] = num2;
			_historyIndex = (_historyIndex + 1) % _energyHistory.Length;
			_frameCount++;
			UpdateAverages(num2);
			if (_frameCount > _energyHistory.Length)
			{
				float num3 = float.MaxValue;
				float num4 = 0f;
				int num5 = Math.Min(_frameCount, _energyHistory.Length);
				for (int i = 0; i < num5; i++)
				{
					num3 = Math.Min(num3, _energyHistory[i]);
					num4 += _energyHistory[i];
				}
				num4 /= (float)num5;
				float num6 = CalculateEnergyVariance(num4, num5);
				float num7 = Mathf.Clamp(num3 + num6 * 0.5f, _minNoiseFloor, _maxNoiseFloor);
				_currentNoiseFloor = Mathf.Lerp(_currentNoiseFloor, num7, _adaptationRate);
				if (DetectVoiceActivity(num2, num6))
				{
					_currentNoiseFloor = Mathf.Lerp(_currentNoiseFloor, num7, _adaptationRate * 0.1f);
				}
			}
			return _currentNoiseFloor;
		}

		private float CalculateRMSEnergy(float[] data, int offset, int length)
		{
			float num = 0f;
			int num2 = 0;
			for (int i = offset; i < offset + length && i < data.Length; i++)
			{
				num += data[i] * data[i];
				num2++;
			}
			if (num2 <= 0)
			{
				return 0f;
			}
			return Mathf.Sqrt(num / (float)num2);
		}

		private void UpdateAverages(float energyDb)
		{
			_shortTermAverage = _shortTermAverage * 0.9f + energyDb * 0.1f;
			_longTermAverage = _longTermAverage * 0.99f + energyDb * 0.01f;
		}

		private float CalculateEnergyVariance(float avgEnergy, int validSamples)
		{
			float num = 0f;
			for (int i = 0; i < validSamples; i++)
			{
				float num2 = _energyHistory[i] - avgEnergy;
				num += num2 * num2;
			}
			if (validSamples <= 1)
			{
				return 0f;
			}
			return Mathf.Sqrt(num / (float)(validSamples - 1));
		}

		private bool DetectVoiceActivity(float currentEnergyDb, float energyVariance)
		{
			float num = _currentNoiseFloor + 6f;
			bool num2 = currentEnergyDb > num;
			bool flag = energyVariance > 3f;
			bool flag2 = _shortTermAverage > _longTermAverage + 3f;
			if (num2)
			{
				return flag || flag2;
			}
			return false;
		}

		public float GetCurrentNoiseFloor()
		{
			return _currentNoiseFloor;
		}

		public void Reset()
		{
			_historyIndex = 0;
			_frameCount = 0;
			_currentNoiseFloor = -40f;
			_longTermAverage = 0f;
			_shortTermAverage = 0f;
			Array.Clear(_energyHistory, 0, _energyHistory.Length);
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
			}
		}
	}
	public class CircularBuffer
	{
		private readonly float[] _buffer;

		private int _writeIndex;

		private int _readIndex;

		private int _count;

		private readonly object _lock = new object();

		public int Capacity { get; }

		public int Count
		{
			get
			{
				lock (_lock)
				{
					return _count;
				}
			}
		}

		public CircularBuffer(int capacity)
		{
			if (capacity <= 0)
			{
				throw new ArgumentException("Capacity must be positive", "capacity");
			}
			Capacity = capacity;
			_buffer = new float[capacity];
			_writeIndex = 0;
			_readIndex = 0;
			_count = 0;
		}

		public void Write(float[] data)
		{
			if (data == null || data.Length == 0)
			{
				return;
			}
			lock (_lock)
			{
				foreach (float num in data)
				{
					_buffer[_writeIndex] = num;
					_writeIndex = (_writeIndex + 1) % Capacity;
					if (_count < Capacity)
					{
						_count++;
					}
					else
					{
						_readIndex = (_readIndex + 1) % Capacity;
					}
				}
			}
		}

		public int Read(float[] buffer, int offset, int count)
		{
			if (buffer == null)
			{
				throw new ArgumentNullException("buffer");
			}
			if (offset < 0 || offset >= buffer.Length)
			{
				throw new ArgumentOutOfRangeException("offset");
			}
			if (count < 0 || offset + count > buffer.Length)
			{
				throw new ArgumentOutOfRangeException("count");
			}
			lock (_lock)
			{
				int num = Math.Min(count, _count);
				for (int i = 0; i < num; i++)
				{
					buffer[offset + i] = _buffer[_readIndex];
					_readIndex = (_readIndex + 1) % Capacity;
					_count--;
				}
				return num;
			}
		}

		public void Clear()
		{
			lock (_lock)
			{
				_writeIndex = 0;
				_readIndex = 0;
				_count = 0;
				Array.Clear(_buffer, 0, _buffer.Length);
			}
		}

		public float[] ToArray()
		{
			lock (_lock)
			{
				float[] array = new float[_count];
				int num = _readIndex;
				for (int i = 0; i < _count; i++)
				{
					array[i] = _buffer[num];
					num = (num + 1) % Capacity;
				}
				return array;
			}
		}
	}
	public class FrequencyDomainLoopDetector : IDisposable
	{
		private readonly int _sampleRate;

		private readonly int _fftSize;

		private readonly int _maxBufferSize;

		private readonly Complex[] _inputFFT;

		private readonly Complex[] _outputFFT;

		private readonly float[] _inputMagnitude;

		private readonly float[] _outputMagnitude;

		private readonly float[] _correlationHistory;

		private readonly float[] _windowFunction;

		private readonly Queue<float[]> _inputHistory;

		private readonly Queue<float[]> _outputHistory;

		private readonly int _historyLength;

		private int _frameCount;

		private bool _disposed;

		private int[] _frequencyBands;

		private readonly float[] _bandCorrelations;

		private readonly float _voiceFreqMin = 300f;

		private readonly float _voiceFreqMax = 3400f;

		private float _adaptiveThreshold;

		private readonly float _thresholdAdaptationRate = 0.05f;

		public FrequencyDomainLoopDetector(int sampleRate, int fftSize = 1024, int maxBufferSize = 48000)
		{
			_sampleRate = sampleRate;
			_fftSize = fftSize;
			_maxBufferSize = maxBufferSize;
			_inputFFT = new Complex[_fftSize];
			_outputFFT = new Complex[_fftSize];
			_inputMagnitude = new float[_fftSize / 2 + 1];
			_outputMagnitude = new float[_fftSize / 2 + 1];
			_correlationHistory = new float[10];
			_windowFunction = CreateHannWindow(_fftSize);
			_historyLength = 5;
			_inputHistory = new Queue<float[]>(_historyLength);
			_outputHistory = new Queue<float[]>(_historyLength);
			_frameCount = 0;
			_adaptiveThreshold = 0.3f;
			InitializeFrequencyBands();
			_bandCorrelations = new float[_frequencyBands.Length - 1];
		}

		private void InitializeFrequencyBands()
		{
			List<float> source = new List<float>
			{
				0f,
				100f,
				300f,
				800f,
				1500f,
				3000f,
				6000f,
				12000f,
				_sampleRate / 2
			};
			_frequencyBands = source.Select((float f) => (int)(f * (float)_fftSize / (float)_sampleRate)).ToArray();
		}

		public bool DetectLoop(float[] inputData, float[] outputData, float threshold)
		{
			if (_disposed)
			{
				throw new ObjectDisposedException("FrequencyDomainLoopDetector");
			}
			StoreFrameHistory(inputData, outputData);
			if (_inputHistory.Count < 2)
			{
				return false;
			}
			float num = 0f;
			bool flag = false;
			float[][] array = _inputHistory.ToArray();
			float[][] array2 = _outputHistory.ToArray();
			for (int i = 0; i < array.Length - 1; i++)
			{
				for (int j = 0; j < array2.Length; j++)
				{
					float num2 = AnalyzeFrequencyCorrelation(array[i], array2[j]);
					num = Math.Max(num, num2);
					if (num2 > _adaptiveThreshold && ValidateLoopInFrequencyBands(array[i], array2[j]))
					{
						flag = true;
					}
				}
			}
			UpdateAdaptiveThreshold(num);
			UpdateCorrelationHistory(num);
			if (!flag)
			{
				flag = DetectSustainedCorrelation();
			}
			return flag;
		}

		private void StoreFrameHistory(float[] inputData, float[] outputData)
		{
			float[] item = ConvertToMono(inputData);
			float[] item2 = ConvertToMono(outputData);
			_inputHistory.Enqueue(item);
			_outputHistory.Enqueue(item2);
			while (_inputHistory.Count > _historyLength)
			{
				_inputHistory.Dequeue();
				_outputHistory.Dequeue();
			}
			_frameCount++;
		}

		private float[] ConvertToMono(float[] data)
		{
			int num = Math.Min(_fftSize, data.Length);
			float[] array = new float[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = ((i < data.Length) ? data[i] : 0f);
			}
			return array;
		}

		private float AnalyzeFrequencyCorrelation(float[] inputFrame, float[] outputFrame)
		{
			for (int i = 0; i < _fftSize; i++)
			{
				float num = ((i < inputFrame.Length) ? (inputFrame[i] * _windowFunction[i]) : 0f);
				float num2 = ((i < outputFrame.Length) ? (outputFrame[i] * _windowFunction[i]) : 0f);
				_inputFFT[i] = new Complex(num, 0.0);
				_outputFFT[i] = new Complex(num2, 0.0);
			}
			SimpleFFT(_inputFFT);
			SimpleFFT(_outputFFT);
			for (int j = 0; j < _inputMagnitude.Length; j++)
			{
				_inputMagnitude[j] = (float)_inputFFT[j].Magnitude;
				_outputMagnitude[j] = (float)_outputFFT[j].Magnitude;
			}
			return CalculateSpectralCorrelation(_inputMagnitude, _outputMagnitude);
		}

		private float CalculateSpectralCorrelation(float[] spectrum1, float[] spectrum2)
		{
			float num = 0f;
			float num2 = 0f;
			float num3 = 0f;
			int num4 = (int)(_voiceFreqMin * (float)_fftSize / (float)_sampleRate);
			int val = (int)(_voiceFreqMax * (float)_fftSize / (float)_sampleRate);
			for (int i = num4; i < Math.Min(val, spectrum1.Length); i++)
			{
				num += spectrum1[i] * spectrum2[i];
				num2 += spectrum1[i] * spectrum1[i];
				num3 += spectrum2[i] * spectrum2[i];
			}
			if (num2 > 0f && num3 > 0f)
			{
				return num / Mathf.Sqrt(num2 * num3);
			}
			return 0f;
		}

		private bool ValidateLoopInFrequencyBands(float[] inputFrame, float[] outputFrame)
		{
			AnalyzeFrequencyCorrelation(inputFrame, outputFrame);
			for (int i = 0; i < _frequencyBands.Length - 1; i++)
			{
				int num = _frequencyBands[i];
				int val = _frequencyBands[i + 1];
				float num2 = 0f;
				float num3 = 0f;
				float num4 = 0f;
				for (int j = num; j < Math.Min(val, _inputMagnitude.Length); j++)
				{
					num2 += _inputMagnitude[j] * _outputMagnitude[j];
					num3 += _inputMagnitude[j] * _inputMagnitude[j];
					num4 += _outputMagnitude[j] * _outputMagnitude[j];
				}
				if (num3 > 0f && num4 > 0f)
				{
					_bandCorrelations[i] = num2 / Mathf.Sqrt(num3 * num4);
				}
				else
				{
					_bandCorrelations[i] = 0f;
				}
			}
			int num5 = 0;
			for (int k = 0; k < _bandCorrelations.Length; k++)
			{
				if (_bandCorrelations[k] > 0.4f)
				{
					num5++;
				}
			}
			return num5 >= 2;
		}

		private void UpdateAdaptiveThreshold(float currentCorrelation)
		{
			float num = 0.3f;
			float num2 = _correlationHistory.Where((float c) => c > 0f).DefaultIfEmpty(0f).Average();
			if (num2 < 0.1f)
			{
				num = 0.25f;
			}
			else if (num2 > 0.5f)
			{
				num = 0.4f;
			}
			_adaptiveThreshold = Mathf.Lerp(_adaptiveThreshold, num, _thresholdAdaptationRate);
		}

		private void UpdateCorrelationHistory(float correlation)
		{
			for (int num = _correlationHistory.Length - 1; num > 0; num--)
			{
				_correlationHistory[num] = _correlationHistory[num - 1];
			}
			_correlationHistory[0] = correlation;
		}

		private bool DetectSustainedCorrelation()
		{
			int num = 0;
			float num2 = _adaptiveThreshold * 0.8f;
			for (int i = 0; i < Math.Min(5, _correlationHistory.Length); i++)
			{
				if (_correlationHistory[i] > num2)
				{
					num++;
				}
			}
			return num >= 3;
		}

		private float[] CreateHannWindow(int size)
		{
			float[] array = new float[size];
			for (int i = 0; i < size; i++)
			{
				array[i] = 0.5f * (1f - Mathf.Cos(MathF.PI * 2f * (float)i / (float)(size - 1)));
			}
			return array;
		}

		private void SimpleFFT(Complex[] buffer)
		{
			int num = buffer.Length;
			if (num <= 1)
			{
				return;
			}
			int i = 1;
			int num2 = 0;
			for (; i < num; i++)
			{
				int num3 = num >> 1;
				while ((num2 & num3) != 0)
				{
					num2 ^= num3;
					num3 >>= 1;
				}
				num2 ^= num3;
				if (i < num2)
				{
					ref Complex reference = ref buffer[i];
					ref Complex reference2 = ref buffer[num2];
					Complex complex = buffer[num2];
					Complex complex2 = buffer[i];
					reference = complex;
					reference2 = complex2;
				}
			}
			for (int num4 = 2; num4 <= num; num4 <<= 1)
			{
				double num5 = Math.PI * 2.0 / (double)num4;
				Complex complex3 = new Complex(Math.Cos(num5), Math.Sin(num5));
				for (int j = 0; j < num; j += num4)
				{
					Complex one = Complex.One;
					for (int k = 0; k < num4 / 2; k++)
					{
						Complex complex4 = buffer[j + k];
						Complex complex5 = buffer[j + k + num4 / 2] * one;
						buffer[j + k] = complex4 + complex5;
						buffer[j + k + num4 / 2] = complex4 - complex5;
						one *= complex3;
					}
				}
			}
		}

		public void Dispose()
		{
			if (!_disposed)
			{
				_disposed = true;
			}
		}
	}
	[BepInPlugin("com.xenoveni.lethalmic.Static", "LethalMic (Static)", "2.0.0")]
	public class LethalMicStatic : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(StartOfRound), "Start")]
		public static class StartOfRound_Start_Patch
		{
			public static void Postfix()
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				try
				{
					GetLogger().LogInfo((object)"Game started - initializing audio");
					ManualLogSource logger = GetLogger();
					Scene activeScene = SceneManager.GetActiveScene();
					logger.LogInfo((object)("Scene name: " + ((Scene)(ref activeScene)).name));
					StartRecording();
				}
				catch (Exception ex)
				{
					GetLogger().LogError((object)$"Error in game start patch: {ex}");
					GetLogger().LogError((object)("Stack trace: " + ex.StackTrace));
					errorCount++;
				}
			}
		}

		[HarmonyPatch(typeof(StartOfRound), "OnDestroy")]
		public static class StartOfRound_OnDestroy_Patch
		{
			public static void Prefix()
			{
				try
				{
					GetLogger().LogInfo((object)"Game quitting - stopping audio");
					GetLogger().LogInfo((object)$"Total audio frames processed: {audioFrameCount}");
					GetLogger().LogInfo((object)$"Total errors encountered: {errorCount}");
					StopRecording();
				}
				catch (Exception ex)
				{
					GetLogger().LogError((object)$"Error in game quit patch: {ex}");
					GetLogger().LogError((object)("Stack trace: " + ex.StackTrace));
					errorCount++;
				}
			}
		}

		public enum EchoSuppressionMode
		{
			Headphones,
			StereoMix,
			WasapiLoopback
		}

		private static ManualLogSource Logger;

		private static Harmony HarmonyInstance;

		private static ConfigFile ConfigFile;

		private static bool IsInitialized = false;

		private static DateTime LastLogTime = DateTime.MinValue;

		private static int LogCounter = 0;

		private static string selectedDevice;

		private static bool isRecording = false;

		private static float currentMicrophoneLevel = -60f;

		private static float peakMicLevel = -60f;

		private static bool voiceDetected = false;

		private static float noiseFloor = -60f;

		private static float cpuUsage = 0f;

		private static int audioFrameCount = 0;

		private static int errorCount = 0;

		private static ConfigEntry<bool> EnableMod;

		private static ConfigEntry<float> MicrophoneGain;

		private static ConfigEntry<string> InputDevice;

		private static ConfigEntry<bool> NoiseGate;

		private static ConfigEntry<float> NoiseGateThreshold;

		private static ConfigEntry<bool> DebugLogging;

		private static ConfigEntry<bool> Compression;

		private static ConfigEntry<float> CompressionRatio;

		private static ConfigEntry<float> AttackTime;

		private static ConfigEntry<float> ReleaseTime;

		private static ConfigEntry<EchoSuppressionMode> EchoMode;

		private static GameObject uiObject;

		private static LethalMicUI uiInstance;

		private static GameObject updaterObject;

		private static DateTime _lastSummaryLog = DateTime.MinValue;

		private static HashSet<string> _arrayCopyErrors = new HashSet<string>();

		private static DateTime _lastArrayCopyLog = DateTime.MinValue;

		private static DateTime _lastAudioLog = DateTime.MinValue;

		private void Awake()
		{
			//IL_01e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f2: Expected O, but got Unknown
			try
			{
				Logger = Logger.CreateLogSource("LethalMic");
				if (Logger == null)
				{
					Debug.LogError((object)"[LethalMic] Failed to create logger!");
					return;
				}
				GetLogger().LogInfo((object)"Initializing LethalMic static class...");
				ConfigFile = ((BaseUnityPlugin)this).Config;
				EnableMod = ConfigFile.Bind<bool>("General", "EnableMod", true, "Enable/disable the LethalMic mod");
				MicrophoneGain = ConfigFile.Bind<float>("Audio", "MicrophoneGain", 1f, "Microphone input gain (0.0 to 5.0)");
				InputDevice = ConfigFile.Bind<string>("Audio", "InputDevice", "", "Preferred input device (empty for default)");
				NoiseGate = ConfigFile.Bind<bool>("Audio", "NoiseGate", true, "Enable noise gate");
				NoiseGateThreshold = ConfigFile.Bind<float>("Audio", "NoiseGateThreshold", 0.05f, "Noise gate threshold (0.0 to 1.0)");
				DebugLogging = ConfigFile.Bind<bool>("Debug", "DebugLogging", false, "Enable debug logging");
				Compression = ConfigFile.Bind<bool>("Audio", "Compression", true, "Enable audio compression");
				CompressionRatio = ConfigFile.Bind<float>("Audio", "CompressionRatio", 10f, "Audio compression ratio (1:1 to 20:1)");
				AttackTime = ConfigFile.Bind<float>("Audio", "AttackTime", 2f, "Compressor attack time in milliseconds (0-100)");
				ReleaseTime = ConfigFile.Bind<float>("Audio", "ReleaseTime", 50f, "Compressor release time in milliseconds (0-1000)");
				EchoMode = ConfigFile.Bind<EchoSuppressionMode>("Audio", "EchoSuppressionMode", EchoSuppressionMode.Headphones, "Echo/Noise Suppression Mode: Headphones, StereoMix, WasapiLoopback");
				GetLogger().LogInfo((object)$"Configuration loaded - Enabled: {EnableMod.Value}, Gain: {MicrophoneGain.Value}");
				InitializeAudio();
				HarmonyInstance = new Harmony("com.xenoveni.lethalmic");
				GetLogger().LogInfo((object)"Applying Harmony patches...");
				HarmonyInstance.PatchAll();
				GetLogger().LogInfo((object)"Harmony patches applied successfully");
				VoiceChatPatch.Initialize(Logger);
				InitializeUI();
				IsInitialized = true;
				GetLogger().LogInfo((object)"Successfully initialized with static implementation");
				GetLogger().LogInfo((object)$"Initialization completed at {DateTime.Now}");
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[LethalMic] Error in Awake: {arg}");
				if (Logger != null)
				{
					Logger.LogError((object)$"Error in Awake: {arg}");
				}
			}
		}

		private static void InitializeAudio()
		{
			if (!EnableMod.Value)
			{
				GetLogger().LogInfo((object)"Mod disabled in configuration");
				return;
			}
			try
			{
				GetLogger().LogInfo((object)"Initializing audio system...");
				StaticAudioManager.Initialize(Logger);
				string[] devices = Microphone.devices;
				GetLogger().LogInfo((object)$"Found {devices.Length} microphone devices");
				if (devices.Length == 0)
				{
					GetLogger().LogError((object)"No microphone devices found. Please check your system settings and ensure a microphone is connected.");
					if ((Object)(object)uiInstance != (Object)null)
					{
						uiInstance.UpdateMicStatus("None", "No Devices", 0f);
					}
					return;
				}
				string[] array = devices;
				foreach (string text in array)
				{
					GetLogger().LogInfo((object)("Available device: " + text));
				}
				if (string.IsNullOrEmpty(InputDevice.Value))
				{
					selectedDevice = devices[0];
					GetLogger().LogInfo((object)("No device selected in config, using default device: " + selectedDevice));
				}
				else if (devices.Contains(InputDevice.Value))
				{
					selectedDevice = InputDevice.Value;
					GetLogger().LogInfo((object)("Using configured device: " + selectedDevice));
				}
				else
				{
					GetLogger().LogWarning((object)("Configured device '" + InputDevice.Value + "' not found, falling back to default device: " + devices[0]));
					selectedDevice = devices[0];
				}
				StaticAudioManager.StartRecording();
				GetLogger().LogInfo((object)("Successfully initialized audio system with device: " + selectedDevice));
				if ((Object)(object)uiInstance != (Object)null)
				{
					uiInstance.UpdateMicStatus(selectedDevice, "Connected", 0f);
				}
			}
			catch (Exception ex)
			{
				GetLogger().LogError((object)$"Error initializing audio system: {ex}");
				GetLogger().LogError((object)("Stack trace: " + ex.StackTrace));
				errorCount++;
				if ((Object)(object)uiInstance != (Object)null)
				{
					uiInstance.UpdateMicStatus("Error", "Initialization Failed", 0f);
				}
			}
		}

		private static void InitializeUI()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			try
			{
				GetLogger().LogInfo((object)"Initializing UI...");
				uiObject = new GameObject("LethalMicUI");
				((Object)uiObject).hideFlags = (HideFlags)61;
				Object.DontDestroyOnLoad((Object)(object)uiObject);
				uiInstance = uiObject.AddComponent<LethalMicUI>();
				uiInstance.Initialize(Logger, ConfigFile);
				if ((Object)(object)updaterObject == (Object)null)
				{
					updaterObject = new GameObject("LethalMicUpdater");
					((Object)updaterObject).hideFlags = (HideFlags)61;
					Object.DontDestroyOnLoad((Object)(object)updaterObject);
					updaterObject.AddComponent<LethalMicUpdater>();
				}
				GetLogger().LogInfo((object)"UI initialized successfully");
			}
			catch (Exception arg)
			{
				GetLogger().LogError((object)$"Failed to initialize UI: {arg}");
			}
		}

		public static void ToggleUI()
		{
			try
			{
				ManualLogSource logger = GetLogger();
				if (logger != null)
				{
					logger.LogInfo((object)"[UI] ToggleUI called - attempting to toggle UI visibility");
				}
				if ((Object)(object)uiInstance != (Object)null)
				{
					ManualLogSource logger2 = GetLogger();
					if (logger2 != null)
					{
						logger2.LogInfo((object)"[UI] uiInstance is not null, calling ToggleVisibility()");
					}
					uiInstance.ToggleVisibility();
					ManualLogSource logger3 = GetLogger();
					if (logger3 != null)
					{
						logger3.LogInfo((object)$"[UI] UI visibility is now: {uiInstance.IsVisible}");
					}
				}
				else
				{
					ManualLogSource logger4 = GetLogger();
					if (logger4 != null)
					{
						logger4.LogWarning((object)"[UI] ToggleUI called but UI instance is null");
					}
					InitializeUI();
				}
			}
			catch (Exception arg)
			{
				ManualLogSource logger5 = GetLogger();
				if (logger5 != null)
				{
					logger5.LogError((object)$"[UI] Error in ToggleUI: {arg}");
				}
			}
		}

		private static void LogDiagnostic(string message, LogLevel level = 16)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Invalid comparison between Unknown and I4
			//IL_0082: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Logger == null)
				{
					Debug.Log((object)("[LethalMic] " + message));
				}
				else
				{
					if ((int)level == 32 && DebugLogging != null && !DebugLogging.Value)
					{
						return;
					}
					DateTime now = DateTime.Now;
					if ((now - LastLogTime).TotalMilliseconds < 100.0)
					{
						LogCounter++;
						if (LogCounter > 100)
						{
							return;
						}
					}
					else
					{
						LogCounter = 0;
						LastLogTime = now;
					}
					Logger.Log(level, (object)$"[{now:HH:mm:ss.fff}] {message}");
				}
			}
			catch (Exception arg)
			{
				Debug.LogError((object)$"[LethalMic] Error in LogDiagnostic: {arg}");
			}
		}

		public static void StartRecording()
		{
			if (!EnableMod.Value)
			{
				GetLogger().LogInfo((object)"StartRecording skipped - mod disabled");
				return;
			}
			try
			{
				GetLogger().LogInfo((object)"Starting microphone recording...");
				StaticAudioManager.StartRecording();
				isRecording = true;
				GetLogger().LogInfo((object)"Microphone recording started successfully");
			}
			catch (Exception ex)
			{
				GetLogger().LogError((object)$"Failed to start recording: {ex}");
				GetLogger().LogError((object)("Stack trace: " + ex.StackTrace));
				errorCount++;
			}
		}

		public static void StopRecording()
		{
			if (!isRecording)
			{
				GetLogger().LogInfo((object)"StopRecording skipped - not recording");
				return;
			}
			try
			{
				GetLogger().LogInfo((object)"Stopping microphone recording...");
				StaticAudioManager.StopRecording();
				isRecording = false;
				GetLogger().LogInfo((object)"Microphone recording stopped");
			}
			catch (Exception ex)
			{
				GetLogger().LogError((object)$"Failed to stop recording: {ex}");
				GetLogger().LogError((object)("Stack trace: " + ex.StackTrace));
				errorCount++;
			}
		}

		public static void UpdateAudio()
		{
			if (!isRecording)
			{
				return;
			}
			try
			{
				StaticAudioManager.ProcessAudio();
				currentMicrophoneLevel = StaticAudioManager.GetCurrentMicrophoneLevel();
				peakMicLevel = StaticAudioManager.GetPeakMicrophoneLevel();
				voiceDetected = StaticAudioManager.IsVoiceDetected();
				noiseFloor = StaticAudioManager.GetNoiseFloor();
				cpuUsage = StaticAudioManager.GetCPUUsage();
				audioFrameCount++;
			}
			catch (Exception arg)
			{
				GetLogger().LogError((object)$"Error in UpdateAudio: {arg}");
				errorCount++;
			}
		}

		public static float GetCurrentMicrophoneLevel()
		{
			return currentMicrophoneLevel;
		}

		public static bool IsVoiceDetected()
		{
			return voiceDetected;
		}

		public static float GetNoiseFloor()
		{
			return noiseFloor;
		}

		public static float GetCPUUsage()
		{
			return cpuUsage;
		}

		public static int GetErrorCount()
		{
			return errorCount;
		}

		public static int GetAudioFrameCount()
		{
			return audioFrameCount;
		}

		public static bool IsUIVisible()
		{
			if ((Object)(object)uiInstance != (Object)null)
			{
				return uiInstance.IsVisible;
			}
			return false;
		}

		public static void Cleanup()
		{
			try
			{
				GetLogger().LogInfo((object)"Cleaning up LethalMicStatic...");
				StaticAudioManager.Cleanup();
				if ((Object)(object)uiInstance != (Object)null)
				{
					Object.Destroy((Object)(object)((Component)uiInstance).gameObject);
					uiInstance = null;
				}
				GetLogger().LogInfo((object)"LethalMicStatic cleaned up");
			}
			catch (Exception arg)
			{
				GetLogger().LogError((object)$"Error during cleanup: {arg}");
			}
		}

		private static ManualLogSource GetLogger()
		{
			return Logger;
		}

		public static float GetMicrophoneGain()
		{
			return MicrophoneGain?.Value ?? 1f;
		}

		public static void SetMicrophoneGain(float value)
		{
			if (MicrophoneGain != null)
			{
				MicrophoneGain.Value = value;
				StaticAudioManager.UpdateProcessorSettings();
			}
		}

		public static float GetNoiseGateThreshold()
		{
			return NoiseGateThreshold?.Value ?? 0.05f;
		}

		public static void SetNoiseGateThreshold(float value)
		{
			if (NoiseGateThreshold != null)
			{
				NoiseGateThreshold.Value = value;
				StaticAudioManager.UpdateProcessorSettings();
			}
		}

		public static bool GetNoiseGateEnabled()
		{
			return NoiseGate?.Value ?? true;
		}

		public static void SetNoiseGateEnabled(bool value)
		{
			if (NoiseGate != null)
			{
				NoiseGate.Value = value;
				StaticAudioManager.UpdateProcessorSettings();
			}
		}

		public static void SetInputDevice(string deviceName)
		{
			if (InputDevice != null)
			{
				InputDevice.Value = deviceName;
			}
			selectedDevice = deviceName;
			StaticAudioManager.SetInputDevice(deviceName);
		}

		public static string GetInputDevice()
		{
			return selectedDevice;
		}

		public static bool GetCompressionEnabled()
		{
			return Compression?.Value ?? true;
		}

		public static void SetCompressionEnabled(bool value)
		{
			if (Compression != null)
			{
				Compression.Value = value;
				StaticAudioManager.UpdateProcessorSettings();
			}
		}

		public static float GetCompressionRatio()
		{
			return CompressionRatio?.Value ?? 10f;
		}

		public static void SetCompressionRatio(float value)
		{
			if (CompressionRatio != null)
			{
				CompressionRatio.Value = value;
				StaticAudioManager.UpdateProcessorSettings();
			}
		}

		public static float GetAttackTime()
		{
			return AttackTime?.Value ?? 2f;
		}

		public static void SetAttackTime(float value)
		{
			if (AttackTime != null)
			{
				AttackTime.Value = value;
				StaticAudioManager.UpdateProcessorSettings();
			}
		}

		public static float GetReleaseTime()
		{
			return ReleaseTime?.Value ?? 50f;
		}

		public static void SetReleaseTime(float value)
		{
			if (ReleaseTime != null)
			{
				ReleaseTime.Value = value;
				StaticAudioManager.UpdateProcessorSettings();
			}
		}

		public static LethalMicUI GetUIIInstance()
		{
			return uiInstance;
		}

		private static void UpdateProcessorSettings()
		{
			switch (EchoMode.Value)
			{
			case EchoSuppressionMode.Headphones:
				StaticAudioManager.SetAggressiveSuppression();
				break;
			case EchoSuppressionMode.StereoMix:
				StaticAudioManager.SetStereoMixSuppression();
				break;
			case EchoSuppressionMode.WasapiLoopback:
				StaticAudioManager.SetWasapiSuppression();
				break;
			}
		}

		public static EchoSuppressionMode GetEchoSuppressionMode()
		{
			return EchoMode?.Value ?? EchoSuppressionMode.Headphones;
		}

		public static void SetEchoSuppressionMode(EchoSuppressionMode mode)
		{
			if (EchoMode != null)
			{
				EchoMode.Value = mode;
				UpdateProcessorSettings();
			}
		}
	}
	public class LethalMicUpdater : MonoBehaviour
	{
		private void Update()
		{
			LethalMicStatic.UpdateAudio();
		}
	}
	public class LethalMicInputActions : LcInputActions
	{
		public static readonly LethalMicInputActions Instance = new LethalMicInputActions();

		public InputAction ToggleUI => ((LcInputActions)this).Asset["toggleui"];

		public InputAction QuickMute => ((LcInputActions)this).Asset["quickmute"];

		public InputAction PushToTalk => ((LcInputActions)this).Asset["pushtotalk"];

		public LethalMicInputActions()
		{
			Logger.CreateLogSource("LethalMicInputActions").LogInfo((object)"[INPUT] LethalMicInputActions instance created");
		}

		public override void CreateInputActions(in InputActionMapBuilder builder)
		{
			builder.NewActionBinding().WithActionId("toggleui").WithActionType((InputActionType)1)
				.WithKeyboardControl((KeyboardControl)33)
				.WithBindingName("Toggle UI")
				.Finish();
			Logger.CreateLogSource("LethalMicInputActions").LogInfo((object)"[INPUT] ToggleUI action bound to M key");
			builder.NewActionBinding().WithActionId("quickmute").WithActionType((InputActionType)1)
				.WithKeyboardControl((KeyboardControl)106)
				.WithBindingName("Quick Mute")
				.Finish();
			builder.NewActionBinding().WithActionId("pushtotalk").WithActionType((InputActionType)1)
				.WithKeyboardControl((KeyboardControl)107)
				.WithBindingName("Push to Talk")
				.Finish();
		}
	}
	public class PerformanceOptimizer : IDisposable
	{
		private readonly int _sampleRate;

		private readonly int _channels;

		private bool _disposed;

		private readonly Stopwatch _performanceTimer = new Stopwatch();

		private readonly Queue<float> _cpuUsageHistory = new Queue<float>();

		private const int MAX_HISTORY_SIZE = 100;

		private bool _rnnoiseDllAvailable;

		private bool _opusLibraryOptimized;

		private readonly Dictionary<int, byte[]> _opusBufferPool = new Dictionary<int, byte[]>();

		private readonly object _bufferPoolLock = new object();

		public bool IsRNNoiseAvailable => _rnnoiseDllAvailable;

		public bool IsOpusOptimized => _opusLibraryOptimized;

		public PerformanceOptimizer(int sampleRate, int channels)
		{
			_sampleRate = sampleRate;
			_channels = channels;
			InitializeExternalLibraries();
			OptimizeCodecSettings();
		}

		private void InitializeExternalLibraries()
		{
			try
			{
				IntPtr intPtr = rnnoise_create(IntPtr.Zero);
				if (intPtr != IntPtr.Zero)
				{
					rnnoise_destroy(intPtr);
					_rnnoiseDllAvailable = true;
					Debug.Log((object)"RNNoise library successfully loaded");
				}
			}
			catch (DllNotFoundException)
			{
				Debug.LogWarning((object)"RNNoise library not found. Noise suppression will use fallback algorithms.");
				_rnnoiseDllAvailable = false;
			}
			catch (Exception ex2)
			{
				Debug.LogWarning((object)("RNNoise initialization failed: " + ex2.Message));
				_rnnoiseDllAvailable = false;
			}
		}

		private void OptimizeCodecSettings()
		{
			try
			{
				int[] array = new int[5] { 120, 240, 480, 960, 1920 };
				lock (_bufferPoolLock)
				{
					int[] array2 = array;
					foreach (int num in array2)
					{
						int num2 = num * _channels * 4;
						_opusBufferPool[num] = new byte[num2];
					}
				}
				_opusLibraryOptimized = true;
				Debug.Log((object)"Opus codec buffers optimized");
			}
			catch (Exception ex)
			{
				Debug.LogWarning((object)("Opus optimization failed: " + ex.Message));
				_opusLibraryOptimized = false;
			}
		}

		public void StartPerformanceMonitoring()
		{
			_performanceTimer.Start();
		}

		public float StopPerformanceMonitoring()
		{
			_performanceTimer.Stop();
			float num = (float)_performanceTimer.Elapsed.TotalMilliseconds;
			_cpuUsageHistory.Enqueue(num);
			if (_cpuUsageHistory.Count > 100)
			{
				_cpuUsageHistory.Dequeue();
			}
			_performanceTimer.Reset();
			return num;
		}

		public float GetAverageCpuUsage()
		{
			if (_cpuUsageHistory.Count == 0)
			{
				return 0f;
			}
			float num = 0f;
			foreach (float item in _cpuUsageHistory)
			{
				num += item;
			}
			return num / (float)_cpuUsageHistory.Count;
		}

		public byte[] GetOptimizedOpusBuffer(int frameSize)
		{
			lock (_bufferPoolLock)
			{
				if (_opusBufferPool.TryGetValue(frameSize, out var value))
				{
					return value;
				}
				byte[] array = new byte[frameSize * _channels * 4];
				_opusBufferPool[frameSize] = array;
				return array;
			}
		}

		public PerformanceRecommendation GetPerformanceRecommendation()
		{
			float averageCpuUsage = GetAverageCpuUsage();
			if (averageCpuUsage > 10f)
			{
				return new PerformanceRecommendation
				{
					RecommendedFFTSize = 512,
					RecommendedQuality = 1,
					DisableAIProcessing = true,
					Message = "High CPU usage detected. Consider reducing processing quality."
				};
			}
			if (averageCpuUsage > 5f)
			{
				return new PerformanceRecommendation
				{
					RecommendedFFTSize = 1024,
					RecommendedQuality = 2,
					DisableAIProcessing = false,
					Message = "Moderate CPU usage. Current settings are acceptable."
				};
			}
			return new PerformanceRecommendation
			{
				RecommendedFFTSize = 2048,
				RecommendedQuality = 3,
				DisableAIProcessing = false,
				Message = "Low CPU usage. You can increase quality settings."
			};
		}

		public void OptimizeForLowEndSystem()
		{
			Debug.Log((object)"Applying low-end system optimizations");
			lock (_bufferPoolLock)
			{
				_opusBufferPool.Clear();
				_opusBufferPool[480] = new byte[480 * _channels * 2];
			}
			GC.Collect();