0

HALP! Wave Shield n00b issue
Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.

Re: HALP! Wave Shield n00b issue

by hughie_san on Fri Dec 07, 2018 12:53 pm

Yep, makes sense. That's great :)

Thank you again, you are a wizard!!!

Now to try and merge the code from the SampleRateHC example with this (°_o)

hughie_san
 
Posts: 17
Joined: Fri Nov 30, 2018 2:21 am

Re: HALP! Wave Shield n00b issue

by hughie_san on Fri Dec 07, 2018 3:23 pm

I'm very sorry but I have one final question (I promise/hope).

In this version of the code below I have been able to merge the final version of the code used to switch tracks with the sample rate example (SampleRateHC) from the waveHC library. I am able to skip through tracks and adjust the sample rate prior to playing the track. I have achieved this by inserting a 'break;' but I would like to be able to adjust the sample rate as the files are playing. I can see that the 'while (wave isplaying)' is crucial here but without the 'break;' it prevents the code from returning to the currentsongcount and receiving input from the button (until the track has finished playing). Would I need to use a 'do...while' instead somewhere? Sorry I've tried but can't seem to work this out.

Code: Select all | TOGGLE FULL SIZE
#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

int currentSongcount = 0;    // Keeps track of which song to play each time the button is pressed
int previousSongcount = 0; // previous song count
int maxSongcount = 3;        // Total number of songs on the SD card

#define DEBOUNCE 5  // button debouncer

// here is where we define the buttons that we'll use. button "1" is the first, button "6" is the 6th, etc
byte buttons[] = {8};
// This handy macro lets us determine how big the array up above is, by checking the size
#define NUMBUTTONS sizeof(buttons)
// we will track if a button is just pressed, just released, or 'pressed' (the current state
volatile byte pressed[NUMBUTTONS], justpressed[NUMBUTTONS], justreleased[NUMBUTTONS];

// 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;
 
  // set up serial port
  Serial.begin(9600);
  putstring_nl("WaveHC with ");
  Serial.print(NUMBUTTONS, DEC);
  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);

   //button
  pinMode(8, INPUT_PULLUP);
 
  // pin13 LED
  pinMode(13, OUTPUT);
 
  // Make input & enable pull-up resistors on switch pins
  for (i=0; i< NUMBUTTONS; i++) {
    pinMode(buttons[i], INPUT);
    digitalWrite(buttons[i], HIGH);
  }
 
  //  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) {
  check_switches();
}

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
   
    /*     
    Serial.print(index, DEC);
    Serial.print(": cstate=");
    Serial.print(currentstate[index], DEC);
    Serial.print(", pstate=");
    Serial.print(previousstate[index], DEC);
    Serial.print(", press=");
    */
   
    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
  }
}

 int16_t lastpotval = 0;
 #define HYSTERESIS 3
 int16_t potval;
 uint32_t newsamplerate;

void loop()
{
   if ((justpressed[0]) ||(! wave.isplaying))
   {
      if (justpressed[0])
      {
         currentSongcount == currentSongcount++;
         if (currentSongcount > maxSongcount)
         {
            currentSongcount = 1;
         }
         Serial.println(currentSongcount);
         delay(20);  // 20 ms of debounce
         justpressed[0] = 0;
      }

      if (currentSongcount == 1)
      {
         playfile("1.wav");
      }
      else if (currentSongcount == 2) 
      {
         playfile("2.wav");
      }
      else if (currentSongcount == 3) 
      {
         playfile("3.wav");
      }
     
      while (wave.isplaying) {
     potval = analogRead(0);
     if ( ((potval - lastpotval) > HYSTERESIS) || ((lastpotval - potval) > HYSTERESIS)) {
         putstring("pot = ");
         Serial.println(potval, DEC);
         putstring("tickspersam = ");
         Serial.print(wave.dwSamplesPerSec, DEC);
         putstring(" -> ");
         newsamplerate = wave.dwSamplesPerSec;
         newsamplerate *= potval;
         newsamplerate /= 512;   // we want to 'split' between sped up and slowed down.
        if (newsamplerate > 24000) {
          newsamplerate = 24000; 
        }
        if (newsamplerate < 1000) {
          newsamplerate = 1000; 
        }       
        wave.setSampleRate(newsamplerate);
       
        Serial.println(newsamplerate, DEC);
        lastpotval = potval;
        break;
     }
     delay(100);
   }
   sdErrorCheck();
 }
}



