Decompiled source of Concentus v2.2.2

BepInEx/core/Concentus/Concentus.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using Concentus.Celt;
using Concentus.Celt.Structs;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Enums;
using Concentus.Native;
using Concentus.Silk;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using Concentus.Structs;
using Microsoft.CodeAnalysis;
using Microsoft.Win32.SafeHandles;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("ParityTest, PublicKey=002400000480000094000000060200000024000052534131000400000100010071a2f675c04c87e64b9be6d37f5833c5285fb4ed883780cf6d61e80aee5d77950b2f06dd45bc634f53405f2a2b7b2332f4dfdcb0554ffc97b935e7343e76e733eea44346e56ac1098c12a66de71e324f2f503f9f2e32560910e2082d6943df50db42679a330e52979bd1eefbb59485d2c7420d158f6ab6d41bdf42d2172675e1")]
[assembly: InternalsVisibleTo("TestOpusEncode, PublicKey=002400000480000094000000060200000024000052534131000400000100010071a2f675c04c87e64b9be6d37f5833c5285fb4ed883780cf6d61e80aee5d77950b2f06dd45bc634f53405f2a2b7b2332f4dfdcb0554ffc97b935e7343e76e733eea44346e56ac1098c12a66de71e324f2f503f9f2e32560910e2082d6943df50db42679a330e52979bd1eefbb59485d2c7420d158f6ab6d41bdf42d2172675e1")]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Logan Stromberg")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("© Xiph.Org Foundation, Skype Limited, CSIRO, Microsoft Corp.")]
[assembly: AssemblyDescription("This package is a portable C# implementation of the Opus audio compression codec (see https://opus-codec.org/ for more details). This package contains the Opus encoder, decoder, multistream codecs, repacketizer, as well as a port of the libspeexdsp resampler. It does NOT contain code to parse .ogg or .opus container files or to manage RTP packet streams. For better performance depending on your platform, see also the Concentus.Native package.")]
[assembly: AssemblyFileVersion("2.2.2.0")]
[assembly: AssemblyInformationalVersion("2.2.2+6c2328dc19044601e33a9c11628b8d60e1f3011c")]
[assembly: AssemblyProduct("Concentus")]
[assembly: AssemblyTitle("Concentus")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/lostromb/concentus")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("2.2.2.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
}
namespace Concentus
{
	public interface IOpusDecoder : IDisposable
	{
		OpusBandwidth Bandwidth { get; }

		uint FinalRange { get; }

		int Gain { get; set; }

		int LastPacketDuration { get; }

		int NumChannels { get; }

		int Pitch { get; }

		int SampleRate { get; }

		int Decode(ReadOnlySpan<byte> in_data, Span<float> out_pcm, int frame_size, bool decode_fec = false);

		int Decode(ReadOnlySpan<byte> in_data, Span<short> out_pcm, int frame_size, bool decode_fec = false);

		void ResetState();

		string GetVersionString();
	}
	public interface IOpusEncoder : IDisposable
	{
		OpusApplication Application { get; set; }

		int Bitrate { get; set; }

		int ForceChannels { get; set; }

		OpusBandwidth MaxBandwidth { get; set; }

		OpusBandwidth Bandwidth { get; set; }

		bool UseDTX { get; set; }

		int Complexity { get; set; }

		bool UseInbandFEC { get; set; }

		int PacketLossPercent { get; set; }

		bool UseVBR { get; set; }

		bool UseConstrainedVBR { get; set; }

		OpusSignal SignalType { get; set; }

		int Lookahead { get; }

		int SampleRate { get; }

		int NumChannels { get; }

		uint FinalRange { get; }

		int LSBDepth { get; set; }

		OpusFramesize ExpertFrameDuration { get; set; }

		OpusMode ForceMode { set; }

		bool PredictionDisabled { get; set; }

		int Encode(ReadOnlySpan<short> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes);

		int Encode(ReadOnlySpan<float> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes);

		void ResetState();

		string GetVersionString();
	}
	public interface IOpusMultiStreamDecoder : IDisposable
	{
		OpusBandwidth Bandwidth { get; }

		uint FinalRange { get; }

		int Gain { get; set; }

		int LastPacketDuration { get; }

		int SampleRate { get; }

		int NumChannels { get; }

		int DecodeMultistream(ReadOnlySpan<byte> data, Span<float> out_pcm, int frame_size, bool decode_fec);

		int DecodeMultistream(ReadOnlySpan<byte> data, Span<short> out_pcm, int frame_size, bool decode_fec);

		void ResetState();

		string GetVersionString();
	}
	public interface IOpusMultiStreamEncoder : IDisposable
	{
		OpusApplication Application { get; set; }

		OpusBandwidth Bandwidth { get; set; }

		int Bitrate { get; set; }

		int Complexity { get; set; }

		int NumChannels { get; }

		OpusFramesize ExpertFrameDuration { get; set; }

		uint FinalRange { get; }

		OpusMode ForceMode { set; }

		int Lookahead { get; }

		int LSBDepth { get; set; }

		OpusBandwidth MaxBandwidth { get; set; }

		int PacketLossPercent { get; set; }

		bool PredictionDisabled { get; set; }

		int SampleRate { get; }

		OpusSignal SignalType { get; set; }

		bool UseConstrainedVBR { get; set; }

		bool UseDTX { get; set; }

		bool UseInbandFEC { get; set; }

		bool UseVBR { get; set; }

		int EncodeMultistream(ReadOnlySpan<float> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes);

		int EncodeMultistream(ReadOnlySpan<short> in_pcm, int frame_size, Span<byte> out_data, int max_data_bytes);

		void ResetState();

		string GetVersionString();
	}
	public interface IResampler : IDisposable
	{
		int InputLatency { get; }

		int InputStride { get; set; }

		int OutputLatencySamples { get; }

		TimeSpan OutputLatency { get; }

		int OutputStride { get; set; }

		int Quality { get; set; }

		void GetRateFraction(out int ratio_num, out int ratio_den);

		void GetRates(out int in_rate, out int out_rate);

		void ResetMem();

		void SkipZeroes();

		void Process(int channel_index, Span<float> input, ref int in_len, Span<float> output, ref int out_len);

		void Process(int channel_index, Span<short> input, ref int in_len, Span<short> output, ref int out_len);

		void ProcessInterleaved(Span<float> input, ref int in_len, Span<float> output, ref int out_len);

		void ProcessInterleaved(Span<short> input, ref int in_len, Span<short> output, ref int out_len);

		void SetRateFraction(int ratio_num, int ratio_den, int in_rate, int out_rate);

		void SetRates(int in_rate, int out_rate);
	}
	public static class OpusCodecFactory
	{
		private static readonly object _mutex = new object();

		private static bool _nativeLibInitialized = false;

		private static bool _isNativeLibAvailable = false;

		private static bool _userAllowNativeLib = true;

		public static bool AttemptToUseNativeLibrary
		{
			get
			{
				return _userAllowNativeLib;
			}
			set
			{
				_userAllowNativeLib = value;
			}
		}

