VOIP.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using BreakoutMods.BreakoutNet;
using Concentus.Celt;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using Concentus.Silk;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using Concentus.Structs;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace VOIP
{
	internal sealed class VoiceCapture : MonoBehaviour
	{
		private const int MicrophoneBufferSeconds = 1;

		private static readonly MethodInfo AudioClipGetData = typeof(AudioClip).GetMethod("GetData", new Type[2]
		{
			typeof(float[]),
			typeof(int)
		});

		private VoiceNetwork _network;

		private AudioClip _microphoneClip;

		private string _device;

		private int _lastPosition;

		private int _sequence;

		private int _activeSampleRate;

		private int _activeFrameMilliseconds;

		private float _stopMicrophoneAt;

		private float[] _frameBuffer;

		private float[] _scratch;

		private readonly OpusVoiceCodec _codec = new OpusVoiceCodec();

		public void Initialize(VoiceNetwork network)
		{
			_network = network;
		}

		private void Update()
		{
			if (!VoiceRuntimeSettings.Enabled || (Object)(object)ZNet.instance == (Object)null || !IsInWorld())
			{
				StopMicrophone();
			}
			else if (!ShouldTransmit())
			{
				if (!VoiceSettings.VoiceActivation.Value && (Object)(object)_microphoneClip != (Object)null && Time.time >= _stopMicrophoneAt)
				{
					StopMicrophone();
				}
			}
			else
			{
				_stopMicrophoneAt = Time.time + (float)VoiceSettings.EffectiveMicrophoneStopDelayMilliseconds / 1000f;
				EnsureMicrophone();
				CaptureAvailableFrames();
			}
		}

		private static bool IsInWorld()
		{
			return (Object)(object)Player.m_localPlayer != (Object)null && ZRoutedRpc.instance != null;
		}

		private static bool ShouldTransmit()
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			return VoiceSettings.VoiceActivation.Value || Input.GetKey(VoiceSettings.PushToTalkKeyCode);
		}

		private void EnsureMicrophone()
		{
			int sampleRate = VoiceRuntimeSettings.SampleRate;
			int frameMilliseconds = VoiceRuntimeSettings.FrameMilliseconds;
			string text = ResolveMicrophoneDevice();
			if (!((Object)(object)_microphoneClip != (Object)null) || !Microphone.IsRecording(_device) || _activeSampleRate != sampleRate || _activeFrameMilliseconds != frameMilliseconds || !(_device == text))
			{
				StopMicrophone();
				if (Microphone.devices.Length == 0)
				{
					VoiceLog.WarningRateLimited("voice-no-microphone", "VOIP could not find a microphone device.", 10f);
					return;
				}
				_device = text;
				_microphoneClip = Microphone.Start(_device, true, 1, sampleRate);
				_lastPosition = 0;
				_activeSampleRate = sampleRate;
				_activeFrameMilliseconds = frameMilliseconds;
				int num = sampleRate * frameMilliseconds / 1000;
				_frameBuffer = new float[num];
				_scratch = new float[_microphoneClip.samples];
			}
		}

		private static string ResolveMicrophoneDevice()
		{
			string value = VoiceSettings.MicrophoneDevice.Value;
			if (!string.IsNullOrWhiteSpace(value))
			{
				string[] devices = Microphone.devices;
				foreach (string text in devices)
				{
					if (string.Equals(text, value, StringComparison.OrdinalIgnoreCase))
					{
						return text;
					}
				}
				VoiceLog.WarningRateLimited("voice-microphone-missing-" + value, "Configured VOIP microphone device was not found: " + value, 10f);
			}
			return (Microphone.devices.Length > 0) ? Microphone.devices[0] : null;
		}

		private void StopMicrophone()
		{
			if (!((Object)(object)_microphoneClip == (Object)null))
			{
				if (Microphone.IsRecording(_device))
				{
					Microphone.End(_device);
				}
				_microphoneClip = null;
				_lastPosition = 0;
				_frameBuffer = null;
				_scratch = null;
			}
		}

		private void CaptureAvailableFrames()
		{
			if ((Object)(object)_microphoneClip == (Object)null)
			{
				return;
			}
			int position = Microphone.GetPosition(_device);
			if (position >= 0 && position != _lastPosition)
			{
				int num = ((position > _lastPosition) ? (position - _lastPosition) : (_microphoneClip.samples - _lastPosition + position));
				while (num >= _frameBuffer.Length)
				{
					ReadFrame(_lastPosition, _frameBuffer);
					_lastPosition = (_lastPosition + _frameBuffer.Length) % _microphoneClip.samples;
					num -= _frameBuffer.Length;
					TrySendFrame(_frameBuffer);
				}
			}
		}

		private void ReadFrame(int startPosition, float[] destination)
		{
			if (startPosition + destination.Length <= _microphoneClip.samples)
			{
				GetAudioClipData(_microphoneClip, destination, startPosition);
				return;
			}
			GetAudioClipData(_microphoneClip, _scratch, 0);
			for (int i = 0; i < destination.Length; i++)
			{
				destination[i] = _scratch[(startPosition + i) % _microphoneClip.samples];
			}
		}

		private static void GetAudioClipData(AudioClip clip, float[] destination, int offsetSamples)
		{
			if (AudioClipGetData == null)
			{
				throw new MissingMethodException("UnityEngine.AudioClip.GetData(float[], int)");
			}
			AudioClipGetData.Invoke(clip, new object[2] { destination, offsetSamples });
		}

		private void TrySendFrame(float[] frame)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			if (!VoiceSettings.VoiceActivation.Value || !(AudioMath.Rms(frame, frame.Length) < VoiceSettings.VoiceActivationThreshold.Value))
			{
				Player localPlayer = Player.m_localPlayer;
				if (!((Object)(object)localPlayer == (Object)null))
				{
					VoicePacket voicePacket = new VoicePacket();
					voicePacket.SpeakerId = localPlayer.GetPlayerID();
					voicePacket.SenderPeerId = ZNet.GetUID();
					voicePacket.Sequence = ++_sequence;
					voicePacket.SpeakerPosition = ((Component)localPlayer).transform.position;
					voicePacket.SampleRate = VoiceRuntimeSettings.SampleRate;
					voicePacket.Samples = frame.Length;
					voicePacket.OpusPayload = _codec.Encode(frame, frame.Length, VoiceRuntimeSettings.SampleRate);
					VoicePacket packet = voicePacket;
					_network.Send(packet);
					VoiceHud.MarkTransmitting();
				}
			}
		}
	}
	internal sealed class VoiceClient
	{
		private readonly BreakoutModuleContext _context;

		public VoiceClient(BreakoutModuleContext context)
		{
			_context = context;
		}

		public void Send(VoicePacket packet)
		{
			if (BreakoutSide.IsInWorld && !((Object)(object)ZNet.instance == (Object)null))
			{
				packet.SenderPeerId = ZNet.GetUID();
				Client.SendToServer<VoicePacket>("voip.voice.frame", packet, (_context != null) ? _context.ModGuid : "com.breakoutmods.voip");
			}
		}

		public void ApplyServerSettings(VoiceServerSettings settings)
		{
			try
			{
				string summary;
				bool flag = VoiceRuntimeSettings.ApplyServerSettings(settings, out summary);
				if (flag)
				{
					VOIPPlugin.Log.LogInfo((object)("Applied server voice settings: " + summary));
				}
				else
				{
					VoiceLog.InfoRateLimited("voice-settings-unchanged", "Received server voice settings: " + summary, 60f);
				}
				if (_context != null)
				{
					_context.Events.Publish<VoiceSettingsAppliedEvent>(new VoiceSettingsAppliedEvent(flag, summary));
					_context.Events.Publish<VoiceSettingsAppliedEvent>("voip.settings.applied", new VoiceSettingsAppliedEvent(flag, summary));
				}
			}
			catch (Exception ex)
			{
				VoiceLog.WarningRateLimited("voice-settings-malformed", "Dropped malformed voice settings package from server: " + ex.Message, 10f);
			}
		}

		public void OnRpcUnavailable()
		{
			VoiceRuntimeSettings.ClearServerSettings();
		}
	}
	internal sealed class VoiceHud : MonoBehaviour
	{
		private const float IndicatorSeconds = 0.35f;

		private static float _transmittingUntil;

		private static float _receivingUntil;

		public static void MarkTransmitting()
		{
			_transmittingUntil = Time.time + 0.35f;
		}

		public static void MarkReceiving()
		{
			_receivingUntil = Time.time + 0.35f;
		}

		private void Update()
		{
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && VoiceRuntimeSettings.Enabled)
			{
				HandleInput();
			}
		}

		private void OnGUI()
		{
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)Player.m_localPlayer == (Object)null) && VoiceRuntimeSettings.Enabled)
			{
				string text = null;
				if (VoiceMuteState.Deafened)
				{
					text = "VOIP DEAFENED";
				}
				else if (VoiceMuteState.HasLastSpeaker && VoiceMuteState.IsMuted(VoiceMuteState.LastSpeakerId))
				{
					text = "VOIP MUTED";
				}
				else if (Time.time < _transmittingUntil)
				{
					text = "VOIP TX";
				}
				else if (Time.time < _receivingUntil)
				{
					text = "VOIP RX";
				}
				if (text != null)
				{
					GUI.Label(new Rect(20f, (float)Screen.height - 60f, 160f, 30f), text);
				}
			}
		}

		private static void HandleInput()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			if (Input.GetKeyDown(VoiceSettings.ToggleDeafenKeyCode))
			{
				VoiceMuteState.ToggleDeafen();
			}
			if (Input.GetKeyDown(VoiceSettings.ToggleMuteLastSpeakerKeyCode))
			{
				VoiceMuteState.ToggleLastSpeakerMute();
			}
		}
	}
	internal static class VoiceMuteState
	{
		private static readonly HashSet<long> MutedSpeakerIds = new HashSet<long>();

		private static string _loadedConfigValue;

		public static bool Deafened { get; private set; }

		public static long LastSpeakerId { get; private set; }

		public static bool HasLastSpeaker { get; private set; }

		public static void ToggleDeafen()
		{
			Deafened = !Deafened;
			VOIPPlugin.Log.LogInfo((object)("VOIP deafen " + (Deafened ? "enabled" : "disabled") + "."));
		}

		public static void RememberSpeaker(long speakerId)
		{
			LastSpeakerId = speakerId;
			HasLastSpeaker = true;
		}

		public static void ToggleLastSpeakerMute()
		{
			LoadMutedSpeakers();
			if (!HasLastSpeaker)
			{
				VoiceLog.InfoRateLimited("voice-mute-no-speaker", "VOIP has no recent speaker to mute.", 3f);
				return;
			}
			if (MutedSpeakerIds.Contains(LastSpeakerId))
			{
				MutedSpeakerIds.Remove(LastSpeakerId);
				VOIPPlugin.Log.LogInfo((object)("Unmuted VOIP speaker " + LastSpeakerId + "."));
			}
			else
			{
				MutedSpeakerIds.Add(LastSpeakerId);
				VOIPPlugin.Log.LogInfo((object)("Muted VOIP speaker " + LastSpeakerId + "."));
			}
			SaveMutedSpeakers();
		}

		public static bool IsMuted(long speakerId)
		{
			LoadMutedSpeakers();
			return MutedSpeakerIds.Contains(speakerId);
		}

		private static void LoadMutedSpeakers()
		{
			string text = VoiceSettings.MutedSpeakerIds.Value ?? string.Empty;
			if (_loadedConfigValue == text)
			{
				return;
			}
			MutedSpeakerIds.Clear();
			string[] array = text.Split(',');
			foreach (string text2 in array)
			{
				if (long.TryParse(text2.Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out var result))
				{
					MutedSpeakerIds.Add(result);
				}
			}
			_loadedConfigValue = text;
		}

		private static void SaveMutedSpeakers()
		{
			string text = string.Join(",", (from id in MutedSpeakerIds
				orderby id
				select id.ToString(CultureInfo.InvariantCulture)).ToArray());
			VoiceSettings.MutedSpeakerIds.Value = text;
			_loadedConfigValue = text;
		}
	}
	internal sealed class VoicePlayback : MonoBehaviour
	{
		private sealed class SpeakerPlayback
		{
			private readonly object _lock = new object();

			private readonly GameObject _sourceObject;

			private float[] _ring;

			private AudioClip _clip;

			private int _readIndex;

			private int _writeIndex;

			private int _bufferedSamples;

			private int _sampleRate;

			private int _lastSequence;

			private bool _hasSequence;

			private int _underflowEvents;

			private int _droppedSamples;

			private int _lostPackets;

			private float _nextBufferLog;

			public readonly long SpeakerId;

			public readonly AudioSource Source;

			public readonly OpusVoiceCodec Codec = new OpusVoiceCodec();

			public float LastPacketTime { get; private set; }

			private int BufferedSamples
			{
				get
				{
					lock (_lock)
					{
						return _bufferedSamples;
					}
				}
			}

			public SpeakerPlayback(long speakerId, GameObject sourceObject, int sampleRate)
			{
				SpeakerId = speakerId;
				_sourceObject = sourceObject;
				Source = sourceObject.AddComponent<AudioSource>();
				Source.playOnAwake = false;
				Source.spatialBlend = 1f;
				Source.rolloffMode = (AudioRolloffMode)1;
				Source.dopplerLevel = 0f;
				EnsureSampleRate(sampleRate);
			}

			public void EnsureSampleRate(int sampleRate)
			{
				//IL_00b6: Unknown result type (might be due to invalid IL or missing references)
				//IL_00c2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00cc: Expected O, but got Unknown
				//IL_00cc: Expected O, but got Unknown
				sampleRate = Mathf.Max(8000, sampleRate);
				if (!((Object)(object)_clip != (Object)null) || _sampleRate != sampleRate)
				{
					_sampleRate = sampleRate;
					lock (_lock)
					{
						ClearBuffer();
					}
					if (Source.isPlaying)
					{
						Source.Stop();
					}
					_clip = AudioClip.Create("voip-stream-" + SpeakerId, _sampleRate, 1, _sampleRate, true, new PCMReaderCallback(OnAudioRead), new PCMSetPositionCallback(OnAudioSetPosition));
					Source.clip = _clip;
					Source.loop = true;
				}
			}

			public void UpdateSource(Vector3 position)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				LastPacketTime = Time.time;
				((Component)Source).transform.position = position;
				Source.maxDistance = VoiceRuntimeSettings.ProximityMeters;
				Source.minDistance = VoiceRuntimeSettings.FullVolumeMeters;
				Source.volume = VoiceSettings.PlaybackVolume.Value;
			}

			public void TrackSequence(int sequence)
			{
				lock (_lock)
				{
					if (_hasSequence && sequence > _lastSequence + 1)
					{
						_lostPackets += sequence - _lastSequence - 1;
					}
					_lastSequence = sequence;
					_hasSequence = true;
				}
			}

			public void Enqueue(float[] decodedSamples)
			{
				if (decodedSamples == null || decodedSamples.Length == 0)
				{
					return;
				}
				int capacity = Mathf.Max(1, _sampleRate * VoiceSettings.EffectiveMaxJitterBufferMilliseconds / 1000);
				int num = 0;
				lock (_lock)
				{
					EnsureRingCapacity(capacity);
					foreach (float num2 in decodedSamples)
					{
						if (_bufferedSamples == _ring.Length)
						{
							_readIndex = (_readIndex + 1) % _ring.Length;
							_bufferedSamples--;
							num++;
						}
						_ring[_writeIndex] = num2;
						_writeIndex = (_writeIndex + 1) % _ring.Length;
						_bufferedSamples++;
					}
					_droppedSamples += num;
				}
			}

			public void UpdatePlayback()
			{
				int num = Mathf.Max(1, _sampleRate * VoiceSettings.EffectiveJitterBufferMilliseconds / 1000);
				if (!Source.isPlaying && BufferedSamples >= num)
				{
					Source.Play();
					VoiceLog.InfoRateLimited("voice-jitter-start-" + SpeakerId, "Started jitter-buffered playback for " + SpeakerId + " with " + BufferedSamples + " queued samples.", 10f);
				}
				if (Time.time >= _nextBufferLog)
				{
					int underflowEvents;
					int droppedSamples;
					int lostPackets;
					lock (_lock)
					{
						underflowEvents = _underflowEvents;
						droppedSamples = _droppedSamples;
						lostPackets = _lostPackets;
						_underflowEvents = 0;
						_droppedSamples = 0;
						_lostPackets = 0;
					}
					_nextBufferLog = Time.time + 10f;
					if (underflowEvents > 0)
					{
						VOIPPlugin.Log.LogWarning((object)("Voice jitter buffer underflow for " + SpeakerId + " (" + underflowEvents + " silent samples in the last window)."));
					}
					if (droppedSamples > 0)
					{
						VOIPPlugin.Log.LogWarning((object)("Voice jitter buffer dropped " + droppedSamples + " old samples for " + SpeakerId + " to stay within the max buffer."));
					}
					if (lostPackets > 0)
					{
						VOIPPlugin.Log.LogWarning((object)("Voice playback detected " + lostPackets + " missing packet sequence(s) for " + SpeakerId + " in the last window."));
					}
				}
			}

			public void Destroy()
			{
				if ((Object)(object)_sourceObject != (Object)null)
				{
					Object.Destroy((Object)(object)_sourceObject);
				}
			}

			private void OnAudioRead(float[] data)
			{
				lock (_lock)
				{
					for (int i = 0; i < data.Length; i++)
					{
						if (_bufferedSamples > 0 && _ring != null)
						{
							data[i] = _ring[_readIndex];
							_readIndex = (_readIndex + 1) % _ring.Length;
							_bufferedSamples--;
						}
						else
						{
							data[i] = 0f;
							_underflowEvents++;
						}
					}
				}
			}

			private void OnAudioSetPosition(int position)
			{
			}

			private void EnsureRingCapacity(int capacity)
			{
				if (_ring == null || _ring.Length != capacity)
				{
					_ring = new float[capacity];
					ClearBuffer();
				}
			}

			private void ClearBuffer()
			{
				_readIndex = 0;
				_writeIndex = 0;
				_bufferedSamples = 0;
			}
		}

		private const float SpeakerIdleDestroySeconds = 10f;

		private readonly Dictionary<long, SpeakerPlayback> _speakers = new Dictionary<long, SpeakerPlayback>();

		public void Play(VoicePacket packet)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			VoiceMuteState.RememberSpeaker(packet.SpeakerId);
			if (VoiceMuteState.Deafened || VoiceMuteState.IsMuted(packet.SpeakerId) || packet.OpusPayload == null || packet.OpusPayload.Length == 0 || packet.Samples <= 0)
			{
				return;
			}
			SpeakerPlayback speaker = GetSpeaker(packet.SpeakerId, packet.SampleRate);
			speaker.UpdateSource(packet.SpeakerPosition);
			speaker.TrackSequence(packet.Sequence);
			try
			{
				speaker.Enqueue(speaker.Codec.Decode(packet.OpusPayload, packet.Samples, packet.SampleRate));
				VoiceHud.MarkReceiving();
			}
			catch (Exception ex)
			{
				VoiceLog.WarningRateLimited("voice-decode-failed-" + packet.SpeakerId, "Dropped undecodable voice frame from " + packet.SpeakerId + ": " + ex.Message, 5f);
			}
		}

		private void Update()
		{
			List<long> list = null;
			foreach (KeyValuePair<long, SpeakerPlayback> speaker in _speakers)
			{
				speaker.Value.UpdatePlayback();
				if (Time.time - speaker.Value.LastPacketTime > 10f)
				{
					if (list == null)
					{
						list = new List<long>();
					}
					list.Add(speaker.Key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (long item in list)
			{
				if (_speakers.TryGetValue(item, out var value))
				{
					value.Destroy();
					_speakers.Remove(item);
				}
			}
		}

		private SpeakerPlayback GetSpeaker(long speakerId, int sampleRate)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Expected O, but got Unknown
			if (_speakers.TryGetValue(speakerId, out var value) && value != null)
			{
				value.EnsureSampleRate(sampleRate);
				return value;
			}
			GameObject val = new GameObject("VOIP Speaker " + speakerId);
			Object.DontDestroyOnLoad((Object)(object)val);
			value = new SpeakerPlayback(speakerId, val, sampleRate);
			_speakers[speakerId] = value;
			return value;
		}

		private void OnDestroy()
		{
			foreach (SpeakerPlayback value in _speakers.Values)
			{
				value.Destroy();
			}
			_speakers.Clear();
		}
	}
	internal sealed class VoiceRateLimiter
	{
		private sealed class Bucket
		{
			public float Tokens;

			public float LastSeen;

			public Bucket(float tokens, float lastSeen)
			{
				Tokens = tokens;
				LastSeen = lastSeen;
			}
		}

		private const float BurstSeconds = 1.5f;

		private const float MaxSilenceBeforeResetSeconds = 10f;

		private readonly Dictionary<long, Bucket> _buckets = new Dictionary<long, Bucket>();

		public bool Allow(long senderPeerId)
		{
			float time = Time.time;
			float num = Mathf.Max(0.02f, (float)VoiceRuntimeSettings.FrameMilliseconds / 1000f);
			float num2 = 1f / num;
			float num3 = Mathf.Max(3f, num2 * 1.5f);
			if (!_buckets.TryGetValue(senderPeerId, out var value))
			{
				value = new Bucket(num3, time);
				_buckets[senderPeerId] = value;
			}
			if (time - value.LastSeen > 10f)
			{
				value.Tokens = num3;
			}
			else
			{
				value.Tokens = Mathf.Min(num3, value.Tokens + (time - value.LastSeen) * num2);
			}
			value.LastSeen = time;
			if (value.Tokens < 1f)
			{
				return false;
			}
			value.Tokens -= 1f;
			return true;
		}
	}
	internal sealed class VoiceServer : MonoBehaviour
	{
		private readonly VoiceRateLimiter _rateLimiter = new VoiceRateLimiter();

		private BreakoutModuleContext _context;

		internal static VoiceServer Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		public void Initialize(BreakoutModuleContext context)
		{
			_context = context;
		}

		private void OnDestroy()
		{
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
			}
		}

		public VoicePacket Relay(long senderPeerId, VoicePacket packet)
		{
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_011e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0124: Unknown result type (might be due to invalid IL or missing references)
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || !BreakoutSide.IsServer)
			{
				return null;
			}
			if (!VoiceRuntimeSettings.Enabled)
			{
				return null;
			}
			if (!_rateLimiter.Allow(senderPeerId))
			{
				VoiceLog.WarningRateLimited("voice-rate-limit-" + senderPeerId, "Dropped voice frames from peer " + senderPeerId + " because they exceeded the configured voice rate.", 5f);
				return null;
			}
			if (!TryGetServerKnownPosition(senderPeerId, out var position))
			{
				VoiceLog.WarningRateLimited("voice-missing-position-" + senderPeerId, "Dropped voice frame from peer " + senderPeerId + " because the server could not resolve its position.", 5f);
				return null;
			}
			VoicePacket voicePacket = packet.WithServerSpeaker(senderPeerId, position);
			float proximityMeters = VoiceRuntimeSettings.ProximityMeters;
			float num = proximityMeters * proximityMeters;
			int num2 = 0;
			foreach (ZNetPeer connectedPeer in BreakoutPeers.ConnectedPeers)
			{
				if (connectedPeer != null && connectedPeer.m_uid != senderPeerId)
				{
					Vector3 val = connectedPeer.GetRefPos() - voicePacket.SpeakerPosition;
					if (!(((Vector3)(ref val)).sqrMagnitude > num) && Server.SendToClient<VoicePacket>(connectedPeer.m_uid, "voip.voice.frame", voicePacket, (_context != null) ? _context.ModGuid : "com.breakoutmods.voip"))
					{
						num2++;
					}
				}
			}
			if (_context != null)
			{
				VoicePacketRelayedEvent voicePacketRelayedEvent = new VoicePacketRelayedEvent(voicePacket.SpeakerId, num2, voicePacket.Sequence);
				_context.Events.Publish<VoicePacketRelayedEvent>(voicePacketRelayedEvent);
				_context.Events.Publish<VoicePacketRelayedEvent>("voip.voice.relayed", voicePacketRelayedEvent);
			}
			return voicePacket;
		}

		private static bool TryGetServerKnownPosition(long senderPeerId, out Vector3 position)
		{
			//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_009c: 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_006f: 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)
			if (senderPeerId == ZNet.GetUID() && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				position = ((Component)Player.m_localPlayer).transform.position;
				return true;
			}
			foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers())
			{
				if (connectedPeer != null && connectedPeer.m_uid == senderPeerId)
				{
					position = connectedPeer.GetRefPos();
					return true;
				}
			}
			position = Vector3.zero;
			return false;
		}
	}
	internal static class AudioMath
	{
		public static float Rms(float[] samples, int count)
		{
			if (count <= 0)
			{
				return 0f;
			}
			double num = 0.0;
			for (int i = 0; i < count; i++)
			{
				num += (double)(samples[i] * samples[i]);
			}
			return (float)Math.Sqrt(num / (double)count);
		}
	}
	internal sealed class OpusVoiceCodec
	{
		private const int Channels = 1;

		private const int MaxOpusPacketBytes = 1275;

		private OpusEncoder _encoder;

		private OpusDecoder _decoder;

		private int _encoderSampleRate;

		private int _decoderSampleRate;

		private int _bitrate;

		private int _complexity;

		private short[] _encodeBuffer;

		private short[] _decodeBuffer;

		private byte[] _packetBuffer;

		public byte[] Encode(float[] samples, int count, int sampleRate)
		{
			EnsureEncoder(sampleRate, count);
			for (int i = 0; i < count; i++)
			{
				float num = Math.Max(-1f, Math.Min(1f, samples[i]));
				_encodeBuffer[i] = (short)Math.Round(num * 32767f);
			}
			int num2 = _encoder.Encode(_encodeBuffer, 0, count, _packetBuffer, 0, _packetBuffer.Length);
			byte[] array = new byte[num2];
			Buffer.BlockCopy(_packetBuffer, 0, array, 0, num2);
			return array;
		}

		public float[] Decode(byte[] payload, int samples, int sampleRate)
		{
			EnsureDecoder(sampleRate, samples);
			int num = _decoder.Decode(payload, 0, payload.Length, _decodeBuffer, 0, samples);
			float[] array = new float[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = (float)_decodeBuffer[i] / 32768f;
			}
			return array;
		}

		private void EnsureEncoder(int sampleRate, int frameSamples)
		{
			int opusBitrate = VoiceRuntimeSettings.OpusBitrate;
			int opusComplexity = VoiceRuntimeSettings.OpusComplexity;
			if (_encoder == null || _encoderSampleRate != sampleRate || _bitrate != opusBitrate || _complexity != opusComplexity)
			{
				_encoder = new OpusEncoder(sampleRate, 1, OpusApplication.OPUS_APPLICATION_VOIP);
				_encoder.Bitrate = opusBitrate;
				_encoder.Complexity = opusComplexity;
				_encoder.SignalType = OpusSignal.OPUS_SIGNAL_VOICE;
				_encoder.UseVBR = true;
				_encoder.UseConstrainedVBR = true;
				_encoderSampleRate = sampleRate;
				_bitrate = opusBitrate;
				_complexity = opusComplexity;
			}
			if (_encodeBuffer == null || _encodeBuffer.Length < frameSamples)
			{
				_encodeBuffer = new short[frameSamples];
			}
			if (_packetBuffer == null)
			{
				_packetBuffer = new byte[1275];
			}
		}

		private void EnsureDecoder(int sampleRate, int frameSamples)
		{
			if (_decoder == null || _decoderSampleRate != sampleRate)
			{
				_decoder = new OpusDecoder(sampleRate, 1);
				_decoderSampleRate = sampleRate;
			}
			if (_decodeBuffer == null || _decodeBuffer.Length < frameSamples)
			{
				_decodeBuffer = new short[frameSamples];
			}
		}
	}
	public sealed class VoiceSettingsAppliedEvent : IBreakoutEvent
	{
		public bool Changed { get; private set; }

		public string Summary { get; private set; }

		public VoiceSettingsAppliedEvent(bool changed, string summary)
		{
			Changed = changed;
			Summary = summary ?? string.Empty;
		}
	}
	public sealed class VoicePacketRelayedEvent : IBreakoutEvent
	{
		public long SpeakerId { get; private set; }

		public int Recipients { get; private set; }

		public int Sequence { get; private set; }

		public VoicePacketRelayedEvent(long speakerId, int recipients, int sequence)
		{
			SpeakerId = speakerId;
			Recipients = recipients;
			Sequence = sequence;
		}
	}
	internal static class VoiceLog
	{
		private static readonly Dictionary<string, float> NextLogTimeByKey = new Dictionary<string, float>();

		public static void InfoRateLimited(string key, string message, float intervalSeconds)
		{
			if (ShouldLog(key, intervalSeconds))
			{
				VOIPPlugin.Log.LogInfo((object)message);
			}
		}

		public static void WarningRateLimited(string key, string message, float intervalSeconds)
		{
			if (ShouldLog(key, intervalSeconds))
			{
				VOIPPlugin.Log.LogWarning((object)message);
			}
		}

		private static bool ShouldLog(string key, float intervalSeconds)
		{
			float time = Time.time;
			if (NextLogTimeByKey.TryGetValue(key, out var value) && time < value)
			{
				return false;
			}
			NextLogTimeByKey[key] = time + Mathf.Max(1f, intervalSeconds);
			return true;
		}
	}
	internal sealed class VoiceNetwork : MonoBehaviour
	{
		internal const string VoiceFrameRpcName = "voip.voice.frame";

		internal const string SettingsName = "voip.settings";

		private static readonly BreakoutRpcRateLimit VoiceFrameRateLimit = BreakoutRpcRateLimit.ForMessagesPerSecond(60f, 3f);

		private BreakoutModuleContext _context;

		private VoiceClient _client;

		private VoiceServer _server;

		private VoicePlayback _playback;

		public void Initialize(BreakoutModuleContext context, VoiceClient client, VoiceServer server, VoicePlayback playback)
		{
			_context = context;
			_client = client;
			_server = server;
			_playback = playback;
			Server.Register<VoicePacket>("voip.voice.frame", (BreakoutRpcHandler<VoicePacket>)OnServerVoiceFrame, VoiceFrameRateLimit);
			Client.Register<VoicePacket>("voip.voice.frame", (BreakoutRpcHandler<VoicePacket>)OnClientVoiceFrame);
			BreakoutSettingsSync.RegisterServerSettings<VoiceServerSettings>("voip.settings", (Func<VoiceServerSettings>)VoiceRuntimeSettings.CreateServerSettings);
			Client.Register<VoiceServerSettings>("voip.settings", (Action<VoiceServerSettings>)OnClientSettings);
			if (_context != null)
			{
				_context.Hooks.OnWorldLeft((Action<BreakoutWorldLeftEvent>)OnWorldLeft);
				_context.Hooks.OnRpcRejected((Action<BreakoutRpcRejectedEvent>)OnRpcRejected);
			}
			VOIPPlugin.Log.LogInfo((object)"Voice RPC registered through BreakoutNet.");
		}

		public void Send(VoicePacket packet)
		{
			if (_client != null)
			{
				_client.Send(packet);
			}
		}

		private void OnWorldLeft(BreakoutWorldLeftEvent evt)
		{
			if (_client != null)
			{
				_client.OnRpcUnavailable();
			}
		}

		private static void OnRpcRejected(BreakoutRpcRejectedEvent evt)
		{
			if (!(evt.RpcName != "voip.voice.frame") || !(evt.RpcName != string.Empty))
			{
				VoiceLog.WarningRateLimited("voice-breakoutnet-rpc-rejected-" + evt.Category, "BreakoutNet rejected a VOIP RPC from peer " + evt.SenderPeerId + ": " + evt.Reason, 10f);
			}
		}

		private void OnServerVoiceFrame(BreakoutRpcContext context, VoicePacket packet)
		{
			if (VoiceRuntimeSettings.Enabled)
			{
				VoicePacket voicePacket = (((Object)(object)_server != (Object)null) ? _server.Relay(context.SenderPeerId, packet) : null);
				if (voicePacket != null && (Object)(object)_playback != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null && voicePacket.SpeakerId != ZNet.GetUID())
				{
					_playback.Play(voicePacket);
				}
			}
		}

		private void OnClientVoiceFrame(BreakoutRpcContext context, VoicePacket packet)
		{
			if (!context.IsFromServer)
			{
				context.Reject("Voice frame must come from the server.");
			}
			else if (!((Object)(object)_playback == (Object)null) && packet.SpeakerId != ZNet.GetUID())
			{
				_playback.Play(packet);
			}
		}

		private void OnClientSettings(VoiceServerSettings settings)
		{
			if (_client != null)
			{
				_client.ApplyServerSettings(settings);
			}
		}
	}
	internal sealed class VoicePacket : IBreakoutSerializable
	{
		public const int CurrentProtocolVersion = 2;

		public const int MaxOpusPayloadBytes = 1275;

		public int ProtocolVersion = 2;

		public int Sequence;

		public long SenderPeerId;

		public long SpeakerId;

		public Vector3 SpeakerPosition;

		public int SampleRate;

		public int Samples;

		public byte[] OpusPayload;

		public ZPackage ToPackage()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			Write(val);
			val.SetPos(0);
			return val;
		}

		public static VoicePacket FromPackage(ZPackage package)
		{
			VoicePacket voicePacket = new VoicePacket();
			voicePacket.Read(package);
			Validate(voicePacket);
			return voicePacket;
		}

		public void Write(ZPackage package)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			package.Write(ProtocolVersion);
			package.Write(Sequence);
			package.Write(SenderPeerId);
			package.Write(SpeakerId);
			package.Write(SpeakerPosition);
			package.Write(SampleRate);
			package.Write(Samples);
			package.Write(OpusPayload);
		}

		public void Read(ZPackage package)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			ProtocolVersion = package.ReadInt();
			Sequence = package.ReadInt();
			SenderPeerId = package.ReadLong();
			SpeakerId = package.ReadLong();
			SpeakerPosition = package.ReadVector3();
			SampleRate = package.ReadInt();
			Samples = package.ReadInt();
			OpusPayload = package.ReadByteArray();
			Validate(this);
		}

		public static void Validate(VoicePacket packet)
		{
			//IL_0137: Unknown result type (might be due to invalid IL or missing references)
			if (packet == null)
			{
				throw new ArgumentNullException("packet");
			}
			if (packet.ProtocolVersion != 2)
			{
				throw new InvalidOperationException("Unsupported voice protocol version " + packet.ProtocolVersion + ".");
			}
			if (!IsSupportedSampleRate(packet.SampleRate))
			{
				throw new InvalidOperationException("Unsupported voice sample rate " + packet.SampleRate + ".");
			}
			if (!IsSupportedSampleCount(packet.SampleRate, packet.Samples))
			{
				throw new InvalidOperationException("Unsupported voice sample count " + packet.Samples + " for sample rate " + packet.SampleRate + ".");
			}
			if (packet.OpusPayload == null || packet.OpusPayload.Length == 0)
			{
				throw new InvalidOperationException("Voice payload is empty.");
			}
			if (packet.OpusPayload.Length > 1275)
			{
				throw new InvalidOperationException("Voice payload is too large: " + packet.OpusPayload.Length + " bytes.");
			}
			if (!IsFinite(packet.SpeakerPosition))
			{
				throw new InvalidOperationException("Voice speaker position is invalid.");
			}
		}

		public VoicePacket WithServerSpeaker(long senderPeerId, Vector3 serverPosition)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			VoicePacket voicePacket = new VoicePacket();
			voicePacket.ProtocolVersion = 2;
			voicePacket.Sequence = Sequence;
			voicePacket.SenderPeerId = senderPeerId;
			voicePacket.SpeakerId = senderPeerId;
			voicePacket.SpeakerPosition = serverPosition;
			voicePacket.SampleRate = SampleRate;
			voicePacket.Samples = Samples;
			voicePacket.OpusPayload = OpusPayload;
			VoicePacket voicePacket2 = voicePacket;
			Validate(voicePacket2);
			return voicePacket2;
		}

		private static bool IsSupportedSampleRate(int sampleRate)
		{
			return sampleRate == 8000 || sampleRate == 12000 || sampleRate == 16000 || sampleRate == 24000 || sampleRate == 48000;
		}

		private static bool IsSupportedSampleCount(int sampleRate, int samples)
		{
			return samples == sampleRate * 20 / 1000 || samples == sampleRate * 40 / 1000 || samples == sampleRate * 60 / 1000;
		}

		private static bool IsFinite(Vector3 value)
		{
			return IsFinite(value.x) && IsFinite(value.y) && IsFinite(value.z);
		}

		private static bool IsFinite(float value)
		{
			return !float.IsNaN(value) && !float.IsInfinity(value);
		}
	}
	internal static class VoiceRuntimeSettings
	{
		private static bool _hasServerSettings;

		private static bool _enabled;

		private static float _proximityMeters;

		private static float _fullVolumeMeters;

		private static int _sampleRate;

		private static int _frameMilliseconds;

		private static int _opusBitrate;

		private static int _opusComplexity;

		private static string _lastAppliedSummary = string.Empty;

		public static bool Enabled => _hasServerSettings ? _enabled : VoiceSettings.Enabled.Value;

		public static float ProximityMeters => _hasServerSettings ? _proximityMeters : VoiceSettings.ProximityMeters.Value;

		public static float FullVolumeMeters => _hasServerSettings ? _fullVolumeMeters : VoiceSettings.FullVolumeMeters.Value;

		public static int SampleRate => _hasServerSettings ? _sampleRate : VoiceSettings.EffectiveSampleRate;

		public static int FrameMilliseconds => _hasServerSettings ? _frameMilliseconds : VoiceSettings.EffectiveFrameMilliseconds;

		public static int OpusBitrate => _hasServerSettings ? _opusBitrate : VoiceSettings.EffectiveOpusBitrate;

		public static int OpusComplexity => _hasServerSettings ? _opusComplexity : VoiceSettings.EffectiveOpusComplexity;

		public static void ClearServerSettings()
		{
			if (_hasServerSettings)
			{
				VoiceLog.InfoRateLimited("voice-settings-cleared", "Cleared server voice settings; using local config until the server syncs again.", 10f);
			}
			_hasServerSettings = false;
			_lastAppliedSummary = string.Empty;
		}

		public static VoiceServerSettings CreateServerSettings()
		{
			VoiceServerSettings voiceServerSettings = new VoiceServerSettings();
			voiceServerSettings.Enabled = VoiceSettings.Enabled.Value;
			voiceServerSettings.ProximityMeters = VoiceSettings.ProximityMeters.Value;
			voiceServerSettings.FullVolumeMeters = VoiceSettings.FullVolumeMeters.Value;
			voiceServerSettings.SampleRate = VoiceSettings.EffectiveSampleRate;
			voiceServerSettings.FrameMilliseconds = VoiceSettings.EffectiveFrameMilliseconds;
			voiceServerSettings.OpusBitrate = VoiceSettings.EffectiveOpusBitrate;
			voiceServerSettings.OpusComplexity = VoiceSettings.EffectiveOpusComplexity;
			return voiceServerSettings;
		}

		public static ZPackage CreateServerPackage()
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			ZPackage val = new ZPackage();
			CreateServerSettings().Write(val);
			val.SetPos(0);
			return val;
		}

		public static bool ApplyServerPackage(ZPackage package, out string summary)
		{
			VoiceServerSettings voiceServerSettings = new VoiceServerSettings();
			voiceServerSettings.Read(package);
			return ApplyServerSettings(voiceServerSettings, out summary);
		}

		public static bool ApplyServerSettings(VoiceServerSettings settings, out string summary)
		{
			summary = string.Empty;
			if (settings.Version != 1)
			{
				VoiceLog.WarningRateLimited("voice-settings-version", "Ignored unsupported voice settings package version " + settings.Version + ".", 30f);
				return false;
			}
			_enabled = settings.Enabled;
			_proximityMeters = Mathf.Max(1f, settings.ProximityMeters);
			_fullVolumeMeters = Mathf.Clamp(settings.FullVolumeMeters, 0.1f, _proximityMeters);
			_sampleRate = SanitizeSampleRate(settings.SampleRate);
			_frameMilliseconds = SanitizeFrameMilliseconds(settings.FrameMilliseconds);
			_opusBitrate = Mathf.Clamp(settings.OpusBitrate, 6000, 128000);
			_opusComplexity = Mathf.Clamp(settings.OpusComplexity, 0, 10);
			_hasServerSettings = true;
			summary = CreateSummary(_enabled, _proximityMeters, _fullVolumeMeters, _sampleRate, _frameMilliseconds, _opusBitrate, _opusComplexity);
			bool result = summary != _lastAppliedSummary;
			_lastAppliedSummary = summary;
			return result;
		}

		public static string CreateServerSummary()
		{
			return CreateSummary(VoiceSettings.Enabled.Value, VoiceSettings.ProximityMeters.Value, VoiceSettings.FullVolumeMeters.Value, VoiceSettings.EffectiveSampleRate, VoiceSettings.EffectiveFrameMilliseconds, VoiceSettings.EffectiveOpusBitrate, VoiceSettings.EffectiveOpusComplexity);
		}

		private static string CreateSummary(bool enabled, float proximityMeters, float fullVolumeMeters, int sampleRate, int frameMilliseconds, int opusBitrate, int opusComplexity)
		{
			return "enabled=" + enabled + ", proximity=" + proximityMeters.ToString("0.#") + "m, fullVolume=" + fullVolumeMeters.ToString("0.#") + "m, sampleRate=" + sampleRate + ", frameMs=" + frameMilliseconds + ", bitrate=" + opusBitrate + ", complexity=" + opusComplexity;
		}

		private static int SanitizeSampleRate(int rate)
		{
			if (rate == 8000 || rate == 12000 || rate == 16000 || rate == 24000 || rate == 48000)
			{
				return rate;
			}
			return 16000;
		}

		private static int SanitizeFrameMilliseconds(int ms)
		{
			if (ms == 20 || ms == 40 || ms == 60)
			{
				return ms;
			}
			return 60;
		}
	}
	internal sealed class VoiceServerSettings : IBreakoutSerializable
	{
		public const int CurrentVersion = 1;

		public int Version = 1;

		public bool Enabled;

		public float ProximityMeters;

		public float FullVolumeMeters;

		public int SampleRate;

		public int FrameMilliseconds;

		public int OpusBitrate;

		public int OpusComplexity;

		public void Write(ZPackage package)
		{
			package.Write(Version);
			package.Write(Enabled);
			package.Write(ProximityMeters);
			package.Write(FullVolumeMeters);
			package.Write(SampleRate);
			package.Write(FrameMilliseconds);
			package.Write(OpusBitrate);
			package.Write(OpusComplexity);
		}

		public void Read(ZPackage package)
		{
			Version = package.ReadInt();
			Enabled = package.ReadBool();
			ProximityMeters = package.ReadSingle();
			FullVolumeMeters = package.ReadSingle();
			SampleRate = package.ReadInt();
			FrameMilliseconds = package.ReadInt();
			OpusBitrate = package.ReadInt();
			OpusComplexity = package.ReadInt();
		}
	}
	internal static class VoiceSettings
	{
		public static ConfigEntry<bool> Enabled { get; private set; }

		public static ConfigEntry<string> PushToTalkKey { get; private set; }

		public static ConfigEntry<string> MicrophoneDevice { get; private set; }

		public static ConfigEntry<int> MicrophoneStopDelayMilliseconds { get; private set; }

		public static ConfigEntry<string> ToggleDeafenKey { get; private set; }

		public static ConfigEntry<string> ToggleMuteLastSpeakerKey { get; private set; }

		public static ConfigEntry<string> MutedSpeakerIds { get; private set; }

		public static ConfigEntry<bool> VoiceActivation { get; private set; }

		public static ConfigEntry<float> VoiceActivationThreshold { get; private set; }

		public static ConfigEntry<float> ProximityMeters { get; private set; }

		public static ConfigEntry<float> FullVolumeMeters { get; private set; }

		public static ConfigEntry<float> PlaybackVolume { get; private set; }

		public static ConfigEntry<int> SampleRate { get; private set; }

		public static ConfigEntry<int> FrameMilliseconds { get; private set; }

		public static ConfigEntry<int> JitterBufferMilliseconds { get; private set; }

		public static ConfigEntry<int> MaxJitterBufferMilliseconds { get; private set; }

		public static ConfigEntry<int> OpusBitrate { get; private set; }

		public static ConfigEntry<int> OpusComplexity { get; private set; }

		public static KeyCode PushToTalkKeyCode => ParseKeyCode(PushToTalkKey.Value, (KeyCode)118);

		public static KeyCode ToggleDeafenKeyCode => ParseKeyCode(ToggleDeafenKey.Value, (KeyCode)98);

		public static KeyCode ToggleMuteLastSpeakerKeyCode => ParseKeyCode(ToggleMuteLastSpeakerKey.Value, (KeyCode)109);

		public static int EffectiveSampleRate
		{
			get
			{
				int value = SampleRate.Value;
				if (value == 8000 || value == 12000 || value == 16000 || value == 24000 || value == 48000)
				{
					return value;
				}
				return 16000;
			}
		}

		public static int EffectiveFrameMilliseconds
		{
			get
			{
				int value = FrameMilliseconds.Value;
				if (value == 20 || value == 40 || value == 60)
				{
					return value;
				}
				return 60;
			}
		}

		public static int EffectiveOpusComplexity => Mathf.Clamp(OpusComplexity.Value, 0, 10);

		public static int EffectiveOpusBitrate => Mathf.Clamp(OpusBitrate.Value, 6000, 128000);

		public static int EffectiveJitterBufferMilliseconds => Mathf.Clamp(JitterBufferMilliseconds.Value, 20, 1000);

		public static int EffectiveMaxJitterBufferMilliseconds => Mathf.Clamp(MaxJitterBufferMilliseconds.Value, EffectiveJitterBufferMilliseconds, 2000);

		public static int EffectiveMicrophoneStopDelayMilliseconds => Mathf.Clamp(MicrophoneStopDelayMilliseconds.Value, 0, 5000);

		public static void Bind(ConfigFile config)
		{
			Enabled = config.Bind<bool>("General", "Enabled", true, "Enable proximity voice chat.");
			PushToTalkKey = config.Bind<string>("Input", "PushToTalkKey", "V", "Unity KeyCode name used for push-to-talk.");
			MicrophoneDevice = config.Bind<string>("Input", "MicrophoneDevice", "", "Preferred Unity microphone device name. Leave empty to use the default device.");
			MicrophoneStopDelayMilliseconds = config.Bind<int>("Input", "MicrophoneStopDelayMilliseconds", 250, "Delay before stopping the microphone after push-to-talk is released.");
			ToggleDeafenKey = config.Bind<string>("Input", "ToggleDeafenKey", "B", "Unity KeyCode name used to toggle local deafen.");
			ToggleMuteLastSpeakerKey = config.Bind<string>("Input", "ToggleMuteLastSpeakerKey", "M", "Unity KeyCode name used to mute or unmute the last received speaker.");
			MutedSpeakerIds = config.Bind<string>("Moderation", "MutedSpeakerIds", "", "Comma-separated VOIP speaker IDs muted locally.");
			VoiceActivation = config.Bind<bool>("Input", "VoiceActivation", false, "Transmit when the microphone level exceeds the configured threshold.");
			VoiceActivationThreshold = config.Bind<float>("Input", "VoiceActivationThreshold", 0.015f, "RMS threshold used when voice activation is enabled.");
			ProximityMeters = config.Bind<float>("Proximity", "ProximityMeters", 35f, "Maximum distance in meters for receiving voice.");
			FullVolumeMeters = config.Bind<float>("Proximity", "FullVolumeMeters", 6f, "Distance in meters where playback is still full volume.");
			PlaybackVolume = config.Bind<float>("Audio", "PlaybackVolume", 1f, "Master playback volume for received voice.");
			SampleRate = config.Bind<int>("Audio", "SampleRate", 16000, "Microphone sample rate. Opus supports 8000, 12000, 16000, 24000, and 48000.");
			FrameMilliseconds = config.Bind<int>("Audio", "FrameMilliseconds", 60, "Captured audio duration per network packet. Opus supports 20, 40, or 60 here.");
			JitterBufferMilliseconds = config.Bind<int>("Audio", "JitterBufferMilliseconds", 120, "Target playback buffer before received voice starts.");
			MaxJitterBufferMilliseconds = config.Bind<int>("Audio", "MaxJitterBufferMilliseconds", 500, "Maximum queued playback audio before old samples are dropped.");
			OpusBitrate = config.Bind<int>("Opus", "Bitrate", 24000, "Target Opus bitrate in bits per second.");
			OpusComplexity = config.Bind<int>("Opus", "Complexity", 5, "Opus encoder complexity from 0 to 10.");
		}

		private static KeyCode ParseKeyCode(string value, KeyCode fallback)
		{
			//IL_000f: 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_0011: 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)
			KeyCode result;
			return Enum.TryParse<KeyCode>(value, ignoreCase: true, out result) ? result : fallback;
		}
	}
	internal static class VoiceValidationHarness
	{
		public static bool InvalidProtocolVersionFails()
		{
			return Fails(CreatePackage(999, 1, 2L, 16000, 960, new byte[1] { 1 }));
		}

		public static bool InvalidSampleRateFails()
		{
			return Fails(CreatePackage(2, 1, 1L, 11025, 220, new byte[1] { 1 }));
		}

		public static bool MismatchedSampleCountFails()
		{
			return Fails(CreatePackage(2, 1, 1L, 16000, 123, new byte[1] { 1 }));
		}

		public static bool EmptyPayloadFails()
		{
			return Fails(CreatePackage(2, 1, 1L, 16000, 960, new byte[0]));
		}

		public static bool OversizedPayloadFails()
		{
			return Fails(CreatePackage(2, 1, 1L, 16000, 960, new byte[1276]));
		}

		private static bool Fails(ZPackage package)
		{
			try
			{
				VoicePacket.FromPackage(package);
				return false;
			}
			catch
			{
				return true;
			}
		}

		private static ZPackage CreatePackage(int protocolVersion, int sequence, long speakerId, int sampleRate, int samples, byte[] payload)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = new ZPackage();
			val.Write(protocolVersion);
			val.Write(sequence);
			val.Write(speakerId);
			val.Write(speakerId);
			val.Write(Vector3.zero);
			val.Write(sampleRate);
			val.Write(samples);
			val.Write(payload);
			return val;
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("com.breakoutmods.voip", "VOIP", "0.4.1")]
	public sealed class VOIPPlugin : BaseUnityPlugin
	{
		public const string ModGuid = "com.breakoutmods.voip";

		public const string ModName = "VOIP";

		public const string ModVersion = "0.4.1";

		private BreakoutModApp _breakoutApp;

		private GameObject _runnerObject;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			VoiceSettings.Bind(((BaseUnityPlugin)this).Config);
			_breakoutApp = BreakoutNet.ForPlugin((BaseUnityPlugin)(object)this, "com.breakoutmods.voip").Build();
			_runnerObject = new GameObject("VOIP");
			Object.DontDestroyOnLoad((Object)(object)_runnerObject);
			VoiceNetwork voiceNetwork = _runnerObject.AddComponent<VoiceNetwork>();
			VoiceServer voiceServer = _runnerObject.AddComponent<VoiceServer>();
			VoiceClient client = null;
			VoicePlayback playback = null;
			voiceServer.Initialize(_breakoutApp.Context);
			if (!Application.isBatchMode)
			{
				client = new VoiceClient(_breakoutApp.Context);
				playback = _runnerObject.AddComponent<VoicePlayback>();
				VoiceCapture voiceCapture = _runnerObject.AddComponent<VoiceCapture>();
				voiceCapture.Initialize(voiceNetwork);
				_runnerObject.AddComponent<VoiceHud>();
			}
			voiceNetwork.Initialize(_breakoutApp.Context, client, voiceServer, playback);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"VOIP 0.4.1 loaded");
		}

		private void OnDestroy()
		{
			if ((Object)(object)_runnerObject != (Object)null)
			{
				Object.Destroy((Object)(object)_runnerObject);
			}
			if (_breakoutApp != null)
			{
				_breakoutApp.Dispose();
				_breakoutApp = null;
			}
		}
	}
}
namespace Concentus.Celt
{
	internal static class Bands
	{
		public class band_ctx
		{
			public int encode;

