I am working on some variations of the library examples, and I am bumping up against something that seems easiest to get to the bottom of here.
Below is my code, which I have modified from the TympanPDMMics.ino example sketch, which is working, YAY!
The ultimate goal is to be able to change the input audio source between all the sources, with the option to record audio from any input source.
- On-Board Mics
- External Mic
- Line In breakout
- Tympan Synthesized Audio (as done in the OutputTone.ino library example)
- Play WAV file from SD Card
- Record to SD from any of the above (except SD wav file play)
The thing I think I’m running into has to do with the AudioConnection_F32 patchcord variables. Is there a way to ‘disconnect’ and ‘reconnect’ the various patchcords?
Also, the sketch below has SD card record/stop record via serial keystrokes. Is there a better way to service the SD card (recording and playing) that can ‘turn off’ the SD card? Or otherwise not spend time servicing it? Or am I over-thinking things?
I am using latest Arduino v.2.3.8 with latest Teensy board files v1.6.0
Using a Tympan Rev F
/*
Created: Joel, March 2026
Based on TympanPDMMics.ino example sketch, Tympan Library. Eric, Sept 2019
Functionality
-Select from PCB mic, line in, or external mic using keystrokes
-Pass thru audio to headphone
-Record/Stop Record to SD card using keystrokes
*/
#include <Tympan_Library.h>
//definitions for SD writing
#define PRINT_OVERRUN_WARNING 1 //set to 1 to print a warning that the there's been a hiccup in the writing to the SD.
//set the sample rate and block size
const float sample_rate_Hz = 44100.0f ; //Allowed values: 8000, 11025, 16000, 22050, 24000, 32000, 44100, 44118, or 48000 Hz
const int audio_block_samples = 128; //do not make bigger than AUDIO_BLOCK_SAMPLES from AudioStream.h (which is 128)
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples);
//setup the Tympan using the default settings
Tympan myTympan(TympanRev::F, audio_settings); //do TympanRev::D or E or F
// Define audio objects
AudioInputI2S_F32 i2s_in(audio_settings); //This is the Teensy Audio library's built-in 4-channel I2S class
AudioMixer4_F32 mixerIn(audio_settings);
AudioSDWriter_F32 audioSDWriter(audio_settings); //this is stereo by default
AudioOutputI2S_F32 i2s_out(audio_settings); //This is the Teensy Audio library's built-in 4-channel I2S class
//AUDIO CONNECTIONS
//Connect Left & Right Input Channel to Left and Right SD card queue
AudioConnection_F32 patchcord1(i2s_in, 0, audioSDWriter, 0); //connect Raw audio to queue (to enable SD writing)
AudioConnection_F32 patchcord2(i2s_in, 1, audioSDWriter, 1); //connect Raw audio to queue (to enable SD writing)
//Connect Left & Right Input to Audio Output Left and Right
AudioConnection_F32 patchcord3(i2s_in, 0, i2s_out, 0); //echo audio to output
AudioConnection_F32 patchcord4(i2s_in, 1, i2s_out, 1); //echo audio to output
String overall_name = String("InputSelect.ino");
//control display and serial interaction
bool enable_printCPUandMemory = false;
void togglePrintMemoryAndCPU(void) { enable_printCPUandMemory = !enable_printCPUandMemory; };
void setPrintMemoryAndCPU(bool state) { enable_printCPUandMemory = state;};
bool enable_printAveSignalLevels = false, printAveSignalLevels_as_dBSPL = false;
void togglePrintAveSignalLevels(bool as_dBSPL) {
enable_printAveSignalLevels = !enable_printAveSignalLevels;
printAveSignalLevels_as_dBSPL = as_dBSPL;
};
//set the recording configuration
const int config_pcb_mics = 20;
const int config_mic_jack = 21;
const int config_line_in_SE = 22;
int current_config = 0;
float input_gain_dB = 0.0; //gain on the microphone
float vol_knob_gain_dB = 0.0; //speaker output gain
void setConfiguration(int config) {
switch (config) {
case config_pcb_mics:
//Select Input
myTympan.inputSelect(TYMPAN_INPUT_ON_BOARD_MIC); // use the on-board microphones
//Set mixer gain (right channel only; left is already sent to SD)
mixerIn.gain(0,0.0);
mixerIn.gain(1,1.0);
//Set input gain to 0dB
input_gain_dB = 0.0;
myTympan.setInputGain_dB(input_gain_dB);
//Store configuration
current_config = config_pcb_mics;
break;
case config_mic_jack:
//Select Input
myTympan.inputSelect(TYMPAN_INPUT_JACK_AS_MIC); // use the mic jack/
//Set mixer gain (right channel only; left is already sent to SD)
mixerIn.gain(0,0.0);
mixerIn.gain(1,1.0);
//Set input gain to 0dB
input_gain_dB = 0.0;
myTympan.setInputGain_dB(input_gain_dB);
//Store configuration
current_config = config_mic_jack;
break;
case config_line_in_SE:
//Select Input
myTympan.inputSelect(TYMPAN_INPUT_LINE_IN); // use the line-input through holes
//Set mixer gain (right channel only; left is already sent to SD)
mixerIn.gain(0,0.0);
mixerIn.gain(1,1.0);
//Set input gain to 0dB
input_gain_dB = 0.0;
myTympan.setInputGain_dB(input_gain_dB);
//Store configuration
current_config = config_line_in_SE;
break;
default:
break;
}
}
// ///////////////// Main setup() and loop() as required for all Arduino programs
void setup() {
Serial.begin(115200); delay(1000);
Serial.print(overall_name); Serial.println(": setup():...");
Serial.print("Sample Rate (Hz): "); Serial.println(audio_settings.sample_rate_Hz);
Serial.print("Audio Block Size (samples): "); Serial.println(audio_settings.audio_block_samples);
//allocate the dynamic memory for audio processing blocks
AudioMemory_F32(50,audio_settings); //I can only seem to allocate 400 blocks
Serial.println("Setup: memory allocated.");
//activate the Tympan audio hardware
myTympan.enable(); // activate AIC
//Set the state of the LEDs
myTympan.setRedLED(HIGH);
myTympan.setGreenLED(LOW);
//prepare the SD writer for the format that we want and any error statements
audioSDWriter.setSerial(&myTympan); //the library will print any error info to this serial stream (note that myTympan is also a serial stream)
audioSDWriter.setNumWriteChannels(2); //this is also the built-in defaullt, but you could change it to 4 (maybe?), if you wanted 4 channels.
int ret_val = audioSDWriter.setWriteDataType(AudioSDWriter::WriteDataType::INT16); //this is the built-in default, but here you could change it to FLOAT32
Serial.print("setup: setWriteDataType() yielded return code: "); Serial.print(ret_val);
if (ret_val < 0) { Serial.println(": ERROR!"); } else { Serial.println(": OK"); }
//End of setup
Serial.println("Setup: complete.");
printHelp();
}
void loop() {
//update the memory and CPU usage...if enough time has passed
if (enable_printCPUandMemory) myTympan.printCPUandMemory(millis(),3000); //print every 3000 msec
manageSerial();
//service the SD recording
serviceSD();
}
// ///////////////// Servicing routines
void serviceSD(void) {
static int max_max_bytes_written = 0; //for timing diagnotstics
static int max_bytes_written = 0; //for timing diagnotstics
static int max_dT_micros = 0; //for timing diagnotstics
static int max_max_dT_micros = 0; //for timing diagnotstics
unsigned long dT_micros = micros(); //for timing diagnotstics
int bytes_written = audioSDWriter.serviceSD();
dT_micros = micros() - dT_micros; //timing calculation
if ( bytes_written > 0 ) {
max_bytes_written = max(max_bytes_written, bytes_written);
max_dT_micros = max((int)max_dT_micros, (int)dT_micros);
if (dT_micros > 10000) { //if the write took a while, print some diagnostic info
max_max_bytes_written = max(max_bytes_written,max_max_bytes_written);
max_max_dT_micros = max(max_dT_micros, max_max_dT_micros);
Serial.print("serviceSD: bytes written = ");
Serial.print(bytes_written); Serial.print(", ");
Serial.print(max_bytes_written); Serial.print(", ");
Serial.print(max_max_bytes_written); Serial.print(", ");
Serial.print("dT millis = ");
Serial.print((float)dT_micros/1000.0,1); Serial.print(", ");
Serial.print((float)max_dT_micros/1000.0,1); Serial.print(", ");
Serial.print((float)max_max_dT_micros/1000.0,1);Serial.print(", ");
Serial.println();
max_bytes_written = 0;
max_dT_micros = 0;
}
//print a warning if there has been an SD writing hiccup
if (PRINT_OVERRUN_WARNING) {
//if (audioSDWriter.getQueueOverrun() || i2s_in.get_isOutOfMemory()) {
if (i2s_in.get_isOutOfMemory()) {
float approx_time_sec = ((float)(millis()-audioSDWriter.getStartTimeMillis()))/1000.0;
if (approx_time_sec > 0.1) {
myTympan.print("SD Write Warning: there was a hiccup in the writing.");// Approx Time (sec): ");
myTympan.println(approx_time_sec );
}
}
}
i2s_in.clear_isOutOfMemory();
}
}
// here's a function to change the volume settings. Control via Serial
void incrementInputGain(float increment_dB) {
input_gain_dB += increment_dB;
myTympan.setInputGain_dB(input_gain_dB);
}
//Increment Headphone Output Volume
void incrementHeadphoneVol(float increment_dB) {
vol_knob_gain_dB += increment_dB;
myTympan.volume_dB(vol_knob_gain_dB); // headphone amplifier. -63.6 to +24 dB in 0.5dB steps.
}
const float INCREMENT_INPUT_GAIN_DB = 2.5f;
const float INCREMENT_HEADPHONE_GAIN_DB = 2.5f;
void printHelp(void) {
Serial.println();
Serial.println("SerialManager Help: Available Commands:");
Serial.println(" h: Print this help");
Serial.println(" c: Start printing of CPU and Memory usage");
Serial.println(" C: Stop printing of CPU and Memory usage");
Serial.println(" r: begin recording");
Serial.println(" s: stop recording");
Serial.println(" b/n/m: Switch between (b) on-PCB mic; (n) line-input (single-ended); (m) mic jack");
Serial.print (" i/o: Decrease/Increase the Input Gain by "); myTympan.print(INCREMENT_INPUT_GAIN_DB); myTympan.println(" dB");
Serial.print (" j/k: Decrease/Increase the Headphone Volume by "); myTympan.print(INCREMENT_HEADPHONE_GAIN_DB); myTympan.println(" dB");
Serial.println();
}
//switch yard to determine the desired action
void manageSerial() {
if(Serial.available()){
char inChar = Serial.read();
switch (inChar) {
case 'h': case '?':
printHelp(); break;
case 'c':
Serial.println("Received: start CPU reporting");
setPrintMemoryAndCPU(true);
break;
case 'C':
Serial.println("Received: stop CPU reporting");
setPrintMemoryAndCPU(false);
break;
case 'o':
incrementInputGain(INCREMENT_INPUT_GAIN_DB);
printGainSettings();
break;
case 'i':
incrementInputGain(-INCREMENT_INPUT_GAIN_DB);
printGainSettings();
break;
case 'k':
incrementHeadphoneVol(INCREMENT_HEADPHONE_GAIN_DB);
printGainSettings();
break;
case 'j':
incrementHeadphoneVol(-INCREMENT_HEADPHONE_GAIN_DB);
printGainSettings();
break;
case 'b':
Serial.println("Command Received: switch to on-PCB mic; InputGain = 0dB");
setConfiguration(config_pcb_mics);
break;
case 'n':
Serial.println("Command Received: switch to line-input through-holes (Single-Ended); InputGain = 0dB");
setConfiguration(config_line_in_SE);
break;
case 'm':
Serial.println("Command Received: switch to external mic; InputGain = 0dB");
setConfiguration(config_mic_jack);
break;
case 'r':
myTympan.println("Received: begin SD recording");
audioSDWriter.startRecording();
if (audioSDWriter.getState() == AudioSDWriter_F32::STATE::RECORDING) {
//Serial.print("SD Filename = "); Serial.println(audioSDWriter.getCurrentFilename());
} else {
Serial.print("SD Failed to start recording.");
}
break;
case 's':
myTympan.println("Received: stop SD recording");
audioSDWriter.stopRecording();
break;
default:
break;
}
}
}
void printGainSettings(void) {
Serial.print("Vol Knob = ");
Serial.print(vol_knob_gain_dB,1);
Serial.print(", Input PGA = ");
Serial.print(input_gain_dB,1);
Serial.println();
}