Draft for adding ConvFilt's

pull/13/head
boblark 3 years ago
parent f436107b4e
commit f917ff4f53
  1. 145
      AudioFilterConvolution_F32.cpp
  2. 159
      AudioFilterConvolution_F32.h
  3. 74
      examples/TestConvolutinalFilter/TestConvolutinalFilter.ino

@ -2,8 +2,8 @@
****************************************************************************** ******************************************************************************
* @file AudioFilterConvolution_F32.cpp * @file AudioFilterConvolution_F32.cpp
* @author Giuseppe Callipo - IK8YFW - ik8yfw@libero.it * @author Giuseppe Callipo - IK8YFW - ik8yfw@libero.it
* @version V1.0.0 * @version V2.0.0
* @date 02-05-2021 * @date 06-02-2021
* @brief F32 Filter Convolution * @brief F32 Filter Convolution
* *
****************************************************************************** ******************************************************************************
@ -21,25 +21,22 @@
4) Added initFilter method for single anf fast initialization and on 4) Added initFilter method for single anf fast initialization and on
the fly reinititializzation; the fly reinititializzation;
5) Optimize it to use as output audio filter on SDR receiver. 5) Optimize it to use as output audio filter on SDR receiver.
6) Optimize the time execution
*******************************************************************/ *******************************************************************/
// Revised for OpenAudio_Arduino Teensy F32 library, 8 Feb 2022
#include "AudioFilterConvolution_F32.h" #include "AudioFilterConvolution_F32.h"
boolean AudioFilterConvolution_F32::begin(int status)
{
enabled = status;
return(true);
}
void AudioFilterConvolution_F32::passThrough(int stat) void AudioFilterConvolution_F32::passThrough(int stat)
{ {
passThru=stat; passThru=stat;
} }
void AudioFilterConvolution_F32::impulse(float32_t *FIR_coef) { // Function to pre-calculate the multiplying frequency function, the "mask."
// arm_q15_to_float(coefs, FIR_coef, 513); // convert int_buffer to float 32bit void AudioFilterConvolution_F32::impulse(float32_t *FIR_coef)
int k = 0; {
int i = 0; uint32_t k = 0;
uint32_t i = 0;
enabled = 0; // shut off audio stream while impulse is loading enabled = 0; // shut off audio stream while impulse is loading
for (i = 0; i < (FFT_length / 2) + 1; i++) for (i = 0; i < (FFT_length / 2) + 1; i++)
{ {
@ -52,11 +49,10 @@ void AudioFilterConvolution_F32::impulse(float32_t *FIR_coef) {
FIR_filter_mask[i] = 0.0; FIR_filter_mask[i] = 0.0;
} }
arm_cfft_f32( &arm_cfft_sR_f32_len1024, FIR_filter_mask, 0, 1); arm_cfft_f32( &arm_cfft_sR_f32_len1024, FIR_filter_mask, 0, 1);
for (int i = 0; i < 1024; i++) {
// Serial.println(FIR_filter_mask[i] * 32768);
}
// for 1st time thru, zero out the last sample buffer to 0 // for 1st time thru, zero out the last sample buffer to 0
memset(last_sample_buffer_L, 0, sizeof(last_sample_buffer_L)); arm_fill_f32(0, last_sample_buffer_L, BUFFER_SIZE *4);
state = 0; state = 0;
enabled = 1; //enable audio stream again enabled = 1; //enable audio stream again
} }
@ -71,51 +67,59 @@ void AudioFilterConvolution_F32::update(void)
if (block) { if (block) {
switch (state) { switch (state) {
case 0: case 0:
bp = block->data; if (passThru ==0) {
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { arm_cmplx_mult_cmplx_f32(FFT_buffer, FIR_filter_mask, iFFT_buffer, FFT_length); // complex multiplication in Freq domain = convolution in time domain
buffer[i] = *bp++; arm_cfft_f32(&arm_cfft_sR_f32_len1024, iFFT_buffer, 1, 1); // perform complex inverse FFT
k = 0;
l = 1024;
for (int i = 0; i < 512; i++) {
buffer[i] = last_sample_buffer_L[i] + iFFT_buffer[k++]; // this performs the "ADD" in overlap/Add
last_sample_buffer_L[i] = iFFT_buffer[l++]; // this saves 512 samples (overlap) for next time around
k++;
l++;
}
// convert floats to Q15 and save in temporary array tbuffer
arm_copy_f32 (&buffer[0], &tbuffer[0], BUFFER_SIZE*4);
} }
bp = block->data; bp = block->data;
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
*bp++ = tbuffer[i]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering) buffer[i] = *bp;
*bp++ = tbuffer[i];
} }
AudioStream_F32::transmit(block); AudioStream_F32::transmit(block);
AudioStream_F32::release(block); AudioStream_F32::release(block);
state = 1; state = 1;
break; break;
case 1: case 1:
bp = block->data; bp = block->data;
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
buffer[128+i] = *bp++; buffer[128+i] = *bp;
} *bp++ = tbuffer[i+128];
bp = block->data;
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
*bp++ = tbuffer[i+128]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering)
} }
AudioStream_F32::transmit(block); AudioStream_F32::transmit(block);
AudioStream_F32::release(block); AudioStream_F32::release(block);
state = 2; state = 2;
break; break;
case 2: case 2:
bp = block->data; bp = block->data;
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
buffer[256 + i] = *bp++; buffer[256 + i] = *bp;
}
bp = block->data;
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
*bp++ = tbuffer[i+256]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering) *bp++ = tbuffer[i+256]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering)
} }
AudioStream_F32::transmit(block); AudioStream_F32::transmit(block);
AudioStream_F32::release(block); AudioStream_F32::release(block);
// zero pad last half of array- necessary to prevent aliasing in FFT
arm_fill_f32(0, FFT_buffer + 1024, FFT_length);
state = 3; state = 3;
break; break;
case 3: case 3:
bp = block->data; bp = block->data;
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
buffer[384 + i] = *bp++; buffer[384 + i] = *bp;
}
bp = block->data;
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
*bp++ = tbuffer[i + 384]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering) *bp++ = tbuffer[i + 384]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering)
} }
AudioStream_F32::transmit(block); AudioStream_F32::transmit(block);
@ -123,35 +127,17 @@ void AudioFilterConvolution_F32::update(void)
state = 0; state = 0;
// 4 blocks are in- now do the FFT1024,complex multiply and iFFT1024 on 512samples of data // 4 blocks are in- now do the FFT1024,complex multiply and iFFT1024 on 512samples of data
// using the overlap/add method // using the overlap/add method
// 1st convert Q15 samples to float
//arm_q15_to_float(buffer, float_buffer_L, 512);
arm_copy_f32 (buffer, float_buffer_L, 512);
// float_buffer samples are now standardized from > -1.0 to < 1.0
if (passThru ==0) { if (passThru ==0) {
memset(FFT_buffer + 1024, 0, sizeof(FFT_buffer) / 2); // zero pad last half of array- necessary to prevent aliasing in FFT
//fill FFT_buffer with current audio samples //fill FFT_buffer with current audio samples
k = 0; k = 0;
for (i = 0; i < 512; i++) for (i = 0; i < 512; i++)
{ {
FFT_buffer[k++] = float_buffer_L[i]; // real FFT_buffer[k++] = buffer[i]; // real
FFT_buffer[k++] = float_buffer_L[i]; // imag FFT_buffer[k++] = buffer[i]; // imag
} }
// calculations are performed in-place in FFT routines // calculations are performed in-place in FFT routines
arm_cfft_f32(&arm_cfft_sR_f32_len1024, FFT_buffer, 0, 1);// perform complex FFT arm_cfft_f32(&arm_cfft_sR_f32_len1024, FFT_buffer, 0, 1);// perform complex FFT
arm_cmplx_mult_cmplx_f32(FFT_buffer, FIR_filter_mask, iFFT_buffer, FFT_length); // complex multiplication in Freq domain = convolution in time domain
arm_cfft_f32(&arm_cfft_sR_f32_len1024, iFFT_buffer, 1, 1); // perform complex inverse FFT
k = 0;
l = 1024;
for (int i = 0; i < 512; i++) {
float_buffer_L[i] = last_sample_buffer_L[i] + iFFT_buffer[k++]; // this performs the "ADD" in overlap/Add
last_sample_buffer_L[i] = iFFT_buffer[l++]; // this saves 512 samples (overlap) for next time around
k++;
l++;
}
} //end if passTHru } //end if passTHru
// convert floats to Q15 and save in temporary array tbuffer
//arm_float_to_q15(&float_buffer_L[0], &tbuffer[0], BUFFER_SIZE*4);
arm_copy_f32 (&float_buffer_L[0], &tbuffer[0], BUFFER_SIZE*4);
break; break;
} }
} }
@ -187,20 +173,25 @@ float AudioFilterConvolution_F32::m_sinc(int m, float fc)
return sinf(x*fc)/(fc*x); return sinf(x*fc)/(fc*x);
} }
void AudioFilterConvolution_F32::calc_FIR_coeffs (float * coeffs, int numCoeffs, float32_t fc, float32_t Astop, int type, float dfc, float Fsamprate) void AudioFilterConvolution_F32::calc_FIR_coeffs
// pointer to coefficients variable, no. of coefficients to calculate, frequency where it happens, stopband attenuation in dB, (float *coeffs, int numCoeffs, float32_t fc, float32_t Astop,
// filter type, half-filter bandwidth (only for bandpass and notch) int type, float dfc, float Fsamprate){
{ // modified by WMXZ and DD4WH after // pointer to coefficients variable, no. of coefficients to calculate,
// Wheatley, M. (2011): CuteSDR Technical Manual. www.metronix.com, pages 118 - 120, FIR with Kaiser-Bessel Window // frequency where it happens, stopband attenuation in dB,
// assess required number of coefficients by // filter type, half-filter bandwidth (only for bandpass and notch).
// Modified by WMXZ and DD4WH after
// Wheatley, M. (2011): CuteSDR Technical Manual. www.metronix.com,
// pages 118 - 120, FIR with Kaiser-Bessel Window.
// Assess required number of coefficients by
// numCoeffs = (Astop - 8.0) / (2.285 * TPI * normFtrans); // numCoeffs = (Astop - 8.0) / (2.285 * TPI * normFtrans);
// selecting high-pass, numCoeffs is forced to an even number for better frequency response // selecting high-pass, numCoeffs is forced to an even number for
// better frequency response
int ii,jj; int ii,jj;
float32_t Beta; float32_t Beta;
float32_t izb; float32_t izb;
float fcf = fc; float fcf = fc;
int nc = numCoeffs; int nc = numCoeffs;
fc = fc / Fsamprate; fc = 2.0f * fc / Fsamprate; // Corrected
dfc = dfc / Fsamprate; dfc = dfc / Fsamprate;
// calculate Kaiser-Bessel window shape factor beta from stop-band attenuation // calculate Kaiser-Bessel window shape factor beta from stop-band attenuation
if (Astop < 20.96) if (Astop < 20.96)
@ -211,38 +202,40 @@ void AudioFilterConvolution_F32::calc_FIR_coeffs (float * coeffs, int numCoeffs,
Beta = 0.5842 * powf((Astop - 20.96), 0.4) + 0.07886 * (Astop - 20.96); Beta = 0.5842 * powf((Astop - 20.96), 0.4) + 0.07886 * (Astop - 20.96);
izb = Izero (Beta); izb = Izero (Beta);
if(type == 0) // low pass filter if(type == LOWPASS)
// { fcf = fc; { fcf = fc;
{ fcf = fc * 2.0;
nc = numCoeffs; nc = numCoeffs;
} }
else if(type == 1) // high-pass filter else if(type == HIGHPASS)
{ fcf = -fc; { fcf = -fc;
nc = 2*(numCoeffs/2); nc = 2*(numCoeffs/2);
} }
else if ((type == 2) || (type==3)) // band-pass filter else if ((type == BANDPASS) || (type==BANDREJECT))
{ {
fcf = dfc; fcf = dfc;
nc = 2*(numCoeffs/2); // maybe not needed nc = 2*(numCoeffs/2); // maybe not needed
} }
else if (type==4) // Hilbert transform
/*
else if (type==HILBERT)
{ {
nc = 2*(numCoeffs/2); nc = 2*(numCoeffs/2);
// clear coefficients // clear coefficients
for(ii=0; ii< 2*(nc-1); ii++) coeffs[ii]=0; for(ii=0; ii< 2*(nc-1); ii++) coeffs[ii]=0;
// set real delay // set real delay
coeffs[nc]=1; coeffs[nc]=1; // <<<<<??????????
// set imaginary Hilbert coefficients // set imaginary Hilbert coefficients
for(ii=1; ii< (nc+1); ii+=2) for(ii=1; ii< (nc+1); ii+=2)
{ {
if(2*ii==nc) continue; if(2*ii==nc) continue;
float x =(float)(2*ii - nc)/(float)nc; float x =(float)(2*ii - nc)/(float)nc;
float w = Izero(Beta*sqrtf(1.0f - x*x))/izb; // Kaiser window float w = Izero(Beta*sqrtf(1.0f - x*x))/izb; // Kaiser window
coeffs[2*ii+1] = 1.0f/(PIH*(float)(ii-nc/2)) * w ; coeffs[ii-1] = 1.0f/(PIH*(float)(ii-nc/2)) * w ;
//coeffs[2*ii+1] = 1.0f/(PIH*(float)(ii-nc/2)) * w ;
} }
return; return; // From Hilbert design
} }
*/
for(ii= - nc, jj=0; ii< nc; ii+=2,jj++) for(ii= - nc, jj=0; ii< nc; ii+=2,jj++)
{ {
@ -251,26 +244,24 @@ void AudioFilterConvolution_F32::calc_FIR_coeffs (float * coeffs, int numCoeffs,
coeffs[jj] = fcf * m_sinc(ii,fcf) * w; coeffs[jj] = fcf * m_sinc(ii,fcf) * w;
} }
if(type==1) if(type==HIGHPASS)
{ {
coeffs[nc/2] += 1; coeffs[nc/2] += 1;
} }
else if (type==2) else if (type==BANDPASS)
{ {
for(jj=0; jj< nc+1; jj++) coeffs[jj] *= 2.0f*cosf(PIH*(2*jj-nc)*fc); for(jj=0; jj< nc+1; jj++) coeffs[jj] *= 2.0f*cosf(PIH*(2*jj-nc)*fc);
} }
else if (type==3) else if (type==BANDREJECT)
{ {
for(jj=0; jj< nc+1; jj++) coeffs[jj] *= -2.0f*cosf(PIH*(2*jj-nc)*fc); for(jj=0; jj< nc+1; jj++) coeffs[jj] *= -2.0f*cosf(PIH*(2*jj-nc)*fc);
coeffs[nc/2] += 1; coeffs[nc/2] += 1;
} }
} // END calc_FIR_coef } // END calc_FIR_coef
void AudioFilterConvolution_F32::initFilter ( float32_t fc, float32_t Astop, int type, float dfc){ void AudioFilterConvolution_F32::initFilter ( float32_t fc, float32_t Astop, int type, float dfc){
//Init Fir //Init Fir
calc_FIR_coeffs (FIR_Coef, MAX_NUMCOEF, fc, Astop, type, dfc, fs); calc_FIR_coeffs (FIR_Coef, MAX_NUMCOEF, fc, Astop, type, dfc, fs);
begin(0); // set to zero to disable audio processing until impulse has been loaded enabled = 0; // set to zero to disable audio processing until impulse has been loaded
impulse(FIR_Coef); // generates Filter Mask and enables the audio stream impulse(FIR_Coef); // generates Filter Mask and enables the audio stream
} }

@ -1,9 +1,9 @@
/** /**
****************************************************************************** ******************************************************************************
* @file AudioFilterConvolution_F32.h * @file AudioFilterConvolution_F32.cpp
* @author Giuseppe Callipo - IK8YFW - ik8yfw@libero.it * @author Giuseppe Callipo - IK8YFW - ik8yfw@libero.it
* @version V1.0.0 * @version V2.0.0
* @date 02-05-2021 * @date 06-02-2021
* @brief F32 Filter Convolution * @brief F32 Filter Convolution
* *
****************************************************************************** ******************************************************************************
@ -19,52 +19,82 @@
3) Change the class for running in both with F32 3) Change the class for running in both with F32
OpenAudio_ArduinoLibrary for Teensy; OpenAudio_ArduinoLibrary for Teensy;
4) Added initFilter method for single anf fast initialization and on 4) Added initFilter method for single anf fast initialization and on
the fly re-inititialization; the fly reinititializzation;
5) Optimize it to use as output audio filter on SDR receiver. 5) Optimize it to use as output audio filter on SDR receiver.
6) Optimize the time execution
*******************************************************************/
Brought to the Teensy F32 OpenAudio_ArduinoLibrary 2 Feb 2022, Bob Larkin
******************************************************************************/
/*Notes from Giuseppe Callipo, IK8YFW:
*** How to use it. ***
Create an audio project based on chipaudette/OpenAudio_ArduinoLibrary
like the Keiths SDR Project, and just add the h and cpp file to your
processing chain as unique output filter:
************************************************************
1) Include the header
#include "AudioFilterConvolution_F32.h" (or OpenAudio_ArduinoLibrary.h)
2) Initialize as F32 block
(the block must be 128 but the sample rate can be changed but must initialized)
const float sample_rate_Hz = 96000.0f; // or 44100.0f or other
const int audio_block_samples = 128;
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples);
AudioFilterConvolution_F32 FilterConv(audio_settings);
3) Connect before output
AudioConnection_F32 patchCord1(FilterConv,0,Output_i2s,0);
AudioConnection_F32 patchCord2(FilterConv,0,Output_i2s,1);
4) Set the filter value when you need - some examples:
// CW - Centered at 800Hz, ( 40 db x oct ), 2=BPF, width = 1200Hz
FilterConv.initFilter((float32_t)800, 40, 2, 1200.0);
// SSB - Centered at 1500Hz, ( 60 db x oct ), 2=BPF, width = 3000Hz
FilterConv.initFilter((float32_t)1500, 60, 2, 3000.0);
************************************************************** */
/* Additional Notes from Bob /* Additional Notes from Bob
* Measured 128 word update in update() is 248 microseconds (T4.x) * Object creations is required. See the OpenAudio_ArduinoLibrary
* Comparison with a conventional FIR, from this library, the * Design Tool for object declarations along with
* AudioFilterFIRGeneral_F32 showed that a 512 tap FIR gave * automatic generatin of code. As an example this could produce
* essentially the same response and was slightly faster at * the following needed global code
* 225 microseconds per update. Also, note that this form of * AudioFilterConvolution_F32 FilterConv(audio_settings);
* computation uses about 52 kB of memory where the direct FIR * AudioConnection_F32 patchCord1(FilterConv,0,Output_i2s,0);
* uses about 10 kB. The responses differ in only minor ways. * AudioConnection_F32 patchCord2(FilterConv,0,Output_i2s,1);
*
* There are three class functions:
* void initFilter(float32_t fc, float32_t Astop,
* int type, float32_t dfc);
* void passThrough(int stat);
* float32_t* getCoeffPtr(void);
*
* initFilter() is used to design the "mask" function that sets the filter
* response. All filters use the Kaiser window that is characterized by
* a programable first sidelobe level and decreasing sidelobes as the
* frequency departs from the pass band. For many applications this is an
* excellent response. The response type is set by the integer "type." The
* options are:
* type=LOWPASS Low pass with fc cutoff frequency and dfc not used.
* type=HIGHPASS High pass with fc cutoff frequency and dfc not used.
* type=BANDPASS Band pass with fc center frequency and dfc pass band width.
* type=BANDREJECT Band reject with fc center frequency and dfc reject band width.
* type=HILBERT Hilbert transform. *** Not Currently Available ***
*
* Astop is a value in dB that approximates the first sidelobe level
* going into the stop band. This is a feature of the Kaiser window that
* allows trading off first sidelobe levels against the speed of
* transition from the passband to the stop band(s). Values in the 25
* to 70 dB range work well.
*
* Two examples of initFilter():
* // IK8YFW CW - Centered at 800Hz, ( 40 db x oct ), 2=BPF, width = 1200Hz
* FilterConv.initFilter((float32_t)800, 40, 2, 1200.0);
*
* // IK8YFWSSB - Centered at 1500Hz, ( 60 db x oct ), 2=BPF, width = 3000Hz
* FilterConv.initFilter((float32_t)1500, 60, 2, 3000.0);
*
* The band edges of filters here are specified by their -6 dB points.
*
* passThrough(int stat) allows data for this filter object to be passed through
* unchanged with stat=1. The dfault is stat=0.
*
* getCoeffPtr() returns a pointer to the coefficient array. To use this, compute
* the coefficients of a 512 tap FIR filter with the desired response. Then
* load the 512 float32_t buffer with the coefficients. Disabling the audio
* path may be needed to prevent "pop" noises.
*
* An alternate way to specify
*
* This class is compatible with, and included in, OpenAudio_ArduinoLibrary_F32.
* If you are using the include OpenAudio_ArduinoLibrary.h, this class's
* include file will be swept in.
*
* Only block_size = 128 is supported.
* Sample rate can be changed.
*
* Speed of execution is the force behind the convolution filter form.
* Measured 128 sample in update() is 139 microseconds (T4.x).
* Comparison with a conventional FIR from this library, the
* AudioFilterFIRGeneral_F32, showed that a 512 tap FIR gave
* essentially the same response but was somewhat slower at
* 225 microseconds per 128 update. Also, note that this form of the
* computation uses about 44 kB of data memory where the direct FIR
* uses about 10 kB.
*
* See the example TestConvolutionFilter.ino for more inforation on the
* use of this class.
*
* ************************************************************ */ * ************************************************************ */
#ifndef AudioFilterConvolution_F32_h_ #ifndef AudioFilterConvolution_F32_h_
@ -80,52 +110,61 @@
#define FOURPI 2.0 * TPI #define FOURPI 2.0 * TPI
#define SIXPI 3.0 * TPI #define SIXPI 3.0 * TPI
#define LOWPASS 0
#define HIGHPASS 1
#define BANDPASS 2
#define BANDREJECT 3
#define HILBERT 4
class AudioFilterConvolution_F32 : class AudioFilterConvolution_F32 :
public AudioStream_F32 public AudioStream_F32
{ {
public: public:
AudioFilterConvolution_F32(void) : AudioStream_F32(1, inputQueueArray_f32) { AudioFilterConvolution_F32(void) : AudioStream_F32(1, inputQueueArray_F32) {
fs = AUDIO_SAMPLE_RATE; fs = AUDIO_SAMPLE_RATE;
//block_size = AUDIO_BLOCK_SAMPLES; //block_size = AUDIO_BLOCK_SAMPLES;
}; };
AudioFilterConvolution_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_f32) { AudioFilterConvolution_F32(const AudioSettings_F32 &settings) : AudioStream_F32(1, inputQueueArray_F32) {
// Performs the first initialize // Performs the first initialize
fs = settings.sample_rate_Hz; fs = settings.sample_rate_Hz;
}; };
boolean begin(int status);
virtual void update(void); virtual void update(void);
void passThrough(int stat); void passThrough(int stat);
void initFilter (float32_t fc, float32_t Astop, int type, float dfc); void initFilter (float32_t fc, float32_t Astop,
int type, float32_t dfc);
float32_t* getCoeffPtr(void) {return &FIR_Coef[0];}
//#define Alternate filter init
private: private:
#define BUFFER_SIZE 128 #define BUFFER_SIZE 128
float32_t fs; float32_t fs;
audio_block_f32_t *inputQueueArray_F32[1];
audio_block_f32_t *inputQueueArray_f32[1];
float32_t *sp_L; float32_t *sp_L;
volatile uint8_t state; volatile uint8_t state;
int i; int i;
int k; int k;
int l; int l;
int passThru; int passThru=0;
int enabled; int enabled=0;
float32_t FIR_Coef[MAX_NUMCOEF]; float32_t FIR_Coef[MAX_NUMCOEF];
const uint32_t FFT_length = 1024; const uint32_t FFT_length = 1024;
float32_t FIR_coef[2048] __attribute__((aligned(4))); // float32_t FIR_coef[2048] __attribute__((aligned(4))); <<<<<<<<<<<<<<<
float32_t FIR_filter_mask[2048] __attribute__((aligned(4))); float32_t FIR_filter_mask[2048] __attribute__((aligned(4)));
float32_t buffer[2048] __attribute__((aligned(4))); float32_t buffer[2048] __attribute__((aligned(4)));
float32_t tbuffer[2048]__attribute__((aligned(4))); float32_t tbuffer[2048]__attribute__((aligned(4)));
float32_t FFT_buffer[2048] __attribute__((aligned(4))); float32_t FFT_buffer[2048] __attribute__((aligned(4)));
float32_t iFFT_buffer[2048] __attribute__((aligned(4))); float32_t iFFT_buffer[2048] __attribute__((aligned(4)));
float32_t float_buffer_L[512]__attribute__((aligned(4)));
float32_t last_sample_buffer_L[512]; float32_t last_sample_buffer_L[512];
void impulse(float32_t *coefs); void impulse(float32_t *coefs);
float32_t Izero (float32_t x);
float m_sinc(int m, float fc);
void calc_FIR_coeffs (float * coeffs, int numCoeffs, float32_t fc, float32_t Astop, int type, float dfc, float Fsamprate);
float32_t Izero (float32_t x);
float32_t m_sinc(int m, float32_t fc);
void calc_FIR_coeffs (float32_t * coeffs, int numCoeffs,
float32_t fc, float32_t Astop,
int type, float32_t dfc,
float32_t Fsamprate);
}; };
#endif #endif

