Is there a logarithmic function for FFT bin selection for any given # of bands?

Status
Not open for further replies.

MrTom

Well-known member
Here's the FFT example code:
Code:
    // read the 512 FFT frequencies into 16 levels music is heard in octaves, but the
    // FFT data is linear, so for the higher octaves, read many FFT bins together.
    level[0] =  fft1024.read(0);
    level[1] =  fft1024.read(1);
    level[2] =  fft1024.read(2, 3);
    level[3] =  fft1024.read(4, 6);
    level[4] =  fft1024.read(7, 10);
    level[5] =  fft1024.read(11, 15);
    level[6] =  fft1024.read(16, 22);
    level[7] =  fft1024.read(23, 32);
    level[8] =  fft1024.read(33, 46);
    level[9] =  fft1024.read(47, 66);
    level[10] = fft1024.read(67, 93);
    level[11] = fft1024.read(94, 131);
    level[12] = fft1024.read(132, 184);
    level[13] = fft1024.read(185, 257);
    level[14] = fft1024.read(258, 359);
    level[15] = fft1024.read(360, 511);

That's great and all for 16 bands, but what if I wanted 32, 64, or say 40 bands. I can't manually figure out all this before hand.
Is there a simple log function to reduce this to a simple for() loop?
 
Here's a little Perl script that might help....

Code:
#! /usr/bin/perl

$e = 1.3937;
$num = 16;

$sum = 0;
$count = 0;

for ($i=0; $i < $num; $i++) {
        printf "%2d  ", $i;
        $n = $e ** $i;
        printf "%6.2f  ", $n;
        $d = int($n + 0.5);
        printf "%3d  ", $d;
        $sum += $n;
        printf "%6.2f  ", $sum;
        printf "%3d ", $count;
        $count += $d - 1;
        printf "%3d ", $count;
        $count++;
        print "\n";
}
 
Well I tried the above calculations for 32 bands, do you change $num? And I got the following:
Obviously there's no bin numbered "99704". I don't think this will work.

Code:
 0    1.00    1    1.00    0   0 
 1    1.39    1    2.39    1   1 
 2    1.94    2    4.33    2   3 
 3    2.69    3    7.02    4   6 
 4    3.75    4   10.77    7  10 
 5    5.22    5   15.99   11  15 
 6    7.26    7   23.25   16  22 
 7   10.10   10   33.35   23  32 
 8   14.06   14   47.41   33  46 
 9   19.56   20   66.96   47  66 
10   27.22   27   94.18   67  93 
11   37.87   38  132.05   94 131 
12   52.70   53  184.75  132 184 
13   73.33   73  258.08  185 257 
14  102.04  102  360.12  258 359 
15  141.99  142  502.11  360 501 
16  197.57  198  699.68  502 699 
17  274.93  275  974.61  700 974 
18  382.56  383  1357.16  975 1357 
19  532.33  532  1889.49  1358 1889 
20  740.74  741  2630.23  1890 2630 
21  1030.74  1031  3660.97  2631 3661 
22  1434.27  1434  5095.24  3662 5095 
23  1995.79  1996  7091.02  5096 7091 
24  2777.14  2777  9868.16  7092 9868 
25  3864.38  3864  13732.54  9869 13732 
26  5377.29  5377  19109.83  13733 19109 
27  7482.50  7482  26592.33  19110 26591 
28  10411.90  10412  37004.23  26592 37003 
29  14488.15  14488  51492.38  37004 51491 
30  20160.27  20160  71652.65  51492 71651 
31  28053.01  28053  99705.66  71652 99704
 
The linked thread indicated the problem of more bins being a compromise. Did note lower bins groups would hold the smaller sets. With 16 bins on average for 32 groups instead of 32 bins average for 16 groups.

I realized that more than 18..20 steps make not much sense for a "pretty" display with fft256, so 16 is perhaps the best choice.
 
Last edited:
Well I tried the above calculations for 32 bands, do you change $num?

You need to edit both $e and $num.

How you choose $e involves either trail-and-error or some impressive math skills. If you figure out the math way, I'd love to hear the equation!
 
