It is pretty simple to build a simple mp3 audio player using the Stream API of my Arduino Audio Tools library:

  • A SD file is a subclass of an Arduino Stream, so all you need to do is to copy from the file to the desired output stream.
  • when we use the EncodedAudioStream as output, we can support different audio formats
  • we can add a status flag to halt and continue the processing
  • Finally we need to add some logic which handles the end of file to automatically process the next file
  • On top of that it is rather easy to add volume control and meta data support.

In order to simplify the usage of my library even more, I decided to provide this functionality as well in a separate class and to prove the point: The AudioPlayer class took only 120 lines of code to implement!

The AudioPlayer supports

  • multiple processor architectures
  • multiple audio data sources (SD, URL, callbacks)
  • different Output Scenarios (I2S, PWM, A2DP etc). Just pass the desired output stream object to the constructor.
  • different Decoders for MP3, AAC, WAV. Just pass the desired decoder object to the constructor.
  • Volume Control (by calling player.setVolume())
  • Stopping and Resuming the processing (by calling player.stop() and player.play())
  • You can move to the next file by calling player.next();
  • support for Metadata

Dependencies

The sketch is using the following dependencies:

Setup

Install the necessary libraries. Before you start double check your settings in the AudioConfig.h of the arduino-audio-tools project. If the following line is commented out for your processor, remove the comments.

#define USE_SDFAT

A Basic Sketch

The following Arduino Sketch demonstrates how to implement an MP3 Player: which gets the data from a SD drive and provides the audio as analog output: The AudioSourceSdFat class builds on the SdFat Library from Bill Greiman which provides FAT16/FAT32 and exFAT support with long filenames.

#define USE_HELIX 
#define USE_SDFAT

#include "AudioTools.h"
#include "AudioCodecs/CodecMP3Helix.h"

using namespace audio_tools;  

const char *startFilePath="/";
const char* ext="mp3";
AudioSourceSdFat source(startFilePath, ext);
AnalogAudioStream out;
MP3DecoderHelix decoder;
AudioPlayer player(source, out, decoder);

// Arduino setup
void setup() {
  Serial.begin(115200);
  AudioLogger::instance().begin(Serial, AudioLogger::Info);

  // setup output
  auto cfg = out.defaultConfig();
  out.begin(cfg);

  // setup player
  player.begin();
}

// Arduino loop
void loop() {
  player.copy();
}

As you can see, this approach has quite some flexibility: if you want to use I2S as output just replace the AnalogAudioStream with I2SStream. If you want to process another file format, just replace the Decoder implementation and if you want to get the audio data from a different source, just replace use a different AudioSource..

Extending the Functionality

With just a few more lines we can add the support for metadata and the filtering of files by name:

#define USE_HELIX 
#define USE_SDFAT

#include "AudioTools.h"
#include "AudioCodecs/CodecMP3Helix.h"

using namespace audio_tools;  

const char *startFilePath="/";
const char* ext="mp3";
AudioSourceSdFat source(startFilePath, ext);
AnalogAudioStream out;
MP3DecoderHelix decoder;
AudioPlayer player(source, out, decoder);

// metadata callback
void printMetaData(MetaDataType type, const char* str, int len){
  Serial.print("==> ");
  Serial.print(MetaDataTypeStr[type]);
  Serial.print(": ");
  Serial.println(str);
}

// Arduino setup
void setup() {
  Serial.begin(115200);
  AudioLogger::instance().begin(Serial, AudioLogger::Info);

  // setup output
  auto cfg = out.defaultConfig();
  out.begin(cfg);

  // setup player
  source.setFileFilter("*Bob Dylan*");
  player.setCallbackMetadata(printMetaData);
  player.begin();
}

// Arduino loop
void loop() {
  player.copy();
}

SD Card

Here is the information how to wire the SD card to the ESP32

SD ESP32
CS VSPI-CS0 (GPIO 05)
SCK VSPI-CLK (GPIO 18)
MOSI VSPI-MOSI (GPIO 23)
MISO VSPI-MISO (GPIO 19)
VCC VIN (5V)
GND GND

SD

The Output

