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.
180 lines
7.2 KiB
180 lines
7.2 KiB
4 years ago
|
/* FreqShifter_FD_OA.ino
|
||
|
*
|
||
|
* Demonstrate frequency shifting via frequency domain processin.
|
||
|
*
|
||
|
* Created: Chip Audette (OpenAudio) Aug 2019
|
||
|
*
|
||
|
* Approach: This processing is performed in the frequency domain.
|
||
|
* Frequencies can only be shifted by an integer number of bins,
|
||
|
* so small frequency shifts are not possible. For example, for
|
||
|
* a sample rate of 44.1kHz, and when using N=256, one can only
|
||
|
* shift frequencies in multiples of 44.1/256 = 172.3 Hz.
|
||
|
*
|
||
|
* This processing is performed in the frequency domain where
|
||
|
* we take the FFT, shift the bins upward or downward, take
|
||
|
* the IFFT, and listen to the results. In effect, this is
|
||
|
* single sideband modulation, which will sound very unnatural
|
||
|
* (like robot voices!). Maybe you'll like it, or maybe not.
|
||
|
* Probably not, unless you like weird. ;)
|
||
|
*
|
||
|
* You can shift frequencies upward or downward with this algorithm.
|
||
|
*
|
||
|
* Frequency Domain Processing:
|
||
|
* * Take samples in the time domain
|
||
|
* * Take FFT to convert to frequency domain
|
||
|
* * Manipulate the frequency bins to do the freqyebct shifting
|
||
|
* * Take IFFT to convert back to time domain
|
||
|
* * Send samples back to the audio interface
|
||
|
*
|
||
|
* Built for the Tympan library for Teensy 3.6-based hardware
|
||
|
*
|
||
|
* Convert to Open Audio Bob Larkin June 2020
|
||
|
* Tested OK for Teensy 3.6 and 4.0.
|
||
|
* For settings:
|
||
|
* sample rate (Hz) = 44117.00
|
||
|
* block size (samples) = 128
|
||
|
* N_FFT = 512
|
||
|
* the following resources were used for Teensy 3.6
|
||
|
* CPU Cur/Peak: 50.02%/50.24%
|
||
|
* MEM Float32 Cur/Peak: 8/9
|
||
|
* MEM Int16 Cur/Peak: 3/5
|
||
|
* For Teensy 4.0:
|
||
|
* CPU Cur/Peak: 6.53%/6.84%
|
||
|
* MEM Float32 Cur/Peak: 8/9
|
||
|
* MEM Int16 Cur/Peak: 3/4
|
||
|
*
|
||
|
* MIT License. Use at your own risk.
|
||
|
*/
|
||
|
|
||
|
#include "Audio.h"
|
||
|
#include "AudioStream_F32.h"
|
||
|
#include "OpenAudio_ArduinoLibrary.h"
|
||
|
#include "SerialManager_FreqShift_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; // Convert to float
|
||
|
AudioEffectFreqShiftFD_OA_F32 freqShift(audio_settings); // the frequency-domain processing block
|
||
|
AudioEffectGain_F32 gain1; //Applies digital gain to audio data.
|
||
|
AudioConvert_F32toI16 cnvrt2;
|
||
|
AudioOutputI2S i2sOut;
|
||
|
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, freqShift, 0);
|
||
|
AudioConnection_F32 patchCord3(freqShift, 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; };
|
||
|
SerialManagerFreqShift_OA serialMgr;
|
||
|
|
||
|
//inputs and levels
|
||
|
float input_gain_dB = 15.0f; //gain on the microphone
|
||
|
float vol_knob_gain_dB = 0.0; //will be overridden by volume knob
|
||
|
|
||
|
// *************** SETUP **********************************
|
||
|
void setup() {
|
||
|
Serial.begin(1); delay(1000);
|
||
|
Serial.println("freqShifter: 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);
|
||
|
|
||
|
// Configure the FFT parameters algorithm
|
||
|
int overlap_factor = 4; //set to 2, 4 or 8...which yields 50%, 75%, or 87.5% overlap (8x)
|
||
|
int N_FFT = audio_block_samples * overlap_factor;
|
||
|
Serial.print(" : N_FFT = "); Serial.println(N_FFT);
|
||
|
freqShift.setup(audio_settings, N_FFT); //do after AudioMemory_F32();
|
||
|
|
||
|
//configure the frequency shifting
|
||
|
float shiftFreq_Hz = 750.0; //shift audio upward a bit
|
||
|
float Hz_per_bin = audio_settings.sample_rate_Hz / ((float)N_FFT);
|
||
|
int shift_bins = (int)(shiftFreq_Hz / Hz_per_bin + 0.5); //round to nearest bin
|
||
|
|
||
|
shiftFreq_Hz = shift_bins * Hz_per_bin;
|
||
|
Serial.print("Setting shift to "); Serial.print(shiftFreq_Hz);
|
||
|
Serial.print(" Hz, which is "); Serial.print(shift_bins);
|
||
|
Serial.println(" bins");
|
||
|
freqShift.setShift_bins(shift_bins); //0 is no ffreq shifting.
|
||
|
|
||
|
//Enable the Tympan to start the audio flowing!
|
||
|
codec.enable();
|
||
|
codec.adcHighPassFilterEnable();
|
||
|
codec.inputSelect(AUDIO_INPUT_LINEIN);
|
||
|
|
||
|
//finish the setup by printing the help menu to the serial connections
|
||
|
serialMgr.printHelp();
|
||
|
}
|
||
|
|
||
|
// ************************* LOOP ****************************
|
||
|
void loop() {
|
||
|
|
||
|
//respond to Serial commands
|
||
|
while (Serial.available()) serialMgr.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();
|
||
|
|
||
|
//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("CPU Cur/Peak: ");
|
||
|
Serial.print(audio_settings.processorUsage());
|
||
|
Serial.print("%/");
|
||
|
Serial.print(audio_settings.processorUsageMax());
|
||
|
Serial.print("%, ");
|
||
|
Serial.print(" Dyn MEM Float32 Cur/Peak: ");
|
||
|
Serial.print(AudioMemoryUsage_F32());
|
||
|
Serial.print("/");
|
||
|
Serial.print(AudioMemoryUsageMax_F32());
|
||
|
Serial.print(" MEM Int16 Cur/Peak: ");
|
||
|
Serial.print(AudioMemoryUsage());
|
||
|
Serial.print("/");
|
||
|
Serial.print(AudioMemoryUsageMax());
|
||
|
Serial.println();
|
||
|
|
||
|
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);
|
||
|
printGainSettings();
|
||
|
}
|
||
|
|
||
|
int incrementFreqShift(int incr_factor) {
|
||
|
int cur_shift_bins = freqShift.getShift_bins();
|
||
|
return freqShift.setShift_bins(cur_shift_bins + incr_factor);
|
||
|
}
|