|
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;
00114
00115
00116
00117
00118 #ifdef VS1003
00119
00120 Mp3WriteRegister(SPI_CLOCKF,0x66,0x96);
00121 #else
00122 Mp3WriteRegister(SPI_CLOCKF,156,204);
00123 #endif
00124
00125
00126 Mp3WriteRegister(SPI_AICTRL0,0x00,0x09);
00127 Mp3WriteRegister(SPI_AICTRL1,0x10,0x00);
00128 Mp3WriteRegister(SPI_MODE,0x18,0x04);
00129 Delay(10);
00130
00131
00132 #ifdef VS1003
00133
00134 Mp3WriteRegister(SPI_CLOCKF,0x66,0x96);
00135 #else
00136 Mp3WriteRegister(SPI_CLOCKF,156,204);
00137 #endif
00138
00139
00140
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++){
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
00184 ScanForFreeSector();
00185 sectorAddress. l = freeSector;
00186 dataBufPtr = diskSect. raw. buf;
00187
00188 Delay(10);
00189
00190 while ( Mp3ReadRegister(SPI_HDAT1)>>8)
00191 ;
00192
00193 dataBufPtr = diskSect. raw. buf;
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
00205 if ( Mp3ReadRegister(SPI_HDAT1) >= 128){
00206
00207 GREEN_LED = LED_OFF;
00208 blockNumber++;
00209
00210
00211 if ( dataBufPtr>( diskSect. raw. buf+511)){
00212 ConsoleWrite( "\rBuffer indexing error. Stop.\r");
00213 while(1);
00214 }
00215
00216
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 {
00226
00227 signed int soundlevel;
00228
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 }
00251
00252 }
00253
00254
00255
00256 Mp3DeselectControl();
00257
00258 if (blockNumber==2){
00259
00260
00261
00262
00263
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();
00274 sectorAddress. l = freeSector;
00275 dataBufPtr = diskSect. raw. buf;
00276 if ( freeSector!=(lastSector+1)){
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 }
00287 fragment[0]. length = sectorCount;
00288
00289
00290
00291 {
00292 xdata addressType size;
00293
00294 ReadDiskSector(fragment[0].start);
00295
00296
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
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
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
00326 fragment[1]. start = 0x0fffffff;
00327 WriteClusterChain();
00328
00331 sectorAddress. l = 0;
00332
00333 Mp3Reset();
00334 if (continueRecording) return PS_RECORDING;
00335 return PS_NEXT_SONG;
00336
00337 }
Here is the call graph for this function:
|