0

I want to use the audio from my PC and have it going to the speakers and Arduino for some lighting effects. This project is a bit of a struggle because I don't know what the best way of analysing audio is. So for the lights I only want bass frequencies to affect the lights.

3
  • 2
    You need an FFT for that - and an integer based one, too. And an Arduino that can store enough audio samples to make it meaningful. And ideally a faster system to process it quicker. Basically, while an Arduino could do it, it would struggle and not be wonderful. Better to use an ARM or PIC32 based board with more memory and a fast 32-bit CPU. An FPU as well would help, then you could do it with floats instead of ints (though ints are fine). Even better would be a chip with a DSP in it (such as a dsPIC33), but then you're straying way outside the bounds of Arduino... – Majenko Feb 8 '17 at 13:39
  • While i dont really understand what you mean with the arduino not being able to store enough sample i could try and use my raspberry pi (i think i remember it being something with ARM). – Jean Feb 8 '17 at 15:28
  • I usually use at least 1024 samples (though you can use less, but you get less FFT buckets). At 16 bits per sample (actually 10, but you there aren't 10 bit data types) that's the entire memory of the Uno. For simple LED control you could get away with maybe 128 samples and sample at a lower frequency, since you aren't interested in the higher frequency range, and it then becomes viable. The fewer samples you have the less frequency resolution you get, but the less memory it uses and the faster it processes. – Majenko Feb 8 '17 at 15:41
1

I have actually just finished a project doing exactly that. I used a frequency analyser chip called an MSGEQ7 (Available from Sparksfun), here's the datasheet.

https://www.sparkfun.com/datasheets/Components/General/MSGEQ7.pdf

These little things are really easy to use. Give me a shout if you would like any help with it such as coding or wiring.

1
  • Thanks alot i am currently loooking into it to see if its what i need :-) – Jean Feb 8 '17 at 15:32
0

There's many ways of achieving what you want - some in software, some in hardware.

Since you are only interested in the lower frequencies this opens up the possibility of a low pass filter - either a hardware one, or a software one. The simplest hardware one is just a resistor in series followed by a capacitor in parallel:

schematic

simulate this circuit – Schematic created using CircuitLab

(representative component values: increase capacitance and/or resistance to decrease frequency)

Doing it in software is just as simple: you sample at a regular frequency through whatever means are suitable (there's plenty of resources for sampling at a specific frequency on the Arduino - google them), then you average samples together to remove higher frequency components. The more you average the lower your cut-off frequency. Each time you average you halve the frequency range. Note that the frequency range is already half the sample frequency.

For instance: if you sample at 8kHz (say 512 samples) you get a 0-4kHz frequency range in your sample set. Averaging each pair of samples (which results in 256 samples) gives you a 0-2kHz frequency range. Averaging again, for 128 samples, gives you 0-1kHz. Average again, for 64 samples, gives you 0-500Hz. Again, 32 samples, 0-250Hz. Keep going, until you have your desired cut-off frequency.

Then you can scan the sample set for the maximum and minimum and take that as your peak amplitude for the sample set.

You have to make sure that your sample frequency is at least twice the highest frequency you are interested in (preferably more so that you can do some averaging), and that your sample set is large enough, at the chosen sample frequency, to contain at least one full cycle of the lowest frequency you are interested in. For instance, at 8kHz sample frequency, if you are interested in responding to frequencies down to 50Hz, you need to sample for at least (1/50) 20ms, which at 8kHz means (8000 samples per second times 0.02 seconds) 160 samples. Since it's usually better to work with powers of 2 that means 256 samples is the next biggest "slot" that would do.

If you want to access more information about the actual frequency content of your sample set so as to light different LEDs when different frequency components are in the signal you need a Fast Fourier Transform (FFT). This converts the sample set into a collection of buckets (exactly half the buckets as you have samples) each representing a portion of the frequency range of your sample set. For 256 samples that gives you 128 buckets, each representing 1/128 of your total frequency range. So at 8000 samples per second, or a 4kHz frequency range, each bucket gives you a 31.25Hz resolution.

The problem with FFT is it is computationally expensive. It also needs more memory than simple averaging.

Depending on the algorithm you use, you will probably find you need 16 bit samples, and each sample should be in the form of a "real" and "imaginary" component, amounting to 32 bits per sample. Most algorithms require powers-of-two for the number of samples. For 256 samples that means you are using 1024kB of memory just for sample storage and processing, which is half of what the Uno has. You certainly won't be able to go over 256 samples on an Uno (the next step up is 512, which would want to use the entire memory leaving no room for any other variables).

There are two basic forms of algorithms for FFT: floating point, and integer. The floating point variant is great on a CPU that has an FPU with it to do the number crunching, but on an Arduino that just isn't possible. So the floating point version would take a massive amount of time to process making your results incredibly jittery. Far from desirable. So you need to find the integer version (one example) which runs much faster.

Calculating an FFT is done in two parts: first it calculates the real and imaginary components from the sample set, and second it converts those two components into a single vector, the length of which is the amplitude of the bucket. Whether or not you are using the integer FFT algorithm that second phase is going to be slow. It involves a square root (it's basically pythagoras: sqrt((r*r) + (i*i));) and that is almost always slow. Getting that to work fast on an Arduino is not at all easy. You can sacrifice accuracy for speed though and write your own square root approximation routine (as I have done before for 3D vector manipulation in a graphics library):

float quick_sqrt(float number) {
    int32_t i;
    float x2, y2;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y2  = number;
    i  = * ( long * ) &y2;
    i  = 0x5f3759df - ( i >> 1 );
    y2  = * ( float * ) &i;
    y2  = y2 * ( threehalfs - ( x2 * y2 * y2 ) );

    return 1.0/y2;
}

That uses a few tricks to do with the way a floating point number is stored to create an approximation of the square root.

With all that you can see that the Arduino can do FFT, but it's not the best solution for FFT. Ideally you need a faster, more powerful, chip with more memory (personally I use a PIC32MZ "EF" variant: 200MHz, 512kB RAM, FPU).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.