Wave Shield Adafruit - Play next song and previous song

Hello,

I am having trouble coding with the Adafruit Wave Shield. Everything works fine, I can read data from the SD card and I can select a file to play with some buttons.
What I am trying to do is to be able to press a button and the shield should play the next song (or the previous one with another button). I spend a lot of time working with the examples given on the Adafruit website. They are very helpfull but I'm still stuck because my C language skills are not that good.

To be able to play the next song I thought it would be interesting to store in a Array of String (pointers in C ?) all the files on the SD card. When I start playing a song I can save it in a variable and I would know with the help of the Array of Strings which song to play next.

Here is the example code given by Adafruit that could help me build this Array of String :

void lsR(FatReader &d)
{
 int8_t r;                     // indicates the level of recursion
 
 while ((r = d.readDir(dirBuf)) > 0) {     // read the next file in the directory 
   // skip subdirs . and ..
   if (dirBuf.name[0] == '.') 
     continue;
   
   for (uint8_t i = 0; i < dirLevel; i++) 
     Serial.print(' ');        // this is for prettyprinting, put spaces in front
   printEntryName(dirBuf);           // print the name of the file we just found
   Serial.println();           // and a new line
   
   if (DIR_IS_SUBDIR(dirBuf)) {   // we will recurse on any direcory
     FatReader s;                 // make a new directory object to hold information
     dirLevel += 2;               // indent 2 spaces for future prints
     if (s.open(vol, dirBuf)) 
       lsR(s);                    // list all the files in this directory now!
     dirLevel -=2;                // remove the extra indentation
   }
 }
 sdErrorCheck();                  // are we doign OK?
}

Here is the code I have that I'm using in my project. I'm capturing the pressed button with a 4 Channels RF Remote Module, the Arduino shield used in the project is an Arduino Mega 2560. With the free space left on it, I dont think that adding elements dynamically to an Array of String should be an issue. Anyway, if there is another solution to this problem I would be glad to take it.

#include <Servo.h>
#include <WaveHC.h>
#include <WaveUtil.h>

// PIN set-up for all components
const byte DISTANCE_TRIGGER_PIN = 36;
const byte DISTANCE_ECHO_PIN = 37;
const byte SERVO_PIN = 6;
const byte REMOTE_A_PIN = 26;
const byte REMOTE_B_PIN = 25;
const byte REMOTE_C_PIN = 24;
const byte REMOTE_D_PIN = 27;

//Servo motors parameters
Servo servo;
const byte startPosition = 0;
const int SERVO_MAX = 2000;
const int SERVO_MIN = 1023;
int servoPosition = 0;
int capture = 0;

//Distance parameters
const unsigned long MEASURE_TIMEOUT = 25000UL; // 25ms = ~8m à 340m/s
const float SOUND_SPEED = 340.0 / 1000;
int CAPTURE_INTERVAL = 10;
float captureArray[10];

//Remote buttons
byte buttons[] = {REMOTE_A_PIN, REMOTE_B_PIN, REMOTE_C_PIN, REMOTE_D_PIN};
#define NUMBUTTONS sizeof(buttons)
volatile byte pressed[NUMBUTTONS], justpressed[NUMBUTTONS], justreleased[NUMBUTTONS];

//Wave shield parameters
SdReader card;    // This object holds the information for the card
FatVolume vol;    // This holds the information for the partition on the card
FatReader root;   // This holds the information for the volumes root directory
WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time
uint8_t dirLevel; // indent level for file/dir names    (for prettyprinting)
dir_t dirBuf;     // buffer for directory reads
FatReader f;      // This holds the information for the file we're play

void setup() {
 Serial.begin(9600);  
 
 //Distance configuration
 pinMode(DISTANCE_TRIGGER_PIN, OUTPUT);
 digitalWrite(DISTANCE_TRIGGER_PIN, LOW); // La broche TRIGGER doit être à LOW au repos
 pinMode(DISTANCE_ECHO_PIN, INPUT);
 
 //Servo configuration
 servo.write(startPosition);
 servo.attach(SERVO_PIN);
 delay(1500);

 //Wave shield configuration
 card.init();
 card.partialBlockRead(true);
 uint8_t part;
 for (part = 0; part < 5; part++) {   // we have up to 5 slots to look in
   if (vol.init(card, part)) 
     break;                           // we found one, lets bail
 }
 root.openRoot(vol);

 //Timer2 Overflow Interrupt Enable
 TCCR2A = 0;
 TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20;
 
 TIMSK2 |= 1<<TOIE2;
}

void lsR(FatReader &d)
{
 // what to do ?
}

SIGNAL(TIMER2_OVF_vect) {
 check_switches();
}

void loop() {
   if (justpressed[0]) {
   justpressed[0] = 0;
   // here should be the code to play the next song
   }
   else if (justpressed[1]) {
       justpressed[1] = 0;
   // here should be the code to play the previous song
   }
   else if (justpressed[2]) {
       justpressed[2] = 0;
       playfile("TEST3.WAV");
   }
   else if (justpressed[3]) {
       justpressed[3] = 0;
       playfile("TEST4.WAV");
   }
}

