Inserting short mp3 tracks in the middle of another track

I have a similar problem to Zeeakram's "how to play a audio file from a specific time", but the solution might be different so I thought I should not hijack that post. I will follow Zeeakram's post so don't feel obliged to repeat info.

I want to be able to play an mp3 track, pause the track, play a different mp3 track and then continue the paused track. Obviously, being able to play from a specific time would solve my problem.

I have 2 types of mp3 breakout board. A Catalex YX5300 and a DXF player mini.
I have done some reading and I am pretty confident that these boards cannot start a track at a specific time (e.g. play track 3 from 2min 10sec). Does anybody know if there is a way I can pause track A, play track B and continue track A from where it was paused?

I think the Teensy board might possibly offer a solution (it can mix tracks) but I prefer Arduino and I am not sure what "Arduino compatible" means? It might be too much of a learning curve for my poor brain.
Teensy audio tutorial

Its doable using that DF Mini player. The Df Mini Fast library has the playAdvertisement and StopAdvertisement function- which will allow you to stop playing the current track at a specified time or input (button push, IR Sensor, etc.) , play the Advertisement, and then resume the original track where it left off. The Advertisement can be an entirely different MP3 track if you wish. Available from Downloads in the Arduino IDE or available here:

Cheers Halloweenrick. I was just rushing back to say I had found the ADVERT function. Lucky I did because I didn't know it needed the fast library so I probably would have tried and given up. I will report back with any tips I find whilst getting it going.

I have run both the Arduino Nano and Arduino Uno with the DF player min****i breakout board using the Fast DF library and Software serial to send commands from Arduino to DF player. Here are some useful notes: -

The Play Advertisement command works brilliantly for inserting a track in the middle of another track. It is not completely seamless and I would guess there is a delay on the order of 100s of milliseconds between tracks.

The Uno can run itself, the DF player and a small speaker from the PC USB. The Nano was flaky without an external power supply. Everything seemed smoother with the UNO so I recommend starting with that.

So far it seems to respond reliably to commands for play/pause/stop.

I have used the isPlaying command and the output of the DF players BUSY pin to test whether the track is playing or paused/stopped. I found these feedback methods to be intermittently unreliable, even if I leave a 5 second gap after a state change before checking what the state is.
I think the original DF library performed checks to confirm that the DF player had received commands, I am not sure if the Fast DF library does the same, but if so then I guess I don't need to worry about doing a separate check on its state? Corrections/Ideas welcome.

