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.
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:
- Modified
continueRecording
function: Added peak detection by analyzing the buffer data. - 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:
- Adjusted the loop condition in
continueRecording
: Ensured thatb
does not exceed the valid index range forbufferL
andbufferR
.
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! 🎵