Need help selecting bins for an FFT

LionRelaxe

New member
Hello!

I am adapting the AnalogIn->FFT->OctoWS2811 exemple for my project.
My LED matrix is 16 wide by 79 high.
My main issue is adapting the frequencyBinsHorizontal
I now (almost) understand the concept, but I have no clue on how to select/tune the appropriate bin values.
A quick look on Excel showed the previous values were following an exponential curve, so I selected values on an exponential curve with the sum of the bins to be about the same:

int frequencyBinsHorizontal[matrix_width] = {
4, 4, 6, 8, 8, 12, 16, 19, 25, 30, 38, 48, 60, 74, 94
};

It works well, but the last two bins are much lower than the rest.
Is there a formula, procedure or guidance to split the FFT into bins with the human perception in mind?
Basically recreating the audio meter bar graph of old.

Thanks!
 
Last edited:
It works well, but the last two bins are much lower than the rest.
What exactly dou you mean by that?
For usual signals there is not much energy in the highest frequency bands. Could help to compare with a software device how the used signals look there.
Human musical pitch perception works "in octaves" = logarithmic... so you have to combine the linear frequency bands from the FFT.
You say matrix is 16 wide but in frequencyBinsHorizontal I see only 15 values. How does that work?
 
It works well, but the last two bins are much lower than the rest.
What exactly dou you mean by that
Of the 16 columm, each representing an FFT bin, the highest frequency columns show less amplitude.
Human musical pitch perception works "in octaves" = logarithmic... so you have to combine the linear frequency bands from the FFT.
Agreed, I'm looking for the mathematical relationship between "fft 1024" and Octave binnings. Currently, I'm just playing with trial and errors.
You say matrix is 16 wide but in frequencyBinsHorizontal I see only 15 values. How does that work?
The Example code use "0" as the first limit, then does look between last limit and add the next number to it, creating 16 intervals.
first is 0 to 0+4=4, second is 4 to 4+4=8, third is 8 to 8+6=14, and so on.
 
At what signal are you looking?
Compare it to other FFT representations you like.

There is always some playing, making it aesthetic, rounding. It is not 100% precise and cannot be with a limited number of output representations which are shown on LEDs or so. It is a decision for what signals that should work and look as desired. So it is your decision at what bin/frequency you exactly start and end. Even for technically oriented systems there has to be done some manual adjustmens. (what is your goal?)

Of course it is possible to calculate the distribution/mapping of the FFT bins to have a logarithmic represantion as good as possible if that is needed. This is a formula which works in Matlab ...:
Code:
matrix_width = 16;
fft_size = 1024;
freqBinIndex_vec = (2.^((0:matrix_width-1)/matrix_width * log2(fft_size/2)) -1)';

freqBinIndex_vec  = floor(freqBinIndex_vec)+1;
Fs = 44100;
binFrequencies = Fs/fft_size*(0:fft_size/2);

for n = 1:matrix_width
    Nbins = freqBinIndex_vec(n) - freqBinIndex_vec(max(n-1,1));
    disp(['n= ' num2str(n) ...
        '  fftbin=' num2str(freqBinIndex_vec(n)) ...
        ' ->Nbins=' num2str(Nbins) ...
        '  left freq[Hz]=' num2str(sprintf('%4.2f  ', binFrequencies (freqBinIndex_vec(n)))) ])
end
That gives:
Code:
n= 1  fftbin=1 ->Nbins=0  left freq[Hz]=0.00
n= 2  fftbin=1 ->Nbins=0  left freq[Hz]=0.00
n= 3  fftbin=2 ->Nbins=1  left freq[Hz]=43.07
n= 4  fftbin=3 ->Nbins=1  left freq[Hz]=86.13
n= 5  fftbin=4 ->Nbins=1  left freq[Hz]=129.20
n= 6  fftbin=7 ->Nbins=3  left freq[Hz]=258.40
n= 7  fftbin=10 ->Nbins=3  left freq[Hz]=387.60
n= 8  fftbin=15 ->Nbins=5  left freq[Hz]=602.93
n= 9  fftbin=22 ->Nbins=7  left freq[Hz]=904.39
n= 10  fftbin=33 ->Nbins=11  left freq[Hz]=1378.13
n= 11  fftbin=49 ->Nbins=16  left freq[Hz]=2067.19
n= 12  fftbin=72 ->Nbins=23  left freq[Hz]=3057.71
n= 13  fftbin=107 ->Nbins=35  left freq[Hz]=4565.04
n= 14  fftbin=158 ->Nbins=51  left freq[Hz]=6761.43
n= 15  fftbin=234 ->Nbins=76  left freq[Hz]=10034.47
n= 16  fftbin=346 ->Nbins=112  left freq[Hz]=14857.91
Hope this helps as a starting point and orientation.
(no guarantee that this calculation is without errors; Nbins is the difference to the last... and not displayed right for the lowest frequencies; note that in Matlab counting starts at 1 and not 0; probably you have to check the exact counting and indices; keep in mind that you want to map from the 1024 fft bins the first half from 0..511; so 511 is the last bin you want to include)

Note that in the example (SpectrumAnalyser) there is another mapping chosen which differs from this. What I am showing here is the way which is usually used in spectrum analysers.


when you define
C++:
int frequencyBinsHorizontal[matrix_width] = {
4, 4, 6, 8, 8, 12, 16, 19, 25, 30, 38, 48, 60, 74, 94
};
and matrix_width is set to 16, one entry in the array is missing.
In the example (SpectrumAnalyser) there is matrix_width set to 60, and in the array (frequencyBinsHorizontal) there are 60 entries.
That could be also a cause to have a strange behaviour of your last led part.

Another thing is the representation of level. Human perception is also logarithmic here. So it is good to use something like this and work in decibels:
C++:
level[i] = 20 * log10(level[i] + 0.000001);

I think this is not done in the example.

You can decide about the range to be used, e.g. -50 to 0 (dbFS is the unit here).

To make it look smooth, it can also make sense to have some filtering on the level. Or to use "attack and release times", i.e. a quicker reaction time, when levels go up, and then let them go down more slowly.
 
Back
Top