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

How to improve FFT results accuracy ?

Aug 12, 2016 at 5:20 PM
Edited Aug 12, 2016 at 5:21 PM
I'm trying to make an application to show pitch of microphone input in a pitch contour in real-time using NAudio. To check the FFT results I inputted an A4 note on a clarinet which has the known f0 of 440Hz. But I got 430.6641Hz and got 904.3945Hz once in a blue moon.

Button_Click Event
            var inputStream = new AudioFileReader("a4.wav");
            fileStream = inputStream;
            var aggregator = new SampleAggregator(inputStream);
            aggregator.NotificationCount = inputStream.WaveFormat.SampleRate / 100;
            aggregator.PerformFFT = true;
            aggregator.FftCalculated+=OnFftCalculated;
            playback = new WaveOut();
            playback.Init(aggregator);

            int read = 0;
            float[] buffer = new float[1024];

            do
            {
                read = aggregator.Read(buffer, 0, buffer.Length);
            } while (read > 0);

            playback.Play();
OnFFTCalculated Event
            float[] magnitudes = new float[e.Result.Length / 2];

            for (int i = 0; i < e.Result.Length / 2; i++)
                magnitudes[i] = ((float)Math.Sqrt((e.Result[i].X * e.Result[i].X) + (e.Result[i].Y * e.Result[i].Y)));


            float max_mag = float.MinValue;
            float max_index = -1;
            for (int i = 0; i < e.Result.Length / 2; i++)
                if (magnitudes[i] > max_mag)
                {
                    max_mag = magnitudes[i];
                    max_index = i;
                }

            var frequency = max_index * samplingFrequency / 1024; // CurrentSamplerate gets set dynamically incase some tracks have a different samplerate.
            Dispatcher.Invoke(new Action(() =>
            {
                Debug.WriteLine(frequency.ToString() + "Hz");
            }));
NAudio's Sample Aggregator Class
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; }
    }
}
What can be the issue? How can I improve the accuracy of the results?
Aug 14, 2016 at 4:45 PM
Only idea I got in mind on first view: You could use double instead of float to improve precision.
Aug 20, 2016 at 10:01 AM
Hey I'm trying to apply harmonic product spectrum to FFT result. But when I try to downsample I'm getting "cannot implicitly convert float to naudio.dsp.complex" because the X value (Real part) is a float. How can I solve this?
      public Complex[] Downsample(Complex[] data, int n)
        {
            Complex[] array = new Complex[Convert.ToInt32(Math.Ceiling(data.Length * 1.0 / n))];
            for (int i = 0; i < array.Length; i++)
            {
                array[i] = data[i * n].X; //Here .. It was originally __data[i * n].Re;__
__            }
            return array;
        }