Audio Library - Possible Error in analyze_fft1024.h - read(binFirst, binLast)

drmartin

Well-known member
It appears to me that the fft.read(binFirst, binLast) function does add the energy in binLast to the sum.

Perhaps I misunderstood how to apply the function but I've read most of the examples using this function and it appears to me that the common understanding of function's usage expects that all of the bins from binFirst to binLast (inclusive) would be summed together.

Below is a self contained program that demonstrates the problem.

This program steps a sineWaveGenerator through several of the fft bins.

The bins are then read using fft.read(binFirst, binLast) and the resulting sum is displayed.

You can see that every binLast is not included.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveformSine sineWaveGen;
AudioAnalyzeFFT1024 fft;
AudioOutputAnalog dac1;
AudioConnection patchCord1(sineWaveGen, fft);
AudioConnection patchCord2(sineWaveGen, dac1);
// GUItool: end automatically generated code

// An array to hold the 16 frequency bands
float level[16];

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  delay(2000);
  Serial.println("Begin Setup");
  Serial.printf("F_BUS = %d\n", F_BUS);;
  int sampleRate = F_BUS / PDB0_MOD + 1;
  Serial.printf("Sample Rate = %d\n", sampleRate );
  Serial.printf("Bin Size = %d\n", sampleRate / 1024);
  delay(2000);

  // Audio requires memory to work.
  AudioMemory(12);

  // Configure the window algorithm to use              // - relative bin response to sine wave input -
  //fft.windowFunction(AudioWindowTukey1024);           // 0.04 0.10 0.18 0.63 0.18 0.10 0.03 = 1.26
  //fft.windowFunction(AudioWindowWelch1024);           // 0.02 0.04 0.17 0.56 0.17 0.04 0.02 = 1.02
  //fft.windowFunction(AudioWindowCosine1024);          // 0.02 0.04 0.18 0.53 0.18 0.04 0.02 = 1.01
  fft.windowFunction(AudioWindowHamming1024);           //           0.19 0.45 0.19           = 0.83
  //fft.windowFunction(AudioWindowHanning1024);         //           0.21 0.42 0.21           = 0.84
  //fft.windowFunction(AudioWindowBartlett1024);        //      0.02 0.17 0.42 0.17 0.02      = 0.80
  //fft.windowFunction(AudioWindowBlackman1024);        //      0.03 0.21 0.35 0.21 0.03      = 0.83
  //fft.windowFunction(AudioWindowBlackmanHarris1024);  //      0.06 0.20 0.30 0.20 0.06      = 0.82
  //fft.windowFunction(AudioWindowBlackmanNuttall1024); //      0.06 0.20 0.30 0.20 0.06      = 0.82
  //fft.windowFunction(AudioWindowNuttall1024);         //      0.06 0.20 0.30 0.20 0.06      = 0.82
  //fft.windowFunction(AudioWindowFlattop1024);         // 0.04 0.12 0.17 0.18 0.17 0.12 0.04 = 0.82

  // init the generator freq and level
  sineWaveGen.frequency(100.00);
  sineWaveGen.amplitude(1.0);
  Serial.println("End Setup");  
}

