Programming Arduino Uno to play specific WAV based off input voltages

Hello everyone! I am completely new to the Arduino community and have been messing with my first micro-controller for several weeks now but am now seeking advice from the experts. I have very limited programming knowledge but have been going through the starter book examples. My current setup is an Arduino Uno with a WAV shield (with speaker) placed on top. I have uploaded the daphc example (plays all wav files on SD card in a loop) to test whether or not the assembled shield worked properly and had all the music on the SD card successfully loop (yay!). I also checked out a thresholding example that allowed me to turn on 3 different LED’s based on input voltages controlled by a potentiometer. This would be very similar to what I need IF it played a specific WAV file along with the corresponding LED. So what I need, is the Arduino to receive a voltage (in my case it will be an EMG signal passed through a differential amplifier->op-amp->low pass filter->high pass filter->Arduino A0 input pin) that will then cause one LED to light up and play a specific wav file in a loop as long as the input voltage is within a threshold range. These music files would have to be interruptable since the EMG will fluctuate upon strain.

Unfortunately my minimal programming experience has prevented me from figuring out the audio part. I have tried piecing together example code and modifying it, along with mixing my LED program with the audio examples, but have had no success =(. My code so far can be verified without errors, but it doesn’t play a specific song, or any song for that matter. Since my initial 3 LED system uses 3 different output pins, I am suspecting that I am scrambling my pins (along with dozens of other errors I’m sure!). The daphc example uses too many terms I do not recognize and seem to be referencing the in depth wav libraries I downloaded (WaveHC). If anyone could help me it would be greatly appreciated! Also I send my thanks to anyone who even read this post but was unable to help =). Here is my code so far:

#include <FatReader.h>
#include <SdReader.h>
#include <avr/pgmspace.h>
#include "WaveUtil.h"
#include "WaveHC.h"


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 filesystem on the card
FatReader f;      // This holds the information for the file we're play

WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time

const int analogPin = A0;    // pin that the sensor is attached to
const int ledPinOne = 13;       // pin that the LED is attached to
const int ledPinTwo = 12;
const int ledPinThree = 11;  
const int thresholdOne = 1000;   // an arbitrary threshold level that's in the range of the analog input
const int thresholdTwo = 500;
const int thresholdThree = 100;

// this handy function will return the number of bytes currently free in RAM, great for debugging!   
int freeRam(void)
{
  extern int  __bss_end; 
  extern int  *__brkval; 
  int free_memory; 
  if((int)__brkval == 0) {
    free_memory = ((int)&free_memory) - ((int)&__bss_end); 
  }
  else {
    free_memory = ((int)&free_memory) - ((int)__brkval); 
  }
  return free_memory; 
} 

void sdErrorCheck(void)
{
  if (!card.errorCode()) return;
  putstring("\n\rSD I/O error: ");
  Serial.print(card.errorCode(), HEX);
  putstring(", ");
  Serial.println(card.errorData(), HEX);
  while(1);
}

void setup() {
  byte i;
  pinMode(ledPinOne, OUTPUT);
  pinMode(ledPinTwo, OUTPUT);
  pinMode(ledPinThree, OUTPUT);
  // set up serial port
  Serial.begin(9600);
  putstring_nl("WaveHC with ");
  putstring_nl("buttons");
  
  putstring("Free RAM: ");       // This can help with debugging, running out of RAM is bad
  Serial.println(freeRam());      // if this is under 150 bytes it may spell trouble!
  
  // Set the output pins for the DAC control. This pins are defined in the library
  pinMode(2, OUTPUT);
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
 
  // pin13 LED
  pinMode(13, OUTPUT);
 
 
  //  if (!card.init(true)) { //play with 4 MHz spi if 8MHz isn't working for you
  if (!card.init()) {         //play with 8 MHz spi (default faster!)  
    putstring_nl("Card init. failed!");  // Something went wrong, lets print out why
    sdErrorCheck();
    while(1);                            // then 'halt' - do nothing!
  }
  
  // enable optimize read - some cards may timeout. Disable if you're having problems
  card.partialBlockRead(true);
 
// Now we will look for a FAT partition!
  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
  }
  if (part == 5) {                       // if we ended up not finding one  :(
    putstring_nl("No valid FAT partition!");
    sdErrorCheck();      // Something went wrong, lets print out why
    while(1);                            // then 'halt' - do nothing!
  }
  
  // Lets tell the user about what we found
  putstring("Using partition ");
  Serial.print(part, DEC);
  putstring(", type is FAT");
  Serial.println(vol.fatType(),DEC);     // FAT16 or FAT32?
  
  // Try to open the root directory
  if (!root.openRoot(vol)) {
    putstring_nl("Can't open root dir!"); // Something went wrong,
    while(1);                             // then 'halt' - do nothing!
  }
  
  // Whew! We got past the tough parts.
  putstring_nl("Ready!");
  
  TCCR2A = 0;
  TCCR2B = 1<<CS22 | 1<<CS21 | 1<<CS20;

  //Timer2 Overflow Interrupt Enable
  TIMSK2 |= 1<<TOIE2;


}

