Stop WAV files from queuing

Status
Not open for further replies.

Robhilken

Member
Hi,

I have a setup with 2 teensys, the first is the main controller and sends a number over serial (using easy transfer) after a button push to the other, which tells the second teensy which WAV file to play. I have to do this as the first is doing a lot of FastLED stuff and it wasn't playing nicely with that and the audio on one teensy

At the moment, it is queing the wav files, so when you hit the pushbutton (on the first teensy) several times, it queues the wav file so it finishes playing each time before the next sample starts.

What i would like is either to layer the WAVs up, so the first carries on playing but the next sample starts at the moment the button is pushed, or if that is not possible then to stop the first wav playing as soon as the second one starts.

Here's the code from the audio teensy that recieves the number over easytransfer.

Ignore the pushbutton in this code as it's just used for testing without the serial.

As always, I'm grateful for any help!

Best,

Rob

Code:
// Simple WAV file player example
//
// Three types of output may be used, by configuring the code below.
//
//   1: Digital I2S - Normally used with the audio shield:
//         http://www.pjrc.com/store/teensy3_audio.html
//
//   2: Digital S/PDIF - Connect pin 22 to a S/PDIF transmitter
//         https://www.oshpark.com/shared_projects/KcDBKHta
//
//   3: Analog DAC - Connect the DAC pin to an amplified speaker
//         http://www.pjrc.com/teensy/gui/?info=AudioOutputAnalog
//
// To configure the output type, first uncomment one of the three
// output objects.  If not using the audio shield, comment out
// the sgtl5000_1 lines in setup(), so it does not wait forever
// trying to configure the SGTL5000 codec chip.
//
// The SD card may connect to different pins, depending on the
// hardware you are using.  Uncomment or configure the SD card
// pins to match your hardware.
//
// Data files to put on your SD card can be downloaded here:
//   http://www.pjrc.com/teensy/td_libs_AudioDataFiles.html
//
// This example code is in the public domain.

#include <Arduino.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce2.h>
#include <EasyTransfer.h>

AudioPlaySdWav playWav1;
// Use one of these 3 output types: Digital I2S, Digital S/PDIF, or Analog DAC
AudioOutputI2S audioOutput;
//AudioOutputSPDIF       audioOutput;
//AudioOutputAnalog      audioOutput;
AudioConnection patchCord1(playWav1, 0, audioOutput, 0);
AudioConnection patchCord2(playWav1, 1, audioOutput, 1);
AudioControlSGTL5000 sgtl5000_1;

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 7
#define SDCARD_SCK_PIN 14

// Use these with the Teensy 3.5 & 3.6 SD card
//#define SDCARD_CS_PIN    BUILTIN_SDCARD
//#define SDCARD_MOSI_PIN  11  // not actually used
//#define SDCARD_SCK_PIN   13  // not actually used

// Use these for the SD+Wiz820 or other adaptors
//#define SDCARD_CS_PIN    4
//#define SDCARD_MOSI_PIN  11
//#define SDCARD_SCK_PIN   13


/*  ========== EasyTransfer ============
*/
//create object
EasyTransfer ET; 

struct RECEIVE_DATA_STRUCTURE{
  //put your variable definitions here for the data you want to receive
  //THIS MUST BE EXACTLY THE SAME ON THE OTHER ARDUINO
  int16_t sound;
};

//give a name to the group of data
RECEIVE_DATA_STRUCTURE mydata;

void playFile(const char *filename)
{
  Serial.print("Playing file: ");
  Serial.println(filename);

  // Start playing the file.  This sketch continues to
  // run while the file plays.
  playWav1.play(filename);

  // A brief delay for the library read WAV info
  delay(5);

  // Simply wait for the file to finish playing.
  while (playWav1.isPlaying())
  {
    // uncomment these lines if you audio shield
    // has the optional volume pot soldered
    //float vol = analogRead(15);
    //vol = vol / 1024;
    // sgtl5000_1.volume(vol);
  }
}

