filerandomarduinoembeddedsd-card

Arduino random file on SD: rewindDirectory


I am building a fancy alarm clock with an Arduino, Mp3 player and an LED matrix to display the time and to serve as a soft wake up light. I intend to make this project accessible and open source when it's done. I want the Arduino to chose randomly an mp3 file located on the SD card and play it, until the end of the song and then chose another one until you push the stop button. But I have trouble with the random file selection:

To have something that doesn't take too much memory I thought of a way to do the selection:

  1. The mp3 files present on the root folder of the SD are counted during initialisation using File::openNextFile() until it return the null object. ->int n_files
  2. Select a random number between 1 and n_files. -> int rand_song
  3. Open the rand_song file using a loop of File::openNextFile() until the wanted file is reached (might not be so efficient but it doesn't matter if it takes a couple of seconds). -->File chosen_rand
  4. Give the name of chosen_rand.name() to musicPlayer.startPlayingFile() so the song plays.
  5. Go back to 1) if end of song reached

So first question, do you think it makes sense to do it this way?

Then, I did a working implementation of the above algorithm missing 4th point (see Code1). But when I add the startPlaying(), a problem occurs: between two call of the function selecting the rand_song file, the position is conserved even with calling File::rewindDirectory() (see Code2).

This lead to my second question, what is the righteous way to use File, SD and File::rewindDirectory together?

I already tried a couple a thing like closing the files (because apparently only one at the time must be open) (see Code3). But always the same, it is working for the first file, but as the rewind doesn't work, it started from the middle of the folder and then hit the end of the folder.

I hope I am precise enough and that I am not missing the essential to make it work.

void setup()
{  ... intializations done before
  // Count the number of files
  File folder = SD.open(music_folder);
  n_files = countMP3File(folder);
  folder.close();
  Serial.print("Number of MP3 files: "); Serial.println(n_files);
}

int countMP3File(File dir)
{
  int counter = 0;
  while(true)
  {
    File entry = dir.openNextFile();
    if (! entry)
    {
      dir.rewindDirectory();
      break;
    }
    Serial.println(entry.name());
    if(strstr(entry.name(), extansion) != NULL)
      counter++;

    entry.close();
  }
  Serial.println("----------------");
  return counter;
}

File selectFileN(int number, File dir)
{
  int counter = 0;
  File return_entry;
  while(true)
  {
    File entry = dir.openNextFile();
    if (! entry)
    {
      Serial.println("Last file reached");
      dir.rewindDirectory();
      break;
    }
    Serial.println(entry.name());
    if(strstr(entry.name(), extansion) != NULL)
      counter++;
    if(counter==number)
    {
      return_entry = entry;
      dir.rewindDirectory();
      break;
    }
    entry.close();
  }
  return return_entry;
}

Code1 (working)

void loop() 
{
  int i, rand_song;
  File folder = SD.open(music_folder);
  File chosen_rand;
  rand_song = random(0, n_files)+1;
  Serial.print("Random number: "); Serial.println(rand_song);
  chosen_rand = selectFileN(rand_song, folder);
  if(chosen_rand)
    Serial.print("Random file name: "); Serial.println(chosen_rand.name());
  folder.close();
  chosen_rand.close();
  Serial.print("playingMusic= "); Serial.println(musicPlayer.playingMusic);
  Serial.println();
  Serial.println();
  delay(200);
}

Code2 (not working)

void loop() 
{
  int i, rand_song;
  File folder = SD.open(music_folder);
  File chosen_rand;
  rand_song = random(0, n_files)+1;
  Serial.print("Random number: "); Serial.println(rand_song);
  chosen_rand = selectFileN(rand_song, folder);
  if(chosen_rand)
    Serial.print("Random file name: "); Serial.println(chosen_rand.name());

  //Added code here
  if(!musicPlayer.startPlayingFile(chosen_rand.name()))
  {
    Serial.println("Could not open mp3 file");
  }
  else Serial.println("Stated playing!");
  //End added code

  folder.close();
  chosen_rand.close();
  Serial.print("playingMusic= "); Serial.println(musicPlayer.playingMusic);
  Serial.println();
  Serial.println();

  //Added code here
  delay(2000);
  musicPlayer.stopPlaying();
  //End added code

  delay(200);
}

Code3 (not working)

void loop() 
{
  int i, rand_song;
  File folder = SD.open(music_folder);
  File chosen_rand;
  rand_song = random(0, n_files)+1;
  Serial.print("Random number: "); Serial.println(rand_song);
  chosen_rand = selectFileN(rand_song, folder);
  if(chosen_rand)
    Serial.print("Random file name: "); Serial.println(chosen_rand.name());

  //Added code to close file before playing it
  char * namefile = (char*)malloc(15);
  strcpy(namefile, chosen_rand.name());
  folder.close();
  chosen_rand.close();      
  //End added code

  if(!musicPlayer.startPlayingFile(chosen_rand.name()))
  {
    Serial.println("Could not open mp3 file");
  }
  else Serial.println("Stated playing!");

  free(namefile);
  Serial.print("playingMusic= "); Serial.println(musicPlayer.playingMusic);
  Serial.println();
  Serial.println();

  delay(2000);
  musicPlayer.stopPlaying();

  delay(200);
}

Solution

  • Alright, I found the problem. As only one File of the SD card should be open at a time, when the file is open by the musicPlayer, the rewindDirectory fails (I think, correct me if I am wrong, but my tests make me believe that). So the fix is quiet easy and logic, just put the rewindDirectory after the musicPlayer closed the file, so a good place to do that is just before my function that select the random file.

    So here's the working loop:

    void loop() 
    {
      int i, rand_song;
      File folder = SD.open(music_folder);
      File random_file;
      rand_song = random(0, n_files)+1;
      Serial.print("Random number: "); Serial.println(rand_song);
      folder.rewindDirectory();  
      random_file = selectFileN(rand_song, folder);
      folder.close();
      if(!musicPlayer.startPlayingFile(random_file.name()))
      {
        Serial.println("Could not open mp3 file");
      }
      else Serial.println("Stated playing!");
      random_file.close();
    
      Serial.print("playingMusic= "); Serial.println(musicPlayer.playingMusic);
      Serial.println();
      Serial.println();
    
      delay(2000);
      musicPlayer.stopPlaying();
      delay(200);
    }