		public static IOpusEncoder CreateEncoder(int sampleRate, int numChannels, OpusApplication application = OpusApplication.OPUS_APPLICATION_AUDIO, TextWriter messageLogger = null)
		{
			if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger))
			{
				return NativeOpusEncoder.Create(sampleRate, numChannels, application);
			}
			return new OpusEncoder(sampleRate, numChannels, application);
		}

		public static IOpusDecoder CreateDecoder(int sampleRate, int numChannels, TextWriter messageLogger = null)
		{
			if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger))
			{
				return NativeOpusDecoder.Create(sampleRate, numChannels);
			}
			return new OpusDecoder(sampleRate, numChannels);
		}

		public static IOpusMultiStreamEncoder CreateMultiStreamEncoder(int sampleRate, int numChannels, int mappingFamily, out int streams, out int coupledStreams, byte[] mapping, OpusApplication application, TextWriter messageLogger = null)
		{
			if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger))
			{
				return NativeOpusMultistreamEncoder.Create(sampleRate, numChannels, mappingFamily, out streams, out coupledStreams, mapping, application);
			}
			return OpusMSEncoder.CreateSurround(sampleRate, numChannels, mappingFamily, out streams, out coupledStreams, mapping, application);
		}

		public static IOpusMultiStreamDecoder CreateMultiStreamDecoder(int sampleRate, int numChannels, int streams, int coupledStreams, byte[] mapping, TextWriter messageLogger = null)
		{
			if (_userAllowNativeLib && NativeLibraryAvailable(messageLogger))
			{
				return NativeOpusMultistreamDecoder.Create(sampleRate, numChannels, streams, coupledStreams, mapping);
			}
			return new OpusMSDecoder(sampleRate, numChannels, streams, coupledStreams, mapping);
		}

		private static bool NativeLibraryAvailable(TextWriter messageLogger)
		{
			lock (_mutex)
			{
				if (!_nativeLibInitialized)
				{
					try
					{
						_isNativeLibAvailable = NativeOpus.Initialize(messageLogger);
						messageLogger?.WriteLine($"Is native opus available? {_isNativeLibAvailable}");
					}
					catch (Exception ex)
					{
						messageLogger?.WriteLine(ex.ToString());
					}
					_nativeLibInitialized = true;
				}
				return _isNativeLibAvailable;
			}
		}
	}
	internal static class Analysis
	{
		private const double M_PI = 3.141592653;

		private const float cA = 0.43157974f;

		private const float cB = 0.678484f;

		private const float cC = 0.08595542f;

		private const float cE = MathF.PI / 2f;

		private const int NB_TONAL_SKIP_BANDS = 9;

		internal static float fast_atan2f(float y, float x)
		{
			if (Inlines.ABS16(x) + Inlines.ABS16(y) < 1E-09f)
			{
				x *= 1E+12f;
				y *= 1E+12f;
			}
			float num = x * x;
			float num2 = y * y;
			if (num < num2)
			{
				float num3 = (num2 + 0.678484f * num) * (num2 + 0.08595542f * num);
				if (num3 != 0f)
				{
					return (0f - x) * y * (num2 + 0.43157974f * num) / num3 + ((y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f));
				}
				if (!(y < 0f))
				{
					return MathF.PI / 2f;
				}
				return -MathF.PI / 2f;
			}
			float num4 = (num + 0.678484f * num2) * (num + 0.08595542f * num2);
			if (num4 != 0f)
			{
				return x * y * (num + 0.43157974f * num2) / num4 + ((y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f)) - ((x * y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f));
			}
			return ((y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f)) - ((x * y < 0f) ? (-MathF.PI / 2f) : (MathF.PI / 2f));
		}

		internal static void tonality_analysis_init(TonalityAnalysisState tonal)
		{
			tonal.Reset();
		}

		internal static void tonality_get_info(TonalityAnalysisState tonal, AnalysisInfo info_out, int len)
		{
			int num = tonal.read_pos;
			int num2 = tonal.write_pos - tonal.read_pos;
			if (num2 < 0)
			{
				num2 += 200;
			}
			if (len > 480 && num != tonal.write_pos)
			{
				num++;
				if (num == 200)
				{
					num = 0;
				}
			}
			if (num == tonal.write_pos)
			{
				num--;
			}
			if (num < 0)
			{
				num = 199;
			}
			info_out.Assign(tonal.info[num]);
			tonal.read_subframe += len / 120;
			while (tonal.read_subframe >= 4)
			{
				tonal.read_subframe -= 4;
				tonal.read_pos++;
			}
			if (tonal.read_pos >= 200)
			{
				tonal.read_pos -= 200;
			}
			num2 = Inlines.IMAX(num2 - 10, 0);
			float num3 = 0f;
			int i;
			for (i = 0; i < 200 - num2; i++)
			{
				num3 += tonal.pmusic[i];
			}
			for (; i < 200; i++)
			{
				num3 += tonal.pspeech[i];
			}
			num3 = num3 * tonal.music_confidence + (1f - num3) * tonal.speech_confidence;
			info_out.music_prob = num3;
		}

		internal static void tonality_analysis<T>(TonalityAnalysisState tonal, CeltMode celt_mode, ReadOnlySpan<T> x, int len, int offset, int c1, int c2, int C, int lsb_depth, Downmix.downmix_func<T> downmix)
		{
			int num = 480;
			int num2 = 240;
			float[] angle = tonal.angle;
			float[] d_angle = tonal.d_angle;
			float[] d2_angle = tonal.d2_angle;
			float[] array = new float[18];
			float[] array2 = new float[18];
			float[] array3 = new float[8];
			float[] array4 = new float[25];
			float num3 = 97.40909f;
			float num4 = 0f;
			float[] array5 = new float[2];
			int num5 = 0;
			float num6 = 0f;
			tonal.last_transition++;
			float num7 = 1f / (float)Inlines.IMIN(20, 1 + tonal.count);
			float num8 = 1f / (float)Inlines.IMIN(50, 1 + tonal.count);
			float num9 = 1f / (float)Inlines.IMIN(1000, 1 + tonal.count);
			if (tonal.count < 4)
			{
				tonal.music_prob = 0.5f;
			}
			FFTState st = celt_mode.mdct.kfft[0];
			if (tonal.count == 0)
			{
				tonal.mem_fill = 240;
			}
			downmix(x, tonal.inmem, tonal.mem_fill, Inlines.IMIN(len, 720 - tonal.mem_fill), offset, c1, c2, C);
			if (tonal.mem_fill + len < 720)
			{
				tonal.mem_fill += len;
				return;
			}
			AnalysisInfo analysisInfo = tonal.info[tonal.write_pos++];
			if (tonal.write_pos >= 200)
			{
				tonal.write_pos -= 200;
			}
			int[] array6 = new int[960];
			int[] array7 = new int[960];
			float[] array8 = new float[240];
			float[] array9 = new float[240];
			for (int i = 0; i < num2; i++)
			{
				float num10 = Tables.analysis_window[i];
				array6[2 * i] = (int)(num10 * (float)tonal.inmem[i]);
				array6[2 * i + 1] = (int)(num10 * (float)tonal.inmem[num2 + i]);
				array6[2 * (num - i - 1)] = (int)(num10 * (float)tonal.inmem[num - i - 1]);
				array6[2 * (num - i - 1) + 1] = (int)(num10 * (float)tonal.inmem[num + num2 - i - 1]);
			}
			Arrays.MemMoveInt(tonal.inmem, 480, 0, 240);
			int num11 = len - (720 - tonal.mem_fill);
			downmix(x, tonal.inmem, 240, num11, offset + 720 - tonal.mem_fill, c1, c2, C);
			tonal.mem_fill = 240 + num11;
			KissFFT.opus_fft(st, array6, array7);
			for (int i = 1; i < num2; i++)
			{
				float x2 = (float)array7[2 * i] + (float)array7[2 * (num - i)];
				float y = (float)array7[2 * i + 1] - (float)array7[2 * (num - i) + 1];
				float x3 = (float)array7[2 * i + 1] + (float)array7[2 * (num - i) + 1];
				float y2 = (float)array7[2 * (num - i)] - (float)array7[2 * i];
				float num12 = 1f / (2f * MathF.PI) * fast_atan2f(y, x2);
				float num13 = num12 - angle[i];
				float num14 = num13 - d_angle[i];
				float num15 = 1f / (2f * MathF.PI) * fast_atan2f(y2, x3);
				float num16 = num15 - num12;
				float num17 = num16 - num13;
				float num18 = num14 - (float)Math.Floor(0.5f + num14);
				array9[i] = Inlines.ABS16(num18);
				num18 *= num18;
				num18 *= num18;
				float num19 = num17 - (float)Math.Floor(0.5f + num17);
				array9[i] += Inlines.ABS16(num19);
				num19 *= num19;
				num19 *= num19;
				float num20 = 0.25f * (d2_angle[i] + 2f * num18 + num19);
				array8[i] = 1f / (1f + 640f * num3 * num20) - 0.015f;
				angle[i] = num15;
				d_angle[i] = num16;
				d2_angle[i] = num19;
			}
			float num21 = 0f;
			float num22 = 0f;
			analysisInfo.activity = 0f;
			float num23 = 0f;
			float num24 = 0f;
			if (tonal.count == 0)
			{
				for (int j = 0; j < 18; j++)
				{
					tonal.lowE[j] = 1E+10f;
					tonal.highE[j] = -1E+10f;
				}
			}
			float num25 = 0f;
			float num26 = 0f;
			for (int j = 0; j < 18; j++)
			{
				float num27 = 0f;
				float num28 = 0f;
				float num29 = 0f;
				for (int i = Tables.tbands[j]; i < Tables.tbands[j + 1]; i++)
				{
					float num30 = (float)array7[2 * i] * (float)array7[2 * i] + (float)array7[2 * (num - i)] * (float)array7[2 * (num - i)] + (float)array7[2 * i + 1] * (float)array7[2 * i + 1] + (float)array7[2 * (num - i) + 1] * (float)array7[2 * (num - i) + 1];
					num30 *= 5.55E-17f;
					num27 += num30;
					num28 += num30 * array8[i];
					num29 += num30 * 2f * (0.5f - array9[i]);
				}
				tonal.E[tonal.E_count][j] = num27;
				num23 += num29 / (1E-15f + num27);
				num26 += (float)Math.Sqrt(num27 + 1E-10f);
				array2[j] = (float)Math.Log(num27 + 1E-10f);
				tonal.lowE[j] = Inlines.MIN32(array2[j], tonal.lowE[j] + 0.01f);
				tonal.highE[j] = Inlines.MAX32(array2[j], tonal.highE[j] - 0.1f);
				if (tonal.highE[j] < tonal.lowE[j] + 1f)
				{
					tonal.highE[j] += 0.5f;
					tonal.lowE[j] -= 0.5f;
				}
				num25 += (array2[j] - tonal.lowE[j]) / (1E-15f + tonal.highE[j] - tonal.lowE[j]);
				float num31;
				float num32 = (num31 = 0f);
				for (int i = 0; i < 8; i++)
				{
					num32 += (float)Math.Sqrt(tonal.E[i][j]);
					num31 += tonal.E[i][j];
				}
				float num33 = Inlines.MIN16(0.99f, num32 / (float)Math.Sqrt(1E-15 + (double)(8f * num31)));
				num33 *= num33;
				num33 *= num33;
				num24 += num33;
				array[j] = Inlines.MAX16(num28 / (1E-15f + num27), num33 * tonal.prev_band_tonality[j]);
				num21 += array[j];
				if (j >= 9)
				{
					num21 -= array[j - 18 + 9];
				}
				num22 = Inlines.MAX16(num22, (1f + 0.03f * (float)(j - 18)) * num21);
				num4 += array[j] * (float)(j - 8);
				tonal.prev_band_tonality[j] = array[j];
			}
			float num34 = 0f;
			num5 = 0;
			num6 = 0f;
			float num35 = 0.00057f / (float)(1 << Inlines.IMAX(0, lsb_depth - 8));
			num35 *= 134217730f;
			num35 *= num35;
			for (int j = 0; j < 21; j++)
			{
				float num36 = 0f;
				int num37 = Tables.extra_bands[j];
				int num38 = Tables.extra_bands[j + 1];
				for (int i = num37; i < num38; i++)
				{
					float num39 = (float)array7[2 * i] * (float)array7[2 * i] + (float)array7[2 * (num - i)] * (float)array7[2 * (num - i)] + (float)array7[2 * i + 1] * (float)array7[2 * i + 1] + (float)array7[2 * (num - i) + 1] * (float)array7[2 * (num - i) + 1];
					num36 += num39;
				}
				num6 = Inlines.MAX32(num6, num36);
				tonal.meanE[j] = Inlines.MAX32((1f - num9) * tonal.meanE[j], num36);
				num36 = Inlines.MAX32(num36, tonal.meanE[j]);
				num34 = Inlines.MAX32(0.05f * num34, num36);
				if ((double)num36 > 0.1 * (double)num34 && num36 * 1E+09f > num6 && num36 > num35 * (float)(num38 - num37))
				{
					num5 = j;
				}
			}
			if (tonal.count <= 2)
			{
				num5 = 20;
			}
			num26 = 20f * (float)Math.Log10(num26);
			tonal.Etracker = Inlines.MAX32(tonal.Etracker - 0.03f, num26);
			tonal.lowECount *= 1f - num8;
			if (num26 < tonal.Etracker - 30f)
			{
				tonal.lowECount += num8;
			}
			for (int i = 0; i < 8; i++)
			{
				float num40 = 0f;
				for (int j = 0; j < 16; j++)
				{
					num40 += Tables.dct_table[i * 16 + j] * array2[j];
				}
				array3[i] = num40;
			}
			num24 /= 18f;
			num25 /= 18f;
			if (tonal.count < 10)
			{
				num25 = 0.5f;
			}
			num23 /= 18f;
			analysisInfo.activity = num23 + (1f - num23) * num25;
			num21 = num22 / 9f;
			num21 = (tonal.prev_tonality = Inlines.MAX16(num21, tonal.prev_tonality * 0.8f));
			num4 /= 64f;
			analysisInfo.tonality_slope = num4;
			tonal.E_count = (tonal.E_count + 1) % 8;
			tonal.count++;
			analysisInfo.tonality = num21;
			for (int i = 0; i < 4; i++)
			{
				array4[i] = -0.12299f * (array3[i] + tonal.mem[i + 24]) + 0.49195f * (tonal.mem[i] + tonal.mem[i + 16]) + 0.69693f * tonal.mem[i + 8] - 1.4349f * tonal.cmean[i];
			}
			for (int i = 0; i < 4; i++)
			{
				tonal.cmean[i] = (1f - num7) * tonal.cmean[i] + num7 * array3[i];
			}
			for (int i = 0; i < 4; i++)
			{
				array4[4 + i] = 0.63246f * (array3[i] - tonal.mem[i + 24]) + 0.31623f * (tonal.mem[i] - tonal.mem[i + 16]);
			}
			for (int i = 0; i < 3; i++)
			{
				array4[8 + i] = 0.53452f * (array3[i] + tonal.mem[i + 24]) - 0.26726f * (tonal.mem[i] + tonal.mem[i + 16]) - 0.53452f * tonal.mem[i + 8];
			}
			if (tonal.count > 5)
			{
				for (int i = 0; i < 9; i++)
				{
					tonal.std[i] = (1f - num7) * tonal.std[i] + num7 * array4[i] * array4[i];
				}
			}
			for (int i = 0; i < 8; i++)
			{
				tonal.mem[i + 24] = tonal.mem[i + 16];
				tonal.mem[i + 16] = tonal.mem[i + 8];
				tonal.mem[i + 8] = tonal.mem[i];
				tonal.mem[i] = array3[i];
			}
			for (int i = 0; i < 9; i++)
			{
				array4[11 + i] = (float)Math.Sqrt(tonal.std[i]);
			}
			array4[20] = analysisInfo.tonality;
			array4[21] = analysisInfo.activity;
			array4[22] = num24;
			array4[23] = analysisInfo.tonality_slope;
			array4[24] = tonal.lowECount;
			MultiLayerPerceptron.mlp_process(Tables.net, array4, array5);
			array5[0] = 0.5f * (array5[0] + 1f);
			array5[0] = 0.01f + 1.21f * array5[0] * array5[0] - 0.23f * (float)Math.Pow(array5[0], 10.0);
			array5[1] = 0.5f * array5[1] + 0.5f;
			array5[0] = array5[1] * array5[0] + (1f - array5[1]) * 0.5f;
			float num41 = 5E-05f * array5[1];
			float num42 = 0.05f;
			float num43 = Inlines.MAX16(0.05f, Inlines.MIN16(0.95f, array5[0]));
			float num44 = Inlines.MAX16(0.05f, Inlines.MIN16(0.95f, tonal.music_prob));
			num42 = 0.01f + 0.05f * Inlines.ABS16(num43 - num44) / (num43 * (1f - num44) + num44 * (1f - num43));
			float num45 = (1f - tonal.music_prob) * (1f - num41) + tonal.music_prob * num41;
			float num46 = tonal.music_prob * (1f - num41) + (1f - tonal.music_prob) * num41;
			num45 *= (float)Math.Pow(1f - array5[0], num42);
			num46 *= (float)Math.Pow(array5[0], num42);
			tonal.music_prob = num46 / (num45 + num46);
			analysisInfo.music_prob = tonal.music_prob;
			float num47 = 1E-20f;
			float num48 = (float)Math.Pow(1f - array5[0], num42);
			float num49 = (float)Math.Pow(array5[0], num42);
			if (tonal.count == 1)
			{
				tonal.pspeech[0] = 0.5f;
				tonal.pmusic[0] = 0.5f;
			}
			float num50 = tonal.pspeech[0] + tonal.pspeech[1];
			float num51 = tonal.pmusic[0] + tonal.pmusic[1];
			tonal.pspeech[0] = num50 * (1f - num41) * num48;
			tonal.pmusic[0] = num51 * (1f - num41) * num49;
			for (int i = 1; i < 199; i++)
			{
				tonal.pspeech[i] = tonal.pspeech[i + 1] * num48;
				tonal.pmusic[i] = tonal.pmusic[i + 1] * num49;
			}
			tonal.pspeech[199] = num51 * num41 * num48;
			tonal.pmusic[199] = num50 * num41 * num49;
			for (int i = 0; i < 200; i++)
			{
				num47 += tonal.pspeech[i] + tonal.pmusic[i];
			}
			num47 = 1f / num47;
			for (int i = 0; i < 200; i++)
			{
				tonal.pspeech[i] *= num47;
				tonal.pmusic[i] *= num47;
			}
			num47 = tonal.pmusic[0];
			for (int i = 1; i < 200; i++)
			{
				num47 += tonal.pspeech[i];
			}
			if ((double)array5[1] > 0.75)
			{
				if ((double)tonal.music_prob > 0.9)
				{
					float num52 = 1f / (float)(++tonal.music_confidence_count);
					tonal.music_confidence_count = Inlines.IMIN(tonal.music_confidence_count, 500);
					tonal.music_confidence += num52 * Inlines.MAX16(-0.2f, array5[0] - tonal.music_confidence);
				}
				if ((double)tonal.music_prob < 0.1)
				{
					float num53 = 1f / (float)(++tonal.speech_confidence_count);
					tonal.speech_confidence_count = Inlines.IMIN(tonal.speech_confidence_count, 500);
					tonal.speech_confidence += num53 * Inlines.MIN16(0.2f, array5[0] - tonal.speech_confidence);
				}
			}
			else
			{
				if (tonal.music_confidence_count == 0)
				{
					tonal.music_confidence = 0.9f;
				}
				if (tonal.speech_confidence_count == 0)
				{
					tonal.speech_confidence = 0.1f;
				}
			}
			if (tonal.last_music != ((tonal.music_prob > 0.5f) ? 1 : 0))
			{
				tonal.last_transition = 0;
			}
			tonal.last_music = ((tonal.music_prob > 0.5f) ? 1 : 0);
			analysisInfo.bandwidth = num5;
			analysisInfo.noisiness = num23;
			analysisInfo.valid = 1;
		}

		internal static void run_analysis<T>(TonalityAnalysisState analysis, CeltMode celt_mode, ReadOnlySpan<T> analysis_pcm, int analysis_frame_size, int frame_size, int c1, int c2, int C, int Fs, int lsb_depth, Downmix.downmix_func<T> downmix, AnalysisInfo analysis_info)
		{
			if (!analysis_pcm.IsEmpty)
			{
				analysis_frame_size = Inlines.IMIN(195 * Fs / 100, analysis_frame_size);
				int num = analysis_frame_size - analysis.analysis_offset;
				int num2 = analysis.analysis_offset;
				do
				{
					tonality_analysis(analysis, celt_mode, analysis_pcm, Inlines.IMIN(480, num), num2, c1, c2, C, lsb_depth, downmix);
					num2 += 480;
					num -= 480;
				}
				while (num > 0);
				analysis.analysis_offset = analysis_frame_size;
				analysis.analysis_offset -= frame_size;
			}
			analysis_info.valid = 0;
			tonality_get_info(analysis, analysis_info, frame_size);
		}
	}
	internal static class CodecHelpers
	{
		private const int MAX_DYNAMIC_FRAMESIZE = 24;

		internal static byte gen_toc(OpusMode mode, int framerate, OpusBandwidth bandwidth, int channels)
		{
			int num = 0;
			while (framerate < 400)
			{
				framerate <<= 1;
				num++;
			}
			byte b;
			switch (mode)
			{
			case OpusMode.MODE_SILK_ONLY:
				b = (byte)((int)(bandwidth - 1101) << 5);
				b |= (byte)(num - 2 << 3);
				break;
			case OpusMode.MODE_CELT_ONLY:
			{
				int num2 = (int)(bandwidth - 1102);
				if (num2 < 0)
				{
					num2 = 0;
				}
				b = 128;
				b |= (byte)(num2 << 5);
				b |= (byte)(num << 3);
				break;
			}
			default:
				b = 96;
				b |= (byte)((int)(bandwidth - 1104) << 4);
				b |= (byte)(num - 2 << 3);
				break;
			}
			return (byte)(b | (byte)(((channels == 2) ? 1u : 0u) << 2));
		}

		internal static void hp_cutoff(ReadOnlySpan<short> input, int input_ptr, int cutoff_Hz, Span<short> output, int output_ptr, int[] hp_mem, int len, int channels, int Fs)
		{
			int[] array = new int[3];
			int[] array2 = new int[2];
			int num = Inlines.silk_DIV32_16(Inlines.silk_SMULBB(2471, cutoff_Hz), Fs / 1000);
			int num2 = (array[0] = 268435456 - Inlines.silk_MUL(471, num));
			array[1] = Inlines.silk_LSHIFT(-num2, 1);
			array[2] = num2;
			int num3 = Inlines.silk_RSHIFT(num2, 6);
			array2[0] = Inlines.silk_SMULWW(num3, Inlines.silk_SMULWW(num, num) - 8388608);
			array2[1] = Inlines.silk_SMULWW(num3, num3);
			Filters.silk_biquad_alt(input, input_ptr, array, array2, hp_mem, 0, output, output_ptr, len, channels);
			if (channels == 2)
			{
				Filters.silk_biquad_alt(input, input_ptr + 1, array, array2, hp_mem, 2, output, output_ptr + 1, len, channels);
			}
		}

		internal static void dc_reject(ReadOnlySpan<short> input, int input_ptr, int cutoff_Hz, Span<short> output, int output_ptr, int[] hp_mem, int len, int channels, int Fs)
		{
			int shift = Inlines.celt_ilog2(Fs / (cutoff_Hz * 3));
			for (int i = 0; i < channels; i++)
			{
				for (int j = 0; j < len; j++)
				{
					int num = Inlines.SHL32(Inlines.EXTEND32(input[channels * j + i + input_ptr]), 15);
					int num2 = num - hp_mem[2 * i];
					hp_mem[2 * i] += Inlines.PSHR32(num - hp_mem[2 * i], shift);
					int a = num2 - hp_mem[2 * i + 1];
					hp_mem[2 * i + 1] += Inlines.PSHR32(num2 - hp_mem[2 * i + 1], shift);
					output[channels * j + i + output_ptr] = Inlines.EXTRACT16(Inlines.SATURATE(Inlines.PSHR32(a, 15), 32767));
				}
			}
		}

		internal static void stereo_fade(short[] pcm_buf, int g1, int g2, int overlap48, int frame_size, int channels, int[] window, int Fs)
		{
			int num = 48000 / Fs;
			int num2 = overlap48 / num;
			g1 = 32767 - g1;
			g2 = 32767 - g2;
			int i;
			for (i = 0; i < num2; i++)
			{
				int num3 = Inlines.MULT16_16_Q15(window[i * num], window[i * num]);
				int a = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num3, g2), 32767 - num3, g1), 15);
				int b = Inlines.EXTRACT16(Inlines.HALF32(pcm_buf[i * channels] - pcm_buf[i * channels + 1]));
				b = Inlines.MULT16_16_Q15(a, b);
				pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - b);
				pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + b);
			}
			for (; i < frame_size; i++)
			{
				int b2 = Inlines.EXTRACT16(Inlines.HALF32(pcm_buf[i * channels] - pcm_buf[i * channels + 1]));
				b2 = Inlines.MULT16_16_Q15(g2, b2);
				pcm_buf[i * channels] = (short)(pcm_buf[i * channels] - b2);
				pcm_buf[i * channels + 1] = (short)(pcm_buf[i * channels + 1] + b2);
			}
		}

		internal static void gain_fade(short[] buffer, int buf_ptr, int g1, int g2, int overlap48, int frame_size, int channels, int[] window, int Fs)
		{
			int num = 48000 / Fs;
			int num2 = overlap48 / num;
			if (channels == 1)
			{
				for (int i = 0; i < num2; i++)
				{
					int num3 = Inlines.MULT16_16_Q15(window[i * num], window[i * num]);
					int a = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num3, g2), 32767 - num3, g1), 15);
					buffer[buf_ptr + i] = (short)Inlines.MULT16_16_Q15(a, buffer[buf_ptr + i]);
				}
			}
			else
			{
				for (int i = 0; i < num2; i++)
				{
					int num4 = Inlines.MULT16_16_Q15(window[i * num], window[i * num]);
					int a2 = Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num4, g2), 32767 - num4, g1), 15);
					buffer[buf_ptr + i * 2] = (short)Inlines.MULT16_16_Q15(a2, buffer[buf_ptr + i * 2]);
					buffer[buf_ptr + i * 2 + 1] = (short)Inlines.MULT16_16_Q15(a2, buffer[buf_ptr + i * 2 + 1]);
				}
			}
			int num5 = 0;
			do
			{
				for (int i = num2; i < frame_size; i++)
				{
					buffer[buf_ptr + i * channels + num5] = (short)Inlines.MULT16_16_Q15(g2, buffer[buf_ptr + i * channels + num5]);
				}
			}
			while (++num5 < channels);
		}

		internal static float transient_boost(Span<float> E, int E_ptr, float[] E_1, int LM, int maxM)
		{
			float num = 0f;
			float num2 = 0f;
			int num3 = Inlines.IMIN(maxM, (1 << LM) + 1);
			for (int i = E_ptr; i < num3 + E_ptr; i++)
			{
				num += E[i];
				num2 += E_1[i];
			}
			float num4 = num * num2 / (float)(num3 * num3);
			return Inlines.MIN16(1f, (float)Math.Sqrt(Inlines.MAX16(0f, 0.05f * (num4 - 2f))));
		}

		internal static int transient_viterbi(float[] E, float[] E_1, int N, int frame_cost, int rate)
		{
			float[][] array = Arrays.InitTwoDimensionalArray<float>(24, 16);
			int[][] array2 = Arrays.InitTwoDimensionalArray<int>(24, 16);
			float num = ((rate < 80) ? 0f : ((rate <= 160) ? (((float)rate - 80f) / 80f) : 1f));
			for (int i = 0; i < 16; i++)
			{
				array2[0][i] = -1;
				array[0][i] = 1E+10f;
			}
			for (int i = 0; i < 4; i++)
			{
				array[0][1 << i] = (float)(frame_cost + rate * (1 << i)) * (1f + num * transient_boost(E, 0, E_1, i, N + 1));
				array2[0][1 << i] = i;
			}
			for (int i = 1; i < N; i++)
			{
				for (int j = 2; j < 16; j++)
				{
					array[i][j] = array[i - 1][j - 1];
					array2[i][j] = j - 1;
				}
				for (int j = 0; j < 4; j++)
				{
					array2[i][1 << j] = 1;
					float num2 = array[i - 1][1];
					for (int k = 1; k < 4; k++)
					{
						float num3 = array[i - 1][(1 << k + 1) - 1];
						if (num3 < num2)
						{
							array2[i][1 << j] = (1 << k + 1) - 1;
							num2 = num3;
						}
					}
					float num4 = (float)(frame_cost + rate * (1 << j)) * (1f + num * transient_boost(E, i, E_1, j, N - i + 1));
					array[i][1 << j] = num2;
					if (N - i < 1 << j)
					{
						array[i][1 << j] += num4 * (float)(N - i) / (float)(1 << j);
					}
					else
					{
						array[i][1 << j] += num4;
					}
				}
			}
			int num5 = 1;
			float num6 = array[N - 1][1];
			for (int i = 2; i < 16; i++)
			{
				if (array[N - 1][i] < num6)
				{
					num6 = array[N - 1][i];
					num5 = i;
				}
			}
			for (int i = N - 1; i >= 0; i--)
			{
				num5 = array2[i][num5];
			}
			return num5;
		}

		internal static int optimize_framesize<T>(ReadOnlySpan<T> x, int len, int C, int Fs, int bitrate, int tonality, float[] mem, int buffering, Downmix.downmix_func<T> downmix)
		{
			float[] array = new float[28];
			float[] array2 = new float[27];
			int num = 0;
			int num2 = Fs / 400;
			int[] array3 = new int[num2];
			array[0] = mem[0];
			array2[0] = 1f / (1f + mem[0]);
			int num3;
			int num4;
			if (buffering != 0)
			{
				num3 = 2 * num2 - buffering;
				len -= num3;
				array[1] = mem[1];
				array2[1] = 1f / (1f + mem[1]);
				array[2] = mem[2];
				array2[2] = 1f / (1f + mem[2]);
				num4 = 3;
			}
			else
			{
				num4 = 1;
				num3 = 0;
			}
			int num5 = Inlines.IMIN(len / num2, 24);
			int num6 = 0;
			int i;
			for (i = 0; i < num5; i++)
			{
				float num7 = 1f;
				downmix(x, array3, 0, num2, i * num2 + num3, 0, -2, C);
				if (i == 0)
				{
					num6 = array3[0];
				}
				for (int j = 0; j < num2; j++)
				{
					int num8 = array3[j];
					num7 += (float)(num8 - num6) * (float)(num8 - num6);
					num6 = num8;
				}
				array[i + num4] = num7;
				array2[i + num4] = 1f / num7;
			}
			array[i + num4] = array[i + num4 - 1];
			if (buffering != 0)
			{
				num5 = Inlines.IMIN(24, num5 + 2);
			}
			num = transient_viterbi(array, array2, num5, (int)((1f + 0.5f * (float)tonality) * (float)(60 * C + 40)), bitrate / 400);
			mem[0] = array[1 << num];
			if (buffering != 0)
			{
				mem[1] = array[(1 << num) + 1];
				mem[2] = array[(1 << num) + 2];
			}
			return num;
		}

		internal static int frame_size_select(int frame_size, OpusFramesize variable_duration, int Fs)
		{
			if (frame_size < Fs / 400)
			{
				return -1;
			}
			int num;
			switch (variable_duration)
			{
			case OpusFramesize.OPUS_FRAMESIZE_ARG:
				num = frame_size;
				break;
			case OpusFramesize.OPUS_FRAMESIZE_VARIABLE:
				num = Fs / 50;
				break;
			case OpusFramesize.OPUS_FRAMESIZE_2_5_MS:
			case OpusFramesize.OPUS_FRAMESIZE_5_MS:
			case OpusFramesize.OPUS_FRAMESIZE_10_MS:
			case OpusFramesize.OPUS_FRAMESIZE_20_MS:
			case OpusFramesize.OPUS_FRAMESIZE_40_MS:
			case OpusFramesize.OPUS_FRAMESIZE_60_MS:
				num = Inlines.IMIN(3 * Fs / 50, Fs / 400 << (int)(variable_duration - 5001));
				break;
			default:
				return -1;
			}
			if (num > frame_size)
			{
				return -1;
			}
			if (400 * num != Fs && 200 * num != Fs && 100 * num != Fs && 50 * num != Fs && 25 * num != Fs && 50 * num != 3 * Fs)
			{
				return -1;
			}
			return num;
		}

		internal static int compute_frame_size<T>(ReadOnlySpan<T> analysis_pcm, int frame_size, OpusFramesize variable_duration, int C, int Fs, int bitrate_bps, int delay_compensation, Downmix.downmix_func<T> downmix, float[] subframe_mem, bool analysis_enabled)
		{
			if (analysis_enabled && variable_duration == OpusFramesize.OPUS_FRAMESIZE_VARIABLE && frame_size >= Fs / 200)
			{
				int num = 3;
				num = optimize_framesize(analysis_pcm, frame_size, C, Fs, bitrate_bps, 0, subframe_mem, delay_compensation, downmix);
				while (Fs / 400 << num > frame_size)
				{
					num--;
				}
				frame_size = Fs / 400 << num;
			}
			else
			{
				frame_size = frame_size_select(frame_size, variable_duration, Fs);
			}
			if (frame_size < 0)
			{
				return -1;
			}
			return frame_size;
		}

		internal static int compute_stereo_width(ReadOnlySpan<short> pcm, int pcm_ptr, int frame_size, int Fs, StereoWidthState mem)
		{
			int num = Fs / frame_size;
			int a = 32767 - 819175 / Inlines.IMAX(50, num);
			int num3;
			int num2;
			int num4 = (num3 = (num2 = 0));
			for (int i = 0; i < frame_size - 3; i += 4)
			{
				int num5 = 0;
				int num6 = 0;
				int num7 = 0;
				int num8 = pcm_ptr + 2 * i;
				int num9 = pcm[num8];
				int num10 = pcm[num8 + 1];
				num5 = Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2);
				num6 = Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2);
				num7 = Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2);
				num9 = pcm[num8 + 2];
				num10 = pcm[num8 + 3];
				num5 += Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2);
				num6 += Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2);
				num7 += Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2);
				num9 = pcm[num8 + 4];
				num10 = pcm[num8 + 5];
				num5 += Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2);
				num6 += Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2);
				num7 += Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2);
				num9 = pcm[num8 + 6];
				num10 = pcm[num8 + 7];
				num5 += Inlines.SHR32(Inlines.MULT16_16(num9, num9), 2);
				num6 += Inlines.SHR32(Inlines.MULT16_16(num9, num10), 2);
				num7 += Inlines.SHR32(Inlines.MULT16_16(num10, num10), 2);
				num4 += Inlines.SHR32(num5, 10);
				num3 += Inlines.SHR32(num6, 10);
				num2 += Inlines.SHR32(num7, 10);
			}
			mem.XX += Inlines.MULT16_32_Q15(a, num4 - mem.XX);
			mem.XY += Inlines.MULT16_32_Q15(a, num3 - mem.XY);
			mem.YY += Inlines.MULT16_32_Q15(a, num2 - mem.YY);
			mem.XX = Inlines.MAX32(0, mem.XX);
			mem.XY = Inlines.MAX32(0, mem.XY);
			mem.YY = Inlines.MAX32(0, mem.YY);
			if (Inlines.MAX32(mem.XX, mem.YY) > 210)
			{
				int num11 = Inlines.celt_sqrt(mem.XX);
				int num12 = Inlines.celt_sqrt(mem.YY);
				int num13 = Inlines.celt_sqrt(num11);
				int num14 = Inlines.celt_sqrt(num12);
				mem.XY = Inlines.MIN32(mem.XY, num11 * num12);
				int num15 = Inlines.SHR32(Inlines.frac_div32(mem.XY, 1 + Inlines.MULT16_16(num11, num12)), 16);
				int b = 32767 * Inlines.ABS16(num13 - num14) / (1 + num13 + num14);
				int num16 = Inlines.MULT16_16_Q15(Inlines.celt_sqrt(1073741824 - Inlines.MULT16_16(num15, num15)), b);
				mem.smoothed_width += (num16 - mem.smoothed_width) / num;
				mem.max_follower = Inlines.MAX16(mem.max_follower - 655 / num, mem.smoothed_width);
			}
			else
			{
				int num16 = 0;
				int num15 = 32767;
				int b = 0;
			}
			return Inlines.EXTRACT16(Inlines.MIN32(32767, 20 * mem.max_follower));
		}

		internal static void smooth_fade(Span<short> in1, int in1_ptr, Span<short> in2, int in2_ptr, Span<short> output, int output_ptr, int overlap, int channels, int[] window, int Fs)
		{
			int num = 48000 / Fs;
			for (int i = 0; i < channels; i++)
			{
				for (int j = 0; j < overlap; j++)
				{
					int num2 = Inlines.MULT16_16_Q15(window[j * num], window[j * num]);
					output[output_ptr + j * channels + i] = (short)Inlines.SHR32(Inlines.MAC16_16(Inlines.MULT16_16(num2, in2[in2_ptr + j * channels + i]), 32767 - num2, in1[in1_ptr + j * channels + i]), 15);
				}
			}
		}

		internal static string opus_strerror(int error)
		{
			string[] array = new string[8] { "success", "invalid argument", "buffer too small", "internal error", "corrupted stream", "request not implemented", "invalid state", "memory allocation failed" };
			if (error > 0 || error < -7)
			{
				return "unknown error";
			}
			return array[-error];
		}

		internal static string GetVersionString()
		{
			return "Concentus 2.1.2";
		}
	}
	internal static class Downmix
	{
		internal delegate void downmix_func<T>(ReadOnlySpan<T> _x, Span<int> sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C);

		internal static void downmix_float(ReadOnlySpan<float> x, Span<int> sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C)
		{
			for (int i = 0; i < subframe; i++)
			{
				sub[sub_ptr + i] = Inlines.FLOAT2INT16(x[(i + offset) * C + c1]);
			}
			if (c2 > -1)
			{
				for (int i = 0; i < subframe; i++)
				{
					sub[sub_ptr + i] += Inlines.FLOAT2INT16(x[(i + offset) * C + c2]);
				}
			}
			else if (c2 == -2)
			{
				for (int j = 1; j < C; j++)
				{
					int num = j;
					for (int i = 0; i < subframe; i++)
					{
						sub[sub_ptr + i] += Inlines.FLOAT2INT16(x[(i + offset) * C + num]);
					}
				}
			}
			int num2 = 4096;
			num2 = ((C != -2) ? (num2 / 2) : (num2 / C));
			for (int i = 0; i < subframe; i++)
			{
				sub[sub_ptr + i] *= num2;
			}
		}

		internal static void downmix_int(ReadOnlySpan<short> x, Span<int> sub, int sub_ptr, int subframe, int offset, int c1, int c2, int C)
		{
			for (int i = 0; i < subframe; i++)
			{
				sub[i + sub_ptr] = x[(i + offset) * C + c1];
			}
			if (c2 > -1)
			{
				for (int i = 0; i < subframe; i++)
				{
					sub[i + sub_ptr] += x[(i + offset) * C + c2];
				}
			}
			else if (c2 == -2)
			{
				for (int j = 1; j < C; j++)
				{
					for (int i = 0; i < subframe; i++)
					{
						sub[i + sub_ptr] += x[(i + offset) * C + j];
					}
				}
			}
			int num = 4096;
			num = ((C != -2) ? (num / 2) : (num / C));
			for (int i = 0; i < subframe; i++)
			{
				sub[i + sub_ptr] *= num;
			}
		}
	}
	internal static class MultiLayerPerceptron
	{
		private const int MAX_NEURONS = 100;

		internal static float tansig_approx(float x)
		{
			float num = 1f;
			if (!(x < 8f))
			{
				return 1f;
			}
			if (!(x > -8f))
			{
				return -1f;
			}
			if (x < 0f)
			{
				x = 0f - x;
				num = -1f;
			}
			int num2 = (int)Math.Floor(0.5f + 25f * x);
			x -= 0.04f * (float)num2;
			float num3 = Tables.tansig_table[num2];
			float num4 = 1f - num3 * num3;
			num3 += x * num4 * (1f - num3 * x);
			return num * num3;
		}

		internal static void mlp_process(MLP m, float[] input, float[] output)
		{
			float[] array = new float[100];
			float[] weights = m.weights;
			int num = 0;
			for (int i = 0; i < m.topo[1]; i++)
			{
				float num2 = weights[num];
				num++;
				for (int j = 0; j < m.topo[0]; j++)
				{
					num2 += input[j] * weights[num];
					num++;
				}
				array[i] = tansig_approx(num2);
			}
			for (int i = 0; i < m.topo[2]; i++)
			{
				float num3 = weights[num];
				num++;
				for (int k = 0; k < m.topo[1]; k++)
				{
					num3 += array[k] * weights[num];
					num++;
				}
				output[i] = tansig_approx(num3);
			}
		}
	}
	internal static class OpusCompare
	{
		private const int NBANDS = 21;

		private const int NFREQS = 240;

		private const int TEST_WIN_SIZE = 480;

		private const int TEST_WIN_STEP = 120;

		private static readonly int[] BANDS = new int[22]
		{
			0, 2, 4, 6, 8, 10, 12, 14, 16, 20,
			24, 28, 32, 40, 48, 56, 68, 80, 96, 120,
			156, 200
		};

		private static void band_energy(Pointer<float> _out, Pointer<float> _ps, Pointer<int> _bands, int _nbands, Pointer<float> _in, int _nchannels, int _nframes, int _window_sz, int _step, int _downsample)
		{
			Pointer<float> pointer = Concentus.Common.CPlusPlus.Pointer.Malloc<float>((3 + _nchannels) * _window_sz);
			Pointer<float> pointer2 = pointer.Point(_window_sz);
			Pointer<float> pointer3 = pointer2.Point(_window_sz);
			Pointer<float> pointer4 = pointer3.Point(_window_sz);
			int num = _window_sz / 2;
			for (int i = 0; i < _window_sz; i++)
			{
				pointer[i] = (float)(0.5 - 0.5 * Math.Cos(Math.PI * 2.0 / (double)(_window_sz - 1) * (double)i));
			}
			for (int i = 0; i < _window_sz; i++)
			{
				pointer2[i] = (float)Math.Cos(Math.PI * 2.0 / (double)_window_sz * (double)i);
			}
			for (int i = 0; i < _window_sz; i++)
			{
				pointer3[i] = (float)Math.Sin(Math.PI * 2.0 / (double)_window_sz * (double)i);
			}
			for (int j = 0; j < _nframes; j++)
			{
				for (int k = 0; k < _nchannels; k++)
				{
					for (int l = 0; l < _window_sz; l++)
					{
						pointer4[k * _window_sz + l] = pointer[l] * _in[(j * _step + l) * _nchannels + k];
					}
				}
				int i;
				for (int m = (i = 0); m < _nbands; m++)
				{
					float[] array = new float[2];
					for (; i < _bands[m + 1]; i++)
					{
						for (int k = 0; k < _nchannels; k++)
						{
							int num2 = 0;
							float num3;
							float num4 = (num3 = 0f);
							for (int l = 0; l < _window_sz; l++)
							{
								num4 += pointer2[num2] * pointer4[k * _window_sz + l];
								num3 -= pointer3[num2] * pointer4[k * _window_sz + l];
								num2 += i;
								if (num2 >= _window_sz)
								{
									num2 -= _window_sz;
								}
							}
							num4 *= (float)_downsample;
							num3 *= (float)_downsample;
							_ps[(j * num + i) * _nchannels + k] = num4 * num4 + num3 * num3 + 100000f;
							array[k] += _ps[(j * num + i) * _nchannels + k];
						}
					}
					if (_out != null)
					{
						_out[(j * _nbands + m) * _nchannels] = array[0] / (float)(_bands[m + 1] - _bands[m]);
						if (_nchannels == 2)
						{
							_out[(j * _nbands + m) * _nchannels + 1] = array[1] / (float)(_bands[m + 1] - _bands[m]);
						}
					}
				}
			}
		}

		internal static float compare(float[] x, float[] y, int nchannels, int rate = 48000)
		{
			int num = x.Length;
			int num2 = y.Length;
			int num3 = 21;
			int num4 = 240;
			if (rate != 8000 && rate != 12000 && rate != 16000 && rate != 24000 && rate != 48000)
			{
				throw new ArgumentException("Sampling rate must be 8000, 12000, 16000, 24000, or 48000\n");
			}
			int num5;
			if (rate != 48000)
			{
				num5 = 48000 / rate;
				switch (rate)
				{
				case 8000:
					num3 = 13;
					break;
				case 12000:
					num3 = 15;
					break;
				case 16000:
					num3 = 17;
					break;
				case 24000:
					num3 = 19;
					break;
				}
				num4 = 240 / num5;
			}
			else
			{
				num5 = 1;
			}
			if (num != num2 * num5)
			{
				throw new ArgumentException("Sample counts do not match");
			}
			if (num < 480)
			{
				throw new ArgumentException("Insufficient sample data");
			}
			int num6 = (num - 480 + 120) / 120;
			Pointer<float> pointer = Concentus.Common.CPlusPlus.Pointer.Malloc<float>(num6 * 21 * nchannels);
			Pointer<float> pointer2 = Concentus.Common.CPlusPlus.Pointer.Malloc<float>(num6 * 240 * nchannels);
			Pointer<float> pointer3 = Concentus.Common.CPlusPlus.Pointer.Malloc<float>(num6 * num4 * nchannels);
			band_energy(pointer, pointer2, BANDS.GetPointer(), 21, x.GetPointer(), nchannels, num6, 480, 120, 1);
			band_energy(null, pointer3, BANDS.GetPointer(), num3, y.GetPointer(), nchannels, num6, 480 / num5, 120 / num5, num5);
			for (int i = 0; i < num6; i++)
			{
				int j;
				for (j = 1; j < 21; j++)
				{
					for (int k = 0; k < nchannels; k++)
					{
						pointer[(i * 21 + j) * nchannels + k] += 0.1f * pointer[(i * 21 + j - 1) * nchannels + k];
					}
				}
				j = 20;
				while (j-- > 0)
				{
					for (int k = 0; k < nchannels; k++)
					{
						pointer[(i * 21 + j) * nchannels + k] += 0.03f * pointer[(i * 21 + j + 1) * nchannels + k];
					}
				}
				if (i > 0)
				{
					for (j = 0; j < 21; j++)
					{
						for (int k = 0; k < nchannels; k++)
						{
							pointer[(i * 21 + j) * nchannels + k] += 0.5f * pointer[((i - 1) * 21 + j) * nchannels + k];
						}
					}
				}
				if (nchannels == 2)
				{
					for (j = 0; j < 21; j++)
					{
						float num7 = pointer[(i * 21 + j) * nchannels];
						float num8 = pointer[(i * 21 + j) * nchannels + 1];
						pointer[(i * 21 + j) * nchannels] += 0.01f * num8;
						pointer[(i * 21 + j) * nchannels + 1] += 0.01f * num7;
					}
				}
				for (j = 0; j < num3; j++)
				{
					for (int l = BANDS[j]; l < BANDS[j + 1]; l++)
					{
						for (int k = 0; k < nchannels; k++)
						{
							pointer2[(i * 240 + l) * nchannels + k] += 0.1f * pointer[(i * 21 + j) * nchannels + k];
							pointer3[(i * num4 + l) * nchannels + k] += 0.1f * pointer[(i * 21 + j) * nchannels + k];
						}
					}
				}
			}
			for (int j = 0; j < num3; j++)
			{
				for (int l = BANDS[j]; l < BANDS[j + 1]; l++)
				{
					for (int k = 0; k < nchannels; k++)
					{
						float num9 = pointer2[l * nchannels + k];
						float num10 = pointer3[l * nchannels + k];
						for (int i = 1; i < num6; i++)
						{
							float num11 = pointer2[(i * 240 + l) * nchannels + k];
							float num12 = pointer3[(i * num4 + l) * nchannels + k];
							pointer2[(i * 240 + l) * nchannels + k] += num9;
							pointer3[(i * num4 + l) * nchannels + k] += num10;
							num9 = num11;
							num10 = num12;
						}
					}
				}
			}
			int num13 = rate switch
			{
				48000 => BANDS[21], 
				12000 => BANDS[num3], 
				_ => BANDS[num3] - 3, 
			};
			double num14 = 0.0;
			for (int i = 0; i < num6; i++)
			{
				double num15 = 0.0;
				for (int j = 0; j < num3; j++)
				{
					double num16 = 0.0;
					for (int l = BANDS[j]; l < BANDS[j + 1] && l < num13; l++)
					{
						for (int k = 0; k < nchannels; k++)
						{
							float num17 = pointer3[(i * num4 + l) * nchannels + k] / pointer2[(i * 240 + l) * nchannels + k];
							float num18 = num17 - (float)Math.Log(num17) - 1f;
							if (l >= 79 && l <= 81)
							{
								num18 *= 0.1f;
							}
							if (l == 80)
							{
								num18 *= 0.1f;
							}
							num16 += (double)num18;
						}
					}
					num16 /= (double)((BANDS[j + 1] - BANDS[j]) * nchannels);
					num15 += num16 * num16;
				}
				num15 /= 21.0;
				num15 *= num15;
				num14 += num15 * num15;
			}
			num14 = Math.Pow(num14 / (double)num6, 0.0625);
			float result = (float)(100.0 * (1.0 - 0.5 * Math.Log(1.0 + num14) / Math.Log(1.13)));
			_ = 0f;
			return result;
		}
	}
	internal static class OpusConstants
	{
		internal const int OPUS_AUTO = -1000;

		internal const int OPUS_BITRATE_MAX = -1;

		internal const int NB_FRAMES = 8;

		internal const int NB_TBANDS = 18;

		internal const int NB_TOT_BANDS = 21;

		internal const int NB_TONAL_SKIP_BANDS = 9;

		internal const int ANALYSIS_BUF_SIZE = 720;

		internal const int DETECT_SIZE = 200;

		internal const int MAX_ENCODER_BUFFER = 480;
	}
	public class OpusException : Exception
	{
		public int OpusErrorCode { get; private set; }

		internal OpusException()
			: base("Unknown error")
		{
			OpusErrorCode = -100;
		}

		internal OpusException(string message)
			: base(message)
		{
			OpusErrorCode = -100;
		}

		internal OpusException(int opusError)
			: base(CodecHelpers.opus_strerror(opusError))
		{
			OpusErrorCode = opusError;
		}

		internal OpusException(string message, int opusError)
			: base(message)
		{
			OpusErrorCode = opusError;
		}
	}
	internal static class OpusMultistream
	{
		internal static int validate_layout(ChannelLayout layout)
		{
			int num = layout.nb_streams + layout.nb_coupled_streams;
			if (num > 255)
			{
				return 0;
			}
			for (int i = 0; i < layout.nb_channels; i++)
			{
				if (layout.mapping[i] >= num && layout.mapping[i] != byte.MaxValue)
				{
					return 0;
				}
			}
			return 1;
		}

		internal static int get_left_channel(ChannelLayout layout, int stream_id, int prev)
		{
			for (int i = ((prev >= 0) ? (prev + 1) : 0); i < layout.nb_channels; i++)
			{
				if (layout.mapping[i] == stream_id * 2)
				{
					return i;
				}
			}
			return -1;
		}

		internal static int get_right_channel(ChannelLayout layout, int stream_id, int prev)
		{
			for (int i = ((prev >= 0) ? (prev + 1) : 0); i < layout.nb_channels; i++)
			{
				if (layout.mapping[i] == stream_id * 2 + 1)
				{
					return i;
				}
			}
			return -1;
		}

		internal static int get_mono_channel(ChannelLayout layout, int stream_id, int prev)
		{
			for (int i = ((prev >= 0) ? (prev + 1) : 0); i < layout.nb_channels; i++)
			{
				if (layout.mapping[i] == stream_id + layout.nb_coupled_streams)
				{
					return i;
				}
			}
			return -1;
		}
	}
	internal static class Tables
	{
		internal static readonly float[] dct_table = new float[128]
		{
			0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f,
			0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.25f, 0.351851f, 0.33833f, 0.311806f, 0.2733f,
			0.224292f, 0.166664f, 0.102631f, 0.034654f, -0.034654f, -0.102631f, -0.166664f, -0.224292f, -0.2733f, -0.311806f,
			-0.33833f, -0.351851f, 0.34676f, 0.293969f, 0.196424f, 0.068975f, -0.068975f, -0.196424f, -0.293969f, -0.34676f,
			-0.34676f, -0.293969f, -0.196424f, -0.068975f, 0.068975f, 0.196424f, 0.293969f, 0.34676f, 0.33833f, 0.224292f,
			0.034654f, -0.166664f, -0.311806f, -0.351851f, -0.2733f, -0.102631f, 0.102631f, 0.2733f, 0.351851f, 0.311806f,
			0.166664f, -0.034654f, -0.224292f, -0.33833f, 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f,
			0.135299f, 0.326641f, 0.326641f, 0.135299f, -0.135299f, -0.326641f, -0.326641f, -0.135299f, 0.135299f, 0.326641f,
			0.311806f, 0.034654f, -0.2733f, -0.33833f, -0.102631f, 0.224292f, 0.351851f, 0.166664f, -0.166664f, -0.351851f,
			-0.224292f, 0.102631f, 0.33833f, 0.2733f, -0.034654f, -0.311806f, 0.293969f, -0.068975f, -0.34676f, -0.196424f,
			0.196424f, 0.34676f, 0.068975f, -0.293969f, -0.293969f, 0.068975f, 0.34676f, 0.196424f, -0.196424f, -0.34676f,
			-0.068975f, 0.293969f, 0.2733f, -0.166664f, -0.33833f, 0.034654f, 0.351851f, 0.102631f, -0.311806f, -0.224292f,
			0.224292f, 0.311806f, -0.102631f, -0.351851f, -0.034654f, 0.33833f, 0.166664f, -0.2733f
		};

		internal static readonly float[] analysis_window = new float[240]
		{
			4.3E-05f, 0.000171f, 0.000385f, 0.000685f, 0.001071f, 0.001541f, 0.002098f, 0.002739f, 0.003466f, 0.004278f,
			0.005174f, 0.006156f, 0.007222f, 0.008373f, 0.009607f, 0.010926f, 0.012329f, 0.013815f, 0.015385f, 0.017037f,
			0.018772f, 0.02059f, 0.02249f, 0.024472f, 0.026535f, 0.028679f, 0.030904f, 0.03321f, 0.035595f, 0.03806f,
			0.040604f, 0.043227f, 0.045928f, 0.048707f, 0.051564f, 0.054497f, 0.057506f, 0.060591f, 0.063752f, 0.066987f,
			0.070297f, 0.07368f, 0.077136f, 0.080665f, 0.084265f, 0.087937f, 0.091679f, 0.095492f, 0.099373f, 0.103323f,
			0.107342f, 0.111427f, 0.115579f, 0.119797f, 0.12408f, 0.128428f, 0.132839f, 0.137313f, 0.141849f, 0.146447f,
			0.151105f, 0.155823f, 0.1606f, 0.165435f, 0.170327f, 0.175276f, 0.18028f, 0.18534f, 0.190453f, 0.195619f,
			0.200838f, 0.206107f, 0.211427f, 0.216797f, 0.222215f, 0.22768f, 0.233193f, 0.238751f, 0.244353f, 0.25f,
			0.255689f, 0.261421f, 0.267193f, 0.273005f, 0.278856f, 0.284744f, 0.29067f, 0.296632f, 0.302628f, 0.308658f,
			0.314721f, 0.320816f, 0.326941f, 0.333097f, 0.33928f, 0.345492f, 0.351729f, 0.357992f, 0.36428f, 0.37059f,
			0.376923f, 0.383277f, 0.389651f, 0.396044f, 0.402455f, 0.408882f, 0.415325f, 0.421783f, 0.428254f, 0.434737f,
			0.441231f, 0.447736f, 0.454249f, 0.46077f, 0.467298f, 0.473832f, 0.48037f, 0.486912f, 0.493455f, 0.5f,
			0.506545f, 0.513088f, 0.51963f, 0.526168f, 0.532702f, 0.53923f, 0.545751f, 0.552264f, 0.558769f, 0.565263f,
			0.571746f, 0.578217f, 0.584675f, 0.591118f, 0.597545f, 0.603956f, 0.610349f, 0.616723f, 0.623077f, 0.62941f,
			0.63572f, 0.642008f, 0.648271f, 0.654508f, 0.66072f, 0.666903f, 0.673059f, 0.679184f, 0.685279f, 0.691342f,
			0.697372f, 0.703368f, 0.70933f, 0.715256f, 0.721144f, 0.726995f, 0.732807f, 0.738579f, 0.744311f, 0.75f,
			0.755647f, 0.761249f, 0.766807f, 0.77232f, 0.777785f, 0.783203f, 0.788573f, 0.793893f, 0.799162f, 0.804381f,
			0.809547f, 0.81466f, 0.81972f, 0.824724f, 0.829673f, 0.834565f, 0.8394f, 0.844177f, 0.848895f, 0.853553f,
			0.858151f, 0.862687f, 0.867161f, 0.871572f, 0.87592f, 0.880203f, 0.884421f, 0.888573f, 0.892658f, 0.896677f,
			0.900627f, 0.904508f, 0.908321f, 0.912063f, 0.915735f, 0.919335f, 0.922864f, 0.92632f, 0.929703f, 0.933013f,
			0.936248f, 0.939409f, 0.942494f, 0.945503f, 0.948436f, 0.951293f, 0.954072f, 0.956773f, 0.959396f, 0.96194f,
			0.964405f, 0.96679f, 0.969096f, 0.971321f, 0.973465f, 0.975528f, 0.97751f, 0.97941f, 0.981228f, 0.982963f,
			0.984615f, 0.986185f, 0.987671f, 0.989074f, 0.990393f, 0.991627f, 0.992778f, 0.993844f, 0.994826f, 0.995722f,
			0.996534f, 0.997261f, 0.997902f, 0.998459f, 0.998929f, 0.999315f, 0.999615f, 0.999829f, 0.999957f, 1f
		};

		internal static readonly int[] tbands = new int[19]
		{
			2, 4, 6, 8, 10, 12, 14, 16, 20, 24,
			28, 32, 40, 48, 56, 68, 80, 96, 120
		};

		internal static readonly int[] extra_bands = new int[22]
		{
			1, 2, 4, 6, 8, 10, 12, 14, 16, 20,
			24, 28, 32, 40, 48, 56, 68, 80, 96, 120,
			160, 200
		};

		internal static readonly float[] weights = new float[422]
		{
			-0.0941125f,
			-0.302976f,
			-0.603555f,
			-0.19393f,
			-0.185983f,
			-0.601617f,
			-0.0465317f,
			-0.114563f,
			-0.103599f,
			-0.618938f,
			-0.317859f,
			-0.169949f,
			-0.0702885f,
			0.148065f,
			0.409524f,
			0.548432f,
			0.367649f,
			-0.494393f,
			0.764306f,
			-1.83957f,
			0.170849f,
			12.786f,
			-1.08848f,
			-1.27284f,
			-16.2606f,
			24.1773f,
			-5.57454f,
			-0.17276f,
			-0.163388f,
			-0.224421f,
			-0.0948944f,
			-0.0728695f,
			-0.26557f,
			-0.100283f,
			-0.0515459f,
			-0.146142f,
			-0.120674f,
			-0.180655f,
			0.12857f,
			0.442138f,
			-0.493735f,
			0.167767f,
			0.206699f,
			-0.197567f,
			0.417999f,
			1.50364f,
			-0.773341f,
			-10.0401f,
			0.401872f,
			2.97966f,
			15.2165f,
			-1.88905f,
			-1.19254f,
			0.0285397f,
			-0.00405139f,
			0.0707565f,
			0.00825699f,
			-0.0927269f,
			-0.010393f,
			-0.00428882f,
			-0.00489743f,
			-0.0709731f,
			-0.00255992f,
			0.0395619f,
			0.226424f,
			0.0325231f,
			0.162175f,
			-0.100118f,
			0.485789f,
			0.12697f,
			0.285937f,
			0.0155637f,
			0.10546f,
			3.05558f,
			1.15059f,
			-1.00904f,
			-1.83088f,
			3.31766f,
			-3.42516f,
			-0.119135f,
			-0.0405654f,
			0.00690068f,
			0.0179877f,
			-0.0382487f,
			0.00597941f,
			-0.0183611f,
			0.00190395f,
			-0.144322f,
			-0.0435671f,
			0.000990594f,
			0.221087f,
			0.142405f,
			0.484066f,
			0.404395f,
			0.511955f,
			-0.237255f,
			0.241742f,
			0.35045f,
			-0.699428f,
			10.3993f,
			2.6507f,
			-2.43459f,
			-4.18838f,
			1.05928f,
			1.71067f,
			0.00667811f,
			-0.0721335f,
			-0.0397346f,
			0.0362704f,
			-0.11496f,
			-0.0235776f,
			0.0082161f,
			-0.0141741f,
			-0.0329699f,
			-0.0354253f,
			0.00277404f,
			-0.290654f,
			-1.14767f,
			-0.319157f,
			-0.686544f,
			0.36897f,
			0.478899f,
			0.182579f,
			-0.411069f,
			0.881104f,
			-4.60683f,
			1.4697f,
			325f / (356f * MathF.E),
			-1.81905f,
			-30.1699f,
			5.55225f,
			0.0019508f,
			-0.123576f,
			-0.0727332f,
			-0.0641597f,
			-0.0534458f,
			-0.108166f,
			-0.0937368f,
			-0.0697883f,
			-0.0275475f,
			-0.192309f,
			-0.110074f,
			0.285375f,
			-0.405597f,
			0.0926724f,
			-0.287881f,
			-0.851193f,
			-0.099493f,
			-0.233764f,
			-1.2852f,
			1.13611f,
			3.12168f,
			-0.0699f,
			-1.86216f,
			2.65292f,
			-7.31036f,
			2.44776f,
			-0.00111802f,
			-0.0632786f,
			-0.0376296f,
			-0.149851f,
			0.142963f,
			0.184368f,
			0.123433f,
			0.0756158f,
			0.117312f,
			0.0933395f,
			0.0692163f,
			0.0842592f,
			0.0704683f,
			0.0589963f,
			0.0942205f,
			-0.448862f,
			0.0262677f,
			0.270352f,
			-0.262317f,
			0.172586f,
			2.00227f,
			-0.159216f,
			0.038422f,
			10.2073f,
			4.15536f,
			-2.3407f,
			-0.0550265f,
			0.00964792f,
			-0.141336f,
			0.0274501f,
			0.0343921f,
			-0.0487428f,
			0.0950172f,
			-0.00775017f,
			-0.0372492f,
			-0.00548121f,
			-0.0663695f,
			0.0960506f,
			-0.200008f,
			-0.0412827f,
			0.58728f,
			0.0515787f,
			0.337254f,
			0.855024f,
			0.668371f,
			-0.114904f,
			-3.62962f,
			-0.467477f,
			-0.215472f,
			2.61537f,
			0.406117f,
			-1.36373f,
			0.0425394f,
			0.12208f,
			0.0934502f,
			0.123055f,
			0.0340935f,
			-0.142466f,
			0.035037f,
			-0.0490666f,
			0.0733208f,
			0.0576672f,
			0.123984f,
			-0.0517194f,
			-0.253018f,
			0.590565f,
			0.145849f,
			0.315185f,
			0.221534f,
			-0.149081f,
			0.216161f,
			-0.349575f,
			24.5664f,
			-0.994196f,
			0.614289f,
			-18.7905f,
			-2.83277f,
			-0.716801f,
			-0.347201f,
			0.479515f,
			-0.246027f,
			0.0758683f,
			0.137293f,
			-0.17781f,
			0.118751f,
			-0.00108329f,
			-0.237334f,
			0.355732f,
			-0.12991f,
			-0.0547627f,
			-0.318576f,
			-0.325524f,
			0.180494f,
			-0.0625604f,
			0.141219f,
			0.344064f,
			0.37658f,
			-0.591772f,
			5.8427f,
			-0.38075f,
			0.221894f,
			-1.41934f,
			-1879430f,
			1.34114f,
			0.0283355f,
			-0.0447856f,
			-0.0211466f,
			-0.0256927f,
			0.0139618f,
			0.0207934f,
			-0.0107666f,
			0.0110969f,
			0.0586069f,
			-0.0253545f,
			-0.0328433f,
			0.11872f,
			-0.216943f,
			0.145748f,
			0.119808f,
			-0.0915211f,
			-0.120647f,
			-0.0787719f,
			-0.143644f,
			-0.595116f,
			-1.152f,
			-1.25335f,
			-1.17092f,
			4.34023f,
			-975268f,
			-1.37033f,
			-0.0401123f,
			0.210602f,
			-0.136656f,
			0.135962f,
			-0.0523293f,
			0.0444604f,
			0.0143928f,
			0.00412666f,
			-0.0193003f,
			0.218452f,
			-0.110204f,
			-2.02563f,
			0.918238f,
			-2.45362f,
			1.19542f,
			-0.061362f,
			-1.92243f,
			0.308111f,
			0.49764f,
			0.912356f,
			0.209272f,
			-2.34525f,
			2.19326f,
			-6.47121f,
			1.69771f,
			-0.725123f,
			0.0118929f,
			0.0377944f,
			0.0554003f,
			0.0226452f,
			-0.0704421f,
			-0.0300309f,
			0.0122978f,
			-0.0041782f,
			-0.0686612f,
			0.0313115f,
			0.039111f,
			0.364111f,
			-0.0945548f,
			0.0229876f,
			-0.17414f,
			0.329795f,
			0.114714f,
			0.30022f,
			0.106997f,
			0.132355f,
			5.79932f,
			0.908058f,
			-0.905324f,
			-3.3561f,
			0.190647f,
			0.184211f,
			-0.673648f,
			0.231807f,
			-0.0586222f,
			0.230752f,
			-0.438277f,
			0.245857f,
			-0.17215f,
			0.0876383f,
			-0.720512f,
			0.162515f,
			0.0170571f,
			0.101781f,
			0.388477f,
			1.32931f,
			1.08548f,
			-0.936301f,
			-2.36958f,
			-6.71988f,
			-3.44376f,
			2.13818f,
			14.2318f,
			4.91459f,
			-3.09052f,
			-9.69191f,
			-0.768234f,
			1.79604f,
			0.0549653f,
			0.163399f,
			0.0797025f,
			0.0343933f,
			-0.0555876f,
			-0.00505673f,
			0.0187258f,
			0.0326628f,
			0.0231486f,
			0.15573f,
			0.0476223f,
			-0.254824f,
			1.60155f,
			-0.801221f,
			2.55496f,
			0.737629f,
			-1.36249f,
			-0.695463f,
			-2.44301f,
			-1.73188f,
			3.95279f,
			1.89068f,
			0.486087f,
			-11.3343f,
			3941600f,
			-0.381439f,
			0.12115f,
			-0.906927f,
			2.93878f,
			1.6388f,
			0.882811f,
			0.874344f,
			1.21726f,
			-0.874545f,
			0.321706f,
			0.785055f,
			0.946558f,
			-0.575066f,
			-3.46553f,
			0.884905f,
			0.0924047f,
			-9.90712f,
			0.391338f,
			0.160103f,
			-2.04954f,
			4.1455f,
			0.0684029f,
			-0.144761f,
			-0.285282f,
			0.379244f,
			-1.1584f,
			-0.0277241f,
			-9.85f,
			-4.82386f,
			3.71333f,
			3.87308f,
			3.52558f
		};

		internal static readonly int[] topo = new int[3] { 25, 15, 2 };

		internal static readonly MLP net = new MLP
		{
			layers = 3,
			topo = topo,
			weights = weights
		};

		internal static readonly float[] tansig_table = new float[201]
		{
			0f, 0.039979f, 0.07983f, 0.119427f, 0.158649f, 0.197375f, 0.235496f, 0.272905f, 0.309507f, 0.345214f,
			0.379949f, 0.413644f, 0.446244f, 0.4777f, 0.507977f, 0.53705f, 0.5649f, 0.591519f, 0.616909f, 0.641077f,
			0.664037f, 0.685809f, 0.706419f, 0.725897f, 0.744277f, 0.761594f, 0.777888f, 0.793199f, 0.807569f, 0.82104f,
			0.833655f, 0.845456f, 0.856485f, 0.866784f, 0.876393f, 0.885352f, 0.893698f, 0.901468f, 0.908698f, 0.91542f,
			0.921669f, 0.927473f, 0.932862f, 0.937863f, 0.942503f, 0.946806f, 0.950795f, 0.954492f, 0.957917f, 0.96109f,
			0.964028f, 0.966747f, 0.969265f, 0.971594f, 0.973749f, 0.975743f, 0.977587f, 0.979293f, 0.980869f, 0.982327f,
			0.983675f, 0.984921f, 0.986072f, 0.987136f, 0.988119f, 0.989027f, 0.989867f, 0.990642f, 0.991359f, 0.99202f,
			0.992631f, 0.993196f, 0.993718f, 0.994199f, 0.994644f, 0.995055f, 0.995434f, 0.995784f, 0.996108f, 0.996407f,
			0.996682f, 0.996937f, 0.997172f, 0.997389f, 0.99759f, 0.997775f, 0.997946f, 0.998104f, 0.998249f, 0.998384f,
			0.998508f, 0.998623f, 0.998728f, 0.998826f, 0.998916f, 0.999f, 0.999076f, 0.999147f, 0.999213f, 0.999273f,
			0.999329f, 0.999381f, 0.999428f, 0.999472f, 0.999513f, 0.99955f, 0.999585f, 0.999617f, 0.999646f, 0.999673f,
			0.999699f, 0.999722f, 0.999743f, 0.999763f, 0.999781f, 0.999798f, 0.999813f, 0.999828f, 0.999841f, 0.999853f,
			0.999865f, 0.999875f, 0.999885f, 0.999893f, 0.999902f, 0.999909f, 0.999916f, 0.999923f, 0.999929f, 0.999934f,
			0.999939f, 0.999944f, 0.999948f, 0.999952f, 0.999956f, 0.999959f, 0.999962f, 0.999965f, 0.999968f, 0.99997f,
			0.999973f, 0.999975f, 0.999977f, 0.999978f, 0.99998f, 0.999982f, 0.999983f, 0.999984f, 0.999986f, 0.999987f,
			0.999988f, 0.999989f, 0.99999f, 0.99999f, 0.999991f, 0.999992f, 0.999992f, 0.999993f, 0.999994f, 0.999994f,
			0.999994f, 0.999995f, 0.999995f, 0.999996f, 0.999996f, 0.999996f, 0.999997f, 0.999997f, 0.999997f, 0.999997f,
			0.999997f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999998f, 0.999999f, 0.999999f, 0.999999f,
			0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f, 0.999999f,
			1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f,
			1f
		};

		internal static readonly int[] mono_voice_bandwidth_thresholds = new int[8] { 11000, 1000, 14000, 1000, 17000, 1000, 21000, 2000 };

		internal static readonly int[] mono_music_bandwidth_thresholds = new int[8] { 12000, 1000, 15000, 1000, 18000, 2000, 22000, 2000 };

		internal static readonly int[] stereo_voice_bandwidth_thresholds = new int[8] { 11000, 1000, 14000, 1000, 21000, 2000, 28000, 2000 };

		internal static readonly int[] stereo_music_bandwidth_thresholds = new int[8] { 12000, 1000, 18000, 2000, 21000, 2000, 30000, 2000 };

		internal const int stereo_voice_threshold = 30000;

		internal const int stereo_music_threshold = 30000;

		internal static readonly int[][] mode_thresholds = new int[2][]
		{
			new int[2] { 64000, 16000 },
			new int[2] { 36000, 16000 }
		};
	}
	public static class ResamplerFactory
	{
		public static IResampler CreateResampler(int numChannels, int inRate, int outRate, int quality, TextWriter logger = null)
		{
			return CreateResampler(numChannels, inRate, outRate, inRate, outRate, quality, logger);
		}

		public static IResampler CreateResampler(int numChannels, int ratioNum, int ratioDen, int inRate, int outRate, int quality, TextWriter logger = null)
		{
			if (numChannels <= 0)
			{
				throw new ArgumentOutOfRangeException("numChannels");
			}
			if (ratioNum <= 0)
			{
				throw new ArgumentOutOfRangeException("ratioNum");
			}
			if (ratioDen <= 0)
			{
				throw new ArgumentOutOfRangeException("ratioDen");
			}
			if (inRate <= 0)
			{
				throw new ArgumentOutOfRangeException("inRate");
			}
			if (outRate <= 0)
			{
				throw new ArgumentOutOfRangeException("outRate");
			}
			if (quality < 0 || quality > 10)
			{
				throw new ArgumentOutOfRangeException("quality", "Quality must be between 0 and 10");
			}
			return new SpeexResampler(numChannels, ratioNum, ratioDen, inRate, outRate, quality);
		}
	}
}
namespace Concentus.Silk
{
	internal static class ApplySineWindow
	{
		private static readonly short[] freq_table_Q16 = new short[27]
		{
			12111, 9804, 8235, 7100, 6239, 5565, 5022, 4575, 4202, 3885,
			3612, 3375, 3167, 2984, 2820, 2674, 2542, 2422, 2313, 2214,
			2123, 2038, 1961, 1889, 1822, 1760, 1702
		};

