Range of FHT Outputs

Hi all,

I'm using the FHT library from Open Music Labs to analyze the spectrum from a MAX9814 mic amp from Adafruit. I've adapted the example code to use the included octave function:

#define OCTAVE 1 // use the octave 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 -= 245; // used to be 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_octave(); // take the output of the fht
    sei();
    Serial.println("start");
    for (int i = 0; i < LOG_N; i++) {
      Serial.println(fht_oct_out[i]);
    }
    delay(1000);
  }
}

The output is responsive to sounds played on my phone (both "pure tones" and music). The problem is that all eight bins output ~70 at typical noise levels and only reach up to ~140 even when I bring my phone's speaker right next to the mic. Is it possible to give myself a greater range of values here? I'm worried that the automatic gain of the mic might make this quite hard, but I'm not sure that it would. Thanks!

austinsid:
The output is responsive to sounds played on my phone (both "pure tones" and music). The problem is that all eight bins output ~70 at typical noise levels and only reach up to ~140 even when I bring my phone's speaker right next to the mic. Is it possible to give myself a greater range of values here? I'm worried that the automatic gain of the mic might make this quite hard, but I'm not sure that it would. Thanks!

I've had a quick look at the FHT library and it says:

fht_mag_octave() - This outputs the RMS value of the bins in an octave (doubling of frequencies) format. This is more useful in some ways, as it is closer to how humans perceive sound. It doesn't take any variables, and doesn't return any variables. The input is taken from fht_output[] and returned in fht_oct_out[]. The data is represented as an 8b value of 16*log2(sqrt(mag)).

So the numbers you see in fht_oct_out[] are not 'linear volume', a value of 140 is a LOT louder than 70, not simply twice as loud.

The mic's automatic gain will of course ramp up when there's no sound, but when it's adjusted to the nearby fairly loud tone from your phone I guess you will have to be very loud a few feet away to make much difference.

Try playing a sound on your phone with the mic. an inch or two away from it and see what numbers you get. Then stand back and talk quite loud... how much does the level change ?

Yours,
TonyWilk

Right. So if I wanted to get values, say, from ~30 to ~200 for the same range in volume I would have to adjust the library? That seems daunting (something I would love to look into, but this project is due this Wednesday).

The automatic gain does work quite well. Watching the output on an oscilloscope, it did seem to adjust to about the same level when speaking far away vs playing music relatively close, but I don't have things ready to quantify it now. I guess I was just worried that automatic gain might make volume differences mean a little less than they should? I think my worry was misplaced though. The automatic gain probably couldn't hurt anything unless average volume levels change drastically.

austinsid:
Right. So if I wanted to get values, say, from ~30 to ~200 for the same range in volume I would have to adjust the library? That seems daunting (something I would love to look into, but this project is due this Wednesday).

No, you just have to scale the values you get in fht_oct_out.

Assuming you are not interested in anything below 70 and you measured 140 right up to your 'phone speaker you just need to scale up that range, something like:

// *** only example code this... *** 
//
value= fht_oct_out[ some_index ];
int adjustedValue;

// calc adjustedValue to have range 30 - 200 (30+170) from value range 70 - 140 (70+70)
//
if( value <= 70 ){
  adjustedValue= 30;
 } else {
  adjustedValue= int( 30 + (((float)value - 70.0) * (170.0/70.0))  );    
}

Yours,
TonyWilk

I don't think it's quite worth it to do it that way, at least for what I want to use the values for. Since this would just "spread out" the ~70 possible volume levels from fht_oct_out to a larger range, I'm not actually getting more information about the relative amplitudes of the frequency bins.

By the way, thank you for the help! You're certainly helping me narrow down my question, if nothing else.

austinsid:
I don't think it's quite worth it to do it that way, at least for what I want to use the values for. Since this would just "spread out" the ~70 possible volume levels from fht_oct_out to a larger range, I'm not actually getting more information about the relative amplitudes of the frequency bins.

Sorry for that being a bit simplistic, but this FFT doesn't really have any more useful resolution to give, even if you processed the output of the fft yourself.

from the ArduinoFHT page under Implementation Details : (my highlight)

This 32b version is not as precise as a true square root library, but it only takes around 40 clock cycles, compared to 500 for a true square root. This lookup table version only gives an accurate first 8b on the return value, but for the purposes of this FFT, that is good enough. The total bit depth of the FFT is not much past 12b since it is implemented in fixed point (each value must be divided by 2 before adding to prevent overflow - this gives an eventual divide by 256 for a 256 point FFT). The relative accuracy is a function of output value size. For a return value of 8b, it is as close as you can get. For a 9b value, its lsb might be wrong. for a 10b value, 2 lsbs might be wrong, and so on. So the worst case scenario is a 16b return value where you get +/-0.5% accuracy.

