/* AudioFilterFIRGeneralr_F32.cpp * * Bob Larkin, W7PUA 20 May 2020 (c) * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* A math note - The design of the FIR filter from the frequency response requires * taking a discrete Fourier transform. The calculation of the achieved * frequency response requires another Fourier transform. Good news is * that this is arithmetically simple, because of * working with real (not complex) inputs and requiring the FIR be linear * phase. One of a number of references is C.S. Burris and T. W. Parks, * "Digital Filter Design." That book also has excellent sections on * Chebychev error (Parks-McClellan-Rabiner) FIR filters and IIR Filters. */ #include "AudioFilterFIRGeneral_F32.h" void AudioFilterFIRGeneral_F32::update(void) { audio_block_f32_t *blockIn, *blockOut; #if TEST_TIME_FIRG if (iitt++ >1000000) iitt = -10; uint32_t t1, t2; t1 = tElapse; #endif blockIn = AudioStream_F32::receiveReadOnly_f32(); if (!blockIn) return; // If there's no coefficient table, give up. if (cf32f == NULL) { AudioStream_F32::release(blockIn); return; } blockOut = AudioStream_F32::allocate_f32(); // get a block for the FIR output if (blockOut) { // The FIR update arm_fir_f32(&fir_inst, blockIn->data, blockOut->data, blockIn->length); AudioStream_F32::transmit(blockOut); // send the FIR output AudioStream_F32::release(blockOut); } AudioStream_F32::release(blockIn); #if TEST_TIME_FIRG t2 = tElapse; if(iitt++ < 0) {Serial.print("At FIRGeneral end, microseconds = "); Serial.println (t2 - t1); // } Serial.print("numtaps = "); Serial.println (fir_inst.numTaps); } t1 = tElapse; #endif } /* FIRGeneralNew() calculates the generalFIR filter coefficients. Works from: * adb Pointer to nFIR/2 array adb[] of levels, in dB * nFIR The number of FIR coefficients (taps) used * cf32f Pointer to an array of float to hold nFIR FIR coefficients * kdb A parameter that trades off sidelobe levels for sharpness of band transition. * kdb=30 sharp cutoff, poor sidelobes * kdb=60 slow cutoff, low sidelobes * pStateArray Pointer to 128+nFIR array of floats, used here briefly and then * passed to the FIR routine as working storage * * The arrays, adb[], cf32f[] and pStateArray[] are supplied by the calling .INO * * Returns: 0 if successful, or an error code if not. * Errors: 1 = NU * 2 = sidelobe level out of range, must be > 0 * 3 = nFIR out of range * * Note - This function runs at setup time, so slowness is not an issue */ uint16_t AudioFilterFIRGeneral_F32::FIRGeneralNew( float32_t *adb, uint16_t _nFIR, float32_t *_cf32f, float32_t kdb, float32_t *pStateArray) { uint16_t i, k, n; uint16_t nHalfFIR; float32_t beta, kbes; float32_t num, xn2, scaleXn2, WindowWt; float32_t M; // Burris and Parks, (2.16) mathDSP_F32 mathEqualizer; // For Bessel function bool even; cf32f = _cf32f; // Make pivate copies nFIR = _nFIR; // Check range of nFIR if (nFIR<4) return ERR_FIRGEN_NFIR; M = 0.5f*(float32_t)(nFIR - 1); // The number of FIR coefficients is even or odd if (2*(nFIR/2) == nFIR) { even = true; nHalfFIR = nFIR/2; } else { even = false; nHalfFIR = (nFIR - 1)/2; } for (i=0; i 0.1f) // kdb==0.0 means no window cf32f[nHalfFIR - n - 1] *= WindowWt; // Apply window, reverse subscripts cf32f[nHalfFIR + n] = cf32f[nHalfFIR - n - 1]; // and create the upper half } } else { // nFIR is odd, nHalfFIR = (nFIR - 1)/2 for (n=0; n<=nHalfFIR; n++) { // For 21 Taps, this is 0 to 10, including center xn2 = (int16_t)(0.5f+(float32_t)n); xn2 = scaleXn2*xn2*xn2; WindowWt=kbes*(mathEqualizer.i0f(beta*sqrt(1.0-xn2))); if(kdb > 0.1f) cf32f[nHalfFIR - n] *= WindowWt; // 21 taps, for n=0, nHalfFIR-n = 10, for n=1 nHalfFIR-n=9, for n=nHalfFIR, nHalfFIR-n=0 cf32f[nHalfFIR + n] = cf32f[nHalfFIR - n]; // and create upper half (rewrite center is OK) } } // And finally, fill in the members of fir_inst given in update() to the ARM FIR routine. AudioNoInterrupts(); arm_fir_init_f32(&fir_inst, nFIR, (float32_t *)cf32f, &pStateArray[0], (uint32_t)block_size); AudioInterrupts(); return 0; } // FIRGeneralLoad() allows an array of nFIR FIR coefficients to be loaded. They come from an .INO // supplied array. Also, pStateArray[] is .INO supplied and must be (block_size + nFIR) in size. uint16_t AudioFilterFIRGeneral_F32::LoadCoeffs(uint16_t _nFIR, float32_t *_cf32f, float32_t *pStateArray) { nFIR = _nFIR; cf32f = _cf32f; if (nFIR<4) // Check range of nFIR return ERR_FIRGEN_NFIR; for(int i=0; i<(nFIR+AUDIO_BLOCK_SAMPLES); i++) // Zero, to be sure pStateArray[i] = 0.0f; AudioNoInterrupts(); arm_fir_init_f32(&fir_inst, nFIR, &cf32f[0], &pStateArray[0], (uint32_t)block_size); AudioInterrupts(); return 0; } /* Calculate frequency response in dB. Leave nFreq point result in array rdb[] supplied * by the calling .INO See B&P p27 (Type 1 and 2). Be aware that if nFIR*nFreq is big, * like 100,000 or more, this will take a while to calcuulate all the cosf(). Normally, * this is not an issue as this is an infrequent calculation. * This function assumes that the phase of the FIR is linear with frequency, i.e., * the coefficients are symmetrical about the middle. Otherwise, doubling of values, * as is done here, is not valid. */ void AudioFilterFIRGeneral_F32::getResponse(uint16_t nFreq, float32_t *rdb) { uint16_t i, n; float32_t bt; float32_t piOnNfreq; uint16_t nHalfFIR; float32_t M; piOnNfreq = MF_PI / (float32_t)nFreq; // The number of FIR coefficients, even or odd? if (2*(nFIR/2) == nFIR) { // it is even nHalfFIR = nFIR/2; M = 0.5f*(float32_t)(nFIR - 1); for (i=0; i