summaryrefslogtreecommitdiffstats
path: root/sys/tools
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2009-06-07 19:12:08 +0000
committerariff <ariff@FreeBSD.org>2009-06-07 19:12:08 +0000
commite3faadaafebd1b18fd6b8ed30f654df974d390a6 (patch)
tree906f402638735c3f32e226f6868f207db569d6a9 /sys/tools
parentf482e4a9e0f02a82e107e2bbee139c6a0b48be80 (diff)
downloadFreeBSD-src-e3faadaafebd1b18fd6b8ed30f654df974d390a6.zip
FreeBSD-src-e3faadaafebd1b18fd6b8ed30f654df974d390a6.tar.gz
Sound Mega-commit. Expect further cleanup until code freeze.
For a slightly thorough explaination, please refer to [1] http://people.freebsd.org/~ariff/SOUND_4.TXT.html . Summary of changes includes: 1 Volume Per-Channel (vpc). Provides private / standalone volume control unique per-stream pcm channel without touching master volume / pcm. Applications can directly use SNDCTL_DSP_[GET|SET][PLAY|REC]VOL, or for backwards compatibility, SOUND_MIXER_PCM through the opened dsp device instead of /dev/mixer. Special "bypass" mode is enabled through /dev/mixer which will automatically detect if the adjustment is made through /dev/mixer and forward its request to this private volume controller. Changes to this volume object will not interfere with other channels. Requirements: - SNDCTL_DSP_[GET|SET][PLAY|REC]_VOL are newer ioctls (OSSv4) which require specific application modifications (preferred). - No modifications required for using bypass mode, so applications like mplayer or xmms should work out of the box. Kernel hints: - hint.pcm.%d.vpc (0 = disable vpc). Kernel sysctls: - hw.snd.vpc_mixer_bypass (default: 1). Enable or disable /dev/mixer bypass mode. - hw.snd.vpc_autoreset (default: 1). By default, closing/opening /dev/dsp will reset the volume back to 0 db gain/attenuation. Setting this to 0 will preserve its settings across device closing/opening. - hw.snd.vpc_reset (default: 0). Panic/reset button to reset all volume settings back to 0 db. - hw.snd.vpc_0db (default: 45). 0 db relative to linear mixer value. 2 High quality fixed-point Bandlimited SINC sampling rate converter, based on Julius O'Smith's Digital Audio Resampling - http://ccrma.stanford.edu/~jos/resample/. It includes a filter design script written in awk (the clumsiest joke I've ever written) - 100% 32bit fixed-point, 64bit accumulator. - Possibly among the fastest (if not fastest) of its kind. - Resampling quality is tunable, either runtime or during kernel compilation (FEEDER_RATE_PRESETS). - Quality can be further customized during kernel compilation by defining FEEDER_RATE_PRESETS in /etc/make.conf. Kernel sysctls: - hw.snd.feeder_rate_quality. 0 - Zero-order Hold (ZOH). Fastest, bad quality. 1 - Linear Interpolation (LINEAR). Slightly slower than ZOH, better quality but still does not eliminate aliasing. 2 - (and above) - Sinc Interpolation(SINC). Best quality. SINC quality always start from 2 and above. Rough quality comparisons: - http://people.freebsd.org/~ariff/z_comparison/ 3 Bit-perfect mode. Bypasses all feeder/dsp effects. Pure sound will be directly fed into the hardware. 4 Parametric (compile time) Software Equalizer (Bass/Treble mixer). Can be customized by defining FEEDER_EQ_PRESETS in /etc/make.conf. 5 Transparent/Adaptive Virtual Channel. Now you don't have to disable vchans in order to make digital format pass through. It also makes vchans more dynamic by choosing a better format/rate among all the concurrent streams, which means that dev.pcm.X.play.vchanformat/rate becomes sort of optional. 6 Exclusive Stream, with special open() mode O_EXCL. This will "mute" other concurrent vchan streams and only allow a single channel with O_EXCL set to keep producing sound. Other Changes: * most feeder_* stuffs are compilable in userland. Let's not speculate whether we should go all out for it (save that for FreeBSD 16.0-RELEASE). * kobj signature fixups, thanks to Andriy Gapon <avg@freebsd.org> * pull out channel mixing logic out of vchan.c and create its own feeder_mixer for world justice. * various refactoring here and there, for good or bad. * activation of few more OSSv4 ioctls() (see [1] above). * opt_snd.h for possible compile time configuration: (mostly for debugging purposes, don't try these at home) SND_DEBUG SND_DIAGNOSTIC SND_FEEDER_MULTIFORMAT SND_FEEDER_FULL_MULTIFORMAT SND_FEEDER_RATE_HP SND_PCM_64 SND_OLDSTEREO Manual page updates are on the way. Tested by: joel, Olivier SMEDTS <olivier at gid0 d org>, too many unsung / unnamed heroes.
Diffstat (limited to 'sys/tools')
-rw-r--r--sys/tools/feeder_eq_mkfilter.awk467
-rw-r--r--sys/tools/feeder_rate_mkfilter.awk767
-rw-r--r--sys/tools/snd_fxdiv_gen.awk142
3 files changed, 1376 insertions, 0 deletions
diff --git a/sys/tools/feeder_eq_mkfilter.awk b/sys/tools/feeder_eq_mkfilter.awk
new file mode 100644
index 0000000..5c2efb6
--- /dev/null
+++ b/sys/tools/feeder_eq_mkfilter.awk
@@ -0,0 +1,467 @@
+#!/usr/bin/awk -f
+#
+# Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
+# 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.
+#
+# $FreeBSD$
+#
+
+#
+# Biquad coefficients generator for Parametric Software Equalizer. Not as ugly
+# as 'feeder_rate_mkfilter.awk'
+#
+# Based on:
+#
+# "Cookbook formulae for audio EQ biquad filter coefficients"
+# by Robert Bristow-Johnson <rbj@audioimagination.com>
+#
+# - http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
+#
+
+
+
+#
+# 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 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 feedeq_w0(fc, rate)
+{
+ return ((2.0 * M_PI * fc) / (1.0 * rate));
+}
+
+function feedeq_A(gain, A)
+{
+ if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ || FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF)
+ A = pow(10, gain / 40.0);
+ else
+ A = sqrt(pow(10, gain / 20.0));
+
+ return (A);
+}
+
+function feedeq_alpha(w0, A, QS)
+{
+ if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ)
+ alpha = sin(w0) / (2.0 * QS);
+ else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF)
+ alpha = sin(w0) * 0.5 * sqrt(A + ((1.0 / A) * \
+ ((1.0 / QS) - 1.0)) + 2.0);
+ else
+ alpha = 0.0;
+
+ return (alpha);
+}
+
+function feedeq_fx_floor(v, r)
+{
+ if (fabs(v) < fabs(smallest))
+ smallest = v;
+ if (fabs(v) > fabs(largest))
+ largest = v;
+
+ r = floor((v * FEEDEQ_COEFF_ONE) + 0.5);
+
+ if (r < INT32_MIN || r > INT32_MAX)
+ printf("\n#error overflow v=%f, " \
+ "please reduce FEEDEQ_COEFF_SHIFT\n", v);
+
+ return (r);
+}
+
+function feedeq_gen_biquad_coeffs(coeffs, rate, gain, \
+ w0, A, alpha, a0, a1, a2, b0, b1, b2)
+{
+ w0 = feedeq_w0(FEEDEQ_TREBLE_SFREQ, 1.0 * rate);
+ A = feedeq_A(1.0 * gain);
+ alpha = feedeq_alpha(w0, A, FEEDEQ_TREBLE_SLOPE);
+
+ if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) {
+ b0 = 1.0 + (alpha * A);
+ b1 = -2.0 * cos(w0);
+ b2 = 1.0 - (alpha * A);
+ a0 = 1.0 + (alpha / A);
+ a1 = -2.0 * cos(w0);
+ a2 = 1.0 - (alpha / A);
+ } else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) {
+ b0 = A*((A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha));
+ b1 = -2.0*A*((A-1.0)+((A+1.0)*cos(w0)) );
+ b2 = A*((A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha));
+ a0 = (A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha );
+ a1 = 2.0 * ((A-1.0)-((A+1.0)*cos(w0)) );
+ a2 = (A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha );
+ } else
+ b0 = b1 = b2 = a0 = a1 = a2 = 0.0;
+
+ b0 /= a0;
+ b1 /= a0;
+ b2 /= a0;
+ a1 /= a0;
+ a2 /= a0;
+
+ coeffs["treble", gain, 0] = feedeq_fx_floor(a0);
+ coeffs["treble", gain, 1] = feedeq_fx_floor(a1);
+ coeffs["treble", gain, 2] = feedeq_fx_floor(a2);
+ coeffs["treble", gain, 3] = feedeq_fx_floor(b0);
+ coeffs["treble", gain, 4] = feedeq_fx_floor(b1);
+ coeffs["treble", gain, 5] = feedeq_fx_floor(b2);
+
+ w0 = feedeq_w0(FEEDEQ_BASS_SFREQ, 1.0 * rate);
+ A = feedeq_A(1.0 * gain);
+ alpha = feedeq_alpha(w0, A, FEEDEQ_BASS_SLOPE);
+
+ if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) {
+ b0 = 1.0 + (alpha * A);
+ b1 = -2.0 * cos(w0);
+ b2 = 1.0 - (alpha * A);
+ a0 = 1.0 + (alpha / A);
+ a1 = -2.0 * cos(w0);
+ a2 = 1.0 - (alpha / A);
+ } else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) {
+ b0 = A*((A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha));
+ b1 = 2.0*A*((A-1.0)-((A+1.0)*cos(w0)) );
+ b2 = A*((A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha));
+ a0 = (A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha );
+ a1 = -2.0 * ((A-1.0)+((A+1.0)*cos(w0)) );
+ a2 = (A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha );
+ } else
+ b0 = b1 = b2 = a0 = a1 = a2 = 0.0;
+
+ b0 /= a0;
+ b1 /= a0;
+ b2 /= a0;
+ a1 /= a0;
+ a2 /= a0;
+
+ coeffs["bass", gain, 0] = feedeq_fx_floor(a0);
+ coeffs["bass", gain, 1] = feedeq_fx_floor(a1);
+ coeffs["bass", gain, 2] = feedeq_fx_floor(a2);
+ coeffs["bass", gain, 3] = feedeq_fx_floor(b0);
+ coeffs["bass", gain, 4] = feedeq_fx_floor(b1);
+ coeffs["bass", gain, 5] = feedeq_fx_floor(b2);
+}
+
+function feedeq_gen_freq_coeffs(frq, g, i, v)
+{
+ coeffs[0] = 0;
+
+ for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \
+ g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \
+ g += FEEDEQ_GAIN_STEP) {
+ feedeq_gen_biquad_coeffs(coeffs, frq, \
+ g * FEEDEQ_GAIN_RECIPROCAL);
+ }
+
+ printf("\nstatic struct feed_eq_coeff eq_%d[%d] " \
+ "= {\n", frq, FEEDEQ_LEVELS);
+ for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \
+ g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \
+ g += FEEDEQ_GAIN_STEP) {
+ printf(" {{ ");
+ for (i = 1; i < 6; i++) {
+ v = coeffs["treble", g * FEEDEQ_GAIN_RECIPROCAL, i];
+ printf("%s0x%08x%s", \
+ (v < 0) ? "-" : " ", abs(v), \
+ (i == 5) ? " " : ", ");
+ }
+ printf("},\n { ");
+ for (i = 1; i < 6; i++) {
+ v = coeffs["bass", g * FEEDEQ_GAIN_RECIPROCAL, i];
+ printf("%s0x%08x%s", \
+ (v < 0) ? "-" : " ", abs(v), \
+ (i == 5) ? " " : ", ");
+ }
+ printf("}}%s\n", \
+ (g < (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV)) ? "," : "");
+ }
+ printf("};\n");
+}
+
+function feedeq_calc_preamp(norm, gain, shift, mul, bit, attn)
+{
+ shift = FEEDEQ_PREAMP_SHIFT;
+
+ if (floor(FEEDEQ_PREAMP_BITDB) == 6 && \
+ (1.0 * floor(gain)) == gain && (floor(gain) % 6) == 0) {
+ mul = 1;
+ shift = floor(floor(gain) / 6);
+ } else {
+ bit = 32.0 - ((1.0 * gain) / (1.0 * FEEDEQ_PREAMP_BITDB));
+ attn = pow(2.0, bit) / pow(2.0, 32.0);
+ mul = floor((attn * FEEDEQ_PREAMP_ONE) + 0.5);
+ }
+
+ while ((mul % 2) == 0 && shift > 0) {
+ mul = floor(mul / 2);
+ shift--;
+ }
+
+ norm["mul"] = mul;
+ norm["shift"] = shift;
+}
+
+BEGIN {
+ M_PI = atan2(0.0, -1.0);
+
+ INT32_MAX = 1 + ((shl(1, 30) - 1) * 2);
+ INT32_MIN = -1 - INT32_MAX;
+
+ FEEDEQ_TYPE_PEQ = 0;
+ FEEDEQ_TYPE_SHELF = 1;
+
+ FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ;
+
+ FEEDEQ_COEFF_SHIFT = 24;
+ FEEDEQ_COEFF_ONE = shl(1, FEEDEQ_COEFF_SHIFT);
+
+ FEEDEQ_PREAMP_SHIFT = 31;
+ FEEDEQ_PREAMP_ONE = shl(1, FEEDEQ_PREAMP_SHIFT);
+ FEEDEQ_PREAMP_BITDB = 6; # 20.0 * (log(2.0) / log(10.0));
+
+ FEEDEQ_GAIN_DIV = 10;
+ i = 0;
+ j = 1;
+ while (j < FEEDEQ_GAIN_DIV) {
+ j *= 2;
+ i++;
+ }
+ FEEDEQ_GAIN_SHIFT = i;
+ FEEDEQ_GAIN_FMASK = shl(1, FEEDEQ_GAIN_SHIFT) - 1;
+
+ FEEDEQ_GAIN_RECIPROCAL = 1.0 / FEEDEQ_GAIN_DIV;
+
+ if (ARGC == 2) {
+ i = 1;
+ split(ARGV[1], arg, ":");
+ while (match(arg[i], "^[^0-9]*$")) {
+ if (arg[i] == "PEQ") {
+ FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ;
+ } else if (arg[i] == "SHELF") {
+ FEEDEQ_TYPE = FEEDEQ_TYPE_SHELF;
+ }
+ i++;
+ }
+ split(arg[i++], subarg, ",");
+ FEEDEQ_TREBLE_SFREQ = 1.0 * subarg[1];
+ FEEDEQ_TREBLE_SLOPE = 1.0 * subarg[2];
+ split(arg[i++], subarg, ",");
+ FEEDEQ_BASS_SFREQ = 1.0 * subarg[1];
+ FEEDEQ_BASS_SLOPE = 1.0 * subarg[2];
+ split(arg[i++], subarg, ",");
+ FEEDEQ_GAIN_MIN = floor(1.0 * subarg[1]);
+ FEEDEQ_GAIN_MAX = floor(1.0 * subarg[2]);
+ if (length(subarg) > 2) {
+ j = floor(1.0 * FEEDEQ_GAIN_DIV * subarg[3]);
+ if (j < 2)
+ j = 1;
+ else if (j < 5)
+ j = 2;
+ else if (j < 10)
+ j = 5;
+ else
+ j = 10;
+ if (j > FEEDEQ_GAIN_DIV || (FEEDEQ_GAIN_DIV % j) != 0)
+ j = FEEDEQ_GAIN_DIV;
+ FEEDEQ_GAIN_STEP = j;
+ } else
+ FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV;
+ split(arg[i], subarg, ",");
+ for (i = 1; i <= length(subarg); i++)
+ allfreq[i - 1] = floor(1.0 * subarg[i]);
+ } else {
+ FEEDEQ_TREBLE_SFREQ = 16000.0;
+ FEEDEQ_TREBLE_SLOPE = 0.25;
+ FEEDEQ_BASS_SFREQ = 62.0;
+ FEEDEQ_BASS_SLOPE = 0.25;
+
+ FEEDEQ_GAIN_MIN = -9;
+ FEEDEQ_GAIN_MAX = 9;
+
+ FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV;
+
+
+ allfreq[0] = 44100;
+ allfreq[1] = 48000;
+ allfreq[2] = 88200;
+ allfreq[3] = 96000;
+ allfreq[4] = 176400;
+ allfreq[5] = 192000;
+ }
+
+ FEEDEQ_LEVELS = ((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) * \
+ floor(FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1;
+
+ FEEDEQ_ERR_CLIP = 0;
+
+ smallest = 10.000000;
+ largest = 0.000010;
+
+ printf("#ifndef _FEEDER_EQ_GEN_H_\n");
+ printf("#define _FEEDER_EQ_GEN_H_\n\n");
+ printf("/*\n");
+ printf(" * Generated using feeder_eq_mkfilter.awk, heaven, wind and awesome.\n");
+ printf(" *\n");
+ printf(" * DO NOT EDIT!\n");
+ printf(" */\n\n");
+ printf("/*\n");
+ printf(" * EQ: %s\n", (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? \
+ "Shelving" : "Peaking EQ");
+ printf(" */\n");
+ printf("#define FEEDER_EQ_PRESETS\t\"");
+ printf("%s:%d,%.4f,%d,%.4f:%d,%d,%.1f:", \
+ (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? "SHELF" : "PEQ", \
+ FEEDEQ_TREBLE_SFREQ, FEEDEQ_TREBLE_SLOPE, \
+ FEEDEQ_BASS_SFREQ, FEEDEQ_BASS_SLOPE, \
+ FEEDEQ_GAIN_MIN, FEEDEQ_GAIN_MAX, \
+ FEEDEQ_GAIN_STEP * FEEDEQ_GAIN_RECIPROCAL);
+ for (i = 0; i < length(allfreq); i++) {
+ if (i != 0)
+ printf(",");
+ printf("%d", allfreq[i]);
+ }
+ printf("\"\n\n");
+ printf("struct feed_eq_coeff_tone {\n");
+ printf("\tint32_t a1, a2;\n");
+ printf("\tint32_t b0, b1, b2;\n");
+ printf("};\n\n");
+ printf("struct feed_eq_coeff {\n");
+ #printf("\tstruct {\n");
+ #printf("\t\tint32_t a1, a2;\n");
+ #printf("\t\tint32_t b0, b1, b2;\n");
+ #printf("\t} treble, bass;\n");
+ printf("\tstruct feed_eq_coeff_tone treble;\n");
+ printf("\tstruct feed_eq_coeff_tone bass;\n");
+ #printf("\tstruct {\n");
+ #printf("\t\tint32_t a1, a2;\n");
+ #printf("\t\tint32_t b0, b1, b2;\n");
+ #printf("\t} bass;\n");
+ printf("};\n");
+ for (i = 0; i < length(allfreq); i++)
+ feedeq_gen_freq_coeffs(allfreq[i]);
+ printf("\n");
+ printf("static const struct {\n");
+ printf("\tuint32_t rate;\n");
+ printf("\tstruct feed_eq_coeff *coeff;\n");
+ printf("} feed_eq_tab[] = {\n");
+ for (i = 0; i < length(allfreq); i++) {
+ printf("\t{ %6d, eq_%-6d },\n", allfreq[i], allfreq[i]);
+ }
+ printf("};\n");
+
+ printf("\n#define FEEDEQ_RATE_MIN\t\t%d\n", allfreq[0]);
+ printf("#define FEEDEQ_RATE_MAX\t\t%d\n", allfreq[length(allfreq) - 1]);
+ printf("\n#define FEEDEQ_TAB_SIZE\t\t\t\t\t\t\t\\\n");
+ printf("\t((int32_t)(sizeof(feed_eq_tab) / sizeof(feed_eq_tab[0])))\n");
+
+ printf("\nstatic const struct {\n");
+ printf("\tint32_t mul, shift;\n");
+ printf("} feed_eq_preamp[] = {\n");
+ for (i = (FEEDEQ_GAIN_MAX * 2 * FEEDEQ_GAIN_DIV); i >= 0; \
+ i -= FEEDEQ_GAIN_STEP) {
+ feedeq_calc_preamp(norm, i * FEEDEQ_GAIN_RECIPROCAL);
+ dbgain = ((FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV) - i) * \
+ FEEDEQ_GAIN_RECIPROCAL;
+ printf("\t{ 0x%08x, 0x%08x },\t/* %+5.1f dB */\n", \
+ norm["mul"], norm["shift"], dbgain);
+ }
+ printf("};\n");
+
+ printf("\n#define FEEDEQ_GAIN_MIN\t\t%d", FEEDEQ_GAIN_MIN);
+ printf("\n#define FEEDEQ_GAIN_MAX\t\t%d\n", FEEDEQ_GAIN_MAX);
+
+ printf("\n#define FEEDEQ_GAIN_SHIFT\t%d\n", FEEDEQ_GAIN_SHIFT);
+ printf("#define FEEDEQ_GAIN_DIV\t\t%d\n", FEEDEQ_GAIN_DIV);
+ printf("#define FEEDEQ_GAIN_FMASK\t0x%08x\n", FEEDEQ_GAIN_FMASK);
+ printf("#define FEEDEQ_GAIN_STEP\t%d\n", FEEDEQ_GAIN_STEP);
+
+ #printf("\n#define FEEDEQ_PREAMP_MIN\t-%d\n", \
+ # shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT));
+ #printf("#define FEEDEQ_PREAMP_MAX\t%d\n", \
+ # shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT));
+
+ printf("\n#define FEEDEQ_COEFF_SHIFT\t%d\n", FEEDEQ_COEFF_SHIFT);
+
+ #feedeq_calc_preamp(norm, FEEDEQ_GAIN_MAX);
+
+ #printf("#define FEEDEQ_COEFF_NORM(v)\t(");
+ #if (norm["mul"] == 1)
+ # printf("(v) >> %d", norm["shift"]);
+ #else
+ # printf("(0x%xLL * (v)) >> %d", norm["mul"], norm["shift"]);
+ #printf(")\n");
+
+ #printf("\n#define FEEDEQ_LEVELS\t\t%d\n", FEEDEQ_LEVELS);
+ if (FEEDEQ_ERR_CLIP != 0)
+ printf("\n#define FEEDEQ_ERR_CLIP\t\t%d\n", FEEDEQ_ERR_CLIP);
+ printf("\n/*\n");
+ printf(" * volume level mapping (0 - 100):\n");
+ printf(" *\n");
+
+ for (i = 0; i <= 100; i++) {
+ ind = floor((i * FEEDEQ_LEVELS) / 100);
+ if (ind >= FEEDEQ_LEVELS)
+ ind = FEEDEQ_LEVELS - 1;
+ printf(" *\t%3d -> %3d (%+5.1f dB)\n", \
+ i, ind, FEEDEQ_GAIN_MIN + \
+ (ind * (FEEDEQ_GAIN_RECIPROCAL * FEEDEQ_GAIN_STEP)));
+ }
+
+ printf(" */\n");
+ printf("\n/*\n * smallest: %.32f\n * largest: %.32f\n */\n", \
+ smallest, largest);
+ printf("\n#endif\t/* !_FEEDER_EQ_GEN_H_ */\n");
+}
diff --git a/sys/tools/feeder_rate_mkfilter.awk b/sys/tools/feeder_rate_mkfilter.awk
new file mode 100644
index 0000000..898c737
--- /dev/null
+++ b/sys/tools/feeder_rate_mkfilter.awk
@@ -0,0 +1,767 @@
+#!/usr/bin/awk -f
+#
+# Copyright (c) 2007-2009 Ariff Abdullah <ariff@FreeBSD.org>
+# 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.
+#
+# $FreeBSD$
+#
+
+#
+# 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".
+#
+# XXX EXPECT TOTAL REWRITE! DON'T ARGUE!
+#
+# TODO: Using ultraspherical window might be a good idea.
+#
+# Based on:
+#
+# "Digital Audio Resampling" by Julius O. Smith III
+#
+# - http://ccrma.stanford.edu/~jos/resample/
+#
+
+#
+# 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 ceil(x, r)
+{
+ r = int(x);
+ if (r < x)
+ r++;
+ return (r + 0);
+}
+
+function floor(x, r)
+{
+ r = int(x);
+ if (r > x)
+ 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 != 0) {
+ x = floor(x / 2);
+ y--;
+ }
+ return (x);
+}
+
+function fx_floor(v, o, r)
+{
+ if (fabs(v) < fabs(smallest))
+ smallest = v;
+ if (fabs(v) > fabs(largest))
+ largest = v;
+
+ r = floor((v * o) + 0.5);
+ if (r < INT32_MIN || r > INT32_MAX)
+ printf("\n#error overflow v=%f, please reduce %d\n", v, o);
+
+ return (r);
+}
+
+#
+# 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 tap_round(x, y)
+{
+ y = floor(x + 3);
+ y -= y % 4;
+ return (y);
+}
+
+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;
+
+ nm = 1.0 * (j - 1);
+
+ if (beta >= Z_WINDOW_KAISER)
+ ibeta = I0(beta);
+
+ for (i = 1; i < j; 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"] = j;
+ imp["rolloff"] = rolloff;
+}
+
+function mkfilter(imp, nmult, rolloff, beta, num, \
+ nwing, mwing, nrolloff, i, dcgain, v, quality)
+{
+ nwing = floor((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;
+
+ 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 + (Z_COEFF_OFFSET * 2));
+ for (i = 0; i < (nwing + (Z_COEFF_OFFSET * 2)); i++) {
+ if ((i % 5) == 0)
+ printf("\n ");
+ if (i < Z_COEFF_OFFSET)
+ v = fx_floor(imp[Z_COEFF_OFFSET - i], Z_COEFF_ONE);
+ else if ((i - Z_COEFF_OFFSET) >= nwing)
+ v = fx_floor( \
+ imp[nwing + nwing - i + Z_COEFF_OFFSET - 1],\
+ Z_COEFF_ONE);
+ else
+ v = fx_floor(imp[i - Z_COEFF_OFFSET], Z_COEFF_ONE);
+ printf(" %s0x%08x,", (v < 0) ? "-" : " ", abs(v));
+ }
+ printf("\n};\n\n");
+ printf("/*\n");
+ printf(" * interpolated 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 % 5) == 1)
+ printf("\n ");
+ v = -imp[i - 1];
+ if (i != nwing)
+ v += imp[i];
+ v = fx_floor(v, Z_INTERP_COEFF_ONE);
+ if (abs(v) > abs(largest_interp))
+ largest_interp = v;
+ printf(" %s0x%08x,", (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 = tap_round(a[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 = tap_round(a[i]);
+ 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"] = tap_round(a[i++]);
+ 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);
+}
+
+function genscale(bit, s1, s2, scale)
+{
+ s1 = Z_COEFF_SHIFT - (32 - bit);
+ s2 = Z_SHIFT + (32 - bit);
+
+ if (s1 == 0)
+ scale = "v";
+ else if (s1 < 0)
+ scale = sprintf("(v) << %d", abs(s1));
+ else
+ scale = sprintf("(v) >> %d", s1);
+
+ scale = sprintf("(%s) * Z_SCALE_CAST(s)", scale);
+
+ if (s2 != 0)
+ scale = sprintf("(%s) >> %d", scale, s2);
+
+ printf("#define Z_SCALE_%d(v, s)\t%s(%s)\n", \
+ bit, (bit < 10) ? "\t" : "", scale);
+}
+
+function genlerp(bit, use64, lerp)
+{
+ if ((bit + Z_LINEAR_SHIFT) <= 32) {
+ lerp = sprintf("(((y) - (x)) * (z)) >> %d", Z_LINEAR_SHIFT);
+ } else if (use64 != 0) {
+ if ((bit + Z_LINEAR_SHIFT) <= 64) {
+ lerp = sprintf( \
+ "(((int64_t)(y) - (x)) * (z)) " \
+ ">> %d", \
+ Z_LINEAR_SHIFT);
+ } else {
+ lerp = sprintf( \
+ "((int64_t)((y) >> %d) - ((x) >> %d)) * ", \
+ "(z)" \
+ bit + Z_LINEAR_SHIFT - 64, \
+ bit + Z_LINEAR_SHIFT - 64);
+ if ((64 - bit) != 0)
+ lerp = sprintf("(%s) >> %d", lerp, 64 - bit);
+ }
+ } else {
+ lerp = sprintf( \
+ "(((y) >> %d) - ((x) >> %d)) * (z)", \
+ bit + Z_LINEAR_SHIFT - 32, \
+ bit + Z_LINEAR_SHIFT - 32);
+ if ((32 - bit) != 0)
+ lerp = sprintf("(%s) >> %d", lerp, 32 - bit);
+ }
+
+ printf("#define Z_LINEAR_INTERPOLATE_%d(z, x, y)" \
+ "\t\t\t\t%s\\\n\t((x) + (%s))\n", \
+ bit, (bit < 10) ? "\t" : "", lerp);
+}
+
+BEGIN {
+ I0_EPSILON = 1e-21;
+ M_PI = atan2(0.0, -1.0);
+
+ INT32_MAX = 1 + ((shl(1, 30) - 1) * 2);
+ INT32_MIN = -1 - INT32_MAX;
+
+ Z_COEFF_OFFSET = 5;
+
+ Z_FULL_SHIFT = 30;
+ Z_FULL_ONE = shl(1, Z_FULL_SHIFT);
+
+ Z_COEFF_SHIFT = 28;
+ Z_COEFF_ONE = shl(1, Z_COEFF_SHIFT);
+
+ Z_INTERP_COEFF_SHIFT = 24;
+ Z_INTERP_COEFF_ONE = shl(1, Z_INTERP_COEFF_SHIFT);
+
+ #
+ # Filter oversampling factor.
+ #
+ # 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_LINEAR_FULL_SHIFT = Z_FULL_SHIFT;
+ Z_LINEAR_FULL_ONE = shl(1, Z_LINEAR_FULL_SHIFT);
+ Z_LINEAR_SHIFT = 8;
+ Z_LINEAR_UNSHIFT = Z_LINEAR_FULL_SHIFT - Z_LINEAR_SHIFT;
+ Z_LINEAR_ONE = shl(1, Z_LINEAR_SHIFT)
+
+ # meehhhh... let it overflow...
+ #Z_SCALE_SHIFT = 31;
+ #Z_SCALE_ONE = shl(1, Z_SCALE_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;
+
+ smallest = 10.000000;
+ largest = 0.000010;
+ largest_interp = 0;
+
+ if (ARGC < 2) {
+ ARGC = 1;
+ ARGV[ARGC++] = "100:8:0.85";
+ ARGV[ARGC++] = "100:36:0.90";
+ ARGV[ARGC++] = "100:164:0.97";
+ #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 _FEEDER_RATE_GEN_H_\n");
+ printf("#define _FEEDER_RATE_GEN_H_\n\n");
+ printf("/*\n");
+ printf(" * Generated using feeder_rate_mkfilter.awk, heaven, wind and awesome.\n");
+ printf(" *\n");
+ printf(" * DO NOT EDIT!\n");
+ printf(" */\n\n");
+ printf("#define FEEDER_RATE_PRESETS\t\"");
+ for (i = 1; i < ARGC; i++)
+ printf("%s%s", (i == 1) ? "" : " ", ARGV[i]);
+ 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] = \
+ mkfilter(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");
+
+ #Z_UNSHIFT = 0;
+ #v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
+ #while (v < 0 || v > INT32_MAX) {
+ # Z_UNSHIFT += 1;
+ # v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
+ #}
+ v = ((Z_ONE - 1) * abs(largest_interp)) / INT32_MAX;
+ Z_UNSHIFT = ceil(log(v) / log(2.0));
+ Z_INTERP_SHIFT = Z_SHIFT - Z_UNSHIFT + Z_INTERP_COEFF_SHIFT;
+
+ Z_INTERP_UNSHIFT = (Z_SHIFT - Z_UNSHIFT) + Z_INTERP_COEFF_SHIFT \
+ - Z_COEFF_SHIFT;
+
+ 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_COEFF_OFFSET\t\t%d\n\n", Z_COEFF_OFFSET);
+ printf("#define Z_RSHIFT(x, y)\t\t(((x) + " \
+ "(1 << ((y) - 1))) >> (y))\n");
+ printf("#define Z_RSHIFT_L(x, y)\t(((x) + " \
+ "(1LL << ((y) - 1))) >> (y))\n\n");
+ printf("#define Z_FULL_SHIFT\t\t%d\n", Z_FULL_SHIFT);
+ printf("#define Z_FULL_ONE\t\t0x%08x%s\n", Z_FULL_ONE, \
+ (Z_FULL_ONE > INT32_MAX) ? "U" : "");
+ printf("\n");
+ printf("#define Z_DRIFT_SHIFT\t\t%d\n", Z_DRIFT_SHIFT);
+ #printf("#define Z_DRIFT_ONE\t\t0x%08x\n", Z_DRIFT_ONE);
+ printf("\n");
+ printf("#define Z_SHIFT\t\t\t%d\n", Z_SHIFT);
+ printf("#define Z_ONE\t\t\t0x%08x\n", Z_ONE);
+ printf("#define Z_MASK\t\t\t0x%08x\n", Z_MASK);
+ printf("\n");
+ printf("#define Z_COEFF_SHIFT\t\t%d\n", Z_COEFF_SHIFT);
+ zinterphp = "(z) * (d)";
+ zinterpunshift = Z_SHIFT + Z_INTERP_COEFF_SHIFT - Z_COEFF_SHIFT;
+ if (zinterpunshift > 0) {
+ v = (Z_ONE - 1) * abs(largest_interp);
+ if (v < INT32_MIN || v > INT32_MAX)
+ zinterphp = sprintf("(int64_t)%s", zinterphp);
+ zinterphp = sprintf("(%s) >> %d", zinterphp, zinterpunshift);
+ } else if (zinterpunshift < 0)
+ zinterphp = sprintf("(%s) << %d", zinterphp, \
+ abs(zinterpunshift));
+ if (Z_UNSHIFT == 0)
+ zinterp = "z";
+ else
+ zinterp = sprintf("(z) >> %d", Z_UNSHIFT);
+ zinterp = sprintf("(%s) * (d)", zinterp);
+ if (Z_INTERP_UNSHIFT < 0)
+ zinterp = sprintf("(%s) << %d", zinterp, \
+ abs(Z_INTERP_UNSHIFT));
+ else if (Z_INTERP_UNSHIFT > 0)
+ zinterp = sprintf("(%s) >> %d", zinterp, Z_INTERP_UNSHIFT);
+ if (zinterphp != zinterp) {
+ printf("\n#ifdef SND_FEEDER_RATE_HP\n");
+ printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \
+ "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterphp);
+ printf("#else\n");
+ printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \
+ "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp);
+ printf("#endif\n");
+ } else
+ printf("#define Z_COEFF_INTERPOLATE(z, c, d)" \
+ "\t\t\t\t\t\\\n\t((c) + (%s))\n", zinterp);
+ #printf("\n");
+ #printf("#define Z_SCALE_SHIFT\t\t%d\n", Z_SCALE_SHIFT);
+ #printf("#define Z_SCALE_ONE\t\t0x%08x%s\n", Z_SCALE_ONE, \
+ # (Z_SCALE_ONE > INT32_MAX) ? "U" : "");
+ printf("\n");
+ printf("#define Z_SCALE_CAST(s)\t\t((uint32_t)(s))\n");
+ genscale(8);
+ genscale(16);
+ genscale(24);
+ genscale(32);
+ printf("\n");
+ printf("#define Z_LINEAR_FULL_ONE\t0x%08xU\n", Z_LINEAR_FULL_ONE);
+ printf("#define Z_LINEAR_SHIFT\t\t%d\n", Z_LINEAR_SHIFT);
+ printf("#define Z_LINEAR_UNSHIFT\t%d\n", Z_LINEAR_UNSHIFT);
+ printf("#define Z_LINEAR_ONE\t\t0x%08x\n", Z_LINEAR_ONE);
+ printf("\n");
+ printf("#ifdef SND_PCM_64\n");
+ genlerp(8, 1);
+ genlerp(16, 1);
+ genlerp(24, 1);
+ genlerp(32, 1);
+ printf("#else\t/* !SND_PCM_64 */\n");
+ genlerp(8, 0);
+ genlerp(16, 0);
+ genlerp(24, 0);
+ genlerp(32, 0);
+ printf("#endif\t/* SND_PCM_64 */\n");
+ 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", \
+ floor((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/*\n * smallest: %.32f\n * largest: %.32f\n *\n", \
+ smallest, largest);
+ printf(" * z_unshift=%d, z_interp_shift=%d\n *\n", \
+ Z_UNSHIFT, Z_INTERP_SHIFT);
+ v = shr(Z_ONE - 1, Z_UNSHIFT) * abs(largest_interp);
+ printf(" * largest interpolation multiplication: %d\n */\n", v);
+ if (v < INT32_MIN || v > INT32_MAX) {
+ printf("\n#ifndef SND_FEEDER_RATE_HP\n");
+ printf("#error interpolation overflow, please reduce" \
+ " Z_INTERP_SHIFT\n");
+ printf("#endif\n");
+ }
+
+ printf("\n#endif /* !_FEEDER_RATE_GEN_H_ */\n");
+}
diff --git a/sys/tools/snd_fxdiv_gen.awk b/sys/tools/snd_fxdiv_gen.awk
new file mode 100644
index 0000000..5828062
--- /dev/null
+++ b/sys/tools/snd_fxdiv_gen.awk
@@ -0,0 +1,142 @@
+#!/usr/bin/awk -f
+#
+# Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
+# 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.
+#
+# $FreeBSD$
+#
+
+function floor(x, r)
+{
+ r = int(x);
+ if (r > x)
+ r--;
+ return (r + 0);
+}
+
+function shl(x, y)
+{
+ while (y > 0) {
+ x *= 2;
+ y--;
+ }
+ return (x);
+}
+
+function shr(x, y)
+{
+ while (y > 0 && x != 0) {
+ x = floor(x / 2);
+ y--;
+ }
+ return (x);
+}
+
+function calcdiv(r, x, y, z)
+{
+ y = floor(FXONE / x);
+ z = FXSHIFT;
+
+ while (shr((y * x), z) < 1)
+ y++;
+
+ while ((y % 2) == 0 && z > 0) {
+ y = floor(y / 2);
+ z--;
+ }
+
+ r["mul"] = y;
+ r["shift"] = z;
+}
+
+BEGIN {
+ FXSHIFT = 16;
+ FXONE = shl(1, FXSHIFT);
+
+ SND_CHN_MAX = 18;
+
+ PCM_8_BPS = 1;
+ PCM_16_BPS = 2;
+ PCM_24_BPS = 3;
+ PCM_32_BPS = 4;
+
+ SND_MAX_ALIGN = SND_CHN_MAX * PCM_32_BPS;
+
+ for (i = 1; i <= SND_CHN_MAX; i++) {
+ aligns[PCM_8_BPS * i] = 1;
+ aligns[PCM_16_BPS * i] = 1;
+ aligns[PCM_24_BPS * i] = 1;
+ aligns[PCM_32_BPS * i] = 1;
+ }
+
+ printf("#ifndef _SND_FXDIV_GEN_H_\n");
+ printf("#define _SND_FXDIV_GEN_H_\n\n");
+
+ printf("/*\n");
+ printf(" * Generated using snd_fxdiv_gen.awk, heaven, wind and awesome.\n");
+ printf(" *\n");
+ printf(" * DO NOT EDIT!\n");
+ printf(" */\n\n");
+ printf("#ifdef SND_USE_FXDIV\n\n");
+
+ printf("/*\n");
+ printf(" * Fast unsigned 32bit integer division and rounding, accurate for\n");
+ printf(" * x = 1 - %d. This table should be enough to handle possible\n", FXONE);
+ printf(" * division for 1 - 72 (more can be generated though..).\n");
+ printf(" *\n");
+ printf(" * 72 = SND_CHN_MAX * PCM_32_BPS, which is why....\n");
+ printf(" */\n\n");
+
+ printf("static const uint32_t snd_fxdiv_table[][2] = {\n");
+
+ for (i = 1; i <= SND_MAX_ALIGN; i++) {
+ if (aligns[i] != 1)
+ continue;
+ calcdiv(r, i);
+ printf("\t[0x%02x] = { 0x%04x, 0x%02x },", \
+ i, r["mul"], r["shift"]);
+ printf("\t/* x / %-2d = (x * %-5d) >> %-2d */\n", \
+ i, r["mul"], r["shift"]);
+ }
+
+ printf("};\n\n");
+
+ printf("#define SND_FXDIV_MAX\t\t0x%08x\n", FXONE);
+ printf("#define SND_FXDIV(x, y)\t\t(((uint32_t)(x) *\t\t\t\\\n");
+ printf("\t\t\t\t snd_fxdiv_table[y][0]) >>\t\t\\\n");
+ printf("\t\t\t\t snd_fxdiv_table[y][1])\n");
+ printf("#define SND_FXROUND(x, y)\t(SND_FXDIV(x, y) * (y))\n");
+ printf("#define SND_FXMOD(x, y)\t\t((x) - SND_FXROUND(x, y))\n\n");
+
+ printf("#else\t/* !SND_USE_FXDIV */\n\n");
+
+ printf("#define SND_FXDIV_MAX\t\t0x%08x\n", 131072);
+ printf("#define SND_FXDIV(x, y)\t\t((x) / (y))\n");
+ printf("#define SND_FXROUND(x, y)\t((x) - ((x) %% (y)))\n");
+ printf("#define SND_FXMOD(x, y)\t\t((x) %% (y))\n\n");
+
+ printf("#endif\t/* SND_USE_FXDIV */\n\n");
+
+ printf("#endif\t/* !_SND_FXDIV_GEN_H_ */\n");
+}
OpenPOWER on IntegriCloud