Go Down

Topic: Find fundamental frequency using FFT (Read 333 times) previous topic - next topic

louisch

May 18, 2017, 02:29 pm Last Edit: May 19, 2017, 12:23 am by louisch
Hello and excuse me for my english level in advance,

   I'm a French student and I want to find the fundamental frequency of a note played by a guitar in order to tune it. So after many hours of research, I've thought the fix_fft library was the more easy way to do that. So I've used this program :
Quote
char im[128];
char data[128];
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
int static i = 0;
  static long tt;
  int val;
 
   if (millis() > tt){
  if (i < 128){
    val = analogRead(A0);
    data = val / 4 - 128;
    im = 0;
    i++; 
   
  }
  else{
    //this could be done with the fix_fftr function without the im array.
    fix_fft(data,im,7,0);
    // I am only interessted in the absolute value of the transformation
    for (i=0; i< 64;i++){
       data = sqrt(data * data + im * im);
       
    }
    Serial.println(data[0]);
   
   
  }
   
    tt = millis();
   }
}
I've used Serial.println(data[0])" because I've understood it's here I'll find the frequency value, this program should work according to this topic (https://forum.arduino.cc/index.php?topic=299461.0) . The problem is that the monitor's screen scrolls and nothing is written. I use an adafruit microphone for arduino and I've amplified it, but amplified or not, the result is the same. The wiring is good, 9600 baud for the serial works for other programs, there's no error message so I really don't know what's wrong.

I sincerely hope someone will be able to help me
Thank you

Grumpy_Mike

Quote
I've used Serial.println(data[0])" because I've understood it's here I'll find the frequency value
No that is the DC component of the signal. You have to search the data bins to find the lowest one with a peak in it.
Finding the fundamental frequency of a guitar string is not easy, print out all the bins and see what you get.

louisch

Ok thank you for answering, I've tried to print every bin but it's always the same : there's nothing on the monitor but it's scrolling like the program returns nothing again and again

Grumpy_Mike

#3
May 18, 2017, 04:40 pm Last Edit: May 18, 2017, 04:45 pm by Grumpy_Mike
Quote
I've tried to print every bin but it's always the same
Did you change the code to something like this:-
Code: [Select]

  for (i=0; i< 64;i++){
       data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
       Serial.println(data[i]);
    }


Because I noticed you are not actually removing the data from the array in your original code.

Also as the data type is only a byte would you not be better doing:-
Code: [Select]

  for (i=0; i< 64;i++){
       abs_data[i] = sqrt(data[i] * data[i] + im[i] * im[i]);
       Serial.println(abs_data[i]);
    }


where you have declared an array abs_data as a float array.

DVDdoug

I've never used the FFT library but you can run the Analog Read Serial to see if you are getting a signal.

Quote
I use an adafruit microphone for arduino and I've amplified it, but amplified or not, the result is the same.
I assume that's a "microphone board" with a preamp built-in.   Usually, these things have a preamp with a biased output (because the Arduino can't read the negative-half of a normal audio signal.)  

If that's all working, Analog Read Serial should read about 512 with no signal.   When a signal is present the readings should jump-around "randomly" with quiet signals giving you readings near 512 and very loud signals jumping around between zero and 1023.

It that works as expected we know the hardware is working and you've got a software problem.

This has been done before (or at least attempted before ;) ) so try searching the forum for "guitar tuner".

louisch

@ Grumpy_Mike   I did what you tell me, nothing has changed but I noticed when there's no signal that the program returns > for data[0] and ) for data[1]. I don't know if this could help...

@Doug there's no problem with my microphone and my wiring, I tested it

el_supremo

Looks like it's printing the values as characters. Cast it to an int. See if that helps:
Code: [Select]
       Serial.println((int)abs_data[i]);

Pete
Don't send me technical questions via Private Message.

louisch

@el_supremo I think you got it. I join you a screenshot of the monitor (each line is a new loop). It looks like an expected result.

If it is, thanks a million for your help and your rapidity everyone.

Just an other question : how could I know which frequency are sampled and can I adjust this ( for example if I only want 0-400 Hz wich parameter should I change)?

el_supremo

There are a couple of things wrong with your code.
You declare 'i' to be global and initialize it to zero but you never reset it to zero after that.
Worse yet, you also use 'i' in the for loop. This will leave it set to 64 when the loop is done so that you will store subsequent samples only in the last 64 elements of the array.
I can't tell if it is a formatting error due to using quote instead of code tags but you don't appear to be storing anything in the array.
Change all of this:
Code: [Select]
     data = val / 4 - 128;
      im = 0;
      i++;
    }
    else {
      //this could be done with the fix_fftr function without the im array.
      fix_fft(data, im, 7, 0);
      // I am only interessted in the absolute value of the transformation
      for (i = 0; i < 64; i++) {
        data = sqrt(data * data + im * im);
      }
      Serial.println(data[0]);
    }


