From 6e9aa1b10c300dd8a449b22b953b2070b6dd59f1 Mon Sep 17 00:00:00 2001 From: boblark Date: Tue, 23 Jun 2020 14:40:13 -0700 Subject: [PATCH] Moved FormantShifter to OA --- .../AudioEffectFormantShiftFD_OA_F32.h | 36 +++--- .../FormantShifter_FD_OA.ino | 119 ++++++------------ .../SerialManagerFormant_OA.h | 95 +++++++------- readme.md | 21 ++-- 4 files changed, 116 insertions(+), 155 deletions(-) diff --git a/examples/FormantShifter_FD_OA/AudioEffectFormantShiftFD_OA_F32.h b/examples/FormantShifter_FD_OA/AudioEffectFormantShiftFD_OA_F32.h index 4d0c342..3a82fc9 100644 --- a/examples/FormantShifter_FD_OA/AudioEffectFormantShiftFD_OA_F32.h +++ b/examples/FormantShifter_FD_OA/AudioEffectFormantShiftFD_OA_F32.h @@ -1,16 +1,25 @@ - +/* AudioEffectFormantShiiftFD_OA_F32.h + * Demonstrate formant shifting via frequency domain processin. + * + * Created: Chip Audette (OpenAudio) March 2019 + * See FormantShifter_FD_OA.ino for notes. + * + * Adapt to OpenAudio Library - Bob Larkin June 2020 + * MIT License. Use at your own risk. + */ #ifndef _AudioEffectFormantShiftFD_OA_F32_h #define _AudioEffectFormantShiftFD_OA_F32_h #include "AudioStream_F32.h" #include -#include "FFT_Overlapped_F32.h" +#include "FFT_Overlapped_OA_F32.h" class AudioEffectFormantShiftFD_OA_F32 : public AudioStream_F32 { public: - //constructors...a few different options. The usual one should be: AudioEffectFormantShiftFD_OA_F32(const AudioSettings_F32 &settings, const int _N_FFT) - AudioEffectFormantShiftFD_OA_F32(void) : AudioStream_F32(1, inputQueueArray_f32) {}; + // constructors...a few different options. The usual one should be: + // AudioEffectFormantShiftFD_OA_F32(const AudioSettings_F32 &settings, const int _N_FFT) + AudioEffectFormantShiftFD_OA_F32(void) : AudioStream_F32(1, inputQueueArray_f32) { }; AudioEffectFormantShiftFD_OA_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) { sample_rate_Hz = settings.sample_rate_Hz; @@ -47,12 +56,12 @@ class AudioEffectFormantShiftFD_OA_F32 : public AudioStream_F32 //print info about setup Serial.println("AudioEffectFormantShiftFD_OA_F32: FFT parameters..."); - Serial.print(" : N_FFT = "); Serial.println(N_FFT); - Serial.print(" : audio_block_samples = "); Serial.println(settings.audio_block_samples); - Serial.print(" : FFT N_BUFF_BLOCKS = "); Serial.println(myFFT.getNBuffBlocks()); - Serial.print(" : IFFT N_BUFF_BLOCKS = "); Serial.println(myIFFT.getNBuffBlocks()); - Serial.print(" : FFT use window = "); Serial.println(myFFT.getFFTObject()->get_flagUseWindow()); - Serial.print(" : IFFT use window = "); Serial.println((myIFFT.getIFFTObject())->get_flagUseWindow()); + Serial.print(" : N_FFT = "); Serial.println(N_FFT); + Serial.print(" : audio_block_samples = "); Serial.println(settings.audio_block_samples); + Serial.print(" : FFT N_BUFF_BLOCKS = "); Serial.println(myFFT.getNBuffBlocks()); + Serial.print(" : IFFT N_BUFF_BLOCKS = "); Serial.println(myIFFT.getNBuffBlocks()); + Serial.print(" : FFT use window = "); Serial.println(myFFT.getFFTObject()->get_flagUseWindow()); + Serial.print(" : IFFT use window = "); Serial.println((myIFFT.getIFFTObject())->get_flagUseWindow()); //allocate memory to hold frequency domain data complex_2N_buffer = new float32_t[2 * N_FFT]; @@ -62,8 +71,6 @@ class AudioEffectFormantShiftFD_OA_F32 : public AudioStream_F32 return N_FFT; } - //void setLowpassFreq_Hz(float freq_Hz) { lowpass_freq_Hz = freq_Hz; } - //float getLowpassFreq_Hz(void) { return lowpass_freq_Hz; } float setScaleFactor(float scale_fac) { if (scale_fac < 0.00001) scale_fac = 0.00001; return shift_scale_fac = scale_fac; @@ -78,11 +85,10 @@ class AudioEffectFormantShiftFD_OA_F32 : public AudioStream_F32 int enabled = 0; float32_t *complex_2N_buffer; audio_block_f32_t *inputQueueArray_f32[1]; - FFT_Overlapped_F32 myFFT; - IFFT_Overlapped_F32 myIFFT; + FFT_Overlapped_OA_F32 myFFT; + IFFT_Overlapped_OA_F32 myIFFT; float lowpass_freq_Hz = 1000.f; float sample_rate_Hz = AUDIO_SAMPLE_RATE; - float shift_scale_fac = 1.0; //how much to shift formants (frequency multiplier). 1.0 is no shift }; diff --git a/examples/FormantShifter_FD_OA/FormantShifter_FD_OA.ino b/examples/FormantShifter_FD_OA/FormantShifter_FD_OA.ino index 9566668..947417d 100644 --- a/examples/FormantShifter_FD_OA/FormantShifter_FD_OA.ino +++ b/examples/FormantShifter_FD_OA/FormantShifter_FD_OA.ino @@ -1,4 +1,4 @@ -/* FormantShifter_FD.ino +/* FormantShifter_FD_OA.ino * * Demonstrate formant shifting via frequency domain processin. * @@ -17,25 +17,32 @@ * Built for the Tympan library for Teensy 3.6-based hardware * * Adapt to OpenAudio Library - Bob Larkin June 2020 - * Ref: http://iris.elf.stuba.sk/JEEEC/data/pdf/1_110-08.pdf - * + * This example supports only audio Line In, left channel, of SGTL5000 + * Codec (Teensy Audio Adaptor) and Line Out on left channel. Simplified + * control by using only serial commands. This is an interesting block, + * but it doesn't produce the most pleasant result. Have fun and see what you + * can do with the algorithm in the update() function in + * 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 "SerialManager_OA.h" +#include "SerialManagerFormant_OA.h" //set the sample rate and block size -const float sample_rate_Hz = 44117.f; ; //24000 or 44117 (or other frequencies in the table in AudioOutputI2S_F32) -const int audio_block_samples = 128; //for freq domain processing choose a power of 2 (16, 32, 64, 128) but no higher than 128 +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; -AudioConvert_I16toF32 cnvrt1; -AudioEffectFormantShiftFD_F32 formantShift(audio_settings); //create the frequency-domain processing block +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; @@ -43,106 +50,59 @@ AudioControlSGTL5000 codec; //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 patchCord2(formantShift, 0, gain1, 0); //connect to gain -AudioConnection_F32 patchCord3(gain1, 0, cnvrt2, 0); //connect to the left output +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); //control display and serial interaction bool enable_printCPUandMemory = false; void togglePrintMemoryAndCPU(void) { enable_printCPUandMemory = !enable_printCPUandMemory; }; -SerialManager_OA SerialManager_OA(audioHardware); +SerialManagerFormant_OA SerMgr; //inputs and levels float input_gain_dB = 20.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 -// define the setup() function, the function that is called once when the device is booting +// **************************** SETUP ********************** void setup() { Serial.begin(1); delay(1000); - mySerial.println("FormantShifter: starting setup()..."); - mySerial.print(" : sample rate (Hz) = "); mySerial.println(audio_settings.sample_rate_Hz); - mySerial.print(" : block size (samples) = "); mySerial.println(audio_settings.audio_block_samples); + 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(3); // I16 type AudioMemory_F32(40, audio_settings); + codec.enable(); + codec.adcHighPassFilterEnable(); + 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.5); //1.0 is no formant shifting. + 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; } - - codec.enable(); // activate AIC - - //setup DC-blocking highpass filter running in the ADC hardware itself - - //Choose the desired input - - - //Set the desired volume levels - audioHardware.volume_dB(0); // headphone amplifier. -63.6 to +24 dB in 0.5dB steps. - - //finish the setup by printing the help menu to the serial connections - Serial.printHelp(); + SerMgr.printHelp(); } - -// define the loop() function, the function that is repeated over and over for the life of the device +// ************************* LOOP ********************* void loop() { - //respond to Serial commands - while (Serial.available()) SerialManager_OA.respondToByte((char)Serial.read()); //USB Serial - //while (Serial1.available()) SerialManager_OA.respondToByte((char)Serial1.read()); //BT Serial - - //check the potentiometer - servicePotentiometer(millis(), 100); //service the potentiometer every 100 msec - + 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 +} -} //end loop(); - - -// ///////////////// Servicing routines - -//servicePotentiometer: listens to the blue potentiometer and sends the new pot value -// to the audio processing algorithm as a control parameter -void servicePotentiometer(unsigned long curTime_millis, const unsigned long updatePeriod_millis) { - //static unsigned long updatePeriod_millis = 100; //how many milliseconds between updating the potentiometer reading? - static unsigned long lastUpdate_millis = 0; - static float prev_val = -1.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? - - //read potentiometer - float val = float(audioHardware.readPotentiometer()) / 1023.0; //0.0 to 1.0 - val = (1.0/9.0) * (float)((int)(9.0 * val + 0.5)); //quantize so that it doesn't chatter...0 to 1.0 - - #if 0 - //set the volume of the system - setVolKnobGain_dB(val*45.0f - 10.0f - input_gain_dB); - #else - //set the amount of formant shifting - float new_scale_fac = powf(2.0,(val-0.5)*2.0); - formantShift.setScaleFactor(new_scale_fac); - #endif - } - - - lastUpdate_millis = curTime_millis; - } // end if -} //end servicePotentiometer(); - - +// ******************************************************** //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) { @@ -177,14 +137,13 @@ void printGainSettings(void) { Serial.println(); } - -void incrementKnobGain(float increment_dB) { //"extern" to make it available to other files, such as SerialManager_OA.h - setVolKnobGain_dB(vol_knob_gain_dB+increment_dB); +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); + gain1.setGain_dB(vol_knob_gain_dB + formant_shift_gain_correction_dB); printGainSettings(); } diff --git a/examples/FormantShifter_FD_OA/SerialManagerFormant_OA.h b/examples/FormantShifter_FD_OA/SerialManagerFormant_OA.h index 5569da1..22f7bed 100644 --- a/examples/FormantShifter_FD_OA/SerialManagerFormant_OA.h +++ b/examples/FormantShifter_FD_OA/SerialManagerFormant_OA.h @@ -1,45 +1,48 @@ +/* + * SerialManagerFormant_OA.h + * Demonstrate formant shifting via frequency domain processing + * Created: Chip Audette (OpenAudio) March 2019 + * + * Moved to OpenAudio, removed Tympan dependencies, fixed for T4.x + * Bob Larkin June 2020 + * + * MIT License. Use at your own risk. + */ +#ifndef _SerialManagerFormant_OA_h +#define _SerialManagerFormant_OA_h -#ifndef _SerialManager_OA_h -#define _SerialManager_OA_h - -#include - +#include //now, define the Serial Manager class -class SerialManager_OA { - public: +class SerialManagerFormant_OA { public: - SerialManager_OA(Tympan &_audioHardware) - : audioHardware(_audioHardware) - { }; - //SerialManager_OA(void) - //{ }; - - void respondToByte(char c); - void printHelp(void); - int N_CHAN; - float channelGainIncrement_dB = 2.5f; - float formantScaleIncrement = powf(2.0,1.0/6.0); - - private: - Tympan &audioHardware; + void respondToByte(char c); + void printHelp(void); + int N_CHAN; + float channelGainIncrement_dB = 2.5f; + float formantScaleIncrement = powf(2.0,1.0/6.0); }; -#define thisSerial audioHardware -void SerialManager_OA::printHelp(void) { - thisSerial.println(); - thisSerial.println("SerialManager_OA Help: Available Commands:"); - thisSerial.println(" h: Print this help"); - thisSerial.println(" g: Print the gain settings of the device."); - thisSerial.println(" C: Toggle printing of CPU and Memory usage"); - thisSerial.println(" p: Switch to built-in PCB microphones"); - thisSerial.println(" m: switch to external mic via mic jack"); - thisSerial.println(" l: switch to line-in via mic jack"); - thisSerial.print(" k: Increase the gain of all channels (ie, knob gain) by "); thisSerial.print(channelGainIncrement_dB); thisSerial.println(" dB"); - thisSerial.print(" K: Decrease the gain of all channels (ie, knob gain) by ");thisSerial.print(-channelGainIncrement_dB); thisSerial.println(" dB"); - thisSerial.print(" f: Raise formant shifting (change by "); thisSerial.print(formantScaleIncrement); thisSerial.println("x)"); - thisSerial.print(" F: Lower formant shifting (change by "); thisSerial.print(1.0/formantScaleIncrement); thisSerial.println("x)"); thisSerial.println(); +void SerialManagerFormant_OA::printHelp(void) { + Serial.println(); + Serial.println("SerialManager_OA Help: Available Commands:"); + Serial.println(" h: Print this help"); + Serial.println(" g: Print the gain settings of the device."); + Serial.println(" C: Toggle printing of CPU and Memory usage"); + Serial.print(" k: Increase the audio by "); + Serial.print(channelGainIncrement_dB); + Serial.println(" dB"); + Serial.print(" K: Decrease the audio gain by "); + Serial.print(-channelGainIncrement_dB); + Serial.println(" dB"); + Serial.print(" f: Raise formant shifting (change by "); + Serial.print(formantScaleIncrement); + Serial.println("x)"); + Serial.print(" F: Lower formant shifting (change by "); + Serial.print(1.0/formantScaleIncrement); + Serial.println("x)"); + Serial.println(); } //functions in the main sketch that I want to call from here @@ -47,12 +50,12 @@ extern void incrementKnobGain(float); extern void printGainSettings(void); extern void togglePrintMemoryAndCPU(void); extern float incrementFormantShift(float); -extern void switchToPCBMics(void); -extern void switchToMicInOnMicJack(void); -extern void switchToLineInOnMicJack(void); +//extern void switchToPCBMics(void); +//extern void switchToMicInOnMicJack(void); +//extern void switchToLineInOnMicJack(void); //switch yard to determine the desired action -void SerialManager_OA::respondToByte(char c) { +void SerialManagerFormant_OA::respondToByte(char c) { //float old_val = 0.0, new_val = 0.0; switch (c) { case 'h': case '?': @@ -64,24 +67,16 @@ void SerialManager_OA::respondToByte(char c) { case 'K': //which is "shift k" incrementKnobGain(-channelGainIncrement_dB); break; case 'C': case 'c': - thisSerial.println("Received: toggle printing of memory and CPU usage."); + Serial.println("Received: toggle printing of memory and CPU usage."); togglePrintMemoryAndCPU(); break; - case 'p': - switchToPCBMics(); break; - case 'm': - switchToMicInOnMicJack(); break; - case 'l': - switchToLineInOnMicJack();break; case 'f': { float new_val = incrementFormantShift(formantScaleIncrement); - thisSerial.print("Recieved: new format scale = "); thisSerial.println(new_val);} + Serial.print("Recieved: new formant scale = "); Serial.println(new_val);} break; case 'F': { float new_val = incrementFormantShift(1./formantScaleIncrement); - thisSerial.print("Recieved: new format scale = "); thisSerial.println(new_val);} + Serial.print("Recieved: new formant scale = "); Serial.println(new_val);} break; } } - - #endif diff --git a/readme.md b/readme.md index 4e56a40..511d824 100644 --- a/readme.md +++ b/readme.md @@ -7,16 +7,17 @@ OpenAudio Library for Teensy 2-Fix synth_pinknoise_f32.h & .cpp built in I16 to F32. Now T3 and T4. 3-Fix synth_whitenoise_f32.cpp to include IMXRT1062 and built in I16 to F32. Now T3 and T4. 4-Added Gaussian White Noise F32. This is T3 and T4. -5-Renamed 3206 codec to .xxx to stop collisions with Teensy I16 library. Needs work. -6-Added new examples/SignalNoise_float.ino to test white, pink, Gaussian noise and sine. Audio out. -7-Disabled output_i2s_f32.h, .cpp by .xxxfile type. These need rework for T4. Use Convert_F32toI16. -8-Disabled control_tlv320aic3206.h, .cpp by .xxxfile type. These collide with Teensy I16 Audio. -9-Added new analyze_peak_f32.h and .cpp that parallel similar classes in the Teensy I16 library. -10-Added new analyze_rms_f32.h and .cpp that parallel similar classes in the Teensy I16 library. -11-Moved AudioSwitch_F32 from Tympan, added 8-channel version. -12-Added /examples/Switches_float.ino for 4 and 8 channel switches. -13-Moved FFT_Overlapped_F32 files from Tympan, revised for T4.0, added _OA name isolation. -14-Moved Overlapped FFT LPF .INO from Tympan to OA. Working T3.6 and T4.0 +5-Renamed 3206 codec to .xxx to stop collisions with Teensy I16 library. Needs work. +6-Added new examples/SignalNoise_float.ino to test white, pink, Gaussian noise and sine. Audio out. +7-Disabled output_i2s_f32.h, .cpp by .xxxfile type. These need rework for T4. Use Convert_F32toI16. +8-Disabled control_tlv320aic3206.h, .cpp by .xxxfile type. These collide with Teensy I16 Audio. +9-Added new analyze_peak_f32.h and .cpp that parallel similar classes in the Teensy I16 library. +10-Added new analyze_rms_f32.h and .cpp that parallel similar classes in the Teensy I16 library. +11-Moved AudioSwitch_F32 from Tympan, added 8-channel version. +12-Added /examples/Switches_float.ino for 4 and 8 channel switches. +13-Moved FFT_Overlapped_F32 files from Tympan, revised for T4.0, added _OA name isolation. +14-Moved Overlapped FFT LPF .INO from Tympan to OA. Working T3.6 and T4.0 +15-Moved Overlapped FFT AudioFormantShifter INO and related from Tympan to _OA Open Audio. Tested 3.6 and 4.0. **Purpose**: The purpose of this library is to build upon the [Teensy Audio Library](http://www.pjrc.com/teensy/td_libs_Audio.html) to enable new functionality for real-time audio processing.