Skip to content

Commit

Permalink
Example: CalibrateAnalogIO: add auto-stepping of test freq (and add o…
Browse files Browse the repository at this point in the history
…ther stuff)
  • Loading branch information
chipaudette committed Oct 2, 2024
1 parent 21ef6ff commit 224611d
Show file tree
Hide file tree
Showing 5 changed files with 211 additions and 65 deletions.
91 changes: 63 additions & 28 deletions examples/02-Utility/CalibrateAnalogIO/AudioProcessing.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
#define _AudioProcessing_h

AudioInputI2S_F32 i2s_in(audio_settings); //Digital audio input from the ADC
AudioCalcLevel_F32 calcInputLevel_L(audio_settings); //use this to measure the input signal level
AudioCalcLevel_F32 calcInputLevel_R(audio_settings); //use this to measure the input signal level
AudioCalcLeq_F32 calcInputLevel_L(audio_settings); //use this to measure the input signal level
AudioCalcLeq_F32 calcInputLevel_R(audio_settings); //use this to measure the input signal level
AudioSynthWaveform_F32 sineWave(audio_settings); //generate a synthetic sine wave
AudioSwitchMatrix4_F32 outputSwitchMatrix(audio_settings); //use this to route the sine wave to L, R, or Both
AudioCalcLevel_F32 calcOutputLevel(audio_settings); //use this to measure the input signal level
AudioCalcLeq_F32 calcOutputLevel(audio_settings); //use this to measure the input signal level
AudioSDWriter_F32 audioSDWriter(audio_settings); //this is stereo by default
AudioOutputI2S_F32 i2s_out(audio_settings); //Digital audio output to the DAC. Should always be last.

Expand Down Expand Up @@ -79,26 +79,17 @@ void setConfiguration(int config) {
}
}

