diff options
author | ariff <ariff@FreeBSD.org> | 2009-06-07 19:12:08 +0000 |
---|---|---|
committer | ariff <ariff@FreeBSD.org> | 2009-06-07 19:12:08 +0000 |
commit | e3faadaafebd1b18fd6b8ed30f654df974d390a6 (patch) | |
tree | 906f402638735c3f32e226f6868f207db569d6a9 /sys/tools | |
parent | f482e4a9e0f02a82e107e2bbee139c6a0b48be80 (diff) | |
download | FreeBSD-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.awk | 467 | ||||
-rw-r--r-- | sys/tools/feeder_rate_mkfilter.awk | 767 | ||||
-rw-r--r-- | sys/tools/snd_fxdiv_gen.awk | 142 |
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"); +} |