Decompiled source of HowToChonk v0.2.0

plugins/ArceDev.HowToChonk.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using MetaVoiceChat.Output.AudioSource;
using Microsoft.CodeAnalysis;
using NWaves.Effects;
using NWaves.Filters.Base;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ArceDev.HowToChonk")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.2.0.0")]
[assembly: AssemblyInformationalVersion("0.2.0")]
[assembly: AssemblyProduct("ArceDev.HowToChonk")]
[assembly: AssemblyTitle("HowToChonk")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.2.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace Microsoft.CodeAnalysis
{
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace HowToChonk
{
	internal static class BodyMassSystem
	{
		private const float BroadcastInterval = 0.25f;

		private static readonly Dictionary<int, float> BodyWeight = new Dictionary<int, float>();

		private static float _nextBroadcastAt;

		internal static void Consume(Player player, Creature creature)
		{
			if (InstanceFinder.IsServerStarted && Object.op_Implicit((Object)(object)player) && !((NetworkBehaviour)player).IsDeinitializing && Object.op_Implicit((Object)(object)creature) && !((NetworkBehaviour)creature).IsDeinitializing)
			{
				int num = (int)((float)creature.FullnessToRestore * GameInfo.CooknessEatCurve.Evaluate(((Item)creature).Cookness));
				float num2 = Mathf.Max(0f, ((Item)creature)._weight * ((Item)creature).RandomizedWeight);
				float num3 = BodyMassRules.ExcessWeightGain(player.Vitals.Fullness, num, num2, Plugin.WeightGainMultiplier);
				if (!(num3 <= 0f))
				{
					float num4 = Current(player) + num3;
					BodyWeight[((NetworkBehaviour)player).ObjectId] = Mathf.Clamp(num4, 0f - Plugin.MaximumThinWeight, Plugin.MaximumFatWeight);
					Plugin.Log.LogInfo((object)($"{player.SteamName} gained {num3:0.###} body weight from {num2:0.###} kg of food " + $"at {player.Vitals.Fullness}/100 fullness (+{num})."));
				}
			}
		}

		internal static void UpdateServer()
		{
			if (!InstanceFinder.IsServerStarted)
			{
				return;
			}
			foreach (Player player in PlayerManager.Players)
			{
				UpdatePlayer(player);
			}
			if (Time.time < _nextBroadcastAt)
			{
				return;
			}
			_nextBroadcastAt = Time.time + 0.25f;
			foreach (Player player2 in PlayerManager.Players)
			{
				BodyNetwork.Send(player2, Shape(player2));
			}
		}

		internal static void Remove(Player player)
		{
			BodyWeight.Remove(((NetworkBehaviour)player).ObjectId);
		}

		private static void UpdatePlayer(Player player)
		{
			if (Object.op_Implicit((Object)(object)player) && !((NetworkBehaviour)player).IsDeinitializing && !player.Dying.IsDead)
			{
				BodyWeight[((NetworkBehaviour)player).ObjectId] = BodyMassRules.Update(Current(player), player.Vitals.Fullness, Time.deltaTime, Plugin.NaturalFatLossPerMinute, Plugin.StarvationLossPerMinute, Plugin.CriticalFullness, Plugin.MaximumThinWeight);
			}
		}

		private static float Current(Player player)
		{
			if (!BodyWeight.TryGetValue(((NetworkBehaviour)player).ObjectId, out var value))
			{
				return 0f;
			}
			return value;
		}

		private static float Shape(Player player)
		{
			float num = Current(player);
			float num2 = ((num >= 0f) ? Plugin.MaximumFatWeight : Plugin.MaximumThinWeight);
			return Mathf.Clamp(num / num2, -1f, 1f);
		}
	}
	internal static class BodyMassRules
	{
		internal static float ExcessWeightGain(int fullness, int fullnessGain, float creatureWeight, float multiplier)
		{
			if (fullnessGain <= 0 || creatureWeight <= 0f || multiplier <= 0f)
			{
				return 0f;
			}
			int num = Mathf.Max(0, 100 - fullness);
			int num2 = Mathf.Max(0, fullnessGain - num);
			return creatureWeight * (float)num2 / (float)fullnessGain * multiplier;
		}

		internal static float Update(float bodyWeight, int fullness, float deltaTime, float fatLossPerMinute, float starvationLossPerMinute, int criticalFullness, float maximumThinWeight)
		{
			if (bodyWeight > 0f)
			{
				return Mathf.MoveTowards(bodyWeight, 0f, fatLossPerMinute / 60f * deltaTime);
			}
			if (fullness >= criticalFullness)
			{
				return bodyWeight;
			}
			float num = (float)(criticalFullness - fullness) / (float)criticalFullness;
			float num2 = starvationLossPerMinute / 60f * num * deltaTime;
			return Mathf.Max(0f - maximumThinWeight, bodyWeight - num2);
		}

		internal static void Validate()
		{
			if (!Mathf.Approximately(ExcessWeightGain(80, 20, 10f, 1f), 0f) || !Mathf.Approximately(ExcessWeightGain(90, 20, 10f, 1f), 5f) || !Mathf.Approximately(ExcessWeightGain(100, 20, 10f, 1f), 10f) || !Mathf.Approximately(Update(10f, 100, 60f, 2f, 2f, 50, 20f), 8f) || !Mathf.Approximately(Update(0f, 25, 60f, 2f, 2f, 50, 20f), -1f) || !Mathf.Approximately(Update(-5f, 75, 60f, 2f, 2f, 50, 20f), -5f))
			{
				throw new InvalidOperationException("Body mass rule validation failed.");
			}
		}
	}
	internal readonly record struct BodyShapeBroadcast(int PlayerObjectId, float Shape) : IBroadcast;
	internal static class BodyNetwork
	{
		private static readonly Action<BodyShapeBroadcast, Channel> Handler = OnBodyShape;

		private static readonly Dictionary<int, float> Shapes = new Dictionary<int, float>();

		private static bool _clientRegistered;

		internal static void Initialize()
		{
			GenericWriter<BodyShapeBroadcast>.SetWrite((Action<Writer, BodyShapeBroadcast>)delegate(Writer writer, BodyShapeBroadcast message)
			{
				writer.WriteInt32(message.PlayerObjectId);
				writer.WriteSingle(message.Shape);
			});
			GenericReader<BodyShapeBroadcast>.SetRead((Func<Reader, BodyShapeBroadcast>)((Reader reader) => new BodyShapeBroadcast(reader.ReadInt32(), reader.ReadSingle())));
		}

		internal static void RegisterClient()
		{
			if (!_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
			{
				InstanceFinder.ClientManager.RegisterBroadcast<BodyShapeBroadcast>(Handler);
				_clientRegistered = true;
			}
		}

		internal static void Send(Player player, float shape)
		{
			if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)InstanceFinder.ServerManager))
			{
				return;
			}
			BodyShapeBroadcast bodyShapeBroadcast = new BodyShapeBroadcast(((NetworkBehaviour)player).ObjectId, Mathf.Clamp(shape, -1f, 1f));
			foreach (NetworkConnection value in InstanceFinder.ServerManager.Clients.Values)
			{
				if (value.IsAuthenticated)
				{
					InstanceFinder.ServerManager.Broadcast<BodyShapeBroadcast>(value, bodyShapeBroadcast, true, (Channel)0);
				}
			}
		}

		internal static float ShapeFor(Player player)
		{
			if (!Object.op_Implicit((Object)(object)player) || !Shapes.TryGetValue(((NetworkBehaviour)player).ObjectId, out var value))
			{
				return 0f;
			}
			return value;
		}

		internal static void Remove(Player player)
		{
			Shapes.Remove(((NetworkBehaviour)player).ObjectId);
		}

		private static void OnBodyShape(BodyShapeBroadcast message, Channel channel)
		{
			Shapes[message.PlayerObjectId] = Mathf.Clamp(message.Shape, -1f, 1f);
		}
	}
	[DefaultExecutionOrder(10000)]
	internal sealed class BodyShapeController : MonoBehaviour
	{
		private readonly List<BoneBinding> _bones = new List<BoneBinding>();

		private BeanVisualBinding? _beanVisual;

		private Player _player;

		private float _currentShape;

		internal static void Attach(Player player)
		{
			BodyShapeController bodyShapeController = ((Component)player).GetComponent<BodyShapeController>() ?? ((Component)player).gameObject.AddComponent<BodyShapeController>();
			bodyShapeController._player = player;
			bodyShapeController.CaptureBones();
		}

		internal static void Refresh(Player player)
		{
			BodyShapeController component = ((Component)player).GetComponent<BodyShapeController>();
			if (Object.op_Implicit((Object)(object)component))
			{
				component.CaptureBones();
			}
		}

		private void OnDisable()
		{
			RestoreBones();
		}

		private void LateUpdate()
		{
			_currentShape = Mathf.MoveTowards(_currentShape, Plugin.ShapeFor(_player), Time.deltaTime * 1.5f);
			foreach (BoneBinding bone in _bones)
			{
				bone.Apply(_currentShape);
			}
			_beanVisual?.Apply(_currentShape);
		}

		private void CaptureBones()
		{
			RestoreBones();
			_bones.Clear();
			_beanVisual = null;
			Transform[] array = (from bone in ((Component)this).GetComponentsInChildren<SkinnedMeshRenderer>(true).SelectMany((SkinnedMeshRenderer renderer) => renderer.bones)
				where Object.op_Implicit((Object)(object)bone)
				select bone).Distinct().ToArray();
			Transform[] array2 = array;
			foreach (Transform val in array2)
			{
				if (BodyShapeProfile.TryClassify(((Object)val).name, out var region))
				{
					_bones.Add(new BoneBinding(val, region));
				}
			}
			Player player = _player;
			PlayerBody val2 = ((player != null) ? player.Body : null);
			if (val2 != null && val2._isOldModel)
			{
				_beanVisual = new BeanVisualBinding(val2);
			}
		}

		private void RestoreBones()
		{
			_beanVisual?.Restore();
			foreach (BoneBinding bone in _bones)
			{
				bone.Restore();
			}
		}
	}
	internal sealed class BeanVisualBinding
	{
		private readonly Transform _body;

		private readonly Transform _head;

		private readonly Transform _nameCanvas;

		private readonly Vector3 _bodyScale;

		private readonly Vector3 _headScale;

		private readonly Vector3 _nameCanvasScale;

		private readonly bool _headInheritsBodyScale;

		internal BeanVisualBinding(PlayerBody playerBody)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			_body = playerBody._oldCharacter.transform;
			_head = playerBody._oldHead.transform;
			_nameCanvas = playerBody._nameTextCanvasHolder;
			_bodyScale = _body.localScale;
			_headScale = _head.localScale;
			_nameCanvasScale = _nameCanvas.localScale;
			_headInheritsBodyScale = _head.IsChildOf(_body);
		}

		internal void Apply(float shape)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			float num = BodyShapeProfile.RadialMultiplier(BoneRegion.Torso, shape);
			float num2 = BodyShapeProfile.RadialMultiplier(BoneRegion.Head, shape);
			_body.localScale = RadialScale(_bodyScale, num);
			_head.localScale = RadialScale(_headScale, _headInheritsBodyScale ? (num2 / num) : num2);
			_nameCanvas.localScale = RadialScale(_nameCanvasScale, 1f / num);
		}

		internal void Restore()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_body))
			{
				_body.localScale = _bodyScale;
			}
			if (Object.op_Implicit((Object)(object)_head))
			{
				_head.localScale = _headScale;
			}
			if (Object.op_Implicit((Object)(object)_nameCanvas))
			{
				_nameCanvas.localScale = _nameCanvasScale;
			}
		}

		private static Vector3 RadialScale(Vector3 original, float multiplier)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			return Vector3.Scale(original, new Vector3(multiplier, 1f, multiplier));
		}
	}
	internal sealed class BoneBinding
	{
		private readonly Transform _bone;

		private readonly BoneRegion _region;

		private readonly Vector3 _originalScale;

		private readonly int _lengthAxis;

		internal BoneBinding(Transform bone, BoneRegion region)
		{
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			_bone = bone;
			_region = region;
			_originalScale = bone.localScale;
			_lengthAxis = LongestChildAxis(bone);
		}

		internal void Apply(float shape)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_bone))
			{
				float num = BodyShapeProfile.RadialMultiplier(_region, shape);
				Vector3 val = default(Vector3);
				((Vector3)(ref val))..ctor(num, num, num);
				((Vector3)(ref val))[_lengthAxis] = 1f;
				_bone.localScale = Vector3.Scale(_originalScale, val);
			}
		}

		internal void Restore()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)_bone))
			{
				_bone.localScale = _originalScale;
			}
		}

		private static int LongestChildAxis(Transform bone)
		{
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_0087: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = (from index in Enumerable.Range(0, bone.childCount)
				select bone.GetChild(index).localPosition into position
				orderby ((Vector3)(ref position)).sqrMagnitude descending
				select position).FirstOrDefault();
			Vector3 val2 = default(Vector3);
			((Vector3)(ref val2))..ctor(Mathf.Abs(val.x), Mathf.Abs(val.y), Mathf.Abs(val.z));
			if (val2.x > val2.y && val2.x > val2.z)
			{
				return 0;
			}
			if (val2.z > val2.y)
			{
				return 2;
			}
			return 1;
		}
	}
	internal enum BoneRegion
	{
		Torso,
		UpperLimb,
		LowerLimb,
		Extremity,
		Head
	}
	internal static class BodyShapeProfile
	{
		internal static bool TryClassify(string name, out BoneRegion region)
		{
			string value = name.Replace("_", string.Empty).Replace(" ", string.Empty).ToLowerInvariant();
			if (ContainsAny(value, "armlower", "lowerarm", "forearm", "leglower", "lowerleg", "calf", "shin"))
			{
				region = BoneRegion.LowerLimb;
				return true;
			}
			if (ContainsAny(value, "armupper", "upperarm", "legupper", "upperleg", "thigh"))
			{
				region = BoneRegion.UpperLimb;
				return true;
			}
			if (ContainsAny(value, "hand", "foot"))
			{
				region = BoneRegion.Extremity;
				return true;
			}
			if (ContainsAny(value, "head", "neck"))
			{
				region = BoneRegion.Head;
				return true;
			}
			if (ContainsAny(value, "body", "spine", "chest", "torso", "hip", "pelvis", "belly"))
			{
				region = BoneRegion.Torso;
				return true;
			}
			region = BoneRegion.Torso;
			return false;
		}

		internal static float RadialMultiplier(BoneRegion region, float shape)
		{
			if (!(shape < 0f))
			{
				return ChonkMultiplier(region, shape);
			}
			return ThinMultiplier(region, 0f - shape);
		}

		private static float ChonkMultiplier(BoneRegion region, float shape)
		{
			return region switch
			{
				BoneRegion.Torso => 1f + 0.7f * Stage(shape, 0f, 0.5f), 
				BoneRegion.UpperLimb => 1f + 0.38f * Stage(shape, 0.2f, 0.8f), 
				BoneRegion.LowerLimb => 1f + 0.24f * Stage(shape, 0.45f, 1f), 
				BoneRegion.Extremity => 1f + 0.1f * Stage(shape, 0.7f, 1f), 
				BoneRegion.Head => 1f + 0.03f * Stage(shape, 0.9f, 1f), 
				_ => 1f, 
			};
		}

		private static float ThinMultiplier(BoneRegion region, float thin)
		{
			return region switch
			{
				BoneRegion.Torso => 1f - 0.28f * thin, 
				BoneRegion.UpperLimb => 1f - 0.18f * thin, 
				BoneRegion.LowerLimb => 1f - 0.12f * thin, 
				BoneRegion.Extremity => 1f - 0.04f * thin, 
				BoneRegion.Head => 1f - 0.01f * thin, 
				_ => 1f, 
			};
		}

		internal static void Validate()
		{
			if (Enum.GetValues(typeof(BoneRegion)).Cast<BoneRegion>().Any((BoneRegion region) => RadialMultiplier(region, 0f) != 1f) || RadialMultiplier(BoneRegion.UpperLimb, 0.1f) != 1f || RadialMultiplier(BoneRegion.LowerLimb, 0.4f) != 1f || RadialMultiplier(BoneRegion.Extremity, 0.6f) != 1f || !(RadialMultiplier(BoneRegion.Torso, 1f) > RadialMultiplier(BoneRegion.UpperLimb, 1f)) || !(RadialMultiplier(BoneRegion.UpperLimb, 1f) > RadialMultiplier(BoneRegion.LowerLimb, 1f)) || !(RadialMultiplier(BoneRegion.Torso, -1f) < RadialMultiplier(BoneRegion.UpperLimb, -1f)))
			{
				throw new InvalidOperationException("Body shape profile validation failed.");
			}
		}

		private static bool ContainsAny(string value, params string[] candidates)
		{
			return candidates.Any(value.Contains);
		}

		private static float Stage(float value, float start, float end)
		{
			return Mathf.SmoothStep(0f, 1f, Mathf.InverseLerp(start, end, value));
		}
	}
	internal sealed class PitchShiftProcessor
	{
		private const int FftSize = 1024;

		private const int HopSize = 128;

		private const float Smoothing = 0.001f;

		private const float BypassThreshold = 0.001f;

		private const float PeakLimit = 0.9f;

		private const float LimiterReleasePerBlock = 0.02f;

		private readonly int _sampleRate;

		private PitchShiftVocoderEffect[] _effects;

		private float _currentPitch = 1f;

		private float _wet;

		private float _limiterGain = 1f;

		private int _processedFrames;

		internal PitchShiftProcessor(int sampleRate)
		{
			_sampleRate = sampleRate;
			_effects = CreateEffects(2);
		}

		internal void Process(float[] data, int channels, float targetPitch)
		{
			if (channels <= 0 || data.Length % channels != 0)
			{
				return;
			}
			EnsureChannels(channels);
			targetPitch = Math.Clamp(targetPitch, 0.5f, 2f);
			for (int i = 0; i < data.Length; i += channels)
			{
				_currentPitch += (targetPitch - _currentPitch) * 0.001f;
				_processedFrames++;
				float num = ((_processedFrames >= 1024 && MathF.Abs(_currentPitch - 1f) >= 0.001f) ? 1f : 0f);
				_wet += (num - _wet) * 0.001f;
				float num2 = ((_currentPitch < 1f) ? (_currentPitch * _currentPitch * _currentPitch) : 1f);
				for (int j = 0; j < channels; j++)
				{
					PitchShiftVocoderEffect val = _effects[j];
					val.Shift = _currentPitch;
					float num3 = data[i + j];
					float num4 = ((OverlapAddFilter)val).Process(num3) * num2;
					data[i + j] = num3 + (num4 - num3) * _wet;
				}
			}
			LimitPeaks(data);
		}

		internal static void Validate()
		{
		}

		private void EnsureChannels(int channels)
		{
			if (_effects.Length < channels)
			{
				_effects = CreateEffects(channels);
			}
		}

		private PitchShiftVocoderEffect[] CreateEffects(int channels)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Expected O, but got Unknown
			PitchShiftVocoderEffect[] array = (PitchShiftVocoderEffect[])(object)new PitchShiftVocoderEffect[channels];
			for (int i = 0; i < channels; i++)
			{
				array[i] = new PitchShiftVocoderEffect(_sampleRate, 1.0, 1024, 128);
			}
			return array;
		}

		private void LimitPeaks(float[] data)
		{
			if (_wet <= 0.001f)
			{
				_limiterGain = 1f;
				return;
			}
			float num = 0f;
			for (int i = 0; i < data.Length; i++)
			{
				num = MathF.Max(num, MathF.Abs(data[i]));
			}
			float num2 = ((num > 0.9f) ? (0.9f / num) : 1f);
			_limiterGain = ((num2 < _limiterGain) ? num2 : MathF.Min(num2, _limiterGain + 0.02f));
			if (!(_limiterGain >= 1f))
			{
				for (int j = 0; j < data.Length; j++)
				{
					data[j] *= _limiterGain;
				}
			}
		}
	}
	[BepInPlugin("ArceDev.HowToChonk", "HowToChonk", "0.2.0")]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(Player), "OnStartClient")]
		private static class PlayerStartPatch
		{
			private static void Postfix(Player __instance)
			{
				BodyNetwork.RegisterClient();
				BodyShapeController.Attach(__instance);
			}
		}

		[HarmonyPatch(typeof(Player), "OnStopClient")]
		private static class PlayerStopPatch
		{
			private static void Prefix(Player __instance)
			{
				BodyMassSystem.Remove(__instance);
				BodyNetwork.Remove(__instance);
			}
		}

		[HarmonyPatch(typeof(Server), "RpcLogic___FinishEatingCreature___1039939981")]
		private static class FinishEatingPatch
		{
			private static void Prefix(Creature __0, Player __1)
			{
				BodyMassSystem.Consume(__1, __0);
			}
		}

		[HarmonyPatch(typeof(PlayerBody), "ToggleOldModel")]
		private static class PlayerModelPatch
		{
			private static void Postfix(Player ____player)
			{
				BodyShapeController.Refresh(____player);
			}
		}

		[HarmonyPatch(typeof(VcAudioSourceOutput), "Start")]
		private static class VoicePitchPatch
		{
			private static void Postfix(VcAudioSourceOutput __instance, Player ____player)
			{
				if (Object.op_Implicit((Object)(object)____player) && Object.op_Implicit((Object)(object)__instance.audioSource))
				{
					VoicePitchFilter.Attach(__instance.audioSource, ____player);
				}
			}
		}

		public const string Id = "ArceDev.HowToChonk";

		internal static ManualLogSource Log { get; private set; }

		internal static float WeightGainMultiplier => GainMultiplier.Value;

		internal static float NaturalFatLossPerMinute => FatLossPerMinute.Value;

		internal static float StarvationLossPerMinute => ThinLossPerMinute.Value;

		internal static float MaximumFatWeight => MaxFatWeight.Value;

		internal static float MaximumThinWeight => MaxThinWeight.Value;

		internal static int CriticalFullness => CriticalFullnessEntry.Value;

		private static ConfigEntry<float> GainMultiplier { get; set; }

		private static ConfigEntry<float> FatLossPerMinute { get; set; }

		private static ConfigEntry<float> ThinLossPerMinute { get; set; }

		private static ConfigEntry<float> MaxFatWeight { get; set; }

		private static ConfigEntry<float> MaxThinWeight { get; set; }

		private static ConfigEntry<int> CriticalFullnessEntry { get; set; }

		private static ConfigEntry<float> ChonkVoicePitch { get; set; }

		private static ConfigEntry<float> ThinVoicePitch { get; set; }

		public static string Name => "HowToChonk";

		public static string Version => "0.2.0";

		private void Awake()
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Expected O, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Expected O, but got Unknown
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Expected O, but got Unknown
			//IL_012d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0137: Expected O, but got Unknown
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_016a: Expected O, but got Unknown
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Expected O, but got Unknown
			//IL_01da: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e4: Expected O, but got Unknown
			Log = ((BaseUnityPlugin)this).Logger;
			GainMultiplier = ((BaseUnityPlugin)this).Config.Bind<float>("Body Weight", "GainMultiplier", 1f, new ConfigDescription("Multiplier applied to the excess fraction of a creature's real weight.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			FatLossPerMinute = ((BaseUnityPlugin)this).Config.Bind<float>("Body Weight", "NaturalFatLossPerMinute", 1f, new ConfigDescription("Positive body weight naturally lost per minute.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			ThinLossPerMinute = ((BaseUnityPlugin)this).Config.Bind<float>("Body Weight", "StarvationLossPerMinute", 1f, new ConfigDescription("Maximum body deficit gained per minute at zero fullness.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			MaxFatWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Body Weight", "MaximumFatWeight", 50f, new ConfigDescription("Body weight that displays maximum chonk.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 500f), Array.Empty<object>()));
			MaxThinWeight = ((BaseUnityPlugin)this).Config.Bind<float>("Body Weight", "MaximumThinWeight", 20f, new ConfigDescription("Body deficit that displays maximum thinness.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 200f), Array.Empty<object>()));
			CriticalFullnessEntry = ((BaseUnityPlugin)this).Config.Bind<int>("Body Weight", "CriticalFullness", 50, new ConfigDescription("Below this fullness, a base-weight character gradually becomes thin.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 99), Array.Empty<object>()));
			ChonkVoicePitch = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "PitchAtMaximumChonk", 0.7f, new ConfigDescription("Voice pitch multiplier at maximum chonk.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.5f, 1f), Array.Empty<object>()));
			ThinVoicePitch = ((BaseUnityPlugin)this).Config.Bind<float>("Voice", "PitchAtMaximumThinness", 1.5f, new ConfigDescription("Voice pitch multiplier at maximum thinness.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 1.5f), Array.Empty<object>()));
			BodyShapeProfile.Validate();
			BodyMassRules.Validate();
			ValidateVoicePitch();
			PitchShiftProcessor.Validate();
			BodyNetwork.Initialize();
			Harmony.CreateAndPatchAll(typeof(Plugin).Assembly, "ArceDev.HowToChonk");
			Log.LogInfo((object)("Plugin " + Name + " is loaded."));
		}

		private void Update()
		{
			BodyMassSystem.UpdateServer();
		}

		internal static float ShapeFor(Player player)
		{
			return BodyNetwork.ShapeFor(player);
		}

		internal static float VoicePitchFor(Player player)
		{
			return VoicePitchForShape(ShapeFor(player));
		}

		private static float VoicePitchForShape(float shape)
		{
			return Mathf.Lerp(1f, (shape < 0f) ? ThinVoicePitch.Value : ChonkVoicePitch.Value, Mathf.Abs(shape));
		}

		private static void ValidateVoicePitch()
		{
			if (!Mathf.Approximately(VoicePitchForShape(0f), 1f) || !Mathf.Approximately(VoicePitchForShape(-1f), ThinVoicePitch.Value) || !Mathf.Approximately(VoicePitchForShape(1f), ChonkVoicePitch.Value))
			{
				throw new InvalidOperationException("Voice pitch profile validation failed.");
			}
		}
	}
	internal sealed class VoicePitchFilter : MonoBehaviour
	{
		private PitchShiftProcessor _processor;

		private Player _player;

		private volatile float _targetPitch = 1f;

		internal static void Attach(AudioSource source, Player player)
		{
			VoicePitchFilter voicePitchFilter = ((Component)source).GetComponent<VoicePitchFilter>() ?? ((Component)source).gameObject.AddComponent<VoicePitchFilter>();
			voicePitchFilter._player = player;
			VoicePitchFilter voicePitchFilter2 = voicePitchFilter;
			if (voicePitchFilter2._processor == null)
			{
				voicePitchFilter2._processor = new PitchShiftProcessor(AudioSettings.outputSampleRate);
			}
		}

		private void Update()
		{
			if (Object.op_Implicit((Object)(object)_player))
			{
				_targetPitch = Plugin.VoicePitchFor(_player);
			}
		}

		private void OnAudioFilterRead(float[] data, int channels)
		{
			_processor.Process(data, channels, _targetPitch);
		}
	}
}
namespace System.Diagnostics.CodeAnalysis
{
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ConstantExpectedAttribute : Attribute
	{
		public object? Min { get; set; }

		public object? Max { get; set; }
	}
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ExperimentalAttribute : Attribute
	{
		public string DiagnosticId { get; }

		public string? UrlFormat { get; set; }

		public ExperimentalAttribute(string diagnosticId)
		{
			DiagnosticId = diagnosticId;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullAttribute : Attribute
	{
		public string[] Members { get; }

		public MemberNotNullAttribute(string member)
		{
			Members = new string[1] { member };
		}

		public MemberNotNullAttribute(params string[] members)
		{
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
	[ExcludeFromCodeCoverage]
	internal sealed class MemberNotNullWhenAttribute : Attribute
	{
		public bool ReturnValue { get; }

		public string[] Members { get; }

		public MemberNotNullWhenAttribute(bool returnValue, string member)
		{
			ReturnValue = returnValue;
			Members = new string[1] { member };
		}

		public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
		{
			ReturnValue = returnValue;
			Members = members;
		}
	}
	[AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class SetsRequiredMembersAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class StringSyntaxAttribute : Attribute
	{
		public const string CompositeFormat = "CompositeFormat";

		public const string DateOnlyFormat = "DateOnlyFormat";

		public const string DateTimeFormat = "DateTimeFormat";

		public const string EnumFormat = "EnumFormat";

		public const string GuidFormat = "GuidFormat";

		public const string Json = "Json";

		public const string NumericFormat = "NumericFormat";

		public const string Regex = "Regex";

		public const string TimeOnlyFormat = "TimeOnlyFormat";

		public const string TimeSpanFormat = "TimeSpanFormat";

		public const string Uri = "Uri";

		public const string Xml = "Xml";

		public string Syntax { get; }

		public object?[] Arguments { get; }

		public StringSyntaxAttribute(string syntax)
		{
			Syntax = syntax;
			Arguments = new object[0];
		}

		public StringSyntaxAttribute(string syntax, params object?[] arguments)
		{
			Syntax = syntax;
			Arguments = arguments;
		}
	}
	[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class UnscopedRefAttribute : Attribute
	{
	}
}
namespace System.Runtime.Versioning
{
	[AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Interface | AttributeTargets.Delegate, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiresPreviewFeaturesAttribute : Attribute
	{
		public string? Message { get; }

		public string? Url { get; set; }

		public RequiresPreviewFeaturesAttribute()
		{
		}

		public RequiresPreviewFeaturesAttribute(string? message)
		{
			Message = message;
		}
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CallerArgumentExpressionAttribute : Attribute
	{
		public string ParameterName { get; }

		public CallerArgumentExpressionAttribute(string parameterName)
		{
			ParameterName = parameterName;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CollectionBuilderAttribute : Attribute
	{
		public Type BuilderType { get; }

		public string MethodName { get; }

		public CollectionBuilderAttribute(Type builderType, string methodName)
		{
			BuilderType = builderType;
			MethodName = methodName;
		}
	}
	[AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class CompilerFeatureRequiredAttribute : Attribute
	{
		public const string RefStructs = "RefStructs";

		public const string RequiredMembers = "RequiredMembers";

		public string FeatureName { get; }

		public bool IsOptional { get; set; }

		public CompilerFeatureRequiredAttribute(string featureName)
		{
			FeatureName = featureName;
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
	{
		public string[] Arguments { get; }

		public InterpolatedStringHandlerArgumentAttribute(string argument)
		{
			Arguments = new string[1] { argument };
		}

		public InterpolatedStringHandlerArgumentAttribute(params string[] arguments)
		{
			Arguments = arguments;
		}
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class InterpolatedStringHandlerAttribute : Attribute
	{
	}
	[EditorBrowsable(EditorBrowsableState.Never)]
	[ExcludeFromCodeCoverage]
	internal static class IsExternalInit
	{
	}
	[AttributeUsage(AttributeTargets.Method, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ModuleInitializerAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class OverloadResolutionPriorityAttribute : Attribute
	{
		public int Priority { get; }

		public OverloadResolutionPriorityAttribute(int priority)
		{
			Priority = priority;
		}
	}
	[AttributeUsage(AttributeTargets.Parameter, Inherited = true, AllowMultiple = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class ParamCollectionAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiredMemberAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
	[EditorBrowsable(EditorBrowsableState.Never)]
	[ExcludeFromCodeCoverage]
	internal sealed class RequiresLocationAttribute : Attribute
	{
	}
	[AttributeUsage(AttributeTargets.Module | AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event | AttributeTargets.Interface, Inherited = false)]
	[ExcludeFromCodeCoverage]
	internal sealed class SkipLocalsInitAttribute : Attribute
	{
	}
}

plugins/NWaves.dll

Decompiled 2 days ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml;
using NWaves.Audio.Interfaces;
using NWaves.Effects.Base;
using NWaves.FeatureExtractors.Base;
using NWaves.FeatureExtractors.Options;
using NWaves.Features;
using NWaves.Filters;
using NWaves.Filters.Base;
using NWaves.Filters.Base64;
using NWaves.Filters.BiQuad;
using NWaves.Filters.Fda;
using NWaves.Filters.OnePole;
using NWaves.Operations;
using NWaves.Operations.Convolution;
using NWaves.Operations.Tsm;
using NWaves.Signals;
using NWaves.Signals.Builders;
using NWaves.Signals.Builders.Base;
using NWaves.Transforms;
using NWaves.Transforms.Base;
using NWaves.Transforms.Wavelets;
using NWaves.Utils;
using NWaves.Windows;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("Tim Sharii")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyCopyright("ar1st0crat")]
[assembly: AssemblyDescription(".NET DSP library with a lot of audio processing functions")]
[assembly: AssemblyFileVersion("0.9.6")]
[assembly: AssemblyInformationalVersion("0.9.6")]
[assembly: AssemblyProduct("NWaves")]
[assembly: AssemblyTitle("NWaves")]
[assembly: AssemblyMetadata("RepositoryUrl", "https://github.com/ar1st0crat/NWaves")]
[assembly: AssemblyVersion("0.9.6.0")]
namespace NWaves.Windows
{
	public static class Window
	{
		public static float[] OfType(WindowType type, int length, params object[] parameters)
		{
			switch (type)
			{
			case WindowType.Triangular:
				return Triangular(length);
			case WindowType.Hamming:
				return Hamming(length);
			case WindowType.Blackman:
				return Blackman(length);
			case WindowType.Hann:
				return Hann(length);
			case WindowType.Gaussian:
				return Gaussian(length);
			case WindowType.Kaiser:
				if (parameters.Length == 0)
				{
					return Kaiser(length);
				}
				return Kaiser(length, (double)parameters[0]);
			case WindowType.Kbd:
				if (parameters.Length == 0)
				{
					return Kbd(length);
				}
				return Kbd(length, (double)parameters[0]);
			case WindowType.BartlettHann:
				return BartlettHann(length);
			case WindowType.Lanczos:
				return Lanczos(length);
			case WindowType.PowerOfSine:
				if (parameters.Length == 0)
				{
					return PowerOfSine(length);
				}
				return PowerOfSine(length, (double)parameters[0]);
			case WindowType.Flattop:
				return Flattop(length);
			case WindowType.Liftering:
				if (parameters.Length == 0)
				{
					return Liftering(length);
				}
				return Liftering(length, (int)parameters[0]);
			default:
				return Rectangular(length);
			}
		}

		public static float[] Rectangular(int length)
		{
			return Enumerable.Repeat(1f, length).ToArray();
		}

		public static float[] Triangular(int length)
		{
			int n = length - 1;
			return (from i in Enumerable.Range(0, length)
				select 1.0 - 2.0 * Math.Abs((double)i - (double)n / 2.0) / (double)length).ToFloats();
		}

		public static float[] Hamming(int length)
		{
			double n = Math.PI * 2.0 / (double)(length - 1);
			return (from i in Enumerable.Range(0, length)
				select 0.54 - 0.46 * Math.Cos((double)i * n)).ToFloats();
		}

		public static float[] Blackman(int length)
		{
			double n = Math.PI * 2.0 / (double)(length - 1);
			return (from i in Enumerable.Range(0, length)
				select 0.42 - 0.5 * Math.Cos((double)i * n) + 0.08 * Math.Cos((double)(2 * i) * n)).ToFloats();
		}

		public static float[] Hann(int length)
		{
			double n = Math.PI * 2.0 / (double)(length - 1);
			return (from i in Enumerable.Range(0, length)
				select 0.5 * (1.0 - Math.Cos((double)i * n))).ToFloats();
		}

		public static float[] Gaussian(int length)
		{
			int n = (length - 1) / 2;
			return (from i in Enumerable.Range(0, length)
				select Math.Exp(-0.5 * Math.Pow((double)(i - n) / (0.4 * (double)n), 2.0))).ToFloats();
		}

		public static float[] Kaiser(int length, double alpha = 12.0)
		{
			double n = 2.0 / (double)(length - 1);
			return (from i in Enumerable.Range(0, length)
				select MathUtils.I0(alpha * Math.Sqrt(1.0 - ((double)i * n - 1.0) * ((double)i * n - 1.0))) / MathUtils.I0(alpha)).ToFloats();
		}

		public static float[] Kbd(int length, double alpha = 4.0)
		{
			float[] array = new float[length];
			double num = 4.0 / (double)length;
			double num2 = 0.0;
			for (int i = 0; i <= length / 2; i++)
			{
				num2 += MathUtils.I0(Math.PI * alpha * Math.Sqrt(1.0 - ((double)i * num - 1.0) * ((double)i * num - 1.0)));
				array[i] = (float)num2;
			}
			for (int j = 0; j < length / 2; j++)
			{
				array[j] = (float)Math.Sqrt((double)array[j] / num2);
				array[length - 1 - j] = array[j];
			}
			return array;
		}

		public static float[] BartlettHann(int length)
		{
			double n = 1.0 / (double)(length - 1);
			return (from i in Enumerable.Range(0, length)
				select 0.62 - 0.48 * Math.Abs((double)i * n - 0.5) - 0.38 * Math.Cos(Math.PI * 2.0 * (double)i * n)).ToFloats();
		}

		public static float[] Lanczos(int length)
		{
			double n = 2.0 / (double)(length - 1);
			return (from i in Enumerable.Range(0, length)
				select MathUtils.Sinc((double)i * n - 1.0)).ToFloats();
		}

		public static float[] PowerOfSine(int length, double alpha = 1.5)
		{
			double n = Math.PI / (double)length;
			return (from i in Enumerable.Range(0, length)
				select Math.Pow(Math.Sin((double)i * n), alpha)).ToFloats();
		}

		public static float[] Flattop(int length)
		{
			double n = Math.PI * 2.0 / (double)(length - 1);
			return (from i in Enumerable.Range(0, length)
				select 0.216 - 0.417 * Math.Cos((double)i * n) + 0.278 * Math.Cos((double)(2 * i) * n) - 0.084 * Math.Cos((double)(3 * i) * n) + 0.007 * Math.Cos((double)(4 * i) * n)).ToFloats();
		}

		public static float[] Liftering(int length, int l = 22)
		{
			if (l <= 0)
			{
				return Rectangular(length);
			}
			return (from i in Enumerable.Range(0, length)
				select 1.0 + (double)l * Math.Sin(Math.PI * (double)i / (double)l) / 2.0).ToFloats();
		}
	}
	public static class WindowExtensions
	{
		public static void ApplyWindow(this float[] samples, float[] windowSamples)
		{
			for (int i = 0; i < windowSamples.Length; i++)
			{
				samples[i] *= windowSamples[i];
			}
		}

		public static void ApplyWindow(this double[] samples, double[] windowSamples)
		{
			for (int i = 0; i < windowSamples.Length; i++)
			{
				samples[i] *= windowSamples[i];
			}
		}

		public static void ApplyWindow(this DiscreteSignal signal, float[] windowSamples)
		{
			signal.Samples.ApplyWindow(windowSamples);
		}

		public static void ApplyWindow(this float[] samples, WindowType window, params object[] parameters)
		{
			float[] windowSamples = Window.OfType(window, samples.Length, parameters);
			samples.ApplyWindow(windowSamples);
		}

		public static void ApplyWindow(this double[] samples, WindowType window, params object[] parameters)
		{
			double[] windowSamples = Window.OfType(window, samples.Length, parameters).ToDoubles();
			samples.ApplyWindow(windowSamples);
		}

		public static void ApplyWindow(this DiscreteSignal signal, WindowType window, params object[] parameters)
		{
			float[] windowSamples = Window.OfType(window, signal.Length, parameters);
			signal.Samples.ApplyWindow(windowSamples);
		}
	}
	public enum WindowType
	{
		Rectangular,
		Triangular,
		Hamming,
		Blackman,
		Hann,
		Gaussian,
		Kaiser,
		Kbd,
		BartlettHann,
		Lanczos,
		PowerOfSine,
		Flattop,
		Liftering
	}
}
namespace NWaves.Utils
{
	public class FractionalDelayLine
	{
		private int _delayLineSize;

		private float[] _delayLine;

		private int _n;

		private float _prevInterpolated;

		public InterpolationMode InterpolationMode { get; set; }

		public int Size => _delayLineSize;

		public FractionalDelayLine(int size, InterpolationMode interpolationMode = InterpolationMode.Linear)
		{
			_delayLineSize = Math.Max(4, size);
			_delayLine = new float[_delayLineSize];
			_n = 0;
			InterpolationMode = interpolationMode;
		}

		public FractionalDelayLine(int samplingRate, double maxDelay, InterpolationMode interpolationMode = InterpolationMode.Linear)
			: this((int)((double)samplingRate * maxDelay) + 1, interpolationMode)
		{
		}

		public void Write(float sample)
		{
			_delayLine[_n] = sample;
			if (++_n >= _delayLineSize)
			{
				_n = 0;
			}
		}

		public float Read(double delay)
		{
			float num = (float)((double)_n - delay + (double)_delayLineSize) % (float)_delayLineSize;
			int num2 = (int)num;
			float num3 = num - (float)num2;
			switch (InterpolationMode)
			{
			case InterpolationMode.Nearest:
				return _delayLine[num2 % _delayLineSize];
			case InterpolationMode.Cubic:
			{
				float num9 = num3 * num3;
				float num10 = num9 * num3;
				float num11 = _delayLine[(num2 - 1 + _delayLineSize) % _delayLineSize];
				float num12 = _delayLine[num2];
				float num13 = _delayLine[(num2 + 1) % _delayLineSize];
				float num14 = _delayLine[(num2 + 2) % _delayLineSize];
				float num15 = -0.5f * num11 + 1.5f * num12 - 1.5f * num13 + 0.5f * num14;
				float num16 = num11 - 2.5f * num12 + 2f * num13 - 0.5f * num14;
				float num17 = -0.5f * num11 + 0.5f * num13;
				float num18 = num12;
				return num15 * num10 + num16 * num9 + num17 * num3 + num18;
			}
			case InterpolationMode.Thiran:
			{
				float num6 = _delayLine[num2];
				float num7 = _delayLine[(num2 + 1) % _delayLineSize];
				if ((double)num3 < 0.618)
				{
					num3 += 1f;
				}
				float num8 = (1f - num3) / (1f + num3);
				return _prevInterpolated = num7 + num8 * (num6 - _prevInterpolated);
			}
			default:
			{
				float num4 = _delayLine[num2];
				float num5 = _delayLine[(num2 + 1) % _delayLineSize];
				return num4 + num3 * (num5 - num4);
			}
			}
		}

		public void Reset()
		{
			Array.Clear(_delayLine, 0, _delayLineSize);
			_n = 0;
			_prevInterpolated = 0f;
		}

		public void Ensure(int size)
		{
			if (size > _delayLineSize)
			{
				Array.Resize(ref _delayLine, size);
				_delayLineSize = size;
			}
		}

		public void Ensure(int samplingRate, double maxDelay)
		{
			Ensure((int)((double)samplingRate * maxDelay) + 1);
		}
	}
	[DebuggerStepThrough]
	public static class Guard
	{
		public static void AgainstNonPositive(double arg, string argName = "argument")
		{
			if (arg < 1E-30)
			{
				throw new ArgumentException(argName + " must be positive!");
			}
		}

		public static void AgainstInequality(double arg1, double arg2, string arg1Name = "argument1", string arg2Name = "argument2")
		{
			if (Math.Abs(arg2 - arg1) > 1E-30)
			{
				throw new ArgumentException(arg1Name + " must be equal to " + arg2Name + "!");
			}
		}

		public static void AgainstInvalidRange(double value, double low, double high, string valueName = "value")
		{
			if (value < low || value > high)
			{
				throw new ArgumentException($"{valueName} must be in range [{low}, {high}]!");
			}
		}

		public static void AgainstInvalidRange(double low, double high, string lowName = "low", string highName = "high")
		{
			if (high - low < 1E-30)
			{
				throw new ArgumentException(highName + " must be greater than " + lowName + "!");
			}
		}

		public static void AgainstExceedance(double low, double high, string lowName = "low", string highName = "high")
		{
			if (low > high)
			{
				throw new ArgumentException(lowName + " must not exceed " + highName + "!");
			}
		}

		public static void AgainstNotPowerOfTwo(int n, string argName = "Parameter")
		{
			int num = (int)Math.Log(n, 2.0);
			if (n != 1 << num)
			{
				throw new ArgumentException(argName + " must be a power of 2!");
			}
		}

		public static void AgainstEvenNumber(int n, string argName = "Parameter")
		{
			if (n % 2 == 0)
			{
				throw new ArgumentException(argName + " must be an odd number!");
			}
		}

		public static void AgainstNotOrdered(double[] values, string argName = "Values")
		{
			for (int i = 1; i < values.Length; i++)
			{
				if (values[i] <= values[i - 1])
				{
					throw new ArgumentException(argName + " must be ordered!");
				}
			}
		}

		public static void AgainstIncorrectFilterParams(double[] freqs, double[] desired, double[] weights)
		{
			int num = freqs.Length;
			if (num < 4 || num % 2 != 0)
			{
				throw new ArgumentException("Frequency array must have even number of at least 4 values!");
			}
			if (freqs[0] != 0.0 || freqs[num - 1] != 0.5)
			{
				throw new ArgumentException("Frequency array must start with 0 and end with 0.5!");
			}
			AgainstInequality(desired.Length, num / 2, "Size of desired array", "half-size of freqs array");
			AgainstInequality(weights.Length, num / 2, "Size of weights array", "half-size of freqs array");
		}
	}
	public enum InterpolationMode
	{
		Linear,
		Cubic,
		Thiran,
		Nearest
	}
	public static class Lpc
	{
		public static float LevinsonDurbin(float[] input, float[] a, int order, int offset = 0)
		{
			float num = input[offset];
			a[0] = 1f;
			for (int i = 1; i <= order; i++)
			{
				float num2 = 0f;
				for (int j = 0; j < i; j++)
				{
					num2 -= a[j] * input[offset + i - j];
				}
				num2 /= num;
				for (int k = 0; k <= i / 2; k++)
				{
					float num3 = a[i - k] + num2 * a[k];
					a[k] += num2 * a[i - k];
					a[i - k] = num3;
				}
				num *= 1f - num2 * num2;
			}
			return num;
		}

		public static void ToCepstrum(float[] lpc, float gain, float[] lpcc)
		{
			int num = lpcc.Length;
			int num2 = lpc.Length;
			lpcc[0] = (float)Math.Log(gain);
			for (int i = 1; i < Math.Min(num, num2); i++)
			{
				float num3 = 0f;
				for (int j = 1; j < i; j++)
				{
					num3 += (float)j * lpcc[j] * lpc[i - j];
				}
				lpcc[i] = 0f - lpc[i] - num3 / (float)i;
			}
			for (int k = num2; k < num; k++)
			{
				float num4 = 0f;
				for (int l = 1; l < num2; l++)
				{
					num4 += (float)(k - l) * lpcc[k - l] * lpc[l];
				}
				lpcc[k] = (0f - num4) / (float)k;
			}
		}

		public static float FromCepstrum(float[] lpcc, float[] lpc)
		{
			int num = lpc.Length;
			lpc[0] = 1f;
			for (int i = 1; i < num; i++)
			{
				float num2 = 0f;
				for (int j = 1; j < i; j++)
				{
					num2 += (float)j * lpcc[j] * lpc[i - j];
				}
				lpc[i] = 0f - lpcc[i] - num2 / (float)i;
			}
			return (float)Math.Exp(lpcc[0]);
		}

		public static int EstimateOrder(int samplingRate)
		{
			return 2 + samplingRate / 1000;
		}

		public static void ToLsf(float[] lpc, float[] lsf)
		{
			float num = lpc[0];
			if ((double)Math.Abs(num - 1f) > 1E-10)
			{
				for (int i = 0; i < lpc.Length; i++)
				{
					lpc[i] /= num;
				}
			}
			float[] array = new float[lpc.Length + 1];
			float[] array2 = new float[lpc.Length + 1];
			array[0] = (array2[0] = 1f);
			for (int j = 1; j < array.Length - 1; j++)
			{
				array[j] = lpc[j] - lpc[array.Length - j - 1];
				array2[j] = lpc[j] + lpc[array.Length - j - 1];
			}
			array[^1] = -1f;
			array2[^1] = 1f;
			double[] array3 = (from r in MathUtils.PolynomialRoots(array.ToDoubles())
				select r.Phase).ToArray();
			double[] array4 = (from r in MathUtils.PolynomialRoots(array2.ToDoubles())
				select r.Phase).ToArray();
			Array.Sort(array3);
			Array.Sort(array4);
			int num2 = 0;
			for (int num3 = 0; num3 < array4.Length; num3++)
			{
				if (array4[num3] > 0.0)
				{
					lsf[num2++] = (float)array4[num3];
				}
			}
			for (int num4 = 0; num4 < array3.Length; num4++)
			{
				if (array3[num4] > 0.0)
				{
					lsf[num2++] = (float)array3[num4];
				}
			}
			Array.Sort(lsf);
		}

		public static void FromLsf(float[] lsf, float[] lpc)
		{
			int num = lsf.Length - 1;
			int num2 = num / 2;
			Complex[] array = new Complex[num];
			Complex[] array2 = new Complex[num + 2 * (num % 2)];
			int i = 0;
			int num3 = 0;
			for (; i < num2; i++)
			{
				array2[i] = new Complex(Math.Cos(lsf[num3]), Math.Sin(lsf[num3]));
				array[i] = new Complex(Math.Cos(lsf[num3 + 1]), Math.Sin(lsf[num3 + 1]));
				num3 += 2;
			}
			int num4 = 0;
			for (; i < 2 * num2; i++)
			{
				array2[i] = new Complex(Math.Cos(lsf[num4]), Math.Sin(0f - lsf[num4]));
				array[i] = new Complex(Math.Cos(lsf[num4 + 1]), Math.Sin(0f - lsf[num4 + 1]));
				num4 += 2;
			}
			if (num % 2 == 1)
			{
				array2[num] = new Complex(Math.Cos(lsf[num - 1]), Math.Sin(lsf[num - 1]));
				array2[num + 1] = new Complex(Math.Cos(lsf[num - 1]), Math.Sin(0f - lsf[num - 1]));
			}
			ComplexDiscreteSignal signal = new ComplexDiscreteSignal(1, TransferFunction.ZpToTf(array));
			ComplexDiscreteSignal complexDiscreteSignal = new ComplexDiscreteSignal(1, TransferFunction.ZpToTf(array2));
			if (num % 2 == 1)
			{
				signal = Operation.Convolve(signal, new ComplexDiscreteSignal(1, new double[3] { 1.0, 0.0, -1.0 }));
			}
			else
			{
				signal = Operation.Convolve(signal, new ComplexDiscreteSignal(1, new double[2] { 1.0, -1.0 }));
				complexDiscreteSignal = Operation.Convolve(complexDiscreteSignal, new ComplexDiscreteSignal(1, new double[2] { 1.0, 1.0 }));
			}
			for (int j = 0; j < lpc.Length; j++)
			{
				lpc[j] = (float)(0.5 * (signal.Real[j] + complexDiscreteSignal.Real[j]));
			}
		}
	}
	public static class MathUtils
	{
		public const int PolyRootsIterations = 25000;

		public static double Sinc(double x)
		{
			if (!(Math.Abs(x) > 1E-20))
			{
				return 1.0;
			}
			return Math.Sin(Math.PI * x) / (Math.PI * x);
		}

		public static int NextPowerOfTwo(int n)
		{
			return (int)Math.Pow(2.0, Math.Ceiling(Math.Log(n, 2.0)));
		}

		public static int Gcd(int n, int m)
		{
			while (m != 0)
			{
				m = n % (n = m);
			}
			return n;
		}

		public static double Mod(double a, double b)
		{
			return (a % b + b) % b;
		}

		public static double Asinh(double x)
		{
			return Math.Log(x + Math.Sqrt(x * x + 1.0));
		}

		public static double Factorial(int n)
		{
			double num = 1.0;
			int num2 = 2;
			while (num2 <= n)
			{
				num *= (double)num2++;
			}
			return num;
		}

		public static double BinomialCoefficient(int k, int n)
		{
			return Factorial(n) / (Factorial(k) * Factorial(n - k));
		}

		public static void Diff(float[] samples, float[] diff)
		{
			diff[0] = samples[0];
			for (int i = 1; i < samples.Length; i++)
			{
				diff[i] = samples[i] - samples[i - 1];
			}
		}

		public static void InterpolateLinear(float[] x, float[] y, float[] arg, float[] interp)
		{
			int num = 0;
			int num2 = 1;
			for (int i = 0; i < arg.Length; i++)
			{
				while (arg[i] > x[num2] && num2 < x.Length - 1)
				{
					num2++;
					num++;
				}
				interp[i] = y[num] + (y[num2] - y[num]) * (arg[i] - x[num]) / (x[num2] - x[num]);
			}
		}

		public static void BilinearTransform(double[] re, double[] im)
		{
			for (int i = 0; i < re.Length; i++)
			{
				double num = (1.0 - re[i]) * (1.0 - re[i]) + im[i] * im[i];
				re[i] = (1.0 - re[i] * re[i] - im[i] * im[i]) / num;
				im[i] = 2.0 * im[i] / num;
			}
		}

		public static double[] Unwrap(double[] phase, double tolerance = Math.PI)
		{
			double[] array = phase.FastCopy();
			double num = 0.0;
			for (int i = 1; i < phase.Length; i++)
			{
				double num2 = phase[i] - phase[i - 1];
				if (num2 > tolerance)
				{
					num -= tolerance * 2.0;
				}
				else if (num2 < 0.0 - tolerance)
				{
					num += tolerance * 2.0;
				}
				array[i] = phase[i] + num;
			}
			return array;
		}

		public static double[] Wrap(double[] phase, double tolerance = Math.PI)
		{
			double[] array = phase.FastCopy();
			for (int i = 0; i < phase.Length; i++)
			{
				double num = phase[i] % (tolerance * 2.0);
				if (num > tolerance)
				{
					num -= tolerance * 2.0;
				}
				else if (num < 0.0 - tolerance)
				{
					num += tolerance * 2.0;
				}
				array[i] = num;
			}
			return array;
		}

		public static float FindNth(float[] a, int n, int start, int end)
		{
			int num2;
			while (true)
			{
				float num = a[end];
				num2 = start - 1;
				for (int i = start; i < end; i++)
				{
					if (a[i] <= num)
					{
						num2++;
						float num3 = a[i];
						a[i] = a[num2];
						a[num2] = num3;
					}
				}
				num2++;
				float num4 = a[end];
				a[end] = a[num2];
				a[num2] = num4;
				if (num2 == n)
				{
					break;
				}
				if (n < num2)
				{
					end = num2 - 1;
				}
				else
				{
					start = num2 + 1;
				}
			}
			return a[num2];
		}

		public static double I0(double x)
		{
			double num = 1.0;
			double num2 = 1.0;
			int num3 = 1;
			while (Math.Abs(num2) > 1E-20)
			{
				double num4 = num2 * x * x / (double)(4 * num3 * num3);
				num += num4;
				num2 = num4;
				num3++;
			}
			return num;
		}

		public static Complex[] PolynomialRoots(double[] a, int maxIterations = 25000)
		{
			if (a.Length <= 1)
			{
				return null;
			}
			Complex one = Complex.One;
			Complex[] array = new Complex[a.Length - 1];
			Complex[] array2 = new Complex[a.Length - 1];
			Complex complex = new Complex(0.4, 0.9);
			array[0] = one;
			for (int i = 1; i < array.Length; i++)
			{
				array[i] = array[i - 1] * complex;
			}
			int num = 0;
			while (true)
			{
				for (int j = 0; j < array.Length; j++)
				{
					complex = one;
					for (int k = 0; k < array.Length; k++)
					{
						if (j != k)
						{
							complex = (array[j] - array[k]) * complex;
						}
					}
					array2[j] = array[j] - EvaluatePolynomial(a, array[j]) / complex;
				}
				if (++num > maxIterations || ArraysAreEqual(array, array2))
				{
					break;
				}
				Array.Copy(array2, array, array2.Length);
			}
			return array2;
		}

		private static bool ArraysAreEqual(Complex[] a, Complex[] b, double tolerance = 1E-16)
		{
			for (int i = 0; i < a.Length; i++)
			{
				if (Complex.Abs(a[i] - b[i]) > tolerance)
				{
					return false;
				}
			}
			return true;
		}

		public static Complex EvaluatePolynomial(double[] a, Complex x)
		{
			Complex result = new Complex(a[0], 0.0);
			for (int i = 1; i < a.Length; i++)
			{
				result *= x;
				result += (Complex)a[i];
			}
			return result;
		}

		public static Complex[] MultiplyPolynomials(Complex[] poly1, Complex[] poly2)
		{
			Complex[] array = new Complex[poly1.Length + poly2.Length - 1];
			for (int i = 0; i < poly1.Length; i++)
			{
				for (int j = 0; j < poly2.Length; j++)
				{
					array[i + j] += poly1[i] * poly2[j];
				}
			}
			return array;
		}

		public static Complex[][] DividePolynomial(Complex[] dividend, Complex[] divisor)
		{
			Complex[] array = (Complex[])dividend.Clone();
			Complex complex = divisor[0];
			for (int i = 0; i < dividend.Length - divisor.Length + 1; i++)
			{
				array[i] /= complex;
				Complex complex2 = array[i];
				if (Math.Abs(complex2.Real) > 1E-10 || Math.Abs(complex2.Imaginary) > 1E-10)
				{
					for (int j = 1; j < divisor.Length; j++)
					{
						array[i + j] -= divisor[j] * complex2;
					}
				}
			}
			int num = array.Length - divisor.Length + 1;
			Complex[] array2 = new Complex[num];
			Complex[] array3 = new Complex[array.Length - num];
			Array.Copy(array, 0, array2, 0, num);
			Array.Copy(array, num, array3, 0, array.Length - num);
			return new Complex[2][] { array2, array3 };
		}
	}
	public class Matrix
	{
		private readonly double[][] _matrix;

		public int Rows { get; set; }

		public int Columns { get; set; }

		public Matrix T
		{
			get
			{
				Matrix matrix = new Matrix(Columns, Rows);
				for (int i = 0; i < Columns; i++)
				{
					for (int j = 0; j < Rows; j++)
					{
						matrix[i][j] = _matrix[j][i];
					}
				}
				return matrix;
			}
		}

		public double[] this[int i] => _matrix[i];

		public Matrix(int rows, int columns = 0)
		{
			if (columns == 0)
			{
				columns = rows;
			}
			Guard.AgainstNonPositive(rows, "Number of rows");
			Guard.AgainstNonPositive(columns, "Number of columns");
			_matrix = new double[rows][];
			for (int i = 0; i < rows; i++)
			{
				_matrix[i] = new double[columns];
			}
			Rows = rows;
			Columns = columns;
		}

		public double[][] As2dArray()
		{
			return _matrix;
		}

		public static Matrix Companion(double[] a)
		{
			if (a.Length < 2)
			{
				throw new ArgumentException("The size of input array must be at least 2!");
			}
			if (Math.Abs(a[0]) < 1E-30)
			{
				throw new ArgumentException("The first coefficient must not be zero!");
			}
			int num = a.Length - 1;
			Matrix matrix = new Matrix(num);
			for (int i = 0; i < num; i++)
			{
				matrix[0][i] = (0.0 - a[i + 1]) / a[0];
			}
			for (int j = 1; j < num; j++)
			{
				matrix[j][j - 1] = 1.0;
			}
			return matrix;
		}

		public static Matrix Eye(int size)
		{
			Matrix matrix = new Matrix(size);
			for (int i = 0; i < size; i++)
			{
				matrix[i][i] = 1.0;
			}
			return matrix;
		}

		public static Matrix operator +(Matrix m1, Matrix m2)
		{
			Guard.AgainstInequality(m1.Rows, m2.Rows, "Number of rows in first matrix", "number of rows in second matrix");
			Guard.AgainstInequality(m1.Columns, m2.Columns, "Number of columns in first matrix", "number of columns in second matrix");
			Matrix matrix = new Matrix(m1.Rows, m1.Columns);
			for (int i = 0; i < m1.Rows; i++)
			{
				for (int j = 0; j < m1.Columns; j++)
				{
					matrix[i][j] = m1[i][j] + m2[i][j];
				}
			}
			return matrix;
		}

		public static Matrix operator -(Matrix m1, Matrix m2)
		{
			Guard.AgainstInequality(m1.Rows, m2.Rows, "Number of rows in first matrix", "number of rows in second matrix");
			Guard.AgainstInequality(m1.Columns, m2.Columns, "Number of columns in first matrix", "number of columns in second matrix");
			Matrix matrix = new Matrix(m1.Rows, m1.Columns);
			for (int i = 0; i < m1.Rows; i++)
			{
				for (int j = 0; j < m1.Columns; j++)
				{
					matrix[i][j] = m1[i][j] - m2[i][j];
				}
			}
			return matrix;
		}
	}
	public static class MemoryOperationExtensions
	{
		private const byte _32Bits = 4;

		private const byte _64Bits = 8;

		public static float[] ToFloats(this IEnumerable<double> values)
		{
			return values.Select((double v) => (float)v).ToArray();
		}

		public static double[] ToDoubles(this IEnumerable<float> values)
		{
			return values.Select((Func<float, double>)((float v) => v)).ToArray();
		}

		public static float[] FastCopy(this float[] source)
		{
			float[] array = new float[source.Length];
			Buffer.BlockCopy(source, 0, array, 0, source.Length * 4);
			return array;
		}

		public static void FastCopyTo(this float[] source, float[] destination, int size, int sourceOffset = 0, int destinationOffset = 0)
		{
			Buffer.BlockCopy(source, sourceOffset * 4, destination, destinationOffset * 4, size * 4);
		}

		public static float[] FastCopyFragment(this float[] source, int size, int sourceOffset = 0, int destinationOffset = 0)
		{
			float[] array = new float[size + destinationOffset];
			Buffer.BlockCopy(source, sourceOffset * 4, array, destinationOffset * 4, size * 4);
			return array;
		}

		public static float[] MergeWithArray(this float[] source, float[] another)
		{
			float[] array = new float[source.Length + another.Length];
			Buffer.BlockCopy(source, 0, array, 0, source.Length * 4);
			Buffer.BlockCopy(another, 0, array, source.Length * 4, another.Length * 4);
			return array;
		}

		public static float[] RepeatArray(this float[] source, int n)
		{
			float[] array = new float[source.Length * n];
			int num = 0;
			for (int i = 0; i < n; i++)
			{
				Buffer.BlockCopy(source, 0, array, num * 4, source.Length * 4);
				num += source.Length;
			}
			return array;
		}

		public static float[] PadZeros(this float[] source, int size)
		{
			float[] array = new float[size];
			Buffer.BlockCopy(source, 0, array, 0, source.Length * 4);
			return array;
		}

		public static double[] FastCopy(this double[] source)
		{
			double[] array = new double[source.Length];
			Buffer.BlockCopy(source, 0, array, 0, source.Length * 8);
			return array;
		}

		public static void FastCopyTo(this double[] source, double[] destination, int size, int sourceOffset = 0, int destinationOffset = 0)
		{
			Buffer.BlockCopy(source, sourceOffset * 8, destination, destinationOffset * 8, size * 8);
		}

		public static double[] FastCopyFragment(this double[] source, int size, int sourceOffset = 0, int destinationOffset = 0)
		{
			double[] array = new double[size + destinationOffset];
			Buffer.BlockCopy(source, sourceOffset * 8, array, destinationOffset * 8, size * 8);
			return array;
		}

		public static double[] MergeWithArray(this double[] source, double[] another)
		{
			double[] array = new double[source.Length + another.Length];
			Buffer.BlockCopy(source, 0, array, 0, source.Length * 8);
			Buffer.BlockCopy(another, 0, array, source.Length * 8, another.Length * 8);
			return array;
		}

		public static double[] RepeatArray(this double[] source, int n)
		{
			double[] array = new double[source.Length * n];
			int num = 0;
			for (int i = 0; i < n; i++)
			{
				Buffer.BlockCopy(source, 0, array, num * 8, source.Length * 8);
				num += source.Length;
			}
			return array;
		}

		public static double[] PadZeros(this double[] source, int size)
		{
			double[] array = new double[size];
			Buffer.BlockCopy(source, 0, array, 0, source.Length * 8);
			return array;
		}
	}
	public static class Scale
	{
		public static string[] Notes = new string[12]
		{
			"C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A",
			"A#", "B"
		};

		public static double ToDecibel(double value, double valueReference)
		{
			return 20.0 * Math.Log10(value / valueReference + double.Epsilon);
		}

		public static double ToDecibel(double value)
		{
			return 20.0 * Math.Log10(value);
		}

		public static double ToDecibelPower(double value, double valueReference = 1.0)
		{
			return 10.0 * Math.Log10(value / valueReference + double.Epsilon);
		}

		public static double FromDecibel(double level, double valueReference)
		{
			return valueReference * Math.Pow(10.0, level / 20.0);
		}

		public static double FromDecibel(double level)
		{
			return Math.Pow(10.0, level / 20.0);
		}

		public static double FromDecibelPower(double level, double valueReference = 1.0)
		{
			return valueReference * Math.Pow(10.0, level / 10.0);
		}

		public static double PitchToFreq(int pitch)
		{
			return 440.0 * Math.Pow(2.0, (double)(pitch - 69) / 12.0);
		}

		public static int FreqToPitch(double freq)
		{
			return (int)Math.Round(69.0 + 12.0 * Math.Log(freq / 440.0, 2.0), MidpointRounding.AwayFromZero);
		}

		public static double NoteToFreq(string note, int octave)
		{
			int num = Array.IndexOf(Notes, note);
			if (num < 0)
			{
				throw new ArgumentException("Incorrect note. Valid notes are: " + string.Join(", ", Notes));
			}
			if (octave < 0 || octave > 8)
			{
				throw new ArgumentException("Incorrect octave. Valid octave range is [0, 8]");
			}
			return PitchToFreq(num + 12 * (octave + 1));
		}

		public static (string, int) FreqToNote(double freq)
		{
			int num = FreqToPitch(freq);
			string item = Notes[num % 12];
			int item2 = num / 12 - 1;
			return (item, item2);
		}

		public static double HerzToMel(double herz)
		{
			return 1127.0 * Math.Log(herz / 700.0 + 1.0);
		}

		public static double MelToHerz(double mel)
		{
			return (Math.Exp(mel / 1127.0) - 1.0) * 700.0;
		}

		public static double HerzToMelSlaney(double herz)
		{
			double num = Math.Log(6.4) / 27.0;
			if (!(herz < 1000.0))
			{
				return 14.999999999999998 + Math.Log(herz / 1000.0) / num;
			}
			return (herz - 0.0) / 66.66666666666667;
		}

		public static double MelToHerzSlaney(double mel)
		{
			double num = Math.Log(6.4) / 27.0;
			if (!(mel < 14.999999999999998))
			{
				return 1000.0 * Math.Exp(num * (mel - 14.999999999999998));
			}
			return 0.0 + 66.66666666666667 * mel;
		}

		public static double HerzToBark(double herz)
		{
			return 26.81 * herz / (1960.0 + herz) - 0.53;
		}

		public static double BarkToHerz(double bark)
		{
			return 1960.0 / (26.81 / (bark + 0.53) - 1.0);
		}

		public static double HerzToBarkSlaney(double herz)
		{
			return 6.0 * MathUtils.Asinh(herz / 600.0);
		}

		public static double BarkToHerzSlaney(double bark)
		{
			return 600.0 * Math.Sinh(bark / 6.0);
		}

		public static double HerzToErb(double herz)
		{
			return 9.26449 * Math.Log(1.0 + herz) / 228.832903;
		}

		public static double ErbToHerz(double erb)
		{
			return (Math.Exp(erb / 9.26449) - 1.0) * 228.832903;
		}

		public static double HerzToOctave(double herz, double tuning = 0.0, int binsPerOctave = 12)
		{
			double num = 440.0 * Math.Pow(2.0, tuning / (double)binsPerOctave);
			return Math.Log(16.0 * herz / num, 2.0);
		}

		public static double LoudnessWeighting(double frequency, string weightingType = "A")
		{
			double num = frequency * frequency;
			string text = weightingType.ToUpper();
			if (!(text == "B"))
			{
				if (text == "C")
				{
					double d = num * 148693636.0 / ((num + 424.36) * (num + 148693636.0));
					return 20.0 * Math.Log10(d) + 0.06;
				}
				double d2 = num * num * 148693636.0 / ((num + 424.36) * Math.Sqrt((num + 11599.29) * (num + 544496.41)) * (num + 148693636.0));
				return 20.0 * Math.Log10(d2) + 2.0;
			}
			double d3 = num * frequency * 148693636.0 / ((num + 424.36) * Math.Sqrt(num + 25122.25) * (num + 148693636.0));
			return 20.0 * Math.Log10(d3) + 0.17;
		}
	}
}
namespace NWaves.Transforms
{
	public class CepstralTransform : ITransform
	{
		private readonly Fft _fft;

		private readonly double _logBase;

		private readonly float[] _re;

		private readonly float[] _im;

		private readonly double[] _unwrapped;

		public int Size { get; }

		public CepstralTransform(int cepstrumSize, int fftSize = 0, double logBase = Math.E)
		{
			Size = cepstrumSize;
			if (cepstrumSize > fftSize)
			{
				fftSize = MathUtils.NextPowerOfTwo(cepstrumSize);
			}
			_fft = new Fft(fftSize);
			_logBase = logBase;
			_re = new float[fftSize];
			_im = new float[fftSize];
			_unwrapped = new double[fftSize];
		}

		public double ComplexCepstrum(float[] input, float[] cepstrum, bool normalize = true)
		{
			Array.Clear(_re, 0, _re.Length);
			Array.Clear(_im, 0, _im.Length);
			input.FastCopyTo(_re, input.Length);
			_fft.Direct(_re, _im);
			double num = 0.0;
			_unwrapped[0] = 0.0;
			double num2 = Math.Atan2(_im[0], _re[0]);
			for (int i = 1; i < _unwrapped.Length; i++)
			{
				double num3 = Math.Atan2(_im[i], _re[i]);
				double num4 = num3 - num2;
				if (num4 > Math.PI)
				{
					num -= Math.PI * 2.0;
				}
				else if (num4 < -Math.PI)
				{
					num += Math.PI * 2.0;
				}
				_unwrapped[i] = num3 + num;
				num2 = num3;
			}
			int num5 = _re.Length / 2;
			double num6 = Math.Round(_unwrapped[num5] / Math.PI);
			for (int j = 0; j < _re.Length; j++)
			{
				_unwrapped[j] -= Math.PI * num6 * (double)j / (double)num5;
				double num7 = Math.Sqrt(_re[j] * _re[j] + _im[j] * _im[j]);
				_re[j] = (float)Math.Log(num7 + 1.401298464324817E-45, _logBase);
				_im[j] = (float)_unwrapped[j];
			}
			_fft.Inverse(_re, _im);
			_re.FastCopyTo(cepstrum, Size);
			if (normalize)
			{
				for (int k = 0; k < cepstrum.Length; k++)
				{
					cepstrum[k] /= _fft.Size;
				}
			}
			return num6;
		}

		public void InverseComplexCepstrum(float[] input, float[] cepstrum, bool normalize = true, double delay = 0.0)
		{
			Array.Clear(_re, 0, _re.Length);
			Array.Clear(_im, 0, _im.Length);
			input.FastCopyTo(_re, input.Length);
			_fft.Direct(_re, _im);
			int num = _re.Length / 2;
			for (int i = 0; i < _re.Length; i++)
			{
				float num2 = _re[i];
				double num3 = (double)_im[i] + Math.PI * delay * (double)i / (double)num;
				_re[i] = (float)(Math.Pow(_logBase, num2) * Math.Cos(num3));
				_im[i] = (float)(Math.Pow(_logBase, num2) * Math.Sin(num3));
			}
			_fft.Inverse(_re, _im);
			_re.FastCopyTo(cepstrum, cepstrum.Length);
			if (normalize)
			{
				for (int j = 0; j < cepstrum.Length; j++)
				{
					cepstrum[j] /= _fft.Size;
				}
			}
		}

		public void RealCepstrum(float[] input, float[] cepstrum, bool normalize = true)
		{
			Array.Clear(_re, 0, _re.Length);
			Array.Clear(_im, 0, _im.Length);
			input.FastCopyTo(_re, input.Length);
			_fft.Direct(_re, _im);
			for (int i = 0; i < _re.Length; i++)
			{
				double num = Math.Sqrt(_re[i] * _re[i] + _im[i] * _im[i]);
				_re[i] = (float)Math.Log(num + 1.401298464324817E-45, _logBase);
				_im[i] = 0f;
			}
			_fft.Inverse(_re, _im);
			_re.FastCopyTo(cepstrum, Size);
			if (normalize)
			{
				for (int j = 0; j < cepstrum.Length; j++)
				{
					cepstrum[j] /= _fft.Size;
				}
			}
		}

		public void PowerCepstrum(float[] input, float[] cepstrum, bool normalize = true)
		{
			RealCepstrum(input, cepstrum, normalize);
			for (int i = 0; i < cepstrum.Length; i++)
			{
				float num = 4f * cepstrum[i] * cepstrum[i];
				cepstrum[i] = num;
			}
		}

		public void PhaseCepstrum(float[] input, float[] cepstrum, bool normalize = true)
		{
			ComplexCepstrum(input, cepstrum, normalize);
			cepstrum.FastCopyTo(_re, cepstrum.Length);
			for (int i = 0; i < cepstrum.Length; i++)
			{
				float num = cepstrum[i] - _re[cepstrum.Length - 1 - i];
				cepstrum[i] = num * num;
			}
		}

		public void Direct(float[] input, float[] output)
		{
			ComplexCepstrum(input, output, normalize: false);
		}

		public void DirectNorm(float[] input, float[] output)
		{
			ComplexCepstrum(input, output);
		}

		public void Inverse(float[] input, float[] output)
		{
			InverseComplexCepstrum(input, output, normalize: false);
		}

		public void InverseNorm(float[] input, float[] output)
		{
			InverseComplexCepstrum(input, output);
		}
	}
	public class Dct1 : IDct, ITransform
	{
		private readonly float[][] _dctMtx;

		private readonly int _dctSize;

		public int Size => _dctSize;

		public Dct1(int dctSize)
		{
			_dctSize = dctSize;
			_dctMtx = new float[dctSize][];
			double num = Math.PI / (double)(dctSize - 1);
			for (int i = 0; i < dctSize; i++)
			{
				_dctMtx[i] = new float[dctSize];
				for (int j = 1; j < dctSize - 1; j++)
				{
					_dctMtx[i][j] = 2f * (float)Math.Cos(num * (double)j * (double)i);
				}
			}
		}

		public void Direct(float[] input, float[] output)
		{
			for (int i = 0; i < output.Length; i++)
			{
				if ((i & 1) == 0)
				{
					output[i] = input[0] + input[^1];
				}
				else
				{
					output[i] = input[0] - input[^1];
				}
				for (int j = 1; j < input.Length - 1; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
			}
		}

		public void DirectNorm(float[] input, float[] output)
		{
			float num = (float)Math.Sqrt(2.0);
			float num2 = 0.5f * (float)Math.Sqrt(1.0 / (double)(_dctSize - 1));
			float num3 = num2 * num;
			for (int i = 0; i < output.Length; i++)
			{
				if ((i & 1) == 0)
				{
					output[i] = (input[0] + input[^1]) * num;
				}
				else
				{
					output[i] = (input[0] - input[^1]) * num;
				}
				for (int j = 1; j < input.Length - 1; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
				if (i > 0 && i < _dctSize - 1)
				{
					output[i] *= num3;
				}
			}
			output[0] *= num2;
			if (output.Length >= _dctSize)
			{
				output[_dctSize - 1] *= num2;
			}
		}

		public void Inverse(float[] input, float[] output)
		{
			for (int i = 0; i < output.Length; i++)
			{
				if ((i & 1) == 0)
				{
					output[i] = input[0] + input[^1];
				}
				else
				{
					output[i] = input[0] - input[^1];
				}
				for (int j = 1; j < input.Length - 1; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
			}
		}

		public void InverseNorm(float[] input, float[] output)
		{
			float num = (float)Math.Sqrt(2.0);
			float num2 = 0.5f * (float)Math.Sqrt(1.0 / (double)(_dctSize - 1));
			float num3 = num2 * num;
			for (int i = 0; i < output.Length; i++)
			{
				if ((i & 1) == 0)
				{
					output[i] = (input[0] + input[^1]) * num;
				}
				else
				{
					output[i] = (input[0] - input[^1]) * num;
				}
				for (int j = 1; j < input.Length - 1; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
				if (i > 0 && i < _dctSize - 1)
				{
					output[i] *= num3;
				}
			}
			output[0] *= num2;
			if (output.Length >= _dctSize)
			{
				output[_dctSize - 1] *= num2;
			}
		}
	}
	public class Dct2 : IDct, ITransform
	{
		private readonly float[][] _dctMtx;

		private readonly float[][] _dctMtxInv;

		private readonly int _dctSize;

		public int Size => _dctSize;

		public Dct2(int dctSize)
		{
			_dctSize = dctSize;
			_dctMtx = new float[dctSize][];
			_dctMtxInv = new float[dctSize][];
			double num = Math.PI / (double)(dctSize << 1);
			for (int i = 0; i < dctSize; i++)
			{
				_dctMtx[i] = new float[dctSize];
				for (int j = 0; j < dctSize; j++)
				{
					_dctMtx[i][j] = 2f * (float)Math.Cos((double)(((j << 1) + 1) * i) * num);
				}
			}
			for (int k = 0; k < dctSize; k++)
			{
				_dctMtxInv[k] = new float[dctSize];
				for (int l = 0; l < dctSize; l++)
				{
					_dctMtxInv[k][l] = 2f * (float)Math.Cos((double)(((k << 1) + 1) * l) * num);
				}
			}
		}

		public void Direct(float[] input, float[] output)
		{
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f;
				for (int j = 0; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
			}
		}

		public void DirectNorm(float[] input, float[] output)
		{
			float num = (float)Math.Sqrt(0.5);
			float num2 = (float)Math.Sqrt(0.5 / (double)_dctSize);
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f;
				for (int j = 0; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
				output[i] *= num2;
			}
			output[0] *= num;
		}

		public void Inverse(float[] input, float[] output)
		{
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = input[0];
				for (int j = 1; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtxInv[i][j];
				}
			}
		}

		public void InverseNorm(float[] input, float[] output)
		{
			float num = (float)Math.Sqrt(0.5);
			float num2 = (float)Math.Sqrt(0.5 / (double)_dctSize);
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = input[0] * _dctMtxInv[i][0] * num;
				for (int j = 1; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtxInv[i][j];
				}
				output[i] *= num2;
			}
		}
	}
	public class Dct3 : IDct, ITransform
	{
		private readonly float[][] _dctMtx;

		private readonly float[][] _dctMtxInv;

		private readonly int _dctSize;

		public int Size => _dctSize;

		public Dct3(int dctSize)
		{
			_dctSize = dctSize;
			_dctMtx = new float[dctSize][];
			_dctMtxInv = new float[dctSize][];
			double num = Math.PI / (double)(dctSize << 1);
			for (int i = 0; i < dctSize; i++)
			{
				_dctMtx[i] = new float[dctSize];
				for (int j = 1; j < dctSize; j++)
				{
					_dctMtx[i][j] = 2f * (float)Math.Cos((double)(((i << 1) + 1) * j) * num);
				}
			}
			for (int k = 0; k < dctSize; k++)
			{
				_dctMtxInv[k] = new float[dctSize];
				for (int l = 0; l < dctSize; l++)
				{
					_dctMtxInv[k][l] = 2f * (float)Math.Cos((double)(((l << 1) + 1) * k) * num);
				}
			}
		}

		public void Direct(float[] input, float[] output)
		{
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = input[0];
				for (int j = 1; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
			}
		}

		public void DirectNorm(float[] input, float[] output)
		{
			float num = (float)(1.0 / Math.Sqrt(_dctSize));
			float num2 = (float)Math.Sqrt(0.5 / (double)_dctSize);
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f;
				for (int j = 1; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
				output[i] *= num2;
				output[i] += input[0] * num;
			}
		}

		public void Inverse(float[] input, float[] output)
		{
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f;
				for (int j = 0; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtxInv[i][j];
				}
			}
		}

		public void InverseNorm(float[] input, float[] output)
		{
			float num = (float)Math.Sqrt(0.5);
			float num2 = (float)Math.Sqrt(0.5 / (double)_dctSize);
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f;
				for (int j = 0; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtxInv[i][j];
				}
				output[i] *= num2;
			}
			output[0] *= num;
		}
	}
	public class Dct4 : IDct, ITransform
	{
		private readonly float[][] _dctMtx;

		private readonly int _dctSize;

		public int Size => _dctSize;

		public Dct4(int dctSize)
		{
			_dctSize = dctSize;
			_dctMtx = new float[dctSize][];
			double num = Math.PI / (double)(dctSize << 2);
			for (int i = 0; i < dctSize; i++)
			{
				_dctMtx[i] = new float[dctSize];
				for (int j = 0; j < dctSize; j++)
				{
					_dctMtx[i][j] = 2f * (float)Math.Cos((double)(((i << 1) + 1) * ((j << 1) + 1)) * num);
				}
			}
		}

		public void Direct(float[] input, float[] output)
		{
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f;
				for (int j = 0; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
			}
		}

		public void DirectNorm(float[] input, float[] output)
		{
			float num = (float)(0.5 * Math.Sqrt(2.0 / (double)_dctSize));
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f;
				for (int j = 0; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
				output[i] *= num;
			}
		}

		public void Inverse(float[] input, float[] output)
		{
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f;
				for (int j = 0; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
			}
		}

		public void InverseNorm(float[] input, float[] output)
		{
			float num = (float)(0.5 * Math.Sqrt(2.0 / (double)_dctSize));
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f;
				for (int j = 0; j < input.Length; j++)
				{
					output[i] += input[j] * _dctMtx[i][j];
				}
				output[i] *= num;
			}
		}
	}
	public class FastDct2 : IDct, ITransform
	{
		private readonly Fft _fft;

		private readonly float[] _temp;

		public int Size => _fft.Size;

		public FastDct2(int dctSize)
		{
			_fft = new Fft(dctSize);
			_temp = new float[dctSize];
		}

		public void Direct(float[] input, float[] output)
		{
			Array.Clear(output, 0, output.Length);
			for (int i = 0; i < _temp.Length / 2; i++)
			{
				_temp[i] = input[2 * i];
				_temp[_temp.Length - 1 - i] = input[2 * i + 1];
			}
			_fft.Direct(_temp, output);
			int size = _fft.Size;
			for (int j = 0; j < size; j++)
			{
				output[j] = 2f * (float)((double)_temp[j] * Math.Cos(Math.PI / 2.0 * (double)j / (double)size) - (double)output[j] * Math.Sin(-Math.PI / 2.0 * (double)j / (double)size));
			}
		}

		public void DirectNorm(float[] input, float[] output)
		{
			Array.Clear(output, 0, output.Length);
			for (int i = 0; i < _temp.Length / 2; i++)
			{
				_temp[i] = input[2 * i];
				_temp[_temp.Length - 1 - i] = input[2 * i + 1];
			}
			_fft.Direct(_temp, output);
			int size = _fft.Size;
			float num = (float)Math.Sqrt(0.5 / (double)size);
			for (int j = 0; j < size; j++)
			{
				output[j] = 2f * num * (float)((double)_temp[j] * Math.Cos(Math.PI / 2.0 * (double)j / (double)size) - (double)output[j] * Math.Sin(-Math.PI / 2.0 * (double)j / (double)size));
			}
			output[0] *= (float)Math.Sqrt(0.5);
		}

		public void Inverse(float[] input, float[] output)
		{
			int size = _fft.Size;
			for (int i = 0; i < size; i++)
			{
				_temp[i] = (float)((double)input[i] * Math.Cos(Math.PI / 2.0 * (double)i / (double)size));
				output[i] = (float)((double)input[i] * Math.Sin(Math.PI / 2.0 * (double)i / (double)size));
			}
			_temp[0] *= 0.5f;
			output[0] *= 0.5f;
			_fft.Inverse(_temp, output);
			for (int j = 0; j < _temp.Length / 2; j++)
			{
				output[2 * j] = 2f * _temp[j];
				output[2 * j + 1] = 2f * _temp[size - 1 - j];
			}
		}

		public void InverseNorm(float[] input, float[] output)
		{
			Inverse(input, output);
			float num = (float)(1.0 / Math.Sqrt(_fft.Size));
			float num2 = (float)Math.Sqrt(0.5 / (double)_fft.Size);
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = (output[i] - input[0]) * num2 + input[0] * num;
			}
		}
	}
	public class FastDct3 : IDct, ITransform
	{
		private readonly FastDct2 _dct2;

		public int Size => _dct2.Size;

		public FastDct3(int dctSize)
		{
			_dct2 = new FastDct2(dctSize);
		}

		public void Direct(float[] input, float[] output)
		{
			_dct2.Inverse(input, output);
		}

		public void DirectNorm(float[] input, float[] output)
		{
			_dct2.InverseNorm(input, output);
		}

		public void Inverse(float[] input, float[] output)
		{
			_dct2.Direct(input, output);
		}

		public void InverseNorm(float[] input, float[] output)
		{
			_dct2.DirectNorm(input, output);
		}
	}
	public class FastDct4 : IDct, ITransform
	{
		private readonly Fft _fft;

		private readonly float[] _temp;

		private readonly float[] _tempRe;

		private readonly float[] _tempIm;

		public int Size => 2 * _fft.Size;

		public FastDct4(int dctSize)
		{
			int num = dctSize / 2;
			_fft = new Fft(num);
			_temp = new float[num];
			_tempRe = new float[num];
			_tempIm = new float[num];
		}

		public void Direct(float[] input, float[] output)
		{
			Array.Clear(output, 0, output.Length);
			int size = Size;
			for (int i = 0; i < _temp.Length; i++)
			{
				float num = input[2 * i];
				float num2 = input[size - 1 - 2 * i];
				double num3 = Math.Cos(Math.PI * (double)i / (double)size);
				double num4 = Math.Sin(-Math.PI * (double)i / (double)size);
				_temp[i] = 2f * (float)((double)num * num3 - (double)num2 * num4);
				output[i] = 2f * (float)((double)num * num4 + (double)num2 * num3);
			}
			_fft.Direct(_temp, output);
			for (int j = 0; j < _temp.Length; j++)
			{
				float num5 = _temp[j];
				float num6 = output[j];
				double num7 = Math.Cos(Math.PI / 2.0 * ((double)(2 * j) + 0.5) / (double)size);
				double num8 = Math.Sin(-Math.PI / 2.0 * ((double)(2 * j) + 0.5) / (double)size);
				_tempRe[j] = (float)((double)num5 * num7 - (double)num6 * num8);
				_tempIm[j] = (float)((double)num5 * num8 + (double)num6 * num7);
			}
			int num9 = 0;
			int num10 = 0;
			while (num9 < size)
			{
				output[num9] = _tempRe[num10];
				num9 += 2;
				num10++;
			}
			int num11 = 1;
			int num12 = (size - 2) / 2;
			while (num11 < size)
			{
				output[num11] = 0f - _tempIm[num12];
				num11 += 2;
				num12--;
			}
		}

		public void DirectNorm(float[] input, float[] output)
		{
			Direct(input, output);
			float num = (float)(0.5 * Math.Sqrt(2.0 / (double)Size));
			int num2 = 0;
			while (num2 < Size)
			{
				output[num2++] *= num;
			}
		}

		public void Inverse(float[] input, float[] output)
		{
			Direct(input, output);
		}

		public void InverseNorm(float[] input, float[] output)
		{
			DirectNorm(input, output);
		}
	}
	public class FastMdct : Mdct
	{
		public FastMdct(int dctSize)
			: base(dctSize, new FastDct4(dctSize))
		{
		}
	}
	public interface IDct : ITransform
	{
	}
	public class Mdct : IDct, ITransform
	{
		private readonly IDct _dct;

		private readonly float[] _temp;

		public int Size => _dct.Size;

		public Mdct(int dctSize, IDct dct = null)
		{
			_dct = dct ?? new Dct4(dctSize);
			_temp = new float[dctSize];
		}

		public void Direct(float[] input, float[] output)
		{
			int size = _dct.Size;
			for (int i = 0; i < size / 2; i++)
			{
				_temp[i] = 0f - input[3 * size / 2 - 1 - i] - input[3 * size / 2 + i];
			}
			for (int j = size / 2; j < size; j++)
			{
				_temp[j] = input[j - size / 2] - input[3 * size / 2 - 1 - j];
			}
			_dct.Direct(_temp, output);
		}

		public void DirectNorm(float[] input, float[] output)
		{
			Direct(input, output);
			float num = 2f * (float)Math.Sqrt(2 * _dct.Size);
			int num2 = 0;
			while (num2 < output.Length)
			{
				output[num2++] /= num;
			}
		}

		public void Inverse(float[] input, float[] output)
		{
			int size = _dct.Size;
			_dct.Direct(input, _temp);
			int num = size;
			int num2 = size / 2 - 1;
			while (num < 3 * size / 2 && num2 >= 0)
			{
				output[num] = 0f - _temp[num2];
				num++;
				num2--;
			}
			int num3 = 3 * size / 2;
			int num4 = 0;
			while (num3 < 2 * size && num4 < size / 2)
			{
				output[num3] = 0f - _temp[num4];
				num3++;
				num4++;
			}
			int num5 = 0;
			int num6 = size / 2;
			while (num5 < size / 2 && num6 < size)
			{
				output[num5] = _temp[num6];
				num5++;
				num6++;
			}
			int num7 = size / 2;
			int num8 = size / 2 - 1;
			while (num7 < size && num8 >= 0)
			{
				output[num7] = 0f - output[num8];
				num7++;
				num8--;
			}
		}

		public void InverseNorm(float[] input, float[] output)
		{
			Inverse(input, output);
			float num = (float)Math.Sqrt(2 * _dct.Size);
			int num2 = 0;
			while (num2 < output.Length)
			{
				output[num2++] /= num;
			}
		}
	}
	public class Fft : IComplexTransform
	{
		private readonly int _fftSize;

		private readonly float[] _cosTbl;

		private readonly float[] _sinTbl;

		private readonly float[] _realSpectrum;

		private readonly float[] _imagSpectrum;

		public int Size => _fftSize;

		public Fft(int fftSize = 512)
		{
			Guard.AgainstNotPowerOfTwo(fftSize, "FFT size");
			_fftSize = fftSize;
			_realSpectrum = new float[fftSize];
			_imagSpectrum = new float[fftSize];
			int num = (int)Math.Log(fftSize, 2.0);
			_cosTbl = new float[num];
			_sinTbl = new float[num];
			int num2 = 1;
			int num3 = 0;
			while (num2 < _fftSize)
			{
				_cosTbl[num3] = (float)Math.Cos(Math.PI * 2.0 * (double)num2 / (double)_fftSize);
				_sinTbl[num3] = (float)Math.Sin(Math.PI * 2.0 * (double)num2 / (double)_fftSize);
				num2 *= 2;
				num3++;
			}
		}

		public void Direct(float[] re, float[] im)
		{
			int num = _fftSize;
			int num2 = _fftSize >> 1;
			int num3 = _fftSize - 1;
			int num4 = 0;
			while (num >= 2)
			{
				int num5 = num >> 1;
				float num6 = 1f;
				float num7 = 0f;
				float num8 = _cosTbl[num4];
				float num9 = 0f - _sinTbl[num4];
				num4++;
				for (int i = 0; i < num5; i++)
				{
					for (int j = i; j < _fftSize; j += num)
					{
						int num10 = j + num5;
						float num11 = re[j] + re[num10];
						float num12 = im[j] + im[num10];
						float num13 = re[j] - re[num10];
						float num14 = im[j] - im[num10];
						re[num10] = num13 * num6 - num14 * num7;
						im[num10] = num14 * num6 + num13 * num7;
						re[j] = num11;
						im[j] = num12;
					}
					float num15 = num6 * num8 - num7 * num9;
					num7 = num7 * num8 + num6 * num9;
					num6 = num15;
				}
				num >>= 1;
			}
			int k = 0;
			int num16 = 0;
			for (; k < num3; k++)
			{
				if (k > num16)
				{
					float num17 = re[num16];
					float num18 = im[num16];
					re[num16] = re[k];
					im[num16] = im[k];
					re[k] = num17;
					im[k] = num18;
				}
				int num19 = num2;
				while (num16 >= num19)
				{
					num16 -= num19;
					num19 >>= 1;
				}
				num16 += num19;
			}
		}

		public void Inverse(float[] re, float[] im)
		{
			int num = _fftSize;
			int num2 = _fftSize >> 1;
			int num3 = _fftSize - 1;
			int num4 = 0;
			while (num >= 2)
			{
				int num5 = num >> 1;
				float num6 = 1f;
				float num7 = 0f;
				float num8 = _cosTbl[num4];
				float num9 = _sinTbl[num4];
				num4++;
				for (int i = 0; i < num5; i++)
				{
					for (int j = i; j < _fftSize; j += num)
					{
						int num10 = j + num5;
						float num11 = re[j] + re[num10];
						float num12 = im[j] + im[num10];
						float num13 = re[j] - re[num10];
						float num14 = im[j] - im[num10];
						re[num10] = num13 * num6 - num14 * num7;
						im[num10] = num14 * num6 + num13 * num7;
						re[j] = num11;
						im[j] = num12;
					}
					float num15 = num6 * num8 - num7 * num9;
					num7 = num7 * num8 + num6 * num9;
					num6 = num15;
				}
				num >>= 1;
			}
			int k = 0;
			int num16 = 0;
			for (; k < num3; k++)
			{
				if (k > num16)
				{
					float num17 = re[num16];
					float num18 = im[num16];
					re[num16] = re[k];
					im[num16] = im[k];
					re[k] = num17;
					im[k] = num18;
				}
				int num19 = num2;
				while (num16 >= num19)
				{
					num16 -= num19;
					num19 >>= 1;
				}
				num16 += num19;
			}
		}

		public void InverseNorm(float[] re, float[] im)
		{
			Inverse(re, im);
			for (int i = 0; i < _fftSize; i++)
			{
				re[i] /= _fftSize;
				im[i] /= _fftSize;
			}
		}

		public void Direct(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			inRe.FastCopyTo(outRe, inRe.Length);
			inIm.FastCopyTo(outIm, inIm.Length);
			Direct(outRe, outIm);
		}

		public void DirectNorm(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			Direct(inRe, inIm, outRe, outIm);
		}

		public void Inverse(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			inRe.FastCopyTo(outRe, inRe.Length);
			inIm.FastCopyTo(outIm, inIm.Length);
			Inverse(outRe, outIm);
		}

		public void InverseNorm(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			inRe.FastCopyTo(outRe, inRe.Length);
			inIm.FastCopyTo(outIm, inIm.Length);
			InverseNorm(outRe, outIm);
		}

		public void MagnitudeSpectrum(float[] samples, float[] spectrum, bool normalize = false)
		{
			Array.Clear(_realSpectrum, 0, _fftSize);
			Array.Clear(_imagSpectrum, 0, _fftSize);
			samples.FastCopyTo(_realSpectrum, Math.Min(samples.Length, _fftSize));
			Direct(_realSpectrum, _imagSpectrum);
			int num = _fftSize / 2;
			if (normalize)
			{
				spectrum[0] = Math.Abs(_realSpectrum[0]) / (float)_fftSize;
				spectrum[num] = Math.Abs(_realSpectrum[num]) / (float)_fftSize;
				for (int i = 1; i < num; i++)
				{
					spectrum[i] = (float)(Math.Sqrt(_realSpectrum[i] * _realSpectrum[i] + _imagSpectrum[i] * _imagSpectrum[i]) / (double)_fftSize);
				}
			}
			else
			{
				spectrum[0] = Math.Abs(_realSpectrum[0]);
				spectrum[num] = Math.Abs(_realSpectrum[num]);
				for (int j = 1; j < num; j++)
				{
					spectrum[j] = (float)Math.Sqrt(_realSpectrum[j] * _realSpectrum[j] + _imagSpectrum[j] * _imagSpectrum[j]);
				}
			}
		}

		public void PowerSpectrum(float[] samples, float[] spectrum, bool normalize = true)
		{
			Array.Clear(_realSpectrum, 0, _fftSize);
			Array.Clear(_imagSpectrum, 0, _fftSize);
			samples.FastCopyTo(_realSpectrum, Math.Min(samples.Length, _fftSize));
			Direct(_realSpectrum, _imagSpectrum);
			int num = _fftSize / 2;
			if (normalize)
			{
				spectrum[0] = _realSpectrum[0] * _realSpectrum[0] / (float)_fftSize;
				spectrum[num] = _realSpectrum[num] * _realSpectrum[num] / (float)_fftSize;
				for (int i = 1; i < num; i++)
				{
					spectrum[i] = (_realSpectrum[i] * _realSpectrum[i] + _imagSpectrum[i] * _imagSpectrum[i]) / (float)_fftSize;
				}
			}
			else
			{
				spectrum[0] = _realSpectrum[0] * _realSpectrum[0];
				spectrum[num] = _realSpectrum[num] * _realSpectrum[num];
				for (int j = 1; j < num; j++)
				{
					spectrum[j] = _realSpectrum[j] * _realSpectrum[j] + _imagSpectrum[j] * _imagSpectrum[j];
				}
			}
		}

		public DiscreteSignal MagnitudeSpectrum(DiscreteSignal signal, bool normalize = false)
		{
			float[] array = new float[_fftSize / 2 + 1];
			MagnitudeSpectrum(signal.Samples, array, normalize);
			return new DiscreteSignal(signal.SamplingRate, array);
		}

		public DiscreteSignal PowerSpectrum(DiscreteSignal signal, bool normalize = true)
		{
			float[] array = new float[_fftSize / 2 + 1];
			PowerSpectrum(signal.Samples, array, normalize);
			return new DiscreteSignal(signal.SamplingRate, array);
		}

		public static void Shift(float[] samples)
		{
			if ((samples.Length & 1) == 1)
			{
				throw new ArgumentException("FFT shift is not supported for arrays with odd lengths");
			}
			int num = samples.Length / 2;
			for (int i = 0; i < samples.Length / 2; i++)
			{
				int num2 = i + num;
				float num3 = samples[i];
				samples[i] = samples[num2];
				samples[num2] = num3;
			}
		}
	}
	public class Fft64
	{
		private readonly int _fftSize;

		private readonly double[] _cosTbl;

		private readonly double[] _sinTbl;

		public int Size => _fftSize;

		public Fft64(int fftSize = 512)
		{
			Guard.AgainstNotPowerOfTwo(fftSize, "FFT size");
			_fftSize = fftSize;
			int num = (int)Math.Log(fftSize, 2.0);
			_cosTbl = new double[num];
			_sinTbl = new double[num];
			int num2 = 1;
			int num3 = 0;
			while (num2 < _fftSize)
			{
				_cosTbl[num3] = Math.Cos(Math.PI * 2.0 * (double)num2 / (double)_fftSize);
				_sinTbl[num3] = Math.Sin(Math.PI * 2.0 * (double)num2 / (double)_fftSize);
				num2 *= 2;
				num3++;
			}
		}

		public void Direct(double[] re, double[] im)
		{
			int num = _fftSize;
			int num2 = _fftSize >> 1;
			int num3 = _fftSize - 1;
			int num4 = 0;
			while (num >= 2)
			{
				int num5 = num >> 1;
				double num6 = 1.0;
				double num7 = 0.0;
				double num8 = _cosTbl[num4];
				double num9 = 0.0 - _sinTbl[num4];
				num4++;
				for (int i = 0; i < num5; i++)
				{
					for (int j = i; j < _fftSize; j += num)
					{
						int num10 = j + num5;
						double num11 = re[j] + re[num10];
						double num12 = im[j] + im[num10];
						double num13 = re[j] - re[num10];
						double num14 = im[j] - im[num10];
						re[num10] = num13 * num6 - num14 * num7;
						im[num10] = num14 * num6 + num13 * num7;
						re[j] = num11;
						im[j] = num12;
					}
					double num15 = num6 * num8 - num7 * num9;
					num7 = num7 * num8 + num6 * num9;
					num6 = num15;
				}
				num >>= 1;
			}
			int k = 0;
			int num16 = 0;
			for (; k < num3; k++)
			{
				if (k > num16)
				{
					double num17 = re[num16];
					double num18 = im[num16];
					re[num16] = re[k];
					im[num16] = im[k];
					re[k] = num17;
					im[k] = num18;
				}
				int num19 = num2;
				while (num16 >= num19)
				{
					num16 -= num19;
					num19 >>= 1;
				}
				num16 += num19;
			}
		}

		public void Inverse(double[] re, double[] im)
		{
			int num = _fftSize;
			int num2 = _fftSize >> 1;
			int num3 = _fftSize - 1;
			int num4 = 0;
			while (num >= 2)
			{
				int num5 = num >> 1;
				double num6 = 1.0;
				double num7 = 0.0;
				double num8 = _cosTbl[num4];
				double num9 = _sinTbl[num4];
				num4++;
				for (int i = 0; i < num5; i++)
				{
					for (int j = i; j < _fftSize; j += num)
					{
						int num10 = j + num5;
						double num11 = re[j] + re[num10];
						double num12 = im[j] + im[num10];
						double num13 = re[j] - re[num10];
						double num14 = im[j] - im[num10];
						re[num10] = num13 * num6 - num14 * num7;
						im[num10] = num14 * num6 + num13 * num7;
						re[j] = num11;
						im[j] = num12;
					}
					double num15 = num6 * num8 - num7 * num9;
					num7 = num7 * num8 + num6 * num9;
					num6 = num15;
				}
				num >>= 1;
			}
			int k = 0;
			int num16 = 0;
			for (; k < num3; k++)
			{
				if (k > num16)
				{
					double num17 = re[num16];
					double num18 = im[num16];
					re[num16] = re[k];
					im[num16] = im[k];
					re[k] = num17;
					im[k] = num18;
				}
				int num19 = num2;
				while (num16 >= num19)
				{
					num16 -= num19;
					num19 >>= 1;
				}
				num16 += num19;
			}
		}

		public void InverseNorm(double[] re, double[] im)
		{
			Inverse(re, im);
			for (int i = 0; i < _fftSize; i++)
			{
				re[i] /= _fftSize;
				im[i] /= _fftSize;
			}
		}

		public void Direct(double[] inRe, double[] inIm, double[] outRe, double[] outIm)
		{
			inRe.FastCopyTo(outRe, inRe.Length);
			inIm.FastCopyTo(outIm, inIm.Length);
			Direct(outRe, outIm);
		}

		public void DirectNorm(double[] inRe, double[] inIm, double[] outRe, double[] outIm)
		{
			Direct(inRe, inIm, outRe, outIm);
		}

		public void Inverse(double[] inRe, double[] inIm, double[] outRe, double[] outIm)
		{
			inRe.FastCopyTo(outRe, inRe.Length);
			inIm.FastCopyTo(outIm, inIm.Length);
			Inverse(outRe, outIm);
		}

		public void InverseNorm(double[] inRe, double[] inIm, double[] outRe, double[] outIm)
		{
			inRe.FastCopyTo(outRe, inRe.Length);
			inIm.FastCopyTo(outIm, inIm.Length);
			InverseNorm(outRe, outIm);
		}
	}
	public class Goertzel
	{
		private readonly int _fftSize;

		public Goertzel(int fftSize)
		{
			_fftSize = fftSize;
		}

		public Complex Direct(float[] input, int n)
		{
			float num = (float)(2.0 * Math.Cos(Math.PI * 2.0 * (double)n / (double)_fftSize));
			float num2 = 0f;
			float num3 = 0f;
			float num4 = 0f;
			for (int i = 0; i < _fftSize; i++)
			{
				num4 = input[i] + num2 * num - num3;
				num3 = num2;
				num2 = num4;
			}
			return Complex.FromPolarCoordinates(1.0, Math.PI * 2.0 * (double)n / (double)_fftSize) * (Complex)num4 - (Complex)num2;
		}

		public Complex Direct(DiscreteSignal input, int n)
		{
			return Direct(input.Samples, n);
		}
	}
	public class HartleyTransform : ITransform
	{
		private readonly Fft _fft;

		private readonly float[] _im;

		public int Size { get; private set; }

		public HartleyTransform(int size)
		{
			Size = size;
			_fft = new Fft(size);
			_im = new float[size];
		}

		public void Direct(float[] re)
		{
			Array.Clear(_im, 0, _im.Length);
			_fft.Direct(re, _im);
			for (int i = 0; i < re.Length; i++)
			{
				re[i] -= _im[i];
			}
		}

		public void Inverse(float[] re)
		{
			Direct(re);
		}

		public void InverseNorm(float[] re)
		{
			Direct(re);
			for (int i = 0; i < re.Length; i++)
			{
				re[i] /= Size;
			}
		}

		public void Direct(float[] input, float[] output)
		{
			input.FastCopyTo(output, input.Length);
			Direct(output);
		}

		public void DirectNorm(float[] input, float[] output)
		{
			input.FastCopyTo(output, input.Length);
			Direct(output);
		}

		public void Inverse(float[] input, float[] output)
		{
			input.FastCopyTo(output, input.Length);
			Inverse(output);
		}

		public void InverseNorm(float[] input, float[] output)
		{
			input.FastCopyTo(output, input.Length);
			InverseNorm(output);
		}
	}
	public class HilbertTransform : ITransform
	{
		private readonly Fft _fft;

		private readonly float[] _re;

		private readonly float[] _im;

		public int Size { get; }

		public HilbertTransform(int size = 512)
		{
			Size = size;
			_fft = new Fft(size);
			_re = new float[size];
			_im = new float[size];
		}

		public ComplexDiscreteSignal AnalyticSignal(float[] input)
		{
			Direct(input, _im);
			for (int i = 0; i < Size; i++)
			{
				_re[i] /= Size;
				_im[i] /= Size;
			}
			return new ComplexDiscreteSignal(1, _re.ToDoubles(), _im.ToDoubles(), allocateNew: true);
		}

		public void Direct(float[] input, float[] output)
		{
			Array.Clear(_re, 0, _re.Length);
			Array.Clear(output, 0, output.Length);
			input.FastCopyTo(_re, input.Length);
			_fft.Direct(_re, output);
			for (int i = 1; i < _re.Length / 2; i++)
			{
				_re[i] *= 2f;
				output[i] *= 2f;
			}
			for (int j = _re.Length / 2 + 1; j < _re.Length; j++)
			{
				_re[j] = 0f;
				output[j] = 0f;
			}
			_fft.Inverse(_re, output);
		}

		public void DirectNorm(float[] input, float[] output)
		{
			Direct(input, output);
			for (int i = 0; i < Size; i++)
			{
				output[i] /= Size;
			}
		}

		public void Inverse(float[] input, float[] output)
		{
			Direct(input, output);
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f - output[i];
			}
		}

		public void InverseNorm(float[] input, float[] output)
		{
			DirectNorm(input, output);
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0f - output[i];
			}
		}
	}
	public class HilbertTransform64
	{
		private readonly Fft64 _fft;

		private readonly double[] _re;

		private readonly double[] _im;

		public int Size { get; }

		public HilbertTransform64(int size = 512)
		{
			Size = size;
			_fft = new Fft64(size);
			_re = new double[size];
			_im = new double[size];
		}

		public ComplexDiscreteSignal AnalyticSignal(double[] input)
		{
			Direct(input, _im);
			for (int i = 0; i < Size; i++)
			{
				_re[i] /= Size;
				_im[i] /= Size;
			}
			return new ComplexDiscreteSignal(1, _re, _im, allocateNew: true);
		}

		public void Direct(double[] input, double[] output)
		{
			Array.Clear(_re, 0, _re.Length);
			Array.Clear(output, 0, output.Length);
			input.FastCopyTo(_re, input.Length);
			_fft.Direct(_re, output);
			for (int i = 1; i < _re.Length / 2; i++)
			{
				_re[i] *= 2.0;
				output[i] *= 2.0;
			}
			for (int j = _re.Length / 2 + 1; j < _re.Length; j++)
			{
				_re[j] = 0.0;
				output[j] = 0.0;
			}
			_fft.Inverse(_re, output);
		}

		public void DirectNorm(double[] input, double[] output)
		{
			Direct(input, output);
			for (int i = 0; i < Size; i++)
			{
				output[i] /= Size;
			}
		}

		public void Inverse(double[] input, double[] output)
		{
			Direct(input, output);
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0.0 - output[i];
			}
		}

		public void InverseNorm(double[] input, double[] output)
		{
			DirectNorm(input, output);
			for (int i = 0; i < output.Length; i++)
			{
				output[i] = 0.0 - output[i];
			}
		}
	}
	public class MellinTransform : IComplexTransform
	{
		private readonly double _beta;

		private readonly float[] _linScale;

		private readonly float[] _expScale;

		private readonly RealFft _fft;

		public int InputSize { get; private set; }

		public int Size { get; private set; }

		public MellinTransform(int inputSize, int size, double beta = 0.5)
		{
			Guard.AgainstNotPowerOfTwo(size, "Output size of Mellin Transform");
			InputSize = inputSize;
			Size = size;
			_beta = beta;
			_fft = new RealFft(size);
			_linScale = (from i in Enumerable.Range(0, inputSize)
				select (float)i / (float)inputSize).ToArray();
			_expScale = new float[size];
			float num = 0f - (float)Math.Log(size);
			float num2 = (0f - num) / (float)size;
			int num3 = 0;
			while (num3 < _expScale.Length)
			{
				_expScale[num3] = (float)Math.Exp(num);
				num3++;
				num += num2;
			}
		}

		public void Direct(float[] input, float[] outRe, float[] outIm)
		{
			MathUtils.InterpolateLinear(_linScale, input, _expScale, outRe);
			for (int i = 0; i < outRe.Length; i++)
			{
				outRe[i] *= (float)Math.Pow(_expScale[i], _beta);
				outIm[i] = 0f;
			}
			_fft.Direct(outRe, outRe, outIm);
		}

		public void DirectNorm(float[] input, float[] outRe, float[] outIm)
		{
			Direct(input, outRe, outIm);
			float num = (float)(1.0 / Math.Sqrt(outRe.Length));
			for (int i = 0; i < outRe.Length; i++)
			{
				outRe[i] *= num;
				outIm[i] *= num;
			}
		}

		public void Direct(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			Direct(inRe, outRe, outIm);
		}

		public void DirectNorm(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			DirectNorm(inRe, outRe, outIm);
		}

		public void Inverse(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			throw new NotImplementedException();
		}

		public void InverseNorm(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			throw new NotImplementedException();
		}
	}
	public class RealFft : IComplexTransform
	{
		private readonly int _fftSize;

		private readonly float[] _cosTbl;

		private readonly float[] _sinTbl;

		private readonly float[] _ar;

		private readonly float[] _br;

		private readonly float[] _ai;

		private readonly float[] _bi;

		private readonly float[] _re;

		private readonly float[] _im;

		private readonly float[] _realSpectrum;

		private readonly float[] _imagSpectrum;

		public int Size => _fftSize * 2;

		public RealFft(int size)
		{
			Guard.AgainstNotPowerOfTwo(size, "Size of FFT");
			_fftSize = size / 2;
			_re = new float[_fftSize];
			_im = new float[_fftSize];
			_realSpectrum = new float[_fftSize + 1];
			_imagSpectrum = new float[_fftSize + 1];
			int num = (int)Math.Log(_fftSize, 2.0);
			_cosTbl = new float[num];
			_sinTbl = new float[num];
			int num2 = 1;
			int num3 = 0;
			while (num2 < _fftSize)
			{
				_cosTbl[num3] = (float)Math.Cos(Math.PI * 2.0 * (double)num2 / (double)_fftSize);
				_sinTbl[num3] = (float)Math.Sin(Math.PI * 2.0 * (double)num2 / (double)_fftSize);
				num2 *= 2;
				num3++;
			}
			_ar = new float[_fftSize];
			_br = new float[_fftSize];
			_ai = new float[_fftSize];
			_bi = new float[_fftSize];
			double num4 = Math.PI / (double)_fftSize;
			for (int i = 0; i < _fftSize; i++)
			{
				_ar[i] = (float)(0.5 * (1.0 - Math.Sin(num4 * (double)i)));
				_ai[i] = (float)(-0.5 * Math.Cos(num4 * (double)i));
				_br[i] = (float)(0.5 * (1.0 + Math.Sin(num4 * (double)i)));
				_bi[i] = (float)(0.5 * Math.Cos(num4 * (double)i));
			}
		}

		public void Direct(float[] input, float[] re, float[] im)
		{
			int i = 0;
			int num = 0;
			for (; i < _fftSize; i++)
			{
				_re[i] = input[num++];
				_im[i] = input[num++];
			}
			int num2 = _fftSize;
			int num3 = _fftSize >> 1;
			int num4 = _fftSize - 1;
			int num5 = 0;
			while (num2 >= 2)
			{
				int num6 = num2 >> 1;
				float num7 = 1f;
				float num8 = 0f;
				float num9 = _cosTbl[num5];
				float num10 = 0f - _sinTbl[num5];
				num5++;
				for (int j = 0; j < num6; j++)
				{
					for (int k = j; k < _fftSize; k += num2)
					{
						int num11 = k + num6;
						float num12 = _re[k] + _re[num11];
						float num13 = _im[k] + _im[num11];
						float num14 = _re[k] - _re[num11];
						float num15 = _im[k] - _im[num11];
						_re[num11] = num14 * num7 - num15 * num8;
						_im[num11] = num15 * num7 + num14 * num8;
						_re[k] = num12;
						_im[k] = num13;
					}
					float num16 = num7 * num9 - num8 * num10;
					num8 = num8 * num9 + num7 * num10;
					num7 = num16;
				}
				num2 >>= 1;
			}
			int l = 0;
			int num17 = 0;
			for (; l < num4; l++)
			{
				if (l > num17)
				{
					float num18 = _re[num17];
					float num19 = _im[num17];
					_re[num17] = _re[l];
					_im[num17] = _im[l];
					_re[l] = num18;
					_im[l] = num19;
				}
				int num20 = num3;
				while (num17 >= num20)
				{
					num17 -= num20;
					num20 >>= 1;
				}
				num17 += num20;
			}
			re[0] = _re[0] * _ar[0] - _im[0] * _ai[0] + _re[0] * _br[0] + _im[0] * _bi[0];
			im[0] = _im[0] * _ar[0] + _re[0] * _ai[0] + _re[0] * _bi[0] - _im[0] * _br[0];
			for (int m = 1; m < _fftSize; m++)
			{
				re[m] = _re[m] * _ar[m] - _im[m] * _ai[m] + _re[_fftSize - m] * _br[m] + _im[_fftSize - m] * _bi[m];
				im[m] = _im[m] * _ar[m] + _re[m] * _ai[m] + _re[_fftSize - m] * _bi[m] - _im[_fftSize - m] * _br[m];
			}
			re[_fftSize] = _re[0] - _im[0];
			im[_fftSize] = 0f;
		}

		public void Inverse(float[] re, float[] im, float[] output)
		{
			for (int i = 0; i < _fftSize; i++)
			{
				_re[i] = re[i] * _ar[i] + im[i] * _ai[i] + re[_fftSize - i] * _br[i] - im[_fftSize - i] * _bi[i];
				_im[i] = im[i] * _ar[i] - re[i] * _ai[i] - re[_fftSize - i] * _bi[i] - im[_fftSize - i] * _br[i];
			}
			int num = _fftSize;
			int num2 = _fftSize >> 1;
			int num3 = _fftSize - 1;
			int num4 = 0;
			while (num >= 2)
			{
				int num5 = num >> 1;
				float num6 = 1f;
				float num7 = 0f;
				float num8 = _cosTbl[num4];
				float num9 = _sinTbl[num4];
				num4++;
				for (int j = 0; j < num5; j++)
				{
					for (int k = j; k < _fftSize; k += num)
					{
						int num10 = k + num5;
						float num11 = _re[k] + _re[num10];
						float num12 = _im[k] + _im[num10];
						float num13 = _re[k] - _re[num10];
						float num14 = _im[k] - _im[num10];
						_re[num10] = num13 * num6 - num14 * num7;
						_im[num10] = num14 * num6 + num13 * num7;
						_re[k] = num11;
						_im[k] = num12;
					}
					float num15 = num6 * num8 - num7 * num9;
					num7 = num7 * num8 + num6 * num9;
					num6 = num15;
				}
				num >>= 1;
			}
			int l = 0;
			int num16 = 0;
			for (; l < num3; l++)
			{
				if (l > num16)
				{
					float num17 = _re[num16];
					float num18 = _im[num16];
					_re[num16] = _re[l];
					_im[num16] = _im[l];
					_re[l] = num17;
					_im[l] = num18;
				}
				int num19 = num2;
				while (num16 >= num19)
				{
					num16 -= num19;
					num19 >>= 1;
				}
				num16 += num19;
			}
			int m = 0;
			int num20 = 0;
			for (; m < _fftSize; m++)
			{
				output[num20++] = _re[m] * 2f;
				output[num20++] = _im[m] * 2f;
			}
		}

		public void InverseNorm(float[] re, float[] im, float[] output)
		{
			for (int i = 0; i < _fftSize; i++)
			{
				_re[i] = re[i] * _ar[i] + im[i] * _ai[i] + re[_fftSize - i] * _br[i] - im[_fftSize - i] * _bi[i];
				_im[i] = im[i] * _ar[i] - re[i] * _ai[i] - re[_fftSize - i] * _bi[i] - im[_fftSize - i] * _br[i];
			}
			int num = _fftSize;
			int num2 = _fftSize >> 1;
			int num3 = _fftSize - 1;
			int num4 = 0;
			while (num >= 2)
			{
				int num5 = num >> 1;
				float num6 = 1f;
				float num7 = 0f;
				float num8 = _cosTbl[num4];
				float num9 = _sinTbl[num4];
				num4++;
				for (int j = 0; j < num5; j++)
				{
					for (int k = j; k < _fftSize; k += num)
					{
						int num10 = k + num5;
						float num11 = _re[k] + _re[num10];
						float num12 = _im[k] + _im[num10];
						float num13 = _re[k] - _re[num10];
						float num14 = _im[k] - _im[num10];
						_re[num10] = num13 * num6 - num14 * num7;
						_im[num10] = num14 * num6 + num13 * num7;
						_re[k] = num11;
						_im[k] = num12;
					}
					float num15 = num6 * num8 - num7 * num9;
					num7 = num7 * num8 + num6 * num9;
					num6 = num15;
				}
				num >>= 1;
			}
			int l = 0;
			int num16 = 0;
			for (; l < num3; l++)
			{
				if (l > num16)
				{
					float num17 = _re[num16];
					float num18 = _im[num16];
					_re[num16] = _re[l];
					_im[num16] = _im[l];
					_re[l] = num17;
					_im[l] = num18;
				}
				int num19 = num2;
				while (num16 >= num19)
				{
					num16 -= num19;
					num19 >>= 1;
				}
				num16 += num19;
			}
			int m = 0;
			int num20 = 0;
			for (; m < _fftSize; m++)
			{
				output[num20++] = _re[m] / (float)_fftSize;
				output[num20++] = _im[m] / (float)_fftSize;
			}
		}

		public void Direct(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			Direct(inRe, outRe, outIm);
		}

		public void DirectNorm(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			Direct(inRe, outRe, outIm);
		}

		public void Inverse(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			Inverse(inRe, inIm, outRe);
		}

		public void InverseNorm(float[] inRe, float[] inIm, float[] outRe, float[] outIm)
		{
			InverseNorm(inRe, inIm, outRe);
		}

		public void MagnitudeSpectrum(float[] samples, float[] spectrum, bool normalize = false)
		{
			Direct(samples, _realSpectrum, _imagSpectrum);
			if (normalize)
			{
				for (int i = 0; i < spectrum.Length; i++)
				{
					spectrum[i] = (float)(Math.Sqrt(_realSpectrum[i] * _realSpectrum[i] + _imagSpectrum[i] * _imagSpectrum[i]) / (double)_fftSize);
				}
			}
			else
			{
				for (int j = 0; j < spectrum.Length; j++)
				{
					spectrum[j] = (float)Math.Sqrt(_realSpectrum[j] * _realSpectrum[j] + _imagSpectrum[j] * _imagSpectrum[j]);
				}
			}
		}

		public void PowerSpectrum(float[] samples, float[] spectrum, bool normalize = true)
		{
			Direct(samples, _realSpectrum, _imagSpectrum);
			if (normalize)
			{
				for (int i = 0; i < spectrum.Length; i++)
				{
					spectrum[i] = (_realSpectrum[i] * _realSpectrum[i] + _imagSpectrum[i] * _imagSpectrum[i]) / (float)_fftSize;
				}
			}
			else
			{
				for (int j = 0; j < spectrum.Length; j++)
				{
					spectrum[j] = _realSpectrum[j] * _realSpectrum[j] + _imagSpectrum[j] * _imagSpectrum[j];
				}
			}
		}

		public DiscreteSignal MagnitudeSpectrum(DiscreteSignal signal, bool normalize = false)
		{
			float[] array = new float[_fftSize + 1];
			MagnitudeSpectrum(signal.Samples, array, normalize);
			return new DiscreteSignal(signal.SamplingRate, array);
		}

		public DiscreteSignal PowerSpectrum(DiscreteSignal signal, bool normalize = true)
		{
			float[] array = new float[_fftSize + 1];
			PowerSpectrum(signal.Samples, array, normalize);
			return new DiscreteSignal(signal.SamplingRate, array);
		}

		public static void Shift(float[] samples)
		{
			if ((samples.Length & 1) == 1)
			{
				throw new ArgumentException("FFT shift is not supported for arrays with odd lengths");
			}
			int num = samples.Length / 2;
			for (int i = 0; i < samples.Length / 2; i++)
			{
				int num2 = i + num;
				float num3 = samples[i];
				samples[i] = samples[num2];
				samples[num2] = num3;
			}
		}
	}
	public class RealFft64
	{
		private readonly int _fftSize;

		private readonly double[] _cosTbl;

		private readonly double[] _sinTbl;

		private readonly double[] _ar;

		private readonly double[] _br;

		private readonly double[] _ai;

		private readonly double[] _bi;

		private readonly double[] _re;

		private readonly double[] _im;

		public int Size => _fftSize * 2;

		public RealFft64(int size)
		{
			Guard.AgainstNotPowerOfTwo(size, "Size of FFT");
			_fftSize = size / 2;
			_re = new double[_fftSize];
			_im = new double[_fftSize];
			int num = (int)Math.Log(_fftSize, 2.0);
			_cosTbl = new double[num];
			_sinTbl = new double[num];
			int num2 = 1;
			int num3 = 0;
			while (num2 < _fftSize)
			{
				_cosTbl[num3] = Math.Cos(Math.PI * 2.0 * (double)num2 / (double)_fftSize);
				_sinTbl[num3] = Math.Sin(Math.PI * 2.0 * (double)num2 / (double)_fftSize);
				num2 *= 2;
				num3++;
			}
			_ar = new double[_fftSize];
			_br = new double[_fftSize];
			_ai = new double[_fftSize];
			_bi = new double[_fftSize];
			double num4 = Math.PI / (double)_fftSize;
			for (int i = 0; i < _fftSize; i++)
			{
				_ar[i] = 0.5 * (1.0 - Math.Sin(num4 * (double)i));
				_ai[i] = -0.5 * Math.Cos(num4 * (double)i);
				_br[i] = 0.5 * (1.0 + Math.Sin(num4 * (double)i));
				_bi[i] = 0.5 * Math.Cos(num4 * (double)i);
			}
		}

		public void Direct(double[] input, double[] re, double[] im)
		{
			int i = 0;
			int num = 0;
			for (; i < _fftSize; i++)
			{
				_re[i] = input[num++];
				_im[i] = input[num++];
			}
			int num2 = _fftSize;
			int num3 = _fftSize >> 1;
			int num4 = _fftSize - 1;
			int num5 = 0;
			while (num2 >= 2)
			{
				int num6 = num2 >> 1;
				double num7 = 1.0;
				double num8 = 0.0;
				double num9 = _cosTbl[num5];
				double num10 = 0.0 - _sinTbl[num5];
				num5++;
				for (int j = 0; j < num6; j++)
				{
					for (int k = j; k < _fftSize; k += num2)
					{
						int num11 = k + num6;
						double num12 = _re[k] + _re[num11];
						double num13 = _im[k] + _im[num11];
						double num14 = _re[k] - _re[num11];
						double num15 = _im[k] - _im[num11];
						_re[num11] = num14 * num7 - num15 * num8;
						_im[num11] = num15 * num7 + num14 * num8;
						_re[k] = num12;
						_im[k] = num13;
					}
					double num16 = num7 * num9 - num8 * num10;
					num8 = num8 * num9 + num7 * num10;
					num7 = num16;
				}
				num2 >>= 1;
			}
			int l = 0;
			int num17 = 0;
			for (; l < num4; l++)
			{
				if (l > num17)
				{
					double num18 = _re[num17];
					double num19 = _im[num17];
					_re[num17] = _re[l];
					_im[num17] = _im[l];
					_re[l] = num18;
					_im[l] = num19;
				}
				int num20 = num3;
				while (num17 >= num20)
				{
					num17 -= num20;
					num20 >>= 1;
				}
				num17 += num20;
			}
			re[0] = _re[0] * _ar[0] - _im[0] * _ai[0] + _re[0] * _br[0] + _im[0] * _bi[0];
			im[0] = _im[0] * _ar[0] + _re[0] * _ai[0] + _re[0] * _bi[0] - _im[0] * _br[0];
			for (int m = 1; m < _fftSize; m++)
			{
				re[m] = _re[m] * _ar[m] - _im[m] * _ai[m] + _re[_fftSize - m] * _br[m] + _im[_fftSize - m] * _bi[m];
				im[m] = _im[m] * _ar[m] + _re[m] * _ai[m] + _re[_fftSize - m] * _bi[m] - _im[_fftSize - m] * _br[m];
			}
			re[_fftSize] = _re[0] - _im[0];
			im[_fftSize] = 0.0;
		}

		public void Inverse(double[] re, double[] im, double[] output)
		{
			for (int i = 0; i < _fftSize; i++)
			{
				_re[i] = re[i] * _ar[i] + im[i] * _ai[i] + re[_fftSize - i] * _br[i] - im[_fftSize - i] * _bi[i];
				_im[i] = im[i] * _ar[i] - re[i] * _ai[i] - re[_fftSize - i] * _bi[i] - im[_fftSize - i] * _br[i];
			}
			int num = _fftSize;
			int num2 = _fftSize >> 1;
			int num3 = _fftSize - 1;
			int num4 = 0;
			while (num >= 2)
			{
				int num5 = num >> 1;
				double num6 = 1.0;
				double num7 = 0.0;
				double num8 = _cosTbl[num4];
				double num9 = _sinTbl[num4];
				num4++;
				for (int j = 0; j < num5; j++)
				{
					for (int k = j; k < _fftSize; k += num)
					{
						int num10 = k + num5;
						double num11 = _re[k] + _re[num10];
						double num12 = _im[k] + _im[num10];
						double num13 = _re[k] - _re[num10];
						double num14 = _im[k] - _im[num10];
						_re[num10] = num13 * num6 - num14 * num7;
						_im[num10] = num14 * num6 + num13 * num7;
						_re[k] = num11;
						_im[k] = num12;
					}
					double num15 = num6 * num8 - num7 * num9;
					num7 = num7 * num8 + num6 * num9;
					num6 = num15;
				}
				num >>= 1;
			}
			int l = 0;
			int num16 = 0;
			for (; l < num3; l++)
			{
				if (l > num16)
				{
					double num17 = _re[num16];
					double num18 = _im[num16];
					_re[num16] = _re[l];
					_im[num16] = _im[l];
					_re[l] = num17;
					_im[l] = num18;
				}
				int num19 = num2;
				while (num16 >= num19)
				{
					num16 -= num19;
					num19 >>= 1;
				}
				num16 += num19;
			}
			int m = 0;
			int num20 = 0;
			for (; m < _fftSize; m++)
			{
				output[num20++] = _re[m] * 2.0;
				output[num20++] = _im[m] * 2.0;
			}
		}

		public void InverseNorm(double[] re, double[] im, double[] output)
		{
			for (int i = 0; i < _fftSize; i++)
			{
				_re[i] = re[i] * _ar[i] + im[i] * _ai[i] + re[_fftSize - i] * _br[i] - im[_fftSize - i] * _bi[i];
				_im[i] = im[i] * _ar[i] - re[i] * _ai[i] - re[_fftSize - i] * _bi[i] - im[_fftSize - i] * _br[i];
			}
			int num = _fftSize;
			int num2 = _fftSize >> 1;
			int num3 = _fftSize - 1;
			int num4 = 0;
			while (num >= 2)
			{
				int num5 = num >> 1;
				double num6 = 1.0;
				double num7 = 0.0;
				double num8 = _cosTbl[num4];
				double num9 = _sinTbl[num4];
				num4++;
				for (int j = 0; j < num5; j++)
				{
					for (int k = j; k < _fftSize; k += num)
					{
						int num10 = k + num5;
						double num11 = _re[k] + _re[num10];
						double num12 = _im[k] + _im[num10];
						double num13 = _re[k] - _re[num10];
						double num14 = _im[k] - _im[num10];
						_re[num10] = num13 * num6 - num14 * num7;
						_im[num10] = num14 * num6 + num13 * num7;
						_re[k] = num11;
						_im[k] = num12;
					}
					double num15 = num6 * num8 - num7 * num9;
					num7 = num7 * num8 + num6 * num9;
					num6 = num15;
				}
				num >>= 1;
			}
			int l = 0;
			int num16 = 0;
			for (; l < num3; l++)
			{
				if (l > num16)
				{
					double num17 = _re[num16];
					double num18 = _im[num16];
					_re[num16] = _re[l];
					_im[num16] = _im[l];
					_re[l] = num17;
					_im[l] = num18;
				}
				int num19 = num2;
				while (num16 >= num19)
				{
					num16 -= num19;
					num19 >>= 1;
				}
				num16 += num19;
			}
			int m = 0;
			int num20 = 0;
			for (; m < _fftSize; m++)
			{
				output[num20++] = _re[m] / (double)_fftSize;
				output[num20++] = _im[m] / (double)_fftSize;
			}
		}

		public void Direct(double[] inRe, double[] inIm, double[] outRe, double[] outIm)
		{
			Direct(inRe, outRe, outIm);
		}

		public void DirectNorm(double[] inRe, double[] inIm, double[] outRe, double[] outIm)
		{
			Direct(inRe, outRe, outIm);
		}

		public void Inverse(double[] inRe, double[] inIm, double[] outRe, double[] outIm)
		{
			Inverse(inRe, inIm, outRe);
		}

		public void InverseNorm(double[] inRe, double[] inIm, double[] outRe, double[] outIm)
		{
			InverseNorm(inRe, inIm, outRe);
		}
	}
	public class Stft
	{
		private readonly int _fftSize;

		private readonly RealFft _fft;

		private readonly int _hopSize;

		private readonly int _windowSize;

		private readonly WindowType _window;

		private readonly float[] _windowSamples;

		public int Size => _fftSize;

		public Stft(int windowSize = 1024, int hopSize = 256, WindowType window = WindowType.Hann, int fftSize = 0)
		{
			_fftSize = ((fftSize >= windowSize) ? fftSize : MathUtils.NextPowerOfTwo(windowSize));
			_fft = new RealFft(_fftSize);
			_hopSize = hopSize;
			_windowSize = windowSize;
			_window = window;
			_windowSamples = Window.OfType(_window, _windowSize);
		}

		public List<(float[], float[])> Direct(float[] input)
		{
			int num = ((input.Length >= _windowSize) ? ((input.Length - _windowSize) / _hopSize + 1) : 0);
			List<(float[], float[])> list = new List<(float[], float[])>(num + 1);
			for (int i = 0; i < num; i++)
			{
				list.Add((new float[_fftSize], new float[_fftSize]));
			}
			float[] array = new float[_fftSize];
			int num2 = 0;
			for (int j = 0; j < num; j++)
			{
				input.FastCopyTo(array, _windowSize, num2);
				array.ApplyWindow(_windowSamples);
				var (re, im) = list[j];
				_fft.Direct(array, re, im);
				num2 += _hopSize;
			}
			list.Add((new float[_fftSize], new float[_fftSize]));
			Array.Clear(array, 0, _fftSize);
			input.FastCopyTo(array, input.Length - num2, num2);
			array.ApplyWindow(_windowSamples);
			var (re2, im2) = list.Last();
			_fft.Direct(array, re2, im2);
			return list;
		}

		public List<(float[], float[])> Direct(DiscreteSignal signal)
		{
			return Direct(signal.Samples);
		}

		public float[] Inverse(List<(float[], float[])> stft, bool perfectReconstruction = true)
		{
			int count = stft.Count;
			float[] array = new float[count * _hopSize + _fftSize];
			float[] array2 = new float[_fftSize];
			float num;
			if (perfectReconstruction)
			{
				Guard.AgainstExceedance(_hopSize, _windowSize, "Hop size for perfect reconstruction", "window size");
				num = 1f / (float)_windowSize;
			}
			else
			{
				num = 1f / ((float)_fftSize * _windowSamples.Select((float w) => w * w).Sum() / (float)_hopSize);
			}
			int num2 = 0;
			for (int num3 = 0; num3 < count; num3++)
			{
				var (re, im) = stft[num3];
				_fft.Inverse(re, im, array2);
				for (int num4 = 0; num4 < _windowSize; num4++)
				{
					array[num2 + num4] += array2[num4] * _windowSamples[num4];
				}
				for (int num5 = 0; num5 < _hopSize; num5++)
				{
					array[num2 + num5] *= num;
				}
				num2 += _hopSize;
			}
			for (int num6 = 0; num6 < _windowSize; num6++)
			{
				array[num2 + num6] *= num;
			}
			if (perfectReconstruction)
			{
				float[] array3 = ComputeWindowSummed();
				int num7 = _windowSize - _hopSize;
				int num8 = 0;
				int num9 = array.Length - _hopSize - 1;
				while (num8 < num7)
				{
					if ((double)Math.Abs(array3[num8]) > 1E-30)
					{
						array[num8] /= array3[num8];
						array[num9] /= array3[num8];
					}
					num8++;
					num9--;
				}
				int num10 = num7;
				int num11 = num7;
				while (num10 < array.Length - _windowSize)
				{
					if (num11 == _windowSize)
					{
						num11 = num7;
					}
					array[num10] /= array3[num11];
					num10++;
					num11++;
				}
			}
			return array;
		}

		private float[] ComputeWindowSummed()
		{
			float[] array = new float[_windowSize];
			for (int i = 0; i < _windowSize; i += _hopSize)
			{
				for (int j = 0; i + j < _windowSize; j++)
				{
					array[i + j] += _windowSamples[j] * _windowSamples[j];
				}
			}
			return array;
		}

		public List<float[]> Spectrogram(float[] input, bool normalize = true)
		{
			int num = ((input.Length >= _windowSize) ? ((input.Length - _windowSize) / _hopSize + 1) : 0);
			List<float[]> list = new List<float[]>(num + 1);
			for (int i = 0; i < num; i++)
			{
				list.Add(new float[_fftSize / 2 + 1]);
			}
			float[] array = new float[_fftSize];
			int num2 = 0;
			for (int j = 0; j < num; j++)
			{
				input.FastCopyTo(array, _windowSize, num2);
				if (_window != WindowType.Rectangular)
				{
					array.ApplyWindow(_windowSamples);
				}
				_fft.PowerSpectrum(array, list[j], normalize);
				num2 += _hopSize;
			}
			Array.Clear(array, 0, _fftSize);
			input.FastCopyTo(array, input.Length - num2, num2);
			array.ApplyWindow(_windowSamples);
			list.Add(new float[_fftSize / 2 + 1]);
			_fft.PowerSpectrum(array, list.Last(), normalize);
			return list;
		}

		public List<float[]> Spectrogram(DiscreteSignal signal, bool normalize = true)
		{
			return Spectrogram(signal.Samples, normalize);
		}

		public float[] AveragePeriodogram(float[] input)
		{
			int num = ((input.Length >= _windowSize) ? ((input.Length - _windowSize) / _hopSize + 1) : 0);
			float[] array = new float[_fftSize / 2 + 1];
			float[] array2 = new float[_fftSize / 2 + 1];
			float[] array3 = new float[_fftSize];
			int num2 = 0;
			for (int i = 0; i < num; i++)
			{
				input.FastCopyTo(array3, _windowSize, num2);
				if (_window != WindowType.Rectangular)
				{
					array3.ApplyWindow(_windowSamples);
				}
				_fft.PowerSpectrum(array3, array, normalize: false);
				for (int j = 0; j < array2.Length; j++)
				{
					array2[j] += array[j];
				}
				num2 += _hopSize;
			}
			Array.Clear(array3, 0, _fftSize);
			input.FastCopyTo(array3, input.Length - num2, num2);
			array3.ApplyWindow(_windowSamples);
			_fft.PowerSpectrum(array3, array, normalize: false);
			for (int k = 0; k < array2.Length; k++)
			{
				array2[k] += array[k];
				array2[k] /= num + 1;
			}
			return array2;
		}

		public MagnitudePhaseList MagnitudePhaseSpectrogram(float[] input)
		{
			int num = ((input.Length >= _windowSize) ? ((input.Length - _windowSize) / _hopSize + 1) : 0);
			List<float[]> list = new List<float[]>(num + 1);
			List<float[]> list2 = new List<float[]>(num + 1);
			for (int i = 0; i < num; i++)
			{
				list.Add(new float[_fftSize / 2 + 1]);
				list2.Add(new float[_fftSize / 2 + 1]);
			}
			float[] array = new float[_fftSize];
			float[] array2 = new float[_fftSize / 2 + 1];
			float[] array3 = new float[_fftSize / 2 + 1];
			int num2 = 0;
			for (int j = 0; j < num; j++)
			{
				input.FastCopyTo(array, _windowSize, num2);
				array.ApplyWindow(_windowSamples);
				_fft.Direct(array, array2, array3);
				for (int k = 0; k <= _fftSize / 2; k++)
				{
					list[j][k] = (float)Math.Sqrt(array2[k] * array2[k] + array3[k] * array3[k]);
					list2[j][k] = (float)Math.Atan2(array3[k], array2[k]);
				}
				num2 += _hopSize;
			}
			Array.Clear(array, 0, _fftSize);
			input.FastCopyTo(array, input.Length - num2, num2);
			array.ApplyWindow(_windowSamples);
			list.Add(new float[_fftSize / 2 + 1]);
			list2.Add(new float[_fftSize / 2 + 1]);
			_fft.Direct(array, array2, array3);
			float[] array4 = list.Last();
			float[] array5 = list2.Last();
			for (int l = 0; l <= _fftSize / 2; l++)
			{
				array4[l] = (float)Math.Sqrt(array2[l] * array2[l] + array3[l] * array3[l]);
				array5[l] = (float)Math.Atan2(array3[l], array2[l]);
			}
			return new MagnitudePhaseList
			{
				Magnitudes = list,
				Phases = list2
			};
		}

		public MagnitudePhaseList MagnitudePhaseSpectrogram(DiscreteSignal signal)
		{
			return MagnitudePhaseSpectrogram(signal.Samples);
		}

		public float[] ReconstructMagnitudePhase(MagnitudePhaseList spectrogram, bool perfectReconstruction = true)
		{
			int count = spectrogram.Magnitudes.Count;
			float[] array = new float[count * _hopSize + _windowSize];
			List<float[]> magnitudes = spectrogram.Magnitudes;
			List<float[]> phases = spectrogram.Phases;
			float[] array2 = new float[_fftSize];
			float[] array3 = new float[_fftSize / 2 + 1];
			float[] array4 = new float[_fftSize / 2 + 1];
			float num;
			if (perfectReconstruction)
			{
				Guard.AgainstExceedance(_hopSize, _windowSize, "Hop size for perfect reconstruction", "window size");
				num = 1f / (float)_windowSize;
			}
			else
			{
				num = 1f / ((float)_fftSize * _windowSamples.Select((float w) => w * w).Sum() / (float)_hopSize);
			}
			int num2 = 0;
			for (int num3 = 0; num3 < count; num3++)
			{
				for (int num4 = 0; num4 <= _fftSize / 2; num4++)
				{
					array3[num4] = (float)((double)magnitudes[num3][num4] * Math.Cos(phases[num3][num4]));
					array4[num4] = (float)((double)magnitudes[num3][num4] * Math.Sin(phases[num3][num4]));
				}
				_fft.Inverse(array3, array4, array2);
				for (int num5 = 0; num5 < _windowSize; num5++)
				{
					array[num2 + num5] += array2[num5] * _windowSamples[num5];
				}
				for (int num6 = 0; num6 < _hopSize; num6++)
				{
					array[num2 + num6] *= num;
				}
				num2 += _hopSize;
			}
			for (int num7 = 0; num7 < _windowSize; num7++)
			{
				array[num2 + num7] *= num;
			}
			if (perfectReconstruction)
			{
				float[] array5 = ComputeWindowSummed();
				int num8 = _windowSize - _hopSize;
				int num9 = 0;
				int num10 = array.Length - _hopSize - 1;
				while (num9 < num8)
				{
					if ((double)Math.Abs(array5[num9]) > 1E-30)
					{
						array[num9] /= array5[num9];
						array[num10] /= array5[num9];
					}
					num9++;
					num10--;
				}
				int num11 = num8;
				int num12 = num8;
				while (num11 < array.Length - _windowSize)
				{
					if (num12 == _windowSize)
					{
						num12 = num8;
					}
					array[num11] /= array5[num12];
					num11++;
					num12++;
				}
			}
			return array;
		}
	}
	public struct MagnitudePhaseList
	{
		public List<float[]> Magnitudes { get; set; }

		public List<float[]> Phases { get; set; }
	}
}
namespace NWaves.Transforms.Wavelets
{
	public class Fwt : ITransform
	{
		protected int _waveletLength;

		protected float[] _loD;

		protected float[] _hiD;

		protected float[] _loR;

		protected float[] _hiR;

		protected float[] _temp;

		public int Size { get; protected set; }

		public Fwt(int size, Wavelet wavelet)
		{
			Size = size;
			_waveletLength = wavelet.Length;
			_loD = wavelet.LoD.Reverse().ToArray();
			_hiD = wavelet.HiD.Reverse().ToArray();
			_loR = wavelet.LoR.ToArray();
			_hiR = wavelet.HiR.ToArray();
			_temp = new float[size];
		}

		public void Direct(float[] input, float[] output)
		{
			Direct(input, output, 0);
		}

		public void DirectNorm(float[] input, float[] output)
		{
			Direct(input, output, 0);
		}

		public void Inverse(float[] input, float[] output)
		{
			Inverse(input, output, 0);
		}

		public void InverseNorm(float[] input, float[] output)
		{
			Inverse(input, output, 0);
		}

		public void Direct(float[] input, float[] output, int level)
		{
			int num = MaxLevel(input.Length);
			if (level <= 0)
			{
				level = num;
			}
			else if (level > num)
			{
				throw new ArgumentException($"Specified level is too large for input array. Max level is {num}");
			}
			input.FastCopyTo(_temp, input.Length);
			bool flag = _waveletLength / 2 % 2 == 0;
			int num2 = input.Length;
			int num3 = 0;
			while (num3 < level && num2 >= _waveletLength)
			{
				int num4 = num2 / 2;
				int num5 = (flag ? (num2 - 1) : 0);
				int num6 = (_waveletLength - 1) / 4;
				int num7 = 0;
				while (num7 < num4)
				{
					if (num6 == num4)
					{
						num6 = 0;
					}
					output[num6] = (output[num6 + num4] = 0f);
					for (int i = 0; i < _waveletLength; i++)
					{
						int num8 = (num7 * 2 + i + num5) % num2;
						output[num6] += _temp[num8] * _loD[i];
						output[num6 + num4] += _temp[num8] * _hiD[i];
					}
					num7++;
					num6++;
				}
				output.FastCopyTo(_temp, num2);
				num3++;
				num2 /= 2;
			}
		}

		public void Inverse(float[] input, float[] output, int level)
		{
			int num = MaxLevel(input.Length);
			if (level <= 0)
			{
				level = num;
			}
			else if (level > num)
			{
				throw new ArgumentException($"Specified level is too large for input array. Max level is {num}");
			}
			input.FastCopyTo(_temp, input.Length);
			bool flag = _waveletLength / 2 % 2 == 0;
			for (int num2 = (int)((double)input.Length / Math.Pow(2.0, level - 1)); num2 <= input.Length; num2 *= 2)
			{
				Array.Clear(output, 0, output.Length);
				int num3 = num2 / 2;
				int num4 = (flag ? (num2 - 1) : 0);
				int num5 = (_waveletLength - 1) / 4;
				int num6 = 0;
				while (num6 < num3)
				{
					if (num5 == num3)
					{
						num5 = 0;
					}
					for (int i = 0; i < _waveletLength; i++)
					{
						int num7 = (num6 * 2 + i + num4) % num2;
						output[num7] += _temp[num5] * _loR[i] + _temp[num5 + num3] * _hiR[i];
					}
					num6++;
					num5++;
				}
				output.FastCopyTo(_temp, num2);
			}
		}

		public int MaxLevel(int length)
		{
			return (int)Math.Log(length / (_waveletLength - 1), 2.0);
		}
	}
	public class Wavelet
	{
		public string Name { get; protected set; }

		public int Length { get; protected set; }

		public float[] LoD { get; protected set; }

		public float[] HiD { get; protected set; }

		public float[] LoR { get; protected set; }

		public float[] HiR { get; protected set; }

		public Wavelet(WaveletFamily waveletFamily, int taps = 1)
		{
			MakeWavelet(waveletFamily, taps);
		}

		public Wavelet(string name)
		{
			int taps = 1;
			name = name.ToLower();
			WaveletFamily waveletFamily;
			if (name == "haar")
			{
				waveletFamily = WaveletFamily.Haar;
			}
			else
			{
				int num = -1;
				for (int i = 0; i < name.Length; i++)
				{
					if (char.IsDigit(name[i]))
					{
						num = i;
						break;
					}
				}
				string text = name;
				if (num < 0)
				{
					taps = 1;
				}
				else
				{
					text = name.Substring(0, num);
					taps = int.Parse(name.Substring(num));
				}
				waveletFamily = text switch
				{
					"db" => WaveletFamily.Daubechies, 
					"sym" => WaveletFamily.Symlet, 
					"coif" => WaveletFamily.Coiflet, 
					_ => throw new ArgumentException("Unrecognized wavelet name: " + name), 
				};
			}
			MakeWavelet(waveletFamily, taps);
		}

		public Wavelet(IEnumerable<float> loD, IEnumerable<float> hiD, IEnumerable<float> loR, IEnumerable<float> hiR)
		{
			LoD = loD.ToArray();
			HiD = hiD.ToArray();
			LoR = loR.ToArray();
			HiR = hiR.ToArray();
			Guard.AgainstInequality(LoD.Length, HiD.Length, "LP coeffs for decomposition", "HP coeffs for decomposition");
			Guard.AgainstInequality(LoD.Length, LoR.Length, "LP coeffs for decomposition", "LP coeffs for reconstruction");
			Guard.AgainstInequality(LoD.Length, HiR.Length, "LP coeffs for decomposition", "HP coeffs for reconstruction");
			Name = "custom";
			Length = LoD.Length;
		}

		private void MakeWavelet(WaveletFamily waveletFamily, int taps)
		{
			switch (waveletFamily)
			{
			case WaveletFamily.Daubechies:
				MakeDaubechiesWavelet(taps);
				break;
			case WaveletFamily.Symlet:
				MakeSymletWavelet(taps);
				break;
			case WaveletFamily.Coiflet:
				MakeCoifletWavelet(taps);
				break;
			default:
				MakeHaarWavelet();
				break;
			}
			ComputeOrthonormalCoeffs();
		}

		public void ComputeOrthonormalCoeffs()
		{
			HiD = LoD.Reverse().ToArray();
			for (int i = 0; i < HiD.Length; i += 2)
			{
				HiD[i] = 0f - HiD[i];
			}
			LoR = LoD.Reverse().ToArray();
			HiR = HiD.Reverse().ToArray();
		}

		protected void MakeHaarWavelet()
		{
			Name = "haar";
			Length = 2;
			float num = (float)Math.Sqrt(2.0);
			LoD = new float[2]
			{
				1f / num,
				1f / num
			};
		}

		protected void MakeDaubechiesWavelet(int taps)
		{
			Name = $"db{taps}";