It is my UNDERSTANDING (re: I haven't done it myself) that the isPlaying command will work without the use of the BUSY pin connection. Here is a sample code from another user who used the IsPlaying command as a boolean and note that it does involve some programming:
https://forum.arduino.cc/?topic=574417

///              MP3 PLAYER PROJECT[color=#222222][/color]
/// http://educ8s.tv/arduino-mp3-player/[color=#222222][/color]
//////////////////////////////////////////[color=#222222][/color]
[color=#222222][/color]
[color=#222222][/color]
#include "SoftwareSerial.h"[color=#222222][/color]
SoftwareSerial mySerial(10, 11);[color=#222222][/color]
# define Start_Byte 0x7E[color=#222222][/color]
# define Version_Byte 0xFF[color=#222222][/color]
# define Command_Length 0x06[color=#222222][/color]
# define End_Byte 0xEF[color=#222222][/color]
# define Acknowledge 0x00 //Returns info with command 0x41 [0x01: info, 0x00: no info][color=#222222][/color]
[color=#222222][/color]
# define ACTIVATED LOW[color=#222222][/color]
[color=#222222][/color]
int buttonNext = 2;[color=#222222][/color]
int buttonPause = 3;[color=#222222][/color]
int buttonPrevious = 4;[color=#222222][/color]
boolean isPlaying = false;[color=#222222][/color]
[color=#222222][/color]
[color=#222222][/color]
[color=#222222][/color]
void setup () {[color=#222222][/color]
[color=#222222][/color]
pinMode(buttonPause, INPUT);[color=#222222][/color]
digitalWrite(buttonPause,HIGH);[color=#222222][/color]
pinMode(buttonNext, INPUT);[color=#222222][/color]
digitalWrite(buttonNext,HIGH);[color=#222222][/color]
pinMode(buttonPrevious, INPUT);[color=#222222][/color]
digitalWrite(buttonPrevious,HIGH);[color=#222222][/color]
[color=#222222][/color]
mySerial.begin (9600);[color=#222222][/color]
delay(1000);[color=#222222][/color]
playFirst();[color=#222222][/color]
isPlaying = true;[color=#222222][/color]
[color=#222222][/color]
[color=#222222][/color]
}[color=#222222][/color]
[color=#222222][/color]
[color=#222222][/color]
[color=#222222][/color]
void loop () {[color=#222222][/color]
[color=#222222][/color]
 if (digitalRead(buttonPause) == ACTIVATED)[color=#222222][/color]
  {[color=#222222][/color]
    if(isPlaying)[color=#222222][/color]
    {[color=#222222][/color]
      pause();[color=#222222][/color]
      isPlaying = false;[color=#222222][/color]
    }else[color=#222222][/color]
    {[color=#222222][/color]
      isPlaying = true;[color=#222222][/color]
      play();[color=#222222][/color]
    }[color=#222222][/color]
  }[color=#222222][/color]
[color=#222222][/color]
[color=#222222][/color]
 if (digitalRead(buttonNext) == ACTIVATED)[color=#222222][/color]
  {[color=#222222][/color]
    if(isPlaying)[color=#222222][/color]
    {[color=#222222][/color]
      playNext();[color=#222222][/color]
    }[color=#222222][/color]
  }[color=#222222][/color]
[color=#222222][/color]
   if (digitalRead(buttonPrevious) == ACTIVATED)[color=#222222][/color]
  {[color=#222222][/color]
    if(isPlaying)[color=#222222][/color]
    {[color=#222222][/color]
      playPrevious();[color=#222222][/color]
    }[color=#222222][/color]
  }[color=#222222][/color]
}[color=#222222][/color]
[color=#222222][/color]
void playFirst()[color=#222222][/color]
{[color=#222222][/color]
  execute_CMD(0x3F, 0, 0);[color=#222222][/color]
  delay(500);[color=#222222][/color]
  setVolume(20);[color=#222222][/color]
  delay(500);[color=#222222][/color]
  execute_CMD(0x11,0,1);[color=#222222][/color]
  delay(500);[color=#222222][/color]
}[color=#222222][/color]
[color=#222222][/color]
void pause()[color=#222222][/color]
{[color=#222222][/color]
  execute_CMD(0x0E,0,0);[color=#222222][/color]
  delay(500);[color=#222222][/color]
}[color=#222222][/color]
[color=#222222][/color]
void play()[color=#222222][/color]
{[color=#222222][/color]
  execute_CMD(0x0D,0,1);[color=#222222][/color]
  delay(500);[color=#222222][/color]
}[color=#222222][/color]
[color=#222222][/color]
void playNext()[color=#222222][/color]
{[color=#222222][/color]
  execute_CMD(0x01,0,1);[color=#222222][/color]
  delay(500);[color=#222222][/color]
}[color=#222222][/color]
[color=#222222][/color]
void playPrevious()[color=#222222][/color]
{[color=#222222][/color]
  execute_CMD(0x02,0,1);[color=#222222][/color]
  delay(500);[color=#222222][/color]
}[color=#222222][/color]
[color=#222222][/color]
void setVolume(int volume)[color=#222222][/color]
{[color=#222222][/color]
  execute_CMD(0x06, 0, volume); // Set the volume (0x00~0x30)[color=#222222][/color]
  delay(2000);[color=#222222][/color]
}[color=#222222][/color]
[color=#222222][/color]
void execute_CMD(byte CMD, byte Par1, byte Par2)[color=#222222][/color]
// Excecute the command and parameters[color=#222222][/color]
{[color=#222222][/color]
// Calculate the checksum (2 bytes)[color=#222222][/color]
word checksum = -(Version_Byte + Command_Length + CMD + Acknowledge + Par1 + Par2);[color=#222222][/color]
// Build the command line[color=#222222][/color]
byte Command_line[10] = { Start_Byte, Version_Byte, Command_Length, CMD, Acknowledge,[color=#222222][/color]
Par1, Par2, highByte(checksum), lowByte(checksum), End_Byte};[color=#222222][/color]
//Send the command line to the module[color=#222222][/color]
for (byte k=0; k<10; k++)[color=#222222][/color]
{[color=#222222][/color]
mySerial.write( Command_line[k]);[color=#222222][/color]
}

}[/code]

All of my work with the playAdvertisement command has just let the Advertisement (in my case, Halloween scream) play out its length and then the original music resumes. I THINK (heavy on the think) this will work with the DF Mini Fast Library as IsPlaying is a command, and won't need to go back to the original DFMiniMp3 Library.

EDIT: Ignore the color #2222 things and /color tags- I copied and pasted from that post referenced above and the guy didn't use code tags, so it added that where I highlighted it. Moral of the Story: Everyone please use code tags.

Sorry I should have been more clear, I am using them both separately just to test them out, they do work independently.

I am doing these two things independently: -
Reading the state of the Busy Pin
Using the command myMP3.isPlaying()

Both of them give the correct answer most of the time, but intermittently give incorrect answers (as the code cycles around the main loop).

I think (noting that this is at the limits of my abilities) the code you linked to is ignoring the DF libraries completely and sending the serial control commands directly, so in that code "isPlaying" is just a local flag that he sets in his main loop?

I have with some trepidation opened up the DFPlayerMini_Fast.h library code. I think I can see that it sends a feedback request and a checksum value. Also see this datasheet extract: -

"we conducted an industrial level of optimization by adding frame checksum, retransmission, error handling, and other measures to significantly strengthen the stability and reliability of communication. " [DF Player data sheet]

So I assume from this that I can possibly assume it is sending and confirming commands and I don't need to do any of my own checking with the BUSY pin or myMP3.isPlaying() command? But I also notice that it has the option to turn feedback requests On/Off but I don't understand it enough to see where this is being selected?

I have posted the library code below in the hope that somebody who understands it can explain. Even though it is doing what I want right now I just want to make sure it doesn't start skipping commands once I pull it into my main project.

/*!
 * @file DFPlayerMini_Fast.h
 *
 * This is the documentation for the YX5200-24SS MP3 player driver code for the
 * Arduino platform.  It is designed specifically to work with the
 * <a href="https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299">
 * DFPlayerMini breakout</a>
 *
 * These MP3 players use UART to communicate, 2 pins (TX + RX) are required
 * to interface with the breakout. An external power supply and output amp
 * improves the MP3 player's functionality. Also, see <a href="https://wiki.dfrobot.com/DFPlayer_Mini_SKU_DFR0299">
 * the wiki</a> for more detailed wiring instructions.
 *
 * Written by Power_Broker as a hobby.
 *
 * None.
 *
 */

#pragma once
#include "Arduino.h"
#include "FireTimer.h"




 /**************************************************************************/
 /*!
   @brief  Namespace for constants
 */
 /**************************************************************************/
namespace dfplayer
{
  /** Packet Values */
  const uint8_t STACK_SIZE      = 10;   // total number of bytes in a stack/packet (same for cmds and queries)
  const uint8_t SB              = 0x7E; // start byte
  const uint8_t VER             = 0xFF; // version
  const uint8_t LEN             = 0x6;  // number of bytes after "LEN" (except for checksum data and EB)
  const uint8_t FEEDBACK        = 1;    // feedback requested
  const uint8_t NO_FEEDBACK     = 0;    // no feedback requested
  const uint8_t EB              = 0xEF; // end byte

  /** Control Command Values */
  const uint8_t NEXT            = 0x01;
  const uint8_t PREV            = 0x02;
  const uint8_t PLAY            = 0x03;
  const uint8_t INC_VOL         = 0x04;
  const uint8_t DEC_VOL         = 0x05;
  const uint8_t VOLUME          = 0x06;
  const uint8_t EQ              = 0x07;
  const uint8_t PLAYBACK_MODE   = 0x08;
  const uint8_t PLAYBACK_SRC    = 0x09;
  const uint8_t STANDBY         = 0x0A;
  const uint8_t NORMAL          = 0x0B;
  const uint8_t RESET           = 0x0C;
  const uint8_t PLAYBACK        = 0x0D;
  const uint8_t PAUSE           = 0x0E;
  const uint8_t SPEC_FOLDER     = 0x0F;
  const uint8_t VOL_ADJ         = 0x10;
  const uint8_t REPEAT_PLAY     = 0x11;
  const uint8_t USE_MP3_FOLDER  = 0x12;
  const uint8_t INSERT_ADVERT   = 0x13;
  const uint8_t SPEC_TRACK_3000 = 0x14;
  const uint8_t STOP_ADVERT     = 0x15;
  const uint8_t STOP            = 0x16;
  const uint8_t REPEAT_FOLDER   = 0x17;
  const uint8_t RANDOM_ALL      = 0x18;
  const uint8_t REPEAT_CURRENT  = 0x19;
  const uint8_t SET_DAC         = 0x1A;

  /** Query Command Values */
  const uint8_t SEND_INIT        = 0x3F;
  const uint8_t RETRANSMIT       = 0x40;
  const uint8_t REPLY            = 0x41;
  const uint8_t GET_STATUS_      = 0x42;
  const uint8_t GET_VOL          = 0x43;
  const uint8_t GET_EQ           = 0x44;
  const uint8_t GET_MODE         = 0x45;
  const uint8_t GET_VERSION      = 0x46;
  const uint8_t GET_TF_FILES     = 0x47;
  const uint8_t GET_U_FILES      = 0x48;
  const uint8_t GET_FLASH_FILES  = 0x49;
  const uint8_t KEEP_ON          = 0x4A;
  const uint8_t GET_TF_TRACK     = 0x4B;
  const uint8_t GET_U_TRACK      = 0x4C;
  const uint8_t GET_FLASH_TRACK  = 0x4D;
  const uint8_t GET_FOLDER_FILES = 0x4E;
  const uint8_t GET_FOLDERS      = 0x4F;

  /** EQ Values */
  const uint8_t EQ_NORMAL       = 0;
  const uint8_t EQ_POP          = 1;
  const uint8_t EQ_ROCK         = 2;
  const uint8_t EQ_JAZZ         = 3;
  const uint8_t EQ_CLASSIC      = 4;
  const uint8_t EQ_BASE         = 5;

  /** Mode Values */
  const uint8_t REPEAT          = 0;
  const uint8_t FOLDER_REPEAT   = 1;
  const uint8_t SINGLE_REPEAT   = 2;
  const uint8_t RANDOM          = 3;

  /** Playback Source Values */
  const uint8_t U               = 1;
  const uint8_t TF              = 2;
  const uint8_t AUX             = 3;
  const uint8_t SLEEP           = 4;
  const uint8_t FLASH           = 5;

  /** Base Volume Adjust Value */
  const uint8_t VOL_ADJUST      = 0x10;

  /** Repeat Play Values */
  const uint8_t STOP_REPEAT     = 0;
  const uint8_t START_REPEAT    = 1;
}




/**************************************************************************/
/*!
  @brief  Class for interacting with DFPlayerMini MP3 player
*/
/**************************************************************************/
class DFPlayerMini_Fast
{
public:
  Stream* _serial;
    
  /** Struct to store entire serial datapacket used for MP3 config/control */
  struct stack {
    uint8_t start_byte;
    uint8_t version;
    uint8_t length;
    uint8_t commandValue;
    uint8_t feedbackValue;
    uint8_t paramMSB;
    uint8_t paramLSB;
    uint8_t checksumMSB;
    uint8_t checksumLSB;
    uint8_t end_byte;
  } sendStack, recStack;




  bool begin(Stream& stream, unsigned long threshold=500);

  void playNext();
  void playPrevious();
  void play(uint16_t trackNum);
  void stop();
  void playFromMP3Folder(uint16_t trackNum);
  void playAdvertisement(uint16_t trackNum);
  void stopAdvertisement();
  void incVolume();
  void decVolume();
  void volume(uint8_t volume);
  void EQSelect(uint8_t setting);
  void loop(uint16_t trackNum);
  void playbackSource(uint8_t source);
  void standbyMode();
  void normalMode();
  void reset();
  void resume();
  void pause();
  void playFolder(uint8_t folderNum, uint8_t trackNum);
  void volumeAdjustSet(uint8_t gain);
  void startRepeatPlay();
  void stopRepeatPlay();
  void repeatFolder(uint16_t folder);
  void randomAll();
  void startRepeat();
  void stopRepeat();
  void startDAC();
  void stopDAC();
  void sleep();
  void wakeUp();

  bool isPlaying();
  int16_t currentVolume();
  int16_t currentEQ();
  int16_t currentMode();
  int16_t currentVersion();
  int16_t numUsbTracks();
  int16_t numSdTracks();
  int16_t numFlashTracks();
  int16_t currentUsbTrack();
  int16_t currentSdTrack();
  int16_t currentFlashTrack();
  int16_t numTracksInFolder(uint8_t folder);
  int16_t numFolders();
  
  void setTimeout(unsigned long threshold);
  void findChecksum(stack *_stack);
  void sendData();
  void flush();
  int16_t query(uint8_t cmd, uint8_t msb=0, uint8_t lsb=0);
  bool getStatus();
  bool parseFeedback();

  void printStack(stack _stack);




private:
  FireTimer timoutTimer;
  unsigned long _threshold;

  /** MP3 response packet parsing states */
  enum fsm {
    find_start_byte,
    find_ver_byte,
    find_len_byte,
    find_command_byte,
    find_feedback_byte,
    find_param_MSB,
    find_param_LSB,
    find_checksum_MSB, 
    find_checksum_LSB,
    find_end_byte
  };
  fsm state = find_start_byte;
};

i emailed Power_Broker, the author of the DF Mini Fast library to check on this answer. I expect he'll chime in shortly.

The lib ships with HTML documentation generated by Doxygen (see the docs folder in the root library folder).

For the "isPlaying()" function, you want to look at the CPP instead of the header. Here's what you should be looking at:

/**************************************************************************/
 /*!
	 @brief  Determine if a track is currently playing.
	 @return True if a track is currently playing, false if not, -1 if error.
 */
 /**************************************************************************/
bool DFPlayerMini_Fast::isPlaying()
{
	int16_t result = query(dfplayer::GET_STATUS_);

	if (result != -1)
		return (result & 1);

	return result;
}

However, you'll notice that "isPlaying()" relies on "query()", which is the general function used for all queries, not just "isPlaying()". Here is the source code for "query()":

/**************************************************************************/
 /*!
	 @brief  Query the MP3 player for specific information.
	 @param    cmd
			   The command/query ID.
	 @param    msb
			   The payload/parameter MSB.
	 @param    lsb
			   The payload/parameter LSB.
	 @return Query response, -1 if error.
 */
 /**************************************************************************/
int16_t DFPlayerMini_Fast::query(uint8_t cmd, uint8_t msb, uint8_t lsb)
{
	flush();

	sendStack.commandValue  = cmd;
	sendStack.feedbackValue = dfplayer::FEEDBACK;
	sendStack.paramMSB = msb;
	sendStack.paramLSB = lsb;

	findChecksum(&sendStack);
	sendData();
	delay(100); // This delay is oddly required for a successful query response

	if (getStatus())
		return (recStack.paramMSB << 8) | recStack.paramLSB;

	return -1;
}

You can still drill down farther through these functions, but I think this is enough to show that when you call "isPlaying()", a query is sent with feedback hard-coded true and then parses the response (or times out)

As a rule I encourage users to use the busy pin instead of "isPlaying()" since some people strangely have trouble with that particular query.

Many Thanks to Halloweenrick and Power_Broker. I have just played around a bit more and here are my notes: -

Delay
I found the delay between track changes (including using insert ADVERT) to be very short and almost imperceptible. This was the case for WAV or MP3 (but I think MP3 may have had a very slightly longer delay).

Power
I found that the UNO could run the DF player with a 0.5W speaker from USB or with external PSU. The Nano needed the external PSU (it would sometimes run Erroneously from just the USB). I know it is not a good idea to run high power things straight from the Arduino, this is basically a reminder to others to include an external PSU.

Power draw for a Nano+DF+0.5Wspeaker with the track stopped or paused or with DF in sleep mode was roughly 0.15W (roughly 20hours run time on one AA).

BUSY pin
I tested this for an Arduino Uno and Nano (running off PSU).
I found that I needed about 50ms delay after playing, changing, stopping, or pausing the track before the BUSY pin would show the DF player status without error.

IsPlaying command
I couldn't get this to work reliably even when using delays before calling it. Here is how I am calling it, just in case I am doing something obviously wrong.

  bool track = myMP3.isPlaying();
  Serial.print("SOFTWARE IS PLAYING = ");
  Serial.println(track);

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.