--- sys/conf/files.orig 2007-10-28 23:56:09.000000000 +0800 +++ sys/conf/files 2007-10-28 23:58:17.000000000 +0800 @@ -65,6 +65,11 @@ 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_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" 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" \ @@ -1007,7 +1012,8 @@ dev/sound/pcm/feeder.c optional sound dev/sound/pcm/feeder_fmt.c optional sound dev/sound/pcm/feeder_if.m optional sound -dev/sound/pcm/feeder_rate.c optional sound +dev/sound/pcm/feeder_rate.c optional sound \ + dependency "feeder_rate_gen.h" dev/sound/pcm/feeder_volume.c optional sound dev/sound/pcm/mixer.c optional sound dev/sound/pcm/mixer_if.m optional sound --- sys/tools/feeder_rate_mkfilter.awk.orig 1970-01-01 07:30:00.000000000 +0800 +++ sys/tools/feeder_rate_mkfilter.awk 2007-10-28 23:55:22.000000000 +0800 @@ -0,0 +1,570 @@ +#!/usr/bin/awk -f +# +# Copyright (c) 2007 Ariff Abdullah +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# + +# +# FIR filter design by windowing method. This might become one of the +# funniest joke I've ever written due to too many tricks being applied to +# ensure maximum precision (well, in fact this is already have the same +# precision granularity compared to its C counterpart). Nevertheless, it's +# working, precise, dynamically tunable based on "presets" (so that +# LIARS^H^H^H^H^H audiophiles can tune it based on their own +# placebo^H^H^H^H^H^H^H needs). +# + +# +# Some basic Math functions. +# +function abs(x) +{ + return (((x < 0) ? -x : x) + 0); +} + +function fabs(x) +{ + return (((x < 0.0) ? -x : x) + 0.0); +} + +function floor(x, r) +{ + r = int(x); + if (r > x) + r--; + return (r + 0); +} + +#function xfloor(x, a, r) +#{ +# split(sprintf("%f", x), a, "."); +# r = abs(a[1]) + 0; +# if (x < 0.0) { +# if ((x + r) != 0.0) +# r++; +# r = -r; +# } +# return (r + 0); +#} + +function pow(x, y) +{ + return (exp(1.0 * y * log(1.0 * x))); +} + +# +# What the hell... +# +function shl(x, y) +{ + while (y > 0) { + x *= 2; + y--; + } + return (x); +} + +function shr(x, y) +{ + while (y > 0) { + x /= 2; + y--; + } + return (x); +} + +# +# Kaiser linear piecewise functions. +# +function kaiserAttn2Beta(attn, beta) +{ + if (attn < 0.0) + return (Z_KAISER_BETA_DEFAULT); + + if (attn > 50.0) + beta = 0.1102 * ((1.0 * attn) - 8.7); + else if (attn > 21.0) + beta = (0.5842 * pow((1.0 * attn) - 21.0, 0.4)) + \ + (0.07886 * ((1.0 * attn) - 21.0)); + else + beta = 0.0; + + return (beta); +} + +function kaiserBeta2Attn(beta, x, y, i, attn, xbeta) +{ + if (beta < Z_WINDOW_KAISER) + return (Z_KAISER_ATTN_DEFAULT); + + if (beta > kaiserAttn2Beta(50.0)) + attn = ((1.0 * beta) / 0.1102) + 8.7; + else { + x = 21.0; + y = 50.0; + attn = 0.5 * (x + y); + for (i = 0; i < 128; i++) { + xbeta = kaiserAttn2Beta(attn) + if (beta == xbeta || \ + (i > 63 && \ + fabs(beta - xbeta) < Z_KAISER_EPSILON)) + break; + if (beta > xbeta) + x = attn; + else + y = attn; + attn = 0.5 * (x + y); + } + } + + return (attn); +} + +function kaiserRolloff(len, attn) +{ + return (1.0 - (((1.0 * attn) - 7.95) / (((1.0 * len) - 1.0) * 14.36))); +} + +# +# 0th order modified Bessel function of the first kind. +# +function I0(x, s, u, n, h, t) +{ + s = n = u = 1.0; + h = x * 0.5; + + do { + t = h / n; + n += 1.0; + t *= t; + u *= t; + s += u; + } while (u >= (I0_EPSILON * s)); + + return (s); +} + +function wname(beta) +{ + if (beta >= Z_WINDOW_KAISER) + return ("Kaiser"); + else if (beta == Z_WINDOW_BLACKMAN_NUTTALL) + return ("Blackman - Nuttall"); + else if (beta == Z_WINDOW_NUTTALL) + return ("Nuttall"); + else if (beta == Z_WINDOW_BLACKMAN_HARRIS) + return ("Blackman - Harris"); + else if (beta == Z_WINDOW_BLACKMAN) + return ("Blackman"); + else if (beta == Z_WINDOW_HAMMING) + return ("Hamming"); + else if (beta == Z_WINDOW_HANN) + return ("Hann"); + else + return ("What The Hell !?!?"); +} + +function rolloff_round(x) +{ + if (x < 0.67) + x = 0.67; + else if (x > 1.0) + x = 1.0; + + return (x); +} + +function lpf(imp, n, rolloff, beta, num, i, j, x, nm, ibeta, w) +{ + rolloff = rolloff_round(rolloff + Z_NYQUIST_HOVER * (1.0 - rolloff)); + imp[0] = rolloff; + + # + # Generate ideal sinc impulses, locate the last zero-crossing and pad + # the remaining with 0. + # + # Note that there are other (faster) ways of calculating this without + # the misery of traversing the entire sinc given the fact that the + # distance between each zero crossings is actually the bandwidth of + # the impulses, but it seems having 0.0001% chances of failure due to + # limited precision. + # + j = n; + for (i = 1; i < n; i++) { + x = (M_PI * i) / (1.0 * num); + imp[i] = sin(x * rolloff) / x; + if (i != 1 && (imp[i] * imp[i - 1]) <= 0.0) + j = i; + } + + for (i = j; i < n; i++) + imp[i] = 0.0; + + 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 = ((nmult * num) / 2) + 1; + + lpf(imp, nwing, rolloff, beta, num); + + mwing = imp["impulse_length"]; + nrolloff = imp["rolloff"]; + quality = imp["quality"]; + + dcgain = 0.0; + for (i = num; i < mwing; i += num) + dcgain += imp[i]; + dcgain *= 2.0; + dcgain += imp[0]; + + for (i = 0; i < nwing; i++) { + imp[i] /= dcgain; + imp["fixed", i] = floor((imp[i] * Z_COEFF_ONE) + 0.5); + } + + if (quality > 2) + printf("\n"); + printf("/*\n"); + printf(" * quality = %d\n", quality); + printf(" * window = %s\n", wname(beta)); + if (beta >= Z_WINDOW_KAISER) { + printf(" * beta: %.2f\n", beta); + printf(" * stop: -%.2f dB\n", \ + kaiserBeta2Attn(beta)); + } + printf(" * length = %d\n", nmult); + printf(" * bandwidth = %.2f%%", rolloff * 100.0); + if (rolloff != nrolloff) { + printf(" + %.2f%% = %.2f%% (nyquist hover: %.2f%%)", \ + (nrolloff - rolloff) * 100.0, nrolloff * 100.0, \ + Z_NYQUIST_HOVER * 100.0); + } + printf("\n"); + printf(" * drift = %d\n", num); + printf(" * width = %d\n", mwing); + printf(" */\n"); + printf("static int32_t z_coeff_q%d[%d] = {", quality, nwing); + for (i = 0; i < nwing; i++) { + if ((i % 7) == 0) + printf("\n"); + v = imp["fixed", i]; + printf(" %s0x%05x,", (v < 0) ? "-" : " ", abs(v)); + } + printf("\n};\n\n"); + printf("/*\n"); + printf(" * q%d differences.\n", quality); + printf(" */\n"); + printf("static int32_t z_dcoeff_q%d[%d] = {", quality, nwing); + for (i = 1; i <= nwing; i++) { + if ((i % 7) == 1) + printf("\n"); + v = -imp["fixed", i - 1]; + if (i != nwing) + v += imp["fixed", i]; + printf(" %s0x%05x,", (v < 0) ? "-" : " ", abs(v)); + } + printf("\n};\n"); + + return (nwing); +} + +function filter_parse(s, a, i, attn, alen) +{ + split(s, a, ":"); + alen = length(a); + + if (alen == 1 || alen == 2) { + if (a[1] == "nyquist_hover") { + i = 1.0 * a[2]; + Z_NYQUIST_HOVER = (i > 0.0 && i < 1.0) ? i : 0.0; + return (-1); + } + i = 1; + if (alen == 1) { + attn = Z_KAISER_ATTN_DEFAULT; + Popts["beta"] = Z_KAISER_BETA_DEFAULT; + } else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) { + Popts["beta"] = Z_WINDOWS[a[1]]; + i = floor(a[2]) + 1; + i -= i % 2; + Popts["nmult"] = i; + if (i < 28) + i = 28; + i = 1.0 - (6.44 / i); + Popts["rolloff"] = rolloff_round(i); + return (0); + } else { + attn = 1.0 * a[i++]; + Popts["beta"] = kaiserAttn2Beta(attn); + } + i = floor(a[i]) + 1; + i -= i % 2; + Popts["nmult"] = i; + if (i > 7 && i < 28) + i = 27; + i = kaiserRolloff(i, attn); + Popts["rolloff"] = rolloff_round(i); + + return (0); + } + + if (!(alen == 3 || alen == 4)) + return (-1); + + i = 2; + + if (a[1] == "kaiser") { + if (alen > 2) + Popts["beta"] = 1.0 * a[i++]; + else + Popts["beta"] = Z_KAISER_BETA_DEFAULT; + } else if (Z_WINDOWS[a[1]] < Z_WINDOW_KAISER) + Popts["beta"] = Z_WINDOWS[a[1]]; + else if (1.0 * a[1] < Z_WINDOW_KAISER) + return (-1); + else + Popts["beta"] = kaiserAttn2Beta(1.0 * a[1]); + Popts["nmult"] = floor(a[i++]) + 1; + Popts["nmult"] -= Popts["nmult"] % 2; + if (a[1] == "kaiser" && alen == 3) + i = kaiserRolloff(Popts["nmult"], \ + kaiserBeta2Attn(Popts["beta"])); + else + i = 1.0 * a[i]; + Popts["rolloff"] = rolloff_round(i); + + return (0); +} + +BEGIN { + I0_EPSILON = 1e-21; + M_PI = atan2(0.0, -1.0); + + Z_FULL_SHIFT = 30; + Z_FULL_ONE = shl(1, Z_FULL_SHIFT); + + # + # 6, 7, or 8 depending on how much you can trade off between memory + # consumption (due to large tables) and precision / quality. + # + Z_DRIFT_SHIFT = 7; + Z_DRIFT_ONE = shl(1, Z_DRIFT_SHIFT); + + Z_SHIFT = Z_FULL_SHIFT - Z_DRIFT_SHIFT; + Z_ONE = shl(1, Z_SHIFT); + Z_MASK = Z_ONE - 1; + + Z_COEFF_SHIFT = 15 + (Z_DRIFT_SHIFT - 4); + if (Z_COEFF_SHIFT > 19) + Z_COEFF_SHIFT = 19; + else if (Z_COEFF_SHIFT < 15) + Z_COEFF_SHIFT = 15; + + Z_COEFF_ONE = shl(1, Z_COEFF_SHIFT); + Z_COEFF_UNSHIFT = Z_SHIFT - Z_COEFF_SHIFT; + + Z_LINEAR_SHIFT = 8; + Z_LINEAR_ONE = shl(1, Z_LINEAR_SHIFT) + + Z_WINDOW_KAISER = 0.0; + Z_WINDOW_BLACKMAN_NUTTALL = -1.0; + Z_WINDOW_NUTTALL = -2.0; + Z_WINDOW_BLACKMAN_HARRIS = -3.0; + Z_WINDOW_BLACKMAN = -4.0; + Z_WINDOW_HAMMING = -5.0; + Z_WINDOW_HANN = -6.0; + + Z_WINDOWS["blackman_nuttall"] = Z_WINDOW_BLACKMAN_NUTTALL; + Z_WINDOWS["nuttall"] = Z_WINDOW_NUTTALL; + Z_WINDOWS["blackman_harris"] = Z_WINDOW_BLACKMAN_HARRIS; + Z_WINDOWS["blackman"] = Z_WINDOW_BLACKMAN; + Z_WINDOWS["hamming"] = Z_WINDOW_HAMMING; + Z_WINDOWS["hann"] = Z_WINDOW_HANN; + + Z_KAISER_2_BLACKMAN_BETA = 8.568611; + Z_KAISER_2_BLACKMAN_NUTTALL_BETA = 11.98; + + Z_KAISER_ATTN_DEFAULT = 100; + Z_KAISER_BETA_DEFAULT = kaiserAttn2Beta(Z_KAISER_ATTN_DEFAULT); + + Z_KAISER_EPSILON = 1e-21; + + # + # This is practically a joke. + # + Z_NYQUIST_HOVER = 0.0; + + if (ARGC < 2) { + ARGC = 1; + ARGV[ARGC++] = "100:8"; + ARGV[ARGC++] = "100:16"; + ARGV[ARGC++] = "100:32:0.7929"; + ARGV[ARGC++] = "100:64:0.8990"; + ARGV[ARGC++] = "100:128:0.9499"; + } + + printf("#ifndef _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"); + printf("#define Z_COEFF_TAB_SIZE\t\t\t\t\t\t\\\n"); + printf("\t((int32_t)(sizeof(z_coeff_tab) /"); + printf(" sizeof(z_coeff_tab[0])))\n\n"); + printf("#define Z_FULL_SHIFT\t\t%d\n", Z_FULL_SHIFT); + printf("#define Z_FULL_ONE\t\t0x%08xU\n", Z_FULL_ONE); + printf("\n"); + printf("#define Z_DRIFT_SHIFT\t\t%d\n", Z_DRIFT_SHIFT); + printf("#define Z_DRIFT_ONE\t\t0x%08xU\n", Z_DRIFT_ONE); + printf("\n"); + printf("#define Z_SHIFT\t\t\t%d\n", Z_SHIFT); + printf("#define Z_ONE\t\t\t0x%08xU\n", Z_ONE); + printf("#define Z_MASK\t\t\t0x%08xU\n", Z_MASK); + printf("\n"); + printf("#define Z_COEFF_SHIFT\t\t%d\n", Z_COEFF_SHIFT); + printf("#define Z_COEFF_UNSHIFT\t\t%d\n", Z_COEFF_UNSHIFT); + printf("\n"); + printf("#define Z_LINEAR_SHIFT\t\t%d\n", Z_LINEAR_SHIFT); + printf("#define Z_LINEAR_ONE\t\t0x%08xU\n", Z_LINEAR_ONE); + printf("\n"); + printf("#define Z_QUALITY_ZOH\t\t0\n"); + printf("#define Z_QUALITY_LINEAR\t1\n"); + printf("#define Z_QUALITY_SINC\t\t%d\n", ((length(ztab) - 1) / 2) + 2); + printf("\n"); + printf("#define Z_QUALITY_MIN\t\t0\n"); + printf("#define Z_QUALITY_MAX\t\t%d\n", length(ztab) + 1); + printf("\n#endif /* !_FEEDER_RATE_GEN_H_ */\n"); +} --- sys/dev/sound/pcm/channel.c.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/channel.c 2007-10-30 01:55:19.000000000 +0800 @@ -107,6 +107,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; @@ -1102,6 +1176,19 @@ c->feederflags = 0; c->sm = NULL; + /* Only Front Left/Right, for now. */ + c->matrix[0] = SND_CHN_T_FL; + c->matrix[1] = SND_CHN_T_FR; + c->matrix[2] = SND_CHN_T_MAX; + + 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; + + c->volume[SND_VOL_C_MASTER][SND_CHN_T_FL] = SND_VOL_0DB_MASTER; + c->volume[SND_VOL_C_MASTER][SND_CHN_T_FR] = SND_VOL_0DB_MASTER; + + chn_vpc_reset(c, SND_VOL_C_PCM, 1); + ret = ENODEV; CHN_UNLOCK(c); /* XXX - Unlock for CHANNEL_INIT() malloc() call */ c->devinfo = CHANNEL_INIT(c->methods, devinfo, b, c, direction); @@ -1202,21 +1289,94 @@ return r; } +/* XXX Obsolete. Use *_matrix() variant instead. */ int chn_setvolume(struct pcm_channel *c, int left, int right) { + 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_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t 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); - /* 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; + + 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 = 0; c->matrix[i] != SND_CHN_T_MAX; i++) { + vt = c->matrix[i]; + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } + } else + c->volume[SND_VOL_C_VAL(vc)][vt] = + SND_VOL_CALC_VAL(c->volume, vc, vt); + } + + return (val); +} + +int +chn_getvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t 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); + + return (c->volume[vc][vt]); +} + +void +chn_vpc_reset(struct pcm_channel *c, snd_volume_class_t 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 = 0; c->matrix[i] != SND_CHN_T_MAX; i++) + CHN_SETVOLUME(c, vc, c->matrix[i], + c->volume[vc][SND_CHN_T_VOL_0DB]); } static u_int32_t @@ -1840,11 +2000,13 @@ static int chn_buildfeeder(struct pcm_channel *c) { + struct snddev_info *d; struct feeder_class *fc; struct pcm_feederdesc desc; + struct pcm_feeder *f; struct snd_mixer *m; - u_int32_t tmp[2], type, flags, hwfmt, *fmtlist; - int err; + u_int32_t tmp[2], type, flags, hwfmt, parent, *fmtlist; + int err, vol, left, right; char fmtstr[AFMTSTR_MAXSZ]; CHN_LOCKASSERT(c); @@ -1894,26 +2056,28 @@ } 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; + d = c->parentsnddev; + + if (c->direction == PCMDIR_PLAY && (d->flags & SD_F_SOFTPCMVOL) && + d->mixer_dev != NULL) + m = d->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)) + if (((d->flags & SD_F_VPC) && CHN_EMPTY(c, children)) || + (!(d->flags & SD_F_VPC) && (d->flags & SD_F_SOFTPCMVOL) && + !(c->flags & CHN_F_VIRTUAL))) 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); + if (!(c->flags & CHN_F_VIRTUAL) && ((c->direction == PCMDIR_PLAY && + (d->flags & SD_F_PSWAPLR)) || (c->direction == PCMDIR_REC && + (d->flags & SD_F_RSWAPLR)))) c->feederflags |= 1 << FEEDER_SWAPLR; flags = c->feederflags; + fmtlist = chn_getcaps(c)->fmtlist; DEB(printf("feederflags %x\n", flags)); @@ -1927,9 +2091,11 @@ 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)); + strlcpy(fmtstr,"s32le", + sizeof(fmtstr)); else if (c->feeder->desc->out & AFMT_24BIT) - strlcpy(fmtstr, "s24le", sizeof(fmtstr)); + strlcpy(fmtstr, "s24le", + sizeof(fmtstr)); else { /* * 8bit doesn't provide enough headroom @@ -1937,20 +2103,23 @@ * creating too much noises. Force to * 16bit instead. */ - strlcpy(fmtstr, "s16le", sizeof(fmtstr)); + strlcpy(fmtstr, "s16le", + sizeof(fmtstr)); } if (!(c->feeder->desc->out & AFMT_8BIT) && - c->feeder->desc->out & AFMT_BIGENDIAN) + 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)) + if (!(c->feeder->desc->out & AFMT_SIGNED)) afmtstr_swap_sign(fmtstr); - desc.in = afmtstr2afmt(NULL, fmtstr, AFMTSTR_MONO_RETURN); + 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) + (c->feeder->desc->out & AFMT_STEREO) || + (type != FEEDER_VOLUME && + (flags & (1 << FEEDER_VOLUME)))) desc.in |= AFMT_STEREO; desc.out = desc.in; } else if (type == FEEDER_SWAPLR) { @@ -2024,36 +2193,40 @@ 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 (flags & (1 << FEEDER_VOLUME)) { + if (m != NULL) { + CHN_UNLOCK(c); + vol = mix_get(m, SOUND_MIXER_PCM); if (vol == -1) { device_printf(c->dev, - "Soft Volume: Failed to read parent " + "Soft PCM Volume: Failed to read " "default value\n"); vol = 100 | (100 << 8); } - left = (left * (vol & 0x7f)) / 100; - right = (right * ((vol >> 8) & 0x7f)) / 100; + 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, SND_VOL_C_MASTER, SND_CHN_T_FL, left); + CHN_SETVOLUME(c, SND_VOL_C_MASTER, SND_CHN_T_FR, right); } - CHN_LOCK(c); - chn_setvolume(c, left, right); + f = chn_findfeeder(c, FEEDER_VOLUME); + if (f != NULL) + FEEDER_SET(f, FEEDVOL_CLASS, SND_VOL_C_PCM); } - return 0; + return (0); } int --- sys/dev/sound/pcm/channel.h.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/channel.h 2007-10-30 01:55:19.000000000 +0800 @@ -26,6 +26,8 @@ * $FreeBSD: src/sys/dev/sound/pcm/channel.h,v 1.37 2007/06/16 03:37:28 ariff Exp $ */ +#include + struct pcmchan_caps { u_int32_t minspeed, maxspeed; u_int32_t *fmtlist; @@ -63,7 +65,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 +77,6 @@ struct pcm_feeder *feeder; u_int32_t align; - int volume; int latency; u_int32_t speed; u_int32_t format; @@ -90,6 +94,7 @@ device_t dev; int unit; char name[CHN_NAMELEN]; + char comm[MAXCOMLEN + 1]; struct mtx *lock; int trigger; /** @@ -139,9 +144,15 @@ struct { SLIST_ENTRY(pcm_channel) link; } busy; + struct { + SLIST_ENTRY(pcm_channel) link; + } opened; } pcm; } channels; + int volume[SND_VOL_C_MAX][SND_CHN_T_VOL_MAX]; + int matrix[SND_CHN_T_MAX + 1]; + void *data1, *data2; }; @@ -172,9 +183,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 +193,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 +203,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)) @@ -228,6 +253,11 @@ int chn_setdir(struct pcm_channel *c, int dir); int chn_reset(struct pcm_channel *c, u_int32_t fmt); int chn_setvolume(struct pcm_channel *c, int left, int right); +int chn_setvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt, int val); +int chn_getvolume_matrix(struct pcm_channel *c, snd_volume_class_t vc, + snd_channel_t vt); +void chn_vpc_reset(struct pcm_channel *c, snd_volume_class_t vc, int force); int chn_setspeed(struct pcm_channel *c, int speed); int chn_setformat(struct pcm_channel *c, u_int32_t fmt); int chn_setblocksize(struct pcm_channel *c, int blkcnt, int blksz); @@ -253,6 +283,13 @@ int chn_getrates(struct pcm_channel *c, int **rates); int chn_syncdestroy(struct pcm_channel *c); +#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_GETVOLUME(x, y, z) ((x)->volume[y][z]) +#endif + #ifdef OSSV4_EXPERIMENT int chn_getpeaks(struct pcm_channel *c, int *lpeak, int *rpeak); #endif --- sys/dev/sound/pcm/dsp.c.orig 2007-07-04 20:33:11.000000000 +0800 +++ sys/dev/sound/pcm/dsp.c 2007-10-30 01:55:19.000000000 +0800 @@ -35,12 +35,14 @@ 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 @@ -200,7 +202,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 +212,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 +228,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); @@ -237,6 +241,7 @@ 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 +259,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 +269,7 @@ dev->si_drv1 = NULL; cdi->rdch = NULL; cdi->wrch = NULL; + cdi->volch = NULL; cdi->simplex = 0; cdi->busy = 0; @@ -340,6 +347,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 +363,41 @@ 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, + { SND_DEV_DSP, "dsp", ".", NULL, 0, 0, 0, 0, AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_AUDIO, "audio", ".", 0, 0, 0, + { SND_DEV_AUDIO, "audio", ".", NULL, 0, 0, 0, 0, AFMT_MU_LAW, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSP16, "dspW", ".", 0, 0, 0, + { SND_DEV_DSP16, "dspW", ".", NULL, 0, 0, 0, 0, AFMT_S16_LE, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, - { SND_DEV_DSPHW_PLAY, "dsp", ".p", 1, 1, SND_MAXHWCHAN, + { SND_DEV_DSPHW_PLAY, "dsp", ".p", NULL, 1, 1, SND_MAXHWCHAN, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", 1, 1, SND_MAXVCHANS, + { SND_DEV_DSPHW_VPLAY, "dsp", ".vp", NULL, 1, 1, SND_MAXVCHANS, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_WRONLY }, - { SND_DEV_DSPHW_REC, "dsp", ".r", 1, 1, SND_MAXHWCHAN, + { SND_DEV_DSPHW_REC, "dsp", ".r", NULL, 1, 1, SND_MAXHWCHAN, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_VREC, "dsp", ".vr", 1, 1, SND_MAXVCHANS, + { SND_DEV_DSPHW_VREC, "dsp", ".vr", NULL, 1, 1, SND_MAXVCHANS, 1, AFMT_S16_LE | AFMT_STEREO, 48000, DSP_CDEV_TYPE_RDONLY }, - { SND_DEV_DSPHW_CD, "dspcd", ".", 0, 0, 0, + { SND_DEV_DSPHW_CD, "dspcd", ".", NULL, 0, 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 }, + /* Low priority, OSSv4 aliases. */ + { SND_DEV_DSP, "dsp_ac3", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_mmap", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_multich", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifout", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, + { SND_DEV_DSP, "dsp_spdifin", ".", "dsp", 0, 0, 0, 0, + AFMT_U8, DSP_DEFAULT_SPEED, DSP_CDEV_TYPE_RDWR }, }; #define DSP_FIXUP_ERROR() do { \ @@ -397,7 +421,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.. */ @@ -449,27 +473,46 @@ 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 +536,15 @@ 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))) rderror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_READ) + rderror = 0; + if (rderror != 0) { if (rdch != NULL) pcm_chnrelease(rdch); @@ -509,10 +555,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 +573,15 @@ 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))) wrerror = ENXIO; + if (volctl == DSP_CDEV_VOLCTL_WRITE) + wrerror = 0; + if (wrerror != 0) { if (wrch != NULL) pcm_chnrelease(wrch); @@ -545,27 +601,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); /* * 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. @@ -583,9 +668,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)) @@ -595,24 +680,49 @@ 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 +731,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); + rdch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | + CHN_F_DEAD); chn_reset(rdch, 0); pcm_chnrelease(rdch); - PCM_RDCH(i_dev) = NULL; } - if (wrch) { + if (wrch != NULL) { /* * Please see block above. */ @@ -639,33 +749,26 @@ 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); + wrch->flags &= ~(CHN_F_RUNNING | CHN_F_MAPPED | + CHN_F_DEAD); chn_reset(wrch, 0); pcm_chnrelease(wrch); - PCM_WRCH(i_dev) = NULL; } - 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); - (void)snd_clone_unref(i_dev); - } - PCM_RELEASE(d); } + 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); + (void)snd_clone_unref(i_dev); + + PCM_RELEASE(d); pcm_unlock(d); PCM_GIANT_LEAVE(d); @@ -760,11 +863,172 @@ } 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); + } + + mtx_assert(d->lock, MA_NOTOWNED); + + *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) || !(d->flags & SD_F_VPC)) + return (-1); + + mtx_assert(d->lock, MA_NOTOWNED); + + 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)) { + CHN_SETVOLUME(volch, SND_VOL_C_PCM, SND_CHN_T_FL, + *(int *)arg & 0x7f); + CHN_SETVOLUME(volch, SND_VOL_C_PCM, SND_CHN_T_FR, + (*(int *)arg >> 8) & 0x7f); + } 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)) @@ -775,15 +1039,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, @@ -823,23 +1087,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); } @@ -1465,20 +1718,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); @@ -1487,6 +1754,7 @@ PCM_RELEASE_QUICK(d); } else ret = ENOTSUP; + break; case SNDCTL_DSP_GET_RECSRC_NAMES: @@ -1796,8 +2064,6 @@ break; } - relchns(i_dev, rdch, wrch, 0); - PCM_GIANT_LEAVE(d); return (ret); @@ -1969,7 +2235,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!")); @@ -1988,13 +2254,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; @@ -2131,7 +2400,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)); --- sys/dev/sound/pcm/feeder.h.orig 2007-03-17 01:15:33.000000000 +0800 +++ sys/dev/sound/pcm/feeder.h 2007-10-30 01:55:19.000000000 +0800 @@ -74,17 +74,22 @@ }; \ 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 +enum { + FEEDER_ROOT, + FEEDER_FMT, + FEEDER_MIXER, + FEEDER_RATE, + FEEDER_FILTER, + FEEDER_VOLUME, + FEEDER_SWAPLR, + FEEDER_LAST +}; + +#define FEEDRATE_SRC 1 +#define FEEDRATE_DST 2 +#define FEEDRATE_QUALITY 3 -#define FEEDRATE_SRC 1 -#define FEEDRATE_DST 2 +#define FEEDVOL_CLASS 1 #define FEEDRATE_RATEMIN 1 #define FEEDRATE_RATEMAX 2016000 /* 48000 * 42 */ --- sys/dev/sound/pcm/feeder_fmt.c.orig 2007-06-02 21:07:44.000000000 +0800 +++ sys/dev/sound/pcm/feeder_fmt.c 2007-10-30 01:55:19.000000000 +0800 @@ -39,6 +39,8 @@ */ #include +#include +#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 $"); @@ -50,7 +52,7 @@ &feeder_fmt_stereodownmix, 1, "averaging stereo downmix"); #endif -#define FEEDFMT_RESERVOIR 8 /* 32bit stereo */ +#define FEEDFMT_RESERVOIR (PCM_32_BPS * SND_CHN_MAX) static uint8_t ulaw_to_u8_tbl[] = { 3, 7, 11, 15, 19, 23, 27, 31, @@ -374,11 +376,11 @@ * 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) +#define FBIT_DATA(i, o, c) ((intptr_t)((((c) & 0x3f) << 8) | \ + (((i) & 0xf) << 4) | ((o) & 0xf))) +#define FBIT_OUTBPS(m) ((m) & 0xf) +#define FBIT_INBPS(m) FBIT_OUTBPS((m) >> 4) +#define FBIT_CHANNELS(m) (((m) >> 8) & 0x3f) static int feed_updownbit_init(struct pcm_feeder *f) @@ -944,32 +946,31 @@ /* * Channel conversion (stereo -> mono) */ -#define FEEDER_FMT_STEREODOWNMIX(FMTBIT, FMT_INTCAST, SIGN, \ - SIGNS, ENDIAN, ENDIANS) \ +#define FEEDER_FMT_STEREODOWNMIX(FMTBIT, SIGN, SIGNS, ENDIAN, ENDIANS) \ static void \ -SIGNS##FMTBIT##ENDIANS##_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) \ +SIGNS##FMTBIT##ENDIANS##e_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) \ { \ - int32_t v; \ + intpcm_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); \ + v = (INTPCM##FMTBIT##_T(PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(sx)) + \ + PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(sy)) >> 1; \ + PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN##E(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); +FEEDER_FMT_STEREODOWNMIX(8, S, s, N, n) +FEEDER_FMT_STEREODOWNMIX(16, S, s, L, l) +FEEDER_FMT_STEREODOWNMIX(24, S, s, L, l) +FEEDER_FMT_STEREODOWNMIX(32, S, s, L, l) +FEEDER_FMT_STEREODOWNMIX(16, S, s, B, b) +FEEDER_FMT_STEREODOWNMIX(24, S, s, B, b) +FEEDER_FMT_STEREODOWNMIX(32, S, s, B, b) +FEEDER_FMT_STEREODOWNMIX(8, U, u, N, n) +FEEDER_FMT_STEREODOWNMIX(16, U, u, L, l) +FEEDER_FMT_STEREODOWNMIX(24, U, u, L, l) +FEEDER_FMT_STEREODOWNMIX(32, U, u, L, l) +FEEDER_FMT_STEREODOWNMIX(16, U, u, B, b) +FEEDER_FMT_STEREODOWNMIX(24, U, u, B, b) +FEEDER_FMT_STEREODOWNMIX(32, U, u, B, b) static void ulaw_stereodownmix(uint8_t *dst, uint8_t *sx, uint8_t *sy) @@ -1017,8 +1018,8 @@ { 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_DATA(i, j) ((intptr_t)((((i) & 0x3f) << 1) | ((j) & 0x1))) +#define FSM_INFOIDX(m) (((m) >> 1) & 0x3f) #define FSM_FUNCIDX(m) ((m) & 0x1) static int --- sys/dev/sound/pcm/feeder_rate.c.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/feeder_rate.c 2007-10-30 01:55:19.000000000 +0800 @@ -1,7 +1,5 @@ /*- - * Copyright (c) 1999 Cameron Grant - * Copyright (c) 2003 Orion Hodson - * Copyright (c) 2005 Ariff Abdullah + * Copyright (c) 2005-2007 Ariff Abdullah * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -27,102 +25,120 @@ */ /* - * 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. + */ + +/* + * This is an entirely new bandlimited sinc interpolator, technically + * based on excellent research paper by Julius O. Smith III + * - http://ccrma.stanford.edu/~jos/resample/ . */ +#ifdef _KERNEL #include +#include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_rate.c,v 1.23 2007/06/16 03:37:28 ariff Exp $"); +#else /* _KERNEL */ +#include "pcm.h" +#define FEEDRATE_SRC 1 +#define FEEDRATE_DST 2 +#define FEEDRATE_RATEMIN 1 +#define FEEDRATE_RATEMAX 2016000 +#define FEEDRATE_ROUNDHZ 0 +#endif + +#include "feeder_rate_gen.h" + +#ifndef AFMT_S8_NE +#define AFMT_S8_NE AFMT_S8 +#endif +#ifndef AFMT_U8_NE +#define AFMT_U8_NE AFMT_U8 +#endif -#define RATE_ASSERT(x, y) /* KASSERT(x,y) */ -#define RATE_TEST(x, y) /* if (!(x)) printf y */ -#define RATE_TRACE(x...) /* printf(x) */ +#ifndef FEEDRATE_QUALITY +#define FEEDRATE_QUALITY (FEEDRATE_DST + 1) +#endif + +#ifndef Z_QUALITY_DEFAULT +#define Z_QUALITY_DEFAULT Z_QUALITY_LINEAR +#endif + +#define Z_RESERVOIR 8192 +#define Z_RESERVOIR_MAX 131072 + +#define Z_SINC_MAX 0x3fffff + +#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 -MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder"); +#define Z_RATE_SRC FEEDRATE_SRC +#define Z_RATE_DST FEEDRATE_DST +#define Z_RATE_QUALITY FEEDRATE_QUALITY + +#define Z_PARANOID 1 /* * 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; /* input / output sample time 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_scale; /* output scaling */ + int32_t z_dx; /* input sample drift increment */ + int32_t z_dy; /* output sample drift increment */ + 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 */ +#ifndef _KERNEL + uint32_t z_cycle; /* output cycle, purely for statistical */ #endif - uint8_t *buffer; - feed_rate_converter convert; + + 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; +static int feeder_rate_quality = Z_QUALITY_DEFAULT; +#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); static int sysctl_hw_snd_feeder_rate_min(SYSCTL_HANDLER_ARGS) @@ -131,17 +147,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 +169,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,456 +191,849 @@ 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) \ +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; + + 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; + } + 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 */ + + +/* + * Macroses for accurate sample time drift calculations. + */ +#define _Z_GY2GX(i, a, v) \ + ((((i)->z_gx * (v)) + (i)->z_gy - (a) - 1) / (i)->z_gy) + +#define _Z_GX2GY(i, a, v) \ + (((a) + ((v) * (i)->z_gy)) / (i)->z_gx) + +#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) (((x) * (i)->z_gy) - ((y) * (i)->z_gx)) + +/* + * 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)) + +#define Z_IS_SINC(i) (Z_SINC_COEFF_IDX(i) >= 0) + +/* + * 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) memcpy(dst, src, sz) +#define z_feed FEEDER_FEED + + +static int32_t +z_gcd(int32_t x, int32_t y) +{ + while (x != y) { + if (x > y) + x -= y; + else + y -= x; + } + + return (x); +} + +static int32_t +z_roundpow2(int32_t v) +{ + int32_t i; + + i = 1; + + 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 +z_feed_zoh(struct z_info *info, uint8_t *dst) +{ + z_copy(info->z_delay + + (info->z_start * info->channels * info->bps), dst, + info->channels * info->bps); +} + +/* + * (Classic) Linear Interpolation. This at least sounds better (perceptually) + * and fast, but no further advance filtering which means aliasing still exist + * and could become worst with a right sample. Interpolation centered around + * 8bit distance between the present and previous sample, so there are chances + * to shift 8 and 16 bit samples to 24 bit to gain extra dynamic range during + * interpolation while retaining 32bit arithmetic. + */ +#define Z_DECLARE_LINEAR(SIGN, BIT, ENDIAN, SHIFT) \ +static void \ +z_feed_linear_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \ { \ - uint32_t ret, smpsz, ch, pos, bpos, gx, gy, alpha, d1, d2; \ - int32_t x, y; \ - int i; \ - uint8_t *src, *sx, *sy; \ + uint32_t dx, dy; \ + intpcm_t x, y; \ + int32_t ch; \ + uint8_t *sx, *sy; \ + \ + dy = ((uint32_t)info->z_alpha << Z_LINEAR_SHIFT) / info->z_gy; \ + dx = Z_LINEAR_ONE - dy; \ + \ + sx = info->z_delay + (info->z_start * info->channels * \ + PCM_##BIT##_BPS); \ + sy = info->z_delay + \ + (z_prev(info, info->z_start, 1) * info->channels * \ + PCM_##BIT##_BPS); \ \ - 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) + \ + do { \ + x = PCM_READ_##SIGN##BIT##_##ENDIAN(sx) << SHIFT; \ + y = PCM_READ_##SIGN##BIT##_##ENDIAN(sy) << SHIFT; \ + x = ((INTPCM##BIT##_T(x) * dx) + \ + (INTPCM##BIT##_T(y) * dy)) >> Z_LINEAR_SHIFT; \ + PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, x >> SHIFT); \ + sx += PCM_##BIT##_BPS; \ + sy += PCM_##BIT##_BPS; \ + dst += PCM_##BIT##_BPS; \ + } while (--ch != 0); \ +} -static void -feed_speed_ratio(uint32_t src, uint32_t dst, uint32_t *gx, uint32_t *gy) -{ - uint32_t w, x = src, y = dst; +/* + * 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 and restore during write for maximum dynamic + * range during accumulation. + */ +#define Z_DECLARE_SINC(SIGN, BIT, ENDIAN, SHIFT) \ +static void \ +z_feed_sinc_##SIGN##BIT##ENDIAN(struct z_info *info, uint8_t *dst) \ +{ \ + int64_t v; \ + intpcm_t x; \ + int32_t c, z, z1, z_dy, coeff, *z_coeff, *z_dcoeff; \ + int32_t i, center, ch; \ + uint8_t *p; \ + \ + center = z_prev(info, info->z_start, info->z_size); \ + z_coeff = info->z_coeff; \ + z_dcoeff = info->z_dcoeff; \ + z_dy = info->z_dy; \ + z1 = info->z_alpha * info->z_dx; \ + ch = info->channels - 1; \ + dst += ch * PCM_##BIT##_BPS; \ + \ + do { \ + v = 0; \ + z = z1; \ + c = 0; \ + p = info->z_delay + (z_next(info, center, 1) * \ + info->channels * PCM_##BIT##_BPS) + (ch * PCM_##BIT##_BPS); \ + i = info->z_size; \ + do { \ + c += z >> Z_SHIFT; \ + z &= Z_MASK; \ + coeff = z_coeff[c] + (((z >> Z_COEFF_UNSHIFT) * \ + z_dcoeff[c]) >> Z_COEFF_SHIFT); \ + x = _PCM_READ_##SIGN##BIT##_##ENDIAN(p); \ + v += (int64_t)x * coeff; \ + z += z_dy; \ + p += info->channels * PCM_##BIT##_BPS; \ + } while (--i != 0); \ + z = z_dy - z1; \ + c = 0; \ + p = info->z_delay + (center * info->channels * \ + PCM_##BIT##_BPS) + (ch * PCM_##BIT##_BPS); \ + i = info->z_size; \ + do { \ + c += z >> Z_SHIFT; \ + z &= Z_MASK; \ + coeff = z_coeff[c] + (((z >> Z_COEFF_UNSHIFT) * \ + z_dcoeff[c]) >> Z_COEFF_SHIFT); \ + x = _PCM_READ_##SIGN##BIT##_##ENDIAN(p); \ + v += (int64_t)x * coeff; \ + z += z_dy; \ + p -= info->channels * PCM_##BIT##_BPS; \ + } while (--i != 0); \ + v <<= SHIFT; \ + v >>= Z_COEFF_SHIFT; \ + if (info->z_scale != Z_ONE) \ + v = (v * info->z_scale) >> Z_SHIFT; \ + v >>= SHIFT; \ + if (v > PCM_S##BIT##_MAX) \ + v = PCM_S##BIT##_MAX; \ + else if (v < PCM_S##BIT##_MIN) \ + v = PCM_S##BIT##_MIN; \ + _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \ + dst -= PCM_##BIT##_BPS; \ + } while (ch-- != 0); \ +} + +#define Z_DECLARE(SIGN, BIT, ENDIAN, L_SHIFT, S_SHIFT) \ + Z_DECLARE_LINEAR(SIGN, BIT, ENDIAN, L_SHIFT) \ + Z_DECLARE_SINC(SIGN, BIT, ENDIAN, S_SHIFT) + +Z_DECLARE(S, 8, NE, 16, 24) +Z_DECLARE(S, 16, LE, 8, 16) +Z_DECLARE(S, 24, LE, 0, 8) +Z_DECLARE(S, 32, LE, 0, 0) +Z_DECLARE(S, 16, BE, 8, 16) +Z_DECLARE(S, 24, BE, 0, 8) +Z_DECLARE(S, 32, BE, 0, 0) +Z_DECLARE(U, 8, NE, 16, 24) +Z_DECLARE(U, 16, LE, 8, 16) +Z_DECLARE(U, 24, LE, 0, 8) +Z_DECLARE(U, 32, LE, 0, 0) +Z_DECLARE(U, 16, BE, 8, 16) +Z_DECLARE(U, 24, BE, 0, 8) +Z_DECLARE(U, 32, BE, 0, 0) + +enum { + Z_RESAMPLER_ZOH, + Z_RESAMPLER_LINEAR, + Z_RESAMPLER_SINC, + Z_RESAMPLER_LAST +}; + +#define Z_RESAMPLER_IDX(i) \ + (Z_IS_SINC(i) ? Z_RESAMPLER_SINC : (i)->quality) - while (y != 0) { - w = x % y; - x = y; - y = w; +#define Z_RESAMPLER_ENTRY(SIGN, BIT, ENDIAN) \ + { \ + AFMT_##SIGN##BIT##_##ENDIAN, PCM_##BIT##_BPS, \ + { \ + [Z_RESAMPLER_ZOH] = z_feed_zoh, \ + [Z_RESAMPLER_LINEAR] = z_feed_linear_##SIGN##BIT##ENDIAN, \ + [Z_RESAMPLER_SINC] = z_feed_sinc_##SIGN##BIT##ENDIAN \ + } \ } - *gx = src / x; - *gy = dst / x; -} + +static const struct { + uint32_t format; + int32_t bps; + z_resampler_t resampler[Z_RESAMPLER_LAST]; +} z_resampler_tab[] = { + Z_RESAMPLER_ENTRY(S, 8, NE), + Z_RESAMPLER_ENTRY(S, 16, LE), + Z_RESAMPLER_ENTRY(S, 24, LE), + Z_RESAMPLER_ENTRY(S, 32, LE), + Z_RESAMPLER_ENTRY(S, 16, BE), + Z_RESAMPLER_ENTRY(S, 24, BE), + Z_RESAMPLER_ENTRY(S, 32, 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), +}; + +#define Z_RESAMPLER_TAB_SIZE \ + ((int32_t)(sizeof(z_resampler_tab) / sizeof(z_resampler_tab[0]))) static void -feed_rate_reset(struct feed_rate_info *info) +z_resampler_reset(struct z_info *info) { - 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; + 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; + info->z_scale = -1; + info->z_dx = -1; + info->z_dy = -1; +#ifndef _KERNEL + 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); + + 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, "sinc l=%d != Z_SINC_LEN=%d\n", + 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 + static int -feed_rate_setup(struct pcm_feeder *f) +z_resampler_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; + struct z_info *info; + uint64_t z_fact; + uint32_t format; + int32_t i; + int adaptive; - feed_rate_reset(info); + info = f->data; + z_resampler_reset(info); - if (info->src != info->dst) - feed_speed_ratio(info->src, info->dst, &info->gx, &info->gy); + if (info->src == info->dst) + return (0); - if (!(RATE_FACTOR_SAFE(info->gx) && RATE_FACTOR_SAFE(info->gy))) - return (-1); + /* 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->out; + + z_fact = 0; + adaptive = 0; + + if (Z_IS_SINC(info)) { + z_fact = ((uint64_t)info->z_gy << Z_SHIFT) / info->z_gx; + /* + * This is actually impossible, unless Z_SHIFT is too + * big. + */ + if (z_fact == 0) + 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 (adaptive == 0 && z_fact < Z_ONE) { + info->z_dy = ((z_fact << Z_FULL_SHIFT) + + (Z_ONE >> 1)) >> Z_SHIFT; + if (info->z_dy < 1) + return (E2BIG); + info->z_scale = z_fact; + } else { + info->z_dy = Z_FULL_ONE; + info->z_scale = Z_ONE; + } + + /* Smallest drift increment */ + info->z_dx = info->z_dy / info->z_gy; - 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; + /* + * Another ridiculous attempt that bork on us, but since + * we're being made adaptive, let it continue and retry. + */ + if (info->z_dx < 1) { + if (adaptive == 0) { + adaptive = 1; + goto z_setup_adaptive_sinc; + } + return (E2BIG); + } + + 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); + if (info->z_size < 1 || 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; + info->z_dcoeff = z_coeff_tab[i].dcoeff; break; } } /* - * 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++) { + if ((format & ~AFMT_STEREO) != z_resampler_tab[i].format) + continue; + info->bps = z_resampler_tab[i].bps; + info->z_resample = + z_resampler_tab[i].resampler[Z_RESAMPLER_IDX(info)]; + break; + } + + if (info->z_resample == NULL) + return (EINVAL); + + info->channels = (format & AFMT_STEREO) ? 2 : 1; + + 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 * info->channels * info->bps) < 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 + * shifting in main conversion/feeder loop. */ - if (info->gx == info->gy) - info->convert = NULL; + 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); + +#ifndef _KERNEL +#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)); + fprintf(stderr, "factor=%llu, ", z_fact); + fprintf(stderr, "base_length=%d, ", Z_SINC_BASE_LEN(info)); + fprintf(stderr, "adaptive=%s\n", (adaptive != 0) ? "YES" : "NO"); + dumpz(size); + dumpz(alloc); + 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 / Z_FULL_ONE); + dumpz(dy); + fprintf(stderr, "\t%12s %10d\n", "", info->z_dy >> Z_SHIFT); + 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 - 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); + /* + * Allocate or reuse delay line buffer, whichever makes sense. + */ + i = info->z_full * info->channels * info->bps; + 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 * info->channels * info->bps); 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); info->rsrc = value; break; - case FEEDRATE_DST: + case Z_RATE_DST: + if (value < feeder_rate_min || value > feeder_rate_max) + return (E2BIG); info->rdst = value; break; + case Z_RATE_QUALITY: + if (value < Z_QUALITY_MIN || value > Z_QUALITY_MAX) + return (EINVAL); + /* + * 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; 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; 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; if (f->desc->out != f->desc->in) 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; f->data = info; - return (feed_rate_setup(f)); + + return (z_resampler_setup(f)); } 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_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) +z_resampler_feed(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)); + struct z_info *info; + int32_t alphadrift, startdrift, reqout, ocount, reqin, smpsz; + 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)); /* - * 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. + * Calculate sample size 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) + smpsz = info->channels * info->bps; + ocount = count / smpsz; + 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)); -#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) + reqin = z_gy2gx(info, ocount) - z_fetched(info); + + startdrift = _Z_GY2GX(info, 0, 1); + alphadrift = z_drift(info, startdrift, 1); + + dst = b; + + do { + if (reqin != 0) { + fetch = 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 * smpsz), + info->z_delay, cp * smpsz); + info->z_start = + z_prev(info, info->z_size << 1, 1); + info->z_pos = + z_next(info, info->z_start, fetched + 1); + fetch = min(z_free(info), reqin); +#ifndef _KERNEL + 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 - 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 (fetch != 0) { + /* + * Fetch in byte domain and jump back + * to sample domain. + */ + fetched = z_feed(f->source, c, + info->z_delay + (info->z_pos * smpsz), + fetch * smpsz, source) / smpsz; + /* + * 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; + } } - if (info->pos == info->bpos) { - RATE_TEST(info->pos == smpsz, - ("%s: EOF while in progress\n", __func__)); - break; - } - 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 = 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; - } - if (i == count) - break; - } - - 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)); + 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 += smpsz; +#ifndef _KERNEL + info->z_cycle++; #endif + } while (--reqout != 0); + } + } while (reqin != 0 && ocount != 0); - return (i); + /* + * Back to byte domain.. + */ + return (dst - b); } static struct pcm_feederdesc feeder_rate_desc[] = { - {FEEDER_RATE, AFMT_S8, AFMT_S8, 0}, + {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_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_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_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}, @@ -629,11 +1044,11 @@ }; 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), + 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} }; --- sys/dev/sound/pcm/feeder_volume.c.orig 2007-06-17 04:36:39.000000000 +0800 +++ sys/dev/sound/pcm/feeder_volume.c 2007-10-30 01:55:19.000000000 +0800 @@ -27,26 +27,25 @@ /* feeder_volume, a long 'Lost Technology' rather than a new feature. */ #include +#include #include "feeder_if.h" SND_DECLARE_FILE("$FreeBSD: src/sys/dev/sound/pcm/feeder_volume.c,v 1.6 2007/06/16 20:36:39 ariff Exp $"); -#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) \ +typedef int (*feed_volume_filter)(uint8_t *, int *, int, int); + +#define FVOL_CALC_8(s, v) SND_VOL_CALC_SAMPLE((int32_t)(s), v) +#define FVOL_CALC_16(s, v) SND_VOL_CALC_SAMPLE((int32_t)(s), v) +#define FVOL_CALC_24(s, v) SND_VOL_CALC_SAMPLE((int64_t)(s), v) +#define FVOL_CALC_32(s, v) SND_VOL_CALC_SAMPLE((int64_t)(s), v) + +#define FEEDER_VOLUME_FILTER(FMTBIT, SIGN, SIGNS, ENDIAN, ENDIANS) \ +static int \ +feed_volume_filter_##SIGNS##FMTBIT##ENDIANS##e(uint8_t *b, int *vol, \ + int channels, int count) \ { \ - int32_t j; \ + intpcm##FMTBIT##_t k; \ + intpcm_t j; \ int i; \ \ i = count; \ @@ -55,29 +54,30 @@ 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); \ + j = PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(b); \ + k = FVOL_CALC_##FMTBIT(j, \ + vol[(i / PCM_##FMTBIT##_BPS) % channels]); \ + j = PCM_CLAMP_##SIGN##FMTBIT(k); \ + _PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN##E(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) +FEEDER_VOLUME_FILTER(8, S, s, N, n) +FEEDER_VOLUME_FILTER(16, S, s, L, l) +FEEDER_VOLUME_FILTER(24, S, s, L, l) +FEEDER_VOLUME_FILTER(32, S, s, L, l) +FEEDER_VOLUME_FILTER(16, S, s, B, b) +FEEDER_VOLUME_FILTER(24, S, s, B, b) +FEEDER_VOLUME_FILTER(32, S, s, B, b) +FEEDER_VOLUME_FILTER(8, U, u, N, n) +FEEDER_VOLUME_FILTER(16, U, u, L, l) +FEEDER_VOLUME_FILTER(24, U, u, L, l) +FEEDER_VOLUME_FILTER(32, U, u, L, l) +FEEDER_VOLUME_FILTER(16, U, u, B, b) +FEEDER_VOLUME_FILTER(24, U, u, B, b) +FEEDER_VOLUME_FILTER(32, U, u, B, b) struct feed_volume_info { uint32_t format; @@ -102,16 +102,19 @@ { AFMT_U32_BE, PCM_32_BPS, feed_volume_filter_u32be }, }; -#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 FVOL_DATA(vc, i, c) ((intptr_t)((((vc) & 0x3f) << 12) | \ + (((i) & 0x3f) << 6) | ((c) & 0x3f))) +#define FVOL_CLASS(m) (((m) >> 12) & 0x3f) +#define FVOL_INFOIDX(m) (((m) >> 6) & 0x3f) +#define FVOL_CHANNELS(m) ((m) & 0x3f) static int -feed_volume_init(struct pcm_feeder *f) +feed_volume_setup(struct pcm_feeder *f, int vc) { int i, channels; - if (f->desc->in != f->desc->out) + if (f->desc->in != f->desc->out || vc < SND_VOL_C_BEGIN || + vc > SND_VOL_C_END) return (EINVAL); /* For now, this is mandatory! */ @@ -120,34 +123,84 @@ channels = 2; - for (i = 0; i < sizeof(feed_volume_tbl) / sizeof(feed_volume_tbl[0]); + /* XXX Future interleave multichannel check yada yada.. */ + if (channels < SND_CHN_MIN || channels > SND_CHN_MAX) + return (EINVAL); + + 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); + f->data = (void *)FVOL_DATA(vc, i, channels); return (0); } } + return (EINVAL); +} + +static int +feed_volume_init(struct pcm_feeder *f) +{ + return (feed_volume_setup(f, SND_VOL_C_PCM)); +} + +static int +feed_volume_set(struct pcm_feeder *f, int what, int value) +{ + switch (what) { + case FEEDVOL_CLASS: + return (feed_volume_setup(f, value)); + break; + default: + break; + } + + return (EINVAL); +} + +static int +feed_volume_get(struct pcm_feeder *f, int what) +{ + switch (what) { + case FEEDVOL_CLASS: + return (FVOL_CLASS((intptr_t)f->data)); + break; + default: + break; + } + return (-1); } static int feed_volume(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, - uint32_t count, void *source) + uint32_t count, void *source) { struct feed_volume_info *info; - int vol[2]; - int k, smpsz; + int vol[SND_CHN_MAX]; + int j, k, v, smpsz, channels; + snd_volume_class_t vcv; + + channels = FVOL_CHANNELS((intptr_t)f->data); + vcv = SND_VOL_C_VAL(FVOL_CLASS((intptr_t)f->data)); + j = channels - 1; + k = 0; + + do { + KASSERT(c->matrix[j] != SND_CHN_T_MAX, + ("%s(): invalid matrix j=%d", __func__, j)); + v = c->volume[vcv][c->matrix[j]]; + if (v != SND_VOL_FLAT) + k = 1; + vol[j] = v; + } while (j-- != 0); - vol[0] = FVOL_LEFT(c->volume); - vol[1] = FVOL_RIGHT(c->volume); - - if (vol[0] == FVOL_MAX && vol[1] == FVOL_MAX) + if (k == 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); + smpsz = info->bps * channels; if (count < smpsz) return (0); @@ -156,7 +209,8 @@ return (0); k -= k % smpsz; - return (info->filter(b, vol, k)); + + return (info->filter(b, vol, channels, k)); } static struct pcm_feederdesc feeder_volume_desc[] = { @@ -178,6 +232,8 @@ }; static kobj_method_t feeder_volume_methods[] = { KOBJMETHOD(feeder_init, feed_volume_init), + KOBJMETHOD(feeder_set, feed_volume_set), + KOBJMETHOD(feeder_get, feed_volume_get), KOBJMETHOD(feeder_feed, feed_volume), {0, 0} }; --- sys/dev/sound/pcm/matrix.h.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/matrix.h 2007-10-30 01:55:19.000000000 +0800 @@ -0,0 +1,99 @@ +/*- + * 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$ + */ + +#ifndef _SND_MATRIX_H_ +#define _SND_MATRIX_H_ + +typedef enum { + SND_CHN_T_FL, /* Front Left */ + SND_CHN_T_FR, /* Front Right */ + SND_CHN_T_FC, /* Front Center */ + SND_CHN_T_LF, /* Low Frequency */ + SND_CHN_T_BL, /* Back Left */ + SND_CHN_T_BR, /* Back Right */ + SND_CHN_T_FLC, /* Front Left Center */ + SND_CHN_T_FRC, /* Front Right Center */ + SND_CHN_T_BC, /* Back Center */ + SND_CHN_T_SL, /* Side Left */ + SND_CHN_T_SR, /* Side Right */ + SND_CHN_T_TC, /* Top Center */ + SND_CHN_T_TFL, /* Top Front Left */ + SND_CHN_T_TFC, /* Top Front Center */ + SND_CHN_T_TFR, /* Top Front Right */ + SND_CHN_T_TBL, /* Top Back Left */ + SND_CHN_T_TBC, /* Top Back Center */ + SND_CHN_T_TBR, /* Top Back Right */ + SND_CHN_T_MAX, + SND_CHN_T_VOL_0DB = SND_CHN_T_MAX, + SND_CHN_T_VOL_MAX +} snd_channel_t; + +#define SND_CHN_T_BEGIN SND_CHN_T_FL +#define SND_CHN_T_END SND_CHN_T_FR +#define SND_CHN_T_STEP 1 + +#define SND_CHN_MIN 1 +#define SND_CHN_MAX 2 + +/* + * 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). + */ +typedef enum { + SND_VOL_C_MASTER, + SND_VOL_C_PCM, + SND_VOL_C_PCM_VAL, + SND_VOL_C_MAX +} snd_volume_class_t; + +#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/mixer.c.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/mixer.c 2007-10-30 01:55:19.000000000 +0800 @@ -32,6 +32,12 @@ MALLOC_DEFINE(M_MIXER, "mixer", "mixer"); +static int mixer_bypass = 0; +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; @@ -170,16 +176,24 @@ CHN_FOREACH(c, d, channels.pcm) { CHN_LOCK(c); if (c->direction == PCMDIR_PLAY && - (c->feederflags & (1 << FEEDER_VOLUME))) - chn_setvolume(c, left, right); + (c->feederflags & (1 << FEEDER_VOLUME))) { + CHN_SETVOLUME(c, + SND_VOL_C_MASTER, SND_CHN_T_FL, left); + CHN_SETVOLUME(c, + SND_VOL_C_MASTER, SND_CHN_T_FR, 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); + (c->feederflags & (1 << FEEDER_VOLUME))) { + CHN_SETVOLUME(c, + SND_VOL_C_MASTER, SND_CHN_T_FL, left); + CHN_SETVOLUME(c, + SND_VOL_C_MASTER, SND_CHN_T_FR, right); + } CHN_UNLOCK(c); } } @@ -243,7 +257,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); @@ -976,6 +991,112 @@ } 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)) { + CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FL, + *(int *)arg & 0x7f); + CHN_SETVOLUME(c, SND_VOL_C_PCM, SND_CHN_T_FR, + (*(int *)arg >> 8) & 0x7f); + } 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) { @@ -992,7 +1113,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); @@ -1002,7 +1131,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, --- sys/dev/sound/pcm/pcm.h.orig 1970-01-01 07:30:00.000000000 +0730 +++ sys/dev/sound/pcm/pcm.h 2007-10-30 01:55:19.000000000 +0800 @@ -0,0 +1,374 @@ +/*- + * 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$ + */ + +#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; +#ifdef PCM_USE_64BIT_ARITH +typedef int64_t intpcm32_t; +#else +typedef int32_t intpcm32_t; +#endif + +/* 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 2007-10-30 01:55:19.000000000 +0800 @@ -25,7 +25,7 @@ */ #include -#include +#include #include #ifdef USING_MUTEX #include @@ -354,7 +354,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 2007-10-30 01:55:19.000000000 +0800 @@ -0,0 +1,148 @@ +/*- + * 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$ + */ + +#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_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 q:%d)", \ + FEEDER_GET(f, FEEDRATE_SRC), \ + FEEDER_GET(f, FEEDRATE_DST), \ + FEEDER_GET(f, FEEDRATE_QUALITY)); \ + 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); \ +} while(0) + +#endif /* !_SND_SNDSTAT_H_ */ --- sys/dev/sound/pcm/sound.c.orig 2007-06-18 03:02:05.000000000 +0800 +++ sys/dev/sound/pcm/sound.c 2007-10-30 01:55:19.000000000 +0800 @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -66,14 +67,19 @@ 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) @@ -228,6 +234,7 @@ 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; @@ -267,11 +274,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 +299,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 +332,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 +377,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 +405,7 @@ c->flags &= ~CHN_F_BUSY; c->pid = -1; + strlcpy(c->comm, CHN_COMM_UNUSED, sizeof(c->comm)); CHN_UNLOCK(c); return (0); @@ -575,6 +597,7 @@ 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; @@ -620,46 +643,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: @@ -981,6 +970,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 +1000,10 @@ */ d->flags = 0; #endif + i = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "vpc_disabled", &i) != 0 || i == 0) + d->flags |= SD_F_VPC; d->devinfo = devinfo; d->devcount = 0; d->reccount = 0; @@ -1041,6 +1035,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) @@ -1228,103 +1223,6 @@ /************************************************************************/ -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) --- sys/dev/sound/pcm/sound.h.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/sound.h 2007-10-30 01:55:19.000000000 +0800 @@ -141,6 +141,7 @@ #define SD_F_BUSY 0x00000080 #define SD_F_MPSAFE 0x00000100 #define SD_F_REGISTERED 0x00000200 +#define SD_F_VPC 0x00000400 /* volume-per-channel */ #define SD_F_PRIO_RD 0x10000000 #define SD_F_PRIO_WR 0x20000000 @@ -158,281 +159,6 @@ (((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. - */ - -/* - * 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 - -#ifdef PCM_USE_64BIT_ARITH -typedef int64_t intpcm_t; -#else -typedef int32_t intpcm_t; -#endif - -/* 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 - -#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 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 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 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) - -#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 - -/* - * 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))) -#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) - /* 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) @@ -475,9 +201,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 +241,8 @@ 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_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); @@ -579,6 +315,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; @@ -611,7 +350,7 @@ #endif /* - * For PCM_CV_[WAIT | ACQUIRE | RELEASE], be sure to surround these + * 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 --- sys/dev/sound/pcm/vchan.c.orig 2007-06-16 11:37:28.000000000 +0800 +++ sys/dev/sound/pcm/vchan.c 2007-10-30 01:55:19.000000000 +0800 @@ -28,6 +28,7 @@ /* Almost entirely rewritten to add multi-format/channels mixing support. */ #include +#include #include #include "feeder_if.h" @@ -71,32 +72,13 @@ #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) \ +#define FEEDER_VCHAN_MIX(FMTBIT, SIGN, SIGNS, ENDIAN, ENDIANS) \ static uint32_t \ -feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS(uint8_t *to, uint8_t *tmp, \ - uint32_t count) \ +feed_vchan_mix_##SIGNS##FMTBIT##ENDIANS##e(uint8_t *to, uint8_t *tmp, \ + uint32_t count) \ { \ - int32_t x, y; \ - VCHAN_INTCAST z; \ + intpcm##FMTBIT##_t z; \ + intpcm_t x, y; \ int i; \ \ i = count; \ @@ -107,30 +89,30 @@ 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_READ_##SIGN##FMTBIT##_##ENDIAN##E(tmp); \ + y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN##E(to); \ + z = INTPCM##FMTBIT##_T(x) + y; \ x = PCM_CLAMP_##SIGN##FMTBIT(z); \ - VCHAN_PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(to, x); \ + _PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN##E(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) +FEEDER_VCHAN_MIX(8, S, s, N, n) +FEEDER_VCHAN_MIX(16, S, s, L, l) +FEEDER_VCHAN_MIX(24, S, s, L, l) +FEEDER_VCHAN_MIX(32, S, s, L, l) +FEEDER_VCHAN_MIX(16, S, s, B, b) +FEEDER_VCHAN_MIX(24, S, s, B, b) +FEEDER_VCHAN_MIX(32, S, s, B, b) +FEEDER_VCHAN_MIX(8, U, u, N, n) +FEEDER_VCHAN_MIX(16, U, u, L, l) +FEEDER_VCHAN_MIX(24, U, u, L, l) +FEEDER_VCHAN_MIX(32, U, u, L, l) +FEEDER_VCHAN_MIX(16, U, u, B, b) +FEEDER_VCHAN_MIX(24, U, u, B, b) +FEEDER_VCHAN_MIX(32, U, u, B, b) struct feed_vchan_info { uint32_t format; @@ -222,7 +204,7 @@ CHN_FOREACH(ch, c, children.busy) { CHN_LOCK(ch); - if (!(ch->flags & CHN_F_TRIGGERED)) { + if (CHN_STOPPED(ch)) { CHN_UNLOCK(ch); continue; } @@ -304,7 +286,7 @@ CHN_FOREACH(ch, c, children.busy) { CHN_LOCK(ch); - if (!(ch->flags & CHN_F_TRIGGERED)) { + if (CHN_STOPPED(ch)) { CHN_UNLOCK(ch); continue; } @@ -449,15 +431,13 @@ 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; @@ -512,7 +492,7 @@ */ #ifdef SND_DYNSYSCTL 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; @@ -621,7 +601,7 @@ } 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; @@ -739,7 +719,7 @@ vchan_create(struct pcm_channel *parent, int num) { struct snddev_info *d = parent->parentsnddev; - struct pcm_channel *ch, *tmp, *after; + struct pcm_channel *ch; struct pcmchan_caps *parent_caps; uint32_t vchanfmt; int err, first, speed, r; @@ -781,20 +761,13 @@ } CHN_LOCK(parent); - /* add us to our parent channel's children */ + /* + * Add us to our parent channel's children in reverse order + * so future destruction will pick the last (biggest number) + * channel. + */ 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); - } + CHN_INSERT_SORT_DESCEND(parent, ch, children); parent->flags |= CHN_F_HAS_VCHAN; if (first) { @@ -941,22 +914,30 @@ int vchan_destroy(struct pcm_channel *c) { - struct pcm_channel *parent = c->parentchannel; - struct snddev_info *d = parent->parentsnddev; + struct pcm_channel *parent; + struct snddev_info *d; uint32_t spd; 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); @@ -979,6 +960,8 @@ if (!err) err = pcm_chn_destroy(c); + CHN_LOCK(parent); + return (err); } @@ -1002,12 +985,12 @@ 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), @@ -1018,12 +1001,12 @@ 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"); + sysctl_dev_pcm_vchanformat, "A", "virtual channel format"); #endif return (0); --- sys/dev/sound/usb/uaudio.c.orig 2007-06-20 13:11:37.000000000 +0800 +++ sys/dev/sound/usb/uaudio.c 2007-10-30 01:55:19.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 @@ -4497,104 +4497,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; + SNDSTAT_PREPARE_PCM_BEGIN(); - 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 (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/version.h.orig 2007-06-16 11:37:27.000000000 +0800 +++ sys/dev/sound/version.h 2007-11-19 02:13:57.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 2007121300 #endif /* !_SND_VERSION_H_ */ --- sys/modules/sound/sound/Makefile.orig 2007-06-01 02:43:33.000000000 +0800 +++ sys/modules/sound/sound/Makefile 2007-10-28 23:54:29.000000000 +0800 @@ -16,6 +16,13 @@ SRCS+= mixer.c sndstat.c sound.c unit.c vchan.c SRCS+= midi.c mpu401.c sequencer.c +SRCS+= feeder_rate_gen.h + +feeder_rate_gen.h: + ${AWK} -f @/tools/feeder_rate_mkfilter.awk -- ${FEEDER_RATE_PRESETS} > ${.TARGET} + +CLEANFILES+= feeder_rate_gen.h + EXPORT_SYMS= YES # XXX evaluate .if ${MACHINE_ARCH} == "sparc64"