Coding with ChatGPT!

I’ve been working on code for a couple of Teensy Micro Controllers. One board records. The other plays back.

I’m very close to having the desired functionality. But I’m getting down to some tweaks. I’ve been going back and forth with ChatGPT during the whole process. Here’s an example exchange.

Player, Teensy 4.0… Photobombed by Mortimer
Recorder, Teensy 4.

Me: Works great. can the record LED respond to peaks instead of just be on when recording?

ChatGPT:

Sure, you can modify the code so that the recording LED (LED_PIN_1) responds to audio peaks instead of just staying on when recording. This can be done by analyzing the audio buffer data for peaks and toggling the LED accordingly.

cppCopy code#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=168,145
AudioRecordQueue         queue1;         //xy=360,62
AudioRecordQueue         queue2;         //xy=389,145
AudioConnection          patchCord1(i2s1, 0, queue1, 0);
AudioConnection          patchCord2(i2s1, 1, queue2, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
// GUItool: end automatically generated code

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    BUILTIN_SDCARD
#define SDCARD_MOSI_PIN  11  // not actually used
#define SDCARD_SCK_PIN   13  // not actually used

#define LED_PIN_1 9  // LED indicating recording state
#define LED_PIN_2 5  // LED indicating standby state (flashing)

// Define the button pins
#define BUTTON_RECORD 0
#define BUTTON_STOP 1
#define BUTTON_INCREMENT 2
#define BUTTON_ERASE 3

// Define the toggle switch pin
#define TOGGLE_SWITCH_PIN 4

Bounce button0 = Bounce(BUTTON_RECORD, 15);  // Record
Bounce button1 = Bounce(BUTTON_STOP, 15);    // Stop
Bounce button2 = Bounce(BUTTON_INCREMENT, 15);  // Increment file name
Bounce button3 = Bounce(BUTTON_ERASE, 15);   // Erase
Bounce toggleSwitch = Bounce(TOGGLE_SWITCH_PIN, 15); // Toggle switch

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording

// The file where data is recorded
File frec;
int fileNumber = 1;  // Start with RECORD1.WAV

elapsedMillis blinkTime;

void setup() {
  Serial.begin(9600);
  
  // record queue uses this memory to buffer incoming audio.
  AudioMemory(120);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
  sgtl5000_1.volume(0.5);

  // Configure headphone and lineout settings
  sgtl5000_1.lineOutLevel(13);  // Adjust line out level if needed
  sgtl5000_1.dacVolume(0.5, 0.5); // Set initial DAC volumes
  sgtl5000_1.headphoneSelect(AUDIO_HEADPHONE_DAC); // headphones monitor DAC output

  // 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);
    }
  }
  
  pinMode(LED_PIN_1, OUTPUT);
  pinMode(LED_PIN_2, OUTPUT);
  pinMode(BUTTON_RECORD, INPUT_PULLUP);
  pinMode(BUTTON_STOP, INPUT_PULLUP);
  pinMode(BUTTON_INCREMENT, INPUT_PULLUP);
  pinMode(BUTTON_ERASE, INPUT_PULLUP);
  pinMode(TOGGLE_SWITCH_PIN, INPUT_PULLUP); // Toggle switch for input selection

  digitalWrite(LED_PIN_2, HIGH);  // Turn on standby LED

  delay(1000);
}

