From 86b0b29a207ef1cfe39f70c97cc269585271480c Mon Sep 17 00:00:00 2001 From: boblark Date: Fri, 26 Feb 2021 16:05:48 -0800 Subject: [PATCH] major redo of AudioFilterBiquad_F32 --- AudioFilterBiquad_F32.cpp | 41 ++- AudioFilterBiquad_F32.h | 394 +++++++++++++++++------------ examples/TestBiquad/TestBiquad.ino | 121 +++++++++ 3 files changed, 366 insertions(+), 190 deletions(-) create mode 100644 examples/TestBiquad/TestBiquad.ino diff --git a/AudioFilterBiquad_F32.cpp b/AudioFilterBiquad_F32.cpp index 44a6fea..b397958 100644 --- a/AudioFilterBiquad_F32.cpp +++ b/AudioFilterBiquad_F32.cpp @@ -1,41 +1,32 @@ - /* * AudioFilterBiquad_F32.cpp * * Chip Audette, OpenAudio, Apr 2017 - * * MIT License, Use at your own risk. * + * This filter has been running in F32 as a single stage. This + * would work by using multiple instantations, but compute time and + * latency suffer. So, Feb 2021 convert to MAX_STAGES 4 as is the I16 + * Teensy Audio library. Bob Larkin + * + * See AudioFilterBiquad_F32.h for more notes. */ - - #include "AudioFilterBiquad_F32.h" -void AudioFilterBiquad_F32::update(void) -{ +void AudioFilterBiquad_F32::update(void) { audio_block_f32_t *block; block = AudioStream_F32::receiveWritable_f32(); - if (!block) return; - - // If there's no coefficient table, give up. - if (coeff_p == NULL) { - AudioStream_F32::release(block); - return; - } + if (!block) return; // Out of memory + // Serial.print(block->data[37],6); Serial.print(", IN "); Serial.println(block->data[38],6); + if(doBiquad) // Filter is defined, so go to it + arm_biquad_cascade_df1_f32(&iir_inst, block->data, + block->data, block->length); + // Serial.print(block->data[37],6); Serial.print(", OUT "); Serial.println(block->data[38],6); - // do passthru - if (coeff_p == IIR_F32_PASSTHRU) { - // Just passthrough - AudioStream_F32::transmit(block); - AudioStream_F32::release(block); - return; - } + // Add double filter call here - // do IIR - arm_biquad_cascade_df1_f32(&iir_inst, block->data, block->data, block->length); - - //transmit the data - AudioStream_F32::transmit(block); // send the IIR output + // Transmit the data, filtered or unfiltered + AudioStream_F32::transmit(block); AudioStream_F32::release(block); } diff --git a/AudioFilterBiquad_F32.h b/AudioFilterBiquad_F32.h index 2a2bd5c..f94a7f4 100644 --- a/AudioFilterBiquad_F32.h +++ b/AudioFilterBiquad_F32.h @@ -1,10 +1,29 @@ /* - * AudioFilterBiquad_F32 - * - * Created: Chip Audette (OpenAudio) Feb 2017 + * AudioFilterBiquad_F32.cpp + * Chip Audette, OpenAudio, Apr 2017 + * MIT License, Use at your own risk. + * + * This filter has been running in F32 as a single stage. This + * would work by using multiple instantations, but compute time and + * latency suffer. So, Feb 2021 convert to MAX_STAGES 4 as is the I16 + * Teensy Audio library. Bob Larrkin * - * License: MIT License. Use at your own risk. + * Float vs Double. There are times when double precision in the + * BiQuad calculation is needed to prevent + * serious numerical errors. This can be a processor time problem for + * T3.x. This routine (NOT QUITE YET) allows for either by + * a function with float as the default. This allows different BiQuads + * to use float or double. RSL + * + * NOTE: If your INO is broken, we had to do it. + * Some setting of coefficients also did a + * begin of the ARM CMSIS. We can't do that with multiple stages. If you + * encouter this, add myBiquad.begin(); to your INO after the + * coefficients have been set. Feb 2021 * + * The sign of the coefficients for feedback, the a[], here use the + * convention of the ARM CMSIS library. Matlab reverses the signs of these. + * I believe these are treated per those rules!! Bob */ #ifndef _filter_iir_f32 @@ -15,190 +34,235 @@ #include "arm_math.h" // Indicates that the code should just pass through the audio -// without any filtering (as opposed to doing nothing at all) +// without any filtering (as opposed to doing nothing at all) ,,,,,REMOVE???? WE HAVE DO_BIQUAD THAT DOES THISS #define IIR_F32_PASSTHRU ((const float32_t *) 1) -#define IIR_MAX_STAGES 1 //meaningless right now +// Changed Feb 2021 +#define IIR_MAX_STAGES 4 + +// T4.x can generally use doubles, they may be a burden for T3.x +// Leave commented out to compile for BOTH float and double +// See the function useDouble(bool d) below +// #define NEVER_DOUBLE class AudioFilterBiquad_F32 : public AudioStream_F32 { //GUI: inputs:1, outputs:1 //this line used for automatic generation of GUI node //GUI: shortName:IIR public: - AudioFilterBiquad_F32(void): AudioStream_F32(1,inputQueueArray), coeff_p(IIR_F32_PASSTHRU) { - setSampleRate_Hz(AUDIO_SAMPLE_RATE_EXACT); - } - AudioFilterBiquad_F32(const AudioSettings_F32 &settings): - AudioStream_F32(1,inputQueueArray), coeff_p(IIR_F32_PASSTHRU) { - setSampleRate_Hz(settings.sample_rate_Hz); - } - - void begin(const float32_t *cp, int n_stages = 1) { - coeff_p = cp; - // Initialize FIR instance (ARM DSP Math Library) - if (coeff_p && (coeff_p != IIR_F32_PASSTHRU) && n_stages <= IIR_MAX_STAGES) { - //https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html - arm_biquad_cascade_df1_init_f32(&iir_inst, n_stages, (float32_t *)coeff_p, &StateF32[0]); - } + AudioFilterBiquad_F32(void): AudioStream_F32(1,inputQueueArray) { + setSampleRate_Hz(AUDIO_SAMPLE_RATE_EXACT); + sampleRate_Hz = AUDIO_SAMPLE_RATE_EXACT; // <<<<<<<<<<<<<<<<<<<<<< CHECK IF NEEDED?? + doClassInit(); } - void end(void) { - coeff_p = NULL; + AudioFilterBiquad_F32(const AudioSettings_F32 &settings): + AudioStream_F32(1,inputQueueArray) { + setSampleRate_Hz(settings.sample_rate_Hz); + doClassInit(); } - - void setSampleRate_Hz(float _fs_Hz) { sampleRate_Hz = _fs_Hz; } - + + void doClassInit(void) { + for(int ii=0; ii<5*IIR_MAX_STAGES; ii++) { + coeff32[ii] = 0.0; + coeff64[ii] = 0.0; + } + for(int ii=0; ii<4; ii++) { + coeff32[5*ii] = 1.0; // b0 = 1 for pass through + coeff64[5*ii] = 1.0; + } + numStagesUsed = 0; // Can be 0 to 4 + doBiquad = false; // This is the way to jump over the biquad + } + + // Up to 4 stages are allowed. Coefficients, either by design function + // or from direct setCoefficients() need to be added to the double array + // and also to the float + void setCoefficients(int iStage, double *cf) { + if (iStage > IIR_MAX_STAGES) { + if (Serial) { + Serial.print("AudioFilterBiquad_F32: setCoefficients:"); + Serial.println(" *** MaxStages Error"); + } + return; + } + if((iStage + 1) > numStagesUsed) + numStagesUsed = iStage + 1; // There may be blank pass throughs + for(int ii=0; ii<5; ii++) { + coeff64[ii + 5*iStage] = cf[ii]; // The local collection of double coefficients + coeff32[ii + 5*iStage] = (float)cf[ii]; // and of floats + } + doBiquad = true; + } + + // ARM DSP Math library filter instance. + // Does the initialization of ARM CMSIS DSP BiQuad structure. This MUST follow the + // setting of coefficients to catch the max number of stages and do the + // double to float conversion for the CMSIS routine. + void begin(void) { + // Initialize FIR instance (ARM DSP Math Library) + //https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html + arm_biquad_cascade_df1_init_f32(&iir_inst, numStagesUsed, &coeff32[0], &StateF32[0]); + } + + void end(void) { + doBiquad = false; + } + + void setSampleRate_Hz(float _fs_Hz) { sampleRate_Hz = _fs_Hz; } + + // Deprecated void setBlockDC(void) { - //https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5 - //Use matlab to compute the coeff for HP at 40Hz: [b,a]=butter(2,40/(44100/2),'high'); %assumes fs_Hz = 44100 - float32_t b[] = {8.173653471988667e-01, -1.634730694397733e+00, 8.173653471988667e-01}; //from Matlab - float32_t a[] = { 1.000000000000000e+00, -1.601092394183619e+00, 6.683689946118476e-01}; //from Matlab + // https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5 + // Use matlab to compute the coeff for HP at 40Hz: [b,a]=butter(2,40/(44100/2),'high'); %assumes fs_Hz = 44100 + double b[] = {8.173653471988667e-01, -1.634730694397733e+00, 8.173653471988667e-01}; //from Matlab + double a[] = { 1.000000000000000e+00, -1.601092394183619e+00, 6.683689946118476e-01}; //from Matlab setFilterCoeff_Matlab(b, a); } - - void setFilterCoeff_Matlab(float32_t b[], float32_t a[]) { //one stage of N=2 IIR + + void setFilterCoeff_Matlab(double b[], double a[]) { //one stage of N=2 IIR + double coeff[5]; //https://www.keil.com/pack/doc/CMSIS/DSP/html/group__BiquadCascadeDF1.html#ga8e73b69a788e681a61bccc8959d823c5 //Use matlab to compute the coeff, such as: [b,a]=butter(2,20/(44100/2),'high'); %assumes fs_Hz = 44100 coeff[0] = b[0]; coeff[1] = b[1]; coeff[2] = b[2]; //here are the matlab "b" coefficients - coeff[3] = -a[1]; coeff[4] = -a[2]; //the DSP needs the "a" terms to have opposite sign vs Matlab ; - begin(coeff); + coeff[3] = -a[1]; coeff[4] = -a[2]; //the DSP needs the "a" terms to have opposite sign vs Matlab + setCoefficients(0, coeff); } - - //note: stage is currently ignored - void setCoefficients(int stage, float c[]) { - if (stage > 0) { - if (Serial) { - Serial.println(F("AudioFilterBiquad_F32: setCoefficients: *** ERROR ***")); - Serial.print(F(" : This module only accepts one stage.")); - Serial.print(F(" : You are attempting to set stage "));Serial.print(stage); - Serial.print(F(" : Ignoring this filter.")); - } - return; - } - coeff[0] = c[0]; - coeff[1] = c[1]; - coeff[2] = c[2]; - coeff[3] = -c[3]; - coeff[4] = -c[4]; - begin(coeff); - } - - // Compute common filter functions - // http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt - //void setLowpass(uint32_t stage, float frequency, float q = 0.7071) { - void setLowpass(uint32_t stage, float frequency, float q = 0.7071) { - //int coeff[5]; - double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); - double sinW0 = sin(w0); - double alpha = sinW0 / ((double)q * 2.0); - double cosW0 = cos(w0); - //double scale = 1073741824.0 / (1.0 + alpha); - double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0 - /* b0 */ coeff[0] = ((1.0 - cosW0) / 2.0) * scale; - /* b1 */ coeff[1] = (1.0 - cosW0) * scale; - /* b2 */ coeff[2] = coeff[0]; - /* a1 */ coeff[3] = (-2.0 * cosW0) * scale; - /* a2 */ coeff[4] = (1.0 - alpha) * scale; - - setCoefficients(stage, coeff); - } - void setHighpass(uint32_t stage, float frequency, float q = 0.7071) { - //int coeff[5]; - double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); - double sinW0 = sin(w0); - double alpha = sinW0 / ((double)q * 2.0); - double cosW0 = cos(w0); - double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0 - /* b0 */ coeff[0] = ((1.0 + cosW0) / 2.0) * scale; - /* b1 */ coeff[1] = -(1.0 + cosW0) * scale; - /* b2 */ coeff[2] = coeff[0]; - /* a1 */ coeff[3] = (-2.0 * cosW0) * scale; - /* a2 */ coeff[4] = (1.0 - alpha) * scale; - setCoefficients(stage, coeff); - } - void setBandpass(uint32_t stage, float frequency, float q = 1.0) { - //int coeff[5]; - double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); - double sinW0 = sin(w0); - double alpha = sinW0 / ((double)q * 2.0); - double cosW0 = cos(w0); - double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0 - /* b0 */ coeff[0] = alpha * scale; - /* b1 */ coeff[1] = 0; - /* b2 */ coeff[2] = (-alpha) * scale; - /* a1 */ coeff[3] = (-2.0 * cosW0) * scale; - /* a2 */ coeff[4] = (1.0 - alpha) * scale; - setCoefficients(stage, coeff); - } - void setNotch(uint32_t stage, float frequency, float q = 1.0) { - //int coeff[5]; - double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); - double sinW0 = sin(w0); - double alpha = sinW0 / ((double)q * 2.0); - double cosW0 = cos(w0); - double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0 - /* b0 */ coeff[0] = scale; - /* b1 */ coeff[1] = (-2.0 * cosW0) * scale; - /* b2 */ coeff[2] = coeff[0]; - /* a1 */ coeff[3] = (-2.0 * cosW0) * scale; - /* a2 */ coeff[4] = (1.0 - alpha) * scale; - setCoefficients(stage, coeff); - } - void setLowShelf(uint32_t stage, float frequency, float gain, float slope = 1.0f) { - //int coeff[5]; - double a = pow(10.0, gain/40.0); - double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); - double sinW0 = sin(w0); - //double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0; - double cosW0 = cos(w0); - //generate three helper-values (intermediate results): - double sinsq = sinW0 * sqrt( (pow(a,2.0)+1.0)*(1.0/slope-1.0)+2.0*a ); - double aMinus = (a-1.0)*cosW0; - double aPlus = (a+1.0)*cosW0; - double scale = 1.0 / ( (a+1.0) + aMinus + sinsq); - /* b0 */ coeff[0] = a * ( (a+1.0) - aMinus + sinsq ) * scale; - /* b1 */ coeff[1] = 2.0*a * ( (a-1.0) - aPlus ) * scale; - /* b2 */ coeff[2] = a * ( (a+1.0) - aMinus - sinsq ) * scale; - /* a1 */ coeff[3] = -2.0* ( (a-1.0) + aPlus ) * scale; - /* a2 */ coeff[4] = ( (a+1.0) + aMinus - sinsq ) * scale; - setCoefficients(stage, coeff); - } - void setHighShelf(uint32_t stage, float frequency, float gain, float slope = 1.0f) { - //int coeff[5]; - double a = pow(10.0, gain/40.0); - double w0 = frequency * (2 * 3.141592654 / AUDIO_SAMPLE_RATE_EXACT); - double sinW0 = sin(w0); - //double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0; - double cosW0 = cos(w0); - //generate three helper-values (intermediate results): - double sinsq = sinW0 * sqrt( (pow(a,2.0)+1.0)*(1.0/slope-1.0)+2.0*a ); - double aMinus = (a-1.0)*cosW0; - double aPlus = (a+1.0)*cosW0; - double scale = 1.0 / ( (a+1.0) - aMinus + sinsq); - /* b0 */ coeff[0] = a * ( (a+1.0) + aMinus + sinsq ) * scale; - /* b1 */ coeff[1] = -2.0*a * ( (a-1.0) + aPlus ) * scale; - /* b2 */ coeff[2] = a * ( (a+1.0) + aMinus - sinsq ) * scale; - /* a1 */ coeff[3] = 2.0* ( (a-1.0) - aPlus ) * scale; - /* a2 */ coeff[4] = ( (a+1.0) - aMinus - sinsq ) * scale; - setCoefficients(stage, coeff); - } - + + //Two update() options, floats or doubles + void useDouble(bool ud) { + useDoubleCoefs = ud; // true is to use doubles + useDoubleCoefs = false; // Not implemented yet + } + + // Compute common filter functions + // http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt + //void setLowpass(uint32_t stage, float frequency, float q = 0.7071) { + void setLowpass(int stage, float frequency, float q) { + double coeff[5]; + double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz); + double sinW0 = sin(w0); + double alpha = sinW0 / ((double)q * 2.0); + double cosW0 = cos(w0); + double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0 + /* b0 */ coeff[0] = ((1.0 - cosW0) / 2.0) * scale; + /* b1 */ coeff[1] = (1.0 - cosW0) * scale; + /* b2 */ coeff[2] = coeff[0]; + /* a1 */ coeff[3] = -(-2.0 * cosW0) * scale; + /* a2 */ coeff[4] = -(1.0 - alpha) * scale; + setCoefficients(stage, coeff); + } + + void setHighpass(uint32_t stage, float frequency, float q) { + double coeff[5]; + double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz); + double sinW0 = sin(w0); + double alpha = sinW0 / ((double)q * 2.0); + double cosW0 = cos(w0); + double scale = 1.0 / (1.0+alpha); + /* b0 */ coeff[0] = ((1.0 + cosW0) / 2.0) * scale; + /* b1 */ coeff[1] = -(1.0 + cosW0) * scale; + /* b2 */ coeff[2] = coeff[0]; + /* a1 */ coeff[3] = -(-2.0 * cosW0) * scale; + /* a2 */ coeff[4] = -(1.0 - alpha) * scale; + setCoefficients(stage, coeff); + } + + void setBandpass(uint32_t stage, float frequency, float q) { + double coeff[5]; + double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz); + double sinW0 = sin(w0); + double alpha = sinW0 / ((double)q * 2.0); + double cosW0 = cos(w0); + double scale = 1.0 / (1.0+alpha); + /* b0 */ coeff[0] = alpha * scale; + /* b1 */ coeff[1] = 0; + /* b2 */ coeff[2] = (-alpha) * scale; + /* a1 */ coeff[3] = -(-2.0 * cosW0) * scale; + /* a2 */ coeff[4] = -(1.0 - alpha) * scale; + setCoefficients(stage, coeff); + } + + void setNotch(uint32_t stage, float frequency, float q) { + double coeff[5]; + double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz); + double sinW0 = sin(w0); + double alpha = sinW0 / ((double)q * 2.0); + double cosW0 = cos(w0); + double scale = 1.0 / (1.0+alpha); // which is equal to 1.0 / a0 + /* b0 */ coeff[0] = scale; + /* b1 */ coeff[1] = (-2.0 * cosW0) * scale; + /* b2 */ coeff[2] = coeff[0]; + /* a1 */ coeff[3] = -(-2.0 * cosW0) * scale; + /* a2 */ coeff[4] = -(1.0 - alpha) * scale; + setCoefficients(stage, coeff); + } + + void setLowShelf(uint32_t stage, float frequency, float gain, float slope) { + double coeff[5]; + double a = pow(10.0, gain/40.0); + double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz); + double sinW0 = sin(w0); + //double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0; + double cosW0 = cos(w0); + //generate three helper-values (intermediate results): + double sinsq = sinW0 * sqrt( (pow(a,2.0)+1.0)*(1.0/slope-1.0)+2.0*a ); + double aMinus = (a-1.0)*cosW0; + double aPlus = (a+1.0)*cosW0; + double scale = 1.0 / ( (a+1.0) + aMinus + sinsq); + /* b0 */ coeff[0] = a * ( (a+1.0) - aMinus + sinsq ) * scale; + /* b1 */ coeff[1] = 2.0*a * ( (a-1.0) - aPlus ) * scale; + /* b2 */ coeff[2] = a * ( (a+1.0) - aMinus - sinsq ) * scale; + /* a1 */ coeff[3] = 2.0* ( (a-1.0) + aPlus ) * scale; + /* a2 */ coeff[4] = - ( (a+1.0) + aMinus - sinsq ) * scale; + setCoefficients(stage, coeff); + } + + void setHighShelf(uint32_t stage, float frequency, float gain, float slope) { + double coeff[5]; + double a = pow(10.0, gain/40.0); + double w0 = frequency * (2 * 3.141592654 / sampleRate_Hz); + double sinW0 = sin(w0); + //double alpha = (sinW0 * sqrt((a+1/a)*(1/slope-1)+2) ) / 2.0; + double cosW0 = cos(w0); + //generate three helper-values (intermediate results): + double sinsq = sinW0 * sqrt( (pow(a,2.0)+1.0)*(1.0/slope-1.0)+2.0*a ); + double aMinus = (a-1.0)*cosW0; + double aPlus = (a+1.0)*cosW0; + double scale = 1.0 / ( (a+1.0) - aMinus + sinsq); + /* b0 */ coeff[0] = a * ( (a+1.0) + aMinus + sinsq ) * scale; + /* b1 */ coeff[1] = -2.0*a * ( (a-1.0) + aPlus ) * scale; + /* b2 */ coeff[2] = a * ( (a+1.0) + aMinus - sinsq ) * scale; + /* a1 */ coeff[3] = -2.0* ( (a-1.0) - aPlus ) * scale; + /* a2 */ coeff[4] = -( (a+1.0) - aMinus - sinsq ) * scale; + setCoefficients(stage, coeff); + } + + double* getCoeffs(void) { + return coeff64; // Pointer to 20 coefficients in double. + } + void update(void); - + private: audio_block_f32_t *inputQueueArray[1]; - float32_t coeff[5 * 1] = {1.0, 0.0, 0.0, 0.0, 0.0}; //no filtering. actual filter coeff set later - float sampleRate_Hz = AUDIO_SAMPLE_RATE_EXACT; //default. from AudioStream.h?? - - // pointer to current coefficients or NULL or FIR_PASSTHRU - const float32_t *coeff_p; - - // ARM DSP Math library filter instance + float coeff32[5 * IIR_MAX_STAGES]; // Local copies to be transferred with begin() + double coeff64[5 * IIR_MAX_STAGES]; + float StateF32[4*IIR_MAX_STAGES]; + //double StateF64[4*IIR_MAX_STAGES]; // Will need this for 64 bit version + float sampleRate_Hz = AUDIO_SAMPLE_RATE_EXACT; //default. from AudioStream.h?? + int numStagesUsed = 0; + bool useDoubleCoefs = false; // As of now, all float <<<<<<<<<<<<<<<<<<<< + bool doBiquad = false; + + /* Info - The structure from arm_biquad_casd_df1_inst_f32 consists of + * uint32_t numStages; + * const float32_t *pCoeffs; //Points to the array of coefficients, length 5*numStages. + * float32_t *pState; //Points to the array of state variables, length 4*numStages. + */ + // ARM DSP Math library filter instance. arm_biquad_casd_df1_inst_f32 iir_inst; - float32_t StateF32[4*IIR_MAX_STAGES]; }; - - #endif diff --git a/examples/TestBiquad/TestBiquad.ino b/examples/TestBiquad/TestBiquad.ino new file mode 100644 index 0000000..3b72342 --- /dev/null +++ b/examples/TestBiquad/TestBiquad.ino @@ -0,0 +1,121 @@ +/* + TestBiquad.ino Test the +* F32 library AudioFilterBiquad_F32 +* Bob Larkin 26Feb 2021 +* Public Domain +*/ +#include "OpenAudio_ArduinoLibrary.h" +#include "AudioStream_F32.h" +#include +#include +#include +#include +#include + +// T3.x supported sample rates: 2000, 8000, 11025, 16000, 22050, 24000, 32000, 44100, 44117, 48000, +// 88200, 88235 (44117*2), 95680, 96000, 176400, 176470, 192000 +// T4.x supports any sample rate the codec will handle. +const float sample_rate_Hz = 44117.0f ; // 24000, 44117, or other frequencies listed above (untested) +const int audio_block_samples = 128; // Others untested +AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples); // Not used + +// GUItool: begin automatically generated code +AudioInputI2S_F32 audioInI2S1; //xy=73.5,556 +AudioSynthWaveformSine_F32 sine1; //xy=89,595 +AudioMixer4_F32 mixer4_1; //xy=263,572 +AudioAnalyzeRMS_F32 rms1; //xy=411,528 +AudioFilterBiquad_F32 biquad1; //xy=414,586 +AudioAnalyzeRMS_F32 rms2; //xy=547,528 +AudioOutputI2S_F32 audioOutI2S1; //xy=576,585 +AudioConnection_F32 patchCord1(audioInI2S1, 0, mixer4_1, 0); +AudioConnection_F32 patchCord2(audioInI2S1, 1, mixer4_1, 1); +AudioConnection_F32 patchCord3(sine1, 0, mixer4_1, 2); +AudioConnection_F32 patchCord4(mixer4_1, biquad1); +AudioConnection_F32 patchCord5(mixer4_1, rms1); +AudioConnection_F32 patchCord6(biquad1, 0, audioOutI2S1, 1); +AudioConnection_F32 patchCord7(biquad1, 0, audioOutI2S1, 0); +AudioConnection_F32 patchCord8(biquad1, rms2); +AudioControlSGTL5000 sgtl5000_1; //xy=327,636 +// GUItool: end automatically generated code +// Reminder: Check that AudioMemory_F32 is allocated and that +// settings are added to F32 objects as needed, including Memory. + +void setup() { + Serial.begin(300); + delay(1000); + Serial.println("OpenAudio_ArduinoLibrary - TestBiquad"); + + AudioMemory(10); //allocate Int16 audio data blocks + + AudioMemory_F32(20, audio_settings); + + // Enable the audio shield, select input, and enable output + sgtl5000_1.enable(); //start the audio board + sgtl5000_1.inputSelect(AUDIO_INPUT_LINEIN); // or AUDIO_INPUT_MIC + sgtl5000_1.volume(0.8); //volume can be 0.0 to 1.0 + sgtl5000_1.lineInLevel(10, 10); //level can be 0 to 15. 5 is the Teensy Audio Library's default + + mixer4_1.gain(0, 1.0f); // Left in + mixer4_1.gain(1, 1.0f); // Right In + mixer4_1.gain(2, 0.0f); // Sine wave in + + // **** BiQuad Filter Selection - Pick one of the next **** + //biquad1.setHighpass(0, 40.0f, 0.707f); + //biquad1.setHighpass(0, 1000.0f, 0.707f); + biquad1.setLowpass(0, 2500.0f, 0.707f); + //for(int jj=0; jj<4; jj++) biquad1.setLowpass(jj, 2000.0f, 0.707f); + //biquad1.setBandpass(0, 1000.0f, 1.5f); + //biquad1.setNotch(0, 1000.0f, 1.0f); + + // Coeffs, a0=1.0 always b0 b1 b2 a1 a2 + //double cfLP2500[] = {0.025157343556, 0.050314687112, 0.025157343556, 1.503844064936, -0.604473439160}; + //biquad1.setCoefficients(0, cfLP2500); // (int iStage, double *cf) // Load cfLP2500, 1 stage + // ************************************************************ + biquad1.begin(); + + // Download a pointer to the coefficients to see what is happening + double* pc=biquad1.getCoeffs(); + Serial.println(" b0 b1 b2 a1 a2"); + for(int ii=0; ii<4; ii++) { + Serial.print( *(pc + 0 + ii*5),12 ); Serial.print(", "); + Serial.print( *(pc + 1 + ii*5),12 ); Serial.print(", "); + Serial.print( *(pc + 2 + ii*5),12 ); Serial.print(", "); + Serial.print( *(pc + 3 + ii*5),12 ); Serial.print(", "); + Serial.print( *(pc + 4 + ii*5),12 ); Serial.println(); + } + + sine1.frequency(500.0); // Connect via mixer4_1.gain(2, 0.0f); + sine1.amplitude(0.02); +} + +void loop() { + //update the memory and CPU usage...if enough time has passed + printMemoryAndCPU(millis()); +} + +void printMemoryAndCPU(unsigned long curTime_millis) { + static unsigned long updatePeriod_millis = 2000; //how many milliseconds between updating gain reading? + static unsigned long lastUpdate_millis = 0; + float r1, r2; + //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? + + if( rms1.available() ) { + Serial.print("RMS in: "); + Serial.print(r1=rms1.read(), 6); + } + if( rms2.available() ) { + Serial.print(" RMS out: "); + Serial.print(r2=rms2.read(), 6); + Serial.print(" Gain, dB = "); + Serial.print( 20.0f*log10f(r2/r1)); + } + + Serial.print(" CPU: Usage, Max: "); + Serial.print(AudioProcessorUsageMax()); + Serial.print(" Float Memory, Max: "); + Serial.println(AudioMemoryUsageMax_F32()); + lastUpdate_millis = curTime_millis; //we will use this value the next time around. + } +}