Teensy 4.0 - based Audio Guestbook

tedm

Active member
I built the project shown under this Youtube entry using a Teensy 4.0 and the 4.0 Audio card and a Sandisk Ultra 32 GB micro SD card; it sort of works but there are issues that I see have been experienced by others (towards the end of the comments under the video). The code implements the following sequence:

1. After initialization, a beep is heard through the earpiece
2. Then user lifts receiver, hears a beep (not sure this first beep should be there), then the outgoing recorded message, then a tone
3. User then speaks and records raw file and then hangs up
4. Then it's supposed to go back to 2
5. Pressing the playback button momentarially is supposed to play back the messages but it only works if I hold it down - probably need to tune the bounce value for this

It works the first time but, after recording one message correctly, the subsequent times, there's no tone heard at the end of the outgoing message and the second and subsequent .raw recordings include (faintly) the outgoing message at the front of the recording and then the actual recorded sound after that. I have a few theories on what's causing this:

A. There's a code flaw in the way the switch statement is written to implement the state machine - I would not have used a wait or Return in the section where it's playing the message - I think this is where the problem lies
B. Users have said that, if they change the format of the outgoing message to a raw file and change 3 lines of code, it all works properly so maybe there's an issue with the PlayWav function?
C. Power issues causing brownout (although I am running it from a 2.5A capable Pi USB PSU)

I got the .ino file from this github account (also linked in the Youtube video) and the only thing I've changed so far are the gain settings for mic and playback volume. I wonder if there are known problems with this code so I'll wait for any suggestions - my first thought is to rewrite the state machine so there's no Return statement and no waiting at while statements.

Any input or suggestions much appreciated.
 
Hey, I'm also in middle of building this. Did you make any progress?

I'm having the same trouble with the bounce, but I actually think it could be the hook switch on the phone not always sending the signal. The phone I bought is from the 1950s. When I run serial monitor and repeatedly press the hook switch it works about 80% of the time. Not sure how I'm going to solve that problem (if it is the problem).

B. Users have said that, if they change the format of the outgoing message to a raw file and change 3 lines of code, it all works properly so maybe there's an issue with the PlayWav function?

Quick question re the above, what 3 lines of code need to be changed for this? I can only find reference to the greeting once.
 
There's a comment under the Youtube video by Surreylic, and one of the answers says...

nevermind, had enough time to figure it out myself. But thanks for your hint, saved the wedding today! For anyone with the same problem, just switch every playWave1 with playRaw1 and „greeting.wav“ with „greeting.raw“ in the loop, and use audacity to export it as a raw file.

And another comment way down in the list says...