void loop() {
  button0.update();
  button1.update();
  button2.update();
  button3.update();
  toggleSwitch.update(); // Update the toggle switch state

  if (button0.fallingEdge()) {
    startRecording();
  }
  
  if (button1.fallingEdge()) {
    stopAll();
  }
  
  if (button2.fallingEdge()) {
    incrementFileName();
  }

  if (button3.fallingEdge()) {
    eraseWavRecordings();
  }
  if (mode == 1) {
    continueRecording();
  } else {
    // Blink LED_PIN_2 to indicate standby mode
    if (blinkTime >= 500) {
      digitalWrite(LED_PIN_2, !digitalRead(LED_PIN_2)); // Toggle LED state
      blinkTime = 0; // Reset blink timer
    }
  }

  // Check the toggle switch to select the input
  if (toggleSwitch.fallingEdge() || toggleSwitch.risingEdge()) {
    if (digitalRead(TOGGLE_SWITCH_PIN) == LOW) {
      sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
      Serial.println("Switched to MIC input");
    } else {
      sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
      Serial.println("Switched to LINE input");
    }
  }

  // Update volumes
  float recordVolume = analogRead(A0) / 1023.0;
  float headphoneVolume = analogRead(A2) / 1023.0;
  // Serial.print("Record Volume: "); Serial.println(recordVolume);
  // Serial.print("Headphone Volume: "); Serial.println(headphoneVolume);
  sgtl5000_1.lineInLevel(recordVolume * 15); // scale to appropriate range (0-15)
  sgtl5000_1.volume(headphoneVolume); // scale to appropriate range (0-1)
  sgtl5000_1.dacVolume(headphoneVolume, headphoneVolume); // update DAC volumes

  // Update mic gain if MIC input is selected
  if (digitalRead(TOGGLE_SWITCH_PIN) == LOW) {
    float micGain = analogRead(A1) / 1023.0 * 63.0; // scale to appropriate range (0-63)
    sgtl5000_1.micGain(micGain);
  }
}

void startRecording() {
  Serial.println("StartRecording");
  char fileName[13];
  snprintf(fileName, sizeof(fileName), "RECORD%d.WAV", fileNumber);
  if (SD.exists(fileName)) {
    SD.remove(fileName);
  }
  frec = SD.open(fileName, FILE_WRITE);
  if (frec) {
    Serial.print("File Open: ");
    Serial.println(fileName);

    // Reserve space for the header
    frec.seek(44);

    queue1.begin();
    queue2.begin();
    mode = 1;
    digitalWrite(LED_PIN_1, HIGH);  // Turn on recording LED
    digitalWrite(LED_PIN_2, LOW);   // Turn off standby LED
  }
}

void continueRecording() {
  if (queue1.available() >= 2 && queue2.available() >= 2) {
    byte buffer[512];
    byte bufferL[256];
    byte bufferR[256];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    int b = 0;
    int peakL = 0;  // To store peak value of left channel
    int peakR = 0;  // To store peak value of right channel
    for (int i = 0; i < 512; i += 4) {
      buffer[i] = bufferL[b];
      buffer[i + 1] = bufferL[b + 1];
      buffer[i + 2] = bufferR[b];
      buffer[i + 3] = bufferR[b + 1];
      b += 2;

      // Check for peak values
      int16_t valL = (bufferL[b + 1] << 8) | bufferL[b];
      int16_t valR = (bufferR[b + 1] << 8) | bufferR[b];
      if (abs(valL) > peakL) peakL = abs(valL);
      if (abs(valR) > peakR) peakR = abs(valR);
    }
    frec.write(buffer, 512);

    // Adjust LED based on peak values
    if (peakL > 5000 || peakR > 5000) {
      digitalWrite(LED_PIN_1, HIGH);  // Turn on recording LED if peak is detected
    } else {
      digitalWrite(LED_PIN_1, LOW);   // Turn off recording LED if no peak
    }
  }
}

void stopRecording() {
  Serial.println("StopRecording");
  queue1.end();
  queue2.end();
  // flush buffer
  while (queue1.available() > 0 && queue2.available() > 0) {
    queue1.readBuffer();
    queue1.freeBuffer();
    queue2.readBuffer();
    queue2.freeBuffer();
  }
  uint32_t dataSize = frec.size() - 44;
  frec.seek(0);
  writeWavHeader(frec, 44100, 16, 2, dataSize);
  frec.close();
  mode = 0;
  digitalWrite(LED_PIN_1, LOW);  // Turn off recording LED
  digitalWrite(LED_PIN_2, HIGH); // Turn on standby LED
}