		internal static void silk_apply_sine_window(short[] px_win, int px_win_ptr, short[] px, int px_ptr, int win_type, int length)
		{
			int num = (length >> 2) - 4;
			int num2 = freq_table_Q16[num];
			int num3 = Inlines.silk_SMULWB(num2, -num2);
			int num4;
			int num5;
			if (win_type == 1)
			{
				num4 = 0;
				num5 = num2 + Inlines.silk_RSHIFT(length, 3);
			}
			else
			{
				num4 = 65536;
				num5 = 65536 + Inlines.silk_RSHIFT(num3, 1) + Inlines.silk_RSHIFT(length, 4);
			}
			for (num = 0; num < length; num += 4)
			{
				int num6 = px_win_ptr + num;
				int num7 = px_ptr + num;
				px_win[num6] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(num4 + num5, 1), px[num7]);
				px_win[num6 + 1] = (short)Inlines.silk_SMULWB(num5, px[num7 + 1]);
				num4 = Inlines.silk_SMULWB(num5, num3) + Inlines.silk_LSHIFT(num5, 1) - num4 + 1;
				num4 = Inlines.silk_min(num4, 65536);
				px_win[num6 + 2] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(num4 + num5, 1), px[num7 + 2]);
				px_win[num6 + 3] = (short)Inlines.silk_SMULWB(num4, px[num7 + 3]);
				num5 = Inlines.silk_SMULWB(num4, num3) + Inlines.silk_LSHIFT(num4, 1) - num5;
				num5 = Inlines.silk_min(num5, 65536);
			}
		}
	}
	internal static class BurgModified
	{
		private const int MAX_FRAME_SIZE = 384;

		private const int QA = 25;

		private const int N_BITS_HEAD_ROOM = 2;

		private const int MIN_RSHIFTS = -16;

		private const int MAX_RSHIFTS = 7;

		internal static void silk_burg_modified(BoxedValueInt res_nrg, BoxedValueInt res_nrg_Q, int[] A_Q16, short[] x, int x_ptr, int minInvGain_Q30, int subfr_length, int nb_subfr, int D)
		{
			int[] array = new int[16];
			int[] array2 = new int[16];
			int[] array3 = new int[16];
			int[] array4 = new int[17];
			int[] array5 = new int[17];
			int[] array6 = new int[16];
			long num = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr);
			int num2 = Inlines.silk_CLZ64(num);
			int num3 = 35 - num2;
			if (num3 > 7)
			{
				num3 = 7;
			}
			if (num3 < -16)
			{
				num3 = -16;
			}
			int num4 = (int)((num3 <= 0) ? Inlines.silk_LSHIFT32((int)num, -num3) : Inlines.silk_RSHIFT64(num, num3));
			array5[0] = (array4[0] = num4 + Inlines.silk_SMMUL(42950, num4) + 1);
			Arrays.MemSetInt(array, 0, 16);
			if (num3 > 0)
			{
				for (int i = 0; i < nb_subfr; i++)
				{
					int num5 = x_ptr + i * subfr_length;
					for (int j = 1; j < D + 1; j++)
					{
						array[j - 1] += (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, num5, x, num5 + j, subfr_length - j), num3);
					}
				}
			}
			else
			{
				for (int i = 0; i < nb_subfr; i++)
				{
					int num5 = x_ptr + i * subfr_length;
					CeltPitchXCorr.pitch_xcorr(x, num5, x, num5 + 1, array6, subfr_length - D, D);
					for (int j = 1; j < D + 1; j++)
					{
						int k = j + subfr_length - D;
						int num6 = 0;
						for (; k < subfr_length; k++)
						{
							num6 = Inlines.MAC16_16(num6, x[num5 + k], x[num5 + k - j]);
						}
						array6[j - 1] += num6;
					}
					for (int j = 1; j < D + 1; j++)
					{
						array[j - 1] += Inlines.silk_LSHIFT32(array6[j - 1], -num3);
					}
				}
			}
			Arrays.MemCopy(array, 0, array2, 0, 16);
			array5[0] = (array4[0] = num4 + Inlines.silk_SMMUL(42950, num4) + 1);
			int num7 = 1073741824;
			int num8 = 0;
			for (int j = 0; j < D; j++)
			{
				int num9;
				int num10;
				if (num3 > -2)
				{
					for (int i = 0; i < nb_subfr; i++)
					{
						int num5 = x_ptr + i * subfr_length;
						int b = -Inlines.silk_LSHIFT32(x[num5 + j], 16 - num3);
						int b2 = -Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], 16 - num3);
						num9 = Inlines.silk_LSHIFT32(x[num5 + j], 9);
						num10 = Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], 9);
						for (int l = 0; l < j; l++)
						{
							array[l] = Inlines.silk_SMLAWB(array[l], b, x[num5 + j - l - 1]);
							array2[l] = Inlines.silk_SMLAWB(array2[l], b2, x[num5 + subfr_length - j + l]);
							int b3 = array3[l];
							num9 = Inlines.silk_SMLAWB(num9, b3, x[num5 + j - l - 1]);
							num10 = Inlines.silk_SMLAWB(num10, b3, x[num5 + subfr_length - j + l]);
						}
						num9 = Inlines.silk_LSHIFT32(-num9, 7 - num3);
						num10 = Inlines.silk_LSHIFT32(-num10, 7 - num3);
						for (int l = 0; l <= j; l++)
						{
							array4[l] = Inlines.silk_SMLAWB(array4[l], num9, x[num5 + j - l]);
							array5[l] = Inlines.silk_SMLAWB(array5[l], num10, x[num5 + subfr_length - j + l - 1]);
						}
					}
				}
				else
				{
					for (int i = 0; i < nb_subfr; i++)
					{
						int num5 = x_ptr + i * subfr_length;
						int b = -Inlines.silk_LSHIFT32(x[num5 + j], -num3);
						int b2 = -Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], -num3);
						num9 = Inlines.silk_LSHIFT32(x[num5 + j], 17);
						num10 = Inlines.silk_LSHIFT32(x[num5 + subfr_length - j - 1], 17);
						for (int l = 0; l < j; l++)
						{
							array[l] = Inlines.silk_MLA(array[l], b, x[num5 + j - l - 1]);
							array2[l] = Inlines.silk_MLA(array2[l], b2, x[num5 + subfr_length - j + l]);
							int c = Inlines.silk_RSHIFT_ROUND(array3[l], 8);
							num9 = Inlines.silk_MLA(num9, x[num5 + j - l - 1], c);
							num10 = Inlines.silk_MLA(num10, x[num5 + subfr_length - j + l], c);
						}
						num9 = -num9;
						num10 = -num10;
						for (int l = 0; l <= j; l++)
						{
							array4[l] = Inlines.silk_SMLAWW(array4[l], num9, Inlines.silk_LSHIFT32(x[num5 + j - l], -num3 - 1));
							array5[l] = Inlines.silk_SMLAWW(array5[l], num10, Inlines.silk_LSHIFT32(x[num5 + subfr_length - j + l - 1], -num3 - 1));
						}
					}
				}
				num9 = array[j];
				num10 = array2[j];
				int a = 0;
				int num11 = Inlines.silk_ADD32(array5[0], array4[0]);
				for (int l = 0; l < j; l++)
				{
					int b3 = array3[l];
					num2 = Inlines.silk_CLZ32(Inlines.silk_abs(b3)) - 1;
					num2 = Inlines.silk_min(7, num2);
					int c = Inlines.silk_LSHIFT32(b3, num2);
					num9 = Inlines.silk_ADD_LSHIFT32(num9, Inlines.silk_SMMUL(array2[j - l - 1], c), 7 - num2);
					num10 = Inlines.silk_ADD_LSHIFT32(num10, Inlines.silk_SMMUL(array[j - l - 1], c), 7 - num2);
					a = Inlines.silk_ADD_LSHIFT32(a, Inlines.silk_SMMUL(array5[j - l], c), 7 - num2);
					num11 = Inlines.silk_ADD_LSHIFT32(num11, Inlines.silk_SMMUL(Inlines.silk_ADD32(array5[l + 1], array4[l + 1]), c), 7 - num2);
				}
				array4[j + 1] = num9;
				array5[j + 1] = num10;
				a = Inlines.silk_ADD32(a, num10);
				a = Inlines.silk_LSHIFT32(-a, 1);
				int num12 = ((Inlines.silk_abs(a) >= num11) ? ((a > 0) ? int.MaxValue : int.MinValue) : Inlines.silk_DIV32_varQ(a, num11, 31));
				num9 = 1073741824 - Inlines.silk_SMMUL(num12, num12);
				num9 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(num7, num9), 2);
				if (num9 <= minInvGain_Q30)
				{
					num10 = 1073741824 - Inlines.silk_DIV32_varQ(minInvGain_Q30, num7, 30);
					num12 = Inlines.silk_SQRT_APPROX(num10);
					num12 = Inlines.silk_RSHIFT32(num12 + Inlines.silk_DIV32(num10, num12), 1);
					num12 = Inlines.silk_LSHIFT32(num12, 16);
					if (a < 0)
					{
						num12 = -num12;
					}
					num7 = minInvGain_Q30;
					num8 = 1;
				}
				else
				{
					num7 = num9;
				}
				for (int l = 0; l < j + 1 >> 1; l++)
				{
					num9 = array3[l];
					num10 = array3[j - l - 1];
					array3[l] = Inlines.silk_ADD_LSHIFT32(num9, Inlines.silk_SMMUL(num10, num12), 1);
					array3[j - l - 1] = Inlines.silk_ADD_LSHIFT32(num10, Inlines.silk_SMMUL(num9, num12), 1);
				}
				array3[j] = Inlines.silk_RSHIFT32(num12, 6);
				if (num8 != 0)
				{
					for (int l = j + 1; l < D; l++)
					{
						array3[l] = 0;
					}
					break;
				}
				for (int l = 0; l <= j + 1; l++)
				{
					num9 = array4[l];
					num10 = array5[j - l + 1];
					array4[l] = Inlines.silk_ADD_LSHIFT32(num9, Inlines.silk_SMMUL(num10, num12), 1);
					array5[j - l + 1] = Inlines.silk_ADD_LSHIFT32(num10, Inlines.silk_SMMUL(num9, num12), 1);
				}
			}
			if (num8 != 0)
			{
				for (int l = 0; l < D; l++)
				{
					A_Q16[l] = -Inlines.silk_RSHIFT_ROUND(array3[l], 9);
				}
				if (num3 > 0)
				{
					for (int i = 0; i < nb_subfr; i++)
					{
						int num5 = x_ptr + i * subfr_length;
						num4 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, num5, x, num5, D), num3);
					}
				}
				else
				{
					for (int i = 0; i < nb_subfr; i++)
					{
						int num5 = x_ptr + i * subfr_length;
						num4 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, num5, D), -num3);
					}
				}
				res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(num7, num4), 2);
				res_nrg_Q.Val = -num3;
			}
			else
			{
				int num11 = array4[0];
				int num9 = 65536;
				for (int l = 0; l < D; l++)
				{
					int c = Inlines.silk_RSHIFT_ROUND(array3[l], 9);
					num11 = Inlines.silk_SMLAWW(num11, array4[l + 1], c);
					num9 = Inlines.silk_SMLAWW(num9, c, c);
					A_Q16[l] = -c;
				}
				res_nrg.Val = Inlines.silk_SMLAWW(num11, Inlines.silk_SMMUL(42950, num4), -num9);
				res_nrg_Q.Val = -num3;
			}
		}
	}
	internal static class BWExpander
	{
		internal static void silk_bwexpander_32(int[] ar, int d, int chirp_Q16)
		{
			int b = chirp_Q16 - 65536;
			for (int i = 0; i < d - 1; i++)
			{
				ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]);
				chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, b), 16);
			}
			ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]);
		}

		internal static void silk_bwexpander(short[] ar, int d, int chirp_Q16)
		{
			int b = chirp_Q16 - 65536;
			for (int i = 0; i < d - 1; i++)
			{
				ar[i] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[i]), 16);
				chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, b), 16);
			}
			ar[d - 1] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[d - 1]), 16);
		}
	}
	internal static class CNG
	{
		internal static void silk_CNG_exc(Span<int> exc_Q10, int exc_Q10_ptr, int[] exc_buf_Q14, int Gain_Q16, int length, ref int rand_seed)
		{
			int num;
			for (num = 255; num > length; num = Inlines.silk_RSHIFT(num, 1))
			{
			}
			int num2 = rand_seed;
			for (int i = exc_Q10_ptr; i < exc_Q10_ptr + length; i++)
			{
				num2 = Inlines.silk_RAND(num2);
				int num3 = Inlines.silk_RSHIFT(num2, 24) & num;
				exc_Q10[i] = (short)Inlines.silk_SAT16(Inlines.silk_SMULWW(exc_buf_Q14[num3], Gain_Q16 >> 4));
			}
			rand_seed = num2;
		}

		internal static void silk_CNG_Reset(SilkChannelDecoder psDec)
		{
			int num = Inlines.silk_DIV32_16(32767, (short)(psDec.LPC_order + 1));
			int num2 = 0;
			for (int i = 0; i < psDec.LPC_order; i++)
			{
				num2 += num;
				psDec.sCNG.CNG_smth_NLSF_Q15[i] = (short)num2;
			}
			psDec.sCNG.CNG_smth_Gain_Q16 = 0;
			psDec.sCNG.rand_seed = 3176576;
		}

		internal static void silk_CNG(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, Span<short> frame, int frame_ptr, int length)
		{
			short[] array = new short[psDec.LPC_order];
			CNGState sCNG = psDec.sCNG;
			if (psDec.fs_kHz != sCNG.fs_kHz)
			{
				silk_CNG_Reset(psDec);
				sCNG.fs_kHz = psDec.fs_kHz;
			}
			if (psDec.lossCnt == 0 && psDec.prevSignalType == 0)
			{
				for (int i = 0; i < psDec.LPC_order; i++)
				{
					sCNG.CNG_smth_NLSF_Q15[i] += (short)Inlines.silk_SMULWB(psDec.prevNLSF_Q15[i] - sCNG.CNG_smth_NLSF_Q15[i], 16348);
				}
				int num = 0;
				for (int i = 0; i < psDec.nb_subfr; i++)
				{
					if (psDecCtrl.Gains_Q16[i] > num)
					{
						num = psDecCtrl.Gains_Q16[i];
					}
				}
				Arrays.MemMoveInt(sCNG.CNG_exc_buf_Q14, 0, psDec.subfr_length, (psDec.nb_subfr - 1) * psDec.subfr_length);
				for (int i = 0; i < psDec.nb_subfr; i++)
				{
					sCNG.CNG_smth_Gain_Q16 += Inlines.silk_SMULWB(psDecCtrl.Gains_Q16[i] - sCNG.CNG_smth_Gain_Q16, 4634);
				}
			}
			if (psDec.lossCnt != 0)
			{
				int[] array2 = new int[length + 16];
				int num2 = Inlines.silk_SMULWW(psDec.sPLC.randScale_Q14, psDec.sPLC.prevGain_Q16[1]);
				if (num2 >= 2097152 || sCNG.CNG_smth_Gain_Q16 > 8388608)
				{
					num2 = Inlines.silk_SMULTT(num2, num2);
					num2 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULTT(sCNG.CNG_smth_Gain_Q16, sCNG.CNG_smth_Gain_Q16), num2, 5);
					num2 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(num2), 16);
				}
				else
				{
					num2 = Inlines.silk_SMULWW(num2, num2);
					num2 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULWW(sCNG.CNG_smth_Gain_Q16, sCNG.CNG_smth_Gain_Q16), num2, 5);
					num2 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(num2), 8);
				}
				silk_CNG_exc(array2, 16, sCNG.CNG_exc_buf_Q14, num2, length, ref sCNG.rand_seed);
				NLSF.silk_NLSF2A(array, sCNG.CNG_smth_NLSF_Q15, psDec.LPC_order);
				Arrays.MemCopy(sCNG.CNG_synth_state, 0, array2, 0, 16);
				for (int i = 0; i < length; i++)
				{
					int num3 = 16 + i;
					int a = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 1], array[0]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 2], array[1]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 3], array[2]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 4], array[3]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 5], array[4]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 6], array[5]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 7], array[6]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 8], array[7]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 9], array[8]);
					a = Inlines.silk_SMLAWB(a, array2[num3 - 10], array[9]);
					if (psDec.LPC_order == 16)
					{
						a = Inlines.silk_SMLAWB(a, array2[num3 - 11], array[10]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 12], array[11]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 13], array[12]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 14], array[13]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 15], array[14]);
						a = Inlines.silk_SMLAWB(a, array2[num3 - 16], array[15]);
					}
					array2[num3] = Inlines.silk_ADD_LSHIFT(array2[num3], a, 4);
					frame[frame_ptr + i] = Inlines.silk_ADD_SAT16(frame[frame_ptr + i], (short)Inlines.silk_RSHIFT_ROUND(array2[num3], 10));
				}
				Arrays.MemCopy(array2, length, sCNG.CNG_synth_state, 0, 16);
			}
			else
			{
				Arrays.MemSetInt(sCNG.CNG_synth_state, 0, psDec.LPC_order);
			}
		}
	}
	internal static class CodeSigns
	{
		private static int silk_enc_map(int a)
		{
			return Inlines.silk_RSHIFT(a, 15) + 1;
		}

		private static int silk_dec_map(int a)
		{
			return Inlines.silk_LSHIFT(a, 1) - 1;
		}

		internal static void silk_encode_signs(EntropyCoder psRangeEnc, Span<byte> encodedData, Span<sbyte> pulses, int length, int signalType, int quantOffsetType, int[] sum_pulses)
		{
			byte[] array = new byte[2];
			byte[] silk_sign_iCDF = Tables.silk_sign_iCDF;
			array[1] = 0;
			int num = 0;
			int num2 = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1));
			int num3 = num2;
			length = Inlines.silk_RSHIFT(length + 8, 4);
			for (num2 = 0; num2 < length; num2++)
			{
				int num4 = sum_pulses[num2];
				if (num4 > 0)
				{
					array[0] = silk_sign_iCDF[num3 + Inlines.silk_min(num4 & 0x1F, 6)];
					for (int i = num; i < num + 16; i++)
					{
						if (pulses[i] != 0)
						{
							psRangeEnc.enc_icdf(encodedData, silk_enc_map(pulses[i]), array, 8u);
						}
					}
				}
				num += 16;
			}
		}

		internal static void silk_decode_signs(EntropyCoder psRangeDec, ReadOnlySpan<byte> encodedData, short[] pulses, int length, int signalType, int quantOffsetType, int[] sum_pulses)
		{
			byte[] array = new byte[2];
			byte[] silk_sign_iCDF = Tables.silk_sign_iCDF;
			array[1] = 0;
			int num = 0;
			int num2 = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1));
			int num3 = num2;
			length = Inlines.silk_RSHIFT(length + 8, 4);
			for (num2 = 0; num2 < length; num2++)
			{
				int num4 = sum_pulses[num2];
				if (num4 > 0)
				{
					array[0] = silk_sign_iCDF[num3 + Inlines.silk_min(num4 & 0x1F, 6)];
					for (int i = 0; i < 16; i++)
					{
						if (pulses[num + i] > 0)
						{
							pulses[num + i] *= (short)silk_dec_map(psRangeDec.dec_icdf(encodedData, array, 8u));
						}
					}
				}
				num += 16;
			}
		}
	}
	internal static class CorrelateMatrix
	{
		internal static void silk_corrVector(short[] x, int x_ptr, short[] t, int t_ptr, int L, int order, int[] Xt, int rshifts)
		{
			int num = x_ptr + order - 1;
			if (rshifts > 0)
			{
				for (int i = 0; i < order; i++)
				{
					int num2 = 0;
					for (int j = 0; j < L; j++)
					{
						num2 += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num + j], t[t_ptr + j]), rshifts);
					}
					Xt[i] = num2;
					num--;
				}
			}
			else
			{
				for (int i = 0; i < order; i++)
				{
					Xt[i] = Inlines.silk_inner_prod(x, num, t, t_ptr, L);
					num--;
				}
			}
		}

		internal static void silk_corrMatrix(short[] x, int x_ptr, int L, int order, int head_room, int[] XX, int XX_ptr, BoxedValueInt rshifts)
		{
			SumSqrShift.silk_sum_sqr_shift(out var energy, out var shift, x, x_ptr, L + order - 1);
			int num = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0);
			energy = Inlines.silk_RSHIFT32(energy, num);
			shift += num;
			for (int i = x_ptr; i < x_ptr + order - 1; i++)
			{
				energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), shift);
			}
			if (shift < rshifts.Val)
			{
				energy = Inlines.silk_RSHIFT32(energy, rshifts.Val - shift);
				shift = rshifts.Val;
			}
			Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy);
			int num2 = x_ptr + order - 1;
			for (int j = 1; j < order; j++)
			{
				energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 + L - j], x[num2 + L - j]), shift));
				energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 - j], x[num2 - j]), shift));
				Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy);
			}
			int num3 = x_ptr + order - 2;
			if (shift > 0)
			{
				for (int k = 1; k < order; k++)
				{
					energy = 0;
					for (int i = 0; i < L; i++)
					{
						energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 + i], x[num3 + i]), shift);
					}
					Inlines.MatrixSet(XX, XX_ptr, k, 0, order, energy);
					Inlines.MatrixSet(XX, XX_ptr, 0, k, order, energy);
					for (int j = 1; j < order - k; j++)
					{
						energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 + L - j], x[num3 + L - j]), shift));
						energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[num2 - j], x[num3 - j]), shift));
						Inlines.MatrixSet(XX, XX_ptr, k + j, j, order, energy);
						Inlines.MatrixSet(XX, XX_ptr, j, k + j, order, energy);
					}
					num3--;
				}
			}
			else
			{
				for (int k = 1; k < order; k++)
				{
					energy = Inlines.silk_inner_prod(x, num2, x, num3, L);
					Inlines.MatrixSet(XX, XX_ptr, k, 0, order, energy);
					Inlines.MatrixSet(XX, XX_ptr, 0, k, order, energy);
					for (int j = 1; j < order - k; j++)
					{
						energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[num2 + L - j], x[num3 + L - j]));
						energy = Inlines.silk_SMLABB(energy, x[num2 - j], x[num3 - j]);
						Inlines.MatrixSet(XX, XX_ptr, k + j, j, order, energy);
						Inlines.MatrixSet(XX, XX_ptr, j, k + j, order, energy);
					}
					num3--;
				}
			}
			rshifts.Val = shift;
		}
	}
	internal static class DecodeAPI
	{
		internal static int silk_InitDecoder(SilkDecoder decState)
		{
			decState.Reset();
			int result = SilkError.SILK_NO_ERROR;
			SilkChannelDecoder[] channel_state = decState.channel_state;
			for (int i = 0; i < 2; i++)
			{
				result = channel_state[i].silk_init_decoder();
			}
			decState.sStereo.Reset();
			decState.prev_decode_only_middle = 0;
			return result;
		}

		internal static int silk_Decode(SilkDecoder psDec, DecControlState decControl, ReadOnlySpan<byte> frameData, int lostFlag, int newPacketFlag, EntropyCoder psRangeDec, Span<short> samplesOut, int samplesOut_ptr, out int nSamplesOut)
		{
			int num = 0;
			int num2 = SilkError.SILK_NO_ERROR;
			BoxedValueInt boxedValueInt = new BoxedValueInt();
			int[] array = new int[2];
			int[] array2 = new int[2];
			SilkChannelDecoder[] channel_state = psDec.channel_state;
			nSamplesOut = 0;
			if (newPacketFlag != 0)
			{
				for (int i = 0; i < decControl.nChannelsInternal; i++)
				{
					channel_state[i].nFramesDecoded = 0;
				}
			}
			if (decControl.nChannelsInternal > psDec.nChannelsInternal)
			{
				num2 += channel_state[1].silk_init_decoder();
			}
			int num3 = ((decControl.nChannelsInternal == 1 && psDec.nChannelsInternal == 2 && decControl.internalSampleRate == 1000 * channel_state[0].fs_kHz) ? 1 : 0);
			if (channel_state[0].nFramesDecoded == 0)
			{
				for (int i = 0; i < decControl.nChannelsInternal; i++)
				{
					if (decControl.payloadSize_ms == 0)
					{
						channel_state[i].nFramesPerPacket = 1;
						channel_state[i].nb_subfr = 2;
					}
					else if (decControl.payloadSize_ms == 10)
					{
						channel_state[i].nFramesPerPacket = 1;
						channel_state[i].nb_subfr = 2;
					}
					else if (decControl.payloadSize_ms == 20)
					{
						channel_state[i].nFramesPerPacket = 1;
						channel_state[i].nb_subfr = 4;
					}
					else if (decControl.payloadSize_ms == 40)
					{
						channel_state[i].nFramesPerPacket = 2;
						channel_state[i].nb_subfr = 4;
					}
					else
					{
						if (decControl.payloadSize_ms != 60)
						{
							return SilkError.SILK_DEC_INVALID_FRAME_SIZE;
						}
						channel_state[i].nFramesPerPacket = 3;
						channel_state[i].nb_subfr = 4;
					}
					int num4 = (decControl.internalSampleRate >> 10) + 1;
					if (num4 != 8 && num4 != 12 && num4 != 16)
					{
						return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY;
					}
					num2 += channel_state[i].silk_decoder_set_fs(num4, decControl.API_sampleRate);
				}
			}
			if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2 && (psDec.nChannelsAPI == 1 || psDec.nChannelsInternal == 1))
			{
				Arrays.MemSetShort(psDec.sStereo.pred_prev_Q13, 0, 2);
				Arrays.MemSetShort(psDec.sStereo.sSide, 0, 2);
				channel_state[1].resampler_state.Assign(channel_state[0].resampler_state);
			}
			psDec.nChannelsAPI = decControl.nChannelsAPI;
			psDec.nChannelsInternal = decControl.nChannelsInternal;
			if (decControl.API_sampleRate > 48000 || decControl.API_sampleRate < 8000)
			{
				return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY;
			}
			if (lostFlag != 1 && channel_state[0].nFramesDecoded == 0)
			{
				for (int i = 0; i < decControl.nChannelsInternal; i++)
				{
					for (int j = 0; j < channel_state[i].nFramesPerPacket; j++)
					{
						channel_state[i].VAD_flags[j] = psRangeDec.dec_bit_logp(frameData, 1u);
					}
					channel_state[i].LBRR_flag = psRangeDec.dec_bit_logp(frameData, 1u);
				}
				for (int i = 0; i < decControl.nChannelsInternal; i++)
				{
					Arrays.MemSetInt(channel_state[i].LBRR_flags, 0, 3);
					if (channel_state[i].LBRR_flag == 0)
					{
						continue;
					}
					if (channel_state[i].nFramesPerPacket == 1)
					{
						channel_state[i].LBRR_flags[0] = 1;
						continue;
					}
					int a = psRangeDec.dec_icdf(frameData, Tables.silk_LBRR_flags_iCDF_ptr[channel_state[i].nFramesPerPacket - 2], 8u) + 1;
					for (int j = 0; j < channel_state[i].nFramesPerPacket; j++)
					{
						channel_state[i].LBRR_flags[j] = Inlines.silk_RSHIFT(a, j) & 1;
					}
				}
				if (lostFlag == 0)
				{
					for (int j = 0; j < channel_state[0].nFramesPerPacket; j++)
					{
						for (int i = 0; i < decControl.nChannelsInternal; i++)
						{
							if (channel_state[i].LBRR_flags[j] == 0)
							{
								continue;
							}
							short[] pulses = new short[320];
							if (decControl.nChannelsInternal == 2 && i == 0)
							{
								Stereo.silk_stereo_decode_pred(psRangeDec, frameData, array2);
								if (channel_state[1].LBRR_flags[j] == 0)
								{
									BoxedValueInt boxedValueInt2 = new BoxedValueInt(num);
									Stereo.silk_stereo_decode_mid_only(psRangeDec, frameData, boxedValueInt2);
									num = boxedValueInt2.Val;
								}
							}
							DecodeIndices.silk_decode_indices(condCoding: (j > 0 && channel_state[i].LBRR_flags[j - 1] != 0) ? 2 : 0, psDec: channel_state[i], psRangeDec: psRangeDec, frameData: frameData, FrameIndex: j, decode_LBRR: 1);
							DecodePulses.silk_decode_pulses(psRangeDec, frameData, pulses, channel_state[i].indices.signalType, channel_state[i].indices.quantOffsetType, channel_state[i].frame_length);
						}
					}
				}
			}
			if (decControl.nChannelsInternal == 2)
			{
				if (lostFlag == 0 || (lostFlag == 2 && channel_state[0].LBRR_flags[channel_state[0].nFramesDecoded] == 1))
				{
					Stereo.silk_stereo_decode_pred(psRangeDec, frameData, array2);
					if ((lostFlag == 0 && channel_state[1].VAD_flags[channel_state[0].nFramesDecoded] == 0) || (lostFlag == 2 && channel_state[1].LBRR_flags[channel_state[0].nFramesDecoded] == 0))
					{
						BoxedValueInt boxedValueInt3 = new BoxedValueInt(num);
						Stereo.silk_stereo_decode_mid_only(psRangeDec, frameData, boxedValueInt3);
						num = boxedValueInt3.Val;
					}
					else
					{
						num = 0;
					}
				}
				else
				{
					for (int i = 0; i < 2; i++)
					{
						array2[i] = psDec.sStereo.pred_prev_Q13[i];
					}
				}
			}
			if (decControl.nChannelsInternal == 2 && num == 0 && psDec.prev_decode_only_middle == 1)
			{
				Arrays.MemSetShort(psDec.channel_state[1].outBuf, 0, 480);
				Arrays.MemSetInt(psDec.channel_state[1].sLPC_Q14_buf, 0, 16);
				psDec.channel_state[1].lagPrev = 100;
				psDec.channel_state[1].LastGainIndex = 10;
				psDec.channel_state[1].prevSignalType = 0;
				psDec.channel_state[1].first_frame_after_reset = 1;
			}
			int num5 = ((decControl.internalSampleRate * decControl.nChannelsInternal < decControl.API_sampleRate * decControl.nChannelsAPI) ? 1 : 0);
			Span<short> span;
			if (num5 != 0)
			{
				span = samplesOut;
				array[0] = samplesOut_ptr;
				array[1] = samplesOut_ptr + channel_state[0].frame_length + 2;
			}
			else
			{
				span = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)];
				array[0] = 0;
				array[1] = channel_state[0].frame_length + 2;
			}
			int num6 = ((lostFlag != 0) ? ((psDec.prev_decode_only_middle == 0 || (decControl.nChannelsInternal == 2 && lostFlag == 2 && channel_state[1].LBRR_flags[channel_state[1].nFramesDecoded] == 1)) ? 1 : 0) : ((num == 0) ? 1 : 0));
			for (int i = 0; i < decControl.nChannelsInternal; i++)
			{
				if (i == 0 || num6 != 0)
				{
					int num7 = channel_state[0].nFramesDecoded - i;
					int condCoding2 = ((num7 > 0) ? ((lostFlag == 2) ? ((channel_state[i].LBRR_flags[num7 - 1] != 0) ? 2 : 0) : ((i > 0 && psDec.prev_decode_only_middle != 0) ? 1 : 2)) : 0);
					num2 += channel_state[i].silk_decode_frame(psRangeDec, frameData, span, array[i] + 2, boxedValueInt, lostFlag, condCoding2);
				}
				else
				{
					Arrays.MemSetWithOffset<short>(span, 0, array[i] + 2, boxedValueInt.Val);
				}
				channel_state[i].nFramesDecoded++;
			}
			if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2)
			{
				Stereo.silk_stereo_MS_to_LR(psDec.sStereo, span, array[0], span, array[1], array2, channel_state[0].fs_kHz, boxedValueInt.Val);
			}
			else
			{
				psDec.sStereo.sMid.AsSpan(0, 2).CopyTo(span.Slice(array[0]));
				span.Slice(array[0] + boxedValueInt.Val, 2).CopyTo(psDec.sStereo.sMid);
			}
			nSamplesOut = Inlines.silk_DIV32(boxedValueInt.Val * decControl.API_sampleRate, Inlines.silk_SMULBB(channel_state[0].fs_kHz, 1000));
			Span<short> output;
			int num8;
			if (decControl.nChannelsAPI == 2)
			{
				output = new short[nSamplesOut];
				num8 = 0;
			}
			else
			{
				output = samplesOut;
				num8 = samplesOut_ptr;
			}
			if (num5 != 0)
			{
				short[] array3 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)];
				samplesOut.Slice(samplesOut_ptr, decControl.nChannelsInternal * (channel_state[0].frame_length + 2)).CopyTo(array3);
				span = array3;
				array[0] = 0;
				array[1] = channel_state[0].frame_length + 2;
			}
			for (int i = 0; i < Inlines.silk_min(decControl.nChannelsAPI, decControl.nChannelsInternal); i++)
			{
				num2 += Resampler.silk_resampler(channel_state[i].resampler_state, output, num8, span, array[i] + 1, boxedValueInt.Val);
				if (decControl.nChannelsAPI == 2)
				{
					int num9 = samplesOut_ptr + i;
					for (int j = 0; j < nSamplesOut; j++)
					{
						samplesOut[num9 + 2 * j] = output[num8 + j];
					}
				}
			}
			if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 1)
			{
				if (num3 != 0)
				{
					num2 += Resampler.silk_resampler(channel_state[1].resampler_state, output, num8, span, array[0] + 1, boxedValueInt.Val);
					for (int j = 0; j < nSamplesOut; j++)
					{
						samplesOut[samplesOut_ptr + 1 + 2 * j] = output[num8 + j];
					}
				}
				else
				{
					for (int j = 0; j < nSamplesOut; j++)
					{
						samplesOut[samplesOut_ptr + 1 + 2 * j] = samplesOut[samplesOut_ptr + 2 * j];
					}
				}
			}
			if (channel_state[0].prevSignalType == 2)
			{
				int[] array4 = new int[3] { 6, 4, 3 };
				decControl.prevPitchLag = channel_state[0].lagPrev * array4[channel_state[0].fs_kHz - 8 >> 2];
			}
			else
			{
				decControl.prevPitchLag = 0;
			}
			if (lostFlag == 1)
			{
				for (int j = 0; j < psDec.nChannelsInternal; j++)
				{
					psDec.channel_state[j].LastGainIndex = 10;
				}
			}
			else
			{
				psDec.prev_decode_only_middle = num;
			}
			return num2;
		}
	}
	internal static class DecodeCore
	{
		internal static void silk_decode_core(SilkChannelDecoder psDec, SilkDecoderControl psDecCtrl, Span<short> xq, int xq_ptr, short[] pulses)
		{
			int num = 0;
			short[] lTPCoef_Q = psDecCtrl.LTPCoef_Q14;
			short[] array = new short[psDec.ltp_mem_length];
			int[] array2 = new int[psDec.ltp_mem_length + psDec.frame_length];
			int[] array3 = new int[psDec.subfr_length];
			int[] array4 = new int[psDec.subfr_length + 16];
			int num2 = Tables.silk_Quantization_Offsets_Q10[psDec.indices.signalType >> 1][psDec.indices.quantOffsetType];
			int num3 = ((psDec.indices.NLSFInterpCoef_Q2 < 4) ? 1 : 0);
			int seed = psDec.indices.Seed;
			for (int i = 0; i < psDec.frame_length; i++)
			{
				seed = Inlines.silk_RAND(seed);
				psDec.exc_Q14[i] = Inlines.silk_LSHIFT(pulses[i], 14);
				if (psDec.exc_Q14[i] > 0)
				{
					psDec.exc_Q14[i] -= 1280;
				}
				else if (psDec.exc_Q14[i] < 0)
				{
					psDec.exc_Q14[i] += 1280;
				}
				psDec.exc_Q14[i] += num2 << 4;
				if (seed < 0)
				{
					psDec.exc_Q14[i] = -psDec.exc_Q14[i];
				}
				seed = Inlines.silk_ADD32_ovflw(seed, pulses[i]);
			}
			Arrays.MemCopy(psDec.sLPC_Q14_buf, 0, array4, 0, 16);
			int num4 = 0;
			int num5 = xq_ptr;
			int num6 = psDec.ltp_mem_length;
			for (int j = 0; j < psDec.nb_subfr; j++)
			{
				int[] array5 = array3;
				int num7 = 0;
				short[] array6 = psDecCtrl.PredCoef_Q12[j >> 1];
				int num8 = j * 5;
				int num9 = psDec.indices.signalType;
				int b = Inlines.silk_RSHIFT(psDecCtrl.Gains_Q16[j], 6);
				int a = Inlines.silk_INVERSE32_varQ(psDecCtrl.Gains_Q16[j], 47);
				int num10;
				if (psDecCtrl.Gains_Q16[j] != psDec.prev_gain_Q16)
				{
					num10 = Inlines.silk_DIV32_varQ(psDec.prev_gain_Q16, psDecCtrl.Gains_Q16[j], 16);
					for (int i = 0; i < 16; i++)
					{
						array4[i] = Inlines.silk_SMULWW(num10, array4[i]);
					}
				}
				else
				{
					num10 = 65536;
				}
				psDec.prev_gain_Q16 = psDecCtrl.Gains_Q16[j];
				if (psDec.lossCnt != 0 && psDec.prevSignalType == 2 && psDec.indices.signalType != 2 && j < 2)
				{
					Arrays.MemSetWithOffset(lTPCoef_Q, (short)0, num8, 5);
					lTPCoef_Q[num8 + 2] = 4096;
					num9 = 2;
					psDecCtrl.pitchL[j] = psDec.lagPrev;
				}
				if (num9 == 2)
				{
					num = psDecCtrl.pitchL[j];
					if (j == 0 || (j == 2 && num3 != 0))
					{
						int num11 = psDec.ltp_mem_length - num - psDec.LPC_order - 2;
						if (j == 2)
						{
							xq.Slice(xq_ptr, 2 * psDec.subfr_length).CopyTo(psDec.outBuf.AsSpan(psDec.ltp_mem_length));
						}
						Filters.silk_LPC_analysis_filter(array, num11, psDec.outBuf, num11 + j * psDec.subfr_length, array6, 0, psDec.ltp_mem_length - num11, psDec.LPC_order);
						if (j == 0)
						{
							a = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(a, psDecCtrl.LTP_scale_Q14), 2);
						}
						for (int i = 0; i < num + 2; i++)
						{
							array2[num6 - i - 1] = Inlines.silk_SMULWB(a, array[psDec.ltp_mem_length - i - 1]);
						}
					}
					else if (num10 != 65536)
					{
						for (int i = 0; i < num + 2; i++)
						{
							array2[num6 - i - 1] = Inlines.silk_SMULWW(num10, array2[num6 - i - 1]);
						}
					}
				}
				if (num9 == 2)
				{
					int num12 = num6 - num + 2;
					for (int i = 0; i < psDec.subfr_length; i++)
					{
						int a2 = 2;
						a2 = Inlines.silk_SMLAWB(a2, array2[num12], lTPCoef_Q[num8]);
						a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 1], lTPCoef_Q[num8 + 1]);
						a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 2], lTPCoef_Q[num8 + 2]);
						a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 3], lTPCoef_Q[num8 + 3]);
						a2 = Inlines.silk_SMLAWB(a2, array2[num12 - 4], lTPCoef_Q[num8 + 4]);
						num12++;
						array5[num7 + i] = Inlines.silk_ADD_LSHIFT32(psDec.exc_Q14[num4 + i], a2, 1);
						array2[num6] = Inlines.silk_LSHIFT(array5[num7 + i], 1);
						num6++;
					}
				}
				else
				{
					array5 = psDec.exc_Q14;
					num7 = num4;
				}
				for (int i = 0; i < psDec.subfr_length; i++)
				{
					int a3 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 1], array6[0]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 2], array6[1]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 3], array6[2]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 4], array6[3]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 5], array6[4]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 6], array6[5]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 7], array6[6]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 8], array6[7]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 9], array6[8]);
					a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 10], array6[9]);
					if (psDec.LPC_order == 16)
					{
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 11], array6[10]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 12], array6[11]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 13], array6[12]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 14], array6[13]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 15], array6[14]);
						a3 = Inlines.silk_SMLAWB(a3, array4[16 + i - 16], array6[15]);
					}
					array4[16 + i] = Inlines.silk_ADD_LSHIFT32(array5[num7 + i], a3, 4);
					xq[num5 + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(array4[16 + i], b), 8));
				}
				Arrays.MemCopy(array4, psDec.subfr_length, array4, 0, 16);
				num4 += psDec.subfr_length;
				num5 += psDec.subfr_length;
			}
			Arrays.MemCopy(array4, 0, psDec.sLPC_Q14_buf, 0, 16);
		}
	}
	internal static class DecodeIndices
	{
		internal static void silk_decode_indices(SilkChannelDecoder psDec, EntropyCoder psRangeDec, ReadOnlySpan<byte> frameData, int FrameIndex, int decode_LBRR, int condCoding)
		{
			short[] array = new short[psDec.LPC_order];
			byte[] array2 = new byte[psDec.LPC_order];
			int num = ((decode_LBRR == 0 && psDec.VAD_flags[FrameIndex] == 0) ? psRangeDec.dec_icdf(frameData, Tables.silk_type_offset_no_VAD_iCDF, 8u) : (psRangeDec.dec_icdf(frameData, Tables.silk_type_offset_VAD_iCDF, 8u) + 2));
			psDec.indices.signalType = (sbyte)Inlines.silk_RSHIFT(num, 1);
			psDec.indices.quantOffsetType = (sbyte)(num & 1);
			if (condCoding == 2)
			{
				psDec.indices.GainsIndices[0] = (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_delta_gain_iCDF, 8u);
			}
			else
			{
				psDec.indices.GainsIndices[0] = (sbyte)Inlines.silk_LSHIFT(psRangeDec.dec_icdf(frameData, Tables.silk_gain_iCDF[psDec.indices.signalType], 8u), 3);
				psDec.indices.GainsIndices[0] += (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_uniform8_iCDF, 8u);
			}
			for (int i = 1; i < psDec.nb_subfr; i++)
			{
				psDec.indices.GainsIndices[i] = (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_delta_gain_iCDF, 8u);
			}
			psDec.indices.NLSFIndices[0] = (sbyte)psRangeDec.dec_icdf(frameData, psDec.psNLSF_CB.CB1_iCDF, (psDec.indices.signalType >> 1) * psDec.psNLSF_CB.nVectors, 8u);
			NLSF.silk_NLSF_unpack(array, array2, psDec.psNLSF_CB, psDec.indices.NLSFIndices[0]);
			for (int i = 0; i < psDec.psNLSF_CB.order; i++)
			{
				num = psRangeDec.dec_icdf(frameData, psDec.psNLSF_CB.ec_iCDF, array[i], 8u);
				switch (num)
				{
				case 0:
					num -= psRangeDec.dec_icdf(frameData, Tables.silk_NLSF_EXT_iCDF, 8u);
					break;
				case 8:
					num += psRangeDec.dec_icdf(frameData, Tables.silk_NLSF_EXT_iCDF, 8u);
					break;
				}
				psDec.indices.NLSFIndices[i + 1] = (sbyte)(num - 4);
			}
			if (psDec.nb_subfr == 4)
			{
				psDec.indices.NLSFInterpCoef_Q2 = (sbyte)psRangeDec.dec_icdf(frameData, Tables.silk_NLSF

BepInEx/core/Concentus.Oggfile/Concentus.Oggfile.dll

Decompiled 4 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Resources;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using Concentus.Structs;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTrademark("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = ".NET Standard 2.0")]
[assembly: AssemblyCompany("Logan Stromberg")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("Copyright © Logan Stromberg, Andrew Ward")]
[assembly: AssemblyDescription("This package implements file streams which can be used to extract or encode Opus packets in an Ogg-formatted audio file (usually .opus), giving developers a very simple API to perform the task of reading or writing audio files that can be played universally. The Concentus library is used to encode/decode the opus packets automatically. The codec can optionally be accelerated by also referencing the Concentus.Native package.")]
[assembly: AssemblyFileVersion("1.0.6.0")]
[assembly: AssemblyInformationalVersion("1.0.6.0+dee65d9de0ef009658c2d60be6fd9b29d362a482")]
[assembly: AssemblyProduct("Concentus.Oggfile")]
[assembly: AssemblyTitle("Concentus.Oggfile")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/lostromb/concentus.oggfile")]
[assembly: AssemblyVersion("1.0.6.0")]
namespace Concentus.Oggfile;

internal static class BinaryHelpers
{
	internal static void Int16ToByteArrayLittleEndian(short val, byte[] target, int targetOffset)
	{
		UInt16ToByteArrayLittleEndian((ushort)val, target, targetOffset);
	}

	internal static void UInt16ToByteArrayLittleEndian(ushort val, byte[] target, int targetOffset)
	{
		target[targetOffset + 1] = (byte)((uint)(val >> 8) & 0xFFu);
		target[targetOffset] = (byte)(val & 0xFFu);
	}

	internal static void Int16ToByteSpanLittleEndian(short val, ref Span<byte> target)
	{
		UInt16ToByteArraySpanEndian((ushort)val, ref target);
	}

	internal static void UInt16ToByteArraySpanEndian(ushort val, ref Span<byte> target)
	{
		target[1] = (byte)((uint)(val >> 8) & 0xFFu);
		target[0] = (byte)(val & 0xFFu);
	}

	internal static void Int16ToByteArrayBigEndian(short val, byte[] target, int targetOffset)
	{
		UInt16ToByteArrayBigEndian((ushort)val, target, targetOffset);
	}

	internal static void UInt16ToByteArrayBigEndian(ushort val, byte[] target, int targetOffset)
	{
		target[targetOffset] = (byte)((uint)(val >> 8) & 0xFFu);
		target[targetOffset + 1] = (byte)(val & 0xFFu);
	}

	internal static void Int16ToByteSpanBigEndian(short val, ref Span<byte> target)
	{
		UInt16ToByteSpanBigEndian((ushort)val, ref target);
	}

	internal static void UInt16ToByteSpanBigEndian(ushort val, ref Span<byte> target)
	{
		target[0] = (byte)((uint)(val >> 8) & 0xFFu);
		target[1] = (byte)(val & 0xFFu);
	}

	internal static void Int32ToByteArrayLittleEndian(int val, byte[] target, int targetOffset)
	{
		UInt32ToByteArrayLittleEndian((uint)val, target, targetOffset);
	}

	internal static void UInt32ToByteArrayLittleEndian(uint val, byte[] target, int targetOffset)
	{
		target[targetOffset + 3] = (byte)((val >> 24) & 0xFFu);
		target[targetOffset + 2] = (byte)((val >> 16) & 0xFFu);
		target[targetOffset + 1] = (byte)((val >> 8) & 0xFFu);
		target[targetOffset] = (byte)(val & 0xFFu);
	}

	internal static void Int32ToByteSpanLittleEndian(int val, ref Span<byte> target)
	{
		UInt32ToByteSpanLittleEndian((uint)val, ref target);
	}

	internal static void UInt32ToByteSpanLittleEndian(uint val, ref Span<byte> target)
	{
		target[3] = (byte)((val >> 24) & 0xFFu);
		target[2] = (byte)((val >> 16) & 0xFFu);
		target[1] = (byte)((val >> 8) & 0xFFu);
		target[0] = (byte)(val & 0xFFu);
	}

	internal static void UInt24ToByteArrayBigEndian(uint val, byte[] target, int targetOffset)
	{
		target[targetOffset] = (byte)((val >> 16) & 0xFFu);
		target[targetOffset + 1] = (byte)((val >> 8) & 0xFFu);
		target[targetOffset + 2] = (byte)(val & 0xFFu);
	}

	internal static void UInt24ToByteSpanBigEndian(uint val, ref Span<byte> target)
	{
		target[0] = (byte)((val >> 16) & 0xFFu);
		target[1] = (byte)((val >> 8) & 0xFFu);
		target[2] = (byte)(val & 0xFFu);
	}

	internal static void Int32ToByteArrayBigEndian(int val, byte[] target, int targetOffset)
	{
		UInt32ToByteArrayBigEndian((uint)val, target, targetOffset);
	}

	internal static void UInt32ToByteArrayBigEndian(uint val, byte[] target, int targetOffset)
	{
		target[targetOffset] = (byte)((val >> 24) & 0xFFu);
		target[targetOffset + 1] = (byte)((val >> 16) & 0xFFu);
		target[targetOffset + 2] = (byte)((val >> 8) & 0xFFu);
		target[targetOffset + 3] = (byte)(val & 0xFFu);
	}

	internal static void Int32ToByteSpanBigEndian(int val, ref Span<byte> target)
	{
		UInt32ToByteSpanBigEndian((uint)val, ref target);
	}

	internal static void UInt32ToByteSpanBigEndian(uint val, ref Span<byte> target)
	{
		target[0] = (byte)((val >> 24) & 0xFFu);
		target[1] = (byte)((val >> 16) & 0xFFu);
		target[2] = (byte)((val >> 8) & 0xFFu);
		target[3] = (byte)(val & 0xFFu);
	}

	internal static void Int64ToByteArrayLittleEndian(long val, byte[] target, int targetOffset)
	{
		UInt64ToByteArrayLittleEndian((ulong)val, target, targetOffset);
	}

	internal static void UInt64ToByteArrayLittleEndian(ulong val, byte[] target, int targetOffset)
	{
		target[targetOffset + 7] = (byte)((val >> 56) & 0xFF);
		target[targetOffset + 6] = (byte)((val >> 48) & 0xFF);
		target[targetOffset + 5] = (byte)((val >> 40) & 0xFF);
		target[targetOffset + 4] = (byte)((val >> 32) & 0xFF);
		target[targetOffset + 3] = (byte)((val >> 24) & 0xFF);
		target[targetOffset + 2] = (byte)((val >> 16) & 0xFF);
		target[targetOffset + 1] = (byte)((val >> 8) & 0xFF);
		target[targetOffset] = (byte)(val & 0xFF);
	}

	internal static void Int64ToByteSpanLittleEndian(long val, ref Span<byte> target)
	{
		UInt64ToByteSpanLittleEndian((ulong)val, ref target);
	}

	internal static void UInt64ToByteSpanLittleEndian(ulong val, ref Span<byte> target)
	{
		target[7] = (byte)((val >> 56) & 0xFF);
		target[6] = (byte)((val >> 48) & 0xFF);
		target[5] = (byte)((val >> 40) & 0xFF);
		target[4] = (byte)((val >> 32) & 0xFF);
		target[3] = (byte)((val >> 24) & 0xFF);
		target[2] = (byte)((val >> 16) & 0xFF);
		target[1] = (byte)((val >> 8) & 0xFF);
		target[0] = (byte)(val & 0xFF);
	}

	internal static void Int64ToByteArrayBigEndian(long val, byte[] target, int targetOffset)
	{
		UInt64ToByteArrayBigEndian((ulong)val, target, targetOffset);
	}

	internal static void UInt64ToByteArrayBigEndian(ulong val, byte[] target, int targetOffset)
	{
		target[targetOffset] = (byte)((val >> 56) & 0xFF);
		target[targetOffset + 1] = (byte)((val >> 48) & 0xFF);
		target[targetOffset + 2] = (byte)((val >> 40) & 0xFF);
		target[targetOffset + 3] = (byte)((val >> 32) & 0xFF);
		target[targetOffset + 4] = (byte)((val >> 24) & 0xFF);
		target[targetOffset + 5] = (byte)((val >> 16) & 0xFF);
		target[targetOffset + 6] = (byte)((val >> 8) & 0xFF);
		target[targetOffset + 7] = (byte)(val & 0xFF);
	}

	internal static void Int64ToByteSpanBigEndian(long val, ref Span<byte> target)
	{
		UInt64ToByteSpanBigEndian((ulong)val, ref target);
	}

	internal static void UInt64ToByteSpanBigEndian(ulong val, ref Span<byte> target)
	{
		target[0] = (byte)((val >> 56) & 0xFF);
		target[1] = (byte)((val >> 48) & 0xFF);
		target[2] = (byte)((val >> 40) & 0xFF);
		target[3] = (byte)((val >> 32) & 0xFF);
		target[4] = (byte)((val >> 24) & 0xFF);
		target[5] = (byte)((val >> 16) & 0xFF);
		target[6] = (byte)((val >> 8) & 0xFF);
		target[7] = (byte)(val & 0xFF);
	}

	internal static short ByteArrayToInt16LittleEndian(byte[] source, int offset)
	{
		return (short)((short)(0 | (short)(source[offset + 1] << 8)) | (short)source[offset]);
	}

	internal static short ByteSpanToInt16LittleEndian(ref Span<byte> source)
	{
		return (short)((short)(0 | (short)(source[1] << 8)) | (short)source[0]);
	}

	internal static short ByteSpanToInt16LittleEndian(ref ReadOnlySpan<byte> source)
	{
		return (short)((short)(0 | (short)(source[1] << 8)) | (short)source[0]);
	}

	internal static ushort ByteArrayToUInt16LittleEndian(byte[] source, int offset)
	{
		return (ushort)((ushort)(0u | (ushort)(source[offset + 1] << 8)) | source[offset]);
	}

	internal static ushort ByteSpanToUInt16LittleEndian(ref Span<byte> source)
	{
		return (ushort)((ushort)(0u | (ushort)(source[1] << 8)) | source[0]);
	}

	internal static ushort ByteSpanToUInt16LittleEndian(ref ReadOnlySpan<byte> source)
	{
		return (ushort)((ushort)(0u | (ushort)(source[1] << 8)) | source[0]);
	}

	internal static short ByteArrayToInt16BigEndian(byte[] source, int offset)
	{
		return (short)((short)(0 | (short)(source[offset] << 8)) | (short)source[offset + 1]);
	}

	internal static short ByteSpanToInt16BigEndian(ref Span<byte> source)
	{
		return (short)((short)(0 | (short)(source[0] << 8)) | (short)source[1]);
	}

	internal static short ByteSpanToInt16BigEndian(ref ReadOnlySpan<byte> source)
	{
		return (short)((short)(0 | (short)(source[0] << 8)) | (short)source[1]);
	}

	internal static ushort ByteArrayToUInt16BigEndian(byte[] source, int offset)
	{
		return (ushort)((ushort)(0u | (ushort)(source[offset] << 8)) | source[offset + 1]);
	}

	internal static ushort ByteSpanToUInt16BigEndian(ref Span<byte> source)
	{
		return (ushort)((ushort)(0u | (ushort)(source[0] << 8)) | source[1]);
	}

	internal static ushort ByteSpanToUInt16BigEndian(ref ReadOnlySpan<byte> source)
	{
		return (ushort)((ushort)(0u | (ushort)(source[0] << 8)) | source[1]);
	}

	internal static int ByteArrayToInt32LittleEndian(byte[] source, int offset)
	{
		return 0 | (source[offset + 3] << 24) | (source[offset + 2] << 16) | (source[offset + 1] << 8) | source[offset];
	}

	internal static int ByteSpanToInt32LittleEndian(ref Span<byte> source)
	{
		return 0 | (source[3] << 24) | (source[2] << 16) | (source[1] << 8) | source[0];
	}

	internal static int ByteSpanToInt32LittleEndian(ref ReadOnlySpan<byte> source)
	{
		return 0 | (source[3] << 24) | (source[2] << 16) | (source[1] << 8) | source[0];
	}

	internal static uint ByteArrayToUInt32LittleEndian(byte[] source, int offset)
	{
		return 0u | (uint)(source[offset + 3] << 24) | (uint)(source[offset + 2] << 16) | (uint)(source[offset + 1] << 8) | source[offset];
	}

	internal static uint ByteSpanToUInt32LittleEndian(ref Span<byte> source)
	{
		return 0u | (uint)(source[3] << 24) | (uint)(source[2] << 16) | (uint)(source[1] << 8) | source[0];
	}

	internal static uint ByteSpanToUInt32LittleEndian(ref ReadOnlySpan<byte> source)
	{
		return 0u | (uint)(source[3] << 24) | (uint)(source[2] << 16) | (uint)(source[1] << 8) | source[0];
	}

	internal static uint ByteArrayToUInt24BigEndian(byte[] source, int offset)
	{
		return 0u | (uint)(source[offset] << 16) | (uint)(source[offset + 1] << 8) | source[offset + 2];
	}

	internal static uint ByteSpanToUInt24BigEndian(ref Span<byte> source)
	{
		return 0u | (uint)(source[0] << 16) | (uint)(source[1] << 8) | source[2];
	}

	internal static uint ByteSpanToUInt24BigEndian(ref ReadOnlySpan<byte> source)
	{
		return 0u | (uint)(source[0] << 16) | (uint)(source[1] << 8) | source[2];
	}

	internal static int ByteArrayToInt32BigEndian(byte[] source, int offset)
	{
		return 0 | (source[offset] << 24) | (source[offset + 1] << 16) | (source[offset + 2] << 8) | source[offset + 3];
	}

	internal static int ByteSpanToInt32BigEndian(ref Span<byte> source)
	{
		return 0 | (source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3];
	}

	internal static int ByteSpanToInt32BigEndian(ref ReadOnlySpan<byte> source)
	{
		return 0 | (source[0] << 24) | (source[1] << 16) | (source[2] << 8) | source[3];
	}

	internal static uint ByteArrayToUInt32BigEndian(byte[] source, int offset)
	{
		return 0u | (uint)(source[offset] << 24) | (uint)(source[offset + 1] << 16) | (uint)(source[offset + 2] << 8) | source[offset + 3];
	}

	internal static uint ByteSpanToUInt32BigEndian(ref Span<byte> source)
	{
		return 0u | (uint)(source[0] << 24) | (uint)(source[1] << 16) | (uint)(source[2] << 8) | source[3];
	}

	internal static uint ByteSpanToUInt32BigEndian(ref ReadOnlySpan<byte> source)
	{
		return 0u | (uint)(source[0] << 24) | (uint)(source[1] << 16) | (uint)(source[2] << 8) | source[3];
	}

	internal static long ByteArrayToInt64LittleEndian(byte[] source, int offset)
	{
		return (long)(0 | ((ulong)source[offset + 7] << 56) | ((ulong)source[offset + 6] << 48) | ((ulong)source[offset + 5] << 40) | ((ulong)source[offset + 4] << 32) | ((ulong)source[offset + 3] << 24) | ((ulong)source[offset + 2] << 16) | ((ulong)source[offset + 1] << 8) | source[offset]);
	}

	internal static long ByteSpanToInt64LittleEndian(ref Span<byte> source)
	{
		return (long)(0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]);
	}

	internal static long ByteSpanToInt64LittleEndian(ref ReadOnlySpan<byte> source)
	{
		return (long)(0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0]);
	}

	internal static ulong ByteArrayToUInt64LittleEndian(byte[] source, int offset)
	{
		return 0 | ((ulong)source[offset + 7] << 56) | ((ulong)source[offset + 6] << 48) | ((ulong)source[offset + 5] << 40) | ((ulong)source[offset + 4] << 32) | ((ulong)source[offset + 3] << 24) | ((ulong)source[offset + 2] << 16) | ((ulong)source[offset + 1] << 8) | source[offset];
	}

	internal static ulong ByteSpanToUInt64LittleEndian(ref Span<byte> source)
	{
		return 0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0];
	}

	internal static ulong ByteSpanToUInt64LittleEndian(ref ReadOnlySpan<byte> source)
	{
		return 0 | ((ulong)source[7] << 56) | ((ulong)source[6] << 48) | ((ulong)source[5] << 40) | ((ulong)source[4] << 32) | ((ulong)source[3] << 24) | ((ulong)source[2] << 16) | ((ulong)source[1] << 8) | source[0];
	}

	internal static long ByteArrayToInt64BigEndian(byte[] source, int offset)
	{
		return (long)(0 | ((ulong)source[offset] << 56) | ((ulong)source[offset + 1] << 48) | ((ulong)source[offset + 2] << 40) | ((ulong)source[offset + 3] << 32) | ((ulong)source[offset + 4] << 24) | ((ulong)source[offset + 5] << 16) | ((ulong)source[offset + 6] << 8) | source[offset + 7]);
	}

	internal static long ByteSpanToInt64BigEndian(ref Span<byte> source)
	{
		return (long)(0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]);
	}

	internal static long ByteSpanToInt64BigEndian(ref ReadOnlySpan<byte> source)
	{
		return (long)(0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7]);
	}

	internal static ulong ByteArrayToUInt64BigEndian(byte[] source, int offset)
	{
		return 0 | ((ulong)source[offset] << 56) | ((ulong)source[offset + 1] << 48) | ((ulong)source[offset + 2] << 40) | ((ulong)source[offset + 3] << 32) | ((ulong)source[offset + 4] << 24) | ((ulong)source[offset + 5] << 16) | ((ulong)source[offset + 6] << 8) | source[offset + 7];
	}

	internal static ulong ByteSpanToUInt64BigEndian(ref Span<byte> source)
	{
		return 0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7];
	}

	internal static ulong ByteSpanToUInt64BigEndian(ref ReadOnlySpan<byte> source)
	{
		return 0 | ((ulong)source[0] << 56) | ((ulong)source[1] << 48) | ((ulong)source[2] << 40) | ((ulong)source[3] << 32) | ((ulong)source[4] << 24) | ((ulong)source[5] << 16) | ((ulong)source[6] << 8) | source[7];
	}
}
internal class BufferedReadStream : Stream
{
	private const int DEFAULT_INITIAL_SIZE = 32768;

