/ src / Adafruit_MP3.cpp
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