void loop() {
  float n;
  int i, j;

  for (j = 1; j < 18; j++) {
    sineWaveGen.frequency(j * 43); // step the generator through the first 17 fft bins
    digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); // Toggle the LED 
    Serial.printf("\n  --  Sinewave Generator set to %d Hz\n", j * 43);
    delay(200); // let the frequency step transits settle out -- send "pure" sine wave to fft
    if (fft.available()) {
      // each time new FFT data is available
      // print it all to the Arduino Serial Monitor
      Serial.print("FFT bin ");
      for (i = 0; i < 511; i++) {
        n = fft.read(i);
        // print only the bins with data for this sine wave binning test
        if (n >= 0.01) {
          Serial.print(i);
          Serial.print(" = ");
          Serial.print(n);
          Serial.print("    ");
        }
      }
      Serial.println();

      // bin size = 44,100 Hz / 1024  or 43.08 Hz per bin
      // to keep things simple - only sum the first few fft bins into groups of 3 or 43+43+43 or 129Hz buckets
      level[0] = fft.read(0);     // FFT "0Hz"     -> "DC"
      level[1] = fft.read(1, 3);
      level[2] = fft.read(4, 6);
      level[3] = fft.read(7, 9);
      level[4] = fft.read(10, 12);
      level[5] = fft.read(13, 15);
      level[6] = fft.read(16, 18);

      Serial.print(level[0]);
      Serial.print(" ");
      Serial.print(level[1]);
      Serial.print(" ");
      Serial.print(level[2]);
      Serial.print(" ");
      Serial.print(level[3]);
      Serial.print(" ");
      Serial.print(level[4]);
      Serial.print(" ");
      Serial.print(level[5]);
      Serial.print(" ");
      Serial.print(level[6]);
      Serial.println();
      if ((j % 3) == 0) {
        Serial.printf("NOTE - the energy in fft bin %d is NOT in the sum for bucket %d\n", j, j / 3);
      }
    }
  }
}

The output from this test program is:

Code:
  --  Sinewave Generator set to 43 Hz
FFT bin 0 = 0.05    1 = 0.54    2 = 0.23    
0.05 0.77 0.00 0.00 0.00 0.00 0.00

  --  Sinewave Generator set to 86 Hz
FFT bin 1 = 0.23    2 = 0.54    3 = 0.23    
0.00 0.77 0.00 0.00 0.00 0.00 0.00

  --  Sinewave Generator set to 129 Hz
FFT bin 2 = 0.23    3 = 0.54    4 = 0.23    
0.00 0.23 0.23 0.00 0.00 0.00 0.00
NOTE - the energy in fft bin 3 is NOT in the sum for bucket 1

  --  Sinewave Generator set to 172 Hz
FFT bin 3 = 0.23    4 = 0.54    5 = 0.23    
0.00 0.00 0.77 0.00 0.00 0.00 0.00

  --  Sinewave Generator set to 215 Hz
FFT bin 4 = 0.23    5 = 0.54    6 = 0.23    
0.00 0.00 0.77 0.00 0.00 0.00 0.00

  --  Sinewave Generator set to 258 Hz
FFT bin 5 = 0.24    6 = 0.54    7 = 0.23    
0.00 0.00 0.24 0.23 0.00 0.00 0.00
NOTE - the energy in fft bin 6 is NOT in the sum for bucket 2

  --  Sinewave Generator set to 301 Hz
FFT bin 6 = 0.24    7 = 0.54    8 = 0.22    
0.00 0.00 0.00 0.76 0.00 0.00 0.00

  --  Sinewave Generator set to 344 Hz
FFT bin 7 = 0.24    8 = 0.54    9 = 0.22    
0.00 0.00 0.00 0.78 0.00 0.00 0.00

  --  Sinewave Generator set to 387 Hz
FFT bin 8 = 0.24    9 = 0.54    10 = 0.22    
0.00 0.00 0.00 0.24 0.22 0.00 0.00
NOTE - the energy in fft bin 9 is NOT in the sum for bucket 3

I believe the error is the comparison operator used in the WHILE statement shown below