float setCalcLevelTimeConstants(float time_const_sec) {
time_const_sec = max(time_const_sec,0.0001);
calcInputLevel_L.setTimeConst_sec(time_const_sec);
myState.calcLevel_timeConst_sec = calcInputLevel_L.getTimeConst_sec();
calcInputLevel_R.setTimeConst_sec(myState.calcLevel_timeConst_sec);
calcOutputLevel.setTimeConst_sec(myState.calcLevel_timeConst_sec);
return myState.calcLevel_timeConst_sec;
void printInputConfiguration(void) {
switch (myState.input_source) {
case State::INPUT_PCBMICS:
Serial.print("PCB Mics"); break;
case State::INPUT_JACK_MIC:
Serial.print("Input Jack as Mic"); break;
case State::INPUT_JACK_LINE:
Serial.print("Input Jack as Line-In"); break;
}
}

float setFrequency_Hz(float freq_Hz) {
myState.sine_freq_Hz = constrain(freq_Hz, 125.0f/4.0, 16000.0f); //constrain between 32 Hz and 16 kHz
sineWave.frequency(myState.sine_freq_Hz);
return myState.sine_freq_Hz;
}

float setAmplitude(float amplitude) {
myState.sine_amplitude = constrain(amplitude, 0.0, 1.0); //constrain between 0.0 and 1.0
sineWave.amplitude(myState.sine_amplitude);
return myState.sine_amplitude;
}

int setOutputChan(int chan) {
const int inputChanForSineWave = 0;
Expand All @@ -111,23 +102,67 @@ int setOutputChan(int chan) {
Serial.println("setOutputChan: chan = " + String(chan) + ". Setting to LEFT...");
outputSwitchMatrix.setInputToOutput(inputChanForSineWave,outputChanLeft); //sine to left output
outputSwitchMatrix.setInputToOutput(inputChanForMutedOutput,outputChanRight); //mute the right output
return chan; //return early
myState.output_chan = chan;
break;
case State::OUT_RIGHT:
Serial.println("setOutputChan: chan = " + String(chan) + ". Setting to RIGHT...");
outputSwitchMatrix.setInputToOutput(inputChanForMutedOutput,outputChanLeft); //mute the left output
outputSwitchMatrix.setInputToOutput(inputChanForSineWave,outputChanRight); //sine to right output
return chan; //return early
myState.output_chan = chan;
break;
case State::OUT_BOTH:
Serial.println("setOutputChan: chan = " + String(chan) + ". Setting to BOTH...");
outputSwitchMatrix.setInputToOutput(inputChanForSineWave,outputChanLeft); //sine to left output
outputSwitchMatrix.setInputToOutput(inputChanForSineWave,outputChanRight); //sine to right output
return chan; //return early
myState.output_chan = chan;
break;
default:
Serial.println("setOutputChan: *** WARNING ***: chan = " + String(chan) + " not recognized. Muting all output channels...");
outputSwitchMatrix.setInputToOutput(inputChanForMutedOutput,outputChanLeft); //sine to left output
outputSwitchMatrix.setInputToOutput(inputChanForMutedOutput,outputChanRight); //sine to right output
Serial.println("setOutputChan: *** WARNING ***: chan = " + String(chan) + " not recognized. Setting to BOTH...");
outputSwitchMatrix.setInputToOutput(inputChanForSineWave,outputChanLeft); //sine to left output
outputSwitchMatrix.setInputToOutput(inputChanForSineWave,outputChanRight); //sine to right output
myState.output_chan = chan;
break;
}
return -1; //indicates an error
return myState.output_chan;
}

void printOutputChannel(void) {
switch (myState.output_chan) {
case State::OUT_LEFT:
Serial.print("LEFT"); break;
case State::OUT_RIGHT:
Serial.print("RIGHT"); break;
case State::OUT_BOTH:
Serial.print("BOTH"); break;
}
}

// /////////// Functions for configuring the audio processing

float setCalcLevelTimeWindow(float time_window_sec) {
time_window_sec = max(time_window_sec,0.0001); //don't windows that are too short
calcInputLevel_L.setTimeWindow_sec(time_window_sec); //try to set the window time
myState.calcLevel_timeWindow_sec = calcInputLevel_L.getTimeWindow_sec(); //ask what time was actually set

//set the rest of the time windows
calcInputLevel_R.setTimeWindow_sec(myState.calcLevel_timeWindow_sec);
calcOutputLevel.setTimeWindow_sec(myState.calcLevel_timeWindow_sec);
return myState.calcLevel_timeWindow_sec;
}

float setFrequency_Hz(float freq_Hz) {
const float min_freq_Hz = 125.0/8;
const float max_freq_Hz = 20000.0;
myState.sine_freq_Hz = constrain(freq_Hz, min_freq_Hz, max_freq_Hz); //constrain between 32 Hz and 16 kHz
sineWave.frequency(myState.sine_freq_Hz);
return myState.sine_freq_Hz;
}

float setAmplitude(float amplitude) {
myState.sine_amplitude = constrain(amplitude, 0.0, 1.0); //constrain between 0.0 and 1.0
sineWave.amplitude(myState.sine_amplitude);
return myState.sine_amplitude;
}


#endif
52 changes: 34 additions & 18 deletions examples/02-Utility/CalibrateAnalogIO/CalibrateAnalogIO.ino
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
You can measure the voltage of this signal using a volt meter. You can then feed
the signal back into the Tympan's input (pink) jack to perform the calibration.
This example includes a mode for automatically stepping up through many test
frequencies. See the options available in the Serial Monitor menu. Send
an "h" (without quotes) in the Serial Monitor to see the help menu.
This example also lets you record the audio to the SD card for analysis on your
PC or Mac.
Expand All @@ -30,17 +34,21 @@
#include "State.h"

//set the sample rate and block size
const float sample_rate_Hz = 44100.0f ; //24000 or 44117 or 96000 (or other frequencies in the table in AudioOutputI2S_F32)
const int audio_block_samples = 128; //do not make bigger than AUDIO_BLOCK_SAMPLES from AudioStream.h (which is 128) Must be 128 for SD recording.
const float sample_rate_Hz = 44100.0f ; //24000 or 44117 or 96000 (or other frequencies in the table in AudioOutputI2S_F32)
const int audio_block_samples = 128; //do not make bigger than AUDIO_BLOCK_SAMPLES from AudioStream.h (which is 128) Must be 128 for SD recording.
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples);

