Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 21 of 21

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

  1. #1

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

    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?

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    16,886
    MrTom - did you see this thread? Spectrum-Analyzer - it might lead you in the right direction. I put your question in Bing ("FFT bin selection for any given # of bands pjrc") and first 'hit' found that old thread as I remembered it . . . if only Bing could find @KPC . . .

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,692
    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";
    }

  4. #4
    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

  5. #5
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    16,886
    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.

    Quote Originally Posted by Frank B View Post
    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 by defragster; 01-28-2016 at 05:19 AM.

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,692
    Quote Originally Posted by MrTom View Post
    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!

  7. #7
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,481
    Quote Originally Posted by PaulStoffregen View Post
    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

  8. #8
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    27,692
    I've added a comment with a link to this thread.

    https://github.com/PaulStoffregen/Au...bde237f1fbe24e

  9. #9
    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 by MrTom; 02-06-2016 at 03:09 AM.

  10. #10
    Junior Member varind's Avatar
    Join Date
    Jan 2017
    Location
    Portland, OR
    Posts
    1
    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
    }

  11. #11
    Junior Member
    Join Date
    Oct 2014
    Posts
    8
    Thank you for this thread, was able to put it to good use here building a little LED spectrum analyser to hang off the back of my Teensy-powered Radio Music module:
    https://www.instagram.com/p/BR6wnzjA...by=tomwhitwell
    (the matrix is one of these: https://www.boldport.com/products/the-matrix/ )

  12. #12
    Junior Member
    Join Date
    May 2017
    Posts
    4
    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.

  13. #13
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,481
    Quote Originally Posted by 0kino7ori View Post
    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

  14. #14
    Junior Member
    Join Date
    May 2017
    Posts
    4
    Quote Originally Posted by WMXZ View Post
    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.

  15. #15
    Junior Member
    Join Date
    May 2017
    Posts
    4
    Quote Originally Posted by WMXZ View Post
    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 by 0kino7ori; 05-10-2017 at 06:35 AM.

  16. #16
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,481
    Quote Originally Posted by 0kino7ori View Post
    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 WMXZ; 05-10-2017 at 06:49 AM.

  17. #17
    Junior Member
    Join Date
    May 2017
    Posts
    4
    Quote Originally Posted by WMXZ View Post
    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!

  18. #18
    Senior Member
    Join Date
    Jul 2014
    Posts
    3,481
    Quote Originally Posted by 0kino7ori View Post
    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

  19. #19
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64
    Hi,

    Quote Originally Posted by MrTom View Post
    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)?

    Click image for larger version. 

Name:	pinkhole.png 
Views:	68 
Size:	4.9 KB 
ID:	25519

    Kind regards
    Bruno

  20. #20
    Senior Member
    Join Date
    Jul 2020
    Posts
    1,791
    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) ;
        ....
      }
    }

  21. #21
    Member DIYLAB's Avatar
    Join Date
    Jun 2020
    Location
    Germany
    Posts
    64
    Thank you very much, I will try this tomorrow.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •