using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using Concentus; using Concentus.Enums; using Microsoft.CodeAnalysis; using NAudio.Wave; using UnityEngine; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = "")] [assembly: AssemblyCompany("ProximityVoice")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("ProximityVoice")] [assembly: AssemblyTitle("ProximityVoice")] [assembly: AssemblyVersion("1.0.0.0")] [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 ProximityVoice { public interface IMicCapture { bool Running { get; } void Start(); void Stop(); void PollFrames(List frames); } public static class MicCapture { public static IMicCapture Create() { try { if (NAudioMicCapture.IsAvailable()) { Plugin.Log.LogInfo((object)"Mic capture backend: NAudio (WaveIn)."); return new NAudioMicCapture(); } Plugin.Log.LogWarning((object)"NAudio reports no capture devices; using Unity Microphone."); } catch (Exception ex) { Plugin.Log.LogWarning((object)("NAudio capture unavailable (" + ex.GetType().Name + "); using Unity Microphone.")); } return new UnityMicCapture(); } } public sealed class NAudioMicCapture : IMicCapture { private const int BytesPerSample = 2; private static readonly int BytesPerFrame = 1920; private const int MaxQueuedFrames = 50; private WaveInEvent _wave; private readonly object _lock = new object(); private readonly Queue _frames = new Queue(); private byte[] _accum = new byte[BytesPerFrame * 4]; private int _accumLen; public bool Running { get; private set; } public static bool IsAvailable() { try { return WaveInEvent.DeviceCount > 0; } catch { return false; } } public void Start() { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0044: Expected O, but got Unknown if (!Running) { _wave = new WaveInEvent { DeviceNumber = ResolveDevice(VoiceConfig.MicDevice.Value), WaveFormat = new WaveFormat(48000, 16, 1), BufferMilliseconds = 40 }; _wave.DataAvailable += OnData; _accumLen = 0; lock (_lock) { _frames.Clear(); } _wave.StartRecording(); Running = true; } } public void Stop() { if (!Running) { return; } Running = false; try { _wave.StopRecording(); } catch { } if (_wave != null) { _wave.DataAvailable -= OnData; _wave.Dispose(); _wave = null; } lock (_lock) { _frames.Clear(); } } public void PollFrames(List frames) { lock (_lock) { while (_frames.Count > 0) { frames.Add(_frames.Dequeue()); } } } private void OnData(object sender, WaveInEventArgs e) { EnsureCapacity(_accumLen + e.BytesRecorded); Buffer.BlockCopy(e.Buffer, 0, _accum, _accumLen, e.BytesRecorded); _accumLen += e.BytesRecorded; int num = 0; while (_accumLen - num >= BytesPerFrame) { short[] array = new short[960]; for (int i = 0; i < 960; i++) { int num2 = num + i * 2; array[i] = (short)(_accum[num2] | (_accum[num2 + 1] << 8)); } num += BytesPerFrame; lock (_lock) { if (_frames.Count >= 50) { _frames.Dequeue(); } _frames.Enqueue(array); } } int num3 = _accumLen - num; if (num3 > 0 && num > 0) { Buffer.BlockCopy(_accum, num, _accum, 0, num3); } _accumLen = num3; } private void EnsureCapacity(int needed) { if (_accum.Length < needed) { byte[] array = new byte[Math.Max(needed, _accum.Length * 2)]; Buffer.BlockCopy(_accum, 0, array, 0, _accumLen); _accum = array; } } private static int ResolveDevice(string name) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0014: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(name)) { return -1; } for (int i = 0; i < WaveInEvent.DeviceCount; i++) { WaveInCapabilities capabilities = WaveInEvent.GetCapabilities(i); if (((WaveInCapabilities)(ref capabilities)).ProductName == name) { return i; } } Plugin.Log.LogWarning((object)("Mic device \"" + name + "\" not found; using system default.")); return -1; } } public sealed class SpeakerVoice { private const int RingSeconds = 2; private readonly int _ringSize = 96000; private readonly GameObject _go; private readonly AudioSource _source; private readonly VoiceDecoder _decoder = new VoiceDecoder(); private readonly float[] _ring; private readonly object _lock = new object(); private int _writeIdx; private int _readIdx; private int _count; public float LastActiveTime; public SpeakerVoice(long speakerId) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0054: Expected O, but got Unknown //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_011f: Expected O, but got Unknown _ring = new float[_ringSize]; _go = new GameObject("PV_Speaker_" + speakerId); Object.DontDestroyOnLoad((Object)(object)_go); _source = _go.AddComponent(); _source.spatialBlend = 1f; _source.rolloffMode = (AudioRolloffMode)1; _source.minDistance = VoiceConfig.FullVolumeRange.Value; _source.maxDistance = VoiceConfig.HearingRange.Value; _source.bypassReverbZones = true; _source.bypassListenerEffects = true; _source.dopplerLevel = 0f; _source.loop = true; _source.clip = AudioClip.Create("PV_Stream_" + speakerId, _ringSize, 1, 48000, true, new PCMReaderCallback(OnPcmRead)); _source.Play(); } public void SetPosition(Vector3 pos) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) _go.transform.position = pos; } public void PushPacket(byte[] packet) { short[] pcmOut; int num = _decoder.Decode(packet, out pcmOut); if (num <= 0) { return; } float value = VoiceConfig.Volume.Value; lock (_lock) { for (int i = 0; i < num; i++) { if (_count >= _ringSize) { break; } _ring[_writeIdx] = (float)pcmOut[i] / 32767f * value; _writeIdx = (_writeIdx + 1) % _ringSize; _count++; } } LastActiveTime = Time.realtimeSinceStartup; } private void OnPcmRead(float[] data) { lock (_lock) { for (int i = 0; i < data.Length; i++) { if (_count > 0) { data[i] = _ring[_readIdx]; _readIdx = (_readIdx + 1) % _ringSize; _count--; } else { data[i] = 0f; } } } } public void Destroy() { _decoder.Dispose(); if ((Object)(object)_go != (Object)null) { Object.Destroy((Object)(object)_go); } } } public sealed class UnityMicCapture : IMicCapture { private const int ClipSeconds = 1; private AudioClip _clip; private string _device; private int _readPos; private readonly float[] _scratch = new float[960]; private readonly List _accum = new List(1920); public bool Running => (Object)(object)_clip != (Object)null; public void Start() { if (!Running) { _device = (string.IsNullOrEmpty(VoiceConfig.MicDevice.Value) ? null : VoiceConfig.MicDevice.Value); if (Microphone.devices == null || Microphone.devices.Length == 0) { Plugin.Log.LogWarning((object)"No microphone devices found; capture disabled."); return; } _clip = Microphone.Start(_device, true, 1, 48000); _readPos = 0; _accum.Clear(); } } public void Stop() { if (Running) { Microphone.End(_device); _clip = null; _accum.Clear(); } } public void PollFrames(List frames) { if (!Running) { return; } int position = Microphone.GetPosition(_device); if (position < 0) { return; } int samples = _clip.samples; int num = position - _readPos; if (num < 0) { num += samples; } if (num <= 0) { return; } int num2 = Mathf.Min(num, samples - _readPos); AppendFromClip(_readPos, num2); if (num > num2) { AppendFromClip(0, num - num2); } _readPos = position; while (_accum.Count >= 960) { short[] array = new short[960]; for (int i = 0; i < 960; i++) { array[i] = FloatToPcm(_accum[i]); } _accum.RemoveRange(0, 960); frames.Add(array); } } private void AppendFromClip(int offset, int count) { int num = count; int num2 = offset; while (num > 0) { int num3 = Mathf.Min(num, _scratch.Length); _clip.GetData(_scratch, num2); for (int i = 0; i < num3; i++) { _accum.Add(_scratch[i]); } num2 += num3; if (num2 >= _clip.samples) { num2 -= _clip.samples; } num -= num3; } } private static short FloatToPcm(float f) { f = Mathf.Clamp(f, -1f, 1f); return (short)Mathf.RoundToInt(f * 32767f); } } public class VoiceHud : MonoBehaviour { private class MeterPanel { public GameObject root; public Text title; public Image fill; public Image marker; public Text info; public RectTransform track; } private const int W = 26; private const int H = 18; private const float DbFloor = -60f; private const float GainMax = 12f; private static readonly float[] WaveRadius = new float[3] { 11f, 14f, 17f }; private static readonly float[] WaveHalf = new float[3] { 2.5f, 4.5f, 7f }; private static readonly float[] WaveThreshold = new float[3] { 0.06f, 0.33f, 0.62f }; private static readonly Color Gold = new Color(0.96f, 0.85f, 0.5f); private static readonly Color GreenLvl = new Color(0.3f, 0.95f, 0.4f, 0.95f); private static readonly Color GreyLvl = new Color(0.6f, 0.6f, 0.6f, 0.85f); private static readonly Color Red = new Color(1f, 0.32f, 0.32f, 1f); private static readonly Color Blue = new Color(0.4f, 0.7f, 1f, 0.95f); private static Sprite _bodySprite; private static Sprite[] _waveSprites; private static Sprite _whiteSprite; private static Sprite _panelSprite; private static Font _font; private RectTransform _overlayRoot; private RectTransform _micRoot; private Image _micBody; private Image[] _micWaves; private MeterPanel _gainPanel; private MeterPanel _calPanel; private GameObject _rebindPanel; private bool _micVisible; private bool _micGate; private bool _calOpen; private bool _calGate; private bool _gainOpen; private bool _gainGate; private bool _rebindOpen; private float _micLvl; private float _calDb; private float _calThr; private float _gainDb; private float _gainVal; public static VoiceHud Instance { get; private set; } private void Awake() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected O, but got Unknown Instance = this; GameObject val = new GameObject("ProximityVoice_Overlay"); val.transform.SetParent(((Component)this).transform, false); Canvas obj = val.AddComponent(); obj.renderMode = (RenderMode)0; obj.sortingOrder = 1000; val.AddComponent().uiScaleMode = (ScaleMode)0; _overlayRoot = (RectTransform)val.transform; } private void OnDestroy() { if ((Object)(object)Instance == (Object)(object)this) { Instance = null; } } public void SetMicVisible(bool on) { _micVisible = on; } public void SetMicLevel(float level, bool gateOpen) { _micLvl = level; _micGate = gateOpen; } public void SetCalibration(bool open, float db, float thresholdDb, bool gateOpen) { _calOpen = open; _calDb = db; _calThr = thresholdDb; _calGate = gateOpen; } public void SetGain(bool open, float db, float micGain, bool gateOpen) { _gainOpen = open; _gainDb = db; _gainVal = micGain; _gainGate = gateOpen; } public void SetRebind(bool open) { _rebindOpen = open; } private void LateUpdate() { //IL_00ab: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_01b8: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_02cb: Unknown result type (might be due to invalid IL or missing references) //IL_02c4: Unknown result type (might be due to invalid IL or missing references) //IL_01da: Unknown result type (might be due to invalid IL or missing references) //IL_020f: Unknown result type (might be due to invalid IL or missing references) //IL_0300: Unknown result type (might be due to invalid IL or missing references) bool flag = (Object)(object)Player.m_localPlayer != (Object)null; EnsureMicIndicator(); if ((Object)(object)_micRoot != (Object)null) { bool flag2 = flag && _micVisible && !_rebindOpen; ((Component)_micRoot).gameObject.SetActive(flag2); if (flag2) { float num = (_micGate ? Mathf.Clamp01(_micLvl * 4f) : 0f); for (int i = 0; i < _micWaves.Length; i++) { float num2 = Mathf.Clamp01((num - WaveThreshold[i]) / 0.15f); ((Graphic)_micWaves[i]).color = new Color(1f, 1f, 1f, num2); } } } bool flag3 = flag && _rebindOpen; bool flag4 = flag && _calOpen && !_rebindOpen; bool flag5 = flag && _gainOpen && !_rebindOpen; if (flag3) { EnsureRebindPanel(); } if ((Object)(object)_rebindPanel != (Object)null) { _rebindPanel.SetActive(flag3); } if (flag4 || flag5) { EnsureMeterPanels(); } if (_calPanel != null) { _calPanel.root.SetActive(flag4); if (flag4) { float fillAmount = Mathf.Clamp01(Mathf.InverseLerp(-60f, 0f, _calDb)); float frac = Mathf.Clamp01(Mathf.InverseLerp(-60f, 0f, _calThr)); _calPanel.fill.fillAmount = fillAmount; ((Graphic)_calPanel.fill).color = (_calGate ? GreenLvl : GreyLvl); SetMarker(_calPanel, frac); ((Graphic)_calPanel.marker).color = Red; _calPanel.info.text = $"Level {_calDb:0.0} dB · Gate {_calThr:0} dB · [ ] / scroll · {VoiceConfig.CalibrateKey.Value} exit"; } } if (_gainPanel != null) { _gainPanel.root.SetActive(flag5); if (flag5) { float fillAmount2 = Mathf.Clamp01(Mathf.InverseLerp(1f, 12f, _gainVal)); float frac2 = Mathf.Clamp01(Mathf.InverseLerp(-60f, 0f, _gainDb)); _gainPanel.fill.fillAmount = fillAmount2; ((Graphic)_gainPanel.fill).color = Blue; SetMarker(_gainPanel, frac2); ((Graphic)_gainPanel.marker).color = (_gainGate ? GreenLvl : GreyLvl); _gainPanel.info.text = $"Gain {_gainVal:0.0}x · Level {_gainDb:0.0} dB · scroll / Up Down · {VoiceConfig.MicGainKey.Value} exit"; } } } private static void SetMarker(MeterPanel p, float frac) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) ((RectTransform)((Component)p.marker).transform).anchoredPosition = new Vector2(p.track.sizeDelta.x * frac, 0f); } private void EnsureMicIndicator() { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: 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_013f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_micRoot != (Object)null)) { EnsureSprites(); float num = Mathf.Max(0.1f, VoiceConfig.HudScale.Value); Vector2 size = default(Vector2); ((Vector2)(ref size))..ctor(26f * num, 18f * num); _micRoot = NewRect("ProximityVoice_Mic", (Transform)(object)_overlayRoot, size); RectTransform micRoot = _micRoot; RectTransform micRoot2 = _micRoot; Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0f, 0.5f); micRoot2.anchorMax = val; micRoot.anchorMin = val; _micRoot.pivot = new Vector2(0f, 0.5f); _micRoot.anchoredPosition = new Vector2(VoiceConfig.HudX.Value, VoiceConfig.HudBottom.Value); _micBody = NewFill("Body", (Transform)(object)_micRoot, _bodySprite, new Color(1f, 1f, 1f, 0.95f)); _micWaves = (Image[])(object)new Image[_waveSprites.Length]; for (int i = 0; i < _waveSprites.Length; i++) { _micWaves[i] = NewFill("Wave" + i, (Transform)(object)_micRoot, _waveSprites[i], new Color(1f, 1f, 1f, 0f)); } ((Component)_micRoot).gameObject.SetActive(false); } } private void EnsureMeterPanels() { if (_gainPanel == null || _calPanel == null) { EnsurePanelSprite(); _calPanel = BuildMeterPanel("Mic Calibration", 0f); _gainPanel = BuildMeterPanel("Mic Gain", 0f); } } private void EnsureRebindPanel() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00a1: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0100: Unknown result type (might be due to invalid IL or missing references) //IL_010f: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_rebindPanel != (Object)null)) { EnsurePanelSprite(); RectTransform val = NewRect("RebindPanel", (Transform)(object)_overlayRoot, new Vector2(560f, 150f)); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = new Vector2(0.5f, 0.5f); val.anchoredPosition = Vector2.zero; AddBackground(val); Text obj = NewText("Main", (Transform)(object)val, 22, (TextAnchor)4, Gold, new Vector2(512f, 30f)); ((RectTransform)((Component)obj).transform).anchoredPosition = new Vector2(0f, -54f); obj.text = "Set a new Push-to-Talk key"; Text obj2 = NewText("Sub", (Transform)(object)val, 15, (TextAnchor)4, new Color(1f, 1f, 1f, 0.78f), new Vector2(512f, 22f)); ((RectTransform)((Component)obj2).transform).anchoredPosition = new Vector2(0f, -90f); obj2.text = "Press the key or mouse button you want to talk with · Esc to cancel"; ((Component)val).gameObject.SetActive(false); _rebindPanel = ((Component)val).gameObject; } } private MeterPanel BuildMeterPanel(string title, float centreY) { //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c1: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_0116: Unknown result type (might be due to invalid IL or missing references) //IL_011d: Unknown result type (might be due to invalid IL or missing references) //IL_0134: Unknown result type (might be due to invalid IL or missing references) //IL_014e: Unknown result type (might be due to invalid IL or missing references) //IL_0188: Unknown result type (might be due to invalid IL or missing references) //IL_01a9: Unknown result type (might be due to invalid IL or missing references) //IL_0201: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0226: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_028d: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Unknown result type (might be due to invalid IL or missing references) //IL_02b6: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Unknown result type (might be due to invalid IL or missing references) MeterPanel meterPanel = new MeterPanel(); RectTransform val = NewRect("Panel", (Transform)(object)_overlayRoot, new Vector2(460f, 116f)); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 0.5f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = new Vector2(0.5f, 0.5f); val.anchoredPosition = new Vector2(0f, centreY); AddBackground(val); meterPanel.root = ((Component)val).gameObject; meterPanel.title = NewText("Title", (Transform)(object)val, 16, (TextAnchor)3, Gold, new Vector2(424f, 22f)); ((RectTransform)((Component)meterPanel.title).transform).anchoredPosition = new Vector2(0f, -28f); meterPanel.title.text = title; float num = 424f; meterPanel.track = NewRect("Track", (Transform)(object)val, new Vector2(num, 18f)); RectTransform track = meterPanel.track; RectTransform track2 = meterPanel.track; ((Vector2)(ref val2))..ctor(0.5f, 1f); track2.anchorMax = val2; track.anchorMin = val2; meterPanel.track.pivot = new Vector2(0.5f, 1f); meterPanel.track.anchoredPosition = new Vector2(0f, -58f); Image obj = ((Component)meterPanel.track).gameObject.AddComponent(); obj.sprite = _whiteSprite; ((Graphic)obj).color = new Color(1f, 1f, 1f, 0.14f); ((Graphic)obj).raycastTarget = false; meterPanel.fill = NewFill("Fill", (Transform)(object)meterPanel.track, _whiteSprite, GreenLvl); meterPanel.fill.type = (Type)3; meterPanel.fill.fillMethod = (FillMethod)0; meterPanel.fill.fillOrigin = 0; meterPanel.fill.fillAmount = 0f; RectTransform val3 = NewRect("Marker", (Transform)(object)meterPanel.track, new Vector2(2f, 26f)); ((Vector2)(ref val2))..ctor(0f, 0.5f); val3.anchorMax = val2; val3.anchorMin = val2; val3.pivot = new Vector2(0.5f, 0.5f); meterPanel.marker = ((Component)val3).gameObject.AddComponent(); meterPanel.marker.sprite = _whiteSprite; ((Graphic)meterPanel.marker).raycastTarget = false; meterPanel.info = NewText("Info", (Transform)(object)val, 13, (TextAnchor)3, new Color(1f, 1f, 1f, 0.92f), new Vector2(424f, 20f)); ((RectTransform)((Component)meterPanel.info).transform).anchoredPosition = new Vector2(0f, -90f); ((Component)val).gameObject.SetActive(false); return meterPanel; } private static RectTransform NewRect(string name, Transform parent, Vector2 size) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) RectTransform obj = new GameObject(name).AddComponent(); ((Transform)obj).SetParent(parent, false); Vector2 val = default(Vector2); ((Vector2)(ref val))..ctor(0.5f, 0.5f); obj.anchorMax = val; obj.anchorMin = val; obj.sizeDelta = size; obj.anchoredPosition = Vector2.zero; return obj; } private static Image NewFill(string name, Transform parent, Sprite sprite, Color color) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject(name); RectTransform obj = val.AddComponent(); ((Transform)obj).SetParent(parent, false); obj.anchorMin = Vector2.zero; obj.anchorMax = Vector2.one; Vector2 offsetMin = (obj.offsetMax = Vector2.zero); obj.offsetMin = offsetMin; Image obj2 = val.AddComponent(); obj2.sprite = sprite; ((Graphic)obj2).color = color; ((Graphic)obj2).raycastTarget = false; return obj2; } private static Text NewText(string name, Transform parent, int fontSize, TextAnchor anchor, Color color, Vector2 size) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) RectTransform val = NewRect(name, parent, size); Vector2 val2 = default(Vector2); ((Vector2)(ref val2))..ctor(0.5f, 1f); val.anchorMax = val2; val.anchorMin = val2; val.pivot = new Vector2(0.5f, 1f); Text obj = ((Component)val).gameObject.AddComponent(); obj.alignment = anchor; obj.fontSize = fontSize; obj.font = _font; ((Graphic)obj).color = color; ((Graphic)obj).raycastTarget = false; obj.horizontalOverflow = (HorizontalWrapMode)1; obj.verticalOverflow = (VerticalWrapMode)1; Outline obj2 = ((Component)val).gameObject.AddComponent(); ((Shadow)obj2).effectColor = Color.black; ((Shadow)obj2).effectDistance = new Vector2(1f, -1f); return obj; } private static void AddBackground(RectTransform panel) { //IL_005e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) Image val = ((Component)panel).gameObject.AddComponent(); ((Graphic)val).raycastTarget = false; if ((Object)(object)_panelSprite != (Object)null) { val.sprite = _panelSprite; val.type = (Type)1; ((Graphic)val).color = Color.white; } else { val.sprite = _whiteSprite; ((Graphic)val).color = new Color(0.06f, 0.06f, 0.07f, 0.92f); } } private static void EnsurePanelSprite() { //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_font == (Object)null) { _font = FindGameFont(); } if ((Object)(object)_whiteSprite == (Object)null) { Texture2D whiteTexture = Texture2D.whiteTexture; _whiteSprite = Sprite.Create(whiteTexture, new Rect(0f, 0f, (float)((Texture)whiteTexture).width, (float)((Texture)whiteTexture).height), new Vector2(0.5f, 0.5f)); } if ((Object)(object)_panelSprite == (Object)null) { _panelSprite = FindSignCardSprite(); } } private static Font FindGameFont() { Font val = null; Font[] array = Resources.FindObjectsOfTypeAll(); foreach (Font val2 in array) { if (((Object)val2).name == "AveriaSerifLibre-Bold") { return val2; } if ((Object)(object)val == (Object)null) { val = val2; } } if (!((Object)(object)val != (Object)null)) { return Font.CreateDynamicFontFromOSFont("Arial", 28); } return val; } private static Sprite FindSignCardSprite() { //IL_005d: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) TextInput[] array = Resources.FindObjectsOfTypeAll(); foreach (TextInput val in array) { if ((Object)(object)val == (Object)null || (Object)(object)val.m_panel == (Object)null) { continue; } Image[] componentsInChildren = val.m_panel.GetComponentsInChildren(true); foreach (Image val2 in componentsInChildren) { if ((Object)(object)val2 != (Object)null && (Object)(object)val2.sprite != (Object)null && val2.sprite.border != Vector4.zero) { return val2.sprite; } } } return null; } private static void EnsureSprites() { if ((Object)(object)_bodySprite != (Object)null) { return; } _bodySprite = ToSprite(Build((int x, int y) => IsBody(x, y))); _waveSprites = (Sprite[])(object)new Sprite[WaveRadius.Length]; for (int i = 0; i < _waveSprites.Length; i++) { int wave = i; _waveSprites[i] = ToSprite(Build((int x, int y) => IsWave(x, y, wave))); } } private static Sprite ToSprite(Texture2D tex) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) return Sprite.Create(tex, new Rect(0f, 0f, 26f, 18f), new Vector2(0.5f, 0.5f)); } private static bool IsBody(int x, int y) { if (x >= 2 && x <= 4 && Mathf.Abs((float)y - 8.5f) <= 3f) { return true; } if (x >= 4 && x <= 9) { float num = 2.5f + (float)(x - 4) * 1.1f; if (Mathf.Abs((float)y - 8.5f) <= num) { return true; } } return false; } private static bool IsWave(int x, int y, int k) { if (x < 10) { return false; } float num = (float)x - 2f; float num2 = (float)y - 8.5f; if (num <= 0f || Mathf.Abs(num2) > WaveHalf[k]) { return false; } return Mathf.Abs(Mathf.Sqrt(num * num + num2 * num2) - WaveRadius[k]) <= 0.9f; } private static Texture2D Build(Func on) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_005d: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(26, 18, (TextureFormat)5, false) { filterMode = (FilterMode)0 }; Color[] array = (Color[])(object)new Color[468]; Color val2 = default(Color); ((Color)(ref val2))..ctor(1f, 1f, 1f, 0f); for (int i = 0; i < 18; i++) { for (int j = 0; j < 26; j++) { array[i * 26 + j] = (on(j, i) ? Color.white : val2); } } val.SetPixels(array); val.Apply(); return val; } } public sealed class VoiceEncoder : IDisposable { private readonly IOpusEncoder _enc; private readonly byte[] _out = new byte[4000]; public VoiceEncoder() { _enc = OpusCodecFactory.CreateEncoder(48000, 1, (OpusApplication)2048, (TextWriter)null); _enc.Bitrate = 24000; } public byte[] Encode(short[] pcmFrame) { try { int num = _enc.Encode((ReadOnlySpan)pcmFrame, 960, (Span)_out, _out.Length); if (num <= 0) { return null; } byte[] array = new byte[num]; Buffer.BlockCopy(_out, 0, array, 0, num); return array; } catch { return null; } } public void Dispose() { ((IDisposable)_enc)?.Dispose(); } } public sealed class VoiceDecoder : IDisposable { private readonly IOpusDecoder _dec; private readonly short[] _pcm = new short[960]; public VoiceDecoder() { _dec = OpusCodecFactory.CreateDecoder(48000, 1, (TextWriter)null); } public int Decode(byte[] packet, out short[] pcmOut) { pcmOut = _pcm; try { return _dec.Decode((ReadOnlySpan)packet, (Span)_pcm, 960, false); } catch { return 0; } } public void Dispose() { ((IDisposable)_dec)?.Dispose(); } } public enum MicMode { Toggle, PushToTalk } public static class VoiceConfig { public const int SampleRate = 48000; public const int Channels = 1; public const int FrameMs = 20; public const int FrameSamples = 960; public const int Bitrate = 24000; public static ConfigEntry Mode; public static ConfigEntry PttReleaseTail; public static ConfigEntry PushToTalkKey; public static ConfigEntry CalibrateKey; public static ConfigEntry MicGainKey; public static ConfigEntry RebindKey; public static ConfigEntry FullVolumeRange; public static ConfigEntry HearingRange; public static ConfigEntry Volume; public static ConfigEntry MicGain; public static ConfigEntry MicDevice; public static ConfigEntry NoiseGateDb; public static ConfigEntry HudX; public static ConfigEntry HudBottom; public static ConfigEntry HudScale; public static ConfigEntry SelfTest; public static void Init(ConfigFile cfg) { Mode = cfg.Bind("General", "MicMode", MicMode.PushToTalk, "Toggle = press the talk key to turn the mic on/off. PushToTalk = hold the talk key to talk."); PttReleaseTail = cfg.Bind("General", "PushToTalkReleaseTail", 0.5f, "Push-to-talk only: keep transmitting this many seconds after releasing the key, so the end of your sentence isn't cut off by capture latency."); PushToTalkKey = cfg.Bind("General", "PushToTalkKey", (KeyCode)117, "Talk key (used by both modes). Mouse buttons work too: Mouse3/Mouse4 are the side buttons, Mouse2 = middle. Any Unity KeyCode is accepted."); CalibrateKey = cfg.Bind("General", "CalibrateKey", (KeyCode)287, "Toggles the in-game mic calibration overlay (live dB + adjustable noise-gate threshold)."); MicGainKey = cfg.Bind("General", "MicGainKey", (KeyCode)285, "Toggles the mic gain overlay. While open, scroll the mouse wheel (or Up/Down arrows) to raise/lower how loud your own microphone is sent to others."); RebindKey = cfg.Bind("General", "RebindKey", (KeyCode)288, "Press this, then press any key or mouse button to set it as the talk key (Esc cancels)."); FullVolumeRange = cfg.Bind("General", "FullVolumeRange", 20f, "Distance (metres) within which a speaker is heard at full volume, with no falloff. Beyond this, volume fades linearly to silence at HearingRange."); HearingRange = cfg.Bind("General", "HearingRange", 80f, "Max distance (metres) at which other players can hear you. Voice plays at full volume within FullVolumeRange, then fades linearly to silence at this distance. Used for server-side culling too."); Volume = cfg.Bind("Audio", "Volume", 1f, "Playback volume multiplier for incoming voices."); MicGain = cfg.Bind("Audio", "MicGain", 4f, "Amplification applied to your OWN microphone before sending (1 = no change). Raise this if others can barely hear you. Adjust live in the calibration overlay (CalibrateKey) with the Up/Down arrow keys."); MicDevice = cfg.Bind("Audio", "MicDevice", "", "Microphone device name. Leave empty to use the system default."); NoiseGateDb = cfg.Bind("Audio", "NoiseGateDb", -50f, "Noise gate threshold in dBFS: audio quieter than this is not transmitted. Use the in-game calibration overlay (CalibrateKey) to set it visually. -80 = off."); HudX = cfg.Bind("HUD", "MicIndicatorMarginX", 24f, "Distance in pixels from the left screen edge to the mic level indicator."); HudBottom = cfg.Bind("HUD", "MicIndicatorOffsetY", 0f, "Vertical offset in pixels of the mic level indicator from the screen centre (positive = up, negative = down)."); HudScale = cfg.Bind("HUD", "MicIndicatorScale", 2f, "Size multiplier for the mic level indicator."); SelfTest = cfg.Bind("Debug", "SelfTest", false, "Loopback test: also play your OWN voice back locally (through the full Opus pipeline) so you can judge audio quality solo. Leave OFF for normal play."); } } public static class VoiceRouter { private sealed class RateLimiter { private sealed class Bucket { public float Tokens; public float LastSeen; } private const float BurstSeconds = 1.5f; private const float ResetAfterSilence = 10f; private readonly Dictionary _buckets = new Dictionary(); public bool Allow(long senderPeerId) { float realtimeSinceStartup = Time.realtimeSinceStartup; float num = Mathf.Max(0.02f, 0.02f); float num2 = 1f / num; float num3 = Mathf.Max(3f, num2 * 1.5f); if (!_buckets.TryGetValue(senderPeerId, out var value)) { value = new Bucket { Tokens = num3, LastSeen = realtimeSinceStartup }; _buckets[senderPeerId] = value; } if (realtimeSinceStartup - value.LastSeen > 10f) { value.Tokens = num3; } else { value.Tokens = Mathf.Min(num3, value.Tokens + (realtimeSinceStartup - value.LastSeen) * num2); } value.LastSeen = realtimeSinceStartup; if (value.Tokens < 1f) { return false; } value.Tokens -= 1f; return true; } } private const string RpcUp = "PV_Up"; private const string RpcDown = "PV_Down"; private const int MaxOpusPayloadBytes = 1275; private const long LoopbackId = long.MinValue; private static ZRoutedRpc _registeredOn; private static readonly Dictionary Speakers = new Dictionary(); private static readonly RateLimiter Limiter = new RateLimiter(); public static bool Ready { get { if (_registeredOn != null) { return ZRoutedRpc.instance == _registeredOn; } return false; } } public static void EnsureRegistered() { ZRoutedRpc instance = ZRoutedRpc.instance; if (instance != _registeredOn) { if (instance == null) { Reset(); return; } instance.Register("PV_Up", (Action)OnUpstream); instance.Register("PV_Down", (Action)OnDownstream); _registeredOn = instance; ClearSpeakers(); Plugin.Log.LogInfo((object)"VoiceRouter registered RPCs."); } } public static void Reset() { _registeredOn = null; ClearSpeakers(); } private static void ClearSpeakers() { foreach (SpeakerVoice value in Speakers.Values) { value.Destroy(); } Speakers.Clear(); } public static void SendFrame(byte[] opus, Vector3 pos) { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Expected O, but got Unknown //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!Ready || opus == null) { return; } if (VoiceConfig.SelfTest.Value) { PlayLocal(long.MinValue, pos, opus); } ZPackage val = new ZPackage(); val.Write(pos.x); val.Write(pos.y); val.Write(pos.z); val.Write(opus); if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer()) { val.SetPos(0); OnUpstream(ZDOMan.GetSessionID(), val); return; } ZNetPeer val2 = (((Object)(object)ZNet.instance != (Object)null) ? ZNet.instance.GetServerPeer() : null); if (val2 != null) { ZRoutedRpc.instance.InvokeRoutedRPC(val2.m_uid, "PV_Up", new object[1] { val }); } } private static void OnUpstream(long sender, ZPackage pkg) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0073: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00ec: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ZNet.instance == (Object)null || !ZNet.instance.IsServer() || !Limiter.Allow(sender) || !TryReadFrame(pkg, out var pos, out var opus)) { return; } float value = VoiceConfig.HearingRange.Value; float num = value * value; Vector3 val; foreach (ZNetPeer peer in ZNet.instance.GetPeers()) { if (peer != null && peer.m_uid != sender) { val = peer.m_refPos - pos; if (!(((Vector3)(ref val)).sqrMagnitude > num)) { ZPackage val2 = BuildDown(sender, pos, opus); ZRoutedRpc.instance.InvokeRoutedRPC(peer.m_uid, "PV_Down", new object[1] { val2 }); } } } Player localPlayer = Player.m_localPlayer; if ((Object)(object)localPlayer != (Object)null && ZDOMan.GetSessionID() != sender) { val = ((Component)localPlayer).transform.position - pos; if (((Vector3)(ref val)).sqrMagnitude <= num) { PlayLocal(sender, pos, opus); } } } private static void OnDownstream(long sender, ZPackage pkg) { //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) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) long speakerId; Vector3 pos; byte[] opus; try { speakerId = pkg.ReadLong(); pos = new Vector3(pkg.ReadSingle(), pkg.ReadSingle(), pkg.ReadSingle()); opus = pkg.ReadByteArray(); } catch { return; } if (IsValidFrame(pos, opus)) { PlayLocal(speakerId, pos, opus); } } private static bool TryReadFrame(ZPackage pkg, out Vector3 pos, out byte[] opus) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) pos = default(Vector3); opus = null; try { pos = new Vector3(pkg.ReadSingle(), pkg.ReadSingle(), pkg.ReadSingle()); opus = pkg.ReadByteArray(); } catch { return false; } return IsValidFrame(pos, opus); } private static bool IsValidFrame(Vector3 pos, byte[] opus) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) if (opus == null || opus.Length == 0 || opus.Length > 1275) { return false; } if (!IsFinite(pos.x) || !IsFinite(pos.y) || !IsFinite(pos.z)) { return false; } return true; } private static bool IsFinite(float f) { if (!float.IsNaN(f)) { return !float.IsInfinity(f); } return false; } private static void PlayLocal(long speakerId, Vector3 pos, byte[] opus) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) if (!Speakers.TryGetValue(speakerId, out var value)) { value = new SpeakerVoice(speakerId); Speakers[speakerId] = value; } value.SetPosition(pos); value.PushPacket(opus); } private static ZPackage BuildDown(long speakerId, Vector3 pos, byte[] opus) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_0018: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown ZPackage val = new ZPackage(); val.Write(speakerId); val.Write(pos.x); val.Write(pos.y); val.Write(pos.z); val.Write(opus); return val; } public static void PruneIdle(float idleSeconds) { if (Speakers.Count == 0) { return; } float realtimeSinceStartup = Time.realtimeSinceStartup; List list = null; foreach (KeyValuePair speaker in Speakers) { if (realtimeSinceStartup - speaker.Value.LastActiveTime > idleSeconds) { (list ?? (list = new List())).Add(speaker.Key); } } if (list == null) { return; } foreach (long item in list) { Speakers[item].Destroy(); Speakers.Remove(item); } } } [BepInPlugin("Nubedeu.proximityvoice", "ProximityVoice", "0.2.1")] public class Plugin : BaseUnityPlugin { public const string PluginGUID = "Nubedeu.proximityvoice"; public const string PluginName = "ProximityVoice"; public const string PluginVersion = "0.2.1"; public static ManualLogSource Log; private IMicCapture _mic; private readonly List _frameBuf = new List(); private VoiceEncoder _encoder; private bool _wasCapturing; private bool _micOn; private bool _calibrating; private bool _gainAdjust; private bool _rebinding; private static KeyCode[] _rebindCandidates; private float _micLevel; private float _curDb; private bool _gateOpen; private float _gateOpenUntil; private float _pttHeldUntil; private const float GateHold = 0.25f; private static KeyCode[] RebindCandidates { get { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (_rebindCandidates == null) { List list = new List(); foreach (KeyCode value in Enum.GetValues(typeof(KeyCode))) { if ((int)value != 0) { list.Add(value); } } _rebindCandidates = list.ToArray(); } return _rebindCandidates; } } private void Awake() { //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Expected O, but got Unknown Log = ((BaseUnityPlugin)this).Logger; VoiceConfig.Init(((BaseUnityPlugin)this).Config); _mic = MicCapture.Create(); _encoder = new VoiceEncoder(); GameObject val = new GameObject("ProximityVoice_Hud"); Object.DontDestroyOnLoad((Object)val); val.AddComponent(); Log.LogInfo((object)"ProximityVoice loaded."); } private void Update() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0040: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Unknown result type (might be due to invalid IL or missing references) //IL_0195: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_01e8: Unknown result type (might be due to invalid IL or missing references) //IL_037d: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_03b1: Unknown result type (might be due to invalid IL or missing references) VoiceRouter.EnsureRegistered(); if (!VoiceRouter.Ready) { return; } Player localPlayer = Player.m_localPlayer; if (_rebinding) { HandleRebind(); return; } if (Input.GetKeyDown(VoiceConfig.RebindKey.Value)) { _rebinding = true; return; } KeyCode value = VoiceConfig.PushToTalkKey.Value; if (VoiceConfig.Mode.Value == MicMode.PushToTalk) { if (Input.GetKey(value)) { _pttHeldUntil = Time.time + VoiceConfig.PttReleaseTail.Value; } _micOn = Time.time <= _pttHeldUntil; } else if (Input.GetKeyDown(value)) { _micOn = !_micOn; if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, _micOn ? "VOIP Ligado" : "VOIP Desligado", 0, (Sprite)null, false); } } if (Input.GetKeyDown(VoiceConfig.MicGainKey.Value)) { _gainAdjust = !_gainAdjust; } if (_gainAdjust) { float num = 0f; if (Input.GetKeyDown((KeyCode)273)) { num += 0.5f; } if (Input.GetKeyDown((KeyCode)274)) { num -= 0.5f; } float y = Input.mouseScrollDelta.y; if (y > 0f) { num += 0.5f; } else if (y < 0f) { num -= 0.5f; } if (num != 0f) { VoiceConfig.MicGain.Value = Mathf.Clamp(VoiceConfig.MicGain.Value + num, 1f, 20f); } } if (Input.GetKeyDown(VoiceConfig.CalibrateKey.Value)) { _calibrating = !_calibrating; } if (_calibrating) { float num2 = 0f; if (Input.GetKeyDown((KeyCode)91)) { num2 -= 1f; } if (Input.GetKeyDown((KeyCode)93)) { num2 += 1f; } float y2 = Input.mouseScrollDelta.y; if (y2 > 0f) { num2 += 1f; } else if (y2 < 0f) { num2 -= 1f; } if (num2 != 0f) { VoiceConfig.NoiseGateDb.Value = Mathf.Clamp(VoiceConfig.NoiseGateDb.Value + num2, -80f, 0f); } } bool flag = (_micOn || _calibrating || _gainAdjust) && (Object)(object)localPlayer != (Object)null; float num3 = 0f; if (flag) { if (!_mic.Running) { _mic.Start(); } _frameBuf.Clear(); _mic.PollFrames(_frameBuf); ApplyMicGain(_frameBuf, VoiceConfig.MicGain.Value); foreach (short[] item in _frameBuf) { for (int i = 0; i < item.Length; i++) { float num4 = (float)Mathf.Abs((int)item[i]) * 3.0517578E-05f; if (num4 > num3) { num3 = num4; } } } if (Dbfs(num3) >= VoiceConfig.NoiseGateDb.Value) { _gateOpenUntil = Time.time + 0.25f; } _gateOpen = Time.time <= _gateOpenUntil; if (_micOn && _gateOpen && (Object)(object)localPlayer != (Object)null) { Vector3 position = ((Component)localPlayer).transform.position; foreach (short[] item2 in _frameBuf) { byte[] array = _encoder.Encode(item2); if (array != null) { VoiceRouter.SendFrame(array, position); } } } } else if (_wasCapturing) { _mic.Stop(); _gateOpen = false; } float num5 = (flag ? num3 : 0f); if (num5 > _micLevel) { _micLevel = num5; } else { _micLevel = Mathf.MoveTowards(_micLevel, num5, Time.deltaTime * 1.5f); } _curDb = Dbfs(_micLevel); _wasCapturing = flag; VoiceRouter.PruneIdle(2f); } private static void ApplyMicGain(List frames, float gain) { if (gain == 1f) { return; } foreach (short[] frame in frames) { for (int i = 0; i < frame.Length; i++) { int num = (int)((float)frame[i] * gain); if (num > 32767) { num = 32767; } else if (num < -32768) { num = -32768; } frame[i] = (short)num; } } } private static float Dbfs(float amp) { if (!(amp <= 0.0001f)) { return Mathf.Max(-80f, 20f * Mathf.Log10(amp)); } return -80f; } private void HandleRebind() { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Unknown result type (might be due to invalid IL or missing references) if (Input.GetKeyDown((KeyCode)27)) { _rebinding = false; } else { if (!Input.anyKeyDown) { return; } KeyCode[] rebindCandidates = RebindCandidates; for (int i = 0; i < rebindCandidates.Length; i++) { KeyCode val = rebindCandidates[i]; if ((int)val != 27 && val != VoiceConfig.RebindKey.Value && Input.GetKeyDown(val)) { VoiceConfig.PushToTalkKey.Value = val; _rebinding = false; if ((Object)(object)MessageHud.instance != (Object)null) { MessageHud.instance.ShowMessage((MessageType)1, "Talk key: " + ((object)(KeyCode)(ref val)).ToString(), 0, (Sprite)null, false); } break; } } } } private void LateUpdate() { VoiceHud instance = VoiceHud.Instance; if (!((Object)(object)instance == (Object)null)) { instance.SetRebind(_rebinding); instance.SetMicVisible(_micOn); instance.SetMicLevel(_micLevel, _gateOpen); instance.SetCalibration(_calibrating, _curDb, VoiceConfig.NoiseGateDb.Value, _gateOpen); instance.SetGain(_gainAdjust, _curDb, VoiceConfig.MicGain.Value, _gateOpen); } } private void OnDestroy() { _mic.Stop(); _encoder?.Dispose(); VoiceRouter.Reset(); } } }