Teensy guestbook - No sound when play wav file on my computer

Hi everyone!
I am new here and I am with a problem with my project for weeks, any help will be appreciated.
I'm trying to make a guestbook with a rotary phone like that: https://www.youtube.com/watch?v=dI6ielrP1SE, my issue is that I can't hear audio when I play the wav, the file has size and the player time is running, I tried many things, I tested with 3 different microphones, connections look good, serial monitor look ok as well, the greeting message is ok. I don't know if I need a specific microphone or some connection is wrong, I really appreciate some help guys. If you need more information, images or any specific thing please let me know, since now thanks.

I am using teensy 4.0 and the audio board Rev D.
Code is here: https://github.com/playfultechnology/audio-guestbook/blob/main/audio-guestbook.ino

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

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

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

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

// 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");
  print_mode();
  // Configure the input pins
  pinMode(HOOK_PIN, INPUT_PULLUP);

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

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

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

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

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

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

  // Add SD Card
  //    MTP.addFilesystem(SD, "SD Card");
  MTP.addFilesystem(SD, "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(40); // 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();

  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();
    }
    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();
      // Handset is replaced
      if (buttonRecord.risingEdge())
      {
        playWav1.stop();
        mode = Mode::Ready;
        print_mode();
        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
      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
      buttonRecord.update();
      // Button is pressed again
      //      if(buttonPlay.risingEdge() || buttonRecord.risingEdge()) { // FIX
      if (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
    buttonRecord.update();
    // Button is pressed again
    //      if(buttonPlay.risingEdge() || buttonRecord.risingEdge()) { // FIX
    if (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();
    if (buttonRecord.fallingEdge())
      Serial.println("Button (pin 0) Press");
    if (buttonRecord.risingEdge())
      Serial.println("Button (pin 0) 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");
}
 

Attachments

  • IMG_1097.jpg
    IMG_1097.jpg
    184.4 KB · Views: 47
  • IMG_1098.jpg
    IMG_1098.jpg
    167.2 KB · Views: 43
  • IMG_1099.jpg
    IMG_1099.jpg
    151.2 KB · Views: 36
  • IMG_1101.jpg
    IMG_1101.jpg
    154.5 KB · Views: 43
Post a WAV file that shows the problem? See Forum Rule at the top of every page you visit here ... "details to reproduce any issue".

One issue with DD4WH's fork, which I fixed and got pulled into the original repo, is that the WAV files have incorrect chunk lengths in them. I can get them to play on my PC, but that may be application-dependent.

Or the record level may just be very low, and you're boosting the output enough to hear it on the phone, but not the PC. Take a look at the waveform in Audacity or similar.
 
Thank you h4yn0nnym0u5e. New here -- didn't seem like my question needed my sketch (since it's all pretty much copy/paste) but I can do so next. For now I've imported a wav into Audacity and I see a flatline of audio. It sounded loud enough when played back on the phone.
But the bitrate is cofusing me. If I look at the file (00014.wav) properties with Windows Explorer it says bitrate is 705 kbps! The sketch says 'unsigned long sampleRate = 44100;' -- Audacity appears to show 44100 Hz (see image). Not sure if my recent library downloads included your chunk-length fixes.

Having lots of trouble trying to include a jpg image here. Where does it show up after attaching? :p Will also attempt to attach the 00014.wav file. Sorry, it's not letting me attach the wav file.
- Vince D
 
Last edited:
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, 40);
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.04f; // not too loud :-)

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


