Sound Mega-commit. Expect further cleanup until code freeze.
For a slightly thorough explaination, please refer to
[1] http://people.freebsd.org/~ariff/SOUND_4.TXT.html .
Summary of changes includes:
1 Volume Per-Channel (vpc). Provides private / standalone volume control
unique per-stream pcm channel without touching master volume / pcm.
Applications can directly use SNDCTL_DSP_[GET|SET][PLAY|REC]VOL, or for
backwards compatibility, SOUND_MIXER_PCM through the opened dsp device
instead of /dev/mixer. Special "bypass" mode is enabled through
/dev/mixer which will automatically detect if the adjustment is made
through /dev/mixer and forward its request to this private volume
controller. Changes to this volume object will not interfere with
other channels.
Requirements:
- SNDCTL_DSP_[GET|SET][PLAY|REC]_VOL are newer ioctls (OSSv4) which
require specific application modifications (preferred).
- No modifications required for using bypass mode, so applications
like mplayer or xmms should work out of the box.
Kernel hints:
- hint.pcm.%d.vpc (0 = disable vpc).
Kernel sysctls:
- hw.snd.vpc_mixer_bypass (default: 1). Enable or disable /dev/mixer
bypass mode.
- hw.snd.vpc_autoreset (default: 1). By default, closing/opening
/dev/dsp will reset the volume back to 0 db gain/attenuation.
Setting this to 0 will preserve its settings across device
closing/opening.
- hw.snd.vpc_reset (default: 0). Panic/reset button to reset all
volume settings back to 0 db.
- hw.snd.vpc_0db (default: 45). 0 db relative to linear mixer value.
2 High quality fixed-point Bandlimited SINC sampling rate converter,
based on Julius O'Smith's Digital Audio Resampling -
http://ccrma.stanford.edu/~jos/resample/. It includes a filter design
script written in awk (the clumsiest joke I've ever written)
- 100% 32bit fixed-point, 64bit accumulator.
- Possibly among the fastest (if not fastest) of its kind.
- Resampling quality is tunable, either runtime or during kernel
compilation (FEEDER_RATE_PRESETS).
- Quality can be further customized during kernel compilation by
defining FEEDER_RATE_PRESETS in /etc/make.conf.
Kernel sysctls:
- hw.snd.feeder_rate_quality.
0 - Zero-order Hold (ZOH). Fastest, bad quality.
1 - Linear Interpolation (LINEAR). Slightly slower than ZOH,
better quality but still does not eliminate aliasing.
2 - (and above) - Sinc Interpolation(SINC). Best quality. SINC
quality always start from 2 and above.
Rough quality comparisons:
- http://people.freebsd.org/~ariff/z_comparison/
3 Bit-perfect mode. Bypasses all feeder/dsp effects. Pure sound will be
directly fed into the hardware.
4 Parametric (compile time) Software Equalizer (Bass/Treble mixer). Can
be customized by defining FEEDER_EQ_PRESETS in /etc/make.conf.
5 Transparent/Adaptive Virtual Channel. Now you don't have to disable
vchans in order to make digital format pass through. It also makes
vchans more dynamic by choosing a better format/rate among all the
concurrent streams, which means that dev.pcm.X.play.vchanformat/rate
becomes sort of optional.
6 Exclusive Stream, with special open() mode O_EXCL. This will "mute"
other concurrent vchan streams and only allow a single channel with
O_EXCL set to keep producing sound.
Other Changes:
* most feeder_* stuffs are compilable in userland. Let's not
speculate whether we should go all out for it (save that for
FreeBSD 16.0-RELEASE).
* kobj signature fixups, thanks to Andriy Gapon <avg@freebsd.org>
* pull out channel mixing logic out of vchan.c and create its own
feeder_mixer for world justice.
* various refactoring here and there, for good or bad.
* activation of few more OSSv4 ioctls() (see [1] above).
* opt_snd.h for possible compile time configuration:
(mostly for debugging purposes, don't try these at home)
SND_DEBUG
SND_DIAGNOSTIC
SND_FEEDER_MULTIFORMAT
SND_FEEDER_FULL_MULTIFORMAT
SND_FEEDER_RATE_HP
SND_PCM_64
SND_OLDSTEREO
Manual page updates are on the way.
Tested by: joel, Olivier SMEDTS <olivier at gid0 d org>, too many
unsung / unnamed heroes.
2009-06-07 19:12:08 +00:00
|
|
|
#!/usr/bin/awk -f
|
|
|
|
#
|
2017-11-27 15:23:17 +00:00
|
|
|
# SPDX-License-Identifier: BSD-2-Clause-FreeBSD
|
|
|
|
#
|
Sound Mega-commit. Expect further cleanup until code freeze.
For a slightly thorough explaination, please refer to
[1] http://people.freebsd.org/~ariff/SOUND_4.TXT.html .
Summary of changes includes:
1 Volume Per-Channel (vpc). Provides private / standalone volume control
unique per-stream pcm channel without touching master volume / pcm.
Applications can directly use SNDCTL_DSP_[GET|SET][PLAY|REC]VOL, or for
backwards compatibility, SOUND_MIXER_PCM through the opened dsp device
instead of /dev/mixer. Special "bypass" mode is enabled through
/dev/mixer which will automatically detect if the adjustment is made
through /dev/mixer and forward its request to this private volume
controller. Changes to this volume object will not interfere with
other channels.
Requirements:
- SNDCTL_DSP_[GET|SET][PLAY|REC]_VOL are newer ioctls (OSSv4) which
require specific application modifications (preferred).
- No modifications required for using bypass mode, so applications
like mplayer or xmms should work out of the box.
Kernel hints:
- hint.pcm.%d.vpc (0 = disable vpc).
Kernel sysctls:
- hw.snd.vpc_mixer_bypass (default: 1). Enable or disable /dev/mixer
bypass mode.
- hw.snd.vpc_autoreset (default: 1). By default, closing/opening
/dev/dsp will reset the volume back to 0 db gain/attenuation.
Setting this to 0 will preserve its settings across device
closing/opening.
- hw.snd.vpc_reset (default: 0). Panic/reset button to reset all
volume settings back to 0 db.
- hw.snd.vpc_0db (default: 45). 0 db relative to linear mixer value.
2 High quality fixed-point Bandlimited SINC sampling rate converter,
based on Julius O'Smith's Digital Audio Resampling -
http://ccrma.stanford.edu/~jos/resample/. It includes a filter design
script written in awk (the clumsiest joke I've ever written)
- 100% 32bit fixed-point, 64bit accumulator.
- Possibly among the fastest (if not fastest) of its kind.
- Resampling quality is tunable, either runtime or during kernel
compilation (FEEDER_RATE_PRESETS).
- Quality can be further customized during kernel compilation by
defining FEEDER_RATE_PRESETS in /etc/make.conf.
Kernel sysctls:
- hw.snd.feeder_rate_quality.
0 - Zero-order Hold (ZOH). Fastest, bad quality.
1 - Linear Interpolation (LINEAR). Slightly slower than ZOH,
better quality but still does not eliminate aliasing.
2 - (and above) - Sinc Interpolation(SINC). Best quality. SINC
quality always start from 2 and above.
Rough quality comparisons:
- http://people.freebsd.org/~ariff/z_comparison/
3 Bit-perfect mode. Bypasses all feeder/dsp effects. Pure sound will be
directly fed into the hardware.
4 Parametric (compile time) Software Equalizer (Bass/Treble mixer). Can
be customized by defining FEEDER_EQ_PRESETS in /etc/make.conf.
5 Transparent/Adaptive Virtual Channel. Now you don't have to disable
vchans in order to make digital format pass through. It also makes
vchans more dynamic by choosing a better format/rate among all the
concurrent streams, which means that dev.pcm.X.play.vchanformat/rate
becomes sort of optional.
6 Exclusive Stream, with special open() mode O_EXCL. This will "mute"
other concurrent vchan streams and only allow a single channel with
O_EXCL set to keep producing sound.
Other Changes:
* most feeder_* stuffs are compilable in userland. Let's not
speculate whether we should go all out for it (save that for
FreeBSD 16.0-RELEASE).
* kobj signature fixups, thanks to Andriy Gapon <avg@freebsd.org>
* pull out channel mixing logic out of vchan.c and create its own
feeder_mixer for world justice.
* various refactoring here and there, for good or bad.
* activation of few more OSSv4 ioctls() (see [1] above).
* opt_snd.h for possible compile time configuration:
(mostly for debugging purposes, don't try these at home)
SND_DEBUG
SND_DIAGNOSTIC
SND_FEEDER_MULTIFORMAT
SND_FEEDER_FULL_MULTIFORMAT
SND_FEEDER_RATE_HP
SND_PCM_64
SND_OLDSTEREO
Manual page updates are on the way.
Tested by: joel, Olivier SMEDTS <olivier at gid0 d org>, too many
unsung / unnamed heroes.
2009-06-07 19:12:08 +00:00
|
|
|
# Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org>
|
|
|
|
# All rights reserved.
|
|
|
|
#
|
|
|
|
# Redistribution and use in source and binary forms, with or without
|
|
|
|
# modification, are permitted provided that the following conditions
|
|
|
|
# are met:
|
|
|
|
# 1. Redistributions of source code must retain the above copyright
|
|
|
|
# notice, this list of conditions and the following disclaimer.
|
|
|
|
# 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
# notice, this list of conditions and the following disclaimer in the
|
|
|
|
# documentation and/or other materials provided with the distribution.
|
|
|
|
#
|
|
|
|
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
# SUCH DAMAGE.
|
|
|
|
#
|
|
|
|
# $FreeBSD$
|
|
|
|
#
|
|
|
|
|
|
|
|
#
|
|
|
|
# Biquad coefficients generator for Parametric Software Equalizer. Not as ugly
|
|
|
|
# as 'feeder_rate_mkfilter.awk'
|
|
|
|
#
|
|
|
|
# Based on:
|
|
|
|
#
|
|
|
|
# "Cookbook formulae for audio EQ biquad filter coefficients"
|
|
|
|
# by Robert Bristow-Johnson <rbj@audioimagination.com>
|
|
|
|
#
|
|
|
|
# - http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt
|
|
|
|
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#
|
|
|
|
# Some basic Math functions.
|
|
|
|
#
|
|
|
|
function abs(x)
|
|
|
|
{
|
|
|
|
return (((x < 0) ? -x : x) + 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
function fabs(x)
|
|
|
|
{
|
|
|
|
return (((x < 0.0) ? -x : x) + 0.0);
|
|
|
|
}
|
|
|
|
|
|
|
|
function floor(x, r)
|
|
|
|
{
|
|
|
|
r = int(x);
|
|
|
|
if (r > x)
|
|
|
|
r--;
|
|
|
|
return (r + 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
function pow(x, y)
|
|
|
|
{
|
|
|
|
return (exp(1.0 * y * log(1.0 * x)));
|
|
|
|
}
|
|
|
|
|
|
|
|
#
|
|
|
|
# What the hell...
|
|
|
|
#
|
|
|
|
function shl(x, y)
|
|
|
|
{
|
|
|
|
while (y > 0) {
|
|
|
|
x *= 2;
|
|
|
|
y--;
|
|
|
|
}
|
|
|
|
return (x);
|
|
|
|
}
|
|
|
|
|
|
|
|
function feedeq_w0(fc, rate)
|
|
|
|
{
|
|
|
|
return ((2.0 * M_PI * fc) / (1.0 * rate));
|
|
|
|
}
|
|
|
|
|
|
|
|
function feedeq_A(gain, A)
|
|
|
|
{
|
|
|
|
if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ || FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF)
|
|
|
|
A = pow(10, gain / 40.0);
|
|
|
|
else
|
|
|
|
A = sqrt(pow(10, gain / 20.0));
|
|
|
|
|
|
|
|
return (A);
|
|
|
|
}
|
|
|
|
|
|
|
|
function feedeq_alpha(w0, A, QS)
|
|
|
|
{
|
|
|
|
if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ)
|
|
|
|
alpha = sin(w0) / (2.0 * QS);
|
|
|
|
else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF)
|
|
|
|
alpha = sin(w0) * 0.5 * sqrt(A + ((1.0 / A) * \
|
|
|
|
((1.0 / QS) - 1.0)) + 2.0);
|
|
|
|
else
|
|
|
|
alpha = 0.0;
|
|
|
|
|
|
|
|
return (alpha);
|
|
|
|
}
|
|
|
|
|
|
|
|
function feedeq_fx_floor(v, r)
|
|
|
|
{
|
|
|
|
if (fabs(v) < fabs(smallest))
|
|
|
|
smallest = v;
|
|
|
|
if (fabs(v) > fabs(largest))
|
|
|
|
largest = v;
|
|
|
|
|
|
|
|
r = floor((v * FEEDEQ_COEFF_ONE) + 0.5);
|
|
|
|
|
|
|
|
if (r < INT32_MIN || r > INT32_MAX)
|
|
|
|
printf("\n#error overflow v=%f, " \
|
|
|
|
"please reduce FEEDEQ_COEFF_SHIFT\n", v);
|
|
|
|
|
|
|
|
return (r);
|
|
|
|
}
|
|
|
|
|
|
|
|
function feedeq_gen_biquad_coeffs(coeffs, rate, gain, \
|
|
|
|
w0, A, alpha, a0, a1, a2, b0, b1, b2)
|
|
|
|
{
|
|
|
|
w0 = feedeq_w0(FEEDEQ_TREBLE_SFREQ, 1.0 * rate);
|
|
|
|
A = feedeq_A(1.0 * gain);
|
|
|
|
alpha = feedeq_alpha(w0, A, FEEDEQ_TREBLE_SLOPE);
|
|
|
|
|
|
|
|
if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) {
|
|
|
|
b0 = 1.0 + (alpha * A);
|
|
|
|
b1 = -2.0 * cos(w0);
|
|
|
|
b2 = 1.0 - (alpha * A);
|
|
|
|
a0 = 1.0 + (alpha / A);
|
|
|
|
a1 = -2.0 * cos(w0);
|
|
|
|
a2 = 1.0 - (alpha / A);
|
|
|
|
} else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) {
|
|
|
|
b0 = A*((A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha));
|
|
|
|
b1 = -2.0*A*((A-1.0)+((A+1.0)*cos(w0)) );
|
|
|
|
b2 = A*((A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha));
|
|
|
|
a0 = (A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha );
|
|
|
|
a1 = 2.0 * ((A-1.0)-((A+1.0)*cos(w0)) );
|
|
|
|
a2 = (A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha );
|
|
|
|
} else
|
|
|
|
b0 = b1 = b2 = a0 = a1 = a2 = 0.0;
|
|
|
|
|
|
|
|
b0 /= a0;
|
|
|
|
b1 /= a0;
|
|
|
|
b2 /= a0;
|
|
|
|
a1 /= a0;
|
|
|
|
a2 /= a0;
|
|
|
|
|
|
|
|
coeffs["treble", gain, 0] = feedeq_fx_floor(a0);
|
|
|
|
coeffs["treble", gain, 1] = feedeq_fx_floor(a1);
|
|
|
|
coeffs["treble", gain, 2] = feedeq_fx_floor(a2);
|
|
|
|
coeffs["treble", gain, 3] = feedeq_fx_floor(b0);
|
|
|
|
coeffs["treble", gain, 4] = feedeq_fx_floor(b1);
|
|
|
|
coeffs["treble", gain, 5] = feedeq_fx_floor(b2);
|
|
|
|
|
|
|
|
w0 = feedeq_w0(FEEDEQ_BASS_SFREQ, 1.0 * rate);
|
|
|
|
A = feedeq_A(1.0 * gain);
|
|
|
|
alpha = feedeq_alpha(w0, A, FEEDEQ_BASS_SLOPE);
|
|
|
|
|
|
|
|
if (FEEDEQ_TYPE == FEEDEQ_TYPE_PEQ) {
|
|
|
|
b0 = 1.0 + (alpha * A);
|
|
|
|
b1 = -2.0 * cos(w0);
|
|
|
|
b2 = 1.0 - (alpha * A);
|
|
|
|
a0 = 1.0 + (alpha / A);
|
|
|
|
a1 = -2.0 * cos(w0);
|
|
|
|
a2 = 1.0 - (alpha / A);
|
|
|
|
} else if (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) {
|
|
|
|
b0 = A*((A+1.0)-((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha));
|
|
|
|
b1 = 2.0*A*((A-1.0)-((A+1.0)*cos(w0)) );
|
|
|
|
b2 = A*((A+1.0)-((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha));
|
|
|
|
a0 = (A+1.0)+((A-1.0)*cos(w0))+(2.0*sqrt(A)*alpha );
|
|
|
|
a1 = -2.0 * ((A-1.0)+((A+1.0)*cos(w0)) );
|
|
|
|
a2 = (A+1.0)+((A-1.0)*cos(w0))-(2.0*sqrt(A)*alpha );
|
|
|
|
} else
|
|
|
|
b0 = b1 = b2 = a0 = a1 = a2 = 0.0;
|
|
|
|
|
|
|
|
b0 /= a0;
|
|
|
|
b1 /= a0;
|
|
|
|
b2 /= a0;
|
|
|
|
a1 /= a0;
|
|
|
|
a2 /= a0;
|
|
|
|
|
|
|
|
coeffs["bass", gain, 0] = feedeq_fx_floor(a0);
|
|
|
|
coeffs["bass", gain, 1] = feedeq_fx_floor(a1);
|
|
|
|
coeffs["bass", gain, 2] = feedeq_fx_floor(a2);
|
|
|
|
coeffs["bass", gain, 3] = feedeq_fx_floor(b0);
|
|
|
|
coeffs["bass", gain, 4] = feedeq_fx_floor(b1);
|
|
|
|
coeffs["bass", gain, 5] = feedeq_fx_floor(b2);
|
|
|
|
}
|
|
|
|
|
|
|
|
function feedeq_gen_freq_coeffs(frq, g, i, v)
|
|
|
|
{
|
|
|
|
coeffs[0] = 0;
|
|
|
|
|
|
|
|
for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \
|
|
|
|
g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \
|
|
|
|
g += FEEDEQ_GAIN_STEP) {
|
|
|
|
feedeq_gen_biquad_coeffs(coeffs, frq, \
|
|
|
|
g * FEEDEQ_GAIN_RECIPROCAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\nstatic struct feed_eq_coeff eq_%d[%d] " \
|
|
|
|
"= {\n", frq, FEEDEQ_LEVELS);
|
|
|
|
for (g = (FEEDEQ_GAIN_MIN * FEEDEQ_GAIN_DIV); \
|
|
|
|
g <= (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV); \
|
|
|
|
g += FEEDEQ_GAIN_STEP) {
|
|
|
|
printf(" {{ ");
|
|
|
|
for (i = 1; i < 6; i++) {
|
|
|
|
v = coeffs["treble", g * FEEDEQ_GAIN_RECIPROCAL, i];
|
|
|
|
printf("%s0x%08x%s", \
|
|
|
|
(v < 0) ? "-" : " ", abs(v), \
|
|
|
|
(i == 5) ? " " : ", ");
|
|
|
|
}
|
|
|
|
printf("},\n { ");
|
|
|
|
for (i = 1; i < 6; i++) {
|
|
|
|
v = coeffs["bass", g * FEEDEQ_GAIN_RECIPROCAL, i];
|
|
|
|
printf("%s0x%08x%s", \
|
|
|
|
(v < 0) ? "-" : " ", abs(v), \
|
|
|
|
(i == 5) ? " " : ", ");
|
|
|
|
}
|
|
|
|
printf("}}%s\n", \
|
|
|
|
(g < (FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV)) ? "," : "");
|
|
|
|
}
|
|
|
|
printf("};\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
function feedeq_calc_preamp(norm, gain, shift, mul, bit, attn)
|
|
|
|
{
|
|
|
|
shift = FEEDEQ_PREAMP_SHIFT;
|
|
|
|
|
|
|
|
if (floor(FEEDEQ_PREAMP_BITDB) == 6 && \
|
|
|
|
(1.0 * floor(gain)) == gain && (floor(gain) % 6) == 0) {
|
|
|
|
mul = 1;
|
|
|
|
shift = floor(floor(gain) / 6);
|
|
|
|
} else {
|
|
|
|
bit = 32.0 - ((1.0 * gain) / (1.0 * FEEDEQ_PREAMP_BITDB));
|
|
|
|
attn = pow(2.0, bit) / pow(2.0, 32.0);
|
|
|
|
mul = floor((attn * FEEDEQ_PREAMP_ONE) + 0.5);
|
|
|
|
}
|
|
|
|
|
|
|
|
while ((mul % 2) == 0 && shift > 0) {
|
|
|
|
mul = floor(mul / 2);
|
|
|
|
shift--;
|
|
|
|
}
|
|
|
|
|
|
|
|
norm["mul"] = mul;
|
|
|
|
norm["shift"] = shift;
|
|
|
|
}
|
|
|
|
|
|
|
|
BEGIN {
|
|
|
|
M_PI = atan2(0.0, -1.0);
|
|
|
|
|
|
|
|
INT32_MAX = 1 + ((shl(1, 30) - 1) * 2);
|
|
|
|
INT32_MIN = -1 - INT32_MAX;
|
|
|
|
|
|
|
|
FEEDEQ_TYPE_PEQ = 0;
|
|
|
|
FEEDEQ_TYPE_SHELF = 1;
|
|
|
|
|
|
|
|
FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ;
|
|
|
|
|
|
|
|
FEEDEQ_COEFF_SHIFT = 24;
|
|
|
|
FEEDEQ_COEFF_ONE = shl(1, FEEDEQ_COEFF_SHIFT);
|
|
|
|
|
|
|
|
FEEDEQ_PREAMP_SHIFT = 31;
|
|
|
|
FEEDEQ_PREAMP_ONE = shl(1, FEEDEQ_PREAMP_SHIFT);
|
|
|
|
FEEDEQ_PREAMP_BITDB = 6; # 20.0 * (log(2.0) / log(10.0));
|
|
|
|
|
|
|
|
FEEDEQ_GAIN_DIV = 10;
|
|
|
|
i = 0;
|
|
|
|
j = 1;
|
|
|
|
while (j < FEEDEQ_GAIN_DIV) {
|
|
|
|
j *= 2;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
FEEDEQ_GAIN_SHIFT = i;
|
|
|
|
FEEDEQ_GAIN_FMASK = shl(1, FEEDEQ_GAIN_SHIFT) - 1;
|
|
|
|
|
|
|
|
FEEDEQ_GAIN_RECIPROCAL = 1.0 / FEEDEQ_GAIN_DIV;
|
|
|
|
|
|
|
|
if (ARGC == 2) {
|
|
|
|
i = 1;
|
|
|
|
split(ARGV[1], arg, ":");
|
|
|
|
while (match(arg[i], "^[^0-9]*$")) {
|
|
|
|
if (arg[i] == "PEQ") {
|
|
|
|
FEEDEQ_TYPE = FEEDEQ_TYPE_PEQ;
|
|
|
|
} else if (arg[i] == "SHELF") {
|
|
|
|
FEEDEQ_TYPE = FEEDEQ_TYPE_SHELF;
|
|
|
|
}
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
split(arg[i++], subarg, ",");
|
|
|
|
FEEDEQ_TREBLE_SFREQ = 1.0 * subarg[1];
|
|
|
|
FEEDEQ_TREBLE_SLOPE = 1.0 * subarg[2];
|
2022-08-22 07:41:05 +00:00
|
|
|
FEEDEQ_BASS_SFREQ = 1.0 * subarg[3];
|
|
|
|
FEEDEQ_BASS_SLOPE = 1.0 * subarg[4];
|
Sound Mega-commit. Expect further cleanup until code freeze.
For a slightly thorough explaination, please refer to
[1] http://people.freebsd.org/~ariff/SOUND_4.TXT.html .
Summary of changes includes:
1 Volume Per-Channel (vpc). Provides private / standalone volume control
unique per-stream pcm channel without touching master volume / pcm.
Applications can directly use SNDCTL_DSP_[GET|SET][PLAY|REC]VOL, or for
backwards compatibility, SOUND_MIXER_PCM through the opened dsp device
instead of /dev/mixer. Special "bypass" mode is enabled through
/dev/mixer which will automatically detect if the adjustment is made
through /dev/mixer and forward its request to this private volume
controller. Changes to this volume object will not interfere with
other channels.
Requirements:
- SNDCTL_DSP_[GET|SET][PLAY|REC]_VOL are newer ioctls (OSSv4) which
require specific application modifications (preferred).
- No modifications required for using bypass mode, so applications
like mplayer or xmms should work out of the box.
Kernel hints:
- hint.pcm.%d.vpc (0 = disable vpc).
Kernel sysctls:
- hw.snd.vpc_mixer_bypass (default: 1). Enable or disable /dev/mixer
bypass mode.
- hw.snd.vpc_autoreset (default: 1). By default, closing/opening
/dev/dsp will reset the volume back to 0 db gain/attenuation.
Setting this to 0 will preserve its settings across device
closing/opening.
- hw.snd.vpc_reset (default: 0). Panic/reset button to reset all
volume settings back to 0 db.
- hw.snd.vpc_0db (default: 45). 0 db relative to linear mixer value.
2 High quality fixed-point Bandlimited SINC sampling rate converter,
based on Julius O'Smith's Digital Audio Resampling -
http://ccrma.stanford.edu/~jos/resample/. It includes a filter design
script written in awk (the clumsiest joke I've ever written)
- 100% 32bit fixed-point, 64bit accumulator.
- Possibly among the fastest (if not fastest) of its kind.
- Resampling quality is tunable, either runtime or during kernel
compilation (FEEDER_RATE_PRESETS).
- Quality can be further customized during kernel compilation by
defining FEEDER_RATE_PRESETS in /etc/make.conf.
Kernel sysctls:
- hw.snd.feeder_rate_quality.
0 - Zero-order Hold (ZOH). Fastest, bad quality.
1 - Linear Interpolation (LINEAR). Slightly slower than ZOH,
better quality but still does not eliminate aliasing.
2 - (and above) - Sinc Interpolation(SINC). Best quality. SINC
quality always start from 2 and above.
Rough quality comparisons:
- http://people.freebsd.org/~ariff/z_comparison/
3 Bit-perfect mode. Bypasses all feeder/dsp effects. Pure sound will be
directly fed into the hardware.
4 Parametric (compile time) Software Equalizer (Bass/Treble mixer). Can
be customized by defining FEEDER_EQ_PRESETS in /etc/make.conf.
5 Transparent/Adaptive Virtual Channel. Now you don't have to disable
vchans in order to make digital format pass through. It also makes
vchans more dynamic by choosing a better format/rate among all the
concurrent streams, which means that dev.pcm.X.play.vchanformat/rate
becomes sort of optional.
6 Exclusive Stream, with special open() mode O_EXCL. This will "mute"
other concurrent vchan streams and only allow a single channel with
O_EXCL set to keep producing sound.
Other Changes:
* most feeder_* stuffs are compilable in userland. Let's not
speculate whether we should go all out for it (save that for
FreeBSD 16.0-RELEASE).
* kobj signature fixups, thanks to Andriy Gapon <avg@freebsd.org>
* pull out channel mixing logic out of vchan.c and create its own
feeder_mixer for world justice.
* various refactoring here and there, for good or bad.
* activation of few more OSSv4 ioctls() (see [1] above).
* opt_snd.h for possible compile time configuration:
(mostly for debugging purposes, don't try these at home)
SND_DEBUG
SND_DIAGNOSTIC
SND_FEEDER_MULTIFORMAT
SND_FEEDER_FULL_MULTIFORMAT
SND_FEEDER_RATE_HP
SND_PCM_64
SND_OLDSTEREO
Manual page updates are on the way.
Tested by: joel, Olivier SMEDTS <olivier at gid0 d org>, too many
unsung / unnamed heroes.
2009-06-07 19:12:08 +00:00
|
|
|
split(arg[i++], subarg, ",");
|
|
|
|
FEEDEQ_GAIN_MIN = floor(1.0 * subarg[1]);
|
|
|
|
FEEDEQ_GAIN_MAX = floor(1.0 * subarg[2]);
|
|
|
|
if (length(subarg) > 2) {
|
|
|
|
j = floor(1.0 * FEEDEQ_GAIN_DIV * subarg[3]);
|
|
|
|
if (j < 2)
|
|
|
|
j = 1;
|
|
|
|
else if (j < 5)
|
|
|
|
j = 2;
|
|
|
|
else if (j < 10)
|
|
|
|
j = 5;
|
|
|
|
else
|
|
|
|
j = 10;
|
|
|
|
if (j > FEEDEQ_GAIN_DIV || (FEEDEQ_GAIN_DIV % j) != 0)
|
|
|
|
j = FEEDEQ_GAIN_DIV;
|
|
|
|
FEEDEQ_GAIN_STEP = j;
|
|
|
|
} else
|
|
|
|
FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV;
|
|
|
|
split(arg[i], subarg, ",");
|
|
|
|
for (i = 1; i <= length(subarg); i++)
|
|
|
|
allfreq[i - 1] = floor(1.0 * subarg[i]);
|
|
|
|
} else {
|
|
|
|
FEEDEQ_TREBLE_SFREQ = 16000.0;
|
|
|
|
FEEDEQ_TREBLE_SLOPE = 0.25;
|
|
|
|
FEEDEQ_BASS_SFREQ = 62.0;
|
|
|
|
FEEDEQ_BASS_SLOPE = 0.25;
|
|
|
|
|
|
|
|
FEEDEQ_GAIN_MIN = -9;
|
|
|
|
FEEDEQ_GAIN_MAX = 9;
|
|
|
|
|
|
|
|
FEEDEQ_GAIN_STEP = FEEDEQ_GAIN_DIV;
|
|
|
|
|
|
|
|
|
|
|
|
allfreq[0] = 44100;
|
|
|
|
allfreq[1] = 48000;
|
|
|
|
allfreq[2] = 88200;
|
|
|
|
allfreq[3] = 96000;
|
|
|
|
allfreq[4] = 176400;
|
|
|
|
allfreq[5] = 192000;
|
|
|
|
}
|
|
|
|
|
|
|
|
FEEDEQ_LEVELS = ((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) * \
|
|
|
|
floor(FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1;
|
|
|
|
|
|
|
|
FEEDEQ_ERR_CLIP = 0;
|
|
|
|
|
|
|
|
smallest = 10.000000;
|
|
|
|
largest = 0.000010;
|
|
|
|
|
|
|
|
printf("#ifndef _FEEDER_EQ_GEN_H_\n");
|
|
|
|
printf("#define _FEEDER_EQ_GEN_H_\n\n");
|
|
|
|
printf("/*\n");
|
|
|
|
printf(" * Generated using feeder_eq_mkfilter.awk, heaven, wind and awesome.\n");
|
|
|
|
printf(" *\n");
|
|
|
|
printf(" * DO NOT EDIT!\n");
|
|
|
|
printf(" */\n\n");
|
|
|
|
printf("/*\n");
|
|
|
|
printf(" * EQ: %s\n", (FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? \
|
|
|
|
"Shelving" : "Peaking EQ");
|
|
|
|
printf(" */\n");
|
|
|
|
printf("#define FEEDER_EQ_PRESETS\t\"");
|
|
|
|
printf("%s:%d,%.4f,%d,%.4f:%d,%d,%.1f:", \
|
|
|
|
(FEEDEQ_TYPE == FEEDEQ_TYPE_SHELF) ? "SHELF" : "PEQ", \
|
|
|
|
FEEDEQ_TREBLE_SFREQ, FEEDEQ_TREBLE_SLOPE, \
|
|
|
|
FEEDEQ_BASS_SFREQ, FEEDEQ_BASS_SLOPE, \
|
|
|
|
FEEDEQ_GAIN_MIN, FEEDEQ_GAIN_MAX, \
|
|
|
|
FEEDEQ_GAIN_STEP * FEEDEQ_GAIN_RECIPROCAL);
|
|
|
|
for (i = 0; i < length(allfreq); i++) {
|
|
|
|
if (i != 0)
|
|
|
|
printf(",");
|
|
|
|
printf("%d", allfreq[i]);
|
|
|
|
}
|
|
|
|
printf("\"\n\n");
|
|
|
|
printf("struct feed_eq_coeff_tone {\n");
|
|
|
|
printf("\tint32_t a1, a2;\n");
|
|
|
|
printf("\tint32_t b0, b1, b2;\n");
|
|
|
|
printf("};\n\n");
|
|
|
|
printf("struct feed_eq_coeff {\n");
|
|
|
|
#printf("\tstruct {\n");
|
|
|
|
#printf("\t\tint32_t a1, a2;\n");
|
|
|
|
#printf("\t\tint32_t b0, b1, b2;\n");
|
|
|
|
#printf("\t} treble, bass;\n");
|
|
|
|
printf("\tstruct feed_eq_coeff_tone treble;\n");
|
|
|
|
printf("\tstruct feed_eq_coeff_tone bass;\n");
|
|
|
|
#printf("\tstruct {\n");
|
|
|
|
#printf("\t\tint32_t a1, a2;\n");
|
|
|
|
#printf("\t\tint32_t b0, b1, b2;\n");
|
|
|
|
#printf("\t} bass;\n");
|
|
|
|
printf("};\n");
|
|
|
|
for (i = 0; i < length(allfreq); i++)
|
|
|
|
feedeq_gen_freq_coeffs(allfreq[i]);
|
|
|
|
printf("\n");
|
|
|
|
printf("static const struct {\n");
|
|
|
|
printf("\tuint32_t rate;\n");
|
|
|
|
printf("\tstruct feed_eq_coeff *coeff;\n");
|
|
|
|
printf("} feed_eq_tab[] = {\n");
|
|
|
|
for (i = 0; i < length(allfreq); i++) {
|
|
|
|
printf("\t{ %6d, eq_%-6d },\n", allfreq[i], allfreq[i]);
|
|
|
|
}
|
|
|
|
printf("};\n");
|
|
|
|
|
|
|
|
printf("\n#define FEEDEQ_RATE_MIN\t\t%d\n", allfreq[0]);
|
|
|
|
printf("#define FEEDEQ_RATE_MAX\t\t%d\n", allfreq[length(allfreq) - 1]);
|
|
|
|
printf("\n#define FEEDEQ_TAB_SIZE\t\t\t\t\t\t\t\\\n");
|
|
|
|
printf("\t((int32_t)(sizeof(feed_eq_tab) / sizeof(feed_eq_tab[0])))\n");
|
|
|
|
|
|
|
|
printf("\nstatic const struct {\n");
|
|
|
|
printf("\tint32_t mul, shift;\n");
|
|
|
|
printf("} feed_eq_preamp[] = {\n");
|
|
|
|
for (i = (FEEDEQ_GAIN_MAX * 2 * FEEDEQ_GAIN_DIV); i >= 0; \
|
|
|
|
i -= FEEDEQ_GAIN_STEP) {
|
|
|
|
feedeq_calc_preamp(norm, i * FEEDEQ_GAIN_RECIPROCAL);
|
|
|
|
dbgain = ((FEEDEQ_GAIN_MAX * FEEDEQ_GAIN_DIV) - i) * \
|
|
|
|
FEEDEQ_GAIN_RECIPROCAL;
|
|
|
|
printf("\t{ 0x%08x, 0x%08x },\t/* %+5.1f dB */\n", \
|
|
|
|
norm["mul"], norm["shift"], dbgain);
|
|
|
|
}
|
|
|
|
printf("};\n");
|
|
|
|
|
|
|
|
printf("\n#define FEEDEQ_GAIN_MIN\t\t%d", FEEDEQ_GAIN_MIN);
|
|
|
|
printf("\n#define FEEDEQ_GAIN_MAX\t\t%d\n", FEEDEQ_GAIN_MAX);
|
|
|
|
|
|
|
|
printf("\n#define FEEDEQ_GAIN_SHIFT\t%d\n", FEEDEQ_GAIN_SHIFT);
|
|
|
|
printf("#define FEEDEQ_GAIN_DIV\t\t%d\n", FEEDEQ_GAIN_DIV);
|
|
|
|
printf("#define FEEDEQ_GAIN_FMASK\t0x%08x\n", FEEDEQ_GAIN_FMASK);
|
|
|
|
printf("#define FEEDEQ_GAIN_STEP\t%d\n", FEEDEQ_GAIN_STEP);
|
|
|
|
|
|
|
|
#printf("\n#define FEEDEQ_PREAMP_MIN\t-%d\n", \
|
|
|
|
# shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT));
|
|
|
|
#printf("#define FEEDEQ_PREAMP_MAX\t%d\n", \
|
|
|
|
# shl(FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_SHIFT));
|
|
|
|
|
|
|
|
printf("\n#define FEEDEQ_COEFF_SHIFT\t%d\n", FEEDEQ_COEFF_SHIFT);
|
|
|
|
|
|
|
|
#feedeq_calc_preamp(norm, FEEDEQ_GAIN_MAX);
|
|
|
|
|
|
|
|
#printf("#define FEEDEQ_COEFF_NORM(v)\t(");
|
|
|
|
#if (norm["mul"] == 1)
|
|
|
|
# printf("(v) >> %d", norm["shift"]);
|
|
|
|
#else
|
|
|
|
# printf("(0x%xLL * (v)) >> %d", norm["mul"], norm["shift"]);
|
|
|
|
#printf(")\n");
|
|
|
|
|
|
|
|
#printf("\n#define FEEDEQ_LEVELS\t\t%d\n", FEEDEQ_LEVELS);
|
|
|
|
if (FEEDEQ_ERR_CLIP != 0)
|
|
|
|
printf("\n#define FEEDEQ_ERR_CLIP\t\t%d\n", FEEDEQ_ERR_CLIP);
|
|
|
|
printf("\n/*\n");
|
|
|
|
printf(" * volume level mapping (0 - 100):\n");
|
|
|
|
printf(" *\n");
|
|
|
|
|
|
|
|
for (i = 0; i <= 100; i++) {
|
|
|
|
ind = floor((i * FEEDEQ_LEVELS) / 100);
|
|
|
|
if (ind >= FEEDEQ_LEVELS)
|
|
|
|
ind = FEEDEQ_LEVELS - 1;
|
|
|
|
printf(" *\t%3d -> %3d (%+5.1f dB)\n", \
|
|
|
|
i, ind, FEEDEQ_GAIN_MIN + \
|
|
|
|
(ind * (FEEDEQ_GAIN_RECIPROCAL * FEEDEQ_GAIN_STEP)));
|
|
|
|
}
|
|
|
|
|
|
|
|
printf(" */\n");
|
|
|
|
printf("\n/*\n * smallest: %.32f\n * largest: %.32f\n */\n", \
|
|
|
|
smallest, largest);
|
|
|
|
printf("\n#endif\t/* !_FEEDER_EQ_GEN_H_ */\n");
|
|
|
|
}
|