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
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;
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, so
fft comprise the complex number for the first bucket,
fft -- for the second, and so on.
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