Fast Fourier Transformation Library? v2.0

So I am trying to understand the fft library for arduino
http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1286718155.
and i am currrently trying to base my code off of the LOL shields example code to create a VU meter with 12 columns with an intensity of 12(mapped to 12).
I am having trouble figuring out what to do after i break up the signal and map it accourdingly, are there returns that i can use? Any help would be greatly appreciated.
Here is the original unmodified for the LOL shield:

/*
 FFT for LoL Shield v0.9
 by Andy Doro
 http://andydoro.com/
 
based on FFT library and code from the Arduino forums and
 the Charlieplexing library for the LoL Shield.
 */
 
#include <Charliplexing.h>
 #include <fix_fft.h>
 
#define AUDIOPIN 5
 

char im[128], data[128];
 
char data_avgs[14];
 
int i=0,val;
 


void setup() {
 LedSign::Init(); //Initilizes the LoL Shield
 }
 

void loop() {
 
for (i=0; i < 128; i++){
 val = analogRead(AUDIOPIN);
 data[i] = val;
 im[i] = 0;
 };
 
fix_fft(data,im,7,0);
 
for (i=0; i< 64;i++){
 data[i] = sqrt(data[i] * data[i] + im[i] * im[i]); // this gets the absolute value of the values in the array, so we're only dealing with positive numbers
 };
 

// average bars together
 for (i=0; i<14; i++) {
 data_avgs[i] = data[i*4] + data[i*4 + 1] + data[i*4 + 2] + data[i*4 + 3]; // average together
 data_avgs[i] = map(data_avgs[i], 0, 30, 0, 9); // remap values for LoL
 }
 


// set LoLShield
 
for (int x=0; x < 14; x++) {
 for (int y=0; y < 9; y++) {
 if (y < data_avgs[13-x]) { // 13-x reverses the bars so low to high frequences are represented from left to right.
 LedSign::Set(x,y,1); // set the LED on
 } else {
 LedSign::Set(x,y,0); // set the LED off
 }
 }
 }
 
}

Here it is with what i have figured out:

/*
 FFT for LoL Shield v0.9 //nope, for Omnomni Table
 by Andy Doro //kinda
 http://andydoro.com/
 */
 #include <fix_fft.h>
 
#define AUDIOPIN 5
 

char im[128], data[128];
 
char data_avgs[12]; //changed to 12 cause 12 columns???
 
int i=0,val;
 


void setup() {
Serial.begin(9600); //is this baud rate to slow? can i print the values of the 12 columns?
 }
 

void loop() {
 
for (i=0; i < 128; i++){
 val = analogRead(AUDIOPIN);
 data[i] = val;
 im[i] = 0;
 };
 
fix_fft(data,im,7,0); // does this need changing? What is it for?
 
for (i=0; i< 64;i++){
 data[i] = sqrt(data[i] * data[i] + im[i] * im[i]); // this gets the absolute value of the values in the array, so we're only dealing with positive numbers
 };
 

// average bars together
 for (i=0; i<12; i++) {
 data_avgs[i] = data[i*4] + data[i*4 + 1] + data[i*4 + 2] + data[i*4 + 3]; // average together, but for 12 not 14
 data_avgs[i] = map(data_avgs[i], 0, 30, 0, 12); // remap values for my application, so 12
 }
 


// set LoLShield, this is where i am confused, i need to be able to see the values in a serial moniter, anyone??
 
for (int x=0; x < 14; x++) {
 for (int y=0; y < 9; y++) {
 if (y < data_avgs[13-x]) { // 13-x reverses the bars so low to high frequences are represented from left to right.
 LedSign::Set(x,y,1); // set the LED on
 } else {
 LedSign::Set(x,y,0); // set the LED off
 }
 }
 }
 
}

The 128-sample FFT gives 64 results (n/2). The original code averages them as 14 sets of 4 (56 of the 64 samples). If you use 12 sets of 4 you are only using 48 of the 64 samples. You might want to make it 12 sets of 5 to use 60 of the 64 samples.

// average bars together
 for (i=0; i<12; i++) {
 data_avgs[i] = data[i*5] + data[i*5 + 1] + data[i*5 + 2] + data[i*5 + 3] + data[i*5 + 4]; // average together, for 12 sets of 5
 data_avgs[i] = map(data_avgs[i], 0, 37, 0, 12); // remap values for my application, so 12
 }