to this:
Code: [Select]
     data[i] = val / 4 - 128;
      im[i] = 0;
      i++;
    }
    else {
      // reset the array index
      i = 0;
      //this could be done with the fix_fftr function without the im array.
      fix_fft(data, im, 7, 0);
      // I am only interested in the absolute value of the transformation
      for (int j = 0; j < 64; j++) {
        int mag = sqrt(data[j] * data[j] + im[j] * im[j]);
        Serial.print(mag);
        Serial.print(" ");
      }
      Serial.println("");
    }


Please use code tags (use the </> icon) instead of quotes.

Pete
Don't send me technical questions via Private Message.

louisch

#9
May 18, 2017, 09:20 pm Last Edit: May 18, 2017, 09:26 pm by louisch
Ok I applied changes but it's exactly the same. Do you think results are bad? Because values remains constant and near to 0 until I play a chord and I can see several values peak, so for me it looked like an good result

And yes, quote has erased ,[i, from my program
 

Grumpy_Mike

Can you tell us what sort of Arduino you are using? That will help to determine the sample rate and thus what frequencies the bins correspond to.

Quote
but it's exactly the same. Do you think results are bad?
Not so much the results as the code. I can't see how the code you originally posted manages to store the samples in successive bytes of the data buffer. When you do put them in successive bytes and you say there is no difference leads me to think there is something more fundamentally wrong that you think.  

louisch

@ Mike I'm using an Arduino Uno.
            Maybe I'm stupid but in the for loop don't we initialize i to 0 again? Maybe that's why there's no change when I add the "i=0 " line

Grumpy_Mike

Quote
for loop don't we initialize i to 0 again?
Yes but when it exits the loop it will be at 64, so that is where it will start from when it does a second load of acquisition of samples.
Quote
Maybe that's why there's no change when I add the "i=0 " line
No, you have something very wrong.

Quote
I'm using an Arduino Uno
So basically your sample rate is 10KHz.

el_supremo

#13
May 18, 2017, 10:04 pm Last Edit: May 18, 2017, 10:05 pm by el_supremo
Quote
in the for loop don't we initialize i to 0 again
No. That's why I changed the index of the for loop from 'i' to 'j' so that there's no confusion.

Please post the current version of your code (in code tags) and also post its output (please copy and paste the output as text, again in code tags).

Pete
Don't send me technical questions via Private Message.

louisch

Here you go:
Code: [Select]
#include "fix_fft.h"
char im[128];
char data[128];
void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
}

void loop() {
  // put your main code here, to run repeatedly:
int static i = 0;
  static long tt;
  int val;
 
   if (millis() > tt){
  if (i < 128){
    val = analogRead(A0);
    data[i] = val / 4 - 128;
    im[i] = 0;
    i++; 
   
  }
  else{
    i=0;
    //this could be done with the fix_fftr function without the im array.
    fix_fft(data,im,7,0);
    // I am only interessted in the absolute value of the transformation
    for (int j=0; j< 64;j++){
       data[j] = sqrt(data[j] * data[j] + im[j] * im[j]);
       
       Serial.print((int)data[j]);
       Serial.print("  ");
       
    }
    Serial.print("\n\n");
    //do something with the data values 1..64 and ignore im
   
  }
   
    tt = millis();
   }
}


And it returns (top lines when I play a string,bottom when I do nothing):
Code: [Select]
4  1  2  2  1  2  1  2  0  1  1  2  2  5  5  1  1  5  14  13  1  4  2  3  1  1  1  0  0  0  1  1  0  1  1  1  1  2  4  5  1  2  6  2  1  1  1  1  1  1  0  0  0  1  1  11  3  4  4  3  1  1  0  2 

1  1  0  1  1  0  1  1  0  1  0  0  0  0  0  0  1  2  13  10  2  2  2  1  1  0  0  1  1  2  1  1  1  2  2  2  4  5  1  1  0  1  1  1  2  1  1  0  1  2  1  1  1  1  1  12  1  0  0  1  0  1  1  0 

0  1  1  1  1  1  1  1  1  2  0  1  0  0  0  0  1  1  12  9  2  2  1  1  1  1  1  1  1  2  1  1  1  2  2  1  4  5  2  1  1  1  0  1  0  1  0  1  0  1  1  0  1  1  1  11  1  1  1  1  1  1  0  0 

1  1  0  1  1  1  1  1  1  2  2  1  0  1  1  1  1  2  7  2  2  0  1  0  1  1  1  1  1  0  1  1  1  1  1  0  2  7  2  1  2  2  2  1  1  1  2  1  1  1  1  0  1  0  0  12  1  1  1  1  1  1  1  1 

