[linux-audio-dev] Using FFT to figure out frequencies of partials?

Mario Lang mlang at delysid.org
Wed Sep 8 23:34:39 UTC 2004


Hi.

I am trying to finally get a grip on what a FFT really does by the
learning by doing approach.  I ripped some code from qjacktuner (thanks to
Karsten Wiese) and rewrote it such that it uses fftw3 (which I'd
prefer as a backend for now).  However, the code executes, but returns
very strange results, and so I am asking the DSP coders of you
if you perhaps can see the probably quite obvious error I am making?

When I pluck the A string on my guitar, I receive output like this:

Sample rate: 48000
0 = {freq=143.807441, amp=8.776460}
1 = {freq=162.811147, amp=9.086173}
2 = {freq=181.814852, amp=9.604586}
3 = {freq=200.818557, amp=9.631306}
0 = {freq=148.405443, amp=9.330607}
1 = {freq=271.871957, amp=nan}
2 = {freq=148.405443, amp=49.469994}
3 = {freq=148.405443, amp=45.180032}
0 = {freq=19.003705, amp=47.543385}
2 = {freq=19.003705, amp=66.636058}
3 = {freq=394.003705, amp=63.710202}
0 = {freq=19.003705, amp=19.350767}
2 = {freq=19.003705, amp=28.120214}
3 = {freq=394.003705, amp=56.406310}
0 = {freq=19.003705, amp=42.607500}
2 = {freq=19.003705, amp=78.669607}
3 = {freq=241.974062, amp=38.201785}
0 = {freq=19.003705, amp=2.835922}
2 = {freq=241.974062, amp=93.692646}
3 = {freq=394.003705, amp=48.011389}
0 = {freq=19.003705, amp=45.488988}
2 = {freq=19.003705, amp=46.009611}
3 = {freq=394.003705, amp=60.820928}
2 = {freq=19.003705, amp=54.438861}
3 = {freq=394.003705, amp=47.402751}

In fact, it seems no matter what sound input I give it, it ends up reporting
19.003705 as the loudest frequency.

This is obviously wrong.  Insight appreciated :-)

/* foolowing code is only partial, so it doesn't compile, I can provide
 * you with the full source of the program if you want.
 * Variable "rate" is the current sampling rate (48000 in my case).
 */

#include <fftw3.h>

#define M_PI 3.14159265358979323846
#define MAX_FFT_LENGTH 16384
static float sampleFIFO[MAX_FFT_LENGTH];
static float *sample = NULL;
static float lastPhase[MAX_FFT_LENGTH/2+1];
static int fftSize;
static double *fftIn;
static fftw_complex *fftOut;
static fftw_plan fftPlan;

void
fftInit (int size)
{
  fftIn = fftw_malloc(sizeof(double) * size);
  fftOut = fftw_malloc(sizeof(fftw_complex) * (size/2+1));
  fftSize = size;
  fftPlan = fftw_plan_dft_r2c_1d(fftSize, fftIn, fftOut, FFTW_MEASURE);

  memset(sampleFIFO, 0, MAX_FFT_LENGTH*sizeof(float));
  memset(lastPhase, 0, (MAX_FFT_LENGTH*sizeof(float))/2);
}

void
fftMeasure (int nframes, int osamp, float *indata)
{
  double freqPerBin, expct;
  long i,k, qpd, inFifoLatency, stepSize;

  stepSize = fftSize/osamp;
  freqPerBin = rate/(double)fftSize;
  expct = 2.*M_PI*(double)stepSize/(double)fftSize;
  inFifoLatency = fftSize-stepSize;

  if (!sample) sample = sampleFIFO + inFifoLatency;

  for (i=0; i<nframes; i++) {
    *sample++ = indata[i];

    if (sample-sampleFIFO >= fftSize) {
      sample = sampleFIFO + inFifoLatency;

      for (k=0; k<fftSize; k++) {
        double window = -.5*cos(2.*M_PI*(double)k/(double)fftSize)+.5;
        fftIn[k] = sampleFIFO[k] * window;
      }
      fftw_execute(fftPlan);

      for (k=0; k<=fftSize/2; k++) {
	double magn, phase, tmp, real, imag;

        real = fftOut[k][0];
        imag = fftOut[k][1];

        /* compute magnitude and phase */
        magn = 2.*sqrt(real*real + imag*imag);
        phase = atan2(imag,real);

        /* compute phase difference */
        tmp = phase - lastPhase[k];
        lastPhase[k] = phase;

        /* subtract expected phase difference */
        tmp -= (double)k*expct;

        /* map delta phase into +/- Pi interval */
        qpd = (long)(tmp / M_PI);
        if (qpd >= 0) qpd += qpd&1;
        else qpd -= qpd&1;
        tmp -= M_PI*(double)qpd;

        /* get deviation from bin frequency from the +/- Pi interval */
        tmp = osamp*tmp/(2.*M_PI);

        /* compute the k-th partials' true frequency */
        tmp = (double)k*freqPerBin + tmp*freqPerBin;

	if (tmp > 0.0) {
	  printf("%d = {freq=%f, amp=%f}\n", k, tmp, magn);
	}
      }

      /* move input FIFO */
      for (k=0; k<inFifoLatency; k++) sampleFIFO[k] = sampleFIFO[k+stepSize];
    }
  }
}

void
fftFree ()
{
  fftw_destroy_plan(fftPlan);
  fftw_free(fftIn); fftw_free(fftOut);
}

-- 
Thanks,
  Mario



More information about the Linux-audio-dev mailing list