Teensy 4.0 - based Audio Guestbook

Shawzy & Johnlago:
Noise problems can be very tricky to resolve. Without a detailed system wide schematic diagram of the system it is very difficult to diagnose from afar. When I tackled these types of problems at Hughes Aircraft 40 years ago we had at our disposal some very expensive test equipment(Many costing more than my house), and shield rooms in which to diagnose these types of problems.
There are some very smart and experienced people here on the forum, but without detailed information concerning these designs that you have created, as well as pictures of the wiring and all connections to the outside world including power inputs. Ground connections via the third prongs on power cords can also create weird ground loops that are difficult to find.
High current pulses flowing through flying wires can radiate all kinds of noises at various frequencies. The possibilities are almost endless.

Regards,
Ed
 
Some testing results

Hi folks

I've just circled back to this, having bought some cheap electret microphones off eBay then not got around to trying them. Here's the test "setup", if you can dignify it with that name:
IMG_0609.jpg
The buttons on the stripboard stand in for the hook switch and playback buttons. One microphone (9767) is connected, and the two other types are just above that. I got three types, 6050, 9767 and 4015. I think the numbers are to do with the sizes, and the documentation is pretty minimal... Playback is via the headphone socket.

I tried
  • source code from both the playfultechnology and the DD4WH repositories.
  • 128- and 256-byte audio block size (only the former for DD4WH's code)
  • various settings for sgtl5000_1.micGain(): +39dB seemed to be a fairly good option, +63dB (the maximum) gave an overloaded signal from these microphones
  • recording to both the audio adaptor's SD slot (SanDisk Extreme Plus 64Gb) and the Teensy's built-in one (Kingston Canvas Select Plus 32Gb)
  • with both data and charge-only USB cables connected to the PC
  • there was a DC offset in WAV files generated by DD4WH's code, due to the inclusion of sgtl5000_1.adcHighPassFilterDisable(); . This is supposed to reduce noise in some cases, but I saw little evidence of that, and although it does make the recorded waveforms look a bit weird I don't think it matters very much.
Essentially everything worked, and I didn't have noise problems despite using 20cm of unshielded cable to connect the microphone. The only issue was when I accidentally left 256-sample audio blocks switched on, which does not work with DD4WH's code; fair enough, it's not designed for it. I did fix the WAV header calculations in his code to make it easier to load the files into Goldwave, but it's almost certainly not vital to do this.

The conclusions I can reach are:
  • do first testing with a simple setup like this, where you can swap stuff in and out easily
  • based on a sample of 3, cheap eBay electrets stand a good chance of at least getting you going
  • it doesn't much matter which repository you download from, but if one doesn't work give the other a try, and either way do get the latest commit
 
Thanks a lot for this. I have tried a few different microphones, still a buzz. However, I’ll need to check if 256-sample audio blocks is switched on or not and match the mic level to your +39dB (not what I finally set mine too).

One thing that is notable is that your Audio Shield is placed on top of the Teensy Board, where as all of my Audio Shields are bellow so the usb connector will be closer the the mic terminals - it may have an impact? I also notice that you are using a Teensy 4.1 board? All mine are Teensy 4.

Thanks a lot for your input, I’ll make some mods an retest. 👍🏻
 
That's just my standard test setup, I was too lazy to switch to a Teensy 4.0. If I get a moment I may give that a try, which will also force me to put the audio adaptor below the Teensy as I don't have top sockets on a 4.0, just pins. There's no logical reason to expect a difference from a T4.1, but you never know until you try. It might just be worth posting an example WAV of your noise (I think you'd have to zip it, WAVs aren't acceptable attachments); someone might recognise where it's coming from.

I also realise at this point that I didn't test at 150MHz clock speed, but 600MHz - I should probably do that, as the lower speed has been recommended for power saving.

I also don't actually have a phone, so I can't test weird shielding and grounding issues ... but those are going to be different from build to build anyway.
 
I have tried changing from 150MHz to 600MHz, the buzz is still there but if I remember rightly the noise pitch is higher at 600MHz, which makes sense.
I’m kind of in the same boat for changing the mounting, I’ve snipped the redundant pins now 🙄🤣.

I can convert a sample file to MP3 if it’s more acceptable? The conversion is unlikely to strip out the noise.

I’ll definitely make the other checks first at some point tomorrow. Once again thanks for giving this your time.
 
I’d say a zipped WAV is better, because the exact information is preserved. There’s no knowing what an MP3 will throw away! Interesting to know about the effect of the clock speed change, if it’s 4x higher then it probably rules out a few causes.
 
OK, getting somewhere now. I tried the Teensy 4.0 and got audible noise - a sort of "motorboating" sound. Played around with CPU speed (no difference), swapped back to Teensy 4.1 not on the breadboard, same noise, put audio adaptor back on top, same noise. Took a really close look at the sound, the pulse interval is about 46ms. That's the SD card save interval. Change NBLOX from 16 to 8, sure enough the interval halves, and if you look really closely you can actually see the current change spikes for each sector write:
2023-05-01 10_24_59-NBLOX difference.jpg
The top file is NBLOX=8 so 4 sector writes; you can see one write, a delay, then the remaining 3. The bottom trace is with NBLOX set to 16 (as in the repo); similar to before, one write, delay, remaining 7 sectors.

Got to stop for the moment, but it looks as if improving the power supply to the SD card is key here. Better decoupling, or a properly starred current path from the supply perhaps. Need to look at the power routing on the audio adaptor.
 
Ah, this is getting quite interesting now. I have noticed the motor boarding effect as well but, it’s different again to the audio buzz that I’m experiencing.
I grabbed one of my phones today and noticed the mic level was actually really low (+15), I increased it to +39 and the buzz went away, I thought “BINGO!” Until I plugged it into a mains supply and the buzz returned 🙄. I have 4 different phones with exactly the same teensy setup but I only managed to resolve the problem today on the first one I played with, the others stayed the same no matter if it was on battery or PSU.

Power source is definitively playing a part here somewhere, and I don’t think the handset RJ11 connection to the phone helps me much either. The original Playful Technology setup uses a classic GPO hard wired to the Audio Board. I really don’t want to chop off the RJ11 if I can help it, I need to be able to easily replace the cable if someone breaks one, but it’s a breeding ground for additional noise unfortunately.
 
OK, getting somewhere now. I tried the Teensy 4.0 and got audible noise - a sort of "motorboating" sound. Played around with CPU speed (no difference), swapped back to Teensy 4.1 not on the breadboard, same noise, put audio adaptor back on top, same noise. Took a really close look at the sound, the pulse interval is about 46ms. That's the SD card save interval.

This is very, very interesting, and the same problem I've got with mine. Since the SD port is on the audio board itself, is there a chance that this is actually a manufacturing defect? As far as I can tell, there's nowhere for me to have introduced this specific interference myself, since the SD card slot is soldered right alongside the audio path. I'm tempted to pick up a second audio shield and swap it in, to see if this is some kind of issue particular to some copies of the board. (This could help explain why only a few of us have observed this SD write issue, too.)
 
This is very, very interesting, and the same problem I've got with mine. Since the SD port is on the audio board itself, is there a chance that this is actually a manufacturing defect? As far as I can tell, there's nowhere for me to have introduced this specific interference myself, since the SD card slot is soldered right alongside the audio path. I'm tempted to pick up a second audio shield and swap it in, to see if this is some kind of issue particular to some copies of the board. (This could help explain why only a few of us have observed this SD write issue, too.)
I'm thinking either manufacturing, or design... It could be, as this board has been in production in one form or another for quite a long time (2014?), that SD card behaviour has changed and board revisions haven't kept up. As far as I can tell SD cards are allowed to draw pulses of up to 200mA, and 100mA is not untypical. Looking at the decoupling, and the track layout, there really needs to be either thicker copper or wider tracks supplying the card, and the power should be "starred" from the pins. As it is, I think the power passes the SGTL5000 before it gets to the card, so current pulses can result in changes to its supply voltage due to track resistance. With +39dB gain in the mic amp, that gives an audible signal.

To verify this theory, I added some relatively fat wires to the adaptor:
IMG_0610.jpg
At first I just added the wire connecting the two ground pins and the mic ground. This was an improvement, but not a complete fix. So I (carefully!) scraped some solder resist from the SD card's power rails (just under "ud" and obliterating "Rev"), and starred those rails directly off the 3V and GND pins. Result? No "motorboat", though I do have a bit of 50Hz mains hum left, probably due to unscreened mic cable.

There are a few things people could do if they encounter this:
  • if using the playfultechnology code, change NBLOX from 16 to 8 - if the noise changes frequency, this is your issue, and if so...
  • ...either add wires as I've done (fiddly, but doable), or
  • ...try another SD card - maybe it takes less current during write, or
  • ...try the built-in SD card on a Teensy 4.1
Using DD4WH's code it's hard to change the SD write interval, so you'll just have to go for one of the above and see if it works.
 
I’ve somehow not seen this post. Thanks for your continued work on this project and helping keep it alive. I’ve already tried to linking GNDs to see if there’s an improvement to the buzzing (even though they are linked anyway) but no joy.

I think the mic input was designed originally for a mic capsule to be soldered directly to the board, and for very different setups for what we’re trying to use it for - thoughts? I’m thinking of trying the line in and using something like a Electret Microphone Amplifier like the MAX4466 instead? I know it might put a little more strain on battery power, but it may be cleaner?
 
As far as I could tell both GND pins aren't actually connected - I've asked if Gerbers of the most recent layout are available, but so far no response.

Note that what I've shown in post #311 is much more than linking grounds, it's providing an easy path to the SD card for the high-current pulses that happen during writes, bypassing the rather narrow and poorly routed tracks on the PCB. So far no-one has reported back on trying it out, though I live in hope.

Absolutely another approach, such as an extra amplifier and using the line-in pins, would have potential. I did toy with using a PDM mic and testing to see if the high-speed digital signal was likely to make it through a handset cable, but decided my interest in this project only stretched so far!
 
I did take onboard your comments regarding the power path to the SD card, I just didn’t mention it in my reply - it’s very much appreciated and I will give it a try and report back when I get chance.

Interesting the all GND’s aren’t linked, I thought they were? Certainly worth a revisit.

I think that since the flood of prebuilt Audio Guest Books have hit the market from China over that last few months it’s sadly had an impact on the interest in this project. However, I’ve spent quite a bit on building these units from these wonderful posts so I’m not prepared to let it go just yet.

Thank you (and others) for giving it the time you have!
 
I did take onboard your comments regarding the power path to the SD card, I just didn’t mention it in my reply - it’s very much appreciated and I will give it a try and report back when I get chance.

Interesting the all GND’s aren’t linked, I thought they were? Certainly worth a revisit.

I think that since the flood of prebuilt Audio Guest Books have hit the market from China over that last few months it’s sadly had an impact on the interest in this project. However, I’ve spent quite a bit on building these units from these wonderful posts so I’m not prepared to let it go just yet.

Thank you (and others) for giving it the time you have!

I believe the reason is the original Audio Shields were designed for Teensy 3.1 and 3.2 (later adding the 3.5, and 3.6). These microprocessors have 2 different ground pins. The first ground pin (left of pin #0) is the normal ground. The second ground pin between VIN and 3.3v is an analog ground. The audio stuff uses the analog ground for its processing, and the SPI stuff (SD card and soldered flash memory) use the normal ground.

The microprocessor used in the Teensy 4.0 and 4.1 does not have a separate analog ground, and instead both pins are connected to the normal ground plane.

When Paul designed the revision D audio shield for the Teensy 4.0 and 4.1, he re-used the design of revisions A-C, and kept the SPI stuff using the ground pin next to pin #0 and the audio stuff using the ground pin between VIN and 3.3v. Normally people won't notice, since they would typically just connect all 28 pins, but if you don't, you need to connect the ground pin to do SPI stuff.
 
Last edited:
I've just revisited this, and I wasn't quite right. Here's an edit of the only Gerbers available at the moment:
Audio Rev C.jpg
Green is one ground, blue the other, and yellow the 3.3V supply. For this board, the between-5V-and-3V ground (blue) is only used for the volume control pot; all other ground (green) is from the next-to-pin-0 one. Caveat: I did this manually and may have missed stuff. Caveat 2: this may have been revised in later issues (though it looks pretty close to me).

You can see the mic ground routing is horrible, and the power / ground to the SD card routes first to the SGTL5000, then to the SD card, which is not optimal.
 
Hi everybody,
quick update from my side. I have sorted the audio issues now. I just bought a new capsule microphone which solved the problem. The build-in microphone was crap. In general all is working fine except following:
- if i interrupt the prompting by hanging on the headset it will continue the prompting and start the recording session after the beep. The recording session only will stopped if i pick up the headset and hang it on again. This will close the recording session.
Can anybody please advice, having my first event coming up in 2 weeks. Thanks for all your support so far.
BR
Stefan

Code:
/**
 * Audio Guestbook, Copyright (c) 2022 Playful Technology
 * 
 * Tested using a Teensy 4.0 with Teensy Audio Shield, although should work 
 * with minor modifications on other similar hardware
 * 
 * When handset is lifted, a pre-recorded greeting message is played, followed by a tone.
 * Then, recording starts, and continues until the handset is replaced.
 * Playback button allows all messages currently saved on SD card through earpiece 
 * 
 * follow the detailed instructions here:
 * https://github.com/DD4WH/audio-guest...main/README.md
 * 
 * 
 *   the sketch only works with the latest Teensyduino 1.57 version, so please update your Arduino IDE AND your Teensyduino to Arduino version 1.8.19 and the latest Teensyduino version 1.57
 *   download the following library, unzip it and put it into your local Arduino folder (on my computer, the local Arduino folder is: "C:/Users/DD4WH/Documents/Arduino/libraries/"): https://github.com/KurtE/MTP_Teensy
 *   compile with option: "Serial + MTP Disk (Experimental)"" and with option "CPU speed: 150MHz" (this can save about 70% of battery power)
 *
 * Modifications by Frank DD4WH, August 2022
 * - now uses a Teensy 4.1 with built-in SD card (faster via SDIO), if you want to use a Teensy 4.0, uncomment in the USER CONFIGURATION below
 * - Files are saved on SD card as 44.1kHz, 16-bit mono WAV audio files 
 * - if you plug in the telephones´ USB cable into your computer, the telephone is mounted as a drive and you can acess the recordings 
 * - if there is no "greeting.wav" message on the SD card, the telephone automatically plays an invitation to record this message and then you can record the greeting message 
 * - if you want to record the greeting message again, just delete it from the telephone and lift the handheld again to record the greeting message  
 * --> if your handheld contact switch opens on lifting, simply uncomment in the USER CONFIGURATION below, everything else is done by the software
 * 
 * GNU GPL v3.0 license
 * 
 */

#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <TimeLib.h>
#include <MTP_Teensy.h>  // this library has to be downloaded separately (https://github.com/KurtE/MTP_Teensy)
// unzip the downloaded file and its content into your local Arduino folder (on my computer, the local Arduino folder is: "C:/Users/DD4WH/Documents/Arduino/libraries/")

/***************************************************************************************************************************************************/
/**USER CONFIGURATION ******************************************************************************************************************************/
/**COMMENT / UNCOMMENT ACCORDING TO YOUR HARDWARE **************************************************************************************************/
/***************************************************************************************************************************************************/

// comment this out, if your handheld OPENS the contact on lift
// use a digital voltmeter to find out
#define HANDHELD_CLOSES_ON_LIFT

// comment this out, if you want to record your greeting message with an external recorder
// leave as-is if you want to have the telephone automatically switch to recording the greeting message, in case there is no "greeting.wav" on the SD card 
//#define AUTO_GREETING_MESSAGE

// comment out, if you use a Teensy 4.0 (and thus the SD card slot on the audio board)
// if you leave this as-is, you have to use the built-in SD card slot on the Teensy 4.1, NOT the SD card slot on the audio board 
//#define TEENSY_41

/***************************************************************************************************************************************************/
/**END OF USER CONFIGURATION ***********************************************************************************************************************/
/***************************************************************************************************************************************************/
/***************************************************************************************************************************************************/

// Define pins used by Teensy Audio Shield
#ifdef TEENSY_41
#define SDCARD_CS_PIN    BUILTIN_SDCARD
#else
#define SDCARD_CS_PIN    10
#endif
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14
// And those used for inputs
// You can choose the pins you use here:
#define HOOK_PIN 0
#define PLAYBACK_BUTTON_PIN 1   // SM: no replay
//#define HOOK_PIN 0              // this is the default
//#define PLAYBACK_BUTTON_PIN 1   // this is the default


// GLOBALS
// Audio initialisation code can be generated using the GUI interface at https://www.pjrc.com/teensy/gui/
// Inputs
AudioSynthWaveform          waveform1; // To create the "beep" sfx
AudioInputI2S               i2s2; // I2S input from microphone on audio shield
AudioPlaySdWav              playWav1; // Play 44.1kHz 16-bit PCM greeting WAV file
AudioRecordQueue            queue1; // Creating an audio buffer in memory before saving to SD
AudioMixer4                 mixer; // Allows merging several inputs to same output
AudioOutputI2S              i2s1; // I2S interface to Speaker/Line Out on Audio shield
AudioConnection patchCord1(waveform1, 0, mixer, 0); // wave to mixer 
AudioConnection patchCord3(playWav1, 0, mixer, 1); // wav file playback mixer
AudioConnection patchCord4(mixer, 0, i2s1, 0); // mixer output to speaker (L)
AudioConnection patchCord6(mixer, 0, i2s1, 1); // mixer output to speaker (R)
AudioConnection patchCord5(i2s2, 0, queue1, 0); // mic input to queue (L)
AudioControlSGTL5000      sgtl5000_1;

// Filename to save audio recording on SD card
char filename[15];
// The file object itself
File frec;

// Use long 40ms debounce time on both switches
Bounce buttonRecord = Bounce(HOOK_PIN, 0);
Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 1);

// Keep track of current state of the device
enum Mode {Initialising, Ready, Prompting, Recording, Playing, Recording_Greeting};
Mode mode = Mode::Initialising;

float beep_volume = 0.04f; // not too loud :-)

// variables for writing to WAV file
unsigned long ChunkSize = 0L;
unsigned long Subchunk1Size = 16;
unsigned int AudioFormat = 1;
unsigned int numChannels = 1;
unsigned long sampleRate = 44100;
unsigned int bitsPerSample = 16;
unsigned long byteRate = sampleRate*numChannels*(bitsPerSample/8);// samplerate x channels x (bitspersample / 8)
unsigned int blockAlign = numChannels*bitsPerSample/8;
unsigned long Subchunk2Size = 0L;
unsigned long recByteSaved = 0L;
unsigned long NumSamples = 0L;
byte byte1, byte2, byte3, byte4;


void setup() {

  Serial.begin(9600);
  while (!Serial && millis() < 5000) {
    // wait for serial port to connect.
  }
  Serial.println("Serial set up correctly");
  print_mode();
  // Configure the input pins
  pinMode(HOOK_PIN, INPUT_PULLUP);
  pinMode(PLAYBACK_BUTTON_PIN, INPUT_PULLUP);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  // Define which input on the audio shield to use (AUDIO_INPUT_LINEIN / AUDIO_INPUT_MIC)
  sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
  sgtl5000_1.adcHighPassFilterDisable(); //
  sgtl5000_1.volume(0.85);

  mixer.gain(0, 1.0f);
  mixer.gain(1, 1.0f);

  // Play a beep to indicate system is online
  waveform1.begin(beep_volume, 440, WAVEFORM_SINE);
  wait(1000);
  waveform1.amplitude(0);
  delay(1000);

  // Initialize the SD card
#ifndef TEENSY_41   // assumes that you are using the SD card slot of the AUDIO BOARD
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
#endif  
  if (!(SD.begin(SDCARD_CS_PIN))) 
  {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
    else Serial.println("SD card correctly initialized");

  // mandatory to begin the MTP session.
    MTP.begin();

  // Add SD Card
    MTP.addFilesystem(SD, "LONDON"); // choose a nice name for the SD card volume to appear in your file explorer
    Serial.println("Added SD card via MTP");
    
    // Value in dB
  sgtl5000_1.micGain(25); //SM: default war 15. Mit neuem Mikrophon auf 25 erhöht.
  // sgtl5000_1.micGain(10); // much lower gain is required for the AOM5024 electret capsule

  // Synchronise the Time object used in the program code with the RTC time provider.
  // See https://github.com/PaulStoffregen/Time
  setSyncProvider(getTeensy3Time);
  
  // Define a callback that will assign the correct datetime for any file system operations
  // (i.e. saving a new audio recording onto the SD card)
  FsDateTime::setCallback(dateTime);

  mode = Mode::Ready; print_mode();
}

void loop() { //1
  // First, read the buttons
  buttonRecord.update();
  buttonPlay.update();

  switch(mode)
  { //2
    case Mode::Ready:
      // Falling edge occurs when the handset is lifted --> 611 telephone
#if defined(HANDHELD_CLOSES_ON_LIFT)
      if (buttonRecord.fallingEdge()) 
#else
      if (buttonRecord.risingEdge()) 
#endif
      {
        Serial.println("Handset lifted");
        mode = Mode::Prompting; print_mode();
      } //3
      else if(buttonPlay.fallingEdge()) 
      { //4
        playAllRecordings();
        //playLastRecording();
      } //4
      break;

    case Mode::Prompting:
      // Wait a second for users to put the handset to their ear
      wait(1000);

#if defined(AUTO_GREETING_MESSAGE)

    if (!SD.exists("greeting.wav")) 
    { //5
      mode = Mode::Recording_Greeting;
      break;
    } //5
#endif
     
      // Play the greeting inviting them to record their message
      playWav1.play("greeting.wav");    
      // Wait until the  message has finished playing
//      while (playWav1.isPlaying()) {
      while (!playWav1.isStopped()) 
      { //6
        // Check whether the handset is replaced
        buttonRecord.update();
        buttonPlay.update();
        // Handset is replaced
#if defined(HANDHELD_CLOSES_ON_LIFT)
      if (buttonRecord.risingEdge()) 
#else
      if (buttonRecord.fallingEdge())
#endif
        {
          playWav1.stop();
          mode = Mode::Ready; print_mode();
          return;
        } //7
        if(buttonPlay.fallingEdge()) 
        { //8
          playWav1.stop();
          playAllRecordings();
          //playLastRecording();
          return;
        } //8
        
      }  // 
      
      // Debug message
      Serial.println("Starting Recording");
      // Play the tone sound effect
      waveform1.begin(beep_volume, 440, WAVEFORM_SINE);
      wait(1250);
      waveform1.amplitude(0);
      // Start the recording function
      startRecording();
      break;

    case Mode::Recording:
      // Handset is replaced
#if defined(HANDHELD_CLOSES_ON_LIFT)
      if (buttonRecord.risingEdge()) 
#else
      if (buttonRecord.fallingEdge())
#endif
      { //9  
      // Debug log
        Serial.println("Stopping Recording");
        // Stop recording
        stopRecording();
        // Play audio tone to confirm recording has ended
        end_Beep();
      } //9
      else 
      { //10
        continueRecording();
      } // 10
      break;

    case Mode::Playing: // to make compiler happy
      break;  

    case Mode::Initialising: // to make compiler happy
      break;  

    case Mode::Recording_Greeting: // to make compiler happy
      startRecordingGreeting();
      mode = Mode::Recording;
      break;  
      } // 2 end switch   
  MTP.loop();  //This is mandatory to be placed in the loop code.
} // 1 end loop

void startRecordingGreeting() {
    if (SD.exists("greeting.wav")) {
      return;
    }
    // play message "Please record Greeting message now !" 
    playWav1.play("invitation_greeting.wav");
    while (!playWav1.isStopped()) { // this works for playWav
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
#if defined(HANDHELD_CLOSES_ON_LIFT)
      if(buttonPlay.fallingEdge() || buttonRecord.risingEdge())  
#else
      if(buttonPlay.fallingEdge() || buttonRecord.fallingEdge())  
#endif
      { 
        playWav1.stop();
        mode = Mode::Ready; print_mode();
        return;
      }   
    }
    // play beep
    two_tone_Beep();
  frec = SD.open("greeting.wav", FILE_WRITE);
  Serial.println("Opened Greeting file !");
  if(frec) {
    Serial.print("Recording to greeting.wav");
    queue1.begin();
    mode = Mode::Recording; print_mode();
    recByteSaved = 0L;
  }
  else {
    Serial.println("Couldn't open file to record!");
  }
}

void startRecording() {
  // Find the first available file number
//  for (uint8_t i=0; i<9999; i++) { // BUGFIX uint8_t overflows if it reaches 255  
  for (uint16_t i=0; i<9999; i++) {   
    // Format the counter as a five-digit number with leading zeroes, followed by file extension
    snprintf(filename, 11, " %05d.wav", i);
    // Create if does not exist, do not open existing, write, sync after write
    if (!SD.exists(filename)) {
      break;
    }
  }
  frec = SD.open(filename, FILE_WRITE);
  Serial.println("Opened file !");
  if(frec) {
    Serial.print("Recording to ");
    Serial.println(filename);
    queue1.begin();
    mode = Mode::Recording; print_mode();
    recByteSaved = 0L;
  }
  else {
    Serial.println("Couldn't open file to record!");
  }
}

void continueRecording() {
  // Check if there is data in the queue
  if (queue1.available() >= 2) {
    byte buffer[512];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    memcpy(buffer, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    memcpy(buffer+256, queue1.readBuffer(), 256);
    queue1.freeBuffer();
    // Write all 512 bytes to the SD card
    frec.write(buffer, 512);
    recByteSaved += 512;
  }
}

void stopRecording() {
  // Stop adding any new data to the queue
  queue1.end();
  // Flush all existing remaining data from the queue
  while (queue1.available() > 0) {
    // Save to open file
    frec.write((byte*)queue1.readBuffer(), 256);
    queue1.freeBuffer();
    recByteSaved += 256;
  }
  writeOutHeader();
  // Close the file
  frec.close();
  Serial.println("Closed file");
  mode = Mode::Ready; print_mode();
}


void playAllRecordings() {
  // Recording files are saved in the root directory
  File dir = SD.open("/");
  
  while (true) {
    File entry =  dir.openNextFile();
    if (strstr(entry.name(), "greeting"))
    {
       entry =  dir.openNextFile();
    }
    if (!entry) {
      // no more files
      entry.close();
      end_Beep();
      break;
    }
    //int8_t len = strlen(entry.name()) - 4;
//    if (strstr(strlwr(entry.name() + (len - 4)), ".raw")) {
//    if (strstr(strlwr(entry.name() + (len - 4)), ".wav")) {
    // the lines above throw a warning, so I replace them with this (which is also easier to read):
    if (strstr(entry.name(), ".wav") || strstr(entry.name(), ".WAV")) {
      Serial.print("Now playing ");
      Serial.println(entry.name());
      // Play a short beep before each message
      waveform1.amplitude(beep_volume);
      wait(750);
      waveform1.amplitude(0);
      // Play the file
      playWav1.play(entry.name());
      mode = Mode::Playing; print_mode();
    }
    entry.close();

//    while (playWav1.isPlaying()) { // strangely enough, this works for playRaw, but it does not work properly for playWav
    while (!playWav1.isStopped()) { // this works for playWav
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
#if defined(HANDHELD_CLOSES_ON_LIFT)
      if(buttonPlay.fallingEdge() || buttonRecord.risingEdge())  
#else
      if(buttonPlay.fallingEdge() || buttonRecord.fallingEdge())  
#endif
      { 
        playWav1.stop();
        mode = Mode::Ready; print_mode();
        return;
      }   
    }
  }
  // All files have been played
  mode = Mode::Ready; print_mode();
}

void playLastRecording() { // 1
  // Find the first available file number
  uint16_t idx = 0; 
  for (uint16_t i=0; i<9999; i++) { // 2
    // Format the counter as a five-digit number with leading zeroes, followed by file extension
    snprintf(filename, 11, " %05d.wav", i);
    // check, if file with index i exists
    if (!SD.exists(filename)) { // 3
     idx = i - 1;
     break;
      } // 3
  } // 2
      // now play file with index idx == last recorded file
      snprintf(filename, 11, " %05d.wav", idx);
      Serial.println(filename);
      playWav1.play(filename);
      mode = Mode::Playing; print_mode();
      while (!playWav1.isStopped()) 
      { // 5 // this works for playWav
          buttonPlay.update();
          buttonRecord.update();
          // Button is pressed again
    #if defined(HANDHELD_CLOSES_ON_LIFT)
          if(buttonPlay.fallingEdge() || buttonRecord.risingEdge())  
    #else
          if(buttonPlay.fallingEdge() || buttonRecord.fallingEdge()) 
    #endif
          {
            playWav1.stop();
            mode = Mode::Ready; print_mode();
            return;
          }   //4
      } // 5 end while
      // file has been played
  mode = Mode::Ready; print_mode();  
  end_Beep();
} // 1 end playLastRecording


// Retrieve the current time from Teensy built-in RTC
time_t getTeensy3Time(){
  return Teensy3Clock.get();
}

// Callback to assign timestamps for file system operations
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {

  // Return date using FS_DATE macro to format fields.
  *date = FS_DATE(year(), month(), day());

  // Return time using FS_TIME macro to format fields.
  *time = FS_TIME(hour(), minute(), second());

  // Return low time bits in units of 10 ms.
  *ms10 = second() & 1 ? 100 : 0;
}

// Non-blocking delay, which pauses execution of main program logic,
// but while still listening for input 
void wait(unsigned int milliseconds) {
  elapsedMillis msec=0;

  while (msec <= milliseconds) {
    buttonRecord.update();
    buttonPlay.update();
    if (buttonRecord.fallingEdge()) Serial.println("Button (pin 0) Press");
    if (buttonPlay.fallingEdge()) Serial.println("Button (pin 1) Press");
    if (buttonRecord.risingEdge()) Serial.println("Button (pin 0) Release");
    if (buttonPlay.risingEdge()) Serial.println("Button (pin 1) Release");
  }
}


void writeOutHeader() { // update WAV header with final filesize/datasize

//  NumSamples = (recByteSaved*8)/bitsPerSample/numChannels;
//  Subchunk2Size = NumSamples*numChannels*bitsPerSample/8; // number of samples x number of channels x number of bytes per sample
  Subchunk2Size = recByteSaved;
  ChunkSize = Subchunk2Size + 36;
  frec.seek(0);
  frec.write("RIFF");
  byte1 = ChunkSize & 0xff;
  byte2 = (ChunkSize >> 8) & 0xff;
  byte3 = (ChunkSize >> 16) & 0xff;
  byte4 = (ChunkSize >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  frec.write("WAVE");
  frec.write("fmt ");
  byte1 = Subchunk1Size & 0xff;
  byte2 = (Subchunk1Size >> 8) & 0xff;
  byte3 = (Subchunk1Size >> 16) & 0xff;
  byte4 = (Subchunk1Size >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = AudioFormat & 0xff;
  byte2 = (AudioFormat >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  byte1 = numChannels & 0xff;
  byte2 = (numChannels >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  byte1 = sampleRate & 0xff;
  byte2 = (sampleRate >> 8) & 0xff;
  byte3 = (sampleRate >> 16) & 0xff;
  byte4 = (sampleRate >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = byteRate & 0xff;
  byte2 = (byteRate >> 8) & 0xff;
  byte3 = (byteRate >> 16) & 0xff;
  byte4 = (byteRate >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = blockAlign & 0xff;
  byte2 = (blockAlign >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  byte1 = bitsPerSample & 0xff;
  byte2 = (bitsPerSample >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  frec.write("data");
  byte1 = Subchunk2Size & 0xff;
  byte2 = (Subchunk2Size >> 8) & 0xff;
  byte3 = (Subchunk2Size >> 16) & 0xff;
  byte4 = (Subchunk2Size >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  frec.close();
  Serial.println("header written"); 
  Serial.print("Subchunk2: "); 
  Serial.println(Subchunk2Size); 
}

void end_Beep(void) {
        waveform1.frequency(523.25);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
        wait(250);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
        wait(250);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
        wait(250);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
}

void two_tone_Beep(void) {
        waveform1.frequency(523.25);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
        waveform1.frequency(375.0);
        wait(250);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
        waveform1.frequency(523.25);
        wait(250);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
        waveform1.frequency(375.0);
        wait(250);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
}

void print_mode(void) { // only for debugging
  Serial.print("Mode switched to: ");
  // Initialising, Ready, Prompting, Recording, Playing
  if(mode == Mode::Ready)           Serial.println(" Ready");
  else if(mode == Mode::Prompting)  Serial.println(" Prompting");
  else if(mode == Mode::Recording)  Serial.println(" Recording");
  else if(mode == Mode::Playing)    Serial.println(" Playing");
  else if(mode == Mode::Initialising)  Serial.println(" Initialising");
  else if(mode == Mode::Recording_Greeting)  Serial.println(" Recording Greeting");
  else Serial.println(" Undefined");
}
 
Looked at this again, finally realised what the issue is. It's this:
Code:
// Non-blocking delay, which pauses execution of main program logic,
// but while [COLOR="#FF0000"]still listening for input[/COLOR] 
void wait(unsigned int milliseconds) {
  elapsedMillis msec=0;

  while (msec <= milliseconds) {
    buttonRecord.update();
    buttonPlay.update();
    if (buttonRecord.fallingEdge()) Serial.println("Button (pin 0) Press");
    if (buttonPlay.fallingEdge()) Serial.println("Button (pin 1) Press");
    if (buttonRecord.risingEdge()) Serial.println("Button (pin 0) Release");
    if (buttonPlay.risingEdge()) Serial.println("Button (pin 1) Release");
  }
}
Yes indeed ... listening for input, and throwing it away unused! I'd been misled by all reports saying this happens while the greeting is playing, but it doesn't - it's if you hang up during the 1 second wait().

Try this minor code edit instead:
Code:
      // Play the greeting inviting them to record their message
      playWav1.play("greeting.wav");    
      // Wait until the  message has finished playing
//      while (playWav1.isPlaying()) {
      while (!playWav1.isStopped()) 
      { //6
        // Check whether the handset is replaced
        buttonRecord.update();
        buttonPlay.update();
        // Handset is replaced
#if defined(HANDHELD_CLOSES_ON_LIFT)
      if (buttonRecord.[COLOR="#FF0000"]read[/COLOR]()) 
#else
      if ([COLOR="#FF0000"]![/COLOR]buttonRecord.[COLOR="#FF0000"]read[/COLOR]())
#endif
        {
          playWav1.stop();
          mode = Mode::Ready; print_mode();
          return;
        } //7

Please note - I have NOT tested this!
 
What audio issues were you having? and which capsule model did you purchase as a replacement for which telephone?
 
right here is my code i cannot get the greetings file to work get long bleep b4 recording then get 4 bleeps when I have hung up I can get playback the last message but cant get the greeting message to work at all helppppppppppppppp please

the first bounce, audio, wire, spi, sd, timelib are all in red text the mtp teensy and play_sd_wav are in black text ??????


Code:
/**
 * Audio Guestbook, Copyright (c) 2022 Playful Technology
 * 
 * Tested using a Teensy 4.0 with Teensy Audio Shield, although should work 
 * with minor modifications on other similar hardware
 * 
 * When handset is lifted, a pre-recorded greeting message is played, followed by a tone.
 * Then, recording starts, and continues until the handset is replaced.
 * Playback button allows all messages currently saved on SD card through earpiece 
 * 
 * Files are saved on SD card as 44.1kHz, 16-bit, mono signed integer RAW audio format 
 * --> changed this to WAV recording, DD4WH 2022_07_31
 * --> added MTP support, which enables copying WAV files from the SD card via the USB connection, DD4WH 2022_08_01
 * 
 * 
 * Frank DD4WH, August 1st 2022 
 * for a DBP 611 telephone (closed contact when handheld is lifted) & with recording to WAV file
 * contact for switch button 0 is closed when handheld is lifted
 * 
 * GNU GPL v3.0 license
 * 
 */

#include <Bounce.h>      [COLOR="#FF0000"]in red text[/COLOR]
#include <Audio.h>         [COLOR="#FF0000"]in red tex[/COLOR]t
#include <Wire.h>          [COLOR="#FF0000"]in red text[/COLOR]
#include <SPI.h>           [COLOR="#FF0000"]in red text[/COLOR]
#include <SD.h>            [COLOR="#FF0000"]in red text [/COLOR]
#include <TimeLib.h>     [COLOR="#FF0000"]in red text[/COLOR]
#include <MTP_Teensy.h>    [COLOR="#000000"]in black text[/COLOR]
#include "play_sd_wav.h" // local copy with fixes  [COLOR="#000000"]in black text[/COLOR]

// DEFINES
// Define pins used by Teensy Audio Shield
#define SDCARD_CS_PIN    10
#define SDCARD_MOSI_PIN  7
#define SDCARD_SCK_PIN   14
// And those used for inputs
#define HOOK_PIN 0
#define PLAYBACK_BUTTON_PIN 1

#define noINSTRUMENT_SD_WRITE

// GLOBALS
// Audio initialisation code can be generated using the GUI interface at https://www.pjrc.com/teensy/gui/
// Inputs
AudioSynthWaveform          waveform1; // To create the "beep" sfx
AudioInputI2S               i2s2; // I2S input from microphone on audio shield
AudioPlaySdWav              playWav1; // Play 44.1kHz 16-bit PCM greeting WAV file
AudioRecordQueue            queue1; // Creating an audio buffer in memory before saving to SD
AudioMixer4                 mixer; // Allows merging several inputs to same output
AudioOutputI2S              i2s1; // I2S interface to Speaker/Line Out on Audio shield
AudioConnection patchCord1(waveform1, 0, mixer, 0); // wave to mixer 
AudioConnection patchCord3(playWav1, 0, mixer, 1); // wav file playback mixer
AudioConnection patchCord4(mixer, 0, i2s1, 0); // mixer output to speaker (L)
AudioConnection patchCord6(mixer, 0, i2s1, 1); // mixer output to speaker (R)
AudioConnection patchCord5(i2s2, 0, queue1, 0); // mic input to queue (L)
AudioControlSGTL5000      sgtl5000_1;

// Filename to save audio recording on SD card
char filename[15];
// The file object itself
File frec;

// Use long 40ms debounce time on both switches
Bounce buttonRecord = Bounce(HOOK_PIN, 40);
Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 40);

// Keep track of current state of the device
enum Mode {Initialising, Ready, Prompting, Recording, Playing};
Mode mode = Mode::Initialising;

float beep_volume = 0.04f; // not too loud :-)

uint32_t MTPcheckInterval; // default value of device check interval [ms]

// variables for writing to WAV file
unsigned long ChunkSize = 0L;
unsigned long Subchunk1Size = 16;
unsigned int AudioFormat = 1;
unsigned int numChannels = 1;
unsigned long sampleRate = 44100;
unsigned int bitsPerSample = 16;
unsigned long byteRate = sampleRate*numChannels*(bitsPerSample/8);// samplerate x channels x (bitspersample / 8)
unsigned int blockAlign = numChannels*bitsPerSample/8;
unsigned long Subchunk2Size = 0L;
unsigned long recByteSaved = 0L;
unsigned long NumSamples = 0L;
byte byte1, byte2, byte3, byte4;


void setup() {

  Serial.begin(9600);
  while (!Serial && millis() < 5000) {
    // wait for serial port to connect.
  }
  Serial.println("Serial set up correctly");
  Serial.printf("Audio block set to %d samples\n",AUDIO_BLOCK_SAMPLES);
  print_mode();
  // Configure the input pins
  pinMode(HOOK_PIN, INPUT_PULLUP);
  pinMode(PLAYBACK_BUTTON_PIN, INPUT_PULLUP);

  // Audio connections require memory, and the record queue
  // uses this memory to buffer incoming audio.
  AudioMemory(60);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  // Define which input on the audio shield to use (AUDIO_INPUT_LINEIN / AUDIO_INPUT_MIC)
  sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
  //sgtl5000_1.adcHighPassFilterDisable(); //
  sgtl5000_1.volume(0.95);

  mixer.gain(0, 1.0f);
  mixer.gain(1, 1.0f);

  // Play a beep to indicate system is online
  waveform1.begin(beep_volume, 440, WAVEFORM_SINE);
  wait(1000);
  waveform1.amplitude(0);
  delay(1000);

  // Initialize the SD card
  SPI.setMOSI(SDCARD_MOSI_PIN);
  SPI.setSCK(SDCARD_SCK_PIN);
  if (!(SD.begin(SDCARD_CS_PIN))) 
  {
    // stop here if no SD card, but print a message
    while (1) {
      Serial.println("Unable to access the SD card");
      delay(500);
    }
  }
    else Serial.println("SD card correctly initialized");


  // mandatory to begin the MTP session.
    MTP.begin();

  // Add SD Card
//    MTP.addFilesystem(SD, "SD Card");
    MTP.addFilesystem(SD, "Ian Audio guestbook"); // choose a nice name for the SD card volume to appear in your file explorer
    Serial.println("Added SD card via MTP");
    MTPcheckInterval = MTP.storage()->get_DeltaDeviceCheckTimeMS();
    
    // Value in dB
//  sgtl5000_1.micGain(80);
  sgtl5000_1.micGain(23); // much lower gain is required for the AOM5024 electret capsule

  // Synchronise the Time object used in the program code with the RTC time provider.
  // See https://github.com/PaulStoffregen/Time
  setSyncProvider(getTeensy3Time);
  
  // Define a callback that will assign the correct datetime for any file system operations
  // (i.e. saving a new audio recording onto the SD card)
  FsDateTime::setCallback(dateTime);

  mode = Mode::Ready; print_mode();
}

void loop() {
  // First, read the buttons
  buttonRecord.update();
  buttonPlay.update();

  switch(mode){
    case Mode::Ready:
      // Falling edge occurs when the handset is lifted --> 611 telephone
      if (buttonRecord.fallingEdge()) {
        Serial.println("Handset lifted");
        mode = Mode::Prompting; print_mode();
      }
      else if(buttonPlay.fallingEdge()) {
        //playAllRecordings();
        playLastRecording();
      }
      break;

    case Mode::Prompting:
      // Wait a second for users to put the handset to their ear
      wait(1000);
      // Play the greeting inviting them to record their message
      playWav1.play("greeting.wav");    
      // Wait until the  message has finished playing
//      while (playWav1.isPlaying()) {
      while (!playWav1.isStopped()) {
        // Check whether the handset is replaced
        buttonRecord.update();
        buttonPlay.update();
        // Handset is replaced
        if(buttonRecord.risingEdge()) {
          playWav1.stop();
          mode = Mode::Ready; print_mode();
          return;
        }
        if(buttonPlay.fallingEdge()) {
          playWav1.stop();
          //playAllRecordings();
          playLastRecording();
          return;
        }
        
      }
      // Debug message
      Serial.println("Starting Recording");
      // Play the tone sound effect
      waveform1.begin(beep_volume, 725, WAVEFORM_SINE);
      wait(1250);
      waveform1.amplitude(0);
      // Start the recording function
      startRecording();
      break;

    case Mode::Recording:
      // Handset is replaced
      if(buttonRecord.risingEdge()){
        // Debug log
        Serial.println("Stopping Recording");
        // Stop recording
        stopRecording();
        // Play audio tone to confirm recording has ended
        end_Beep();
      }
      else {
        continueRecording();
      }
      break;

    case Mode::Playing: // to make compiler happy
      break;  

    case Mode::Initialising: // to make compiler happy
      break;  
  }   
  
  MTP.loop();  // This is mandatory to be placed in the loop code.
}

void setMTPdeviceChecks(bool nable)
{
  if (nable)
  {
    MTP.storage()->set_DeltaDeviceCheckTimeMS(MTPcheckInterval);
    Serial.print("En");
  }
  else
  {
    MTP.storage()->set_DeltaDeviceCheckTimeMS((uint32_t) -1);
    Serial.print("Dis");
  }
  Serial.println("abled MTP storage device checks");
}
  

#if defined(INSTRUMENT_SD_WRITE)
static uint32_t worstSDwrite, printNext;
#endif // defined(INSTRUMENT_SD_WRITE)

void startRecording() {
  setMTPdeviceChecks(false); // disable MTP device checks while recording
#if defined(INSTRUMENT_SD_WRITE)
  worstSDwrite = 0;
  printNext = 0;
#endif // defined(INSTRUMENT_SD_WRITE)
  // Find the first available file number
//  for (uint8_t i=0; i<9999; i++) { // BUGFIX uint8_t overflows if it reaches 255  
  for (uint16_t i=0; i<9999; i++) {   
    // Format the counter as a five-digit number with leading zeroes, followed by file extension
    snprintf(filename, 11, " %05d.wav", i);
    // Create if does not exist, do not open existing, write, sync after write
    if (!SD.exists(filename)) {
      break;
    }
  }
  frec = SD.open(filename, FILE_WRITE);
  Serial.println("Opened file !");
  if(frec) {
    Serial.print("Recording to ");
    Serial.println(filename);
    queue1.begin();
    mode = Mode::Recording; print_mode();
    recByteSaved = 0L;
  }
  else {
    Serial.println("Couldn't open file to record!");
  }
}

void continueRecording() {
#if defined(INSTRUMENT_SD_WRITE)
  uint32_t started = micros();
#endif // defined(INSTRUMENT_SD_WRITE)
#define NBLOX 16  
  // Check if there is data in the queue
  if (queue1.available() >= NBLOX) {
    byte buffer[NBLOX*AUDIO_BLOCK_SAMPLES*sizeof(int16_t)];
    // Fetch 2 blocks from the audio library and copy
    // into a 512 byte buffer.  The Arduino SD library
    // is most efficient when full 512 byte sector size
    // writes are used.
    for (int i=0;i<NBLOX;i++)
    {
      memcpy(buffer+i*AUDIO_BLOCK_SAMPLES*sizeof(int16_t), queue1.readBuffer(), AUDIO_BLOCK_SAMPLES*sizeof(int16_t));
      queue1.freeBuffer();
    }
    // Write all 512 bytes to the SD card
    frec.write(buffer, sizeof buffer);
    recByteSaved += sizeof buffer;
  }
  
#if defined(INSTRUMENT_SD_WRITE)
  started = micros() - started;
  if (started > worstSDwrite)
    worstSDwrite = started;

  if (millis() >= printNext)
  {
    Serial.printf("Worst write took %luus\n",worstSDwrite);
    worstSDwrite = 0;
    printNext = millis()+250;
  }
#endif // defined(INSTRUMENT_SD_WRITE)
}

void stopRecording() {
  // Stop adding any new data to the queue
  queue1.end();
  // Flush all existing remaining data from the queue
  while (queue1.available() > 0) {
    // Save to open file
    frec.write((byte*)queue1.readBuffer(), AUDIO_BLOCK_SAMPLES*sizeof(int16_t));
    queue1.freeBuffer();
    recByteSaved += AUDIO_BLOCK_SAMPLES*sizeof(int16_t);
  }
  writeOutHeader();
  // Close the file
  frec.close();
  Serial.println("Closed file");
  mode = Mode::Ready; print_mode();
  setMTPdeviceChecks(true); // enable MTP device checks, recording is finished
}


void playAllRecordings() {
  // Recording files are saved in the root directory
  File dir = SD.open("/");
  
  while (true) {
    File entry =  dir.openNextFile();
    if (strstr(entry.name(), "greeting"))
    {
       entry =  dir.openNextFile();
    }
    if (!entry) {
      // no more files
      entry.close();
      end_Beep();
      break;
    }
    //int8_t len = strlen(entry.name()) - 4;
//    if (strstr(strlwr(entry.name() + (len - 4)), ".raw")) {
//    if (strstr(strlwr(entry.name() + (len - 4)), ".wav")) {
    // the lines above throw a warning, so I replace them with this (which is also easier to read):
    if (strstr(entry.name(), ".wav") || strstr(entry.name(), ".WAV")) {
      Serial.print("Now playing ");
      Serial.println(entry.name());
      // Play a short beep before each message
      waveform1.amplitude(beep_volume);
      wait(750);
      waveform1.amplitude(0);
      // Play the file
      playWav1.play(entry.name());
      mode = Mode::Playing; print_mode();
    }
    entry.close();

//    while (playWav1.isPlaying()) { // strangely enough, this works for playRaw, but it does not work properly for playWav
    while (!playWav1.isStopped()) { // this works for playWav
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
//      if(buttonPlay.risingEdge() || buttonRecord.risingEdge()) { // FIX
      if(buttonPlay.fallingEdge() || buttonRecord.risingEdge()) { 
        playWav1.stop();
        mode = Mode::Ready; print_mode();
        return;
      }   
    }
  }
  // All files have been played
  mode = Mode::Ready; print_mode();
}

void playLastRecording() {
  // Find the first available file number
  uint16_t idx = 0; 
  for (uint16_t i=0; i<9999; i++) {
    // Format the counter as a five-digit number with leading zeroes, followed by file extension
    snprintf(filename, 11, " %05d.wav", i);
    // check, if file with index i exists
    if (!SD.exists(filename)) {
     idx = i - 1;
     break;
      }
  }
      // now play file with index idx == last recorded file
      snprintf(filename, 11, " %05d.wav", idx);
      Serial.println(filename);
      playWav1.play(filename);
      mode = Mode::Playing; print_mode();
      while (!playWav1.isStopped()) { // this works for playWav
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
//      if(buttonPlay.risingEdge() || buttonRecord.risingEdge()) { // FIX
      if(buttonPlay.fallingEdge() || buttonRecord.risingEdge()) {
        playWav1.stop();
        mode = Mode::Ready; print_mode();
        return;
      }   
    }
      // file has been played
  mode = Mode::Ready; print_mode();  
  end_Beep();
}


// Retrieve the current time from Teensy built-in RTC
time_t getTeensy3Time(){
  return Teensy3Clock.get();
}

// Callback to assign timestamps for file system operations
void dateTime(uint16_t* date, uint16_t* time, uint8_t* ms10) {

  // Return date using FS_DATE macro to format fields.
  *date = FS_DATE(year(), month(), day());

  // Return time using FS_TIME macro to format fields.
  *time = FS_TIME(hour(), minute(), second());

  // Return low time bits in units of 10 ms.
  *ms10 = second() & 1 ? 100 : 0;
}

// Non-blocking delay, which pauses execution of main program logic,
// but while still listening for input 
void wait(unsigned int milliseconds) {
  elapsedMillis msec=0;

  while (msec <= milliseconds) {
    buttonRecord.update();
    buttonPlay.update();
    if (buttonRecord.fallingEdge()) Serial.println("Button (pin 0) Press");
    if (buttonPlay.fallingEdge()) Serial.println("Button (pin 1) Press");
    if (buttonRecord.risingEdge()) Serial.println("Button (pin 0) Release");
    if (buttonPlay.risingEdge()) Serial.println("Button (pin 1) Release");
  }
}


void writeOutHeader() { // update WAV header with final filesize/datasize

//  NumSamples = (recByteSaved*8)/bitsPerSample/numChannels;
//  Subchunk2Size = NumSamples*numChannels*bitsPerSample/8; // number of samples x number of channels x number of bytes per sample
  Subchunk2Size = recByteSaved - 42; // because we didn't make space for the header to start with! Lose 21 samples...
  ChunkSize = Subchunk2Size + 34; // was 36;
  frec.seek(0);
  frec.write("RIFF");
  byte1 = ChunkSize & 0xff;
  byte2 = (ChunkSize >> 8) & 0xff;
  byte3 = (ChunkSize >> 16) & 0xff;
  byte4 = (ChunkSize >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  frec.write("WAVE");
  frec.write("fmt ");
  byte1 = Subchunk1Size & 0xff;
  byte2 = (Subchunk1Size >> 8) & 0xff;
  byte3 = (Subchunk1Size >> 16) & 0xff;
  byte4 = (Subchunk1Size >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = AudioFormat & 0xff;
  byte2 = (AudioFormat >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  byte1 = numChannels & 0xff;
  byte2 = (numChannels >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  byte1 = sampleRate & 0xff;
  byte2 = (sampleRate >> 8) & 0xff;
  byte3 = (sampleRate >> 16) & 0xff;
  byte4 = (sampleRate >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = byteRate & 0xff;
  byte2 = (byteRate >> 8) & 0xff;
  byte3 = (byteRate >> 16) & 0xff;
  byte4 = (byteRate >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  byte1 = blockAlign & 0xff;
  byte2 = (blockAlign >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  byte1 = bitsPerSample & 0xff;
  byte2 = (bitsPerSample >> 8) & 0xff;
  frec.write(byte1);  frec.write(byte2); 
  frec.write("data");
  byte1 = Subchunk2Size & 0xff;
  byte2 = (Subchunk2Size >> 8) & 0xff;
  byte3 = (Subchunk2Size >> 16) & 0xff;
  byte4 = (Subchunk2Size >> 24) & 0xff;  
  frec.write(byte1);  frec.write(byte2);  frec.write(byte3);  frec.write(byte4);
  frec.close();
  Serial.println("header written"); 
  Serial.print("Subchunk2: "); 
  Serial.println(Subchunk2Size); 
}

void end_Beep(void) {
          waveform1.frequency(525.25);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
        wait(250);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
        wait(250);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
        wait(250);
        waveform1.amplitude(beep_volume);
        wait(250);
        waveform1.amplitude(0);
}

void print_mode(void) { // only for debugging
  Serial.print("Mode switched to: ");
  // Initialising, Ready, Prompting, Recording, Playing
  if(mode == Mode::Ready)           Serial.println(" Ready");
  else if(mode == Mode::Prompting)  Serial.println(" Prompting");
  else if(mode == Mode::Recording)  Serial.println(" Recording");
  else if(mode == Mode::Playing)    Serial.println(" Playing");
  else if(mode == Mode::Initialising)  Serial.println(" Initialising");
  else Serial.println(" Undefined");
}

play back is fine i think just getting the greeting mess on to play https://soundcloud.com/user-61715834/00019a/s-qnHbMcWvwV0?si=c70ad2425b2a4c2faa4d1a593212e29a&utm_source=clipboard&utm_medium=text&utm_campaign=social_sharing
 
Last edited:
Can you post your greeting.wav here? You’ll probably need to zip it as the forum doesn’t accept wav attachments. AFK ATM but will look at some point.

What software are you using to create the file? You could try recording your greeting as a normal message then rename it.
 
this is the greeting message in wav format and it on the sd card i can use any format to record it mp3, AAC, WMA as got audio converter

https://soundcloud.com/user-61715834/greeting/s-aYm01dwcmw3?si=893658217b174c2ebce86452632e96c6&utm_source=clipboard&utm_medium=text&utm_campaign=social_sharing

this is my play sd wave capp tab
Code:
[code]
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 *
 * Development of this audio library was funded by PJRC.COM, LLC by sales of
 * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
 * open source software by purchasing Teensy or other PJRC products.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include <Arduino.h>
#include "play_sd_wav.h"
#include "spi_interrupt.h"


#define STATE_DIRECT_8BIT_MONO		0  // playing mono at native sample rate
#define STATE_DIRECT_8BIT_STEREO	1  // playing stereo at native sample rate
#define STATE_DIRECT_16BIT_MONO		2  // playing mono at native sample rate
#define STATE_DIRECT_16BIT_STEREO	3  // playing stereo at native sample rate
#define STATE_CONVERT_8BIT_MONO		4  // playing mono, converting sample rate
#define STATE_CONVERT_8BIT_STEREO	5  // playing stereo, converting sample rate
#define STATE_CONVERT_16BIT_MONO	6  // playing mono, converting sample rate
#define STATE_CONVERT_16BIT_STEREO	7  // playing stereo, converting sample rate
#define STATE_PARSE1			8  // looking for 20 byte ID header
#define STATE_PARSE2			9  // looking for 16 byte format header
#define STATE_PARSE3			10 // looking for 8 byte data header
#define STATE_PARSE4			11 // ignoring unknown chunk after "fmt "
#define STATE_PARSE5			12 // ignoring unknown chunk before "fmt "
#define STATE_PAUSED			13
#define STATE_STOP			14

void AudioPlaySdWavX::begin(void)
{
	state = STATE_STOP;
	state_play = STATE_STOP;
	data_length = 0;
	if (block_left) {
		release(block_left);
		block_left = NULL;
	}
	if (block_right) {
		release(block_right);
		block_right = NULL;
	}
}


bool AudioPlaySdWavX::play(const char *filename)
{
	stop();
	bool irq = false;
	if (NVIC_IS_ENABLED(IRQ_SOFTWARE)) {
		NVIC_DISABLE_IRQ(IRQ_SOFTWARE);
		irq = true;
	}
#if defined(HAS_KINETIS_SDHC)
	if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStartUsingSPI();
#else
	AudioStartUsingSPI();
#endif
	wavfile = SD.open(filename);
	if (!wavfile) {
#if defined(HAS_KINETIS_SDHC)
		if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
#else
		AudioStopUsingSPI();
#endif
		if (irq) NVIC_ENABLE_IRQ(IRQ_SOFTWARE);
		return false;
	}
	buffer_length = 0;
	buffer_offset = 0;
	state_play = STATE_STOP;
	data_length = 20;
	header_offset = 0;
	state = STATE_PARSE1;
	if (irq) NVIC_ENABLE_IRQ(IRQ_SOFTWARE);
	return true;
}

void AudioPlaySdWavX::stop(void)
{
	bool irq = false;
	if (NVIC_IS_ENABLED(IRQ_SOFTWARE)) {
		NVIC_DISABLE_IRQ(IRQ_SOFTWARE);
		irq = true;
	}
	if (state != STATE_STOP) {
		audio_block_t *b1 = block_left;
		block_left = NULL;
		audio_block_t *b2 = block_right;
		block_right = NULL;
		state = STATE_STOP;
		if (b1) release(b1);
		if (b2) release(b2);
		wavfile.close();
#if defined(HAS_KINETIS_SDHC)
		if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
#else
		AudioStopUsingSPI();
#endif
	}
	if (irq) NVIC_ENABLE_IRQ(IRQ_SOFTWARE);
}

void AudioPlaySdWavX::togglePlayPause(void) {
	// take no action if wave header is not parsed OR
	// state is explicitly STATE_STOP
	if(state_play >= 8 || state == STATE_STOP) return;

	// toggle back and forth between state_play and STATE_PAUSED
	if(state == state_play) {
		state = STATE_PAUSED;
	}
	else if(state == STATE_PAUSED) {
		state = state_play;
	}
}

void AudioPlaySdWavX::update(void)
{
	int32_t n;

	// only update if we're playing and not paused
	if (state == STATE_STOP || state == STATE_PAUSED) return;

	// allocate the audio blocks to transmit
	block_left = allocate();
	if (block_left == NULL) return;
	if (state < 8 && (state & 1) == 1) {
		// if we're playing stereo, allocate another
		// block for the right channel output
		block_right = allocate();
		if (block_right == NULL) {
			release(block_left);
			return;
		}
	} else {
		// if we're playing mono or just parsing
		// the WAV file header, no right-side block
		block_right = NULL;
	}
	block_offset = 0;

	//Serial.println("update");

	// is there buffered data?
	n = buffer_length - buffer_offset;
	if (n > 0) {
		// we have buffered data
		if (consume(n)) return; // it was enough to transmit audio
	}

	// we only get to this point when buffer[] is empty
	if (state != STATE_STOP && wavfile.available()) {
		// we can read more data from the file...
		readagain:
		buffer_length = wavfile.read(buffer, sizeof buffer);
		if (buffer_length == 0) goto end;
		buffer_offset = 0;
		bool parsing = (state >= 8);
		bool txok = consume(buffer_length);
		if (txok) {
			if (state != STATE_STOP) return;
		} else {
			if (state != STATE_STOP) {
				if (parsing && state < 8) goto readagain;
				else goto cleanup;
			}
		}
	}
end:	// end of file reached or other reason to stop
	wavfile.close();
#if defined(HAS_KINETIS_SDHC)
	if (!(SIM_SCGC3 & SIM_SCGC3_SDHC)) AudioStopUsingSPI();
#else
	AudioStopUsingSPI();
#endif
	state_play = STATE_STOP;
	state = STATE_STOP;
cleanup:
	if (block_left) {
		if (block_offset > 0) {
			for (uint32_t i=block_offset; i < AUDIO_BLOCK_SAMPLES; i++) {
				block_left->data[i] = 0;
			}
			transmit(block_left, 0);
			if (state < 8 && (state & 1) == 0) {
				transmit(block_left, 1);
			}
		}
		release(block_left);
		block_left = NULL;
	}
	if (block_right) {
		if (block_offset > 0) {
			for (uint32_t i=block_offset; i < AUDIO_BLOCK_SAMPLES; i++) {
				block_right->data[i] = 0;
			}
			transmit(block_right, 1);
		}
		release(block_right);
		block_right = NULL;
	}
}


// https://ccrma.stanford.edu/courses/422/projects/WaveFormat/

// Consume already buffered data.  Returns true if audio transmitted.
bool AudioPlaySdWavX::consume(uint32_t size)
{
	uint32_t len;
	uint8_t lsb, msb;
	const uint8_t *p;

	p = buffer + buffer_offset;
start:
	if (size == 0) return false;
#if 0
	Serial.print("AudioPlaySdWavX consume, ");
	Serial.print("size = ");
	Serial.print(size);
	Serial.print(", buffer_offset = ");
	Serial.print(buffer_offset);
	Serial.print(", data_length = ");
	Serial.print(data_length);
	Serial.print(", space = ");
	Serial.print((AUDIO_BLOCK_SAMPLES - block_offset) * 2);
	Serial.print(", state = ");
	Serial.println(state);
#endif
	switch (state) {
	  // parse wav file header, is this really a .wav file?
	  case STATE_PARSE1:
		len = data_length;
		if (size < len) len = size;
		memcpy((uint8_t *)header + header_offset, p, len);
		header_offset += len;
		buffer_offset += len;
		data_length -= len;
		if (data_length > 0) return false;
		// parse the header...
		if (header[0] == 0x46464952 && header[2] == 0x45564157) {
			//Serial.println("is wav file");
			if (header[3] == 0x20746D66) {
				// "fmt " header
				if (header[4] < 16) {
					// WAV "fmt " info must be at least 16 bytes
					break;
				}
				if (header[4] > sizeof(header)) {
					// if such .wav files exist, increasing the
					// size of header[] should accomodate them...
					//Serial.println("WAVEFORMATEXTENSIBLE too long");
					break;
				}
				//Serial.println("header ok");
				header_offset = 0;
				state = STATE_PARSE2;
			} else {
				// first chuck is something other than "fmt "
				//Serial.print("skipping \"");
				//Serial.printf("\" (%08X), ", __builtin_bswap32(header[3]));
				//Serial.print(header[4]);
				//Serial.println(" bytes");
				header_offset = 12;
				state = STATE_PARSE5;
			}
			p += len;
			size -= len;
			data_length = header[4];
			goto start;
		}
		//Serial.println("unknown WAV header");
		break;

	  // check & extract key audio parameters
	  case STATE_PARSE2:
		len = data_length;
		if (size < len) len = size;
		memcpy((uint8_t *)header + header_offset, p, len);
		header_offset += len;
		buffer_offset += len;
		data_length -= len;
		if (data_length > 0) return false;
		if (parse_format()) {
			//Serial.println("audio format ok");
			p += len;
			size -= len;
			data_length = 8;
			header_offset = 0;
			state = STATE_PARSE3;
			goto start;
		}
		//Serial.println("unknown audio format");
		break;

	  // find the data chunk
	  case STATE_PARSE3: // 10
		len = data_length;
		if (size < len) len = size;
		memcpy((uint8_t *)header + header_offset, p, len);
		header_offset += len;
		buffer_offset += len;
		data_length -= len;
		if (data_length > 0) return false;
		//Serial.print("chunk id = ");
		//Serial.print(header[0], HEX);
		//Serial.print(", length = ");
		//Serial.println(header[1]);
		p += len;
		size -= len;
		data_length = header[1];
		if (header[0] == 0x61746164) {
			//Serial.print("wav: found data chunk, len=");
			//Serial.println(data_length);
			// TODO: verify offset in file is an even number
			// as required by WAV format.  abort if odd.  Code
			// below will depend upon this and fail if not even.
			leftover_bytes = 0;
			state = state_play;
			if (state & 1) {
				// if we're going to start stereo
				// better allocate another output block
				block_right = allocate();
				if (!block_right) return false;
			}
			total_length = data_length;
		} else {
			state = STATE_PARSE4;
		}
		goto start;

	  // ignore any extra unknown chunks (title & artist info)
	  case STATE_PARSE4: // 11
		if (size < data_length) {
			data_length -= size;
			buffer_offset += size;
			return false;
		}
		p += data_length;
		size -= data_length;
		buffer_offset += data_length;
		data_length = 8;
		header_offset = 0;
		state = STATE_PARSE3;
		//Serial.println("consumed unknown chunk");
		goto start;

	  // skip past "junk" data before "fmt " header
	  case STATE_PARSE5:
		len = data_length;
		if (size < len) len = size;
		buffer_offset += len;
		data_length -= len;
		if (data_length > 0) return false;
		p += len;
		size -= len;
		data_length = 8;
		state = STATE_PARSE1;
		goto start;

	  // playing mono at native sample rate
	  case STATE_DIRECT_8BIT_MONO:
		return false;

	  // playing stereo at native sample rate
	  case STATE_DIRECT_8BIT_STEREO:
		return false;

	  // playing mono at native sample rate
	  case STATE_DIRECT_16BIT_MONO:
		if (size > data_length) size = data_length;
		data_length -= size;
		while (1) {
			lsb = *p++;
			msb = *p++;
			size -= 2;
			block_left->data[block_offset++] = (msb << 8) | lsb;
			if (block_offset >= AUDIO_BLOCK_SAMPLES) {
				transmit(block_left, 0);
				transmit(block_left, 1);
				release(block_left);
				block_left = NULL;
				data_length += size;
				buffer_offset = p - buffer;
				if (block_right) release(block_right);
				if (data_length == 0) state = STATE_STOP;
				return true;
			}
			if (size == 0) {
				if (data_length == 0) break;
				return false;
			}
		}
		//Serial.println("end of file reached");
		// end of file reached
		if (block_offset > 0) {
			// TODO: fill remainder of last block with zero and transmit
		}
		state = STATE_STOP;
		return false;

	  // playing stereo at native sample rate
	  case STATE_DIRECT_16BIT_STEREO:
		if (size > data_length) size = data_length;
		data_length -= size;
		if (leftover_bytes) {
			block_left->data[block_offset] = header[0];
//PAH fix problem with left+right channels being swapped
			leftover_bytes = 0;
			goto right16;
		}
		while (1) {
			lsb = *p++;
			msb = *p++;
			size -= 2;
			if (size == 0) {
				if (data_length == 0) break;
				header[0] = (msb << 8) | lsb;
				leftover_bytes = 2;
				return false;
			}
			block_left->data[block_offset] = (msb << 8) | lsb;
			right16:
			lsb = *p++;
			msb = *p++;
			size -= 2;
			block_right->data[block_offset++] = (msb << 8) | lsb;
			if (block_offset >= AUDIO_BLOCK_SAMPLES) {
				transmit(block_left, 0);
				release(block_left);
				block_left = NULL;
				transmit(block_right, 1);
				release(block_right);
				block_right = NULL;
				data_length += size;
				buffer_offset = p - buffer;
				if (data_length == 0) state = STATE_STOP;
				return true;
			}
			if (size == 0) {
				if (data_length == 0) break;
				leftover_bytes = 0;
				return false;
			}
		}
		// end of file reached
		if (block_offset > 0) {
			// TODO: fill remainder of last block with zero and transmit
		}
		state = STATE_STOP;
		return false;

	  // playing mono, converting sample rate
	  case STATE_CONVERT_8BIT_MONO :
		return false;

	  // playing stereo, converting sample rate
	  case STATE_CONVERT_8BIT_STEREO:
		return false;

	  // playing mono, converting sample rate
	  case STATE_CONVERT_16BIT_MONO:
		return false;

	  // playing stereo, converting sample rate
	  case STATE_CONVERT_16BIT_STEREO:
		return false;

	  // ignore any extra data after playing
	  // or anything following any error
	  case STATE_STOP:
		return false;

	  // this is not supposed to happen!
	  //default:
		//Serial.println("AudioPlaySdWavX, unknown state");
	}
	state_play = STATE_STOP;
	state = STATE_STOP;
	return false;
}


/*
00000000  52494646 66EA6903 57415645 666D7420  RIFFf.i.WAVEfmt 
00000010  10000000 01000200 44AC0000 10B10200  ........D.......
00000020  04001000 4C495354 3A000000 494E464F  ....LIST:...INFO
00000030  494E414D 14000000 49205761 6E742054  INAM....I Want T
00000040  6F20436F 6D65204F 76657200 49415254  o Come Over.IART
00000050  12000000 4D656C69 73736120 45746865  ....Melissa Ethe
00000060  72696467 65006461 746100EA 69030100  ridge.data..i...
00000070  FEFF0300 FCFF0400 FDFF0200 0000FEFF  ................
00000080  0300FDFF 0200FFFF 00000100 FEFF0300  ................
00000090  FDFF0300 FDFF0200 FFFF0100 0000FFFF  ................
*/





// SD library on Teensy3 at 96 MHz
//  256 byte chunks, speed is 443272 bytes/sec
//  512 byte chunks, speed is 468023 bytes/sec

#define B2M_44100 (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT) // 97352592
#define B2M_22050 (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT * 2.0)
#define B2M_11025 (uint32_t)((double)4294967296000.0 / AUDIO_SAMPLE_RATE_EXACT * 4.0)

bool AudioPlaySdWavX::parse_format(void)
{
	uint8_t num = 0;
	uint16_t format;
	uint16_t channels;
	uint32_t rate, b2m;
	uint16_t bits;

	format = header[0];
	//Serial.print("  format = ");
	//Serial.println(format);
	if (format != 1) return false;

	rate = header[1];
	//Serial.print("  rate = ");
	//Serial.println(rate);
	if (rate == 44100) {
		b2m = B2M_44100;
	} else if (rate == 22050) {
		b2m = B2M_22050;
		num |= 4;
	} else if (rate == 11025) {
		b2m = B2M_11025;
		num |= 4;
	} else {
		return false;
	}

	channels = header[0] >> 16;
	//Serial.print("  channels = ");
	//Serial.println(channels);
	if (channels == 1) {
	} else if (channels == 2) {
		b2m >>= 1;
		num |= 1;
	} else {
		return false;
	}

	bits = header[3] >> 16;
	//Serial.print("  bits = ");
	//Serial.println(bits);
	if (bits == 8) {
	} else if (bits == 16) {
		b2m >>= 1;
		num |= 2;
	} else {
		return false;
	}

	bytes2millis = b2m;
	//Serial.print("  bytes2millis = ");
	//Serial.println(b2m);

	// we're not checking the byte rate and block align fields
	// if they're not the expected values, all we could do is
	// return false.  Do any real wav files have unexpected
	// values in these other fields?
	state_play = num;
	return true;
}


bool AudioPlaySdWavX::isPlaying(void)
{
	uint8_t s = *(volatile uint8_t *)&state;
	return (s < 8);
}


bool AudioPlaySdWavX::isPaused(void)
{
	uint8_t s = *(volatile uint8_t *)&state;
	return (s == STATE_PAUSED);
}


bool AudioPlaySdWavX::isStopped(void)
{
	uint8_t s = *(volatile uint8_t *)&state;
	return (s == STATE_STOP);
}


uint32_t AudioPlaySdWavX::positionMillis(void)
{
	uint8_t s = *(volatile uint8_t *)&state;
	if (s >= 8 && s != STATE_PAUSED) return 0;
	uint32_t tlength = *(volatile uint32_t *)&total_length;
	uint32_t dlength = *(volatile uint32_t *)&data_length;
	uint32_t offset = tlength - dlength;
	uint32_t b2m = *(volatile uint32_t *)&bytes2millis;
	return ((uint64_t)offset * b2m) >> 32;
}


uint32_t AudioPlaySdWavX::lengthMillis(void)
{
	uint8_t s = *(volatile uint8_t *)&state;
	if (s >= 8 && s != STATE_PAUSED) return 0;
	uint32_t tlength = *(volatile uint32_t *)&total_length;
	uint32_t b2m = *(volatile uint32_t *)&bytes2millis;
	return ((uint64_t)tlength * b2m) >> 32;
}
[/CODE]

this is my play sd wav h tab

Code:
[code]
/* Audio Library for Teensy 3.X
 * Copyright (c) 2014, Paul Stoffregen, paul@pjrc.com
 *
 * Development of this audio library was funded by PJRC.COM, LLC by sales of
 * Teensy and Audio Adaptor boards.  Please support PJRC's efforts to develop
 * open source software by purchasing Teensy or other PJRC products.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice, development funding notice, and this permission
 * notice shall be included in all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#ifndef play_sd_wavx_h_
#define play_sd_wavx_h_

#include "Arduino.h"
#include "AudioStream.h"
#include "SD.h"

class AudioPlaySdWavX : public AudioStream
{
public:
	AudioPlaySdWavX(void) : AudioStream(0, NULL), block_left(NULL), block_right(NULL) { begin(); }
	void begin(void);
	bool play(const char *filename);
	void togglePlayPause(void);
	void stop(void);
	bool isPlaying(void);
	bool isPaused(void);
	bool isStopped(void);
	uint32_t positionMillis(void);
	uint32_t lengthMillis(void);
	virtual void update(void);
private:
	File wavfile;
	bool consume(uint32_t size);
	bool parse_format(void);
	uint32_t header[10];		// temporary storage of wav header data
	uint32_t data_length;		// number of bytes remaining in current section
	uint32_t total_length;		// number of audio data bytes in file
	uint32_t bytes2millis;
	audio_block_t *block_left;
	audio_block_t *block_right;
	uint16_t block_offset;		// how much data is in block_left & block_right
	uint8_t buffer[AUDIO_BLOCK_SAMPLES*sizeof(int16_t)*2];		// buffer two blocks of data
	uint16_t buffer_offset;		// where we're at consuming "buffer"
	uint16_t buffer_length;		// how much data is in "buffer" 
	uint8_t header_offset;		// number of bytes in header[]
	uint8_t state;
	uint8_t state_play;
	uint8_t leftover_bytes;
};

#endif
[/CODE]
 
Back
Top