// 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!
   wave.play();
}

hughie_san
 
Posts: 17
Joined: Fri Nov 30, 2018 2:21 am

Re: HALP! Wave Shield n00b issue

by adafruit_support_bill on Fri Dec 07, 2018 4:59 pm

I'm not familiar with that sketch. But it looks like the sample-rate logic needs to be moved to the top level of the loop() to play nice with the rest of your code.

Code: Select all | TOGGLE FULL SIZE
void loop()
{
   if ((justpressed[0]) ||(! wave.isplaying))
   {
      if (justpressed[0])
      {
         currentSongcount == currentSongcount++;
         if (currentSongcount > maxSongcount)
         {
            currentSongcount = 1;
         }
         Serial.println(currentSongcount);
         delay(20);  // 20 ms of debounce
         justpressed[0] = 0;
      }

      if (currentSongcount == 1)
      {
         playfile("1.wav");
      }
      else if (currentSongcount == 2) 
      {
         playfile("2.wav");
      }
      else if (currentSongcount == 3) 
      {
         playfile("3.wav");
      }
    }
     
     potval = analogRead(0);
     if ( ((potval - lastpotval) > HYSTERESIS) || ((lastpotval - potval) > HYSTERESIS))
     {
         putstring("pot = ");
         Serial.println(potval, DEC);
         putstring("tickspersam = ");
         Serial.print(wave.dwSamplesPerSec, DEC);
         putstring(" -> ");
         newsamplerate = wave.dwSamplesPerSec;
         newsamplerate *= potval;
         newsamplerate /= 512;   // we want to 'split' between sped up and slowed down.
        if (newsamplerate > 24000) {
          newsamplerate = 24000; 
        }
        if (newsamplerate < 1000) {
          newsamplerate = 1000; 
        }       
        wave.setSampleRate(newsamplerate);
       
        Serial.println(newsamplerate, DEC);
        lastpotval = potval;
        break;
     }
}

adafruit_support_bill
 
Posts: 73049
Joined: Sat Feb 07, 2009 10:11 am

Re: HALP! Wave Shield n00b issue

by hughie_san on Sat Dec 08, 2018 4:26 am

Hi there, thank you so much for the reply. Unfortunately that edit to the code you sent over won't verify. I'm getting the following error massage -

Adafruit_Forum_Response_pitch_2:223:9: error: break statement not within loop or switch
break;
^
exit status 1
break statement not within loop or switch

hughie_san
 
Posts: 17
Joined: Fri Nov 30, 2018 2:21 am

Re: HALP! Wave Shield n00b issue

by adafruit_support_bill on Sat Dec 08, 2018 8:09 am

You can probably just delete that break statement. It serves no purpose in that loop.

adafruit_support_bill
 
Posts: 73049
Joined: Sat Feb 07, 2009 10:11 am

Re: HALP! Wave Shield n00b issue

by hughie_san on Sat Dec 08, 2018 8:53 pm

It works!

Thanks so much for your help :)

hughie_san
 
Posts: 17
Joined: Fri Nov 30, 2018 2:21 am

Re: HALP! Wave Shield n00b issue

by adafruit_support_bill on Sun Dec 09, 2018 7:43 am

Good to hear. Thanks for the follow-up.

adafruit_support_bill
 
Posts: 73049
Joined: Sat Feb 07, 2009 10:11 am

Re: HALP! Wave Shield n00b issue

by hughie_san on Sun Dec 09, 2018 8:44 am

I've noticed one small issue, actually! Unfortunately, the sample rate resets each time the track loops back to the start.

