Yet another FHT thread, the basics

Hi all,

I'm probably the 4x106th person asking about this but... I spent allmost the whole day trying to comprehend what the Open Music Labs FHT library does.

What am I trying to do ? I would like to make neopixel strip flash to music (am I the first to try this :slight_smile: ?).
I have an arduino mega, neopixel led strip and this microphone board from adafruit: Electret Microphone Amplifier - MAX9814

Now I'm looking at the example code over at open music labs: Programming example

Now I'm confused about these "frequency bins". I understand that at the end of the example code there should be an "array of bins" ?

  1. Which array would that be ? >> EDIT: Ok, that would be fht_input[]
  2. Does each of the values tell me something about the level at a certain frequency ?
  3. And if 2=yes, how to tell to which frequency (range?) the value refers ?

In the end I would like to narrow it down to say 5 frequency bands that my ledstrip will flash to.

EDIT: I'm now trying to understand what is explained here :http://wiki.openmusiclabs.com/wiki/FFTFunctions

Thanks in advance !

The FHT converts an array of amplitude values, sampled at regular intervals in time, to frequency values, in place.

Most people use one of the fht_mag() functions to convert the array into frequency magnitudes, which tells you the amount of a particular frequency component in the input.

If you want to make any sense out of the output, it is extremely important that no frequency higher than 1/2 the sample frequency is in the input. That usually means you need a good low pass filter on the input, something that the OpenMusicLabs author forgets to tell you.

For the Arduino, the default analogRead() sample frequency is 9.6 kHz, so that means no frequencies higher than 4.8 kHz in the input.

Finally, if you have 64 input bins, there are 64/2 = 32 frequency magnitude output bins. At the default sample frequency of 9.6 kHz, the output frequency bins are 4800Hz/32 = 150 Hz wide (0-149, 150-299, etc.).

OK, thnx for your answer.

But wait:

If you want to make any sense out of the output, it is extremely important that no frequency higher than 1/2 the sample frequency is in the input. That usually means you need a good low pass filter on the input, something that the OpenMusicLabs author forgets to tell you.

For the Arduino, the default analogRead() sample frequency is 9.6 kHz, so that means no frequencies higher than 4.8 kHz in the input.

I'm a bit confused, I see various sound/music reactive projects using fft/fht, but music contains frequencies above 4.8K right ? For example this track: https://www.youtube.com/watch?v=ti2FWCtSUC8

The "high beats" seem to be in the 14K range:

So that would mean these highs can not be detected ?

On the other hand you say, the default sample rate is 9.6 kHz
Is there an option to set it in another mode, might this be the following line in the example code then :

ADCSRA = 0xe5; // set the adc to free running mode

mrExplore:
But wait:I'm a bit confused, I see various sound/music reactive projects using fft/fht, but music contains frequencies above 4.8K right ? For example this track: https://www.youtube.com/watch?v=ti2FWCtSUC8

The "high beats" seem to be in the 14K range:

So that would mean these highs can not be detected ?

Correct.

mrExplore:
On the other hand you say, the default sample rate is 9.6 kHz
Is there an option to set it in another mode, might this be the following line in the example code then :

ADCSRA = 0xe5; // set the adc to free running mode

See §24.4 of the ATmega328P datasheet.

Pieter

So that would mean these highs can not be detected ?

Yes but it is worse than that, unless the input is filtered the "numbers" from those high beats are spread as "random" noise all over the other bins. This is called aliasing and messes up your analysis.

am I the first to try this

No.
I did a complete project of this in my book
Arduino Music

am I the first to try this

The following statement is an underestimate:

I'm probably the 4x10^6th person asking about this

hmm, ok... thanks for clearing that up guys !
Good to know that Higher frequencies on the input mess up all the readings.

Just another minor detail no one ever mention when showing off their cool projects :frowning:

I found this blogpost and it looked interesting.
I did get a bit hopeful reading this instructable. This guy claims that he can get the sampling rate up to 38.5kHz

I manually set the Arduino's internal analog to digital converter (ADC) counter to 500kHz and read an 8 bit value from analog input 0 from the ADCH directly (I just read the most significant 8 bits of the 10 bit ADC to save time in the code). I set the ADC counter to 500kHz because the ADC takes 13 clock cycles to read a new analog value. 500/13 =~ 38.5kHz which gets me pretty close to 40kHz (standard audio sampling rate) without introducing extra noise.

