syedhali / EZAudio

An iOS and macOS audio visualization framework built upon Core Audio useful for anyone doing real-time, low-latency audio processing and visualizations.
Other
4.96k stars 822 forks source link

More precise frequency from FFT with EZAudio? #371

Open audiokit16 opened 6 years ago

audiokit16 commented 6 years ago

I've generated a few pure sine tones with Audacity at different frequencies to test with. The issue I'm seeing is that the code is returning the same frequency for two different sine tones that are relatively close in value.

For example: A sine tone generated at 19255Hz will show up from FFT as 19293.750000Hz. So will a sine tone generated at 19330Hz. Something must be off in the calculations.

Any assistance in how I can modify the above code to get a more precise FFT frequency reading for pure sine tones is greatly appreciated. Thank you!

// // Initialize FFT // float maximumBufferSizeBytes = self.maximumBufferSize sizeof(float); self.info = (EZAudioFFTInfo )calloc(1, sizeof(EZAudioFFTInfo)); vDSP_Length log2n = log2f(self.maximumBufferSize); self.info->fftSetup = vDSP_create_fftsetup(log2n, FFT_RADIX2); long nOver2 = maximumBufferSizeBytes / 2; size_t maximumSizePerComponentBytes = nOver2 sizeof(float); self.info->complexA.realp = (float )malloc(maximumSizePerComponentBytes); self.info->complexA.imagp = (float )malloc(maximumSizePerComponentBytes); self.info->outFFTData = (float )malloc(maximumSizePerComponentBytes); memset(self.info->outFFTData, 0, maximumSizePerComponentBytes); self.info->inversedFFTData = (float *)malloc(maximumSizePerComponentBytes);

// // Calculate real + imaginary components and normalize // vDSP_Length log2n = log2f(bufferSize); long nOver2 = bufferSize / 2; float mFFTNormFactor = 10.0 / (2 bufferSize); vDSP_ctoz((COMPLEX)buffer, 2, &(self.info->complexA), 1, nOver2); vDSP_fft_zrip(self.info->fftSetup, &(self.info->complexA), 1, log2n, FFT_FORWARD); vDSP_vsmul(self.info->complexA.realp, 1, &mFFTNormFactor, self.info->complexA.realp, 1, nOver2); vDSP_vsmul(self.info->complexA.imagp, 1, &mFFTNormFactor, self.info->complexA.imagp, 1, nOver2); vDSP_zvmags(&(self.info->complexA), 1, self.info->outFFTData, 1, nOver2); vDSP_fft_zrip(self.info->fftSetup, &(self.info->complexA), 1, log2n, FFT_INVERSE); vDSP_ztoc(&(self.info->complexA), 1, (COMPLEX *) self.info->inversedFFTData , 2, nOver2); self.info->outFFTDataLength = nOver2;

// // Calculate max freq // if (self.sampleRate > 0.0f) { vDSP_maxvi(self.info->outFFTData, 1, &self.info->maxFrequencyMangitude, &self.info->maxFrequencyIndex, nOver2); self.info->maxFrequency = [self frequencyAtIndex:self.info->maxFrequencyIndex];

float nyquistMaxFreq = self.sampleRate / 2.0;
NSLog(@"FREQ: %f", (((float)self.info->maxFrequencyIndex / (float)self.info->outFFTDataLength) * nyquistMaxFreq));

} EZAudio code here: https://github.com/syedhali/EZAudio/blob/master/EZAudio/EZAudioFFT.m

heyaibek commented 4 years ago

Any suggestions for this question? I'm interested too on this topic 🤓

JanX2 commented 4 years ago

I would guess that this issue is a result of the use of FFT. It quantizes the frequencies into bins (log2n is effectively the number of bins in the example). If you need more frequency resolution, you can try weighing the bins around the loudest bin according to their energies or use another algorithm.