Decompiled source of HowToChonk v0.2.0
plugins/ArceDev.HowToChonk.dll
Decompiled 2 days agousing 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
The result has been truncated due to the large size, download it to view full contents!
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}";