1

I'm trying to determine the fundamental frequency of an input signal (from a tone generator, or possibly a musical instrument) using JavaScript's WebAudio API, along with some other SO articles (How to get frequency from fft result?, How do I obtain the frequencies of each value in an FFT?), but I can only seem to determine a frequency within about 5-10Hz.

I'm testing using a signal generator app for iPad positioned next to a high-quality microphone in a quiet room.

For 500Hz, it consistently returns 506; for 600Hz, I get 592. 1kHz is 1001Hz, but 2kHz is 1991. I've also noticed that I can modulate the frequency by 5Hz (at 1Hz increments) before seeing any change in data from the FFT. And I've only been able to get this accurate by averaging together the two highest bins.

Does this mean that there's not enough resolution in the FFT data to accurately determine the fundamental frequency within 1Hz, or have I gone about it the wrong way?

I've tried using both the native FFT libs (like this, for example):

var fFrequencyData = new Float32Array(analyser.frequencyBinCount);
analyser.getFloatFrequencyData(fFrequencyData);

(you can assume I've properly initialized and connected an Analyser node), which only shows a sensitivity/resolution of about

and also using DSP.js' FFT lib, like this:

var fft = new FFT();
fft.forward(e.inputBuffer.getChannelData(0));
fFrequencyData = fft.spectrum;

where e is the event object passed to onaudioprocess.

I seem to have a problem with FFT data (like FFT::spectrum) being null - is that normal? The data from fft.spectrum is naught unless I run it through analyser.getFloatFrequencyData, which I'm thinking overwrites the data with stuff from the native FFT, defeating the purpose entirely?

Hopefully someone out there can help steer me in the right direction - thanks! :)

Community
  • 1
  • 1
numonium
  • 131
  • 2
  • 7

1 Answers1

2

You would need a very large FFT to get high-quality pitch detection this way. To get a 1Hz resolution, for example, with a 44,100kHz sample rate, you would need a 64k(-ish) FFT size. You're far better off using autocorrelation to do monophonic pitch detection.

cwilso
  • 13,610
  • 1
  • 30
  • 35
  • I was thinking about an absurdly large FFT, but there's only 2048 indices of the InputBuffer::getChannelData() results, so there's not much I can do there, right? Could you shed a little light on autocorrelation for monophonic pitch detection? Is there a nice JS lib that might make this a bit easier (not as much funky maths)? – numonium Jun 08 '14 at 18:57
  • 1
    As it turns out, I wrote a pitch detector: https://github.com/cwilso/PitchDetect. :) – cwilso Jun 09 '14 at 14:00