void stopAll() {
  if (mode == 1) {
    stopRecording();
  }
  Serial.println("StopAll");
}

void incrementFileName() {
  fileNumber++;
  Serial.print("Incrementing file number to: ");
  Serial.println(fileNumber);
}

void eraseWavRecordings() {
  // Open the root directory
  File root = SD.open("/");
  
  // Delete all .WAV files
  deleteWavFiles(root);

  // Close the root directory
  root.close();

  Serial.println("All .WAV recordings erased");
}

void deleteWavFiles(File dir) {
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) {
      // No more files
      break;
    }
    if (entry.isDirectory()) {
      deleteWavFiles(entry);  // Recursively delete directory contents
    } else {
      String fileName = entry.name();
      if (fileName.endsWith(".WAV")) {
        SD.remove(entry.name()); // Delete the file
        Serial.print("Deleted: ");
        Serial.println(fileName);
      }
    }
    entry.close();
  }
}

void writeWavHeader(File& file, uint32_t sampleRate, uint16_t bitsPerSample, uint16_t channels, uint32_t dataSize) {
  uint32_t byteRate = sampleRate * channels * (bitsPerSample / 8);
  uint16_t blockAlign = channels * (bitsPerSample / 8);

  file.write("RIFF");
  file.write((dataSize + 36) & 0xff);
  file.write(((dataSize + 36) >> 8) & 0xff);
  file.write(((dataSize + 36) >> 16) & 0xff);
  file.write(((dataSize + 36) >> 24) & 0xff);
  file.write("WAVE");
  file.write("fmt ");
  file.write(16 & 0xff);
  file.write((16 >> 8) & 0xff);
  file.write((16 >> 16) & 0xff);
  file.write((16 >> 24) & 0xff);
  file.write(1 & 0xff);
  file.write((1 >> 8) & 0xff);
  file.write(channels & 0xff);
  file.write((channels >> 8) & 0xff);
  file.write(sampleRate & 0xff);
  file.write((sampleRate >> 8) & 0xff);
  file.write((sampleRate >> 16) & 0xff);
  file.write((sampleRate >> 24) & 0xff);
  file.write(byteRate & 0xff);
  file.write((byteRate >> 8) & 0xff);
  file.write((byteRate >> 16) & 0xff);
  file.write((byteRate >> 24) & 0xff);
  file.write(blockAlign & 0xff);
  file.write((blockAlign >> 8) & 0xff);
  file.write(bitsPerSample & 0xff);
  file.write((bitsPerSample >> 8) & 0xff);
  file.write("data");
  file.write(dataSize & 0xff);
  file.write((dataSize >> 8) & 0xff);
  file.write((dataSize >> 16) & 0xff);
  file.write((dataSize >> 24) & 0xff);
}

Here’s a modified version of your code to achieve this:

Changes made:

  1. Modified continueRecording function: Added peak detection by analyzing the buffer data.
  2. Updated LED control: Turn the LED on if a peak is detected and off if no peak is found.

This will make the recording LED respond to audio peaks, creating a visual indication of the audio levels being recorded.

Me:

