Arduino project: Audio spectrum analyser

401_arduino_opener

If you’ve been following our Arduino masterclass for the last little bit, you’ll know we’ve been concentrating on analog-to-digital converters (ADCs). Without these little electronic circuit elements, there’s no grey, no shade, no colour – everything is black and white, one or zero. ADCs are the glue that joins the real analog world to the digital precision of the computing environment.

We’ve already had a look at ADC theory and last month, we put some of that theory into practice, building a stereo peak program meter (modern day VU meter). This month, we crank things up further, introducing a bunch of new components and some head-spinning maths as we build an audio spectrum analyser.

The space-time continuum

When I first saw Back to the Future, Doc’ Brown was cool, mostly because of his awesome electronics man-cave, but I digress. You know the basic time-travel story – that instead of seeing time as one-dimensional, you can travel through it, like scrubbing through an MP3 track to your favourite part on your iPad.

When an ADC grabs samples of an analog signal, it’s taking regular samples of that signal, capturing its amplitude over time. Do this in real time and you can display that amplitude, which is exactly what our stereo peak program meter did last month. If you line up those samples one after the other, you can see a record of that signal. Play those samples and you get your original signal back – exactly what any digital audio player does.

However, you’re looking at only a one-dimension view of that signal. Just like Back to the Future, there’s another way you can view those samples – and that’s by frequency.

Back in the early 1800s, French mathematician Joseph Fourier came up with an idea that you could break down any repeating mathematical function into a series of trigonometry equations (sine and cosine).

German maths freak Carl Gauss turned the idea into a mathematic transform around the same time, but it wasn’t until 1965 when Jim Cooley from IBM and John Tukey from Princeton University found it could also be applied to computers.

These guys figured that you could take a series of discrete samples of an analog signal and break it down into its frequency components using what’s now called the Fast Fourier Transform (FFT).

Now when it comes to audio, the most pure sound is a sinewave – it consists of just one tone or ‘frequency’. It has zero distortion, it is audio perfection. But Fourier’s idea is that every sound you hear, every song you listen to is just made up of a complex array of sine and cosine waves at varying frequencies and amplitudes. And the FFT is the tool you use to get those frequency and amplitude components.

This might sound a bit Back to the Future again, but the process is called converting the signal from the time domain into the frequency domain.

So Arduino can do maths?

Arduino is cool for moving robots, quadcopters and flashing lights on and off, but the modest 16MHz/8-bit Atmel ATMEGA328P microcontroller is also capable of some seriously nifty mathemagical gymnastics.

FFTs creates some pretty complex numbers, so complex that they have two components – a real and an imaginary component. If this maths talk is starting to make you break out into cold sweats, don’t worry, we’re almost done – suffice to say that in our application here, we don’t need the imaginary part; we just need the real part.

The FFT is reasonably quick but since the Arduino Uno is a little skinny on speed, we’re using a mathematical cousin called the Fast Hartley Transform (FHT), named after US electronics engineer Ralph Hartley, who invented the mathematical transform named in his honour during World War II.

But rather than having to code this complex mathematics ourselves, the team over at OpenMusicLabs have turned this FHT into an Arduino library and once we capture our audio signal samples, it’s this ArduinoFHT code that does the heavy lifting, turning those time-domain amplitude samples into discrete frequency-domain components.

ADC setupssa-circuit

Over the last couple of months, we’ve talked about the ATMEGA328P’s ADC setup and how it defaults to a sample rate of 9.6kHz through a division of the 16MHz clock, a 128-prescaler or divider and the 13 clock cycles it takes to capture one sample. From Nyquist’s Theorem, we know the effective bandwidth is half the sample rate, or 4.8kHz – and that‘s not great.

But by knocking down the 128-prescaler to 32, we now increase the sample rate by the same factor up to 38.4kHz. Thanks to Nyquist again, that gives us an audio bandwidth of 19.2kHz, which is close enough to 20kHz-perfection.

Remember back two months ago we looked at the ADCSRA (ADC Control and Status Register A) register and the last three bits that set this prescaler. By setting the register to hex number 0xf5 in our sketch, we set the prescaler to 32 and the 38.4kHz sample we’re after:

ADCSRA = 0xf5;

   if (sampleSet == “L”) {

     ADMUX = 0x45; // use adc5

   } else {

     ADMUX = 0x44; // use adc4

   }

The ADMUX controls which analog pin we’re sampling from – we take the left channel from analog pin A5 and the right-channel from pin A4.

What do we get?ssa-overlay

Combine all of this together and what we eventually end up with is an array of coefficients where each array element represents a frequency band or ‘bin’ and the coefficient in that bin is the amplitude of that frequency band. (An array is a series of variables assigned to one key or name through an index or address. Think of a block of units – each unit is an element that belongs to that block).