			public CeltMode m;

			public int i;

			public int intensity;

			public int spread;

			public int tf_change;

			public EntropyCoder ec;

			public int remaining_bits;

			public int[][] bandE;

			public uint seed;
		}

		public class split_ctx
		{
			public int inv;

			public int imid;

			public int iside;

			public int delta;

			public int itheta;

			public int qalloc;
		}

		private static readonly byte[] bit_interleave_table = new byte[16]
		{
			0, 1, 1, 1, 2, 3, 3, 3, 2, 3,
			3, 3, 2, 3, 3, 3
		};

		private static readonly byte[] bit_deinterleave_table = new byte[16]
		{
			0, 3, 12, 15, 48, 51, 60, 63, 192, 195,
			204, 207, 240, 243, 252, 255
		};

		internal static int hysteresis_decision(int val, int[] thresholds, int[] hysteresis, int N, int prev)
		{
			int i;
			for (i = 0; i < N && val >= thresholds[i]; i++)
			{
			}
			if (i > prev && val < thresholds[prev] + hysteresis[prev])
			{
				i = prev;
			}
			if (i < prev && val > thresholds[prev - 1] - hysteresis[prev - 1])
			{
				i = prev;
			}
			return i;
		}

		internal static uint celt_lcg_rand(uint seed)
		{
			return 1664525 * seed + 1013904223;
		}

		internal static int bitexact_cos(int x)
		{
			int num = 4096 + x * x >> 13;
			int num2 = num;
			num2 = 32767 - num2 + Inlines.FRAC_MUL16(num2, -7651 + Inlines.FRAC_MUL16(num2, 8277 + Inlines.FRAC_MUL16(-626, num2)));
			return 1 + num2;
		}

		internal static int bitexact_log2tan(int isin, int icos)
		{
			int num = Inlines.EC_ILOG((uint)icos);
			int num2 = Inlines.EC_ILOG((uint)isin);
			icos <<= 15 - num;
			isin <<= 15 - num2;
			return (num2 - num) * 2048 + Inlines.FRAC_MUL16(isin, Inlines.FRAC_MUL16(isin, -2597) + 7932) - Inlines.FRAC_MUL16(icos, Inlines.FRAC_MUL16(icos, -2597) + 7932);
		}

		internal static void compute_band_energies(CeltMode m, int[][] X, int[][] bandE, int end, int C, int LM)
		{
			short[] eBands = m.eBands;
			int num = m.shortMdctSize << LM;
			int num2 = 0;
			do
			{
				for (int i = 0; i < end; i++)
				{
					int num3 = 0;
					int num4 = 0;
					num3 = Inlines.celt_maxabs32(X[num2], eBands[i] << LM, eBands[i + 1] - eBands[i] << LM);
					if (num3 > 0)
					{
						int num5 = Inlines.celt_ilog2(num3) - 14 + ((m.logN[i] >> 3) + LM + 1 >> 1);
						int num6 = eBands[i] << LM;
						if (num5 > 0)
						{
							do
							{
								num4 = Inlines.MAC16_16(num4, Inlines.EXTRACT16(Inlines.SHR32(X[num2][num6], num5)), Inlines.EXTRACT16(Inlines.SHR32(X[num2][num6], num5)));
							}
							while (++num6 < eBands[i + 1] << LM);
						}
						else
						{
							do
							{
								num4 = Inlines.MAC16_16(num4, Inlines.EXTRACT16(Inlines.SHL32(X[num2][num6], -num5)), Inlines.EXTRACT16(Inlines.SHL32(X[num2][num6], -num5)));
							}
							while (++num6 < eBands[i + 1] << LM);
						}
						bandE[num2][i] = 1 + Inlines.VSHR32(Inlines.celt_sqrt(num4), -num5);
					}
					else
					{
						bandE[num2][i] = 1;
					}
				}
			}
			while (++num2 < C);
		}

