This project has moved. For the latest updates, please go here.

My program gets very slow to respond/stuck while fft is calculated

Jul 28, 2016 at 1:42 PM
Edited Jul 30, 2016 at 5:29 AM
Im trying to get the pitch of microphone input in real time. I have a capture base class which includes the sample rate, channels etc., and the sample aggregator class has taken from Mark's WPF demo

Capture Base
    public class CaptureBase : IDisposable
    {
        static int fftLength = 1024;
        const int samplingFrequency = 44100;
        const int channels = 1;
        const int noOfBuffers = 5;
        const int bufferMilisecs = 10;
        const int latency = 50;
        WaveIn sourceStream;
        DirectSoundOut soundOut;
        SampleAggregator sampleAggregator;
        bool disposed = false;
        bool isCapturing = false;

        public void StartCapture(Guid selectedDevice)
        {
            isCapturing = true;
           
            sampleAggregator = new SampleAggregator(fftLength);
            sampleAggregator.FftCalculated += new EventHandler<FftEventArgs>(FftCalculated);
            sampleAggregator.PerformFFT = true;

            sourceStream = new WaveIn();
            sourceStream.NumberOfBuffers = noOfBuffers;
            sourceStream.BufferMilliseconds = bufferMilisecs;
            sourceStream.WaveFormat = new WaveFormat(samplingFrequency, channels);
            sourceStream.DataAvailable += new EventHandler<WaveInEventArgs>(sourceStream_DataAvailable);
            sourceStream.StartRecording();
        }

        private void FftCalculated(object sender, FftEventArgs e)
        {
              //Analysis.HPS hps = new Analysis.HPS();
            for (var i = 0; i < e.Result.Length; i++)
            {
                Debug.WriteLine("FFT output.");
                Debug.WriteLine(e.Result[i].X);
                Debug.WriteLine(e.Result[i].Y);     
                //hps.HarmonicProductSpectrum(e.Result);
            }
        }

        public SampleAggregator SampleAggregator
        {
            get
            {
                return sampleAggregator;
            }
        }
      
        private void sourceStream_DataAvailable(object sender, WaveInEventArgs e)
        {
            byte[] buffer = e.Buffer;
            int bytesRecorded = e.BytesRecorded;
           
            for (int i = 0; i < e.BytesRecorded; i += 2)
            {
                short sample = (short)((buffer[i + 1] << 8) | buffer[i + 0]);
                float sample32 = (sample / 32758f);
                sampleAggregator.Add(sample32);
            }
        }

        public void Stop()
        {
            if (isCapturing)
            {
                isCapturing = false;
            }
        }

        public void Dispose(bool disposing)
        {
            if (disposed) return;

            disposed = true;
            GC.SuppressFinalize(this);
            if (isCapturing) Stop();
        }

        void IDisposable.Dispose()
        {
            Dispose(true);
        }
    }
}
Sample Aggregator
 public class SampleAggregator
    {
        private readonly Complex[] fftBuffer;
        private readonly FftEventArgs fftArgs;
        private int fftPos;
        private readonly int fftLength;
        private int m;
        public event EventHandler<FftEventArgs> FftCalculated;

        public bool PerformFFT { get; set; }

        public SampleAggregator(int fftLength)
        {
            if (!IsPowerOfTwo(fftLength))
            {
                throw new ArgumentException("FFT Length must be a power of two");
            }
            this.m = (int)Math.Log(fftLength, 2.0);
            this.fftLength = fftLength;
            this.fftBuffer = new Complex[fftLength];
            this.fftArgs = new FftEventArgs(fftBuffer);
        }

        bool IsPowerOfTwo(int x)
        {
            return (x & (x - 1)) == 0;
        }

        public void Add(float value)
        {
            if (PerformFFT && FftCalculated != null)
            {
                // Remember the window function! There are many others as well.
                fftBuffer[fftPos].X = (float)(value * FastFourierTransform.HammingWindow(fftPos, fftLength));
                fftBuffer[fftPos].Y = 0; // This is always zero with audio.
                fftPos++;
                if (fftPos >= fftLength)
                {
                    fftPos = 0;
                    FastFourierTransform.FFT(true, m, fftBuffer);
                    FftCalculated(this, fftArgs);
                }
            }
        }
    }

    public class FftEventArgs : EventArgs
    {
        [DebuggerStepThrough]
        public FftEventArgs(Complex[] result)
        {
            this.Result = result;
        }
        public Complex[] Result { get; private set; }
    }
}
}
Please can someone explain what i have done wrong.
Coordinator
Oct 11, 2016 at 10:32 AM
FFT calculation is CPU intensive, so performance can be an issue. There might be some performance optimisations available when adding to the sample aggregator. You could try running a profiler over it. Also consider running in release mode.
Oct 14, 2016 at 8:02 AM
Yeah. I move it to a background thread. Now it's working. Thanks Mark.

I have a small issue. I used both FFT and harmonic product spectrum (using FFT) to get fundamental frequency value of A4 note (440Hz) to see which one has the more accurate result. But they give results as ;

HPS : 443.3594 FFT: 441.2985Hz
HPS : 445.3125 FFT: 886.5023Hz
HPS : 443.3594 FFT: 888.455Hz
HPS : 441.4063 FFT: 443.2512Hz

I want to use HPS but can you suggest me a way to improve the frequency resolution?
Coordinator
Oct 14, 2016 at 8:09 AM
Not my area of speciality I'm afraid. I suggest asking at a music DSP forum
Try here: http://www.kvraudio.com/forum/viewforum.php?f=33
or here: https://dsp.stackexchange.com/
Oct 14, 2016 at 1:38 PM
Okay. Thanks Mark