Edited version of the wave player class that uses the MODDMA library to handle transfers to the DAC.
Dependents: WavePlayer_MODDMA wave_player_DMA_mbed
wave_player.cpp
00001 //----------------------------------------------------------------------------- 00002 // a sample mbed library to play back wave files using MODDMA 00003 // Based on the wave_player library by Steve Ravet and the 00004 // MODDMA library by Andy Kirkham. 00005 // 00006 // The wave_player library is located here: 00007 // https://developer.mbed.org/users/sravet/code/wave_player/ 00008 // 00009 // The MODDMA library is located here: 00010 // https://developer.mbed.org/users/AjK/code/MODDMA/ 00011 // A wiki page and example are located here: 00012 // https://developer.mbed.org/cookbook/MODDMA 00013 // 00014 // explanation of wave file format. 00015 // https://ccrma.stanford.edu/courses/422/projects/WaveFormat/ 00016 00017 // if VERBOSE is uncommented then the wave player will enter a verbose 00018 // mode that displays all data values as it reads them from the file 00019 // and writes them to the DAC. Very slow and unusable output on the DAC, 00020 // but useful for debugging wave files that don't work. 00021 //#define VERBOSE 00022 00023 00024 #include <mbed.h> 00025 #include <stdio.h> 00026 #include <wave_player.h> 00027 00028 //----------------------------------------------------------------------------- 00029 // constructor -- accepts an mbed pin to use for AnalogOut. Only p18 will work 00030 wave_player::wave_player(AnalogOut *_dac) 00031 { 00032 wave_DAC=_dac; 00033 wave_DAC->write_u16(32768); //DAC is 0-3.3V, so idles at ~1.6V 00034 verbosity=0; 00035 } 00036 00037 //----------------------------------------------------------------------------- 00038 // if verbosity is set then wave player enters a mode where the wave file 00039 // is decoded and displayed to the screen, including sample values put into 00040 // the DAC FIFO, and values read out of the DAC FIFO by the ISR. The DAC output 00041 // itself is so slow as to be unusable, but this might be handy for debugging 00042 // wave files that don't play 00043 //----------------------------------------------------------------------------- 00044 void wave_player::set_verbosity(int v) 00045 { 00046 verbosity=v; 00047 } 00048 00049 //----------------------------------------------------------------------------- 00050 // player function. Takes a pointer to an opened wave file. The file needs 00051 // to be stored in a filesystem with enough bandwidth to feed the wave data. 00052 // LocalFileSystem isn't, but the SDcard is, at least for 22kHz files. The 00053 // SDcard filesystem can be hotrodded by increasing the SPI frequency it uses 00054 // internally. 00055 //----------------------------------------------------------------------------- 00056 void wave_player::play(FILE *wavefile) 00057 { 00058 unsigned chunk_id,chunk_size,channel; 00059 unsigned data,i; 00060 unsigned j,k; 00061 int dac_cntval; 00062 bool buf0_flag; 00063 long long slice_value; 00064 char *slice_buf; 00065 short *data_sptr; 00066 unsigned char *data_bptr; 00067 int *data_wptr; 00068 FMT_STRUCT wav_format; 00069 long num_slices; 00070 00071 for(i=0; i<BUF_SIZE; i++){ 00072 DAC_buf0[i]=0; 00073 DAC_buf1[i]=0; 00074 } 00075 00076 DAC_on=0; 00077 00078 int time=0; 00079 00080 fread(&chunk_id,4,1,wavefile); 00081 fread(&chunk_size,4,1,wavefile); 00082 00083 while (!feof(wavefile)) { 00084 if (verbosity) 00085 printf("Read chunk ID 0x%x, size 0x%x\n",chunk_id,chunk_size); 00086 switch (chunk_id) { 00087 case 0x46464952: 00088 fread(&data,4,1,wavefile); 00089 if (verbosity) { 00090 printf("RIFF chunk\n"); 00091 printf(" chunk size %d (0x%x)\n",chunk_size,chunk_size); 00092 printf(" RIFF type 0x%x\n",data); 00093 } 00094 break; 00095 case 0x20746d66: 00096 fread(&wav_format,sizeof(wav_format),1,wavefile); 00097 if (verbosity) { 00098 printf("FORMAT chunk\n"); 00099 printf(" chunk size %d (0x%x)\n",chunk_size,chunk_size); 00100 printf(" compression code %d\n",wav_format.comp_code); 00101 printf(" %d channels\n",wav_format.num_channels); 00102 printf(" %d samples/sec\n",wav_format.sample_rate); 00103 printf(" %d bytes/sec\n",wav_format.avg_Bps); 00104 printf(" block align %d\n",wav_format.block_align); 00105 printf(" %d bits per sample\n",wav_format.sig_bps); 00106 } 00107 if (chunk_size > sizeof(wav_format)) 00108 fseek(wavefile,chunk_size-sizeof(wav_format),SEEK_CUR); 00109 break; 00110 case 0x61746164: 00111 // Data chunk which contains the audio samples to send to the DAC 00112 00113 // allocate a buffer big enough to hold a slice 00114 slice_buf=(char *)malloc(wav_format.block_align); 00115 if (!slice_buf) { 00116 printf("Unable to malloc slice buffer"); 00117 exit(1); 00118 } 00119 num_slices=chunk_size/wav_format.block_align; 00120 00121 // Calculating the transfer frequency: 00122 // By default, the Mbed library sets the PCLK_DAC clock value 00123 // to 24MHz. 00124 00125 dac_cntval=(24000000/wav_format.sample_rate); 00126 00127 if (verbosity) { 00128 printf("DATA chunk\n\r"); 00129 printf(" chunk size %d (0x%x)\n\r",chunk_size,chunk_size); 00130 printf(" %d slices\n\r",num_slices); 00131 printf(" sample rate is %d\r\n", wav_format.sample_rate); 00132 printf(" Ideal sample interval=%d\n\r",(unsigned)(1000000.0/wav_format.sample_rate)); 00133 printf(" programmed interrupt tick interval=%d\n\r",dac_cntval); 00134 } 00135 00136 // Prepare the GPDMA system for buffer0. 00137 conf0 = new MODDMA_Config; 00138 conf0 00139 ->channelNum ( MODDMA::Channel_0 ) 00140 ->srcMemAddr ( (uint32_t) &DAC_buf0 ) 00141 ->dstMemAddr ( MODDMA::DAC ) 00142 ->transferSize ( BUF_SIZE ) 00143 ->transferType ( MODDMA::m2p ) 00144 ->dstConn ( MODDMA::DAC ) 00145 ->attach_tc ( this,&wave_player::TC0_callback ) 00146 ->attach_err ( this,&wave_player::ERR0_callback ) 00147 ; // config end 00148 00149 // Prepare the GPDMA system for buffer1. 00150 conf1 = new MODDMA_Config; 00151 conf1 00152 ->channelNum ( MODDMA::Channel_1 ) 00153 ->srcMemAddr ( (uint32_t) &DAC_buf1 ) 00154 ->dstMemAddr ( MODDMA::DAC ) 00155 ->transferSize ( BUF_SIZE ) 00156 ->transferType ( MODDMA::m2p ) 00157 ->dstConn ( MODDMA::DAC ) 00158 ->attach_tc ( this,&wave_player::TC1_callback ) 00159 ->attach_err ( this,&wave_player::ERR1_callback ) 00160 ; // config end 00161 00162 // Set the DAC to the audio sample rate 00163 LPC_DAC->DACCNTVAL = dac_cntval; 00164 00165 // Begin (enable DMA and counter). Note, don't enable 00166 // DBLBUF_ENA as we are using DMA double buffering. 00167 LPC_DAC->DACCTRL |= (3UL << 2); 00168 00169 DAC_on=1; 00170 00171 // start reading slices, which contain one sample each for however many channels 00172 // are in the wave file. one channel=mono, two channels=stereo, etc. Since 00173 // mbed only has a single AnalogOut, all of the channels present are averaged 00174 // to produce a single sample value. This summing and averaging happens in 00175 // a variable of type signed long long, to make sure that the data doesn't 00176 // overflow regardless of sample size (8 bits, 16 bits, 32 bits). 00177 // 00178 // note that from what I can find that 8 bit wave files use unsigned data, 00179 // while 16 and 32 bit wave files use signed data 00180 // 00181 00182 // Fill buffer 0 first 00183 buf0_flag=1; 00184 00185 // Separate slices into sections of BUF_SIZE samples 00186 for (j=0; j<((num_slices/BUF_SIZE)+1); j++) 00187 { 00188 for(k=0; k<BUF_SIZE; k++) 00189 { 00190 // The last buffer will likely not be exactly BUF_SIZE, 00191 // so fill the remaining spots with 0 00192 if((j*BUF_SIZE+k)>num_slices){ 00193 if(buf0_flag) 00194 DAC_buf0[k]=0; 00195 else 00196 DAC_buf1[k]=0; 00197 } 00198 else{ 00199 // Read audio samples from sd card 00200 fread(slice_buf,wav_format.block_align,1,wavefile); 00201 if (feof(wavefile)) { 00202 printf("Oops -- not enough slices in the wave file\n"); 00203 exit(1); 00204 } 00205 data_sptr=(short *)slice_buf; // 16 bit samples 00206 data_bptr=(unsigned char *)slice_buf; // 8 bit samples 00207 data_wptr=(int *)slice_buf; // 32 bit samples 00208 slice_value=0; 00209 for (channel=0;channel<wav_format.num_channels;channel++) { 00210 switch (wav_format.sig_bps) { 00211 case 16: 00212 if (verbosity) 00213 printf("16 bit channel %d data=%d ",channel,data_sptr[channel]); 00214 slice_value+=data_sptr[channel]; 00215 break; 00216 case 32: 00217 if (verbosity) 00218 printf("32 bit channel %d data=%d ",channel,data_wptr[channel]); 00219 slice_value+=data_wptr[channel]; 00220 break; 00221 case 8: 00222 if (verbosity) 00223 printf("8 bit channel %d data=%d ",channel,(int)data_bptr[channel]); 00224 slice_value+=data_bptr[channel]; 00225 break; 00226 } 00227 } 00228 slice_value/=wav_format.num_channels; 00229 00230 // slice_value is now averaged. Next it needs to be scaled to an unsigned 16 bit value 00231 // with DC offset so it can be written to the DAC. 00232 switch (wav_format.sig_bps) { 00233 case 8: slice_value<<=8; 00234 break; 00235 case 16: slice_value+=32768; 00236 break; 00237 case 32: slice_value>>=16; 00238 slice_value+=32768; 00239 break; 00240 } 00241 00242 if (verbosity) 00243 printf("sample %d slice_value %d\n",(j*BUF_SIZE+k),(int)slice_value); 00244 if(buf0_flag) 00245 DAC_buf0[k]=(uint32_t)slice_value; 00246 else 00247 DAC_buf1[k]=(uint32_t)slice_value; 00248 } 00249 } 00250 00251 // Except for the first buffer, wait until the previous DMA transfer is 00252 // complete before switching buffers 00253 if(j>0) 00254 { 00255 while((dma0_fin_flag==0)&(dma1_fin_flag==0)&(time<500000000)){ 00256 wait_us(1); 00257 time++; 00258 } 00259 } 00260 00261 if (time>499999999) 00262 { 00263 printf("timeout, %d of %d\r\n", (j*BUF_SIZE), num_slices); 00264 exit(1); 00265 } 00266 00267 time=0; 00268 dma0_fin_flag=0; 00269 dma1_fin_flag=0; 00270 00271 // If finished filling buffer 0, set flag to fill buffer 1 00272 // and start the DMA transfer for buffer 0 00273 if(buf0_flag==1) 00274 { 00275 buf0_flag=0; 00276 dma.Setup(conf0); 00277 dma.Enable( conf0 ); 00278 } 00279 else // Similarly for buffer 1 00280 { 00281 buf0_flag=1; 00282 dma.Setup(conf1); 00283 dma.Enable( conf1 ); 00284 } 00285 00286 } 00287 DAC_on=0; 00288 delete conf0; 00289 delete conf1; 00290 free(slice_buf); 00291 break; 00292 case 0x5453494c: 00293 if (verbosity) 00294 printf("INFO chunk, size %d\n",chunk_size); 00295 fseek(wavefile,chunk_size,SEEK_CUR); 00296 break; 00297 default: 00298 printf("unknown chunk type 0x%x, size %d\n",chunk_id,chunk_size); 00299 data=fseek(wavefile,chunk_size,SEEK_CUR); 00300 break; 00301 } 00302 fread(&chunk_id,4,1,wavefile); 00303 fread(&chunk_size,4,1,wavefile); 00304 } 00305 } 00306 00307 // Configuration callback on TC; runs when the DMA has finished transferring buffer 0 to the DAC 00308 void wave_player::TC0_callback(void) { 00309 00310 // Get configuration pointer. 00311 MODDMA_Config *config = dma.getConfig(); 00312 00313 // Only run once 00314 dma.haltAndWaitChannelComplete( (MODDMA::CHANNELS)config->channelNum()); 00315 00316 // Finish the DMA cycle by shutting down the channel. 00317 dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); 00318 00319 // Clear DMA IRQ flags. 00320 if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); 00321 00322 // Indicate that buffer 0 has finished transferring 00323 dma0_fin_flag=1; 00324 } 00325 00326 // Configuration callback on Error 00327 void wave_player::ERR0_callback(void) { 00328 error("Oh no! My Mbed EXPLODED! :( Only kidding, go find the problem"); 00329 } 00330 00331 // Configuration callback on TC; runs when the DMA has finished transferring buffer 1 to the DAC 00332 void wave_player::TC1_callback(void) { 00333 00334 // Get configuration pointer. 00335 MODDMA_Config *config = dma.getConfig(); 00336 00337 // Only run once 00338 dma.haltAndWaitChannelComplete( (MODDMA::CHANNELS)config->channelNum()); 00339 00340 // Finish the DMA cycle by shutting down the channel. 00341 dma.Disable( (MODDMA::CHANNELS)config->channelNum() ); 00342 00343 // Clear DMA IRQ flags. 00344 if (dma.irqType() == MODDMA::TcIrq) dma.clearTcIrq(); 00345 00346 // Indicate that buffer 1 has finished transferring 00347 dma1_fin_flag=1; 00348 } 00349 00350 // Configuration callback on Error 00351 void wave_player::ERR1_callback(void) { 00352 error("Oh no! My Mbed EXPLODED! :( Only kidding, go find the problem"); 00353 }
Generated on Fri Jul 15 2022 01:28:23 by 1.7.2