The width of the frequency band is calculated by dividing the sample rate by the number of steps in the FHT (usually set as a binary-compatible number). The more steps, the narrower the frequency band each bin covers and the greater the precision.

If your head hurts half as much as mine after reading those two paragraphs, here’s an example – if we’re covering a basic 20kHz signal bandwidth, setting the FHT size to 16 means we’re dividing that bandwidth into 16 equal-sized sections, each covering 1.25kHz.

The first bin covers 0 to 1.25kHz, the second 1.25 to 2.5kHz and so on. But if we set the size to 32, each bin now covers just 0.625kHz. Using 64 bins drops that to 312.5Hz and so on. The more steps, the greater the precision you can record the frequency spectrum.

In effect, we’ve got a visual equivalent of a graphic equaliser. The difference is that where we have linear frequency steps, a graphic equaliser typically works on an ‘octave’ scale, where each step might be one, a half or a third of an octave (an octave is a doubling of frequency, so A below middle-C on the piano is 440Hz; the next A up is 880Hz, the one after than 1760Hz and so on).

While the ArduinoFHT library has an octave bin step function, unfortunately, we couldn’t get it to work reliably, so we’ve stuck with the linear step pattern instead.

ssa-3How to display it

Now imagine that each bin is represented by a column of LEDs and the coefficient in each bin lights up a proportional number of those LEDs. You’ve now got yourself a very simple but functional audio spectrum analyser.

Rather than manually wire up a huge bunch of LEDs (yuk!), we decided to go for something a bit funky for this project and we’ve hauled in a huge 32 x 16 LED Dot Matrix Display (DMD) from Australian company Freetronics. The red version sells for $40 from Freetronics’ online or any Jaycar Electronics store.

What we love about this display is that you can drive it directly from an Arduino Uno R3 – and more importantly, it only needs a few digital I/O lines to do it. It uses the Arduino’s SPI (Serial Peripheral Interface) to control all 512 LEDs in the display and is the fastest interface the Arduino has.

The DMD is bright, even running just off the Arduino itself, but it has an external power input to really make it shine – but be warned, you need a 5VDC power supply capable of supplying up to 40-amps (we suggest an old PC PSU or something similar – check the DMD instruction page).

ssa-1Input to the ADC

Last month, we built our peak program meter on a sizeable breadboard. But although we still need the analog input circuit from that project here, we don’t need such a big board. So instead, we’re using a compact DIY protoboard shield that includes a tiny, weeny 170-tiepoint breadboard you glue to the shield board using the supplied double-sided tape.

The adapter cable for the DMD plugs into the shield, which goes into the Arduino.

As we mentioned last month, the ADC needs the analog signal moved or ‘biased’ to half the supply voltage to ensure it not only measures it correctly but so the analog voltage doesn’t damage the ADC. We have two sets of two 10kohm resistors providing a half-way voltage divider for the two analog input pins, A4 and A5.

The two capacitors provide DC-blocking (ensuring no DC voltage from the signal source enters the Arduino circuitry). The 1kohm resistor in series with each capacitor ensures that an audio signal with amplitude greater than 2.5VDC peak can’t seriously overload the Arduino ADC input and cause any damage.

Two modes

For added versatility, we’ve created two operating modes for our Spectrum Analyser – you can run it in mono (single-channel) mode where you use the full 32-wide LED array to display the frequency domain of the left-channel; or you can show two-channel stereo – left channel on the left, right on the right. In stereo mode, there is a two-column gap in the middle for delineation and you obviously get less detail since you only have half the columns.

To change modes, you edit a line of code in the Arduino sketch – you enter ‘MONO’ for single-channel or ‘STEREO’ for dual-channel display. The sketch code does the rest.

Give it a go

So there you have it – a spectrum analyser powered by a low-wattage 8-bit microcontroller.

We don’t claim this to be the world’s most accurate spectrum analyser, but it’s still a pretty fun way of combining ADC theory with music and lots of flashing lights. (Now if only we could just get a flux-capacitor in there…)

Where to get the code

Download the code from here. Copy the contents of the library folder into the libraries subfolder in your Arduino IDE folder and load the .ino sketch file into the IDE. We used version 1.0.5 to build the app. Note that there is some minor unavoidable display flicker as the SPI interacts with the ADC.

More on the ArduinoFHT library

This project wouldn’t be possible without the brilliant work by the team at OpenMusicLabs who created the ArduinoFHT library. To learn more about how it works (and we seriously recommend you do), check out the Wiki page at http://wiki.openmusiclabs.com/wiki/ArduinoFHT.

