[DataRow(1174.66F, 106, DisplayName = "D6")] // Top octave public void OutputBinTestSingleValueWithPureSine(float testFreq, int expectedPeak) { const float BASE_FREQ = 55F; // A2 ShinNoteFinderDFT NF = new ShinNoteFinderDFT(); NF.CalculateFrequencies(BASE_FREQ); NF.FillReferenceTables(); NF.PrepareSampleStorage(); // Fill input. float Omega1 = testFreq * MathF.PI * 2 / NF.SampleRate; float[] TestWaveform = new float[NF.WindowSize / 2]; for (uint i = 0; i < TestWaveform.Length; i++) { TestWaveform[i] = MathF.Sin(i * Omega1); } NF.AddSamples(TestWaveform); //NF.SaveData(); // Get output float[] Output = NF.Magnitudes; // Find peak float PeakVal = -1F; int PeakInd = -1; for (int i = 0; i < Output.Length; i++) { if (Output[i] > PeakVal) { PeakVal = Output[i]; PeakInd = i; } } // Make sure peak is correct and large enough Assert.IsTrue(PeakVal > 1500F, "Peak was not large enough"); Assert.IsTrue(PeakInd == expectedPeak, "Peak was in the wrong place"); // Make sure all far-away bins are small enough for (int i = 0; i < Output.Length; i++) { if (Math.Abs(i - PeakInd) > 3) { Assert.IsTrue(Output[i] < (PeakVal / 2), "Other frequencies had content too strong compared to peak"); } } }
public void CheckOctavePattern() { const float BASE_FREQ = 55F; // A2 const float SIGNAL_FREQ = 880F; const float PHASE_OFFSET = MathF.PI / 4; // Apply a small phase offset so that the signal doesn't start at 0. ShinNoteFinderDFT NF = new ShinNoteFinderDFT(); NF.CalculateFrequencies(BASE_FREQ); NF.FillReferenceTables(); NF.PrepareSampleStorage(); float Omega = SIGNAL_FREQ * MathF.PI * 2 / NF.SampleRate; // Add the first sample, so we should only see content in the topmost octave NF.AddSample(MathF.Sin((0 * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 1) * NF.BinsPerOctave); // Second sample, now the top two octaves should be calculated. NF.AddSample(MathF.Sin((1 * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 2) * NF.BinsPerOctave); // Third sample, only the top octave should update NF.AddSample(MathF.Sin((2 * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 2) * NF.BinsPerOctave); // Fourth sample, the top 3 octaves should all get calculated NF.AddSample(MathF.Sin((3 * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 3) * NF.BinsPerOctave); // Fifth sample, only top octave NF.AddSample(MathF.Sin((4 * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 3) * NF.BinsPerOctave); // Sixth sample, top 2 octaves NF.AddSample(MathF.Sin((5 * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 3) * NF.BinsPerOctave); // Seventh sample, only top NF.AddSample(MathF.Sin((6 * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 3) * NF.BinsPerOctave); // Eighth sample, top 4 octaves should all be calculated. NF.AddSample(MathF.Sin((7 * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 4) * NF.BinsPerOctave); // 9th - 15th samples, same pattern, no new octaves yet for (int i = 8; i < 15; i++) { NF.AddSample(MathF.Sin((i * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 4) * NF.BinsPerOctave); } // 16th sample, top 5 octaves should be calculated. NF.AddSample(MathF.Sin((15 * Omega) + PHASE_OFFSET), true); CheckAllBins((NF.OctaveCount - 5) * NF.BinsPerOctave); void CheckAllBins(int contentStart) { for (ushort i = 0; i < contentStart; i++) // This octave should not yet have been calculated. { Assert.IsTrue(NF.Magnitudes[i] == 0, "Bin " + i + " should have been empty"); } for (ushort i = (ushort)contentStart; i < NF.BinCount; i++) // All other bins should have at least a slight amount of content. { Assert.IsTrue(NF.Magnitudes[i] != 0, "Bin " + i + " should not have been empty"); } } }
public void OutputBinTestCompondSineProprotional(float testFreq1, float testFreq2, int expectedPeak1, int expectedPeak2, float ratio) { const float BASE_FREQ = 55F; // A2 ShinNoteFinderDFT NF = new ShinNoteFinderDFT(); NF.CalculateFrequencies(BASE_FREQ); NF.FillReferenceTables(); NF.PrepareSampleStorage(); // Fill input. float Omega1 = testFreq1 * MathF.PI * 2 / NF.SampleRate; float Omega2 = testFreq2 * MathF.PI * 2 / NF.SampleRate; float[] TestWaveform = new float[NF.WindowSize / 2]; for (uint i = 0; i < TestWaveform.Length; i++) { float Wave1 = ratio * MathF.Sin(i * Omega1); float Wave2 = (1 - ratio) * MathF.Sin(i * Omega2); TestWaveform[i] = Wave1 + Wave2; } NF.AddSamples(TestWaveform); // Get output float[] Output = NF.Magnitudes; // Find peaks float PeakVal1 = -1F; float PeakVal2 = -1F; int PeakInd1 = -1; int PeakInd2 = -1; for (int i = 0; i < Output.Length; i++) { if (Output[i] > PeakVal1) { PeakVal2 = PeakVal1; PeakVal1 = Output[i]; PeakInd2 = PeakInd1; PeakInd1 = i; } else if (Output[i] > PeakVal2) { PeakVal2 = Output[i]; PeakInd2 = i; } } // Sort the peaks if (PeakInd1 > PeakInd2) { float ValTemp = PeakVal1; PeakVal1 = PeakVal2; PeakVal2 = ValTemp; int IndTemp = PeakInd1; PeakInd1 = PeakInd2; PeakInd2 = IndTemp; } // Make sure peaks are correct and large enough Assert.IsTrue(PeakVal1 > (1500F * ratio), "Peak 1 was not large enough"); Assert.IsTrue(PeakVal2 > (1500F * (1 - ratio)), "Peak 2 was not large enough"); Assert.IsTrue(PeakInd1 == expectedPeak1, "Peak 1 was in the wrong place"); Assert.IsTrue(PeakInd2 == expectedPeak2, "Peak 2 was in the wrong place"); // Make sure the ratio is about right float Total = PeakVal1 + PeakVal2; Assert.IsTrue(Math.Abs((PeakVal1 / Total) - ratio) < 0.05F, "Peak 1 was not balanced to ratio"); Assert.IsTrue(Math.Abs((PeakVal2 / Total) - (1 - ratio)) < 0.05F, "Peak 2 was not balanced to ratio"); // Make sure all far-away bins are small enough for (int i = 0; i < Output.Length; i++) { if (Math.Abs(i - PeakInd1) > 3 && Math.Abs(i - PeakInd2) > 3) { Assert.IsTrue(Output[i] < (Math.Max(PeakVal1, PeakVal2) / 1.5F), "Too much noise far away from peaks"); } } }