Arduino Projects: Digital Audio Recorder

arduino recorder

Being able to capture sound, store it and play it over and over again never fails to leave me in awe of its pioneers, from Thomas Edison to Alan Blumlein, the British electrical engineer who, in 1931, invented ‘binaural recording’ – what we now call ‘stereo’. (Never heard of him? Blumlein amassed 128 patents in audio, radar and television that are still in use today, but tragically, was killed in a plane crash during World War II while testing airborne radar. His loss was considered so great, news of his death was kept secret until after the war).

So far in this series, we’ve turned an Arduino into a number of audio-related projects from a digital audio player to, most recently, an audio spectrum analyser. This month, we see just how far we can push the popular microcontroller as we begin from scratch turning it into a basic but working digital audio recorder.

How it works

The ATMEGA328P’s ADC sample rate is adjustable through a clock prescaler.No matter what they look like, all digital audio recording devices have to perform the same basic functions – they have to capture an analog audio signal at regular intervals while at the same time, saving the digital data to storage. That might be easy for a PC or smartphone, but we’ll need to introduce some advanced programming techniques plus tap into some hidden features to get this working on an Arduino Uno.

For the record, our Digital Audio Recorder will capture a single (mono) analog audio channel with a sample rate of 22.05kHz, 8-bit sample depth and store it as a Windows WAV file with up to 4GB filesize on a microSD flash card. Now before you yawn in excitement at those specs, remember, we’re doing this with a 16MHz processor, just 2KB of RAM and 32KB of programming space. If only a Windows PC could be so efficient!

To help make the project (and source code) as easy to understand as possible, our recorder has just two buttons – record and stop. It doesn’t play audio and only records to a single fixed file in the root folder of the flash card called ‘REC00000.WAV’. An existing file with the same name will be overwritten. For playback, just take the flash card, load it into your PC, phone or tablet and play the file in any standard WAV file-ready media player or editor.

A sample takes 13 clock cycles and appears in registers ADCL/ADCH.

Nyquist Theorem

We all know digital audio – we all listen to music and we’ve all no doubt ripped a few CDs in our time. But how do we capture an analog signal and turn it into digital audio?

This is where the work of another electrical engineer, Harry Nyquist, helps us out. He figured out that in order to digitally capture an analog signal, we need to capture or ‘sample’ it at regular intervals, a rate which needs to be at least twice the highest audio frequency we want to capture. That means if we want a 5kHz audio bandwidth for example, we need a minimum 10kHz sample rate.

The way we capture those samples is with a circuit device called an analog-to-digital converter (ADC) and the Arduino Uno’s ATMEGA328P microcontroller chip has one on-board. But by default, it has a 9.6kHz sample rate and 10-bit sample depth, so we already have work to do to knock it into shape. For starters, the sample rate is too low (we’d only get a 4.8kHz audio bandwidth, which is AM radio quality at best) and the bit depth is the wrong size. (Bit depth is the sample precision, which is normally 16-bit in CD audio, but the Arduino’s ADC only has 10-bits to start with).

The circuit diagram for our Digital Audio Recorder. Still, the ATMEGA328P has a few tricks up its sleeve. One of those we used in the Audio Spectrum Analyser project is an adjustable ADC clock prescaler. Just like any CPU, the ADC requires a clock signal to synchronise its function and here, this is set by a programmable divider or ‘prescaler’, dividing the 16MHz Arduino master clock by a default factor of 128 to create a 125kHz ADC clock rate.

Because the ADC uses the ‘successive approximation’ sampling method (we looked at this in detail a few months ago), each sample takes 13 clock cycles, giving us a sample rate of 125kHz/13 or approximately 9.6kHz. But if we reduce the prescale factor, we can increase the ADC clock rate – a prescaler factor of 16 immediately cranks up the sample rate to nearly 77kHz, or a sample every 13microseconds.

