Teensy 4.0 - based Audio Guestbook

Hi Ujjal, I have a similar problem. If I count from 1-20, I only record every other number, so the recording is not continuous. Can I ask if you were able to fix the issue and how did you fix it?

I'm having the same issue. I'm currently using a 128GB SanDisk High Endurance SDXC U3 v30 but read in a previous post in this thread that a better SD card could help alleviate this problem.

Does anyone have any recommendations on the type of card I should be looking at? UHS-I vs UHS-II? A1 vs A2? SDXC vs SDHC? etc.
 
IIRC: Paul has noted that the Higher A# cards are better as that perhaps represents the type of access.

Found this: mymemory.co.uk/blog/a1-vs-a2-sandisk-microsd-card-whats-the-difference/#difference-between-a1-and-a2

Code:
A1:  “The Application Performance Class 1 (A1) was defined by SD Physical 5.1 specification. Not only for storing maps, pictures, videos, music, dictionary and documents, it also enables the user to be freed from sluggish for editing and updating data.”

A2: “The Application Performance Class 2 (A2) is defined by SD Physical 6.0 specification. It makes SD memory card much higher performance than the A1 performance by using functions of Command Queuing and Cache.”

And this summary - assuming the SDFat code offers the needed support to be faster?:
Code:
The Class A1 SD card requires a minimum of 1500 IOPS reading and 500 IOPS writing speeds, while A2 requires 4000 and 2000 IOPS. To obtain higher speeds, the A2 SD card requires a host driver since it utilizes command queuing and writes caching. If it’s used in an unsupported host driver, the A2 SD card might be slower than the A1 card.
 
Unfortunately, for microcontrollers, the "Class" and other labels can't be used as criterium wether a card is fast or not. They are all optimized for large buffers (Megabytes-size) on PCs or similar. There are several threads here, about this. Best is to do your own benchmarks with small buffers (kb-size) or to just read the forum...
I had some "A2" that where incredible slow on Teensy (i.e "Sandisk" with "A2" labels, and I sent them back).
 
What write speed is required?

Unfortunately, for microcontrollers, the "Class" and other labels can't be used as criterium wether a card is fast or not. They are all optimized for large buffers (Megabytes-size) on PCs or similar. There are several threads here, about this. Best is to do your own benchmarks with small buffers (kb-size) or to just read the forum...
I had some "A2" that where incredible slow on Teensy (i.e "Sandisk" with "A2" labels, and I sent them back).

Thanks for the feedback. I've read through Bill Greiman's thread regarding "Which SD is Best for Audio Projects?". I ultimately purchased the three cards that were recommended: PNY Elite-X 64GB, Canvas GO! Plus 64GB, and Lexar 1066x 64GB. I've benchmarked them all and neither of them solved the buffering issue.

SEQ1M (Q8T1) : Read - 297.00 || Write - 248.54
SEQ1M (Q1T1) : Read - 293.15 || Write - 242.21
RND4K (Q32T1) : Read - 12.10 || Write - 3.69
RND4K (Q1T1) : Read - 11.16 || Write - 3.45

What write speed should I be looking for?
 
Thanks for the feedback. I've read through Bill Greiman's thread regarding "Which SD is Best for Audio Projects?". I ultimately purchased the three cards that were recommended: PNY Elite-X 64GB, Canvas GO! Plus 64GB, and Lexar 1066x 64GB. I've benchmarked them all and neither of them solved the buffering issue.

SEQ1M (Q8T1) : Read - 297.00 || Write - 248.54
SEQ1M (Q1T1) : Read - 293.15 || Write - 242.21
RND4K (Q32T1) : Read - 12.10 || Write - 3.69
RND4K (Q1T1) : Read - 11.16 || Write - 3.45

What write speed should I be looking for?

I finally got this project working but I'm now also in the same situation. Everything is working as expected except the recording is "choppy". I'm using Sandisk 32gb A1 UHS-1 Class 10.
 
Hi, it's me again, Ted. Thanks to all that have moved this forward in my absence, especially Frank; I will try Frank's revised code soon.

As many have found, most issues are with wiring.
I did research for the best SD card to use and got this 32 Gig Sandisk Ultra from Amazon https://www.amazon.com/gp/product/B010NE3QHQ/ref=ppx_yo_dt_b_search_asin_title?ie=UTF8&psc=1
I also strongly recommend using the official SD card formatter from here https://www.sdcard.org/downloads/formatter/ to format your cards and
I also recommend reformatting the card when you want to start a new recording session - I find odd results when I used my Win 11 PC to delete some files on the card expecting that the Teensy file handler would just use the deleted space.
 
I’ve been taking a look at the “choppy recording” issue, and it seems to be due to the SD card writes turning interrupts off, so any write that takes over 6ms (2 audio update cycles) loses data. I’m not sure why or where the interrupts get disabled, the SD code is a huge labyrinth and I don’t currently have a very big ball of string…

I’ll take another look at some point, but would be more than happy if someone beat me to the solution!

Full disclosure, I’ve actually been testing on a Teensy 4.1, but using the audio adaptor’s SD card slot so everything should be comparable with the documented T4.0 system.
 
So did a few upgrades to the code to be able to use it with a more modern phone (https://a.co/d/8yH8w7j) and to have it have an LED status light when recording (on pin 2 for T4). Also now have a time recording limit.

Issue that I'm still having is that the phone doesn't always seem to catch pickups or hangups. It also doesn't seem to like my externally recorded greeting.wav Here's the code for anyone wanting to update or play with it, thanks to @dd4wh for the work on it from the getgo!

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-guestbook/blob/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 40
//#define PLAYBACK_BUTTON_PIN 41
#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, 100);
Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 40);

// 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.4f; // 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;
// PiProjects variables 
int recordTimeStamp;
int recordingTimeLimit = 60000; // This is the recording time limit in milliseconds 
int LED_PIN = 2;

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);
  pinMode(LED_PIN, OUTPUT);
  
  // 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(1.0);

  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(500);
  waveform1.amplitude(0);
  delay(500);

  // 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(5000);
    }
  }
    else Serial.println("SD card correctly initialized");

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

  // Add SD Card
    MTP.addFilesystem(SD, "My Audio guestbook"); // 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(15);
  sgtl5000_1.micGain(30); // 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();
      digitalWrite(LED_PIN, HIGH);
      recordTimeStamp = millis(); // For recording time limit
      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();
        digitalWrite(LED_PIN, LOW);
      } //9
      else 
      { //10
        if(recordTimeStamp + recordingTimeLimit < millis()){
          stopRecording();
           // Play audio tone to confirm recording has ended
          end_Beep();
          digitalWrite(LED_PIN, LOW);
        }
        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");
}

I love the idea of having a LED status light for recording. Would you be willing to highlight the coding that I would need to add to the original code to add this feature? I am still learning coding and don't want to completely ruin the phone trying to add it. Much appreciated!
 
Im having trouble getting my wav file to play.
Right now its just going straight into the "beep" and will start recording, which all works great.
Ive tried changing the code and file format to raw, mp3, and mp4 with same results.

Ive even put in a serial.println for playing the greeting and it says playing and immediately changes to recording.

Does anyone have any other options?

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>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <TimeLib.h>
//#include <MTP_Teensy.h>

// 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

// 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              playRaw1; // 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(playRaw1, 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 :-)

// 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 = 48000;
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(65);

  // 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(.70);

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

  // Play a beep to indicate system is online
  waveform1.begin(beep_volume, 600, 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, "Kais Audio guestbook"); // 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(15);
  sgtl5000_1.micGain(30); // 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
      playRaw1.play("greeting.raw");   
      // Wait until the  message has finished playing