However:

Also, continuous monitoring of A0 means that the other analog pins are now useless

I would like to sample the whole musical spectrum ánd I need other analog inputs for potmeters, so this is not going to work.

Sow... what are my options here,

  • Buying an arduino zero ?
  • Using an external ADC ?

Or...

I have an arduino uno lying around, let's say I use the techniques described in the blogpost and link.
Then I would have :

  • an arduino uno doing the ADC with a sampling rate of > 40 kHz ánd doing the fft/fht
  • one arduino mega to do the light effects and connect potmeters to
  • then I would have the mega read the results from the uno in some way

Or...

Could I hook my microphone Electret Microphone Amplifier - MAX9814 up to the msgeq7 that I started out with ?

Again, in the end, the level of 8 frequency bands would be enough

This is getting quite complicated... for my level.

In fact, the following line in the FFT/FHT example code sets the input sample rate to 38.5 kHz, another fact that the OpenMusicLabs author forgot to mention:

ADCSRA = 0xe5; // set the adc to free running mode

So, with that setting you can process the entire conventional audio spectrum but you still need to make sure that no signals with frequencies higher than 19 kHz get to the input.

PS: Avoid Instructables, most of them are crap and some can even lead to destruction of your Arduino.

I would like to sample the whole musical spectrum ánd I need other analog inputs for potmeters, so this is not going to work.

What you do is take a batch of samples and process them then display it. While you are doing the processing and displaying you are missing a whole load of audio samples anyway. So you read the other analogue inputs before you do the processing. You can not, and do not need to continuously sample the audio, you have to miss some. Not even with the more powerful processors in the Arduino range, for just flashing lights their is simply no need.

You can do a continuous FFT but that requires a type if chip known as an FPGA Field-programmable gate array - Wikipedia

Thanks for you replies

jremington:
In fact, the following line in the FFT/FHT example code sets the input sample rate to 38.5 kHz
.....
So, with that setting you can process the entire conventional audio spectrum but you still need to make sure that no signals with frequencies higher than 19 kHz get to the input.

Ok, so this microphone I'm using Electret Microphone Amplifier - MAX9814 has a range of 20Hz-20kHz but signal between 19kHz and 20kHz would be problematic for the FHT. Is there a workaround for this ?

Grumpy_Mike:
What you do is take a batch of samples and process them then display it. While you are doing the processing and displaying you are missing a whole load of audio samples anyway. So you read the other analogue inputs before you do the processing. You can not, and do not need to continuously sample the audio, you have to miss some. Not even with the more powerful processors in the Arduino range, for just flashing lights their is simply no need.

You can do a continuous FFT but that requires a type if chip known as an FPGA Field-programmable gate array - Wikipedia

Ok, good point. I hope I understood correctly because I've been spending some time now to get this concept to work.

But first, i found this page Fast sampling from analog input This guys fiddles with the prescaler and interrupts.

When I run this code:

int numSamples=0;
long t, t0;

void setup()
{
  Serial.begin(115200);

  ADCSRA = 0;             // clear ADCSRA register
  ADCSRB = 0;             // clear ADCSRB register
  ADMUX |= (0 & 0x07);    // set A0 analog input pin
  ADMUX |= (1 << REFS0);  // set reference voltage
  ADMUX |= (1 << ADLAR);  // left align ADC value to 8 bits from ADCH register

  // sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
  // for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles
  //ADCSRA |= (1 << ADPS2) | (1 << ADPS0);    // 32 prescaler for 38.5 KHz
  ADCSRA |= (1 << ADPS2);                     // 16 prescaler for 76.9 KHz
  //ADCSRA |= (1 << ADPS1) | (1 << ADPS0);    // 8 prescaler for 153.8 KHz

  ADCSRA |= (1 << ADATE); // enable auto trigger
  ADCSRA |= (1 << ADIE);  // enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN);  // enable ADC
  ADCSRA |= (1 << ADSC);  // start ADC measurements
}

ISR(ADC_vect)
{
  byte x = ADCH;  // read 8 bit value from ADC
  numSamples++;
}
  