@ -1,36 +1,41 @@
/* /*
* TestConvolutionalFilter.ino Bob Larkin 2 Feb 2022 * TestConvolutionalFilter.ino Bob Larkin 2 Feb 2022
* Measure frequency response of LPF. * Measure frequency response of convolution filtering.
* *
* This uses the Goertzel algoritm of AudioAnalyzeToneDetect_F32 as * This uses the Goertzel algoritm of AudioAnalyzeToneDetect_F32 as
* a "direct conversion receiver." This provides the excellent * a "direct conversion receiver." This provides the excellent
* selectivity needed for measuring below -100 dBfs. * selectivity needed for measuring below -100 dBfs.
* *
* Comparison with 512 tap FIR filtering is included, but double slash
* commented out.
*
* Public Domain - Teensy * Public Domain - Teensy
*/ */
#include "Audio.h" #include "Audio.h"
#include "OpenAudio_ArduinoLibrary.h" #include "OpenAudio_ArduinoLibrary.h"
#include "AudioStream_F32.h" #include "AudioStream_F32.h"
#include "AudioFilterConvolution_F32.h"
AudioSynthSineCosine_F32 sine_cos1; //xy=87,151 AudioSynthSineCosine_F32 sine_cos1; //xy=87,151
AudioFilterConvolution_F32 convolution1; //xy=163,245 AudioFilterConvolution_F32 convFilt1; //xy=163,245
AudioFilterFIRGeneral_F32 filterFIRgeneral1; //xy=285,177 //AudioFilterFIRGeneral_F32 filterFIRgeneral1; //xy=285,177
AudioOutputI2S_F32 audioOutI2S1; //xy=357,337 AudioOutputI2S_F32 audioOutI2S1; //xy=357,337
AudioAnalyzeToneDetect_F32 toneDet1; //xy=377,259 //AudioAnalyzeToneDetect_F32 toneDet1; //xy=377,259
AudioAnalyzeToneDetect_F32 toneDet2; //xy=378,299 AudioAnalyzeToneDetect_F32 toneDet2; //xy=378,299
AudioConnection_F32 patchCord1(sine_cos1, 0, filterFIRgeneral1, 0); //AudioConnection_F32 patchCord1(sine_cos1, 0, filterFIRgeneral1, 0);
AudioConnection_F32 patchCord2(sine_cos1, 1, convolution1, 0); AudioConnection_F32 patchCord2(sine_cos1, 1, convFilt1, 0);
AudioConnection_F32 patchCord3(convolution1, toneDet2); AudioConnection_F32 patchCord3(convFilt1, toneDet2);
AudioConnection_F32 patchCord4(convolution1, 0, audioOutI2S1, 0); AudioConnection_F32 patchCord4(convFilt1, 0, audioOutI2S1, 0);
AudioConnection_F32 patchCord5(filterFIRgeneral1, 0, toneDet1, 0); //AudioConnection_F32 patchCord5(filterFIRgeneral1, 0, toneDet1, 0);
AudioControlSGTL5000 sgtl5000_1; //xy=164,343 AudioControlSGTL5000 sgtl5000_1; //xy=164,343
float32_t saveDat[512]; // CAUTION that audio output is intended for an oscilloscope and may be very
float32_t coeffFIR[512]; // VERY loud in a speaker or headphone.
float32_t stateFIR[1160];
float32_t attenFIR[256]; //float32_t coeffFIR[512];
float32_t rdb[1000]; //float32_t stateFIR[1160];
//float32_t attenFIR[256];
void setup(void) { void setup(void) {
AudioMemory(5); AudioMemory(5);
@ -39,38 +44,53 @@ void setup(void) {
Serial.println("*** Test Convolutional Filtering routine for F32 ***"); Serial.println("*** Test Convolutional Filtering routine for F32 ***");
sine_cos1.frequency(1000.0f); sine_cos1.frequency(1000.0f);
sine_cos1.amplitude(1.0f); sine_cos1.amplitude(1.0f);
toneDet1.frequency(100.0f, 5); // toneDet1.frequency(100.0f, 5);
toneDet2.frequency(100.0f, 5); toneDet2.frequency(100.0f, 5);
convolution1.initFilter(4000.0f, 60.0f, 0, 0.0f);
convolution1.begin(1); // *** Un-comment just ONE of the following filters ***
// Low Pass Filter, 4000 Hz cut off, 60 dB first sidelobe
convFilt1.initFilter(4000.0f, 60.0f, LOWPASS, 0.0f);
// High Pass Filter, 2000 Hz cut off, 25 dB first sidelobe
//convFilt1.initFilter(2000.0f, 25.0f, HIGHPASS, 0.0f);
// IK8YFW CW - Centered at 800Hz, 40 db SL, 2=BPF, width = 1200Hz
//convFilt1.initFilter(800.0f, 40.0f, BANDPASS, 1200.0f);
// IK8YFW SSB - Centered at 1500Hz, 60 db SL, 2=BPF, width = 3000Hz
//convFilt1.initFilter(1500.0f, 60.0f, BANDPASS, 3000.0f);
// Band Reject filter, Centered at 6000Hz, 50 db SL, 3=BRF, width = 2000Hz
//convFilt1.initFilter(6000.0f, 60.0f, BANDREJECT, 2000.0f);
// BP filter, Centered at 6000Hz, 50 db SL, 2=BPF, width = 2000Hz
//convFilt1.initFilter(6000.0f, 60.0f, BANDPASS, 2000.0f);
// Design for direct FIR. This sets frequency response. // Design for direct FIR. This sets frequency response.
// Bin for 4kHz at 44.117kHz sample rate and 400 coeff's is (4000/44117) * (512/2) = 23.2 // Bin for 4kHz at 44.117kHz sample rate and 400 coeff's is (4000/44117) * (512/2) = 23.2
for(int ii=0; ii<47; ii++) attenFIR[ii] = 0.0f; // for(int ii=0; ii<47; ii++) attenFIR[ii] = 0.0f;
for(int ii=47; ii<256; ii++) attenFIR[ii] = -150.0f; // for(int ii=47; ii<256; ii++) attenFIR[ii] = -150.0f;
// FIRGeneralNew(float *adb, uint16_t nFIR, float *cf32f, float kdb, float *pStateArray); // FIRGeneralNew(float *adb, uint16_t nFIR, float *cf32f, float kdb, float *pStateArray);
filterFIRgeneral1.FIRGeneralNew(attenFIR, 512, coeffFIR, 60.0, stateFIR); // filterFIRgeneral1.FIRGeneralNew(attenFIR, 512, coeffFIR, 60.0, stateFIR);
// DSP measure frequency response in dB uniformly spaced from 100 to 20000 Hz // DSP measure frequency response in dB uniformly spaced from 100 to 20000 Hz
Serial.println("Freq Hz Conv dB FIR dB"); Serial.println("Freq Hz Conv dB FIR dB");
for(float32_t f=100.0; f<=20000.0f; f+=50.0f) for(float32_t f=100.0; f<=20000.0f; f+=50.0f)
{ {
sine_cos1.frequency(f); sine_cos1.frequency(f);
toneDet1.frequency(f, (f>2000.0f ? 100 : 5)); // toneDet1.frequency(f, (f>2000.0f ? 100 : 5));
toneDet2.frequency(f, (f>2000.0f ? 100 : 5)); toneDet2.frequency(f, (f>2000.0f ? 100 : 5));
delay(50); delay(50);
while(!toneDet1.available())delay(2) ; // while(!toneDet1.available())delay(2) ;
toneDet1.read(); // toneDet1.read();
while(!toneDet2.available())delay(2) ; while(!toneDet2.available())delay(2) ;
toneDet2.read(); toneDet2.read();
while(!toneDet2.available())delay(2) ; while(!toneDet2.available())delay(2) ;
Serial.print(f); Serial.print(f);
Serial.print(", "); Serial.print(", ");
while(!toneDet2.available())delay(2) ; while(!toneDet2.available())delay(2) ;
Serial.print(20.0f*log10f(toneDet2.read() ) ); Serial.println(20.0f*log10f(toneDet2.read() ) );
Serial.print(", ");
while(!toneDet1.available())delay(2) ;
Serial.println(20.0f*log10f(toneDet1.read() ) );
} }
} }

Loading…
Cancel
Save