Would it be possible to put something early in the code so that the pot value for the analog pin is checked each time a track begins? I have experimented with this but haven't had much luck (I ended up getting it but the loop function was disabled as a result). Thanks again in advance.

hughie_san
 
Posts: 17
Joined: Fri Nov 30, 2018 2:21 am

Re: HALP! Wave Shield n00b issue

by adafruit_support_bill on Sun Dec 09, 2018 9:11 am

You can try setting the value of lastpotval to zero after each call to playfile.

adafruit_support_bill
 
Posts: 73049
Joined: Sat Feb 07, 2009 10:11 am

Re: HALP! Wave Shield n00b issue

by hughie_san on Sun Dec 09, 2018 9:20 am

Yes I tried exactly that by using the code below but it doesn't seem to work. Maybe my logic is incorrect?

Code: Select all | TOGGLE FULL SIZE
 if (currentSongcount == 1)
      {
         playfile("1.wav");
         potval = analogRead(0);
      }
      else if (currentSongcount == 2) 
      {
         playfile("2.wav");
         potval = analogRead(0);
      }
      else if (currentSongcount == 3) 
      {
         playfile("3.wav");
         potval = analogRead(0);
      }
    }

hughie_san
 
Posts: 17
Joined: Fri Nov 30, 2018 2:21 am

Re: HALP! Wave Shield n00b issue

by adafruit_support_bill on Sun Dec 09, 2018 9:59 am

Yes I tried exactly that by using the code below

That is not the same thing. You need to set the value to zero - not read the pot again.

Code: Select all | TOGGLE FULL SIZE
lastpotval = 0;

adafruit_support_bill
 
Posts: 73049
Joined: Sat Feb 07, 2009 10:11 am

Re: HALP! Wave Shield n00b issue

by hughie_san on Sun Dec 09, 2018 10:06 am

Ah I see! I assumed reading it would return this result but obviously not. Thanks again, it works. Your help was much appreciated :)

hughie_san
 
Posts: 17
Joined: Fri Nov 30, 2018 2:21 am

Re: HALP! Wave Shield n00b issue

by petespaco on Tue Dec 18, 2018 1:11 pm

Wow!
For a self proclaimed "n00b", you are doing quite well!!!
Great job of feeding back, too.

petespaco
 
Posts: 70
Joined: Thu Apr 19, 2012 7:53 pm
Location: west central wisconsin

Re: HALP! Wave Shield n00b issue

by hughie_san on Mon May 06, 2019 10:58 pm

Hi guys,

this build was successful, we gave it to our friends as a gift and they love it. We're planning on building a couple more for friends/family, before I do so I wanted to ask if there is a different board that would be capable of performing this task? The current setup that went in to this one seems like it might be overcomplicated/expensive for the task at hand? On top of the arduino Uno and the wave shield I also had to purchase the PAM8302 to get the speaker level to a desirable listening point. Would I be able to achieve the same results with this https://www.adafruit.com/product/2217 ?

Or is there another/better solution. If you inspect the code below you can see my functionality. Essentially - Playing/Looping around 8 or 9 short audio files on a small 3W speaker, switching between tracks with a single switch, controlling pitch and volume with separate pots...

Any help would be greatly appreciated :)

Code: Select all | TOGGLE FULL SIZE
#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

int currentSongcount = 0;    // Keeps track of which song to play each time the button is pressed
int previousSongcount = 0; // previous song count
int maxSongcount = 9;        // Total number of songs on the SD card

#define DEBOUNCE 5  // button debouncer

// here is where we define the buttons that we'll use. button "1" is the first, button "6" is the 6th, etc
byte buttons[] = {8};
// This handy macro lets us determine how big the array up above is, by checking the size
#define NUMBUTTONS sizeof(buttons)
// we will track if a button is just pressed, just released, or 'pressed' (the current state
volatile byte pressed[NUMBUTTONS], justpressed[NUMBUTTONS], justreleased[NUMBUTTONS];