	private const int DEFAULT_MAX_SIZE = 262144;

	private Stream _baseStream;

	private StreamReadBuffer _buffer;

	private long _readPosition;

	public bool CloseBaseStream { get; set; }

	public bool MinimalRead
	{
		get
		{
			return _buffer.MinimalRead;
		}
		set
		{
			_buffer.MinimalRead = value;
		}
	}

	public int MaxBufferSize
	{
		get
		{
			return _buffer.MaxSize;
		}
		set
		{
			CheckLock();
			_buffer.MaxSize = value;
		}
	}

	public long BufferBaseOffset => _buffer.BaseOffset;

	public int BufferBytesFilled => _buffer.BytesFilled;

	public override bool CanRead => true;

	public override bool CanSeek => true;

	public override bool CanWrite => false;

	public override long Length => _baseStream.Length;

	public override long Position
	{
		get
		{
			return _readPosition;
		}
		set
		{
			Seek(value, SeekOrigin.Begin);
		}
	}

	public BufferedReadStream(Stream baseStream)
		: this(baseStream, 32768, 262144, minimalRead: false)
	{
	}

	public BufferedReadStream(Stream baseStream, bool minimalRead)
		: this(baseStream, 32768, 262144, minimalRead)
	{
	}

	public BufferedReadStream(Stream baseStream, int initialSize, int maxSize)
		: this(baseStream, initialSize, maxSize, minimalRead: false)
	{
	}