gives this error message: /Users/kenbeck/Documents/Arduino/Teensy41_WAV-recorder_PeakReadingLED/Teensy41_WAV-recorder_PeakReadingLED.ino: In function ‘void continueRecording()’: /Users/kenbeck/Documents/Arduino/Teensy41_WAV-recorder_PeakReadingLED/Teensy41_WAV-recorder_PeakReadingLED.ino:191:36: warning: iteration 127 invokes undefined behavior [-Waggressive-loop-optimizations] 191 | int16_t valL = (bufferL[b + 1] << 8) | bufferL[b]; | ~~~~~~~~~~~~~^ /Users/kenbeck/Documents/Arduino/Teensy41_WAV-recorder_PeakReadingLED/Teensy41_WAV-recorder_PeakReadingLED.ino:183:23: note: within this loop 183 | for (int i = 0; i < 512; i += 4) { | ~~^~~~~

ChatGPT:

The error message you’re seeing indicates a problem with accessing out-of-bounds elements in the arrays bufferL and bufferR. This typically happens at the last iteration of the loop when b is incremented beyond the valid index range for the arrays.

Here’s the corrected code to ensure the array bounds are not violated:

cppCopy code#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
#include <Bounce.h>

// GUItool: begin automatically generated code
AudioInputI2S            i2s1;           //xy=168,145
AudioRecordQueue         queue1;         //xy=360,62
AudioRecordQueue         queue2;         //xy=389,145
AudioConnection          patchCord1(i2s1, 0, queue1, 0);
AudioConnection          patchCord2(i2s1, 1, queue2, 0);
AudioControlSGTL5000     sgtl5000_1;     //xy=265,212
// GUItool: end automatically generated code

// Use these with the Teensy Audio Shield
#define SDCARD_CS_PIN    BUILTIN_SDCARD
#define SDCARD_MOSI_PIN  11  // not actually used
#define SDCARD_SCK_PIN   13  // not actually used

#define LED_PIN_1 9  // LED indicating recording state
#define LED_PIN_2 5  // LED indicating standby state (flashing)

// Define the button pins
#define BUTTON_RECORD 0
#define BUTTON_STOP 1
#define BUTTON_INCREMENT 2
#define BUTTON_ERASE 3

// Define the toggle switch pin
#define TOGGLE_SWITCH_PIN 4

Bounce button0 = Bounce(BUTTON_RECORD, 15);  // Record
Bounce button1 = Bounce(BUTTON_STOP, 15);    // Stop
Bounce button2 = Bounce(BUTTON_INCREMENT, 15);  // Increment file name
Bounce button3 = Bounce(BUTTON_ERASE, 15);   // Erase
Bounce toggleSwitch = Bounce(TOGGLE_SWITCH_PIN, 15); // Toggle switch

// Remember which mode we're doing
int mode = 0;  // 0=stopped, 1=recording

// The file where data is recorded
File frec;
int fileNumber = 1;  // Start with RECORD1.WAV

elapsedMillis blinkTime;

void setup() {
  Serial.begin(9600);
  
  // record queue uses this memory to buffer incoming audio.
  AudioMemory(120);

  // Enable the audio shield, select input, and enable output
  sgtl5000_1.enable();
  sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
  sgtl5000_1.volume(0.5);

  // Configure headphone and lineout settings
  sgtl5000_1.lineOutLevel(13);  // Adjust line out level if needed
  sgtl5000_1.dacVolume(0.5, 0.5); // Set initial DAC volumes
  sgtl5000_1.headphoneSelect(AUDIO_HEADPHONE_DAC); // headphones monitor DAC output

  // 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);
    }
  }
  
  pinMode(LED_PIN_1, OUTPUT);
  pinMode(LED_PIN_2, OUTPUT);
  pinMode(BUTTON_RECORD, INPUT_PULLUP);
  pinMode(BUTTON_STOP, INPUT_PULLUP);
  pinMode(BUTTON_INCREMENT, INPUT_PULLUP);
  pinMode(BUTTON_ERASE, INPUT_PULLUP);
  pinMode(TOGGLE_SWITCH_PIN, INPUT_PULLUP); // Toggle switch for input selection

  digitalWrite(LED_PIN_2, HIGH);  // Turn on standby LED

  delay(1000);
}

