Playing WAV Files with a DAC

What are we making?

We're going to make a WAV player! (If you're not familiar, a WAV is an uncompressed audio file). We're going to build it with an Arduino and the AD5330 breakout board, an 8-bit DAC. WAV files are uncompressed and the Arduino doesn't have too much internal ROM so we'll have to store the WAV files on an SD card. This means we'll also add the microSD shield to the project.

A DAC is a Digital to Analog Converter. Basically a DAC takes a digital value (think 1's and 0's) supplied by a microcontroller and turns it into a corresponding analog voltage. An 8-bit DAC means that the DAC has 8 pins on which to set the digital value. Remember, an 8 bit value can be any number between 0 and 255. We will power the DAC with 3.3 Volts. By using an Arduino with the DAC we will be able to create 256 distinct voltages between 0 and 3.3V by providing 8 bits via digital output pins on the Arduino. By creating different voltages on a set frequency we can create sound!

In this tutorial we'll cover how to attach the microSD shield to the Arduino platform, and how to connect the AD5330 breakout board to the shield. After connecting all of the pieces together we'll look at some code to get WAV files playing from the SD card.

What do I need?



Hook up the hardware!

Geez that's a whole lot of stuff we've got. Connecting all the pieces won't be very hard, but it will take some soldering! So what all do we need to do? Well for starters the microSD shield needs to go on top of the Arduino. Start by soldering the 6 and 8 pin stackable headers onto the shield. Make sure that the male ends are pointing down away from the components on the shield.

SD Shield with Headers

Now the shield can be placed on top of the Arduino.
SD Shield Installed on Arduino Duemilanove

Let's move on to the AD5330 breakout board. In order to connect this to the shield and the speaker we'll need to place the breakout board onto a breadboard, but first we'll have to solder some headers onto the breakout board. Solder breakaway male headers to the breakout board; make sure the pins are facing away from the components on top of the board.

AD5330 Breakout with Male Headers

Now you can put the AD5330 into the breadboard

AD5330 Breakout Installed in a Breadboard

Alright, with those two things taken care of we can get to wiring the project up. Things get a little messy here so use care while wiring all of these signals. You'll need 17 jumper wires to wire everything from the microSD shield to the AD5330 breakout board. The table below shows the pins that need to be connected. For each connection plug a jumper wire from the female header on the microSD shield to row on the breadboard that aligns with the specified pin name.

microSD Shield Pin Number
AD5330 Pin Name
Rx DB0
Tx DB1
2 DB2
3 DB3
4 DB4
5 DB5
6 DB6
7 DB7
10 PD
An. In 0 GAIN
An. In 1 CLR
An. In 2 LDAC
An. In 3 WR
An. In 4 CS
+3.3V VCC

Double check all of your wiring. With this many wires it's very easy to misplace one or two of them; if that happens the whole circuit may not work once you upload some code. Debugging the entire project then becomes an exercise in frustration. Trust me, it's better to just double check the wiring now and make sure you've wired the project up properly. Now that the microSD shield is wired up to the AD5330 breakout board we're almost finished. The whole point of the project, though, is to play some music. The missing piece is glaringly obvious; we still need to throw a speaker on there! You might be tempted to just attach the speaker to the VOUT pin of the AD5330, but unfortunately that won't cut it. In order to create sound the voltage must be AC coupled. It may sound complicated, but really all it means is that we need to put a capacitor between the VOUT pin and the speaker. Place the positive lead of a 10uF Capacitor in the VOUT row of the breadboard and put the other end of the capacitor onto the positive terminal of the speaker. Connect the negative terminal of the speaker to the GND pin of the AD5330 breakout board. (If your speaker didn't come with wires soldered onto the terminal you'll have to add them yourself.)
Entire Project Setup

Adding the Firmware

Now on to the fun stuff! Download the two zip files from the Code Downloads section at the top of the tutorial. The first zip file contains an Arduino Library for the AD5330. Unzip this in the \hardware\libraries directory of your Arduino directory. The library contains two example sketches: a tone sketch and a 'WAV files from SD' sketch. The first sketch just produces a single tone on the speaker while the second set will retrieve WAV files from the SD card and play them.