If you want a quick fix, you can use Audacity to export the greeting as a .raw file and change 3 lines of code to make it work. The only drawback is that the greeting will play at the end when listening back to recorded messages, since it will now also be a .raw file. You just need to change line 147 playWav1.play("greeting.wav"); to playRaw1.play("greeting.raw"); line 149 while (playWav1.isPlaying()) { to while (playRaw1.isPlaying()) { and line 154 playWav1.stop(); to playRaw1.stop();

I wondered about trying this but putting the greeting.raw file in a sub folder so that it's not found by the playback system.

The wedding this is for is on July 29 for me, how about you?

I may rewrite the state machine / switch statement code but only if I have to. Hopefully I'll do some more testing today and report back here.
 
Ah yes. Okay that makes sense I will try it. Thanks a lot.

Congrats on the nuptials! My wedding is July 22nd. Fiancee is really starting to tire of the house being an electronics workshop for something I said would take "3-4 hours max". Will be totally worth it for the drunk voicemails.
 
I've been married for over 23 years (3rd time lucky), the wedding Ion Jul 29th is for my youngest son; hope your wedding goes well. I've just retired (Aug 2021) and electronics is a great hobby for retirement.

I was just looking at my Arduino IDE and, after changing the comment text color (makes me realize how clunky the Arduino IDE is - I worked mainly in PSoC Creator in recent years), I realized that the sketchbook location under file>preferences was wrong and I think that makes it use the wrong bounce.h library file (that warning/fact is in the comments section and the Youtube video). I am wondering if this is the cause of the issue I - and others - have, I'll test and report back.
 
Hmmm, so the test I just did had this effect:

1. The bootup was normal, phone down and I hear the power up beep from the earpiece as I power up.
2. Lift phone and hear the outgoing message then record tone, I record a message and hang up.
3. Lift phone again and I immediately hear the record tone, then the outgoing message but no record tone after it, I leave a message and hang up.

The recording done at 2 above is as expected, just the message I left.
The recording done at 3 above has the outgoing message and then the message I left - looks like I'm going to have to rewrite the state machine - sigh!
 
It seems that the while (playWav1.isPlaying()) code works the first time it's called but doesn't work the second (and subsequent) time(s) it's called. In the code section below, it should wait until the outgoing message has stopped playing but the second time it's called, it jumps straight through to play the record tone so the while (playWav1.isPlaying()) must be returning a false value. Does anyone have any ideas?

Code:
      playWav1.play("greeting.wav");    
      // Wait until the  message has finished playing
      while (playWav1.isPlaying()) {
        // Check whether the handset is replaced
        buttonRecord.update();
        // Handset is replaced
        if(buttonRecord.fallingEdge()) {
          playWav1.stop();
          mode = Mode::Ready;
          return;
        }
      }
      // Debug message
      Serial.println("Starting Recording");
      // Play the tone sound effect
 
Looking at the AudioPlaySdWav code (which is deeply nasty), it looks as if it may require an audio interrupt to fire after a .play(), before .isPlaying() becomes true. A crude fix might be putting a delay() in, thus:
Rich (BB code):
      playWav1.play("greeting.wav");
      delay(4); // allow time for an audio interrupt to occur 
      // Wait until the  message has finished playing
      while (playWav1.isPlaying()) {
        // Check whether the handset is replaced
        buttonRecord.update();
        // Handset is replaced
        if(buttonRecord.fallingEdge()) {
          playWav1.stop();
          mode = Mode::Ready;
          return;
        }
      }
The state machine does require a complete re-write, anyone who writes code like the above clearly has but a tenuous grasp of how they work... That little fragment should look more like:
C++:
    case Mode::Prompting:
      // Wait for users to put the handset to their ear
      wait_start = millis();
      mode = Mode::WaitHandsetEar;
      break;
   
    case Mode::WaitHandsetEar:
      // should check for button changes here, and every case
      if (millis() - wait_start > 1000) // give them a second
      {
        // Play the greeting inviting them to record their message
        playWav1.play("greeting.wav"); 
        mode = Mode::WaitGreetingStart;
      }
      break;
   
    case Mode::WaitGreetingStart:
      if (playWav1.isPlaying()) // WAV file has started playing
      {
        mode = Mode::GreetingIsPlaying;
      }
      break;
   
   
    case Mode::GreetingIsPlaying:
      // Wait for greeting to end OR handset to be replaced
      if (playWav1.isPlaying()) // still playing
      {
        // Check whether the handset is replaced
        buttonRecord.update();
        if(buttonRecord.fallingEdge())
        {
          // Handset is replaced
          playWav1.stop();
          mode = Mode::Ready;
          // return; // NO NO NO!
        }
      }
      else
      {
        waveform1.frequency(440);
        waveform1.amplitude(0.9);
        wait_start = millis();
        mode = Mode::PlayingGreetingBeep;
      }
      break;
   
    case Mode::PlayingGreetingBeep:
      if (millis() - wait_start > 250) // 250ms beep
      {
          // beep is done, stop it
          waveform1.amplitude(0);
          // Start the recording function
          startRecording();
      }
      break;
 
Last edited:
Thanks for confirming that the switch statement is not well written and especially thanks for your suggestion on how it should be written; I'll try a code tweak tomorrow.
 
Did you have any joy tweaking this? I had everything working great. Was waiting on an audio breakout board to wire everything 'properly' and when I turned it on today having not touched it for 2 weeks the hook switch will not play ball. It is constantly reversed. Lifting the handset stops recording, replacing it starts it. Don't understand how that can happen if I haven't changed anything but would appreciate any help you can give.
 
Did you have any joy tweaking this? I had everything working great. Was waiting on an audio breakout board to wire everything 'properly' and when I turned it on today having not touched it for 2 weeks the hook switch will not play ball. It is constantly reversed. Lifting the handset stops recording, replacing it starts it. Don't understand how that can happen if I haven't changed anything but would appreciate any help you can give.

This is what I get now. Where it says handset lifted is when I pressed the hook switch. It never reads it as down, only as lifting it.

Code:
Button (pin 0) Release
Button (pin 1) Release
Handset lifted
Handset lifted
Starting Recording
Button (pin 0) Press
Button (pin 0) Release
Button (pin 0) Press
Button (pin 0) Release
Recording to  00002.RAW
 
My modified .ino file

Attached modified .ino file by request.
I am new to posting so I hope I am doing this right.
I am not a programmer so the mods I made can for sure be done better.
I set the speaker and mic levels for my device.
I increased bounce times to suit my device.
I increased the time between pickup and when the greeting starts.
I changed code to use .raw audio file for the greeting.
I added code to stop the program if the handset is returned before the greeting starts playing.
I added additional audio patch cable to allow the person talking to hear their own voice in the speaker as they are talking. Regular phones do this so it seems more natural to me.
I tried to mark my changes with RAM in the comments.

My device is now working great so maybe this will help someone else.

Also many thanks to Playful Technology for this wonderful project.
 

Attachments

  • audio-guestbook_mod1 - Copy.ino
    11.6 KB · Views: 867
Attached modified .ino file by request.
I am new to posting so I hope I am doing this right.

From one Rick to another Rik, thanks a lot. I cannot open the ino file without the other data, but if you select the code in the file and copy, paste the code here that would work. Thanks again for your help.
 
Will this work?

From one Rick to another Rik, thanks a lot. I cannot open the ino file without the other data, but if you select the code in the file and copy, paste the code here that would work. Thanks again for your help.

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
 * These can be loaded directly into Audacity ([url]https://www.audacityteam.org/[/url]). Or else, converted to WAV/MP3 format using SOX ([url]https://sourceforge.net/projects/sox/[/url])
 * 
 **/
 

// **********   Rik  6/22/2022  -   My mods are marked with RAM in comments.  *********

// ************  GREETING MUST BE RECORDED IN .RAW FORMAT  (16 BIT 44.1Khz. MONO)   ****************************************************************


// INCLUDES
// The default "sketchbook" location in which Arduino IDE installs libraries is:
// C:\Users\alast\Documents\Arduino
// However, the TeensyDuino installer installs libraries in:
// C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries
// To ensure the correct libraries are used when targetting Teensy platform in Arduino IDE, go File->Preferences and change the sketchbook location to avoid conflicts with Arduino libraries.
// When targetting Arduino boards, change it back again to default
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <TimeLib.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

 
int     hookRead;  // Added to check for hang up before playing greeting.  RAM
int     x;

// 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
AudioPlaySdRaw          playRaw1; // Play .RAW audio files saved on SD card
AudioPlaySdWav          playWav1; // Play 44.1kHz 16-bit PCM greeting WAV file
// Outputs
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
// Connections
AudioConnection patchCord1(waveform1, 0, mixer, 0); // wave to mixer 
AudioConnection patchCord2(playRaw1, 0, mixer, 1); // raw audio to mixer
AudioConnection patchCord3(playWav1, 0, mixer, 2); // wav file playback mixer
AudioConnection patchCord4(mixer, 0, i2s1, 0); // mixer output to speaker (L)
AudioConnection patchCord5(i2s2, 0, queue1, 0); // mic input to queue (L)

AudioConnection patchcord6(i2s2, 0, mixer, 3); // Added mic input to mixer to hear talking through handset speaker.  RAM

AudioControlSGTL5000     sgtl5000_1;

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

// Use long 100ms debounce time on hook switch
Bounce buttonRecord = Bounce(HOOK_PIN, 100);    // default (40).  RAM
Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 100);   // default (8).  RAM



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

void setup() {

  // Note that Serial.begin() is not required for Teensy - 
  // by default it initialises serial communication at full USB speed
  // See [url]https://www.pjrc.com/teensy/td_serial.html[/url]
  // Serial.begin()
  Serial.println(__FILE__ __DATE__);
  
  // 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.volume(0.6);  // Output speaker volume.  default (.5).   RAM

  // Play a beep to indicate system is online
  waveform1.begin(WAVEFORM_SINE);
  waveform1.frequency(440);
  waveform1.amplitude(0.9);
  wait(250);
  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);
    }
  }

  // Mic Gain (Value in dB)
  sgtl5000_1.micGain(38);   // default (15).  RAM

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

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

  switch(mode){
    case Mode::Ready:
      // Rising edge occurs when the handset is lifted
      if (buttonRecord.risingEdge()) {
        Serial.println("Handset lifted");
        mode = Mode::Prompting;
      }
      else if(buttonPlay.fallingEdge()) {
        playAllRecordings();
      }
      break;
      

    case Mode::Prompting:

    
             // Wait a second for users to put the handset to their ear.   RAM
             // Added to read hook pin to check if handset has been replaced before proceeding.  RAM
                      
      x=0;   // Clear counter.   
      while (x < 30)  {
        wait(50);   // Wait 50 mS.  
        x=x+1;      // Increment counter
        Serial.println(x);  // Print counter value
        hookRead=digitalRead(HOOK_PIN);  // Check if handset replaced.  
          if (hookRead==0)  {
          Serial.println("Handset Replaced Before Message");
          mode = Mode::Ready;  // Return to ready mode if hang up.
          return;
        }
      }
      
 
           
      // Play the greeting inviting them to record their message
      playRaw1.play("greeting.raw");    //  Changed from default (.wav) to (.raw) format.  Greeting MUST be recorded in .raw format.   RAM
      // Wait until the  message has finished playing
      while (playRaw1.isPlaying()) {
      
        // Check whether the handset is replaced
        buttonRecord.update();
        // Handset is replaced
        if(buttonRecord.fallingEdge()) {
          playRaw1.stop();
          mode = Mode::Ready;
          return;

          
        }
        
      }
      // Debug message
      Serial.println("Starting Recording");
      // Play the tone sound effect
      waveform1.frequency(440);
      waveform1.amplitude(0.9);
      wait(250);
      waveform1.amplitude(0);
      // Start the recording function
      startRecording();
      break;

    case Mode::Recording:
      // Handset is replaced
      if(buttonRecord.fallingEdge()){
        // Debug log
        Serial.println("Stopping Recording");
        // Stop recording
        stopRecording();
        // Play audio tone to confirm recording has ended
        waveform1.frequency(523.25);
        waveform1.amplitude(0.9);
        wait(50);
        waveform1.amplitude(0);
        wait(50);
        waveform1.amplitude(0.9);
        wait(50);
        waveform1.amplitude(0);
      }
      else {
        continueRecording();
      }
      break;

    case Mode::Playing:
      break;  
  }   
}