SIGNAL(TIMER2_OVF_vect);

void loop() {
  byte i;
// read the value of the potentiometer:
  int analogValue = analogRead(analogPin);
  
  if (analogValue < thresholdThree) {
    playfile("Pink_Try.WAV");
    while (wave.isplaying && analogValue < thresholdThree) {
      //Serial.print(".");
    }
    wave.stop();    
  }
  if (analogValue < thresholdTwo && analogValue > thresholdThree) {
    playfile("EOT.WAV");
    while (wave.isplaying && analogValue < thresholdTwo && analogValue > thresholdThree) {
      //Serial.print(".");
    }
    wave.stop();    
  }
  if (analogValue > thresholdTwo) {
    playfile("WhatDoesntKU.WAV");
    while (wave.isplaying && analogValue > thresholdTwo) {
      //Serial.print(".");
    }
    wave.stop();    
  }
}



// Plays a full file from beginning to end with no pause.
void playcomplete(char *name) {
  // call our helper to find and play this name
  playfile(name);
  while (wave.isplaying) {
  // do nothing while its playing
  }
  // now its done playing
}

void playfile(char *name) {
  // see if the wave object is currently doing something
  if (wave.isplaying) {// already playing something, so stop it!
    wave.stop(); // stop it
  }
  // look in the root directory and open the file
  if (!f.open(root, name)) {
    putstring("Couldn't open file "); Serial.print(name); return;
  }
  // OK read the file and turn it into a wave object
  if (!wave.create(f)) {
    putstring_nl("Not a valid WAV"); return;
  }
  
  // ok time to play! start playback
  wave.play();
}

Thank you all again!
Special thanks to Boguz for helping clean up my thresholding so far! No sounds are playing still however.

it seems to me that when you say:

if (analogValue > thresholdTwo) {
    ...  
}
if (analogValue < thresholdTwo) {
  ...
}
if (analogValue < thresholdThree) {
  ...  
}

you should be more precise.

Let me give you an example of what i mean.
Let’s say the analogValue is 80. Which file would you like to be played? Which of the if statements is true?

if (analogValue < thresholdTwo) {
    ...
  }

or

if (analogValue < thresholdThree) {
    ...
  }

?
In this case (if analogValue would be 80) both this functions would be true because 80 is smaller than thresholdTwo (500) and it is also smaller than thresholdThree (100).

You could maybe use && (AND) operators to define a better range of values when you would like an if statement to be true, something like

// value smaller the thresholdThree
if (analogValue < thresholdThree) {
  // play file x 
}

// value between thresholdTwo and thresholdThree
 if (analogValue < thresholdTwo && analogValue > thresholdThree) {
  play file y
}

… and so on to cover all the ranges you want to check.

Thank you very much Boguz! I cleaned up my thresholding. Currently the music does not play, but having the thresholding correct is certainly a step forward.

Currently the music does not play, but having the thresholding correct is certainly a step forward.

No, it isn’t. You should create a sketch that does NOTHING but play a file. You shouldn’t be diddling around with free memory or extensive error reporting or reading ANY sensors or diddling with timers or using any interrupts.

    playfile("Pink_Try.WAV");
    while (wave.isplaying && analogValue < thresholdThree) {
      //Serial.print(".");
    }
    wave.stop();

Once you start playing the file, the only thing that will stop the while loop is for the song to come to the end. The values of analogValue and thresholdThree never change, so they are useless. Once the song has come to an end, is there really any need to tell it stop?

The reason for including all the checking for free memory and error reporting, is simply due to their presence in all the examples I have seen. Much of "my" code is copying from these examples and my knowledge of what is required when reading an SD card is virtually zero. The most basic example Adafruit(brand of my shield) gives is playing all the wav files on the SD card in a loop and even that includes all this extra code. What is the simplest code necessary to read the SD card and then play a specific file? In the end I will need the program to compare analogValue to the thresholds during playback to determine whether or not the arduino should stop the current song and switch to a different one. Thank you for your input PaulS!

In the end I will need the program to

The key phrase there is "in the end". You don't need that right now. Right now, you need the thing to make some noise. Right?

void loop() 
{ // Down here, where it belongs
    playfile("Pink_Try.WAV");
}

If this doesn't make any noise, the file doesn't exist on the SD card or the hardware is miswired. No amount of f***ing around with code will fix either of those problems.

When I verify your code, it gives me:

sketch_apr23b.ino: In function 'void loop()':
sketch_apr23b:26: error: 'play' was not declared in this scope

to have the program verify through without errors the minimum code is:

#include "WaveUtil.h"
#include "WaveHC.h"
WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time

void setup() {
}

void loop() 
{ // Down here, where it belongs
    playfile("Pink_Try.WAV");
}

void playfile(char *name) {
  // see if the wave object is currently doing something
  if (wave.isplaying) {// already playing something, so stop it!
    wave.stop(); // stop it
  }
}

No sound plays but I know that the shield works, since when I use the daphc example code it plays all the wav files in a loop without issue. Again thank you for your time.