You may need to tune the map(). Since you are adding 5 samples together instead of 4 I made the limit 25% higher. I suspect they just tried several values until they got one that looked nice (30).

ok cool, thanks for the quick reply. I will take that into account, do you know how i could change it so i can print the values to the serial moniter??

Paranemertes: do you know how i could change it so i can print the values to the serial moniter??

After calculating the values, use a for-loop and Serial.print() to send the values to the Serial Monitor.

Could you pretty please set up some example code?? Im just not sure where to start.... (sorry about the noobness)

    // print out the averaged bars
    for (i=0; i<12; i++)
        {
        Serial.print(data_avgs[i]);
        if (i < 11) 
            Serial.print(", ");  // Separate values for easy reading
        else
            Serial.println(".");   // End of line
        }

Do you know offhand if there is an Arduino function/library available that will print out a simple form of spectrum display to the Serial Monitor using the Serial.print() function, eg

http://images.google.com/search?q=spectrum+display&tbm=isch&hl=en&source=hp&gbv=2

Doesn't have to be in color, or super high-res.

Do you know offhand if there is an Arduino function/library available that will print out a simple form of spectrum display to the Serial Monitor using the Serial.print() function

You are aware, I hope, that the Serial Monitor is a text-based display. None of those images could be created on a typewriter.

You are aware, I hope, that the Serial Monitor is a text-based display. None of those images could be created on a typewriter.

Yes, that's why I said simple form, not hi-res, eg (horizontal preferable to vertical orientation)

|
|   |      |||     |
||  ||    ||||  ||||    |
.......................................

I know it's not too hard to do, just wondered whether it's already been done.

Thanks a ton johnwasser, ill try that when i get home. So i am assuming that data_avgs[] is an array?

Paranemertes: So i am assuming that data_avgs[] is an array?

What else did you think it could be?

What else did you think it could be?

ummmmmmm, ninjas in disguise??? just making sure :) could i change the array somehow so i just have 12 integers/variables, might make it easier for my beginner/novice experiance... Im also assuming i would use a for() to do this too? would it not really be neccesary? thanks

Paranemertes: could i change the array somehow so i just have 12 integers/variables, might make it easier for my beginner/novice experiance... Im also assuming i would use a for() to do this too? would it not really be neccesary? thanks

Trying to tuen that array into 12 separate variables will make the code quite a bit more complex. You can't use the for-loop to reference the 12 values so you would have to write very code 12 times:

// average bars together
 data_avgs0 = data[0*5] + data[0*5 + 1] + data[0*5 + 2] + data[0*5 + 3] + data[0*5 + 4]; // average together, for 12 sets of 5
 data_avgs0 = map(data_avgs0, 0, 37, 0, 12); // remap values for my application, so 12

 data_avgs1 = data[1*5] + data[1*5 + 1] + data[1*5 + 2] + data[1*5 + 3] + data[1*5 + 4]; // average together, for 12 sets of 5
 data_avgs1 = map(data_avgs0, 0, 37, 0, 12); // remap values for my application, so 12
 .
 .
 .
 data_avgs11 = data[11*5] + data[11*5 + 1] + data[11*5 + 2] + data[11*5 + 3] + data[11*5 + 4]; // average together, for 12 sets of 5
 data_avgs11 = map(data_avgs0, 0, 37, 0, 12); // remap values for my application, so 12

so this is what i got in the serial monitor when i tried your code:

, 
, a,    , , , a, a, , , 
, .
, a, , , ,  , , , a, a, , a.
, , , , , , , , , , a, .
    , , ,     , , , , , 
, a, , .
, , , , , , , , , , , .
, , , , , , , , a, , , a.
, , , a, , , , , , , , a.
, , , , , , , , , , , .
, , , , , , , , , , , .
a, a, a,    , , a, , ,   , ,    , a.
, , , , , , , , , , , .
, , a, , , , a, , , , , .
, , , , a, , a, 
, , , a, .
, a, , a,     , , 
,   , 
, ,    , a.
a, , a, , a,  , 
, , , ,  , .
    , a, a, a, , a, , , ,   , , a.
, 
,   ,   ,   ,   , , a, , , , a.

