-
Notifications
You must be signed in to change notification settings - Fork 32
/
Copy pathAudioFilterFIRGeneral_F32.cpp
259 lines (241 loc) · 11 KB
/
AudioFilterFIRGeneral_F32.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
/* 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<nFIR; i++) // To be sure, zero the coefficients
cf32f[i] = 0.0f;
for(i=0; i<(nFIR+AUDIO_BLOCK_SAMPLES); i++) // And the storage
pStateArray[i] = 0.0f;
// Convert dB to "Voltage" ratios, frequencies to fractions of sampling freq
// Borrow pStateArray, as it has not yet gone to ARM math
for(i=0; i<=nHalfFIR; i++)
pStateArray[i] = powf(10.0, 0.05f*adb[i]);
/* Find FIR coefficients, the Fourier transform of the frequency
* response. This general frequency description has half as many
* frequency dB points as FIR coefficients. If nFIR is odd,
* the number of frequency points is nHalfFIR = (nFIR - 1)/2.
*
* Numbering example for nFIR==21:
* Odd nFIR: Subscript 0 to 9 is 10 taps; 11 to 20 is 10 taps; 10+1+10=21 taps
*
* Numbering example for nFIR==20:
* Even nFIR: Subscript 0 to 9 is 10 taps; 10 to 19 is 10 taps; 10+10=20 taps
*/
float32_t oneOnNFIR = 1.0f / (float32_t)nFIR;
if(even) {
for(n=0; n<nHalfFIR; n++) { // Over half of even nFIR FIR coeffs
cf32f[n] = pStateArray[0]; // DC term
for(k=1; k<nHalfFIR; k++) { // Over all frequencies, half of nFIR
num = (M - (float32_t)n) * (float32_t)k;
cf32f[n] += 2.0f*pStateArray[k]*cosf(MF_TWOPI*num*oneOnNFIR);
}
cf32f[n] *= oneOnNFIR; // Divide by nFIR
}
}
else { // nFIR is odd
for(n=0; n<=nHalfFIR; n++) { // Over half of FIR coeffs, nFIR odd
cf32f[n] = pStateArray[0];
for(k=1; k<=nHalfFIR; k++) {
num = (float32_t)((nHalfFIR - n)*k);
cf32f[n] += 2.0f*pStateArray[k]*cosf(MF_TWOPI*num*oneOnNFIR);
}
cf32f[n] *= oneOnNFIR;
}
}
/* At this point, the cf32f[] coefficients are simply truncated, creating
* high sidelobe responses. To reduce the sidelobes, a windowing function is applied.
* This has the side affect of increasing the rate of cutoff for sharp frequency transition.
* The only windowing function available here is that of James Kaiser. This has a number
* of desirable features. The sidelobes drop off as the frequency away from a transition.
* Also, the tradeoff of sidelobe level versus cutoff rate is variable.
* Here we specify it in terms of kdb, the highest sidelobe, in dB, next to a sharp cutoff. For
* calculating the windowing vector, we need a parameter beta, found as follows:
*/
if (kdb<0.0f) return ERR_FIRGEN_SIDELOBES;
if (kdb < 20.0f)
beta = 0.0;
else
beta = -2.17+0.17153*kdb-0.0002841*kdb*kdb; // Within a dB or so
// Note: i0f is the fp zero'th order modified Bessel function (see mathDSP_F32.h)
kbes = 1.0f / mathEqualizer.i0f(beta); // An additional derived parameter used in loop
scaleXn2 = 4.0f / ( ((float32_t)nFIR - 1.0f)*((float32_t)nFIR - 1.0f) ); // Needed for even & odd
if (even) {
// nHalfFIR = nFIR/2; for nFIR even
for (n=0; n<nHalfFIR; n++) { // For 20 Taps, this is 0 to 9
xn2 = 0.5f+(float32_t)n;
xn2 = scaleXn2*xn2*xn2;
WindowWt=kbes*(mathEqualizer.i0f(beta*sqrt(1.0-xn2)));
if(kdb > 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<nFreq; i++) {
bt = 0.0;
for (n=0; n<nHalfFIR; n++) // Add in terms twice, as they are symmetric
bt += 2.0f*cf32f[n]*cosf(piOnNfreq*((M-(float32_t)n)*(float32_t)i));
rdb[i] = 20.0f*log10f(fabsf(bt)); // Convert to dB
}
}
else { // it is odd
nHalfFIR = (nFIR - 1)/2;
for (i=0; i<nFreq; i++) {
bt = cf32f[nHalfFIR]; // Center coefficient
for (n=0; n<nHalfFIR; n++) // Add in the others twice, as they are symmetric
bt += 2.0f*cf32f[n]*cosf(piOnNfreq*(float32_t)((nHalfFIR-n)*i));
rdb[i] = 20.0f*log10f(fabsf(bt));
}
}
}