You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
163 lines
6.8 KiB
163 lines
6.8 KiB
/* FormantShifter_FD_OA.ino
|
|
*
|
|
* Demonstrate formant shifting via frequency domain processin.
|
|
*
|
|
* Created: Chip Audette (OpenAudio) March 2019
|
|
*
|
|
* Approach:
|
|
* * Take samples in the time domain
|
|
* * Take FFT to convert to frequency domain
|
|
* * Manipulate the frequency bins to do the formant shifting
|
|
* * Take IFFT to convert back to time domain
|
|
* * Send samples back to the audio interface
|
|
*
|
|
* The amount of formant shifting is controLled via the Serial link.
|
|
* It defaults to a modest upward shifting of the formants
|
|
*
|
|
* Built for the Tympan library for Teensy 3.6-based hardware
|
|
*
|
|
* Adapt to OpenAudio Library - Bob Larkin June 2020
|
|
* This example supports only audio Line In, left channel, of SGTL5000
|
|
* Codec (Teensy Audio Adaptor) and Line Out on left and right channel.
|
|
* Simplified control by using only serial commands.
|
|
*
|
|
* This is an interesting block. Try it! Ask the internet about
|
|
* formant shifting, and check out Chip's implementation in update()
|
|
* of AudioEffectFormantShiftFD_OA_F32.h
|
|
*
|
|
* This is tested to run on Teensy 3.6 and Teensy 4.0 using the PJRC
|
|
* Teensy Audio Adaptor.
|
|
*
|
|
* MIT License. Use at your own risk.
|
|
*/
|
|
#include "AudioStream_F32.h"
|
|
#include "OpenAudio_ArduinoLibrary.h"
|
|
#include "AudioEffectFormantShiftFD_OA_F32.h" //the local file holding your custom function
|
|
#include "SerialManagerFormant_OA.h"
|
|
|
|
//set the sample rate and block size
|
|
const float sample_rate_Hz = 44117.f; ; // other frequencies in the table in AudioOutputI2S_F32 for T3.x only
|
|
const int audio_block_samples = 128; //for freq domain processing, a power of 2: 16, 32, 64, 128
|
|
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples);
|
|
|
|
//create audio library objects for handling the audio
|
|
AudioInputI2S i2sIn; // This I16 input/output is T4.x compatible
|
|
AudioConvert_I16toF32 cnvrt1; // Cobevrt to float
|
|
AudioEffectFormantShiftFD_OA_F32 formantShift(audio_settings); // the frequency-domain processing block
|
|
AudioEffectGain_F32 gain1; //Applies digital gain to audio data.
|
|
AudioConvert_F32toI16 cnvrt2;
|
|
AudioOutputI2S i2sOut;
|
|
AudioControlSGTL5000 codec;
|
|
|
|
AudioAnalyzePeak_F32 peak1;
|
|
//Make all of the audio connections
|
|
AudioConnection patchCord1(i2sIn, 0, cnvrt1, 0); // connect to Left codec, 16-bit
|
|
AudioConnection_F32 patchCord2(cnvrt1, 0, formantShift, 0);
|
|
AudioConnection_F32 patchCord3(formantShift, 0, gain1, 0); //connect to gain
|
|
AudioConnection_F32 patchCord4(gain1, 0, cnvrt2, 0); //connect to the left output
|
|
AudioConnection patchCord6(cnvrt2, 0, i2sOut, 0);
|
|
AudioConnection patchCord7(cnvrt2, 0, i2sOut, 1);
|
|
AudioConnection_F32 connx(gain1, 0, peak1, 0);
|
|
//control display and serial interaction
|
|
bool enable_printCPUandMemory = false;
|
|
void togglePrintMemoryAndCPU(void) { enable_printCPUandMemory = !enable_printCPUandMemory; };
|
|
SerialManagerFormant_OA SerMgr;
|
|
|
|
//inputs and levels
|
|
float input_gain_dB = 0.0f; //gain on the microphone
|
|
float formant_shift_gain_correction_dB = 0.0; //will be used to adjust for gain in formant shifter
|
|
float vol_knob_gain_dB = 0.0; //will be overridden by volume knob
|
|
|
|
// **************************** SETUP **********************
|
|
void setup() {
|
|
Serial.begin(1); delay(1000);
|
|
Serial.println("FormantShifter: starting setup()...");
|
|
Serial.print(" : sample rate (Hz) = "); Serial.println(audio_settings.sample_rate_Hz);
|
|
Serial.print(" : block size (samples) = "); Serial.println(audio_settings.audio_block_samples);
|
|
|
|
// Audio connections require memory to work. For more
|
|
// detailed information, see the MemoryAndCpuUsage example
|
|
AudioMemory(10); // I16 type
|
|
AudioMemory_F32(40, audio_settings);
|
|
|
|
codec.enable();
|
|
codec.adcHighPassFilterDisable();
|
|
codec.inputSelect(AUDIO_INPUT_LINEIN);
|
|
|
|
// Configure the frequency-domain algorithm
|
|
int overlap_factor = 4; //set to 4 or 8 or either 75% overlap (4x) or 87.5% overlap (8x)
|
|
int N_FFT = audio_block_samples * overlap_factor;
|
|
formantShift.setup(audio_settings, N_FFT); //do after AudioMemory_F32();
|
|
formantShift.setScaleFactor(1.587401f); //1.0 is no formant shifting.
|
|
if (overlap_factor == 4) {
|
|
formant_shift_gain_correction_dB = -3.0;
|
|
} else if (overlap_factor == 8) {
|
|
formant_shift_gain_correction_dB = -9.0;
|
|
}
|
|
SerMgr.printHelp();
|
|
}
|
|
|
|
// ************************* LOOP *********************
|
|
void loop() {
|
|
//respond to Serial commands
|
|
while (Serial.available()) SerMgr.respondToByte((char)Serial.read()); //USB Serial
|
|
|
|
//check to see whether to print the CPU and Memory Usage
|
|
if (enable_printCPUandMemory) printCPUandMemory(millis(), 3000); //print every 3000 msec
|
|
}
|
|
|
|
// ********************************************************
|
|
|
|
//This routine prints the current and maximum CPU usage and the current usage of the AudioMemory that has been allocated
|
|
void printCPUandMemory(unsigned long curTime_millis, unsigned long updatePeriod_millis) {
|
|
//static unsigned long updatePeriod_millis = 3000; //how many milliseconds between updating gain reading?
|
|
static unsigned long lastUpdate_millis = 0;
|
|
|
|
//has enough time passed to update everything?
|
|
if (curTime_millis < lastUpdate_millis) lastUpdate_millis = 0; //handle wrap-around of the clock
|
|
if ((curTime_millis - lastUpdate_millis) > updatePeriod_millis) { //is it time to update the user interface?
|
|
Serial.print("printCPUandMemory: ");
|
|
Serial.print("CPU Cur/Peak: ");
|
|
Serial.print(audio_settings.processorUsage());
|
|
//Serial.print(AudioProcessorUsage()); //if not using AudioSettings_F32
|
|
Serial.print("%/");
|
|
Serial.print(audio_settings.processorUsageMax());
|
|
//Serial.print(AudioProcessorUsageMax()); //if not using AudioSettings_F32
|
|
Serial.print("%, ");
|
|
Serial.print("Audio MEM Float32 Cur/Peak: ");
|
|
Serial.print(AudioMemoryUsage_F32());
|
|
Serial.print("/");
|
|
Serial.println(AudioMemoryUsageMax_F32());
|
|
Serial.print("Audio MEM Int16 Cur/Peak: ");
|
|
Serial.print(AudioMemoryUsage());
|
|
Serial.print("/");
|
|
Serial.println(AudioMemoryUsageMax());
|
|
Serial.println();
|
|
//if(peak1.available()) Serial.println(peak1.read(), 6);
|
|
lastUpdate_millis = curTime_millis; //we will use this value the next time around.
|
|
}
|
|
}
|
|
|
|
void printGainSettings(void) {
|
|
Serial.print("Gain (dB): ");
|
|
Serial.print("Vol Knob = "); Serial.print(vol_knob_gain_dB,1);
|
|
//Serial.print(", Input PGA = "); Serial.print(input_gain_dB,1);
|
|
Serial.println();
|
|
}
|
|
|
|
void incrementKnobGain(float increment_dB) {
|
|
setVolKnobGain_dB(vol_knob_gain_dB + increment_dB);
|
|
}
|
|
|
|
void setVolKnobGain_dB(float gain_dB) {
|
|
vol_knob_gain_dB = gain_dB;
|
|
gain1.setGain_dB(vol_knob_gain_dB + formant_shift_gain_correction_dB);
|
|
printGainSettings();
|
|
}
|
|
|
|
float incrementFormantShift(float incr_factor) {
|
|
float cur_scale_factor = formantShift.getScaleFactor();
|
|
return formantShift.setScaleFactor(cur_scale_factor*incr_factor);
|
|
}
|
|
|
|
|
|
|