Record audio data to a .wav file, complete with header, using the TLV320 CODEC and I2S port

Dependencies:   mbed

main.cpp

Committer:
d_worrall
Date:
2011-08-05
Revision:
0:e7efc8468066

File content as of revision 0:e7efc8468066:

#include "mbed.h"
#include "SDHCFileSystem.h"
#include "MSCFileSystem.h"
#include "TLV320.h"
 
TLV320 audio(p9, p10, 52, p5, p6, p7, p8, p29);         //TLV320 object
SDFileSystem sd(p11, p12, p13, p14, "sd");              //SD Card object
MSCFileSystem msc("usb");                               //USB object
InterruptIn finish(p16);                                //button to stop recording                     
FILE *outfp;                                            //file pointer object

/* Buffers, pointers and states */
int circularBuffer[4096];    
volatile int writePointer = 0;
volatile int theta = 0;
volatile bool recording = false;
volatile long size = 36;

/* Record settings */
unsigned int frequency = 8000;                         //sample rate
unsigned short wordWidth = 16;                          //bits per sample per channel
unsigned short channels = 2;                            //number of channels

/* Function to read content of I2SRXFIFO into circular buffer */
void record(void){ 
    audio.read();
    circularBuffer[writePointer] = audio.rxBuffer[0];
    circularBuffer[++writePointer] = audio.rxBuffer[1];
    circularBuffer[++writePointer] = audio.rxBuffer[2];
    circularBuffer[++writePointer] = audio.rxBuffer[3];
    ++writePointer;
    theta += 4;  
    if(writePointer > 4094) writePointer = 0;
}

/*Function to write data from circular buffer to usb storage */
void streamToFile(void){
    static volatile int readPointer = 0;
    while(recording){
        if(theta > 512){                                                // only start writing when there is enough data!
            fwrite(&circularBuffer[readPointer], 4, 128, outfp);        //write data in 512 byte chunks, this is
            theta -= 128;                                               //the natural usb memory chunk size
            readPointer += 128;
            if(readPointer > 4094) readPointer = 0; 
            size += 512;      
        }
    }
    //Complete file and header details
    fseek(outfp, 0, SEEK_END);
    for(int k = 0;  k < theta; ++k){
        fwrite(&circularBuffer[readPointer], 4, 1, outfp);              //this writes the last of the data
        ++readPointer;                                                  //held in the circular buffer to memory
        if(readPointer > 4094) readPointer = 0; 
        size += 4;       
    }
    return;
}

/* Function to stop recording, namely to stop audio interrupts */
void stopRecording(void){
    audio.stop();
    recording = false;
    return;
}

/* Function to write .wav header */
void startHeader(void){
/* RIFF WAV header
 * ---------------
 * This is composed of several sub chunks. All strings
 * are in big endian form. All numeric data is in little endian form.
 * All data to be completed after recording (i.e. data chunk size) is default blank.
 */
    char blockAlign = (char)channels * (wordWidth/8);
    int bps = (int)frequency * blockAlign;
    char header[44] = { 0x52, 0x49, 0x46, 0x46,     //'RIFF'
                        0x00, 0x00, 0x00, 0x00,     //file size
                        0x57, 0x41, 0x56, 0x45,     //'WAVE'
       /*sub chunk 1*/  0x66, 0x6d, 0x74, 0x20,     //'fmt '
                        0x10, 0x00, 0x00, 0x00,     //subchunk size = 16
                        0x01, 0x00, ((char)(channels & 0xff)), 0x00,     //PCM compression code | number of channels - I assume no more than 2 channels
                        ((char)((frequency & 0xff)>>0)), ((char)((frequency & 0xff00)>>8)), ((char)((frequency & 0xff0000)>>16)), ((char)((frequency & 0xff000000)>>24)),   //sample rate
                        ((char)((bps & 0xff)>>0)), ((char)((bps & 0xff00)>>8)), ((char)((bps & 0xff0000)>>16)), ((char)((bps & 0xff000000)>>24)),     //bit rate
                        blockAlign, 0x00, ((char)((wordWidth & 0xff)>>0)), ((char)((wordWidth & 0xff00)>>8)),   //bloack align | wordwidth
       /*sub chunk 2*/  0x64, 0x61, 0x74, 0x61,     //'data'
                        0x00, 0x00, 0x00, 0x00};    //size of data chunk in bytes
     fwrite(header, 1, 44, outfp);                   
}

/* Function to complete header, once recording finishes */
void completeHeader(void){
    fseek(outfp, 4, SEEK_SET);
    fputc((int) (0xff & size), outfp);              //All these lines are needed to switch the endianess of the data
    fputc((int) ((0xff00 & size) >> 8), outfp);
    fputc((int) ((0xff0000 & size) >> 16), outfp);
    fputc((int) ((0xff000000 & size) >> 24), outfp);
    fseek(outfp, 40, SEEK_SET);
    size -= 36;
    fputc((int) (0xff & size), outfp);
    fputc((int) ((0xff00 & size) >> 8), outfp);
    fputc((int) ((0xff0000 & size) >> 16), outfp);
    fputc((int) ((0xff000000 & size) >> 24), outfp);
}

/* Main */
int main(){   
    /* Create a file to write to */
    outfp = fopen("/usb/rec.wav", "w");
    if(outfp == NULL){
        perror("Error opening file!");
        exit(1);
    }
    startHeader();                          //write first half of header
    audio.power(0x02);                      //power up all TLV320, just not microphone
    audio.inputVolume(0.999, 0.999);        //Set input volume to max - I don't like to use 1.0 due to rounding errors
    audio.frequency(frequency);             
    audio.format(wordWidth, (2-channels));  //note int mode = 2 - channels
    audio.attach(&record);                  //attach record function to audio interrupt
    finish.rise(&stopRecording);            //attach stop button
    for(int j = 0; j < 4096; ++j){
        circularBuffer[j] = 0;              //clear circular buffer
    }
    recording = true;
    audio.start(RECEIVE);                   //start recording
    streamToFile();                             
    completeHeader();                       //once finished recording complete the header
    fclose(outfp);                          //and close the file
}