Adafruit_MP3.cpp
1 #include "Adafruit_MP3.h" 2 #include "mp3common.h" 3 4 #if defined(__SAMD51__) // feather/metro m4 5 6 #define WAIT_TC16_REGS_SYNC(x) while(x->COUNT16.SYNCBUSY.bit.ENABLE); 7 8 #elif defined(__MK66FX1M0__) || defined(__MK64FX512__) || defined(__MK20DX256__) // teensy 3.6, 3.5. or 3.1/2 9 10 IntervalTimer Adafruit_MP3::_MP3Timer; 11 uint32_t Adafruit_MP3::currentPeriod; 12 static void MP3_Handler(); 13 14 #endif 15 16 volatile bool activeOutbuf; 17 Adafruit_MP3_outbuf outbufs[2]; 18 volatile int16_t *outptr; 19 static void (*sampleReadyCallback)(int16_t, int16_t); 20 21 uint8_t Adafruit_MP3::numChannels = 0; 22 /** 23 ***************************************************************************************** 24 * @brief enable the playback timer 25 * 26 * @return none 27 ****************************************************************************************/ 28 static inline void enableTimer() 29 { 30 #if defined(__SAMD51__) // feather/metro m4 31 MP3_TC->COUNT16.CTRLA.reg |= TC_CTRLA_ENABLE; 32 WAIT_TC16_REGS_SYNC(MP3_TC) 33 #elif defined(__MK66FX1M0__) || defined(__MK64FX512__) || defined(__MK20DX256__) // teensy 3.6, 3.5. or 3.1/2 34 Adafruit_MP3::_MP3Timer.begin(MP3_Handler, Adafruit_MP3::currentPeriod); 35 #elif defined(NRF52) 36 MP3_TIMER->TASKS_START = 1; 37 #endif 38 } 39 40 /** 41 ***************************************************************************************** 42 * @brief disable the playback timer 43 * 44 * @return none 45 ****************************************************************************************/ 46 static inline void disableTimer() 47 { 48 #if defined(__SAMD51__) // feather/metro m4 49 MP3_TC->COUNT16.CTRLA.bit.ENABLE = 0; 50 WAIT_TC16_REGS_SYNC(MP3_TC) 51 #elif defined(__MK66FX1M0__) || defined(__MK64FX512__) || defined(__MK20DX256__) // teensy 3.6, 3.5. or 3.1/2 52 Adafruit_MP3::_MP3Timer.end(); 53 #elif defined(NRF52) 54 MP3_TIMER->TASKS_STOP = 1; 55 #endif 56 } 57 58 /** 59 ***************************************************************************************** 60 * @brief reset the playback timer 61 * 62 * @return none 63 ****************************************************************************************/ 64 #if defined(__SAMD51__) // feather/metro m4 65 static inline void resetTC (Tc* TCx) 66 { 67 68 // Disable TCx 69 TCx->COUNT16.CTRLA.reg &= ~TC_CTRLA_ENABLE; 70 WAIT_TC16_REGS_SYNC(TCx) 71 72 // Reset TCx 73 TCx->COUNT16.CTRLA.reg = TC_CTRLA_SWRST; 74 WAIT_TC16_REGS_SYNC(TCx) 75 while (TCx->COUNT16.CTRLA.bit.SWRST); 76 77 } 78 #endif 79 80 static inline void configureTimer() 81 { 82 #if defined(__SAMD51__) // feather/metro m4 83 84 NVIC_DisableIRQ(MP3_IRQn); 85 NVIC_ClearPendingIRQ(MP3_IRQn); 86 NVIC_SetPriority(MP3_IRQn, 0); 87 88 GCLK->PCHCTRL[MP3_GCLK_ID].reg = GCLK_PCHCTRL_GEN_GCLK0_Val | (1 << GCLK_PCHCTRL_CHEN_Pos); 89 90 resetTC(MP3_TC); 91 92 //configure timer for 44.1khz 93 MP3_TC->COUNT16.WAVE.reg = TC_WAVE_WAVEGEN_MFRQ; // Set TONE_TC mode as match frequency 94 95 MP3_TC->COUNT16.CTRLA.reg |= TC_CTRLA_MODE_COUNT16 | TC_CTRLA_PRESCALER_DIV4; 96 WAIT_TC16_REGS_SYNC(MP3_TC) 97 98 MP3_TC->COUNT16.CC[0].reg = (uint16_t)( (SystemCoreClock >> 2) / MP3_SAMPLE_RATE_DEFAULT); 99 WAIT_TC16_REGS_SYNC(MP3_TC) 100 101 // Enable the TONE_TC interrupt request 102 MP3_TC->COUNT16.INTENSET.bit.MC0 = 1; 103 104 #elif defined(NRF52) 105 106 NVIC_DisableIRQ(MP3_IRQn); 107 NVIC_ClearPendingIRQ(MP3_IRQn); 108 NVIC_SetPriority(MP3_IRQn, 15); 109 110 disableTimer(); 111 MP3_TIMER->MODE = 0; //timer mode 112 MP3_TIMER->BITMODE = 0; //16 bit mode 113 MP3_TIMER->PRESCALER = 0; //no prescale 114 MP3_TIMER->CC[0] = 16000000UL/MP3_SAMPLE_RATE_DEFAULT; 115 MP3_TIMER->EVENTS_COMPARE[0] = 0; 116 MP3_TIMER->TASKS_CLEAR = 1; 117 118 MP3_TIMER->INTENSET = (1UL << 16); //enable compare 0 interrupt 119 120 #elif defined(__MK66FX1M0__) || defined(__MK64FX512__) || defined(__MK20DX256__) // teensy 3.6, 3.5. or 3.1/2 121 float sec = 1.0 / (float)MP3_SAMPLE_RATE_DEFAULT; 122 Adafruit_MP3::currentPeriod = sec * 1000000UL; 123 #endif 124 } 125 126 static inline void acknowledgeInterrupt() 127 { 128 #if defined(__SAMD51__) // feather/metro m4 129 if (MP3_TC->COUNT16.INTFLAG.bit.MC0 == 1) { 130 MP3_TC->COUNT16.INTFLAG.bit.MC0 = 1; 131 } 132 133 #elif defined(NRF52) 134 MP3_TIMER->EVENTS_COMPARE[0] = 0; 135 MP3_TIMER->TASKS_CLEAR = 1; 136 #endif 137 } 138 139 static inline void updateTimerFreq(uint32_t freq) 140 { 141 disableTimer(); 142 143 #if defined(__SAMD51__) // feather/metro m4 144 MP3_TC->COUNT16.CC[0].reg = (uint16_t)( (SystemCoreClock >> 2) / freq); 145 WAIT_TC16_REGS_SYNC(MP3_TC); 146 #elif defined(__MK66FX1M0__) || defined(__MK64FX512__) || defined(__MK20DX256__) // teensy 3.6, 3.5. or 3.1/2 147 float sec = 1.0 / (float)freq; 148 Adafruit_MP3::currentPeriod = sec * 1000000UL; 149 #elif defined(NRF52) 150 MP3_TIMER->CC[0] = 16000000UL/freq; 151 #endif 152 153 enableTimer(); 154 } 155 156 /** 157 ***************************************************************************************** 158 * @brief Begin the mp3 player. This initializes the playback timer and necessary interrupts. 159 * 160 * @return none 161 ****************************************************************************************/ 162 bool Adafruit_MP3::begin() 163 { 164 sampleReadyCallback = NULL; 165 bufferCallback = NULL; 166 167 configureTimer(); 168 169 if ((hMP3Decoder = MP3InitDecoder()) == 0) 170 { 171 return false; 172 } 173 else return true; 174 } 175 176 /** 177 ***************************************************************************************** 178 * @brief Set the function the player will call when it's buffers need to be filled. 179 * Care must be taken to ensure that the callback function is efficient. 180 * If the callback takes too long to fill the buffer, playback will be choppy 181 * 182 * @param fun_ptr the pointer to the callback function. This function must take a pointer 183 * to the location the bytes will be written, as well as an integer that represents 184 * the maximum possible bytes that can be written. The function should return the 185 * actual number of bytes that were written. 186 * 187 * @return none 188 ****************************************************************************************/ 189 void Adafruit_MP3::setBufferCallback(int (*fun_ptr)(uint8_t *, int)){ bufferCallback = fun_ptr; } 190 191 /** 192 ***************************************************************************************** 193 * @brief Set the function that the player will call when the playback timer fires. 194 * The callback is called inside of an ISR, so it should be short and efficient. 195 * This will usually just be writing samples to the DAC. 196 * 197 * @param fun_ptr the pointer to the callback function. The function must take two 198 * unsigned 16 bit integers. The first argument to the callback will be the 199 * left channel sample, and the second channel will be the right channel sample. 200 * If the played file is mono, only the left channel data is used. 201 * 202 * @return none 203 ****************************************************************************************/ 204 void Adafruit_MP3::setSampleReadyCallback(void (*fun_ptr)(int16_t, int16_t)) { sampleReadyCallback = fun_ptr; } 205 206 /** 207 ***************************************************************************************** 208 * @brief Play an mp3 file. This function resets the buffers and should only be used 209 * when beginning playback of a new mp3 file. If playback has been stopped 210 * and you would like to resume playback at the current location, use Adafruit_MP3::resume instead. 211 * 212 * @return none 213 ****************************************************************************************/ 214 void Adafruit_MP3::play() 215 { 216 bytesLeft = 0; 217 activeOutbuf = 0; 218 readPtr = inBuf; 219 writePtr = inBuf; 220 221 outbufs[0].count = 0; 222 outbufs[1].count = 0; 223 playing = false; 224 225 //start the playback timer 226 enableTimer(); 227 228 #if defined(__SAMD51__) || defined(NRF52) // feather/metro m4 229 NVIC_EnableIRQ(MP3_IRQn); 230 #endif 231 } 232 233 void Adafruit_MP3_DMA::play() 234 { 235 #if defined(__SAMD51__) 236 //don't need an interrupt here 237 MP3_TC->COUNT16.INTENCLR.bit.MC0 = 1; 238 #endif 239 Adafruit_MP3::play(); 240 #if defined(__SAMD51__) || defined(NRF52) // feather/metro m4 241 NVIC_DisableIRQ(MP3_IRQn); //we don't need the interrupt 242 #endif 243 leftoverSamples = 0; 244 245 //fill both buffers 246 fill(); 247 } 248 249 /** 250 ***************************************************************************************** 251 * @brief pause playback. This function stops the playback timer. 252 * 253 * @return none 254 ****************************************************************************************/ 255 void Adafruit_MP3::pause() 256 { 257 disableTimer(); 258 } 259 260 /** 261 ***************************************************************************************** 262 * @brief Resume playback. This function re-enables the playback timer. If you are 263 * starting playback of a new file, use Adafruit_MP3::play instead 264 * 265 * @return none 266 ****************************************************************************************/ 267 void Adafruit_MP3::resume() 268 { 269 enableTimer(); 270 } 271 272 /** 273 ***************************************************************************************** 274 * @brief Get the number of bytes until the end of the ID3 tag. 275 * 276 * @param readPtr current read pointer 277 * 278 * @return none 279 ****************************************************************************************/ 280 int Adafruit_MP3::findID3Offset(uint8_t *readPtr) 281 { 282 char header[10]; 283 memcpy(header, readPtr, 10); 284 //http://id3.org/id3v2.3.0#ID3v2_header 285 if(header[0] == 0x49 && header[1] == 0x44 && header[2] == 0x33 && header[3] < 0xFF){ 286 //this is a tag 287 uint32_t sz = ((uint32_t)header[6] << 23) | ((uint32_t)header[7] << 15) | ((uint32_t)header[8] << 7) | header[9]; 288 return sz; 289 } 290 else{ 291 //this is not a tag 292 return 0; 293 } 294 } 295 296 void Adafruit_MP3_DMA::getBuffers(int16_t **ping, int16_t **pong){ 297 *pong = outbufs[0].buffer; 298 *ping = outbufs[1].buffer; 299 } 300 301 /** 302 ***************************************************************************************** 303 * @brief The main loop of the mp3 player. This function should be called as fast as 304 * possible in the loop() function of your sketch. This checks to see if the 305 * buffers need to be filled, and calls the buffer callback function if necessary. 306 * It also calls the functions to decode another frame of mp3 data. 307 * 308 * @return none 309 ****************************************************************************************/ 310 int Adafruit_MP3::tick(){ 311 noInterrupts(); 312 if (outbufs[activeOutbuf].count == 0 && outbufs[!activeOutbuf].count > 0) { 313 //time to swap the buffers 314 activeOutbuf = !activeOutbuf; 315 outptr = outbufs[activeOutbuf].buffer; 316 } 317 interrupts(); 318 319 //if we are running out of samples, and don't yet have another buffer ready, get busy. 320 if ((outbufs[activeOutbuf].count < BUFFER_LOWER_THRESH) && 321 (outbufs[!activeOutbuf].count < (MP3_OUTBUF_SIZE/2))) { 322 //Serial.println("running low"); 323 //dumb, but we need to move any bytes to the beginning of the buffer 324 if ((readPtr != inBuf) && (bytesLeft > 0) && (bytesLeft < BUFFER_LOWER_THRESH)) { 325 /* 326 Serial.print("move bytes: "); 327 Serial.print((uint32_t)&inBuf, HEX); Serial.print(", "); 328 Serial.print((uint32_t)&readPtr, HEX); Serial.print(", "); 329 Serial.println(bytesLeft); 330 */ 331 memmove(inBuf, readPtr, bytesLeft); 332 //Serial.println("moved"); 333 readPtr = inBuf; 334 writePtr = inBuf + bytesLeft; 335 } 336 337 //get more data from the user application 338 if (bufferCallback != NULL){ 339 //Serial.println("ask!"); 340 if ( (inbufend - writePtr) > 0) { 341 int bytesRead = bufferCallback(writePtr, inbufend - writePtr); 342 writePtr += bytesRead; 343 bytesLeft += bytesRead; 344 } 345 } 346 347 MP3FrameInfo frameInfo; 348 int err, offset; 349 350 if (!playing) { 351 Serial.println("Starting playback"); 352 353 /* Find start of next MP3 frame. Assume EOF if no sync found. */ 354 offset = MP3FindSyncWord(readPtr, bytesLeft); 355 Serial.print("Offset: "); Serial.println(offset); 356 if (offset >= 0) { 357 readPtr += offset; 358 bytesLeft -= offset; 359 } 360 361 err = MP3GetNextFrameInfo(hMP3Decoder, &frameInfo, readPtr); 362 if (err == ERR_MP3_INVALID_FRAMEHEADER) { 363 readPtr += 1; 364 bytesLeft -= 1; 365 } else { 366 Serial.print("Setting timer sample rate to: "); Serial.println(frameInfo.samprate); 367 if (frameInfo.samprate != MP3_SAMPLE_RATE_DEFAULT) { 368 updateTimerFreq(frameInfo.samprate); 369 } 370 playing = true; 371 numChannels = frameInfo.nChans; 372 } 373 return 1; 374 } 375 376 offset = MP3FindSyncWord(readPtr, bytesLeft); 377 if (offset >= 0) { 378 //Serial.print("found sync @ "); Serial.println(offset); 379 readPtr += offset; 380 bytesLeft -= offset; 381 382 //fill the inactive outbuffer 383 err = MP3Decode(hMP3Decoder, &readPtr, (int*) &bytesLeft, outbufs[!activeOutbuf].buffer + outbufs[!activeOutbuf].count, 0); 384 if (err) { 385 // sometimes we have a bad frame, lets just nudge forward one byte 386 if (err == ERR_MP3_INVALID_FRAMEHEADER) { 387 readPtr += 1; 388 bytesLeft -= 1; 389 } 390 return err; 391 } 392 MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; 393 outbufs[!activeOutbuf].count += mp3DecInfo->nGrans * mp3DecInfo->nGranSamps * mp3DecInfo->nChans; 394 } 395 } 396 return 0; 397 } 398 399 //fill a buffer with data 400 int Adafruit_MP3_DMA::fill(){ 401 402 int ret = 0; 403 int16_t *curBuf = outbufs[activeOutbuf].buffer; 404 405 //put any leftover samples in the new buffer 406 if(leftoverSamples > 0){ 407 memcpy(outbufs[activeOutbuf].buffer, leftover, leftoverSamples*sizeof(int16_t)); 408 outbufs[activeOutbuf].count = leftoverSamples; 409 leftoverSamples = 0; 410 } 411 412 while(outbufs[activeOutbuf].count < MP3_OUTBUF_SIZE){ 413 loopstart: 414 //dumb, but we need to move any bytes to the beginning of the buffer 415 if(readPtr != inBuf && bytesLeft < BUFFER_LOWER_THRESH){ 416 memmove(inBuf, readPtr, bytesLeft); 417 readPtr = inBuf; 418 writePtr = inBuf + bytesLeft; 419 } 420 421 //get more data from the user application 422 if(bufferCallback != NULL){ 423 if(inbufend - writePtr > 0){ 424 int bytesRead = bufferCallback(writePtr, inbufend - writePtr); 425 if(bytesRead == 0){ 426 ret = 1; 427 break; 428 } 429 writePtr += bytesRead; 430 bytesLeft += bytesRead; 431 } 432 } 433 434 int err, offset; 435 436 if(!playing){ 437 /* Find start of next MP3 frame. Assume EOF if no sync found. */ 438 offset = MP3FindSyncWord(readPtr, bytesLeft); 439 if(offset >= 0){ 440 readPtr += offset; 441 bytesLeft -= offset; 442 } 443 444 err = MP3GetNextFrameInfo(hMP3Decoder, &frameInfo, readPtr); 445 if(err != ERR_MP3_INVALID_FRAMEHEADER){ 446 if(frameInfo.samprate != MP3_SAMPLE_RATE_DEFAULT) 447 { 448 updateTimerFreq(frameInfo.samprate); 449 } 450 playing = true; 451 Adafruit_MP3::numChannels = frameInfo.nChans; 452 } 453 if(framebuf != NULL) free(framebuf); 454 framebuf = (int16_t *)malloc(frameInfo.outputSamps*sizeof(int16_t)); 455 goto loopstart; 456 } 457 458 offset = MP3FindSyncWord(readPtr, bytesLeft); 459 if(offset >= 0){ 460 readPtr += offset; 461 bytesLeft -= offset; 462 463 MP3DecInfo *mp3DecInfo = (MP3DecInfo *)hMP3Decoder; 464 int toRead = mp3DecInfo->nGrans * mp3DecInfo->nGranSamps * mp3DecInfo->nChans; 465 if(outbufs[activeOutbuf].count + toRead < MP3_OUTBUF_SIZE){ 466 //we can read directly into the output buffer so lets do that 467 err = MP3Decode(hMP3Decoder, &readPtr, (int*) &bytesLeft, outbufs[activeOutbuf].buffer + outbufs[activeOutbuf].count, 0); 468 outbufs[activeOutbuf].count += toRead; 469 } 470 else{ 471 //the frame would cross byte boundaries, we need to split manually 472 err = MP3Decode(hMP3Decoder, &readPtr, (int*) &bytesLeft, framebuf, 0); 473 int remainder = MP3_OUTBUF_SIZE - outbufs[activeOutbuf].count; 474 memcpy(outbufs[activeOutbuf].buffer + outbufs[activeOutbuf].count, framebuf, remainder*sizeof(int16_t)); 475 leftover = framebuf + remainder; 476 leftoverSamples = (toRead-remainder); 477 478 //swap buffers 479 activeOutbuf = !activeOutbuf; 480 outbufs[activeOutbuf].count = 0; 481 ret = 0; 482 break; 483 } 484 485 if (err) { 486 return err; 487 } 488 } 489 } 490 491 if(decodeCallback != NULL) decodeCallback(curBuf, MP3_OUTBUF_SIZE); 492 493 494 return ret; 495 } 496 497 498 499 /** 500 ***************************************************************************************** 501 * @brief The IRQ function that gets called whenever the playback timer fires. 502 * 503 * @return none 504 ****************************************************************************************/ 505 #if defined(NRF52) 506 extern "C" { 507 #endif 508 void MP3_Handler() 509 { 510 //disableTimer(); 511 512 if(outbufs[activeOutbuf].count >= Adafruit_MP3::numChannels){ 513 //it's sample time! 514 if(sampleReadyCallback != NULL){ 515 if(Adafruit_MP3::numChannels == 1) 516 sampleReadyCallback(*outptr, 0); 517 else 518 sampleReadyCallback(*outptr, *(outptr + 1)); 519 520 //increment the read position and decrement the remaining sample count 521 outptr += Adafruit_MP3::numChannels; 522 outbufs[activeOutbuf].count -= Adafruit_MP3::numChannels; 523 } 524 } 525 526 //enableTimer(); 527 528 acknowledgeInterrupt(); 529 } 530 #if defined(NRF52) 531 } 532 #endif