1193640Sariff/*- 2193640Sariff * Copyright (c) 2008-2009 Ariff Abdullah <ariff@FreeBSD.org> 3193640Sariff * All rights reserved. 4193640Sariff * 5193640Sariff * Redistribution and use in source and binary forms, with or without 6193640Sariff * modification, are permitted provided that the following conditions 7193640Sariff * are met: 8193640Sariff * 1. Redistributions of source code must retain the above copyright 9193640Sariff * notice, this list of conditions and the following disclaimer. 10193640Sariff * 2. Redistributions in binary form must reproduce the above copyright 11193640Sariff * notice, this list of conditions and the following disclaimer in the 12193640Sariff * documentation and/or other materials provided with the distribution. 13193640Sariff * 14193640Sariff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15193640Sariff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16193640Sariff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17193640Sariff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18193640Sariff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19193640Sariff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20193640Sariff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21193640Sariff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22193640Sariff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23193640Sariff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24193640Sariff * SUCH DAMAGE. 25193640Sariff */ 26193640Sariff 27193640Sariff/* 28193640Sariff * feeder_eq: Parametric (compile time) Software Equalizer. Though accidental, 29193640Sariff * it proves good enough for educational and general consumption. 30193640Sariff * 31193640Sariff * "Cookbook formulae for audio EQ biquad filter coefficients" 32193640Sariff * by Robert Bristow-Johnson <rbj@audioimagination.com> 33193640Sariff * - http://www.musicdsp.org/files/Audio-EQ-Cookbook.txt 34193640Sariff */ 35193640Sariff 36193640Sariff#ifdef _KERNEL 37193640Sariff#ifdef HAVE_KERNEL_OPTION_HEADERS 38193640Sariff#include "opt_snd.h" 39193640Sariff#endif 40193640Sariff#include <dev/sound/pcm/sound.h> 41193640Sariff#include <dev/sound/pcm/pcm.h> 42193640Sariff#include "feeder_if.h" 43193640Sariff 44193640Sariff#define SND_USE_FXDIV 45193640Sariff#include "snd_fxdiv_gen.h" 46193640Sariff 47193640SariffSND_DECLARE_FILE("$FreeBSD$"); 48193640Sariff#endif 49193640Sariff 50193640Sariff#include "feeder_eq_gen.h" 51193640Sariff 52193640Sariff#define FEEDEQ_LEVELS \ 53193640Sariff (((FEEDEQ_GAIN_MAX - FEEDEQ_GAIN_MIN) * \ 54193640Sariff (FEEDEQ_GAIN_DIV / FEEDEQ_GAIN_STEP)) + 1) 55193640Sariff 56193640Sariff#define FEEDEQ_L2GAIN(v) \ 57193640Sariff ((int)min(((v) * FEEDEQ_LEVELS) / 100, FEEDEQ_LEVELS - 1)) 58193640Sariff 59193640Sariff#define FEEDEQ_PREAMP_IPART(x) (abs(x) >> FEEDEQ_GAIN_SHIFT) 60193640Sariff#define FEEDEQ_PREAMP_FPART(x) (abs(x) & FEEDEQ_GAIN_FMASK) 61193640Sariff#define FEEDEQ_PREAMP_SIGNVAL(x) ((x) < 0 ? -1 : 1) 62193640Sariff#define FEEDEQ_PREAMP_SIGNMARK(x) (((x) < 0) ? '-' : '+') 63193640Sariff 64193640Sariff#define FEEDEQ_PREAMP_IMIN -192 65193640Sariff#define FEEDEQ_PREAMP_IMAX 192 66193640Sariff#define FEEDEQ_PREAMP_FMIN 0 67193640Sariff#define FEEDEQ_PREAMP_FMAX 9 68193640Sariff 69193640Sariff#define FEEDEQ_PREAMP_INVALID INT_MAX 70193640Sariff 71193640Sariff#define FEEDEQ_IF2PREAMP(i, f) \ 72193640Sariff ((abs(i) << FEEDEQ_GAIN_SHIFT) | \ 73193640Sariff (((abs(f) / FEEDEQ_GAIN_STEP) * FEEDEQ_GAIN_STEP) & \ 74193640Sariff FEEDEQ_GAIN_FMASK)) 75193640Sariff 76193640Sariff#define FEEDEQ_PREAMP_MIN \ 77193640Sariff (FEEDEQ_PREAMP_SIGNVAL(FEEDEQ_GAIN_MIN) * \ 78193640Sariff FEEDEQ_IF2PREAMP(FEEDEQ_GAIN_MIN, 0)) 79193640Sariff 80193640Sariff#define FEEDEQ_PREAMP_MAX \ 81193640Sariff (FEEDEQ_PREAMP_SIGNVAL(FEEDEQ_GAIN_MAX) * \ 82193640Sariff FEEDEQ_IF2PREAMP(FEEDEQ_GAIN_MAX, 0)) 83193640Sariff 84193640Sariff#define FEEDEQ_PREAMP_DEFAULT FEEDEQ_IF2PREAMP(0, 0) 85193640Sariff 86193640Sariff#define FEEDEQ_PREAMP2IDX(v) \ 87193640Sariff ((int32_t)((FEEDEQ_GAIN_MAX * (FEEDEQ_GAIN_DIV / \ 88193640Sariff FEEDEQ_GAIN_STEP)) + (FEEDEQ_PREAMP_SIGNVAL(v) * \ 89193640Sariff FEEDEQ_PREAMP_IPART(v) * (FEEDEQ_GAIN_DIV / \ 90193640Sariff FEEDEQ_GAIN_STEP)) + (FEEDEQ_PREAMP_SIGNVAL(v) * \ 91193640Sariff (FEEDEQ_PREAMP_FPART(v) / FEEDEQ_GAIN_STEP)))) 92193640Sariff 93193640Sariffstatic int feeder_eq_exact_rate = 0; 94193640Sariff 95193640Sariff#ifdef _KERNEL 96209193Savgstatic char feeder_eq_presets[] = FEEDER_EQ_PRESETS; 97193640SariffSYSCTL_STRING(_hw_snd, OID_AUTO, feeder_eq_presets, CTLFLAG_RD, 98193640Sariff &feeder_eq_presets, 0, "compile-time eq presets"); 99193640Sariff 100193640SariffTUNABLE_INT("hw.snd.feeder_eq_exact_rate", &feeder_eq_exact_rate); 101193640SariffSYSCTL_INT(_hw_snd, OID_AUTO, feeder_eq_exact_rate, CTLFLAG_RW, 102193640Sariff &feeder_eq_exact_rate, 0, "force exact rate validation"); 103193640Sariff#endif 104193640Sariff 105193640Sariffstruct feed_eq_info; 106193640Sariff 107193640Sarifftypedef void (*feed_eq_t)(struct feed_eq_info *, uint8_t *, uint32_t); 108193640Sariff 109193640Sariffstruct feed_eq_tone { 110193640Sariff intpcm_t o1[SND_CHN_MAX]; 111193640Sariff intpcm_t o2[SND_CHN_MAX]; 112193640Sariff intpcm_t i1[SND_CHN_MAX]; 113193640Sariff intpcm_t i2[SND_CHN_MAX]; 114193640Sariff int gain; 115193640Sariff}; 116193640Sariff 117193640Sariffstruct feed_eq_info { 118193640Sariff struct feed_eq_tone treble; 119193640Sariff struct feed_eq_tone bass; 120193640Sariff struct feed_eq_coeff *coeff; 121193640Sariff feed_eq_t biquad; 122193640Sariff uint32_t channels; 123193640Sariff uint32_t rate; 124193640Sariff uint32_t align; 125193640Sariff int32_t preamp; 126193640Sariff int state; 127193640Sariff}; 128193640Sariff 129193640Sariff#if !defined(_KERNEL) && defined(FEEDEQ_ERR_CLIP) 130193640Sariff#define FEEDEQ_ERR_CLIP_CHECK(t, v) do { \ 131193640Sariff if ((v) < PCM_S32_MIN || (v) > PCM_S32_MAX) \ 132193640Sariff errx(1, "\n\n%s(): ["#t"] Sample clipping: %jd\n", \ 133193640Sariff __func__, (intmax_t)(v)); \ 134193640Sariff} while (0) 135193640Sariff#else 136193640Sariff#define FEEDEQ_ERR_CLIP_CHECK(...) 137193640Sariff#endif 138193640Sariff 139193640Sariff#define FEEDEQ_CLAMP(v) (((v) > PCM_S32_MAX) ? PCM_S32_MAX : \ 140193640Sariff (((v) < PCM_S32_MIN) ? PCM_S32_MIN : \ 141193640Sariff (v))) 142193640Sariff 143193640Sariff#define FEEDEQ_DECLARE(SIGN, BIT, ENDIAN) \ 144193640Sariffstatic void \ 145193640Sarifffeed_eq_biquad_##SIGN##BIT##ENDIAN(struct feed_eq_info *info, \ 146193640Sariff uint8_t *dst, uint32_t count) \ 147193640Sariff{ \ 148193640Sariff struct feed_eq_coeff_tone *treble, *bass; \ 149193640Sariff intpcm64_t w; \ 150193640Sariff intpcm_t v; \ 151193640Sariff uint32_t i, j; \ 152193640Sariff int32_t pmul, pshift; \ 153193640Sariff \ 154193640Sariff pmul = feed_eq_preamp[info->preamp].mul; \ 155193640Sariff pshift = feed_eq_preamp[info->preamp].shift; \ 156193640Sariff \ 157193640Sariff if (info->state == FEEDEQ_DISABLE) { \ 158193640Sariff j = count * info->channels; \ 159193640Sariff dst += j * PCM_##BIT##_BPS; \ 160193640Sariff do { \ 161193640Sariff dst -= PCM_##BIT##_BPS; \ 162193640Sariff v = _PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ 163193640Sariff v = ((intpcm64_t)pmul * v) >> pshift; \ 164193640Sariff _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \ 165193640Sariff } while (--j != 0); \ 166193640Sariff \ 167193640Sariff return; \ 168193640Sariff } \ 169193640Sariff \ 170193640Sariff treble = &(info->coeff[info->treble.gain].treble); \ 171193640Sariff bass = &(info->coeff[info->bass.gain].bass); \ 172193640Sariff \ 173193640Sariff do { \ 174193640Sariff i = 0; \ 175193640Sariff j = info->channels; \ 176193640Sariff do { \ 177193640Sariff v = _PCM_READ_##SIGN##BIT##_##ENDIAN(dst); \ 178193640Sariff v <<= 32 - BIT; \ 179193640Sariff v = ((intpcm64_t)pmul * v) >> pshift; \ 180193640Sariff \ 181193640Sariff w = (intpcm64_t)v * treble->b0; \ 182193640Sariff w += (intpcm64_t)info->treble.i1[i] * treble->b1; \ 183193640Sariff w += (intpcm64_t)info->treble.i2[i] * treble->b2; \ 184193640Sariff w -= (intpcm64_t)info->treble.o1[i] * treble->a1; \ 185193640Sariff w -= (intpcm64_t)info->treble.o2[i] * treble->a2; \ 186193640Sariff info->treble.i2[i] = info->treble.i1[i]; \ 187193640Sariff info->treble.i1[i] = v; \ 188193640Sariff info->treble.o2[i] = info->treble.o1[i]; \ 189193640Sariff w >>= FEEDEQ_COEFF_SHIFT; \ 190193640Sariff FEEDEQ_ERR_CLIP_CHECK(treble, w); \ 191193640Sariff v = FEEDEQ_CLAMP(w); \ 192193640Sariff info->treble.o1[i] = v; \ 193193640Sariff \ 194193640Sariff w = (intpcm64_t)v * bass->b0; \ 195193640Sariff w += (intpcm64_t)info->bass.i1[i] * bass->b1; \ 196193640Sariff w += (intpcm64_t)info->bass.i2[i] * bass->b2; \ 197193640Sariff w -= (intpcm64_t)info->bass.o1[i] * bass->a1; \ 198193640Sariff w -= (intpcm64_t)info->bass.o2[i] * bass->a2; \ 199193640Sariff info->bass.i2[i] = info->bass.i1[i]; \ 200193640Sariff info->bass.i1[i] = v; \ 201193640Sariff info->bass.o2[i] = info->bass.o1[i]; \ 202193640Sariff w >>= FEEDEQ_COEFF_SHIFT; \ 203193640Sariff FEEDEQ_ERR_CLIP_CHECK(bass, w); \ 204193640Sariff v = FEEDEQ_CLAMP(w); \ 205193640Sariff info->bass.o1[i] = v; \ 206193640Sariff \ 207193640Sariff v >>= 32 - BIT; \ 208193640Sariff _PCM_WRITE_##SIGN##BIT##_##ENDIAN(dst, v); \ 209193640Sariff dst += PCM_##BIT##_BPS; \ 210193640Sariff i++; \ 211193640Sariff } while (--j != 0); \ 212193640Sariff } while (--count != 0); \ 213193640Sariff} 214193640Sariff 215193640Sariff#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 216193640SariffFEEDEQ_DECLARE(S, 16, LE) 217193640SariffFEEDEQ_DECLARE(S, 32, LE) 218193640Sariff#endif 219193640Sariff#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 220193640SariffFEEDEQ_DECLARE(S, 16, BE) 221193640SariffFEEDEQ_DECLARE(S, 32, BE) 222193640Sariff#endif 223193640Sariff#ifdef SND_FEEDER_MULTIFORMAT 224193640SariffFEEDEQ_DECLARE(S, 8, NE) 225193640SariffFEEDEQ_DECLARE(S, 24, LE) 226193640SariffFEEDEQ_DECLARE(S, 24, BE) 227193640SariffFEEDEQ_DECLARE(U, 8, NE) 228193640SariffFEEDEQ_DECLARE(U, 16, LE) 229193640SariffFEEDEQ_DECLARE(U, 24, LE) 230193640SariffFEEDEQ_DECLARE(U, 32, LE) 231193640SariffFEEDEQ_DECLARE(U, 16, BE) 232193640SariffFEEDEQ_DECLARE(U, 24, BE) 233193640SariffFEEDEQ_DECLARE(U, 32, BE) 234193640Sariff#endif 235193640Sariff 236193640Sariff#define FEEDEQ_ENTRY(SIGN, BIT, ENDIAN) \ 237193640Sariff { \ 238193640Sariff AFMT_##SIGN##BIT##_##ENDIAN, \ 239193640Sariff feed_eq_biquad_##SIGN##BIT##ENDIAN \ 240193640Sariff } 241193640Sariff 242193640Sariff 243193640Sariffstatic const struct { 244193640Sariff uint32_t format; 245193640Sariff feed_eq_t biquad; 246193640Sariff} feed_eq_biquad_tab[] = { 247193640Sariff#if BYTE_ORDER == LITTLE_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 248193640Sariff FEEDEQ_ENTRY(S, 16, LE), 249193640Sariff FEEDEQ_ENTRY(S, 32, LE), 250193640Sariff#endif 251193640Sariff#if BYTE_ORDER == BIG_ENDIAN || defined(SND_FEEDER_MULTIFORMAT) 252193640Sariff FEEDEQ_ENTRY(S, 16, BE), 253193640Sariff FEEDEQ_ENTRY(S, 32, BE), 254193640Sariff#endif 255193640Sariff#ifdef SND_FEEDER_MULTIFORMAT 256193640Sariff FEEDEQ_ENTRY(S, 8, NE), 257193640Sariff FEEDEQ_ENTRY(S, 24, LE), 258193640Sariff FEEDEQ_ENTRY(S, 24, BE), 259193640Sariff FEEDEQ_ENTRY(U, 8, NE), 260193640Sariff FEEDEQ_ENTRY(U, 16, LE), 261193640Sariff FEEDEQ_ENTRY(U, 24, LE), 262193640Sariff FEEDEQ_ENTRY(U, 32, LE), 263193640Sariff FEEDEQ_ENTRY(U, 16, BE), 264193640Sariff FEEDEQ_ENTRY(U, 24, BE), 265193640Sariff FEEDEQ_ENTRY(U, 32, BE) 266193640Sariff#endif 267193640Sariff}; 268193640Sariff 269193640Sariff#define FEEDEQ_BIQUAD_TAB_SIZE \ 270193640Sariff ((int32_t)(sizeof(feed_eq_biquad_tab) / sizeof(feed_eq_biquad_tab[0]))) 271193640Sariff 272193640Sariffstatic struct feed_eq_coeff * 273193640Sarifffeed_eq_coeff_rate(uint32_t rate) 274193640Sariff{ 275193640Sariff uint32_t spd, threshold; 276193640Sariff int i; 277193640Sariff 278193640Sariff if (rate < FEEDEQ_RATE_MIN || rate > FEEDEQ_RATE_MAX) 279193640Sariff return (NULL); 280193640Sariff 281193640Sariff /* 282193640Sariff * Not all rates are supported. Choose the best rate that we can to 283193640Sariff * allow 'sloppy' conversion. Good enough for naive listeners. 284193640Sariff */ 285193640Sariff for (i = 0; i < FEEDEQ_TAB_SIZE; i++) { 286193640Sariff spd = feed_eq_tab[i].rate; 287193640Sariff threshold = spd + ((i < (FEEDEQ_TAB_SIZE - 1) && 288193640Sariff feed_eq_tab[i + 1].rate > spd) ? 289193640Sariff ((feed_eq_tab[i + 1].rate - spd) >> 1) : 0); 290193640Sariff if (rate == spd || 291193640Sariff (feeder_eq_exact_rate == 0 && rate <= threshold)) 292193640Sariff return (feed_eq_tab[i].coeff); 293193640Sariff } 294193640Sariff 295193640Sariff return (NULL); 296193640Sariff} 297193640Sariff 298193640Sariffint 299193640Sarifffeeder_eq_validrate(uint32_t rate) 300193640Sariff{ 301193640Sariff 302193640Sariff if (feed_eq_coeff_rate(rate) != NULL) 303193640Sariff return (1); 304193640Sariff 305193640Sariff return (0); 306193640Sariff} 307193640Sariff 308193640Sariffstatic void 309193640Sarifffeed_eq_reset(struct feed_eq_info *info) 310193640Sariff{ 311193640Sariff uint32_t i; 312193640Sariff 313193640Sariff for (i = 0; i < info->channels; i++) { 314193640Sariff info->treble.i1[i] = 0; 315193640Sariff info->treble.i2[i] = 0; 316193640Sariff info->treble.o1[i] = 0; 317193640Sariff info->treble.o2[i] = 0; 318193640Sariff info->bass.i1[i] = 0; 319193640Sariff info->bass.i2[i] = 0; 320193640Sariff info->bass.o1[i] = 0; 321193640Sariff info->bass.o2[i] = 0; 322193640Sariff } 323193640Sariff} 324193640Sariff 325193640Sariffstatic int 326193640Sarifffeed_eq_setup(struct feed_eq_info *info) 327193640Sariff{ 328193640Sariff 329193640Sariff info->coeff = feed_eq_coeff_rate(info->rate); 330193640Sariff if (info->coeff == NULL) 331193640Sariff return (EINVAL); 332193640Sariff 333193640Sariff feed_eq_reset(info); 334193640Sariff 335193640Sariff return (0); 336193640Sariff} 337193640Sariff 338193640Sariffstatic int 339193640Sarifffeed_eq_init(struct pcm_feeder *f) 340193640Sariff{ 341193640Sariff struct feed_eq_info *info; 342193640Sariff feed_eq_t biquad_op; 343193640Sariff int i; 344193640Sariff 345193640Sariff if (f->desc->in != f->desc->out) 346193640Sariff return (EINVAL); 347193640Sariff 348193640Sariff biquad_op = NULL; 349193640Sariff 350193640Sariff for (i = 0; i < FEEDEQ_BIQUAD_TAB_SIZE && biquad_op == NULL; i++) { 351193640Sariff if (AFMT_ENCODING(f->desc->in) == feed_eq_biquad_tab[i].format) 352193640Sariff biquad_op = feed_eq_biquad_tab[i].biquad; 353193640Sariff } 354193640Sariff 355193640Sariff if (biquad_op == NULL) 356193640Sariff return (EINVAL); 357193640Sariff 358193640Sariff info = malloc(sizeof(*info), M_DEVBUF, M_NOWAIT | M_ZERO); 359193640Sariff if (info == NULL) 360193640Sariff return (ENOMEM); 361193640Sariff 362193640Sariff info->channels = AFMT_CHANNEL(f->desc->in); 363193640Sariff info->align = info->channels * AFMT_BPS(f->desc->in); 364193640Sariff 365193640Sariff info->rate = FEEDEQ_RATE_MIN; 366193640Sariff info->treble.gain = FEEDEQ_L2GAIN(50); 367193640Sariff info->bass.gain = FEEDEQ_L2GAIN(50); 368193640Sariff info->preamp = FEEDEQ_PREAMP2IDX(FEEDEQ_PREAMP_DEFAULT); 369193640Sariff info->state = FEEDEQ_UNKNOWN; 370193640Sariff 371193640Sariff info->biquad = biquad_op; 372193640Sariff 373193640Sariff f->data = info; 374193640Sariff 375193640Sariff return (feed_eq_setup(info)); 376193640Sariff} 377193640Sariff 378193640Sariffstatic int 379193640Sarifffeed_eq_set(struct pcm_feeder *f, int what, int value) 380193640Sariff{ 381193640Sariff struct feed_eq_info *info; 382193640Sariff 383193640Sariff info = f->data; 384193640Sariff 385193640Sariff switch (what) { 386193640Sariff case FEEDEQ_CHANNELS: 387193640Sariff if (value < SND_CHN_MIN || value > SND_CHN_MAX) 388193640Sariff return (EINVAL); 389193640Sariff info->channels = (uint32_t)value; 390193640Sariff info->align = info->channels * AFMT_BPS(f->desc->in); 391193640Sariff feed_eq_reset(info); 392193640Sariff break; 393193640Sariff case FEEDEQ_RATE: 394193640Sariff if (feeder_eq_validrate(value) == 0) 395193640Sariff return (EINVAL); 396193640Sariff info->rate = (uint32_t)value; 397193640Sariff if (info->state == FEEDEQ_UNKNOWN) 398193640Sariff info->state = FEEDEQ_ENABLE; 399193640Sariff return (feed_eq_setup(info)); 400193640Sariff break; 401193640Sariff case FEEDEQ_TREBLE: 402193640Sariff case FEEDEQ_BASS: 403193640Sariff if (value < 0 || value > 100) 404193640Sariff return (EINVAL); 405193640Sariff if (what == FEEDEQ_TREBLE) 406193640Sariff info->treble.gain = FEEDEQ_L2GAIN(value); 407193640Sariff else 408193640Sariff info->bass.gain = FEEDEQ_L2GAIN(value); 409193640Sariff break; 410193640Sariff case FEEDEQ_PREAMP: 411193640Sariff if (value < FEEDEQ_PREAMP_MIN || value > FEEDEQ_PREAMP_MAX) 412193640Sariff return (EINVAL); 413193640Sariff info->preamp = FEEDEQ_PREAMP2IDX(value); 414193640Sariff break; 415193640Sariff case FEEDEQ_STATE: 416193640Sariff if (!(value == FEEDEQ_BYPASS || value == FEEDEQ_ENABLE || 417193640Sariff value == FEEDEQ_DISABLE)) 418193640Sariff return (EINVAL); 419193640Sariff info->state = value; 420193640Sariff feed_eq_reset(info); 421193640Sariff break; 422193640Sariff default: 423193640Sariff return (EINVAL); 424193640Sariff break; 425193640Sariff } 426193640Sariff 427193640Sariff return (0); 428193640Sariff} 429193640Sariff 430193640Sariffstatic int 431193640Sarifffeed_eq_free(struct pcm_feeder *f) 432193640Sariff{ 433193640Sariff struct feed_eq_info *info; 434193640Sariff 435193640Sariff info = f->data; 436193640Sariff if (info != NULL) 437193640Sariff free(info, M_DEVBUF); 438193640Sariff 439193640Sariff f->data = NULL; 440193640Sariff 441193640Sariff return (0); 442193640Sariff} 443193640Sariff 444193640Sariffstatic int 445193640Sarifffeed_eq_feed(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 446193640Sariff uint32_t count, void *source) 447193640Sariff{ 448193640Sariff struct feed_eq_info *info; 449193640Sariff uint32_t j; 450193640Sariff uint8_t *dst; 451193640Sariff 452193640Sariff info = f->data; 453193640Sariff 454193640Sariff /* 455193640Sariff * 3 major states: 456193640Sariff * FEEDEQ_BYPASS - Bypass entirely, nothing happened. 457193640Sariff * FEEDEQ_ENABLE - Preamp+biquad filtering. 458193640Sariff * FEEDEQ_DISABLE - Preamp only. 459193640Sariff */ 460193640Sariff if (info->state == FEEDEQ_BYPASS) 461193640Sariff return (FEEDER_FEED(f->source, c, b, count, source)); 462193640Sariff 463193640Sariff dst = b; 464193640Sariff count = SND_FXROUND(count, info->align); 465193640Sariff 466193640Sariff do { 467193640Sariff if (count < info->align) 468193640Sariff break; 469193640Sariff 470193640Sariff j = SND_FXDIV(FEEDER_FEED(f->source, c, dst, count, source), 471193640Sariff info->align); 472193640Sariff if (j == 0) 473193640Sariff break; 474193640Sariff 475193640Sariff info->biquad(info, dst, j); 476193640Sariff 477193640Sariff j *= info->align; 478193640Sariff dst += j; 479193640Sariff count -= j; 480193640Sariff 481193640Sariff } while (count != 0); 482193640Sariff 483193640Sariff return (dst - b); 484193640Sariff} 485193640Sariff 486193640Sariffstatic struct pcm_feederdesc feeder_eq_desc[] = { 487193640Sariff { FEEDER_EQ, 0, 0, 0, 0 }, 488193640Sariff { 0, 0, 0, 0, 0 } 489193640Sariff}; 490193640Sariff 491193640Sariffstatic kobj_method_t feeder_eq_methods[] = { 492193640Sariff KOBJMETHOD(feeder_init, feed_eq_init), 493193640Sariff KOBJMETHOD(feeder_free, feed_eq_free), 494193640Sariff KOBJMETHOD(feeder_set, feed_eq_set), 495193640Sariff KOBJMETHOD(feeder_feed, feed_eq_feed), 496193640Sariff KOBJMETHOD_END 497193640Sariff}; 498193640Sariff 499193640SariffFEEDER_DECLARE(feeder_eq, NULL); 500193640Sariff 501193640Sariffstatic int32_t 502193640Sarifffeed_eq_scan_preamp_arg(const char *s) 503193640Sariff{ 504193640Sariff int r, i, f; 505193640Sariff size_t len; 506193640Sariff char buf[32]; 507193640Sariff 508193640Sariff bzero(buf, sizeof(buf)); 509193640Sariff 510193640Sariff /* XXX kind of ugly, but works for now.. */ 511193640Sariff 512193640Sariff r = sscanf(s, "%d.%d", &i, &f); 513193640Sariff 514193640Sariff if (r == 1 && !(i < FEEDEQ_PREAMP_IMIN || i > FEEDEQ_PREAMP_IMAX)) { 515193640Sariff snprintf(buf, sizeof(buf), "%c%d", 516193640Sariff FEEDEQ_PREAMP_SIGNMARK(i), abs(i)); 517193640Sariff f = 0; 518193640Sariff } else if (r == 2 && 519193640Sariff !(i < FEEDEQ_PREAMP_IMIN || i > FEEDEQ_PREAMP_IMAX || 520193640Sariff f < FEEDEQ_PREAMP_FMIN || f > FEEDEQ_PREAMP_FMAX)) 521193640Sariff snprintf(buf, sizeof(buf), "%c%d.%d", 522193640Sariff FEEDEQ_PREAMP_SIGNMARK(i), abs(i), f); 523193640Sariff else 524193640Sariff return (FEEDEQ_PREAMP_INVALID); 525193640Sariff 526193640Sariff len = strlen(s); 527193640Sariff if (len > 2 && strcasecmp(s + len - 2, "dB") == 0) 528193640Sariff strlcat(buf, "dB", sizeof(buf)); 529193640Sariff 530193640Sariff if (i == 0 && *s == '-') 531193640Sariff *buf = '-'; 532193640Sariff 533193640Sariff if (strcasecmp(buf + ((*s >= '0' && *s <= '9') ? 1 : 0), s) != 0) 534193640Sariff return (FEEDEQ_PREAMP_INVALID); 535193640Sariff 536193640Sariff while ((f / FEEDEQ_GAIN_DIV) > 0) 537193640Sariff f /= FEEDEQ_GAIN_DIV; 538193640Sariff 539193640Sariff return (((i < 0 || *buf == '-') ? -1 : 1) * FEEDEQ_IF2PREAMP(i, f)); 540193640Sariff} 541193640Sariff 542193640Sariff#ifdef _KERNEL 543193640Sariffstatic int 544193640Sariffsysctl_dev_pcm_eq(SYSCTL_HANDLER_ARGS) 545193640Sariff{ 546193640Sariff struct snddev_info *d; 547193640Sariff struct pcm_channel *c; 548193640Sariff struct pcm_feeder *f; 549193640Sariff int err, val, oval; 550193640Sariff 551193640Sariff d = oidp->oid_arg1; 552193640Sariff if (!PCM_REGISTERED(d)) 553193640Sariff return (ENODEV); 554193640Sariff 555193640Sariff PCM_LOCK(d); 556193640Sariff PCM_WAIT(d); 557193640Sariff if (d->flags & SD_F_EQ_BYPASSED) 558193640Sariff val = 2; 559193640Sariff else if (d->flags & SD_F_EQ_ENABLED) 560193640Sariff val = 1; 561193640Sariff else 562193640Sariff val = 0; 563193640Sariff PCM_ACQUIRE(d); 564193640Sariff PCM_UNLOCK(d); 565193640Sariff 566193640Sariff oval = val; 567193640Sariff err = sysctl_handle_int(oidp, &val, 0, req); 568193640Sariff 569193640Sariff if (err == 0 && req->newptr != NULL && val != oval) { 570193640Sariff if (!(val == 0 || val == 1 || val == 2)) { 571193640Sariff PCM_RELEASE_QUICK(d); 572193640Sariff return (EINVAL); 573193640Sariff } 574193640Sariff 575193640Sariff PCM_LOCK(d); 576193640Sariff 577193640Sariff d->flags &= ~(SD_F_EQ_ENABLED | SD_F_EQ_BYPASSED); 578193640Sariff if (val == 2) { 579193640Sariff val = FEEDEQ_BYPASS; 580193640Sariff d->flags |= SD_F_EQ_BYPASSED; 581193640Sariff } else if (val == 1) { 582193640Sariff val = FEEDEQ_ENABLE; 583193640Sariff d->flags |= SD_F_EQ_ENABLED; 584193640Sariff } else 585193640Sariff val = FEEDEQ_DISABLE; 586193640Sariff 587193640Sariff CHN_FOREACH(c, d, channels.pcm.busy) { 588193640Sariff CHN_LOCK(c); 589193640Sariff f = chn_findfeeder(c, FEEDER_EQ); 590193640Sariff if (f != NULL) 591193640Sariff (void)FEEDER_SET(f, FEEDEQ_STATE, val); 592193640Sariff CHN_UNLOCK(c); 593193640Sariff } 594193640Sariff 595193640Sariff PCM_RELEASE(d); 596193640Sariff PCM_UNLOCK(d); 597193640Sariff } else 598193640Sariff PCM_RELEASE_QUICK(d); 599193640Sariff 600193640Sariff return (err); 601193640Sariff} 602193640Sariff 603193640Sariffstatic int 604193640Sariffsysctl_dev_pcm_eq_preamp(SYSCTL_HANDLER_ARGS) 605193640Sariff{ 606193640Sariff struct snddev_info *d; 607193640Sariff struct pcm_channel *c; 608193640Sariff struct pcm_feeder *f; 609193640Sariff int err, val, oval; 610193640Sariff char buf[32]; 611193640Sariff 612193640Sariff d = oidp->oid_arg1; 613193640Sariff if (!PCM_REGISTERED(d)) 614193640Sariff return (ENODEV); 615193640Sariff 616193640Sariff PCM_LOCK(d); 617193640Sariff PCM_WAIT(d); 618193640Sariff val = d->eqpreamp; 619193640Sariff bzero(buf, sizeof(buf)); 620193640Sariff (void)snprintf(buf, sizeof(buf), "%c%d.%ddB", 621193640Sariff FEEDEQ_PREAMP_SIGNMARK(val), FEEDEQ_PREAMP_IPART(val), 622193640Sariff FEEDEQ_PREAMP_FPART(val)); 623193640Sariff PCM_ACQUIRE(d); 624193640Sariff PCM_UNLOCK(d); 625193640Sariff 626193640Sariff oval = val; 627193640Sariff err = sysctl_handle_string(oidp, buf, sizeof(buf), req); 628193640Sariff 629193640Sariff if (err == 0 && req->newptr != NULL) { 630193640Sariff val = feed_eq_scan_preamp_arg(buf); 631193640Sariff if (val == FEEDEQ_PREAMP_INVALID) { 632193640Sariff PCM_RELEASE_QUICK(d); 633193640Sariff return (EINVAL); 634193640Sariff } 635193640Sariff 636193640Sariff PCM_LOCK(d); 637193640Sariff 638193640Sariff if (val != oval) { 639193640Sariff if (val < FEEDEQ_PREAMP_MIN) 640193640Sariff val = FEEDEQ_PREAMP_MIN; 641193640Sariff else if (val > FEEDEQ_PREAMP_MAX) 642193640Sariff val = FEEDEQ_PREAMP_MAX; 643193640Sariff 644193640Sariff d->eqpreamp = val; 645193640Sariff 646193640Sariff CHN_FOREACH(c, d, channels.pcm.busy) { 647193640Sariff CHN_LOCK(c); 648193640Sariff f = chn_findfeeder(c, FEEDER_EQ); 649193640Sariff if (f != NULL) 650193640Sariff (void)FEEDER_SET(f, FEEDEQ_PREAMP, val); 651193640Sariff CHN_UNLOCK(c); 652193640Sariff } 653193640Sariff 654193640Sariff } 655193640Sariff 656193640Sariff PCM_RELEASE(d); 657193640Sariff PCM_UNLOCK(d); 658193640Sariff } else 659193640Sariff PCM_RELEASE_QUICK(d); 660193640Sariff 661193640Sariff return (err); 662193640Sariff} 663193640Sariff 664193640Sariffvoid 665193640Sarifffeeder_eq_initsys(device_t dev) 666193640Sariff{ 667193640Sariff struct snddev_info *d; 668193640Sariff const char *preamp; 669193640Sariff char buf[64]; 670193640Sariff 671193640Sariff d = device_get_softc(dev); 672193640Sariff 673193640Sariff if (!(resource_string_value(device_get_name(dev), device_get_unit(dev), 674193640Sariff "eq_preamp", &preamp) == 0 && 675193640Sariff (d->eqpreamp = feed_eq_scan_preamp_arg(preamp)) != 676193640Sariff FEEDEQ_PREAMP_INVALID)) 677193640Sariff d->eqpreamp = FEEDEQ_PREAMP_DEFAULT; 678193640Sariff 679193640Sariff if (d->eqpreamp < FEEDEQ_PREAMP_MIN) 680193640Sariff d->eqpreamp = FEEDEQ_PREAMP_MIN; 681193640Sariff else if (d->eqpreamp > FEEDEQ_PREAMP_MAX) 682193640Sariff d->eqpreamp = FEEDEQ_PREAMP_MAX; 683193640Sariff 684193640Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 685193640Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 686193640Sariff "eq", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d), 687193640Sariff sysctl_dev_pcm_eq, "I", 688193640Sariff "Bass/Treble Equalizer (0=disable, 1=enable, 2=bypass)"); 689193640Sariff 690193640Sariff bzero(buf, sizeof(buf)); 691193640Sariff 692193640Sariff (void)snprintf(buf, sizeof(buf), "Bass/Treble Equalizer Preamp " 693193640Sariff "(-/+ %d.0dB , %d.%ddB step)", 694193640Sariff FEEDEQ_GAIN_MAX, FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV, 695193640Sariff FEEDEQ_GAIN_STEP - ((FEEDEQ_GAIN_STEP / FEEDEQ_GAIN_DIV) * 696193640Sariff FEEDEQ_GAIN_DIV)); 697193640Sariff 698193640Sariff SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), 699193640Sariff SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, 700193640Sariff "eq_preamp", CTLTYPE_STRING | CTLFLAG_RW, d, sizeof(d), 701193640Sariff sysctl_dev_pcm_eq_preamp, "A", buf); 702193640Sariff} 703193640Sariff#endif 704