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

Thread: Spectrum Analyzer

  1. #1
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,208

    Spectrum Analyzer

    Hi,

    since i have some cpu-cycles left, i want to add a nice fft-display (with ILI9341) to my project.
    In the SpectrumAnalyzerBasic example is following "table":

    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);
    I want to use fft256, but i'm not still sure how many "levels" (see code) i want, so my Question is:
    How do you calc the values for L[x]=fft256.read(n,m) for a number "y" of "levels", resp. what's the formula/algorithm ?

  2. #2
    Senior Member
    Join Date
    Jul 2014
    Posts
    2,522
    Quote Originally Posted by Frank B View Post
    Hi,
    I want to use fft256, but i'm not still sure how many "levels" (see code) i want, so my Question is:
    How do you calc the values for L[x]=fft256.read(n,m) for a number "y" of "levels", resp. what's the formula/algorithm ?
    The formula for third octave would be (pseudo matlab)
    Code:
    2.^([ii;ii+1]/3)
    with
    Code:
    ii=0..N
    if you change 3 to 1.77 (16/ld(512)) you get roughly the bands from the example you showed

    However, if one analyses the bandwidth more carefully, one realizes that the relative bandwidth is not constant but decreases with frequency.
    edit: obviously, one must convert the frequency bins to integer and decide on bin overlap.
    Last edited by WMXZ; 04-21-2015 at 08:34 PM.

  3. #3
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,597
    Frank - the data in the FFT bins isn't directly magnitude. kpc or others know more - here is the last place I saw deriving it posted:

    // Calculate the magnitude:

    https://forum.pjrc.com/threads/28411...ll=1#post71043

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,319
    Quote Originally Posted by Frank B View Post
    How do you calc the values for L[x]=fft256.read(n,m) for a number "y" of "levels", resp. what's the formula/algorithm ?
    When I wrote the example, I create this perl script to make up the numbers.

    Code:
    #! /usr/bin/perl
    
    $e = 1.3915;
    
    $sum = 0;
    $count = 0;
    
    for ($i=0; $i < 16; $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";
    }
    If you run this, you'll see it ends at 501. I simply rounded the last one up. So far, nobody's ever noticed they're not perfectly scaled.....

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,319
    Quote Originally Posted by defragster View Post
    // Calculate the magnitude:
    If you're using the audio library objects, they DO compute the magnitude for you.

    For example, in the 256 point FFT you can see the square root call on line 91:

    https://github.com/PaulStoffregen/Au...fft256.cpp#L91

  6. #6
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,597
    Does it look like this?
    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
    Code:
    /*
    // https://forum.pjrc.com/threads/28430-Spectrum-Analyzer?p=71187&viewfull=1#post71187
    // How do you calc the values for L[x]=fft256.read(n,m) for a number "y" of "levels", resp. what's the formula/algorithm ?
    // When I wrote the example, I create this perl script to make up the numbers.
    */
    
    void setup() {
      // put your setup code here, to run once:
      while (!Serial && (millis() <= 6000));
    
    
      float e = 1.3915;
    
      float sum = 0;
      float n = 0;
      int count = 0;
      int d = 0;
    
      for (int i = 0; i < 16; i++) {
        Serial.printf("%2d  ", i);
        // n = e ** i;
        n = pow(e, i);
        Serial.printf( "%6.2f  ", n);
    
        d = int(n + 0.5);
        Serial.printf( "%3d  ", d);
    
        sum += n;
        Serial.printf( "%6.2f  ", sum);
    
        Serial.printf( "%3d ", count);
        count += d - 1;
        Serial.printf( "%3d ", count);
        count++;
    
        Serial.print( "\n");
      }
    
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    
    
    }

  7. #7
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    21,319
    Yup, those are the same numbers.

  8. #8
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,597
    Paul - Correct - I was thinking of that manual method outside Audio in that post. Combined with an old item kpc gave to calculate the frequency of a peak value from the Audio magnitudes.

  9. #9
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,597
    Cool - Go Teensy and C!

  10. #10
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,208
    Great, thank you all.

    I made a little excelsheet with WMXZ's formula, that's the easiest way to adjust the numbers.

    Edit:
    With 21 Bars (FFT256) it gives:
    Code:
    1	2
    2	2
    2	3
    3	4
    4	5
    5	6
    6	7
    7	9
    9	11
    11	13
    13	16
    16	20
    20	25
    25	31
    31	38
    38	46
    46	57
    57	69
    69	85
    85	104
    104	128
    Looks good, thanks.
    Last edited by Frank B; 04-21-2015 at 11:05 PM.

  11. #11
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,208
    If i want to display Dezibel: Is log10f(value)*factor correct ?

  12. #12
    Senior Member
    Join Date
    Jan 2015
    Location
    frisia
    Posts
    285
    You might want to reduce the top value with one. Now you have two overlapping bins. Eg. at the end, bin nr 104 will appear in the last and one-but last value.

    I think you forgot the scaling factor. log10 is just the log-base-10. It does not include the multiplication factor of 10.
    Also people in general are always confused when to take 10*log10 or 20*log10. The easiest is to remember that dB always refers to power. Since power is proportional to voltage squared, in this case you need to take 20.f * log10f(value).
    Probably also need a special case for a value of 0, since log(0) is undefined.

  13. #13
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,208
    Works :-)



    Teensy decoding 128kBps mp3-webstream + fft256

    Sorry for the soundquality, i had only headphones connected

  14. #14
    Junior Member
    Join Date
    Oct 2014
    Location
    Germany, Blumberg
    Posts
    11
    Hello,

    I just join this thread because I also like to do some FFT with a configurable number of bands. I'm using FFT256 so my user is able to chose his number of bands between 8 to 128 (in powers of 2). Thus, I also need some code to calculate the offsets for the .read(x, y) function.

    First question:
    Is someone having code that lets me pre-calculate the offsets during setup() and store them in an array?

    Second question:
    The example in the first post of this thread uses some values where no band is used twice. But some later reply is showing some offsets like this:
    1 2
    2 2
    2 3
    ...

    Now band 2 is in the sum of the first, second and third result. Is this good or wrong?

    Kind regards,

    Kukulkan

  15. #15
    Senior Member
    Join Date
    Jan 2015
    Location
    frisia
    Posts
    285
    wrt the second question. This is why I said previously
    You might want to reduce the top value with one. Now you have two overlapping bins. Eg. at the end, bin nr 104 will appear in the last and one-but last value.
    Even when doing this, you will find that you have to use the same band twice at the low end. You simply do not have enough resolution on a log scale, to show separate bins. You can
    1: Leave partly ovelapping bins at the low end
    2: Use an FFT with more points
    3: Cheat a little at the low end. Use a linear scale there, until the logarithmic steps are large enough.
    4: Perform some resampling/interpolation scheme at the low end (sinc?).
    Probably more options.

  16. #16
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,208
    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.

  17. #17
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,597
    Frank - is that the ILI9341 SD Card working (doesn't seem all soldered up) or another one? I'd like to see your display bar write code if you wanted to share - it looks good.

  18. #18
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,208
    Quote Originally Posted by defragster View Post
    Frank - is that the ILI9341 SD Card working (doesn't seem all soldered up) or another one? I'd like to see your display bar write code if you wanted to share - it looks good.
    Yes it is the ILI9341. I upload the code this evening.

  19. #19
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    10,597
    Thanks, looking forward to it. To clarify about the ILI9341 - you are using the SD card on that board?

  20. #20
    Junior Member
    Join Date
    Oct 2014
    Location
    Germany, Blumberg
    Posts
    11
    Thanks for the answers. I think I will manually calculate several options. Maybe only 32 and 64 bands, manually tweaked.

  21. #21
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,208
    Code:
    inline 
    bool fftDisplay(void)
    {      
          const int displayFreq = 24; //max Hertz
          const int displayPeriod = 1000 / displayFreq;
          const int decay = 15; //speed of bars going lower
          const int nBars = sizeof(fftOctTab) / 2 ;
          
          const int barWidth = 16;
          const int barGap = 2;
          
          const int posX = (320 / 2) - (nBars * barWidth / 2);
          const int posY = 239;
          const int minHeight = 2;
          const int maxHeight = 100;
          
          static uint16_t bar = 0;     
          static uint16_t oldData[nBars];
          static uint32_t oldMillis = millis();
          
          if (bar == 0) {
            uint32_t t = millis();
            if (t - oldMillis <= displayPeriod) return false;
            oldMillis = t;
          }
          
          float n;
          int16_t val;
          uint16_t oldval;       
          n = myFFT.read(fftOctTab[bar * 2], fftOctTab[bar * 2 + 1]);      
          val = log10f(n) * 60 + 150;
    
          oldval = oldData[bar];       
          if (val < oldval - decay ) val = oldval - decay;
          if (val < minHeight) val = minHeight;
          else 
          if (val > maxHeight) val = maxHeight;
    
          oldData[bar] = val;     
          //draw only the nessesary portions - the difference to the old bar.
          if (val < oldval)
            tft.fillRect(posX + bar * barWidth, posY - oldval, barWidth - barGap, oldval - val, ILI9341_BLACK);
          else if (val > oldval) {
             int y;
             int x = posX + bar * barWidth;
             for (y = posY - val; y < posY - oldval; y++)
               if (y & 6) 
                 tft.drawFastHLine(x, y,  barWidth - barGap , ILI9341_BLUE);
    //           else 
    //             tft.drawFastHLine(x, y,  barWidth - barGap , ILI9341_RED);
            // tft.fillRect(posX + bar * barWidth, posY - val, barWidth - barGap,  val - oldval , ILI9341_BLUE);
             }
          else {} //nothing to do
    
          if (++bar >= nBars) bar = 0;
          return true;
    }
    with
    Code:
    const uint8_t fftOctTab[] = {
    //0,	0, 
    1,	1,
    2,	2,
    3,	3,
    4,	4,
    5,	5,
    6,      7,
    8,       9,
    10,	11,
    12,	14,
    14,	16,
    17,	20,
    21,	25,
    26,	31,
    32,	38,
    39,	46,
    47,	57,
    58,	69,
    70,	85,
    86, 127
    //86,	103,
    //104,	127
    };
    This table is not perfect and should be optimized a bit - or simply use pauls' value from the example.
    Simply call fftDisplay() in your loop() often enough. Every call draws only one bar - this is needed, because drawing alls bars at once take too much time. Nevertheless, it's quite fast. But there is room for optimizations - the "drawFastHLine" in a loop for example could be exchanged with a optimzed version. The version above looks like this:

    (This version is with a smaller "decay" value. If you want it "faster" use values between 12..20 like in the code above)

    If you dont want the black grid, remove the "if (y & 6)" and evemtually use the "fillrect" version some lines later (instead of the "for..".
    Last edited by Frank B; 04-30-2015 at 04:57 PM.

  22. #22
    Senior Member+ Frank B's Avatar
    Join Date
    Apr 2014
    Location
    Germany NRW
    Posts
    6,208
    Variant with specialized "gridFillRect" which is a bit faster than the loop above (less spi-transmitted bytes):
    Code:
    class myILI9341_t3:  public ILI9341_t3
    {
    public:
          myILI9341_t3(uint8_t _CS, uint8_t _DC, uint8_t _RST = 255, uint8_t _MOSI=11, uint8_t _SCLK=13, uint8_t _MISO=12): ILI9341_t3(_CS,_DC,_RST,_MOSI,_SCLK,_MISO) {};
    
          inline void gridFillRect(int16_t x, int16_t y, const int16_t w, const int16_t h, const uint16_t color)
          {
    	SPI.beginTransaction(SPISettings(30000000, MSBFIRST, SPI_MODE0));
            int m = y+h-1;
    	setAddr(x, y, x+w-1, m);
    	writecommand_last(ILI9341_RAMWR);
    
    	for(int i=y; i<=m; i++) {
                   if ( i & 6 ) {
    		  for(x=w; x>1; x--) 
    			writedata16_cont(color);
    		  writedata16_last(color);
                   } else {
    		  for(x=w; x>1; x--) 
    			writedata16_cont(textbgcolor);
    		  writedata16_last(textbgcolor);
                   }
    
    	}
    	SPI.endTransaction();
         }  
    };
    
    
    inline 
    bool fftDisplay(void)
    {      
          const int displayFreq = 24; //max Hertz
          const int displayPeriod = 1000 / displayFreq;
          const int decay = 15; //speed of bars going lower
          const int nBars = sizeof(fftOctTab) / 2 ;
          
          const int barWidth = 16;
          const int barGap = 2;
          
          const int posX = (320 / 2) - (nBars * barWidth / 2);
          const int posY = 239;
          const int minHeight = 2;
          const int maxHeight = 100;
          
          static uint8_t bar = 0;     
          static uint8_t oldData[nBars];
          static uint32_t oldMillis = millis();
          
          if (bar == 0) {
            uint32_t t = millis();
            if (t - oldMillis <= displayPeriod) return false;
            oldMillis = t;
          }
          
          float n;
          int16_t val;
          uint16_t oldval;       
          n = myFFT.read(fftOctTab[bar * 2], fftOctTab[bar * 2 + 1]);      
          val = log10f(n) * 60 + 150;
    //      in = n * 400.0;
    
          oldval = oldData[bar];       
          if (val < oldval - decay ) val = oldval - decay;
          if (val < minHeight) val = minHeight;
          else 
          if (val > maxHeight) val = maxHeight;
    
          oldData[bar] = val;     
          //draw only the nessesary portions - the difference to the old bar.
          if (val < oldval)
            tft.fillRect(posX + bar * barWidth, posY - oldval, barWidth - barGap, oldval - val, ILI9341_BLACK);
          else if (val > oldval) {
    /*         int y;
             int x = posX + bar * barWidth;
             for (y = posY - val; y < posY - oldval; y++)
               if (y & 6) 
                 tft.drawFastHLine(x, y,  barWidth - barGap , ILI9341_BLUE);
    //           else 
    //             tft.drawFastHLine(x, y,  barWidth - barGap , ILI9341_RED);
    */
             tft.gridFillRect(posX + bar * barWidth, posY - val, barWidth - barGap,  val - oldval , ILI9341_BLUE);
             }
          else {} //nothing to do
    
          if (++bar >= nBars) bar = 0;
          return true;
    }
    (You have to remove the "private" keyword in the class in ILI9341_t3.h)

  23. #23
    Junior Member
    Join Date
    Nov 2017
    Posts
    2
    Is there a place where we can see the source code? Or do we load the SpectrumAnalyzerBasic and add this code?

    I appreciate any help

  24. #24
    Junior Member
    Join Date
    Aug 2014
    Location
    Kuala Lumpur, Malaysia
    Posts
    10

    Sound distorted using NeoMatrix on teensy audio adapter

    Hi,

    I'm using the teensy audio adapter with teensy 3.1

    I'm having some sound output issues , the sound output from the audio board is totally distorted when I apply the NeoMatrix library
    Is the NeoMatrix slowing down the audio processing ??

    Here is the codes, https://pastebin.com/bh41SNEb

    I can confirmed no sound distortion without applying the NeoMatrix library

    Any suggestion to solve this ?



    https://github.com/adafruit/Adafruit_NeoMatrix

    I'm using a 8 x 32 ws2812 with NeoMatrix library ...

    8x32 audio by stanley_seow, on Flickr

Posting Permissions

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