		internal static void normalise_bands(CeltMode m, int[][] freq, int[][] X, int[][] bandE, int end, int C, int M)
		{
			short[] eBands = m.eBands;
			int num = 0;
			do
			{
				int num2 = 0;
				do
				{
					int num3 = Inlines.celt_zlog2(bandE[num][num2]) - 13;
					int a = Inlines.VSHR32(bandE[num][num2], num3);
					int b = Inlines.EXTRACT16(Inlines.celt_rcp(Inlines.SHL32(a, 3)));
					int num4 = M * eBands[num2];
					do
					{
						X[num][num4] = Inlines.MULT16_16_Q15(Inlines.VSHR32(freq[num][num4], num3 - 1), b);
					}
					while (++num4 < M * eBands[num2 + 1]);
				}
				while (++num2 < end);
			}
			while (++num < C);
		}

		internal static void denormalise_bands(CeltMode m, int[] X, int[] freq, int freq_ptr, int[] bandLogE, int bandLogE_ptr, int start, int end, int M, int downsample, int silence)
		{
			short[] eBands = m.eBands;
			int num = M * m.shortMdctSize;
			int num2 = M * eBands[end];
			if (downsample != 1)
			{
				num2 = Inlines.IMIN(num2, num / downsample);
			}
			if (silence != 0)
			{
				num2 = 0;
				start = (end = 0);
			}
			int num3 = freq_ptr;
			int num4 = M * eBands[start];
			for (int i = 0; i < M * eBands[start]; i++)
			{
				freq[num3++] = 0;
			}
			for (int i = start; i < end; i++)
			{
				int num5 = M * eBands[i];
				int num6 = M * eBands[i + 1];
				int num7 = Inlines.ADD16(bandLogE[bandLogE_ptr + i], Inlines.SHL16(Tables.eMeans[i], 6));
				int num8 = 16 - (num7 >> 10);
				int b;
				if (num8 > 31)
				{
					num8 = 0;
					b = 0;
				}
				else
				{
					b = Inlines.celt_exp2_frac(num7 & 0x3FF);
				}
				if (num8 < 0)
				{
					if (num8 < -2)
					{
						b = 32767;
						num8 = -2;
					}
					do
					{
						freq[num3] = Inlines.SHR32(Inlines.MULT16_16(X[num4], b), -num8);
					}
					while (++num5 < num6);
				}
				else
				{
					do
					{
						freq[num3++] = Inlines.SHR32(Inlines.MULT16_16(X[num4++], b), num8);
					}
					while (++num5 < num6);
				}
			}
			Arrays.MemSetWithOffset(freq, 0, freq_ptr + num2, num - num2);
		}