genGet an audio sweep function generator for $1.50

The most practical way to test an audio spectrum analyser is to run an audio signal through it. But ideally you want a continuously varying sinewave to ensure each bin and each LED column work as expected. This ‘continuously varying’ signal is called a frequency sweep and it’s a function you find on most decent (expensive) signal generators, test equipment you’d find in any well-equipped lab.

Chances are you probably don’t have one, but if you have an Android smartphone or tablet, how does $1.50 sound? Grab hold of Waveform Generator from Google Play and it turns your Android device into a brilliant signal generator that creates sine, square, triangle and sawtooth waveforms, generators white, pink and brown noise, creates AM (amplitude modulation) and FM (frequency modulation) signals and has an exceptional frequency sweep function.

If you want to dip your toe into electrical engineering, this is a must-have app!

 

 

  • Oxy

    HI, I am new with arduino. I have 1.6.0 arduino version and for some reason I am unable to compile the code. Error below:

    “Arduino: 1.6.0 (Windows 7), Board: “Arduino Uno”

    In file included from APC_16_bigASA_V1.ino:2:0:
    C:….librariesFHT/FHT.h:72:10: error: ‘prog_int16_t’ does not name a type
    PROGMEM prog_int16_t _cas_constants[] = {

    ^
    In file included from APC_16_bigASA_V1.ino:2:0:
    C:…..librariesFHT/FHT.h:87:12: error: ‘prog_uint8_t’ does not name a type
    PROGMEM prog_uint8_t _reorder_table[] = {

    ^
    In file included from APC_16_bigASA_V1.ino:2:0:
    C:…….librariesFHT/FHT.h:131:12: error: ‘prog_int16_t’ does not name a type
    PROGMEM prog_int16_t _window_func[] = {

    ^
    In file included from APC_16_bigASA_V1.ino:1:0:
    C:…….librariesDMD_master/SystemFont5x7.h:48:28: error: variable ‘System5x7’ must be const in order to be put into read-only section by means of ‘__attribute__((progmem))’
    static uint8_t System5x7[] PROGMEM = {

    ^
    C:……librariesDMD_master/Arial_black_16.h:48:33: error: variable ‘Arial_Black_16’ must be const in order to be put into read-only section by means of ‘__attribute__((progmem))’
    static uint8_t Arial_Black_16[] PROGMEM = {
    ^
    APC_16_bigASA_V1.ino: In function ‘void sampleFix()’:
    APC_16_bigASA_V1.ino:140:13: error: ‘fht_lin_out’ was not declared in this scope
    Error compiling.

    This report would have more information with
    “Show verbose output during compilation”
    enabled in File > Preferences.”

    • Roman Kelhar

      I got the same error. Did you solve your problem?

  • Hi Oxy,
    It was built with 1.0.5. The 1.6.0 version is brand-new and untested with this project.
    Cheers,
    Darren.

  • Roger Tinembart

    Hi Oxy and Roman

    I had the same problem. The solution was to use the newer version of the FHT library. Use the link in this how to under “More on the ArduinoFHT library

    “. From there you can download the “Library(2.0)” which is described as “Arduino FHT library (new)”. This fixes the PROGMEM problem.
    @Darren: Great work! Could use it as a start for my son’s project :-)

    • Thanks, Roger. Missed these comments.
      Cheers,
      Darren.

  • I am reading, re-reading, and will re-re-read. Thank you for the FFT to FHT history. You made it painless. The frizting looks easy enough to follow, and the DMD with interface to the Arduino looks affordable. Could you clarify the input at the 3.5mm jack ? Is this an audio feed (probably stating the obvious – sorry to be such a nube) and can I substitute an Arduino compatible sound sensor for the feed ? I am looking forward to sound sampling, in real time, from drum percussion snare, and the like.

  • Dmitry Artyomov

    Hi Darren, you are making cool projects! Awesome work. Building this one I got into trouble: The data I get after FHT is noisy on low frequencies, so on my 8×8 led screen the data is very noisy (even using the voltage divider giving 2.5 volts and without any input signal) I get the same result without that divider. What can you offer to investigate into?

    • Thanks, Dmitry (let the APC editor know! :)
      If you’re getting noise on the low end, check your power supply – it may be 50Hz/60Hz (whatever your AC mains frequency is) coming in on the supply rail. If your using your PC’s USB port to power it, try using a USB phone charger instead – the good ones are much cleaner (supply rail-wise). Failing that, I’d go a 470uF/16VW electrolytic cap across the supply rail (watch the polarity!) to see if that can’t clean things up a bit too.
      Cheers,
      Darren.

      • Dmitry Artyomov

        Thanks for the reply. But I have tried everything – applying capacitor to the GND-AREF pins, 5V-GND pins. Tried using power bank for powering the duino, using internal 1.1 V reference. Nothing helps, the noise keeps being so high (btw maybe it’s not noise and it’s something else). I’m trying to make the 3 channel light organ for RGB LED strip. So I’m using 128 prescaler to get less frequencies – 4.8 kHz top is suitable for me. Also I’m using 32 point FHT to get 300 Hz intervals.
        Can you please write your email or other contact to write to you, cause links here (wanted to share code) isn’t allowed

        • First, I’d just confirm that you can get the original code working. If you’re modifying the code, there’s not much I can do, given I don’t know what you’re doing with it. Other than that, all I can suggest is if the noise/whatever is happening at the low-end, test by setting your max frequency very low to see which frequency band it is and try to bypass it by ignoring that band.
          Other than that, I’m out of ideas.
          Cheers,
          Darren.

  • Янебот

    Hi Darren!cool project again! )
    I need some help for novice…
    I try to change some code to monitiring some sounds, up to ~3 kHz, but with

    not so big step between. How can I descrease up frequency, and step? Must I use fixed, proportional steps/devider only, or I can make my own custom grid, not linear to monitoring interesting peaks on given frequencies? I try to listen honey-bees for monitoring of bee-family mood and etc )

  • Янебот,
    Head to the link under the heading ‘More on the ArduinoFHT library’ in the above story (sorry, can’t post links). In short, you can’t make your own ‘steps’ or bins – at least not with this code. The Fast Hartley Transform (FHT) uses the following to set the size of each frequency step:
    F(k) = k x (sample_rate/FHT_N)
    Where F(k) is frequency bin, k is the kth step, sample_rate is the sample rate of the Arduino ADC and FHT_N is the total number of bins you set in the code (currently set to 128 in my code I think).
    That gives you two options for change – the sample rate of the Arduino ADC or the number of bins (FHT_N).
    If you’re only interested in say a bandwidth of 4kHz, set the sample rate to 8kHz, choose 256 bins and each bin measures a bandwidth of 8000/256 or 31.25Hz.
    That’s about the best I can suggest.
    Cheers,
    Darren.

    • Янебот

      tnx for yor comment! Looks like spectrum analyzer (that realisation) is not good idea for that job, and exists another right way to detect some custom frequencies in custom grid (about 6 with not linear steps) by some dsp features, like set of software filters? Like analog-resonance filters, but DSP software realised? Spectrum analyzer (serious) is good for lab-research and detect frequencies for watch by end device, learn to some interesting peaks and “datamining” but not like simple auto-detector device, imho. May be… is it possible to start some “instances” of that lib with custom settings, each for interesting peak +/- 1 little step? or every loop re-init lib? change min/max f by round?

    • Янебот

      idea – how to work glass-crash detector from sequrity systems space? looks like ideal acoustic detector as I need, but some custom filters, f, specialy for that noise. Heh, how to hack it for my purposes… ))

  • Gabriel Balici

    Hello

    What hardware and software changes to make it work with Adafruit RGB 16×32 matrix?

    Thanks,
    Gabriel

  • ProCactus

    I only get

    exit status 1
    Error compiling for board Arduino/Genuino Uno.

    When I compile. So far I dont know why

  • Alex

    Hi,

    Thanks for the pretty detailed article. I wanted to do basically the same thing only with a different display, and I ended up doing all the same things – OpenMusic’s FHT library and “overclocked” ADC – that was all before I’ve found this article. Looking forward to borrow your DC biasing circuit – so far I’m testing without it, and, of course, I don’t get very good results. Have a problem with low freq. noise, too – good tip on using a different power source.

    One thing of note: Arduino only has a single ADC. So when you set up two analog inputs through a multiplexer, you get 38.4 kHz total sampling rate, but only 19.2 kHz per channel, for the audio bandwith of only 9.6 kHz. It seems you did not consider that.

    And another thing: your use of String objects is unjustified. Instead of doing “String sampleSet = “L”:”, you should use an enum: “enum {L, R} sampleSet = L”; and similarly for “displayType”. That will use less flash, less RAM, and much fewer runtime cycles. Furthermore, the “displayText” String is not referenced anywhere, removing it after converting other strings to enums will remove the dependency on String library altogether for a significant reduction of the program (flash) size and some 20 bytes RAM consumption reduction.

  • C. bot

    hi, i need help, msg error “ldi r16, “STRINGIFY(FHT_N/8)” n” // prep loop counter, arduino v. 1.8.2 fht libraries line 178:3. you please version arduino used for compilation and send file, arduino board uno r3