earlephilhower / ESP8266Audio

Arduino library to play MOD, WAV, FLAC, MIDI, RTTTL, MP3, and AAC files on I2S DACs or with a software emulated delta-sigma DAC on the ESP8266 and ESP32
GNU General Public License v3.0
2.05k stars 435 forks source link

Releasing the SD card for other operations during MP3 playback? #489

Closed nicholmikey closed 2 years ago

nicholmikey commented 2 years ago

I think it would be good to add documentation on how to manually buffer from an SD card. This is required when you want to schedule other SD card operations during playback, as such in my use case where I need to read/write some text files as an MP3 plays.

I am having some success taking a buffer such as char *SongBuffer = (char*)malloc(102400);

and filling it from the SD card, then playing with auto in = new AudioFileSourcePROGMEM(SongBuffer , 102400);

But with this library how could you tell when the player is x% through the buffer to grab some more, and move the pointer back to the start of the buffer once you refresh the first half?

nicholmikey commented 2 years ago

My solution for filling my buffer has an issue as when you hit the end of the bytes you need to use in->seek(0,0); and this is resulting in a pop or distortion as it seeks. Has anyone solved such a thing?

nicholmikey commented 2 years ago

I think I have this working through trial and error, I want to share some details here in case others google it in the future.

This is just my take on the solution, there may be a better way.

The problem: play an MP3 of any size off of an SD card, while also able to read/write other files to the SD.

The solution: Use freeRTOS tasks, pin the mp3 player to core 1 (this is a 2 core esp32). Create another task on core 0 that waits for SD card commands, such as:

  1. Fill an entire buffer
  2. Fill first half of buffer
  3. Fill second half of buffer

You use a xQueueReceive system built in to freeRTOS to have a FIFO queue of SD commands. See tutorial here https://www.youtube.com/watch?v=pHJ3lxOoWeI

Fill a buffer with MP3 bytes and play it using auto in = new AudioFileSourcePROGMEM(buffer, size);

Usein->getPos(); to check if you are past 1/2 way though your buffer, if so add a command to your SD queue to get fresh bytes for the top half of your buffer.

When the position hits the end of your buffer use in->seek(0,0); to send the player to the top of your buffer, and fire off another SD queue item to fill the second half of your buffer with fresh bytes.

I was having pops and cracks with in->seek(0,0); as I was firing it after the MP3 stopped, rather you should fire it as soon as the getPos(); matches the buffer length.

It all seems to work fine now I can play an MP3 of any size while at the same time reading/writing other files. Very nice.