using System;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
namespace FftSharp
{
public enum WindowType
{
Bartlett,
Blackman,
BlackmanHarris,
Cosine,
FlatTop,
Hamming,
Hanning,
Kaiser,
Rectangular,
Tukey,
Welch
}
public enum WindowAveragingType
{
Averaging,
PeakHoldMax,
PeakHoldMin
}
public abstract class Window : IWindow
{
public abstract string Name { get; }
public abstract string Description { get; }
public override string ToString() => Name;
protected abstract double windowValue(int index, int size);
public virtual double[] Create(int size, bool normalize = false)
{
// save this window so it can be re-used if the next request is the same size
if (lastWindow.Length == size && lastWindowNormalize == normalize)
{
return lastWindow;
}
if (size <= 0)
{
return new double[0];
}
double[] window = new double[size];
for (int i = 0; i < size; i++)
window[i] = windowValue(i, size);
if (normalize)
NormalizeInPlace(window);
lastWindow = window;
lastWindowNormalize = normalize;
return window;
}
protected double[] lastWindow = new double[0];
protected bool lastWindowNormalize = false;
///
/// Multiply the array by this window and return the result as a new array
///
public double[] Apply(double[] input, bool normalize = false)
{
double[] window = Create(input.Length, normalize);
double[] output = new double[input.Length];
Parallel.For(0, input.Length, i => output[i] = input[i] * window[i]);
return output;
}
///
/// Multiply the array by this window, modifying it in place
///
public void ApplyInPlace(double[] input, bool normalize = false)
{
double[] window = Create(input.Length, normalize);
Parallel.For(0, input.Length, i => input[i] = input[i] * window[i]);
}
internal static void NormalizeInPlace(double[] values)
{
double sum = 0;
Parallel.For(0, values.Length, i => sum += values[i]);
Parallel.For(0, values.Length, i => values[i] /= sum);
}
///
/// Return an array containing all available windows.
/// Note that all windows returned will use the default constructor, but some
/// windows have customization options in their constructors if you create them individually.
///
public static IWindow[] GetWindows()
{
return Assembly.GetExecutingAssembly()
.GetTypes()
.Where(x => x.IsClass)
.Where(x => !x.IsAbstract)
.Where(x => x.GetInterfaces().Contains(typeof(IWindow)))
.Select(x => (IWindow)Activator.CreateInstance(x))
.ToArray();
}
public static IWindow GetWindow(WindowType type)
{
return GetWindows().First(w => type.ToString() == w.Name);
}
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] Rectangular(int pointCount) => new Windows.Rectangular().Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] Hanning(int pointCount) => new Windows.Hanning().Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] Hamming(int pointCount) => new Windows.Hanning().Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] Blackman(int pointCount) => new Windows.Blackman().Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] BlackmanCustom(int pointCount, double a = .42, double b = .5, double c = .08) => new Windows.Blackman(a, b, c).Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] BlackmanHarris(int pointCount) => new Windows.Blackman(0.42323, 0.49755, 0.07922).Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] FlatTop(int pointCount) => new Windows.FlatTop().Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] Bartlett(int pointCount) => new Windows.Bartlett().Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] Cosine(int pointCount) => new Windows.Cosine().Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] Kaiser(int pointCount, double beta) => new Windows.Kaiser(beta).Create(pointCount);
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static double[] Apply(double[] window, double[] signal)
{
if (window.Length != signal.Length)
throw new ArgumentException("window and signal must be same length");
double[] output = new double[window.Length];
for (int i = 0; i < signal.Length; i++)
output[i] = signal[i] * window[i];
return output;
}
[Obsolete("This method is obsolete. Create a window in the Windows namespace and interact with its methods.")]
public static void ApplyInPlace(double[] window, double[] signal)
{
if (window.Length != signal.Length)
throw new ArgumentException("window and signal must be same length");
Parallel.For(0, signal.Length, i => signal[i] = signal[i] * window[i]);
}
[Obsolete("Use GetWindows() instead")]
public static string[] GetWindowNames()
{
return typeof(Window)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(x => x.ReturnType.Equals(typeof(double[])))
.Where(x => x.GetParameters().Length == 1)
.Where(x => x.GetParameters()[0].ParameterType == typeof(int))
.Select(x => x.Name)
.ToArray();
}
[Obsolete("Use GetWindows() and work with the output instead")]
public static double[] GetWindowByName(string windowName, int pointCount)
{
MethodInfo[] windowInfos = typeof(Window)
.GetMethods(BindingFlags.Public | BindingFlags.Static)
.Where(x => x.ReturnType.Equals(typeof(double[])))
.Where(x => x.GetParameters().Length == 1)
.Where(x => x.GetParameters()[0].ParameterType == typeof(int))
.Where(x => x.Name == windowName)
.ToArray();
if (windowInfos.Length == 0)
throw new ArgumentException($"invalid window name: {windowName}");
object[] parameters = new object[] { pointCount };
double[] result = (double[])windowInfos[0].Invoke(null, parameters);
return result;
}
}
}