Introduction: Playing Audio Sound Files ( Wav ) With an Arduino and a DAC

Play wav file Audio from your Audino SD card.
This Instructable will show you how a wav file on your SdCard can be played through a simple circuit to a speaker.

The wav file must be 8 bit mono. I have had no problem playing 44 KHz files.

Whilst not hi-fidelity, the sound quality is very satisfactory.

The serial monitor is used to select the file. The files must be in a folder called adlog.

This instructable follows from an earlier project where I saved wav recordings to the SdCard: https://www.instructables.com/id/Arduino-Mega-Audio...

The circuit uses a cheap 8 bit digital to analog converter (DAC) and a single chip audio amplifier.

Key sections for setting up interrupts were taken from the excellent article by Amanda Ghassaei: https://www.instructables.com/id/Arduino-Timer-Inte...

Attachments

Step 1: Requirements

Arduino- I use the Mega, however there is no reason why the Uno should not work.

SdCard reader- the program is configured for the: MicroSD Breakout Board Regulated with Logic Conversion V2
http://www.hobbytronics.co.uk/microsd-card-regula....

.

See this instructable for SdCard setup details : https://www.instructables.com/id/Arduino-Mega-Audio...

.

DAC0832 LCN- an excellent 8 bit digital to analogue converter- A few pounds.

LM386 N-1 Op amp- cheap as chips

20 way chip socket

8 way chip socket

9 volt power supply- a battery will do.

LM336 2.5 V voltage reference

10uF Capacitor * 3 (any voltage more than 9V)

10 ohm resistor

50nF capacitor- (Or somewhere near-47nF, 56nf, 68nf- will do)

220uF Capacitor

64 ohm speaker

10K linear potentiometer

Cable to link the 8 data lines between the Arduino and the circuit-

On the Uno the 8 connections are in line, on the Mega they are in pairs.

On the Mega I used 10 way ribbon cable with a 10 way IDC header. (2 wires are spare)

Socket connectors for 0V, 9V and DAC out

Copper strip board, solder, wire,cutters etc

Step 2: The Specifications

Serial set at 115200 baud.

Support is in place for the Hobbytronics MicroSD Breakout Board using a Mega. The chip select and other ports will alter between Mega and Uno.

The Wav files must exist in a directory called adlog- Feel free to name it something else and re-arrange the necessary coding.

The wav file must be 8 bit mono. I have tested up to 44KHz.

The Serial monitor displays the wav files in the adlog folder. File names are sent from the monitor output line.

File size is only limited by SdCard size.

Step 3: Getting Started

Connect the SD card reader. These are the connections for the Mega.

0, 5V

CLK to pin 52

D0 to pin 50

D1 to pin 51

CS to pin 53

(See suppliers website for Uno port connection)

You will want to test that your card works at this stage- use the scripts supplied by the vendor.

.

We need to make a small circuit.

We are going to send a stream of audio bytes from the Arduino.

These numbers are between 0 and 255. They represent the voltage.

Silence is 127-128.

255 is speaker cone hard one way.

0 is speaker cone hard the other way.

So audio is recorded as saved numbers, which create varying voltages, which create moving speaker cones.

.

We can send the numbers out of 8 lines on the Arduino, simultaneously, by using a "port".

If we feed the 8 lines into a digital to analogue converter, it does what it says on the tin and produces an analogue voltage which is proportional to the digital number.

All we need to do then is pack the voltage off to a small operational amplifier and then to a speaker.

Step 4: The Small Circuit

The DAC0832 LCN

This is a superb, cheap 8 bit Digital to analogue converter. (DAC)

It can be fully controlled with an array of data hold, data sample lines.

Or it can be setup to do it all automatically in "Flow through operation".

To quote the manual:

Simply grounding CS, WR1, WR2, and XFER and tying ILE high allows both internal registers to follow the applied digital inputs (flow-through) and directly affect the DAC analog output.