/*  ========== pushButton ============
  set pushbutton to pin 3
  create pushbutton object
  when the single button is pushed, select a
  random candle on MyCandleRack to animate
  the burntime of the candle is handled by the candlerack
*/
const byte buttonPin = 2;
Bounce debouncePushButton = Bounce();

void pushButton()
{
  if (debouncePushButton.update())
  {
    if (debouncePushButton.fell())
    {
      playFile("HAL.WAV"); // filenames are always uppercase 8.3 format
    }
  }
}

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

  //start the library, pass in the data details and the name of the Serial port. Can be Serial, Serial1, Serial2, etc. 
  ET.begin(details(mydata), &Serial1);

  // Audio connections require memory to work.  For more
  // detailed information, see the MemoryAndCpuUsage example
  AudioMemory(8);

  // Comment these out if not using the audio adaptor board.
  // This may wait forever if the SDA & SCL pins lack
  // pullup resistors
  sgtl5000_1.enable();
  sgtl5000_1.volume(0.5);

  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN)))
  {
    // stop here, but print a message repetitively
    while (1)
    {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
    pinMode(buttonPin, INPUT_PULLUP);
    debouncePushButton.attach(buttonPin);
    debouncePushButton.interval(50);
}



void loop()
{
  pushButton();

 //check and see if a data packet has come in. 
  if(ET.receiveData()){
    //this is how you access the variables. [name of the group].[variable name]
    //since we have data, we will blink it out. 
    
    //Serial.println(mydata.sound);

      switch (mydata.sound)
      {
        case 0:
        Serial.print("We're at case ");
        Serial.println(mydata.sound);

        playFile("HAL.WAV"); // filenames are always uppercase 8.3 format
        break;

        case 1:
        Serial.print("We're at case ");
        Serial.println(mydata.sound);
        break;

        case 2:
        Serial.print("We're at case ");
        Serial.println(mydata.sound);
        break;
      }

  }
  
  //you should make this delay shorter then your transmit delay or else messages could be lost
  //delay(250);
}
 
If you'd like to have multiple simultaneous wavs playing, you'll need multiple playSdWav objects. Then each time the button is pressed, you'll loop through all of your playSdWav objects, and use the first one that's idle to play the new wav file. You can determine idle or busy by calling isPlaying().
 
If you'd like to have multiple simultaneous wavs playing, you'll need multiple playSdWav objects. Then each time the button is pressed, you'll loop through all of your playSdWav objects, and use the first one that's idle to play the new wav file. You can determine idle or busy by calling isPlaying().

Thanks for the tip!

is this the most efficiant way to do this?

Code:
if (findFreePlayer)
  {
    if (!playWav1.isPlaying())
    {
      playWav1.play(filename);
      findFreePlayer = false;
    }
    if (!playWav2.isPlaying())
    {
      playWav2.play(filename);
      findFreePlayer = false;
    }
    if (!playWav3.isPlaying())
    {
      playWav3.play(filename);
      findFreePlayer = false;
    }
    if (!playWav4.isPlaying())
    {
      playWav4.play(filename);
      findFreePlayer = false;
    }
    if (!playWav5.isPlaying())
    {
      playWav5.play(filename);
      findFreePlayer = false;
    }
  }
 
Just use "else if".
This is efficient enough as it is and in no way timecritical.

Thanks! I'm a novice coder and have never used else if before!!!

Code:
if (!playWav1.isPlaying())
  {
    playWav1.play(filename);
  }
  else if (!playWav2.isPlaying())
  {
    playWav2.play(filename);
  }
  else if (!playWav3.isPlaying())
  {
    playWav3.play(filename);
  }
  else if (!playWav4.isPlaying())
  {
    playWav4.play(filename);
  }
  else (!playWav5.isPlaying())
  {
    playWav5.play(filename);
  }
 
You can do something like this if you want the logic to be a little more compact and easier to maintain.
Code:
#define NUM_PLAYERS 5
PlaySdWav *players[NUM_PLAYERS] = { &playWav1, &playWav2, &playWav3, &playWav4, &playWav5 };

void playFile(const char *filename) {
    for(int i=0; i<NUM_PLAYERS; i++) {
        if(!players[i]->isPlaying()) {
            players[i]->play(filename);
            // break out of loop once an idle player is found, otherwise will be played multiple times.
            break;
        }
    }
}
 
You can do something like this if you want the logic to be a little more compact and easier to maintain.
Code:
#define NUM_PLAYERS 5
PlaySdWav *players[NUM_PLAYERS] = { &playWav1, &playWav2, &playWav3, &playWav4, &playWav5 };

void playFile(const char *filename) {
    for(int i=0; i<NUM_PLAYERS; i++) {
        if(!players[i]->isPlaying()) {
            players[i]->play(filename);
            // break out of loop once an idle player is found, otherwise will be played multiple times.
            break;
        }
    }
}

That's amazing, thank you! I was wondering if there was a way to do it with a loop so I could easily add players if necessary, but there are some tricks in here that are new to me... using break; in a FOR loop, I have only ever used those in a switch. That is very handy to know as I was imagingin I would have to do that with a flag.

Pointers are also a world of unknowns to me!
 
Please keep in mind that such "optimizations" are not always useful, especially for beginners.
It can save a few bytes, that's all (if the compiler does not decide to unroll the loop). You have nothing to gain from saving a few bytes.
Just write in a way that is easy to read / understand for you. Keep it simple. This is worth more than any "optimization" (which makes no sense here).
Usually after 6 months or a year you don't remember what the goals were when writing parts of the code. It will then be MUCH easier if you can understand the code at first sight.

Just a tip.

Edit: wcalverts code is ok and good - but it always depends on the level of knowledge and the circumstances. And it may help you to expand your knowledge.
 
Last edited:
Please keep in mind that such "optimizations" are not always useful, especially for beginners.
It can save a few bytes, that's all (if the compiler does not decide to unroll the loop). You have nothing to gain from saving a few bytes.
Just write in a way that is easy to read / understand for you. Keep it simple. This is worth more than any "optimization" (which makes no sense here).
Usually after 6 months or a year you don't remember what the goals were when writing parts of the code. It will then be MUCH easier if you can understand the code at first sight.

Just a tip.

Edit: wcalverts code is ok and good - but it always depends on the level of knowledge and the circumstances. And it may help you to expand your knowledge.

Yes useful advice, I'm trying to get my head around it slowly but surely.

I am already finding that limitations to my understanding are causing issues...

I am getting 'playWav1 is undefined in his scope' errors unless I also define each player object like this:

Code:
AudioPlaySdWav playWav1;
AudioPlaySdWav playWav2;
AudioPlaySdWav playWav3;
AudioPlaySdWav playWav4;
AudioPlaySdWav playWav5;

AudioPlaySdWav *players[NUM_PLAYERS] = {&playWav1, &playWav2, &playWav3, &playWav4, &playWav5};

I had originally assumed that the array was defining the objects but is that not the case?
 
I had originally assumed that the array was defining the objects but is that not the case?

Right, it's not the case. The '&' means "address of". So for the following line of code:
Code:
AudioPlaySdWav *players[NUM_PLAYERS] = {&playWav1, &playWav2, &playWav3, &playWav4, &playWav5};

In English we might say something like "make an array of pointers, the first pointer will point to the address of playWav1, the second pointer will point to the address of playWav2, and so on". So we must separately define the things we need to point to.

In practice, I would use the GUI tool to design all of my playWav objects, and copy-paste the GUI tool code to the beginning of my project, with the array of pointers and other logic coming after the copy-pasted code.

One final note is that at some point, your SD card just won't be fast enough to play any more sounds concurrently, so adding more playWav objects won't help you.
 
Status
Not open for further replies.
Back
Top