Draft for adding ConvFilt's

pull/13/head
boblark 3 years ago
parent f436107b4e
commit f917ff4f53
  1. 293
      AudioFilterConvolution_F32.cpp
  2. 179
      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
* *
****************************************************************************** ******************************************************************************
@ -17,144 +17,130 @@
1) Class refactoring, change some methods visibility; 1) Class refactoring, change some methods visibility;
2) Filter coefficients calculation included into class; 2) Filter coefficients calculation included into class;
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 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;
enabled = 0; // shut off audio stream while impulse is loading uint32_t i = 0;
for (i = 0; i < (FFT_length / 2) + 1; i++) enabled = 0; // shut off audio stream while impulse is loading
{ for (i = 0; i < (FFT_length / 2) + 1; i++)
FIR_filter_mask[k++] = FIR_coef[i]; {
FIR_filter_mask[k++] = 0; FIR_filter_mask[k++] = FIR_coef[i];
} FIR_filter_mask[k++] = 0;
}
for (i = FFT_length + 1; i < FFT_length * 2; i++) for (i = FFT_length + 1; i < FFT_length * 2; i++)
{ {
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
} arm_fill_f32(0, last_sample_buffer_L, BUFFER_SIZE *4);
// for 1st time thru, zero out the last sample buffer to 0
memset(last_sample_buffer_L, 0, sizeof(last_sample_buffer_L)); state = 0;
state = 0; enabled = 1; //enable audio stream again
enabled = 1; //enable audio stream again
} }
void AudioFilterConvolution_F32::update(void) void AudioFilterConvolution_F32::update(void)
{ {
audio_block_f32_t *block; audio_block_f32_t *block;
float32_t *bp; float32_t *bp;
if (enabled != 1 ) return; if (enabled != 1 ) return;
block = receiveWritable_f32(0); // MUST be Writable, as convolution results are written into block block = receiveWritable_f32(0); // MUST be Writable, as convolution results are written into block
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;
bp = block->data; l = 1024;
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { for (int i = 0; i < 512; i++) {
*bp++ = tbuffer[i]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering) 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
AudioStream_F32::transmit(block); k++;
AudioStream_F32::release(block); l++;
state = 1; }
break; // convert floats to Q15 and save in temporary array tbuffer
case 1: arm_copy_f32 (&buffer[0], &tbuffer[0], BUFFER_SIZE*4);
bp = block->data; }
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { bp = block->data;
buffer[128+i] = *bp++; for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
} buffer[i] = *bp;
bp = block->data; *bp++ = tbuffer[i];
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::release(block);
AudioStream_F32::transmit(block); state = 1;
AudioStream_F32::release(block); break;
state = 2;
break; case 1:
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[128+i] = *bp;
buffer[256 + i] = *bp++; *bp++ = tbuffer[i+128];
} }
bp = block->data; AudioStream_F32::transmit(block);
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { AudioStream_F32::release(block);
*bp++ = tbuffer[i+256]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering) state = 2;
} break;
AudioStream_F32::transmit(block);
AudioStream_F32::release(block); case 2:
state = 3; bp = block->data;
break; for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
case 3: buffer[256 + i] = *bp;
bp = block->data; *bp++ = tbuffer[i+256]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering)
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { }
buffer[384 + i] = *bp++;
} AudioStream_F32::transmit(block);
bp = block->data; AudioStream_F32::release(block);
for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) { // zero pad last half of array- necessary to prevent aliasing in FFT
*bp++ = tbuffer[i + 384]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering) arm_fill_f32(0, FFT_buffer + 1024, FFT_length);
} state = 3;
AudioStream_F32::transmit(block); break;
AudioStream_F32::release(block);
state = 0; case 3:
// 4 blocks are in- now do the FFT1024,complex multiply and iFFT1024 on 512samples of data bp = block->data;
// using the overlap/add method for (int i = 0; i < AUDIO_BLOCK_SAMPLES; i++) {
// 1st convert Q15 samples to float buffer[384 + i] = *bp;
//arm_q15_to_float(buffer, float_buffer_L, 512); *bp++ = tbuffer[i + 384]; // tbuffer contains results of last FFT/multiply/iFFT processing (convolution filtering)
arm_copy_f32 (buffer, float_buffer_L, 512); }
// float_buffer samples are now standardized from > -1.0 to < 1.0 AudioStream_F32::transmit(block);
if (passThru ==0) { AudioStream_F32::release(block);
memset(FFT_buffer + 1024, 0, sizeof(FFT_buffer) / 2); // zero pad last half of array- necessary to prevent aliasing in FFT state = 0;
//fill FFT_buffer with current audio samples // 4 blocks are in- now do the FFT1024,complex multiply and iFFT1024 on 512samples of data
k = 0; // using the overlap/add method
for (i = 0; i < 512; i++) if (passThru ==0) {
{ //fill FFT_buffer with current audio samples
FFT_buffer[k++] = float_buffer_L[i]; // real k = 0;
FFT_buffer[k++] = float_buffer_L[i]; // imag for (i = 0; i < 512; i++)
} {
// calculations are performed in-place in FFT routines FFT_buffer[k++] = buffer[i]; // real
arm_cfft_f32(&arm_cfft_sR_f32_len1024, FFT_buffer, 0, 1);// perform complex FFT FFT_buffer[k++] = buffer[i]; // imag
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 // calculations are performed in-place in FFT routines
k = 0; arm_cfft_f32(&arm_cfft_sR_f32_len1024, FFT_buffer, 0, 1);// perform complex FFT
l = 1024; } //end if passTHru
for (int i = 0; i < 512; i++) { break;
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
// 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;
} }
}
} }
float32_t AudioFilterConvolution_F32::Izero (float32_t x) float32_t AudioFilterConvolution_F32::Izero (float32_t x)
@ -177,7 +163,7 @@ float32_t AudioFilterConvolution_F32::Izero (float32_t x)
} // END Izero } // END Izero
float AudioFilterConvolution_F32::m_sinc(int m, float fc) float AudioFilterConvolution_F32::m_sinc(int m, float fc)
{ // fc is f_cut/(Fsamp/2) { // fc is f_cut/(Fsamp/2)
// m is between -M and M step 2 // m is between -M and M step 2
// //
float x = m*PIH; float x = m*PIH;
@ -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
{ /*
nc = 2*(numCoeffs/2); else if (type==HILBERT)
{
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
* *
****************************************************************************** ******************************************************************************
@ -17,54 +17,84 @@
1) Class refactoring, change some methods visibility; 1) Class refactoring, change some methods visibility;
2) Filter coefficients calculation included into class; 2) Filter coefficients calculation included into class;
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: /* Additional Notes from Bob
* Object creations is required. See the OpenAudio_ArduinoLibrary
*** How to use it. *** * Design Tool for object declarations along with
* automatic generatin of code. As an example this could produce
Create an audio project based on chipaudette/OpenAudio_ArduinoLibrary * the following needed global code
like the Keiths SDR Project, and just add the h and cpp file to your * AudioFilterConvolution_F32 FilterConv(audio_settings);
processing chain as unique output filter: * AudioConnection_F32 patchCord1(FilterConv,0,Output_i2s,0);
* AudioConnection_F32 patchCord2(FilterConv,0,Output_i2s,1);
************************************************************ *
* There are three class functions:
1) Include the header * void initFilter(float32_t fc, float32_t Astop,
#include "AudioFilterConvolution_F32.h" (or OpenAudio_ArduinoLibrary.h) * int type, float32_t dfc);
* void passThrough(int stat);
2) Initialize as F32 block * float32_t* getCoeffPtr(void);
(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 * initFilter() is used to design the "mask" function that sets the filter
const int audio_block_samples = 128; * response. All filters use the Kaiser window that is characterized by
AudioSettings_F32 audio_settings(sample_rate_Hz, audio_block_samples); * a programable first sidelobe level and decreasing sidelobes as the
* frequency departs from the pass band. For many applications this is an
AudioFilterConvolution_F32 FilterConv(audio_settings); * excellent response. The response type is set by the integer "type." The
* options are:
3) Connect before output * type=LOWPASS Low pass with fc cutoff frequency and dfc not used.
* type=HIGHPASS High pass with fc cutoff frequency and dfc not used.
AudioConnection_F32 patchCord1(FilterConv,0,Output_i2s,0); * type=BANDPASS Band pass with fc center frequency and dfc pass band width.
AudioConnection_F32 patchCord2(FilterConv,0,Output_i2s,1); * type=BANDREJECT Band reject with fc center frequency and dfc reject band width.
* type=HILBERT Hilbert transform. *** Not Currently Available ***
4) Set the filter value when you need - some examples: *
// CW - Centered at 800Hz, ( 40 db x oct ), 2=BPF, width = 1200Hz * Astop is a value in dB that approximates the first sidelobe level
FilterConv.initFilter((float32_t)800, 40, 2, 1200.0); * going into the stop band. This is a feature of the Kaiser window that
* allows trading off first sidelobe levels against the speed of
// SSB - Centered at 1500Hz, ( 60 db x oct ), 2=BPF, width = 3000Hz * transition from the passband to the stop band(s). Values in the 25
FilterConv.initFilter((float32_t)1500, 60, 2, 3000.0); * to 70 dB range work well.
************************************************************** */ *
/* Additional Notes from Bob * Two examples of initFilter():
* Measured 128 word update in update() is 248 microseconds (T4.x) * // IK8YFW CW - Centered at 800Hz, ( 40 db x oct ), 2=BPF, width = 1200Hz
* Comparison with a conventional FIR, from this library, the * FilterConv.initFilter((float32_t)800, 40, 2, 1200.0);
* AudioFilterFIRGeneral_F32 showed that a 512 tap FIR gave *
* essentially the same response and was slightly faster at * // IK8YFWSSB - Centered at 1500Hz, ( 60 db x oct ), 2=BPF, width = 3000Hz
* 225 microseconds per update. Also, note that this form of * FilterConv.initFilter((float32_t)1500, 60, 2, 3000.0);
* computation uses about 52 kB of memory where the direct FIR *
* uses about 10 kB. The responses differ in only minor ways. * 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