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 values) { return values.Select((double v) => (float)v).ToArray(); } public static double[] ToDoubles(this IEnumerable values) { return values.Select((Func)((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 Spectrogram(float[] input, bool normalize = true) { int num = ((input.Length >= _windowSize) ? ((input.Length - _windowSize) / _hopSize + 1) : 0); List list = new List(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 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 list = new List(num + 1); List list2 = new List(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 magnitudes = spectrogram.Magnitudes; List 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 Magnitudes { get; set; } public List 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 loD, IEnumerable hiD, IEnumerable loR, IEnumerable 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}"; Length = 2 * taps; switch (taps) { case 1: { float num = (float)Math.Sqrt(2.0); LoD = new float[2] { 1f / num, 1f / num }; break; } case 2: LoD = new float[4] { -0.12940952f, 0.22414386f, 0.8365163f, 0.4829629f }; break; case 3: LoD = new float[6] { 0.035226293f, -0.08544128f, -0.13501102f, 0.4598775f, 0.8068915f, 0.33267054f }; break; case 4: LoD = new float[8] { -0.010597402f, 0.03288301f, 0.030841382f, -0.18703482f, -0.02798377f, 0.6308808f, 0.71484655f, 0.23037781f }; break; case 5: LoD = new float[10] { 0.0033357253f, -0.012580752f, -0.00624149f, 0.0775715f, -0.03224487f, -0.2422949f, 0.13842815f, 0.72430855f, 0.60382926f, 0.1601024f }; break; case 6: LoD = new float[12] { -0.0010773011f, 0.0047772573f, 0.0005538422f, -0.03158204f, 0.027522866f, 0.097501606f, -0.12976687f, -0.2262647f, 0.31525034f, 0.7511339f, 0.4946239f, 0.11154074f }; break; case 7: LoD = new float[14] { 0.0003537138f, -0.0018016407f, 0.00042957798f, 0.0125509985f, -0.016574541f, -0.038029935f, 0.08061261f, 0.07130922f, -0.22403619f, -0.143906f, 0.4697823f, 0.7291321f, 0.39653933f, 0.077852055f }; break; case 8: LoD = new float[16] { -0.00011747678f, 0.0006754494f, -0.00039174038f, -0.004870353f, 0.008746094f, 0.0139810275f, -0.044088256f, -0.0173693f, 0.12874743f, 0.00047248456f, -0.28401554f, -0.015829105f, 0.5853547f, 0.67563075f, 0.3128716f, 0.05441584f }; break; case 9: LoD = new float[18] { 3.934732E-05f, -0.00025196318f, 0.00023038576f, 0.0018476469f, -0.0042815036f, -0.0047232048f, 0.022361662f, 0.0002509471f, -0.06763283f, 0.030725682f, 0.14854075f, -0.096840784f, -0.29327378f, 0.13319738f, 0.6572881f, 0.6048231f, 0.24383467f, 0.038077947f }; break; case 10: LoD = new float[20] { -1.3264203E-05f, 9.358867E-05f, -0.00011646686f, -0.0006858567f, 0.0019924054f, 0.0013953517f, -0.010733175f, 0.0036065537f, 0.033212673f, -0.029457537f, -0.071394145f, 0.093057364f, 0.12736934f, -0.19594628f, -0.24984643f, 0.28117234f, 0.68845904f, 0.5272012f, 0.1881768f, 0.026670057f }; break; case 11: LoD = new float[22] { 4.4942744E-06f, -3.4634984E-05f, 5.4439075E-05f, 0.00024915254f, -0.0008930233f, -0.00030859286f, 0.0049284175f, -0.003340859f, -0.015364821f, 0.020840904f, 0.03133509f, -0.06643879f, -0.046479955f, 0.14981201f, 0.066043586f, -0.27423084f, -0.16227524f, 0.41196436f, 0.68568677f, 0.44989976f, 0.14406702f, 0.018694298f }; break; case 12: LoD = new float[24] { -1.5290717E-06f, 1.2776953E-05f, -2.4241546E-05f, -8.850411E-05f, 0.00038865308f, 6.545128E-06f, -0.0021795037f, 0.0022486073f, 0.006711499f, -0.012840825f, -0.0122186495f, 0.041546278f, 0.01084913f, -0.09643212f, 0.0053595696f, 0.1824786f, -0.023779258f, -0.31617844f, -0.044763885f, 0.5158865f, 0.6571987f, 0.37735513f, 0.10956627f, 0.013112258f }; break; case 13: LoD = new float[26] { 5.220035E-07f, -4.7004164E-06f, 1.04419305E-05f, 3.0678537E-05f, -0.000165129f, 4.9251525E-05f, 0.00093232613f, -0.0013156739f, -0.0027619111f, 0.0072555896f, 0.0039239414f, -0.023831422f, 0.0023799723f, 0.056139477f, -0.026488407f, -0.10580762f, 0.07294893f, 0.17947608f, -0.12457673f, -0.3149729f, 0.08698573f, 0.5888896f, 0.61105585f, 0.3119963f, 0.082861245f, 0.009202134f }; break; case 14: LoD = new float[28] { -1.78714E-07f, 1.7249947E-06f, -4.389705E-06f, -1.03372095E-05f, 6.875504E-05f, -4.1777246E-05f, -0.00038683196f, 0.00070802117f, 0.0010616911f, -0.0038496389f, -0.000746219f, 0.012789493f, -0.0056150495f, -0.030185351f, 0.026981408f, 0.055237126f, -0.07154895f, -0.086748414f, 0.13998902f, 0.13839522f, -0.21803354f, -0.27168855f, 0.21867068f, 0.63118786f, 0.5543056f, 0.25485027f, 0.062364757f, 0.0064611533f }; break; case 15: LoD = new float[30] { 6.13336E-08f, -6.3168824E-07f, 1.8112704E-06f, 3.3629872E-06f, -2.8133296E-05f, 2.5792699E-05f, 0.00015589649f, -0.00035956525f, -0.00037348235f, 0.001943324f, -0.0002417565f, -0.0064877346f, 0.0051010004f, 0.015083918f, -0.02081005f, -0.025767008f, 0.05478055f, 0.033877145f, -0.11112094f, -0.039666176f, 0.19014671f, 0.065282956f, -0.28888258f, -0.19320413f, 0.33900255f, 0.64581317f, 0.49263176f, 0.20602386f, 0.046743397f, 0.0045385375f }; break; case 16: LoD = new float[32] { -2.1093395E-08f, 2.3087841E-07f, -7.363657E-07f, -1.0435714E-06f, 1.1336609E-05f, -1.3945669E-05f, -6.1035964E-05f, 0.00017478724f, 0.00011424152f, -0.0009410218f, 0.000407897f, 0.0031280234f, -0.0036442797f, -0.0069900146f, 0.013993769f, 0.01029766f, -0.0368884f, -0.007588974f, 0.07592423f, -0.006239723f, -0.13238831f, 0.027340263f, 0.2111907f, -0.027918208f, -0.32706332f, -0.08975109f, 0.44029024f, 0.63735634f, 0.43031272f, 0.16506429f, 0.034907714f, 0.003189221f }; break; case 17: LoD = new float[34] { 7.267493E-09f, -8.423948E-08f, 2.957701E-07f, 3.0165495E-07f, -4.5059423E-06f, 6.990601E-06f, 2.3186814E-05f, -8.2048035E-05f, -2.561011E-05f, 0.00043946542f, -0.00032813253f, -0.0014368453f, 0.0023012052f, 0.0029679968f, -0.008602922f, -0.0030429899f, 0.022733677f, -0.0032709555f, -0.046922438f, 0.022312336f, 0.081105985f, -0.05709142f, -0.12681569f, 0.10113549f, 0.1973106f, -0.12659976f, -0.32832074f, 0.02731497f, 0.5183158f, 0.6109966f, 0.37035072f, 0.1312149f, 0.025985394f, 0.002241807f }; break; case 18: LoD = new float[36] { -2.5079345E-09f, 3.068836E-08f, -1.1760988E-07f, -7.691633E-08f, 1.768713E-06f, -3.3326344E-06f, -8.520603E-06f, 3.7412377E-05f, -1.5359171E-07f, -0.00019864856f, 0.00021358156f, 0.00062846567f, -0.0013405964f, -0.0011187326f, 0.004943344f, 0.000118630036f, -0.013051481f, 0.006262168f, 0.026670706f, -0.02373321f, -0.04452614f, 0.05705125f, 0.06488722f, -0.10675225f, -0.09233189f, 0.16708131f, 0.14953397f, -0.21648094f, -0.29365405f, 0.14722311f, 0.57180166f, 0.5718268f, 0.31467894f, 0.10358847f, 0.019288532f, 0.0015763103f }; break; case 19: LoD = new float[38] { 8.666849E-10f, -1.1164021E-08f, 4.636938E-08f, 1.4470883E-08f, -6.8627554E-07f, 1.5319315E-06f, 3.0109643E-06f, -1.6640177E-05f, 5.1059505E-06f, 8.7112705E-05f, -0.00012460079f, -0.00026067614f, 0.0007358025f, 0.00034180866f, -0.0026875518f, 0.00076895434f, 0.0070407474f, -0.0058669224f, -0.013988389f, 0.01937555f, 0.021623768f, -0.045674227f, -0.026501236f, 0.08690675f, 0.02758435f, -0.1427857f, -0.03351854f, 0.21234974f, 0.07465227f, -0.28583863f, -0.22809139f, 0.26089495f, 0.60170454f, 0.52443635f, 0.26438844f, 0.081278116f, 0.014281099f, 0.0011086698f }; break; case 20: LoD = new float[40] { -2.9988365E-10f, 4.056127E-09f, -1.8148432E-08f, 2.014322E-10f, 2.6339242E-07f, -6.8470797E-07f, -1.011994E-06f, 7.241248E-06f, -4.376144E-06f, -3.7105863E-05f, 6.774281E-05f, 0.00010153289f, -0.00038510474f, -5.34976E-05f, 0.0013925596f, -0.00083156215f, -0.0035814943f, 0.004420542f, 0.0067216274f, -0.013810527f, -0.008789325f, 0.0322943f, 0.0058746818f, -0.0617229f, 0.005632247f, 0.10229172f, -0.024716828f, -0.15545875f, 0.039850246f, 0.22829105f, -0.016727088f, -0.3267868f, -0.13921209f, 0.3615023f, 0.61049324f, 0.4726962f, 0.21994211f, 0.06342378f, 0.010549394f, 0.0007799536f }; break; default: throw new ArgumentException("Only db1-db20 are supported"); } } protected void MakeSymletWavelet(int taps) { Name = $"sym{taps}"; Length = 2 * taps; switch (taps) { case 2: LoD = new float[4] { -0.12940952f, 0.22414386f, 0.8365163f, 0.4829629f }; break; case 3: LoD = new float[6] { 0.035226293f, -0.08544128f, -0.13501102f, 0.4598775f, 0.8068915f, 0.33267054f }; break; case 4: LoD = new float[8] { -0.075765714f, -0.029635528f, 0.49761868f, 0.8037388f, 0.2978578f, -0.099219546f, -0.012603967f, 0.0322231f }; break; case 5: LoD = new float[10] { 0.027333068f, 0.02951949f, -0.03913425f, 0.19939753f, 0.7234077f, 0.63397896f, 0.016602106f, -0.17532809f, -0.021101834f, 0.019538883f }; break; case 6: LoD = new float[12] { 0.015404109f, 0.003490712f, -0.117990114f, -0.048311744f, 0.49105594f, 0.78764117f, 0.33792943f, -0.07263752f, -0.021060292f, 0.0447249f, 0.0017677118f, -0.0078007085f }; break; case 7: LoD = new float[14] { 0.0026818146f, -0.0010473849f, -0.012636303f, 0.030515512f, 0.06789269f, -0.049552836f, 0.017441256f, 0.53610194f, 0.76776433f, 0.28862962f, -0.14004724f, -0.10780824f, 0.0040102447f, 0.010268177f }; break; case 8: LoD = new float[16] { -0.003382416f, -0.00054213236f, 0.031695087f, 0.0076074875f, -0.14329425f, -0.06127336f, 0.48135966f, 0.77718574f, 0.3644419f, -0.05194584f, -0.02721903f, 0.04913718f, 0.003808752f, -0.014952258f, -0.00030292053f, 0.0018899504f }; break; case 9: LoD = new float[18] { 0.0014009156f, 0.0006197809f, -0.013271968f, -0.01152821f, 0.030224878f, 0.00058346277f, -0.054568958f, 0.23876092f, 0.71789706f, 0.6173385f, 0.035272487f, -0.19155084f, -0.01823377f, 0.06207779f, 0.008859267f, -0.010264064f, -0.0004731545f, 0.00106949f }; break; case 10: LoD = new float[20] { 0.0007701598f, 9.563267E-05f, -0.008641299f, -0.0014653826f, 0.045927238f, 0.011609894f, -0.15949428f, -0.07088053f, 0.47169065f, 0.76951003f, 0.38382676f, -0.03553674f, -0.03199006f, 0.04999497f, 0.005764912f, -0.02035494f, -0.0008043589f, 0.0045931735f, 5.7036083E-05f, -0.00045932943f }; break; case 11: LoD = new float[22] { 0.00017172196f, -3.8795657E-05f, -0.0017343663f, 0.00058835273f, 0.0065124957f, -0.009857935f, -0.02408084f, 0.037037417f, 0.0699768f, -0.02283265f, 0.0971984f, 0.572023f, 0.7303435f, 0.23768991f, -0.2046548f, -0.14460234f, 0.03526676f, 0.04300019f, -0.0020034718f, -0.0063896035f, 0.0001105351f, 0.0004892636f }; break; case 12: LoD = new float[24] { 0.000111967194f, -1.1353928E-05f, -0.0013497558f, 0.00018021409f, 0.0074149654f, -0.0014089092f, -0.024220722f, 0.0075537805f, 0.04917932f, -0.03584883f, -0.022162307f, 0.39888597f, 0.7634791f, 0.46274102f, -0.078332625f, -0.1703707f, 0.015301741f, 0.05780418f, -0.002604391f, -0.014589837f, 0.0003076478f, 0.0023502975f, -1.8158078E-05f, -0.00017906658f }; break; case 13: LoD = new float[26] { 6.8203255E-05f, -3.5738623E-05f, -0.0011360635f, -0.00017094286f, 0.0075262254f, 0.00529636f, -0.020216769f, -0.017211642f, 0.013862497f, -0.059750628f, -0.12436246f, 0.19770482f, 0.69573915f, 0.6445644f, 0.11023022f, -0.1404901f, 0.008819758f, 0.09292603f, 0.017618297f, -0.020749686f, -0.0014924472f, 0.005674854f, 0.0004132612f, -0.0007213644f, 3.6905374E-05f, 7.0429865E-05f }; break; case 14: LoD = new float[28] { -2.587909E-05f, 1.12108655E-05f, 0.00039843566f, -6.286542E-05f, -0.0025794418f, 0.00036647657f, 0.010037694f, -0.0027537749f, -0.029196218f, 0.0042805206f, 0.037433088f, -0.0576345f, -0.035318114f, 0.39320153f, 0.75997627f, 0.47533578f, -0.058111824f, -0.15999742f, 0.025898587f, 0.069827616f, -0.0023650487f, -0.019439314f, 0.0010131419f, 0.0045326776f, -7.3214214E-05f, -0.0006057602f, 1.9329016E-05f, 4.4618977E-05f }; break; case 15: LoD = new float[30] { 9.712419E-06f, -7.3596666E-06f, -0.00016066186f, 5.512255E-05f, 0.0010705672f, -0.00026731644f, -0.0035901654f, 0.0034234507f, 0.010079977f, -0.019405011f, -0.038876716f, 0.021937642f, 0.04073548f, -0.041082665f, 0.111533694f, 0.5786404f, 0.721843f, 0.2439627f, -0.19662637f, -0.1340563f, 0.06839331f, 0.06796983f, -0.008744789f, -0.017171253f, 0.0015261383f, 0.0034810288f, -0.0001081544f, -0.00040216855f, 2.171789E-05f, 2.8660708E-05f }; break; case 16: LoD = new float[32] { 6.230007E-06f, -3.1135564E-06f, -0.00010943148f, 2.8078583E-05f, 0.0008523547f, -0.00010844562f, -0.0038809122f, 0.000718212f, 0.012666732f, -0.0031265172f, -0.031051204f, 0.0048692743f, 0.03233309f, -0.06698305f, -0.03457423f, 0.39712292f, 0.756525f, 0.4753428f, -0.0540406f, -0.1595922f, 0.03072114f, 0.07803785f, -0.0035102752f, -0.024952758f, 0.0013598447f, 0.0069377613f, -0.00022211648f, -0.0013387206f, 3.6565925E-05f, 0.00016545679f, -5.396483E-06f, -1.0797982E-05f }; break; case 17: LoD = new float[34] { 4.2973434E-06f, 2.7801268E-06f, -6.2937026E-05f, -1.3506384E-05f, 0.00047599638f, -0.0001386423f, -0.002741676f, 0.0008567701f, 0.010482367f, -0.004819213f, -0.033291385f, 0.017903952f, 0.10475461f, 0.017271178f, -0.11856693f, 0.14239836f, 0.6507166f, 0.681489f, 0.18053958f, -0.15507601f, -0.08607087f, 0.016158808f, -0.007261635f, -0.018038897f, 0.009952983f, 0.012396988f, -0.0019054076f, -0.003932325f, 5.840043E-05f, 0.0007198271f, 2.5207934E-05f, -7.607124E-05f, -2.4527164E-06f, 3.7912532E-06f }; break; case 18: LoD = new float[36] { 2.6126127E-06f, 1.3549158E-06f, -4.5246758E-05f, -1.4020992E-05f, 0.0003961684f, 7.0212736E-05f, -0.0023138719f, -0.00041152112f, 0.009502164f, 0.0016429864f, -0.03032509f, -0.005077085f, 0.08421993f, 0.033995666f, -0.15993814f, -0.05202916f, 0.47396907f, 0.75362915f, 0.40148386f, -0.032480575f, -0.07379921f, 0.028529597f, 0.0062779444f, -0.031712685f, -0.0032607443f, 0.015012356f, 0.0010877848f, -0.00523979f, -0.00018877623f, 0.0014280863f, 4.7416146E-05f, -0.0002658301f, -9.858816E-06f, 2.9557437E-05f, 7.847298E-07f, -1.513153E-06f }; break; case 19: LoD = new float[38] { 5.487733E-07f, -6.4636515E-07f, -1.1880518E-05f, 8.873312E-06f, 0.00011553923f, -4.6120396E-05f, -0.0006357645f, 0.00015915805f, 0.002121425f, -0.0011607033f, -0.005122205f, 0.007968438f, 0.01579744f, -0.022651993f, -0.046635985f, 0.007015574f, 0.008954591f, -0.06752506f, 0.10902583f, 0.57814497f, 0.7195555f, 0.25826618f, -0.17659687f, -0.11624173f, 0.09363084f, 0.08407268f, -0.016908234f, -0.027709898f, 0.004319352f, 0.008262237f, -0.00061792234f, -0.0017049602f, 0.00012930768f, 0.00027621878f, -1.6821386E-05f, -2.8151138E-05f, 2.062317E-06f, 1.7509368E-06f }; break; case 20: LoD = new float[40] { 3.6955376E-07f, -1.9015675E-07f, -7.919361E-06f, 3.025666E-06f, 7.9929676E-05f, -1.9284123E-05f, -0.00049473107f, 7.215991E-05f, 0.0020889947f, -0.00030526283f, -0.006606586f, 0.0014230873f, 0.017004048f, -0.0033138574f, -0.031629436f, 0.008123228f, 0.02557935f, -0.07899435f, -0.02981937f, 0.40583146f, 0.7511627f, 0.47199148f, -0.051088344f, -0.1605783f, 0.036250953f, 0.08891967f, -0.006843702f, -0.035373338f, 0.0019385971f, 0.012157041f, -0.00061112636f, -0.0034716479f, 0.00012544091f, 0.0007476109f, -2.6615551E-05f, -0.00011739133f, 4.5254224E-06f, 1.22872525E-05f, -3.2567027E-07f, -6.3291293E-07f }; break; default: throw new ArgumentException("Only sym2-sym20 are supported"); } } protected void MakeCoifletWavelet(int taps) { Name = $"coif{taps}"; Length = 6 * taps; switch (taps) { case 1: LoD = new float[6] { -0.015655728f, -0.07273262f, 0.38486484f, 0.852572f, 0.33789766f, -0.07273262f }; break; case 2: LoD = new float[12] { -0.00072054943f, -0.0018232089f, 0.005611435f, 0.023680173f, -0.059434418f, -0.0764886f, 0.41700518f, 0.81272364f, 0.38611007f, -0.06737255f, -0.041464936f, 0.016387336f }; break; case 3: LoD = new float[18] { -3.4599772E-05f, -7.0983304E-05f, 0.00046621697f, 0.0011175188f, -0.0025745176f, -0.009007976f, 0.015880546f, 0.03455503f, -0.08230193f, -0.07179982f, 0.4284835f, 0.7937772f, 0.4051769f, -0.06112339f, -0.06577191f, 0.023452695f, 0.0077825966f, -0.003793513f }; break; case 4: LoD = new float[24] { -1.784985E-06f, -3.2596802E-06f, 3.1229876E-05f, 6.233904E-05f, -0.00025997456f, -0.0005890208f, 0.0012665619f, 0.0037514362f, -0.0056582866f, -0.015211731f, 0.025082262f, 0.039334428f, -0.09622044f, -0.06662747f, 0.43438604f, 0.78223896f, 0.41530842f, -0.056077313f, -0.0812667f, 0.0266823f, 0.016068945f, -0.0073461663f, -0.001629492f, 0.00089231366f }; break; case 5: LoD = new float[30] { -9.5176574E-08f, -1.6744289E-07f, 2.063762E-06f, 3.7346551E-06f, -2.1315027E-05f, -4.134043E-05f, 0.00014054115f, 0.00030225958f, -0.00063813134f, -0.0016628637f, 0.0024333731f, 0.0067641856f, -0.009164231f, -0.019761778f, 0.032683574f, 0.04128921f, -0.105574206f, -0.062035963f, 0.43799162f, 0.7742896f, 0.42156622f, -0.052043162f, -0.09192001f, 0.028168028f, 0.023408156f, -0.010131118f, -0.004159359f, 0.0021782364f, 0.0003585897f, -0.00021208083f }; break; default: throw new ArgumentException("Only coif1-coif5 are supported"); } } } public enum WaveletFamily { Haar, Daubechies, Coiflet, Symlet } } namespace NWaves.Transforms.Base { public interface IComplexTransform { int Size { get; } void Direct(float[] inRe, float[] inIm, float[] outRe, float[] outIm); void DirectNorm(float[] inRe, float[] inIm, float[] outRe, float[] outIm); void Inverse(float[] inRe, float[] inIm, float[] outRe, float[] outIm); void InverseNorm(float[] inRe, float[] inIm, float[] outRe, float[] outIm); } public interface ITransform { int Size { get; } void Direct(float[] input, float[] output); void DirectNorm(float[] input, float[] output); void Inverse(float[] input, float[] output); void InverseNorm(float[] input, float[] output); } } namespace NWaves.Signals { public class ComplexDiscreteSignal { public int SamplingRate { get; } public double[] Real { get; } public double[] Imag { get; } public int Length => Real.Length; public double this[int index] { get { return Real[index]; } set { Real[index] = value; } } public ComplexDiscreteSignal this[int startPos, int endPos] { get { Guard.AgainstInvalidRange(startPos, endPos, "Left index", "Right index"); int size = endPos - startPos; return new ComplexDiscreteSignal(SamplingRate, Real.FastCopyFragment(size, startPos), Imag.FastCopyFragment(size, startPos)); } } public double[] Magnitude { get { double[] real = Real; double[] imag = Imag; double[] array = new double[real.Length]; for (int i = 0; i < array.Length; i++) { array[i] = Math.Sqrt(real[i] * real[i] + imag[i] * imag[i]); } return array; } } public double[] Power { get { double[] real = Real; double[] imag = Imag; double[] array = new double[real.Length]; for (int i = 0; i < array.Length; i++) { array[i] = real[i] * real[i] + imag[i] * imag[i]; } return array; } } public double[] Phase { get { double[] real = Real; double[] imag = Imag; double[] array = new double[real.Length]; for (int i = 0; i < array.Length; i++) { array[i] = Math.Atan2(imag[i], real[i]); } return array; } } public double[] PhaseUnwrapped => MathUtils.Unwrap(Phase); public ComplexDiscreteSignal(int samplingRate, double[] real, double[] imag = null, bool allocateNew = false) { Guard.AgainstNonPositive(samplingRate, "Sampling rate"); SamplingRate = samplingRate; Real = (allocateNew ? real.FastCopy() : real); if (imag != null) { Guard.AgainstInequality(real.Length, imag.Length, "Number of real parts", "number of imaginary parts"); Imag = (allocateNew ? imag.FastCopy() : imag); } else { Imag = new double[real.Length]; } } public ComplexDiscreteSignal(int samplingRate, IEnumerable real, IEnumerable imag = null) : this(samplingRate, real.ToArray(), imag?.ToArray()) { } public ComplexDiscreteSignal(int samplingRate, IEnumerable samples) : this(samplingRate, samples.Select((Complex s) => s.Real), samples.Select((Complex s) => s.Imaginary)) { } public ComplexDiscreteSignal(int samplingRate, int length, double real = 0.0, double imag = 0.0) { Guard.AgainstNonPositive(samplingRate, "Sampling rate"); SamplingRate = samplingRate; double[] array = new double[length]; double[] array2 = new double[length]; for (int i = 0; i < length; i++) { array[i] = real; array2[i] = imag; } Real = array; Imag = array2; } public ComplexDiscreteSignal(int samplingRate, IEnumerable samples, double normalizeFactor = 1.0) { Guard.AgainstNonPositive(samplingRate, "Sampling rate"); SamplingRate = samplingRate; int[] array = samples.ToArray(); double[] array2 = new double[array.Length]; for (int i = 0; i < array.Length; i++) { array2[i] = (double)array[i] / normalizeFactor; } Real = array2; Imag = new double[array.Length]; } public ComplexDiscreteSignal Copy() { return new ComplexDiscreteSignal(SamplingRate, Real, Imag, allocateNew: true); } public static ComplexDiscreteSignal operator +(ComplexDiscreteSignal s1, ComplexDiscreteSignal s2) { return s1.Superimpose(s2); } public static ComplexDiscreteSignal operator +(ComplexDiscreteSignal s, double constant) { return new ComplexDiscreteSignal(s.SamplingRate, s.Real.Select((double x) => x + constant)); } public static ComplexDiscreteSignal operator -(ComplexDiscreteSignal s, double constant) { return new ComplexDiscreteSignal(s.SamplingRate, s.Real.Select((double x) => x - constant)); } public static ComplexDiscreteSignal operator *(ComplexDiscreteSignal s, float coeff) { ComplexDiscreteSignal complexDiscreteSignal = s.Copy(); complexDiscreteSignal.Amplify(coeff); return complexDiscreteSignal; } } public static class ComplexDiscreteSignalExtensions { public static ComplexDiscreteSignal Delay(this ComplexDiscreteSignal signal, int delay) { int length = signal.Length; if (delay <= 0) { delay = -delay; Guard.AgainstInvalidRange(delay, length, "Delay", "signal length"); return new ComplexDiscreteSignal(signal.SamplingRate, signal.Real.FastCopyFragment(length - delay, delay), signal.Imag.FastCopyFragment(length - delay, delay)); } return new ComplexDiscreteSignal(signal.SamplingRate, signal.Real.FastCopyFragment(length, 0, delay), signal.Imag.FastCopyFragment(length, 0, delay)); } public static ComplexDiscreteSignal Superimpose(this ComplexDiscreteSignal signal1, ComplexDiscreteSignal signal2) { Guard.AgainstInequality(signal1.SamplingRate, signal2.SamplingRate, "Sampling rate of signal1", "sampling rate of signal2"); ComplexDiscreteSignal complexDiscreteSignal; if (signal1.Length > signal2.Length) { complexDiscreteSignal = signal1.Copy(); for (int i = 0; i < signal2.Length; i++) { complexDiscreteSignal.Real[i] += signal2.Real[i]; complexDiscreteSignal.Imag[i] += signal2.Imag[i]; } } else { complexDiscreteSignal = signal2.Copy(); for (int j = 0; j < signal1.Length; j++) { complexDiscreteSignal.Real[j] += signal1.Real[j]; complexDiscreteSignal.Imag[j] += signal1.Imag[j]; } } return complexDiscreteSignal; } public static ComplexDiscreteSignal Concatenate(this ComplexDiscreteSignal signal1, ComplexDiscreteSignal signal2) { Guard.AgainstInequality(signal1.SamplingRate, signal2.SamplingRate, "Sampling rate of signal1", "sampling rate of signal2"); return new ComplexDiscreteSignal(signal1.SamplingRate, signal1.Real.MergeWithArray(signal2.Real), signal1.Imag.MergeWithArray(signal2.Imag)); } public static ComplexDiscreteSignal Repeat(this ComplexDiscreteSignal signal, int n) { Guard.AgainstNonPositive(n, "Number of repeat times"); return new ComplexDiscreteSignal(signal.SamplingRate, signal.Real.RepeatArray(n), signal.Imag.RepeatArray(n)); } public static void Amplify(this ComplexDiscreteSignal signal, double coeff) { for (int i = 0; i < signal.Length; i++) { signal.Real[i] *= coeff; signal.Imag[i] *= coeff; } } public static void Attenuate(this ComplexDiscreteSignal signal, double coeff) { Guard.AgainstNonPositive(coeff, "Attenuation coefficient"); signal.Amplify(1.0 / coeff); } public static ComplexDiscreteSignal First(this ComplexDiscreteSignal signal, int n) { Guard.AgainstNonPositive(n, "Number of samples"); Guard.AgainstExceedance(n, signal.Length, "Number of samples", "signal length"); return new ComplexDiscreteSignal(signal.SamplingRate, signal.Real.FastCopyFragment(n), signal.Imag.FastCopyFragment(n)); } public static ComplexDiscreteSignal Last(this ComplexDiscreteSignal signal, int n) { Guard.AgainstNonPositive(n, "Number of samples"); Guard.AgainstExceedance(n, signal.Length, "Number of samples", "signal length"); return new ComplexDiscreteSignal(signal.SamplingRate, signal.Real.FastCopyFragment(n, signal.Length - n), signal.Imag.FastCopyFragment(n, signal.Imag.Length - n)); } public static ComplexDiscreteSignal ZeroPadded(this ComplexDiscreteSignal signal, int length) { if (length <= 0) { length = MathUtils.NextPowerOfTwo(signal.Length); } return new ComplexDiscreteSignal(signal.SamplingRate, signal.Real.PadZeros(length), signal.Imag.PadZeros(length)); } public static ComplexDiscreteSignal Multiply(this ComplexDiscreteSignal signal1, ComplexDiscreteSignal signal2) { Guard.AgainstInequality(signal1.SamplingRate, signal2.SamplingRate, "Sampling rate of signal1", "sampling rate of signal2"); int length = signal1.Length; double[] array = new double[length]; double[] array2 = new double[length]; double[] real = signal1.Real; double[] imag = signal1.Imag; double[] real2 = signal2.Real; double[] imag2 = signal2.Imag; for (int i = 0; i < length; i++) { array[i] = real[i] * real2[i] - imag[i] * imag2[i]; array2[i] = real[i] * imag2[i] + imag[i] * real2[i]; } return new ComplexDiscreteSignal(signal1.SamplingRate, array, array2); } public static ComplexDiscreteSignal Divide(this ComplexDiscreteSignal signal1, ComplexDiscreteSignal signal2) { Guard.AgainstInequality(signal1.SamplingRate, signal2.SamplingRate, "Sampling rate of signal1", "sampling rate of signal2"); int length = signal1.Length; double[] array = new double[length]; double[] array2 = new double[length]; double[] real = signal1.Real; double[] imag = signal1.Imag; double[] real2 = signal2.Real; double[] imag2 = signal2.Imag; for (int i = 0; i < length; i++) { double num = imag[i] * imag[i] + imag2[i] * imag2[i]; array[i] = (real[i] * real2[i] + imag[i] * imag2[i]) / num; array2[i] = (real2[i] * imag[i] - imag2[i] * real[i]) / num; } return new ComplexDiscreteSignal(signal1.SamplingRate, array, array2); } public static double[] Unwrap(this double[] phase, double tolerance = Math.PI) { return MathUtils.Unwrap(phase, tolerance); } public static IEnumerable ToComplexNumbers(this ComplexDiscreteSignal signal) { for (int i = 0; i < signal.Length; i++) { yield return new Complex(signal.Real[i], signal.Imag[i]); } } } public class DiscreteSignal { public int SamplingRate { get; } public float[] Samples { get; } public int Length => Samples.Length; public double Duration => (double)Samples.Length / (double)SamplingRate; public float this[int index] { get { return Samples[index]; } set { Samples[index] = value; } } public DiscreteSignal this[int startPos, int endPos] { get { Guard.AgainstInvalidRange(startPos, endPos, "Left index", "Right index"); return new DiscreteSignal(SamplingRate, Samples.FastCopyFragment(endPos - startPos, startPos)); } } public DiscreteSignal(int samplingRate, float[] samples, bool allocateNew = false) { Guard.AgainstNonPositive(samplingRate, "Sampling rate"); SamplingRate = samplingRate; Samples = (allocateNew ? samples.FastCopy() : samples); } public DiscreteSignal(int samplingRate, IEnumerable samples) : this(samplingRate, samples?.ToArray()) { } public DiscreteSignal(int samplingRate, int length, float value = 0f) { Guard.AgainstNonPositive(samplingRate, "Sampling rate"); SamplingRate = samplingRate; float[] array = new float[length]; for (int i = 0; i < array.Length; i++) { array[i] = value; } Samples = array; } public DiscreteSignal(int samplingRate, IEnumerable samples, float normalizeFactor = 1f) { Guard.AgainstNonPositive(samplingRate, "Sampling rate"); SamplingRate = samplingRate; int[] array = samples.ToArray(); float[] array2 = new float[array.Length]; for (int i = 0; i < array.Length; i++) { array2[i] = (float)array[i] / normalizeFactor; } Samples = array2; } public static DiscreteSignal Unit(int length, int samplingRate = 1) { float[] array = new float[length]; array[0] = 1f; return new DiscreteSignal(samplingRate, array); } public static DiscreteSignal Constant(float constant, int length, int samplingRate = 1) { return new DiscreteSignal(samplingRate, length, constant); } public DiscreteSignal Copy() { return new DiscreteSignal(SamplingRate, Samples, allocateNew: true); } public static DiscreteSignal operator +(DiscreteSignal s1, DiscreteSignal s2) { return s1.Superimpose(s2); } public static DiscreteSignal operator -(DiscreteSignal s) { return new DiscreteSignal(s.SamplingRate, s.Samples.Select((float x) => 0f - x)); } public static DiscreteSignal operator -(DiscreteSignal s1, DiscreteSignal s2) { return s1.Subtract(s2); } public static DiscreteSignal operator +(DiscreteSignal s, float constant) { return new DiscreteSignal(s.SamplingRate, s.Samples.Select((float x) => x + constant)); } public static DiscreteSignal operator -(DiscreteSignal s, float constant) { return new DiscreteSignal(s.SamplingRate, s.Samples.Select((float x) => x - constant)); } public static DiscreteSignal operator *(DiscreteSignal s, float coeff) { DiscreteSignal discreteSignal = s.Copy(); discreteSignal.Amplify(coeff); return discreteSignal; } public float Energy(int startPos, int endPos) { float num = 0f; for (int i = startPos; i < endPos; i++) { num += Samples[i] * Samples[i]; } return num / (float)(endPos - startPos); } public float Energy() { return Energy(0, Length); } public float Rms(int startPos, int endPos) { return (float)Math.Sqrt(Energy(startPos, endPos)); } public float Rms() { return (float)Math.Sqrt(Energy(0, Length)); } public float ZeroCrossingRate(int startPos, int endPos) { float num = Samples[startPos] + 0.0001f; int num2 = 0; for (int i = startPos + 1; i < endPos; i++) { float num3 = Samples[i] + 0.0001f; if (num3 >= 0f != num >= 0f) { num2++; } num = num3; } return (float)num2 / (float)(endPos - startPos - 1); } public float ZeroCrossingRate() { return ZeroCrossingRate(0, Length); } public float Entropy(int startPos, int endPos, int binCount = 32) { int num = endPos - startPos; if (num < binCount) { binCount = num; } int[] array = new int[binCount + 1]; float num2 = Samples[0]; float num3 = Samples[0]; for (int i = startPos; i < endPos; i++) { float num4 = Math.Abs(Samples[i]); if (num4 < num2) { num2 = num4; } if (num4 > num3) { num3 = num4; } } if (num3 - num2 < 1E-08f) { return 0f; } float num5 = (num3 - num2) / (float)binCount; for (int j = startPos; j < endPos; j++) { array[(int)((Math.Abs(Samples[j]) - num2) / num5)]++; } double num6 = 0.0; for (int k = 0; k < binCount; k++) { float num7 = (float)array[k] / (float)(endPos - startPos); if (num7 > 1E-08f) { num6 += (double)num7 * Math.Log(num7, 2.0); } } return (float)((0.0 - num6) / Math.Log(binCount, 2.0)); } public float Entropy(int binCount = 32) { return Entropy(0, Length, binCount); } } public static class DiscreteSignalExtensions { public static DiscreteSignal Delay(this DiscreteSignal signal, int delay) { int length = signal.Length; if (delay <= 0) { delay = -delay; Guard.AgainstInvalidRange(delay, length, "Delay", "signal length"); return new DiscreteSignal(signal.SamplingRate, signal.Samples.FastCopyFragment(length - delay, delay)); } return new DiscreteSignal(signal.SamplingRate, signal.Samples.FastCopyFragment(length, 0, delay)); } public static DiscreteSignal Superimpose(this DiscreteSignal signal1, DiscreteSignal signal2) { Guard.AgainstInequality(signal1.SamplingRate, signal2.SamplingRate, "Sampling rate of signal1", "sampling rate of signal2"); DiscreteSignal discreteSignal; if (signal1.Length >= signal2.Length) { discreteSignal = signal1.Copy(); for (int i = 0; i < signal2.Length; i++) { discreteSignal[i] += signal2.Samples[i]; } } else { discreteSignal = signal2.Copy(); for (int j = 0; j < signal1.Length; j++) { discreteSignal[j] += signal1.Samples[j]; } } return discreteSignal; } public static DiscreteSignal SuperimposeMany(this DiscreteSignal signal1, DiscreteSignal signal2, int[] positions) { Guard.AgainstInequality(signal1.SamplingRate, signal2.SamplingRate, "Sampling rate of signal1", "sampling rate of signal2"); int length = Math.Max(signal1.Length, signal2.Length + positions.Max()); DiscreteSignal discreteSignal = new DiscreteSignal(signal1.SamplingRate, length); signal1.Samples.FastCopyTo(discreteSignal.Samples, signal1.Length); foreach (int num in positions) { for (int j = 0; j < signal2.Length; j++) { discreteSignal[num + j] += signal2.Samples[j]; } } return discreteSignal; } public static DiscreteSignal Subtract(this DiscreteSignal signal1, DiscreteSignal signal2) { Guard.AgainstInequality(signal1.SamplingRate, signal2.SamplingRate, "Sampling rate of signal1", "sampling rate of signal2"); DiscreteSignal discreteSignal; if (signal1.Length >= signal2.Length) { discreteSignal = signal1.Copy(); for (int i = 0; i < signal2.Length; i++) { discreteSignal[i] -= signal2.Samples[i]; } } else { discreteSignal = new DiscreteSignal(signal2.SamplingRate, signal2.Length); for (int j = 0; j < signal1.Length; j++) { discreteSignal[j] = signal1.Samples[j] - signal2.Samples[j]; } for (int k = signal1.Length; k < signal2.Length; k++) { discreteSignal[k] = 0f - signal2.Samples[k]; } } return discreteSignal; } public static DiscreteSignal Concatenate(this DiscreteSignal signal1, DiscreteSignal signal2) { Guard.AgainstInequality(signal1.SamplingRate, signal2.SamplingRate, "Sampling rate of signal1", "sampling rate of signal2"); return new DiscreteSignal(signal1.SamplingRate, signal1.Samples.MergeWithArray(signal2.Samples)); } public static DiscreteSignal Repeat(this DiscreteSignal signal, int n) { Guard.AgainstNonPositive(n, "Number of repeat times"); return new DiscreteSignal(signal.SamplingRate, signal.Samples.RepeatArray(n)); } public static void Amplify(this DiscreteSignal signal, float coeff) { for (int i = 0; i < signal.Length; i++) { signal[i] *= coeff; } } public static void Attenuate(this DiscreteSignal signal, float coeff) { Guard.AgainstNonPositive(coeff, "Attenuation coefficient"); signal.Amplify(1f / coeff); } public static void Reverse(this DiscreteSignal signal) { float[] samples = signal.Samples; int num = 0; int num2 = samples.Length - 1; while (num < samples.Length / 2) { float num3 = samples[num]; samples[num] = samples[num2]; samples[num2] = num3; num++; num2--; } } public static DiscreteSignal First(this DiscreteSignal signal, int n) { Guard.AgainstNonPositive(n, "Number of samples"); Guard.AgainstExceedance(n, signal.Length, "Number of samples", "signal length"); return new DiscreteSignal(signal.SamplingRate, signal.Samples.FastCopyFragment(n)); } public static DiscreteSignal Last(this DiscreteSignal signal, int n) { Guard.AgainstNonPositive(n, "Number of samples"); Guard.AgainstExceedance(n, signal.Length, "Number of samples", "signal length"); return new DiscreteSignal(signal.SamplingRate, signal.Samples.FastCopyFragment(n, signal.Length - n)); } public static void FullRectify(this DiscreteSignal signal) { for (int i = 0; i < signal.Length; i++) { if (signal[i] < 0f) { signal[i] = 0f - signal[i]; } } } public static void HalfRectify(this DiscreteSignal signal) { for (int i = 0; i < signal.Length; i++) { if (signal[i] < 0f) { signal[i] = 0f; } } } public static void NormalizeMax(this DiscreteSignal signal, int bitsPerSample = 0) { float num = 1f / signal.Samples.Max((float s) => Math.Abs(s)); if (bitsPerSample > 0) { num *= (float)(1.0 - 1.0 / Math.Pow(2.0, bitsPerSample)); } signal.Amplify(num); } public static ComplexDiscreteSignal ToComplex(this DiscreteSignal signal) { return new ComplexDiscreteSignal(signal.SamplingRate, signal.Samples.ToDoubles()); } public static void FadeInFadeOut(this DiscreteSignal signal, double fadeInDuration, double fadeOutDuration) { signal.FadeIn(fadeInDuration); signal.FadeOut(fadeOutDuration); } public static void FadeIn(this DiscreteSignal signal, double duration) { Guard.AgainstNonPositive(duration, "Fade-in duration"); int num = Math.Min(signal.Length, (int)((double)signal.SamplingRate * duration)); for (int i = 0; i < num; i++) { signal[i] *= (float)i / (float)num; } } public static void FadeOut(this DiscreteSignal signal, double duration) { Guard.AgainstNonPositive(duration, "Fade-out duration"); int num = Math.Min(signal.Length, (int)((double)signal.SamplingRate * duration)); int num2 = signal.Length - num; int num3 = num - 1; while (num2 < signal.Length) { signal[num2] *= (float)num3 / (float)num; num2++; num3--; } } public static DiscreteSignal Crossfade(this DiscreteSignal signal1, DiscreteSignal signal2, double duration) { Guard.AgainstNonPositive(duration, "Crossfade duration"); int val = Math.Min(signal1.Length, signal2.Length); int num = Math.Min((int)((double)signal1.SamplingRate * duration), val); DiscreteSignal discreteSignal = new DiscreteSignal(signal1.SamplingRate, signal1.Length + signal2.Length - num); Array.Copy(signal1.Samples, discreteSignal.Samples, signal1.Length - num); Array.Copy(signal2.Samples, num, discreteSignal.Samples, signal1.Length, signal2.Length - num); int num2 = signal1.Length - num; int num3 = 0; while (num3 < num) { float num4 = (float)num3 / (float)num; discreteSignal[num2] = (1f - num4) * signal1[num2] + num4 * signal2[num3]; num3++; num2++; } return discreteSignal; } } } namespace NWaves.Signals.Builders { public class AdsrBuilder : SignalBuilder { public enum AdsrState { Attack, Decay, Sustain, Release } private AdsrState _state; private double _attack; private double _decay; private double _sustain; private double _release; private float _attackAmp = 1.5f; private double _attackSlope = 0.2; private double _decaySlope = 0.2; private double _sustainSlope = 0.2; private double _releaseSlope = 0.2; private float _a; private float _b; private int _n; private float _prev; public AdsrState State { get { return _state; } private set { _state = value; UpdateCoefficients(); } } public AdsrBuilder(int attack, int decay, int sustain, int release) { base.ParameterSetters = new Dictionary> { { "attack, a", delegate(double param) { _attackSlope = param; UpdateCoefficients(); } }, { "decay, d", delegate(double param) { _decaySlope = param; UpdateCoefficients(); } }, { "sustain, s", delegate(double param) { _sustainSlope = param; UpdateCoefficients(); } }, { "release, r", delegate(double param) { _releaseSlope = param; UpdateCoefficients(); } }, { "amp, attackAmp", delegate(double param) { _attackAmp = (float)param; } } }; _attack = attack; _decay = _attack + (double)decay; _sustain = _decay + (double)sustain; _release = _sustain + (double)release; Reset(); } public AdsrBuilder(double attack, double decay, double sustain, double release) { _attack = attack; _decay = _attack + decay; _sustain = _decay + sustain; _release = _sustain + release; base.ParameterSetters = new Dictionary> { { "attack, a", delegate(double param) { _attackSlope = param; UpdateCoefficients(); } }, { "decay, d", delegate(double param) { _decaySlope = param; UpdateCoefficients(); } }, { "sustain, s", delegate(double param) { _sustainSlope = param; UpdateCoefficients(); } }, { "release, r", delegate(double param) { _releaseSlope = param; UpdateCoefficients(); } }, { "amp, attackAmp", delegate(double param) { _attackAmp = (float)param; } } }; Reset(); } public override float NextSample() { float num; if ((double)_n > _sustain) { if (_state != AdsrState.Release) { State = AdsrState.Release; } num = 0f; } else if ((double)_n > _decay) { if (_state != AdsrState.Sustain) { State = AdsrState.Sustain; } num = 1f; } else if ((double)_n > _attack) { if (_state != AdsrState.Decay) { State = AdsrState.Decay; } num = 1f; } else { num = _attackAmp; } _prev = _b * num - _a * _prev; if ((double)(++_n) == _release) { _n = 0; } return _prev; } public override void Reset() { _n = 0; _prev = 0f; State = AdsrState.Attack; } public override SignalBuilder SampledAt(int samplingRate) { _attack *= samplingRate; _decay *= samplingRate; _sustain *= samplingRate; _release *= samplingRate; UpdateCoefficients(); return base.SampledAt(samplingRate); } private void UpdateCoefficients() { switch (_state) { case AdsrState.Release: _a = (float)(0.0 - Math.Exp(-1.0 / ((_release - _sustain) * _releaseSlope))); break; case AdsrState.Sustain: _a = (float)(0.0 - Math.Exp(-1.0 / ((_sustain - _decay) * _sustainSlope))); break; case AdsrState.Decay: _a = (float)(0.0 - Math.Exp(-1.0 / ((_decay - _attack) * _decaySlope))); break; default: _a = (float)(0.0 - Math.Exp(-1.0 / (_attack * _attackSlope))); break; } _b = 1f + _a; } } public class AwgnBuilder : SignalBuilder { private double _mu; private double _sigma; private float _next; private bool _nextReady; private readonly Random _rand = new Random(); public AwgnBuilder() { base.ParameterSetters = new Dictionary> { { "mu, mean", delegate(double param) { _mu = param; } }, { "sigma, stddev", delegate(double param) { _sigma = param; } } }; _mu = 0.0; _sigma = 1.0; } public override float NextSample() { if (_nextReady) { return _next; } double d = _rand.NextDouble(); double num = _rand.NextDouble(); double num2 = Math.Sqrt(-2.0 * Math.Log(d)); double num3 = Math.PI * 2.0 * num; float result = (float)(num2 * Math.Cos(num3) * _sigma + _mu); _next = (float)(num2 * Math.Sin(num3) * _sigma + _mu); return result; } public override void Reset() { _nextReady = false; } } public class ChirpBuilder : SignalBuilder { private double _low; private double _high; private double _f0; private double _f1; private int _n; public ChirpBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } }, { "f0, freq0, start", delegate(double param) { _f0 = param; } }, { "f1, freq1, end", delegate(double param) { _f1 = param; } } }; _low = -1.0; _high = 1.0; _f0 = 100.0; _f1 = 1000.0; } public override float NextSample() { float num = (float)((_f1 - _f0) / (double)base.Length); int samplingRate = base.SamplingRate; double num2 = Math.Cos(Math.PI * 2.0 * (_f0 / (double)samplingRate + (double)(num * (float)_n)) * (double)_n / (double)samplingRate); num2 = _low + (_high - _low) * (1.0 + num2) / 2.0; if (++_n == base.Length) { _n = 0; } return (float)num2; } public override void Reset() { _n = 0; } protected override DiscreteSignal Generate() { Guard.AgainstNonPositive(_f0, "Start frequency"); Guard.AgainstNonPositive(_f1, "End frequency"); return base.Generate(); } } public class CosineBuilder : SignalBuilder { private double _low; private double _high; private double _frequency; private double _phase; private int _n; public CosineBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } }, { "frequency, freq", delegate(double param) { _frequency = param; } }, { "phase, phi", delegate(double param) { _phase = param; } } }; _low = -1.0; _high = 1.0; _frequency = 100.0; _phase = 0.0; } public override float NextSample() { double num = Math.Cos(Math.PI * 2.0 * _frequency / (double)base.SamplingRate * (double)_n + _phase); num = _low + (_high - _low) * (1.0 + num) / 2.0; _n++; return (float)num; } public override void Reset() { _n = 0; } protected override DiscreteSignal Generate() { Guard.AgainstNonPositive(_frequency, "Frequency"); return base.Generate(); } } public class FadeInOutBuilder : SignalBuilder { private readonly SignalBuilder _builder; private int _fadeInSampleCount; private int _fadeOutSampleCount; private int _fadeInIndex; private int _fadeOutIndex; private int _index; public bool FadeStarted { get; protected set; } public bool FadeFinished => _fadeOutIndex <= 0; public FadeInOutBuilder(SignalBuilder builder) { _builder = builder; _index = 0; base.Length = _builder.Length; base.SamplingRate = _builder.SamplingRate; } public override float NextSample() { float num = _builder.NextSample(); if (FadeStarted || _index++ > base.Length - _fadeOutSampleCount) { num *= (float)_fadeOutIndex-- / (float)_fadeOutSampleCount; FadeStarted = !FadeFinished; } if (_fadeInIndex < _fadeInSampleCount) { num *= (float)_fadeInIndex++ / (float)_fadeInSampleCount; } return num; } public override void Reset() { base.Reset(); _index = 0; _fadeInIndex = 0; _fadeOutIndex = _fadeOutSampleCount - 1; FadeStarted = false; } public FadeInOutBuilder In(double seconds) { _fadeInSampleCount = (int)(seconds * (double)base.SamplingRate); return this; } public FadeInOutBuilder Out(double seconds) { _fadeOutSampleCount = (int)(seconds * (double)base.SamplingRate); _fadeOutIndex = _fadeOutSampleCount - 1; return this; } public void FadeOut() { if (_fadeOutSampleCount > 0) { _fadeOutIndex = _fadeOutSampleCount - 1; FadeStarted = true; } } } public class KarplusStrongBuilder : WaveTableBuilder { protected double _frequency = 100.0; protected double _stretchFactor = 1.0; protected float _feedback = 1f; protected float _prev; protected readonly Random _rand = new Random(); public KarplusStrongBuilder() : base(null) { Init(); } public KarplusStrongBuilder(float[] samples) : base(samples) { Init(); } private void Init() { base.ParameterSetters.Add("freq, f, frequency", delegate(double param) { SetFrequency(param); }); base.ParameterSetters.Add("stretch, s", delegate(double param) { _stretchFactor = param; }); base.ParameterSetters.Add("feedback, a", delegate(double param) { _feedback = (float)param; }); } private void SetFrequency(double param) { _frequency = param; if (base.SamplingRate > 0) { GenerateWaveTable(base.SamplingRate / (int)_frequency); } } protected void GenerateWaveTable(int sampleCount) { float[] values = new float[2] { -1f, 1f }; _samples = (from _ in Enumerable.Range(0, sampleCount) select values[_rand.Next(2)]).ToArray(); } public override float NextSample() { int num = (int)_n % _samples.Length; if (_rand.NextDouble() < 1.0 / _stretchFactor) { _samples[num] = 0.5f * (_samples[num] + _prev) * _feedback; } _prev = _samples[num]; _n += 1f; return _prev; } public override void Reset() { float[] array = new float[2] { -1f, 1f }; for (int i = 0; i < _samples.Length; i++) { _samples[i] = array[_rand.Next(2)]; } base.Reset(); } public override SignalBuilder SampledAt(int samplingRate) { if (_frequency > 0.0) { GenerateWaveTable(samplingRate / (int)_frequency); } return base.SampledAt(samplingRate); } } public class KarplusStrongDrumBuilder : KarplusStrongBuilder { private double _probability = 0.5; public KarplusStrongDrumBuilder() : base(null) { Init(); } public KarplusStrongDrumBuilder(float[] samples) : base(samples) { Init(); } private void Init() { base.ParameterSetters.Add("probability, prob", delegate(double param) { _probability = param; }); } public override float NextSample() { int num = (int)_n % _samples.Length; if (_rand.NextDouble() < 1.0 / _stretchFactor) { if (_rand.NextDouble() < _probability) { _samples[num] = 0.5f * (_samples[num] + _prev) * _feedback; } else { _samples[num] = -0.5f * (_samples[num] + _prev) * _feedback; } } _prev = _samples[num]; _n += 1f; return _prev; } } public class PadSynthBuilder : WaveTableBuilder { private readonly Random _rand = new Random(); protected float _frequency = 440f; protected float[] _amplitudes; protected float _bw = 40f; protected float _bwScale = 1.25f; protected RealFft _fft; protected int _fftSize = 2048; protected float[] _re; protected float[] _im; public PadSynthBuilder() : base(null) { base.ParameterSetters.Add("frequency, freq, f", delegate(double param) { SetFrequency((float)param); }); base.ParameterSetters.Add("fftsize, size", delegate(double param) { SetFftSize((int)param); }); base.ParameterSetters.Add("bandwidth, bw", delegate(double param) { SetBandwidth((float)param); }); base.ParameterSetters.Add("scale, bwscale", delegate(double param) { SetScale((float)param); }); } protected void SetFrequency(float frequency) { _frequency = frequency; GenerateWavetable(); } protected void SetFftSize(int fftSize) { _fftSize = fftSize; if (_fft == null || _fft.Size != _fftSize) { _fft = new RealFft(_fftSize); _re = new float[_fftSize]; _im = new float[_fftSize]; _samples = new float[_fftSize]; } GenerateWavetable(); } protected void SetBandwidth(float bw) { _bw = bw; GenerateWavetable(); } protected void SetScale(float bwScale) { _bwScale = bwScale; GenerateWavetable(); } public PadSynthBuilder SetAmplitudes(float[] amplitudes) { _amplitudes = amplitudes; GenerateWavetable(); return this; } protected void GenerateWavetable() { if (_fft == null || _amplitudes == null || _frequency <= 0f) { return; } Array.Clear(_re, 0, _re.Length); Array.Clear(_im, 0, _im.Length); int num = _fftSize / 2; for (int i = 1; i <= _amplitudes.Length; i++) { if (_amplitudes[i - 1] == 0f) { continue; } double num2 = (Math.Pow(2.0, _bw / 1200f) - 1.0) * (double)_frequency * Math.Pow(i, _bwScale); float num3 = _frequency * (float)i / (float)base.SamplingRate; double bw = num2 / (2.0 * (double)base.SamplingRate); int num4 = (int)(num3 * (float)num); if (num4 < num) { double num5 = 1.0; int num6 = num4; while (num5 > 1E-10) { num5 = Profile(1.0 * (double)num6 / (double)num - (double)num3, bw); _re[num6--] += (float)num5 * _amplitudes[i - 1]; } num5 = 1.0; num6 = num4 + 1; while (num5 > 1E-10) { num5 = Profile(1.0 * (double)num6 / (double)num - (double)num3, bw); _re[num6++] += (float)num5 * _amplitudes[i - 1]; } } } for (int j = 0; j < _re.Length; j++) { float num7 = _re[j]; double num8 = _rand.NextDouble() * 2.0 * Math.PI; _re[j] = (float)((double)num7 * Math.Cos(num8)); _im[j] = (float)((double)num7 * Math.Sin(num8)); } _fft.Inverse(_re, _im, _samples); float num9 = 1f / _samples.Max(); int num10 = 0; while (num10 < _samples.Length) { _samples[num10++] *= num9; } } protected static double Profile(double f, double bw) { double num = f / bw; return Math.Exp((0.0 - num) * num) / bw; } public override SignalBuilder SampledAt(int samplingRate) { if (samplingRate <= 0) { throw new ArgumentException("Sampling rate must be positive!"); } base.SamplingRate = samplingRate; GenerateWavetable(); return this; } } public class PerlinNoiseBuilder : SignalBuilder { private double _low; private double _high; private double _scale; private readonly byte[] _permutation = new byte[512]; private int _n; private readonly Random _rand = new Random(); public PerlinNoiseBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } }, { "scale, octave", delegate(double param) { _scale = param; } } }; _low = -1.0; _high = 1.0; _scale = 0.02; _rand.NextBytes(_permutation); } private double GenerateSample(double x) { int num = (((double)(int)x < x) ? ((int)x) : ((int)x - 1)); int num2 = (num + 1) & 0xFF; double num3 = x - (double)num; double x2 = num3 - 1.0; num &= 0xFF; return 0.188 * Lerp(Fade(num3), Gradient(_permutation[num], num3), Gradient(_permutation[num2], x2)); } private static double Gradient(int hash, double x) { int num = hash & 0xF; double num2 = 1.0 + (double)(num & 7); if ((num & 8) != 0) { return (0.0 - num2) * x; } return num2 * x; } private static double Fade(double t) { return t * t * t * (t * (t * 6.0 - 15.0) + 10.0); } private static double Lerp(double t, double a, double b) { return a + t * (b - a); } public override float NextSample() { double num = GenerateSample((double)_n * _scale) * (_high - _low) / 2.0 + (_high + _low) / 2.0; _n++; return (float)num; } public override void Reset() { _n = 0; _rand.NextBytes(_permutation); } protected override DiscreteSignal Generate() { Guard.AgainstInvalidRange(_low, _high, "Upper amplitude", "Lower amplitude"); return base.Generate(); } } public class PinkNoiseBuilder : SignalBuilder { private double _low; private double _high; private double _b0; private double _b1; private double _b2; private double _b3; private double _b4; private double _b5; private double _b6; private readonly Random _rand = new Random(); public PinkNoiseBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } } }; _low = -1.0; _high = 1.0; } public override float NextSample() { double num = (_low + _high) / 2.0; _low -= num; _high -= num; double num2 = _rand.NextDouble() * (_high - _low) + _low; _b0 = 0.9988600015640259 * _b0 + num2 * 0.05551790073513985; _b1 = 0.9933199882507324 * _b1 + num2 * 0.0750759020447731; _b2 = 0.968999981880188 * _b2 + num2 * 0.15385200083255768; _b3 = 0.8665000200271606 * _b3 + num2 * 0.3104856014251709; _b4 = 0.550000011920929 * _b4 + num2 * 0.5329521894454956; _b5 = -0.7616000175476074 * _b5 - num2 * 0.016898000612854958; double num3 = (_b0 + _b1 + _b2 + _b3 + _b4 + _b5 + _b6 + num2 * 0.5362) * 0.11 + num; _b6 = num2 * 0.115926; return (float)num3; } public override void Reset() { _b0 = (_b1 = (_b2 = (_b3 = (_b4 = (_b5 = (_b6 = 0.0)))))); } protected override DiscreteSignal Generate() { Guard.AgainstInvalidRange(_low, _high, "Upper amplitude", "Lower amplitude"); return base.Generate(); } } public class PulseWaveBuilder : SignalBuilder { private double _low; private double _high; private double _pulse; private double _period; private int _n; public PulseWaveBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } }, { "pulse, width", delegate(double param) { _pulse = param; } }, { "period, t", delegate(double param) { _period = param; } } }; _low = -1.0; _high = 1.0; _pulse = 0.05; _period = 0.1; } public override float NextSample() { double num = ((_n <= (int)(_pulse * (double)base.SamplingRate)) ? _high : _low); if (++_n == (int)(_period * (double)base.SamplingRate)) { _n = 0; } return (float)num; } public override void Reset() { _n = 0; } protected override DiscreteSignal Generate() { Guard.AgainstNonPositive(_period, "Period"); Guard.AgainstNonPositive(_pulse, "Pulse duration"); Guard.AgainstInvalidRange(_pulse, _period, "Pulse duration", "Period"); return base.Generate(); } } public class RampBuilder : SignalBuilder { private double _slope; private double _intercept; private int _n; public RampBuilder() { base.ParameterSetters = new Dictionary> { { "slope, k", delegate(double param) { _slope = param; } }, { "intercept, b", delegate(double param) { _intercept = param; } } }; _slope = 0.0; _intercept = 0.0; } public override float NextSample() { float result = (float)(_slope * (double)_n + _intercept); _n++; return result; } public override void Reset() { _n = 0; } } public class RedNoiseBuilder : SignalBuilder { private double _low; private double _high; private double _prev; private readonly Random _rand = new Random(); public RedNoiseBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } } }; _low = -1.0; _high = 1.0; } public override float NextSample() { double num = (_low + _high) / 2.0; _low -= num; _high -= num; double num2 = _rand.NextDouble() * (_high - _low) + _low; return (float)((_prev = (_prev + 0.02 * num2) / 1.02) * 3.5 + num); } public override void Reset() { _prev = 0.0; } protected override DiscreteSignal Generate() { Guard.AgainstInvalidRange(_low, _high, "Upper amplitude", "Lower amplitude"); return base.Generate(); } } public class SawtoothBuilder : SignalBuilder { private double _low; private double _high; private double _frequency; private int _n; private double _cycles; public SawtoothBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } }, { "frequency, freq", delegate(double param) { _frequency = param; _cycles = (double)base.SamplingRate / _frequency; _n = (int)(_cycles / 2.0); } } }; _low = -1.0; _high = 1.0; _frequency = 100.0; } public override float NextSample() { double num = _low + (_high - _low) * ((double)_n % _cycles) / _cycles; _n++; return (float)num; } public override void Reset() { _n = (int)(_cycles / 2.0); } public override SignalBuilder SampledAt(int samplingRate) { _cycles = (double)samplingRate / _frequency; _n = (int)(_cycles / 2.0); return base.SampledAt(samplingRate); } protected override DiscreteSignal Generate() { Guard.AgainstNonPositive(_frequency, "Frequency"); Guard.AgainstInvalidRange(_low, _high, "Upper amplitude", "Lower amplitude"); return base.Generate(); } } public class SincBuilder : SignalBuilder { private double _low; private double _high; private double _frequency; private int _n; public SincBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } }, { "frequency, freq", delegate(double param) { _frequency = param; } } }; _low = -1.0; _high = 1.0; _frequency = 100.0; } public override float NextSample() { float result = (float)(_low + (_high - _low) * MathUtils.Sinc((double)_n * _frequency / (double)base.SamplingRate)); _n++; return result; } public override void Reset() { _n = 0; } protected override DiscreteSignal Generate() { Guard.AgainstNonPositive(_frequency, "Frequency"); return base.Generate(); } } public class SineBuilder : SignalBuilder { private double _low; private double _high; private double _frequency; private double _phase; private int _n; public SineBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } }, { "frequency, freq", delegate(double param) { _frequency = param; } }, { "phase, phi", delegate(double param) { _phase = param; } } }; _low = -1.0; _high = 1.0; _frequency = 100.0; _phase = 0.0; } public override float NextSample() { double num = Math.Sin(Math.PI * 2.0 * _frequency / (double)base.SamplingRate * (double)_n + _phase); num = _low + (_high - _low) * (1.0 + num) / 2.0; _n++; return (float)num; } public override void Reset() { _n = 0; } protected override DiscreteSignal Generate() { Guard.AgainstNonPositive(_frequency, "Frequency"); return base.Generate(); } } public class SquareWaveBuilder : SignalBuilder { private double _low; private double _high; private double _frequency; private int _n; private double _cycles; public SquareWaveBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } }, { "frequency, freq", delegate(double param) { _frequency = param; _cycles = (double)base.SamplingRate / _frequency; } } }; _low = -1.0; _high = 1.0; _frequency = 100.0; } public override float NextSample() { double num = (((double)_n % _cycles < _cycles / 2.0) ? _high : _low); _n++; return (float)num; } public override void Reset() { _n = 0; } public override SignalBuilder SampledAt(int samplingRate) { _cycles = (double)samplingRate / _frequency; return base.SampledAt(samplingRate); } protected override DiscreteSignal Generate() { Guard.AgainstNonPositive(_frequency, "Frequency"); Guard.AgainstInvalidRange(_low, _high, "Upper amplitude", "Lower amplitude"); return base.Generate(); } } public class TriangleWaveBuilder : SignalBuilder { private double _low; private double _high; private double _frequency; private int _n; private double _cycles; public TriangleWaveBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } }, { "frequency, freq", delegate(double param) { _frequency = param; _cycles = (double)base.SamplingRate / _frequency; _n = (int)(_cycles / 4.0); } } }; _low = -1.0; _high = 1.0; _frequency = 100.0; } public override float NextSample() { double num = (double)_n % _cycles; double num2 = ((num < _cycles / 2.0) ? (_low + 2.0 * num * (_high - _low) / _cycles) : (_high + 2.0 * (num - _cycles / 2.0) * (_low - _high) / _cycles)); _n++; return (float)num2; } public override void Reset() { _n = (int)(_cycles / 4.0); } public override SignalBuilder SampledAt(int samplingRate) { _cycles = (double)samplingRate / _frequency; _n = (int)(_cycles / 4.0); return base.SampledAt(samplingRate); } protected override DiscreteSignal Generate() { Guard.AgainstNonPositive(_frequency, "Frequency"); Guard.AgainstInvalidRange(_low, _high, "Upper amplitude", "Lower amplitude"); return base.Generate(); } } public class WaveTableBuilder : SignalBuilder { protected float[] _samples; protected float _stride = 1f; protected bool _interpolate; protected float _n; public WaveTableBuilder(float[] samples) { _samples = samples; base.ParameterSetters = new Dictionary> { { "stride, step, delta", delegate(double param) { SetStride(param); } } }; } private void SetStride(double stride) { _stride = (float)stride; _interpolate = Math.Abs(Math.Round(stride) - stride) > 1E-05; } public override float NextSample() { int num = (int)_n % _samples.Length; if (_interpolate) { float num2 = _n - (float)(int)_n; _n += _stride; return _samples[num] + num2 * (_samples[(num + 1) % _samples.Length] - _samples[num]); } _n += _stride; return _samples[num]; } public override void Reset() { _n = 0f; } } public class WhiteNoiseBuilder : SignalBuilder { private double _low; private double _high; private readonly Random _rand = new Random(); public WhiteNoiseBuilder() { base.ParameterSetters = new Dictionary> { { "low, lo, min", delegate(double param) { _low = param; } }, { "high, hi, max", delegate(double param) { _high = param; } } }; _low = -1.0; _high = 1.0; } public override float NextSample() { return (float)(_rand.NextDouble() * (_high - _low) + _low); } protected override DiscreteSignal Generate() { Guard.AgainstInvalidRange(_low, _high, "Upper amplitude", "Lower amplitude"); return base.Generate(); } } } namespace NWaves.Signals.Builders.Base { public interface ISampleGenerator { float NextSample(); void Reset(); } public interface ISignalBuilder { int Length { get; } DiscreteSignal Build(); } public abstract class SignalBuilder : ISampleGenerator, ISignalBuilder { private int _delay; private int _repeatTimes; private readonly List _toSuperimpose = new List(); protected DiscreteSignal Signal { get; set; } protected Dictionary> ParameterSetters { get; set; } public int SamplingRate { get; protected set; } = 1; public int Length { get; protected set; } public double Duration { get; protected set; } public virtual string[] GetParametersInfo() { return ParameterSetters.Keys.ToArray(); } public virtual SignalBuilder SetParameter(string parameterName, double parameterValue) { foreach (string key in ParameterSetters.Keys) { if ((from s in key.Split(new char[1] { ',' }) select s.Trim()).Any((string keyword) => string.Compare(keyword, parameterName, StringComparison.OrdinalIgnoreCase) == 0)) { ParameterSetters[key](parameterValue); return this; } } return this; } public abstract float NextSample(); public virtual void Reset() { } protected virtual DiscreteSignal Generate() { DiscreteSignal discreteSignal = new DiscreteSignal(SamplingRate, Length); for (int i = 0; i < discreteSignal.Length; i++) { discreteSignal[i] = NextSample(); } return discreteSignal; } public virtual DiscreteSignal Build() { DiscreteSignal seed = Generate(); seed = _toSuperimpose.Aggregate(seed, (DiscreteSignal current, DiscreteSignal s) => current.Superimpose(s)); if (_delay != 0) { seed = seed.Delay(_delay); } if (_repeatTimes > 1) { seed = seed.Repeat(_repeatTimes); } return seed; } public virtual SignalBuilder OfLength(int sampleCount) { Length = sampleCount; Duration = (double)sampleCount / (double)SamplingRate; return this; } public virtual SignalBuilder OfDuration(double seconds) { Duration = seconds; Length = (int)(seconds * (double)SamplingRate); return this; } public virtual SignalBuilder SampledAt(int samplingRate) { if (samplingRate <= 0) { throw new ArgumentException("Sampling rate must be positive!"); } SamplingRate = samplingRate; if (Length <= 0) { OfDuration(Duration); } else { OfLength(Length); } return this; } public virtual SignalBuilder DelayedBy(int delay) { _delay += delay; return this; } public virtual SignalBuilder SuperimposedWith(DiscreteSignal signal) { _toSuperimpose.Add(signal); return this; } public virtual SignalBuilder RepeatedTimes(int times) { _repeatTimes += times; return this; } } } namespace NWaves.Operations { public enum DynamicsMode { Compressor, Limiter, Expander, NoiseGate } public class DynamicsProcessor : IFilter, IOnlineFilter { private readonly DynamicsMode _mode; private readonly EnvelopeFollower _envelopeFollower; private readonly int _fs; private readonly float _minAmplitudeDb; private readonly float T = 1f / (float)Math.Log(9.0); public float Threshold { get; set; } public float Ratio { get; set; } public float MakeupGain { get; set; } public float Attack { get { DynamicsMode mode = _mode; if ((uint)mode <= 1u || (uint)(mode - 2) > 1u) { return _envelopeFollower.AttackTime / T; } return _envelopeFollower.ReleaseTime / T; } set { DynamicsMode mode = _mode; if ((uint)mode <= 1u || (uint)(mode - 2) > 1u) { _envelopeFollower.AttackTime = value * T; } else { _envelopeFollower.ReleaseTime = value * T; } } } public float Release { get { DynamicsMode mode = _mode; if ((uint)mode <= 1u || (uint)(mode - 2) > 1u) { return _envelopeFollower.ReleaseTime / T; } return _envelopeFollower.AttackTime / T; } set { DynamicsMode mode = _mode; if ((uint)mode <= 1u || (uint)(mode - 2) > 1u) { _envelopeFollower.ReleaseTime = value * T; } else { _envelopeFollower.AttackTime = value * T; } } } public DynamicsProcessor(DynamicsMode mode, int samplingRate, float threshold, float ratio, float makeupGain = 0f, float attack = 0.01f, float release = 0.1f, float minAmplitudeDb = -120f) { _mode = mode; _fs = samplingRate; _envelopeFollower = new EnvelopeFollower(_fs); _minAmplitudeDb = minAmplitudeDb; Threshold = threshold; Ratio = ratio; MakeupGain = makeupGain; Attack = attack; Release = release; } public float Process(float sample) { float num = Math.Abs(sample); float num2 = ((num > 1E-06f) ? ((float)Scale.ToDecibel(num)) : _minAmplitudeDb); float num3 = 0f; switch (_mode) { case DynamicsMode.Compressor: case DynamicsMode.Limiter: num3 = ((num2 < Threshold) ? num2 : (Threshold + (num2 - Threshold) / Ratio)); break; case DynamicsMode.Expander: case DynamicsMode.NoiseGate: num3 = ((num2 > Threshold) ? num2 : (Threshold + (num2 - Threshold) * Ratio)); break; } float num4 = _envelopeFollower.Process(num3 - num2); float num5 = (float)Scale.FromDecibel(MakeupGain - num4); return sample * num5; } public void Reset() { _envelopeFollower.Reset(); } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } public class EnvelopeFollower : IFilter, IOnlineFilter { private float _attackTime; private float _releaseTime; private readonly int _fs; private float _env; private float _ga; private float _gr; public float AttackTime { get { return _attackTime; } set { _attackTime = value; _ga = (((double)value < 1E-20) ? 0f : ((float)Math.Exp(-1.0 / (double)(value * (float)_fs)))); } } public float ReleaseTime { get { return _releaseTime; } set { _releaseTime = value; _gr = (((double)value < 1E-20) ? 0f : ((float)Math.Exp(-1.0 / (double)(value * (float)_fs)))); } } public EnvelopeFollower(int samplingRate, float attackTime = 0.01f, float releaseTime = 0.05f) { _fs = samplingRate; AttackTime = attackTime; ReleaseTime = releaseTime; } public float Process(float sample) { float num = Math.Abs(sample); _env = ((_env < num) ? (_ga * _env + (1f - _ga) * num) : (_gr * _env + (1f - _gr) * num)); return _env; } public void Reset() { _env = 0f; } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } public class GriffinLimReconstructor { private readonly Stft _stft; private readonly List _magnitudes; public float Gain { get; set; } = 16f; public GriffinLimReconstructor(List spectrogram, int windowSize = 1024, int hopSize = 256, WindowType window = WindowType.Hann, int power = 2) : this(spectrogram, new Stft(windowSize, hopSize, window), power) { } public GriffinLimReconstructor(List spectrogram, Stft stft, int power = 2) { _stft = stft; _magnitudes = spectrogram; if (power == 2) { for (int i = 0; i < _magnitudes.Count; i++) { for (int j = 0; j < _magnitudes[i].Length; j++) { _magnitudes[i][j] = (float)Math.Sqrt(_magnitudes[i][j]); } } } for (int k = 0; k < _magnitudes.Count; k++) { for (int l = 0; l < _magnitudes[k].Length; l++) { _magnitudes[k][l] *= Gain; } } } public float[] Iterate(float[] signal = null) { MagnitudePhaseList spectrogram = new MagnitudePhaseList { Magnitudes = _magnitudes }; if (signal == null) { int count = _magnitudes[0].Length; Random r = new Random(); List list = new List(); for (int i = 0; i < _magnitudes.Count; i++) { list.Add((from s in Enumerable.Range(0, count) select (float)(Math.PI * 2.0 * r.NextDouble())).ToArray()); } spectrogram.Phases = list; } else { spectrogram.Phases = _stft.MagnitudePhaseSpectrogram(signal).Phases; } return _stft.ReconstructMagnitudePhase(spectrogram); } public float[] Reconstruct(int iterations = 20) { float[] array = Iterate(); for (int i = 0; i < iterations - 1; i++) { array = Iterate(array); } return array; } } public class HarmonicPercussiveSeparator { private readonly Stft _stft; private readonly Func _mask; private readonly MedianFilter _medianHarmonic; private readonly MedianFilter _medianPercussive; public HarmonicPercussiveSeparator(int fftSize = 2048, int hopSize = 512, int harmonicWinSize = 17, int percussiveWinSize = 17, HpsMasking masking = HpsMasking.WienerOrder2) { _stft = new Stft(fftSize, hopSize); _medianHarmonic = new MedianFilter(harmonicWinSize); _medianPercussive = new MedianFilter(percussiveWinSize); switch (masking) { case HpsMasking.Binary: _mask = BinaryMask; break; case HpsMasking.WienerOrder1: _mask = WienerMask1; break; default: _mask = WienerMask2; break; } } public (MagnitudePhaseList, MagnitudePhaseList) EvaluateSpectrograms(DiscreteSignal signal) { MagnitudePhaseList item = _stft.MagnitudePhaseSpectrogram(signal); List magnitudes = item.Magnitudes; List list = new List(magnitudes.Count); for (int i = 0; i < magnitudes.Count; i++) { DiscreteSignal signal2 = new DiscreteSignal(1, magnitudes[i]); list.Add(_medianPercussive.ApplyTo(signal2).Samples); _medianPercussive.Reset(); } for (int j = 0; j <= _stft.Size / 2; j++) { int num = 0; int k; for (k = 0; k < _medianHarmonic.Size / 2; k++) { _medianHarmonic.Process(magnitudes[k][j]); } while (num < magnitudes.Count - _medianHarmonic.Size / 2) { float num2 = _medianHarmonic.Process(magnitudes[k][j]); magnitudes[num][j] *= _mask(num2, list[k][j]); list[num][j] *= _mask(list[k][j], num2); num++; k++; } for (k = 0; k < _medianHarmonic.Size / 2; k++) { float num3 = _medianHarmonic.Process(0f); magnitudes[num][j] *= _mask(num3, list[num][j]); list[num][j] *= _mask(list[num][j], num3); num++; } _medianHarmonic.Reset(); } MagnitudePhaseList item2 = new MagnitudePhaseList { Magnitudes = list, Phases = item.Phases }; return (item, item2); } public (DiscreteSignal, DiscreteSignal) EvaluateSignals(DiscreteSignal signal) { (MagnitudePhaseList, MagnitudePhaseList) tuple = EvaluateSpectrograms(signal); MagnitudePhaseList item = tuple.Item1; MagnitudePhaseList item2 = tuple.Item2; DiscreteSignal item3 = new DiscreteSignal(signal.SamplingRate, _stft.ReconstructMagnitudePhase(item)); DiscreteSignal item4 = new DiscreteSignal(signal.SamplingRate, _stft.ReconstructMagnitudePhase(item2)); return (item3, item4); } private float BinaryMask(float h, float p) { return (h > p) ? 1 : 0; } private float WienerMask1(float h, float p) { if (!((double)(h + p) > 1E-10)) { return 0f; } return h / (h + p); } private float WienerMask2(float h, float p) { if (!((double)(h + p) > 1E-10)) { return 0f; } return h * h / (h * h + p * p); } } public enum HpsMasking { Binary, WienerOrder1, WienerOrder2 } public class Modulator { public static DiscreteSignal Ring(DiscreteSignal carrier, DiscreteSignal modulator) { if (carrier.SamplingRate != modulator.SamplingRate) { throw new ArgumentException("Sampling rates must be the same!"); } return new DiscreteSignal(carrier.SamplingRate, carrier.Samples.Zip(modulator.Samples, (float c, float m) => c * m)); } public static DiscreteSignal Amplitude(DiscreteSignal carrier, float modulatorFrequency = 20f, float modulationIndex = 0.5f) { int fs = carrier.SamplingRate; IEnumerable values = from i in Enumerable.Range(0, carrier.Length) select (double)carrier[i] * (1.0 + (double)modulationIndex * Math.Cos(Math.PI * 2.0 * (double)modulatorFrequency / (double)fs * (double)i)); return new DiscreteSignal(fs, values.ToFloats()); } public static DiscreteSignal Frequency(DiscreteSignal baseband, float carrierAmplitude, float carrierFrequency, float deviation = 0.1f) { int fs = baseband.SamplingRate; double integral = 0.0; IEnumerable values = from i in Enumerable.Range(0, baseband.Length) select (double)carrierAmplitude * Math.Cos(Math.PI * 2.0 * (double)carrierFrequency / (double)fs * (double)i + Math.PI * 2.0 * (double)deviation * (integral += baseband[i])); return new DiscreteSignal(fs, values.ToFloats()); } public static DiscreteSignal FrequencySinusoidal(float carrierFrequency, float carrierAmplitude, float modulatorFrequency, float modulationIndex, int length, int samplingRate = 1) { int fs = samplingRate; IEnumerable values = from i in Enumerable.Range(0, length) select (double)carrierAmplitude * Math.Cos(Math.PI * 2.0 * (double)carrierFrequency / (double)fs * (double)i + (double)modulationIndex * Math.Sin(Math.PI * 2.0 * (double)modulatorFrequency / (double)fs * (double)i)); return new DiscreteSignal(samplingRate, values.ToFloats()); } public static DiscreteSignal FrequencyLinear(float carrierFrequency, float carrierAmplitude, float modulationIndex, int length, int samplingRate = 1) { IEnumerable values = from i in Enumerable.Range(0, length) select (double)carrierAmplitude * Math.Cos(Math.PI * 2.0 * (double)(carrierFrequency / (float)samplingRate + modulationIndex * (float)i) * (double)i / (double)samplingRate); return new DiscreteSignal(samplingRate, values.ToFloats()); } public static DiscreteSignal Phase(DiscreteSignal baseband, float carrierAmplitude, float carrierFrequency, float deviation = 0.8f) { int fs = baseband.SamplingRate; IEnumerable values = from i in Enumerable.Range(0, baseband.Length) select (double)carrierAmplitude * Math.Cos(Math.PI * 2.0 * (double)carrierFrequency / (double)fs * (double)i + (double)(deviation * baseband[i])); return new DiscreteSignal(fs, values.ToFloats()); } public static DiscreteSignal DemodulateAmplitude(DiscreteSignal signal) { double[] magnitude = new HilbertTransform(signal.Length).AnalyticSignal(signal.Samples).Magnitude; return new DiscreteSignal(signal.SamplingRate, magnitude.ToFloats()) - 1f; } public static DiscreteSignal DemodulateFrequency(DiscreteSignal signal) { float[] array = new float[signal.Length]; MathUtils.Diff(signal.Samples, array); double[] magnitude = new HilbertTransform(signal.Length).AnalyticSignal(array).Magnitude; return new DiscreteSignal(signal.SamplingRate, magnitude.ToFloats()) - 1f; } } public static class Operation { public static DiscreteSignal Convolve(DiscreteSignal signal, DiscreteSignal kernel) { return new Convolver().Convolve(signal, kernel); } public static ComplexDiscreteSignal Convolve(ComplexDiscreteSignal signal, ComplexDiscreteSignal kernel) { return new ComplexConvolver().Convolve(signal, kernel); } public static double[] Convolve(double[] signal, double[] kernel) { return Convolve(new ComplexDiscreteSignal(1, signal), new ComplexDiscreteSignal(1, kernel)).Real; } public static DiscreteSignal CrossCorrelate(DiscreteSignal signal1, DiscreteSignal signal2) { return new Convolver().CrossCorrelate(signal1, signal2); } public static ComplexDiscreteSignal CrossCorrelate(ComplexDiscreteSignal signal1, ComplexDiscreteSignal signal2) { return new ComplexConvolver().CrossCorrelate(signal1, signal2); } public static DiscreteSignal BlockConvolve(DiscreteSignal signal, DiscreteSignal kernel, int fftSize, FilteringMethod method = FilteringMethod.OverlapSave) { IFilter filter = ((method != FilteringMethod.OverlapAdd) ? ((IFilter)new OlsBlockConvolver(kernel.Samples, fftSize)) : ((IFilter)new OlaBlockConvolver(kernel.Samples, fftSize))); return filter.ApplyTo(signal); } public static ComplexDiscreteSignal Deconvolve(ComplexDiscreteSignal signal, ComplexDiscreteSignal kernel) { return new ComplexConvolver().Deconvolve(signal, kernel); } public static DiscreteSignal Interpolate(DiscreteSignal signal, int factor, FirFilter filter = null) { return new Resampler().Interpolate(signal, factor, filter); } public static DiscreteSignal Decimate(DiscreteSignal signal, int factor, FirFilter filter = null) { return new Resampler().Decimate(signal, factor, filter); } public static DiscreteSignal Resample(DiscreteSignal signal, int newSamplingRate, FirFilter filter = null, int order = 15) { return new Resampler().Resample(signal, newSamplingRate, filter, order); } public static DiscreteSignal ResampleUpDown(DiscreteSignal signal, int up, int down, FirFilter filter = null) { return new Resampler().ResampleUpDown(signal, up, down, filter); } public static DiscreteSignal TimeStretch(DiscreteSignal signal, double stretch, int windowSize, int hopSize, TsmAlgorithm algorithm = TsmAlgorithm.PhaseVocoderPhaseLocking) { if (Math.Abs(stretch - 1.0) < 1E-10) { return signal.Copy(); } return (algorithm switch { TsmAlgorithm.PhaseVocoder => new PhaseVocoder(stretch, hopSize, windowSize), TsmAlgorithm.PhaseVocoderPhaseLocking => new PhaseLockingVocoder(stretch, hopSize, windowSize), TsmAlgorithm.PaulStretch => new PaulStretch(stretch, hopSize, windowSize), _ => new Wsola(stretch, windowSize, hopSize), }).ApplyTo(signal); } public static DiscreteSignal TimeStretch(DiscreteSignal signal, double stretch, TsmAlgorithm algorithm = TsmAlgorithm.PhaseVocoderPhaseLocking) { if (Math.Abs(stretch - 1.0) < 1E-10) { return signal.Copy(); } int num = MathUtils.NextPowerOfTwo(1024 * signal.SamplingRate / 16000); return (algorithm switch { TsmAlgorithm.PhaseVocoder => new PhaseVocoder(stretch, num / 10, num), TsmAlgorithm.PhaseVocoderPhaseLocking => new PhaseLockingVocoder(stretch, num / 8, num), TsmAlgorithm.PaulStretch => new PaulStretch(stretch, num / 10, num * 4), _ => new Wsola(stretch), }).ApplyTo(signal); } public static DiscreteSignal Envelope(DiscreteSignal signal, float attackTime = 0.01f, float releaseTime = 0.05f) { EnvelopeFollower envelopeFollower = new EnvelopeFollower(signal.SamplingRate, attackTime, releaseTime); return new DiscreteSignal(signal.SamplingRate, signal.Samples.Select((float s) => envelopeFollower.Process(s))); } public static DiscreteSignal FullRectify(DiscreteSignal signal) { return new DiscreteSignal(signal.SamplingRate, signal.Samples.Select((float s) => (!(s < 0f)) ? s : (0f - s))); } public static DiscreteSignal HalfRectify(DiscreteSignal signal) { return new DiscreteSignal(signal.SamplingRate, signal.Samples.Select((float s) => (!(s < 0f)) ? s : 0f)); } public static DiscreteSignal SpectralSubtract(DiscreteSignal signal, DiscreteSignal noise, int fftSize = 1024, int hopSize = 256) { return new SpectralSubtractor(noise, fftSize, hopSize).ApplyTo(signal); } public static void NormalizePeak(float[] samples, double peakDb) { float num = (float)Scale.FromDecibel(peakDb) / samples.Max((float x) => Math.Abs(x)); for (int num2 = 0; num2 < samples.Length; num2++) { samples[num2] *= num; } } public static DiscreteSignal NormalizePeak(DiscreteSignal signal, double peakDb) { DiscreteSignal discreteSignal = signal.Copy(); NormalizePeak(discreteSignal.Samples, peakDb); return discreteSignal; } public static void ChangePeak(float[] samples, double peakDb) { float num = (float)Scale.FromDecibel(peakDb); for (int i = 0; i < samples.Length; i++) { samples[i] *= num; } } public static DiscreteSignal ChangePeak(DiscreteSignal signal, double peakDb) { DiscreteSignal discreteSignal = signal.Copy(); ChangeRms(discreteSignal.Samples, peakDb); return discreteSignal; } public static void NormalizeRms(float[] samples, double rmsDb) { float num = 0f; for (int i = 0; i < samples.Length; i++) { num += samples[i] * samples[i]; } float num2 = (float)Math.Sqrt((double)samples.Length * Math.Pow(10.0, rmsDb / 10.0) / (double)num); for (int j = 0; j < samples.Length; j++) { samples[j] *= num2; } } public static DiscreteSignal NormalizeRms(DiscreteSignal signal, double rmsDb) { DiscreteSignal discreteSignal = signal.Copy(); NormalizeRms(discreteSignal.Samples, rmsDb); return discreteSignal; } public static void ChangeRms(float[] samples, double rmsDb) { float num = 0f; for (int i = 0; i < samples.Length; i++) { num += samples[i] * samples[i]; } double num2 = -20.0 * Math.Log10(Math.Sqrt(num / (float)samples.Length)); rmsDb -= num2; float num3 = (float)Math.Sqrt((double)samples.Length * Math.Pow(10.0, rmsDb / 10.0) / (double)num); for (int j = 0; j < samples.Length; j++) { samples[j] *= num3; } } public static DiscreteSignal ChangeRms(DiscreteSignal signal, double rmsDb) { DiscreteSignal discreteSignal = signal.Copy(); ChangeRms(discreteSignal.Samples, rmsDb); return discreteSignal; } public static float[] Welch(DiscreteSignal signal, int windowSize = 1024, int hopSize = 256, WindowType window = WindowType.Hann, int fftSize = 0, int samplingRate = 0) { float[] array = new Stft(windowSize, hopSize, window, fftSize).AveragePeriodogram(signal.Samples); float num2; if (samplingRate > 0) { float num = (from w in Window.OfType(window, windowSize) select w * w).Sum(); num2 = 2f / (num * (float)samplingRate); } else { float num3 = Window.OfType(window, windowSize).Sum(); num2 = 2f / (num3 * num3); } for (int num4 = 0; num4 < array.Length; num4++) { array[num4] *= num2; } return array; } public static float[] LombScargle(float[] x, float[] y, float[] freqs, bool subtractMean = false, bool normalize = false) { Guard.AgainstInequality(x.Length, y.Length, "X array size", "Y array size"); float[] array = new float[freqs.Length]; if (subtractMean) { float num = y.Average(); for (int i = 0; i < y.Length; i++) { y[i] -= num; } } float[] array2 = new float[x.Length]; float[] array3 = new float[x.Length]; for (int j = 0; j < freqs.Length; j++) { float num2 = 0f; float num3 = 0f; float num4 = 0f; float num5 = 0f; float num6 = 0f; for (int k = 0; k < x.Length; k++) { array2[k] = (float)Math.Cos(freqs[j] * x[k]); array3[k] = (float)Math.Sin(freqs[j] * x[k]); num2 += y[k] * array2[k]; num3 += y[k] * array3[k]; num4 += array2[k] * array2[k]; num5 += array3[k] * array3[k]; num6 += array2[k] * array3[k]; float num7 = (float)Math.Atan2(2f * num6, num4 - num5) / (2f * freqs[j]); float num8 = (float)Math.Cos(freqs[j] * num7); float num9 = (float)Math.Sin(freqs[j] * num7); float num10 = num8 * num8; float num11 = num9 * num9; float num12 = 2f * num8 * num9; array[j] = 0.5f * ((num8 * num2 + num9 * num3) * (num8 * num2 + num9 * num3) / (num10 * num4 + num12 * num6 + num11 * num5) + (num8 * num3 - num9 * num2) * (num8 * num3 - num9 * num2) / (num10 * num5 - num12 * num6 + num11 * num4)); } } if (normalize) { float num13 = 2f / y.Sum((float v) => v * v); for (int num14 = 0; num14 < array.Length; num14++) { array[num14] *= num13; } } return array; } } public class Resampler { public int MinResamplingFilterOrder { get; set; } = 101; public DiscreteSignal Interpolate(DiscreteSignal signal, int factor, FirFilter filter = null) { if (factor == 1) { return signal.Copy(); } float[] array = new float[signal.Length * factor]; int num = 0; for (int i = 0; i < signal.Length; i++) { array[num] = (float)factor * signal[i]; num += factor; } FirFilter firFilter = filter; if (filter == null) { firFilter = new FirFilter(DesignFilter.FirWinLp((factor > MinResamplingFilterOrder / 2) ? (2 * factor + 1) : MinResamplingFilterOrder, 0.5f / (float)factor)); } return firFilter.ApplyTo(new DiscreteSignal(signal.SamplingRate * factor, array)); } public DiscreteSignal Decimate(DiscreteSignal signal, int factor, FirFilter filter = null) { if (factor == 1) { return signal.Copy(); } int order = ((factor > MinResamplingFilterOrder / 2) ? (2 * factor + 1) : MinResamplingFilterOrder); if (filter == null) { signal = new FirFilter(DesignFilter.FirWinLp(order, 0.5f / (float)factor)).ApplyTo(signal); } float[] array = new float[signal.Length / factor]; int num = 0; for (int i = 0; i < array.Length; i++) { array[i] = signal[num]; num += factor; } return new DiscreteSignal(signal.SamplingRate / factor, array); } public DiscreteSignal Resample(DiscreteSignal signal, int newSamplingRate, FirFilter filter = null, int order = 15) { if (signal.SamplingRate == newSamplingRate) { return signal.Copy(); } float num = (float)newSamplingRate / (float)signal.SamplingRate; float[] samples = signal.Samples; float[] array = new float[(int)((float)samples.Length * num)]; if (num < 1f && filter == null) { filter = new FirFilter(DesignFilter.FirWinLp(MinResamplingFilterOrder, num / 2f)); samples = filter.ApplyTo(signal).Samples; } float num2 = 1f / num; for (int i = 0; i < array.Length; i++) { float num3 = (float)i * num2; for (int j = -order; j < order; j++) { int num4 = (int)Math.Floor(num3) - j; if (num4 >= 0 && num4 < samples.Length) { float num5 = num3 - (float)num4; float num6 = (float)(0.5 * (1.0 + Math.Cos((double)(num5 / (float)order) * Math.PI))); float num7 = (float)MathUtils.Sinc(num5); array[i] += num6 * num7 * samples[num4]; } } } return new DiscreteSignal(newSamplingRate, array); } public DiscreteSignal ResampleUpDown(DiscreteSignal signal, int up, int down, FirFilter filter = null) { if (up == down) { return signal.Copy(); } int num = signal.SamplingRate * up / down; if (up > 20 && down > 20) { return Resample(signal, num, filter); } float[] array = new float[signal.Length * up]; int num2 = 0; for (int i = 0; i < signal.Length; i++) { array[num2] = (float)up * signal[i]; num2 += up; } FirFilter firFilter = filter; if (filter == null) { int num3 = Math.Max(up, down); firFilter = new FirFilter(DesignFilter.FirWinLp((num3 > MinResamplingFilterOrder / 2) ? (8 * num3 + 1) : MinResamplingFilterOrder, 0.5f / (float)num3)); } DiscreteSignal discreteSignal = firFilter.ApplyTo(new DiscreteSignal(signal.SamplingRate * up, array)); array = new float[discreteSignal.Length / down]; num2 = 0; for (int j = 0; j < array.Length; j++) { array[j] = discreteSignal[num2]; num2 += down; } return new DiscreteSignal(num, array); } } public class SpectralSubtractor : OverlapAddFilter { private readonly float[] _noiseEstimate; private readonly float[] _noiseBuf; private readonly float[] _noiseSpectrum; private readonly float[] _noiseAcc; public float Beta { get; set; } = 0.009f; public float AlphaMin { get; set; } = 2f; public float AlphaMax { get; set; } = 5f; public float SnrMin { get; set; } = -5f; public float SnrMax { get; set; } = 20f; public SpectralSubtractor(float[] noise, int fftSize = 1024, int hopSize = 128) : base(hopSize, fftSize) { _noiseEstimate = new float[_fftSize / 2 + 1]; _noiseBuf = new float[_fftSize]; _noiseSpectrum = new float[_fftSize / 2 + 1]; _noiseAcc = new float[_fftSize / 2 + 2]; EstimateNoise(noise); } public SpectralSubtractor(DiscreteSignal noise, int fftSize = 1024, int hopSize = 128) : this(noise.Samples, fftSize, hopSize) { } protected override void ProcessSpectrum(float[] re, float[] im, float[] filteredRe, float[] filteredIm) { float num = (AlphaMin - AlphaMax) / (SnrMax - SnrMin); float num2 = AlphaMax - num * SnrMin; for (int i = 1; i <= _fftSize / 2; i++) { float num3 = re[i] * re[i] + im[i] * im[i]; double num4 = Math.Atan2(im[i], re[i]); float num5 = _noiseEstimate[i]; double num6 = 10.0 * Math.Log10(num3 / num5); double num7 = Math.Max(Math.Min((double)num * num6 + (double)num2, AlphaMax), AlphaMin); double num8 = Math.Sqrt(Math.Max((double)num3 - num7 * (double)num5, Beta * num5)); filteredRe[i] = (float)(num8 * Math.Cos(num4)); filteredIm[i] = (float)(num8 * Math.Sin(num4)); } } public void EstimateNoise(float[] noise, int startPos = 0, int endPos = -1) { if (endPos < 0) { endPos = noise.Length + endPos + 1; } int num = 0; int num2 = startPos; while (num2 + _fftSize < endPos) { noise.FastCopyTo(_noiseBuf, _fftSize, num2); _fft.PowerSpectrum(_noiseBuf, _noiseSpectrum, normalize: false); for (int i = 1; i <= _fftSize / 2; i++) { _noiseAcc[i] += _noiseSpectrum[i]; } num2 += _hopSize; num++; } for (int j = 1; j <= _fftSize / 2; j++) { _noiseEstimate[j] = (_noiseAcc[j - 1] + _noiseAcc[j] + _noiseAcc[j + 1]) / (float)(3 * num); } } public void EstimateNoise(DiscreteSignal noise, int startPos = 0, int endPos = -1) { EstimateNoise(noise.Samples, startPos, endPos); } } public class WaveShaper : IFilter, IOnlineFilter { private readonly Func _waveShapingFunction; public WaveShaper(Func waveShapingFunction) { _waveShapingFunction = waveShapingFunction; } public float Process(float sample) { return _waveShapingFunction(sample); } public void Reset() { } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } } namespace NWaves.Operations.Tsm { internal class PaulStretch : PhaseVocoder { private readonly Random _rand = new Random(); public PaulStretch(double stretch, int hopAnalysis, int fftSize = 0) : base(stretch, hopAnalysis, fftSize) { } protected override void ProcessSpectrum() { for (int i = 1; i <= _fftSize / 2; i++) { double num = Math.Sqrt(_re[i] * _re[i] + _im[i] * _im[i]); double num2 = Math.PI * 2.0 * _rand.NextDouble(); _re[i] = (float)(num * Math.Cos(num2)); _im[i] = (float)(num * Math.Sin(num2)); } } public override void Reset() { } } public class PhaseLockingVocoder : PhaseVocoder { private readonly double[] _mag; private readonly double[] _phase; private readonly double[] _delta; private readonly int[] _peaks; public PhaseLockingVocoder(double stretch, int hopAnalysis, int fftSize = 0) : base(stretch, hopAnalysis, fftSize) { _mag = new double[_fftSize / 2 + 1]; _phase = new double[_fftSize / 2 + 1]; _delta = new double[_fftSize / 2 + 1]; _peaks = new int[_fftSize / 4]; } protected override void ProcessSpectrum() { for (int i = 0; i < _mag.Length; i++) { _mag[i] = Math.Sqrt(_re[i] * _re[i] + _im[i] * _im[i]); _phase[i] = Math.Atan2(_im[i], _re[i]); } int num = 0; for (int j = 2; j < _mag.Length - 3; j++) { if (!(_mag[j] <= _mag[j - 1]) && !(_mag[j] <= _mag[j - 2]) && !(_mag[j] <= _mag[j + 1]) && !(_mag[j] <= _mag[j + 2])) { _peaks[num++] = j; } } _peaks[num++] = _mag.Length - 1; int num2 = 1; for (int k = 0; k < num - 1; k++) { int num3 = _peaks[k]; double num4 = _phase[num3]; _delta[num3] = num4 - _prevPhase[num3]; double num5 = MathUtils.Mod(_delta[num3] - (double)_hopAnalysis * _omega[num3] + Math.PI, Math.PI * 2.0) - Math.PI; double num6 = _omega[num3] + num5 / (double)_hopAnalysis; _phaseTotal[num3] += (double)_hopSynthesis * num6; int num7 = (_peaks[k] + _peaks[k + 1]) / 2; for (int l = num2; l < num7; l++) { _phaseTotal[l] = _phaseTotal[num3] + _phase[l] - _phase[num3]; _prevPhase[l] = _phase[l]; _re[l] = (float)(_mag[l] * Math.Cos(_phaseTotal[l])); _im[l] = (float)(_mag[l] * Math.Sin(_phaseTotal[l])); } num2 = num7; } } } public class PhaseVocoder : IFilter { protected readonly int _hopAnalysis; protected readonly int _hopSynthesis; protected readonly int _fftSize; protected readonly double _stretch; protected readonly RealFft _fft; protected readonly float[] _window; protected readonly float _gain; protected readonly double[] _omega; protected readonly float[] _re; protected readonly float[] _im; protected readonly double[] _prevPhase; protected readonly double[] _phaseTotal; public PhaseVocoder(double stretch, int hopAnalysis, int fftSize = 0) { _stretch = stretch; _hopAnalysis = hopAnalysis; _hopSynthesis = (int)((double)hopAnalysis * stretch); _fftSize = ((fftSize > 0) ? fftSize : (8 * Math.Max(_hopAnalysis, _hopSynthesis))); _fft = new RealFft(_fftSize); _window = Window.OfType(WindowType.Hann, _fftSize); _gain = 1f / ((float)_fftSize * _window.Select((float w) => w * w).Sum() / (float)_hopSynthesis); _omega = (from f in Enumerable.Range(0, _fftSize / 2 + 1) select Math.PI * 2.0 * (double)f / (double)_fftSize).ToArray(); _re = new float[_fftSize]; _im = new float[_fftSize]; _prevPhase = new double[_fftSize / 2 + 1]; _phaseTotal = new double[_fftSize / 2 + 1]; } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { float[] samples = signal.Samples; float[] array = new float[(int)((double)samples.Length * _stretch) + _fftSize]; int i = 0; for (int j = 0; j + _fftSize < samples.Length; j += _hopAnalysis) { samples.FastCopyTo(_re, _fftSize, j); _re.ApplyWindow(_window); _fft.Direct(_re, _re, _im); ProcessSpectrum(); _fft.Inverse(_re, _im, _re); for (int k = 0; k < _re.Length; k++) { array[i + k] += _re[k] * _window[k]; } for (int l = 0; l < _hopSynthesis; l++) { array[i + l] *= _gain; } i += _hopSynthesis; } for (; i < array.Length; i++) { array[i] *= _gain; } return new DiscreteSignal(signal.SamplingRate, array); } protected virtual void ProcessSpectrum() { for (int i = 1; i <= _fftSize / 2; i++) { double num = Math.Sqrt(_re[i] * _re[i] + _im[i] * _im[i]); double num2 = Math.Atan2(_im[i], _re[i]); double num3 = MathUtils.Mod(num2 - _prevPhase[i] - (double)_hopAnalysis * _omega[i] + Math.PI, Math.PI * 2.0) - Math.PI; double num4 = _omega[i] + num3 / (double)_hopAnalysis; _phaseTotal[i] += (double)_hopSynthesis * num4; _prevPhase[i] = num2; _re[i] = (float)(num * Math.Cos(_phaseTotal[i])); _im[i] = (float)(num * Math.Sin(_phaseTotal[i])); } } public virtual void Reset() { Array.Clear(_phaseTotal, 0, _phaseTotal.Length); Array.Clear(_prevPhase, 0, _prevPhase.Length); } } public enum TsmAlgorithm { PhaseVocoder, PhaseVocoderPhaseLocking, Wsola, PaulStretch } public class Wsola : IFilter { protected readonly double _stretch; private int _windowSize; private int _hopAnalysis; private int _hopSynthesis; private int _maxDelta; private readonly bool _userParameters; private Convolver _convolver; private float[] _cc; public Wsola(double stretch, int windowSize, int hopAnalysis, int maxDelta = 0) { _stretch = stretch; _windowSize = Math.Max(windowSize, 32); _hopAnalysis = Math.Max(hopAnalysis, 10); _hopSynthesis = (int)((double)_hopAnalysis * stretch); _maxDelta = ((maxDelta > 2) ? maxDelta : _hopSynthesis); _userParameters = true; PrepareConvolver(); } public Wsola(double stretch) { _stretch = stretch; if (_stretch > 1.5) { _windowSize = 1024; _hopAnalysis = 128; } else if (_stretch > 1.1) { _windowSize = 1536; _hopAnalysis = 256; } else if (_stretch > 0.6) { _windowSize = 1536; _hopAnalysis = 690; } else { _windowSize = 1024; _hopAnalysis = 896; } _hopSynthesis = (int)((double)_hopAnalysis * stretch); _maxDelta = _hopSynthesis; PrepareConvolver(); } private void PrepareConvolver() { int num = MathUtils.NextPowerOfTwo(_windowSize + _maxDelta - 1); if (num >= 512) { _convolver = new Convolver(num); _cc = new float[num]; } } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { if (signal.SamplingRate != 22050 && !_userParameters) { float num = (float)signal.SamplingRate / 22050f; _windowSize = (int)((float)_windowSize * num); _hopAnalysis = (int)((float)_hopAnalysis * num); _hopSynthesis = (int)((double)_hopAnalysis * _stretch); _maxDelta = (int)((float)_maxDelta * num); PrepareConvolver(); } float[] array = Window.OfType(WindowType.Hann, _windowSize); float num2 = (float)_hopSynthesis / array.Select((float w) => w * w).Sum() * 0.75f; float[] samples = signal.Samples; float[] array2 = new float[(int)(_stretch * (double)(samples.Length + _windowSize))]; float[] array3 = new float[_windowSize + _maxDelta]; float[] array4 = new float[_windowSize]; int num3 = 0; int num4 = 0; while (num4 + _windowSize + _maxDelta + _hopSynthesis < samples.Length) { int num5 = 0; if (num4 > _maxDelta / 2) { samples.FastCopyTo(array3, _windowSize + _maxDelta, num4 - _maxDelta / 2); num5 = WaveformSimilarityPos(array3, array4, _maxDelta); } else { samples.FastCopyTo(array3, _windowSize + _maxDelta, num4); } int num6 = Math.Min(_windowSize, array2.Length - num3); for (int num7 = 0; num7 < num6; num7++) { array2[num3 + num7] += array3[num5 + num7] * array[num7]; } for (int num8 = 0; num8 < _hopSynthesis; num8++) { array2[num3 + num8] *= num2; } samples.FastCopyTo(array4, _windowSize, num4 + num5 - _maxDelta / 2 + _hopSynthesis); num4 += _hopAnalysis; num3 += _hopSynthesis; } return new DiscreteSignal(signal.SamplingRate, array2); } protected int WaveformSimilarityPos(float[] current, float[] prev, int maxDelta) { int result = 0; float num = 0f; if (_convolver == null) { for (int i = 0; i < maxDelta; i++) { float num2 = 0f; for (int j = 0; j < prev.Length; j++) { num2 += current[i + j] * prev[j]; } if (num2 > num) { num = num2; result = i; } } } else { _convolver.CrossCorrelate(current, prev, _cc); int num3 = prev.Length - 1; int num4 = 0; while (num3 < prev.Length + _maxDelta - 1) { if (_cc[num3] > num) { num = _cc[num3]; result = num4; } num3++; num4++; } } return result; } } } namespace NWaves.Operations.Convolution { public class ComplexConvolver { public ComplexDiscreteSignal Convolve(ComplexDiscreteSignal signal, ComplexDiscreteSignal kernel, int fftSize = 0) { int n = signal.Length + kernel.Length - 1; if (fftSize == 0) { fftSize = MathUtils.NextPowerOfTwo(n); } Fft64 fft = new Fft64(fftSize); signal = signal.ZeroPadded(fftSize); kernel = kernel.ZeroPadded(fftSize); fft.Direct(signal.Real, signal.Imag); fft.Direct(kernel.Real, kernel.Imag); ComplexDiscreteSignal complexDiscreteSignal = signal.Multiply(kernel); fft.Inverse(complexDiscreteSignal.Real, complexDiscreteSignal.Imag); for (int i = 0; i < complexDiscreteSignal.Length; i++) { complexDiscreteSignal.Real[i] /= fftSize; complexDiscreteSignal.Imag[i] /= fftSize; } return new ComplexDiscreteSignal(signal.SamplingRate, complexDiscreteSignal.Real, complexDiscreteSignal.Imag).First(n); } public ComplexDiscreteSignal CrossCorrelate(ComplexDiscreteSignal signal, ComplexDiscreteSignal kernel, int fftSize = 0) { ComplexDiscreteSignal kernel2 = new ComplexDiscreteSignal(kernel.SamplingRate, kernel.Real.Reverse(), kernel.Imag.Reverse()); return Convolve(signal, kernel2, fftSize); } public ComplexDiscreteSignal Deconvolve(ComplexDiscreteSignal signal, ComplexDiscreteSignal kernel, int fftSize = 0) { Complex[][] array = MathUtils.DividePolynomial(signal.Real.Zip(signal.Imag, (double r, double i) => new Complex(r, i)).ToArray(), kernel.Real.Zip(kernel.Imag, (double r, double i) => new Complex(r, i)).ToArray()); Complex[] source = array[0]; Complex[] source2 = array[1]; if (source2.All((Complex d) => Math.Abs(d.Real) < 1E-10) && source2.All((Complex d) => Math.Abs(d.Imaginary) < 1E-10)) { return new ComplexDiscreteSignal(signal.SamplingRate, source.Select((Complex q) => q.Real), source.Select((Complex q) => q.Imaginary)); } int size = signal.Length - kernel.Length + 1; if (fftSize == 0) { fftSize = MathUtils.NextPowerOfTwo(signal.Length); } Fft64 fft = new Fft64(fftSize); signal = signal.ZeroPadded(fftSize); kernel = kernel.ZeroPadded(fftSize); fft.Direct(signal.Real, signal.Imag); fft.Direct(kernel.Real, kernel.Imag); for (int num = 0; num < fftSize; num++) { signal.Real[num] += 1E-10; signal.Imag[num] += 1E-10; kernel.Real[num] += 1E-10; kernel.Imag[num] += 1E-10; } ComplexDiscreteSignal complexDiscreteSignal = signal.Divide(kernel); fft.Inverse(complexDiscreteSignal.Real, complexDiscreteSignal.Imag); return new ComplexDiscreteSignal(signal.SamplingRate, complexDiscreteSignal.Real.FastCopyFragment(size), complexDiscreteSignal.Imag.FastCopyFragment(size)); } } public class Convolver { private int _fftSize; private RealFft _fft; private float[] _real1; private float[] _imag1; private float[] _real2; private float[] _imag2; public Convolver(int fftSize = 0) { if (fftSize > 0) { PrepareMemory(fftSize); } } private void PrepareMemory(int fftSize) { _fftSize = fftSize; _fft = new RealFft(_fftSize); _real1 = new float[_fftSize]; _imag1 = new float[_fftSize]; _real2 = new float[_fftSize]; _imag2 = new float[_fftSize]; } public DiscreteSignal Convolve(DiscreteSignal signal, DiscreteSignal kernel) { int n = signal.Length + kernel.Length - 1; if (_fft == null) { PrepareMemory(MathUtils.NextPowerOfTwo(n)); } float[] array = new float[_fftSize]; Convolve(signal.Samples, kernel.Samples, array); return new DiscreteSignal(signal.SamplingRate, array).First(n); } public void Convolve(float[] input, float[] kernel, float[] output) { Array.Clear(_real1, 0, _fftSize); Array.Clear(_real2, 0, _fftSize); input.FastCopyTo(_real1, input.Length); kernel.FastCopyTo(_real2, kernel.Length); _fft.Direct(_real1, _real1, _imag1); _fft.Direct(_real2, _real2, _imag2); for (int i = 0; i <= _fftSize / 2; i++) { float num = _real1[i] * _real2[i] - _imag1[i] * _imag2[i]; float num2 = _real1[i] * _imag2[i] + _imag1[i] * _real2[i]; _real1[i] = num / (float)_fftSize; _imag1[i] = num2 / (float)_fftSize; } _fft.Inverse(_real1, _imag1, output); } public DiscreteSignal CrossCorrelate(DiscreteSignal signal1, DiscreteSignal signal2) { DiscreteSignal kernel = new DiscreteSignal(signal2.SamplingRate, signal2.Samples.Reverse()); return Convolve(signal1, kernel); } public void CrossCorrelate(float[] input1, float[] input2, float[] output) { int num = input2.Length - 1; for (int i = 0; i < num / 2; i++) { float num2 = input2[i]; input2[i] = input2[num - i]; input2[num - i] = num2; } Convolve(input1, input2, output); } } public class OlaBlockConvolver : IFilter, IOnlineFilter { private readonly float[] _kernel; private readonly int _fftSize; private readonly RealFft _fft; private int _bufferOffset; private int _outputBufferOffset; private readonly float[] _kernelSpectrumRe; private readonly float[] _kernelSpectrumIm; private readonly float[] _blockRe; private readonly float[] _blockIm; private readonly float[] _convRe; private readonly float[] _convIm; private readonly float[] _lastSaved; public int HopSize => _fftSize - _kernel.Length + 1; public OlaBlockConvolver(IEnumerable kernel, int fftSize) { _kernel = kernel.ToArray(); _fftSize = MathUtils.NextPowerOfTwo(fftSize); Guard.AgainstExceedance(_kernel.Length, _fftSize, "Kernel length", "the size of FFT"); _fft = new RealFft(_fftSize); _kernelSpectrumRe = _kernel.PadZeros(_fftSize); _kernelSpectrumIm = new float[_fftSize]; _convRe = new float[_fftSize]; _convIm = new float[_fftSize]; _blockRe = new float[_fftSize]; _blockIm = new float[_fftSize]; _lastSaved = new float[_kernel.Length - 1]; _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); Reset(); } public OlaBlockConvolver(IEnumerable kernel, int fftSize) : this(kernel.ToFloats(), fftSize) { } public static OlaBlockConvolver FromFilter(FirFilter filter, int fftSize) { fftSize = MathUtils.NextPowerOfTwo(fftSize); return new OlaBlockConvolver(filter.Kernel, fftSize); } public void ChangeKernel(float[] kernel) { if (kernel.Length == _kernel.Length) { Array.Clear(_kernelSpectrumRe, 0, _fftSize); kernel.FastCopyTo(_kernel, kernel.Length); kernel.FastCopyTo(_kernelSpectrumRe, kernel.Length); _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); } } public float Process(float sample) { _blockRe[_bufferOffset++] = sample; if (_bufferOffset == HopSize) { ProcessFrame(); } return _convRe[_outputBufferOffset++]; } protected void ProcessFrame() { int num = _kernel.Length; int num2 = _fftSize / 2; Array.Clear(_blockRe, HopSize, num - 1); _fft.Direct(_blockRe, _blockRe, _blockIm); for (int i = 0; i <= num2; i++) { _convRe[i] = (_blockRe[i] * _kernelSpectrumRe[i] - _blockIm[i] * _kernelSpectrumIm[i]) / (float)_fftSize; _convIm[i] = (_blockRe[i] * _kernelSpectrumIm[i] + _blockIm[i] * _kernelSpectrumRe[i]) / (float)_fftSize; } _fft.Inverse(_convRe, _convIm, _convRe); for (int j = 0; j < num - 1; j++) { _convRe[j] += _lastSaved[j]; } _convRe.FastCopyTo(_lastSaved, num - 1, HopSize); _outputBufferOffset = 0; _bufferOffset = 0; } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { int num = Math.Min(HopSize - 1, signal.Length); int i = 0; int num2 = 0; for (; i < num; i++) { Process(signal[i]); } float[] array = new float[signal.Length + _kernel.Length - 1]; while (i < signal.Length) { array[num2] = Process(signal[i]); i++; num2++; } int num3 = num + _kernel.Length - 1; i = 0; while (i < num3) { array[num2] = Process(0f); i++; num2++; } return new DiscreteSignal(signal.SamplingRate, array); } public void Reset() { _bufferOffset = 0; _outputBufferOffset = 0; Array.Clear(_lastSaved, 0, _lastSaved.Length); Array.Clear(_blockRe, 0, _blockRe.Length); Array.Clear(_blockIm, 0, _blockIm.Length); Array.Clear(_convRe, 0, _convRe.Length); Array.Clear(_convIm, 0, _convIm.Length); } } public class OlaBlockConvolver64 : IFilter64, IOnlineFilter64 { private readonly double[] _kernel; private readonly int _fftSize; private readonly RealFft64 _fft; private int _bufferOffset; private int _outputBufferOffset; private readonly double[] _kernelSpectrumRe; private readonly double[] _kernelSpectrumIm; private readonly double[] _blockRe; private readonly double[] _blockIm; private readonly double[] _convRe; private readonly double[] _convIm; private readonly double[] _lastSaved; public int HopSize => _fftSize - _kernel.Length + 1; public OlaBlockConvolver64(IEnumerable kernel, int fftSize) { _kernel = kernel.ToArray(); _fftSize = MathUtils.NextPowerOfTwo(fftSize); Guard.AgainstExceedance(_kernel.Length, _fftSize, "Kernel length", "the size of FFT"); _fft = new RealFft64(_fftSize); _kernelSpectrumRe = _kernel.PadZeros(_fftSize); _kernelSpectrumIm = new double[_fftSize]; _convRe = new double[_fftSize]; _convIm = new double[_fftSize]; _blockRe = new double[_fftSize]; _blockIm = new double[_fftSize]; _lastSaved = new double[_kernel.Length - 1]; _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); Reset(); } public static OlaBlockConvolver64 FromFilter(FirFilter64 filter, int fftSize) { fftSize = MathUtils.NextPowerOfTwo(fftSize); return new OlaBlockConvolver64(filter.Kernel, fftSize); } public void ChangeKernel(double[] kernel) { if (kernel.Length == _kernel.Length) { Array.Clear(_kernelSpectrumRe, 0, _fftSize); kernel.FastCopyTo(_kernel, kernel.Length); kernel.FastCopyTo(_kernelSpectrumRe, kernel.Length); _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); } } public double Process(double sample) { _blockRe[_bufferOffset++] = sample; if (_bufferOffset == HopSize) { ProcessFrame(); } return _convRe[_outputBufferOffset++]; } protected void ProcessFrame() { int num = _kernel.Length; int num2 = _fftSize / 2; Array.Clear(_blockRe, HopSize, num - 1); _fft.Direct(_blockRe, _blockRe, _blockIm); for (int i = 0; i <= num2; i++) { _convRe[i] = (_blockRe[i] * _kernelSpectrumRe[i] - _blockIm[i] * _kernelSpectrumIm[i]) / (double)_fftSize; _convIm[i] = (_blockRe[i] * _kernelSpectrumIm[i] + _blockIm[i] * _kernelSpectrumRe[i]) / (double)_fftSize; } _fft.Inverse(_convRe, _convIm, _convRe); for (int j = 0; j < num - 1; j++) { _convRe[j] += _lastSaved[j]; } _convRe.FastCopyTo(_lastSaved, num - 1, HopSize); _outputBufferOffset = 0; _bufferOffset = 0; } public double[] ApplyTo(double[] signal, FilteringMethod method = FilteringMethod.Auto) { int num = Math.Min(HopSize - 1, signal.Length); int i = 0; int num2 = 0; for (; i < num; i++) { Process(signal[i]); } double[] array = new double[signal.Length + _kernel.Length - 1]; while (i < signal.Length) { array[num2] = Process(signal[i]); i++; num2++; } int num3 = num + _kernel.Length - 1; i = 0; while (i < num3) { array[num2] = Process(0.0); i++; num2++; } return array; } public void Reset() { _bufferOffset = 0; _outputBufferOffset = 0; Array.Clear(_lastSaved, 0, _lastSaved.Length); Array.Clear(_blockRe, 0, _blockRe.Length); Array.Clear(_blockIm, 0, _blockIm.Length); Array.Clear(_convRe, 0, _convRe.Length); Array.Clear(_convIm, 0, _convIm.Length); } } public class OlsBlockConvolver : IFilter, IOnlineFilter { private readonly float[] _kernel; private readonly int _fftSize; private readonly RealFft _fft; private int _bufferOffset; private int _outputBufferOffset; private readonly float[] _kernelSpectrumRe; private readonly float[] _kernelSpectrumIm; private readonly float[] _blockRe; private readonly float[] _blockIm; private readonly float[] _convRe; private readonly float[] _convIm; private readonly float[] _lastSaved; public int HopSize => _fftSize - _kernel.Length + 1; public OlsBlockConvolver(IEnumerable kernel, int fftSize) { _kernel = kernel.ToArray(); _fftSize = MathUtils.NextPowerOfTwo(fftSize); Guard.AgainstExceedance(_kernel.Length, _fftSize, "Kernel length", "the size of FFT"); _fft = new RealFft(_fftSize); _kernelSpectrumRe = _kernel.PadZeros(_fftSize); _kernelSpectrumIm = new float[_fftSize]; _convRe = new float[_fftSize]; _convIm = new float[_fftSize]; _blockRe = new float[_fftSize]; _blockIm = new float[_fftSize]; _lastSaved = new float[_kernel.Length - 1]; _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); Reset(); } public OlsBlockConvolver(IEnumerable kernel, int fftSize) : this(kernel.ToFloats(), fftSize) { } public static OlsBlockConvolver FromFilter(FirFilter filter, int fftSize) { fftSize = MathUtils.NextPowerOfTwo(fftSize); return new OlsBlockConvolver(filter.Kernel, fftSize); } public void ChangeKernel(float[] kernel) { if (kernel.Length == _kernel.Length) { Array.Clear(_kernelSpectrumRe, 0, _fftSize); kernel.FastCopyTo(_kernel, kernel.Length); kernel.FastCopyTo(_kernelSpectrumRe, kernel.Length); _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); } } public float Process(float sample) { int num = _bufferOffset + _kernel.Length - 1; _blockRe[num] = sample; if (++_bufferOffset == HopSize) { ProcessFrame(); } return _convRe[_outputBufferOffset++]; } protected void ProcessFrame() { int num = _kernel.Length; int num2 = _fftSize / 2; _lastSaved.FastCopyTo(_blockRe, num - 1); _blockRe.FastCopyTo(_lastSaved, num - 1, HopSize); _fft.Direct(_blockRe, _blockRe, _blockIm); for (int i = 0; i <= num2; i++) { _convRe[i] = (_blockRe[i] * _kernelSpectrumRe[i] - _blockIm[i] * _kernelSpectrumIm[i]) / (float)_fftSize; _convIm[i] = (_blockRe[i] * _kernelSpectrumIm[i] + _blockIm[i] * _kernelSpectrumRe[i]) / (float)_fftSize; } _fft.Inverse(_convRe, _convIm, _convRe); _outputBufferOffset = num - 1; _bufferOffset = 0; } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { int num = Math.Min(HopSize - 1, signal.Length); int i = 0; int num2 = 0; for (; i < num; i++) { Process(signal[i]); } float[] array = new float[signal.Length + _kernel.Length - 1]; while (i < signal.Length) { array[num2] = Process(signal[i]); i++; num2++; } int num3 = num + _kernel.Length - 1; i = 0; while (i < num3) { array[num2] = Process(0f); i++; num2++; } return new DiscreteSignal(signal.SamplingRate, array); } public void Reset() { _bufferOffset = _kernel.Length - 1; _outputBufferOffset = 0; Array.Clear(_lastSaved, 0, _lastSaved.Length); Array.Clear(_blockRe, 0, _blockRe.Length); Array.Clear(_blockIm, 0, _blockIm.Length); Array.Clear(_convRe, 0, _convRe.Length); Array.Clear(_convIm, 0, _convIm.Length); } } public class OlsBlockConvolver64 : IFilter64, IOnlineFilter64 { private readonly double[] _kernel; private readonly int _fftSize; private readonly RealFft64 _fft; private int _bufferOffset; private int _outputBufferOffset; private readonly double[] _kernelSpectrumRe; private readonly double[] _kernelSpectrumIm; private readonly double[] _blockRe; private readonly double[] _blockIm; private readonly double[] _convRe; private readonly double[] _convIm; private readonly double[] _lastSaved; public int HopSize => _fftSize - _kernel.Length + 1; public OlsBlockConvolver64(IEnumerable kernel, int fftSize) { _kernel = kernel.ToArray(); _fftSize = MathUtils.NextPowerOfTwo(fftSize); Guard.AgainstExceedance(_kernel.Length, _fftSize, "Kernel length", "the size of FFT"); _fft = new RealFft64(_fftSize); _kernelSpectrumRe = _kernel.PadZeros(_fftSize); _kernelSpectrumIm = new double[_fftSize]; _convRe = new double[_fftSize]; _convIm = new double[_fftSize]; _blockRe = new double[_fftSize]; _blockIm = new double[_fftSize]; _lastSaved = new double[_kernel.Length - 1]; _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); Reset(); } public static OlsBlockConvolver64 FromFilter(FirFilter64 filter, int fftSize) { fftSize = MathUtils.NextPowerOfTwo(fftSize); return new OlsBlockConvolver64(filter.Kernel, fftSize); } public void ChangeKernel(double[] kernel) { if (kernel.Length == _kernel.Length) { Array.Clear(_kernelSpectrumRe, 0, _fftSize); kernel.FastCopyTo(_kernel, kernel.Length); kernel.FastCopyTo(_kernelSpectrumRe, kernel.Length); _fft.Direct(_kernelSpectrumRe, _kernelSpectrumRe, _kernelSpectrumIm); } } public double Process(double sample) { int num = _bufferOffset + _kernel.Length - 1; _blockRe[num] = sample; if (++_bufferOffset == HopSize) { ProcessFrame(); } return _convRe[_outputBufferOffset++]; } protected void ProcessFrame() { int num = _kernel.Length; int num2 = _fftSize / 2; _lastSaved.FastCopyTo(_blockRe, num - 1); _blockRe.FastCopyTo(_lastSaved, num - 1, HopSize); _fft.Direct(_blockRe, _blockRe, _blockIm); for (int i = 0; i <= num2; i++) { _convRe[i] = (_blockRe[i] * _kernelSpectrumRe[i] - _blockIm[i] * _kernelSpectrumIm[i]) / (double)_fftSize; _convIm[i] = (_blockRe[i] * _kernelSpectrumIm[i] + _blockIm[i] * _kernelSpectrumRe[i]) / (double)_fftSize; } _fft.Inverse(_convRe, _convIm, _convRe); _outputBufferOffset = num - 1; _bufferOffset = 0; } public double[] ApplyTo(double[] signal, FilteringMethod method = FilteringMethod.Auto) { int num = Math.Min(HopSize - 1, signal.Length); int i = 0; int num2 = 0; for (; i < num; i++) { Process(signal[i]); } double[] array = new double[signal.Length + _kernel.Length - 1]; while (i < signal.Length) { array[num2] = Process(signal[i]); i++; num2++; } int num3 = num + _kernel.Length - 1; i = 0; while (i < num3) { array[num2] = Process(0.0); i++; num2++; } return array; } public void Reset() { _bufferOffset = _kernel.Length - 1; _outputBufferOffset = 0; Array.Clear(_lastSaved, 0, _lastSaved.Length); Array.Clear(_blockRe, 0, _blockRe.Length); Array.Clear(_blockIm, 0, _blockIm.Length); Array.Clear(_convRe, 0, _convRe.Length); Array.Clear(_convIm, 0, _convIm.Length); } } } namespace NWaves.Filters { public class CombFeedbackFilter : IirFilter { private readonly int _delay; public CombFeedbackFilter(int m, double b0 = 1.0, double am = 0.6) : base(new float[1], new float[m + 1]) { _a[0] = 1f; _a[m] = (float)am; _b[0] = (float)b0; _delay = m; } public override float Process(float sample) { float num = _b[0]; float num2 = _a[_delay]; float num3 = num * sample - num2 * _delayLineA[_delayLineOffsetA]; _delayLineA[_delayLineOffsetA] = num3; if (--_delayLineOffsetA < 1) { _delayLineOffsetA = _denominatorSize - 1; } return num3; } public override DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { if (method != FilteringMethod.Auto) { return base.ApplyTo(signal, method); } float[] samples = signal.Samples; float[] array = new float[samples.Length]; float num = _b[0]; float num2 = _a[_delay]; for (int i = 0; i < _delay; i++) { array[i] = num * samples[i]; } int num3 = _delay; int num4 = 0; while (num3 < signal.Length) { array[num3] = num * samples[num3] - num2 * array[num4]; num3++; num4++; } return new DiscreteSignal(signal.SamplingRate, array); } public void Change(double b0, double am) { _b[0] = (float)b0; _a[_delay] = (float)am; } } public class CombFeedforwardFilter : FirFilter { private readonly int _delay; public CombFeedforwardFilter(int m, double b0 = 1.0, double bm = 0.5, bool normalize = true) : base(MakeKernel(m, b0, bm, normalize)) { _delay = m; } private static float[] MakeKernel(int m, double b0, double bm, bool normalize) { float[] array = new float[m + 1]; array[0] = (float)b0; array[m] = (float)bm; if (normalize) { float num = (float)(b0 + bm); array[0] /= num; array[m] /= num; } return array; } public override float Process(float sample) { float num = _b[0]; float num2 = _b[_delay]; float result = num * sample + num2 * _delayLine[_delayLineOffset]; _delayLine[_delayLineOffset] = sample; if (--_delayLineOffset < 1) { _delayLineOffset = _kernelSize - 1; } return result; } public override DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { if (method != FilteringMethod.Auto) { return base.ApplyTo(signal, method); } float[] samples = signal.Samples; float[] array = new float[samples.Length + _kernelSize - 1]; float num = _b[0]; float num2 = _b[_delay]; int i = 0; int num3 = 0; for (; i < _delay; i++) { array[i] = num * samples[i]; } while (i < signal.Length) { array[i] = num * samples[i] + num2 * samples[num3]; i++; num3++; } while (i < array.Length) { array[i] = num2 * samples[num3]; i++; num3++; } return new DiscreteSignal(signal.SamplingRate, array); } public void Change(double b0, double bm) { _b[0] = (float)b0; _b[_delay] = (float)bm; } } public class DcRemovalFilter : IirFilter { private readonly float _r; private float _in1; private float _out1; public DcRemovalFilter(double r = 0.995) : base(new double[2] { 1.0, -1.0 }, new double[2] { 1.0, 0.0 - r }) { _r = (float)r; } public override float Process(float sample) { float num = sample - _in1 + _r * _out1; _in1 = sample; _out1 = num; return num; } public override void Reset() { _in1 = (_out1 = 0f); } } public class DeEmphasisFilter : OnePoleFilter { public DeEmphasisFilter(double a = 0.97, bool normalize = false) : base(1.0, 0.0 - a) { if (normalize) { _b[0] = (float)(1.0 - a); } } } public class HilbertFilter : FirFilter { public int Size { get; } public HilbertFilter(int size = 128) : base(MakeKernel(size)) { Size = size; } private static IEnumerable MakeKernel(int size) { double[] array = new double[size]; array[0] = 0.0; for (int i = 1; i < size; i++) { array[i] = 2.0 * Math.Pow(Math.Sin(Math.PI * (double)i / 2.0), 2.0) / (Math.PI * (double)i); } return array; } } public class MedianFilter : IFilter, IOnlineFilter { private readonly float[] _delayLine; private readonly List _sortedSamples; private int _n; public int Size { get; } public MedianFilter(int size = 9) { Guard.AgainstEvenNumber(size, "The size of the filter"); Size = size; _sortedSamples = Enumerable.Repeat(0f, Size).ToList(); _delayLine = new float[Size]; } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { float[] samples = signal.Samples; float[] array = new float[samples.Length]; int num = 0; int num2 = 0; for (num = 0; num < Size / 2; num++) { Process(samples[num]); } while (num2 < samples.Length - Size / 2) { array[num2] = Process(samples[num]); num2++; num++; } num = 0; while (num < Size / 2) { array[num2] = Process(0f); num++; num2++; } return new DiscreteSignal(signal.SamplingRate, array); } public float Process(float sample) { if (_n == Size) { _n = 0; } float item = _delayLine[_n]; _delayLine[_n++] = sample; int index = _sortedSamples.BinarySearch(item); _sortedSamples.RemoveAt(index); int num = _sortedSamples.Count - 1; while (num >= 0 && sample < _sortedSamples[num]) { num--; } _sortedSamples.Insert(num + 1, sample); return _sortedSamples[Size / 2]; } public void Reset() { _n = 0; int num = 0; while (num < _sortedSamples.Count) { _sortedSamples[num++] = 0f; } Array.Clear(_delayLine, 0, Size); } } public class MedianFilter2 : IFilter, IOnlineFilter { private int _n; private readonly float[] _buf; private readonly float[] _tmp; public int Size { get; } public MedianFilter2(int size = 9) { Guard.AgainstEvenNumber(size, "The size of the filter"); Size = size; _buf = new float[Size]; _tmp = new float[Size]; _n = Size / 2; } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { float[] samples = signal.Samples; float[] array = new float[samples.Length]; int num = 0; int num2 = 0; for (num = 0; num < Size / 2; num++) { Process(samples[num]); } while (num2 < samples.Length - Size / 2) { array[num2] = Process(samples[num]); num2++; num++; } num = 0; while (num < Size / 2) { array[num2] = Process(0f); num++; num2++; } return new DiscreteSignal(signal.SamplingRate, array); } public float Process(float sample) { if (_n == _buf.Length) { _n = 0; } _buf[_n++] = sample; _buf.FastCopyTo(_tmp, _buf.Length); return MathUtils.FindNth(_tmp, Size / 2, 0, Size - 1); } public void Reset() { _n = Size / 2; for (int i = 0; i < _buf.Length; i++) { _buf[i] = 0f; } } } public class MovingAverageFilter : FirFilter { public int Size { get; } public MovingAverageFilter(int size = 9) : base(MakeKernel(size)) { Size = size; } private static IEnumerable MakeKernel(int size) { return Enumerable.Repeat(1f / (float)size, size); } } public class MovingAverageRecursiveFilter : IirFilter { private float _out1; public int Size { get; } public MovingAverageRecursiveFilter(int size = 9) : base(MakeNumerator(size), new float[2] { 1f, -1f }) { Size = size; } private static float[] MakeNumerator(int size) { float[] array = new float[size + 1]; array[0] = 1f / (float)size; array[size] = 0f - array[0]; return array; } public override DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { if (method != FilteringMethod.Auto) { return base.ApplyTo(signal, method); } float[] samples = signal.Samples; int size = Size; float[] array = new float[samples.Length]; float num = _b[0]; float num2 = _b[Size]; array[0] = samples[0] * num; int num3 = 1; int num4 = 0; while (num3 < size) { array[num3] = samples[num3] * num + array[num4]; num3++; num4++; } int num5 = size; int num6 = size - 1; int num7 = 0; while (num5 < samples.Length) { array[num5] = samples[num7] * num2 + samples[num5] * num + array[num6]; num5++; num6++; num7++; } return new DiscreteSignal(signal.SamplingRate, array); } public override float Process(float sample) { float num = _b[0]; float num2 = _b[Size]; float num3 = num * sample + num2 * _delayLineB[_delayLineOffsetB] + _out1; _delayLineB[_delayLineOffsetB] = sample; _out1 = num3; if (--_delayLineOffsetB < 1) { _delayLineOffsetB = _numeratorSize - 1; } return num3; } public override void Reset() { _out1 = 0f; base.Reset(); } } public class PreEmphasisFilter : FirFilter { private float _prevSample; public PreEmphasisFilter(double a = 0.97, bool normalize = false) : base(new double[2] { 1.0, 0.0 - a }) { if (normalize) { float num = (float)(1.0 + a); _b[0] /= num; _b[1] /= num; } } public override float Process(float sample) { float result = _b[0] * sample + _b[1] * _prevSample; _prevSample = sample; return result; } public override DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { if (method != FilteringMethod.Auto) { return base.ApplyTo(signal, method); } float[] samples = signal.Samples; float[] array = new float[samples.Length + 1]; float num = _b[0]; float num2 = _b[1]; _prevSample = 0f; int i; for (i = 0; i < samples.Length; i++) { float num3 = samples[i]; array[i] = num * num3 + num2 * _prevSample; _prevSample = num3; } array[i] = num2 * _prevSample; return new DiscreteSignal(signal.SamplingRate, array); } public override void Reset() { _prevSample = 0f; } } public class RastaFilter : IirFilter { public RastaFilter(double pole = 0.98) : base(new float[5] { 0.2f, 0.1f, 0f, -0.1f, -0.2f }, new float[2] { 1f, 0f - (float)pole }) { } } public class SavitzkyGolayFilter : FirFilter { public int Size { get; } public SavitzkyGolayFilter(int size, int deriv = 0) : base(MakeKernel(size, deriv)) { Size = size; } private static double[] MakeKernel(int size, int deriv = 0) { Guard.AgainstEvenNumber(size, "The size of the filter"); return deriv switch { 0 => size switch { 5 => new double[5] { -0.08571429, 0.34285714, 0.48571429, 0.34285714, -0.08571429 }, 7 => new double[7] { -0.0952381, 0.14285714, 0.28571429, 0.33333333, 0.28571429, 0.14285714, -0.0952381 }, 9 => new double[9] { -0.09090909, 0.06060606, 0.16883117, 0.23376623, 0.25541126, 0.23376623, 0.16883117, 0.06060606, -0.09090909 }, 11 => new double[11] { -0.08391608, 0.02097902, 0.1025641, 0.16083916, 0.1958042, 0.20745921, 0.1958042, 0.16083916, 0.1025641, 0.02097902, -0.08391608 }, 13 => new double[13] { -0.0769230769, 5.55111512E-17, 0.0629370629, 0.111888112, 0.146853147, 0.167832168, 0.174825175, 0.167832168, 0.146853147, 0.111888112, 0.0629370629, 1.38777878E-17, -0.0769230769 }, 15 => new double[15] { -0.07058824, -0.01176471, 0.03800905, 0.07873303, 0.11040724, 0.13303167, 0.14660633, 0.15113122, 0.14660633, 0.13303167, 0.11040724, 0.07873303, 0.03800905, -0.01176471, -0.07058824 }, 17 => new double[17] { -0.06501548, -0.01857585, 0.02167183, 0.05572755, 0.08359133, 0.10526316, 0.12074303, 0.13003096, 0.13312693, 0.13003096, 0.12074303, 0.10526316, 0.08359133, 0.05572755, 0.02167183, -0.01857585, -0.06501548 }, 19 => new double[19] { -0.06015038, -0.02255639, 0.01061477, 0.03936311, 0.06368863, 0.08359133, 0.09907121, 0.11012826, 0.11676249, 0.11897391, 0.11676249, 0.11012826, 0.09907121, 0.08359133, 0.06368863, 0.03936311, 0.01061477, -0.02255639, -0.06015038 }, 21 => new double[21] { -0.05590062, -0.02484472, 0.00294214, 0.02745995, 0.04870873, 0.06668846, 0.08139915, 0.0928408, 0.1010134, 0.10591697, 0.10755149, 0.10591697, 0.1010134, 0.0928408, 0.08139915, 0.06668846, 0.04870873, 0.02745995, 0.00294214, -0.02484472, -0.05590062 }, 23 => new double[23] { -0.05217391, -0.02608696, -0.00248447, 0.01863354, 0.03726708, 0.05341615, 0.06708075, 0.07826087, 0.08695652, 0.0931677, 0.09689441, 0.09813665, 0.09689441, 0.0931677, 0.08695652, 0.07826087, 0.06708075, 0.05341615, 0.03726708, 0.01863354, -0.00248447, -0.02608696, -0.05217391 }, 25 => new double[25] { -0.04888889, -0.02666667, -0.00637681, 0.01198068, 0.0284058, 0.04289855, 0.05545894, 0.06608696, 0.07478261, 0.08154589, 0.08637681, 0.08927536, 0.09024155, 0.08927536, 0.08637681, 0.08154589, 0.07478261, 0.06608696, 0.05545894, 0.04289855, 0.0284058, 0.01198068, -0.00637681, -0.02666667, -0.04888889 }, 27 => new double[27] { -0.04597701, -0.02681992, -0.0091954, 0.00689655, 0.02145594, 0.03448276, 0.04597701, 0.0559387, 0.06436782, 0.07126437, 0.07662835, 0.08045977, 0.08275862, 0.0835249, 0.08275862, 0.08045977, 0.07662835, 0.07126437, 0.06436782, 0.0559387, 0.04597701, 0.03448276, 0.02145594, 0.00689655, -0.0091954, -0.02681992, -0.04597701 }, 29 => new double[29] { -0.04338154, -0.02669633, -0.01124706, 0.00296626, 0.01594364, 0.02768508, 0.03819058, 0.04746014, 0.05549376, 0.06229143, 0.06785317, 0.07217896, 0.07526882, 0.07712273, 0.0777407, 0.07712273, 0.07526882, 0.07217896, 0.06785317, 0.06229143, 0.05549376, 0.04746014, 0.03819058, 0.02768508, 0.01594364, 0.00296626, -0.01124706, -0.02669633, -0.04338154 }, 31 => new double[31] { -0.04105572, -0.02639296, -0.01274143, -0.00010112, 0.01152796, 0.02214582, 0.03175245, 0.04034786, 0.04793205, 0.05450501, 0.06006674, 0.06461725, 0.06815654, 0.0706846, 0.07220144, 0.07270705, 0.07220144, 0.0706846, 0.06815654, 0.06461725, 0.06006674, 0.05450501, 0.04793205, 0.04034786, 0.03175245, 0.02214582, 0.01152796, -0.00010112, -0.01274143, -0.02639296, -0.04105572 }, _ => throw new ArgumentException("Size of the filter must be in range [5, 31]!"), }, 1 => size switch { 5 => new double[5] { 0.2, 0.1, 0.0, -0.1, -0.2 }, 7 => new double[7] { 0.107142857, 0.0714285714, 0.0357142857, 0.0, -0.0357142857, -0.0714285714, -0.107142857 }, 9 => new double[9] { 0.0666666667, 0.05, 0.0333333333, 0.0166666667, 0.0, -0.0166666667, -0.0333333333, -0.05, -0.0666666667 }, 11 => new double[11] { 0.0454545455, 0.0363636364, 0.0272727273, 0.0181818182, 0.00909090909, 0.0, -0.00909090909, -0.0181818182, -0.0272727273, -0.0363636364, -0.0454545455 }, 13 => new double[13] { 0.032967033, 0.0274725275, 0.021978022, 0.0164835165, 0.010989011, 0.00549450549, 0.0, -0.00549450549, -0.010989011, -0.0164835165, -0.021978022, -0.0274725275, -0.032967033 }, 15 => new double[15] { 0.025, 0.0214285714, 0.0178571429, 0.0142857143, 0.0107142857, 0.00714285714, 0.00357142857, 0.0, -0.00357142857, -0.00714285714, -0.0107142857, -0.0142857143, -0.0178571429, -0.0214285714, -0.025 }, 17 => new double[17] { 0.0196078431, 0.0171568627, 0.0147058824, 0.012254902, 0.00980392157, 0.00735294118, 0.00490196078, 0.00245098039, 0.0, -0.00245098039, -0.00490196078, -0.00735294118, -0.00980392157, -0.012254902, -0.0147058824, -0.0171568627, -0.0196078431 }, 19 => new double[19] { 0.0157894737, 0.0140350877, 0.0122807018, 0.0105263158, 0.00877192982, 0.00701754386, 0.00526315789, 0.00350877193, 0.00175438596, 0.0, -0.00175438596, -0.00350877193, -0.00526315789, -0.00701754386, -0.00877192982, -0.0105263158, -0.0122807018, -0.0140350877, -0.0157894737 }, 21 => new double[21] { 0.012987013, 0.0116883117, 0.0103896104, 0.00909090909, 0.00779220779, 0.00649350649, 0.00519480519, 0.0038961039, 0.0025974026, 0.0012987013, 0.0, -0.0012987013, -0.0025974026, -0.0038961039, -0.00519480519, -0.00649350649, -0.00779220779, -0.00909090909, -0.0103896104, -0.0116883117, -0.012987013 }, 23 => new double[23] { 0.01086957, 0.00988142, 0.00889328, 0.00790514, 0.006917, 0.00592885, 0.00494071, 0.00395257, 0.00296443, 0.00197628, 0.00098814, 0.0, -0.00098814, -0.00197628, -0.00296443, -0.00395257, -0.00494071, -0.00592885, -0.006917, -0.00790514, -0.00889328, -0.00988142, -0.01086957 }, 25 => new double[25] { 0.00923076923, 0.00846153846, 0.00769230769, 0.00692307692, 0.00615384615, 0.00538461538, 0.00461538462, 0.00384615385, 0.00307692308, 0.00230769231, 0.00153846154, 0.000769230769, 0.0, -0.000769230769, -0.00153846154, -0.00230769231, -0.00307692308, -0.00384615385, -0.00461538462, -0.00538461538, -0.00615384615, -0.00692307692, -0.00769230769, -0.00846153846, -0.00923076923 }, 27 => new double[27] { 0.00793650794, 0.00732600733, 0.00671550672, 0.00610500611, 0.00549450549, 0.00488400488, 0.00427350427, 0.00366300366, 0.00305250305, 0.00244200244, 0.00183150183, 0.00122100122, 0.000610500611, 0.0, -0.000610500611, -0.00122100122, -0.00183150183, -0.00244200244, -0.00305250305, -0.00366300366, -0.00427350427, -0.00488400488, -0.00549450549, -0.00610500611, -0.00671550672, -0.00732600733, -0.00793650794 }, 29 => new double[29] { 0.00689655172, 0.00640394089, 0.00591133005, 0.00541871921, 0.00492610837, 0.00443349754, 0.0039408867, 0.00344827586, 0.00295566502, 0.00246305419, 0.00197044335, 0.00147783251, 0.000985221675, 0.000492610837, 0.0, -0.000492610837, -0.000985221675, -0.00147783251, -0.00197044335, -0.00246305419, -0.00295566502, -0.00344827586, -0.0039408867, -0.00443349754, -0.00492610837, -0.00541871921, -0.00591133005, -0.00640394089, -0.00689655172 }, 31 => new double[31] { 0.00604839, 0.00564516, 0.00524194, 0.00483871, 0.00443548, 0.00403226, 0.00362903, 0.00322581, 0.00282258, 0.00241935, 0.00201613, 0.0016129, 0.00120968, 0.00080645, 0.00040323, 0.0, -0.00040323, -0.00080645, -0.00120968, -0.0016129, -0.00201613, -0.00241935, -0.00282258, -0.00322581, -0.00362903, -0.00403226, -0.00443548, -0.00483871, -0.00524194, -0.00564516, -0.00604839 }, _ => throw new ArgumentException("Size of the filter must be in range [5, 31]!"), }, 2 => size switch { 5 => new double[5] { 0.28571429, -0.14285714, -0.28571429, -0.14285714, 0.28571429 }, 7 => new double[7] { 0.119047619, 0.0, -0.0714285714, -0.0952380952, -0.0714285714, 1.38777878E-17, 0.119047619 }, 9 => new double[9] { 0.06060606, 0.01515152, -0.01731602, -0.03679654, -0.04329004, -0.03679654, -0.01731602, 0.01515152, 0.06060606 }, 11 => new double[11] { 0.03496503, 0.01398601, -0.002331, -0.01398601, -0.02097902, -0.02331002, -0.02097902, -0.01398601, -0.002331, 0.01398601, 0.03496503 }, 13 => new double[13] { 0.02197802, 0.01098901, 0.001998, -0.004995, -0.00999001, -0.01298701, -0.01398601, -0.01298701, -0.00999001, -0.004995, 0.001998, 0.01098901, 0.02197802 }, 15 => new double[15] { 0.01470588, 0.00840336, 0.00307046, -0.00129282, -0.00468649, -0.00711054, -0.00856496, -0.00904977, -0.00856496, -0.00711054, -0.00468649, -0.00129282, 0.00307046, 0.00840336, 0.01470588 }, 17 => new double[17] { 0.01031992, 0.00644995, 0.00309598, 0.000258, -0.00206398, -0.00386997, -0.00515996, -0.00593395, -0.00619195, -0.00593395, -0.00515996, -0.00386997, -0.00206398, 0.000258, 0.00309598, 0.00644995, 0.01031992 }, 19 => new double[19] { 0.0075188, 0.00501253, 0.00280112, 0.00088456, -0.00073714, -0.00206398, -0.00309598, -0.00383311, -0.00427539, -0.00442282, -0.00427539, -0.00383311, -0.00309598, -0.00206398, -0.00073714, 0.00088456, 0.00280112, 0.00501253, 0.0075188 }, 21 => new double[21] { 0.00564652739, 0.00395256917, 0.00243692235, 0.00109958691, -5.94371304E-05, -0.00104014978, -0.00184255104, -0.00246664091, -0.00291241939, -0.00317988648, -0.00326904217, -0.00317988648, -0.00291241939, -0.00246664091, -0.00184255104, -0.00104014978, -5.94371304E-05, 0.00109958691, 0.00243692235, 0.00395256917, 0.00564652739 }, 23 => new double[23] { 0.00434783, 0.00316206, 0.00208922, 0.00112931, 0.00028233, -0.00045172, -0.00107284, -0.00158103, -0.00197628, -0.00225861, -0.00242801, -0.00248447, -0.00242801, -0.00225861, -0.00197628, -0.00158103, -0.00107284, -0.00045172, 0.00028233, 0.00112931, 0.00208922, 0.00316206, 0.00434783 }, 25 => new double[25] { 0.0034188, 0.0025641, 0.00178372, 0.00107767, 0.00044593, -0.00011148, -0.00059457, -0.00100334, -0.00133779, -0.00159792, -0.00178372, -0.00189521, -0.00193237, -0.00189521, -0.00178372, -0.00159792, -0.00133779, -0.00100334, -0.00059457, -0.00011148, 0.00044593, 0.00107767, 0.00178372, 0.0025641, 0.0034188 }, 27 => new double[27] { 0.00273672687, 0.00210517452, 0.00152414635, 0.000993642373, 0.000513662583, 8.42069808E-05, -0.000294724433, -0.000623131658, -0.000901014694, -0.00112837354, -0.0013052082, -0.00143151867, -0.00150730496, -0.00153256705, -0.00150730496, -0.00143151867, -0.0013052082, -0.00112837354, -0.000901014694, -0.000623131658, -0.000294724433, 8.42069808E-05, 0.000513662583, 0.000993642373, 0.00152414635, 0.00210517452, 0.00273672687 }, 29 => new double[29] { 0.00222469, 0.00174797, 0.00130657, 0.00090047, 0.00052969, 0.00019422, -0.00010594, -0.00037078, -0.00060031, -0.00079453, -0.00095344, -0.00107703, -0.00116532, -0.00121828, -0.00123594, -0.00121828, -0.00116532, -0.00107703, -0.00095344, -0.00079453, -0.00060031, -0.00037078, -0.00010594, 0.00019422, 0.00052969, 0.00090047, 0.00130657, 0.00174797, 0.00222469 }, 31 => new double[31] { 0.00183284457, 0.00146627566, 0.00112498736, 0.000808979674, 0.000518252604, 0.000252806148, 1.26403074E-05, -0.000202244919, -0.00039184953, -0.000556173526, -0.000695216908, -0.000808979674, -0.000897461826, -0.000960663363, -0.000998584286, -0.00101122459, -0.000998584286, -0.000960663363, -0.000897461826, -0.000808979674, -0.000695216908, -0.000556173526, -0.00039184953, -0.000202244919, 1.26403074E-05, 0.000252806148, 0.000518252604, 0.000808979674, 0.00112498736, 0.00146627566, 0.00183284457 }, _ => throw new ArgumentException("Size of the filter must be in range [5, 31]!"), }, _ => throw new ArgumentException("Parameter deriv must be 0, 1 or 2!"), }; } } public class ThiranFilter : IirFilter { public ThiranFilter(int order, double delta) : base(MakeTf(order, delta)) { } private static TransferFunction MakeTf(int order, double delta) { IEnumerable source = from i in Enumerable.Range(0, order + 1) select ThiranCoefficient(i, order, delta); return new TransferFunction(source.Reverse().ToArray(), source.ToArray()); } private static double ThiranCoefficient(int k, int n, double delta) { double num = 1.0; for (int i = 0; i <= n; i++) { num *= (delta - (double)n + (double)i) / (delta - (double)n + (double)k + (double)i); } return num * (Math.Pow(-1.0, k) * MathUtils.BinomialCoefficient(k, n)); } } public class WienerFilter : IFilter, IOnlineFilter { private readonly int _size; private readonly double _noise; private int _n; private readonly float[] _buf; public WienerFilter(int size = 3, double noise = 0.0) { Guard.AgainstEvenNumber(size, "The size of the filter"); _size = size; _noise = noise; _buf = new float[_size]; _n = _size / 2; } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { float[] array = new float[signal.Length]; int num = 0; int num2 = 0; for (num = 0; num < _size / 2; num++) { Process(signal[num]); } while (num2 < signal.Length - _size / 2) { array[num2] = Process(signal[num]); num2++; num++; } num = 0; while (num < _size / 2) { array[num2] = Process(0f); num++; num2++; } return new DiscreteSignal(signal.SamplingRate, array); } public float Process(float sample) { if (_n == _buf.Length) { _n = 0; } _buf[_n] = sample; float num = 0f; for (int i = 0; i < _size; i++) { num += _buf[i]; } num /= (float)_size; float num2 = 0f; for (int j = 0; j < _size; j++) { num2 += _buf[j] * _buf[j]; } num2 /= (float)_size; num2 -= num * num; float num3 = ((_n > 0) ? _buf[_n - 1] : _buf[_size - 1]); _n++; if (!((double)num2 < _noise)) { return (float)((double)num + (double)(num3 - num) * (1.0 - _noise / (double)num2)); } return num; } public void Reset() { _n = _size / 2; for (int i = 0; i < _buf.Length; i++) { _buf[i] = 0f; } } } } namespace NWaves.Filters.Polyphase { public class PolyphaseSystem : IFilter, IOnlineFilter { public FirFilter[] Filters { get; private set; } public FirFilter[] MultirateFilters { get; private set; } public PolyphaseSystem(double[] kernel, int n, int type = 1) { Filters = new FirFilter[n]; MultirateFilters = new FirFilter[n]; int num = (kernel.Length + 1) / n; for (int i = 0; i < Filters.Length; i++) { double[] array = new double[kernel.Length]; double[] array2 = new double[num]; for (int j = 0; j < num; j++) { int num2 = i + n * j; if (num2 < kernel.Length) { array[num2] = kernel[num2]; array2[j] = kernel[num2]; } } Filters[i] = new FirFilter(array); MultirateFilters[i] = new FirFilter(array2); } if (type == 2) { for (int k = 0; k < Filters.Length / 2; k++) { FirFilter firFilter = Filters[k]; Filters[k] = Filters[n - 1 - k]; Filters[n - 1 - k] = firFilter; firFilter = MultirateFilters[k]; MultirateFilters[k] = MultirateFilters[n - 1 - k]; MultirateFilters[n - 1 - k] = firFilter; } } } public DiscreteSignal Decimate(DiscreteSignal signal) { int samplingRate = signal.SamplingRate / MultirateFilters.Length; int length = signal.Length / MultirateFilters.Length; DiscreteSignal discreteSignal = new DiscreteSignal(samplingRate, length); float num = 0f; for (int num2 = MultirateFilters.Length - 1; num2 >= 1; num2--) { num += MultirateFilters[num2].Process(0f); } num += MultirateFilters[0].Process(signal[0]); discreteSignal[0] = num; int num3 = 1; for (int i = 1; i < discreteSignal.Length; i++) { num = 0f; for (int num4 = MultirateFilters.Length - 1; num4 >= 0; num4--) { num += MultirateFilters[num4].Process(signal[num3++]); } discreteSignal[i] = num; } return discreteSignal; } public DiscreteSignal Interpolate(DiscreteSignal signal) { int num = MultirateFilters.Length; int samplingRate = signal.SamplingRate * num; int length = signal.Length * num; DiscreteSignal discreteSignal = new DiscreteSignal(samplingRate, length); int num2 = 0; for (int i = 0; i < signal.Length; i++) { for (int num3 = MultirateFilters.Length - 1; num3 >= 0; num3--) { discreteSignal[num2++] = (float)num * MultirateFilters[num3].Process(signal[i]); } } return discreteSignal; } public float Process(float sample) { float num = 0f; FirFilter[] filters = Filters; foreach (FirFilter firFilter in filters) { num += firFilter.Process(sample); } return num; } public void Reset() { FirFilter[] filters = Filters; for (int i = 0; i < filters.Length; i++) { filters[i].Reset(); } filters = MultirateFilters; for (int i = 0; i < filters.Length; i++) { filters[i].Reset(); } } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } } namespace NWaves.Filters.OnePole { public class HighPassFilter : OnePoleFilter { public double Frequency { get; protected set; } public HighPassFilter(double frequency) { SetCoefficients(frequency); } private void SetCoefficients(double frequency) { Frequency = frequency; _a[0] = 1f; _a[1] = (float)Math.Exp(Math.PI * -2.0 * (0.5 - frequency)); _b[0] = 1f - _a[1]; } public void Change(double frequency) { SetCoefficients(frequency); } } public class LowPassFilter : OnePoleFilter { public double Frequency { get; protected set; } public LowPassFilter(double frequency) { SetCoefficients(frequency); } private void SetCoefficients(double frequency) { Frequency = frequency; _a[0] = 1f; _a[1] = (float)(0.0 - Math.Exp(Math.PI * -2.0 * frequency)); _b[0] = 1f + _a[1]; } public void Change(double frequency) { SetCoefficients(frequency); } } public class OnePoleFilter : IirFilter { private float _prev; protected OnePoleFilter() : base(new double[1] { 1.0 }, new double[2] { 1.0, 0.0 }) { } public OnePoleFilter(double b, double a) : base(new double[1] { b }, new double[2] { 1.0, a }) { } public override float Process(float sample) { return _prev = _b[0] * sample - _a[1] * _prev; } public override void Reset() { _prev = 0f; } } } namespace NWaves.Filters.Fda { public static class DesignFilter { private static readonly Func Any = (Complex c) => true; private static readonly Func IsReal = (Complex c) => Math.Abs(c.Imaginary) < 1E-10; private static readonly Func IsComplex = (Complex c) => Math.Abs(c.Imaginary) > 1E-10; public static double[] FirWinFdLp(int order, double frequency, double delay, WindowType window = WindowType.Blackman) { Guard.AgainstInvalidRange(frequency, 0.0, 0.5, "Cutoff frequency"); double[] array = new double[order]; int num = (order - 1) / 2; double num2 = Math.PI * 2.0 * frequency; for (int i = 0; i < order; i++) { double num3 = (double)i - delay - (double)num; array[i] = ((num3 == 0.0) ? (2.0 * frequency) : (Math.Sin(num2 * num3) / (Math.PI * num3))); } array.ApplyWindow(window); NormalizeKernel(array); return array; } public static double[] FirWinFdHp(int order, double frequency, double delay, WindowType window = WindowType.Blackman) { Guard.AgainstInvalidRange(frequency, 0.0, 0.5, "Cutoff frequency"); double[] array = new double[order]; int num = (order - 1) / 2; double num2 = Math.PI * 2.0 * (0.5 - frequency); int num3 = -1; for (int i = 0; i < order; i++) { double num4 = (double)i - delay - (double)num; array[i] = ((num4 == 0.0) ? (2.0 * (0.5 - frequency)) : ((double)num3 * Math.Sin(num2 * num4) / (Math.PI * num4))); num3 = -num3; } array.ApplyWindow(window); NormalizeKernel(array, Math.PI); return array; } public static double[] FirWinFdBp(int order, double frequencyLow, double frequencyHigh, double delay, WindowType window = WindowType.Blackman) { Guard.AgainstInvalidRange(frequencyLow, 0.0, 0.5, "low cutoff frequency"); Guard.AgainstInvalidRange(frequencyHigh, 0.0, 0.5, "high cutoff frequency"); Guard.AgainstInvalidRange(frequencyLow, frequencyHigh, "low cutoff frequency", "high cutoff frequency"); double[] array = new double[order]; int num = (order - 1) / 2; double num2 = Math.PI * 2.0 * frequencyLow; double num3 = Math.PI * 2.0 * frequencyHigh; for (int i = 0; i < order; i++) { double num4 = (double)i - delay - (double)num; array[i] = ((num4 == 0.0) ? (2.0 * (frequencyHigh - frequencyLow)) : ((Math.Sin(num3 * num4) - Math.Sin(num2 * num4)) / (Math.PI * num4))); } array.ApplyWindow(window); NormalizeKernel(array, Math.PI * 2.0 * (frequencyLow + frequencyHigh) / 2.0); return array; } public static double[] FirWinFdBs(int order, double frequencyLow, double frequencyHigh, double delay, WindowType window = WindowType.Blackman) { Guard.AgainstInvalidRange(frequencyLow, 0.0, 0.5, "low cutoff frequency"); Guard.AgainstInvalidRange(frequencyHigh, 0.0, 0.5, "high cutoff frequency"); Guard.AgainstInvalidRange(frequencyLow, frequencyHigh, "low cutoff frequency", "high cutoff frequency"); double[] array = new double[order]; int num = (order - 1) / 2; double num2 = Math.PI * 2.0 * frequencyLow; double num3 = Math.PI * 2.0 * (0.5 - frequencyHigh); int num4 = 1; for (int i = 0; i < order; i++) { double num5 = (double)i - delay - (double)num; array[i] = ((num5 == 0.0) ? (2.0 * (0.5 - frequencyHigh + frequencyLow)) : ((Math.Sin(num2 * num5) + (double)num4 * Math.Sin(num3 * num5)) / (Math.PI * num5))); num4 = -num4; } array.ApplyWindow(window); NormalizeKernel(array); return array; } public static double[] FirWinFdAp(int order, double delay, WindowType window = WindowType.Blackman) { double[] array = new double[order]; int num = (order - 1) / 2; for (int i = 0; i < order; i++) { array[i] = MathUtils.Sinc((double)i - delay - (double)num); } array.ApplyWindow(window); NormalizeKernel(array); return array; } public static void NormalizeKernel(double[] kernel, double frequency = 0.0) { Complex x = Complex.FromPolarCoordinates(1.0, frequency); double num = Complex.Abs((Complex)1 / MathUtils.EvaluatePolynomial(kernel, x)); for (int i = 0; i < kernel.Length; i++) { kernel[i] *= num; } } public static double[] FirWinLp(int order, double frequency, WindowType window = WindowType.Blackman) { return FirWinFdLp(order, frequency, (double)((order + 1) % 2) * 0.5, window); } public static double[] FirWinHp(int order, double frequency, WindowType window = WindowType.Blackman) { return FirWinFdHp(order, frequency, (double)((order + 1) % 2) * 0.5, window); } public static double[] FirWinBp(int order, double frequencyLow, double frequencyHigh, WindowType window = WindowType.Blackman) { return FirWinFdBp(order, frequencyLow, frequencyHigh, (double)((order + 1) % 2) * 0.5, window); } public static double[] FirWinBs(int order, double frequencyLow, double frequencyHigh, WindowType window = WindowType.Blackman) { return FirWinFdBs(order, frequencyLow, frequencyHigh, (double)((order + 1) % 2) * 0.5, window); } public static double[] Fir(int order, double[] frequencies, double[] gain, int fftSize = 0, WindowType window = WindowType.Hamming) { if (fftSize == 0) { fftSize = 2 * MathUtils.NextPowerOfTwo(order); } int num = fftSize / 2 + 1; if (frequencies == null) { frequencies = (from i in Enumerable.Range(0, num) select (double)i / (double)fftSize).ToArray(); } if (order >= num) { throw new ArgumentException($"Given that filter order is {order} the FFT size must be at least {2 * MathUtils.NextPowerOfTwo(order)}"); } Guard.AgainstInequality(frequencies.Length, gain.Length, "Length of frequencies array", "length of gain array"); Guard.AgainstNotOrdered(frequencies, "Array of frequencies"); double step = 1.0 / (double)fftSize; double[] array = (from f in Enumerable.Range(0, num) select (double)f * step).ToArray(); double[] array2 = new double[array.Length]; double[] array3 = frequencies; int num2 = 0; int num3 = 1; for (int num4 = 0; num4 < array.Length; num4++) { while (array[num4] > array3[num3] && num3 < array3.Length - 1) { num3++; num2++; } array2[num4] = gain[num2] + (gain[num3] - gain[num2]) * (array[num4] - array3[num2]) / (array3[num3] - array3[num2]); } Complex[] array4 = new Complex[fftSize]; for (int num5 = 0; num5 < array2.Length; num5++) { array4[num5] = (Complex)array2[num5] * Complex.Exp(new Complex(0.0, (double)(-(order - 1)) / 2.0 * 2.0 * Math.PI * (double)num5 / (double)fftSize)); } double[] array5 = array4.Select((Complex c) => c.Real).ToArray(); double[] im = array4.Select((Complex c) => c.Imaginary).ToArray(); new RealFft64(fftSize).Inverse(array5, im, array5); double[] array6 = (from s in array5.Take(order) select s / (double)fftSize).ToArray(); array6.ApplyWindow(window); return array6; } public static double[] FirEquirippleLp(int order, double fp, double fa, double wp, double wa) { return new Remez(order, new double[4] { 0.0, fp, fa, 0.5 }, new double[2] { 1.0, 0.0 }, new double[2] { wp, wa }).Design(); } public static double[] FirEquirippleHp(int order, double fa, double fp, double wa, double wp) { return new Remez(order, new double[4] { 0.0, fa, fp, 0.5 }, new double[2] { 0.0, 1.0 }, new double[2] { wa, wp }).Design(); } public static double[] FirEquirippleBp(int order, double fa1, double fp1, double fp2, double fa2, double wa1, double wp, double wa2) { return new Remez(order, new double[6] { 0.0, fa1, fp1, fp2, fa2, 0.5 }, new double[3] { 0.0, 1.0, 0.0 }, new double[3] { wa1, wp, wa2 }).Design(); } public static double[] FirEquirippleBs(int order, double fp1, double fa1, double fa2, double fp2, double wp1, double wa, double wp2) { return new Remez(order, new double[6] { 0.0, fp1, fa1, fa2, fp2, 0.5 }, new double[3] { 1.0, 0.0, 1.0 }, new double[3] { wp1, wa, wp2 }).Design(); } public static double[] FirLpToHp(double[] kernel) { Guard.AgainstEvenNumber(kernel.Length, "The order of the filter"); double[] array = kernel.Select((double k) => 0.0 - k).ToArray(); array[array.Length / 2] += 1.0; return array; } public static double[] FirHpToLp(double[] kernel) { return FirLpToHp(kernel); } public static double[] FirBpToBs(double[] kernel) { return FirLpToHp(kernel); } public static double[] FirBsToBp(double[] kernel) { return FirLpToHp(kernel); } public static TransferFunction IirNotch(double frequency, double q = 20.0) { Guard.AgainstInvalidRange(frequency, 0.0, 0.5, "Center frequency"); double num = 2.0 * frequency * Math.PI; double num2 = num / q; double num3 = 1.0 / Math.Sqrt(2.0); double num4 = Math.Sqrt(1.0 - num3 * num3) / num3 * Math.Tan(num2 / 2.0); double num5 = 1.0 / (1.0 + num4); double[] numerator = new double[3] { num5, -2.0 * Math.Cos(num) * num5, num5 }; double[] denominator = new double[3] { 1.0, -2.0 * Math.Cos(num) * num5, 2.0 * num5 - 1.0 }; return new TransferFunction(numerator, denominator); } public static TransferFunction IirPeak(double frequency, double q = 20.0) { Guard.AgainstInvalidRange(frequency, 0.0, 0.5, "Center frequency"); double num = 2.0 * frequency * Math.PI; double num2 = num / q; double num3 = 1.0 / Math.Sqrt(2.0); double num4 = num3 / Math.Sqrt(1.0 - num3 * num3) * Math.Tan(num2 / 2.0); double num5 = 1.0 / (1.0 + num4); double[] numerator = new double[3] { 1.0 - num5, 0.0, num5 - 1.0 }; double[] denominator = new double[3] { 1.0, -2.0 * Math.Cos(num) * num5, 2.0 * num5 - 1.0 }; return new TransferFunction(numerator, denominator); } public static TransferFunction IirCombNotch(double frequency, double q = 20.0) { Guard.AgainstInvalidRange(frequency, 0.0, 0.5, "Center frequency"); double num = 2.0 * frequency * Math.PI / q; double num2 = 1.0 / Math.Sqrt(2.0); int num3 = (int)(1.0 / frequency); double num4 = Math.Sqrt((1.0 - num2 * num2) / (num2 * num2)) * Math.Tan((double)num3 * num / 4.0); double[] array = new double[num3 + 1]; double[] array2 = new double[num3 + 1]; array[0] = 1.0 / (1.0 + num4); array[^1] = -1.0 / (1.0 + num4); array2[0] = 1.0; array2[^1] = (0.0 - (1.0 - num4)) / (1.0 + num4); return new TransferFunction(array, array2); } public static TransferFunction IirCombPeak(double frequency, double q = 20.0) { Guard.AgainstInvalidRange(frequency, 0.0, 0.5, "Center frequency"); double num = 2.0 * frequency * Math.PI / q; double num2 = 1.0 / Math.Sqrt(2.0); int num3 = (int)(1.0 / frequency); double num4 = Math.Sqrt(num2 * num2 / (1.0 - num2 * num2)) * Math.Tan((double)num3 * num / 4.0); double[] array = new double[num3 + 1]; double[] array2 = new double[num3 + 1]; array[0] = num4 / (1.0 + num4); array[^1] = (0.0 - num4) / (1.0 + num4); array2[0] = 1.0; array2[^1] = (1.0 - num4) / (1.0 + num4); return new TransferFunction(array, array2); } public static TransferFunction IirLpTf(double frequency, Complex[] poles, Complex[] zeros = null) { Guard.AgainstInvalidRange(frequency, 0.0, 0.5, "Cutoff frequency"); double[] array = new double[poles.Length]; double[] array2 = new double[poles.Length]; double num = Math.Tan(Math.PI * frequency); for (int i = 0; i < poles.Length; i++) { Complex complex = (Complex)num * poles[i]; array[i] = complex.Real; array2[i] = complex.Imaginary; } MathUtils.BilinearTransform(array, array2); double[] array3; double[] array4; if (zeros != null) { array3 = new double[zeros.Length]; array4 = new double[zeros.Length]; for (int j = 0; j < zeros.Length; j++) { Complex complex2 = (Complex)num * zeros[j]; array3[j] = complex2.Real; array4[j] = complex2.Imaginary; } MathUtils.BilinearTransform(array3, array4); } else { array3 = Enumerable.Repeat(-1.0, poles.Length).ToArray(); array4 = new double[poles.Length]; } TransferFunction transferFunction = new TransferFunction(new ComplexDiscreteSignal(1, array3, array4), new ComplexDiscreteSignal(1, array, array2)); transferFunction.NormalizeAt(0.0); return transferFunction; } public static TransferFunction IirHpTf(double frequency, Complex[] poles, Complex[] zeros = null) { Guard.AgainstInvalidRange(frequency, 0.0, 0.5, "Cutoff frequency"); double[] array = new double[poles.Length]; double[] array2 = new double[poles.Length]; double num = Math.Tan(Math.PI * frequency); for (int i = 0; i < poles.Length; i++) { Complex complex = (Complex)num / poles[i]; array[i] = complex.Real; array2[i] = complex.Imaginary; } MathUtils.BilinearTransform(array, array2); double[] array3; double[] array4; if (zeros != null) { array3 = new double[zeros.Length]; array4 = new double[zeros.Length]; for (int j = 0; j < zeros.Length; j++) { Complex complex2 = (Complex)num / zeros[j]; array3[j] = complex2.Real; array4[j] = complex2.Imaginary; } MathUtils.BilinearTransform(array3, array4); } else { array3 = Enumerable.Repeat(1.0, poles.Length).ToArray(); array4 = new double[poles.Length]; } TransferFunction transferFunction = new TransferFunction(new ComplexDiscreteSignal(1, array3, array4), new ComplexDiscreteSignal(1, array, array2)); transferFunction.NormalizeAt(Math.PI); return transferFunction; } public static TransferFunction IirBpTf(double frequencyLow, double frequencyHigh, Complex[] poles, Complex[] zeros = null) { Guard.AgainstInvalidRange(frequencyLow, 0.0, 0.5, "lower frequency"); Guard.AgainstInvalidRange(frequencyHigh, 0.0, 0.5, "upper frequency"); Guard.AgainstInvalidRange(frequencyLow, frequencyHigh, "lower frequency", "upper frequency"); double[] array = new double[poles.Length * 2]; double[] array2 = new double[poles.Length * 2]; double freq = Math.PI * 2.0 * (frequencyLow + frequencyHigh) / 2.0; double num = Math.Tan(Math.PI * frequencyLow); double num2 = Math.Tan(Math.PI * frequencyHigh); double num3 = Math.Sqrt(num * num2); double num4 = num2 - num; for (int i = 0; i < poles.Length; i++) { Complex complex = (Complex)(num4 / 2.0) * poles[i]; Complex complex2 = Complex.Sqrt((Complex)1 - Complex.Pow((Complex)num3 / complex, 2.0)); Complex complex3 = complex * ((Complex)1 + complex2); array[i] = complex3.Real; array2[i] = complex3.Imaginary; Complex complex4 = complex * ((Complex)1 - complex2); array[poles.Length + i] = complex4.Real; array2[poles.Length + i] = complex4.Imaginary; } MathUtils.BilinearTransform(array, array2); double[] array3; double[] array4; if (zeros != null) { array3 = new double[zeros.Length * 2]; array4 = new double[zeros.Length * 2]; for (int j = 0; j < zeros.Length; j++) { Complex complex5 = (Complex)(num4 / 2.0) * zeros[j]; Complex complex6 = Complex.Sqrt((Complex)1 - Complex.Pow((Complex)num3 / complex5, 2.0)); Complex complex7 = complex5 * ((Complex)1 + complex6); array3[j] = complex7.Real; array4[j] = complex7.Imaginary; Complex complex8 = complex5 * ((Complex)1 - complex6); array3[zeros.Length + j] = complex8.Real; array4[zeros.Length + j] = complex8.Imaginary; } MathUtils.BilinearTransform(array3, array4); } else { array3 = Enumerable.Repeat(-1.0, poles.Length).Concat(Enumerable.Repeat(1.0, poles.Length)).ToArray(); array4 = new double[poles.Length * 2]; } TransferFunction transferFunction = new TransferFunction(new ComplexDiscreteSignal(1, array3, array4), new ComplexDiscreteSignal(1, array, array2)); transferFunction.NormalizeAt(freq); return transferFunction; } public static TransferFunction IirBsTf(double frequencyLow, double frequencyHigh, Complex[] poles, Complex[] zeros = null) { Guard.AgainstInvalidRange(frequencyLow, 0.0, 0.5, "lower frequency"); Guard.AgainstInvalidRange(frequencyHigh, 0.0, 0.5, "upper frequency"); Guard.AgainstInvalidRange(frequencyLow, frequencyHigh, "lower frequency", "upper frequency"); double[] array = new double[poles.Length * 2]; double[] array2 = new double[poles.Length * 2]; double num = Math.Tan(Math.PI * frequencyLow); double num2 = Math.Tan(Math.PI * frequencyHigh); double num3 = Math.Sqrt(num * num2); double num4 = num2 - num; double num5 = 2.0 * Math.Atan(num3); for (int i = 0; i < poles.Length; i++) { Complex complex = (Complex)(num4 / 2.0) / poles[i]; Complex complex2 = Complex.Sqrt((Complex)1 - Complex.Pow((Complex)num3 / complex, 2.0)); Complex complex3 = complex * ((Complex)1 + complex2); array[i] = complex3.Real; array2[i] = complex3.Imaginary; Complex complex4 = complex * ((Complex)1 - complex2); array[poles.Length + i] = complex4.Real; array2[poles.Length + i] = complex4.Imaginary; } MathUtils.BilinearTransform(array, array2); double[] array3; double[] array4; if (zeros != null) { array3 = new double[zeros.Length * 2]; array4 = new double[zeros.Length * 2]; for (int j = 0; j < zeros.Length; j++) { Complex complex5 = (Complex)(num4 / 2.0) / zeros[j]; Complex complex6 = Complex.Sqrt((Complex)1 - Complex.Pow((Complex)num3 / complex5, 2.0)); Complex complex7 = complex5 * ((Complex)1 + complex6); array3[j] = complex7.Real; array4[j] = complex7.Imaginary; Complex complex8 = complex5 * ((Complex)1 - complex6); array3[zeros.Length + j] = complex8.Real; array4[zeros.Length + j] = complex8.Imaginary; } MathUtils.BilinearTransform(array3, array4); } else { array3 = new double[poles.Length * 2]; array4 = new double[poles.Length * 2]; for (int k = 0; k < poles.Length; k++) { array3[k] = Math.Cos(num5); array4[k] = Math.Sin(num5); array3[poles.Length + k] = Math.Cos(0.0 - num5); array4[poles.Length + k] = Math.Sin(0.0 - num5); } } TransferFunction transferFunction = new TransferFunction(new ComplexDiscreteSignal(1, array3, array4), new ComplexDiscreteSignal(1, array, array2)); transferFunction.NormalizeAt(0.0); return transferFunction; } public static TransferFunction SosToTf(TransferFunction[] sos) { return sos.Aggregate((TransferFunction tf, TransferFunction s) => tf * s); } public static TransferFunction[] TfToSos(TransferFunction tf) { List list = tf.Zeros.ToList(); List list2 = tf.Poles.ToList(); if (list.Count != list2.Count) { if (list.Count > list2.Count) { list2.AddRange(new Complex[list.Count - list2.Count]); } if (list.Count < list2.Count) { list.AddRange(new Complex[list2.Count - list.Count]); } } int num = (list2.Count + 1) / 2; if (list2.Count % 2 == 1) { list.Add(Complex.Zero); list2.Add(Complex.Zero); } RemoveConjugated(list); RemoveConjugated(list2); double[] array = new double[num]; array[0] = tf.Gain; for (int i = 1; i < array.Length; i++) { array[i] = 1.0; } TransferFunction[] array2 = new TransferFunction[num]; for (int num2 = num - 1; num2 >= 0; num2--) { int index = ClosestToUnitCircle(list2, Any); Complex complex = list2[index]; list2.RemoveAt(index); Complex complex2; Complex complex3; Complex complex4; if (IsReal(complex) && list2.All(IsComplex)) { index = ClosestToComplexValue(list, complex, IsReal); complex2 = list[index]; list.RemoveAt(index); complex3 = Complex.Zero; complex4 = Complex.Zero; } else { index = ((!IsComplex(complex) || list.Count(IsReal) != 1) ? ClosestToComplexValue(list, complex, Any) : ClosestToComplexValue(list, complex, IsComplex)); complex2 = list[index]; list.RemoveAt(index); if (IsComplex(complex)) { complex3 = Complex.Conjugate(complex); if (IsComplex(complex2)) { complex4 = Complex.Conjugate(complex2); } else { index = ClosestToComplexValue(list, complex, IsReal); complex4 = list[index]; list.RemoveAt(index); } } else if (IsComplex(complex2)) { complex4 = Complex.Conjugate(complex2); index = ClosestToComplexValue(list2, complex2, IsReal); complex3 = list2[index]; list2.RemoveAt(index); } else { index = ClosestToUnitCircle(list2, IsReal); complex3 = list2[index]; list2.RemoveAt(index); index = ClosestToComplexValue(list, complex3, IsReal); complex4 = list[index]; list.RemoveAt(index); } } array2[num2] = new TransferFunction(new Complex[2] { complex2, complex4 }, new Complex[2] { complex, complex3 }, array[num2]); } return array2; } private static int ClosestToComplexValue(List arr, Complex value, Func condition) { int result = 0; double num = double.MaxValue; for (int i = 0; i < arr.Count; i++) { if (condition(arr[i])) { double num2 = Complex.Abs(arr[i] - value); if (num2 < num) { num = num2; result = i; } } } return result; } private static int ClosestToUnitCircle(List arr, Func condition) { int result = 0; double num = double.MaxValue; for (int i = 0; i < arr.Count; i++) { if (condition(arr[i])) { double num2 = Complex.Abs(Complex.Abs(arr[i]) - 1.0); if (num2 < num) { num = num2; result = i; } } } return result; } private static void RemoveConjugated(List c) { for (int i = 0; i < c.Count; i++) { if (!IsReal(c[i])) { int j; for (j = i + 1; j < c.Count && (!(Math.Abs(c[i].Real - c[j].Real) < 1E-10) || !(Math.Abs(c[i].Imaginary + c[j].Imaginary) < 1E-10)); j++) { } if (j == c.Count) { throw new ArgumentException($"Complex array does not contain conjugated pair for {c[i]}"); } c.RemoveAt(j); } } } } public static class FilterBanks { public static float[][] Triangular(int fftSize, int samplingRate, (double, double, double)[] frequencies, VtlnWarper vtln = null, Func mapper = null) { if (mapper == null) { mapper = (double x) => x; } Func func = ((vtln == null) ? mapper : ((Func)((double x) => mapper(vtln.Warp(x))))); double herzResolution = (double)samplingRate / (double)fftSize; double[] array = (from f in Enumerable.Range(0, fftSize / 2 + 1) select (double)f * herzResolution).ToArray(); int num = frequencies.Length; float[][] array2 = new float[num][]; for (int num2 = 0; num2 < num; num2++) { array2[num2] = new float[fftSize / 2 + 1]; (double, double, double) tuple = frequencies[num2]; double item = tuple.Item1; double item2 = tuple.Item2; double item3 = tuple.Item3; item = func(item); item2 = func(item2); item3 = func(item3); int num3; for (num3 = 0; mapper(array[num3]) <= item; num3++) { } for (; mapper(array[num3]) <= item2; num3++) { array2[num2][num3] = (float)((mapper(array[num3]) - item) / (item2 - item)); } for (; num3 < array.Length && mapper(array[num3]) < item3; num3++) { array2[num2][num3] = (float)((item3 - mapper(array[num3])) / (item3 - item2)); } } return array2; } public static float[][] Rectangular(int fftSize, int samplingRate, (double, double, double)[] frequencies, VtlnWarper vtln = null, Func mapper = null) { if (mapper == null) { mapper = (double x) => x; } Func func = ((vtln == null) ? mapper : ((Func)((double x) => mapper(vtln.Warp(x))))); double herzResolution = (double)samplingRate / (double)fftSize; double[] array = (from f in Enumerable.Range(0, fftSize / 2 + 1) select (double)f * herzResolution).ToArray(); int num = frequencies.Length; float[][] array2 = new float[num][]; for (int num2 = 0; num2 < num; num2++) { array2[num2] = new float[fftSize / 2 + 1]; (double, double, double) tuple = frequencies[num2]; double item = tuple.Item1; double item2 = tuple.Item2; double item3 = tuple.Item3; item = func(item); item2 = func(item2); item3 = func(item3); int num3; for (num3 = 0; mapper(array[num3]) <= item; num3++) { } for (; num3 < array.Length && mapper(array[num3]) < item3; num3++) { array2[num2][num3] = 1f; } } return array2; } public static float[][] Trapezoidal(int fftSize, int samplingRate, (double, double, double)[] frequencies, VtlnWarper vtln = null, Func mapper = null) { float[][] array = Rectangular(fftSize, samplingRate, frequencies, vtln, mapper); for (int i = 0; i < array.Length; i++) { TransferFunction transferFunction = new TransferFunction(DesignFilter.Fir(fftSize / 4 + 1, null, array[i].ToDoubles(), fftSize)); array[i] = transferFunction.FrequencyResponse(fftSize).Magnitude.ToFloats(); float num = 0f; for (int j = 0; j < array[i].Length; j++) { if (array[i][j] > num) { num = array[i][j]; } } for (int k = 0; k < array[i].Length; k++) { array[i][k] /= num; } } return array; } public static float[][] BiQuad(int fftSize, int samplingRate, (double, double, double)[] frequencies) { double[] array = frequencies.Select(((double, double, double) f) => f.Item2).ToArray(); int num = frequencies.Length; float[][] array2 = new float[num][]; for (int num2 = 0; num2 < num; num2++) { BandPassFilter bandPassFilter = new BandPassFilter(array[num2] / (double)samplingRate, 2.0); array2[num2] = bandPassFilter.Tf.FrequencyResponse(fftSize).Magnitude.ToFloats(); } return array2; } private static (double, double, double)[] UniformBands(Func scaleMapper, Func inverseMapper, int filterCount, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, bool overlap = true) { if (lowFreq < 0.0) { lowFreq = 0.0; } if (highFreq <= lowFreq) { highFreq = (double)samplingRate / 2.0; } double startingFrequency = scaleMapper(lowFreq); (double, double, double)[] array = new(double, double, double)[filterCount]; if (overlap) { double newResolution = (scaleMapper(highFreq) - scaleMapper(lowFreq)) / (double)(filterCount + 1); double[] array2 = (from i in Enumerable.Range(0, filterCount + 2) select inverseMapper(startingFrequency + (double)i * newResolution)).ToArray(); for (int num = 0; num < filterCount; num++) { array[num] = (array2[num], array2[num + 1], array2[num + 2]); } } else { double newResolution2 = (scaleMapper(highFreq) - scaleMapper(lowFreq)) / (double)filterCount; double[] array3 = (from i in Enumerable.Range(0, filterCount + 1) select inverseMapper(startingFrequency + (double)i * newResolution2)).ToArray(); for (int num2 = 0; num2 < filterCount; num2++) { array[num2] = (array3[num2], (array3[num2] + array3[num2 + 1]) / 2.0, array3[num2 + 1]); } } return array; } public static (double, double, double)[] HerzBands(int combFilterCount, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, bool overlap = false) { return UniformBands((double x) => x, (double x) => x, combFilterCount, samplingRate, lowFreq, highFreq, overlap); } public static (double, double, double)[] MelBands(int melFilterCount, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, bool overlap = true) { return UniformBands(Scale.HerzToMel, Scale.MelToHerz, melFilterCount, samplingRate, lowFreq, highFreq, overlap); } public static (double, double, double)[] MelBandsSlaney(int melFilterCount, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, bool overlap = true) { return UniformBands(Scale.HerzToMelSlaney, Scale.MelToHerzSlaney, melFilterCount, samplingRate, lowFreq, highFreq, overlap); } public static (double, double, double)[] BarkBands(int barkFilterCount, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, bool overlap = true) { return UniformBands(Scale.HerzToBark, Scale.BarkToHerz, barkFilterCount, samplingRate, lowFreq, highFreq, overlap); } public static (double, double, double)[] BarkBandsSlaney(int barkFilterCount, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, bool overlap = true) { return UniformBands(Scale.HerzToBarkSlaney, Scale.BarkToHerzSlaney, barkFilterCount, samplingRate, lowFreq, highFreq, overlap); } public static (double, double, double)[] CriticalBands(int filterCount, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0) { if (lowFreq < 0.0) { lowFreq = 0.0; } if (highFreq <= lowFreq) { highFreq = (double)samplingRate / 2.0; } double[] source = new double[26] { 20.0, 100.0, 200.0, 300.0, 400.0, 510.0, 630.0, 770.0, 920.0, 1080.0, 1270.0, 1480.0, 1720.0, 2000.0, 2320.0, 2700.0, 3150.0, 3700.0, 4400.0, 5300.0, 6400.0, 7700.0, 9500.0, 12000.0, 15500.0, 20500.0 }; double[] array = new double[25] { 50.0, 150.0, 250.0, 350.0, 450.0, 570.0, 700.0, 840.0, 1000.0, 1170.0, 1370.0, 1600.0, 1850.0, 2150.0, 2500.0, 2900.0, 3400.0, 4000.0, 4800.0, 5800.0, 7000.0, 8500.0, 10500.0, 13500.0, 17500.0 }; int num = 0; for (int i = 0; i < array.Length; i++) { if (!(array[i] < lowFreq)) { num = i; break; } } int num2 = 0; for (int num3 = array.Length - 1; num3 >= 0; num3--) { if (!(array[num3] > highFreq)) { num2 = num3; break; } } filterCount = Math.Min(num2 - num + 1, filterCount); double[] array2 = source.Skip(num).Take(filterCount + 1).ToArray(); double[] array3 = array.Skip(num).Take(filterCount).ToArray(); (double, double, double)[] array4 = new(double, double, double)[filterCount]; for (int j = 0; j < filterCount; j++) { array4[j] = (array2[j], array3[j], array2[j + 1]); } return array4; } public static (double, double, double)[] OctaveBands(int octaveCount, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, bool overlap = false) { if (lowFreq < 1E-10) { lowFreq = 62.5; } if (highFreq <= lowFreq) { highFreq = (double)samplingRate / 2.0; } double num = lowFreq; double num2 = lowFreq * 2.0; List<(double, double, double)> list = new List<(double, double, double)>(); if (overlap) { double num3 = num2 * 2.0; for (int i = 0; i < octaveCount; i++) { if (!(num3 < highFreq)) { break; } list.Add((num, num2, num3)); num = num2; num2 = num3; num3 *= 2.0; } } else { for (int j = 0; j < octaveCount; j++) { if (!(num2 < highFreq)) { break; } list.Add((num, (num + num2) / 2.0, num2)); num *= 2.0; num2 *= 2.0; } } return list.ToArray(); } public static float[][] Chroma(int fftSize, int samplingRate, int chromaCount = 12, double tuning = 0.0, double centerOctave = 5.0, double octaveWidth = 2.0, int norm = 2, bool baseC = true) { float step = (float)samplingRate / (float)fftSize; fftSize = fftSize / 2 + 1; float[] source = (from num13 in Enumerable.Range(1, fftSize - 1) select (float)num13 * step).ToArray(); float[] freqBins = new float[1].Concat(source.Select((float f) => (float)chromaCount * (float)Scale.HerzToOctave(f, tuning, chromaCount))).ToArray(); freqBins[0] = freqBins[1] - (float)chromaCount * 1.5f; float[] array = (from num13 in Enumerable.Range(1, fftSize - 1) select Math.Max(freqBins[num13] - freqBins[num13 - 1], 1f)).Concat(new float[1] { 1f }).ToArray(); float[][] array2 = new float[chromaCount][]; int i; for (i = 0; i < chromaCount; i++) { array2[i] = freqBins.Select((float f) => f - (float)i).ToArray(); } double num = Math.Round((double)chromaCount / 2.0, MidpointRounding.AwayFromZero); for (int num2 = 0; num2 < chromaCount; num2++) { for (int num3 = 0; num3 < array2[num2].Length; num3++) { double num4 = ((double)array2[num2][num3] + num + (double)(10 * chromaCount)) % (double)chromaCount - num; array2[num2][num3] = (float)Math.Exp(-0.5 * Math.Pow(2.0 * num4 / (double)array[num3], 2.0)); } } if (norm > 0) { for (int num5 = 0; num5 < fftSize; num5++) { float num6 = 0f; for (int num7 = 0; num7 < chromaCount; num7++) { num6 += (float)Math.Pow(Math.Abs(array2[num7][num5]), norm); } num6 = (float)(1.0 / Math.Pow(num6, 1.0 / (double)norm)); for (int num8 = 0; num8 < chromaCount; num8++) { array2[num8][num5] *= num6; } } } if (octaveWidth > 0.0) { for (int num9 = 0; num9 < chromaCount; num9++) { for (int num10 = 0; num10 < array2[num9].Length; num10++) { array2[num9][num10] *= (float)Math.Exp(-0.5 * Math.Pow(((double)(freqBins[num10] / (float)chromaCount) - centerOctave) / octaveWidth, 2.0)); } } } if (baseC) { for (int num11 = 0; num11 < 3; num11++) { float[] array3 = array2[0]; for (int num12 = 0; num12 < array2.Length - 1; num12++) { array2[num12] = array2[num12 + 1]; } array2[^1] = array3; } } return array2; } public static float[][] MelBankSlaney(int filterCount, int fftSize, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, bool normalizeGain = true, VtlnWarper vtln = null) { if (lowFreq < 0.0) { lowFreq = 0.0; } if (highFreq <= lowFreq) { highFreq = (double)samplingRate / 2.0; } (double, double, double)[] frequencies = UniformBands(Scale.HerzToMelSlaney, Scale.MelToHerzSlaney, filterCount, samplingRate, lowFreq, highFreq); float[][] array = Triangular(fftSize, samplingRate, frequencies, vtln); if (normalizeGain) { Normalize(filterCount, frequencies, array); } return array; } public static float[][] BarkBankSlaney(int filterCount, int fftSize, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, double width = 1.0) { if (lowFreq < 0.0) { lowFreq = 0.0; } if (highFreq <= lowFreq) { highFreq = (double)samplingRate / 2.0; } double num = Scale.HerzToBarkSlaney(lowFreq); double num2 = Scale.HerzToBarkSlaney(highFreq) - num; double herzResolution = (double)samplingRate / (double)fftSize; double num3 = num2 / (double)(filterCount - 1); double[] array = (from i in Enumerable.Range(0, fftSize / 2 + 1) select Scale.HerzToBarkSlaney((double)i * herzResolution)).ToArray(); float[][] array2 = new float[filterCount][]; double num4 = num; int num5 = 0; while (num5 < filterCount) { array2[num5] = new float[fftSize / 2 + 1]; for (int num6 = 0; num6 < array2[num5].Length; num6++) { double num7 = array[num6] - num4 - 0.5; double val = array[num6] - num4 + 0.5; array2[num5][num6] = (float)Math.Pow(10.0, Math.Min(0.0, Math.Min(val, -2.5 * num7) / width)); } num5++; num4 += num3; } return array2; } public static float[][] Erb(int erbFilterCount, int fftSize, int samplingRate, double lowFreq = 0.0, double highFreq = 0.0, bool normalizeGain = true) { if (lowFreq < 0.0) { lowFreq = 0.0; } if (highFreq <= lowFreq) { highFreq = (double)samplingRate / 2.0; } double num = 1.0 / (double)samplingRate; double[] array = new double[erbFilterCount]; for (int i = 1; i <= erbFilterCount; i++) { array[erbFilterCount - i] = -228.832903 + Math.Exp((double)i * (0.0 - Math.Log(highFreq + 228.832903) + Math.Log(lowFreq + 228.832903)) / (double)erbFilterCount) * (highFreq + 228.832903); } Complex[] array2 = new Complex[fftSize / 2 + 1]; for (int j = 0; j < array2.Length; j++) { array2[j] = Complex.Exp((Complex)2 * Complex.ImaginaryOne * (Complex)j * (Complex)Math.PI / (Complex)fftSize); } double num2 = Math.Sqrt(3.0 + Math.Pow(2.0, 1.5)); double num3 = Math.Sqrt(3.0 - Math.Pow(2.0, 1.5)); Fft fft = new Fft(fftSize); float[][] array3 = new float[erbFilterCount][]; for (int k = 0; k < erbFilterCount; k++) { double num4 = array[k]; double num5 = Math.Pow(Math.Pow(num4 / 9.26449, 1.0) + Math.Pow(24.7, 1.0), 1.0); double num6 = 6.402565828015998 * num5; double num7 = 2.0 * num4 * Math.PI * num; Complex complex = Complex.Exp((Complex)2 * Complex.ImaginaryOne * (Complex)num7); double num8 = num; double num9 = 0.0; double num10 = 1.0; double num11 = -2.0 * Math.Cos(num7) / Math.Exp(num6 * num); double num12 = Math.Exp(-2.0 * num6 * num); double num13 = (0.0 - num) * Math.Exp((0.0 - num6) * num); double num14 = Math.Cos(num7) + num2 * Math.Sin(num7); double num15 = Math.Cos(num7) - num2 * Math.Sin(num7); double num16 = Math.Cos(num7) + num3 * Math.Sin(num7); double num17 = Math.Cos(num7) - num3 * Math.Sin(num7); double num18 = num13 * num14; double num19 = num13 * num15; double num20 = num13 * num16; double num21 = num13 * num17; Complex complex2 = Complex.Exp(Complex.ImaginaryOne * (Complex)num7 - (Complex)(num6 * num)); float coeff = (float)Complex.Abs((complex - complex2 * (Complex)num14) * (complex - complex2 * (Complex)num15) * (complex - complex2 * (Complex)num16) * (complex - complex2 * (Complex)num17) * Complex.Pow((Complex)(num * Math.Exp(num6 * num)) / ((Complex)(-1.0 / Math.Exp(num6 * num) + 1.0) + complex * (Complex)(1.0 - Math.Exp(num6 * num))), 4.0)); IirFilter iirFilter = new IirFilter(new double[3] { num8, num18, num9 }, new double[3] { num10, num11, num12 }); IirFilter iirFilter2 = new IirFilter(new double[3] { num8, num19, num9 }, new double[3] { num10, num11, num12 }); IirFilter iirFilter3 = new IirFilter(new double[3] { num8, num20, num9 }, new double[3] { num10, num11, num12 }); IirFilter iirFilter4 = new IirFilter(new double[3] { num8, num21, num9 }, new double[3] { num10, num11, num12 }); DiscreteSignal signal = DiscreteSignal.Unit(fftSize); DiscreteSignal signal2 = new FilterChain(new IirFilter[4] { iirFilter, iirFilter2, iirFilter3, iirFilter4 }).ApplyTo(signal); signal2.Attenuate(coeff); array3[k] = fft.PowerSpectrum(signal2, normalize: false).Samples; } if (!normalizeGain) { return array3; } float[][] array4 = array3; foreach (float[] array5 in array4) { double num22 = 0.0; for (int m = 0; m < array5.Length; m++) { num22 += (double)Math.Abs(array5[m] * array5[m]); } double num23 = Math.Sqrt(num22 * (double)samplingRate / (double)fftSize); for (int n = 0; n < array5.Length; n++) { array5[n] = (float)((double)array5[n] / num23); } } return array3; } public static void Normalize(int filterCount, (double, double, double)[] frequencies, float[][] filterBank) { for (int i = 0; i < filterCount; i++) { (double, double, double) tuple = frequencies[i]; double item = tuple.Item1; double item2 = tuple.Item3; for (int j = 0; j < filterBank[i].Length; j++) { filterBank[i][j] *= 2f / (float)(item2 - item); } } } public static void Apply(float[][] filterbank, float[] spectrum, float[] filtered) { for (int i = 0; i < filterbank.Length; i++) { float num = 0f; for (int j = 0; j < spectrum.Length; j++) { num += filterbank[i][j] * spectrum[j]; } filtered[i] = num; } } public static float[][] Apply(float[][] filterbank, IList spectrogram) { float[][] array = new float[spectrogram.Count][]; for (int i = 0; i < array.Length; i++) { array[i] = new float[filterbank.Length]; } for (int j = 0; j < filterbank.Length; j++) { for (int k = 0; k < array.Length; k++) { float num = 0f; for (int l = 0; l < spectrogram[j].Length; l++) { num += filterbank[j][l] * spectrogram[k][l]; } array[k][j] = num; } } return array; } public static void ApplyAndLog(float[][] filterbank, float[] spectrum, float[] filtered, float floor = float.Epsilon) { for (int i = 0; i < filterbank.Length; i++) { float num = 0f; for (int j = 0; j < spectrum.Length; j++) { num += filterbank[i][j] * spectrum[j]; } filtered[i] = (float)Math.Log(Math.Max(num, floor)); } } public static void ApplyAndLog10(float[][] filterbank, float[] spectrum, float[] filtered, float floor = float.Epsilon) { for (int i = 0; i < filterbank.Length; i++) { float num = 0f; for (int j = 0; j < spectrum.Length; j++) { num += filterbank[i][j] * spectrum[j]; } filtered[i] = (float)Math.Log10(Math.Max(num, floor)); } } public static void ApplyAndToDecibel(float[][] filterbank, float[] spectrum, float[] filtered, float minLevel = 1E-10f) { for (int i = 0; i < filterbank.Length; i++) { float num = 0f; for (int j = 0; j < spectrum.Length; j++) { num += filterbank[i][j] * spectrum[j]; } filtered[i] = (float)Scale.ToDecibelPower(Math.Max(num, minLevel)); } } public static void ApplyAndPow(float[][] filterbank, float[] spectrum, float[] filtered, double power = 1.0 / 3.0) { for (int i = 0; i < filterbank.Length; i++) { float num = 0f; for (int j = 0; j < spectrum.Length; j++) { num += filterbank[i][j] * spectrum[j]; } filtered[i] = (float)Math.Pow(num, power); } } } public class Remez { private const double Tolerance = 1E-07; private readonly int[] _extrs; private double[] _grid; private readonly double[] _freqs; private double[] _desired; private double[] _weights; private readonly double[] _points; private readonly double[] _gammas; private readonly double[] _cosTable; public int Order { get; private set; } public int Iterations { get; private set; } public int K { get; private set; } public double[] InterpolatedResponse { get; private set; } public double[] Error { get; private set; } public double[] ExtremalFrequencies => _extrs.Select((int e) => _grid[e]).ToArray(); public Remez(int order, double[] frequencies, double[] desired, double[] weights, int gridDensity = 16) { Guard.AgainstEvenNumber(order, "The order of the filter"); Guard.AgainstIncorrectFilterParams(frequencies, desired, weights); Order = order; K = Order / 2 + 2; _freqs = frequencies; MakeGrid(desired, weights, gridDensity); InterpolatedResponse = new double[_grid.Length]; Error = new double[_grid.Length]; _extrs = new int[K]; _points = new double[K]; _gammas = new double[K]; _cosTable = new double[K]; } private void MakeGrid(double[] desired, double[] weights, int gridDensity = 16) { int num = 0; int[] array = new int[_freqs.Length / 2]; double num2 = 0.5 / (double)(gridDensity * (K - 1)); for (int i = 0; i < array.Length; i++) { array[i] = (int)((_freqs[2 * i + 1] - _freqs[2 * i]) / num2 + 0.5); num += array[i]; } _grid = new double[num]; _weights = new double[num]; _desired = new double[num]; int num3 = 0; for (int j = 0; j < array.Length; j++) { double num4 = _freqs[2 * j]; int num5 = 0; while (num5 < array[j]) { _grid[num3] = num4; _weights[num3] = weights[j]; _desired[num3] = desired[j]; num5++; num3++; num4 += num2; } _grid[num3 - 1] = _freqs[2 * j + 1]; } } private void InitExtrema() { int num = _grid.Length; for (int i = 0; i < K; i++) { _extrs[i] = (int)((double)i * ((double)num - 1.0) / (double)(K - 1)); } } public double[] Design(int maxIterations = 100) { InitExtrema(); int[] array = new int[2 * K]; Iterations = 0; while (Iterations < maxIterations) { UpdateCoefficients(); for (int i = 0; i < _grid.Length; i++) { InterpolatedResponse[i] = Lagrange(_grid[i]); } for (int j = 0; j < _grid.Length; j++) { Error[j] = _weights[j] * (_desired[j] - InterpolatedResponse[j]); } int num = 0; int num2 = _grid.Length; if (Math.Abs(Error[0]) > Math.Abs(Error[1])) { array[num++] = 0; } for (int k = 1; k < num2 - 1; k++) { if ((Error[k] > 0.0 && Error[k] >= Error[k - 1] && Error[k] > Error[k + 1]) || (Error[k] < 0.0 && Error[k] <= Error[k - 1] && Error[k] < Error[k + 1])) { array[num++] = k; } } if (Math.Abs(Error[num2 - 1]) > Math.Abs(Error[num2 - 2])) { array[num++] = num2 - 1; } if (num < K) { break; } while (num > K) { int num3 = 0; for (int l = 1; l < num; l++) { if (Math.Abs(Error[array[l]]) < Math.Abs(Error[array[num3]])) { num3 = l; } } num--; for (int m = num3; m < num; m++) { array[m] = array[m + 1]; } } Array.Copy(array, _extrs, K); double num4 = Math.Abs(Error[0]); double num5 = num4; for (int n = 0; n < K; n++) { double num6 = Math.Abs(Error[_extrs[n]]); if (num6 < num5) { num5 = num6; } if (num6 > num4) { num4 = num6; } } if ((num4 - num5) / num5 < 1E-06) { break; } Iterations++; } return ImpulseResponse(); } private void UpdateCoefficients() { for (int i = 0; i < _cosTable.Length; i++) { _cosTable[i] = Math.Cos(Math.PI * 2.0 * _grid[_extrs[i]]); } double num = 0.0; double num2 = 0.0; int num3 = 0; int num4 = 1; while (num3 < K) { _gammas[num3] = Gamma(num3); num += _gammas[num3] * _desired[_extrs[num3]]; num2 += (double)num4 * _gammas[num3] / _weights[_extrs[num3]]; num3++; num4 = -num4; } double num5 = num / num2; int num6 = 0; int num7 = 1; while (num6 < K) { _points[num6] = _desired[_extrs[num6]] - (double)num7 * num5 / _weights[_extrs[num6]]; num6++; num7 = -num7; } } private double[] ImpulseResponse() { UpdateCoefficients(); int num = Order / 2; double[] array = (from i in Enumerable.Range(0, num + 1) select Lagrange((double)i / (double)Order)).ToArray(); double[] array2 = new double[Order]; for (int num2 = 0; num2 < Order; num2++) { double num3 = 0.0; for (int num4 = 1; num4 <= num; num4++) { num3 += array[num4] * Math.Cos(Math.PI * 2.0 * (double)num4 * (double)(num2 - num) / (double)Order); } array2[num2] = (array[0] + 2.0 * num3) / (double)Order; } return array2; } private double Gamma(int k) { int num = (K - 1) / 15 + 1; double num2 = 1.0; for (int i = 0; i < num; i++) { for (int j = i; j < K; j += num) { if (j != k) { num2 *= 2.0 * (_cosTable[k] - _cosTable[j]); } } } if (Math.Abs(num2) < 1E-07) { num2 = 1E-07; } return 1.0 / num2; } private double Lagrange(double freq) { double num = 0.0; double num2 = 0.0; double num3 = Math.Cos(Math.PI * 2.0 * freq); for (int i = 0; i < K; i++) { double num4 = num3 - _cosTable[i]; if (Math.Abs(num4) < 1E-07) { return _points[i]; } num4 = _gammas[i] / num4; num2 += num4; num += num4 * _points[i]; } return num / num2; } public static double DbToPassbandWeight(double ripple) { return (Math.Pow(10.0, ripple / 20.0) - 1.0) / (Math.Pow(10.0, ripple / 20.0) + 1.0); } public static double DbToStopbandWeight(double ripple) { return Math.Pow(10.0, (0.0 - ripple) / 20.0); } public static int EstimateOrder(double fp, double fa, double dp, double da) { if (dp < da) { double num = dp; dp = da; da = num; } double num2 = fa - fp; double num3 = (0.005309 * Math.Log10(dp) * Math.Log10(dp) + 0.07114 * Math.Log10(dp) - 0.4761) * Math.Log10(da) - (0.00266 * Math.Log10(dp) * Math.Log10(dp) + 0.5941 * Math.Log10(dp) + 0.4278); double num4 = 0.51244 * (Math.Log10(dp) - Math.Log10(da)) + 11.012; int num5 = (int)((num3 - num4 * num2 * num2) / num2 + 1.5); if (num5 % 2 != 1) { return num5 + 1; } return num5; } public static int EstimateOrder(double[] frequencies, double[] deltas) { int num = 0; int num2 = 1; for (int i = 0; i < deltas.Length - 1; i++) { int num3 = EstimateOrder(frequencies[num2], frequencies[num2 + 1], deltas[i], deltas[i + 1]); if (num3 > num) { num = num3; } num2 += 2; } return num; } } public class VtlnWarper { private readonly double _lowFreq; private readonly double _highFreq; private readonly double _lowVtln; private readonly double _highVtln; private readonly double _scale; private readonly double _scaleLeft; private readonly double _scaleRight; public VtlnWarper(double alpha, double lowFrequency, double highFrequency, double lowVtln, double highVtln) { _lowFreq = lowFrequency; _highFreq = highFrequency; _lowVtln = lowVtln * Math.Max(1.0, alpha); _highVtln = highVtln * Math.Min(1.0, alpha); _scale = 1.0 / alpha; _scaleLeft = (_scale * _lowVtln - lowFrequency) / (_lowVtln - lowFrequency); _scaleRight = (highFrequency - _scale * _highVtln) / (highFrequency - _highVtln); } public double Warp(double frequency) { if (frequency < _lowVtln) { return _lowFreq + _scaleLeft * (frequency - _lowFreq); } if (frequency < _highVtln) { return _scale * frequency; } return _highFreq + _scaleRight * (frequency - _highFreq); } } } namespace NWaves.Filters.Elliptic { public class BandPassFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public double RipplePassband { get; private set; } public double RippleStopband { get; private set; } public int Order => (_a.Length - 1) / 2; public BandPassFilter(double frequencyLow, double frequencyHigh, int order, double ripplePass = 1.0, double rippleStop = 20.0) : base(MakeTf(frequencyLow, frequencyHigh, order, ripplePass, rippleStop)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; RipplePassband = ripplePass; RippleStopband = rippleStop; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order, double ripplePass = 1.0, double rippleStop = 20.0) { return DesignFilter.IirBpTf(frequencyLow, frequencyHigh, PrototypeElliptic.Poles(order, ripplePass, rippleStop), PrototypeElliptic.Zeros(order, ripplePass, rippleStop)); } public void Change(double frequencyLow, double frequencyHigh, double ripplePass = 1.0, double rippleStop = 20.0) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; RipplePassband = ripplePass; RippleStopband = rippleStop; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2, ripplePass, rippleStop)); } } public class BandStopFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public double RipplePassband { get; private set; } public double RippleStopband { get; private set; } public int Order => (_a.Length - 1) / 2; public BandStopFilter(double frequencyLow, double frequencyHigh, int order, double ripplePass = 1.0, double rippleStop = 20.0) : base(MakeTf(frequencyLow, frequencyHigh, order, ripplePass, rippleStop)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; RipplePassband = ripplePass; RippleStopband = rippleStop; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order, double ripplePass = 1.0, double rippleStop = 20.0) { return DesignFilter.IirBsTf(frequencyLow, frequencyHigh, PrototypeElliptic.Poles(order, ripplePass, rippleStop), PrototypeElliptic.Zeros(order, ripplePass, rippleStop)); } public void Change(double frequencyLow, double frequencyHigh, double ripplePass = 1.0, double rippleStop = 20.0) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; RipplePassband = ripplePass; RippleStopband = rippleStop; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2, ripplePass, rippleStop)); } } public class HighPassFilter : ZiFilter { public double Frequency { get; private set; } public double RipplePassband { get; private set; } public double RippleStopband { get; private set; } public int Order => _a.Length - 1; public HighPassFilter(double frequency, int order, double ripplePass = 1.0, double rippleStop = 20.0) : base(MakeTf(frequency, order, ripplePass, rippleStop)) { Frequency = frequency; RipplePassband = ripplePass; RippleStopband = rippleStop; } private static TransferFunction MakeTf(double frequency, int order, double ripplePass = 1.0, double rippleStop = 20.0) { return DesignFilter.IirHpTf(frequency, PrototypeElliptic.Poles(order, ripplePass, rippleStop), PrototypeElliptic.Zeros(order, ripplePass, rippleStop)); } public void Change(double frequency, double ripplePass = 1.0, double rippleStop = 20.0) { Frequency = frequency; RipplePassband = ripplePass; RippleStopband = rippleStop; Change(MakeTf(frequency, _a.Length - 1, ripplePass, rippleStop)); } } public class LowPassFilter : ZiFilter { public double Frequency { get; private set; } public double RipplePassband { get; private set; } public double RippleStopband { get; private set; } public int Order => _a.Length - 1; public LowPassFilter(double frequency, int order, double ripplePass = 1.0, double rippleStop = 20.0) : base(MakeTf(frequency, order, ripplePass, rippleStop)) { Frequency = frequency; RipplePassband = ripplePass; RippleStopband = rippleStop; } private static TransferFunction MakeTf(double frequency, int order, double ripplePass = 1.0, double rippleStop = 20.0) { return DesignFilter.IirLpTf(frequency, PrototypeElliptic.Poles(order, ripplePass, rippleStop), PrototypeElliptic.Zeros(order, ripplePass, rippleStop)); } public void Change(double frequency, double ripplePass = 1.0, double rippleStop = 20.0) { Frequency = frequency; RipplePassband = ripplePass; RippleStopband = rippleStop; Change(MakeTf(frequency, _a.Length - 1, ripplePass, rippleStop)); } } public static class PrototypeElliptic { public static Complex[] Poles(int order, double ripplePass = 1.0, double rippleStop = 20.0) { Guard.AgainstInvalidRange(ripplePass, rippleStop, "ripple in passband", "ripple in stopband"); double num = Math.Sqrt(Math.Pow(10.0, ripplePass / 10.0) - 1.0); double num2 = Math.Sqrt(Math.Pow(10.0, rippleStop / 10.0) - 1.0); double num3 = num / num2; double num4 = Math.Sqrt(1.0 - num3 * num3); double[] landen = Landen(num4); Complex one = Complex.One; for (int i = 0; i < order / 2; i++) { one *= Sne(((double)(2 * i) + 1.0) / (double)order, landen); } one = Complex.Pow(num4 * num4, order / 2) * Complex.Pow(one, 4.0); double[] landen2 = Landen(Math.Sqrt(1.0 - Complex.Abs(one) * Complex.Abs(one))); Complex complex = -Complex.ImaginaryOne / (Complex)order * Asne(Complex.ImaginaryOne / (Complex)num, num3); Complex[] array = new Complex[order]; for (int j = 0; j < order; j++) { double num5 = ((double)(2 * j) + 1.0) / (double)order; array[j] = Complex.ImaginaryOne * Cde((Complex)num5 - Complex.ImaginaryOne * complex, landen2); } return array; } public static Complex[] Zeros(int order, double ripplePass = 1.0, double rippleStop = 20.0) { Guard.AgainstInvalidRange(ripplePass, rippleStop, "ripple in passband", "ripple in stopband"); double num = Math.Sqrt(Math.Pow(10.0, ripplePass / 10.0) - 1.0); double num2 = Math.Sqrt(Math.Pow(10.0, rippleStop / 10.0) - 1.0); double num3 = num / num2; double num4 = Math.Sqrt(1.0 - num3 * num3); double[] landen = Landen(num4); Complex one = Complex.One; for (int i = 0; i < order / 2; i++) { one *= Sne(((double)(2 * i) + 1.0) / (double)order, landen); } one = Complex.Pow(num4 * num4, order / 2) * Complex.Pow(one, 4.0); double num5 = Math.Sqrt(1.0 - Complex.Abs(one) * Complex.Abs(one)); double[] landen2 = Landen(num5); Complex[] array = new Complex[order]; for (int j = 0; j < order; j++) { double num6 = ((double)(2 * j) + 1.0) / (double)order; array[j] = new Complex(0.0, -1.0 / ((Complex)num5 * Cde(num6, landen2)).Real); } return array; } public static double[] Landen(double k, int iterCount = 5) { double[] array = new double[iterCount]; for (int i = 0; i < iterCount; i++) { double num = Math.Sqrt(1.0 - k * k); k = (1.0 - num) / (1.0 + num); array[i] = k; } return array; } public static Complex Cde(Complex x, double[] landen) { Complex complex = (Complex)1 / Complex.Cos(x * (Complex)Math.PI / (Complex)2); for (int num = landen.Length - 1; num >= 0; num--) { complex = (Complex)(1.0 / (1.0 + landen[num])) * (complex + (Complex)landen[num] / complex); } return (Complex)1 / complex; } public static Complex Sne(Complex x, double[] landen) { Complex complex = (Complex)1 / Complex.Sin(x * (Complex)Math.PI / (Complex)2); for (int num = landen.Length - 1; num >= 0; num--) { complex = (Complex)(1.0 / (1.0 + landen[num])) * (complex + (Complex)landen[num] / complex); } return (Complex)1 / complex; } public static Complex Asne(Complex x, double k, int iterCount = 5) { for (int i = 1; i <= iterCount; i++) { double num = k; k = Math.Pow(k / (1.0 + Math.Sqrt(1.0 - k * k)), 2.0); x = (Complex)2 * x / ((Complex)(1.0 + k) * ((Complex)1 + Complex.Sqrt((Complex)1 - (Complex)(num * num) * x * x))); } return (Complex)2 * Complex.Asin(x) / (Complex)Math.PI; } } } namespace NWaves.Filters.ChebyshevI { public class BandPassFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public double Ripple { get; private set; } public int Order => (_a.Length - 1) / 2; public BandPassFilter(double frequencyLow, double frequencyHigh, int order, double ripple = 0.1) : base(MakeTf(frequencyLow, frequencyHigh, order, ripple)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Ripple = ripple; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order, double ripple = 0.1) { return DesignFilter.IirBpTf(frequencyLow, frequencyHigh, PrototypeChebyshevI.Poles(order, ripple)); } public void Change(double frequencyLow, double frequencyHigh, double ripple = 0.1) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Ripple = ripple; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2, ripple)); } } public class BandStopFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public double Ripple { get; private set; } public int Order => (_a.Length - 1) / 2; public BandStopFilter(double frequencyLow, double frequencyHigh, int order, double ripple = 0.1) : base(MakeTf(frequencyLow, frequencyHigh, order, ripple)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Ripple = ripple; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order, double ripple = 0.1) { return DesignFilter.IirBsTf(frequencyLow, frequencyHigh, PrototypeChebyshevI.Poles(order, ripple)); } public void Change(double frequencyLow, double frequencyHigh, double ripple = 0.1) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Ripple = ripple; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2, ripple)); } } public class HighPassFilter : ZiFilter { public double Frequency { get; private set; } public double Ripple { get; private set; } public int Order => _a.Length - 1; public HighPassFilter(double frequency, int order, double ripple = 0.1) : base(MakeTf(frequency, order, ripple)) { Frequency = frequency; Ripple = ripple; } private static TransferFunction MakeTf(double frequency, int order, double ripple = 0.1) { return DesignFilter.IirHpTf(frequency, PrototypeChebyshevI.Poles(order, ripple)); } public void Change(double frequency, double ripple = 0.1) { Frequency = frequency; Ripple = ripple; Change(MakeTf(frequency, _a.Length - 1, ripple)); } } public class LowPassFilter : ZiFilter { public double Frequency { get; private set; } public double Ripple { get; private set; } public int Order => _a.Length - 1; public LowPassFilter(double frequency, int order, double ripple = 0.1) : base(MakeTf(frequency, order, ripple)) { Frequency = frequency; Ripple = ripple; } private static TransferFunction MakeTf(double frequency, int order, double ripple = 0.1) { return DesignFilter.IirLpTf(frequency, PrototypeChebyshevI.Poles(order, ripple)); } public void Change(double frequency, double ripple = 0.1) { Frequency = frequency; Ripple = ripple; Change(MakeTf(frequency, _a.Length - 1, ripple)); } } public static class PrototypeChebyshevI { public static Complex[] Poles(int order, double ripple = 0.1) { double num = Math.Sqrt(Math.Pow(10.0, ripple / 10.0) - 1.0); double value = MathUtils.Asinh(1.0 / num) / (double)order; double num2 = Math.Sinh(value); double num3 = Math.Cosh(value); Complex[] array = new Complex[order]; for (int i = 0; i < order; i++) { double num4 = Math.PI * (double)(2 * i + 1) / (double)(2 * order); double real = (0.0 - num2) * Math.Sin(num4); double imaginary = num3 * Math.Cos(num4); array[i] = new Complex(real, imaginary); } return array; } } } namespace NWaves.Filters.ChebyshevII { public class BandPassFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public double Ripple { get; private set; } public int Order => (_a.Length - 1) / 2; public BandPassFilter(double frequencyLow, double frequencyHigh, int order, double ripple = 0.1) : base(MakeTf(frequencyLow, frequencyHigh, order, ripple)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Ripple = ripple; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order, double ripple = 0.1) { return DesignFilter.IirBpTf(frequencyLow, frequencyHigh, PrototypeChebyshevII.Poles(order, ripple), PrototypeChebyshevII.Zeros(order)); } public void Change(double frequencyLow, double frequencyHigh, double ripple = 0.1) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Ripple = ripple; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2, ripple)); } } public class BandStopFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public double Ripple { get; private set; } public int Order => (_a.Length - 1) / 2; public BandStopFilter(double frequencyLow, double frequencyHigh, int order, double ripple = 0.1) : base(MakeTf(frequencyLow, frequencyHigh, order, ripple)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Ripple = ripple; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order, double ripple = 0.1) { return DesignFilter.IirBsTf(frequencyLow, frequencyHigh, PrototypeChebyshevII.Poles(order, ripple), PrototypeChebyshevII.Zeros(order)); } public void Change(double frequencyLow, double frequencyHigh, double ripple = 0.1) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Ripple = ripple; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2, ripple)); } } public class HighPassFilter : ZiFilter { public double Frequency { get; private set; } public double Ripple { get; private set; } public int Order => _a.Length - 1; public HighPassFilter(double frequency, int order, double ripple = 0.1) : base(MakeTf(frequency, order, ripple)) { Frequency = frequency; Ripple = ripple; } private static TransferFunction MakeTf(double frequency, int order, double ripple = 0.1) { return DesignFilter.IirHpTf(frequency, PrototypeChebyshevII.Poles(order, ripple)); } public void Change(double frequency, double ripple = 0.1) { Frequency = frequency; Ripple = ripple; Change(MakeTf(frequency, _a.Length - 1, ripple)); } } public class LowPassFilter : ZiFilter { public double Frequency { get; private set; } public double Ripple { get; private set; } public int Order => _a.Length - 1; public LowPassFilter(double frequency, int order, double ripple = 0.1) : base(MakeTf(frequency, order, ripple)) { Frequency = frequency; Ripple = ripple; } private static TransferFunction MakeTf(double frequency, int order, double ripple = 0.1) { return DesignFilter.IirLpTf(frequency, PrototypeChebyshevII.Poles(order, ripple)); } public void Change(double frequency, double ripple = 0.1) { Frequency = frequency; Ripple = ripple; Change(MakeTf(frequency, _a.Length - 1, ripple)); } } public static class PrototypeChebyshevII { public static Complex[] Poles(int order, double ripple = 0.1) { double num = Math.Sqrt(Math.Pow(10.0, ripple / 10.0) - 1.0); double value = MathUtils.Asinh(1.0 / num) / (double)order; double num2 = Math.Sinh(value); double num3 = Math.Cosh(value); Complex[] array = new Complex[order]; for (int i = 0; i < order; i++) { double num4 = Math.PI * (double)(2 * i + 1) / (double)(2 * order); double real = (0.0 - num2) * Math.Sin(num4); double imaginary = num3 * Math.Cos(num4); array[i] = (Complex)1 / new Complex(real, imaginary); } return array; } public static Complex[] Zeros(int order) { Complex[] array = new Complex[order]; for (int i = 0; i < order; i++) { double d = Math.PI * (double)(2 * i + 1) / (double)(2 * order); array[i] = new Complex(0.0, -1.0 / Math.Cos(d)); } return array; } } } namespace NWaves.Filters.Butterworth { public class BandPassFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public int Order => (_a.Length - 1) / 2; public BandPassFilter(double frequencyLow, double frequencyHigh, int order) : base(MakeTf(frequencyLow, frequencyHigh, order)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order) { return DesignFilter.IirBpTf(frequencyLow, frequencyHigh, PrototypeButterworth.Poles(order)); } public void Change(double frequencyLow, double frequencyHigh) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2)); } } public class BandStopFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public int Order => (_a.Length - 1) / 2; public BandStopFilter(double frequencyLow, double frequencyHigh, int order) : base(MakeTf(frequencyLow, frequencyHigh, order)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order) { return DesignFilter.IirBsTf(frequencyLow, frequencyHigh, PrototypeButterworth.Poles(order)); } public void Change(double frequencyLow, double frequencyHigh) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2)); } } public class HighPassFilter : ZiFilter { public double Frequency { get; private set; } public int Order => _a.Length - 1; public HighPassFilter(double frequency, int order) : base(MakeTf(frequency, order)) { Frequency = frequency; } private static TransferFunction MakeTf(double frequency, int order) { return DesignFilter.IirHpTf(frequency, PrototypeButterworth.Poles(order)); } public void Change(double frequency) { Frequency = frequency; Change(MakeTf(frequency, _a.Length - 1)); } } public class LowPassFilter : ZiFilter { public double Frequency { get; private set; } public int Order => _a.Length - 1; public LowPassFilter(double frequency, int order) : base(MakeTf(frequency, order)) { Frequency = frequency; } private static TransferFunction MakeTf(double frequency, int order) { return DesignFilter.IirLpTf(frequency, PrototypeButterworth.Poles(order)); } public void Change(double frequency) { Frequency = frequency; Change(MakeTf(frequency, _a.Length - 1)); } } public static class PrototypeButterworth { public static Complex[] Poles(int order) { Complex[] array = new Complex[order]; for (int i = 0; i < order; i++) { double num = Math.PI * (double)(2 * i + 1) / (double)(2 * order); array[i] = new Complex(0.0 - Math.Sin(num), Math.Cos(num)); } return array; } } } namespace NWaves.Filters.BiQuad { public class AllPassFilter : BiQuadFilter { public double Frequency { get; protected set; } public double Q { get; protected set; } public AllPassFilter(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } private void SetCoefficients(double frequency, double q) { Frequency = frequency; Q = q; double num = Math.PI * 2.0 * frequency; double num2 = Math.Sin(num) / (2.0 * q); double num3 = Math.Cos(num); _b[0] = (float)(1.0 - num2); _b[1] = (float)(-2.0 * num3); _b[2] = (float)(1.0 + num2); _a[0] = _b[2]; _a[1] = _b[1]; _a[2] = _b[0]; Normalize(); } public void Change(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } } public class BandPassFilter : BiQuadFilter { public double Frequency { get; protected set; } public double Q { get; protected set; } public BandPassFilter(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } private void SetCoefficients(double frequency, double q) { Frequency = frequency; Q = q; double num = Math.PI * 2.0 * frequency; double num2 = Math.Sin(num) / (2.0 * q); double num3 = Math.Cos(num); _b[0] = (float)num2; _b[1] = 0f; _b[2] = 0f - _b[0]; _a[0] = (float)(1.0 + num2); _a[1] = (float)(-2.0 * num3); _a[2] = (float)(1.0 - num2); Normalize(); } public void Change(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } } public class BiQuadFilter : IirFilter { private float _in1; private float _in2; private float _out1; private float _out2; protected BiQuadFilter() : base(new double[3] { 1.0, 0.0, 0.0 }, new double[3] { 1.0, 0.0, 0.0 }) { } public BiQuadFilter(double b0, double b1, double b2, double a0, double a1, double a2) : base(new double[3] { b0, b1, b2 }, new double[3] { a0, a1, a2 }) { } public override float Process(float sample) { float num = _b[0] * sample + _b[1] * _in1 + _b[2] * _in2 - _a[1] * _out1 - _a[2] * _out2; _in2 = _in1; _in1 = sample; _out2 = _out1; _out1 = num; return num; } public override void Reset() { _in1 = (_in2 = (_out1 = (_out2 = 0f))); } public void Change(float b0, float b1, float b2, float a0, float a1, float a2) { if (Math.Abs(a0) < 1E-30f) { throw new ArgumentException("The coefficient a0 can not be zero!"); } _b[0] = b0 / a0; _b[1] = b1 / a0; _b[2] = b2 / a0; _a[1] = a1 / a0; _a[2] = a2 / a0; } } public class HighPassFilter : BiQuadFilter { public double Frequency { get; protected set; } public double Q { get; protected set; } public HighPassFilter(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } private void SetCoefficients(double frequency, double q) { Frequency = frequency; Q = q; double num = Math.PI * 2.0 * frequency; double num2 = Math.Sin(num) / (2.0 * q); double num3 = Math.Cos(num); _b[0] = (float)((1.0 + num3) / 2.0); _b[1] = (float)(0.0 - (1.0 + num3)); _b[2] = _b[0]; _a[0] = (float)(1.0 + num2); _a[1] = (float)(-2.0 * num3); _a[2] = (float)(1.0 - num2); Normalize(); } public void Change(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } } public class HighShelfFilter : BiQuadFilter { public double Frequency { get; protected set; } public double Q { get; protected set; } public double Gain { get; protected set; } public HighShelfFilter(double frequency, double q = 1.0, double gain = 1.0) { SetCoefficients(frequency, q, gain); } private void SetCoefficients(double frequency, double q, double gain) { Frequency = frequency; Q = q; Gain = gain; double num = Math.Pow(10.0, gain / 40.0); double num2 = Math.Sqrt(num); double num3 = Math.PI * 2.0 * frequency; double num4 = Math.Sin(num3) / 2.0 * Math.Sqrt((num + 1.0 / num) * (1.0 / q - 1.0) + 2.0); double num5 = Math.Cos(num3); _b[0] = (float)(num * (num + 1.0 + (num - 1.0) * num5 + 2.0 * num2 * num4)); _b[1] = (float)(-2.0 * num * (num - 1.0 + (num + 1.0) * num5)); _b[2] = (float)(num * (num + 1.0 + (num - 1.0) * num5 - 2.0 * num2 * num4)); _a[0] = (float)(num + 1.0 - (num - 1.0) * num5 + 2.0 * num2 * num4); _a[1] = (float)(2.0 * (num - 1.0 - (num + 1.0) * num5)); _a[2] = (float)(num + 1.0 - (num - 1.0) * num5 - 2.0 * num2 * num4); Normalize(); } public void Change(double frequency, double q = 1.0, double gain = 1.0) { SetCoefficients(frequency, q, gain); } } public class LowPassFilter : BiQuadFilter { public double Frequency { get; protected set; } public double Q { get; protected set; } public LowPassFilter(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } private void SetCoefficients(double frequency, double q) { Frequency = frequency; Q = q; double num = Math.PI * 2.0 * frequency; double num2 = Math.Sin(num) / (2.0 * q); double num3 = Math.Cos(num); _b[0] = (float)((1.0 - num3) / 2.0); _b[1] = (float)(1.0 - num3); _b[2] = _b[0]; _a[0] = (float)(1.0 + num2); _a[1] = (float)(-2.0 * num3); _a[2] = (float)(1.0 - num2); Normalize(); } public void Change(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } } public class LowShelfFilter : BiQuadFilter { public double Frequency { get; protected set; } public double Q { get; protected set; } public double Gain { get; protected set; } public LowShelfFilter(double frequency, double q = 1.0, double gain = 1.0) { SetCoefficients(frequency, q, gain); } private void SetCoefficients(double frequency, double q, double gain) { Frequency = frequency; Q = q; Gain = gain; double num = Math.Pow(10.0, gain / 40.0); double num2 = Math.Sqrt(num); double num3 = Math.PI * 2.0 * frequency; double num4 = Math.Sin(num3) / 2.0 * Math.Sqrt((num + 1.0 / num) * (1.0 / q - 1.0) + 2.0); double num5 = Math.Cos(num3); _b[0] = (float)(num * (num + 1.0 - (num - 1.0) * num5 + 2.0 * num2 * num4)); _b[1] = (float)(2.0 * num * (num - 1.0 - (num + 1.0) * num5)); _b[2] = (float)(num * (num + 1.0 - (num - 1.0) * num5 - 2.0 * num2 * num4)); _a[0] = (float)(num + 1.0 + (num - 1.0) * num5 + 2.0 * num2 * num4); _a[1] = (float)(-2.0 * (num - 1.0 + (num + 1.0) * num5)); _a[2] = (float)(num + 1.0 + (num - 1.0) * num5 - 2.0 * num2 * num4); Normalize(); } public void Change(double frequency, double q = 1.0, double gain = 1.0) { SetCoefficients(frequency, q, gain); } } public class NotchFilter : BiQuadFilter { public double Frequency { get; protected set; } public double Q { get; protected set; } public NotchFilter(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } private void SetCoefficients(double frequency, double q) { Frequency = frequency; Q = q; double num = Math.PI * 2.0 * frequency; double num2 = Math.Sin(num) / (2.0 * q); double num3 = Math.Cos(num); _b[0] = 1f; _b[1] = (float)(-2.0 * num3); _b[2] = 1f; _a[0] = (float)(1.0 + num2); _a[1] = (float)(-2.0 * num3); _a[2] = (float)(1.0 - num2); Normalize(); } public void Change(double frequency, double q = 1.0) { SetCoefficients(frequency, q); } } public class PeakFilter : BiQuadFilter { public double Frequency { get; protected set; } public double Q { get; protected set; } public double Gain { get; protected set; } public PeakFilter(double frequency, double q = 1.0, double gain = 1.0) { SetCoefficients(frequency, q, gain); } private void SetCoefficients(double frequency, double q, double gain) { Frequency = frequency; Q = q; Gain = gain; double num = Math.Pow(10.0, gain / 40.0); double num2 = Math.PI * 2.0 * frequency; double num3 = Math.Sin(num2) / (2.0 * q); double num4 = Math.Cos(num2); _b[0] = (float)(1.0 + num3 * num); _b[1] = (float)(-2.0 * num4); _b[2] = (float)(1.0 - num3 * num); _a[0] = (float)(1.0 + num3 / num); _a[1] = (float)(-2.0 * num4); _a[2] = (float)(1.0 - num3 / num); Normalize(); } public void Change(double frequency, double q = 1.0, double gain = 1.0) { SetCoefficients(frequency, q, gain); } } } namespace NWaves.Filters.Bessel { public class BandPassFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public int Order => (_a.Length - 1) / 2; public BandPassFilter(double frequencyLow, double frequencyHigh, int order) : base(MakeTf(frequencyLow, frequencyHigh, order)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order) { return DesignFilter.IirBpTf(frequencyLow, frequencyHigh, PrototypeBessel.Poles(order)); } public void Change(double frequencyLow, double frequencyHigh) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2)); } } public class BandStopFilter : ZiFilter { public double FrequencyLow { get; private set; } public double FrequencyHigh { get; private set; } public int Order => (_a.Length - 1) / 2; public BandStopFilter(double frequencyLow, double frequencyHigh, int order) : base(MakeTf(frequencyLow, frequencyHigh, order)) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; } private static TransferFunction MakeTf(double frequencyLow, double frequencyHigh, int order) { return DesignFilter.IirBsTf(frequencyLow, frequencyHigh, PrototypeBessel.Poles(order)); } public void Change(double frequencyLow, double frequencyHigh) { FrequencyLow = frequencyLow; FrequencyHigh = frequencyHigh; Change(MakeTf(frequencyLow, frequencyHigh, (_a.Length - 1) / 2)); } } public class HighPassFilter : ZiFilter { public double Frequency { get; private set; } public int Order => _a.Length - 1; public HighPassFilter(double frequency, int order) : base(MakeTf(frequency, order)) { Frequency = frequency; } private static TransferFunction MakeTf(double frequency, int order) { return DesignFilter.IirHpTf(frequency, PrototypeBessel.Poles(order)); } public void Change(double frequency) { Frequency = frequency; Change(MakeTf(frequency, _a.Length - 1)); } } public class LowPassFilter : ZiFilter { public double Frequency { get; private set; } public int Order => _a.Length - 1; public LowPassFilter(double frequency, int order) : base(MakeTf(frequency, order)) { Frequency = frequency; } private static TransferFunction MakeTf(double frequency, int order) { return DesignFilter.IirLpTf(frequency, PrototypeBessel.Poles(order)); } public void Change(double frequency) { Frequency = frequency; Change(MakeTf(frequency, _a.Length - 1)); } } public static class PrototypeBessel { public static double Reverse(int k, int n) { return MathUtils.Factorial(2 * n - k) / (Math.Pow(2.0, n - k) * MathUtils.Factorial(k) * MathUtils.Factorial(n - k)); } public static Complex[] Poles(int order) { double[] array = (from i in Enumerable.Range(0, order + 1) select Reverse(order - i, order)).ToArray(); Complex[] array2 = MathUtils.PolynomialRoots(array); double num = Math.Pow(10.0, (0.0 - Math.Log10(array[order - 1])) / (double)order); for (int num2 = 0; num2 < array2.Length; num2++) { array2[num2] *= (Complex)num; } return array2; } } } namespace NWaves.Filters.Base { public class FilterChain : IFilter, IOnlineFilter { private readonly List _filters; public FilterChain(IEnumerable filters = null) { _filters = filters?.ToList() ?? new List(); } public FilterChain(IEnumerable tfs) { _filters = new List(); foreach (TransferFunction tf in tfs) { _filters.Add(new IirFilter(tf)); } } public void Add(IOnlineFilter filter) { _filters.Add(filter); } public void Insert(int index, IOnlineFilter filter) { _filters.Insert(index, filter); } public void RemoveAt(int index) { _filters.RemoveAt(index); } public float Process(float sample) { float num = sample; foreach (IOnlineFilter filter in _filters) { num = filter.Process(num); } return num; } public void Reset() { foreach (IOnlineFilter filter in _filters) { filter.Reset(); } } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } public enum FilteringMethod { Auto, DifferenceEquation, OverlapAdd, OverlapSave, Custom } public class FirFilter : LtiFilter { protected readonly float[] _b; protected int _kernelSize; protected TransferFunction _tf; protected float[] _delayLine; protected int _delayLineOffset; public float[] Kernel => _b.Take(_kernelSize).ToArray(); public override TransferFunction Tf { get { return _tf ?? new TransferFunction(_b.Take(_kernelSize).ToDoubles()); } protected set { _tf = value; } } public int KernelSizeForBlockConvolution { get; set; } = 64; public FirFilter(IEnumerable kernel) { _kernelSize = kernel.Count(); _b = new float[_kernelSize * 2]; for (int i = 0; i < _kernelSize; i++) { _b[i] = (_b[_kernelSize + i] = kernel.ElementAt(i)); } _delayLine = new float[_kernelSize]; _delayLineOffset = _kernelSize - 1; } public FirFilter(IEnumerable kernel) : this(kernel.ToFloats()) { } public FirFilter(TransferFunction tf) : this(tf.Numerator.ToFloats()) { Tf = tf; } public override DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { if (_kernelSize >= KernelSizeForBlockConvolution && method == FilteringMethod.Auto) { method = FilteringMethod.OverlapSave; } switch (method) { case FilteringMethod.OverlapAdd: { int fftSize2 = MathUtils.NextPowerOfTwo(4 * _kernelSize); return OlaBlockConvolver.FromFilter(this, fftSize2).ApplyTo(signal); } case FilteringMethod.OverlapSave: { int fftSize = MathUtils.NextPowerOfTwo(4 * _kernelSize); return OlsBlockConvolver.FromFilter(this, fftSize).ApplyTo(signal); } case FilteringMethod.DifferenceEquation: return ApplyFilterDirectly(signal); default: return new DiscreteSignal(signal.SamplingRate, ProcessAllSamples(signal.Samples)); } } public override float Process(float sample) { _delayLine[_delayLineOffset] = sample; float num = 0f; int num2 = 0; int num3 = _kernelSize - _delayLineOffset; while (num2 < _kernelSize) { num += _delayLine[num2] * _b[num3]; num2++; num3++; } if (--_delayLineOffset < 0) { _delayLineOffset = _kernelSize - 1; } return num; } public float[] ProcessAllSamples(float[] samples) { float[] array = new float[samples.Length + _kernelSize - 1]; int num = 0; while (num < samples.Length) { _delayLine[_delayLineOffset] = samples[num]; float num2 = 0f; int num3 = 0; int num4 = _kernelSize - _delayLineOffset; while (num3 < _kernelSize) { num2 += _delayLine[num3] * _b[num4]; num3++; num4++; } if (--_delayLineOffset < 0) { _delayLineOffset = _kernelSize - 1; } array[num++] = num2; } while (num < array.Length) { array[num++] = Process(0f); } return array; } protected DiscreteSignal ApplyFilterDirectly(DiscreteSignal signal) { float[] samples = signal.Samples; float[] array = new float[samples.Length + _kernelSize - 1]; for (int i = 0; i < array.Length; i++) { for (int j = 0; j < _kernelSize; j++) { if (i >= j && i < samples.Length + j) { array[i] += _b[j] * samples[i - j]; } } } return new DiscreteSignal(signal.SamplingRate, array); } public void ChangeKernel(float[] kernel) { if (kernel.Length == _kernelSize) { for (int i = 0; i < _kernelSize; i++) { _b[i] = (_b[_kernelSize + i] = kernel[i]); } } } public override void Reset() { _delayLineOffset = _kernelSize - 1; Array.Clear(_delayLine, 0, _kernelSize); } public static FirFilter operator *(FirFilter filter1, FirFilter filter2) { return new FirFilter((filter1.Tf * filter2.Tf).Numerator); } public static IirFilter operator *(FirFilter filter1, IirFilter filter2) { TransferFunction transferFunction = filter1.Tf * filter2.Tf; return new IirFilter(transferFunction.Numerator, transferFunction.Denominator); } public static FirFilter operator +(FirFilter filter1, FirFilter filter2) { return new FirFilter((filter1.Tf + filter2.Tf).Numerator); } public static IirFilter operator +(FirFilter filter1, IirFilter filter2) { TransferFunction transferFunction = filter1.Tf + filter2.Tf; return new IirFilter(transferFunction.Numerator, transferFunction.Denominator); } } public interface IFilter { DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto); } public static class IFilterExtensions { public static void Process(this IOnlineFilter filter, float[] input, float[] output, int count = 0, int inputPos = 0, int outputPos = 0) { if (count <= 0) { count = input.Length; } int num = inputPos + count; int num2 = inputPos; int num3 = outputPos; while (num2 < num) { output[num3] = filter.Process(input[num2]); num2++; num3++; } } public static DiscreteSignal FilterOnline(this IOnlineFilter filter, DiscreteSignal signal) { float[] array = new float[signal.Length]; float[] samples = signal.Samples; for (int i = 0; i < samples.Length; i++) { array[i] = filter.Process(samples[i]); } return new DiscreteSignal(signal.SamplingRate, array); } public static double[] FilterOnline(this IOnlineFilter64 filter, double[] signal) { double[] array = new double[signal.Length]; for (int i = 0; i < signal.Length; i++) { array[i] = filter.Process(signal[i]); } return array; } public static float EstimateGain(this IOnlineFilter filter, int fftSize = 512) { float[] samples = DiscreteSignal.Unit(fftSize).Samples.Select((float s) => filter.Process(s)).ToArray(); float[] array = new float[fftSize / 2 + 1]; new RealFft(fftSize).MagnitudeSpectrum(samples, array); return 1f / array.Max((float s) => Math.Abs(s)); } public static DiscreteSignal ApplyTo(this IOnlineFilter filter, DiscreteSignal signal, float gain) { IEnumerable samples = signal.Samples.Select((float s) => gain * filter.Process(s)); return new DiscreteSignal(signal.SamplingRate, samples); } public static float Process(this IOnlineFilter filter, float sample, float gain) { return gain * filter.Process(sample); } } public class IirFilter : LtiFilter { protected readonly float[] _b; protected readonly float[] _a; protected readonly int _numeratorSize; protected readonly int _denominatorSize; protected TransferFunction _tf; protected float[] _delayLineA; protected float[] _delayLineB; protected int _delayLineOffsetA; protected int _delayLineOffsetB; public override TransferFunction Tf { get { return _tf ?? new TransferFunction(_b.Take(_numeratorSize).ToDoubles(), _a.ToDoubles()); } protected set { _tf = value; } } public int DefaultImpulseResponseLength { get; set; } = 512; public IirFilter(IEnumerable b, IEnumerable a) { _numeratorSize = b.Count(); _denominatorSize = a.Count(); _b = new float[_numeratorSize * 2]; for (int i = 0; i < _numeratorSize; i++) { _b[i] = (_b[_numeratorSize + i] = b.ElementAt(i)); } _a = a.ToArray(); _delayLineB = new float[_numeratorSize]; _delayLineA = new float[_denominatorSize]; _delayLineOffsetB = _numeratorSize - 1; _delayLineOffsetA = _denominatorSize - 1; } public IirFilter(IEnumerable b, IEnumerable a) : this(b.ToFloats(), a.ToFloats()) { } public IirFilter(TransferFunction tf) : this(tf.Numerator, tf.Denominator) { Tf = tf; } public override DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { switch (method) { case FilteringMethod.OverlapAdd: case FilteringMethod.OverlapSave: { int num = Math.Max(DefaultImpulseResponseLength, _denominatorSize + _numeratorSize); int fftSize = MathUtils.NextPowerOfTwo(4 * num); DiscreteSignal kernel = new DiscreteSignal(signal.SamplingRate, Tf.ImpulseResponse(num).ToFloats()); return Operation.BlockConvolve(signal, kernel, fftSize, method); } case FilteringMethod.DifferenceEquation: return ApplyFilterDirectly(signal); default: return this.FilterOnline(signal); } } public override float Process(float sample) { float num = 0f; _delayLineB[_delayLineOffsetB] = sample; int num2 = 0; int num3 = _numeratorSize - _delayLineOffsetB; while (num2 < _numeratorSize) { num += _delayLineB[num2] * _b[num3]; num2++; num3++; } int num4 = 1; for (int i = _delayLineOffsetA + 1; i < _a.Length; i++) { num -= _a[num4++] * _delayLineA[i]; } for (int j = 0; j < _delayLineOffsetA; j++) { num -= _a[num4++] * _delayLineA[j]; } _delayLineA[_delayLineOffsetA] = num; if (--_delayLineOffsetB < 0) { _delayLineOffsetB = _numeratorSize - 1; } if (--_delayLineOffsetA < 0) { _delayLineOffsetA = _denominatorSize - 1; } return num; } protected DiscreteSignal ApplyFilterDirectly(DiscreteSignal signal) { float[] samples = signal.Samples; float[] array = new float[samples.Length]; for (int i = 0; i < samples.Length; i++) { for (int j = 0; j < _numeratorSize; j++) { if (i >= j) { array[i] += _b[j] * samples[i - j]; } } for (int k = 1; k < _denominatorSize; k++) { if (i >= k) { array[i] -= _a[k] * array[i - k]; } } } return new DiscreteSignal(signal.SamplingRate, array); } public void ChangeNumeratorCoeffs(float[] b) { if (b.Length == _numeratorSize) { for (int i = 0; i < _numeratorSize; i++) { _b[i] = (_b[_numeratorSize + i] = b[i]); } } } public void ChangeDenominatorCoeffs(float[] a) { if (a.Length == _denominatorSize) { for (int i = 0; i < _denominatorSize; i++) { _a[i] = a[i]; } } } public void Change(TransferFunction tf) { double[] numerator = tf.Numerator; if (numerator.Length == _numeratorSize) { for (int i = 0; i < _numeratorSize; i++) { _b[i] = (_b[_numeratorSize + i] = (float)numerator[i]); } } double[] denominator = tf.Denominator; if (denominator.Length == _denominatorSize) { for (int j = 0; j < _a.Length; j++) { _a[j] = (float)denominator[j]; } } } public override void Reset() { _delayLineOffsetB = _numeratorSize - 1; _delayLineOffsetA = _denominatorSize - 1; int num = 0; while (num < _delayLineB.Length) { _delayLineB[num++] = 0f; } int num2 = 0; while (num2 < _delayLineA.Length) { _delayLineA[num2++] = 0f; } } public void Normalize() { float num = _a[0]; if (!(Math.Abs(num - 1f) < 1E-10f)) { if (Math.Abs(num) < 1E-30f) { throw new ArgumentException("The coefficient a[0] can not be zero!"); } int num2 = 0; while (num2 < _a.Length) { _a[num2++] /= num; } int num3 = 0; while (num3 < _b.Length) { _b[num3++] /= num; } _tf?.Normalize(); } } public static IirFilter operator *(IirFilter filter1, LtiFilter filter2) { TransferFunction transferFunction = filter1.Tf * filter2.Tf; return new IirFilter(transferFunction.Numerator, transferFunction.Denominator); } public static IirFilter operator +(IirFilter filter1, LtiFilter filter2) { TransferFunction transferFunction = filter1.Tf + filter2.Tf; return new IirFilter(transferFunction.Numerator, transferFunction.Denominator); } } public interface IOnlineFilter { float Process(float sample); void Reset(); } public abstract class LtiFilter : IFilter, IOnlineFilter { public abstract TransferFunction Tf { get; protected set; } public abstract DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto); public abstract float Process(float sample); public abstract void Reset(); } public abstract class OverlapAddFilter : WetDryMixer, IFilter, IOnlineFilter { protected readonly int _hopSize; protected readonly int _fftSize; protected float _gain; protected readonly int _overlapSize; protected readonly RealFft _fft; protected readonly float[] _window; private readonly float[] _dl; private int _inOffset; private int _outOffset; private readonly float[] _re; private readonly float[] _im; private readonly float[] _filteredRe; private readonly float[] _filteredIm; private readonly float[] _lastSaved; public OverlapAddFilter(int hopSize, int fftSize = 0) { _hopSize = hopSize; _fftSize = ((fftSize > 0) ? fftSize : (8 * hopSize)); _overlapSize = _fftSize - _hopSize; Guard.AgainstInvalidRange(_hopSize, _fftSize, "Hop size", "FFT size"); _fft = new RealFft(_fftSize); _window = Window.OfType(WindowType.Hann, _fftSize); _gain = 1f / ((float)_fftSize * _window.Select((float w) => w * w).Sum() / (float)_hopSize); _dl = new float[_fftSize]; _re = new float[_fftSize]; _im = new float[_fftSize]; _filteredRe = new float[_fftSize]; _filteredIm = new float[_fftSize]; _lastSaved = new float[_overlapSize]; _inOffset = _overlapSize; } public virtual float Process(float sample) { _dl[_inOffset++] = sample; if (_inOffset == _fftSize) { ProcessFrame(); } return _filteredRe[_outOffset++]; } protected virtual void ProcessFrame() { _dl.FastCopyTo(_re, _fftSize); _re.ApplyWindow(_window); _fft.Direct(_re, _re, _im); ProcessSpectrum(_re, _im, _filteredRe, _filteredIm); _fft.Inverse(_filteredRe, _filteredIm, _filteredRe); _filteredRe.ApplyWindow(_window); for (int i = 0; i < _overlapSize; i++) { _filteredRe[i] += _lastSaved[i]; } _filteredRe.FastCopyTo(_lastSaved, _overlapSize, _hopSize); for (int j = 0; j < _filteredRe.Length; j++) { _filteredRe[j] *= base.Wet * _gain; _filteredRe[j] += _dl[j] * base.Dry; } _dl.FastCopyTo(_dl, _overlapSize, _hopSize); _inOffset = _overlapSize; _outOffset = 0; } protected abstract void ProcessSpectrum(float[] re, float[] im, float[] filteredRe, float[] filteredIm); public virtual void Reset() { _inOffset = _overlapSize; _outOffset = 0; Array.Clear(_dl, 0, _dl.Length); Array.Clear(_re, 0, _re.Length); Array.Clear(_im, 0, _im.Length); Array.Clear(_filteredRe, 0, _filteredRe.Length); Array.Clear(_filteredIm, 0, _filteredIm.Length); Array.Clear(_lastSaved, 0, _lastSaved.Length); } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } public class StateSpace { public double[][] A { get; set; } public double[] B { get; set; } public double[] C { get; set; } public double[] D { get; set; } } public class StereoFilter : IFilter, IOnlineFilter { private readonly IOnlineFilter _filterLeft; private readonly IOnlineFilter _filterRight; private bool _isRight; public StereoFilter(IOnlineFilter filterLeft, IOnlineFilter filterRight) { _filterLeft = filterLeft; _filterRight = filterRight; } public float Process(float sample) { if (_isRight) { _isRight = false; return _filterRight.Process(sample); } _isRight = true; return _filterLeft.Process(sample); } public void Reset() { _filterLeft.Reset(); _filterRight.Reset(); } public DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } public class TransferFunction { protected Complex[] _zeros; protected Complex[] _poles; public double[] Numerator { get; protected set; } public double[] Denominator { get; protected set; } public int CalculateZpIterations { get; set; } = 25000; public Complex[] Zeros => _zeros ?? TfToZp(Numerator, CalculateZpIterations); public Complex[] Poles => _poles ?? TfToZp(Denominator, CalculateZpIterations); public double Gain => Numerator[0]; public StateSpace StateSpace { get { int num = Numerator.Length; int num2 = Denominator.Length; if (num > num2) { throw new ArgumentException("Numerator size must not exceed denominator size"); } double num3 = Denominator[0]; if (num2 == 1) { StateSpace stateSpace = new StateSpace(); stateSpace.A = new Matrix(1).As2dArray(); stateSpace.B = new double[num]; stateSpace.C = new double[num]; stateSpace.D = new double[1] { Numerator[0] / num3 }; return stateSpace; } double[] array = Numerator; if (num < num2) { array = new double[num2]; Numerator.FastCopyTo(array, num, 0, num2 - num); } Matrix matrix = new Matrix(num2 - 1); for (int i = 0; i < num2 - 1; i++) { matrix[0][i] = (0.0 - Denominator[i + 1]) / num3; } for (int j = 1; j < num2 - 1; j++) { matrix[j][j - 1] = 1.0; } double[] array2 = new double[num2 - 1]; array2[0] = 1.0; double[] array3 = new double[num2 - 1]; for (int k = 0; k < num2 - 1; k++) { array3[k] = (array[k + 1] - array[0] * Denominator[k + 1] / num3) / num3; } double[] d = new double[1] { array[0] / num3 }; return new StateSpace { A = matrix.As2dArray(), B = array2, C = array3, D = d }; } } public double[] Zi { get { int num = Math.Max(Numerator.Length, Denominator.Length); double[] array = Denominator.PadZeros(num); double[] array2 = Numerator.PadZeros(num); double num2 = array[0]; int num3 = 0; while (num3 < array.Length) { array[num3++] /= num2; } int num4 = 0; while (num4 < array2.Length) { array2[num4++] /= num2; } double[] array3 = new double[num - 1]; for (int i = 1; i < num; i++) { array3[i - 1] = array2[i] - array[i] * array2[0]; } Matrix matrix = Matrix.Eye(num - 1) - Matrix.Companion(array).T; double num5 = 0.0; for (int j = 0; j < num - 1; j++) { num5 += matrix[j][0]; } double[] array4 = new double[num]; array4[0] = array3.Sum() / num5; double num6 = 1.0; double num7 = 0.0; for (int k = 1; k < num - 1; k++) { num6 += array[k]; num7 += array2[k] - array[k] * array2[0]; array4[k] = num6 * array4[0] - num7; } return array4; } } public TransferFunction(double[] numerator, double[] denominator = null) { Numerator = numerator; Denominator = denominator ?? new double[1] { 1.0 }; } public TransferFunction(Complex[] zeros, Complex[] poles, double gain = 1.0) { _zeros = zeros; _poles = poles; Denominator = ((poles != null) ? ZpToTf(poles) : new double[1] { 1.0 }); Numerator = ((zeros != null) ? ZpToTf(zeros) : new double[1] { 1.0 }); for (int i = 0; i < Numerator.Length; i++) { Numerator[i] *= gain; } } public TransferFunction(ComplexDiscreteSignal zeros, ComplexDiscreteSignal poles, double gain = 1.0) : this(zeros.ToComplexNumbers().ToArray(), poles.ToComplexNumbers().ToArray(), gain) { } public TransferFunction(StateSpace stateSpace) { double[][] a = stateSpace.A; Denominator = new double[a.Length + 1]; Denominator[0] = 1.0; for (int i = 1; i < Denominator.Length; i++) { Denominator[i] = 0.0 - a[0][i - 1]; } double[] c = stateSpace.C; double[] d = stateSpace.D; double[] array = new double[a.Length + 1]; for (int j = 0; j < a.Length; j++) { array[j + 1] = 0.0 - (a[0][j] - c[j]) + (d[0] - 1.0) * Denominator[j + 1]; } int num = 0; for (int k = 1; k < array.Length; k++) { if (Math.Abs(array[k]) > 1E-08) { num = k; break; } } if (Math.Abs(d[0]) > 1E-08) { num--; } Numerator = array.FastCopyFragment(array.Length - num, num); if (Math.Abs(d[0]) > 1E-08) { Numerator[0] = d[0]; } } public double[] ImpulseResponse(int length = 512) { if (Denominator.Length == 1) { return Numerator.FastCopy(); } double[] numerator = Numerator; double[] denominator = Denominator; double[] array = new double[length]; for (int i = 0; i < array.Length; i++) { if (i < numerator.Length) { array[i] = numerator[i]; } for (int j = 1; j < denominator.Length; j++) { if (i >= j) { array[i] -= denominator[j] * array[i - j]; } } } return array; } public ComplexDiscreteSignal FrequencyResponse(int length = 512) { double[] array = ImpulseResponse(length); double[] array2 = ((array.Length == length) ? array : ((array.Length < length) ? array.PadZeros(length) : array.FastCopyFragment(length))); double[] array3 = new double[length]; new Fft64(length).Direct(array2, array3); return new ComplexDiscreteSignal(1, array2.Take(length / 2 + 1), array3.Take(length / 2 + 1)); } public double[] GroupDelay(int length = 512) { double[] real = new ComplexConvolver().CrossCorrelate(new ComplexDiscreteSignal(1, Numerator), new ComplexDiscreteSignal(1, Denominator)).Real; double[] a = Enumerable.Range(0, real.Length).Zip(real, (int r, double c) => (double)r * c).Reverse() .ToArray(); real = real.Reverse().ToArray(); double num = Math.PI / (double)length; double num2 = 0.0; int num3 = Denominator.Length - 1; double[] array = new double[length]; for (int num4 = 0; num4 < array.Length; num4++) { Complex x = Complex.FromPolarCoordinates(1.0, 0.0 - num2); Complex complex = MathUtils.EvaluatePolynomial(a, x); Complex complex2 = MathUtils.EvaluatePolynomial(real, x); array[num4] = ((Complex.Abs(complex2) < 1E-30) ? 0.0 : ((complex / complex2).Real - (double)num3)); num2 += num; } return array; } public double[] PhaseDelay(int length = 512) { double[] array = GroupDelay(length); double[] array2 = new double[array.Length]; double num = 0.0; for (int i = 0; i < array2.Length; i++) { num += array[i]; array2[i] = num / (double)(i + 1); } return array2; } public void NormalizeAt(double freq) { Complex x = Complex.FromPolarCoordinates(1.0, freq); double num = Complex.Abs(MathUtils.EvaluatePolynomial(Denominator, x) / MathUtils.EvaluatePolynomial(Numerator, x)); for (int i = 0; i < Numerator.Length; i++) { Numerator[i] *= num; } } public void Normalize() { double num = Denominator[0]; if (Math.Abs(num) < 1E-10) { throw new ArgumentException("The first denominator coefficient can not be zero!"); } for (int i = 0; i < Denominator.Length; i++) { Denominator[i] /= num; } for (int j = 0; j < Numerator.Length; j++) { Numerator[j] /= num; } } public static double[] ZpToTf(Complex[] zp) { Complex[] array = new Complex[2] { 1, -zp[0] }; for (int i = 1; i < zp.Length; i++) { Complex[] poly = new Complex[2] { 1, -zp[i] }; array = MathUtils.MultiplyPolynomials(array, poly); } return array.Select((Complex p) => p.Real).ToArray(); } public static double[] ZpToTf(ComplexDiscreteSignal zp) { return ZpToTf(zp.ToComplexNumbers().ToArray()); } public static double[] ZpToTf(double[] re, double[] im = null) { if (im == null) { im = new double[re.Length]; } return ZpToTf(re.Zip(im, (double r, double i) => new Complex(r, i)).ToArray()); } public static Complex[] TfToZp(double[] numeratorOrDenominator, int maxIterations = 25000) { if (numeratorOrDenominator.Length <= 1) { return null; } return MathUtils.PolynomialRoots(numeratorOrDenominator, maxIterations); } public static TransferFunction operator *(TransferFunction tf1, TransferFunction tf2) { double[] numerator = Operation.Convolve(tf1.Numerator, tf2.Numerator); double[] denominator = Operation.Convolve(tf1.Denominator, tf2.Denominator); return new TransferFunction(numerator, denominator); } public static TransferFunction operator +(TransferFunction tf1, TransferFunction tf2) { double[] array = Operation.Convolve(tf1.Numerator, tf2.Denominator); double[] array2 = Operation.Convolve(tf2.Numerator, tf1.Denominator); double[] array3 = array; double[] array4 = array2; if (array.Length < array2.Length) { array3 = array2; array4 = array; } for (int i = 0; i < array4.Length; i++) { array3[i] += array4[i]; } double[] denominator = Operation.Convolve(tf1.Denominator, tf2.Denominator); return new TransferFunction(array3, denominator); } public static TransferFunction FromCsv(Stream stream, char delimiter = ',') { using StreamReader streamReader = new StreamReader(stream); double[] numerator = (from s in streamReader.ReadLine().Split(new char[1] { delimiter }) select double.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture)).ToArray(); double[] denominator = (from s in streamReader.ReadLine().Split(new char[1] { delimiter }) select double.Parse(s, NumberStyles.Any, CultureInfo.InvariantCulture)).ToArray(); return new TransferFunction(numerator, denominator); } public void ToCsv(Stream stream, char delimiter = ',') { using StreamWriter streamWriter = new StreamWriter(stream); string value = string.Join(delimiter.ToString(), Numerator.Select((double k) => k.ToString(CultureInfo.InvariantCulture))); streamWriter.WriteLine(value); value = string.Join(delimiter.ToString(), Denominator.Select((double k) => k.ToString(CultureInfo.InvariantCulture))); streamWriter.WriteLine(value); } } public class ZiFilter : LtiFilter { protected readonly float[] _b; protected readonly float[] _a; protected readonly float[] _zi; protected TransferFunction _tf; public float[] Zi => _zi; public override TransferFunction Tf { get { return _tf ?? new TransferFunction(_b.ToDoubles(), _a.ToDoubles()); } protected set { _tf = value; } } public ZiFilter(IEnumerable b, IEnumerable a) { _b = b.ToArray(); _a = a.ToArray(); int num = _a.Length; if (_a.Length > _b.Length) { num = _a.Length; _b = _b.PadZeros(num); } else if (_a.Length < _b.Length) { num = _b.Length; _a = _a.PadZeros(num); } _zi = new float[num]; } public ZiFilter(IEnumerable b, IEnumerable a) : this(b.ToFloats(), a.ToFloats()) { } public ZiFilter(TransferFunction tf) : this(tf.Numerator, tf.Denominator) { Tf = tf; } public virtual void Init(float[] zi) { Array.Copy(zi, 0, _zi, 0, Math.Min(zi.Length, _zi.Length)); } public virtual void Init(double[] zi) { Array.Copy(zi.ToFloats(), 0, _zi, 0, Math.Min(zi.Length, _zi.Length)); } public override float Process(float sample) { float num = _b[0] * sample + _zi[0]; for (int i = 1; i < _zi.Length; i++) { _zi[i - 1] = _b[i] * sample - _a[i] * num + _zi[i]; } return num; } public DiscreteSignal ZeroPhase(DiscreteSignal signal, int padLength = 0) { if (padLength <= 0) { padLength = 3 * (Math.Max(_a.Length, _b.Length) - 1); } Guard.AgainstInvalidRange(padLength, signal.Length, "pad length", "Signal length"); float[] samples = signal.Samples; float[] array = new float[signal.Length]; float[] array2 = new float[padLength]; float[] array3 = new float[padLength]; double[] zi = Tf.Zi; double[] array4 = zi.FastCopy(); float num = 2f * samples[0] - samples[padLength]; int num2 = 0; while (num2 < array4.Length) { array4[num2++] *= num; } Init(array4); num = samples[0]; int num3 = 0; for (int num4 = padLength; num4 > 0; num4--) { array2[num3] = Process(2f * num - samples[num4]); num3++; } for (int i = 0; i < samples.Length; i++) { array[i] = Process(samples[i]); } num = samples.Last(); int num5 = 0; for (int num6 = samples.Length - 2; num6 > samples.Length - 2 - padLength; num6--) { array3[num5] = Process(2f * num - samples[num6]); num5++; } array4 = zi; num = array3.Last(); int num7 = 0; while (num7 < array4.Length) { array4[num7++] *= num; } Init(array4); for (int num8 = padLength - 1; num8 >= 0; num8--) { Process(array3[num8]); } for (int num9 = array.Length - 1; num9 >= 0; num9--) { array[num9] = Process(array[num9]); } for (int num10 = padLength - 1; num10 >= 0; num10--) { Process(array2[num10]); } return new DiscreteSignal(signal.SamplingRate, array); } public void ChangeNumeratorCoeffs(float[] b) { if (b.Length == _b.Length) { for (int i = 0; i < _b.Length; i++) { _b[i] = b[i]; } } } public void ChangeDenominatorCoeffs(float[] a) { if (a.Length == _a.Length) { for (int i = 0; i < _a.Length; i++) { _a[i] = a[i]; } } } public void Change(TransferFunction tf) { double[] numerator = tf.Numerator; if (numerator.Length == _b.Length) { for (int i = 0; i < _b.Length; i++) { _b[i] = (float)numerator[i]; } } double[] denominator = tf.Denominator; if (denominator.Length == _a.Length) { for (int j = 0; j < _a.Length; j++) { _a[j] = (float)denominator[j]; } } } public override void Reset() { Array.Clear(_zi, 0, _zi.Length); } public override DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } } namespace NWaves.Filters.Base64 { public class FilterChain64 : IFilter64, IOnlineFilter64 { private readonly List _filters; public FilterChain64(IEnumerable filters = null) { _filters = filters?.ToList() ?? new List(); } public FilterChain64(IEnumerable tfs) { _filters = new List(); foreach (TransferFunction tf in tfs) { _filters.Add(new IirFilter64(tf)); } } public void Add(IOnlineFilter64 filter) { _filters.Add(filter); } public void Insert(int index, IOnlineFilter64 filter) { _filters.Insert(index, filter); } public void RemoveAt(int index) { _filters.RemoveAt(index); } public double Process(double sample) { double num = sample; foreach (IOnlineFilter64 filter in _filters) { num = filter.Process(num); } return num; } public void Reset() { foreach (IOnlineFilter64 filter in _filters) { filter.Reset(); } } public double[] ApplyTo(double[] signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } public class FirFilter64 : LtiFilter64 { protected readonly double[] _b; protected int _kernelSize; protected TransferFunction _tf; protected double[] _delayLine; protected int _delayLineOffset; public double[] Kernel => _b.Take(_kernelSize).ToArray(); public override TransferFunction Tf { get { return _tf ?? new TransferFunction(_b.Take(_kernelSize).ToArray()); } protected set { _tf = value; } } public int KernelSizeForBlockConvolution { get; set; } = 64; public FirFilter64(IEnumerable kernel) { _kernelSize = kernel.Count(); _b = new double[_kernelSize * 2]; for (int i = 0; i < _kernelSize; i++) { _b[i] = (_b[_kernelSize + i] = kernel.ElementAt(i)); } _delayLine = new double[_kernelSize]; _delayLineOffset = _kernelSize - 1; } public FirFilter64(TransferFunction tf) : this(tf.Numerator) { Tf = tf; } public override double[] ApplyTo(double[] signal, FilteringMethod method = FilteringMethod.Auto) { if (_kernelSize >= KernelSizeForBlockConvolution && method == FilteringMethod.Auto) { method = FilteringMethod.OverlapSave; } switch (method) { case FilteringMethod.OverlapAdd: { int fftSize2 = MathUtils.NextPowerOfTwo(4 * _kernelSize); return OlaBlockConvolver64.FromFilter(this, fftSize2).ApplyTo(signal); } case FilteringMethod.OverlapSave: { int fftSize = MathUtils.NextPowerOfTwo(4 * _kernelSize); return OlsBlockConvolver64.FromFilter(this, fftSize).ApplyTo(signal); } default: return ProcessAllSamples(signal); } } public override double Process(double sample) { _delayLine[_delayLineOffset] = sample; double num = 0.0; int num2 = 0; int num3 = _kernelSize - _delayLineOffset; while (num2 < _kernelSize) { num += _delayLine[num2] * _b[num3]; num2++; num3++; } if (--_delayLineOffset < 0) { _delayLineOffset = _kernelSize - 1; } return num; } public double[] ProcessAllSamples(double[] samples) { double[] array = new double[samples.Length + _kernelSize - 1]; int num = 0; while (num < samples.Length) { _delayLine[_delayLineOffset] = samples[num]; double num2 = 0.0; int num3 = 0; int num4 = _kernelSize - _delayLineOffset; while (num3 < _kernelSize) { num2 += _delayLine[num3] * _b[num4]; num3++; num4++; } if (--_delayLineOffset < 0) { _delayLineOffset = _kernelSize - 1; } array[num++] = num2; } while (num < array.Length) { array[num++] = Process(0.0); } return array; } public void ChangeKernel(double[] kernel) { if (kernel.Length == _kernelSize) { for (int i = 0; i < _kernelSize; i++) { _b[i] = (_b[_kernelSize + i] = kernel[i]); } } } public override void Reset() { _delayLineOffset = _kernelSize - 1; Array.Clear(_delayLine, 0, _kernelSize); } } public interface IFilter64 { double[] ApplyTo(double[] signal, FilteringMethod method = FilteringMethod.Auto); } public class IirFilter64 : LtiFilter64 { protected readonly double[] _b; protected readonly double[] _a; protected readonly int _numeratorSize; protected readonly int _denominatorSize; protected TransferFunction _tf; protected double[] _delayLineA; protected double[] _delayLineB; protected int _delayLineOffsetA; protected int _delayLineOffsetB; public override TransferFunction Tf { get { return _tf ?? new TransferFunction(_b.Take(_numeratorSize).ToArray(), _a.ToArray()); } protected set { _tf = value; } } public int DefaultImpulseResponseLength { get; set; } = 512; public IirFilter64(IEnumerable b, IEnumerable a) { _numeratorSize = b.Count(); _denominatorSize = a.Count(); _b = new double[_numeratorSize * 2]; for (int i = 0; i < _numeratorSize; i++) { _b[i] = (_b[_numeratorSize + i] = b.ElementAt(i)); } _a = a.ToArray(); _delayLineB = new double[_numeratorSize]; _delayLineA = new double[_denominatorSize]; _delayLineOffsetB = _numeratorSize - 1; _delayLineOffsetA = _denominatorSize - 1; } public IirFilter64(TransferFunction tf) : this(tf.Numerator, tf.Denominator) { Tf = tf; } public override double[] ApplyTo(double[] signal, FilteringMethod method = FilteringMethod.Auto) { if ((uint)(method - 2) <= 1u) { int num = Math.Max(DefaultImpulseResponseLength, _denominatorSize + _numeratorSize); int fftSize = MathUtils.NextPowerOfTwo(4 * num); return new OlsBlockConvolver64(Tf.ImpulseResponse(num), fftSize).ApplyTo(signal); } return this.FilterOnline(signal); } public override double Process(double sample) { double num = 0.0; _delayLineB[_delayLineOffsetB] = sample; int num2 = 0; int num3 = _numeratorSize - _delayLineOffsetB; while (num2 < _numeratorSize) { num += _delayLineB[num2] * _b[num3]; num2++; num3++; } int num4 = 1; for (int i = _delayLineOffsetA + 1; i < _a.Length; i++) { num -= _a[num4++] * _delayLineA[i]; } for (int j = 0; j < _delayLineOffsetA; j++) { num -= _a[num4++] * _delayLineA[j]; } _delayLineA[_delayLineOffsetA] = num; if (--_delayLineOffsetB < 0) { _delayLineOffsetB = _numeratorSize - 1; } if (--_delayLineOffsetA < 0) { _delayLineOffsetA = _denominatorSize - 1; } return num; } public void ChangeNumeratorCoeffs(double[] b) { if (b.Length == _numeratorSize) { for (int i = 0; i < _numeratorSize; i++) { _b[i] = (_b[_numeratorSize + i] = b[i]); } } } public void ChangeDenominatorCoeffs(double[] a) { if (a.Length == _denominatorSize) { for (int i = 0; i < _denominatorSize; i++) { _a[i] = a[i]; } } } public override void Reset() { _delayLineOffsetB = _numeratorSize - 1; _delayLineOffsetA = _denominatorSize - 1; int num = 0; while (num < _delayLineB.Length) { _delayLineB[num++] = 0.0; } int num2 = 0; while (num2 < _delayLineA.Length) { _delayLineA[num2++] = 0.0; } } public void Normalize() { double num = _a[0]; if (!(Math.Abs(num - 1.0) < 1E-10)) { if (Math.Abs(num) < 1E-30) { throw new ArgumentException("The coefficient a[0] can not be zero!"); } int num2 = 0; while (num2 < _a.Length) { _a[num2++] /= num; } int num3 = 0; while (num3 < _b.Length) { _b[num3++] /= num; } _tf?.Normalize(); } } } public interface IOnlineFilter64 { double Process(double sample); void Reset(); } public abstract class LtiFilter64 : IFilter64, IOnlineFilter64 { public abstract TransferFunction Tf { get; protected set; } public abstract double Process(double sample); public abstract void Reset(); public abstract double[] ApplyTo(double[] signal, FilteringMethod method = FilteringMethod.Auto); } public class StereoFilter64 : IFilter64, IOnlineFilter64 { private readonly IOnlineFilter64 _filterLeft; private readonly IOnlineFilter64 _filterRight; private bool _isRight; public StereoFilter64(IOnlineFilter64 filterLeft, IOnlineFilter64 filterRight) { _filterLeft = filterLeft; _filterRight = filterRight; } public double Process(double sample) { if (_isRight) { _isRight = false; return _filterRight.Process(sample); } _isRight = true; return _filterLeft.Process(sample); } public void Reset() { _filterLeft.Reset(); _filterRight.Reset(); } public double[] ApplyTo(double[] signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } public class ZiFilter64 : LtiFilter64 { protected readonly double[] _b; protected readonly double[] _a; protected readonly double[] _zi; protected TransferFunction _tf; public double[] Zi => _zi; public override TransferFunction Tf { get { return _tf ?? new TransferFunction(_b, _a); } protected set { _tf = value; } } public ZiFilter64(IEnumerable b, IEnumerable a) { _b = b.ToArray(); _a = a.ToArray(); int num = _a.Length; if (_a.Length > _b.Length) { num = _a.Length; _b = _b.PadZeros(num); } else if (_a.Length < _b.Length) { num = _b.Length; _a = _a.PadZeros(num); } _zi = new double[num]; } public ZiFilter64(TransferFunction tf) : this(tf.Numerator, tf.Denominator) { Tf = tf; } public virtual void Init(double[] zi) { Array.Copy(zi, 0, _zi, 0, Math.Min(zi.Length, _zi.Length)); } public override double Process(double sample) { double num = _b[0] * sample + _zi[0]; for (int i = 1; i < _zi.Length; i++) { _zi[i - 1] = _b[i] * sample - _a[i] * num + _zi[i]; } return num; } public double[] ZeroPhase(double[] signal, int padLength = 0) { if (padLength <= 0) { padLength = 3 * (Math.Max(_a.Length, _b.Length) - 1); } Guard.AgainstInvalidRange(padLength, signal.Length, "pad length", "Signal length"); double[] array = new double[signal.Length]; double[] array2 = new double[padLength]; double[] array3 = new double[padLength]; double[] zi = Tf.Zi; double[] array4 = zi.FastCopy(); double num = 2.0 * signal[0] - signal[padLength]; int num2 = 0; while (num2 < array4.Length) { array4[num2++] *= num; } Init(array4); num = signal[0]; int num3 = 0; for (int num4 = padLength; num4 > 0; num4--) { array2[num3] = Process(2.0 * num - signal[num4]); num3++; } for (int i = 0; i < signal.Length; i++) { array[i] = Process(signal[i]); } num = signal.Last(); int num5 = 0; for (int num6 = signal.Length - 2; num6 > signal.Length - 2 - padLength; num6--) { array3[num5] = Process(2.0 * num - signal[num6]); num5++; } array4 = zi; num = array3.Last(); int num7 = 0; while (num7 < array4.Length) { array4[num7++] *= num; } Init(array4); for (int num8 = padLength - 1; num8 >= 0; num8--) { Process(array3[num8]); } for (int num9 = array.Length - 1; num9 >= 0; num9--) { array[num9] = Process(array[num9]); } for (int num10 = padLength - 1; num10 >= 0; num10--) { Process(array2[num10]); } return array; } public void ChangeNumeratorCoeffs(double[] b) { if (b.Length == _b.Length) { for (int i = 0; i < _b.Length; i++) { _b[i] = b[i]; } } } public void ChangeDenominatorCoeffs(double[] a) { if (a.Length == _a.Length) { for (int i = 0; i < _a.Length; i++) { _a[i] = a[i]; } } } public void Change(TransferFunction tf) { double[] numerator = tf.Numerator; if (numerator.Length == _b.Length) { for (int i = 0; i < _b.Length; i++) { _b[i] = numerator[i]; } } double[] denominator = tf.Denominator; if (denominator.Length == _a.Length) { for (int j = 0; j < _a.Length; j++) { _a[j] = denominator[j]; } } } public override void Reset() { Array.Clear(_zi, 0, _zi.Length); } public override double[] ApplyTo(double[] signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } } namespace NWaves.Filters.Adaptive { public abstract class AdaptiveFilter : FirFilter { public AdaptiveFilter(int order) : base(new float[order]) { Array.Resize(ref _delayLine, _kernelSize * 2); } public void Init(float[] weights) { Guard.AgainstInequality(_kernelSize, weights.Length, "Filter order", "Weights array size"); ChangeKernel(weights); } public abstract float Process(float input, float desired); } public class LmfFilter : AdaptiveFilter { private readonly float _mu; private readonly float _leakage; public LmfFilter(int order, float mu = 0.75f, float leakage = 0f) : base(order) { _mu = mu; _leakage = leakage; } public override float Process(float input, float desired) { int num = _delayLineOffset; _delayLine[num + _kernelSize] = input; float num2 = Process(input); float num3 = desired - num2; int num4 = 0; while (num4 < _kernelSize) { _b[num4] = (_b[_kernelSize + num4] = (1f - _leakage * _mu) * _b[num4] + 4f * _mu * num3 * num3 * num3 * _delayLine[num]); num4++; num++; } return num2; } } public class LmsFilter : AdaptiveFilter { private readonly float _mu; private readonly float _leakage; public LmsFilter(int order, float mu = 0.75f, float leakage = 0f) : base(order) { _mu = mu; _leakage = leakage; } public override float Process(float input, float desired) { int num = _delayLineOffset; _delayLine[num + _kernelSize] = input; float num2 = Process(input); float num3 = desired - num2; int num4 = 0; while (num4 < _kernelSize) { _b[num4] = (_b[_kernelSize + num4] = (1f - _leakage * _mu) * _b[num4] + _mu * num3 * _delayLine[num]); num4++; num++; } return num2; } } public class NlmfFilter : AdaptiveFilter { private readonly float _mu; private readonly float _eps; private readonly float _leakage; public NlmfFilter(int order, float mu = 0.75f, float eps = 1f, float leakage = 0f) : base(order) { _mu = mu; _eps = eps; _leakage = leakage; } public override float Process(float input, float desired) { int num = _delayLineOffset; _delayLine[num + _kernelSize] = input; float num2 = Process(input); float num3 = desired - num2; float num4 = _eps + _delayLine.Sum((float x) => x * x); int num5 = 0; while (num5 < _kernelSize) { _b[num5] = (_b[_kernelSize + num5] = (1f - _leakage * _mu) * _b[num5] + 4f * _mu * num3 * num3 * num3 * _delayLine[num] / num4); num5++; num++; } return num2; } } public class NlmsFilter : AdaptiveFilter { private readonly float _mu; private readonly float _eps; private readonly float _leakage; public NlmsFilter(int order, float mu = 0.75f, float eps = 1f, float leakage = 0f) : base(order) { _mu = mu; _eps = eps; _leakage = leakage; } public override float Process(float input, float desired) { int num = _delayLineOffset; _delayLine[num + _kernelSize] = input; float num2 = Process(input); float num3 = desired - num2; float num4 = _eps + _delayLine.Sum((float x) => x * x); int num5 = 0; while (num5 < _kernelSize) { _b[num5] = (_b[_kernelSize + num5] = (1f - _leakage * _mu) * _b[num5] + _mu * num3 * _delayLine[num] / num4); num5++; num++; } return num2; } } public class RlsFilter : AdaptiveFilter { private readonly float _lambda; private readonly float[,] _p; private readonly float[] _gains; private readonly float[,] _dp; private readonly float[,] _tmp; public RlsFilter(int order, float lambda = 0.99f, float initCorrMatrix = 100f) : base(order) { _lambda = lambda; _p = new float[_kernelSize, _kernelSize]; for (int i = 0; i < _kernelSize; i++) { _p[i, i] = initCorrMatrix; } _gains = new float[_kernelSize]; _dp = new float[_kernelSize, _kernelSize]; _tmp = new float[_kernelSize, _kernelSize]; } public override float Process(float input, float desired) { int delayLineOffset = _delayLineOffset; _delayLine[delayLineOffset + _kernelSize] = input; float num = Process(input); float num2 = desired - num; for (int i = 0; i < _kernelSize; i++) { _gains[i] = 0f; } float num3 = _lambda; for (int j = 0; j < _kernelSize; j++) { int num4 = 0; int num5 = delayLineOffset; while (num4 < _kernelSize) { _gains[j] += _p[j, num4] * _delayLine[num5]; num4++; num5++; } } int num6 = 0; int num7 = delayLineOffset; while (num6 < _kernelSize) { num3 += _gains[num6] * _delayLine[num7]; num6++; num7++; } for (int k = 0; k < _kernelSize; k++) { _gains[k] /= num3; } for (int l = 0; l < _kernelSize; l++) { int num8 = 0; int num9 = delayLineOffset; while (num8 < _kernelSize) { _tmp[l, num8] = _gains[l] * _delayLine[num9]; num8++; num9++; } } for (int m = 0; m < _kernelSize; m++) { for (int n = 0; n < _kernelSize; n++) { for (int num10 = 0; num10 < _kernelSize; num10++) { _dp[m, n] = _tmp[m, num10] * _p[num10, n]; } } } for (int num11 = 0; num11 < _kernelSize; num11++) { for (int num12 = 0; num12 < _kernelSize; num12++) { _p[num11, num12] = (_p[num11, num12] - _dp[num11, num12]) / _lambda; } } for (int num13 = 0; num13 < _kernelSize; num13++) { _b[num13] = (_b[_kernelSize + num13] = _b[num13] + _gains[num13] * num2); } return num; } } public class SignLmsFilter : AdaptiveFilter { private readonly float _mu; private readonly float _leakage; public SignLmsFilter(int order, float mu = 0.75f, float leakage = 0f) : base(order) { _mu = mu; _leakage = leakage; } public override float Process(float input, float desired) { int num = _delayLineOffset; _delayLine[num + _kernelSize] = input; float num2 = Process(input); float value = desired - num2; int num3 = 0; while (num3 < _kernelSize) { _b[num3] = (_b[_kernelSize + num3] = (1f - _leakage * _mu) * _b[num3] + _mu * (float)Math.Sign(value) * (float)Math.Sign(_delayLine[num])); num3++; num++; } return num2; } } public class VariableStepLmsFilter : AdaptiveFilter { private readonly float[] _mu; private readonly float _leakage; public VariableStepLmsFilter(int order, float[] mu = null, float leakage = 0f) : base(order) { _mu = mu ?? Enumerable.Repeat(0.75f, order).ToArray(); Guard.AgainstInequality(order, _mu.Length, "Filter order", "Steps array size"); _leakage = leakage; } public override float Process(float input, float desired) { int num = _delayLineOffset; _delayLine[num + _kernelSize] = input; float num2 = Process(input); float num3 = desired - num2; int num4 = 0; while (num4 < _kernelSize) { _b[num4] = (_b[_kernelSize + num4] = (1f - _leakage * _mu[num4]) * _b[num4] + _mu[num4] * num3 * _delayLine[num]); num4++; num++; } return num2; } } } namespace NWaves.Features { public static class Harmonic { public static void Peaks(float[] spectrum, int[] peaks, float[] peakFrequencies, int samplingRate, float pitch = -1f) { if (pitch < 0f) { pitch = Pitch.FromSpectralPeaks(spectrum, samplingRate); } float num = (float)samplingRate / (float)(2 * (spectrum.Length - 1)); int num2 = (int)(pitch / (2f * num)); peaks[0] = (int)(pitch / num); peakFrequencies[0] = pitch; for (int i = 0; i < peaks.Length; i++) { int num3 = (i + 1) * peaks[0]; if (num3 >= spectrum.Length) { peaks[i] = spectrum.Length - 1; peakFrequencies[i] = num * (float)(spectrum.Length - 1); continue; } int num4 = num3; for (int j = -num2; j < num2; j++) { if (num4 + j - 1 > 0 && num4 + j + 1 < spectrum.Length && spectrum[num4 + j] > spectrum[num4 + j - 1] && spectrum[num4 + j] > spectrum[num4 + j + 1] && spectrum[num4 + j] > spectrum[num3]) { num3 = num4 + j; } } peaks[i] = num3; peakFrequencies[i] = num * (float)num3; } } public static float Centroid(float[] spectrum, int[] peaks, float[] peakFrequencies) { if (peaks[0] == 0) { return 0f; } float num = 1E-10f; float num2 = 0f; for (int i = 0; i < peaks.Length; i++) { int num3 = peaks[i]; num += spectrum[num3]; num2 += peakFrequencies[i] * spectrum[num3]; } return num2 / num; } public static float Spread(float[] spectrum, int[] peaks, float[] peakFrequencies) { if (peaks[0] == 0) { return 0f; } float num = Centroid(spectrum, peaks, peakFrequencies); float num2 = 1E-10f; float num3 = 0f; for (int i = 0; i < peaks.Length; i++) { int num4 = peaks[i]; num2 += spectrum[num4]; num3 += spectrum[num4] * (peakFrequencies[i] - num) * (peakFrequencies[i] - num); } return (float)Math.Sqrt(num3 / num2); } public static float Inharmonicity(float[] spectrum, int[] peaks, float[] peakFrequencies) { if (peaks[0] == 0) { return 0f; } float num = peakFrequencies[0]; float num2 = 1E-10f; float num3 = 0f; for (int i = 0; i < peaks.Length; i++) { int num4 = peaks[i]; float num5 = spectrum[num4] * spectrum[num4]; num3 += (peakFrequencies[i] - (float)(i + 1) * num) * num5; num2 += num5; } return 2f * num3 / (num * num2); } public static float OddToEvenRatio(float[] spectrum, int[] peaks) { if (peaks[0] == 0) { return 0f; } float num = 1E-10f; float num2 = 1E-10f; for (int i = 0; i < peaks.Length; i += 2) { num2 += spectrum[peaks[i]]; } for (int j = 1; j < peaks.Length; j += 2) { num += spectrum[peaks[j]]; } return num / num2; } public static float Tristimulus(float[] spectrum, int[] peaks, int n) { if (peaks[0] == 0) { return 0f; } float num = 1E-10f; for (int i = 0; i < peaks.Length; i++) { num += spectrum[peaks[i]]; } return n switch { 1 => spectrum[peaks[0]] / num, 2 => (spectrum[peaks[1]] + spectrum[peaks[2]] + spectrum[peaks[3]]) / num, _ => (num - spectrum[peaks[0]] - spectrum[peaks[1]] - spectrum[peaks[2]] - spectrum[peaks[3]]) / num, }; } } public static class Perceptual { public static float Loudness(float[] spectralBands) { double num = 0.0; for (int i = 0; i < spectralBands.Length; i++) { num += Math.Pow(spectralBands[i], 0.23); } return (float)num; } public static float Sharpness(float[] spectralBands) { double num = 0.0; double num2 = 0.0; for (int i = 0; i < spectralBands.Length; i++) { double num3 = Math.Pow(spectralBands[i], 0.23); num += (double)(i + 1) * num3; num2 += num3; } return (float)(num / num2 * 0.11); } } public static class Pitch { public static float FromAutoCorrelation(float[] samples, int samplingRate, int startPos = 0, int endPos = -1, float low = 80f, float high = 400f) { if (endPos == -1) { endPos = samples.Length; } int num = (int)(1.0 * (double)samplingRate / (double)high); int num2 = (int)(1.0 * (double)samplingRate / (double)low); float[] samples2 = new DiscreteSignal(samplingRate, samples)[startPos, endPos].Samples; int num3 = MathUtils.NextPowerOfTwo(2 * samples2.Length - 1); float[] array = new float[num3]; new Convolver(num3).CrossCorrelate(samples2, samples2.FastCopy(), array); int num4 = num + samples2.Length - 1; int num5 = Math.Min(num4 + num2, array.Length); float num6 = ((num4 < array.Length) ? array[num4] : 0f); int num7 = num4; for (int i = num4; i < num5; i++) { if (array[i] > num6) { num6 = array[i]; num7 = i - samples2.Length + 1; } } if (!(num6 > 1f)) { return 0f; } return (float)samplingRate / (float)num7; } public static float FromAutoCorrelation(DiscreteSignal signal, int startPos = 0, int endPos = -1, float low = 80f, float high = 400f) { return FromAutoCorrelation(signal.Samples, signal.SamplingRate, startPos, endPos, low, high); } public static float FromZeroCrossingsSchmitt(float[] samples, int samplingRate, int startPos = 0, int endPos = -1, float lowSchmittThreshold = -1E+10f, float highSchmittThreshold = 1E+10f) { if (endPos == -1) { endPos = samples.Length; } float num = 0f; float num2 = 0f; for (int i = startPos; i < endPos; i++) { if (samples[i] > 0f && samples[i] > num) { num = samples[i]; } if (samples[i] < 0f && samples[i] < num2) { num2 = samples[i]; } } float num3 = ((highSchmittThreshold < 1E+09f) ? highSchmittThreshold : (0.75f * num)); float num4 = ((lowSchmittThreshold > -1E+09f) ? lowSchmittThreshold : (0.75f * num2)); int num5 = 0; int num6 = endPos; int num7 = startPos; bool flag = false; int j; for (j = startPos; j < endPos - 1; j++) { if (samples[j] < num3 && samples[j + 1] >= num3 && !flag) { flag = true; num6 = j; break; } if (samples[j] > num4 && samples[j + 1] <= num4 && flag) { flag = false; num6 = j; break; } } for (; j < endPos - 1; j++) { if (samples[j] < num3 && samples[j + 1] >= num3 && !flag) { num5++; flag = true; num7 = j; } if (samples[j] > num4 && samples[j + 1] <= num4 && flag) { num5++; flag = false; num7 = j; } } if (num5 <= 0 || num7 <= num6) { return 0f; } return (float)num5 * (float)samplingRate / 2f / (float)(num7 - num6); } public static float FromZeroCrossingsSchmitt(DiscreteSignal signal, int startPos = 0, int endPos = -1, float lowSchmittThreshold = -1E+10f, float highSchmittThreshold = 1E+10f) { return FromZeroCrossingsSchmitt(signal.Samples, signal.SamplingRate, startPos, endPos, lowSchmittThreshold, highSchmittThreshold); } public static float FromYin(float[] samples, int samplingRate, int startPos = 0, int endPos = -1, float low = 80f, float high = 400f, float cmdfThreshold = 0.2f) { if (endPos == -1) { endPos = samples.Length; } int num = (int)(1.0 * (double)samplingRate / (double)high); int num2 = (int)(1.0 * (double)samplingRate / (double)low); int num3 = (endPos - startPos) / 2; float[] array = new float[num3]; for (int i = 0; i < num3; i++) { for (int j = 0; j < num3; j++) { float num4 = samples[j + startPos] - samples[i + j + startPos]; array[i] += num4 * num4; } } array[0] = 1f; float num5 = 0f; for (int k = 1; k < num3; k++) { num5 += array[k]; array[k] *= (float)k / num5; } int l; for (l = num; l < num2; l++) { if (array[l] < cmdfThreshold) { for (; l + 1 < num2 && array[l + 1] < array[l]; l++) { } break; } } if (l == num2 || array[l] >= cmdfThreshold) { return 0f; } int num6 = ((l < 1) ? l : (l - 1)); int num7 = ((l + 1 < num3) ? (l + 1) : l); if (l == num6) { if (array[l] > array[num7]) { l = num7; } } else if (l == num7) { if (array[l] > array[num6]) { l = num6; } } else { l = (int)((float)l + (array[num7] - array[num6]) / (2f * array[l] - array[num7] - array[num6]) / 2f); } return samplingRate / l; } public static float FromYin(DiscreteSignal signal, int startPos = 0, int endPos = -1, float low = 80f, float high = 400f, float cmdfThreshold = 0.2f) { return FromYin(signal.Samples, signal.SamplingRate, startPos, endPos, low, high, cmdfThreshold); } public static float FromHss(DiscreteSignal signal, int startPos = 0, int endPos = -1, float low = 80f, float high = 400f, int fftSize = 0) { if (endPos == -1) { endPos = signal.Length; } if (startPos != 0 || endPos != signal.Length) { signal = signal[startPos, endPos]; } signal.ApplyWindow(WindowType.Hann); return FromHss(new RealFft((fftSize > 0) ? fftSize : MathUtils.NextPowerOfTwo(signal.Length)).PowerSpectrum(signal, normalize: false).Samples, signal.SamplingRate, low, high); } public static float FromHss(float[] spectrum, int samplingRate, float low = 80f, float high = 400f) { float[] array = spectrum.FastCopy(); int num = (spectrum.Length - 1) * 2; int num2 = (int)(low * (float)num / (float)samplingRate) + 1; int num3 = (int)(high * (float)num / (float)samplingRate) + 1; int num4 = Math.Min(spectrum.Length / num3, 10); int num5 = 0; float num6 = 0f; for (int i = num2; i < num3; i++) { array[i] *= 1.5f; for (int j = 2; j < num4; j++) { array[i] += (spectrum[i * j - 1] + spectrum[i * j] + spectrum[i * j + 1]) / 3f; } if (array[i] > num6) { num6 = array[i]; num5 = i; } } return (float)num5 * (float)samplingRate / (float)num; } public static float FromHps(DiscreteSignal signal, int startPos = 0, int endPos = -1, float low = 80f, float high = 400f, int fftSize = 0) { if (endPos == -1) { endPos = signal.Length; } if (startPos != 0 || endPos != signal.Length) { signal = signal[startPos, endPos]; } signal.ApplyWindow(WindowType.Hann); return FromHps(new RealFft((fftSize > 0) ? fftSize : MathUtils.NextPowerOfTwo(signal.Length)).PowerSpectrum(signal, normalize: false).Samples, signal.SamplingRate, low, high); } public static float FromHps(float[] spectrum, int samplingRate, float low = 80f, float high = 400f) { float[] array = spectrum.FastCopy(); int num = (spectrum.Length - 1) * 2; int num2 = (int)(low * (float)num / (float)samplingRate) + 1; int num3 = (int)(high * (float)num / (float)samplingRate) + 1; int num4 = Math.Min(spectrum.Length / num3, 10); int num5 = 0; float num6 = 0f; for (int i = num2; i < num3; i++) { for (int j = 2; j < num4; j++) { array[i] *= (spectrum[i * j - 1] + spectrum[i * j] + spectrum[i * j + 1]) / 3f; } if (array[i] > num6) { num6 = array[i]; num5 = i; } } return (float)num5 * (float)samplingRate / (float)num; } public static float FromSpectralPeaks(DiscreteSignal signal, int startPos = 0, int endPos = -1, float low = 80f, float high = 400f, int fftSize = 0) { if (endPos == -1) { endPos = signal.Length; } if (startPos != 0 || endPos != signal.Length) { signal = signal[startPos, endPos]; } signal.ApplyWindow(WindowType.Hann); return FromSpectralPeaks(new RealFft((fftSize > 0) ? fftSize : MathUtils.NextPowerOfTwo(signal.Length)).PowerSpectrum(signal, normalize: false).Samples, signal.SamplingRate, low, high); } public static float FromSpectralPeaks(float[] spectrum, int samplingRate, float low = 80f, float high = 400f) { int num = (spectrum.Length - 1) * 2; int num2 = (int)(low * (float)num / (float)samplingRate) + 1; int num3 = (int)(high * (float)num / (float)samplingRate) + 1; for (int i = num2 + 1; i < num3; i++) { if (spectrum[i] > spectrum[i - 1] && spectrum[i] > spectrum[i - 2] && spectrum[i] > spectrum[i + 1] && spectrum[i] > spectrum[i + 2]) { return (float)i * (float)samplingRate / (float)num; } } return (float)num2 * (float)samplingRate / (float)num; } public static float FromCepstrum(DiscreteSignal signal, int startPos = 0, int endPos = -1, float low = 80f, float high = 400f, int cepstrumSize = 256, int fftSize = 1024) { int samplingRate = signal.SamplingRate; if (endPos == -1) { endPos = signal.Length; } if (startPos != 0 || endPos != signal.Length) { signal = signal[startPos, endPos]; } int num = (int)(1.0 * (double)samplingRate / (double)high); int num2 = Math.Min(cepstrumSize - 1, (int)(1.0 * (double)samplingRate / (double)low)); float[] array = new float[cepstrumSize]; new CepstralTransform(cepstrumSize, fftSize).RealCepstrum(signal.Samples, array); float num3 = array[num]; int num4 = num; for (int i = num + 1; i <= num2; i++) { if (array[i] > num3) { num3 = array[i]; num4 = i; } } return (float)samplingRate / (float)num4; } } public static class Spectral { public static float Centroid(float[] spectrum, float[] frequencies) { float num = 1E-10f; float num2 = 0f; for (int i = 1; i < spectrum.Length; i++) { num += spectrum[i]; num2 += frequencies[i] * spectrum[i]; } return num2 / num; } public static float Spread(float[] spectrum, float[] frequencies) { float num = Centroid(spectrum, frequencies); float num2 = 1E-10f; float num3 = 0f; for (int i = 1; i < spectrum.Length; i++) { num2 += spectrum[i]; num3 += spectrum[i] * (frequencies[i] - num) * (frequencies[i] - num); } return (float)Math.Sqrt(num3 / num2); } public static float Decrease(float[] spectrum) { float num = 1E-10f; float num2 = 0f; for (int i = 2; i < spectrum.Length; i++) { num += spectrum[i]; num2 += (spectrum[i] - spectrum[1]) / (float)(i - 1); } return num2 / num; } public static float Flatness(float[] spectrum, float minLevel = 1E-10f) { float num = 0f; double num2 = 0.0; for (int i = 1; i < spectrum.Length; i++) { float num3 = Math.Max(spectrum[i], minLevel); num += num3; num2 += Math.Log(num3); } num /= (float)spectrum.Length; num2 /= (double)spectrum.Length; if (!((double)num > 1E-10)) { return 0f; } return (float)Math.Exp(num2) / num; } public static float Noiseness(float[] spectrum, float[] frequencies, float noiseFrequency = 3000f) { float num = 0f; float num2 = 1E-10f; int i; for (i = 1; i < spectrum.Length && frequencies[i] < noiseFrequency; i++) { num2 += spectrum[i]; } for (; i < spectrum.Length; i++) { num += spectrum[i]; num2 += spectrum[i]; } return num / num2; } public static float Rolloff(float[] spectrum, float[] frequencies, float rolloffPercent = 0.85f) { float num = 0f; for (int i = 1; i < spectrum.Length; i++) { num += spectrum[i]; } num *= rolloffPercent; float num2 = 0f; int num3 = 0; for (int j = 1; j < spectrum.Length; j++) { num2 += spectrum[j]; if (num2 > num) { num3 = j; break; } } return frequencies[num3]; } public static float Crest(float[] spectrum) { float num = 0f; float num2 = 0f; for (int i = 1; i < spectrum.Length; i++) { float num3 = spectrum[i] * spectrum[i]; num += num3; if (num3 > num2) { num2 = num3; } } if (!((double)num > 1E-10)) { return 1f; } return (float)spectrum.Length * num2 / num; } public static float[] Contrast(float[] spectrum, float[] frequencies, float minFrequency = 200f, int bandCount = 6) { float[] array = new float[bandCount]; float octaveLow = minFrequency; float octaveHigh = 2f * octaveLow; for (int i = 0; i < bandCount; i++) { float[] array2 = (from s in spectrum.Where((float s, int num5) => frequencies[num5] >= octaveLow && frequencies[num5] <= octaveHigh) orderby s select s).ToArray(); if (array2.Length == 0) { return array; } double num = Math.Max(0.02 * (double)array2.Length, 1.0); double num2 = 0.0; double num3 = 0.0; for (int num4 = 0; (double)num4 < num; num4++) { num3 += (double)array2[num4]; num2 += (double)array2[array2.Length - num4 - 1]; } num2 /= num; num3 /= num; array[i] = (float)Math.Log10(num2 / num3); octaveLow *= 2f; octaveHigh *= 2f; } return array; } public static float Contrast(float[] spectrum, float[] frequencies, int bandNo, float minFrequency = 200f) { double octaveLow = (double)minFrequency * Math.Pow(2.0, bandNo - 1); double octaveHigh = 2.0 * octaveLow; float[] array = (from s in spectrum.Where((float s, int i) => (double)frequencies[i] >= octaveLow && (double)frequencies[i] <= octaveHigh) orderby s select s).ToArray(); if (array.Length == 0) { return 0f; } double num = Math.Max(0.02 * (double)array.Length, 1.0); double num2 = 0.0; double num3 = 0.0; for (int num4 = 0; (double)num4 < num; num4++) { num3 += (double)array[num4]; num2 += (double)array[array.Length - num4 - 1]; } num2 /= num; num3 /= num; return (float)Math.Log10(num2 / num3); } public static float Entropy(float[] spectrum) { double num = 0.0; float num2 = spectrum.Sum(); if ((double)num2 < 1E-08) { return 0f; } for (int i = 1; i < spectrum.Length; i++) { float num3 = spectrum[i] / num2; if ((double)num3 > 1E-08) { num += (double)num3 * Math.Log(num3, 2.0); } } return (float)((0.0 - num) / Math.Log(spectrum.Length, 2.0)); } } } namespace NWaves.FeatureExtractors { public class AmsExtractor : FeatureExtractor { protected readonly float[][] _featuregram; protected readonly float[][] _filterbank; protected float[][] _envelopes; protected readonly int _fftSize; protected readonly RealFft _fft; protected readonly RealFft _modulationFft; protected readonly int _modulationFftSize; protected readonly int _modulationHopSize; protected readonly float[] _block; protected readonly float[] _spectrum; protected readonly float[] _filteredSpectrum; protected readonly float[] _modBlock; protected readonly float[] _modSpectrum; public override List FeatureDescriptions { get; } public float[][] Filterbank => _filterbank; public float[][] Envelopes => _envelopes; public AmsExtractor(AmsOptions options) : base(options) { _modulationFftSize = options.ModulationFftSize; _modulationHopSize = options.ModulationHopSize; _modulationFft = new RealFft(_modulationFftSize); _featuregram = options.Featuregram?.ToArray(); if (_featuregram != null) { base.FeatureCount = _featuregram[0].Length * (_modulationFftSize / 2 + 1); } else { if (options.FilterBank == null) { _fftSize = ((options.FftSize > base.FrameSize) ? options.FftSize : MathUtils.NextPowerOfTwo(base.FrameSize)); _filterbank = FilterBanks.Triangular(_fftSize, base.SamplingRate, FilterBanks.MelBands(12, base.SamplingRate, 100.0, 3200.0)); } else { _filterbank = options.FilterBank; _fftSize = 2 * (_filterbank[0].Length - 1); Guard.AgainstExceedance(base.FrameSize, _fftSize, "frame size", "FFT size"); } _fft = new RealFft(_fftSize); base.FeatureCount = _filterbank.Length * (_modulationFftSize / 2 + 1); _spectrum = new float[_fftSize / 2 + 1]; _filteredSpectrum = new float[_filterbank.Length]; _block = new float[_fftSize]; } _modBlock = new float[_modulationFftSize]; _modSpectrum = new float[_modulationFftSize / 2 + 1]; int num = ((_featuregram == null) ? _filterbank.Length : _featuregram[0].Length); FeatureDescriptions = new List(); float num2 = (float)base.SamplingRate / (float)base.HopSize / (float)_modulationFftSize; for (int i = 0; i < num; i++) { for (int j = 0; j <= _modulationFftSize / 2; j++) { FeatureDescriptions.Add($"band_{i + 1}_mf_{(float)j * num2:F2}_Hz"); } } } public override List ComputeFrom(float[] samples, int startSample, int endSample) { Guard.AgainstInvalidRange(startSample, endSample, "starting pos", "ending pos"); int frameSize = base.FrameSize; int hopSize = base.HopSize; List list = new List(); int num = 0; int num2 = startSample; if (_featuregram == null) { _envelopes = new float[_filterbank.Length][]; for (int i = 0; i < _envelopes.Length; i++) { _envelopes[i] = new float[samples.Length / hopSize]; } float num3 = ((startSample > 0) ? samples[startSample - 1] : 0f); int num4 = endSample - Math.Max(frameSize, hopSize); for (num2 = startSample; num2 < num4; num2 += hopSize) { samples.FastCopyTo(_block, frameSize, num2); int num5 = frameSize; while (num5 < _block.Length) { _block[num5++] = 0f; } if (_preEmphasis > 1E-10f) { for (int j = 0; j < frameSize; j++) { float num6 = _block[j] - num3 * _preEmphasis; num3 = _block[j]; _block[j] = num6; } num3 = samples[num2 + hopSize - 1]; } if (_window != WindowType.Rectangular) { _block.ApplyWindow(_windowSamples); } _fft.PowerSpectrum(_block, _spectrum); FilterBanks.Apply(_filterbank, _spectrum, _filteredSpectrum); for (int k = 0; k < _envelopes.Length; k++) { _envelopes[k][num] = _filteredSpectrum[k]; } num++; } } else { num = _featuregram.Length; _envelopes = new float[_featuregram[0].Length][]; for (int l = 0; l < _envelopes.Length; l++) { _envelopes[l] = new float[num]; for (num2 = 0; num2 < num; num2++) { _envelopes[l][num2] = _featuregram[num2][l]; } } } int num7 = num; float[][] envelopes = _envelopes; foreach (float[] array in envelopes) { float num8 = 0f; for (int n = 0; n < num7; n++) { num8 += ((n >= 0) ? array[n] : (0f - array[n])); } num8 /= (float)num7; if (num8 >= 1E-10f) { for (int num9 = 0; num9 < num7; num9++) { array[num9] /= num8; } } } for (num2 = 0; num2 < num7; num2 += _modulationHopSize) { float[] array2 = new float[_envelopes.Length * (_modulationFftSize / 2 + 1)]; int num10 = 0; envelopes = _envelopes; foreach (float[] source in envelopes) { int num11 = Math.Min(_modulationFftSize, num7 - num2); source.FastCopyTo(_modBlock, num11, num2); int num12 = num11; while (num12 < _modBlock.Length) { _modBlock[num12++] = 0f; } _modulationFft.PowerSpectrum(_modBlock, _modSpectrum); _modSpectrum.FastCopyTo(array2, _modSpectrum.Length, 0, num10); num10 += _modSpectrum.Length; } list.Add(array2); } return list; } public float[][] MakeSpectrum2D(float[] featureVector) { float[][] filterbank = _filterbank; float[][] array = new float[(filterbank != null) ? filterbank.Length : _featuregram[0].Length][]; int num = _modulationFftSize / 2 + 1; int num2 = 0; for (int i = 0; i < array.Length; i++) { array[i] = featureVector.FastCopyFragment(num, num2); num2 += num; } return array; } public List VectorsAtHerz(IList featureVectors, float herz = 4f) { float[][] filterbank = _filterbank; int num = ((filterbank != null) ? filterbank.Length : _featuregram[0].Length); float num2 = (float)base.SamplingRate / (float)base.HopSize / (float)_modulationFftSize; int num3 = (int)Math.Round(herz / num2); int num4 = _modulationFftSize / 2 + 1; List list = new List(); foreach (float[] featureVector in featureVectors) { float[] array = new float[num]; for (int i = 0; i < array.Length; i++) { array[i] = featureVector[num3 + i * num4]; } list.Add(array); } return list; } public override void ProcessFrame(float[] block, float[] features) { throw new NotImplementedException("AmsExtractor does not provide this function. Please call ComputeFrom() method"); } public override int ComputeFrom(float[] samples, int startSample, int endSample, IList vectors) { throw new NotImplementedException("AmsExtractor does not provide this function. Please call overloaded ComputeFrom() method"); } } public class ChromaExtractor : FeatureExtractor { protected readonly float[][] _filterBank; protected readonly RealFft _fft; protected readonly float[] _spectrum; protected readonly ChromaOptions _options; public override List FeatureDescriptions { get { if (base.FeatureCount != 12) { return (from i in Enumerable.Range(1, base.FeatureCount) select "chroma" + i).ToList(); } if (!_options.BaseC) { return new string[12] { "A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#" }.ToList(); } return new string[12] { "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#", "A", "A#", "B" }.ToList(); } } public float[][] FilterBank => _filterBank; public ChromaExtractor(ChromaOptions options) : base(options) { _options = options; _blockSize = ((options.FftSize > base.FrameSize) ? options.FftSize : MathUtils.NextPowerOfTwo(base.FrameSize)); base.FeatureCount = options.FeatureCount; _filterBank = FilterBanks.Chroma(_blockSize, base.SamplingRate, base.FeatureCount, options.Tuning, options.CenterOctave, options.OctaveWidth, options.Norm, options.BaseC); _fft = new RealFft(_blockSize); _spectrum = new float[_blockSize / 2 + 1]; } public override void ProcessFrame(float[] block, float[] features) { _fft.PowerSpectrum(block, _spectrum, normalize: false); FilterBanks.Apply(_filterBank, _spectrum, features); } public override bool IsParallelizable() { return true; } public override FeatureExtractor ParallelCopy() { return new ChromaExtractor(_options); } } public class FilterbankExtractor : FeatureExtractor { protected readonly RealFft _fft; protected readonly NonLinearityType _nonLinearityType; protected readonly SpectrumType _spectrumType; protected readonly float _logFloor; protected readonly Action _getSpectrum; protected readonly Action _postProcessSpectrum; protected readonly float[] _spectrum; protected readonly float[] _bandSpectrum; public override List FeatureDescriptions => (from i in Enumerable.Range(0, base.FeatureCount) select "fb" + i).ToList(); public float[][] FilterBank { get; } public FilterbankExtractor(FilterbankOptions options) : base(options) { int num = options.FilterBankSize; if (options.FilterBank == null) { _blockSize = ((options.FftSize > base.FrameSize) ? options.FftSize : MathUtils.NextPowerOfTwo(base.FrameSize)); (double, double, double)[] frequencies = FilterBanks.MelBands(num, base.SamplingRate, options.LowFrequency, options.HighFrequency, overlap: false); FilterBank = FilterBanks.Rectangular(_blockSize, base.SamplingRate, frequencies, null, Scale.HerzToMel); } else { FilterBank = options.FilterBank; num = FilterBank.Length; _blockSize = 2 * (FilterBank[0].Length - 1); Guard.AgainstExceedance(base.FrameSize, _blockSize, "frame size", "FFT size"); } base.FeatureCount = num; _fft = new RealFft(_blockSize); _logFloor = options.LogFloor; _nonLinearityType = options.NonLinearity; switch (_nonLinearityType) { case NonLinearityType.Log10: _postProcessSpectrum = delegate { FilterBanks.ApplyAndLog10(FilterBank, _spectrum, _bandSpectrum, _logFloor); }; break; case NonLinearityType.LogE: _postProcessSpectrum = delegate { FilterBanks.ApplyAndLog(FilterBank, _spectrum, _bandSpectrum, _logFloor); }; break; case NonLinearityType.ToDecibel: _postProcessSpectrum = delegate { FilterBanks.ApplyAndToDecibel(FilterBank, _spectrum, _bandSpectrum, _logFloor); }; break; case NonLinearityType.CubicRoot: _postProcessSpectrum = delegate { FilterBanks.ApplyAndPow(FilterBank, _spectrum, _bandSpectrum, 0.33); }; break; default: _postProcessSpectrum = delegate { FilterBanks.Apply(FilterBank, _spectrum, _bandSpectrum); }; break; } _spectrumType = options.SpectrumType; switch (_spectrumType) { case SpectrumType.Magnitude: _getSpectrum = delegate(float[] block) { _fft.MagnitudeSpectrum(block, _spectrum); }; break; case SpectrumType.MagnitudeNormalized: _getSpectrum = delegate(float[] block) { _fft.MagnitudeSpectrum(block, _spectrum, normalize: true); }; break; case SpectrumType.PowerNormalized: _getSpectrum = delegate(float[] block) { _fft.PowerSpectrum(block, _spectrum); }; break; default: _getSpectrum = delegate(float[] block) { _fft.PowerSpectrum(block, _spectrum, normalize: false); }; break; } _spectrum = new float[_blockSize / 2 + 1]; _bandSpectrum = new float[num]; } public override void ProcessFrame(float[] block, float[] features) { _getSpectrum(block); _postProcessSpectrum(); _bandSpectrum.FastCopyTo(features, base.FeatureCount); } public override bool IsParallelizable() { return true; } public override FeatureExtractor ParallelCopy() { return new FilterbankExtractor(new FilterbankOptions { SamplingRate = base.SamplingRate, FilterBank = FilterBank, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, PreEmphasis = _preEmphasis, NonLinearity = _nonLinearityType, SpectrumType = _spectrumType, Window = _window, LogFloor = _logFloor }); } } public class LpccExtractor : FeatureExtractor { protected readonly int _order; protected readonly int _lifterSize; protected readonly float[] _lifterCoeffs; protected readonly Convolver _convolver; protected readonly float[] _cc; protected readonly float[] _lpc; protected readonly float[] _reversed; public override List FeatureDescriptions => (from i in Enumerable.Range(0, base.FeatureCount) select "lpcc" + i).ToList(); public LpccExtractor(LpccOptions options) : base(options) { base.FeatureCount = options.FeatureCount; _order = ((options.LpcOrder > 0) ? options.LpcOrder : (base.FeatureCount - 1)); _blockSize = MathUtils.NextPowerOfTwo(2 * base.FrameSize - 1); _convolver = new Convolver(_blockSize); _lifterSize = options.LifterSize; _lifterCoeffs = ((_lifterSize > 0) ? Window.Liftering(base.FeatureCount, _lifterSize) : null); _reversed = new float[base.FrameSize]; _cc = new float[_blockSize]; _lpc = new float[_order + 1]; } public override void ProcessFrame(float[] block, float[] features) { block.FastCopyTo(_reversed, base.FrameSize); _convolver.CrossCorrelate(block, _reversed, _cc); for (int i = 0; i < _lpc.Length; i++) { _lpc[i] = 0f; } float gain = Lpc.LevinsonDurbin(_cc, _lpc, _order, base.FrameSize - 1); Lpc.ToCepstrum(_lpc, gain, features); if (_lifterCoeffs != null) { features.ApplyWindow(_lifterCoeffs); } } public override bool IsParallelizable() { return true; } public override FeatureExtractor ParallelCopy() { return new LpccExtractor(new LpccOptions { SamplingRate = base.SamplingRate, FeatureCount = base.FeatureCount, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, LpcOrder = _order, LifterSize = _lifterSize, PreEmphasis = _preEmphasis, Window = _window }); } } public class LpcExtractor : FeatureExtractor { protected readonly int _order; protected readonly Convolver _convolver; protected readonly float[] _reversed; protected readonly float[] _cc; public override List FeatureDescriptions => new string[1] { "error" }.Concat(from i in Enumerable.Range(1, _order) select "lpc" + i).ToList(); public LpcExtractor(LpcOptions options) : base(options) { _order = options.LpcOrder; base.FeatureCount = _order + 1; _blockSize = MathUtils.NextPowerOfTwo(2 * base.FrameSize - 1); _convolver = new Convolver(_blockSize); _reversed = new float[base.FrameSize]; _cc = new float[_blockSize]; } public override void ProcessFrame(float[] block, float[] features) { block.FastCopyTo(_reversed, base.FrameSize); _convolver.CrossCorrelate(block, _reversed, _cc); float num = Lpc.LevinsonDurbin(_cc, features, _order, base.FrameSize - 1); features[0] = num; } public override bool IsParallelizable() { return true; } public override FeatureExtractor ParallelCopy() { return new LpcExtractor(new LpcOptions { SamplingRate = base.SamplingRate, LpcOrder = _order, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, PreEmphasis = _preEmphasis, Window = _window }); } } public class MfccExtractor : FeatureExtractor { protected readonly int _lifterSize; protected readonly float[] _lifterCoeffs; protected readonly RealFft _fft; protected readonly IDct _dct; protected readonly string _dctType; protected readonly NonLinearityType _nonLinearityType; protected readonly SpectrumType _spectrumType; protected readonly float _logFloor; protected readonly bool _includeEnergy; protected readonly float _logEnergyFloor; protected readonly Action _getSpectrum; protected readonly Action _postProcessSpectrum; protected readonly Action _applyDct; protected readonly float[] _spectrum; protected readonly float[] _melSpectrum; public override List FeatureDescriptions { get { List list = (from i in Enumerable.Range(0, base.FeatureCount) select "mfcc" + i).ToList(); if (_includeEnergy) { list[0] = "log_En"; } return list; } } public float[][] FilterBank { get; } public MfccExtractor(MfccOptions options) : base(options) { base.FeatureCount = options.FeatureCount; int num = options.FilterBankSize; if (options.FilterBank == null) { _blockSize = ((options.FftSize > base.FrameSize) ? options.FftSize : MathUtils.NextPowerOfTwo(base.FrameSize)); (double, double, double)[] frequencies = FilterBanks.MelBands(num, base.SamplingRate, options.LowFrequency, options.HighFrequency); FilterBank = FilterBanks.Triangular(_blockSize, base.SamplingRate, frequencies, null, Scale.HerzToMel); } else { FilterBank = options.FilterBank; num = FilterBank.Length; _blockSize = 2 * (FilterBank[0].Length - 1); Guard.AgainstExceedance(base.FrameSize, _blockSize, "frame size", "FFT size"); } _fft = new RealFft(_blockSize); _lifterSize = options.LifterSize; _lifterCoeffs = ((_lifterSize > 0) ? Window.Liftering(base.FeatureCount, _lifterSize) : null); _includeEnergy = options.IncludeEnergy; _logEnergyFloor = options.LogEnergyFloor; _dctType = options.DctType; switch (_dctType[0]) { case '1': _dct = new Dct1(num); break; case '3': _dct = new Dct3(num); break; case '4': _dct = new Dct4(num); break; default: _dct = new Dct2(num); break; } if (_dctType.EndsWith("N", StringComparison.OrdinalIgnoreCase)) { _applyDct = delegate(float[] mfccs) { _dct.DirectNorm(_melSpectrum, mfccs); }; } else { _applyDct = delegate(float[] mfccs) { _dct.Direct(_melSpectrum, mfccs); }; } _logFloor = options.LogFloor; _nonLinearityType = options.NonLinearity; switch (_nonLinearityType) { case NonLinearityType.Log10: _postProcessSpectrum = delegate { FilterBanks.ApplyAndLog10(FilterBank, _spectrum, _melSpectrum, _logFloor); }; break; case NonLinearityType.LogE: _postProcessSpectrum = delegate { FilterBanks.ApplyAndLog(FilterBank, _spectrum, _melSpectrum, _logFloor); }; break; case NonLinearityType.ToDecibel: _postProcessSpectrum = delegate { FilterBanks.ApplyAndToDecibel(FilterBank, _spectrum, _melSpectrum, _logFloor); }; break; case NonLinearityType.CubicRoot: _postProcessSpectrum = delegate { FilterBanks.ApplyAndPow(FilterBank, _spectrum, _melSpectrum, 0.33); }; break; default: _postProcessSpectrum = delegate { FilterBanks.Apply(FilterBank, _spectrum, _melSpectrum); }; break; } _spectrumType = options.SpectrumType; switch (_spectrumType) { case SpectrumType.Magnitude: _getSpectrum = delegate(float[] block) { _fft.MagnitudeSpectrum(block, _spectrum); }; break; case SpectrumType.MagnitudeNormalized: _getSpectrum = delegate(float[] block) { _fft.MagnitudeSpectrum(block, _spectrum, normalize: true); }; break; case SpectrumType.PowerNormalized: _getSpectrum = delegate(float[] block) { _fft.PowerSpectrum(block, _spectrum); }; break; default: _getSpectrum = delegate(float[] block) { _fft.PowerSpectrum(block, _spectrum, normalize: false); }; break; } _spectrum = new float[_blockSize / 2 + 1]; _melSpectrum = new float[num]; } public override void ProcessFrame(float[] block, float[] features) { _getSpectrum(block); _postProcessSpectrum(); _applyDct(features); if (_lifterCoeffs != null) { features.ApplyWindow(_lifterCoeffs); } if (_includeEnergy) { features[0] = (float)Math.Log(Math.Max(block.Sum((float x) => x * x), _logEnergyFloor)); } } public override bool IsParallelizable() { return true; } public override FeatureExtractor ParallelCopy() { return new MfccExtractor(new MfccOptions { SamplingRate = base.SamplingRate, FeatureCount = base.FeatureCount, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, FilterBankSize = FilterBank.Length, FftSize = _blockSize, FilterBank = FilterBank, LifterSize = _lifterSize, PreEmphasis = _preEmphasis, DctType = _dctType, NonLinearity = _nonLinearityType, SpectrumType = _spectrumType, Window = _window, LogFloor = _logFloor, IncludeEnergy = _includeEnergy, LogEnergyFloor = _logEnergyFloor }); } } public class PitchExtractor : FeatureExtractor { protected readonly float _low; protected readonly float _high; protected readonly Convolver _convolver; protected readonly float[] _reversed; protected readonly float[] _cc; public override List FeatureDescriptions { get; } public PitchExtractor(PitchOptions options) : base(options) { _low = (float)options.LowFrequency; _high = (float)options.HighFrequency; _blockSize = MathUtils.NextPowerOfTwo(2 * base.FrameSize - 1); _convolver = new Convolver(_blockSize); _reversed = new float[base.FrameSize]; _cc = new float[_blockSize]; base.FeatureCount = 1; FeatureDescriptions = new List { "pitch" }; } public override void ProcessFrame(float[] block, float[] features) { block.FastCopyTo(_reversed, base.FrameSize); _convolver.CrossCorrelate(block, _reversed, _cc); int num = (int)((float)base.SamplingRate / _high); int num2 = (int)((float)base.SamplingRate / _low); int num3 = num + base.FrameSize - 1; int num4 = Math.Min(num3 + num2, _cc.Length); float num5 = ((num3 < _cc.Length) ? _cc[num3] : 0f); int num6 = num3; for (int i = num3; i < num4; i++) { if (_cc[i] > num5) { num5 = _cc[i]; num6 = i - base.FrameSize + 1; } } features[0] = ((num5 > 1f) ? ((float)base.SamplingRate / (float)num6) : 0f); } public override bool IsParallelizable() { return true; } public override FeatureExtractor ParallelCopy() { return new PitchExtractor(new PitchOptions { SamplingRate = base.SamplingRate, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, LowFrequency = _low, HighFrequency = _high, PreEmphasis = _preEmphasis, Window = _window }); } } public class PlpExtractor : FeatureExtractor { protected readonly double[] _centerFrequencies; protected readonly double _rasta; protected readonly RastaFilter[] _rastaFilters; protected readonly int _lifterSize; protected readonly float[] _lifterCoeffs; protected readonly bool _includeEnergy; protected readonly float _logEnergyFloor; protected readonly RealFft _fft; protected readonly float[] _spectrum; protected readonly float[] _bandSpectrum; protected readonly double[] _equalLoudnessCurve; protected readonly int _lpcOrder; protected readonly float[] _lpc; protected readonly float[][] _idftTable; protected readonly float[] _cc; public override List FeatureDescriptions { get { List list = (from i in Enumerable.Range(0, base.FeatureCount) select "plp" + i).ToList(); if (_includeEnergy) { list[0] = "log_En"; } return list; } } public float[][] FilterBank { get; } public PlpExtractor(PlpOptions options) : base(options) { base.FeatureCount = options.FeatureCount; int num = options.FilterBankSize; if (options.FilterBank == null) { _blockSize = ((options.FftSize > base.FrameSize) ? options.FftSize : MathUtils.NextPowerOfTwo(base.FrameSize)); double lowFrequency = options.LowFrequency; double highFrequency = options.HighFrequency; FilterBank = FilterBanks.BarkBankSlaney(num, _blockSize, base.SamplingRate, lowFrequency, highFrequency); (double, double, double)[] source = FilterBanks.BarkBandsSlaney(num, base.SamplingRate, lowFrequency, highFrequency); _centerFrequencies = source.Select(((double, double, double) b) => b.Item2).ToArray(); } else { FilterBank = options.FilterBank; num = FilterBank.Length; _blockSize = 2 * (FilterBank[0].Length - 1); Guard.AgainstExceedance(base.FrameSize, _blockSize, "frame size", "FFT size"); if (options.CenterFrequencies != null) { _centerFrequencies = options.CenterFrequencies; } else { double num2 = (double)base.SamplingRate / (double)_blockSize; _centerFrequencies = new double[num]; for (int num3 = 0; num3 < FilterBank.Length; num3++) { int num4 = 0; int num5 = _blockSize / 2; for (int num6 = 0; num6 < FilterBank[num3].Length; num6++) { if (FilterBank[num3][num6] > 0f) { num4 = num6; break; } } for (int num7 = num4; num7 < FilterBank[num3].Length; num7++) { if (FilterBank[num3][num7] == 0f) { num5 = num7; break; } } _centerFrequencies[num3] = num2 * (double)(num5 + num4) / 2.0; } } } _equalLoudnessCurve = new double[num]; for (int num8 = 0; num8 < _centerFrequencies.Length; num8++) { double num9 = _centerFrequencies[num8] * _centerFrequencies[num8]; _equalLoudnessCurve[num8] = Math.Pow(num9 / (num9 + 160000.0), 2.0) * ((num9 + 1440000.0) / (num9 + 9610000.0)); } _rasta = options.Rasta; if (_rasta > 0.0) { _rastaFilters = (from f in Enumerable.Range(0, num) select new RastaFilter(_rasta)).ToArray(); } _lpcOrder = ((options.LpcOrder > 0) ? options.LpcOrder : (base.FeatureCount - 1)); _idftTable = new float[_lpcOrder + 1][]; int num10 = num + 2; double num11 = Math.PI / (double)(num10 - 1); for (int num12 = 0; num12 < _idftTable.Length; num12++) { _idftTable[num12] = new float[num10]; _idftTable[num12][0] = 1f; for (int num13 = 1; num13 < num10 - 1; num13++) { _idftTable[num12][num13] = 2f * (float)Math.Cos(num11 * (double)num12 * (double)num13); } _idftTable[num12][num10 - 1] = (float)Math.Cos(num11 * (double)num12 * (double)(num10 - 1)); } _lpc = new float[_lpcOrder + 1]; _cc = new float[num10]; _fft = new RealFft(_blockSize); _lifterSize = options.LifterSize; _lifterCoeffs = ((_lifterSize > 0) ? Window.Liftering(base.FeatureCount, _lifterSize) : null); _includeEnergy = options.IncludeEnergy; _logEnergyFloor = options.LogEnergyFloor; _spectrum = new float[_blockSize / 2 + 1]; _bandSpectrum = new float[num]; } public override void ProcessFrame(float[] block, float[] features) { _fft.PowerSpectrum(block, _spectrum, normalize: false); FilterBanks.Apply(FilterBank, _spectrum, _bandSpectrum); if (_rasta > 0.0) { for (int i = 0; i < _bandSpectrum.Length; i++) { float sample = (float)Math.Log(_bandSpectrum[i] + float.Epsilon); sample = _rastaFilters[i].Process(sample); _bandSpectrum[i] = (float)Math.Exp(sample); } } for (int j = 0; j < _bandSpectrum.Length; j++) { _bandSpectrum[j] = (float)Math.Pow(Math.Max(_bandSpectrum[j], 1.0) * _equalLoudnessCurve[j], 0.33); } int num = _idftTable[0].Length; for (int k = 0; k < _idftTable.Length; k++) { float num2 = _idftTable[k][0] * _bandSpectrum[0] + _idftTable[k][num - 1] * _bandSpectrum[num - 3]; for (int l = 1; l < num - 1; l++) { num2 += _idftTable[k][l] * _bandSpectrum[l - 1]; } _cc[k] = num2 / (float)(2 * (num - 1)); } for (int m = 0; m < _lpc.Length; m++) { _lpc[m] = 0f; } float gain = Lpc.LevinsonDurbin(_cc, _lpc, _lpcOrder); Lpc.ToCepstrum(_lpc, gain, features); if (_lifterCoeffs != null) { features.ApplyWindow(_lifterCoeffs); } if (_includeEnergy) { features[0] = (float)Math.Log(Math.Max(block.Sum((float x) => x * x), _logEnergyFloor)); } } public override void Reset() { if (_rastaFilters != null) { RastaFilter[] rastaFilters = _rastaFilters; for (int i = 0; i < rastaFilters.Length; i++) { rastaFilters[i].Reset(); } } } public override bool IsParallelizable() { return _rasta == 0.0; } public override FeatureExtractor ParallelCopy() { if (!IsParallelizable()) { return null; } return new PlpExtractor(new PlpOptions { SamplingRate = base.SamplingRate, FeatureCount = base.FeatureCount, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, LpcOrder = _lpcOrder, Rasta = _rasta, FilterBank = FilterBank, FilterBankSize = FilterBank.Length, FftSize = _blockSize, LifterSize = _lifterSize, PreEmphasis = _preEmphasis, Window = _window, CenterFrequencies = _centerFrequencies, IncludeEnergy = _includeEnergy, LogEnergyFloor = _logEnergyFloor }); } } public class PnccExtractor : FeatureExtractor { protected class SpectraRingBuffer { protected readonly float[][] _spectra; protected int _count; protected int _capacity; protected int _current; public float[] CentralSpectrum; public float[] AverageSpectrum; public SpectraRingBuffer(int capacity, int spectrumSize) { _spectra = new float[capacity][]; _capacity = capacity; _count = 0; _current = 0; AverageSpectrum = new float[spectrumSize]; } public void Add(float[] spectrum) { if (_count < _capacity) { _count++; } _spectra[_current] = spectrum; for (int i = 0; i < spectrum.Length; i++) { AverageSpectrum[i] = 0f; for (int j = 0; j < _count; j++) { AverageSpectrum[i] += _spectra[j][i]; } AverageSpectrum[i] /= _count; } CentralSpectrum = _spectra[(_current + _capacity / 2 + 1) % _capacity]; _current = (_current + 1) % _capacity; } public void Reset() { _count = 0; _current = 0; Array.Clear(AverageSpectrum, 0, AverageSpectrum.Length); float[][] spectra = _spectra; foreach (float[] array in spectra) { Array.Clear(array, 0, array.Length); } } } protected readonly int _power; protected readonly bool _includeEnergy; protected readonly float _logEnergyFloor; protected readonly RealFft _fft; protected readonly Dct2 _dct; protected readonly float[] _spectrum; protected float _mean = 40000000f; protected readonly SpectraRingBuffer _ringBuffer; protected int _step; protected readonly float[] _gammatoneSpectrum; protected readonly float[] _spectrumQOut; protected readonly float[] _filteredSpectrumQ; protected readonly float[] _spectrumS; protected readonly float[] _smoothedSpectrumS; protected readonly float[] _avgSpectrumQ1; protected readonly float[] _avgSpectrumQ2; protected readonly float[] _smoothedSpectrum; public override List FeatureDescriptions { get { List list = (from i in Enumerable.Range(0, base.FeatureCount) select "pncc" + i).ToList(); if (_includeEnergy) { list[0] = "log_En"; } return list; } } public int M { get; set; } = 2; public int N { get; set; } = 4; public float LambdaA { get; set; } = 0.999f; public float LambdaB { get; set; } = 0.5f; public float LambdaT { get; set; } = 0.85f; public float LambdaMu { get; set; } = 0.999f; public float C { get; set; } = 2f; public float MuT { get; set; } = 0.2f; public float[][] FilterBank { get; } public PnccExtractor(PnccOptions options) : base(options) { base.FeatureCount = options.FeatureCount; int num = options.FilterBankSize; if (options.FilterBank == null) { _blockSize = ((options.FftSize > base.FrameSize) ? options.FftSize : MathUtils.NextPowerOfTwo(base.FrameSize)); FilterBank = FilterBanks.Erb(options.FilterBankSize, _blockSize, base.SamplingRate, options.LowFrequency, options.HighFrequency); } else { FilterBank = options.FilterBank; num = FilterBank.Length; _blockSize = 2 * (FilterBank[0].Length - 1); Guard.AgainstExceedance(base.FrameSize, _blockSize, "frame size", "FFT size"); } _fft = new RealFft(_blockSize); _dct = new Dct2(num); _power = options.Power; _includeEnergy = options.IncludeEnergy; _logEnergyFloor = options.LogEnergyFloor; _spectrum = new float[_blockSize / 2 + 1]; _spectrumQOut = new float[num]; _gammatoneSpectrum = new float[num]; _filteredSpectrumQ = new float[num]; _spectrumS = new float[num]; _smoothedSpectrumS = new float[num]; _avgSpectrumQ1 = new float[num]; _avgSpectrumQ2 = new float[num]; _smoothedSpectrum = new float[num]; _ringBuffer = new SpectraRingBuffer(2 * M + 1, num); _step = M - 1; } public override void ProcessFrame(float[] block, float[] features) { _step++; _fft.PowerSpectrum(block, _spectrum, normalize: false); FilterBanks.Apply(FilterBank, _spectrum, _gammatoneSpectrum); _ringBuffer.Add(_gammatoneSpectrum); float[] averageSpectrum = _ringBuffer.AverageSpectrum; if (_step == 2 * M) { for (int i = 0; i < _spectrumQOut.Length; i++) { _spectrumQOut[i] = averageSpectrum[i] * 0.9f; } } if (_step < 2 * M) { return; } for (int j = 0; j < _spectrumQOut.Length; j++) { if (averageSpectrum[j] > _spectrumQOut[j]) { _spectrumQOut[j] = LambdaA * _spectrumQOut[j] + (1f - LambdaA) * averageSpectrum[j]; } else { _spectrumQOut[j] = LambdaB * _spectrumQOut[j] + (1f - LambdaB) * averageSpectrum[j]; } } for (int k = 0; k < _filteredSpectrumQ.Length; k++) { _filteredSpectrumQ[k] = Math.Max(averageSpectrum[k] - _spectrumQOut[k], 0f); if (_step == 2 * M) { _avgSpectrumQ1[k] = 0.9f * _filteredSpectrumQ[k]; _avgSpectrumQ2[k] = _filteredSpectrumQ[k]; } if (_filteredSpectrumQ[k] > _avgSpectrumQ1[k]) { _avgSpectrumQ1[k] = LambdaA * _avgSpectrumQ1[k] + (1f - LambdaA) * _filteredSpectrumQ[k]; } else { _avgSpectrumQ1[k] = LambdaB * _avgSpectrumQ1[k] + (1f - LambdaB) * _filteredSpectrumQ[k]; } float val = _filteredSpectrumQ[k]; _avgSpectrumQ2[k] *= LambdaT; if (averageSpectrum[k] < C * _spectrumQOut[k]) { _filteredSpectrumQ[k] = _avgSpectrumQ1[k]; } else if (_filteredSpectrumQ[k] <= _avgSpectrumQ2[k]) { _filteredSpectrumQ[k] = MuT * _avgSpectrumQ2[k]; } _avgSpectrumQ2[k] = Math.Max(_avgSpectrumQ2[k], val); _filteredSpectrumQ[k] = Math.Max(_filteredSpectrumQ[k], _avgSpectrumQ1[k]); } for (int l = 0; l < _spectrumS.Length; l++) { _spectrumS[l] = _filteredSpectrumQ[l] / Math.Max(averageSpectrum[l], 2.22E-16f); } for (int m = 0; m < _smoothedSpectrumS.Length; m++) { _smoothedSpectrumS[m] = 0f; int num = 0; int num2 = Math.Max(m - N, 0); while (num2 < Math.Min(m + N + 1, FilterBank.Length)) { _smoothedSpectrumS[m] += _spectrumS[num2]; num2++; num++; } _smoothedSpectrumS[m] /= num; } float[] centralSpectrum = _ringBuffer.CentralSpectrum; float num3 = 0f; for (int n = 0; n < _smoothedSpectrum.Length; n++) { _smoothedSpectrum[n] = _smoothedSpectrumS[n] * centralSpectrum[n]; num3 += _smoothedSpectrum[n]; } _mean = LambdaMu * _mean + (1f - LambdaMu) * num3; for (int num4 = 0; num4 < _smoothedSpectrum.Length; num4++) { _smoothedSpectrum[num4] /= _mean; _smoothedSpectrum[num4] *= 1E+10f; } if (_power != 0) { for (int num5 = 0; num5 < _smoothedSpectrum.Length; num5++) { _smoothedSpectrum[num5] = (float)Math.Pow(_smoothedSpectrum[num5], 1.0 / (double)_power); } } else { for (int num6 = 0; num6 < _smoothedSpectrum.Length; num6++) { _smoothedSpectrum[num6] = (float)Math.Log(_smoothedSpectrum[num6] + 2.22E-16f); } } _dct.DirectNorm(_smoothedSpectrum, features); if (_includeEnergy) { features[0] = (float)Math.Log(Math.Max(block.Sum((float x) => x * x), _logEnergyFloor)); } if (_step == 2147483646) { _step = 2 * M + 1; } } public override void Reset() { _step = M - 1; _mean = 40000000f; _ringBuffer.Reset(); } } public class SpnccExtractor : FeatureExtractor { protected readonly int _power; protected readonly bool _includeEnergy; protected readonly float _logEnergyFloor; protected readonly RealFft _fft; protected readonly Dct2 _dct; protected readonly float[] _spectrum; protected readonly float[] _filteredSpectrum; protected float _mean = 40000000f; public override List FeatureDescriptions { get { List list = (from i in Enumerable.Range(0, base.FeatureCount) select "spncc" + i).ToList(); if (_includeEnergy) { list[0] = "log_En"; } return list; } } public float LambdaMu { get; set; } = 0.999f; public float[][] FilterBank { get; } public SpnccExtractor(PnccOptions options) : base(options) { base.FeatureCount = options.FeatureCount; int num = options.FilterBankSize; if (options.FilterBank == null) { _blockSize = ((options.FftSize > base.FrameSize) ? options.FftSize : MathUtils.NextPowerOfTwo(base.FrameSize)); FilterBank = FilterBanks.Erb(num, _blockSize, base.SamplingRate, options.LowFrequency, options.HighFrequency); } else { FilterBank = options.FilterBank; num = FilterBank.Length; _blockSize = 2 * (FilterBank[0].Length - 1); Guard.AgainstExceedance(base.FrameSize, _blockSize, "frame size", "FFT size"); } _power = options.Power; _includeEnergy = options.IncludeEnergy; _logEnergyFloor = options.LogEnergyFloor; _fft = new RealFft(_blockSize); _dct = new Dct2(num); _spectrum = new float[_blockSize / 2 + 1]; _filteredSpectrum = new float[num]; } public override void ProcessFrame(float[] block, float[] features) { _fft.PowerSpectrum(block, _spectrum, normalize: false); FilterBanks.Apply(FilterBank, _spectrum, _filteredSpectrum); float num = 0f; for (int i = 0; i < _filteredSpectrum.Length; i++) { num += _filteredSpectrum[i]; } _mean = LambdaMu * _mean + (1f - LambdaMu) * num; for (int j = 0; j < _filteredSpectrum.Length; j++) { _filteredSpectrum[j] *= 1E+10f / _mean; } if (_power != 0) { for (int k = 0; k < _filteredSpectrum.Length; k++) { _filteredSpectrum[k] = (float)Math.Pow(_filteredSpectrum[k], 1.0 / (double)_power); } } else { for (int l = 0; l < _filteredSpectrum.Length; l++) { _filteredSpectrum[l] = (float)Math.Log10(_filteredSpectrum[l] + float.Epsilon); } } _dct.DirectNorm(_filteredSpectrum, features); if (_includeEnergy) { features[0] = (float)Math.Log(Math.Max(block.Sum((float x) => x * x), _logEnergyFloor)); } } public override void Reset() { _mean = 40000000f; } } public class WaveletExtractor : FeatureExtractor { protected readonly Fwt _fwt; protected readonly string _waveletName; protected readonly int _level; protected readonly float[] _coeffs; public override List FeatureDescriptions => (from i in Enumerable.Range(0, base.FeatureCount) select "w" + i).ToList(); public WaveletExtractor(WaveletOptions options) : base(options) { _blockSize = ((options.FwtSize > base.FrameSize) ? options.FwtSize : MathUtils.NextPowerOfTwo(base.FrameSize)); base.FeatureCount = ((options.FeatureCount > 0) ? options.FeatureCount : _blockSize); _waveletName = options.WaveletName; _level = options.FwtLevel; _fwt = new Fwt(_blockSize, new Wavelet(_waveletName)); _coeffs = new float[_blockSize]; } public override void ProcessFrame(float[] block, float[] features) { _fwt.Direct(block, _coeffs, _level); _coeffs.FastCopyTo(features, base.FeatureCount); } public override bool IsParallelizable() { return true; } public override FeatureExtractor ParallelCopy() { return new WaveletExtractor(new WaveletOptions { SamplingRate = base.SamplingRate, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, WaveletName = _waveletName, FeatureCount = base.FeatureCount, FwtSize = _blockSize, FwtLevel = _level, PreEmphasis = _preEmphasis, Window = _window }); } } } namespace NWaves.FeatureExtractors.Serializers { public class CsvFeatureSerializer { private readonly IList _vectors; private readonly IList _timeMarkers; private readonly IList _names; private readonly char _delimiter; public CsvFeatureSerializer(IList featureVectors, IList timeMarkers = null, IList featureNames = null, char delimiter = ',') { _vectors = featureVectors; _timeMarkers = timeMarkers; _names = featureNames; _delimiter = delimiter; } public async Task SerializeAsync(Stream stream, string format = "0.00000", string timeFormat = "0.000") { char delimiter = _delimiter; string comma = delimiter.ToString(); using StreamWriter writer = new StreamWriter(stream); if (_names != null) { string text = string.Join(comma, _names); string value = ((_timeMarkers == null) ? (text ?? "") : ("time_pos" + comma + text)); await writer.WriteLineAsync(value).ConfigureAwait(continueOnCapturedContext: false); } if (_timeMarkers == null) { foreach (float[] vector in _vectors) { string value2 = string.Join(comma, vector.Select((float f) => f.ToString(format, CultureInfo.InvariantCulture))); await writer.WriteLineAsync(value2).ConfigureAwait(continueOnCapturedContext: false); } return; } for (int i = 0; i < _vectors.Count; i++) { string value3 = $"{_timeMarkers[i].ToString(timeFormat, CultureInfo.InvariantCulture)}{comma}{string.Join(comma, _vectors[i].Select((float f) => f.ToString(format, CultureInfo.InvariantCulture)))}"; await writer.WriteLineAsync(value3).ConfigureAwait(continueOnCapturedContext: false); } } } } namespace NWaves.FeatureExtractors.Options { [DataContract] public class AmsOptions : FeatureExtractorOptions { [DataMember] public int ModulationFftSize { get; set; } = 64; [DataMember] public int ModulationHopSize { get; set; } = 4; [DataMember] public int FftSize { get; set; } [DataMember] public IEnumerable Featuregram { get; set; } [DataMember] public float[][] FilterBank { get; set; } } [DataContract] public class ChromaOptions : FeatureExtractorOptions { [DataMember] public int FftSize { get; set; } [DataMember] public double Tuning { get; set; } [DataMember] public double CenterOctave { get; set; } = 5.0; [DataMember] public double OctaveWidth { get; set; } = 2.0; [DataMember] public int Norm { get; set; } = 2; [DataMember] public bool BaseC { get; set; } = true; public override List Errors { get { List errors = base.Errors; if (base.FeatureCount <= 0) { errors.Add("Positive number of chroma coefficients must be specified"); } if (Norm < 0) { errors.Add("Positive Norm must be specified"); } if (OctaveWidth < 0.0) { errors.Add("Positive octave width must be specified"); } return errors; } } public ChromaOptions() { base.FeatureCount = 12; base.Window = WindowType.Hann; } } [DataContract] public class FeatureExtractorOptions { [DataMember] public int FeatureCount { get; set; } [DataMember] public int SamplingRate { get; set; } [DataMember] public double FrameDuration { get; set; } = 0.025; [DataMember] public double HopDuration { get; set; } = 0.01; [DataMember] public int FrameSize { get; set; } [DataMember] public int HopSize { get; set; } [DataMember] public double PreEmphasis { get; set; } [DataMember] public WindowType Window { get; set; } public virtual List Errors { get { List list = new List(); if (SamplingRate <= 0) { list.Add("Positive sampling rate must be specified"); } if (FrameDuration <= 0.0 && FrameSize <= 0) { list.Add("Positive frame duration (in seconds) or frame size (in samples) must be specified"); } if (HopDuration <= 0.0 && HopSize <= 0) { list.Add("Positive hop duration (in seconds) or hop size (in samples) must be specified"); } return list; } } } public static class FeatureExtractorOptionsExtensions { public static void SaveOptions(this Stream stream, FeatureExtractorOptions options) { CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; try { using XmlDictionaryWriter writer = JsonReaderWriterFactory.CreateJsonWriter(stream, Encoding.UTF8, ownsStream: true, indent: true, " "); new DataContractJsonSerializer(options.GetType()).WriteObject(writer, options); stream.Flush(); } finally { Thread.CurrentThread.CurrentCulture = currentCulture; } } public static T LoadOptions(this Stream stream) where T : FeatureExtractorOptions { CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture; Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; try { return (T)new DataContractJsonSerializer(typeof(T)).ReadObject(stream); } finally { Thread.CurrentThread.CurrentCulture = currentCulture; } } public static U Cast(this T options) where T : FeatureExtractorOptions where U : FeatureExtractorOptions { byte[] buffer; using (MemoryStream memoryStream = new MemoryStream()) { memoryStream.SaveOptions(options); buffer = memoryStream.ToArray(); } using MemoryStream stream = new MemoryStream(buffer); return stream.LoadOptions(); } } [DataContract] public class FilterbankOptions : FeatureExtractorOptions { [DataMember] public float[][] FilterBank { get; set; } [DataMember] public int FilterBankSize { get; set; } = 12; [DataMember] public double LowFrequency { get; set; } [DataMember] public double HighFrequency { get; set; } [DataMember] public int FftSize { get; set; } [DataMember] public NonLinearityType NonLinearity { get; set; } = NonLinearityType.None; [DataMember] public SpectrumType SpectrumType { get; set; } = SpectrumType.Power; [DataMember] public float LogFloor { get; set; } = float.Epsilon; public override List Errors { get { List errors = base.Errors; if (FilterBank == null && FilterBankSize <= 0) { errors.Add("Positive number of filters must be specified"); } return errors; } } public FilterbankOptions() { base.Window = WindowType.Hamming; } } [DataContract] public class LpccOptions : LpcOptions { [DataMember] public int LifterSize { get; set; } = 22; public override List Errors { get { List errors = base.Errors; if (base.FeatureCount <= 0) { errors.Add("Positive number of LPCC coefficients must be specified"); } return errors; } } } [DataContract] public class LpcOptions : FeatureExtractorOptions { [DataMember] public int LpcOrder { get; set; } public override List Errors { get { List errors = base.Errors; if (LpcOrder <= 0) { errors.Add("Positive order of LPC must be specified"); } return errors; } } } [DataContract] public class MfccHtkOptions : MfccOptions { public MfccHtkOptions(int samplingRate, int featureCount, double frameDuration, double lowFrequency = 0.0, double highFrequency = 0.0, int filterbankSize = 24, int fftSize = 0) { int num = (int)(frameDuration * (double)samplingRate); fftSize = ((fftSize > num) ? fftSize : MathUtils.NextPowerOfTwo(num)); (double, double, double)[] frequencies = FilterBanks.MelBands(filterbankSize, samplingRate, lowFrequency, highFrequency); base.FilterBank = FilterBanks.Triangular(fftSize, samplingRate, frequencies, null, Scale.HerzToMel); base.FilterBankSize = filterbankSize; base.FeatureCount = featureCount; base.FftSize = fftSize; base.SamplingRate = samplingRate; base.LowFrequency = lowFrequency; base.HighFrequency = highFrequency; base.NonLinearity = NonLinearityType.LogE; base.LogFloor = 1f; } } [DataContract] public class MfccOptions : FilterbankOptions { [DataMember] public int LifterSize { get; set; } [DataMember] public string DctType { get; set; } = "2N"; [DataMember] public bool IncludeEnergy { get; set; } [DataMember] public float LogEnergyFloor { get; set; } = float.Epsilon; public override List Errors { get { List errors = base.Errors; if (base.FeatureCount <= 0) { errors.Add("Positive number of MFCC coefficients must be specified"); } if ((base.FilterBank == null && base.FilterBankSize < base.FeatureCount) || (base.FilterBank != null && base.FilterBank.Length < base.FeatureCount)) { errors.Add("Number of coefficients must not exceed number of filters"); } string item = "Supported DCT formats: 1, 2, 3, 4, 1N, 2N, 3N, 4N"; if (string.IsNullOrEmpty(DctType) || DctType.Length > 2) { errors.Add(item); } else if (!"1234".Contains(DctType.Substring(0, 1))) { errors.Add(item); } else if (DctType.Length == 2 && char.ToUpper(DctType[1]) != 'N') { errors.Add(item); } return errors; } } public MfccOptions() { base.FilterBankSize = 24; base.NonLinearity = NonLinearityType.Log10; base.Window = WindowType.Hamming; } } [DataContract] public class MfccSlaneyOptions : MfccOptions { public MfccSlaneyOptions(int samplingRate, int featureCount, double frameDuration, double lowFrequency = 0.0, double highFrequency = 0.0, int filterbankSize = 40, int fftSize = 0, bool normalize = true) { int num = (int)(frameDuration * (double)samplingRate); fftSize = ((fftSize > num) ? fftSize : MathUtils.NextPowerOfTwo(num)); base.FilterBank = FilterBanks.MelBankSlaney(filterbankSize, fftSize, samplingRate, lowFrequency, highFrequency, normalize); base.FilterBankSize = filterbankSize; base.FeatureCount = featureCount; base.FftSize = fftSize; base.SamplingRate = samplingRate; base.LowFrequency = lowFrequency; base.HighFrequency = highFrequency; base.NonLinearity = NonLinearityType.LogE; } } [DataContract] public class MultiFeatureOptions : FeatureExtractorOptions { [DataMember] public string FeatureList { get; set; } = "all"; [DataMember] public int FftSize { get; set; } [DataMember] public float[] Frequencies { get; set; } [DataMember] public (double, double, double)[] FrequencyBands { get; set; } [DataMember] public Dictionary Parameters { get; set; } } public enum NonLinearityType { LogE, Log10, ToDecibel, CubicRoot, None } [DataContract] public class PitchOptions : FeatureExtractorOptions { [DataMember] public double LowFrequency { get; set; } = 80.0; [DataMember] public double HighFrequency { get; set; } = 400.0; public override List Errors { get { List errors = base.Errors; if (LowFrequency >= HighFrequency) { errors.Add("Upper frequency must be greater than lower frequency"); } return errors; } } } [DataContract] public class PlpOptions : FilterbankOptions { [DataMember] public int LpcOrder { get; set; } [DataMember] public double Rasta { get; set; } [DataMember] public int LifterSize { get; set; } [DataMember] public double[] CenterFrequencies { get; set; } [DataMember] public bool IncludeEnergy { get; set; } [DataMember] public float LogEnergyFloor { get; set; } = float.Epsilon; public override List Errors { get { List errors = base.Errors; if (base.FeatureCount <= 0) { errors.Add("Positive number of PLP coefficients must be specified"); } return errors; } } public PlpOptions() { base.FilterBankSize = 24; base.Window = WindowType.Hamming; } } [DataContract] public class PnccOptions : FilterbankOptions { [DataMember] public int Power { get; set; } = 15; [DataMember] public bool IncludeEnergy { get; set; } [DataMember] public float LogEnergyFloor { get; set; } = float.Epsilon; public override List Errors { get { List errors = base.Errors; if (base.FeatureCount <= 0) { errors.Add("Positive number of PNCC coefficients must be specified"); } return errors; } } public PnccOptions() { base.LowFrequency = 100.0; base.HighFrequency = 6800.0; base.FilterBankSize = 40; base.Window = WindowType.Hamming; } } public enum SpectrumType { Magnitude, Power, MagnitudeNormalized, PowerNormalized } [DataContract] public class WaveletOptions : FeatureExtractorOptions { [DataMember] public string WaveletName { get; set; } = "haar"; [DataMember] public int FwtSize { get; set; } [DataMember] public int FwtLevel { get; set; } } } namespace NWaves.FeatureExtractors.Multi { public class Mpeg7SpectralFeaturesExtractor : FeatureExtractor { public const string FeatureSet = "centroid, spread, flatness, noiseness, rolloff, crest, entropy, decrease, loudness, sharpness"; public const string HarmonicSet = "hcentroid, hspread, inharmonicity, oer, t1+t2+t3"; protected readonly float[][] _filterbank; protected readonly (double, double, double)[] _frequencyBands; protected readonly float[] _frequencies; protected float[] _peakFrequencies; protected int[] _peaks; protected List> _extractors; protected readonly Dictionary _parameters; protected List> _harmonicExtractors; protected readonly RealFft _fft; protected readonly float[] _spectrum; protected readonly float[] _mappedSpectrum; protected Func _pitchEstimator; protected float[] _pitchTrack; protected int _pitchPos; protected Action _peaksDetector; public override List FeatureDescriptions { get; } public Mpeg7SpectralFeaturesExtractor(MultiFeatureOptions options) : base(options) { string text = options.FeatureList; if (text == "all" || text == "full") { text = "centroid, spread, flatness, noiseness, rolloff, crest, entropy, decrease, loudness, sharpness"; } List list = (from f in text.Split(',', '+', '-', ';', ':') select f.Trim().ToLower()).ToList(); _parameters = options.Parameters; _extractors = ((IEnumerable)list).Select((Func>)delegate(string feature) { switch (feature) { case "sc": case "centroid": return Spectral.Centroid; case "ss": case "spread": return Spectral.Spread; case "sfm": case "flatness": { Dictionary parameters = _parameters; if (parameters != null && parameters.ContainsKey("minLevel")) { float minLevel = (float)_parameters["minLevel"]; return (float[] spectrum, float[] freqs) => Spectral.Flatness(spectrum, minLevel); } return (float[] spectrum, float[] freqs) => Spectral.Flatness(spectrum); } case "sn": case "noiseness": { Dictionary parameters2 = _parameters; if (parameters2 != null && parameters2.ContainsKey("noiseFrequency")) { float noiseFrequency = (float)_parameters["noiseFrequency"]; return (float[] spectrum, float[] freqs) => Spectral.Noiseness(spectrum, freqs, noiseFrequency); } return (float[] spectrum, float[] freqs) => Spectral.Noiseness(spectrum, freqs); } case "rolloff": { Dictionary parameters3 = _parameters; if (parameters3 != null && parameters3.ContainsKey("rolloffPercent")) { float rolloffPercent = (float)_parameters["rolloffPercent"]; return (float[] spectrum, float[] freqs) => Spectral.Rolloff(spectrum, freqs, rolloffPercent); } return (float[] spectrum, float[] freqs) => Spectral.Rolloff(spectrum, freqs); } case "crest": return (float[] spectrum, float[] freqs) => Spectral.Crest(spectrum); case "entropy": case "ent": return (float[] spectrum, float[] freqs) => Spectral.Entropy(spectrum); case "sd": case "decrease": return (float[] spectrum, float[] freqs) => Spectral.Decrease(spectrum); case "loud": case "loudness": return (float[] spectrum, float[] freqs) => Perceptual.Loudness(spectrum); case "sharp": case "sharpness": return (float[] spectrum, float[] freqs) => Perceptual.Sharpness(spectrum); default: return (float[] spectrum, float[] freqs) => 0f; } }).ToList(); base.FeatureCount = list.Count; FeatureDescriptions = list; _blockSize = ((options.FftSize > base.FrameSize) ? options.FftSize : MathUtils.NextPowerOfTwo(base.FrameSize)); _fft = new RealFft(_blockSize); _frequencyBands = options.FrequencyBands ?? FilterBanks.OctaveBands(6, base.SamplingRate); _filterbank = FilterBanks.Rectangular(_blockSize, base.SamplingRate, _frequencyBands); List list2 = _frequencyBands.Select(((double, double, double) b) => b.Item2).ToList(); list2.Insert(0, 0.0); _frequencies = list2.ToFloats(); _spectrum = new float[_blockSize / 2 + 1]; _mappedSpectrum = new float[_filterbank.Length + 1]; } public void IncludeHarmonicFeatures(string featureList, int peakCount = 10, Func pitchEstimator = null, Action peaksDetector = null, float lowPitch = 80f, float highPitch = 400f) { if (featureList == "all" || featureList == "full") { featureList = "hcentroid, hspread, inharmonicity, oer, t1+t2+t3"; } List list = (from f in featureList.Split(',', '+', '-', ';', ':') select f.Trim().ToLower()).ToList(); _harmonicExtractors = ((IEnumerable)list).Select((Func>)delegate(string feature) { switch (feature) { case "hc": case "hcentroid": return Harmonic.Centroid; case "hs": case "hspread": return Harmonic.Spread; case "inh": case "inharmonicity": return Harmonic.Inharmonicity; case "oer": case "oddevenratio": return (float[] spectrum, int[] peaks, float[] freqs) => Harmonic.OddToEvenRatio(spectrum, peaks); case "t1": case "t2": case "t3": return (float[] spectrum, int[] peaks, float[] freqs) => Harmonic.Tristimulus(spectrum, peaks, int.Parse(feature.Substring(1))); default: return (float[] spectrum, int[] peaks, float[] freqs) => 0f; } }).ToList(); base.FeatureCount += list.Count; FeatureDescriptions.AddRange(list); if (pitchEstimator == null) { _pitchEstimator = (float[] spectrum) => Pitch.FromSpectralPeaks(spectrum, base.SamplingRate, lowPitch, highPitch); } else { _pitchEstimator = pitchEstimator; } if (peaksDetector == null) { _peaksDetector = Harmonic.Peaks; } else { _peaksDetector = peaksDetector; } _peaks = new int[peakCount]; _peakFrequencies = new float[peakCount]; } public void AddHarmonicFeature(string name, Func algorithm) { if (_harmonicExtractors != null) { base.FeatureCount++; FeatureDescriptions.Add(name); _harmonicExtractors.Add(algorithm); } } public void SetPitchTrack(float[] pitchTrack) { _pitchTrack = pitchTrack; } public override int ComputeFrom(float[] samples, int startSample, int endSample, IList vectors) { _pitchPos = 0; return base.ComputeFrom(samples, startSample, endSample, vectors); } public override void ProcessFrame(float[] block, float[] features) { _fft.MagnitudeSpectrum(block, _spectrum); for (int i = 0; i < _filterbank.Length; i++) { _mappedSpectrum[i + 1] = 0f; for (int j = 0; j < _spectrum.Length; j++) { _mappedSpectrum[i + 1] += _filterbank[i][j] * _spectrum[j]; } } for (int k = 0; k < _extractors.Count; k++) { features[k] = _extractors[k](_mappedSpectrum, _frequencies); } if (_harmonicExtractors != null) { float arg = ((_pitchTrack == null) ? _pitchEstimator(_spectrum) : _pitchTrack[_pitchPos++]); _peaksDetector(_spectrum, _peaks, _peakFrequencies, base.SamplingRate, arg); int count = _extractors.Count; for (int l = 0; l < _harmonicExtractors.Count; l++) { features[l + count] = _harmonicExtractors[l](_spectrum, _peaks, _peakFrequencies); } } } public override bool IsParallelizable() { return _pitchTrack == null; } public override FeatureExtractor ParallelCopy() { if (!IsParallelizable()) { return null; } string featureList = string.Join(",", FeatureDescriptions.Take(_extractors.Count)); Mpeg7SpectralFeaturesExtractor mpeg7SpectralFeaturesExtractor = new Mpeg7SpectralFeaturesExtractor(new MultiFeatureOptions { SamplingRate = base.SamplingRate, FeatureList = featureList, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, FftSize = _blockSize, FrequencyBands = _frequencyBands, PreEmphasis = _preEmphasis, Window = _window, Parameters = _parameters }) { _extractors = _extractors, _pitchTrack = _pitchTrack }; if (_harmonicExtractors != null) { string featureList2 = string.Join(",", FeatureDescriptions.Skip(_extractors.Count)); mpeg7SpectralFeaturesExtractor.IncludeHarmonicFeatures(featureList2, _peaks.Length, _pitchEstimator); } return mpeg7SpectralFeaturesExtractor; } } public class SpectralFeaturesExtractor : FeatureExtractor { public const string FeatureSet = "centroid, spread, flatness, noiseness, rolloff, crest, entropy, decrease, c1+c2+c3+c4+c5+c6"; protected List> _extractors; protected readonly Dictionary _parameters; protected readonly RealFft _fft; protected readonly float[] _frequencies; protected readonly float[] _spectrum; protected float[] _mappedSpectrum; protected readonly int[] _frequencyPositions; public override List FeatureDescriptions { get; } public SpectralFeaturesExtractor(MultiFeatureOptions options) : base(options) { string text = options.FeatureList; if (text == "all" || text == "full") { text = "centroid, spread, flatness, noiseness, rolloff, crest, entropy, decrease, c1+c2+c3+c4+c5+c6"; } List list = (from f in text.Split(',', '+', '-', ';', ':') select f.Trim().ToLower()).ToList(); _parameters = options.Parameters; _extractors = ((IEnumerable)list).Select((Func>)delegate(string feature) { switch (feature) { case "sc": case "centroid": return Spectral.Centroid; case "ss": case "spread": return Spectral.Spread; case "sfm": case "flatness": { Dictionary parameters2 = _parameters; if (parameters2 != null && parameters2.ContainsKey("minLevel")) { float minLevel = (float)_parameters["minLevel"]; return (float[] spectrum, float[] freqs) => Spectral.Flatness(spectrum, minLevel); } return (float[] spectrum, float[] freqs) => Spectral.Flatness(spectrum); } case "sn": case "noiseness": { Dictionary parameters3 = _parameters; if (parameters3 != null && parameters3.ContainsKey("noiseFrequency")) { float noiseFrequency = (float)_parameters["noiseFrequency"]; return (float[] spectrum, float[] freqs) => Spectral.Noiseness(spectrum, freqs, noiseFrequency); } return (float[] spectrum, float[] freqs) => Spectral.Noiseness(spectrum, freqs); } case "rolloff": { Dictionary parameters = _parameters; if (parameters != null && parameters.ContainsKey("rolloffPercent")) { float rolloffPercent = (float)_parameters["rolloffPercent"]; return (float[] spectrum, float[] freqs) => Spectral.Rolloff(spectrum, freqs, rolloffPercent); } return (float[] spectrum, float[] freqs) => Spectral.Rolloff(spectrum, freqs); } case "crest": return (float[] spectrum, float[] freqs) => Spectral.Crest(spectrum); case "entropy": case "ent": return (float[] spectrum, float[] freqs) => Spectral.Entropy(spectrum); case "sd": case "decrease": return (float[] spectrum, float[] freqs) => Spectral.Decrease(spectrum); case "c1": case "c2": case "c3": case "c4": case "c5": case "c6": return (float[] spectrum, float[] freqs) => Spectral.Contrast(spectrum, freqs, int.Parse(feature.Substring(1))); default: return (float[] spectrum, float[] freqs) => 0f; } }).ToList(); base.FeatureCount = list.Count; FeatureDescriptions = list; _blockSize = ((options.FftSize > base.FrameSize) ? options.FftSize : MathUtils.NextPowerOfTwo(base.FrameSize)); _fft = new RealFft(_blockSize); float[] frequencies = options.Frequencies; float resolution = (float)base.SamplingRate / (float)_blockSize; if (frequencies == null) { _frequencies = (from f in Enumerable.Range(0, _blockSize / 2 + 1) select (float)f * resolution).ToArray(); } else if (frequencies.Length == _blockSize / 2 + 1) { _frequencies = frequencies; } else { _frequencies = new float[frequencies.Length + 1]; frequencies.FastCopyTo(_frequencies, frequencies.Length, 0, 1); _mappedSpectrum = new float[_frequencies.Length]; _frequencyPositions = new int[_frequencies.Length]; for (int num = 1; num < _frequencies.Length; num++) { _frequencyPositions[num] = (int)(_frequencies[num] / resolution) + 1; } } _spectrum = new float[_blockSize / 2 + 1]; } public void AddFeature(string name, Func algorithm) { base.FeatureCount++; FeatureDescriptions.Insert(_extractors.Count, name); _extractors.Add(algorithm); } public override void ProcessFrame(float[] block, float[] features) { _fft.MagnitudeSpectrum(block, _spectrum); if (_spectrum.Length == _frequencies.Length) { _mappedSpectrum = _spectrum; } else { for (int i = 0; i < _mappedSpectrum.Length; i++) { _mappedSpectrum[i] = _spectrum[_frequencyPositions[i]]; } } for (int j = 0; j < _extractors.Count; j++) { features[j] = _extractors[j](_mappedSpectrum, _frequencies); } } public override bool IsParallelizable() { return true; } public override FeatureExtractor ParallelCopy() { string featureList = string.Join(",", FeatureDescriptions.Take(_extractors.Count)); return new SpectralFeaturesExtractor(new MultiFeatureOptions { SamplingRate = base.SamplingRate, FeatureList = featureList, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, FftSize = _blockSize, Frequencies = _frequencies, PreEmphasis = _preEmphasis, Window = _window, Parameters = _parameters }) { _extractors = _extractors }; } } public class TimeDomainFeaturesExtractor : FeatureExtractor { public const string FeatureSet = "energy, rms, zcr, entropy"; protected List> _extractors; protected readonly Dictionary _parameters; public override List FeatureDescriptions { get; } public TimeDomainFeaturesExtractor(MultiFeatureOptions options) : base(options) { string text = options.FeatureList; if (text == "all" || text == "full") { text = "energy, rms, zcr, entropy"; } List list = (from f in text.Split(',', '+', '-', ';', ':') select f.Trim().ToLower()).ToList(); _parameters = options.Parameters; _extractors = ((IEnumerable)list).Select((Func>)delegate(string feature) { switch (feature) { case "e": case "en": case "energy": return (DiscreteSignal signal, int start, int end) => signal.Energy(start, end); case "rms": return (DiscreteSignal signal, int start, int end) => signal.Rms(start, end); case "zcr": case "zero-crossing-rate": return (DiscreteSignal signal, int start, int end) => signal.ZeroCrossingRate(start, end); case "entropy": return (DiscreteSignal signal, int start, int end) => signal.Entropy(start, end); default: return (DiscreteSignal signal, int start, int end) => 0f; } }).ToList(); base.FeatureCount = list.Count; FeatureDescriptions = list; } public void AddFeature(string name, Func algorithm) { base.FeatureCount++; FeatureDescriptions.Add(name); _extractors.Add(algorithm); } public override int ComputeFrom(float[] samples, int startSample, int endSample, IList vectors) { DiscreteSignal arg = new DiscreteSignal(base.SamplingRate, samples); int num = 0; int num2 = startSample; while (num2 + base.FrameSize < endSample) { float[] array = vectors[num]; for (int i = 0; i < array.Length; i++) { array[i] = _extractors[i](arg, num2, num2 + base.FrameSize); } num2 += base.HopSize; num++; } return num; } public override void ProcessFrame(float[] block, float[] features) { throw new NotImplementedException("TimeDomainFeaturesExtractor does not provide this function. Please call ComputeFrom() method"); } public override bool IsParallelizable() { return true; } public override FeatureExtractor ParallelCopy() { return new TimeDomainFeaturesExtractor(new MultiFeatureOptions { SamplingRate = base.SamplingRate, FrameDuration = base.FrameDuration, HopDuration = base.HopDuration, FeatureList = string.Join(",", FeatureDescriptions), Parameters = _parameters }) { _extractors = _extractors }; } } } namespace NWaves.FeatureExtractors.Base { public abstract class FeatureExtractor : IFeatureExtractor, IParallelFeatureExtractor { protected int _blockSize; protected float _preEmphasis; protected readonly WindowType _window; protected readonly float[] _windowSamples; public int FeatureCount { get; protected set; } public abstract List FeatureDescriptions { get; } public virtual List DeltaFeatureDescriptions => FeatureDescriptions.Select((string d) => "delta_" + d).ToList(); public virtual List DeltaDeltaFeatureDescriptions => FeatureDescriptions.Select((string d) => "delta_delta_" + d).ToList(); public double FrameDuration { get; protected set; } public double HopDuration { get; protected set; } public int FrameSize { get; protected set; } public int HopSize { get; protected set; } public int SamplingRate { get; protected set; } protected FeatureExtractor(FeatureExtractorOptions options) { if (options.Errors.Count > 0) { throw new ArgumentException("Invalid configuration:\r\n" + string.Join("\r\n", options.Errors)); } SamplingRate = options.SamplingRate; if (options.FrameSize > 0) { FrameSize = options.FrameSize; FrameDuration = (double)FrameSize / (double)SamplingRate; } else { FrameDuration = options.FrameDuration; FrameSize = (int)Math.Round((double)SamplingRate * FrameDuration, MidpointRounding.AwayFromZero); } if (options.HopSize > 0) { HopSize = options.HopSize; HopDuration = (double)HopSize / (double)SamplingRate; } else { HopDuration = options.HopDuration; HopSize = (int)Math.Round((double)SamplingRate * HopDuration, MidpointRounding.AwayFromZero); } _blockSize = FrameSize; _preEmphasis = (float)options.PreEmphasis; _window = options.Window; if (_window != WindowType.Rectangular) { _windowSamples = Window.OfType(_window, FrameSize); } } public virtual int ComputeFrom(float[] samples, int startSample, int endSample, IList vectors) { Guard.AgainstInvalidRange(startSample, endSample, "starting pos", "ending pos"); int frameSize = FrameSize; int hopSize = HopSize; float num = ((startSample > 0) ? samples[startSample - 1] : 0f); int num2 = endSample - frameSize; float[] array = new float[_blockSize]; int num3 = 0; int num4 = startSample; while (num4 <= num2) { samples.FastCopyTo(array, frameSize, num4); int num5 = frameSize; while (num5 < array.Length) { array[num5++] = 0f; } if (_preEmphasis > 1E-10f) { for (int i = 0; i < frameSize; i++) { float num6 = array[i] - num * _preEmphasis; num = array[i]; array[i] = num6; } num = samples[num4 + hopSize - 1]; } if (_windowSamples != null) { array.ApplyWindow(_windowSamples); } ProcessFrame(array, vectors[num3]); num4 += hopSize; num3++; } return num3; } public virtual int ComputeFrom(float[] samples, IList vectors) { return ComputeFrom(samples, 0, samples.Length, vectors); } public virtual List ComputeFrom(float[] samples, int startSample, int endSample) { Guard.AgainstInvalidRange(startSample, endSample, "starting pos", "ending pos"); if (endSample - startSample < FrameSize) { return new List(); } int num = (endSample - FrameSize - startSample) / HopSize + 1; List list = new List(num); for (int i = 0; i < num; i++) { list.Add(new float[FeatureCount]); } ComputeFrom(samples, startSample, endSample, list); return list; } public virtual List TimeMarkers(int vectorCount, double startFrom = 0.0) { return (from x in Enumerable.Range(0, vectorCount) select startFrom + (double)x * HopDuration).ToList(); } public abstract void ProcessFrame(float[] block, float[] features); public List ComputeFrom(float[] samples) { return ComputeFrom(samples, 0, samples.Length); } public List ComputeFrom(DiscreteSignal signal, int startSample, int endSample) { return ComputeFrom(signal.Samples, startSample, endSample); } public List ComputeFrom(DiscreteSignal signal) { return ComputeFrom(signal.Samples, 0, signal.Length); } public virtual void Reset() { } public virtual bool IsParallelizable() { return false; } public virtual FeatureExtractor ParallelCopy() { return null; } public virtual List[] ParallelChunksComputeFrom(float[] samples, int startSample, int endSample, int parallelThreads = 0) { if (!IsParallelizable()) { throw new NotImplementedException("Current configuration of the extractor does not support parallel computation"); } int num = ((parallelThreads > 0) ? parallelThreads : Environment.ProcessorCount); int num2 = (endSample - startSample) / num; if (num2 < FrameSize) { return new List[1] { ComputeFrom(samples, startSample, endSample) }; } FeatureExtractor[] extractors = new FeatureExtractor[num]; extractors[0] = this; for (int i = 1; i < num; i++) { extractors[i] = ParallelCopy(); } int[] startPositions = new int[num]; int[] endPositions = new int[num]; int num3 = (num2 - FrameSize) / HopSize; int num4 = startSample - 1; for (int j = 0; j < num; j++) { startPositions[j] = num4 + 1; endPositions[j] = num4 + num3 * HopSize + FrameSize; num4 = endPositions[j] - FrameSize; } endPositions[num - 1] = endSample; List[] featureVectors = new List[num]; Parallel.For(0, num, delegate(int num5) { featureVectors[num5] = extractors[num5].ComputeFrom(samples, startPositions[num5], endPositions[num5]); }); return featureVectors; } public virtual List ParallelComputeFrom(float[] samples, int startSample, int endSample, int parallelThreads = 0) { List[] array = ParallelChunksComputeFrom(samples, startSample, endSample, parallelThreads); List list = new List(); List[] array2 = array; foreach (List collection in array2) { list.AddRange(collection); } return list; } public virtual List ParallelComputeFrom(float[] samples, int parallelThreads = 0) { return ParallelComputeFrom(samples, 0, samples.Length, parallelThreads); } public List ParallelComputeFrom(DiscreteSignal signal, int startSample, int endSample, int parallelThreads = 0) { return ParallelComputeFrom(signal.Samples, startSample, endSample, parallelThreads); } public List ParallelComputeFrom(DiscreteSignal signal, int parallelThreads = 0) { return ParallelComputeFrom(signal.Samples, 0, signal.Length, parallelThreads); } } public static class FeaturePostProcessing { public static void NormalizeMean(IList vectors) { if (vectors.Count < 2) { return; } int num = vectors[0].Length; int i; for (i = 0; i < num; i++) { float num2 = vectors.Average((float[] t) => t[i]); foreach (float[] vector in vectors) { vector[i] -= num2; } } } public static void NormalizeVariance(IList vectors, int bias = 1) { int n = vectors.Count; if (n < 2) { return; } int num = vectors[0].Length; for (int i = 0; i < num; i++) { float mean = vectors.Average((float[] t) => t[i]); float num2 = vectors.Sum((float[] t) => (t[i] - mean) * (t[i] - mean) / (float)(n - bias)); if (num2 < Math.Abs(1E-30f)) { num2 = 1f; } foreach (float[] vector in vectors) { vector[i] /= (float)Math.Sqrt(num2); } } } public static void AddDeltas(IList vectors, IList previous = null, IList next = null, bool includeDeltaDelta = true, int N = 2) { if (previous == null) { previous = new List(N); for (int i = 0; i < N; i++) { previous.Add(vectors[0]); } } if (next == null) { next = new List(N); for (int j = 0; j < N; j++) { next.Add(vectors.Last()); } } int num = vectors[0].Length; float[][] array = previous.Concat(vectors).Concat(next).ToArray(); int num2 = 2 * Enumerable.Range(1, N).Sum((int x) => x * x); int num3 = (includeDeltaDelta ? (3 * num) : (2 * num)); for (int num4 = N; num4 < array.Length - N; num4++) { float[] array2 = new float[num3]; for (int num5 = 0; num5 < num; num5++) { array2[num5] = vectors[num4 - N][num5]; } for (int num6 = 0; num6 < num; num6++) { float num7 = 0f; for (int num8 = 1; num8 <= N; num8++) { num7 += (float)num8 * (array[num4 + num8][num6] - array[num4 - num8][num6]); } array2[num6 + num] = num7 / (float)num2; } int num9 = num4; float[] array3 = (vectors[num4 - N] = array2); array[num9] = array3; } if (!includeDeltaDelta) { return; } for (int num10 = 1; num10 <= N; num10++) { array[num10 - 1] = vectors[0]; array[^num10] = vectors.Last(); } for (int num11 = N; num11 < array.Length - N; num11++) { for (int num12 = 0; num12 < num; num12++) { float num13 = 0f; for (int num14 = 1; num14 <= N; num14++) { num13 += (float)num14 * (array[num11 + num14][num12 + num] - array[num11 - num14][num12 + num]); } vectors[num11 - N][num12 + 2 * num] = num13 / (float)num2; } } } public static float[][] Join(params IList[] vectors) { int num = vectors.Length; switch (num) { case 0: throw new ArgumentException("Empty collection of feature vectors!"); case 1: return vectors.ElementAt(0).ToArray(); default: { int totalVectors = vectors[0].Count; if (vectors.Any((IList v) => v.Count != totalVectors)) { throw new InvalidOperationException("All sequences of feature vectors must have the same length!"); } int num2 = vectors.Sum((IList v) => v[0].Length); float[][] array = new float[totalVectors][]; for (int num3 = 0; num3 < array.Length; num3++) { float[] array2 = new float[num2]; int num4 = 0; for (int num5 = 0; num5 < num; num5++) { int num6 = vectors[num5][num3].Length; vectors[num5][num3].FastCopyTo(array2, num6, 0, num4); num4 += num6; } array[num3] = array2; } return array; } } } } public static class FeatureVectorExtensions { public static Dictionary Statistics(this float[] vector) { float mean = vector.Average(); return new Dictionary { { "min", vector.Min() }, { "max", vector.Max() }, { "mean", mean }, { "var", vector.Average((float v) => (v - mean) * (v - mean)) } }; } } public interface IFeatureExtractor { int FeatureCount { get; } List ComputeFrom(float[] samples); List ComputeFrom(float[] samples, int startSample, int endSample); int ComputeFrom(float[] samples, IList vectors); int ComputeFrom(float[] samples, int startSample, int endSample, IList vectors); void Reset(); } public interface IParallelFeatureExtractor { List ParallelComputeFrom(float[] samples, int parallelThreads); List ParallelComputeFrom(float[] samples, int startSample, int endSample, int parallelThreads); } public class OnlineFeatureExtractor : IFeatureExtractor { private readonly bool _ignoreLastSamples; private int _skippedCount; private float[] _tempBuffer; public FeatureExtractor Extractor { get; set; } public int FeatureCount => Extractor.FeatureCount; public OnlineFeatureExtractor(FeatureExtractor extractor, bool ignoreLastSamples = false, int maxDataSize = 0) { Extractor = extractor; _ignoreLastSamples = ignoreLastSamples; _tempBuffer = ((maxDataSize > 0) ? new float[maxDataSize] : new float[extractor.SamplingRate]); } public int VectorCount(int dataSize) { if (dataSize >= Extractor.FrameSize) { return dataSize / Extractor.HopSize + 1; } return 1; } public int VectorCountFromSeconds(double seconds) { return VectorCount((int)((double)Extractor.SamplingRate * seconds)); } public void EnsureSize(int dataSize) { if (_tempBuffer.Length < dataSize) { Array.Resize(ref _tempBuffer, dataSize); } } public void EnsureSizeFromSeconds(double seconds) { int num = (int)(seconds * (double)Extractor.SamplingRate) + 1; if (_tempBuffer.Length < num) { Array.Resize(ref _tempBuffer, num); } } public int ComputeFrom(float[] data, IList featureVectors) { if (_ignoreLastSamples) { return Extractor.ComputeFrom(data, 0, data.Length, featureVectors); } data.FastCopyTo(_tempBuffer, data.Length, 0, _skippedCount); int num = data.Length + _skippedCount; int num2 = Extractor.ComputeFrom(_tempBuffer, 0, num, featureVectors); _skippedCount = num - num2 * Extractor.HopSize; _tempBuffer.FastCopyTo(_tempBuffer, _skippedCount, num - _skippedCount); return num2; } public List ComputeFrom(float[] data) { int num = data.Length + _skippedCount; int num2 = ((num >= Extractor.FrameSize) ? ((num - Extractor.FrameSize) / Extractor.HopSize + 1) : 0); if (num2 == 0) { data.FastCopyTo(_tempBuffer, data.Length, 0, _skippedCount); _skippedCount += data.Length; return new List(); } List list = new List(num2); for (int i = 0; i < num2; i++) { list.Add(new float[Extractor.FeatureCount]); } ComputeFrom(data, list); return list; } public int ComputeFrom(float[] data, int startSample, int endSample, IList featureVectors) { Guard.AgainstInvalidRange(startSample, endSample, "starting pos", "ending pos"); return ComputeFrom(data.FastCopyFragment(endSample - startSample + 1, startSample), featureVectors); } public List ComputeFrom(float[] data, int startSample, int endSample) { Guard.AgainstInvalidRange(startSample, endSample, "starting pos", "ending pos"); return ComputeFrom(data.FastCopyFragment(endSample - startSample + 1, startSample)); } public void Reset() { Array.Clear(_tempBuffer, 0, _tempBuffer.Length); _skippedCount = 0; } } } namespace NWaves.Effects { public class AutowahEffect : AudioEffect { private readonly int _fs; private readonly EnvelopeFollower _envelopeFollower; private float _yh; private float _yb; private float _yl; public float Q { get; set; } public float MinFrequency { get; set; } public float MaxFrequency { get; set; } public float AttackTime { get { return _envelopeFollower.AttackTime; } set { _envelopeFollower.AttackTime = value; } } public float ReleaseTime { get { return _envelopeFollower.ReleaseTime; } set { _envelopeFollower.ReleaseTime = value; } } public AutowahEffect(int samplingRate, float minFrequency = 30f, float maxFrequency = 2000f, float q = 0.5f, float attackTime = 0.01f, float releaseTime = 0.05f) { _fs = samplingRate; MinFrequency = minFrequency; MaxFrequency = maxFrequency; Q = q; _envelopeFollower = new EnvelopeFollower(samplingRate, attackTime, releaseTime); } public override float Process(float sample) { double num = (double)_envelopeFollower.Process(sample) * Math.Sqrt(Q); double num2 = Math.PI * (double)(MaxFrequency - MinFrequency) / (double)_fs; double num3 = Math.PI * (double)MinFrequency / (double)_fs; double a = num * num2 + num3; float num4 = (float)(2.0 * Math.Sin(a)); _yh = sample - _yl - Q * _yb; _yb += num4 * _yh; _yl += num4 * _yb; return base.Wet * _yb + base.Dry * sample; } public override void Reset() { _yh = (_yl = (_yb = 0f)); _envelopeFollower.Reset(); } } public class BitCrusherEffect : AudioEffect { private float _step; private int _bitDepth; public int BitDepth { get { return _bitDepth; } set { _bitDepth = value; _step = 2f * (float)Math.Pow(0.5, _bitDepth); } } public BitCrusherEffect(int bitDepth) { BitDepth = bitDepth; } public override float Process(float sample) { return (float)((double)_step * Math.Floor((double)(sample / _step) + 0.5)) * base.Wet + sample * base.Dry; } public override void Reset() { } } public class ChorusEffect : AudioEffect { private float[] _lfoFrequencies; private readonly VibratoEffect[] _voices; public float[] Widths { get { return _voices.Select((VibratoEffect v) => v.Width).ToArray(); } set { for (int i = 0; i < _voices.Length; i++) { _voices[i].Width = value[i]; } } } public float[] LfoFrequencies { get { return _lfoFrequencies; } set { _lfoFrequencies = value; for (int i = 0; i < _voices.Length; i++) { _voices[i].LfoFrequency = value[i]; } } } public ChorusEffect(int samplingRate, float[] lfoFrequencies, float[] widths) { Guard.AgainstInequality(lfoFrequencies.Length, widths.Length, "Size of frequency array", "size of widths array"); _lfoFrequencies = lfoFrequencies; _voices = new VibratoEffect[widths.Length]; for (int i = 0; i < _voices.Length; i++) { _voices[i] = new VibratoEffect(samplingRate, lfoFrequencies[i], widths[i]); } } public ChorusEffect(int samplingRate, SignalBuilder[] lfos, float[] widths) { Guard.AgainstInequality(lfos.Length, widths.Length, "Size of frequency array", "number of LFOs"); _voices = new VibratoEffect[widths.Length]; for (int i = 0; i < _voices.Length; i++) { _voices[i] = new VibratoEffect(samplingRate, lfos[i], widths[i]); } } public override float Process(float sample) { float num = _voices.Sum((VibratoEffect v) => v.Process(sample)) / (float)_voices.Length; return sample * base.Dry + num * base.Wet; } public override void Reset() { VibratoEffect[] voices = _voices; for (int i = 0; i < voices.Length; i++) { voices[i].Reset(); } } } public class DelayEffect : AudioEffect { private readonly FractionalDelayLine _delayLine; private readonly int _fs; private float _delay; public float Delay { get { return _delay / (float)_fs; } set { _delayLine.Ensure(_fs, value); _delay = (float)_fs * value; } } public float Feedback { get; set; } public DelayEffect(int samplingRate, float delay, float feedback = 0.5f, InterpolationMode interpolationMode = InterpolationMode.Nearest, float reserveDelay = 0f) { _fs = samplingRate; if (reserveDelay < delay) { _delayLine = new FractionalDelayLine(samplingRate, delay, interpolationMode); } else { _delayLine = new FractionalDelayLine(samplingRate, reserveDelay, interpolationMode); } Delay = delay; Feedback = feedback; } public override float Process(float sample) { float num = _delayLine.Read(_delay); float num2 = sample + num * Feedback; _delayLine.Write(sample); return sample * base.Dry + num2 * base.Wet; } public override void Reset() { _delayLine.Reset(); } } public class DistortionEffect : AudioEffect { private float _inputGain; private float _outputGain; public DistortionMode Mode { get; set; } public float InputGain { get { return (float)Scale.ToDecibel(_inputGain); } set { _inputGain = (float)Scale.FromDecibel(value); } } public float OutputGain { get { return (float)Scale.ToDecibel(_outputGain); } set { _outputGain = (float)Scale.FromDecibel(value); } } public DistortionEffect(DistortionMode mode, float inputGain = 12f, float outputGain = -12f) { Mode = mode; InputGain = inputGain; OutputGain = outputGain; } public override float Process(float sample) { sample *= _inputGain; float num; switch (Mode) { case DistortionMode.HardClipping: num = ((!(sample > 0.5f)) ? ((!(sample < -0.5f)) ? sample : (-0.5f)) : 0.5f); break; case DistortionMode.Exponential: num = ((!(sample > 0f)) ? ((float)(-1.0 + Math.Exp(sample))) : ((float)(1.0 - Math.Exp(0f - sample)))); break; case DistortionMode.FullWaveRectify: num = Math.Abs(sample); break; case DistortionMode.HalfWaveRectify: num = ((!(sample < 0f)) ? Math.Abs(sample) : 0f); break; default: num = ((sample > 2f / 3f) ? 1f : ((sample > 1f / 3f) ? (1f - (2f - 3f * sample) * (2f - 3f * sample) / 3f) : ((sample < -2f / 3f) ? (-1f) : ((!(sample < -1f / 3f)) ? (2f * sample) : (-1f + (2f + 3f * sample) * (2f + 3f * sample) / 3f))))); num *= 0.5f; break; } num *= _outputGain; return num * base.Wet + sample * base.Dry; } public override void Reset() { } } public enum DistortionMode { SoftClipping, HardClipping, Exponential, FullWaveRectify, HalfWaveRectify } public class EchoEffect : AudioEffect { private readonly FractionalDelayLine _delayLine; private readonly int _fs; private float _delay; public float Delay { get { return _delay / (float)_fs; } set { _delayLine.Ensure(_fs, value); _delay = (float)_fs * value; } } public float Feedback { get; set; } public EchoEffect(int samplingRate, float delay, float feedback = 0.5f, InterpolationMode interpolationMode = InterpolationMode.Nearest, float reserveDelay = 0f) { _fs = samplingRate; if (reserveDelay < delay) { _delayLine = new FractionalDelayLine(samplingRate, delay, interpolationMode); } else { _delayLine = new FractionalDelayLine(samplingRate, reserveDelay, interpolationMode); } Delay = delay; Feedback = feedback; } public override float Process(float sample) { float num = _delayLine.Read(_delay); float num2 = sample + num * Feedback; _delayLine.Write(num2); return sample * base.Dry + num2 * base.Wet; } public override void Reset() { _delayLine.Reset(); } } public class FlangerEffect : AudioEffect { private readonly FractionalDelayLine _delayLine; private readonly int _fs; private float _width; private float _lfoFrequency; private SignalBuilder _lfo; public float Width { get { return _width; } set { _delayLine.Ensure(_fs, value); _width = value; } } public float LfoFrequency { get { return _lfoFrequency; } set { _lfoFrequency = value; _lfo.SetParameter("freq", value); } } public SignalBuilder Lfo { get { return _lfo; } set { _lfo = value; _lfo.SetParameter("min", 0.0).SetParameter("max", 1.0); } } public float Depth { get; set; } public float Feedback { get; set; } public bool Inverted { get; set; } public InterpolationMode InterpolationMode { get { return _delayLine.InterpolationMode; } set { _delayLine.InterpolationMode = value; } } public FlangerEffect(int samplingRate, float lfoFrequency = 1f, float width = 0.003f, float depth = 0.5f, float feedback = 0f, bool inverted = false, InterpolationMode interpolationMode = InterpolationMode.Linear, float reserveWidth = 0f) : this(samplingRate, new SineBuilder().SampledAt(samplingRate), width, depth, feedback, inverted, interpolationMode, reserveWidth) { LfoFrequency = lfoFrequency; } public FlangerEffect(int samplingRate, SignalBuilder lfo, float width = 0.003f, float depth = 0.5f, float feedback = 0f, bool inverted = false, InterpolationMode interpolationMode = InterpolationMode.Linear, float reserveWidth = 0f) { _fs = samplingRate; _width = width; Depth = depth; Feedback = feedback; Inverted = inverted; Lfo = lfo; if (reserveWidth < width) { _delayLine = new FractionalDelayLine(samplingRate, width, interpolationMode); } else { _delayLine = new FractionalDelayLine(samplingRate, reserveWidth, interpolationMode); } } public override float Process(float sample) { float num = _lfo.NextSample() * _width * (float)_fs; float num2 = _delayLine.Read(num); _delayLine.Write(sample + Feedback * num2); if (!Inverted) { return base.Dry * sample + base.Wet * Depth * num2; } return base.Dry * sample - base.Wet * Depth * num2; } public override void Reset() { _delayLine.Reset(); _lfo.Reset(); } } public class MorphEffect : AudioEffect { private readonly int _hopSize; private readonly int _fftSize; private readonly int _overlapSize; private readonly RealFft _fft; private readonly float[] _window; private readonly float[] _dl1; private readonly float[] _dl2; private int _inOffset; private int _outOffset; private readonly float[] _re1; private readonly float[] _re2; private readonly float[] _im1; private readonly float[] _im2; private readonly float[] _filteredRe; private readonly float[] _filteredIm; private readonly float[] _lastSaved; public MorphEffect(int hopSize, int fftSize = 0) { _hopSize = hopSize; _fftSize = ((fftSize > 0) ? fftSize : (8 * hopSize)); _overlapSize = _fftSize - _hopSize; Guard.AgainstInvalidRange(_hopSize, _fftSize, "Hop size", "FFT size"); _fft = new RealFft(_fftSize); _window = Window.OfType(WindowType.Hann, _fftSize); _dl1 = new float[_fftSize]; _re1 = new float[_fftSize]; _im1 = new float[_fftSize]; _dl2 = new float[_fftSize]; _re2 = new float[_fftSize]; _im2 = new float[_fftSize]; _filteredRe = new float[_fftSize]; _filteredIm = new float[_fftSize]; _lastSaved = new float[_overlapSize]; } public float Process(float sample, float mix) { _dl1[_inOffset] = sample; _dl2[_inOffset] = mix; _inOffset++; if (_inOffset == _fftSize) { ProcessFrame(); } return _filteredRe[_outOffset++]; } protected void ProcessFrame() { _dl1.FastCopyTo(_re1, _fftSize); _dl2.FastCopyTo(_re2, _fftSize); _re1.ApplyWindow(_window); _re2.ApplyWindow(_window); _fft.Direct(_re1, _re1, _im1); _fft.Direct(_re2, _re2, _im2); for (int i = 1; i <= _fftSize / 2; i++) { double num = Math.Sqrt(_re1[i] * _re1[i] + _im1[i] * _im1[i]); double num2 = Math.Atan2(_im2[i], _re2[i]); _filteredRe[i] = (float)(num * Math.Cos(num2)); _filteredIm[i] = (float)(num * Math.Sin(num2)); } _fft.Inverse(_filteredRe, _filteredIm, _filteredRe); _filteredRe.ApplyWindow(_window); for (int j = 0; j < _overlapSize; j++) { _filteredRe[j] += _lastSaved[j]; } _filteredRe.FastCopyTo(_lastSaved, _overlapSize, _hopSize); for (int k = 0; k < _filteredRe.Length; k++) { _filteredRe[k] *= base.Wet / (float)_fftSize; _filteredRe[k] += _dl2[k] * base.Dry; } _dl1.FastCopyTo(_dl1, _overlapSize, _hopSize); _dl2.FastCopyTo(_dl2, _overlapSize, _hopSize); _inOffset = _overlapSize; _outOffset = 0; } public override void Reset() { _inOffset = _overlapSize; _outOffset = 0; Array.Clear(_dl1, 0, _dl1.Length); Array.Clear(_re1, 0, _re1.Length); Array.Clear(_im1, 0, _im1.Length); Array.Clear(_dl2, 0, _dl2.Length); Array.Clear(_re2, 0, _re2.Length); Array.Clear(_im2, 0, _im2.Length); Array.Clear(_filteredRe, 0, _filteredRe.Length); Array.Clear(_filteredIm, 0, _filteredIm.Length); Array.Clear(_lastSaved, 0, _lastSaved.Length); } public DiscreteSignal ApplyTo(DiscreteSignal signal, DiscreteSignal mix) { Guard.AgainstInequality(signal.SamplingRate, mix.SamplingRate, "Input signal sampling rate", "Mix signal sampling rate"); float[] array = new float[signal.Length]; int num = 0; int num2 = 0; while (num < array.Length) { if (num2 == mix.Length) { num2 = 0; } array[num] = Process(signal[num], mix[num2]); num++; num2++; } return new DiscreteSignal(signal.SamplingRate, array); } public override float Process(float sample) { throw new NotImplementedException(); } } public class PhaserEffect : AudioEffect { private float _lfoFrequency; private float _minFrequency; private float _maxFrequency; private readonly int _fs; private readonly NotchFilter _filter; public float Q { get; set; } public float LfoFrequency { get { return _lfoFrequency; } set { _lfoFrequency = value; Lfo.SetParameter("freq", value); } } public float MinFrequency { get { return _minFrequency; } set { _minFrequency = value; Lfo.SetParameter("min", value); } } public float MaxFrequency { get { return _maxFrequency; } set { _maxFrequency = value; Lfo.SetParameter("max", value); } } public SignalBuilder Lfo { get; set; } public PhaserEffect(int samplingRate, float lfoFrequency = 1f, float minFrequency = 300f, float maxFrequency = 3000f, float q = 0.5f) { _fs = samplingRate; Lfo = new TriangleWaveBuilder().SampledAt(samplingRate); LfoFrequency = lfoFrequency; MinFrequency = minFrequency; MaxFrequency = maxFrequency; Q = q; _filter = new NotchFilter(Lfo.NextSample() / (float)_fs, Q); } public PhaserEffect(int samplingRate, SignalBuilder lfo, float q = 0.5f) { _fs = samplingRate; Q = q; Lfo = lfo; _filter = new NotchFilter(Lfo.NextSample() / (float)_fs, Q); } public override float Process(float sample) { float num = _filter.Process(sample); _filter.Change(Lfo.NextSample() / (float)_fs, Q); return num * base.Wet + sample * base.Dry; } public override void Reset() { _filter.Reset(); Lfo.Reset(); } } public class PitchShiftEffect : AudioEffect { public double Shift { get; set; } public TsmAlgorithm Tsm { get; set; } public int WindowSize { get; set; } public int HopSize { get; set; } public PitchShiftEffect(double shift, int windowSize = 1024, int hopSize = 128, TsmAlgorithm tsm = TsmAlgorithm.PhaseVocoderPhaseLocking) { Shift = shift; WindowSize = windowSize; HopSize = hopSize; Tsm = tsm; } public override DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { DiscreteSignal discreteSignal = Operation.TimeStretch(signal, Shift, WindowSize, HopSize, Tsm); float[] x = Enumerable.Range(0, discreteSignal.Length).Select((Func)((int s) => s)).ToArray(); float[] array = (from s in Enumerable.Range(0, signal.Length) select (float)(Shift * (double)s)).ToArray(); float[] array2 = new float[array.Length]; MathUtils.InterpolateLinear(x, discreteSignal.Samples, array, array2); for (int num = 0; num < array2.Length; num++) { array2[num] = signal[num] * base.Dry + array2[num] * base.Wet; } return new DiscreteSignal(signal.SamplingRate, array2); } public override float Process(float sample) { throw new NotImplementedException(); } public override void Reset() { } } public class PitchShiftVocoderEffect : OverlapAddFilter { private readonly float _freqResolution; private readonly float[] _mag; private readonly float[] _phase; private readonly float[] _prevPhase; private readonly float[] _phaseTotal; public float Shift { get; set; } public PitchShiftVocoderEffect(int samplingRate, double shift, int fftSize = 1024, int hopSize = 64) : base(hopSize, fftSize) { Shift = (float)shift; _gain = (float)(Math.PI * 2.0 / (double)((float)_fftSize * _window.Select((float w) => w * w).Sum() / (float)_hopSize)); _freqResolution = samplingRate / _fftSize; _mag = new float[_fftSize / 2 + 1]; _phase = new float[_fftSize / 2 + 1]; _prevPhase = new float[_fftSize / 2 + 1]; _phaseTotal = new float[_fftSize / 2 + 1]; } protected override void ProcessSpectrum(float[] re, float[] im, float[] filteredRe, float[] filteredIm) { float num = (float)(Math.PI * 2.0 * (double)_hopSize / (double)_fftSize); for (int i = 1; i <= _fftSize / 2; i++) { _mag[i] = (float)Math.Sqrt(re[i] * re[i] + im[i] * im[i]); _phase[i] = (float)Math.Atan2(im[i], re[i]); float num2 = _phase[i] - _prevPhase[i]; _prevPhase[i] = _phase[i]; double num3 = MathUtils.Mod((double)(num2 - (float)i * num) + Math.PI, Math.PI * 2.0) - Math.PI; _phase[i] = _freqResolution * ((float)i + (float)num3 / num); } Array.Clear(re, 0, _fftSize); Array.Clear(im, 0, _fftSize); int num4 = 0; for (int j = 0; j <= _fftSize / 2; j++) { if (num4 > _fftSize / 2) { break; } re[num4] += _mag[j]; im[num4] = _phase[j] * Shift; num4 = (int)((float)j * Shift); } for (int k = 1; k <= _fftSize / 2; k++) { float num5 = re[k]; float num6 = (im[k] - (float)k * _freqResolution) / _freqResolution; _phaseTotal[k] += num * (num6 + (float)k); filteredRe[k] = (float)((double)num5 * Math.Cos(_phaseTotal[k])); filteredIm[k] = (float)((double)num5 * Math.Sin(_phaseTotal[k])); } } public override void Reset() { base.Reset(); Array.Clear(_prevPhase, 0, _prevPhase.Length); Array.Clear(_phaseTotal, 0, _phaseTotal.Length); } } public class RobotEffect : OverlapAddFilter { public RobotEffect(int hopSize, int fftSize = 0) : base(hopSize, fftSize) { _gain *= MathF.PI; } protected override void ProcessSpectrum(float[] re, float[] im, float[] filteredRe, float[] filteredIm) { for (int i = 0; i <= _fftSize / 2; i++) { filteredRe[i] = (float)Math.Sqrt(re[i] * re[i] + im[i] * im[i]); filteredIm[i] = 0f; } } } public class TremoloEffect : AudioEffect { private float _frequency; private float _index; public float Depth { get; set; } public float Frequency { get { return _frequency; } set { _frequency = value; Lfo.SetParameter("freq", value); } } public float Index { get { return _index; } set { _index = value; Lfo.SetParameter("min", 0.0).SetParameter("max", value * 2f); } } public SignalBuilder Lfo { get; set; } public TremoloEffect(int samplingRate, float depth = 0.5f, float frequency = 10f, float tremoloIndex = 0.5f) { Lfo = new CosineBuilder().SampledAt(samplingRate); Depth = depth; Frequency = frequency; Index = tremoloIndex; } public TremoloEffect(SignalBuilder lfo, float depth = 0.5f) { Lfo = lfo; Depth = depth; } public override float Process(float sample) { return sample * (1f - Depth + Depth * Lfo.NextSample()) * base.Wet + sample * base.Dry; } public override void Reset() { Lfo.Reset(); } } public class TubeDistortionEffect : AudioEffect { private float _inputGain; private float _outputGain; private readonly LtiFilter _outputFilter; public float InputGain { get { return (float)Scale.ToDecibel(_inputGain); } set { _inputGain = (float)Scale.FromDecibel(value); } } public float OutputGain { get { return (float)Scale.ToDecibel(_outputGain); } set { _outputGain = (float)Scale.FromDecibel(value); } } public float Q { get; set; } public float Dist { get; set; } public float Rh { get; } public float Rl { get; } public TubeDistortionEffect(float inputGain = 20f, float outputGain = -12f, float q = -0.2f, float dist = 5f, float rh = 0.995f, float rl = 0.5f) { InputGain = inputGain; OutputGain = outputGain; Q = q; Dist = dist; Rh = rh; Rl = rl; IirFilter iirFilter = new IirFilter(new double[3] { 1.0, -2.0, 1.0 }, new double[3] { 1.0, -2f * Rh, Rh * Rh }); IirFilter iirFilter2 = new IirFilter(new double[1] { 1.0 - (double)Rl }, new double[2] { 1.0, 0f - Rl }); _outputFilter = iirFilter * iirFilter2; } public override float Process(float sample) { float num = sample * _inputGain; float sample2 = ((!((double)Math.Abs(Q) < 1E-10)) ? (((double)Math.Abs(num - Q) < 1E-10) ? ((float)(1.0 / (double)Dist + (double)Q / (1.0 - Math.Exp(Dist * Q)))) : ((float)((double)(num - Q) / (1.0 - Math.Exp((0f - Dist) * (num - Q))) + (double)Q / (1.0 - Math.Exp(Dist * Q))))) : (((double)Math.Abs(num - Q) < 1E-10) ? (1f / Dist) : ((float)((double)num / (1.0 - Math.Exp((0f - Dist) * num)))))); sample2 = _outputFilter.Process(sample2) * _outputGain; return sample2 * base.Wet + sample * base.Dry; } public override void Reset() { _outputFilter.Reset(); } } public class VibratoEffect : AudioEffect { private readonly FractionalDelayLine _delayLine; private readonly int _fs; private float _width; private float _lfoFrequency = 1f; private SignalBuilder _lfo; public float Width { get { return _width; } set { _delayLine.Ensure(_fs, value); _width = value; } } public float LfoFrequency { get { return _lfoFrequency; } set { _lfoFrequency = value; _lfo.SetParameter("freq", value); } } public SignalBuilder Lfo { get { return _lfo; } set { _lfo = value; _lfo.SetParameter("min", 0.0).SetParameter("max", 1.0); } } public InterpolationMode InterpolationMode { get { return _delayLine.InterpolationMode; } set { _delayLine.InterpolationMode = value; } } public VibratoEffect(int samplingRate, float lfoFrequency = 1f, float width = 0.003f, InterpolationMode interpolationMode = InterpolationMode.Linear, float reserveWidth = 0f) : this(samplingRate, new SineBuilder().SampledAt(samplingRate), width, interpolationMode, reserveWidth) { LfoFrequency = lfoFrequency; } public VibratoEffect(int samplingRate, SignalBuilder lfo, float width = 0.003f, InterpolationMode interpolationMode = InterpolationMode.Linear, float reserveWidth = 0f) { _fs = samplingRate; _width = width; Lfo = lfo; if (reserveWidth < width) { _delayLine = new FractionalDelayLine(samplingRate, width, interpolationMode); } else { _delayLine = new FractionalDelayLine(samplingRate, reserveWidth, interpolationMode); } } public override float Process(float sample) { float num = _lfo.NextSample() * _width * (float)_fs; float num2 = _delayLine.Read(num); _delayLine.Write(sample); return base.Dry * sample + base.Wet * num2; } public override void Reset() { _delayLine.Reset(); _lfo.Reset(); } } public class WahwahEffect : AudioEffect { private float _lfoFrequency; private float _minFrequency; private float _maxFrequency; private readonly int _fs; private float _yh; private float _yb; private float _yl; public float LfoFrequency { get { return _lfoFrequency; } set { _lfoFrequency = value; Lfo.SetParameter("freq", value); } } public float MinFrequency { get { return _minFrequency; } set { _minFrequency = value; Lfo.SetParameter("min", value); } } public float MaxFrequency { get { return _maxFrequency; } set { _maxFrequency = value; Lfo.SetParameter("max", value); } } public float Q { get; set; } public SignalBuilder Lfo { get; set; } public WahwahEffect(int samplingRate, float lfoFrequency = 1f, float minFrequency = 300f, float maxFrequency = 1500f, float q = 0.5f) { _fs = samplingRate; Lfo = new TriangleWaveBuilder().SampledAt(samplingRate); LfoFrequency = lfoFrequency; MinFrequency = minFrequency; MaxFrequency = maxFrequency; Q = q; } public WahwahEffect(int samplingRate, SignalBuilder lfo, float q = 0.5f) { _fs = samplingRate; Q = q; Lfo = lfo; } public override float Process(float sample) { double num = Math.PI * 2.0 / (double)_fs; float num2 = (float)(2.0 * Math.Sin((double)Lfo.NextSample() * num)); _yh = sample - _yl - Q * _yb; _yb += num2 * _yh; _yl += num2 * _yb; return _yb * base.Wet + sample * base.Dry; } public override void Reset() { _yh = (_yb = (_yl = 0f)); Lfo.Reset(); } } public class WhisperEffect : OverlapAddFilter { private readonly Random _rand = new Random(); public WhisperEffect(int hopSize, int fftSize = 0) : base(hopSize, fftSize) { _gain = 1f / (float)_fftSize; } protected override void ProcessSpectrum(float[] re, float[] im, float[] filteredRe, float[] filteredIm) { for (int i = 1; i <= _fftSize / 2; i++) { double num = Math.Sqrt(re[i] * re[i] + im[i] * im[i]); double num2 = Math.PI * 2.0 * _rand.NextDouble(); filteredRe[i] = (float)(num * Math.Cos(num2)); filteredIm[i] = (float)(num * Math.Sin(num2)); } } } } namespace NWaves.Effects.Stereo { public class BinauralPanEffect : StereoEffect { private readonly float[] _leftEarHrir; private readonly float[] _rightEarHrir; private readonly float[][][] _leftHrirTable; private readonly float[][][] _rightHrirTable; private readonly float[] _azimuths; private readonly float[] _elevations; private readonly OlsBlockConvolver _leftEarConvolver; private readonly OlsBlockConvolver _rightEarConvolver; private bool _useCrossover; private IOnlineFilter _crossoverLpFilterLeft; private IOnlineFilter _crossoverLpFilterRight; private IOnlineFilter _crossoverHpFilterLeft; private IOnlineFilter _crossoverHpFilterRight; private float _azimuth; private float _elevation; public float Azimuth { get { return _azimuth; } set { _azimuth = value; UpdateHrir(_azimuth, _elevation); } } public float Elevation { get { return _elevation; } set { _elevation = value; UpdateHrir(_azimuth, _elevation); } } public BinauralPanEffect(float[] azimuths, float[] elevations, float[][][] leftHrirs, float[][][] rightHrirs) { _azimuths = azimuths ?? throw new ArgumentNullException("azimuths"); _elevations = elevations ?? throw new ArgumentNullException("elevations"); _leftHrirTable = leftHrirs ?? throw new ArgumentNullException("leftHrirs"); _rightHrirTable = rightHrirs ?? throw new ArgumentNullException("rightHrirs"); if (leftHrirs.Any((float[][] h) => h == null) || rightHrirs.Any((float[][] h) => h == null)) { throw new ArgumentNullException("One of HRIRs is null!"); } if (leftHrirs.Any((float[][] h) => h.Any((float[] r) => r == null)) || rightHrirs.Any((float[][] h) => h.Any((float[] r) => r == null))) { throw new ArgumentNullException("One of HRIRs is null!"); } int hrirLength = leftHrirs[0][0].Length; if (leftHrirs.Any((float[][] h) => h.Any((float[] r) => r.Length != hrirLength)) || rightHrirs.Any((float[][] h) => h.Any((float[] r) => r.Length != hrirLength))) { throw new ArgumentException("All HRIRs must have the same size!"); } _leftEarHrir = new float[hrirLength]; _rightEarHrir = new float[hrirLength]; _leftEarConvolver = new OlsBlockConvolver(_leftEarHrir, MathUtils.NextPowerOfTwo(4 * hrirLength)); _rightEarConvolver = new OlsBlockConvolver(_rightEarHrir, MathUtils.NextPowerOfTwo(4 * hrirLength)); _crossoverLpFilterLeft = new NWaves.Filters.BiQuad.LowPassFilter(0.01); _crossoverHpFilterLeft = new NWaves.Filters.BiQuad.HighPassFilter(0.01); _crossoverLpFilterRight = new NWaves.Filters.BiQuad.LowPassFilter(0.01); _crossoverHpFilterRight = new NWaves.Filters.BiQuad.HighPassFilter(0.01); UpdateHrir(0f, 0f); } public void UseCrossover(bool useCrossover) { _useCrossover = useCrossover; } public void SetCrossoverParameters(double freq, int samplingRate) { if (_crossoverLpFilterLeft is NWaves.Filters.BiQuad.LowPassFilter lowPassFilter) { lowPassFilter.Change(freq / (double)samplingRate); } if (_crossoverLpFilterRight is NWaves.Filters.BiQuad.LowPassFilter lowPassFilter2) { lowPassFilter2.Change(freq / (double)samplingRate); } if (_crossoverHpFilterLeft is NWaves.Filters.BiQuad.HighPassFilter highPassFilter) { highPassFilter.Change(freq / (double)samplingRate); } if (_crossoverHpFilterRight is NWaves.Filters.BiQuad.HighPassFilter highPassFilter2) { highPassFilter2.Change(freq / (double)samplingRate); } } public void SetCrossoverFilters(IOnlineFilter lowpassLeft, IOnlineFilter highpassLeft, IOnlineFilter lowpassRight, IOnlineFilter highpassRight) { _crossoverLpFilterLeft = lowpassLeft; _crossoverHpFilterLeft = highpassLeft; _crossoverLpFilterRight = lowpassRight; _crossoverHpFilterRight = highpassRight; } protected void UpdateHrir(float azimuth, float elevation) { int num = 0; int num2 = 0; float[] azimuths = _azimuths; for (int i = 0; i < azimuths.Length && !(azimuths[i] >= azimuth); i++) { num++; } azimuths = _elevations; for (int i = 0; i < azimuths.Length && !(azimuths[i] >= elevation); i++) { num2++; } int num3 = num - 1; int num4 = num2 - 1; if (num3 < 0 || num4 < 0) { _leftHrirTable[num][num2].FastCopyTo(_leftEarHrir, _leftEarHrir.Length); _rightHrirTable[num][num2].FastCopyTo(_rightEarHrir, _rightEarHrir.Length); } else if (num == _azimuths.Length || num2 == _elevations.Length) { _leftHrirTable[num3][num4].FastCopyTo(_leftEarHrir, _leftEarHrir.Length); _rightHrirTable[num3][num4].FastCopyTo(_rightEarHrir, _rightEarHrir.Length); } else { if (_azimuths[num] - azimuth > azimuth - _azimuths[num3]) { int num5 = num; num = num3; num3 = num5; } if (_elevations[num2] - elevation > elevation - _elevations[num4]) { int num6 = num2; num2 = num4; num4 = num6; } float num7 = _azimuths[num]; float num8 = _azimuths[num3]; float num9 = _elevations[num2]; float num10 = _elevations[num4]; float num11 = ((num10 - num9) * (azimuth - num8) + (num8 - num7) * (elevation - num9)) / ((num10 - num9) * (num7 - num8)); float num12 = (num7 - num8) * (elevation - num9) / ((num10 - num9) * (num7 - num8)); float num13 = 1f - num11 - num12; float[] array = _leftHrirTable[num][num2]; float[] array2 = _leftHrirTable[num][num4]; float[] array3 = _leftHrirTable[num3][num2]; float[] array4 = _rightHrirTable[num][num2]; float[] array5 = _rightHrirTable[num][num4]; float[] array6 = _rightHrirTable[num3][num2]; for (int j = 0; j < _leftEarHrir.Length; j++) { _leftEarHrir[j] = num11 * array[j] + num12 * array2[j] + num13 * array3[j]; _rightEarHrir[j] = num11 * array4[j] + num12 * array5[j] + num13 * array6[j]; } } _leftEarConvolver.ChangeKernel(_leftEarHrir); _rightEarConvolver.ChangeKernel(_rightEarHrir); } public override void Process(ref float left, ref float right) { float num = left; float num2 = right; float num3 = 0f; float num4 = 0f; if (_useCrossover) { num3 = _crossoverLpFilterLeft.Process(left); num4 = _crossoverLpFilterRight.Process(right); left = _crossoverHpFilterLeft.Process(left); right = _crossoverHpFilterRight.Process(right); } left = _leftEarConvolver.Process(left); right = _rightEarConvolver.Process(right); left += num3; right += num4; left = num * base.Dry + left * base.Wet; right = num2 * base.Dry + right * base.Wet; } public override void Reset() { _leftEarConvolver.Reset(); _rightEarConvolver.Reset(); _crossoverLpFilterLeft?.Reset(); _crossoverLpFilterRight?.Reset(); _crossoverHpFilterLeft?.Reset(); _crossoverHpFilterRight?.Reset(); } } public class ItdIldPanEffect : StereoEffect { private const float SpeedOfSound = 340f; private const double Pi2 = Math.PI / 2.0; private readonly float _headRadius; private readonly int _samplingRate; private readonly float _headFactor; private readonly FractionalDelayLine _itdDelayLeft; private readonly FractionalDelayLine _itdDelayRight; private readonly BiQuadFilter _ildFilterLeft; private readonly BiQuadFilter _ildFilterRight; private double _delayLeft; private double _delayRight; private float _pan; public float HeadRadius => _headRadius; public float Pan { get { return _pan; } set { _pan = value; double num = (double)_pan * (Math.PI / 2.0); _delayLeft = Itd(num + Math.PI / 2.0); _delayRight = Itd(num - Math.PI / 2.0); float num2 = 1f + (float)Math.Cos(num + Math.PI / 2.0); float num3 = 1f + (float)Math.Cos(num - Math.PI / 2.0); _ildFilterLeft.Change(_headFactor + num2, _headFactor - num2, 0f, _headFactor + 1f, _headFactor - 1f, 0f); _ildFilterRight.Change(_headFactor + num3, _headFactor - num3, 0f, _headFactor + 1f, _headFactor - 1f, 0f); } } private double Itd(double angle) { if (Math.Abs(angle) < Math.PI / 2.0) { return (double)_headFactor * (1.0 - Math.Cos(angle)); } return (double)_headFactor * (Math.Abs(angle) + 1.0 - Math.PI / 2.0); } public ItdIldPanEffect(int samplingRate, float pan, InterpolationMode interpolationMode = InterpolationMode.Linear, double reserveDelay = 0.005, float headRadius = 0.085f) { _samplingRate = samplingRate; _headRadius = headRadius; _headFactor = _headRadius / 340f; _itdDelayLeft = new FractionalDelayLine(samplingRate, reserveDelay, interpolationMode); _itdDelayRight = new FractionalDelayLine(samplingRate, reserveDelay, interpolationMode); _ildFilterLeft = new BiQuadFilter(1.0, 0.0, 0.0, 0.0, 0.0, 0.0); _ildFilterRight = new BiQuadFilter(1.0, 0.0, 0.0, 0.0, 0.0, 0.0); Pan = pan; } public override void Process(ref float left, ref float right) { float num = left; float num2 = right; _itdDelayLeft.Write(left); _itdDelayRight.Write(right); left = _itdDelayLeft.Read(_delayLeft * (double)_samplingRate); right = _itdDelayRight.Read(_delayRight * (double)_samplingRate); left = _ildFilterLeft.Process(left); right = _ildFilterRight.Process(right); left = num * base.Dry + left * base.Wet; right = num2 * base.Dry + right * base.Wet; } public override void Reset() { _itdDelayLeft.Reset(); _itdDelayRight.Reset(); _ildFilterLeft.Reset(); _ildFilterRight.Reset(); } } public class PanEffect : StereoEffect { protected float _pan; protected float _mappedPan; protected float _constantPowerPan; public float Pan { get { return _pan; } set { _pan = value; if (_pan > 1f) { _pan = 1f; } if (_pan < -1f) { _pan = -1f; } _mappedPan = (_pan + 1f) / 2f; _constantPowerPan = (float)(Math.PI * (double)(_pan + 1f) / 4.0); } } public PanRule PanRule { get; set; } public PanEffect(float pan, PanRule panRule) { Pan = pan; PanRule = panRule; } public override void Process(ref float left, ref float right) { float num = left; float num2 = right; switch (PanRule) { case PanRule.Balanced: left *= 2f * Math.Min(0.5f, 1f - _mappedPan); right *= 2f * Math.Min(0.5f, _mappedPan); break; case PanRule.ConstantPower: left *= (float)Math.Cos(_constantPowerPan); right *= (float)Math.Sin(_constantPowerPan); break; case PanRule.Sin3Db: { double num7 = Math.Sqrt(2.0); left *= (float)(num7 * Math.Cos(Math.PI / 2.0 * (double)_mappedPan)); right *= (float)(num7 * Math.Sin(Math.PI / 2.0 * (double)_mappedPan)); break; } case PanRule.Sin4_5Db: { double num6 = Math.Pow(2.0, 0.75); left *= (float)(num6 * Math.Pow(Math.Cos(Math.PI / 2.0 * (double)_mappedPan), 1.5)); right *= (float)(num6 * Math.Pow(Math.Sin(Math.PI / 2.0 * (double)_mappedPan), 1.5)); break; } case PanRule.Sin6Db: { double num5 = 2.0; left *= (float)(num5 * Math.Pow(Math.Cos(Math.PI / 2.0 * (double)_mappedPan), 2.0)); right *= (float)(num5 * Math.Pow(Math.Sin(Math.PI / 2.0 * (double)_mappedPan), 2.0)); break; } case PanRule.SqRoot3Db: { double num4 = Math.Sqrt(2.0); left *= (float)(num4 * Math.Sqrt(1f - _mappedPan)); right *= (float)(num4 * Math.Sqrt(_mappedPan)); break; } case PanRule.SqRoot4_5Db: { double num3 = Math.Pow(2.0, 0.75); left *= (float)(num3 * Math.Pow(1f - _mappedPan, 1.5)); right *= (float)(num3 * Math.Pow(_mappedPan, 1.5)); break; } default: left *= 2f * (1f - _mappedPan); right *= 2f * _mappedPan; break; } left = num * base.Dry + left * base.Wet; right = num2 * base.Dry + right * base.Wet; } public override void Reset() { } } public enum PanRule { Linear, Balanced, ConstantPower, Sin3Db, Sin4_5Db, Sin6Db, SqRoot3Db, SqRoot4_5Db } public class PingPongDelayEffect : StereoEffect { private readonly FractionalDelayLine _delayLineLeft; private readonly FractionalDelayLine _delayLineRight; private readonly int _fs; private float _delay; public float Pan { get; set; } public float Delay { get { return _delay / (float)_fs; } set { _delayLineLeft.Ensure(_fs, value); _delayLineRight.Ensure(_fs, value); _delay = (float)_fs * value; } } public float Feedback { get; set; } public PingPongDelayEffect(int samplingRate, float pan, float delay, float feedback = 0.5f, InterpolationMode interpolationMode = InterpolationMode.Nearest, float reserveDelay = 0f) { _fs = samplingRate; if (reserveDelay < delay) { _delayLineLeft = new FractionalDelayLine(samplingRate, delay, interpolationMode); _delayLineRight = new FractionalDelayLine(samplingRate, delay, interpolationMode); } else { _delayLineLeft = new FractionalDelayLine(samplingRate, reserveDelay, interpolationMode); _delayLineRight = new FractionalDelayLine(samplingRate, reserveDelay, interpolationMode); } Delay = delay; Feedback = feedback; Pan = pan; } public override void Process(ref float left, ref float right) { float num = _delayLineLeft.Read(_delay); float num2 = _delayLineRight.Read(_delay); float num3 = left * (1f - Pan) + num2 * Feedback; float num4 = right * Pan + num * Feedback; _delayLineLeft.Write(num3); _delayLineRight.Write(num4); left = left * base.Dry + num3 * base.Wet; right = right * base.Dry + num4 * base.Wet; } public override void Reset() { _delayLineLeft.Reset(); _delayLineRight.Reset(); } } public class StereoDelayEffect : StereoEffect { private readonly DelayEffect _delayEffectLeft; private readonly DelayEffect _delayEffectRight; public float DelayLeft { get { return _delayEffectLeft.Delay; } set { _delayEffectLeft.Delay = value; } } public float DelayRight { get { return _delayEffectRight.Delay; } set { _delayEffectRight.Delay = value; } } public float FeedbackLeft { get { return _delayEffectLeft.Feedback; } set { _delayEffectLeft.Feedback = value; } } public float FeedbackRight { get { return _delayEffectRight.Feedback; } set { _delayEffectRight.Feedback = value; } } public float Pan { get; set; } public StereoDelayEffect(int samplingRate, float pan, float delayLeft, float delayRight, float feedbackLeft = 0.5f, float feedbackRight = 0.5f, InterpolationMode interpolationMode = InterpolationMode.Nearest, float reserveDelay = 0f) { _delayEffectLeft = new DelayEffect(samplingRate, delayLeft, feedbackLeft, interpolationMode, reserveDelay); _delayEffectRight = new DelayEffect(samplingRate, delayRight, feedbackRight, interpolationMode, reserveDelay); Pan = pan; } public override void Process(ref float left, ref float right) { float num = _delayEffectLeft.Process(left); float num2 = _delayEffectRight.Process(right); num *= 1f - Pan; num2 *= Pan; left = left * base.Dry + num * base.Wet; right = right * base.Dry + num2 * base.Wet; } public override void Reset() { _delayEffectLeft.Reset(); _delayEffectRight.Reset(); } } public abstract class StereoEffect : WetDryMixer { public abstract void Process(ref float left, ref float right); public virtual void Process(float sample, out float left, out float right) { left = (right = sample); Process(ref left, ref right); } public virtual void Process(float[] inputLeft, float[] inputRight, float[] outputLeft, float[] outputRight, int count = 0, int inputPos = 0, int outputPos = 0) { if (count <= 0) { count = Math.Min(inputLeft.Length, inputRight.Length); } int num = inputPos + count; int num2 = inputPos; int num3 = outputPos; while (num2 < num) { outputLeft[num3] = inputLeft[num2]; outputRight[num3] = inputRight[num2]; Process(ref outputLeft[num3], ref outputRight[num3]); num2++; num3++; } } public virtual void Process(float[] input, float[] outputLeft, float[] outputRight, int count = 0, int inputPos = 0, int outputPos = 0) { if (count <= 0) { count = input.Length; } int num = inputPos + count; int num2 = inputPos; int num3 = outputPos; while (num2 < num) { outputLeft[num3] = (outputRight[num3] = input[num2]); Process(ref outputLeft[num3], ref outputRight[num3]); num2++; num3++; } } public virtual (DiscreteSignal, DiscreteSignal) ApplyTo(DiscreteSignal signal) { int samplingRate = signal.SamplingRate; float[] array = new float[signal.Length]; float[] array2 = new float[signal.Length]; Process(signal.Samples, array, array2); return (new DiscreteSignal(samplingRate, array), new DiscreteSignal(samplingRate, array2)); } public virtual (DiscreteSignal, DiscreteSignal) ApplyTo(DiscreteSignal leftSignal, DiscreteSignal rightSignal) { int samplingRate = leftSignal.SamplingRate; int samplingRate2 = rightSignal.SamplingRate; float[] array = new float[leftSignal.Length]; float[] array2 = new float[rightSignal.Length]; Process(leftSignal.Samples, rightSignal.Samples, array, array2); return (new DiscreteSignal(samplingRate, array), new DiscreteSignal(samplingRate2, array2)); } public abstract void Reset(); } } namespace NWaves.Effects.Base { public abstract class AudioEffect : WetDryMixer, IFilter, IOnlineFilter { public abstract float Process(float sample); public abstract void Reset(); public virtual DiscreteSignal ApplyTo(DiscreteSignal signal, FilteringMethod method = FilteringMethod.Auto) { return this.FilterOnline(signal); } } public interface IMixable { float Wet { get; set; } float Dry { get; set; } } public enum MixingRule { Linear, Balanced, Sin3Db, Sin4_5Db, Sin6Db, SqRoot3Db, SqRoot4_5Db } public class WetDryMixer : IMixable { public float Wet { get; set; } = 1f; public float Dry { get; set; } public void WetDryMix(float mix, MixingRule mixingRule = MixingRule.Linear) { if (mix < 0f) { mix = 0f; } if (mix > 1f) { mix = 1f; } switch (mixingRule) { case MixingRule.Balanced: Dry = 2f * Math.Min(0.5f, 1f - mix); Wet = 2f * Math.Min(0.5f, mix); break; case MixingRule.Sin3Db: Dry = (float)Math.Cos(Math.PI / 2.0 * (double)mix); Wet = (float)Math.Sin(Math.PI / 2.0 * (double)mix); break; case MixingRule.Sin4_5Db: Dry = (float)Math.Pow(Math.Cos(Math.PI / 2.0 * (double)mix), 1.5); Wet = (float)Math.Pow(Math.Sin(Math.PI / 2.0 * (double)mix), 1.5); break; case MixingRule.Sin6Db: Dry = (float)Math.Pow(Math.Cos(Math.PI / 2.0 * (double)mix), 2.0); Wet = (float)Math.Pow(Math.Sin(Math.PI / 2.0 * (double)mix), 2.0); break; case MixingRule.SqRoot3Db: Dry = (float)Math.Sqrt(1f - mix); Wet = (float)Math.Sqrt(mix); break; case MixingRule.SqRoot4_5Db: Dry = (float)Math.Pow(1f - mix, 1.5); Wet = (float)Math.Pow(mix, 1.5); break; default: Dry = 1f - mix; Wet = mix; break; } } public void WetDryDb(double wetDb, double dryDb) { double num = Math.Pow(10.0, wetDb / 20.0); double num2 = Math.Pow(10.0, dryDb / 20.0); double num3 = num / (num + num2); WetDryMix((float)num3); } } } namespace NWaves.Audio { public static class ByteConverter { public static void ToFloats8Bit(byte[] bytes, float[][] floats, bool normalize = true) { int num = floats.Length; if (normalize) { for (int i = 0; i < num; i++) { int num2 = i; int num3 = 0; while (num2 < bytes.Length) { floats[i][num3] = (float)(bytes[num2] - 128) / 128f; num2 += num; num3++; } } return; } for (int j = 0; j < num; j++) { int num4 = j; int num5 = 0; while (num4 < bytes.Length) { floats[j][num5] = (int)bytes[num4]; num4 += num; num5++; } } } public static void FromFloats8Bit(float[][] floats, byte[] bytes, bool normalized = true) { int num = floats.Length; if (normalized) { for (int i = 0; i < num; i++) { int num2 = i; for (int j = 0; j < floats[i].Length; j++) { bytes[num2] = (byte)(floats[i][j] * 128f + 128f); num2 += num; } } return; } for (int k = 0; k < num; k++) { int num3 = k; for (int l = 0; l < floats[k].Length; l++) { bytes[num3] = (byte)floats[k][l]; num3 += num; } } } public static void ToFloats16Bit(byte[] bytes, float[][] floats, bool normalize = true, bool bigEndian = false) { int num = floats.Length; int num2 = num * 2; if (bigEndian) { if (normalize) { for (int i = 0; i < num; i++) { int num3 = 2 * i; int num4 = 0; while (num3 < bytes.Length) { floats[i][num4] = (float)(short)((bytes[num3] << 8) | bytes[num3 + 1]) / 32768f; num3 += num2; num4++; } } return; } for (int j = 0; j < num; j++) { int num5 = 2 * j; int num6 = 0; while (num5 < bytes.Length) { floats[j][num6] = (short)((bytes[num5] << 8) | bytes[num5 + 1]); num5 += num2; num6++; } } return; } if (normalize) { for (int k = 0; k < num; k++) { int num7 = 2 * k; int num8 = 0; while (num7 < bytes.Length) { floats[k][num8] = (float)(short)(bytes[num7] | (bytes[num7 + 1] << 8)) / 32768f; num7 += num2; num8++; } } return; } for (int l = 0; l < num; l++) { int num9 = 2 * l; int num10 = 0; while (num9 < bytes.Length) { floats[l][num10] = (short)(bytes[num9] | (bytes[num9 + 1] << 8)); num9 += num2; num10++; } } } public static void FromFloats16Bit(float[][] floats, byte[] bytes, bool normalized = true, bool bigEndian = false) { int num = floats.Length; int num2 = num * 2; if (bigEndian) { if (normalized) { for (int i = 0; i < num; i++) { int num3 = 2 * i; for (int j = 0; j < floats[i].Length; j++) { short num4 = (short)(floats[i][j] * 32768f); bytes[num3] = (byte)(num4 >> 8); bytes[num3 + 1] = (byte)num4; num3 += num2; } } return; } for (int k = 0; k < num; k++) { int num5 = 2 * k; for (int l = 0; l < floats[k].Length; l++) { short num6 = (short)floats[k][l]; bytes[num5] = (byte)(num6 >> 8); bytes[num5 + 1] = (byte)num6; num5 += num2; } } return; } if (normalized) { for (int m = 0; m < num; m++) { int num7 = 2 * m; for (int n = 0; n < floats[m].Length; n++) { short num8 = (short)(floats[m][n] * 32768f); bytes[num7] = (byte)num8; bytes[num7 + 1] = (byte)(num8 >> 8); num7 += num2; } } return; } for (int num9 = 0; num9 < num; num9++) { int num10 = 2 * num9; for (int num11 = 0; num11 < floats[num9].Length; num11++) { short num12 = (short)floats[num9][num11]; bytes[num10] = (byte)num12; bytes[num10 + 1] = (byte)(num12 >> 8); num10 += num2; } } } } public enum Channels { Left = 0, Right = 1, Sum = 253, Average = 254, Interleave = 255 } public class WaveFile : IAudioContainer { public short[] SupportedBitDepths = new short[4] { 8, 16, 24, 32 }; public List Signals { get; protected set; } public WaveFormat WaveFmt { get; protected set; } public DiscreteSignal this[Channels channel] { get { if (channel != Channels.Interleave && channel != Channels.Sum && channel != Channels.Average) { return Signals[(int)channel]; } if (WaveFmt.ChannelCount == 1) { return Signals[0]; } int length = Signals[0].Length; switch (channel) { case Channels.Sum: { float[] array3 = new float[length]; for (int m = 0; m < array3.Length; m++) { for (int n = 0; n < Signals.Count; n++) { array3[m] += Signals[n][m]; } } return new DiscreteSignal(WaveFmt.SamplingRate, array3); } case Channels.Average: { float[] array2 = new float[length]; for (int k = 0; k < array2.Length; k++) { for (int l = 0; l < Signals.Count; l++) { array2[k] += Signals[l][k]; } array2[k] /= Signals.Count; } return new DiscreteSignal(WaveFmt.SamplingRate, array2); } default: { float[] array = new float[WaveFmt.ChannelCount * length]; int num = 0; for (int i = 0; i < length; i++) { for (int j = 0; j < WaveFmt.ChannelCount; j++) { array[num++] = Signals[j][i]; } } return new DiscreteSignal(WaveFmt.SamplingRate, array); } } } } public WaveFile(Stream waveStream, bool normalized = true) { ReadWaveStream(waveStream, normalized); } public WaveFile(byte[] waveBytes, bool normalized = true) { using MemoryStream waveStream = new MemoryStream(waveBytes); ReadWaveStream(waveStream, normalized); } public WaveFile(byte[] waveBytes, int index, bool normalized = true) { using MemoryStream waveStream = new MemoryStream(waveBytes, index, waveBytes.Length - index); ReadWaveStream(waveStream, normalized); } protected void ReadWaveStream(Stream waveStream, bool normalized = true) { using BinaryReader binaryReader = new BinaryReader(waveStream, Encoding.ASCII, leaveOpen: true); if (binaryReader.ReadInt32() != 1179011410) { throw new FormatException("WAV data error: NOT RIFF!"); } binaryReader.ReadInt32(); if (binaryReader.ReadInt32() != 1163280727) { throw new FormatException("WAV data error: NOT WAVE!"); } long num; for (num = binaryReader.BaseStream.Position; num != binaryReader.BaseStream.Length - 1; num++) { binaryReader.BaseStream.Position = num; if (binaryReader.ReadInt32() == 544501094) { break; } } if (num == binaryReader.BaseStream.Length - 1) { throw new FormatException("WAV data error: NOT fmt !"); } int num2 = binaryReader.ReadInt32(); WaveFormat waveFmt = default(WaveFormat); waveFmt.AudioFormat = binaryReader.ReadInt16(); waveFmt.ChannelCount = binaryReader.ReadInt16(); waveFmt.SamplingRate = binaryReader.ReadInt32(); waveFmt.ByteRate = binaryReader.ReadInt32(); waveFmt.Align = binaryReader.ReadInt16(); waveFmt.BitsPerSample = binaryReader.ReadInt16(); WaveFmt = waveFmt; if (num2 == 18) { short count = binaryReader.ReadInt16(); binaryReader.ReadBytes(count); } long num3; for (num3 = binaryReader.BaseStream.Position; num3 != binaryReader.BaseStream.Length - 1; num3++) { binaryReader.BaseStream.Position = num3; if (binaryReader.ReadInt32() == 1635017060) { break; } } if (num3 == binaryReader.BaseStream.Length - 1) { throw new FormatException("WAV data error: NOT data!"); } int num4 = binaryReader.ReadInt32(); num4 /= waveFmt.ChannelCount; num4 /= waveFmt.BitsPerSample / 8; Signals = new List(); for (int i = 0; i < waveFmt.ChannelCount; i++) { Signals.Add(new DiscreteSignal(waveFmt.SamplingRate, num4)); } switch (waveFmt.BitsPerSample) { case 8: { for (int l = 0; l < num4; l++) { for (int m = 0; m < waveFmt.ChannelCount; m++) { Signals[m][l] = binaryReader.ReadByte() - 128; if (normalized) { Signals[m][l] /= 128f; } } } break; } case 16: { for (int num8 = 0; num8 < num4; num8++) { for (int num9 = 0; num9 < waveFmt.ChannelCount; num9++) { Signals[num9][num8] = binaryReader.ReadInt16(); if (normalized) { Signals[num9][num8] /= 32768f; } } } break; } case 32: if (waveFmt.AudioFormat == 1) { for (int n = 0; n < num4; n++) { for (int num5 = 0; num5 < waveFmt.ChannelCount; num5++) { Signals[num5][n] = binaryReader.ReadInt32(); if (normalized) { Signals[num5][n] /= 2.1474836E+09f; } } } } else { if (waveFmt.AudioFormat != 3) { break; } for (int num6 = 0; num6 < num4; num6++) { for (int num7 = 0; num7 < waveFmt.ChannelCount; num7++) { Signals[num7][num6] = binaryReader.ReadSingle(); } } } break; case 24: { for (int j = 0; j < num4; j++) { for (int k = 0; k < waveFmt.ChannelCount; k++) { byte b = binaryReader.ReadByte(); byte b2 = binaryReader.ReadByte(); byte b3 = binaryReader.ReadByte(); Signals[k][j] = (b << 8) | (b2 << 16) | (b3 << 24); if (normalized) { Signals[k][j] /= 2.1474836E+09f; } } } break; } default: throw new ArgumentException("Wrong bit depth! Supported values are: " + string.Join(", ", SupportedBitDepths)); } } public WaveFile(IList signals, short bitsPerSample = 16) { if (signals == null || !signals.Any()) { throw new ArgumentException("At least one signal must be provided"); } int samplingRate = signals[0].SamplingRate; if (signals.Any((DiscreteSignal s) => s.SamplingRate != samplingRate)) { throw new ArgumentException("Signals must be sampled at the same sampling rate"); } int length = signals[0].Length; if (signals.Any((DiscreteSignal s) => s.Length != length)) { throw new ArgumentException("Signals must have the same length"); } if (!SupportedBitDepths.Contains(bitsPerSample)) { throw new ArgumentException("Wrong bit depth! Supported values are: " + string.Join(", ", SupportedBitDepths)); } WaveFormat waveFmt = default(WaveFormat); waveFmt.AudioFormat = 1; waveFmt.ChannelCount = (short)signals.Count; waveFmt.BitsPerSample = bitsPerSample; waveFmt.Align = (short)(waveFmt.ChannelCount * waveFmt.BitsPerSample / 8); waveFmt.SamplingRate = samplingRate; waveFmt.ByteRate = waveFmt.SamplingRate * waveFmt.ChannelCount * waveFmt.BitsPerSample / 8; WaveFmt = waveFmt; Signals = signals.ToList(); } public WaveFile(DiscreteSignal signal, short bitsPerSample = 16) : this(new DiscreteSignal[1] { signal }, bitsPerSample) { } public byte[] GetBytes(bool normalized = true) { using MemoryStream memoryStream = new MemoryStream(); SaveTo(memoryStream, normalized); return memoryStream.ToArray(); } public void SaveTo(Stream waveStream, bool normalized = true) { using BinaryWriter binaryWriter = new BinaryWriter(waveStream, Encoding.ASCII, leaveOpen: true); int length = Signals[0].Length; binaryWriter.Write(1179011410); int num = length * WaveFmt.ChannelCount * WaveFmt.BitsPerSample / 8; int value = 36 + num; binaryWriter.Write(value); binaryWriter.Write(1163280727); binaryWriter.Write(544501094); binaryWriter.Write(16); binaryWriter.Write(WaveFmt.AudioFormat); binaryWriter.Write(WaveFmt.ChannelCount); binaryWriter.Write(WaveFmt.SamplingRate); binaryWriter.Write(WaveFmt.ByteRate); binaryWriter.Write(WaveFmt.Align); binaryWriter.Write(WaveFmt.BitsPerSample); binaryWriter.Write(1635017060); binaryWriter.Write(num); switch (WaveFmt.BitsPerSample) { case 8: { for (int num5 = 0; num5 < length; num5++) { for (int num6 = 0; num6 < WaveFmt.ChannelCount; num6++) { float num7 = (normalized ? (Signals[num6][num5] * 128f + 128f) : Signals[num6][num5]); binaryWriter.Write((sbyte)num7); } } break; } case 16: { for (int k = 0; k < length; k++) { for (int l = 0; l < WaveFmt.ChannelCount; l++) { float num3 = (normalized ? (Signals[l][k] * 32768f) : Signals[l][k]); binaryWriter.Write((short)num3); } } break; } case 32: { for (int m = 0; m < length; m++) { for (int n = 0; n < WaveFmt.ChannelCount; n++) { float num4 = (normalized ? (Signals[n][m] * 2.1474836E+09f) : Signals[n][m]); binaryWriter.Write((int)num4); } } break; } case 24: { for (int i = 0; i < length; i++) { for (int j = 0; j < WaveFmt.ChannelCount; j++) { int num2 = (int)(normalized ? (Signals[j][i] * 2.1474836E+09f) : Signals[j][i]); byte value2 = (byte)(num2 >> 8); binaryWriter.Write(value2); value2 = (byte)(num2 >> 16); binaryWriter.Write(value2); value2 = (byte)(num2 >> 24); binaryWriter.Write(value2); } } break; } } } } public struct WaveFormat { public short AudioFormat; public short ChannelCount; public int SamplingRate; public int ByteRate; public short Align; public short BitsPerSample; } } namespace NWaves.Audio.Mci { public static class Mci { [DllImport("winmm.dll", EntryPoint = "mciSendString")] public static extern int SendString(string command, StringBuilder returnValue, int returnLength, int winHandle); [DllImport("winmm.dll", EntryPoint = "mciGetErrorString")] public static extern uint GetErrorString(int dwError, StringBuilder lpstrBuffer, uint wLength); [DllImport("winmm.dll", EntryPoint = "mciExecute")] public static extern int Execute(string command); } public class MciAudioPlayer : IAudioPlayer { private string _alias; private int _pauseDuration; private DateTime _pauseTime; private bool _isPaused; public float Volume { get; set; } public async Task PlayAsync(string source, int startPos = 0, int endPos = -1) { if (_isPaused) { Resume(); return; } Stop(); _alias = Guid.NewGuid().ToString(); Mci.SendString($"open \"{source}\" type waveaudio alias {_alias}", null, 0, 0); Mci.SendString($"set {_alias} time format samples", null, 0, 0); StringBuilder stringBuilder = new StringBuilder(255); Mci.SendString($"status {_alias} length", stringBuilder, 255, 0); int num = int.Parse(stringBuilder.ToString()); StringBuilder stringBuilder2 = new StringBuilder(255); Mci.SendString($"status {_alias} samplespersec", stringBuilder2, 255, 0); int num2 = int.Parse(stringBuilder2.ToString()); Mci.SendString(string.Format("play {2} from {0} to {1} notify", startPos, endPos, _alias).Replace(" to -1", ""), null, 0, 0); string currentAlias = _alias; await Task.Delay((int)((double)num * 1000.0 / (double)num2)); while (_isPaused || _pauseDuration > 0) { if (_isPaused) { await Task.Delay(1000); } if (_pauseDuration > 0) { await Task.Delay(_pauseDuration); _pauseDuration = 0; } } if (currentAlias == _alias) { Stop(); } } public Task PlayAsync(DiscreteSignal signal, int startPos = 0, int endPos = -1, short bitDepth = 16) { throw new NotImplementedException(); } public void Pause() { if (_alias != null) { Mci.SendString($"pause {_alias}", null, 0, 0); _pauseTime = DateTime.Now; _isPaused = true; } } public void Resume() { if (_alias != null && _isPaused) { Mci.SendString($"resume {_alias}", null, 0, 0); TimeSpan timeSpan = DateTime.Now - _pauseTime; _pauseDuration += timeSpan.Duration().Seconds * 1000 + timeSpan.Duration().Milliseconds; _isPaused = false; } } public void Stop() { if (_alias != null) { if (_isPaused) { Resume(); } Mci.SendString($"stop {_alias}", null, 0, 0); Mci.SendString($"close {_alias}", null, 0, 0); _alias = null; } } } public class MciAudioRecorder : IAudioRecorder { public void StartRecording(int samplingRate = 44100, short channelCount = 1, short bitsPerSample = 16) { if (Mci.SendString("open new type waveaudio alias capture", null, 0, 0) != 0) { throw new InvalidOperationException("Could not open device for recording!"); } Mci.SendString($"set capture alignment {channelCount * bitsPerSample / 8} bitspersample {bitsPerSample} samplespersec {samplingRate} channels {channelCount} bytespersec {samplingRate * channelCount * bitsPerSample / 8} time format samples format tag pcm", null, 0, 0); Mci.SendString("record capture", null, 0, 0); } public void StopRecording(string destination) { Mci.SendString("stop capture", null, 0, 0); Mci.SendString($"save capture {destination}", null, 0, 0); Mci.SendString("close capture", null, 0, 0); } } } namespace NWaves.Audio.Interfaces { public interface IAudioContainer { List Signals { get; } DiscreteSignal this[Channels channel] { get; } } public interface IAudioPlayer { float Volume { get; set; } Task PlayAsync(DiscreteSignal signal, int startPos = 0, int endPos = -1, short bitDepth = 16); Task PlayAsync(string source, int startPos = 0, int endPos = -1); void Pause(); void Resume(); void Stop(); } public interface IAudioRecorder { void StartRecording(int samplingRate, short channelCount, short bitsPerSample); void StopRecording(string destination); } }