	public BufferedReadStream(Stream baseStream, int initialSize, int maxBufferSize, bool minimalRead)
	{
		if (baseStream == null)
		{
			throw new ArgumentNullException("baseStream");
		}
		if (!baseStream.CanRead)
		{
			throw new ArgumentException("baseStream");
		}
		if (maxBufferSize < 1)
		{
			maxBufferSize = 1;
		}
		if (initialSize < 1)
		{
			initialSize = 1;
		}
		if (initialSize > maxBufferSize)
		{
			initialSize = maxBufferSize;
		}
		_baseStream = baseStream;
		_buffer = new StreamReadBuffer(baseStream, initialSize, maxBufferSize, minimalRead);
		_buffer.MaxSize = maxBufferSize;
		_buffer.MinimalRead = minimalRead;
	}

	protected override void Dispose(bool disposing)
	{
		base.Dispose(disposing);
		if (disposing)
		{
			if (_buffer != null)
			{
				_buffer.Dispose();
				_buffer = null;
			}
			_ = CloseBaseStream;
		}
	}

	public void TakeLock()
	{
	}

	private void CheckLock()
	{
	}

	public void ReleaseLock()
	{
	}

	public void Discard(int bytes)
	{
		CheckLock();
		_buffer.DiscardThrough(_buffer.BaseOffset + bytes);
	}

