| /* | 
 |  * Copyright (C) 2007 The Android Open Source Project | 
 |  * | 
 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 |  * you may not use this file except in compliance with the License. | 
 |  * You may obtain a copy of the License at | 
 |  * | 
 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 |  * | 
 |  * Unless required by applicable law or agreed to in writing, software | 
 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 |  * See the License for the specific language governing permissions and | 
 |  * limitations under the License. | 
 |  */ | 
 |  | 
 | #include <math.h> | 
 | #include <stdio.h> | 
 | #include <unistd.h> | 
 | #include <stdlib.h> | 
 | #include <string.h> | 
 |  | 
 | static inline double sinc(double x) { | 
 |     if (fabs(x) == 0.0f) return 1.0f; | 
 |     return sin(x) / x; | 
 | } | 
 |  | 
 | static inline double sqr(double x) { | 
 |     return x*x; | 
 | } | 
 |  | 
 | static inline int64_t toint(double x, int64_t maxval) { | 
 |     int64_t v; | 
 |  | 
 |     v = static_cast<int64_t>(floor(x * maxval + 0.5)); | 
 |     if (v >= maxval) { | 
 |         return maxval - 1; // error! | 
 |     } | 
 |     return v; | 
 | } | 
 |  | 
 | static double I0(double x) { | 
 |     // from the Numerical Recipes in C p. 237 | 
 |     double ax,ans,y; | 
 |     ax=fabs(x); | 
 |     if (ax < 3.75) { | 
 |         y=x/3.75; | 
 |         y*=y; | 
 |         ans=1.0+y*(3.5156229+y*(3.0899424+y*(1.2067492 | 
 |                 +y*(0.2659732+y*(0.360768e-1+y*0.45813e-2))))); | 
 |     } else { | 
 |         y=3.75/ax; | 
 |         ans=(exp(ax)/sqrt(ax))*(0.39894228+y*(0.1328592e-1 | 
 |                 +y*(0.225319e-2+y*(-0.157565e-2+y*(0.916281e-2 | 
 |                         +y*(-0.2057706e-1+y*(0.2635537e-1+y*(-0.1647633e-1 | 
 |                                 +y*0.392377e-2)))))))); | 
 |     } | 
 |     return ans; | 
 | } | 
 |  | 
 | static double kaiser(int k, int N, double beta) { | 
 |     if (k < 0 || k > N) | 
 |         return 0; | 
 |     return I0(beta * sqrt(1.0 - sqr((2.0*k)/N - 1.0))) / I0(beta); | 
 | } | 
 |  | 
 | static void usage(char* name) { | 
 |     fprintf(stderr, | 
 |             "usage: %s [-h] [-d] [-D] [-s sample_rate] [-c cut-off_frequency] [-n half_zero_crossings]" | 
 |             " [-f {float|fixed|fixed16}] [-b beta] [-v dBFS] [-l lerp]\n" | 
 |             "       %s [-h] [-d] [-D] [-s sample_rate] [-c cut-off_frequency] [-n half_zero_crossings]" | 
 |             " [-f {float|fixed|fixed16}] [-b beta] [-v dBFS] -p M/N\n" | 
 |             "    -h    this help message\n" | 
 |             "    -d    debug, print comma-separated coefficient table\n" | 
 |             "    -D    generate extra declarations\n" | 
 |             "    -p    generate poly-phase filter coefficients, with sample increment M/N\n" | 
 |             "    -s    sample rate (48000)\n" | 
 |             "    -c    cut-off frequency (20478)\n" | 
 |             "    -n    number of zero-crossings on one side (8)\n" | 
 |             "    -l    number of lerping bits (4)\n" | 
 |             "    -m    number of polyphases (related to -l, default 16)\n" | 
 |             "    -f    output format, can be fixed, fixed16, or float (fixed)\n" | 
 |             "    -b    kaiser window parameter beta (7.865 [-80dB])\n" | 
 |             "    -v    attenuation in dBFS (0)\n", | 
 |             name, name | 
 |     ); | 
 |     exit(0); | 
 | } | 
 |  | 
 | int main(int argc, char** argv) | 
 | { | 
 |     // nc is the number of bits to store the coefficients | 
 |     int nc = 32; | 
 |     bool polyphase = false; | 
 |     unsigned int polyM = 160; | 
 |     unsigned int polyN = 147; | 
 |     bool debug = false; | 
 |     double Fs = 48000; | 
 |     double Fc = 20478; | 
 |     double atten = 1; | 
 |     int format = 0;     // 0=fixed, 1=float | 
 |     bool declarations = false; | 
 |  | 
 |     // in order to keep the errors associated with the linear | 
 |     // interpolation of the coefficients below the quantization error | 
 |     // we must satisfy: | 
 |     //   2^nz >= 2^(nc/2) | 
 |     // | 
 |     // for 16 bit coefficients that would be 256 | 
 |     // | 
 |     // note that increasing nz only increases memory requirements, | 
 |     // but doesn't increase the amount of computation to do. | 
 |     // | 
 |     // | 
 |     // see: | 
 |     // Smith, J.O. Digital Audio Resampling Home Page | 
 |     // https://ccrma.stanford.edu/~jos/resample/, 2011-03-29 | 
 |     // | 
 |  | 
 |     //         | 0.1102*(A - 8.7)                         A > 50 | 
 |     //  beta = | 0.5842*(A - 21)^0.4 + 0.07886*(A - 21)   21 <= A <= 50 | 
 |     //         | 0                                        A < 21 | 
 |     //   with A is the desired stop-band attenuation in dBFS | 
 |     // | 
 |     // for eg: | 
 |     // | 
 |     //    30 dB    2.210 | 
 |     //    40 dB    3.384 | 
 |     //    50 dB    4.538 | 
 |     //    60 dB    5.658 | 
 |     //    70 dB    6.764 | 
 |     //    80 dB    7.865 | 
 |     //    90 dB    8.960 | 
 |     //   100 dB   10.056 | 
 |     double beta = 7.865; | 
 |  | 
 |     // 2*nzc = (A - 8) / (2.285 * dw) | 
 |     //      with dw the transition width = 2*pi*dF/Fs | 
 |     // | 
 |     int nzc = 8; | 
 |  | 
 |     /* | 
 |      * Example: | 
 |      * 44.1 KHz to 48 KHz resampling | 
 |      * 100 dB rejection above 28 KHz | 
 |      *   (the spectrum will fold around 24 KHz and we want 100 dB rejection | 
 |      *    at the point where the folding reaches 20 KHz) | 
 |      *  ...___|_____ | 
 |      *        |     \| | 
 |      *        | ____/|\____ | 
 |      *        |/alias|     \ | 
 |      *  ------/------+------\---------> KHz | 
 |      *       20     24     28 | 
 |      * | 
 |      * Transition band 8 KHz, or dw = 1.0472 | 
 |      * | 
 |      * beta = 10.056 | 
 |      * nzc  = 20 | 
 |      */ | 
 |  | 
 |     int M = 1 << 4; // number of phases for interpolation | 
 |     int ch; | 
 |     while ((ch = getopt(argc, argv, ":hds:c:n:f:l:m:b:p:v:z:D")) != -1) { | 
 |         switch (ch) { | 
 |             case 'd': | 
 |                 debug = true; | 
 |                 break; | 
 |             case 'D': | 
 |                 declarations = true; | 
 |                 break; | 
 |             case 'p': | 
 |                 if (sscanf(optarg, "%u/%u", &polyM, &polyN) != 2) { | 
 |                     usage(argv[0]); | 
 |                 } | 
 |                 polyphase = true; | 
 |                 break; | 
 |             case 's': | 
 |                 Fs = atof(optarg); | 
 |                 break; | 
 |             case 'c': | 
 |                 Fc = atof(optarg); | 
 |                 break; | 
 |             case 'n': | 
 |                 nzc = atoi(optarg); | 
 |                 break; | 
 |             case 'm': | 
 |                 M = atoi(optarg); | 
 |                 break; | 
 |             case 'l': | 
 |                 M = 1 << atoi(optarg); | 
 |                 break; | 
 |             case 'f': | 
 |                 if (!strcmp(optarg, "fixed")) { | 
 |                     format = 0; | 
 |                 } | 
 |                 else if (!strcmp(optarg, "fixed16")) { | 
 |                     format = 0; | 
 |                     nc = 16; | 
 |                 } | 
 |                 else if (!strcmp(optarg, "float")) { | 
 |                     format = 1; | 
 |                 } | 
 |                 else { | 
 |                     usage(argv[0]); | 
 |                 } | 
 |                 break; | 
 |             case 'b': | 
 |                 beta = atof(optarg); | 
 |                 break; | 
 |             case 'v': | 
 |                 atten = pow(10, -fabs(atof(optarg))*0.05 ); | 
 |                 break; | 
 |             case 'h': | 
 |             default: | 
 |                 usage(argv[0]); | 
 |                 break; | 
 |         } | 
 |     } | 
 |  | 
 |     // cut off frequency ratio Fc/Fs | 
 |     double Fcr = Fc / Fs; | 
 |  | 
 |     // total number of coefficients (one side) | 
 |  | 
 |     const int N = M * nzc; | 
 |  | 
 |     // lerp (which is most useful if M is a power of 2) | 
 |  | 
 |     int nz = 0; // recalculate nz as the bits needed to represent M | 
 |     for (int i = M-1 ; i; i>>=1, nz++); | 
 |     // generate the right half of the filter | 
 |     if (!debug) { | 
 |         printf("// cmd-line:"); | 
 |         for (int i=0 ; i<argc ; i++) { | 
 |             printf(" %s", argv[i]); | 
 |         } | 
 |         printf("\n"); | 
 |         if (declarations) { | 
 |             if (!polyphase) { | 
 |                 printf("const int32_t RESAMPLE_FIR_SIZE           = %d;\n", N); | 
 |                 printf("const int32_t RESAMPLE_FIR_INT_PHASES     = %d;\n", M); | 
 |                 printf("const int32_t RESAMPLE_FIR_NUM_COEF       = %d;\n", nzc); | 
 |             } else { | 
 |                 printf("const int32_t RESAMPLE_FIR_SIZE           = %d;\n", 2*nzc*polyN); | 
 |                 printf("const int32_t RESAMPLE_FIR_NUM_COEF       = %d;\n", 2*nzc); | 
 |             } | 
 |             if (!format) { | 
 |                 printf("const int32_t RESAMPLE_FIR_COEF_BITS      = %d;\n", nc); | 
 |             } | 
 |             printf("\n"); | 
 |             printf("static %s resampleFIR[] = {", !format ? "int32_t" : "float"); | 
 |         } | 
 |     } | 
 |  | 
 |     if (!polyphase) { | 
 |         for (int i=0 ; i<=M ; i++) { // an extra set of coefs for interpolation | 
 |             for (int j=0 ; j<nzc ; j++) { | 
 |                 int ix = j*M + i; | 
 |                 double x = (2.0 * M_PI * ix * Fcr) / M; | 
 |                 double y = kaiser(ix+N, 2*N, beta) * sinc(x) * 2.0 * Fcr; | 
 |                 y *= atten; | 
 |  | 
 |                 if (!debug) { | 
 |                     if (j == 0) | 
 |                         printf("\n    "); | 
 |                 } | 
 |                 if (!format) { | 
 |                     int64_t yi = toint(y, 1ULL<<(nc-1)); | 
 |                     if (nc > 16) { | 
 |                         printf("0x%08x,", int32_t(yi)); | 
 |                     } else { | 
 |                         printf("0x%04x,", int32_t(yi)&0xffff); | 
 |                     } | 
 |                 } else { | 
 |                     printf("%.9g%s", y, debug ? "," : "f,"); | 
 |                 } | 
 |                 if (j != nzc-1) { | 
 |                     printf(" "); | 
 |                 } | 
 |             } | 
 |         } | 
 |     } else { | 
 |         for (unsigned int j=0 ; j<polyN ; j++) { | 
 |             // calculate the phase | 
 |             double p = ((polyM*j) % polyN) / double(polyN); | 
 |             if (!debug) printf("\n    "); | 
 |             else        printf("\n"); | 
 |             // generate a FIR per phase | 
 |             for (int i=-nzc ; i<nzc ; i++) { | 
 |                 double x = 2.0 * M_PI * Fcr * (i + p); | 
 |                 double y = kaiser(i+N, 2*N, beta) * sinc(x) * 2.0 * Fcr;; | 
 |                 y *= atten; | 
 |                 if (!format) { | 
 |                     int64_t yi = toint(y, 1ULL<<(nc-1)); | 
 |                     if (nc > 16) { | 
 |                         printf("0x%08x,", int32_t(yi)); | 
 |                     } else { | 
 |                         printf("0x%04x,", int32_t(yi)&0xffff); | 
 |                     } | 
 |                 } else { | 
 |                     printf("%.9g%s", y, debug ? "," : "f,"); | 
 |                 } | 
 |  | 
 |                 if (i != nzc-1) { | 
 |                     printf(" "); | 
 |                 } | 
 |             } | 
 |         } | 
 |     } | 
 |  | 
 |     if (!debug && declarations) { | 
 |         printf("\n};"); | 
 |     } | 
 |     printf("\n"); | 
 |     return 0; | 
 | } | 
 |  | 
 | // http://www.csee.umbc.edu/help/sound/AFsp-V2R1/html/audio/ResampAudio.html |