#!/usr/bin/awk -f # # Copyright (c) 2007 Ariff Abdullah # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. # # # FIR filter design by windowing method. This might become one of the # funniest joke I've ever written due to too many tricks being applied to # ensure maximum precision (well, in fact this is already have the same # precision granularity compared to its C counterpart). Nevertheless, it's # working, precise, dynamically tunable based on "presets" (so that # LIARS^H^H^H^H^H audiophiles can tune it based on their own # placebo^H^H^H^H^H^H^H needs). # # # Some basic Math functions. # function abs(x) { return (((x < 0) ? -x : x) + 0); } function fabs(x) { return (((x < 0.0) ? -x : x) + 0.0); } function floor(x, r) { r = int(x); if (r > x) r--; return (r + 0); } #function xfloor(x, a, r) #{ # split(sprintf("%f", x), a, "."); # r = abs(a[1]) + 0; # if (x < 0.0) { # if ((x + r) != 0.0) # r++; # r = -r; # } # return (r + 0); #} function pow(x, y) { return (exp(1.0 * y * log(1.0 * x))); } # # What the hell... # function shl(x, y) { while (y > 0) { x *= 2; y--; } return (x); } function shr(x, y) { while (y > 0) { x /= 2; y--; } return (x); } # # Kaiser linear piecewise functions. # function kaiserAttn2Beta(attn, beta) { if (attn < 0.0) return (Z_KAISER_BETA_DEFAULT); if (attn > 50.0) beta = 0.1102 * ((1.0 * attn) - 8.7); else if (attn > 21.0) beta = (0.5842 * pow((1.0 * attn) - 21.0, 0.4)) + \ (0.07886 * ((1.0 * attn) - 21.0)); else beta = 0.0; return (beta); } function kaiserBeta2Attn(beta, x, y, i, attn, xbeta) { if (beta < Z_WINDOW_KAISER) return (Z_KAISER_ATTN_DEFAULT); if (beta > kaiserAttn2Beta(50.0)) attn = ((1.0 * beta) / 0.1102) + 8.7; else { x = 21.0; y = 50.0; attn = 0.5 * (x + y); for (i = 0; i < 128; i++) { xbeta = kaiserAttn2Beta(attn) if (beta == xbeta || \ (i > 63 && \ fabs(beta - xbeta) < Z_KAISER_EPSILON)) break; if (beta > xbeta) x = attn; else y = attn; attn = 0.5 * (x + y); } } return (attn); } function kaiserRolloff(len, attn) { return (1.0 - (((1.0 * attn) - 7.95) / (((1.0 * len) - 1.0) * 14.36))); } # # 0th order modified Bessel function of the first kind. # function I0(x, s, u, n, h, t) { s = n = u = 1.0; h = x * 0.5; do { t = h / n; n += 1.0; t *= t; u *= t; s += u; } while (u >= (I0_EPSILON * s)); return (s); } function wname(beta) { if (beta >= Z_WINDOW_KAISER) return ("Kaiser"); else if (beta == Z_WINDOW_BLACKMAN_NUTTALL) return ("Blackman - Nuttall"); else if (beta == Z_WINDOW_NUTTALL) return ("Nuttall"); else if (beta == Z_WINDOW_BLACKMAN_HARRIS) return ("Blackman - Harris"); else if (beta == Z_WINDOW_BLACKMAN) return ("Blackman"); else if (beta == Z_WINDOW_HAMMING) return ("Hamming"); else if (beta == Z_WINDOW_HANN) return ("Hann"); else return ("What The Hell !?!?"); } function rolloff_round(x) { if (x < 0.67) x = 0.67; else if (x > 1.0) x = 1.0; return (x); } function lpf(imp, n, rolloff, beta, num, i, j, x, nm, ibeta, w) { rolloff = rolloff_round(rolloff + Z_NYQUIST_HOVER * (1.0 - rolloff)); imp[0] = rolloff; # # Generate ideal sinc impulses, locate the last zero-crossing and pad # the remaining with 0. # # Note that there are other (faster) ways of calculating this without # the misery of traversing the entire sinc given the fact that the # distance between each zero crossings is actually the bandwidth of # the impulses, but it seems having 0.0001% chances of failure due to # limited precision. # j = n; for (i = 1; i < n; i++) { x = (M_PI * i) / (1.0 * num); imp[i] = sin(x * rolloff) / x; if (i != 1 && (imp[i] * imp[i - 1]) <= 0.0) j = i; } for (i = j; i < n; i++) imp[i] = 0.0; n = j; nm = 1.0 * (n - 1); if (beta >= Z_WINDOW_KAISER) ibeta = I0(beta); for (i = 1; i < n; i++) { if (beta >= Z_WINDOW_KAISER) { # Kaiser window... x = (1.0 * i) / nm; w = I0(beta * sqrt(1.0 - (x * x))) / ibeta; } else { # Cosined windows... x = (M_PI * i) / nm; if (beta == Z_WINDOW_BLACKMAN_NUTTALL) { # Blackman - Nuttall w = 0.36335819 + (0.4891775 * cos(x)) + \ (0.1365995 * cos(2 * x)) + \ (0.0106411 * cos(3 * x)); } else if (beta == Z_WINDOW_NUTTALL) { # Nuttall w = 0.355768 + (0.487396 * cos(x)) + \ (0.144232 * cos(2 * x)) + \ (0.012604 * cos(3 * x)); } else if (beta == Z_WINDOW_BLACKMAN_HARRIS) { # Blackman - Harris w = 0.422323 + (0.49755 * cos(x)) + \ (0.07922 * cos(2 * x)); } else if (beta == Z_WINDOW_BLACKMAN) { # Blackman w = 0.42 + (0.50 * cos(x)) + \ (0.08 * cos(2 * x)); } else if (beta == Z_WINDOW_HAMMING) { # Hamming w = 0.54 + (0.46 * cos(x)); } else if (beta == Z_WINDOW_HANN) { # Hann w = 0.50 + (0.50 * cos(x)); } else { # What The Hell !?!? w = 0.0; } } imp[i] *= w; } imp["impulse_length"] = n; imp["rolloff"] = rolloff; } function makeFilter(imp, nmult, rolloff, beta, num, \ nwing, mwing, nrolloff, i, dcgain, v, quality) { nwing = ((nmult * num) / 2) + 1; lpf(imp, nwing, rolloff, beta, num); mwing = imp["impulse_length"]; nrolloff = imp["rolloff"]; quality = imp["quality"]; dcgain = 0.0; for (i = num; i < mwing; i += num) dcgain += imp[i]; dcgain *= 2.0; dcgain += imp[0]; for (i = 0; i < nwing; i++) { imp[i] /= dcgain; imp["fixed", i] = floor((imp[i] * Z_COEFF_ONE) + 0.5); } if (quality > 2) printf("\n"); printf("/*\n"); printf(" * quality = %d\n", quality); printf(" * window = %s\n", wname(beta)); if (beta >= Z_WINDOW_KAISER) { printf(" * beta: %.2f\n", beta); printf(" * stop: -%.2f dB\n", \ kaiserBeta2Attn(beta)); } printf(" * length = %d\n", nmult); printf(" * bandwidth = %.2f%%", rolloff * 100.0); if (rolloff != nrolloff) { printf(" + %.2f%% = %.2f%% (nyquist hover: %.2f%%)", \ (nrolloff - rolloff) * 100.0, nrolloff * 100.0, \ Z_NYQUIST_HOVER * 100.0); } printf("\n"); printf(" * drift = %d\n", num); printf(" * width = %d\n", mwing); printf(" */\n"); printf("static int32_t z_coeff_q%d[%d] = {", quality, nwing); for (i = 0; i < nwing; i++) { if ((i % 7) == 0) printf("\n"); v = imp["fixed", i]; printf(" %s0x%05x,", (v < 0) ? "-" : " ", abs(v)); } printf("\n};\n\n"); printf("/*\n"); printf(" * q%d differences.\n", quality); printf(" */\n"); printf("static int32_t z_dcoeff_q%d[%d] = {", quality, nwing); for (i = 1; i <= nwing; i++) { if ((i % 7) == 1) printf("\n"); v = -imp["fixed", i - 1]; if (i != nwing) v += imp["fixed", i]; printf(" %s0x%05x,", (v < 0) ? "-" : " ", abs(v)); } printf("\n};\n"); return (nwing); } function filter_parse(s, a, i, attn, alen) { split(s, a, ":"); alen = length(a); if (alen == 1 || alen == 2) { if (a[1] == "nyquist_hover") { i = 1.0 * a[2]; Z_NYQUIST_HOVER = (i > 0.0 && i < 1.0) ? i : 0.0; return (-1); } i = 1; if (alen == 1) { attn = Z_KAISER_ATTN_DEFAULT; Popts["beta"] = Z_KAISER_BETA_DEFAULT; } else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) { Popts["beta"] = Z_WINDOWS[a[1]]; i = floor(a[2]) + 1; i -= i % 2; Popts["nmult"] = i; if (i < 28) i = 28; i = 1.0 - (6.44 / i); Popts["rolloff"] = rolloff_round(i); return (0); } else { attn = 1.0 * a[i++]; Popts["beta"] = kaiserAttn2Beta(attn); } i = floor(a[i]) + 1; i -= i % 2; Popts["nmult"] = i; if (i > 7 && i < 28) i = 27; i = kaiserRolloff(i, attn); Popts["rolloff"] = rolloff_round(i); return (0); } if (!(alen == 3 || alen == 4)) return (-1); i = 2; if (a[1] == "kaiser") { if (alen > 2) Popts["beta"] = 1.0 * a[i++]; else Popts["beta"] = Z_KAISER_BETA_DEFAULT; } else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) Popts["beta"] = Z_WINDOWS[a[1]]; else if (1.0 * a[1] < Z_WINDOW_KAISER) return (-1); else Popts["beta"] = kaiserAttn2Beta(1.0 * a[1]); Popts["nmult"] = floor(a[i++]) + 1; Popts["nmult"] -= Popts["nmult"] % 2; if (a[1] == "kaiser" && alen == 3) i = kaiserRolloff(Popts["nmult"], \ kaiserBeta2Attn(Popts["beta"])); else i = 1.0 * a[i]; Popts["rolloff"] = rolloff_round(i); return (0); } BEGIN { I0_EPSILON = 1e-21; M_PI = atan2(0.0, -1.0); Z_FULL_SHIFT = 30; Z_FULL_ONE = shl(1, Z_FULL_SHIFT); # # 6, 7, or 8 depending on how much you can trade off between memory # consumption (due to large tables) and precision / quality. # Z_DRIFT_SHIFT = 7; Z_DRIFT_ONE = shl(1, Z_DRIFT_SHIFT); Z_SHIFT = Z_FULL_SHIFT - Z_DRIFT_SHIFT; Z_ONE = shl(1, Z_SHIFT); Z_MASK = Z_ONE - 1; Z_COEFF_SHIFT = 15 + (Z_DRIFT_SHIFT - 4); if (Z_COEFF_SHIFT > 19) Z_COEFF_SHIFT = 19; else if (Z_COEFF_SHIFT < 15) Z_COEFF_SHIFT = 15; Z_COEFF_ONE = shl(1, Z_COEFF_SHIFT); Z_COEFF_UNSHIFT = Z_SHIFT - Z_COEFF_SHIFT; Z_LINEAR_SHIFT = 8; Z_LINEAR_ONE = shl(1, Z_LINEAR_SHIFT) Z_WINDOW_KAISER = 0.0; Z_WINDOW_BLACKMAN_NUTTALL = -1.0; Z_WINDOW_NUTTALL = -2.0; Z_WINDOW_BLACKMAN_HARRIS = -3.0; Z_WINDOW_BLACKMAN = -4.0; Z_WINDOW_HAMMING = -5.0; Z_WINDOW_HANN = -6.0; Z_WINDOWS["blackman_nuttall"] = Z_WINDOW_BLACKMAN_NUTTALL; Z_WINDOWS["nuttall"] = Z_WINDOW_NUTTALL; Z_WINDOWS["blackman_harris"] = Z_WINDOW_BLACKMAN_HARRIS; Z_WINDOWS["blackman"] = Z_WINDOW_BLACKMAN; Z_WINDOWS["hamming"] = Z_WINDOW_HAMMING; Z_WINDOWS["hann"] = Z_WINDOW_HANN; Z_KAISER_2_BLACKMAN_BETA = 8.568611; Z_KAISER_2_BLACKMAN_NUTTALL_BETA = 11.98; Z_KAISER_ATTN_DEFAULT = 100; Z_KAISER_BETA_DEFAULT = kaiserAttn2Beta(Z_KAISER_ATTN_DEFAULT); Z_KAISER_EPSILON = 1e-21; # # This is practically a joke. # Z_NYQUIST_HOVER = 0.0; if (ARGC < 2) { ARGC = 1; ARGV[ARGC++] = "100:8"; ARGV[ARGC++] = "100:16"; ARGV[ARGC++] = "100:32:0.7929"; ARGV[ARGC++] = "100:64:0.8990"; ARGV[ARGC++] = "100:128:0.9499"; } printf("#ifndef _Z_COEFF_H_\n"); printf("#define _Z_COEFF_H_\n\n"); printf("/*\n"); printf(" * Generated using awk/mkfilter.awk, heaven, wind and awesome.\n"); printf(" *\n"); printf(" * DO NOT EDIT!\n"); printf(" */\n\n"); imp["quality"] = 2; for (i = 1; i < ARGC; i++) { if (filter_parse(ARGV[i]) == 0) { beta = Popts["beta"]; nmult = Popts["nmult"]; rolloff = Popts["rolloff"]; ztab[imp["quality"] - 2] = \ makeFilter(imp, nmult, rolloff, beta, Z_DRIFT_ONE); imp["quality"]++; } } printf("\n"); # # XXX # #if (length(ztab) > 0) { # j = 0; # for (i = 0; i < length(ztab); i++) { # if (ztab[i] > j) # j = ztab[i]; # } # printf("static int32_t z_coeff_zero[%d] = {", j); # for (i = 0; i < j; i++) { # if ((i % 19) == 0) # printf("\n"); # printf(" 0,"); # } # printf("\n};\n\n"); #} # # XXX # printf("static const struct {\n"); printf("\tint32_t len;\n"); printf("\tint32_t *coeff;\n"); printf("\tint32_t *dcoeff;\n"); printf("} z_coeff_tab[] = {\n"); if (length(ztab) > 0) { j = 0; for (i = 0; i < length(ztab); i++) { if (ztab[i] > j) j = ztab[i]; } j = length(sprintf("%d", j)); lfmt = sprintf("%%%dd", j); j = length(sprintf("z_coeff_q%d", length(ztab) + 1)); zcfmt = sprintf("%%-%ds", j); zdcfmt = sprintf("%%-%ds", j + 1); for (i = 0; i < length(ztab); i++) { l = sprintf(lfmt, ztab[i]); zc = sprintf("z_coeff_q%d", i + 2); zc = sprintf(zcfmt, zc); zdc = sprintf("z_dcoeff_q%d", i + 2); zdc = sprintf(zdcfmt, zdc); printf("\t{ %s, %s, %s },\n", l, zc, zdc); } } else printf("\t{ 0, NULL, NULL }\n"); printf("};\n\n"); printf("#define Z_COEFF_TAB_SIZE\t\t\t\t\t\t\\\n"); printf("\t((int32_t)(sizeof(z_coeff_tab) /"); printf(" sizeof(z_coeff_tab[0])))\n\n"); printf("#define Z_FULL_SHIFT\t\t%d\n", Z_FULL_SHIFT); printf("#define Z_FULL_ONE\t\t0x%08xU\n", Z_FULL_ONE); printf("\n"); printf("#define Z_DRIFT_SHIFT\t\t%d\n", Z_DRIFT_SHIFT); printf("#define Z_DRIFT_ONE\t\t0x%08xU\n", Z_DRIFT_ONE); printf("\n"); printf("#define Z_SHIFT\t\t\t%d\n", Z_SHIFT); printf("#define Z_ONE\t\t\t0x%08xU\n", Z_ONE); printf("#define Z_MASK\t\t\t0x%08xU\n", Z_MASK); printf("\n"); printf("#define Z_COEFF_SHIFT\t\t%d\n", Z_COEFF_SHIFT); printf("#define Z_COEFF_UNSHIFT\t\t%d\n", Z_COEFF_UNSHIFT); printf("\n"); printf("#define Z_LINEAR_SHIFT\t\t%d\n", Z_LINEAR_SHIFT); printf("#define Z_LINEAR_ONE\t\t0x%08xU\n", Z_LINEAR_ONE); printf("\n"); printf("#define Z_QUALITY_ZOH\t\t0\n"); printf("#define Z_QUALITY_LINEAR\t1\n"); printf("#define Z_QUALITY_SINC\t\t%d\n", ((length(ztab) - 1) / 2) + 2); printf("\n"); printf("#define Z_QUALITY_MIN\t\t0\n"); printf("#define Z_QUALITY_MAX\t\t%d\n", length(ztab) + 1); printf("\n#endif /* !_Z_COEFF_H_ */\n"); }