How to analyze only some of audio spectrum with FFT/FHT?

Experts,

I am looking for a way to use the standard arduino FFT (no FHT for non-AVR) but only for a small section of the audio spectrum.

Instead of breaking down the entire spectrum, resulting in wide bins even after /2, I'd like to only sample 2 or 3kHz of the spectrum, and thereby have a more fine-grained analysis, and use less computational power (i.e. make it faster)...

All the examples, like ArduinoFFT, are apparently sampling virtually all of 20Hz-20kHz... If I can let the code focus on a smaller part of the spectrum, I'm hoping for more passes/second and a smaller bin size that will run efficiently on an ESP32 or even ESP8266.

Anyone have any experience in taking the shotgun approach and using a rifle?

The FFT (Fast Fourier Transform) always transforms the entire input sampled data array into an entire output frequency array.

For a subset of the frequency spectrum, use the slower, standard Fourier transform and calculate only the bins of interest. Whether the end result is obtained more quickly depends on how many points you calculate. This page walks you through it, using very slow sine/cosine function calls: How to implement the discrete Fourier transform (see the C++ source code, real number version).

PS: I suspect that OpenMusicLabs FFT will be significantly faster than ArduinoFFT.

Or down-convert the signal frequency of interest down to start at DC.

So for instance to look at 4kHz to 6kHz range, mix with a 4kHz complex signal, thus shifting everything
4kHz down in frequency, then resample at 4kSPS and do the FFT. Be careful to only look at positive
frequencies, not negative, and be sure to shift down in frequency, not up.

jremington:
PS: I suspect that OpenMusicLabs FFT will be significantly faster than ArduinoFFT.

Without a doubt. But it is written specifically for the AVR chips. For speed (more samples/second, smaller bins) I'm using the ESP-32.

MarkT:
Or down-convert the signal frequency of interest down to start at DC.

So for instance to look at 4kHz to 6kHz range, mix with a 4kHz complex signal, thus shifting everything
4kHz down in frequency, then resample at 4kSPS and do the FFT. Be careful to only look at positive
frequencies, not negative, and be sure to shift down in frequency, not up.

Mark,
Thanks for writing. This is a very interesting idea. I'll have to learn about this in order to comment further. :slight_smile:

jremington:
For a subset of the frequency spectrum, use the slower, standard Fourier transform and calculate only the bins of interest. Whether the end result is obtained more quickly depends on how many points you calculate. This page walks you through it, using very slow sine/cosine function calls: How to implement the discrete Fourier transform (see the C++ source code, real number version).

@jremington, I forgot to thank you for this link. I only commented on your P.S. initially and overlooked extending my appreciation for the link & tip!
Sorry if I seemed like I didn't care... I did/do! :slight_smile:

If you want to experiment, here is a fully functional test version of the linked DFT code, intended for a standard C/C++ console program. I also added the ability to do the inverse DFT (merely a change of sign). It does get the correct answer, but uses slow sine/cosine calculations. That could be sped up significantly by using a lookup sine table, for example.

I use the free Code::Blocks IDE on a PC for testing, and more serious calculations.

If you want to modify the code to calculate only a subset of the output bins, just change the limits on k in the following line. In any case, k need run only until n/2 but running until n-1 allows you to see the full symmetry of the transform.

	for (k = 0; k < n; k++) {  // For each output element

Functional test program

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

/*
 * Computes the discrete Fourier transform (DFT) of the given complex vector, separated
 * into real and imaginary parts.
 *
 * If (n<0) compute the inverse transformation.
 *
 * All the array arguments must have the same length.
 * Only the first n/2 points in the output arrays are unique.
 */
void computeDft(int n, float *inreal, float *inimag, float *outreal, float *outimag)
		{
    float sign=1.;
    if (n<0) {n=-n; sign=-1.;}
    float factor = sign*2*M_PI/n; //take out of inner loop
    int k,t;
	for (k = 0; k < n; k++) {  // For each output element
		double sumreal = 0;
		double sumimag = 0;
		for (t = 0; t < n; t++) {  // For each input element
			double angle = factor * t * k;
			sumreal +=  inreal[t] * cos(angle) + inimag[t] * sin(angle);
			sumimag += -inreal[t] * sin(angle) + inimag[t] * cos(angle);
		}
		outreal[k] = sumreal;
		outimag[k] = sumimag;
	}
}
int main()
{
// length of FT
#define N 16

    float inreal[]={1, 1 , 1, 1, 1, 1, 1, 1, -1, -1, -1, -1, -1, -1, -1, -1}; //real square wave of period N samples
    float inimag[N]={0};
    float outreal[N]={0};
    float outimag[N]={0};
    computeDft(N,inreal,inimag,outreal,outimag);
    int k;
    printf("   real     imag  \n");
    for (k=0; k<N; k++) printf("%8.3f %8.3f\n",outreal[k],outimag[k]);

//compute the inverse transform. Output should be N x (original input).

    computeDft(-N,outreal,outimag,inreal,inimag);
    printf("   real     imag  \n");
    for (k=0; k<N; k++) printf("%8.3f %8.3f\n",inreal[k],inimag[k]);

    return 0;
}