From 59f9d5ac5f669437502e59e737e14ebb9118fda5 Mon Sep 17 00:00:00 2001 From: Scott W Harden Date: Tue, 16 May 2023 22:39:52 -0400 Subject: [PATCH 1/5] =?UTF-8?q?FftSharp.Complex=20=E2=86=92=20System.Numer?= =?UTF-8?q?ics.Complex?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/FftSharp.Demo/FftSharp.Demo.csproj | 6 +- src/FftSharp.Demo/FormAudio.cs | 6 +- src/FftSharp.Demo/FormMicrophone.Designer.cs | 226 +-- src/FftSharp.Demo/FormMicrophone.cs | 30 +- src/FftSharp.Demo/FormMicrophone.resx | 62 +- src/FftSharp.Demo/FormQuickstart.cs | 3 +- src/FftSharp.Demo/FormWindowInspector.cs | 3 +- src/FftSharp.Tests/ComplexTests.cs | 1 + src/FftSharp.Tests/FftFreqTests.cs | 19 +- src/FftSharp.Tests/FftTests.cs | 2 +- src/FftSharp.Tests/LoadData.cs | 4 +- src/FftSharp.Tests/Mel.cs | 2 +- src/FftSharp.Tests/Padding.cs | 8 +- src/FftSharp.Tests/PeakFrequencyDetection.cs | 5 +- src/FftSharp.Tests/Quickstart.cs | 6 +- src/FftSharp.Tests/Readme.cs | 25 +- src/FftSharp.Tests/Value.cs | 1690 +++++++++--------- src/FftSharp.Tests/VsNumpy.cs | 42 +- src/FftSharp/Complex.cs | 12 + src/FftSharp/FFT.cs | 165 +- src/FftSharp/FftOperations.cs | 56 +- src/FftSharp/Filter.cs | 18 +- src/FftSharp/Mel.cs | 46 + src/FftSharp/Pad.cs | 23 + src/FftSharp/Transform.cs | 171 +- 25 files changed, 1385 insertions(+), 1246 deletions(-) create mode 100644 src/FftSharp/Mel.cs diff --git a/src/FftSharp.Demo/FftSharp.Demo.csproj b/src/FftSharp.Demo/FftSharp.Demo.csproj index 6af234d..37f462d 100644 --- a/src/FftSharp.Demo/FftSharp.Demo.csproj +++ b/src/FftSharp.Demo/FftSharp.Demo.csproj @@ -20,9 +20,9 @@ - - + + - + \ No newline at end of file diff --git a/src/FftSharp.Demo/FormAudio.cs b/src/FftSharp.Demo/FormAudio.cs index db1343b..5831706 100644 --- a/src/FftSharp.Demo/FormAudio.cs +++ b/src/FftSharp.Demo/FormAudio.cs @@ -1,4 +1,5 @@ -using System; +using ScottPlot.Drawing.Colormaps; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -117,7 +118,8 @@ private void UpdateWindowed(double[] audio) private void UpdateFFT(double[] audio) { - double[] ys = cbLog.Checked ? Transform.FFTpower(audio) : Transform.FFTmagnitude(audio); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(audio); + double[] ys = cbLog.Checked ? FftSharp.FFT.Power(spectrum) : FftSharp.FFT.Magnitude(spectrum); string yLabel = cbLog.Checked ? "Power (dB)" : "Magnitude (RMS²)"; plotFFT.Plot.Clear(); diff --git a/src/FftSharp.Demo/FormMicrophone.Designer.cs b/src/FftSharp.Demo/FormMicrophone.Designer.cs index 2a737de..d7982bd 100644 --- a/src/FftSharp.Demo/FormMicrophone.Designer.cs +++ b/src/FftSharp.Demo/FormMicrophone.Designer.cs @@ -28,150 +28,160 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); - this.cbDevices = new System.Windows.Forms.ComboBox(); - this.label1 = new System.Windows.Forms.Label(); - this.formsPlot1 = new ScottPlot.FormsPlot(); - this.timer1 = new System.Windows.Forms.Timer(this.components); - this.cbAutoAxis = new System.Windows.Forms.CheckBox(); - this.cbDecibel = new System.Windows.Forms.CheckBox(); - this.label3 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.label5 = new System.Windows.Forms.Label(); - this.lblPeak = new System.Windows.Forms.Label(); - this.cbPeak = new System.Windows.Forms.CheckBox(); - this.SuspendLayout(); + components = new System.ComponentModel.Container(); + cbDevices = new System.Windows.Forms.ComboBox(); + label1 = new System.Windows.Forms.Label(); + formsPlot1 = new ScottPlot.FormsPlot(); + timer1 = new System.Windows.Forms.Timer(components); + cbAutoAxis = new System.Windows.Forms.CheckBox(); + cbDecibel = new System.Windows.Forms.CheckBox(); + label3 = new System.Windows.Forms.Label(); + label4 = new System.Windows.Forms.Label(); + label5 = new System.Windows.Forms.Label(); + lblPeak = new System.Windows.Forms.Label(); + cbPeak = new System.Windows.Forms.CheckBox(); + SuspendLayout(); // // cbDevices // - this.cbDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.cbDevices.FormattingEnabled = true; - this.cbDevices.Location = new System.Drawing.Point(12, 25); - this.cbDevices.Name = "cbDevices"; - this.cbDevices.Size = new System.Drawing.Size(121, 21); - this.cbDevices.TabIndex = 0; - this.cbDevices.SelectedIndexChanged += new System.EventHandler(this.cbDevices_SelectedIndexChanged); + cbDevices.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + cbDevices.FormattingEnabled = true; + cbDevices.Location = new System.Drawing.Point(14, 29); + cbDevices.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + cbDevices.Name = "cbDevices"; + cbDevices.Size = new System.Drawing.Size(140, 23); + cbDevices.TabIndex = 0; + cbDevices.SelectedIndexChanged += cbDevices_SelectedIndexChanged; // // label1 // - this.label1.AutoSize = true; - this.label1.Location = new System.Drawing.Point(12, 9); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(71, 13); - this.label1.TabIndex = 1; - this.label1.Text = "Audio Device"; + label1.AutoSize = true; + label1.Location = new System.Drawing.Point(14, 10); + label1.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label1.Name = "label1"; + label1.Size = new System.Drawing.Size(77, 15); + label1.TabIndex = 1; + label1.Text = "Audio Device"; // // formsPlot1 // - this.formsPlot1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.formsPlot1.Location = new System.Drawing.Point(12, 52); - this.formsPlot1.Name = "formsPlot1"; - this.formsPlot1.Size = new System.Drawing.Size(776, 386); - this.formsPlot1.TabIndex = 2; + formsPlot1.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left | System.Windows.Forms.AnchorStyles.Right; + formsPlot1.Location = new System.Drawing.Point(14, 60); + formsPlot1.Margin = new System.Windows.Forms.Padding(5, 3, 5, 3); + formsPlot1.Name = "formsPlot1"; + formsPlot1.Size = new System.Drawing.Size(905, 445); + formsPlot1.TabIndex = 2; // // timer1 // - this.timer1.Enabled = true; - this.timer1.Interval = 20; - this.timer1.Tick += new System.EventHandler(this.timer1_Tick); + timer1.Enabled = true; + timer1.Interval = 20; + timer1.Tick += timer1_Tick; // // cbAutoAxis // - this.cbAutoAxis.AutoSize = true; - this.cbAutoAxis.Checked = true; - this.cbAutoAxis.CheckState = System.Windows.Forms.CheckState.Checked; - this.cbAutoAxis.Location = new System.Drawing.Point(139, 27); - this.cbAutoAxis.Name = "cbAutoAxis"; - this.cbAutoAxis.Size = new System.Drawing.Size(70, 17); - this.cbAutoAxis.TabIndex = 3; - this.cbAutoAxis.Text = "Auto-Axis"; - this.cbAutoAxis.UseVisualStyleBackColor = true; + cbAutoAxis.AutoSize = true; + cbAutoAxis.Checked = true; + cbAutoAxis.CheckState = System.Windows.Forms.CheckState.Checked; + cbAutoAxis.Location = new System.Drawing.Point(162, 31); + cbAutoAxis.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + cbAutoAxis.Name = "cbAutoAxis"; + cbAutoAxis.Size = new System.Drawing.Size(79, 19); + cbAutoAxis.TabIndex = 3; + cbAutoAxis.Text = "Auto-Axis"; + cbAutoAxis.UseVisualStyleBackColor = true; + cbAutoAxis.CheckedChanged += cbAutoAxis_CheckedChanged; // // cbDecibel // - this.cbDecibel.AutoSize = true; - this.cbDecibel.Location = new System.Drawing.Point(215, 27); - this.cbDecibel.Name = "cbDecibel"; - this.cbDecibel.Size = new System.Drawing.Size(39, 17); - this.cbDecibel.TabIndex = 4; - this.cbDecibel.Text = "dB"; - this.cbDecibel.UseVisualStyleBackColor = true; + cbDecibel.AutoSize = true; + cbDecibel.Location = new System.Drawing.Point(251, 31); + cbDecibel.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + cbDecibel.Name = "cbDecibel"; + cbDecibel.Size = new System.Drawing.Size(40, 19); + cbDecibel.TabIndex = 4; + cbDecibel.Text = "dB"; + cbDecibel.UseVisualStyleBackColor = true; + cbDecibel.CheckedChanged += cbDecibel_CheckedChanged; // // label3 // - this.label3.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.label3.AutoSize = true; - this.label3.ForeColor = System.Drawing.SystemColors.ControlDark; - this.label3.Location = new System.Drawing.Point(676, 22); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(95, 13); - this.label3.TabIndex = 6; - this.label3.Text = "Left-click-drag pan"; + label3.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right; + label3.AutoSize = true; + label3.ForeColor = System.Drawing.SystemColors.ControlDark; + label3.Location = new System.Drawing.Point(789, 25); + label3.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label3.Name = "label3"; + label3.Size = new System.Drawing.Size(108, 15); + label3.TabIndex = 6; + label3.Text = "Left-click-drag pan"; // // label4 // - this.label4.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.label4.AutoSize = true; - this.label4.ForeColor = System.Drawing.SystemColors.ControlDark; - this.label4.Location = new System.Drawing.Point(676, 35); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(109, 13); - this.label4.TabIndex = 7; - this.label4.Text = "Right-click-drag zoom"; + label4.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right; + label4.AutoSize = true; + label4.ForeColor = System.Drawing.SystemColors.ControlDark; + label4.Location = new System.Drawing.Point(789, 40); + label4.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label4.Name = "label4"; + label4.Size = new System.Drawing.Size(126, 15); + label4.TabIndex = 7; + label4.Text = "Right-click-drag zoom"; // // label5 // - this.label5.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.label5.AutoSize = true; - this.label5.ForeColor = System.Drawing.SystemColors.ControlDark; - this.label5.Location = new System.Drawing.Point(676, 9); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(108, 13); - this.label5.TabIndex = 8; - this.label5.Text = "Middle-click auto-axis"; + label5.Anchor = System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right; + label5.AutoSize = true; + label5.ForeColor = System.Drawing.SystemColors.ControlDark; + label5.Location = new System.Drawing.Point(789, 10); + label5.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + label5.Name = "label5"; + label5.Size = new System.Drawing.Size(125, 15); + label5.TabIndex = 8; + label5.Text = "Middle-click auto-axis"; // // lblPeak // - this.lblPeak.AutoSize = true; - this.lblPeak.Location = new System.Drawing.Point(260, 9); - this.lblPeak.Name = "lblPeak"; - this.lblPeak.Size = new System.Drawing.Size(131, 13); - this.lblPeak.TabIndex = 9; - this.lblPeak.Text = "Peak Frequency: 1234 Hz"; + lblPeak.AutoSize = true; + lblPeak.Location = new System.Drawing.Point(303, 10); + lblPeak.Margin = new System.Windows.Forms.Padding(4, 0, 4, 0); + lblPeak.Name = "lblPeak"; + lblPeak.Size = new System.Drawing.Size(137, 15); + lblPeak.TabIndex = 9; + lblPeak.Text = "Peak Frequency: 1234 Hz"; // // cbPeak // - this.cbPeak.AutoSize = true; - this.cbPeak.Location = new System.Drawing.Point(263, 27); - this.cbPeak.Name = "cbPeak"; - this.cbPeak.Size = new System.Drawing.Size(78, 17); - this.cbPeak.TabIndex = 10; - this.cbPeak.Text = "show peak"; - this.cbPeak.UseVisualStyleBackColor = true; + cbPeak.AutoSize = true; + cbPeak.Location = new System.Drawing.Point(307, 31); + cbPeak.Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + cbPeak.Name = "cbPeak"; + cbPeak.Size = new System.Drawing.Size(82, 19); + cbPeak.TabIndex = 10; + cbPeak.Text = "show peak"; + cbPeak.UseVisualStyleBackColor = true; // // FormMicrophone // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(800, 450); - this.Controls.Add(this.lblPeak); - this.Controls.Add(this.label5); - this.Controls.Add(this.label4); - this.Controls.Add(this.label3); - this.Controls.Add(this.cbPeak); - this.Controls.Add(this.cbDecibel); - this.Controls.Add(this.cbAutoAxis); - this.Controls.Add(this.formsPlot1); - this.Controls.Add(this.label1); - this.Controls.Add(this.cbDevices); - this.Name = "FormMicrophone"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "FftSharp Microphone Demo"; - this.ResumeLayout(false); - this.PerformLayout(); - + AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F); + AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + ClientSize = new System.Drawing.Size(933, 519); + Controls.Add(lblPeak); + Controls.Add(label5); + Controls.Add(label4); + Controls.Add(label3); + Controls.Add(cbPeak); + Controls.Add(cbDecibel); + Controls.Add(cbAutoAxis); + Controls.Add(formsPlot1); + Controls.Add(label1); + Controls.Add(cbDevices); + Margin = new System.Windows.Forms.Padding(4, 3, 4, 3); + Name = "FormMicrophone"; + StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; + Text = "FftSharp Microphone Demo"; + ResumeLayout(false); + PerformLayout(); } #endregion diff --git a/src/FftSharp.Demo/FormMicrophone.cs b/src/FftSharp.Demo/FormMicrophone.cs index 05abec0..f1bebaa 100644 --- a/src/FftSharp.Demo/FormMicrophone.cs +++ b/src/FftSharp.Demo/FormMicrophone.cs @@ -1,4 +1,5 @@ -using System; +using ScottPlot; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; @@ -15,14 +16,16 @@ namespace FftSharp.Demo { public partial class FormMicrophone : Form { - private const int SAMPLE_RATE = 48000; - private NAudio.Wave.WaveInEvent wvin; + const int SAMPLE_RATE = 48000; + NAudio.Wave.WaveInEvent wvin; + double MaxLevel = 0; public FormMicrophone() { InitializeComponent(); formsPlot1.Plot.Margins(0); + formsPlot1.Plot.Layout(left: 50); cbDevices.Items.Clear(); for (int i = 0; i < NAudio.Wave.WaveIn.DeviceCount; i++) @@ -65,10 +68,11 @@ private void timer1_Tick(object sender, EventArgs e) var window = new Windows.Hanning(); double[] windowed = window.Apply(lastBuffer); double[] zeroPadded = FftSharp.Pad.ZeroPad(windowed); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(zeroPadded); double[] fftPower = cbDecibel.Checked ? - FftSharp.Transform.FFTpower(zeroPadded) : - FftSharp.Transform.FFTmagnitude(zeroPadded); - double[] fftFreq = FftSharp.Transform.FFTfreq(SAMPLE_RATE, fftPower.Length); + FftSharp.FFT.Power(spectrum) : + FftSharp.FFT.Magnitude(spectrum); + double[] fftFreq = FftSharp.FFT.FrequencyScale(fftPower.Length, SAMPLE_RATE); // determine peak frequency double peakFreq = 0; @@ -86,7 +90,7 @@ private void timer1_Tick(object sender, EventArgs e) formsPlot1.Plot.XLabel("Frequency Hz"); // make the plot for the first time, otherwise update the existing plot - if (formsPlot1.Plot.GetPlottables().Count() == 0) + if (formsPlot1.Plot.GetPlottables().Length == 0) { signalPlot = formsPlot1.Plot.AddSignal(fftPower, 2.0 * fftPower.Length / SAMPLE_RATE); peakLine = formsPlot1.Plot.AddVerticalLine(peakFreq, ColorTranslator.FromHtml("#66FF0000"), 2); @@ -103,6 +107,8 @@ private void timer1_Tick(object sender, EventArgs e) try { formsPlot1.Plot.AxisAuto(horizontalMargin: 0); + MaxLevel = Math.Max(MaxLevel, formsPlot1.Plot.GetAxisLimits().YMax); + formsPlot1.Plot.SetAxisLimitsY(-MaxLevel / 100, MaxLevel); } catch (Exception ex) { @@ -119,5 +125,15 @@ private void timer1_Tick(object sender, EventArgs e) System.Diagnostics.Debug.WriteLine(ex); } } + + private void cbAutoAxis_CheckedChanged(object sender, EventArgs e) + { + MaxLevel = 0; + } + + private void cbDecibel_CheckedChanged(object sender, EventArgs e) + { + MaxLevel = 0; + } } } diff --git a/src/FftSharp.Demo/FormMicrophone.resx b/src/FftSharp.Demo/FormMicrophone.resx index 1f666f2..d731088 100644 --- a/src/FftSharp.Demo/FormMicrophone.resx +++ b/src/FftSharp.Demo/FormMicrophone.resx @@ -1,64 +1,4 @@ - - - + diff --git a/src/FftSharp.Demo/FormQuickstart.cs b/src/FftSharp.Demo/FormQuickstart.cs index 5109b21..117a5db 100644 --- a/src/FftSharp.Demo/FormQuickstart.cs +++ b/src/FftSharp.Demo/FormQuickstart.cs @@ -28,7 +28,8 @@ private void trackBar1_Scroll(object sender, EventArgs e) private void UpdateFFT(double[] input) { // calculate FFT - double[] fft = FftSharp.Transform.FFTmagnitude(input); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(input); + double[] fft = FftSharp.FFT.Magnitude(spectrum); // plot the input signal formsPlot1.Plot.Clear(); diff --git a/src/FftSharp.Demo/FormWindowInspector.cs b/src/FftSharp.Demo/FormWindowInspector.cs index aee9bc8..69fa366 100644 --- a/src/FftSharp.Demo/FormWindowInspector.cs +++ b/src/FftSharp.Demo/FormWindowInspector.cs @@ -48,7 +48,8 @@ private void UpdateFrequencyPlot(IWindow window) double[] xs = ScottPlot.DataGen.Consecutive(fftSize); double[] ys = xs.Select(x => Math.Sin(x / fftSize * Math.PI * fftSize / 2)).ToArray(); double[] windowed = window.Apply(ys); - double[] power = Transform.FFTpower(windowed); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(windowed); + double[] power = FFT.Power(spectrum); // hide DC component power[0] = power[1]; diff --git a/src/FftSharp.Tests/ComplexTests.cs b/src/FftSharp.Tests/ComplexTests.cs index c651beb..db8fd1c 100644 --- a/src/FftSharp.Tests/ComplexTests.cs +++ b/src/FftSharp.Tests/ComplexTests.cs @@ -5,6 +5,7 @@ namespace FftSharp.Tests { + [Obsolete("Use methods which consume System.Numerics.Complex")] class ComplexTests { [Test] diff --git a/src/FftSharp.Tests/FftFreqTests.cs b/src/FftSharp.Tests/FftFreqTests.cs index 03e5dbf..baf0f01 100644 --- a/src/FftSharp.Tests/FftFreqTests.cs +++ b/src/FftSharp.Tests/FftFreqTests.cs @@ -20,16 +20,15 @@ public void Test_FftFreq_KnownValues() .ToArray(); // get FFT - double[] fft = FftSharp.Transform.FFTmagnitude(samples); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(samples); + double[] fft = FftSharp.FFT.Magnitude(spectrum); double[] fftKnown = { 0, 0, 1, 0, 0, 0, 0, 0, 0 }; Assert.That(fft, Is.EqualTo(fftKnown).Within(1e-10)); // calculate FFT frequencies both ways double[] fftFreqKnown = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; - double[] fftFreq = FftSharp.Transform.FFTfreq(sampleRateHz, fft.Length); - double[] fftFreq2 = FftSharp.Transform.FFTfreq(sampleRateHz, fft); + double[] fftFreq = FftSharp.FFT.FrequencyScale(fft.Length, sampleRateHz); Assert.That(fftFreq, Is.EqualTo(fftFreqKnown)); - Assert.That(fftFreq2, Is.EqualTo(fftFreqKnown)); ScottPlot.Plot plt1 = new(400, 200); plt1.AddSignal(samples, sampleRateHz); @@ -55,24 +54,22 @@ public void Test_Freq_Lookup() int sampleRate = 16000; int pointCount = 16; double[] signal = new double[pointCount]; - double[] fft = FftSharp.Transform.FFTmagnitude(signal); + + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(signal); + double[] fft = FftSharp.FFT.Magnitude(spectrum); double[] freqsFullKnown = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, -8000, -7000, -6000, -5000, -4000, -3000, -2000, -1000 }; - double[] freqsFull = FftSharp.Transform.FFTfreq(sampleRate, signal, oneSided: false); - double[] freqsFull2 = FftSharp.Transform.FFTfreq(sampleRate, signal.Length, oneSided: false); + double[] freqsFull = FftSharp.FFT.FrequencyScale(signal.Length, sampleRate, false); Assert.That(freqsFull, Is.EqualTo(freqsFullKnown)); - Assert.That(freqsFull2, Is.EqualTo(freqsFullKnown)); double[] freqsHalfKnown = { 0, 1000, 2000, 3000, 4000, 5000, 6000, 7000, 8000 }; - double[] freqsHalf = FftSharp.Transform.FFTfreq(sampleRate, fft, oneSided: true); - double[] freqsHalf2 = FftSharp.Transform.FFTfreq(sampleRate, fft.Length, oneSided: true); + double[] freqsHalf = FftSharp.FFT.FrequencyScale(fft.Length, sampleRate, true); Assert.That(freqsHalf, Is.EqualTo(freqsHalfKnown)); - Assert.That(freqsHalf2, Is.EqualTo(freqsHalfKnown)); } } } diff --git a/src/FftSharp.Tests/FftTests.cs b/src/FftSharp.Tests/FftTests.cs index 857b6f1..6f87c15 100644 --- a/src/FftSharp.Tests/FftTests.cs +++ b/src/FftSharp.Tests/FftTests.cs @@ -10,7 +10,7 @@ public void Test_FFT_Forward() double[] sample = LoadData.Double("sample.txt"); System.Numerics.Complex[] fft = FftSharp.FFT.Forward(sample); - Complex[] numpyFft = LoadData.Complex("fft.txt"); + System.Numerics.Complex[] numpyFft = LoadData.Complex("fft.txt"); Assert.AreEqual(numpyFft.Length, fft.Length); for (int i = 0; i < fft.Length; i++) diff --git a/src/FftSharp.Tests/LoadData.cs b/src/FftSharp.Tests/LoadData.cs index 9678346..1e07d51 100644 --- a/src/FftSharp.Tests/LoadData.cs +++ b/src/FftSharp.Tests/LoadData.cs @@ -12,11 +12,11 @@ public static class LoadData .Select(x => double.Parse(x)) .ToArray(); - public static Complex[] Complex(string fileName) => + public static System.Numerics.Complex[] Complex(string fileName) => File.ReadLines($"../../../../../dev/data/{fileName}") .Select(x => x.Trim('(').Trim(')').Trim('j')) .Select(x => x.Replace("-", " -").Replace("+", " +").Trim()) - .Select(x => new Complex(double.Parse(x.Split(' ')[0]), double.Parse(x.Split(' ')[1]))) + .Select(x => new System.Numerics.Complex(double.Parse(x.Split(' ')[0]), double.Parse(x.Split(' ')[1]))) .ToArray(); } } diff --git a/src/FftSharp.Tests/Mel.cs b/src/FftSharp.Tests/Mel.cs index bd6acb7..d6b222e 100644 --- a/src/FftSharp.Tests/Mel.cs +++ b/src/FftSharp.Tests/Mel.cs @@ -6,7 +6,7 @@ namespace FftSharp.Tests { - #pragma warning disable 618 +#pragma warning disable 618 class Mel { [Test] diff --git a/src/FftSharp.Tests/Padding.cs b/src/FftSharp.Tests/Padding.cs index 8118b33..390004d 100644 --- a/src/FftSharp.Tests/Padding.cs +++ b/src/FftSharp.Tests/Padding.cs @@ -11,12 +11,12 @@ class Padding [Test] public void Test_FFT_AllLengthInput() { - for (int i = 1; i < 200; i++) + for (int i = 2; i < 200; i++) { - Complex[] input = new Complex[i]; - Complex[] padded = FftSharp.Pad.ZeroPad(input); + System.Numerics.Complex[] input = new System.Numerics.Complex[i]; + System.Numerics.Complex[] padded = FftSharp.Pad.ZeroPad(input); Console.WriteLine($"Length {input.Length} -> {padded.Length}"); - FftSharp.Transform.FFT(padded); + FftSharp.FFT.Forward(padded); } } } diff --git a/src/FftSharp.Tests/PeakFrequencyDetection.cs b/src/FftSharp.Tests/PeakFrequencyDetection.cs index 6d596b3..2ab925d 100644 --- a/src/FftSharp.Tests/PeakFrequencyDetection.cs +++ b/src/FftSharp.Tests/PeakFrequencyDetection.cs @@ -15,8 +15,9 @@ public void Test_PeakFrequency_MatchesExpectation() double sampleRate = 48_000; double[] signal = FftSharp.SampleData.SampleAudio1(); // 2 kHz peak frequency - double[] fftMag = FftSharp.Transform.FFTmagnitude(signal); - double[] fftFreq = FftSharp.Transform.FFTfreq(sampleRate, fftMag); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(signal); + double[] fftMag = FftSharp.FFT.Magnitude(spectrum); + double[] fftFreq = FftSharp.FFT.FrequencyScale(fftMag.Length, sampleRate); int peakIndex = 0; double peakValue = fftMag[0]; diff --git a/src/FftSharp.Tests/Quickstart.cs b/src/FftSharp.Tests/Quickstart.cs index 4ac5ba3..d943bc9 100644 --- a/src/FftSharp.Tests/Quickstart.cs +++ b/src/FftSharp.Tests/Quickstart.cs @@ -31,13 +31,13 @@ public static void Test_SimpleFftWithGraphs(bool useWindow) } // You could get the FFT as a complex result - Complex[] fft = FftSharp.Transform.FFT(audio); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(audio); // For audio we typically want the FFT amplitude (in dB) - double[] fftPower = FftSharp.Transform.FFTpower(audio); + double[] fftPower = FftSharp.FFT.Power(spectrum); // Create an array of frequencies for each point of the FFT - double[] freqs = FftSharp.Transform.FFTfreq(sampleRate, fftPower.Length); + double[] freqs = FftSharp.FFT.FrequencyScale(fftPower.Length, sampleRate); // create an array of audio sample times to aid plotting double[] times = ScottPlot.DataGen.Consecutive(audio.Length, 1000d / sampleRate); diff --git a/src/FftSharp.Tests/Readme.cs b/src/FftSharp.Tests/Readme.cs index 49e4b0f..ce07b54 100644 --- a/src/FftSharp.Tests/Readme.cs +++ b/src/FftSharp.Tests/Readme.cs @@ -18,11 +18,11 @@ public void Test_Readme_Quickstart() window.ApplyInPlace(signal); // Calculate the FFT as an array of complex numbers - Complex[] fft = FftSharp.Transform.FFT(signal); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(signal); // Or get the spectral power (dB) or magnitude (RMS²) as real numbers - double[] fftPwr = FftSharp.Transform.FFTpower(signal); - double[] fftMag = FftSharp.Transform.FFTmagnitude(signal); + double[] fftPower = FftSharp.FFT.Power(spectrum); + double[] fftMagnitude = FftSharp.FFT.Magnitude(spectrum); } [Test] @@ -49,8 +49,9 @@ public void Test_Plot_MagnitudePowerFreq() int sampleRate = 48_000; // calculate the power spectral density using FFT - double[] psd = FftSharp.Transform.FFTpower(signal); - double[] freq = FftSharp.Transform.FFTfreq(sampleRate, psd.Length); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(signal); + double[] psd = FftSharp.FFT.Power(spectrum); + double[] freq = FftSharp.FFT.FrequencyScale(psd.Length, sampleRate); // plot the sample audio var plt = new ScottPlot.Plot(400, 200); @@ -62,20 +63,6 @@ public void Test_Plot_MagnitudePowerFreq() plt.SaveFig(Path.Combine(OUTPUT_FOLDER, "periodogram.png")); } - [Test] - public void Test_Complex() - { - Complex[] buffer = - { - new Complex(42, 0), - new Complex(96, 0), - new Complex(13, 0), - new Complex(99, 0), - }; - - FftSharp.Transform.FFT(buffer); - } - [Test] public void Test_Window() { diff --git a/src/FftSharp.Tests/Value.cs b/src/FftSharp.Tests/Value.cs index 24de115..ed02314 100644 --- a/src/FftSharp.Tests/Value.cs +++ b/src/FftSharp.Tests/Value.cs @@ -18,7 +18,7 @@ public void Test_FFT_ValuesMatchNumpy() // https://numpy.org/doc/1.18/reference/generated/numpy.fft.fft.html // This test assserts our FFT results match Numpy's - Complex[] fft = FftSharp.Transform.FFT(audio); + System.Numerics.Complex[] fft = FftSharp.FFT.Forward(audio); for (int i = 0; i < fft.Length; i++) { Assert.AreEqual(numpyFFT[i].Real, fft[i].Real, 1e-12); @@ -34,13 +34,14 @@ public void Test_RFFT_ValuesMatchNumpy() // https://numpy.org/doc/1.18/reference/generated/numpy.fft.rfft.html // This test assserts our FFT results match Numpy's - Complex[] rfft = FftSharp.Transform.RFFT(audio); - Assert.AreEqual(NumpyRFFT.Length, rfft.Length); + System.Numerics.Complex[] spectrum = FftSharp.FFT.ForwardReal(audio); - for (int i = 0; i < rfft.Length; i++) + Assert.AreEqual(NumpyRFFT.Length, spectrum.Length); + + for (int i = 0; i < spectrum.Length; i++) { - Assert.AreEqual(NumpyRFFT[i].Real, rfft[i].Real, 1e-12); - Assert.AreEqual(NumpyRFFT[i].Imaginary, rfft[i].Imaginary, 1e-12); + Assert.AreEqual(NumpyRFFT[i].Real, spectrum[i].Real, 1e-12); + Assert.AreEqual(NumpyRFFT[i].Imaginary, spectrum[i].Imaginary, 1e-12); } } @@ -52,15 +53,14 @@ public void Test_Absolute_ValuesMatchNumpy() // https://numpy.org/doc/1.18/reference/generated/numpy.fft.rfft.html // This test assserts our FFT results match Numpy's - Complex[] rfft = FftSharp.Transform.RFFT(audio); - double[] rfftAbs = FftSharp.Transform.Absolute(rfft); + System.Numerics.Complex[] real = FftSharp.FFT.ForwardReal(audio); + double[] magnitudes = real.Select(x => x.Magnitude).ToArray(); - Assert.AreEqual(rfftAbs.Length, rfft.Length); + Assert.AreEqual(NumpyRFFTabs.Length, magnitudes.Length); - for (int i = 0; i < rfftAbs.Length; i++) + for (int i = 0; i < NumpyRFFTabs.Length; i++) { - Assert.AreEqual(NumpyRFFTabs[i], rfftAbs[i], 1e-12); - Assert.AreEqual(NumpyRFFTabs[i], rfftAbs[i], 1e-12); + Assert.AreEqual(NumpyRFFTabs[i], magnitudes[i], 1e-12); } } @@ -72,10 +72,10 @@ public void Test_FFTfast_ValuesMatchNumpy() // https://numpy.org/doc/1.18/reference/generated/numpy.fft.fft.html // This test assserts our FFT results match Numpy's - Complex[] fft = new Complex[audio.Length]; + System.Numerics.Complex[] fft = new System.Numerics.Complex[audio.Length]; for (int i = 0; i < audio.Length; i++) - fft[i] = new Complex(audio[i], 0); - FftSharp.Transform.FFT(fft); + fft[i] = new(audio[i], 0); + FftSharp.FFT.Forward(fft); for (int i = 0; i < fft.Length; i++) { @@ -92,12 +92,12 @@ public void Test_FftFromDoubleArray_MatchesFftResult() // https://numpy.org/doc/1.18/reference/generated/numpy.fft.fft.html // This test assserts our FFT results match Numpy's - Complex[] fft = new Complex[audio.Length]; + System.Numerics.Complex[] fft = new System.Numerics.Complex[audio.Length]; for (int i = 0; i < audio.Length; i++) - fft[i] = new Complex(audio[i], 0); - FftSharp.Transform.FFT(fft); + fft[i] = new(audio[i], 0); + FftSharp.FFT.Forward(fft); - Complex[] fft2 = FftSharp.Transform.FFT(audio); + System.Numerics.Complex[] fft2 = FftSharp.FFT.Forward(audio); for (int i = 0; i < fft.Length; i++) { @@ -108,837 +108,837 @@ public void Test_FftFromDoubleArray_MatchesFftResult() private readonly double[] audio = { -0.330, 2.150, 1.440, 1.370, 0.240, 2.600, 3.510, 1.980, 1.880, 0.080, -1.820, 1.300, 0.230, -1.160, -1.350, -0.580, -0.840, -1.350, -2.720, -2.530, --0.020, -0.760, -0.480, -2.100, 0.300, 1.860, 1.600, 1.490, 0.580, 2.120, -2.790, 1.990, 1.200, 0.800, 2.180, 1.600, -0.370, -1.250, -1.990, 0.350, --1.190, -1.620, -3.280, -2.570, 0.070, -0.810, -1.130, -1.680, -0.250, 1.550, -1.080, 1.530, 0.650, 2.530, 2.790, 2.420, 1.720, 0.540, 2.390, 1.510, -0.220, -1.420, -1.440, 0.290, -1.610, -1.500, -3.230, -2.200, -0.010, -1.390, --0.470, -1.650, 0.250, 2.050, 1.480, 0.910, 0.760, 2.760, 2.730, 2.450, -1.090, 0.280, 2.070, 1.160, 0.270, -1.170, -1.500, 0.200, -0.910, -1.580, --2.460, -2.550, -0.310, -0.940, -1.130, -1.850, 0.420, 1.560, 0.850, 0.880, -0.660, 2.730, 3.230, 2.470, 1.120, 0.740, 1.600, 1.730, 0.280, -1.540, --2.180, -0.500, -1.090, -1.390, -2.910, -2.690, -0.160, -1.040, -1.240, -1.520, --0.390, 1.690, 1.520, 0.870, 0.310, 2.750, 3.560, 2.530, 1.290, 0.330, -1.810, 1.340, 0.130, -1.580, -2.050, -0.110, -0.850, -1.730, -3.300, -2.100, --0.430, -0.670, -1.340, -1.430, 0.220, 2.160, 1.350, 1.380, 0.210, 2.230, -3.210, 1.790, 1.900, 0.380, 1.600, 1.100, 0.440, -1.070, -1.690, -0.090, --0.730, -2.260, -2.890, -2.680, -0.020, -0.960, -0.890, -1.580, 0.270, 2.330, -0.970, 0.870, 0.500, 2.520, 2.820, 1.610, 1.130, -0.040, 1.980, 1.280, --0.380, -1.240, -1.520, -0.400, -0.790, -2.310, -2.890, -1.880, 0.160, -1.590, --0.810, -1.860, 0.570, 1.920, 1.440, 1.130, 0.450, 3.020, 3.490, 2.510, -1.150, -0.060, 2.430, 1.010, 0.480, -1.090, -1.550, -0.090, -1.350, -1.350, --3.370, -2.150, -0.710, -1.410, -0.970, -1.550, -0.140, 1.640, 0.910, 1.590, -0.170, 2.650, 3.160, 2.200, 1.240, -0.170, 1.630, 1.710, 0.310, -0.740, --1.680, -0.350, -1.430, -1.870, -3.200, -1.950, -0.340, -0.970, -1.150, -1.760, --0.160, 2.330, 1.280, 0.810, 1.020, 3.000, 2.760, 2.310, 0.990, -0.000, -1.600, 0.940, 0.330, -1.530, -1.490, 0.040, -1.130, -2.100, -2.560, -1.980, --0.390, -0.700, -0.660, -1.670, -0.060, 2.110, 1.090, 1.450, 1.030, 2.650, -2.690, 2.160, 1.890, 0.680, 2.070, 0.970, -0.400, -1.080, -1.660, -0.230, --0.830, -2.020, -2.610, -2.320, -0.000, -1.070, -0.940, -1.970, 0.230, 1.890, -0.980, 1.060, 0.830, 2.500, 3.520, 1.880, 1.090, -0.040, 2.190, 1.040, -0.130, -1.120, -1.560, -0.120, -1.600, -1.900, -3.280, -1.980, -0.270, -0.900, --0.830, -2.120, 0.170, 1.790, 1.660, 0.930, 0.150, 2.320, 3.230, 2.340, -1.150, 0.070, 1.550, 1.280, -0.110, -0.790, -1.510, -0.080, -0.750, -2.140, --2.450, -1.990, 0.060, -1.140, -0.620, -1.780, 0.150, 1.640, 1.090, 1.200, -0.450, 2.700, 3.200, 2.470, 1.810, 0.110, 2.210, 1.180, 0.070, -0.830, --2.120, 0.300, -1.180, -1.480, -2.450, -2.570, -0.340, -1.280, -1.280, -1.870, -0.220, 1.920, 1.580, 1.170, 0.790, 2.830, 2.720, 1.640, 1.510, 0.440, -2.100, 1.650, 0.460, -1.390, -1.960, -0.010, -1.040, -2.260, -2.870, -1.850, --0.670, -1.130, -1.400, -1.980, 0.590, 1.370, 1.000, 0.840, 0.550, 2.610, -3.460, 1.760, 1.020, -0.040, 2.310, 1.670, 0.350, -1.390, -2.160, -0.480, --1.520, -1.760, -2.670, -2.010, -0.600, -1.210, -1.420, -1.850, 0.080, 1.690, -1.270, 1.220, 0.830, 2.230, 2.700, 1.680, 1.420, 0.560, 1.910, 1.550, -0.060, -1.550, -1.750, -0.570, -0.920, -1.990, -2.700, -2.130, -0.370, -1.060, --0.630, -1.710, 0.510, 1.740, 1.480, 1.390, 0.780, 2.270, 3.520, 2.130, -1.890, -0.140, 2.080, 0.990, 0.570, -1.190, -1.900, 0.320, -1.640, -1.700, --3.090, -1.840, 0.030, -1.150, -0.800, -2.040, 0.590, 2.020, 0.720, 1.690, -0.730, 2.380, 3.420, 2.480, 1.420, -0.010, 2.040, 1.220, -0.020, -1.110, --1.950, -0.320, -0.870, -1.550, -2.670, -2.440, -0.300, -1.180, -1.390, -1.800, -0.520, 2.110, 1.320, 1.630, 0.270, 2.880, 3.160, 1.990, 1.640, 0.530, -2.120, 0.900, -0.220, -1.590, -1.450, 0.050, -1.460, -1.730, -2.760, -2.060, -0.100, -1.560, -0.920, -1.600, -0.140, 1.350, 0.830, 0.880, 0.760, 2.300, -3.160, 2.110, -}; + 0.330, 2.150, 1.440, 1.370, 0.240, 2.600, 3.510, 1.980, 1.880, 0.080, + 1.820, 1.300, 0.230, -1.160, -1.350, -0.580, -0.840, -1.350, -2.720, -2.530, + -0.020, -0.760, -0.480, -2.100, 0.300, 1.860, 1.600, 1.490, 0.580, 2.120, + 2.790, 1.990, 1.200, 0.800, 2.180, 1.600, -0.370, -1.250, -1.990, 0.350, + -1.190, -1.620, -3.280, -2.570, 0.070, -0.810, -1.130, -1.680, -0.250, 1.550, + 1.080, 1.530, 0.650, 2.530, 2.790, 2.420, 1.720, 0.540, 2.390, 1.510, + 0.220, -1.420, -1.440, 0.290, -1.610, -1.500, -3.230, -2.200, -0.010, -1.390, + -0.470, -1.650, 0.250, 2.050, 1.480, 0.910, 0.760, 2.760, 2.730, 2.450, + 1.090, 0.280, 2.070, 1.160, 0.270, -1.170, -1.500, 0.200, -0.910, -1.580, + -2.460, -2.550, -0.310, -0.940, -1.130, -1.850, 0.420, 1.560, 0.850, 0.880, + 0.660, 2.730, 3.230, 2.470, 1.120, 0.740, 1.600, 1.730, 0.280, -1.540, + -2.180, -0.500, -1.090, -1.390, -2.910, -2.690, -0.160, -1.040, -1.240, -1.520, + -0.390, 1.690, 1.520, 0.870, 0.310, 2.750, 3.560, 2.530, 1.290, 0.330, + 1.810, 1.340, 0.130, -1.580, -2.050, -0.110, -0.850, -1.730, -3.300, -2.100, + -0.430, -0.670, -1.340, -1.430, 0.220, 2.160, 1.350, 1.380, 0.210, 2.230, + 3.210, 1.790, 1.900, 0.380, 1.600, 1.100, 0.440, -1.070, -1.690, -0.090, + -0.730, -2.260, -2.890, -2.680, -0.020, -0.960, -0.890, -1.580, 0.270, 2.330, + 0.970, 0.870, 0.500, 2.520, 2.820, 1.610, 1.130, -0.040, 1.980, 1.280, + -0.380, -1.240, -1.520, -0.400, -0.790, -2.310, -2.890, -1.880, 0.160, -1.590, + -0.810, -1.860, 0.570, 1.920, 1.440, 1.130, 0.450, 3.020, 3.490, 2.510, + 1.150, -0.060, 2.430, 1.010, 0.480, -1.090, -1.550, -0.090, -1.350, -1.350, + -3.370, -2.150, -0.710, -1.410, -0.970, -1.550, -0.140, 1.640, 0.910, 1.590, + 0.170, 2.650, 3.160, 2.200, 1.240, -0.170, 1.630, 1.710, 0.310, -0.740, + -1.680, -0.350, -1.430, -1.870, -3.200, -1.950, -0.340, -0.970, -1.150, -1.760, + -0.160, 2.330, 1.280, 0.810, 1.020, 3.000, 2.760, 2.310, 0.990, -0.000, + 1.600, 0.940, 0.330, -1.530, -1.490, 0.040, -1.130, -2.100, -2.560, -1.980, + -0.390, -0.700, -0.660, -1.670, -0.060, 2.110, 1.090, 1.450, 1.030, 2.650, + 2.690, 2.160, 1.890, 0.680, 2.070, 0.970, -0.400, -1.080, -1.660, -0.230, + -0.830, -2.020, -2.610, -2.320, -0.000, -1.070, -0.940, -1.970, 0.230, 1.890, + 0.980, 1.060, 0.830, 2.500, 3.520, 1.880, 1.090, -0.040, 2.190, 1.040, + 0.130, -1.120, -1.560, -0.120, -1.600, -1.900, -3.280, -1.980, -0.270, -0.900, + -0.830, -2.120, 0.170, 1.790, 1.660, 0.930, 0.150, 2.320, 3.230, 2.340, + 1.150, 0.070, 1.550, 1.280, -0.110, -0.790, -1.510, -0.080, -0.750, -2.140, + -2.450, -1.990, 0.060, -1.140, -0.620, -1.780, 0.150, 1.640, 1.090, 1.200, + 0.450, 2.700, 3.200, 2.470, 1.810, 0.110, 2.210, 1.180, 0.070, -0.830, + -2.120, 0.300, -1.180, -1.480, -2.450, -2.570, -0.340, -1.280, -1.280, -1.870, + 0.220, 1.920, 1.580, 1.170, 0.790, 2.830, 2.720, 1.640, 1.510, 0.440, + 2.100, 1.650, 0.460, -1.390, -1.960, -0.010, -1.040, -2.260, -2.870, -1.850, + -0.670, -1.130, -1.400, -1.980, 0.590, 1.370, 1.000, 0.840, 0.550, 2.610, + 3.460, 1.760, 1.020, -0.040, 2.310, 1.670, 0.350, -1.390, -2.160, -0.480, + -1.520, -1.760, -2.670, -2.010, -0.600, -1.210, -1.420, -1.850, 0.080, 1.690, + 1.270, 1.220, 0.830, 2.230, 2.700, 1.680, 1.420, 0.560, 1.910, 1.550, + 0.060, -1.550, -1.750, -0.570, -0.920, -1.990, -2.700, -2.130, -0.370, -1.060, + -0.630, -1.710, 0.510, 1.740, 1.480, 1.390, 0.780, 2.270, 3.520, 2.130, + 1.890, -0.140, 2.080, 0.990, 0.570, -1.190, -1.900, 0.320, -1.640, -1.700, + -3.090, -1.840, 0.030, -1.150, -0.800, -2.040, 0.590, 2.020, 0.720, 1.690, + 0.730, 2.380, 3.420, 2.480, 1.420, -0.010, 2.040, 1.220, -0.020, -1.110, + -1.950, -0.320, -0.870, -1.550, -2.670, -2.440, -0.300, -1.180, -1.390, -1.800, + 0.520, 2.110, 1.320, 1.630, 0.270, 2.880, 3.160, 1.990, 1.640, 0.530, + 2.120, 0.900, -0.220, -1.590, -1.450, 0.050, -1.460, -1.730, -2.760, -2.060, + 0.100, -1.560, -0.920, -1.600, -0.140, 1.350, 0.830, 0.880, 0.760, 2.300, + 3.160, 2.110, + }; // generated with python/Numpy: fft = numpy.fft.fft(sampleAudio1) - private readonly Complex[] numpyFFT = + private readonly System.Numerics.Complex[] numpyFFT = { -new Complex(71.52, 0.0), -new Complex(16.292599219664993, -1.650266240953031), -new Complex(16.58819220541474, -4.456837908626264), -new Complex(11.867798701774102, 0.7429551118770812), -new Complex(6.339252942386048, 1.3944730123175706), -new Complex(5.091353556901778, -4.23187134652945), -new Complex(18.814490506606102, -8.665625451587731), -new Complex(16.81258122144206, 1.5049383063313775), -new Complex(16.250262936175652, -10.898983835764772), -new Complex(9.653138749580403, -3.4907810508138843), -new Complex(13.307669165815344, -4.967069973560199), -new Complex(14.963784782916647, -12.000161985633959), -new Complex(11.96745034011839, -6.99870154432028), -new Complex(20.457611574040772, -7.503111641685473), -new Complex(16.65888595885314, -13.091972828691972), -new Complex(21.507612856521956, -0.699201769934322), -new Complex(26.771895548958383, -16.35220257819907), -new Complex(31.077223230796143, -16.494436421460076), -new Complex(45.10852008979767, -20.58984095476262), -new Complex(59.25922110005847, -29.04396354790871), -new Complex(94.30531062507758, -51.976078289996565), -new Complex(366.88585732605077, -205.12349027793272), -new Complex(-177.8491671506428, 105.09540721761974), -new Complex(-71.83350266181674, 50.22287606700222), -new Complex(-45.553168448188615, 26.10712988794811), -new Complex(-20.986245062901816, 15.999954756852027), -new Complex(-24.858594932136125, 13.645918510329325), -new Complex(-19.173373012705525, 16.449007609589398), -new Complex(-8.537098037860998, 8.88530912169331), -new Complex(-11.266585527651117, 8.094594399106153), -new Complex(-9.241923204154396, -1.847317739378453), -new Complex(-10.46615838300978, 4.64345496924585), -new Complex(-12.27027085039127, 5.87765864860703), -new Complex(-7.370274298222351, 6.341810090314409), -new Complex(-1.2131051366901628, 6.993893647840464), -new Complex(-6.1359988292690755, 7.692984484782312), -new Complex(4.091706423869168, 1.365798023974214), -new Complex(-4.655458008198221, -0.7217109700711242), -new Complex(-4.463444898038833, 2.2094751854192), -new Complex(-2.4342160634907675, 11.254011503392162), -new Complex(5.098783312195228, 4.827669742579822), -new Complex(-5.831778282084546, 4.025856294966041), -new Complex(3.665136034495667, 5.806578149334394), -new Complex(-6.782761994698575, 12.087457969879013), -new Complex(-0.454577044366566, 8.989165374120152), -new Complex(1.1190972614148489, 7.3826047223482005), -new Complex(-5.300512604596365, 3.3787295501896804), -new Complex(-5.303638228818758, 0.663150420740581), -new Complex(-1.402392307772061, 4.385841205789253), -new Complex(2.839320733358291, 6.177222092996357), -new Complex(-5.471846454174141, -3.646041429888425), -new Complex(-0.1825607735763244, 4.335020541082649), -new Complex(0.23366490444061006, 12.53115614224979), -new Complex(-4.607834909859168, 2.731226568868507), -new Complex(0.4407190353942614, 5.867753191224533), -new Complex(-0.6346634815558851, 5.2502903349451495), -new Complex(-4.644687701252085, 13.308500752662315), -new Complex(-4.186255039897505, 1.883033665479715), -new Complex(3.1011324895272105, 6.889303301004), -new Complex(-5.994624072627106, 3.0893738437673104), -new Complex(2.148665980983523, -2.165175261206472), -new Complex(8.120784577044827, -1.026413324380261), -new Complex(-2.736075709061385, 8.309850036461317), -new Complex(-1.122976392687268, 1.3868358039512898), -new Complex(-0.4420963209812834, 3.394939033455901), -new Complex(10.571135745703966, -7.214497105214868), -new Complex(-0.6361813663828921, 6.457986031215809), -new Complex(-0.0025957626844732573, 9.037941129554671), -new Complex(-2.797973427324928, -1.3949959065210518), -new Complex(0.32042128744426146, 5.166759010512948), -new Complex(1.8672167221767477, 3.7484970064639), -new Complex(4.284785289539634, 11.97800470285899), -new Complex(-1.9130289340345799, 4.890086838063299), -new Complex(-8.063646970616297, 7.70671282524425), -new Complex(-2.5076616550924244, -0.5616066179697525), -new Complex(4.848590964002215, 0.4155821028619835), -new Complex(0.6519047716752894, 8.365467739744268), -new Complex(5.925896594814173, 1.6214148805227862), -new Complex(-0.11732472297714991, 2.084514460872517), -new Complex(3.1379824354268417, -6.2577633910536195), -new Complex(6.774385287871493, 9.629330590835693), -new Complex(5.4393155868096645, -6.121406497914311), -new Complex(1.0174178671562233, -8.607714904038465), -new Complex(5.398310408984056, -7.203490766128718), -new Complex(-3.785148744276335, 3.9182113455816534), -new Complex(7.557346542357081, 6.081240307670624), -new Complex(-8.76993242973248, 5.33687737351268), -new Complex(-0.30138424584816725, 2.3135609200854255), -new Complex(6.9465856216898105, 8.502927040297298), -new Complex(-8.363128478889873, 9.156259453584855), -new Complex(9.67066074018313, 4.95686087152842), -new Complex(8.861238026483612, -0.9840687253038303), -new Complex(10.426812822364912, -3.327862985228994), -new Complex(7.296132588971696, 4.341249905128431), -new Complex(2.513188500291645, 5.861603466455876), -new Complex(-1.0480377808580927, 3.4893475665297666), -new Complex(7.821845003550149, -1.9841378599467063), -new Complex(-1.6324823820087873, 7.790777253754385), -new Complex(8.970903795772337, 4.066014902164978), -new Complex(-1.6707970781871344, 2.136015162752384), -new Complex(7.25823248662058, 2.7652788765225464), -new Complex(18.74397062908053, 8.896718444008549), -new Complex(2.1660703208919037, -0.16068219834732878), -new Complex(13.825097858675338, 8.427493951505237), -new Complex(18.47832853222178, 12.667650849412713), -new Complex(37.569125833815264, 21.578994508186412), -new Complex(90.94566028906655, 50.697370827337764), -new Complex(-191.4717302736666, -117.08331011378777), -new Complex(-51.32832640613284, -23.124699639369688), -new Complex(-23.500774862545306, -12.60161060026162), -new Complex(-23.59827162418354, -16.220434411970672), -new Complex(-21.623922884263543, -12.128429398236538), -new Complex(-8.156016294103669, -2.0796298320611637), -new Complex(-3.320934257799828, -4.826944007044764), -new Complex(-8.966686390330285, 2.8174321266953233), -new Complex(-4.036510916197747, -8.306905035682055), -new Complex(-9.118052343426735, -0.7998264524711765), -new Complex(1.7056006968282986, 2.48014371582694), -new Complex(-0.4317759856237964, -2.0729669596807687), -new Complex(1.3875511565884053, -2.3921170941765446), -new Complex(-7.976980514611284, 4.704983072668933), -new Complex(-1.0412939420119793, 2.0304346948254044), -new Complex(-0.4712297547813127, -1.8877908393403056), -new Complex(-12.834400468053518, -1.928010984003001), -new Complex(-0.4020716349072191, -4.708004655448063), -new Complex(-8.267438571150924, -3.7039764685576895), -new Complex(-4.8684310845372405, -5.189260677129305), -new Complex(0.4631251388647053, 1.0836107137372681), -new Complex(0.9899999999999949, -2.049999999999997), -new Complex(-4.84931978116202, -0.23933081088023878), -new Complex(-9.934189775670093, 0.7971585720723557), -new Complex(-2.600011910104527, -0.9923343205338109), -new Complex(-2.160469164514625, -5.322803319988892), -new Complex(-8.342213173106142, -0.17252901564132728), -new Complex(-4.196598202151177, -4.7274680682126755), -new Complex(-4.944944633762713, -1.4791201736168527), -new Complex(-6.434479741808574, 6.482812912010042), -new Complex(-5.158937174706397, 3.713563429843517), -new Complex(-2.5917604439233597, -1.074410553473996), -new Complex(1.6430036734162927, -0.26848993074113725), -new Complex(-4.68619932790007, 3.5540911138722024), -new Complex(-9.114112417604312, -5.863251584381021), -new Complex(-1.511010285838398, 0.5916676752843228), -new Complex(-1.0248996347649997, -5.221535985301575), -new Complex(-5.907774632673288, 3.6263871521782516), -new Complex(-0.8134349081877801, -2.8827110905821716), -new Complex(-3.8883597256888898, 6.5096521647811105), -new Complex(3.6028695146052687, 4.906754934650181), -new Complex(-1.43392165399176, 0.09877393185500516), -new Complex(2.5399337102167436, -2.6091846772843255), -new Complex(-2.4029291340916004, 4.978313193479735), -new Complex(-3.715287570789176, -4.39451837716846), -new Complex(-10.306371528157012, -2.7488670997481837), -new Complex(-0.024313261096906125, -5.986905188653612), -new Complex(-2.8298389130063915, 3.448368650672101), -new Complex(4.7341520993712995, -11.062510314288573), -new Complex(-5.3829294937018854, -2.783057603406332), -new Complex(0.0026383233212190493, -0.18978654624226898), -new Complex(-1.8812828884644066, 4.871022697842905), -new Complex(-3.2509005555527533, 1.4393838886348913), -new Complex(-2.3369676800222434, 2.482582201564716), -new Complex(-1.2142355119041524, 0.1834635506672304), -new Complex(-9.582245656802916, -0.027020924935291735), -new Complex(-6.581883483527928, -0.9168370285871652), -new Complex(2.23633907309973, -2.3021508943840727), -new Complex(-2.0748219655573625, -2.0037979487038773), -new Complex(-3.765563906978262, 3.088432721629983), -new Complex(5.332530718835812, -2.0516059158581865), -new Complex(-0.2549012255641858, -0.6932903665209049), -new Complex(-7.277178212857739, 2.9116631246832343), -new Complex(-2.1936009994012435, 0.4001679051474425), -new Complex(-8.794157662232939, 3.5717135906207176), -new Complex(9.191032309546063, -1.9398929261634499), -new Complex(-3.869682067783085, 0.5152936097585425), -new Complex(3.147392558038452, 2.216334156350135), -new Complex(4.337683896574441, 0.82997589223395), -new Complex(5.8600818792670735, 0.02923610475152838), -new Complex(-2.7839860346841525, 2.4914655265099466), -new Complex(-1.903842415258608, 1.4113411741607438), -new Complex(-9.894054706023422, 4.9668960901740284), -new Complex(-0.8081382242155792, 11.349170741938877), -new Complex(8.373197781633165, 2.1638002986517635), -new Complex(-2.4852724753585846, 4.218258048332267), -new Complex(-3.755737925182469, -4.490350833996583), -new Complex(-2.879274257660312, 2.1626921190768043), -new Complex(0.7024288556739815, -0.03525952952816347), -new Complex(-2.673777491391463, 2.058724091865224), -new Complex(2.1942581278786912, -4.751521874754496), -new Complex(3.8112923807165675, 3.5946006951844867), -new Complex(4.095532859213184, -0.28973422842765917), -new Complex(-6.83825512481851, -9.6297170417451), -new Complex(-1.9910301634396683, -10.188877845696899), -new Complex(-1.4179036790187196, -3.8050609665441018), -new Complex(-4.500591208775358, 2.5115489328726808), -new Complex(2.5605067559698993, -1.0184596329131184), -new Complex(6.717336341148254, -1.1098887472444834), -new Complex(-3.6308727316719613, -2.930509025563401), -new Complex(-1.2800687813817113, -5.2293267918825705), -new Complex(-0.24178054270822846, -0.34674641143824747), -new Complex(-3.5825821902320762, -6.877312499750982), -new Complex(-2.602886071425474, -6.86407264784425), -new Complex(-1.2785892290938932, -1.6397567663004344), -new Complex(-8.675100612964963, -7.055963310707158), -new Complex(8.84974247542792, -6.56325254726892), -new Complex(2.885016983143895, -6.95362008379264), -new Complex(6.918222156603157, 9.497082265446183), -new Complex(4.10405309937396, -7.9120673382156275), -new Complex(-1.584044627114519, -3.1949874308238275), -new Complex(0.3670925294266434, -1.6673485244636947), -new Complex(0.29461814038296197, -10.528089110189706), -new Complex(10.194833330276264, -7.1146946624422025), -new Complex(17.627086740678745, -7.638274663697334), -new Complex(22.6731707587984, -11.536058001405237), -new Complex(90.79168081510915, -41.13328830004425), -new Complex(-43.229531626011074, 23.294890907589057), -new Complex(-17.52387246706339, 7.853673956180025), -new Complex(-15.806202527275513, -0.5183772215827949), -new Complex(-25.289765401997723, 13.089544474677414), -new Complex(-7.587901316147817, 6.612185770225549), -new Complex(-7.7510301280002984, -4.450483522400161), -new Complex(3.0154490825863447, 4.85718259595836), -new Complex(-5.588006589467025, 1.8753775661904744), -new Complex(-4.522700439505712, -6.941653406278663), -new Complex(-9.29990757512051, 7.73164297352557), -new Complex(-15.054606473136637, -4.415621289881546), -new Complex(-5.649212036777634, -3.004224743561956), -new Complex(-5.3245866110889075, -2.3232079416455855), -new Complex(-6.09582893633526, -2.833602503098011), -new Complex(2.3719671688372457, 7.823224635184997), -new Complex(-4.378899544434763, -2.1004484536234473), -new Complex(-1.847804254120236, -1.2174634584260025), -new Complex(-7.151969645971247, 5.608493469097141), -new Complex(-6.962043610144818, 2.4741041607524394), -new Complex(-0.9411822402381276, 6.274216197339793), -new Complex(0.501900088529041, -4.787955462633128), -new Complex(-1.1594019555348325, -3.84174782623154), -new Complex(3.3001766896560056, 2.9594456628498946), -new Complex(-10.066436810536924, 0.6384104860672224), -new Complex(-7.19567825040399, -3.2261296085795568), -new Complex(-4.691470830071458, 2.9464762422417987), -new Complex(-0.14727201097456977, -4.859280838128437), -new Complex(-14.56692223701291, -7.90084256027122), -new Complex(-4.324446487468897, 0.004185311431190719), -new Complex(-1.0407514948403307, 2.059605296404119), -new Complex(-2.98340696573519, -3.1029075652331937), -new Complex(-4.636760771943916, 10.373963334679214), -new Complex(-8.26960957572124, 0.7354864925759217), -new Complex(-4.979951151973244, 2.4789920137824786), -new Complex(1.4400641578399824, 0.8869891013658968), -new Complex(0.6818750261182362, 2.470099039214686), -new Complex(8.336841493956832, 7.404955214008821), -new Complex(2.8157431492244953, -6.567660517080393), -new Complex(-5.878260543893631, 8.516509649574305), -new Complex(2.879678032286943, -3.1017131232821384), -new Complex(-3.801558789466788, -2.7381256767431728), -new Complex(-0.27745160787512013, -7.4993828895293335), -new Complex(11.019999999999996, 0.0), -new Complex(-0.2774516078751077, 7.499382889529332), -new Complex(-3.801558789466789, 2.738125676743172), -new Complex(2.8796780322869546, 3.101713123282132), -new Complex(-5.87826054389363, -8.516509649574308), -new Complex(2.8157431492244895, 6.567660517080403), -new Complex(8.336841493956836, -7.404955214008817), -new Complex(0.6818750261182362, -2.470099039214685), -new Complex(1.4400641578399824, -0.8869891013658968), -new Complex(-4.979951151973249, -2.4789920137824777), -new Complex(-8.269609575721244, -0.7354864925759133), -new Complex(-4.636760771943918, -10.373963334679196), -new Complex(-2.9834069657351927, 3.1029075652331986), -new Complex(-1.0407514948403325, -2.0596052964041296), -new Complex(-4.324446487468897, -0.004185311431191607), -new Complex(-14.566922237012907, 7.900842560271221), -new Complex(-0.14727201097457154, 4.85928083812844), -new Complex(-4.691470830071458, -2.9464762422418005), -new Complex(-7.195678250403994, 3.2261296085795585), -new Complex(-10.066436810536924, -0.6384104860672206), -new Complex(3.3001766896559985, -2.9594456628499017), -new Complex(-1.1594019555349178, 3.84174782623154), -new Complex(0.5019000885290694, 4.787955462633128), -new Complex(-0.9411822402381276, -6.274216197339783), -new Complex(-6.962043610144818, -2.4741041607524394), -new Complex(-7.151969645971249, -5.608493469097138), -new Complex(-1.8478042541202342, 1.2174634584260051), -new Complex(-4.378899544434763, 2.100448453623444), -new Complex(2.3719671688372457, -7.8232246351849986), -new Complex(-6.09582893633526, 2.833602503098014), -new Complex(-5.324586611088915, 2.3232079416455873), -new Complex(-5.649212036777634, 3.0042247435619593), -new Complex(-15.054606473136637, 4.415621289881546), -new Complex(-9.299907575120518, -7.731642973525572), -new Complex(-4.522700439505707, 6.941653406278663), -new Complex(-5.58800658946703, -1.8753775661904735), -new Complex(3.015449082586345, -4.85718259595836), -new Complex(-7.751030128000305, 4.45048352240015), -new Complex(-7.587901316147819, -6.612185770225553), -new Complex(-25.289765401997727, -13.089544474677405), -new Complex(-15.806202527275513, 0.5183772215827931), -new Complex(-17.523872467063388, -7.853673956180031), -new Complex(-43.229531626011074, -23.294890907589064), -new Complex(90.79168081510915, 41.13328830004425), -new Complex(22.6731707587984, 11.536058001405241), -new Complex(17.62708674067875, 7.6382746636973495), -new Complex(10.194833330276262, 7.114694662442204), -new Complex(0.2946181403829593, 10.528089110189704), -new Complex(0.3670925294266425, 1.6673485244636956), -new Complex(-1.584044627114522, 3.1949874308238337), -new Complex(4.104053099373957, 7.912067338215623), -new Complex(6.918222156603163, -9.497082265446183), -new Complex(2.8850169831438968, 6.953620083792638), -new Complex(8.849742475427908, 6.563252547268917), -new Complex(-8.675100612964956, 7.0559633107071535), -new Complex(-1.2785892290938947, 1.6397567663004302), -new Complex(-2.602886071425475, 6.86407264784425), -new Complex(-3.5825821902320816, 6.877312499750978), -new Complex(-0.2417805427082258, 0.346746411438247), -new Complex(-1.280068781381706, 5.229326791882579), -new Complex(-3.6308727316719613, 2.930509025563401), -new Complex(6.717336341148264, 1.1098887472444712), -new Complex(2.560506755969895, 1.0184596329131175), -new Complex(-4.500591208775362, -2.511548932872677), -new Complex(-1.4179036790187196, 3.8050609665441018), -new Complex(-1.9910301634396674, 10.1888778456969), -new Complex(-6.838255124818515, 9.629717041745097), -new Complex(4.095532859213191, 0.28973422842765384), -new Complex(3.8112923807165693, -3.5946006951844875), -new Complex(2.194258127878689, 4.751521874754488), -new Complex(-2.6737774913914603, -2.0587240918652236), -new Complex(0.7024288556739806, 0.03525952952816347), -new Complex(-2.8792742576603123, -2.162692119076805), -new Complex(-3.7557379251824736, 4.490350833996582), -new Complex(-2.4852724753585793, -4.21825804833226), -new Complex(8.373197781633138, -2.1638002986517675), -new Complex(-0.8081382242155787, -11.349170741938874), -new Complex(-9.894054706023425, -4.966896090174037), -new Complex(-1.9038424152586149, -1.4113411741607438), -new Complex(-2.78398603468415, -2.491465526509942), -new Complex(5.8600818792670735, -0.029236104751530156), -new Complex(4.337683896574438, -0.8299758922339566), -new Complex(3.147392558038458, -2.216334156350134), -new Complex(-3.869682067783092, -0.5152936097585532), -new Complex(9.191032309546063, 1.9398929261634485), -new Complex(-8.79415766223292, -3.5717135906207194), -new Complex(-2.193600999401239, -0.40016790514745004), -new Complex(-7.277178212857741, -2.911663124683228), -new Complex(-0.2549012255641854, 0.6932903665209089), -new Complex(5.3325307188358035, 2.0516059158581808), -new Complex(-3.765563906978266, -3.0884327216299763), -new Complex(-2.0748219655573683, 2.0037979487038813), -new Complex(2.236339073099729, 2.3021508943840754), -new Complex(-6.581883483527934, 0.9168370285871648), -new Complex(-9.582245656802918, 0.027020924935293067), -new Complex(-1.2142355119041532, -0.18346355066723263), -new Complex(-2.3369676800222434, -2.482582201564716), -new Complex(-3.2509005555527524, -1.439383888634893), -new Complex(-1.881282888464412, -4.871022697842905), -new Complex(0.002638323321221492, 0.18978654624225966), -new Complex(-5.3829294937018854, 2.7830576034063332), -new Complex(4.734152099371303, 11.062510314288577), -new Complex(-2.8298389130063937, -3.448368650672099), -new Complex(-0.024313261096907013, 5.986905188653619), -new Complex(-10.306371528157014, 2.7488670997481854), -new Complex(-3.715287570789169, 4.394518377168463), -new Complex(-2.402929134091579, -4.978313193479721), -new Complex(2.539933710216687, 2.609184677284304), -new Complex(-1.4339216539917672, -0.09877393185499805), -new Complex(3.602869514605274, -4.906754934650175), -new Complex(-3.8883597256888915, -6.509652164781107), -new Complex(-0.8134349081877765, 2.8827110905821733), -new Complex(-5.907774632673294, -3.6263871521782534), -new Complex(-1.0248996347650001, 5.221535985301575), -new Complex(-1.5110102858383998, -0.5916676752843197), -new Complex(-9.114112417604304, 5.863251584381027), -new Complex(-4.6861993279000735, -3.5540911138722056), -new Complex(1.643003673416314, 0.26848993074112837), -new Complex(-2.5917604439233606, 1.0744105534739878), -new Complex(-5.1589371747064, -3.713563429843524), -new Complex(-6.434479741808579, -6.482812912010042), -new Complex(-4.944944633762709, 1.4791201736168564), -new Complex(-4.196598202151179, 4.72746806821267), -new Complex(-8.342213173106137, 0.17252901564131817), -new Complex(-2.160469164514622, 5.322803319988891), -new Complex(-2.600011910104522, 0.9923343205338124), -new Complex(-9.934189775670099, -0.7971585720723513), -new Complex(-4.8493197811620155, 0.23933081088024055), -new Complex(0.9899999999999949, 2.049999999999997), -new Complex(0.46312513886469997, -1.083610713737262), -new Complex(-4.8684310845372405, 5.189260677129301), -new Complex(-8.26743857115093, 3.7039764685576912), -new Complex(-0.4020716349072171, 4.7080046554480655), -new Complex(-12.834400468053516, 1.9280109840029835), -new Complex(-0.471229754781314, 1.8877908393403002), -new Complex(-1.0412939420119778, -2.030434694825405), -new Complex(-7.976980514611285, -4.704983072668933), -new Complex(1.3875511565884113, 2.3921170941765437), -new Complex(-0.43177598562379993, 2.072966959680773), -new Complex(1.7056006968282746, -2.4801437158269524), -new Complex(-9.118052343426733, 0.7998264524711733), -new Complex(-4.03651091619774, 8.306905035682062), -new Complex(-8.966686390330285, -2.8174321266953237), -new Complex(-3.3209342577998275, 4.826944007044759), -new Complex(-8.156016294103669, 2.0796298320611637), -new Complex(-21.623922884263532, 12.12842939823654), -new Complex(-23.59827162418354, 16.220434411970665), -new Complex(-23.5007748625453, 12.601610600261623), -new Complex(-51.32832640613283, 23.124699639369688), -new Complex(-191.47173027366654, 117.08331011378777), -new Complex(90.94566028906652, -50.697370827337764), -new Complex(37.569125833815264, -21.57899450818642), -new Complex(18.47832853222178, -12.667650849412711), -new Complex(13.825097858675331, -8.427493951505243), -new Complex(2.1660703208919014, 0.160682198347327), -new Complex(18.743970629080533, -8.896718444008545), -new Complex(7.258232486620578, -2.765278876522548), -new Complex(-1.67079707818714, -2.1360151627523862), -new Complex(8.970903795772342, -4.066014902164978), -new Complex(-1.63248238200879, -7.790777253754383), -new Complex(7.821845003550149, 1.9841378599467063), -new Complex(-1.0480377808580839, -3.489347566529763), -new Complex(2.5131885002916463, -5.861603466455877), -new Complex(7.296132588971693, -4.341249905128436), -new Complex(10.426812822364907, 3.327862985228995), -new Complex(8.861238026483615, 0.9840687253038444), -new Complex(9.670660740183134, -4.95686087152842), -new Complex(-8.363128478889875, -9.156259453584862), -new Complex(6.946585621689808, -8.5029270402973), -new Complex(-0.30138424584816637, -2.313560920085428), -new Complex(-8.769932429732478, -5.336877373512683), -new Complex(7.557346542357086, -6.081240307670633), -new Complex(-3.785148744276337, -3.918211345581653), -new Complex(5.398310408984054, 7.203490766128711), -new Complex(1.0174178671562275, 8.607714904038463), -new Complex(5.439315586809667, 6.121406497914306), -new Complex(6.774385287871493, -9.629330590835693), -new Complex(3.1379824354268426, 6.257763391053615), -new Complex(-0.11732472297715368, -2.0845144608725157), -new Complex(5.925896594814177, -1.6214148805227864), -new Complex(0.6519047716752884, -8.365467739744261), -new Complex(4.848590964002236, -0.4155821028619928), -new Complex(-2.5076616550924316, 0.5616066179697503), -new Complex(-8.0636469706163, -7.706712825244248), -new Complex(-1.9130289340345779, -4.890086838063298), -new Complex(4.2847852895396334, -11.97800470285899), -new Complex(1.8672167221767484, -3.748497006463906), -new Complex(0.3204212874442627, -5.166759010512947), -new Complex(-2.797973427324931, 1.3949959065210518), -new Complex(-0.002595762684481251, -9.037941129554675), -new Complex(-0.6361813663828864, -6.4579860312158095), -new Complex(10.57113574570397, 7.214497105214866), -new Complex(-0.4420963209812834, -3.394939033455901), -new Complex(-1.1229763926872622, -1.3868358039512907), -new Complex(-2.7360757090613825, -8.309850036461313), -new Complex(8.120784577044823, 1.026413324380274), -new Complex(2.1486659809835205, 2.165175261206473), -new Complex(-5.99462407262711, -3.089373843767308), -new Complex(3.1011324895272074, -6.8893033010039995), -new Complex(-4.186255039897514, -1.883033665479715), -new Complex(-4.644687701252083, -13.308500752662315), -new Complex(-0.6346634815558894, -5.25029033494515), -new Complex(0.4407190353942614, -5.8677531912245335), -new Complex(-4.607834909859174, -2.7312265688684985), -new Complex(0.23366490444060828, -12.531156142249792), -new Complex(-0.18256077357631373, -4.335020541082644), -new Complex(-5.471846454174138, 3.6460414298884283), -new Complex(2.839320733358294, -6.1772220929963595), -new Complex(-1.402392307772061, -4.385841205789253), -new Complex(-5.303638228818758, -0.6631504207405765), -new Complex(-5.3005126045963635, -3.37872955018968), -new Complex(1.119097261414847, -7.382604722348194), -new Complex(-0.45457704436656776, -8.989165374120152), -new Complex(-6.782761994698582, -12.08745796987899), -new Complex(3.6651360344956814, -5.806578149334387), -new Complex(-5.831778282084541, -4.025856294966035), -new Complex(5.098783312195226, -4.827669742579823), -new Complex(-2.434216063490762, -11.25401150339216), -new Complex(-4.463444898038834, -2.2094751854191976), -new Complex(-4.655458008198199, 0.7217109700711257), -new Complex(4.091706423869164, -1.3657980239742185), -new Complex(-6.135998829269078, -7.692984484782302), -new Complex(-1.213105136690158, -6.993893647840464), -new Complex(-7.3702742982223475, -6.341810090314407), -new Complex(-12.27027085039127, -5.87765864860703), -new Complex(-10.466158383009784, -4.64345496924585), -new Complex(-9.241923204154393, 1.8473177393784537), -new Complex(-11.266585527651117, -8.094594399106146), -new Complex(-8.537098037861002, -8.885309121693309), -new Complex(-19.17337301270554, -16.4490076095894), -new Complex(-24.85859493213612, -13.64591851032932), -new Complex(-20.98624506290183, -15.99995475685203), -new Complex(-45.553168448188615, -26.107129887948112), -new Complex(-71.83350266181674, -50.22287606700222), -new Complex(-177.8491671506428, -105.09540721761977), -new Complex(366.88585732605077, 205.12349027793272), -new Complex(94.3053106250776, 51.97607828999656), -new Complex(59.25922110005847, 29.043963547908717), -new Complex(45.108520089797665, 20.58984095476262), -new Complex(31.077223230796157, 16.494436421460072), -new Complex(26.77189554895839, 16.352202578199066), -new Complex(21.50761285652196, 0.6992017699343238), -new Complex(16.65888595885314, 13.091972828691969), -new Complex(20.457611574040776, 7.5031116416854715), -new Complex(11.967450340118393, 6.99870154432028), -new Complex(14.963784782916644, 12.000161985633948), -new Complex(13.307669165815334, 4.967069973560196), -new Complex(9.653138749580414, 3.490781050813886), -new Complex(16.250262936175652, 10.898983835764772), -new Complex(16.81258122144206, -1.5049383063313797), -new Complex(18.814490506606106, 8.665625451587735), -new Complex(5.0913535569017805, 4.231871346529461), -new Complex(6.3392529423860475, -1.3944730123175701), -new Complex(11.867798701774106, -0.7429551118770841), -new Complex(16.588192205414742, 4.456837908626263), -new Complex(16.292599219664993, 1.6502662409530293), -}; + new(71.52, 0.0), + new(16.292599219664993, -1.650266240953031), + new(16.58819220541474, -4.456837908626264), + new(11.867798701774102, 0.7429551118770812), + new(6.339252942386048, 1.3944730123175706), + new(5.091353556901778, -4.23187134652945), + new(18.814490506606102, -8.665625451587731), + new(16.81258122144206, 1.5049383063313775), + new(16.250262936175652, -10.898983835764772), + new(9.653138749580403, -3.4907810508138843), + new(13.307669165815344, -4.967069973560199), + new(14.963784782916647, -12.000161985633959), + new(11.96745034011839, -6.99870154432028), + new(20.457611574040772, -7.503111641685473), + new(16.65888595885314, -13.091972828691972), + new(21.507612856521956, -0.699201769934322), + new(26.771895548958383, -16.35220257819907), + new(31.077223230796143, -16.494436421460076), + new(45.10852008979767, -20.58984095476262), + new(59.25922110005847, -29.04396354790871), + new(94.30531062507758, -51.976078289996565), + new(366.88585732605077, -205.12349027793272), + new(-177.8491671506428, 105.09540721761974), + new(-71.83350266181674, 50.22287606700222), + new(-45.553168448188615, 26.10712988794811), + new(-20.986245062901816, 15.999954756852027), + new(-24.858594932136125, 13.645918510329325), + new(-19.173373012705525, 16.449007609589398), + new(-8.537098037860998, 8.88530912169331), + new(-11.266585527651117, 8.094594399106153), + new(-9.241923204154396, -1.847317739378453), + new(-10.46615838300978, 4.64345496924585), + new(-12.27027085039127, 5.87765864860703), + new(-7.370274298222351, 6.341810090314409), + new(-1.2131051366901628, 6.993893647840464), + new(-6.1359988292690755, 7.692984484782312), + new(4.091706423869168, 1.365798023974214), + new(-4.655458008198221, -0.7217109700711242), + new(-4.463444898038833, 2.2094751854192), + new(-2.4342160634907675, 11.254011503392162), + new(5.098783312195228, 4.827669742579822), + new(-5.831778282084546, 4.025856294966041), + new(3.665136034495667, 5.806578149334394), + new(-6.782761994698575, 12.087457969879013), + new(-0.454577044366566, 8.989165374120152), + new(1.1190972614148489, 7.3826047223482005), + new(-5.300512604596365, 3.3787295501896804), + new(-5.303638228818758, 0.663150420740581), + new(-1.402392307772061, 4.385841205789253), + new(2.839320733358291, 6.177222092996357), + new(-5.471846454174141, -3.646041429888425), + new(-0.1825607735763244, 4.335020541082649), + new(0.23366490444061006, 12.53115614224979), + new(-4.607834909859168, 2.731226568868507), + new(0.4407190353942614, 5.867753191224533), + new(-0.6346634815558851, 5.2502903349451495), + new(-4.644687701252085, 13.308500752662315), + new(-4.186255039897505, 1.883033665479715), + new(3.1011324895272105, 6.889303301004), + new(-5.994624072627106, 3.0893738437673104), + new(2.148665980983523, -2.165175261206472), + new(8.120784577044827, -1.026413324380261), + new(-2.736075709061385, 8.309850036461317), + new(-1.122976392687268, 1.3868358039512898), + new(-0.4420963209812834, 3.394939033455901), + new(10.571135745703966, -7.214497105214868), + new(-0.6361813663828921, 6.457986031215809), + new(-0.0025957626844732573, 9.037941129554671), + new(-2.797973427324928, -1.3949959065210518), + new(0.32042128744426146, 5.166759010512948), + new(1.8672167221767477, 3.7484970064639), + new(4.284785289539634, 11.97800470285899), + new(-1.9130289340345799, 4.890086838063299), + new(-8.063646970616297, 7.70671282524425), + new(-2.5076616550924244, -0.5616066179697525), + new(4.848590964002215, 0.4155821028619835), + new(0.6519047716752894, 8.365467739744268), + new(5.925896594814173, 1.6214148805227862), + new(-0.11732472297714991, 2.084514460872517), + new(3.1379824354268417, -6.2577633910536195), + new(6.774385287871493, 9.629330590835693), + new(5.4393155868096645, -6.121406497914311), + new(1.0174178671562233, -8.607714904038465), + new(5.398310408984056, -7.203490766128718), + new(-3.785148744276335, 3.9182113455816534), + new(7.557346542357081, 6.081240307670624), + new(-8.76993242973248, 5.33687737351268), + new(-0.30138424584816725, 2.3135609200854255), + new(6.9465856216898105, 8.502927040297298), + new(-8.363128478889873, 9.156259453584855), + new(9.67066074018313, 4.95686087152842), + new(8.861238026483612, -0.9840687253038303), + new(10.426812822364912, -3.327862985228994), + new(7.296132588971696, 4.341249905128431), + new(2.513188500291645, 5.861603466455876), + new(-1.0480377808580927, 3.4893475665297666), + new(7.821845003550149, -1.9841378599467063), + new(-1.6324823820087873, 7.790777253754385), + new(8.970903795772337, 4.066014902164978), + new(-1.6707970781871344, 2.136015162752384), + new(7.25823248662058, 2.7652788765225464), + new(18.74397062908053, 8.896718444008549), + new(2.1660703208919037, -0.16068219834732878), + new(13.825097858675338, 8.427493951505237), + new(18.47832853222178, 12.667650849412713), + new(37.569125833815264, 21.578994508186412), + new(90.94566028906655, 50.697370827337764), + new(-191.4717302736666, -117.08331011378777), + new(-51.32832640613284, -23.124699639369688), + new(-23.500774862545306, -12.60161060026162), + new(-23.59827162418354, -16.220434411970672), + new(-21.623922884263543, -12.128429398236538), + new(-8.156016294103669, -2.0796298320611637), + new(-3.320934257799828, -4.826944007044764), + new(-8.966686390330285, 2.8174321266953233), + new(-4.036510916197747, -8.306905035682055), + new(-9.118052343426735, -0.7998264524711765), + new(1.7056006968282986, 2.48014371582694), + new(-0.4317759856237964, -2.0729669596807687), + new(1.3875511565884053, -2.3921170941765446), + new(-7.976980514611284, 4.704983072668933), + new(-1.0412939420119793, 2.0304346948254044), + new(-0.4712297547813127, -1.8877908393403056), + new(-12.834400468053518, -1.928010984003001), + new(-0.4020716349072191, -4.708004655448063), + new(-8.267438571150924, -3.7039764685576895), + new(-4.8684310845372405, -5.189260677129305), + new(0.4631251388647053, 1.0836107137372681), + new(0.9899999999999949, -2.049999999999997), + new(-4.84931978116202, -0.23933081088023878), + new(-9.934189775670093, 0.7971585720723557), + new(-2.600011910104527, -0.9923343205338109), + new(-2.160469164514625, -5.322803319988892), + new(-8.342213173106142, -0.17252901564132728), + new(-4.196598202151177, -4.7274680682126755), + new(-4.944944633762713, -1.4791201736168527), + new(-6.434479741808574, 6.482812912010042), + new(-5.158937174706397, 3.713563429843517), + new(-2.5917604439233597, -1.074410553473996), + new(1.6430036734162927, -0.26848993074113725), + new(-4.68619932790007, 3.5540911138722024), + new(-9.114112417604312, -5.863251584381021), + new(-1.511010285838398, 0.5916676752843228), + new(-1.0248996347649997, -5.221535985301575), + new(-5.907774632673288, 3.6263871521782516), + new(-0.8134349081877801, -2.8827110905821716), + new(-3.8883597256888898, 6.5096521647811105), + new(3.6028695146052687, 4.906754934650181), + new(-1.43392165399176, 0.09877393185500516), + new(2.5399337102167436, -2.6091846772843255), + new(-2.4029291340916004, 4.978313193479735), + new(-3.715287570789176, -4.39451837716846), + new(-10.306371528157012, -2.7488670997481837), + new(-0.024313261096906125, -5.986905188653612), + new(-2.8298389130063915, 3.448368650672101), + new(4.7341520993712995, -11.062510314288573), + new(-5.3829294937018854, -2.783057603406332), + new(0.0026383233212190493, -0.18978654624226898), + new(-1.8812828884644066, 4.871022697842905), + new(-3.2509005555527533, 1.4393838886348913), + new(-2.3369676800222434, 2.482582201564716), + new(-1.2142355119041524, 0.1834635506672304), + new(-9.582245656802916, -0.027020924935291735), + new(-6.581883483527928, -0.9168370285871652), + new(2.23633907309973, -2.3021508943840727), + new(-2.0748219655573625, -2.0037979487038773), + new(-3.765563906978262, 3.088432721629983), + new(5.332530718835812, -2.0516059158581865), + new(-0.2549012255641858, -0.6932903665209049), + new(-7.277178212857739, 2.9116631246832343), + new(-2.1936009994012435, 0.4001679051474425), + new(-8.794157662232939, 3.5717135906207176), + new(9.191032309546063, -1.9398929261634499), + new(-3.869682067783085, 0.5152936097585425), + new(3.147392558038452, 2.216334156350135), + new(4.337683896574441, 0.82997589223395), + new(5.8600818792670735, 0.02923610475152838), + new(-2.7839860346841525, 2.4914655265099466), + new(-1.903842415258608, 1.4113411741607438), + new(-9.894054706023422, 4.9668960901740284), + new(-0.8081382242155792, 11.349170741938877), + new(8.373197781633165, 2.1638002986517635), + new(-2.4852724753585846, 4.218258048332267), + new(-3.755737925182469, -4.490350833996583), + new(-2.879274257660312, 2.1626921190768043), + new(0.7024288556739815, -0.03525952952816347), + new(-2.673777491391463, 2.058724091865224), + new(2.1942581278786912, -4.751521874754496), + new(3.8112923807165675, 3.5946006951844867), + new(4.095532859213184, -0.28973422842765917), + new(-6.83825512481851, -9.6297170417451), + new(-1.9910301634396683, -10.188877845696899), + new(-1.4179036790187196, -3.8050609665441018), + new(-4.500591208775358, 2.5115489328726808), + new(2.5605067559698993, -1.0184596329131184), + new(6.717336341148254, -1.1098887472444834), + new(-3.6308727316719613, -2.930509025563401), + new(-1.2800687813817113, -5.2293267918825705), + new(-0.24178054270822846, -0.34674641143824747), + new(-3.5825821902320762, -6.877312499750982), + new(-2.602886071425474, -6.86407264784425), + new(-1.2785892290938932, -1.6397567663004344), + new(-8.675100612964963, -7.055963310707158), + new(8.84974247542792, -6.56325254726892), + new(2.885016983143895, -6.95362008379264), + new(6.918222156603157, 9.497082265446183), + new(4.10405309937396, -7.9120673382156275), + new(-1.584044627114519, -3.1949874308238275), + new(0.3670925294266434, -1.6673485244636947), + new(0.29461814038296197, -10.528089110189706), + new(10.194833330276264, -7.1146946624422025), + new(17.627086740678745, -7.638274663697334), + new(22.6731707587984, -11.536058001405237), + new(90.79168081510915, -41.13328830004425), + new(-43.229531626011074, 23.294890907589057), + new(-17.52387246706339, 7.853673956180025), + new(-15.806202527275513, -0.5183772215827949), + new(-25.289765401997723, 13.089544474677414), + new(-7.587901316147817, 6.612185770225549), + new(-7.7510301280002984, -4.450483522400161), + new(3.0154490825863447, 4.85718259595836), + new(-5.588006589467025, 1.8753775661904744), + new(-4.522700439505712, -6.941653406278663), + new(-9.29990757512051, 7.73164297352557), + new(-15.054606473136637, -4.415621289881546), + new(-5.649212036777634, -3.004224743561956), + new(-5.3245866110889075, -2.3232079416455855), + new(-6.09582893633526, -2.833602503098011), + new(2.3719671688372457, 7.823224635184997), + new(-4.378899544434763, -2.1004484536234473), + new(-1.847804254120236, -1.2174634584260025), + new(-7.151969645971247, 5.608493469097141), + new(-6.962043610144818, 2.4741041607524394), + new(-0.9411822402381276, 6.274216197339793), + new(0.501900088529041, -4.787955462633128), + new(-1.1594019555348325, -3.84174782623154), + new(3.3001766896560056, 2.9594456628498946), + new(-10.066436810536924, 0.6384104860672224), + new(-7.19567825040399, -3.2261296085795568), + new(-4.691470830071458, 2.9464762422417987), + new(-0.14727201097456977, -4.859280838128437), + new(-14.56692223701291, -7.90084256027122), + new(-4.324446487468897, 0.004185311431190719), + new(-1.0407514948403307, 2.059605296404119), + new(-2.98340696573519, -3.1029075652331937), + new(-4.636760771943916, 10.373963334679214), + new(-8.26960957572124, 0.7354864925759217), + new(-4.979951151973244, 2.4789920137824786), + new(1.4400641578399824, 0.8869891013658968), + new(0.6818750261182362, 2.470099039214686), + new(8.336841493956832, 7.404955214008821), + new(2.8157431492244953, -6.567660517080393), + new(-5.878260543893631, 8.516509649574305), + new(2.879678032286943, -3.1017131232821384), + new(-3.801558789466788, -2.7381256767431728), + new(-0.27745160787512013, -7.4993828895293335), + new(11.019999999999996, 0.0), + new(-0.2774516078751077, 7.499382889529332), + new(-3.801558789466789, 2.738125676743172), + new(2.8796780322869546, 3.101713123282132), + new(-5.87826054389363, -8.516509649574308), + new(2.8157431492244895, 6.567660517080403), + new(8.336841493956836, -7.404955214008817), + new(0.6818750261182362, -2.470099039214685), + new(1.4400641578399824, -0.8869891013658968), + new(-4.979951151973249, -2.4789920137824777), + new(-8.269609575721244, -0.7354864925759133), + new(-4.636760771943918, -10.373963334679196), + new(-2.9834069657351927, 3.1029075652331986), + new(-1.0407514948403325, -2.0596052964041296), + new(-4.324446487468897, -0.004185311431191607), + new(-14.566922237012907, 7.900842560271221), + new(-0.14727201097457154, 4.85928083812844), + new(-4.691470830071458, -2.9464762422418005), + new(-7.195678250403994, 3.2261296085795585), + new(-10.066436810536924, -0.6384104860672206), + new(3.3001766896559985, -2.9594456628499017), + new(-1.1594019555349178, 3.84174782623154), + new(0.5019000885290694, 4.787955462633128), + new(-0.9411822402381276, -6.274216197339783), + new(-6.962043610144818, -2.4741041607524394), + new(-7.151969645971249, -5.608493469097138), + new(-1.8478042541202342, 1.2174634584260051), + new(-4.378899544434763, 2.100448453623444), + new(2.3719671688372457, -7.8232246351849986), + new(-6.09582893633526, 2.833602503098014), + new(-5.324586611088915, 2.3232079416455873), + new(-5.649212036777634, 3.0042247435619593), + new(-15.054606473136637, 4.415621289881546), + new(-9.299907575120518, -7.731642973525572), + new(-4.522700439505707, 6.941653406278663), + new(-5.58800658946703, -1.8753775661904735), + new(3.015449082586345, -4.85718259595836), + new(-7.751030128000305, 4.45048352240015), + new(-7.587901316147819, -6.612185770225553), + new(-25.289765401997727, -13.089544474677405), + new(-15.806202527275513, 0.5183772215827931), + new(-17.523872467063388, -7.853673956180031), + new(-43.229531626011074, -23.294890907589064), + new(90.79168081510915, 41.13328830004425), + new(22.6731707587984, 11.536058001405241), + new(17.62708674067875, 7.6382746636973495), + new(10.194833330276262, 7.114694662442204), + new(0.2946181403829593, 10.528089110189704), + new(0.3670925294266425, 1.6673485244636956), + new(-1.584044627114522, 3.1949874308238337), + new(4.104053099373957, 7.912067338215623), + new(6.918222156603163, -9.497082265446183), + new(2.8850169831438968, 6.953620083792638), + new(8.849742475427908, 6.563252547268917), + new(-8.675100612964956, 7.0559633107071535), + new(-1.2785892290938947, 1.6397567663004302), + new(-2.602886071425475, 6.86407264784425), + new(-3.5825821902320816, 6.877312499750978), + new(-0.2417805427082258, 0.346746411438247), + new(-1.280068781381706, 5.229326791882579), + new(-3.6308727316719613, 2.930509025563401), + new(6.717336341148264, 1.1098887472444712), + new(2.560506755969895, 1.0184596329131175), + new(-4.500591208775362, -2.511548932872677), + new(-1.4179036790187196, 3.8050609665441018), + new(-1.9910301634396674, 10.1888778456969), + new(-6.838255124818515, 9.629717041745097), + new(4.095532859213191, 0.28973422842765384), + new(3.8112923807165693, -3.5946006951844875), + new(2.194258127878689, 4.751521874754488), + new(-2.6737774913914603, -2.0587240918652236), + new(0.7024288556739806, 0.03525952952816347), + new(-2.8792742576603123, -2.162692119076805), + new(-3.7557379251824736, 4.490350833996582), + new(-2.4852724753585793, -4.21825804833226), + new(8.373197781633138, -2.1638002986517675), + new(-0.8081382242155787, -11.349170741938874), + new(-9.894054706023425, -4.966896090174037), + new(-1.9038424152586149, -1.4113411741607438), + new(-2.78398603468415, -2.491465526509942), + new(5.8600818792670735, -0.029236104751530156), + new(4.337683896574438, -0.8299758922339566), + new(3.147392558038458, -2.216334156350134), + new(-3.869682067783092, -0.5152936097585532), + new(9.191032309546063, 1.9398929261634485), + new(-8.79415766223292, -3.5717135906207194), + new(-2.193600999401239, -0.40016790514745004), + new(-7.277178212857741, -2.911663124683228), + new(-0.2549012255641854, 0.6932903665209089), + new(5.3325307188358035, 2.0516059158581808), + new(-3.765563906978266, -3.0884327216299763), + new(-2.0748219655573683, 2.0037979487038813), + new(2.236339073099729, 2.3021508943840754), + new(-6.581883483527934, 0.9168370285871648), + new(-9.582245656802918, 0.027020924935293067), + new(-1.2142355119041532, -0.18346355066723263), + new(-2.3369676800222434, -2.482582201564716), + new(-3.2509005555527524, -1.439383888634893), + new(-1.881282888464412, -4.871022697842905), + new(0.002638323321221492, 0.18978654624225966), + new(-5.3829294937018854, 2.7830576034063332), + new(4.734152099371303, 11.062510314288577), + new(-2.8298389130063937, -3.448368650672099), + new(-0.024313261096907013, 5.986905188653619), + new(-10.306371528157014, 2.7488670997481854), + new(-3.715287570789169, 4.394518377168463), + new(-2.402929134091579, -4.978313193479721), + new(2.539933710216687, 2.609184677284304), + new(-1.4339216539917672, -0.09877393185499805), + new(3.602869514605274, -4.906754934650175), + new(-3.8883597256888915, -6.509652164781107), + new(-0.8134349081877765, 2.8827110905821733), + new(-5.907774632673294, -3.6263871521782534), + new(-1.0248996347650001, 5.221535985301575), + new(-1.5110102858383998, -0.5916676752843197), + new(-9.114112417604304, 5.863251584381027), + new(-4.6861993279000735, -3.5540911138722056), + new(1.643003673416314, 0.26848993074112837), + new(-2.5917604439233606, 1.0744105534739878), + new(-5.1589371747064, -3.713563429843524), + new(-6.434479741808579, -6.482812912010042), + new(-4.944944633762709, 1.4791201736168564), + new(-4.196598202151179, 4.72746806821267), + new(-8.342213173106137, 0.17252901564131817), + new(-2.160469164514622, 5.322803319988891), + new(-2.600011910104522, 0.9923343205338124), + new(-9.934189775670099, -0.7971585720723513), + new(-4.8493197811620155, 0.23933081088024055), + new(0.9899999999999949, 2.049999999999997), + new(0.46312513886469997, -1.083610713737262), + new(-4.8684310845372405, 5.189260677129301), + new(-8.26743857115093, 3.7039764685576912), + new(-0.4020716349072171, 4.7080046554480655), + new(-12.834400468053516, 1.9280109840029835), + new(-0.471229754781314, 1.8877908393403002), + new(-1.0412939420119778, -2.030434694825405), + new(-7.976980514611285, -4.704983072668933), + new(1.3875511565884113, 2.3921170941765437), + new(-0.43177598562379993, 2.072966959680773), + new(1.7056006968282746, -2.4801437158269524), + new(-9.118052343426733, 0.7998264524711733), + new(-4.03651091619774, 8.306905035682062), + new(-8.966686390330285, -2.8174321266953237), + new(-3.3209342577998275, 4.826944007044759), + new(-8.156016294103669, 2.0796298320611637), + new(-21.623922884263532, 12.12842939823654), + new(-23.59827162418354, 16.220434411970665), + new(-23.5007748625453, 12.601610600261623), + new(-51.32832640613283, 23.124699639369688), + new(-191.47173027366654, 117.08331011378777), + new(90.94566028906652, -50.697370827337764), + new(37.569125833815264, -21.57899450818642), + new(18.47832853222178, -12.667650849412711), + new(13.825097858675331, -8.427493951505243), + new(2.1660703208919014, 0.160682198347327), + new(18.743970629080533, -8.896718444008545), + new(7.258232486620578, -2.765278876522548), + new(-1.67079707818714, -2.1360151627523862), + new(8.970903795772342, -4.066014902164978), + new(-1.63248238200879, -7.790777253754383), + new(7.821845003550149, 1.9841378599467063), + new(-1.0480377808580839, -3.489347566529763), + new(2.5131885002916463, -5.861603466455877), + new(7.296132588971693, -4.341249905128436), + new(10.426812822364907, 3.327862985228995), + new(8.861238026483615, 0.9840687253038444), + new(9.670660740183134, -4.95686087152842), + new(-8.363128478889875, -9.156259453584862), + new(6.946585621689808, -8.5029270402973), + new(-0.30138424584816637, -2.313560920085428), + new(-8.769932429732478, -5.336877373512683), + new(7.557346542357086, -6.081240307670633), + new(-3.785148744276337, -3.918211345581653), + new(5.398310408984054, 7.203490766128711), + new(1.0174178671562275, 8.607714904038463), + new(5.439315586809667, 6.121406497914306), + new(6.774385287871493, -9.629330590835693), + new(3.1379824354268426, 6.257763391053615), + new(-0.11732472297715368, -2.0845144608725157), + new(5.925896594814177, -1.6214148805227864), + new(0.6519047716752884, -8.365467739744261), + new(4.848590964002236, -0.4155821028619928), + new(-2.5076616550924316, 0.5616066179697503), + new(-8.0636469706163, -7.706712825244248), + new(-1.9130289340345779, -4.890086838063298), + new(4.2847852895396334, -11.97800470285899), + new(1.8672167221767484, -3.748497006463906), + new(0.3204212874442627, -5.166759010512947), + new(-2.797973427324931, 1.3949959065210518), + new(-0.002595762684481251, -9.037941129554675), + new(-0.6361813663828864, -6.4579860312158095), + new(10.57113574570397, 7.214497105214866), + new(-0.4420963209812834, -3.394939033455901), + new(-1.1229763926872622, -1.3868358039512907), + new(-2.7360757090613825, -8.309850036461313), + new(8.120784577044823, 1.026413324380274), + new(2.1486659809835205, 2.165175261206473), + new(-5.99462407262711, -3.089373843767308), + new(3.1011324895272074, -6.8893033010039995), + new(-4.186255039897514, -1.883033665479715), + new(-4.644687701252083, -13.308500752662315), + new(-0.6346634815558894, -5.25029033494515), + new(0.4407190353942614, -5.8677531912245335), + new(-4.607834909859174, -2.7312265688684985), + new(0.23366490444060828, -12.531156142249792), + new(-0.18256077357631373, -4.335020541082644), + new(-5.471846454174138, 3.6460414298884283), + new(2.839320733358294, -6.1772220929963595), + new(-1.402392307772061, -4.385841205789253), + new(-5.303638228818758, -0.6631504207405765), + new(-5.3005126045963635, -3.37872955018968), + new(1.119097261414847, -7.382604722348194), + new(-0.45457704436656776, -8.989165374120152), + new(-6.782761994698582, -12.08745796987899), + new(3.6651360344956814, -5.806578149334387), + new(-5.831778282084541, -4.025856294966035), + new(5.098783312195226, -4.827669742579823), + new(-2.434216063490762, -11.25401150339216), + new(-4.463444898038834, -2.2094751854191976), + new(-4.655458008198199, 0.7217109700711257), + new(4.091706423869164, -1.3657980239742185), + new(-6.135998829269078, -7.692984484782302), + new(-1.213105136690158, -6.993893647840464), + new(-7.3702742982223475, -6.341810090314407), + new(-12.27027085039127, -5.87765864860703), + new(-10.466158383009784, -4.64345496924585), + new(-9.241923204154393, 1.8473177393784537), + new(-11.266585527651117, -8.094594399106146), + new(-8.537098037861002, -8.885309121693309), + new(-19.17337301270554, -16.4490076095894), + new(-24.85859493213612, -13.64591851032932), + new(-20.98624506290183, -15.99995475685203), + new(-45.553168448188615, -26.107129887948112), + new(-71.83350266181674, -50.22287606700222), + new(-177.8491671506428, -105.09540721761977), + new(366.88585732605077, 205.12349027793272), + new(94.3053106250776, 51.97607828999656), + new(59.25922110005847, 29.043963547908717), + new(45.108520089797665, 20.58984095476262), + new(31.077223230796157, 16.494436421460072), + new(26.77189554895839, 16.352202578199066), + new(21.50761285652196, 0.6992017699343238), + new(16.65888595885314, 13.091972828691969), + new(20.457611574040776, 7.5031116416854715), + new(11.967450340118393, 6.99870154432028), + new(14.963784782916644, 12.000161985633948), + new(13.307669165815334, 4.967069973560196), + new(9.653138749580414, 3.490781050813886), + new(16.250262936175652, 10.898983835764772), + new(16.81258122144206, -1.5049383063313797), + new(18.814490506606106, 8.665625451587735), + new(5.0913535569017805, 4.231871346529461), + new(6.3392529423860475, -1.3944730123175701), + new(11.867798701774106, -0.7429551118770841), + new(16.588192205414742, 4.456837908626263), + new(16.292599219664993, 1.6502662409530293), + }; - private readonly Complex[] NumpyRFFT = + private readonly System.Numerics.Complex[] NumpyRFFT = { -new Complex(71.52, 0.0), -new Complex(16.292599219664986, -1.650266240953031), -new Complex(16.588192205414742, -4.456837908626266), -new Complex(11.867798701774108, 0.7429551118770739), -new Complex(6.339252942386047, 1.3944730123175706), -new Complex(5.091353556901781, -4.231871346529461), -new Complex(18.8144905066061, -8.665625451587735), -new Complex(16.81258122144206, 1.5049383063313806), -new Complex(16.250262936175655, -10.898983835764774), -new Complex(9.65313874958041, -3.49078105081389), -new Complex(13.307669165815337, -4.967069973560201), -new Complex(14.963784782916647, -12.000161985633948), -new Complex(11.96745034011839, -6.998701544320284), -new Complex(20.457611574040772, -7.503111641685466), -new Complex(16.658885958853144, -13.091972828691969), -new Complex(21.507612856521952, -0.6992017699343278), -new Complex(26.771895548958383, -16.35220257819907), -new Complex(31.077223230796147, -16.494436421460072), -new Complex(45.10852008979767, -20.589840954762618), -new Complex(59.259221100058475, -29.043963547908714), -new Complex(94.30531062507757, -51.97607828999656), -new Complex(366.8858573260508, -205.12349027793277), -new Complex(-177.84916715064278, 105.09540721761974), -new Complex(-71.83350266181674, 50.22287606700222), -new Complex(-45.553168448188615, 26.10712988794811), -new Complex(-20.986245062901823, 15.999954756852036), -new Complex(-24.85859493213612, 13.645918510329333), -new Complex(-19.17337301270553, 16.449007609589387), -new Complex(-8.537098037861, 8.88530912169331), -new Complex(-11.266585527651117, 8.094594399106143), -new Complex(-9.241923204154396, -1.8473177393784619), -new Complex(-10.466158383009773, 4.643454969245847), -new Complex(-12.27027085039127, 5.877658648607031), -new Complex(-7.370274298222345, 6.341810090314407), -new Complex(-1.213105136690162, 6.993893647840459), -new Complex(-6.1359988292690755, 7.692984484782306), -new Complex(4.091706423869168, 1.3657980239742202), -new Complex(-4.655458008198215, -0.7217109700711151), -new Complex(-4.4634448980388335, 2.2094751854191967), -new Complex(-2.434216063490762, 11.25401150339216), -new Complex(5.098783312195228, 4.827669742579822), -new Complex(-5.831778282084553, 4.025856294966037), -new Complex(3.6651360344956743, 5.806578149334403), -new Complex(-6.782761994698575, 12.087457969879019), -new Complex(-0.45457704436657487, 8.989165374120159), -new Complex(1.1190972614148507, 7.382604722348191), -new Complex(-5.300512604596362, 3.378729550189676), -new Complex(-5.3036382288187545, 0.6631504207405756), -new Complex(-1.402392307772061, 4.385841205789253), -new Complex(2.8393207333582873, 6.177222092996363), -new Complex(-5.471846454174142, -3.6460414298884287), -new Complex(-0.1825607735763195, 4.335020541082642), -new Complex(0.23366490444060628, 12.531156142249785), -new Complex(-4.607834909859171, 2.7312265688685136), -new Complex(0.4407190353942596, 5.867753191224537), -new Complex(-0.6346634815558827, 5.250290334945159), -new Complex(-4.644687701252086, 13.308500752662315), -new Complex(-4.186255039897507, 1.883033665479711), -new Complex(3.101132489527204, 6.889303301004002), -new Complex(-5.9946240726271025, 3.0893738437673117), -new Complex(2.148665980983527, -2.1651752612064716), -new Complex(8.120784577044823, -1.0264133243802687), -new Complex(-2.7360757090613825, 8.309850036461313), -new Complex(-1.1229763926872607, 1.3868358039512911), -new Complex(-0.4420963209812834, 3.394939033455901), -new Complex(10.571135745703966, -7.214497105214872), -new Complex(-0.6361813663828926, 6.457986031215812), -new Complex(-0.0025957626844785864, 9.037941129554667), -new Complex(-2.79797342732493, -1.3949959065210513), -new Complex(0.3204212874442537, 5.1667590105129415), -new Complex(1.8672167221767446, 3.7484970064638996), -new Complex(4.284785289539631, 11.978004702858989), -new Complex(-1.9130289340345774, 4.890086838063301), -new Complex(-8.0636469706163, 7.706712825244253), -new Complex(-2.507661655092423, -0.5616066179697499), -new Complex(4.848590964002231, 0.4155821028619948), -new Complex(0.6519047716752902, 8.365467739744261), -new Complex(5.925896594814184, 1.6214148805227837), -new Complex(-0.11732472297715268, 2.084514460872518), -new Complex(3.1379824354268404, -6.257763391053611), -new Complex(6.774385287871493, 9.629330590835693), -new Complex(5.4393155868096645, -6.121406497914308), -new Complex(1.017417867156226, -8.607714904038467), -new Complex(5.398310408984063, -7.203490766128716), -new Complex(-3.7851487442763334, 3.9182113455816596), -new Complex(7.557346542357088, 6.08124030767064), -new Complex(-8.769932429732474, 5.336877373512683), -new Complex(-0.3013842458481659, 2.313560920085437), -new Complex(6.946585621689808, 8.502927040297298), -new Complex(-8.363128478889877, 9.156259453584862), -new Complex(9.670660740183127, 4.956860871528423), -new Complex(8.861238026483619, -0.9840687253038265), -new Complex(10.426812822364909, -3.3278629852289914), -new Complex(7.296132588971698, 4.341249905128432), -new Complex(2.513188500291643, 5.861603466455874), -new Complex(-1.048037780858094, 3.489347566529764), -new Complex(7.821845003550148, -1.984137859946705), -new Complex(-1.6324823820087966, 7.790777253754383), -new Complex(8.970903795772337, 4.066014902164974), -new Complex(-1.6707970781871395, 2.136015162752383), -new Complex(7.2582324866205825, 2.765278876522548), -new Complex(18.743970629080543, 8.896718444008545), -new Complex(2.1660703208919028, -0.16068219834732655), -new Complex(13.825097858675331, 8.427493951505237), -new Complex(18.478328532221784, 12.667650849412713), -new Complex(37.569125833815264, 21.578994508186412), -new Complex(90.94566028906652, 50.697370827337764), -new Complex(-191.47173027366662, -117.0833101137878), -new Complex(-51.32832640613284, -23.124699639369688), -new Complex(-23.500774862545306, -12.601610600261623), -new Complex(-23.598271624183546, -16.220434411970665), -new Complex(-21.623922884263536, -12.128429398236536), -new Complex(-8.156016294103667, -2.079629832061165), -new Complex(-3.3209342577998284, -4.826944007044762), -new Complex(-8.966686390330285, 2.8174321266953246), -new Complex(-4.036510916197741, -8.306905035682057), -new Complex(-9.118052343426735, -0.7998264524711769), -new Complex(1.705600696828284, 2.4801437158269257), -new Complex(-0.4317759856237946, -2.0729669596807727), -new Complex(1.3875511565884089, -2.3921170941765446), -new Complex(-7.976980514611284, 4.704983072668934), -new Complex(-1.0412939420119758, 2.0304346948254097), -new Complex(-0.471229754781314, -1.8877908393403056), -new Complex(-12.834400468053524, -1.9280109840030062), -new Complex(-0.40207163490721864, -4.708004655448063), -new Complex(-8.26743857115093, -3.7039764685576984), -new Complex(-4.868431084537242, -5.189260677129303), -new Complex(0.4631251388647035, 1.0836107137372712), -new Complex(0.9899999999999949, -2.049999999999997), -new Complex(-4.849319781162014, -0.2393308108802369), -new Complex(-9.934189775670095, 0.7971585720723549), -new Complex(-2.6000119101045227, -0.9923343205338122), -new Complex(-2.1604691645146215, -5.322803319988891), -new Complex(-8.342213173106131, -0.17252901564132417), -new Complex(-4.19659820215117, -4.727468068212671), -new Complex(-4.944944633762708, -1.4791201736168522), -new Complex(-6.434479741808575, 6.482812912010042), -new Complex(-5.1589371747063995, 3.7135634298435116), -new Complex(-2.5917604439233606, -1.0744105534739985), -new Complex(1.6430036734163007, -0.2684899307411228), -new Complex(-4.686199327900066, 3.554091113872207), -new Complex(-9.114112417604307, -5.863251584381021), -new Complex(-1.5110102858383967, 0.5916676752843237), -new Complex(-1.0248996347650015, -5.221535985301577), -new Complex(-5.90777463267329, 3.6263871521782503), -new Complex(-0.8134349081877801, -2.882711090582175), -new Complex(-3.8883597256888844, 6.509652164781103), -new Complex(3.6028695146052705, 4.9067549346501815), -new Complex(-1.43392165399176, 0.0987739318549945), -new Complex(2.539933710216701, -2.609184677284297), -new Complex(-2.4029291340916075, 4.978313193479746), -new Complex(-3.7152875707891617, -4.394518377168467), -new Complex(-10.306371528157012, -2.7488670997481854), -new Complex(-0.02431326109690435, -5.986905188653616), -new Complex(-2.829838913006398, 3.4483686506720996), -new Complex(4.734152099371297, -11.06251031428857), -new Complex(-5.382929493701886, -2.783057603406332), -new Complex(0.0026383233212212698, -0.18978654624226154), -new Complex(-1.881282888464411, 4.871022697842906), -new Complex(-3.25090055555276, 1.439383888634893), -new Complex(-2.3369676800222425, 2.4825822015647154), -new Complex(-1.2142355119041537, 0.18346355066723175), -new Complex(-9.582245656802915, -0.02702092493529351), -new Complex(-6.581883483527932, -0.9168370285871694), -new Complex(2.2363390730997272, -2.3021508943840754), -new Complex(-2.0748219655573523, -2.0037979487038737), -new Complex(-3.7655639069782607, 3.0884327216299807), -new Complex(5.332530718835811, -2.05160591585819), -new Complex(-0.2549012255641827, -0.6932903665209045), -new Complex(-7.277178212857729, 2.9116631246832334), -new Complex(-2.19360099940124, 0.40016790514743894), -new Complex(-8.794157662232948, 3.571713590620707), -new Complex(9.191032309546067, -1.9398929261634494), -new Complex(-3.869682067783092, 0.5152936097585563), -new Complex(3.1473925580384554, 2.2163341563501353), -new Complex(4.3376838965744415, 0.829975892233958), -new Complex(5.8600818792670735, 0.029236104751530156), -new Complex(-2.7839860346841467, 2.4914655265099475), -new Complex(-1.9038424152586106, 1.4113411741607425), -new Complex(-9.894054706023423, 4.966896090174032), -new Complex(-0.8081382242155761, 11.349170741938874), -new Complex(8.373197781633154, 2.163800298651763), -new Complex(-2.4852724753585824, 4.218258048332261), -new Complex(-3.755737925182478, -4.490350833996587), -new Complex(-2.8792742576603123, 2.1626921190768047), -new Complex(0.7024288556739773, -0.035259529528166134), -new Complex(-2.673777491391459, 2.058724091865225), -new Complex(2.194258127878678, -4.751521874754496), -new Complex(3.8112923807165657, 3.594600695184486), -new Complex(4.095532859213183, -0.28973422842766006), -new Complex(-6.838255124818513, -9.629717041745101), -new Complex(-1.9910301634396719, -10.188877845696904), -new Complex(-1.4179036790187196, -3.8050609665441018), -new Complex(-4.500591208775357, 2.5115489328726808), -new Complex(2.5605067559698993, -1.0184596329131206), -new Complex(6.717336341148265, -1.109888747244478), -new Complex(-3.630872731671961, -2.9305090255634014), -new Complex(-1.2800687813817038, -5.229326791882572), -new Complex(-0.2417805427082249, -0.34674641143824525), -new Complex(-3.582582190232076, -6.877312499750984), -new Complex(-2.6028860714254742, -6.864072647844253), -new Complex(-1.278589229093891, -1.639756766300434), -new Complex(-8.67510061296496, -7.05596331070716), -new Complex(8.849742475427906, -6.5632525472689105), -new Complex(2.8850169831438945, -6.953620083792635), -new Complex(6.918222156603155, 9.497082265446181), -new Complex(4.104053099373963, -7.912067338215625), -new Complex(-1.5840446271145168, -3.1949874308238315), -new Complex(0.3670925294266435, -1.6673485244636947), -new Complex(0.2946181403829611, -10.528089110189708), -new Complex(10.194833330276262, -7.114694662442203), -new Complex(17.62708674067874, -7.638274663697333), -new Complex(22.6731707587984, -11.536058001405237), -new Complex(90.79168081510915, -41.13328830004425), -new Complex(-43.22953162601108, 23.29489090758906), -new Complex(-17.523872467063395, 7.853673956180025), -new Complex(-15.806202527275513, -0.5183772215827918), -new Complex(-25.289765401997723, 13.089544474677407), -new Complex(-7.587901316147816, 6.612185770225552), -new Complex(-7.751030128000307, -4.450483522400161), -new Complex(3.0154490825863447, 4.857182595958358), -new Complex(-5.58800658946703, 1.8753775661904708), -new Complex(-4.522700439505712, -6.9416534062786655), -new Complex(-9.299907575120514, 7.73164297352557), -new Complex(-15.054606473136637, -4.415621289881547), -new Complex(-5.649212036777629, -3.004224743561953), -new Complex(-5.324586611088913, -2.3232079416455873), -new Complex(-6.095828936335264, -2.8336025030980143), -new Complex(2.3719671688372443, 7.8232246351849986), -new Complex(-4.378899544434765, -2.1004484536234376), -new Complex(-1.8478042541202289, -1.2174634584260051), -new Complex(-7.151969645971242, 5.608493469097139), -new Complex(-6.962043610144821, 2.474104160752436), -new Complex(-0.9411822402381418, 6.274216197339786), -new Complex(0.5019000885290552, -4.787955462633121), -new Complex(-1.159401955534861, -3.8417478262315257), -new Complex(3.3001766896560056, 2.9594456628498946), -new Complex(-10.06643681053692, 0.638410486067226), -new Complex(-7.195678250403994, -3.226129608579562), -new Complex(-4.691470830071456, 2.946476242241795), -new Complex(-0.14727201097456977, -4.85928083812844), -new Complex(-14.566922237012909, -7.900842560271224), -new Complex(-4.3244464874689, 0.004185311431188055), -new Complex(-1.040751494840329, 2.0596052964041256), -new Complex(-2.9834069657351936, -3.1029075652331937), -new Complex(-4.636760771943913, 10.373963334679232), -new Complex(-8.269609575721237, 0.7354864925759204), -new Complex(-4.9799511519732516, 2.478992013782473), -new Complex(1.4400641578399807, 0.8869891013658959), -new Complex(0.6818750261182345, 2.4700990392146815), -new Complex(8.336841493956832, 7.404955214008821), -new Complex(2.815743149224481, -6.567660517080393), -new Complex(-5.87826054389363, 8.516509649574305), -new Complex(2.8796780322869413, -3.1017131232821358), -new Complex(-3.801558789466787, -2.7381256767431754), -new Complex(-0.2774516078751166, -7.499382889529337), -new Complex(11.019999999999996, 0.0) -}; + new(71.52, 0.0), + new(16.292599219664986, -1.650266240953031), + new(16.588192205414742, -4.456837908626266), + new(11.867798701774108, 0.7429551118770739), + new(6.339252942386047, 1.3944730123175706), + new(5.091353556901781, -4.231871346529461), + new(18.8144905066061, -8.665625451587735), + new(16.81258122144206, 1.5049383063313806), + new(16.250262936175655, -10.898983835764774), + new(9.65313874958041, -3.49078105081389), + new(13.307669165815337, -4.967069973560201), + new(14.963784782916647, -12.000161985633948), + new(11.96745034011839, -6.998701544320284), + new(20.457611574040772, -7.503111641685466), + new(16.658885958853144, -13.091972828691969), + new(21.507612856521952, -0.6992017699343278), + new(26.771895548958383, -16.35220257819907), + new(31.077223230796147, -16.494436421460072), + new(45.10852008979767, -20.589840954762618), + new(59.259221100058475, -29.043963547908714), + new(94.30531062507757, -51.97607828999656), + new(366.8858573260508, -205.12349027793277), + new(-177.84916715064278, 105.09540721761974), + new(-71.83350266181674, 50.22287606700222), + new(-45.553168448188615, 26.10712988794811), + new(-20.986245062901823, 15.999954756852036), + new(-24.85859493213612, 13.645918510329333), + new(-19.17337301270553, 16.449007609589387), + new(-8.537098037861, 8.88530912169331), + new(-11.266585527651117, 8.094594399106143), + new(-9.241923204154396, -1.8473177393784619), + new(-10.466158383009773, 4.643454969245847), + new(-12.27027085039127, 5.877658648607031), + new(-7.370274298222345, 6.341810090314407), + new(-1.213105136690162, 6.993893647840459), + new(-6.1359988292690755, 7.692984484782306), + new(4.091706423869168, 1.3657980239742202), + new(-4.655458008198215, -0.7217109700711151), + new(-4.4634448980388335, 2.2094751854191967), + new(-2.434216063490762, 11.25401150339216), + new(5.098783312195228, 4.827669742579822), + new(-5.831778282084553, 4.025856294966037), + new(3.6651360344956743, 5.806578149334403), + new(-6.782761994698575, 12.087457969879019), + new(-0.45457704436657487, 8.989165374120159), + new(1.1190972614148507, 7.382604722348191), + new(-5.300512604596362, 3.378729550189676), + new(-5.3036382288187545, 0.6631504207405756), + new(-1.402392307772061, 4.385841205789253), + new(2.8393207333582873, 6.177222092996363), + new(-5.471846454174142, -3.6460414298884287), + new(-0.1825607735763195, 4.335020541082642), + new(0.23366490444060628, 12.531156142249785), + new(-4.607834909859171, 2.7312265688685136), + new(0.4407190353942596, 5.867753191224537), + new(-0.6346634815558827, 5.250290334945159), + new(-4.644687701252086, 13.308500752662315), + new(-4.186255039897507, 1.883033665479711), + new(3.101132489527204, 6.889303301004002), + new(-5.9946240726271025, 3.0893738437673117), + new(2.148665980983527, -2.1651752612064716), + new(8.120784577044823, -1.0264133243802687), + new(-2.7360757090613825, 8.309850036461313), + new(-1.1229763926872607, 1.3868358039512911), + new(-0.4420963209812834, 3.394939033455901), + new(10.571135745703966, -7.214497105214872), + new(-0.6361813663828926, 6.457986031215812), + new(-0.0025957626844785864, 9.037941129554667), + new(-2.79797342732493, -1.3949959065210513), + new(0.3204212874442537, 5.1667590105129415), + new(1.8672167221767446, 3.7484970064638996), + new(4.284785289539631, 11.978004702858989), + new(-1.9130289340345774, 4.890086838063301), + new(-8.0636469706163, 7.706712825244253), + new(-2.507661655092423, -0.5616066179697499), + new(4.848590964002231, 0.4155821028619948), + new(0.6519047716752902, 8.365467739744261), + new(5.925896594814184, 1.6214148805227837), + new(-0.11732472297715268, 2.084514460872518), + new(3.1379824354268404, -6.257763391053611), + new(6.774385287871493, 9.629330590835693), + new(5.4393155868096645, -6.121406497914308), + new(1.017417867156226, -8.607714904038467), + new(5.398310408984063, -7.203490766128716), + new(-3.7851487442763334, 3.9182113455816596), + new(7.557346542357088, 6.08124030767064), + new(-8.769932429732474, 5.336877373512683), + new(-0.3013842458481659, 2.313560920085437), + new(6.946585621689808, 8.502927040297298), + new(-8.363128478889877, 9.156259453584862), + new(9.670660740183127, 4.956860871528423), + new(8.861238026483619, -0.9840687253038265), + new(10.426812822364909, -3.3278629852289914), + new(7.296132588971698, 4.341249905128432), + new(2.513188500291643, 5.861603466455874), + new(-1.048037780858094, 3.489347566529764), + new(7.821845003550148, -1.984137859946705), + new(-1.6324823820087966, 7.790777253754383), + new(8.970903795772337, 4.066014902164974), + new(-1.6707970781871395, 2.136015162752383), + new(7.2582324866205825, 2.765278876522548), + new(18.743970629080543, 8.896718444008545), + new(2.1660703208919028, -0.16068219834732655), + new(13.825097858675331, 8.427493951505237), + new(18.478328532221784, 12.667650849412713), + new(37.569125833815264, 21.578994508186412), + new(90.94566028906652, 50.697370827337764), + new(-191.47173027366662, -117.0833101137878), + new(-51.32832640613284, -23.124699639369688), + new(-23.500774862545306, -12.601610600261623), + new(-23.598271624183546, -16.220434411970665), + new(-21.623922884263536, -12.128429398236536), + new(-8.156016294103667, -2.079629832061165), + new(-3.3209342577998284, -4.826944007044762), + new(-8.966686390330285, 2.8174321266953246), + new(-4.036510916197741, -8.306905035682057), + new(-9.118052343426735, -0.7998264524711769), + new(1.705600696828284, 2.4801437158269257), + new(-0.4317759856237946, -2.0729669596807727), + new(1.3875511565884089, -2.3921170941765446), + new(-7.976980514611284, 4.704983072668934), + new(-1.0412939420119758, 2.0304346948254097), + new(-0.471229754781314, -1.8877908393403056), + new(-12.834400468053524, -1.9280109840030062), + new(-0.40207163490721864, -4.708004655448063), + new(-8.26743857115093, -3.7039764685576984), + new(-4.868431084537242, -5.189260677129303), + new(0.4631251388647035, 1.0836107137372712), + new(0.9899999999999949, -2.049999999999997), + new(-4.849319781162014, -0.2393308108802369), + new(-9.934189775670095, 0.7971585720723549), + new(-2.6000119101045227, -0.9923343205338122), + new(-2.1604691645146215, -5.322803319988891), + new(-8.342213173106131, -0.17252901564132417), + new(-4.19659820215117, -4.727468068212671), + new(-4.944944633762708, -1.4791201736168522), + new(-6.434479741808575, 6.482812912010042), + new(-5.1589371747063995, 3.7135634298435116), + new(-2.5917604439233606, -1.0744105534739985), + new(1.6430036734163007, -0.2684899307411228), + new(-4.686199327900066, 3.554091113872207), + new(-9.114112417604307, -5.863251584381021), + new(-1.5110102858383967, 0.5916676752843237), + new(-1.0248996347650015, -5.221535985301577), + new(-5.90777463267329, 3.6263871521782503), + new(-0.8134349081877801, -2.882711090582175), + new(-3.8883597256888844, 6.509652164781103), + new(3.6028695146052705, 4.9067549346501815), + new(-1.43392165399176, 0.0987739318549945), + new(2.539933710216701, -2.609184677284297), + new(-2.4029291340916075, 4.978313193479746), + new(-3.7152875707891617, -4.394518377168467), + new(-10.306371528157012, -2.7488670997481854), + new(-0.02431326109690435, -5.986905188653616), + new(-2.829838913006398, 3.4483686506720996), + new(4.734152099371297, -11.06251031428857), + new(-5.382929493701886, -2.783057603406332), + new(0.0026383233212212698, -0.18978654624226154), + new(-1.881282888464411, 4.871022697842906), + new(-3.25090055555276, 1.439383888634893), + new(-2.3369676800222425, 2.4825822015647154), + new(-1.2142355119041537, 0.18346355066723175), + new(-9.582245656802915, -0.02702092493529351), + new(-6.581883483527932, -0.9168370285871694), + new(2.2363390730997272, -2.3021508943840754), + new(-2.0748219655573523, -2.0037979487038737), + new(-3.7655639069782607, 3.0884327216299807), + new(5.332530718835811, -2.05160591585819), + new(-0.2549012255641827, -0.6932903665209045), + new(-7.277178212857729, 2.9116631246832334), + new(-2.19360099940124, 0.40016790514743894), + new(-8.794157662232948, 3.571713590620707), + new(9.191032309546067, -1.9398929261634494), + new(-3.869682067783092, 0.5152936097585563), + new(3.1473925580384554, 2.2163341563501353), + new(4.3376838965744415, 0.829975892233958), + new(5.8600818792670735, 0.029236104751530156), + new(-2.7839860346841467, 2.4914655265099475), + new(-1.9038424152586106, 1.4113411741607425), + new(-9.894054706023423, 4.966896090174032), + new(-0.8081382242155761, 11.349170741938874), + new(8.373197781633154, 2.163800298651763), + new(-2.4852724753585824, 4.218258048332261), + new(-3.755737925182478, -4.490350833996587), + new(-2.8792742576603123, 2.1626921190768047), + new(0.7024288556739773, -0.035259529528166134), + new(-2.673777491391459, 2.058724091865225), + new(2.194258127878678, -4.751521874754496), + new(3.8112923807165657, 3.594600695184486), + new(4.095532859213183, -0.28973422842766006), + new(-6.838255124818513, -9.629717041745101), + new(-1.9910301634396719, -10.188877845696904), + new(-1.4179036790187196, -3.8050609665441018), + new(-4.500591208775357, 2.5115489328726808), + new(2.5605067559698993, -1.0184596329131206), + new(6.717336341148265, -1.109888747244478), + new(-3.630872731671961, -2.9305090255634014), + new(-1.2800687813817038, -5.229326791882572), + new(-0.2417805427082249, -0.34674641143824525), + new(-3.582582190232076, -6.877312499750984), + new(-2.6028860714254742, -6.864072647844253), + new(-1.278589229093891, -1.639756766300434), + new(-8.67510061296496, -7.05596331070716), + new(8.849742475427906, -6.5632525472689105), + new(2.8850169831438945, -6.953620083792635), + new(6.918222156603155, 9.497082265446181), + new(4.104053099373963, -7.912067338215625), + new(-1.5840446271145168, -3.1949874308238315), + new(0.3670925294266435, -1.6673485244636947), + new(0.2946181403829611, -10.528089110189708), + new(10.194833330276262, -7.114694662442203), + new(17.62708674067874, -7.638274663697333), + new(22.6731707587984, -11.536058001405237), + new(90.79168081510915, -41.13328830004425), + new(-43.22953162601108, 23.29489090758906), + new(-17.523872467063395, 7.853673956180025), + new(-15.806202527275513, -0.5183772215827918), + new(-25.289765401997723, 13.089544474677407), + new(-7.587901316147816, 6.612185770225552), + new(-7.751030128000307, -4.450483522400161), + new(3.0154490825863447, 4.857182595958358), + new(-5.58800658946703, 1.8753775661904708), + new(-4.522700439505712, -6.9416534062786655), + new(-9.299907575120514, 7.73164297352557), + new(-15.054606473136637, -4.415621289881547), + new(-5.649212036777629, -3.004224743561953), + new(-5.324586611088913, -2.3232079416455873), + new(-6.095828936335264, -2.8336025030980143), + new(2.3719671688372443, 7.8232246351849986), + new(-4.378899544434765, -2.1004484536234376), + new(-1.8478042541202289, -1.2174634584260051), + new(-7.151969645971242, 5.608493469097139), + new(-6.962043610144821, 2.474104160752436), + new(-0.9411822402381418, 6.274216197339786), + new(0.5019000885290552, -4.787955462633121), + new(-1.159401955534861, -3.8417478262315257), + new(3.3001766896560056, 2.9594456628498946), + new(-10.06643681053692, 0.638410486067226), + new(-7.195678250403994, -3.226129608579562), + new(-4.691470830071456, 2.946476242241795), + new(-0.14727201097456977, -4.85928083812844), + new(-14.566922237012909, -7.900842560271224), + new(-4.3244464874689, 0.004185311431188055), + new(-1.040751494840329, 2.0596052964041256), + new(-2.9834069657351936, -3.1029075652331937), + new(-4.636760771943913, 10.373963334679232), + new(-8.269609575721237, 0.7354864925759204), + new(-4.9799511519732516, 2.478992013782473), + new(1.4400641578399807, 0.8869891013658959), + new(0.6818750261182345, 2.4700990392146815), + new(8.336841493956832, 7.404955214008821), + new(2.815743149224481, -6.567660517080393), + new(-5.87826054389363, 8.516509649574305), + new(2.8796780322869413, -3.1017131232821358), + new(-3.801558789466787, -2.7381256767431754), + new(-0.2774516078751166, -7.499382889529337), + new(11.019999999999996, 0.0), + }; public readonly double[] NumpyFFTabs = { diff --git a/src/FftSharp.Tests/VsNumpy.cs b/src/FftSharp.Tests/VsNumpy.cs index 2e12d0c..73c940c 100644 --- a/src/FftSharp.Tests/VsNumpy.cs +++ b/src/FftSharp.Tests/VsNumpy.cs @@ -25,8 +25,8 @@ public void LoadAndVerifyData() [Test] public void Test_VsNumpy_Fft() { - Complex[] fft = FftSharp.Transform.FFT(values); - Complex[] numpyFft = LoadData.Complex("fft.txt"); + System.Numerics.Complex[] fft = FftSharp.FFT.Forward(values); + System.Numerics.Complex[] numpyFft = LoadData.Complex("fft.txt"); Assert.AreEqual(numpyFft.Length, fft.Length); @@ -44,9 +44,9 @@ public void Test_VsNumpy_Fft_SystemNumericsComplex() for (int i = 0; i < values.Length; i++) buffer[i] = new System.Numerics.Complex(real: values[i], imaginary: 0); - FftSharp.Transform.FFT(buffer); + FftSharp.FFT.Forward(buffer); - Complex[] numpyFft = LoadData.Complex("fft.txt"); + System.Numerics.Complex[] numpyFft = LoadData.Complex("fft.txt"); Assert.AreEqual(numpyFft.Length, buffer.Length); @@ -60,8 +60,8 @@ public void Test_VsNumpy_Fft_SystemNumericsComplex() [Test] public void Test_VsNumpy_Rfft() { - Complex[] rfft = FftSharp.Transform.RFFT(values); - Complex[] numpyRfft = LoadData.Complex("fftReal.txt"); + System.Numerics.Complex[] rfft = FftSharp.FFT.ForwardReal(values); + System.Numerics.Complex[] numpyRfft = LoadData.Complex("fftReal.txt"); Assert.AreEqual(numpyRfft.Length, rfft.Length); @@ -75,7 +75,8 @@ public void Test_VsNumpy_Rfft() [Test] public void Test_VsNumpy_FftMag() { - double[] fftMag = FftSharp.Transform.FFTmagnitude(values); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(values); + double[] fftMag = FftSharp.FFT.Magnitude(spectrum); double[] numpyFftMag = LoadData.Double("fftMag.txt"); Assert.AreEqual(numpyFftMag.Length, fftMag.Length); @@ -87,7 +88,8 @@ public void Test_VsNumpy_FftMag() [Test] public void Test_VsNumpy_FftDB() { - double[] fftDB = FftSharp.Transform.FFTpower(values); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(values); + double[] fftDB = FftSharp.FFT.Power(spectrum); double[] numpyFftDB = LoadData.Double("fftDB.txt"); Assert.AreEqual(numpyFftDB.Length, fftDB.Length); @@ -99,7 +101,8 @@ public void Test_VsNumpy_FftDB() [Test] public void Test_VsNumpy_FftPhase() { - double[] phases = FftSharp.Transform.FFTphase(values); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(values); + double[] phases = FftSharp.FFT.Phase(spectrum); double[] numpyFftPhase = LoadData.Double("fftPhase.txt"); Assert.AreEqual(numpyFftPhase.Length, phases.Length); @@ -111,7 +114,7 @@ public void Test_VsNumpy_FftPhase() [Test] public void Test_VsNumpy_FftFreq() { - double[] fftFreq = FftSharp.Transform.FFTfreq(sampleRate: 48_000, pointCount: values.Length, oneSided: false); + double[] fftFreq = FftSharp.FFT.FrequencyScale(values.Length, 48_000, false); double[] numpyFftFreq = LoadData.Double("fftFreq.txt"); Assert.AreEqual(numpyFftFreq.Length, fftFreq.Length); @@ -134,7 +137,8 @@ public void Test_VsNumpy_Fft_Length32() 0.12648992889321545, 0.17946592505602227, 0.08145444884938172, 0.1681978420275756, 0.2919426435801102, 0.35936842267425245, 0.2704107530566828, 0.05312499999999987 }; - double[] mag = FftSharp.Transform.FFTmagnitude(values); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(values); + double[] mag = FftSharp.FFT.Magnitude(spectrum); Assert.AreEqual(numpyFftMag.Length, mag.Length); for (int i = 0; i < numpyFftMag.Length; i++) @@ -153,7 +157,8 @@ public void Test_VsNumpy_Fft_Length16() double[] numpyFftMag = { 0.99, 1.3848691625350331, 0.3643928332909465, 1.0042721499156904, 0.34613039450473, 0.1401344145671993, 0.2637216582804615, 0.5006617594915167, 0.04499999999999993 }; - double[] mag = FftSharp.Transform.FFTmagnitude(values); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(values); + double[] mag = FftSharp.FFT.Magnitude(spectrum); Assert.AreEqual(numpyFftMag.Length, mag.Length); for (int i = 0; i < numpyFftMag.Length; i++) @@ -171,7 +176,8 @@ public void Test_VsNumpy_Fft_Length8() double[] numpyFftMag = { 1.7025, 0.706710339966861, 1.1495760087962865, 0.33016737480242325, 0.645 }; - double[] mag = FftSharp.Transform.FFTmagnitude(values); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(values); + double[] mag = FftSharp.FFT.Magnitude(spectrum); Assert.AreEqual(numpyFftMag.Length, mag.Length); for (int i = 0; i < numpyFftMag.Length; i++) @@ -189,7 +195,8 @@ public void Test_VsNumpy_Fft_Length4() double[] numpyFftMag = { 1.3225, 0.6783251432757007, 0.875 }; - double[] mag = FftSharp.Transform.FFTmagnitude(values); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(values); + double[] mag = FftSharp.FFT.Magnitude(spectrum); Assert.AreEqual(numpyFftMag.Length, mag.Length); for (int i = 0; i < numpyFftMag.Length; i++) @@ -207,7 +214,8 @@ public void Test_VsNumpy_Fft_Length2() double[] numpyFftMag = { 1.24, 1.8199999999999998 }; - double[] mag = FftSharp.Transform.FFTmagnitude(values); + System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(values); + double[] mag = FftSharp.FFT.Magnitude(spectrum); Assert.AreEqual(numpyFftMag.Length, mag.Length); for (int i = 0; i < numpyFftMag.Length; i++) @@ -220,14 +228,14 @@ public void Test_VsNumpy_Fft_Length2() public void Test_VsNumpy_Fft_Length1() { double[] values = { 0.33 }; - Assert.Throws(() => { FftSharp.Transform.FFTmagnitude(values); }); + Assert.Throws(() => { FftSharp.FFT.Forward(values); }); } [Test] public void Test_VsNumpy_Fft_Length0() { double[] values = { }; - Assert.Throws(() => { FftSharp.Transform.FFTmagnitude(values); }); + Assert.Throws(() => { FftSharp.FFT.Forward(values); }); } } } diff --git a/src/FftSharp/Complex.cs b/src/FftSharp/Complex.cs index a92d09c..a5daac8 100644 --- a/src/FftSharp/Complex.cs +++ b/src/FftSharp/Complex.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Text; namespace FftSharp { + [Obsolete("Use System.Numerics.Complex")] public struct Complex { public double Real; @@ -63,5 +65,15 @@ public static double[] GetMagnitudes(Complex[] input) output[i] = input[i].Magnitude; return output; } + + public System.Numerics.Complex ToNumerics() + { + return new(Real, Imaginary); + } + + public static System.Numerics.Complex[] ToNumerics(Complex[] values) + { + return values.Select(x => x.ToNumerics()).ToArray(); + } } } diff --git a/src/FftSharp/FFT.cs b/src/FftSharp/FFT.cs index a2b68a9..42f2755 100644 --- a/src/FftSharp/FFT.cs +++ b/src/FftSharp/FFT.cs @@ -2,49 +2,99 @@ namespace FftSharp; -// TODO: support spans - /// /// Fast Fourier Transform (FFT) operations using System.Numerics.Complex data types. /// public static class FFT { /// - /// Apply the Fast Fourier Transform (FFT) to the Complex array in place. - /// Length of the array must be a power of 2. + /// Compute the discrete Fourier Transform (in-place) using the FFT algorithm. /// + /// Data to transform in-place. Length must be a power of 2. public static void Forward(System.Numerics.Complex[] samples) { - if (!Transform.IsPowerOfTwo(samples.Length)) + if (samples is null) + throw new ArgumentNullException(nameof(samples)); + + Forward(samples.AsSpan()); + } + + /// + /// Compute the discrete Fourier Transform (in-place) using the FFT algorithm. + /// + /// Data to transform in-place. Length must be a power of 2. + public static System.Numerics.Complex[] Forward(double[] samples) + { + if (samples is null) + throw new ArgumentNullException(nameof(samples)); + + System.Numerics.Complex[] buffer = samples.ToComplexArray(); + Forward(buffer); + return buffer; + } + + /// + /// Compute the discrete Fourier Transform (in-place) using the FFT algorithm. + /// + /// Data to transform in-place. Length must be a power of 2. + public static void Forward(Span samples) + { + if (!FftOperations.IsPowerOfTwo(samples.Length)) throw new ArgumentException($"{nameof(samples)} length must be a power of 2"); + if (samples.Length < 2) + throw new ArgumentException($"FFT requires at least 2 samples but sample length was {samples.Length}"); + FftOperations.FFT_WithoutChecks(samples); } /// - /// Create a Complex array from the given data, apply the FFT, and return the result. + /// Calculate FFT and return just the real component of the spectrum + /// + public static System.Numerics.Complex[] ForwardReal(System.Numerics.Complex[] samples) + { + if (!FftOperations.IsPowerOfTwo(samples.Length)) + throw new ArgumentException($"{nameof(samples)} length must be a power of 2"); + + System.Numerics.Complex[] realBuffer = new System.Numerics.Complex[samples.Length / 2 + 1]; + FftOperations.RFFT_WithoutChecks(realBuffer, samples); + return realBuffer; + } + + /// + /// Calculate FFT and return just the real component of the spectrum + /// + public static System.Numerics.Complex[] ForwardReal(double[] samples) + { + System.Numerics.Complex[] buffer = samples.ToComplexArray(); + Forward(buffer); + System.Numerics.Complex[] real = new System.Numerics.Complex[samples.Length / 2 + 1]; + Array.Copy(buffer, 0, real, 0, real.Length); + return real; + } + + /// + /// Apply the inverse Fast Fourier Transform (iFFT) to the Complex array in place. /// Length of the array must be a power of 2. /// - public static System.Numerics.Complex[] Forward(double[] real) + public static void Inverse(System.Numerics.Complex[] spectrum) { - if (!Transform.IsPowerOfTwo(real.Length)) - throw new ArgumentException($"{nameof(real)} length must be a power of 2"); + if (spectrum is null) + throw new ArgumentNullException(nameof(spectrum)); - System.Numerics.Complex[] samples = real.ToComplexArray(); - FftOperations.FFT_WithoutChecks(samples); - return samples; + Inverse(spectrum.AsSpan()); } /// /// Apply the inverse Fast Fourier Transform (iFFT) to the Complex array in place. /// Length of the array must be a power of 2. /// - public static void Inverse(System.Numerics.Complex[] samples) + public static void Inverse(Span spectrum) { - if (!Transform.IsPowerOfTwo(samples.Length)) - throw new ArgumentException($"{nameof(samples)} length must be a power of 2"); + if (!FftOperations.IsPowerOfTwo(spectrum.Length)) + throw new ArgumentException($"{nameof(spectrum)} length must be a power of 2"); - FftOperations.IFFT_WithoutChecks(samples); + FftOperations.IFFT_WithoutChecks(spectrum); } /// @@ -52,6 +102,87 @@ public static void Inverse(System.Numerics.Complex[] samples) /// public static double[] FrequencyScale(int length, double sampleRate, bool positiveOnly = true) { - return Transform.FFTfreq(sampleRate: sampleRate, pointCount: length, oneSided: positiveOnly); + double[] freqs = new double[length]; + + if (positiveOnly) + { + double fftPeriodHz = sampleRate / (length - 1) / 2; + + // freqs start at 0 and approach maxFreq + for (int i = 0; i < length; i++) + freqs[i] = i * fftPeriodHz; + + return freqs; + } + else + { + double fftPeriodHz = sampleRate / length; + + // first half: freqs start a 0 and approach maxFreq + int halfIndex = length / 2; + for (int i = 0; i < halfIndex; i++) + freqs[i] = i * fftPeriodHz; + + // second half: then start at -maxFreq and approach 0 + for (int i = halfIndex; i < length; i++) + freqs[i] = -(length - i) * fftPeriodHz; + + return freqs; + } + } + + /// + /// Return the resolution (distance between each frequency) of the FFT in Hz + /// + public static double FrequencyResolution(int length, double sampleRate, bool positiveOnly = true) + { + return positiveOnly + ? sampleRate / (length - 1) / 2 + : sampleRate / length; + } + + /// + /// Return the phase for each point in a Complex array + /// + public static double[] Phase(System.Numerics.Complex[] spectrum) + { + double[] phase = new double[spectrum.Length]; + + for (int i = 0; i < spectrum.Length; i++) + phase[i] = spectrum[i].Phase; + + return phase; + } + + /// + /// Calculate power spectrum density (PSD) in RMS units + /// + public static double[] Magnitude(System.Numerics.Complex[] spectrum, bool positiveOnly = true) + { + int length = positiveOnly ? spectrum.Length / 2 + 1 : spectrum.Length; + + double[] output = new double[length]; + + // first point (DC component) is not doubled + output[0] = spectrum[0].Magnitude / spectrum.Length; + + // subsequent points are doubled to account for combined positive and negative frequencies + for (int i = 1; i < output.Length; i++) + output[i] = 2 * spectrum[i].Magnitude / spectrum.Length; + + return output; + } + + /// + /// Calculate power spectrum density (PSD) in dB units + /// + public static double[] Power(System.Numerics.Complex[] spectrum, bool positiveOnly = true) + { + double[] output = Magnitude(spectrum, positiveOnly); + + for (int i = 0; i < output.Length; i++) + output[i] = 20 * Math.Log10(output[i]); + + return output; } } \ No newline at end of file diff --git a/src/FftSharp/FftOperations.cs b/src/FftSharp/FftOperations.cs index 517f6d2..3c5ee1c 100644 --- a/src/FftSharp/FftOperations.cs +++ b/src/FftSharp/FftOperations.cs @@ -1,4 +1,5 @@ using System; +using System.Buffers; namespace FftSharp; @@ -7,12 +8,21 @@ namespace FftSharp; /// internal class FftOperations { + /// + /// Returns true if the given number is evenly divisible by a power of 2 + /// + public static bool IsPowerOfTwo(int x) + { + return ((x & (x - 1)) == 0) && (x > 0); + } + /// /// High performance FFT function. /// Complex input will be transformed in place. /// Input array length must be a power of two. This length is NOT validated. /// Running on an array with an invalid length may throw an invalid index exception. /// + [Obsolete("Use methods which consume System.Numerics.Complex")] public static void FFT_WithoutChecks(Span buffer) { for (int i = 1; i < buffer.Length; i++) @@ -73,24 +83,64 @@ public static void FFT_WithoutChecks(Span buffer) } } + /// + /// Calculate FFT of the input and return just the real component + /// Input array length must be a power of two. This length is NOT validated. + /// Running on an array with an invalid length may throw an invalid index exception. + /// + public static void RFFT_WithoutChecks(Span destination, Span input) + { + System.Numerics.Complex[] temp = ArrayPool.Shared.Rent(input.Length); + + Span buffer = temp.AsSpan(0, input.Length); + + FFT_WithoutChecks(buffer); + buffer.Slice(0, destination.Length).CopyTo(destination); + + ArrayPool.Shared.Return(temp); + } + /// /// High performance inverse FFT function. /// Complex input will be transformed in place. /// Input array length must be a power of two. This length is NOT validated. /// Running on an array with an invalid length may throw an invalid index exception. /// - public static void IFFT_WithoutChecks(Complex[] buffer) + [Obsolete("Use methods which consume System.Numerics.Complex")] + public static void IFFT_WithoutChecks(Span buffer) { // invert the imaginary component for (int i = 0; i < buffer.Length; i++) - buffer[i] = new Complex(buffer[i].Real, -buffer[i].Imaginary); + buffer[i] = new(buffer[i].Real, -buffer[i].Imaginary); // perform a forward Fourier transform FFT_WithoutChecks(buffer); // invert the imaginary component again and scale the output for (int i = 0; i < buffer.Length; i++) - buffer[i] = new Complex( + buffer[i] = new( + real: buffer[i].Real / buffer.Length, + imaginary: -buffer[i].Imaginary / buffer.Length); + } + + /// + /// High performance inverse FFT function. + /// Complex input will be transformed in place. + /// Input array length must be a power of two. This length is NOT validated. + /// Running on an array with an invalid length may throw an invalid index exception. + /// + public static void IFFT_WithoutChecks(Span buffer) + { + // invert the imaginary component + for (int i = 0; i < buffer.Length; i++) + buffer[i] = new(buffer[i].Real, -buffer[i].Imaginary); + + // perform a forward Fourier transform + FFT_WithoutChecks(buffer); + + // invert the imaginary component again and scale the output + for (int i = 0; i < buffer.Length; i++) + buffer[i] = new( real: buffer[i].Real / buffer.Length, imaginary: -buffer[i].Imaginary / buffer.Length); } diff --git a/src/FftSharp/Filter.cs b/src/FftSharp/Filter.cs index 13faec0..442ebda 100644 --- a/src/FftSharp/Filter.cs +++ b/src/FftSharp/Filter.cs @@ -21,15 +21,14 @@ public static class Filter /// public static double[] BandPass(double[] values, double sampleRate, double minFrequency, double maxFrequency) { - Complex[] fft = Transform.FFT(values); - double[] fftFreqs = Transform.FFTfreq(sampleRate, fft.Length, oneSided: false); + System.Numerics.Complex[] fft = FFT.Forward(values); + double[] fftFreqs = FFT.FrequencyScale(fft.Length, sampleRate, false); for (int i = 0; i < fft.Length; i++) { double freq = Math.Abs(fftFreqs[i]); if ((freq > maxFrequency) || (freq < minFrequency)) { - fft[i].Real = 0; - fft[i].Imaginary = 0; + fft[i] = new(0, 0); } } return InverseReal(fft); @@ -40,23 +39,22 @@ public static double[] BandPass(double[] values, double sampleRate, double minFr /// public static double[] BandStop(double[] values, double sampleRate, double minFrequency, double maxFrequency) { - Complex[] fft = Transform.FFT(values); - double[] fftFreqs = Transform.FFTfreq(sampleRate, fft.Length, oneSided: false); + System.Numerics.Complex[] fft = FFT.Forward(values); + double[] fftFreqs = FFT.FrequencyScale(fft.Length, sampleRate, false); for (int i = 0; i < fft.Length; i++) { double freq = Math.Abs(fftFreqs[i]); if ((freq <= maxFrequency) && (freq >= minFrequency)) { - fft[i].Real = 0; - fft[i].Imaginary = 0; + fft[i] = new(0, 0); } } return InverseReal(fft); } - private static double[] InverseReal(Complex[] fft) + private static double[] InverseReal(System.Numerics.Complex[] fft) { - Transform.IFFT(fft); + FFT.Inverse(fft); double[] Filtered = new double[fft.Length]; for (int i = 0; i < fft.Length; i++) Filtered[i] = fft[i].Real; diff --git a/src/FftSharp/Mel.cs b/src/FftSharp/Mel.cs new file mode 100644 index 0000000..bc39797 --- /dev/null +++ b/src/FftSharp/Mel.cs @@ -0,0 +1,46 @@ +using System; + +namespace FftSharp; + +/// +/// Methods for conversion to/from Mel scaling +/// +public static class Mel +{ + public static double ToFreq(double mel) => 700 * (Math.Pow(10, mel / 2595d) - 1); + + public static double FromFreq(double frequencyHz) => 2595 * Math.Log10(1 + frequencyHz / 700); + + public static double[] Scale(double[] fft, int sampleRate, int melBinCount) + { + double freqMax = sampleRate / 2; + double maxMel = FromFreq(freqMax); + + double[] fftMel = new double[melBinCount]; + double melPerBin = maxMel / (melBinCount + 1); + for (int binIndex = 0; binIndex < melBinCount; binIndex++) + { + double melLow = melPerBin * binIndex; + double melHigh = melPerBin * (binIndex + 2); + + double freqLow = ToFreq(melLow); + double freqHigh = ToFreq(melHigh); + + int indexLow = (int)(fft.Length * freqLow / freqMax); + int indexHigh = (int)(fft.Length * freqHigh / freqMax); + int indexSpan = indexHigh - indexLow; + + double binScaleSum = 0; + for (int i = 0; i < indexSpan; i++) + { + double binFrac = (double)i / indexSpan; + double indexScale = (binFrac < .5) ? binFrac * 2 : 1 - binFrac; + binScaleSum += indexScale; + fftMel[binIndex] += fft[indexLow + i] * indexScale; + } + fftMel[binIndex] /= binScaleSum; + } + + return fftMel; + } +} diff --git a/src/FftSharp/Pad.cs b/src/FftSharp/Pad.cs index 643a802..29b75c8 100644 --- a/src/FftSharp/Pad.cs +++ b/src/FftSharp/Pad.cs @@ -16,6 +16,7 @@ public static class Pad /// /// array of any length /// the input array or a zero-padded copy + [Obsolete("Use methods which consume System.Numerics.Complex")] public static Complex[] ZeroPad(Complex[] input) { if (IsPowerOfTwo(input.Length)) @@ -32,6 +33,27 @@ public static Complex[] ZeroPad(Complex[] input) return padded; } + /// + /// Return the input array (or a new zero-padded new one) ensuring length is a power of 2 + /// + /// array of any length + /// the input array or a zero-padded copy + public static System.Numerics.Complex[] ZeroPad(System.Numerics.Complex[] input) + { + if (IsPowerOfTwo(input.Length)) + return input; + + int targetLength = 1; + while (targetLength < input.Length) + targetLength *= 2; + + int difference = targetLength - input.Length; + System.Numerics.Complex[] padded = new System.Numerics.Complex[targetLength]; + Array.Copy(input, 0, padded, difference / 2, input.Length); + + return padded; + } + /// /// Return the input array (or a new zero-padded new one) ensuring length is a power of 2 /// @@ -59,6 +81,7 @@ public static double[] ZeroPad(double[] input) /// array of any length /// pad the array with zeros a the end to achieve this final length /// a zero-padded copy of the input array + [Obsolete("Use methods which consume System.Numerics.Complex")] public static Complex[] ZeroPad(Complex[] input, int finalLength) { int difference = finalLength - input.Length; diff --git a/src/FftSharp/Transform.cs b/src/FftSharp/Transform.cs index 5624a2a..5b536d1 100644 --- a/src/FftSharp/Transform.cs +++ b/src/FftSharp/Transform.cs @@ -1,17 +1,17 @@ using System; using System.Buffers; +using System.Linq; namespace FftSharp; -/// -/// FFT operations and helper methods -/// +//[Obsolete("Use methods in the FftSharp.FFT class")] public static class Transform { /// /// Compute the discrete Fourier Transform (in-place) using the FFT algorithm. /// /// Data to transform in-place. Length must be a power of 2. + [Obsolete("Use FftSharp.FFT.Forward()")] public static void FFT(Complex[] buffer) { if (buffer is null) @@ -24,18 +24,7 @@ public static void FFT(Complex[] buffer) /// Compute the discrete Fourier Transform (in-place) using the FFT algorithm. /// /// Data to transform in-place. Length must be a power of 2. - public static void FFT(System.Numerics.Complex[] buffer) - { - if (buffer is null) - throw new ArgumentNullException(nameof(buffer)); - - FFT(buffer.AsSpan()); - } - - /// - /// Compute the discrete Fourier Transform (in-place) using the FFT algorithm. - /// - /// Data to transform in-place. Length must be a power of 2. + [Obsolete("Use FftSharp.FFT.Forward()")] public static void FFT(Span buffer) { if (buffer.Length == 0) @@ -47,85 +36,43 @@ public static void FFT(Span buffer) FftOperations.FFT_WithoutChecks(buffer); } - /// - /// Compute the discrete Fourier Transform (in-place) using the FFT algorithm. - /// - /// Data to transform in-place. Length must be a power of 2. - public static void FFT(Span buffer) - { - if (buffer.Length == 0) - throw new ArgumentException("Buffer must not be empty"); - - if (!IsPowerOfTwo(buffer.Length)) - throw new ArgumentException("Buffer length must be a power of 2"); - - FftOperations.FFT_WithoutChecks(buffer); - } - /// /// Calculate sample frequency for each point in a FFT /// /// Sample rate (Hz) of the original signal /// Number of points to generate (typically the length of the FFT) /// Whether or not frequencies are for a one-sided FFT (containing only real numbers) + [Obsolete("Use FftSharp.FFT.FrequencyScale()")] public static double[] FFTfreq(double sampleRate, int pointCount, bool oneSided = true) { - double[] freqs = new double[pointCount]; - - if (oneSided) - { - double fftPeriodHz = sampleRate / (pointCount - 1) / 2; - - // freqs start at 0 and approach maxFreq - for (int i = 0; i < pointCount; i++) - freqs[i] = i * fftPeriodHz; - return freqs; - } - else - { - double fftPeriodHz = sampleRate / pointCount; - - // first half: freqs start a 0 and approach maxFreq - int halfIndex = pointCount / 2; - for (int i = 0; i < halfIndex; i++) - freqs[i] = i * fftPeriodHz; - - // second half: then start at -maxFreq and approach 0 - for (int i = halfIndex; i < pointCount; i++) - freqs[i] = -(pointCount - i) * fftPeriodHz; - return freqs; - } + return FftSharp.FFT.FrequencyScale(pointCount, sampleRate, oneSided); } /// - /// Return the phase for each point in a Complex array + /// Calculate sample frequency for each point in a FFT /// - public static double[] FFTphase(Complex[] values) + /// Sample rate (Hz) of the original signal + /// FFT array for which frequencies should be generated + /// Whether or not frequencies are for a one-sided FFT (containing only real numbers) + [Obsolete("Use FftSharp.FFT.FrequencyScale()")] + public static double[] FFTfreq(double sampleRate, double[] fft, bool oneSided = true) { - double[] phase = new double[values.Length]; - - for (int i = 0; i < values.Length; i++) - phase[i] = values[i].Phase; - - return phase; + return FFTfreq(sampleRate, fft.Length, oneSided); } /// /// Return the phase for each point in a Complex array /// - public static double[] FFTphase(System.Numerics.Complex[] values) + [Obsolete("Use FftSharp.FFT.Phase()")] + public static double[] FFTphase(Complex[] values) { - double[] phase = new double[values.Length]; - - for (int i = 0; i < values.Length; i++) - phase[i] = values[i].Phase; - - return phase; + return FftSharp.FFT.Phase(Complex.ToNumerics(values)); } /// /// Take the FFT of the given array and return its phase /// + [Obsolete("Use FftSharp.FFT.Phase()")] public static double[] FFTphase(double[] values) { Complex[] fft = FFT(values); @@ -133,20 +80,10 @@ public static double[] FFTphase(double[] values) return phase; } - /// - /// Calculate sample frequency for each point in a FFT - /// - /// Sample rate (Hz) of the original signal - /// FFT array for which frequencies should be generated - /// Whether or not frequencies are for a one-sided FFT (containing only real numbers) - public static double[] FFTfreq(double sampleRate, double[] fft, bool oneSided = true) - { - return FFTfreq(sampleRate, fft.Length, oneSided); - } - /// /// Return the distance between each FFT point in frequency units (Hz) /// + [Obsolete("Use FftSharp.FFT.FrequencyResolution()")] public static double FFTfreqPeriod(int sampleRate, int pointCount) { return .5 * sampleRate / pointCount; @@ -155,14 +92,13 @@ public static double FFTfreqPeriod(int sampleRate, int pointCount) /// /// Returns true if the given number is evenly divisible by a power of 2 /// - public static bool IsPowerOfTwo(int x) - { - return ((x & (x - 1)) == 0) && (x > 0); - } + [Obsolete("Use FftSharp.FftOperations.IsPowerOfTwo()")] + public static bool IsPowerOfTwo(int x) => FftOperations.IsPowerOfTwo(x); /// /// Create an array of Complex data given the real component /// + [Obsolete("Use System.Numerics.Complex data structures instead")] public static Complex[] MakeComplex(double[] real) { Complex[] com = new Complex[real.Length]; @@ -173,6 +109,7 @@ public static Complex[] MakeComplex(double[] real) /// /// Create an array of Complex data given the real component /// + [Obsolete("Use System.Numerics.Complex data structures instead")] public static void MakeComplex(Span com, Span real) { if (com.Length != real.Length) @@ -182,23 +119,12 @@ public static void MakeComplex(Span com, Span real) com[i] = new Complex(real[i], 0); } - /// - /// Create an array of Complex data given the real component - /// - internal static void MakeComplexNumeric(Span com, Span real) - { - if (com.Length != real.Length) - throw new ArgumentOutOfRangeException("Input length must match"); - - for (int i = 0; i < real.Length; i++) - com[i] = new System.Numerics.Complex(real[i], 0); - } - /// /// Compute the 1D discrete Fourier Transform using the Fast Fourier Transform (FFT) algorithm /// /// real input (must be an array with length that is a power of 2) /// transformed input + [Obsolete("Use FftSharp.FFT.Forward()")] public static Complex[] FFT(double[] input) { if (input is null) @@ -220,6 +146,7 @@ public static Complex[] FFT(double[] input) /// /// real input (must be an array with length that is a power of 2) /// real component of transformed input + [Obsolete("Use FftSharp.FFT.ForwardReal()")] public static Complex[] RFFT(double[] input) { if (input is null) @@ -242,6 +169,7 @@ public static Complex[] RFFT(double[] input) /// Memory location of the results (must be an equal to input length / 2 + 1) /// real input (must be an array with length that is a power of 2) /// real component of transformed input + [Obsolete("Use FftSharp.FFT.ForwardReal()")] public static void RFFT(Span destination, Span input) { if (!IsPowerOfTwo(input.Length)) @@ -273,6 +201,7 @@ public static void RFFT(Span destination, Span input) /// Compute the inverse discrete Fourier Transform (in-place) using the FFT algorithm. /// /// Data to transform in-place. Length must be a power of 2. + [Obsolete("Use FftSharp.FFT.Inverse()")] public static void IFFT(Complex[] buffer) { if (buffer is null) @@ -292,6 +221,7 @@ public static void IFFT(Complex[] buffer) /// /// /// + [Obsolete("Use FftSharp.FFT.Magnitude()")] public static double[] Absolute(Complex[] input) { double[] output = new double[input.Length]; @@ -304,6 +234,7 @@ public static double[] Absolute(Complex[] input) /// Calculate power spectrum density (PSD) original (RMS) units /// /// real input + [Obsolete("Use FftSharp.FFT.Forward() then FftSharp.FFT.Magnitude()")] public static double[] FFTmagnitude(double[] input) { double[] output = new double[input.Length / 2 + 1]; @@ -316,6 +247,7 @@ public static double[] FFTmagnitude(double[] input) /// /// Memory location of the results. /// real input + [Obsolete("Use FftSharp.FFT.Magnitude()")] public static void FFTmagnitude(Span destination, Span input) { if (input.Length < 2) @@ -353,6 +285,7 @@ public static void FFTmagnitude(Span destination, Span input) /// Calculate power spectrum density (PSD) in dB units /// /// real input + [Obsolete("Use FftSharp.FFT.Forward() then FftSharp.FFT.Power()")] public static double[] FFTpower(double[] input) { if (!IsPowerOfTwo(input.Length)) @@ -369,6 +302,7 @@ public static double[] FFTpower(double[] input) /// /// Memory location of the results. /// real input + [Obsolete("Use FftSharp.FFT.Power()")] public static void FFTpower(Span destination, double[] input) { if (!IsPowerOfTwo(input.Length)) @@ -379,46 +313,27 @@ public static void FFTpower(Span destination, double[] input) destination[i] = 2 * 10 * Math.Log10(destination[i]); } + /// + /// Convert a Mel bin to frequency (Hz) + /// + [Obsolete("use FftSharp.Mel.ToFreq()")] public static double MelToFreq(double mel) { - return 700 * (Math.Pow(10, mel / 2595d) - 1); + return Mel.ToFreq(mel); } + /// + /// Convert a frequency (Hz) to a Mel bin + /// + [Obsolete("use FftSharp.Mel.FromFreq()")] public static double MelFromFreq(double frequencyHz) { - return 2595 * Math.Log10(1 + frequencyHz / 700); + return Mel.FromFreq(frequencyHz); } + [Obsolete("use FftSharp.Mel.FrequencyScale()")] public static double[] MelScale(double[] fft, int sampleRate, int melBinCount) { - double freqMax = sampleRate / 2; - double maxMel = MelFromFreq(freqMax); - - double[] fftMel = new double[melBinCount]; - double melPerBin = maxMel / (melBinCount + 1); - for (int binIndex = 0; binIndex < melBinCount; binIndex++) - { - double melLow = melPerBin * binIndex; - double melHigh = melPerBin * (binIndex + 2); - - double freqLow = MelToFreq(melLow); - double freqHigh = MelToFreq(melHigh); - - int indexLow = (int)(fft.Length * freqLow / freqMax); - int indexHigh = (int)(fft.Length * freqHigh / freqMax); - int indexSpan = indexHigh - indexLow; - - double binScaleSum = 0; - for (int i = 0; i < indexSpan; i++) - { - double binFrac = (double)i / indexSpan; - double indexScale = (binFrac < .5) ? binFrac * 2 : 1 - binFrac; - binScaleSum += indexScale; - fftMel[binIndex] += fft[indexLow + i] * indexScale; - } - fftMel[binIndex] /= binScaleSum; - } - - return fftMel; + return Mel.Scale(fft, sampleRate, melBinCount); } } From 96862a4fc8533bbf7e1b1c41e46b83f41c1018da Mon Sep 17 00:00:00 2001 From: Scott W Harden Date: Tue, 16 May 2023 22:48:18 -0400 Subject: [PATCH 2/5] Update README.md --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index fe3f455..65ddc80 100644 --- a/README.md +++ b/README.md @@ -15,11 +15,11 @@ var window = new FftSharp.Windows.Hanning(); window.ApplyInPlace(signal); // Calculate the FFT as an array of complex numbers -Complex[] fftRaw = FftSharp.Transform.FFT(signal); +System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(signal); // or get the magnitude (units²) or power (dB) as real numbers -double[] fftMag = FftSharp.Transform.FFTmagnitude(signal); -double[] fftPwr = FftSharp.Transform.FFTpower(signal); +double[] magnitude = FftSharp.FFT.Magnitude(signal); +double[] power = FftSharp.FFT.Power(signal); ``` Signal | Windowed Signal | FFT @@ -32,12 +32,12 @@ Signal | Windowed Signal | FFT // sample audio with tones at 2, 10, and 20 kHz plus white noise double[] signal = FftSharp.SampleData.SampleAudio1(); int sampleRate = 48_000; +double samplePeriod = sampleRate / 1000.0; // plot the sample audio -var plt = new ScottPlot.Plot(400, 200); -plt.AddSignal(signal, sampleRate / 1000.0); +ScottPlot.Plot plt = new(); +plt.AddSignal(signal, samplePeriod); plt.YLabel("Amplitude"); -plt.Margins(0); plt.SaveFig("time-series.png"); ``` @@ -59,15 +59,15 @@ double[] signal = FftSharp.SampleData.SampleAudio1(); int sampleRate = 48_000; // calculate the power spectral density using FFT -double[] psd = FftSharp.Transform.FFTpower(signal); -double[] freq = FftSharp.Transform.FFTfreq(sampleRate, psd.Length); +System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(audio); +double[] psd = FftSharp.FFT.Power(signal); +double[] freq = FftSharp.FFT.FrequencyScale(psd.Length, sampleRate); // plot the sample audio -var plt = new ScottPlot.Plot(400, 200); +ScottPlot.Plot plt = new ScottPlot.Plot(); plt.AddScatterLines(freq, psd); plt.YLabel("Power (dB)"); plt.XLabel("Frequency (Hz)"); -plt.Margins(0); plt.SaveFig("periodogram.png"); ``` @@ -82,12 +82,12 @@ plt.SaveFig("periodogram.png"); If you are writing a performance application or just enjoy working with real and imaginary components of complex numbers, you can build your own complex array perform FFT operations on it in place: ```cs -Complex[] buffer = +System.Numerics.Complex[] buffer = { - new Complex(42, 0), - new Complex(96, 0), - new Complex(13, 0), - new Complex(99, 0), + new(real: 42, imaginary: 12), + new(real: 96, imaginary: 34), + new(real: 13, imaginary: 56), + new(real: 99, imaginary: 78), }; FftSharp.Transform.FFT(buffer); From e8741f56748dc1ac99c435ffdd985dc1fc77551d Mon Sep 17 00:00:00 2001 From: Scott W Harden Date: Tue, 16 May 2023 22:52:23 -0400 Subject: [PATCH 3/5] Pad: use FftOperations.IsPowerOfTwo() --- src/FftSharp/Pad.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/FftSharp/Pad.cs b/src/FftSharp/Pad.cs index 29b75c8..e98912d 100644 --- a/src/FftSharp/Pad.cs +++ b/src/FftSharp/Pad.cs @@ -6,11 +6,6 @@ namespace FftSharp { public static class Pad { - /// - /// Test if a number is an even power of 2 - /// - public static bool IsPowerOfTwo(int x) => ((x & (x - 1)) == 0) && (x > 0); - /// /// Return the input array (or a new zero-padded new one) ensuring length is a power of 2 /// @@ -19,7 +14,7 @@ public static class Pad [Obsolete("Use methods which consume System.Numerics.Complex")] public static Complex[] ZeroPad(Complex[] input) { - if (IsPowerOfTwo(input.Length)) + if (FftOperations.IsPowerOfTwo(input.Length)) return input; int targetLength = 1; @@ -40,7 +35,7 @@ public static Complex[] ZeroPad(Complex[] input) /// the input array or a zero-padded copy public static System.Numerics.Complex[] ZeroPad(System.Numerics.Complex[] input) { - if (IsPowerOfTwo(input.Length)) + if (FftOperations.IsPowerOfTwo(input.Length)) return input; int targetLength = 1; @@ -61,7 +56,7 @@ public static System.Numerics.Complex[] ZeroPad(System.Numerics.Complex[] input) /// the input array or a zero-padded copy public static double[] ZeroPad(double[] input) { - if (IsPowerOfTwo(input.Length)) + if (FftOperations.IsPowerOfTwo(input.Length)) return input; int targetLength = 1; From ca6c74f76ceab0a4597f925505d038250a23de39 Mon Sep 17 00:00:00 2001 From: Scott W Harden Date: Tue, 16 May 2023 22:52:30 -0400 Subject: [PATCH 4/5] FftSharp 2.0.0 --- src/FftSharp/FftSharp.csproj | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/FftSharp/FftSharp.csproj b/src/FftSharp/FftSharp.csproj index 33dfe15..20f49fa 100644 --- a/src/FftSharp/FftSharp.csproj +++ b/src/FftSharp/FftSharp.csproj @@ -16,9 +16,9 @@ https://github.com/swharden/FftSharp/releases true FftSharp.xml - 1.1.6 - 1.1.6.0 - 1.1.6.0 + 2.0.0 + 2.0.0.0 + 2.0.0.0 1591 portable true From 535e906417ab771cf8bfce8b79be95c5b3b2ef7d Mon Sep 17 00:00:00 2001 From: Scott W Harden Date: Tue, 16 May 2023 22:56:22 -0400 Subject: [PATCH 5/5] update readme --- README.md | 6 +++--- src/FftSharp/README.md | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 65ddc80..815000d 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ window.ApplyInPlace(signal); System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(signal); // or get the magnitude (units²) or power (dB) as real numbers -double[] magnitude = FftSharp.FFT.Magnitude(signal); -double[] power = FftSharp.FFT.Power(signal); +double[] magnitude = FftSharp.FFT.Magnitude(spectrum); +double[] power = FftSharp.FFT.Power(spectrum); ``` Signal | Windowed Signal | FFT @@ -60,7 +60,7 @@ int sampleRate = 48_000; // calculate the power spectral density using FFT System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(audio); -double[] psd = FftSharp.FFT.Power(signal); +double[] psd = FftSharp.FFT.Power(spectrum); double[] freq = FftSharp.FFT.FrequencyScale(psd.Length, sampleRate); // plot the sample audio diff --git a/src/FftSharp/README.md b/src/FftSharp/README.md index aeec22c..6ecd210 100644 --- a/src/FftSharp/README.md +++ b/src/FftSharp/README.md @@ -11,9 +11,9 @@ var window = new FftSharp.Windows.Hanning(); window.ApplyInPlace(signal); // Calculate the FFT as an array of complex numbers -Complex[] fftRaw = FftSharp.Transform.FFT(signal); +System.Numerics.Complex[] spectrum = FftSharp.FFT.Forward(signal); // or get the magnitude (units²) or power (dB) as real numbers -double[] fftMag = FftSharp.Transform.FFTmagnitude(signal); -double[] fftPwr = FftSharp.Transform.FFTpower(signal); +double[] magnitude = FftSharp.FFT.Magnitude(spectrum); +double[] power = FftSharp.FFT.Power(spectrum); ``` \ No newline at end of file