But there is a downside – the ADC loses sample accuracy with increased clock speed, however, even at this higher rate, the accuracy is still close to 8-bit, which is all we need. This ‘overclocking’ technique works extremely well, but there is one other major limitation – the limited number of prescaler settings leaves us with audio-unfriendly samples rates of 9.6, 19.2, 38.4 and 76.8kHz, none of which are WAV format-standard.

This $5 microSD reader handles communication between Arduino and card.Read through the ATMEGA328P datasheet and you’ll find that in addition to the normal ‘free-running’ sample mode we’ve been talking about, the ADC also has a ‘single conversion’ mode, whereby the ADC is enabled by setting the sampling register bit or ‘flag’, it grabs that sample and immediately resets the flag when the sample is ready for processing.

That mightn’t sound like cause for celebration, but when we combine it with another of the ATMEGA328P’s hidden talents called ‘timer interrupts’, we now have a mechanism for setting a much more precise sample rate.

Timer interrupts

In computer architecture, an ‘interrupt’ is a trigger to tell the processor to immediately divert from or ‘interrupt’ the current process and run a specific task associated with that interrupt. Once the new task is completed, the processor returns to the original process and picks up where it left off.

Now, the ATMEGA328P has all sorts of interrupt triggers to play with – you can trigger an interrupt externally by pulling an interrupt pin high or low as appropriate, but the chip also has a number of software-controlled options, one set in particular called ‘timer interrupts’.

The CTC timer interrupt enables a much more precise sample rate setting.In any CPU or microcontroller, a timer is just a hardware variable or ‘register’ that counts up to its maximum count (for example, 256 for an 8-bit timer), instantly drops back to zero and starts again. Because timers run off the master clock and each count takes a fixed number of clock cycles, we can programmatically figure out how long it will take to reach the top count, hence the ‘timer’ name.

The ATMEGA328P has three of them – one 16-bit and two 8-bit timers – along with different ways you can use them. One simple way is once the timer reaches its maximum count, it can set an ‘overflow’ flag, which can be used to trigger an interrupt. Like the ADC, timers also have a programmable prescaler for the input clock, so we can adjust how long it takes to reach that overflow condition.

This overlay diagram shows you how the components fit in place. But another far more useful option is a special mode called ‘Clear Timer on Compare Match’ or CTC. Instead of waiting for the timer to reach its overflow point, we can choose our own – for example, rather than wait for an 8-bit timer to count to 256, we can load any number between 1 and 255 into a special ‘compare’ register and once the timer reaches that number, it triggers an interrupt, the timer instantly reverts to zero and counts again. Using this technique to drive the ADC sampling, we can set the sample rate with far greater precision.

In our project, we use the chip’s ‘Timer2’ timer, switch it to this CTC mode and load an 8-bit register called ‘OCR2A’ with our ‘compare’ number to give us an interrupt every 45microseconds. That gives us a sample rate of approximately 22.19kHz – not perfect, but closer than anything else.

Storing the samples

You’d never guess, but this is a functioning digital audio recorder.However, now we have these 10-bit samples turning up every 45 microseconds, we’ve got to do something with them. The first thing is to drop the two least significant bits (LSBs) and turn them into 8-bit samples – that’s relatively easy since each 10-bit sample is split and stored in two 8-bit registers ADCL and ADCH. We program the ADC control register ADCSRA to give us just the top eight bits in the ADCH register.

But with only 2KB of RAM on-board (closer to 1KB by the time we run our code), the ATMEGA328P will still run out of space in a heart-beat – that’s where the microSD card module comes in.

We all know SecureDigital (SD) flash cards – they’re in everything from cameras to phones and tablets. We load them into our PCs and they appear as yet another storage drive. But when it comes to low-level hardware design like this, we need to understand a lot more about these tiny little storage devices.

All SD cards normally use a four-bit wide parallel interface to transfer data and achieve average write speeds beyond 10MB/second. But the key word is ‘average’ – the actual write speed can vary considerably, depending on the latency or delay in writing data to the card.