void startRecording() {
  // Find the first available file number
  for (uint8_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.RAW", 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);
  if(frec) {
    Serial.print("Recording to ");
    Serial.println(filename);
    queue1.begin();
    mode = Mode::Recording;
  }
  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);
  }
}

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();
  }
  // Close the file
  frec.close();
  mode = Mode::Ready;
}


void playAllRecordings() {
  // Recording files are saved in the root directory
  File dir = SD.open("/");
  
  while (true) {
    File entry =  dir.openNextFile();
    if (!entry) {
      // no more files
      entry.close();
      break;
    }

    int8_t len = strlen(entry.name());
    if (strstr(strlwr(entry.name() + (len - 4)), ".raw")) {
      Serial.print("Now playing ");
      Serial.println(entry.name());
      // Play a short beep before each message
      waveform1.amplitude(0.5);
      wait(250);
      waveform1.amplitude(0);
      // Play the file
      playRaw1.play(entry.name());
      mode = Mode::Playing;
    }
    entry.close();

    while (playRaw1.isPlaying()) {
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
      if(buttonPlay.risingEdge() || buttonRecord.fallingEdge()) {
        playRaw1.stop();
        mode = Mode::Ready;
        return;
      }   
    }
  }
  // All files have been played
  mode = Mode::Ready;
}

