Teensy 4.0 w/ 4.0 Audio Shield Audio.h Logic

Adafruit Ethernet, Motor, Proto, Wave, Datalogger, GPS Shields - etc!

Moderators: adafruit_support_bill, adafruit

Please be positive and constructive with your questions and comments.
Locked
User avatar
Mars102
 
Posts: 5
Joined: Tue Oct 18, 2022 6:41 pm

Teensy 4.0 w/ 4.0 Audio Shield Audio.h Logic

Post by Mars102 »

Can someone please help me debug this logic? I've been trying for a while lol I'm trying to include additional functionality of a BANNED rotary project utilizing a Teensy 4.0, and Teensy 4.0 music shield.

The original logic records after the handset is 'picked up' and a .wav file plays and then a beep, stops recording after 'hanging up', and then can be played back immediately by pressing a button while the phone is still 'hung up'. I partially implanted logic to read the rotary dial itself. I'd like the option to record by dialing "1-0-1" while the handset is up, playback the last message with "4-1-1" while the handset is up, and delete the last message by dialing "7-7-7" while the handset is up.

I figure a wait or delay of 5 seconds between dialed numbers before the phone should switch back to "Case: Prompt."

I'm also adding another button to re-record the greeting message after a 10 seconds of holding down said button. I'd imagine there'd be a statement in 'startRecording()' to overwrite the 'greeting.wav' audio file. I tried, I really have.

Code: Select all

/**
 **/

// 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 <Audio.h>
#include <Bounce2.h>
#include <Wire.h>
#include <SD.h>
#include <SPI.h>
#include <SerialFlash.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
// 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() {
  Serial.begin(9600);
  while (!Serial && millis() < 5000) {
    // wait for serial port to connect.
  }
  // 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.Was 50.
  AudioMemory(70);

  // 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.inputSelect(AUDIO_INPUT_MIC);
  //Below is increased from 0.5
  //Decrease if the speaker sounds distorted
  sgtl5000_1.volume(0.55); 

  // Play a beep to indicate system is online
  waveform1.begin(WAVEFORM_SINE);
  waveform1.frequency(440);
  waveform1.amplitude(0.5);
  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 - 65 max - was 15 - input volume
  sgtl5000_1.micGain(17);

  // 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 - activates when previously low value is high
      //Note: falllingEdge and risingEdge were switched b/c phone is on closed circuit
      //MAY NEED TO CHANGE FOR LIFT + DIAL to playback messages!!!!!
      if (buttonRecord.risingEdge()) {
        Serial.println("Handset lifted");
        mode = Mode::Prompting;
      }
      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 (SAVED ON SD CARD) inviting them to record their message
      //'Thanks for celebrating with us and taking the time to record a message'
      playWav1.play("greeting.wav");    
      // Wait until the  message has finished playing
      while (!playWav1.isStopped()) {
        // Check whether the handset is replaced
        buttonRecord.update();
        // Handset is replaced
        if(buttonRecord.fallingEdge()) {
          Serial.println("Hung up");
          playWav1.stop();
          mode = Mode::Ready;
          return;
        }
      }

      startRecording();
      break;

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

    case Mode::Playing:
      break;  
  }   
}

void startRecording() {
      // Debug message
      Serial.println("Started Recording");
      // Play the tone sound effect/TONE A/ "leave a message at the tone" to be included in message
      waveform1.frequency(440);
      waveform1.amplitude(0.7);//was .9
      wait(250);
      waveform1.amplitude(0);
      // Start the recording function
      
  // 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);
    //The '%50d' can be used to playback only the last message recorded!!!!
    // 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
      Serial.print("No more files to play! ");
      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.32);
      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();
        Serial.println("Stopped playback; resetting to Mode:Prompt");
        mode = Mode::Ready;
        return;
      }   
    }
  }
  // All files have been played
  Serial.print("Done Playing ");
  mode = Mode::Ready;
}

void playLastRecording() {
  // Find the first available file number
  Serial.println("Play last recording.");
  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.raw", 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.raw", idx);
      Serial.println(filename);
      playRaw1.play(filename);
      mode = Mode::Playing;
      while (playRaw1.isPlaying()) { // this works for playWav
      buttonPlay.update();
      buttonRecord.update();
      // Button is pressed again
      if(buttonPlay.fallingEdge() || buttonRecord.fallingEdge()) { //was "buttonPlay.fallingEdge()" 12/11/22
        playRaw1.stop();
        Serial.println("Stopped playback; resetting to Mode:Ready");
        mode = Mode::Ready;
        return;
      }   
    }
      // file has been played
        Serial.println("Done Playing ");
  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();
    if (buttonRecord.fallingEdge()) Serial.println("Button (pin 0) Press");
    if (buttonRecord.risingEdge()) Serial.println("Button (pin 0) Release");
  }
}

Locked
Please be positive and constructive with your questions and comments.

Return to “Arduino Shields from Adafruit”