Normally, your PC or device has sufficient RAM to buffer the data and smooth out the writing process so there’s no perceived loss in write speed. But the ATMEGA328P has two things working against it – it only has 2KB of RAM, but more importantly, it doesn’t have a 4-bit data interface. Instead, we have to use the one-bit Serial Peripheral Interface (SPI) bus. It’s the fastest interface the Arduino Uno has, but even running at 8MHz, it leaves us with an average write speed of only 150KB/second.

Since we only want to store 22.05 (22.2) KB per second, it sounds like we’re fine, but again, this SPI-bus write speed is an average. SD card storage is divided into blocks, each 512 bytes wide, so we have to write the data in 512-byte chunks – but that write process can’t interfere with the ADC sampling every 45microseconds. The problem is, average SD card latency in SPI mode is between 700-900 microseconds.

The simplest write method we could try is to just open up a file on the card, start grabbing samples, count them up as we go and when we hit 512, dump that block to the card using the sample interrupt routine. But if we do that, we’ll still be writing the block when the next sample interrupt request arrives – and that’ll result in either new samples or the entire block being lost.

Twin buffers

Our audio recorder requires just a microSD card reader and an Arduino Uno. We solve this problem using a technique commonly found in digital audio design called ‘double buffering’ – we set up two 512-byte blocks of RAM called ‘buffers’, labelled ‘buf00’ and ‘buf01’ in our code. As we start recording samples, we store them in the first buffer. When that buffer is full, we switch over to the second buffer; meanwhile, the first buffer is now saved to the flash card. The 512-byte buffer size gives us roughly 22 milliseconds to get that first buffer written to the flash card.

The 900-microsecond average latency for SD card writes via SPI is way too long to handle inside the 45-microsecond sample interrupt process, but with 22 milliseconds per buffer to play with, we can get the ATMEGA328P to multitask and write the block in between ADC samples.

Once the second buffer fills up, we switch back to loading up the first buffer again that has now been saved and meanwhile, start saving the contents of that second buffer in the next 22 milliseconds. What we end up with is this constant swapping – we’re storing samples in one buffer while writing the other buffer to the flash card. But on the card itself, we end up with a seamless stream of audio data, all done with just 1KB of RAM. (In reality, the Arduino’s SD library grabs its own 512-byte buffer, but without our double-buffering, this project wouldn’t work).

Your recordings will load and play in any WAV format ready software or device.

Listen to history in the making