// 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");
  }
}
 
Last edited by a moderator:
Thanks a lot, unfortunately has not solved the issue.

For anyone who has bright ideas, here is where I am at.

Audio output on the audio shield is outputting fine. Can plug ear buds or ear phones into and it plays the greeting no problem.

SparkFun breakout board is receiving audio fine and passing it through to the phone without issue. I've played the .raw greeting via my laptop headphone jack and can hear it perfectly through the phone handset.

When I connect the audio shield to the breakout board, I can hear the tone generated by the teensy for start up and the tone for 'stopped recording' but the greeting is just static. The file I have put on the SD card is 44.1khz, 16bit in mono. The only thing I can think of is that somehow the audio shield is passing the audio over ring1 and ring2 parts of the 3.5mm jack instead of the tip and sleeve? Does anyone have any ideas?
 
I connected my speaker to the audio board at the side of the jack. Negative wire soldered to the metal leg at the front corner of the jack and the positive wire goes in the first small hole right beside the metal leg. Don't know if yours is this way but it works for mine.
 
Thanks a lot, unfortunately has not solved the issue.

For anyone who has bright ideas, here is where I am at.

Audio output on the audio shield is outputting fine. Can plug ear buds or ear phones into and it plays the greeting no problem.

SparkFun breakout board is receiving audio fine and passing it through to the phone without issue. I've played the .raw greeting via my laptop headphone jack and can hear it perfectly through the phone handset.