	public void DiscardThrough(long offset)
	{
		CheckLock();
		_buffer.DiscardThrough(offset);
	}

	public override void Flush()
	{
	}

	public override int ReadByte()
	{
		CheckLock();
		int num = _buffer.ReadByte(Position);
		if (num > -1)
		{
			Seek(1L, SeekOrigin.Current);
		}
		return num;
	}

	public override int Read(byte[] buffer, int offset, int count)
	{
		CheckLock();
		int num = _buffer.Read(Position, buffer, offset, count);
		Seek(num, SeekOrigin.Current);
		return num;
	}

	public override long Seek(long offset, SeekOrigin origin)
	{
		CheckLock();
		switch (origin)
		{
		case SeekOrigin.Current:
			offset += Position;
			break;
		case SeekOrigin.End:
			offset += _baseStream.Length;
			break;
		}
		if (!_baseStream.CanSeek)
		{
			if (offset < _buffer.BaseOffset)
			{
				throw new InvalidOperationException("Cannot seek to before the start of the buffer!");
			}
			if (offset > _buffer.BufferEndOffset)
			{
				throw new InvalidOperationException("Cannot seek to beyond the end of the buffer!  Discard some bytes.");
			}
		}
		return _readPosition = offset;
	}

	public override void SetLength(long value)
	{
		throw new NotSupportedException();
	}

	public override void Write(byte[] buffer, int offset, int count)
	{
		throw new NotSupportedException();
	}
}
internal abstract class DataPacket
{
	[Flags]
	protected enum PacketFlags : byte
	{
		IsResync = 1,
		IsEndOfStream = 2,
		IsShort = 4,
		HasGranuleCount = 8,
		User1 = 0x10,
		User2 = 0x20,
		User3 = 0x40,
		User4 = 0x80
	}

	private ulong _bitBucket;

	private int _bitCount;

	private int _readBits;

	private byte _overflowBits;

	private PacketFlags _packetFlags;

	private long _granulePosition;

	private long _pageGranulePosition;

	private int _length;

	private int _granuleCount;

	private int _pageSequenceNumber;

	public bool IsResync
	{
		get
		{
			return GetFlag(PacketFlags.IsResync);
		}
		internal set
		{
			SetFlag(PacketFlags.IsResync, value);
		}
	}

	public long GranulePosition
	{
		get
		{
			return _granulePosition;
		}
		set
		{
			_granulePosition = value;
		}
	}

	public long PageGranulePosition
	{
		get
		{
			return _pageGranulePosition;
		}
		internal set
		{
			_pageGranulePosition = value;
		}
	}

	public int Length
	{
		get
		{
			return _length;
		}
		protected set
		{
			_length = value;
		}
	}

	public bool IsEndOfStream
	{
		get
		{
			return GetFlag(PacketFlags.IsEndOfStream);
		}
		internal set
		{
			SetFlag(PacketFlags.IsEndOfStream, value);
		}
	}

	public long BitsRead => _readBits;

	public int? GranuleCount
	{
		get
		{
			if (GetFlag(PacketFlags.HasGranuleCount))
			{
				return _granuleCount;
			}
			return null;
		}
		set
		{
			if (value.HasValue)
			{
				_granuleCount = value.Value;
				SetFlag(PacketFlags.HasGranuleCount, value: true);
			}
			else
			{
				SetFlag(PacketFlags.HasGranuleCount, value: false);
			}
		}
	}

	internal int PageSequenceNumber
	{
		get
		{
			return _pageSequenceNumber;
		}
		set
		{
			_pageSequenceNumber = value;
		}
	}

	internal bool IsShort
	{
		get
		{
			return GetFlag(PacketFlags.IsShort);
		}
		private set
		{
			SetFlag(PacketFlags.IsShort, value);
		}
	}

	protected bool GetFlag(PacketFlags flag)
	{
		return (_packetFlags & flag) == flag;
	}

	protected void SetFlag(PacketFlags flag, bool value)
	{
		if (value)
		{
			_packetFlags |= flag;
		}
		else
		{
			_packetFlags &= (PacketFlags)(byte)(~(int)flag);
		}
	}

	protected DataPacket(int length)
	{
		Length = length;
	}

	protected abstract int ReadNextByte();

	public virtual void Done()
	{
	}

	public ulong TryPeekBits(int count, out int bitsRead)
	{
		ulong num = 0uL;
		switch (count)
		{
		default:
			throw new ArgumentOutOfRangeException("count");
		case 0:
			bitsRead = 0;
			return 0uL;
		case 1:
		case 2:
		case 3:
		case 4:
		case 5:
		case 6:
		case 7:
		case 8:
		case 9:
		case 10:
		case 11:
		case 12:
		case 13:
		case 14:
		case 15:
		case 16:
		case 17:
		case 18:
		case 19:
		case 20:
		case 21:
		case 22:
		case 23:
		case 24:
		case 25:
		case 26:
		case 27:
		case 28:
		case 29:
		case 30:
		case 31:
		case 32:
		case 33:
		case 34:
		case 35:
		case 36:
		case 37:
		case 38:
		case 39:
		case 40:
		case 41:
		case 42:
		case 43:
		case 44:
		case 45:
		case 46:
		case 47:
		case 48:
		case 49:
		case 50:
		case 51:
		case 52:
		case 53:
		case 54:
		case 55:
		case 56:
		case 57:
		case 58:
		case 59:
		case 60:
		case 61:
		case 62:
		case 63:
		case 64:
			break;
		}
		while (_bitCount < count)
		{
			int num2 = ReadNextByte();
			if (num2 == -1)
			{
				bitsRead = _bitCount;
				num = _bitBucket;
				_bitBucket = 0uL;
				_bitCount = 0;
				IsShort = true;
				return num;
			}
			_bitBucket = (ulong)((long)(num2 & 0xFF) << _bitCount) | _bitBucket;
			_bitCount += 8;
			if (_bitCount > 64)
			{
				_overflowBits = (byte)(num2 >> 72 - _bitCount);
			}
		}
		num = _bitBucket;
		if (count < 64)
		{
			num &= (ulong)((1L << count) - 1);
		}
		bitsRead = count;
		return num;
	}

	public void SkipBits(int count)
	{
		if (count == 0)
		{
			return;
		}
		if (_bitCount > count)
		{
			if (count > 63)
			{
				_bitBucket = 0uL;
			}
			else
			{
				_bitBucket >>= count;
			}
			if (_bitCount > 64)
			{
				int num = _bitCount - 64;
				_bitBucket |= (ulong)_overflowBits << _bitCount - count - num;
				if (num > count)
				{
					_overflowBits = (byte)(_overflowBits >> count);
				}
			}
			_bitCount -= count;
			_readBits += count;
			return;
		}
		if (_bitCount == count)
		{
			_bitBucket = 0uL;
			_bitCount = 0;
			_readBits += count;
			return;
		}
		count -= _bitCount;
		_readBits += _bitCount;
		_bitCount = 0;
		_bitBucket = 0uL;
		while (count > 8)
		{
			if (ReadNextByte() == -1)
			{
				count = 0;
				IsShort = true;
				break;
			}
			count -= 8;
			_readBits += 8;
		}
		if (count > 0)
		{
			int num2 = ReadNextByte();
			if (num2 == -1)
			{
				IsShort = true;
				return;
			}
			_bitBucket = (ulong)(num2 >> count);
			_bitCount = 8 - count;
			_readBits += count;
		}
	}

	protected void ResetBitReader()
	{
		_bitBucket = 0uL;
		_bitCount = 0;
		_readBits = 0;
		IsShort = false;
	}

	public ulong ReadBits(int count)
	{
		if (count == 0)
		{
			return 0uL;
		}
		int bitsRead;
		ulong result = TryPeekBits(count, out bitsRead);
		SkipBits(count);
		return result;
	}

	public byte PeekByte()
	{
		int bitsRead;
		return (byte)TryPeekBits(8, out bitsRead);
	}

	public byte ReadByte()
	{
		return (byte)ReadBits(8);
	}

	public byte[] ReadBytes(int count)
	{
		List<byte> list = new List<byte>(count);
		while (list.Count < count)
		{
			list.Add(ReadByte());
		}
		return list.ToArray();
	}

	public int Read(byte[] buffer, int index, int count)
	{
		if (index < 0 || index + count > buffer.Length)
		{
			throw new ArgumentOutOfRangeException("index");
		}
		for (int i = 0; i < count; i++)
		{
			int bitsRead;
			byte b = (byte)TryPeekBits(8, out bitsRead);
			if (bitsRead == 0)
			{
				return i;
			}
			buffer[index++] = b;
			SkipBits(8);
		}
		return count;
	}

	public bool ReadBit()
	{
		return ReadBits(1) == 1;
	}

	public short ReadInt16()
	{
		return (short)ReadBits(16);
	}

	public int ReadInt32()
	{
		return (int)ReadBits(32);
	}

	public long ReadInt64()
	{
		return (long)ReadBits(64);
	}

	public ushort ReadUInt16()
	{
		return (ushort)ReadBits(16);
	}

	public uint ReadUInt32()
	{
		return (uint)ReadBits(32);
	}

	public ulong ReadUInt64()
	{
		return ReadBits(64);
	}

	public void SkipBytes(int count)
	{
		SkipBits(count * 8);
	}
}
internal interface IContainerReader : IDisposable
{
	int[] StreamSerials { get; }

	bool CanSeek { get; }

	long WasteBits { get; }

	int PagesRead { get; }

	event EventHandler<NewStreamEventArgs> NewStream;

	bool Init();

	bool FindNextStream();

	int GetTotalPageCount();
}
internal interface IPacketProvider : IDisposable
{
	int StreamSerial { get; }

	bool CanSeek { get; }

	long ContainerBits { get; }

	event EventHandler<ParameterChangeEventArgs> ParameterChange;

	int GetTotalPageCount();

	DataPacket GetNextPacket();

	DataPacket PeekNextPacket();

	DataPacket GetPacket(int packetIndex);

	long GetGranuleCount();

	DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback);

	void SeekToPacket(DataPacket packet, int preRoll);
}
internal class NewStreamEventArgs : EventArgs
{
	public IPacketProvider PacketProvider { get; private set; }

	public bool IgnoreStream { get; set; }

	public NewStreamEventArgs(IPacketProvider packetProvider)
	{
		if (packetProvider == null)
		{
			throw new ArgumentNullException("packetProvider");
		}
		PacketProvider = packetProvider;
	}
}
internal class OggContainerReader : IContainerReader, IDisposable
{
	private class PageHeader
	{
		public int StreamSerial { get; set; }

		public PageFlags Flags { get; set; }

		public long GranulePosition { get; set; }

		public int SequenceNumber { get; set; }

		public long DataOffset { get; set; }

		public int[] PacketSizes { get; set; }

		public bool LastPacketContinues { get; set; }

		public bool IsResync { get; set; }
	}

	private Crc _crc = new Crc();

	private BufferedReadStream _stream;

	private Dictionary<int, PacketReader> _packetReaders;

	private List<int> _disposedStreamSerials;

	private long _nextPageOffset;

	private int _pageCount;

	private byte[] _readBuffer = new byte[65025];

	private long _containerBits;

	private long _wasteBits;

	public int[] StreamSerials => _packetReaders.Keys.ToArray();

	public int PagesRead => _pageCount;

	public bool CanSeek => _stream.CanSeek;

	public long WasteBits => _wasteBits;

	public event EventHandler<NewStreamEventArgs> NewStream;

	public OggContainerReader(Stream stream, bool closeOnDispose)
	{
		_packetReaders = new Dictionary<int, PacketReader>();
		_disposedStreamSerials = new List<int>();
		_stream = (stream as BufferedReadStream) ?? new BufferedReadStream(stream)
		{
			CloseBaseStream = closeOnDispose
		};
	}

	public bool Init()
	{
		_stream.TakeLock();
		try
		{
			return GatherNextPage() != -1;
		}
		finally
		{
			_stream.ReleaseLock();
		}
	}

	public void Dispose()
	{
		int[] streamSerials = StreamSerials;
		foreach (int key in streamSerials)
		{
			_packetReaders[key].Dispose();
		}
		_nextPageOffset = 0L;
		_containerBits = 0L;
		_wasteBits = 0L;
		_stream.Dispose();
	}

	public IPacketProvider GetStream(int streamSerial)
	{
		if (!_packetReaders.TryGetValue(streamSerial, out var value))
		{
			throw new ArgumentOutOfRangeException("streamSerial");
		}
		return value;
	}

	public bool FindNextStream()
	{
		if (!CanSeek)
		{
			throw new InvalidOperationException();
		}
		int count = _packetReaders.Count;
		while (_packetReaders.Count == count)
		{
			_stream.TakeLock();
			try
			{
				if (GatherNextPage() == -1)
				{
					break;
				}
			}
			finally
			{
				_stream.ReleaseLock();
			}
		}
		return count > _packetReaders.Count;
	}

	public int GetTotalPageCount()
	{
		if (!CanSeek)
		{
			throw new InvalidOperationException();
		}
		while (true)
		{
			_stream.TakeLock();
			try
			{
				if (GatherNextPage() == -1)
				{
					break;
				}
			}
			finally
			{
				_stream.ReleaseLock();
			}
		}
		return _pageCount;
	}

	private PageHeader ReadPageHeader(long position)
	{
		_stream.Seek(position, SeekOrigin.Begin);
		if (_stream.Read(_readBuffer, 0, 27) != 27)
		{
			return null;
		}
		if (_readBuffer[0] != 79 || _readBuffer[1] != 103 || _readBuffer[2] != 103 || _readBuffer[3] != 83)
		{
			return null;
		}
		if (_readBuffer[4] != 0)
		{
			return null;
		}
		PageHeader pageHeader = new PageHeader();
		pageHeader.Flags = (PageFlags)_readBuffer[5];
		pageHeader.GranulePosition = BitConverter.ToInt64(_readBuffer, 6);
		pageHeader.StreamSerial = BitConverter.ToInt32(_readBuffer, 14);
		pageHeader.SequenceNumber = BitConverter.ToInt32(_readBuffer, 18);
		uint checkCrc = BitConverter.ToUInt32(_readBuffer, 22);
		_crc.Reset();
		for (int i = 0; i < 22; i++)
		{
			_crc.Update(_readBuffer[i]);
		}
		_crc.Update(0);
		_crc.Update(0);
		_crc.Update(0);
		_crc.Update(0);
		_crc.Update(_readBuffer[26]);
		int num = _readBuffer[26];
		if (_stream.Read(_readBuffer, 0, num) != num)
		{
			return null;
		}
		List<int> list = new List<int>(num);
		int num2 = 0;
		int num3 = 0;
		for (int j = 0; j < num; j++)
		{
			byte b = _readBuffer[j];
			_crc.Update(b);
			if (num3 == list.Count)
			{
				list.Add(0);
			}
			list[num3] += b;
			if (b < byte.MaxValue)
			{
				num3++;
				pageHeader.LastPacketContinues = false;
			}
			else
			{
				pageHeader.LastPacketContinues = true;
			}
			num2 += b;
		}
		pageHeader.PacketSizes = list.ToArray();
		pageHeader.DataOffset = position + 27 + num;
		if (_stream.Read(_readBuffer, 0, num2) != num2)
		{
			return null;
		}
		for (int k = 0; k < num2; k++)
		{
			_crc.Update(_readBuffer[k]);
		}
		if (_crc.Test(checkCrc))
		{
			_containerBits += 8 * (27 + num);
			_pageCount++;
			return pageHeader;
		}
		return null;
	}

	private PageHeader FindNextPageHeader()
	{
		long num = _nextPageOffset;
		bool isResync = false;
		PageHeader pageHeader;
		while ((pageHeader = ReadPageHeader(num)) == null)
		{
			isResync = true;
			_wasteBits += 8L;
			num = (_stream.Position = num + 1);
			int num3 = 0;
			do
			{
				switch (_stream.ReadByte())
				{
				case 79:
					if (_stream.ReadByte() == 103 && _stream.ReadByte() == 103 && _stream.ReadByte() == 83)
					{
						num += num3;
						goto end_IL_0032;
					}
					_stream.Seek(-3L, SeekOrigin.Current);
					break;
				case -1:
					return null;
				}
				_wasteBits += 8L;
				continue;
				end_IL_0032:
				break;
			}
			while (++num3 < 65536);
			if (num3 == 65536)
			{
				return null;
			}
		}
		pageHeader.IsResync = isResync;
		_nextPageOffset = pageHeader.DataOffset;
		for (int i = 0; i < pageHeader.PacketSizes.Length; i++)
		{
			_nextPageOffset += pageHeader.PacketSizes[i];
		}
		return pageHeader;
	}