void loop()
{
  if (numSamples>=1000)
  {
    t = micros()-t0;  // calculate elapsed time

    Serial.print("Sampling frequency: ");
    Serial.print((float)1000000/t);
    Serial.println(" KHz");
    delay(2000);
    
    // restart
    t0 = micros();
    numSamples=0;
  }
}

This is the result on the serial monitor:

Sampling frequency: 76.88 KHz
Sampling frequency: 76.92 KHz
Sampling frequency: 76.88 KHz
Sampling frequency: 76.95 KHz
Sampling frequency: 76.88 KHz
Sampling frequency: 76.95 KHz
Sampling frequency: 76.88 KHz
Sampling frequency: 76.90 KHz

So if I understand correctly ISR(ADC_vect) is fired everytime the ADC has new data, at a frequency of ~77kHz ?

So my thought was/is:

  • I disable interrupts at the beginning of my code
  • When a reading from the FHT is needed, I enable interrupts so the ISR(ADC_vect) routine is fired.
  • In this routine 256 readings are taken and put into fht_input[]
  • While the reading is been done my main code should wait until 256 readings have been done
  • When 256 reading have been taken interrupts are disabled and the contents of fht_input[] can be processed by the main code

Might this work ?

Here's my code so far. I haven't gotten it to work like I want to but if I'm completely going in the wrong direction, please let me know

#define LOG_OUT 1 // use the log output function
#define FHT_N 256 // set to 256 point fht

#include <FHT.h> // include the library

bool bSampling;                                   //boolean that indicates an FHT sample is being taken
int intSampleNumber = 0;                          //keeps track of the number of samples that have been taken
long lngStartTime, lngEndTime, lngSampleTime;     //start, end and duration of sampling time to determine frequency


void setup()
{
  Serial.begin(115200);

  //Disable interrupts until a reading is needed
  cli();

  ADCSRA = 0;             // clear ADCSRA register
  ADCSRB = 0;             // clear ADCSRB register
  ADMUX |= (0 & 0x07);    // set A0 analog input pin
  ADMUX |= (1 << REFS0);  // set reference voltage
  ADMUX |= (1 << ADLAR);  // left align ADC value to 8 bits from ADCH register

  // sampling rate is [ADC clock] / [prescaler] / [conversion clock cycles]
  // for Arduino Uno ADC clock is 16 MHz and a conversion takes 13 clock cycles
  //ADCSRA |= (1 << ADPS2) | (1 << ADPS0);    // 32 prescaler for 38.5 KHz
  ADCSRA |= (1 << ADPS2);                     // 16 prescaler for 76.9 KHz
  //ADCSRA |= (1 << ADPS1) | (1 << ADPS0);    // 8 prescaler for 153.8 KHz

  ADCSRA |= (1 << ADATE); // enable auto trigger
  ADCSRA |= (1 << ADIE);  // enable interrupts when measurement complete
  ADCSRA |= (1 << ADEN);  // enable ADC
  ADCSRA |= (1 << ADSC);  // start ADC measurements
}


ISR(ADC_vect){
  
    byte m = ADCL;  // fetch adc data, first the low byte
    byte j = ADCH;  // then the high byte
    int k = (j << 8) | m; // form into an int
    k -= 0x0200; // form into a signed int
    k <<= 6; // form into a 16b signed int
    fht_input[intSampleNumber] = k; // put real data into bins
  
    intSampleNumber++;
  
    //if enough samples have been taken (FHT_N): set samplenumber back to zero 
    //and disable interrupts so ISR(ADC_vect) will not be called until needed
    if(intSampleNumber == FHT_N){
      
      intSampleNumber = 0;                  //reset intSampleNumber for the next reading
      
      bSampling = false;                    //set bSampling to false when sampling finishes
      Serial.println(bSampling);
      
    }
      
}

  
void loop()
{
    //A reading is needed:    
    lngStartTime = micros();                  //mark the time at which the sampling started, it starts right after enabling interrupts
    Serial.print("StartTime: ");
    Serial.println(lngStartTime);
    bSampling = true;                         
    sei();                                    //enable interrupts so an FHT reading is made bij the ISR(ADC_vect) interrupt
    
    while(bSampling == true){                         //Wait until the reading is finished, bSampling is set to false in the interrupt routine, when enough samples have been taken
      Serial.println("=======");
    }

    cli();                                //disable interrupts

    lngEndTime = micros();                //mark the time at which the sampling stopped
      Serial.print("  EndTime: ");
      Serial.println(lngEndTime);

    delay(500);

    fht_window(); // window the data for better frequency response
    fht_reorder(); // reorder the data before doing the fht
    fht_run(); // process the data in the fht
    
    //trying different magnitude functions
    //fht_mag_lin8();         
    //fht_mag_lin();
    fht_mag_log();       // take the output of the fht
    //fht_mag_octave();
//    for(int a = 0; a < FHT_N - 1; a++){
//      Serial.print(fht_input[a]);
//      Serial.print("   ");
//    }
//    Serial.println("<");

    
  
}