, , , , , 
, , , 
, , , 
.
a, a, 
, , , ,  , a,    , , , .
a, 
, a, a, ,  , a, 
, ,    ,   , a.
, a, , a,     , a, ,     ,   , , , .
, , ,    , , , , , , a, , 
.
    , , , a,  , , , a,  , ,    , .
, a, ,    , a, , ,  , , , ,  .
,  ,   , a, , ,  , , 
,   , , .
    , , a, , , ,    , 
, ,    , , .
, 
, , , , , a, 
, a, a, , .
a, , , , , , a, , , , , .
, , a, 
, , , , 
, , a,     , .
, , , , , , ,    ,   , , , a.
, , 
, , , a,  , , , , , a.

, , , , , , , , , , a,     .
, , ,    , a, , 
, 
, , , ,  .
,  , a, 
, , a, , ,   , ,    , a.
, 
, , 
, , ,     , , , , a, a.
    , , a, , , ,    , a, , , , .
a, a, , 
,   , , , ,  , , , 
.
, , a, , , , a, a, , , , .
    , a, , , , , a, , , ,    , .
, , , 
,   , , a, ,  , a, , .
, a, a, , , , ,    , , , , .
, 
, a, , , a, a, a,

is that what its supposed to look like? I was expecting numbers. like 0,0,0,0,0,0,0,0,0,0,0,0,when nothing is happening and then 3,6,2,9,5,2,8,6,1,8,10,11 when it starts, then those numbers change due to the music....

Paranemertes: so this is what i got in the serial monitor when i tried your code:

I think I see a face there.

But his code didn't print anything. What if you showed your code?

BTW, do you know how to print numbers with Serial.print? If you expect numbers perhaps you try to print numbers.

To save you the trouble of looking it up, look at this page:

http://arduino.cc/en/Serial/Print

Well, since no one volunteered the display I was after, I cobbled one toghether, will print
upto 128 values, and positive-only values, bipolar values, or fullwave-rectified values.

Bar height set = 8 here for compactness, but can be changed inside the display function.
I built a sinewave for display.

0 49 97 142 180 212 236 251 255 251 236 213 181 142 98 50 
0 -49 -97 -141 -180 -212 -236 -250 -255 -251 -236 -213 -181 -142 -98 -50 
0 49 97 141 180 212 236 250 255 251 236 213 181 143 99 51 
1 -48 -96 -141 -180 -212 -235 -250 -255 -251 -237 -213 -182 -143 -99 -51 
        |                               |                       
      |||||                           |||||                     
     |||||||                         |||||||                    
    |||||||||                       |||||||||                   
   |||||||||||                     |||||||||||                  
  |||||||||||||                   |||||||||||||                 
  |||||||||||||                   |||||||||||||                 
 |||||||||||||||                 |||||||||||||||                
----------------------------------------------------------------
                 |||||||||||||||                 |||||||||||||||
                  |||||||||||||                   ||||||||||||| 
                  |||||||||||||                   ||||||||||||| 
                   |||||||||||                     |||||||||||  
                    |||||||||                       |||||||||   
                     |||||||                         |||||||    
                      |||||                           |||||     
                        |                               |       


        |               |               |               |       
      |||||           |||||           |||||           |||||     
     |||||||         |||||||         |||||||         |||||||    
    |||||||||       |||||||||       |||||||||       |||||||||   
   |||||||||||     |||||||||||     |||||||||||     |||||||||||  
  |||||||||||||   |||||||||||||   |||||||||||||   ||||||||||||| 
  |||||||||||||   |||||||||||||   |||||||||||||   ||||||||||||| 
 ||||||||||||||| ||||||||||||||| ||||||||||||||| |||||||||||||||
----------------------------------------------------------------

/*
file: spectrum1
(revised: 03/01/12, orig 03/01/12).

Simple bargraph-type spectrum display.
*********************************************/

#define WAVSZ  64
int wave[WAVSZ];

/********************************************/
void setup() 
{
  Serial.begin(57600);
  build_sine(2, wave, WAVSZ);
  display_wave(wave, WAVSZ, -1);
  // 
  newline();
  display_wave(wave, WAVSZ, 0);
}

/********************************************/
void loop()
{
}