void playfile(char *name) {
  // see if the wave object is currently doing something
  if (wave.isplaying) {// already playing something, so stop it!
    wave.stop(); // stop it
  }
}

If not, God forbid that we should actually do something with the name that was entered.

I have gotten everything working after I read that the Wave shield is already playing the song asynchronously. The thresholds will be modified of course, but the ones in my final code show that it works.

#include <WaveHC.h>
#include <WaveUtil.h>
#include <avr/io.h>
#include <avr/interrupt.h>

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
FatReader file;   // This object represent the WAV file 
WaveHC wave;      // This is the only wave (audio) object, since we will only play one at a time

const int analogPin = A0;    // pin that the sensor is attached to
const int thresholdOne = 5;   // 5V threshold
const int thresholdTwo = 3;   // 3V threshold
const int thresholdThree = 1; // 1V threshold
float Voltage_Reading = analogPin*(5.0/1023.0);

#define playcomplete(x) ROM_playcomplete(PSTR(x))         // save RAM by using program memory strings



void setup() {
  Serial.begin(9600);           // set up Serial library at 9600 bps
  Serial.println(F("Wave test!"));

  pinMode(2, OUTPUT); 
  pinMode(3, OUTPUT);
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);

  if (!card.init()) {
    Serial.println(F("Card init. failed!")); return;
  }
  // enable optimize read - some cards may timeout. Disable if you're having problems
  card.partialBlockRead(true);
  
  // Now we will look for a FAT partition!
  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
  }
  if (part == 5) {                     // if we ended up not finding one  :(
    Serial.println(F("No valid FAT partition!"));  // Something went wrong, lets print out why
  }
  
  // Lets tell the user about what we found
  putstring("Using partition ");
  Serial.print(part, DEC);
  Serial.print(F(", type is FAT"));
  Serial.println(vol.fatType(), DEC);     // FAT16 or FAT32?
  
  // Try to open the root directory
  if (!root.openRoot(vol)) {
    Serial.println(F("Can't open root dir!"));      // Something went wrong,
  }
  
  // Whew! We got past the tough parts.
  Serial.println(F("Files found (* = fragmented):"));

  // Print out all of the files in all the directories.
  root.ls(LS_R | LS_FLAG_FRAGMENTED);
}

void loop() { 
  int analogValue = analogRead(analogPin);                      //reads A0 pin resistance
  float Voltage_Reading = analogValue*(5.0/1023.0);             //converts resistance reading from A0 pin to voltage
  Serial.println(Voltage_Reading, DEC);                         //prints out voltage reading to serial monitor
   long time;
 
 if (Voltage_Reading < thresholdThree) {
   wave.stop();                                                 //stops any current audio
   playfile("PinkCM.WAV");                                      //plays the specified audio
   while (Voltage_Reading < thresholdThree) {                   //if voltage is within the first threshold range, it will continue
     int analogValue = analogRead(analogPin);                   //reads A0 pin resistance
     float Voltage_Reading = analogValue*(5.0/1023.0);          //converts resistance reading from A0 pin to voltage
     Serial.println(analogValue, DEC);                          //prints out voltage reading to serial monitor
     Serial.println("Pink");                                    //prints out song name to serial monitor (for testing purposes)
     if (Voltage_Reading > thresholdThree) {                    //if voltage is not within the first threshold range, it will break the while loop
     break;}
     delay(100);
     if (wave.isplaying){}                                      //Does nothing if the audio is playing and breaks the while loop if it has stopped
      else{break;}
    }
    wave.stop();

}
 if (Voltage_Reading < thresholdTwo && Voltage_Reading > thresholdThree) {
   wave.stop();                                               
    playfile("EOTCM.WAV");                                    
    while (Voltage_Reading < thresholdTwo && Voltage_Reading > thresholdThree) { 
      int analogValue = analogRead(analogPin);
      float Voltage_Reading = analogValue*(5.0/1023.0);
      Serial.println(Voltage_Reading, DEC);
      Serial.println("EOT");
      if (Voltage_Reading > thresholdTwo || Voltage_Reading < thresholdThree) {
     break;}
      delay(100);
      if (wave.isplaying){}
       else{break;}
      //Serial.print(".");
    } 
   wave.stop();  
}

  if (Voltage_Reading > thresholdTwo) {
    wave.stop();
    playfile("WhatCM.WAV");
    while (Voltage_Reading > thresholdTwo) {
      int analogValue = analogRead(analogPin);
      float Voltage_Reading = analogValue*(5.0/1023.0);
      Serial.println(Voltage_Reading, DEC);
      Serial.println("What");
      if (Voltage_Reading < thresholdTwo) {
     break;}
      delay(100);
      if (wave.isplaying){}
       else{break;}
 
    }
    wave.stop();
  }
}

void playfile(char *name) {

   if (!file.open(root, name)) {
      Serial.println(F(" Couldn't open file")); return;
   }
   if (!wave.create(file)) {
     Serial.println(F(" Not a valid WAV")); return;
   }
   // ok time to play!
   wave.play();
}

Thanks again to everyone who took the time to read my problem!