Decompiled source of ValheimVOIP v0.1.1

ValheimVoip.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
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 ValheimVoip
{
	internal sealed class VoiceCapture : MonoBehaviour
	{
		private const int MicrophoneBufferSeconds = 1;

		private VoiceNetwork _network;

		private AudioClip _microphoneClip;

		private string _device;

		private int _lastPosition;

		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())
			{
				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()
		{
			if ((!((Object)(object)_microphoneClip != (Object)null) || !Microphone.IsRecording(_device)) && Microphone.devices.Length != 0)
			{
				_device = Microphone.devices[0];
				int sampleRate = VoiceRuntimeSettings.SampleRate;
				_microphoneClip = Microphone.Start(_device, true, 1, sampleRate);
				_lastPosition = 0;
				int num = sampleRate * VoiceRuntimeSettings.FrameMilliseconds / 1000;
				_frameBuffer = new float[num];
				_scratch = new float[_microphoneClip.samples];
			}
		}

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

		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)
			{
				_microphoneClip.GetData(destination, startPosition);
				return;
			}
			_microphoneClip.GetData(_scratch, 0);
			for (int i = 0; i < destination.Length; i++)
			{
				destination[i] = _scratch[(startPosition + i) % _microphoneClip.samples];
			}
		}

		private void TrySendFrame(float[] frame)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: 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.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);
				}
			}
		}
	}
	internal sealed class VoiceClient
	{
		public void Send(VoicePacket packet)
		{
			if (ZRoutedRpc.instance == null || (Object)(object)ZNet.instance == (Object)null)
			{
				return;
			}
			if (ZNet.instance.IsServer())
			{
				VoiceServer instance = VoiceServer.Instance;
				if ((Object)(object)instance != (Object)null)
				{
					instance.Relay(ZNet.GetUID(), packet);
				}
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(serverPeer.m_uid, "ValheimVoip_VoiceFrame", new object[1] { packet.ToPackage() });
			}
		}

		public void ApplyServerSettings(long senderPeerId, ZPackage package)
		{
			if ((Object)(object)ZNet.instance == (Object)null || ZNet.instance.IsServer())
			{
				return;
			}
			ZNetPeer serverPeer = ZNet.instance.GetServerPeer();
			if (serverPeer == null || serverPeer.m_uid != senderPeerId)
			{
				VoiceLog.WarningRateLimited("voice-settings-unauthorized", "Ignored voice settings package from non-server peer " + senderPeerId + ".", 30f);
				return;
			}
			try
			{
				if (VoiceRuntimeSettings.ApplyServerPackage(package, out var summary))
				{
					ValheimVoipPlugin.Log.LogInfo((object)("Applied server voice settings: " + summary));
				}
				else
				{
					VoiceLog.InfoRateLimited("voice-settings-unchanged", "Received server voice settings: " + summary, 60f);
				}
			}
			catch (Exception ex)
			{
				VoiceLog.WarningRateLimited("voice-settings-malformed", "Dropped malformed voice settings package from server peer " + senderPeerId + ": " + ex.Message, 10f);
			}
		}

		public void OnRpcUnavailable()
		{
			VoiceRuntimeSettings.ClearServerSettings();
		}
	}
	internal sealed class VoicePlayback : MonoBehaviour
	{
		private sealed class SpeakerPlayback
		{
			private readonly object _lock = new object();

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

			private readonly GameObject _sourceObject;

			private AudioClip _clip;

			private int _bufferedSamples;

			private int _sampleRate;

			private int _underflowEvents;

			private int _droppedSamples;

			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_00c2: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
				//IL_00d8: Expected O, but got Unknown
				//IL_00d8: Expected O, but got Unknown
				sampleRate = Mathf.Max(8000, sampleRate);
				if (!((Object)(object)_clip != (Object)null) || _sampleRate != sampleRate)
				{
					_sampleRate = sampleRate;
					lock (_lock)
					{
						_samples.Clear();
						_bufferedSamples = 0;
					}
					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 Enqueue(float[] decodedSamples)
			{
				if (decodedSamples == null || decodedSamples.Length == 0)
				{
					return;
				}
				int num = Mathf.Max(1, _sampleRate * VoiceSettings.EffectiveMaxJitterBufferMilliseconds / 1000);
				int num2 = 0;
				lock (_lock)
				{
					foreach (float item in decodedSamples)
					{
						_samples.Enqueue(item);
					}
					_bufferedSamples += decodedSamples.Length;
					while (_bufferedSamples > num && _samples.Count > 0)
					{
						_samples.Dequeue();
						_bufferedSamples--;
						num2++;
					}
					_droppedSamples += num2;
				}
			}

			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;
					lock (_lock)
					{
						underflowEvents = _underflowEvents;
						droppedSamples = _droppedSamples;
						_underflowEvents = 0;
						_droppedSamples = 0;
					}
					_nextBufferLog = Time.time + 10f;
					if (underflowEvents > 0)
					{
						ValheimVoipPlugin.Log.LogWarning((object)("Voice jitter buffer underflow for " + SpeakerId + " (" + underflowEvents + " silent samples in the last window)."));
					}
					if (droppedSamples > 0)
					{
						ValheimVoipPlugin.Log.LogWarning((object)("Voice jitter buffer dropped " + droppedSamples + " old samples for " + SpeakerId + " to stay within the max buffer."));
					}
				}
			}

			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 (_samples.Count > 0)
						{
							data[i] = _samples.Dequeue();
							_bufferedSamples--;
						}
						else
						{
							data[i] = 0f;
							_underflowEvents++;
						}
					}
				}
			}

			private void OnAudioSetPosition(int position)
			{
			}
		}

		private const float SpeakerIdleDestroySeconds = 10f;

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

		public void Play(VoicePacket packet)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (packet.OpusPayload == null || packet.OpusPayload.Length == 0 || packet.Samples <= 0)
			{
				return;
			}
			SpeakerPlayback speaker = GetSpeaker(packet.SpeakerId, packet.SampleRate);
			speaker.UpdateSource(packet.SpeakerPosition);
			try
			{
				speaker.Enqueue(speaker.Codec.Decode(packet.OpusPayload, packet.Samples, packet.SampleRate));
			}
			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 VoiceServer : MonoBehaviour
	{
		private const float SettingsBroadcastInterval = 10f;

		private float _nextSettingsBroadcast;

		internal static VoiceServer Instance { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

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

		private void Update()
		{
			if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer() && Time.time >= _nextSettingsBroadcast)
			{
				BroadcastServerSettings();
				_nextSettingsBroadcast = Time.time + 10f;
			}
		}

		public void Relay(long senderPeerId, VoicePacket packet)
		{
			//IL_0069: 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)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)ZNet.instance == (Object)null || ZRoutedRpc.instance == null)
			{
				return;
			}
			float proximityMeters = VoiceRuntimeSettings.ProximityMeters;
			float num = proximityMeters * proximityMeters;
			foreach (ZNetPeer connectedPeer in ZNet.instance.GetConnectedPeers())
			{
				if (connectedPeer != null && connectedPeer.m_uid != senderPeerId)
				{
					Vector3 val = connectedPeer.GetRefPos() - packet.SpeakerPosition;
					if (!(((Vector3)(ref val)).sqrMagnitude > num))
					{
						ZRoutedRpc.instance.InvokeRoutedRPC(connectedPeer.m_uid, "ValheimVoip_VoiceFrame", new object[1] { packet.ToPackage() });
					}
				}
			}
		}

		private static void BroadcastServerSettings()
		{
			if (ZRoutedRpc.instance != null)
			{
				ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "ValheimVoip_Settings", new object[1] { VoiceRuntimeSettings.CreateServerPackage() });
				VoiceLog.InfoRateLimited("voice-settings-broadcast", "Broadcasting server voice settings: " + VoiceRuntimeSettings.CreateServerSummary(), 60f);
			}
		}
	}
	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];
			}
		}
	}
	[BepInPlugin("de.valheim.voip", "Valheim VOIP", "0.1.0")]
	public sealed class ValheimVoipPlugin : BaseUnityPlugin
	{
		public const string ModGuid = "de.valheim.voip";

		public const string ModName = "Valheim VOIP";

		public const string ModVersion = "0.1.0";

		private GameObject _runnerObject;

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			VoiceSettings.Bind(((BaseUnityPlugin)this).Config);
			_runnerObject = new GameObject("Valheim VOIP");
			Object.DontDestroyOnLoad((Object)(object)_runnerObject);
			VoiceNetwork voiceNetwork = _runnerObject.AddComponent<VoiceNetwork>();
			VoiceServer server = _runnerObject.AddComponent<VoiceServer>();
			VoiceClient client = new VoiceClient();
			VoicePlayback playback = _runnerObject.AddComponent<VoicePlayback>();
			VoiceCapture voiceCapture = _runnerObject.AddComponent<VoiceCapture>();
			voiceNetwork.Initialize(client, server, playback);
			voiceCapture.Initialize(voiceNetwork);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Valheim VOIP 0.1.0 loaded");
		}

		private void OnDestroy()
		{
			if ((Object)(object)_runnerObject != (Object)null)
			{
				Object.Destroy((Object)(object)_runnerObject);
			}
		}
	}
	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))
			{
				ValheimVoipPlugin.Log.LogInfo((object)message);
			}
		}

		public static void WarningRateLimited(string key, string message, float intervalSeconds)
		{
			if (ShouldLog(key, intervalSeconds))
			{
				ValheimVoipPlugin.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 = "ValheimVoip_VoiceFrame";

		internal const string SettingsRpcName = "ValheimVoip_Settings";

		private VoiceClient _client;

		private VoiceServer _server;

		private VoicePlayback _playback;

		private bool _registered;

		private ZRoutedRpc _registeredRpc;

		public void Initialize(VoiceClient client, VoiceServer server, VoicePlayback playback)
		{
			_client = client;
			_server = server;
			_playback = playback;
		}

		private void Update()
		{
			if (ZRoutedRpc.instance == null)
			{
				_registered = false;
				_registeredRpc = null;
				if (_client != null)
				{
					_client.OnRpcUnavailable();
				}
			}
			else if (!_registered || _registeredRpc != ZRoutedRpc.instance)
			{
				ZRoutedRpc.instance.Register<ZPackage>("ValheimVoip_VoiceFrame", (Action<long, ZPackage>)OnVoiceFrame);
				ZRoutedRpc.instance.Register<ZPackage>("ValheimVoip_Settings", (Action<long, ZPackage>)OnSettings);
				_registered = true;
				_registeredRpc = ZRoutedRpc.instance;
				ValheimVoipPlugin.Log.LogInfo((object)"Voice RPC registered");
			}
		}

		public void Send(VoicePacket packet)
		{
			_client.Send(packet);
		}

		private void OnVoiceFrame(long senderPeerId, ZPackage package)
		{
			if (!VoiceRuntimeSettings.Enabled)
			{
				return;
			}
			VoicePacket voicePacket;
			try
			{
				voicePacket = VoicePacket.FromPackage(package);
			}
			catch (Exception ex)
			{
				VoiceLog.WarningRateLimited("voice-packet-malformed-" + senderPeerId, "Dropped malformed voice packet from peer " + senderPeerId + ": " + ex.Message, 5f);
				return;
			}
			if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
			{
				_server.Relay(senderPeerId, voicePacket);
				if ((Object)(object)Player.m_localPlayer != (Object)null && voicePacket.SpeakerId != Player.m_localPlayer.GetPlayerID())
				{
					_playback.Play(voicePacket);
				}
			}
			else if (!((Object)(object)Player.m_localPlayer != (Object)null) || voicePacket.SpeakerId != Player.m_localPlayer.GetPlayerID())
			{
				_playback.Play(voicePacket);
			}
		}

		private void OnSettings(long senderPeerId, ZPackage package)
		{
			_client.ApplyServerSettings(senderPeerId, package);
		}
	}
	internal sealed class VoicePacket
	{
		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
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			ZPackage val = new ZPackage();
			val.Write(SpeakerId);
			val.Write(SpeakerPosition);
			val.Write(SampleRate);
			val.Write(Samples);
			val.Write(OpusPayload);
			return val;
		}

		public static VoicePacket FromPackage(ZPackage package)
		{
			//IL_0015: 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)
			VoicePacket voicePacket = new VoicePacket();
			voicePacket.SpeakerId = package.ReadLong();
			voicePacket.SpeakerPosition = package.ReadVector3();
			voicePacket.SampleRate = package.ReadInt();
			voicePacket.Samples = package.ReadInt();
			voicePacket.OpusPayload = package.ReadByteArray();
			return voicePacket;
		}
	}
	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 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();
			val.Write(1);
			val.Write(VoiceSettings.Enabled.Value);
			val.Write(VoiceSettings.ProximityMeters.Value);
			val.Write(VoiceSettings.FullVolumeMeters.Value);
			val.Write(VoiceSettings.EffectiveSampleRate);
			val.Write(VoiceSettings.EffectiveFrameMilliseconds);
			val.Write(VoiceSettings.EffectiveOpusBitrate);
			val.Write(VoiceSettings.EffectiveOpusComplexity);
			return val;
		}

		public static bool ApplyServerPackage(ZPackage package, out string summary)
		{
			summary = string.Empty;
			int num = package.ReadInt();
			if (num != 1)
			{
				VoiceLog.WarningRateLimited("voice-settings-version", "Ignored unsupported voice settings package version " + num + ".", 30f);
				return false;
			}
			_enabled = package.ReadBool();
			_proximityMeters = Mathf.Max(1f, package.ReadSingle());
			_fullVolumeMeters = Mathf.Clamp(package.ReadSingle(), 0.1f, _proximityMeters);
			_sampleRate = SanitizeSampleRate(package.ReadInt());
			_frameMilliseconds = SanitizeFrameMilliseconds(package.ReadInt());
			_opusBitrate = Mathf.Clamp(package.ReadInt(), 6000, 128000);
			_opusComplexity = Mathf.Clamp(package.ReadInt(), 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 static class VoiceSettings
	{
		public static ConfigEntry<bool> Enabled { get; private set; }

		public static ConfigEntry<string> PushToTalkKey { 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
		{
			get
			{
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				KeyCode result;
				return (KeyCode)((!Enum.TryParse<KeyCode>(PushToTalkKey.Value, ignoreCase: true, out result)) ? 118 : ((int)result));
			}
		}

		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 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.");
			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.");
		}
	}
}
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];
			}
			else
			{
				num2 = x[x_ptr];
				num3 = x[x_ptr + 1];
			}
			int num4;
			int num5;
			if (x[x_ptr + 3] > x[x_ptr + 4])
			{
				num4 = x[x_ptr + 4];
				num5 = x[x_ptr + 3];
			}
			else
			{
				num4 = x[x_ptr + 3];
				num5 = x[x_ptr + 4];
			}
			if (num2 > num4)
			{
				int num6 = num4;
				num4 = num2;
				num2 = num6;
				num6 = num5;
				num5 = num3;
				num3 = num6;
			}
			if (num > num3)
			{
				if (num3 < num4)
				{
					return Inlines.MIN16(num, num4);
				}
				return Inlines.MIN16(num5, num3);
			}
			if (num < num4)
			{
				return Inlines.MIN16(num3, num4);
			}
			return Inlines.MIN16(num, num5);
		}

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

		internal static int dynalloc_analysis(int[][] bandLogE, int[][] bandLogE2, int nbEBands, int start, int end, int C, int[] offsets, int lsb_depth, short[] logN, int isTransient, int vbr, int constrained_vbr, short[] eBands, int LM, int effectiveBytes, out int tot_boost_, int lfe, int[] surround_dynalloc)
		{
			int num = 0;
			int[][] array = Arrays.InitTwoDimensionalArray<int>(2, nbEBands);
			int[] array2 = new int[C * nbEBands];
			Arrays.MemSet(offsets, 0, nbEBands);
			int num2 = -32666;
			for (int i = 0; i < end; i++)
			{
				array2[i] = Inlines.MULT16_16((short)64, logN[i]) + 512 + Inlines.SHL16(9 - lsb_depth, 10) - Inlines.SHL16(Tables.eMeans[i], 6) + Inlines.MULT16_16(6, (i + 5) * (i + 5));
			}
			int num3 = 0;
			do
			{
				for (int i = 0; i < end; i++)
				{
					num2 = Inlines.MAX16(num2, bandLogE[num3][i] - array2[i]);
				}
			}
			while (++num3 < C);
			if (effectiveBytes > 50 && LM >= 1 && lfe == 0)
			{
				int num4 = 0;
				num3 = 0;
				do
				{
					int[] array3 = array[num3];
					array3[0] = bandLogE2[num3][0];
					for (int i = 1; i < end; i++)
					{
						if (bandLogE2[num3][i] > bandLogE2[num3][i - 1] + 512)
						{
							num4 = i;
						}
						array3[i] = Inlines.MIN16(array3[i - 1] + 1536, bandLogE2[num3][i]);
					}
					for (int i = num4 - 1; i >= 0; i--)
					{
						array3[i] = Inlines.MIN16(array3[i], Inlines.MIN16(array3[i + 1] + 2048, bandLogE2[num3][i]));
					}
					int num5 = 1024;
					for (int i = 2; i < end - 2; i++)
					{
						array3[i] = Inlines.MAX16(array3[i], median_of_5(bandLogE2[num3], i - 2) - num5);
					}
					int b = median_of_3(bandLogE2[num3], 0) - num5;
					array3[0] = Inlines.MAX16(array3[0], b);
					array3[1] = Inlines.MAX16(array3[1], b);
					b = median_of_3(bandLogE2[num3], end - 3) - num5;
					array3[end - 2] = Inlines.MAX16(array3[end - 2], b);
					array3[end - 1] = Inlines.MAX16(array3[end - 1], b);
					for (int i = 0; i < end; i++)
					{
						array3[i] = Inlines.MAX16(array3[i], array2[i]);
					}
				}
				while (++num3 < C);
				if (C == 2)
				{
					for (int i = start; i < end; i++)
					{
						array[1][i] = Inlines.MAX16(array[1][i], array[0][i] - 4096);
						array[0][i] = Inlines.MAX16(array[0][i], array[1][i] - 4096);
						array[0][i] = Inlines.HALF16(Inlines.MAX16(0, bandLogE[0][i] - array[0][i]) + Inlines.MAX16(0, bandLogE[1][i] - array[1][i]));
					}
				}
				else
				{
					for (int i = start; i < end; i++)
					{
						array[0][i] = Inlines.MAX16(0, bandLogE[0][i] - array[0][i]);
					}
				}
				for (int i = start; i < end; i++)
				{
					array[0][i] = Inlines.MAX16(array[0][i], surround_dynalloc[i]);
				}
				if ((vbr == 0 || constrained_vbr != 0) && isTransient == 0)
				{
					for (int i = start; i < end; i++)
					{
						array[0][i] = Inlines.HALF16(array[0][i]);
					}
				}
				for (int i = start; i < end; i++)
				{
					if (i < 8)
					{
						array[0][i] *= 2;
					}
					if (i >= 12)
					{
						array[0][i] = Inlines.HALF16(array[0][i]);
					}
					array[0][i] = Inlines.MIN16(array[0][i], 4096);
					int num6 = C * (eBands[i + 1] - eBands[i]) << LM;
					int num7;
					int num8;
					if (num6 < 6)
					{
						num7 = Inlines.SHR32(array[0][i], 10);
						num8 = num7 * num6 << 3;
					}
					else if (num6 > 48)
					{
						num7 = Inlines.SHR32(array[0][i] * 8, 10);
						num8 = (num7 * num6 << 3) / 8;
					}
					else
					{
						num7 = Inlines.SHR32(array[0][i] * num6 / 6, 10);
						num8 = num7 * 6 << 3;
					}
					if ((vbr == 0 || (constrained_vbr != 0 && isTransient == 0)) && num + num8 >> 3 >> 3 > effectiveBytes / 4)
					{
						int num9 = effectiveBytes / 4 << 3 << 3;
						offsets[i] = num9 - num;
						num = num9;
						break;
					}
					offsets[i] = num7;
					num += num8;
				}
			}
			tot_boost_ = num;
			return num2;
		}

		internal static void deemphasis(int[][] input, int[] input_ptrs, short[] pcm, int pcm_ptr, int N, int C, int downsample, int[] coef, int[] mem, int accum)
		{
			int num = 0;
			if (downsample == 1 && C == 2 && accum == 0)
			{
				deemphasis_stereo_simple(input, input_ptrs, pcm, pcm_ptr, N, coef[0], mem);
				return;
			}
			int[] array = new int[N];
			int a = coef[0];
			int num2 = N / downsample;
			int num3 = 0;
			do
			{
				int num4 = mem[num3];
				int[] array2 = input[num3];
				int num5 = input_ptrs[num3];
				int num6 = pcm_ptr + num3;
				if (downsample > 1)
				{
					for (int i = 0; i < N; i++)
					{
						int num7 = array2[num5 + i] + num4;
						num4 = Inlines.MULT16_32_Q15(a, num7);
						array[i] = num7;
					}
					num = 1;
				}
				else if (accum != 0)
				{
					for (int i = 0; i < N; i++)
					{
						int num7 = array2[num5 + i] + num4;
						num4 = Inlines.MULT16_32_Q15(a, num7);
						pcm[num6 + i * C] = Inlines.SAT16(Inlines.ADD32(pcm[num6 + i * C], Inlines.SIG2WORD16(num7)));
					}
				}
				else
				{
					for (int i = 0; i < N; i++)
					{
						int num7 = array2[num5 + i] + num4;
						if (array2[num5 + i] > 0 && num4 > 0 && num7 < 0)
						{
							num7 = int.MaxValue;
							num4 = int.MaxValue;
						}
						else
						{
							num4 = Inlines.MULT16_32_Q15(a, num7);
						}
						pcm[num6 + i * C] = Inlines.SIG2WORD16(num7);
					}
				}
				mem[num3] = num4;
				if (num != 0)
				{
					for (int i = 0; i < num2; i++)
					{
						pcm[num6 + i * C] = Inlines.SIG2WORD16(array[i * downsample]);
					}
				}
			}
			while (++num3 < C);
		}

		internal static void deemphasis_stereo_simple(int[][] input, int[] input_ptrs, short[] pcm, int pcm_ptr, int N, int coef0, int[] mem)
		{
			int[] array = input[0];
			int[] array2 = input[1];
			int num = input_ptrs[0];
			int num2 = input_ptrs[1];
			int num3 = mem[0];
			int num4 = mem[1];
			for (int i = 0; i < N; i++)
			{
				int num5 = array[num + i] + num3;
				int num6 = array2[num2 + i] + num4;
				num3 = Inlines.MULT16_32_Q15(coef0, num5);
				num4 = Inlines.MULT16_32_Q15(coef0, num6);
				pcm[pcm_ptr + 2 * i] = Inlines.SIG2WORD16(num5);
				pcm[pcm_ptr + 2 * i + 1] = Inlines.SIG2WORD16(num6);
			}
			mem[0] = num3;
			mem[1] = num4;
		}

		internal static void celt_synthesis(CeltMode mode, int[][] X, int[][] out_syn, int[] out_syn_ptrs, int[] oldBandE, int start, int effEnd, int C, int CC, int isTransient, int LM, int downsample, int silence)
		{
			int overlap = mode.overlap;
			int nbEBands = mode.nbEBands;
			int num = mode.shortMdctSize << LM;
			int[] array = new int[num];
			int num2 = 1 << LM;
			int num3;
			int num4;
			int shift;
			if (isTransient != 0)
			{
				num3 = num2;
				num4 = mode.shortMdctSize;
				shift = mode.maxLM;
			}
			else
			{
				num3 = 1;
				num4 = mode.shortMdctSize << LM;
				shift = mode.maxLM - LM;
			}
			if (CC == 2 && C == 1)
			{
				Bands.denormalise_bands(mode, X[0], array, 0, oldBandE, 0, start, effEnd, num2, downsample, silence);
				int num5 = out_syn_ptrs[1] + overlap / 2;
				Array.Copy(array, 0, out_syn[1], num5, num);
				for (int i = 0; i < num3; i++)
				{
					MDCT.clt_mdct_backward(mode.mdct, out_syn[1], num5 + i, out_syn[0], out_syn_ptrs[0] + num4 * i, mode.window, overlap, shift, num3);
				}
				for (int i = 0; i < num3; i++)
				{
					MDCT.clt_mdct_backward(mode.mdct, array, i, out_syn[1], out_syn_ptrs[1] + num4 * i, mode.window, overlap, shift, num3);
				}
				return;
			}
			if (CC == 1 && C == 2)
			{
				int num5 = out_syn_ptrs[0] + overlap / 2;
				Bands.denormalise_bands(mode, X[0], array, 0, oldBandE, 0, start, effEnd, num2, downsample, silence);
				Bands.denormalise_bands(mode, X[1], out_syn[0], num5, oldBandE, nbEBands, start, effEnd, num2, downsample, silence);
				for (int j = 0; j < num; j++)
				{
					array[j] = Inlines.HALF32(Inlines.ADD32(array[j], out_syn[0][num5 + j]));
				}
				for (int i = 0; i < num3; i++)
				{
					MDCT.clt_mdct_backward(mode.mdct, array, i, out_syn[0], out_syn_ptrs[0] + num4 * i, mode.window, overlap, shift, num3);
				}
				return;
			}
			int num6 = 0;
			do
			{
				Bands.denormalise_bands(mode, X[num6], array, 0, oldBandE, num6 * nbEBands, start, effEnd, num2, downsample, silence);
				for (int i = 0; i < num3; i++)
				{
					MDCT.clt_mdct_backward(mode.mdct, array, i, out_syn[num6], out_syn_ptrs[num6] + num4 * i, mode.window, overlap, shift, num3);
				}
			}
			while (++num6 < CC);
		}

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

		internal static int celt_plc_pitch_search(int[][] decode_mem, int C)
		{
			int[] array = new int[1024];
			Pitch.pitch_downsample(decode_mem, array, 2048, C);
			Pitch.pitch_search(array, 360, array, 1328, 620, out var pitch);
			return 720 - pitch;
		}

		internal static int resampling_factor(int rate)
		{
			return rate switch
			{
				48000 => 1, 
				24000 => 2, 
				16000 => 3, 
				12000 => 4, 
				8000 => 6, 
				_ => 0, 
			};
		}

		internal static void comb_filter_const(int[] y, int y_ptr, int[] x, int x_ptr, int T, int N, int g10, int g11, int g12)
		{
			int num = x_ptr - T;
			int b = x[num - 2];
			int num2 = x[num - 1];
			int num3 = x[num];
			int num4 = x[num + 1];
			for (int i = 0; i < N; i++)
			{
				int num5 = x[num + i + 2];
				y[y_ptr + i] = x[x_ptr + i] + Inlines.MULT16_32_Q15(g10, num3) + Inlines.MULT16_32_Q15(g11, Inlines.ADD32(num4, num2)) + Inlines.MULT16_32_Q15(g12, Inlines.ADD32(num5, b));
				b = num2;
				num2 = num3;
				num3 = num4;
				num4 = num5;
			}
		}

		internal static void comb_filter(int[] y, int y_ptr, int[] x, int x_ptr, int T0, int T1, int N, int g0, int g1, int tapset0, int tapset1, int[] window, int overlap)
		{
			if (g0 == 0 && g1 == 0)
			{
				if (x_ptr == y_ptr)
				{
				}
				return;
			}
			int b = Inlines.MULT16_16_P15(g0, gains[tapset0][0]);
			int b2 = Inlines.MULT16_16_P15(g0, gains[tapset0][1]);
			int b3 = Inlines.MULT16_16_P15(g0, gains[tapset0][2]);
			int num = Inlines.MULT16_16_P15(g1, gains[tapset1][0]);
			int num2 = Inlines.MULT16_16_P15(g1, gains[tapset1][1]);
			int num3 = Inlines.MULT16_16_P15(g1, gains[tapset1][2]);
			int num4 = x[x_ptr - T1 + 1];
			int num5 = x[x_ptr - T1];
			int num6 = x[x_ptr - T1 - 1];
			int b4 = x[x_ptr - T1 - 2];
			if (g0 == g1 && T0 == T1 && tapset0 == tapset1)
			{
				overlap = 0;
			}
			int i;
			for (i = 0; i < overlap; i++)
			{
				int num7 = x[x_ptr + i - T1 + 2];
				int num8 = Inlines.MULT16_16_Q15(window[i], window[i]);
				y[y_ptr + i] = x[x_ptr + i] + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((short)(32767 - num8), b), x[x_ptr + i - T0]) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((short)(32767 - num8), b2), Inlines.ADD32(x[x_ptr + i - T0 + 1], x[x_ptr + i - T0 - 1])) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15((short)(32767 - num8), b3), Inlines.ADD32(x[x_ptr + i - T0 + 2], x[x_ptr + i - T0 - 2])) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(num8, num), num5) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(num8, num2), Inlines.ADD32(num4, num6)) + Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(num8, num3), Inlines.ADD32(num7, b4));
				b4 = num6;
				num6 = num5;
				num5 = num4;
				num4 = num7;
			}
			if (g1 == 0)
			{
				if (x_ptr == y_ptr)
				{
				}
			}
			else
			{
				comb_filter_const(y, y_ptr + i, x, x_ptr + i, T1, N - i, num, num2, num3);
			}
		}

		internal static void init_caps(CeltMode m, int[] cap, int LM, int C)
		{
			for (int i = 0; i < m.nbEBands; i++)
			{
				int num = m.eBands[i + 1] - m.eBands[i] << LM;
				cap[i] = (m.cache.caps[m.nbEBands * (2 * LM + C - 1) + i] + 64) * C * num >> 2;
			}
		}
	}
	internal static class CeltConstants
	{
		public const int Q15ONE = 32767;

		public const float CELT_SIG_SCALE = 32768f;

		public const int SIG_SHIFT = 12;

		public const int NORM_SCALING = 16384;

		public const int DB_SHIFT = 10;

		public const int EPSILON = 1;

		public const int VERY_SMALL = 0;

		public const short VERY_LARGE16 = short.MaxValue;

		public const short Q15_ONE = short.MaxValue;

		public const int COMBFILTER_MAXPERIOD = 1024;

		public const int COMBFILTER_MINPERIOD = 15;

		public const int DECODE_BUFFER_SIZE = 2048;

		public const int BITALLOC_SIZE = 11;

		public const int MAX_PERIOD = 1024;

		public const int TOTAL_MODES = 1;

		public const int MAX_PSEUDO = 40;

		public const int LOG_MAX_PSEUDO = 6;

		public const int CELT_MAX_PULSES = 128;

		public const int MAX_FINE_BITS = 8;

		public const int FINE_OFFSET = 21;

		public const int QTHETA_OFFSET = 4;

		public const int QTHETA_OFFSET_TWOPHASE = 16;

		public const int PLC_PITCH_LAG_MAX = 720;

		public const int PLC_PITCH_LAG_MIN = 100;

		public const int LPC_ORDER = 24;
	}
	internal static class CeltLPC
	{
		internal static void celt_lpc(int[] _lpc, int[] ac, int p)
		{
			int num = ac[0];
			int[] array = new int[p];
			if (ac[0] != 0)
			{
				for (int i = 0; i < p; i++)
				{
					int num2 = 0;
					for (int j = 0; j < i; j++)
					{
						num2 += Inlines.MULT32_32_Q31(array[j], ac[i - j]);
					}
					num2 += Inlines.SHR32(ac[i + 1], 3);
					int num3 = -Inlines.frac_div32(Inlines.SHL32(num2, 3), num);
					array[i] = Inlines.SHR32(num3, 3);
					for (int j = 0; j < i + 1 >> 1; j++)
					{
						int num4 = array[j];
						int num5 = array[i - 1 - j];
						array[j] = num4 + Inlines.MULT32_32_Q31(num3, num5);
						array[i - 1 - j] = num5 + Inlines.MULT32_32_Q31(num3, num4);
					}
					num -= Inlines.MULT32_32_Q31(Inlines.MULT32_32_Q31(num3, num3), num);
					if (num < Inlines.SHR32(ac[0], 10))
					{
						break;
					}
				}
			}
			for (int i = 0; i < p; i++)
			{
				_lpc[i] = Inlines.ROUND16(array[i], 16);
			}
		}

		internal static void celt_iir(int[] _x, int _x_ptr, int[] den, int[] _y, int _y_ptr, int N, int ord, int[] mem)
		{
			int[] array = new int[ord];
			int[] array2 = new int[N + ord];
			int i;
			for (i = 0; i < ord; i++)
			{
				array[i] = den[ord - i - 1];
			}
			for (i = 0; i < ord; i++)
			{
				array2[i] = -mem[ord - i - 1];
			}
			for (; i < N + ord; i++)
			{
				array2[i] = 0;
			}
			for (i = 0; i < N - 3; i += 4)
			{
				int sum = _x[_x_ptr + i];
				int sum2 = _x[_x_ptr + i + 1];
				int sum3 = _x[_x_ptr + i + 2];
				int sum4 = _x[_x_ptr + i + 3];
				Kernels.xcorr_kernel(array, array2, i, ref sum, ref sum2, ref sum3, ref sum4, ord);
				array2[i + ord] = -Inlines.ROUND16(sum, 12);
				_y[_y_ptr + i] = sum;
				sum2 = Inlines.MAC16_16(sum2, array2[i + ord], den[0]);
				array2[i + ord + 1] = -Inlines.ROUND16(sum2, 12);
				_y[_y_ptr + i + 1] = sum2;
				sum3 = Inlines.MAC16_16(sum3, array2[i + ord + 1], den[0]);
				sum3 = Inlines.MAC16_16(sum3, array2[i + ord], den[1]);
				array2[i + ord + 2] = -Inlines.ROUND16(sum3, 12);
				_y[_y_ptr + i + 2] = sum3;
				sum4 = Inlines.MAC16_16(sum4, array2[i + ord + 2], den[0]);
				sum4 = Inlines.MAC16_16(sum4, array2[i + ord + 1], den[1]);
				sum4 = Inlines.MAC16_16(sum4, array2[i + ord], den[2]);
				array2[i + ord + 3] = -Inlines.ROUND16(sum4, 12);
				_y[_y_ptr + i + 3] = sum4;
			}
			for (; i < N; i++)
			{
				int num = _x[_x_ptr + i];
				for (int j = 0; j < ord; j++)
				{
					num -= Inlines.MULT16_16(array[j], array2[i + j]);
				}
				array2[i + ord] = Inlines.ROUND16(num, 12);
				_y[_y_ptr + i] = num;
			}
			for (i = 0; i < ord; i++)
			{
				mem[i] = _y[_y_ptr + N - i - 1];
			}
		}
	}
	internal static class CeltPitchXCorr
	{
		internal static int pitch_xcorr(int[] _x, int[] _y, int[] xcorr, int len, int max_pitch)
		{
			int num = 1;
			int i;
			for (i = 0; i < max_pitch - 3; i += 4)
			{
				int sum = 0;
				int sum2 = 0;
				int sum3 = 0;
				int sum4 = 0;
				Kernels.xcorr_kernel(_x, _y, i, ref sum, ref sum2, ref sum3, ref sum4, len);
				xcorr[i] = sum;
				xcorr[i + 1] = sum2;
				xcorr[i + 2] = sum3;
				xcorr[i + 3] = sum4;
				sum = Inlines.MAX32(sum, sum2);
				sum3 = Inlines.MAX32(sum3, sum4);
				sum = Inlines.MAX32(sum, sum3);
				num = Inlines.MAX32(num, sum);
			}
			for (; i < max_pitch; i++)
			{
				num = Inlines.MAX32(num, xcorr[i] = Kernels.celt_inner_prod(_x, 0, _y, i, len));
			}
			return num;
		}

		internal static int pitch_xcorr(short[] _x, int _x_ptr, short[] _y, int _y_ptr, int[] xcorr, int len, int max_pitch)
		{
			int num = 1;
			int i;
			for (i = 0; i < max_pitch - 3; i += 4)
			{
				int sum = 0;
				int sum2 = 0;
				int sum3 = 0;
				int sum4 = 0;
				Kernels.xcorr_kernel(_x, _x_ptr, _y, _y_ptr + i, ref sum, ref sum2, ref sum3, ref sum4, len);
				xcorr[i] = sum;
				xcorr[i + 1] = sum2;
				xcorr[i + 2] = sum3;
				xcorr[i + 3] = sum4;
				sum = Inlines.MAX32(sum, sum2);
				sum3 = Inlines.MAX32(sum3, sum4);
				sum = Inlines.MAX32(sum, sum3);
				num = Inlines.MAX32(num, sum);
			}
			for (; i < max_pitch; i++)
			{
				num = Inlines.MAX32(num, xcorr[i] = Kernels.celt_inner_prod(_x, _x_ptr, _y, _y_ptr + i, len));
			}
			return num;
		}

		internal static int pitch_xcorr(short[] _x, short[] _y, int[] xcorr, int len, int max_pitch)
		{
			int num = 1;
			int i;
			for (i = 0; i < max_pitch - 3; i += 4)
			{
				int sum = 0;
				int sum2 = 0;
				int sum3 = 0;
				int sum4 = 0;
				Kernels.xcorr_kernel(_x, 0, _y, i, ref sum, ref sum2, ref sum3, ref sum4, len);
				xcorr[i] = sum;
				xcorr[i + 1] = sum2;
				xcorr[i + 2] = sum3;
				xcorr[i + 3] = sum4;
				sum = Inlines.MAX32(sum, sum2);
				sum3 = Inlines.MAX32(sum3, sum4);
				sum = Inlines.MAX32(sum, sum3);
				num = Inlines.MAX32(num, sum);
			}
			for (; i < max_pitch; i++)
			{
				num = Inlines.MAX32(num, xcorr[i] = Kernels.celt_inner_prod(_x, _y, i, len));
			}
			return num;
		}
	}
	internal static class CWRS
	{
		internal static readonly uint[] CELT_PVQ_U_ROW = new uint[15]
		{
			0u, 176u, 351u, 525u, 698u, 870u, 1041u, 1131u, 1178u, 1207u,
			1226u, 1240u, 1248u, 1254u, 1257u
		};

		priva