This project has moved and is read-only. For the latest updates, please go here.

how to plot fft results in realtime from a mic and get a stable and accurate spectrum

Oct 15, 2016 at 2:31 PM
Edited Oct 15, 2016 at 2:35 PM
Hello,
Im a little bit new to audio processing
Im trying to record a 17khz pitch using the laptop built in mic and then plot the spectrum graph and see how it changes through the whole spectrum, I used the naudio library, first I created a wavein to record the sound then added the samples from the buffer to the sampleaggregator function to return the fft values so that I can plot them. the graph gives correct results but its not too stable and its hard to know what the right frequency is as the magnitude keeps changing to high and low very fast. and also why when there is some noise in the background the 17khz pitch is not detected by the fft or has very small magnitude which cant be recognized even though the noise is usually voice which is far from 17khz.

I tried an application that shows the frequency spectrum and had no issues so the problem is not from the microphone

Im using a sample rate of 44100 and a fftlength of 4096
this is the main with button when pressed it starts recording
private void B1_Click(object sender, EventArgs e)
        {
             recorder = new WaveIn();
            recorder.DeviceNumber = 0;
            recorder.BufferMilliseconds = 50;
            recorder.WaveFormat = new WaveFormat(samplerate,32, 1);
            recorder.DataAvailable += new EventHandler<WaveInEventArgs>(waveIn_DataAvailable);

            recorder.StartRecording();

             //fft
            sampleAggregator.FftCalculated += new EventHandler<FftEventArgs>(FftCalculated);
            sampleAggregator.PerformFFT = true;

               //chart graph
            chart1.Series["wave"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.FastLine;
                chart1.Series["wave"].ChartArea = "ChartArea1";
        }

private void waveIn_DataAvailable(object sender, WaveInEventArgs e)
        {
         
        byte[] buffer = e.Buffer;
        int bytesRecorded = e.BytesRecorded;
        int bufferIncrement = recorder.WaveFormat.BlockAlign;        
        for (int index = 0; index < bytesRecorded; index +=bufferIncrement)
            {
                float sample = Convert.ToSingle(buffer[index] + buffer[index + 1] + buffer[index + 2] + buffer[index + 3]);
                sampleAggregator.Add(sample);
            }
        }

void FftCalculated(object sender, FftEventArgs e)
        {
            chart1.Series["wave"].Points.Clear();
            int len= e.Result.Length;
            double ampl;
              for (int i = Freq2Index(15000, samplerate, fftLength); i < Freq2Index(21000, samplerate,fftLength); i++)
            {

                ampl = Math.Sqrt((e.Result[i].X * e.Result[i].X) + (e.Result[i].Y * e.Result[i].Y));
                 chart1.Series["wave"].Points.AddXY((int)Index2Freq(i, samplerate, fftLength), ampl);
            }
        }
and this the sampleaggregator class
using NAudio.Dsp; // The Complex and FFT are here!

class SampleAggregator
{
    // FFT
    public event System.EventHandler<FftEventArgs> FftCalculated;
    public bool PerformFFT { get; set; }

    // This Complex is NAudio's own! 
    private Complex[] fftBuffer;
    private FftEventArgs fftArgs;
    private int fftPos;
    private int fftLength;
    private int m;

    public SampleAggregator(int fftLength)
    {
        if (!IsPowerOfTwo(fftLength))
        {
            throw new System.ArgumentException("FFT Length must be a power of two");
        }
        this.m = (int)System.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 : System.EventArgs
{
    public FftEventArgs(Complex[] result)
    {
        this.Result = result;
    }
    public Complex[] Result { get; private set; }
}
what can i do to get more stable and accurate measures from the fft function and also not affected by any other frequencies.

can you help or give any suggestion that i can use that can make it better.
and thank you very much
Oct 15, 2016 at 3:00 PM
for starters, your method of getting the floating point samples is flawed. You can't add together the four byte values. Also, you're not recording IEEE float, but 32 bit integer samples. So you'd need to use BitConverter.ToInt32 on every four bytes. I'd record at 16 bit, use BitConverter.ToInt16 and then divide by 32768f to turn it into a floating point sample.
Oct 15, 2016 at 3:13 PM
Thank you very much mark, I have been looking for an answer for a couple of days now and didnt know what was the problem. I tried your solution and now it works great .
The signal is stable and its not affected by noise.
I really appreciate your help