void controlServoWithDistance() {
 digitalWrite(DISTANCE_TRIGGER_PIN, HIGH);
 delayMicroseconds(10);
 digitalWrite(DISTANCE_TRIGGER_PIN, LOW);
 long measure = pulseIn(DISTANCE_ECHO_PIN, HIGH, MEASURE_TIMEOUT);
 float distance_mm = measure / 2.0 * SOUND_SPEED;
 if(capture < CAPTURE_INTERVAL) {
   captureArray[capture] = distance_mm;
   capture = capture +1;
 } else {
   float moyenne = getMoyenne();
   useServo(moyenne);
   capture = 0;
 }
 delay(20);
}

void goUp(int servoMilliSeconds) {
   servo.write(servoMilliSeconds);
   delay(10);
}

void goDown(){
 servo.write(SERVO_MIN);
 delay(1000);
}

void printDistance(float distance_mm) {
 if(distance_mm != 0) {
   /* Affiche les résultats en mm, cm et m */
 Serial.print(F("Distance: "));
 Serial.print(distance_mm);
 Serial.print(F("mm ("));
 Serial.print(distance_mm / 10.0, 2);
 Serial.print(F("cm, "));
 Serial.print(distance_mm / 1000.0, 2);
 Serial.println(F("m)"));
 } else {
   Serial.println("0");
 }
}

float getMoyenne(){
 float sum = 0;
 for (int index = 0; index < 10; index++){
   sum = sum + captureArray[index];
 }
 float result = sum/10;
 return result;
}

void useServo(float distance_mm) {
 printDistance(distance_mm);
 if (distance_mm <80) {
   Serial.print(F("servoPosition: "));
   Serial.println(servoPosition);
   if(servoPosition != 6) {
     goUp(SERVO_MIN + (servoPosition*200) + 200);
     servoPosition = servoPosition +1;
   } else {
     goDown();
     servoPosition = 0;
   }
 } else {
   if(servoPosition != 0){
     goDown();
     servoPosition = 0;
   }
 }
}

void check_switches() {
 static byte previousstate[NUMBUTTONS];
 static byte currentstate[NUMBUTTONS];
 byte index;

 for (index = 0; index < NUMBUTTONS; index++) {
   currentstate[index] = digitalRead(buttons[index]);   // read the button
   
   if (currentstate[index] == previousstate[index]) {
     if ((pressed[index] == LOW) && (currentstate[index] == LOW)) {
         // just pressed
         justpressed[index] = 1;
     }
     else if ((pressed[index] == HIGH) && (currentstate[index] == HIGH)) {
         // just released
         justreleased[index] = 1;
     }
     pressed[index] = !currentstate[index];  // remember, digital HIGH means NOT pressed
   }
   //Serial.println(pressed[index], DEC);
   previousstate[index] = currentstate[index];   // keep a running tally of the buttons
 }
}

void playfile(char *name) {
 if (wave.isplaying) {
   wave.stop();
 }
 // look in the root directory and open the file
 f.open(root, name);
 // OK read the file and turn it into a wave object
 wave.create(f);
 // ok time to play! start playback
 wave.play();
}