//      while (playRaw1.isPlaying()) {
      while (!playRaw1.isStopped()) {
        // Check whether the handset is replaced
        buttonRecord.update();
        buttonPlay.update();
        // Handset is replaced
        if(buttonRecord.risingEdge()) {
          playRaw1.stop();
          mode = Mode::Ready; print_mode();
          return;
        }
        if(buttonPlay.risingEdge()) {
          playRaw1.stop();
          //playAllRecordings();
          playLastRecording();
          return;
        }
        
      }
      // 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(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 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
      playRaw1.play(entry.name());
      mode = Mode::Playing; print_mode();
    }
    entry.close();

//    while (play.isPlaying()) { // strangely enough, this works for playRaw, but it does not work properly for playWav
    while (!playRaw1.isStopped()) { // this works for playWav
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
//      if(buttonPlay.risingEdge() || buttonRecord.risingEdge()) { // FIX
      if(buttonPlay.fallingEdge() || buttonRecord.risingEdge()) { 
        playRaw1.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);
      playRaw1.play(filename);
      mode = Mode::Playing; print_mode();
      while (!playRaw1.isStopped()) { // this works for playWav
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
//      if(buttonPlay.risingEdge() || buttonRecord.risingEdge()) { // FIX
      if(buttonPlay.fallingEdge() || buttonRecord.risingEdge()) {
        playRaw1.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;
  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 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");
}
 
Make sure your wav file is 16bit 44.1khz.

Im extracting with audacity, and they have all been 16bit, 44.1 hz.

Ive even tried recording on the phone and changing the name to "greeting" of that recording.

SD card is being initialized and recognized, but my greeting file isnt. Ive even erased and reprogrammed the SD card a few times to be sure.
 
Im extracting with audacity, and they have all been 16bit, 44.1 hz.

Ive even tried recording on the phone and changing the name to "greeting" of that recording.

SD card is being initialized and recognized, but my greeting file isnt. Ive even erased and reprogrammed the SD card a few times to be sure.
You might want to double-check the filename:
Code:
 playRaw1.play("greeting.[COLOR="#FF0000"]raw[/COLOR]");
 
I finally got this project working but I'm now also in the same situation. Everything is working as expected except the recording is "choppy". I'm using Sandisk 32gb A1 UHS-1 Class 10.
If you feel like diving in a bit, could you give https://github.com/h4yn0nnym0u5e/audio-guestbook/tree/bugfix/better-recording a try? Using it does mean editing platform.txt and boards.local.txt in your Arduino install folder, I'm afraid, but it does mean you can increase the audio block size on a per-project basis, which allows SD writes to take up to 11.6ms, rather than the measly 5.8ms with the standard block size.

This fix is a minimal and very specific set of changes to the current state of the "original" playfultechnology repo, rather than @DD4WH's massive set of updates, which are likely to be most excellent but have remained as a pull request for over 2 months now... if my stuff works OK for a few people then we can see who merges which changes!
 
If you feel like diving in a bit, could you give https://github.com/h4yn0nnym0u5e/audio-guestbook/tree/bugfix/better-recording a try? Using it does mean editing platform.txt and boards.local.txt in your Arduino install folder, I'm afraid, but it does mean you can increase the audio block size on a per-project basis, which allows SD writes to take up to 11.6ms, rather than the measly 5.8ms with the standard block size.

This fix is a minimal and very specific set of changes to the current state of the "original" playfultechnology repo, rather than @DD4WH's massive set of updates, which are likely to be most excellent but have remained as a pull request for over 2 months now... if my stuff works OK for a few people then we can see who merges which changes!


I got the following error message.


Arduino: 1.8.19 (Windows 10), TD: 1.57, Board: "Teensy 4.1, Serial + MTP Disk (Experimental), 150 MHz, Faster, US English, Bigger blocks (256 samples)"

D:\Documents\Arduino\audio_guestbook\audio_guestbookv3.ino\audio_guestbookv3.ino.ino: In function 'void continueRecording()':

D:\Documents\Arduino\audio_guestbook\audio_guestbookv3.ino\audio_guestbookv3.ino.ino:291:58: error: conflicting declaration 'byte buffer [8192]'

byte buffer[NBLOX*AUDIO_BLOCK_SAMPLES*sizeof(int16_t)];

^

D:\Documents\Arduino\audio_guestbook\audio_guestbookv3.ino\audio_guestbookv3.ino.ino:290:10: note: previous declaration as 'byte buffer [512]'

byte buffer[512];

^

Error compiling for board Teensy 4.1.
 
Last edited:
I removed line #263 from your ino file and and successfully uploaded it. I got the message "Audio block set to 256 samples" too. Checked my recording and still choppy. Almost the same as my previous recording.
 
It was not clear on your instructions but I also replaced my local play_sd_wav.cpp and play_sd_wav.h with the modified versions. Can't say it improved though. Maybe a little bit? But definitely the recordings are still far from usable.
 
Hmm. Thanks for testing - I've fixed the bogus line 263 (apologies for that...) and pushed another update, and re-tested at 150MHz (which I'd forgotten to do), and all is OK here. I'm using a Kingston Canvas Select Plus 32Gb A1 card, so should be pretty comparable to your SanDisk.

You shouldn't need to replace the play_sd_wav.cpp and .h in your Audio library - you should see the modified ones appear as part of your project when you open it in the Arduino IDE, as project-specific files. They define a new object AudioPlaySdWavX which my sketch uses at line 47 in place of the original AudioPlaySdWav (which doesn't play nicely with the 256-sample audio blocks).
2022-10-28 21_48_54-audio-guestbook _ Arduino 1.8.19.png
I see you said in your post #155 you "finally" got it working - did you have to make any changes to my code for this test, apart from removing line 263? Might be worth posting your code if so.

I'll play a bit with instrumenting the SD writes, to see if that can tell us anything, though I know some SD cards are just hopeless - I have one which occasionally takes over a second to do a write! Unlikely with a SanDisk, I'd think.
 
Initially I'm getting error because of the AudioPlaySdWavX. I figured that that object was defined in those two files. After editing, I successfuly uploaded it with AudioPlaySdWavX.

The only changes I made to your ino file is I added the lines for the teensy 4.1 that came from DD4WH's version.

Here is the code I used.

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>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <TimeLib.h>
#include <MTP_Teensy.h>
#include "play_sd_wav.h" // local copy with fixes

/***************************************************************************************************************************************************/
/**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 ***********************************************************************************************************************/
/***************************************************************************************************************************************************/
/***************************************************************************************************************************************************/


// DEFINES
// 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
#define HOOK_PIN 0
#define PLAYBACK_BUTTON_PIN 1

// GLOBALS
// Audio initialisation code can be generated using the GUI interface at [url]https://www.pjrc.com/teensy/gui/[/url]
// Inputs
AudioSynthWaveform          waveform1; // To create the "beep" sfx
AudioInputI2S               i2s2; // I2S input from microphone on audio shield
AudioPlaySdWavX              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 :-)

