VLSI Solution Oy VLSI Solution Oy Evaluation MP3 Player Source Code Documentation

Main Page | Class List | File List | Class Members | File Members | Related Pages

record.c File Reference

ADPCM Recording. More...

#include "record.h"
#include "filesys.h"
#include "storage.h"
#include "vs10xx.h"
#include "console.h"
#include "ui.h"
#include "display.h"

Include dependency graph for record.c:

Include dependency graph

Go to the source code of this file.

Functions

unsigned char Record ()
 Record ADPCM.


Variables

xdata unsigned char SPMax
code const unsigned char RIFFHeader0 []
 first part of RIFF Header, insert 444 zeroes after this

code const unsigned char RIFFHeader504 []
 second part of RIFF Header, bytes 504...511


Detailed Description

ADPCM Recording.

Definition in file record.c.


Function Documentation

unsigned char Record  ) 
 

Record ADPCM.

Recording is probably the least straightforward task with vs10xx, though it's not difficult! The main tasks are:

  • prepare storage space to save recording
  • write a strictly special kind of RIFF header to start of file
    Warning:
    the RIFF header must be of the kind stated in datasheet!
  • set sampling speed (Clk/256 divider at SPI_AICTRL0)
    • divider 4 is minimum, giving:
      • 24kHz @ 24.576MHz
      • 42kHz with VS1003, 12.288Mhz xtal, clock multiplier 3.5
    • divider 12 gives 8kHz @ 24.576Mhz
    • remember to put the correct sample rate in the RIFF header too!
  • set recording level (gain at SPI_AICTRL1)
    • this is digital gain, depends on mic signal level
      • mic/line input impedance is 100 kilo-ohms
      • for VS1002, 100...150mVpp signal level gives best resolution
      • for VS1003, 50...100mVpp is best for mic, 2Vpp for line in
    • 0x1000 is a good first guess for the gain value
    • value 0 sets autogain
  • SPI_VOLUME register controls the loopback volume to earphones
  • reset vs10xx with ADPCM recording mode bit SM_ADPCM (SPI_MODE.12) set
    • for vs1003, to select line in instead of mic, set SPI_MODE.14
  • monitor ADPCM_DATA_AVAILABLE level in SPI_HDAT1
  • when data is available, read it into a disksector buffer
    • data is provided in 256-byte (128-word) blocks
      • overfilling buffer resets and aligns automatically to block boundary
      • the fourth byte in a block is always 0x00.
      • check big-endianness / little-endianness for your processor
  • Be relaxed with your timing when reading the SCI registers, give VS10xx a few microseconds time to update the register value after each read. You might need to slower your SPI clock slightly since VS10xx can accept SCI data at slightly faster rate than it is able to provide it.
  • save disksector buffers to disk
  • at end of recording, set SM_OUT_OF_WAV / do a reset of vs10xx
  • insert correct length information in the RIFF HEADER
  • finalize diskfile (write FAT records and dir entry)

Remarks:
in this test version, recording more is entered by pressing the LEFT button while booting and then releasing it after "Filesys OK" message

this version can only record continuous fragments, If a fragment boundary is reached, recording will stop. The main player loop is constructed so that in this situation record() is called again to continue recording to a new file.

Most MMC handling issues (bugs) seem to be corrected now but the filesystem code might still overwrite files or corrupt FAT tables in some type of MMC card. You are warned, this is beta quality software :)

Todo:
VS1002 recording: only new sample rate values need to be calculated.

This recording function can only save data into a continuous area on disk. The Fragment Table fragment[] is used in a special way for recording (to save microcontroller RAM space). fragment[0].start is the first disk sector of the file fragment[0].length will contain the number of disk sectors for the file.

Bug:
In case of fragmented filesystem, this version will not function properly.

The RIFF header is constructed in a special way (to make life a bit easier). The most proper way to do it would be to take the header "as is" from the datasheet. But to make life a bit easier, the header here is extended to be 512 bytes (a disk sector) long by adding zeroes to the 'fact' chunk. 'junk' chunks should not be used.