When I connect the audio shield to the breakout board, I can hear the tone generated by the teensy for start up and the tone for 'stopped recording' but the greeting is just static. The file I have put on the SD card is 44.1khz, 16bit in mono. The only thing I can think of is that somehow the audio shield is passing the audio over ring1 and ring2 parts of the 3.5mm jack instead of the tip and sleeve? Does anyone have any ideas?

I think your connections are correct, it is the tip and sleeve for audio playback.

I'm working on this today; have you increased the volume of the greeting playback - the level for the tones are set separately I think?
 
A quick question on Teensy code functionality concerning when the buttons are tested for a rising or falling edge.

If the user lifts the handset then the function buttonRecord.update(); that's at the top of the loop will test and, presumably, if the handset is lifted, buttonRecord.risingEdge will be true but, now the handset is up and stays up, the next time that buttonRecord.update(); function is called, buttonRecord.risingEdge will not be true, have I got this right?
 
OK, so I made the code changes suggested by h4n0nnym0u5e and, as far as my limited testing has gone, the logic works fine. The problem I have now is a buzzing noise on the recorded audio which I think is higher than 60 Hz which suggests to me that it's caused by some kind of interaction with the logic testing done on the buttons; maybe a ground error. Nayway, here's the code listing for anyone that wants to try it...

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
 * These can be loaded directly into Audacity (https://www.audacityteam.org/). Or else, converted to WAV/MP3 format using SOX (https://sourceforge.net/projects/sox/)
 * 
 **/

// INCLUDES
// The default "sketchbook" location in which Arduino IDE installs libraries is:
// C:\Users\alast\Documents\Arduino
// However, the TeensyDuino installer installs libraries in:
// C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries
// To ensure the correct libraries are used when targetting Teensy platform in Arduino IDE, go File->Preferences and change the sketchbook location to avoid conflicts with Arduino libraries.
// When targetting Arduino boards, change it back again to default
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <TimeLib.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
AudioPlaySdRaw          playRaw1; // Play .RAW audio files saved on SD card
AudioPlaySdWav          playWav1; // Play 44.1kHz 16-bit PCM greeting WAV file
// Outputs
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
// Connections
AudioConnection patchCord1(waveform1, 0, mixer, 0); // wave to mixer 
AudioConnection patchCord2(playRaw1, 0, mixer, 1); // raw audio to mixer
AudioConnection patchCord3(playWav1, 0, mixer, 2); // wav file playback mixer
AudioConnection patchCord4(mixer, 0, i2s1, 0); // mixer output to speaker (L)
AudioConnection patchCord5(i2s2, 0, queue1, 0); // mic input to queue (L)
AudioControlSGTL5000     sgtl5000_1;

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

// Use long 40ms debounce time on hook switch
Bounce buttonRecord = Bounce(HOOK_PIN, 40);
Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 8);

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

