fftw of 16bit Audio :: peak appearing wrong at 2f

455 views Asked by At

I am taking 32bit float audio(44.1Khz) on my PC(between -1 and +1) using Port Audio and taking fft of it with fftw.

Now I need to take the 16bit int Audio and take its fft. I have converted the Audio samples to float between -1 and +1. The fft works but the peak occurs at 2x the frequency at where it should be and so the max frequency resolution is also reduced. So with 44KHz, the max component that I can see is around 10 KHz whereas it was around 20KHz with 32 bit int/float.

For e.g if I give 10KHz signal from Sign generator to the Sound Card, the peak is now shown at 20KHz. Whereas the only thing that I changed is the format from paInt32 to paInt16. It works correctly on paInt32 format.

  outputStreamParam.channelCount = 1;
  outputStreamParam.device = Pa_GetDefaultOutputDevice();
  outputStreamParam.sampleFormat = paInt16;
  outputStreamParam.suggestedLatency = suggestedLatency;
  outputStreamParam.hostApiSpecificStreamInfo = NULL;

  inputStreamParam.channelCount = 1;
  inputStreamParam.device = Pa_GetDefaultInputDevice();
  inputStreamParam.sampleFormat = paInt16;
  inputStreamParam.suggestedLatency = suggestedLatency;
  inputStreamParam.hostApiSpecificStreamInfo = NULL; 

Conversion of int(16 or 32) to float between -1 and +1.

int audioProcessor::processingCallback(const void *inputBuffer,
                                        void *outputBuffer,
                                        unsigned long framesPerBuffer,
                                        const PaStreamCallbackTimeInfo* timeInfo,
                                       PaStreamCallbackFlags statusFlags)
{   unsigned int i;
     framesPerBuffer = framesPerBuffer/2;

      int *inint = (int*) inputBuffer;

    float *out = (float*) outputBuffer;
    float *in = (float*) inputBuffer;


     for( i=0; i<framesPerBuffer; i++ )
     {


        in[i] = inint[i]/2147483647.0f;


     }

FFTW Processor code.

 this->fftSize = fftSize;
    cout << "Plan start " <<  endl;

  outArraySize = fftSize/2+1;
  cout << "fft Processor start \n";
  fftIn = (double*) fftw_malloc(sizeof(double) * fftSize);
  fftOut = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * outArraySize );
  fftOutAbs = (double*) fftw_malloc(sizeof(double) * outArraySize );

  // fftwPlan = fftw_plan_dft_r2c_1d(fftSize, fftIn, fftOut, FFTW_ESTIMATE);
  cout << "Plan succeed " <<  endl;

    fftwPlan = fftw_plan_dft_r2c_1d(fftSize, fftIn, fftOut, FFTW_MEASURE);
}
1

There are 1 answers

3
jaket On BEST ANSWER

You need to implement two different methods of converting. One for int32 to float and another from int16 to float. As currently implemented it is using the int32 conversion in the int16 case. One problem with this is that the scaling factor for conversion to float is wrong. The other problem is that it strides through the input signal twice as fast as it supposed to which causes the frequencies to all be off by a factor of 2.

For the conversion from int16 you need to do something like this:

{   
   unsigned int i;
   framesPerBuffer = framesPerBuffer/2;

   short *in = (short*) inputBuffer;
   float *out = (float*) outputBuffer;

   for (i=0; i<framesPerBuffer; i++)
   {
      out[i] = in[i]/32767.0f;
   }

Another problem with the posted code is that it is writing the floating point samples back to the wrong buffer. You may not have noticed it with int32 since sizeof(int)==sizeof(float).

Also, the framesPerFrameBuffer = framesPerFrameBuffer/2 is suspicious. I don't see why you would need that.