Forum Rule: Always post complete source code & details to reproduce any issue!

1. ## 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.
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 ?  Reply With Quote

2. Originally Posted by Frank B 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.  Reply With Quote

3. 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:  Reply With Quote

4. Originally Posted by Frank B 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.....  Reply With Quote

5. Originally Posted by defragster // 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  Reply With Quote

6. 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:
```/*
// 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:

}```  Reply With Quote

7. Yup, those are the same numbers.   Reply With Quote

8. 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.  Reply With Quote

9. Cool - Go Teensy and C!  Reply With Quote

10. 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.  Reply With Quote

11. If i want to display Dezibel: Is log10f(value)*factor correct ?  Reply With Quote

12. 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.  Reply With Quote

13. Works :-)

Teensy decoding 128kBps mp3-webstream + fft256  Reply With Quote

14. 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  Reply With Quote

15. 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.  Reply With Quote

16. 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.  Reply With Quote

17. 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.  Reply With Quote

18. Originally Posted by defragster 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.  Reply With Quote

19. Thanks, looking forward to it. To clarify about the ILI9341 - you are using the SD card on that board?  Reply With Quote

20. Thanks for the answers. I think I will manually calculate several options. Maybe only 32 and 64 bands, manually tweaked.  Reply With Quote

21. 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..".  Reply With Quote

22. 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;
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)  Reply With Quote

23. 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  Reply With Quote

24. ## 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 ? 8x32 audio by stanley_seow, on Flickr  Reply With Quote