You need to edit both $e and $num.

How you choose $e involves either trail-and-error or some impressive math skills. If you figure out the math way, I'd love to hear the equation!

OK,
I try to take on the challenge
first,
the perl script seems to calculate not correctly
it says (as of MtTom's post)
Code:
1.3937 ** 15 = 141.99
while a matlab script says
Code:
1.3937^15 = 145.3917
I can reproduce the numbers in MrTom's post with
Code:
e=1.3915

OK, we can argue where the error is, so it does not matter

second
from an earlier post we know that the last bin was simply adjusted, so it band width is not consistent with previous values.

But ideally we wanted to generate a logarithmic scale that ends up with the last frequency bin, having the lower bins non overlapping.

so we note that a single consistent formula for the bin count could be a cumulative sum equivalent to

Code:
ee= 1.3937;
upper_bin = cumsum(round(ee^(0:15)))-1;
lower_bin = [0 upper_bin(1:end-1)+1];

resulting in
Code:
  0   1   3   6  10  15  22  32  46  66  94 133 187 262 366 511 
  0   1   2   4   7  11  16  23  33  47  67  95 134 188 263 367

Now, if one accepts this approach, then the approach of estimating the bounds becomes easy

determine number of bands, say 16
second determine second last desired frequency bin (here 366) describing the last band width
then we note that
Code:
511 - 366 = (cumsum(round(e^(0:15)))-1) - (cumsum(round(ee^(0:14)))-1) = round(ee^15)
so a good formula for obtaining an estimate for ee is
Code:
ee = exp(log(511-366)/15) = 1.3934

which is very close to the original value.
The difference is that the formula for estimating ee can not undo the round operation
with this value of ee the bounds become
Code:
  0   1   3   6  10  15  22  32  46  66  94 132 186 261 365 510 
  0   1   2   4   7  11  16  23  33  47  67  95 133 187 262 366
as 510 is short of 511, the value of ee should be slightly incremented (here from 1.2934 to 1.3937)

Now,
changing the number of bands to, say 20 one would simply get with

ee=1.2824

corrected to
ee=1.2829

with the following bin boundaries
Code:
 0   1   3   5   8  11  15  21  28  37  49  64  84 110 143 185 239 308 397 511 
 0   1   2   4   6   9  12  16  22  29  38  50  65  85 111 144 186 240 309 398
 
I got the e=1.3915 from the thread in the 2nd post in this thread.

Did some experimenting and made a "brute force" function to find a given E value. Just plug in the bands and the bins and the program does the rest to find and test the correct E value.

Code:
void setup() {
  float e, n;
  int b, bands, bins, count=0, d;

  while (!Serial && (millis() <= 6000));  // Wait for Serial interface

  bands = 20;                             // Frequency bands; (Adjust to desired value)
  bins = 512;                             // FFT bins; (Adjust to desired value)
  
  e = FindE(bands, bins);                 // Find calculated E value
  if (e) {                                // If a value was returned continue
    Serial.printf("E = %4.4f\n", e);      // Print calculated E value
    for (b = 0; b < bands; b++) {         // Test and print the bins from the calculated E
      n = pow(e, b);
      d = int(n + 0.5);

      Serial.printf( "%4d ", count);      // Print low bin
      count += d - 1;
      Serial.printf( "%4d\n", count);     // Print high bin
      ++count;
    }
  }
  else
    Serial.println("Error\n");            // Error, something happened
}

void loop() {
  // put your main code here, to run repeatedly:

}

float FindE(int bands, int bins) {
  float increment=0.1, eTest, n;
  int b, count, d;

  for (eTest = 1; eTest < bins; eTest += increment) {     // Find E through brute force calculations
    count = 0;
    for (b = 0; b < bands; b++) {                         // Calculate full log values
      n = pow(eTest, b);
      d = int(n + 0.5);
      count += d;
    }
    if (count > bins) {     // We calculated over our last bin
      eTest -= increment;   // Revert back to previous calculation increment
      increment /= 10.0;    // Get a finer detailed calculation & increment a decimal point lower
    }
    else
      if (count == bins)    // We found the correct E
        return eTest;       // Return calculated E
    if (increment < 0.0000001)        // Ran out of calculations. Return previous E. Last bin will be lower than (bins-1)
      return (eTest - increment);
  }
  return 0;                 // Return error 0
}

EDIT: Added an increment check "< 0.0000001", because if you try FindE(123, 512), it can never find the exact high bin of 511,
it can only find either 510 or 512, and it should be (bins-1) because the bins go from 0 to (bins-1), which is 0-511 in this case.

This should work from 2+ bands and whatever bins.

Example: 27 bands, 512 bins
E = 1.1836
0 0
1 1
2 2
3 4
5 6
7 8
9 11
12 14
15 18
19 23
24 28
29 34
35 42
43 51
52 62
63 75
76 90
91 108
109 129
130 154
155 183
184 217
218 258
259 306
307 363
364 431
432 511
 
Last edited:
I found this thread very interesting, and useful! By combining Mr Tom's example, the Adafruit NeoMatrix Library, Paul's SpectrumAnalyzerBasic, and adding some of my own sauce, I've created a very dynamic/scalable visualizer for WS2812's.

Code:
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Adafruit_NeoMatrix.h>
#include <Adafruit_NeoPixel.h>
#ifndef PSTR
#define PSTR // Make Arduino Due happy
#endif

AudioInputAnalog         adc1;
AudioAnalyzeFFT1024      fft1024;
AudioConnection          patchCord1(adc1, fft1024);

#define PIN 2
#define WIDTH 8
#define HEIGHT 5

// MATRIX DECLARATION:
// Parameter 1 = width of NeoPixel matrix
// Parameter 2 = height of matrix
// Parameter 3 = pin number (most are valid)
// Parameter 4 = matrix layout flags, add together as needed:
//   NEO_MATRIX_TOP, NEO_MATRIX_BOTTOM, NEO_MATRIX_LEFT, NEO_MATRIX_RIGHT:
//     Position of the FIRST LED in the matrix; pick two, e.g.
//     NEO_MATRIX_TOP + NEO_MATRIX_LEFT for the top-left corner.
//   NEO_MATRIX_ROWS, NEO_MATRIX_COLUMNS: LEDs are arranged in horizontal
//     rows or in vertical columns, respectively; pick one or the other.
//   NEO_MATRIX_PROGRESSIVE, NEO_MATRIX_ZIGZAG: all rows/columns proceed
//     in the same order, or alternate lines reverse direction; pick one.
//   See example below for these values in action.
// Parameter 5 = pixel type flags, add together as needed:
//   NEO_KHZ800  800 KHz bitstream (most NeoPixel products w/WS2812 LEDs)
//   NEO_KHZ400  400 KHz (classic 'v1' (not v2) FLORA pixels, WS2811 drivers)
//   NEO_GRB     Pixels are wired for GRB bitstream (most NeoPixel products)
//   NEO_RGB     Pixels are wired for RGB bitstream (v1 FLORA pixels, not v2)


// Example for NeoPixel Shield.  In this application we'd like to use it
// as a 5x8 tall matrix, with the USB port positioned at the top of the
// Arduino.  When held that way, the first pixel is at the top right, and
// lines are arranged in columns, progressive order.  The shield uses
// 800 KHz (v2) pixels that expect GRB color data.
Adafruit_NeoMatrix matrix = Adafruit_NeoMatrix(WIDTH, HEIGHT, PIN,
                            NEO_MATRIX_BOTTOM    + NEO_MATRIX_LEFT +
                            NEO_MATRIX_ROWS + NEO_MATRIX_ZIGZAG,
                            NEO_GRB            + NEO_KHZ800);

int fftBins[WIDTH];
float level[WIDTH];
int shown[WIDTH];
int peaks[WIDTH];
float scale = 80.0;
uint32_t dropTimer = millis();
uint32_t peakDropTimer = millis();

void setup() {
  matrix.begin();
  matrix.setTextWrap(false);
  matrix.setBrightness(30);
  AudioMemory(12);
  getFFTBins();
}

void loop() {
  display_fft(50, 1000);
  matrix.show();
}

void display_fft(uint32_t levelDecay, uint32_t peakDecay) {
  if (fft1024.available()) {
    for (int i = 0; i < WIDTH; i++) {
      if (i < WIDTH - 1) {
        level[i] = fft1024.read(fftBins[i], fftBins[i + 1] - 1);
      } else {
        level[i] = fft1024.read(fftBins[i], 511);
      }
    }

    int val = 0;
    for (int i = 0; i < WIDTH; i++) {
//    Serial.print(level[i]);
      val = level[i] * scale;
      if (val > HEIGHT) val = HEIGHT;
      if (val >= shown[i]) {
        shown[i] = val;
      }
      if (val >= peaks[i]) {
        peaks[i] = val;
      }
//    Serial.print(" ");
      matrix.drawLine(i, 0, i, HEIGHT - 1, 0);
      matrix.drawLine(i, HEIGHT - shown[i], i, HEIGHT, Wheel(180));
      matrix.drawPixel(i, HEIGHT - shown[i], Wheel(20));
      matrix.drawPixel(i, HEIGHT - peaks[i], Wheel(0));
    }
  }

  if (dropTimer + levelDecay < millis()) {
    for (int i = 0; i < WIDTH; i++) {
      if (shown[i] > 0) {
        shown[i] = shown[i] - 1;
      }
      dropTimer = millis();
    }
  }

  if (peakDropTimer + peakDecay < millis()) {
    for (int i = 0; i < WIDTH; i++) {
      if (peaks[i] > 0) {
        peaks[i] = peaks[i] - 1;
      }
      peakDropTimer = millis();
    }
  }
//Serial.print(" cpu:");
//Serial.println(AudioProcessorUsageMax());
}


uint32_t Wheel(byte WheelPos) {
  WheelPos = 255 - WheelPos;
  if (WheelPos < 85) {
    return matrix.Color(255 - WheelPos * 3, 0, WheelPos * 3);
  }
  if (WheelPos < 170) {
    WheelPos -= 85;
    return matrix.Color(0, WheelPos * 3, 255 - WheelPos * 3);
  }
  WheelPos -= 170;
  return matrix.Color(WheelPos * 3, 255 - WheelPos * 3, 0);
}

void getFFTBins() {
  float e, n;
  int b, bands, bins, count = 0, d;
  while (!Serial && (millis() <= 6000));  // Wait for Serial interface

  bands = WIDTH;                          // Frequency bands; (Adjust to desired value)
  bins = 512;                             // FFT bins; (Adjust to desired value)

  e = FindE(bands, bins);                 // Find calculated E value
  if (e) {                                // If a value was returned continue
    Serial.printf("E = %4.4f\n", e);      // Print calculated E value
    for (b = 0; b < bands; b++) {         // Test and print the bins from the calculated E
      n = pow(e, b);
      d = int(n + 0.5);
      Serial.printf( "%4d ", count);      // Print low bin
      fftBins[b] = count;
      count += d - 1;
      Serial.printf( "%4d\n", count);     // Print high bin
      ++count;
    }
  }
  else
    Serial.println("Error\n");            // Error, something happened
}

float FindE(int bands, int bins) {
  float increment = 0.1, eTest, n;
  int b, count, d;

  for (eTest = 1; eTest < bins; eTest += increment) {     // Find E through brute force calculations
    count = 0;
    for (b = 0; b < bands; b++) {                         // Calculate full log values
      n = pow(eTest, b);
      d = int(n + 0.5);
      count += d;
    }
    if (count > bins) {     // We calculated over our last bin
      eTest -= increment;   // Revert back to previous calculation increment
      increment /= 10.0;    // Get a finer detailed calculation & increment a decimal point lower
    }
    else if (count == bins)   // We found the correct E
      return eTest;       // Return calculated E
    if (increment < 0.0000001)        // Ran out of calculations. Return previous E. Last bin will be lower than (bins-1)
      return (eTest - increment);
  }
  return 0;                 // Return error 0
}
 
This document explains how to calculate frequency bands LIMITS and CENTER FREQUENCIES.

Looks like your "E" number is the ratio between any two consecutive CENTER or LIMIT frequencies.

If you divide the spectrum in full-octave bands, E = 2 = (2^1 octave), and you get about 11 bands in the spectrum.
For 1/2-octave bands, E=1,4142 (2^1/2 octave), with about 21 bands.
For 1/3 octave bands, E=1,2599 = (2^1/3 octave), with about 32 bands.

So the problem here is how to translate number of bands to a precise octave-fraction, and you can then determine "E".
With the information above, one should be able to come up with the formula and not have to brute force for a solution.

In your 27 bands example, your E (1,1836) is somewhat close to that of an 1/4 octave band split (E=1,1892).
But for that bandwidth, the audio spectrum has approximately 43 bands, not 27...
The issue is that only the 27 higher bands have a bandwidth above 43Hz.
Given the lack of resolution, it's always going to be a compromise in the last few lower bands.
 
So the problem here is how to translate number of bands to a precise octave-fraction, and you can then determine "E".

If you are interested in 'standard' (third) octave bands you are stuck with the iso list of center frequencies and you can use the formula given in Wikipedia https://en.wikipedia.org/wiki/Octave_band
Code:
%% Calculate Third Octave Bands in Matlab
fcentre  = 10^3 * (2 .^ ([-18:13]/3))
fd = 2^(1/6);
fupper = fcentre * fd
flower = fcentre / fd

Code:
%% Calculate Third Octave Bands in Matlab
flower = 10^3 * (2 .^ ([-18.5:12.5]/3))
fcentre  = 10^3 * (2 .^ ([-18:13]/3))
fupper = 10^3 * (2 .^ ([-17.5:13.5]/3))

extending this to lower bins and translating to frequency should be easy

In the earlier posts of this thread, the question was to have only log frequency bands (not necessarily (third) octave) and the question was on dividing the 512 frequency bins into a logarithmic scale of variable number of bands, so the value of E would be calculated from the number of desired bands.

So, if some approximation would be acceptable
I would use
Code:
ee= 2^(1/3);
upper_bin = min(511,cumsum(round(ee.^(0:21)))-1);
lower_bin = [0 upper_bin(1:end-1)+1];
[lower_bin;upper_bin]
round([lower_bin;upper_bin]*44100/1024)
which results to following FFT bins
Code:
     0     1     2     4     6     9    12    16    21    27    35    45    58    74    94   119   151
     0     1     3     5     8    11    15    20    26    34    44    57    73    93   118   150   190

   191   242   306   387   489
   241   305   386   488   511
or in Hz for 44.1kHz sampling and 1024 point FFT
Code:
           0          43          86         172         258         388         517         689
           0          43         129         215         345         474         646         861

         904        1163        1507        1938        2498        3187        4048        5125
        1120        1464        1895        2455        3144        4005        5082        6460

        6503        8226       10422       13178       16667       21059
        8183       10379       13135       16624       21016       22007
The nominal 1/3 octave bandwidth around 1k kHz is 981 - 1122, the resulting band of the above formula is 904 - 1120 Hz which IMO is fairly close
 
Code:
           0          43          86         172         258         388         517         689
           0          43         129         215         345         474         646         861

         904        1163        1507        1938        2498        3187        4048        5125
        1120        1464        1895        2455        3144        4005        5082        6460

        6503        8226       10422       13178       16667       21059
        8183       10379       13135       16624       21016       22007
The nominal 1/3 octave bandwidth around 1k kHz is 981 - 1122, the resulting band of the above formula is 904 - 1120 Hz which IMO is fairly close

Thanks for the reply!
I am not sure though if you are validating or correcting my comment (that "e" could be determined by the exact octave-fraction necessary to generate the number of bands desired). I understand the original problem of the thread, but I wasn't clear enough in my post. I suspect the "e" calculation used here in this discussion might not generate the bins that one really expects.

When one intends to divide the audio spectrum in N-bands (specially if one is going to measure magnitudes for something like a level bar-graph), it's important that all of the bands have the same octave-fraction, otherwise the relative magnitudes do not represent what they are supposed to. In other words, if you run pink-noise through a precise bar-graph, you should get a straight line connecting the bars (this line can be horizontal or have a slope, depending on how the magnitudes are computed, but the line should be, well, a straight line).

The more distorted the bar-graph response, the less straight will be this line. We understand that given the discrete nature of FFT and low frequency resolution in this case (SR 44.1kHz and FFT size 1024), the bar-graph would be severely distorted in the low frequency bands. There's not much we can do here unless we change the FFT parameters. This is also why it does not make sense to have more than 16 bands (or even 10) if you prioritize precision over visuals/resolution.

But my point is that the calculation used here in this thread to derive "e" seems to distort the bar-graph response even more.
If one uses this approach to divide the spectrum in, say, 20 bands, the resulting bins are smaller (less bandwidth) than would be expected for a true equal 20-bands spectrum. While the high frequency bars would be very well aligned, they would not represent the magnitude of a 1/20th spectrum band. I suppose the formula to find "e" (or the best possible "e") for this discrete case is much more complicated, although this approximation might be very good for many applications. After all, I believe what everyone is looking for here is to find a way to choose the lower and upper bins for each band in the less distorted possible way.

I guess a good experiment would be to actually run pink noise through the FFT/bar-graph and adjust the bins in real time to compare responses and choose the "less distorted". Of course this could also be done off-line in code.

I am waiting for my Teensy to arrive and I will certainly run some tests and share my findings with you.
 
Code:
     0     1     2     4     6     9    12    16    21    27    35    45    58    74    94   119   151
     0     1     3     5     8    11    15    20    26    34    44    57    73    93   118   150   190

   191   242   306   387   489
   241   305   386   488   511
or in Hz for 44.1kHz sampling and 1024 point FFT
Code:
           0          43          86         172         258         388         517         689
           0          43         129         215         345         474         646         861

         904        1163        1507        1938        2498        3187        4048        5125
        1120        1464        1895        2455        3144        4005        5082        6460

        6503        8226       10422       13178       16667       21059
        8183       10379       13135       16624       21016       22007
To illustrate my previous post, in my excel calculations, if you wanted 1/3 octave bands, the bins would be:

Code:
     1     1     1     1     1     2     2    3    3    4    5    6    8    10    13    16   20   25   32   40   51   64    81   102   128   162   203   256   323   407
     1     1     1     1     1     2     2    3    3    4    5    7    9    12    15    19   24   31   39   50   63   80   101   127   161   202   255   322   406   511

You would have 30 bands (30 bar graphs) that would represent a (possibly?) less distorted spectrum, with each bar actually measuring energy of a 1/3 octave.
Of course you would have to repeat a lot of bins in the lower bands (even for a non-discrete division, you can have infinite bands if you start with the upper limit and calculate the next band towards DC.). Here, I chose to to start where the center frequency in the continuous domain would be around 24Hz.
 
Last edited:
When one intends to divide the audio spectrum in N-bands (specially if one is going to measure magnitudes for something like a level bar-graph), it's important that all of the bands have the same octave-fraction, otherwise the relative magnitudes do not represent what they are supposed to. In other words, if you run pink-noise through a precise bar-graph, you should get a straight line connecting the bars (this line can be horizontal or have a slope, depending on how the magnitudes are computed, but the line should be, well, a straight line).

By definition e^(0:n) gives you logarithmic scale, what ever 'e' is chosen to be (e=2^(1/3) for third octave)
when converting these bands to FFT bins, you will always have a jitter due to finite bin width.
As it was implicit assumed not to use same frequency bin twice, no bin interpolation is done.
therefore, IMO, 'round(e^(0:n))' gives you the bin-based bandwidth closest to logarithmic scaled bandwidth.

Of course, you can convert the log bands by hand and pick the one you like best, but the OP asked for
Code:
Is there a simple log function to reduce this to a simple for() loop?

I look forward to see your simple implementation.
 
Last edited:
By definition e^(0:n) gives you logarithmic bandwidth, what ever 'e' is chosen to be (e=2^(1/3) for third octave)
when converting these bands to FFT bins, you will always have a jitter due to finite bin width.
As it was implicit assumed not to use same frequency bin twice, no bin interpolation is done.
therefore, IMO, 'round(e^(0:n))' gives you the bin-based bandwidth closest to logarithmic scaled bandwidth.

Sure, totally agree! I did not confront the logarithmic bandwidth generated by the code, but:
1. the actual bandwidth, and
2. the center frequency of the bar-graph (center bin).

I wish I had more math skills to try and make the point, or maybe I am totally wrong.
But experimentation should shed a light, I'll let you know!
 
To illustrate my previous post, in my excel calculations, if you wanted 1/3 octave bands, the bins would be:

Code:
     1     1     1     1     1     2     2    3    3    4    5    6    8    10    13    16   20   25   32   40   51   64    81   102   128   162   203   256   323   407
     1     1     1     1     1     2     2    3    3    4    5    7    9    12    15    19   24   31   39   50   63   80   101   127   161   202   255   322   406   511

You would have 30 bands (30 bar graphs) that would represent a (possibly?) less distorted spectrum, with each bar actually measuring energy of a 1/3 octave.
Of course you would have to repeat a lot of bins in the lower bands (even for a non-discrete division, you can have infinite bands if you start with the upper limit and calculate the next band towards DC.). Here, I chose to to start where the center frequency in the continuous domain would be around 24Hz.

Our posts crossed.
Duplicating frequencies provides redundant values and was excluded
 
Hi,

I got the e=1.3915 from the thread in the 2nd post in this thread.

Did some experimenting and made a "brute force" function to find a given E value. Just plug in the bands and the bins and the program does the rest to find and test the correct E value.

Code:
void setup() {
  float e, n;
  int b, bands, bins, count=0, d;

  while (!Serial && (millis() <= 6000));  // Wait for Serial interface

  bands = 20;                             // Frequency bands; (Adjust to desired value)
  bins = 512;                             // FFT bins; (Adjust to desired value)
  
  e = FindE(bands, bins);                 // Find calculated E value
  if (e) {                                // If a value was returned continue
    Serial.printf("E = %4.4f\n", e);      // Print calculated E value
    for (b = 0; b < bands; b++) {         // Test and print the bins from the calculated E
      n = pow(e, b);
      d = int(n + 0.5);

      Serial.printf( "%4d ", count);      // Print low bin
      count += d - 1;
      Serial.printf( "%4d\n", count);     // Print high bin
      ++count;
    }
  }
  else
    Serial.println("Error\n");            // Error, something happened
}

void loop() {
  // put your main code here, to run repeatedly:

}

float FindE(int bands, int bins) {
  float increment=0.1, eTest, n;
  int b, count, d;

  for (eTest = 1; eTest < bins; eTest += increment) {     // Find E through brute force calculations
    count = 0;
    for (b = 0; b < bands; b++) {                         // Calculate full log values
      n = pow(eTest, b);
      d = int(n + 0.5);
      count += d;
    }
    if (count > bins) {     // We calculated over our last bin
      eTest -= increment;   // Revert back to previous calculation increment
      increment /= 10.0;    // Get a finer detailed calculation & increment a decimal point lower
    }
    else
      if (count == bins)    // We found the correct E
        return eTest;       // Return calculated E
    if (increment < 0.0000001)        // Ran out of calculations. Return previous E. Last bin will be lower than (bins-1)
      return (eTest - increment);
  }
  return 0;                 // Return error 0
}

sorry for the reply in this old thread.
I used the calculation for 69 bands.
Why is there such a big hole there (pink noise -6dB)?

pinkhole.png

Kind regards
Bruno
 
That code is unnecessarily complicated - had a stare and didn't look clear to me - could be bugs hiding
in it.

This is probably a cleaner approach:
Code:
for (int b = 0 ; b < bands ; b++)
{
  int from = int (exp (log (bins) * b / bands)) ;
  int to   = int (exp (log (bins) * (b+1) / bands)) ;
  if (to > from)
  {
    float value = fft.read (from, to) ;
    ....
  }
}
 
Status
Not open for further replies.
Back
Top