parent
dbf79e1d1e
commit
766bb48788
@ -0,0 +1,26 @@ |
|||||||
|
/*
|
||||||
|
* AudioSwitch_F32.cpp see AudioSwitch_F32.h for notes |
||||||
|
* Created: Chip Audette, OpenAudio, April 2019 |
||||||
|
* MIT License. use at your own risk. |
||||||
|
*/ |
||||||
|
#include "AudioSwitch_F32.h" |
||||||
|
|
||||||
|
void AudioSwitch4_OA_F32::update(void) { |
||||||
|
audio_block_f32_t *out=NULL; |
||||||
|
|
||||||
|
out = receiveReadOnly_f32(0); |
||||||
|
if (!out) return; |
||||||
|
|
||||||
|
AudioStream_F32::transmit(out,outputChannel); //just output to the one channel
|
||||||
|
AudioStream_F32::release(out); |
||||||
|
} |
||||||
|
|
||||||
|
void AudioSwitch8_OA_F32::update(void) { |
||||||
|
audio_block_f32_t *out=NULL; |
||||||
|
|
||||||
|
out = receiveReadOnly_f32(0); |
||||||
|
if (!out) return; |
||||||
|
|
||||||
|
AudioStream_F32::transmit(out,outputChannel); |
||||||
|
AudioStream_F32::release(out); |
||||||
|
} |
@ -0,0 +1,68 @@ |
|||||||
|
/*
|
||||||
|
* AudioSwitch_F32.h |
||||||
|
*
|
||||||
|
* AudioSwitch4 |
||||||
|
* Created: Chip Audette, OpenAudio, April 2019 |
||||||
|
* Purpose: Switch one input to 4 outputs, which should only trigger one of the 4 |
||||||
|
* audio processing paths (therebys saving computation on paths that you don't |
||||||
|
* care about). |
||||||
|
* Assumes floating-point data. |
||||||
|
* From Tympan Library, to OpenAudio_ArduinoLibrary June 2020, add 8 position class. |
||||||
|
*
|
||||||
|
* This processes a single stream of audio data (ie, it is mono)
|
||||||
|
*
|
||||||
|
* MIT License. use at your own risk. |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef AUDIOSWITCH_OA_F32_H |
||||||
|
#define AUDIOSWITCH_OA_F32_H |
||||||
|
|
||||||
|
#include <AudioStream_F32.h> |
||||||
|
|
||||||
|
class AudioSwitch4_OA_F32 : public AudioStream_F32 { |
||||||
|
//GUI: inputs:1, outputs:4 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName:Switch4
|
||||||
|
public: |
||||||
|
AudioSwitch4_OA_F32() : AudioStream_F32(1, inputQueueArray) { setDefaultValues(); } |
||||||
|
AudioSwitch4_OA_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray) { setDefaultValues(); } |
||||||
|
|
||||||
|
void setDefaultValues(void) { |
||||||
|
outputChannel = 0; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void update(void); |
||||||
|
|
||||||
|
int setChannel(unsigned int channel) { |
||||||
|
if (channel >= 4 || channel < 0) return outputChannel; //invalid! stick with previous channel
|
||||||
|
return outputChannel = channel; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
audio_block_f32_t *inputQueueArray[1]; |
||||||
|
int outputChannel; |
||||||
|
}; |
||||||
|
|
||||||
|
class AudioSwitch8_OA_F32 : public AudioStream_F32 { |
||||||
|
//GUI: inputs:1, outputs:8 //this line used for automatic generation of GUI node
|
||||||
|
//GUI: shortName:Switch8
|
||||||
|
public: |
||||||
|
AudioSwitch8_OA_F32() : AudioStream_F32(1, inputQueueArray) { setDefaultValues(); } |
||||||
|
AudioSwitch8_OA_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray) { setDefaultValues(); } |
||||||
|
|
||||||
|
void setDefaultValues(void) { |
||||||
|
outputChannel = 0; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void update(void); |
||||||
|
|
||||||
|
int setChannel(unsigned int channel) { |
||||||
|
if (channel >= 8 || channel < 0) return outputChannel; //invalid! stick with previous channel
|
||||||
|
return outputChannel = channel; |
||||||
|
} |
||||||
|
|
||||||
|
private: |
||||||
|
audio_block_f32_t *inputQueueArray[1]; |
||||||
|
int outputChannel; |
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,175 @@ |
|||||||
|
|
||||||
|
/*
|
||||||
|
* FFT_F32 |
||||||
|
*
|
||||||
|
* Purpose: Encapsulate the ARM floating point FFT/IFFT functions |
||||||
|
* in a way that naturally handles the radix2 vs radix4 |
||||||
|
* constraints on FFT size inherent to the particular |
||||||
|
* version of the ARM CMSIS FFT functions included with |
||||||
|
* the Teensy libraries. |
||||||
|
*
|
||||||
|
* Created: Chip Audette (openaudio.blogspot.com) |
||||||
|
* Jan-Jul 2017 |
||||||
|
*
|
||||||
|
* License: MIT License |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef _FFT_h |
||||||
|
#define _FFT_h |
||||||
|
|
||||||
|
#include <Arduino.h> //for Serial |
||||||
|
//include <math.h>
|
||||||
|
#include <arm_math.h> |
||||||
|
|
||||||
|
class FFT_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
FFT_F32(void) {}; |
||||||
|
FFT_F32(const int _N_FFT) { |
||||||
|
setup(_N_FFT); |
||||||
|
} |
||||||
|
FFT_F32(const int _N_FFT, const int _is_IFFT) { |
||||||
|
setup(_N_FFT, _is_IFFT); |
||||||
|
} |
||||||
|
~FFT_F32(void) { delete window; }; //destructor
|
||||||
|
|
||||||
|
virtual int setup(const int _N_FFT) { |
||||||
|
int _is_IFFT = 0; |
||||||
|
return setup(_N_FFT,_is_IFFT); |
||||||
|
} |
||||||
|
virtual int setup(const int _N_FFT, const int _is_IFFT) { |
||||||
|
if (!is_valid_N_FFT(_N_FFT)) { |
||||||
|
Serial.println(F("FFT_F32: *** ERROR ***")); |
||||||
|
Serial.print(F(" : Cannot use N_FFT = ")); Serial.println(N_FFT); |
||||||
|
Serial.print(F(" : Must be power of 2 between 16 and 2048")); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
N_FFT = _N_FFT; |
||||||
|
is_IFFT = _is_IFFT; |
||||||
|
|
||||||
|
if ((N_FFT == 16) || (N_FFT == 64) || (N_FFT == 256) || (N_FFT == 1024)) { |
||||||
|
arm_cfft_radix4_init_f32(&fft_inst_r4, N_FFT, is_IFFT, 1); //FFT
|
||||||
|
is_rad4 = 1; |
||||||
|
} else { |
||||||
|
arm_cfft_radix2_init_f32(&fft_inst_r2, N_FFT, is_IFFT, 1); //FFT
|
||||||
|
} |
||||||
|
|
||||||
|
//allocate window
|
||||||
|
window = new float[N_FFT]; |
||||||
|
if (is_IFFT) { |
||||||
|
useRectangularWindow(); //default to no windowing for IFFT
|
||||||
|
} else { |
||||||
|
useHanningWindow(); //default to windowing for FFT
|
||||||
|
} |
||||||
|
return N_FFT; |
||||||
|
} |
||||||
|
static int is_valid_N_FFT(const int N) { |
||||||
|
if ((N == 16) || (N == 32) || (N == 64) || (N == 128) ||
|
||||||
|
(N == 256) || (N == 512) || (N==1024) || (N==2048)) { |
||||||
|
return 1; |
||||||
|
} else { |
||||||
|
return 0; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
virtual void useRectangularWindow(void) { |
||||||
|
flag__useWindow = 0; |
||||||
|
if (window != NULL) { |
||||||
|
for (int i=0; i < N_FFT; i++) window[i] = 1.0;
|
||||||
|
} |
||||||
|
//if (Serial) { Serial.print("FFT_F32: useRectangularWindow. flag__useWindow = "); Serial.println(flag__useWindow); }
|
||||||
|
} |
||||||
|
virtual void useHanningWindow(void) { |
||||||
|
flag__useWindow = 1; |
||||||
|
if (window != NULL) { |
||||||
|
//Serial.print("FFT_F32: useHanningWindow, N=");
|
||||||
|
//Serial.print(N_FFT); Serial.print(": ");
|
||||||
|
for (int i=0; i < N_FFT; i++) {
|
||||||
|
window[i] = 0.5*(1.0 - cosf(2.0*M_PI*(float)i/((float)(N_FFT-1)))); |
||||||
|
//Serial.print(window[i]);
|
||||||
|
//Serial.print(" ");
|
||||||
|
} |
||||||
|
//Serial.println();
|
||||||
|
} |
||||||
|
//if (Serial) { Serial.print("FFT_F32: useHanningWindow. flag__useWindow = "); Serial.println(flag__useWindow); }
|
||||||
|
} |
||||||
|
|
||||||
|
virtual void applyWindowToRealPartOfComplexVector(float32_t *complex_2N_buffer) { |
||||||
|
for (int i=0; i < N_FFT; i++) { |
||||||
|
complex_2N_buffer[2*i] *= window[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
virtual void applyWindowToRealVector(float32_t *real_N_buffer) { |
||||||
|
for (int i=0; i < N_FFT; i++) { |
||||||
|
real_N_buffer[i] *= window[i]; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
virtual void execute(float32_t *complex_2N_buffer) { //interleaved [real,imaginary], total length is 2*N_FFT
|
||||||
|
if (N_FFT == 0) return; |
||||||
|
|
||||||
|
//apply window before FFT (if it is an FFT and not IFFT)
|
||||||
|
if ((!is_IFFT) && (flag__useWindow)) applyWindowToRealPartOfComplexVector(complex_2N_buffer); |
||||||
|
|
||||||
|
//do the FFT
|
||||||
|
if (is_rad4) { |
||||||
|
arm_cfft_radix4_f32(&fft_inst_r4, complex_2N_buffer); |
||||||
|
} else { |
||||||
|
arm_cfft_radix2_f32(&fft_inst_r2, complex_2N_buffer); |
||||||
|
} |
||||||
|
|
||||||
|
//apply window after FFT (if it is an IFFT and not FFT)
|
||||||
|
if ((is_IFFT) && (flag__useWindow)) applyWindowToRealPartOfComplexVector(complex_2N_buffer); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
virtual void rebuildNegativeFrequencySpace(float *complex_2N_buffer) { |
||||||
|
//create the negative frequency space via complex conjugate of the positive frequency space
|
||||||
|
|
||||||
|
//int ind_nyquist_bin = N_FFT/2; //nyquist is neither positive nor negative
|
||||||
|
//int targ_ind = ind_nyquist_bin+1; //negative frequencies start start one above nyquist
|
||||||
|
//for (int source_ind = ind_nyquist_bin-1; source_ind > 0; source_ind--) { //exclude the 0'th bin as DC is neither positive nor negative
|
||||||
|
//complex_2N_buffer[2*targ_ind] = complex_2N_buffer[2*source_ind]; //real
|
||||||
|
//complex_2N_buffer[2*targ_ind+1] = -complex_2N_buffer[2*source_ind+1]; //imaginary. negative makes it the complex conjugate, which is what we want for the neg freq space
|
||||||
|
//targ_ind++;
|
||||||
|
//}
|
||||||
|
|
||||||
|
int targ_ind = 0; |
||||||
|
for (int source_ind = 1; source_ind < (N_FFT/2-1); source_ind++) { |
||||||
|
targ_ind = N_FFT - source_ind; |
||||||
|
complex_2N_buffer[2*targ_ind] = complex_2N_buffer[2*source_ind]; //real
|
||||||
|
complex_2N_buffer[2*targ_ind+1] = -complex_2N_buffer[2*source_ind+1]; //imaginary. negative makes it the complex conjugate, which is what we want for the neg freq space
|
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
virtual int getNFFT(void) { return N_FFT; }; |
||||||
|
int get_flagUseWindow(void) { return flag__useWindow; }; |
||||||
|
|
||||||
|
private: |
||||||
|
int N_FFT=0; |
||||||
|
int is_IFFT=0; |
||||||
|
int is_rad4=0; |
||||||
|
float *window; |
||||||
|
int flag__useWindow=0; |
||||||
|
arm_cfft_radix4_instance_f32 fft_inst_r4; |
||||||
|
arm_cfft_radix2_instance_f32 fft_inst_r2; |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
class IFFT_F32 : public FFT_F32 // is basically the same as FFT, so let's inherent FFT
|
||||||
|
{ |
||||||
|
public: |
||||||
|
IFFT_F32(void) : FFT_F32() {}; |
||||||
|
IFFT_F32(const int _N_FFT): FFT_F32(_N_FFT) { |
||||||
|
//constructor
|
||||||
|
IFFT_F32::setup(_N_FFT); //call FFT's setup routine
|
||||||
|
} |
||||||
|
virtual int setup(const int _N_FFT) { |
||||||
|
const int _is_IFFT = 1; |
||||||
|
return FFT_F32::setup(_N_FFT, _is_IFFT); //call FFT's setup routine
|
||||||
|
} |
||||||
|
//all other functions are in FFT
|
||||||
|
}; |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,70 @@ |
|||||||
|
|
||||||
|
#include "FFT_Overlapped_F32.h" |
||||||
|
|
||||||
|
void FFT_Overlapped_F32::execute(audio_block_f32_t *block, float *complex_2N_buffer) //results returned inc omplex_2N_buffer
|
||||||
|
{ |
||||||
|
int targ_ind; |
||||||
|
|
||||||
|
//get a pointer to the latest data
|
||||||
|
//audio_block_f32_t *block = AudioStream_F32::receiveReadOnly_f32();
|
||||||
|
if (!block) return; |
||||||
|
|
||||||
|
//add a claim to this block. As a result, be sure that this function issues a "release()".
|
||||||
|
//Also, be sure that the calling function issues its own release() to release its claim.
|
||||||
|
block->ref_count++;
|
||||||
|
|
||||||
|
//shuffle all of input data blocks in preperation for this latest processing
|
||||||
|
AudioStream_F32::release(buff_blocks[0]); //release the oldest one...this is the release the corresponds to the claim above
|
||||||
|
for (int i = 1; i < N_BUFF_BLOCKS; i++) buff_blocks[i - 1] = buff_blocks[i]; |
||||||
|
buff_blocks[N_BUFF_BLOCKS - 1] = block; //append the newest input data to the complex_buffer blocks
|
||||||
|
|
||||||
|
//copy all input data blocks into one big block...the big block is interleaved [real,imaginary]
|
||||||
|
targ_ind = 0; |
||||||
|
//Serial.print("Overlapped_FFT_F32: N_BUFF_BLOCKS = "); Serial.print(N_BUFF_BLOCKS);
|
||||||
|
//Serial.print(", audio_block_samples = "); Serial.println(audio_block_samples);
|
||||||
|
for (int i = 0; i < N_BUFF_BLOCKS; i++) { |
||||||
|
for (int j = 0; j < audio_block_samples; j++) { |
||||||
|
complex_2N_buffer[2*targ_ind] = buff_blocks[i]->data[j]; //real
|
||||||
|
complex_2N_buffer[2*targ_ind+1] = 0; //imaginary
|
||||||
|
targ_ind++; |
||||||
|
} |
||||||
|
} |
||||||
|
//call the FFT...windowing of the data happens in the FFT routine, if configured
|
||||||
|
myFFT.execute(complex_2N_buffer); |
||||||
|
} |
||||||
|
|
||||||
|
audio_block_f32_t* IFFT_Overlapped_F32::execute(float *complex_2N_buffer) { //real results returned through audio_block_f32_t
|
||||||
|
|
||||||
|
//Serial.print("Overlapped_IFFT_F32: N_BUFF_BLOCKS = "); Serial.print(N_BUFF_BLOCKS);
|
||||||
|
//Serial.print(", audio_block_samples = "); Serial.println(audio_block_samples);
|
||||||
|
|
||||||
|
|
||||||
|
//call the IFFT...any follow-up windowing is handdled in the IFFT routine, if configured
|
||||||
|
myIFFT.execute(complex_2N_buffer); |
||||||
|
|
||||||
|
|
||||||
|
//prepare for the overlap-and-add for the output
|
||||||
|
audio_block_f32_t *temp_buff = buff_blocks[0]; //hold onto this one for a moment...it'll get overwritten later
|
||||||
|
for (int i = 1; i < N_BUFF_BLOCKS; i++) buff_blocks[i - 1] = buff_blocks[i]; //shuffle the output data blocks
|
||||||
|
buff_blocks[N_BUFF_BLOCKS - 1] = temp_buff; //put the oldest output buffer back in the list
|
||||||
|
|
||||||
|
//do overlap and add with previously computed data
|
||||||
|
int output_count = 0; |
||||||
|
for (int i = 0; i < (N_BUFF_BLOCKS-1); i++) { //Notice that this loop does NOT do the last block. That's a special case after.
|
||||||
|
for (int j = 0; j < audio_block_samples; j++) { |
||||||
|
buff_blocks[i]->data[j] += complex_2N_buffer[2*output_count]; //add only the real part into the previous results
|
||||||
|
output_count++; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
//now write in the newest data into the last block, overwriting any garbage that might have existed there
|
||||||
|
for (int j = 0; j < audio_block_samples; j++) { |
||||||
|
buff_blocks[N_BUFF_BLOCKS - 1]->data[j] = complex_2N_buffer[2*output_count]; //overwrite with the newest data
|
||||||
|
output_count++; |
||||||
|
} |
||||||
|
|
||||||
|
//send the oldest data. Don't issue the release command here because we will release it the next time through this routine
|
||||||
|
//transmit(buff_blocks[0]); //don't release this buffer because we re-use it every time this is called
|
||||||
|
return buff_blocks[0]; //send back the pointer to this audio block...but don't release it because we'll re-use it here
|
||||||
|
}; |
||||||
|
|
@ -0,0 +1,177 @@ |
|||||||
|
/*
|
||||||
|
* FFT_Overrlapped_F32 |
||||||
|
*
|
||||||
|
* Purpose: Encapsulate the ARM floating point FFT/IFFT functions |
||||||
|
* in a way that naturally interfaces to my float32 |
||||||
|
* extension of the Teensy Audio Library. |
||||||
|
*
|
||||||
|
* Provides functionality to do overlapped FFT/IFFT where |
||||||
|
* each audio block is a fraction (1, 1/2, 1/4) of the |
||||||
|
* totaly FFT length. This class handles all of the |
||||||
|
* data shuffling to composite the previous data blocks |
||||||
|
* with the current data block to provide the full FFT. |
||||||
|
* Does similar data shuffling (overlapp-add) for IFFT. |
||||||
|
*
|
||||||
|
* Created: Chip Audette (openaudio.blogspot.com) |
||||||
|
* Jan-Jul 2017
|
||||||
|
*
|
||||||
|
* Typical Usage as FFT: |
||||||
|
*
|
||||||
|
* //setup the audio stuff
|
||||||
|
* float sample_rate_Hz = 44100.0; //define sample rate
|
||||||
|
* int audio_block_samples = 32; //define size of audio blocks
|
||||||
|
* AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples); |
||||||
|
* // ... continue creating all of your Audio Processing Blocks ...
|
||||||
|
*
|
||||||
|
* // within a custom audio processing algorithm that you've written
|
||||||
|
* // you'd create the FFT and IFFT elements
|
||||||
|
* int NFFT = 128; //define length of FFT that you want (multiple of audio_block_samples)
|
||||||
|
* FFT_Overrlapped_F32 FFT_obj(audio_settings,NFFT); //Creare FFT object
|
||||||
|
* FFT_Overrlapped_F32 IFFT_obj(audio_settings,NFFT); //Creare IFFT object
|
||||||
|
* float complex_2N_buffer[2*NFFT]; //create buffer to hold the FFT output
|
||||||
|
*
|
||||||
|
* // within your own algorithm's "update()" function (which is what
|
||||||
|
* // is called automatically by the Teensy Audio Libarary approach
|
||||||
|
* // to audio processing), you can execute the FFT and IFFT
|
||||||
|
*
|
||||||
|
* // First, get the audio and convert to frequency-domain using an FFT
|
||||||
|
* audio_block_f32_t *in_audio_block = AudioStream_F32::receiveReadOnly_f32(); |
||||||
|
* FFT_obj.execute(in_audio_block, complex_2N_buffer); //output is in complex_2N_buffer
|
||||||
|
* AudioStream_F32::release(in_audio_block); //We just passed ownership to FFT_obj, so release it here.
|
||||||
|
*
|
||||||
|
* // Next do whatever processing you'd like on the frequency domain data
|
||||||
|
* // that is held in complex_2N_buffer
|
||||||
|
*
|
||||||
|
* // Finally, you can convert back to the time domain via IFFT
|
||||||
|
* audio_block_f32_t *out_audio_block = IFFT_obj.execute(complex_2N_buffer);
|
||||||
|
* //note that the "out_audio_block" is mananged by IFFT_obj, so don't worry about releasing it.
|
||||||
|
*
|
||||||
|
* License: MIT License |
||||||
|
*/ |
||||||
|
#ifndef _FFT_Overlapped_F32_h |
||||||
|
#define _FFT_Overlapped_F32_h |
||||||
|
|
||||||
|
#include "AudioStream_F32.h" |
||||||
|
#include <arm_math.h> |
||||||
|
#include "FFT_F32.h" |
||||||
|
//#include "utility/dspinst.h" //copied from analyze_fft256.cpp. Do we need this?
|
||||||
|
|
||||||
|
// set the max amount of allowed overlap...some number larger than you'll want to use
|
||||||
|
#define MAX_N_BUFF_BLOCKS 32 //32 blocks x 16 sample blocks enables NFFT = 512, if the Teensy could keep up.
|
||||||
|
|
||||||
|
class FFT_Overlapped_Base_F32 { //handles all the data structures for the overlapping stuff. Doesn't care if FFT or IFFT
|
||||||
|
public: |
||||||
|
FFT_Overlapped_Base_F32(void) {}; |
||||||
|
~FFT_Overlapped_Base_F32(void) { |
||||||
|
if (N_BUFF_BLOCKS > 0) { |
||||||
|
for (int i = 0; i < N_BUFF_BLOCKS; i++) { |
||||||
|
if (buff_blocks[i] != NULL) AudioStream_F32::release(buff_blocks[i]); |
||||||
|
} |
||||||
|
} |
||||||
|
if (complex_buffer != NULL) delete complex_buffer; |
||||||
|
} |
||||||
|
|
||||||
|
virtual int setup(const AudioSettings_F32 &settings, const int _N_FFT) { |
||||||
|
int N_FFT; |
||||||
|
|
||||||
|
///choose valid _N_FFT
|
||||||
|
if (!FFT_F32::is_valid_N_FFT(_N_FFT)) { |
||||||
|
Serial.println(F("FFT_Overlapped_Base_F32: *** ERROR ***")); |
||||||
|
Serial.print(F(" : N_FFT ")); Serial.print(_N_FFT);
|
||||||
|
Serial.print(F(" is not allowed. Try a power of 2 between 16 and 2048")); |
||||||
|
N_FFT = -1; |
||||||
|
return N_FFT; |
||||||
|
} |
||||||
|
|
||||||
|
//how many buffers will compose each FFT?
|
||||||
|
audio_block_samples = settings.audio_block_samples; |
||||||
|
N_BUFF_BLOCKS = _N_FFT / audio_block_samples; //truncates!
|
||||||
|
N_BUFF_BLOCKS = max(1,min(MAX_N_BUFF_BLOCKS,N_BUFF_BLOCKS)); |
||||||
|
|
||||||
|
//what does the fft length actually end up being?
|
||||||
|
N_FFT = N_BUFF_BLOCKS * audio_block_samples; |
||||||
|
|
||||||
|
//allocate memory for buffers...this is dynamic allocation. Always dangerous.
|
||||||
|
complex_buffer = new float32_t[2*N_FFT]; //should I check to see if it was successfully allcoated?
|
||||||
|
|
||||||
|
//initialize the blocks for holding the previous data
|
||||||
|
for (int i = 0; i < N_BUFF_BLOCKS; i++) { |
||||||
|
buff_blocks[i] = AudioStream_F32::allocate_f32(); |
||||||
|
clear_audio_block(buff_blocks[i]); |
||||||
|
} |
||||||
|
|
||||||
|
return N_FFT; |
||||||
|
} |
||||||
|
virtual int getNFFT(void) = 0; |
||||||
|
virtual int getNBuffBlocks(void) { return N_BUFF_BLOCKS; } |
||||||
|
|
||||||
|
protected: |
||||||
|
int N_BUFF_BLOCKS = 0; |
||||||
|
int audio_block_samples; |
||||||
|
|
||||||
|
audio_block_f32_t *buff_blocks[MAX_N_BUFF_BLOCKS]; |
||||||
|
float32_t *complex_buffer; |
||||||
|
|
||||||
|
void clear_audio_block(audio_block_f32_t *block) { |
||||||
|
for (int i = 0; i < block->length; i++) block->data[i] = 0.f; |
||||||
|
}
|
||||||
|
}; |
||||||
|
|
||||||
|
class FFT_Overlapped_F32: public FFT_Overlapped_Base_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
//constructors
|
||||||
|
FFT_Overlapped_F32(void): FFT_Overlapped_Base_F32() {}; |
||||||
|
FFT_Overlapped_F32(const AudioSettings_F32 &settings): FFT_Overlapped_Base_F32() { } |
||||||
|
FFT_Overlapped_F32(const AudioSettings_F32 &settings, const int _N_FFT): FFT_Overlapped_Base_F32() {
|
||||||
|
setup(settings,_N_FFT); |
||||||
|
} |
||||||
|
|
||||||
|
virtual int setup(const AudioSettings_F32 &settings, const int _N_FFT) { |
||||||
|
int N_FFT = FFT_Overlapped_Base_F32::setup(settings, _N_FFT); |
||||||
|
|
||||||
|
//setup the FFT routines
|
||||||
|
N_FFT = myFFT.setup(N_FFT);
|
||||||
|
return N_FFT; |
||||||
|
} |
||||||
|
|
||||||
|
virtual void execute(audio_block_f32_t *block, float *complex_2N_buffer); |
||||||
|
virtual int getNFFT(void) { return myFFT.getNFFT(); }; |
||||||
|
FFT_F32* getFFTObject(void) { return &myFFT; }; |
||||||
|
virtual void rebuildNegativeFrequencySpace(float *complex_2N_buffer) { myFFT.rebuildNegativeFrequencySpace(complex_2N_buffer); } |
||||||
|
|
||||||
|
private: |
||||||
|
FFT_F32 myFFT; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
class IFFT_Overlapped_F32: public FFT_Overlapped_Base_F32 |
||||||
|
{ |
||||||
|
public: |
||||||
|
//constructors
|
||||||
|
IFFT_Overlapped_F32(void): FFT_Overlapped_Base_F32() {}; |
||||||
|
IFFT_Overlapped_F32(const AudioSettings_F32 &settings): FFT_Overlapped_Base_F32() { } |
||||||
|
IFFT_Overlapped_F32(const AudioSettings_F32 &settings, const int _N_FFT): FFT_Overlapped_Base_F32() {
|
||||||
|
setup(settings,_N_FFT); |
||||||
|
} |
||||||
|
|
||||||
|
virtual int setup(const AudioSettings_F32 &settings, const int _N_FFT) { |
||||||
|
int N_FFT = FFT_Overlapped_Base_F32::setup(settings, _N_FFT); |
||||||
|
|
||||||
|
//setup the FFT routines
|
||||||
|
N_FFT = myIFFT.setup(N_FFT);
|
||||||
|
return N_FFT; |
||||||
|
} |
||||||
|
|
||||||
|
virtual audio_block_f32_t* execute(float *complex_2N_buffer); |
||||||
|
virtual int getNFFT(void) { return myIFFT.getNFFT(); }; |
||||||
|
IFFT_F32* getFFTObject(void) { return &myIFFT; }; |
||||||
|
IFFT_F32* getIFFTObject(void) { return &myIFFT; }; |
||||||
|
private: |
||||||
|
IFFT_F32 myIFFT; |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,55 @@ |
|||||||
|
/* Switches_float.ino Bob Larkin 20 June 2020
|
||||||
|
* |
||||||
|
* Cascade an 4 position switch with an 8 position to swicth between |
||||||
|
* an RMS and a Peak measure of a sine wave. This is a trivial |
||||||
|
* example of switching. Normally, the switched paths would be involved |
||||||
|
* enough to benefit from saving computations and DC power. An example |
||||||
|
* that is more like this is <<<<<<<<<<<<<<<<<<<<<<<<<< |
||||||
|
* |
||||||
|
*/ |
||||||
|
|
||||||
|
#include "Audio.h" |
||||||
|
#include "OpenAudio_ArduinoLibrary.h" |
||||||
|
|
||||||
|
// To work with T4.0 the I2S routine outputs 16-bit integer (I16). Then
|
||||||
|
// use Audette I16 to F32 convert. Same below for output, in reverse.
|
||||||
|
AudioInputI2S in1; |
||||||
|
AudioSynthWaveformSine_F32 sine1; |
||||||
|
AudioSwitch4_OA_F32 switch1; |
||||||
|
AudioSwitch8_OA_F32 switch2; |
||||||
|
AudioAnalyzePeak_F32 peak1; |
||||||
|
AudioAnalyzeRMS_F32 rms1; |
||||||
|
AudioConnection_F32 connect1(sine1, 0, switch1, 0); |
||||||
|
AudioConnection_F32 connect2(switch1, 2, switch2, 0); |
||||||
|
AudioConnection_F32 connect3(switch2, 1, peak1, 0); |
||||||
|
AudioConnection_F32 connect4(switch2, 6, rms1, 0); |
||||||
|
|
||||||
|
|
||||||
|
void setup(void) { |
||||||
|
float32_t gain; |
||||||
|
AudioMemory(5); |
||||||
|
AudioMemory_F32(8); |
||||||
|
|
||||||
|
Serial.begin(1); delay(1000); |
||||||
|
sine1.frequency(1000.0f); |
||||||
|
sine1.amplitude(0.2f); |
||||||
|
// switch1 is always set to channel 2 to feed switch2.
|
||||||
|
switch1.setChannel(2); |
||||||
|
} |
||||||
|
|
||||||
|
void loop(void) { |
||||||
|
// Lets switch back and forth between RMS and Peak measurements.
|
||||||
|
// This is switch2, channels 6 and 1.
|
||||||
|
switch2.setChannel(6); // RMS measurement
|
||||||
|
delay(1000); |
||||||
|
if (rms1.available() ) { |
||||||
|
Serial.print("RMS ="); |
||||||
|
Serial.println(rms1.read(), 6); |
||||||
|
} |
||||||
|
switch2.setChannel(1); // Peak-to-peak measurement
|
||||||
|
delay(1000); |
||||||
|
if (peak1.available() ) { |
||||||
|
Serial.print("P-P ="); |
||||||
|
Serial.println(peak1.readPeakToPeak(), 6);} |
||||||
|
delay(1000); |
||||||
|
} |
Loading…
Reference in new issue