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

source:PlayMusic/Docs/Header_Reader.jpg

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.