Moved FormantShifter to OA

pull/11/head
boblark 4 years ago
parent 13eb4a3545
commit 6e9aa1b10c
  1. 24
      examples/FormantShifter_FD_OA/AudioEffectFormantShiftFD_OA_F32.h
  2. 115
      examples/FormantShifter_FD_OA/FormantShifter_FD_OA.ino
  3. 85
      examples/FormantShifter_FD_OA/SerialManagerFormant_OA.h
  4. 1
      readme.md

@ -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 #ifndef _AudioEffectFormantShiftFD_OA_F32_h
#define _AudioEffectFormantShiftFD_OA_F32_h #define _AudioEffectFormantShiftFD_OA_F32_h
#include "AudioStream_F32.h" #include "AudioStream_F32.h"
#include <arm_math.h> #include <arm_math.h>
#include "FFT_Overlapped_F32.h" #include "FFT_Overlapped_OA_F32.h"
class AudioEffectFormantShiftFD_OA_F32 : public AudioStream_F32 class AudioEffectFormantShiftFD_OA_F32 : public AudioStream_F32
{ {
public: public:
//constructors...a few different options. The usual one should be: AudioEffectFormantShiftFD_OA_F32(const AudioSettings_F32 &settings, const int _N_FFT) // constructors...a few different options. The usual one should be:
AudioEffectFormantShiftFD_OA_F32(void) : AudioStream_F32(1, inputQueueArray_f32) {}; // 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) : AudioEffectFormantShiftFD_OA_F32(const AudioSettings_F32 &settings) :
AudioStream_F32(1, inputQueueArray_f32) { AudioStream_F32(1, inputQueueArray_f32) {
sample_rate_Hz = settings.sample_rate_Hz; sample_rate_Hz = settings.sample_rate_Hz;
@ -62,8 +71,6 @@ class AudioEffectFormantShiftFD_OA_F32 : public AudioStream_F32
return N_FFT; 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) { float setScaleFactor(float scale_fac) {
if (scale_fac < 0.00001) scale_fac = 0.00001; if (scale_fac < 0.00001) scale_fac = 0.00001;
return shift_scale_fac = scale_fac; return shift_scale_fac = scale_fac;
@ -78,11 +85,10 @@ class AudioEffectFormantShiftFD_OA_F32 : public AudioStream_F32
int enabled = 0; int enabled = 0;
float32_t *complex_2N_buffer; float32_t *complex_2N_buffer;
audio_block_f32_t *inputQueueArray_f32[1]; audio_block_f32_t *inputQueueArray_f32[1];
FFT_Overlapped_F32 myFFT; FFT_Overlapped_OA_F32 myFFT;
IFFT_Overlapped_F32 myIFFT; IFFT_Overlapped_OA_F32 myIFFT;
float lowpass_freq_Hz = 1000.f; float lowpass_freq_Hz = 1000.f;
float sample_rate_Hz = AUDIO_SAMPLE_RATE; 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 float shift_scale_fac = 1.0; //how much to shift formants (frequency multiplier). 1.0 is no shift
}; };

@ -1,4 +1,4 @@
/* FormantShifter_FD.ino /* FormantShifter_FD_OA.ino
* *
* Demonstrate formant shifting via frequency domain processin. * Demonstrate formant shifting via frequency domain processin.
* *
@ -17,25 +17,32 @@
* Built for the Tympan library for Teensy 3.6-based hardware * Built for the Tympan library for Teensy 3.6-based hardware
* *
* Adapt to OpenAudio Library - Bob Larkin June 2020 * 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.
* *
* MIT License. Use at your own risk. * 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 "AudioStream_F32.h"
#include "OpenAudio_ArduinoLibrary.h" #include "OpenAudio_ArduinoLibrary.h"
#include "AudioEffectFormantShiftFD_OA_F32.h" //the local file holding your custom function #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 //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 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 choose a power of 2 (16, 32, 64, 128) but no higher than 128 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); AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples);
//create audio library objects for handling the audio //create audio library objects for handling the audio
AudioInputI2S i2sIn; AudioInputI2S i2sIn; // This I16 input/output is T4.x compatible
AudioConvert_I16toF32 cnvrt1; AudioConvert_I16toF32 cnvrt1; // Cobevrt to float
AudioEffectFormantShiftFD_F32 formantShift(audio_settings); //create the frequency-domain processing block AudioEffectFormantShiftFD_OA_F32 formantShift(audio_settings); // the frequency-domain processing block
AudioEffectGain_F32 gain1; //Applies digital gain to audio data. AudioEffectGain_F32 gain1; //Applies digital gain to audio data.
AudioConvert_F32toI16 cnvrt2; AudioConvert_F32toI16 cnvrt2;
AudioOutputI2S i2sOut; AudioOutputI2S i2sOut;
@ -43,106 +50,59 @@ AudioControlSGTL5000 codec;
//Make all of the audio connections //Make all of the audio connections
AudioConnection patchCord1(i2sIn, 0, cnvrt1, 0); // connect to Left codec, 16-bit AudioConnection patchCord1(i2sIn, 0, cnvrt1, 0); // connect to Left codec, 16-bit
AudioConnection_F32 patchCord2(cnvrt1, 0, formantShift, 0); AudioConnection_F32 patchCord2(cnvrt1, 0, formantShift, 0);
AudioConnection_F32 patchCord2(formantShift, 0, gain1, 0); //connect to gain AudioConnection_F32 patchCord3(formantShift, 0, gain1, 0); //connect to gain
AudioConnection_F32 patchCord3(gain1, 0, cnvrt2, 0); //connect to the left output AudioConnection_F32 patchCord4(gain1, 0, cnvrt2, 0); //connect to the left output
AudioConnection patchCord6(cnvrt2, 0, i2sOut, 0); AudioConnection patchCord6(cnvrt2, 0, i2sOut, 0);
//control display and serial interaction //control display and serial interaction
bool enable_printCPUandMemory = false; bool enable_printCPUandMemory = false;
void togglePrintMemoryAndCPU(void) { enable_printCPUandMemory = !enable_printCPUandMemory; }; void togglePrintMemoryAndCPU(void) { enable_printCPUandMemory = !enable_printCPUandMemory; };
SerialManager_OA SerialManager_OA(audioHardware); SerialManagerFormant_OA SerMgr;
//inputs and levels //inputs and levels
float input_gain_dB = 20.0f; //gain on the microphone 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 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() { void setup() {
Serial.begin(1); delay(1000); Serial.begin(1); delay(1000);
mySerial.println("FormantShifter: starting setup()..."); Serial.println("FormantShifter: starting setup()...");
mySerial.print(" : sample rate (Hz) = "); mySerial.println(audio_settings.sample_rate_Hz); Serial.print(" : sample rate (Hz) = "); Serial.println(audio_settings.sample_rate_Hz);
mySerial.print(" : block size (samples) = "); mySerial.println(audio_settings.audio_block_samples); Serial.print(" : block size (samples) = "); Serial.println(audio_settings.audio_block_samples);
// Audio connections require memory to work. For more // Audio connections require memory to work. For more
// detailed information, see the MemoryAndCpuUsage example // detailed information, see the MemoryAndCpuUsage example
AudioMemory(3); // I16 type AudioMemory(3); // I16 type
AudioMemory_F32(40, audio_settings); AudioMemory_F32(40, audio_settings);
codec.enable();
codec.adcHighPassFilterEnable();
codec.inputSelect(AUDIO_INPUT_LINEIN);
// Configure the frequency-domain algorithm // 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 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; int N_FFT = audio_block_samples * overlap_factor;
formantShift.setup(audio_settings, N_FFT); //do after AudioMemory_F32(); 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) { if (overlap_factor == 4) {
formant_shift_gain_correction_dB = -3.0; formant_shift_gain_correction_dB = -3.0;
} else if (overlap_factor == 8) { } else if (overlap_factor == 8) {
formant_shift_gain_correction_dB = -9.0; formant_shift_gain_correction_dB = -9.0;
} }
SerMgr.printHelp();
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();
} }
// ************************* LOOP *********************
// define the loop() function, the function that is repeated over and over for the life of the device
void loop() { void loop() {
//respond to Serial commands //respond to Serial commands
while (Serial.available()) SerialManager_OA.respondToByte((char)Serial.read()); //USB Serial while (Serial.available()) SerMgr.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
//check to see whether to print the CPU and Memory Usage //check to see whether to print the CPU and Memory Usage
if (enable_printCPUandMemory) printCPUandMemory(millis(), 3000); //print every 3000 msec 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 //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) { void printCPUandMemory(unsigned long curTime_millis, unsigned long updatePeriod_millis) {
@ -177,14 +137,13 @@ void printGainSettings(void) {
Serial.println(); Serial.println();
} }
void incrementKnobGain(float increment_dB) {
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);
setVolKnobGain_dB(vol_knob_gain_dB+increment_dB);
} }
void setVolKnobGain_dB(float gain_dB) { void setVolKnobGain_dB(float gain_dB) {
vol_knob_gain_dB = 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(); printGainSettings();
} }

@ -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 #include <OpenAudio_ArduinoLibrary.h>
#define _SerialManager_OA_h
#include <Tympan_Library.h>
//now, define the Serial Manager class //now, define the Serial Manager class
class SerialManager_OA { class SerialManagerFormant_OA {
public: public:
public:
SerialManager_OA(Tympan &_audioHardware)
: audioHardware(_audioHardware)
{ };
//SerialManager_OA(void)
//{ };
void respondToByte(char c); void respondToByte(char c);
void printHelp(void); void printHelp(void);
int N_CHAN; int N_CHAN;
float channelGainIncrement_dB = 2.5f; float channelGainIncrement_dB = 2.5f;
float formantScaleIncrement = powf(2.0,1.0/6.0); float formantScaleIncrement = powf(2.0,1.0/6.0);
private:
Tympan &audioHardware;
}; };
#define thisSerial audioHardware
void SerialManager_OA::printHelp(void) { void SerialManagerFormant_OA::printHelp(void) {
thisSerial.println(); Serial.println();
thisSerial.println("SerialManager_OA Help: Available Commands:"); Serial.println("SerialManager_OA Help: Available Commands:");
thisSerial.println(" h: Print this help"); Serial.println(" h: Print this help");
thisSerial.println(" g: Print the gain settings of the device."); Serial.println(" g: Print the gain settings of the device.");
thisSerial.println(" C: Toggle printing of CPU and Memory usage"); Serial.println(" C: Toggle printing of CPU and Memory usage");
thisSerial.println(" p: Switch to built-in PCB microphones"); Serial.print(" k: Increase the audio by ");
thisSerial.println(" m: switch to external mic via mic jack"); Serial.print(channelGainIncrement_dB);
thisSerial.println(" l: switch to line-in via mic jack"); Serial.println(" dB");
thisSerial.print(" k: Increase the gain of all channels (ie, knob gain) by "); thisSerial.print(channelGainIncrement_dB); thisSerial.println(" dB"); Serial.print(" K: Decrease the audio gain by ");
thisSerial.print(" K: Decrease the gain of all channels (ie, knob gain) by ");thisSerial.print(-channelGainIncrement_dB); thisSerial.println(" dB"); Serial.print(-channelGainIncrement_dB);
thisSerial.print(" f: Raise formant shifting (change by "); thisSerial.print(formantScaleIncrement); thisSerial.println("x)"); Serial.println(" dB");
thisSerial.print(" F: Lower formant shifting (change by "); thisSerial.print(1.0/formantScaleIncrement); thisSerial.println("x)"); thisSerial.println(); 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 //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 printGainSettings(void);
extern void togglePrintMemoryAndCPU(void); extern void togglePrintMemoryAndCPU(void);
extern float incrementFormantShift(float); extern float incrementFormantShift(float);
extern void switchToPCBMics(void); //extern void switchToPCBMics(void);
extern void switchToMicInOnMicJack(void); //extern void switchToMicInOnMicJack(void);
extern void switchToLineInOnMicJack(void); //extern void switchToLineInOnMicJack(void);
//switch yard to determine the desired action //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; //float old_val = 0.0, new_val = 0.0;
switch (c) { switch (c) {
case 'h': case '?': case 'h': case '?':
@ -64,24 +67,16 @@ void SerialManager_OA::respondToByte(char c) {
case 'K': //which is "shift k" case 'K': //which is "shift k"
incrementKnobGain(-channelGainIncrement_dB); break; incrementKnobGain(-channelGainIncrement_dB); break;
case 'C': case 'c': 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; togglePrintMemoryAndCPU(); break;
case 'p':
switchToPCBMics(); break;
case 'm':
switchToMicInOnMicJack(); break;
case 'l':
switchToLineInOnMicJack();break;
case 'f': case 'f':
{ float new_val = incrementFormantShift(formantScaleIncrement); { 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; break;
case 'F': case 'F':
{ float new_val = incrementFormantShift(1./formantScaleIncrement); { 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; break;
} }
} }
#endif #endif

@ -17,6 +17,7 @@ OpenAudio Library for Teensy
12-Added /examples/Switches_float.ino for 4 and 8 channel switches. 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. 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 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. **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.

Loading…
Cancel
Save