Predominant frequency identification with FFT

Status
Not open for further replies.

yannbec

Member
HI,

All this is based on the FFT tutorial in the audio tutorial of Teensy with its audio shield.

I'm trying a simple code in order to identifie in real time the predominant frequency in a complexe sound. In a FOR loop, I compare each FFT value at each 43Hz to get at the end of the loop the highest value and then print it in the monitor.
So if I simply ask it to print the value each time it gets a new higher value while being in one step of the loop, the code works and I get in the monitor in each line all the highest frequencies in the sample with the highest one at the end of the line. So for each sample I will get 0 (initiale value in HZ) then 43 (first test) and then some other frequencies that the code founds in the FFT higher than the 43HZ value. I compare the value of the FFT but I print the frequency that correspond to the value.

code1.jpg

But I would like only to get the highest value of the sample and print it at the end of the FOR loop. So I tried the program below and it doesn't work (I've put in red my modifications). The monitor only gives "FFT : -" on each line as if the "c" value I test in the code always got to the initial value "0"... I don't understand why. Could someone help me please? :) thx!

#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>

// GUItool: begin automatically generated code
AudioInputI2S i2s2; //xy=280,253
AudioSynthWaveform waveform1; //xy=452,228
AudioMixer4 mixer1; //xy=459,307
AudioAnalyzeFFT1024 fft1024_1; //xy=658,324
AudioOutputI2S i2s1; //xy=659,254
AudioConnection patchCord1(i2s2, 0, mixer1, 0);
AudioConnection patchCord2(i2s2, 1, mixer1, 1);
AudioConnection patchCord3(waveform1, 0, i2s1, 0);
AudioConnection patchCord4(waveform1, 0, i2s1, 1);
AudioConnection patchCord5(mixer1, fft1024_1);
AudioControlSGTL5000 sgtl5000_1; //xy=321,382
// GUItool: end automatically generated code


void setup() {
Serial.begin(9600);
AudioMemory(10);
sgtl5000_1.enable();
sgtl5000_1.volume(0.5);
sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
sgtl5000_1.micGain(20);
mixer1.gain(0, 0.5);
mixer1.gain(1, 0.5);
mixer1.gain(2, 0.5);
waveform1.begin(0.5,440,WAVEFORM_PULSE);
delay(1000);
}


void loop() {
float c = 0 ;
float a = 0 ;

if (fft1024_1.available()) {

Serial.print("FFT: ");
for (int i=0; i<50; i++) { // 0-25 --> DC to 1.25 kHz
float n = fft1024_1.read(i);

if ( n > a ) {
a = n ;
c = i*43 ;

if (i=50){
printNumber(c);
waveform1.begin(0.5,c ,WAVEFORM_PULSE);
}

}
else{

}


}

Serial.println();
}
else{
waveform1.begin(0,440,WAVEFORM_PULSE);
}
}


void printNumber(float n) {

if (n >= 0.004) {
Serial.print(n, 3);
Serial.print(" ");
} else {
Serial.print(" - "); // don't print "0.00"
}

}
 
Your first problem is that this:
Code:
        if (i = 50) {
does not test if i equals 50, it sets i to 50. Use:
Code:
        if (i == 50) {
Second, your for loop index goes up to i<50 so i can never equal 50 inside the loop which makes this statement redundant.

Change all of this:
Code:
if (fft1024_1.available()) {

Serial.print("FFT: ");
for (int i=0; i<50; i++) { // 0-25 --> DC to 1.25 kHz
float n = fft1024_1.read(i);

if ( n > a ) {
a = n ;
c = i*43 ;

if (i=50){
printNumber(c);
waveform1.begin(0.5,c ,WAVEFORM_PULSE);
}

}
else{

}


}

Serial.println();
}
else{
waveform1.begin(0,440,WAVEFORM_PULSE);
}

to this:
Code:
  if (fft1024_1.available()) {
    for (int i = 0; i < 50; i++) { // 0-25 --> DC to 1.25 kHz
      float n = fft1024_1.read(i);
      if ( n > a ) {
        a = n ;
        c = i * 43 ;
      }
    }
    Serial.print("FFT: ");
    printNumber(c);
    Serial.println();
    waveform1.begin(0.5, c , WAVEFORM_PULSE);
  } else {
    waveform1.begin(0, 440, WAVEFORM_PULSE);
  }

Also note the indenting I've added which makes the code more readable. It is easy to indent your code using the IDE. Just select Tools|Auto Format (or you can just type Control-T).

Pete
 
Thank you so much the code now works except for the waveform generation that does pretty much nothing but some "pop" some times.

Any ideas? Thank you again Pete.
 
I think your problem is that you are calling waveform1.begin every time through the loop function which probably keeps restarting the waveform which can cause a click.
Remove this part of the code (note that the end brace '}' before the 'else' is not removed):
Code:
else {
    waveform1.begin(0, 440, WAVEFORM_PULSE);
  }
so that you only change the waveform if you detect a different frequency. In fact, it might be a good idea to add some code in the loop so that you remember the last value of 'c' that was used to begin a new waveform and only call begin again if the value of 'c' has changed. That should also reduce any clicks in the audio.

Pete
 
I'm trying a simple code in order to identifie in real time the predominant frequency in a complexe sound.

Usually the YIN algorithm works much better than FFT for this application. Use the NoteFrequency object. Look at File > Examples > Audio > Analysis > NoteFrequency.
 
Thank you it works now. Another problem was the Audiomemory was set to only 10. I changed it for 30 and now the sine generation works.
 
Paul, thank you the YIN algorithm is really accurate about the frequency ! but I lose the level information I got with the FFT. How could I mesure it so I can associate a frequency with a volume level while using the YIN ?
 
Status
Not open for further replies.
Back
Top