The second download, the C code, is actually the same program as the second Arduino sketch; however this code is written in plain old C. There are many things I love about Arduino, but sometimes you just need some extra speed and power and it's hard to get around this in the Arduino IDE. Using C gives you some extra room to push data a bit faster. We'll talk about this some more later.

Let's get started looking at the code. Open the first sketch, the AD5330_Tone sketch. After you've unzipped the folder in the Arduino directory open the Arduino IDE. Go to File->Examples->AD5330 and select AD5330_Tone. Check out the sketch a bit before you load it. It's simple enough to figure out without digging too much. After defining the pin locations we set all of the pins on the Arduino as outputs. I used the datasheet to find out how to configure all of the inputs to the AD5330. After setting the initial values of all of the pins we're off to the 'Loop' code. To create a tone we need to send a sound wave to the AD5330. To send a sound wave to the AD5330 all we need to do is set a value on the DB0-DB7 pins, clock them into the module and delay, then set the DB0-DB7 pins to zero, clock the new data in and delay again. If this procedure is repeated over and over again the tone that's generated will be directly related to the delay. The shorter the delay time, the higher the tone will be; the longer the delay the lower the tone will be. Check out the graphic representation below of how a digital wave is represented.

Digital Representation of a Sine Wave

Set the board to Arduino Duemilanove w/ 328 and  compile the code. After you load the code to your Arduino you should hear a sound coming out of the speaker. If you don't you need to go back and double check all of your wiring. Play with the FREQ and VOL values to see how they affect the tone and volume of the generated sound. If the code doesn't compile and gives you errors related to pre-existing files it may be because you already have a FAT library installed. If this is the case the FAT library will have to be temporarily removed while using this code.

That was neat, and produced a pretty nice sound, but we can do that with 1 pin very easily. I want to play some music on this thing! I decided I wanted to play 8 bit WAV files, pretty much because it would take the least amount of work. WAV file are uncompressed so the data manipulation wouldn't be a concern; and 8-bits, well, duh... For some reason the first thing I thought of after hearing 8-bit DAC was the theme song to Mario Bros. So I got on the Internets and started searching for some Mario WAV files. Wasn't too hard. The first files I grabbed happened to have a sampling rate of 22 kHz. So with this in mind I'd settled several issues: the sample width and the sample rate. Our WAV player is going to play 8 bit WAV files with a sampling rate of 22 kHz. If you put any other kind of formatted WAV file on this it may still play, but it won't sound very good!

Open the Arduino sketch from the AD5330 library named AD5330_SD_WAV_Playback. You'll see that several files are now included so the sketch can use FAT16. Some of the code at the beginning of the sketch will look familiar; the Setup section of the code is actually the same! But there's also some more advanced stuff going on. When playing audio files it's pretty important that we update the data at the correct rate; never faster or slower than our sample rate of 22 kHz. In order to do this an Interrupt Routine was created. The interrupt will stop the 'Loop' code no matter what it's doing and update the data on the AD5330. This means that we can be sure that we're sending sound consistently at 22 kHz. The code for the interrupt routine is inserted between the Setup and Loop sections of the code.

The 'Loop' section of the code is commented pretty well. Here's a basic run down of what's going on:

  1. Find a WAV file on the SD card.
  2. Create two buffers that will hold data
  3. While 'playing' the data from one buffer, read new data from the SD card into the other buffer
  4. When the first buffer is empty; swap them
  5. If a song is over look for another WAV file. If no more WAV files are found then stop running!

Sounds pretty easy huh? It's actually not that bad once you get used to using the FAT16 functions. By the way we didn't talk about why we actually need an SD card for this. Unfortunately the ATmega328 only has 2 kB of EEPROM. This isn't nearly enough to hold a WAV file, so we added the SD card for some external memory.