/*
  freq in #cycles.
**********************************************/
void build_sine(int freq, int ary[], int siz)
{
  for( int i=0; i<siz; i++) {
    if( (i % 16) == 0) newline();
    ary[i] = (int)(256 * sin(freq * (float)i * 6.28 / (float)siz));
    Serial.print( ary[i] );
    spc();
  }
}

/*
  display integer ary[] of siz values.
  - show 64 cells max.
  - posneg = 1, display positive values only.
    posneg = 0, full-wave rectify data.
    posneg = -1, display in bipolar format.
*******************************************************/
void display_wave(int ary[], int siz, int posneg)
{
  #define BARMAX    8   // maximum bar height. 
  #define DISPMAX  128  // maximum display size.
  int dat[DISPMAX];
  int i, j, x, max=0;
  int dispsz=DISPMAX;
  
  // set max# display cells.
  if( siz < DISPMAX ) dispsz = siz;
  
  // find maximum, and normalize data.
  for( i=0; i<siz; i++) {
    x = ary[i];
    if( x < 0 ) x = -x;
    if( x > max) max = x;
  }
  // normalize data to +/-BARMAX.
  for( i=0; i<siz; i++) {
    x = ary[i];
    if( (posneg == 0) && (x < 0) ) x = -x;
    dat[i] = (int)( (long)x * (long)BARMAX / max);
  }
  newline();
  // display data >= 0.
  for( j=BARMAX; j > 0; j--) {
    for( i=0; i<dispsz; i++) {
      x = dat[i];
      if( x < j ) spc(); 
        else vbar();
    }
    newline();
  }
  disp_xaxis(siz);
  newline();
  if( posneg >= 0 ) return;
  
  // display data < 0.
  for( j=0; j > -BARMAX; j--) {
    for( i=0; i<dispsz; i++) {
      x = dat[i];
      if( x < j ) vbar(); 
        else spc();
    }
    newline();
  } 
}

/*
  display horizontal axis.
******************************/
void disp_xaxis(int siz)
{
  for( int i=0; i<siz; i++) Serial.write("-");  
}

/*
  print utilities: vertical bar, space, newline.
***************************************************/
void vbar()    {  Serial.write('|');  }
void spc()     {  Serial.write(' ');  }
void newline() {  Serial.print("\n"); }

That's very cool! Well done. :)

On the bright side, think of how much you learned by doing it yourself.

And thanks for sharing the code.

@Nick Gammon, yeah i converted the print to DEC and it works great now. I was getting values over 12 in the monitor, but i think i can manage getting that to work correctly. here is the code:

/*
FFT for printing 12 values mapped to 12
based on FFT library and code from the Arduino forums
*/

#include <fix_fft.h>

#define AUDIOPIN 5


char im[128], data[128];

char data_avgs[12];

int i = 0, val;



void setup() {
Serial.begin(9600);
}


void loop() {

for (i=0; i < 128; i++){
  val = analogRead(AUDIOPIN);
  data[i] = val;
  im[i] = 0;
}

fix_fft(data,im,7,0);

for (i = 0; i < 64;i++){
data[i] = sqrt(data[i] * data[i] + im[i] * im[i]); // this gets the 
//absolute value of the values in the array, so we're only dealing 
//with positive numbers
}


// average bars together
 for (i=0; i<12; i++) {
 data_avgs[i] = data[i*5] + data[i*5 + 1] + data[i*5 + 2] + data[i*5 + 3] + data[i*5 + 4]; // average together, for 12 sets of 5
 data_avgs[i] = map(data_avgs[i], 0, 37, 0, 12); // remap values for my application, so 12
 }
// print out the averaged bars
    for (i=0; i<12; i++)
        {
        Serial.print(data_avgs[i], DEC);
        if (i < 11) 
            Serial.print(", ");  // Separate values for easy reading
        else
            Serial.println(".");   // End of line
        }
}

You guys are the best, thanks so much. :slight_smile: :slight_smile: :slight_smile: :slight_smile: :slight_smile:

One more quick question, the intensity of the numbers in the moniter changed intensity depending on what volume the music was being played. Is there a simple hardware or software solution for this?? Thanks.

Is there a simple hardware or software solution for this??

There is a very simple solution. Quit changing the volume.

You could always find the minimum value in the array, and subtract that value from all the other positions. However, if you are trying to make a VU meter, the values are supposed to change as volume does.