We’ve been recording sound since Thomas Edison invented the tinfoil system in 1877. Check out cylinder recordings. Alan Blumlein’s stereo test recordings from 1933 have also been preserved and restored by the British Library. Listen online here.

  • There’s a follow-up story to this project in the current issue of APC (July 2014) that outlines improvements I’ve made to this design.

  • Souvik Paul

    Hi Darren, I’m a relative newcomer to C programming, and am looking to implement this project on an Arduino Micro [which is a smaller version of the Leonardo, which uses the ATMegaXu4 chip. This chip doesn’t have timer2, though it does have a 10bit timer4. I’ve been trying to reconfigure the code in order to use timer4 instead of timer2, but I haven’t gotten very far. Am I correct in assuming that the only code that needs to change in the sketch is in Setup_Timer2(), Setup_ADC(), and ISR(TIMER2_COMPA_vect)? Do you have an idea of how the code would need to change?

    • I’m not sure. I have a Leonardo board, but haven’t used it yet. (They haven’t seemed as popular and Arduino seem to be swapping and changing things around a bit at the moment with their board/chip choices.)
      Provided everything else is the same, then it should only be the parts that relate to TIMER2 that should need to change. However, with timer4 being only 10-bits, that might affect things. TIMER2 I think is 16-bit IIRC.
      The real trick will be if the ATMEGA32u4 chip supports a comparator vector into TIMER4 – that I don’t know. If it doesn’t, you’ll need to come up with another way of creating the right timing mechanism. The TIMER2_COMPA_vect acts like a comparator input, allowing you to see if the TIMER2 has reached a certain count. If it has, it launches an interrupt to run the code that samples the audio and restarts the count again.
      I haven’t read the specsheet for the ATMEGA32u4, so I can’t answer that question at this stage.

      • prithivi raj

        Can i get the program for this project

        • Follow the ‘source code’ link near the top of the story.

  • Vinod Naidu

    what you have connected to 3.5mm stero panel socket ?
    headset which is having a inbuilt microphone!

  • 黃詠揚

    HI Darren
    I have a question.
    Is the microphone connected to the 3.5mm stereo panel socket?

    Thx for your help.

    • Short answer: yes – but no.
      Long answer: That’s where you would connect the microphone in theory, but the problem is a microphone has too low an output signal and it won’t be picked up by the Arduino’s ADC. Most microphones put out little more than 5-millivolts DC. You would need 200 times level that for the ADC to make a good fist of it.
      So what you need is a microphone preamplifier with around 50dB of amplification or ‘gain’. Plug your microphone into the preamp, plug the output from the preamp into the stereo socket of the recorder.
      Search ‘microphone preamp circuit’ on Google and there’s a stack of circuits. A couple of transistors would do the job, or an LM833 dual opamp. It’s up to you.

      • 黃詠揚

        thx again for your help

  • 黃詠揚

    HI Darren
    I have a question.
    Why is the circuit has two points to connect with the microphone?
    Is it because the microphone you use is the stereo type?
    If my microphone is mono type,should I just connect 1k resistor to the 2.2uF capacitor?
    thx for your help:)

    • I think I’ve said before, it’s NOT for connecting a microphone, but a line-level input. The reason for the two inputs is that it allows for a stereo input source – the two resistors are simply mixing the two channels together to create a mixed single-channel input.

      • 黃詠揚

        Hello Darren
        I have another question about the preamp.
        I looked up the datasheet of LM833.I saw two input which are IN+(Noninverting input) and IN-(Inverting Input).
        How should I connect my microphone with this OPA(LM833)?
        Thank you so much for all your help:)

        • Sorry, I can’t draw circuits here, but I suggest you search Google for ‘LM833 microphone preamp’. Basically, what you need is a small-signal audio preamplifier with a gain of around 30-40dB (depending on the output of your microphone).
          Note, you don’t have to use an LM833 – in fact, I’d say it’s probably a bit too good for an 8-bit digital audio recorder. An NE5532 or NE5534 should also do the job – whichever one you can get cheaper.
          You’ll also need to check if your microphone is a dynamic or electret type. Electret mics produce much higher signal output, but also need power of their own, which may need to be 48VDC, depending on the mic.
          Anyway, lots of variables, but Google is your friend.

  • ric

    I’m getting a buzzing sound overlaying my recording ( the recording is there and sounds ok). i tracked it down to writing to the sdcard. ie.. if i comment out the code that writes 512 bytes each time a buffer fills the buzz is eliminated. i’m using an uno, but with the breadboard separate. My arduino pwr is usb from the computer where the line-level input to the circuit originates.
    I’ve tried all kinds of capacitance in the circuit, but nothing seems to eliminate the buzz when writing to the sdcard. I’m trying different scenarios to eliminate this.
    Any ideas? my circuit is same as yours.
    thank you

    • Hi Ric,
      So if you comment out that line it means it’s no longer storing, am I reading that right? Buzzing can come from a number of sources, so here’s what I’d do. First, make sure your wiring is solid – check that all of your ground lines are grounded.
      I’d be tempted to use a USB power brick (your phone charger for example), just to see if its the power source.
      If the buzz goes by commenting out that line, it suggests you have your analog ground line on the wrong side of the SDcard ground, by which, you probably have this arrangement:
      SDcard GND————-analog input GND—————USB power GND
      What you need to have is:

      analog input GND ————- SD card GND ———— USB power GND
      Basically, it sounds like a ground-loop problem whereby the return power path of the SDcard is entering the ground path of the analog audio input before heading back to USB GND.
      Sorry if that sounds a bit complicated – it’s a common problem you find with audio circuits that also have digital components.
      Physically follow the ground plane path from your analog input ground to the USB connector and make sure you’ve got the order right. (Mind you, it is only 8-bit so there will be some minor hiss as a result).

  • ric

    Hi Darren,
    Thanks very much for your help. i switched power to usb from my android phone charger and the buzz is gone. There is only the expected hissing you mentioned. It must have been the ground loop problem you described. That would make sense given that both the power and analog input were from the same computer.
    This is a very cool project and has been a great learning experience for me. One thing i’m wondering about is if you think the wear leveling algorithm in the sdcard could slow down some of the 512 byte block writes enough to cause a timing issue in the sketch code? I did some tests where i sampled the timing of several hundred 512 byte writes. Most were in the 768uS range, but there were also a few in the 6-9mS range. Any ideas about what might cause such a bump in the time? Used the same initialization code from your project for those tests.

    • Good question, but your timings are roughly in line with what I found – mostly okay, with the occasional glitch. I think it depends on the card – a couple of cards I had were fine, provided you formatted with straight FAT16 right before you recorded, so it could well be the File Allocation Table writing slowing things down.
      Of course, the real problem is we’re trying to do this with 2KB of RAM. Most audio recording apps would have MBs of RAM buffering, not 512bytes.
      I had best results with Class-4 cards – yep, Class-4 – don’t ask me why.

  • Khalieda Rosli

    hi, darren.. can i separate it into two parts? one arduino as tx to save the recording and another one as rx to play the recording?? if can.. how??

    • Nah, that’s overkill – a single Arduino can do both. You just need to set up a DAC (digital-to-analog converter) to handle the output. I can’t go into full details here, but two basic options are either an R-2R resistor DAC using eight digital I/O lines (you’d probably even get away with just six) or you feed out audio directly via PWM, in which case, you just need to apply a low-pass filter on the PWM output to remove the base clock frequency and send it to an audio amp.
      I just haven’t got around to writing the code yet. :)

  • Jessica Patterson

    Hi Darren,

    We are a group of college students from various backgrounds taking an Ocean Technology General Education class. We are trying to use your device to record sound and store it on an SD card to load to our computer and edit sound in Audacity. We are running into troubles getting the audio to actually record on the SD card and play back on the computer. We think it has to do with our SD card not recognizing the bytes and not being able to use the buffers. What advice do you have for us?

    Thanks for your time!

    • Jessica, there’s a standard method I use for isolating circuit problems that may help.
      1. Check that your wiring is correct and matches the overlay diagram.
      2. Check that the Arduino is actually working – this can be done by pressing the function buttons on the circuit and looking for the LED indicators.
      3. If all seems correct and you suspect the SD card, swap out the card for another one.
      The idea is that you want to make one change at a time and test to see if that change makes a difference. If you change more than one thing at once, you can’t be sure which change makes the difference.
      Also, make sure you read the second part of this story – search the site for ‘Arduino digital audio recorder’ and look for part 2.

  • krishna p

    Hi Darren,

    I appreciate your immense efforts of coming up with so much contribution. First of all thanks a ton!

    I have been trying to execute this and confronting few doubts, please help me in clarifying; the below are my doubts:

    1. In the circuit diagram you showed only one capacitor, but in the overlay picture we observed there are 2 capacitors . Can you give us a clarity about the connections .

    2. In the program the pinmode 10 has been stated as output, but I dont see any connection to pin 10 at all, (according to the instruction, diagram and pictures)

    Looking forward to hear from you :)

    • Hi Krishna,
      1. I didn’t think anyone would notice that! :) Nevermind. The circuit diagram and the overlay diagram are correct – only one input capacitor is required. We’re mixing the two inputs together via the two input resistors, so we only need one cap. I used two caps in my build as a I didn’t have a single 2.2uF cap and had to suffice with 2 x 1uF caps instead (close enough)!
      2. No idea why that’s there. You can remove it.

  • krishna p

    Thank you :)

  • krishna p

    We are facing a problem that the voice is not being captured while the sound is being recorded(which means we are able to see the file, but when we open the file it has a disturbance but nor recorded sound). Do we have an alternate for stereo panel socket in this case?

    • Well, before you panic, check your wiring, make sure you’ve built the circuit correctly. Also, make sure you read Part 2 of this story (search for Digital Audio Recorder Part 2).
      The other thing is, what’s the source of your audio? You cannot just plug a microphone into the input – it must be a line-level source.

      • Rishabh Sawant

        what source should be used?

  • Mohammad

    “If only a Windows PC could be so efficient!” LOL

  • yasir hussain

    Is there any other way for taking an analog signal input without stereo panel socket?

    • It doesn’t have to be a panel socket, it can be whatever you want – as long as the signal level is somewhere near 1-volt RMS. Remove one of the 1kohm resistors, connect your input signal to the other and connect the ground line to that of your signal source.

      • yasir hussain

        So i can use directly a mic along with LM386 to amplify the input signal?

        • Sorry, but I can’t answer that. I have no idea of the output signal level your mic produces (every mic is different), so I don’t know if the gain provided by an LM386 is enough.
          There’s one way to find out – build it yourself! :)

  • I’m Boat

    For instructions on how to do it .

    Thank You

  • Jason Ruthermore

    my family was requiring Fipc 553 form last year and learned about a business with lots of form templates . If people want Fipc 553 form also , here’s a

  • Philip

    Amazing work, Darren!

    My work doesn’t have to do with audio recording, but I am trying to store a huge amount of data on an SD card. The main problem is the write latencies. Can you provide some insights to my doubts?

    When you write 512 bytes of data (which is 256 integers right?) onto the SD card, does the write latency occur only once? Does the Arduino write ALL 512 bytes in 1 go? If not, then if 256 integers are written one by one with an average latency of 900 microseconds, wouldn’t the write time actually far surpass the 22 millisecond threshold you have?

    Thanks! :)

    • Hi Philip,
      No, what tends to occur is that there is a seek latency, but actual writing is comparatively quick. The microSD card module here runs via SPI (serial peripheral interface) and from memory (I mean ‘my memory’), I’ve set it in code to run at 8MHz, or basically as fast as it can go. Yes, 8-bit SDIO would’ve been ideal, but SPI is the best an Arduino Uno can do.
      However, that said, my research in this project was that there are occasional latencies that exceed 22ms, which does result in lost samples, but surprisingly enough, those losses are quite rare. Obviously, the higher the audio sample rate, the more of a problem this is. I start with a clean formatted card and the results are pretty good. It tends to come down to how well internally the flash card reclaims dumped blocks as to when and how often those extended latencies seem to occur.
      There is a later version of this project using an Arduino MEGA – the larger 8KB cache of RAM on the ATMEGA2560 overcomes this issue to a large extent. But as I said before, the sample losses were only occasional.
      Still, if you’re not dealing with audio recording, I’d imagine you’d be able to slow the sample rate down, in which case, this becomes less of an issue. In testing, I saw occasional seek latencies of up to 70-80ms, but they seemed quite random. If you’re happy to use the default ADC rate of around 8.9kHz, I can’t see it being a problem.
      Meanwhile, I’ve begun looking into 32-bit ARM Cortex M3 boards featuring the STM32F103C8T6 in particular – these have 20KB of RAM, which makes things easier to deal with. I did say somewhere that audio recording pushes the Arduino to its limit – and mostly that limit revolves around the squinchy 2KB of RAM.

  • Yukio G.

    Perhaps this is a rather dumb question, but I don’t quite get it. Why do you trim the resolution to 8-bit? Is it noise related or does it have to do with the size standard for the audio data?

    • No, actually they’re good questions, but the short answer is ‘both’.
      First, by overclocking the ADC, the noise level goes up and we only get about 8-bits of decent signal out of it. Nothing we can do about that. Second, because the ATMEGA328 is only 8-bit, it has to split the 10-bit ADC output over two 8-bit registers anyway and it’s much easier to just take the top eight bits. Third, we don’t have enough buffer space as it is, really, and storing 10-bits cuts the buffer period down by half (we still have to keep the byte boundary).
      It might only be 8-bit, but it actually sounds surprisingly good.