// 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.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
  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, "Kais Audio guestbook"); // 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(15);
  sgtl5000_1.micGain(8); // 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 [url]https://github.com/PaulStoffregen/Time[/url]
  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, 440, 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 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() {
#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;
  }
}

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();
}


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(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 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");
}
 
Curiouser and curiouser!

I've instrumented my code, and using the audio adaptor SD slot I get writes taking mostly 3.8ms, with the occasional one at 7.2ms - this would give me glitches without the bigger audio blocks, but is OK with them. HOWEVER ... simply switching to the Teensy 4.1's SD card slot, using the same SD card, I get some writes taking 400us, and some taking a whole second...

Could you try switching to non-T4.1 mode, and use the adaptor's card slot, with the exact same card? Just commenting line 48 should do it.
 
Aha! Another thing for you to try (I'm off to bed...). Remove the MTP stuff, by putting the following in (some existing lines shown for context):
Code:
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <TimeLib.h>
[COLOR="#FF0000"]// #include <MTP_Teensy.h>[/COLOR]
//#include "TeensyDebug.h"
#include "play_sd_wav.h" // local copy with fixes
[COLOR="#FF0000"]
#if !defined(MTP_TEENSY_H)
class MTPdummy 
{
  public:
    void begin(void) { Serial.println("Using dummy MTP!"); }
    void loop(void) {}
    void addFilesystem(SDClass& sd,const char* cp) {}
};
MTPdummy MTP;
#endif // !defined(MTP_TEENSY_H)[/COLOR]

I think this should be enough, if not I'll come back to it tomorrow... essentially it replaces the MTP class with a stub which does nothing but print a message saying it's a dummy MTP, not the real thing. You don't need to change from the "Serial + MTP Disk (experimental)" setting. With this in place I find using the Teensy 4.1 SD card slot works perfectly; I suspect MTP accesses the SD card under interrupt, and I know from bitter experience that This Ends In Tears.
 
I know that a lot of people came to this thread from the original YouTube project, which used Teensy 4.0. They may not know there is a difference between the audio shield uSD interface and the native 4.1 uSD slot (faster), and are sticking with the audio shield SD and the 4.0 Teensy because it is what the original project used. I'm curious...

Is there anyone that is using the onboard 4.1 microSD slot that is having audio issues? Specifically the audio cutout problem persisting even with different high speed SD cards, each perhaps initially blessed with a fresh SD formatting?

If no-one using this setup is suffering, maybe the solution is "Get a 4.1, they are more available anyways. Then use the 4.1's uSD slot not the Audio shield."

I haven't made the phone project but had the (slower) Teensy 3.2 recording in mono and playing 2-4 mono tracks before crispy SD access cutouts began, so I'm baffled to see difficulties with the 4.0 and especially 4.1 versions.
 
I'm curious...

Is there anyone that is using the onboard 4.1 microSD slot that is having audio issues?
Yes. See my post immediately preceding yours!

Add a couple of buttons, a microphone and earbuds to your setup and give it a go, I’m curious as to whether the MTP/Teensy 4.1 onboard SD slot issue is reproducible. I’m using Windows 10, it could be dependent on how the OS deals with MTP devices.
 
Aha! Another thing for you to try (I'm off to bed...). Remove the MTP stuff, by putting the following in (some existing lines shown for context):
Code:
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <TimeLib.h>
[COLOR="#FF0000"]// #include <MTP_Teensy.h>[/COLOR]
//#include "TeensyDebug.h"
#include "play_sd_wav.h" // local copy with fixes
[COLOR="#FF0000"]
#if !defined(MTP_TEENSY_H)
class MTPdummy 
{
  public:
    void begin(void) { Serial.println("Using dummy MTP!"); }
    void loop(void) {}
    void addFilesystem(SDClass& sd,const char* cp) {}
};
MTPdummy MTP;
#endif // !defined(MTP_TEENSY_H)[/COLOR]

I think this should be enough, if not I'll come back to it tomorrow... essentially it replaces the MTP class with a stub which does nothing but print a message saying it's a dummy MTP, not the real thing. You don't need to change from the "Serial + MTP Disk (experimental)" setting. With this in place I find using the Teensy 4.1 SD card slot works perfectly; I suspect MTP accesses the SD card under interrupt, and I know from bitter experience that This Ends In Tears.

Finally! After doing this my recording is perfect. Thank you!

Though 1 small "issue" is that the SD card doesn't show up in my PC when I did this. I have to go back and put the MTP class again to check the recording in my PC. Other than that, it works perfectly.
 
Brilliant! Taking the MTP stuff out will "obviously" prevent the files showing up on the PC, but it's now worthwhile tinkering to get a way of enabling it only when needed, e.g. when you boot with the handset off-hook and the playback button pressed. Or maybe someone who groks the MTP will be able to sort out a proper fix - after all, it's still billed as "experimental".

Did you try the option of using the audio adaptor's SD slot? I guess another test is to leave MTP active, but power the system up without it being connected to a PC.
 
Back
Top