// Create the audio objects and then connect them
Tympan myTympan(TympanRev::F,audio_settings); //do TympanRev::D or TympanRev::E or TympanRev::F
Tympan myTympan(TympanRev::F,audio_settings); //do TympanRev::D or TympanRev::E or TympanRev::F
#include "AudioProcessing.h" //see here for audio objects, connections, and configuration functions

// /////////// Create classes for controlling the system, espcially via USB Serial and via the App
SerialManager serialManager; //create the serial manager for real-time control (via USB or App)
State myState(&audio_settings, &myTympan, &serialManager); //keeping one's state is useful for the App's GUI
// Create classes for controlling the system, espcially via USB Serial and via the App
SerialManager serialManager; //create the serial manager for real-time control (via USB or App)
State myState(&audio_settings, &myTympan, &serialManager); //keeping one's state is useful for the App's GUI

// Be aware that this calibration program can output steady tones or it can automatically step the tones across frequencies
#include "TestToneControl.h" //see here for the relevant functions for managing the changing test tones


// ///////////////// Main setup() and loop() as required for all Arduino programs

Expand All @@ -64,20 +72,19 @@ void setup() {
//Select the input that we will use
setConfiguration(myState.input_source); //use the default that is written in State.h

//Setup the sine wave
setFrequency_Hz(myState.sine_freq_Hz);
setAmplitude(myState.sine_amplitude);
setOutputChan(myState.output_chan);

//Setup the level measuring
setCalcLevelTimeConstants(myState.calcLevel_timeConst_sec);
Serial.println("Setup: Setting time constants for level measurements to " + String(myState.calcLevel_timeConst_sec,4) + " sec");
setCalcLevelTimeWindow(myState.calcLevel_timeWindow_sec);
Serial.println("Setup: Setting time windows for level measurements to " + String(myState.calcLevel_timeWindow_sec,4) + " sec");

//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.
Serial.println("Setup: SD configured for " + String(audioSDWriter.getNumWriteChannels()) + " channels.");


//Setup the output signal
setOutputChan(myState.output_chan);
switchTestToneMode(myState.current_test_tone_mode);

//End of setup
Serial.println("Setup: complete.");
serialManager.printHelp();
Expand All @@ -99,11 +106,20 @@ void loop() {
//periodically print the CPU and Memory Usage
if (myState.enable_printCpuToUSB) myState.printCPUandMemory(millis(), 3000); //print every 3000msec (method is built into TympanStateBase.h, which myState inherits from)

//periodically print the signal levels
if (myState.flag_printInputLevelToUSB) printInputSignalLevels(millis(),1000); //print every 1000 msec
//check to see what test mode we're in
if (myState.current_test_tone_mode == State::TONE_MODE_STEPPED_FREQUENCY) { //are we doing stepped tones?

//update the stepped dones
serviceSteppedToneTest(millis()); //update the tones

} else { //we are not doing stepped tones
//periodically print the signal levels
if (myState.flag_printInputLevelToUSB) printInputSignalLevels(millis(),1000); //print every 1000 msec

//periodically print the output levels
if (myState.flag_printOutputLevelToUSB) printOutputSignalLevels(millis(),1000); //print every 1000 msec
}

//periodically print the output levels
if (myState.flag_printOutputLevelToUSB) printOutputSignalLevels(millis(),1000); //print every 1000 msec
} //end loop()

// //////////////////////////////////////// Other functions
Expand Down
38 changes: 23 additions & 15 deletions examples/02-Utility/CalibrateAnalogIO/SerialManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ extern float setFrequency_Hz(float freq_Hz);
extern float setAmplitude(float amplitude);
extern int setOutputChan(int chan);
extern bool enablePrintMemoryAndCPU(bool _enable);
extern void printInputConfiguration(void);
extern void printOutputChannel(void);
extern void printTestToneMode(void);
extern int switchTestToneMode(int new_mode);

class SerialManager : public SerialManagerBase { // see Tympan_Library for SerialManagerBase for more functions!
public:
Expand All @@ -35,19 +39,17 @@ class SerialManager : public SerialManagerBase { // see Tympan_Library for Ser

void SerialManager::printHelp(void) {
Serial.println("SerialManager Help: Available Commands:");
Serial.println(" General: No Prefix");
Serial.println(" h: Print this help");
Serial.print( " w: Switch Input to PCB Mics"); if (myState.input_source==State::INPUT_PCBMICS) {Serial.println(" (active)");} else { Serial.println(); }
Serial.print( " W: Switch Input to MicIn on the Pink Jack"); if (myState.input_source==State::INPUT_JACK_MIC) {Serial.println(" (active)");} else { Serial.println(); }
Serial.print( " e: Switch Input to LineIn on the Pink Jack"); if (myState.input_source==State::INPUT_JACK_LINE) {Serial.println(" (active)");} else { Serial.println(); }
Serial.println(" i/I: Input: Increase or decrease input gain (current = " + String(myState.input_gain_dB,1) + " dB)");
Serial.println(" f/F: Sine: Increase or decrease sine frequency (current = " + String(myState.sine_freq_Hz,1) + " Hz");
Serial.println(" a/A: Sine: Increase or decrease sine amplitude (current = " + String(20*log10(myState.sine_amplitude)-3.0,1) + " dBFS (output), " + String(myState.sine_amplitude,3) + " amplitude)");
Serial.println(" 1/2/3: Sine: Output to left (1), right (2), or both (3)");
Serial.print( " p/P: Printing: start/Stop printing of input signal levels"); if (myState.flag_printInputLevelToUSB) {Serial.println(" (active)");} else { Serial.println(" (off)"); }
Serial.print( " o/O: Printing: start/Stop printing of output signal levels"); if (myState.flag_printOutputLevelToUSB) {Serial.println(" (active)");} else { Serial.println(" (off)"); }
Serial.println(" r: SD: Start recording audio to SD card");
Serial.println(" s: SD: Stop recording audio to SD card");
Serial.println("General: No Prefix");
Serial.println(" h: Print this help");
Serial.print( " w/W/E: Input: Use PCB mics (w), jack as mic (W), jack as line-in (e) (current = "); printInputConfiguration(); Serial.println(")");
Serial.println(" i/I: Input: Increase or decrease input gain (current = " + String(myState.input_gain_dB,1) + " dB)");
Serial.println(" f/F: Sine: Increase or decrease steady-tone frequency (current = " + String(myState.sine_freq_Hz,1) + " Hz)");
Serial.println(" a/A: Sine: Increase or decrease sine amplitude (current = " + String(20*log10(myState.sine_amplitude)-3.0,1) + " dB re: output FS = " + String(myState.sine_amplitude,3) + " amplitude)");
Serial.print( " 1/2/3: Sine: Output to left (1), right (2), or both (3) (current = "); printOutputChannel(); Serial.println(")");
Serial.print( " t/T: TestMode: Switch between steady tone (t) or stepped-tone (T) modes (current = "); printTestToneMode(); Serial.println(")");
Serial.print( " p/P: Printing: start/Stop printing of input signal levels"); if (myState.flag_printInputLevelToUSB) {Serial.println(" (active)");} else { Serial.println(" (off)"); }
Serial.print( " o/O: Printing: start/Stop printing of output signal levels"); if (myState.flag_printOutputLevelToUSB) {Serial.println(" (active)");} else { Serial.println(" (off)"); }
Serial.println(" r/s: SD: Start recording (r) or stop (s) audio to SD card");
Serial.println();
}

Expand Down Expand Up @@ -88,11 +90,11 @@ bool SerialManager::processCharacter(char c) { //this is called by SerialManager
Serial.println("SerialManager: Decreased input gain to: " + String(myState.input_gain_dB) + " dB");
break;
case 'f':
setFrequency_Hz(myState.sine_freq_Hz*frequencyIncrement_factor);
setFrequency_Hz(max(125.0/4,min(16000.0,myState.sine_freq_Hz*frequencyIncrement_factor))); //octave-based incrementing. Limit freuqencies to 31.25 Hz -> 16 kHz
Serial.println("SerialManager: increased tone frequency to " + String(myState.sine_freq_Hz));
break;
case 'F':
setFrequency_Hz(myState.sine_freq_Hz/frequencyIncrement_factor);
setFrequency_Hz(max(125.0/4,min(16000.0,myState.sine_freq_Hz/frequencyIncrement_factor))); //octave-based incrementing. Limit freuqencies to 31.25 Hz -> 16 kHz
Serial.println("SerialManager: decreased tone frequency to " + String(myState.sine_freq_Hz));
break;
case 'a':
Expand All @@ -115,6 +117,12 @@ bool SerialManager::processCharacter(char c) { //this is called by SerialManager
Serial.println("SerialManager: outputing sine to both left and right");
setOutputChan(State::OUT_BOTH);
break;
case 't':
switchTestToneMode(State::TONE_MODE_STEADY);
break;
case 'T':
switchTestToneMode(State::TONE_MODE_STEPPED_FREQUENCY);
break;
case 'p':
myState.flag_printInputLevelToUSB = true;
Serial.println("SerialManager: enabled printing of the input signal levels");
Expand Down
19 changes: 15 additions & 4 deletions examples/02-Utility/CalibrateAnalogIO/State.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,27 @@ class State : public TympanStateBase_UI { // look in TympanStateBase or TympanSt
//set highpass filter on ADC
float adc_hp_cutoff_Hz = 31.25f;

//variables related to the sine wave
float sine_freq_Hz = 1000.0f;
float sine_amplitude = sqrt(2)*sqrt(pow(10.0,0.1*-20.0)); //default amplitude (-20dBFS converted to linear and then converted from RMS to amplitude)
//variables related to the test tone mode
enum Test_Tone_Mode { TONE_MODE_MUTE, TONE_MODE_STEADY, TONE_MODE_STEPPED_FREQUENCY};
int current_test_tone_mode = TONE_MODE_STEADY;
float stepped_test_start_freq_Hz = 20.0; //starting frequency for stepped tones
float stepped_test_end_freq_Hz = 20000.0; //ending frequency for stepped tones
float stepped_test_step_Hz = 50.0; //frequency inrement for stepped tones
float stepped_test_step_dur_sec = 1.0; //duration at each step
unsigned long stepped_test_next_change_millis = 0UL;

//variables related to the current sine wave
float default_sine_freq_Hz = 1000.0; //used whenever switching the TONE_MODE
float default_sine_amplitude = sqrt(2.0)*sqrt(pow(10.0,0.1*-20.0)); //(-20dBFS converted to linear and then converted from RMS to amplitude)
float sine_freq_Hz = 1000.0; //the current frequency
float sine_amplitude = 0.0; //the current amplitude

//variables relating to the output switching of the sine wave
enum OUT_CHAN { OUT_LEFT=0, OUT_RIGHT=1, OUT_BOTH=9};
int output_chan = OUT_BOTH;

//variables associated with level measurement
float calcLevel_timeConst_sec = 0.125f;
float calcLevel_timeWindow_sec = 0.125f;

//Put different gain settings (except those in the compressors) here to ease the updating of the GUI
float input_gain_dB = 0.0; //gain of the hardware PGA in the AIC
Expand Down
Loading

0 comments on commit 224611d

Please sign in to comment.