If you look at the Adafruit website, it says the wave shield is NOT compatible with the Mega (only AT'328 chipsets) so that won't work.

After you change boards, what you will need to do is read in the song list and store it in an array rather than print it out.

Also, you button checking seems very complicated and not really needing to be done inside an ISR. Why don't you just check at the beginning of the loop()? There are also libraries for buttons that do all the debounce sort of stuff.

Hello blh64 !

Thank you for taking the time to reply.

I know that the wave shield is not meant for the mega but I followed some tutorials to hack it and it works just fine for me. I relay needed the mega because I'm controlling a servo, a PIR motion sensor, buttons (4 Channels RF Remote Module) and the wave shield.

For the moment I can control input/output af all the different components.

The tricky part where I realy need some help is to do just as you said : "read in the song list and store it in an array rather than print it out".

How do you do this ? This seems complicated for me because I specialise in Java and not C language.

The easiest way would be to declare a fixed array of strings (maybe 20?) and then copy the filenames into that array, taking care not to exceed your predefined max.

const int maxFilenames = 20;
String filenames[maxFilenames];


int readFileList(FatReader &d)
{
  int currentFileIndex = 0;
  while (readDir(dirBuf) > 0) {     // read the next file in the directory
    // skip subdirs . and ..
    if (dirBuf.name[0] == '.')
      continue;

    String[currentFileIndex] = dirBuf.name;
    currentFileIndex++;

    if ( currentFileIndex == maxFilenames ) {
      break;  // all full, don't read any more
    }
  return currentFileIndex;
}

Then, inside setup(), you call this function which returns the number of files read and you simply index forward or backward with some variable (maybe currentIndex?) taking care to wrap around from 0..numer of files read.

I'll leave that as an exercise for you :slight_smile:

Thanks a lot. I'll try this at home today.

I was wondering if you knew a way to make the size of "String filenames[]" dynamic ?

Maybe i'll check out if there is not a library that would do this stuff for me :slight_smile:

And thanks for the exercise part, there is no comfort in work if there is no hardwork :wink:

I was wondering if you knew a way to make the size of "String filenames[]" dynamic ?

malloc().

But, you DO need to know the size.

You could scan the card twice. Count the files on the first pass. Copy (strdup()) the names to a char * array, NOT a String array, on the second pass.

That seems like a realy nice idea !!! I'll try to post the code tonight or tomorrow if it works well !

Thanks a lot again !

Hi there !

So I have tried some stuff but I'm still not managing to get my result.

First of all, I had to declare my array out of my setup loop for it to become a global variable. That means that I had to set it to a random number.

Then I manage to put in it the appropriate strings through the save saveEntryName function.

When I Serial.write my result in the saveEntryName function I get the good result, the result I'm excpecting. When I do the exact same print from my setup loop I get empty results.

Here is the code :

int numberOfFiles = 0;
int currentFile = 0;
char* filesList[150];

void setup() {
 Serial.begin(9600);  

 //Wave shield configuration
 card.init();
 card.partialBlockRead(true);
 uint8_t part;
 for (part = 0; part < 5; part++) {   // we have up to 5 slots to look in
   if (vol.init(card, part)) 
     break;                           // we found one, lets bail
 }
 root.openRoot(vol);
 getNumberOfFiles(root);
 root.rewind();
 Serial.print("Good content : ");
 Serial.println();
 getFiles(root);
 
 Serial.print("Bad content : ");
 Serial.println();
 Serial.write("\"");
 Serial.write(filesList[0]);
 Serial.write("\"");
 Serial.println();
 Serial.write("\"");
 Serial.write(filesList[1]);
 Serial.write("\"");
 Serial.println();
 Serial.write("\"");
 Serial.write(filesList[2]);
 Serial.write("\"");

}

void getFiles(FatReader &d) { 
 while (d.readDir(dirBuf) > 0) {  
    
   if (dirBuf.name[0] == '.') continue; 
       
   if (DIR_IS_SUBDIR(dirBuf)) {   
     FatReader s;                
     dirLevel += 2;               
     if (s.open(vol, dirBuf))
     getFiles(s);                    
     dirLevel -=2;               
   } else {      
     saveEntryName(dirBuf);
     currentFile += 1;
   }
 }
}

/**
* Return the entry name
*/
void saveEntryName(dir_t &dir) {
 String entryNameString = "";
 for (uint8_t i = 0; i < 11; i++) {
   if (dir.name[i] == ' ') continue;
   if (i == 8) entryNameString.concat('.');
   char a;
   a = dir.name[i];
   entryNameString.concat(a);
 }
 char entryNameChar[entryNameString.length()+1];
 for (int j = 0; j< entryNameString.length()+1; j++) {
   entryNameChar[j] = entryNameString[j];
 }
 char *entryNamePointer = entryNameChar;
 filesList[currentFile] = entryNamePointer;
 Serial.write("\"");
 Serial.write(filesList[currentFile]);
 Serial.write("\"");
 Serial.println();
}

void getNumberOfFiles(FatReader &d)
{ 
 while (d.readDir(dirBuf) > 0) {     // read the next file in the directory 
   if (dirBuf.name[0] == '.') continue; // skip subdirs . and ..
   if (DIR_IS_SUBDIR(dirBuf)) {   // we will recurse on any direcory
     FatReader s;                 // make a new directory object to hold information
     dirLevel += 2;               // indent 2 spaces for future prints
     if (s.open(vol, dirBuf)) 
     getNumberOfFiles(s);                    // list all the files in this directory now!
     dirLevel -=2;                // remove the extra indentation
   } else {
     numberOfFiles += 1;
   }
 }
}

Here is the result :

Good content :
"INDEXE~1"
"WPSETT~1.DAT"
"TEST.WAV"
Bad content :
""
""
""

Does anybody have an idea ?

Hey !

Wonderfull news, I found an old code I did 5 years ago in my studies and I found my awnser !!

Here is the code if it could help anybody :

char **filesList;

void setup() {

  *
  *
  *

 getNumberOfFiles(root);
 filesList = malloc(numberOfFiles * sizeof(*filesList));
 
 for (int i = 0; i< numberOfFiles; i++) {
   filesList[i] = malloc(30 * sizeof(**filesList));
   if (filesList[i]==NULL) {
     for (i = i-1 ; i >= 0 ; i--) {
       free(filesList[i]);
     }
   }
 }
 
 root.rewind();
 getFiles(root);

  *
  *
  *

}

void getFiles(FatReader &d) { 
 
  *
  * no change
  *

}

void saveEntryName(dir_t &dir) {
 String entryNameString = "";
 for (uint8_t i = 0; i < 11; i++) {
   if (dir.name[i] == ' ') continue;
   if (i == 8) entryNameString.concat('.');
   char a;
   a = dir.name[i];
   entryNameString.concat(a);
 }
 char entryNameChar[entryNameString.length()+1];
 for (int j = 0; j< entryNameString.length()+1; j++) {
   entryNameChar[j] = entryNameString[j];
 }
 strcpy(filesList[currentFile], entryNameChar);
}

Hope it helps !! :slight_smile: