Daniel Worrall
/
Recorder
Record audio data to a .wav file, complete with header, using the TLV320 CODEC and I2S port
main.cpp@0:e7efc8468066, 2011-08-05 (annotated)
- Committer:
- d_worrall
- Date:
- Fri Aug 05 15:00:51 2011 +0000
- Revision:
- 0:e7efc8468066
version 2.0
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
d_worrall | 0:e7efc8468066 | 1 | #include "mbed.h" |
d_worrall | 0:e7efc8468066 | 2 | #include "SDHCFileSystem.h" |
d_worrall | 0:e7efc8468066 | 3 | #include "MSCFileSystem.h" |
d_worrall | 0:e7efc8468066 | 4 | #include "TLV320.h" |
d_worrall | 0:e7efc8468066 | 5 | |
d_worrall | 0:e7efc8468066 | 6 | TLV320 audio(p9, p10, 52, p5, p6, p7, p8, p29); //TLV320 object |
d_worrall | 0:e7efc8468066 | 7 | SDFileSystem sd(p11, p12, p13, p14, "sd"); //SD Card object |
d_worrall | 0:e7efc8468066 | 8 | MSCFileSystem msc("usb"); //USB object |
d_worrall | 0:e7efc8468066 | 9 | InterruptIn finish(p16); //button to stop recording |
d_worrall | 0:e7efc8468066 | 10 | FILE *outfp; //file pointer object |
d_worrall | 0:e7efc8468066 | 11 | |
d_worrall | 0:e7efc8468066 | 12 | /* Buffers, pointers and states */ |
d_worrall | 0:e7efc8468066 | 13 | int circularBuffer[4096]; |
d_worrall | 0:e7efc8468066 | 14 | volatile int writePointer = 0; |
d_worrall | 0:e7efc8468066 | 15 | volatile int theta = 0; |
d_worrall | 0:e7efc8468066 | 16 | volatile bool recording = false; |
d_worrall | 0:e7efc8468066 | 17 | volatile long size = 36; |
d_worrall | 0:e7efc8468066 | 18 | |
d_worrall | 0:e7efc8468066 | 19 | /* Record settings */ |
d_worrall | 0:e7efc8468066 | 20 | unsigned int frequency = 8000; //sample rate |
d_worrall | 0:e7efc8468066 | 21 | unsigned short wordWidth = 16; //bits per sample per channel |
d_worrall | 0:e7efc8468066 | 22 | unsigned short channels = 2; //number of channels |
d_worrall | 0:e7efc8468066 | 23 | |
d_worrall | 0:e7efc8468066 | 24 | /* Function to read content of I2SRXFIFO into circular buffer */ |
d_worrall | 0:e7efc8468066 | 25 | void record(void){ |
d_worrall | 0:e7efc8468066 | 26 | audio.read(); |
d_worrall | 0:e7efc8468066 | 27 | circularBuffer[writePointer] = audio.rxBuffer[0]; |
d_worrall | 0:e7efc8468066 | 28 | circularBuffer[++writePointer] = audio.rxBuffer[1]; |
d_worrall | 0:e7efc8468066 | 29 | circularBuffer[++writePointer] = audio.rxBuffer[2]; |
d_worrall | 0:e7efc8468066 | 30 | circularBuffer[++writePointer] = audio.rxBuffer[3]; |
d_worrall | 0:e7efc8468066 | 31 | ++writePointer; |
d_worrall | 0:e7efc8468066 | 32 | theta += 4; |
d_worrall | 0:e7efc8468066 | 33 | if(writePointer > 4094) writePointer = 0; |
d_worrall | 0:e7efc8468066 | 34 | } |
d_worrall | 0:e7efc8468066 | 35 | |
d_worrall | 0:e7efc8468066 | 36 | /*Function to write data from circular buffer to usb storage */ |
d_worrall | 0:e7efc8468066 | 37 | void streamToFile(void){ |
d_worrall | 0:e7efc8468066 | 38 | static volatile int readPointer = 0; |
d_worrall | 0:e7efc8468066 | 39 | while(recording){ |
d_worrall | 0:e7efc8468066 | 40 | if(theta > 512){ // only start writing when there is enough data! |
d_worrall | 0:e7efc8468066 | 41 | fwrite(&circularBuffer[readPointer], 4, 128, outfp); //write data in 512 byte chunks, this is |
d_worrall | 0:e7efc8468066 | 42 | theta -= 128; //the natural usb memory chunk size |
d_worrall | 0:e7efc8468066 | 43 | readPointer += 128; |
d_worrall | 0:e7efc8468066 | 44 | if(readPointer > 4094) readPointer = 0; |
d_worrall | 0:e7efc8468066 | 45 | size += 512; |
d_worrall | 0:e7efc8468066 | 46 | } |
d_worrall | 0:e7efc8468066 | 47 | } |
d_worrall | 0:e7efc8468066 | 48 | //Complete file and header details |
d_worrall | 0:e7efc8468066 | 49 | fseek(outfp, 0, SEEK_END); |
d_worrall | 0:e7efc8468066 | 50 | for(int k = 0; k < theta; ++k){ |
d_worrall | 0:e7efc8468066 | 51 | fwrite(&circularBuffer[readPointer], 4, 1, outfp); //this writes the last of the data |
d_worrall | 0:e7efc8468066 | 52 | ++readPointer; //held in the circular buffer to memory |
d_worrall | 0:e7efc8468066 | 53 | if(readPointer > 4094) readPointer = 0; |
d_worrall | 0:e7efc8468066 | 54 | size += 4; |
d_worrall | 0:e7efc8468066 | 55 | } |
d_worrall | 0:e7efc8468066 | 56 | return; |
d_worrall | 0:e7efc8468066 | 57 | } |
d_worrall | 0:e7efc8468066 | 58 | |
d_worrall | 0:e7efc8468066 | 59 | /* Function to stop recording, namely to stop audio interrupts */ |
d_worrall | 0:e7efc8468066 | 60 | void stopRecording(void){ |
d_worrall | 0:e7efc8468066 | 61 | audio.stop(); |
d_worrall | 0:e7efc8468066 | 62 | recording = false; |
d_worrall | 0:e7efc8468066 | 63 | return; |
d_worrall | 0:e7efc8468066 | 64 | } |
d_worrall | 0:e7efc8468066 | 65 | |
d_worrall | 0:e7efc8468066 | 66 | /* Function to write .wav header */ |
d_worrall | 0:e7efc8468066 | 67 | void startHeader(void){ |
d_worrall | 0:e7efc8468066 | 68 | /* RIFF WAV header |
d_worrall | 0:e7efc8468066 | 69 | * --------------- |
d_worrall | 0:e7efc8468066 | 70 | * This is composed of several sub chunks. All strings |
d_worrall | 0:e7efc8468066 | 71 | * are in big endian form. All numeric data is in little endian form. |
d_worrall | 0:e7efc8468066 | 72 | * All data to be completed after recording (i.e. data chunk size) is default blank. |
d_worrall | 0:e7efc8468066 | 73 | */ |
d_worrall | 0:e7efc8468066 | 74 | char blockAlign = (char)channels * (wordWidth/8); |
d_worrall | 0:e7efc8468066 | 75 | int bps = (int)frequency * blockAlign; |
d_worrall | 0:e7efc8468066 | 76 | char header[44] = { 0x52, 0x49, 0x46, 0x46, //'RIFF' |
d_worrall | 0:e7efc8468066 | 77 | 0x00, 0x00, 0x00, 0x00, //file size |
d_worrall | 0:e7efc8468066 | 78 | 0x57, 0x41, 0x56, 0x45, //'WAVE' |
d_worrall | 0:e7efc8468066 | 79 | /*sub chunk 1*/ 0x66, 0x6d, 0x74, 0x20, //'fmt ' |
d_worrall | 0:e7efc8468066 | 80 | 0x10, 0x00, 0x00, 0x00, //subchunk size = 16 |
d_worrall | 0:e7efc8468066 | 81 | 0x01, 0x00, ((char)(channels & 0xff)), 0x00, //PCM compression code | number of channels - I assume no more than 2 channels |
d_worrall | 0:e7efc8468066 | 82 | ((char)((frequency & 0xff)>>0)), ((char)((frequency & 0xff00)>>8)), ((char)((frequency & 0xff0000)>>16)), ((char)((frequency & 0xff000000)>>24)), //sample rate |
d_worrall | 0:e7efc8468066 | 83 | ((char)((bps & 0xff)>>0)), ((char)((bps & 0xff00)>>8)), ((char)((bps & 0xff0000)>>16)), ((char)((bps & 0xff000000)>>24)), //bit rate |
d_worrall | 0:e7efc8468066 | 84 | blockAlign, 0x00, ((char)((wordWidth & 0xff)>>0)), ((char)((wordWidth & 0xff00)>>8)), //bloack align | wordwidth |
d_worrall | 0:e7efc8468066 | 85 | /*sub chunk 2*/ 0x64, 0x61, 0x74, 0x61, //'data' |
d_worrall | 0:e7efc8468066 | 86 | 0x00, 0x00, 0x00, 0x00}; //size of data chunk in bytes |
d_worrall | 0:e7efc8468066 | 87 | fwrite(header, 1, 44, outfp); |
d_worrall | 0:e7efc8468066 | 88 | } |
d_worrall | 0:e7efc8468066 | 89 | |
d_worrall | 0:e7efc8468066 | 90 | /* Function to complete header, once recording finishes */ |
d_worrall | 0:e7efc8468066 | 91 | void completeHeader(void){ |
d_worrall | 0:e7efc8468066 | 92 | fseek(outfp, 4, SEEK_SET); |
d_worrall | 0:e7efc8468066 | 93 | fputc((int) (0xff & size), outfp); //All these lines are needed to switch the endianess of the data |
d_worrall | 0:e7efc8468066 | 94 | fputc((int) ((0xff00 & size) >> 8), outfp); |
d_worrall | 0:e7efc8468066 | 95 | fputc((int) ((0xff0000 & size) >> 16), outfp); |
d_worrall | 0:e7efc8468066 | 96 | fputc((int) ((0xff000000 & size) >> 24), outfp); |
d_worrall | 0:e7efc8468066 | 97 | fseek(outfp, 40, SEEK_SET); |
d_worrall | 0:e7efc8468066 | 98 | size -= 36; |
d_worrall | 0:e7efc8468066 | 99 | fputc((int) (0xff & size), outfp); |
d_worrall | 0:e7efc8468066 | 100 | fputc((int) ((0xff00 & size) >> 8), outfp); |
d_worrall | 0:e7efc8468066 | 101 | fputc((int) ((0xff0000 & size) >> 16), outfp); |
d_worrall | 0:e7efc8468066 | 102 | fputc((int) ((0xff000000 & size) >> 24), outfp); |
d_worrall | 0:e7efc8468066 | 103 | } |
d_worrall | 0:e7efc8468066 | 104 | |
d_worrall | 0:e7efc8468066 | 105 | /* Main */ |
d_worrall | 0:e7efc8468066 | 106 | int main(){ |
d_worrall | 0:e7efc8468066 | 107 | /* Create a file to write to */ |
d_worrall | 0:e7efc8468066 | 108 | outfp = fopen("/usb/rec.wav", "w"); |
d_worrall | 0:e7efc8468066 | 109 | if(outfp == NULL){ |
d_worrall | 0:e7efc8468066 | 110 | perror("Error opening file!"); |
d_worrall | 0:e7efc8468066 | 111 | exit(1); |
d_worrall | 0:e7efc8468066 | 112 | } |
d_worrall | 0:e7efc8468066 | 113 | startHeader(); //write first half of header |
d_worrall | 0:e7efc8468066 | 114 | audio.power(0x02); //power up all TLV320, just not microphone |
d_worrall | 0:e7efc8468066 | 115 | audio.inputVolume(0.999, 0.999); //Set input volume to max - I don't like to use 1.0 due to rounding errors |
d_worrall | 0:e7efc8468066 | 116 | audio.frequency(frequency); |
d_worrall | 0:e7efc8468066 | 117 | audio.format(wordWidth, (2-channels)); //note int mode = 2 - channels |
d_worrall | 0:e7efc8468066 | 118 | audio.attach(&record); //attach record function to audio interrupt |
d_worrall | 0:e7efc8468066 | 119 | finish.rise(&stopRecording); //attach stop button |
d_worrall | 0:e7efc8468066 | 120 | for(int j = 0; j < 4096; ++j){ |
d_worrall | 0:e7efc8468066 | 121 | circularBuffer[j] = 0; //clear circular buffer |
d_worrall | 0:e7efc8468066 | 122 | } |
d_worrall | 0:e7efc8468066 | 123 | recording = true; |
d_worrall | 0:e7efc8468066 | 124 | audio.start(RECEIVE); //start recording |
d_worrall | 0:e7efc8468066 | 125 | streamToFile(); |
d_worrall | 0:e7efc8468066 | 126 | completeHeader(); //once finished recording complete the header |
d_worrall | 0:e7efc8468066 | 127 | fclose(outfp); //and close the file |
d_worrall | 0:e7efc8468066 | 128 | } |