void loop() {
  button0.update();
  button1.update();
  button2.update();
  button3.update();
  toggleSwitch.update(); // Update the toggle switch state

  if (button0.fallingEdge()) {
    startRecording();
  }
  
  if (button1.fallingEdge()) {
    stopAll();
  }
  
  if (button2.fallingEdge()) {
    incrementFileName();
  }

  if (button3.fallingEdge()) {
    eraseWavRecordings();
  }
  if (mode == 1) {
    continueRecording();
  } else {
    // Blink LED_PIN_2 to indicate standby mode
    if (blinkTime >= 500) {
      digitalWrite(LED_PIN_2, !digitalRead(LED_PIN_2)); // Toggle LED state
      blinkTime = 0; // Reset blink timer
    }
  }

  // Check the toggle switch to select the input
  if (toggleSwitch.fallingEdge() || toggleSwitch.risingEdge()) {
    if (digitalRead(TOGGLE_SWITCH_PIN) == LOW) {
      sgtl5000_1.inputSelect(AUDIO_INPUT_MIC);
      Serial.println("Switched to MIC input");
    } else {
      sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN);
      Serial.println("Switched to LINE input");
    }
  }

  // Update volumes
  float recordVolume = analogRead(A0) / 1023.0;
  float headphoneVolume = analogRead(A2) / 1023.0;
  // Serial.print("Record Volume: "); Serial.println(recordVolume);
  // Serial.print("Headphone Volume: "); Serial.println(headphoneVolume);
  sgtl5000_1.lineInLevel(recordVolume * 15); // scale to appropriate range (0-15)
  sgtl5000_1.volume(headphoneVolume); // scale to appropriate range (0-1)
  sgtl5000_1.dacVolume(headphoneVolume, headphoneVolume); // update DAC volumes

  // Update mic gain if MIC input is selected
  if (digitalRead(TOGGLE_SWITCH_PIN) == LOW) {
    float micGain = analogRead(A1) / 1023.0 * 63.0; // scale to appropriate range (0-63)
    sgtl5000_1.micGain(micGain);
  }
}

void startRecording() {
  Serial.println("StartRecording");
  char fileName[13];
  snprintf(fileName, sizeof(fileName), "RECORD%d.WAV", fileNumber);
  if (SD.exists(fileName)) {
    SD.remove(fileName);
  }
  frec = SD.open(fileName, FILE_WRITE);
  if (frec) {
    Serial.print("File Open: ");
    Serial.println(fileName);

    // Reserve space for the header
    frec.seek(44);

    queue1.begin();
    queue2.begin();
    mode = 1;
    digitalWrite(LED_PIN_1, HIGH);  // Turn on recording LED
    digitalWrite(LED_PIN_2, LOW);   // Turn off standby LED
  }
}

void continueRecording() {
  if (queue1.available() >= 2 && queue2.available() >= 2) {
    byte buffer[512];
    byte bufferL[256];
    byte bufferR[256];
    memcpy(bufferL, queue1.readBuffer(), 256);
    memcpy(bufferR, queue2.readBuffer(), 256);
    queue1.freeBuffer();
    queue2.freeBuffer();
    int b = 0;
    int peakL = 0;  // To store peak value of left channel
    int peakR = 0;  // To store peak value of right channel
    for (int i = 0; i < 512; i += 4) {
      buffer[i] = bufferL[b];
      buffer[i + 1] = bufferL[b + 1];
      buffer[i + 2] = bufferR[b];
      buffer[i + 3] = bufferR[b + 1];

      // Check for peak values
      int16_t valL = (bufferL[b + 1] << 8) | bufferL[b];
      int16_t valR = (bufferR[b + 1] << 8) | bufferR[b];
      if (abs(valL) > peakL) peakL = abs(valL);
      if (abs(valR) > peakR) peakR = abs(valR);

      b += 2;
    }
    frec.write(buffer, 512);

    // Adjust LED based on peak values
    if (peakL > 5000 || peakR > 5000) {
      digitalWrite(LED_PIN_1, HIGH);  // Turn on recording LED if peak is detected
    } else {
      digitalWrite(LED_PIN_1, LOW);   // Turn off recording LED if no peak
    }
  }
}