Code:
// code snippet from analyze_fft1024.h in the audio library

	float read(unsigned int binFirst, unsigned int binLast) {
		if (binFirst > binLast) {
			unsigned int tmp = binLast;
			binLast = binFirst;
			binFirst = tmp;
		}
		if (binFirst > 511) return 0.0;
		if (binLast > 511) binLast = 511;
		uint32_t sum = 0;
		do {
			sum += output[binFirst++];
		} while (binFirst < binLast);
		return (float)sum * (1.0 / 16384.0);
 
Last edited:
Below is a program version that demonstrates the "correct" bin summing action expected when using fft.read(binFirst, binLast)

This program was simply "patched" to compensate for the missing binLast .. of course a proper fix would be to correct the underlying library function but this sample program demonstrates what the output of a "correct" bin sum should be.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioSynthWaveformSine sineWaveGen;
AudioAnalyzeFFT1024 fft;
AudioOutputAnalog dac1;
AudioConnection patchCord1(sineWaveGen, fft);
AudioConnection patchCord2(sineWaveGen, dac1);
// GUItool: end automatically generated code

// An array to hold the 16 frequency bands
float level[16];

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
  delay(2000);
  Serial.println("Begin Setup - This version Patched to correct missing lastBin");
  Serial.printf("F_BUS = %d\n", F_BUS);;
  int sampleRate = F_BUS / PDB0_MOD + 1;
  Serial.printf("Sample Rate = %d\n", sampleRate );
  Serial.printf("Bin Size = %d\n", sampleRate / 1024);
  delay(2000);

  // Audio requires memory to work.
  AudioMemory(12);

  // Configure the window algorithm to use              // - relative bin response to sine wave input -
  //fft.windowFunction(AudioWindowTukey1024);           // 0.04 0.10 0.18 0.63 0.18 0.10 0.03 = 1.26
  //fft.windowFunction(AudioWindowWelch1024);           // 0.02 0.04 0.17 0.56 0.17 0.04 0.02 = 1.02
  //fft.windowFunction(AudioWindowCosine1024);          // 0.02 0.04 0.18 0.53 0.18 0.04 0.02 = 1.01
  fft.windowFunction(AudioWindowHamming1024);           //           0.19 0.45 0.19           = 0.83
  //fft.windowFunction(AudioWindowHanning1024);         //           0.21 0.42 0.21           = 0.84
  //fft.windowFunction(AudioWindowBartlett1024);        //      0.02 0.17 0.42 0.17 0.02      = 0.80
  //fft.windowFunction(AudioWindowBlackman1024);        //      0.03 0.21 0.35 0.21 0.03      = 0.83
  //fft.windowFunction(AudioWindowBlackmanHarris1024);  //      0.06 0.20 0.30 0.20 0.06      = 0.82
  //fft.windowFunction(AudioWindowBlackmanNuttall1024); //      0.06 0.20 0.30 0.20 0.06      = 0.82
  //fft.windowFunction(AudioWindowNuttall1024);         //      0.06 0.20 0.30 0.20 0.06      = 0.82
  //fft.windowFunction(AudioWindowFlattop1024);         // 0.04 0.12 0.17 0.18 0.17 0.12 0.04 = 0.82

  // init the generator freq and level
  sineWaveGen.frequency(100.00);
  sineWaveGen.amplitude(1.0);
  Serial.println("End Setup");
}

void loop() {
  float n;
  int i, j;

  for (j = 1; j < 18; j++) {
    sineWaveGen.frequency(j * 43); // step the generator through the first 17 fft bins
    digitalWriteFast(LED_BUILTIN, !digitalReadFast(LED_BUILTIN)); // Toggle the LED
    Serial.printf("\n  --  Sinewave Generator set to %d Hz\n", j * 43);
    delay(200); // let the frequency step transits settle out -- send "pure" sine wave to fft
    if (fft.available()) {
      // each time new FFT data is available
      // print it all to the Arduino Serial Monitor
      Serial.print("FFT bin ");
      for (i = 0; i < 511; i++) {
        n = fft.read(i);
        // print only the bins with data for this sine wave binning test
        if (n >= 0.01) {
          Serial.print(i);
          Serial.print(" = ");
          Serial.print(n);
          Serial.print("    ");
        }
      }
      Serial.println();

      // bin size = 44,100 Hz / 1024  or 43.08 Hz per bin
      // to keep things simple - only sum the first few fft bins into groups of 3 or 43+43+43 or 129Hz buckets
      level[0] = fft.read(0);     // FFT "0Hz"     -> "DC"
      level[1] = fft.read(1, 3 + 1); // +1 = Patch to compensate for missing lastBin
      level[2] = fft.read(4, 6 + 1); // +1 = Patch to compensate for missing lastBin
      level[3] = fft.read(7, 9 + 1); // +1 = Patch to compensate for missing lastBin
      level[4] = fft.read(10, 12 + 1); // +1 = Patch to compensate for missing lastBin
      level[5] = fft.read(13, 15 + 1); // +1 = Patch to compensate for missing lastBin
      level[6] = fft.read(16, 18 + 1); // +1 = Patch to compensate for missing lastBin

      Serial.print(level[0]);
      Serial.print(" ");
      Serial.print(level[1]);
      Serial.print(" ");
      Serial.print(level[2]);
      Serial.print(" ");
      Serial.print(level[3]);
      Serial.print(" ");
      Serial.print(level[4]);
      Serial.print(" ");
      Serial.print(level[5]);
      Serial.print(" ");
      Serial.print(level[6]);
      Serial.println();
    }
  }
}

The "correct" program output is

Code:
Begin Setup - This version Patched to correct missing lastBin
F_BUS = 48000000
Sample Rate = 44159
Bin Size = 43
End Setup

  --  Sinewave Generator set to 43 Hz
FFT bin 0 = 0.05    1 = 0.54    2 = 0.23    
0.05 0.77 0.00 0.00 0.00 0.00 0.00

  --  Sinewave Generator set to 86 Hz
FFT bin 1 = 0.23    2 = 0.54    3 = 0.23    
0.00 1.00 0.00 0.00 0.00 0.00 0.00

  --  Sinewave Generator set to 129 Hz
FFT bin 2 = 0.23    3 = 0.54    4 = 0.23    
0.00 0.77 0.23 0.00 0.00 0.00 0.00

  --  Sinewave Generator set to 172 Hz
FFT bin 3 = 0.23    4 = 0.54    5 = 0.23    
0.00 0.23 0.77 0.00 0.00 0.00 0.00

  --  Sinewave Generator set to 215 Hz
FFT bin 4 = 0.23    5 = 0.54    6 = 0.23    
0.00 0.00 1.00 0.00 0.00 0.00 0.00

  --  Sinewave Generator set to 258 Hz
FFT bin 5 = 0.24    6 = 0.54    7 = 0.23    
0.00 0.00 0.77 0.23 0.00 0.00 0.00

  --  Sinewave Generator set to 301 Hz
FFT bin 6 = 0.24    7 = 0.54    8 = 0.22    
0.00 0.00 0.24 0.76 0.00 0.00 0.00

  --  Sinewave Generator set to 344 Hz
FFT bin 7 = 0.24    8 = 0.54    9 = 0.22    
0.00 0.00 0.00 1.00 0.00 0.00 0.00

  --  Sinewave Generator set to 387 Hz
FFT bin 8 = 0.24    9 = 0.54    10 = 0.22    
0.00 0.00 0.00 0.78 0.22 0.00 0.00

  --  Sinewave Generator set to 430 Hz
FFT bin 9 = 0.24    10 = 0.54    11 = 0.22    
0.00 0.00 0.00 0.24 0.76 0.00 0.00

  --  Sinewave Generator set to 473 Hz
FFT bin 10 = 0.24    11 = 0.54    12 = 0.22    
0.00 0.00 0.00 0.00 1.00 0.00 0.00

  --  Sinewave Generator set to 516 Hz
FFT bin 11 = 0.24    12 = 0.54    13 = 0.22    
0.00 0.00 0.00 0.00 0.78 0.22 0.00

  --  Sinewave Generator set to 559 Hz
FFT bin 12 = 0.24    13 = 0.54    14 = 0.22    
0.00 0.00 0.00 0.00 0.24 0.76 0.00

  --  Sinewave Generator set to 602 Hz
FFT bin 13 = 0.24    14 = 0.54    15 = 0.22    
0.00 0.00 0.00 0.00 0.00 1.00 0.00
 
Last edited:
Back
Top