The preliminary header is written to disk prior to recording (to give some chances of recovering track in case of battery failure etc). It has a ridiculously long track length. After recording, the header must be rewritten with correct track length.

Todo:
second FAT table update

Definition at line 101 of file record.c.

References AvailableProcessorTime(), Temp::b, DiskBlock::Raw::buf, Temp::c, dataBufPtr, Delay(), diskSect, displayValue, DS_STATIC, fatSectorsPerCluster, fragment, freeSector, GREEN_LED, Temp::i, InitDisplay(), KEY_BUTTON, Address::l, Temp::l, LED_OFF, LED_ON, fragmentEntry::length, Mp3DeselectControl, Mp3ReadRegister(), Mp3Reset(), Mp3WriteRegister, playingState, PS_NEXT_SONG, PS_RECORDING, DiskBlock::raw, ReadDiskSector(), RIFFHeader0, RIFFHeader504, ScanForFreeSector(), sectorAddress, SPI_AICTRL0, SPI_AICTRL1, SPI_CLOCKF, SPI_DECODE_TIME, SPI_HDAT0, SPI_HDAT1, SPI_MODE, fragmentEntry::start, temp, UI_TITLE, uiMode, WriteClusterChain(), and WriteDiskSector().

Referenced by main().

00101 { 00102 00103 xdata unsigned char blockNumber; 00104 xdata unsigned long sectorCount; 00105 xdata unsigned long lastSector; 00106 bit stopRecording = 0; 00107 bit continueRecording = 0; 00108 00109 char oldlevel=0; 00110 blockNumber = 0; 00111 sectorCount = 1; 00112 00113 playingState = PS_RECORDING; //Inform the world that rec mode is on. 00114 00115 // Kick vs10xx into action! 00116 00117 /* Set clock register, doubler etc. */ 00118 #ifdef VS1003 00119 // Set base xtal 14.7456MHZ (0x0696) | multiplier:fixed 2.5 (0x600) 00120 Mp3WriteRegister(SPI_CLOCKF,0x66,0x96); 00121 #else 00122 Mp3WriteRegister(SPI_CLOCKF,156,204); //for 14.576MHz XTAL 00123 #endif 00124 00125 //Set sample rate 16khz for VS1003 (6.4kHz for VS1002 :( ) 00126 Mp3WriteRegister(SPI_AICTRL0,0x00,0x09); //Set sample rate divider=9 00127 Mp3WriteRegister(SPI_AICTRL1,0x10,0x00); //AutoGain OFF, reclevel 0x1000 00128 Mp3WriteRegister(SPI_MODE,0x18,0x04); //RECORD,NEWMODE,RESET 00129 Delay(10); 00130 00131 /* Set clock register, doubler etc. */ 00132 #ifdef VS1003 00133 // Set base xtal 14.7456MHZ (0x0696) | multiplier:fixed 2.5 (0x600) 00134 Mp3WriteRegister(SPI_CLOCKF,0x66,0x96); 00135 #else 00136 Mp3WriteRegister(SPI_CLOCKF,156,204); //for 14.576MHz XTAL 00137 #endif 00138 00139 00140 // Locate free space 00151 freeSector = 0; 00152 ScanForFreeSector(); 00153 sectorAddress.l = freeSector; 00154 fragment[0].start = freeSector; 00155 00156 00164 for (temp.c=0; temp.c<56; temp.c++){ //temp is unsafe global temp variable. 00165 diskSect.raw.buf[temp.c] = RIFFHeader0[temp.c]; 00166 } 00167 for (temp.i=52; temp.i<504; temp.i++){ 00168 diskSect.raw.buf[temp.i] = 0; 00169 } 00170 for (temp.i=504; temp.i<512; temp.i++){ 00171 diskSect.raw.buf[temp.i] = RIFFHeader504[temp.i-504]; 00172 } 00173 00180 lastSector = freeSector; 00181 WriteDiskSector(freeSector); 00182 00183 /* Get a new free sector */ 00184 ScanForFreeSector(); 00185 sectorAddress.l = freeSector; 00186 dataBufPtr = diskSect.raw.buf; 00187 00188 Delay(10); 00189 //Sync to incoming audio frame... 00190 while (Mp3ReadRegister(SPI_HDAT1)>>8) //lots of data in buffer 00191 ; //Wait until buffer level restarts from 0 00192 00193 dataBufPtr = diskSect.raw.buf; //reset dataBufPtr to start of filebuffer 00194 blockNumber = 0; 00195 00196 ConsoleWrite("\rRecording, push button to stop..."); 00197 while((!KEY_BUTTON 00198 || (blockNumber!=0) 00199 || ((sectorCount)%(fatSectorsPerCluster)!=0)) 00200 && (!stopRecording)){ 00201 00202 GREEN_LED = LED_ON; 00203 00204 //Check for data 00205 if (Mp3ReadRegister(SPI_HDAT1) >= 128){ 00206 //there is a data block to be read... 00207 GREEN_LED = LED_OFF; 00208 blockNumber++; 00209 00210 //dataBufPtr should point inside disk buffer, this should never fail: 00211 if (dataBufPtr>(diskSect.raw.buf+511)){ 00212 ConsoleWrite("\rBuffer indexing error. Stop.\r"); 00213 while(1); //stop 00214 } 00215 00216 //Get the data 00217 for (temp.c=0;temp.c<128;temp.c++){ 00218 data unsigned int i; 00219 i = Mp3ReadRegister(SPI_HDAT0); 00220 *dataBufPtr++ = (i>>8); 00221 *dataBufPtr++ = (i&0xff); 00222 } 00223 00224 00225 { //Do basic SOUND LEVEL BAR calculation based on the 1 linear 00226 //sample in block[0] and [1] 00227 signed int soundlevel; 00228 // the user interface (level bar) calculation 00229 if (uiMode == UI_TITLE){ 00230 soundlevel = (signed char)diskSect.raw.buf[1]<<7; 00231 soundlevel |= diskSect.raw.buf[0]>>1; 00232 if (soundlevel<0) soundlevel = -soundlevel; 00233 displayValue=0; 00234 while (soundlevel>31){ 00235 displayValue++; 00236 soundlevel>>=1; 00237 } 00238 if (soundlevel>19) displayValue++; 00239 if (soundlevel>12) displayValue++; 00240 if (soundlevel>6) displayValue++; 00241 displayValue-=3; 00242 displayValue*=13; 00243 if (oldlevel>displayValue){ 00244 displayValue=oldlevel-3; 00245 } 00246 oldlevel=displayValue; 00247 } 00248 AvailableProcessorTime(); 00249 00250 }//SOUND LEVEL BAR calculation 00251 00252 }//if there was data to read 00253 00254 00255 //Release SCI chip select, we might want to use MMC card 00256 Mp3DeselectControl(); 00257 00258 if (blockNumber==2){ //2 blocks (512 bytes) received, write to disk... 00259 00260 //If we update the SPI_DECODE_TIME, it will show on player screen :) 00261 //we can do it, no problem. Here is the calculation for 16khz; 00262 //for 16kHz sample rate, 505 samples/block, 8 disk sectors are 00263 //exactly 505 milliseconds. 00264 temp.l = (sectorCount/8) * 505; 00265 temp.l /= 1000; 00266 Mp3WriteRegister(SPI_DECODE_TIME,temp.b.b1,temp.b.b0); 00267 Mp3DeselectControl(); 00268 00269 blockNumber = 0; 00270 sectorCount++; 00271 WriteDiskSector(sectorAddress.l); 00272 lastSector = freeSector; 00273 ScanForFreeSector(); //this would be the proper way to go... 00274 sectorAddress.l = freeSector; //keep all variables in proper values 00275 dataBufPtr = diskSect.raw.buf; //reset data buffer pointer 00276 if (freeSector!=(lastSector+1)){ //end of continuous space, must break! 00277 stopRecording = 1; 00278 ConsoleWrite("\nFragment end - can't continue recording!\n"); 00279 InitDisplay(DS_STATIC,"FRAGMENT"," LIMIT!!",0); 00280 continueRecording = 1; 00281 } 00282 } 00283 00284 displayValue = 0; 00285 00286 }//while not button - stop recording when BUTTON is pressed 00287 fragment[0].length = sectorCount; 00288 00289 00290 //Enter size information to RIFF header.. 00291 { 00292 xdata addressType size; 00293 00294 ReadDiskSector(fragment[0].start); 00295 00296 //Patch in number of samples 00297 size.l = (sectorCount-1)*1010; 00298 diskSect.raw.buf[48] = size.b.b0; 00299 diskSect.raw.buf[49] = size.b.b1; 00300 diskSect.raw.buf[50] = size.b.b2; 00301 diskSect.raw.buf[51] = size.b.b3; 00302 00303 //Patch in size of RIFF chunk... 00304 size.l = (sectorCount*512)-8; 00305 diskSect.raw.buf[4] = size.b.b0; 00306 diskSect.raw.buf[5] = size.b.b1; 00307 diskSect.raw.buf[6] = size.b.b2; 00308 diskSect.raw.buf[7] = size.b.b3; 00309 00310 //Patch in size of DATA chunk... 00311 size.l = (sectorCount*512)-512; 00312 diskSect.raw.buf[508] = size.b.b0; 00313 diskSect.raw.buf[509] = size.b.b1; 00314 diskSect.raw.buf[510] = size.b.b2; 00315 diskSect.raw.buf[511] = size.b.b3; 00316 00317 WriteDiskSector(sectorAddress.l); 00318 } 00319 00320 ConsoleWrite("Registering... FStart:"); 00321 ConsolePutUInt(fragment[0].start); 00322 ConsoleWrite("Registering... Size:"); 00323 ConsolePutUInt(fragment[0].length); 00324 00325 //Create FAT records. 00326 fragment[1].start = 0x0fffffff; //fragment 0 is the only fragment 00327 WriteClusterChain(); //register newly created file in FAT FS 00328 00331 sectorAddress.l = 0; //force sector reload for next access 00332 00333 Mp3Reset(); 00334 if (continueRecording) return PS_RECORDING; 00335 return PS_NEXT_SONG; 00336 00337 }

Here is the call graph for this function:


Variable Documentation

code const unsigned char RIFFHeader0[]
 

Initial value:

{ 'R' , 'I' , 'F' , 'F' , 0x70, 0x70, 0x70, 0x70, 'W' , 'A' , 'V' , 'E' , 'f' , 'm' , 't' , , 0x14, 0x00, 0x00, 0x00, 0x11, 0x00, 0x01, 0x00, 0x80, 0x3e, 0x00, 0x00, 0xd7, 0x0f, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00, 0x02, 0x00, 0xf9, 0x01, 'f' , 'a' , 'c' , 't' , 0xc8, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff }
first part of RIFF Header, insert 444 zeroes after this

Definition at line 15 of file record.c.

Referenced by Record().

code const unsigned char RIFFHeader504[]
 

Initial value:

{ 'd' , 'a' , 't' , 'a' , 0x70, 0x70, 0x70, 0x70 }
second part of RIFF Header, bytes 504...511

Definition at line 38 of file record.c.

Referenced by Record().

xdata unsigned char SPMax
 

Definition at line 12 of file record.c.

Referenced by main(), and timer0_interrupt().


All software copyright 2000-2004 VLSI Solution OY. Redistribution of these software modules are limited to promotional use only and only with the VS1011 / VS1002 / VS1003 MP3-Evakit evaluation boards. Free or commercial use of these software modules in MP3 players is ok if the product includes MP3 decoder chip(s) from VLSI. You can request the complete (compilable) package from mp3@vlsi.fi