xref: /aosp_15_r20/frameworks/av/media/libaudioprocessing/AudioResamplerFirGen.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1*ec779b8eSAndroid Build Coastguard Worker /*
2*ec779b8eSAndroid Build Coastguard Worker  * Copyright (C) 2013 The Android Open Source Project
3*ec779b8eSAndroid Build Coastguard Worker  *
4*ec779b8eSAndroid Build Coastguard Worker  * Licensed under the Apache License, Version 2.0 (the "License");
5*ec779b8eSAndroid Build Coastguard Worker  * you may not use this file except in compliance with the License.
6*ec779b8eSAndroid Build Coastguard Worker  * You may obtain a copy of the License at
7*ec779b8eSAndroid Build Coastguard Worker  *
8*ec779b8eSAndroid Build Coastguard Worker  *      http://www.apache.org/licenses/LICENSE-2.0
9*ec779b8eSAndroid Build Coastguard Worker  *
10*ec779b8eSAndroid Build Coastguard Worker  * Unless required by applicable law or agreed to in writing, software
11*ec779b8eSAndroid Build Coastguard Worker  * distributed under the License is distributed on an "AS IS" BASIS,
12*ec779b8eSAndroid Build Coastguard Worker  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*ec779b8eSAndroid Build Coastguard Worker  * See the License for the specific language governing permissions and
14*ec779b8eSAndroid Build Coastguard Worker  * limitations under the License.
15*ec779b8eSAndroid Build Coastguard Worker  */
16*ec779b8eSAndroid Build Coastguard Worker 
17*ec779b8eSAndroid Build Coastguard Worker #ifndef ANDROID_AUDIO_RESAMPLER_FIR_GEN_H
18*ec779b8eSAndroid Build Coastguard Worker #define ANDROID_AUDIO_RESAMPLER_FIR_GEN_H
19*ec779b8eSAndroid Build Coastguard Worker 
20*ec779b8eSAndroid Build Coastguard Worker #include "utils/Compat.h"
21*ec779b8eSAndroid Build Coastguard Worker 
22*ec779b8eSAndroid Build Coastguard Worker namespace android {
23*ec779b8eSAndroid Build Coastguard Worker 
24*ec779b8eSAndroid Build Coastguard Worker /*
25*ec779b8eSAndroid Build Coastguard Worker  * generates a sine wave at equal steps.
26*ec779b8eSAndroid Build Coastguard Worker  *
27*ec779b8eSAndroid Build Coastguard Worker  * As most of our functions use sine or cosine at equal steps,
28*ec779b8eSAndroid Build Coastguard Worker  * it is very efficient to compute them that way (single multiply and subtract),
29*ec779b8eSAndroid Build Coastguard Worker  * rather than invoking the math library sin() or cos() each time.
30*ec779b8eSAndroid Build Coastguard Worker  *
31*ec779b8eSAndroid Build Coastguard Worker  * SineGen uses Goertzel's Algorithm (as a generator not a filter)
32*ec779b8eSAndroid Build Coastguard Worker  * to calculate sine(wstart + n * wstep) or cosine(wstart + n * wstep)
33*ec779b8eSAndroid Build Coastguard Worker  * by stepping through 0, 1, ... n.
34*ec779b8eSAndroid Build Coastguard Worker  *
35*ec779b8eSAndroid Build Coastguard Worker  * e^i(wstart+wstep) = 2cos(wstep) * e^i(wstart) - e^i(wstart-wstep)
36*ec779b8eSAndroid Build Coastguard Worker  *
37*ec779b8eSAndroid Build Coastguard Worker  * or looking at just the imaginary sine term, as the cosine follows identically:
38*ec779b8eSAndroid Build Coastguard Worker  *
39*ec779b8eSAndroid Build Coastguard Worker  * sin(wstart+wstep) = 2cos(wstep) * sin(wstart) - sin(wstart-wstep)
40*ec779b8eSAndroid Build Coastguard Worker  *
41*ec779b8eSAndroid Build Coastguard Worker  * Goertzel's algorithm is more efficient than the angle addition formula,
42*ec779b8eSAndroid Build Coastguard Worker  * e^i(wstart+wstep) = e^i(wstart) * e^i(wstep), which takes up to
43*ec779b8eSAndroid Build Coastguard Worker  * 4 multiplies and 2 adds (or 3* and 3+) and requires both sine and
44*ec779b8eSAndroid Build Coastguard Worker  * cosine generation due to the complex * complex multiply (full rotation).
45*ec779b8eSAndroid Build Coastguard Worker  *
46*ec779b8eSAndroid Build Coastguard Worker  * See: http://en.wikipedia.org/wiki/Goertzel_algorithm
47*ec779b8eSAndroid Build Coastguard Worker  *
48*ec779b8eSAndroid Build Coastguard Worker  */
49*ec779b8eSAndroid Build Coastguard Worker 
50*ec779b8eSAndroid Build Coastguard Worker class SineGen {
51*ec779b8eSAndroid Build Coastguard Worker public:
52*ec779b8eSAndroid Build Coastguard Worker     SineGen(double wstart, double wstep, bool cosine = false) {
53*ec779b8eSAndroid Build Coastguard Worker         if (cosine) {
54*ec779b8eSAndroid Build Coastguard Worker             mCurrent = cos(wstart);
55*ec779b8eSAndroid Build Coastguard Worker             mPrevious = cos(wstart - wstep);
56*ec779b8eSAndroid Build Coastguard Worker         } else {
57*ec779b8eSAndroid Build Coastguard Worker             mCurrent = sin(wstart);
58*ec779b8eSAndroid Build Coastguard Worker             mPrevious = sin(wstart - wstep);
59*ec779b8eSAndroid Build Coastguard Worker         }
60*ec779b8eSAndroid Build Coastguard Worker         mTwoCos = 2.*cos(wstep);
61*ec779b8eSAndroid Build Coastguard Worker     }
SineGen(double expNow,double expPrev,double twoCosStep)62*ec779b8eSAndroid Build Coastguard Worker     SineGen(double expNow, double expPrev, double twoCosStep) {
63*ec779b8eSAndroid Build Coastguard Worker         mCurrent = expNow;
64*ec779b8eSAndroid Build Coastguard Worker         mPrevious = expPrev;
65*ec779b8eSAndroid Build Coastguard Worker         mTwoCos = twoCosStep;
66*ec779b8eSAndroid Build Coastguard Worker     }
value()67*ec779b8eSAndroid Build Coastguard Worker     inline double value() const {
68*ec779b8eSAndroid Build Coastguard Worker         return mCurrent;
69*ec779b8eSAndroid Build Coastguard Worker     }
advance()70*ec779b8eSAndroid Build Coastguard Worker     inline void advance() {
71*ec779b8eSAndroid Build Coastguard Worker         double tmp = mCurrent;
72*ec779b8eSAndroid Build Coastguard Worker         mCurrent = mCurrent*mTwoCos - mPrevious;
73*ec779b8eSAndroid Build Coastguard Worker         mPrevious = tmp;
74*ec779b8eSAndroid Build Coastguard Worker     }
valueAdvance()75*ec779b8eSAndroid Build Coastguard Worker     inline double valueAdvance() {
76*ec779b8eSAndroid Build Coastguard Worker         double tmp = mCurrent;
77*ec779b8eSAndroid Build Coastguard Worker         mCurrent = mCurrent*mTwoCos - mPrevious;
78*ec779b8eSAndroid Build Coastguard Worker         mPrevious = tmp;
79*ec779b8eSAndroid Build Coastguard Worker         return tmp;
80*ec779b8eSAndroid Build Coastguard Worker     }
81*ec779b8eSAndroid Build Coastguard Worker 
82*ec779b8eSAndroid Build Coastguard Worker private:
83*ec779b8eSAndroid Build Coastguard Worker     double mCurrent; // current value of sine/cosine
84*ec779b8eSAndroid Build Coastguard Worker     double mPrevious; // previous value of sine/cosine
85*ec779b8eSAndroid Build Coastguard Worker     double mTwoCos; // stepping factor
86*ec779b8eSAndroid Build Coastguard Worker };
87*ec779b8eSAndroid Build Coastguard Worker 
88*ec779b8eSAndroid Build Coastguard Worker /*
89*ec779b8eSAndroid Build Coastguard Worker  * generates a series of sine generators, phase offset by fixed steps.
90*ec779b8eSAndroid Build Coastguard Worker  *
91*ec779b8eSAndroid Build Coastguard Worker  * This is used to generate polyphase sine generators, one per polyphase
92*ec779b8eSAndroid Build Coastguard Worker  * in the filter code below.
93*ec779b8eSAndroid Build Coastguard Worker  *
94*ec779b8eSAndroid Build Coastguard Worker  * The SineGen returned by value() starts at innerStart = outerStart + n*outerStep;
95*ec779b8eSAndroid Build Coastguard Worker  * increments by innerStep.
96*ec779b8eSAndroid Build Coastguard Worker  *
97*ec779b8eSAndroid Build Coastguard Worker  */
98*ec779b8eSAndroid Build Coastguard Worker 
99*ec779b8eSAndroid Build Coastguard Worker class SineGenGen {
100*ec779b8eSAndroid Build Coastguard Worker public:
101*ec779b8eSAndroid Build Coastguard Worker     SineGenGen(double outerStart, double outerStep, double innerStep, bool cosine = false)
mSineInnerCur(outerStart,outerStep,cosine)102*ec779b8eSAndroid Build Coastguard Worker             : mSineInnerCur(outerStart, outerStep, cosine),
103*ec779b8eSAndroid Build Coastguard Worker               mSineInnerPrev(outerStart-innerStep, outerStep, cosine)
104*ec779b8eSAndroid Build Coastguard Worker     {
105*ec779b8eSAndroid Build Coastguard Worker         mTwoCos = 2.*cos(innerStep);
106*ec779b8eSAndroid Build Coastguard Worker     }
value()107*ec779b8eSAndroid Build Coastguard Worker     inline SineGen value() {
108*ec779b8eSAndroid Build Coastguard Worker         return SineGen(mSineInnerCur.value(), mSineInnerPrev.value(), mTwoCos);
109*ec779b8eSAndroid Build Coastguard Worker     }
advance()110*ec779b8eSAndroid Build Coastguard Worker     inline void advance() {
111*ec779b8eSAndroid Build Coastguard Worker         mSineInnerCur.advance();
112*ec779b8eSAndroid Build Coastguard Worker         mSineInnerPrev.advance();
113*ec779b8eSAndroid Build Coastguard Worker     }
valueAdvance()114*ec779b8eSAndroid Build Coastguard Worker     inline SineGen valueAdvance() {
115*ec779b8eSAndroid Build Coastguard Worker         return SineGen(mSineInnerCur.valueAdvance(), mSineInnerPrev.valueAdvance(), mTwoCos);
116*ec779b8eSAndroid Build Coastguard Worker     }
117*ec779b8eSAndroid Build Coastguard Worker 
118*ec779b8eSAndroid Build Coastguard Worker private:
119*ec779b8eSAndroid Build Coastguard Worker     SineGen mSineInnerCur; // generate the inner sine values (stepped by outerStep).
120*ec779b8eSAndroid Build Coastguard Worker     SineGen mSineInnerPrev; // generate the inner sine previous values
121*ec779b8eSAndroid Build Coastguard Worker                             // (behind by innerStep, stepped by outerStep).
122*ec779b8eSAndroid Build Coastguard Worker     double mTwoCos; // the inner stepping factor for the returned SineGen.
123*ec779b8eSAndroid Build Coastguard Worker };
124*ec779b8eSAndroid Build Coastguard Worker 
sqr(double x)125*ec779b8eSAndroid Build Coastguard Worker static inline double sqr(double x) {
126*ec779b8eSAndroid Build Coastguard Worker     return x * x;
127*ec779b8eSAndroid Build Coastguard Worker }
128*ec779b8eSAndroid Build Coastguard Worker 
129*ec779b8eSAndroid Build Coastguard Worker /*
130*ec779b8eSAndroid Build Coastguard Worker  * rounds a double to the nearest integer for FIR coefficients.
131*ec779b8eSAndroid Build Coastguard Worker  *
132*ec779b8eSAndroid Build Coastguard Worker  * One variant uses noise shaping, which must keep error history
133*ec779b8eSAndroid Build Coastguard Worker  * to work (the err parameter, initialized to 0).
134*ec779b8eSAndroid Build Coastguard Worker  * The other variant is a non-noise shaped version for
135*ec779b8eSAndroid Build Coastguard Worker  * S32 coefficients (noise shaping doesn't gain much).
136*ec779b8eSAndroid Build Coastguard Worker  *
137*ec779b8eSAndroid Build Coastguard Worker  * Caution: No bounds saturation is applied, but isn't needed in this case.
138*ec779b8eSAndroid Build Coastguard Worker  *
139*ec779b8eSAndroid Build Coastguard Worker  * @param x is the value to round.
140*ec779b8eSAndroid Build Coastguard Worker  *
141*ec779b8eSAndroid Build Coastguard Worker  * @param maxval is the maximum integer scale factor expressed as an int64 (for headroom).
142*ec779b8eSAndroid Build Coastguard Worker  * Typically this may be the maximum positive integer+1 (using the fact that double precision
143*ec779b8eSAndroid Build Coastguard Worker  * FIR coefficients generated here are never that close to 1.0 to pose an overflow condition).
144*ec779b8eSAndroid Build Coastguard Worker  *
145*ec779b8eSAndroid Build Coastguard Worker  * @param err is the previous error (actual - rounded) for the previous rounding op.
146*ec779b8eSAndroid Build Coastguard Worker  * For 16b coefficients this can improve stopband dB performance by up to 2dB.
147*ec779b8eSAndroid Build Coastguard Worker  *
148*ec779b8eSAndroid Build Coastguard Worker  * Many variants exist for the noise shaping: http://en.wikipedia.org/wiki/Noise_shaping
149*ec779b8eSAndroid Build Coastguard Worker  *
150*ec779b8eSAndroid Build Coastguard Worker  */
151*ec779b8eSAndroid Build Coastguard Worker 
toint(double x,int64_t maxval,double & err)152*ec779b8eSAndroid Build Coastguard Worker static inline int64_t toint(double x, int64_t maxval, double& err) {
153*ec779b8eSAndroid Build Coastguard Worker     double val = x * maxval;
154*ec779b8eSAndroid Build Coastguard Worker     double ival = floor(val + 0.5 + err*0.2);
155*ec779b8eSAndroid Build Coastguard Worker     err = val - ival;
156*ec779b8eSAndroid Build Coastguard Worker     return static_cast<int64_t>(ival);
157*ec779b8eSAndroid Build Coastguard Worker }
158*ec779b8eSAndroid Build Coastguard Worker 
toint(double x,int64_t maxval)159*ec779b8eSAndroid Build Coastguard Worker static inline int64_t toint(double x, int64_t maxval) {
160*ec779b8eSAndroid Build Coastguard Worker     return static_cast<int64_t>(floor(x * maxval + 0.5));
161*ec779b8eSAndroid Build Coastguard Worker }
162*ec779b8eSAndroid Build Coastguard Worker 
163*ec779b8eSAndroid Build Coastguard Worker /*
164*ec779b8eSAndroid Build Coastguard Worker  * Modified Bessel function of the first kind
165*ec779b8eSAndroid Build Coastguard Worker  * http://en.wikipedia.org/wiki/Bessel_function
166*ec779b8eSAndroid Build Coastguard Worker  *
167*ec779b8eSAndroid Build Coastguard Worker  * The formulas are taken from Abramowitz and Stegun,
168*ec779b8eSAndroid Build Coastguard Worker  * _Handbook of Mathematical Functions_ (links below):
169*ec779b8eSAndroid Build Coastguard Worker  *
170*ec779b8eSAndroid Build Coastguard Worker  * http://people.math.sfu.ca/~cbm/aands/page_375.htm
171*ec779b8eSAndroid Build Coastguard Worker  * http://people.math.sfu.ca/~cbm/aands/page_378.htm
172*ec779b8eSAndroid Build Coastguard Worker  *
173*ec779b8eSAndroid Build Coastguard Worker  * http://dlmf.nist.gov/10.25
174*ec779b8eSAndroid Build Coastguard Worker  * http://dlmf.nist.gov/10.40
175*ec779b8eSAndroid Build Coastguard Worker  *
176*ec779b8eSAndroid Build Coastguard Worker  * Note we assume x is nonnegative (the function is symmetric,
177*ec779b8eSAndroid Build Coastguard Worker  * pass in the absolute value as needed).
178*ec779b8eSAndroid Build Coastguard Worker  *
179*ec779b8eSAndroid Build Coastguard Worker  * Constants are compile time derived with templates I0Term<> and
180*ec779b8eSAndroid Build Coastguard Worker  * I0ATerm<> to the precision of the compiler.  The series can be expanded
181*ec779b8eSAndroid Build Coastguard Worker  * to any precision needed, but currently set around 24b precision.
182*ec779b8eSAndroid Build Coastguard Worker  *
183*ec779b8eSAndroid Build Coastguard Worker  * We use a bit of template math here, constexpr would probably be
184*ec779b8eSAndroid Build Coastguard Worker  * more appropriate for a C++11 compiler.
185*ec779b8eSAndroid Build Coastguard Worker  *
186*ec779b8eSAndroid Build Coastguard Worker  * For the intermediate range 3.75 < x < 15, we use minimax polynomial fit.
187*ec779b8eSAndroid Build Coastguard Worker  *
188*ec779b8eSAndroid Build Coastguard Worker  */
189*ec779b8eSAndroid Build Coastguard Worker 
190*ec779b8eSAndroid Build Coastguard Worker template <int N>
191*ec779b8eSAndroid Build Coastguard Worker struct I0Term {
192*ec779b8eSAndroid Build Coastguard Worker     static const CONSTEXPR double value = I0Term<N-1>::value / (4. * N * N);
193*ec779b8eSAndroid Build Coastguard Worker };
194*ec779b8eSAndroid Build Coastguard Worker 
195*ec779b8eSAndroid Build Coastguard Worker template <>
196*ec779b8eSAndroid Build Coastguard Worker struct I0Term<0> {
197*ec779b8eSAndroid Build Coastguard Worker     static const CONSTEXPR double value = 1.;
198*ec779b8eSAndroid Build Coastguard Worker };
199*ec779b8eSAndroid Build Coastguard Worker 
200*ec779b8eSAndroid Build Coastguard Worker template <int N>
201*ec779b8eSAndroid Build Coastguard Worker struct I0ATerm {
202*ec779b8eSAndroid Build Coastguard Worker     static const CONSTEXPR double value = I0ATerm<N-1>::value * (2.*N-1.) * (2.*N-1.) / (8. * N);
203*ec779b8eSAndroid Build Coastguard Worker };
204*ec779b8eSAndroid Build Coastguard Worker 
205*ec779b8eSAndroid Build Coastguard Worker template <>
206*ec779b8eSAndroid Build Coastguard Worker struct I0ATerm<0> { // 1/sqrt(2*PI);
207*ec779b8eSAndroid Build Coastguard Worker     static const CONSTEXPR double value =
208*ec779b8eSAndroid Build Coastguard Worker             0.398942280401432677939946059934381868475858631164934657665925;
209*ec779b8eSAndroid Build Coastguard Worker };
210*ec779b8eSAndroid Build Coastguard Worker 
211*ec779b8eSAndroid Build Coastguard Worker #if USE_HORNERS_METHOD
212*ec779b8eSAndroid Build Coastguard Worker /* Polynomial evaluation of A + Bx + Cx^2 + Dx^3 + ...
213*ec779b8eSAndroid Build Coastguard Worker  * using Horner's Method: http://en.wikipedia.org/wiki/Horner's_method
214*ec779b8eSAndroid Build Coastguard Worker  *
215*ec779b8eSAndroid Build Coastguard Worker  * This has fewer multiplications than Estrin's method below, but has back to back
216*ec779b8eSAndroid Build Coastguard Worker  * floating point dependencies.
217*ec779b8eSAndroid Build Coastguard Worker  *
218*ec779b8eSAndroid Build Coastguard Worker  * On ARM this appears to work slower, so USE_HORNERS_METHOD is not default enabled.
219*ec779b8eSAndroid Build Coastguard Worker  */
220*ec779b8eSAndroid Build Coastguard Worker 
221*ec779b8eSAndroid Build Coastguard Worker inline double Poly2(double A, double B, double x) {
222*ec779b8eSAndroid Build Coastguard Worker     return A + x * B;
223*ec779b8eSAndroid Build Coastguard Worker }
224*ec779b8eSAndroid Build Coastguard Worker 
225*ec779b8eSAndroid Build Coastguard Worker inline double Poly4(double A, double B, double C, double D, double x) {
226*ec779b8eSAndroid Build Coastguard Worker     return A + x * (B + x * (C + x * (D)));
227*ec779b8eSAndroid Build Coastguard Worker }
228*ec779b8eSAndroid Build Coastguard Worker 
229*ec779b8eSAndroid Build Coastguard Worker inline double Poly7(double A, double B, double C, double D, double E, double F, double G,
230*ec779b8eSAndroid Build Coastguard Worker         double x) {
231*ec779b8eSAndroid Build Coastguard Worker     return A + x * (B + x * (C + x * (D + x * (E + x * (F + x * (G))))));
232*ec779b8eSAndroid Build Coastguard Worker }
233*ec779b8eSAndroid Build Coastguard Worker 
234*ec779b8eSAndroid Build Coastguard Worker inline double Poly9(double A, double B, double C, double D, double E, double F, double G,
235*ec779b8eSAndroid Build Coastguard Worker         double H, double I, double x) {
236*ec779b8eSAndroid Build Coastguard Worker     return A + x * (B + x * (C + x * (D + x * (E + x * (F + x * (G + x * (H + x * (I))))))));
237*ec779b8eSAndroid Build Coastguard Worker }
238*ec779b8eSAndroid Build Coastguard Worker 
239*ec779b8eSAndroid Build Coastguard Worker #else
240*ec779b8eSAndroid Build Coastguard Worker /* Polynomial evaluation of A + Bx + Cx^2 + Dx^3 + ...
241*ec779b8eSAndroid Build Coastguard Worker  * using Estrin's Method: http://en.wikipedia.org/wiki/Estrin's_scheme
242*ec779b8eSAndroid Build Coastguard Worker  *
243*ec779b8eSAndroid Build Coastguard Worker  * This is typically faster, perhaps gains about 5-10% overall on ARM processors
244*ec779b8eSAndroid Build Coastguard Worker  * over Horner's method above.
245*ec779b8eSAndroid Build Coastguard Worker  */
246*ec779b8eSAndroid Build Coastguard Worker 
247*ec779b8eSAndroid Build Coastguard Worker inline double Poly2(double A, double B, double x) {
248*ec779b8eSAndroid Build Coastguard Worker     return A + B * x;
249*ec779b8eSAndroid Build Coastguard Worker }
250*ec779b8eSAndroid Build Coastguard Worker 
251*ec779b8eSAndroid Build Coastguard Worker inline double Poly3(double A, double B, double C, double x, double x2) {
252*ec779b8eSAndroid Build Coastguard Worker     return Poly2(A, B, x) + C * x2;
253*ec779b8eSAndroid Build Coastguard Worker }
254*ec779b8eSAndroid Build Coastguard Worker 
255*ec779b8eSAndroid Build Coastguard Worker inline double Poly3(double A, double B, double C, double x) {
256*ec779b8eSAndroid Build Coastguard Worker     return Poly2(A, B, x) + C * x * x;
257*ec779b8eSAndroid Build Coastguard Worker }
258*ec779b8eSAndroid Build Coastguard Worker 
259*ec779b8eSAndroid Build Coastguard Worker inline double Poly4(double A, double B, double C, double D, double x, double x2) {
260*ec779b8eSAndroid Build Coastguard Worker     return Poly2(A, B, x) + Poly2(C, D, x) * x2; // same as poly2(poly2, poly2, x2);
261*ec779b8eSAndroid Build Coastguard Worker }
262*ec779b8eSAndroid Build Coastguard Worker 
263*ec779b8eSAndroid Build Coastguard Worker inline double Poly4(double A, double B, double C, double D, double x) {
264*ec779b8eSAndroid Build Coastguard Worker     return Poly4(A, B, C, D, x, x * x);
265*ec779b8eSAndroid Build Coastguard Worker }
266*ec779b8eSAndroid Build Coastguard Worker 
267*ec779b8eSAndroid Build Coastguard Worker inline double Poly7(double A, double B, double C, double D, double E, double F, double G,
268*ec779b8eSAndroid Build Coastguard Worker         double x) {
269*ec779b8eSAndroid Build Coastguard Worker     double x2 = x * x;
270*ec779b8eSAndroid Build Coastguard Worker     return Poly4(A, B, C, D, x, x2) + Poly3(E, F, G, x, x2) * (x2 * x2);
271*ec779b8eSAndroid Build Coastguard Worker }
272*ec779b8eSAndroid Build Coastguard Worker 
273*ec779b8eSAndroid Build Coastguard Worker inline double Poly8(double A, double B, double C, double D, double E, double F, double G,
274*ec779b8eSAndroid Build Coastguard Worker         double H, double x, double x2, double x4) {
275*ec779b8eSAndroid Build Coastguard Worker     return Poly4(A, B, C, D, x, x2) + Poly4(E, F, G, H, x, x2) * x4;
276*ec779b8eSAndroid Build Coastguard Worker }
277*ec779b8eSAndroid Build Coastguard Worker 
278*ec779b8eSAndroid Build Coastguard Worker inline double Poly9(double A, double B, double C, double D, double E, double F, double G,
279*ec779b8eSAndroid Build Coastguard Worker         double H, double I, double x) {
280*ec779b8eSAndroid Build Coastguard Worker     double x2 = x * x;
281*ec779b8eSAndroid Build Coastguard Worker #if 1
282*ec779b8eSAndroid Build Coastguard Worker     // It does not seem faster to explicitly decompose Poly8 into Poly4, but
283*ec779b8eSAndroid Build Coastguard Worker     // could depend on compiler floating point scheduling.
284*ec779b8eSAndroid Build Coastguard Worker     double x4 = x2 * x2;
285*ec779b8eSAndroid Build Coastguard Worker     return Poly8(A, B, C, D, E, F, G, H, x, x2, x4) + I * (x4 * x4);
286*ec779b8eSAndroid Build Coastguard Worker #else
287*ec779b8eSAndroid Build Coastguard Worker     double val = Poly4(A, B, C, D, x, x2);
288*ec779b8eSAndroid Build Coastguard Worker     double x4 = x2 * x2;
289*ec779b8eSAndroid Build Coastguard Worker     return val + Poly4(E, F, G, H, x, x2) * x4 + I * (x4 * x4);
290*ec779b8eSAndroid Build Coastguard Worker #endif
291*ec779b8eSAndroid Build Coastguard Worker }
292*ec779b8eSAndroid Build Coastguard Worker #endif
293*ec779b8eSAndroid Build Coastguard Worker 
294*ec779b8eSAndroid Build Coastguard Worker static inline double I0(double x) {
295*ec779b8eSAndroid Build Coastguard Worker     if (x < 3.75) {
296*ec779b8eSAndroid Build Coastguard Worker         x *= x;
297*ec779b8eSAndroid Build Coastguard Worker         return Poly7(I0Term<0>::value, I0Term<1>::value,
298*ec779b8eSAndroid Build Coastguard Worker                 I0Term<2>::value, I0Term<3>::value,
299*ec779b8eSAndroid Build Coastguard Worker                 I0Term<4>::value, I0Term<5>::value,
300*ec779b8eSAndroid Build Coastguard Worker                 I0Term<6>::value, x); // e < 1.6e-7
301*ec779b8eSAndroid Build Coastguard Worker     }
302*ec779b8eSAndroid Build Coastguard Worker     if (1) {
303*ec779b8eSAndroid Build Coastguard Worker         /*
304*ec779b8eSAndroid Build Coastguard Worker          * Series expansion coefs are easy to calculate, but are expanded around 0,
305*ec779b8eSAndroid Build Coastguard Worker          * so error is unequal over the interval 0 < x < 3.75, the error being
306*ec779b8eSAndroid Build Coastguard Worker          * significantly better near 0.
307*ec779b8eSAndroid Build Coastguard Worker          *
308*ec779b8eSAndroid Build Coastguard Worker          * A better solution is to use precise minimax polynomial fits.
309*ec779b8eSAndroid Build Coastguard Worker          *
310*ec779b8eSAndroid Build Coastguard Worker          * We use a slightly more complicated solution for 3.75 < x < 15, based on
311*ec779b8eSAndroid Build Coastguard Worker          * the tables in Blair and Edwards, "Stable Rational Minimax Approximations
312*ec779b8eSAndroid Build Coastguard Worker          * to the Modified Bessel Functions I0(x) and I1(x)", Chalk Hill Nuclear Laboratory,
313*ec779b8eSAndroid Build Coastguard Worker          * AECL-4928.
314*ec779b8eSAndroid Build Coastguard Worker          *
315*ec779b8eSAndroid Build Coastguard Worker          * http://www.iaea.org/inis/collection/NCLCollectionStore/_Public/06/178/6178667.pdf
316*ec779b8eSAndroid Build Coastguard Worker          *
317*ec779b8eSAndroid Build Coastguard Worker          * See Table 11 for 0 < x < 15; e < 10^(-7.13).
318*ec779b8eSAndroid Build Coastguard Worker          *
319*ec779b8eSAndroid Build Coastguard Worker          * Note: Beta cannot exceed 15 (hence Stopband cannot exceed 144dB = 24b).
320*ec779b8eSAndroid Build Coastguard Worker          *
321*ec779b8eSAndroid Build Coastguard Worker          * This speeds up overall computation by about 40% over using the else clause below,
322*ec779b8eSAndroid Build Coastguard Worker          * which requires sqrt and exp.
323*ec779b8eSAndroid Build Coastguard Worker          *
324*ec779b8eSAndroid Build Coastguard Worker          */
325*ec779b8eSAndroid Build Coastguard Worker 
326*ec779b8eSAndroid Build Coastguard Worker         x *= x;
327*ec779b8eSAndroid Build Coastguard Worker         double num = Poly9(-0.13544938430e9, -0.33153754512e8,
328*ec779b8eSAndroid Build Coastguard Worker                 -0.19406631946e7, -0.48058318783e5,
329*ec779b8eSAndroid Build Coastguard Worker                 -0.63269783360e3, -0.49520779070e1,
330*ec779b8eSAndroid Build Coastguard Worker                 -0.24970910370e-1, -0.74741159550e-4,
331*ec779b8eSAndroid Build Coastguard Worker                 -0.18257612460e-6, x);
332*ec779b8eSAndroid Build Coastguard Worker         double y = x - 225.; // reflection around 15 (squared)
333*ec779b8eSAndroid Build Coastguard Worker         double den = Poly4(-0.34598737196e8, 0.23852643181e6,
334*ec779b8eSAndroid Build Coastguard Worker                 -0.70699387620e3, 0.10000000000e1, y);
335*ec779b8eSAndroid Build Coastguard Worker         return num / den;
336*ec779b8eSAndroid Build Coastguard Worker 
337*ec779b8eSAndroid Build Coastguard Worker #if IO_EXTENDED_BETA
338*ec779b8eSAndroid Build Coastguard Worker         /* Table 42 for x > 15; e < 10^(-8.11).
339*ec779b8eSAndroid Build Coastguard Worker          * This is used for Beta>15, but is disabled here as
340*ec779b8eSAndroid Build Coastguard Worker          * we never use Beta that high.
341*ec779b8eSAndroid Build Coastguard Worker          *
342*ec779b8eSAndroid Build Coastguard Worker          * NOTE: This should be enabled only for x > 15.
343*ec779b8eSAndroid Build Coastguard Worker          */
344*ec779b8eSAndroid Build Coastguard Worker 
345*ec779b8eSAndroid Build Coastguard Worker         double y = 1./x;
346*ec779b8eSAndroid Build Coastguard Worker         double z = y - (1./15);
347*ec779b8eSAndroid Build Coastguard Worker         double num = Poly2(0.415079861746e1, -0.5149092496e1, z);
348*ec779b8eSAndroid Build Coastguard Worker         double den = Poly3(0.103150763823e2, -0.14181687413e2,
349*ec779b8eSAndroid Build Coastguard Worker                 0.1000000000e1, z);
350*ec779b8eSAndroid Build Coastguard Worker         return exp(x) * sqrt(y) * num / den;
351*ec779b8eSAndroid Build Coastguard Worker #endif
352*ec779b8eSAndroid Build Coastguard Worker     } else {
353*ec779b8eSAndroid Build Coastguard Worker         /*
354*ec779b8eSAndroid Build Coastguard Worker          * NOT USED, but reference for large Beta.
355*ec779b8eSAndroid Build Coastguard Worker          *
356*ec779b8eSAndroid Build Coastguard Worker          * Abramowitz and Stegun asymptotic formula.
357*ec779b8eSAndroid Build Coastguard Worker          * works for x > 3.75.
358*ec779b8eSAndroid Build Coastguard Worker          */
359*ec779b8eSAndroid Build Coastguard Worker         double y = 1./x;
360*ec779b8eSAndroid Build Coastguard Worker         return exp(x) * sqrt(y) *
361*ec779b8eSAndroid Build Coastguard Worker                 // note: reciprocal squareroot may be easier!
362*ec779b8eSAndroid Build Coastguard Worker                 // http://en.wikipedia.org/wiki/Fast_inverse_square_root
363*ec779b8eSAndroid Build Coastguard Worker                 Poly9(I0ATerm<0>::value, I0ATerm<1>::value,
364*ec779b8eSAndroid Build Coastguard Worker                         I0ATerm<2>::value, I0ATerm<3>::value,
365*ec779b8eSAndroid Build Coastguard Worker                         I0ATerm<4>::value, I0ATerm<5>::value,
366*ec779b8eSAndroid Build Coastguard Worker                         I0ATerm<6>::value, I0ATerm<7>::value,
367*ec779b8eSAndroid Build Coastguard Worker                         I0ATerm<8>::value, y); // (... e) < 1.9e-7
368*ec779b8eSAndroid Build Coastguard Worker     }
369*ec779b8eSAndroid Build Coastguard Worker }
370*ec779b8eSAndroid Build Coastguard Worker 
371*ec779b8eSAndroid Build Coastguard Worker /* A speed optimized version of the Modified Bessel I0() which incorporates
372*ec779b8eSAndroid Build Coastguard Worker  * the sqrt and numerator multiply and denominator divide into the computation.
373*ec779b8eSAndroid Build Coastguard Worker  * This speeds up filter computation by about 10-15%.
374*ec779b8eSAndroid Build Coastguard Worker  */
375*ec779b8eSAndroid Build Coastguard Worker static inline double I0SqrRat(double x2, double num, double den) {
376*ec779b8eSAndroid Build Coastguard Worker     if (x2 < (3.75 * 3.75)) {
377*ec779b8eSAndroid Build Coastguard Worker         return Poly7(I0Term<0>::value, I0Term<1>::value,
378*ec779b8eSAndroid Build Coastguard Worker                 I0Term<2>::value, I0Term<3>::value,
379*ec779b8eSAndroid Build Coastguard Worker                 I0Term<4>::value, I0Term<5>::value,
380*ec779b8eSAndroid Build Coastguard Worker                 I0Term<6>::value, x2) * num / den; // e < 1.6e-7
381*ec779b8eSAndroid Build Coastguard Worker     }
382*ec779b8eSAndroid Build Coastguard Worker     num *= Poly9(-0.13544938430e9, -0.33153754512e8,
383*ec779b8eSAndroid Build Coastguard Worker             -0.19406631946e7, -0.48058318783e5,
384*ec779b8eSAndroid Build Coastguard Worker             -0.63269783360e3, -0.49520779070e1,
385*ec779b8eSAndroid Build Coastguard Worker             -0.24970910370e-1, -0.74741159550e-4,
386*ec779b8eSAndroid Build Coastguard Worker             -0.18257612460e-6, x2); // e < 10^(-7.13).
387*ec779b8eSAndroid Build Coastguard Worker     double y = x2 - 225.; // reflection around 15 (squared)
388*ec779b8eSAndroid Build Coastguard Worker     den *= Poly4(-0.34598737196e8, 0.23852643181e6,
389*ec779b8eSAndroid Build Coastguard Worker             -0.70699387620e3, 0.10000000000e1, y);
390*ec779b8eSAndroid Build Coastguard Worker     return num / den;
391*ec779b8eSAndroid Build Coastguard Worker }
392*ec779b8eSAndroid Build Coastguard Worker 
393*ec779b8eSAndroid Build Coastguard Worker /*
394*ec779b8eSAndroid Build Coastguard Worker  * calculates the transition bandwidth for a Kaiser filter
395*ec779b8eSAndroid Build Coastguard Worker  *
396*ec779b8eSAndroid Build Coastguard Worker  * Formula 3.2.8, Vaidyanathan, _Multirate Systems and Filter Banks_, p. 48
397*ec779b8eSAndroid Build Coastguard Worker  * Formula 7.76, Oppenheim and Schafer, _Discrete-time Signal Processing, 3e_, p. 542
398*ec779b8eSAndroid Build Coastguard Worker  *
399*ec779b8eSAndroid Build Coastguard Worker  * @param halfNumCoef is half the number of coefficients per filter phase.
400*ec779b8eSAndroid Build Coastguard Worker  *
401*ec779b8eSAndroid Build Coastguard Worker  * @param stopBandAtten is the stop band attenuation desired.
402*ec779b8eSAndroid Build Coastguard Worker  *
403*ec779b8eSAndroid Build Coastguard Worker  * @return the transition bandwidth in normalized frequency (0 <= f <= 0.5)
404*ec779b8eSAndroid Build Coastguard Worker  */
405*ec779b8eSAndroid Build Coastguard Worker static inline double firKaiserTbw(int halfNumCoef, double stopBandAtten) {
406*ec779b8eSAndroid Build Coastguard Worker     return (stopBandAtten - 7.95)/((2.*14.36)*halfNumCoef);
407*ec779b8eSAndroid Build Coastguard Worker }
408*ec779b8eSAndroid Build Coastguard Worker 
409*ec779b8eSAndroid Build Coastguard Worker /*
410*ec779b8eSAndroid Build Coastguard Worker  * calculates the fir transfer response of the overall polyphase filter at w.
411*ec779b8eSAndroid Build Coastguard Worker  *
412*ec779b8eSAndroid Build Coastguard Worker  * Calculates the DTFT transfer coefficient H(w) for 0 <= w <= PI, utilizing the
413*ec779b8eSAndroid Build Coastguard Worker  * fact that h[n] is symmetric (cosines only, no complex arithmetic).
414*ec779b8eSAndroid Build Coastguard Worker  *
415*ec779b8eSAndroid Build Coastguard Worker  * We use Goertzel's algorithm to accelerate the computation to essentially
416*ec779b8eSAndroid Build Coastguard Worker  * a single multiply and 2 adds per filter coefficient h[].
417*ec779b8eSAndroid Build Coastguard Worker  *
418*ec779b8eSAndroid Build Coastguard Worker  * Be careful be careful to consider that h[n] is the overall polyphase filter,
419*ec779b8eSAndroid Build Coastguard Worker  * with L phases, so rescaling H(w)/L is probably what you expect for "unity gain",
420*ec779b8eSAndroid Build Coastguard Worker  * as you only use one of the polyphases at a time.
421*ec779b8eSAndroid Build Coastguard Worker  */
422*ec779b8eSAndroid Build Coastguard Worker template <typename T>
423*ec779b8eSAndroid Build Coastguard Worker static inline double firTransfer(const T* coef, int L, int halfNumCoef, double w) {
424*ec779b8eSAndroid Build Coastguard Worker     double accum = static_cast<double>(coef[0])*0.5;  // "center coefficient" from first bank
425*ec779b8eSAndroid Build Coastguard Worker     coef += halfNumCoef;    // skip first filterbank (picked up by the last filterbank).
426*ec779b8eSAndroid Build Coastguard Worker #if SLOW_FIRTRANSFER
427*ec779b8eSAndroid Build Coastguard Worker     /* Original code for reference.  This is equivalent to the code below, but slower. */
428*ec779b8eSAndroid Build Coastguard Worker     for (int i=1 ; i<=L ; ++i) {
429*ec779b8eSAndroid Build Coastguard Worker         for (int j=0, ix=i ; j<halfNumCoef ; ++j, ix+=L) {
430*ec779b8eSAndroid Build Coastguard Worker             accum += cos(ix*w)*static_cast<double>(*coef++);
431*ec779b8eSAndroid Build Coastguard Worker         }
432*ec779b8eSAndroid Build Coastguard Worker     }
433*ec779b8eSAndroid Build Coastguard Worker #else
434*ec779b8eSAndroid Build Coastguard Worker     /*
435*ec779b8eSAndroid Build Coastguard Worker      * Our overall filter is stored striped by polyphases, not a contiguous h[n].
436*ec779b8eSAndroid Build Coastguard Worker      * We could fetch coefficients in a non-contiguous fashion
437*ec779b8eSAndroid Build Coastguard Worker      * but that will not scale to vector processing.
438*ec779b8eSAndroid Build Coastguard Worker      *
439*ec779b8eSAndroid Build Coastguard Worker      * We apply Goertzel's algorithm directly to each polyphase filter bank instead of
440*ec779b8eSAndroid Build Coastguard Worker      * using cosine generation/multiplication, thereby saving one multiply per inner loop.
441*ec779b8eSAndroid Build Coastguard Worker      *
442*ec779b8eSAndroid Build Coastguard Worker      * See: http://en.wikipedia.org/wiki/Goertzel_algorithm
443*ec779b8eSAndroid Build Coastguard Worker      * Also: Oppenheim and Schafer, _Discrete Time Signal Processing, 3e_, p. 720.
444*ec779b8eSAndroid Build Coastguard Worker      *
445*ec779b8eSAndroid Build Coastguard Worker      * We use the basic recursion to incorporate the cosine steps into real sequence x[n]:
446*ec779b8eSAndroid Build Coastguard Worker      * s[n] = x[n] + (2cosw)*s[n-1] + s[n-2]
447*ec779b8eSAndroid Build Coastguard Worker      *
448*ec779b8eSAndroid Build Coastguard Worker      * y[n] = s[n] - e^(iw)s[n-1]
449*ec779b8eSAndroid Build Coastguard Worker      *      = sum_{k=-\infty}^{n} x[k]e^(-iw(n-k))
450*ec779b8eSAndroid Build Coastguard Worker      *      = e^(-iwn) sum_{k=0}^{n} x[k]e^(iwk)
451*ec779b8eSAndroid Build Coastguard Worker      *
452*ec779b8eSAndroid Build Coastguard Worker      * The summation contains the frequency steps we want multiplied by the source
453*ec779b8eSAndroid Build Coastguard Worker      * (similar to a DTFT).
454*ec779b8eSAndroid Build Coastguard Worker      *
455*ec779b8eSAndroid Build Coastguard Worker      * Using symmetry, and just the real part (be careful, this must happen
456*ec779b8eSAndroid Build Coastguard Worker      * after any internal complex multiplications), the polyphase filterbank
457*ec779b8eSAndroid Build Coastguard Worker      * transfer function is:
458*ec779b8eSAndroid Build Coastguard Worker      *
459*ec779b8eSAndroid Build Coastguard Worker      * Hpp[n, w, w_0] = sum_{k=0}^{n} x[k] * cos(wk + w_0)
460*ec779b8eSAndroid Build Coastguard Worker      *                = Re{ e^(iwn + iw_0) y[n]}
461*ec779b8eSAndroid Build Coastguard Worker      *                = cos(wn+w_0) * s[n] - cos(w(n+1)+w_0) * s[n-1]
462*ec779b8eSAndroid Build Coastguard Worker      *
463*ec779b8eSAndroid Build Coastguard Worker      * using the fact that s[n] of real x[n] is real.
464*ec779b8eSAndroid Build Coastguard Worker      *
465*ec779b8eSAndroid Build Coastguard Worker      */
466*ec779b8eSAndroid Build Coastguard Worker     double dcos = 2. * cos(L*w);
467*ec779b8eSAndroid Build Coastguard Worker     int start = ((halfNumCoef)*L + 1);
468*ec779b8eSAndroid Build Coastguard Worker     SineGen cc((start - L) * w, w, true); // cosine
469*ec779b8eSAndroid Build Coastguard Worker     SineGen cp(start * w, w, true); // cosine
470*ec779b8eSAndroid Build Coastguard Worker     for (int i=1 ; i<=L ; ++i) {
471*ec779b8eSAndroid Build Coastguard Worker         double sc = 0;
472*ec779b8eSAndroid Build Coastguard Worker         double sp = 0;
473*ec779b8eSAndroid Build Coastguard Worker         for (int j=0 ; j<halfNumCoef ; ++j) {
474*ec779b8eSAndroid Build Coastguard Worker             double tmp = sc;
475*ec779b8eSAndroid Build Coastguard Worker             sc  = static_cast<double>(*coef++) + dcos*sc - sp;
476*ec779b8eSAndroid Build Coastguard Worker             sp = tmp;
477*ec779b8eSAndroid Build Coastguard Worker         }
478*ec779b8eSAndroid Build Coastguard Worker         // If we are awfully clever, we can apply Goertzel's algorithm
479*ec779b8eSAndroid Build Coastguard Worker         // again on the sc and sp sequences returned here.
480*ec779b8eSAndroid Build Coastguard Worker         accum += cc.valueAdvance() * sc - cp.valueAdvance() * sp;
481*ec779b8eSAndroid Build Coastguard Worker     }
482*ec779b8eSAndroid Build Coastguard Worker #endif
483*ec779b8eSAndroid Build Coastguard Worker     return accum*2.;
484*ec779b8eSAndroid Build Coastguard Worker }
485*ec779b8eSAndroid Build Coastguard Worker 
486*ec779b8eSAndroid Build Coastguard Worker /*
487*ec779b8eSAndroid Build Coastguard Worker  * evaluates the minimum and maximum |H(f)| bound in a band region.
488*ec779b8eSAndroid Build Coastguard Worker  *
489*ec779b8eSAndroid Build Coastguard Worker  * This is usually done with equally spaced increments in the target band in question.
490*ec779b8eSAndroid Build Coastguard Worker  * The passband is often very small, and sampled that way. The stopband is often much
491*ec779b8eSAndroid Build Coastguard Worker  * larger.
492*ec779b8eSAndroid Build Coastguard Worker  *
493*ec779b8eSAndroid Build Coastguard Worker  * We use the fact that the overall polyphase filter has an additional bank at the end
494*ec779b8eSAndroid Build Coastguard Worker  * for interpolation; hence it is overspecified for the H(f) computation.  Thus the
495*ec779b8eSAndroid Build Coastguard Worker  * first polyphase is never actually checked, excepting its first term.
496*ec779b8eSAndroid Build Coastguard Worker  *
497*ec779b8eSAndroid Build Coastguard Worker  * In this code we use the firTransfer() evaluator above, which uses Goertzel's
498*ec779b8eSAndroid Build Coastguard Worker  * algorithm to calculate the transfer function at each point.
499*ec779b8eSAndroid Build Coastguard Worker  *
500*ec779b8eSAndroid Build Coastguard Worker  * TODO: An alternative with equal spacing is the FFT/DFT.  An alternative with unequal
501*ec779b8eSAndroid Build Coastguard Worker  * spacing is a chirp transform.
502*ec779b8eSAndroid Build Coastguard Worker  *
503*ec779b8eSAndroid Build Coastguard Worker  * @param coef is the designed polyphase filter banks
504*ec779b8eSAndroid Build Coastguard Worker  *
505*ec779b8eSAndroid Build Coastguard Worker  * @param L is the number of phases (for interpolation)
506*ec779b8eSAndroid Build Coastguard Worker  *
507*ec779b8eSAndroid Build Coastguard Worker  * @param halfNumCoef should be half the number of coefficients for a single
508*ec779b8eSAndroid Build Coastguard Worker  * polyphase.
509*ec779b8eSAndroid Build Coastguard Worker  *
510*ec779b8eSAndroid Build Coastguard Worker  * @param fstart is the normalized frequency start.
511*ec779b8eSAndroid Build Coastguard Worker  *
512*ec779b8eSAndroid Build Coastguard Worker  * @param fend is the normalized frequency end.
513*ec779b8eSAndroid Build Coastguard Worker  *
514*ec779b8eSAndroid Build Coastguard Worker  * @param steps is the number of steps to take (sampling) between frequency start and end
515*ec779b8eSAndroid Build Coastguard Worker  *
516*ec779b8eSAndroid Build Coastguard Worker  * @param firMin returns the minimum transfer |H(f)| found
517*ec779b8eSAndroid Build Coastguard Worker  *
518*ec779b8eSAndroid Build Coastguard Worker  * @param firMax returns the maximum transfer |H(f)| found
519*ec779b8eSAndroid Build Coastguard Worker  *
520*ec779b8eSAndroid Build Coastguard Worker  * 0 <= f <= 0.5.
521*ec779b8eSAndroid Build Coastguard Worker  * This is used to test passband and stopband performance.
522*ec779b8eSAndroid Build Coastguard Worker  */
523*ec779b8eSAndroid Build Coastguard Worker template <typename T>
524*ec779b8eSAndroid Build Coastguard Worker static void testFir(const T* coef, int L, int halfNumCoef,
525*ec779b8eSAndroid Build Coastguard Worker         double fstart, double fend, int steps, double &firMin, double &firMax) {
526*ec779b8eSAndroid Build Coastguard Worker     double wstart = fstart*(2.*M_PI);
527*ec779b8eSAndroid Build Coastguard Worker     double wend = fend*(2.*M_PI);
528*ec779b8eSAndroid Build Coastguard Worker     double wstep = (wend - wstart)/steps;
529*ec779b8eSAndroid Build Coastguard Worker     double fmax, fmin;
530*ec779b8eSAndroid Build Coastguard Worker     double trf = firTransfer(coef, L, halfNumCoef, wstart);
531*ec779b8eSAndroid Build Coastguard Worker     if (trf<0) {
532*ec779b8eSAndroid Build Coastguard Worker         trf = -trf;
533*ec779b8eSAndroid Build Coastguard Worker     }
534*ec779b8eSAndroid Build Coastguard Worker     fmin = fmax = trf;
535*ec779b8eSAndroid Build Coastguard Worker     wstart += wstep;
536*ec779b8eSAndroid Build Coastguard Worker     for (int i=1; i<steps; ++i) {
537*ec779b8eSAndroid Build Coastguard Worker         trf = firTransfer(coef, L, halfNumCoef, wstart);
538*ec779b8eSAndroid Build Coastguard Worker         if (trf<0) {
539*ec779b8eSAndroid Build Coastguard Worker             trf = -trf;
540*ec779b8eSAndroid Build Coastguard Worker         }
541*ec779b8eSAndroid Build Coastguard Worker         if (trf>fmax) {
542*ec779b8eSAndroid Build Coastguard Worker             fmax = trf;
543*ec779b8eSAndroid Build Coastguard Worker         }
544*ec779b8eSAndroid Build Coastguard Worker         else if (trf<fmin) {
545*ec779b8eSAndroid Build Coastguard Worker             fmin = trf;
546*ec779b8eSAndroid Build Coastguard Worker         }
547*ec779b8eSAndroid Build Coastguard Worker         wstart += wstep;
548*ec779b8eSAndroid Build Coastguard Worker     }
549*ec779b8eSAndroid Build Coastguard Worker     // renormalize - this is needed for integer filter types, use 1 for float or double.
550*ec779b8eSAndroid Build Coastguard Worker     constexpr int integralShift = std::is_integral<T>::value ? (sizeof(T) * CHAR_BIT - 1) : 0;
551*ec779b8eSAndroid Build Coastguard Worker     const double norm = 1. / (int64_t{L} << integralShift);
552*ec779b8eSAndroid Build Coastguard Worker 
553*ec779b8eSAndroid Build Coastguard Worker     firMin = fmin * norm;
554*ec779b8eSAndroid Build Coastguard Worker     firMax = fmax * norm;
555*ec779b8eSAndroid Build Coastguard Worker }
556*ec779b8eSAndroid Build Coastguard Worker 
557*ec779b8eSAndroid Build Coastguard Worker /*
558*ec779b8eSAndroid Build Coastguard Worker  * evaluates the |H(f)| lowpass band characteristics.
559*ec779b8eSAndroid Build Coastguard Worker  *
560*ec779b8eSAndroid Build Coastguard Worker  * This function tests the lowpass characteristics for the overall polyphase filter,
561*ec779b8eSAndroid Build Coastguard Worker  * and is used to verify the design.
562*ec779b8eSAndroid Build Coastguard Worker  *
563*ec779b8eSAndroid Build Coastguard Worker  * For a polyphase filter (L > 1), typically fp should be set to the
564*ec779b8eSAndroid Build Coastguard Worker  * passband normalized frequency from 0 to 0.5 for the overall filter (thus it
565*ec779b8eSAndroid Build Coastguard Worker  * is the designed polyphase bank value / L).  Likewise for fs.
566*ec779b8eSAndroid Build Coastguard Worker  * Similarly the stopSteps should be L * passSteps for equivalent accuracy.
567*ec779b8eSAndroid Build Coastguard Worker  *
568*ec779b8eSAndroid Build Coastguard Worker  * @param coef is the designed polyphase filter banks
569*ec779b8eSAndroid Build Coastguard Worker  *
570*ec779b8eSAndroid Build Coastguard Worker  * @param L is the number of phases (for interpolation)
571*ec779b8eSAndroid Build Coastguard Worker  *
572*ec779b8eSAndroid Build Coastguard Worker  * @param halfNumCoef should be half the number of coefficients for a single
573*ec779b8eSAndroid Build Coastguard Worker  * polyphase.
574*ec779b8eSAndroid Build Coastguard Worker  *
575*ec779b8eSAndroid Build Coastguard Worker  * @param fp is the passband normalized frequency, 0 < fp < fs < 0.5.
576*ec779b8eSAndroid Build Coastguard Worker  *
577*ec779b8eSAndroid Build Coastguard Worker  * @param fs is the stopband normalized frequency, 0 < fp < fs < 0.5.
578*ec779b8eSAndroid Build Coastguard Worker  *
579*ec779b8eSAndroid Build Coastguard Worker  * @param passSteps is the number of passband sampling steps.
580*ec779b8eSAndroid Build Coastguard Worker  *
581*ec779b8eSAndroid Build Coastguard Worker  * @param stopSteps is the number of stopband sampling steps.
582*ec779b8eSAndroid Build Coastguard Worker  *
583*ec779b8eSAndroid Build Coastguard Worker  * @param passMin is the minimum value in the passband
584*ec779b8eSAndroid Build Coastguard Worker  *
585*ec779b8eSAndroid Build Coastguard Worker  * @param passMax is the maximum value in the passband (useful for scaling).  This should
586*ec779b8eSAndroid Build Coastguard Worker  * be less than 1., to avoid sine wave test overflow.
587*ec779b8eSAndroid Build Coastguard Worker  *
588*ec779b8eSAndroid Build Coastguard Worker  * @param passRipple is the passband ripple.  Typically this should be less than 0.1 for
589*ec779b8eSAndroid Build Coastguard Worker  * an audio filter.  Generally speaker/headphone device characteristics will dominate
590*ec779b8eSAndroid Build Coastguard Worker  * the passband term.
591*ec779b8eSAndroid Build Coastguard Worker  *
592*ec779b8eSAndroid Build Coastguard Worker  * @param stopMax is the maximum value in the stopband.
593*ec779b8eSAndroid Build Coastguard Worker  *
594*ec779b8eSAndroid Build Coastguard Worker  * @param stopRipple is the stopband ripple, also known as stopband attenuation.
595*ec779b8eSAndroid Build Coastguard Worker  * Typically this should be greater than ~80dB for low quality, and greater than
596*ec779b8eSAndroid Build Coastguard Worker  * ~100dB for full 16b quality, otherwise aliasing may become noticeable.
597*ec779b8eSAndroid Build Coastguard Worker  *
598*ec779b8eSAndroid Build Coastguard Worker  */
599*ec779b8eSAndroid Build Coastguard Worker template <typename T>
600*ec779b8eSAndroid Build Coastguard Worker static void testFir(const T* coef, int L, int halfNumCoef,
601*ec779b8eSAndroid Build Coastguard Worker         double fp, double fs, int passSteps, int stopSteps,
602*ec779b8eSAndroid Build Coastguard Worker         double &passMin, double &passMax, double &passRipple,
603*ec779b8eSAndroid Build Coastguard Worker         double &stopMax, double &stopRipple) {
604*ec779b8eSAndroid Build Coastguard Worker     double fmin, fmax;
605*ec779b8eSAndroid Build Coastguard Worker     testFir(coef, L, halfNumCoef, 0., fp, passSteps, fmin, fmax);
606*ec779b8eSAndroid Build Coastguard Worker     double d1 = (fmax - fmin)/2.;
607*ec779b8eSAndroid Build Coastguard Worker     passMin = fmin;
608*ec779b8eSAndroid Build Coastguard Worker     passMax = fmax;
609*ec779b8eSAndroid Build Coastguard Worker     passRipple = -20.*log10(1. - d1); // passband ripple
610*ec779b8eSAndroid Build Coastguard Worker     testFir(coef, L, halfNumCoef, fs, 0.5, stopSteps, fmin, fmax);
611*ec779b8eSAndroid Build Coastguard Worker     // fmin is really not important for the stopband.
612*ec779b8eSAndroid Build Coastguard Worker     stopMax = fmax;
613*ec779b8eSAndroid Build Coastguard Worker     stopRipple = -20.*log10(fmax); // stopband ripple/attenuation
614*ec779b8eSAndroid Build Coastguard Worker }
615*ec779b8eSAndroid Build Coastguard Worker 
616*ec779b8eSAndroid Build Coastguard Worker /*
617*ec779b8eSAndroid Build Coastguard Worker  * Estimate the windowed sinc minimum passband value.
618*ec779b8eSAndroid Build Coastguard Worker  *
619*ec779b8eSAndroid Build Coastguard Worker  * This is the minimum value for a windowed sinc filter in its passband,
620*ec779b8eSAndroid Build Coastguard Worker  * which is identical to the scaling required not to cause overflow of a 0dBFS signal.
621*ec779b8eSAndroid Build Coastguard Worker  * The actual value used to attenuate the filter amplitude should be slightly
622*ec779b8eSAndroid Build Coastguard Worker  * smaller than this (suggest squaring) as this is just an estimate.
623*ec779b8eSAndroid Build Coastguard Worker  *
624*ec779b8eSAndroid Build Coastguard Worker  * As a windowed sinc has a passband ripple commensurate to the stopband attenuation
625*ec779b8eSAndroid Build Coastguard Worker  * due to Gibb's phenomenon from truncating the sinc, we derive this value from
626*ec779b8eSAndroid Build Coastguard Worker  * the design stopbandAttenuationDb (a positive value).
627*ec779b8eSAndroid Build Coastguard Worker  */
628*ec779b8eSAndroid Build Coastguard Worker static inline double computeWindowedSincMinimumPassbandValue(
629*ec779b8eSAndroid Build Coastguard Worker         double stopBandAttenuationDb) {
630*ec779b8eSAndroid Build Coastguard Worker     return 1. - pow(10. /* base */, stopBandAttenuationDb * (-1. / 20.));
631*ec779b8eSAndroid Build Coastguard Worker }
632*ec779b8eSAndroid Build Coastguard Worker 
633*ec779b8eSAndroid Build Coastguard Worker /*
634*ec779b8eSAndroid Build Coastguard Worker  * Compute the windowed sinc passband ripple from stopband attenuation.
635*ec779b8eSAndroid Build Coastguard Worker  *
636*ec779b8eSAndroid Build Coastguard Worker  * As a windowed sinc has an passband ripple commensurate to the stopband attenuation
637*ec779b8eSAndroid Build Coastguard Worker  * due to Gibb's phenomenon from truncating the sinc, we derive this value from
638*ec779b8eSAndroid Build Coastguard Worker  * the design stopbandAttenuationDb (a positive value).
639*ec779b8eSAndroid Build Coastguard Worker  */
640*ec779b8eSAndroid Build Coastguard Worker static inline double computeWindowedSincPassbandRippleDb(
641*ec779b8eSAndroid Build Coastguard Worker         double stopBandAttenuationDb) {
642*ec779b8eSAndroid Build Coastguard Worker     return -20. * log10(computeWindowedSincMinimumPassbandValue(stopBandAttenuationDb));
643*ec779b8eSAndroid Build Coastguard Worker }
644*ec779b8eSAndroid Build Coastguard Worker 
645*ec779b8eSAndroid Build Coastguard Worker /*
646*ec779b8eSAndroid Build Coastguard Worker  * Kaiser window Beta value
647*ec779b8eSAndroid Build Coastguard Worker  *
648*ec779b8eSAndroid Build Coastguard Worker  * Formula 3.2.5, 3.2.7, Vaidyanathan, _Multirate Systems and Filter Banks_, p. 48
649*ec779b8eSAndroid Build Coastguard Worker  * Formula 7.75, Oppenheim and Schafer, _Discrete-time Signal Processing, 3e_, p. 542
650*ec779b8eSAndroid Build Coastguard Worker  *
651*ec779b8eSAndroid Build Coastguard Worker  * See also: http://melodi.ee.washington.edu/courses/ee518/notes/lec17.pdf
652*ec779b8eSAndroid Build Coastguard Worker  *
653*ec779b8eSAndroid Build Coastguard Worker  * Kaiser window and beta parameter
654*ec779b8eSAndroid Build Coastguard Worker  *
655*ec779b8eSAndroid Build Coastguard Worker  *         | 0.1102*(A - 8.7)                         A > 50
656*ec779b8eSAndroid Build Coastguard Worker  *  Beta = | 0.5842*(A - 21)^0.4 + 0.07886*(A - 21)   21 < A <= 50
657*ec779b8eSAndroid Build Coastguard Worker  *         | 0.                                       A <= 21
658*ec779b8eSAndroid Build Coastguard Worker  *
659*ec779b8eSAndroid Build Coastguard Worker  * with A is the desired stop-band attenuation in positive dBFS
660*ec779b8eSAndroid Build Coastguard Worker  *
661*ec779b8eSAndroid Build Coastguard Worker  *    30 dB    2.210
662*ec779b8eSAndroid Build Coastguard Worker  *    40 dB    3.384
663*ec779b8eSAndroid Build Coastguard Worker  *    50 dB    4.538
664*ec779b8eSAndroid Build Coastguard Worker  *    60 dB    5.658
665*ec779b8eSAndroid Build Coastguard Worker  *    70 dB    6.764
666*ec779b8eSAndroid Build Coastguard Worker  *    80 dB    7.865
667*ec779b8eSAndroid Build Coastguard Worker  *    90 dB    8.960
668*ec779b8eSAndroid Build Coastguard Worker  *   100 dB   10.056
669*ec779b8eSAndroid Build Coastguard Worker  *
670*ec779b8eSAndroid Build Coastguard Worker  * For some values of stopBandAttenuationDb the function may be computed
671*ec779b8eSAndroid Build Coastguard Worker  * at compile time.
672*ec779b8eSAndroid Build Coastguard Worker  */
673*ec779b8eSAndroid Build Coastguard Worker static inline constexpr double computeBeta(double stopBandAttenuationDb) {
674*ec779b8eSAndroid Build Coastguard Worker     if (stopBandAttenuationDb > 50.) {
675*ec779b8eSAndroid Build Coastguard Worker         return 0.1102 * (stopBandAttenuationDb - 8.7);
676*ec779b8eSAndroid Build Coastguard Worker     }
677*ec779b8eSAndroid Build Coastguard Worker     const double offset = stopBandAttenuationDb - 21.;
678*ec779b8eSAndroid Build Coastguard Worker     if (offset > 0.) {
679*ec779b8eSAndroid Build Coastguard Worker         return 0.5842 * pow(offset, 0.4) + 0.07886 * offset;
680*ec779b8eSAndroid Build Coastguard Worker     }
681*ec779b8eSAndroid Build Coastguard Worker     return 0.;
682*ec779b8eSAndroid Build Coastguard Worker }
683*ec779b8eSAndroid Build Coastguard Worker 
684*ec779b8eSAndroid Build Coastguard Worker /*
685*ec779b8eSAndroid Build Coastguard Worker  * Calculates the overall polyphase filter based on a windowed sinc function.
686*ec779b8eSAndroid Build Coastguard Worker  *
687*ec779b8eSAndroid Build Coastguard Worker  * The windowed sinc is an odd length symmetric filter of exactly L*halfNumCoef*2+1
688*ec779b8eSAndroid Build Coastguard Worker  * taps for the entire kernel.  This is then decomposed into L+1 polyphase filterbanks.
689*ec779b8eSAndroid Build Coastguard Worker  * The last filterbank is used for interpolation purposes (and is mostly composed
690*ec779b8eSAndroid Build Coastguard Worker  * of the first bank shifted by one sample), and is unnecessary if one does
691*ec779b8eSAndroid Build Coastguard Worker  * not do interpolation.
692*ec779b8eSAndroid Build Coastguard Worker  *
693*ec779b8eSAndroid Build Coastguard Worker  * We use the last filterbank for some transfer function calculation purposes,
694*ec779b8eSAndroid Build Coastguard Worker  * so it needs to be generated anyways.
695*ec779b8eSAndroid Build Coastguard Worker  *
696*ec779b8eSAndroid Build Coastguard Worker  * @param coef is the caller allocated space for coefficients.  This should be
697*ec779b8eSAndroid Build Coastguard Worker  * exactly (L+1)*halfNumCoef in size.
698*ec779b8eSAndroid Build Coastguard Worker  *
699*ec779b8eSAndroid Build Coastguard Worker  * @param L is the number of phases (for interpolation)
700*ec779b8eSAndroid Build Coastguard Worker  *
701*ec779b8eSAndroid Build Coastguard Worker  * @param halfNumCoef should be half the number of coefficients for a single
702*ec779b8eSAndroid Build Coastguard Worker  * polyphase.
703*ec779b8eSAndroid Build Coastguard Worker  *
704*ec779b8eSAndroid Build Coastguard Worker  * @param stopBandAtten is the stopband value, should be >50dB.
705*ec779b8eSAndroid Build Coastguard Worker  *
706*ec779b8eSAndroid Build Coastguard Worker  * @param fcr is cutoff frequency/sampling rate (<0.5).  At this point, the energy
707*ec779b8eSAndroid Build Coastguard Worker  * should be 6dB less. (fcr is where the amplitude drops by half).  Use the
708*ec779b8eSAndroid Build Coastguard Worker  * firKaiserTbw() to calculate the transition bandwidth.  fcr is the midpoint
709*ec779b8eSAndroid Build Coastguard Worker  * between the stop band and the pass band (fstop+fpass)/2.
710*ec779b8eSAndroid Build Coastguard Worker  *
711*ec779b8eSAndroid Build Coastguard Worker  * @param atten is the attenuation (generally slightly less than 1).
712*ec779b8eSAndroid Build Coastguard Worker  */
713*ec779b8eSAndroid Build Coastguard Worker 
714*ec779b8eSAndroid Build Coastguard Worker template <typename T>
715*ec779b8eSAndroid Build Coastguard Worker static inline void firKaiserGen(T* coef, int L, int halfNumCoef,
716*ec779b8eSAndroid Build Coastguard Worker         double stopBandAtten, double fcr, double atten) {
717*ec779b8eSAndroid Build Coastguard Worker     const int N = L * halfNumCoef; // non-negative half
718*ec779b8eSAndroid Build Coastguard Worker     const double beta = computeBeta(stopBandAtten);
719*ec779b8eSAndroid Build Coastguard Worker     const double xstep = (2. * M_PI) * fcr / L;
720*ec779b8eSAndroid Build Coastguard Worker     const double xfrac = 1. / N;
721*ec779b8eSAndroid Build Coastguard Worker     const double yscale = atten * L / (I0(beta) * M_PI);
722*ec779b8eSAndroid Build Coastguard Worker     const double sqrbeta = sqr(beta);
723*ec779b8eSAndroid Build Coastguard Worker 
724*ec779b8eSAndroid Build Coastguard Worker     // We use sine generators, which computes sines on regular step intervals.
725*ec779b8eSAndroid Build Coastguard Worker     // This speeds up overall computation about 40% from computing the sine directly.
726*ec779b8eSAndroid Build Coastguard Worker 
727*ec779b8eSAndroid Build Coastguard Worker     SineGenGen sgg(0., xstep, L*xstep); // generates sine generators (one per polyphase)
728*ec779b8eSAndroid Build Coastguard Worker 
729*ec779b8eSAndroid Build Coastguard Worker     for (int i=0 ; i<=L ; ++i) { // generate an extra set of coefs for interpolation
730*ec779b8eSAndroid Build Coastguard Worker 
731*ec779b8eSAndroid Build Coastguard Worker         // computation for a single polyphase of the overall filter.
732*ec779b8eSAndroid Build Coastguard Worker         SineGen sg = sgg.valueAdvance(); // current sine generator for "j" inner loop.
733*ec779b8eSAndroid Build Coastguard Worker         double err = 0; // for noise shaping on int16_t coefficients (over each polyphase)
734*ec779b8eSAndroid Build Coastguard Worker 
735*ec779b8eSAndroid Build Coastguard Worker         for (int j=0, ix=i ; j<halfNumCoef ; ++j, ix+=L) {
736*ec779b8eSAndroid Build Coastguard Worker             double y;
737*ec779b8eSAndroid Build Coastguard Worker             if (CC_LIKELY(ix)) {
738*ec779b8eSAndroid Build Coastguard Worker                 double x = static_cast<double>(ix);
739*ec779b8eSAndroid Build Coastguard Worker 
740*ec779b8eSAndroid Build Coastguard Worker                 // sine generator: sg.valueAdvance() returns sin(ix*xstep);
741*ec779b8eSAndroid Build Coastguard Worker                 // y = I0(beta * sqrt(1.0 - sqr(x * xfrac))) * yscale * sg.valueAdvance() / x;
742*ec779b8eSAndroid Build Coastguard Worker                 y = I0SqrRat(sqrbeta * (1.0 - sqr(x * xfrac)), yscale * sg.valueAdvance(), x);
743*ec779b8eSAndroid Build Coastguard Worker             } else {
744*ec779b8eSAndroid Build Coastguard Worker                 y = 2. * atten * fcr; // center of filter, sinc(0) = 1.
745*ec779b8eSAndroid Build Coastguard Worker                 sg.advance();
746*ec779b8eSAndroid Build Coastguard Worker             }
747*ec779b8eSAndroid Build Coastguard Worker 
748*ec779b8eSAndroid Build Coastguard Worker             if (std::is_same<T, int16_t>::value) { // int16_t needs noise shaping
749*ec779b8eSAndroid Build Coastguard Worker                 *coef++ = static_cast<T>(toint(y, 1ULL<<(sizeof(T)*8-1), err));
750*ec779b8eSAndroid Build Coastguard Worker             } else if (std::is_same<T, int32_t>::value) {
751*ec779b8eSAndroid Build Coastguard Worker                 *coef++ = static_cast<T>(toint(y, 1ULL<<(sizeof(T)*8-1)));
752*ec779b8eSAndroid Build Coastguard Worker             } else { // assumed float or double
753*ec779b8eSAndroid Build Coastguard Worker                 *coef++ = static_cast<T>(y);
754*ec779b8eSAndroid Build Coastguard Worker             }
755*ec779b8eSAndroid Build Coastguard Worker         }
756*ec779b8eSAndroid Build Coastguard Worker     }
757*ec779b8eSAndroid Build Coastguard Worker }
758*ec779b8eSAndroid Build Coastguard Worker 
759*ec779b8eSAndroid Build Coastguard Worker } // namespace android
760*ec779b8eSAndroid Build Coastguard Worker 
761*ec779b8eSAndroid Build Coastguard Worker #endif /*ANDROID_AUDIO_RESAMPLER_FIR_GEN_H*/
762