public static Image <Rgb24> DrawWaveAndFft(double[] signal, int sr, TimeSpan startTime, double[] fftSpectrum, int maxHz, double[] scores) { int imageHeight = 300; double max = -2.0; foreach (double value in signal) { double absValue = Math.Abs(value); if (absValue > max) { max = absValue; } } double scalingFactor = 0.5 / max; // now process neighbourhood of each max int nyquist = sr / 2; int windowWidth = signal.Length; int binCount = windowWidth / 2; double hzPerBin = nyquist / (double)binCount; if (fftSpectrum == null) { FFT.WindowFunc wf = FFT.Hamming; var fft = new FFT(windowWidth, wf); fftSpectrum = fft.Invoke(signal); } int requiredBinCount = (int)(maxHz / hzPerBin); var subBandSpectrum = DataTools.Subarray(fftSpectrum, 1, requiredBinCount); // ignore DC in bin zero. var endTime = startTime + TimeSpan.FromSeconds(windowWidth / (double)sr); string title1 = $"Bandpass filtered: tStart={startTime.ToString()}, tEnd={endTime.ToString()}"; var image4A = DrawWaveform(title1, signal, signal.Length, imageHeight, scalingFactor); string title2 = $"FFT 1->{maxHz}Hz., hz/bin={hzPerBin:f1}, score={scores[0]:f3}={scores[1]:f3}+{scores[2]:f3}"; var image4B = DrawGraph(title2, subBandSpectrum, signal.Length, imageHeight, 1); var imageList = new List <Image <Rgb24> > { image4A, image4B }; Pen pen1 = new Pen(Color.Wheat, 1f); var stringFont = Drawing.Arial9; var bmp2 = new Image <Rgb24>(signal.Length, 25); bmp2.Mutate(g2 => { g2.DrawLine(pen1, 0, 0, signal.Length, 0); g2.DrawLine(pen1, 0, bmp2.Height - 1, signal.Length, bmp2.Height - 1); int barWidth = signal.Length / subBandSpectrum.Length; for (int i = 1; i < subBandSpectrum.Length - 1; i++) { if (subBandSpectrum[i] > subBandSpectrum[i - 1] && subBandSpectrum[i] > subBandSpectrum[i + 1]) { string label = $"{i + 1},"; g2.DrawText(label, stringFont, Color.Wheat, new PointF((i * barWidth) - 3, 3)); } } }); imageList.Add(bmp2); var image = ImageTools.CombineImagesVertically(imageList); return(image); }