void setup() {

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

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

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

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

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

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

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

  // Add SD Card
    MTP.addFilesystem(SD, "Gruenkohls 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);  // original is 15 -vfd
  // Synchronise the Time object used in the program code with the RTC time provider.
  // See https://github.com/PaulStoffregen/Time
  setSyncProvider(getTeensy3Time);
  
  // Define a callback that will assign the correct datetime for any file system operations
  // (i.e. saving a new audio recording onto the SD card)
  FsDateTime::setCallback(dateTime);

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

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

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

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

#if defined(AUTO_GREETING_MESSAGE)

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

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

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

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

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

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

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

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

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


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

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

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


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

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

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

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

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

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

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


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

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

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

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

void print_mode(void) { // only for debugging
  Serial.print("Mode switched to: ");
  // Initialising, Ready, Prompting, Recording, Playing
  if(mode == Mode::Ready)           Serial.println(" Ready");
  else if(mode == Mode::Prompting)  Serial.println(" Prompting");
  else if(mode == Mode::Recording)  Serial.println(" Recording");
  else if(mode == Mode::Playing)    Serial.println(" Playing");
  else if(mode == Mode::Initialising)  Serial.println(" Initialising");
  else if(mode == Mode::Recording_Greeting)  Serial.println(" Recording Greeting");
  else Serial.println(" Undefined");
}
 
One issue with DD4WH's fork, which I fixed and got pulled into the original repo, is that the WAV files have incorrect chunk lengths in them. I can get them to play on my PC, but that may be application-dependent.

Hmm, I cannot reproduce a problem in playing the WAV files recorded with my code, they play correctly and smoothly with different players on different devices.

You may be right that the chunk lengths are inconsistent, but the files are playing and sound nice . . .

As you claim in various threads that there are issues with the code in my repo (which I cannot reproduce, because I built four Audio Guest Books with my code which function flawlessly), I will now delete my repo in order for all those people interested in the Audio guest Book to have ONE consistent code source. I am sorry that my code produced problems, but without reproducible issue description, there is no way for me to help.

Have fun with the Teensy !
 
Hmm, I cannot reproduce a problem in playing the WAV files recorded with my code, they play correctly and smoothly with different players on different devices.

You may be right that the chunk lengths are inconsistent, but the files are playing and sound nice . . .

As you claim in various threads that there are issues with the code in my repo (which I cannot reproduce, because I built four Audio Guest Books with my code which function flawlessly), I will now delete my repo in order for all those people interested in the Audio guest Book to have ONE consistent code source. I am sorry that my code produced problems, but without reproducible issue description, there is no way for me to help.

Have fun with the Teensy !

Replied to here: https://forum.pjrc.com/threads/70553-Teensy-4-0-based-Audio-Guestbook?p=323057&viewfull=1#post323057
 
Thank you h4yn0nnym0u5e. New here -- didn't seem like my question needed my sketch (since it's all pretty much copy/paste) but I can do so next. For now I've imported a wav into Audacity and I see a flatline of audio. It sounded loud enough when played back on the phone.
But the bitrate is cofusing me. If I look at the file (00014.wav) properties with Windows Explorer it says bitrate is 705 kbps! The sketch says 'unsigned long sampleRate = 44100;' -- Audacity appears to show 44100 Hz (see image). Not sure if my recent library downloads included your chunk-length fixes.

Having lots of trouble trying to include a jpg image here. Where does it show up after attaching? :p Will also attempt to attach the 00014.wav file. Sorry, it's not letting me attach the wav file.
- Vince D

To insert an image, use the insert image button:
2023-03-27 19_37_04-Teensy guestbook - No sound when play wav file on my computer.png
Note you have to choose the file, then upload it.

You may need to zip a WAV file in order to upload it, not sure.

705kbps is correct: 44100 ksps = 88200 kBps = 705.6 kbps (k samples, bytes and bits per second).

No particular need to upload the sketch so long as you've tried an original one unmodified. If you modified it ... you may well have broken it. Use one source repository, and get it working, then make small changes and test as you go along. It's a recipe for failure to start by mixing and matching code from several repositories unless you know exactly what you're doing.
 
Thank you h4yn0nnym0u5e -- Learned something new today (the conversion of kbps from ksps)! I did the Insert Image thing (and upload of the .jpg file) but it didn't work. I'll try it again here for practice. Guess I'll diff my sketch with the repo to be sure I didn't break it.
Just inserted and uploaded a jpg -- the dialog disappears when I click on upload... no image. Ah! I see it as an attachment in the advanced editor view.
 

Attachments

  • audacity-audio-wav.JPG
    audacity-audio-wav.JPG
    24.6 KB · Views: 19
That looks like a noisy example uploaded to the forum recently. Here it is before:
2023-03-27 20_54_12-.png
and now, after I got rid of a loud glitch at the end, then did Effect / Normalize:
2023-03-27 20_54_55-.png
Couldn't hear it before I did that...

EDIT: your code in post #6 doesn't have the chunk size fixes - they're near the start of writeOutHeader()
 
Sure thought I had it when I realized I was recording with the Teensy connected to my PC. So I recorded 00015.wav connected to an external power pack. Still no amplitude in the playback on my PC (the greeting.wav plays fine). Even though it wasn't a factor, the YOU CANNOT RECORD WHILE THE TELEPHONE IS PLUGGED INTO THE PC warning would be good to be placed near the head of audio-guestbook.ino. The attached diff of the current sketch (DD4WH) (left side) and my sketch (right), showing only differences, looks good to me. Will try to attach the wav file (zipped). And FWIW an image of the serial monitor when I plugged back into the PC.
Okay, none of this attach stuff is working (again) -- I'm pretty sure I'm running a current Chrome browser. Maybe I'm downlevel on Java? May fire up Edge and try from there...
Seems to be working with Edge.
 

Attachments

  • audio-guestbook-ino-diff.jpg
    audio-guestbook-ino-diff.jpg
    93.5 KB · Views: 25
  • 00015.zip
    132.7 KB · Views: 28
  • serial-monitor.JPG
    serial-monitor.JPG
    34.8 KB · Views: 21
Last edited:
Odd that you can't attach stuff, not much I can do to help with that! Could be a forum restriction on new joiners, is the only thing I can think of. Can you put the WAV on a DropBox or Google drive, with public access?

My code as pulled into the official repo is intended to mitigate the "can't record while plugged into the PC" restriction, though I can't say for sure it's 100% effective, there are just too many variables. The issue was some weirdness with the MTP, which took serious expertise (i.e. not me!) to diagnose. I'd have to go back to see if the next Teensyduino is likely to have a fix for that, but the mitigation won't hurt if it's left in, as the normal use case is using a power pack anyway.

If in doubt, the power pack option is best for overall quality and lack of glitches. However, it should not affect the level - you ought to get something, even if it's stuttering. So (near?) silence suggests a hardware problem. I've ordered some electret mics from eBay to play with, though they may not be representative of the ones people are actually using.

AH ... just noticed something ...
Way after all the other sgtl5000_1 initialisation stuff, there's a call sgtl5000_1.micGain(15); which changes the mic gain from the default 52dB to 15dB, so 37dB quieter. That's a lot. Try commenting that line out ... you may need to change the mixer gain and / or sgtl5000_1.volume() to avoid blowing your ears out on the phone. And the beep waveform level, too.
 
Okay, I cranked the mic gain up to 52 dB (thank you h4) -- and now the wav has good audio level -- including a terrible noise background! Now I need shielded audio mic lines? Can't do that in the "curly" cable coming from the handset! 00019.wav (zip attached) was done powered by the power pack and with audio tweaks set to "normal" -- with the tweaks enabled, the recording was very distorted (even at the low mic gain). It's unclear why original mic gain was 15 dB.
 

Attachments

  • 00019.zip
    521.5 KB · Views: 32
Okay, I cranked the mic gain up to 52 dB (thank you h4) -- and now the wav has good audio level -- including a terrible noise background! Now I need shielded audio mic lines? Can't do that in the "curly" cable coming from the handset! 00019.wav (zip attached) was done powered by the power pack and with audio tweaks set to "normal" -- with the tweaks enabled, the recording was very distorted (even at the low mic gain). It's unclear why original mic gain was 15 dB.
Yup, that sounds awful all right! But at least we're vaguely in control of the levels now.

As you say, hard to shield the existing cable, though maybe you could get a replacement one which is better-made. Hard to be sure the replacement is actually going to be better, of course. One thing you could try, if you're brave enough, is to get the mic out of the handset and wire it to the audio adaptor with very short wires, just to check that's actually an improvement.

Mixing and matching the "audio tweaks" with DD4WH's code is possible, but non-trivial. I had hoped the original author would accept his outstanding pull requests and give some support to his invention, but it doesn't look likely at this point. Without that, I'm not massively motivated to spend any of my limited bandwidth sorting out a "Best Of" feature set, I'm afraid.

EDIT: just taken a closer look, and it looks as if the noise has a period of 1024 samples, or 8 audio blocks, or 2 SD card sectors. So it may be something internal to the Teensy / audio adaptor assembly, not the mic cable screening.
 
Last edited:
Yup, that sounds awful all right! But at least we're vaguely in control of the levels now.

EDIT: just taken a closer look, and it looks as if the noise has a period of 1024 samples, or 8 audio blocks, or 2 SD card sectors. So it may be something internal to the Teensy / audio adaptor assembly, not the mic cable screening.

It turns out that there is a magic gain setting (at least in my case) of 43 dB -- above which the "noise" kicks in. At 42 dB I'm getting decent recordings but still has pretty weak amplitude. Maybe I need a better mic in the handset. I think one was mentioned in this forum...

I still can't run with the Audio Tweaks enabled -- it totally distorts the greeting.wav. Maybe there are some settings I can tweak but it's not clear that the Tweaks are needed...
- V
 
It turns out that there is a magic gain setting (at least in my case) of 43 dB -- above which the "noise" kicks in. At 42 dB I'm getting decent recordings but still has pretty weak amplitude. Maybe I need a better mic in the handset. I think one was mentioned in this forum...

I still can't run with the Audio Tweaks enabled -- it totally distorts the greeting.wav. Maybe there are some settings I can tweak but it's not clear that the Tweaks are needed...
- V

That's very interesting (or strange, if you prefer...). There seem to be some fixed-gain stages in the SGTL5000 mic amplifier, of which +40dB has the highest gain, so it may be the variable-gain stage is causing most of the issues. A more sensitive mic should definitely help, though finding one may be a bit of a minefield. You could add an AudioAmplifier object between the i2s2 and queue1 objects, and set its gain to over 1.0. Obviously it wouldn't be ideal, but it might serve as a stop-gap until you can get a better mic.

As I noted in post #17, merging the tweaks with DD4WH's code is not simple; you have to change the IDE menu, select the settings, install the revised playback objects, and change the code to use them. Looking at the last code you posted in #6, you haven't done the last of those tweaks, so it doesn't matter whether or not you've done the third, and if you only did the first two it will likely cause mayhem (sounds like it has...). You may not need the tweaks anyway, if your SD card is fast enough.
 
As I noted in post #17, merging the tweaks with DD4WH's code is not simple; you have to change the IDE menu, select the settings, install the revised playback objects, and change the code to use them. Looking at the last code you posted in #6, you haven't done the last of those tweaks, ...

What tells you that I didn't do the last of the tweaks? (in #6 above)
Is it that I didn't use AudioPlaySdWavX ?

And as a side note, the README from playfultechnology states "DO NOT copy the play_wav_sd.cpp and .h files to your Audio library" -- pretty sure it should be "play_sd_wav.cpp and .h". Or maybe I'm more confused than usual ...

UPDATE: Just saw the "do not use" disclaimer by DDWH. Sounds like this involves the August 2022 changes.
 
Last edited:
What tells you that I didn't do the last of the tweaks? (in #6 above)
Is it that I didn't use AudioPlaySdWavX ?
That's right :)

And as a side note, the README from playfultechnology states "DO NOT copy the play_wav_sd.cpp and .h files to your Audio library" -- pretty sure it should be "play_sd_wav.cpp and .h". Or maybe I'm more confused than usual ...
You're quite right. I've corrected my fork, which also updates the pull request that's been pending since 3rd February...

UPDATE: Just saw the "do not use" disclaimer by DDWH. Sounds like this involves the August 2022 changes.
Yes. I think a lot of his changes are very useful, so the "do not use" is perhaps a bit of an extreme reaction - much better just to correct the calculations of the WAV chunk sizes. But it's his fork, he can do with it as he pleases.
 
Back to original sketch now from playfultechnology per the DDWH disclaimer (whose changes were really good ideas). It's running with my mic gain set to 37 dB. The greeting.wav is pretty loud/distorted so I need to figure that out. The recordings have a sort of white noise background...
 
Any thoughts re my comment above? Search for '705' above.

705kbps is correct for 16 bit 44100SPS. 44100 * 16 = 705600

In communications its always measured in either bits per second or baud (symbols per second). So bps means bits per second.
 
Hey guys,

I'm new to this forum, so if there is anything wrong - please let me know.
My english is not the best (especially when using techincal language)..

Last year I already built a phone which is working quite okay (first time "programming" and building something like this phone).
Now I wanted to make a second phone but have some issues with this one.

I have the same issue with my phone - not having sound when playing the wav file on my computer.
I changed the dB and tried different once as well and got some sound but not my recorded voice. It is more rushing(?) than anything else..
Even using the same code as used with my first phone does not help.
Do you have any ideas?

Thank you in advance

#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 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");
print_mode();
// Configure the input pins
pinMode(HOOK_PIN, INPUT_PULLUP);
pinMode(PLAYBACK_BUTTON_PIN, INPUT_PULLUP);

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

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

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

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

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

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

// Add SD Card
// MTP.addFilesystem(SD, "SD Card");
MTP.addFilesystem(SD, "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(5); // much lower gain is required for the AOM5024 electret capsule

// Synchronise the Time object used in the program code with the RTC time provider.
// See https://github.com/PaulStoffregen/Time
setSyncProvider(getTeensy3Time);

// Define a callback that will assign the correct datetime for any file system operations
// (i.e. saving a new audio recording onto the SD card)
FsDateTime::setCallback(dateTime);

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

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

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

case Mode::prompting:
// Wait a second for users to put the handset to their ear
wait(1000);
// Play the greeting inviting them to record their message
playWav1.play("greeting.wav");
// Wait until the message has finished playing
// while (playWav1.isPlaying()) {
while (!playWav1.isStopped()) {
// Check whether the handset is replaced
buttonRecord.update();
buttonPlay.update();
// Handset is replaced
if(buttonRecord.risingEdge()) {
playWav1.stop();
mode = Mode::Ready; print_mode();
return;
}
if(buttonPlay.fallingEdge()) {
playWav1.stop();
//playAllRecordings();
playLastRecording();
return;
}

}
// Debug message
Serial.println("Starting Recording");
// Play the tone sound effect
waveform1.begin(beep_volume, 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
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;
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");
}
 

Attachments

  • voice message.jpg
    voice message.jpg
    74.8 KB · Views: 10
Back
Top