Search code examples
androidaudiobytevisualizer

Visualizer onFftDataCapture - Does the byte[] index represent frequency?


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?


Solution

  • 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]).