/// <summary>
        /// returns the fft spectrum of a cross-correlation function.
        /// </summary>
        /// <param name="v1"></param>
        /// <param name="v2"></param>
        /// <returns></returns>
        public static double[] CrossCorr(double[] v1, double[] v2)
        {
            int n = v1.Length; // assume both vectors of same length

            double[] r;
            alglib.corrr1d(v1, n, v2, n, out r);

            // alglib.complex[] f;
            // alglib.fftr1d(newOp, out f);
            // System.LoggedConsole.WriteLine("{0}", alglib.ap.format(f, 3));
            // for (int i = 0; i < op.Length; i++) LoggedConsole.WriteLine("{0}   {1:f2}", i, op[i]);

            // rearrange corr output and NormaliseMatrixValues
            int xcorrLength = 2 * n;

            double[] xCorr = new double[xcorrLength];

            // for (int i = 0; i < n - 1; i++) newOp[i] = r[i + n];   //rearrange corr output
            // for (int i = n - 1; i < opLength-1; i++) newOp[i] = r[i - n + 1];
            for (int i = 0; i < n - 1; i++)
            {
                xCorr[i] = r[i + n] / (i + 1);  // rearrange and NormaliseMatrixValues
            }

            for (int i = n - 1; i < xcorrLength - 1; i++)
            {
                xCorr[i] = r[i - n + 1] / (xcorrLength - i - 1);
            }

            // add extra value at end so have length = power of 2 for FFT
            // xCorr[xCorr.Length - 1] = xCorr[xCorr.Length - 2];
            // LoggedConsole.WriteLine("xCorr length = " + xCorr.Length);
            // for (int i = 0; i < xCorr.Length; i++) LoggedConsole.WriteLine("{0}   {1:f2}", i, xCorr[i]);
            // DataTools.writeBarGraph(xCorr);

            xCorr = DataTools.DiffFromMean(xCorr);
            FFT.WindowFunc wf  = FFT.Hamming;
            var            fft = new FFT(xCorr.Length, wf);

            var spectrum = fft.Invoke(xCorr);

            return(spectrum);
        }// CrossCorrelation()
        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);
        }