		internal static void anti_collapse(CeltMode m, int[][] X_, byte[] collapse_masks, int LM, int C, int size, int start, int end, int[] logE, int[] prev1logE, int[] prev2logE, int[] pulses, uint seed)
		{
			for (int i = start; i < end; i++)
			{
				int num = m.eBands[i + 1] - m.eBands[i];
				int a = Inlines.celt_udiv(1 + pulses[i], m.eBands[i + 1] - m.eBands[i]) >> LM;
				int b = Inlines.SHR32(Inlines.celt_exp2(-Inlines.SHL16(a, 7)), 1);
				int a2 = Inlines.MULT16_32_Q15((short)16384, Inlines.MIN32(32767, b));
				int num2 = num << LM;
				int num3 = Inlines.celt_ilog2(num2) >> 1;
				num2 = Inlines.SHL32(num2, 7 - num3 << 1);
				int a3 = Inlines.celt_rsqrt_norm(num2);
				int num4 = 0;
				do
				{
					int num5 = 0;
					int a4 = prev1logE[num4 * m.nbEBands + i];
					int num6 = prev2logE[num4 * m.nbEBands + i];
					if (C == 1)
					{
						a4 = Inlines.MAX16(a4, prev1logE[m.nbEBands + i]);
						num6 = Inlines.MAX16(num6, prev2logE[m.nbEBands + i]);
					}
					int b2 = Inlines.EXTEND32(logE[num4 * m.nbEBands + i]) - Inlines.EXTEND32(Inlines.MIN16(a4, num6));
					b2 = Inlines.MAX32(0, b2);
					int b4;
					if (b2 < 16384)
					{
						int b3 = Inlines.SHR32(Inlines.celt_exp2((short)(-Inlines.EXTRACT16(b2))), 1);
						b4 = 2 * Inlines.MIN16(16383, b3);
					}
					else
					{
						b4 = 0;
					}
					if (LM == 3)
					{
						b4 = Inlines.MULT16_16_Q14(23170, Inlines.MIN32(23169, b4));
					}
					b4 = Inlines.SHR16(Inlines.MIN16(a2, b4), 1);
					b4 = Inlines.SHR32(Inlines.MULT16_16_Q15(a3, b4), num3);
					int num7 = m.eBands[i] << LM;
					for (int j = 0; j < 1 << LM; j++)
					{
						if ((collapse_masks[i * C + num4] & (1 << j)) == 0)
						{
							int num8 = num7 + j;
							for (int k = 0; k < num; k++)
							{
								seed = celt_lcg_rand(seed);
								X_[num4][num8 + (k << LM)] = (((seed & 0x8000u) != 0) ? b4 : (-b4));
							}
							num5 = 1;
						}
					}
					if (num5 != 0)
					{
						VQ.renormalise_vector(X_[num4], num7, num << LM, 32767);
					}
				}
				while (++num4 < C);
			}
		}