Is there a workaround for this ?

As mentioned in reply #1, you need a low pass filter between the microphone amplifier and the analog input.

Might this work ?

Yes. To test your code, replace the microphone and amplifier with a sine wave signal generator, or the ADC input routine with some lines that calculate a signal for which you know the answer.

Example:

/*
 fft_test_sine
 example sketch for testing the fft library.
 This generates a simple sine wave data set consisting
 of two frequences f1 and f2, transforms it, calculates 
 and prints the amplitude of the transform.
 */

// do #defines BEFORE #includes
#define LIN_OUT 1 // use the lin output function
#define FFT_N 64 // set to 64 point fft

#include <FFT.h> // include the library

void setup() {
  Serial.begin(9600); // output on the serial port
}

void loop() {
  int i,k;
  float f1=2.0,f2=5.0;  //the two input frequencies (bin values)
  for (i = 0 ; i < FFT_N ; i++) { // create samples
    // amplitudes are 1000 for f1 and 500 for f2
    k=1000*sin(2*PI*f1*i/FFT_N)+500.*sin(2*PI*f2*i/FFT_N);
    fft_input[2*i] = k; // put real data into even bins
    fft_input[2*i+1] = 0; // set odd bins to 0
  }
  
  fft_window();  //Try with and without this line, it smears

  fft_reorder(); // reorder the data before doing the fft
  fft_run(); // process the data using the fft
  fft_mag_lin(); // calculate the magnitude of the output

  // print the frequency index and amplitudes

  Serial.println("bin  amplitude");
  for (i=0; i<FFT_N/2; i++) {
    Serial.print(i);
    Serial.print("       ");
    Serial.println(2*fft_lin_out[i]); //*2 for "negative frequency" amplitude
  }
  Serial.println("Done");
  while(1); //wait here
}

So my thought was/is:

  • I disable interrupts at the beginning of my code

Sledge hammer to crack a nut and might lead to other problems, just disable the ADC interrupt flag in the ADC control registers when you want in the ISR. The interrupts will be automatically disabled when you send all the data out to the LEDs anyway, so playing with the global flag will be undone by that. By using the specific interrupt enable you want you have much better control.

Note as you play about with the prescalier the accuracy of the A/D suffers.

Thanks for the input :slight_smile:

I'll look into it

Ok I'm looking into this now:

Grumpy_Mike:
....just disable the ADC interrupt flag in the ADC control registers when you want in the ISR...

I am, again confused...sorry

Is this the way to set the interrupt flag ?
clear the bit / to zero : ADCSRA |= (0 << ADIF);
set the bit / to one : ADCSRA |= (1 << ADIF);

setting a bit means setting it to 1, clearing means setting it to 0 right ?

From the datasheet, page 319:

Bit 4 - ADIF: ADC Interrupt Flag
This bit is set ( to 1 ?) when an ADC conversion completes and the Data Registers are updated. The ADC Conversion Complete Interrupt is executed if the ADIE bit and the I-bit in SREG are set. ADIF is cleared ( to 0 ? ) by hardware when executing the corresponding interrupt handling vector. Alternatively, ADIF is cleared by writing a logical one to the flag.

"Alternatively, ADIF is cleared by writing a logical one to the flag"
So writing a 1 to the flag clears it ?
And writing a 0 sets it ?

Please read the data sheet more carefully.

To disable ADC interrupts, use

   ADCSRA &= ~(1<<ADIE);  //not ADIF

To enable ADC interrupts, use:

   ADCSRA |= (1<<ADIE);

