Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of VoiceCompression v1.0.2
plugins/VoiceCompression/VoiceCompression.dll
Decompiled a day agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Threading; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; using VoiceCompression.Audio; using VoiceCompression.Dsp; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("RED")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyDescription("Incoming voice compression with native R.E.P.O. audio settings.")] [assembly: AssemblyFileVersion("1.0.2.0")] [assembly: AssemblyInformationalVersion("1.0.2")] [assembly: AssemblyProduct("VoiceCompression")] [assembly: AssemblyTitle("VoiceCompression")] [assembly: AssemblyVersion("1.0.2.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [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 VoiceCompression { public sealed class CompressorConfiguration { private static CompressorSettings _current = new CompressorSettings(enabled: false); public static CompressorConfiguration Instance { get; private set; } public static CompressorSettings Current => Volatile.Read(in _current); public ConfigEntry<bool> Enabled { get; } public ConfigEntry<float> ThresholdDb { get; } public ConfigEntry<float> Ratio { get; } public ConfigEntry<float> AttackMs { get; } public ConfigEntry<float> ReleaseMs { get; } public ConfigEntry<float> MakeupGainDb { get; } public ConfigEntry<float> KneeDb { get; } public ConfigEntry<float> OutputCeilingDb { get; } public CompressorConfiguration(ConfigFile config) { Enabled = config.Bind<bool>("Incoming Voice", "Enabled", true, "Compress other players' incoming voices. Your microphone, music, and sound effects are unaffected."); ThresholdDb = Bind(config, "ThresholdDb", -18f, -60f, 0f, "Level where compression starts, in dBFS. Lower values compress more speech."); Ratio = Bind(config, "Ratio", 4f, 1f, 20f, "Compression ratio above the threshold. 4 means a 4 dB rise becomes 1 dB; 1 disables gain reduction."); AttackMs = Bind(config, "AttackMs", 5f, 0.1f, 100f, "How quickly the compressor responds to loud speech, in milliseconds."); ReleaseMs = Bind(config, "ReleaseMs", 150f, 10f, 1000f, "How quickly gain recovers after loud speech, in milliseconds."); MakeupGainDb = Bind(config, "MakeupGainDb", 3f, 0f, 24f, "Gain applied after compression, in dB. Also raises quiet speech and background noise."); KneeDb = Bind(config, "KneeDb", 6f, 0f, 24f, "Width of the soft transition around the threshold, in dB. 0 selects a hard knee."); OutputCeilingDb = Bind(config, "OutputCeilingDb", -1f, -12f, 0f, "Peak ceiling in dBFS at each voice filter output, before game volume and mixer processing. This is not a master-output limiter."); Enabled.SettingChanged += OnSettingChanged; ThresholdDb.SettingChanged += OnSettingChanged; Ratio.SettingChanged += OnSettingChanged; AttackMs.SettingChanged += OnSettingChanged; ReleaseMs.SettingChanged += OnSettingChanged; MakeupGainDb.SettingChanged += OnSettingChanged; KneeDb.SettingChanged += OnSettingChanged; OutputCeilingDb.SettingChanged += OnSettingChanged; Instance = this; Publish(); } private static ConfigEntry<float> Bind(ConfigFile config, string key, float value, float min, float max, string description) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown return config.Bind<float>("Incoming Voice", key, value, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(min, max), Array.Empty<object>())); } private void OnSettingChanged(object sender, EventArgs args) { Publish(); } private void Publish() { Volatile.Write(ref _current, new CompressorSettings(Enabled.Value, ThresholdDb.Value, Ratio.Value, AttackMs.Value, ReleaseMs.Value, MakeupGainDb.Value, KneeDb.Value, OutputCeilingDb.Value)); } internal static void Bypass() { Volatile.Write(ref _current, new CompressorSettings(enabled: false)); } } [BepInPlugin("redos.voicecompression", "Voice Compression", "1.0.2")] public sealed class Plugin : BaseUnityPlugin { public const string Guid = "redos.voicecompression"; public const string Name = "Voice Compression"; public const string Version = "1.0.2"; private Harmony _harmony; internal static ManualLogSource Log { get; private set; } private void Awake() { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; new CompressorConfiguration(((BaseUnityPlugin)this).Config); _harmony = new Harmony("redos.voicecompression"); _harmony.PatchAll(typeof(Plugin).Assembly); IncomingVoiceCompression.AttachExisting(); ((BaseUnityPlugin)this).Logger.LogInfo((object)"Voice Compression 1.0.2 loaded. Incoming voices only; controls are in Settings > Audio."); } private void OnDestroy() { CompressorConfiguration.Bypass(); IncomingVoiceCompression.RemoveAll(); Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } } namespace VoiceCompression.UI { [HarmonyPatch(typeof(MenuPage), "Start")] internal static class AudioPagePatch { private static void Postfix(MenuPage __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 if ((int)__instance.menuPageIndex == 5 && !Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<NativeAudioSettings>())) { ((Component)__instance).gameObject.AddComponent<NativeAudioSettings>(); } } } [HarmonyPatch(typeof(MenuPageSettingsPage), "ResetSettings")] internal static class AudioDefaultsPatch { private static void Prefix(MenuPageSettingsPage __instance) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) CompressorConfiguration instance = CompressorConfiguration.Instance; if ((int)__instance.settingType == 0 && instance != null) { Reset<bool>(instance.Enabled); Reset<float>(instance.ThresholdDb); Reset<float>(instance.Ratio); Reset<float>(instance.AttackMs); Reset<float>(instance.ReleaseMs); Reset<float>(instance.MakeupGainDb); Reset<float>(instance.KneeDb); Reset<float>(instance.OutputCeilingDb); } } private static void Reset<T>(ConfigEntry<T> entry) { entry.Value = (T)((ConfigEntryBase)entry).DefaultValue; } } internal sealed class NativeAudioSettings : MonoBehaviour { private sealed class SliderBinding { public readonly MenuSlider Slider; public readonly int Segments; public readonly Func<int> Read; public readonly Action<int> Write; public int PreviousIndex = -1; public SliderBinding(MenuSlider slider, int segments, Func<int> read, Action<int> write) { Slider = slider; Segments = segments; Read = read; Write = write; } public void Refresh() { PreviousIndex = Read(); Slider.SetBar((float)PreviousIndex / (float)Segments); Slider.SetBarScaleInstant(); } } private static readonly ManualLogSource Log = Logger.CreateLogSource("VoiceCompression.AudioSettings"); private static readonly FieldInfo CurrentValue = AccessTools.Field(typeof(MenuSlider), "currentValue"); private static readonly FieldInfo SettingsValue = AccessTools.Field(typeof(MenuSlider), "settingsValue"); private static readonly MethodInfo RecalculateScrollHeight = AccessTools.Method(typeof(MenuScrollBox), "RecalculateScrollHeight", (Type[])null, (Type[])null); private const string ControlPrefix = "VoiceCompression - "; private readonly List<SliderBinding> bindings = new List<SliderBinding>(); private readonly List<GameObject> createdObjects = new List<GameObject>(); private readonly Dictionary<GameObject, Vector3> intendedPositions = new Dictionary<GameObject, Vector3>(); private GameObject staging; private MenuPage page; private MenuScrollBox scrollBox; private bool ready; private IEnumerator Start() { yield return null; if (Prepare()) { yield return null; if (Activate()) { yield return null; Finish(); } } } private bool Prepare() { //IL_00ef: Unknown result type (might be due to invalid IL or missing references) //IL_00f9: Expected O, but got Unknown //IL_013d: Unknown result type (might be due to invalid IL or missing references) //IL_0144: Expected O, but got Unknown //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01cd: Unknown result type (might be due to invalid IL or missing references) //IL_01d2: Unknown result type (might be due to invalid IL or missing references) //IL_01d7: Unknown result type (might be due to invalid IL or missing references) //IL_0381: Unknown result type (might be due to invalid IL or missing references) //IL_0388: Expected O, but got Unknown //IL_038c: Unknown result type (might be due to invalid IL or missing references) //IL_0391: Unknown result type (might be due to invalid IL or missing references) //IL_039c: Unknown result type (might be due to invalid IL or missing references) //IL_0168: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Unknown result type (might be due to invalid IL or missing references) //IL_0184: Unknown result type (might be due to invalid IL or missing references) try { page = ((Component)this).GetComponent<MenuPage>(); scrollBox = ((IEnumerable<MenuScrollBox>)((Component)this).GetComponentsInChildren<MenuScrollBox>(true)).FirstOrDefault((Func<MenuScrollBox, bool>)((MenuScrollBox box) => Object.op_Implicit((Object)(object)box.scroller) && (Object)(object)((Component)box).GetComponentInParent<MenuPage>() == (Object)(object)page)); if (!Object.op_Implicit((Object)(object)scrollBox) || !Object.op_Implicit((Object)(object)scrollBox.scroller) || CompressorConfiguration.Instance == null) { throw new InvalidOperationException("The native Audio scroll box or compressor configuration is unavailable."); } if (CurrentValue == null || SettingsValue == null || RecalculateScrollHeight == null) { throw new MissingMemberException("The game's native slider/scroll API has changed."); } MenuSlider val = ((IEnumerable<MenuSlider>)((Component)scrollBox.scroller).GetComponentsInChildren<MenuSlider>(true)).FirstOrDefault((Func<MenuSlider, bool>)((MenuSlider slider) => Object.op_Implicit((Object)(object)((Component)slider).GetComponent<MenuSetting>()) && !Object.op_Implicit((Object)(object)((Component)slider).GetComponent<MenuSliderPlayerMicGain>()) && !Object.op_Implicit((Object)(object)((Component)slider).GetComponent<MenuSliderMicrophone>()) && !Object.op_Implicit((Object)(object)((Component)slider).GetComponent<MenuInputPercentSetting>()) && Object.op_Implicit((Object)(object)slider.elementNameText) && Object.op_Implicit((Object)(object)slider.segmentText) && Object.op_Implicit((Object)(object)slider.segmentMaskText) && Object.op_Implicit((Object)(object)slider.barSize) && Object.op_Implicit((Object)(object)slider.settingsBar) && Object.op_Implicit((Object)(object)slider.extraBar))); if (!Object.op_Implicit((Object)(object)val)) { throw new InvalidOperationException("No compatible native Audio volume slider was found."); } staging = new GameObject("VoiceCompression - Staging", new Type[1] { typeof(RectTransform) }); staging.SetActive(false); staging.transform.SetParent(((Component)this).transform, false); float num = 0f; Rect rect; foreach (Transform item in (Transform)scrollBox.scroller) { Transform val2 = item; RectTransform val3 = (RectTransform)(object)((val2 is RectTransform) ? val2 : null); if (val3 != null && !((Object)val2).name.StartsWith("VoiceCompression - ", StringComparison.Ordinal)) { float num2 = num; float y = ((Transform)val3).localPosition.y; rect = val3.rect; num = Mathf.Min(num2, y - ((Rect)(ref rect)).height * val3.pivot.y); } } float x = ((Component)val).transform.localPosition.x; rect = ((RectTransform)((Component)val).transform).rect; float num3 = Mathf.Max(((Rect)(ref rect)).height + 12f, 42f); float num4 = num - 40f; AddHeading(val, x, num4); num4 -= 44f; CompressorConfiguration instance = CompressorConfiguration.Instance; AddToggle(val, "Voice compression", instance.Enabled, x, num4); num4 -= num3; AddSlider(val, "Threshold", instance.ThresholdDb, -60f, 0f, 1f, "0' dB'", x, num4); num4 -= num3; AddSlider(val, "Ratio", instance.Ratio, 1f, 20f, 0.1f, "0.0':1'", x, num4); num4 -= num3; AddSlider(val, "Attack", instance.AttackMs, 0.1f, 100f, 0.1f, "0.0' ms'", x, num4); num4 -= num3; AddSlider(val, "Release", instance.ReleaseMs, 10f, 1000f, 10f, "0' ms'", x, num4); num4 -= num3; AddSlider(val, "Makeup gain", instance.MakeupGainDb, 0f, 24f, 0.5f, "0.0' dB'", x, num4); num4 -= num3; AddSlider(val, "Soft knee", instance.KneeDb, 0f, 24f, 1f, "0' dB'", x, num4); num4 -= num3; AddSlider(val, "Output ceiling", instance.OutputCeilingDb, -12f, 0f, 0.5f, "0.0' dB'", x, num4); RectTransform val4 = (RectTransform)((Component)val).transform; float num5 = num4; rect = val4.rect; num4 = num5 - (((Rect)(ref rect)).height * val4.pivot.y + 8f); AddGuide(val, x, num4); return true; } catch (Exception exception) { Fail(exception); return false; } } private void AddHeading(MenuSlider template, float x, float y) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00b4: Unknown result type (might be due to invalid IL or missing references) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00be: Unknown result type (might be due to invalid IL or missing references) //IL_00d5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(((Component)template.elementNameText).gameObject, staging.transform, false); ((Object)val).name = "VoiceCompression - Heading"; createdObjects.Add(val); val.SetActive(false); StripLocalization(val); TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).text = "VOICE COMPRESSION\n<size=70%>INCOMING VOICES ONLY</size>"; ((TMP_Text)component).enableAutoSizing = false; ((TMP_Text)component).fontSize = Mathf.Min(((TMP_Text)component).fontSize, 22f); ((TMP_Text)component).alignment = (TextAlignmentOptions)1025; ((TMP_Text)component).isRightToLeftText = false; ((Graphic)component).raycastTarget = false; RectTransform rectTransform = ((TMP_Text)component).rectTransform; Vector2 anchorMin = (rectTransform.anchorMax = Vector2.zero); rectTransform.anchorMin = anchorMin; rectTransform.pivot = Vector2.zero; Rect rect = ((RectTransform)((Component)template).transform).rect; rectTransform.sizeDelta = new Vector2(Mathf.Max(((Rect)(ref rect)).width, 280f), 42f); ((Transform)rectTransform).localPosition = new Vector3(x, y, 0f); ((Transform)rectTransform).localScale = Vector3.one; intendedPositions[val] = ((Transform)rectTransform).localPosition; } private void AddGuide(MenuSlider template, float x, float y) { string[] array = new string[13] { "<b>VOICE COMPRESSION GUIDE</b>\nTurn Voice compression ON and start with the defaults. Changes save automatically.", "<b>Threshold: -18 dB</b>\nCompression starts above this level. More negative means more compression.", "<b>Ratio: 4:1</b>\nHow strongly loud voices are reduced. Higher means stronger compression.", "<b>Attack: 5 ms</b>\nHow quickly it reacts to loud speech. Lower catches sudden peaks faster.", "<b>Release: 150 ms</b>\nHow quickly volume recovers after loud speech.", "<b>Makeup gain: +3 dB</b>\nRaises the compressed voice, helping quiet speech become audible. Also raises background noise.", "<b>Soft knee: 6 dB</b>\nMakes compression engage gradually for smoother sound.", "<b>Output ceiling: -1 dB</b>\nCaps each processed voice's peaks before the game's volume and mixer effects.", "<b>ADJUST BY EAR</b>\n<b>Loud teammates overpower everyone</b>\nLower Threshold to -24 dB, then try Ratio 6:1.", "<b>Quiet voices need more volume</b>\nIncrease Makeup gain gradually to +4 to +6 dB.", "<b>Volume swells between words</b>\nIncrease Release to 250 ms.", "<b>Voices sound too flattened</b>\nReduce Ratio to 2:1 or 3:1.", "Incoming microphone voices only. Your microphone, music, and sound effects stay unchanged." }; for (int i = 0; i < array.Length; i++) { y -= AddGuideBlock(template, x, y, i, array[i]) + 4f; } } private float AddGuideBlock(MenuSlider template, float x, float y, int index, string content) { //IL_00c9: Unknown result type (might be due to invalid IL or missing references) //IL_00dc: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e9: Unknown result type (might be due to invalid IL or missing references) //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_010b: Unknown result type (might be due to invalid IL or missing references) //IL_0110: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Unknown result type (might be due to invalid IL or missing references) //IL_0183: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(((Component)template.elementNameText).gameObject, staging.transform, false); ((Object)val).name = "VoiceCompression - Setup tips " + index; val.SetActive(false); createdObjects.Add(val); StripLocalization(val); TextMeshProUGUI component = val.GetComponent<TextMeshProUGUI>(); ((TMP_Text)component).text = content; ((TMP_Text)component).richText = true; ((TMP_Text)component).fontStyle = (FontStyles)0; ((TMP_Text)component).enableAutoSizing = false; ((TMP_Text)component).fontSize = Mathf.Min(((TMP_Text)template.elementNameText).fontSize, 12f); ((TMP_Text)component).lineSpacing = 0f; ((TMP_Text)component).paragraphSpacing = 0f; ((TMP_Text)component).alignment = (TextAlignmentOptions)257; ((TMP_Text)component).textWrappingMode = (TextWrappingModes)1; ((TMP_Text)component).overflowMode = (TextOverflowModes)0; ((TMP_Text)component).isRightToLeftText = false; ((Graphic)component).raycastTarget = false; ((TMP_Text)component).margin = Vector4.zero; RectTransform rectTransform = ((TMP_Text)component).rectTransform; Vector2 anchorMin = (rectTransform.anchorMax = Vector2.zero); rectTransform.anchorMin = anchorMin; rectTransform.pivot = new Vector2(0f, 1f); Rect rect = ((RectTransform)((Component)template).transform).rect; float num = Mathf.Max(((Rect)(ref rect)).width - 8f, 1f); float num2 = Mathf.Ceil(((TMP_Text)component).GetPreferredValues(((TMP_Text)component).text, num, float.PositiveInfinity).y) + 2f; rectTransform.sizeDelta = new Vector2(num, num2); ((Transform)rectTransform).localPosition = new Vector3(x, y, 0f); ((Transform)rectTransform).localScale = Vector3.one; intendedPositions[val] = ((Transform)rectTransform).localPosition; MenuSelectableElement obj = val.AddComponent<MenuSelectableElement>(); ((Selectable)obj).transition = (Transition)0; ((Selectable)obj).targetGraphic = null; val.AddComponent<MenuElementHover>(); return num2; } private void AddToggle(MenuSlider template, string label, ConfigEntry<bool> entry, float x, float y) { AddControl(template, label, new string[2] { "OFF", "ON" }, () => entry.Value ? 1 : 0, delegate(int index) { entry.Value = index != 0; }, x, y); } private void AddSlider(MenuSlider template, string label, ConfigEntry<float> entry, float defaultMin, float defaultMax, float step, string format, float x, float y) { AcceptableValueRange<float> val = ((ConfigEntryBase)entry).Description.AcceptableValues as AcceptableValueRange<float>; float min = val?.MinValue ?? defaultMin; float num = val?.MaxValue ?? defaultMax; int segments = Mathf.Max(1, Mathf.RoundToInt((num - min) / step)); float actualStep = (num - min) / (float)segments; string[] array = new string[segments + 1]; for (int i = 0; i <= segments; i++) { array[i] = (min + actualStep * (float)i).ToString(format, CultureInfo.InvariantCulture); } AddControl(template, label, array, () => Mathf.Clamp(Mathf.RoundToInt((entry.Value - min) / actualStep), 0, segments), delegate(int index) { entry.Value = min + actualStep * (float)index; }, x, y); } private void AddControl(MenuSlider template, string label, string[] options, Func<int> read, Action<int> write, float x, float y) { //IL_0147: Unknown result type (might be due to invalid IL or missing references) //IL_014c: Unknown result type (might be due to invalid IL or missing references) //IL_0154: Unknown result type (might be due to invalid IL or missing references) //IL_015b: Unknown result type (might be due to invalid IL or missing references) //IL_0162: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0174: Expected O, but got Unknown //IL_0179: Expected O, but got Unknown //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_0192: Expected O, but got Unknown //IL_01f4: Unknown result type (might be due to invalid IL or missing references) //IL_01fe: Expected O, but got Unknown //IL_020d: Unknown result type (might be due to invalid IL or missing references) //IL_0217: Expected O, but got Unknown //IL_021a: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Expected O, but got Unknown //IL_0232: Unknown result type (might be due to invalid IL or missing references) //IL_023c: Expected O, but got Unknown //IL_0242: Unknown result type (might be due to invalid IL or missing references) //IL_0248: Expected O, but got Unknown //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_025d: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0280: Unknown result type (might be due to invalid IL or missing references) GameObject val = Object.Instantiate<GameObject>(((Component)template).gameObject, staging.transform, false); ((Object)val).name = "VoiceCompression - " + label; val.SetActive(false); createdObjects.Add(val); StripLocalization(val); MonoBehaviour[] componentsInChildren = val.GetComponentsInChildren<MonoBehaviour>(true); foreach (MonoBehaviour val2 in componentsInChildren) { if (val2 is MenuSetting || val2 is MenuInputPercentSetting || val2 is MenuSliderPlayerMicGain) { ((Behaviour)val2).enabled = false; Object.Destroy((Object)(object)val2); } } MenuSlider component = val.GetComponent<MenuSlider>(); component.elementName = label; ((TMP_Text)component.elementNameText).text = label; ((TMP_Text)component.elementNameText).isRightToLeftText = false; ((TMP_Text)component.elementNameText).enableAutoSizing = true; ((TMP_Text)component.elementNameText).fontSizeMin = 12f; component.hasCustomOptions = true; component.hasCustomValues = false; component.hasBar = true; component.wrapAround = false; component.startValue = 0; component.endValue = options.Length - 1; component.buttonSegmentJump = (component.pointerSegmentJump = 1); component.stringAtStartOfValue = (component.stringAtEndOfValue = string.Empty); component.customOptions = new List<CustomOption>(options.Length); foreach (string customOptionText in options) { component.customOptions.Add(new CustomOption { customOptionText = customOptionText, localizedCustomOptionText = null, rightToLeft = false, maxTextLength = 0, onOption = new UnityEvent() }); } component.onChange = new UnityEvent(); SettingsValue.SetValue(component, (float)read() / 100f); Button[] array = (from button in val.GetComponentsInChildren<Button>(true) orderby ((Component)button).transform.localPosition.x select button).ToArray(); if (array.Length != 2) { throw new InvalidOperationException("The native Audio slider arrow layout has changed."); } array[0].onClick = new ButtonClickedEvent(); ((UnityEvent)array[0].onClick).AddListener(new UnityAction(component.OnDecrease)); array[1].onClick = new ButtonClickedEvent(); ((UnityEvent)array[1].onClick).AddListener(new UnityAction(component.OnIncrease)); RectTransform val3 = (RectTransform)val.transform; ((Transform)val3).localPosition = new Vector3(x, y, ((Component)template).transform.localPosition.z); ((Transform)val3).localScale = ((Component)template).transform.localScale; intendedPositions[val] = ((Transform)val3).localPosition; bindings.Add(new SliderBinding(component, options.Length - 1, read, write)); } private static void StripLocalization(GameObject root) { LocalizationChangedEvent[] componentsInChildren = root.GetComponentsInChildren<LocalizationChangedEvent>(true); foreach (LocalizationChangedEvent obj in componentsInChildren) { ((Behaviour)obj).enabled = false; obj.onLocalizationChanged = new UnityEvent<string>(); obj.onTextDirectionalityChanged = new UnityEvent<bool>(); Object.Destroy((Object)(object)obj); } } private bool Activate() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) try { foreach (GameObject createdObject in createdObjects) { createdObject.transform.SetParent((Transform)(object)scrollBox.scroller, false); createdObject.transform.localPosition = intendedPositions[createdObject]; createdObject.SetActive(true); } Object.Destroy((Object)(object)staging); return true; } catch (Exception exception) { Fail(exception); return false; } } private void Finish() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown try { foreach (SliderBinding binding in bindings) { binding.Refresh(); binding.Slider.onChange.AddListener((UnityAction)delegate { if (ready) { int num = (int)CurrentValue.GetValue(binding.Slider); binding.Write(num); binding.PreviousIndex = num; } }); } RecalculateScrollHeight.Invoke(scrollBox, new object[1] { true }); page.WireVerticalNavigation(2f); ready = true; Log.LogDebug((object)"Added voice compression controls to the native Audio settings page."); } catch (Exception exception) { Fail(exception); } } private void Update() { if (!ready) { return; } foreach (SliderBinding binding in bindings) { if (Object.op_Implicit((Object)(object)binding.Slider) && binding.Read() != binding.PreviousIndex) { binding.Refresh(); } } } private void Fail(Exception exception) { ready = false; foreach (GameObject createdObject in createdObjects) { if (Object.op_Implicit((Object)(object)createdObject)) { createdObject.SetActive(false); Object.Destroy((Object)(object)createdObject); } } if (Object.op_Implicit((Object)(object)staging)) { Object.Destroy((Object)(object)staging); } ((Behaviour)this).enabled = false; Log.LogError((object)$"Could not add voice compression to the native Audio settings page: {exception}"); } } } namespace VoiceCompression.Dsp { public sealed class CompressorSettings { public static readonly CompressorSettings Default = new CompressorSettings(); public bool Enabled { get; } public float ThresholdDb { get; } public float Ratio { get; } public float AttackMs { get; } public float ReleaseMs { get; } public float MakeupGainDb { get; } public float KneeDb { get; } public float OutputCeilingDb { get; } public CompressorSettings(bool enabled = true, float thresholdDb = -18f, float ratio = 4f, float attackMs = 5f, float releaseMs = 150f, float makeupGainDb = 3f, float kneeDb = 6f, float outputCeilingDb = -1f) { Enabled = enabled; ThresholdDb = Sanitize(thresholdDb, -60f, 0f, -18f); Ratio = Sanitize(ratio, 1f, 20f, 4f); AttackMs = Sanitize(attackMs, 0.1f, 100f, 5f); ReleaseMs = Sanitize(releaseMs, 10f, 1000f, 150f); MakeupGainDb = Sanitize(makeupGainDb, 0f, 24f, 3f); KneeDb = Sanitize(kneeDb, 0f, 24f, 6f); OutputCeilingDb = Sanitize(outputCeilingDb, -12f, 0f, -1f); } private static float Sanitize(float value, float min, float max, float fallback) { if (float.IsNaN(value) || float.IsInfinity(value)) { return fallback; } return Math.Max(min, Math.Min(max, value)); } } public sealed class VoiceCompressor { private double _envelope; private double _attackCoefficient; private double _releaseCoefficient; private double _ceiling; private double _slope; private double _makeupGain; private double _kneeStart; private int _sampleRate; private int _channels; private CompressorSettings? _settings; public void Process(float[] data, int channels, int sampleRate, CompressorSettings settings) { if (data == null) { throw new ArgumentNullException("data"); } if (settings == null) { throw new ArgumentNullException("settings"); } if (channels <= 0) { throw new ArgumentOutOfRangeException("channels"); } if (sampleRate <= 0) { throw new ArgumentOutOfRangeException("sampleRate"); } if (data.Length % channels != 0) { throw new ArgumentException("The buffer must contain complete interleaved frames.", "data"); } if (!settings.Enabled) { Reset(); return; } if (_channels != channels || _sampleRate != sampleRate) { _envelope = 0.0; _channels = channels; } if (_sampleRate != sampleRate || _settings != settings) { _sampleRate = sampleRate; _settings = settings; _attackCoefficient = Math.Exp(-1.0 / ((double)((float)sampleRate * settings.AttackMs) * 0.001)); _releaseCoefficient = Math.Exp(-1.0 / ((double)((float)sampleRate * settings.ReleaseMs) * 0.001)); _ceiling = Math.Pow(10.0, (double)settings.OutputCeilingDb / 20.0); _slope = 1.0 / (double)settings.Ratio - 1.0; _makeupGain = Math.Pow(10.0, (double)settings.MakeupGainDb / 20.0); _kneeStart = Math.Pow(10.0, ((double)settings.ThresholdDb - (double)settings.KneeDb * 0.5) / 20.0); } for (int i = 0; i < data.Length; i += channels) { double num = 0.0; for (int j = 0; j < channels; j++) { int num2 = i + j; float num3 = data[num2]; if (float.IsNaN(num3) || float.IsInfinity(num3)) { data[num2] = 0f; } else { num = Math.Max(num, Math.Abs((double)num3)); } } double num4 = ((num > _envelope) ? _attackCoefficient : _releaseCoefficient); _envelope = num + num4 * (_envelope - num); if (_envelope < 1E-12) { _envelope = 0.0; } double num5 = 0.0; if (_envelope > _kneeStart && _slope != 0.0) { double num6 = 20.0 * Math.Log10(_envelope) - (double)settings.ThresholdDb; double num7 = (double)settings.KneeDb * 0.5; if (settings.KneeDb > 0f && num6 > 0.0 - num7 && num6 < num7) { double num8 = num6 + num7; num5 = _slope * num8 * num8 / (2.0 * (double)settings.KneeDb); } else if (num6 >= num7) { num5 = _slope * num6; } } double num9 = ((num5 == 0.0) ? _makeupGain : (Math.Pow(10.0, num5 / 20.0) * _makeupGain)); if (num > 0.0) { num9 = Math.Min(num9, _ceiling / num); } for (int k = 0; k < channels; k++) { int num10 = i + k; data[num10] = (float)((double)data[num10] * num9); } } } public void Reset() { _envelope = 0.0; _sampleRate = 0; _channels = 0; _settings = null; } } } namespace VoiceCompression.Audio { internal static class IncomingVoiceCompression { private static readonly HashSet<IncomingVoiceFilter> Filters = new HashSet<IncomingVoiceFilter>(); internal static void AttachExisting() { PlayerVoiceChat[] array = Object.FindObjectsByType<PlayerVoiceChat>((FindObjectsInactive)1, (FindObjectsSortMode)0); foreach (PlayerVoiceChat val in array) { if (((Behaviour)val).isActiveAndEnabled) { Attach(val); } } } internal static void Attach(PlayerVoiceChat voice) { PhotonView component = ((Component)voice).GetComponent<PhotonView>(); AudioSource component2 = ((Component)voice).GetComponent<AudioSource>(); if (!((Object)(object)component == (Object)null) && !component.IsMine && !((Object)(object)component2 == (Object)null)) { IncomingVoiceFilter incomingVoiceFilter = ((Component)component2).GetComponent<IncomingVoiceFilter>(); if ((Object)(object)incomingVoiceFilter == (Object)null) { incomingVoiceFilter = ((Component)component2).gameObject.AddComponent<IncomingVoiceFilter>(); } incomingVoiceFilter.Initialize(component2, component); Filters.Add(incomingVoiceFilter); } } internal static void Unregister(IncomingVoiceFilter filter) { Filters.Remove(filter); } internal static void RemoveAll() { foreach (IncomingVoiceFilter filter in Filters) { if ((Object)(object)filter != (Object)null) { ((Behaviour)filter).enabled = false; Object.Destroy((Object)(object)filter); } } Filters.Clear(); } } [HarmonyPatch(typeof(PlayerVoiceChat), "Start")] internal static class IncomingVoiceStartPatch { [HarmonyPostfix] private static void Postfix(PlayerVoiceChat __instance) { IncomingVoiceCompression.Attach(__instance); } } [DisallowMultipleComponent] public sealed class IncomingVoiceFilter : MonoBehaviour { private readonly VoiceCompressor compressor = new VoiceCompressor(); private AudioSource source; private PhotonView owner; private AudioClip previousClip; private int outputSampleRate; private int resetVersion; private int processedResetVersion; private int processingAllowed; internal void Initialize(AudioSource audioSource, PhotonView photonView) { source = audioSource; owner = photonView; previousClip = source.clip; UpdateOutputSampleRate(); Interlocked.Increment(ref resetVersion); Volatile.Write(ref processingAllowed, 1); } private void OnEnable() { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Expected O, but got Unknown UpdateOutputSampleRate(); AudioSettings.OnAudioConfigurationChanged += new AudioConfigurationChangeHandler(OnAudioConfigurationChanged); Interlocked.Increment(ref resetVersion); if ((Object)(object)source != (Object)null && (Object)(object)owner != (Object)null && !owner.IsMine) { Volatile.Write(ref processingAllowed, 1); } } private void OnDisable() { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Expected O, but got Unknown Volatile.Write(ref processingAllowed, 0); AudioSettings.OnAudioConfigurationChanged -= new AudioConfigurationChangeHandler(OnAudioConfigurationChanged); Interlocked.Increment(ref resetVersion); } private void OnDestroy() { IncomingVoiceCompression.Unregister(this); } private void Update() { if ((Object)(object)source == (Object)null || (Object)(object)owner == (Object)null || owner.IsMine) { Volatile.Write(ref processingAllowed, 0); ((Behaviour)this).enabled = false; return; } AudioClip clip = source.clip; if ((Object)(object)clip != (Object)(object)previousClip) { previousClip = clip; Interlocked.Increment(ref resetVersion); } } private void OnAudioConfigurationChanged(bool deviceWasChanged) { UpdateOutputSampleRate(); Interlocked.Increment(ref resetVersion); } private void UpdateOutputSampleRate() { Volatile.Write(ref outputSampleRate, AudioSettings.outputSampleRate); } private void OnAudioFilterRead(float[] data, int channels) { int num = Volatile.Read(in resetVersion); if (num != processedResetVersion) { compressor.Reset(); processedResetVersion = num; } if (Volatile.Read(in processingAllowed) != 0) { int num2 = Volatile.Read(in outputSampleRate); if (num2 > 0 && data != null && channels > 0 && data.Length % channels == 0) { compressor.Process(data, channels, num2, CompressorConfiguration.Current); } } } } }