2

I'm having understanding the Android Visualizer's

onFftDataCapture((Visualizer visualizer, 
            byte[] fft, 
            int samplingRate)

How do I know which frequency is represented by which byte in fft?

For example, would the index be a representation of frequency? Like index = frequency?

For index 0, frequency is 0 Hz, index 1 is 1 Hz, etc?

Dan
  • 278
  • 1
  • 3
  • 14

1 Answers1

3

FFT produces complex numbers, where each number reflects frequency domain information about a bucket containing a continuous range frequencies. The usual approach for interpreting FFT data is as follows:

  • first determine the frequency bucket size by dividing the length of the FFT table by the sampling rate, let's call it bucketSize;

  • then each bucket with index i (assuming it's 0-based) contains information about frequencies in the range from i * bucketSize to (i + 1) * bucketSize Hz;

  • for real-valued signals, the values of the second half of the FFT table (for buckets of frequencies above samplingRate / 2) will be just a mirror of the first half, so they are usually discarded;

  • also, for the first (index 0) and for the last (index samplingRate / 2) buckets, the value of the FFT table will be real as well (assuming real-valued signal);

  • to find the magnitude (signal level) for the frequencies in the bucket, one needs to take the complex value from the FFT table for this bucket, say it's a + ib, and calculate sqrt(a*a + b*b).

Now back to the results of onFftDataCapture. Here the fft array contains complex numbers as consecutive pairs of bytes, except for the first two elements, so fft[2] and fft[3] comprise the complex number for the first bucket, fft[4] and fft[5] -- for the second, and so on. Whereas fft[0] is the FFT value (real) for the 0-th frequency (DC), and fft[1] is for the last frequency.

Because, as I've mentioned, for real-valued signals the second part of the FFT table doesn't bring any benefit, it's not provided in the fft array. But since each FFT bucket takes two array cells, the bucket size in Hz will still be calculated as fft.length / samplingRate. Note that fft.length is actually the capture size set by setCaptureSize method of the Visualizer.

The magnitude of the bucket of frequencies can be easily calculated using Math.hypot function, e.g. for the first bucket it's Math.hypot(fft[2], fft[3]). For the DC bucket it's simply Math.abs(fft[0]), and for last frequency bucket it's Math.abs(fft[1]).

Mikhail Naganov
  • 6,099
  • 1
  • 23
  • 24
  • 1
    Super helpful! Do you have any additional sources for reference or is this just stuff you've learned over time? – Epicality Nov 04 '18 at 05:56
  • @Epicality I'm not sure if you are asking about the FFT itself, or this function. I've learned about the latter mostly from its source code. In fact, I could be wrong about the contents of fft[1]—according to the docs on Visualizer.getFft function ( https://developer.android.com/reference/android/media/audiofx/Visualizer.html#getFft(byte[]) ), the element fft[1] contains the real part of the last bucket (SR / 2). I will check that. If you want to know more about Fourier transform, there is a whole book on it: https://ccrma.stanford.edu/~jos/st/ – Mikhail Naganov Nov 27 '18 at 04:35
  • Thanks, man! I'm trying to add a color-based music visualizer to my navigation customization app. I appreciate the help here. – Epicality Nov 27 '18 at 16:12
  • @MikhailNaganov are you sure that frequency bucket size equals to fft[].lenght / sampling rate? In this comment I can see it'st samplingRate / fft[].length https://dsp.stackexchange.com/questions/26927/what-is-a-frequency-bin#comment96383_26980 – Jack Lynx Sep 17 '19 at 12:43