MP3 playback direct from SD card on XIAO 52840

Just wondering if anybody here has experience with or knowledge of decoding MP3 files.

I’ve found this library GitHub - TMRh20/microDecoder: Decode Audio files using Arduino which I adapted slightly to work.
Using that library, I’ve been able to successfully play back MP3 files on my nRF52840 based devices directly from SD card.

The MP3 decoding library is a bit limited in features and functionality, but it does work! I’m looking for a more fully featured lib or codebase that is actively maintained. Google isn’t helping much.

Working proof of concept code is below: (uses my AutoAnalogAudio library & I2S amp MAX98357A)

#include <SD.h>
#include <AutoAnalogAudio.h>
#include "mp3.h"  // decoder
#include "pcm.h"

mp3 MP3;
pcm audio;
AutoAnalog aaAudio;

const char* audioFilename = "noceil.mp3";
uint8_t SD_CS_PIN = 5;                      // Set this to your CS pin for the SD card/module
#define USE_I2S 1

char songName[64];
float volumeControl = 0.2;
#define AUDIO_BUFFER_SIZE 6400

void setup() {

  pinMode(9, OUTPUT);
  digitalWrite(9, HIGH);
  pinMode(6, OUTPUT);  //Connected to SD pin of MAX98357A
  digitalWrite(6, HIGH);

  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.print("Init SD card...");
  if (!SD.begin(SD_CS_PIN)) {
    Serial.println("init failed!");
    return;
  }
  Serial.println("SD init ok");

  //aaAudio.I2S_PIN_LRCK = 28;  // Use different LRCK pin for Feather Express 52840
  aaAudio.maxBufferSize = AUDIO_BUFFER_SIZE;
  aaAudio.begin(0, 1, USE_I2S);

  playAudio(audioFilename);
}

void loop() {
  
  loadBuffer();

  // Control via Serial for testing
  if (Serial.available()) {
    char c = Serial.read();
    if (c == '=') {
      volumeControl += 0.1;
    } else if (c == '-') {
      volumeControl -= 0.1;
      volumeControl = max(0.0, volumeControl);
    } else if (c == 'p') {
      playAudio("waitress.mp3");
    }
    Serial.println(volumeControl);
  }

}


File myFile;

void playAudio(const char* audioFile) {

  if (myFile) {
    myFile.close();
    MP3.end();
  }
  //Open the designated file
  myFile = SD.open(audioFile);

  MP3.begin(myFile);
  MP3.getMetadata();
  aaAudio.setSampleRate(MP3.Fs, 1);
  Serial.println(MP3.Fs);
  aaAudio.dacBitsPerSample = MP3.bitsPerSample;
  Serial.println(MP3.bitsPerSample);
}

void loadBuffer() {

  if (myFile.available() > AUDIO_BUFFER_SIZE) {
    uint32_t sampleCounter = 0;
    for (uint32_t i = 0; i < 100; i++) {
      audio = MP3.decode();
      memcpy(&aaAudio.dacBuffer16[sampleCounter], audio.interleaved, 128);  // 128 bytes
      sampleCounter += 64;                                                  // 64 samples per go
    }
    for (uint32_t i = 0; i < 6400; i++) {
      int16_t sample = aaAudio.dacBuffer16[i];
      sample *= volumeControl;
      aaAudio.dacBuffer16[i] = sample;
    }
    aaAudio.feedDAC(0, 6400);
  } else {
    myFile.seek(0);
  }
}


Pinouts for test code & MAX98357A:
BCLK connected to Arduino D1 (p0.03)
LRCK connected to Arduino D3 (p0.29)
DIN connected to Arduino D5 (p0.05)
SD connected to Arduino D6
GAIN connected to VIN
SD Card:
SD Card CS Pin user selected

A quick update, I just got MP3 playback working somewhat reliably today with my modified version of the microDecoder library linked above! Am pretty proud of myself, and wanted to share.

I pushed the changes to my GitHub repo, and here is the code I am using:


/*
* Requires https://github.com/TMRh20/microDecoder library
*/

#include <SD.h>
#include <AutoAnalogAudio.h>
#include "mp3.h"  // decoder
#include "pcm.h"

mp3 MP3;
pcm audio;
AutoAnalog aaAudio;


/************ USER CONFIGURATION ***************/
const char* audioFilename = "noceil.mp3";
uint8_t SD_CS_PIN = 2;  // Set this to your CS pin for the SD card/module

float volumeControl = 0.2;
#define AUDIO_BUFFER_SIZE 1152  // Should be a multiple of 64
/**********************************************/

void setup() {

  Serial.begin(115200);
  while (!Serial) delay(10);

  Serial.print("Init SD card...");
  if (!SD.begin(SD_CS_PIN)) {
    Serial.println("init failed!");
    return;
  }
  Serial.println("SD init ok");

  //aaAudio.I2S_PIN_LRCK = 28;  // Use different LRCK pin for Feather Express 52840
  aaAudio.maxBufferSize = AUDIO_BUFFER_SIZE;
  aaAudio.begin(0, 2);

  playAudio(audioFilename);
}

void loop() {

  loadBuffer();

  // Control via Serial for testing
  if (Serial.available()) {
    char c = Serial.read();
    if (c == '=') {
      volumeControl += 0.1;
    } else if (c == '-') {
      volumeControl -= 0.1;
      volumeControl = max(0.0, volumeControl);
    } else if (c == 'p') {
      playAudio("atliens.mp3");
    } else if (c == 'c') {
      playAudio("dopeBoys.mp3");
    }
    Serial.println(volumeControl);
  }
}


File myFile;

void playAudio(const char* audioFile) {

  MP3.end();
  if (myFile) {
    myFile.close();
  }
  aaAudio.disableDAC();

  //Open the designated file
  myFile = SD.open(audioFile);

  if (myFile) {

    MP3.begin(myFile);
    MP3.getMetadata();
    aaAudio.setSampleRate(MP3.Fs, 1);
    Serial.println(MP3.Fs);
    aaAudio.dacBitsPerSample = MP3.bitsPerSample;
    Serial.println(MP3.bitsPerSample);
  } else {
    Serial.println("Failed to open file");
  }
}

void loadBuffer() {

  uint32_t sampleCounter = 0;
  if (myFile.available() >= AUDIO_BUFFER_SIZE) {
    for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE / 64; i++) {
      audio = MP3.decode();
      memcpy(&aaAudio.dacBuffer16[sampleCounter], audio.interleaved, 128);  // 128 bytes
      sampleCounter += 64;                                                  // 64 samples per go
    }
    for (uint32_t i = 0; i < AUDIO_BUFFER_SIZE; i++) {
      int16_t sample = aaAudio.dacBuffer16[i];
      sample *= volumeControl;
      aaAudio.dacBuffer16[i] = sample;
    }
    aaAudio.feedDAC(0, sampleCounter);
  } else {
    aaAudio.disableDAC();
  }
}

It seems simple and reliable enough now that I might incorporate the microDecoder library into the nRF52840 portion of my audio library codebase, but am still undecided on that.

This is all due to a sponsorship from PCBWay.com that allowed me to design and have my own custom circuit boards created for prototyping with my AutoAnalogAudio library. The design is open-source and all of the docs, code, etc are on my GitHub and Blog.

Hi there,

And BIG thanks for the Leg up on the MP3 decoding. Nice work.
I’m sure others will use it too. Lots of users have put the I2S to the work. :+1:
and the MAX amp is actually very good. Thanks for the contribution I’m certain it will be well received. :grin:

GL :slight_smile: PJ :v:

1 Like