	private bool AddPage(PageHeader hdr)
	{
		if (!_packetReaders.TryGetValue(hdr.StreamSerial, out var value))
		{
			value = new PacketReader(this, hdr.StreamSerial);
		}
		value.ContainerBits += _containerBits;
		_containerBits = 0L;
		bool isContinued = hdr.PacketSizes.Length == 1 && hdr.LastPacketContinues;
		bool isContinuation = (hdr.Flags & PageFlags.ContinuesPacket) == PageFlags.ContinuesPacket;
		bool isEndOfStream = false;
		bool isResync = hdr.IsResync;
		long num = hdr.DataOffset;
		int num2 = hdr.PacketSizes.Length;
		int[] packetSizes = hdr.PacketSizes;
		foreach (int num3 in packetSizes)
		{
			Packet packet = new Packet(this, num, num3)
			{
				PageGranulePosition = hdr.GranulePosition,
				IsEndOfStream = isEndOfStream,
				PageSequenceNumber = hdr.SequenceNumber,
				IsContinued = isContinued,
				IsContinuation = isContinuation,
				IsResync = isResync
			};
			value.AddPacket(packet);
			num += num3;
			isContinuation = false;
			isResync = false;
			if (--num2 == 1)
			{
				isContinued = hdr.LastPacketContinues;
				isEndOfStream = (hdr.Flags & PageFlags.EndOfStream) == PageFlags.EndOfStream;
			}
		}
		if (!_packetReaders.ContainsKey(hdr.StreamSerial))
		{
			_packetReaders.Add(hdr.StreamSerial, value);
			return true;
		}
		return false;
	}

	private int GatherNextPage()
	{
		PageHeader pageHeader;
		while (true)
		{
			pageHeader = FindNextPageHeader();
			if (pageHeader == null)
			{
				return -1;
			}
			if (!_disposedStreamSerials.Contains(pageHeader.StreamSerial))
			{
				if (!AddPage(pageHeader))
				{
					break;
				}
				EventHandler<NewStreamEventArgs> newStream = this.NewStream;
				if (newStream == null)
				{
					break;
				}
				NewStreamEventArgs newStreamEventArgs = new NewStreamEventArgs(_packetReaders[pageHeader.StreamSerial]);
				newStream(this, newStreamEventArgs);
				if (!newStreamEventArgs.IgnoreStream)
				{
					break;
				}
				_packetReaders[pageHeader.StreamSerial].Dispose();
			}
		}
		return pageHeader.StreamSerial;
	}

	internal void DisposePacketReader(PacketReader packetReader)
	{
		_disposedStreamSerials.Add(packetReader.StreamSerial);
		_packetReaders.Remove(packetReader.StreamSerial);
	}

	internal int PacketReadByte(long offset)
	{
		_stream.TakeLock();
		try
		{
			_stream.Position = offset;
			return _stream.ReadByte();
		}
		finally
		{
			_stream.ReleaseLock();
		}
	}

	internal void PacketDiscardThrough(long offset)
	{
		_stream.TakeLock();
		try
		{
			_stream.DiscardThrough(offset);
		}
		finally
		{
			_stream.ReleaseLock();
		}
	}

	internal void GatherNextPage(int streamSerial)
	{
		if (!_packetReaders.ContainsKey(streamSerial))
		{
			throw new ArgumentOutOfRangeException("streamSerial");
		}
		int num;
		do
		{
			_stream.TakeLock();
			try
			{
				if (_packetReaders[streamSerial].HasEndOfStream)
				{
					break;
				}
				num = GatherNextPage();
				if (num != -1)
				{
					continue;
				}
				foreach (KeyValuePair<int, PacketReader> packetReader in _packetReaders)
				{
					if (!packetReader.Value.HasEndOfStream)
					{
						packetReader.Value.SetEndOfStream();
					}
				}
				break;
			}
			finally
			{
				_stream.ReleaseLock();
			}
		}
		while (num != streamSerial);
	}
}
internal class Crc
{
	private const uint CRC32_POLY = 79764919u;

	private static uint[] crcTable;

	private uint _crc;

	public uint Value => _crc;

	static Crc()
	{
		crcTable = new uint[256];
		for (uint num = 0u; num < 256; num++)
		{
			uint num2 = num << 24;
			for (int i = 0; i < 8; i++)
			{
				num2 = (num2 << 1) ^ ((num2 >= 2147483648u) ? 79764919u : 0u);
			}
			crcTable[num] = num2;
		}
	}

	public Crc()
	{
		Reset();
	}

	public void Reset()
	{
		_crc = 0u;
	}

	public void Update(int nextVal)
	{
		_crc = (_crc << 8) ^ crcTable[nextVal ^ (_crc >> 24)];
	}

	public bool Test(uint checkCrc)
	{
		return _crc == checkCrc;
	}
}
internal class Packet : DataPacket
{
	private long _offset;

	private int _length;

	private int _curOfs;

	private Packet _mergedPacket;

	private Packet _next;

	private Packet _prev;

	private OggContainerReader _containerReader;

	internal Packet Next
	{
		get
		{
			return _next;
		}
		set
		{
			_next = value;
		}
	}

	internal Packet Prev
	{
		get
		{
			return _prev;
		}
		set
		{
			_prev = value;
		}
	}

	internal bool IsContinued
	{
		get
		{
			return GetFlag(PacketFlags.User1);
		}
		set
		{
			SetFlag(PacketFlags.User1, value);
		}
	}

	internal bool IsContinuation
	{
		get
		{
			return GetFlag(PacketFlags.User2);
		}
		set
		{
			SetFlag(PacketFlags.User2, value);
		}
	}

	internal Packet(OggContainerReader containerReader, long streamOffset, int length)
		: base(length)
	{
		_containerReader = containerReader;
		_offset = streamOffset;
		_length = length;
		_curOfs = 0;
	}

	internal void MergeWith(DataPacket continuation)
	{
		if (!(continuation is Packet mergedPacket))
		{
			throw new ArgumentException("Incorrect packet type!");
		}
		base.Length += continuation.Length;
		if (_mergedPacket == null)
		{
			_mergedPacket = mergedPacket;
		}
		else
		{
			_mergedPacket.MergeWith(continuation);
		}
		base.PageGranulePosition = continuation.PageGranulePosition;
		base.PageSequenceNumber = continuation.PageSequenceNumber;
	}

	internal void Reset()
	{
		_curOfs = 0;
		ResetBitReader();
		if (_mergedPacket != null)
		{
			_mergedPacket.Reset();
		}
	}

	protected override int ReadNextByte()
	{
		if (_curOfs == _length)
		{
			if (_mergedPacket == null)
			{
				return -1;
			}
			return _mergedPacket.ReadNextByte();
		}
		int num = _containerReader.PacketReadByte(_offset + _curOfs);
		if (num != -1)
		{
			_curOfs++;
		}
		return num;
	}

	public override void Done()
	{
		if (_mergedPacket != null)
		{
			_mergedPacket.Done();
		}
		else
		{
			_containerReader.PacketDiscardThrough(_offset + _length);
		}
	}
}
[DebuggerTypeProxy(typeof(DebugView))]
internal class PacketReader : IPacketProvider, IDisposable
{
	internal class DebugView
	{
		private PacketReader _reader;

		private Packet _last;

		private Packet _first;

		private Packet[] _packetList = new Packet[0];

		public OggContainerReader Container => _reader._container;

		public int StreamSerial => _reader._streamSerial;

		public bool EndOfStreamFound => _reader._eosFound;

		public int CurrentPacketIndex
		{
			get
			{
				if (_reader._current == null)
				{
					return -1;
				}
				return Array.IndexOf(Packets, _reader._current);
			}
		}

		public Packet[] Packets
		{
			get
			{
				if (_reader._last == _last && _reader._first == _first)
				{
					return _packetList;
				}
				_last = _reader._last;
				_first = _reader._first;
				List<Packet> list = new List<Packet>();
				for (Packet packet = _first; packet != null; packet = packet.Next)
				{
					list.Add(packet);
				}
				_packetList = list.ToArray();
				return _packetList;
			}
		}

		public DebugView(PacketReader reader)
		{
			if (reader == null)
			{
				throw new ArgumentNullException("reader");
			}
			_reader = reader;
		}
	}

	private OggContainerReader _container;

	private int _streamSerial;

	private bool _eosFound;

	private Packet _first;

	private Packet _current;

	private Packet _last;

	private object _packetLock = new object();

	internal bool HasEndOfStream => _eosFound;

	public int StreamSerial => _streamSerial;

	public long ContainerBits { get; set; }

	public bool CanSeek => _container.CanSeek;

	public event EventHandler<ParameterChangeEventArgs> ParameterChange;

	internal PacketReader(OggContainerReader container, int streamSerial)
	{
		_container = container;
		_streamSerial = streamSerial;
	}

	public void Dispose()
	{
		_eosFound = true;
		_container.DisposePacketReader(this);
		_container = null;
		_current = null;
		if (_first != null)
		{
			Packet packet = _first;
			_first = null;
			while (packet.Next != null)
			{
				Packet next = packet.Next;
				packet.Next = null;
				packet = next;
				packet.Prev = null;
			}
			packet = null;
		}
		_last = null;
	}

	internal void AddPacket(Packet packet)
	{
		lock (_packetLock)
		{
			if (_eosFound)
			{
				return;
			}
			if (packet.IsResync)
			{
				packet.IsContinuation = false;
				if (_last != null)
				{
					_last.IsContinued = false;
				}
			}
			if (packet.IsContinuation)
			{
				if (_last == null)
				{
					throw new InvalidDataException();
				}
				if (!_last.IsContinued)
				{
					throw new InvalidDataException();
				}
				_last.MergeWith(packet);
				_last.IsContinued = packet.IsContinued;
			}
			else
			{
				if (packet == null)
				{
					throw new ArgumentException("Wrong packet datatype", "packet");
				}
				if (_first == null)
				{
					_first = packet;
					_last = packet;
				}
				else
				{
					Packet packet2 = (packet.Prev = _last);
					Packet last2 = (packet2.Next = packet);
					_last = last2;
				}
			}
			if (packet.IsEndOfStream)
			{
				SetEndOfStream();
			}
		}
	}

	internal void SetEndOfStream()
	{
		lock (_packetLock)
		{
			_eosFound = true;
			if (_last.IsContinued)
			{
				_last = _last.Prev;
				_last.Next.Prev = null;
				_last.Next = null;
			}
		}
	}

	public DataPacket GetNextPacket()
	{
		return _current = PeekNextPacketInternal();
	}

	public DataPacket PeekNextPacket()
	{
		return PeekNextPacketInternal();
	}

	private Packet PeekNextPacketInternal()
	{
		Packet packet;
		if (_current == null)
		{
			packet = _first;
		}
		else
		{
			while (true)
			{
				lock (_packetLock)
				{
					packet = _current.Next;
					if ((packet != null && !packet.IsContinued) || _eosFound)
					{
						break;
					}
					goto IL_004f;
				}
				IL_004f:
				_container.GatherNextPage(_streamSerial);
			}
		}
		if (packet != null)
		{
			if (packet.IsContinued)
			{
				throw new InvalidDataException("Packet is incomplete!");
			}
			packet.Reset();
		}
		return packet;
	}

	internal void ReadAllPages()
	{
		if (!CanSeek)
		{
			throw new InvalidOperationException();
		}
		while (!_eosFound)
		{
			_container.GatherNextPage(_streamSerial);
		}
	}

	internal DataPacket GetLastPacket()
	{
		ReadAllPages();
		return _last;
	}

	public int GetTotalPageCount()
	{
		ReadAllPages();
		int num = 0;
		int num2 = 0;
		for (Packet packet = _first; packet != null; packet = packet.Next)
		{
			if (packet.PageSequenceNumber != num2)
			{
				num++;
				num2 = packet.PageSequenceNumber;
			}
		}
		return num;
	}

	public DataPacket GetPacket(int packetIndex)
	{
		if (!CanSeek)
		{
			throw new InvalidOperationException();
		}
		if (packetIndex < 0)
		{
			throw new ArgumentOutOfRangeException("index");
		}
		if (_first == null)
		{
			throw new InvalidOperationException("Packet reader has no packets!");
		}
		Packet packet = _first;
		while (--packetIndex >= 0)
		{
			while (packet.Next == null)
			{
				if (_eosFound)
				{
					throw new ArgumentOutOfRangeException("index");
				}
				_container.GatherNextPage(_streamSerial);
			}
			packet = packet.Next;
		}
		packet.Reset();
		return packet;
	}

	private Packet GetLastPacketInPage(Packet packet)
	{
		if (packet != null)
		{
			int pageSequenceNumber = packet.PageSequenceNumber;
			while (packet.Next != null && packet.Next.PageSequenceNumber == pageSequenceNumber)
			{
				packet = packet.Next;
			}
			if (packet != null && packet.IsContinued)
			{
				packet = packet.Prev;
			}
		}
		return packet;
	}

	private Packet FindPacketInPage(Packet pagePacket, long targetGranulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback)
	{
		Packet lastPacketInPage = GetLastPacketInPage(pagePacket);
		if (lastPacketInPage == null)
		{
			return null;
		}
		Packet packet = lastPacketInPage;
		do
		{
			if (!packet.GranuleCount.HasValue)
			{
				if (packet == lastPacketInPage)
				{
					packet.GranulePosition = packet.PageGranulePosition;
				}
				else
				{
					packet.GranulePosition = packet.Next.GranulePosition - packet.Next.GranuleCount.Value;
				}
				if (packet == _last && _eosFound && packet.Prev.PageSequenceNumber < packet.PageSequenceNumber)
				{
					packet.GranuleCount = (int)(packet.GranulePosition - packet.Prev.PageGranulePosition);
				}
				else if (packet.Prev != null)
				{
					packet.Prev.Reset();
					packet.Reset();
					packet.GranuleCount = packetGranuleCountCallback(packet, packet.Prev);
				}
				else
				{
					if (packet.GranulePosition > packet.Next.GranulePosition - packet.Next.GranuleCount)
					{
						throw new InvalidOperationException("First data packet size mismatch");
					}
					packet.GranuleCount = (int)packet.GranulePosition;
				}
			}
			if (targetGranulePos <= packet.GranulePosition && targetGranulePos > packet.GranulePosition - packet.GranuleCount)
			{
				if (packet.Prev != null && !packet.Prev.GranuleCount.HasValue)
				{
					packet.Prev.GranulePosition = packet.GranulePosition - packet.GranuleCount.Value;
				}
				return packet;
			}
			packet = packet.Prev;
		}
		while (packet != null && packet.PageSequenceNumber == lastPacketInPage.PageSequenceNumber);
		if (packet != null && packet.PageGranulePosition < targetGranulePos)
		{
			packet.GranulePosition = packet.PageGranulePosition;
			return packet.Next;
		}
		return null;
	}

	public DataPacket FindPacket(long granulePos, Func<DataPacket, DataPacket, int> packetGranuleCountCallback)
	{
		if (granulePos < 0)
		{
			throw new ArgumentOutOfRangeException("granulePos");
		}
		Packet packet = null;
		Packet packet2 = _current ?? _first;
		if (granulePos > packet2.PageGranulePosition)
		{
			while (granulePos > packet2.PageGranulePosition)
			{
				if ((packet2.Next == null || packet2.IsContinued) && !_eosFound)
				{
					_container.GatherNextPage(_streamSerial);
					if (_eosFound)
					{
						packet2 = null;
						break;
					}
				}
				packet2 = packet2.Next;
			}
			return FindPacketInPage(packet2, granulePos, packetGranuleCountCallback);
		}
		while (packet2.Prev != null && (granulePos <= packet2.Prev.PageGranulePosition || packet2.Prev.PageGranulePosition == -1))
		{
			packet2 = packet2.Prev;
		}
		return FindPacketInPage(packet2, granulePos, packetGranuleCountCallback);
	}

	public void SeekToPacket(DataPacket packet, int preRoll)
	{
		if (preRoll < 0)
		{
			throw new ArgumentOutOfRangeException("preRoll");
		}
		if (packet == null)
		{
			throw new ArgumentNullException("granulePos");
		}
		Packet packet2 = packet as Packet;
		if (packet2 == null)
		{
			throw new ArgumentException("Incorrect packet type!", "packet");
		}
		while (--preRoll >= 0)
		{
			packet2 = packet2.Prev;
			if (packet2 == null)
			{
				throw new ArgumentOutOfRangeException("preRoll");
			}
		}
		_current = packet2.Prev;
	}

	public long GetGranuleCount()
	{
		return GetLastPacket().PageGranulePosition;
	}
}
[Flags]
internal enum PageFlags
{
	None = 0,
	ContinuesPacket = 1,
	BeginningOfStream = 2,
	EndOfStream = 4
}
internal class OpusHeader
{
	private byte version;

	private byte channel_count;

	private ushort pre_skip;

	private uint input_sample_rate;

	private short output_gain;

	private byte mapping_family;

	private byte stream_count;

	private byte coupled_count;
}
public class OpusOggReadStream
{
	private const double GranuleSampleRate = 48000.0;

	private readonly Stream _stream;

	private readonly IOpusDecoder _decoder;

	private byte[] _nextDataPacket;

	private IPacketProvider _packetProvider;

	private bool _endOfStream;

	public bool CanSeek => _stream.CanSeek;

	public OpusTags Tags { get; private set; }

	public bool HasNextPacket => !_endOfStream;

	public string LastError { get; private set; }

	public long PageGranulePosition { get; private set; }

	public TimeSpan CurrentTime => TimeSpan.FromSeconds((double)PageGranulePosition / 48000.0);

	public long GranuleCount { get; private set; }

	public TimeSpan TotalTime => TimeSpan.FromSeconds((double)GranuleCount / 48000.0);

	public long PagePosition { get; private set; }

	public long PageCount { get; private set; }

	public OpusOggReadStream(IOpusDecoder decoder, Stream stream)
	{
		if (stream == null)
		{
			throw new ArgumentNullException("stream");
		}
		_stream = stream;
		_decoder = decoder;
		_endOfStream = !Initialize();
	}

	public short[] DecodeNextPacket()
	{
		//IL_0091: Expected O, but got Unknown
		if (_decoder == null)
		{
			throw new InvalidOperationException("Cannot decode opus packets as a decoder was never provided");
		}
		if (_nextDataPacket == null || _nextDataPacket.Length == 0)
		{
			_endOfStream = true;
			return null;
		}
		try
		{
			int numSamples = OpusPacketInfo.GetNumSamples((ReadOnlySpan<byte>)_nextDataPacket.AsSpan(), _decoder.SampleRate);
			short[] array = new short[numSamples * _decoder.NumChannels];
			_decoder.Decode((ReadOnlySpan<byte>)_nextDataPacket.AsSpan(), array.AsSpan(), numSamples, false);
			QueueNextPacket();
			return array;
		}
		catch (OpusException val)
		{
			OpusException val2 = val;
			LastError = "Opus decoder threw exception: " + ((Exception)(object)val2).Message;
			return null;
		}
	}

	public byte[] ReadNextRawPacket()
	{
		if (_nextDataPacket == null || _nextDataPacket.Length == 0)
		{
			_endOfStream = true;
			return null;
		}
		byte[] nextDataPacket = _nextDataPacket;
		QueueNextPacket();
		return nextDataPacket;
	}

	private bool Initialize()
	{
		try
		{
			OggContainerReader oggContainerReader = new OggContainerReader(_stream, closeOnDispose: true);
			if (!oggContainerReader.Init())
			{
				LastError = "Could not initialize stream";
				return false;
			}
			if (oggContainerReader.StreamSerials.Length == 0)
			{
				LastError = "Initialization failed: No elementary streams found in input file";
				return false;
			}
			int streamSerial = oggContainerReader.StreamSerials[0];
			_packetProvider = oggContainerReader.GetStream(streamSerial);
			if (CanSeek)
			{
				GranuleCount = _packetProvider.GetGranuleCount();
				PageCount = _packetProvider.GetTotalPageCount();
			}
			QueueNextPacket();
			return true;
		}
		catch (Exception ex)
		{
			LastError = "Unknown initialization error: " + ex.Message;
			return false;
		}
	}

	public void SeekTo(TimeSpan playbackTime)
	{
		if (!CanSeek)
		{
			throw new InvalidOperationException("Stream is not seekable.");
		}
		if (playbackTime < TimeSpan.Zero || playbackTime > TotalTime)
		{
			throw new ArgumentOutOfRangeException("playbackTime");
		}
		long granulePosition = Convert.ToInt64(playbackTime.TotalSeconds * 48000.0);
		SeekToGranulePosition(granulePosition);
	}

	private void SeekToGranulePosition(long granulePosition)
	{
		if (!CanSeek)
		{
			throw new InvalidOperationException("Stream is not seekable.");
		}
		if (granulePosition < 0 || granulePosition > GranuleCount)
		{
			throw new ArgumentOutOfRangeException("granulePosition");
		}
		DataPacket dataPacket = _packetProvider.FindPacket(granulePosition, GetPacketLength);
		if (dataPacket == null || dataPacket.IsEndOfStream)
		{
			_endOfStream = true;
			_nextDataPacket = null;
			return;
		}
		_packetProvider.SeekToPacket(dataPacket, 1);
		PageGranulePosition = _packetProvider.PeekNextPacket().PageGranulePosition;
		if (_decoder != null)
		{
			_decoder.ResetState();
		}
	}

	private int GetPacketLength(DataPacket curPacket, DataPacket lastPacket)
	{
		if (lastPacket == null || curPacket.IsResync)
		{
			return 0;
		}
		if (curPacket.ReadBit())
		{
			return 0;
		}
		if (lastPacket.ReadBit())
		{
			return 0;
		}
		return 1;
	}

	private void QueueNextPacket()
	{
		if (_endOfStream)
		{
			return;
		}
		DataPacket nextPacket = _packetProvider.GetNextPacket();
		if (nextPacket == null)
		{
			_endOfStream = true;
			_nextDataPacket = null;
			return;
		}
		PageGranulePosition = nextPacket.PageGranulePosition;
		PagePosition = nextPacket.PageSequenceNumber;
		byte[] array = new byte[nextPacket.Length];
		nextPacket.Read(array, 0, nextPacket.Length);
		nextPacket.Done();
		if (array.Length > 8 && "OpusHead".Equals(Encoding.UTF8.GetString(array, 0, 8)))
		{
			QueueNextPacket();
		}
		else if (array.Length > 8 && "OpusTags".Equals(Encoding.UTF8.GetString(array, 0, 8)))
		{
			Tags = OpusTags.ParsePacket(array, array.Length);
			QueueNextPacket();
		}
		else
		{
			_nextDataPacket = array;
		}
	}
}
public class OpusOggWriteStream
{
	private const int FRAME_SIZE_MS = 20;

	private IOpusEncoder _encoder;

	private Stream _outputStream;

	private Crc _crc;

	private int _inputChannels;

	private IResampler _resampler;

	private int _inputSampleRate;

	private readonly bool _leaveOpen;

	private int _encoderSampleRate;

	private short[] _opusFrame;

	private int _opusFrameSamples;

	private int _opusFrameIndex;

	private byte[] _currentHeader = new byte[400];

	private byte[] _currentPayload = new byte[65536];

	private int _headerIndex;

	private int _payloadIndex;

	private int _pageCounter;

	private int _logicalStreamId;

	private long _granulePosition;

	private byte _lacingTableCount;

	private const int PAGE_FLAGS_POS = 5;

	private const int GRANULE_COUNT_POS = 6;

	private const int CHECKSUM_HEADER_POS = 22;

	private const int SEGMENT_COUNT_POS = 26;

	private bool _finalized;

	public OpusOggWriteStream(IOpusEncoder encoder, Stream outputStream, OpusTags fileTags = null, int inputSampleRate = 0, int resamplerQuality = 5, bool leaveOpen = false)
	{
		_encoder = encoder;
		if (_encoder.UseDTX)
		{
			throw new ArgumentException("DTX is not currently supported in Ogg streams");
		}
		_inputSampleRate = inputSampleRate;
		_leaveOpen = leaveOpen;
		if (_inputSampleRate == 0)
		{
			_inputSampleRate = _encoder.SampleRate;
		}
		_logicalStreamId = new Random().Next();
		_encoderSampleRate = encoder.SampleRate;
		_inputChannels = encoder.NumChannels;
		_outputStream = outputStream;
		_opusFrameIndex = 0;
		_granulePosition = 0L;
		_opusFrameSamples = (int)((long)_encoderSampleRate * 20L / 1000);
		_opusFrame = new short[_opusFrameSamples * _inputChannels];
		_crc = new Crc();
		_resampler = ResamplerFactory.CreateResampler(_inputChannels, _inputSampleRate, _encoderSampleRate, resamplerQuality, (TextWriter)null);
		BeginNewPage();
		WriteOpusHeadPage();
		WriteOpusTagsPage(fileTags);
	}

