Decompiled source of BigDoom v1.0.0

BigWalkDoom.dll

Decompiled a week ago
#define DEBUG
using System;
using System.Buffers;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using HarmonyLib;
using Il2CppInterop.Runtime;
using Il2CppInterop.Runtime.Injection;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Il2CppSystem;
using ManagedDoom;
using ManagedDoom.Audio;
using ManagedDoom.Unity;
using ManagedDoom.UserInput;
using ManagedDoom.Video;
using MeltySynth;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")]
[assembly: AssemblyCompany("BigWalkDoom")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("BigWalkDoom")]
[assembly: AssemblyTitle("BigWalkDoom")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace MeltySynth
{
	internal static class ArrayMath
	{
		public static void MultiplyAdd(float a, float[] x, float[] destination)
		{
			for (int i = 0; i < destination.Length; i++)
			{
				destination[i] += a * x[i];
			}
		}

		public static void MultiplyAdd(float a, float step, float[] x, float[] destination)
		{
			for (int i = 0; i < destination.Length; i++)
			{
				destination[i] += a * x[i];
				a += step;
			}
		}
	}
	public static class AudioRendererEx
	{
		public static void RenderInterleaved(this IAudioRenderer renderer, Span<float> destination)
		{
			if (renderer == null)
			{
				throw new ArgumentNullException("renderer");
			}
			if (destination.Length % 2 != 0)
			{
				throw new ArgumentException("The length of the destination buffer must be even.", "destination");
			}
			int num = destination.Length / 2;
			int length = destination.Length;
			float[] array = ArrayPool<float>.Shared.Rent(length);
			try
			{
				Span<float> left = new Span<float>(array, 0, num);
				Span<float> right = new Span<float>(array, num, num);
				renderer.Render(left, right);
				int num2 = 0;
				for (int i = 0; i < num; i++)
				{
					destination[num2++] = left[i];
					destination[num2++] = right[i];
				}
			}
			finally
			{
				ArrayPool<float>.Shared.Return(array);
			}
		}

		public static void RenderMono(this IAudioRenderer renderer, Span<float> destination)
		{
			if (renderer == null)
			{
				throw new ArgumentNullException("renderer");
			}
			int length = destination.Length;
			int minimumLength = 2 * destination.Length;
			float[] array = ArrayPool<float>.Shared.Rent(minimumLength);
			try
			{
				Span<float> left = new Span<float>(array, 0, length);
				Span<float> right = new Span<float>(array, length, length);
				renderer.Render(left, right);
				for (int i = 0; i < length; i++)
				{
					destination[i] = (left[i] + right[i]) / 2f;
				}
			}
			finally
			{
				ArrayPool<float>.Shared.Return(array);
			}
		}

		public static void RenderInt16(this IAudioRenderer renderer, Span<short> left, Span<short> right)
		{
			if (renderer == null)
			{
				throw new ArgumentNullException("renderer");
			}
			if (left.Length != right.Length)
			{
				throw new ArgumentException("The output buffers for the left and right must be the same length.");
			}
			int length = left.Length;
			int minimumLength = 2 * left.Length;
			float[] array = ArrayPool<float>.Shared.Rent(minimumLength);
			try
			{
				Span<float> left2 = new Span<float>(array, 0, length);
				Span<float> right2 = new Span<float>(array, length, length);
				renderer.Render(left2, right2);
				for (int i = 0; i < length; i++)
				{
					float num = 32768f * left2[i];
					if (num < -32768f)
					{
						num = -32768f;
					}
					else if (num > 32767f)
					{
						num = 32767f;
					}
					left[i] = (short)num;
				}
				for (int j = 0; j < length; j++)
				{
					float num2 = 32768f * right2[j];
					if (num2 < -32768f)
					{
						num2 = -32768f;
					}
					else if (num2 > 32767f)
					{
						num2 = 32767f;
					}
					right[j] = (short)num2;
				}
			}
			finally
			{
				ArrayPool<float>.Shared.Return(array);
			}
		}

		public static void RenderInterleavedInt16(this IAudioRenderer renderer, Span<short> destination)
		{
			if (renderer == null)
			{
				throw new ArgumentNullException("renderer");
			}
			if (destination.Length % 2 != 0)
			{
				throw new ArgumentException("The length of the destination buffer must be even.", "destination");
			}
			int num = destination.Length / 2;
			int length = destination.Length;
			float[] array = ArrayPool<float>.Shared.Rent(length);
			try
			{
				Span<float> left = new Span<float>(array, 0, num);
				Span<float> right = new Span<float>(array, num, num);
				renderer.Render(left, right);
				int num2 = 0;
				for (int i = 0; i < num; i++)
				{
					int num3 = (int)(32768f * left[i]);
					if (num3 < -32768)
					{
						num3 = -32768;
					}
					else if (num3 > 32767)
					{
						num3 = 32767;
					}
					int num4 = (int)(32768f * right[i]);
					if (num4 < -32768)
					{
						num4 = -32768;
					}
					else if (num4 > 32767)
					{
						num4 = 32767;
					}
					destination[num2++] = (short)num3;
					destination[num2++] = (short)num4;
				}
			}
			finally
			{
				ArrayPool<float>.Shared.Return(array);
			}
		}

		public static void RenderMonoInt16(this IAudioRenderer renderer, Span<short> destination)
		{
			if (renderer == null)
			{
				throw new ArgumentNullException("renderer");
			}
			int length = destination.Length;
			int minimumLength = 2 * destination.Length;
			float[] array = ArrayPool<float>.Shared.Rent(minimumLength);
			try
			{
				Span<float> left = new Span<float>(array, 0, length);
				Span<float> right = new Span<float>(array, length, length);
				renderer.Render(left, right);
				for (int i = 0; i < length; i++)
				{
					int num = (int)(16384f * (left[i] + right[i]));
					if (num < -32768)
					{
						num = -32768;
					}
					else if (num > 32767)
					{
						num = 32767;
					}
					destination[i] = (short)num;
				}
			}
			finally
			{
				ArrayPool<float>.Shared.Return(array);
			}
		}
	}
	internal static class BinaryReaderEx
	{
		public static string ReadFourCC(this BinaryReader reader)
		{
			byte[] array = reader.ReadBytes(4);
			for (int i = 0; i < array.Length; i++)
			{
				byte b = array[i];
				if (32 > b || b > 126)
				{
					array[i] = 63;
				}
			}
			return Encoding.ASCII.GetString(array, 0, array.Length);
		}

		public static string ReadFixedLengthString(this BinaryReader reader, int length)
		{
			byte[] array = reader.ReadBytes(length);
			int i;
			for (i = 0; i < array.Length && array[i] != 0; i++)
			{
			}
			return Encoding.ASCII.GetString(array, 0, i);
		}

		public static short ReadInt16BigEndian(this BinaryReader reader)
		{
			short num = reader.ReadInt16();
			int num2 = 0xFF & num;
			int num3 = 0xFF & (num >> 8);
			return (short)((num2 << 8) | num3);
		}

		public static int ReadInt32BigEndian(this BinaryReader reader)
		{
			int num = reader.ReadInt32();
			int num2 = 0xFF & num;
			int num3 = 0xFF & (num >> 8);
			int num4 = 0xFF & (num >> 16);
			int num5 = 0xFF & (num >> 24);
			return (num2 << 24) | (num3 << 16) | (num4 << 8) | num5;
		}

		public static int ReadIntVariableLength(this BinaryReader reader)
		{
			int num = 0;
			int num2 = 0;
			while (true)
			{
				byte b = reader.ReadByte();
				num = (num << 7) | (b & 0x7F);
				if ((b & 0x80) == 0)
				{
					break;
				}
				num2++;
				if (num2 == 4)
				{
					throw new InvalidDataException("The length of the value must be equal to or less than 4.");
				}
			}
			return num;
		}
	}
	internal sealed class BiQuadFilter
	{
		private static readonly float resonancePeakOffset = 1f - 1f / Mathf.Sqrt(2f);

		private readonly Synthesizer synthesizer;

		private bool active;

		private float a0;

		private float a1;

		private float a2;

		private float a3;

		private float a4;

		private float x1;

		private float x2;

		private float y1;

		private float y2;

		internal BiQuadFilter(Synthesizer synthesizer)
		{
			this.synthesizer = synthesizer;
		}

		public void ClearBuffer()
		{
			x1 = 0f;
			x2 = 0f;
			y1 = 0f;
			y2 = 0f;
		}

		public void SetLowPassFilter(float cutoffFrequency, float resonance)
		{
			if (cutoffFrequency < 0.499f * (float)synthesizer.SampleRate)
			{
				active = true;
				float num = resonance - resonancePeakOffset / (1f + 6f * (resonance - 1f));
				float num2 = (float)Math.PI * 2f * cutoffFrequency / (float)synthesizer.SampleRate;
				float num3 = Mathf.Cos(num2);
				float num4 = Mathf.Sin(num2) / (2f * num);
				float b = (1f - num3) / 2f;
				float b2 = 1f - num3;
				float b3 = (1f - num3) / 2f;
				float num5 = 1f + num4;
				float num6 = -2f * num3;
				float num7 = 1f - num4;
				SetCoefficients(num5, num6, num7, b, b2, b3);
			}
			else
			{
				active = false;
			}
		}

		public void Process(float[] block)
		{
			if (active)
			{
				for (int i = 0; i < block.Length; i++)
				{
					float num = block[i];
					float num2 = a0 * num + a1 * x1 + a2 * x2 - a3 * y1 - a4 * y2;
					x2 = x1;
					x1 = num;
					y2 = y1;
					y1 = num2;
					block[i] = num2;
				}
			}
			else
			{
				x2 = block[^2];
				x1 = block[^1];
				y2 = x2;
				y1 = x1;
			}
		}

		private void SetCoefficients(float a0, float a1, float a2, float b0, float b1, float b2)
		{
			this.a0 = b0 / a0;
			this.a1 = b1 / a0;
			this.a2 = b2 / a0;
			a3 = a1 / a0;
			a4 = a2 / a0;
		}
	}
	internal sealed class Channel
	{
		private readonly Synthesizer synthesizer;

		private readonly bool isPercussionChannel;

		private int bankNumber;

		private int patchNumber;

		private short modulation;

		private short volume;

		private short pan;

		private short expression;

		private bool holdPedal;

		private byte reverbSend;

		private byte chorusSend;

		private short rpn;

		private short pitchBendRange;

		private short coarseTune;

		private short fineTune;

		private float pitchBend;

		public bool IsPercussionChannel => isPercussionChannel;

		public int BankNumber => bankNumber;

		public int PatchNumber => patchNumber;

		public float Modulation => 0.003051944f * (float)modulation;

		public float Volume => 6.103888E-05f * (float)volume;

		public float Pan => 0.006103888f * (float)pan - 50f;

		public float Expression => 6.103888E-05f * (float)expression;

		public bool HoldPedal => holdPedal;

		public float ReverbSend => 1f / 127f * (float)(int)reverbSend;

		public float ChorusSend => 1f / 127f * (float)(int)chorusSend;

		public float PitchBendRange => (float)(pitchBendRange >> 7) + 0.01f * (float)(pitchBendRange & 0x7F);

		public float Tune => (float)coarseTune + 0.00012207031f * (float)(fineTune - 8192);

		public float PitchBend => PitchBendRange * pitchBend;

		internal Channel(Synthesizer synthesizer, bool isPercussionChannel)
		{
			this.synthesizer = synthesizer;
			this.isPercussionChannel = isPercussionChannel;
			Reset();
		}

		public void Reset()
		{
			bankNumber = (isPercussionChannel ? 128 : 0);
			patchNumber = 0;
			modulation = 0;
			volume = 12800;
			pan = 8192;
			expression = 16256;
			holdPedal = false;
			reverbSend = 40;
			chorusSend = 0;
			rpn = -1;
			pitchBendRange = 256;
			coarseTune = 0;
			fineTune = 8192;
			pitchBend = 0f;
		}

		public void ResetAllControllers()
		{
			modulation = 0;
			expression = 16256;
			holdPedal = false;
			rpn = -1;
			pitchBend = 0f;
		}

		public void SetBank(int value)
		{
			bankNumber = value;
			if (isPercussionChannel)
			{
				bankNumber += 128;
			}
		}

		public void SetPatch(int value)
		{
			patchNumber = value;
		}

		public void SetModulationCoarse(int value)
		{
			modulation = (short)((modulation & 0x7F) | (value << 7));
		}

		public void SetModulationFine(int value)
		{
			modulation = (short)((modulation & 0xFF80) | value);
		}

		public void SetVolumeCoarse(int value)
		{
			volume = (short)((volume & 0x7F) | (value << 7));
		}

		public void SetVolumeFine(int value)
		{
			volume = (short)((volume & 0xFF80) | value);
		}

		public void SetPanCoarse(int value)
		{
			pan = (short)((pan & 0x7F) | (value << 7));
		}

		public void SetPanFine(int value)
		{
			pan = (short)((pan & 0xFF80) | value);
		}

		public void SetExpressionCoarse(int value)
		{
			expression = (short)((expression & 0x7F) | (value << 7));
		}

		public void SetExpressionFine(int value)
		{
			expression = (short)((expression & 0xFF80) | value);
		}

		public void SetHoldPedal(int value)
		{
			holdPedal = value >= 64;
		}

		public void SetReverbSend(int value)
		{
			reverbSend = (byte)value;
		}

		public void SetChorusSend(int value)
		{
			chorusSend = (byte)value;
		}

		public void SetRpnCoarse(int value)
		{
			rpn = (short)((rpn & 0x7F) | (value << 7));
		}

		public void SetRpnFine(int value)
		{
			rpn = (short)((rpn & 0xFF80) | value);
		}

		public void DataEntryCoarse(int value)
		{
			switch (rpn)
			{
			case 0:
				pitchBendRange = (short)((pitchBendRange & 0x7F) | (value << 7));
				break;
			case 1:
				fineTune = (short)((fineTune & 0x7F) | (value << 7));
				break;
			case 2:
				coarseTune = (short)(value - 64);
				break;
			}
		}

		public void DataEntryFine(int value)
		{
			switch (rpn)
			{
			case 0:
				pitchBendRange = (short)((pitchBendRange & 0xFF80) | value);
				break;
			case 1:
				fineTune = (short)((fineTune & 0xFF80) | value);
				break;
			}
		}

		public void SetPitchBend(int value1, int value2)
		{
			pitchBend = 0.00012207031f * (float)((value1 | (value2 << 7)) - 8192);
		}
	}
	internal sealed class Chorus
	{
		private readonly float[] bufferL;

		private readonly float[] bufferR;

		private readonly float[] delayTable;

		private int bufferIndexL;

		private int bufferIndexR;

		private int delayTableIndexL;

		private int delayTableIndexR;

		internal Chorus(int sampleRate, double delay, double depth, double frequency)
		{
			bufferL = new float[(int)((double)sampleRate * (delay + depth)) + 2];
			bufferR = new float[(int)((double)sampleRate * (delay + depth)) + 2];
			delayTable = new float[(int)Math.Round((double)sampleRate / frequency)];
			for (int i = 0; i < delayTable.Length; i++)
			{
				double a = Math.PI * 2.0 * (double)i / (double)delayTable.Length;
				delayTable[i] = (float)((double)sampleRate * (delay + depth * Math.Sin(a)));
			}
			bufferIndexL = 0;
			bufferIndexR = 0;
			delayTableIndexL = 0;
			delayTableIndexR = delayTable.Length / 4;
		}

		public void Process(float[] inputLeft, float[] inputRight, float[] outputLeft, float[] outputRight)
		{
			for (int i = 0; i < outputLeft.Length; i++)
			{
				double num = (double)bufferIndexL - (double)delayTable[delayTableIndexL];
				if (num < 0.0)
				{
					num += (double)bufferL.Length;
				}
				int num2 = (int)num;
				int num3 = num2 + 1;
				if (num3 == bufferL.Length)
				{
					num3 = 0;
				}
				double num4 = bufferL[num2];
				double num5 = bufferL[num3];
				double num6 = num - (double)num2;
				outputLeft[i] = (float)(num4 + num6 * (num5 - num4));
				bufferL[bufferIndexL] = inputLeft[i];
				bufferIndexL++;
				if (bufferIndexL == bufferL.Length)
				{
					bufferIndexL = 0;
				}
				delayTableIndexL++;
				if (delayTableIndexL == delayTable.Length)
				{
					delayTableIndexL = 0;
				}
			}
			for (int j = 0; j < outputRight.Length; j++)
			{
				double num7 = (double)bufferIndexR - (double)delayTable[delayTableIndexR];
				if (num7 < 0.0)
				{
					num7 += (double)bufferR.Length;
				}
				int num8 = (int)num7;
				int num9 = num8 + 1;
				if (num9 == bufferR.Length)
				{
					num9 = 0;
				}
				double num10 = bufferR[num8];
				double num11 = bufferR[num9];
				double num12 = num7 - (double)num8;
				outputRight[j] = (float)(num10 + num12 * (num11 - num10));
				bufferR[bufferIndexR] = inputRight[j];
				bufferIndexR++;
				if (bufferIndexR == bufferR.Length)
				{
					bufferIndexR = 0;
				}
				delayTableIndexR++;
				if (delayTableIndexR == delayTable.Length)
				{
					delayTableIndexR = 0;
				}
			}
		}

		public void Mute()
		{
			Array.Clear(bufferL, 0, bufferL.Length);
			Array.Clear(bufferR, 0, bufferR.Length);
		}
	}
	internal struct Generator
	{
		private readonly GeneratorType type;

		private readonly ushort value;

		public GeneratorType Type => type;

		public ushort Value => value;

		private Generator(BinaryReader reader)
		{
			type = (GeneratorType)reader.ReadUInt16();
			value = reader.ReadUInt16();
		}

		internal static Generator[] ReadFromChunk(BinaryReader reader, int size)
		{
			if (size % 4 != 0)
			{
				throw new InvalidDataException("The generator list is invalid.");
			}
			Generator[] array = new Generator[size / 4 - 1];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new Generator(reader);
			}
			new Generator(reader);
			return array;
		}
	}
	internal enum GeneratorType : ushort
	{
		StartAddressOffset,
		EndAddressOffset,
		StartLoopAddressOffset,
		EndLoopAddressOffset,
		StartAddressCoarseOffset,
		ModulationLfoToPitch,
		VibratoLfoToPitch,
		ModulationEnvelopeToPitch,
		InitialFilterCutoffFrequency,
		InitialFilterQ,
		ModulationLfoToFilterCutoffFrequency,
		ModulationEnvelopeToFilterCutoffFrequency,
		EndAddressCoarseOffset,
		ModulationLfoToVolume,
		Unused1,
		ChorusEffectsSend,
		ReverbEffectsSend,
		Pan,
		Unused2,
		Unused3,
		Unused4,
		DelayModulationLfo,
		FrequencyModulationLfo,
		DelayVibratoLfo,
		FrequencyVibratoLfo,
		DelayModulationEnvelope,
		AttackModulationEnvelope,
		HoldModulationEnvelope,
		DecayModulationEnvelope,
		SustainModulationEnvelope,
		ReleaseModulationEnvelope,
		KeyNumberToModulationEnvelopeHold,
		KeyNumberToModulationEnvelopeDecay,
		DelayVolumeEnvelope,
		AttackVolumeEnvelope,
		HoldVolumeEnvelope,
		DecayVolumeEnvelope,
		SustainVolumeEnvelope,
		ReleaseVolumeEnvelope,
		KeyNumberToVolumeEnvelopeHold,
		KeyNumberToVolumeEnvelopeDecay,
		Instrument,
		Reserved1,
		KeyRange,
		VelocityRange,
		StartLoopAddressCoarseOffset,
		KeyNumber,
		Velocity,
		InitialAttenuation,
		Reserved2,
		EndLoopAddressCoarseOffset,
		CoarseTune,
		FineTune,
		SampleID,
		SampleModes,
		Reserved3,
		ScaleTuning,
		ExclusiveClass,
		OverridingRootKey,
		Unused5,
		UnusedEnd
	}
	public interface IAudioRenderer
	{
		void Render(Span<float> left, Span<float> right);
	}
	public sealed class Instrument
	{
		internal static readonly Instrument Default = new Instrument();

		private readonly string name;

		private readonly InstrumentRegion[] regions;

		public string Name => name;

		public IReadOnlyList<InstrumentRegion> Regions => regions;

		internal InstrumentRegion[] RegionArray => regions;

		private Instrument()
		{
			name = "Default";
			regions = Array.Empty<InstrumentRegion>();
		}

		private Instrument(InstrumentInfo info, Zone[] zones, SampleHeader[] samples)
		{
			name = info.Name;
			int num = info.ZoneEndIndex - info.ZoneStartIndex + 1;
			if (num <= 0)
			{
				throw new InvalidDataException("The instrument '" + info.Name + "' has no zone.");
			}
			Span<Zone> zones2 = new Span<Zone>(zones, info.ZoneStartIndex, num);
			regions = InstrumentRegion.Create(this, zones2, samples);
		}

		internal static Instrument[] Create(InstrumentInfo[] infos, Zone[] zones, SampleHeader[] samples)
		{
			if (infos.Length <= 1)
			{
				throw new InvalidDataException("No valid instrument was found.");
			}
			Instrument[] array = new Instrument[infos.Length - 1];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new Instrument(infos[i], zones, samples);
			}
			return array;
		}

		public override string ToString()
		{
			return name;
		}
	}
	internal sealed class InstrumentInfo
	{
		private string name;

		private int zoneStartIndex;

		private int zoneEndIndex;

		public string Name => name;

		public int ZoneStartIndex => zoneStartIndex;

		public int ZoneEndIndex => zoneEndIndex;

		private InstrumentInfo(BinaryReader reader)
		{
			name = reader.ReadFixedLengthString(20);
			zoneStartIndex = reader.ReadUInt16();
		}

		internal static InstrumentInfo[] ReadFromChunk(BinaryReader reader, int size)
		{
			if (size % 22 != 0)
			{
				throw new InvalidDataException("The instrument list is invalid.");
			}
			int num = size / 22;
			InstrumentInfo[] array = new InstrumentInfo[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = new InstrumentInfo(reader);
			}
			for (int j = 0; j < num - 1; j++)
			{
				array[j].zoneEndIndex = array[j + 1].zoneStartIndex - 1;
			}
			return array;
		}
	}
	public sealed class InstrumentRegion
	{
		internal static readonly InstrumentRegion Default = new InstrumentRegion();

		private readonly short[] gs;

		private readonly SampleHeader sample;

		internal short this[GeneratorType generatortType] => gs[(uint)generatortType];

		public SampleHeader Sample => sample;

		public int SampleStart => sample.Start + StartAddressOffset;

		public int SampleEnd => sample.End + EndAddressOffset;

		public int SampleStartLoop => sample.StartLoop + StartLoopAddressOffset;

		public int SampleEndLoop => sample.EndLoop + EndLoopAddressOffset;

		public int StartAddressOffset => 32768 * this[GeneratorType.StartAddressCoarseOffset] + this[GeneratorType.StartAddressOffset];

		public int EndAddressOffset => 32768 * this[GeneratorType.EndAddressCoarseOffset] + this[GeneratorType.EndAddressOffset];

		public int StartLoopAddressOffset => 32768 * this[GeneratorType.StartLoopAddressCoarseOffset] + this[GeneratorType.StartLoopAddressOffset];

		public int EndLoopAddressOffset => 32768 * this[GeneratorType.EndLoopAddressCoarseOffset] + this[GeneratorType.EndLoopAddressOffset];

		public int ModulationLfoToPitch => this[GeneratorType.ModulationLfoToPitch];

		public int VibratoLfoToPitch => this[GeneratorType.VibratoLfoToPitch];

		public int ModulationEnvelopeToPitch => this[GeneratorType.ModulationEnvelopeToPitch];

		public float InitialFilterCutoffFrequency => SoundFontMath.CentsToHertz(this[GeneratorType.InitialFilterCutoffFrequency]);

		public float InitialFilterQ => 0.1f * (float)this[GeneratorType.InitialFilterQ];

		public int ModulationLfoToFilterCutoffFrequency => this[GeneratorType.ModulationLfoToFilterCutoffFrequency];

		public int ModulationEnvelopeToFilterCutoffFrequency => this[GeneratorType.ModulationEnvelopeToFilterCutoffFrequency];

		public float ModulationLfoToVolume => 0.1f * (float)this[GeneratorType.ModulationLfoToVolume];

		public float ChorusEffectsSend => 0.1f * (float)this[GeneratorType.ChorusEffectsSend];

		public float ReverbEffectsSend => 0.1f * (float)this[GeneratorType.ReverbEffectsSend];

		public float Pan => 0.1f * (float)this[GeneratorType.Pan];

		public float DelayModulationLfo => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DelayModulationLfo]);

		public float FrequencyModulationLfo => SoundFontMath.CentsToHertz(this[GeneratorType.FrequencyModulationLfo]);

		public float DelayVibratoLfo => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DelayVibratoLfo]);

		public float FrequencyVibratoLfo => SoundFontMath.CentsToHertz(this[GeneratorType.FrequencyVibratoLfo]);

		public float DelayModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DelayModulationEnvelope]);

		public float AttackModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.AttackModulationEnvelope]);

		public float HoldModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.HoldModulationEnvelope]);

		public float DecayModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DecayModulationEnvelope]);

		public float SustainModulationEnvelope => 0.1f * (float)this[GeneratorType.SustainModulationEnvelope];

		public float ReleaseModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.ReleaseModulationEnvelope]);

		public int KeyNumberToModulationEnvelopeHold => this[GeneratorType.KeyNumberToModulationEnvelopeHold];

		public int KeyNumberToModulationEnvelopeDecay => this[GeneratorType.KeyNumberToModulationEnvelopeDecay];

		public float DelayVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DelayVolumeEnvelope]);

		public float AttackVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.AttackVolumeEnvelope]);

		public float HoldVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.HoldVolumeEnvelope]);

		public float DecayVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DecayVolumeEnvelope]);

		public float SustainVolumeEnvelope => 0.1f * (float)this[GeneratorType.SustainVolumeEnvelope];

		public float ReleaseVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.ReleaseVolumeEnvelope]);

		public int KeyNumberToVolumeEnvelopeHold => this[GeneratorType.KeyNumberToVolumeEnvelopeHold];

		public int KeyNumberToVolumeEnvelopeDecay => this[GeneratorType.KeyNumberToVolumeEnvelopeDecay];

		public int KeyRangeStart => this[GeneratorType.KeyRange] & 0xFF;

		public int KeyRangeEnd => (this[GeneratorType.KeyRange] >> 8) & 0xFF;

		public int VelocityRangeStart => this[GeneratorType.VelocityRange] & 0xFF;

		public int VelocityRangeEnd => (this[GeneratorType.VelocityRange] >> 8) & 0xFF;

		public float InitialAttenuation => 0.1f * (float)this[GeneratorType.InitialAttenuation];

		public int CoarseTune => this[GeneratorType.CoarseTune];

		public int FineTune => this[GeneratorType.FineTune] + sample.PitchCorrection;

		public LoopMode SampleModes => (LoopMode)((this[GeneratorType.SampleModes] != 2) ? this[GeneratorType.SampleModes] : 0);

		public int ScaleTuning => this[GeneratorType.ScaleTuning];

		public int ExclusiveClass => this[GeneratorType.ExclusiveClass];

		public int RootKey => (this[GeneratorType.OverridingRootKey] != -1) ? this[GeneratorType.OverridingRootKey] : sample.OriginalPitch;

		private InstrumentRegion()
		{
			gs = new short[61];
			gs[8] = 13500;
			gs[21] = -12000;
			gs[23] = -12000;
			gs[25] = -12000;
			gs[26] = -12000;
			gs[27] = -12000;
			gs[28] = -12000;
			gs[30] = -12000;
			gs[33] = -12000;
			gs[34] = -12000;
			gs[35] = -12000;
			gs[36] = -12000;
			gs[38] = -12000;
			gs[43] = 32512;
			gs[44] = 32512;
			gs[46] = -1;
			gs[47] = -1;
			gs[56] = 100;
			gs[58] = -1;
			sample = SampleHeader.Default;
		}

		private InstrumentRegion(Instrument instrument, Zone global, Zone local, SampleHeader[] samples)
			: this()
		{
			foreach (Generator generator in global.Generators)
			{
				SetParameter(generator);
			}
			foreach (Generator generator2 in local.Generators)
			{
				SetParameter(generator2);
			}
			short num = gs[53];
			if (0 > num || num >= samples.Length)
			{
				throw new InvalidDataException($"The instrument '{instrument.Name}' contains an invalid sample ID '{num}'.");
			}
			sample = samples[num];
		}

		internal static InstrumentRegion[] Create(Instrument instrument, Span<Zone> zones, SampleHeader[] samples)
		{
			if (zones[0].Generators.Count == 0 || zones[0].Generators.Last().Type != GeneratorType.SampleID)
			{
				Zone global = zones[0];
				InstrumentRegion[] array = new InstrumentRegion[zones.Length - 1];
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = new InstrumentRegion(instrument, global, zones[i + 1], samples);
				}
				return array;
			}
			InstrumentRegion[] array2 = new InstrumentRegion[zones.Length];
			for (int j = 0; j < array2.Length; j++)
			{
				array2[j] = new InstrumentRegion(instrument, Zone.Empty, zones[j], samples);
			}
			return array2;
		}

		private void SetParameter(Generator generator)
		{
			int type = (int)generator.Type;
			if (0 <= type && type < gs.Length)
			{
				gs[type] = (short)generator.Value;
			}
		}

		public bool Contains(int key, int velocity)
		{
			bool flag = KeyRangeStart <= key && key <= KeyRangeEnd;
			bool flag2 = VelocityRangeStart <= velocity && velocity <= VelocityRangeEnd;
			return flag && flag2;
		}

		public override string ToString()
		{
			return $"{sample.Name} (Key: {KeyRangeStart}-{KeyRangeEnd}, Velocity: {VelocityRangeStart}-{VelocityRangeEnd})";
		}
	}
	internal sealed class Lfo
	{
		private readonly Synthesizer synthesizer;

		private bool active;

		private double delay;

		private double period;

		private int processedSampleCount;

		private float value;

		public float Value => value;

		internal Lfo(Synthesizer synthesizer)
		{
			this.synthesizer = synthesizer;
		}

		public void Start(float delay, float frequency)
		{
			if ((double)frequency > 0.001)
			{
				active = true;
				this.delay = delay;
				period = 1.0 / (double)frequency;
				processedSampleCount = 0;
				value = 0f;
			}
			else
			{
				active = false;
				value = 0f;
			}
		}

		public void Process()
		{
			if (!active)
			{
				return;
			}
			processedSampleCount += synthesizer.BlockSize;
			double num = (double)processedSampleCount / (double)synthesizer.SampleRate;
			if (num < delay)
			{
				value = 0f;
				return;
			}
			double num2 = (num - delay) % period / period;
			if (num2 < 0.25)
			{
				value = (float)(4.0 * num2);
			}
			else if (num2 < 0.75)
			{
				value = (float)(4.0 * (0.5 - num2));
			}
			else
			{
				value = (float)(4.0 * (num2 - 1.0));
			}
		}
	}
	public enum LoopMode
	{
		NoLoop = 0,
		Continuous = 1,
		LoopUntilNoteOff = 3
	}
	public sealed class MidiFile
	{
		internal struct Message
		{
			private byte channel;

			private byte command;

			private byte data1;

			private byte data2;

			public MessageType Type => channel switch
			{
				252 => MessageType.TempoChange, 
				253 => MessageType.LoopStart, 
				254 => MessageType.LoopEnd, 
				byte.MaxValue => MessageType.EndOfTrack, 
				_ => MessageType.Normal, 
			};

			public byte Channel => channel;

			public byte Command => command;

			public byte Data1 => data1;

			public byte Data2 => data2;

			public double Tempo => 60000000.0 / (double)((command << 16) | (data1 << 8) | data2);

			private Message(byte channel, byte command, byte data1, byte data2)
			{
				this.channel = channel;
				this.command = command;
				this.data1 = data1;
				this.data2 = data2;
			}

			public static Message Common(byte status, byte data1)
			{
				byte b = (byte)(status & 0xF);
				byte b2 = (byte)(status & 0xF0);
				byte b3 = 0;
				return new Message(b, b2, data1, b3);
			}

			public static Message Common(byte status, byte data1, byte data2, MidiFileLoopType loopType)
			{
				byte b = (byte)(status & 0xF);
				byte b2 = (byte)(status & 0xF0);
				if (b2 == 176)
				{
					switch (loopType)
					{
					case MidiFileLoopType.RpgMaker:
						if (data1 == 111)
						{
							return LoopStart();
						}
						break;
					case MidiFileLoopType.IncredibleMachine:
						switch (data1)
						{
						case 110:
							return LoopStart();
						case 111:
							return LoopEnd();
						}
						break;
					case MidiFileLoopType.FinalFantasy:
						switch (data1)
						{
						case 116:
							return LoopStart();
						case 117:
							return LoopEnd();
						}
						break;
					}
				}
				return new Message(b, b2, data1, data2);
			}

			public static Message TempoChange(int tempo)
			{
				byte b = (byte)(tempo >> 16);
				byte b2 = (byte)(tempo >> 8);
				byte b3 = (byte)tempo;
				return new Message(252, b, b2, b3);
			}

			public static Message LoopStart()
			{
				return new Message(253, 0, 0, 0);
			}

			public static Message LoopEnd()
			{
				return new Message(254, 0, 0, 0);
			}

			public static Message EndOfTrack()
			{
				return new Message(byte.MaxValue, 0, 0, 0);
			}

			public override string ToString()
			{
				return channel switch
				{
					252 => "Tempo: " + Tempo, 
					253 => "LoopStart", 
					254 => "LoopEnd", 
					byte.MaxValue => "EndOfTrack", 
					_ => "CH" + channel + ": " + command.ToString("X2") + ", " + data1.ToString("X2") + ", " + data2.ToString("X2"), 
				};
			}
		}

		internal enum MessageType
		{
			Normal = 0,
			TempoChange = 252,
			LoopStart = 253,
			LoopEnd = 254,
			EndOfTrack = 255
		}

		private Message[] messages;

		private TimeSpan[] times;

		public TimeSpan Length => times.Last();

		internal Message[] Messages => messages;

		internal TimeSpan[] Times => times;

		public MidiFile(Stream stream)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			Load(stream, 0, MidiFileLoopType.None);
			Debug.Assert(messages != null);
			Debug.Assert(times != null);
		}

		public MidiFile(Stream stream, int loopPoint)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			if (loopPoint < 0)
			{
				throw new ArgumentException("The loop point must be a non-negative value.", "loopPoint");
			}
			Load(stream, loopPoint, MidiFileLoopType.None);
			Debug.Assert(messages != null);
			Debug.Assert(times != null);
		}

		public MidiFile(Stream stream, MidiFileLoopType loopType)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			Load(stream, 0, loopType);
			Debug.Assert(messages != null);
			Debug.Assert(times != null);
		}

		public MidiFile(string path)
		{
			if (path == null)
			{
				throw new ArgumentNullException("path");
			}
			using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
			{
				Load(stream, 0, MidiFileLoopType.None);
			}
			Debug.Assert(messages != null);
			Debug.Assert(times != null);
		}

		public MidiFile(string path, int loopPoint)
		{
			if (path == null)
			{
				throw new ArgumentNullException("path");
			}
			if (loopPoint < 0)
			{
				throw new ArgumentException("The loop point must be a non-negative value.", "loopPoint");
			}
			using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
			{
				Load(stream, loopPoint, MidiFileLoopType.None);
			}
			Debug.Assert(messages != null);
			Debug.Assert(times != null);
		}

		public MidiFile(string path, MidiFileLoopType loopType)
		{
			if (path == null)
			{
				throw new ArgumentNullException("path");
			}
			using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
			{
				Load(stream, 0, loopType);
			}
			Debug.Assert(messages != null);
			Debug.Assert(times != null);
		}

		internal static TimeSpan GetTimeSpanFromSeconds(double value)
		{
			return new TimeSpan((long)(10000000.0 * value));
		}

		private void Load(Stream stream, int loopPoint, MidiFileLoopType loopType)
		{
			using BinaryReader reader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true);
			string text = reader.ReadFourCC();
			if (text != "MThd")
			{
				throw new InvalidDataException("The chunk type must be 'MThd', but was '" + text + "'.");
			}
			int num = reader.ReadInt32BigEndian();
			if (num != 6)
			{
				throw new InvalidDataException("The MThd chunk has invalid data.");
			}
			short num2 = reader.ReadInt16BigEndian();
			if (num2 != 0 && num2 != 1)
			{
				throw new NotSupportedException($"The format {num2} is not supported.");
			}
			short num3 = reader.ReadInt16BigEndian();
			short resolution = reader.ReadInt16BigEndian();
			List<Message>[] array = new List<Message>[num3];
			List<int>[] array2 = new List<int>[num3];
			for (int i = 0; i < num3; i++)
			{
				int num4 = i;
				List<int>[] array3 = array2;
				int num5 = i;
				(List<Message>, List<int>) tuple = ReadTrack(reader, loopType);
				array[num4] = tuple.Item1;
				array3[num5] = tuple.Item2;
			}
			if (loopPoint != 0)
			{
				List<int> list = array2[0];
				List<Message> list2 = array[0];
				if (loopPoint <= list.Last())
				{
					for (int j = 0; j < list.Count; j++)
					{
						if (list[j] >= loopPoint)
						{
							list.Insert(j, loopPoint);
							list2.Insert(j, Message.LoopStart());
							break;
						}
					}
				}
				else
				{
					list.Add(loopPoint);
					list2.Add(Message.LoopStart());
				}
			}
			(messages, times) = MergeTracks(array, array2, resolution);
		}

		private static (List<Message>, List<int>) ReadTrack(BinaryReader reader, MidiFileLoopType loopType)
		{
			string text = reader.ReadFourCC();
			if (text != "MTrk")
			{
				throw new InvalidDataException("The chunk type must be 'MTrk', but was '" + text + "'.");
			}
			reader.ReadInt32BigEndian();
			List<Message> list = new List<Message>();
			List<int> list2 = new List<int>();
			int num = 0;
			byte b = 0;
			while (true)
			{
				int num2 = reader.ReadIntVariableLength();
				byte b2 = reader.ReadByte();
				try
				{
					num = checked(num + num2);
				}
				catch (OverflowException)
				{
					throw new NotSupportedException("Long MIDI file is not supported.");
				}
				if ((b2 & 0x80) == 0)
				{
					int num3 = b & 0xF0;
					if (num3 == 192 || num3 == 208)
					{
						list.Add(Message.Common(b, b2));
						list2.Add(num);
					}
					else
					{
						byte data = reader.ReadByte();
						list.Add(Message.Common(b, b2, data, loopType));
						list2.Add(num);
					}
					continue;
				}
				switch (b2)
				{
				case 240:
					DiscardData(reader);
					break;
				case 247:
					DiscardData(reader);
					break;
				case byte.MaxValue:
					switch (reader.ReadByte())
					{
					case 47:
						reader.ReadByte();
						list.Add(Message.EndOfTrack());
						list2.Add(num);
						return (list, list2);
					case 81:
						list.Add(Message.TempoChange(ReadTempo(reader)));
						list2.Add(num);
						break;
					default:
						DiscardData(reader);
						break;
					}
					break;
				default:
				{
					int num4 = b2 & 0xF0;
					if (num4 == 192 || num4 == 208)
					{
						byte data2 = reader.ReadByte();
						list.Add(Message.Common(b2, data2));
						list2.Add(num);
					}
					else
					{
						byte data3 = reader.ReadByte();
						byte data4 = reader.ReadByte();
						list.Add(Message.Common(b2, data3, data4, loopType));
						list2.Add(num);
					}
					break;
				}
				}
				b = b2;
			}
		}

		private static (Message[], TimeSpan[]) MergeTracks(List<Message>[] messageLists, List<int>[] tickLists, int resolution)
		{
			List<Message> list = new List<Message>();
			List<TimeSpan> list2 = new List<TimeSpan>();
			int[] array = new int[messageLists.Length];
			int num = 0;
			TimeSpan zero = TimeSpan.Zero;
			double num2 = 120.0;
			while (true)
			{
				int num3 = int.MaxValue;
				int num4 = -1;
				for (int i = 0; i < tickLists.Length; i++)
				{
					if (array[i] < tickLists[i].Count)
					{
						int num5 = tickLists[i][array[i]];
						if (num5 < num3)
						{
							num3 = num5;
							num4 = i;
						}
					}
				}
				if (num4 == -1)
				{
					break;
				}
				int num6 = tickLists[num4][array[num4]];
				int num7 = num6 - num;
				TimeSpan timeSpanFromSeconds = GetTimeSpanFromSeconds(60.0 / ((double)resolution * num2) * (double)num7);
				num += num7;
				zero += timeSpanFromSeconds;
				Message item = messageLists[num4][array[num4]];
				if (item.Type == MessageType.TempoChange)
				{
					num2 = item.Tempo;
				}
				else
				{
					list.Add(item);
					list2.Add(zero);
				}
				array[num4]++;
			}
			return (list.ToArray(), list2.ToArray());
		}

		private static int ReadTempo(BinaryReader reader)
		{
			int num = reader.ReadIntVariableLength();
			if (num != 3)
			{
				throw new InvalidDataException("Failed to read the tempo value.");
			}
			byte b = reader.ReadByte();
			byte b2 = reader.ReadByte();
			byte b3 = reader.ReadByte();
			return (b << 16) | (b2 << 8) | b3;
		}

		private static void DiscardData(BinaryReader reader)
		{
			int num = reader.ReadIntVariableLength();
			reader.BaseStream.Position += num;
		}
	}
	public enum MidiFileLoopType
	{
		None,
		RpgMaker,
		IncredibleMachine,
		FinalFantasy
	}
	public sealed class MidiFileSequencer : IAudioRenderer
	{
		private readonly Synthesizer synthesizer;

		private float speed;

		private MidiFile midiFile;

		private bool loop;

		private int blockWrote;

		private TimeSpan currentTime;

		private int msgIndex;

		private int loopIndex;

		public TimeSpan Position => currentTime;

		public bool EndOfSequence
		{
			get
			{
				if (midiFile == null)
				{
					return true;
				}
				return msgIndex == midiFile.Messages.Length;
			}
		}

		public float Speed
		{
			get
			{
				return speed;
			}
			set
			{
				if (value > 0f)
				{
					speed = value;
					return;
				}
				throw new ArgumentOutOfRangeException("The playback speed must be a positive value.");
			}
		}

		public MidiFileSequencer(Synthesizer synthesizer)
		{
			if (synthesizer == null)
			{
				throw new ArgumentNullException("synthesizer");
			}
			this.synthesizer = synthesizer;
			speed = 1f;
		}

		public void Play(MidiFile midiFile, bool loop)
		{
			if (midiFile == null)
			{
				throw new ArgumentNullException("midiFile");
			}
			this.midiFile = midiFile;
			this.loop = loop;
			blockWrote = synthesizer.BlockSize;
			currentTime = TimeSpan.Zero;
			msgIndex = 0;
			loopIndex = 0;
			synthesizer.Reset();
		}

		public void Stop()
		{
			midiFile = null;
			synthesizer.Reset();
		}

		public void Render(Span<float> left, Span<float> right)
		{
			if (left.Length != right.Length)
			{
				throw new ArgumentException("The output buffers for the left and right must be the same length.");
			}
			int num;
			for (int i = 0; i < left.Length; i += num)
			{
				if (blockWrote == synthesizer.BlockSize)
				{
					ProcessEvents();
					blockWrote = 0;
					currentTime += MidiFile.GetTimeSpanFromSeconds((double)speed * (double)synthesizer.BlockSize / (double)synthesizer.SampleRate);
				}
				int val = synthesizer.BlockSize - blockWrote;
				int val2 = left.Length - i;
				num = Math.Min(val, val2);
				synthesizer.Render(left.Slice(i, num), right.Slice(i, num));
				blockWrote += num;
			}
		}

		private void ProcessEvents()
		{
			if (midiFile == null)
			{
				return;
			}
			while (msgIndex < midiFile.Messages.Length)
			{
				TimeSpan timeSpan = midiFile.Times[msgIndex];
				MidiFile.Message message = midiFile.Messages[msgIndex];
				if (!(timeSpan <= currentTime))
				{
					break;
				}
				if (message.Type == MidiFile.MessageType.Normal)
				{
					synthesizer.ProcessMidiMessage(message.Channel, message.Command, message.Data1, message.Data2);
				}
				else if (loop)
				{
					if (message.Type == MidiFile.MessageType.LoopStart)
					{
						loopIndex = msgIndex;
					}
					else if (message.Type == MidiFile.MessageType.LoopEnd)
					{
						currentTime = midiFile.Times[loopIndex];
						msgIndex = loopIndex;
						synthesizer.NoteOffAll(immediate: false);
					}
				}
				msgIndex++;
			}
			if (msgIndex == midiFile.Messages.Length && loop)
			{
				currentTime = midiFile.Times[loopIndex];
				msgIndex = loopIndex;
				synthesizer.NoteOffAll(immediate: false);
			}
		}
	}
	internal sealed class ModulationEnvelope
	{
		private enum Stage
		{
			Delay,
			Attack,
			Hold,
			Decay,
			Release
		}

		private readonly Synthesizer synthesizer;

		private double attackSlope;

		private double decaySlope;

		private double releaseSlope;

		private double attackStartTime;

		private double holdStartTime;

		private double decayStartTime;

		private double decayEndTime;

		private double releaseEndTime;

		private float sustainLevel;

		private float releaseLevel;

		private int processedSampleCount;

		private Stage stage;

		private float value;

		public float Value => value;

		internal ModulationEnvelope(Synthesizer synthesizer)
		{
			this.synthesizer = synthesizer;
		}

		public void Start(float delay, float attack, float hold, float decay, float sustain, float release)
		{
			attackSlope = 1f / attack;
			decaySlope = 1f / decay;
			releaseSlope = 1f / release;
			attackStartTime = delay;
			holdStartTime = attackStartTime + (double)attack;
			decayStartTime = holdStartTime + (double)hold;
			decayEndTime = decayStartTime + (double)decay;
			releaseEndTime = release;
			sustainLevel = SoundFontMath.Clamp(sustain, 0f, 1f);
			releaseLevel = 0f;
			processedSampleCount = 0;
			stage = Stage.Delay;
			value = 0f;
			Process(0);
		}

		public void Release()
		{
			stage = Stage.Release;
			releaseEndTime += (double)processedSampleCount / (double)synthesizer.SampleRate;
			releaseLevel = value;
		}

		public bool Process()
		{
			return Process(synthesizer.BlockSize);
		}

		private bool Process(int sampleCount)
		{
			processedSampleCount += sampleCount;
			double num = (double)processedSampleCount / (double)synthesizer.SampleRate;
			while (stage <= Stage.Hold && !(num < stage switch
			{
				Stage.Delay => attackStartTime, 
				Stage.Attack => holdStartTime, 
				Stage.Hold => decayStartTime, 
				_ => throw new InvalidOperationException("Invalid envelope stage."), 
			}))
			{
				stage++;
			}
			switch (stage)
			{
			case Stage.Delay:
				value = 0f;
				return true;
			case Stage.Attack:
				value = (float)(attackSlope * (num - attackStartTime));
				return true;
			case Stage.Hold:
				value = 1f;
				return true;
			case Stage.Decay:
				value = Math.Max((float)(decaySlope * (decayEndTime - num)), sustainLevel);
				return value > SoundFontMath.NonAudible;
			case Stage.Release:
				value = Math.Max((float)((double)releaseLevel * releaseSlope * (releaseEndTime - num)), 0f);
				return value > SoundFontMath.NonAudible;
			default:
				throw new InvalidOperationException("Invalid envelope stage.");
			}
		}
	}
	internal static class Modulator
	{
		internal static void DiscardData(BinaryReader reader, int size)
		{
			if (size % 10 != 0)
			{
				throw new InvalidDataException("The modulator list is invalid.");
			}
			reader.BaseStream.Position += size;
		}
	}
	internal sealed class Oscillator
	{
		private const int fracBits = 24;

		private const long fracUnit = 16777216L;

		private const float fpToSample = 1.8189894E-12f;

		private readonly Synthesizer synthesizer;

		private short[] data;

		private LoopMode loopMode;

		private int sampleRate;

		private int start;

		private int end;

		private int startLoop;

		private int endLoop;

		private int rootKey;

		private float tune;

		private float pitchChangeScale;

		private float sampleRateRatio;

		private bool looping;

		private long position_fp;

		internal Oscillator(Synthesizer synthesizer)
		{
			this.synthesizer = synthesizer;
		}

		public void Start(short[] data, LoopMode loopMode, int sampleRate, int start, int end, int startLoop, int endLoop, int rootKey, int coarseTune, int fineTune, int scaleTuning)
		{
			this.data = data;
			this.loopMode = loopMode;
			this.sampleRate = sampleRate;
			this.start = start;
			this.end = end;
			this.startLoop = startLoop;
			this.endLoop = endLoop;
			this.rootKey = rootKey;
			tune = (float)coarseTune + 0.01f * (float)fineTune;
			pitchChangeScale = 0.01f * (float)scaleTuning;
			sampleRateRatio = (float)sampleRate / (float)synthesizer.SampleRate;
			if (loopMode == LoopMode.NoLoop)
			{
				looping = false;
			}
			else
			{
				looping = true;
			}
			position_fp = (long)start << 24;
		}

		public void Release()
		{
			if (loopMode == LoopMode.LoopUntilNoteOff)
			{
				looping = false;
			}
		}

		public bool Process(float[] block, float pitch)
		{
			float num = pitchChangeScale * (pitch - (float)rootKey) + tune;
			float num2 = sampleRateRatio * Mathf.Pow(2f, num / 12f);
			return FillBlock(block, num2);
		}

		internal bool FillBlock(float[] block, double pitchRatio)
		{
			long pitchRatio_fp = (long)(16777216.0 * pitchRatio);
			if (looping)
			{
				return FillBlock_Continuous(block, pitchRatio_fp);
			}
			return FillBlock_NoLoop(block, pitchRatio_fp);
		}

		private bool FillBlock_NoLoop(float[] block, long pitchRatio_fp)
		{
			for (int i = 0; i < block.Length; i++)
			{
				long num = position_fp >> 24;
				if (num >= end)
				{
					if (i > 0)
					{
						Array.Clear(block, i, block.Length - i);
						return true;
					}
					return false;
				}
				short num2 = data[num];
				short num3 = data[num + 1];
				long num4 = position_fp & 0xFFFFFF;
				block[i] = 1.8189894E-12f * (float)(((long)num2 << 24) + num4 * (num3 - num2));
				position_fp += pitchRatio_fp;
			}
			return true;
		}

		private bool FillBlock_Continuous(float[] block, long pitchRatio_fp)
		{
			long num = (long)endLoop << 24;
			long num2 = endLoop - startLoop;
			long num3 = num2 << 24;
			for (int i = 0; i < block.Length; i++)
			{
				if (position_fp >= num)
				{
					position_fp -= num3;
				}
				long num4 = position_fp >> 24;
				long num5 = num4 + 1;
				if (num5 >= endLoop)
				{
					num5 -= num2;
				}
				short num6 = data[num4];
				short num7 = data[num5];
				long num8 = position_fp & 0xFFFFFF;
				block[i] = 1.8189894E-12f * (float)(((long)num6 << 24) + num8 * (num7 - num6));
				position_fp += pitchRatio_fp;
			}
			return true;
		}
	}
	public sealed class Preset
	{
		internal static readonly Preset Default = new Preset();

		private readonly string name;

		private readonly int patchNumber;

		private readonly int bankNumber;

		private readonly int library;

		private readonly int genre;

		private readonly int morphology;

		private readonly PresetRegion[] regions;

		public string Name => name;

		public int PatchNumber => patchNumber;

		public int BankNumber => bankNumber;

		public IReadOnlyList<PresetRegion> Regions => regions;

		internal PresetRegion[] RegionArray => regions;

		private Preset()
		{
			name = "Default";
			regions = Array.Empty<PresetRegion>();
		}

		private Preset(PresetInfo info, Zone[] zones, Instrument[] instruments)
		{
			name = info.Name;
			patchNumber = info.PatchNumber;
			bankNumber = info.BankNumber;
			library = info.Library;
			genre = info.Genre;
			morphology = info.Morphology;
			int num = info.ZoneEndIndex - info.ZoneStartIndex + 1;
			if (num <= 0)
			{
				throw new InvalidDataException("The preset '" + info.Name + "' has no zone.");
			}
			Span<Zone> zones2 = new Span<Zone>(zones, info.ZoneStartIndex, num);
			regions = PresetRegion.Create(this, zones2, instruments);
		}

		internal static Preset[] Create(PresetInfo[] infos, Zone[] zones, Instrument[] instruments)
		{
			if (infos.Length <= 1)
			{
				throw new InvalidDataException("No valid preset was found.");
			}
			Preset[] array = new Preset[infos.Length - 1];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new Preset(infos[i], zones, instruments);
			}
			return array;
		}

		public override string ToString()
		{
			return name;
		}
	}
	internal sealed class PresetInfo
	{
		private string name;

		private int patchNumber;

		private int bankNumber;

		private int zoneStartIndex;

		private int zoneEndIndex;

		private int library;

		private int genre;

		private int morphology;

		public string Name => name;

		public int PatchNumber => patchNumber;

		public int BankNumber => bankNumber;

		public int ZoneStartIndex => zoneStartIndex;

		public int ZoneEndIndex => zoneEndIndex;

		public int Library => library;

		public int Genre => genre;

		public int Morphology => morphology;

		private PresetInfo(BinaryReader reader)
		{
			name = reader.ReadFixedLengthString(20);
			patchNumber = reader.ReadUInt16();
			bankNumber = reader.ReadUInt16();
			zoneStartIndex = reader.ReadUInt16();
			library = reader.ReadInt32();
			genre = reader.ReadInt32();
			morphology = reader.ReadInt32();
		}

		internal static PresetInfo[] ReadFromChunk(BinaryReader reader, int size)
		{
			if (size % 38 != 0)
			{
				throw new InvalidDataException("The preset list is invalid.");
			}
			int num = size / 38;
			PresetInfo[] array = new PresetInfo[num];
			for (int i = 0; i < num; i++)
			{
				array[i] = new PresetInfo(reader);
			}
			for (int j = 0; j < num - 1; j++)
			{
				array[j].zoneEndIndex = array[j + 1].zoneStartIndex - 1;
			}
			return array;
		}
	}
	public sealed class PresetRegion
	{
		internal static readonly PresetRegion Default = new PresetRegion();

		private readonly short[] gs;

		private readonly Instrument instrument;

		internal short this[GeneratorType generatortType] => gs[(uint)generatortType];

		public Instrument Instrument => instrument;

		public int ModulationLfoToPitch => this[GeneratorType.ModulationLfoToPitch];

		public int VibratoLfoToPitch => this[GeneratorType.VibratoLfoToPitch];

		public int ModulationEnvelopeToPitch => this[GeneratorType.ModulationEnvelopeToPitch];

		public float InitialFilterCutoffFrequency => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.InitialFilterCutoffFrequency]);

		public float InitialFilterQ => 0.1f * (float)this[GeneratorType.InitialFilterQ];

		public int ModulationLfoToFilterCutoffFrequency => this[GeneratorType.ModulationLfoToFilterCutoffFrequency];

		public int ModulationEnvelopeToFilterCutoffFrequency => this[GeneratorType.ModulationEnvelopeToFilterCutoffFrequency];

		public float ModulationLfoToVolume => 0.1f * (float)this[GeneratorType.ModulationLfoToVolume];

		public float ChorusEffectsSend => 0.1f * (float)this[GeneratorType.ChorusEffectsSend];

		public float ReverbEffectsSend => 0.1f * (float)this[GeneratorType.ReverbEffectsSend];

		public float Pan => 0.1f * (float)this[GeneratorType.Pan];

		public float DelayModulationLfo => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.DelayModulationLfo]);

		public float FrequencyModulationLfo => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.FrequencyModulationLfo]);

		public float DelayVibratoLfo => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.DelayVibratoLfo]);

		public float FrequencyVibratoLfo => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.FrequencyVibratoLfo]);

		public float DelayModulationEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.DelayModulationEnvelope]);

		public float AttackModulationEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.AttackModulationEnvelope]);

		public float HoldModulationEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.HoldModulationEnvelope]);

		public float DecayModulationEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.DecayModulationEnvelope]);

		public float SustainModulationEnvelope => 0.1f * (float)this[GeneratorType.SustainModulationEnvelope];

		public float ReleaseModulationEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.ReleaseModulationEnvelope]);

		public int KeyNumberToModulationEnvelopeHold => this[GeneratorType.KeyNumberToModulationEnvelopeHold];

		public int KeyNumberToModulationEnvelopeDecay => this[GeneratorType.KeyNumberToModulationEnvelopeDecay];

		public float DelayVolumeEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.DelayVolumeEnvelope]);

		public float AttackVolumeEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.AttackVolumeEnvelope]);

		public float HoldVolumeEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.HoldVolumeEnvelope]);

		public float DecayVolumeEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.DecayVolumeEnvelope]);

		public float SustainVolumeEnvelope => 0.1f * (float)this[GeneratorType.SustainVolumeEnvelope];

		public float ReleaseVolumeEnvelope => SoundFontMath.CentsToMultiplyingFactor(this[GeneratorType.ReleaseVolumeEnvelope]);

		public int KeyNumberToVolumeEnvelopeHold => this[GeneratorType.KeyNumberToVolumeEnvelopeHold];

		public int KeyNumberToVolumeEnvelopeDecay => this[GeneratorType.KeyNumberToVolumeEnvelopeDecay];

		public int KeyRangeStart => this[GeneratorType.KeyRange] & 0xFF;

		public int KeyRangeEnd => (this[GeneratorType.KeyRange] >> 8) & 0xFF;

		public int VelocityRangeStart => this[GeneratorType.VelocityRange] & 0xFF;

		public int VelocityRangeEnd => (this[GeneratorType.VelocityRange] >> 8) & 0xFF;

		public float InitialAttenuation => 0.1f * (float)this[GeneratorType.InitialAttenuation];

		public int CoarseTune => this[GeneratorType.CoarseTune];

		public int FineTune => this[GeneratorType.FineTune];

		public int ScaleTuning => this[GeneratorType.ScaleTuning];

		private PresetRegion()
		{
			gs = new short[61];
			gs[43] = 32512;
			gs[44] = 32512;
			instrument = Instrument.Default;
		}

		private PresetRegion(Preset preset, Zone global, Zone local, Instrument[] instruments)
			: this()
		{
			foreach (Generator generator in global.Generators)
			{
				SetParameter(generator);
			}
			foreach (Generator generator2 in local.Generators)
			{
				SetParameter(generator2);
			}
			short num = gs[41];
			if (0 > num || num >= instruments.Length)
			{
				throw new InvalidDataException($"The preset '{preset.Name}' contains an invalid instrument ID '{num}'.");
			}
			instrument = instruments[num];
		}

		internal static PresetRegion[] Create(Preset preset, Span<Zone> zones, Instrument[] instruments)
		{
			if (zones[0].Generators.Count == 0 || zones[0].Generators.Last().Type != GeneratorType.Instrument)
			{
				Zone global = zones[0];
				PresetRegion[] array = new PresetRegion[zones.Length - 1];
				for (int i = 0; i < array.Length; i++)
				{
					array[i] = new PresetRegion(preset, global, zones[i + 1], instruments);
				}
				return array;
			}
			PresetRegion[] array2 = new PresetRegion[zones.Length];
			for (int j = 0; j < array2.Length; j++)
			{
				array2[j] = new PresetRegion(preset, Zone.Empty, zones[j], instruments);
			}
			return array2;
		}

		private void SetParameter(Generator generator)
		{
			int type = (int)generator.Type;
			if (0 <= type && type < gs.Length)
			{
				gs[type] = (short)generator.Value;
			}
		}

		public bool Contains(int key, int velocity)
		{
			bool flag = KeyRangeStart <= key && key <= KeyRangeEnd;
			bool flag2 = VelocityRangeStart <= velocity && velocity <= VelocityRangeEnd;
			return flag && flag2;
		}

		public override string ToString()
		{
			return $"{instrument.Name} (Key: {KeyRangeStart}-{KeyRangeEnd}, Velocity: {VelocityRangeStart}-{VelocityRangeEnd})";
		}
	}
	internal static class RegionEx
	{
		public static void Start(this Oscillator oscillator, short[] data, InstrumentRegion region)
		{
			oscillator.Start(data, new RegionPair(PresetRegion.Default, region));
		}

		public static void Start(this Oscillator oscillator, short[] data, RegionPair region)
		{
			int sampleRate = region.Instrument.Sample.SampleRate;
			LoopMode sampleModes = region.SampleModes;
			int sampleStart = region.SampleStart;
			int sampleEnd = region.SampleEnd;
			int sampleStartLoop = region.SampleStartLoop;
			int sampleEndLoop = region.SampleEndLoop;
			int rootKey = region.RootKey;
			int coarseTune = region.CoarseTune;
			int fineTune = region.FineTune;
			int scaleTuning = region.ScaleTuning;
			oscillator.Start(data, sampleModes, sampleRate, sampleStart, sampleEnd, sampleStartLoop, sampleEndLoop, rootKey, coarseTune, fineTune, scaleTuning);
		}

		public static void Start(this VolumeEnvelope envelope, InstrumentRegion region, int key, int velocity)
		{
			envelope.Start(new RegionPair(PresetRegion.Default, region), key, velocity);
		}

		public static void Start(this VolumeEnvelope envelope, RegionPair region, int key, int velocity)
		{
			float delayVolumeEnvelope = region.DelayVolumeEnvelope;
			float attackVolumeEnvelope = region.AttackVolumeEnvelope;
			float hold = region.HoldVolumeEnvelope * SoundFontMath.KeyNumberToMultiplyingFactor(region.KeyNumberToVolumeEnvelopeHold, key);
			float decay = region.DecayVolumeEnvelope * SoundFontMath.KeyNumberToMultiplyingFactor(region.KeyNumberToVolumeEnvelopeDecay, key);
			float sustain = SoundFontMath.DecibelsToLinear(0f - region.SustainVolumeEnvelope);
			float release = Math.Max(region.ReleaseVolumeEnvelope, 0.01f);
			envelope.Start(delayVolumeEnvelope, attackVolumeEnvelope, hold, decay, sustain, release);
		}

		public static void Start(this ModulationEnvelope envelope, InstrumentRegion region, int key, int velocity)
		{
			envelope.Start(new RegionPair(PresetRegion.Default, region), key, velocity);
		}

		public static void Start(this ModulationEnvelope envelope, RegionPair region, int key, int velocity)
		{
			float delayModulationEnvelope = region.DelayModulationEnvelope;
			float attack = region.AttackModulationEnvelope * ((float)(145 - velocity) / 144f);
			float hold = region.HoldModulationEnvelope * SoundFontMath.KeyNumberToMultiplyingFactor(region.KeyNumberToModulationEnvelopeHold, key);
			float decay = region.DecayModulationEnvelope * SoundFontMath.KeyNumberToMultiplyingFactor(region.KeyNumberToModulationEnvelopeDecay, key);
			float sustain = 1f - region.SustainModulationEnvelope / 100f;
			float releaseModulationEnvelope = region.ReleaseModulationEnvelope;
			envelope.Start(delayModulationEnvelope, attack, hold, decay, sustain, releaseModulationEnvelope);
		}

		public static void StartVibrato(this Lfo lfo, InstrumentRegion region, int key, int velocity)
		{
			lfo.StartVibrato(new RegionPair(PresetRegion.Default, region), key, velocity);
		}

		public static void StartVibrato(this Lfo lfo, RegionPair region, int key, int velocity)
		{
			lfo.Start(region.DelayVibratoLfo, region.FrequencyVibratoLfo);
		}

		public static void StartModulation(this Lfo lfo, InstrumentRegion region, int key, int velocity)
		{
			lfo.StartModulation(new RegionPair(PresetRegion.Default, region), key, velocity);
		}

		public static void StartModulation(this Lfo lfo, RegionPair region, int key, int velocity)
		{
			lfo.Start(region.DelayModulationLfo, region.FrequencyModulationLfo);
		}
	}
	internal struct RegionPair
	{
		private readonly PresetRegion preset;

		private readonly InstrumentRegion instrument;

		private int this[GeneratorType generatortType] => instrument[generatortType] + preset[generatortType];

		public PresetRegion Preset => preset;

		public InstrumentRegion Instrument => instrument;

		public int SampleStart => instrument.SampleStart;

		public int SampleEnd => instrument.SampleEnd;

		public int SampleStartLoop => instrument.SampleStartLoop;

		public int SampleEndLoop => instrument.SampleEndLoop;

		public int StartAddressOffset => instrument.StartAddressOffset;

		public int EndAddressOffset => instrument.EndAddressOffset;

		public int StartLoopAddressOffset => instrument.StartLoopAddressOffset;

		public int EndLoopAddressOffset => instrument.EndLoopAddressOffset;

		public int ModulationLfoToPitch => this[GeneratorType.ModulationLfoToPitch];

		public int VibratoLfoToPitch => this[GeneratorType.VibratoLfoToPitch];

		public int ModulationEnvelopeToPitch => this[GeneratorType.ModulationEnvelopeToPitch];

		public float InitialFilterCutoffFrequency => SoundFontMath.CentsToHertz(this[GeneratorType.InitialFilterCutoffFrequency]);

		public float InitialFilterQ => 0.1f * (float)this[GeneratorType.InitialFilterQ];

		public int ModulationLfoToFilterCutoffFrequency => this[GeneratorType.ModulationLfoToFilterCutoffFrequency];

		public int ModulationEnvelopeToFilterCutoffFrequency => this[GeneratorType.ModulationEnvelopeToFilterCutoffFrequency];

		public float ModulationLfoToVolume => 0.1f * (float)this[GeneratorType.ModulationLfoToVolume];

		public float ChorusEffectsSend => 0.1f * (float)this[GeneratorType.ChorusEffectsSend];

		public float ReverbEffectsSend => 0.1f * (float)this[GeneratorType.ReverbEffectsSend];

		public float Pan => 0.1f * (float)this[GeneratorType.Pan];

		public float DelayModulationLfo => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DelayModulationLfo]);

		public float FrequencyModulationLfo => SoundFontMath.CentsToHertz(this[GeneratorType.FrequencyModulationLfo]);

		public float DelayVibratoLfo => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DelayVibratoLfo]);

		public float FrequencyVibratoLfo => SoundFontMath.CentsToHertz(this[GeneratorType.FrequencyVibratoLfo]);

		public float DelayModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DelayModulationEnvelope]);

		public float AttackModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.AttackModulationEnvelope]);

		public float HoldModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.HoldModulationEnvelope]);

		public float DecayModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DecayModulationEnvelope]);

		public float SustainModulationEnvelope => 0.1f * (float)this[GeneratorType.SustainModulationEnvelope];

		public float ReleaseModulationEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.ReleaseModulationEnvelope]);

		public int KeyNumberToModulationEnvelopeHold => this[GeneratorType.KeyNumberToModulationEnvelopeHold];

		public int KeyNumberToModulationEnvelopeDecay => this[GeneratorType.KeyNumberToModulationEnvelopeDecay];

		public float DelayVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DelayVolumeEnvelope]);

		public float AttackVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.AttackVolumeEnvelope]);

		public float HoldVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.HoldVolumeEnvelope]);

		public float DecayVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.DecayVolumeEnvelope]);

		public float SustainVolumeEnvelope => 0.1f * (float)this[GeneratorType.SustainVolumeEnvelope];

		public float ReleaseVolumeEnvelope => SoundFontMath.TimecentsToSeconds(this[GeneratorType.ReleaseVolumeEnvelope]);

		public int KeyNumberToVolumeEnvelopeHold => this[GeneratorType.KeyNumberToVolumeEnvelopeHold];

		public int KeyNumberToVolumeEnvelopeDecay => this[GeneratorType.KeyNumberToVolumeEnvelopeDecay];

		public float InitialAttenuation => 0.1f * (float)this[GeneratorType.InitialAttenuation];

		public int CoarseTune => this[GeneratorType.CoarseTune];

		public int FineTune => this[GeneratorType.FineTune] + instrument.Sample.PitchCorrection;

		public LoopMode SampleModes => instrument.SampleModes;

		public int ScaleTuning => this[GeneratorType.ScaleTuning];

		public int ExclusiveClass => instrument.ExclusiveClass;

		public int RootKey => instrument.RootKey;

		internal RegionPair(PresetRegion preset, InstrumentRegion instrument)
		{
			this.preset = preset;
			this.instrument = instrument;
		}
	}
	internal sealed class Reverb
	{
		internal sealed class CombFilter
		{
			private readonly float[] buffer;

			private int bufferIndex;

			private float filterStore;

			private float feedback;

			private float damp1;

			private float damp2;

			public float Feedback
			{
				get
				{
					return feedback;
				}
				set
				{
					feedback = value;
				}
			}

			public float Damp
			{
				get
				{
					return damp1;
				}
				set
				{
					damp1 = value;
					damp2 = 1f - value;
				}
			}

			public CombFilter(int bufferSize)
			{
				buffer = new float[bufferSize];
				bufferIndex = 0;
				filterStore = 0f;
				feedback = 0f;
				damp1 = 0f;
				damp2 = 0f;
			}

			public void Mute()
			{
				Array.Clear(buffer, 0, buffer.Length);
				filterStore = 0f;
			}

			public void Process(float[] inputBlock, float[] outputBlock)
			{
				int num;
				for (int i = 0; i < outputBlock.Length; i += num)
				{
					if (bufferIndex == buffer.Length)
					{
						bufferIndex = 0;
					}
					int val = buffer.Length - bufferIndex;
					int val2 = outputBlock.Length - i;
					num = Math.Min(val, val2);
					for (int j = 0; j < num; j++)
					{
						int num2 = i + j;
						int num3 = bufferIndex + j;
						float num4 = inputBlock[num2];
						float num5 = buffer[num3];
						if (Mathf.Abs(num5) < 1E-06f)
						{
							num5 = 0f;
						}
						filterStore = num5 * damp2 + filterStore * damp1;
						if (Mathf.Abs(filterStore) < 1E-06f)
						{
							filterStore = 0f;
						}
						buffer[num3] = num4 + filterStore * feedback;
						outputBlock[num2] += num5;
					}
					bufferIndex += num;
				}
			}
		}

		internal sealed class AllPassFilter
		{
			private readonly float[] buffer;

			private int bufferIndex;

			private float feedback;

			public float Feedback
			{
				get
				{
					return feedback;
				}
				set
				{
					feedback = value;
				}
			}

			public AllPassFilter(int bufferSize)
			{
				buffer = new float[bufferSize];
				bufferIndex = 0;
				feedback = 0f;
			}

			public void Mute()
			{
				Array.Clear(buffer, 0, buffer.Length);
			}

			public void Process(float[] block)
			{
				int num;
				for (int i = 0; i < block.Length; i += num)
				{
					if (bufferIndex == buffer.Length)
					{
						bufferIndex = 0;
					}
					int val = buffer.Length - bufferIndex;
					int val2 = block.Length - i;
					num = Math.Min(val, val2);
					for (int j = 0; j < num; j++)
					{
						int num2 = i + j;
						int num3 = bufferIndex + j;
						float num4 = block[num2];
						float num5 = buffer[num3];
						if (Mathf.Abs(num5) < 1E-06f)
						{
							num5 = 0f;
						}
						block[num2] = num5 - num4;
						buffer[num3] = num4 + num5 * feedback;
					}
					bufferIndex += num;
				}
			}
		}

		private const float fixedGain = 0.015f;

		private const float scaleWet = 3f;

		private const float scaleDamp = 0.4f;

		private const float scaleRoom = 0.28f;

		private const float offsetRoom = 0.7f;

		private const float initialRoom = 0.5f;

		private const float initialDamp = 0.5f;

		private const float initialWet = 1f / 3f;

		private const float initialWidth = 1f;

		private const int stereoSpread = 23;

		private const int cfTuningL1 = 1116;

		private const int cfTuningR1 = 1139;

		private const int cfTuningL2 = 1188;

		private const int cfTuningR2 = 1211;

		private const int cfTuningL3 = 1277;

		private const int cfTuningR3 = 1300;

		private const int cfTuningL4 = 1356;

		private const int cfTuningR4 = 1379;

		private const int cfTuningL5 = 1422;

		private const int cfTuningR5 = 1445;

		private const int cfTuningL6 = 1491;

		private const int cfTuningR6 = 1514;

		private const int cfTuningL7 = 1557;

		private const int cfTuningR7 = 1580;

		private const int cfTuningL8 = 1617;

		private const int cfTuningR8 = 1640;

		private const int apfTuningL1 = 556;

		private const int apfTuningR1 = 579;

		private const int apfTuningL2 = 441;

		private const int apfTuningR2 = 464;

		private const int apfTuningL3 = 341;

		private const int apfTuningR3 = 364;

		private const int apfTuningL4 = 225;

		private const int apfTuningR4 = 248;

		private readonly CombFilter[] cfsL;

		private readonly CombFilter[] cfsR;

		private readonly AllPassFilter[] apfsL;

		private readonly AllPassFilter[] apfsR;

		private float gain;

		private float roomSize;

		private float roomSize1;

		private float damp;

		private float damp1;

		private float wet;

		private float wet1;

		private float wet2;

		private float width;

		public float InputGain => gain;

		public float RoomSize
		{
			get
			{
				return (roomSize - 0.7f) / 0.28f;
			}
			set
			{
				roomSize = value * 0.28f + 0.7f;
				Update();
			}
		}

		public float Damp
		{
			get
			{
				return damp / 0.4f;
			}
			set
			{
				damp = value * 0.4f;
				Update();
			}
		}

		public float Wet
		{
			get
			{
				return wet / 3f;
			}
			set
			{
				wet = value * 3f;
				Update();
			}
		}

		public float Width
		{
			get
			{
				return width;
			}
			set
			{
				width = value;
				Update();
			}
		}

		internal Reverb(int sampleRate)
		{
			cfsL = new CombFilter[8]
			{
				new CombFilter(ScaleTuning(sampleRate, 1116)),
				new CombFilter(ScaleTuning(sampleRate, 1188)),
				new CombFilter(ScaleTuning(sampleRate, 1277)),
				new CombFilter(ScaleTuning(sampleRate, 1356)),
				new CombFilter(ScaleTuning(sampleRate, 1422)),
				new CombFilter(ScaleTuning(sampleRate, 1491)),
				new CombFilter(ScaleTuning(sampleRate, 1557)),
				new CombFilter(ScaleTuning(sampleRate, 1617))
			};
			cfsR = new CombFilter[8]
			{
				new CombFilter(ScaleTuning(sampleRate, 1139)),
				new CombFilter(ScaleTuning(sampleRate, 1211)),
				new CombFilter(ScaleTuning(sampleRate, 1300)),
				new CombFilter(ScaleTuning(sampleRate, 1379)),
				new CombFilter(ScaleTuning(sampleRate, 1445)),
				new CombFilter(ScaleTuning(sampleRate, 1514)),
				new CombFilter(ScaleTuning(sampleRate, 1580)),
				new CombFilter(ScaleTuning(sampleRate, 1640))
			};
			apfsL = new AllPassFilter[4]
			{
				new AllPassFilter(ScaleTuning(sampleRate, 556)),
				new AllPassFilter(ScaleTuning(sampleRate, 441)),
				new AllPassFilter(ScaleTuning(sampleRate, 341)),
				new AllPassFilter(ScaleTuning(sampleRate, 225))
			};
			apfsR = new AllPassFilter[4]
			{
				new AllPassFilter(ScaleTuning(sampleRate, 579)),
				new AllPassFilter(ScaleTuning(sampleRate, 464)),
				new AllPassFilter(ScaleTuning(sampleRate, 364)),
				new AllPassFilter(ScaleTuning(sampleRate, 248))
			};
			AllPassFilter[] array = apfsL;
			foreach (AllPassFilter allPassFilter in array)
			{
				allPassFilter.Feedback = 0.5f;
			}
			AllPassFilter[] array2 = apfsR;
			foreach (AllPassFilter allPassFilter2 in array2)
			{
				allPassFilter2.Feedback = 0.5f;
			}
			Wet = 1f / 3f;
			RoomSize = 0.5f;
			Damp = 0.5f;
			Width = 1f;
		}

		private int ScaleTuning(int sampleRate, int tuning)
		{
			return (int)Math.Round((double)sampleRate / 44100.0 * (double)tuning);
		}

		public void Process(float[] input, float[] outputLeft, float[] outputRight)
		{
			Array.Clear(outputLeft, 0, outputLeft.Length);
			Array.Clear(outputRight, 0, outputRight.Length);
			CombFilter[] array = cfsL;
			foreach (CombFilter combFilter in array)
			{
				combFilter.Process(input, outputLeft);
			}
			AllPassFilter[] array2 = apfsL;
			foreach (AllPassFilter allPassFilter in array2)
			{
				allPassFilter.Process(outputLeft);
			}
			CombFilter[] array3 = cfsR;
			foreach (CombFilter combFilter2 in array3)
			{
				combFilter2.Process(input, outputRight);
			}
			AllPassFilter[] array4 = apfsR;
			foreach (AllPassFilter allPassFilter2 in array4)
			{
				allPassFilter2.Process(outputRight);
			}
			if ((double)(1f - wet1) > 0.001 || (double)wet2 > 0.001)
			{
				for (int m = 0; m < input.Length; m++)
				{
					float num = outputLeft[m];
					float num2 = outputRight[m];
					outputLeft[m] = num * wet1 + num2 * wet2;
					outputRight[m] = num2 * wet1 + num * wet2;
				}
			}
		}

		public void Mute()
		{
			CombFilter[] array = cfsL;
			foreach (CombFilter combFilter in array)
			{
				combFilter.Mute();
			}
			CombFilter[] array2 = cfsR;
			foreach (CombFilter combFilter2 in array2)
			{
				combFilter2.Mute();
			}
			AllPassFilter[] array3 = apfsL;
			foreach (AllPassFilter allPassFilter in array3)
			{
				allPassFilter.Mute();
			}
			AllPassFilter[] array4 = apfsR;
			foreach (AllPassFilter allPassFilter2 in array4)
			{
				allPassFilter2.Mute();
			}
		}

		private void Update()
		{
			wet1 = wet * (width / 2f + 0.5f);
			wet2 = wet * ((1f - width) / 2f);
			roomSize1 = roomSize;
			damp1 = damp;
			gain = 0.015f;
			CombFilter[] array = cfsL;
			foreach (CombFilter combFilter in array)
			{
				combFilter.Feedback = roomSize1;
				combFilter.Damp = damp1;
			}
			CombFilter[] array2 = cfsR;
			foreach (CombFilter combFilter2 in array2)
			{
				combFilter2.Feedback = roomSize1;
				combFilter2.Damp = damp1;
			}
		}
	}
	public sealed class SampleHeader
	{
		internal static readonly SampleHeader Default = new SampleHeader();

		private readonly string name;

		private readonly int start;

		private readonly int end;

		private readonly int startLoop;

		private readonly int endLoop;

		private readonly int sampleRate;

		private readonly byte originalPitch;

		private readonly sbyte pitchCorrection;

		private readonly ushort link;

		private readonly SampleType type;

		public string Name => name;

		public int Start => start;

		public int End => end;

		public int StartLoop => startLoop;

		public int EndLoop => endLoop;

		public int SampleRate => sampleRate;

		public byte OriginalPitch => originalPitch;

		public sbyte PitchCorrection => pitchCorrection;

		private SampleHeader()
		{
			name = "Default";
		}

		private SampleHeader(BinaryReader reader)
		{
			name = reader.ReadFixedLengthString(20);
			start = reader.ReadInt32();
			end = reader.ReadInt32();
			startLoop = reader.ReadInt32();
			endLoop = reader.ReadInt32();
			sampleRate = reader.ReadInt32();
			originalPitch = reader.ReadByte();
			pitchCorrection = reader.ReadSByte();
			link = reader.ReadUInt16();
			type = (SampleType)reader.ReadUInt16();
		}

		internal static SampleHeader[] ReadFromChunk(BinaryReader reader, int size)
		{
			if (size % 46 != 0)
			{
				throw new InvalidDataException("The sample header list is invalid.");
			}
			SampleHeader[] array = new SampleHeader[size / 46 - 1];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = new SampleHeader(reader);
			}
			new SampleHeader(reader);
			return array;
		}

		public override string ToString()
		{
			return name;
		}
	}
	internal enum SampleType
	{
		Mono = 1,
		Right = 2,
		Left = 4,
		Linked = 8,
		RomMono = 32769,
		RomRight = 32770,
		RomLeft = 32772,
		RomLinked = 32776
	}
	public sealed class SoundFont
	{
		private SoundFontInfo info;

		private int bitsPerSample;

		private short[] waveData;

		private SampleHeader[] sampleHeaders;

		private Preset[] presets;

		private Instrument[] instruments;

		public SoundFontInfo Info => info;

		public int BitsPerSample => bitsPerSample;

		public ReadOnlySpan<short> WaveData => waveData;

		public IReadOnlyList<SampleHeader> SampleHeaders => sampleHeaders;

		public IReadOnlyList<Preset> Presets => presets;

		public IReadOnlyList<Instrument> Instruments => instruments;

		internal short[] WaveDataArray => waveData;

		internal SampleHeader[] SampleHeaderArray => sampleHeaders;

		internal Preset[] PresetArray => presets;

		internal Instrument[] InstrumentArray => instruments;

		public SoundFont(Stream stream)
		{
			if (stream == null)
			{
				throw new ArgumentNullException("stream");
			}
			Load(stream);
			Debug.Assert(info != null);
			Debug.Assert(waveData != null);
			Debug.Assert(sampleHeaders != null);
			Debug.Assert(presets != null);
			Debug.Assert(instruments != null);
		}

		public SoundFont(string path)
		{
			if (path == null)
			{
				throw new ArgumentNullException("path");
			}
			using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
			{
				Load(stream);
			}
			Debug.Assert(info != null);
			Debug.Assert(waveData != null);
			Debug.Assert(sampleHeaders != null);
			Debug.Assert(presets != null);
			Debug.Assert(instruments != null);
		}

		private void Load(Stream stream)
		{
			using (BinaryReader binaryReader = new BinaryReader(stream, Encoding.ASCII, leaveOpen: true))
			{
				string text = binaryReader.ReadFourCC();
				if (text != "RIFF")
				{
					throw new InvalidDataException("The RIFF chunk was not found.");
				}
				int num = binaryReader.ReadInt32();
				string text2 = binaryReader.ReadFourCC();
				if (text2 != "sfbk")
				{
					throw new InvalidDataException("The type of the RIFF chunk must be 'sfbk', but was '" + text2 + "'.");
				}
				info = new SoundFontInfo(binaryReader);
				SoundFontSampleData soundFontSampleData = new SoundFontSampleData(binaryReader);
				bitsPerSample = soundFontSampleData.BitsPerSample;
				waveData = soundFontSampleData.Samples;
				SoundFontParameters soundFontParameters = new SoundFontParameters(binaryReader);
				sampleHeaders = soundFontParameters.SampleHeaders;
				presets = soundFontParameters.Presets;
				instruments = soundFontParameters.Instruments;
			}
			CheckSamples();
			CheckRegions();
		}

		public override string ToString()
		{
			return info.BankName;
		}

		private void CheckSamples()
		{
			int num = waveData.Length - 4;
			SampleHeader[] array = sampleHeaders;
			foreach (SampleHeader sampleHeader in array)
			{
				if (0 > sampleHeader.Start || sampleHeader.Start >= num)
				{
					throw new InvalidDataException("The start position of the sample '" + sampleHeader.Name + "' is out of range.");
				}
				if (0 > sampleHeader.StartLoop || sampleHeader.StartLoop >= num)
				{
					throw new InvalidDataException("The loop start position of the sample '" + sampleHeader.Name + "' is out of range.");
				}
				if (0 >= sampleHeader.End || sampleHeader.End > num)
				{
					throw new InvalidDataException("The end position of the sample '" + sampleHeader.Name + "' is out of range.");
				}
				if (0 > sampleHeader.EndLoop || sampleHeader.EndLoop > num)
				{
					throw new InvalidDataException("The loop end position of the sample '" + sampleHeader.Name + "' is out of range.");
				}
			}
		}

		private void CheckRegions()
		{
			int num = waveData.Length - 4;
			Instrument[] array = instruments;
			foreach (Instrument instrument in array)
			{
				InstrumentRegion[] regionArray = instrument.RegionArray;
				foreach (InstrumentRegion instrumentRegion in regionArray)
				{
					if (0 > instrumentRegion.SampleStart || instrumentRegion.SampleStart >= num)
					{
						throw new InvalidDataException($"The start position of the sample '{instrumentRegion.Sample.Name}' in the instrument '{instrument.Name}' is out of range.");
					}
					if (0 > instrumentRegion.SampleStartLoop || instrumentRegion.SampleStartLoop >= num)
					{
						throw new InvalidDataException($"The loop start position of the sample '{instrumentRegion.Sample.Name}' in the instrument '{instrument.Name}' is out of range.");
					}
					if (0 >= instrumentRegion.SampleEnd || instrumentRegion.SampleEnd > num)
					{
						throw new InvalidDataException($"The end position of the sample '{instrumentRegion.Sample.Name}' in the instrument '{instrument.Name}' is out of range.");
					}
					if (0 > instrumentRegion.SampleEndLoop || instrumentRegion.SampleEndLoop > num)
					{
						throw new InvalidDataException($"The loop end position of the sample '{instrumentRegion.Sample.Name}' in the instrument '{instrument.Name}' is out of range.");
					}
					LoopMode sampleModes = instrumentRegion.SampleModes;
					LoopMode loopMode = sampleModes;
					if ((uint)loopMode > 1u && loopMode != LoopMode.LoopUntilNoteOff)
					{
						throw new InvalidDataException($"The sample '{instrumentRegion.Sample.Name}' in the instrument '{instrument.Name}' has an invalid loop mode.");
					}
				}
			}
		}
	}
	public sealed class SoundFontInfo
	{
		private readonly SoundFontVersion version = default(SoundFontVersion);

		private readonly string targetSoundEngine = string.Empty;

		private readonly string bankName = string.Empty;

		private readonly string romName = string.Empty;

		private readonly SoundFontVersion romVersion = default(SoundFontVersion);

		private readonly string creationDate = string.Empty;

		private readonly string author = string.Empty;

		private readonly string targetProduct = string.Empty;

		private readonly string copyright = string.Empty;

		private readonly string comments = string.Empty;

		private readonly string tools = string.Empty;

		public SoundFontVersion Version => version;

		public string TargetSoundEngine => targetSoundEngine;

		public string BankName => bankName;

		public string RomName => romName;

		public SoundFontVersion RomVersion => romVersion;

		public string CeationDate => creationDate;

		public string Author => author;

		public string TargetProduct => targetProduct;

		public string Copyright => copyright;

		public string Comments => comments;

		public string Tools => tools;

		internal SoundFontInfo(BinaryReader reader)
		{
			string text = reader.ReadFourCC();
			if (text != "LIST")
			{
				throw new InvalidDataException("The LIST chunk was not found.");
			}
			long num = reader.BaseStream.Position + reader.ReadInt32();
			string text2 = reader.ReadFourCC();
			if (text2 != "INFO")
			{
				throw new InvalidDataException("The type of the LIST chunk must be 'INFO', but was '" + text2 + "'.");
			}
			while (reader.BaseStream.Position < num)
			{
				string text3 = reader.ReadFourCC();
				int length = reader.ReadInt32();
				switch (text3)
				{
				case "ifil":
					version = new SoundFontVersion(reader.ReadInt16(), reader.ReadInt16());
					break;
				case "isng":
					targetSoundEngine = reader.ReadFixedLengthString(length);
					break;
				case "INAM":
					bankName = reader.ReadFixedLengthString(length);
					break;
				case "irom":
					romName = reader.ReadFixedLengthString(length);
					break;
				case "iver":
					romVersion = new SoundFontVersion(reader.ReadInt16(), reader.ReadInt16());
					break;
				case "ICRD":
					creationDate = reader.ReadFixedLengthString(length);
					break;
				case "IENG":
					author = reader.ReadFixedLengthString(length);
					break;
				case "IPRD":
					targetProduct = reader.ReadFixedLengthString(length);
					break;
				case "ICOP":
					copyright = reader.ReadFixedLengthString(length);
					break;
				case "ICMT":
					comments = reader.ReadFixedLengthString(length);
					break;
				case "ISFT":
					tools = reader.ReadFixedLengthString(length);
					break;
				default:
					throw new InvalidDataException("The INFO list contains an unknown ID '" + text3 + "'.");
				}
			}
		}

		public override string ToString()
		{
			return bankName;
		}
	}
	internal static class SoundFontMath
	{
		public const float HalfPi = (float)Math.PI / 2f;

		public static readonly float NonAudible = 0.001f;

		private static readonly double logNonAudible = Math.Log(0.001);

		public static float TimecentsToSeconds(float x)
		{
			return Mathf.Pow(2f, 0.00083333335f * x);
		}

		public static float CentsToHertz(float x)
		{
			return 8.176f * Mathf.Pow(2f, 0.00083333335f * x);
		}

		public static float CentsToMultiplyingFactor(float x)
		{
			return Mathf.Pow(2f, 0.00083333335f * x);
		}

		public static float DecibelsToLinear(float x)
		{
			return Mathf.Pow(10f, 0.05f * x);
		}

		public static float LinearToDecibels(float x)
		{
			return 20f * Mathf.Log10(x);
		}

		public static float KeyNumberToMultiplyingFactor(int cents, int key)
		{
			return TimecentsToSeconds(cents * (60 - key));
		}

		public static double ExpCutoff(double x)
		{
			if (x < logNonAudible)
			{
				return 0.0;
			}
			return Math.Exp(x);
		}

		public static float Clamp(float value, float min, float max)
		{
			if (value < min)
			{
				return min;
			}
			if (value > max)
			{
				return max;
			}
			return value;
		}
	}
	internal sealed class SoundFontParameters
	{
		private readonly SampleHeader[] sampleHeaders;

		private readonly Preset[] presets;

		private readonly Instrument[] instruments;

		public SampleHeader[] SampleHeaders => sampleHeaders;

		public Preset[] Presets => presets;

		public Instrument[] Instruments => instruments;

		internal SoundFontParameters(BinaryReader reader)
		{
			string text = reader.ReadFourCC();
			if (text != "LIST")
			{
				throw new InvalidDataException("The LIST chunk was not found.");
			}
			long num = reader.BaseStream.Position + reader.ReadInt32();
			string text2 = reader.ReadFourCC();
			if (text2 != "pdta")
			{
				throw new InvalidDataException("The type of the LIST chunk must be 'pdta', but was '" + text2 + "'.");
			}
			PresetInfo[] array = null;
			ZoneInfo[] array2 = null;
			Generator[] array3 = null;
			InstrumentInfo[] array4 = null;
			ZoneInfo[] array5 = null;
			Generator[] array6 = null;
			while (reader.BaseStream.Position < num)
			{
				string text3 = reader.ReadFourCC();
				int size = reader.ReadInt32();
				switch (text3)
				{
				case "phdr":
					array = PresetInfo.ReadFromChunk(reader, size);
					break;
				case "pbag":
					array2 = ZoneInfo.ReadFromChunk(reader, size);
					break;
				case "pmod":
					Modulator.DiscardData(reader, size);
					break;
				case "pgen":
					array3 = Generator.ReadFromChunk(reader, size);
					break;
				case "inst":
					array4 = InstrumentInfo.ReadFromChunk(reader, size);
					break;
				case "ibag":
					array5 = ZoneInfo.ReadFromChunk(reader, size);
					break;
				case "imod":
					Modulator.DiscardData(reader, size);
					break;
				case "igen":
					array6 = Generator.ReadFromChunk(reader, size);
					break;
				case "shdr":
					sampleHeaders = SampleHeader.ReadFromChunk(reader, size);
					break;
				default:
					throw new InvalidDataException("The INFO list contains an unknown ID '" + text3 + "'.");
				}
			}
			if (array == null)
			{
				throw new InvalidDataException("The PHDR sub-chunk was not found.");
			}
			if (array2 == null)
			{
				throw new InvalidDataException("The PBAG sub-chunk was not found.");
			}
			if (array3 == null)
			{
				throw new InvalidDataException("The PGEN sub-chunk was not found.");
			}
			if (array4 == null)
			{
				throw new InvalidDataException("The INST sub-chunk was not found.");
			}
			if (array5 == null)
			{
				throw new InvalidDataException("The IBAG sub-chunk was not found.");
			}
			if (array6 == null)
			{
				throw new InvalidDataException("The IGEN sub-chunk was not found.");
			}
			if (sampleHeaders == null)
			{
				throw new InvalidDataException("The SHDR sub-chunk was not found.");
			}
			Zone[] zones = Zone.Create(array5, array6);
			instruments = Instrument.Create(array4, zones, sampleHeaders);
			Zone[] zones2 = Zone.Create(array2, array3);
			presets = Preset.Create(array, zones2, instruments);
		}
	}
	internal sealed class SoundFontSampleData
	{
		private readonly int bitsPerSample;

		private readonly short[] samples;

		public int BitsPerSample => bitsPerSample;

		public short[] Samples => samples;

		internal SoundFontSampleData(BinaryReader reader)
		{
			string text = reader.ReadFourCC();
			if (text != "LIST")
			{
				throw new InvalidDataException("The LIST chunk was not found.");
			}
			long num = reader.BaseStream.Position + reader.ReadInt32();
			string text2 = reader.ReadFourCC();
			if (text2 != "sdta")
			{
				throw new InvalidDataException("The type of the LIST chunk must be 'sdta', but was '" + text2 + "'.");
			}
			while (reader.BaseStream.Position < num)
			{
				string text3 = reader.ReadFourCC();
				int num2 = reader.ReadInt32();
				string text4 = text3;
				string text5 = text4;
				if (!(text5 == "smpl"))
				{
					if (text5 == "sm24")
					{
						reader.BaseStream.Position += num2;
						continue;
					}
					throw new InvalidDataException("The INFO list contains an unknown ID '" + text3 + "'.");
				}
				bitsPerSample = 16;
				samples = new short[num2 / 2];
				for (int i = 0; i < samples.Length; i++)
				{
					samples[i] = reader.ReadInt16();
				}
			}
			if (samples == null)
			{
				throw new InvalidDataException("No valid sample data was found.");
			}
			if (!BitConverter.IsLittleEndian)
			{
				throw new NotSupportedException("Big endian architectures are not yet supported.");
			}
		}
	}
	public struct SoundFontVersion
	{
		private readonly short major;

		private readonly short minor;

		public short Major => major;

		public short Minor => minor;

		internal SoundFontVersion(short major, short minor)
		{
			this.major = major;
			this.minor = minor;
		}

		public override string ToString()
		{
			return $"{major}.{minor}";
		}
	}
	public sealed class Synthesizer : IAudioRenderer
	{
		private static readonly int channelCount = 16;

		private static readonly int percussionChannel = 9;

		private readonly SoundFont soundFont;

		private readonly int sampleRate;

		private readonly int blockSize;

		private readonly int maximumPolyphony;

		private readonly bool enableReverbAndChorus;

		private readonly int minimumVoiceDuration;

		private readonly Dictionary<int, Preset> presetLookup;

		private readonly Preset defaultPreset;

		private readonly Channel[] channels;

		private readonly VoiceCollection voices;

		private readonly float[] blockLeft;

		private readonly float[] blockRight;

		private readonly float inverseBlockSize;

		private int blockRead;

		private float masterVolume;

		private Reverb reverb;

		private float[] reverbInput;

		private float[] reverbOutputLeft;

		private float[] reverbOutputRight;

		private Chorus chorus;

		private float[] chorusInputLeft;

		private float[] chorusInputRight;

		private float[] chorusOutputLeft;

		private float[] chorusOutputRight;

		public int BlockSize => blockSize;

		public int MaximumPolyphony => maximumPolyphony;

		public int ChannelCount => channelCount;

		public int PercussionChannel => percussionChannel;

		public SoundFont SoundFont => soundFont;

		public int SampleRate => sampleRate;

		public int ActiveVoiceCount => voices.ActiveVoiceCount;

		public float MasterVolume
		{
			get
			{
				return masterVolume;
			}
			set
			{
				masterVolume = value;
			}
		}

		internal int MinimumVoiceDuration => minimumVoiceDuration;

		internal Channel[] Channels => channels;

		public Synthesizer(string soundFontPath, int sampleRate)
			: this(new SoundFont(soundFontPath), new SynthesizerSettings(sampleRate))
		{
		}

		public Synthesizer(SoundFont soundFont, int sampleRate)
			: this(soundFont, new SynthesizerSettings(sampleRate))
		{
		}

		public Synthesizer(string soundFontPath, SynthesizerSettings settings)
			: this(new SoundFont(soundFontPath), settings)
		{
		}

		public Synthesizer(SoundFont soundFont, SynthesizerSettings settings)
		{
			if (soundFont == null)
			{
				throw new ArgumentNullException("soundFont");
			}
			if (settings == null)
			{
				throw new ArgumentNullException("settings");
			}
			this.soundFont = soundFont;
			sampleRate = settings.SampleRate;
			blockSize = settings.BlockSize;
			maximumPolyphony = settings.MaximumPolyphony;
			enableReverbAndChorus = settings.EnableReverbAndChorus;
			minimumVoiceDuration = sampleRate / 500;
			presetLookup = new Dictionary<int, Preset>();
			int num = int.MaxValue;
			Preset[] presetArray = soundFont.PresetArray;
			foreach (Preset preset in presetArray)
			{
				int num2 = (preset.BankNumber << 16) | preset.PatchNumber;
				if (!presetLookup.ContainsKey(num2))
				{
					presetLookup.Add(num2, preset);
				}
				if (num2 < num)
				{
					defaultPreset = preset;
					num = num2;
				}
			}
			Debug.Assert(defaultPreset != null);
			channels = new Channel[channelCount];
			for (int j = 0; j < channels.Length; j++)
			{
				channels[j] = new Channel(this, j == percussionChannel);
			}
			voices = new VoiceCollection(this, maximumPolyphony);
			blockLeft = new float[blockSize];
			blockRight = new float[blockSize];
			inverseBlockSize = 1f / (float)blockSize;
			blockRead = blockSize;
			masterVolume = 0.5f;
			if (enableReverbAndChorus)
			{
				reverb = new Reverb(sampleRate);
				reverbInput = new float[blockSize];
				reverbOutputLeft = new float[blockSize];
				reverbOutputRight = new float[blockSize];
				chorus = new Chorus(sampleRate, 0.002, 0.0019, 0.4);
				chorusInputLeft = new float[blockSize];
				chorusInputRight = new float[blockSize];
				chorusOutputLeft = new float[blockSize];
				chorusOutputRight = new float[blockSize];
			}
		}

		public void ProcessMidiMessage(int channel, int command, int data1, int data2)
		{
			if (0 > channel || channel >= channels.Length)
			{
				return;
			}
			Channel channel2 = channels[channel];
			switch (command)
			{
			case 128:
				NoteOff(channel, data1);
				break;
			case 144:
				NoteOn(channel, data1, data2);
				break;
			case 176:
				switch (data1)
				{
				case 0:
					channel2.SetBank(data2);
					break;
				case 1:
					channel2.SetModulationCoarse(data2);
					break;
				case 33:
					channel2.SetModulationFine(data2);
					break;
				case 6:
					channel2.DataEntryCoarse(data2);
					break;
				case 38:
					channel2.DataEntryFine(data2);
					break;
				case 7:
					channel2.SetVolumeCoarse(data2);
					break;
				case 39:
					channel2.SetVolumeFine(data2);
					break;
				case 10:
					channel2.SetPanCoarse(data2);
					break;
				case 42:
					channel2.SetPanFine(data2);
					break;
				case 11:
					channel2.SetExpressionCoarse(data2);
					break;
				case 43:
					channel2.SetExpressionFine(data2);
					break;
				case 64:
					channel2.SetHoldPedal(data2);
					break;
				case 91:
					channel2.SetReverbSend(data2);
					break;
				case 93:
					channel2.SetChorusSend(data2);
					break;
				case 101:
					channel2.SetRpnCoarse(data2);
					break;
				case 100:
					channel2.SetRpnFine(data2);
					break;
				case 120:
					NoteOffAll(channel, immediate: true);
					break;
				case 121:
					ResetAllControllers(channel);
					break;
				case 123:
					NoteOffAll(channel, immediate: false);
					break;
				}
				break;
			case 192:
				channel2.SetPatch(data1);
				break;
			case 224:
				channel2.SetPitchBend(data1, data2);
				break;
			}
		}

		public void NoteOff(int channel, int key)
		{
			if (0 > channel || channel >= channels.Length)
			{
				return;
			}
			foreach (Voice voice in voices)
			{
				if (voice.Channel == channel && voice.Key == key)
				{
					voice.End();
				}
			}
		}

		public void NoteOn(int channel, int key, int velocity)
		{
			if (velocity == 0)
			{
				NoteOff(channel, key);
			}
			else
			{
				if (0 > channel || channel >= channels.Length)
				{
					return;
				}
				Channel channel2 = channels[channel];
				int key2 = (channel2.BankNumber << 16) | channel2.PatchNumber;
				if (!presetLookup.TryGetValue(key2, out var value))
				{
					int key3 = ((channel2.BankNumber < 128) ? channel2.PatchNumber : 8388608);
					if (!presetLookup.TryGetValue(key3, out value))
					{
						value = defaultPreset;
					}
				}
				PresetRegion[] regionArray = value.RegionArray;
				foreach (PresetRegion presetRegion in regionArray)
				{
					if (!presetRegion.Contains(key, velocity))
					{
						continue;
					}
					InstrumentRegion[] regionArray2 = presetRegion.Instrument.RegionArray;
					foreach (InstrumentRegion instrumentRegion in regionArray2)
					{
						if (instrumentRegion.Contains(key, velocity))
						{
							RegionPair region = new RegionPair(presetRegion, instrumentRegion);
							voices.RequestNew(instrumentRegion, channel)?.Start(region, channel, key, velocity);
						}
					}
				}
			}
		}

		public void NoteOffAll(bool immediate)
		{
			if (immediate)
			{
				voices.Clear();
				return;
			}
			foreach (Voice voice in voices)
			{
				voice.End();
			}
		}

		public void NoteOffAll(int channel, bool immediate)
		{
			if (immediate)
			{
				foreach (Voice voice in voices)
				{
					if (voice.Channel == channel)
					{
						voice.Kill();
					}
				}
				return;
			}
			foreach (Voice voice2 in voices)
			{
				if (voice2.Channel == channel)
				{
					voice2.End();
				}
			}
		}

		public void ResetAllControllers()
		{
			Channel[] array = channels;
			foreach (Channel channel in array)
			{
				channel.ResetAllControllers();
			}
		}

		public void ResetAllControllers(int channel)
		{
			if (0 <= channel && channel < channels.Length)
			{
				channels[channel].ResetAllControllers();
			}
		}

		public void Reset()
		{
			voices.Clear();
			Channel[] array = channels;
			foreach (Channel channel in array)
			{
				channel.Reset();
			}
			if (enableReverbAndChorus)
			{
				reverb.Mute();
				chorus.Mute();
			}
			blockRead = blockSize;
		}

		public void Render(Span<float> left, Span<float> right)
		{
			if (left.Length != right.Length)
			{
				throw new ArgumentException("The output buffers for the left and right must be the same length.");
			}
			int num;
			for (int i = 0; i < left.Length; i += num)
			{
				if (blockRead == blockSize)
				{
					RenderBlock();
					blockRead = 0;
				}
				int val = blockSize - blockRead;
				int val2 = left.Length - i;
				num = Math.Min(val, val2);
				new Span<float>(blockLeft, blockRead, num).CopyTo(left.Slice(i, num));
				new Span<float>(blockRight, blockRead, num).CopyTo(right.Slice(i, num));
				blockRead += num;
			}
		}

		private void RenderBlock()
		{
			voices.Process();
			Array.Clear(blockLeft, 0, blockLeft.Length);
			Array.Clear(blockRight, 0, blockRight.Length);
			foreach (Voice voice in voices)
			{
				float previousGain = masterVolume * voice.PreviousMixGainLeft;
				float currentGain = masterVolume * voice.CurrentMixGainLeft;
				WriteBlock(previousGain, currentGain, voice.Block, blockLeft);
				float previousGain2 = masterVolume * voice.PreviousMixGainRight;
				float currentGain2 = masterVolume * voice.CurrentMixGainRight;
				WriteBlock(previousGain2, currentGain2, voice.Block, blockRight);
			}
			if (!enableReverbAndChorus)
			{
				return;
			}
			Array.Clear(chorusInputLeft, 0, chorusInputLeft.Length);
			Array.Clear(chorusInputRight, 0, chorusInputRight.Length);
			foreach (Voice voice2 in voices)
			{
				float previousGain3 = voice2.PreviousChorusSend * voice2.PreviousMixGainLeft;
				float currentGain3 = voice2.CurrentChorusSend * voice2.CurrentMixGainLeft;
				WriteBlock(previousGain3, currentGain3, voice2.Block, chorusInputLeft);
				float previousGain4 = voice2.PreviousChorusSend * voice2.PreviousMixGainRight;
				float currentGain4 = voice2.CurrentChorusSend * voice2.CurrentMixGainRight;
				WriteBlock(previousGain4, currentGain4, voice2.Block, chorusInputRight);
			}
			chorus.Process(chorusInputLeft, chorusInputRight, chorusOutputLeft, chorusOutputRight);
			ArrayMath.MultiplyAdd(masterVolume, chorusOutputLeft, blockLeft);
			ArrayMath.MultiplyAdd(masterVolume, chorusOutputRight, blockRight);
			Array.Clear(reverbInput, 0, reverbInput.Length);
			foreach (Voice voice3 in voices)
			{
				float previousGain5 = reverb.InputGain * voice3.PreviousReverbSend * (voice3.PreviousMixGainLeft + voice3.PreviousMixGainRight);
				float currentGain5 = reverb.InputGain * voice3.CurrentReverbSend * (voice3.CurrentMixGainLeft + voice3.C