void stopRecording() {
  Serial.println("StopRecording");
  queue1.end();
  queue2.end();
  // flush buffer
  while (queue1.available() > 0 && queue2.available() > 0) {
    queue1.readBuffer();
    queue1.freeBuffer();
    queue2.readBuffer();
    queue2.freeBuffer();
  }
  uint32_t dataSize = frec.size() - 44;
  frec.seek(0);
  writeWavHeader(frec, 44100, 16, 2, dataSize);
  frec.close();
  mode = 0;
  digitalWrite(LED_PIN_1, LOW);  // Turn off recording LED
  digitalWrite(LED_PIN_2, HIGH); // Turn on standby LED
}

void stopAll() {
  if (mode == 1) {
    stopRecording();
  }
  Serial.println("StopAll");
}

void incrementFileName() {
  fileNumber++;
  Serial.print("Incrementing file number to: ");
  Serial.println(fileNumber);
}

void eraseWavRecordings() {
  // Open the root directory
  File root = SD.open("/");
  
  // Delete all .WAV files
  deleteWavFiles(root);

  // Close the root directory
  root.close();

  Serial.println("All .WAV recordings erased");
}

void deleteWavFiles(File dir) {
  while (true) {
    File entry = dir.openNextFile();
    if (!entry) {
      // No more files
      break;
    }
    if (entry.isDirectory()) {
      deleteWavFiles(entry);  // Recursively delete directory contents
    } else {
      String fileName = entry.name();
      if (fileName.endsWith(".WAV")) {
        SD.remove(entry.name()); // Delete the file
        Serial.print("Deleted: ");
        Serial.println(fileName);
      }
    }
    entry.close();
  }
}

void writeWavHeader(File& file, uint32_t sampleRate, uint16_t bitsPerSample, uint16_t channels, uint32_t dataSize) {
  uint32_t byteRate = sampleRate * channels * (bitsPerSample / 8);
  uint16_t blockAlign = channels * (bitsPerSample / 8);

  file.write("RIFF");
  file.write((dataSize + 36) & 0xff);
  file.write(((dataSize + 36) >> 8) & 0xff);
  file.write(((dataSize + 36) >> 16) & 0xff);
  file.write(((dataSize + 36) >> 24) & 0xff);
  file.write("WAVE");
  file.write("fmt ");
  file.write(16 & 0xff);
  file.write((16 >> 8) & 0xff);
  file.write((16 >> 16) & 0xff);
  file.write((16 >> 24) & 0xff);
  file.write(1 & 0xff);
  file.write((1 >> 8) & 0xff);
  file.write(channels & 0xff);
  file.write((channels >> 8) & 0xff);
  file.write(sampleRate & 0xff);
  file.write((sampleRate >> 8) & 0xff);
  file.write((sampleRate >> 16) & 0xff);
  file.write((sampleRate >> 24) & 0xff);
  file.write(byteRate & 0xff);
  file.write((byteRate >> 8) & 0xff);
  file.write((byteRate >> 16) & 0xff);
  file.write((byteRate >> 24) & 0xff);
  file.write(blockAlign & 0xff);
  file.write((blockAlign >> 8) & 0xff);
  file.write(bitsPerSample & 0xff);
  file.write((bitsPerSample >> 8) & 0xff);
  file.write("data");
  file.write(dataSize & 0xff);
  file.write((dataSize >> 8) & 0xff);
  file.write((dataSize >> 16) & 0xff);
  file.write((dataSize >> 24) & 0xff);
}

Changes made:

  1. Adjusted the loop condition in continueRecording: Ensured that b does not exceed the valid index range for bufferL and bufferR.

This should prevent the undefined behavior and ensure the peak detection works correctly without accessing out-of-bounds array elements.

Me:

looks good. I’ll test and be back…

ChatGPT:

Great! Give it a test and let me know how it goes. If you encounter any issues or have further questions, I’m here to help. Happy coding! 🎵

GPT