	public void WriteSamples(short[] data, int offset, int count)
	{
		if (_finalized)
		{
			throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!");
		}
		if (data.Length - offset < count)
		{
			throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + count + " samples, but it actually only has " + (data.Length - offset));
		}
		int num = 0;
		for (int num2 = Math.Min(_opusFrame.Length - _opusFrameIndex, count - num); num2 > 0; num2 = Math.Min(_opusFrame.Length - _opusFrameIndex, count - num))
		{
			if (_inputSampleRate != _encoderSampleRate)
			{
				int num3 = (count - num) / _inputChannels;
				int num4 = num2 / _inputChannels;
				_resampler.ProcessInterleaved(data.AsSpan(offset + num), ref num3, _opusFrame.AsSpan(_opusFrameIndex), ref num4);
				num += num3 * _inputChannels;
				_opusFrameIndex += num4 * _inputChannels;
			}
			else
			{
				data.AsSpan(offset + num, num2).CopyTo(_opusFrame.AsSpan(_opusFrameIndex, num2));
				_opusFrameIndex += num2;
				num += num2;
			}
			if (_opusFrameIndex == _opusFrame.Length)
			{
				int num5 = _encoder.Encode((ReadOnlySpan<short>)_opusFrame.AsSpan(), _opusFrameSamples, _currentPayload.AsSpan(_payloadIndex), _currentPayload.Length - _payloadIndex);
				_payloadIndex += num5;
				_granulePosition += 960L;
				int num6 = num5;
				while (num6 >= 255)
				{
					num6 -= 255;
					_currentHeader[_headerIndex++] = byte.MaxValue;
					_lacingTableCount++;
				}
				_currentHeader[_headerIndex++] = (byte)num6;
				_lacingTableCount++;
				if (_lacingTableCount > 248)
				{
					FinalizePage();
				}
				_opusFrameIndex = 0;
			}
		}
	}

	public void WriteSamples(float[] data, int offset, int count)
	{
		if (_finalized)
		{
			throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!");
		}
		short[] array = new short[count];
		for (int i = 0; i < count; i++)
		{
			array[i] = (short)(data[i + offset] * 32767f);
		}
		WriteSamples(array, 0, count);
	}

	public void Finish()
	{
		int num = _opusFrame.Length - _opusFrameIndex;
		short[] data = new short[num];
		WriteSamples(data, 0, num);
		FinalizePage();
		WriteStreamFinishedPage();
		_outputStream.Flush();
		if (!_leaveOpen)
		{
			_outputStream.Dispose();
		}
		_finalized = true;
	}

	private void WriteStreamFinishedPage()
	{
		_currentHeader[_headerIndex++] = 0;
		_lacingTableCount++;
		_currentHeader[5] = 4;
		FinalizePage();
	}

	private void WriteOpusHeadPage()
	{
		if (_payloadIndex != 0)
		{
			throw new InvalidOperationException("Must begin writing OpusHead on a new page!");
		}
		_payloadIndex += WriteValueToByteBuffer("OpusHead", _currentPayload, _payloadIndex);
		_currentPayload[_payloadIndex++] = 1;
		_currentPayload[_payloadIndex++] = (byte)_inputChannels;
		short val = 0;
		_payloadIndex += WriteValueToByteBuffer(val, _currentPayload, _payloadIndex);
		_payloadIndex += WriteValueToByteBuffer(_encoderSampleRate, _currentPayload, _payloadIndex);
		short val2 = 0;
		_payloadIndex += WriteValueToByteBuffer(val2, _currentPayload, _payloadIndex);
		_currentPayload[_payloadIndex++] = 0;
		_currentHeader[_headerIndex++] = (byte)_payloadIndex;
		_lacingTableCount++;
		_currentHeader[5] = 2;
		FinalizePage();
	}

	private void WriteOpusTagsPage(OpusTags tags = null)
	{
		if (tags == null)
		{
			tags = new OpusTags();
		}
		if (string.IsNullOrEmpty(tags.Comment))
		{
			tags.Comment = _encoder.GetVersionString();
		}
		if (_payloadIndex != 0)
		{
			throw new InvalidOperationException("Must begin writing OpusTags on a new page!");
		}
		_payloadIndex += WriteValueToByteBuffer("OpusTags", _currentPayload, _payloadIndex);
		int num = WriteValueToByteBuffer(tags.Comment, _currentPayload, _payloadIndex + 4);
		_payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex);
		_payloadIndex += num;
		int payloadIndex = _payloadIndex;
		_payloadIndex += 4;
		int num2 = 0;
		foreach (KeyValuePair<string, string> field in tags.Fields)
		{
			if (!string.IsNullOrEmpty(field.Key) && !string.IsNullOrEmpty(field.Value))
			{
				num = WriteValueToByteBuffer(field.Key + "=" + field.Value, _currentPayload, _payloadIndex + 4);
				_payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex);
				_payloadIndex += num;
				num2++;
			}
		}
		WriteValueToByteBuffer(num2, _currentPayload, payloadIndex);
		int num3;
		for (num3 = _payloadIndex; num3 >= 255; num3 -= 255)
		{
			_currentHeader[_headerIndex++] = byte.MaxValue;
			_lacingTableCount++;
		}
		_currentHeader[_headerIndex++] = (byte)num3;
		_lacingTableCount++;
		FinalizePage();
	}

	private void BeginNewPage()
	{
		_headerIndex = 0;
		_payloadIndex = 0;
		_lacingTableCount = 0;
		_headerIndex += WriteValueToByteBuffer("OggS", _currentHeader, _headerIndex);
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = 0;
		_headerIndex += WriteValueToByteBuffer(_granulePosition, _currentHeader, _headerIndex);
		_headerIndex += WriteValueToByteBuffer(_logicalStreamId, _currentHeader, _headerIndex);
		_headerIndex += WriteValueToByteBuffer(_pageCounter, _currentHeader, _headerIndex);
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = _lacingTableCount;
		_pageCounter++;
	}

	private void FinalizePage()
	{
		if (_finalized)
		{
			throw new InvalidOperationException("Cannot finalize page, the output stream is already closed!");
		}
		if (_lacingTableCount != 0)
		{
			_currentHeader[26] = _lacingTableCount;
			WriteValueToByteBuffer(_granulePosition, _currentHeader, 6);
			_crc.Reset();
			for (int i = 0; i < _headerIndex; i++)
			{
				_crc.Update(_currentHeader[i]);
			}
			for (int j = 0; j < _payloadIndex; j++)
			{
				_crc.Update(_currentPayload[j]);
			}
			WriteValueToByteBuffer(_crc.Value, _currentHeader, 22);
			_outputStream.Write(_currentHeader, 0, _headerIndex);
			_outputStream.Write(_currentPayload, 0, _payloadIndex);
			BeginNewPage();
		}
	}

	private static int WriteValueToByteBuffer(int val, byte[] target, int targetOffset)
	{
		BinaryHelpers.Int32ToByteArrayLittleEndian(val, target, targetOffset);
		return 4;
	}

	private static int WriteValueToByteBuffer(long val, byte[] target, int targetOffset)
	{
		BinaryHelpers.Int64ToByteArrayLittleEndian(val, target, targetOffset);
		return 8;
	}

	private static int WriteValueToByteBuffer(uint val, byte[] target, int targetOffset)
	{
		BinaryHelpers.UInt32ToByteArrayLittleEndian(val, target, targetOffset);
		return 4;
	}

	private static int WriteValueToByteBuffer(short val, byte[] target, int targetOffset)
	{
		BinaryHelpers.Int16ToByteArrayLittleEndian(val, target, targetOffset);
		return 2;
	}

	private static int WriteValueToByteBuffer(string val, byte[] target, int targetOffset)
	{
		if (string.IsNullOrEmpty(val))
		{
			return 0;
		}
		return Encoding.UTF8.GetBytes(val, 0, val.Length, target, targetOffset);
	}
}
public class OpusRawWriteStream
{
	private Stream _outputStream;

	private Crc _crc;

	private int _inputChannels;

	private readonly bool _leaveOpen;

	private int _inputSampleRate;

	private byte[] _currentHeader = new byte[400];

	private byte[] _currentPayload = new byte[65536];

	private int _headerIndex;

	private int _payloadIndex;

	private int _pageCounter;

	private int _logicalStreamId;

	private long _granulePosition;

	private byte _lacingTableCount;

	private const int PAGE_FLAGS_POS = 5;

	private const int GRANULE_COUNT_POS = 6;

	private const int CHECKSUM_HEADER_POS = 22;

	private const int SEGMENT_COUNT_POS = 26;

	private bool _finalized;

	public OpusRawWriteStream(Stream outputStream, OpusTags fileTags, int inputSampleRate, int inputNumChannels, bool leaveOpen = false)
	{
		_inputSampleRate = inputSampleRate;
		_logicalStreamId = new Random().Next();
		_inputChannels = inputNumChannels;
		_leaveOpen = leaveOpen;
		_outputStream = outputStream;
		_granulePosition = 0L;
		_crc = new Crc();
		BeginNewPage();
		WriteOpusHeadPage();
		WriteOpusTagsPage(fileTags);
	}

	public void WriteSinglePacket(byte[] opusPacket, int offset, int packetSize)
	{
		if (_finalized)
		{
			throw new InvalidOperationException("Cannot write new samples to Ogg file, the output stream is already closed!");
		}
		if (opusPacket.Length - offset < packetSize)
		{
			throw new ArgumentOutOfRangeException("The given audio buffer claims to have " + packetSize + " samples, but it actually only has " + (opusPacket.Length - offset));
		}
		Array.Copy(opusPacket, offset, _currentPayload, _payloadIndex, packetSize);
		_payloadIndex += packetSize;
		int numSamplesPerFrame = OpusPacketInfo.GetNumSamplesPerFrame((ReadOnlySpan<byte>)opusPacket.AsSpan(offset), 48000);
		_granulePosition += numSamplesPerFrame;
		int num = packetSize;
		while (num >= 255)
		{
			num -= 255;
			_currentHeader[_headerIndex++] = byte.MaxValue;
			_lacingTableCount++;
		}
		_currentHeader[_headerIndex++] = (byte)num;
		_lacingTableCount++;
		if (_lacingTableCount > 248)
		{
			FinalizePage();
		}
	}

	public void Finish()
	{
		FinalizePage();
		WriteStreamFinishedPage();
		_outputStream.Flush();
		if (!_leaveOpen)
		{
			_outputStream.Dispose();
		}
		_finalized = true;
	}

	private void WriteStreamFinishedPage()
	{
		_currentHeader[_headerIndex++] = 0;
		_lacingTableCount++;
		_currentHeader[5] = 4;
		FinalizePage();
	}

	private void WriteOpusHeadPage()
	{
		if (_payloadIndex != 0)
		{
			throw new InvalidOperationException("Must begin writing OpusHead on a new page!");
		}
		_payloadIndex += WriteValueToByteBuffer("OpusHead", _currentPayload, _payloadIndex);
		_currentPayload[_payloadIndex++] = 1;
		_currentPayload[_payloadIndex++] = (byte)_inputChannels;
		short val = 0;
		_payloadIndex += WriteValueToByteBuffer(val, _currentPayload, _payloadIndex);
		_payloadIndex += WriteValueToByteBuffer(_inputSampleRate, _currentPayload, _payloadIndex);
		short val2 = 0;
		_payloadIndex += WriteValueToByteBuffer(val2, _currentPayload, _payloadIndex);
		_currentPayload[_payloadIndex++] = 0;
		_currentHeader[_headerIndex++] = (byte)_payloadIndex;
		_lacingTableCount++;
		_currentHeader[5] = 2;
		FinalizePage();
	}

	private void WriteOpusTagsPage(OpusTags tags = null)
	{
		if (tags == null)
		{
			tags = new OpusTags();
		}
		if (string.IsNullOrEmpty(tags.Comment))
		{
			tags.Comment = "Concentus.OggFile";
		}
		if (_payloadIndex != 0)
		{
			throw new InvalidOperationException("Must begin writing OpusTags on a new page!");
		}
		_payloadIndex += WriteValueToByteBuffer("OpusTags", _currentPayload, _payloadIndex);
		int num = WriteValueToByteBuffer(tags.Comment, _currentPayload, _payloadIndex + 4);
		_payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex);
		_payloadIndex += num;
		int payloadIndex = _payloadIndex;
		_payloadIndex += 4;
		int num2 = 0;
		foreach (KeyValuePair<string, string> field in tags.Fields)
		{
			if (!string.IsNullOrEmpty(field.Key) && !string.IsNullOrEmpty(field.Value))
			{
				num = WriteValueToByteBuffer(field.Key + "=" + field.Value, _currentPayload, _payloadIndex + 4);
				_payloadIndex += WriteValueToByteBuffer(num, _currentPayload, _payloadIndex);
				_payloadIndex += num;
				num2++;
			}
		}
		WriteValueToByteBuffer(num2, _currentPayload, payloadIndex);
		int num3;
		for (num3 = _payloadIndex; num3 >= 255; num3 -= 255)
		{
			_currentHeader[_headerIndex++] = byte.MaxValue;
			_lacingTableCount++;
		}
		_currentHeader[_headerIndex++] = (byte)num3;
		_lacingTableCount++;
		FinalizePage();
	}

	private void BeginNewPage()
	{
		_headerIndex = 0;
		_payloadIndex = 0;
		_lacingTableCount = 0;
		_headerIndex += WriteValueToByteBuffer("OggS", _currentHeader, _headerIndex);
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = 0;
		_headerIndex += WriteValueToByteBuffer(_granulePosition, _currentHeader, _headerIndex);
		_headerIndex += WriteValueToByteBuffer(_logicalStreamId, _currentHeader, _headerIndex);
		_headerIndex += WriteValueToByteBuffer(_pageCounter, _currentHeader, _headerIndex);
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = 0;
		_currentHeader[_headerIndex++] = _lacingTableCount;
		_pageCounter++;
	}

	private void FinalizePage()
	{
		if (_finalized)
		{
			throw new InvalidOperationException("Cannot finalize page, the output stream is already closed!");
		}
		if (_lacingTableCount != 0)
		{
			_currentHeader[26] = _lacingTableCount;
			WriteValueToByteBuffer(_granulePosition, _currentHeader, 6);
			_crc.Reset();
			for (int i = 0; i < _headerIndex; i++)
			{
				_crc.Update(_currentHeader[i]);
			}
			for (int j = 0; j < _payloadIndex; j++)
			{
				_crc.Update(_currentPayload[j]);
			}
			WriteValueToByteBuffer(_crc.Value, _currentHeader, 22);
			_outputStream.Write(_currentHeader, 0, _headerIndex);
			_outputStream.Write(_currentPayload, 0, _payloadIndex);
			BeginNewPage();
		}
	}

	private static int WriteValueToByteBuffer(int val, byte[] target, int targetOffset)
	{
		Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 4);
		return 4;
	}

	private static int WriteValueToByteBuffer(long val, byte[] target, int targetOffset)
	{
		Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 8);
		return 8;
	}

	private static int WriteValueToByteBuffer(uint val, byte[] target, int targetOffset)
	{
		Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 4);
		return 4;
	}

	private static int WriteValueToByteBuffer(short val, byte[] target, int targetOffset)
	{
		Array.Copy(BitConverter.GetBytes(val), 0, target, targetOffset, 2);
		return 2;
	}

	private static int WriteValueToByteBuffer(string val, byte[] target, int targetOffset)
	{
		if (string.IsNullOrEmpty(val))
		{
			return 0;
		}
		byte[] bytes = Encoding.UTF8.GetBytes(val);
		Array.Copy(bytes, 0, target, targetOffset, bytes.Length);
		return bytes.Length;
	}
}
public class OpusTagName
{
	public const string Title = "title";

	public const string Artist = "artist";

	public const string Album = "album";
}
public class OpusTags
{
	private string _comment = string.Empty;

	private IDictionary<string, string> _fields = new Dictionary<string, string>();

	public string Comment
	{
		get
		{
			return _comment;
		}
		set
		{
			_comment = value;
		}
	}

	public IDictionary<string, string> Fields => _fields;

	internal static OpusTags ParsePacket(byte[] packet, int packetLength)
	{
		if (packetLength < 8)
		{
			return null;
		}
		if (!"OpusTags".Equals(Encoding.UTF8.GetString(packet, 0, 8)))
		{
			return null;
		}
		OpusTags opusTags = new OpusTags();
		int num = 8;
		int num2 = BitConverter.ToInt32(packet, num);
		num += 4;
		if (num2 > 0)
		{
			opusTags._comment = Encoding.UTF8.GetString(packet, num, num2);
			num += num2;
		}
		int num3 = BitConverter.ToInt32(packet, num);
		num += 4;
		for (int i = 0; i < num3; i++)
		{
			num2 = BitConverter.ToInt32(packet, num);
			num += 4;
			if (num2 > 0)
			{
				string @string = Encoding.UTF8.GetString(packet, num, num2);
				num += num2;
				int num4 = @string.IndexOf('=');
				if (num4 > 0)
				{
					string key = @string.Substring(0, num4);
					string value = @string.Substring(num4 + 1);
					opusTags._fields[key] = value;
				}
			}
		}
		return opusTags;
	}
}
internal class ParameterChangeEventArgs : EventArgs
{
	public DataPacket FirstPacket { get; private set; }

	public ParameterChangeEventArgs(DataPacket firstPacket)
	{
		FirstPacket = firstPacket;
	}
}
internal class StreamReadBuffer : IDisposable
{
	internal class StreamWrapper
	{
		internal Stream Source;

		internal object LockObject = new object();

		internal long EofOffset = long.MaxValue;

		internal int RefCount = 1;
	}

	private class SavedBuffer
	{
		public byte[] Buffer;

		public long BaseOffset;

		public int End;

		public int DiscardCount;

		public long VersionSaved;
	}

	private static Dictionary<Stream, StreamWrapper> _lockObjects = new Dictionary<Stream, StreamWrapper>();

	private StreamWrapper _wrapper;

	private int _maxSize;

	private byte[] _data;

	private long _baseOffset;

	private int _end;

	private int _discardCount;

	private bool _minimalRead;

	private long _versionCounter;

	private List<SavedBuffer> _savedBuffers;

	public bool MinimalRead
	{
		get
		{
			return _minimalRead;
		}
		set
		{
			_minimalRead = value;
		}
	}

	public int MaxSize
	{
		get
		{
			return _maxSize;
		}
		set
		{
			if (value < 1)
			{
				throw new ArgumentOutOfRangeException("Must be greater than zero.");
			}
			int num = 1 << (int)Math.Ceiling(Math.Log(value, 2.0));
			if (num < _end)
			{
				if (num < _end - _discardCount)
				{
					throw new ArgumentOutOfRangeException("Must be greater than or equal to the number of bytes currently buffered.");
				}
				CommitDiscard();
				byte[] array = new byte[num];
				Buffer.BlockCopy(_data, 0, array, 0, _end);
				_data = array;
			}
			_maxSize = num;
		}
	}

	public long BaseOffset => _baseOffset + _discardCount;

	public int BytesFilled => _end - _discardCount;

	public int Length => _data.Length;

	internal long BufferEndOffset
	{
		get
		{
			if (_end - _discardCount > 0)
			{
				return _baseOffset + _discardCount + _maxSize;
			}
			if (_wrapper.Source.CanSeek)
			{
				return _wrapper.Source.Length;
			}
			return _baseOffset + Length;
		}
	}

	internal StreamReadBuffer(Stream source, int initialSize, int maxSize, bool minimalRead)
	{
		StreamWrapper value;
		lock (_lockObjects)
		{
			if (!_lockObjects.TryGetValue(source, out value))
			{
				_lockObjects.Add(source, new StreamWrapper
				{
					Source = source
				});
				value = _lockObjects[source];
				if (source.CanSeek)
				{
					value.EofOffset = source.Length;
				}
			}
			else
			{
				value.RefCount++;
			}
		}
		initialSize = 2 << (int)Math.Log(initialSize - 1, 2.0);
		maxSize = 1 << (int)Math.Log(maxSize, 2.0);
		_wrapper = value;
		_data = new byte[initialSize];
		_maxSize = maxSize;
		_minimalRead = minimalRead;
		_savedBuffers = new List<SavedBuffer>();
	}

	public void Dispose()
	{
		lock (_lockObjects)
		{
			if (--_wrapper.RefCount == 0)
			{
				_lockObjects.Remove(_wrapper.Source);
			}
		}
	}

	public int Read(long offset, byte[] buffer, int index, int count)
	{
		if (offset < 0)
		{
			throw new ArgumentOutOfRangeException("offset");
		}
		if (buffer == null)
		{
			throw new ArgumentNullException("buffer");
		}
		if (index < 0 || index + count > buffer.Length)
		{
			throw new ArgumentOutOfRangeException("index");
		}
		if (count < 0)
		{
			throw new ArgumentOutOfRangeException("count");
		}
		if (offset >= _wrapper.EofOffset)
		{
			return 0;
		}
		int srcOffset = EnsureAvailable(offset, ref count, isRecursion: false);
		Buffer.BlockCopy(_data, srcOffset, buffer, index, count);
		return count;
	}

	internal int ReadByte(long offset)
	{
		if (offset < 0)
		{
			throw new ArgumentOutOfRangeException("offset");
		}
		if (offset >= _wrapper.EofOffset)
		{
			return -1;
		}
		int count = 1;
		int num = EnsureAvailable(offset, ref count, isRecursion: false);
		if (count == 1)
		{
			return _data[num];
		}
		return -1;
	}

	private int EnsureAvailable(long offset, ref int count, bool isRecursion)
	{
		if (offset >= _baseOffset && offset + count < _baseOffset + _end)
		{
			return (int)(offset - _baseOffset);
		}
		if (count > _maxSize)
		{
			throw new InvalidOperationException("Not enough room in the buffer!  Increase the maximum size and try again.");
		}
		_versionCounter++;
		if (!isRecursion)
		{
			for (int i = 0; i < _savedBuffers.Count; i++)
			{
				long num = _savedBuffers[i].BaseOffset - offset;
				if ((num < 0 && _savedBuffers[i].End + num > 0) || (num > 0 && count - num > 0))
				{
					SwapBuffers(_savedBuffers[i]);
					return EnsureAvailable(offset, ref count, isRecursion: true);
				}
			}
		}
		while (_savedBuffers.Count > 0 && _savedBuffers[0].VersionSaved + 25 < _versionCounter)
		{
			_savedBuffers[0].Buffer = null;
			_savedBuffers.RemoveAt(0);
		}
		if (offset < _baseOffset && !_wrapper.Source.CanSeek)
		{
			throw new InvalidOperationException("Cannot seek before buffer on forward-only streams!");
		}
		CalcBuffer(offset, count, out var readStart, out var readEnd);
		count = FillBuffer(offset, count, readStart, readEnd);
		return (int)(offset - _baseOffset);
	}

	private void SaveBuffer()
	{
		_savedBuffers.Add(new SavedBuffer
		{
			Buffer = _data,
			BaseOffset = _baseOffset,
			End = _end,
			DiscardCount = _discardCount,
			VersionSaved = _versionCounter
		});
		_data = null;
		_end = 0;
		_discardCount = 0;
	}

	private void CreateNewBuffer(long offset, int count)
	{
		SaveBuffer();
		_data = new byte[Math.Min(2 << (int)Math.Log(count - 1, 2.0), _maxSize)];
		_baseOffset = offset;
	}

	private void SwapBuffers(SavedBuffer savedBuffer)
	{
		_savedBuffers.Remove(savedBuffer);
		SaveBuffer();
		_data = savedBuffer.Buffer;
		_baseOffset = savedBuffer.BaseOffset;
		_end = savedBuffer.End;
		_discardCount = savedBuffer.DiscardCount;
	}

	private void CalcBuffer(long offset, int count, out int readStart, out int readEnd)
	{
		readStart = 0;
		readEnd = 0;
		if (offset < _baseOffset)
		{
			if (offset + _maxSize <= _baseOffset)
			{
				if (_baseOffset - (offset + _maxSize) > _maxSize)
				{
					CreateNewBuffer(offset, count);
				}
				else
				{
					EnsureBufferSize(count, copyContents: false, 0);
				}
				_baseOffset = offset;
				readEnd = count;
			}
			else
			{
				readEnd = (int)(offset - _baseOffset);
				EnsureBufferSize(Math.Min((int)(offset + _maxSize - _baseOffset), _end) - readEnd, copyContents: true, readEnd);
				readEnd = (int)(offset - _baseOffset) - readEnd;
			}
		}
		else if (offset >= _baseOffset + _maxSize)
		{
			if (offset - (_baseOffset + _maxSize) > _maxSize)
			{
				CreateNewBuffer(offset, count);
			}
			else
			{
				EnsureBufferSize(count, copyContents: false, 0);
			}
			_baseOffset = offset;
			readEnd = count;
		}
		else
		{
			readEnd = (int)(offset + count - _baseOffset);
			int num = Math.Max(readEnd - _maxSize, 0);
			EnsureBufferSize(readEnd - num, copyContents: true, num);
			readStart = _end;
			readEnd = (int)(offset + count - _baseOffset);
		}
	}

	private void EnsureBufferSize(int reqSize, bool copyContents, int copyOffset)
	{
		byte[] array = _data;
		if (reqSize > _data.Length)
		{
			if (reqSize > _maxSize)
			{
				if (!_wrapper.Source.CanSeek && reqSize - _discardCount > _maxSize)
				{
					throw new InvalidOperationException("Not enough room in the buffer!  Increase the maximum size and try again.");
				}
				int num = reqSize - _maxSize;
				copyOffset += num;
				reqSize = _maxSize;
			}
			else
			{
				int num2;
				for (num2 = _data.Length; num2 < reqSize; num2 *= 2)
				{
				}
				reqSize = num2;
			}
			if (reqSize > _data.Length)
			{
				array = new byte[reqSize];
			}
		}
		if (copyContents)
		{
			if ((copyOffset > 0 && copyOffset < _end) || (copyOffset == 0 && array != _data))
			{
				Buffer.BlockCopy(_data, copyOffset, array, 0, _end - copyOffset);
				if ((_discardCount -= copyOffset) < 0)
				{
					_discardCount = 0;
				}
			}
			else if (copyOffset < 0 && -copyOffset < _end)
			{
				if (array != _data || _end <= -copyOffset)
				{
					Buffer.BlockCopy(_data, 0, array, -copyOffset, Math.Max(_end, Math.Min(_end, _data.Length + copyOffset)));
				}
				else
				{
					_end = copyOffset;
				}
				_discardCount = 0;
			}
			else
			{
				_end = copyOffset;
				_discardCount = 0;
			}
			_baseOffset += copyOffset;
			_end -= copyOffset;
			if (_end > array.Length)
			{
				_end = array.Length;
			}
		}
		else
		{
			_discardCount = 0;
			_end = 0;
		}
		_data = array;
	}

	private int FillBuffer(long offset, int count, int readStart, int readEnd)
	{
		long readOffset = _baseOffset + readStart;
		int readCount = readEnd - readStart;
		lock (_wrapper.LockObject)
		{
			readCount = PrepareStreamForRead(readCount, readOffset);
			ReadStream(readStart, readCount, readOffset);
			if (_end < readStart + readCount)
			{
				count = Math.Max(0, (int)(_baseOffset + _end - offset));
			}
			else if (!_minimalRead && _end < _data.Length)
			{
				readCount = _data.Length - _end;
				readCount = PrepareStreamForRead(readCount, _baseOffset + _end);
				_end += _wrapper.Source.Read(_data, _end, readCount);
			}
		}
		return count;
	}

	private int PrepareStreamForRead(int readCount, long readOffset)
	{
		if (readCount > 0 && _wrapper.Source.Position != readOffset)
		{
			if (readOffset < _wrapper.EofOffset)
			{
				if (_wrapper.Source.CanSeek)
				{
					_wrapper.Source.Position = readOffset;
				}
				else
				{
					long num = readOffset - _wrapper.Source.Position;
					if (num < 0)
					{
						readCount = 0;
					}
					else
					{
						while (--num >= 0)
						{
							if (_wrapper.Source.ReadByte() == -1)
							{
								_wrapper.EofOffset = _wrapper.Source.Position;
								readCount = 0;
								break;
							}
						}
					}
				}
			}
			else
			{
				readCount = 0;
			}
		}
		return readCount;
	}

	private void ReadStream(int readStart, int readCount, long readOffset)
	{
		while (readCount > 0 && readOffset < _wrapper.EofOffset)
		{
			int num = _wrapper.Source.Read(_data, readStart, readCount);
			if (num == 0)
			{
				break;
			}
			readStart += num;
			readOffset += num;
			readCount -= num;
		}
		if (readStart > _end)
		{
			_end = readStart;
		}
	}

	public void DiscardThrough(long offset)
	{
		int val = (int)(offset - _baseOffset);
		_discardCount = Math.Max(val, _discardCount);
		if (_discardCount >= _data.Length)
		{
			CommitDiscard();
		}
	}

	private void CommitDiscard()
	{
		if (_discardCount >= _data.Length || _discardCount >= _end)
		{
			_baseOffset += _discardCount;
			_end = 0;
		}
		else
		{
			Buffer.BlockCopy(_data, _discardCount, _data, 0, _end - _discardCount);
			_baseOffset += _discardCount;
			_end -= _discardCount;
		}
		_discardCount = 0;
	}
}