// Record sound as a stereo WAV file data to an SD card, and play it back.
// (Note: modified by D.L. June, 2023 to record a sound file while simultaneously playing back a sound file.)
//
// Requires the audio shield:
// http://www.pjrc.com/store/teensy3_audio.html
//
// Three pushbuttons need to be connected:
// Record Button: pin 0 to GND
// Stop Button: pin 1 to GND
// Play Button: pin 2 to GND
//
// This example code is in the public domain.
#include <Bounce.h>
#include <Audio.h>
// GUItool: begin automatically generated code
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// GUItool: begin automatically generated code
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// GUItool: begin automatically generated code
#include <Audio.h>
#include <Wire.h>
#include <SPI.h>
#include <SD.h>
#include <SerialFlash.h>
// GUItool: begin automatically generated code
AudioInputI2S i2sIn; //xy=304,319
AudioMixer4 mixer3; //xy=585,250
AudioAnalyzePeak peakR; //xy=606,161
AudioAnalyzePeak peakL; //xy=613,115
AudioPlayWAVstereo playWAVstereo2; //xy=626,491
AudioPlayWAVstereo playWAVstereo1; //xy=629,422
AudioRecordWAVmono recordWAVmono1; //xy=772,214
AudioMixer4 mixer1; //xy=869,310
AudioMixer4 mixer2; //xy=873,408
AudioOutputI2S i2sOut; //xy=1029,353
AudioConnection patchCord1(i2sIn, 0, peakL, 0);
AudioConnection patchCord2(i2sIn, 0, mixer1, 0);
AudioConnection patchCord3(i2sIn, 0, mixer3, 0);
AudioConnection patchCord4(i2sIn, 1, peakR, 0);
AudioConnection patchCord5(i2sIn, 1, mixer2, 0);
AudioConnection patchCord6(i2sIn, 1, mixer3, 1);
AudioConnection patchCord7(mixer3, recordWAVmono1);
AudioConnection patchCord8(playWAVstereo2, 0, mixer1, 2);
AudioConnection patchCord9(playWAVstereo2, 1, mixer2, 2);
AudioConnection patchCord10(playWAVstereo1, 0, mixer1, 1);
AudioConnection patchCord11(playWAVstereo1, 1, mixer2, 1);
AudioConnection patchCord12(mixer1, 0, i2sOut, 0);
AudioConnection patchCord13(mixer2, 0, i2sOut, 1);
AudioControlSGTL5000 control; //xy=406,489
// GUItool: end automatically generated code
// Bounce objects to easily and reliably read the buttons
Bounce buttonRecord = Bounce(0, 8);
Bounce buttonStop = Bounce(1, 8); // 8 = 8 ms debounce time
Bounce buttonPlay = Bounce(2, 8);
Bounce buttonFileInfo = Bounce(3, 8);
// which input on the audio shield will be used?
const int myInput = AUDIO_INPUT_LINEIN;
//const int myInput = AUDIO_INPUT_MIC;
// Use these with the Teensy Audio Shield
/*
#define SDCARD_CS_PIN 10
#define SDCARD_MOSI_PIN 7
#define SDCARD_SCK_PIN 14
//*/
// Use these with the Teensy 3.5, 3.6 and 4.1 SD card
//*
#define SDCARD_CS_PIN BUILTIN_SDCARD
#define SDCARD_MOSI_PIN 11 // not actually used
#define SDCARD_SCK_PIN 13 // not actually used
//*/
// Use these for the SD+Wiz820 or other adaptors
/*
#define SDCARD_CS_PIN 4
#define SDCARD_MOSI_PIN 11
#define SDCARD_SCK_PIN 13
//*/
void findSGTL5000(AudioControlSGTL5000& sgtl5000_1)
{
// search for SGTL5000 at both I²C addresses
for (int i=0;i<2;i++)
{
uint8_t levels[] {LOW,HIGH};
sgtl5000_1.setAddress(levels[i]);
sgtl5000_1.enable();
if (sgtl5000_1.volume(0.2))
{
Serial.printf("SGTL5000 found at %s address\n",i?"HIGH":"LOW");
break;
}
}
}
// Remember which mode we're doing
int mode = 0; // 0=stopped, 1=recording, 2=playing
const char* recFile = "newtrack.wav";
const char* backingFile = "EchosMX.wav";
float volume;
boolean monitorOn = false;
void setup() {
// Configure the pushbutton pins
pinMode(0, INPUT_PULLUP);
pinMode(1, INPUT_PULLUP);
pinMode(2, INPUT_PULLUP);
pinMode(3, INPUT_PULLUP);
Serial.begin(9600);
delay(500);
// Audio connections require memory:
AudioMemory(20); // started at 16 I think. Then 20,
// SD audio objects need buffers configuring:
AudioBuffer::bufType bufMem = AudioBuffer::inHeap;
size_t sz = 4096;
Serial.print("Play Buf1 ret val: ");
Serial.println(playWAVstereo1.createBuffer(8*sz,bufMem));
Serial.print("Rec Buf ret val: ");
Serial.println(recordWAVmono1.createBuffer(16*sz,bufMem));
Serial.print("Play Buf2 ret val: ");
Serial.println(playWAVstereo2.createBuffer(8*sz,bufMem));
// setup mixer gains
mixer1.gain(0,0.3); // input (mic/line) channes
mixer1.gain(1,0.3);
mixer1.gain(2,.5); // playback channels
mixer2.gain(0,0.3); // input (mic/line) channes
mixer2.gain(1,0.3);
mixer2.gain(2,.5); // playback channels
findSGTL5000(control);
// Enable the audio shield, select input, and enable output
control.inputSelect(myInput);
control.lineInLevel(5);
control.volume(0);
control.enable(); // do these last so no speaker thump!
// Initialize the SD card
SPI.setMOSI(SDCARD_MOSI_PIN);
SPI.setSCK(SDCARD_SCK_PIN);
while (!(SD.begin(SDCARD_CS_PIN)))
{
// loop here if no SD card, printing a message
Serial.println("Unable to access the SD card");
delay(500);
}
}
void loop() {
if(!monitorOn)
{
monitorOn = true;
control.volume(1); // do it here so no thump
}
// First, read the buttons
buttonRecord.update();
buttonStop.update();
buttonPlay.update();
buttonFileInfo.update();
// Respond to button presses
if (buttonRecord.fallingEdge()) {
Serial.println("Record Button Press");
if (mode == 2) stopPlaying();
if (mode == 0) startRecording();
}
if (buttonStop.fallingEdge()) {
Serial.println("Stop Button Press");
/*if (mode == 1)*/ stopRecording();
/*if (mode == 2)*/ stopPlaying();
}
if (buttonPlay.fallingEdge()) {
Serial.println("Play Button Press");
if (mode == 1) stopRecording();
if (mode == 0) startPlaying();
}
// If we're playing or recording, carry on...
if (mode == 1) {
continueRecording();
}
if (mode == 2) {
continuePlaying();
}
//showLevels();
// when using a microphone, continuously adjust gain
if (myInput == AUDIO_INPUT_MIC) adjustMicLevel();
// Volume control
// uncomment if you have a volume pot soldered to your audio shield
/*
int n = analogRead(A0);
if (n != volume) {
volume = n;
control.volume((float)n / 1023);
}
*/
// show buffer read/write data
static uint32_t next = 0;
if (millis() >= next)
{
next = millis() + 250;
if (playWAVstereo1.isPlaying())
printPlayInstrument(playWAVstereo1,"PlayWAV1");
if (playWAVstereo2.isPlaying())
printPlayInstrument(playWAVstereo2,"PlayWAV2");
if(recordWAVmono1.isRecording())
printRecInstrument(recordWAVmono1, "RecWAV1");
}
// list file info on button press
/*if(buttonFileInfo.fallingEdge()) {
}*/
yield();
} // end loop()
void startRecording()
{
// uint32_t start1, start2;
Serial.println("startRecording");
//start1 = millis();
playWAVstereo2.playSD(backingFile);
//start2 = millis();
recordWAVmono1.recordSD(recFile);
//Serial.print("time diff: "); Serial.println(start2-start1);
mode = 1;
}
void continueRecording()
{
// nothing to do here!
}
void stopRecording()
{
Serial.println("stopRecording");
recordWAVmono1.stop();
playWAVstereo2.stop();
mode = 0;
// mute input
}
void startPlaying()
{
Serial.println("startPlaying");
playWAVstereo1.cueSD(recFile);
playWAVstereo2.cueSD(backingFile);
playWAVstereo1.play();
playWAVstereo2.play();
mode = 2;
}
void continuePlaying()
{
if (!playWAVstereo1.isPlaying())
{
Serial.println(" playWAV1 - endOfPlayback");
mode = 0;
}
if(!playWAVstereo2.isPlaying())
{
Serial.println("playWAV2 - endOfPlayback");
mode = 0;
}
}
void stopPlaying()
{
Serial.println("stopPlaying");
playWAVstereo1.stop();
playWAVstereo2.stop();
mode = 0;
}
void adjustMicLevel()
{
// TODO: read the peak1 object and adjust control.micGain()
// if anyone gets this working, please submit a github pull request :-)
}
void printPlayInstrument(AudioPlayWAVstereo& o, const char* nam)
{
Serial.printf("%s: low-water: %u; worst read time: %uus; updates: %u\n",
nam,
o.bufferAvail.getLowest(),
o.readMicros.getHighest(),
o.bufferAvail.getUpdates());
o.bufferAvail.reset();
o.readMicros.reset();
}
void printRecInstrument(AudioRecordWAVmono& o, const char* nam)
{
Serial.printf("%s: low-water: %u; worst read time: %uus; updates: %u\n",
nam,
o.bufferAvail.getLowest(),
o.readMicros.getHighest(),
o.bufferAvail.getUpdates());
o.bufferAvail.reset();
o.readMicros.reset();
}
void showLevels()
{
static uint32_t nextOutput;
if (millis() > nextOutput)
{
int lp = 0, rp = 0, scale = 20;
char cl='?',cr='?';
char buf[scale*2+10];
nextOutput = millis() + 1000;
if (peakL.available())
{
lp = peakL.read() * scale;
cl = '#';
}
if (peakR.available())
{
rp = peakR.read() * scale;
cr = '#';
}
for (int i=0;i<scale;i++)
{
buf[scale-i-1] = lp>=i?cl:' ';
buf[scale+i+1] = rp>=i?cr:' ';
buf[scale] = '|';
buf[scale*2+2] = 0;
}
Serial.println(buf);
}
}