void setup() {

  // Note that Serial.begin() is not required for Teensy - 
  // by default it initialises serial communication at full USB speed
  // See https://www.pjrc.com/teensy/td_serial.html
  // Serial.begin()
  Serial.println(__FILE__ __DATE__);
  
  // 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.volume(0.9); 
  //                ^^^ was 0.5

  // Play a beep to indicate system is online
  waveform1.begin(WAVEFORM_SINE);
  waveform1.frequency(440);
  waveform1.amplitude(0.9);
  wait(250);
  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);
    }
  }

  // Value in dB
  sgtl5000_1.micGain(40);
  //                 ^^ was 15

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

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

  switch(mode){
    case Mode::Ready:
      // Rising edge occurs when the handset is lifted
      if (buttonRecord.risingEdge()) {
        Serial.println("Handset lifted");
        mode = Mode::Prompting;
      }
      else if(buttonPlay.fallingEdge()) {
        playAllRecordings();
      }
      break;

  case Mode::Prompting:
    // Wait for users to put the handset to their ear
    wait_start = millis();
    mode = Mode::WaitHandsetEar;
    break;

  case Mode::WaitHandsetEar:
    // should check for button changes here, and every case
    if (millis() - wait_start > 1000) {   // give them a second
      // Play the greeting inviting them to record their message
      playWav1.play("greeting.wav");    
      mode = Mode::WaitGreetingStart;
    }
    break;
    
  case Mode::WaitGreetingStart:
    if (playWav1.isPlaying()) {         // WAV file has started playing
      mode = Mode::GreetingIsPlaying;
    }
    break;
     
  case Mode::GreetingIsPlaying:
    // Wait for greeting to end OR handset to be replaced
    if (playWav1.isPlaying()) {         // still playing
      // Check whether the handset is replaced
      buttonRecord.update();
      if(buttonRecord.fallingEdge()) {
        // Handset is replaced
        playWav1.stop();
        mode = Mode::Ready;
        // return; // NO NO NO!
      }
    }
    else {
      waveform1.frequency(440);
      waveform1.amplitude(0.9);
      wait_start = millis();
      mode = Mode::PlayingGreetingBeep;
    }
    break;
    
  case Mode::PlayingGreetingBeep:
    if (millis() - wait_start > 250) {      // 250ms beep
      // beep is done, stop it
      waveform1.amplitude(0);
      // Start the recording function
      startRecording();
    }
    break;


    case Mode::Recording:
      // Handset is replaced
      if(buttonRecord.fallingEdge()){
        // Debug log
        Serial.println("Stopping Recording");
        // Stop recording
        stopRecording();
        // Play audio tone to confirm recording has ended
        waveform1.frequency(523.25);
        waveform1.amplitude(0.9);
        wait(50);
        waveform1.amplitude(0);
        wait(50);
        waveform1.amplitude(0.9);
        wait(50);
        waveform1.amplitude(0);
      }
      else {
        continueRecording();
      }
      break;

    case Mode::Playing:
      break;  
  }   
}

void startRecording() {
  // Find the first available file number
  for (uint8_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.RAW", 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);
  if(frec) {
    Serial.print("Recording to ");
    Serial.println(filename);
    queue1.begin();
    mode = Mode::Recording;
  }
  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);
  }
}

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();
  }
  // Close the file
  frec.close();
  mode = Mode::Ready;
}


void playAllRecordings() {
  // Recording files are saved in the root directory
  File dir = SD.open("/");
  
  while (true) {
    File entry =  dir.openNextFile();
    if (!entry) {
      // no more files
      entry.close();
      break;
    }

    int8_t len = strlen(entry.name());
    if (strstr(strlwr(entry.name() + (len - 4)), ".raw")) {
      Serial.print("Now playing ");
      Serial.println(entry.name());
      // Play a short beep before each message
      waveform1.amplitude(0.5);
      wait(250);
      waveform1.amplitude(0);
      // Play the file
      playRaw1.play(entry.name());
      mode = Mode::Playing;
    }
    entry.close();

    while (playRaw1.isPlaying()) {
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
      if(buttonPlay.risingEdge() || buttonRecord.fallingEdge()) {
        playRaw1.stop();
        mode = Mode::Ready;
        return;
      }   
    }
  }
  // All files have been played
  mode = Mode::Ready;
}

// 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");
  }
}
 
A quick question on Teensy code functionality concerning when the buttons are tested for a rising or falling edge.

If the user lifts the handset then the function buttonRecord.update(); that's at the top of the loop will test and, presumably, if the handset is lifted, buttonRecord.risingEdge will be true but, now the handset is up and stays up, the next time that buttonRecord.update(); function is called, buttonRecord.risingEdge will not be true, have I got this right?

That's how I saw it.

I got my audio playing eventually, by swapping the hook pin wires over. So this doesn't make sense to me, that that is what was stopping the audio playing, because pin 0 wasn't being called. But anyway I got that playing but now the hook is reversed, so I have to press the hook switch to get the greeting to play. I've tried reversing the risingEdge/fallingEdge logic in the code without success. I'll try your code now and see if that works. Thanks for your continued help!
 
That's how I saw it.

I got my audio playing eventually, by swapping the hook pin wires over. So this doesn't make sense to me, that that is what was stopping the audio playing, because pin 0 wasn't being called. But anyway I got that playing but now the hook is reversed, so I have to press the hook switch to get the greeting to play. I've tried reversing the risingEdge/fallingEdge logic in the code without success. I'll try your code now and see if that works. Thanks for your continued help!

Still doesn't work, though is different. With your code I get the greeting working, but mostly it cuts off after a second or two.

