I'm working on a spectrum analyzer and it works great, except for the fact that my bins are 150Hz in size, so the first three bins (I skipped the first bin, so 150-600Hz) combine all of the beat/bass frequencies, so my display isn't as impressive. The LEDs for these three bins don't even fluctuate with the beat that well, so I was hoping for some help diagnosing that.
Anyways, I was wondering how I could decrease the sampling rate using FFT.h such that my bins will be a much smaller size so that I will have more LEDs to display the lower bass frequencies.
Below is my code relevant to the FFT.
#define LIN_OUT 1 // use the log output function
#define FFT_N 256 // set to 256 point fft
#define DC_OFFSET 511
#include <FFT.h> //FFT library
void setup() {
delay(1000); //soft startup to ease flow of electrons
LEDS.addLeds<LED_TYPE, LED_DT, COLOR_ORDER>(leds, NUM_LEDS);
FastLED.setBrightness(100);
TIMSK0 = 0; // turn off timer0 for lower jitter - delay() and millis() killed
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 < 512 ; i += 2) { // 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 -= DC_OFFSET;
adc_val = k<<6;
fft_input[i] = adc_val; // put real data into even bins
fft_input[i + 1] = 0; // set odd bins to 0
}
// window data, then reorder, then run, then take output
fft_window(); // window the data for better frequency response
fft_reorder(); // reorder the data before doing the fft
fft_run(); // process the data in the fft
fft_mag_lin(); // take the output of the fft
sei(); // turn interrupts back on
fft_lin_out[1] -= 10; //adjust for values in bin when silent (unknown cause)
}
Do you have room to double the number of bins in your FFT?
Another choice might be to throw out every other sample when sampling the audio. I think that will cut your sample frequency in half, making each bin half as wide (75 hz). You could alternate between full sampling and half sampling, using full for upper frequencies and half for lower frequencies.
With this line you are calculating the amplitude of the transform, which is not simply related to audio volume.
fft_mag_lin(); // take the output of the fft
Change the above to
fft_mag_log();
to get audio volume bins. You may have to rescale the output for your display.
You can reduce the ADC sample frequency (38.4 kHz in the posted code), which will reduce the highest frequency bin to (sample frequency)/2, thus narrowing the bin width.
The next lowest sample rate requires you to change ADCSRA to
ADCSRA = 0xe6; // set the adc to free running mode, 19.2 kHz
However, you must make certain that no frequencies higher than (sample frequency)/2 =9.6 kHz are present in the input, or the resulting spectrum may become uninterpretable due to aliasing. With music input, you will need a good low pass filter on the input to accomplish that.
jremington:
With this line you are calculating the amplitude of the transform, which is not simply related to audio volume.
fft_mag_lin(); // take the output of the fft
Change the above to
fft_mag_log();
to get audio volume bins. You may have to rescale the output for your display.
What is the difference between amplitude and volume in the context of the spectrum analyzer? Does that make it easier for beat detection if I get audio volume bins?
In any case, I tested fft_mag_log, but there was a lot of noise present in the bins, so I'm not sure if I can use it (output data attached).
jremington:
You can reduce the ADC sample frequency (38.4 kHz in the posted code), which will reduce the highest frequency bin to (sample frequency)/2, thus narrowing the bin width.
The next lowest sample rate requires you to change ADCSRA to
ADCSRA = 0xe6; // set the adc to free running mode, 19.2 kHz
However, you must make certain that no frequencies higher than (sample frequency)/2 =9.6 kHz are present in the input, or the resulting spectrum may become uninterpretable due to aliasing. With music input, you will need a good low pass filter on the input to accomplish that.
Lowering the sampling rate would definitely help. However, I'm getting strange outputs. When I play a 1000Hz tone, the frequency output says 500Hz. This wasn't a problem before I halved the sampling rate. Also, some of the bins near the frequency bin read 0. This problem didn't show up with a sampling rate of 38.4kHz either. (output below)
Honestly, even with a sampling rate of 19.2kHz the bass beats only occupy the first 2-3 columns, so it's not really worth displaying, unless it is possible to have the bin size be non-constant (eg low frequency bin sizes are 10Hz, higher freq bin size 75Hz), or have small bin sizes but over a wide frequency range (eg all bin sizes 10Hz, but that would mean a range of 256Hz).
If there are any other ways to show more bass on the spectrum analyzer, I am very open to suggestions.
Human hearing has logarithmic response in volume, a signal with amplitude of 10X sounds "twice as loud". The log output scale allows you to visualize volume of signal rather than amplitude.
Does that make it easier for beat detection if I get audio volume bins?
Experiment to find out, but I would guess not. I think beat detection is not simple.
When I play a 1000Hz tone, the frequency output says 500Hz.
You forgot to change the code to reflect the new sample frequency.
If you need more bins, use a different FFT package, and a faster Arduino with more memory. The PJRC Teensy series comes to mind, but is more challenging to use.
jremington:
Human hearing has logarithmic response in volume, a signal with amplitude of 10X sounds "twice as loud". The log output scale allows you to visualize volume of signal rather than amplitude.
Assuming I can get rid of the large noise values in the log bins, what would this mean in terms of the display? D Do the heights of the columns increase logarithmically with the volume?
jremington:
No. When you use fft_mag_log() the heights of the columns are proportional to volume, which increases logarithmically with amplitude.
Ok, so it sounds like fft_mag_log() is the way to go. Do you know why there are such large noise values when silent? Perhaps that is why all of the FFT spectrum analyzer examples use fft_mag_lin().