// 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;
 
  // set up serial port
  Serial.begin(9600);
  putstring_nl("WaveHC with ");
  Serial.print(NUMBUTTONS, DEC);
  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);

   //button
  pinMode(8, INPUT_PULLUP);
 
  // pin13 LED
  pinMode(13, OUTPUT);
 
  // Make input & enable pull-up resistors on switch pins
  for (i=0; i< NUMBUTTONS; i++) {
    pinMode(buttons[i], INPUT);
    digitalWrite(buttons[i], HIGH);
  }
 
  //  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) {
  check_switches();
}

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
   
    /*     
    Serial.print(index, DEC);
    Serial.print(": cstate=");
    Serial.print(currentstate[index], DEC);
    Serial.print(", pstate=");
    Serial.print(previousstate[index], DEC);
    Serial.print(", press=");
    */
   
    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
  }
}

 int16_t lastpotval = 0;
 #define HYSTERESIS 3
 int16_t potval;
 uint32_t newsamplerate;
 
void loop()
{
   if ((justpressed[0]) ||(! wave.isplaying))
   {
      if (justpressed[0])
      {
         currentSongcount == currentSongcount++;
         if (currentSongcount > maxSongcount)
         {
            currentSongcount = 1;
         }
         Serial.println(currentSongcount);
         delay(20);  // 20 ms of debounce
         justpressed[0] = 0;
      }

      if (currentSongcount == 1)
      {
         playfile("1.wav");
         lastpotval = 0;
      }
      else if (currentSongcount == 2) 
      {
         playfile("2.wav");
         lastpotval = 0;
      }
      else if (currentSongcount == 3) 
      {
         playfile("3.wav");
         lastpotval = 0;
      }
       else if (currentSongcount == 4) 
      {
         playfile("4.wav");
         lastpotval = 0;
      }
       else if (currentSongcount == 5) 
      {
         playfile("5.wav");
         lastpotval = 0;
      }
       else if (currentSongcount == 6) 
      {
         playfile("6.wav");
         lastpotval = 0;
      }
       else if (currentSongcount == 7) 
      {
         playfile("7.wav");
         lastpotval = 0;
      }
       else if (currentSongcount == 8) 
      {
         playfile("8.wav");
         lastpotval = 0;
      }
       else if (currentSongcount == 9) 
      {
         playfile("9.wav");
         lastpotval = 0;
      }
    }
     
     potval = analogRead(0);
     if ( ((potval - lastpotval) > HYSTERESIS) || ((lastpotval - potval) > HYSTERESIS))
     {
         putstring("pot = ");
         Serial.println(potval, DEC);
         putstring("tickspersam = ");
         Serial.print(wave.dwSamplesPerSec, DEC);
         putstring(" -> ");
         newsamplerate = wave.dwSamplesPerSec;
         newsamplerate *= potval;
         newsamplerate /= 512;   // we want to 'split' between sped up and slowed down.
        if (newsamplerate > 44000) {
          newsamplerate = 44000; 
        }
        if (newsamplerate < 5000) {
          newsamplerate = 5000; 
        }       
        wave.setSampleRate(newsamplerate);
       
        Serial.println(newsamplerate, DEC);
        lastpotval = potval;
     }
}

// 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!
   wave.play();
}

hughie_san
 
Posts: 17
Joined: Fri Nov 30, 2018 2:21 am

Re: HALP! Wave Shield n00b issue

by adafruit_support_bill on Tue May 07, 2019 6:21 am

A step up from the wave shield would be a MusicMaker shield (or feather wing). These use the VS1053 chip, which is capable of playing all sorts of music formats - and in stereo. We have these boards available with built-in amplifiers.

https://www.adafruit.com/product/3436
https://www.adafruit.com/product/1788

The VS1053 is also capable of things like shifting pitch. Although such operations require a bit of hacking of the chip firmware.

A white-paper on pitch-shifting can be found here.
http://www.hmangas.com/Electronica/Data ... Plugin.pdf

adafruit_support_bill
 
Posts: 73049
Joined: Sat Feb 07, 2009 10:11 am

Please be positive and constructive with your questions and comments.