On the ESP32 the analog output is available on GPIO25 (Channel 1) and GPIO26 (Channel 2).

You can use a piezo electric element or connect some earphones:
Earphones

Using PlatformIO

You can also implement this sketch in PlatformIO. This has the advantage, that the dependencies get installed automatically. Just use the following platform.ini file:

[platformio]
description = Audio MP3 Player
default_envs = esp32dev

[env:esp32dev]
platform = espressif32
board = esp32dev
framework = arduino
lib_deps = https://github.com/pschatzmann/arduino-audio-tools, https://github.com/pschatzmann/arduino-libhelix, https://github.com/greiman/SdFat
build_flags = -DCORE_DEBUG_LEVEL=5 -Wno-unused-variable -Wno-unused-but-set-variable -Wno-unused-function -Wno-format-extra-args 
monitor_speed = 115200

Source code

You can find the source code for this and other audio player examples on Github.


13 Comments

Markus · 16. July 2024 at 0:16

Hello Phil,

thank you very much for programming the arduino-mp3-player. What a great job!
When I’m stopping with calling player.stop() and then resume with calling player.play(), then the mp3-file will be played exactly at the time, when it had been stopped before.
Is it possible to read out informations about that moment of the stream (playing-time), so that I can store that informations in the flash memory and the mp3-file could be resumed after a later restart of the mp3 player exactly at that playing-time.
It would be very helpful for example when listening to a longer mp3-file like a podcast.

Thank you very much and have a great time,
Markus

    pschatzmann · 16. July 2024 at 1:15

    just use the File API to determine the current name and location

      Markus · 16. July 2024 at 10:05

      Hello Phil,
      thank you very much for your answer.
      It’s no problem for me to start after a restart/reboot with the song , which had been played as last song before switching off the ESP32.
      The problem I have, is to start that song not at the beginnig of the song but for example 1 Minute, 30 Seconds after the beginning of the song.
      What I forgot to mention yesterday, I’m playing the songs from SD-Card.

      Would you please be so kind, and give a little help?

      Best regards,
      Markus

        pschatzmann · 16. July 2024 at 10:26

        Please use Github discussions and I have already answered this kind of question there. Check the reference documentation of you SD library on how to get and set the file position!

Erik · 29. May 2024 at 21:15

Can you please explain, how you convert an audioFile to a stream which then can be used to mix several inputs/ outputs together?

Ich habe von objektorientiertem Programmieren keinen Plan. Die einzelnen Beispiel-Codes funktionieren zwar, jedoch möchte ich sie auf meine Bedürfnisse zuschneiden. Könntest eventuell kurz erklären wie die Konversion funktioniert?

Vielen Dank im Voraus.

Mit besten Grüßen

Erik Belenko 🙂

    pschatzmann · 30. May 2024 at 0:32

    A File is a Stream! so there is nothing to convert…

Joseph · 13. January 2024 at 16:09

I want to print the name of the file being played on an oled display. How do I access the name of the file. I know it is printed when it prints the metadata, but I don’t know how to access it and cannot find an example that shows how. Here is my current code: https://create.arduino.cc/editor/11irishjs/d96c99fb-885a-40ff-a4c4-fd62fe490f7d/preview

    pschatzmann · 25. January 2024 at 15:37

    if you call getStream() you get a pointer to a Stream: just cast it to a File and you have all file operations available

Joseph · 24. December 2023 at 21:43

I am haveing a hard time finding a working mp3 player example sketch. Can you direct me to the correct example on your Gethub page?

priyanshu jain · 9. March 2023 at 4:42

I have used the same code as above and getting the following error: please guide:
what i need is to play a specific music on button press.

sketch_mar09a:11:1: error: ‘AudioSourceSdFat’ does not name a type
AudioSourceSdFat source(startFilePath, ext);

    pschatzmann · 9. March 2023 at 7:40

    Please use the examples from Github.
    In the meantime, I am supporting not only SDFAT but many other sd libraries as well…

      Stephan · 20. April 2023 at 0:23

      Thank you for your quick response.

Leave a Reply

Avatar placeholder

Your email address will not be published. Required fields are marked *