The interrupt itself sets the ADIF flag. Counter intuitively, you clear the interrupt flag it by writing a 1 to it as follows:

   ADCSRA |= (1<<ADIF);

But you normally don't need to do that, as the ADC interrupt routine clears it automatically.

Man, this project is driving me nuts :S

Thanks for your input guys but tit's a bit over my head, I already spent too much time trying to get some results. At this point I'm going back to the drawing board, taking a simpler approach with the msgeq7.

This is an advanced project, requiring you to have some understanding of the ATMega hardware details.

On the other hand, the example code from OpenMusicLabs works properly without modification. You just have to understand what everything does.

I can't let go of this, I would like to understand it a bit and get it working...

jremington:
This is an advanced project, requiring you to have some understanding of the ATMega hardware details.

On the other hand, the example code from OpenMusicLabs works properly without modification. You just have to understand what everything does.

Well, I have some very basic understanding maybe, a little, just a bit :slight_smile:

But you say the example is working. I was indeed wondering why there would be a library, with examples and it didn't work. Anyway can I ask some questions about it ?

Let's take this example, fht_adc_serial.ino

Should this code actually be able to accept frequencies up to 20 kHz ?
That is the reason why the ADC is put into freerunning mode, isn't it ?

And I read somewhere that timing is critical when taking the samples, at a fixed frequency that is.
Is that garantueed in the for loop ( for (int i = 0 ; i < FHT_N ; i++) ) ?

Read on below the code...

/*
fht_adc_serial.pde
guest openmusiclabs.com 7.7.14
example sketch for testing the fht library.
it takes in data on ADC0 (Analog0) and processes them
with the fht. the data is sent out over the serial
port at 115.2kb.
*/

#define LOG_OUT 1 // use the log output function
#define FHT_N 256 // set to 256 point fht

#include <FHT.h> // include the library

void setup() {
  Serial.begin(115200); // use the serial port
  TIMSK0 = 0; // turn off timer0 for lower jitter
  ADCSRA = 0xe5; // set the adc to free running mode
  ADMUX = 0x40; // use adc0
  DIDR0 = 0x01; // turn off the digital input for adc0
}

void loop() {
  while(1) { // reduces jitter
    cli();  // UDRE interrupt slows this way down on arduino1.0
    for (int i = 0 ; i < FHT_N ; i++) { // save 256 samples
      while(!(ADCSRA & 0x10)); // wait for adc to be ready
      ADCSRA = 0xf5; // restart adc
      byte m = ADCL; // fetch adc data
      byte j = ADCH;
      int k = (j << 8) | m; // form into an int
      k -= 0x0200; // form into a signed int
      k <<= 6; // form into a 16b signed int
      fht_input[i] = k; // put real data into bins
    }
    fht_window(); // window the data for better frequency response
    fht_reorder(); // reorder the data before doing the fht
    fht_run(); // process the data in the fht
    fht_mag_log(); // take the output of the fht
    sei();
    Serial.println("start");
    for (byte i = 0 ; i < FHT_N/2 ; i++) {
      Serial.println(fht_log_out[i]); // send out the data
    }
  }
}

I ran this code with my adafruit microphone board connected. I changed the serial.print options to :

for (byte i = 0 ; i < FHT_N/2 ; i++) {
      Serial.print(fht_log_out[i]); // send out the data
      Serial.print("  ");
    }
    Serial.println();

So now I get the complete array one one line:

Yay, numbers :slight_smile: A lot of numbers...

What is the approach to see if these numbers actually represent the levels of the frequencies ?
I mean, the serial monitor is no good for that really. I have been thinking about combining bins, calculating the averages of bins over 10 milliseconds and sending them out over serial for display. I've been programming in flash/air to make a bar graph. Could you give me a hint on how to process this data before I start wasting more time on ideas that will never work ?

And another question, I tried Serial.print(micros()); but I only get 4 digits in the serial output, why ?

Thanks again for reading

Instead of sending the output to the serial monitor why not use the serial plotter, available under the Tools menu. That will give you a graph of the numbers you send. I think their are 500 samples on the window, so to make successive plots line up make sure you flush the display before you send the next set of data.

This video might help you:- A new feature in the Arduino IDE : a serial graph plotter - YouTube