Playing Music
Background
This is a follow on from the SD Card project which gives access to a volume filing and storage medium, ideal for keeping music.
Music Source
I'm using WaveLab Lite, which I've had for ages. This has the capability of creating a wav file containing whatever sounds I put in from CD, PC file, line input with my choice of mono/stereo, 8 bit/16 bit and a selection of sample rates including user specified. The wav file saves to the root of the SD Card and becomes available to the mbed via an SD socket and electric string as an SPI peripheral.
Story so far
Not good, yet! As a first step, I have been investigating wav file format and have produced a routine to read the header information and display it on a Nokia LCD Screen. This bit works.
Header Reader Code
/*Header Reader. This reads the header of a WAV file stored on
an SD Card and displays the detail on a Nokia LCD screen.
*/
#include "mbed.h"
#include "SDFileSystem.h"
#include "MobileLCD.h"
MobileLCD lcd(5, 6, 7, 8, 9);
SDFileSystem sd(11, 12, 13, 14, "sd");
int main() {
lcd.background(0x0000FF);
lcd.cls();
lcd.printf("Header Reader");
lcd.locate(0,2);
int ch;
FILE *fp = fopen("/sd/QI2.wav", "r");
if(fp == NULL) {
error("Could not open file for write\n");
lcd.printf("Could not open file for read");
}
for(int i = 1; i < 5; i++) { //"RIFF"
ch = fgetc(fp);
lcd.printf("%c ", ch);
if(i == 4) {lcd.locate(0,3);}
}
for(int i = 5; i < 9; i++) { //Chunk size
ch = fgetc(fp);
lcd.printf("%x ", ch);
if(i == 8) {lcd.locate(0,4);}
}
for(int i = 9; i < 13; i++) { //"WAVE"
ch = fgetc(fp);
lcd.printf("%c ", ch);
if(i == 12) {lcd.locate(0,5);}
}
for(int i = 13; i < 17; i++) { //ID = "fmt"
ch = fgetc(fp);
lcd.printf("%c ", ch);
if(i == 16) {lcd.locate(0,6);}
}
for(int i = 17; i < 21; i++) { //Chunk Size
ch = fgetc(fp);
lcd.printf("%x ", ch);
if(i == 20) {lcd.locate(0,7);}
}
for(int i = 21; i < 23; i++) { //Audio format
ch = fgetc(fp); //1 = Uncompressed
lcd.printf("%x ", ch);
if(i == 22) {lcd.locate(0,8);}
}
for(int i = 23; i < 25; i++) { //Channels
ch = fgetc(fp); //1 = mono, 2 = stereo
lcd.printf("%x ", ch);
if(i == 24) {lcd.locate(0,9);}
}
for(int i = 25; i < 29; i++) { //Sample Rate
ch = fgetc(fp);
lcd.printf("%x ", ch);
if(i == 28) {lcd.locate(0,10);}
}
for(int i = 29; i < 33; i++) { //Byte Rate
ch = fgetc(fp);
lcd.printf("%x ", ch);
if(i == 32) {lcd.locate(0,11);}
}
for(int i = 33; i < 35; i++) { //Align
ch = fgetc(fp);
lcd.printf("%x ", ch);
if(i == 34) {lcd.locate(0,12);}
}
for(int i = 35; i < 37; i++) { //Bits per Sample
ch = fgetc(fp); //8 = 8 bit, 16 = 16 bit
lcd.printf("%d ", ch);
if(i == 36) {lcd.locate(0,13);}
}
for(int i = 37; i < 41; i++) { //"data"
ch = fgetc(fp);
lcd.printf("%c ", ch);
if(i == 40) {lcd.locate(0,14);}
}
for(int i = 41; i < 45; i++) { //data chunk size
ch = fgetc(fp);
lcd.printf("%x ", ch);
}
fclose(fp); //close the file
lcd.locate(0,15);
lcd.printf("file closed");
}
My first attempt at playing a wav file produced an unpleasant buzzing from the piezo disc. Perhaps I need something with a bit more fidelity but the scope picture was ugly too. The wav file was mono, 16 bit and 44.1k sample rate. The code to do this is a bit basic, which is probably why it's rubbish. I tried 11k sample rate but got much the same result.
Play Music Code
/*Play Music. This is a first (unsuccessful) attempt at playing a WAV file.
Data file is stored on an SD Card. Format is WAV, Mono, 16 bit, 11k samples/s
The output is AnalogOut on pin 18.
The transducer is a piezo disc as supplied by mbed.
I have ignored the header and played that too.
*/
#include "mbed.h"
#include "SDFileSystem.h"
SDFileSystem sd(11, 12, 13, 14, "sd");
AnalogOut aout(18);
int main() {
int ch; //first read byte
int ch2; //second read byte
int dat; //combined 16 bit word for DtoA
FILE *fp = fopen("/sd/QI2.wav", "r"); //open File to read
if(fp == NULL) {error("Could not open file for write\n");}
while(!feof(fp)) { //Play the music until End Of File
dat = 0;
ch = fgetc(fp); //get first byte
ch2 =fgetc(fp); //get second byte
ch = ch << 8; //shift bits 8 places to left
dat = ch2 | ch; //combine bits to single 16 bit int
aout.write_u16((unsigned short)(dat)); //DtoA conversion
wait_us(88); //set for 11k sample rate
}
fclose(fp); //close the file
}
Simon suggested that as this is unbuffered SD data directly to DtoA, there may be timing problems with the SD access interfering with the DtoA timing. Mmm.

