--- sys/conf/files.orig 2008-03-20 13:51:15.000000000 +0800 +++ sys/conf/files 2008-04-07 21:28:27.000000000 +0800 @@ -65,6 +65,21 @@ compile-with "CC='${CC}' AWK=${AWK} sh $S/tools/emu10k1-mkalsa.sh $S/gnu/dev/sound/pci/p17v-alsa.h p17v-alsa%diked.h" \ no-obj no-implicit-rule before-depend \ clean "p17v-alsa%diked.h" +feeder_eq_gen.h optional sound \ + dependency "$S/tools/feeder_eq_mkfilter.awk" \ + compile-with "${AWK} -f $S/tools/feeder_eq_mkfilter.awk -- ${FEEDER_EQ_PRESETS} > feeder_eq_gen.h" \ + no-obj no-implicit-rule before-depend \ + clean "feeder_eq_gen.h" +feeder_rate_gen.h optional sound \ + dependency "$S/tools/feeder_rate_mkfilter.awk" \ + compile-with "${AWK} -f $S/tools/feeder_rate_mkfilter.awk -- ${FEEDER_RATE_PRESETS} > feeder_rate_gen.h" \ + no-obj no-implicit-rule before-depend \ + clean "feeder_rate_gen.h" +snd_fxdiv_gen.h optional sound \ + dependency "$S/tools/snd_fxdiv_gen.awk" \ + compile-with "${AWK} -f $S/tools/snd_fxdiv_gen.awk -- > snd_fxdiv_gen.h" \ + no-obj no-implicit-rule before-depend \ + clean "snd_fxdiv_gen.h" miidevs.h optional miibus | mii \ dependency "$S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ compile-with "${AWK} -f $S/tools/miidevs2h.awk $S/dev/mii/miidevs" \ @@ -1384,10 +1399,22 @@ dev/sound/pcm/dsp.c optional sound dev/sound/pcm/fake.c optional sound dev/sound/pcm/feeder.c optional sound -dev/sound/pcm/feeder_fmt.c optional sound +dev/sound/pcm/feeder_chain.c optional sound +dev/sound/pcm/feeder_eq.c optional sound \ + dependency "feeder_eq_gen.h" \ + dependency "snd_fxdiv_gen.h" dev/sound/pcm/feeder_if.m optional sound -dev/sound/pcm/feeder_rate.c optional sound -dev/sound/pcm/feeder_volume.c optional sound +dev/sound/pcm/feeder_format.c optional sound \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_matrix.c optional sound \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_mixer.c optional sound \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_rate.c optional sound \ + dependency "feeder_rate_gen.h" \ + dependency "snd_fxdiv_gen.h" +dev/sound/pcm/feeder_volume.c optional sound \ + dependency "snd_fxdiv_gen.h" dev/sound/pcm/mixer.c optional sound dev/sound/pcm/mixer_if.m optional sound dev/sound/pcm/sndstat.c optional sound --- sys/tools/feeder_eq_mkfilter.awk.orig 1970-01-01 07:30:00.000000000 +07302009-01-17 19:33:00.000000000 +0800 +++ sys/tools/feeder_eq_mkfilter.awk 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,463 @@ +#!/usr/bin/awk -f +# +# Copyright (c) 2008 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. +# +# $FreeBSD$ +# + +# +# Based on +# +# "Cookbook formulae for audio EQ biquad filter coefficients" +# by Robert Bristow-Johnson +# +# - 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_iir_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_iir_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 * Settings:\n"); + printf(" * %d,%.4f,%d,%.4f:%d,%d,%.1f:", \ + 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"); + 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"); +} --- sys/tools/feeder_rate_mkfilter.awk.orig 1970-01-01 07:30:00.000000000 +07302009-01-17 19:33:00.000000000 +0800 +++ sys/tools/feeder_rate_mkfilter.awk 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,753 @@ +#!/usr/bin/awk -f +# +# Copyright (c) 2007-2008 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 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 makeFilter(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"); + 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"); + + #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 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 PCM_USE_64BIT_ARITH\n"); + genlerp(8, 1); + genlerp(16, 1); + genlerp(24, 1); + genlerp(32, 1); + printf("#else\t/* !PCM_USE_64BIT_ARITH */\n"); + genlerp(8, 0); + genlerp(16, 0); + genlerp(24, 0); + genlerp(32, 0); + printf("#endif\t/* PCM_USE_64BIT_ARITH */\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 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"); +} --- sys/tools/snd_fxdiv_gen.awk.orig 1970-01-01 07:30:00.000000000 +07302009-01-17 19:33:00.000000000 +0800 +++ sys/tools/snd_fxdiv_gen.awk 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,142 @@ +#!/usr/bin/awk -f +# +# Copyright (c) 2008 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. +# +# $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"); +} --- sys/sys/soundcard.h.orig 2009-01-17 19:31:45.000000000 +0800 +++ sys/sys/soundcard.h 2009-01-17 19:29:20.000000000 +0800 @@ -1665,7 +1665,8 @@ #define SNDCTL_DSP_GET_CHNORDER _IOR ('P', 42, unsigned long long) #define SNDCTL_DSP_SET_CHNORDER _IOWR('P', 42, unsigned long long) # define CHID_UNDEF 0 -# define CHID_L 1 # define CHID_R 2 +# define CHID_L 1 +# define CHID_R 2 # define CHID_C 3 # define CHID_LFE 4 # define CHID_LS 5 @@ -1682,6 +1683,25 @@ #define SNDCTL_DSP_POLICY _IOW('P', 45, int) /* See the manual */ /* + **************************************************************************** + * Few ioctl calls that are not official parts of OSS. They have been used + * by few freeware implementations of OSS. + */ +#define SNDCTL_DSP_GETCHANNELMASK _IOWR('P', 64, int) +#define SNDCTL_DSP_BIND_CHANNEL _IOWR('P', 65, int) +#define DSP_BIND_QUERY 0x00000000 +#define DSP_BIND_FRONT 0x00000001 +#define DSP_BIND_SURR 0x00000002 +#define DSP_BIND_CENTER_LFE 0x00000004 +#define DSP_BIND_HANDSET 0x00000008 +#define DSP_BIND_MIC 0x00000010 +#define DSP_BIND_MODEM1 0x00000020 +#define DSP_BIND_MODEM2 0x00000040 +#define DSP_BIND_I2S 0x00000080 +#define DSP_BIND_SPDIF 0x00000100 +#define DSP_BIND_REAR 0x00000200 + +/* * OSS_SYSIFO is obsolete. Use SNDCTL_SYSINFO insteads. */ #define OSS_GETVERSION _IOR ('M', 118, int) --- sys/dev/sound/isa/ad1816.c.orig 2007-10-12 14:03:44.000000000 +0800 +++ sys/dev/sound/isa/ad1816.c 2009-01-17 19:29:20.000000000 +0800 @@ -62,14 +62,14 @@ }; static u_int32_t ad1816_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_MU_LAW, - AFMT_STEREO | AFMT_MU_LAW, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_MU_LAW, 1, 0), + SND_FORMAT(AFMT_MU_LAW, 2, 0), + SND_FORMAT(AFMT_A_LAW, 1, 0), + SND_FORMAT(AFMT_A_LAW, 2, 0), 0 }; @@ -315,23 +315,19 @@ struct ad1816_info *ad1816 = devinfo; struct ad1816_chinfo *ch = (dir == PCMDIR_PLAY)? &ad1816->pch : &ad1816->rch; + ch->dir = dir; ch->parent = ad1816; ch->channel = c; ch->buffer = b; if (sndbuf_alloc(ch->buffer, ad1816->parent_dmat, 0, ad1816->bufsize) != 0) return NULL; - return ch; -} -static int -ad1816chan_setdir(kobj_t obj, void *data, int dir) -{ - struct ad1816_chinfo *ch = data; - struct ad1816_info *ad1816 = ch->parent; + sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY) ? ad1816->drq1 : + ad1816->drq2); + if (SND_DMA(ch->buffer)) + sndbuf_dmasetdir(ch->buffer, dir); - sndbuf_dmasetup(ch->buffer, (dir == PCMDIR_PLAY)? ad1816->drq1 : ad1816->drq2); - ch->dir = dir; - return 0; + return ch; } static int @@ -351,7 +347,7 @@ ad1816_write(ad1816, 10, 0x0000); ad1816_write(ad1816, 11, 0x0000); } - switch (format & ~AFMT_STEREO) { + switch (AFMT_ENCODING(format)) { case AFMT_A_LAW: fmt = AD1816_ALAW; break; @@ -372,7 +368,7 @@ fmt = AD1816_U8; break; } - if (format & AFMT_STEREO) fmt |= AD1816_STEREO; + if (AFMT_CHANNEL(format) > 1) fmt |= AD1816_STEREO; io_wr(ad1816, reg, fmt); ad1816_unlock(ad1816); #if 0 @@ -471,7 +467,6 @@ static kobj_method_t ad1816chan_methods[] = { KOBJMETHOD(channel_init, ad1816chan_init), - KOBJMETHOD(channel_setdir, ad1816chan_setdir), KOBJMETHOD(channel_setformat, ad1816chan_setformat), KOBJMETHOD(channel_setspeed, ad1816chan_setspeed), KOBJMETHOD(channel_setblocksize, ad1816chan_setblocksize), --- sys/dev/sound/isa/ess.c.orig 2007-06-17 14:10:40.000000000 +0800 +++ sys/dev/sound/isa/ess.c 2009-01-17 19:29:20.000000000 +0800 @@ -50,28 +50,28 @@ #define ESS18XX_NEWSPEED static u_int32_t ess_pfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_pfmt, 0}; static u_int32_t ess_rfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; @@ -458,7 +458,7 @@ { int play = (dir == PCMDIR_PLAY)? 1 : 0; int b16 = (fmt & AFMT_16BIT)? 1 : 0; - int stereo = (fmt & AFMT_STEREO)? 1 : 0; + int stereo = (AFMT_CHANNEL(fmt) > 1)? 1 : 0; int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE)? 1 : 0; u_int8_t spdval, fmtval; --- sys/dev/sound/isa/mss.c.orig 2007-06-17 14:10:40.000000000 +0800 +++ sys/dev/sound/isa/mss.c 2009-01-17 19:29:20.000000000 +0800 @@ -128,34 +128,34 @@ static driver_intr_t opti931_intr; static u_int32_t mss_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_MU_LAW, - AFMT_STEREO | AFMT_MU_LAW, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_MU_LAW, 1, 0), + SND_FORMAT(AFMT_MU_LAW, 2, 0), + SND_FORMAT(AFMT_A_LAW, 1, 0), + SND_FORMAT(AFMT_A_LAW, 2, 0), 0 }; static struct pcmchan_caps mss_caps = {4000, 48000, mss_fmt, 0}; static u_int32_t guspnp_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_A_LAW, 1, 0), + SND_FORMAT(AFMT_A_LAW, 2, 0), 0 }; static struct pcmchan_caps guspnp_caps = {4000, 48000, guspnp_fmt, 0}; static u_int32_t opti931_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps opti931_caps = {4000, 48000, opti931_fmt, 0}; @@ -997,7 +997,7 @@ mss_format(struct mss_chinfo *ch, u_int32_t format) { struct mss_info *mss = ch->parent; - int i, arg = format & ~AFMT_STEREO; + int i, arg = AFMT_ENCODING(format); /* * The data format uses 3 bits (just 2 on the 1848). For each @@ -1014,7 +1014,7 @@ ch->fmt = format; for (i = 0; i < 8; i++) if (arg == fmts[i]) break; arg = i << 1; - if (format & AFMT_STEREO) arg |= 1; + if (AFMT_CHANNEL(format) > 1) arg |= 1; arg <<= 4; ad_enter_MCE(mss); ad_write(mss, 8, (ad_read(mss, 8) & 0x0f) | arg); @@ -1035,7 +1035,7 @@ int retry, wr, cnt, ss; ss = 1; - ss <<= (ch->fmt & AFMT_STEREO)? 1 : 0; + ss <<= (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; ss <<= (ch->fmt & AFMT_16BIT)? 1 : 0; wr = (ch->dir == PCMDIR_PLAY)? 1 : 0; --- sys/dev/sound/isa/sb16.c.orig 2007-06-17 14:10:40.000000000 +0800 +++ sys/dev/sound/isa/sb16.c 2009-01-17 19:29:20.000000000 +0800 @@ -44,24 +44,24 @@ #define PLAIN_SB16(x) ((((x)->bd_flags) & (BD_F_SB16|BD_F_SB16X)) == BD_F_SB16) static u_int32_t sb16_fmt8[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), 0 }; static struct pcmchan_caps sb16_caps8 = {5000, 45000, sb16_fmt8, 0}; static u_int32_t sb16_fmt16[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps sb16_caps16 = {5000, 45000, sb16_fmt16, 0}; static u_int32_t sb16x_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps sb16x_caps = {5000, 49000, sb16x_fmt, 0}; @@ -633,7 +633,7 @@ v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8; sb_cmd(sb, v); - v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0; + v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0; v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; sb_cmd2(sb, v, l); sndbuf_dma(ch->buffer, PCMTRIG_START); @@ -658,7 +658,7 @@ v |= (ch->fmt & AFMT_16BIT)? DSP_DMA16 : DSP_DMA8; sb_cmd(sb, v); - v = (ch->fmt & AFMT_STEREO)? DSP_F16_STEREO : 0; + v = (AFMT_CHANNEL(ch->fmt) > 1)? DSP_F16_STEREO : 0; v |= (ch->fmt & AFMT_SIGNED)? DSP_F16_SIGNED : 0; sb_cmd2(sb, v, l); sndbuf_dma(ch->buffer, PCMTRIG_START); --- sys/dev/sound/isa/sb8.c.orig 2007-06-17 14:10:41.000000000 +0800 +++ sys/dev/sound/isa/sb8.c 2009-01-17 19:29:20.000000000 +0800 @@ -43,7 +43,7 @@ #define SB_DEFAULT_BUFSZ 4096 static u_int32_t sb_fmt[] = { - AFMT_U8, + SND_FORMAT(AFMT_U8, 1, 0), 0 }; static struct pcmchan_caps sb200_playcaps = {4000, 23000, sb_fmt, 0}; @@ -52,8 +52,8 @@ static struct pcmchan_caps sb201_reccaps = {4000, 15000, sb_fmt, 0}; static u_int32_t sbpro_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), 0 }; static struct pcmchan_caps sbpro_playcaps = {4000, 44100, sbpro_fmt, 0}; @@ -496,7 +496,7 @@ { struct sb_info *sb = ch->parent; int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; - int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + int stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; int speed, tmp, thresh, max; u_char tconst; @@ -537,7 +537,7 @@ { struct sb_info *sb = ch->parent; int play = (ch->dir == PCMDIR_PLAY)? 1 : 0; - int stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + int stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; int l = ch->blksz; u_char i; --- sys/dev/sound/lpmap.c.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/lpmap.c 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,312 @@ +/*- + * 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. + * + * $FreeBSD$ + * + * "Little-pmap": Lost Technology for RELENG_5/RELENG_6. + * + */ + +#ifndef _LPMAP_C_ +#define _LPMAP_C_ + +#if !(defined(__i386__) || defined(__amd64__)) +#error "Not applicable for non-i386/non-amd64" +#endif + +/* For cpu_feature, VM_MAXUSER_ADDRESS */ +#include +#include +#include + +#ifndef PG_PTE_PAT +#define PG_PTE_PAT 0x0080 +#endif + +#ifndef PG_PDE_PAT +#define PG_PDE_PAT 0x1000 +#endif + +#ifndef PAT_UNCACHEABLE +#define PAT_UNCACHEABLE 0x00 +#endif + +#ifndef PAT_WRITE_COMBINING +#define PAT_WRITE_COMBINING 0x01 +#endif + +#ifndef PAT_WRITE_THROUGH +#define PAT_WRITE_THROUGH 0x04 +#endif + +#ifndef PAT_WRITE_PROTECTED +#define PAT_WRITE_PROTECTED 0x05 +#endif + +#ifndef PAT_WRITE_BACK +#define PAT_WRITE_BACK 0x06 +#endif + +#ifndef PAT_UNCACHED +#define PAT_UNCACHED 0x07 +#endif + +#define lpmap_p(x, y...) /*printf("%s(): "x, __func__, y)*/ + +#undef pmap_change_attr +#define pmap_change_attr lpmap_change_attr + +#if defined(__i386__) +#define lpmap_pde(m, v) (&((m)->pm_pdir[(vm_offset_t)(v) >> PDRSHIFT])) +#elif defined(__amd64__) +static __inline vm_pindex_t +lpmap_pde_index(vm_offset_t va) +{ + return ((va >> PDRSHIFT) & ((1UL << NPDEPGSHIFT) - 1)); +} + +static __inline vm_pindex_t +lpmap_pdpe_index(vm_offset_t va) +{ + return ((va >> PDPSHIFT) & ((1UL << NPDPEPGSHIFT) - 1)); +} + +static __inline vm_pindex_t +lpmap_pml4e_index(vm_offset_t va) +{ + return ((va >> PML4SHIFT) & ((1UL << NPML4EPGSHIFT) - 1)); +} + +static __inline pd_entry_t * +lpmap_pdpe_to_pde(pdp_entry_t *pdpe, vm_offset_t va) +{ + pd_entry_t *pde; + + pde = (pd_entry_t *)PHYS_TO_DMAP(*pdpe & PG_FRAME); + return (&pde[lpmap_pde_index(va)]); +} + +static __inline pml4_entry_t * +lpmap_pml4e(pmap_t pmap, vm_offset_t va) +{ + if (pmap == NULL) + return (NULL); + return (&pmap->pm_pml4[lpmap_pml4e_index(va)]); +} + +static __inline pdp_entry_t * +lpmap_pml4e_to_pdpe(pml4_entry_t *pml4e, vm_offset_t va) +{ + pdp_entry_t *pdpe; + + pdpe = (pdp_entry_t *)PHYS_TO_DMAP(*pml4e & PG_FRAME); + return (&pdpe[lpmap_pdpe_index(va)]); +} + +static __inline pdp_entry_t * +lpmap_pdpe(pmap_t pmap, vm_offset_t va) +{ + pml4_entry_t *pml4e; + + pml4e = lpmap_pml4e(pmap, va); + if (pml4e == NULL || (*pml4e & PG_V) == 0) + return (NULL); + return (lpmap_pml4e_to_pdpe(pml4e, va)); +} + +static __inline pd_entry_t * +lpmap_pde(pmap_t pmap, vm_offset_t va) +{ + pdp_entry_t *pdpe; + + pdpe = lpmap_pdpe(pmap, va); + if (pdpe == NULL || (*pdpe & PG_V) == 0) + return (NULL); + return (lpmap_pdpe_to_pde(pdpe, va)); +} +#endif + +static __inline int +lpmap_validate_range(vm_offset_t base, vm_size_t size) +{ + vm_offset_t va; + pt_entry_t *pte; + pd_entry_t *pde; + + if (base <= VM_MAXUSER_ADDRESS) { + lpmap_p("base <= VM_MAXUSER_ADDRESS : base=0x%jx\n", + (uintmax_t)base); + return (EINVAL); + } + + va = base; + while (va < (base + size)) { + pde = lpmap_pde(kernel_pmap, va); + if (pde == NULL || *pde == 0) { + lpmap_p("Failed: %s : va=0x%jx\n", + (pde == NULL) ? "pde == NULL" : "*pde == 0", + (uintmax_t)va); + return (EINVAL); + } + if (*pde & PG_PS) { +#if defined(__amd64__) + if (size < NBPDR) { + lpmap_p("Failed: size < NBPDR : va=0x%jx\n", + (uintmax_t)va); + return (EINVAL); + } + va += NBPDR; +#else + lpmap_p("Failed: (*pde & PG_PS) != 0 : va=0x%jx\n", + (uintmax_t)va); + return (EINVAL); +#endif + } else { + pte = vtopte(va); + if (pte == NULL || *pte == 0) { + lpmap_p("Failed: %s : va=0x%jx\n", + (pte == NULL) ? "pte == NULL" : "*pte == 0", + (uintmax_t)va); + return (EINVAL); + } + va += PAGE_SIZE; + } + } + + return (0); +} + +#if (__FreeBSD_version >= 800000 && __FreeBSD_version < 800005) || \ + __FreeBSD_version < 700055 +extern int osreldate; +#else +#include +#endif + +static int +lpmap_change_attr(vm_offset_t va, vm_size_t size, int mode) +{ + vm_offset_t base, offset; + pt_entry_t *pte; +#if defined(__amd64__) + pd_entry_t *pde; +#endif + u_int opxe, npxe, ptefl, pdefl, attr; + int err, flushtlb; + + switch (mode) { + case PAT_UNCACHED: + case PAT_WRITE_COMBINING: + case PAT_WRITE_PROTECTED: + if (!(cpu_feature & CPUID_PAT)) + mode = PAT_UNCACHEABLE; + break; + default: + break; + } + + attr = 0; + + switch (mode) { + case PAT_UNCACHED: + case PAT_UNCACHEABLE: + case PAT_WRITE_PROTECTED: + attr |= PG_NC_PCD; + case PAT_WRITE_THROUGH: + attr |= PG_NC_PWT; + break; + case PAT_WRITE_COMBINING: + attr |= PG_NC_PCD; + break; + case PAT_WRITE_BACK: + break; + default: + lpmap_p("Unsupported mode=0x%08x\n", mode); + return (EINVAL); + break; + } + + ptefl = PG_NC_PCD | PG_NC_PWT; + pdefl = PG_NC_PCD | PG_NC_PWT; + if (osreldate >= 602110) { + ptefl |= PG_PTE_PAT; + pdefl |= PG_PDE_PAT; + } + + base = va & PG_FRAME; + offset = va & PAGE_MASK; + size = roundup(offset + size, PAGE_SIZE); + + err = lpmap_validate_range(base, size); + if (err != 0) { + lpmap_p("Validation failed! " + "vm_offset_t=0x%jx vm_size_t=%ju attr=0x%04x\n", + (uintmax_t)va, (uintmax_t)size, attr); + return (err); + } + + lpmap_p("vm_offset_t=0x%jx vm_size_t=%ju attr=0x%04x\n", + (uintmax_t)va, (uintmax_t)size, attr); + + flushtlb = 0; + + while (size > 0) { +#if defined(__amd64__) + pde = lpmap_pde(kernel_pmap, base); + if (*pde & PG_PS) { + do { + opxe = *(u_int *)pde; + npxe = opxe & ~pdefl; + npxe |= attr; + } while (npxe != opxe && ++flushtlb && + !atomic_cmpset_int((u_int *)pde, opxe, npxe)); + size -= NBPDR; + base += NBPDR; + } else { +#endif + pte = vtopte(base); + do { + opxe = *(u_int *)pte; + npxe = opxe & ~ptefl; + npxe |= attr; + } while (npxe != opxe && ++flushtlb && + !atomic_cmpset_int((u_int *)pte, opxe, npxe)); + size -= PAGE_SIZE; + base += PAGE_SIZE; +#if defined(__amd64__) + } +#endif + } + + /* XXX Gross!! */ + if (flushtlb != 0) { + lpmap_p("flushtlb=%d\n", flushtlb); + pmap_invalidate_all(kernel_pmap); + } + + return (0); +} + +#endif /* !_LPMAP_C_ */ --- sys/dev/sound/midi/sequencer.c.orig 2008-11-06 23:05:14.000000000 +0800 +++ sys/dev/sound/midi/sequencer.c 2009-01-17 19:29:20.000000000 +0800 @@ -459,7 +459,12 @@ cv_broadcast(&scp->th_cv); mtx_unlock(&scp->seq_lock); SEQ_DEBUG(2, printf("seq_eventthread finished\n")); +#if __FreeBSD_version >= 800002 kproc_exit(0); +#else + mtx_lock(&Giant); + kthread_exit(0); +#endif } /* @@ -574,7 +579,13 @@ * TODO: Add to list of sequencers this module provides */ - ret = kproc_create(seq_eventthread, scp, NULL, RFHIGHPID, 0, + ret = +#if __FreeBSD_version >= 800002 + kproc_create +#else + kthread_create +#endif + (seq_eventthread, scp, NULL, RFHIGHPID, 0, "sequencer %02d", scp->unit); if (ret) --- sys/dev/sound/null.c.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/null.c 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,609 @@ +/*- + * 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, WHETHERIN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THEPOSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include +#include "mixer_if.h" + +SND_DECLARE_FILE("$FreeBSD$"); + +#define SNDNULL_DESC "NULL Audio" + +#define SNDNULL_RATE_MIN 4000 +#define SNDNULL_RATE_MAX 192000 + +#define SNDNULL_RATE_DEFAULT 48000 +#define SNDNULL_FMT_DEFAULT SND_FORMAT(AFMT_S16_LE, 2, 0) +#define SNDNULL_FMTSTR_DEFAULT "s16le:2.0" + +#define SNDNULL_NPCHAN 1 +#define SNDNULL_NRCHAN 1 +#define SNDNULL_MAXCHAN (SNDNULL_NPCHAN + SNDNULL_NRCHAN) + +#define SNDNULL_BUFSZ_MIN 4096 +#define SNDNULL_BUFSZ_MAX 65536 +#define SNDNULL_BUFSZ_DEFAULT 4096 + +#define SNDNULL_BLKCNT_MIN 2 +#define SNDNULL_BLKCNT_MAX 512 +#define SNDNULL_BLKCNT_DEFAULT SNDNULL_BLKCNT_MIN + +#define SNDNULL_LOCK(sc) snd_mtxlock((sc)->lock) +#define SNDNULL_UNLOCK(sc) snd_mtxunlock((sc)->lock) + +struct sndnull_info; + +struct sndnull_chinfo { + struct snd_dbuf *buffer; + struct pcm_channel *channel; + struct pcmchan_caps *caps; + struct sndnull_info *parent; + uint32_t ptr, intrcnt; + int dir, active; +}; + +struct sndnull_info { + device_t dev; + struct sndnull_chinfo ch[SNDNULL_MAXCHAN]; + struct pcmchan_caps caps; + uint32_t bufsz; + uint32_t blkcnt; + uint32_t fmtlist[2]; + struct mtx *lock; + uint8_t *ringbuffer; + int chnum; + + struct callout poll_timer; + int poll_ticks, polling; +}; + +static void * +sndnull_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, + struct pcm_channel *c, int dir) +{ + struct sndnull_info *sc = devinfo; + struct sndnull_chinfo *ch; + + SNDNULL_LOCK(sc); + + ch = &sc->ch[sc->chnum++]; + ch->buffer = b; + ch->parent = sc; + ch->channel = c; + ch->dir = dir; + ch->caps = &sc->caps; + + SNDNULL_UNLOCK(sc); + + if (sndbuf_setup(ch->buffer, sc->ringbuffer, sc->bufsz) == -1) + return (NULL); + + return (ch); +} + +static int +sndnull_chan_setformat(kobj_t obj, void *data, uint32_t format) +{ + struct sndnull_chinfo *ch = data; + + if (ch->caps->fmtlist[0] != format) + return (-1); + + return (0); +} + +static int +sndnull_chan_setspeed(kobj_t obj, void *data, uint32_t spd) +{ + struct sndnull_chinfo *ch = data; + + if (spd < ch->caps->minspeed) + spd = ch->caps->minspeed; + if (spd > ch->caps->maxspeed) + spd = ch->caps->maxspeed; + + return (spd); +} + +static int +sndnull_chan_setfragments(kobj_t obj, void *data, + uint32_t blksz, uint32_t blkcnt) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + + blkcnt = sc->blkcnt; + blksz = sndbuf_getmaxsize(ch->buffer) / blkcnt; + blksz -= blksz % sndbuf_getbps(ch->buffer); + + if ((sndbuf_getblksz(ch->buffer) != blksz || + sndbuf_getblkcnt(ch->buffer) != blkcnt) && + sndbuf_resize(ch->buffer, blkcnt, blksz) != 0) + device_printf(sc->dev, "%s: failed blksz=%u blkcnt=%u\n", + __func__, blksz, blkcnt); + + return (1); +} + +static int +sndnull_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + + sndnull_chan_setfragments(obj, data, blksz, sc->blkcnt); + + return (sndbuf_getblksz(ch->buffer)); +} + +#define SNDNULL_CHAN_ACTIVE(ch) ((ch)->active != 0) + +static __inline int +sndnull_anychan_active(struct sndnull_info *sc) +{ + int i; + + for (i = 0; i < sc->chnum; i++) { + if (SNDNULL_CHAN_ACTIVE(&sc->ch[i])) + return (1); + } + + return (0); +} + +static void +sndnull_poll_callback(void *arg) +{ + struct sndnull_info *sc = arg; + struct sndnull_chinfo *ch; + int i; + + if (sc == NULL) + return; + + SNDNULL_LOCK(sc); + + if (!sndnull_anychan_active(sc)) { + SNDNULL_UNLOCK(sc); + return; + } + + for (i = 0; i < sc->chnum; i++) { + ch = &sc->ch[i]; + if (SNDNULL_CHAN_ACTIVE(ch)) { + ch->ptr += sndbuf_getblksz(ch->buffer); + ch->intrcnt += 1; + SNDNULL_UNLOCK(sc); + chn_intr(ch->channel); + SNDNULL_LOCK(sc); + } + } + + callout_reset(&sc->poll_timer, sc->poll_ticks, + sndnull_poll_callback, sc); + + SNDNULL_UNLOCK(sc); +} + +static int +sndnull_chan_trigger(kobj_t obj, void *data, int go) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + int pollticks; + + if (!PCMTRIG_COMMON(go)) + return (0); + + SNDNULL_LOCK(sc); + + switch (go) { + case PCMTRIG_START: + if (!sndnull_anychan_active(sc)) { + pollticks = ((uint64_t)hz * + sndbuf_getblksz(ch->buffer)) / + ((uint64_t)sndbuf_getbps(ch->buffer) * + sndbuf_getspd(ch->buffer)); + if (pollticks < 1) + pollticks = 1; + sc->poll_ticks = pollticks; + callout_reset(&sc->poll_timer, 1, + sndnull_poll_callback, sc); + if (bootverbose) + device_printf(sc->dev, + "PCMTRIG_START: pollticks=%d\n", + pollticks); + } + if (ch->dir == PCMDIR_REC) + memset(sc->ringbuffer, sndbuf_zerodata( + sndbuf_getfmt(ch->buffer)), + sndbuf_getmaxsize(ch->buffer)); + ch->ptr = 0; + ch->intrcnt = 0; + ch->active = 1; + break; + case PCMTRIG_STOP: + case PCMTRIG_ABORT: + ch->active = 0; + if (!sndnull_anychan_active(sc)) + callout_stop(&sc->poll_timer); + if (ch->dir == PCMDIR_PLAY) + memset(sc->ringbuffer, sndbuf_zerodata( + sndbuf_getfmt(ch->buffer)), + sndbuf_getmaxsize(ch->buffer)); + break; + default: + break; + } + + SNDNULL_UNLOCK(sc); + + return (0); +} + +static int +sndnull_chan_getptr(kobj_t obj, void *data) +{ + struct sndnull_chinfo *ch = data; + struct sndnull_info *sc = ch->parent; + uint32_t ptr; + + SNDNULL_LOCK(sc); + ptr = (SNDNULL_CHAN_ACTIVE(ch)) ? ch->ptr : 0; + SNDNULL_UNLOCK(sc); + + return (ptr); +} + +static struct pcmchan_caps * +sndnull_chan_getcaps(kobj_t obj, void *data) +{ + return (((struct sndnull_chinfo *)data)->caps); +} + +static kobj_method_t sndnull_chan_methods[] = { + KOBJMETHOD(channel_init, sndnull_chan_init), + KOBJMETHOD(channel_setformat, sndnull_chan_setformat), + KOBJMETHOD(channel_setspeed, sndnull_chan_setspeed), + KOBJMETHOD(channel_setblocksize, sndnull_chan_setblocksize), + KOBJMETHOD(channel_setfragments, sndnull_chan_setfragments), + KOBJMETHOD(channel_trigger, sndnull_chan_trigger), + KOBJMETHOD(channel_getptr, sndnull_chan_getptr), + KOBJMETHOD(channel_getcaps, sndnull_chan_getcaps), + { 0, 0 } +}; +CHANNEL_DECLARE(sndnull_chan); + +static const struct { + int ctl; + int rec; +} sndnull_mixer_ctls[SOUND_MIXER_NRDEVICES] = { + [SOUND_MIXER_VOLUME] = { 1, 0 }, + [SOUND_MIXER_BASS] = { 1, 0 }, + [SOUND_MIXER_TREBLE] = { 1, 0 }, + [SOUND_MIXER_SYNTH] = { 1, 1 }, + [SOUND_MIXER_PCM] = { 1, 1 }, + [SOUND_MIXER_SPEAKER] = { 1, 0 }, + [SOUND_MIXER_LINE] = { 1, 1 }, + [SOUND_MIXER_MIC] = { 1, 1 }, + [SOUND_MIXER_CD] = { 1, 1 }, + [SOUND_MIXER_IMIX] = { 1, 1 }, + [SOUND_MIXER_RECLEV] = { 1, 0 }, +}; + +static int +sndnull_mixer_init(struct snd_mixer *m) +{ + uint32_t mask, recmask; + int i; + + mask = 0; + recmask = 0; + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (sndnull_mixer_ctls[i].ctl != 0) + mask |= 1 << i; + if (sndnull_mixer_ctls[i].rec != 0) + recmask |= 1 << i; + } + + mix_setdevs(m, mask); + mix_setrecdevs(m, recmask); + + return (0); +} + +static int +sndnull_mixer_set(struct snd_mixer *m, unsigned dev, + unsigned left, unsigned right) +{ + if (!(dev < SOUND_MIXER_NRDEVICES && sndnull_mixer_ctls[dev].ctl != 0)) + return (-1); + + return (left | (right << 8)); +} + +static int +sndnull_mixer_setrecsrc(struct snd_mixer *m, uint32_t src) +{ + uint32_t recsrc; + int i; + + recsrc = src; + + if (recsrc & SOUND_MASK_IMIX) + recsrc &= SOUND_MASK_IMIX; + else { + for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { + if (sndnull_mixer_ctls[i].rec == 0) + recsrc &= ~(1 << i); + } + } + + return (recsrc); +} + +static kobj_method_t sndnull_mixer_methods[] = { + KOBJMETHOD(mixer_init, sndnull_mixer_init), + KOBJMETHOD(mixer_set, sndnull_mixer_set), + KOBJMETHOD(mixer_setrecsrc, sndnull_mixer_setrecsrc), + { 0, 0 } +}; +MIXER_DECLARE(sndnull_mixer); + +static int +sysctl_sndnull_rate(SYSCTL_HANDLER_ARGS) +{ + struct sndnull_info *sc; + device_t dev; + int err, val; + + dev = oidp->oid_arg1; + + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return (EINVAL); + + SNDNULL_LOCK(sc); + val = sc->caps.maxspeed; + SNDNULL_UNLOCK(sc); + + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err != 0 || req->newptr == NULL) + return (err); + + if (val < SNDNULL_RATE_MIN) + val = SNDNULL_RATE_MIN; + if (val > SNDNULL_RATE_MAX) + val = SNDNULL_RATE_MAX; + + SNDNULL_LOCK(sc); + if (sndnull_anychan_active(sc)) + err = EBUSY; + else { + sc->caps.minspeed = (uint32_t)val; + sc->caps.maxspeed = sc->caps.minspeed; + } + SNDNULL_UNLOCK(sc); + + return (err); +} + +static int +sysctl_sndnull_format(SYSCTL_HANDLER_ARGS) +{ + struct sndnull_info *sc; + device_t dev; + int err; + char fmtstr[AFMTSTR_LEN]; + uint32_t fmt; + + dev = oidp->oid_arg1; + + sc = pcm_getdevinfo(dev); + if (sc == NULL) + return (EINVAL); + + SNDNULL_LOCK(sc); + fmt = sc->fmtlist[0]; + if (snd_afmt2str(fmt, fmtstr, sizeof(fmtstr)) != fmt) + strlcpy(fmtstr, SNDNULL_FMTSTR_DEFAULT, sizeof(fmtstr)); + SNDNULL_UNLOCK(sc); + + err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); + + if (err != 0 || req->newptr == NULL) + return (err); + + fmt = snd_str2afmt(fmtstr); + if (fmt == 0) + return (EINVAL); + + SNDNULL_LOCK(sc); + if (fmt != sc->fmtlist[0]) { + if (sndnull_anychan_active(sc)) + err = EBUSY; + else + sc->fmtlist[0] = fmt; + } + SNDNULL_UNLOCK(sc); + + return (err); +} + +static device_t sndnull_dev = NULL; + +static void +sndnull_dev_identify(driver_t *driver, device_t parent) +{ + if (sndnull_dev == NULL) + sndnull_dev = BUS_ADD_CHILD(parent, 0, "pcm", -1); +} + +static int +sndnull_dev_probe(device_t dev) +{ + if (dev != NULL && dev == sndnull_dev) { + device_set_desc(dev, SNDNULL_DESC); + return (BUS_PROBE_DEFAULT); + } + + return (ENXIO); +} + +static int +sndnull_dev_attach(device_t dev) +{ + struct sndnull_info *sc; + char status[SND_STATUSLEN]; + int i; + + sc = malloc(sizeof(*sc), M_DEVBUF, M_WAITOK | M_ZERO); + + sc->lock = snd_mtxcreate(device_get_nameunit(dev), "snd_null softc"); + sc->dev = dev; + + callout_init(&sc->poll_timer, CALLOUT_MPSAFE); + sc->poll_ticks = 1; + + sc->caps.minspeed = SNDNULL_RATE_DEFAULT; + sc->caps.maxspeed = SNDNULL_RATE_DEFAULT; + sc->fmtlist[0] = SNDNULL_FMT_DEFAULT; + sc->fmtlist[1] = 0; + sc->caps.fmtlist = sc->fmtlist; + + sc->bufsz = pcm_getbuffersize(dev, SNDNULL_BUFSZ_MIN, + SNDNULL_BUFSZ_DEFAULT, SNDNULL_BUFSZ_MAX); + sc->blkcnt = SNDNULL_BLKCNT_DEFAULT; + + sc->ringbuffer = malloc(sc->bufsz, M_DEVBUF, M_WAITOK | M_ZERO); + + if (mixer_init(dev, &sndnull_mixer_class, sc) != 0) + device_printf(dev, "mixer_init() failed\n"); + + if (pcm_register(dev, sc, SNDNULL_NPCHAN, SNDNULL_NRCHAN)) + return (ENXIO); + + for (i = 0; i < SNDNULL_NPCHAN; i++) + pcm_addchan(dev, PCMDIR_PLAY, &sndnull_chan_class, sc); + for (i = 0; i < SNDNULL_NRCHAN; i++) + pcm_addchan(dev, PCMDIR_REC, &sndnull_chan_class, sc); + + snprintf(status, SND_STATUSLEN, "at %s %s", + device_get_nameunit(device_get_parent(dev)), + PCM_KLDSTRING(snd_null)); + + pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); + pcm_setstatus(dev, status); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "rate", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), + sysctl_sndnull_rate, "I", "runtime rate"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "format", CTLTYPE_STRING | CTLFLAG_RW, dev, sizeof(dev), + sysctl_sndnull_format, "A", "runtime format"); + + return (0); +} + +static void +sndnull_release_resources(struct sndnull_info *sc) +{ + if (sc == NULL) + return; + if (sc->chnum != 0) { + SNDNULL_LOCK(sc); + callout_stop(&sc->poll_timer); + SNDNULL_UNLOCK(sc); + callout_drain(&sc->poll_timer); + } + if (sc->ringbuffer != NULL) { + free(sc->ringbuffer, M_DEVBUF); + sc->ringbuffer = NULL; + } + if (sc->lock != NULL) { + snd_mtxfree(sc->lock); + sc->lock = NULL; + } + free(sc, M_DEVBUF); +} + +static int +sndnull_dev_detach(device_t dev) +{ + struct sndnull_info *sc; + int err; + + sc = pcm_getdevinfo(dev); + if (sc != NULL) { + err = pcm_unregister(dev); + if (err != 0) + return (err); + sndnull_release_resources(sc); + } + + return (0); +} + +static device_method_t sndnull_methods[] = { + DEVMETHOD(device_identify, sndnull_dev_identify), + DEVMETHOD(device_probe, sndnull_dev_probe), + DEVMETHOD(device_attach, sndnull_dev_attach), + DEVMETHOD(device_detach, sndnull_dev_detach), + { 0, 0 } +}; + +static driver_t sndnull_driver = { + "pcm", + sndnull_methods, + PCM_SOFTC_SIZE, +}; + +static int +sndnull_modevent(module_t mod, int type, void *data) +{ + switch (type) { + case MOD_UNLOAD: + if (sndnull_dev != NULL) + device_delete_child(device_get_parent(sndnull_dev), + sndnull_dev); + sndnull_dev = NULL; + case MOD_LOAD: + return (0); + break; + default: + break; + } + + return (EOPNOTSUPP); +} + +DRIVER_MODULE(snd_null, nexus, sndnull_driver, pcm_devclass, sndnull_modevent, + 0); +MODULE_DEPEND(snd_null, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); +MODULE_VERSION(snd_null, 1); --- sys/dev/sound/pci/als4000.c.orig 2007-06-17 14:10:41.000000000 +0800 +++ sys/dev/sound/pci/als4000.c 2009-01-17 19:29:20.000000000 +0800 @@ -84,10 +84,10 @@ /* Channel caps */ static u_int32_t als_format[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -216,7 +216,7 @@ ch->parent = sc; ch->channel = c; ch->bps = 1; - ch->format = AFMT_U8; + ch->format = SND_FORMAT(AFMT_U8, 1, 0); ch->speed = DSP_DEFAULT_SPEED; ch->buffer = b; snd_mtxunlock(sc->lock); @@ -316,10 +316,10 @@ u_int8_t dma_prog; /* sb16 dma program */ u_int8_t dma_stop; /* sb16 stop register */ } static const playback_cmds[] = { - ALS_8BIT_CMD(AFMT_U8, DSP_MODE_U8MONO), - ALS_8BIT_CMD(AFMT_U8 | AFMT_STEREO, DSP_MODE_U8STEREO), - ALS_16BIT_CMD(AFMT_S16_LE, DSP_MODE_S16MONO), - ALS_16BIT_CMD(AFMT_S16_LE | AFMT_STEREO, DSP_MODE_S16STEREO), + ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 1, 0), DSP_MODE_U8MONO), + ALS_8BIT_CMD(SND_FORMAT(AFMT_U8, 2, 0), DSP_MODE_U8STEREO), + ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_MODE_S16MONO), + ALS_16BIT_CMD(SND_FORMAT(AFMT_S16_LE, 2, 0), DSP_MODE_S16STEREO), }; static const struct playback_command* @@ -429,13 +429,13 @@ als_get_fifo_format(struct sc_info *sc, u_int32_t format) { switch (format) { - case AFMT_U8: + case SND_FORMAT(AFMT_U8, 1, 0): return ALS_FIFO1_8BIT; - case AFMT_U8 | AFMT_STEREO: + case SND_FORMAT(AFMT_U8, 2, 0): return ALS_FIFO1_8BIT | ALS_FIFO1_STEREO; - case AFMT_S16_LE: + case SND_FORMAT(AFMT_S16_LE, 1, 0): return ALS_FIFO1_SIGNED; - case AFMT_S16_LE | AFMT_STEREO: + case SND_FORMAT(AFMT_S16_LE, 2, 0): return ALS_FIFO1_SIGNED | ALS_FIFO1_STEREO; } device_printf(sc->dev, "format not found: 0x%08x\n", format); --- sys/dev/sound/pci/atiixp.c.orig 2007-11-05 00:03:10.000000000 +0800 +++ sys/dev/sound/pci/atiixp.c 2009-01-17 19:29:20.000000000 +0800 @@ -140,13 +140,13 @@ #define atiixp_assert(_sc) snd_mtxassert((_sc)->lock) static uint32_t atiixp_fmt_32bit[] = { - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; static uint32_t atiixp_fmt[] = { - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -954,7 +954,6 @@ atiixp_unlock(sc); } -#ifdef SND_DYNSYSCTL static int sysctl_atiixp_polling(SYSCTL_HANDLER_ARGS) { @@ -994,7 +993,6 @@ return (err); } -#endif static void atiixp_chip_post_init(void *arg) @@ -1090,12 +1088,10 @@ for (i = 0; i < ATI_IXP_NRCHAN; i++) pcm_addchan(sc->dev, PCMDIR_REC, &atiixp_chan_class, sc); -#ifdef SND_DYNSYSCTL SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_atiixp_polling, "I", "Enable polling mode"); -#endif snprintf(status, SND_STATUSLEN, "at memory 0x%lx irq %ld %s", rman_get_start(sc->reg), rman_get_start(sc->irq), --- sys/dev/sound/pci/aureal.c.orig 2007-06-17 14:10:41.000000000 +0800 +++ sys/dev/sound/pci/aureal.c 2009-01-17 19:29:20.000000000 +0800 @@ -38,19 +38,19 @@ /* channel interface */ static u_int32_t au_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps au_playcaps = {4000, 48000, au_playfmt, 0}; static u_int32_t au_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps au_reccaps = {4000, 48000, au_recfmt, 0}; @@ -242,13 +242,13 @@ au_prepareoutput(struct au_chinfo *ch, u_int32_t format) { struct au_info *au = ch->parent; - int i, stereo = (format & AFMT_STEREO)? 1 : 0; + int i, stereo = (AFMT_CHANNEL(format) > 1)? 1 : 0; u_int32_t baseaddr = sndbuf_getbufaddr(ch->buffer); au_wr(au, 0, 0x1061c, 0, 4); au_wr(au, 0, 0x10620, 0, 4); au_wr(au, 0, 0x10624, 0, 4); - switch(format & ~AFMT_STEREO) { + switch(AFMT_ENCODING(format)) { case 1: i=0xb000; break; --- sys/dev/sound/pci/cmi.c.orig 2007-06-17 14:10:41.000000000 +0800 +++ sys/dev/sound/pci/cmi.c 2009-01-17 19:29:20.000000000 +0800 @@ -126,10 +126,10 @@ /* Channel caps */ static u_int32_t cmi_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -345,7 +345,7 @@ ch->parent = sc; ch->channel = c; ch->bps = 1; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; ch->buffer = b; ch->dma_active = 0; @@ -381,7 +381,7 @@ ch->bps = 1; } - if (format & AFMT_STEREO) { + if (AFMT_CHANNEL(format) > 1) { f |= CMPCI_REG_FORMAT_STEREO; ch->bps *= 2; } else { @@ -745,7 +745,6 @@ static int cmi_initsys(struct sc_info* sc) { -#ifdef SND_DYNSYSCTL /* XXX: an user should be able to set this with a control tool, if not done before 7.0-RELEASE, this needs to be converted to a device specific sysctl "dev.pcm.X.yyy" via @@ -756,7 +755,7 @@ OID_AUTO, "spdif_enabled", CTLFLAG_RW, &sc->spdif_enabled, 0, "enable SPDIF output at 44.1 kHz and above"); -#endif /* SND_DYNSYSCTL */ + return 0; } --- sys/dev/sound/pci/cs4281.c.orig 2007-06-17 14:10:41.000000000 +0800 +++ sys/dev/sound/pci/cs4281.c 2009-01-17 19:29:20.000000000 +0800 @@ -117,18 +117,18 @@ /* formats (do not add formats without editing cs_fmt_tab) */ static u_int32_t cs4281_fmts[] = { - AFMT_U8, - AFMT_U8 | AFMT_STEREO, - AFMT_S8, - AFMT_S8 | AFMT_STEREO, - AFMT_S16_LE, - AFMT_S16_LE | AFMT_STEREO, - AFMT_U16_LE, - AFMT_U16_LE | AFMT_STEREO, - AFMT_S16_BE, - AFMT_S16_BE | AFMT_STEREO, - AFMT_U16_BE, - AFMT_U16_BE | AFMT_STEREO, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), + SND_FORMAT(AFMT_S16_BE, 1, 0), + SND_FORMAT(AFMT_S16_BE, 2, 0), + SND_FORMAT(AFMT_U16_BE, 1, 0), + SND_FORMAT(AFMT_U16_BE, 2, 0), 0 }; @@ -229,7 +229,7 @@ { u_int32_t dmr = 0; if (AFMT_8BIT & format) dmr |= CS4281PCI_DMR_SIZE8; - if (!(AFMT_STEREO & format)) dmr |= CS4281PCI_DMR_MONO; + if (AFMT_CHANNEL(format) < 2) dmr |= CS4281PCI_DMR_MONO; if (AFMT_BIGENDIAN & format) dmr |= CS4281PCI_DMR_BEND; if (!(AFMT_SIGNED & format)) dmr |= CS4281PCI_DMR_USIGN; return dmr; @@ -238,7 +238,8 @@ static inline u_int32_t cs4281_format_to_bps(u_int32_t format) { - return ((AFMT_8BIT & format) ? 1 : 2) * ((AFMT_STEREO & format) ? 2 : 1); + return ((AFMT_8BIT & format) ? 1 : 2) * + ((AFMT_CHANNEL(format) > 1) ? 2 : 1); } /* -------------------------------------------------------------------- */ @@ -320,7 +321,7 @@ ch->parent = sc; ch->channel = c; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; ch->bps = 1; ch->blksz = sndbuf_getsize(ch->buffer); --- sys/dev/sound/pci/csapcm.c.orig 2007-06-17 14:10:41.000000000 +0800 +++ sys/dev/sound/pci/csapcm.c 2009-01-17 19:29:20.000000000 +0800 @@ -94,21 +94,21 @@ static void csa_ac97_resume(struct csa_info *csa); static u_int32_t csa_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_S16_BE, - AFMT_STEREO | AFMT_S16_BE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S16_BE, 1, 0), + SND_FORMAT(AFMT_S16_BE, 2, 0), 0 }; static struct pcmchan_caps csa_playcaps = {8000, 48000, csa_playfmt, 0}; static u_int32_t csa_recfmt[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps csa_reccaps = {11025, 48000, csa_recfmt, 0}; @@ -489,7 +489,7 @@ csa->pfie |= 0x8000; if (ch->fmt & AFMT_BIGENDIAN) csa->pfie |= 0x4000; - if (!(ch->fmt & AFMT_STEREO)) + if (AFMT_CHANNEL(ch->fmt) < 2) csa->pfie |= 0x2000; if (ch->fmt & AFMT_8BIT) csa->pfie |= 0x1000; @@ -498,7 +498,7 @@ tmp = 4; if (ch->fmt & AFMT_16BIT) tmp <<= 1; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) tmp <<= 1; tmp--; --- sys/dev/sound/pci/ds1.c.orig 2007-06-17 14:10:41.000000000 +0800 +++ sys/dev/sound/pci/ds1.c 2009-01-17 19:29:20.000000000 +0800 @@ -167,23 +167,23 @@ /* -------------------------------------------------------------------- */ static u_int32_t ds_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps ds_reccaps = {4000, 48000, ds_recfmt, 0}; static u_int32_t ds_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - /* AFMT_S16_LE, */ - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + /* SND_FORMAT(AFMT_S16_LE, 1, 0), */ + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps ds_playcaps = {4000, 96000, ds_playfmt, 0}; @@ -432,7 +432,7 @@ int stereo, b16, c, sz; bus_addr_t addr; - stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; c = stereo? 1 : 0; addr = sndbuf_getbufaddr(ch->buffer); @@ -452,7 +452,7 @@ u_int32_t x, y; bus_addr_t addr; - stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; b16 = (ch->fmt & AFMT_16BIT)? 1 : 0; addr = sndbuf_getbufaddr(ch->buffer); sz = sndbuf_getsize(ch->buffer); @@ -487,7 +487,7 @@ ch->parent = sc; ch->channel = c; ch->dir = dir; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; ch->run = 0; if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, 0, sc->bufsz) != 0) @@ -547,7 +547,7 @@ if (!PCMTRIG_COMMON(go)) return 0; - stereo = (ch->fmt & AFMT_STEREO)? 1 : 0; + stereo = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; if (go == PCMTRIG_START) { ch->run = 1; ds_setuppch(ch); @@ -575,7 +575,7 @@ int ss; u_int32_t ptr; - ss = (ch->fmt & AFMT_STEREO)? 1 : 0; + ss = (AFMT_CHANNEL(ch->fmt) > 1)? 1 : 0; ss += (ch->fmt & AFMT_16BIT)? 1 : 0; bank = ch->lslot + sc->currbank; @@ -619,7 +619,7 @@ ch->parent = sc; ch->channel = c; ch->dir = dir; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; if (sndbuf_alloc(ch->buffer, sc->buffer_dmat, 0, sc->bufsz) != 0) return NULL; --- sys/dev/sound/pci/emu10k1.c.orig 2007-06-17 14:10:42.000000000 +0800 +++ sys/dev/sound/pci/emu10k1.c 2009-01-17 19:29:20.000000000 +0800 @@ -168,18 +168,18 @@ /* -------------------------------------------------------------------- */ static u_int32_t emu_rfmt_ac97[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static u_int32_t emu_rfmt_mic[] = { - AFMT_U8, + SND_FORMAT(AFMT_U8, 1, 0), 0 }; static u_int32_t emu_rfmt_efx[] = { - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -190,10 +190,10 @@ }; static u_int32_t emu_pfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -533,7 +533,7 @@ if (ch->fmt) { v->b16 = (ch->fmt & AFMT_16BIT) ? 1 : 0; - v->stereo = (ch->fmt & AFMT_STEREO) ? 1 : 0; + v->stereo = (AFMT_CHANNEL(ch->fmt) > 1) ? 1 : 0; if (v->slave != NULL) { v->slave->b16 = v->b16; v->slave->stereo = v->stereo; @@ -729,7 +729,7 @@ ch->parent = sc; ch->channel = c; ch->blksz = sc->bufsz / 2; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; snd_mtxlock(sc->lock); ch->master = emu_valloc(sc); @@ -866,7 +866,7 @@ ch->parent = sc; ch->channel = c; ch->blksz = sc->bufsz / 2; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; ch->num = sc->rnum; switch(sc->rnum) { @@ -994,12 +994,12 @@ if (ch->num == 0) { if (sc->audigy) { val = A_ADCCR_LCHANENABLE; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) val |= A_ADCCR_RCHANENABLE; val |= audigy_recval(ch->spd); } else { val = ADCCR_LCHANENABLE; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) val |= ADCCR_RCHANENABLE; val |= emu_recval(ch->spd); } --- sys/dev/sound/pci/emu10kx-pcm.c.orig 2007-09-12 15:43:42.000000000 +0800 +++ sys/dev/sound/pci/emu10kx-pcm.c 2009-01-17 19:29:20.000000000 +0800 @@ -112,8 +112,8 @@ static uint32_t emu_rfmt_adc[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps emu_reccaps_adc = { @@ -121,7 +121,7 @@ }; static uint32_t emu_rfmt_efx[] = { - AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), 0 }; @@ -142,15 +142,15 @@ }; static uint32_t emu_pfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static uint32_t emu_pfmt_mono[] = { - AFMT_U8, - AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), 0 }; @@ -718,7 +718,7 @@ ch->pcm = sc; ch->channel = c; ch->blksz = sc->bufsz; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; ch->master = emu_valloc(sc->card); /* @@ -789,7 +789,7 @@ snd_mtxlock(sc->lock); /* XXX can we trigger on parallel threads ? */ if (go == PCMTRIG_START) { emu_vsetup(ch->master, ch->fmt, ch->spd); - if ((ch->fmt & AFMT_STEREO) == AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) emu_vroute(sc->card, &(sc->rt), ch->master); else emu_vroute(sc->card, &(sc->rt_mono), ch->master); @@ -864,7 +864,7 @@ ch->pcm = sc; ch->channel = c; ch->blksz = sc->bufsz / 2; /* We rise interrupt for half-full buffer */ - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = 8000; ch->idxreg = sc->is_emu10k1 ? ADCIDX : A_ADCIDX; ch->basereg = ADCBA; @@ -973,7 +973,7 @@ ch->run = 1; emu_wrptr(sc->card, 0, ch->sizereg, sz); val = sc->is_emu10k1 ? ADCCR_LCHANENABLE : A_ADCCR_LCHANENABLE; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) val |= sc->is_emu10k1 ? ADCCR_RCHANENABLE : A_ADCCR_RCHANENABLE; val |= sc->is_emu10k1 ? emu_k1_recval(ch->spd) : emu_k2_recval(ch->spd); emu_wrptr(sc->card, 0, ch->setupreg, 0); @@ -1043,7 +1043,7 @@ if (sc == NULL) return (NULL); ch = &(sc->rch_efx); - ch->fmt = AFMT_S16_LE; + ch->fmt = SND_FORMAT(AFMT_S16_LE, 1, 0); ch->spd = sc->is_emu10k1 ? 48000*32 : 48000 * 64; ch->idxreg = FXIDX; ch->basereg = FXBA; @@ -1067,7 +1067,7 @@ static int emufxrchan_setformat(kobj_t obj __unused, void *c_devinfo __unused, uint32_t format) { - if (format == AFMT_S16_LE) return (0); + if (format == SND_FORMAT(AFMT_S16_LE, 1, 0)) return (0); return (EINVAL); } --- sys/dev/sound/pci/emu10kx.c.orig 2008-11-06 23:05:14.000000000 +0800 +++ sys/dev/sound/pci/emu10kx.c 2009-01-17 19:29:20.000000000 +0800 @@ -1325,7 +1325,7 @@ { if (fmt) { v->b16 = (fmt & AFMT_16BIT) ? 1 : 0; - v->stereo = (fmt & AFMT_STEREO) ? 1 : 0; + v->stereo = (AFMT_CHANNEL(fmt) > 1) ? 1 : 0; if (v->slave != NULL) { v->slave->b16 = v->b16; v->slave->stereo = v->stereo; @@ -3193,7 +3193,11 @@ i = 0; sc->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &i, RF_ACTIVE | RF_SHAREABLE); - if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, NULL, emu_intr, sc, &sc->ih)) { + if ((sc->irq == NULL) || bus_setup_intr(dev, sc->irq, INTR_MPSAFE | INTR_TYPE_AV, +#if __FreeBSD_version >= 700031 + NULL, +#endif + emu_intr, sc, &sc->ih)) { device_printf(dev, "unable to map interrupt\n"); goto bad; } --- sys/dev/sound/pci/envy24.c.orig 2007-10-12 14:03:44.000000000 +0800 +++ sys/dev/sound/pci/envy24.c 2009-01-17 19:29:20.000000000 +0800 @@ -51,7 +51,7 @@ #define ENVY24_TIMEOUT 1000 -#define ENVY24_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE) +#define ENVY24_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0) #define ENVY24_NAMELEN 32 @@ -313,16 +313,16 @@ }; static u_int32_t envy24_recfmt[] = { - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; static struct pcmchan_caps envy24_reccaps = {8000, 96000, envy24_recfmt, 0}; static u_int32_t envy24_playfmt[] = { - AFMT_STEREO | AFMT_U8, - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; @@ -335,15 +335,15 @@ }; static struct envy24_emldma envy24_pemltab[] = { - {AFMT_STEREO | AFMT_U8, envy24_p8u, 2}, - {AFMT_STEREO | AFMT_S16_LE, envy24_p16sl, 4}, - {AFMT_STEREO | AFMT_S32_LE, envy24_p32sl, 8}, + {SND_FORMAT(AFMT_U8, 2, 0), envy24_p8u, 2}, + {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24_p16sl, 4}, + {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24_p32sl, 8}, {0, NULL, 0} }; static struct envy24_emldma envy24_remltab[] = { - {AFMT_STEREO | AFMT_S16_LE, envy24_r16sl, 4}, - {AFMT_STEREO | AFMT_S32_LE, envy24_r32sl, 8}, + {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24_r16sl, 4}, + {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24_r32sl, 8}, {0, NULL, 0} }; --- sys/dev/sound/pci/envy24ht.c.orig 2007-10-12 14:03:44.000000000 +0800 +++ sys/dev/sound/pci/envy24ht.c 2009-01-17 19:29:20.000000000 +0800 @@ -63,7 +63,7 @@ #define ENVY24HT_TIMEOUT 1000 -#define ENVY24HT_DEFAULT_FORMAT (AFMT_STEREO | AFMT_S16_LE) +#define ENVY24HT_DEFAULT_FORMAT SND_FORMAT(AFMT_S16_LE, 2, 0) #define ENVY24HT_NAMELEN 32 @@ -402,16 +402,16 @@ }; static u_int32_t envy24ht_recfmt[] = { - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; static struct pcmchan_caps envy24ht_reccaps = {8000, 96000, envy24ht_recfmt, 0}; static u_int32_t envy24ht_playfmt[] = { - AFMT_STEREO | AFMT_U8, - AFMT_STEREO | AFMT_S16_LE, - AFMT_STEREO | AFMT_S32_LE, + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), 0 }; @@ -424,15 +424,15 @@ }; static struct envy24ht_emldma envy24ht_pemltab[] = { - {AFMT_STEREO | AFMT_U8, envy24ht_p8u, 2}, - {AFMT_STEREO | AFMT_S16_LE, envy24ht_p16sl, 4}, - {AFMT_STEREO | AFMT_S32_LE, envy24ht_p32sl, 8}, + {SND_FORMAT(AFMT_U8, 2, 0), envy24ht_p8u, 2}, + {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_p16sl, 4}, + {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_p32sl, 8}, {0, NULL, 0} }; static struct envy24ht_emldma envy24ht_remltab[] = { - {AFMT_STEREO | AFMT_S16_LE, envy24ht_r16sl, 4}, - {AFMT_STEREO | AFMT_S32_LE, envy24ht_r32sl, 8}, + {SND_FORMAT(AFMT_S16_LE, 2, 0), envy24ht_r16sl, 4}, + {SND_FORMAT(AFMT_S32_LE, 2, 0), envy24ht_r32sl, 8}, {0, NULL, 0} }; --- sys/dev/sound/pci/es137x.c.orig 2007-07-05 18:22:37.000000000 +0800 +++ sys/dev/sound/pci/es137x.c 2009-01-17 19:29:20.000000000 +0800 @@ -216,10 +216,10 @@ static int es1370_wrcodec(struct es_info *, unsigned char, unsigned char); static uint32_t es_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps es_caps = {4000, 48000, es_fmt, 0}; @@ -513,20 +513,20 @@ es->sctrl &= ~SCTRL_P1FMT; if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P1SEB; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) es->sctrl |= SCTRL_P1SMB; } else { es->sctrl &= ~SCTRL_P2FMT; if (format & AFMT_S16_LE) es->sctrl |= SCTRL_P2SEB; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) es->sctrl |= SCTRL_P2SMB; } } else { es->sctrl &= ~SCTRL_R1FMT; if (format & AFMT_S16_LE) es->sctrl |= SCTRL_R1SEB; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) es->sctrl |= SCTRL_R1SMB; } es_wr(es, ES1370_REG_SERIAL_CONTROL, es->sctrl, 4); @@ -736,7 +736,7 @@ cnt = (ch->blksz / sndbuf_getbps(ch->buffer)) - 1; if (ch->fmt & AFMT_16BIT) b |= 0x02; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) b |= 0x01; if (ch->dir == PCMDIR_PLAY) { if (go == PCMTRIG_START) { @@ -1362,7 +1362,6 @@ } } -#ifdef SND_DYNSYSCTL static int sysctl_es137x_spdif_enable(SYSCTL_HANDLER_ARGS) { @@ -1595,12 +1594,10 @@ return (err); } -#endif /* SND_DYNSYSCTL */ static void es_init_sysctls(device_t dev) { -#ifdef SND_DYNSYSCTL struct es_info *es; int r, devid, revid; @@ -1673,7 +1670,6 @@ "polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), sysctl_es_polling, "I", "Enable polling mode"); -#endif /* SND_DYNSYSCTL */ } static int --- sys/dev/sound/pci/fm801.c.orig 2007-06-17 14:10:42.000000000 +0800 +++ sys/dev/sound/pci/fm801.c 2009-01-17 19:29:20.000000000 +0800 @@ -106,10 +106,10 @@ */ static u_int32_t fmts[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -346,19 +346,20 @@ struct fm801_info *fm801 = ch->parent; DPRINT("fm801ch_setformat 0x%x : %s, %s, %s, %s\n", format, - (format & AFMT_STEREO)?"stereo":"mono", - (format & (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE)) ? "16bit":"8bit", + (AFMT_CHANNEL(format) > 1)?"stereo":"mono", + (format & AFMT_16BIT) ? "16bit":"8bit", (format & AFMT_SIGNED)? "signed":"unsigned", (format & AFMT_BIGENDIAN)?"bigendiah":"littleendian" ); if(ch->dir == PCMDIR_PLAY) { - fm801->play_fmt = (format & AFMT_STEREO)? FM_PLAY_STEREO : 0; + fm801->play_fmt = + (AFMT_CHANNEL(format) > 1)? FM_PLAY_STEREO : 0; fm801->play_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; return 0; } if(ch->dir == PCMDIR_REC ) { - fm801->rec_fmt = (format & AFMT_STEREO)? FM_REC_STEREO:0; + fm801->rec_fmt = (AFMT_CHANNEL(format) > 1)? FM_REC_STEREO:0; fm801->rec_fmt |= (format & AFMT_16BIT) ? FM_PLAY_16BIT : 0; return 0; } --- sys/dev/sound/pci/hda/hdac.c.orig 2009-01-17 19:31:45.000000000 +0800 +++ sys/dev/sound/pci/hda/hdac.c 2009-01-17 19:29:20.000000000 +0800 @@ -467,7 +467,7 @@ /* Default */ static uint32_t hdac_fmt[] = { - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -3412,11 +3412,9 @@ } } - if (ch->fmt & (AFMT_STEREO | AFMT_AC3)) { + totalchn = AFMT_CHANNEL(ch->fmt); + if (totalchn > 1) fmt |= 1; - totalchn = 2; - } else - totalchn = 1; HDAC_WRITE_2(&sc->mem, ch->off + HDAC_SDFMT, fmt); @@ -3696,7 +3694,7 @@ } /* Declare soft PCM volume if needed. */ - if (pdevinfo->play >= 0 && !pdevinfo->digital) { + if (pdevinfo->play >= 0) { ctl = NULL; if ((mask & SOUND_MASK_PCM) == 0 || (devinfo->function.audio.quirks & HDA_QUIRK_SOFTPCMVOL)) { @@ -6375,17 +6373,20 @@ else if (HDA_PARAM_SUPP_PCM_SIZE_RATE_20BIT(pcmcap)) ch->bit32 = 2; if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) - ch->fmtlist[i++] = AFMT_S16_LE; - ch->fmtlist[i++] = AFMT_S16_LE | AFMT_STEREO; + ch->fmtlist[i++] = + SND_FORMAT(AFMT_S16_LE, 1, 0); + ch->fmtlist[i++] = SND_FORMAT(AFMT_S16_LE, 2, 0); if (ch->bit32 > 0) { if (!(devinfo->function.audio.quirks & HDA_QUIRK_FORCESTEREO)) - ch->fmtlist[i++] = AFMT_S32_LE; - ch->fmtlist[i++] = AFMT_S32_LE | AFMT_STEREO; + ch->fmtlist[i++] = + SND_FORMAT(AFMT_S32_LE, 1, 0); + ch->fmtlist[i++] = + SND_FORMAT(AFMT_S32_LE, 2, 0); } } if (HDA_PARAM_SUPP_STREAM_FORMATS_AC3(fmtcap)) { - ch->fmtlist[i++] = AFMT_AC3; + ch->fmtlist[i++] = SND_FORMAT(AFMT_AC3, 2, 0); } ch->fmtlist[i] = 0; i = 0; @@ -7110,7 +7111,6 @@ } } -#ifdef SND_DYNSYSCTL static int sysctl_hdac_polling(SYSCTL_HANDLER_ARGS) { @@ -7338,7 +7338,6 @@ hdac_unlock(sc); return (0); } -#endif static void hdac_attach2(void *arg) @@ -7586,7 +7585,6 @@ bus_generic_attach(sc->dev); -#ifdef SND_DYNSYSCTL SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "polling", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), @@ -7600,7 +7598,6 @@ SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, "pindump", CTLTYPE_INT | CTLFLAG_RW, sc->dev, sizeof(sc->dev), sysctl_hdac_pindump, "I", "Dump pin states/data"); -#endif } /**************************************************************************** --- sys/dev/sound/pci/ich.c.orig 2007-12-13 02:21:05.000000000 +0800 +++ sys/dev/sound/pci/ich.c 2009-01-17 19:29:20.000000000 +0800 @@ -92,6 +92,26 @@ #define ICH_DMA_NOCACHE (1 << 4) #define ICH_HIGH_LATENCY (1 << 5) +#if defined(__i386__) || defined(__amd64__) +#if __FreeBSD_version < 602110 || defined(SND_USE_LPMAP) +#include +#endif +#endif + +#ifdef _LPMAP_C_ +#define ICH_BUS_DMA_NOCACHE 0 +#define ICH_DMA_ATTR(sc, v, s, attr) do { \ + vm_offset_t va = (vm_offset_t)(v); \ + vm_size_t sz = (vm_size_t)(s); \ + if ((sc) != NULL && ((sc)->flags & ICH_DMA_NOCACHE) && \ + va != 0 && sz != 0) \ + (void)lpmap_change_attr(va, sz, (attr)); \ +} while(0) +#else +#define ICH_BUS_DMA_NOCACHE BUS_DMA_NOCACHE +#define ICH_DMA_ATTR(...) +#endif + static const struct ich_type { uint16_t vendor; uint16_t devid; @@ -198,7 +218,7 @@ /* -------------------------------------------------------------------- */ static uint32_t ich_fmt[] = { - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps ich_vrcaps = {8000, 48000, ich_fmt, 0}; @@ -412,10 +432,13 @@ ICH_UNLOCK(sc); if (sndbuf_alloc(ch->buffer, sc->chan_dmat, - ((sc->flags & ICH_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0), + ((sc->flags & ICH_DMA_NOCACHE) ? ICH_BUS_DMA_NOCACHE : 0), sc->bufsz) != 0) return (NULL); + ICH_DMA_ATTR(sc, sndbuf_getbuf(ch->buffer), + sndbuf_getmaxsize(ch->buffer), PAT_UNCACHEABLE); + ICH_LOCK(sc); ich_wr(sc, ch->regbase + ICH_REG_X_BDBAR, (uint32_t)(ch->desc_addr), 4); ICH_UNLOCK(sc); @@ -423,6 +446,24 @@ return (ch); } +#ifdef _LPMAP_C_ +static int +ichchan_free(kobj_t obj, void *data) +{ + struct sc_chinfo *ch; + struct sc_info *sc; + + ch = (struct sc_chinfo *)data; + sc = (ch != NULL) ? ch->parent : NULL; + if (ch != NULL && sc != NULL) { + ICH_DMA_ATTR(sc, sndbuf_getbuf(ch->buffer), + sndbuf_getmaxsize(ch->buffer), PAT_WRITE_BACK); + } + + return (1); +} +#endif + static int ichchan_setformat(kobj_t obj, void *data, uint32_t format) { @@ -577,6 +618,9 @@ static kobj_method_t ichchan_methods[] = { KOBJMETHOD(channel_init, ichchan_init), +#ifdef _LPMAP_C_ + KOBJMETHOD(channel_free, ichchan_free), +#endif KOBJMETHOD(channel_setformat, ichchan_setformat), KOBJMETHOD(channel_setspeed, ichchan_setspeed), KOBJMETHOD(channel_setblocksize, ichchan_setblocksize), @@ -665,7 +709,6 @@ static int ich_initsys(struct sc_info* sc) { -#ifdef SND_DYNSYSCTL /* XXX: this should move to a device specific sysctl "dev.pcm.X.yyy" via device_get_sysctl_*() as discussed on multimedia@ in msg-id <861wujij2q.fsf@xps.des.no> */ @@ -674,7 +717,7 @@ OID_AUTO, "ac97rate", CTLFLAG_RW, &sc->ac97rate, 48000, "AC97 link rate (default = 48000)"); -#endif /* SND_DYNSYSCTL */ + return (0); } @@ -1054,10 +1097,12 @@ } if (bus_dmamem_alloc(sc->dmat, (void **)&sc->dtbl, BUS_DMA_NOWAIT | - ((sc->flags & ICH_DMA_NOCACHE) ? BUS_DMA_NOCACHE : 0), + ((sc->flags & ICH_DMA_NOCACHE) ? ICH_BUS_DMA_NOCACHE : 0), &sc->dtmap)) goto bad; + ICH_DMA_ATTR(sc, sc->dtbl, sc->dtbl_size, PAT_UNCACHEABLE); + if (bus_dmamap_load(sc->dmat, sc->dtmap, sc->dtbl, sc->dtbl_size, ich_setmap, sc, 0)) goto bad; @@ -1102,8 +1147,10 @@ sc->nabmbarid, sc->nabmbar); if (sc->dtmap) bus_dmamap_unload(sc->dmat, sc->dtmap); - if (sc->dtbl) + if (sc->dtbl) { + ICH_DMA_ATTR(sc, sc->dtbl, sc->dtbl_size, PAT_WRITE_BACK); bus_dmamem_free(sc->dmat, sc->dtbl, sc->dtmap); + } if (sc->chan_dmat) bus_dma_tag_destroy(sc->chan_dmat); if (sc->dmat) @@ -1130,6 +1177,7 @@ bus_release_resource(dev, sc->regtype, sc->nambarid, sc->nambar); bus_release_resource(dev, sc->regtype, sc->nabmbarid, sc->nabmbar); bus_dmamap_unload(sc->dmat, sc->dtmap); + ICH_DMA_ATTR(sc, sc->dtbl, sc->dtbl_size, PAT_WRITE_BACK); bus_dmamem_free(sc->dmat, sc->dtbl, sc->dtmap); bus_dma_tag_destroy(sc->chan_dmat); bus_dma_tag_destroy(sc->dmat); --- sys/dev/sound/pci/maestro.c.orig 2007-12-14 10:16:26.000000000 +0800 +++ sys/dev/sound/pci/maestro.c 2009-01-17 19:29:20.000000000 +0800 @@ -157,9 +157,7 @@ bus_dma_tag_t stat_dmat; /* FreeBSD SMPng related */ -#ifdef USING_MUTEX struct mtx lock; /* mutual exclusion */ -#endif /* FreeBSD newpcm related */ struct ac97_info *codec; @@ -278,11 +276,7 @@ timo = msec * hz / 1000; if (timo == 0) timo = 1; -#ifdef USING_MUTEX msleep(sc, &sc->lock, PWAIT, wmesg, timo); -#else - tsleep(sc, PWAIT, wmesg, timo); -#endif } @@ -1370,7 +1364,7 @@ if (format & AFMT_BIGENDIAN || format & AFMT_U16_LE) return EINVAL; ch->stereo = ch->qs16 = ch->us = 0; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) ch->stereo = 1; if (format & AFMT_U8 || format & AFMT_S8) { @@ -1449,12 +1443,12 @@ aggpch_getcaps(kobj_t obj, void *data) { static u_int32_t playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps playcaps = {8000, 48000, playfmt, 0}; @@ -1519,7 +1513,7 @@ if (!(format & AFMT_S16_LE)) return EINVAL; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) ch->stereo = 1; else ch->stereo = 0; @@ -1591,8 +1585,8 @@ aggrch_getcaps(kobj_t obj, void *sc) { static u_int32_t recfmt[] = { - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps reccaps = {8000, 48000, recfmt, 0}; @@ -1776,7 +1770,6 @@ ess = malloc(sizeof(*ess), M_DEVBUF, M_WAITOK | M_ZERO); ess->dev = dev; -#ifdef USING_MUTEX mtx_init(&ess->lock, device_get_desc(dev), "snd_maestro softc", MTX_DEF | MTX_RECURSE); if (!mtx_initialized(&ess->lock)) { @@ -1784,7 +1777,6 @@ ret = ENOMEM; goto bad; } -#endif if (resource_int_value(device_get_name(dev), device_get_unit(dev), "dac", &dacn) == 0) { @@ -1941,10 +1933,8 @@ bus_dma_tag_destroy(ess->stat_dmat); if (ess->buf_dmat != NULL) bus_dma_tag_destroy(ess->buf_dmat); -#ifdef USING_MUTEX if (mtx_initialized(&ess->lock)) mtx_destroy(&ess->lock); -#endif free(ess, M_DEVBUF); } @@ -1985,9 +1975,7 @@ dma_free(ess->stat_dmat, ess->stat); bus_dma_tag_destroy(ess->stat_dmat); bus_dma_tag_destroy(ess->buf_dmat); -#ifdef USING_MUTEX mtx_destroy(&ess->lock); -#endif free(ess, M_DEVBUF); return 0; } @@ -1996,18 +1984,11 @@ agg_suspend(device_t dev) { struct agg_info *ess = pcm_getdevinfo(dev); -#ifndef USING_MUTEX - int x; - x = spltty(); -#endif AGG_WR(ess, PORT_HOSTINT_CTRL, 0, 2); agg_lock(ess); agg_power(ess, PCI_POWERSTATE_D3); agg_unlock(ess); -#ifndef USING_MUTEX - splx(x); -#endif return 0; } @@ -2017,11 +1998,7 @@ { int i; struct agg_info *ess = pcm_getdevinfo(dev); -#ifndef USING_MUTEX - int x; - x = spltty(); -#endif for (i = 0; i < ess->playchns; i++) if (ess->active & (1 << i)) aggch_start_dac(ess->pch + i); @@ -2032,9 +2009,6 @@ if (!ess->active) agg_power(ess, powerstate_init); agg_unlock(ess); -#ifndef USING_MUTEX - splx(x); -#endif if (mixer_reinit(dev)) { device_printf(dev, "unable to reinitialize the mixer\n"); --- sys/dev/sound/pci/maestro3.c.orig 2007-06-17 14:10:42.000000000 +0800 +++ sys/dev/sound/pci/maestro3.c 2009-01-17 19:29:20.000000000 +0800 @@ -214,10 +214,10 @@ /* channel descriptors */ static u_int32_t m3_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps m3_playcaps = {8000, 48000, m3_playfmt, 0}; @@ -236,10 +236,10 @@ CHANNEL_DECLARE(m3_pch); static u_int32_t m3_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps m3_reccaps = {8000, 48000, m3_recfmt, 0}; @@ -404,7 +404,7 @@ ch->buffer = b; ch->parent = sc; ch->channel = c; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; M3_UNLOCK(sc); /* XXX */ if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) { @@ -516,10 +516,10 @@ ("m3_pchan_setformat(dac=%d, format=0x%x{%s-%s})\n", ch->dac_idx, format, format & (AFMT_U8|AFMT_S8) ? "8bit":"16bit", - format & AFMT_STEREO ? "STEREO":"MONO")); + (AFMT_CHANNEL(format) > 1) ? "STEREO":"MONO")); /* mono word */ - data = (format & AFMT_STEREO) ? 0 : 1; + data = (AFMT_CHANNEL(format) > 1)? 0 : 1; m3_wr_assp_data(sc, ch->dac_data + SRC3_MODE_OFFSET, data); /* 8bit word */ @@ -756,7 +756,7 @@ ch->buffer = b; ch->parent = sc; ch->channel = c; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; M3_UNLOCK(sc); /* XXX */ if (sndbuf_alloc(ch->buffer, sc->parent_dmat, 0, sc->bufsz) != 0) { @@ -863,10 +863,10 @@ ("m3_rchan_setformat(dac=%d, format=0x%x{%s-%s})\n", ch->adc_idx, format, format & (AFMT_U8|AFMT_S8) ? "8bit":"16bit", - format & AFMT_STEREO ? "STEREO":"MONO")); + (AFMT_CHANNEL(format) > 1) ? "STEREO":"MONO")); /* mono word */ - data = (format & AFMT_STEREO) ? 0 : 1; + data = (AFMT_CHANNEL(format) > 1) ? 0 : 1; m3_wr_assp_data(sc, ch->adc_data + SRC3_MODE_OFFSET, data); /* 8bit word */ --- sys/dev/sound/pci/neomagic.c.orig 2007-06-17 14:10:42.000000000 +0800 +++ sys/dev/sound/pci/neomagic.c 2009-01-17 19:29:20.000000000 +0800 @@ -113,10 +113,10 @@ /* -------------------------------------------------------------------- */ static u_int32_t nm_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps nm_caps = {4000, 48000, nm_fmt, 0}; @@ -334,7 +334,7 @@ x <<= 4; x &= NM_RATE_MASK; if (ch->fmt & AFMT_16BIT) x |= NM_RATE_BITS_16; - if (ch->fmt & AFMT_STEREO) x |= NM_RATE_STEREO; + if (AFMT_CHANNEL(ch->fmt) > 1) x |= NM_RATE_STEREO; base = (ch->dir == PCMDIR_PLAY)? NM_PLAYBACK_REG_OFFSET : NM_RECORD_REG_OFFSET; nm_wr(sc, base + NM_RATE_REG_OFFSET, x, 1); @@ -410,7 +410,7 @@ return 0; ssz = (ch->fmt & AFMT_16BIT)? 2 : 1; - if (ch->fmt & AFMT_STEREO) + if (AFMT_CHANNEL(ch->fmt) > 1) ssz <<= 1; if (ch->dir == PCMDIR_PLAY) { --- sys/dev/sound/pci/solo.c.orig 2007-06-17 14:10:42.000000000 +0800 +++ sys/dev/sound/pci/solo.c 2009-01-17 19:29:20.000000000 +0800 @@ -48,14 +48,14 @@ #define ESS18XX_MPSAFE 1 static u_int32_t ess_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps ess_playcaps = {6000, 48000, ess_playfmt, 0}; @@ -64,14 +64,14 @@ * Recording output is byte-swapped */ static u_int32_t ess_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_BE, - AFMT_STEREO | AFMT_S16_BE, - AFMT_U16_BE, - AFMT_STEREO | AFMT_U16_BE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_BE, 1, 0), + SND_FORMAT(AFMT_S16_BE, 2, 0), + SND_FORMAT(AFMT_U16_BE, 1, 0), + SND_FORMAT(AFMT_U16_BE, 2, 0), 0 }; static struct pcmchan_caps ess_reccaps = {6000, 48000, ess_recfmt, 0}; @@ -424,8 +424,8 @@ { int play = (dir == PCMDIR_PLAY)? 1 : 0; int b16 = (fmt & AFMT_16BIT)? 1 : 0; - int stereo = (fmt & AFMT_STEREO)? 1 : 0; - int unsign = (fmt == AFMT_U8 || fmt == AFMT_U16_LE || fmt == AFMT_U16_BE)? 1 : 0; + int stereo = (AFMT_CHANNEL(fmt) > 1)? 1 : 0; + int unsign = (!(fmt & AFMT_SIGNED))? 1 : 0; u_int8_t spdval, fmtval; DEB(printf("ess_setupch\n")); --- sys/dev/sound/pci/t4dwave.c.orig 2007-11-10 12:32:50.000000000 +0800 +++ sys/dev/sound/pci/t4dwave.c 2009-01-17 19:29:20.000000000 +0800 @@ -103,27 +103,27 @@ /* -------------------------------------------------------------------- */ static u_int32_t tr_recfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps tr_reccaps = {4000, 48000, tr_recfmt, 0}; static u_int32_t tr_playfmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), 0 }; static struct pcmchan_caps tr_playcaps = {4000, 48000, tr_playfmt, 0}; @@ -473,7 +473,7 @@ bits = 0; bits |= (fmt & AFMT_SIGNED)? 0x2 : 0; - bits |= (fmt & AFMT_STEREO)? 0x4 : 0; + bits |= (AFMT_CHANNEL(fmt) > 1)? 0x4 : 0; bits |= (fmt & AFMT_16BIT)? 0x8 : 0; return bits; --- sys/dev/sound/pci/via8233.c.orig 2007-06-14 19:13:38.000000000 +0800 +++ sys/dev/sound/pci/via8233.c 2009-01-17 19:29:20.000000000 +0800 @@ -123,10 +123,10 @@ }; static uint32_t via_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -150,7 +150,6 @@ return (ret); } -#ifdef SND_DYNSYSCTL static int sysctl_via8233_spdif_enable(SYSCTL_HANDLER_ARGS) { @@ -243,12 +242,10 @@ return (err); } -#endif /* SND_DYNSYSCTL */ static void via_init_sysctls(device_t dev) { -#ifdef SND_DYNSYSCTL /* XXX: an user should be able to set this with a control tool, if not done before 7.0-RELEASE, this needs to be converted to a device specific sysctl "dev.pcm.X.yyy" via device_get_sysctl_*() @@ -268,7 +265,6 @@ "polling", CTLTYPE_INT | CTLFLAG_RW, dev, sizeof(dev), sysctl_via_polling, "I", "Enable polling mode"); -#endif } static __inline uint32_t @@ -408,7 +404,7 @@ uint32_t f = WR_FORMAT_STOP_INDEX; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) f |= WR_FORMAT_STEREO; if (format & AFMT_S16_LE) f |= WR_FORMAT_16BIT; @@ -431,7 +427,7 @@ v = via_rd(via, r, 4); v &= ~(VIA8233_DXS_RATEFMT_STEREO | VIA8233_DXS_RATEFMT_16BIT); - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) v |= VIA8233_DXS_RATEFMT_STEREO; if (format & AFMT_16BIT) v |= VIA8233_DXS_RATEFMT_16BIT; @@ -450,7 +446,7 @@ uint32_t s = 0xff000000; uint8_t v = (format & AFMT_S16_LE) ? MC_SGD_16BIT : MC_SGD_8BIT; - if (format & AFMT_STEREO) { + if (AFMT_CHANNEL(format) > 1) { v |= MC_SGD_CHANNELS(2); s |= SLOT3(1) | SLOT4(2); } else { --- sys/dev/sound/pci/via82c686.c.orig 2007-06-17 14:10:42.000000000 +0800 +++ sys/dev/sound/pci/via82c686.c 2009-01-17 19:29:20.000000000 +0800 @@ -90,10 +90,10 @@ }; static u_int32_t via_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; static struct pcmchan_caps via_vracaps = {4000, 48000, via_fmt, 0}; @@ -284,7 +284,7 @@ int mode, mode_set; mode_set = 0; - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) mode_set |= VIA_RPMODE_STEREO; if (format & AFMT_S16_LE) mode_set |= VIA_RPMODE_16BIT; --- sys/dev/sound/pci/vibes.c.orig 2007-06-17 14:10:42.000000000 +0800 +++ sys/dev/sound/pci/vibes.c 2009-01-17 19:29:20.000000000 +0800 @@ -97,10 +97,10 @@ }; static u_int32_t sc_fmt[] = { - AFMT_U8, - AFMT_U8 | AFMT_STEREO, - AFMT_S16_LE, - AFMT_S16_LE | AFMT_STEREO, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), 0 }; @@ -197,7 +197,7 @@ return NULL; } ch->buffer = b; - ch->fmt = AFMT_U8; + ch->fmt = SND_FORMAT(AFMT_U8, 1, 0); ch->spd = DSP_DEFAULT_SPEED; ch->dma_active = ch->dma_was_active = 0; @@ -229,7 +229,7 @@ struct sc_chinfo *ch = data; /* NB Just note format here as setting format register * generates noise if dma channel is inactive. */ - ch->fmt = (format & AFMT_STEREO) ? SV_AFMT_STEREO : SV_AFMT_MONO; + ch->fmt = (AFMT_CHANNEL(format) > 1) ? SV_AFMT_STEREO : SV_AFMT_MONO; ch->fmt |= (format & AFMT_16BIT) ? SV_AFMT_S16 : SV_AFMT_U8; return 0; } --- sys/dev/sound/pcm/ac97.c.orig 2007-10-27 04:49:59.000000000 +0800 +++ sys/dev/sound/pcm/ac97.c 2009-01-17 19:29:20.000000000 +0800 @@ -822,7 +822,7 @@ ac97_create(device_t dev, void *devinfo, kobj_class_t cls) { struct ac97_info *codec; - int eapdinv; + int i; codec = malloc(sizeof(*codec), M_AC97, M_WAITOK | M_ZERO); snprintf(codec->name, sizeof(codec->name), "%s:ac97", @@ -832,11 +832,15 @@ codec->dev = dev; codec->devinfo = devinfo; codec->flags = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), - "eapdinv", &eapdinv) == 0) { - if (eapdinv != 0) - codec->flags |= AC97_F_EAPD_INV; - } + "eapdinv", &i) == 0 && i != 0) + codec->flags |= AC97_F_EAPD_INV; + + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "softpcmvol", &i) == 0 && i != 0) + pcm_setflags(dev, pcm_getflags(dev) | SD_F_SOFTPCMVOL); + return codec; } @@ -864,7 +868,6 @@ /* -------------------------------------------------------------------- */ -#ifdef SND_DYNSYSCTL static int sysctl_hw_snd_ac97_eapd(SYSCTL_HANDLER_ARGS) { @@ -892,12 +895,10 @@ } return err; } -#endif static void ac97_init_sysctl(struct ac97_info *codec) { -#ifdef SND_DYNSYSCTL u_int16_t orig, val; if (codec == NULL || codec->dev == NULL) @@ -915,7 +916,6 @@ OID_AUTO, "eapd", CTLTYPE_INT | CTLFLAG_RW, codec, sizeof(codec), sysctl_hw_snd_ac97_eapd, "I", "AC97 External Amplifier"); -#endif } static int @@ -984,7 +984,6 @@ case 0x434d4978: /* CMI9761 */ case 0x434d4982: /* CMI9761 */ case 0x434d4983: /* CMI9761 */ - ac97_wrcd(codec, AC97_MIX_PCM, 0); bzero(&codec->mix[SOUND_MIXER_PCM], sizeof(codec->mix[SOUND_MIXER_PCM])); pcm_setflags(codec->dev, pcm_getflags(codec->dev) | @@ -995,6 +994,8 @@ break; } + if (pcm_getflags(codec->dev) & SD_F_SOFTPCMVOL) + ac97_wrcd(codec, AC97_MIX_PCM, 0); #if 0 /* XXX For the sake of debugging purposes */ mix_setparentchild(m, SOUND_MIXER_VOLUME, --- sys/dev/sound/pcm/buffer.c.orig 2007-06-16 11:37:27.000000000 +0800 +++ sys/dev/sound/pcm/buffer.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Portions Copyright (c) Ryan Beasley - GSoC 2006 + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,6 +30,9 @@ #include "feeder_if.h" +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/buffer.c,v 1.37 2007/06/16 03:37:27 ariff Exp $"); struct snd_dbuf * @@ -154,7 +159,7 @@ unsigned int bufsize, allocsize; u_int8_t *tmpbuf; - chn_lock(b->channel); + CHN_LOCK(b->channel); if (b->maxsize == 0) goto out; if (blkcnt == 0) @@ -162,7 +167,7 @@ if (blksz == 0) blksz = b->blksz; if (blkcnt < 2 || blksz < 16 || (blkcnt * blksz) > b->maxsize) { - chn_unlock(b->channel); + CHN_UNLOCK(b->channel); return EINVAL; } if (blkcnt == b->blkcnt && blksz == b->blksz) @@ -173,9 +178,9 @@ if (bufsize > b->allocsize || bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) { allocsize = round_page(bufsize); - chn_unlock(b->channel); + CHN_UNLOCK(b->channel); tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK); - chn_lock(b->channel); + CHN_LOCK(b->channel); if (snd_verbose > 3) printf("%s(): b=%p %p -> %p [%d -> %d : %d]\n", __func__, b, b->tmpbuf, tmpbuf, @@ -194,7 +199,7 @@ sndbuf_reset(b); out: - chn_unlock(b->channel); + CHN_UNLOCK(b->channel); return 0; } @@ -212,11 +217,11 @@ if (bufsize > b->allocsize || bufsize < (b->allocsize >> SNDBUF_CACHE_SHIFT)) { allocsize = round_page(bufsize); - chn_unlock(b->channel); + CHN_UNLOCK(b->channel); buf = malloc(allocsize, M_DEVBUF, M_WAITOK); tmpbuf = malloc(allocsize, M_DEVBUF, M_WAITOK); shadbuf = malloc(allocsize, M_DEVBUF, M_WAITOK); - chn_lock(b->channel); + CHN_LOCK(b->channel); if (b->buf != NULL) free(b->buf, M_DEVBUF); b->buf = buf; @@ -334,8 +339,7 @@ sndbuf_setfmt(struct snd_dbuf *b, u_int32_t fmt) { b->fmt = fmt; - b->bps = 1; - b->bps <<= (b->fmt & AFMT_STEREO)? 1 : 0; + b->bps = AFMT_CHANNEL(b->fmt); if (b->fmt & AFMT_16BIT) b->bps <<= 1; else if (b->fmt & AFMT_24BIT) @@ -670,26 +674,55 @@ return 0; } +#ifdef SND_DIAGNOSTIC +static uint32_t snd_feeder_maxfeed = 0; +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_maxfeed, CTLFLAG_RD, + &snd_feeder_maxfeed, 0, "maximum feeder count request"); + +static uint32_t snd_feeder_maxcycle = 0; +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_maxcycle, CTLFLAG_RD, + &snd_feeder_maxcycle, 0, "maximum feeder cycle"); +#endif + /* count is number of bytes we want added to destination buffer */ int sndbuf_feed(struct snd_dbuf *from, struct snd_dbuf *to, struct pcm_channel *channel, struct pcm_feeder *feeder, unsigned int count) { - unsigned int cnt; + unsigned int cnt, maxfeed; +#ifdef SND_DIAGNOSTIC + unsigned int cycle; + + if (count > snd_feeder_maxfeed) + snd_feeder_maxfeed = count; + + cycle = 0; +#endif KASSERT(count > 0, ("can't feed 0 bytes")); if (sndbuf_getfree(to) < count) - return EINVAL; + return (EINVAL); + + maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getbps(to)); do { - cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, count, from); - if (cnt) { - sndbuf_acquire(to, to->tmpbuf, cnt); - count -= cnt; - } - } while (count && cnt); + cnt = FEEDER_FEED(feeder, channel, to->tmpbuf, + min(count, maxfeed), from); + if (cnt == 0) + break; + sndbuf_acquire(to, to->tmpbuf, cnt); + count -= cnt; +#ifdef SND_DIAGNOSTIC + cycle++; +#endif + } while (count != 0); - return 0; +#ifdef SND_DIAGNOSTIC + if (cycle > snd_feeder_maxcycle) + snd_feeder_maxcycle = cycle; +#endif + + return (0); } /************************************************************/ --- sys/dev/sound/pcm/channel.c.orig 2008-11-06 23:05:15.000000000 +0800 +++ sys/dev/sound/pcm/channel.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,6 +1,8 @@ /*- - * Copyright (c) 1999 Cameron Grant - * Portions Copyright by Luigi Rizzo - 1997-99 + * Copyright (c) 2005-2009 Ariff Abdullah + * Portions Copyright (c) Ryan Beasley - GSoC 2006 + * Copyright (c) 1999 Cameron Grant + * Portions Copyright (c) Luigi Rizzo - 1997-99 * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -37,6 +39,10 @@ SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW, &report_soft_formats, 1, "report software-emulated formats"); +int report_soft_matrix = 1; +SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_matrix, CTLFLAG_RW, + &report_soft_matrix, 1, "report software-emulated channel matrixing"); + int chn_latency = CHN_LATENCY_DEFAULT; TUNABLE_INT("hw.snd.latency", &chn_latency); @@ -107,6 +113,80 @@ "interrupt timeout (1 - 10) seconds"); #endif +static int chn_vpc_autoreset = 1; +TUNABLE_INT("hw.snd.vpc_autoreset", &chn_vpc_autoreset); +SYSCTL_INT(_hw_snd, OID_AUTO, vpc_autoreset, CTLFLAG_RW, + &chn_vpc_autoreset, 0, "automatically reset channels volume to 0db"); + +static int chn_vol_0db_pcm = SND_VOL_0DB_PCM; + +static void +chn_vpc_proc(int reset, int db) +{ + struct snddev_info *d; + struct pcm_channel *c; + int i; + + for (i = 0; pcm_devclass != NULL && + i < devclass_get_maxunit(pcm_devclass); i++) { + d = devclass_get_softc(pcm_devclass, i); + if (!PCM_REGISTERED(d)) + continue; + PCM_LOCK(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_VOL_0DB, db); + if (reset != 0) + chn_vpc_reset(c, SND_VOL_C_PCM, 1); + CHN_UNLOCK(c); + } + PCM_RELEASE(d); + PCM_UNLOCK(d); + } +} + +static int +sysctl_hw_snd_vpc_0db(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = chn_vol_0db_pcm; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL) + return (err); + if (val < SND_VOL_0DB_MIN || val > SND_VOL_0DB_MAX) + return (EINVAL); + + chn_vol_0db_pcm = val; + chn_vpc_proc(0, val); + + return (0); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_0db, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_vpc_0db, "I", + "0db relative level"); + +static int +sysctl_hw_snd_vpc_reset(SYSCTL_HANDLER_ARGS) +{ + int err, val; + + val = 0; + err = sysctl_handle_int(oidp, &val, 0, req); + if (err != 0 || req->newptr == NULL || val == 0) + return (err); + + chn_vol_0db_pcm = SND_VOL_0DB_PCM; + chn_vpc_proc(1, SND_VOL_0DB_PCM); + + return (0); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, vpc_reset, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_vpc_reset, "I", + "reset volume on all channels"); + static int chn_usefrags = 0; TUNABLE_INT("hw.snd.usefrags", &chn_usefrags); static int chn_syncdelay = -1; @@ -137,8 +217,6 @@ */ struct pcm_synclist snd_pcm_syncgroups = SLIST_HEAD_INITIALIZER(head); -static int chn_buildfeeder(struct pcm_channel *c); - static void chn_lockinit(struct pcm_channel *c, int dir) { @@ -159,10 +237,13 @@ c->lock = snd_mtxcreate(c->name, "pcm virtual record channel"); cv_init(&c->intr_cv, "pcmrdv"); break; - case 0: + case PCMDIR_FAKE: c->lock = snd_mtxcreate(c->name, "pcm fake channel"); cv_init(&c->intr_cv, "pcmfk"); break; + default: + panic("%s(): Invalid direction=%d", __func__, dir); + break; } cv_init(&c->cv, "pcmchn"); @@ -568,28 +649,31 @@ } void -chn_intr(struct pcm_channel *c) +chn_intr_locked(struct pcm_channel *c) { - uint8_t do_unlock; - if (CHN_LOCK_OWNED(c)) { - /* - * Allow sound drivers to call this function with - * "CHN_LOCK()" locked: - */ - do_unlock = 0; - } else { - do_unlock = 1; - CHN_LOCK(c); - } + + CHN_LOCKASSERT(c); + c->interrupts++; + if (c->direction == PCMDIR_PLAY) chn_wrintr(c); else chn_rdintr(c); - if (do_unlock) { - CHN_UNLOCK(c); +} + +void +chn_intr(struct pcm_channel *c) +{ + + if (CHN_LOCKOWNED(c)) { + chn_intr_locked(c); + return; } - return; + + CHN_LOCK(c); + chn_intr_locked(c); + CHN_UNLOCK(c); } u_int32_t @@ -634,6 +718,23 @@ } if (i >= j) { + if ((c->flags & CHN_F_VIRTUAL) && c->parentchannel != NULL && + (sndbuf_getfmt(c->bufhard) != c->parentchannel->format || + sndbuf_getspd(c->bufhard) != c->parentchannel->speed || + (c->flags & CHN_F_DIRTY))) { + if (snd_verbose > 3) + printf("%s: vchan out of sync: " + "%u vs %u / 0x%08x vs 0x%08x\n", c->name, + sndbuf_getspd(c->bufhard), + c->parentchannel->speed, + sndbuf_getfmt(c->bufhard), + c->parentchannel->format); + sndbuf_setspd(c->bufhard, c->parentchannel->speed); + err = feeder_chain(c); + if (err != 0) + return (err); + c->flags &= ~CHN_F_DIRTY; + } c->flags |= CHN_F_TRIGGERED; sndbuf_setrun(b, 1); c->feedcount = (c->flags & CHN_F_CLOSING) ? 2 : 0; @@ -886,145 +987,177 @@ } int -fmtvalid(u_int32_t fmt, u_int32_t *fmtlist) +snd_fmtvalid(uint32_t fmt, uint32_t *fmtlist) { int i; - for (i = 0; fmtlist[i]; i++) - if (fmt == fmtlist[i]) - return 1; - return 0; + for (i = 0; fmtlist[i] != 0; i++) { + if (fmt == fmtlist[i] || + ((fmt & AFMT_AC3) && (fmtlist[i] & AFMT_AC3))) + return (1); + } + + return (0); } -static struct afmtstr_table default_afmtstr_table[] = { - { "alaw", AFMT_A_LAW }, { "mulaw", AFMT_MU_LAW }, - { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, - { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, - { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, - { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, - { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, - { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, - { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, - { NULL, 0 }, +static const struct { + char *name, *alias1, *alias2; + uint32_t afmt; +} afmt_tab[] = { + { "alaw", NULL, NULL, AFMT_A_LAW }, + { "mulaw", NULL, NULL, AFMT_MU_LAW }, + { "u8", "8", NULL, AFMT_U8 }, + { "s8", NULL, NULL, AFMT_S8 }, +#if BYTE_ORDER == LITTLE_ENDIAN + { "s16le", "s16", "16", AFMT_S16_LE }, + { "s16be", NULL, NULL, AFMT_S16_BE }, +#else + { "s16le", NULL, NULL, AFMT_S16_LE }, + { "s16be", "s16", "16", AFMT_S16_BE }, +#endif + { "u16le", NULL, NULL, AFMT_U16_LE }, + { "u16be", NULL, NULL, AFMT_U16_BE }, + { "s24le", NULL, NULL, AFMT_S24_LE }, + { "s24be", NULL, NULL, AFMT_S24_BE }, + { "u24le", NULL, NULL, AFMT_U24_LE }, + { "u24be", NULL, NULL, AFMT_U24_BE }, +#if BYTE_ORDER == LITTLE_ENDIAN + { "s32le", "s32", "32", AFMT_S32_LE }, + { "s32be", NULL, NULL, AFMT_S32_BE }, +#else + { "s32le", NULL, NULL, AFMT_S32_LE }, + { "s32be", "s32", "32", AFMT_S32_BE }, +#endif + { "u32le", NULL, NULL, AFMT_U32_LE }, + { "u32be", NULL, NULL, AFMT_U32_BE }, + { NULL, NULL, NULL, 0 } }; -int -afmtstr_swap_sign(char *s) -{ - if (s == NULL || strlen(s) < 2) /* full length of "s8" */ - return 0; - if (*s == 's') - *s = 'u'; - else if (*s == 'u') - *s = 's'; - else - return 0; - return 1; -} +static const struct { + char *name, *alias1, *alias2; + int matrix_id; +} matrix_id_tab[] = { + { "1.0", "1", "mono", SND_CHN_MATRIX_1_0 }, + { "2.0", "2", "stereo", SND_CHN_MATRIX_2_0 }, + { "2.1", NULL, NULL, SND_CHN_MATRIX_2_1 }, + { "3.0", "3", NULL, SND_CHN_MATRIX_3_0 }, + { "4.0", "4", "quad", SND_CHN_MATRIX_4_0 }, + { "4.1", NULL, NULL, SND_CHN_MATRIX_4_1 }, + { "5.0", "5", NULL, SND_CHN_MATRIX_5_0 }, + { "5.1", "6", NULL, SND_CHN_MATRIX_5_1 }, + { "6.0", NULL, NULL, SND_CHN_MATRIX_6_0 }, + { "6.1", "7", NULL, SND_CHN_MATRIX_6_1 }, + { "7.1", "8", NULL, SND_CHN_MATRIX_7_1 }, + { NULL, NULL, NULL, SND_CHN_MATRIX_UNKNOWN } +}; -int -afmtstr_swap_endian(char *s) +uint32_t +snd_str2afmt(const char *req) { - if (s == NULL || strlen(s) < 5) /* full length of "s16le" */ - return 0; - if (s[3] == 'l') - s[3] = 'b'; - else if (s[3] == 'b') - s[3] = 'l'; - else - return 0; - return 1; -} + uint32_t i, afmt; + int matrix_id; + char b1[8], b2[8]; -u_int32_t -afmtstr2afmt(struct afmtstr_table *tbl, const char *s, int stereo) -{ - size_t fsz, sz; + i = sscanf(req, "%5[^:]:%6s", b1, b2); - sz = (s == NULL) ? 0 : strlen(s); + if (i == 1) { + if (strlen(req) != strlen(b1)) + return (0); + strlcpy(b2, "2.0", sizeof(b2)); + } else if (i == 2) { + if (strlen(req) != (strlen(b1) + 1 + strlen(b2))) + return (0); + } else + return (0); - if (sz > 1) { + afmt = 0; + matrix_id = SND_CHN_MATRIX_UNKNOWN; - if (tbl == NULL) - tbl = default_afmtstr_table; - - for (; tbl->fmtstr != NULL; tbl++) { - fsz = strlen(tbl->fmtstr); - if (sz < fsz) - continue; - if (strncmp(s, tbl->fmtstr, fsz) != 0) - continue; - if (fsz == sz) - return tbl->format | - ((stereo) ? AFMT_STEREO : 0); - if ((sz - fsz) < 2 || s[fsz] != ':') - break; - /* - * For now, just handle mono/stereo. - */ - if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 'm' || - s[fsz + 1] == '1')) || - strcmp(s + fsz + 1, "mono") == 0) - return tbl->format; - if ((s[fsz + 2] == '\0' && (s[fsz + 1] == 's' || - s[fsz + 1] == '2')) || - strcmp(s + fsz + 1, "stereo") == 0) - return tbl->format | AFMT_STEREO; - break; + for (i = 0; afmt == 0 && afmt_tab[i].name != NULL; i++) { + if (strcasecmp(afmt_tab[i].name, b1) == 0 || + (afmt_tab[i].alias1 != NULL && + strcasecmp(afmt_tab[i].alias1, b1) == 0) || + (afmt_tab[i].alias2 != NULL && + strcasecmp(afmt_tab[i].alias2, b1) == 0)) { + afmt = afmt_tab[i].afmt; + strlcpy(b1, afmt_tab[i].name, sizeof(b1)); } } - return 0; + if (afmt == 0) + return (0); + + for (i = 0; matrix_id == SND_CHN_MATRIX_UNKNOWN && + matrix_id_tab[i].name != NULL; i++) { + if (strcmp(matrix_id_tab[i].name, b2) == 0 || + (matrix_id_tab[i].alias1 != NULL && + strcmp(matrix_id_tab[i].alias1, b2) == 0) || + (matrix_id_tab[i].alias2 != NULL && + strcasecmp(matrix_id_tab[i].alias2, b2) == 0)) { + matrix_id = matrix_id_tab[i].matrix_id; + strlcpy(b2, matrix_id_tab[i].name, sizeof(b2)); + } + } + + if (matrix_id == SND_CHN_MATRIX_UNKNOWN) + return (0); + +#ifndef _KERNEL + printf("Parse OK: '%s' -> '%s:%s' %d\n", req, b1, b2, + (int)(b2[0]) - '0' + (int)(b2[2]) - '0'); +#endif + + return (SND_FORMAT(afmt, b2[0] - '0' + b2[2] - '0', b2[2] - '0')); } -u_int32_t -afmt2afmtstr(struct afmtstr_table *tbl, u_int32_t afmt, char *dst, - size_t len, int type, int stereo) +uint32_t +snd_afmt2str(uint32_t afmt, char *buf, size_t len) { - u_int32_t fmt = 0; - char *fmtstr = NULL, *tag = ""; + uint32_t i, enc, ch, ext; + char tmp[AFMTSTR_LEN]; - if (tbl == NULL) - tbl = default_afmtstr_table; + if (buf == NULL || len < AFMTSTR_LEN) + return (0); + + + bzero(tmp, sizeof(tmp)); - for (; tbl->format != 0; tbl++) { - if (tbl->format == 0) + enc = AFMT_ENCODING(afmt); + ch = AFMT_CHANNEL(afmt); + ext = AFMT_EXTCHANNEL(afmt); + + for (i = 0; afmt_tab[i].name != NULL; i++) { + if (enc == afmt_tab[i].afmt) { + strlcpy(tmp, afmt_tab[i].name, sizeof(tmp)); + strlcat(tmp, ":", sizeof(tmp)); break; - if ((afmt & ~AFMT_STEREO) != tbl->format) - continue; - fmt = afmt; - fmtstr = tbl->fmtstr; - break; + } } - if (fmt != 0 && fmtstr != NULL && dst != NULL && len > 0) { - strlcpy(dst, fmtstr, len); - switch (type) { - case AFMTSTR_SIMPLE: - tag = (fmt & AFMT_STEREO) ? ":s" : ":m"; - break; - case AFMTSTR_NUM: - tag = (fmt & AFMT_STEREO) ? ":2" : ":1"; - break; - case AFMTSTR_FULL: - tag = (fmt & AFMT_STEREO) ? ":stereo" : ":mono"; - break; - case AFMTSTR_NONE: - default: + if (strlen(tmp) == 0) + return (0); + + for (i = 0; matrix_id_tab[i].name != NULL; i++) { + if (ch == (matrix_id_tab[i].name[0] - '0' + + matrix_id_tab[i].name[2] - '0') && + ext == (matrix_id_tab[i].name[2] - '0')) { + strlcat(tmp, matrix_id_tab[i].name, sizeof(tmp)); break; } - if (strlen(tag) > 0 && ((stereo && !(fmt & AFMT_STEREO)) || \ - (!stereo && (fmt & AFMT_STEREO)))) - strlcat(dst, tag, len); } - return fmt; + if (strlen(tmp) == 0) + return (0); + + strlcpy(buf, tmp, len); + + return (snd_str2afmt(buf)); } int -chn_reset(struct pcm_channel *c, u_int32_t fmt) +chn_reset(struct pcm_channel *c, uint32_t fmt, uint32_t spd) { - int hwspd, r; + int r; CHN_LOCKASSERT(c); c->feedcount = 0; @@ -1033,27 +1166,19 @@ c->timeout = 1; c->xruns = 0; - r = CHANNEL_RESET(c->methods, c->devinfo); - if (fmt != 0) { -#if 0 - hwspd = DSP_DEFAULT_SPEED; - /* only do this on a record channel until feederbuilder works */ - if (c->direction == PCMDIR_REC) - RANGE(hwspd, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); - c->speed = hwspd; -#endif - hwspd = chn_getcaps(c)->minspeed; - c->speed = hwspd; + c->flags |= (pcm_getflags(c->dev) & SD_F_BITPERFECT) ? + CHN_F_BITPERFECT : 0; - if (r == 0) - r = chn_setformat(c, fmt); - if (r == 0) - r = chn_setspeed(c, hwspd); -#if 0 - if (r == 0) - r = chn_setvolume(c, 100, 100); -#endif - } + r = CHANNEL_RESET(c->methods, c->devinfo); + if (r == 0 && fmt != 0 && spd != 0) { + r = chn_setparam(c, fmt, spd); + fmt = 0; + spd = 0; + } + if (r == 0 && fmt != 0) + r = chn_setformat(c, fmt); + if (r == 0 && spd != 0) + r = chn_setspeed(c, spd); if (r == 0) r = chn_setlatency(c, chn_latency); if (r == 0) { @@ -1068,7 +1193,7 @@ { struct feeder_class *fc; struct snd_dbuf *b, *bs; - int ret; + int i, ret; if (chn_timeout < CHN_TIMEOUT_MIN || chn_timeout > CHN_TIMEOUT_MAX) chn_timeout = CHN_TIMEOUT; @@ -1114,6 +1239,20 @@ c->flags = 0; c->feederflags = 0; c->sm = NULL; + c->format = SND_FORMAT(AFMT_U8, 1, 0); + c->speed = DSP_DEFAULT_SPEED; + + c->matrix = *feeder_matrix_id_map(SND_CHN_MATRIX_1_0); + c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL; + + for (i = 0; i < SND_CHN_T_MAX; i++) { + c->volume[SND_VOL_C_MASTER][i] = SND_VOL_0DB_MASTER; + } + + c->volume[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB] = SND_VOL_0DB_MASTER; + c->volume[SND_VOL_C_PCM][SND_CHN_T_VOL_0DB] = chn_vol_0db_pcm; + + chn_vpc_reset(c, SND_VOL_C_PCM, 1); ret = ENODEV; CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */ @@ -1126,17 +1265,13 @@ if ((sndbuf_getsize(b) == 0) && ((c->flags & CHN_F_VIRTUAL) == 0)) goto out; - ret = chn_setdir(c, direction); - if (ret) - goto out; + ret = 0; + c->direction = direction; - ret = sndbuf_setfmt(b, AFMT_U8); - if (ret) - goto out; - - ret = sndbuf_setfmt(bs, AFMT_U8); - if (ret) - goto out; + sndbuf_setfmt(b, c->format); + sndbuf_setspd(b, c->speed); + sndbuf_setfmt(bs, c->format); + sndbuf_setspd(bs, c->speed); /** * @todo Should this be moved somewhere else? The primary buffer @@ -1197,39 +1332,221 @@ return (0); } +/* XXX Obsolete. Use *_matrix() variant instead. */ int -chn_setdir(struct pcm_channel *c, int dir) +chn_setvolume(struct pcm_channel *c, int left, int right) { -#ifdef DEV_ISA - struct snd_dbuf *b = c->bufhard; -#endif - int r; + int ret; + + ret = chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FL, left); + ret |= chn_setvolume_matrix(c, SND_VOL_C_MASTER, SND_CHN_T_FR, + right) << 8; + + return (ret); +} + +int +chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right, + int center) +{ + int i, ret; + + ret = 0; + + for (i = 0; i < SND_CHN_T_MAX; i++) { + if ((1 << i) & SND_CHN_LEFT_MASK) + ret |= chn_setvolume_matrix(c, vc, i, left); + else if ((1 << i) & SND_CHN_RIGHT_MASK) + ret |= chn_setvolume_matrix(c, vc, i, right) << 8; + else + ret |= chn_setvolume_matrix(c, vc, i, center) << 16; + } + + return (ret); +} + +int +chn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val) +{ + int i; + KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && + (vc == SND_VOL_C_MASTER || (vc & 1)) && + (vt == SND_CHN_T_VOL_0DB || (vt >= SND_CHN_T_BEGIN && + vt <= SND_CHN_T_END)) && (vt != SND_CHN_T_VOL_0DB || + (val >= SND_VOL_0DB_MIN && val <= SND_VOL_0DB_MAX)), + ("%s(): invalid volume matrix c=%p vc=%d vt=%d val=%d", + __func__, c, vc, vt, val)); CHN_LOCKASSERT(c); - c->direction = dir; - r = CHANNEL_SETDIR(c->methods, c->devinfo, c->direction); -#ifdef DEV_ISA - if (!r && SND_DMA(b)) - sndbuf_dmasetdir(b, c->direction); -#endif - return r; + + if (val < 0) + val = 0; + if (val > 100) + val = 100; + + c->volume[vc][vt] = val; + + /* + * Do relative calculation here and store it into class + 1 + * to ease the job of feeder_volume. + */ + if (vc == SND_VOL_C_MASTER) { + for (vc = SND_VOL_C_BEGIN; vc <= SND_VOL_C_END; + vc += SND_VOL_C_STEP) + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } else if (vc & 1) { + if (vt == SND_CHN_T_VOL_0DB) + for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; + i += SND_CHN_T_STEP) { + c->volume[SND_VOL_C_VAL(vc)][i] = + SND_VOL_CALC_VAL(c->volume, vc, i); + } + else + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } + + return (val); } int -chn_setvolume(struct pcm_channel *c, int left, int right) +chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt) { + KASSERT(c != NULL && vc >= SND_VOL_C_MASTER && vc < SND_VOL_C_MAX && + (vt == SND_CHN_T_VOL_0DB || + (vt >= SND_CHN_T_BEGIN && vt <= SND_CHN_T_END)), + ("%s(): invalid volume matrix c=%p vc=%d vt=%d", + __func__, c, vc, vt)); CHN_LOCKASSERT(c); - /* should add a feeder for volume changing if channel returns -1 */ - if (left > 100) - left = 100; - if (left < 0) - left = 0; - if (right > 100) - right = 100; - if (right < 0) - right = 0; - c->volume = left | (right << 8); - return 0; + + return (c->volume[vc][vt]); +} + +struct pcmchan_matrix * +chn_getmatrix(struct pcm_channel *c) +{ + + KASSERT(c != NULL, ("%s(): NULL channel", __func__)); + CHN_LOCKASSERT(c); + + if (!(c->format & AFMT_CONVERTIBLE)) + return (NULL); + + return (&c->matrix); +} + +int +chn_setmatrix(struct pcm_channel *c, struct pcmchan_matrix *m) +{ + + KASSERT(c != NULL && m != NULL, + ("%s(): NULL channel or matrix", __func__)); + CHN_LOCKASSERT(c); + + if (!(c->format & AFMT_CONVERTIBLE)) + return (EINVAL); + + c->matrix = *m; + c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL; + + return (chn_setformat(c, SND_FORMAT(c->format, m->channels, m->ext))); +} + +/* + * XXX chn_oss_* exists for the sake of compatibility. + */ +int +chn_oss_getorder(struct pcm_channel *c, unsigned long long *map) +{ + + KASSERT(c != NULL && map != NULL, + ("%s(): NULL channel or map", __func__)); + CHN_LOCKASSERT(c); + + return (feeder_matrix_oss_get_channel_order(&c->matrix, map)); +} + +int +chn_oss_setorder(struct pcm_channel *c, unsigned long long *map) +{ + struct pcmchan_matrix m; + int ret; + + KASSERT(c != NULL && map != NULL, + ("%s(): NULL channel or map", __func__)); + CHN_LOCKASSERT(c); + + + m = c->matrix; + ret = feeder_matrix_oss_set_channel_order(&m, map); + if (ret != 0) + return (ret); + + return (chn_setmatrix(c, &m)); +} + +#define SND_CHN_OSS_FRONT (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR) +#define SND_CHN_OSS_SURR (SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR) +#define SND_CHN_OSS_CENTER_LFE (SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF) +#define SND_CHN_OSS_REAR (SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR) + +int +chn_oss_getmask(struct pcm_channel *c, uint32_t *retmask) +{ + struct pcmchan_matrix *m; + struct pcmchan_caps *caps; + uint32_t i, format; + + KASSERT(c != NULL && retmask != NULL, + ("%s(): NULL channel or retmask", __func__)); + CHN_LOCKASSERT(c); + + caps = chn_getcaps(c); + if (caps == NULL || caps->fmtlist == NULL) + return (ENODEV); + + for (i = 0; caps->fmtlist[i] != 0; i++) { + format = caps->fmtlist[i]; + if (!(format & AFMT_CONVERTIBLE)) { + *retmask |= DSP_BIND_SPDIF; + continue; + } + m = CHANNEL_GETMATRIX(c->methods, c->devinfo, format); + if (m == NULL) + continue; + if (m->mask & SND_CHN_OSS_FRONT) + *retmask |= DSP_BIND_FRONT; + if (m->mask & SND_CHN_OSS_SURR) + *retmask |= DSP_BIND_SURR; + if (m->mask & SND_CHN_OSS_CENTER_LFE) + *retmask |= DSP_BIND_CENTER_LFE; + if (m->mask & SND_CHN_OSS_REAR) + *retmask |= DSP_BIND_REAR; + } + + /* report software-supported binding mask */ + if (!CHN_BITPERFECT(c) && report_soft_matrix) + *retmask |= DSP_BIND_FRONT | DSP_BIND_SURR | + DSP_BIND_CENTER_LFE | DSP_BIND_REAR; + + return (0); +} + +void +chn_vpc_reset(struct pcm_channel *c, int vc, int force) +{ + int i; + + KASSERT(c != NULL && vc >= SND_VOL_C_BEGIN && vc <= SND_VOL_C_END, + ("%s(): invalid reset c=%p vc=%d", __func__, c, vc)); + CHN_LOCKASSERT(c); + + if (force == 0 && chn_vpc_autoreset == 0) + return; + + for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; i += SND_CHN_T_STEP) + CHN_SETVOLUME(c, vc, i, c->volume[vc][SND_CHN_T_VOL_0DB]); } static u_int32_t @@ -1610,132 +1927,184 @@ return chn_resizebuf(c, -1, blkcnt, blksz); } -static int -chn_tryspeed(struct pcm_channel *c, int speed) +int +chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed) { - struct pcm_feeder *f; - struct snd_dbuf *b = c->bufhard; - struct snd_dbuf *bs = c->bufsoft; - struct snd_dbuf *x; - int r, delta; + uint32_t hwspeed, delta; + int ret; CHN_LOCKASSERT(c); - DEB(printf("setspeed, channel %s\n", c->name)); - DEB(printf("want speed %d, ", speed)); - if (speed <= 0) - return EINVAL; - if (CHN_STOPPED(c)) { - r = 0; - c->speed = speed; - sndbuf_setspd(bs, speed); - RANGE(speed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); - DEB(printf("try speed %d, ", speed)); - sndbuf_setspd(b, CHANNEL_SETSPEED(c->methods, c->devinfo, speed)); - DEB(printf("got speed %d\n", sndbuf_getspd(b))); - - delta = sndbuf_getspd(b) - sndbuf_getspd(bs); - if (delta < 0) - delta = -delta; - c->feederflags &= ~(1 << FEEDER_RATE); - /* - * Used to be 500. It was too big! - */ - if (delta > feeder_rate_round) - c->feederflags |= 1 << FEEDER_RATE; - else - sndbuf_setspd(bs, sndbuf_getspd(b)); + if (speed <= 0 || format == 0 || CHN_STARTED(c)) + return (EINVAL); - r = chn_buildfeeder(c); - DEB(printf("r = %d\n", r)); - if (r) - goto out; + hwspeed = speed; + RANGE(hwspeed, chn_getcaps(c)->minspeed, chn_getcaps(c)->maxspeed); - if (!(c->feederflags & (1 << FEEDER_RATE))) - goto out; + sndbuf_setspd(c->bufhard, CHANNEL_SETSPEED(c->methods, c->devinfo, + hwspeed)); + hwspeed = sndbuf_getspd(c->bufhard); - r = EINVAL; - f = chn_findfeeder(c, FEEDER_RATE); - DEB(printf("feedrate = %p\n", f)); - if (f == NULL) - goto out; + delta = (hwspeed > speed) ? (hwspeed - speed) : (speed - hwspeed); - x = (c->direction == PCMDIR_REC)? b : bs; - r = FEEDER_SET(f, FEEDRATE_SRC, sndbuf_getspd(x)); - DEB(printf("feeder_set(FEEDRATE_SRC, %d) = %d\n", sndbuf_getspd(x), r)); - if (r) - goto out; + c->format = format; + c->speed = (delta > feeder_rate_round) ? speed : hwspeed; - x = (c->direction == PCMDIR_REC)? bs : b; - r = FEEDER_SET(f, FEEDRATE_DST, sndbuf_getspd(x)); - DEB(printf("feeder_set(FEEDRATE_DST, %d) = %d\n", sndbuf_getspd(x), r)); -out: - if (!r) - r = CHANNEL_SETFORMAT(c->methods, c->devinfo, - sndbuf_getfmt(b)); - if (!r) - sndbuf_setfmt(bs, c->format); - if (!r) - r = chn_resizebuf(c, -2, 0, 0); - DEB(printf("setspeed done, r = %d\n", r)); - return r; - } else - return EINVAL; + ret = feeder_chain(c); + + if (ret == 0) + ret = CHANNEL_SETFORMAT(c->methods, c->devinfo, + sndbuf_getfmt(c->bufhard)); + + if (ret == 0) + ret = chn_resizebuf(c, -2, 0, 0); + + return (ret); } int -chn_setspeed(struct pcm_channel *c, int speed) +chn_setspeed(struct pcm_channel *c, uint32_t speed) { - int r, oldspeed = c->speed; + uint32_t oldspeed; + int ret; - r = chn_tryspeed(c, speed); - if (r) { + oldspeed = c->speed; + ret = chn_setparam(c, c->format, speed); + if (ret != 0) { if (snd_verbose > 3) - printf("Failed to set speed %d falling back to %d\n", - speed, oldspeed); - r = chn_tryspeed(c, oldspeed); + device_printf(c->dev, + "%s(): Setting speed %d failed, " + "falling back to %d\n", + __func__, speed, oldspeed); + chn_setparam(c, c->format, oldspeed); } - return r; + + return (ret); } -static int -chn_tryformat(struct pcm_channel *c, u_int32_t fmt) +int +chn_setformat(struct pcm_channel *c, uint32_t format) { - struct snd_dbuf *b = c->bufhard; - struct snd_dbuf *bs = c->bufsoft; - int r; + uint32_t oldformat; + int ret; - CHN_LOCKASSERT(c); - if (CHN_STOPPED(c)) { - DEB(printf("want format %d\n", fmt)); - c->format = fmt; - r = chn_buildfeeder(c); - if (r == 0) { - sndbuf_setfmt(bs, c->format); - chn_resetbuf(c); - r = CHANNEL_SETFORMAT(c->methods, c->devinfo, sndbuf_getfmt(b)); - if (r == 0) - r = chn_tryspeed(c, c->speed); - } - return r; - } else - return EINVAL; + if (format & AFMT_AC3) + format = SND_FORMAT(AFMT_AC3, 2, 0); + + oldformat = c->format; + ret = chn_setparam(c, format, c->speed); + if (ret != 0) { + if (snd_verbose > 3) + device_printf(c->dev, + "%s(): Format change 0x%08x failed, " + "falling back to 0x%08x\n", + __func__, format, oldformat); + chn_setparam(c, oldformat, c->speed); + } + + return (ret); } -int -chn_setformat(struct pcm_channel *c, u_int32_t fmt) +void +chn_syncstate(struct pcm_channel *c) { - u_int32_t oldfmt = c->format; - int r; + struct snddev_info *d; + struct snd_mixer *m; - r = chn_tryformat(c, fmt); - if (r) { - if (snd_verbose > 3) - printf("Format change 0x%08x failed, reverting to 0x%08x\n", - fmt, oldfmt); - chn_tryformat(c, oldfmt); + d = (c != NULL) ? c->parentsnddev : NULL; + m = (d != NULL && d->mixer_dev != NULL) ? d->mixer_dev->si_drv1 : + NULL; + + if (d == NULL || m == NULL) + return; + + CHN_LOCKASSERT(c); + + if (c->feederflags & (1 << FEEDER_VOLUME)) { + uint32_t parent; + int vol, pvol, left, right, center; + + if (c->direction == PCMDIR_PLAY && + (d->flags & SD_F_SOFTPCMVOL)) { + CHN_UNLOCK(c); + vol = mix_get(m, SOUND_MIXER_PCM); + parent = mix_getparent(m, SOUND_MIXER_PCM); + if (parent != SOUND_MIXER_NONE) + pvol = mix_get(m, parent); + else + pvol = 100 | (100 << 8); + CHN_LOCK(c); + } else { + vol = 100 | (100 << 8); + pvol = vol; + } + + if (vol == -1) { + device_printf(c->dev, + "Soft PCM Volume: Failed to read pcm " + "default value\n"); + vol = 100 | (100 << 8); + } + + if (pvol == -1) { + device_printf(c->dev, + "Soft PCM Volume: Failed to read parent " + "default value\n"); + pvol = 100 | (100 << 8); + } + + left = ((vol & 0x7f) * (pvol & 0x7f)) / 100; + right = (((vol >> 8) & 0x7f) * ((pvol >> 8) & 0x7f)) / 100; + center = (left + right) >> 1; + + chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, center); + } + + if (c->feederflags & (1 << FEEDER_EQ)) { + struct pcm_feeder *f; + int treble, bass, state; + + CHN_UNLOCK(c); + treble = mix_get(m, SOUND_MIXER_TREBLE); + bass = mix_get(m, SOUND_MIXER_BASS); + CHN_LOCK(c); + + if (treble == -1) + treble = 50; + else + treble = ((treble & 0x7f) + + ((treble >> 8) & 0x7f)) >> 1; + + if (bass == -1) + bass = 50; + else + bass = ((bass & 0x7f) + ((bass >> 8) & 0x7f)) >> 1; + + f = chn_findfeeder(c, FEEDER_EQ); + if (f != NULL) { + if (FEEDER_SET(f, FEEDEQ_TREBLE, treble) != 0) + device_printf(c->dev, + "EQ: Failed to set treble -- %d\n", + treble); + if (FEEDER_SET(f, FEEDEQ_BASS, bass) != 0) + device_printf(c->dev, + "EQ: Failed to set bass -- %d\n", + bass); + if (FEEDER_SET(f, FEEDEQ_PREAMP, d->eqpreamp) != 0) + device_printf(c->dev, + "EQ: Failed to set preamp -- %d\n", + d->eqpreamp); + if (d->flags & SD_F_EQ_BYPASSED) + state = FEEDEQ_BYPASS; + else if (d->flags & SD_F_EQ_ENABLED) + state = FEEDEQ_ENABLE; + else + state = FEEDEQ_DISABLE; + if (FEEDER_SET(f, FEEDEQ_STATE, state) != 0) + device_printf(c->dev, + "EQ: Failed to set state -- %d\n", state); + } } - return r; } int @@ -1772,10 +2141,11 @@ if (c->trigger != PCMTRIG_START) { c->trigger = go; CHN_UNLOCK(c); - pcm_lock(d); + PCM_LOCK(d); CHN_INSERT_HEAD(d, c, channels.pcm.busy); - pcm_unlock(d); + PCM_UNLOCK(d); CHN_LOCK(c); + chn_syncstate(c); } break; case PCMTRIG_STOP: @@ -1788,9 +2158,9 @@ if (c->trigger == PCMTRIG_START) { c->trigger = go; CHN_UNLOCK(c); - pcm_lock(d); + PCM_LOCK(d); CHN_REMOVE(d, c, channels.pcm.busy); - pcm_unlock(d); + PCM_UNLOCK(d); CHN_LOCK(c); } break; @@ -1841,238 +2211,19 @@ fmts |= fmtlist[i]; /* report software-supported formats */ - if (report_soft_formats) - fmts |= AFMT_MU_LAW|AFMT_A_LAW|AFMT_U32_LE|AFMT_U32_BE| - AFMT_S32_LE|AFMT_S32_BE|AFMT_U24_LE|AFMT_U24_BE| - AFMT_S24_LE|AFMT_S24_BE|AFMT_U16_LE|AFMT_U16_BE| - AFMT_S16_LE|AFMT_S16_BE|AFMT_U8|AFMT_S8; + if (!CHN_BITPERFECT(c) && report_soft_formats) + fmts |= AFMT_CONVERTIBLE; - return fmts; -} - -static int -chn_buildfeeder(struct pcm_channel *c) -{ - struct feeder_class *fc; - struct pcm_feederdesc desc; - struct snd_mixer *m; - u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; - int err; - char fmtstr[AFMTSTR_MAXSZ]; - - CHN_LOCKASSERT(c); - while (chn_removefeeder(c) == 0) - ; - KASSERT((c->feeder == NULL), ("feeder chain not empty")); - - c->align = sndbuf_getalign(c->bufsoft); - - if (CHN_EMPTY(c, children) || c->direction == PCMDIR_REC) { - /* - * Virtual rec need this. - */ - fc = feeder_getclass(NULL); - KASSERT(fc != NULL, ("can't find root feeder")); - - err = chn_addfeeder(c, fc, NULL); - if (err) { - DEB(printf("can't add root feeder, err %d\n", err)); - - return err; - } - c->feeder->desc->out = c->format; - } else if (c->direction == PCMDIR_PLAY) { - if (c->flags & CHN_F_HAS_VCHAN) { - desc.type = FEEDER_MIXER; - desc.in = c->format; - } else { - DEB(printf("can't decide which feeder type to use!\n")); - return EOPNOTSUPP; - } - desc.out = c->format; - desc.flags = 0; - fc = feeder_getclass(&desc); - if (fc == NULL) { - DEB(printf("can't find vchan feeder\n")); - - return EOPNOTSUPP; - } - - err = chn_addfeeder(c, fc, &desc); - if (err) { - DEB(printf("can't add vchan feeder, err %d\n", err)); - - return err; - } - } else - return EOPNOTSUPP; - - /* XXX These are too much.. */ - if (c->parentsnddev != NULL && c->parentsnddev->mixer_dev != NULL && - c->parentsnddev->mixer_dev->si_drv1 != NULL) - m = c->parentsnddev->mixer_dev->si_drv1; - else - m = NULL; - - c->feederflags &= ~(1 << FEEDER_VOLUME); - if (c->direction == PCMDIR_PLAY && !(c->flags & CHN_F_VIRTUAL) && m && - (c->parentsnddev->flags & SD_F_SOFTPCMVOL)) - c->feederflags |= 1 << FEEDER_VOLUME; - - if (!(c->flags & CHN_F_VIRTUAL) && c->parentsnddev && - ((c->direction == PCMDIR_PLAY && - (c->parentsnddev->flags & SD_F_PSWAPLR)) || - (c->direction == PCMDIR_REC && - (c->parentsnddev->flags & SD_F_RSWAPLR)))) - c->feederflags |= 1 << FEEDER_SWAPLR; - - flags = c->feederflags; - fmtlist = chn_getcaps(c)->fmtlist; - - DEB(printf("feederflags %x\n", flags)); - - for (type = FEEDER_RATE; type < FEEDER_LAST; type++) { - if (flags & (1 << type)) { - desc.type = type; - desc.in = 0; - desc.out = 0; - desc.flags = 0; - DEB(printf("find feeder type %d, ", type)); - if (type == FEEDER_VOLUME || type == FEEDER_RATE) { - if (c->feeder->desc->out & AFMT_32BIT) - strlcpy(fmtstr,"s32le", sizeof(fmtstr)); - else if (c->feeder->desc->out & AFMT_24BIT) - strlcpy(fmtstr, "s24le", sizeof(fmtstr)); - else { - /* - * 8bit doesn't provide enough headroom - * for proper processing without - * creating too much noises. Force to - * 16bit instead. - */ - strlcpy(fmtstr, "s16le", sizeof(fmtstr)); - } - if (!(c->feeder->desc->out & AFMT_8BIT) && - c->feeder->desc->out & AFMT_BIGENDIAN) - afmtstr_swap_endian(fmtstr); - if (!(c->feeder->desc->out & (AFMT_A_LAW | AFMT_MU_LAW)) && - !(c->feeder->desc->out & AFMT_SIGNED)) - afmtstr_swap_sign(fmtstr); - desc.in = afmtstr2afmt(NULL, fmtstr, AFMTSTR_MONO_RETURN); - if (desc.in == 0) - desc.in = AFMT_S16_LE; - /* feeder_volume need stereo processing */ - if (type == FEEDER_VOLUME || - c->feeder->desc->out & AFMT_STEREO) - desc.in |= AFMT_STEREO; - desc.out = desc.in; - } else if (type == FEEDER_SWAPLR) { - desc.in = c->feeder->desc->out; - desc.in |= AFMT_STEREO; - desc.out = desc.in; - } - - fc = feeder_getclass(&desc); - DEB(printf("got %p\n", fc)); - if (fc == NULL) { - DEB(printf("can't find required feeder type %d\n", type)); - - return EOPNOTSUPP; - } - - if (desc.in == 0 || desc.out == 0) - desc = *fc->desc; - - DEB(printf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in)); - tmp[0] = desc.in; - tmp[1] = 0; - if (chn_fmtchain(c, tmp) == 0) { - DEB(printf("failed\n")); - - return ENODEV; - } - DEB(printf("ok\n")); - - err = chn_addfeeder(c, fc, &desc); - if (err) { - DEB(printf("can't add feeder %p, output 0x%x, err %d\n", fc, fc->desc->out, err)); - - return err; - } - DEB(printf("added feeder %p, output 0x%x\n", fc, c->feeder->desc->out)); - } - } - - if (c->direction == PCMDIR_REC) { - tmp[0] = c->format; - tmp[1] = 0; - hwfmt = chn_fmtchain(c, tmp); - } else - hwfmt = chn_fmtchain(c, fmtlist); - - if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) { - DEB(printf("Invalid hardware format: 0x%08x\n", hwfmt)); - return ENODEV; - } else if (c->direction == PCMDIR_REC && !CHN_EMPTY(c, children)) { - /* - * Kind of awkward. This whole "MIXER" concept need a - * rethinking, I guess :) . Recording is the inverse - * of Playback, which is why we push mixer vchan down here. - */ - if (c->flags & CHN_F_HAS_VCHAN) { - desc.type = FEEDER_MIXER; - desc.in = c->format; - } else - return EOPNOTSUPP; - desc.out = c->format; - desc.flags = 0; - fc = feeder_getclass(&desc); - if (fc == NULL) - return EOPNOTSUPP; - - err = chn_addfeeder(c, fc, &desc); - if (err != 0) - return err; - } - - sndbuf_setfmt(c->bufhard, hwfmt); - - if ((flags & (1 << FEEDER_VOLUME))) { - u_int32_t parent; - int vol, left, right; - - CHN_UNLOCK(c); - vol = mix_get(m, SOUND_MIXER_PCM); - if (vol == -1) { - device_printf(c->dev, - "Soft PCM Volume: Failed to read default value\n"); - vol = 100 | (100 << 8); - } - left = vol & 0x7f; - right = (vol >> 8) & 0x7f; - parent = mix_getparent(m, SOUND_MIXER_PCM); - if (parent != SOUND_MIXER_NONE) { - vol = mix_get(m, parent); - if (vol == -1) { - device_printf(c->dev, - "Soft Volume: Failed to read parent " - "default value\n"); - vol = 100 | (100 << 8); - } - left = (left * (vol & 0x7f)) / 100; - right = (right * ((vol >> 8) & 0x7f)) / 100; - } - CHN_LOCK(c); - chn_setvolume(c, left, right); - } - - return 0; + return (AFMT_ENCODING(fmts)); } int chn_notify(struct pcm_channel *c, u_int32_t flags) { - int err, run, nrun; + struct pcm_channel *ch; + struct pcmchan_caps *caps; + uint32_t bestformat, bestspeed, besthwformat; + int dirty, err, run, nrun; CHN_LOCKASSERT(c); @@ -2090,13 +2241,25 @@ flags &= CHN_N_VOLUME | CHN_N_TRIGGER; if (flags & CHN_N_RATE) { - /* XXX I'll make good use of this someday. */ + /* + * XXX I'll make good use of this someday. + * However this is currently being superseded by + * the availability of CHN_F_DYNAMIC_VCHAN. + */ } if (flags & CHN_N_FORMAT) { - /* XXX I'll make good use of this someday. */ + /* + * XXX I'll make good use of this someday. + * However this is currently being superseded by + * the availability of CHN_F_DYNAMIC_VCHAN. + */ } if (flags & CHN_N_VOLUME) { - /* XXX I'll make good use of this someday. */ + /* + * XXX I'll make good use of this someday, though + * soft volume control is currently pretty much + * integrated. + */ } if (flags & CHN_N_BLOCKSIZE) { /* @@ -2104,12 +2267,70 @@ */ chn_setlatency(c, chn_latency); } - if (flags & CHN_N_TRIGGER) { + if ((flags & CHN_N_TRIGGER) && !(c->flags & CHN_F_DYNAMIC_VCHAN)) { nrun = CHN_EMPTY(c, children.busy) ? 0 : 1; if (nrun && !run) err = chn_start(c, 1); if (!nrun && run) chn_abort(c); + flags &= ~CHN_N_TRIGGER; + } + if (flags & CHN_N_TRIGGER) { + /* Dynamic Virtual Channel */ + bestformat = 0; + bestspeed = 0; + besthwformat = 0; + nrun = 0; + caps = chn_getcaps(c); + dirty = 0; + + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + if (ch->speed > bestspeed) { + bestspeed = ch->speed; + RANGE(bestspeed, caps->minspeed, + caps->maxspeed); + } + besthwformat = snd_fmtbest(ch->format, caps->fmtlist); + if (AFMT_CHANNEL(besthwformat) > + AFMT_CHANNEL(bestformat)) + bestformat = besthwformat; + else if (AFMT_CHANNEL(besthwformat) == + AFMT_CHANNEL(bestformat) && + AFMT_BIT(besthwformat) > AFMT_BIT(bestformat)) + bestformat = besthwformat; + CHN_UNLOCK(ch); + nrun++; + } + + if (bestformat == 0) + bestformat = sndbuf_getfmt(c->bufhard); + if (bestspeed == 0) + bestspeed = sndbuf_getspd(c->bufhard); + + if (bestformat != sndbuf_getfmt(c->bufhard) || + bestspeed != sndbuf_getspd(c->bufhard)) + dirty = 1; + + if (nrun && !run) { + if (dirty) + err = chn_reset(c, bestformat, bestspeed); + if (err == 0) { + if (dirty) + c->flags |= CHN_F_DIRTY; + err = chn_start(c, 1); + } + } + if (nrun && run && dirty) { + chn_abort(c); + err = chn_reset(c, bestformat, bestspeed); + if (err == 0) { + c->flags |= CHN_F_DIRTY; + err = chn_start(c, 1); + } + } + if (!nrun && run) + chn_abort(c); } return (err); @@ -2186,18 +2407,6 @@ return sg_id; } -void -chn_lock(struct pcm_channel *c) -{ - CHN_LOCK(c); -} - -void -chn_unlock(struct pcm_channel *c) -{ - CHN_UNLOCK(c); -} - #ifdef OSSV4_EXPERIMENT int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak) --- sys/dev/sound/pcm/channel.h.orig 2008-11-06 23:05:15.000000000 +0800 +++ sys/dev/sound/pcm/channel.h 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Portions Copyright (c) Ryan Beasley - GSoC 2006 + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,6 +34,17 @@ u_int32_t caps; }; +struct pcmchan_matrix { + int id; + uint8_t channels, ext; + struct { + int type; + uint32_t members; + } map[SND_CHN_T_MAX + 1]; + uint32_t mask; + int8_t offset[SND_CHN_T_MAX]; +}; + /* Forward declarations */ struct pcm_channel; struct pcmchan_syncgroup; @@ -63,7 +76,10 @@ struct pcm_channel *ch; }; -#define CHN_NAMELEN 32 +#define CHN_NAMELEN 32 +#define CHN_COMM_UNUSED "" +#define CHN_COMM_UNKNOWN "" + struct pcm_channel { kobj_t methods; @@ -72,7 +88,6 @@ struct pcm_feeder *feeder; u_int32_t align; - int volume; int latency; u_int32_t speed; u_int32_t format; @@ -90,6 +105,7 @@ device_t dev; int unit; char name[CHN_NAMELEN]; + char comm[MAXCOMLEN + 1]; struct mtx *lock; int trigger; /** @@ -139,9 +155,16 @@ struct { SLIST_ENTRY(pcm_channel) link; } busy; + struct { + SLIST_ENTRY(pcm_channel) link; + } opened; } pcm; } channels; + struct pcmchan_matrix matrix; + + int volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX]; + void *data1, *data2; }; @@ -172,9 +195,8 @@ if (t == y) \ break; \ } \ - if (t != y) { \ + if (t != y) \ CHN_INSERT_HEAD(x, y, z); \ - } \ } while(0) #define CHN_INSERT_AFTER_SAFE(w, x, y, z) do { \ @@ -183,9 +205,8 @@ if (t == y) \ break; \ } \ - if (t != y) { \ + if (t != y) \ CHN_INSERT_AFTER(x, y, z); \ - } \ } while(0) #define CHN_REMOVE_SAFE(x, y, z) do { \ @@ -194,11 +215,27 @@ if (t == y) \ break; \ } \ - if (t == y) { \ + if (t == y) \ CHN_REMOVE(x, y, z); \ +} while(0) + +#define CHN_INSERT_SORT(w, x, y, z) do { \ + struct pcm_channel *t, *a = NULL; \ + CHN_FOREACH(t, x, z) { \ + if ((y)->unit w t->unit) \ + a = t; \ + else \ + break; \ } \ + if (a != NULL) \ + CHN_INSERT_AFTER(a, y, z); \ + else \ + CHN_INSERT_HEAD(x, y, z); \ } while(0) +#define CHN_INSERT_SORT_ASCEND(x, y, z) CHN_INSERT_SORT(>, x, y, z) +#define CHN_INSERT_SORT_DESCEND(x, y, z) CHN_INSERT_SORT(<, x, y, z) + #define CHN_UNIT(x) (snd_unit2u((x)->unit)) #define CHN_DEV(x) (snd_unit2d((x)->unit)) #define CHN_CHAN(x) (snd_unit2c((x)->unit)) @@ -225,19 +262,33 @@ int chn_init(struct pcm_channel *c, void *devinfo, int dir, int direction); int chn_kill(struct pcm_channel *c); -int chn_setdir(struct pcm_channel *c, int dir); -int chn_reset(struct pcm_channel *c, u_int32_t fmt); +int chn_reset(struct pcm_channel *c, u_int32_t fmt, u_int32_t spd); int chn_setvolume(struct pcm_channel *c, int left, int right); -int chn_setspeed(struct pcm_channel *c, int speed); -int chn_setformat(struct pcm_channel *c, u_int32_t fmt); +int chn_setvolume_multi(struct pcm_channel *c, int vc, int left, int right, + int center); +int chn_setvolume_matrix(struct pcm_channel *c, int vc, int vt, int val); +int chn_getvolume_matrix(struct pcm_channel *c, int vc, int vt); +void chn_vpc_reset(struct pcm_channel *c, int vc, int force); +int chn_setparam(struct pcm_channel *c, uint32_t format, uint32_t speed); +int chn_setspeed(struct pcm_channel *c, uint32_t speed); +int chn_setformat(struct pcm_channel *c, uint32_t format); int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz); int chn_setlatency(struct pcm_channel *c, int latency); +void chn_syncstate(struct pcm_channel *c); int chn_trigger(struct pcm_channel *c, int go); int chn_getptr(struct pcm_channel *c); struct pcmchan_caps *chn_getcaps(struct pcm_channel *c); u_int32_t chn_getformats(struct pcm_channel *c); +struct pcmchan_matrix *chn_getmatrix(struct pcm_channel *); +int chn_setmatrix(struct pcm_channel *, struct pcmchan_matrix *); + +int chn_oss_getorder(struct pcm_channel *, unsigned long long *); +int chn_oss_setorder(struct pcm_channel *, unsigned long long *); +int chn_oss_getmask(struct pcm_channel *, uint32_t *); + void chn_resetbuf(struct pcm_channel *c); +void chn_intr_locked(struct pcm_channel *c); void chn_intr(struct pcm_channel *c); int chn_wrfeed(struct pcm_channel *c); int chn_rdfeed(struct pcm_channel *c); @@ -247,55 +298,40 @@ void chn_rdupdate(struct pcm_channel *c); int chn_notify(struct pcm_channel *c, u_int32_t flags); -void chn_lock(struct pcm_channel *c); -void chn_unlock(struct pcm_channel *c); int chn_getrates(struct pcm_channel *c, int **rates); int chn_syncdestroy(struct pcm_channel *c); -#ifdef OSSV4_EXPERIMENT -int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak); -#endif - -#ifdef USING_MUTEX -#define CHN_LOCK_OWNED(c) mtx_owned((struct mtx *)((c)->lock)) -#define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock)) -#define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock)) -#define CHN_TRYLOCK(c) mtx_trylock((struct mtx *)((c)->lock)) -#define CHN_LOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_OWNED) +#define CHN_SETVOLUME(x...) chn_setvolume_matrix(x) +#if defined(SND_DIAGNOSTIC) || defined(INVARIANTS) +#define CHN_GETVOLUME(x...) chn_getvolume_matrix(x) #else -#define CHN_LOCK_OWNED(c) 0 -#define CHN_LOCK(c) -#define CHN_UNLOCK(c) -#define CHN_TRYLOCK(c) -#define CHN_LOCKASSERT(c) +#define CHN_GETVOLUME(x, y, z) ((x)->volume[y][z]) #endif -int fmtvalid(u_int32_t fmt, u_int32_t *fmtlist); +#ifdef OSSV4_EXPERIMENT +int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak); +#endif -#define AFMTSTR_NONE 0 /* "s16le" */ -#define AFMTSTR_SIMPLE 1 /* "s16le:s" */ -#define AFMTSTR_NUM 2 /* "s16le:2" */ -#define AFMTSTR_FULL 3 /* "s16le:stereo" */ +#define CHN_LOCKOWNED(c) mtx_owned((struct mtx *)((c)->lock)) +#define CHN_LOCK(c) mtx_lock((struct mtx *)((c)->lock)) +#define CHN_UNLOCK(c) mtx_unlock((struct mtx *)((c)->lock)) +#define CHN_TRYLOCK(c) mtx_trylock((struct mtx *)((c)->lock)) +#define CHN_LOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_OWNED) +#define CHN_UNLOCKASSERT(c) mtx_assert((struct mtx *)((c)->lock), MA_NOTOWNED) -#define AFMTSTR_MAXSZ 13 /* include null terminator */ +int snd_fmtvalid(uint32_t fmt, uint32_t *fmtlist); -#define AFMTSTR_MONO_RETURN 0 -#define AFMTSTR_STEREO_RETURN 1 +uint32_t snd_str2afmt(const char *); +uint32_t snd_afmt2str(uint32_t, char *, size_t); -struct afmtstr_table { - char *fmtstr; - u_int32_t format; -}; +#define AFMTSTR_LEN 16 -int afmtstr_swap_sign(char *); -int afmtstr_swap_endian(char *); -u_int32_t afmtstr2afmt(struct afmtstr_table *, const char *, int); -u_int32_t afmt2afmtstr(struct afmtstr_table *, u_int32_t, char *, size_t, int, int); extern int chn_latency; extern int chn_latency_profile; extern int report_soft_formats; +extern int report_soft_matrix; #define PCMDIR_FAKE 0 #define PCMDIR_PLAY 1 @@ -328,11 +364,15 @@ #define CHN_F_BADSETTING 0x00040000 #define CHN_F_SETBLOCKSIZE 0x00080000 #define CHN_F_HAS_VCHAN 0x00100000 +#define CHN_F_DYNAMIC_VCHAN 0x00200000 +#define CHN_F_DIRTY 0x00400000 /* DIRTY */ #define CHN_F_VIRTUAL 0x10000000 /* not backed by hardware */ +#define CHN_F_BITPERFECT 0x20000000 #define CHN_F_RESET (CHN_F_BUSY | CHN_F_DEAD | \ - CHN_F_HAS_VCHAN | CHN_F_VIRTUAL) + CHN_F_VIRTUAL | CHN_F_HAS_VCHAN | \ + CHN_F_DYNAMIC_VCHAN) #define CHN_F_MMAP_INVALID (CHN_F_DEAD | CHN_F_RUNNING) @@ -359,6 +399,8 @@ #define CHN_STOPPED(c) (!CHN_STARTED(c)) #define CHN_DIRSTR(c) (((c)->direction == PCMDIR_PLAY) ? \ "PCMDIR_PLAY" : "PCMDIR_REC") +#define CHN_BITPERFECT(c) ((c)->flags & CHN_F_BITPERFECT) +#define CHN_PASSTHROUGH(c) (!((c)->format & AFMT_CONVERTIBLE)) #define CHN_TIMEOUT 5 #define CHN_TIMEOUT_MIN 1 --- sys/dev/sound/pcm/channel_if.m.orig 2007-03-17 01:16:24.000000000 +0800 +++ sys/dev/sound/pcm/channel_if.m 2009-01-17 19:29:20.000000000 +0800 @@ -1,7 +1,9 @@ #- # KOBJ # -# Copyright (c) 2000 Cameron Grant +# Copyright (c) 2005-2009 Ariff Abdullah +# Portions Copyright (c) Ryan Beasley - GSoC 2006 +# Copyright (c) 2000 Cameron Grant # All rights reserved. # # Redistribution and use in source and binary forms, with or without @@ -35,12 +37,6 @@ CODE { static int - channel_nosetdir(kobj_t obj, void *data, int dir) - { - return 0; - } - - static int channel_noreset(kobj_t obj, void *data) { return 0; @@ -89,6 +85,18 @@ return 0; } + static struct pcmchan_matrix * + channel_nogetmatrix(kobj_t obj, void *data, u_int32_t format) + { + format = feeder_matrix_default_format(format); + return (feeder_matrix_format_map(format)); + } + + static int + channel_nosetmatrix(kobj_t obj, void *data, struct pcmchan_matrix *m) + { + return EINVAL; + } }; METHOD void* init { @@ -114,12 +122,6 @@ void *data; } DEFAULT channel_noresetdone; -METHOD int setdir { - kobj_t obj; - void *data; - int dir; -} DEFAULT channel_nosetdir; - METHOD u_int32_t setformat { kobj_t obj; void *data; @@ -217,3 +219,15 @@ void *data; int **rates; } DEFAULT channel_nogetrates; + +METHOD struct pcmchan_matrix * getmatrix { + kobj_t obj; + void *data; + u_int32_t format; +} DEFAULT channel_nogetmatrix; + +METHOD int setmatrix { + kobj_t obj; + void *data; + struct pcmchan_matrix *m; +} DEFAULT channel_nosetmatrix; --- sys/dev/sound/pcm/dsp.c.orig 2009-01-17 19:31:45.000000000 +0800 +++ sys/dev/sound/pcm/dsp.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Portions Copyright (c) Ryan Beasley - GSoC 2006 + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,21 +28,25 @@ #include #include +#include SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/dsp.c,v 1.113 2009/01/10 20:49:15 mav Exp $"); static int dsp_mmap_allow_prot_exec = 0; SYSCTL_INT(_hw_snd, OID_AUTO, compat_linux_mmap, CTLFLAG_RW, - &dsp_mmap_allow_prot_exec, 0, "linux mmap compatibility"); + &dsp_mmap_allow_prot_exec, 0, + "linux mmap compatibility (-1=force disable 0=auto 1=force enable)"); struct dsp_cdevinfo { struct pcm_channel *rdch, *wrch; + struct pcm_channel *volch; int busy, simplex; TAILQ_ENTRY(dsp_cdevinfo) link; }; #define PCM_RDCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->rdch) #define PCM_WRCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->wrch) +#define PCM_VOLCH(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->volch) #define PCM_SIMPLEX(x) (((struct dsp_cdevinfo *)(x)->si_drv1)->simplex) #define DSP_CDEVINFO_CACHESIZE 8 @@ -70,19 +76,18 @@ .d_name = "dsp", }; -#ifdef USING_DEVFS static eventhandler_tag dsp_ehtag = NULL; static int dsp_umax = -1; static int dsp_cmax = -1; -#endif static int dsp_oss_syncgroup(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_syncgroup *group); static int dsp_oss_syncstart(int sg_id); static int dsp_oss_policy(struct pcm_channel *wrch, struct pcm_channel *rdch, int policy); -#ifdef OSSV4_EXPERIMENT static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled); static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map); +static int dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, int *mask); +#ifdef OSSV4_EXPERIMENT static int dsp_oss_getlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); static int dsp_oss_setlabel(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_label_t *label); static int dsp_oss_getsong(struct pcm_channel *wrch, struct pcm_channel *rdch, oss_longname_t *song); @@ -133,7 +138,7 @@ d = dsp_get_info(dev); if (!PCM_REGISTERED(d)) return (ENXIO); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); PCM_ACQUIRE(d); /* @@ -173,7 +178,7 @@ pcm_chnrelease(ch); } PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); } *rdch = PCM_RDCH(dev); @@ -200,7 +205,8 @@ static void dsp_cdevinfo_alloc(struct cdev *dev, - struct pcm_channel *rdch, struct pcm_channel *wrch) + struct pcm_channel *rdch, struct pcm_channel *wrch, + struct pcm_channel *volch) { struct snddev_info *d; struct dsp_cdevinfo *cdi; @@ -209,7 +215,7 @@ d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 == NULL && - rdch != wrch, + ((rdch == NULL && wrch == NULL) || rdch != wrch), ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); mtx_assert(d->lock, MA_OWNED); @@ -225,6 +231,7 @@ break; cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_REMOVE(&d->dsp_cdevinfo_pool, cdi, link); @@ -232,11 +239,12 @@ dev->si_drv1 = cdi; return; } - pcm_unlock(d); + PCM_UNLOCK(d); cdi = malloc(sizeof(*cdi), M_DEVBUF, M_WAITOK | M_ZERO); - pcm_lock(d); + PCM_LOCK(d); cdi->rdch = rdch; cdi->wrch = wrch; + cdi->volch = volch; cdi->simplex = simplex; cdi->busy = 1; TAILQ_INSERT_TAIL(&d->dsp_cdevinfo_pool, cdi, link); @@ -254,7 +262,8 @@ d = dsp_get_info(dev); KASSERT(PCM_REGISTERED(d) && dev != NULL && dev->si_drv1 != NULL && - PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL, + PCM_RDCH(dev) == NULL && PCM_WRCH(dev) == NULL && + PCM_VOLCH(dev) == NULL, ("bogus %s(), what are you trying to accomplish here?", __func__)); PCM_BUSYASSERT(d); mtx_assert(d->lock, MA_OWNED); @@ -263,6 +272,7 @@ dev->si_drv1 = NULL; cdi->rdch = NULL; cdi->wrch = NULL; + cdi->volch = NULL; cdi->simplex = 0; cdi->busy = 0; @@ -306,7 +316,7 @@ KASSERT(d != NULL, ("NULL snddev_info")); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_NOTOWNED); + PCM_UNLOCKASSERT(d); TAILQ_INIT(&d->dsp_cdevinfo_pool); for (i = 0; i < DSP_CDEVINFO_CACHESIZE; i++) { @@ -322,7 +332,7 @@ KASSERT(d != NULL, ("NULL snddev_info")); PCM_BUSYASSERT(d); - mtx_assert(d->lock, MA_NOTOWNED); + PCM_UNLOCKASSERT(d); cdi = TAILQ_FIRST(&d->dsp_cdevinfo_pool); while (cdi != NULL) { @@ -340,6 +350,12 @@ DSP_CDEV_TYPE_RDWR, /* duplex read, write, or both */ }; +enum { + DSP_CDEV_VOLCTL_NONE, + DSP_CDEV_VOLCTL_READ, + DSP_CDEV_VOLCTL_WRITE, +}; + #define DSP_F_VALID(x) ((x) & (FREAD | FWRITE)) #define DSP_F_DUPLEX(x) (((x) & (FREAD | FWRITE)) == (FREAD | FWRITE)) #define DSP_F_SIMPLEX(x) (!DSP_F_DUPLEX(x)) @@ -350,30 +366,49 @@ int type; char *name; char *sep; + char *alias; int use_sep; int hw; int max; + int volctl; uint32_t fmt, spd; int query; } dsp_cdevs[] = { - { SND_DEV_DSP, "dsp", ".", 0, 0, 0, - AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_AUDIO, "audio", ".", 0, 0, 0, - AFMT_MU_LAW, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP16, "dspW", ".", 0, 0, 0, - AFMT_S16_LE, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSPHW_PLAY, "dsp", ".p", 1, 1, SND_MAXHWCHAN, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", 1, 1, SND_MAXVCHANS, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_REC, "dsp", ".r", 1, 1, SND_MAXHWCHAN, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_VREC, "dsp", ".vr", 1, 1, SND_MAXVCHANS, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_CD, "dspcd", ".", 0, 0, 0, - AFMT_S16_LE | AFMT_STEREO, 44100, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP_MMAP, "dsp_mmap", ".", 0, 0, 0, - AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp", ".", NULL, 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_AUDIO, "audio", ".", NULL, 0, 0, 0, 0, + SND_FORMAT(AFMT_MU_LAW, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP16, "dspW", ".", NULL, 0, 0, 0, 0, + SND_FORMAT(AFMT_S16_LE, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSPHW_PLAY, "dsp", ".p", NULL, 1, 1, SND_MAXHWCHAN, 1, + SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY }, + { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", NULL, 1, 1, SND_MAXVCHANS, 1, + SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_WRONLY }, + { SND_DEV_DSPHW_REC, "dsp", ".r", NULL, 1, 1, SND_MAXHWCHAN, 1, + SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY }, + { SND_DEV_DSPHW_VREC, "dsp", ".vr", NULL, 1, 1, SND_MAXVCHANS, 1, + SND_FORMAT(AFMT_S16_LE, 2, 0), 48000, DSP_CDEV_TYPE_RDONLY }, + { SND_DEV_DSPHW_CD, "dspcd", ".", NULL, 0, 0, 0, 0, + SND_FORMAT(AFMT_S16_LE, 2, 0), 44100, DSP_CDEV_TYPE_RDWR }, + /* Low priority, OSSv4 aliases. */ + { SND_DEV_DSP, "dsp_ac3", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_mmap", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_multich", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifout", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifin", ".", "dsp", 0, 0, 0, 0, + SND_FORMAT(AFMT_U8, 1, 0), DSP_DEFAULT_SPEED, + DSP_CDEV_TYPE_RDWR }, }; #define DSP_FIXUP_ERROR() do { \ @@ -397,7 +432,7 @@ { struct pcm_channel *rdch, *wrch; struct snddev_info *d; - uint32_t fmt, spd, prio; + uint32_t fmt, spd, prio, volctl; int i, error, rderror, wrerror, devtype, wdevunit, rdevunit; /* Kind of impossible.. */ @@ -411,7 +446,7 @@ PCM_GIANT_ENTER(d); /* Lock snddev so nobody else can monkey with it. */ - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); /* @@ -421,7 +456,7 @@ error = snd_clone_acquire(i_dev); if (!(error == 0 || error == ENODEV)) { DSP_FIXUP_ERROR(); - pcm_unlock(d); + PCM_UNLOCK(d); PCM_GIANT_EXIT(d); return (error); } @@ -431,7 +466,7 @@ if (error != 0) { (void)snd_clone_release(i_dev); - pcm_unlock(d); + PCM_UNLOCK(d); PCM_GIANT_EXIT(d); return (error); } @@ -442,34 +477,53 @@ * everything. */ PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); devtype = PCMDEV(i_dev); wdevunit = -1; rdevunit = -1; fmt = 0; spd = 0; + volctl = DSP_CDEV_VOLCTL_NONE; for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (devtype != dsp_cdevs[i].type) + if (devtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; - if (DSP_F_SIMPLEX(flags) && - ((dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY && - DSP_F_READ(flags)) || - (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY && - DSP_F_WRITE(flags)))) { - /* - * simplex, opposite direction? Please be gone.. - */ - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return (ENOTSUP); - } - if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) + /* + * Volume control only valid for DSPHW devices, + * and it must be opened in opposite direction be it + * simplex or duplex. Anything else will be handled + * as usual. + */ + if (dsp_cdevs[i].query == DSP_CDEV_TYPE_WRONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_READ(flags)) { + volctl = DSP_CDEV_VOLCTL_WRITE; + flags &= ~FREAD; + flags |= FWRITE; + } + if (DSP_F_READ(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } wdevunit = dev2unit(i_dev); - else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) + } else if (dsp_cdevs[i].query == DSP_CDEV_TYPE_RDONLY) { + if (dsp_cdevs[i].volctl != 0 && + DSP_F_WRITE(flags)) { + volctl = DSP_CDEV_VOLCTL_READ; + flags &= ~FWRITE; + flags |= FREAD; + } + if (DSP_F_WRITE(flags)) { + (void)snd_clone_release(i_dev); + PCM_RELEASE_QUICK(d); + PCM_GIANT_EXIT(d); + return (ENOTSUP); + } rdevunit = dev2unit(i_dev); + } fmt = dsp_cdevs[i].fmt; spd = dsp_cdevs[i].spd; break; @@ -493,12 +547,14 @@ if (DSP_F_READ(flags)) { /* open for read */ rderror = pcm_chnalloc(d, &rdch, PCMDIR_REC, - td->td_proc->p_pid, rdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, rdevunit); - if (rderror == 0 && (chn_reset(rdch, fmt) != 0 || - (chn_setspeed(rdch, spd) != 0))) + if (rderror == 0 && chn_reset(rdch, fmt, spd) != 0) rderror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_READ) + rderror = 0; + if (rderror != 0) { if (rdch != NULL) pcm_chnrelease(rdch); @@ -509,10 +565,17 @@ return (rderror); } rdch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_READ) { + if (rdch != NULL) { + pcm_chnref(rdch, 1); + pcm_chnrelease(rdch); + } } else { if (flags & O_NONBLOCK) rdch->flags |= CHN_F_NBIO; pcm_chnref(rdch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(rdch, SND_VOL_C_PCM, 0); CHN_UNLOCK(rdch); } } @@ -520,12 +583,14 @@ if (DSP_F_WRITE(flags)) { /* open for write */ wrerror = pcm_chnalloc(d, &wrch, PCMDIR_PLAY, - td->td_proc->p_pid, wdevunit); + td->td_proc->p_pid, td->td_proc->p_comm, wdevunit); - if (wrerror == 0 && (chn_reset(wrch, fmt) != 0 || - (chn_setspeed(wrch, spd) != 0))) + if (wrerror == 0 && chn_reset(wrch, fmt, spd) != 0) wrerror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_WRITE) + wrerror = 0; + if (wrerror != 0) { if (wrch != NULL) pcm_chnrelease(wrch); @@ -545,27 +610,56 @@ return (wrerror); } wrch = NULL; + } else if (volctl == DSP_CDEV_VOLCTL_WRITE) { + if (wrch != NULL) { + pcm_chnref(wrch, 1); + pcm_chnrelease(wrch); + } } else { if (flags & O_NONBLOCK) wrch->flags |= CHN_F_NBIO; pcm_chnref(wrch, 1); + if (volctl == DSP_CDEV_VOLCTL_NONE) + chn_vpc_reset(wrch, SND_VOL_C_PCM, 0); CHN_UNLOCK(wrch); } } - if (rdch == NULL && wrch == NULL) { - (void)snd_clone_release(i_dev); - PCM_RELEASE_QUICK(d); - PCM_GIANT_EXIT(d); - return ((wrerror != 0) ? wrerror : rderror); - } - pcm_lock(d); + PCM_LOCK(d); /* * We're done. Allocate channels information for this cdev. */ - dsp_cdevinfo_alloc(i_dev, rdch, wrch); + switch (volctl) { + case DSP_CDEV_VOLCTL_READ: + KASSERT(wrch == NULL, ("wrch=%p not null!", wrch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, rdch); + break; + case DSP_CDEV_VOLCTL_WRITE: + KASSERT(rdch == NULL, ("rdch=%p not null!", rdch)); + dsp_cdevinfo_alloc(i_dev, NULL, NULL, wrch); + break; + case DSP_CDEV_VOLCTL_NONE: + default: + if (wrch == NULL && rdch == NULL) { + (void)snd_clone_release(i_dev); + PCM_RELEASE(d); + PCM_UNLOCK(d); + PCM_GIANT_EXIT(d); + if (wrerror != 0) + return (wrerror); + if (rderror != 0) + return (rderror); + return (EINVAL); + } + dsp_cdevinfo_alloc(i_dev, rdch, wrch, NULL); + if (rdch != NULL) + CHN_INSERT_HEAD(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_INSERT_HEAD(d, wrch, channels.pcm.opened); + break; + } /* * Increase clone refcount for its automatic garbage collector. @@ -573,7 +667,7 @@ (void)snd_clone_ref(i_dev); PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); PCM_GIANT_LEAVE(d); @@ -583,9 +677,9 @@ static int dsp_close(struct cdev *i_dev, int flags, int mode, struct thread *td) { - struct pcm_channel *rdch, *wrch; + struct pcm_channel *rdch, *wrch, *volch; struct snddev_info *d; - int sg_ids, refs; + int sg_ids, rdref, wdref; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -593,26 +687,51 @@ PCM_GIANT_ENTER(d); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); + PCM_ACQUIRE(d); rdch = PCM_RDCH(i_dev); wrch = PCM_WRCH(i_dev); + volch = PCM_VOLCH(i_dev); - if (rdch || wrch) { - PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_RDCH(i_dev) = NULL; + PCM_WRCH(i_dev) = NULL; + PCM_VOLCH(i_dev) = NULL; + + rdref = -1; + wdref = -1; + + if (volch != NULL) { + if (volch == rdch) + rdref--; + else if (volch == wrch) + wdref--; + else { + CHN_LOCK(volch); + pcm_chnref(volch, -1); + CHN_UNLOCK(volch); + } + } - refs = 0; - if (rdch) { + if (rdch != NULL) + CHN_REMOVE(d, rdch, channels.pcm.opened); + if (wrch != NULL) + CHN_REMOVE(d, wrch, channels.pcm.opened); + + if (rdch != NULL || wrch != NULL) { + PCM_UNLOCK(d); + if (rdch != NULL) { /* * The channel itself need not be locked because: - * a) Adding a channel to a syncgroup happens only in dsp_ioctl(), - * which cannot run concurrently to dsp_close(). - * b) The syncmember pointer (sm) is protected by the global - * syncgroup list lock. - * c) A channel can't just disappear, invalidating pointers, - * unless it's closed/dereferenced first. + * a) Adding a channel to a syncgroup happens only + * in dsp_ioctl(), which cannot run concurrently + * to dsp_close(). + * b) The syncmember pointer (sm) is protected by + * the global syncgroup list lock. + * c) A channel can't just disappear, invalidating + * pointers, unless it's closed/dereferenced + * first. */ PCM_SG_LOCK(); sg_ids = chn_syncdestroy(rdch); @@ -621,14 +740,14 @@ free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(rdch); - refs += pcm_chnref(rdch, -1); + pcm_chnref(rdch, rdref); chn_abort(rdch); /* won't sleep */ - rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(rdch, 0); + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | + CHN_F_DEAD); + chn_reset(rdch, 0, 0); pcm_chnrelease(rdch); - PCM_RDCH(i_dev) = NULL; } - if (wrch) { + if (wrch != NULL) { /* * Please see block above. */ @@ -639,41 +758,34 @@ free_unr(pcmsg_unrhdr, sg_ids); CHN_LOCK(wrch); - refs += pcm_chnref(wrch, -1); + pcm_chnref(wrch, wdref); chn_flush(wrch); /* may sleep */ - wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | CHN_F_DEAD); - chn_reset(wrch, 0); + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | + CHN_F_DEAD); + chn_reset(wrch, 0, 0); pcm_chnrelease(wrch); - PCM_WRCH(i_dev) = NULL; } + PCM_LOCK(d); + } - pcm_lock(d); - /* - * If there are no more references, release the channels. - */ - if (refs == 0 && PCM_RDCH(i_dev) == NULL && - PCM_WRCH(i_dev) == NULL) { - dsp_cdevinfo_free(i_dev); - /* - * Release clone busy state and unref it - * so the automatic garbage collector will - * get the hint and do the remaining cleanup - * process. - */ - (void)snd_clone_release(i_dev); + dsp_cdevinfo_free(i_dev); + /* + * Release clone busy state and unref it so the automatic + * garbage collector will get the hint and do the remaining + * cleanup process. + */ + (void)snd_clone_release(i_dev); - /* - * destroy_dev() might sleep, so release pcm lock - * here and rely on pcm cv serialization. - */ - pcm_unlock(d); - (void)snd_clone_unref(i_dev); - pcm_lock(d); - } - PCM_RELEASE(d); - } + /* + * destroy_dev() might sleep, so release pcm lock + * here and rely on pcm cv serialization. + */ + PCM_UNLOCK(d); + (void)snd_clone_unref(i_dev); + PCM_LOCK(d); - pcm_unlock(d); + PCM_RELEASE(d); + PCM_UNLOCK(d); PCM_GIANT_LEAVE(d); @@ -767,11 +879,175 @@ } static int -dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) +dsp_get_volume_channel(struct cdev *dev, struct pcm_channel **volch) +{ + struct snddev_info *d; + struct pcm_channel *c; + int unit; + + KASSERT(dev != NULL && volch != NULL, + ("%s(): NULL query dev=%p volch=%p", __func__, dev, volch)); + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d)) { + *volch = NULL; + return (EINVAL); + } + + PCM_UNLOCKASSERT(d); + + *volch = NULL; + + c = PCM_VOLCH(dev); + if (c != NULL) { + if (!(c->feederflags & (1 << FEEDER_VOLUME))) + return (-1); + *volch = c; + return (0); + } + + PCM_LOCK(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + + unit = dev2unit(dev); + + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + if (c->unit != unit) { + CHN_UNLOCK(c); + continue; + } + *volch = c; + pcm_chnref(c, 1); + PCM_VOLCH(dev) = c; + CHN_UNLOCK(c); + PCM_RELEASE(d); + PCM_UNLOCK(d); + return ((c->feederflags & (1 << FEEDER_VOLUME)) ? 0 : -1); + } + + PCM_RELEASE(d); + PCM_UNLOCK(d); + + return (EINVAL); +} + +static int +dsp_ioctl_channel(struct cdev *dev, struct pcm_channel *volch, u_long cmd, + caddr_t arg) +{ + struct snddev_info *d; + struct pcm_channel *rdch, *wrch; + int j, devtype, ret; + + d = dsp_get_info(dev); + if (!PCM_REGISTERED(d) || !(dsp_get_flags(dev) & SD_F_VPC)) + return (-1); + + PCM_UNLOCKASSERT(d); + + j = cmd & 0xff; + + rdch = PCM_RDCH(dev); + wrch = PCM_WRCH(dev); + + /* No specific channel, look into cache */ + if (volch == NULL) + volch = PCM_VOLCH(dev); + + /* Look harder */ + if (volch == NULL) { + if (j == SOUND_MIXER_RECLEV && rdch != NULL) + volch = rdch; + else if (j == SOUND_MIXER_PCM && wrch != NULL) + volch = wrch; + } + + devtype = PCMDEV(dev); + + /* Look super harder */ + if (volch == NULL && + (devtype == SND_DEV_DSPHW_PLAY || devtype == SND_DEV_DSPHW_VPLAY || + devtype == SND_DEV_DSPHW_REC || devtype == SND_DEV_DSPHW_VREC)) { + ret = dsp_get_volume_channel(dev, &volch); + if (ret != 0) + return (ret); + if (volch == NULL) + return (EINVAL); + } + + /* Final validation */ + if (volch != NULL) { + CHN_LOCK(volch); + if (!(volch->feederflags & (1 << FEEDER_VOLUME))) { + CHN_UNLOCK(volch); + return (-1); + } + if (volch->direction == PCMDIR_PLAY) + wrch = volch; + else + rdch = volch; + } + + ret = EINVAL; + + if (volch != NULL && + ((j == SOUND_MIXER_PCM && volch->direction == PCMDIR_PLAY) || + (j == SOUND_MIXER_RECLEV && volch->direction == PCMDIR_REC))) { + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + int left, right, center; + + left = *(int *)arg & 0x7f; + right = ((*(int *)arg) >> 8) & 0x7f; + center = (left + right) >> 1; + chn_setvolume_multi(volch, SND_VOL_C_PCM, left, right, + center); + } else if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FL); + *(int *)arg |= CHN_GETVOLUME(volch, + SND_VOL_C_PCM, SND_CHN_T_FR) << 8; + } + ret = 0; + } else if (rdch != NULL || wrch != NULL) { + switch (j) { + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = 0; + if (rdch != NULL) + *(int *)arg |= SOUND_MASK_RECLEV; + if (wrch != NULL) + *(int *)arg |= SOUND_MASK_PCM; + } + ret = 0; + break; + case SOUND_MIXER_RECMASK: + case SOUND_MIXER_RECSRC: + if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) + *(int *)arg = 0; + ret = 0; + break; + default: + break; + } + } + + if (volch != NULL) + CHN_UNLOCK(volch); + + return (ret); +} + +static int +dsp_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, + struct thread *td) { struct pcm_channel *chn, *rdch, *wrch; struct snddev_info *d; - int *arg_i, ret, kill, tmp, xcmd; + int *arg_i, ret, tmp, xcmd; d = dsp_get_info(i_dev); if (!DSP_REGISTERED(d, i_dev)) @@ -782,15 +1058,15 @@ arg_i = (int *)arg; ret = 0; xcmd = 0; + chn = NULL; - /* - * this is an evil hack to allow broken apps to perform mixer ioctls - * on dsp devices. - */ if (IOCGROUP(cmd) == 'M') { - /* - * This is at least, a bug to bug compatible with OSS. - */ + ret = dsp_ioctl_channel(i_dev, PCM_VOLCH(i_dev), cmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } + if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); ret = mixer_ioctl_cmd(d->mixer_dev, cmd, arg, -1, td, @@ -835,23 +1111,12 @@ getchns(i_dev, &rdch, &wrch, 0); - kill = 0; - if (wrch && (wrch->flags & CHN_F_DEAD)) - kill |= 1; - if (rdch && (rdch->flags & CHN_F_DEAD)) - kill |= 2; - if (kill == 3) { - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_EXIT(d); - return (EINVAL); - } - if (kill & 1) + if (wrch != NULL && (wrch->flags & CHN_F_DEAD)) wrch = NULL; - if (kill & 2) + if (rdch != NULL && (rdch->flags & CHN_F_DEAD)) rdch = NULL; if (wrch == NULL && rdch == NULL) { - relchns(i_dev, rdch, wrch, 0); PCM_GIANT_EXIT(d); return (EINVAL); } @@ -930,11 +1195,14 @@ if (wrch) { CHN_LOCK(wrch); if (cmd == AIOSFMT && p->play_format != 0) { - chn_setformat(wrch, p->play_format); + chn_setformat(wrch, + SND_FORMAT(p->play_format, + AFMT_CHANNEL(wrch->format), + AFMT_EXTCHANNEL(wrch->format))); chn_setspeed(wrch, p->play_rate); } p->play_rate = wrch->speed; - p->play_format = wrch->format; + p->play_format = AFMT_ENCODING(wrch->format); CHN_UNLOCK(wrch); } else { p->play_rate = 0; @@ -943,11 +1211,14 @@ if (rdch) { CHN_LOCK(rdch); if (cmd == AIOSFMT && p->rec_format != 0) { - chn_setformat(rdch, p->rec_format); + chn_setformat(rdch, + SND_FORMAT(p->rec_format, + AFMT_CHANNEL(rdch->format), + AFMT_EXTCHANNEL(rdch->format))); chn_setspeed(rdch, p->rec_rate); } p->rec_rate = rdch->speed; - p->rec_format = rdch->format; + p->rec_format = AFMT_ENCODING(rdch->format); CHN_UNLOCK(rdch); } else { p->rec_rate = 0; @@ -963,7 +1234,7 @@ struct pcmchan_caps *pcaps = NULL, *rcaps = NULL; struct cdev *pdev; - pcm_lock(d); + PCM_LOCK(d); if (rdch) { CHN_LOCK(rdch); rcaps = chn_getcaps(rdch); @@ -991,7 +1262,7 @@ CHN_UNLOCK(wrch); if (rdch) CHN_UNLOCK(rdch); - pcm_unlock(d); + PCM_UNLOCK(d); } break; @@ -1150,19 +1421,21 @@ case SNDCTL_DSP_STEREO: tmp = -1; - *arg_i = (*arg_i)? AFMT_STEREO : 0; + *arg_i = (*arg_i)? 2 : 1; PCM_ACQUIRE_QUICK(d); if (wrch) { CHN_LOCK(wrch); - ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); - tmp = (wrch->format & AFMT_STEREO)? 1 : 0; + ret = chn_setformat(wrch, + SND_FORMAT(wrch->format, *arg_i, 0)); + tmp = (AFMT_CHANNEL(wrch->format) > 1)? 1 : 0; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); - ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); + ret = chn_setformat(rdch, + SND_FORMAT(rdch->format, *arg_i, 0)); if (tmp == -1) - tmp = (rdch->format & AFMT_STEREO)? 1 : 0; + tmp = (AFMT_CHANNEL(rdch->format) > 1)? 1 : 0; CHN_UNLOCK(rdch); } PCM_RELEASE_QUICK(d); @@ -1171,21 +1444,39 @@ case SOUND_PCM_WRITE_CHANNELS: /* case SNDCTL_DSP_CHANNELS: ( == SOUND_PCM_WRITE_CHANNELS) */ + if (*arg_i < 0) { + *arg_i = 0; + ret = EINVAL; + break; + } if (*arg_i != 0) { + struct pcmchan_matrix *m; + uint32_t ext; + tmp = 0; - *arg_i = (*arg_i != 1)? AFMT_STEREO : 0; + if (*arg_i > SND_CHN_MAX) + *arg_i = SND_CHN_MAX; + + m = feeder_matrix_default_channel_map(*arg_i); + if (m != NULL) + ext = m->ext; + else + ext = 0; + PCM_ACQUIRE_QUICK(d); if (wrch) { CHN_LOCK(wrch); - ret = chn_setformat(wrch, (wrch->format & ~AFMT_STEREO) | *arg_i); - tmp = (wrch->format & AFMT_STEREO)? 2 : 1; + ret = chn_setformat(wrch, + SND_FORMAT(wrch->format, *arg_i, ext)); + tmp = AFMT_CHANNEL(wrch->format); CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); - ret = chn_setformat(rdch, (rdch->format & ~AFMT_STEREO) | *arg_i); + ret = chn_setformat(rdch, + SND_FORMAT(rdch->format, *arg_i, ext)); if (tmp == 0) - tmp = (rdch->format & AFMT_STEREO)? 2 : 1; + tmp = AFMT_CHANNEL(rdch->format); CHN_UNLOCK(rdch); } PCM_RELEASE_QUICK(d); @@ -1193,7 +1484,7 @@ } else { chn = wrch ? wrch : rdch; CHN_LOCK(chn); - *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; + *arg_i = AFMT_CHANNEL(chn->format); CHN_UNLOCK(chn); } break; @@ -1202,7 +1493,7 @@ chn = wrch ? wrch : rdch; if (chn) { CHN_LOCK(chn); - *arg_i = (chn->format & AFMT_STEREO) ? 2 : 1; + *arg_i = AFMT_CHANNEL(chn->format); CHN_UNLOCK(chn); } else { *arg_i = 0; @@ -1223,28 +1514,32 @@ break; case SNDCTL_DSP_SETFMT: /* sets _one_ format */ - if ((*arg_i != AFMT_QUERY)) { + if (*arg_i != AFMT_QUERY) { tmp = 0; PCM_ACQUIRE_QUICK(d); if (wrch) { CHN_LOCK(wrch); - ret = chn_setformat(wrch, (*arg_i) | (wrch->format & AFMT_STEREO)); - tmp = wrch->format & ~AFMT_STEREO; + ret = chn_setformat(wrch, SND_FORMAT(*arg_i, + AFMT_CHANNEL(wrch->format), + AFMT_EXTCHANNEL(wrch->format))); + tmp = wrch->format; CHN_UNLOCK(wrch); } if (rdch && ret == 0) { CHN_LOCK(rdch); - ret = chn_setformat(rdch, (*arg_i) | (rdch->format & AFMT_STEREO)); + ret = chn_setformat(rdch, SND_FORMAT(*arg_i, + AFMT_CHANNEL(rdch->format), + AFMT_EXTCHANNEL(rdch->format))); if (tmp == 0) - tmp = rdch->format & ~AFMT_STEREO; + tmp = rdch->format; CHN_UNLOCK(rdch); } PCM_RELEASE_QUICK(d); - *arg_i = tmp; + *arg_i = AFMT_ENCODING(tmp); } else { chn = wrch ? wrch : rdch; CHN_LOCK(chn); - *arg_i = chn->format & ~AFMT_STEREO; + *arg_i = AFMT_ENCODING(chn->format); CHN_UNLOCK(chn); } break; @@ -1374,11 +1669,11 @@ break; case SNDCTL_DSP_GETCAPS: - pcm_lock(d); + PCM_LOCK(d); *arg_i = PCM_CAP_REALTIME | PCM_CAP_MMAP | PCM_CAP_TRIGGER; if (rdch && wrch && !(dsp_get_flags(i_dev) & SD_F_SIMPLEX)) *arg_i |= PCM_CAP_DUPLEX; - pcm_unlock(d); + PCM_UNLOCK(d); break; case SOUND_PCM_READ_BITS: @@ -1471,10 +1766,10 @@ * switch to full-duplex mode if card is in half-duplex * mode and is able to work in full-duplex mode */ - pcm_lock(d); + PCM_LOCK(d); if (rdch && wrch && (dsp_get_flags(i_dev) & SD_F_SIMPLEX)) dsp_set_flags(i_dev, dsp_get_flags(i_dev)^SD_F_SIMPLEX); - pcm_unlock(d); + PCM_UNLOCK(d); break; /* @@ -1483,20 +1778,34 @@ * command". */ case SNDCTL_DSP_GETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETRECVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_RECLEV; + chn = rdch; + } /* FALLTHROUGH */ case SNDCTL_DSP_GETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_READ_PCM; + chn = wrch; + } /* FALLTHROUGH */ case SNDCTL_DSP_SETPLAYVOL: - if (xcmd == 0) + if (xcmd == 0) { xcmd = SOUND_MIXER_WRITE_PCM; + chn = wrch; + } + + ret = dsp_ioctl_channel(i_dev, chn, xcmd, arg); + if (ret != -1) { + PCM_GIANT_EXIT(d); + return (ret); + } if (d->mixer_dev != NULL) { PCM_ACQUIRE_QUICK(d); @@ -1505,6 +1814,7 @@ PCM_RELEASE_QUICK(d); } else ret = ENOTSUP; + break; case SNDCTL_DSP_GET_RECSRC_NAMES: @@ -1719,6 +2029,30 @@ PCM_RELEASE_QUICK(d); break; + case SNDCTL_DSP_COOKEDMODE: + PCM_ACQUIRE_QUICK(d); + if (!(dsp_get_flags(i_dev) & SD_F_BITPERFECT)) + ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_GET_CHNORDER: + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_SET_CHNORDER: + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_GETCHANNELMASK: /* XXX vlc */ + PCM_ACQUIRE_QUICK(d); + ret = dsp_oss_getchannelmask(wrch, rdch, (int *)arg); + PCM_RELEASE_QUICK(d); + break; + case SNDCTL_DSP_BIND_CHANNEL: /* XXX what?!? */ + ret = EINVAL; + break; #ifdef OSSV4_EXPERIMENT /* * XXX The following ioctls are not yet supported and just return @@ -1749,15 +2083,6 @@ * XXX Once implemented, revisit this for proper cv protection * (if necessary). */ - case SNDCTL_DSP_COOKEDMODE: - ret = dsp_oss_cookedmode(wrch, rdch, *arg_i); - break; - case SNDCTL_DSP_GET_CHNORDER: - ret = dsp_oss_getchnorder(wrch, rdch, (unsigned long long *)arg); - break; - case SNDCTL_DSP_SET_CHNORDER: - ret = dsp_oss_setchnorder(wrch, rdch, (unsigned long long *)arg); - break; case SNDCTL_GETLABEL: ret = dsp_oss_getlabel(wrch, rdch, (oss_label_t *)arg); break; @@ -1802,8 +2127,6 @@ break; } - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_LEAVE(d); return (ret); @@ -1861,7 +2184,14 @@ * http://lists.freebsd.org/pipermail/freebsd-emulation/2007-June/003698.html * */ - if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec == 0) +#ifdef SV_ABI_LINUX + if ((nprot & PROT_EXEC) && (dsp_mmap_allow_prot_exec < 0 || + (dsp_mmap_allow_prot_exec == 0 && + SV_CURPROC_ABI() != SV_ABI_LINUX))) + return (-1); +#else + if ((nprot & PROT_EXEC) && dsp_mmap_allow_prot_exec < 1) +#endif return (-1); d = dsp_get_info(i_dev); @@ -1910,8 +2240,6 @@ return (0); } -#ifdef USING_DEVFS - /* So much for dev_stdclone() */ static int dsp_stdclone(char *name, char *namep, char *sep, int use_sep, int *u, int *c) @@ -1975,7 +2303,7 @@ struct snd_clone_entry *ce; struct pcm_channel *c; int i, unit, udcmask, cunit, devtype, devhw, devcmax, tumax; - char *devname, *devsep; + char *devname, *devcmp, *devsep; KASSERT(dsp_umax >= 0 && dsp_cmax >= 0, ("Uninitialized unit!")); @@ -1994,13 +2322,16 @@ for (i = 0; unit == -1 && i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { devtype = dsp_cdevs[i].type; - devname = dsp_cdevs[i].name; + devcmp = dsp_cdevs[i].name; devsep = dsp_cdevs[i].sep; + devname = dsp_cdevs[i].alias; + if (devname == NULL) + devname = devcmp; devhw = dsp_cdevs[i].hw; devcmax = dsp_cdevs[i].max - 1; - if (strcmp(name, devname) == 0) + if (strcmp(name, devcmp) == 0) unit = snd_unit; - else if (dsp_stdclone(name, devname, devsep, + else if (dsp_stdclone(name, devcmp, devsep, dsp_cdevs[i].use_sep, &unit, &cunit) != 0) { unit = -1; cunit = -1; @@ -2013,15 +2344,15 @@ /* XXX Need Giant magic entry ??? */ - pcm_lock(d); + PCM_LOCK(d); if (snd_clone_disabled(d->clones)) { - pcm_unlock(d); + PCM_UNLOCK(d); return; } PCM_WAIT(d); PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); udcmask = snd_u2unit(unit) | snd_d2unit(devtype); @@ -2124,7 +2455,6 @@ SYSINIT(dsp_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysinit, NULL); SYSUNINIT(dsp_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, dsp_sysuninit, NULL); -#endif char * dsp_unit2name(char *buf, size_t len, int unit) @@ -2137,7 +2467,7 @@ dtype = snd_unit2d(unit); for (i = 0; i < (sizeof(dsp_cdevs) / sizeof(dsp_cdevs[0])); i++) { - if (dtype != dsp_cdevs[i].type) + if (dtype != dsp_cdevs[i].type || dsp_cdevs[i].alias != NULL) continue; snprintf(buf, len, "%s%d%s%d", dsp_cdevs[i].name, snd_unit2u(unit), dsp_cdevs[i].sep, snd_unit2c(unit)); @@ -2215,11 +2545,11 @@ /* XXX Need Giant magic entry ??? */ /* See the note in function docblock */ - mtx_assert(d->lock, MA_NOTOWNED); - pcm_lock(d); + PCM_UNLOCKASSERT(d); + PCM_LOCK(d); CHN_FOREACH(ch, d, channels.pcm) { - mtx_assert(ch->lock, MA_NOTOWNED); + CHN_UNLOCKASSERT(ch); CHN_LOCK(ch); if (ai->dev == -1) { if (DSP_REGISTERED(d, i_dev) && @@ -2299,7 +2629,7 @@ fmts = 0; for (i = 0; caps->fmtlist[i]; i++) { fmts |= caps->fmtlist[i]; - if (caps->fmtlist[i] & AFMT_STEREO) { + if (AFMT_CHANNEL(caps->fmtlist[i]) > 1) { minch = (minch == 0) ? 2 : minch; maxch = 2; } else { @@ -2371,7 +2701,7 @@ CHN_UNLOCK(ch); } - pcm_unlock(d); + PCM_UNLOCK(d); if (devname != NULL) return (0); @@ -2716,7 +3046,6 @@ return (ret); } -#ifdef OSSV4_EXPERIMENT /** * @brief Enable or disable "cooked" mode * @@ -2733,10 +3062,6 @@ * See @c http://manuals.opensound.com/developer/SNDCTL_DSP_COOKEDMODE.html * for more details. * - * @note Currently, this function is just a stub that always returns EINVAL. - * - * @todo Figure out how to and actually implement this. - * * @param wrch playback channel (optional; may be NULL) * @param rdch recording channel (optional; may be NULL) * @param enabled 0 = raw mode, 1 = cooked mode @@ -2746,7 +3071,38 @@ static int dsp_oss_cookedmode(struct pcm_channel *wrch, struct pcm_channel *rdch, int enabled) { - return (EINVAL); + + /* + * XXX I just don't get it. Why don't they call it + * "BITPERFECT" ~ SNDCTL_DSP_BITPERFECT !?!?. + * This is just plain so confusing, incoherent, + * . + */ + if (!(enabled == 1 || enabled == 0)) + return (EINVAL); + + /* + * I won't give in. I'm inverting its logic here and now. + * Brag all you want, but "BITPERFECT" should be the better + * term here. + */ + enabled ^= 0x00000001; + + if (wrch != NULL) { + CHN_LOCK(wrch); + wrch->flags &= ~CHN_F_BITPERFECT; + wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; + CHN_UNLOCK(wrch); + } + + if (rdch != NULL) { + CHN_LOCK(rdch); + rdch->flags &= ~CHN_F_BITPERFECT; + wrch->flags |= (enabled != 0) ? CHN_F_BITPERFECT : 0x00000000; + CHN_UNLOCK(rdch); + } + + return (0); } /** @@ -2769,7 +3125,18 @@ static int dsp_oss_getchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) { - return (EINVAL); + struct pcm_channel *ch; + int ret; + + ch = (wrch != NULL) ? wrch : rdch; + if (ch != NULL) { + CHN_LOCK(ch); + ret = chn_oss_getorder(ch, map); + CHN_UNLOCK(ch); + } else + ret = EINVAL; + + return (ret); } /** @@ -2789,9 +3156,50 @@ static int dsp_oss_setchnorder(struct pcm_channel *wrch, struct pcm_channel *rdch, unsigned long long *map) { - return (EINVAL); + int ret; + + ret = 0; + + if (wrch != NULL) { + CHN_LOCK(wrch); + ret = chn_oss_setorder(wrch, map); + CHN_UNLOCK(wrch); + } + + if (ret == 0 && rdch != NULL) { + CHN_LOCK(rdch); + ret = chn_oss_setorder(rdch, map); + CHN_UNLOCK(rdch); + } + + return (ret); } +static int +dsp_oss_getchannelmask(struct pcm_channel *wrch, struct pcm_channel *rdch, + int *mask) +{ + struct pcm_channel *ch; + uint32_t chnmask; + int ret; + + chnmask = 0; + ch = (wrch != NULL) ? wrch : rdch; + + if (ch != NULL) { + CHN_LOCK(ch); + ret = chn_oss_getmask(ch, &chnmask); + CHN_UNLOCK(ch); + } else + ret = EINVAL; + + if (ret == 0) + *mask = chnmask; + + return (ret); +} + +#ifdef OSSV4_EXPERIMENT /** * @brief Retrieve an audio device's label * --- sys/dev/sound/pcm/dsp.h.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/dsp.h 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Portions Copyright (c) Ryan Beasley - GSoC 2006 + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without --- sys/dev/sound/pcm/fake.c.orig 2007-03-16 02:19:01.000000000 +0800 +++ sys/dev/sound/pcm/fake.c 2009-01-17 19:29:20.000000000 +0800 @@ -29,38 +29,38 @@ SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/fake.c,v 1.18 2007/03/15 18:19:01 ariff Exp $"); static u_int32_t fk_fmt[] = { - AFMT_MU_LAW, - AFMT_STEREO | AFMT_MU_LAW, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_S8, - AFMT_STEREO | AFMT_S8, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_U16_LE, - AFMT_STEREO | AFMT_U16_LE, - AFMT_S16_BE, - AFMT_STEREO | AFMT_S16_BE, - AFMT_U16_BE, - AFMT_STEREO | AFMT_U16_BE, - AFMT_S24_LE, - AFMT_STEREO | AFMT_S24_LE, - AFMT_U24_LE, - AFMT_STEREO | AFMT_U24_LE, - AFMT_S24_BE, - AFMT_STEREO | AFMT_S24_BE, - AFMT_U24_BE, - AFMT_STEREO | AFMT_U24_BE, - AFMT_S32_LE, - AFMT_STEREO | AFMT_S32_LE, - AFMT_U32_LE, - AFMT_STEREO | AFMT_U32_LE, - AFMT_S32_BE, - AFMT_STEREO | AFMT_S32_BE, - AFMT_U32_BE, - AFMT_STEREO | AFMT_U32_BE, + SND_FORMAT(AFMT_MU_LAW, 1, 0), + SND_FORMAT(AFMT_MU_LAW, 2, 0), + SND_FORMAT(AFMT_A_LAW, 1, 0), + SND_FORMAT(AFMT_A_LAW, 2, 0), + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_S8, 1, 0), + SND_FORMAT(AFMT_S8, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(AFMT_S16_LE, 2, 0), + SND_FORMAT(AFMT_U16_LE, 1, 0), + SND_FORMAT(AFMT_U16_LE, 2, 0), + SND_FORMAT(AFMT_S16_BE, 1, 0), + SND_FORMAT(AFMT_S16_BE, 2, 0), + SND_FORMAT(AFMT_U16_BE, 1, 0), + SND_FORMAT(AFMT_U16_BE, 2, 0), + SND_FORMAT(AFMT_S24_LE, 1, 0), + SND_FORMAT(AFMT_S24_LE, 2, 0), + SND_FORMAT(AFMT_U24_LE, 1, 0), + SND_FORMAT(AFMT_U24_LE, 2, 0), + SND_FORMAT(AFMT_S24_BE, 1, 0), + SND_FORMAT(AFMT_S24_BE, 2, 0), + SND_FORMAT(AFMT_U24_BE, 1, 0), + SND_FORMAT(AFMT_U24_BE, 2, 0), + SND_FORMAT(AFMT_S32_LE, 1, 0), + SND_FORMAT(AFMT_S32_LE, 2, 0), + SND_FORMAT(AFMT_U32_LE, 1, 0), + SND_FORMAT(AFMT_U32_LE, 2, 0), + SND_FORMAT(AFMT_S32_BE, 1, 0), + SND_FORMAT(AFMT_S32_BE, 2, 0), + SND_FORMAT(AFMT_U32_BE, 1, 0), + SND_FORMAT(AFMT_U32_BE, 2, 0), 0 }; static struct pcmchan_caps fk_caps = {0, 1000000, fk_fmt, 0}; @@ -140,11 +140,6 @@ c = malloc(sizeof(*c), M_DEVBUF, M_WAITOK | M_ZERO); c->methods = kobj_create(&fkchan_class, M_DEVBUF, M_WAITOK); c->parentsnddev = d; - /* - * Fake channel is such a blessing in disguise. Using this, - * we can keep track prefered virtual channel speed / format without - * querying kernel hint repetitively (see vchan_create / vchan.c). - */ c->speed = 0; c->format = 0; snprintf(c->name, CHN_NAMELEN, "%s:fake", device_get_nameunit(dev)); --- sys/dev/sound/pcm/feeder.c.orig 2007-06-17 23:53:11.000000000 +0800 +++ sys/dev/sound/pcm/feeder.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,6 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -35,43 +36,6 @@ #define MAXFEEDERS 256 #undef FEEDER_DEBUG -int feeder_buffersize = FEEDBUFSZ; -TUNABLE_INT("hw.snd.feeder_buffersize", &feeder_buffersize); - -#ifdef SND_DEBUG -static int -sysctl_hw_snd_feeder_buffersize(SYSCTL_HANDLER_ARGS) -{ - int i, err, val; - - val = feeder_buffersize; - err = sysctl_handle_int(oidp, &val, 0, req); - - if (err != 0 || req->newptr == NULL) - return err; - - if (val < FEEDBUFSZ_MIN || val > FEEDBUFSZ_MAX) - return EINVAL; - - i = 0; - while (val >> i) - i++; - i = 1 << i; - if (i > val && (i >> 1) > 0 && (i >> 1) >= ((val * 3) >> 2)) - i >>= 1; - - feeder_buffersize = i; - - return err; -} -SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_buffersize, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_buffersize, "I", - "feeder buffer size"); -#else -SYSCTL_INT(_hw_snd, OID_AUTO, feeder_buffersize, CTLFLAG_RD, - &feeder_buffersize, FEEDBUFSZ, "feeder buffer size"); -#endif - struct feedertab_entry { SLIST_ENTRY(feedertab_entry) link; struct feeder_class *feederclass; @@ -131,10 +95,6 @@ chn_latency_profile > CHN_LATENCY_PROFILE_MAX) chn_latency_profile = CHN_LATENCY_PROFILE_DEFAULT; - if (feeder_buffersize < FEEDBUFSZ_MIN || - feeder_buffersize > FEEDBUFSZ_MAX) - feeder_buffersize = FEEDBUFSZ; - if (feeder_rate_min < FEEDRATE_MIN || feeder_rate_max < FEEDRATE_MIN || feeder_rate_min > FEEDRATE_MAX || @@ -150,11 +110,11 @@ if (bootverbose) printf("%s: snd_unit=%d snd_maxautovchans=%d " - "latency=%d feeder_buffersize=%d " + "latency=%d " "feeder_rate_min=%d feeder_rate_max=%d " "feeder_rate_round=%d\n", __func__, snd_unit, snd_maxautovchans, - chn_latency, feeder_buffersize, + chn_latency, feeder_rate_min, feeder_rate_max, feeder_rate_round); @@ -321,39 +281,6 @@ return NULL; } -static int -chainok(struct pcm_feeder *test, struct pcm_feeder *stop) -{ - u_int32_t visited[MAXFEEDERS / 32]; - u_int32_t idx, mask; - - bzero(visited, sizeof(visited)); - while (test && (test != stop)) { - idx = test->desc->idx; - if (idx < 0) - panic("bad idx %d", idx); - if (idx >= MAXFEEDERS) - panic("bad idx %d", idx); - mask = 1 << (idx & 31); - idx >>= 5; - if (visited[idx] & mask) - return 0; - visited[idx] |= mask; - test = test->source; - } - - return 1; -} - -/* - * See feeder_fmtchain() for the mumbo-jumbo ridiculous explanation - * of what the heck is this FMT_Q_* - */ -#define FMT_Q_UP 1 -#define FMT_Q_DOWN 2 -#define FMT_Q_EQ 3 -#define FMT_Q_MULTI 4 - /* * 14bit format scoring * -------------------- @@ -384,11 +311,13 @@ #define score_signeq(s1, s2) (((s1) & 0x1) == ((s2) & 0x1)) #define score_endianeq(s1, s2) (((s1) & 0x2) == ((s2) & 0x2)) #define score_cheq(s1, s2) (((s1) & 0xfc) == ((s2) & 0xfc)) +#define score_chgt(s1, s2) (((s1) & 0xfc) > ((s2) & 0xfc)) +#define score_chlt(s1, s2) (((s1) & 0xfc) < ((s2) & 0xfc)) #define score_val(s1) ((s1) & 0x3f00) #define score_cse(s1) ((s1) & 0x7f) u_int32_t -chn_fmtscore(u_int32_t fmt) +snd_fmtscore(u_int32_t fmt) { u_int32_t ret; @@ -397,10 +326,11 @@ ret |= 1 << 0; if (fmt & AFMT_BIGENDIAN) ret |= 1 << 1; - if (fmt & AFMT_STEREO) + /*if (fmt & AFMT_STEREO) ret |= (2 & 0x3f) << 2; else - ret |= (1 & 0x3f) << 2; + ret |= (1 & 0x3f) << 2;*/ + ret |= (AFMT_CHANNEL(fmt) & 0x3f) << 2; if (fmt & AFMT_A_LAW) ret |= 1 << 8; else if (fmt & AFMT_MU_LAW) @@ -418,7 +348,7 @@ } static u_int32_t -chn_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) +snd_fmtbestfunc(u_int32_t fmt, u_int32_t *fmts, int cheq) { u_int32_t best, score, score2, oldscore; int i; @@ -426,16 +356,18 @@ if (fmt == 0 || fmts == NULL || fmts[0] == 0) return 0; - if (fmtvalid(fmt, fmts)) + if (snd_fmtvalid(fmt, fmts)) return fmt; best = 0; - score = chn_fmtscore(fmt); + score = snd_fmtscore(fmt); oldscore = 0; for (i = 0; fmts[i] != 0; i++) { - score2 = chn_fmtscore(fmts[i]); - if (cheq && !score_cheq(score, score2)) - continue; + score2 = snd_fmtscore(fmts[i]); + if (cheq && !score_cheq(score, score2) && + (score_chlt(score2, score) || + (oldscore != 0 && score_chgt(score2, oldscore)))) + continue; if (oldscore == 0 || (score_val(score2) == score_val(score)) || (score_val(score2) == score_val(oldscore)) || @@ -461,36 +393,37 @@ } u_int32_t -chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) +snd_fmtbestbit(u_int32_t fmt, u_int32_t *fmts) { - return chn_fmtbestfunc(fmt, fmts, 0); + return snd_fmtbestfunc(fmt, fmts, 0); } u_int32_t -chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts) +snd_fmtbestchannel(u_int32_t fmt, u_int32_t *fmts) { - return chn_fmtbestfunc(fmt, fmts, 1); + return snd_fmtbestfunc(fmt, fmts, 1); } u_int32_t -chn_fmtbest(u_int32_t fmt, u_int32_t *fmts) +snd_fmtbest(u_int32_t fmt, u_int32_t *fmts) { u_int32_t best1, best2; u_int32_t score, score1, score2; - if (fmtvalid(fmt, fmts)) + if (snd_fmtvalid(fmt, fmts)) return fmt; - best1 = chn_fmtbeststereo(fmt, fmts); - best2 = chn_fmtbestbit(fmt, fmts); + best1 = snd_fmtbestchannel(fmt, fmts); + best2 = snd_fmtbestbit(fmt, fmts); if (best1 != 0 && best2 != 0 && best1 != best2) { - if (fmt & AFMT_STEREO) + /*if (fmt & AFMT_STEREO)*/ + if (AFMT_CHANNEL(fmt) > 1) return best1; else { - score = score_val(chn_fmtscore(fmt)); - score1 = score_val(chn_fmtscore(best1)); - score2 = score_val(chn_fmtscore(best2)); + score = score_val(snd_fmtscore(fmt)); + score1 = score_val(snd_fmtscore(best1)); + score2 = score_val(snd_fmtscore(best2)); if (score1 == score2 || score1 == score) return best1; else if (score2 == score) @@ -505,309 +438,6 @@ return best2; } -static struct pcm_feeder * -feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *stop, int maxdepth) -{ - struct feedertab_entry *fte, *ftebest; - struct pcm_feeder *try, *ret; - uint32_t fl, qout, qsrc, qdst; - int qtype; - - if (to == NULL || to[0] == 0) - return NULL; - - DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out)); - if (fmtvalid(source->desc->out, to)) { - DEB(printf("got it\n")); - return source; - } - - if (maxdepth < 0) - return NULL; - - /* - * WARNING: THIS IS _NOT_ FOR THE FAINT HEART - * Disclaimer: I don't expect anybody could understand this - * without deep logical and mathematical analysis - * involving various unnamed probability theorem. - * - * This "Best Fit Random Chain Selection" (BLEHBLEHWHATEVER) algorithm - * is **extremely** difficult to digest especially when applied to - * large sets / numbers of random chains (feeders), each with - * unique characteristic providing different sets of in/out format. - * - * Basically, our FEEDER_FMT (see feeder_fmt.c) chains characteristic: - * 1) Format chains - * 1.1 "8bit to any, not to 8bit" - * 1.1.1 sign can remain consistent, e.g: u8 -> u16[le|be] - * 1.1.2 sign can be changed, e.g: u8 -> s16[le|be] - * 1.1.3 endian can be changed, e.g: u8 -> u16[le|be] - * 1.1.4 both can be changed, e.g: u8 -> [u|s]16[le|be] - * 1.2 "Any to 8bit, not from 8bit" - * 1.2.1 sign can remain consistent, e.g: s16le -> s8 - * 1.2.2 sign can be changed, e.g: s16le -> u8 - * 1.2.3 source endian can be anything e.g: s16[le|be] -> s8 - * 1.2.4 source endian / sign can be anything e.g: [u|s]16[le|be] -> u8 - * 1.3 "Any to any where BOTH input and output either 8bit or non-8bit" - * 1.3.1 endian MUST remain consistent - * 1.3.2 sign CAN be changed - * 1.4 "Long jump" is allowed, e.g: from 16bit to 32bit, excluding - * 16bit to 24bit . - * 2) Channel chains (mono <-> stereo) - * 2.1 Both endian and sign MUST remain consistent - * 3) Endian chains (big endian <-> little endian) - * 3.1 Channels and sign MUST remain consistent - * 4) Sign chains (signed <-> unsigned) - * 4.1 Channels and endian MUST remain consistent - * - * .. and the mother of all chaining rules: - * - * Rules 0: Source and destination MUST not contain multiple selections. - * (qtype != FMT_Q_MULTI) - * - * First of all, our caller ( chn_fmtchain() ) will reduce the possible - * multiple from/to formats to a single best format using chn_fmtbest(). - * Then, using chn_fmtscore(), we determine the chaining characteristic. - * Our main goal is to narrow it down until it reach FMT_Q_EQ chaining - * type while still adhering above chaining rules. - * - * The need for this complicated chaining procedures is inevitable, - * since currently we have more than 200 different types of FEEDER_FMT - * doing various unique format conversion. Without this (the old way), - * it is possible to generate broken chain since it doesn't do any - * sanity checking to ensure that the output format is "properly aligned" - * with the direction of conversion (quality up/down/equal). - * - * Conversion: s24le to s32le - * Possible chain: 1) s24le -> s32le (correct, optimized) - * 2) s24le -> s16le -> s32le - * (since we have feeder_24to16 and feeder_16to32) - * +-- obviously broken! - * - * Using scoring mechanisme, this will ensure that the chaining - * process do the right thing, or at least, give the best chain - * possible without causing quality (the 'Q') degradation. - */ - - qdst = chn_fmtscore(to[0]); - qsrc = chn_fmtscore(source->desc->out); - -#define score_q(s1) score_val(s1) -#define score_8bit(s1) ((s1) & 0x700) -#define score_non8bit(s1) (!score_8bit(s1)) -#define score_across8bit(s1, s2) ((score_8bit(s1) && score_non8bit(s2)) || \ - (score_8bit(s2) && score_non8bit(s1))) - -#define FMT_CHAIN_Q_UP(s1, s2) (score_q(s1) < score_q(s2)) -#define FMT_CHAIN_Q_DOWN(s1, s2) (score_q(s1) > score_q(s2)) -#define FMT_CHAIN_Q_EQ(s1, s2) (score_q(s1) == score_q(s2)) -#define FMT_Q_DOWN_FLAGS(s1, s2) (0x1 | (score_across8bit(s1, s2) ? \ - 0x2 : 0x0)) -#define FMT_Q_UP_FLAGS(s1, s2) FMT_Q_DOWN_FLAGS(s1, s2) -#define FMT_Q_EQ_FLAGS(s1, s2) (0x3ffc | \ - ((score_cheq(s1, s2) && \ - score_endianeq(s1, s2)) ? \ - 0x1 : 0x0) | \ - ((score_cheq(s1, s2) && \ - score_signeq(s1, s2)) ? \ - 0x2 : 0x0)) - - /* Determine chaining direction and set matching flag */ - fl = 0x3fff; - if (to[1] != 0) { - qtype = FMT_Q_MULTI; - printf("%s: WARNING: FMT_Q_MULTI chaining. Expect the unexpected.\n", __func__); - } else if (FMT_CHAIN_Q_DOWN(qsrc, qdst)) { - qtype = FMT_Q_DOWN; - fl = FMT_Q_DOWN_FLAGS(qsrc, qdst); - } else if (FMT_CHAIN_Q_UP(qsrc, qdst)) { - qtype = FMT_Q_UP; - fl = FMT_Q_UP_FLAGS(qsrc, qdst); - } else { - qtype = FMT_Q_EQ; - fl = FMT_Q_EQ_FLAGS(qsrc, qdst); - } - - ftebest = NULL; - - SLIST_FOREACH(fte, &feedertab, link) { - if (fte->desc == NULL) - continue; - if (fte->desc->type != FEEDER_FMT) - continue; - qout = chn_fmtscore(fte->desc->out); -#define FMT_Q_MULTI_VALIDATE(qt) ((qt) == FMT_Q_MULTI) -#define FMT_Q_FL_MATCH(qfl, s1, s2) (((s1) & (qfl)) == ((s2) & (qfl))) -#define FMT_Q_UP_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_UP && \ - score_q(s3) >= score_q(s1) && \ - score_q(s3) <= score_q(s2)) -#define FMT_Q_DOWN_VALIDATE(qt, s1, s2, s3) ((qt) == FMT_Q_DOWN && \ - score_q(s3) <= score_q(s1) && \ - score_q(s3) >= score_q(s2)) -#define FMT_Q_EQ_VALIDATE(qt, s1, s2) ((qt) == FMT_Q_EQ && \ - score_q(s1) == score_q(s2)) - if (fte->desc->in == source->desc->out && - (FMT_Q_MULTI_VALIDATE(qtype) || - (FMT_Q_FL_MATCH(fl, qout, qdst) && - (FMT_Q_UP_VALIDATE(qtype, qsrc, qdst, qout) || - FMT_Q_DOWN_VALIDATE(qtype, qsrc, qdst, qout) || - FMT_Q_EQ_VALIDATE(qtype, qdst, qout))))) { - try = feeder_create(fte->feederclass, fte->desc); - if (try) { - try->source = source; - ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; - if (ret != NULL) - return ret; - feeder_destroy(try); - } - } else if (fte->desc->in == source->desc->out) { - /* XXX quality must be considered! */ - if (ftebest == NULL) - ftebest = fte; - } - } - - if (ftebest != NULL) { - try = feeder_create(ftebest->feederclass, ftebest->desc); - if (try) { - try->source = source; - ret = chainok(try, stop) ? feeder_fmtchain(to, try, stop, maxdepth - 1) : NULL; - if (ret != NULL) - return ret; - feeder_destroy(try); - } - } - - /* printf("giving up %s...\n", source->class->name); */ - - return NULL; -} - -u_int32_t -chn_fmtchain(struct pcm_channel *c, u_int32_t *to) -{ - struct pcm_feeder *try, *del, *stop; - u_int32_t tmpfrom[2], tmpto[2], best, *from; - int i, max, bestmax; - - KASSERT(c != NULL, ("c == NULL")); - KASSERT(c->feeder != NULL, ("c->feeder == NULL")); - KASSERT(to != NULL, ("to == NULL")); - KASSERT(to[0] != 0, ("to[0] == 0")); - - if (c == NULL || c->feeder == NULL || to == NULL || to[0] == 0) - return 0; - - stop = c->feeder; - best = 0; - - if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) { - from = chn_getcaps(c)->fmtlist; - if (from[1] != 0) { - best = chn_fmtbest(to[0], from); - if (best != 0) { - tmpfrom[0] = best; - tmpfrom[1] = 0; - from = tmpfrom; - } - } - } else { - tmpfrom[0] = c->feeder->desc->out; - tmpfrom[1] = 0; - from = tmpfrom; - if (to[1] != 0) { - best = chn_fmtbest(from[0], to); - if (best != 0) { - tmpto[0] = best; - tmpto[1] = 0; - to = tmpto; - } - } - } - -#define FEEDER_FMTCHAIN_MAXDEPTH 8 - - try = NULL; - - if (to[0] != 0 && from[0] != 0 && - to[1] == 0 && from[1] == 0) { - max = 0; - best = from[0]; - c->feeder->desc->out = best; - do { - try = feeder_fmtchain(to, c->feeder, stop, max); - DEB(if (try != NULL) { - printf("%s: 0x%08x -> 0x%08x (maxdepth: %d)\n", - __func__, from[0], to[0], max); - }); - } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); - } else { - printf("%s: Using the old-way format chaining!\n", __func__); - i = 0; - best = 0; - bestmax = 100; - while (from[i] != 0) { - c->feeder->desc->out = from[i]; - try = NULL; - max = 0; - do { - try = feeder_fmtchain(to, c->feeder, stop, max); - } while (try == NULL && max++ < FEEDER_FMTCHAIN_MAXDEPTH); - if (try != NULL && max < bestmax) { - bestmax = max; - best = from[i]; - } - while (try != NULL && try != stop) { - del = try; - try = try->source; - feeder_destroy(del); - } - i++; - } - if (best == 0) - return 0; - - c->feeder->desc->out = best; - try = feeder_fmtchain(to, c->feeder, stop, bestmax); - } - if (try == NULL) - return 0; - - c->feeder = try; - c->align = 0; -#ifdef FEEDER_DEBUG - printf("\n\nchain: "); -#endif - while (try && (try != stop)) { -#ifdef FEEDER_DEBUG - printf("%s [%d]", try->class->name, try->desc->idx); - if (try->source) - printf(" -> "); -#endif - if (try->source) - try->source->parent = try; - if (try->align > 0) - c->align += try->align; - else if (try->align < 0 && c->align < -try->align) - c->align = -try->align; - try = try->source; - } -#ifdef FEEDER_DEBUG - printf("%s [%d]\n", try->class->name, try->desc->idx); -#endif - - if (c->direction == PCMDIR_REC) { - try = c->feeder; - while (try != NULL) { - if (try->desc->type == FEEDER_ROOT) - return try->desc->out; - try = try->source; - } - return best; - } else - return c->feeder->desc->out; -} - void feeder_printchain(struct pcm_feeder *head) { --- sys/dev/sound/pcm/feeder.h.orig 2007-03-17 01:15:33.000000000 +0800 +++ sys/dev/sound/pcm/feeder.h 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,6 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -53,60 +54,158 @@ void feeder_register(void *p); struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc); -u_int32_t chn_fmtscore(u_int32_t fmt); -u_int32_t chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts); -u_int32_t chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts); -u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts); -u_int32_t chn_fmtchain(struct pcm_channel *c, u_int32_t *to); -int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc); +u_int32_t snd_fmtscore(u_int32_t fmt); +u_int32_t snd_fmtbestbit(u_int32_t fmt, u_int32_t *fmts); +u_int32_t snd_fmtbestchannel(u_int32_t fmt, u_int32_t *fmts); +u_int32_t snd_fmtbest(u_int32_t fmt, u_int32_t *fmts); + +int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, + struct pcm_feederdesc *desc); int chn_removefeeder(struct pcm_channel *c); struct pcm_feeder *chn_findfeeder(struct pcm_channel *c, u_int32_t type); void feeder_printchain(struct pcm_feeder *head); +int feeder_chain(struct pcm_channel *); -#define FEEDER_DECLARE(feeder, palign, pdata) \ -static struct feeder_class feeder ## _class = { \ - .name = #feeder, \ - .methods = feeder ## _methods, \ - .size = sizeof(struct pcm_feeder), \ - .align = palign, \ - .desc = feeder ## _desc, \ - .data = pdata, \ -}; \ -SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, &feeder ## _class); - -#define FEEDER_ROOT 0 -#define FEEDER_FMT 1 -#define FEEDER_MIXER 2 -#define FEEDER_RATE 3 -#define FEEDER_FILTER 4 -#define FEEDER_VOLUME 5 -#define FEEDER_SWAPLR 6 -#define FEEDER_LAST 32 +#define FEEDER_DECLARE(feeder, palign, pdata) \ +static struct feeder_class feeder ## _class = { \ + .name = #feeder, \ + .methods = feeder ## _methods, \ + .size = sizeof(struct pcm_feeder), \ + .align = palign, \ + .desc = feeder ## _desc, \ + .data = pdata, \ +}; \ +SYSINIT(feeder, SI_SUB_DRIVERS, SI_ORDER_ANY, feeder_register, \ + &feeder ## _class); + +enum { + FEEDER_ROOT, + FEEDER_FORMAT, + FEEDER_MIXER, + FEEDER_RATE, + FEEDER_EQ, + FEEDER_VOLUME, + FEEDER_MATRIX, + FEEDER_LAST, +}; -#define FEEDRATE_SRC 1 -#define FEEDRATE_DST 2 +/* feeder_format */ +enum { + FEEDFORMAT_CHANNELS +}; -#define FEEDRATE_RATEMIN 1 -#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */ +/* feeder_mixer */ +enum { + FEEDMIXER_CHANNELS +}; + +/* feeder_rate */ +enum { + FEEDRATE_SRC, + FEEDRATE_DST, + FEEDRATE_QUALITY, + FEEDRATE_CHANNELS +}; +#define FEEDRATE_RATEMIN 1 +#define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */ #define FEEDRATE_MIN 1 #define FEEDRATE_MAX 0x7fffff /* sign 24bit ~ 8ghz ! */ - #define FEEDRATE_ROUNDHZ 25 #define FEEDRATE_ROUNDHZ_MIN 0 #define FEEDRATE_ROUNDHZ_MAX 500 -/* - * Default buffer size for feeder processing. - * - * Big = less sndbuf_feed(), more memory usage. - * Small = aggresive sndbuf_feed() (perhaps too much), less memory usage. - */ -#define FEEDBUFSZ 16384 -#define FEEDBUFSZ_MIN 2048 -#define FEEDBUFSZ_MAX 131072 - extern int feeder_rate_min; extern int feeder_rate_max; extern int feeder_rate_round; -extern int feeder_buffersize; +extern int feeder_rate_quality; + +/* feeder_eq */ +enum { + FEEDEQ_CHANNELS, + FEEDEQ_RATE, + FEEDEQ_TREBLE, + FEEDEQ_BASS, + FEEDEQ_PREAMP, + FEEDEQ_STATE, + FEEDEQ_DISABLE, + FEEDEQ_ENABLE, + FEEDEQ_BYPASS, + FEEDEQ_UNKNOWN +}; + +int feeder_eq_validrate(uint32_t); +void feeder_eq_initsys(device_t); + +/* feeder_volume */ +enum { + FEEDVOLUME_CLASS, + FEEDVOLUME_CHANNELS +}; + +int feeder_volume_apply_matrix(struct pcm_feeder *, struct pcmchan_matrix *); + +/* feeder_matrix */ +int feeder_matrix_default_id(uint32_t); +struct pcmchan_matrix *feeder_matrix_default_channel_map(uint32_t); + +uint32_t feeder_matrix_default_format(uint32_t); + +int feeder_matrix_format_id(uint32_t); +struct pcmchan_matrix *feeder_matrix_format_map(uint32_t); + +struct pcmchan_matrix *feeder_matrix_id_map(int); + +int feeder_matrix_setup(struct pcm_feeder *, struct pcmchan_matrix *, + struct pcmchan_matrix *); +int feeder_matrix_compare(struct pcmchan_matrix *, struct pcmchan_matrix *); + +/* 4Front OSS stuffs */ +int feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *, + unsigned long long *); +int feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *, + unsigned long long *); + +#if 0 +/* feeder_matrix */ +enum { + FEEDMATRIX_TYPE, + FEEDMATRIX_RESET, + FEEDMATRIX_CHANNELS_IN, + FEEDMATRIX_CHANNELS_OUT, + FEEDMATRIX_SET_MAP +}; + +enum { + FEEDMATRIX_TYPE_NONE, + FEEDMATRIX_TYPE_AUTO, + FEEDMATRIX_TYPE_2X1, + FEEDMATRIX_TYPE_1X2, + FEEDMATRIX_TYPE_2X2 +}; + +#define FEEDMATRIX_TYPE_STEREO_TO_MONO FEEDMATRIX_TYPE_2X1 +#define FEEDMATRIX_TYPE_MONO_TO_STEREO FEEDMATRIX_TYPE_1X2 +#define FEEDMATRIX_TYPE_SWAP_STEREO FEEDMATRIX_TYPE_2X2 +#define FEEDMATRIX_MAP(x, y) ((((x) & 0x3f) << 6) | ((y) & 0x3f)) +#define FEEDMATRIX_MAP_SRC(x) ((x) & 0x3f) +#define FEEDMATRIX_MAP_DST(x) (((x) >> 6) & 0x3f) +#endif + +/* + * By default, various feeders only deal with sign 16/32 bit native-endian + * since it should provide the fastest processing path. Processing 8bit samples + * is too noisy due to limited dynamic range, while 24bit is quite slow due to + * unnatural per-byte read/write. However, for debugging purposes, ensuring + * implementation correctness and torture test, the following can be defined: + * + * FEEDER_MULTIFORMAT - Compile all type of converters, but force 8bit + * samples to be converted to 16bit native-endian for + * better dynamic range. Process 24bit samples + * natively. + * FEEDER_FULL_MULTIFORMAT - Ditto, but process 8bit samples natively. + */ +#ifdef FEEDER_FULL_MULTIFORMAT +#undef FEEDER_MULTIFORMAT +#define FEEDER_MULTIFORMAT 1 +#endif --- sys/dev/sound/pcm/feeder_chain.c.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/feeder_chain.c 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,831 @@ +/*- + * Copyright (c) 2008-2009 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. + */ + +#include + +#include "feeder_if.h" + +SND_DECLARE_FILE("$FreeBSD$"); + +struct feeder_chain_state { + uint32_t afmt; /* audio format */ + uint32_t rate; /* sampling rate */ + struct pcmchan_matrix *matrix; /* matrix map */ +}; + +struct feeder_chain_desc { + struct feeder_chain_state origin; /* original state */ + struct feeder_chain_state current; /* current state */ + struct feeder_chain_state target; /* target state */ + struct pcm_feederdesc desc; /* feeder descriptor */ + uint32_t afmt_ne; /* prefered native endian */ + int mode; /* chain mode */ + int use_eq; /* need EQ? */ + int use_matrix; /* need channel matrixing? */ + int use_volume; /* need softpcmvol? */ + int expensive; /* possibly expensive */ +}; + +#define FEEDER_CHAIN_LEAN 0 +#define FEEDER_CHAIN_16 1 +#define FEEDER_CHAIN_32 2 +#define FEEDER_CHAIN_MULTI 3 +#define FEEDER_CHAIN_FULLMULTI 4 +#define FEEDER_CHAIN_LAST 5 + +#if defined(FEEDER_FULL_MULTIFORMAT) +#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_FULLMULTI +#elif defined(FEEDER_MULTIFORMAT) +#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_MULTI +#else +#define FEEDER_CHAIN_DEFAULT FEEDER_CHAIN_LEAN +#endif + +/* + * List of prefered formats that might be required during + * processing. It will be decided through snd_fmtbest(). + */ + +static uint32_t feeder_chain_formats_lean[] = { + AFMT_S16_NE, AFMT_S32_NE, + 0 +}; + +static uint32_t feeder_chain_formats_16[] = { + AFMT_S16_NE, + 0 +}; + +static uint32_t feeder_chain_formats_32[] = { + AFMT_S32_NE, + 0 +}; + +static uint32_t feeder_chain_formats_multi[] = { + AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE, + AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE, + AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE, + 0 +}; + +static uint32_t feeder_chain_formats_fullmulti[] = { + AFMT_S8, AFMT_U8, + AFMT_S16_LE, AFMT_S16_BE, AFMT_U16_LE, AFMT_U16_BE, + AFMT_S24_LE, AFMT_S24_BE, AFMT_U24_LE, AFMT_U24_BE, + AFMT_S32_LE, AFMT_S32_BE, AFMT_U32_LE, AFMT_U32_BE, + 0 +}; + +static uint32_t *feeder_chain_formats[FEEDER_CHAIN_LAST] = { + [FEEDER_CHAIN_LEAN] = feeder_chain_formats_lean, + [FEEDER_CHAIN_16] = feeder_chain_formats_16, + [FEEDER_CHAIN_32] = feeder_chain_formats_32, + [FEEDER_CHAIN_MULTI] = feeder_chain_formats_multi, + [FEEDER_CHAIN_FULLMULTI] = feeder_chain_formats_fullmulti +}; + +static int feeder_chain_mode = FEEDER_CHAIN_DEFAULT; + +#if defined(_KERNEL) && defined(SND_DEBUG) && defined(FEEDER_FULL_MULTIFORMAT) +TUNABLE_INT("hw.snd.feeder_chain_mode", &feeder_chain_mode); +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_chain_mode, CTLFLAG_RW, + &feeder_chain_mode, 0, + "feeder chain mode " + "(0=lean, 1=16bit, 2=32bit, 3=multiformat, 4=fullmultiformat)"); +#endif + +static int +feeder_build_format(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + desc = &(cdesc->desc); + desc->type = FEEDER_FORMAT; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_format\n", __func__); + return (EOPNOTSUPP); + } + + desc->in = cdesc->current.afmt; + desc->out = cdesc->target.afmt; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_format\n", __func__); + return (ret); + } + + f = c->feeder; + + ret = FEEDER_SET(f, FEEDFORMAT_CHANNELS, + cdesc->current.matrix->channels); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set total channels on feeder_format\n", + __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_FORMAT; + + cdesc->current.afmt = cdesc->target.afmt; + + return (0); +} + +static int +feeder_build_formatne(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_chain_state otarget; + int ret; + + if (cdesc->afmt_ne == 0 || + AFMT_ENCODING(cdesc->current.afmt) == cdesc->afmt_ne) + return (0); + + otarget = cdesc->target; + cdesc->target = cdesc->current; + cdesc->target.afmt = SND_FORMAT(cdesc->afmt_ne, + cdesc->current.matrix->channels, cdesc->current.matrix->ext); + + ret = feeder_build_format(c, cdesc); + if (ret != 0) + return (ret); + + cdesc->target = otarget; + + return (0); +} + +static int +feeder_build_rate(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + ret = feeder_build_formatne(c, cdesc); + if (ret != 0) + return (ret); + + desc = &(cdesc->desc); + desc->type = FEEDER_RATE; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_rate\n", __func__); + return (EOPNOTSUPP); + } + + desc->in = cdesc->current.afmt; + desc->out = desc->in; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_rate\n", __func__); + return (ret); + } + + f = c->feeder; + + ret = FEEDER_SET(f, FEEDRATE_SRC, cdesc->current.rate); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set source rate\n", __func__); + return (ret); + } + + ret = FEEDER_SET(f, FEEDRATE_DST, cdesc->target.rate); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set destination rate\n", __func__); + return (ret); + } + + ret = FEEDER_SET(f, FEEDRATE_CHANNELS, cdesc->current.matrix->channels); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set total channels on feeder_rate\n", + __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_RATE; + + cdesc->current.rate = cdesc->target.rate; + + return (0); +} + +static int +feeder_build_matrix(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + ret = feeder_build_formatne(c, cdesc); + if (ret != 0) + return (ret); + + desc = &(cdesc->desc); + desc->type = FEEDER_MATRIX; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_matrix\n", __func__); + return (EOPNOTSUPP); + } + + desc->in = cdesc->current.afmt; + desc->out = SND_FORMAT(cdesc->current.afmt, + cdesc->target.matrix->channels, cdesc->target.matrix->ext); + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_matrix\n", __func__); + return (ret); + } + + f = c->feeder; + ret = feeder_matrix_setup(f, cdesc->current.matrix, + cdesc->target.matrix); + if (ret != 0) { + device_printf(c->dev, + "%s(): feeder_matrix_setup() failed\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_MATRIX; + + cdesc->current.afmt = desc->out; + cdesc->current.matrix = cdesc->target.matrix; + cdesc->use_matrix = 0; + + return (0); +} + +static int +feeder_build_volume(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + ret = feeder_build_formatne(c, cdesc); + if (ret != 0) + return (ret); + + desc = &(cdesc->desc); + desc->type = FEEDER_VOLUME; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_volume\n", __func__); + return (EOPNOTSUPP); + } + + desc->in = cdesc->current.afmt; + desc->out = desc->in; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_volume\n", __func__); + return (ret); + } + + f = c->feeder; + + ret = FEEDER_SET(f, FEEDVOLUME_CLASS, SND_VOL_C_PCM); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set pcm volume class\n", __func__); + return (ret); + } + + ret = FEEDER_SET(f, FEEDVOLUME_CHANNELS, + cdesc->current.matrix->channels); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set total channels on feeder_volume\n", + __func__); + return (ret); + } + + ret = feeder_volume_apply_matrix(f, cdesc->current.matrix); + if (ret != 0) { + device_printf(c->dev, + "%s(): feeder_volume_apply_matrix() failed\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_VOLUME; + + cdesc->use_volume = 0; + + return (0); +} + +static int +feeder_build_eq(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + ret = feeder_build_formatne(c, cdesc); + if (ret != 0) + return (ret); + + desc = &(cdesc->desc); + desc->type = FEEDER_EQ; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_eq\n", __func__); + return (EOPNOTSUPP); + } + + desc->in = cdesc->current.afmt; + desc->out = desc->in; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_eq\n", __func__); + return (ret); + } + + f = c->feeder; + + ret = FEEDER_SET(f, FEEDEQ_RATE, cdesc->current.rate); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set rate on feeder_eq\n", __func__); + return (ret); + } + + ret = FEEDER_SET(f, FEEDEQ_CHANNELS, cdesc->current.matrix->channels); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set total channels on feeder_eq\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_EQ; + + cdesc->use_eq = 0; + + return (0); +} + +static int +feeder_build_root(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + int ret; + + fc = feeder_getclass(NULL); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_root\n", __func__); + return (EOPNOTSUPP); + } + + ret = chn_addfeeder(c, fc, NULL); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_root\n", __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_ROOT; + + c->feeder->desc->in = cdesc->current.afmt; + c->feeder->desc->out = cdesc->current.afmt; + + return (0); +} + +static int +feeder_build_mixer(struct pcm_channel *c, struct feeder_chain_desc *cdesc) +{ + struct feeder_class *fc; + struct pcm_feeder *f; + struct pcm_feederdesc *desc; + int ret; + + desc = &(cdesc->desc); + desc->type = FEEDER_MIXER; + desc->in = 0; + desc->out = 0; + desc->flags = 0; + + fc = feeder_getclass(desc); + if (fc == NULL) { + device_printf(c->dev, + "%s(): can't find feeder_mixer\n", __func__); + return (EOPNOTSUPP); + } + + desc->in = cdesc->current.afmt; + desc->out = desc->in; + + ret = chn_addfeeder(c, fc, desc); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't add feeder_mixer\n", __func__); + return (ret); + } + + f = c->feeder; + + ret = FEEDER_SET(f, FEEDMIXER_CHANNELS, + cdesc->current.matrix->channels); + if (ret != 0) { + device_printf(c->dev, + "%s(): can't set total channels on feeder_mixer\n", + __func__); + return (ret); + } + + c->feederflags |= 1 << FEEDER_MIXER; + + return (0); +} + +#define FEEDER_BW(c, t) ((c)->t.matrix->channels * (c)->t.rate) + +#define FEEDRATE_UP(c) ((c)->target.rate > (c)->current.rate) +#define FEEDRATE_DOWN(c) ((c)->target.rate < (c)->current.rate) +#define FEEDRATE_REQUIRED(c) (FEEDRATE_UP(c) || FEEDRATE_DOWN(c)) + +#define FEEDMATRIX_UP(c) ((c)->target.matrix->channels > \ + (c)->current.matrix->channels) +#define FEEDMATRIX_DOWN(c) ((c)->target.matrix->channels < \ + (c)->current.matrix->channels) +#define FEEDMATRIX_REQUIRED(c) (FEEDMATRIX_UP(c) || \ + FEEDMATRIX_DOWN(c) || (c)->use_matrix != 0) + +#define FEEDFORMAT_REQUIRED(c) (AFMT_ENCODING((c)->current.afmt) != \ + AFMT_ENCODING((c)->target.afmt)) + +#define FEEDVOLUME_REQUIRED(c) ((c)->use_volume != 0) + +#define FEEDEQ_VALIDRATE(c, t) (feeder_eq_validrate((c)->t.rate) != 0) +#define FEEDEQ_ECONOMY(c) (FEEDER_BW(c, current) < FEEDER_BW(c, target)) +#define FEEDEQ_REQUIRED(c) ((c)->use_eq != 0 && \ + FEEDEQ_VALIDRATE(c, current)) + +#define FEEDFORMAT_NE_REQUIRED(c) \ + ((c)->afmt_ne != AFMT_S32_NE && \ + (((c)->mode == FEEDER_CHAIN_16 && \ + AFMT_ENCODING((c)->current.afmt) != AFMT_S16_NE) || \ + ((c)->mode == FEEDER_CHAIN_32 && \ + AFMT_ENCODING((c)->current.afmt) != AFMT_S32_NE) || \ + (c)->mode == FEEDER_CHAIN_FULLMULTI || \ + ((c)->mode == FEEDER_CHAIN_MULTI && \ + ((c)->current.afmt & AFMT_8BIT)) || \ + ((c)->mode == FEEDER_CHAIN_LEAN && \ + !((c)->current.afmt & (AFMT_S16_NE | AFMT_S32_NE))))) + +int +feeder_chain(struct pcm_channel *c) +{ + struct snddev_info *d; + struct pcmchan_caps *caps; + struct feeder_chain_desc cdesc; + struct pcmchan_matrix *hwmatrix, *softmatrix; + uint32_t hwfmt, softfmt; + int ret; + + CHN_LOCKASSERT(c); + + while (chn_removefeeder(c) == 0) + ; + + KASSERT(c->feeder == NULL, ("feeder chain not empty")); + + bzero(&cdesc, sizeof(cdesc)); + + switch (feeder_chain_mode) { + case FEEDER_CHAIN_LEAN: + case FEEDER_CHAIN_16: + case FEEDER_CHAIN_32: +#if defined(FEEDER_MULTIFORMAT) || defined(FEEDER_FULL_MULTIFORMAT) + case FEEDER_CHAIN_MULTI: +#endif +#if defined(FEEDER_FULL_MULTIFORMAT) + case FEEDER_CHAIN_FULLMULTI: +#endif + break; + default: + feeder_chain_mode = FEEDER_CHAIN_DEFAULT; + break; + } + + cdesc.mode = feeder_chain_mode; + cdesc.expensive = 1; /* XXX faster.. but why? */ + + caps = chn_getcaps(c); + if (caps == NULL || caps->fmtlist == NULL) { + device_printf(c->dev, + "%s(): failed to get channel caps\n", __func__); + return (ENODEV); + } + + if (CHN_PASSTHROUGH(c) && !snd_fmtvalid(AFMT_AC3, caps->fmtlist)) + return (ENODEV); + + hwfmt = snd_fmtbest(c->format, caps->fmtlist); + if (hwfmt == 0 || !snd_fmtvalid(hwfmt, caps->fmtlist)) { + device_printf(c->dev, "%s(): invalid hardware format 0x%08x\n", + __func__, hwfmt); + return (ENODEV); + } + + hwmatrix = CHANNEL_GETMATRIX(c->methods, c->devinfo, hwfmt); + if (hwmatrix == NULL) { + device_printf(c->dev, + "%s(): failed to acquire hw matrix [0x%08x]\n", + __func__, hwfmt); + return (ENODEV); + } + hwfmt = SND_FORMAT(hwfmt, hwmatrix->channels, hwmatrix->ext); + + softfmt = c->format; + softmatrix = &c->matrix; + if (softmatrix->channels != AFMT_CHANNEL(softfmt) || + softmatrix->ext != AFMT_EXTCHANNEL(softfmt)) { + softmatrix = feeder_matrix_format_map(softfmt); + if (softmatrix == NULL) { + device_printf(c->dev, + "%s(): failed to acquire soft matrix [0x%08x]\n", + __func__, softfmt); + return (ENODEV); + } + c->matrix = *softmatrix; + c->matrix.id = SND_CHN_MATRIX_PCMCHANNEL; + } + softfmt = SND_FORMAT(softfmt, softmatrix->channels, softmatrix->ext); + if (softfmt != c->format) + device_printf(c->dev, + "%s(): WARNING: %s Soft format 0x%08x -> 0x%08x\n", + __func__, CHN_DIRSTR(c), c->format, softfmt); + + if (c->direction == PCMDIR_PLAY) { + cdesc.origin.afmt = softfmt; + cdesc.origin.matrix = softmatrix; + cdesc.origin.rate = c->speed; + cdesc.target.afmt = hwfmt; + cdesc.target.matrix = hwmatrix; + cdesc.target.rate = sndbuf_getspd(c->bufhard); + } else { + cdesc.origin.afmt = hwfmt; + cdesc.origin.matrix = hwmatrix; + cdesc.origin.rate = sndbuf_getspd(c->bufhard); + cdesc.target.afmt = softfmt; + cdesc.target.matrix = softmatrix; + cdesc.target.rate = c->speed; + } + + d = c->parentsnddev; + + if (CHN_BITPERFECT(c) || CHN_PASSTHROUGH(c)) { + if (c->direction == PCMDIR_PLAY) + cdesc.origin = cdesc.target; + else + cdesc.target = cdesc.origin; + c->format = cdesc.target.afmt; + c->speed = cdesc.target.rate; + } else { + if (((d->flags & SD_F_VPC) && !(c->flags & CHN_F_HAS_VCHAN)) || + (!(d->flags & SD_F_VPC) && (d->flags & SD_F_SOFTPCMVOL) && + !(c->flags & CHN_F_VIRTUAL))) + cdesc.use_volume = 1; + + if (feeder_matrix_compare(cdesc.origin.matrix, + cdesc.target.matrix) != 0) + cdesc.use_matrix = 1; + + if (c->direction == PCMDIR_PLAY && (d->flags & SD_F_EQ) && + (((d->flags & SD_F_EQ_PC) && + !(c->flags & CHN_F_HAS_VCHAN)) || + (!(d->flags & SD_F_EQ_PC) && !(c->flags & CHN_F_VIRTUAL)))) + cdesc.use_eq = 1; + + if (FEEDFORMAT_NE_REQUIRED(&cdesc)) { + cdesc.afmt_ne = + snd_fmtbest(AFMT_ENCODING(cdesc.target.afmt), + feeder_chain_formats[cdesc.mode]); + if (cdesc.afmt_ne == 0) { + device_printf(c->dev, + "%s(): snd_fmtbest failed!\n", __func__); + cdesc.afmt_ne = (cdesc.target.afmt & + (AFMT_24BIT | AFMT_32BIT)) ? + AFMT_S32_NE : AFMT_S16_NE; + } + } + } + + cdesc.current = cdesc.origin; + + /* Build everything */ + + c->feederflags = 0; + +#define FEEDER_BUILD(t) do { \ + ret = feeder_build_##t(c, &cdesc); \ + if (ret != 0) \ + return (ret); \ + } while (0) + + if (!(c->flags & CHN_F_HAS_VCHAN) || c->direction == PCMDIR_REC) + FEEDER_BUILD(root); + else if (c->direction == PCMDIR_PLAY && (c->flags & CHN_F_HAS_VCHAN)) + FEEDER_BUILD(mixer); + else + return (EOPNOTSUPP); + +#if 0 + if (c->direction == PCMDIR_PLAY) + goto feeder_chain_pcmdir_play; + else if (c->direction == PCMDIR_REC) + goto feeder_chain_pcmdir_rec; + else + goto feeder_chain_out; + +feeder_chain_pcmdir_play: + /* PCMDIR_PLAY */ +#endif + if (FEEDMATRIX_UP(&cdesc)) { + if (FEEDEQ_REQUIRED(&cdesc) && + (!FEEDEQ_VALIDRATE(&cdesc, target) || + (cdesc.expensive == 0 && FEEDEQ_ECONOMY(&cdesc)))) + FEEDER_BUILD(eq); + if (FEEDRATE_REQUIRED(&cdesc)) + FEEDER_BUILD(rate); + FEEDER_BUILD(matrix); + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + if (FEEDEQ_REQUIRED(&cdesc)) + FEEDER_BUILD(eq); + } else if (FEEDMATRIX_DOWN(&cdesc)) { + FEEDER_BUILD(matrix); + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + if (FEEDEQ_REQUIRED(&cdesc) && + (!FEEDEQ_VALIDRATE(&cdesc, target) || + FEEDEQ_ECONOMY(&cdesc))) + FEEDER_BUILD(eq); + if (FEEDRATE_REQUIRED(&cdesc)) + FEEDER_BUILD(rate); + if (FEEDEQ_REQUIRED(&cdesc)) + FEEDER_BUILD(eq); + } else { + if (FEEDRATE_DOWN(&cdesc)) { + if (FEEDEQ_REQUIRED(&cdesc) && + !FEEDEQ_VALIDRATE(&cdesc, target)) { + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + FEEDER_BUILD(eq); + } + FEEDER_BUILD(rate); + } + if (FEEDMATRIX_REQUIRED(&cdesc)) + FEEDER_BUILD(matrix); + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + if (FEEDRATE_UP(&cdesc)) { + if (FEEDEQ_REQUIRED(&cdesc) && + !FEEDEQ_VALIDRATE(&cdesc, target)) + FEEDER_BUILD(eq); + FEEDER_BUILD(rate); + } + if (FEEDEQ_REQUIRED(&cdesc)) + FEEDER_BUILD(eq); + } + +#if 0 + goto feeder_chain_out; + +feeder_chain_pcmdir_rec: + /* PCMDIR_REC */ + if (FEEDMATRIX_UP(&cdesc)) { + if (FEEDEQ_REQUIRED(&cdesc) && + (!FEEDEQ_VALIDRATE(&cdesc, target) || + (cdesc.expensive == 0 && FEEDEQ_ECONOMY(&cdesc)))) + FEEDER_BUILD(eq); + if (FEEDRATE_REQUIRED(&cdesc)) + FEEDER_BUILD(rate); + FEEDER_BUILD(matrix); + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + if (FEEDEQ_REQUIRED(&cdesc)) + FEEDER_BUILD(eq); + } else if (FEEDMATRIX_DOWN(&cdesc)) { + FEEDER_BUILD(matrix); + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + if (FEEDEQ_REQUIRED(&cdesc) && + (!FEEDEQ_VALIDRATE(&cdesc, target) || + FEEDEQ_ECONOMY(&cdesc))) + FEEDER_BUILD(eq); + if (FEEDRATE_REQUIRED(&cdesc)) + FEEDER_BUILD(rate); + if (FEEDEQ_REQUIRED(&cdesc)) + FEEDER_BUILD(eq); + } else { + if (FEEDRATE_DOWN(&cdesc)) { + if (FEEDEQ_REQUIRED(&cdesc) && + !FEEDEQ_VALIDRATE(&cdesc, target)) + FEEDER_BUILD(eq); + FEEDER_BUILD(rate); + } + if (FEEDMATRIX_REQUIRED(&cdesc)) + FEEDER_BUILD(matrix); + if (FEEDVOLUME_REQUIRED(&cdesc)) + FEEDER_BUILD(volume); + if (FEEDRATE_UP(&cdesc)) { + if (FEEDEQ_REQUIRED(&cdesc) && + !FEEDEQ_VALIDRATE(&cdesc, target)) + FEEDER_BUILD(eq); + FEEDER_BUILD(rate); + } + if (FEEDEQ_REQUIRED(&cdesc)) + FEEDER_BUILD(eq); + } + +feeder_chain_out: +#endif + if (FEEDFORMAT_REQUIRED(&cdesc)) + FEEDER_BUILD(format); + + if (c->direction == PCMDIR_REC && (c->flags & CHN_F_HAS_VCHAN)) + FEEDER_BUILD(mixer); + + sndbuf_setfmt(c->bufsoft, c->format); + sndbuf_setspd(c->bufsoft, c->speed); + + sndbuf_setfmt(c->bufhard, hwfmt); + + chn_syncstate(c); + + return (0); +} --- sys/dev/sound/pcm/feeder_eq.c.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/feeder_eq.c 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,673 @@ +/*- + * Copyright (c) 2008-2009 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. + */ + +#ifdef _KERNEL +#include +#include +#include "feeder_if.h" + +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + +SND_DECLARE_FILE("$FreeBSD$"); +#endif + +#include "feeder_eq_gen.h" + +#define FEEDEQ_LEVELS \ + (((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) * \ + (FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1) + +#define FEEDEQ_L2GAIN(v) \ + ((int)min(((v) * FEEDEQ_LEVELS) / 100, FEEDEQ_LEVELS - 1)) + +#define FEEDEQ_PREAMP_IPART(x) (abs(x) >> FEEDEQ_GAIN_SHIFT) +#define FEEDEQ_PREAMP_FPART(x) (abs(x) & FEEDEQ_GAIN_FMASK) +#define FEEDEQ_PREAMP_SIGNVAL(x) ((x) < 0 ? -1 : 1) +#define FEEDEQ_PREAMP_SIGNMARK(x) (((x) < 0) ? '-' : '+') + +#define FEEDEQ_PREAMP_IMIN -192 +#define FEEDEQ_PREAMP_IMAX 192 +#define FEEDEQ_PREAMP_FMIN 0 +#define FEEDEQ_PREAMP_FMAX 9 + +#define FEEDEQ_PREAMP_INVALID INT_MAX + +#define FEEDEQ_IF2PREAMP(i, f) \ + ((abs(i) << FEEDEQ_GAIN_SHIFT) | \ + (((abs(f) / FEEDEQ_GAIN_STEP) * FEEDEQ_GAIN_STEP) & \ + FEEDEQ_GAIN_FMASK)) + +#define FEEDEQ_PREAMP_MIN \ + (FEEDEQ_PREAMP_SIGNVAL(FEEDEQ_GAIN_MIN) * \ + FEEDEQ_IF2PREAMP(FEEDEQ_GAIN_MIN, 0)) + +#define FEEDEQ_PREAMP_MAX \ + (FEEDEQ_PREAMP_SIGNVAL(FEEDEQ_GAIN_MAX) * \ + FEEDEQ_IF2PREAMP(FEEDEQ_GAIN_MAX, 0)) + +#define FEEDEQ_PREAMP_DEFAULT FEEDEQ_IF2PREAMP(0, 0) + +#define FEEDEQ_PREAMP2IDX(v) \ + ((int32_t)((FEEDEQ_GAIN_MAX * (FEEDEQ_GAIN_DIV / \ + FEEDEQ_GAIN_STEP)) + (FEEDEQ_PREAMP_SIGNVAL(v) * \ + FEEDEQ_PREAMP_IPART(v) * (FEEDEQ_GAIN_DIV / \ + FEEDEQ_GAIN_STEP)) + (FEEDEQ_PREAMP_SIGNVAL(v) * \ + (FEEDEQ_PREAMP_FPART(v) / FEEDEQ_GAIN_STEP)))) + +static int feeder_eq_exact_rate = 0; + +#ifdef _KERNEL +TUNABLE_INT("hw.snd.feeder_eq_exact_rate", &feeder_eq_exact_rate); +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_eq_exact_rate, CTLFLAG_RW, + &feeder_eq_exact_rate, 0, "force exact rate validation"); +#endif + +struct feed_eq_info; + +typedef void (*feed_eq_t)(struct feed_eq_info *, uint8_t *, uint32_t); + +struct feed_eq_tone { + intpcm_t o1[SND_CHN_MAX]; + intpcm_t o2[SND_CHN_MAX]; + intpcm_t i1[SND_CHN_MAX]; + intpcm_t i2[SND_CHN_MAX]; + int gain; +}; + +struct feed_eq_info { + struct feed_eq_tone treble; + struct feed_eq_tone bass; + struct feed_eq_coeff *coeff; + feed_eq_t iir; + uint32_t channels; + uint32_t rate; + uint32_t align; + int32_t preamp; + int state; +}; + +#if !defined(_KERNEL) && defined(FEEDEQ_ERR_CLIP) +#define FEEDEQ_ERR_CLIP_CHECK(t, v) do { \ + if ((v) < PCM_S32_MIN || (v) > PCM_S32_MAX) \ + errx(1, "\n\n%s(): ["#t"] Sample clipping: %jd\n", \ + __func__, (intmax_t)(v)); \ +} while (0) +#else +#define FEEDEQ_ERR_CLIP_CHECK(...) +#endif + +#define FEEDEQ_CLAMP(v) (((v) > PCM_S32_MAX) ? PCM_S32_MAX : \ + (((v) < PCM_S32_MIN) ? PCM_S32_MIN : \ + (v))) + +#define FEEDEQ_DECLARE(SIGN, BIT, ENDIAN) \ +static void \ +feed_eq_iir_##SIGN##BIT##ENDIAN(struct feed_eq_info *info, \ + uint8_t *dst, uint32_t count) \ +{ \ + struct feed_eq_coeff_tone *treble, *bass; \ + intpcm64_t w; \ + intpcm_t v; \ + uint32_t i, j; \ + int32_t pmul, pshift; \ + \ + pmul = feed_eq_preamp[info->preamp].mul; \ + pshift = feed_eq_preamp[info->preamp].shift; \ + \ + if (info->state == FEEDEQ_DISABLE) { \ + j = count * info->channels; \ + dst += j * PCM_##BIT##_BPS; \ + do { \ + dst -= PCM_##BIT##_BPS; \ + v = _PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ + v = ((intpcm64_t)pmul * v) >> pshift; \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \ + } while (--j != 0); \ + \ + return; \ + } \ + \ + treble = &(info->coeff[info->treble.gain].treble); \ + bass = &(info->coeff[info->bass.gain].bass); \ + \ + do { \ + i = 0; \ + j = info->channels; \ + do { \ + v = _PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ + v <<= 32 - BIT; \ + v = ((intpcm64_t)pmul * v) >> pshift; \ + \ + w = (intpcm64_t)v * treble->b0; \ + w += (intpcm64_t)info->treble.i1[i] * treble->b1; \ + w += (intpcm64_t)info->treble.i2[i] * treble->b2; \ + w -= (intpcm64_t)info->treble.o1[i] * treble->a1; \ + w -= (intpcm64_t)info->treble.o2[i] * treble->a2; \ + info->treble.i2[i] = info->treble.i1[i]; \ + info->treble.i1[i] = v; \ + info->treble.o2[i] = info->treble.o1[i]; \ + w >>= FEEDEQ_COEFF_SHIFT; \ + FEEDEQ_ERR_CLIP_CHECK(treble, w); \ + v = FEEDEQ_CLAMP(w); \ + info->treble.o1[i] = v; \ + \ + w = (intpcm64_t)v * bass->b0; \ + w += (intpcm64_t)info->bass.i1[i] * bass->b1; \ + w += (intpcm64_t)info->bass.i2[i] * bass->b2; \ + w -= (intpcm64_t)info->bass.o1[i] * bass->a1; \ + w -= (intpcm64_t)info->bass.o2[i] * bass->a2; \ + info->bass.i2[i] = info->bass.i1[i]; \ + info->bass.i1[i] = v; \ + info->bass.o2[i] = info->bass.o1[i]; \ + w >>= FEEDEQ_COEFF_SHIFT; \ + FEEDEQ_ERR_CLIP_CHECK(bass, w); \ + v = FEEDEQ_CLAMP(w); \ + info->bass.o1[i] = v; \ + \ + v >>= 32 - BIT; \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \ + dst += PCM_##BIT##_BPS; \ + i++; \ + } while (--j != 0); \ + } while (--count != 0); \ +} + +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) +FEEDEQ_DECLARE(S, 16, LE) +FEEDEQ_DECLARE(S, 32, LE) +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) +FEEDEQ_DECLARE(S, 16, BE) +FEEDEQ_DECLARE(S, 32, BE) +#endif +#ifdef FEEDER_MULTIFORMAT +FEEDEQ_DECLARE(S, 8, NE) +FEEDEQ_DECLARE(S, 24, LE) +FEEDEQ_DECLARE(S, 24, BE) +FEEDEQ_DECLARE(U, 8, NE) +FEEDEQ_DECLARE(U, 16, LE) +FEEDEQ_DECLARE(U, 24, LE) +FEEDEQ_DECLARE(U, 32, LE) +FEEDEQ_DECLARE(U, 16, BE) +FEEDEQ_DECLARE(U, 24, BE) +FEEDEQ_DECLARE(U, 32, BE) +#endif + +#define FEEDEQ_ENTRY(SIGN, BIT, ENDIAN) \ + { \ + AFMT_##SIGN##BIT##_##ENDIAN, \ + feed_eq_iir_##SIGN##BIT##ENDIAN \ + } + + +static const struct { + uint32_t format; + feed_eq_t iir; +} feed_eq_iir_tab[] = { +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) + FEEDEQ_ENTRY(S, 16, LE), + FEEDEQ_ENTRY(S, 32, LE), +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) + FEEDEQ_ENTRY(S, 16, BE), + FEEDEQ_ENTRY(S, 32, BE), +#endif +#ifdef FEEDER_MULTIFORMAT + FEEDEQ_ENTRY(S, 8, NE), + FEEDEQ_ENTRY(S, 24, LE), + FEEDEQ_ENTRY(S, 24, BE), + FEEDEQ_ENTRY(U, 8, NE), + FEEDEQ_ENTRY(U, 16, LE), + FEEDEQ_ENTRY(U, 24, LE), + FEEDEQ_ENTRY(U, 32, LE), + FEEDEQ_ENTRY(U, 16, BE), + FEEDEQ_ENTRY(U, 24, BE), + FEEDEQ_ENTRY(U, 32, BE) +#endif +}; + +#define FEEDEQ_IIR_TAB_SIZE \ + ((int32_t)(sizeof(feed_eq_iir_tab) / sizeof(feed_eq_iir_tab[0]))) + +static struct feed_eq_coeff * +feed_eq_coeff_rate(uint32_t rate) +{ + uint32_t spd, threshold; + int i; + + if (rate < FEEDEQ_RATE_MIN || rate > FEEDEQ_RATE_MAX) + return (NULL); + + for (i = 0; i < FEEDEQ_TAB_SIZE; i++) { + spd = feed_eq_tab[i].rate; + threshold = spd + ((i < (FEEDEQ_TAB_SIZE - 1) && + feed_eq_tab[i + 1].rate > spd) ? + ((feed_eq_tab[i + 1].rate - spd) >> 1) : 0); + if (rate == spd || + (feeder_eq_exact_rate == 0 && rate <= threshold)) + return (feed_eq_tab[i].coeff); + } + + return (NULL); +} + +int +feeder_eq_validrate(uint32_t rate) +{ + + if (feed_eq_coeff_rate(rate) != NULL) + return (1); + + return (0); +} + +static void +feed_eq_reset(struct feed_eq_info *info) +{ + uint32_t i; + + for (i = 0; i < info->channels; i++) { + info->treble.i1[i] = 0; + info->treble.i2[i] = 0; + info->treble.o1[i] = 0; + info->treble.o2[i] = 0; + info->bass.i1[i] = 0; + info->bass.i2[i] = 0; + info->bass.o1[i] = 0; + info->bass.o2[i] = 0; + } +} + +static int +feed_eq_setup(struct feed_eq_info *info) +{ + + info->coeff = feed_eq_coeff_rate(info->rate); + if (info->coeff == NULL) + return (EINVAL); + + feed_eq_reset(info); + + return (0); +} + +static int +feed_eq_init(struct pcm_feeder *f) +{ + struct feed_eq_info *info; + feed_eq_t iir_op; + int i; + + if (f->desc->in != f->desc->out) + return (EINVAL); + + iir_op = NULL; + + for (i = 0; i < FEEDEQ_IIR_TAB_SIZE && iir_op == NULL; i++) { + if (AFMT_ENCODING(f->desc->in) == feed_eq_iir_tab[i].format) + iir_op = feed_eq_iir_tab[i].iir; + } + + if (iir_op == NULL) + return (EINVAL); + + info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO); + if (info == NULL) + return (ENOMEM); + + info->channels = AFMT_CHANNEL(f->desc->in); + info->align = info->channels * AFMT_BPS(f->desc->in); + + info->rate = FEEDEQ_RATE_MIN; + info->treble.gain = FEEDEQ_L2GAIN(50); + info->bass.gain = FEEDEQ_L2GAIN(50); + info->preamp = FEEDEQ_PREAMP2IDX(FEEDEQ_PREAMP_DEFAULT); + info->state = FEEDEQ_UNKNOWN; + + info->iir = iir_op; + + f->data = info; + + return (feed_eq_setup(info)); +} + +static int +feed_eq_set(struct pcm_feeder *f, int what, int value) +{ + struct feed_eq_info *info; + + info = f->data; + + switch (what) { + case FEEDEQ_CHANNELS: + if (value < SND_CHN_MIN || value > SND_CHN_MAX) + return (EINVAL); + info->channels = (uint32_t)value; + info->align = info->channels * AFMT_BPS(f->desc->in); + feed_eq_reset(info); + break; + case FEEDEQ_RATE: + if (feeder_eq_validrate(value) == 0) + return (EINVAL); + info->rate = (uint32_t)value; + if (info->state == FEEDEQ_UNKNOWN) + info->state = FEEDEQ_ENABLE; + return (feed_eq_setup(info)); + break; + case FEEDEQ_TREBLE: + case FEEDEQ_BASS: + if (value < 0 || value > 100) + return (EINVAL); + if (what == FEEDEQ_TREBLE) + info->treble.gain = FEEDEQ_L2GAIN(value); + else + info->bass.gain = FEEDEQ_L2GAIN(value); + break; + case FEEDEQ_PREAMP: + if (value < FEEDEQ_PREAMP_MIN || value > FEEDEQ_PREAMP_MAX) + return (EINVAL); + info->preamp = FEEDEQ_PREAMP2IDX(value); + break; + case FEEDEQ_STATE: + if (!(value == FEEDEQ_BYPASS || value == FEEDEQ_ENABLE || + value == FEEDEQ_DISABLE)) + return (EINVAL); + info->state = value; + feed_eq_reset(info); + break; + default: + return (EINVAL); + break; + } + + return (0); +} +static int +feed_eq_free(struct pcm_feeder *f) +{ + struct feed_eq_info *info; + + info = f->data; + if (info != NULL) + free(info, M_DEVBUF); + + f->data = NULL; + + return (0); +} + +static int +feed_eq_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_eq_info *info; + uint32_t j; + uint8_t *dst; + + info = f->data; + if (info->state == FEEDEQ_BYPASS) + return (FEEDER_FEED(f->source, c, b, count, source)); + + dst = b; + count = SND_FXROUND(count, info->align); + + do { + if (count < info->align) + break; + + j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source), + info->align); + if (j == 0) + break; + + info->iir(info, dst, j); + + j *= info->align; + dst += j; + count -= j; + + } while (count != 0); + + return (dst - b); +} + +static struct pcm_feederdesc feeder_eq_desc[] = { + { FEEDER_EQ, 0, 0, 0 }, + { 0, 0, 0, 0 } +}; + +static kobj_method_t feeder_eq_methods[] = { + KOBJMETHOD(feeder_init, feed_eq_init), + KOBJMETHOD(feeder_free, feed_eq_free), + KOBJMETHOD(feeder_set, feed_eq_set), + KOBJMETHOD(feeder_feed, feed_eq_feed), + { 0, 0 } +}; + +FEEDER_DECLARE(feeder_eq, 0, NULL); + +static int32_t +feed_eq_scan_preamp_arg(const char *s) +{ + int r, i, f; + size_t len; + char buf[32]; + + bzero(buf, sizeof(buf)); + + r = sscanf(s, "%d.%d", &i, &f); + + if (r == 1 && !(i < FEEDEQ_PREAMP_IMIN || i > FEEDEQ_PREAMP_IMAX)) { + snprintf(buf, sizeof(buf), "%c%d", + FEEDEQ_PREAMP_SIGNMARK(i), abs(i)); + f = 0; + } else if (r == 2 && + !(i < FEEDEQ_PREAMP_IMIN || i > FEEDEQ_PREAMP_IMAX || + f < FEEDEQ_PREAMP_FMIN || f > FEEDEQ_PREAMP_FMAX)) + snprintf(buf, sizeof(buf), "%c%d.%d", + FEEDEQ_PREAMP_SIGNMARK(i), abs(i), f); + else + return (FEEDEQ_PREAMP_INVALID); + + len = strlen(s); + if (len > 2 && strcasecmp(s + len - 2, "dB") == 0) + strlcat(buf, "dB", sizeof(buf)); + + if (i == 0 && *s == '-') + *buf = '-'; + + if (strcasecmp(buf + ((*s >= '0' && *s <= '9') ? 1 : 0), s) != 0) + return (FEEDEQ_PREAMP_INVALID); + + while ((f / FEEDEQ_GAIN_DIV) > 0) + f /= FEEDEQ_GAIN_DIV; + + return (((i < 0 || *buf == '-') ? -1 : 1) * FEEDEQ_IF2PREAMP(i, f)); +} + +#ifdef _KERNEL +static int +sysctl_dev_pcm_eq(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct pcm_channel *c; + struct pcm_feeder *f; + int err, val, oval; + + d = oidp->oid_arg1; + if (!PCM_REGISTERED(d)) + return (ENODEV); + + PCM_LOCK(d); + PCM_WAIT(d); + if (d->flags & SD_F_EQ_BYPASSED) + val = 2; + else if (d->flags & SD_F_EQ_ENABLED) + val = 1; + else + val = 0; + PCM_ACQUIRE(d); + PCM_UNLOCK(d); + + oval = val; + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err == 0 && req->newptr != NULL && val != oval) { + if (!(val == 0 || val == 1 || val == 2)) { + PCM_RELEASE_QUICK(d); + return (EINVAL); + } + + PCM_LOCK(d); + + d->flags &= ~(SD_F_EQ_ENABLED | SD_F_EQ_BYPASSED); + if (val == 2) { + val = FEEDEQ_BYPASS; + d->flags |= SD_F_EQ_BYPASSED; + } else if (val == 1) { + val = FEEDEQ_ENABLE; + d->flags |= SD_F_EQ_ENABLED; + } else + val = FEEDEQ_DISABLE; + + CHN_FOREACH(c, d, channels.pcm.busy) { + CHN_LOCK(c); + f = chn_findfeeder(c, FEEDER_EQ); + if (f != NULL) + (void)FEEDER_SET(f, FEEDEQ_STATE, val); + CHN_UNLOCK(c); + } + + PCM_RELEASE(d); + PCM_UNLOCK(d); + } else + PCM_RELEASE_QUICK(d); + + return (err); +} + +static int +sysctl_dev_pcm_eq_preamp(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct pcm_channel *c; + struct pcm_feeder *f; + int err, val, oval; + char buf[32]; + + d = oidp->oid_arg1; + if (!PCM_REGISTERED(d)) + return (ENODEV); + + PCM_LOCK(d); + PCM_WAIT(d); + val = d->eqpreamp; + bzero(buf, sizeof(buf)); + (void)snprintf(buf, sizeof(buf), "%c%d.%ddB", + FEEDEQ_PREAMP_SIGNMARK(val), FEEDEQ_PREAMP_IPART(val), + FEEDEQ_PREAMP_FPART(val)); + PCM_ACQUIRE(d); + PCM_UNLOCK(d); + + oval = val; + err = sysctl_handle_string(oidp, buf, sizeof(buf), req); + + if (err == 0 && req->newptr != NULL) { + val = feed_eq_scan_preamp_arg(buf); + if (val == FEEDEQ_PREAMP_INVALID) { + PCM_RELEASE_QUICK(d); + return (EINVAL); + } + + PCM_LOCK(d); + + if (val != oval) { + if (val < FEEDEQ_PREAMP_MIN) + val = FEEDEQ_PREAMP_MIN; + else if (val > FEEDEQ_PREAMP_MAX) + val = FEEDEQ_PREAMP_MAX; + + d->eqpreamp = val; + + CHN_FOREACH(c, d, channels.pcm.busy) { + CHN_LOCK(c); + f = chn_findfeeder(c, FEEDER_EQ); + if (f != NULL) + (void)FEEDER_SET(f, FEEDEQ_PREAMP, val); + CHN_UNLOCK(c); + } + + } + + PCM_RELEASE(d); + PCM_UNLOCK(d); + } else + PCM_RELEASE_QUICK(d); + + return (err); +} + +void +feeder_eq_initsys(device_t dev) +{ + struct snddev_info *d; + const char *preamp; + char buf[64]; + + d = device_get_softc(dev); + + if (!(resource_string_value(device_get_name(dev), device_get_unit(dev), + "eq_preamp", &preamp) == 0 && + (d->eqpreamp = feed_eq_scan_preamp_arg(preamp)) != + FEEDEQ_PREAMP_INVALID)) + d->eqpreamp = FEEDEQ_PREAMP_DEFAULT; + + if (d->eqpreamp < FEEDEQ_PREAMP_MIN) + d->eqpreamp = FEEDEQ_PREAMP_MIN; + else if (d->eqpreamp > FEEDEQ_PREAMP_MAX) + d->eqpreamp = FEEDEQ_PREAMP_MAX; + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "eq", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), + sysctl_dev_pcm_eq, "I", + "Bass/Treble Equalizer (0=disable, 1=enable, 2=bypass)"); + + bzero(buf, sizeof(buf)); + + (void)snprintf(buf, sizeof(buf), "Bass/Treble Equalizer Preamp " + "(-/+ %d.0dB , %d.%ddB step)", + FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV, + FEEDEQ_GAIN_STEP - ((FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV) * + FEEDEQ_GAIN_DIV)); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "eq_preamp", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d), + sysctl_dev_pcm_eq_preamp, "A", buf); +} +#endif --- sys/dev/sound/pcm/feeder_fmt.c.orig 2007-06-02 21:07:44.000000000 +0800 +++ sys/dev/sound/pcm/feeder_fmt.c 1970-01-01 07:30:00.000000000 +0730 @@ -1,1435 +0,0 @@ -/*- - * Copyright (c) 1999 Cameron Grant - * Copyright (c) 2005 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. - */ - -/* - * *New* and rewritten soft format converter, supporting 24/32bit pcm data, - * simplified and optimized. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * This new implementation is fully dedicated in memory of Cameron Grant, * - * the creator of the magnificent, highly addictive feeder infrastructure. * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * - */ - -#include -#include "feeder_if.h" - -SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_fmt.c,v 1.23 2007/06/02 13:07:44 joel Exp $"); - -static int feeder_fmt_stereodownmix = 0; -TUNABLE_INT("hw.snd.feeder_fmt_stereodownmix", &feeder_fmt_stereodownmix); -#ifdef SND_DEBUG -SYSCTL_INT(_hw_snd, OID_AUTO, feeder_fmt_stereodownmix, CTLFLAG_RW, - &feeder_fmt_stereodownmix, 1, "averaging stereo downmix"); -#endif - -#define FEEDFMT_RESERVOIR 8 /* 32bit stereo */ - -static uint8_t ulaw_to_u8_tbl[] = { - 3, 7, 11, 15, 19, 23, 27, 31, - 35, 39, 43, 47, 51, 55, 59, 63, - 66, 68, 70, 72, 74, 76, 78, 80, - 82, 84, 86, 88, 90, 92, 94, 96, - 98, 99, 100, 101, 102, 103, 104, 105, - 106, 107, 108, 109, 110, 111, 112, 113, - 113, 114, 114, 115, 115, 116, 116, 117, - 117, 118, 118, 119, 119, 120, 120, 121, - 121, 121, 122, 122, 122, 122, 123, 123, - 123, 123, 124, 124, 124, 124, 125, 125, - 125, 125, 125, 125, 126, 126, 126, 126, - 126, 126, 126, 126, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 253, 249, 245, 241, 237, 233, 229, 225, - 221, 217, 213, 209, 205, 201, 197, 193, - 190, 188, 186, 184, 182, 180, 178, 176, - 174, 172, 170, 168, 166, 164, 162, 160, - 158, 157, 156, 155, 154, 153, 152, 151, - 150, 149, 148, 147, 146, 145, 144, 143, - 143, 142, 142, 141, 141, 140, 140, 139, - 139, 138, 138, 137, 137, 136, 136, 135, - 135, 135, 134, 134, 134, 134, 133, 133, - 133, 133, 132, 132, 132, 132, 131, 131, - 131, 131, 131, 131, 130, 130, 130, 130, - 130, 130, 130, 130, 129, 129, 129, 129, - 129, 129, 129, 129, 129, 129, 129, 129, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, -}; - -static uint8_t alaw_to_u8_tbl[] = { - 108, 109, 106, 107, 112, 113, 110, 111, - 100, 101, 98, 99, 104, 105, 102, 103, - 118, 118, 117, 117, 120, 120, 119, 119, - 114, 114, 113, 113, 116, 116, 115, 115, - 43, 47, 35, 39, 59, 63, 51, 55, - 11, 15, 3, 7, 27, 31, 19, 23, - 86, 88, 82, 84, 94, 96, 90, 92, - 70, 72, 66, 68, 78, 80, 74, 76, - 127, 127, 127, 127, 127, 127, 127, 127, - 127, 127, 127, 127, 127, 127, 127, 127, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 123, 123, 123, 123, 124, 124, 124, 124, - 121, 121, 121, 121, 122, 122, 122, 122, - 126, 126, 126, 126, 126, 126, 126, 126, - 125, 125, 125, 125, 125, 125, 125, 125, - 148, 147, 150, 149, 144, 143, 146, 145, - 156, 155, 158, 157, 152, 151, 154, 153, - 138, 138, 139, 139, 136, 136, 137, 137, - 142, 142, 143, 143, 140, 140, 141, 141, - 213, 209, 221, 217, 197, 193, 205, 201, - 245, 241, 253, 249, 229, 225, 237, 233, - 170, 168, 174, 172, 162, 160, 166, 164, - 186, 184, 190, 188, 178, 176, 182, 180, - 129, 129, 129, 129, 129, 129, 129, 129, - 129, 129, 129, 129, 129, 129, 129, 129, - 128, 128, 128, 128, 128, 128, 128, 128, - 128, 128, 128, 128, 128, 128, 128, 128, - 133, 133, 133, 133, 132, 132, 132, 132, - 135, 135, 135, 135, 134, 134, 134, 134, - 130, 130, 130, 130, 130, 130, 130, 130, - 131, 131, 131, 131, 131, 131, 131, 131, -}; - -static uint8_t u8_to_ulaw_tbl[] = { - 0, 0, 0, 0, 0, 1, 1, 1, - 1, 2, 2, 2, 2, 3, 3, 3, - 3, 4, 4, 4, 4, 5, 5, 5, - 5, 6, 6, 6, 6, 7, 7, 7, - 7, 8, 8, 8, 8, 9, 9, 9, - 9, 10, 10, 10, 10, 11, 11, 11, - 11, 12, 12, 12, 12, 13, 13, 13, - 13, 14, 14, 14, 14, 15, 15, 15, - 15, 16, 16, 17, 17, 18, 18, 19, - 19, 20, 20, 21, 21, 22, 22, 23, - 23, 24, 24, 25, 25, 26, 26, 27, - 27, 28, 28, 29, 29, 30, 30, 31, - 31, 32, 33, 34, 35, 36, 37, 38, - 39, 40, 41, 42, 43, 44, 45, 46, - 47, 49, 51, 53, 55, 57, 59, 61, - 63, 66, 70, 74, 78, 84, 92, 104, - 254, 231, 219, 211, 205, 201, 197, 193, - 190, 188, 186, 184, 182, 180, 178, 176, - 175, 174, 173, 172, 171, 170, 169, 168, - 167, 166, 165, 164, 163, 162, 161, 160, - 159, 159, 158, 158, 157, 157, 156, 156, - 155, 155, 154, 154, 153, 153, 152, 152, - 151, 151, 150, 150, 149, 149, 148, 148, - 147, 147, 146, 146, 145, 145, 144, 144, - 143, 143, 143, 143, 142, 142, 142, 142, - 141, 141, 141, 141, 140, 140, 140, 140, - 139, 139, 139, 139, 138, 138, 138, 138, - 137, 137, 137, 137, 136, 136, 136, 136, - 135, 135, 135, 135, 134, 134, 134, 134, - 133, 133, 133, 133, 132, 132, 132, 132, - 131, 131, 131, 131, 130, 130, 130, 130, - 129, 129, 129, 129, 128, 128, 128, 128, -}; - -static uint8_t u8_to_alaw_tbl[] = { - 42, 42, 42, 42, 42, 43, 43, 43, - 43, 40, 40, 40, 40, 41, 41, 41, - 41, 46, 46, 46, 46, 47, 47, 47, - 47, 44, 44, 44, 44, 45, 45, 45, - 45, 34, 34, 34, 34, 35, 35, 35, - 35, 32, 32, 32, 32, 33, 33, 33, - 33, 38, 38, 38, 38, 39, 39, 39, - 39, 36, 36, 36, 36, 37, 37, 37, - 37, 58, 58, 59, 59, 56, 56, 57, - 57, 62, 62, 63, 63, 60, 60, 61, - 61, 50, 50, 51, 51, 48, 48, 49, - 49, 54, 54, 55, 55, 52, 52, 53, - 53, 10, 11, 8, 9, 14, 15, 12, - 13, 2, 3, 0, 1, 6, 7, 4, - 5, 24, 30, 28, 18, 16, 22, 20, - 106, 110, 98, 102, 122, 114, 75, 90, - 213, 197, 245, 253, 229, 225, 237, 233, - 149, 151, 145, 147, 157, 159, 153, 155, - 133, 132, 135, 134, 129, 128, 131, 130, - 141, 140, 143, 142, 137, 136, 139, 138, - 181, 181, 180, 180, 183, 183, 182, 182, - 177, 177, 176, 176, 179, 179, 178, 178, - 189, 189, 188, 188, 191, 191, 190, 190, - 185, 185, 184, 184, 187, 187, 186, 186, - 165, 165, 165, 165, 164, 164, 164, 164, - 167, 167, 167, 167, 166, 166, 166, 166, - 161, 161, 161, 161, 160, 160, 160, 160, - 163, 163, 163, 163, 162, 162, 162, 162, - 173, 173, 173, 173, 172, 172, 172, 172, - 175, 175, 175, 175, 174, 174, 174, 174, - 169, 169, 169, 169, 168, 168, 168, 168, - 171, 171, 171, 171, 170, 170, 170, 170, -}; - -static int -feed_table_8(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int j, sign, k; - uint8_t *tbl = (uint8_t *)f->data; - - if (count < PCM_8_BPS) - return (0); - - k = FEEDER_FEED(f->source, c, b, count, source); - if (k < PCM_8_BPS) - return (0); - - j = k; - sign = (f->desc->out & AFMT_SIGNED) ? 0x80 : 0x00; - - do { - j--; - b[j] = tbl[b[j]] ^ sign; - } while (j != 0); - - return (k); -} - -static int -feed_table_16(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, sign, k; - uint8_t *tbl = (uint8_t *)f->data; - - if (count < PCM_16_BPS) - return (0); - - k = FEEDER_FEED(f->source, c, b, count >> 1, source); - if (k < PCM_8_BPS) - return (0); - - i = k; - k <<= 1; - j = k; - sign = (f->desc->out & AFMT_SIGNED) ? 0x80 : 0x00; - - if (f->desc->out & AFMT_BIGENDIAN) { - do { - b[--j] = 0; - b[--j] = tbl[b[--i]] ^ sign; - } while (i != 0); - } else { - do { - b[--j] = tbl[b[--i]] ^ sign; - b[--j] = 0; - } while (i != 0); - } - - return (k); -} - -static int -feed_table_xlaw(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int j, sign, k; - uint8_t *tbl = (uint8_t *)f->data; - - if (count < PCM_8_BPS) - return (0); - - k = FEEDER_FEED(f->source, c, b, count, source); - if (k < PCM_8_BPS) - return (0); - - j = k ; - sign = (f->desc->in & AFMT_SIGNED) ? 0x80 : 0x00; - - do { - j--; - b[j] = tbl[b[j] ^ sign]; - } while (j != 0); - - return (k); -} - -static struct pcm_feederdesc feeder_ulawto8_desc[] = { - {FEEDER_FMT, AFMT_MU_LAW, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_ulawto8_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_8), - {0, 0} -}; -FEEDER_DECLARE(feeder_ulawto8, 0, ulaw_to_u8_tbl); - -static struct pcm_feederdesc feeder_alawto8_desc[] = { - {FEEDER_FMT, AFMT_A_LAW, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_alawto8_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_8), - {0, 0} -}; -FEEDER_DECLARE(feeder_alawto8, 0, alaw_to_u8_tbl); - -static struct pcm_feederdesc feeder_ulawto16_desc[] = { - {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_ulawto16_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_16), - {0, 0} -}; -FEEDER_DECLARE(feeder_ulawto16, 0, ulaw_to_u8_tbl); - -static struct pcm_feederdesc feeder_alawto16_desc[] = { - {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_alawto16_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_16), - {0, 0} -}; -FEEDER_DECLARE(feeder_alawto16, 0, alaw_to_u8_tbl); - -static struct pcm_feederdesc feeder_8toulaw_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_MU_LAW, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_MU_LAW, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_MU_LAW | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8toulaw_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_xlaw), - {0, 0} -}; -FEEDER_DECLARE(feeder_8toulaw, 0, u8_to_ulaw_tbl); - -static struct pcm_feederdesc feeder_8toalaw_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_A_LAW, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_A_LAW, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8toalaw_methods[] = { - KOBJMETHOD(feeder_feed, feed_table_xlaw), - {0, 0} -}; -FEEDER_DECLARE(feeder_8toalaw, 0, u8_to_alaw_tbl); - -/* - * All conversion done in byte level to preserve endianess. - */ - -#define FEEDFMT_SWAP_SIGN(f) (((((f)->desc->in & AFMT_SIGNED) == 0) != \ - (((f)->desc->out & AFMT_SIGNED) == 0)) \ - ? 0x80 : 0x00) - -/* - * Bit conversion - */ - -#define FBIT_DATA(i, o, c) ((intptr_t)((((c) & 0xf) << 6) | \ - (((i) & 0x7) << 3) | ((o) & 0x7))) -#define FBIT_OUTBPS(m) ((m) & 0x7) -#define FBIT_INBPS(m) FBIT_OUTBPS((m) >> 3) -#define FBIT_CHANNELS(m) (((m) >> 6) & 0xf) - -static int -feed_updownbit_init(struct pcm_feeder *f) -{ - int ibps, obps, channels; - - if (f->desc->in == f->desc->out || (f->desc->in & AFMT_STEREO) != - (f->desc->out & AFMT_STEREO)) - return (-1); - - channels = (f->desc->in & AFMT_STEREO) ? 2 : 1; - - if (f->desc->in & AFMT_32BIT) - ibps = PCM_32_BPS; - else if (f->desc->in & AFMT_24BIT) - ibps = PCM_24_BPS; - else if (f->desc->in & AFMT_16BIT) - ibps = PCM_16_BPS; - else - ibps = PCM_8_BPS; - - if (f->desc->out & AFMT_32BIT) - obps = PCM_32_BPS; - else if (f->desc->out & AFMT_24BIT) - obps = PCM_24_BPS; - else if (f->desc->out & AFMT_16BIT) - obps = PCM_16_BPS; - else - obps = PCM_8_BPS; - - f->data = (void *)FBIT_DATA(ibps, obps, channels); - - return (0); -} - -static int -feed_upbit(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, k, sign, ibps, ialign, obps, oalign, pad; - uint8_t *src, *dst; - - ibps = FBIT_INBPS((intptr_t)f->data); - obps = FBIT_OUTBPS((intptr_t)f->data); - - ialign = ibps * FBIT_CHANNELS((intptr_t)f->data); - oalign = obps * FBIT_CHANNELS((intptr_t)f->data); - - if (count < oalign) - return (0); - - k = FEEDER_FEED(f->source, c, b, (count / oalign) * ialign, source); - if (k < ialign) - return (0); - - k -= k % ialign; - j = (k / ibps) * obps; - pad = obps - ibps; - src = b + k; - dst = b + j; - sign = FEEDFMT_SWAP_SIGN(f); - - if (f->desc->out & AFMT_BIGENDIAN) { - do { - i = pad; - do { - *--dst = 0; - } while (--i != 0); - i = ibps; - while (--i != 0) - *--dst = *--src; - *--dst = *--src ^ sign; - } while (dst != b); - } else { - do { - *--dst = *--src ^ sign; - i = ibps; - while (--i != 0) - *--dst = *--src; - i = pad; - do { - *--dst = 0; - } while (--i != 0); - } while (dst != b); - } - - return (j); -} - -static struct pcm_feederdesc feeder_8to16_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8to16_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_8to16, 0, NULL); - -static struct pcm_feederdesc feeder_8to24_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8to24_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_8to24, 0, NULL); - -static struct pcm_feederdesc feeder_8to32_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U8, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_8to32_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_8to32, 0, NULL); - -static struct pcm_feederdesc feeder_16to24_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_16to24_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_16to24, 0, NULL); - -static struct pcm_feederdesc feeder_16to32_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_16to32_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_16to32, 0, NULL); - -static struct pcm_feederdesc feeder_24to32_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_24to32_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_upbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_24to32, 0, NULL); - -static int -feed_downbit(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, k, sign, be, ibps, ialign, obps, oalign,dump; - uint8_t *src, *dst, *end; - uint8_t reservoir[FEEDFMT_RESERVOIR]; - - ibps = FBIT_INBPS((intptr_t)f->data); - obps = FBIT_OUTBPS((intptr_t)f->data); - - ialign = ibps * FBIT_CHANNELS((intptr_t)f->data); - oalign = obps * FBIT_CHANNELS((intptr_t)f->data); - - if (count < oalign) - return (0); - - dst = b; - dump = ibps - obps; - sign = FEEDFMT_SWAP_SIGN(f); - be = (f->desc->in & AFMT_BIGENDIAN) ? 1 : 0; - k = count - (count % oalign); - - do { - if (k < oalign) - break; - - if (k < ialign) { - src = reservoir; - j = ialign; - } else { - src = dst; - j = k; - } - - j = FEEDER_FEED(f->source, c, src, j - (j % ialign), source); - if (j < ialign) - break; - - j -= j % ialign; - j *= obps; - j /= ibps; - end = dst + j; - - if (be != 0) { - do { - *dst++ = *src++ ^ sign; - i = obps; - while (--i != 0) - *dst++ = *src++; - src += dump; - } while (dst != end); - } else { - do { - src += dump; - i = obps; - while (--i != 0) - *dst++ = *src++; - *dst++ = *src++ ^ sign; - } while (dst != end); - } - - k -= j; - } while (k != 0); - - return (dst - b); -} - -static struct pcm_feederdesc feeder_16to8_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_16to8_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_16to8, 0, NULL); - -static struct pcm_feederdesc feeder_24to8_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_24to8_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_24to8, 0, NULL); - -static struct pcm_feederdesc feeder_24to16_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_24to16_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_24to16, 0, NULL); - -static struct pcm_feederdesc feeder_32to8_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_LE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_32to8_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_32to8, 0, NULL); - -static struct pcm_feederdesc feeder_32to16_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_32to16_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_32to16, 0, NULL); - -static struct pcm_feederdesc feeder_32to24_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_32to24_methods[] = { - KOBJMETHOD(feeder_init, feed_updownbit_init), - KOBJMETHOD(feeder_feed, feed_downbit), - {0, 0} -}; -FEEDER_DECLARE(feeder_32to24, 0, NULL); -/* - * Bit conversion end - */ - -/* - * Channel conversion (mono -> stereo) - */ -static int -feed_monotostereo(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int bps, i, j, k, l; - uint8_t v; - - bps = (int)((intptr_t)f->data); - if (count < (bps << 1)) - return (0); - - j = FEEDER_FEED(f->source, c, b, (count - (count % bps)) >> 1, source); - if (j < bps) - return (0); - - j -= j % bps; - i = j << 1; - l = i; - - do { - k = bps; - do { - v = b[--j]; - b[--i] = v; - b[i - bps] = v; - } while (--k != 0); - i -= bps; - } while (i != 0); - - return (l); -} - -static struct pcm_feederdesc feeder_monotostereo8_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_MU_LAW, AFMT_MU_LAW | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_A_LAW, AFMT_A_LAW | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_monotostereo8_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo), - {0, 0} -}; -FEEDER_DECLARE(feeder_monotostereo8, 0, (void *)PCM_8_BPS); - -static struct pcm_feederdesc feeder_monotostereo16_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_monotostereo16_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo), - {0, 0} -}; -FEEDER_DECLARE(feeder_monotostereo16, 0, (void *)PCM_16_BPS); - -static struct pcm_feederdesc feeder_monotostereo24_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_monotostereo24_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo), - {0, 0} -}; -FEEDER_DECLARE(feeder_monotostereo24, 0, (void *)PCM_24_BPS); - -static struct pcm_feederdesc feeder_monotostereo32_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_monotostereo32_methods[] = { - KOBJMETHOD(feeder_feed, feed_monotostereo), - {0, 0} -}; -FEEDER_DECLARE(feeder_monotostereo32, 0, (void *)PCM_32_BPS); -/* - * Channel conversion (mono -> stereo) end - */ - -/* - * Channel conversion (stereo -> mono) - */ -#define FEEDER_FMT_STEREODOWNMIX(FMTBIT, FMT_INTCAST, SIGN, \ - SIGNS, ENDIAN, ENDIANS) \ -static void \ -SIGNS##FMTBIT##ENDIANS##_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) \ -{ \ - int32_t v; \ - \ - v = ((FMT_INTCAST)PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx) + \ - PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy)) >> 1; \ - PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, v); \ -} - -FEEDER_FMT_STEREODOWNMIX(8, int32_t, S, s, NE, ne); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, S, s, LE, le); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, S, s, LE, le); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, S, s, LE, le); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, S, s, BE, be); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, S, s, BE, be); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, S, s, BE, be); -FEEDER_FMT_STEREODOWNMIX(8, int32_t, U, u, NE, ne); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, U, u, LE, le); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, U, u, LE, le); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, U, u, LE, le); -FEEDER_FMT_STEREODOWNMIX(16, int32_t, U, u, BE, be); -FEEDER_FMT_STEREODOWNMIX(24, int32_t, U, u, BE, be); -FEEDER_FMT_STEREODOWNMIX(32, intpcm_t, U, u, BE, be); - -static void -ulaw_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) -{ - uint8_t v; - - v = ((uint32_t)ulaw_to_u8_tbl[*sx] + ulaw_to_u8_tbl[*sy]) >> 1; - *dst = u8_to_ulaw_tbl[v]; -} - -static void -alaw_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) -{ - uint8_t v; - - v = ((uint32_t)alaw_to_u8_tbl[*sx] + alaw_to_u8_tbl[*sy]) >> 1; - *dst = u8_to_alaw_tbl[v]; -} - -typedef void (*feed_fmt_stereodownmix_filter)(uint8_t *, - uint8_t *, uint8_t *); - -struct feed_fmt_stereodownmix_info { - uint32_t format; - int bps; - feed_fmt_stereodownmix_filter func[2]; -}; - -static struct feed_fmt_stereodownmix_info feed_fmt_stereodownmix_tbl[] = { - { AFMT_S8, PCM_8_BPS, { NULL, s8ne_stereodownmix }}, - { AFMT_S16_LE, PCM_16_BPS, { NULL, s16le_stereodownmix }}, - { AFMT_S16_BE, PCM_16_BPS, { NULL, s16be_stereodownmix }}, - { AFMT_S24_LE, PCM_24_BPS, { NULL, s24le_stereodownmix }}, - { AFMT_S24_BE, PCM_24_BPS, { NULL, s24be_stereodownmix }}, - { AFMT_S32_LE, PCM_32_BPS, { NULL, s32le_stereodownmix }}, - { AFMT_S32_BE, PCM_32_BPS, { NULL, s32be_stereodownmix }}, - { AFMT_U8, PCM_8_BPS, { NULL, u8ne_stereodownmix }}, - { AFMT_A_LAW, PCM_8_BPS, { NULL, alaw_stereodownmix }}, - { AFMT_MU_LAW, PCM_8_BPS, { NULL, ulaw_stereodownmix }}, - { AFMT_U16_LE, PCM_16_BPS, { NULL, u16le_stereodownmix }}, - { AFMT_U16_BE, PCM_16_BPS, { NULL, u16be_stereodownmix }}, - { AFMT_U24_LE, PCM_24_BPS, { NULL, u24le_stereodownmix }}, - { AFMT_U24_BE, PCM_24_BPS, { NULL, u24be_stereodownmix }}, - { AFMT_U32_LE, PCM_32_BPS, { NULL, u32le_stereodownmix }}, - { AFMT_U32_BE, PCM_32_BPS, { NULL, u32be_stereodownmix }}, -}; - -#define FSM_DATA(i, j) ((intptr_t)((((i) & 0x1f) << 1) | ((j) & 0x1))) -#define FSM_INFOIDX(m) (((m) >> 1) & 0x1f) -#define FSM_FUNCIDX(m) ((m) & 0x1) - -static int -feed_stereotomono_init(struct pcm_feeder *f) -{ - int i, funcidx; - - if (!(f->desc->in & AFMT_STEREO) || (f->desc->out & AFMT_STEREO)) - return (-1); - - funcidx = (feeder_fmt_stereodownmix != 0) ? 1 : 0; - - for (i = 0; i < sizeof(feed_fmt_stereodownmix_tbl) / - sizeof(feed_fmt_stereodownmix_tbl[0]); i++) { - if (f->desc->out == feed_fmt_stereodownmix_tbl[i].format) { - f->data = (void *)FSM_DATA(i, funcidx); - return (0); - } - } - - return (-1); -} - -static int -feed_stereotomono(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - struct feed_fmt_stereodownmix_info *info; - feed_fmt_stereodownmix_filter stereodownmix; - int i, j, k, ibps, obps; - uint8_t *src, *dst, *end; - uint8_t reservoir[FEEDFMT_RESERVOIR]; - - info = &feed_fmt_stereodownmix_tbl[FSM_INFOIDX((intptr_t)f->data)]; - obps = info->bps; - - if (count < obps) - return (0); - - stereodownmix = info->func[FSM_FUNCIDX((intptr_t)f->data)]; - ibps = obps << 1; - dst = b; - k = count - (count % obps); - - do { - if (k < obps) - break; - - if (k < ibps) { - src = reservoir; - j = ibps; - } else { - src = dst; - j = k; - } - - j = FEEDER_FEED(f->source, c, src, j - (j % ibps), source); - if (j < ibps) - break; - - j -= j % ibps; - j >>= 1; - end = dst + j; - - if (stereodownmix != NULL) { - do { - stereodownmix(dst, src, src + obps); - dst += obps; - src += ibps; - } while (dst != end); - } else { - do { - i = obps; - do { - *dst++ = *src++; - } while (--i != 0); - src += obps; - } while (dst != end); - } - - k -= j; - } while (k != 0); - - return (dst - b); -} - -static struct pcm_feederdesc feeder_stereotomono8_desc[] = { - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_MU_LAW | AFMT_STEREO, AFMT_MU_LAW, 0}, - {FEEDER_FMT, AFMT_A_LAW | AFMT_STEREO, AFMT_A_LAW, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_stereotomono8_methods[] = { - KOBJMETHOD(feeder_init, feed_stereotomono_init), - KOBJMETHOD(feeder_feed, feed_stereotomono), - {0, 0} -}; -FEEDER_DECLARE(feeder_stereotomono8, 0, NULL); - -static struct pcm_feederdesc feeder_stereotomono16_desc[] = { - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_stereotomono16_methods[] = { - KOBJMETHOD(feeder_init, feed_stereotomono_init), - KOBJMETHOD(feeder_feed, feed_stereotomono), - {0, 0} -}; -FEEDER_DECLARE(feeder_stereotomono16, 0, NULL); - -static struct pcm_feederdesc feeder_stereotomono24_desc[] = { - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_stereotomono24_methods[] = { - KOBJMETHOD(feeder_init, feed_stereotomono_init), - KOBJMETHOD(feeder_feed, feed_stereotomono), - {0, 0} -}; -FEEDER_DECLARE(feeder_stereotomono24, 0, NULL); - -static struct pcm_feederdesc feeder_stereotomono32_desc[] = { - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_stereotomono32_methods[] = { - KOBJMETHOD(feeder_init, feed_stereotomono_init), - KOBJMETHOD(feeder_feed, feed_stereotomono), - {0, 0} -}; -FEEDER_DECLARE(feeder_stereotomono32, 0, NULL); -/* - * Channel conversion (stereo -> mono) end - */ - -/* - * Sign conversion - */ -static int -feed_sign(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, bps, ofs; - - bps = (int)((intptr_t)f->data); - if (count < bps) - return (0); - - i = FEEDER_FEED(f->source, c, b, count - (count % bps), source); - if (i < bps) - return (0); - - i -= i % bps; - j = i; - ofs = (f->desc->in & AFMT_BIGENDIAN) ? bps : 1; - - do { - b[i - ofs] ^= 0x80; - i -= bps; - } while (i != 0); - - return (j); -} -static struct pcm_feederdesc feeder_sign8_desc[] = { - {FEEDER_FMT, AFMT_U8, AFMT_S8, 0}, - {FEEDER_FMT, AFMT_U8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S8, AFMT_U8, 0}, - {FEEDER_FMT, AFMT_S8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_sign8_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - {0, 0} -}; -FEEDER_DECLARE(feeder_sign8, 0, (void *)PCM_8_BPS); - -static struct pcm_feederdesc feeder_sign16_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_sign16_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - {0, 0} -}; -FEEDER_DECLARE(feeder_sign16, 0, (void *)PCM_16_BPS); - -static struct pcm_feederdesc feeder_sign24_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_sign24_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - {0, 0} -}; -FEEDER_DECLARE(feeder_sign24, 0, (void *)PCM_24_BPS); - -static struct pcm_feederdesc feeder_sign32_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_sign32_methods[] = { - KOBJMETHOD(feeder_feed, feed_sign), - {0, 0} -}; -FEEDER_DECLARE(feeder_sign32, 0, (void *)PCM_32_BPS); - -/* - * Endian conversion - */ -static int -feed_endian(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, k, bps; - uint8_t *buf, v; - - bps = (int)((intptr_t)f->data); - if (count < bps) - return (0); - - k = FEEDER_FEED(f->source, c, b, count - (count % bps), source); - if (k < bps) - return (0); - - k -= k % bps; - j = bps >> 1; - buf = b + k; - - do { - buf -= bps; - i = j; - do { - v = buf[--i]; - buf[i] = buf[bps - i - 1]; - buf[bps - i - 1] = v; - } while (i != 0); - } while (buf != b); - - return (k); -} -static struct pcm_feederdesc feeder_endian16_desc[] = { - {FEEDER_FMT, AFMT_U16_LE, AFMT_U16_BE, 0}, - {FEEDER_FMT, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_LE, AFMT_S16_BE, 0}, - {FEEDER_FMT, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U16_BE, AFMT_U16_LE, 0}, - {FEEDER_FMT, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S16_BE, AFMT_S16_LE, 0}, - {FEEDER_FMT, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_endian16_methods[] = { - KOBJMETHOD(feeder_feed, feed_endian), - {0, 0} -}; -FEEDER_DECLARE(feeder_endian16, 0, (void *)PCM_16_BPS); - -static struct pcm_feederdesc feeder_endian24_desc[] = { - {FEEDER_FMT, AFMT_U24_LE, AFMT_U24_BE, 0}, - {FEEDER_FMT, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_LE, AFMT_S24_BE, 0}, - {FEEDER_FMT, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U24_BE, AFMT_U24_LE, 0}, - {FEEDER_FMT, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S24_BE, AFMT_S24_LE, 0}, - {FEEDER_FMT, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_endian24_methods[] = { - KOBJMETHOD(feeder_feed, feed_endian), - {0, 0} -}; -FEEDER_DECLARE(feeder_endian24, 0, (void *)PCM_24_BPS); - -static struct pcm_feederdesc feeder_endian32_desc[] = { - {FEEDER_FMT, AFMT_U32_LE, AFMT_U32_BE, 0}, - {FEEDER_FMT, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_LE, AFMT_S32_BE, 0}, - {FEEDER_FMT, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_U32_BE, AFMT_U32_LE, 0}, - {FEEDER_FMT, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_FMT, AFMT_S32_BE, AFMT_S32_LE, 0}, - {FEEDER_FMT, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_endian32_methods[] = { - KOBJMETHOD(feeder_feed, feed_endian), - {0, 0} -}; -FEEDER_DECLARE(feeder_endian32, 0, (void *)PCM_32_BPS); -/* - * Endian conversion end - */ - -/* - * L/R swap conversion - */ -static int -feed_swaplr(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - int i, j, bps, smpsz; - uint8_t *buf, v; - - bps = (int)((intptr_t)f->data); - smpsz = bps << 1; - if (count < smpsz) - return (0); - - j = FEEDER_FEED(f->source, c, b, count - (count % smpsz), source); - if (j < smpsz) - return (0); - - j -= j % smpsz; - buf = b + j; - - do { - buf -= smpsz; - i = bps; - do { - v = buf[--i]; - buf[i] = buf[bps + i]; - buf[bps + i] = v; - } while (i != 0); - } while (buf != b); - - return (j); -} - -static struct pcm_feederdesc feeder_swaplr8_desc[] = { - {FEEDER_SWAPLR, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_A_LAW | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_MU_LAW | AFMT_STEREO, AFMT_A_LAW | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_swaplr8_methods[] = { - KOBJMETHOD(feeder_feed, feed_swaplr), - {0, 0} -}; -FEEDER_DECLARE(feeder_swaplr8, -1, (void *)PCM_8_BPS); - -static struct pcm_feederdesc feeder_swaplr16_desc[] = { - {FEEDER_SWAPLR, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_swaplr16_methods[] = { - KOBJMETHOD(feeder_feed, feed_swaplr), - {0, 0} -}; -FEEDER_DECLARE(feeder_swaplr16, -1, (void *)PCM_16_BPS); - -static struct pcm_feederdesc feeder_swaplr24_desc[] = { - {FEEDER_SWAPLR, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_swaplr24_methods[] = { - KOBJMETHOD(feeder_feed, feed_swaplr), - {0, 0} -}; -FEEDER_DECLARE(feeder_swaplr24, -1, (void *)PCM_24_BPS); - -static struct pcm_feederdesc feeder_swaplr32_desc[] = { - {FEEDER_SWAPLR, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_SWAPLR, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_swaplr32_methods[] = { - KOBJMETHOD(feeder_feed, feed_swaplr), - {0, 0} -}; -FEEDER_DECLARE(feeder_swaplr32, -1, (void *)PCM_32_BPS); -/* - * L/R swap conversion end - */ --- sys/dev/sound/pcm/feeder_format.c.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/feeder_format.c 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,270 @@ +/*- + * Copyright (c) 2008-2009 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. + */ + +#ifdef _KERNEL +#include +#include +#include +#include +#include "feeder_if.h" + +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + +SND_DECLARE_FILE("$FreeBSD$"); +#endif + +#define FEEDFORMAT_RESERVOIR (SND_CHN_MAX * PCM_32_BPS) + +INTPCM_DECLARE(intpcm_conv_tables) + +struct feed_format_info { + uint32_t ibps, obps; + uint32_t ialign, oalign, channels; + intpcm_read_t *read; + intpcm_write_t *write; + uint8_t reservoir[FEEDFORMAT_RESERVOIR]; +}; + +#define FEEDFORMAT_ENTRY(SIGN, BIT, ENDIAN) \ + { \ + AFMT_##SIGN##BIT##_##ENDIAN, \ + intpcm_read_##SIGN##BIT##ENDIAN, \ + intpcm_write_##SIGN##BIT##ENDIAN \ + } + +static const struct { + uint32_t format; + intpcm_read_t *read; + intpcm_write_t *write; +} feed_format_ops[] = { + FEEDFORMAT_ENTRY(S, 8, NE), + FEEDFORMAT_ENTRY(S, 16, LE), + FEEDFORMAT_ENTRY(S, 24, LE), + FEEDFORMAT_ENTRY(S, 32, LE), + FEEDFORMAT_ENTRY(S, 16, BE), + FEEDFORMAT_ENTRY(S, 24, BE), + FEEDFORMAT_ENTRY(S, 32, BE), + FEEDFORMAT_ENTRY(U, 8, NE), + FEEDFORMAT_ENTRY(U, 16, LE), + FEEDFORMAT_ENTRY(U, 24, LE), + FEEDFORMAT_ENTRY(U, 32, LE), + FEEDFORMAT_ENTRY(U, 16, BE), + FEEDFORMAT_ENTRY(U, 24, BE), + FEEDFORMAT_ENTRY(U, 32, BE), + { + AFMT_MU_LAW, + intpcm_read_ulaw, intpcm_write_ulaw + }, + { + AFMT_A_LAW, + intpcm_read_alaw, intpcm_write_alaw + } +}; + +#define FEEDFORMAT_TAB_SIZE \ + ((int32_t)(sizeof(feed_format_ops) / sizeof(feed_format_ops[0]))) + +static int +feed_format_init(struct pcm_feeder *f) +{ + struct feed_format_info *info; + intpcm_read_t *rd_op; + intpcm_write_t *wr_op; + int i; + + if (f->desc->in == f->desc->out || + AFMT_CHANNEL(f->desc->in) != AFMT_CHANNEL(f->desc->out)) + return (EINVAL); + + rd_op = NULL; + wr_op = NULL; + + for (i = 0; i < FEEDFORMAT_TAB_SIZE && + (rd_op == NULL || wr_op == NULL); i++) { + if (rd_op == NULL && + AFMT_ENCODING(f->desc->in) == feed_format_ops[i].format) + rd_op = feed_format_ops[i].read; + if (wr_op == NULL && + AFMT_ENCODING(f->desc->out) == feed_format_ops[i].format) + wr_op = feed_format_ops[i].write; + } + + if (rd_op == NULL || wr_op == NULL) { + printf("%s(): failed to initialize io ops " + "in=0x%08x out=0x%08x\n", + __func__, f->desc->in, f->desc->out); + return (EINVAL); + } + + info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO); + if (info == NULL) + return (ENOMEM); + + info->channels = AFMT_CHANNEL(f->desc->in); + + info->ibps = AFMT_BPS(f->desc->in); + info->ialign = info->ibps * info->channels; + info->read = rd_op; + + info->obps = AFMT_BPS(f->desc->out); + info->oalign = info->obps * info->channels; + info->write = wr_op; + + f->data = info; + + return (0); +} + +static int +feed_format_free(struct pcm_feeder *f) +{ + struct feed_format_info *info; + + info = f->data; + if (info != NULL) + free(info, M_DEVBUF); + + f->data = NULL; + + return (0); +} + +static int +feed_format_set(struct pcm_feeder *f, int what, int value) +{ + struct feed_format_info *info; + + info = f->data; + + switch (what) { + case FEEDFORMAT_CHANNELS: + if (value < SND_CHN_MIN || value > SND_CHN_MAX) + return (EINVAL); + info->channels = (uint32_t)value; + info->ialign = info->ibps * info->channels; + info->oalign = info->obps * info->channels; + break; + default: + return (EINVAL); + break; + } + + return (0); +} + +static int +feed_format_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_format_info *info; + intpcm_t v; + uint32_t j; + uint8_t *src, *dst; + + info = f->data; + dst = b; + count = SND_FXROUND(count, info->oalign); + + do { + if (count < info->oalign) + break; + + if (count < info->ialign) { + src = info->reservoir; + j = info->ialign; + } else { + if (info->ialign == info->oalign) + j = count; + else if (info->ialign > info->oalign) + j = SND_FXROUND(count, info->ialign); + else + j = SND_FXDIV(count, info->oalign) * + info->ialign; + src = dst + count - j; + } + + j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source), + info->ialign); + if (j == 0) + break; + + j *= info->channels; + count -= j * info->obps; + + do { + v = info->read(src); + info->write(dst, v); + dst += info->obps; + src += info->ibps; + } while (--j != 0); + + } while (count != 0); + + return (dst - b); +} + +static struct pcm_feederdesc feeder_format_desc[] = { + { FEEDER_FORMAT, 0, 0, 0 }, + { 0, 0, 0, 0 } +}; + +static kobj_method_t feeder_format_methods[] = { + KOBJMETHOD(feeder_init, feed_format_init), + KOBJMETHOD(feeder_free, feed_format_free), + KOBJMETHOD(feeder_set, feed_format_set), + KOBJMETHOD(feeder_feed, feed_format_feed), + { 0, 0 } +}; + +FEEDER_DECLARE(feeder_format, 0, NULL); + +/* Extern */ +intpcm_read_t * +feeder_format_read_op(uint32_t format) +{ + int i; + + for (i = 0; i < FEEDFORMAT_TAB_SIZE; i++) { + if (AFMT_ENCODING(format) == feed_format_ops[i].format) + return (feed_format_ops[i].read); + } + + return (NULL); +} + +intpcm_write_t * +feeder_format_write_op(uint32_t format) +{ + int i; + + for (i = 0; i < FEEDFORMAT_TAB_SIZE; i++) { + if (AFMT_ENCODING(format) == feed_format_ops[i].format) + return (feed_format_ops[i].write); + } + + return (NULL); +} --- sys/dev/sound/pcm/feeder_matrix.c.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/feeder_matrix.c 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,738 @@ +/*- + * Copyright (c) 2008-2009 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. + */ + +#ifdef _KERNEL +#include +#include +#include "feeder_if.h" + +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + +SND_DECLARE_FILE("$FreeBSD$"); +#endif + +#define FEEDMATRIX_RESERVOIR (SND_CHN_MAX * PCM_32_BPS) + +#define SND_CHN_T_EOF 0x00e0fe0f +#define SND_CHN_T_NULL 0x0e0e0e0e + +struct feed_matrix_info; + +typedef void (*feed_matrix_t)(struct feed_matrix_info *, uint8_t *, + uint8_t *, uint32_t); + +struct feed_matrix_info { + uint32_t bps; + uint32_t ialign, oalign; + uint32_t in, out; + feed_matrix_t apply; +#ifdef FEEDMATRIX_GENERIC + intpcm_read_t *rd; + intpcm_write_t *wr; +#endif + struct { + int chn[SND_CHN_T_MAX + 1]; + int mul, shift; + } matrix[SND_CHN_T_MAX + 1]; + uint8_t reservoir[FEEDMATRIX_RESERVOIR]; +}; + +static struct pcmchan_matrix feeder_matrix_maps[SND_CHN_MATRIX_MAX] = { + [SND_CHN_MATRIX_1_0] = SND_CHN_MATRIX_MAP_1_0, + [SND_CHN_MATRIX_2_0] = SND_CHN_MATRIX_MAP_2_0, + [SND_CHN_MATRIX_2_1] = SND_CHN_MATRIX_MAP_2_1, + [SND_CHN_MATRIX_3_0] = SND_CHN_MATRIX_MAP_3_0, + [SND_CHN_MATRIX_4_0] = SND_CHN_MATRIX_MAP_4_0, + [SND_CHN_MATRIX_4_1] = SND_CHN_MATRIX_MAP_4_1, + [SND_CHN_MATRIX_5_0] = SND_CHN_MATRIX_MAP_5_0, + [SND_CHN_MATRIX_5_1] = SND_CHN_MATRIX_MAP_5_1, + [SND_CHN_MATRIX_6_0] = SND_CHN_MATRIX_MAP_6_0, + [SND_CHN_MATRIX_6_1] = SND_CHN_MATRIX_MAP_6_1, + [SND_CHN_MATRIX_7_1] = SND_CHN_MATRIX_MAP_7_1 +}; + +static int feeder_matrix_default_ids[9] = { + [0] = SND_CHN_MATRIX_UNKNOWN, + [1] = SND_CHN_MATRIX_1, + [2] = SND_CHN_MATRIX_2, + [3] = SND_CHN_MATRIX_3, + [4] = SND_CHN_MATRIX_4, + [5] = SND_CHN_MATRIX_5, + [6] = SND_CHN_MATRIX_6, + [7] = SND_CHN_MATRIX_7, + [8] = SND_CHN_MATRIX_8 +}; + +#ifdef _KERNEL +#define FEEDMATRIX_CLIP_CHECK(...) +#else +#define FEEDMATRIX_CLIP_CHECK(v, BIT) do { \ + if ((v) < PCM_S##BIT##_MIN || (v) > PCM_S##BIT##_MAX) \ + errx(1, "\n\n%s(): Sample clipping: %jd\n", \ + __func__, (intmax_t)(v)); \ +} while (0) +#endif + +#define FEEDMATRIX_DECLARE(SIGN, BIT, ENDIAN) \ +static void \ +feed_matrix_##SIGN##BIT##ENDIAN(struct feed_matrix_info *info, \ + uint8_t *src, uint8_t *dst, uint32_t count) \ +{ \ + int64_t accum; \ + intpcm_t v; \ + int i, j; \ + \ + do { \ + for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; \ + i++) { \ + if (info->matrix[i].chn[0] == SND_CHN_T_NULL) { \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \ + 0); \ + dst += PCM_##BIT##_BPS; \ + continue; \ + } else if (info->matrix[i].chn[1] == \ + SND_CHN_T_EOF) { \ + v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \ + src + info->matrix[i].chn[0]); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, \ + v); \ + dst += PCM_##BIT##_BPS; \ + continue; \ + } \ + \ + accum = 0; \ + for (j = 0; \ + info->matrix[i].chn[j] != SND_CHN_T_EOF; \ + j++) { \ + v = _PCM_READ_##SIGN##BIT##_##ENDIAN( \ + src + info->matrix[i].chn[j]); \ + accum += v; \ + } \ + \ + accum = (accum * info->matrix[i].mul) >> \ + info->matrix[i].shift; \ + \ + FEEDMATRIX_CLIP_CHECK(accum, BIT); \ + \ + v = (accum > PCM_S##BIT##_MAX) ? \ + PCM_S##BIT##_MAX : \ + ((accum < PCM_S##BIT##_MIN) ? \ + PCM_S##BIT##_MIN : \ + accum); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \ + dst += PCM_##BIT##_BPS; \ + } \ + src += info->ialign; \ + } while (--count != 0); \ +} + +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) +FEEDMATRIX_DECLARE(S, 16, LE) +FEEDMATRIX_DECLARE(S, 32, LE) +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) +FEEDMATRIX_DECLARE(S, 16, BE) +FEEDMATRIX_DECLARE(S, 32, BE) +#endif +#ifdef FEEDER_MULTIFORMAT +FEEDMATRIX_DECLARE(S, 8, NE) +FEEDMATRIX_DECLARE(S, 24, LE) +FEEDMATRIX_DECLARE(S, 24, BE) +FEEDMATRIX_DECLARE(U, 8, NE) +FEEDMATRIX_DECLARE(U, 16, LE) +FEEDMATRIX_DECLARE(U, 24, LE) +FEEDMATRIX_DECLARE(U, 32, LE) +FEEDMATRIX_DECLARE(U, 16, BE) +FEEDMATRIX_DECLARE(U, 24, BE) +FEEDMATRIX_DECLARE(U, 32, BE) +#endif + +#define FEEDMATRIX_ENTRY(SIGN, BIT, ENDIAN) \ + { \ + AFMT_##SIGN##BIT##_##ENDIAN, \ + feed_matrix_##SIGN##BIT##ENDIAN \ + } + +static const struct { + uint32_t format; + feed_matrix_t apply; +} feed_matrix_tab[] = { +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) + FEEDMATRIX_ENTRY(S, 16, LE), + FEEDMATRIX_ENTRY(S, 32, LE), +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) + FEEDMATRIX_ENTRY(S, 16, BE), + FEEDMATRIX_ENTRY(S, 32, BE), +#endif +#ifdef FEEDER_MULTIFORMAT + FEEDMATRIX_ENTRY(S, 8, NE), + FEEDMATRIX_ENTRY(S, 24, LE), + FEEDMATRIX_ENTRY(S, 24, BE), + FEEDMATRIX_ENTRY(U, 8, NE), + FEEDMATRIX_ENTRY(U, 16, LE), + FEEDMATRIX_ENTRY(U, 24, LE), + FEEDMATRIX_ENTRY(U, 32, LE), + FEEDMATRIX_ENTRY(U, 16, BE), + FEEDMATRIX_ENTRY(U, 24, BE), + FEEDMATRIX_ENTRY(U, 32, BE) +#endif +}; + +static void +feed_matrix_reset(struct feed_matrix_info *info) +{ + uint32_t i, j; + + for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) { + for (j = 0; + j < (sizeof(info->matrix[i].chn) / + sizeof(info->matrix[i].chn[0])); j++) { + info->matrix[i].chn[j] = SND_CHN_T_EOF; + } + info->matrix[i].mul = 1; + info->matrix[i].shift = 0; + } +} + +#ifdef FEEDMATRIX_GENERIC +static void +feed_matrix_apply_generic(struct feed_matrix_info *info, + uint8_t *src, uint8_t *dst, uint32_t count) +{ + int64_t accum; + intpcm_t v; + int i, j; + + do { + for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; + i++) { + if (info->matrix[i].chn[0] == SND_CHN_T_NULL) { + info->wr(dst, 0); + dst += info->bps; + continue; + } else if (info->matrix[i].chn[1] == + SND_CHN_T_EOF) { + v = info->rd(src + info->matrix[i].chn[0]); + info->wr(dst, v); + dst += info->bps; + continue; + } + + accum = 0; + for (j = 0; + info->matrix[i].chn[j] != SND_CHN_T_EOF; + j++) { + v = info->rd(src + info->matrix[i].chn[j]); + accum += v; + } + + accum = (accum * info->matrix[i].mul) >> + info->matrix[i].shift; + + FEEDMATRIX_CLIP_CHECK(accum, 32); + + v = (accum > PCM_S32_MAX) ? PCM_S32_MAX : + ((accum < PCM_S32_MIN) ? PCM_S32_MIN : accum); + info->wr(dst, v); + dst += info->bps; + } + src += info->ialign; + } while (--count != 0); +} +#endif + +static int +feed_matrix_setup(struct feed_matrix_info *info, struct pcmchan_matrix *m_in, + struct pcmchan_matrix *m_out) +{ + uint32_t i, j, ch, in_mask, merge_mask; + int mul, shift; + + + if (info == NULL || m_in == NULL || m_out == NULL || + AFMT_CHANNEL(info->in) != m_in->channels || + AFMT_CHANNEL(info->out) != m_out->channels || + m_in->channels < SND_CHN_MIN || m_in->channels > SND_CHN_MAX || + m_out->channels < SND_CHN_MIN || m_out->channels > SND_CHN_MAX) + return (EINVAL); + + feed_matrix_reset(info); + + if (m_in->id == m_out->id && !(m_in->id < SND_CHN_MATRIX_BEGIN || + m_in->id > SND_CHN_MATRIX_END)) + return (0); + + if (m_in->id == SND_CHN_MATRIX_1_0) { + if (m_out->id == SND_CHN_MATRIX_1_0) + in_mask = SND_CHN_T_MASK_FL; + else if (m_out->mask & SND_CHN_T_MASK_FC) + in_mask = SND_CHN_T_MASK_FC; + else + in_mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR; + } else + in_mask = m_in->mask; + + for (ch = SND_CHN_T_BEGIN; ch <= SND_CHN_T_END && + m_out->map[ch].type != SND_CHN_T_MAX; ch += SND_CHN_T_STEP) { + merge_mask = m_out->map[ch].members & in_mask; + if (merge_mask == 0) { + info->matrix[ch].chn[0] = SND_CHN_T_NULL; + continue; + } + + j = 0; + for (i = SND_CHN_T_BEGIN; i <= SND_CHN_T_END; + i += SND_CHN_T_STEP) { + if (merge_mask & (1 << i)) { + if (m_in->offset[i] >= 0 && + m_in->offset[i] < (int)m_in->channels) + info->matrix[ch].chn[j++] = + m_in->offset[i] * info->bps; + else { + info->matrix[ch].chn[j++] = + SND_CHN_T_EOF; + break; + } + } + } + +#define FEEDMATRIX_ATTN_SHIFT 16 + + if (j > 1) { + mul = (1 << (FEEDMATRIX_ATTN_SHIFT - 1)) + 143 - j; + shift = FEEDMATRIX_ATTN_SHIFT; + while ((mul & 1) == 0 && shift > 0) { + mul >>= 1; + shift--; + } + info->matrix[ch].mul = mul; + info->matrix[ch].shift = shift; + } + } + +#ifndef _KERNEL + fprintf(stderr, "Total: %d\n", ch); + + for (i = 0; info->matrix[i].chn[0] != SND_CHN_T_EOF; i++) { + fprintf(stderr, "%d: [", i); + for (j = 0; info->matrix[i].chn[j] != SND_CHN_T_EOF; j++) { + if (j != 0) + fprintf(stderr, ", "); + fprintf(stderr, "%d", + (info->matrix[i].chn[j] == SND_CHN_T_NULL) ? + 0xffffffff : info->matrix[i].chn[j] / info->bps); + } + fprintf(stderr, "] attn: (x * %d) >> %d\n", + info->matrix[i].mul, info->matrix[i].shift); + } +#endif + + return (0); +} + +static int +feed_matrix_init(struct pcm_feeder *f) +{ + struct feed_matrix_info *info; + struct pcmchan_matrix *m_in, *m_out; + uint32_t i; + int ret; + + if (AFMT_ENCODING(f->desc->in) != AFMT_ENCODING(f->desc->out)) + return (EINVAL); + + info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO); + if (info == NULL) + return (ENOMEM); + + info->in = f->desc->in; + info->out = f->desc->out; + info->bps = AFMT_BPS(info->in); + info->ialign = AFMT_ALIGN(info->in); + info->oalign = AFMT_ALIGN(info->out); + info->apply = NULL; + + for (i = 0; info->apply == NULL && + i < (sizeof(feed_matrix_tab) / sizeof(feed_matrix_tab[0])); i++) { + if (AFMT_ENCODING(info->in) == feed_matrix_tab[i].format) + info->apply = feed_matrix_tab[i].apply; + } + + if (info->apply == NULL) { +#ifdef FEEDMATRIX_GENERIC + info->rd = feeder_format_read_op(info->in); + info->wr = feeder_format_write_op(info->out); + if (info->rd == NULL || info->wr == NULL) { + free(info, M_DEVBUF); + return (EINVAL); + } + info->apply = feed_matrix_apply_generic; +#else + free(info, M_DEVBUF); + return (EINVAL); +#endif + } + + m_in = feeder_matrix_format_map(info->in); + m_out = feeder_matrix_format_map(info->out); + + ret = feed_matrix_setup(info, m_in, m_out); + if (ret != 0) { + free(info, M_DEVBUF); + return (ret); + } + + f->data = info; + + return (0); +} + +static int +feed_matrix_free(struct pcm_feeder *f) +{ + struct feed_matrix_info *info; + + info = f->data; + if (info != NULL) + free(info, M_DEVBUF); + + f->data = NULL; + + return (0); +} + +static int +feed_matrix_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_matrix_info *info; + uint32_t j, inmax; + uint8_t *src, *dst; + + info = f->data; + if (info->matrix[0].chn[0] == SND_CHN_T_EOF) + return (FEEDER_FEED(f->source, c, b, count, source)); + + dst = b; + count = SND_FXROUND(count, info->oalign); + inmax = info->ialign + info->oalign; + + do { + if (count < info->oalign) + break; + + if (count < inmax) { + src = info->reservoir; + j = info->ialign; + } else { + if (info->ialign == info->oalign) + j = count - info->oalign; + else if (info->ialign > info->oalign) + j = SND_FXROUND(count - info->oalign, + info->ialign); + else + j = (SND_FXDIV(count, info->oalign) - 1) * + info->ialign; + src = dst + count - j; + } + + j = SND_FXDIV(FEEDER_FEED(f->source, c, src, j, source), + info->ialign); + if (j == 0) + break; + + info->apply(info, src, dst, j); + + j *= info->oalign; + dst += j; + count -= j; + + } while (count != 0); + + return (dst - b); +} + +static struct pcm_feederdesc feeder_matrix_desc[] = { + { FEEDER_MATRIX, 0, 0, 0 }, + { 0, 0, 0, 0 } +}; + +static kobj_method_t feeder_matrix_methods[] = { + KOBJMETHOD(feeder_init, feed_matrix_init), + KOBJMETHOD(feeder_free, feed_matrix_free), + KOBJMETHOD(feeder_feed, feed_matrix_feed), + { 0, 0 } +}; + +FEEDER_DECLARE(feeder_matrix, 0, NULL); + +/* External */ +int +feeder_matrix_setup(struct pcm_feeder *f, struct pcmchan_matrix *m_in, + struct pcmchan_matrix *m_out) +{ + + if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_MATRIX || + f->data == NULL) + return (EINVAL); + + return (feed_matrix_setup(f->data, m_in, m_out)); +} + +int +feeder_matrix_default_id(uint32_t ch) +{ + + if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels || + ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels) + return (SND_CHN_MATRIX_UNKNOWN); + + return (feeder_matrix_maps[feeder_matrix_default_ids[ch]].id); +} + +struct pcmchan_matrix * +feeder_matrix_default_channel_map(uint32_t ch) +{ + if (ch < feeder_matrix_maps[SND_CHN_MATRIX_BEGIN].channels || + ch > feeder_matrix_maps[SND_CHN_MATRIX_END].channels) + return (NULL); + + return (&feeder_matrix_maps[feeder_matrix_default_ids[ch]]); +} + +uint32_t +feeder_matrix_default_format(uint32_t format) +{ + struct pcmchan_matrix *m; + uint32_t i, ch, ext; + + ch = AFMT_CHANNEL(format); + ext = AFMT_EXTCHANNEL(format); + + if (ext != 0) { + for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) { + if (feeder_matrix_maps[i].channels == ch && + feeder_matrix_maps[i].ext == ext) + return (SND_FORMAT(format, ch, ext)); + } + } + + m = feeder_matrix_default_channel_map(ch); + if (m == NULL) + return (0x00000000); + + return (SND_FORMAT(format, ch, m->ext)); +} + +int +feeder_matrix_format_id(uint32_t format) +{ + uint32_t i, ch, ext; + + ch = AFMT_CHANNEL(format); + ext = AFMT_EXTCHANNEL(format); + + for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) { + if (feeder_matrix_maps[i].channels == ch && + feeder_matrix_maps[i].ext == ext) + return (feeder_matrix_maps[i].id); + } + + return (SND_CHN_MATRIX_UNKNOWN); +} + +struct pcmchan_matrix * +feeder_matrix_format_map(uint32_t format) +{ + uint32_t i, ch, ext; + + ch = AFMT_CHANNEL(format); + ext = AFMT_EXTCHANNEL(format); + + for (i = SND_CHN_MATRIX_BEGIN; i <= SND_CHN_MATRIX_END; i++) { + if (feeder_matrix_maps[i].channels == ch && + feeder_matrix_maps[i].ext == ext) + return (&feeder_matrix_maps[i]); + } + + return (NULL); +} + +struct pcmchan_matrix * +feeder_matrix_id_map(int id) +{ + + if (id < SND_CHN_MATRIX_BEGIN || id > SND_CHN_MATRIX_END) + return (NULL); + + return (&feeder_matrix_maps[id]); +} + +int +feeder_matrix_compare(struct pcmchan_matrix *m_in, struct pcmchan_matrix *m_out) +{ + uint32_t i; + + if (m_in == m_out) + return (0); + + if (m_in->channels != m_out->channels || m_in->ext != m_out->ext || + m_in->mask != m_out->mask) + return (1); + + for (i = 0; i < (sizeof(m_in->map) / sizeof(m_in->map[0])); i++) { + if (m_in->map[i].type != m_out->map[i].type) + return (1); + if (m_in->map[i].type == SND_CHN_T_MAX) + break; + if (m_in->map[i].members != m_out->map[i].members) + return (1); + if (i <= SND_CHN_T_END) { + if (m_in->offset[m_in->map[i].type] != + m_out->offset[m_out->map[i].type]) + return (1); + } + } + + return (0); +} + +/* + * XXX 4front intepretation of "surround" is ambigous and sort of + * conflicting with "rear"/"back". Map it to "side". Well.. + * who cares? + */ +static int snd_chn_to_oss[SND_CHN_T_MAX] = { + [SND_CHN_T_FL] = CHID_L, + [SND_CHN_T_FR] = CHID_R, + [SND_CHN_T_FC] = CHID_C, + [SND_CHN_T_LF] = CHID_LFE, + [SND_CHN_T_SL] = CHID_LS, + [SND_CHN_T_SR] = CHID_RS, + [SND_CHN_T_BL] = CHID_LR, + [SND_CHN_T_BR] = CHID_RR +}; + +#define SND_CHN_OSS_VALIDMASK \ + (SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR) + +#define SND_CHN_OSS_MAX 8 +#define SND_CHN_OSS_BEGIN CHID_L +#define SND_CHN_OSS_END CHID_RR + +static int oss_to_snd_chn[SND_CHN_OSS_END + 1] = { + [CHID_L] = SND_CHN_T_FL, + [CHID_R] = SND_CHN_T_FR, + [CHID_C] = SND_CHN_T_FC, + [CHID_LFE] = SND_CHN_T_LF, + [CHID_LS] = SND_CHN_T_SL, + [CHID_RS] = SND_CHN_T_SR, + [CHID_LR] = SND_CHN_T_BL, + [CHID_RR] = SND_CHN_T_BR +}; + +int +feeder_matrix_oss_get_channel_order(struct pcmchan_matrix *m, + unsigned long long *map) +{ + uint32_t i; + + if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK)) + return (EINVAL); + + *map = 0x0000000000000000ULL; + + for (i = 0; m->map[i].type != SND_CHN_T_MAX; i++) + *map |= + (unsigned long long)snd_chn_to_oss[m->map[i].type] << + (i * 4); + + return (0); +} + +int +feeder_matrix_oss_set_channel_order(struct pcmchan_matrix *m, + unsigned long long *map) +{ + struct pcmchan_matrix tmp; + uint32_t i; + int ch, chmask, cheof; + + if (m == NULL || map == NULL || (m->mask & ~SND_CHN_OSS_VALIDMASK) || + (*map & 0xffffffff00000000ULL)) + return (EINVAL); + + tmp = *m; + tmp.channels = 0; + tmp.ext = 0; + tmp.mask = 0; + memset(tmp.offset, -1, sizeof(tmp.offset)); + cheof = 0; + + for (i = 0; i < SND_CHN_OSS_MAX; i++) { + ch = (*map >> (i * 4)) & 0xf; + if (ch < SND_CHN_OSS_BEGIN) { + if (cheof == 0 && m->map[i].type != SND_CHN_T_MAX) + return (EINVAL); + cheof++; + tmp.map[i] = m->map[i]; + continue; + } else if (ch > SND_CHN_OSS_END) + return (EINVAL); + else if (cheof != 0) + return (EINVAL); + ch = oss_to_snd_chn[ch]; + chmask = 1 << ch; + /* Requesting channel not exist in matrix */ + if (!(chmask & m->mask)) + return (EINVAL); + /* Channel being repeated */ + if (chmask & tmp.mask) + return (EINVAL); + tmp.map[i] = m->map[m->offset[ch]]; + if (tmp.map[i].type != ch) + return (EINVAL); + tmp.offset[ch] = i; + tmp.mask |= chmask; + tmp.channels++; + if (chmask & SND_CHN_T_MASK_LF) + tmp.ext++; + } + + if (tmp.channels != m->channels || tmp.ext != m->ext || + tmp.mask != m->mask || + tmp.map[m->channels].type != SND_CHN_T_MAX) + return (EINVAL); + + *m = tmp; + + return (0); +} --- sys/dev/sound/pcm/feeder_mixer.c.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/feeder_mixer.c 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,364 @@ +/*- + * Copyright (c) 2008-2009 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. + */ + +#ifdef _KERNEL +#include +#include +#include "feeder_if.h" + +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + +SND_DECLARE_FILE("$FreeBSD$"); +#endif + +#undef FEEDER_MULTIFORMAT +#define FEEDER_MULTIFORMAT 1 + +typedef void (*feed_mixer_t)(uint8_t *, uint8_t *, uint32_t); + +#define FEEDMIXER_DECLARE(SIGN, BIT, ENDIAN) \ +static void \ +feed_mixer_##SIGN##BIT##ENDIAN(uint8_t *src, uint8_t *dst, \ + uint32_t count) \ +{ \ + intpcm##BIT##_t z; \ + intpcm_t x, y; \ + \ + src += count; \ + dst += count; \ + \ + do { \ + src -= PCM_##BIT##_BPS; \ + dst -= PCM_##BIT##_BPS; \ + count -= PCM_##BIT##_BPS; \ + x = PCM_READ_##SIGN##BIT##_##ENDIAN(src); \ + y = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ + z = INTPCM##BIT##_T(x) + y; \ + x = PCM_CLAMP_##SIGN##BIT(z); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \ + } while (count != 0); \ +} + +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) +FEEDMIXER_DECLARE(S, 16, LE) +FEEDMIXER_DECLARE(S, 32, LE) +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) +FEEDMIXER_DECLARE(S, 16, BE) +FEEDMIXER_DECLARE(S, 32, BE) +#endif +#ifdef FEEDER_MULTIFORMAT +FEEDMIXER_DECLARE(S, 8, NE) +FEEDMIXER_DECLARE(S, 24, LE) +FEEDMIXER_DECLARE(S, 24, BE) +FEEDMIXER_DECLARE(U, 8, NE) +FEEDMIXER_DECLARE(U, 16, LE) +FEEDMIXER_DECLARE(U, 24, LE) +FEEDMIXER_DECLARE(U, 32, LE) +FEEDMIXER_DECLARE(U, 16, BE) +FEEDMIXER_DECLARE(U, 24, BE) +FEEDMIXER_DECLARE(U, 32, BE) +#endif + +struct feed_mixer_info { + uint32_t format; + int bps; + feed_mixer_t mix; +}; + +#define FEEDMIXER_ENTRY(SIGN, BIT, ENDIAN) \ + { \ + AFMT_##SIGN##BIT##_##ENDIAN, PCM_##BIT##_BPS, \ + feed_mixer_##SIGN##BIT##ENDIAN \ + } + +static struct feed_mixer_info feed_mixer_info_tab[] = { + FEEDMIXER_ENTRY(S, 8, NE), +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) + FEEDMIXER_ENTRY(S, 16, LE), + FEEDMIXER_ENTRY(S, 32, LE), +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) + FEEDMIXER_ENTRY(S, 16, BE), + FEEDMIXER_ENTRY(S, 32, BE), +#endif +#ifdef FEEDER_MULTIFORMAT + FEEDMIXER_ENTRY(S, 24, LE), + FEEDMIXER_ENTRY(S, 24, BE), + FEEDMIXER_ENTRY(U, 8, NE), + FEEDMIXER_ENTRY(U, 16, LE), + FEEDMIXER_ENTRY(U, 24, LE), + FEEDMIXER_ENTRY(U, 32, LE), + FEEDMIXER_ENTRY(U, 16, BE), + FEEDMIXER_ENTRY(U, 24, BE), + FEEDMIXER_ENTRY(U, 32, BE) +#endif +}; + +#define FEEDMIXER_TAB_SIZE ((int32_t) \ + (sizeof(feed_mixer_info_tab) / \ + sizeof(feed_mixer_info_tab[0]))) + +#define FEEDMIXER_DATA(i, c) ((void *) \ + ((uintptr_t)((((i) & 0x1f) << 5) | \ + ((c) & 0x1f)))) +#define FEEDMIXER_INFOIDX(d) ((uint32_t)((uintptr_t)(d) >> 5) & 0x1f) +#define FEEDMIXER_CHANNELS(d) ((uint32_t)((uintptr_t)(d)) & 0x1f) + +static int +feed_mixer_init(struct pcm_feeder *f) +{ + int i; + + if (f->desc->in != f->desc->out) + return (EINVAL); + + for (i = 0; i < FEEDMIXER_TAB_SIZE; i++) { + if (AFMT_ENCODING(f->desc->in) == + feed_mixer_info_tab[i].format) { + f->data = + FEEDMIXER_DATA(i, AFMT_CHANNEL(f->desc->in)); + return (0); + } + } + + return (EINVAL); +} + +static int +feed_mixer_set(struct pcm_feeder *f, int what, int value) +{ + + switch (what) { + case FEEDMIXER_CHANNELS: + if (value < SND_CHN_MIN || value > SND_CHN_MAX) + return (EINVAL); + f->data = FEEDMIXER_DATA(FEEDMIXER_INFOIDX(f->data), value); + break; + default: + return (EINVAL); + break; + } + + return (0); +} + +static __inline int +feed_mixer_rec(struct pcm_channel *c, int dirty) +{ + struct pcm_channel *ch; + struct snd_dbuf *b, *bs; + uint32_t cnt, maxfeed; + int rdy; + + /* + * Reset ready and moving pointer. We're not using bufsoft + * anywhere since its sole purpose is to become the primary + * distributor for the recorded buffer and also as an interrupt + * threshold progress indicator. + */ + b = c->bufsoft; + b->rp = 0; + b->rl = 0; + cnt = sndbuf_getsize(b); + maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getbps(b)); + + do { + cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf, + min(cnt, maxfeed), c->bufhard); + if (cnt != 0) { + sndbuf_acquire(b, b->tmpbuf, cnt); + cnt = sndbuf_getfree(b); + } + } while (cnt != 0); + + /* Not enough data */ + if (b->rl < sndbuf_getbps(b)) { + b->rl = 0; + return (0); + } + + /* + * Keep track of ready and moving pointer since we will use + * bufsoft over and over again, pretending nothing has happened. + */ + rdy = b->rl; + + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) { + CHN_UNLOCK(ch); + continue; + } + if (dirty != 0) { + sndbuf_setspd(ch->bufhard, c->speed); + if (feeder_chain(ch) != 0) { + ch->flags |= CHN_F_DIRTY; + CHN_UNLOCK(ch); + continue; + } + } + bs = ch->bufsoft; + if (ch->flags & CHN_F_MAPPED) + sndbuf_dispose(bs, NULL, sndbuf_getready(bs)); + cnt = sndbuf_getfree(bs); + if (cnt < sndbuf_getbps(bs)) { + CHN_UNLOCK(ch); + continue; + } + maxfeed = SND_FXROUND(SND_FXDIV_MAX, sndbuf_getbps(bs)); + do { + cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, + min(cnt, maxfeed), b); + if (cnt != 0) { + sndbuf_acquire(bs, bs->tmpbuf, cnt); + cnt = sndbuf_getfree(bs); + } + } while (cnt != 0); + /* + * Not entirely flushed out... + */ + if (b->rl != 0) + ch->xruns++; + CHN_UNLOCK(ch); + /* + * Rewind buffer position for next virtual channel. + */ + b->rp = 0; + b->rl = rdy; + } + + /* + * Set ready pointer to indicate that our children are ready + * to be woken up, also as an interrupt threshold progress + * indicator. + */ + b->rl = 1; + + /* + * Return 0 to bail out early from sndbuf_feed() loop. + * No need to increase feedcount counter since part of this + * feeder chains already include feed_root(). + */ + return (0); +} + +static int +feed_mixer_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_mixer_info *info; + struct snd_dbuf *src = source; + struct pcm_channel *ch; + uint32_t cnt, mcnt, rcnt, sz; + int dirty; + uint8_t *tmp; + + dirty = (c->flags & CHN_F_DIRTY) ? 1 : 0; + c->flags &= ~CHN_F_DIRTY; + + if (c->direction == PCMDIR_REC) + return (feed_mixer_rec(c, dirty)); + + sz = sndbuf_getsize(src); + if (sz < count) + count = sz; + + info = &feed_mixer_info_tab[FEEDMIXER_INFOIDX(f->data)]; + sz = info->bps * FEEDMIXER_CHANNELS(f->data); + count = SND_FXROUND(count, sz); + if (count < sz) + return (0); + + /* + * we are going to use our source as a temporary buffer since it's + * got no other purpose. we obtain our data by traversing the channel + * list of children and calling mixer function to mix count bytes from + * each into our destination buffer, b + */ + tmp = sndbuf_getbuf(src); + rcnt = 0; + mcnt = 0; + + CHN_FOREACH(ch, c, children.busy) { + CHN_LOCK(ch); + if (CHN_STOPPED(ch) || (ch->flags & CHN_F_DIRTY)) { + CHN_UNLOCK(ch); + continue; + } + if (dirty != 0) { + sndbuf_setspd(ch->bufhard, c->speed); + if (feeder_chain(ch) != 0) { + ch->flags |= CHN_F_DIRTY; + CHN_UNLOCK(ch); + continue; + } + } + if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING)) + sndbuf_acquire(ch->bufsoft, NULL, + sndbuf_getfree(ch->bufsoft)); + if (rcnt == 0) { + rcnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, b, + count, ch->bufsoft), sz); + mcnt = count - rcnt; + } else { + cnt = SND_FXROUND(FEEDER_FEED(ch->feeder, ch, tmp, + count, ch->bufsoft), sz); + if (cnt != 0) { + if (mcnt != 0) { + memset(b + rcnt, + sndbuf_zerodata(f->desc->out), + mcnt); + mcnt = 0; + } + info->mix(tmp, b, cnt); + if (cnt > rcnt) + rcnt = cnt; + } + } + CHN_UNLOCK(ch); + } + + if (++c->feedcount == 0) + c->feedcount = 2; + + return (rcnt); +} + +static struct pcm_feederdesc feeder_mixer_desc[] = { + { FEEDER_MIXER, 0, 0, 0 }, + { 0, 0, 0, 0 } +}; + +static kobj_method_t feeder_mixer_methods[] = { + KOBJMETHOD(feeder_init, feed_mixer_init), + KOBJMETHOD(feeder_set, feed_mixer_set), + KOBJMETHOD(feeder_feed, feed_mixer_feed), + { 0, 0 } +}; + +FEEDER_DECLARE(feeder_mixer, 0, NULL); --- sys/dev/sound/pcm/feeder_rate.c.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/feeder_rate.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,7 +1,5 @@ /*- - * Copyright (c) 1999 Cameron Grant - * Copyright (c) 2003 Orion Hodson - * Copyright (c) 2005 Ariff Abdullah + * Copyright (c) 2005-2009 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,102 +25,135 @@ */ /* - * 2006-02-21: - * ========== + * XXX Rewritten for the great lulz and justice. * - * Major cleanup and overhaul to remove much redundant codes. - * Highlights: - * 1) Support for signed / unsigned 16, 24 and 32 bit, - * big / little endian, - * 2) Unlimited channels. - * - * 2005-06-11: - * ========== - * - * *New* and rewritten soft sample rate converter supporting arbitrary sample - * rates, fine grained scaling/coefficients and a unified up/down stereo - * converter. Most of the disclaimers from orion's notes also applies - * here, regarding linear interpolation deficiencies and pre/post - * anti-aliasing filtering issues. This version comes with a much simpler and - * tighter interface, although it works almost exactly like the older one. - * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * * - * This new implementation is fully dedicated in memory of Cameron Grant, * - * the creator of the magnificent, highly addictive feeder infrastructure. * - * * - * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * - * Orion's notes: - * ============= - * - * This rate conversion code uses linear interpolation without any - * pre- or post- interpolation filtering to combat aliasing. This - * greatly limits the sound quality and should be addressed at some - * stage in the future. - * - * Since this accuracy of interpolation is sensitive and examination - * of the algorithm output is harder from the kernel, the code is - * designed to be compiled in the kernel and in a userland test - * harness. This is done by selectively including and excluding code - * with several portions based on whether _KERNEL is defined. It's a - * little ugly, but exceedingly useful. The testsuite and its - * revisions can be found at: - * http://people.freebsd.org/~orion/files/feedrate/ - * - * Special thanks to Ken Marx for exposing flaws in the code and for - * testing revisions. + * Z Resampler, which means any effort to create future replacement + * for this resampler are simply absurd unless the world decide + * to add new alphabet after Z. */ +/* + * FreeBSD bandlimited sinc interpolator, technically based on + * "Digital Audio Resampling " by Julius O. Smith III + * - http://ccrma.stanford.edu/~jos/resample/ . + */ + +#ifdef _KERNEL #include +#include #include "feeder_if.h" +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_rate.c,v 1.23 2007/06/16 03:37:28 ariff Exp $"); +#endif + +#include "feeder_rate_gen.h" + +#if !defined(_KERNEL) && defined(SND_DIAGNOSTIC) +#undef Z_DIAGNOSTIC +#define Z_DIAGNOSTIC 1 +#elif defined(_KERNEL) +#undef Z_DIAGNOSTIC +#endif + +#ifndef Z_QUALITY_DEFAULT +#define Z_QUALITY_DEFAULT Z_QUALITY_LINEAR +#endif + +#define Z_RESERVOIR 2048 +#define Z_RESERVOIR_MAX 131072 + +#define Z_SINC_MAX 0x3fffff +#define Z_SINC_DOWNMAX 48 /* 384000 / 8000 */ + +#ifdef _KERNEL +#define Z_POLYPHASE_MAX 183040 /* 286 taps, 640 phases */ +#else +#define Z_POLYPHASE_MAX 1464320 /* 286 taps, 5120 phases */ +#endif + +#define Z_RATE_DEFAULT 48000 + +#define Z_RATE_MIN FEEDRATE_RATEMIN +#define Z_RATE_MAX FEEDRATE_RATEMAX +#define Z_ROUNDHZ FEEDRATE_ROUNDHZ +#define Z_ROUNDHZ_MIN FEEDRATE_ROUNDHZ_MIN +#define Z_ROUNDHZ_MAX FEEDRATE_ROUNDHZ_MAX + +#define Z_RATE_SRC FEEDRATE_SRC +#define Z_RATE_DST FEEDRATE_DST +#define Z_RATE_QUALITY FEEDRATE_QUALITY +#define Z_RATE_CHANNELS FEEDRATE_CHANNELS + +#define Z_PARANOID 1 + +#define Z_MULTIFORMAT 1 + +#ifdef _KERNEL +#undef Z_USE_ALPHADRIFT +#define Z_USE_ALPHADRIFT 1 +#endif -#define RATE_ASSERT(x, y) /* KASSERT(x,y) */ -#define RATE_TEST(x, y) /* if (!(x)) printf y */ -#define RATE_TRACE(x...) /* printf(x) */ - -MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder"); - -/* - * Don't overflow 32bit integer, since everything is done - * within 32bit arithmetic. - */ -#define RATE_FACTOR_MIN 1 -#define RATE_FACTOR_MAX PCM_S24_MAX -#define RATE_FACTOR_SAFE(val) (!((val) < RATE_FACTOR_MIN || \ - (val) > RATE_FACTOR_MAX)) - -struct feed_rate_info; - -typedef uint32_t (*feed_rate_converter)(struct feed_rate_info *, - uint8_t *, uint32_t); - -struct feed_rate_info { - uint32_t src, dst; /* rounded source / destination rates */ - uint32_t rsrc, rdst; /* original source / destination rates */ - uint32_t gx, gy; /* interpolation / decimation ratio */ - uint32_t alpha; /* interpolation distance */ - uint32_t pos, bpos; /* current sample / buffer positions */ - uint32_t bufsz; /* total buffer size limit */ - uint32_t bufsz_init; /* allocated buffer size */ - uint32_t channels; /* total channels */ - uint32_t bps; /* bytes-per-sample */ -#ifdef FEEDRATE_STRAY - uint32_t stray; /* stray bytes */ +#define Z_FACTOR_MIN 1 +#define Z_FACTOR_MAX Z_MASK +#define Z_FACTOR_SAFE(v) (!((v) < Z_FACTOR_MIN || (v) > Z_FACTOR_MAX)) + +struct z_info; + +typedef void (*z_resampler_t)(struct z_info *, uint8_t *); + +struct z_info { + int32_t rsrc, rdst; /* original source / destination rates */ + int32_t src, dst; /* rounded source / destination rates */ + int32_t channels; /* total channels */ + int32_t bps; /* bytes-per-sample */ + int32_t quality; /* resampling quality */ + + int32_t z_gx, z_gy; /* interpolation / decimation ratio */ + int32_t z_alpha; /* output sample time phase / drift */ + uint8_t *z_delay; /* FIR delay line / linear buffer */ + int32_t *z_coeff; /* FIR coefficients */ + int32_t *z_dcoeff; /* FIR coefficients differences */ + int32_t *z_pcoeff; /* FIR polyphase coefficients */ + int32_t z_scale; /* output scaling */ + int32_t z_dx; /* input sample drift increment */ + int32_t z_dy; /* output sample drift increment */ +#ifdef Z_USE_ALPHADRIFT + int32_t z_alphadrift; /* alpha drift rate */ + int32_t z_startdrift; /* buffer start position drift rate */ #endif - uint8_t *buffer; - feed_rate_converter convert; + int32_t z_mask; /* delay line full length mask */ + int32_t z_size; /* half width of FIR taps */ + int32_t z_full; /* full size of delay line */ + int32_t z_alloc; /* largest allocated full size of delay line */ + int32_t z_start; /* buffer processing start position */ + int32_t z_pos; /* current position for the next feed */ +#ifdef Z_DIAGNOSTIC + uint32_t z_cycle; /* output cycle, purely for statistical */ +#endif + int32_t z_maxfeed; /* maximum feed to avoid 32bit overflow */ + + z_resampler_t z_resample; }; -int feeder_rate_min = FEEDRATE_RATEMIN; -int feeder_rate_max = FEEDRATE_RATEMAX; -int feeder_rate_round = FEEDRATE_ROUNDHZ; +int feeder_rate_min = Z_RATE_MIN; +int feeder_rate_max = Z_RATE_MAX; +int feeder_rate_round = Z_ROUNDHZ; +int feeder_rate_quality = Z_QUALITY_DEFAULT; + +static int feeder_rate_polyphase_max = Z_POLYPHASE_MAX; +#ifdef _KERNEL TUNABLE_INT("hw.snd.feeder_rate_min", &feeder_rate_min); TUNABLE_INT("hw.snd.feeder_rate_max", &feeder_rate_max); TUNABLE_INT("hw.snd.feeder_rate_round", &feeder_rate_round); +TUNABLE_INT("hw.snd.feeder_rate_quality", &feeder_rate_quality); + +TUNABLE_INT("hw.snd.feeder_rate_polyphase_max", &feeder_rate_polyphase_max); +SYSCTL_INT(_hw_snd, OID_AUTO, feeder_rate_polyphase_max, CTLFLAG_RW, + &feeder_rate_polyphase_max, 0, "maximum allowable polyphase entries"); static int sysctl_hw_snd_feeder_rate_min(SYSCTL_HANDLER_ARGS) @@ -131,17 +162,20 @@ val = feeder_rate_min; err = sysctl_handle_int(oidp, &val, 0, req); - if (err != 0 || req->newptr == NULL) + + if (err != 0 || req->newptr == NULL || val == feeder_rate_min) return (err); - if (RATE_FACTOR_SAFE(val) && val < feeder_rate_max) - feeder_rate_min = val; - else - err = EINVAL; - return (err); + + if (!(Z_FACTOR_SAFE(val) && val < feeder_rate_max)) + return (EINVAL); + + feeder_rate_min = val; + + return (0); } SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_min, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_min, "I", - "minimum allowable rate"); + 0, sizeof(int), sysctl_hw_snd_feeder_rate_min, "I", + "minimum allowable rate"); static int sysctl_hw_snd_feeder_rate_max(SYSCTL_HANDLER_ARGS) @@ -150,17 +184,20 @@ val = feeder_rate_max; err = sysctl_handle_int(oidp, &val, 0, req); - if (err != 0 || req->newptr == NULL) + + if (err != 0 || req->newptr == NULL || val == feeder_rate_max) return (err); - if (RATE_FACTOR_SAFE(val) && val > feeder_rate_min) - feeder_rate_max = val; - else - err = EINVAL; - return (err); + + if (!(Z_FACTOR_SAFE(val) && val > feeder_rate_min)) + return (EINVAL); + + feeder_rate_max = val; + + return (0); } SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_max, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_max, "I", - "maximum allowable rate"); + 0, sizeof(int), sysctl_hw_snd_feeder_rate_max, "I", + "maximum allowable rate"); static int sysctl_hw_snd_feeder_rate_round(SYSCTL_HANDLER_ARGS) @@ -169,472 +206,1502 @@ val = feeder_rate_round; err = sysctl_handle_int(oidp, &val, 0, req); - if (err != 0 || req->newptr == NULL) + + if (err != 0 || req->newptr == NULL || val == feeder_rate_round) return (err); - if (val < FEEDRATE_ROUNDHZ_MIN || val > FEEDRATE_ROUNDHZ_MAX) - err = EINVAL; - else - feeder_rate_round = val - (val % FEEDRATE_ROUNDHZ); - return (err); + + if (val < Z_ROUNDHZ_MIN || val > Z_ROUNDHZ_MAX) + return (EINVAL); + + feeder_rate_round = val - (val % Z_ROUNDHZ); + + return (0); } SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_round, CTLTYPE_INT | CTLFLAG_RW, - 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I", - "sample rate converter rounding threshold"); + 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I", + "sample rate converter rounding threshold"); -#define FEEDER_RATE_CONVERT(FMTBIT, RATE_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ -static uint32_t \ -feed_convert_##SIGNS##FMTBIT##ENDIANS(struct feed_rate_info *info, \ - uint8_t *dst, uint32_t max) \ -{ \ - uint32_t ret, smpsz, ch, pos, bpos, gx, gy, alpha, d1, d2; \ - int32_t x, y; \ - int i; \ - uint8_t *src, *sx, *sy; \ - \ - ret = 0; \ - alpha = info->alpha; \ - gx = info->gx; \ - gy = info->gy; \ - pos = info->pos; \ - bpos = info->bpos; \ - src = info->buffer + pos; \ - ch = info->channels; \ - smpsz = PCM_##FMTBIT##_BPS * ch; \ - for (;;) { \ - if (alpha < gx) { \ - alpha += gy; \ - pos += smpsz; \ - if (pos == bpos) \ - break; \ - src += smpsz; \ - } else { \ - alpha -= gx; \ - d1 = (alpha << PCM_FXSHIFT) / gy; \ - d2 = (1U << PCM_FXSHIFT) - d1; \ - sx = src - smpsz; \ - sy = src; \ - i = ch; \ - do { \ - x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx); \ - y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy); \ - x = (((RATE_INTCAST)x * d1) + \ - ((RATE_INTCAST)y * d2)) >> PCM_FXSHIFT; \ - PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, x); \ - dst += PCM_##FMTBIT##_BPS; \ - sx += PCM_##FMTBIT##_BPS; \ - sy += PCM_##FMTBIT##_BPS; \ - ret += PCM_##FMTBIT##_BPS; \ - } while (--i != 0); \ - if (ret == max) \ - break; \ - } \ - } \ - info->alpha = alpha; \ - info->pos = pos; \ - return (ret); \ -} - -FEEDER_RATE_CONVERT(8, int32_t, S, s, NE, ne) -FEEDER_RATE_CONVERT(16, int32_t, S, s, LE, le) -FEEDER_RATE_CONVERT(24, int32_t, S, s, LE, le) -FEEDER_RATE_CONVERT(32, intpcm_t, S, s, LE, le) -FEEDER_RATE_CONVERT(16, int32_t, S, s, BE, be) -FEEDER_RATE_CONVERT(24, int32_t, S, s, BE, be) -FEEDER_RATE_CONVERT(32, intpcm_t, S, s, BE, be) -FEEDER_RATE_CONVERT(8, int32_t, U, u, NE, ne) -FEEDER_RATE_CONVERT(16, int32_t, U, u, LE, le) -FEEDER_RATE_CONVERT(24, int32_t, U, u, LE, le) -FEEDER_RATE_CONVERT(32, intpcm_t, U, u, LE, le) -FEEDER_RATE_CONVERT(16, int32_t, U, u, BE, be) -FEEDER_RATE_CONVERT(24, int32_t, U, u, BE, be) -FEEDER_RATE_CONVERT(32, intpcm_t, U, u, BE, be) +static int +sysctl_hw_snd_feeder_rate_quality(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct pcm_channel *c; + struct pcm_feeder *f; + int i, err, val; -static void -feed_speed_ratio(uint32_t src, uint32_t dst, uint32_t *gx, uint32_t *gy) + val = feeder_rate_quality; + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err != 0 || req->newptr == NULL || val == feeder_rate_quality) + return (err); + + if (val < Z_QUALITY_MIN || val > Z_QUALITY_MAX) + return (EINVAL); + + feeder_rate_quality = val; + + /* + * Traverse all available channels on each device and try to + * set resampler quality if and only if it is exist as + * part of feeder chains and the channel is idle. + */ + for (i = 0; pcm_devclass != NULL && + i < devclass_get_maxunit(pcm_devclass); i++) { + d = devclass_get_softc(pcm_devclass, i); + if (!PCM_REGISTERED(d)) + continue; + PCM_LOCK(d); + PCM_WAIT(d); + PCM_ACQUIRE(d); + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + f = chn_findfeeder(c, FEEDER_RATE); + if (f == NULL || f->data == NULL || CHN_STARTED(c)) { + CHN_UNLOCK(c); + continue; + } + (void)FEEDER_SET(f, FEEDRATE_QUALITY, val); + CHN_UNLOCK(c); + } + PCM_RELEASE(d); + PCM_UNLOCK(d); + } + + return (0); +} +SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_quality, CTLTYPE_INT | CTLFLAG_RW, + 0, sizeof(int), sysctl_hw_snd_feeder_rate_quality, "I", + "sample rate converter quality ("__XSTRING(Z_QUALITY_MIN)"=low .. " + __XSTRING(Z_QUALITY_MAX)"=high)"); +#endif /* _KERNEL */ + + +/* + * Resampler type. + */ +#define Z_IS_ZOH(i) ((i)->quality == Z_QUALITY_ZOH) +#define Z_IS_LINEAR(i) ((i)->quality == Z_QUALITY_LINEAR) +#define Z_IS_SINC(i) ((i)->quality > Z_QUALITY_LINEAR) + +/* + * Macroses for accurate sample time drift calculations. + * + * gy2gx : given the amount of output, return the _exact_ required amount of + * input. + * gx2gy : given the amount of input, return the _maximum_ amount of output + * that will be generated. + * drift : given the amount of input and output, return the elapsed + * sample-time. + */ +#define _Z_GCAST(x) ((uint64_t)(x)) + +#if defined(__GNUCLIKE_ASM) && defined(__i386__) +/* + * This is where i386 being beaten to a pulp. Fortunately this function is + * rarely being called and if it is, it will decide the best (hopefully) + * fastest way to do the division. If we can ensure that everything is dword + * aligned, letting the compiler to call udivdi3 to do the division can be + * faster compared to this. + * + * amd64 is the clear winner here, no question about it. + */ +static __inline uint32_t +Z_DIV(uint64_t v, uint32_t d) +{ + uint32_t hi, lo, quo, rem; + + hi = v >> 32; + lo = v & 0xffffffff; + + /* + * As much as we can, try to avoid long division like a plague. + */ + if (hi == 0) + quo = lo / d; + else + __asm("divl %2" + : "=a" (quo), "=d" (rem) + : "r" (d), "0" (lo), "1" (hi)); + + return (quo); +} +#else +#define Z_DIV(x, y) ((x) / (y)) +#endif + +#define _Z_GY2GX(i, a, v) \ + Z_DIV(((_Z_GCAST((i)->z_gx) * (v)) + ((i)->z_gy - (a) - 1)), \ + (i)->z_gy) + +#define _Z_GX2GY(i, a, v) \ + Z_DIV(((_Z_GCAST((i)->z_gy) * (v)) + (a)), (i)->z_gx) + +#define _Z_DRIFT(i, x, y) \ + ((_Z_GCAST((i)->z_gy) * (x)) - (_Z_GCAST((i)->z_gx) * (y))) + +#define z_gy2gx(i, v) _Z_GY2GX(i, (i)->z_alpha, v) +#define z_gx2gy(i, v) _Z_GX2GY(i, (i)->z_alpha, v) +#define z_drift(i, x, y) _Z_DRIFT(i, x, y) + +/* + * Macroses for SINC coefficients table manipulations.. whatever. + */ +#define Z_SINC_COEFF_IDX(i) ((i)->quality - Z_QUALITY_LINEAR - 1) + +#define Z_SINC_LEN(i) \ + ((int32_t)(((uint64_t)z_coeff_tab[Z_SINC_COEFF_IDX(i)].len << \ + Z_SHIFT) / (i)->z_dy)) + +#define Z_SINC_BASE_LEN(i) \ + ((z_coeff_tab[Z_SINC_COEFF_IDX(i)].len - 1) >> (Z_DRIFT_SHIFT - 1)) + +/* + * Macroses for linear delay buffer operations. Alignment is not + * really necessary since we're not using true circular buffer, but it + * will help us guard against possible trespasser. To be honest, + * the linear block operations does not need guarding at all due to + * accurate drifting! + */ +#define z_align(i, v) ((v) & (i)->z_mask) +#define z_next(i, o, v) z_align(i, (o) + (v)) +#define z_prev(i, o, v) z_align(i, (o) - (v)) +#define z_fetched(i) (z_align(i, (i)->z_pos - (i)->z_start) - 1) +#define z_free(i) ((i)->z_full - (i)->z_pos) + +/* + * Macroses for Bla Bla .. :) + */ +#define z_copy(src, dst, sz) (void)memcpy(dst, src, sz) +#define z_feed(x...) FEEDER_FEED(x) + +static __inline uint32_t +z_min(uint32_t x, uint32_t y) { - uint32_t w, x = src, y = dst; + + return ((x < y) ? x : y); +} + +static int32_t +z_gcd(int32_t x, int32_t y) +{ + int32_t w; while (y != 0) { w = x % y; x = y; y = w; } - *gx = src / x; - *gy = dst / x; + + return (x); } +static int32_t +z_roundpow2(int32_t v) +{ + int32_t i; + + i = 1; + + /* + * Let it overflow at will.. + */ + while (i > 0 && i < v) + i <<= 1; + + return (i); +} + +/* + * Zero Order Hold, the worst of the worst, an insult against quality, + * but super fast. + */ static void -feed_rate_reset(struct feed_rate_info *info) +z_feed_zoh(struct z_info *info, uint8_t *dst) { - info->src = info->rsrc - (info->rsrc % - ((feeder_rate_round > 0) ? feeder_rate_round : 1)); - info->dst = info->rdst - (info->rdst % - ((feeder_rate_round > 0) ? feeder_rate_round : 1)); - info->gx = 1; - info->gy = 1; - info->alpha = 0; - info->channels = 1; - info->bps = PCM_8_BPS; - info->convert = NULL; - info->bufsz = info->bufsz_init; - info->pos = 1; - info->bpos = 2; -#ifdef FEEDRATE_STRAY - info->stray = 0; +#if 0 + z_copy(info->z_delay + + (info->z_start * info->channels * info->bps), dst, + info->channels * info->bps); +#else + uint32_t cnt; + uint8_t *src; + + cnt = info->channels * info->bps; + src = info->z_delay + (info->z_start * cnt); + + /* + * This is a bit faster than doing bcopy() since we're dealing + * with possible unaligned samples. + */ + do { + *dst++ = *src++; + } while (--cnt != 0); #endif } -static int -feed_rate_setup(struct pcm_feeder *f) -{ - struct feed_rate_info *info = f->data; - static const struct { - uint32_t format; /* pcm / audio format */ - uint32_t bps; /* bytes-per-sample, regardless of - total channels */ - feed_rate_converter convert; - } convtbl[] = { - { AFMT_S8, PCM_8_BPS, feed_convert_s8ne }, - { AFMT_S16_LE, PCM_16_BPS, feed_convert_s16le }, - { AFMT_S24_LE, PCM_24_BPS, feed_convert_s24le }, - { AFMT_S32_LE, PCM_32_BPS, feed_convert_s32le }, - { AFMT_S16_BE, PCM_16_BPS, feed_convert_s16be }, - { AFMT_S24_BE, PCM_24_BPS, feed_convert_s24be }, - { AFMT_S32_BE, PCM_32_BPS, feed_convert_s32be }, - { AFMT_U8, PCM_8_BPS, feed_convert_u8ne }, - { AFMT_U16_LE, PCM_16_BPS, feed_convert_u16le }, - { AFMT_U24_LE, PCM_24_BPS, feed_convert_u24le }, - { AFMT_U32_LE, PCM_32_BPS, feed_convert_u32le }, - { AFMT_U16_BE, PCM_16_BPS, feed_convert_u16be }, - { AFMT_U24_BE, PCM_24_BPS, feed_convert_u24be }, - { AFMT_U32_BE, PCM_32_BPS, feed_convert_u32be }, - { 0, 0, NULL }, - }; - uint32_t i; +/* + * Linear Interpolation. This at least sounds better (perceptually) and fast, + * but without any proper filtering which means aliasing still exist and + * could become worst with a right sample. Interpolation centered within + * Z_LINEAR_ONE between the present and previous sample and everything is + * done with simple 32bit scaling arithmetic. + */ +#define Z_DECLARE_LINEAR(SIGN, BIT, ENDIAN) \ +static void \ +z_feed_linear_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \ +{ \ + int32_t z; \ + intpcm_t x, y; \ + uint32_t ch; \ + uint8_t *sx, *sy; \ + \ + z = ((uint32_t)info->z_alpha * info->z_dx) >> Z_LINEAR_UNSHIFT; \ + \ + sx = info->z_delay + (info->z_start * info->channels * \ + PCM_##BIT##_BPS); \ + sy = sx - (info->channels * PCM_##BIT##_BPS); \ + \ + ch = info->channels; \ + \ + do { \ + x = _PCM_READ_##SIGN##BIT##_##ENDIAN(sx); \ + y = _PCM_READ_##SIGN##BIT##_##ENDIAN(sy); \ + x = Z_LINEAR_INTERPOLATE_##BIT(z, x, y); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \ + sx += PCM_##BIT##_BPS; \ + sy += PCM_##BIT##_BPS; \ + dst += PCM_##BIT##_BPS; \ + } while (--ch != 0); \ +} - feed_rate_reset(info); +/* + * Userland clipping diagnostic check, not enabled in kernel compilation. + * While doing sinc interpolation, unrealistic samples like full scale sine + * wav will clip, but for other things this will not make any noise at all. + * Don't worry, everything has been tuned to NOT OVERFLOW even with idiotic + * samples that participate in loudness war. Besides, everybody should learn + * how to normalized perceived loudness of their own music/sounds/samples + * (hint: ReplayGain). + */ +#ifdef Z_DIAGNOSTIC +#define Z_CLIP_CHECK(v, BIT) do { \ + if ((v) > PCM_S##BIT##_MAX) { \ + fprintf(stderr, "Overflow: v=%jd, max=%jd\n", \ + (intmax_t)(v), (intmax_t)PCM_S##BIT##_MAX); \ + } else if ((v) < PCM_S##BIT##_MIN) { \ + fprintf(stderr, "Underflow: v=%jd, min=%jd\n", \ + (intmax_t)(v), (intmax_t)PCM_S##BIT##_MIN); \ + } \ +} while (0) +#else +#define Z_CLIP_CHECK(...) +#endif - if (info->src != info->dst) - feed_speed_ratio(info->src, info->dst, &info->gx, &info->gy); +#define Z_CLAMP(v, BIT) \ + (((v) > PCM_S##BIT##_MAX) ? PCM_S##BIT##_MAX : \ + (((v) < PCM_S##BIT##_MIN) ? PCM_S##BIT##_MIN : (v))) - if (!(RATE_FACTOR_SAFE(info->gx) && RATE_FACTOR_SAFE(info->gy))) +/* + * Sine Cardinal (SINC) Interpolation. Scaling is done in 64 bit, so + * there's no point to hold the plate any longer. All samples will be + * shifted to a full 32 bit, scaled and restored during write for + * maximum dynamic range (only for downsampling). + */ +#define _Z_SINC_ACCUMULATE(SIGN, BIT, ENDIAN, adv) \ + c += z >> Z_SHIFT; \ + z &= Z_MASK; \ + coeff = Z_COEFF_INTERPOLATE(z, z_coeff[c], z_dcoeff[c]); \ + x = _PCM_READ_##SIGN##BIT##_##ENDIAN(p); \ + v += (intpcm64_t)x * coeff; \ + z += info->z_dy; \ + p adv##= info->channels * PCM_##BIT##_BPS + +/* + * XXX GCC4 optimization is such a !@#$%, need manual unrolling. + */ +#if defined(__GNUC__) && __GNUC__ >= 4 +#define Z_SINC_ACCUMULATE(x...) do { \ + _Z_SINC_ACCUMULATE(x); \ + _Z_SINC_ACCUMULATE(x); \ +} while (0) +#define Z_SINC_ACCUMULATE_DECR 2 +#else +#define Z_SINC_ACCUMULATE(x...) do { \ + _Z_SINC_ACCUMULATE(x); \ +} while (0) +#define Z_SINC_ACCUMULATE_DECR 1 +#endif + +#define Z_DECLARE_SINC(SIGN, BIT, ENDIAN) \ +static void \ +z_feed_sinc_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \ +{ \ + intpcm64_t v; \ + intpcm_t x; \ + uint8_t *p; \ + int32_t coeff, z, *z_coeff, *z_dcoeff; \ + uint32_t c, center, ch, i; \ + \ + z_coeff = info->z_coeff; \ + z_dcoeff = info->z_dcoeff; \ + center = z_prev(info, info->z_start, info->z_size); \ + ch = info->channels * PCM_##BIT##_BPS; \ + dst += ch; \ + \ + do { \ + dst -= PCM_##BIT##_BPS; \ + ch -= PCM_##BIT##_BPS; \ + v = 0; \ + z = info->z_alpha * info->z_dx; \ + c = 0; \ + p = info->z_delay + (z_next(info, center, 1) * \ + info->channels * PCM_##BIT##_BPS) + ch; \ + for (i = info->z_size; i != 0; i -= Z_SINC_ACCUMULATE_DECR) \ + Z_SINC_ACCUMULATE(SIGN, BIT, ENDIAN, +); \ + z = info->z_dy - (info->z_alpha * info->z_dx); \ + c = 0; \ + p = info->z_delay + (center * info->channels * \ + PCM_##BIT##_BPS) + ch; \ + for (i = info->z_size; i != 0; i -= Z_SINC_ACCUMULATE_DECR) \ + Z_SINC_ACCUMULATE(SIGN, BIT, ENDIAN, -); \ + if (info->z_scale != Z_ONE) \ + v = Z_SCALE_##BIT(v, info->z_scale); \ + else \ + v >>= Z_COEFF_SHIFT; \ + Z_CLIP_CHECK(v, BIT); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, Z_CLAMP(v, BIT)); \ + } while (ch != 0); \ +} + +#define Z_DECLARE_SINC_POLYPHASE(SIGN, BIT, ENDIAN) \ +static void \ +z_feed_sinc_polyphase_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \ +{ \ + intpcm64_t v; \ + intpcm_t x; \ + uint8_t *p; \ + int32_t ch, i, start, *z_pcoeff; \ + \ + ch = info->channels * PCM_##BIT##_BPS; \ + dst += ch; \ + start = z_prev(info, info->z_start, (info->z_size << 1) - 1) * ch; \ + \ + do { \ + dst -= PCM_##BIT##_BPS; \ + ch -= PCM_##BIT##_BPS; \ + v = 0; \ + p = info->z_delay + start + ch; \ + z_pcoeff = info->z_pcoeff + \ + ((info->z_alpha * info->z_size) << 1); \ + for (i = info->z_size; i != 0; i--) { \ + x = _PCM_READ_##SIGN##BIT##_##ENDIAN(p); \ + v += (intpcm64_t)x * *z_pcoeff; \ + z_pcoeff++; \ + p += info->channels * PCM_##BIT##_BPS; \ + x = _PCM_READ_##SIGN##BIT##_##ENDIAN(p); \ + v += (intpcm64_t)x * *z_pcoeff; \ + z_pcoeff++; \ + p += info->channels * PCM_##BIT##_BPS; \ + } \ + if (info->z_scale != Z_ONE) \ + v = Z_SCALE_##BIT(v, info->z_scale); \ + else \ + v >>= Z_COEFF_SHIFT; \ + Z_CLIP_CHECK(v, BIT); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, Z_CLAMP(v, BIT)); \ + } while (ch != 0); \ +} + +#define Z_DECLARE(SIGN, BIT, ENDIAN) \ + Z_DECLARE_LINEAR(SIGN, BIT, ENDIAN) \ + Z_DECLARE_SINC(SIGN, BIT, ENDIAN) \ + Z_DECLARE_SINC_POLYPHASE(SIGN, BIT, ENDIAN) + +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) +Z_DECLARE(S, 16, LE) +Z_DECLARE(S, 32, LE) +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) +Z_DECLARE(S, 16, BE) +Z_DECLARE(S, 32, BE) +#endif +#ifdef FEEDER_MULTIFORMAT +Z_DECLARE(S, 8, NE) +Z_DECLARE(S, 24, LE) +Z_DECLARE(S, 24, BE) +Z_DECLARE(U, 8, NE) +Z_DECLARE(U, 16, LE) +Z_DECLARE(U, 24, LE) +Z_DECLARE(U, 32, LE) +Z_DECLARE(U, 16, BE) +Z_DECLARE(U, 24, BE) +Z_DECLARE(U, 32, BE) +#endif + +enum { + Z_RESAMPLER_ZOH, + Z_RESAMPLER_LINEAR, + Z_RESAMPLER_SINC, + Z_RESAMPLER_SINC_POLYPHASE, + Z_RESAMPLER_LAST +}; + +#define Z_RESAMPLER_IDX(i) \ + (Z_IS_SINC(i) ? Z_RESAMPLER_SINC : (i)->quality) + +#define Z_RESAMPLER_ENTRY(SIGN, BIT, ENDIAN) \ + { \ + AFMT_##SIGN##BIT##_##ENDIAN, \ + { \ + [Z_RESAMPLER_ZOH] = z_feed_zoh, \ + [Z_RESAMPLER_LINEAR] = z_feed_linear_##SIGN##BIT##ENDIAN, \ + [Z_RESAMPLER_SINC] = z_feed_sinc_##SIGN##BIT##ENDIAN, \ + [Z_RESAMPLER_SINC_POLYPHASE] = \ + z_feed_sinc_polyphase_##SIGN##BIT##ENDIAN \ + } \ + } + +static const struct { + uint32_t format; + z_resampler_t resampler[Z_RESAMPLER_LAST]; +} z_resampler_tab[] = { +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) + Z_RESAMPLER_ENTRY(S, 16, LE), + Z_RESAMPLER_ENTRY(S, 32, LE), +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) + Z_RESAMPLER_ENTRY(S, 16, BE), + Z_RESAMPLER_ENTRY(S, 32, BE), +#endif +#ifdef FEEDER_MULTIFORMAT + Z_RESAMPLER_ENTRY(S, 8, NE), + Z_RESAMPLER_ENTRY(S, 24, LE), + Z_RESAMPLER_ENTRY(S, 24, BE), + Z_RESAMPLER_ENTRY(U, 8, NE), + Z_RESAMPLER_ENTRY(U, 16, LE), + Z_RESAMPLER_ENTRY(U, 24, LE), + Z_RESAMPLER_ENTRY(U, 32, LE), + Z_RESAMPLER_ENTRY(U, 16, BE), + Z_RESAMPLER_ENTRY(U, 24, BE), + Z_RESAMPLER_ENTRY(U, 32, BE), +#endif +}; + +#define Z_RESAMPLER_TAB_SIZE \ + ((int32_t)(sizeof(z_resampler_tab) / sizeof(z_resampler_tab[0]))) + +static void +z_resampler_reset(struct z_info *info) +{ + + info->src = info->rsrc - (info->rsrc % ((feeder_rate_round > 0 && + info->rsrc > feeder_rate_round) ? feeder_rate_round : 1)); + info->dst = info->rdst - (info->rdst % ((feeder_rate_round > 0 && + info->rdst > feeder_rate_round) ? feeder_rate_round : 1)); + info->z_gx = 1; + info->z_gy = 1; + info->z_alpha = 0; + info->z_resample = NULL; + info->z_size = 1; + info->z_coeff = NULL; + info->z_dcoeff = NULL; + if (info->z_pcoeff != NULL) { + free(info->z_pcoeff, M_DEVBUF); + info->z_pcoeff = NULL; + } + info->z_scale = Z_ONE; + info->z_dx = Z_FULL_ONE; + info->z_dy = Z_FULL_ONE; +#ifdef Z_DIAGNOSTIC + info->z_cycle = 0; +#endif + if (info->quality < Z_QUALITY_MIN) + info->quality = Z_QUALITY_MIN; + else if (info->quality > Z_QUALITY_MAX) + info->quality = Z_QUALITY_MAX; +} + +#ifdef Z_PARANOID +static int32_t +z_resampler_sinc_len(struct z_info *info) +{ + int32_t c, z, len, lmax; + + if (!Z_IS_SINC(info)) + return (1); + + /* + * A rather careful (or useless) way to calculate filter length. + * Z_SINC_LEN() itself is accurate enough to do its job. Extra + * sanity checking is not going to hurt though.. + */ + c = 0; + z = info->z_dy; + len = 0; + lmax = z_coeff_tab[Z_SINC_COEFF_IDX(info)].len; + + do { + c += z >> Z_SHIFT; + z &= Z_MASK; + z += info->z_dy; + } while (c < lmax && ++len > 0); + + if (len != Z_SINC_LEN(info)) { +#ifdef _KERNEL + printf("%s(): sinc l=%d != Z_SINC_LEN=%d\n", + __func__, len, Z_SINC_LEN(info)); +#else + fprintf(stderr, "%s(): sinc l=%d != Z_SINC_LEN=%d\n", + __func__, len, Z_SINC_LEN(info)); return (-1); +#endif + } + + return (len); +} +#else +#define z_resampler_sinc_len(i) (Z_IS_SINC(i) ? Z_SINC_LEN(i) : 1) +#endif + +#define Z_POLYPHASE_COEFF_SHIFT 0 + +#if !(defined(Z_COEFF_INTERP_ZOH) || defined(Z_COEFF_INTERP_LINEAR) || \ + defined(Z_COEFF_INTERP_QUADRATIC) || defined(Z_COEFF_INTERP_HERMITE) || \ + defined(Z_COEFF_INTER_BSPLINE) || defined(Z_COEFF_INTERP_OPT32X) || \ + defined(Z_COEFF_INTERP_OPT16X) || defined(Z_COEFF_INTERP_OPT8X) || \ + defined(Z_COEFF_INTERP_OPT4X) || defined(Z_COEFF_INTERP_OPT2X)) +#if Z_DRIFT_SHIFT >= 8 +#define Z_COEFF_INTERP_LINEAR 1 +#elif Z_DRIFT_SHIFT == 7 +#define Z_COEFF_INTERP_QUADRATIC 1 +#elif Z_DRIFT_SHIFT == 6 +#define Z_COEFF_INTERP_HERMITE 1 +#elif Z_DRIFT_SHIFT == 5 +#define Z_COEFF_INTERP_OPT32X 1 +#elif Z_DRIFT_SHIFT == 4 +#define Z_COEFF_INTERP_OPT16X 1 +#elif Z_DRIFT_SHIFT == 3 +#define Z_COEFF_INTERP_OPT8X 1 +#elif Z_DRIFT_SHIFT == 2 +#define Z_COEFF_INTERP_OPT4X 1 +#elif Z_DRIFT_SHIFT == 1 +#define Z_COEFF_INTERP_OPT2X 1 +#else +#error "Z_DRIFT_SHIFT screwed!" +#endif +#endif + +static int32_t +z_coeff_interpolate(int32_t z, int32_t *z_coeff) +{ + int32_t coeff; +#if defined(Z_COEFF_INTERP_ZOH) + + /* 1-point, 0th-order (Zero Order Hold) */ + z = z; + coeff = z_coeff[0]; +#elif defined(Z_COEFF_INTERP_LINEAR) + int32_t zl0, zl1; + + /* 2-point, 1st-order Linear */ + zl0 = z_coeff[0]; + zl1 = z_coeff[1] - z_coeff[0]; + + coeff = (((int64_t)zl1 * z) >> Z_SHIFT) + zl0; +#elif defined(Z_COEFF_INTERP_QUADRATIC) + int32_t zq0, zq1, zq2; + + /* 3-point, 2nd-order Quadratic */ + zq0 = z_coeff[0]; + zq1 = z_coeff[1] - z_coeff[-1]; + zq2 = z_coeff[1] + z_coeff[-1] - (z_coeff[0] << 1); + + coeff = ((((((int64_t)zq2 * z) >> Z_SHIFT) + + zq1) * z) >> (Z_SHIFT + 1)) + zq0; +#elif defined(Z_COEFF_INTERP_HERMITE) + int32_t zh0, zh1, zh2, zh3; + + /* 4-point, 3rd-order Hermite */ + zh0 = z_coeff[0]; + zh1 = z_coeff[1] - z_coeff[-1]; + zh2 = (z_coeff[-1] << 1) - (z_coeff[0] * 5) + (z_coeff[1] << 2) - + z_coeff[2]; + zh3 = z_coeff[2] - z_coeff[-1] + ((z_coeff[0] - z_coeff[1]) * 3); + + coeff = (((((((((int64_t)zh3 * z) >> Z_SHIFT) + + zh2) * z) >> Z_SHIFT) + zh1) * z) >> (Z_SHIFT + 1)) + zh0; +#elif defined(Z_COEFF_INTERP_BSPLINE) + int32_t zb0, zb1, zb2, zb3; + + /* 4-point, 3rd-order B-Spline */ + zb0 = (((int64_t)z_coeff[0] << 2) + z_coeff[-1] + z_coeff[1]) / 3; + zb1 = z_coeff[1] - z_coeff[-1]; + zb2 = z_coeff[-1] + z_coeff[1] - (z_coeff[0] << 1); + zb3 = (((z_coeff[0] - z_coeff[1]) * 3) + z_coeff[2] - z_coeff[-1]) / 3; + + coeff = ((((((((((int64_t)zb3 * z) >> Z_SHIFT) + + zb2) * z) >> Z_SHIFT) + zb1) * z) >> Z_SHIFT) + zb0) >> 1; +#elif defined(Z_COEFF_INTERP_OPT32X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; + + /* 6-point, 5th-order Optimal 32x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x1ac2260dLL * zoe1)) >> 30) + + ((0x0526cdcaLL * zoe2) >> 30) + ((0x00170c29LL * zoe3) >> 30); + zoc1 = ((0x14f8a49aLL * zoo1) >> 30) + + ((0x0d6d1109LL * zoo2) >> 30) + ((0x008cd4dcLL * zoo3) >> 30); + zoc2 = ((-0x0d3e94a4LL * zoe1) >> 30) + + ((0x0bddded4LL * zoe2) >> 30) + ((0x0160b5d0LL * zoe3) >> 30); + zoc3 = ((-0x0de10cc4LL * zoo1) >> 30) + + ((0x019b2a7dLL * zoo2) >> 30) + ((0x01cfe914LL * zoo3) >> 30); + zoc4 = ((0x02aa12d7LL * zoe1) >> 30) + + ((-0x03ff1bb3LL * zoe2) >> 30) + ((0x015508ddLL * zoe3) >> 30); + zoc5 = ((0x051d29e5LL * zoo1) >> 30) + + ((-0x028e7647LL * zoo2) >> 30) + ((0x0082d81aLL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#elif defined(Z_COEFF_INTERP_OPT16X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; + + /* 6-point, 5th-order Optimal 16x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x1ac2260dLL * zoe1)) >> 30) + + ((0x0526cdcaLL * zoe2) >> 30) + ((0x00170c29LL * zoe3) >> 30); + zoc1 = ((0x14f8a49aLL * zoo1) >> 30) + + ((0x0d6d1109LL * zoo2) >> 30) + ((0x008cd4dcLL * zoo3) >> 30); + zoc2 = ((-0x0d3e94a4LL * zoe1) >> 30) + + ((0x0bddded4LL * zoe2) >> 30) + ((0x0160b5d0LL * zoe3) >> 30); + zoc3 = ((-0x0de10cc4LL * zoo1) >> 30) + + ((0x019b2a7dLL * zoo2) >> 30) + ((0x01cfe914LL * zoo3) >> 30); + zoc4 = ((0x02aa12d7LL * zoe1) >> 30) + + ((-0x03ff1bb3LL * zoe2) >> 30) + ((0x015508ddLL * zoe3) >> 30); + zoc5 = ((0x051d29e5LL * zoo1) >> 30) + + ((-0x028e7647LL * zoo2) >> 30) + ((0x0082d81aLL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#elif defined(Z_COEFF_INTERP_OPT8X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; + + /* 6-point, 5th-order Optimal 8x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x1aa9b47dLL * zoe1)) >> 30) + + ((0x053d9944LL * zoe2) >> 30) + ((0x0018b23fLL * zoe3) >> 30); + zoc1 = ((0x14a104d1LL * zoo1) >> 30) + + ((0x0d7d2504LL * zoo2) >> 30) + ((0x0094b599LL * zoo3) >> 30); + zoc2 = ((-0x0d22530bLL * zoe1) >> 30) + + ((0x0bb37a2cLL * zoe2) >> 30) + ((0x016ed8e0LL * zoe3) >> 30); + zoc3 = ((-0x0d744b1cLL * zoo1) >> 30) + + ((0x01649591LL * zoo2) >> 30) + ((0x01dae93aLL * zoo3) >> 30); + zoc4 = ((0x02a7ee1bLL * zoe1) >> 30) + + ((-0x03fbdb24LL * zoe2) >> 30) + ((0x0153ed07LL * zoe3) >> 30); + zoc5 = ((0x04cf9b6cLL * zoo1) >> 30) + + ((-0x0266b378LL * zoo2) >> 30) + ((0x007a7c26LL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#elif defined(Z_COEFF_INTERP_OPT4X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; + + /* 6-point, 5th-order Optimal 4x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x1a8eda43LL * zoe1)) >> 30) + + ((0x0556ee38LL * zoe2) >> 30) + ((0x001a3784LL * zoe3) >> 30); + zoc1 = ((0x143d863eLL * zoo1) >> 30) + + ((0x0d910e36LL * zoo2) >> 30) + ((0x009ca889LL * zoo3) >> 30); + zoc2 = ((-0x0d026821LL * zoe1) >> 30) + + ((0x0b837773LL * zoe2) >> 30) + ((0x017ef0c6LL * zoe3) >> 30); + zoc3 = ((-0x0cef1502LL * zoo1) >> 30) + + ((0x01207a8eLL * zoo2) >> 30) + ((0x01e936dbLL * zoo3) >> 30); + zoc4 = ((0x029fe643LL * zoe1) >> 30) + + ((-0x03ef3fc8LL * zoe2) >> 30) + ((0x014f5923LL * zoe3) >> 30); + zoc5 = ((0x043a9d08LL * zoo1) >> 30) + + ((-0x02154febLL * zoo2) >> 30) + ((0x00670dbdLL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#elif defined(Z_COEFF_INTERP_OPT2X) + int32_t zoz, zoe1, zoe2, zoe3, zoo1, zoo2, zoo3; + int32_t zoc0, zoc1, zoc2, zoc3, zoc4, zoc5; + + /* 6-point, 5th-order Optimal 2x */ + zoz = z - (Z_ONE >> 1); + zoe1 = z_coeff[1] + z_coeff[0]; + zoe2 = z_coeff[2] + z_coeff[-1]; + zoe3 = z_coeff[3] + z_coeff[-2]; + zoo1 = z_coeff[1] - z_coeff[0]; + zoo2 = z_coeff[2] - z_coeff[-1]; + zoo3 = z_coeff[3] - z_coeff[-2]; + + zoc0 = (((0x19edb6fdLL * zoe1)) >> 30) + + ((0x05ebd062LL * zoe2) >> 30) + ((0x00267881LL * zoe3) >> 30); + zoc1 = ((0x1223af76LL * zoo1) >> 30) + + ((0x0de3dd6bLL * zoo2) >> 30) + ((0x00d683cdLL * zoo3) >> 30); + zoc2 = ((-0x0c3ee068LL * zoe1) >> 30) + + ((0x0a5c3769LL * zoe2) >> 30) + ((0x01e2aceaLL * zoe3) >> 30); + zoc3 = ((-0x0a8ab614LL * zoo1) >> 30) + + ((-0x0019522eLL * zoo2) >> 30) + ((0x022cefc7LL * zoo3) >> 30); + zoc4 = ((0x0276187dLL * zoe1) >> 30) + + ((-0x03a801e8LL * zoe2) >> 30) + ((0x0131d935LL * zoe3) >> 30); + zoc5 = ((0x02c373f5LL * zoo1) >> 30) + + ((-0x01275f83LL * zoo2) >> 30) + ((0x0018ee79LL * zoo3) >> 30); + + coeff = (((((((((((((((int64_t)zoc5 * zoz) >> Z_SHIFT) + + zoc4) * zoz) >> Z_SHIFT) + zoc3) * zoz) >> Z_SHIFT) + + zoc2) * zoz) >> Z_SHIFT) + zoc1) * zoz) >> Z_SHIFT) + zoc0; +#else +#error "Interpolation type screwed!" +#endif + +#if Z_POLYPHASE_COEFF_SHIFT > 0 + coeff = Z_RSHIFT(coeff, Z_POLYPHASE_COEFF_SHIFT); +#endif + return (coeff); +} + +static int +z_resampler_build_polyphase(struct z_info *info) +{ + int32_t alpha, c, i, z, idx; + + if (info->z_pcoeff != NULL) { + free(info->z_pcoeff, M_DEVBUF); + info->z_pcoeff = NULL; + } + + if (((int64_t)info->z_size * info->z_gy * 2) > + feeder_rate_polyphase_max) { +#ifndef _KERNEL + fprintf(stderr, "Polyphase entries exceed: [%d/%d] %jd > %d\n", + info->z_gx, info->z_gy, + (intmax_t)info->z_size * info->z_gy * 2, + feeder_rate_polyphase_max); +#endif + return (0); + } + + info->z_pcoeff = malloc(sizeof(int32_t) * + info->z_size * info->z_gy * 2, M_DEVBUF, M_NOWAIT | M_ZERO); + if (info->z_pcoeff == NULL) + return (0); + + for (alpha = 0; alpha < info->z_gy; alpha++) { + z = alpha * info->z_dx; + c = 0; + for (i = info->z_size; i != 0; i--) { + c += z >> Z_SHIFT; + z &= Z_MASK; + idx = (alpha * info->z_size * 2) + + (info->z_size * 2) - i; + info->z_pcoeff[idx] = + z_coeff_interpolate(z, info->z_coeff + c); + z += info->z_dy; + } + z = info->z_dy - (alpha * info->z_dx); + c = 0; + for (i = info->z_size; i != 0; i--) { + c += z >> Z_SHIFT; + z &= Z_MASK; + idx = (alpha * info->z_size * 2) + i - 1; + info->z_pcoeff[idx] = + z_coeff_interpolate(z, info->z_coeff + c); + z += info->z_dy; + } + } + +#ifndef _KERNEL + fprintf(stderr, "Polyphase: [%d/%d] %d entries\n", + info->z_gx, info->z_gy, info->z_size * info->z_gy * 2); +#endif + + return (1); +} + +static int +z_resampler_setup(struct pcm_feeder *f) +{ + struct z_info *info; + int64_t gy2gx_max, gx2gy_max; + uint32_t format; + int32_t align, i, z_scale; + int adaptive; + + info = f->data; + z_resampler_reset(info); + + if (info->src == info->dst) + return (0); + + /* Shrink by greatest common divisor. */ + i = z_gcd(info->src, info->dst); + info->z_gx = info->src / i; + info->z_gy = info->dst / i; + + /* Too big, or too small. Bail out. */ + if (!(Z_FACTOR_SAFE(info->z_gx) && Z_FACTOR_SAFE(info->z_gy))) + return (EINVAL); + + format = f->desc->in; + adaptive = 0; + z_scale = 0; - for (i = 0; i < sizeof(convtbl) / sizeof(convtbl[0]); i++) { - if (convtbl[i].format == 0) - return (-1); - if ((f->desc->out & ~AFMT_STEREO) == convtbl[i].format) { - info->bps = convtbl[i].bps; - info->convert = convtbl[i].convert; + /* + * Setup everything: filter length, conversion factor, etc. + */ + if (Z_IS_SINC(info)) { + /* + * Downsampling, or upsampling scaling factor. As long as the + * factor can be represented by a fraction of 1 << Z_SHIFT, + * we're pretty much in business. Scaling is not needed for + * upsampling, so we just slap Z_ONE there. + */ + if (info->z_gx > info->z_gy) + /* + * If the downsampling ratio is beyond sanity, + * enable semi-adaptive mode. Although handling + * extreme ratio is possible, the result of the + * conversion is just pointless, unworthy, + * nonsensical noises, etc. + */ + if ((info->z_gx / info->z_gy) > Z_SINC_DOWNMAX) + z_scale = Z_ONE / Z_SINC_DOWNMAX; + else + z_scale = ((uint64_t)info->z_gy << Z_SHIFT) / + info->z_gx; + else + z_scale = Z_ONE; + + /* + * This is actually impossible, unless anything above + * overflow. + */ + if (z_scale < 1) + return (E2BIG); + + /* + * Calculate sample time/coefficients index drift. It is + * a constant for upsampling, but downsampling require + * heavy duty filtering with possible too long filters. + * If anything goes wrong, revisit again and enable + * adaptive mode. + */ +z_setup_adaptive_sinc: + if (info->z_pcoeff != NULL) { + free(info->z_pcoeff, M_DEVBUF); + info->z_pcoeff = NULL; + } + + if (adaptive == 0) { + info->z_dy = z_scale << Z_DRIFT_SHIFT; + if (info->z_dy < 1) + return (E2BIG); + info->z_scale = z_scale; + } else { + info->z_dy = Z_FULL_ONE; + info->z_scale = Z_ONE; + } + +#if 0 +#define Z_SCALE_DIV 10000 +#define Z_SCALE_LIMIT(s, v) \ + ((((uint64_t)(s) * (v)) + (Z_SCALE_DIV >> 1)) / Z_SCALE_DIV) + + info->z_scale = Z_SCALE_LIMIT(info->z_scale, 9780); +#endif + + /* Smallest drift increment. */ + info->z_dx = info->z_dy / info->z_gy; + + /* + * Overflow or underflow. Try adaptive, let it continue and + * retry. + */ + if (info->z_dx < 1) { + if (adaptive == 0) { + adaptive = 1; + goto z_setup_adaptive_sinc; + } + return (E2BIG); + } + + /* + * Round back output drift. + */ + info->z_dy = info->z_dx * info->z_gy; + + for (i = 0; i < Z_COEFF_TAB_SIZE; i++) { + if (Z_SINC_COEFF_IDX(info) != i) + continue; + /* + * Calculate required filter length and guard + * against possible abusive result. Note that + * this represents only 1/2 of the entire filter + * length. + */ + info->z_size = z_resampler_sinc_len(info); + + /* + * Multiple of 2 rounding, for better accumulator + * performance. + */ + info->z_size &= ~1; + + if (info->z_size < 2 || info->z_size > Z_SINC_MAX) { + if (adaptive == 0) { + adaptive = 1; + goto z_setup_adaptive_sinc; + } + return (E2BIG); + } + info->z_coeff = z_coeff_tab[i].coeff + Z_COEFF_OFFSET; + info->z_dcoeff = z_coeff_tab[i].dcoeff; break; } + + if (info->z_coeff == NULL || info->z_dcoeff == NULL) + return (EINVAL); + } else if (Z_IS_LINEAR(info)) { + /* + * Don't put much effort if we're doing linear interpolation. + * Just center the interpolation distance within Z_LINEAR_ONE, + * and be happy about it. + */ + info->z_dx = Z_LINEAR_FULL_ONE / info->z_gy; } /* - * No need to interpolate/decimate, just do plain copy. + * We're safe for now, lets continue.. Look for our resampler + * depending on configured format and quality. + */ + for (i = 0; i < Z_RESAMPLER_TAB_SIZE; i++) { + int ridx; + + if (AFMT_ENCODING(format) != z_resampler_tab[i].format) + continue; + if (Z_IS_SINC(info) && adaptive == 0 && + feeder_rate_polyphase_max > 0 && + z_resampler_build_polyphase(info) != 0) + ridx = Z_RESAMPLER_SINC_POLYPHASE; + else + ridx = Z_RESAMPLER_IDX(info); + info->z_resample = z_resampler_tab[i].resampler[ridx]; + break; + } + + if (info->z_resample == NULL) + return (EINVAL); + + info->bps = AFMT_BPS(format); + align = info->channels * info->bps; + + /* + * Calculate largest value that can be fed into z_gy2gx() and + * z_gx2gy() without causing (signed) 32bit overflow. z_gy2gx() will + * be called early during feeding process to determine how much input + * samples that is required to generate requested output, while + * z_gx2gy() will be called just before samples filtering / + * accumulation process based on available samples that has been + * calculated using z_gx2gy(). + * + * Now that is damn confusing, I guess ;-) . */ - if (info->gx == info->gy) - info->convert = NULL; + gy2gx_max = (((uint64_t)info->z_gy * INT32_MAX) - info->z_gy + 1) / + info->z_gx; - info->channels = (f->desc->out & AFMT_STEREO) ? 2 : 1; - info->pos = info->bps * info->channels; - info->bpos = info->pos << 1; - info->bufsz -= info->bufsz % info->pos; - - memset(info->buffer, sndbuf_zerodata(f->desc->out), info->bpos); - - RATE_TRACE("%s: %u (%u) -> %u (%u) [%u/%u] , " - "format=0x%08x, channels=%u, bufsz=%u\n", - __func__, info->src, info->rsrc, info->dst, info->rdst, - info->gx, info->gy, f->desc->out, info->channels, - info->bufsz - info->pos); + if ((gy2gx_max * align) > SND_FXDIV_MAX) + gy2gx_max = SND_FXDIV_MAX / align; + + if (gy2gx_max < 1) + return (E2BIG); + + gx2gy_max = (((uint64_t)info->z_gx * INT32_MAX) - info->z_gy) / + info->z_gy; + + if (gx2gy_max > INT32_MAX) + gx2gy_max = INT32_MAX; + + if (gx2gy_max < 1) + return (E2BIG); + + /* + * Ensure that z_gy2gx() at its largest possible calculated value + * (alpha = 0) will not cause overflow further late during z_gx2gy() + * stage. + */ + if (z_gy2gx(info, gy2gx_max) > _Z_GCAST(gx2gy_max)) + return (E2BIG); + + info->z_maxfeed = gy2gx_max * align; + +#ifdef Z_USE_ALPHADRIFT + info->z_startdrift = z_gy2gx(info, 1); + info->z_alphadrift = z_drift(info, info->z_startdrift, 1); +#endif + + i = z_gy2gx(info, 1); + info->z_full = z_roundpow2((info->z_size << 1) + i); + + /* + * Too big to be true, and overflowing left and right like mad .. + */ + if ((info->z_full * align) < 1) { + if (adaptive == 0 && Z_IS_SINC(info)) { + adaptive = 1; + goto z_setup_adaptive_sinc; + } + return (E2BIG); + } + + /* + * Increase full buffer size if its too small to reduce cyclic + * buffer shifting in main conversion/feeder loop. + */ + while (info->z_full < Z_RESERVOIR_MAX && + (info->z_full - (info->z_size << 1)) < Z_RESERVOIR) + info->z_full <<= 1; + + /* Initialize buffer position. */ + info->z_mask = info->z_full - 1; + info->z_start = z_prev(info, info->z_size << 1, 1); + info->z_pos = z_next(info, info->z_start, 1); + + /* + * Allocate or reuse delay line buffer, whichever makes sense. + */ + i = info->z_full * align; + if (i < 1) + return (E2BIG); + + if (info->z_delay == NULL || info->z_alloc < i || + i <= (info->z_alloc >> 1)) { + if (info->z_delay != NULL) + free(info->z_delay, M_DEVBUF); + info->z_delay = malloc(i, M_DEVBUF, M_NOWAIT | M_ZERO); + if (info->z_delay == NULL) + return (ENOMEM); + info->z_alloc = i; + } + + /* + * Zero out head of buffer to avoid pops and clicks. + */ + memset(info->z_delay, sndbuf_zerodata(f->desc->out), + info->z_pos * align); + +#ifdef Z_DIAGNOSTIC + /* + * XXX Debuging mess !@#$%^ + */ +#define dumpz(x) fprintf(stderr, "\t%12s = %10u : %-11d\n", \ + "z_"__STRING(x), (uint32_t)info->z_##x, \ + (int32_t)info->z_##x) + fprintf(stderr, "\n%s():\n", __func__); + fprintf(stderr, "\tchannels=%d, bps=%d, format=0x%08x, quality=%d\n", + info->channels, info->bps, format, info->quality); + fprintf(stderr, "\t%d (%d) -> %d (%d), ", + info->src, info->rsrc, info->dst, info->rdst); + fprintf(stderr, "[%d/%d]\n", info->z_gx, info->z_gy); + fprintf(stderr, "\tminreq=%d, ", z_gy2gx(info, 1)); + if (adaptive != 0) + z_scale = Z_ONE; + fprintf(stderr, "factor=0x%08x/0x%08x (%f)\n", + z_scale, Z_ONE, (double)z_scale / Z_ONE); + fprintf(stderr, "\tbase_length=%d, ", Z_SINC_BASE_LEN(info)); + fprintf(stderr, "adaptive=%s\n", (adaptive != 0) ? "YES" : "NO"); + dumpz(size); + dumpz(alloc); + if (info->z_alloc < 1024) + fprintf(stderr, "\t%15s%10d Bytes\n", + "", info->z_alloc); + else if (info->z_alloc < (1024 << 10)) + fprintf(stderr, "\t%15s%10d KBytes\n", + "", info->z_alloc >> 10); + else if (info->z_alloc < (1024 << 20)) + fprintf(stderr, "\t%15s%10d MBytes\n", + "", info->z_alloc >> 20); + else + fprintf(stderr, "\t%15s%10d GBytes\n", + "", info->z_alloc >> 30); + fprintf(stderr, "\t%12s %10d (min output samples)\n", + "", + (int32_t)z_gx2gy(info, info->z_full - (info->z_size << 1))); + fprintf(stderr, "\t%12s %10d (min allocated output samples)\n", + "", + (int32_t)z_gx2gy(info, (info->z_alloc / align) - + (info->z_size << 1))); + fprintf(stderr, "\t%12s = %10d\n", + "z_gy2gx()", (int32_t)z_gy2gx(info, 1)); + fprintf(stderr, "\t%12s = %10d -> z_gy2gx() -> %d\n", + "Max", (int32_t)gy2gx_max, (int32_t)z_gy2gx(info, gy2gx_max)); + fprintf(stderr, "\t%12s = %10d\n", + "z_gx2gy()", (int32_t)z_gx2gy(info, 1)); + fprintf(stderr, "\t%12s = %10d -> z_gx2gy() -> %d\n", + "Max", (int32_t)gx2gy_max, (int32_t)z_gx2gy(info, gx2gy_max)); + dumpz(maxfeed); + dumpz(full); + dumpz(start); + dumpz(pos); + dumpz(scale); + fprintf(stderr, "\t%12s %10f\n", "", + (double)info->z_scale / Z_ONE); + dumpz(dx); + fprintf(stderr, "\t%12s %10f\n", "", + (double)info->z_dx / info->z_dy); + dumpz(dy); + fprintf(stderr, "\t%12s %10d (drift step)\n", "", + info->z_dy >> Z_SHIFT); + fprintf(stderr, "\t%12s %10d (scaling differences)\n", "", + (z_scale << Z_DRIFT_SHIFT) - info->z_dy); + fprintf(stderr, "\t%12s = %u bytes\n", + "intpcm32_t", sizeof(intpcm32_t)); + fprintf(stderr, "\t%12s = 0x%08x, smallest=%.16lf\n", + "Z_ONE", Z_ONE, (double)1.0 / (double)Z_ONE); +#endif return (0); } static int -feed_rate_set(struct pcm_feeder *f, int what, int32_t value) +z_resampler_set(struct pcm_feeder *f, int what, int32_t value) { - struct feed_rate_info *info = f->data; + struct z_info *info; + int32_t oquality; - if (value < feeder_rate_min || value > feeder_rate_max) - return (-1); + info = f->data; switch (what) { - case FEEDRATE_SRC: + case Z_RATE_SRC: + if (value < feeder_rate_min || value > feeder_rate_max) + return (E2BIG); + if (value == info->rsrc) + return (0); info->rsrc = value; break; - case FEEDRATE_DST: + case Z_RATE_DST: + if (value < feeder_rate_min || value > feeder_rate_max) + return (E2BIG); + if (value == info->rdst) + return (0); info->rdst = value; break; + case Z_RATE_QUALITY: + if (value < Z_QUALITY_MIN || value > Z_QUALITY_MAX) + return (EINVAL); + if (value == info->quality) + return (0); + /* + * If we failed to set the requested quality, restore + * the old one. We cannot afford leaving it broken since + * passive feeder chains like vchans never reinitialize + * itself. + */ + oquality = info->quality; + info->quality = value; + if (z_resampler_setup(f) == 0) + return (0); + info->quality = oquality; + break; + case Z_RATE_CHANNELS: + if (value < SND_CHN_MIN || value > SND_CHN_MAX) + return (EINVAL); + if (value == info->channels) + return (0); + info->channels = value; + break; default: - return (-1); + return (EINVAL); + break; } - return (feed_rate_setup(f)); + + return (z_resampler_setup(f)); } static int -feed_rate_get(struct pcm_feeder *f, int what) +z_resampler_get(struct pcm_feeder *f, int what) { - struct feed_rate_info *info = f->data; + struct z_info *info; + + info = f->data; switch (what) { - case FEEDRATE_SRC: + case Z_RATE_SRC: return (info->rsrc); - case FEEDRATE_DST: + break; + case Z_RATE_DST: return (info->rdst); + break; + case Z_RATE_QUALITY: + return (info->quality); + break; + case Z_RATE_CHANNELS: + return (info->channels); + break; default: - return (-1); + break; } + return (-1); } static int -feed_rate_init(struct pcm_feeder *f) +z_resampler_init(struct pcm_feeder *f) { - struct feed_rate_info *info; + struct z_info *info; + int ret; - if (f->desc->out != f->desc->in) + if (f->desc->in != f->desc->out) return (EINVAL); - info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO); + info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO); if (info == NULL) return (ENOMEM); - /* - * bufsz = sample from last cycle + conversion space - */ - info->bufsz_init = 8 + feeder_buffersize; - info->buffer = malloc(info->bufsz_init, M_RATEFEEDER, - M_NOWAIT | M_ZERO); - if (info->buffer == NULL) { - free(info, M_RATEFEEDER); - return (ENOMEM); - } - info->rsrc = DSP_DEFAULT_SPEED; - info->rdst = DSP_DEFAULT_SPEED; + + info->rsrc = Z_RATE_DEFAULT; + info->rdst = Z_RATE_DEFAULT; + info->quality = feeder_rate_quality; + info->channels = AFMT_CHANNEL(f->desc->in); + f->data = info; - return (feed_rate_setup(f)); + + ret = z_resampler_setup(f); + if (ret != 0) { + if (info->z_pcoeff != NULL) + free(info->z_pcoeff, M_DEVBUF); + if (info->z_delay != NULL) + free(info->z_delay, M_DEVBUF); + free(info, M_DEVBUF); + f->data = NULL; + } + + return (ret); } static int -feed_rate_free(struct pcm_feeder *f) +z_resampler_free(struct pcm_feeder *f) { - struct feed_rate_info *info = f->data; + struct z_info *info; + info = f->data; if (info != NULL) { - if (info->buffer != NULL) - free(info->buffer, M_RATEFEEDER); - free(info, M_RATEFEEDER); + if (info->z_pcoeff != NULL) + free(info->z_pcoeff, M_DEVBUF); + if (info->z_delay != NULL) + free(info->z_delay, M_DEVBUF); + free(info, M_DEVBUF); } + f->data = NULL; + return (0); } -static int -feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) +static uint32_t +z_resampler_feed_internal(struct pcm_feeder *f, struct pcm_channel *c, + uint8_t *b, uint32_t count, void *source) { - struct feed_rate_info *info = f->data; - uint32_t i, smpsz; - int32_t fetch, slot; - - if (info->convert == NULL) - return (FEEDER_FEED(f->source, c, b, count, source)); - - /* - * This loop has been optimized to generalize both up / down - * sampling without causing missing samples or excessive buffer - * feeding. The tricky part is to calculate *precise* (slot) value - * needed for the entire conversion space since we are bound to - * return and fill up the buffer according to the requested 'count'. - * Too much feeding will cause the extra buffer stay within temporary - * circular buffer forever and always manifest itself as a truncated - * sound during end of playback / recording. Too few, and we end up - * with possible underruns and waste of cpu cycles. - * - * 'Stray' management exist to combat with possible unaligned - * buffering by the caller. + struct z_info *info; + int32_t alphadrift, startdrift, reqout, ocount, reqin, align; + int32_t fetch, fetched, start, cp; + uint8_t *dst; + + info = f->data; + if (info->z_resample == NULL) + return (z_feed(f->source, c, b, count, source)); + + /* + * Calculate sample size alignment and amount of sample output. + * We will do everything in sample domain, but at the end we + * will jump back to byte domain. */ - smpsz = info->bps * info->channels; - RATE_TEST(count >= smpsz && (count % smpsz) == 0, - ("%s: Count size not sample integral (%d)\n", __func__, count)); - if (count < smpsz) + align = info->channels * info->bps; + ocount = SND_FXDIV(count, align); + if (ocount == 0) return (0); - count -= count % smpsz; + /* - * This slot count formula will stay here for the next million years - * to come. This is the key of our circular buffering precision. + * Calculate amount of input samples that is needed to generate + * exact amount of output. */ - slot = (((info->gx * (count / smpsz)) + info->gy - info->alpha - 1) / - info->gy) * smpsz; - RATE_TEST((slot % smpsz) == 0, - ("%s: Slot count not sample integral (%d)\n", __func__, slot)); -#ifdef FEEDRATE_STRAY - RATE_TEST(info->stray == 0, ("%s: [1] Stray bytes: %u\n", __func__, - info->stray)); + reqin = z_gy2gx(info, ocount) - z_fetched(info); + +#ifdef Z_USE_ALPHADRIFT + startdrift = info->z_startdrift; + alphadrift = info->z_alphadrift; +#else + startdrift = _Z_GY2GX(info, 0, 1); + alphadrift = z_drift(info, startdrift, 1); #endif - if (info->pos != smpsz && info->bpos - info->pos == smpsz && - info->bpos + slot > info->bufsz) { - /* - * Copy last unit sample and its previous to - * beginning of buffer. - */ - bcopy(info->buffer + info->pos - smpsz, info->buffer, - smpsz << 1); - info->pos = smpsz; - info->bpos = smpsz << 1; - } - RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", __func__, slot)); - i = 0; - for (;;) { - for (;;) { - fetch = info->bufsz - info->bpos; -#ifdef FEEDRATE_STRAY - fetch -= info->stray; -#endif - RATE_ASSERT(fetch >= 0, - ("%s: [1] Buffer overrun: %d > %d\n", __func__, - info->bpos, info->bufsz)); - if (slot < fetch) - fetch = slot; -#ifdef FEEDRATE_STRAY - if (fetch < 1) -#else - if (fetch < smpsz) -#endif - break; - RATE_ASSERT((int)(info->bpos -#ifdef FEEDRATE_STRAY - - info->stray -#endif - ) >= 0 && - (info->bpos - info->stray) < info->bufsz, - ("%s: DANGER - BUFFER OVERRUN! bufsz=%d, pos=%d\n", - __func__, info->bufsz, info->bpos -#ifdef FEEDRATE_STRAY - - info->stray -#endif - )); - fetch = FEEDER_FEED(f->source, c, - info->buffer + info->bpos -#ifdef FEEDRATE_STRAY - - info->stray -#endif - , fetch, source); -#ifdef FEEDRATE_STRAY - info->stray = 0; - if (fetch == 0) -#else - if (fetch < smpsz) -#endif - break; - RATE_TEST((fetch % smpsz) == 0, - ("%s: Fetch size not sample integral (%d)\n", - __func__, fetch)); -#ifdef FEEDRATE_STRAY - info->stray += fetch % smpsz; - RATE_TEST(info->stray == 0, - ("%s: Stray bytes detected (%d)\n", __func__, - info->stray)); -#endif - fetch -= fetch % smpsz; - info->bpos += fetch; - slot -= fetch; - RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", - __func__, slot)); - if (slot == 0 || info->bpos == info->bufsz) - break; - } - if (info->pos == info->bpos) { - RATE_TEST(info->pos == smpsz, - ("%s: EOF while in progress\n", __func__)); - break; + + dst = b; + + do { + if (reqin != 0) { + fetch = z_min(z_free(info), reqin); + if (fetch == 0) { + /* + * No more free spaces, so wind enough + * samples back to the head of delay line + * in byte domain. + */ + fetched = z_fetched(info); + start = z_prev(info, info->z_start, + (info->z_size << 1) - 1); + cp = (info->z_size << 1) + fetched; + z_copy(info->z_delay + (start * align), + info->z_delay, cp * align); + info->z_start = + z_prev(info, info->z_size << 1, 1); + info->z_pos = + z_next(info, info->z_start, fetched + 1); + fetch = z_min(z_free(info), reqin); +#ifdef Z_DIAGNOSTIC + if (1) { + static uint32_t kk = 0; + fprintf(stderr, + "Buffer Move: " + "start=%d fetched=%d cp=%d " + "cycle=%u [%u]\r", + start, fetched, cp, info->z_cycle, + ++kk); + } + info->z_cycle = 0; +#endif + } + if (fetch != 0) { + /* + * Fetch in byte domain and jump back + * to sample domain. + */ + fetched = SND_FXDIV(z_feed(f->source, c, + info->z_delay + (info->z_pos * align), + fetch * align, source), align); + /* + * Prepare to convert fetched buffer, + * or mark us done if we cannot fulfill + * the request. + */ + reqin -= fetched; + info->z_pos += fetched; + if (fetched != fetch) + reqin = 0; + } } - RATE_ASSERT(info->pos <= info->bpos, - ("%s: [2] Buffer overrun: %d > %d\n", __func__, info->pos, - info->bpos)); - RATE_ASSERT(info->pos < info->bpos, - ("%s: Zero buffer!\n", __func__)); - RATE_ASSERT(((info->bpos - info->pos) % smpsz) == 0, - ("%s: Buffer not sample integral (%d)\n", __func__, - info->bpos - info->pos)); - i += info->convert(info, b + i, count - i); - RATE_ASSERT(info->pos <= info->bpos, - ("%s: [3] Buffer overrun: %d > %d\n", __func__, info->pos, - info->bpos)); - if (info->pos == info->bpos) { + + reqout = z_min(z_gx2gy(info, z_fetched(info)), ocount); + if (reqout != 0) { + ocount -= reqout; + /* - * End of buffer cycle. Copy last unit sample - * to beginning of buffer so next cycle can - * interpolate using it. + * Drift.. drift.. drift.. + * + * Notice that there are 2 methods of doing the drift + * operations: The former is much cleaner (in a sense + * sense of mathematical readings), but slower due to + * integer division in z_gy2gx(). Nevertheless, both + * should give the same exact accurate drifting + * results, so the later is favourable. */ -#ifdef FEEDRATE_STRAY - RATE_TEST(info->stray == 0, - ("%s: [2] Stray bytes: %u\n", __func__, - info->stray)); -#endif - bcopy(info->buffer + info->pos - smpsz, info->buffer, - smpsz); - info->bpos = smpsz; - info->pos = smpsz; + do { + info->z_resample(info, dst); +#if 0 + startdrift = z_gy2gx(info, 1); + alphadrift = z_drift(info, startdrift, 1); + info->z_start += startdrift; + info->z_alpha += alphadrift; +#else + info->z_alpha += alphadrift; + if (info->z_alpha < info->z_gy) + info->z_start += startdrift; + else { + info->z_start += startdrift - 1; + info->z_alpha -= info->z_gy; + } +#endif + dst += align; +#ifdef Z_DIAGNOSTIC + info->z_cycle++; +#endif + } while (--reqout != 0); } - if (i == count) - break; - } + } while (reqin != 0 && ocount != 0); - RATE_TEST((slot == 0 && count == i) || (slot > 0 && count > i && - info->pos == info->bpos && info->pos == smpsz), - ("%s: Inconsistent slot/count! " - "Count Expect: %u , Got: %u, Slot Left: %d\n", __func__, count, i, - slot)); - -#ifdef FEEDRATE_STRAY - RATE_TEST(info->stray == 0, ("%s: [3] Stray bytes: %u\n", __func__, - info->stray)); -#endif + /* + * Back to byte domain.. + */ + return (dst - b); +} - return (i); +static int +z_resampler_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + uint32_t feed, maxfeed, left; + + /* + * Split count to smaller chunks to avoid possible 32bit overflow. + */ + maxfeed = ((struct z_info *)(f->data))->z_maxfeed; + left = count; + + do { + feed = z_resampler_feed_internal(f, c, b, + z_min(maxfeed, left), source); + b += feed; + left -= feed; + } while (left != 0 && feed != 0); + + return (count - left); } static struct pcm_feederdesc feeder_rate_desc[] = { - {FEEDER_RATE, AFMT_S8, AFMT_S8, 0}, - {FEEDER_RATE, AFMT_S16_LE, AFMT_S16_LE, 0}, - {FEEDER_RATE, AFMT_S24_LE, AFMT_S24_LE, 0}, - {FEEDER_RATE, AFMT_S32_LE, AFMT_S32_LE, 0}, - {FEEDER_RATE, AFMT_S16_BE, AFMT_S16_BE, 0}, - {FEEDER_RATE, AFMT_S24_BE, AFMT_S24_BE, 0}, - {FEEDER_RATE, AFMT_S32_BE, AFMT_S32_BE, 0}, - {FEEDER_RATE, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U8, AFMT_U8, 0}, - {FEEDER_RATE, AFMT_U16_LE, AFMT_U16_LE, 0}, - {FEEDER_RATE, AFMT_U24_LE, AFMT_U24_LE, 0}, - {FEEDER_RATE, AFMT_U32_LE, AFMT_U32_LE, 0}, - {FEEDER_RATE, AFMT_U16_BE, AFMT_U16_BE, 0}, - {FEEDER_RATE, AFMT_U24_BE, AFMT_U24_BE, 0}, - {FEEDER_RATE, AFMT_U32_BE, AFMT_U32_BE, 0}, - {FEEDER_RATE, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_RATE, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, + { FEEDER_RATE, 0, 0, 0 }, + { 0, 0, 0, 0 }, }; static kobj_method_t feeder_rate_methods[] = { - KOBJMETHOD(feeder_init, feed_rate_init), - KOBJMETHOD(feeder_free, feed_rate_free), - KOBJMETHOD(feeder_set, feed_rate_set), - KOBJMETHOD(feeder_get, feed_rate_get), - KOBJMETHOD(feeder_feed, feed_rate), - {0, 0} + KOBJMETHOD(feeder_init, z_resampler_init), + KOBJMETHOD(feeder_free, z_resampler_free), + KOBJMETHOD(feeder_set, z_resampler_set), + KOBJMETHOD(feeder_get, z_resampler_get), + KOBJMETHOD(feeder_feed, z_resampler_feed), + { 0, 0 } }; -FEEDER_DECLARE(feeder_rate, 2, NULL); +FEEDER_DECLARE(feeder_rate, 0, NULL); --- sys/dev/sound/pcm/feeder_volume.c.orig 2007-06-17 04:36:39.000000000 +0800 +++ sys/dev/sound/pcm/feeder_volume.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,5 @@ /*- - * Copyright (c) 2005 Ariff Abdullah + * Copyright (c) 2005-2009 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,159 +26,293 @@ /* feeder_volume, a long 'Lost Technology' rather than a new feature. */ +#ifdef _KERNEL #include +#include #include "feeder_if.h" +#define SND_USE_FXDIV +#include "snd_fxdiv_gen.h" + SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_volume.c,v 1.6 2007/06/16 20:36:39 ariff Exp $"); +#endif + +typedef void (*feed_volume_t)(int *, int *, uint32_t, uint8_t *, uint32_t); + +#define FEEDVOLUME_CALC8(s, v) (SND_VOL_CALC_SAMPLE((intpcm_t) \ + (s) << 8, v) >> 8) +#define FEEDVOLUME_CALC16(s, v) SND_VOL_CALC_SAMPLE((intpcm_t)(s), v) +#define FEEDVOLUME_CALC24(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) +#define FEEDVOLUME_CALC32(s, v) SND_VOL_CALC_SAMPLE((intpcm64_t)(s), v) + +#define FEEDVOLUME_DECLARE(SIGN, BIT, ENDIAN) \ +static void \ +feed_volume_##SIGN##BIT##ENDIAN(int *vol, int *matrix, \ + uint32_t channels, uint8_t *dst, uint32_t count) \ +{ \ + intpcm##BIT##_t v; \ + intpcm_t x; \ + uint32_t i; \ + \ + dst += count * PCM_##BIT##_BPS * channels; \ + do { \ + i = channels; \ + do { \ + dst -= PCM_##BIT##_BPS; \ + i--; \ + x = PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ + v = FEEDVOLUME_CALC##BIT(x, vol[matrix[i]]); \ + x = PCM_CLAMP_##SIGN##BIT(v); \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x); \ + } while (i != 0); \ + } while (--count != 0); \ +} -#define FVOL_OSS_SCALE 100 -#define FVOL_RESOLUTION PCM_FXSHIFT -#define FVOL_CLAMP(val) (((val) << FVOL_RESOLUTION) / FVOL_OSS_SCALE) -#define FVOL_LEFT(val) FVOL_CLAMP((val) & 0x7f) -#define FVOL_RIGHT(val) FVOL_LEFT((val) >> 8) -#define FVOL_MAX (1 << FVOL_RESOLUTION) -#define FVOL_CALC(sval, vval) (((sval) * (vval)) >> FVOL_RESOLUTION) - -typedef uint32_t (*feed_volume_filter)(uint8_t *, int *, uint32_t); - -#define FEEDER_VOLUME_FILTER(FMTBIT, VOL_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ -static uint32_t \ -feed_volume_filter_##SIGNS##FMTBIT##ENDIANS(uint8_t *b, int *vol, \ - uint32_t count) \ -{ \ - int32_t j; \ - int i; \ - \ - i = count; \ - b += i; \ - \ - do { \ - b -= PCM_##FMTBIT##_BPS; \ - i -= PCM_##FMTBIT##_BPS; \ - j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(b); \ - j = FVOL_CALC((VOL_INTCAST)j, \ - vol[(i / PCM_##FMTBIT##_BPS) & 1]); \ - PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(b, j); \ - } while (i != 0); \ - \ - return (count); \ -} - -FEEDER_VOLUME_FILTER(8, int32_t, S, s, NE, ne) -FEEDER_VOLUME_FILTER(16, int32_t, S, s, LE, le) -FEEDER_VOLUME_FILTER(24, int32_t, S, s, LE, le) -FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, LE, le) -FEEDER_VOLUME_FILTER(16, int32_t, S, s, BE, be) -FEEDER_VOLUME_FILTER(24, int32_t, S, s, BE, be) -FEEDER_VOLUME_FILTER(32, intpcm_t, S, s, BE, be) -FEEDER_VOLUME_FILTER(8, int32_t, U, u, NE, ne) -FEEDER_VOLUME_FILTER(16, int32_t, U, u, LE, le) -FEEDER_VOLUME_FILTER(24, int32_t, U, u, LE, le) -FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, LE, le) -FEEDER_VOLUME_FILTER(16, int32_t, U, u, BE, be) -FEEDER_VOLUME_FILTER(24, int32_t, U, u, BE, be) -FEEDER_VOLUME_FILTER(32, intpcm_t, U, u, BE, be) +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) +FEEDVOLUME_DECLARE(S, 16, LE) +FEEDVOLUME_DECLARE(S, 32, LE) +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) +FEEDVOLUME_DECLARE(S, 16, BE) +FEEDVOLUME_DECLARE(S, 32, BE) +#endif +#ifdef FEEDER_MULTIFORMAT +FEEDVOLUME_DECLARE(S, 8, NE) +FEEDVOLUME_DECLARE(S, 24, LE) +FEEDVOLUME_DECLARE(S, 24, BE) +FEEDVOLUME_DECLARE(U, 8, NE) +FEEDVOLUME_DECLARE(U, 16, LE) +FEEDVOLUME_DECLARE(U, 24, LE) +FEEDVOLUME_DECLARE(U, 32, LE) +FEEDVOLUME_DECLARE(U, 16, BE) +FEEDVOLUME_DECLARE(U, 24, BE) +FEEDVOLUME_DECLARE(U, 32, BE) +#endif struct feed_volume_info { - uint32_t format; - int bps; - feed_volume_filter filter; + uint32_t bps, channels; + feed_volume_t apply; + int volume_class; + int matrix[SND_CHN_MAX]; }; -static struct feed_volume_info feed_volume_tbl[] = { - { AFMT_S8, PCM_8_BPS, feed_volume_filter_s8ne }, - { AFMT_S16_LE, PCM_16_BPS, feed_volume_filter_s16le }, - { AFMT_S24_LE, PCM_24_BPS, feed_volume_filter_s24le }, - { AFMT_S32_LE, PCM_32_BPS, feed_volume_filter_s32le }, - { AFMT_S16_BE, PCM_16_BPS, feed_volume_filter_s16be }, - { AFMT_S24_BE, PCM_24_BPS, feed_volume_filter_s24be }, - { AFMT_S32_BE, PCM_32_BPS, feed_volume_filter_s32be }, - { AFMT_U8, PCM_8_BPS, feed_volume_filter_u8ne }, - { AFMT_U16_LE, PCM_16_BPS, feed_volume_filter_u16le }, - { AFMT_U24_LE, PCM_24_BPS, feed_volume_filter_u24le }, - { AFMT_U32_LE, PCM_32_BPS, feed_volume_filter_u32le }, - { AFMT_U16_BE, PCM_16_BPS, feed_volume_filter_u16be }, - { AFMT_U24_BE, PCM_24_BPS, feed_volume_filter_u24be }, - { AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be }, +#define FEEDVOLUME_ENTRY(SIGN, BIT, ENDIAN) \ + { \ + AFMT_##SIGN##BIT##_##ENDIAN, \ + feed_volume_##SIGN##BIT##ENDIAN \ + } + +static const struct { + uint32_t format; + feed_volume_t apply; +} feed_volume_info_tab[] = { +#if BYTE_ORDER == LITTLE_ENDIAN || defined(FEEDER_MULTIFORMAT) + FEEDVOLUME_ENTRY(S, 16, LE), + FEEDVOLUME_ENTRY(S, 32, LE), +#endif +#if BYTE_ORDER == BIG_ENDIAN || defined(FEEDER_MULTIFORMAT) + FEEDVOLUME_ENTRY(S, 16, BE), + FEEDVOLUME_ENTRY(S, 32, BE), +#endif +#ifdef FEEDER_MULTIFORMAT + FEEDVOLUME_ENTRY(S, 8, NE), + FEEDVOLUME_ENTRY(S, 24, LE), + FEEDVOLUME_ENTRY(S, 24, BE), + FEEDVOLUME_ENTRY(U, 8, NE), + FEEDVOLUME_ENTRY(U, 16, LE), + FEEDVOLUME_ENTRY(U, 24, LE), + FEEDVOLUME_ENTRY(U, 32, LE), + FEEDVOLUME_ENTRY(U, 16, BE), + FEEDVOLUME_ENTRY(U, 24, BE), + FEEDVOLUME_ENTRY(U, 32, BE) +#endif }; -#define FVOL_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf))) -#define FVOL_INFOIDX(m) (((m) >> 4) & 0x1f) -#define FVOL_CHANNELS(m) ((m) & 0xf) +#define FEEDVOLUME_TAB_SIZE ((int32_t) \ + (sizeof(feed_volume_info_tab) / \ + sizeof(feed_volume_info_tab[0]))) static int feed_volume_init(struct pcm_feeder *f) { - int i, channels; - - if (f->desc->in != f->desc->out) - return (EINVAL); + struct feed_volume_info *info; + struct pcmchan_matrix *m; + uint32_t i; + int ret; - /* For now, this is mandatory! */ - if (!(f->desc->out & AFMT_STEREO)) + if (f->desc->in != f->desc->out || + AFMT_CHANNEL(f->desc->in) > SND_CHN_MAX) return (EINVAL); - channels = 2; + for (i = 0; i < FEEDVOLUME_TAB_SIZE; i++) { + if (AFMT_ENCODING(f->desc->in) == + feed_volume_info_tab[i].format) { + info = malloc(sizeof(*info), M_DEVBUF, + M_NOWAIT | M_ZERO); + if (info == NULL) + return (ENOMEM); + + info->bps = AFMT_BPS(f->desc->in); + info->channels = AFMT_CHANNEL(f->desc->in); + info->apply = feed_volume_info_tab[i].apply; + info->volume_class = SND_VOL_C_PCM; + + f->data = info; + m = feeder_matrix_default_channel_map(info->channels); + if (m == NULL) { + free(info, M_DEVBUF); + return (EINVAL); + } + + ret = feeder_volume_apply_matrix(f, m); + if (ret != 0) + free(info, M_DEVBUF); - for (i = 0; i < sizeof(feed_volume_tbl) / sizeof(feed_volume_tbl[0]); - i++) { - if ((f->desc->out & ~AFMT_STEREO) == - feed_volume_tbl[i].format) { - f->data = (void *)FVOL_DATA(i, channels); - return (0); + return (ret); } } - return (-1); + return (EINVAL); +} + +static int +feed_volume_free(struct pcm_feeder *f) +{ + struct feed_volume_info *info; + + info = f->data; + if (info != NULL) + free(info, M_DEVBUF); + + f->data = NULL; + + return (0); } static int -feed_volume(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) +feed_volume_set(struct pcm_feeder *f, int what, int value) { struct feed_volume_info *info; - int vol[2]; - int k, smpsz; + struct pcmchan_matrix *m; + int ret; + + info = f->data; + ret = 0; + + switch (what) { + case FEEDVOLUME_CLASS: + if (value < SND_VOL_C_BEGIN || value > SND_VOL_C_END) + return (EINVAL); + info->volume_class = value; + break; + case FEEDVOLUME_CHANNELS: + if (value < SND_CHN_MIN || value > SND_CHN_MAX) + return (EINVAL); + m = feeder_matrix_default_channel_map(value); + if (m == NULL) + return (EINVAL); + ret = feeder_volume_apply_matrix(f, m); + break; + default: + return (EINVAL); + break; + } - vol[0] = FVOL_LEFT(c->volume); - vol[1] = FVOL_RIGHT(c->volume); + return (ret); +} - if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX) +static int +feed_volume_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, + uint32_t count, void *source) +{ + struct feed_volume_info *info; + uint32_t j, align; + int i, *vol, *matrix; + uint8_t *dst; + + /* + * Fetch filter data operation. + */ + info = f->data; + vol = c->volume[SND_VOL_C_VAL(info->volume_class)]; + matrix = info->matrix; + + /* + * First, let see if we really need to apply gain at all. + */ + j = 0; + i = info->channels; + do { + if (vol[matrix[--i]] != SND_VOL_FLAT) { + j = 1; + break; + } + } while (i != 0); + + /* Nope, just bypass entirely. */ + if (j == 0) return (FEEDER_FEED(f->source, c, b, count, source)); - info = &feed_volume_tbl[FVOL_INFOIDX((intptr_t)f->data)]; - smpsz = info->bps * FVOL_CHANNELS((intptr_t)f->data); - if (count < smpsz) - return (0); - - k = FEEDER_FEED(f->source, c, b, count - (count % smpsz), source); - if (k < smpsz) - return (0); + dst = b; + align = info->bps * info->channels; + + do { + if (count < align) + break; - k -= k % smpsz; - return (info->filter(b, vol, k)); + j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source), + align); + if (j == 0) + break; + + info->apply(vol, matrix, info->channels, dst, j); + + j *= align; + dst += j; + count -= j; + + } while (count != 0); + + return (dst - b); } static struct pcm_feederdesc feeder_volume_desc[] = { - {FEEDER_VOLUME, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_VOLUME, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, + { FEEDER_VOLUME, 0, 0, 0 }, + { 0, 0, 0, 0 } }; + static kobj_method_t feeder_volume_methods[] = { KOBJMETHOD(feeder_init, feed_volume_init), - KOBJMETHOD(feeder_feed, feed_volume), - {0, 0} + KOBJMETHOD(feeder_free, feed_volume_free), + KOBJMETHOD(feeder_set, feed_volume_set), + KOBJMETHOD(feeder_feed, feed_volume_feed), + { 0, 0 } }; -FEEDER_DECLARE(feeder_volume, 2, NULL); + +FEEDER_DECLARE(feeder_volume, 0, NULL); + +/* Extern */ +int +feeder_volume_apply_matrix(struct pcm_feeder *f, struct pcmchan_matrix *m) +{ + struct feed_volume_info *info; + uint32_t i; + + if (f == NULL || f->desc == NULL || f->desc->type != FEEDER_VOLUME || + f->data == NULL || m == NULL || m->channels < SND_CHN_MIN || + m->channels > SND_CHN_MAX) + return (EINVAL); + + info = f->data; + + for (i = 0; i < (sizeof(info->matrix) / sizeof(info->matrix[0])); i++) { + if (i < m->channels) + info->matrix[i] = m->map[i].type; + else + info->matrix[i] = SND_CHN_T_FL; + } + + info->channels = m->channels; + + return (0); +} --- sys/dev/sound/pcm/g711.h.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/g711.h 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,225 @@ +/*- + * Copyright (c) 2008-2009 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. + * + * $FreeBSD$ + */ + +#ifndef _SND_G711_H_ +#define _SND_G711_H_ + +#define G711_TABLE_SIZE 256 + +#define ULAW_TO_U8 { \ + 3, 7, 11, 15, 19, 23, 27, 31, \ + 35, 39, 43, 47, 51, 55, 59, 63, \ + 66, 68, 70, 72, 74, 76, 78, 80, \ + 82, 84, 86, 88, 90, 92, 94, 96, \ + 98, 99, 100, 101, 102, 103, 104, 105, \ + 106, 107, 108, 109, 110, 111, 112, 113, \ + 113, 114, 114, 115, 115, 116, 116, 117, \ + 117, 118, 118, 119, 119, 120, 120, 121, \ + 121, 121, 122, 122, 122, 122, 123, 123, \ + 123, 123, 124, 124, 124, 124, 125, 125, \ + 125, 125, 125, 125, 126, 126, 126, 126, \ + 126, 126, 126, 126, 127, 127, 127, 127, \ + 127, 127, 127, 127, 127, 127, 127, 127, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 253, 249, 245, 241, 237, 233, 229, 225, \ + 221, 217, 213, 209, 205, 201, 197, 193, \ + 190, 188, 186, 184, 182, 180, 178, 176, \ + 174, 172, 170, 168, 166, 164, 162, 160, \ + 158, 157, 156, 155, 154, 153, 152, 151, \ + 150, 149, 148, 147, 146, 145, 144, 143, \ + 143, 142, 142, 141, 141, 140, 140, 139, \ + 139, 138, 138, 137, 137, 136, 136, 135, \ + 135, 135, 134, 134, 134, 134, 133, 133, \ + 133, 133, 132, 132, 132, 132, 131, 131, \ + 131, 131, 131, 131, 130, 130, 130, 130, \ + 130, 130, 130, 130, 129, 129, 129, 129, \ + 129, 129, 129, 129, 129, 129, 129, 129, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + } + +#define ALAW_TO_U8 { \ + 108, 109, 106, 107, 112, 113, 110, 111, \ + 100, 101, 98, 99, 104, 105, 102, 103, \ + 118, 118, 117, 117, 120, 120, 119, 119, \ + 114, 114, 113, 113, 116, 116, 115, 115, \ + 43, 47, 35, 39, 59, 63, 51, 55, \ + 11, 15, 3, 7, 27, 31, 19, 23, \ + 86, 88, 82, 84, 94, 96, 90, 92, \ + 70, 72, 66, 68, 78, 80, 74, 76, \ + 127, 127, 127, 127, 127, 127, 127, 127, \ + 127, 127, 127, 127, 127, 127, 127, 127, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 123, 123, 123, 123, 124, 124, 124, 124, \ + 121, 121, 121, 121, 122, 122, 122, 122, \ + 126, 126, 126, 126, 126, 126, 126, 126, \ + 125, 125, 125, 125, 125, 125, 125, 125, \ + 148, 147, 150, 149, 144, 143, 146, 145, \ + 156, 155, 158, 157, 152, 151, 154, 153, \ + 138, 138, 139, 139, 136, 136, 137, 137, \ + 142, 142, 143, 143, 140, 140, 141, 141, \ + 213, 209, 221, 217, 197, 193, 205, 201, \ + 245, 241, 253, 249, 229, 225, 237, 233, \ + 170, 168, 174, 172, 162, 160, 166, 164, \ + 186, 184, 190, 188, 178, 176, 182, 180, \ + 129, 129, 129, 129, 129, 129, 129, 129, \ + 129, 129, 129, 129, 129, 129, 129, 129, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 128, 128, 128, 128, 128, 128, 128, 128, \ + 133, 133, 133, 133, 132, 132, 132, 132, \ + 135, 135, 135, 135, 134, 134, 134, 134, \ + 130, 130, 130, 130, 130, 130, 130, 130, \ + 131, 131, 131, 131, 131, 131, 131, 131, \ + } + +#define U8_TO_ULAW { \ + 0, 0, 0, 0, 0, 1, 1, 1, \ + 1, 2, 2, 2, 2, 3, 3, 3, \ + 3, 4, 4, 4, 4, 5, 5, 5, \ + 5, 6, 6, 6, 6, 7, 7, 7, \ + 7, 8, 8, 8, 8, 9, 9, 9, \ + 9, 10, 10, 10, 10, 11, 11, 11, \ + 11, 12, 12, 12, 12, 13, 13, 13, \ + 13, 14, 14, 14, 14, 15, 15, 15, \ + 15, 16, 16, 17, 17, 18, 18, 19, \ + 19, 20, 20, 21, 21, 22, 22, 23, \ + 23, 24, 24, 25, 25, 26, 26, 27, \ + 27, 28, 28, 29, 29, 30, 30, 31, \ + 31, 32, 33, 34, 35, 36, 37, 38, \ + 39, 40, 41, 42, 43, 44, 45, 46, \ + 47, 49, 51, 53, 55, 57, 59, 61, \ + 63, 66, 70, 74, 78, 84, 92, 104, \ + 254, 231, 219, 211, 205, 201, 197, 193, \ + 190, 188, 186, 184, 182, 180, 178, 176, \ + 175, 174, 173, 172, 171, 170, 169, 168, \ + 167, 166, 165, 164, 163, 162, 161, 160, \ + 159, 159, 158, 158, 157, 157, 156, 156, \ + 155, 155, 154, 154, 153, 153, 152, 152, \ + 151, 151, 150, 150, 149, 149, 148, 148, \ + 147, 147, 146, 146, 145, 145, 144, 144, \ + 143, 143, 143, 143, 142, 142, 142, 142, \ + 141, 141, 141, 141, 140, 140, 140, 140, \ + 139, 139, 139, 139, 138, 138, 138, 138, \ + 137, 137, 137, 137, 136, 136, 136, 136, \ + 135, 135, 135, 135, 134, 134, 134, 134, \ + 133, 133, 133, 133, 132, 132, 132, 132, \ + 131, 131, 131, 131, 130, 130, 130, 130, \ + 129, 129, 129, 129, 128, 128, 128, 128, \ + } + +#define U8_TO_ALAW { \ + 42, 42, 42, 42, 42, 43, 43, 43, \ + 43, 40, 40, 40, 40, 41, 41, 41, \ + 41, 46, 46, 46, 46, 47, 47, 47, \ + 47, 44, 44, 44, 44, 45, 45, 45, \ + 45, 34, 34, 34, 34, 35, 35, 35, \ + 35, 32, 32, 32, 32, 33, 33, 33, \ + 33, 38, 38, 38, 38, 39, 39, 39, \ + 39, 36, 36, 36, 36, 37, 37, 37, \ + 37, 58, 58, 59, 59, 56, 56, 57, \ + 57, 62, 62, 63, 63, 60, 60, 61, \ + 61, 50, 50, 51, 51, 48, 48, 49, \ + 49, 54, 54, 55, 55, 52, 52, 53, \ + 53, 10, 11, 8, 9, 14, 15, 12, \ + 13, 2, 3, 0, 1, 6, 7, 4, \ + 5, 24, 30, 28, 18, 16, 22, 20, \ + 106, 110, 98, 102, 122, 114, 75, 90, \ + 213, 197, 245, 253, 229, 225, 237, 233, \ + 149, 151, 145, 147, 157, 159, 153, 155, \ + 133, 132, 135, 134, 129, 128, 131, 130, \ + 141, 140, 143, 142, 137, 136, 139, 138, \ + 181, 181, 180, 180, 183, 183, 182, 182, \ + 177, 177, 176, 176, 179, 179, 178, 178, \ + 189, 189, 188, 188, 191, 191, 190, 190, \ + 185, 185, 184, 184, 187, 187, 186, 186, \ + 165, 165, 165, 165, 164, 164, 164, 164, \ + 167, 167, 167, 167, 166, 166, 166, 166, \ + 161, 161, 161, 161, 160, 160, 160, 160, \ + 163, 163, 163, 163, 162, 162, 162, 162, \ + 173, 173, 173, 173, 172, 172, 172, 172, \ + 175, 175, 175, 175, 174, 174, 174, 174, \ + 169, 169, 169, 169, 168, 168, 168, 168, \ + 171, 171, 171, 171, 170, 170, 170, 170, \ + } + + +#define _G711_TO_INTPCM(t, v) ((intpcm_t) \ + ((int8_t)((t)[(uint8_t)(v)] ^ 0x80))) + +#define _INTPCM_TO_G711(t, v) ((t)[(uint8_t)((v) ^ 0x80)]) + + +#define G711_DECLARE_TABLE(t) \ +static const struct { \ + const uint8_t ulaw_to_u8[G711_TABLE_SIZE]; \ + const uint8_t alaw_to_u8[G711_TABLE_SIZE]; \ + const uint8_t u8_to_ulaw[G711_TABLE_SIZE]; \ + const uint8_t u8_to_alaw[G711_TABLE_SIZE]; \ +} t = { \ + ULAW_TO_U8, ALAW_TO_U8, \ + U8_TO_ULAW, U8_TO_ALAW \ +} + +#define G711_DECLARE_OP(t) \ +static __inline intpcm_t \ +pcm_read_ulaw(uint8_t v) \ +{ \ + \ + return (_G711_TO_INTPCM((t).ulaw_to_u8, v)); \ +} \ + \ +static __inline intpcm_t \ +pcm_read_alaw(uint8_t v) \ +{ \ + \ + return (_G711_TO_INTPCM((t).alaw_to_u8, v)); \ +} \ + \ +static __inline void \ +pcm_write_ulaw(uint8_t *dst, intpcm_t v) \ +{ \ + \ + *dst = _INTPCM_TO_G711((t).u8_to_ulaw, v); \ +} \ + \ +static __inline void \ +pcm_write_alaw(uint8_t *dst, intpcm_t v) \ +{ \ + \ + *dst = _INTPCM_TO_G711((t).u8_to_alaw, v); \ +} + +#define G711_DECLARE(t) \ + G711_DECLARE_TABLE(t); \ + G711_DECLARE_OP(t) + +#endif /* !_SND_G711_H_ */ --- sys/dev/sound/pcm/intpcm.h.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/intpcm.h 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,136 @@ +/*- + * Copyright (c) 2008-2009 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. + * + * $FreeBSD$ + */ + +#ifndef _SND_INTPCM_H_ +#define _SND_INTPCM_H_ + +typedef intpcm_t intpcm_read_t(uint8_t *); +typedef void intpcm_write_t(uint8_t *, intpcm_t); + +extern intpcm_read_t *feeder_format_read_op(uint32_t); +extern intpcm_write_t *feeder_format_write_op(uint32_t); + +#define INTPCM_DECLARE_OP_WRITE(SIGN, BIT, ENDIAN, SHIFT) \ +static __inline void \ +intpcm_write_##SIGN##BIT##ENDIAN(uint8_t *dst, intpcm_t v) \ +{ \ + \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v >> SHIFT); \ +} + +#define INTPCM_DECLARE_OP_8(SIGN, ENDIAN) \ +static __inline intpcm_t \ +intpcm_read_##SIGN##8##ENDIAN(uint8_t *src) \ +{ \ + \ + return (_PCM_READ_##SIGN##8##_##ENDIAN(src) << 24); \ +} \ +INTPCM_DECLARE_OP_WRITE(SIGN, 8, ENDIAN, 24) + +#define INTPCM_DECLARE_OP_16(SIGN, ENDIAN) \ +static __inline intpcm_t \ +intpcm_read_##SIGN##16##ENDIAN(uint8_t *src) \ +{ \ + \ + return (_PCM_READ_##SIGN##16##_##ENDIAN(src) << 16); \ +} \ +INTPCM_DECLARE_OP_WRITE(SIGN, 16, ENDIAN, 16) + +#define INTPCM_DECLARE_OP_24(SIGN, ENDIAN) \ +static __inline intpcm_t \ +intpcm_read_##SIGN##24##ENDIAN(uint8_t *src) \ +{ \ + \ + return (_PCM_READ_##SIGN##24##_##ENDIAN(src) << 8); \ +} \ +INTPCM_DECLARE_OP_WRITE(SIGN, 24, ENDIAN, 8) + +#define INTPCM_DECLARE_OP_32(SIGN, ENDIAN) \ +static __inline intpcm_t \ +intpcm_read_##SIGN##32##ENDIAN(uint8_t *src) \ +{ \ + \ + return (_PCM_READ_##SIGN##32##_##ENDIAN(src)); \ +} \ + \ +static __inline void \ +intpcm_write_##SIGN##32##ENDIAN(uint8_t *dst, intpcm_t v) \ +{ \ + \ + _PCM_WRITE_##SIGN##32##_##ENDIAN(dst, v); \ +} + + +#define INTPCM_DECLARE(t) \ + \ +G711_DECLARE_TABLE(t); \ + \ +static __inline intpcm_t \ +intpcm_read_ulaw(uint8_t *src) \ +{ \ + \ + return (_G711_TO_INTPCM((t).ulaw_to_u8, *src) << 24); \ +} \ + \ +static __inline intpcm_t \ +intpcm_read_alaw(uint8_t *src) \ +{ \ + \ + return (_G711_TO_INTPCM((t).alaw_to_u8, *src) << 24); \ +} \ + \ +static __inline void \ +intpcm_write_ulaw(uint8_t *dst, intpcm_t v) \ +{ \ + \ + *dst = _INTPCM_TO_G711((t).u8_to_ulaw, v >> 24); \ +} \ + \ +static __inline void \ +intpcm_write_alaw(uint8_t *dst, intpcm_t v) \ +{ \ + \ + *dst = _INTPCM_TO_G711((t).u8_to_alaw, v >> 24); \ +} \ + \ +INTPCM_DECLARE_OP_8(S, NE) \ +INTPCM_DECLARE_OP_16(S, LE) \ +INTPCM_DECLARE_OP_16(S, BE) \ +INTPCM_DECLARE_OP_24(S, LE) \ +INTPCM_DECLARE_OP_24(S, BE) \ +INTPCM_DECLARE_OP_32(S, LE) \ +INTPCM_DECLARE_OP_32(S, BE) \ +INTPCM_DECLARE_OP_8(U, NE) \ +INTPCM_DECLARE_OP_16(U, LE) \ +INTPCM_DECLARE_OP_16(U, BE) \ +INTPCM_DECLARE_OP_24(U, LE) \ +INTPCM_DECLARE_OP_24(U, BE) \ +INTPCM_DECLARE_OP_32(U, LE) \ +INTPCM_DECLARE_OP_32(U, BE) + +#endif /* !_SND_INTPCM_H_ */ --- sys/dev/sound/pcm/matrix.h.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/matrix.h 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,215 @@ +/*- + * Copyright (c) 2007-2009 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. + * + * $FreeBSD$ + */ + +#ifndef _SND_MATRIX_H_ +#define _SND_MATRIX_H_ + +#undef SND_MULTICHANNEL +#ifndef SND_OLDSTEREO +#define SND_MULTICHANNEL 1 +#endif + +#define SND_CHN_T_FL 0 /* Front Left */ +#define SND_CHN_T_FR 1 /* Front Right */ +#define SND_CHN_T_FC 2 /* Front Center */ +#define SND_CHN_T_LF 3 /* Low Frequency */ +#define SND_CHN_T_BL 4 /* Back Left */ +#define SND_CHN_T_BR 5 /* Back Right */ +#define SND_CHN_T_FLC 6 /* Front Left Center XXX */ +#define SND_CHN_T_FRC 7 /* Front Right Center XXX */ +#define SND_CHN_T_BC 8 /* Back Center */ +#define SND_CHN_T_SL 9 /* Side Left */ +#define SND_CHN_T_SR 10 /* Side Right */ +#define SND_CHN_T_TC 11 /* Top Center XXX */ +#define SND_CHN_T_TFL 12 /* Top Front Left XXX */ +#define SND_CHN_T_TFC 13 /* Top Front Center XXX */ +#define SND_CHN_T_TFR 14 /* Top Front Right XXX */ +#define SND_CHN_T_TBL 15 /* Top Back Left XXX */ +#define SND_CHN_T_TBC 16 /* Top Back Center XXX */ +#define SND_CHN_T_TBR 17 /* Top Back Right XXX */ +#define SND_CHN_T_MAX 18 /* Maximum channels */ + +#define SND_CHN_T_ZERO (SND_CHN_T_MAX + 1) /* Zero samples */ + +#define SND_CHN_T_LABELS { \ + "fl", "fr", "fc", "lf", "bl", "br", \ + "flc", "frc", "bc", "sl", "sr", "tc", \ + "tfl", "tfc", "tfr", "tbl", "tbc", "tbr" \ +} + +#define SND_CHN_T_NAMES { \ + "Front Left", "Front Right", "Front Center", \ + "Low Frequency Effects", \ + "Back Left", "Back Right", \ + "Front Left Center", "Front Right Center", \ + "Back Center", \ + "Side Left", "Side Right", \ + "Top Center", \ + "Top Front Left", "Top Front Center", "Top Front Right", \ + "Top Back Left", "Top Back Center", "Top Back Right" \ +} + +#define SND_CHN_T_MASK_FL (1 << SND_CHN_T_FL) +#define SND_CHN_T_MASK_FR (1 << SND_CHN_T_FR) +#define SND_CHN_T_MASK_FC (1 << SND_CHN_T_FC) +#define SND_CHN_T_MASK_LF (1 << SND_CHN_T_LF) +#define SND_CHN_T_MASK_BL (1 << SND_CHN_T_BL) +#define SND_CHN_T_MASK_BR (1 << SND_CHN_T_BR) +#define SND_CHN_T_MASK_FLC (1 << SND_CHN_T_FLC) +#define SND_CHN_T_MASK_FRC (1 << SND_CHN_T_FRC) +#define SND_CHN_T_MASK_BC (1 << SND_CHN_T_BC) +#define SND_CHN_T_MASK_SL (1 << SND_CHN_T_SL) +#define SND_CHN_T_MASK_SR (1 << SND_CHN_T_SR) +#define SND_CHN_T_MASK_TC (1 << SND_CHN_T_TC) +#define SND_CHN_T_MASK_TFL (1 << SND_CHN_T_TFL) +#define SND_CHN_T_MASK_TFC (1 << SND_CHN_T_TFC) +#define SND_CHN_T_MASK_TFR (1 << SND_CHN_T_TFR) +#define SND_CHN_T_MASK_TBL (1 << SND_CHN_T_TBL) +#define SND_CHN_T_MASK_TBC (1 << SND_CHN_T_TBC) +#define SND_CHN_T_MASK_TBR (1 << SND_CHN_T_TBR) + +#define SND_CHN_LEFT_MASK (SND_CHN_T_MASK_FL | \ + SND_CHN_T_MASK_BL | \ + SND_CHN_T_MASK_FLC | \ + SND_CHN_T_MASK_SL | \ + SND_CHN_T_MASK_TFL | \ + SND_CHN_T_MASK_TBL) + +#define SND_CHN_RIGHT_MASK (SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_FRC | \ + SND_CHN_T_MASK_SR | \ + SND_CHN_T_MASK_TFR | \ + SND_CHN_T_MASK_TBR) + +#define SND_CHN_CENTER_MASK (SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_BC | \ + SND_CHN_T_MASK_TC | \ + SND_CHN_T_MASK_TFC | \ + SND_CHN_T_MASK_TBC | \ + SND_CHN_T_MASK_LF) /* XXX what?!? */ + +/* + * Matrix identity. + */ + +/* 1 @ Mono 1.0 */ +#define SND_CHN_MATRIX_1_0 0 +#define SND_CHN_MATRIX_1 SND_CHN_MATRIX_1_0 + +/* 2 @ Stereo 2.0 */ +#define SND_CHN_MATRIX_2_0 1 +#define SND_CHN_MATRIX_2 SND_CHN_MATRIX_2_0 + +/* 3 @ 2.1 (lfe), 3.0 (rear center, DEFAULT) */ +#define SND_CHN_MATRIX_2_1 2 +#define SND_CHN_MATRIX_3_0 3 +#define SND_CHN_MATRIX_3 SND_CHN_MATRIX_3_0 + +/* 4 @ 4.0 Quadraphonic */ +#define SND_CHN_MATRIX_4_0 4 +#define SND_CHN_MATRIX_4 SND_CHN_MATRIX_4_0 + +/* 5 @ 4.1 (lfe), 5.0 (center, DEFAULT) */ +#define SND_CHN_MATRIX_4_1 5 +#define SND_CHN_MATRIX_5_0 6 +#define SND_CHN_MATRIX_5 SND_CHN_MATRIX_5_0 + +/* 6 @ 5.1 (lfe, DEFAULT), 6.0 (rear center) */ +#define SND_CHN_MATRIX_5_1 7 +#define SND_CHN_MATRIX_6_0 8 +#define SND_CHN_MATRIX_6 SND_CHN_MATRIX_5_1 + +/* 7 @ 6.1 (lfe) */ +#define SND_CHN_MATRIX_6_1 9 +#define SND_CHN_MATRIX_7 SND_CHN_MATRIX_6_1 + +/* 8 @ 7.1 (lfe) */ +#define SND_CHN_MATRIX_7_1 10 +#define SND_CHN_MATRIX_8 SND_CHN_MATRIX_7_1 + +#define SND_CHN_MATRIX_MAX 11 + +#define SND_CHN_MATRIX_BEGIN SND_CHN_MATRIX_1_0 +#define SND_CHN_MATRIX_END SND_CHN_MATRIX_7_1 + +/* Custom matrix identity */ +#define SND_CHN_MATRIX_DRV -4 /* driver own identity */ +#define SND_CHN_MATRIX_PCMCHANNEL -3 /* PCM channel identity */ +#define SND_CHN_MATRIX_MISC -2 /* misc, custom defined */ +#define SND_CHN_MATRIX_UNKNOWN -1 /* unknown */ + +#define SND_CHN_T_VOL_0DB SND_CHN_T_MAX +#define SND_CHN_T_VOL_MAX (SND_CHN_T_VOL_0DB + 1) + +#define SND_CHN_T_BEGIN SND_CHN_T_FL +#define SND_CHN_T_END SND_CHN_T_TBR +#define SND_CHN_T_STEP 1 +#define SND_CHN_MIN 1 + +#ifdef SND_MULTICHANNEL +#define SND_CHN_MAX 8 +#else +#define SND_CHN_MAX 2 +#endif + +/* + * Multichannel interleaved volume matrix. Each calculated value relative + * to master and 0db will be stored in each CLASS + 1 as long as + * chn_setvolume_matrix() or the equivalent CHN_SETVOLUME() macros is + * used (see channel.c). + */ +#define SND_VOL_C_MASTER 0 +#define SND_VOL_C_PCM 1 +#define SND_VOL_C_PCM_VAL 2 +#define SND_VOL_C_MAX 3 + +#define SND_VOL_C_BEGIN SND_VOL_C_PCM +#define SND_VOL_C_END SND_VOL_C_PCM +#define SND_VOL_C_STEP 2 + +#define SND_VOL_C_VAL(x) ((x) + 1) + +#define SND_VOL_0DB_MIN 1 +#define SND_VOL_0DB_MAX 100 + +#define SND_VOL_0DB_MASTER 100 +#define SND_VOL_0DB_PCM 45 + +#define SND_VOL_RESOLUTION 8 +#define SND_VOL_FLAT (1 << SND_VOL_RESOLUTION) + +#define SND_VOL_CALC_SAMPLE(x, y) (((x) * (y)) >> SND_VOL_RESOLUTION) + +#define SND_VOL_CALC_VAL(x, y, z) \ + (((((x)[y][z] << SND_VOL_RESOLUTION) / \ + (x)[y][SND_CHN_T_VOL_0DB]) * \ + (x)[SND_VOL_C_MASTER][z]) / \ + (x)[SND_VOL_C_MASTER][SND_CHN_T_VOL_0DB]) \ + +#endif /* !_SND_MATRIX_H_ */ --- sys/dev/sound/pcm/matrix_map.h.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/matrix_map.h 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,533 @@ +/*- + * Copyright (c) 2009 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. + * + * $FreeBSD$ + */ + +#ifndef _SND_MATRIX_MAP_H_ +#define _SND_MATRIX_MAP_H_ + +#define SND_CHN_MATRIX_MAP_1_0 { \ + .id = SND_CHN_MATRIX_1_0, \ + .channels = 1, \ + .ext = 0, \ + .map = { \ + /* Mono, center, etc. */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL | \ + SND_CHN_T_MASK_SR \ + }, \ + [1] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_FC, \ + .offset = { 0, 0, 0, 0, 0, 0, -1, -1, 0, \ + 0, 0, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_2_0 { \ + .id = SND_CHN_MATRIX_2_0, \ + .channels = 2, \ + .ext = 0, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \ + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = \ + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \ + }, \ + [2] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR, \ + .offset = { 0, 1, -1, -1, -1, -1, -1, -1, -1, \ + -1, -1, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_2_1 { \ + .id = SND_CHN_MATRIX_2_1, \ + .channels = 3, \ + .ext = 1, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \ + SND_CHN_T_MASK_SL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = \ + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \ + SND_CHN_T_MASK_SR \ + }, \ + /* LFE */ \ + [2] = { \ + .type = SND_CHN_T_LF, \ + .members = SND_CHN_T_MASK_LF \ + }, \ + [3] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_LF, \ + .offset = { 0, 1, -1, 2, -1, -1, -1, -1, -1, \ + -1, -1, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_3_0 { /* 3 channels default */ \ + .id = SND_CHN_MATRIX_3_0, \ + .channels = 3, \ + .ext = 0, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = \ + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SR \ + }, \ + /* Rear Center */ \ + [2] = { \ + .type = SND_CHN_T_BC, \ + .members = \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \ + SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \ + SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR \ + }, \ + [3] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BC, \ + .offset = { 0, 1, -1, -1, -1, -1, -1, -1, 2, \ + -1, -1, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_4_0 { \ + .id = SND_CHN_MATRIX_4_0, \ + .channels = 4, \ + .ext = 0, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = \ + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_SR \ + }, \ + /* Rear Left */ \ + [2] = { \ + .type = SND_CHN_T_BL, \ + .members = \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \ + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \ + }, \ + /* Rear Right */ \ + [3] = { \ + .type = SND_CHN_T_BR, \ + .members = \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \ + }, \ + [4] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR, \ + .offset = { 0, 1, -1, -1, 2, 3, -1, -1, -1, \ + -1, -1, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_4_1 { \ + .id = SND_CHN_MATRIX_4_1, \ + .channels = 5, \ + .ext = 1, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_SL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = \ + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | \ + SND_CHN_T_MASK_SR \ + }, \ + /* Rear Left */ \ + [2] = { \ + .type = SND_CHN_T_BL, \ + .members = \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \ + SND_CHN_T_MASK_SL \ + }, \ + /* Rear Right */ \ + [3] = { \ + .type = SND_CHN_T_BR, \ + .members = \ + SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \ + SND_CHN_T_MASK_SR \ + }, \ + /* LFE */ \ + [4] = { \ + .type = SND_CHN_T_LF, \ + .members = SND_CHN_T_MASK_LF \ + }, \ + [5] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_LF, \ + .offset = { 0, 1, -1, 4, 2, 3, -1, -1, -1, \ + -1, -1, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_5_0 { /* 5 channels default */ \ + .id = SND_CHN_MATRIX_5_0, \ + .channels = 5, \ + .ext = 0, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_SL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = \ + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_SR \ + }, \ + /* Rear Left */ \ + [2] = { \ + .type = SND_CHN_T_BL, \ + .members = \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | \ + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL \ + }, \ + /* Rear Right */ \ + [3] = { \ + .type = SND_CHN_T_BR, \ + .members = \ + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR \ + }, \ + /* Center */ \ + [4] = { \ + .type = SND_CHN_T_FC, \ + .members = SND_CHN_T_MASK_FC \ + }, \ + [5] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_FC, \ + .offset = { 0, 1, 4, -1, 2, 3, -1, -1, -1, \ + -1, -1, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_5_1 { /* 6 channels default */ \ + .id = SND_CHN_MATRIX_5_1, \ + .channels = 6, \ + .ext = 1, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_SL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = \ + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_SR \ + }, \ + /* Rear Left */ \ + [2] = { \ + .type = SND_CHN_T_BL, \ + .members = \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC | \ + SND_CHN_T_MASK_SL \ + }, \ + /* Rear Right */ \ + [3] = { \ + .type = SND_CHN_T_BR, \ + .members = \ + SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC | \ + SND_CHN_T_MASK_SR \ + }, \ + /* Center */ \ + [4] = { \ + .type = SND_CHN_T_FC, \ + .members = SND_CHN_T_MASK_FC \ + }, \ + /* LFE */ \ + [5] = { \ + .type = SND_CHN_T_LF, \ + .members = SND_CHN_T_MASK_LF \ + }, \ + [6] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF, \ + .offset = { 0, 1, 4, 5, 2, 3, -1, -1, -1, \ + -1, -1, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_6_0 { \ + .id = SND_CHN_MATRIX_6_0, \ + .channels = 6, \ + .ext = 0, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_SL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = \ + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_SR \ + }, \ + /* Rear Left */ \ + [2] = { \ + .type = SND_CHN_T_BL, \ + .members = \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_SL \ + }, \ + /* Rear Right */ \ + [3] = { \ + .type = SND_CHN_T_BR, \ + .members = \ + SND_CHN_T_MASK_BR | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_SR \ + }, \ + /* Center */ \ + [4] = { \ + .type = SND_CHN_T_FC, \ + .members = SND_CHN_T_MASK_FC \ + }, \ + /* Rear Center */ \ + [5] = { \ + .type = SND_CHN_T_BC, \ + .members = SND_CHN_T_MASK_BC \ + }, \ + [6] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_FC | SND_CHN_T_MASK_BC, \ + .offset = { 0, 1, 4, -1, 2, 3, -1, -1, 5, \ + -1, -1, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_6_1 { \ + .id = SND_CHN_MATRIX_6_1, \ + .channels = 7, \ + .ext = 1, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = \ + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_SL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = \ + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_SR \ + }, \ + /* Rear Left */ \ + [2] = { \ + .type = SND_CHN_T_BL, \ + .members = \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_SL \ + }, \ + /* Rear Right */ \ + [3] = { \ + .type = SND_CHN_T_BR, \ + .members = \ + SND_CHN_T_MASK_BR | SND_CHN_T_MASK_SR \ + }, \ + /* Center */ \ + [4] = { \ + .type = SND_CHN_T_FC, \ + .members = SND_CHN_T_MASK_FC \ + }, \ + /* LFE */ \ + [5] = { \ + .type = SND_CHN_T_LF, \ + .members = SND_CHN_T_MASK_LF \ + }, \ + /* Rear Center */ \ + [6] = { \ + .type = SND_CHN_T_BC, \ + .members = SND_CHN_T_MASK_BC \ + }, \ + [7] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_BC, \ + .offset = { 0, 1, 4, 5, 2, 3, -1, -1, 6, \ + -1, -1, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#define SND_CHN_MATRIX_MAP_7_1 { \ + .id = SND_CHN_MATRIX_7_1, \ + .channels = 8, \ + .ext = 1, \ + .map = { \ + /* Left */ \ + [0] = { \ + .type = SND_CHN_T_FL, \ + .members = SND_CHN_T_MASK_FL \ + }, \ + /* Right */ \ + [1] = { \ + .type = SND_CHN_T_FR, \ + .members = SND_CHN_T_MASK_FR \ + }, \ + /* Rear Left */ \ + [2] = { \ + .type = SND_CHN_T_BL, \ + .members = \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BC \ + }, \ + /* Rear Right */ \ + [3] = { \ + .type = SND_CHN_T_BR, \ + .members = \ + SND_CHN_T_MASK_BR | SND_CHN_T_MASK_BC \ + }, \ + /* Center */ \ + [4] = { \ + .type = SND_CHN_T_FC, \ + .members = SND_CHN_T_MASK_FC \ + }, \ + /* LFE */ \ + [5] = { \ + .type = SND_CHN_T_LF, \ + .members = SND_CHN_T_MASK_LF \ + }, \ + /* Side Left */ \ + [6] = { \ + .type = SND_CHN_T_SL, \ + .members = SND_CHN_T_MASK_SL \ + }, \ + /* Side Right */ \ + [7] = { \ + .type = SND_CHN_T_SR, \ + .members = SND_CHN_T_MASK_SR \ + }, \ + [8] = { \ + .type = SND_CHN_T_MAX, \ + .members = 0 \ + } \ + }, \ + .mask = SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FR | \ + SND_CHN_T_MASK_BL | SND_CHN_T_MASK_BR | \ + SND_CHN_T_MASK_FC | SND_CHN_T_MASK_LF | \ + SND_CHN_T_MASK_SL | SND_CHN_T_MASK_SR, \ + .offset = { 0, 1, 4, 5, 2, 3, -1, -1, -1, \ + 6, 7, -1, -1, -1, -1, -1, -1, -1 } \ +} + +#endif /* !_SND_MATRIX_MAP_H_ */ --- sys/dev/sound/pcm/mixer.c.orig 2009-01-17 19:31:45.000000000 +0800 +++ sys/dev/sound/pcm/mixer.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Portions Copyright (c) Ryan Beasley - GSoC 2006 + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -26,12 +28,19 @@ #include +#include "feeder_if.h" #include "mixer_if.h" SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/mixer.c,v 1.65 2009/01/10 21:38:37 mav Exp $"); MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); +static int mixer_bypass = 1; +TUNABLE_INT("hw.snd.vpc_mixer_bypass", &mixer_bypass); +SYSCTL_INT(_hw_snd, OID_AUTO, vpc_mixer_bypass, CTLFLAG_RW, + &mixer_bypass, 0, + "control channel pcm/rec volume, bypassing real mixer device"); + #define MIXER_NAMELEN 16 struct snd_mixer { KOBJ_FIELDS; @@ -98,9 +107,7 @@ */ int mixer_count = 0; -#ifdef USING_DEVFS static eventhandler_tag mixer_ehtag = NULL; -#endif static struct cdev * mixer_get_devt(device_t dev) @@ -112,7 +119,6 @@ return snddev->mixer_dev; } -#ifdef SND_DYNSYSCTL static int mixer_lookup(char *devname) { @@ -124,7 +130,6 @@ return i; return -1; } -#endif #define MIXER_SET_UNLOCK(x, y) do { \ if ((y) != 0) \ @@ -138,7 +143,7 @@ static int mixer_set_softpcmvol(struct snd_mixer *m, struct snddev_info *d, - unsigned left, unsigned right) + u_int left, u_int right) { struct pcm_channel *c; int dropmtx, acquiremtx; @@ -166,22 +171,13 @@ MIXER_SET_UNLOCK(m, dropmtx); MIXER_SET_LOCK(d, acquiremtx); - if (CHN_EMPTY(d, channels.pcm.busy)) { - CHN_FOREACH(c, d, channels.pcm) { - CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY && - (c->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(c, left, right); - CHN_UNLOCK(c); - } - } else { - CHN_FOREACH(c, d, channels.pcm.busy) { - CHN_LOCK(c); - if (c->direction == PCMDIR_PLAY && - (c->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(c, left, right); - CHN_UNLOCK(c); - } + CHN_FOREACH(c, d, channels.pcm.busy) { + CHN_LOCK(c); + if (c->direction == PCMDIR_PLAY && + (c->feederflags & (1 << FEEDER_VOLUME))) + chn_setvolume_multi(c, SND_VOL_C_MASTER, left, right, + (left + right) >> 1); + CHN_UNLOCK(c); } MIXER_SET_UNLOCK(d, acquiremtx); @@ -191,10 +187,62 @@ } static int -mixer_set(struct snd_mixer *m, unsigned dev, unsigned lev) +mixer_set_eq(struct snd_mixer *m, struct snddev_info *d, + u_int dev, u_int level) +{ + struct pcm_channel *c; + struct pcm_feeder *f; + int tone, dropmtx, acquiremtx; + + if (dev == SOUND_MIXER_TREBLE) + tone = FEEDEQ_TREBLE; + else if (dev == SOUND_MIXER_BASS) + tone = FEEDEQ_BASS; + else + return (EINVAL); + + if (!PCM_REGISTERED(d)) + return (EINVAL); + + if (mtx_owned(m->lock)) + dropmtx = 1; + else + dropmtx = 0; + + if (!(d->flags & SD_F_MPSAFE) || mtx_owned(d->lock) != 0) + acquiremtx = 0; + else + acquiremtx = 1; + + /* + * Be careful here. If we're coming from cdev ioctl, it is OK to + * not doing locking AT ALL (except on individual channel) since + * we've been heavily guarded by pcm cv, or if we're still + * under Giant influence. Since we also have mix_* calls, we cannot + * assume such protection and just do the lock as usuall. + */ + MIXER_SET_UNLOCK(m, dropmtx); + MIXER_SET_LOCK(d, acquiremtx); + + CHN_FOREACH(c, d, channels.pcm.busy) { + CHN_LOCK(c); + f = chn_findfeeder(c, FEEDER_EQ); + if (f != NULL) + (void)FEEDER_SET(f, tone, level); + CHN_UNLOCK(c); + } + + MIXER_SET_UNLOCK(d, acquiremtx); + MIXER_SET_LOCK(m, dropmtx); + + return (0); +} + +static int +mixer_set(struct snd_mixer *m, u_int dev, u_int lev) { struct snddev_info *d; - unsigned l, r, tl, tr; + u_int l, r, tl, tr; u_int32_t parent = SOUND_MIXER_NONE, child = 0; u_int32_t realdev; int i, dropmtx; @@ -243,7 +291,8 @@ realdev = m->realdev[i]; tl = (l * (m->level[i] & 0x00ff)) / 100; tr = (r * ((m->level[i] & 0xff00) >> 8)) / 100; - if (i == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) + if (i == SOUND_MIXER_PCM && + (d->flags & SD_F_SOFTPCMVOL)) (void)mixer_set_softpcmvol(m, d, tl, tr); else if (realdev != SOUND_MIXER_NONE) MIXER_SET(m, realdev, tl, tr); @@ -257,6 +306,9 @@ } else { if (dev == SOUND_MIXER_PCM && (d->flags & SD_F_SOFTPCMVOL)) (void)mixer_set_softpcmvol(m, d, l, r); + else if ((dev == SOUND_MIXER_TREBLE || + dev == SOUND_MIXER_BASS) && (d->flags & SD_F_EQ)) + (void)mixer_set_eq(m, d, dev, (l + r) >> 1); else if (realdev != SOUND_MIXER_NONE && MIXER_SET(m, realdev, l, r) < 0) { MIXER_SET_LOCK(m, dropmtx); @@ -398,6 +450,8 @@ d = device_get_softc(m->dev); if (d != NULL && (d->flags & SD_F_SOFTPCMVOL)) v |= SOUND_MASK_PCM; + if (d != NULL && (d->flags & SD_F_EQ)) + v |= SOUND_MASK_TREBLE | SOUND_MASK_BASS; for (i = 0; i < SOUND_MIXER_NRDEVICES; i++) { if (m->parent[i] < SOUND_MIXER_NRDEVICES) v |= 1 << m->parent[i]; @@ -623,6 +677,20 @@ struct cdev *pdev; int i, unit, devunit, val; + snddev = device_get_softc(dev); + if (snddev == NULL) + return (-1); + + if (resource_int_value(device_get_name(dev), + device_get_unit(dev), "eq", &val) == 0 && val != 0) { + snddev->flags |= SD_F_EQ; + if ((val & SD_F_EQ_MASK) == val) + snddev->flags |= val; + else + snddev->flags |= SD_F_EQ_DEFAULT; + snddev->eqpreamp = 0; + } + m = mixer_obj_create(dev, cls, devinfo, MIXER_TYPE_PRIMARY, NULL); if (m == NULL) return (-1); @@ -647,7 +715,6 @@ pdev = make_dev(&mixer_cdevsw, devunit, UID_ROOT, GID_WHEEL, 0666, "mixer%d", unit); pdev->si_drv1 = m; - snddev = device_get_softc(dev); snddev->mixer_dev = pdev; ++mixer_count; @@ -674,6 +741,8 @@ } if (snddev->flags & SD_F_SOFTPCMVOL) device_printf(dev, "Soft PCM mixer ENABLED\n"); + if (snddev->flags & SD_F_EQ) + device_printf(dev, "EQ Treble/Bass ENABLED\n"); } return (0); @@ -760,7 +829,6 @@ return 0; } -#ifdef SND_DYNSYSCTL static int sysctl_hw_snd_hwvol_mixer(SYSCTL_HANDLER_ARGS) { @@ -788,7 +856,6 @@ snd_mtxunlock(m->lock); return error; } -#endif int mixer_hwvol_init(device_t dev) @@ -801,7 +868,6 @@ m->hwvol_mixer = SOUND_MIXER_VOLUME; m->hwvol_step = 5; -#ifdef SND_DYNSYSCTL SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "hwvol_step", CTLFLAG_RW, &m->hwvol_step, 0, ""); @@ -809,7 +875,6 @@ SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "hwvol_mixer", CTLTYPE_STRING | CTLFLAG_RW, m, 0, sysctl_hw_snd_hwvol_mixer, "A", ""); -#endif return 0; } @@ -986,6 +1051,114 @@ } static int +mixer_ioctl_channel(struct cdev *dev, u_long cmd, caddr_t arg, int mode, + struct thread *td, int from) +{ + struct snddev_info *d; + struct snd_mixer *m; + struct pcm_channel *c, *rdch, *wrch; + pid_t pid; + int j, ret; + + if (td == NULL || td->td_proc == NULL) + return (-1); + + m = dev->si_drv1; + d = device_get_softc(m->dev); + j = cmd & 0xff; + + switch (j) { + case SOUND_MIXER_PCM: + case SOUND_MIXER_RECLEV: + case SOUND_MIXER_DEVMASK: + case SOUND_MIXER_CAPS: + case SOUND_MIXER_STEREODEVS: + break; + default: + return (-1); + break; + } + + pid = td->td_proc->p_pid; + rdch = NULL; + wrch = NULL; + c = NULL; + ret = -1; + + /* + * This is unfair. Imagine single proc opening multiple + * instances of same direction. What we do right now + * is looking for the first matching proc/pid, and just + * that. Nothing more. Consider it done. + * + * The better approach of controlling specific channel + * pcm or rec volume is by doing mixer ioctl + * (SNDCTL_DSP_[SET|GET][PLAY|REC]VOL / SOUND_MIXER_[PCM|RECLEV] + * on its open fd, rather than cracky mixer bypassing here. + */ + CHN_FOREACH(c, d, channels.pcm.opened) { + CHN_LOCK(c); + if (c->pid != pid || + !(c->feederflags & (1 << FEEDER_VOLUME))) { + CHN_UNLOCK(c); + continue; + } + if (rdch == NULL && c->direction == PCMDIR_REC) { + rdch = c; + if (j == SOUND_MIXER_RECLEV) + goto mixer_ioctl_channel_proc; + } else if (wrch == NULL && c->direction == PCMDIR_PLAY) { + wrch = c; + if (j == SOUND_MIXER_PCM) + goto mixer_ioctl_channel_proc; + } + CHN_UNLOCK(c); + if (rdch != NULL && wrch != NULL) + break; + } + + if (rdch == NULL && wrch == NULL) + return (-1); + + if ((j == SOUND_MIXER_DEVMASK || j == SOUND_MIXER_CAPS || + j == SOUND_MIXER_STEREODEVS) && + (cmd & MIXER_READ(0)) == MIXER_READ(0)) { + snd_mtxlock(m->lock); + *(int *)arg = mix_getdevs(m); + snd_mtxunlock(m->lock); + if (rdch != NULL) + *(int *)arg |= SOUND_MASK_RECLEV; + if (wrch != NULL) + *(int *)arg |= SOUND_MASK_PCM; + ret = 0; + } + + return (ret); + +mixer_ioctl_channel_proc: + + KASSERT(c != NULL, ("%s(): NULL channel", __func__)); + CHN_LOCKASSERT(c); + + if ((cmd & MIXER_WRITE(0)) == MIXER_WRITE(0)) { + int left, right, center; + + left = *(int *)arg & 0x7f; + right = (*(int *)arg >> 8) & 0x7f; + center = (left + right) >> 1; + chn_setvolume_multi(c, SND_VOL_C_PCM, left, right, center); + } else if ((cmd & MIXER_READ(0)) == MIXER_READ(0)) { + *(int *)arg = CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL); + *(int *)arg |= + CHN_GETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR) << 8; + } + + CHN_UNLOCK(c); + + return (0); +} + +static int mixer_ioctl(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, struct thread *td) { @@ -1002,7 +1175,15 @@ PCM_GIANT_ENTER(d); PCM_ACQUIRE_QUICK(d); - ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, MIXER_CMD_CDEV); + ret = -1; + + if (mixer_bypass != 0 && (d->flags & SD_F_VPC)) + ret = mixer_ioctl_channel(i_dev, cmd, arg, mode, td, + MIXER_CMD_CDEV); + + if (ret == -1) + ret = mixer_ioctl_cmd(i_dev, cmd, arg, mode, td, + MIXER_CMD_CDEV); PCM_RELEASE_QUICK(d); PCM_GIANT_LEAVE(d); @@ -1012,7 +1193,7 @@ /* * XXX Make sure you can guarantee concurrency safety before calling this - * function, be it through Giant, PCM_CV_*, etc ! + * function, be it through Giant, PCM_*, etc ! */ int mixer_ioctl_cmd(struct cdev *i_dev, u_long cmd, caddr_t arg, int mode, @@ -1112,7 +1293,6 @@ return (ret); } -#ifdef USING_DEVFS static void mixer_clone(void *arg, #if __FreeBSD_version >= 600034 @@ -1152,7 +1332,6 @@ SYSINIT(mixer_sysinit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysinit, NULL); SYSUNINIT(mixer_sysuninit, SI_SUB_DRIVERS, SI_ORDER_MIDDLE, mixer_sysuninit, NULL); -#endif /** * @brief Handler for SNDCTL_MIXERINFO @@ -1204,8 +1383,8 @@ /* XXX Need Giant magic entry */ /* See the note in function docblock. */ - mtx_assert(d->lock, MA_NOTOWNED); - pcm_lock(d); + PCM_UNLOCKASSERT(d); + PCM_LOCK(d); if (d->mixer_dev != NULL && d->mixer_dev->si_drv1 != NULL && ((mi->dev == -1 && d->mixer_dev == i_dev) || @@ -1288,7 +1467,7 @@ } else ++nmix; - pcm_unlock(d); + PCM_UNLOCK(d); if (m != NULL) return (0); --- sys/dev/sound/pcm/mixer.h.orig 2008-11-06 23:05:15.000000000 +0800 +++ sys/dev/sound/pcm/mixer.h 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,6 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Copyright (c) 1999 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without --- sys/dev/sound/pcm/pcm.h.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/pcm.h 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,386 @@ +/*- + * Copyright (c) 2006-2009 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. + * + * $FreeBSD$ + */ + +#ifndef _SND_PCM_H_ +#define _SND_PCM_H_ + +#include + +/* + * Macros for reading/writing PCM sample / int values from bytes array. + * Since every process is done using signed integer (and to make our life + * less miserable), unsigned sample will be converted to its signed + * counterpart and restored during writing back. To avoid overflow, + * we truncate 32bit (and only 32bit) samples down to 24bit (see below + * for the reason), unless PCM_USE_64BIT_ARITH is defined. + */ + +/* + * Automatically turn on 64bit arithmetic on suitable archs + * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing. + */ +#if LONG_BIT >= 64 +#undef PCM_USE_64BIT_ARITH +#define PCM_USE_64BIT_ARITH 1 +#endif + +typedef int32_t intpcm_t; + +typedef int32_t intpcm8_t; +typedef int32_t intpcm16_t; +typedef int32_t intpcm24_t; + +typedef uint32_t uintpcm_t; + +typedef uint32_t uintpcm8_t; +typedef uint32_t uintpcm16_t; +typedef uint32_t uintpcm24_t; + +#ifdef PCM_USE_64BIT_ARITH +typedef int64_t intpcm32_t; +typedef uint64_t uintpcm32_t; +#else +typedef int32_t intpcm32_t; +typedef uint32_t uintpcm32_t; +#endif + +typedef int64_t intpcm64_t; +typedef uint64_t uintpcm64_t; + +/* 32bit fixed point shift */ +#define PCM_FXSHIFT 8 + +#define PCM_S8_MAX 0x7f +#define PCM_S8_MIN -0x80 +#define PCM_S16_MAX 0x7fff +#define PCM_S16_MIN -0x8000 +#define PCM_S24_MAX 0x7fffff +#define PCM_S24_MIN -0x800000 +#ifdef PCM_USE_64BIT_ARITH +#if LONG_BIT >= 64 +#define PCM_S32_MAX 0x7fffffffL +#define PCM_S32_MIN -0x80000000L +#else +#define PCM_S32_MAX 0x7fffffffLL +#define PCM_S32_MIN -0x80000000LL +#endif +#else +#define PCM_S32_MAX 0x7fffffff +#define PCM_S32_MIN (-0x7fffffff - 1) +#endif + +/* Bytes-per-sample definition */ +#define PCM_8_BPS 1 +#define PCM_16_BPS 2 +#define PCM_24_BPS 3 +#define PCM_32_BPS 4 + +#define INTPCM_T(v) ((intpcm_t)(v)) +#define INTPCM8_T(v) ((intpcm8_t)(v)) +#define INTPCM16_T(v) ((intpcm16_t)(v)) +#define INTPCM24_T(v) ((intpcm24_t)(v)) +#define INTPCM32_T(v) ((intpcm32_t)(v)) + +#if BYTE_ORDER == LITTLE_ENDIAN +#define _PCM_READ_S16_LE(b8) INTPCM_T(*((int16_t *)(b8))) +#define _PCM_READ_S32_LE(b8) INTPCM_T(*((int32_t *)(b8))) +#define _PCM_READ_S16_BE(b8) \ + INTPCM_T((b8)[1] | (((int8_t)((b8)[0])) << 8)) +#define _PCM_READ_S32_BE(b8) \ + INTPCM_T((b8)[3] | ((b8)[2] << 8) | ((b8)[1] << 16) | \ + (((int8_t)((b8)[0])) << 24)) + +#define _PCM_WRITE_S16_LE(b8, val) do { \ + *((int16_t *)(b8)) = (val); \ +} while (0) +#define _PCM_WRITE_S32_LE(b8, val) do { \ + *((int32_t *)(b8)) = (val); \ +} while (0) +#define _PCM_WRITE_S16_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[1] = val; \ + b8[0] = val >> 8; \ +} while (0) +#define _PCM_WRITE_S32_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[3] = val; \ + b8[2] = val >> 8; \ + b8[1] = val >> 16; \ + b8[0] = val >> 24; \ +} while (0) + +#define _PCM_READ_U16_LE(b8) \ + INTPCM_T((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) +#define _PCM_READ_U32_LE(b8) \ + INTPCM_T((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) +#define _PCM_READ_U16_BE(b8) \ + INTPCM_T((b8)[1] | (((int8_t)((b8)[0] ^ 0x80)) << 8)) +#define _PCM_READ_U32_BE(b8) \ + INTPCM_T((b8)[3] | ((b8)[2] << 8) | ((b8)[1] << 16) | \ + (((int8_t)((b8)[0] ^ 0x80)) << 24)) + +#define _PCM_WRITE_U16_LE(b8, val) do { \ + *((uint16_t *)(b8)) = (val) ^ 0x8000; \ +} while (0) +#define _PCM_WRITE_U32_LE(b8, val) do { \ + *((uint32_t *)(b8)) = (val) ^ 0x80000000; \ +} while (0) +#define _PCM_WRITE_U16_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[1] = val; \ + b8[0] = (val >> 8) ^ 0x80; \ +} while (0) +#define _PCM_WRITE_U32_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[3] = val; \ + b8[2] = val >> 8; \ + b8[1] = val >> 16; \ + b8[0] = (val >> 24) ^ 0x80; \ +} while (0) +#else /* !LITTLE_ENDIAN */ +#define _PCM_READ_S16_LE(b8) \ + INTPCM_T((b8)[0] | (((int8_t)((b8)[1])) << 8)) +#define _PCM_READ_S32_LE(b8) \ + INTPCM_T((b8)[0] | ((b8)[1] << 8) | ((b8)[2] << 16) | \ + (((int8_t)((b8)[3])) << 24)) +#define _PCM_READ_S16_BE(b8) INTPCM_T(*((int16_t *)(b8))) +#define _PCM_READ_S32_BE(b8) INTPCM_T(*((int32_t *)(b8))) + +#define _PCM_WRITE_S16_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ +} while (0) +#define _PCM_WRITE_S32_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = val >> 16; \ + b8[3] = val >> 24; \ +} while (0) +#define _PCM_WRITE_S16_BE(b8, val) do { \ + *((int16_t *)(b8)) = (val); \ +} while (0) +#define _PCM_WRITE_S32_BE(b8, val) do { \ + *((int32_t *)(b8)) = (val); \ +} while (0) + +#define _PCM_READ_U16_LE(b8) \ + INTPCM_T((b8)[0] | (((int8_t)((b8)[1] ^ 0x80)) << 8)) +#define _PCM_READ_U32_LE(b8) \ + INTPCM_T((b8)[0] | ((b8)[1] << 8) | ((b8)[2] << 16) | \ + (((int8_t)((b8)[3] ^ 0x80)) << 24)) +#define _PCM_READ_U16_BE(b8) \ + INTPCM_T((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) +#define _PCM_READ_U32_BE(b8) \ + INTPCM_T((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) + +#define _PCM_WRITE_U16_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = (val >> 8) ^ 0x80; \ +} while (0) +#define _PCM_WRITE_U32_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = val >> 16; \ + b8[3] = (val >> 24) ^ 0x80; \ +} while (0) +#define _PCM_WRITE_U16_BE(b8, val) do { \ + *((uint16_t *)(b8)) = (val) ^ 0x8000; \ +} while (0) +#define _PCM_WRITE_U32_BE(b8, val) do { \ + *((uint32_t *)(b8)) = (val) ^ 0x80000000; \ +} while (0) +#endif + +#define _PCM_READ_S24_LE(b8) \ + INTPCM_T((b8)[0] | ((b8)[1] << 8) | (((int8_t)((b8)[2])) << 16)) +#define _PCM_READ_S24_BE(b8) \ + INTPCM_T((b8)[2] | ((b8)[1] << 8) | (((int8_t)((b8)[0])) << 16)) + +#define _PCM_WRITE_S24_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = val >> 16; \ +} while (0) +#define _PCM_WRITE_S24_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[2] = val; \ + b8[1] = val >> 8; \ + b8[0] = val >> 16; \ +} while (0) + +#define _PCM_READ_U24_LE(b8) \ + INTPCM_T((b8)[0] | ((b8)[1] << 8) | \ + (((int8_t)((b8)[2] ^ 0x80)) << 16)) +#define _PCM_READ_U24_BE(b8) \ + INTPCM_T((b8)[2] | ((b8)[1] << 8) | \ + (((int8_t)((b8)[0] ^ 0x80)) << 16)) + +#define _PCM_WRITE_U24_LE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[0] = val; \ + b8[1] = val >> 8; \ + b8[2] = (val >> 16) ^ 0x80; \ +} while (0) +#define _PCM_WRITE_U24_BE(bb8, vval) do { \ + intpcm_t val = (vval); \ + uint8_t *b8 = (bb8); \ + b8[2] = val; \ + b8[1] = val >> 8; \ + b8[0] = (val >> 16) ^ 0x80; \ +} while (0) + +/* + * 8bit sample is pretty much useless since it doesn't provide + * sufficient dynamic range throughout our filtering process. + * For the sake of completeness, declare it anyway. + */ +#define _PCM_READ_S8_NE(b8) INTPCM_T(*((int8_t *)(b8))) +#define _PCM_READ_U8_NE(b8) \ + INTPCM_T((int8_t)(*((uint8_t *)(b8)) ^ 0x80)) + +#define _PCM_WRITE_S8_NE(b8, val) do { \ + *((int8_t *)(b8)) = (val); \ +} while (0) +#define _PCM_WRITE_U8_NE(b8, val) do { \ + *((uint8_t *)(b8)) = (val) ^ 0x80; \ +} while (0) + +/* + * Common macross. Use this instead of "_", unless we want + * the real sample value. + */ + +/* 8bit */ +#define PCM_READ_S8_NE(b8) _PCM_READ_S8_NE(b8) +#define PCM_READ_U8_NE(b8) _PCM_READ_U8_NE(b8) +#define PCM_WRITE_S8_NE(b8, val) _PCM_WRITE_S8_NE(b8, val) +#define PCM_WRITE_U8_NE(b8, val) _PCM_WRITE_U8_NE(b8, val) + +/* 16bit */ +#define PCM_READ_S16_LE(b8) _PCM_READ_S16_LE(b8) +#define PCM_READ_S16_BE(b8) _PCM_READ_S16_BE(b8) +#define PCM_READ_U16_LE(b8) _PCM_READ_U16_LE(b8) +#define PCM_READ_U16_BE(b8) _PCM_READ_U16_BE(b8) + +#define PCM_WRITE_S16_LE(b8, val) _PCM_WRITE_S16_LE(b8, val) +#define PCM_WRITE_S16_BE(b8, val) _PCM_WRITE_S16_BE(b8, val) +#define PCM_WRITE_U16_LE(b8, val) _PCM_WRITE_U16_LE(b8, val) +#define PCM_WRITE_U16_BE(b8, val) _PCM_WRITE_U16_BE(b8, val) + +/* 24bit */ +#define PCM_READ_S24_LE(b8) _PCM_READ_S24_LE(b8) +#define PCM_READ_S24_BE(b8) _PCM_READ_S24_BE(b8) +#define PCM_READ_U24_LE(b8) _PCM_READ_U24_LE(b8) +#define PCM_READ_U24_BE(b8) _PCM_READ_U24_BE(b8) + +#define PCM_WRITE_S24_LE(b8, val) _PCM_WRITE_S24_LE(b8, val) +#define PCM_WRITE_S24_BE(b8, val) _PCM_WRITE_S24_BE(b8, val) +#define PCM_WRITE_U24_LE(b8, val) _PCM_WRITE_U24_LE(b8, val) +#define PCM_WRITE_U24_BE(b8, val) _PCM_WRITE_U24_BE(b8, val) + +/* 32bit */ +#ifdef PCM_USE_64BIT_ARITH +#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8) +#define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8) +#define PCM_READ_U32_LE(b8) _PCM_READ_U32_LE(b8) +#define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8) + +#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) +#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) +#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) +#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) +#else /* !PCM_USE_64BIT_ARITH */ +/* + * 24bit integer ?!? This is quite unfortunate, eh? Get the fact straight: + * Dynamic range for: + * 1) Human =~ 140db + * 2) 16bit = 96db (close enough) + * 3) 24bit = 144db (perfect) + * 4) 32bit = 196db (way too much) + * 5) Bugs Bunny = Gazillion!@%$Erbzzztt-EINVAL db + * Since we're not Bugs Bunny ..uh..err.. avoiding 64bit arithmetic, 24bit + * is pretty much sufficient for our signed integer processing. + */ +#define PCM_READ_S32_LE(b8) (_PCM_READ_S32_LE(b8) >> PCM_FXSHIFT) +#define PCM_READ_S32_BE(b8) (_PCM_READ_S32_BE(b8) >> PCM_FXSHIFT) +#define PCM_READ_U32_LE(b8) (_PCM_READ_U32_LE(b8) >> PCM_FXSHIFT) +#define PCM_READ_U32_BE(b8) (_PCM_READ_U32_BE(b8) >> PCM_FXSHIFT) + +#define PCM_WRITE_S32_LE(b8, val) \ + _PCM_WRITE_S32_LE(b8, (val) << PCM_FXSHIFT) +#define PCM_WRITE_S32_BE(b8, val) \ + _PCM_WRITE_S32_BE(b8, (val) << PCM_FXSHIFT) +#define PCM_WRITE_U32_LE(b8, val) \ + _PCM_WRITE_U32_LE(b8, (val) << PCM_FXSHIFT) +#define PCM_WRITE_U32_BE(b8, val) \ + _PCM_WRITE_U32_BE(b8, (val) << PCM_FXSHIFT) +#endif + +#define PCM_CLAMP_S8(val) \ + (((val) > PCM_S8_MAX) ? PCM_S8_MAX : \ + (((val) < PCM_S8_MIN) ? PCM_S8_MIN : (val))) +#define PCM_CLAMP_S16(val) \ + (((val) > PCM_S16_MAX) ? PCM_S16_MAX : \ + (((val) < PCM_S16_MIN) ? PCM_S16_MIN : (val))) +#define PCM_CLAMP_S24(val) \ + (((val) > PCM_S24_MAX) ? PCM_S24_MAX : \ + (((val) < PCM_S24_MIN) ? PCM_S24_MIN : (val))) + +#ifdef PCM_USE_64BIT_ARITH +#define PCM_CLAMP_S32(val) \ + (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \ + (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val))) +#else +#define PCM_CLAMP_S32(val) \ + (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \ + (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \ + ((val) << PCM_FXSHIFT))) +#endif + +#define PCM_CLAMP_U8(val) PCM_CLAMP_S8(val) +#define PCM_CLAMP_U16(val) PCM_CLAMP_S16(val) +#define PCM_CLAMP_U24(val) PCM_CLAMP_S24(val) +#define PCM_CLAMP_U32(val) PCM_CLAMP_S32(val) + +#endif /* !_SND_PCM_H_ */ --- sys/dev/sound/pcm/sndstat.c.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/sndstat.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2001 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Copyright (c) 2001 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,11 +26,9 @@ */ #include -#include +#include #include -#ifdef USING_MUTEX #include -#endif SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/sndstat.c,v 1.28 2007/06/16 03:37:28 ariff Exp $"); @@ -60,9 +59,7 @@ int type, unit; }; -#ifdef USING_MUTEX static struct mtx sndstat_lock; -#endif static struct sbuf sndstat_sbuf; static struct cdev *sndstat_dev = NULL; static int sndstat_bufptr = -1; @@ -81,11 +78,7 @@ static SLIST_HEAD(, sndstat_entry) sndstat_devlist = SLIST_HEAD_INITIALIZER(none); int snd_verbose = 1; -#ifdef USING_MUTEX TUNABLE_INT("hw.snd.verbose", &snd_verbose); -#else -TUNABLE_INT_DECL("hw.snd.verbose", 1, snd_verbose); -#endif #ifdef SND_DEBUG static int @@ -354,7 +347,7 @@ int i, j; sbuf_printf(s, "FreeBSD Audio Driver (newpcm: %ubit %d/%s)\n", - (u_int)sizeof(intpcm_t) << 3, SND_DRV_VERSION, MACHINE_ARCH); + (u_int)sizeof(intpcm32_t) << 3, SND_DRV_VERSION, MACHINE_ARCH); if (SLIST_EMPTY(&sndstat_devlist)) { sbuf_printf(s, "No devices installed.\n"); sbuf_finish(s); --- sys/dev/sound/pcm/sndstat.h.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/sndstat.h 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2007-2009 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. + * + * $FreeBSD$ + */ + +#ifndef _SND_SNDSTAT_H_ +#define _SND_SNDSTAT_H_ + +#define SNDSTAT_PREPARE_PCM_ARGS \ + struct sbuf *s, device_t dev, int verbose + +#define SNDSTAT_PREPARE_PCM_BEGIN() do { \ + struct snddev_info *d; \ + struct pcm_channel *c; \ + struct pcm_feeder *f; \ + \ + if (verbose < 1) \ + return (0); \ + \ + d = device_get_softc(dev); \ + PCM_BUSYASSERT(d); \ + \ + if (CHN_EMPTY(d, channels.pcm)) { \ + sbuf_printf(s, " (mixer only)"); \ + return (0); \ + } \ + \ + sbuf_printf(s, " (%dp:%dv/%dr:%dv channels %splex%s)", \ + d->playcount, d->pvchancount, d->reccount, d->rvchancount, \ + (d->flags & SD_F_SIMPLEX) ? "sim" : "du", \ + (device_get_unit(dev) == snd_unit) ? " default" : "") + + +#define SNDSTAT_PREPARE_PCM_END() \ + if (verbose <= 1) \ + return (0); \ + \ + CHN_FOREACH(c, d, channels.pcm) { \ + \ + KASSERT(c->bufhard != NULL && c->bufsoft != NULL, \ + ("hosed pcm channel setup")); \ + \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "%s[%s]: ", \ + (c->parentchannel != NULL) ? \ + c->parentchannel->name : "", c->name); \ + sbuf_printf(s, "spd %d", c->speed); \ + if (c->speed != sndbuf_getspd(c->bufhard)) \ + sbuf_printf(s, "/%d", \ + sndbuf_getspd(c->bufhard)); \ + sbuf_printf(s, ", fmt 0x%08x", c->format); \ + if (c->format != sndbuf_getfmt(c->bufhard)) \ + sbuf_printf(s, "/0x%08x", \ + sndbuf_getfmt(c->bufhard)); \ + sbuf_printf(s, ", flags 0x%08x, 0x%08x", \ + c->flags, c->feederflags); \ + if (c->pid != -1) \ + sbuf_printf(s, ", pid %d (%s)", \ + c->pid, c->comm); \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "interrupts %d, ", c->interrupts); \ + \ + if (c->direction == PCMDIR_REC) \ + sbuf_printf(s, \ + "overruns %d, feed %u, hfree %d, " \ + "sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", \ + c->xruns, c->feedcount, \ + sndbuf_getfree(c->bufhard), \ + sndbuf_getfree(c->bufsoft), \ + sndbuf_getsize(c->bufhard), \ + sndbuf_getblksz(c->bufhard), \ + sndbuf_getblkcnt(c->bufhard), \ + sndbuf_getsize(c->bufsoft), \ + sndbuf_getblksz(c->bufsoft), \ + sndbuf_getblkcnt(c->bufsoft)); \ + else \ + sbuf_printf(s, \ + "underruns %d, feed %u, ready %d " \ + "[b:%d/%d/%d|bs:%d/%d/%d]", \ + c->xruns, c->feedcount, \ + sndbuf_getready(c->bufsoft), \ + sndbuf_getsize(c->bufhard), \ + sndbuf_getblksz(c->bufhard), \ + sndbuf_getblkcnt(c->bufhard), \ + sndbuf_getsize(c->bufsoft), \ + sndbuf_getblksz(c->bufsoft), \ + sndbuf_getblkcnt(c->bufsoft)); \ + sbuf_printf(s, "\n\t"); \ + \ + sbuf_printf(s, "{%s}", \ + (c->direction == PCMDIR_REC) ? "hardware" : \ + "userland"); \ + sbuf_printf(s, " -> "); \ + f = c->feeder; \ + while (f->source != NULL) \ + f = f->source; \ + while (f != NULL) { \ + sbuf_printf(s, "%s", f->class->name); \ + if (f->desc->type == FEEDER_FORMAT) \ + sbuf_printf(s, "(0x%08x -> 0x%08x)", \ + f->desc->in, f->desc->out); \ + else if (f->desc->type == FEEDER_MATRIX) \ + sbuf_printf(s, "(%d.%d -> %d.%d)", \ + AFMT_CHANNEL(f->desc->in) - \ + AFMT_EXTCHANNEL(f->desc->in), \ + AFMT_EXTCHANNEL(f->desc->in), \ + AFMT_CHANNEL(f->desc->out) - \ + AFMT_EXTCHANNEL(f->desc->out), \ + AFMT_EXTCHANNEL(f->desc->out)); \ + else if (f->desc->type == FEEDER_RATE) \ + sbuf_printf(s, \ + "(0x%08x q:%d %d -> %d)", \ + f->desc->out, \ + FEEDER_GET(f, FEEDRATE_QUALITY), \ + FEEDER_GET(f, FEEDRATE_SRC), \ + FEEDER_GET(f, FEEDRATE_DST)); \ + else \ + sbuf_printf(s, "(0x%08x)", \ + f->desc->out); \ + sbuf_printf(s, " -> "); \ + f = f->parent; \ + } \ + sbuf_printf(s, "{%s}", \ + (c->direction == PCMDIR_REC) ? "userland" : \ + "hardware"); \ + } \ + \ + return (0); \ +} while (0) + +#endif /* !_SND_SNDSTAT_H_ */ --- sys/dev/sound/pcm/sound.c.orig 2009-01-17 19:31:45.000000000 +0800 +++ sys/dev/sound/pcm/sound.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,7 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Portions Copyright (c) Ryan Beasley - GSoC 2006 + * Copyright (c) 1999 Cameron Grant * (C) 1997 Luigi Rizzo * All rights reserved. * @@ -29,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -41,10 +44,8 @@ int pcm_veto_load = 1; -#ifdef USING_DEVFS int snd_unit = -1; TUNABLE_INT("hw.snd.default_unit", &snd_unit); -#endif static int snd_unit_auto = 0; TUNABLE_INT("hw.snd.default_auto", &snd_unit_auto); @@ -66,83 +67,56 @@ static const char snd_driver_version[] = __XSTRING(SND_DRV_VERSION)"/"MACHINE_ARCH; SYSCTL_STRING(_hw_snd, OID_AUTO, version, CTLFLAG_RD, &snd_driver_version, - 0, "Driver version/arch"); + 0, "driver version/arch"); /** * @brief Unit number allocator for syncgroup IDs */ struct unrhdr *pcmsg_unrhdr = NULL; -static int sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); +static int +sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS) +{ + SNDSTAT_PREPARE_PCM_BEGIN(); + SNDSTAT_PREPARE_PCM_END(); +} void * snd_mtxcreate(const char *desc, const char *type) { -#ifdef USING_MUTEX struct mtx *m; m = malloc(sizeof(*m), M_DEVBUF, M_WAITOK | M_ZERO); mtx_init(m, desc, type, MTX_DEF); return m; -#else - return (void *)0xcafebabe; -#endif } void snd_mtxfree(void *m) { -#ifdef USING_MUTEX struct mtx *mtx = m; - /* mtx_assert(mtx, MA_OWNED); */ mtx_destroy(mtx); free(mtx, M_DEVBUF); -#endif } void snd_mtxassert(void *m) { -#ifdef USING_MUTEX #ifdef INVARIANTS struct mtx *mtx = m; mtx_assert(mtx, MA_OWNED); #endif -#endif } -/* -void -snd_mtxlock(void *m) -{ -#ifdef USING_MUTEX - struct mtx *mtx = m; - mtx_lock(mtx); -#endif -} - -void -snd_mtxunlock(void *m) -{ -#ifdef USING_MUTEX - struct mtx *mtx = m; - - mtx_unlock(mtx); -#endif -} -*/ int snd_setup_intr(device_t dev, struct resource *res, int flags, driver_intr_t hand, void *param, void **cookiep) { struct snddev_info *d; -#ifdef USING_MUTEX + flags &= INTR_MPSAFE; flags |= INTR_TYPE_AV; -#else - flags = INTR_TYPE_AV; -#endif d = device_get_softc(dev); if (d != NULL && (flags & INTR_MPSAFE)) d->flags |= SD_F_MPSAFE; @@ -154,20 +128,6 @@ hand, param, cookiep); } -#ifndef PCM_DEBUG_MTX -void -pcm_lock(struct snddev_info *d) -{ - snd_mtxlock(d->lock); -} - -void -pcm_unlock(struct snddev_info *d) -{ - snd_mtxunlock(d->lock); -} -#endif - struct pcm_channel * pcm_getfakechan(struct snddev_info *d) { @@ -192,11 +152,12 @@ (void)snd_clone_setmaxunit(d->clones, cmax); } -static int +int pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num) { struct pcm_channel *c, *ch, *nch; - int err, vcnt; + struct pcmchan_caps *caps; + int i, err, vcnt; PCM_BUSYASSERT(d); @@ -228,9 +189,33 @@ CHN_LOCK(c); if (c->direction == direction && ((c->flags & CHN_F_HAS_VCHAN) || (vcnt == 0 && + c->refcount < 1 && !(c->flags & (CHN_F_BUSY | CHN_F_VIRTUAL))))) { - ch = c; - break; + /* + * Reuse hw channel with vchans already + * created. + */ + if (c->flags & CHN_F_HAS_VCHAN) { + ch = c; + break; + } + /* + * No vchans ever created, look for + * channels with supported formats. + */ + caps = chn_getcaps(c); + if (caps == NULL) { + CHN_UNLOCK(c); + continue; + } + for (i = 0; caps->fmtlist[i] != 0; i++) { + if (caps->fmtlist[i] & AFMT_CONVERTIBLE) + break; + } + if (caps->fmtlist[i] != 0) { + ch = c; + break; + } } CHN_UNLOCK(c); } @@ -267,11 +252,12 @@ } CHN_FOREACH_SAFE(ch, c, nch, children) { CHN_LOCK(ch); - if (!(ch->flags & CHN_F_BUSY)) { + if (vcnt == 1 && c->refcount > 0) { CHN_UNLOCK(ch); - CHN_UNLOCK(c); + break; + } if (!(ch->flags & CHN_F_BUSY) && + ch->refcount < 1) { err = vchan_destroy(ch); - CHN_LOCK(c); if (err == 0) vcnt--; } else @@ -291,10 +277,10 @@ /* return error status and a locked channel */ int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, - pid_t pid, int devunit) + pid_t pid, char *comm, int devunit) { struct pcm_channel *c; - int err, vchancount; + int err, vchancount, vchan_num; KASSERT(d != NULL && ch != NULL && (devunit == -1 || !(devunit & ~(SND_U_MASK | SND_D_MASK | SND_C_MASK))) && @@ -324,15 +310,31 @@ } } + *ch = NULL; + vchan_num = 0; + vchancount = (direction == PCMDIR_PLAY) ? d->pvchancount : + d->rvchancount; + retry_chnalloc: err = EOPNOTSUPP; /* scan for a free channel */ CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); + if (devunit == -1 && c->direction == direction && + (c->flags & CHN_F_VIRTUAL)) { + if (vchancount < snd_maxautovchans && + vchan_num < CHN_CHAN(c)) { + CHN_UNLOCK(c); + goto vchan_alloc; + } + vchan_num++; + } if (c->direction == direction && !(c->flags & CHN_F_BUSY) && (devunit == -1 || devunit == -2 || c->unit == devunit)) { c->flags |= CHN_F_BUSY; c->pid = pid; + strlcpy(c->comm, (comm != NULL) ? comm : + CHN_COMM_UNKNOWN, sizeof(c->comm)); *ch = c; return (0); } else if (c->unit == devunit) { @@ -353,13 +355,10 @@ if (devunit == -2) return (err); +vchan_alloc: /* no channel available */ if (devunit == -1 || snd_unit2d(devunit) == SND_DEV_DSPHW_VPLAY || snd_unit2d(devunit) == SND_DEV_DSPHW_VREC) { - if (direction == PCMDIR_PLAY) - vchancount = d->pvchancount; - else - vchancount = d->rvchancount; if (!(vchancount > 0 && vchancount < snd_maxautovchans) && (devunit == -1 || snd_unit2c(devunit) < snd_maxautovchans)) return (err); @@ -384,6 +383,7 @@ c->flags &= ~CHN_F_BUSY; c->pid = -1; + strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); CHN_UNLOCK(c); return (0); @@ -431,7 +431,6 @@ pcm_clonereset(d); } -#ifdef USING_DEVFS static int sysctl_hw_snd_default_unit(SYSCTL_HANDLER_ARGS) { @@ -451,7 +450,6 @@ /* XXX: do we need a way to let the user change the default unit? */ SYSCTL_PROC(_hw_snd, OID_AUTO, default_unit, CTLTYPE_INT | CTLFLAG_RW, 0, sizeof(int), sysctl_hw_snd_default_unit, "I", "default sound device"); -#endif static int sysctl_hw_snd_maxautovchans(SYSCTL_HANDLER_ARGS) @@ -570,11 +568,12 @@ return (NULL); } - pcm_unlock(d); + PCM_UNLOCK(d); ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); ch->methods = kobj_create(cls, M_DEVBUF, M_WAITOK | M_ZERO); ch->unit = udc; ch->pid = -1; + strlcpy(ch->comm, CHN_COMM_UNUSED, sizeof(ch->comm)); ch->parentsnddev = d; ch->parentchannel = parent; ch->dev = d->dev; @@ -583,7 +582,7 @@ device_get_nameunit(ch->dev), dirs, devname); err = chn_init(ch, devinfo, dir, direction); - pcm_lock(d); + PCM_LOCK(d); if (err) { device_printf(d->dev, "chn_init(%s) failed: err = %d\n", ch->name, err); @@ -620,46 +619,12 @@ int pcm_chn_add(struct snddev_info *d, struct pcm_channel *ch) { - struct pcm_channel *tmp, *after; - int num; - PCM_BUSYASSERT(d); snd_mtxassert(d->lock); KASSERT(ch != NULL && (ch->direction == PCMDIR_PLAY || ch->direction == PCMDIR_REC), ("Invalid pcm channel")); - after = NULL; - tmp = NULL; - num = 0; - - /* - * Look for possible device collision. - */ - CHN_FOREACH(tmp, d, channels.pcm) { - if (tmp->unit == ch->unit) { - device_printf(d->dev, "%s(): Device collision " - "old=%p new=%p devunit=0x%08x\n", - __func__, tmp, ch, ch->unit); - return (ENODEV); - } - if (CHN_DEV(tmp) < CHN_DEV(ch)) { - if (num == 0) - after = tmp; - continue; - } else if (CHN_DEV(tmp) > CHN_DEV(ch)) - break; - num++; - if (CHN_CHAN(tmp) < CHN_CHAN(ch)) - after = tmp; - else if (CHN_CHAN(tmp) > CHN_CHAN(ch)) - break; - } - - if (after != NULL) { - CHN_INSERT_AFTER(after, ch, channels.pcm); - } else { - CHN_INSERT_HEAD(d, ch, channels.pcm); - } + CHN_INSERT_SORT_ASCEND(d, ch, channels.pcm); switch (CHN_DEV(ch)) { case SND_DEV_DSPHW_PLAY: @@ -734,17 +699,17 @@ PCM_BUSYASSERT(d); - pcm_lock(d); + PCM_LOCK(d); ch = pcm_chn_create(d, NULL, cls, dir, -1, devinfo); if (!ch) { device_printf(d->dev, "pcm_chn_create(%s, %d, %p) failed\n", cls->name, dir, devinfo); - pcm_unlock(d); + PCM_UNLOCK(d); return (ENODEV); } err = pcm_chn_add(d, ch); - pcm_unlock(d); + PCM_UNLOCK(d); if (err) { device_printf(d->dev, "pcm_chn_add(%s) failed, err=%d\n", ch->name, err); @@ -765,9 +730,9 @@ ch = CHN_FIRST(d, channels.pcm); - pcm_lock(d); + PCM_LOCK(d); error = pcm_chn_remove(d, ch); - pcm_unlock(d); + PCM_UNLOCK(d); if (error) return (error); return (pcm_chn_destroy(ch)); @@ -793,7 +758,7 @@ strlcpy(d->status, str, SND_STATUSLEN); - pcm_lock(d); + PCM_LOCK(d); /* Last stage, enable cloning. */ if (d->clones != NULL) @@ -804,7 +769,7 @@ PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); if (snd_unit < 0 || snd_unit_auto != 0) snd_unit = device_get_unit(dev); @@ -866,7 +831,44 @@ return sz; } -#if defined(SND_DYNSYSCTL) && defined(SND_DEBUG) +static int +sysctl_dev_pcm_bitperfect(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + int err, val; + + d = oidp->oid_arg1; + if (!PCM_REGISTERED(d)) + return (ENODEV); + + PCM_LOCK(d); + PCM_WAIT(d); + val = (d->flags & SD_F_BITPERFECT) ? 1 : 0; + PCM_ACQUIRE(d); + PCM_UNLOCK(d); + + err = sysctl_handle_int(oidp, &val, 0, req); + + if (err == 0 && req->newptr != NULL) { + if (!(val == 0 || val == 1)) { + PCM_RELEASE_QUICK(d); + return (EINVAL); + } + + PCM_LOCK(d); + + d->flags &= ~SD_F_BITPERFECT; + d->flags |= (val != 0) ? SD_F_BITPERFECT : 0; + + PCM_RELEASE(d); + PCM_UNLOCK(d); + } else + PCM_RELEASE_QUICK(d); + + return (err); +} + +#ifdef SND_DEBUG static int sysctl_dev_pcm_clone_flags(SYSCTL_HANDLER_ARGS) { @@ -981,6 +983,7 @@ pcm_register(device_t dev, void *devinfo, int numplay, int numrec) { struct snddev_info *d; + int i; if (pcm_veto_load) { device_printf(dev, "disabled due to an error while initialising: %d\n", pcm_veto_load); @@ -1010,6 +1013,15 @@ */ d->flags = 0; #endif + i = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "vpc", &i) != 0 || i != 0) + d->flags |= SD_F_VPC; + + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "bitperfect", &i) == 0 && i != 0) + d->flags |= SD_F_BITPERFECT; + d->devinfo = devinfo; d->devcount = 0; d->reccount = 0; @@ -1041,6 +1053,7 @@ CHN_INIT(d, channels.pcm); CHN_INIT(d, channels.pcm.busy); + CHN_INIT(d, channels.pcm.opened); /* XXX This is incorrect, but lets play along for now. */ if ((numplay == 0 || numrec == 0) && numplay != numrec) @@ -1049,7 +1062,6 @@ d->fakechan = fkchan_setup(dev); chn_init(d->fakechan, NULL, 0, 0); -#ifdef SND_DYNSYSCTL sysctl_ctx_init(&d->play_sysctl_ctx); d->play_sysctl_tree = SYSCTL_ADD_NODE(&d->play_sysctl_ctx, SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "play", @@ -1063,6 +1075,11 @@ SYSCTL_ADD_INT(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "buffersize", CTLFLAG_RD, &d->bufsz, 0, "allocated buffer size"); + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, + "bitperfect", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), + sysctl_dev_pcm_bitperfect, "I", + "bit-perfect playback/recording (0=disable, 1=enable)"); #ifdef SND_DEBUG SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, @@ -1080,13 +1097,15 @@ sysctl_dev_pcm_clone_gc, "I", "clone garbage collector"); #endif -#endif if (numplay > 0 || numrec > 0) { d->flags |= SD_F_AUTOVCHAN; vchan_initsys(dev); } + if (d->flags & SD_F_EQ) + feeder_eq_initsys(dev); + sndstat_register(dev, d->status, sndstat_prepare_pcm); return 0; @@ -1113,18 +1132,18 @@ return (EBUSY); } - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); if (d->inprog != 0) { device_printf(dev, "unregister: operation in progress\n"); - pcm_unlock(d); + PCM_UNLOCK(d); sndstat_release(td); return (EBUSY); } PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); CHN_FOREACH(ch, d, channels.pcm) { CHN_LOCK(ch); @@ -1147,27 +1166,27 @@ sndstat_release(td); return (EBUSY); } else { - pcm_lock(d); + PCM_LOCK(d); (void)snd_clone_disable(d->clones); - pcm_unlock(d); + PCM_UNLOCK(d); } } if (mixer_uninit(dev) == EBUSY) { device_printf(dev, "unregister: mixer busy\n"); - pcm_lock(d); + PCM_LOCK(d); if (d->clones != NULL) (void)snd_clone_enable(d->clones); PCM_RELEASE(d); - pcm_unlock(d); + PCM_UNLOCK(d); sndstat_release(td); return (EBUSY); } - pcm_lock(d); + PCM_LOCK(d); d->flags |= SD_F_DYING; d->flags &= ~SD_F_REGISTERED; - pcm_unlock(d); + PCM_UNLOCK(d); /* * No lock being held, so this thing can be flushed without @@ -1178,7 +1197,6 @@ d->clones = NULL; } -#ifdef SND_DYNSYSCTL if (d->play_sysctl_tree != NULL) { sysctl_ctx_free(&d->play_sysctl_ctx); d->play_sysctl_tree = NULL; @@ -1187,7 +1205,6 @@ sysctl_ctx_free(&d->rec_sysctl_ctx); d->rec_sysctl_tree = NULL; } -#endif while (!CHN_EMPTY(d, channels.pcm)) pcm_killchan(dev); @@ -1197,10 +1214,10 @@ dsp_cdevinfo_flush(d); - pcm_lock(d); + PCM_LOCK(d); PCM_RELEASE(d); cv_destroy(&d->cv); - pcm_unlock(d); + PCM_UNLOCK(d); snd_mtxfree(d->lock); sndstat_unregister(dev); sndstat_release(td); @@ -1228,106 +1245,8 @@ /************************************************************************/ -static int -sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) -{ - struct snddev_info *d; - struct pcm_channel *c; - struct pcm_feeder *f; - - if (verbose < 1) - return 0; - - d = device_get_softc(dev); - if (!d) - return ENXIO; - - PCM_BUSYASSERT(d); - - if (CHN_EMPTY(d, channels.pcm)) { - sbuf_printf(s, " (mixer only)"); - return 0; - } - - sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", - d->playcount, d->pvchancount, - d->reccount, d->rvchancount, - (d->flags & SD_F_SIMPLEX)? "" : " duplex", -#ifdef USING_DEVFS - (device_get_unit(dev) == snd_unit)? " default" : "" -#else - "" -#endif - ); - - if (verbose <= 1) - return 0; - - CHN_FOREACH(c, d, channels.pcm) { - - KASSERT(c->bufhard != NULL && c->bufsoft != NULL, - ("hosed pcm channel setup")); - - sbuf_printf(s, "\n\t"); - - /* it would be better to indent child channels */ - sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); - sbuf_printf(s, "spd %d", c->speed); - if (c->speed != sndbuf_getspd(c->bufhard)) - sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); - sbuf_printf(s, ", fmt 0x%08x", c->format); - if (c->format != sndbuf_getfmt(c->bufhard)) - sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); - sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); - if (c->pid != -1) - sbuf_printf(s, ", pid %d", c->pid); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "interrupts %d, ", c->interrupts); - if (c->direction == PCMDIR_REC) - sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - else - sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); - sbuf_printf(s, " -> "); - f = c->feeder; - while (f->source != NULL) - f = f->source; - while (f != NULL) { - sbuf_printf(s, "%s", f->class->name); - if (f->desc->type == FEEDER_FMT) - sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); - if (f->desc->type == FEEDER_RATE) - sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); - if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || - f->desc->type == FEEDER_VOLUME) - sbuf_printf(s, "(0x%08x)", f->desc->out); - sbuf_printf(s, " -> "); - f = f->parent; - } - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); - } - - return 0; -} - -/************************************************************************/ - -#ifdef SND_DYNSYSCTL int -sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS) +sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; int direction, vchancount; @@ -1337,7 +1256,7 @@ if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) return (EINVAL); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { @@ -1352,18 +1271,18 @@ cnt = d->reccount; break; default: - pcm_unlock(d); + PCM_UNLOCK(d); return (EINVAL); break; } if (cnt < 1) { - pcm_unlock(d); + PCM_UNLOCK(d); return (ENODEV); } PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); cnt = vchancount; err = sysctl_handle_int(oidp, &cnt, 0, req); @@ -1380,7 +1299,6 @@ return err; } -#endif /************************************************************************/ @@ -1438,14 +1356,14 @@ /* XXX Need Giant magic entry ??? */ /* See note in function's docblock */ - mtx_assert(d->lock, MA_NOTOWNED); - pcm_lock(d); + PCM_UNLOCKASSERT(d); + PCM_LOCK(d); si->numaudios += d->devcount; ++ncards; CHN_FOREACH(c, d, channels.pcm) { - mtx_assert(c->lock, MA_NOTOWNED); + CHN_UNLOCKASSERT(c); CHN_LOCK(c); if (c->flags & CHN_F_BUSY) si->openedaudio[j / intnbits] |= @@ -1454,7 +1372,7 @@ j++; } - pcm_unlock(d); + PCM_UNLOCK(d); } si->numaudioengines = si->numaudios; @@ -1510,8 +1428,8 @@ if (ncards++ != si->card) continue; - mtx_assert(d->lock, MA_NOTOWNED); - pcm_lock(d); + PCM_UNLOCKASSERT(d); + PCM_LOCK(d); strlcpy(si->shortname, device_get_nameunit(d->dev), sizeof(si->shortname)); @@ -1519,7 +1437,9 @@ sizeof(si->longname)); strlcpy(si->hw_info, d->status, sizeof(si->hw_info)); si->intr_count = si->ack_count = 0; - pcm_unlock(d); + + PCM_UNLOCK(d); + return (0); } return (ENXIO); --- sys/dev/sound/pcm/sound.h.orig 2009-01-17 19:31:45.000000000 +0800 +++ sys/dev/sound/pcm/sound.h 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,6 @@ /*- - * Copyright (c) 1999 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Copyright (c) 1999 Cameron Grant * Copyright by Hannu Savolainen 1995 * All rights reserved. * @@ -68,29 +69,18 @@ #include #include -#undef USING_MUTEX -#undef USING_DEVFS - -#if __FreeBSD_version > 500000 #include #include #include -#define USING_MUTEX -#define USING_DEVFS -#else -#define INTR_TYPE_AV INTR_TYPE_TTY -#define INTR_MPSAFE 0 -#endif - -#define SND_DYNSYSCTL - struct pcm_channel; struct pcm_feeder; struct snd_dbuf; struct snd_mixer; #include +#include +#include #include #include #include @@ -102,7 +92,7 @@ #define SND_STATUSLEN 64 -#define SOUND_MODVER 2 +#define SOUND_MODVER 5 #define SOUND_MINVER SOUND_MODVER #define SOUND_PREFVER SOUND_MODVER @@ -134,13 +124,28 @@ #define SD_F_SIMPLEX 0x00000001 #define SD_F_AUTOVCHAN 0x00000002 #define SD_F_SOFTPCMVOL 0x00000004 +/* + * Obsolete due to better matrixing + */ +#if 0 #define SD_F_PSWAPLR 0x00000008 #define SD_F_RSWAPLR 0x00000010 -#define SD_F_DYING 0x00000020 -#define SD_F_SUICIDE 0x00000040 -#define SD_F_BUSY 0x00000080 -#define SD_F_MPSAFE 0x00000100 -#define SD_F_REGISTERED 0x00000200 +#endif +#define SD_F_DYING 0x00000008 +#define SD_F_SUICIDE 0x00000010 +#define SD_F_BUSY 0x00000020 +#define SD_F_MPSAFE 0x00000040 +#define SD_F_REGISTERED 0x00000080 +#define SD_F_BITPERFECT 0x00000100 +#define SD_F_VPC 0x00000200 /* volume-per-channel */ +#define SD_F_EQ 0x00000400 /* EQ */ +#define SD_F_EQ_ENABLED 0x00000800 /* EQ enabled */ +#define SD_F_EQ_BYPASSED 0x00001000 /* EQ bypassed */ +#define SD_F_EQ_PC 0x00002000 /* EQ per-channel */ + +#define SD_F_EQ_DEFAULT (SD_F_EQ | SD_F_EQ_ENABLED) +#define SD_F_EQ_MASK (SD_F_EQ | SD_F_EQ_ENABLED | \ + SD_F_EQ_BYPASSED | SD_F_EQ_PC) #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 @@ -158,290 +163,90 @@ (((var)<(low))? (low) : ((var)>(high))? (high) : (var)) #define DSP_BUFFSIZE (8192) -/* - * Macros for reading/writing PCM sample / int values from bytes array. - * Since every process is done using signed integer (and to make our life - * less miserable), unsigned sample will be converted to its signed - * counterpart and restored during writing back. To avoid overflow, - * we truncate 32bit (and only 32bit) samples down to 24bit (see below - * for the reason), unless PCM_USE_64BIT_ARITH is defined. - */ +/* make figuring out what a format is easier. got AFMT_STEREO already */ +#define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE) +#define AFMT_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE) +#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE) +#define AFMT_G711 (AFMT_MU_LAW | AFMT_A_LAW) +#define AFMT_8BIT (AFMT_G711 | AFMT_U8 | AFMT_S8) +#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | \ + AFMT_S16_LE | AFMT_S16_BE | AFMT_S8) +#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_S24_BE | AFMT_U24_BE | \ + AFMT_S16_BE | AFMT_U16_BE) -/* - * Automatically turn on 64bit arithmetic on suitable archs - * (amd64 64bit, ia64, etc..) for wider 32bit samples / integer processing. - */ -#if LONG_BIT >= 64 -#undef PCM_USE_64BIT_ARITH -#define PCM_USE_64BIT_ARITH 1 -#else -#if 0 -#undef PCM_USE_64BIT_ARITH -#define PCM_USE_64BIT_ARITH 1 -#endif -#endif +#define AFMT_BIT(v) (((v) & AFMT_32BIT) ? 32 : \ + (((v) & AFMT_24BIT) ? 24 : \ + (((v) & AFMT_16BIT) ? 16 : 8))) -#ifdef PCM_USE_64BIT_ARITH -typedef int64_t intpcm_t; -#else -typedef int32_t intpcm_t; -#endif +#define AFMT_BPS(v) (AFMT_BIT(v) >> 3) -/* 32bit fixed point shift */ -#define PCM_FXSHIFT 8 +#define AFMT_CONVERTIBLE (AFMT_8BIT | AFMT_16BIT | AFMT_24BIT | \ + AFMT_32BIT) -#define PCM_S8_MAX 0x7f -#define PCM_S8_MIN -0x80 -#define PCM_S16_MAX 0x7fff -#define PCM_S16_MIN -0x8000 -#define PCM_S24_MAX 0x7fffff -#define PCM_S24_MIN -0x800000 -#ifdef PCM_USE_64BIT_ARITH -#if LONG_BIT >= 64 -#define PCM_S32_MAX 0x7fffffffL -#define PCM_S32_MIN -0x80000000L -#else -#define PCM_S32_MAX 0x7fffffffLL -#define PCM_S32_MIN -0x80000000LL -#endif -#else -#define PCM_S32_MAX 0x7fffffff -#define PCM_S32_MIN (-0x7fffffff - 1) -#endif +/* + * We're simply using unused, contiguous bits from various AFMT_ definitions. + * ~(0xb00ff7ff) + */ +#define AFMT_ENCODING_MASK 0xf00fffff +#define AFMT_CHANNEL_MASK 0x01f00000 +#define AFMT_CHANNEL_SHIFT 20 +#define AFMT_EXTCHANNEL_MASK 0x0e000000 +#define AFMT_EXTCHANNEL_SHIFT 25 -/* Bytes-per-sample definition */ -#define PCM_8_BPS 1 -#define PCM_16_BPS 2 -#define PCM_24_BPS 3 -#define PCM_32_BPS 4 +#define AFMT_ENCODING(v) ((v) & AFMT_ENCODING_MASK) -#if BYTE_ORDER == LITTLE_ENDIAN -#define PCM_READ_S16_LE(b8) *((int16_t *)(b8)) -#define _PCM_READ_S32_LE(b8) *((int32_t *)(b8)) -#define PCM_READ_S16_BE(b8) \ - ((int32_t)((b8)[1] | ((int8_t)((b8)[0])) << 8)) -#define _PCM_READ_S32_BE(b8) \ - ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ - ((int8_t)((b8)[0])) << 24)) - -#define PCM_WRITE_S16_LE(b8, val) *((int16_t *)(b8)) = (val) -#define _PCM_WRITE_S32_LE(b8, val) *((int32_t *)(b8)) = (val) -#define PCM_WRITE_S16_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[1] = val; \ - b8[0] = val >> 8; \ - } while(0) -#define _PCM_WRITE_S32_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[3] = val; \ - b8[2] = val >> 8; \ - b8[1] = val >> 16; \ - b8[0] = val >> 24; \ - } while(0) +#define AFMT_EXTCHANNEL(v) (((v) & AFMT_EXTCHANNEL_MASK) >> \ + AFMT_EXTCHANNEL_SHIFT) -#define PCM_READ_U16_LE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) -#define _PCM_READ_U32_LE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) -#define PCM_READ_U16_BE(b8) \ - ((int32_t)((b8)[1] | ((int8_t)((b8)[0] ^ 0x80)) << 8)) -#define _PCM_READ_U32_BE(b8) \ - ((int32_t)((b8)[3] | (b8)[2] << 8 | (b8)[1] << 16 | \ - ((int8_t)((b8)[0] ^ 0x80)) << 24)) - -#define PCM_WRITE_U16_LE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000 -#define _PCM_WRITE_U32_LE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 -#define PCM_WRITE_U16_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[1] = val; \ - b8[0] = (val >> 8) ^ 0x80; \ - } while(0) -#define _PCM_WRITE_U32_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[3] = val; \ - b8[2] = val >> 8; \ - b8[1] = val >> 16; \ - b8[0] = (val >> 24) ^ 0x80; \ - } while(0) -#else /* !LITTLE_ENDIAN */ -#define PCM_READ_S16_LE(b8) \ - ((int32_t)((b8)[0] | ((int8_t)((b8)[1])) << 8)) -#define _PCM_READ_S32_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ - ((int8_t)((b8)[3])) << 24)) -#define PCM_READ_S16_BE(b8) *((int16_t *)(b8)) -#define _PCM_READ_S32_BE(b8) *((int32_t *)(b8)) - -#define PCM_WRITE_S16_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - } while(0) -#define _PCM_WRITE_S32_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - b8[2] = val >> 16; \ - b8[3] = val >> 24; \ - } while(0) -#define PCM_WRITE_S16_BE(b8, val) *((int16_t *)(b8)) = (val) -#define _PCM_WRITE_S32_BE(b8, val) *((int32_t *)(b8)) = (val) +#define AFMT_CHANNEL(v) (((v) & AFMT_CHANNEL_MASK) >> \ + AFMT_CHANNEL_SHIFT) -#define PCM_READ_U16_LE(b8) \ - ((int32_t)((b8)[0] | ((int8_t)((b8)[1] ^ 0x80)) << 8)) -#define _PCM_READ_U32_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | (b8)[2] << 16 | \ - ((int8_t)((b8)[3] ^ 0x80)) << 24)) -#define PCM_READ_U16_BE(b8) ((int16_t)(*((uint16_t *)(b8)) ^ 0x8000)) -#define _PCM_READ_U32_BE(b8) ((int32_t)(*((uint32_t *)(b8)) ^ 0x80000000)) - -#define PCM_WRITE_U16_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = (val >> 8) ^ 0x80; \ - } while(0) -#define _PCM_WRITE_U32_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - b8[2] = val >> 16; \ - b8[3] = (val >> 24) ^ 0x80; \ - } while(0) -#define PCM_WRITE_U16_BE(b8, val) *((uint16_t *)(b8)) = (val) ^ 0x8000 -#define _PCM_WRITE_U32_BE(b8, val) *((uint32_t *)(b8)) = (val) ^ 0x80000000 -#endif -#define PCM_READ_S24_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | ((int8_t)((b8)[2])) << 16)) -#define PCM_READ_S24_BE(b8) \ - ((int32_t)((b8)[2] | (b8)[1] << 8 | ((int8_t)((b8)[0])) << 16)) - -#define PCM_WRITE_S24_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - b8[2] = val >> 16; \ - } while(0) -#define PCM_WRITE_S24_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[2] = val; \ - b8[1] = val >> 8; \ - b8[0] = val >> 16; \ - } while(0) +#define SND_FORMAT(f, c, m) (AFMT_ENCODING(f) | \ + (((c) << AFMT_CHANNEL_SHIFT) & \ + AFMT_CHANNEL_MASK) | \ + (((m) << AFMT_EXTCHANNEL_SHIFT) & \ + AFMT_EXTCHANNEL_MASK)) -#define PCM_READ_U24_LE(b8) \ - ((int32_t)((b8)[0] | (b8)[1] << 8 | \ - ((int8_t)((b8)[2] ^ 0x80)) << 16)) -#define PCM_READ_U24_BE(b8) \ - ((int32_t)((b8)[2] | (b8)[1] << 8 | \ - ((int8_t)((b8)[0] ^ 0x80)) << 16)) - -#define PCM_WRITE_U24_LE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[0] = val; \ - b8[1] = val >> 8; \ - b8[2] = (val >> 16) ^ 0x80; \ - } while(0) -#define PCM_WRITE_U24_BE(bb8, vval) do { \ - int32_t val = (vval); \ - uint8_t *b8 = (bb8); \ - b8[2] = val; \ - b8[1] = val >> 8; \ - b8[0] = (val >> 16) ^ 0x80; \ - } while(0) +#define AFMT_ALIGN(v) (AFMT_BPS(v) * AFMT_CHANNEL(v)) -#ifdef PCM_USE_64BIT_ARITH -#define PCM_READ_S32_LE(b8) _PCM_READ_S32_LE(b8) -#define PCM_READ_S32_BE(b8) _PCM_READ_S32_BE(b8) -#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) -#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) - -#define PCM_READ_U32_LE(b8) _PCM_READ_U32_LE(b8) -#define PCM_READ_U32_BE(b8) _PCM_READ_U32_BE(b8) -#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) -#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) -#else /* !PCM_USE_64BIT_ARITH */ -/* - * 24bit integer ?!? This is quite unfortunate, eh? Get the fact straight: - * Dynamic range for: - * 1) Human =~ 140db - * 2) 16bit = 96db (close enough) - * 3) 24bit = 144db (perfect) - * 4) 32bit = 196db (way too much) - * 5) Bugs Bunny = Gazillion!@%$Erbzzztt-EINVAL db - * Since we're not Bugs Bunny ..uh..err.. avoiding 64bit arithmetic, 24bit - * is pretty much sufficient for our signed integer processing. - */ -#define PCM_READ_S32_LE(b8) (_PCM_READ_S32_LE(b8) >> PCM_FXSHIFT) -#define PCM_READ_S32_BE(b8) (_PCM_READ_S32_BE(b8) >> PCM_FXSHIFT) -#define PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, (val) << PCM_FXSHIFT) -#define PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, (val) << PCM_FXSHIFT) - -#define PCM_READ_U32_LE(b8) (_PCM_READ_U32_LE(b8) >> PCM_FXSHIFT) -#define PCM_READ_U32_BE(b8) (_PCM_READ_U32_BE(b8) >> PCM_FXSHIFT) -#define PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, (val) << PCM_FXSHIFT) -#define PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, (val) << PCM_FXSHIFT) -#endif +#define AFMT_U8_NE AFMT_U8 +#define AFMT_S8_NE AFMT_S8 -/* - * 8bit sample is pretty much useless since it doesn't provide - * sufficient dynamic range throughout our filtering process. - * For the sake of completeness, declare it anyway. - */ -#define PCM_READ_S8(b8) *((int8_t *)(b8)) -#define PCM_READ_S8_NE(b8) PCM_READ_S8(b8) -#define PCM_READ_U8(b8) ((int8_t)(*((uint8_t *)(b8)) ^ 0x80)) -#define PCM_READ_U8_NE(b8) PCM_READ_U8(b8) - -#define PCM_WRITE_S8(b8, val) *((int8_t *)(b8)) = (val) -#define PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) -#define PCM_WRITE_U8(b8, val) *((uint8_t *)(b8)) = (val) ^ 0x80 -#define PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) - -#define PCM_CLAMP_S8(val) \ - (((val) > PCM_S8_MAX) ? PCM_S8_MAX : \ - (((val) < PCM_S8_MIN) ? PCM_S8_MIN : (val))) -#define PCM_CLAMP_S16(val) \ - (((val) > PCM_S16_MAX) ? PCM_S16_MAX : \ - (((val) < PCM_S16_MIN) ? PCM_S16_MIN : (val))) -#define PCM_CLAMP_S24(val) \ - (((val) > PCM_S24_MAX) ? PCM_S24_MAX : \ - (((val) < PCM_S24_MIN) ? PCM_S24_MIN : (val))) - -#ifdef PCM_USE_64BIT_ARITH -#define PCM_CLAMP_S32(val) \ - (((val) > PCM_S32_MAX) ? PCM_S32_MAX : \ - (((val) < PCM_S32_MIN) ? PCM_S32_MIN : (val))) +#undef AFMT_S16_NE + +#if BYTE_ORDER == LITTLE_ENDIAN +#define AFMT_S16_NE AFMT_S16_LE +#define AFMT_S24_NE AFMT_S24_LE +#define AFMT_S32_NE AFMT_S32_LE +#define AFMT_U16_NE AFMT_U16_LE +#define AFMT_U24_NE AFMT_U24_LE +#define AFMT_U32_NE AFMT_U32_LE +#define AFMT_S16_OE AFMT_S16_BE +#define AFMT_S24_OE AFMT_S24_BE +#define AFMT_S32_OE AFMT_S32_BE +#define AFMT_U16_OE AFMT_U16_BE +#define AFMT_U24_OE AFMT_U24_BE +#define AFMT_U32_OE AFMT_U32_BE #else -#define PCM_CLAMP_S32(val) \ - (((val) > PCM_S24_MAX) ? PCM_S32_MAX : \ - (((val) < PCM_S24_MIN) ? PCM_S32_MIN : \ - ((val) << PCM_FXSHIFT))) +#define AFMT_S16_OE AFMT_S16_LE +#define AFMT_S24_OE AFMT_S24_LE +#define AFMT_S32_OE AFMT_S32_LE +#define AFMT_U16_OE AFMT_U16_LE +#define AFMT_U24_OE AFMT_U24_LE +#define AFMT_U32_OE AFMT_U32_LE +#define AFMT_S16_NE AFMT_S16_BE +#define AFMT_S24_NE AFMT_S24_BE +#define AFMT_S32_NE AFMT_S32_BE +#define AFMT_U16_NE AFMT_U16_BE +#define AFMT_U24_NE AFMT_U24_BE +#define AFMT_U32_NE AFMT_U32_BE #endif -#define PCM_CLAMP_U8(val) PCM_CLAMP_S8(val) -#define PCM_CLAMP_U16(val) PCM_CLAMP_S16(val) -#define PCM_CLAMP_U24(val) PCM_CLAMP_S24(val) -#define PCM_CLAMP_U32(val) PCM_CLAMP_S32(val) +#define AFMT_SIGNED_NE (AFMT_S8_NE | AFMT_S16_NE | AFMT_S24_NE | AFMT_S32_NE) -/* make figuring out what a format is easier. got AFMT_STEREO already */ -#define AFMT_32BIT (AFMT_S32_LE | AFMT_S32_BE | AFMT_U32_LE | AFMT_U32_BE) -#define AFMT_24BIT (AFMT_S24_LE | AFMT_S24_BE | AFMT_U24_LE | AFMT_U24_BE) -#define AFMT_16BIT (AFMT_S16_LE | AFMT_S16_BE | AFMT_U16_LE | AFMT_U16_BE) -#define AFMT_8BIT (AFMT_MU_LAW | AFMT_A_LAW | AFMT_U8 | AFMT_S8) -#define AFMT_SIGNED (AFMT_S32_LE | AFMT_S32_BE | AFMT_S24_LE | AFMT_S24_BE | \ - AFMT_S16_LE | AFMT_S16_BE | AFMT_S8) -#define AFMT_BIGENDIAN (AFMT_S32_BE | AFMT_U32_BE | AFMT_S24_BE | AFMT_U24_BE | \ - AFMT_S16_BE | AFMT_U16_BE) +#define AFMT_NE (AFMT_SIGNED_NE | AFMT_U8_NE | AFMT_U16_NE | \ + AFMT_U24_NE | AFMT_U32_NE) struct pcm_channel *fkchan_setup(device_t dev); int fkchan_kill(struct pcm_channel *c); @@ -475,9 +280,18 @@ #define SND_DEV_DSPHW_CD 15 /* s16le/stereo 44100Hz CD */ -#define SND_DEV_DSP_MMAP 16 /* OSSv4 compatible /dev/dsp_mmap */ +/* + * OSSv4 compatible device. For now, it serve no purpose and + * the cloning itself will forward the request to ordinary /dev/dsp + * instead. + */ +#define SND_DEV_DSP_MMAP 16 /* /dev/dsp_mmap */ +#define SND_DEV_DSP_AC3 17 /* /dev/dsp_ac3 */ +#define SND_DEV_DSP_MULTICH 18 /* /dev/dsp_multich */ +#define SND_DEV_DSP_SPDIFOUT 19 /* /dev/dsp_spdifout */ +#define SND_DEV_DSP_SPDIFIN 20 /* /dev/dsp_spdifin */ -#define SND_DEV_LAST SND_DEV_DSP_MMAP +#define SND_DEV_LAST SND_DEV_DSP_SPDIFIN #define SND_DEV_MAX PCMMAXDEV #define DSP_DEFAULT_SPEED 8000 @@ -506,7 +320,9 @@ SYSCTL_DECL(_hw_snd); struct pcm_channel *pcm_getfakechan(struct snddev_info *d); -int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, pid_t pid, int devunit); +int pcm_setvchans(struct snddev_info *d, int direction, int newcnt, int num); +int pcm_chnalloc(struct snddev_info *d, struct pcm_channel **ch, int direction, + pid_t pid, char *comm, int devunit); int pcm_chnrelease(struct pcm_channel *c); int pcm_chnref(struct pcm_channel *c, int ref); int pcm_inprog(struct snddev_info *d, int delta); @@ -535,7 +351,7 @@ #define snd_mtxlock(m) mtx_lock(m) #define snd_mtxunlock(m) mtx_unlock(m) -int sysctl_hw_snd_vchans(SYSCTL_HANDLER_ARGS); +int sysctl_dev_pcm_vchans(SYSCTL_HANDLER_ARGS); typedef int (*sndstat_handler)(struct sbuf *s, device_t dev, int verbose); int sndstat_acquire(struct thread *td); @@ -564,8 +380,6 @@ #define DV_F_DEV_MASK 0x0000ff00 /* force device type/class */ #define DV_F_DEV_SHIFT 8 /* force device type/class */ -#define PCM_DEBUG_MTX - /* * this is rather kludgey- we need to duplicate these struct def'ns from sound.c * so that the macro versions of pcm_{,un}lock can dereference them. @@ -579,6 +393,9 @@ struct { SLIST_HEAD(, pcm_channel) head; } busy; + struct { + SLIST_HEAD(, pcm_channel) head; + } opened; } pcm; } channels; TAILQ_HEAD(dsp_cdevinfo_linkhead, dsp_cdevinfo) dsp_cdevinfo_pool; @@ -595,6 +412,7 @@ struct cdev *mixer_dev; uint32_t pvchanrate, pvchanformat; uint32_t rvchanrate, rvchanformat; + int32_t eqpreamp; struct sysctl_ctx_list play_sysctl_ctx, rec_sysctl_ctx; struct sysctl_oid *play_sysctl_tree, *rec_sysctl_tree; struct cv cv; @@ -603,21 +421,20 @@ void sound_oss_sysinfo(oss_sysinfo *); int sound_oss_card_info(oss_card_info *); -#ifdef PCM_DEBUG_MTX -#define pcm_lock(d) mtx_lock(((struct snddev_info *)(d))->lock) -#define pcm_unlock(d) mtx_unlock(((struct snddev_info *)(d))->lock) -#else -void pcm_lock(struct snddev_info *d); -void pcm_unlock(struct snddev_info *d); -#endif +#define PCM_LOCKOWNED(c) mtx_owned(((struct snddev_info *)(d))->lock) +#define PCM_LOCK(d) mtx_lock(((struct snddev_info *)(d))->lock) +#define PCM_UNLOCK(d) mtx_unlock(((struct snddev_info *)(d))->lock) +#define PCM_TRYLOCK(c) mtx_trylock(((struct snddev_info *)(d))->lock) +#define PCM_LOCKASSERT(c) mtx_assert(((struct snddev_info *)(d))->lock, MA_OWNED) +#define PCM_UNLOCKASSERT(c) mtx_assert(((struct snddev_info *)(d))->lock, MA_NOTOWNED) /* - * For PCM_CV_[WAIT | ACQUIRE | RELEASE], be sure to surround these - * with pcm_lock/unlock() sequence, or I'll come to gnaw upon you! + * For PCM_[WAIT | ACQUIRE | RELEASE], be sure to surround these + * with PCM_LOCK/UNLOCK() sequence, or I'll come to gnaw upon you! */ #ifdef SND_DIAGNOSTIC #define PCM_WAIT(x) do { \ - if (mtx_owned((x)->lock) == 0) \ + if (!PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM WAIT] Mutex not owned!", \ __func__, __LINE__); \ while ((x)->flags & SD_F_BUSY) { \ @@ -630,7 +447,7 @@ } while(0) #define PCM_ACQUIRE(x) do { \ - if (mtx_owned((x)->lock) == 0) \ + if (!PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM ACQUIRE] Mutex not owned!", \ __func__, __LINE__); \ if ((x)->flags & SD_F_BUSY) \ @@ -640,7 +457,7 @@ } while(0) #define PCM_RELEASE(x) do { \ - if (mtx_owned((x)->lock) == 0) \ + if (!PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM RELEASE] Mutex not owned!", \ __func__, __LINE__); \ if ((x)->flags & SD_F_BUSY) { \ @@ -661,22 +478,22 @@ /* Quick version, for shorter path. */ #define PCM_ACQUIRE_QUICK(x) do { \ - if (mtx_owned((x)->lock) != 0) \ + if (PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM ACQUIRE QUICK] Mutex owned!", \ __func__, __LINE__); \ - pcm_lock(x); \ + PCM_LOCK(x); \ PCM_WAIT(x); \ PCM_ACQUIRE(x); \ - pcm_unlock(x); \ + PCM_UNLOCK(x); \ } while(0) #define PCM_RELEASE_QUICK(x) do { \ - if (mtx_owned((x)->lock) != 0) \ + if (PCM_LOCKOWNED(x)) \ panic("%s(%d): [PCM RELEASE QUICK] Mutex owned!", \ __func__, __LINE__); \ - pcm_lock(x); \ + PCM_LOCK(x); \ PCM_RELEASE(x); \ - pcm_unlock(x); \ + PCM_UNLOCK(x); \ } while(0) #define PCM_BUSYASSERT(x) do { \ @@ -686,8 +503,8 @@ } while(0) #define PCM_GIANT_ENTER(x) do { \ - int _pcm_giant = 0; \ - if (mtx_owned((x)->lock) != 0) \ + int __func__##_pcm_giant = 0; \ + if (PCM_LOCKOWNED(x)) \ panic("%s(%d): [GIANT ENTER] PCM lock owned!", \ __func__, __LINE__); \ if (mtx_owned(&Giant) != 0 && snd_verbose > 3) \ @@ -697,18 +514,18 @@ if (!((x)->flags & SD_F_MPSAFE) && mtx_owned(&Giant) == 0) \ do { \ mtx_lock(&Giant); \ - _pcm_giant = 1; \ + __func__##_pcm_giant = 1; \ } while(0) #define PCM_GIANT_EXIT(x) do { \ - if (mtx_owned((x)->lock) != 0) \ + if (PCM_LOCKOWNED(x)) \ panic("%s(%d): [GIANT EXIT] PCM lock owned!", \ __func__, __LINE__); \ - if (!(_pcm_giant == 0 || _pcm_giant == 1)) \ + if (!(__func__##_pcm_giant == 0 || __func__##_pcm_giant == 1)) \ panic("%s(%d): [GIANT EXIT] _pcm_giant screwed!", \ __func__, __LINE__); \ if ((x)->flags & SD_F_MPSAFE) { \ - if (_pcm_giant == 1) \ + if (__func__##_pcm_giant == 1) \ panic("%s(%d): [GIANT EXIT] MPSAFE Giant?", \ __func__, __LINE__); \ if (mtx_owned(&Giant) != 0 && snd_verbose > 3) \ @@ -716,23 +533,23 @@ "%s(%d): [GIANT EXIT] Giant owned!\n", \ __func__, __LINE__); \ } \ - if (_pcm_giant != 0) { \ + if (__func__##_pcm_giant != 0) { \ if (mtx_owned(&Giant) == 0) \ panic("%s(%d): [GIANT EXIT] Giant not owned!", \ __func__, __LINE__); \ - _pcm_giant = 0; \ + __func__##_pcm_giant = 0; \ mtx_unlock(&Giant); \ } \ } while(0) #else /* SND_DIAGNOSTIC */ #define PCM_WAIT(x) do { \ - mtx_assert((x)->lock, MA_OWNED); \ + PCM_LOCKASSERT(x); \ while ((x)->flags & SD_F_BUSY) \ cv_wait(&(x)->cv, (x)->lock); \ } while(0) #define PCM_ACQUIRE(x) do { \ - mtx_assert((x)->lock, MA_OWNED); \ + PCM_LOCKASSERT(x); \ KASSERT(!((x)->flags & SD_F_BUSY), \ ("%s(%d): [PCM ACQUIRE] Trying to acquire BUSY cv!", \ __func__, __LINE__)); \ @@ -740,7 +557,7 @@ } while(0) #define PCM_RELEASE(x) do { \ - mtx_assert((x)->lock, MA_OWNED); \ + PCM_LOCKASSERT(x); \ KASSERT((x)->flags & SD_F_BUSY, \ ("%s(%d): [PCM RELEASE] Releasing non-BUSY cv!", \ __func__, __LINE__)); \ @@ -751,18 +568,18 @@ /* Quick version, for shorter path. */ #define PCM_ACQUIRE_QUICK(x) do { \ - mtx_assert((x)->lock, MA_NOTOWNED); \ - pcm_lock(x); \ + PCM_UNLOCKASSERT(x); \ + PCM_LOCK(x); \ PCM_WAIT(x); \ PCM_ACQUIRE(x); \ - pcm_unlock(x); \ + PCM_UNLOCK(x); \ } while(0) #define PCM_RELEASE_QUICK(x) do { \ - mtx_assert((x)->lock, MA_NOTOWNED); \ - pcm_lock(x); \ + PCM_UNLOCKASSERT(x); \ + PCM_LOCK(x); \ PCM_RELEASE(x); \ - pcm_unlock(x); \ + PCM_UNLOCK(x); \ } while(0) #define PCM_BUSYASSERT(x) KASSERT(x != NULL && \ @@ -772,26 +589,26 @@ __func__, __LINE__, x)) #define PCM_GIANT_ENTER(x) do { \ - int _pcm_giant = 0; \ - mtx_assert((x)->lock, MA_NOTOWNED); \ + int __func__##_pcm_giant = 0; \ + PCM_UNLOCKASSERT(x); \ if (!((x)->flags & SD_F_MPSAFE) && mtx_owned(&Giant) == 0) \ do { \ mtx_lock(&Giant); \ - _pcm_giant = 1; \ + __func__##_pcm_giant = 1; \ } while(0) #define PCM_GIANT_EXIT(x) do { \ - mtx_assert((x)->lock, MA_NOTOWNED); \ - KASSERT(_pcm_giant == 0 || _pcm_giant == 1, \ + PCM_UNLOCKASSERT(x); \ + KASSERT(__func__##_pcm_giant == 0 || __func__##_pcm_giant == 1, \ ("%s(%d): [GIANT EXIT] _pcm_giant screwed!", \ __func__, __LINE__)); \ KASSERT(!((x)->flags & SD_F_MPSAFE) || \ - (((x)->flags & SD_F_MPSAFE) && _pcm_giant == 0), \ + (((x)->flags & SD_F_MPSAFE) && __func__##_pcm_giant == 0), \ ("%s(%d): [GIANT EXIT] MPSAFE Giant?", \ __func__, __LINE__)); \ - if (_pcm_giant != 0) { \ + if (__func__##_pcm_giant != 0) { \ mtx_assert(&Giant, MA_OWNED); \ - _pcm_giant = 0; \ + __func__##_pcm_giant = 0; \ mtx_unlock(&Giant); \ } \ } while(0) --- sys/dev/sound/pcm/vchan.c.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/vchan.c 2009-01-17 19:29:20.000000000 +0800 @@ -1,6 +1,6 @@ /*- + * Copyright (c) 2006-2009 Ariff Abdullah * Copyright (c) 2001 Cameron Grant - * Copyright (c) 2006 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,376 +29,37 @@ #include #include -#include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/vchan.c,v 1.36 2007/06/16 03:37:28 ariff Exp $"); -MALLOC_DEFINE(M_VCHANFEEDER, "vchanfeed", "pcm vchan feeder"); +#undef FEEDER_MULTIFORMAT +#define FEEDER_MULTIFORMAT 1 -typedef uint32_t (*feed_vchan_mixer)(uint8_t *, uint8_t *, uint32_t); - -struct vchinfo { +struct vchan_info { struct pcm_channel *channel; struct pcmchan_caps caps; uint32_t fmtlist[2]; int trigger; }; -/* support everything (mono / stereo), except a-law / mu-law */ -static struct afmtstr_table vchan_supported_fmts[] = { - { "u8", AFMT_U8 }, { "s8", AFMT_S8 }, - { "s16le", AFMT_S16_LE }, { "s16be", AFMT_S16_BE }, - { "u16le", AFMT_U16_LE }, { "u16be", AFMT_U16_BE }, - { "s24le", AFMT_S24_LE }, { "s24be", AFMT_S24_BE }, - { "u24le", AFMT_U24_LE }, { "u24be", AFMT_U24_BE }, - { "s32le", AFMT_S32_LE }, { "s32be", AFMT_S32_BE }, - { "u32le", AFMT_U32_LE }, { "u32be", AFMT_U32_BE }, - { NULL, 0 }, -}; - -/* alias table, shorter. */ -static const struct { - char *alias, *fmtstr; -} vchan_fmtstralias[] = { - { "8", "u8" }, { "16", "s16le" }, - { "24", "s24le" }, { "32", "s32le" }, - { NULL, NULL }, -}; - -#define vchan_valid_format(fmt) \ - afmt2afmtstr(vchan_supported_fmts, fmt, NULL, 0, 0, \ - AFMTSTR_STEREO_RETURN) -#define vchan_valid_strformat(strfmt) \ - afmtstr2afmt(vchan_supported_fmts, strfmt, AFMTSTR_STEREO_RETURN); - -/* - * Need specialized WRITE macros since 32bit might involved saturation - * if calculation is done within 32bit arithmetic. - */ -#define VCHAN_PCM_WRITE_S8_NE(b8, val) PCM_WRITE_S8(b8, val) -#define VCHAN_PCM_WRITE_S16_LE(b8, val) PCM_WRITE_S16_LE(b8, val) -#define VCHAN_PCM_WRITE_S24_LE(b8, val) PCM_WRITE_S24_LE(b8, val) -#define VCHAN_PCM_WRITE_S32_LE(b8, val) _PCM_WRITE_S32_LE(b8, val) -#define VCHAN_PCM_WRITE_S16_BE(b8, val) PCM_WRITE_S16_BE(b8, val) -#define VCHAN_PCM_WRITE_S24_BE(b8, val) PCM_WRITE_S24_BE(b8, val) -#define VCHAN_PCM_WRITE_S32_BE(b8, val) _PCM_WRITE_S32_BE(b8, val) -#define VCHAN_PCM_WRITE_U8_NE(b8, val) PCM_WRITE_U8(b8, val) -#define VCHAN_PCM_WRITE_U16_LE(b8, val) PCM_WRITE_U16_LE(b8, val) -#define VCHAN_PCM_WRITE_U24_LE(b8, val) PCM_WRITE_U24_LE(b8, val) -#define VCHAN_PCM_WRITE_U32_LE(b8, val) _PCM_WRITE_U32_LE(b8, val) -#define VCHAN_PCM_WRITE_U16_BE(b8, val) PCM_WRITE_U16_BE(b8, val) -#define VCHAN_PCM_WRITE_U24_BE(b8, val) PCM_WRITE_U24_BE(b8, val) -#define VCHAN_PCM_WRITE_U32_BE(b8, val) _PCM_WRITE_U32_BE(b8, val) - -#define FEEDER_VCHAN_MIX(FMTBIT, VCHAN_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ -static uint32_t \ -feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(uint8_t *to, uint8_t *tmp, \ - uint32_t count) \ -{ \ - int32_t x, y; \ - VCHAN_INTCAST z; \ - int i; \ - \ - i = count; \ - tmp += i; \ - to += i; \ - \ - do { \ - tmp -= PCM_##FMTBIT##_BPS; \ - to -= PCM_##FMTBIT##_BPS; \ - i -= PCM_##FMTBIT##_BPS; \ - x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(tmp); \ - y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(to); \ - z = (VCHAN_INTCAST)x + y; \ - x = PCM_CLAMP_##SIGN##FMTBIT(z); \ - VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \ - } while (i != 0); \ - \ - return (count); \ -} - -FEEDER_VCHAN_MIX(8, int32_t, S, s, NE, ne) -FEEDER_VCHAN_MIX(16, int32_t, S, s, LE, le) -FEEDER_VCHAN_MIX(24, int32_t, S, s, LE, le) -FEEDER_VCHAN_MIX(32, intpcm_t, S, s, LE, le) -FEEDER_VCHAN_MIX(16, int32_t, S, s, BE, be) -FEEDER_VCHAN_MIX(24, int32_t, S, s, BE, be) -FEEDER_VCHAN_MIX(32, intpcm_t, S, s, BE, be) -FEEDER_VCHAN_MIX(8, int32_t, U, u, NE, ne) -FEEDER_VCHAN_MIX(16, int32_t, U, u, LE, le) -FEEDER_VCHAN_MIX(24, int32_t, U, u, LE, le) -FEEDER_VCHAN_MIX(32, intpcm_t, U, u, LE, le) -FEEDER_VCHAN_MIX(16, int32_t, U, u, BE, be) -FEEDER_VCHAN_MIX(24, int32_t, U, u, BE, be) -FEEDER_VCHAN_MIX(32, intpcm_t, U, u, BE, be) - -struct feed_vchan_info { - uint32_t format; - int bps; - feed_vchan_mixer mix; -}; - -static struct feed_vchan_info feed_vchan_info_tbl[] = { - { AFMT_S8, PCM_8_BPS, feed_vchan_mix_s8ne }, - { AFMT_S16_LE, PCM_16_BPS, feed_vchan_mix_s16le }, - { AFMT_S24_LE, PCM_24_BPS, feed_vchan_mix_s24le }, - { AFMT_S32_LE, PCM_32_BPS, feed_vchan_mix_s32le }, - { AFMT_S16_BE, PCM_16_BPS, feed_vchan_mix_s16be }, - { AFMT_S24_BE, PCM_24_BPS, feed_vchan_mix_s24be }, - { AFMT_S32_BE, PCM_32_BPS, feed_vchan_mix_s32be }, - { AFMT_U8, PCM_8_BPS, feed_vchan_mix_u8ne }, - { AFMT_U16_LE, PCM_16_BPS, feed_vchan_mix_u16le }, - { AFMT_U24_LE, PCM_24_BPS, feed_vchan_mix_u24le }, - { AFMT_U32_LE, PCM_32_BPS, feed_vchan_mix_u32le }, - { AFMT_U16_BE, PCM_16_BPS, feed_vchan_mix_u16be }, - { AFMT_U24_BE, PCM_24_BPS, feed_vchan_mix_u24be }, - { AFMT_U32_BE, PCM_32_BPS, feed_vchan_mix_u32be }, -}; - -#define FVCHAN_DATA(i, c) ((intptr_t)((((i) & 0x1f) << 4) | ((c) & 0xf))) -#define FVCHAN_INFOIDX(m) (((m) >> 4) & 0x1f) -#define FVCHAN_CHANNELS(m) ((m) & 0xf) - -static int -feed_vchan_init(struct pcm_feeder *f) -{ - int i, channels; - - if (f->desc->out != f->desc->in) - return (EINVAL); - - channels = (f->desc->out & AFMT_STEREO) ? 2 : 1; - - for (i = 0; i < sizeof(feed_vchan_info_tbl) / - sizeof(feed_vchan_info_tbl[0]); i++) { - if ((f->desc->out & ~AFMT_STEREO) == - feed_vchan_info_tbl[i].format) { - f->data = (void *)FVCHAN_DATA(i, channels); - return (0); - } - } - - return (-1); -} - -static __inline int -feed_vchan_rec(struct pcm_channel *c) -{ - struct pcm_channel *ch; - struct snd_dbuf *b, *bs; - int cnt, rdy; - - /* - * Reset ready and moving pointer. We're not using bufsoft - * anywhere since its sole purpose is to become the primary - * distributor for the recorded buffer and also as an interrupt - * threshold progress indicator. - */ - b = c->bufsoft; - b->rp = 0; - b->rl = 0; - cnt = sndbuf_getsize(b); - - do { - cnt = FEEDER_FEED(c->feeder->source, c, b->tmpbuf, cnt, - c->bufhard); - if (cnt != 0) { - sndbuf_acquire(b, b->tmpbuf, cnt); - cnt = sndbuf_getfree(b); - } - } while (cnt != 0); - - /* Not enough data */ - if (b->rl < sndbuf_getbps(b)) { - b->rl = 0; - return (0); - } - - /* - * Keep track of ready and moving pointer since we will use - * bufsoft over and over again, pretending nothing has happened. - */ - rdy = b->rl; - - CHN_FOREACH(ch, c, children.busy) { - CHN_LOCK(ch); - if (!(ch->flags & CHN_F_TRIGGERED)) { - CHN_UNLOCK(ch); - continue; - } - bs = ch->bufsoft; - if (ch->flags & CHN_F_MAPPED) - sndbuf_dispose(bs, NULL, sndbuf_getready(bs)); - cnt = sndbuf_getfree(bs); - if (cnt < sndbuf_getbps(bs)) { - CHN_UNLOCK(ch); - continue; - } - do { - cnt = FEEDER_FEED(ch->feeder, ch, bs->tmpbuf, cnt, b); - if (cnt != 0) { - sndbuf_acquire(bs, bs->tmpbuf, cnt); - cnt = sndbuf_getfree(bs); - } - } while (cnt != 0); - /* - * Not entirely flushed out... - */ - if (b->rl != 0) - ch->xruns++; - CHN_UNLOCK(ch); - /* - * Rewind buffer position for next virtual channel. - */ - b->rp = 0; - b->rl = rdy; - } - - /* - * Set ready pointer to indicate that our children are ready - * to be woken up, also as an interrupt threshold progress - * indicator. - */ - b->rl = 1; - - /* - * Return 0 to bail out early from sndbuf_feed() loop. - * No need to increase feedcount counter since part of this - * feeder chains already include feed_root(). - */ - return (0); -} - -static int -feed_vchan(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) -{ - struct feed_vchan_info *info; - struct snd_dbuf *src = source; - struct pcm_channel *ch; - uint32_t cnt, mcnt, rcnt, sz; - uint8_t *tmp; - - if (c->direction == PCMDIR_REC) - return (feed_vchan_rec(c)); - - sz = sndbuf_getsize(src); - if (sz < count) - count = sz; - - info = &feed_vchan_info_tbl[FVCHAN_INFOIDX((intptr_t)f->data)]; - sz = info->bps * FVCHAN_CHANNELS((intptr_t)f->data); - count -= count % sz; - if (count < sz) - return (0); - - /* - * we are going to use our source as a temporary buffer since it's - * got no other purpose. we obtain our data by traversing the channel - * list of children and calling vchan_mix_* to mix count bytes from - * each into our destination buffer, b - */ - tmp = sndbuf_getbuf(src); - rcnt = 0; - mcnt = 0; - - CHN_FOREACH(ch, c, children.busy) { - CHN_LOCK(ch); - if (!(ch->flags & CHN_F_TRIGGERED)) { - CHN_UNLOCK(ch); - continue; - } - if ((ch->flags & CHN_F_MAPPED) && !(ch->flags & CHN_F_CLOSING)) - sndbuf_acquire(ch->bufsoft, NULL, - sndbuf_getfree(ch->bufsoft)); - if (rcnt == 0) { - rcnt = FEEDER_FEED(ch->feeder, ch, b, count, - ch->bufsoft); - rcnt -= rcnt % sz; - mcnt = count - rcnt; - } else { - cnt = FEEDER_FEED(ch->feeder, ch, tmp, count, - ch->bufsoft); - cnt -= cnt % sz; - if (cnt != 0) { - if (mcnt != 0) { - memset(b + rcnt, - sndbuf_zerodata(f->desc->out), - mcnt); - mcnt = 0; - } - cnt = info->mix(b, tmp, cnt); - if (cnt > rcnt) - rcnt = cnt; - } - } - CHN_UNLOCK(ch); - } - - if (++c->feedcount == 0) - c->feedcount = 2; - - return (rcnt); -} - -static struct pcm_feederdesc feeder_vchan_desc[] = { - {FEEDER_MIXER, AFMT_S8, AFMT_S8, 0}, - {FEEDER_MIXER, AFMT_S16_LE, AFMT_S16_LE, 0}, - {FEEDER_MIXER, AFMT_S24_LE, AFMT_S24_LE, 0}, - {FEEDER_MIXER, AFMT_S32_LE, AFMT_S32_LE, 0}, - {FEEDER_MIXER, AFMT_S16_BE, AFMT_S16_BE, 0}, - {FEEDER_MIXER, AFMT_S24_BE, AFMT_S24_BE, 0}, - {FEEDER_MIXER, AFMT_S32_BE, AFMT_S32_BE, 0}, - {FEEDER_MIXER, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U8, AFMT_U8, 0}, - {FEEDER_MIXER, AFMT_U16_LE, AFMT_U16_LE, 0}, - {FEEDER_MIXER, AFMT_U24_LE, AFMT_U24_LE, 0}, - {FEEDER_MIXER, AFMT_U32_LE, AFMT_U32_LE, 0}, - {FEEDER_MIXER, AFMT_U16_BE, AFMT_U16_BE, 0}, - {FEEDER_MIXER, AFMT_U24_BE, AFMT_U24_BE, 0}, - {FEEDER_MIXER, AFMT_U32_BE, AFMT_U32_BE, 0}, - {FEEDER_MIXER, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, - {FEEDER_MIXER, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, - {0, 0, 0, 0}, -}; -static kobj_method_t feeder_vchan_methods[] = { - KOBJMETHOD(feeder_init, feed_vchan_init), - KOBJMETHOD(feeder_feed, feed_vchan), - {0, 0} -}; -FEEDER_DECLARE(feeder_vchan, 2, NULL); - -/************************************************************/ - static void * vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir) { - struct vchinfo *ch; + struct vchan_info *info; KASSERT(dir == PCMDIR_PLAY || dir == PCMDIR_REC, ("vchan_init: bad direction")); KASSERT(c != NULL && c->parentchannel != NULL, ("vchan_init: bad channels")); - ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO); - ch->channel = c; - ch->trigger = PCMTRIG_STOP; + info = malloc(sizeof(*info), M_DEVBUF, M_WAITOK | M_ZERO); + info->channel = c; + info->trigger = PCMTRIG_STOP; c->flags |= CHN_F_VIRTUAL; - return (ch); + return (info); } static int @@ -412,9 +73,11 @@ static int vchan_setformat(kobj_t obj, void *data, uint32_t format) { - struct vchinfo *ch = data; + struct vchan_info *info; + + info = data; - if (fmtvalid(format, ch->fmtlist) == 0) + if (snd_fmtvalid(format, info->fmtlist) == 0) return (-1); return (0); @@ -423,41 +86,44 @@ static int vchan_setspeed(kobj_t obj, void *data, uint32_t speed) { - struct vchinfo *ch = data; - struct pcm_channel *p = ch->channel->parentchannel; + struct vchan_info *info; + struct pcm_channel *pch; - return (sndbuf_getspd(p->bufsoft)); + info = data; + pch = info->channel->parentchannel; + + return (sndbuf_getspd(pch->bufsoft)); } static int vchan_trigger(kobj_t obj, void *data, int go) { - struct vchinfo *ch = data; + struct vchan_info *info; struct pcm_channel *c, *p; int err, otrigger; - if (!PCMTRIG_COMMON(go) || go == ch->trigger) + info = data; + + if (!PCMTRIG_COMMON(go) || go == info->trigger) return (0); - c = ch->channel; + c = info->channel; p = c->parentchannel; - otrigger = ch->trigger; - ch->trigger = go; + otrigger = info->trigger; + info->trigger = go; CHN_UNLOCK(c); CHN_LOCK(p); switch (go) { case PCMTRIG_START: - if (otrigger != PCMTRIG_START) { + if (otrigger != PCMTRIG_START) CHN_INSERT_HEAD(p, c, children.busy); - } break; case PCMTRIG_STOP: case PCMTRIG_ABORT: - if (otrigger == PCMTRIG_START) { + if (otrigger == PCMTRIG_START) CHN_REMOVE(p, c, children.busy); - } break; default: break; @@ -473,27 +139,35 @@ static struct pcmchan_caps * vchan_getcaps(kobj_t obj, void *data) { - struct vchinfo *ch = data; + struct vchan_info *info; struct pcm_channel *c, *p; uint32_t fmt; - c = ch->channel; + info = data; + c = info->channel; p = c->parentchannel; - ch->caps.minspeed = sndbuf_getspd(p->bufsoft); - ch->caps.maxspeed = ch->caps.minspeed; - ch->caps.caps = 0; - ch->fmtlist[1] = 0; + info->caps.minspeed = sndbuf_getspd(p->bufsoft); + info->caps.maxspeed = info->caps.minspeed; + info->caps.caps = 0; + info->fmtlist[1] = 0; fmt = sndbuf_getfmt(p->bufsoft); - if (fmt != vchan_valid_format(fmt)) { + if (!(fmt & VCHAN_SUPPORTED_FORMAT)) { device_printf(c->dev, - "%s: WARNING: invalid vchan format! (0x%08x)\n", - __func__, fmt); + "%s: WARNING: unsupported vchan format! " + "(0x%08x)\n", __func__, fmt); fmt = VCHAN_DEFAULT_AFMT; } - ch->fmtlist[0] = fmt; - ch->caps.fmtlist = ch->fmtlist; + info->fmtlist[0] = fmt; + info->caps.fmtlist = info->fmtlist; + + return (&info->caps); +} - return (&ch->caps); +static struct pcmchan_matrix * +vchan_getmatrix(kobj_t obj, void *data, uint32_t format) +{ + + return (feeder_matrix_format_map(format)); } static kobj_method_t vchan_methods[] = { @@ -503,19 +177,132 @@ KOBJMETHOD(channel_setspeed, vchan_setspeed), KOBJMETHOD(channel_trigger, vchan_trigger), KOBJMETHOD(channel_getcaps, vchan_getcaps), + KOBJMETHOD(channel_getmatrix, vchan_getmatrix), {0, 0} }; CHANNEL_DECLARE(vchan); +static void +pcm_getparentchannel(struct snddev_info *d, + struct pcm_channel **wrch, struct pcm_channel **rdch) +{ + struct pcm_channel **ch, *wch, *rch, *c; + + KASSERT(d != NULL, ("%s(): NULL snddev_info", __func__)); + + PCM_BUSYASSERT(d); + PCM_UNLOCKASSERT(d); + + wch = NULL; + rch = NULL; + + CHN_FOREACH(c, d, channels.pcm) { + CHN_LOCK(c); + ch = (c->direction == PCMDIR_PLAY) ? &wch : &rch; + if (c->flags & CHN_F_VIRTUAL) { + /* Sanity check */ + if (*ch != NULL && *ch != c->parentchannel) { + CHN_UNLOCK(c); + *ch = NULL; + break; + } + } else if (c->flags & CHN_F_HAS_VCHAN) { + /* No way!! */ + if (*ch != NULL) { + CHN_UNLOCK(c); + *ch = NULL; + break; + } + *ch = c; + } + CHN_UNLOCK(c); + } + + if (wrch != NULL) + *wrch = wch; + if (rdch != NULL) + *rdch = rch; +} + +static int +sysctl_dev_pcm_dynamic_vchan(SYSCTL_HANDLER_ARGS) +{ + struct snddev_info *d; + struct pcm_channel *ch; + int direction, dynamic, err; + + d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); + if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) + return (EINVAL); + + PCM_LOCK(d); + PCM_WAIT(d); + + switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { + case VCHAN_PLAY: + direction = PCMDIR_PLAY; + break; + case VCHAN_REC: + direction = PCMDIR_REC; + break; + default: + PCM_UNLOCK(d); + return (EINVAL); + break; + } + + PCM_ACQUIRE(d); + PCM_UNLOCK(d); + + if (direction == PCMDIR_PLAY) + pcm_getparentchannel(d, &ch, NULL); + else + pcm_getparentchannel(d, NULL, &ch); + + if (ch == NULL) { + PCM_RELEASE_QUICK(d); + return (EINVAL); + } + + KASSERT(direction == ch->direction, ("%s(): invalid direction %d/%d", + __func__, direction, ch->direction)); + + CHN_LOCK(ch); + dynamic = !!(ch->flags & CHN_F_DYNAMIC_VCHAN); + CHN_UNLOCK(ch); + + err = sysctl_handle_int(oidp, &dynamic, 0, req); + if (err == 0 && req->newptr != NULL) { + if (!(dynamic == 0 || dynamic == 1)) { + PCM_RELEASE_QUICK(d); + return (EINVAL); + } + if (dynamic == !!(ch->flags & CHN_F_DYNAMIC_VCHAN)) { + PCM_RELEASE_QUICK(d); + return (0); + } + CHN_LOCK(ch); + ch->flags &= ~CHN_F_DYNAMIC_VCHAN; + ch->flags |= (dynamic != 0) ? CHN_F_DYNAMIC_VCHAN : 0; + CHN_UNLOCK(ch); + } + + PCM_RELEASE_QUICK(d); + + return (err); +} + /* * On the fly vchan rate settings */ -#ifdef SND_DYNSYSCTL + +#define VCHAN_ACCESSIBLE(c) (((c)->flags & CHN_F_DYNAMIC_VCHAN) || \ + CHN_STOPPED(c)) static int -sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS) +sysctl_dev_pcm_vchanrate(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; - struct pcm_channel *c, *ch = NULL; + struct pcm_channel *ch; struct pcmchan_caps *caps; int *vchanrate, vchancount, direction, err, newspd; @@ -523,7 +310,7 @@ if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) return (EINVAL); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { @@ -538,49 +325,36 @@ vchanrate = &d->rvchanrate; break; default: - pcm_unlock(d); + PCM_UNLOCK(d); return (EINVAL); break; } if (vchancount < 1) { - pcm_unlock(d); + PCM_UNLOCK(d); return (EINVAL); } PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); - newspd = 0; + if (direction == PCMDIR_PLAY) + pcm_getparentchannel(d, &ch, NULL); + else + pcm_getparentchannel(d, NULL, &ch); - CHN_FOREACH(c, d, channels.pcm) { - CHN_LOCK(c); - if (c->direction == direction) { - if (c->flags & CHN_F_VIRTUAL) { - /* Sanity check */ - if (ch != NULL && ch != c->parentchannel) { - CHN_UNLOCK(c); - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - } else if (c->flags & CHN_F_HAS_VCHAN) { - /* No way!! */ - if (ch != NULL) { - CHN_UNLOCK(c); - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - ch = c; - newspd = ch->speed; - } - } - CHN_UNLOCK(c); - } if (ch == NULL) { PCM_RELEASE_QUICK(d); return (EINVAL); } + KASSERT(direction == ch->direction, ("%s(): invalid direction %d/%d", + __func__, direction, ch->direction)); + + CHN_LOCK(ch); + newspd = ch->speed; + CHN_UNLOCK(ch); + err = sysctl_handle_int(oidp, &newspd, 0, req); if (err == 0 && req->newptr != NULL) { if (newspd < 1 || newspd < feeder_rate_min || @@ -598,19 +372,30 @@ return (EINVAL); } } - if (CHN_STOPPED(ch) && newspd != ch->speed) { - err = chn_setspeed(ch, newspd); + if (newspd != ch->speed && VCHAN_ACCESSIBLE(ch)) { + int restart; + + restart = CHN_STARTED(ch) ? 1 : 0; + if (restart != 0) + chn_abort(ch); + + err = chn_reset(ch, ch->format, newspd); /* * Try to avoid FEEDER_RATE on parent channel if the * requested value is not supported by the hardware. */ - if (!err && feeder_rate_round && + if (err == 0 && feeder_rate_round && (ch->feederflags & (1 << FEEDER_RATE))) { newspd = sndbuf_getspd(ch->bufhard); - err = chn_setspeed(ch, newspd); + err = chn_reset(ch, ch->format, newspd); + } + if (err == 0) { + *vchanrate = sndbuf_getspd(ch->bufsoft); + if (restart != 0) { + ch->flags |= CHN_F_DIRTY; + err = chn_start(ch, 1); + } } - if (err == 0) - *vchanrate = newspd; } CHN_UNLOCK(ch); } @@ -621,19 +406,19 @@ } static int -sysctl_hw_snd_vchanformat(SYSCTL_HANDLER_ARGS) +sysctl_dev_pcm_vchanformat(SYSCTL_HANDLER_ARGS) { struct snddev_info *d; - struct pcm_channel *c, *ch = NULL; - uint32_t newfmt, spd; - int *vchanformat, vchancount, direction, err, i; - char fmtstr[AFMTSTR_MAXSZ]; + struct pcm_channel *ch; + uint32_t newfmt; + int *vchanformat, vchancount, direction, err; + char fmtstr[AFMTSTR_LEN]; d = devclass_get_softc(pcm_devclass, VCHAN_SYSCTL_UNIT(oidp->oid_arg1)); if (!PCM_REGISTERED(d) || !(d->flags & SD_F_AUTOVCHAN)) return (EINVAL); - pcm_lock(d); + PCM_LOCK(d); PCM_WAIT(d); switch (VCHAN_SYSCTL_DIR(oidp->oid_arg1)) { @@ -648,76 +433,64 @@ vchanformat = &d->rvchanformat; break; default: - pcm_unlock(d); + PCM_UNLOCK(d); return (EINVAL); break; } if (vchancount < 1) { - pcm_unlock(d); + PCM_UNLOCK(d); return (EINVAL); } PCM_ACQUIRE(d); - pcm_unlock(d); + PCM_UNLOCK(d); + + if (direction == PCMDIR_PLAY) + pcm_getparentchannel(d, &ch, NULL); + else + pcm_getparentchannel(d, NULL, &ch); - CHN_FOREACH(c, d, channels.pcm) { - CHN_LOCK(c); - if (c->direction == direction) { - if (c->flags & CHN_F_VIRTUAL) { - /* Sanity check */ - if (ch != NULL && ch != c->parentchannel) { - CHN_UNLOCK(c); - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - } else if (c->flags & CHN_F_HAS_VCHAN) { - /* No way!! */ - if (ch != NULL) { - CHN_UNLOCK(c); - PCM_RELEASE_QUICK(d); - return (EINVAL); - } - ch = c; - if (ch->format != - afmt2afmtstr(vchan_supported_fmts, - ch->format, fmtstr, sizeof(fmtstr), - AFMTSTR_FULL, AFMTSTR_STEREO_RETURN)) { - strlcpy(fmtstr, VCHAN_DEFAULT_STRFMT, - sizeof(fmtstr)); - } - } - } - CHN_UNLOCK(c); - } if (ch == NULL) { PCM_RELEASE_QUICK(d); return (EINVAL); } + KASSERT(direction == ch->direction, ("%s(): invalid direction %d/%d", + __func__, direction, ch->direction)); + + CHN_LOCK(ch); + + bzero(fmtstr, sizeof(fmtstr)); + + if (snd_afmt2str(ch->format, fmtstr, sizeof(fmtstr)) != ch->format) + strlcpy(fmtstr, "", sizeof(fmtstr)); + + CHN_UNLOCK(ch); + err = sysctl_handle_string(oidp, fmtstr, sizeof(fmtstr), req); if (err == 0 && req->newptr != NULL) { - for (i = 0; vchan_fmtstralias[i].alias != NULL; i++) { - if (strcmp(fmtstr, vchan_fmtstralias[i].alias) == 0) { - strlcpy(fmtstr, vchan_fmtstralias[i].fmtstr, - sizeof(fmtstr)); - break; - } - } - newfmt = vchan_valid_strformat(fmtstr); - if (newfmt == 0) { + newfmt = snd_str2afmt(fmtstr); + if (newfmt == 0 || !(newfmt & VCHAN_SUPPORTED_FORMAT)) { PCM_RELEASE_QUICK(d); return (EINVAL); } CHN_LOCK(ch); - if (CHN_STOPPED(ch) && newfmt != ch->format) { - /* Get channel speed, before chn_reset() screw it. */ - spd = ch->speed; - err = chn_reset(ch, newfmt); - if (err == 0) - err = chn_setspeed(ch, spd); - if (err == 0) - *vchanformat = newfmt; + if (newfmt != ch->format && VCHAN_ACCESSIBLE(ch)) { + int restart; + + restart = CHN_STARTED(ch) ? 1 : 0; + if (restart != 0) + chn_abort(ch); + + err = chn_reset(ch, newfmt, ch->speed); + if (err == 0) { + *vchanformat = sndbuf_getfmt(ch->bufsoft); + if (restart != 0) { + ch->flags |= CHN_F_DIRTY; + err = chn_start(ch, 1); + } + } } CHN_UNLOCK(ch); } @@ -726,7 +499,6 @@ return (err); } -#endif /* virtual channel interface */ @@ -738,42 +510,50 @@ int vchan_create(struct pcm_channel *parent, int num) { - struct snddev_info *d = parent->parentsnddev; - struct pcm_channel *ch, *tmp, *after; + struct snddev_info *d; + struct pcm_channel *ch; struct pcmchan_caps *parent_caps; - uint32_t vchanfmt; - int err, first, speed, r; - int direction; + uint32_t vchanfmt, vchanspd; + int err, direction, r, save; + + d = parent->parentsnddev; PCM_BUSYASSERT(d); + CHN_LOCKASSERT(parent); if (!(parent->flags & CHN_F_BUSY)) return (EBUSY); + if (!(parent->direction == PCMDIR_PLAY || + parent->direction == PCMDIR_REC)) + return (EINVAL); + + d = parent->parentsnddev; + + CHN_UNLOCK(parent); + PCM_LOCK(d); + if (parent->direction == PCMDIR_PLAY) { direction = PCMDIR_PLAY_VIRTUAL; vchanfmt = d->pvchanformat; - speed = d->pvchanrate; - } else if (parent->direction == PCMDIR_REC) { + vchanspd = d->pvchanrate; + } else { direction = PCMDIR_REC_VIRTUAL; vchanfmt = d->rvchanformat; - speed = d->rvchanrate; - } else - return (EINVAL); - CHN_UNLOCK(parent); + vchanspd = d->rvchanrate; + } /* create a new playback channel */ - pcm_lock(d); ch = pcm_chn_create(d, parent, &vchan_class, direction, num, parent); if (ch == NULL) { - pcm_unlock(d); + PCM_UNLOCK(d); CHN_LOCK(parent); return (ENODEV); } /* add us to our grandparent's channel list */ err = pcm_chn_add(d, ch); - pcm_unlock(d); + PCM_UNLOCK(d); if (err) { pcm_chn_destroy(ch); CHN_LOCK(parent); @@ -781,211 +561,206 @@ } CHN_LOCK(parent); - /* add us to our parent channel's children */ - first = CHN_EMPTY(parent, children); - after = NULL; - CHN_FOREACH(tmp, parent, children) { - if (CHN_CHAN(tmp) > CHN_CHAN(ch)) - after = tmp; - else if (CHN_CHAN(tmp) < CHN_CHAN(ch)) - break; - } - if (after != NULL) { - CHN_INSERT_AFTER(after, ch, children); - } else { - CHN_INSERT_HEAD(parent, ch, children); - } + /* + * Add us to our parent channel's children in reverse order + * so future destruction will pick the last (biggest number) + * channel. + */ + CHN_INSERT_SORT_DESCEND(parent, ch, children); + + if (parent->flags & CHN_F_HAS_VCHAN) + return (0); + parent->flags |= CHN_F_HAS_VCHAN; - if (first) { - parent_caps = chn_getcaps(parent); - if (parent_caps == NULL) - err = EINVAL; - - if (!err) { - if (vchanfmt == 0) { - const char *vfmt; - - CHN_UNLOCK(parent); - r = resource_string_value( - device_get_name(parent->dev), - device_get_unit(parent->dev), - VCHAN_FMT_HINT(direction), - &vfmt); - CHN_LOCK(parent); - if (r != 0) - vfmt = NULL; - if (vfmt != NULL) { - vchanfmt = vchan_valid_strformat(vfmt); - for (r = 0; vchanfmt == 0 && - vchan_fmtstralias[r].alias != NULL; - r++) { - if (strcmp(vfmt, vchan_fmtstralias[r].alias) == 0) { - vchanfmt = vchan_valid_strformat(vchan_fmtstralias[r].fmtstr); - break; - } - } - } - if (vchanfmt == 0) - vchanfmt = VCHAN_DEFAULT_AFMT; - } - err = chn_reset(parent, vchanfmt); - } + parent_caps = chn_getcaps(parent); + if (parent_caps == NULL) + err = EINVAL; - if (!err) { - /* - * This is very sad. Few soundcards advertised as being - * able to do (insanely) higher/lower speed, but in - * reality, they simply can't. At least, we give user chance - * to set sane value via kernel hints or sysctl. - */ - if (speed < 1) { - CHN_UNLOCK(parent); - r = resource_int_value( - device_get_name(parent->dev), - device_get_unit(parent->dev), - VCHAN_SPD_HINT(direction), - &speed); - CHN_LOCK(parent); - if (r != 0) { - /* - * No saved value, no hint, NOTHING. - * - * Workaround for sb16 running - * poorly at 45k / 49k. - */ - switch (parent_caps->maxspeed) { - case 45000: - case 49000: - speed = 44100; - break; - default: - speed = VCHAN_DEFAULT_SPEED; - if (speed > parent_caps->maxspeed) - speed = parent_caps->maxspeed; - break; - } - if (speed < parent_caps->minspeed) - speed = parent_caps->minspeed; - } - } + save = 0; - if (feeder_rate_round) { - /* - * Limit speed based on driver caps. - * This is supposed to help fixed rate, non-VRA - * AC97 cards, but.. (see below) - */ - if (speed < parent_caps->minspeed) - speed = parent_caps->minspeed; - if (speed > parent_caps->maxspeed) - speed = parent_caps->maxspeed; - } + if (!err && vchanfmt == 0) { + const char *vfmt; + CHN_UNLOCK(parent); + r = resource_string_value(device_get_name(parent->dev), + device_get_unit(parent->dev), VCHAN_FMT_HINT(direction), + &vfmt); + CHN_LOCK(parent); + if (r != 0) + vfmt = NULL; + if (vfmt != NULL) { + vchanfmt = snd_str2afmt(vfmt); + if (vchanfmt != 0 && + !(vchanfmt & VCHAN_SUPPORTED_FORMAT)) + vchanfmt = 0; + } + if (vchanfmt == 0) + vchanfmt = VCHAN_DEFAULT_AFMT; + save = 1; + } + + if (!err && vchanspd == 0) { + /* + * This is very sad. Few soundcards advertised as being + * able to do (insanely) higher/lower speed, but in + * reality, they simply can't. At least, we give user chance + * to set sane value via kernel hints or sysctl. + */ + CHN_UNLOCK(parent); + r = resource_int_value(device_get_name(parent->dev), + device_get_unit(parent->dev), VCHAN_SPD_HINT(direction), + &vchanspd); + CHN_LOCK(parent); + if (r != 0) { /* - * We still need to limit the speed between - * feeder_rate_min <-> feeder_rate_max. This is - * just an escape goat if all of the above failed - * miserably. + * No saved value, no hint, NOTHING. + * + * Workaround for sb16 running + * poorly at 45k / 49k. */ - if (speed < feeder_rate_min) - speed = feeder_rate_min; - if (speed > feeder_rate_max) - speed = feeder_rate_max; + switch (parent_caps->maxspeed) { + case 45000: + case 49000: + vchanspd = 44100; + break; + default: + vchanspd = VCHAN_DEFAULT_SPEED; + if (vchanspd > parent_caps->maxspeed) + vchanspd = parent_caps->maxspeed; + break; + } + if (vchanspd < parent_caps->minspeed) + vchanspd = parent_caps->minspeed; + } + save = 1; + } - err = chn_setspeed(parent, speed); + if (!err) { + if (feeder_rate_round) { /* - * Try to avoid FEEDER_RATE on parent channel if the - * requested value is not supported by the hardware. + * Limit speed based on driver caps. + * This is supposed to help fixed rate, non-VRA + * AC97 cards, but.. (see below) */ - if (!err && feeder_rate_round && - (parent->feederflags & (1 << FEEDER_RATE))) { - speed = sndbuf_getspd(parent->bufhard); - err = chn_setspeed(parent, speed); - } + if (vchanspd < parent_caps->minspeed) + vchanspd = parent_caps->minspeed; + if (vchanspd > parent_caps->maxspeed) + vchanspd = parent_caps->maxspeed; + } - if (!err) { - /* - * Save new value. - */ - CHN_UNLOCK(parent); - if (direction == PCMDIR_PLAY_VIRTUAL) { - d->pvchanformat = vchanfmt; - d->pvchanrate = speed; - } else { - d->rvchanformat = vchanfmt; - d->rvchanrate = speed; - } - CHN_LOCK(parent); - } + /* + * We still need to limit the speed between + * feeder_rate_min <-> feeder_rate_max. This is + * just an escape goat if all of the above failed + * miserably. + */ + if (vchanspd < feeder_rate_min) + vchanspd = feeder_rate_min; + if (vchanspd > feeder_rate_max) + vchanspd = feeder_rate_max; + + err = chn_reset(parent, vchanfmt, vchanspd); + /* + * Try to avoid FEEDER_RATE on parent channel if the + * requested value is not supported by the hardware. + */ + if (!err && feeder_rate_round && + (parent->feederflags & (1 << FEEDER_RATE))) { + vchanspd = sndbuf_getspd(parent->bufhard); + err = chn_reset(parent, vchanfmt, vchanspd); } - - if (err) { - CHN_REMOVE(parent, ch, children); - parent->flags &= ~CHN_F_HAS_VCHAN; - CHN_UNLOCK(parent); - pcm_lock(d); - if (pcm_chn_remove(d, ch) == 0) { - pcm_unlock(d); - pcm_chn_destroy(ch); - } else - pcm_unlock(d); - CHN_LOCK(parent); - return (err); + } + + if (!err && save) { + /* + * Save new value. + */ + vchanfmt = sndbuf_getfmt(parent->bufsoft); + vchanspd = sndbuf_getspd(parent->bufsoft); + CHN_UNLOCK(parent); + PCM_LOCK(d); + if (direction == PCMDIR_PLAY_VIRTUAL) { + d->pvchanformat = vchanfmt; + d->pvchanrate = vchanspd; + } else { + d->rvchanformat = vchanfmt; + d->rvchanrate = vchanspd; } + PCM_UNLOCK(d); + CHN_LOCK(parent); + } + + if (err) { + CHN_REMOVE(parent, ch, children); + parent->flags &= ~CHN_F_HAS_VCHAN; + CHN_UNLOCK(parent); + PCM_LOCK(d); + if (pcm_chn_remove(d, ch) == 0) { + PCM_UNLOCK(d); + pcm_chn_destroy(ch); + } else + PCM_UNLOCK(d); + CHN_LOCK(parent); } - return (0); + return (err); } int vchan_destroy(struct pcm_channel *c) { - struct pcm_channel *parent = c->parentchannel; - struct snddev_info *d = parent->parentsnddev; - uint32_t spd; + struct pcm_channel *parent; + struct snddev_info *d; int err; + KASSERT(c != NULL && c->parentchannel != NULL && + c->parentsnddev != NULL, ("%s(): invalid channel=%p", + __func__, c)); + + CHN_LOCKASSERT(c); + + d = c->parentsnddev; + parent = c->parentchannel; + PCM_BUSYASSERT(d); + CHN_LOCKASSERT(parent); - CHN_LOCK(parent); - if (!(parent->flags & CHN_F_BUSY)) { - CHN_UNLOCK(parent); + CHN_UNLOCK(c); + + if (!(parent->flags & CHN_F_BUSY)) return (EBUSY); - } - if (CHN_EMPTY(parent, children)) { - CHN_UNLOCK(parent); + + if (CHN_EMPTY(parent, children)) return (EINVAL); - } /* remove us from our parent's children list */ CHN_REMOVE(parent, c, children); if (CHN_EMPTY(parent, children)) { parent->flags &= ~(CHN_F_BUSY | CHN_F_HAS_VCHAN); - spd = parent->speed; - if (chn_reset(parent, parent->format) == 0) - chn_setspeed(parent, spd); + chn_reset(parent, parent->format, parent->speed); } CHN_UNLOCK(parent); /* remove us from our grandparent's channel list */ - pcm_lock(d); + PCM_LOCK(d); err = pcm_chn_remove(d, c); - pcm_unlock(d); + PCM_UNLOCK(d); /* destroy ourselves */ if (!err) err = pcm_chn_destroy(c); + CHN_LOCK(parent); + return (err); } -int +void vchan_initsys(device_t dev) { -#ifdef SND_DYNSYSCTL struct snddev_info *d; int unit; @@ -997,34 +772,43 @@ SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); + sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); + SYSCTL_ADD_PROC(&d->play_sysctl_ctx, + SYSCTL_CHILDREN(d->play_sysctl_tree), + OID_AUTO, "dynamic_vchan", CTLTYPE_INT | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, + sysctl_dev_pcm_dynamic_vchan, "I", + "vchan's dynamic format/rate selection"); SYSCTL_ADD_PROC(&d->play_sysctl_ctx, SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanrate, "I", "virtual channel mixing speed/rate"); + sysctl_dev_pcm_vchanrate, "I", "virtual channel mixing speed/rate"); SYSCTL_ADD_PROC(&d->play_sysctl_ctx, SYSCTL_CHILDREN(d->play_sysctl_tree), OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, PLAY), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanformat, "A", "virtual channel format"); + sysctl_dev_pcm_vchanformat, "A", "virtual channel format"); /* Rec */ SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchans, "I", "total allocated virtual channel"); + sysctl_dev_pcm_vchans, "I", "total allocated virtual channel"); + SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, + SYSCTL_CHILDREN(d->rec_sysctl_tree), + OID_AUTO, "dynamic_vchan", CTLTYPE_INT | CTLFLAG_RW, + VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, + sysctl_dev_pcm_dynamic_vchan, "I", + "vchan's dynamic format/rate selection"); SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanrate, "I", "virtual channel base speed/rate"); + sysctl_dev_pcm_vchanrate, "I", "virtual channel base speed/rate"); SYSCTL_ADD_PROC(&d->rec_sysctl_ctx, SYSCTL_CHILDREN(d->rec_sysctl_tree), OID_AUTO, "vchanformat", CTLTYPE_STRING | CTLFLAG_RW, VCHAN_SYSCTL_DATA(unit, REC), VCHAN_SYSCTL_DATA_SIZE, - sysctl_hw_snd_vchanformat, "A", "virtual channel format"); -#endif - - return (0); + sysctl_dev_pcm_vchanformat, "A", "virtual channel format"); } --- sys/dev/sound/pcm/vchan.h.orig 2007-06-01 02:43:32.000000000 +0800 +++ sys/dev/sound/pcm/vchan.h 2009-01-17 19:29:20.000000000 +0800 @@ -1,5 +1,6 @@ /*- - * Copyright (c) 2001 Cameron Grant + * Copyright (c) 2005-2009 Ariff Abdullah + * Copyright (c) 2001 Cameron Grant * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,14 +29,23 @@ int vchan_create(struct pcm_channel *parent, int num); int vchan_destroy(struct pcm_channel *c); -int vchan_initsys(device_t dev); +void vchan_initsys(device_t dev); /* * Default speed / format */ #define VCHAN_DEFAULT_SPEED 48000 -#define VCHAN_DEFAULT_AFMT (AFMT_S16_LE | AFMT_STEREO) -#define VCHAN_DEFAULT_STRFMT "s16le" + +#if BYTE_ORDER == LITTLE_ENDIAN +#define VCHAN_DEFAULT_AFMT SND_FORMAT(AFMT_S16_LE, 2, 0) +#define VCHAN_DEFAULT_STRFMT "s16le:2.0" +#else +#define VCHAN_DEFAULT_AFMT SND_FORMAT(AFMT_S16_BE, 2, 0) +#define VCHAN_DEFAULT_STRFMT "s16be:2.0" +#endif + +#define VCHAN_SUPPORTED_FORMAT ((AFMT_8BIT & ~AFMT_G711) | \ + AFMT_16BIT | AFMT_24BIT | AFMT_32BIT) #define VCHAN_PLAY 0 #define VCHAN_REC 1 --- sys/dev/sound/sbus/cs4231.c.orig 2007-10-12 14:03:44.000000000 +0800 +++ sys/dev/sound/sbus/cs4231.c 2009-01-17 19:29:20.000000000 +0800 @@ -260,18 +260,18 @@ static u_int32_t cs4231_fmt[] = { - AFMT_U8, - AFMT_STEREO | AFMT_U8, - AFMT_MU_LAW, - AFMT_STEREO | AFMT_MU_LAW, - AFMT_A_LAW, - AFMT_STEREO | AFMT_A_LAW, - AFMT_IMA_ADPCM, - AFMT_STEREO | AFMT_IMA_ADPCM, - AFMT_S16_LE, - AFMT_STEREO | AFMT_S16_LE, - AFMT_S16_BE, - AFMT_STEREO | AFMT_S16_BE, + SND_FORMAT(AFMT_U8, 1, 0), + SND_FORMAT(AFMT_U8, 2, 0), + SND_FORMAT(AFMT_MU_LAW, 1, 0), + SND_FORMAT(AFMT_MU_LAW, 2, 0), + SND_FORMAT(AFMT_A_LAW, 1, 0), + SND_FORMAT(AFMT_A_LAW, 2, 0), + SND_FORMAT(AFMT_IMA_ADPCM, 1, 0), + SND_FORMAT(AFMT_IMA_ADPCM, 2, 0), + SND_FORMAT(AFMT_S16_LE, 1, 0), + SND_FORMAT(S16_LE 2), + SND_FORMAT(AFMT_S16_BE, 1, 0), + SND_FORMAT(AFMT_S16_BE, 2, 0), 0 }; @@ -1057,7 +1057,7 @@ return (0); } - encoding = format & ~AFMT_STEREO; + encoding = AFMT_ENCODING(format); fs = 0; switch (encoding) { case AFMT_U8: @@ -1084,7 +1084,7 @@ break; } - if (format & AFMT_STEREO) + if (AFMT_CHANNEL(format) > 1) fs |= CS_AFMT_STEREO; DPRINTF(("FORMAT: %s : 0x%x\n", ch->dir == PCMDIR_PLAY ? "playback" : --- sys/dev/sound/usb/uaudio.c.orig 2008-11-06 23:05:15.000000000 +0800 +++ sys/dev/sound/usb/uaudio.c 2009-01-17 19:29:20.000000000 +0800 @@ -94,6 +94,7 @@ #include #elif defined(__FreeBSD__) #include /* XXXXX */ +#include #include #include "feeder_if.h" #endif @@ -467,7 +468,6 @@ #elif defined(__FreeBSD__) static int audio_attach_mi(device_t); static int uaudio_init_params(struct uaudio_softc * sc, struct chan *ch, int mode); -static int uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose); /* for NetBSD compatibirity */ #define AUMODE_PLAY 0x01 @@ -3833,7 +3833,7 @@ if ((sc->sc_playchan.pipe != NULL) || (sc->sc_recchan.pipe != NULL)) return (-1); - switch(ch->format & 0x000FFFFF) { + switch(AFMT_ENCODING(ch->format)) { case AFMT_U8: enc = AUDIO_ENCODING_ULINEAR_LE; ch->precision = 8; @@ -3901,10 +3901,10 @@ default: enc = 0; ch->precision = 16; - printf("Unknown format %x\n", ch->format); + printf("Unknown format %x\n", AFMT_ENCODING(ch->format)); } - if (ch->format & AFMT_STEREO) { + if (AFMT_CHANNEL(ch->format) > 1) { ch->channels = 2; } else { ch->channels = 1; @@ -4073,10 +4073,7 @@ if (iterator->uaudio_fmt == 0) continue; - fmt = iterator->freebsd_fmt; - - if (numchan == 2) - fmt |= AFMT_STEREO; + fmt = SND_FORMAT(iterator->freebsd_fmt, numchan, 0); foundcount++; @@ -4190,7 +4187,7 @@ continue; for (iterator = accepted_conversion ; iterator->uaudio_fmt != 0 ; iterator++) - if (iterator->uaudio_fmt != format || iterator->freebsd_fmt != (ch->format&0xfffffff)) + if (iterator->uaudio_fmt != format || iterator->freebsd_fmt != AFMT_ENCODING(ch->format)) continue; if (iscontinuous) { if (speed >= UA_SAMP_LO(asf1d) && speed <= UA_SAMP_HI(asf1d)) { @@ -4496,104 +4493,17 @@ } static int -uaudio_sndstat_prepare_pcm(struct sbuf *s, device_t dev, int verbose) +uaudio_sndstat_prepare_pcm(SNDSTAT_PREPARE_PCM_ARGS) { - struct snddev_info *d; - struct pcm_channel *c; - struct pcm_feeder *f; device_t pa_dev = device_get_parent(dev); struct uaudio_softc *sc = device_get_softc(pa_dev); - if (verbose < 1) - return 0; - - d = device_get_softc(dev); - if (!d) - return ENXIO; - - PCM_BUSYASSERT(d); - - if (CHN_EMPTY(d, channels.pcm)) { - sbuf_printf(s, " (mixer only)"); - return 0; - } - - sbuf_printf(s, " (%dp:%dv/%dr:%dv channels%s%s)", - d->playcount, d->pvchancount, - d->reccount, d->rvchancount, - (d->flags & SD_F_SIMPLEX)? "" : " duplex", -#ifdef USING_DEVFS - (device_get_unit(dev) == snd_unit)? " default" : "" -#else - "" -#endif - ); + SNDSTAT_PREPARE_PCM_BEGIN(); - if (sc->uaudio_sndstat_flag != 0) { + if (sc->uaudio_sndstat_flag != 0) sbuf_cat(s, sbuf_data(&(sc->uaudio_sndstat))); - } - - if (verbose <= 1) - return 0; - - CHN_FOREACH(c, d, channels.pcm) { - - KASSERT(c->bufhard != NULL && c->bufsoft != NULL, - ("hosed pcm channel setup")); - - sbuf_printf(s, "\n\t"); - /* it would be better to indent child channels */ - sbuf_printf(s, "%s[%s]: ", c->parentchannel? c->parentchannel->name : "", c->name); - sbuf_printf(s, "spd %d", c->speed); - if (c->speed != sndbuf_getspd(c->bufhard)) - sbuf_printf(s, "/%d", sndbuf_getspd(c->bufhard)); - sbuf_printf(s, ", fmt 0x%08x", c->format); - if (c->format != sndbuf_getfmt(c->bufhard)) - sbuf_printf(s, "/0x%08x", sndbuf_getfmt(c->bufhard)); - sbuf_printf(s, ", flags 0x%08x, 0x%08x", c->flags, c->feederflags); - if (c->pid != -1) - sbuf_printf(s, ", pid %d", c->pid); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "interrupts %d, ", c->interrupts); - if (c->direction == PCMDIR_REC) - sbuf_printf(s, "overruns %d, feed %u, hfree %d, sfree %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getfree(c->bufhard), sndbuf_getfree(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - else - sbuf_printf(s, "underruns %d, feed %u, ready %d [b:%d/%d/%d|bs:%d/%d/%d]", - c->xruns, c->feedcount, sndbuf_getready(c->bufsoft), - sndbuf_getsize(c->bufhard), sndbuf_getblksz(c->bufhard), - sndbuf_getblkcnt(c->bufhard), - sndbuf_getsize(c->bufsoft), sndbuf_getblksz(c->bufsoft), - sndbuf_getblkcnt(c->bufsoft)); - sbuf_printf(s, "\n\t"); - - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "hardware" : "userland"); - sbuf_printf(s, " -> "); - f = c->feeder; - while (f->source != NULL) - f = f->source; - while (f != NULL) { - sbuf_printf(s, "%s", f->class->name); - if (f->desc->type == FEEDER_FMT) - sbuf_printf(s, "(0x%08x -> 0x%08x)", f->desc->in, f->desc->out); - if (f->desc->type == FEEDER_RATE) - sbuf_printf(s, "(%d -> %d)", FEEDER_GET(f, FEEDRATE_SRC), FEEDER_GET(f, FEEDRATE_DST)); - if (f->desc->type == FEEDER_ROOT || f->desc->type == FEEDER_MIXER || - f->desc->type == FEEDER_VOLUME) - sbuf_printf(s, "(0x%08x)", f->desc->out); - sbuf_printf(s, " -> "); - f = f->parent; - } - sbuf_printf(s, "{%s}", (c->direction == PCMDIR_REC)? "userland" : "hardware"); - } - - return 0; + SNDSTAT_PREPARE_PCM_END(); } void --- sys/dev/sound/usb/uaudio_pcm.c.orig 2007-06-17 14:10:43.000000000 +0800 +++ sys/dev/sound/usb/uaudio_pcm.c 2009-01-17 19:29:20.000000000 +0800 @@ -55,17 +55,21 @@ struct pcmchan_caps ua_playcaps; struct pcmchan_caps ua_reccaps; int vendor, product, release; + u_int32_t quirks; }; #define UAUDIO_DEFAULT_BUFSZ 16*1024 +#define UAUDIO_QUIRK_SWAPLR_PLAY (1 << 0) +#define UAUDIO_QUIRK_SWAPLR_REC (1 << 1) + static const struct { int vendor; int product; int release; - uint32_t dflags; + uint32_t quirks; } ua_quirks[] = { - { 0x1130, 0xf211, 0x0101, SD_F_PSWAPLR }, + { 0x1130, 0xf211, 0x0101, UAUDIO_QUIRK_SWAPLR_PLAY }, }; /************************************************************/ @@ -137,6 +141,55 @@ return 0; } +static struct pcmchan_matrix ua_chan_matrix_swap_2_0 = { + .id = SND_CHN_MATRIX_DRV, + .channels = 2, + .ext = 0, + .map = { + /* Right */ + [0] = { + .type = SND_CHN_T_FR, + .members = + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR + }, + /* Left */ + [1] = { + .type = SND_CHN_T_FL, + .members = + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL + }, + [2] = { + .type = SND_CHN_T_MAX, + .members = 0 + } + }, + .mask = SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FL, + .offset = { 1, 0, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1 } +}; + +static struct pcmchan_matrix * +ua_chan_getmatrix(kobj_t obj, void *data, u_int32_t format) +{ + struct ua_info *ua; + struct ua_chinfo *ch; + + ch = data; + ua = ch->parent; + + if (AFMT_CHANNEL(format) == 2 && + ((ch->dir == PCMDIR_PLAY && + (ua->quirks & UAUDIO_QUIRK_SWAPLR_PLAY)) || + (ch->dir == PCMDIR_REC && (ua->quirks & UAUDIO_QUIRK_SWAPLR_REC)))) + return (&ua_chan_matrix_swap_2_0); + + return (NULL); +} + static int ua_chan_setspeed(kobj_t obj, void *data, u_int32_t speed) { @@ -263,6 +316,7 @@ KOBJMETHOD(channel_trigger, ua_chan_trigger), KOBJMETHOD(channel_getptr, ua_chan_getptr), KOBJMETHOD(channel_getcaps, ua_chan_getcaps), + KOBJMETHOD(channel_getmatrix, ua_chan_getmatrix), { 0, 0 } }; @@ -357,7 +411,7 @@ struct sndcard_func *func; char status[SND_STATUSLEN]; device_t pa_dev; - u_int32_t nplay, nrec, flags; + u_int32_t nplay, nrec; int i; ua = malloc(sizeof(*ua), M_DEVBUF, M_WAITOK | M_ZERO); @@ -399,14 +453,14 @@ if (nrec > 1) nrec = 1; - flags = pcm_getflags(dev); + ua->quirks = 0; + for (i = 0; i < (sizeof(ua_quirks) / sizeof(ua_quirks[0])); i++) { if (ua->vendor == ua_quirks[i].vendor && ua->product == ua_quirks[i].product && ua->release == ua_quirks[i].release) - flags |= ua_quirks[i].dflags; + ua->quirks |= ua_quirks[i].quirks; } - pcm_setflags(dev, flags); #ifndef NO_RECORDING if (pcm_register(dev, ua, nplay, nrec)) { --- sys/dev/sound/version.h.orig 2007-06-16 11:37:27.000000000 +0800 +++ sys/dev/sound/version.h 2009-01-17 19:29:20.000000000 +0800 @@ -37,6 +37,6 @@ * Last 2 decimal places reserved for daily versioning, starting * with 0. */ -#define SND_DRV_VERSION 2007061600 +#define SND_DRV_VERSION 2009011702 #endif /* !_SND_VERSION_H_ */ --- sys/dev/usb2/sound/uaudio2.c.orig 2009-01-17 19:31:45.000000000 +0800 +++ sys/dev/usb2/sound/uaudio2.c 2009-01-17 19:29:20.000000000 +0800 @@ -670,7 +670,7 @@ if (sc->sc_uq_audio_swap_lr) { DPRINTF("hardware has swapped left and right\n"); - uaudio_pcm_setflags(dev, SD_F_PSWAPLR); + /* uaudio_pcm_setflags(dev, SD_F_PSWAPLR); */ } if (!(sc->sc_mix_info & SOUND_MASK_PCM)) { @@ -1341,11 +1341,13 @@ ch->pcm_cap.minspeed = ch->sample_rate; ch->pcm_cap.maxspeed = ch->sample_rate; - ch->pcm_cap.fmtlist[0] = ch->p_fmt->freebsd_fmt; - - if (ch->p_asf1d->bNrChannels == 2) { - ch->pcm_cap.fmtlist[0] |= AFMT_STEREO; - } + if (ch->p_asf1d->bNrChannels == 2) + ch->pcm_cap.fmtlist[0] = + SND_FORMAT(ch->p_fmt->freebsd_fmt, 2, 0); + else + ch->pcm_cap.fmtlist[0] = + SND_FORMAT(ch->p_fmt->freebsd_fmt, 1, 0); + ch->pcm_cap.fmtlist[1] = 0; @@ -1457,6 +1459,51 @@ return (&ch->pcm_cap); } +static struct pcmchan_matrix uaudio_chan_matrix_swap_2_0 = { + .id = SND_CHN_MATRIX_DRV, + .channels = 2, + .ext = 0, + .map = { + /* Right */ + [0] = { + .type = SND_CHN_T_FR, + .members = + SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FC | + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BR | + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SR + }, + /* Left */ + [1] = { + .type = SND_CHN_T_FL, + .members = + SND_CHN_T_MASK_FL | SND_CHN_T_MASK_FC | + SND_CHN_T_MASK_LF | SND_CHN_T_MASK_BL | + SND_CHN_T_MASK_BC | SND_CHN_T_MASK_SL + }, + [2] = { + .type = SND_CHN_T_MAX, + .members = 0 + } + }, + .mask = SND_CHN_T_MASK_FR | SND_CHN_T_MASK_FL, + .offset = { 1, 0, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1 } +}; + +struct pcmchan_matrix * +uaudio_chan_getmatrix(struct uaudio_chan *ch, uint32_t format) +{ + struct uaudio_softc *sc; + + sc = ch->priv_sc; + + if (sc != NULL && sc->sc_uq_audio_swap_lr != 0 && + AFMT_CHANNEL(format) == 2) + return (&uaudio_chan_matrix_swap_2_0); + + return (NULL); +} + int uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format) { --- sys/dev/usb2/sound/uaudio2.h.orig 2008-12-12 07:13:02.000000000 +0800 +++ sys/dev/usb2/sound/uaudio2.h 2009-01-17 19:29:20.000000000 +0800 @@ -47,6 +47,8 @@ uint32_t speed); extern int uaudio_chan_getptr(struct uaudio_chan *ch); extern struct pcmchan_caps *uaudio_chan_getcaps(struct uaudio_chan *ch); +extern struct pcmchan_matrix *uaudio_chan_getmatrix(struct uaudio_chan *ch, + uint32_t format); extern int uaudio_chan_set_param_format(struct uaudio_chan *ch, uint32_t format); extern int uaudio_chan_start(struct uaudio_chan *ch); --- sys/dev/usb2/sound/uaudio2_pcm.c.orig 2008-11-04 10:31:03.000000000 +0800 +++ sys/dev/usb2/sound/uaudio2_pcm.c 2009-01-17 19:29:20.000000000 +0800 @@ -101,6 +101,12 @@ return (uaudio_chan_getcaps(data)); } +static struct pcmchan_matrix * +ua_chan_getmatrix(kobj_t obj, void *data, uint32_t format) +{ + return (uaudio_chan_getmatrix(data, format)); +} + static kobj_method_t ua_chan_methods[] = { KOBJMETHOD(channel_init, ua_chan_init), KOBJMETHOD(channel_free, ua_chan_free), @@ -111,6 +117,7 @@ KOBJMETHOD(channel_trigger, ua_chan_trigger), KOBJMETHOD(channel_getptr, ua_chan_getptr), KOBJMETHOD(channel_getcaps, ua_chan_getcaps), + KOBJMETHOD(channel_getmatrix, ua_chan_getmatrix), {0, 0} }; --- sys/modules/sound/driver/Makefile.orig 2008-05-05 00:18:44.000000000 +0800 +++ sys/modules/sound/driver/Makefile 2009-01-17 19:29:20.000000000 +0800 @@ -12,4 +12,6 @@ SUBDIR+= via82c686 vibes driver uaudio .endif +SUBDIR+= null + .include --- sys/modules/sound/driver/null/Makefile.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/modules/sound/driver/null/Makefile 2009-01-17 19:29:20.000000000 +0800 @@ -0,0 +1,9 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../../../dev/sound + +KMOD= snd_null +SRCS= device_if.h bus_if.h +SRCS+= null.c + +.include --- sys/modules/sound/sound/Makefile.orig 2007-06-01 02:43:33.000000000 +0800 +++ sys/modules/sound/sound/Makefile 2009-01-17 19:29:20.000000000 +0800 @@ -9,13 +9,27 @@ SRCS= device_if.h bus_if.h isa_if.h pci_if.h opt_isa.h SRCS+= ac97_if.h channel_if.h feeder_if.h mixer_if.h SRCS+= ac97_if.c channel_if.c feeder_if.c mixer_if.c +SRCS+= feeder_chain.c feeder_eq.c feeder_format.c +SRCS+= feeder_matrix.c feeder_mixer.c +SRCS+= feeder_eq_gen.h feeder_rate_gen.h snd_fxdiv_gen.h SRCS+= mpu_if.h mpufoi_if.h synth_if.h SRCS+= mpu_if.c mpufoi_if.c synth_if.c SRCS+= ac97.c ac97_patch.c buffer.c channel.c clone.c dsp.c -SRCS+= fake.c feeder.c feeder_fmt.c feeder_rate.c feeder_volume.c +SRCS+= fake.c feeder.c feeder_rate.c feeder_volume.c SRCS+= mixer.c sndstat.c sound.c unit.c vchan.c SRCS+= midi.c mpu401.c sequencer.c +feeder_eq_gen.h: + ${AWK} -f @/tools/feeder_eq_mkfilter.awk -- ${FEEDER_EQ_PRESETS} > ${.TARGET} + +feeder_rate_gen.h: + ${AWK} -f @/tools/feeder_rate_mkfilter.awk -- ${FEEDER_RATE_PRESETS} > ${.TARGET} + +snd_fxdiv_gen.h: + ${AWK} -f @/tools/snd_fxdiv_gen.awk -- > ${.TARGET} + +CLEANFILES+= feeder_eq_gen.h feeder_rate_gen.h snd_fxdiv_gen.h + EXPORT_SYMS= YES # XXX evaluate .if ${MACHINE_ARCH} == "sparc64"