Once you read through the sketch go ahead and compile it. Make sure the Arduino Duemilanove w/ 328 is selected as your board and then load it up. Format your SD card for FAT16, then load some 8 bit WAV files onto the card.  Any audio file can be converted into an 8bit WAV file; there are some instructions at the end of this tutorial on how to convert an audio file using iTunes. Once you've got the WAV files onto the SD card put the SD card into the shield. Reset the Arduino and the Arduino should start playing the files. If nothing happens there are a few things you need to check: first double check your wiring; then make sure you've loaded 8-bit WAV single channel 22kHz WAV files. Any other format could cause the project to be upset :(.

After the initial "Wow that's cool!" wears off, the first thing that will probably come to your mind is "huh, this sounds like crap." Well, yes, it does. There's a couple of things to consider:

  1. Oh come on, your using an Arduino, a $2 speaker and an 8 bit DAC all wired up together on a breadboard. What did you expect?
  2. Sometimes Arduino just doesn't cut it! Our interrupt routine isn't running fast enough. We can improve this by migrating the code to C. Let's do that!

Look, I'm the first to admit that I'm an Arduino junkie. I love how easy it is to set up, and that I know there's nothing wrong with the hardware. I really like how easy it is to get sensors up and running. But there are some shortfalls with the compilation process and I often find that I have to move over to C in order to really maximize the power of the ATmega328 microprocessor. 

Download the second zip file to get the C code for this project. The code is actually exactly the same, but the digitalWrite functions are replaced with direct register access to make writing to the pins much faster. One of my favorite things about the Arduino platform is the serial bootloader. A not-so-little known secret about the Arduino hardware is the regular hex files (perhaps compiled using WinAVR) can be uploaded using the serial bootloader. See this tutorial for more help on the subject. What this means is that I can write better, more efficient code in C but still take advantage of the Arduino hardware platform. And that's exactly what's been done in the second zip file.

Once you've downloaded the zip file with the C code feel free to check out the source. You'll notice that it is strikingly familiar to the Arduino sketch that plays songs from the SD card. All that's left to do is load the hex file onto the Arduino. You should hear the difference between the 'C' version and the 'Sketch' version of the code immediately. It's still not high quality audio, but it's a much more clear and crisp sound.

Creating 8-Bit 22kHz WAV files

Google '8-bit WAV converter' and bunch of free software applications will show up right away. You may even have your own program or script that will make this conversion for you. However since most people are at least familiar with iTunes we'll create a WAV file in that program that's compatible with our firmware.

I use Windows so I apologize to all you Mac and Linux users. Please help others with some recommendations inside the comments below the tutorial page. Start up the iTunes application; once it's opened up click on the Edit menu and select Preferences.

iTunes Preferences Window

In the 'General' Tab of the Preferences Window click on 'Import Settings.' Select WAV Encoder from the 'Import Using' drop-down menu and then select 'Custom...' from the 'Setting' drop-down menu. In the WAV Encoder window, choose 22.050kHz Sample Rate, 8-bit Sample Size and Mono Channels. Click OK in the WAV Encoder window, the Import Settings window and the Preferences window to save your settings
iTunes WAV Encoder Window

Now whenever you import a song to your iTunes Library it will be imported with these settings. You can grab the imported file from the iTunes Library directory and put it on the SD card for your WAV player. Remember to change this setting back to the original format for your regularl music imports or you won't be impressed with the audio quality!

One way to double check the files attributes is to right click the WAV file in the directory and select 'properties.' In the summary tab all of the audio properties will be listed, just double check to make sure that the sample rate is 22 kHz, the sample size is 8-bit, it's a 1 channel file, and the audio format is PCM.  As long as your WAV file has these properties it can be loaded onto the SD card and played by the WAV player.

WAV Properties Window

And that's it! Hopefully you learned a bit from this tutorial. Feel free to comment on any errors you find and post any improvements you make. Good luck!


Comments 31 comments

  • Taylor BMI / about 12 years ago / 2

    Yes, you did!

  • redwgn67 / about 8 years ago / 1

    Could this be adapted for an Uno? What about the code is specific to the Duemilanove?

  • Member #495064 / about 8 years ago / 1

    Hi !

    I have a problem with the SD_WAV_PLAYBACK code:

    "aggregate 'fat_dir_entry_struct dir_entry' has incomplete type and connot be defined"

    help me please!! i'm beginner

  • AJH / about 10 years ago / 1

    Hi! I've got this code up and running (partially) on an Arduino Mega. After changing the necessary pin declarations in some of the header files I'm able to get songs playing but not consistently. I modified the code slightly so that it plays a .wav file on a button press. It plays the song on anywhere from 10/10 to 2/10 of the button presses. Any thoughts as to why this inconsistency might be happening?

    On the occasions when it doesn't play anything the bytes_read variable reads -1 indicating that the file has reached it's end even though it has just been opened.

    Any insight would be greatly appreciated. Thanks!!

  • Member #267097 / about 11 years ago / 1

    Has anyone run into a problem with the SD_WAV_PLAYBACK code? My circuit works fine for playing a tone but it just doesn't work for the next one. I formatted my 4GB microSD card in FAT16 with 512MB partition and loaded up with a wave file in 8bit, mono, and sampling rate of 22K. Can anyone tell me any advice where I might get wrong? I re-checked my circuit and connection and it seems to be alright... :( this damn sd card.. grrrr

    • Member #267097 / about 11 years ago / 1

      Is there anything to do with my arduino uno? I donno if there is anything changed from the previous version of this...

  • mikeblow / about 11 years ago * / 1

    Hey SparkFun, kudos for the cool kit and tutorials. Might be worth mentioning this thread: in relation to the GAIN function on the 5330.

  • Member #141822 / about 11 years ago / 1

    Is there anyway to accomplish this without the while loop? The reason I ask is I want to continue reading the sensors while the SFX plays. If a sensor comes back as active I want to add it to a queue to be played next. I tried creating a global boolean Playing variable which would contain the code to play the song. It doesn't work though as it seems the global boolean value I created constantly resets itself.

  • I have a question -
    Can we make a program i which "if" we press a particular button , a particular WAV file plays
    and if we if we press other button , the other WAV file plays.

    • Member #141822 / about 11 years ago / 1

      So I did more digging on Sparkfun and found the SDCard Breakout Tutorial. Pretty helpful and should help you accomplish your project. The link is:

    • Member #141822 / about 11 years ago / 1

      Looking for the same answer. Is it as simple as just using wave file names instead of searching for the next one?

  • baum / about 11 years ago * / 1

    Great tutorial, Spakfun. However, your pin connections can be greatly improved. Instead of using 15 GPIOs as you have, I am come up with a configuration which only uses 10 pins and does not sacrifice anything. While looking at your code, I noticed something: BUF, PD, GAIN, LDAC, and CLR were set either high or low in the setup function and were never changed later on in the code.
    So unless I am mistaken, it seems that those 5 pins can be tied to their respective power rails, saving 5 pins.

    • Member #141822 / about 11 years ago / 1

      It would seem that you can do that however in the code you cannot remove:
      1) Defining Pin PD
      2) Declaring Pin PD as an Output
      I do not know why, but you still save 4 pins which was very helpful. Thanks.

  • Member #197138 / about 11 years ago / 1

    having trouble compiling...
    I keep having issues compiling this project, the following error occurs over multiple files:
    ‘for’ loop initial declaration used outside C99 mode
    Is there a way to enable C99 mode?

  • Fluffy / about 12 years ago / 1

    Can this be done with the arduino DAC and sd card? Does it need to use an external DAC, why?

  • Taylor BMI / about 12 years ago / 1

    I'd use a 10V ceramic in 0805 or 0603 (standard not metric) SMT package

  • Sam Lanzo / about 12 years ago * / 1


    • Are SMD components out of the question? That would have the smallest profile for sure. If adding an smd part to your project isn't feasible go for a 10uF ceramic through hole part in a radial package. There are plenty on digikey; just search for 10uF ceramic capacitor and then filter for through hole devices. They're pretty cheap too, they'll run you about 30 cents each with a minimum order quantity of 5.

  • ThinkerT / about 12 years ago / 1

    If you used a lower sampling rate, a) would it sound awful and b) could you use a microphone to record, write to the SD, read, and play back?

    • Well pretty much by definition the lower the sampling rate the crappier this will sound. It really depends on the application and the audio your sending.
      To be honest I'm not sure if you could add a microphone and make recordings. You'd definitely have to be delicate with your code in order to maintain a consistent sampling rate while also writing data to the SD card; but I would guess that it would end up being pretty similar to the code for the playback demonstrated in this tutorial.
      Let us know if you tackle the project and send a video!

  • Taylor BMI / about 12 years ago / 1

    russosv: This is a great tutorial thanks!
    One question (hopefully not a stupid one!) I noticed you used an electrolytic cap for AC coupling- probably because it's impossible to find a non electrolytic cap at that capacity. I was just wondering if that's "bad" in general to do in my projects?
    The reason I ask, is that the purpose of the cap is to eliminate the DC bias... so does that mean that the current is alternating through the electrolytic cap?
    If it *is* "bad", then how is this type of coupling done in production circuits?

    It's not AC current that's the issue, the issue is whether the voltage across it changes polarity. (If you could only have current going across it one way, you'd have to throw out your capacitor after you charge it up because you couldn't discharge it :p) In this case the voltage on the DAC side is going to always be greater than the speaker side, so you should be okay.
    FWIW, there are ceramics of that size, there are also nonpolar electrolytic capacitors.

    • russosv / about 12 years ago / 1

      Thanks for the reply... that makes sense, thanks for clarifying this.
      I got a little confused, because doing a spice analysis of a similar circuit showed that the voltage on the speaker-side of the cap has it's DC component removed, and hence the AC signal had a negative voltage at some points. But I think this makes sense to me now, because the voltage at the positive end of the cap is always higher than at the negative side. Did I get it right?

  • Adrastos / about 12 years ago / 1

    Note that you don't need to migrate the entire project into C to take advantage of direct power manipulation, you can do that straight in the Arduino code (without doing digitalWrites). Just make sure to add the sbi and cbi macros in the sketch:

    define sbi(var, mask) ((var) |= (uint8_t)(1 << mask))

    define cbi(var, mask) ((var) &= (uint8_t)~(1 << mask))

    void setup()
    sbi(PORTC, 5);
    cbi(PORTC, 5);
    PORTD = 42;
    etc... The Arduino functions just make sure you don't screw yourself if you don't know what you're doing, or for just plain ease of use.

    • Right you are! I wanted to demonstrate the difference in speed for Arduino users who aren't aware of the fact that I/O pins can be manipulated by means other than a digitalWrite function in Arduino.
      It's also easier for me to manage interrupts when I write programs in C; Arduino seems to add some interrupts to manage the delay and PWM functions and I often find myself with interrupt routines that aren't functioning as I want them to.

    • Adrastos / about 12 years ago / 1

      ** port manipulation, not "power manipulation" :P

  • Taylor BMI / about 12 years ago / 1

    I'm looking at the data-sheet for the AD5330, and it says that it can drive 500Ohm outputs, the speaker has an impedance of 8ohms.
    From the looks of this, I would expect the AD5530 to be overheating, the voltages to be very saggy, or both.
    Are you seeing any of that, or is my analysis of the situation incorrect?

    • Hi,
      Nothing was heating up, and the voltage swing was going rail to rail; however you analysis likely explains the 'buzzing' in the speaker. You can hear it in the video that's now posted.

  • russosv / about 12 years ago / 1

    probably because it's impossible to find a non electrolytic cap at that capacity.
    Ok, maybe not impossible! I did a Mouser search and came up with a lot, but I think electrolytic caps are much more common at 10uF.

  • russosv / about 12 years ago / 1

    This is a great tutorial thanks!
    One question (hopefully not a stupid one!) I noticed you used an electrolytic cap for AC coupling- probably because it's impossible to find a non electrolytic cap at that capacity. I was just wondering if that's "bad" in general to do in my projects?
    The reason I ask, is that the purpose of the cap is to eliminate the DC bias... so does that mean that the current is alternating through the electrolytic cap?
    If it is "bad", then how is this type of coupling done in production circuits?

  • Good Tutorial!! I am working on the same thing for one of my own projects, too.
    An easy way to convert into 8 bit wave is using the Sound Recorder tool that comes with Windows. Go to Start > All Programs > Accessories > Entertainment > Sound Recorder. Then go to Edit > Insert File. When you are done, click Save as and change the format to 8 bit mono and whatever sample rate you desire.