I'm wondering now if there is some short in the board of the phone that's causing these intermittent problems. I don't have another old phone I can try to test that. Not sure it even makes sense. Genuinely lost at this point. I had a spare audio shield and went and soldered all the connections (mic, gnd, and 0/1 pins) to make sure. Same result.

I've tried the below code which will work so long as I depress the hook switch a small bit, but its not reliable. I'm about to give up here.


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
 * These can be loaded directly into Audacity (https://www.audacityteam.org/). Or else, converted to WAV/MP3 format using SOX (https://sourceforge.net/projects/sox/)
 * 
 **/

// INCLUDES
// The default "sketchbook" location in which Arduino IDE installs libraries is:
// C:\Users\alast\Documents\Arduino
// However, the TeensyDuino installer installs libraries in:
// C:\Program Files (x86)\Arduino\hardware\teensy\avr\libraries
// To ensure the correct libraries are used when targetting Teensy platform in Arduino IDE, go File->Preferences and change the sketchbook location to avoid conflicts with Arduino libraries.
// When targetting Arduino boards, change it back again to default
#include <Bounce.h>
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <TimeLib.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
AudioPlaySdRaw          playRaw1; // Play .RAW audio files saved on SD card
AudioPlaySdWav          playWav1; // Play 44.1kHz 16-bit PCM greeting WAV file
// Outputs
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
// Connections
AudioConnection patchCord1(waveform1, 0, mixer, 0); // wave to mixer 
AudioConnection patchCord2(playRaw1, 0, mixer, 1); // raw audio to mixer
AudioConnection patchCord3(playWav1, 0, mixer, 2); // wav file playback mixer
AudioConnection patchCord4(mixer, 0, i2s1, 0); // mixer output to speaker (L)
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 hook switch
Bounce buttonRecord = Bounce(HOOK_PIN, 40);
Bounce buttonPlay = Bounce(PLAYBACK_BUTTON_PIN, 8);

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

void setup() {

  // Note that Serial.begin() is not required for Teensy - 
  // by default it initialises serial communication at full USB speed
  // See https://www.pjrc.com/teensy/td_serial.html
  // Serial.begin()
  Serial.println(__FILE__ __DATE__);
  
  // 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.volume(0.7);

  // Play a beep to indicate system is online
  waveform1.begin(WAVEFORM_SINE);
  waveform1.frequency(340);
  waveform1.amplitude(1.0);
  wait(250);
  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);
    }
  }

  // Value in dB
  sgtl5000_1.micGain(45);

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

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

  switch(mode){
    case Mode::Ready:
      // Rising edge occurs when the handset is lifted
      if (buttonRecord.risingEdge()) {
        Serial.println("Handset lifted");
        mode = Mode::Prompting;
      }
      else if(buttonPlay.risingEdge()) {
        playAllRecordings();
      }
      break;

    case Mode::Prompting:
      // Wait a second for users to put the handset to their ear
      wait(1200);
      // Play the greeting inviting them to record their message
      playRaw1.play("greeting.raw");   
      // Wait until the  message has finished playing
      while (playRaw1.isPlaying()) {
        // Check whether the handset is replaced
        buttonRecord.update();
        // Handset is replaced
        if(buttonRecord.fallingEdge()) {
          playRaw1.stop();
          mode = Mode::Ready;
          return;
        }
      }
      // Debug message
      Serial.println("Starting Recording");
      // Play the tone sound effect
      waveform1.frequency(440);
      waveform1.amplitude(0.9);
      wait(250);
      waveform1.amplitude(0);
      // Start the recording function
      startRecording();
      break;

    case Mode::Recording:
      // Handset is replaced
      if(buttonRecord.fallingEdge()){
        // Debug log
        Serial.println("Stopping Recording");
        // Stop recording
        stopRecording();
        // Play audio tone to confirm recording has ended
        waveform1.frequency(523.25);
        waveform1.amplitude(0.9);
        wait(50);
        waveform1.amplitude(0);
        wait(50);
        waveform1.amplitude(0.9);
        wait(50);
        waveform1.amplitude(0);
      }
      else {
        continueRecording();
      }
      break;

    case Mode::Playing:
      break;  
  }   
}

void startRecording() {
  // Find the first available file number
  for (uint8_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.RAW", 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);
  if(frec) {
    Serial.print("Recording to ");
    Serial.println(filename);
    queue1.begin();
    mode = Mode::Recording;
  }
  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);
  }
}

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();
  }
  // Close the file
  frec.close();
  mode = Mode::Ready;
}


