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:
File::openNextFile()
until it return the null object. ->int n_files
int rand_song
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
chosen_rand.name()
to musicPlayer.startPlayingFile()
so the song plays.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);
}
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);
}