1  1  1  2  1  2  2  2  1  2  0  1  1  0  1  2  2  2  15  5  2  1  0  1  0  1  0  0  1  1  0  1  1  1  2  2  5  11  2  3  1  0  1  0  1  1  2  1  0  1  1  1  2  1  2  15  1  1  1  2  1  1  1  0 

1  0  0  1  0  0  1  2  0  0  0  0  0  0  1  2  2  4  12  8  3  1  1  1  1  1  1  0  1  1  0  0  1  1  1  0  2  9  2  2  0  1  0  0  2  0  1  0  1  1  1  1  1  1  0  14  1  0  0  1  1  0  1  0 

1  1  1  1  1  1  1  1  1  1  1  2  0  1  0  0  0  2  12  5  3  1  1  2  1  1  1  2  0  1  2  1  1  1  1  2  7  10  3  2  1  1  2  1  1  2  1  1  0  1  1  1  1  1  2  20  1  2  1  1  1  1  1  1 

1  1  1  1  1  1  2  1  1  1  2  1  1  0  1  1  1  2  13  6  2  1  2  1  1  1  1  1  1  1  2  1  1  2  1  1  1  5  1  1  1  1  1  1  0  1  2  1  0  1  1  1  1  1  1  13  1  0  1  1  1  1  1  1 

1  2  2  2  1  2  2  1  2  3  3  2  3  7  3  2  3  3  3  2  2  1  0  1  1  0  1  1  1  1  1  1  1  1  1  0  0  1  2  1  1  1  1  1  2  0  2  1  1  0  2  1  2  2  2  4  3  4  3  1  1  0  1  1 

1  0  1  1  1  0  1  1  1  1  1  1  1  1  1  0  0  2  1  1  1  0  0  1  1  1  1  1  1  1  2  1  1  1  1  1  1  0  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  0  0  1  1  1  1  0  1  1  0  0 

1  1  1  1  1  0  1  1  0  1  1  1  1  0  0  1  1  1  1  1  1  0  1  1  0  0  1  1  1  1  1  1  1  1  1  1  1  0  1  1  0  1  1  1  1  0  0  1  1  1  1  1  1  0  1  1  0  0  1  1  1  1  1  0 

0  0  0  1  0  1  0  1  0  0  0  1  0  1  0  1  0  0  0  1  0  1  0  0  0  0  0  1  0  1  0  0  0  1  1  1  0  1  0  1  0  0  0  1  0  1  0  1  1  1  0  1  0  1  0  1  0  0  0  1  0  1  0  0 

1  1  0  0  0  1  1  1  1  0  1  0  1  1  0  1  1  1  0  0  0  1  0  0  1  0  1  0  1  1  1  1  1  1  1  0  0  1  1  1  1  0  1  0  1  1  0  2  1  1  0  0  0  1  0  0  1  0  0  0  1  1  1  0 

1  0  0  1  0  1  1  2  1  0  0  1  1  1  1  1  0  0  0  2  1  1  1  1  0  1  1  1  1  1  1  0  1  1  1  0  0  1  1  0  1  0  0  2  1  1  1  1  0  1  0  0  1  1  1  1  0  0  1  1  1  1  1  0 

0  1  1  1  0  1  1  1  0  1  1  1  1  1  1  2  0  1  1  1  0  1  0  1  1  1  1  1  1  0  1  1  0  2  1  0  0  1  1  1  0  1  1  1  1  1  1  1  0  1  1  1  0  1  0  1  1  1  0  1  1  1  1  1 

1  1  1  1  1  1  1  1  1  1  1  0  1  0  1  1  1  1  1  0  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  0  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  1  0  0 

0  1  0  1  1  1  1  1  0  0  0  1  1  1  1  1  0  1  0  1  1  1  1  1  1  0  0  1  1  1  0  1  0  1  0  1  1  1  1  1  0  0  0  1  1  1  1  1  0  0  0  1  1  1  1  1  1  0  0  1  1  1  1  1 

0  0  0  2  0  1  1  1  0  0  0  1  0  0  1  1  0  0  0  1  0  0  1  1  0  0  0  1  0  0  1  1  1  1  0  1  0  0  1  1  0  0  0  1  0  0  1  1  0  0  0  1  0  1  1  1  0  0  0  1  0  1  1  1 

1  1  1  1  1  2  0  1  0  1  2  2  0  2  1  0  1  2  2  1  2  0  0  1  0  1  1  1  1  0  2  0  1  1  1  0  1  0  1  1  0  0  1  1  1  2  0  1  1  1  1  0  1  1  1  1  0  1  1  1  1  0  0  1 

Go Up