void playAllRecordings() {
  // Recording files are saved in the root directory
  File dir = SD.open("/");
  
  while (true) {
    File entry =  dir.openNextFile();
    if (!entry) {
      // no more files
      entry.close();
      break;
    }

    int8_t len = strlen(entry.name());
    if (strstr(strlwr(entry.name() + (len - 4)), ".raw")) {
      Serial.print("Now playing ");
      Serial.println(entry.name());
      // Play a short beep before each message
      waveform1.amplitude(0.5);
      wait(250);
      waveform1.amplitude(0);
      // Play the file
      playRaw1.play(entry.name());
      mode = Mode::Playing;
    }
    entry.close();

    while (playRaw1.isPlaying()) {
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
      if(buttonPlay.risingEdge() || buttonRecord.fallingEdge()) {
        playRaw1.stop();
        mode = Mode::Ready;
        return;
      }   
    }
  }
  // All files have been played
  mode = Mode::Ready;
}

// 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.risingEdge()) Serial.println("Button (pin 1) Press");
    if (buttonRecord.risingEdge()) Serial.println("Button (pin 0) Release");
    if (buttonPlay.fallingEdge()) Serial.println("Button (pin 1) Release");
  }
}
 
Last edited:
finalcut
There are SIGNIFICANT changes between the switch statement in your code and mine. I've updated the code to handle the sending of the recorded message and then wait until that message has been sent before recording. You may want to try it as, among other things, it gets rid of the RETURN statement that is not something I would ever have put in a Switch/case statement. If you've made changes to the original code e.g for levels then copy those changed lines into my code and then try it.
 
finalcut
There are SIGNIFICANT changes between the switch statement in your code and mine. I've updated the code to handle the sending of the recorded message and then wait until that message has been sent before recording. You may want to try it as, among other things, it gets rid of the RETURN statement that is not something I would ever have put in a Switch/case statement. If you've made changes to the original code e.g for levels then copy those changed lines into my code and then try it.

Thanks a lot for updating the code and for your help. The issue is still intermittent. When I lift the handset, I hear nothing. If I slightly depress the hook switch (not all the way) the greeting plays and I get the tone. Serial monitor is normal at that point, showing recording and the phone records to the SD card. To end recording, I need to depress the hook switch all the way and then release it. So it still seems like it is reversed. But if I switch around the risingEdge/fallingEdge statements that doesn't cure it. Leads back to wondering if the actual mechanical switch is not working. I will try to obtain another phone to test this. Wedding is in 12 days so running a little tight on time ;-).
 
Thanks a lot for updating the code and for your help. The issue is still intermittent. When I lift the handset, I hear nothing. If I slightly depress the hook switch (not all the way) the greeting plays and I get the tone. Serial monitor is normal at that point, showing recording and the phone records to the SD card. To end recording, I need to depress the hook switch all the way and then release it. So it still seems like it is reversed. But if I switch around the risingEdge/fallingEdge statements that doesn't cure it. Leads back to wondering if the actual mechanical switch is not working. I will try to obtain another phone to test this. Wedding is in 12 days so running a little tight on time ;-).

You're welcome. It sounds like your switch hook (the switch that operates when you lift your phone) is faulty. Try putting another switch in the circuit and test that it operates correctly; if it does then you need to figure out how to fix your switch. One idea that occurs to me is that there are other components attached to your switch e.g. capacitors; if that's the case then you will probably fix the issue by isolating those components perhaps by cutting their leads or by cutting the PCB tracks and isolating the switch. If it's an old style phone then you may be able to bend the metal work to make the switch hook operated correctly for your needs. Can you post a close up photo of the switch on your phone? I will try to advise what you can do (I'm an EE who was a phone guy back in the day).

I found that my buzzing sound was caused by the mic wire placement too close to the wires carrying the digital switching signals; when I put the Teensy PCBs into the phone properly (it's a rat's nest right now), I will try to keep the mic wires short and away from the other wires. FYI, I fully tested the rest of the code and it all seems to work playing back with beeps etc (my beeps were too loud so I changed the 0.9 numbers to 0.5 and it's better).

One idea I had for improvement was to make the playback work in reverse order - anyone here got experience with the Teensy file drivers that might comment on how to find the newest recording? If the newest played first then the user could press the playback button and review their recording, if they didn't like it, they could record another message.
 
Back
Top