OK that is four connections to the chip set low and one set to 9V - easy.

We do not want any negative voltages out so the manual says we should use "voltage switching mode" and they supply the diagram.

All we need to do is substitute a small Audio amp instead of the one they suggest.

The LM386-N Audio Amp

The Amp's manual provides a minimum parts diagram- providing a gain of 20 (Way too much for us-but it has a volume control).

All we need to do is add a capacitor between the DAC and the amp so that we only amplify AC signals.

We must also add a couple of capacitors close to the supply pin of each of our chips otherwise we will get hum from our 9V supply.

Step 5: Get Out the Soldering Iron

As the circuit is simple I do not intend giving a blow by blow account.

Here are some pointers:

  • Prepare a piece of Copper strip board at least 28 by 28 holes. (Yes I know brain surgeons can make it smaller)
  • If you intend mounting it with screws, allow for them at the start!
  • Mount the chips on sockets. Insert the chips only when everything has been checked.
  • Keep the input wires away from the output.
  • Observe the correct polarity for the capacitors.
  • Refer to the diagram for the base view of the LM336 voltage reference. The adjust leg is not used and can be cut.
  • Note the direct connection to pin 8 of the DAC- It is very useful for testing.
  • I connected to the Audino with ribbon cable and a 10 way IDC connecter.
  • On the Uno the connections are in a straight line - you may find that arranging the 8 input connections in a single straight line allows you to link to the Arduino with a purchased, ready made 8 way connecter,

When its done- check the soldering and check the gaps between the copper tracks.

.

I find a 36 tpi junior hack saw blade very useful for clearing debris. I remove the blade's locating pins and slide the tip of the blade into the track- Obviously the blade is not in a frame.

Step 6: Testing the DAC

Leave the Connection beween the Circuit and the Arduino off.

Set the volume control on your circuit to midway.

Switch on the 9V DC Power to your new circuit.

Check that the circuit is ok- I can not accept any liability for your circuit!

.

Power off.

Connect your circuit to the Arduino.

On the Mega use pins 22-29. (PORTA) Do not mistake the two 5V pins above!

On the Uno use pins 0-7. This is PORTD

Connect the 0V of your power supply to the 0V on the Arduino.

Power up.

Open this test program DAC_TEST

For the UNO, replace all references to PORTA to PORTD

Replace DDRA with DDRD- this instruction sets all 8 lines to output in one go. This is the data direction register.

Set your serial monitor at 115200.

Connect a voltmeter between the DAC out and OV

The program will set the output to 255- all lines on - maximum voltage.

Output 128- half maximum voltage.

Output 0- zero voltage (Or probably nearly zero).

It will then step bitwise: 1, 2, 4, 8,16, 32, 64, 128

The voltage should increase steadily.

If the voltage drops back whilst the number increases you probably have two of the interconnecting wires reversed.

.

You should also hear the speaker quietly clicking as the voltage changes

Step 7: Reading the Wav Header

Wav files are saved with a specified frequency and data size.

This information is contained in a 44 byte header at the start of a wav file.

Although some software extends the header (after byte 35), making the location of the data size more difficult to locate.

To read the header we create a buffer and copy the start of the file.

The frequency is stored in 4 bytes starting 24 bytes into the file.

// read frequency specified in wav file header

byte headbuf[60]

tempfile.seek(0);

tempfile.read(headbuf,60);

retval=headbuf[27];

retval=(retval<<8) | headbuf[26];

retval=(retval<<8) | headbuf[25];

retval=(retval<<8) | headbuf[24];

Serial.print(F("File Frequency "));

Serial.print(retval);

.

The best way to find the data size information is to search for the word "data" in the header.

Then extract the 4 bytes following it, that make up the long value

unsigned long retval;

int mypos=40;

for (int i=36; i<60;i++) {

if (headbuf[i] == 'd') {

if(headbuf[i+1]=='a') {

if(headbuf[i+2]=='t') {

if(headbuf[i+3]=='a') {

// at last we have it

mypos=i+4;

i=60;

}

}

}

}

}