and the output in fht_oct_out[] are 8bit values.

Even if you had 'more information about the relative amplitudes of the frequency bins', in practice I don't think it would help with all the other variables there are in measuring sound; background noise, auto-gain on the mic, now close it the source and so on.

What do you get printed out when playing a simple tone and slowly move the mic. away ?

Yours,
TonyWilk

Playing a simple tone and slowly moving the mic away gives me just about what I should be getting: the bin containing that frequency goes from ~160 (I've changed some things around and am getting higher values now) to ~110, while others usually remain below 80.

I seem to be getting a lot of noise in the lower two bins. The lowest goes down to 0 occasionally then jumps right back up to ~70. Also it seems that the DC offset seems to be drifting occasionally? Sometimes I have to average analogReads in order to change that k offset in the beginning of the loop. I'm wondering if I can just automate this averaging before fht_window()?

Here are some outputs at noise levels. The drop-off here seems to be caused by the DC offset - or maybe just by resonance of the room I'm in?

start
49
51
35
33
30
29
24
22
start
79
55
30
33
30
30
22
24
start
75
45
45
38
32
25
24
21
start
70
48
31
37
28
33
27
22
start
69
27
40
34
31
29
24
22
start
81
64
35
33
29
29
25
24
start
63
54
25
30
35
27
24
25

One more question: I've set the maximum gain to 40 dB and the A:R ratio to 1:500. The gain setting seemed to keep things least noisy; I'm also hoping to direct the microphone at sources of music, so I figure that if the mic is near the speaker then it doesn't matter too much what the max gain is. The attack:release ratio I set to 1:500 since I would like the speaker's gain to react quickly to changes in music. These together seemed to help my outputs stay relatively stable and accurate. Is there any reason to adjust them?

austinsid:
Playing a simple tone and slowly moving the mic away gives me just about what I should be getting: the bin containing that frequency goes from ~160 (I've changed some things around and am getting higher values now) to ~110, while others usually remain below 80.

That sounds ok, there's a reasonable margin there.

I seem to be getting a lot of noise in the lower two bins. The lowest goes down to 0 occasionally then jumps right back up to ~70. Also it seems that the DC offset seems to be drifting occasionally? Sometimes I have to average analogReads in order to change that k offset in the beginning of the loop. I'm wondering if I can just automate this averaging before fht_window()?

you mean the 'k offset' in this bit of code?

int k = (j << 8) | m; // form into an int
      if( k > 511 ){
          k= 511;       // add this to make sure k<<=6 doesn't make rubbish
      }
      k -= 245; // used to be 0x0200, form into a signed int
      k <<= 6; // form into a 16b signed int

The analog should give you values 0..1023 over 5v (?), the mic says 2Vpp max, mid is 1.25V, so you should see values for 0.25V to 2.25V or ~51 to ~460 with the midpoint at ~255. So your 'k offset' (245 above) seems fine.
I'd bound the value tho, so you are sure noise spikes are not translated into funny values (see above)

Here are some outputs at noise levels. The drop-off here seems to be caused by the DC offset - or maybe just by resonance of the room I'm in?

Maybe, the bottom bins of an FFT (I'm assuming the same for this FHT) always seem to have higher value rubbish in 'em.

One more question: I've set the maximum gain to 40 dB and the A:R ratio to 1:500. The gain setting seemed to keep things least noisy; I'm also hoping to direct the microphone at sources of music, so I figure that if the mic is near the speaker then it doesn't matter too much what the max gain is. The attack:release ratio I set to 1:500 since I would like the speaker's gain to react quickly to changes in music. These together seemed to help my outputs stay relatively stable and accurate. Is there any reason to adjust them?

Sounds ok to me. :slight_smile:

Yours,
TonyWilk

P.S. Just thought... if you meant 'source of live music', that's a LOT louder than you might realise so you'd need to knock the gain down quite a bit.

Ignore anything else I've said, everything is working perfectly!... except for the huge variance in the lower two bins (especially the lowest). Why do these jump around so much? I especially don't understand how the FHT is ever calculating them to be 0, but it is, and quite often.

Unless you have a good low pass filter with a cutoff of 4.8 kHz (1/2 the ADC sampling frequency) between the amplifier and the ADC input, the output of the FHT or FFT is more or less meaningless due to aliasing.

The values in bin 0 reflect mostly the DC offset of the microphone preamplifier, 1.25 V.

Um. The values I'm getting certainly don't seem "more or less meaningless".

The sampling frequency in free-running mode is 39.4 kHz (and I'm quite interested in frequencies up to 19.2 kHz) so a 4.8 kHz low pass filter wouldn't be much help for my project. I don't mind aliasing - I'm just looking for relative frequency amplitudes, nothing exact.

The DC offset is already managed by setting the k offset to the average of many analogRead calls. A DC offset doesn't explain low values in bin 0; it would explain large ones, but that's taken care of.

I don't mind aliasing

Yes you do. It is false signals all over the band rendering your measurements meaningless.

Small amounts of aliasing affect almost nothing when the mic is right next to a speaker producing loud sounds entirely within half of the sampling rate. I don't care if the bins are even 10 higher than they "should be". I'm just looking at the relative amplitudes of the bins.

Sorry, I did not notice that you were sampling at higher than the default rate of 9.6 kHz for analogRead().

Regardless, for a meaningful result, it is essential to prevent any frequencies above 1/2 the sampling frequency from reaching the ADC, as their amplitudes will simply show up elsewhere in the spectrum.

mic is right next to a speaker producing loud sounds entirely within half of the sampling rate.

You haven't heard about harmonics, evidently. Good luck on your journey.

Look, if the values that I'm getting match what I'm expecting then I'm happy. And they do. When I play music with high frequencies, the high frequency bins go up in value and when I play music with low frequencies the low frequency bins go up in value.

I don't care much whether that's good enough for you.

What I'm confused about is why no one has bothered to try to answer the question I did ask, rather than bring up irrelevant information. Harmonics above 19.2kHz aren't loud, which is why I don't care about them.

Aliasing won't cause frequency bins to be lower than they should be; my question regards bins that occasionally dip far below what they should be.

austinsid:
Look, if the values that I'm getting match what I'm expecting then I'm happy. And they do. snip

Ok, they do have a point tho', ideally you should filter before ADC.

However, ideally you should pre-filter, 16-bit sample at 44KHz into a moving window buffer and and do a full 8 or 16k FFT on it.

But you can't do that with the hardware you have.

So, you have a small processor doing a cut-down approximation to a FHT on raw ADC values... and it seems to work just enough for what you want.

You did ask about values 'jumping around' - well, you have to put up with some odd things happening when the input data is not 'ideal' and the math is approximated. To get any better results, you would have to upgrade the audio and processor hardware and run better FFT software.

Yours,
TonyWilk

P.S. It might be interesting to look at the effect of aliasing on your bin values if you play a tone which is a bit above your ADC sampling frequency

austinsid:
Look, if the values that I'm getting match what I'm expecting then I'm happy. And they do.

I thought the whole point of this thread was that you were not happy with them. You said:-

The problem is that all eight bins output ~70 at typical noise levels and only reach up to ~140

We are telling you one of the reasons why this is. You however are dismissing this information because you don't like it. If you knew enough to dismiss it you would know enough not to ask the question in the first place.

It constantly amazes me that people come on here not really searching for understanding but a confirmation of their erroneous ideas. Then when they are told they are wrong they get all huffy. Time you started honing your people skills. And before you say the same for me, I am not asking for anyone's free help.

Then when they are told they are wrong they get all huffy.

It seems that some posters want to come across as more sophisticated or skilled than they actually are.

This rarely works, and when their obvious errors are pointed out, they become embarrassed and angry.

Lol. This'll be the last time I come here for help.

austinsid:
Ignore anything else I've said, everything is working perfectly!... except for the huge variance in the lower two bins (especially the lowest). Why do these jump around so much? I especially don't understand how the FHT is ever calculating them to be 0, but it is, and quite often.

I'm "huffy" because you didn't at all address the question I had at the time. Instead, y'all spout outright misinformation (the data is meaningful despite the lack of a filter; just not as perfect as y'all want it to be) that's not actually at all relevant to my question. Should I just have ignored your responses? Sure. But I was hoping that explaining my values as regard this project would have you drop it. Instead, y'all make veiled insults. Thanks.

austinsid:
Lol. This'll be the last time I come here for help.

I'm "huffy" because you didn't at all address the question I had at the time. Instead,
snip

Yeh, I've noticed that. Seems to be a common problem with 'social media' these days.

Just turn up the "Bull**** filter" in your brain :slight_smile:

Still, it is useful to get all sorts of views on what you are trying to do - occasionally it trips the odd neuron and helps you figure out a good solution.

Yours,
TonyWilk