		internal static void intensity_stereo(CeltMode m, int[] X, int X_ptr, int[] Y, int Y_ptr, int[][] bandE, int bandID, int N)
		{
			int shift = Inlines.celt_zlog2(Inlines.MAX32(bandE[0][bandID], bandE[1][bandID])) - 13;
			int num = Inlines.VSHR32(bandE[0][bandID], shift);
			int num2 = Inlines.VSHR32(bandE[1][bandID], shift);
			int b = 1 + Inlines.celt_sqrt(1 + Inlines.MULT16_16(num, num) + Inlines.MULT16_16(num2, num2));
			int a = Inlines.DIV32_16(Inlines.SHL32(num, 14), b);
			int a2 = Inlines.DIV32_16(Inlines.SHL32(num2, 14), b);
			for (int i = 0; i < N; i++)
			{
				int b2 = X[X_ptr + i];
				int b3 = Y[Y_ptr + i];
				X[X_ptr + i] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(a, b2), a2, b3), 14));
			}
		}

		private static void stereo_split(int[] X, int X_ptr, int[] Y, int Y_ptr, int N)
		{
			for (int i = 0; i < N; i++)
			{
				int num = Inlines.MULT16_16(23170, X[X_ptr + i]);
				int num2 = Inlines.MULT16_16(23170, Y[Y_ptr + i]);
				X[X_ptr + i] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(num, num2), 15));
				Y[Y_ptr + i] = Inlines.EXTRACT16(Inlines.SHR32(Inlines.SUB32(num2, num), 15));
			}
		}

		private static void stereo_merge(int[] X, int X_ptr, int[] Y, int Y_ptr, int mid, int N)
		{
			Kernels.dual_inner_prod(Y, Y_ptr, X, X_ptr, Y, Y_ptr, N, out var xy, out var xy2);
			xy = Inlines.MULT16_32_Q15(mid, xy);
			int num = Inlines.SHR16(mid, 1);
			int num2 = Inlines.MULT16_16(num, num) + xy2 - 2 * xy;
			int num3 = Inlines.MULT16_16(num, num) + xy2 + 2 * xy;
			if (num3 < 161061 || num2 < 161061)
			{
				Array.Copy(X, X_ptr, Y, Y_ptr, N);
				return;
			}
			int num4 = Inlines.celt_ilog2(num2) >> 1;
			int num5 = Inlines.celt_ilog2(num3) >> 1;
			int x = Inlines.VSHR32(num2, num4 - 7 << 1);
			int a = Inlines.celt_rsqrt_norm(x);
			x = Inlines.VSHR32(num3, num5 - 7 << 1);
			int a2 = Inlines.celt_rsqrt_norm(x);
			if (num4 < 7)
			{
				num4 = 7;
			}
			if (num5 < 7)
			{
				num5 = 7;
			}
			for (int i = 0; i < N; i++)
			{
				int a3 = Inlines.MULT16_16_P15(mid, X[X_ptr + i]);
				int b = Y[Y_ptr + i];
				X[X_ptr + i] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(a, Inlines.SUB16(a3, b)), num4 + 1));
				Y[Y_ptr + i] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.MULT16_16(a2, Inlines.ADD16(a3, b)), num5 + 1));
			}
		}

		internal static int spreading_decision(CeltMode m, int[][] X, ref int average, int last_decision, ref int hf_average, ref int tapset_decision, int update_hf, int end, int C, int M)
		{
			int num = 0;
			int num2 = 0;
			short[] eBands = m.eBands;
			int num3 = 0;
			if (M * (eBands[end] - eBands[end - 1]) <= 8)
			{
				return 0;
			}
			int num4 = 0;
			do
			{
				for (int i = 0; i < end; i++)
				{
					int num5 = 0;
					int[] array = new int[3];
					int[] array2 = array;
					int[] array3 = X[num4];
					int num6 = M * eBands[i];
					int num7 = M * (eBands[i + 1] - eBands[i]);
					if (num7 <= 8)
					{
						continue;
					}
					for (int j = num6; j < num7 + num6; j++)
					{
						int num8 = Inlines.MULT16_16(Inlines.MULT16_16_Q15(array3[j], array3[j]), num7);
						if (num8 < 2048)
						{
							array2[0]++;
						}
						if (num8 < 512)
						{
							array2[1]++;
						}
						if (num8 < 128)
						{
							array2[2]++;
						}
					}
					if (i > m.nbEBands - 4)
					{
						num3 += Inlines.celt_udiv(32 * (array2[1] + array2[0]), num7);
					}
					num5 = ((2 * array2[2] >= num7) ? 1 : 0) + ((2 * array2[1] >= num7) ? 1 : 0) + ((2 * array2[0] >= num7) ? 1 : 0);
					num += num5 * 256;
					num2++;
				}
			}
			while (++num4 < C);
			if (update_hf != 0)
			{
				if (num3 != 0)
				{
					num3 = Inlines.celt_udiv(num3, C * (4 - m.nbEBands + end));
				}
				hf_average = hf_average + num3 >> 1;
				num3 = hf_average;
				if (tapset_decision == 2)
				{
					num3 += 4;
				}
				else if (tapset_decision == 0)
				{
					num3 -= 4;
				}
				if (num3 > 22)
				{
					tapset_decision = 2;
				}
				else if (num3 > 18)
				{
					tapset_decision = 1;
				}
				else
				{
					tapset_decision = 0;
				}
			}
			num = Inlines.celt_udiv(num, num2);
			num = 3 * (average = num + average >> 1) + ((3 - last_decision << 7) + 64) + 2 >> 2;
			return (num < 80) ? 3 : ((num < 256) ? 2 : ((num < 384) ? 1 : 0));
		}

		internal static void deinterleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard)
		{
			int num = N0 * stride;
			int[] array = new int[num];
			if (hadamard != 0)
			{
				int num2 = stride - 2;
				for (int i = 0; i < stride; i++)
				{
					for (int j = 0; j < N0; j++)
					{
						array[Tables.ordery_table[num2 + i] * N0 + j] = X[j * stride + i + X_ptr];
					}
				}
			}
			else
			{
				for (int i = 0; i < stride; i++)
				{
					for (int j = 0; j < N0; j++)
					{
						array[i * N0 + j] = X[j * stride + i + X_ptr];
					}
				}
			}
			Array.Copy(array, 0, X, X_ptr, num);
		}

		internal static void interleave_hadamard(int[] X, int X_ptr, int N0, int stride, int hadamard)
		{
			int num = N0 * stride;
			int[] array = new int[num];
			if (hadamard != 0)
			{
				int num2 = stride - 2;
				for (int i = 0; i < stride; i++)
				{
					for (int j = 0; j < N0; j++)
					{
						array[j * stride + i] = X[Tables.ordery_table[num2 + i] * N0 + j + X_ptr];
					}
				}
			}
			else
			{
				for (int i = 0; i < stride; i++)
				{
					for (int j = 0; j < N0; j++)
					{
						array[j * stride + i] = X[i * N0 + j + X_ptr];
					}
				}
			}
			Array.Copy(array, 0, X, X_ptr, num);
		}

		internal static void haar1(int[] X, int X_ptr, int N0, int stride)
		{
			N0 >>= 1;
			for (int i = 0; i < stride; i++)
			{
				for (int j = 0; j < N0; j++)
				{
					int num = X_ptr + i + stride * 2 * j;
					int a = Inlines.MULT16_16(23170, X[num]);
					int b = Inlines.MULT16_16(23170, X[num + stride]);
					X[num] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(a, b), 15));
					X[num + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(a, b), 15));
				}
			}
		}

		internal static void haar1ZeroOffset(int[] X, int N0, int stride)
		{
			N0 >>= 1;
			for (int i = 0; i < stride; i++)
			{
				for (int j = 0; j < N0; j++)
				{
					int num = i + stride * 2 * j;
					int a = Inlines.MULT16_16(23170, X[num]);
					int b = Inlines.MULT16_16(23170, X[num + stride]);
					X[num] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.ADD32(a, b), 15));
					X[num + stride] = Inlines.EXTRACT16(Inlines.PSHR32(Inlines.SUB32(a, b), 15));
				}
			}
		}

		internal static int compute_qn(int N, int b, int offset, int pulse_cap, int stereo)
		{
			short[] array = new short[8] { 16384, 17866, 19483, 21247, 23170, 25267, 27554, 30048 };
			int num = 2 * N - 1;
			if (stereo != 0 && N == 2)
			{
				num--;
			}
			int b2 = Inlines.celt_sudiv(b + num * offset, num);
			b2 = Inlines.IMIN(b - pulse_cap - 32, b2);
			b2 = Inlines.IMIN(64, b2);
			if (b2 < 4)
			{
				return 1;
			}
			int num2 = array[b2 & 7] >> 14 - (b2 >> 3);
			return num2 + 1 >> 1 << 1;
		}

		internal static void compute_theta(band_ctx ctx, split_ctx sctx, int[] X, int X_ptr, int[] Y, int Y_ptr, int N, ref int b, int B, int B0, int LM, int stereo, ref int fill)
		{
			int num = 0;
			int num2 = 0;
			int encode = ctx.encode;
			CeltMode m = ctx.m;
			int i = ctx.i;
			int intensity = ctx.intensity;
			EntropyCoder ec = ctx.ec;
			int[][] bandE = ctx.bandE;
			int num3 = m.logN[i] + LM * 8;
			int offset = (num3 >> 1) - ((stereo != 0 && N == 2) ? 16 : 4);
			int num4 = compute_qn(N, b, offset, num3, stereo);
			if (stereo != 0 && i >= intensity)
			{
				num4 = 1;
			}
			if (encode != 0)
			{
				num = VQ.stereo_itheta(X, X_ptr, Y, Y_ptr, stereo, N);
			}
			int num5 = (int)ec.tell_frac();
			if (num4 != 1)
			{
				if (encode != 0)
				{
					num = num * num4 + 8192 >> 14;
				}
				if (stereo != 0 && N > 2)
				{
					int num6 = 3;
					int num7 = num;
					int num8 = num4 / 2;
					uint ft = (uint)(num6 * (num8 + 1) + num8);
					if (encode != 0)
					{
						ec.encode((uint)((num7 <= num8) ? (num6 * num7) : (num7 - 1 - num8 + (num8 + 1) * num6)), (uint)((num7 <= num8) ? (num6 * (num7 + 1)) : (num7 - num8 + (num8 + 1) * num6)), ft);
					}
					else
					{
						int num9 = (int)ec.decode(ft);
						num7 = ((num9 >= (num8 + 1) * num6) ? (num8 + 1 + (num9 - (num8 + 1) * num6)) : (num9 / num6));
						ec.dec_update((uint)((num7 <= num8) ? (num6 * num7) : (num7 - 1 - num8 + (num8 + 1) * num6)), (uint)((num7 <= num8) ? (num6 * (num7 + 1)) : (num7 - num8 + (num8 + 1) * num6)), ft);
						num = num7;
					}
				}
				else if (B0 > 1 || stereo != 0)
				{
					if (encode != 0)
					{
						ec.enc_uint((uint)num, (uint)(num4 + 1));
					}
					else
					{
						num = (int)ec.dec_uint((uint)(num4 + 1));
					}
				}
				else
				{
					int num9 = 1;
					int num10 = ((num4 >> 1) + 1) * ((num4 >> 1) + 1);
					if (encode != 0)
					{
						num9 = ((num <= num4 >> 1) ? (num + 1) : (num4 + 1 - num));
						int num11 = ((num <= num4 >> 1) ? (num * (num + 1) >> 1) : (num10 - ((num4 + 1 - num) * (num4 + 2 - num) >> 1)));
						ec.encode((uint)num11, (uint)(num11 + num9), (uint)num10);
					}
					else
					{
						int num11 = 0;
						int num12 = (int)ec.decode((uint)num10);
						if (num12 < (num4 >> 1) * ((num4 >> 1) + 1) >> 1)
						{
							num = (int)(Inlines.isqrt32((uint)(8 * num12 + 1)) - 1) >> 1;
							num9 = num + 1;
							num11 = num * (num + 1) >> 1;
						}
						else
						{
							num = (int)(2 * (num4 + 1) - Inlines.isqrt32((uint)(8 * (num10 - num12 - 1) + 1))) >> 1;
							num9 = num4 + 1 - num;
							num11 = num10 - ((num4 + 1 - num) * (num4 + 2 - num) >> 1);
						}
						ec.dec_update((uint)num11, (uint)(num11 + num9), (uint)num10);
					}
				}
				num = Inlines.celt_udiv(num * 16384, num4);
				if (encode != 0 && stereo != 0)
				{
					if (num == 0)
					{
						intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N);
					}
					else
					{
						stereo_split(X, X_ptr, Y, Y_ptr, N);
					}
				}
			}
			else if (stereo != 0)
			{
				if (encode != 0)
				{
					num2 = ((num > 8192) ? 1 : 0);
					if (num2 != 0)
					{
						for (int j = 0; j < N; j++)
						{
							Y[Y_ptr + j] = -Y[Y_ptr + j];
						}
					}
					intensity_stereo(m, X, X_ptr, Y, Y_ptr, bandE, i, N);
				}
				if (b > 16 && ctx.remaining_bits > 16)
				{
					if (encode != 0)
					{
						ec.enc_bit_logp(num2, 2u);
					}
					else
					{
						num2 = ec.dec_bit_logp(2u);
					}
				}
				else
				{
					num2 = 0;
				}
				num = 0;
			}
			int num13 = (int)ec.tell_frac() - num5;
			b -= num13;
			int num14;
			int num15;
			int delta;
			switch (num)
			{
			case 0:
				num14 = 32767;
				num15 = 0;
				fill &= (1 << B) - 1;
				delta = -16384;
				break;
			case 16384:
				num14 = 0;
				num15 = 32767;
				fill &= (1 << B) - 1 << B;
				delta = 16384;
				break;
			default:
				num14 = bitexact_cos((short)num);
				num15 = bitexact_cos((short)(16384 - num));
				delta = Inlines.FRAC_MUL16(N - 1 << 7, bitexact_log2tan(num15, num14));
				break;
			}
			sctx.inv = num2;
			sctx.imid = num14;
			sctx.iside = num15;
			sctx.delta = delta;
			sctx.itheta = num;
			sctx.qalloc = num13;
		}

		internal static uint quant_band_n1(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr, int b, int[] lowband_out, int lowband_out_ptr)
		{
			int num = ((ctx.encode == 0) ? 1 : 0);
			int[] array = X;
			int num2 = X_ptr;
			int encode = ctx.encode;
			EntropyCoder ec = ctx.ec;
			int num3 = ((Y != null) ? 1 : 0);
			int num4 = 0;
			do
			{
				int num5 = 0;
				if (ctx.remaining_bits >= 8)
				{
					if (encode != 0)
					{
						num5 = ((array[num2] < 0) ? 1 : 0);
						ec.enc_bits((uint)num5, 1u);
					}
					else
					{
						num5 = (int)ec.dec_bits(1u);
					}
					ctx.remaining_bits -= 8;
					b -= 8;
				}
				if (num != 0)
				{
					array[num2] = ((num5 != 0) ? (-16384) : 16384);
				}
				array = Y;
				num2 = Y_ptr;
			}
			while (++num4 < 1 + num3);
			if (lowband_out != null)
			{
				lowband_out[lowband_out_ptr] = Inlines.SHR16(X[X_ptr], 4);
			}
			return 1u;
		}

		internal static uint quant_partition(band_ctx ctx, int[] X, int X_ptr, int N, int b, int B, int[] lowband, int lowband_ptr, int LM, int gain, int fill)
		{
			int num = 0;
			int num2 = 0;
			int num3 = B;
			int num4 = 0;
			int num5 = 0;
			uint result = 0u;
			int num6 = ((ctx.encode == 0) ? 1 : 0);
			int num7 = 0;
			int encode = ctx.encode;
			CeltMode m = ctx.m;
			int i = ctx.i;
			int spread = ctx.spread;
			EntropyCoder ec = ctx.ec;
			byte[] bits = m.cache.bits;
			int num8 = m.cache.index[(LM + 1) * m.nbEBands + i];
			if (LM != -1 && b > bits[num8 + bits[num8]] + 12 && N > 2)
			{
				split_ctx split_ctx = new split_ctx();
				int lowband_ptr2 = 0;
				N >>= 1;
				num7 = X_ptr + N;
				LM--;
				if (B == 1)
				{
					fill = (fill & 1) | (fill << 1);
				}
				B = B + 1 >> 1;
				compute_theta(ctx, split_ctx, X, X_ptr, X, num7, N, ref b, B, num3, LM, 0, ref fill);
				num = split_ctx.imid;
				num2 = split_ctx.iside;
				int num9 = split_ctx.delta;
				int itheta = split_ctx.itheta;
				int qalloc = split_ctx.qalloc;
				num4 = num;
				num5 = num2;
				if (num3 > 1 && ((uint)itheta & 0x3FFFu) != 0)
				{
					num9 = ((itheta <= 8192) ? Inlines.IMIN(0, num9 + (N << 3 >> 5 - LM)) : (num9 - (num9 >> 4 - LM)));
				}
				int num10 = Inlines.IMAX(0, Inlines.IMIN(b, (b - num9) / 2));
				int num11 = b - num10;
				ctx.remaining_bits -= qalloc;
				if (lowband != null)
				{
					lowband_ptr2 = lowband_ptr + N;
				}
				int remaining_bits = ctx.remaining_bits;
				if (num10 >= num11)
				{
					result = quant_partition(ctx, X, X_ptr, N, num10, B, lowband, lowband_ptr, LM, Inlines.MULT16_16_P15(gain, num4), fill);
					remaining_bits = num10 - (remaining_bits - ctx.remaining_bits);
					if (remaining_bits > 24 && itheta != 0)
					{
						num11 += remaining_bits - 24;
					}
					result |= quant_partition(ctx, X, num7, N, num11, B, lowband, lowband_ptr2, LM, Inlines.MULT16_16_P15(gain, num5), fill >> B) << (num3 >> 1);
				}
				else
				{
					result = quant_partition(ctx, X, num7, N, num11, B, lowband, lowband_ptr2, LM, Inlines.MULT16_16_P15(gain, num5), fill >> B) << (num3 >> 1);
					remaining_bits = num11 - (remaining_bits - ctx.remaining_bits);
					if (remaining_bits > 24 && itheta != 16384)
					{
						num10 += remaining_bits - 24;
					}
					result |= quant_partition(ctx, X, X_ptr, N, num10, B, lowband, lowband_ptr, LM, Inlines.MULT16_16_P15(gain, num4), fill);
				}
			}
			else
			{
				int num12 = Rate.bits2pulses(m, i, LM, b);
				int num13 = Rate.pulses2bits(m, i, LM, num12);
				ctx.remaining_bits -= num13;
				while (ctx.remaining_bits < 0 && num12 > 0)
				{
					ctx.remaining_bits += num13;
					num12--;
					num13 = Rate.pulses2bits(m, i, LM, num12);
					ctx.remaining_bits -= num13;
				}
				if (num12 != 0)
				{
					int k = Rate.get_pulses(num12);
					result = ((encode == 0) ? VQ.alg_unquant(X, X_ptr, N, k, spread, B, ec, gain) : VQ.alg_quant(X, X_ptr, N, k, spread, B, ec));
				}
				else if (num6 != 0)
				{
					uint num14 = (uint)((int)(1L << B) - 1);
					fill &= (int)num14;
					if (fill == 0)
					{
						Arrays.MemSetWithOffset(X, 0, X_ptr, N);
					}
					else
					{
						if (lowband == null)
						{
							for (int j = 0; j < N; j++)
							{
								ctx.seed = celt_lcg_rand(ctx.seed);
								X[X_ptr + j] = (int)ctx.seed >> 20;
							}
							result = num14;
						}
						else
						{
							for (int j = 0; j < N; j++)
							{
								ctx.seed = celt_lcg_rand(ctx.seed);
								int num15 = 4;
								num15 = (((ctx.seed & 0x8000u) != 0) ? num15 : (-num15));
								X[X_ptr + j] = lowband[lowband_ptr + j] + num15;
							}
							result = (uint)fill;
						}
						VQ.renormalise_vector(X, X_ptr, N, gain);
					}
				}
			}
			return result;
		}

		internal static uint quant_band(band_ctx ctx, int[] X, int X_ptr, int N, int b, int B, int[] lowband, int lowband_ptr, int LM, int[] lowband_out, int lowband_out_ptr, int gain, int[] lowband_scratch, int lowband_scratch_ptr, int fill)
		{
			int n = N;
			int num = B;
			int num2 = 0;
			int num3 = 0;
			uint num4 = 0u;
			int num5 = ((ctx.encode == 0) ? 1 : 0);
			int encode = ctx.encode;
			int num6 = ctx.tf_change;
			int hadamard = ((num == 1) ? 1 : 0);
			n = Inlines.celt_udiv(n, B);
			if (N == 1)
			{
				return quant_band_n1(ctx, X, X_ptr, null, 0, b, lowband_out, lowband_out_ptr);
			}
			if (num6 > 0)
			{
				num3 = num6;
			}
			if (lowband_scratch != null && lowband != null && (num3 != 0 || ((n & 1) == 0 && num6 < 0) || num > 1))
			{
				Array.Copy(lowband, lowband_ptr, lowband_scratch, lowband_scratch_ptr, N);
				lowband = lowband_scratch;
				lowband_ptr = lowband_scratch_ptr;
			}
			for (int i = 0; i < num3; i++)
			{
				if (encode != 0)
				{
					haar1(X, X_ptr, N >> i, 1 << i);
				}
				if (lowband != null)
				{
					haar1(lowband, lowband_ptr, N >> i, 1 << i);
				}
				fill = bit_interleave_table[fill & 0xF] | (bit_interleave_table[fill >> 4] << 2);
			}
			B >>= num3;
			n <<= num3;
			while ((n & 1) == 0 && num6 < 0)
			{
				if (encode != 0)
				{
					haar1(X, X_ptr, n, B);
				}
				if (lowband != null)
				{
					haar1(lowband, lowband_ptr, n, B);
				}
				fill |= fill << B;
				B <<= 1;
				n >>= 1;
				num2++;
				num6++;
			}
			num = B;
			int num7 = n;
			if (num > 1)
			{
				if (encode != 0)
				{
					deinterleave_hadamard(X, X_ptr, n >> num3, num << num3, hadamard);
				}
				if (lowband != null)
				{
					deinterleave_hadamard(lowband, lowband_ptr, n >> num3, num << num3, hadamard);
				}
			}
			num4 = quant_partition(ctx, X, X_ptr, N, b, B, lowband, lowband_ptr, LM, gain, fill);
			if (num5 != 0)
			{
				if (num > 1)
				{
					interleave_hadamard(X, X_ptr, n >> num3, num << num3, hadamard);
				}
				n = num7;
				B = num;
				for (int i = 0; i < num2; i++)
				{
					B >>= 1;
					n <<= 1;
					num4 |= num4 >> B;
					haar1(X, X_ptr, n, B);
				}
				for (int i = 0; i < num3; i++)
				{
					num4 = bit_deinterleave_table[num4];
					haar1(X, X_ptr, N >> i, 1 << i);
				}
				B <<= num3;
				if (lowband_out != null)
				{
					int a = Inlines.celt_sqrt(Inlines.SHL32(N, 22));
					for (int j = 0; j < N; j++)
					{
						lowband_out[lowband_out_ptr + j] = Inlines.MULT16_16_Q15(a, X[X_ptr + j]);
					}
				}
				num4 &= (uint)((1 << B) - 1);
			}
			return num4;
		}

		internal static uint quant_band_stereo(band_ctx ctx, int[] X, int X_ptr, int[] Y, int Y_ptr, int N, int b, int B, int[] lowband, int lowband_ptr, int LM, int[] lowband_out, int lowband_out_ptr, int[] lowband_scratch, int lowband_scratch_ptr, int fill)
		{
			int num = 0;
			int num2 = 0;
			int num3 = 0;
			int num4 = 0;
			int num5 = 0;
			uint num6 = 0u;
			int num7 = ((ctx.encode == 0) ? 1 : 0);
			split_ctx split_ctx = new split_ctx();
			int encode = ctx.encode;
			EntropyCoder ec = ctx.ec;
			if (N == 1)
			{
				return quant_band_n1(ctx, X, X_ptr, Y, Y_ptr, b, lowband_out, lowband_out_ptr);
			}
			int fill2 = fill;
			compute_theta(ctx, split_ctx, X, X_ptr, Y, Y_ptr, N, ref b, B, B, LM, 1, ref fill);
			num3 = split_ctx.inv;
			num = split_ctx.imid;
			num2 = split_ctx.iside;
			int delta = split_ctx.delta;
			int itheta = split_ctx.itheta;
			int qalloc = split_ctx.qalloc;
			num4 = num;
			num5 = num2;
			if (N == 2)
			{
				int num8 = 0;
				int num9 = b;
				int num10 = 0;
				if (itheta != 0 && itheta != 16384)
				{
					num10 = 8;
				}
				num9 -= num10;
				int num11 = ((itheta > 8192) ? 1 : 0);
				ctx.remaining_bits -= qalloc + num10;
				int[] array;
				int num12;
				int[] array2;
				if (num11 != 0)
				{
					array = Y;
					num12 = Y_ptr;
					array2 = X;
					int num13 = X_ptr;
				}
				else
				{
					array = X;
					num12 = X_ptr;
					array2 = Y;
					int num13 = Y_ptr;
				}
				if (num10 != 0)
				{
					if (encode != 0)
					{
						num8 = ((array[num12] * array2[Y_ptr + 1] - array[num12 + 1] * array2[Y_ptr] < 0) ? 1 : 0);
						ec.enc_bits((uint)num8, 1u);
					}
					else
					{
						num8 = (int)ec.dec_bits(1u);
					}
				}
				num8 = 1 - 2 * num8;
				num6 = quant_band(ctx, array, num12, N, num9, B, lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, 32767, lowband_scratch, lowband_scratch_ptr, fill2);
				array2[Y_ptr] = -num8 * array[num12 + 1];
				array2[Y_ptr + 1] = num8 * array[num12];
				if (num7 != 0)
				{
					X[X_ptr] = Inlines.MULT16_16_Q15(num4, X[X_ptr]);
					X[X_ptr + 1] = Inlines.MULT16_16_Q15(num4, X[X_ptr + 1]);
					Y[Y_ptr] = Inlines.MULT16_16_Q15(num5, Y[Y_ptr]);
					Y[Y_ptr + 1] = Inlines.MULT16_16_Q15(num5, Y[Y_ptr + 1]);
					int a = X[X_ptr];
					X[X_ptr] = Inlines.SUB16(a, Y[Y_ptr]);
					Y[Y_ptr] = Inlines.ADD16(a, Y[Y_ptr]);
					a = X[X_ptr + 1];
					X[X_ptr + 1] = Inlines.SUB16(a, Y[Y_ptr + 1]);
					Y[Y_ptr + 1] = Inlines.ADD16(a, Y[Y_ptr + 1]);
				}
			}
			else
			{
				int num9 = Inlines.IMAX(0, Inlines.IMIN(b, (b - delta) / 2));
				int num10 = b - num9;
				ctx.remaining_bits -= qalloc;
				int remaining_bits = ctx.remaining_bits;
				if (num9 >= num10)
				{
					num6 = quant_band(ctx, X, X_ptr, N, num9, B, lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, 32767, lowband_scratch, lowband_scratch_ptr, fill);
					remaining_bits = num9 - (remaining_bits - ctx.remaining_bits);
					if (remaining_bits > 24 && itheta != 0)
					{
						num10 += remaining_bits - 24;
					}
					num6 |= quant_band(ctx, Y, Y_ptr, N, num10, B, null, 0, LM, null, 0, num5, null, 0, fill >> B);
				}
				else
				{
					num6 = quant_band(ctx, Y, Y_ptr, N, num10, B, null, 0, LM, null, 0, num5, null, 0, fill >> B);
					remaining_bits = num10 - (remaining_bits - ctx.remaining_bits);
					if (remaining_bits > 24 && itheta != 16384)
					{
						num9 += remaining_bits - 24;
					}
					num6 |= quant_band(ctx, X, X_ptr, N, num9, B, lowband, lowband_ptr, LM, lowband_out, lowband_out_ptr, 32767, lowband_scratch, lowband_scratch_ptr, fill);
				}
			}
			if (num7 != 0)
			{
				if (N != 2)
				{
					stereo_merge(X, X_ptr, Y, Y_ptr, num4, N);
				}
				if (num3 != 0)
				{
					for (int i = Y_ptr; i < N + Y_ptr; i++)
					{
						Y[i] = (short)(-Y[i]);
					}
				}
			}
			return num6;
		}

		internal static void quant_all_bands(int encode, CeltMode m, int start, int end, int[] X_, int[] Y_, byte[] collapse_masks, int[][] bandE, int[] pulses, int shortBlocks, int spread, int dual_stereo, int intensity, int[] tf_res, int total_bits, int balance, EntropyCoder ec, int LM, int codedBands, ref uint seed)
		{
			short[] eBands = m.eBands;
			int num = 1;
			int num2 = ((Y_ == null) ? 1 : 2);
			int num3 = ((encode == 0) ? 1 : 0);
			band_ctx band_ctx = new band_ctx();
			int num4 = 1 << LM;
			int num5 = ((shortBlocks == 0) ? 1 : num4);
			int num6 = num4 * eBands[start];
			int[] array = new int[num2 * (num4 * eBands[m.nbEBands - 1] - num6)];
			int num7 = num4 * eBands[m.nbEBands - 1] - num6;
			int[] lowband_scratch = X_;
			int lowband_scratch_ptr = num4 * eBands[m.nbEBands - 1];
			int num8 = 0;
			band_ctx.bandE = bandE;
			band_ctx.ec = ec;
			band_ctx.encode = encode;
			band_ctx.intensity = intensity;
			band_ctx.m = m;
			band_ctx.seed = seed;
			band_ctx.spread = spread;
			for (int i = start; i < end; i++)
			{
				int num9 = -1;
				int num10 = 0;
				int num11 = 0;
				band_ctx.i = i;
				int num12 = ((i == end - 1) ? 1 : 0);
				int[] x = X_;
				int x_ptr = num4 * eBands[i];
				int[] array2;
				if (Y_ != null)
				{
					array2 = Y_;
					num10 = num4 * eBands[i];
				}
				else
				{
					array2 = null;
				}
				int num13 = num4 * eBands[i + 1] - num4 * eBands[i];
				int num14 = (int)ec.tell_frac();
				if (i != start)
				{
					balance -= num14;
				}
				int num15 = (band_ctx.remaining_bits = total_bits - num14 - 1);
				int num17;
				if (i <= codedBands - 1)
				{
					int num16 = Inlines.celt_sudiv(balance, Inlines.IMIN(3, codedBands - i));
					num17 = Inlines.IMAX(0, Inlines.IMIN(16383, Inlines.IMIN(num15 + 1, pulses[i] + num16)));
				}
				else
				{
					num17 = 0;
				}
				if (num3 != 0 && num4 * eBands[i] - num13 >= num4 * eBands[start] && (num != 0 || num8 == 0))
				{
					num8 = i;
				}
				num11 = (band_ctx.tf_change = tf_res[i]);
				if (i >= m.effEBands)
				{
					x = array;
					x_ptr = 0;
					if (Y_ != null)
					{
						array2 = array;
						num10 = 0;
					}
					lowband_scratch = null;
				}
				if (i == end - 1)
				{
					lowband_scratch = null;
				}
				uint num21;
				uint num20;
				if (num8 != 0 && (spread != 3 || num5 > 1 || num11 < 0))
				{
					num9 = Inlines.IMAX(0, num4 * eBands[num8] - num6 - num13);
					int num18 = num8;
					while (num4 * eBands[--num18] > num9 + num6)
					{
					}
					int num19 = num8 - 1;
					while (num4 * eBands[++num19] < num9 + num6 + num13)
					{
					}
					num21 = (num20 = 0u);
					int num22 = num18;
					do
					{
						num21 |= collapse_masks[num22 * num2];
						num20 |= collapse_masks[num22 * num2 + num2 - 1];
					}
					while (++num22 < num19);
				}
				else
				{
					num21 = (num20 = (uint)((1 << num5) - 1));
				}
				if (dual_stereo != 0 && i == intensity)
				{
					dual_stereo = 0;
					if (num3 != 0)
					{
						for (int j = 0; j < num4 * eBands[i] - num6; j++)
						{
							array[j] = Inlines.HALF32(array[j] + array[num7 + j]);
						}
					}
				}
				if (dual_stereo != 0)
				{
					num21 = quant_band(band_ctx, x, x_ptr, num13, num17 / 2, num5, (num9 != -1) ? array : null, num9, LM, (num12 != 0) ? null : array, num4 * eBands[i] - num6, 32767, lowband_scratch, lowband_scratch_ptr, (int)num21);
					num20 = quant_band(band_ctx, array2, num10, num13, num17 / 2, num5, (num9 != -1) ? array : null, num7 + num9, LM, (num12 != 0) ? null : array, num7 + (num4 * eBands[i] - num6), 32767, lowband_scratch, lowband_scratch_ptr, (int)num20);
				}
				else
				{
					num21 = ((array2 == null) ? quant_band(band_ctx, x, x_ptr, num13, num17, num5, (num9 != -1) ? array : null, num9, LM, (num12 != 0) ? null : array, num4 * eBands[i] - num6, 32767, lowband_scratch, lowband_scratch_ptr, (int)(num21 | num20)) : quant_band_stereo(band_ctx, x, x_ptr, array2, num10, num13, num17, num5, (num9 != -1) ? array : null, num9, LM, (num12 != 0) ? null : array, num4 * eBands[i] - num6, lowband_scratch, lowband_scratch_ptr, (int)(num21 | num20)));
					num20 = num21;
				}
				collapse_masks[i * num2] = (byte)(num21 & 0xFFu);
				collapse_masks[i * num2 + num2 - 1] = (byte)(num20 & 0xFFu);
				balance += pulses[i] + num14;
				num = ((num17 > num13 << 3) ? 1 : 0);
			}
			seed = band_ctx.seed;
		}
	}
	internal class CeltCommon
	{
		private static readonly byte[] inv_table = new byte[128]
		{
			255, 255, 156, 110, 86, 70, 59, 51, 45, 40,
			37, 33, 31, 28, 26, 25, 23, 22, 21, 20,
			19, 18, 17, 16, 16, 15, 15, 14, 13, 13,
			12, 12, 12, 12, 11, 11, 11, 10, 10, 10,
			9, 9, 9, 9, 9, 9, 8, 8, 8, 8,
			8, 7, 7, 7, 7, 7, 7, 6, 6, 6,
			6, 6, 6, 6, 6, 6, 6, 6, 6, 6,
			6, 6, 6, 5, 5, 5, 5, 5, 5, 5,
			5, 5, 5, 5, 5, 4, 4, 4, 4, 4,
			4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
			4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
			3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
			3, 3, 3, 3, 3, 3, 3, 2
		};

		private static readonly short[][] gains = new short[3][]
		{
			new short[3] { 10048, 7112, 4248 },
			new short[3] { 15200, 8784, 0 },
			new short[3] { 26208, 3280, 0 }
		};

		private static readonly sbyte[][] tf_select_table = new sbyte[4][]
		{
			new sbyte[8] { 0, -1, 0, -1, 0, -1, 0, -1 },
			new sbyte[8] { 0, -1, 0, -2, 1, 0, 1, -1 },
			new sbyte[8] { 0, -2, 0, -3, 2, 0, 1, -1 },
			new sbyte[8] { 0, -2, 0, -3, 3, 0, 1, -1 }
		};

		internal static int compute_vbr(CeltMode mode, AnalysisInfo analysis, int base_target, int LM, int bitrate, int lastCodedBands, int C, int intensity, int constrained_vbr, int stereo_saving, int tot_boost, int tf_estimate, int pitch_change, int maxDepth, OpusFramesize variable_duration, int lfe, int has_surround_mask, int surround_masking, int temporal_vbr)
		{
			int nbEBands = mode.nbEBands;
			short[] eBands = mode.eBands;
			int num = ((lastCodedBands != 0) ? lastCodedBands : nbEBands);
			int num2 = eBands[num] << LM;
			if (C == 2)
			{
				num2 += eBands[Inlines.IMIN(intensity, num)] << LM;
			}
			int num3 = base_target;
			if (analysis.enabled && analysis.valid != 0 && (double)analysis.activity < 0.4)
			{
				num3 -= (int)((float)(num2 << 3) * (0.4f - analysis.activity));
			}
			if (C == 2)
			{
				int num4 = Inlines.IMIN(intensity, num);
				int num5 = (eBands[num4] << LM) - num4;
				int a = Inlines.DIV32_16(Inlines.MULT16_16(26214, num5), num2);
				stereo_saving = Inlines.MIN16(stereo_saving, 256);
				num3 -= Inlines.MIN32(Inlines.MULT16_32_Q15(a, num3), Inlines.SHR32(Inlines.MULT16_16(stereo_saving - 26, num5 << 3), 8));
			}
			num3 += tot_boost - (16 << LM);
			int num6 = ((variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE) ? 328 : 655);
			num3 += Inlines.SHL32(Inlines.MULT16_32_Q15(tf_estimate - num6, num3), 1);
			if (analysis.enabled && analysis.valid != 0 && lfe == 0)
			{
				float num7 = Inlines.MAX16(0f, analysis.tonality - 0.15f) - 0.09f;
				int num8 = num3 + (int)((float)(num2 << 3) * 1.2f * num7);
				if (pitch_change != 0)
				{
					num8 += (int)((float)(num2 << 3) * 0.8f);
				}
				num3 = num8;
			}
			if (has_surround_mask != 0 && lfe == 0)
			{
				int b = num3 + Inlines.SHR32(Inlines.MULT16_16(surround_masking, num2 << 3), 10);
				num3 = Inlines.IMAX(num3 / 4, b);
			}
			int num9 = eBands[nbEBands - 2] << LM;
			int a2 = Inlines.SHR32(Inlines.MULT16_16(C * num9 << 3, maxDepth), 10);
			a2 = Inlines.IMAX(a2, num3 >> 2);
			num3 = Inlines.IMIN(num3, a2);
			if ((has_surround_mask == 0 || lfe != 0) && (constrained_vbr != 0 || bitrate < 64000))
			{
				int a3 = Inlines.MAX16(0, bitrate - 32000);
				if (constrained_vbr != 0)
				{
					a3 = Inlines.MIN16(a3, 21955);
				}
				num3 = base_target + Inlines.MULT16_32_Q15(a3, num3 - base_target);
			}
			if (has_surround_mask == 0 && tf_estimate < 3277)
			{
				int b2 = Inlines.MULT16_16_Q15(3329, Inlines.IMAX(0, Inlines.IMIN(32000, 96000 - bitrate)));
				int a4 = Inlines.SHR32(Inlines.MULT16_16(temporal_vbr, b2), 10);
				num3 += Inlines.MULT16_32_Q15(a4, num3);
			}
			return Inlines.IMIN(2 * base_target, num3);
		}

		internal static int transient_analysis(int[][] input, int len, int C, out int tf_estimate, out int tf_chan)
		{
			int num = 0;
			int num2 = 0;
			tf_chan = 0;
			int[] array = new int[len];
			int num3 = len / 2;
			for (int i = 0; i < C; i++)
			{
				int num4 = 0;
				int a = 0;
				int num5 = 0;
				for (int j = 0; j < len; j++)
				{
					int num6 = Inlines.SHR32(input[i][j], 12);
					int num7 = Inlines.ADD32(a, num6);
					a = num5 + num7 - Inlines.SHL32(num6, 1);
					num5 = num6 - Inlines.SHR32(num7, 1);
					array[j] = Inlines.EXTRACT16(Inlines.SHR32(num7, 2));
				}
				Arrays.MemSet(array, 0, 12);
				int num8 = 0;
				num8 = 14 - Inlines.celt_ilog2(1 + Inlines.celt_maxabs32(array, 0, len));
				if (num8 != 0)
				{
					for (int j = 0; j < len; j++)
					{
						array[j] = Inlines.SHL16(array[j], num8);
					}
				}
				int num9 = 0;
				a = 0;
				for (int j = 0; j < num3; j++)
				{
					int num10 = Inlines.PSHR32(Inlines.MULT16_16(array[2 * j], array[2 * j]) + Inlines.MULT16_16(array[2 * j + 1], array[2 * j + 1]), 16);
					num9 += num10;
					array[j] = a + Inlines.PSHR32(num10 - a, 4);
					a = array[j];
				}
				a = 0;
				int a2 = 0;
				for (int j = num3 - 1; j >= 0; j--)
				{
					array[j] = a + Inlines.PSHR32(array[j] - a, 3);
					a = array[j];
					a2 = Inlines.MAX16(a2, a);
				}
				num9 = Inlines.MULT16_16(Inlines.celt_sqrt(num9), Inlines.celt_sqrt(Inlines.MULT16_16(a2, num3 >> 1)));
				int b = Inlines.SHL32(num3, 20) / Inlines.ADD32(1, Inlines.SHR32(num9, 1));
				num4 = 0;
				for (int j = 12; j < num3 - 5; j += 4)
				{
					int num11 = Inlines.MAX32(0, Inlines.MIN32(127, Inlines.MULT16_32_Q15(array[j] + 1, b)));
					num4 += inv_table[num11];
				}
				num4 = 64 * num4 * 4 / (6 * (num3 - 17));
				if (num4 > num2)
				{
					tf_chan = i;
					num2 = num4;
				}
			}
			num = ((num2 > 200) ? 1 : 0);
			int b2 = Inlines.MAX16(0, Inlines.celt_sqrt(27 * num2) - 42);
			tf_estimate = Inlines.celt_sqrt(Inlines.MAX32(0, Inlines.SHL32(Inlines.MULT16_16(113, Inlines.MIN16(163, b2)), 14) - 37312528));
			return num;
		}

		internal static int patch_transient_decision(int[][] newE, int[][] oldE, int nbEBands, int start, int end, int C)
		{
			int a = 0;
			int[] array = new int[26];
			if (C == 1)
			{
				array[start] = oldE[0][start];
				for (int i = start + 1; i < end; i++)
				{
					array[i] = Inlines.MAX16(array[i - 1] - 1024, oldE[0][i]);
				}
			}
			else
			{
				array[start] = Inlines.MAX16(oldE[0][start], oldE[1][start]);
				for (int i = start + 1; i < end; i++)
				{
					array[i] = Inlines.MAX16(array[i - 1] - 1024, Inlines.MAX16(oldE[0][i], oldE[1][i]));
				}
			}
			for (int i = end - 2; i >= start; i--)
			{
				array[i] = Inlines.MAX16(array[i], array[i + 1] - 1024);
			}
			int num = 0;
			do
			{
				for (int i = Inlines.IMAX(2, start); i < end - 1; i++)
				{
					int a2 = Inlines.MAX16(0, newE[num][i]);
					int b = Inlines.MAX16(0, array[i]);
					a = Inlines.ADD32(a, Inlines.MAX16(0, Inlines.SUB16(a2, b)));
				}
			}
			while (++num < C);
			a = Inlines.DIV32(a, C * (end - 1 - Inlines.IMAX(2, start)));
			return (a > 1024) ? 1 : 0;
		}

		internal static void compute_mdcts(CeltMode mode, int shortBlocks, int[][] input, int[][] output, int C, int CC, int LM, int upsample)
		{
			int overlap = mode.overlap;
			int num;
			int num2;
			int shift;
			if (shortBlocks != 0)
			{
				num = shortBlocks;
				num2 = mode.shortMdctSize;
				shift = mode.maxLM;
			}
			else
			{
				num = 1;
				num2 = mode.shortMdctSize << LM;
				shift = mode.maxLM - LM;
			}
			int num3 = 0;
			do
			{
				for (int i = 0; i < num; i++)
				{
					MDCT.clt_mdct_forward(mode.mdct, input[num3], i * num2, output[num3], i, mode.window, overlap, shift, num);
				}
			}
			while (++num3 < CC);
			if (CC == 2 && C == 1)
			{
				for (int j = 0; j < num * num2; j++)
				{
					output[0][j] = Inlines.ADD32(Inlines.HALF32(output[0][j]), Inlines.HALF32(output[1][j]));
				}
			}
			if (upsample == 1)
			{
				return;
			}
			num3 = 0;
			do
			{
				int num4 = num * num2 / upsample;
				for (int j = 0; j < num4; j++)
				{
					output[num3][j] *= upsample;
				}
				Arrays.MemSetWithOffset(output[num3], 0, num4, num * num2 - num4);
			}
			while (++num3 < C);
		}

		internal static void celt_preemphasis(short[] pcmp, int pcmp_ptr, int[] inp, int inp_ptr, int N, int CC, int upsample, int[] coef, ref int mem, int clip)
		{
			int a = coef[0];
			int num = mem;
			if (coef[1] == 0 && upsample == 1 && clip == 0)
			{
				for (int i = 0; i < N; i++)
				{
					int num2 = pcmp[pcmp_ptr + CC * i];
					inp[inp_ptr + i] = Inlines.SHL32(num2, 12) - num;
					num = Inlines.SHR32(Inlines.MULT16_16(a, num2), 3);
				}
				mem = num;
				return;
			}
			int num3 = N / upsample;
			if (upsample != 1)
			{
				Arrays.MemSetWithOffset(inp, 0, inp_ptr, N);
			}
			for (int i = 0; i < num3; i++)
			{
				inp[inp_ptr + i * upsample] = pcmp[pcmp_ptr + CC * i];
			}
			for (int i = 0; i < N; i++)
			{
				int num2 = inp[inp_ptr + i];
				inp[inp_ptr + i] = Inlines.SHL32(num2, 12) - num;
				num = Inlines.SHR32(Inlines.MULT16_16(a, num2), 3);
			}
			mem = num;
		}

		internal static void celt_preemphasis(short[] pcmp, int[] inp, int inp_ptr, int N, int CC, int upsample, int[] coef, BoxedValue<int> mem, int clip)
		{
			int a = coef[0];
			int num = mem.Val;
			if (coef[1] == 0 && upsample == 1 && clip == 0)
			{
				for (int i = 0; i < N; i++)
				{
					int num2 = pcmp[CC * i];
					inp[inp_ptr + i] = Inlines.SHL32(num2, 12) - num;
					num = Inlines.SHR32(Inlines.MULT16_16(a, num2), 3);
				}
				mem.Val = num;
				return;
			}
			int num3 = N / upsample;
			if (upsample != 1)
			{
				Arrays.MemSetWithOffset(inp, 0, inp_ptr, N);
			}
			for (int i = 0; i < num3; i++)
			{
				inp[inp_ptr + i * upsample] = pcmp[CC * i];
			}
			for (int i = 0; i < N; i++)
			{
				int num2 = inp[inp_ptr + i];
				inp[inp_ptr + i] = Inlines.SHL32(num2, 12) - num;
				num = Inlines.SHR32(Inlines.MULT16_16(a, num2), 3);
			}
			mem.Val = num;
		}

		internal static int l1_metric(int[] tmp, int N, int LM, int bias)
		{
			int num = 0;
			for (int i = 0; i < N; i++)
			{
				num += Inlines.EXTEND32(Inlines.ABS32(tmp[i]));
			}
			return Inlines.MAC16_32_Q15(num, LM * bias, num);
		}

		internal static int tf_analysis(CeltMode m, int len, int isTransient, int[] tf_res, int lambda, int[][] X, int N0, int LM, out int tf_sum, int tf_estimate, int tf_chan)
		{
			int[] array = new int[2];
			int num = 0;
			int bias = Inlines.MULT16_16_Q14(1311, Inlines.MAX16(-4096, 8192 - tf_estimate));
			int[] array2 = new int[len];
			int[] array3 = new int[m.eBands[len] - m.eBands[len - 1] << LM];
			int[] array4 = new int[m.eBands[len] - m.eBands[len - 1] << LM];
			int[] array5 = new int[len];
			int[] array6 = new int[len];
			tf_sum = 0;
			for (int i = 0; i < len; i++)
			{
				int num2 = 0;
				int num3 = m.eBands[i + 1] - m.eBands[i] << LM;
				int num4 = ((m.eBands[i + 1] - m.eBands[i] == 1) ? 1 : 0);
				Array.Copy(X[tf_chan], m.eBands[i] << LM, array3, 0, num3);
				int num5 = l1_metric(array3, num3, (isTransient != 0) ? LM : 0, bias);
				int num6 = num5;
				if (isTransient != 0 && num4 == 0)
				{
					Array.Copy(array3, 0, array4, 0, num3);
					Bands.haar1ZeroOffset(array4, num3 >> LM, 1 << LM);
					num5 = l1_metric(array4, num3, LM + 1, bias);
					if (num5 < num6)
					{
						num6 = num5;
						num2 = -1;
					}
				}
				for (int j = 0; j < LM + ((isTransient == 0 && num4 == 0) ? 1 : 0); j++)
				{
					int lM = ((isTransient == 0) ? (j + 1) : (LM - j - 1));
					Bands.haar1ZeroOffset(array3, num3 >> j, 1 << j);
					num5 = l1_metric(array3, num3, lM, bias);
					if (num5 < num6)
					{
						num6 = num5;
						num2 = j + 1;
					}
				}
				if (isTransient != 0)
				{
					array2[i] = 2 * num2;
				}
				else
				{
					array2[i] = -2 * num2;
				}
				tf_sum += ((isTransient != 0) ? LM : 0) - array2[i] / 2;
				if (num4 != 0 && (array2[i] == 0 || array2[i] == -2 * LM))
				{
					array2[i]--;
				}
			}
			num = 0;
			int num7;
			int num8;
			for (int k = 0; k < 2; k++)
			{
				num7 = 0;
				num8 = ((isTransient == 0) ? lambda : 0);
				for (int i = 1; i < len; i++)
				{
					int num9 = Inlines.IMIN(num7, num8 + lambda);
					int num10 = Inlines.IMIN(num7 + lambda, num8);
					num7 = num9 + Inlines.abs(array2[i] - 2 * Tables.tf_select_table[LM][4 * isTransient + 2 * k]);
					num8 = num10 + Inlines.abs(array2[i] - 2 * Tables.tf_select_table[LM][4 * isTransient + 2 * k + 1]);
				}
				num7 = Inlines.IMIN(num7, num8);
				array[k] = num7;
			}
			if (array[1] < array[0] && isTransient != 0)
			{
				num = 1;
			}
			num7 = 0;
			num8 = ((isTransient == 0) ? lambda : 0);
			for (int i = 1; i < len; i++)
			{
				int num11 = num7;
				int num12 = num8 + lambda;
				int num9;
				if (num11 < num12)
				{
					num9 = num11;
					array5[i] = 0;
				}
				else
				{
					num9 = num12;
					array5[i] = 1;
				}
				num11 = num7 + lambda;
				num12 = num8;
				int num10;
				if (num11 < num12)
				{
					num10 = num11;
					array6[i] = 0;
				}
				else
				{
					num10 = num12;
					array6[i] = 1;
				}
				num7 = num9 + Inlines.abs(array2[i] - 2 * Tables.tf_select_table[LM][4 * isTransient + 2 * num]);
				num8 = num10 + Inlines.abs(array2[i] - 2 * Tables.tf_select_table[LM][4 * isTransient + 2 * num + 1]);
			}
			tf_res[len - 1] = ((num7 >= num8) ? 1 : 0);
			for (int i = len - 2; i >= 0; i--)
			{
				if (tf_res[i + 1] == 1)
				{
					tf_res[i] = array6[i + 1];
				}
				else
				{
					tf_res[i] = array5[i + 1];
				}
			}
			return num;
		}

		internal static void tf_encode(int start, int end, int isTransient, int[] tf_res, int LM, int tf_select, EntropyCoder enc)
		{
			uint num = enc.storage * 8;
			uint num2 = (uint)enc.tell();
			int num3 = ((isTransient != 0) ? 2 : 4);
			int num4 = ((LM > 0 && num2 + num3 + 1 <= num) ? 1 : 0);
			num -= (uint)num4;
			int num5;
			int num6 = (num5 = 0);
			for (int i = start; i < end; i++)
			{
				if (num2 + num3 <= num)
				{
					enc.enc_bit_logp(tf_res[i] ^ num6, (uint)num3);
					num2 = (uint)enc.tell();
					num6 = tf_res[i];
					num5 |= num6;
				}
				else
				{
					tf_res[i] = num6;
				}
				num3 = ((isTransient != 0) ? 4 : 5);
			}
			if (num4 != 0 && Tables.tf_select_table[LM][4 * isTransient + num5] != Tables.tf_select_table[LM][4 * isTransient + 2 + num5])
			{
				enc.enc_bit_logp(tf_select, 1u);
			}
			else
			{
				tf_select = 0;
			}
			for (int i = start; i < end; i++)
			{
				tf_res[i] = Tables.tf_select_table[LM][4 * isTransient + 2 * tf_select + tf_res[i]];
			}
		}

		internal static int alloc_trim_analysis(CeltMode m, int[][] X, int[][] bandLogE, int end, int LM, int C, AnalysisInfo analysis, ref int stereo_saving, int tf_estimate, int intensity, int surround_trim)
		{
			int num = 0;
			int num2 = 1280;
			if (C == 2)
			{
				int num3 = 0;
				for (int i = 0; i < 8; i++)
				{
					int a = Kernels.celt_inner_prod(X[0], m.eBands[i] << LM, X[1], m.eBands[i] << LM, m.eBands[i + 1] - m.eBands[i] << LM);
					num3 = Inlines.ADD16(num3, Inlines.EXTRACT16(Inlines.SHR32(a, 18)));
				}
				num3 = Inlines.MULT16_16_Q15(4096, num3);
				num3 = Inlines.MIN16(1024, Inlines.ABS32(num3));
				int num4 = num3;
				for (int i = 8; i < intensity; i++)
				{
					int a = Kernels.celt_inner_prod(X[0], m.eBands[i] << LM, X[1], m.eBands[i] << LM, m.eBands[i + 1] - m.eBands[i] << LM);
					num4 = Inlines.MIN16(num4, Inlines.ABS16(Inlines.EXTRACT16(Inlines.SHR32(a, 18))));
				}
				num4 = Inlines.MIN16(1024, Inlines.ABS32(num4));
				int num5 = Inlines.celt_log2(1049625 - Inlines.MULT16_16(num3, num3));
				int num6 = Inlines.MAX16(Inlines.HALF16(num5), Inlines.celt_log2(1049625 - Inlines.MULT16_16(num4, num4)));
				num5 = Inlines.PSHR32(num5 - 6144, 2);
				num6 = Inlines.PSHR32(num6 - 6144, 2);
				num2 += Inlines.MAX16(-1024, Inlines.MULT16_16_Q15(24576, num5));
				stereo_saving = Inlines.MIN16(stereo_saving + 64, -Inlines.HALF16(num6));
			}
			int num7 = 0;
			do
			{
				for (int i = 0; i < end - 1; i++)
				{
					num += bandLogE[num7][i] * (2 + 2 * i - end);
				}
			}
			while (++num7 < C);
			num /= C * (end - 1);
			num2 -= Inlines.MAX16(Inlines.NEG16((short)512), Inlines.MIN16(512, Inlines.SHR16(num + 1024, 2) / 6));
			num2 -= Inlines.SHR16(surround_trim, 2);
			num2 -= 2 * Inlines.SHR16(tf_estimate, 6);
			if (analysis.enabled && analysis.valid != 0)
			{
				num2 -= Inlines.MAX16(-512, Inlines.MIN16(512, (int)(512f * (analysis.tonality_slope + 0.05f))));
			}
			int b = Inlines.PSHR32(num2, 8);
			return Inlines.IMAX(0, Inlines.IMIN(10, b));
		}

		internal static int stereo_analysis(CeltMode m, int[][] X, int LM)
		{
			int num = 1;
			int num2 = 1;
			for (int i = 0; i < 13; i++)
			{
				for (int j = m.eBands[i] << LM; j < m.eBands[i + 1] << LM; j++)
				{
					int num3 = Inlines.EXTEND32(X[0][j]);
					int num4 = Inlines.EXTEND32(X[1][j]);
					int x = Inlines.ADD32(num3, num4);
					int x2 = Inlines.SUB32(num3, num4);
					num = Inlines.ADD32(num, Inlines.ADD32(Inlines.ABS32(num3), Inlines.ABS32(num4)));
					num2 = Inlines.ADD32(num2, Inlines.ADD32(Inlines.ABS32(x), Inlines.ABS32(x2)));
				}
			}
			num2 = Inlines.MULT16_32_Q15((short)23170, num2);
			int num5 = 13;
			if (LM <= 1)
			{
				num5 -= 8;
			}
			return (Inlines.MULT16_32_Q15((m.eBands[13] << LM + 1) + num5, num2) > Inlines.MULT16_32_Q15(m.eBands[13] << LM + 1, num)) ? 1 : 0;
		}

		internal static int median_of_5(int[] x, int x_ptr)
		{
			int num = x[x_ptr + 2];
			int num3;
			int num2;
			if (x[x_ptr] > x[x_ptr + 1])
			{
				num2 = x[x_ptr + 1];
				num3 = x[x_ptr];
			}