tempfile.seek(mypos);

retval=headbuf[mypos+3];

retval=(retval<<8) | headbuf[mypos+2];

retval=(retval<<8) | headbuf[mypos+1];

retval=(retval<<8) | headbuf[mypos];

.

OK we have the data length and frequency!

The audio data follows the 4 bytes making up the data length value.

Step 8: Interrupt, Interrupt....

We use the frequency information to create a software interrupt at , or near, the required frequency.

The interrupt can not always be set precisely, but it is sufficient. The frequency read from the file is passed to the setintrupt subroutine.

void setintrupt(float freq){
float bitval=8; // 8 for 8 bit timers 0 and 2, 1024 for timer 1 byte

setocroa=(16000000/(freq*bitval)) - 0.5;

// The setocroa value requires a subtraction of -1. However adding 0.5 rounds to nearest 0.5

// The resolution of the timer is limited

// Ultimately determined by magnitude of bitval

cli(); // disable interrupts
// set timer2 interrupt

TCCR2A = 0; // set entire TCCR2A register to 0

TCCR2B = 0; // same for TCCR2B

TCNT2 = 0; // initialize counter value to 0

// set compare match register for frequency (hz) increments

OCR2A = setocroa; // = (16*10^6) / (frequency*8) - 1 (must be <256)

// turn on CTC mode

TCCR2A |= (1 << WGM21); // Set CS21 bit for 8 prescaler

TCCR2B |= (1 << CS21); // enable timer compare interrupt

// TIMSK2 |= (1 << OCIE2A); // this works, as does the following line

sbi(TIMSK2,OCIE2A); // enable interrupt on timer 2

sei(); // enable interrupts

.

Discerning readers will have spotted sbi(TIMSK2,OCIE2A)

I setup a couple of (internet acquired) functions for setting and clearing register bits:

// Defines for clearing register bits
#ifndef cbi

#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))

#endif

// Defines for setting register bits

#ifndef sbi

#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))

#endif

These functions provide an easy call to set or clear the interrupt.

.

So the interrupt is running, what can we make it do?

Step 9: Interrupts and Double Buffering

At 22 Khz a byte of audio data is outputted every 0.045 ms

512 bytes (the buffer size) is read in 2.08 ms.

So the buffer can not be read from the SDCard in one write cycle.

However 512 bytes are written to the port in 23.22ms.

So all we have to do is setup a new file read every time the buffer empties and we have enough time to get the data before a new data block is required... Assuming we use two buffers, emptying one as we fill another.

This is double buffering.

The file read will be slowed by the repeated interrupt, but it will get done.

.

I have setup two 512 byte buffers called bufa and bufb.

If the flag aready is true we read from porta otherwise we read from portb

When the buffer position (bufcount) reaches the buffer size (BUF_SIZE 512) we set a flag called readit to true.

The void loop routine looks for this flag and starts a block read:

if(readit){
if (! aready){

// initiate SDCard block read to bufa

tempfile.read(bufa, BUF_SIZE);

} else {

// initiate SDCard block read to bufb

tempfile.read(bufb, BUF_SIZE);

}

readit=false;

}

When it has finished the routine flags readit=false.

Within the interrupt routine we must check that the void loop has finished by checking if readit== false.

This being the case we signal that another read is required and toggle the aready flag to switch buffers.

If the SDcard is still reading we have to back track one reading (counter--; bufcount--;) and exit the interrupt to try again later. (Clicks in the audio output signal imply that this has occurred.)

When all the data is read the interrupt is cancelled, the port re-set to the mid voltage value of 128 and the audio file closed.

.

Before running the dac2.ino script for the first time, set your volume to 50%. This will be too loud, but it is better than 100%!

If your volume control works in reverse swap the leads at opposite ends of the 10K potentiometer.

Let me know how it sounds.