feeder.c revision 50724
1285101Semaste/* 2285101Semaste * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3353358Sdim * All rights reserved. 4353358Sdim * 5353358Sdim * Redistribution and use in source and binary forms, with or without 6285101Semaste * modification, are permitted provided that the following conditions 7285101Semaste * are met: 8285101Semaste * 1. Redistributions of source code must retain the above copyright 9314564Sdim * notice, this list of conditions and the following disclaimer. 10285101Semaste * 2. Redistributions in binary form must reproduce the above copyright 11360784Sdim * notice, this list of conditions and the following disclaimer in the 12314564Sdim * documentation and/or other materials provided with the distribution. 13285101Semaste * 14285101Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15285101Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16285101Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17285101Semaste * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18314564Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19285101Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20314564Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21285101Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22314564Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23285101Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24314564Sdim * SUCH DAMAGE. 25360784Sdim * 26314564Sdim * $Id$ 27314564Sdim */ 28314564Sdim 29314564Sdim#include <dev/pcm/sound.h> 30314564Sdim 31285101Semastestatic int chn_addfeeder(pcm_channel *c, pcm_feeder *f); 32285101Semastestatic int chn_removefeeder(pcm_channel *c); 33285101Semaste 34314564Sdim#define FEEDBUFSZ 8192 35285101Semaste 36314564Sdimstatic unsigned char ulaw_to_u8[] = { 37314564Sdim 3, 7, 11, 15, 19, 23, 27, 31, 38314564Sdim 35, 39, 43, 47, 51, 55, 59, 63, 39314564Sdim 66, 68, 70, 72, 74, 76, 78, 80, 40314564Sdim 82, 84, 86, 88, 90, 92, 94, 96, 41314564Sdim 98, 99, 100, 101, 102, 103, 104, 105, 42314564Sdim 106, 107, 108, 109, 110, 111, 112, 113, 43285101Semaste 113, 114, 114, 115, 115, 116, 116, 117, 44285101Semaste 117, 118, 118, 119, 119, 120, 120, 121, 45314564Sdim 121, 121, 122, 122, 122, 122, 123, 123, 46360784Sdim 123, 123, 124, 124, 124, 124, 125, 125, 47314564Sdim 125, 125, 125, 125, 126, 126, 126, 126, 48314564Sdim 126, 126, 126, 126, 127, 127, 127, 127, 49314564Sdim 127, 127, 127, 127, 127, 127, 127, 127, 50314564Sdim 128, 128, 128, 128, 128, 128, 128, 128, 51285101Semaste 128, 128, 128, 128, 128, 128, 128, 128, 52314564Sdim 128, 128, 128, 128, 128, 128, 128, 128, 53285101Semaste 253, 249, 245, 241, 237, 233, 229, 225, 54285101Semaste 221, 217, 213, 209, 205, 201, 197, 193, 55314564Sdim 190, 188, 186, 184, 182, 180, 178, 176, 56314564Sdim 174, 172, 170, 168, 166, 164, 162, 160, 57360784Sdim 158, 157, 156, 155, 154, 153, 152, 151, 58314564Sdim 150, 149, 148, 147, 146, 145, 144, 143, 59314564Sdim 143, 142, 142, 141, 141, 140, 140, 139, 60314564Sdim 139, 138, 138, 137, 137, 136, 136, 135, 61314564Sdim 135, 135, 134, 134, 134, 134, 133, 133, 62285101Semaste 133, 133, 132, 132, 132, 132, 131, 131, 63314564Sdim 131, 131, 131, 131, 130, 130, 130, 130, 64285101Semaste 130, 130, 130, 130, 129, 129, 129, 129, 65285101Semaste 129, 129, 129, 129, 129, 129, 129, 129, 66314564Sdim 128, 128, 128, 128, 128, 128, 128, 128, 67360784Sdim 128, 128, 128, 128, 128, 128, 128, 128, 68314564Sdim 128, 128, 128, 128, 128, 128, 128, 128, 69314564Sdim}; 70314564Sdim 71314564Sdimstatic unsigned char u8_to_ulaw[] = { 72314564Sdim 0, 0, 0, 0, 0, 1, 1, 1, 73314564Sdim 1, 2, 2, 2, 2, 3, 3, 3, 74314564Sdim 3, 4, 4, 4, 4, 5, 5, 5, 75314564Sdim 5, 6, 6, 6, 6, 7, 7, 7, 76285101Semaste 7, 8, 8, 8, 8, 9, 9, 9, 77314564Sdim 9, 10, 10, 10, 10, 11, 11, 11, 78285101Semaste 11, 12, 12, 12, 12, 13, 13, 13, 79314564Sdim 13, 14, 14, 14, 14, 15, 15, 15, 80285101Semaste 15, 16, 16, 17, 17, 18, 18, 19, 81285101Semaste 19, 20, 20, 21, 21, 22, 22, 23, 82314564Sdim 23, 24, 24, 25, 25, 26, 26, 27, 83285101Semaste 27, 28, 28, 29, 29, 30, 30, 31, 84314564Sdim 31, 32, 33, 34, 35, 36, 37, 38, 85360784Sdim 39, 40, 41, 42, 43, 44, 45, 46, 86314564Sdim 47, 49, 51, 53, 55, 57, 59, 61, 87285101Semaste 63, 66, 70, 74, 78, 84, 92, 104, 88314564Sdim 254, 231, 219, 211, 205, 201, 197, 193, 89285101Semaste 190, 188, 186, 184, 182, 180, 178, 176, 90285101Semaste 175, 174, 173, 172, 171, 170, 169, 168, 91285101Semaste 167, 166, 165, 164, 163, 162, 161, 160, 92314564Sdim 159, 159, 158, 158, 157, 157, 156, 156, 93285101Semaste 155, 155, 154, 154, 153, 153, 152, 152, 94314564Sdim 151, 151, 150, 150, 149, 149, 148, 148, 95285101Semaste 147, 147, 146, 146, 145, 145, 144, 144, 96314564Sdim 143, 143, 143, 143, 142, 142, 142, 142, 97285101Semaste 141, 141, 141, 141, 140, 140, 140, 140, 98314564Sdim 139, 139, 139, 139, 138, 138, 138, 138, 99285101Semaste 137, 137, 137, 137, 136, 136, 136, 136, 100314564Sdim 135, 135, 135, 135, 134, 134, 134, 134, 101285101Semaste 133, 133, 133, 133, 132, 132, 132, 132, 102314564Sdim 131, 131, 131, 131, 130, 130, 130, 130, 103360784Sdim 129, 129, 129, 129, 128, 128, 128, 128, 104314564Sdim}; 105314564Sdim 106314564Sdim/*****************************************************************************/ 107314564Sdim 108285101Semastestatic int 109314564Sdimfeed_root(pcm_feeder *feeder, u_int8_t *buffer, u_int32_t count, struct uio *stream) 110285101Semaste{ 111285101Semaste int ret, tmp; 112285101Semaste if (!count) panic("feed_root: count == 0"); 113314564Sdim tmp = stream->uio_resid; 114360784Sdim ret = uiomove(buffer, count, stream); 115314564Sdim if (ret) panic("feed_root: uiomove failed"); 116314564Sdim tmp -= stream->uio_resid; 117314564Sdim if (!tmp) panic("feed_root: uiomove didn't"); 118314564Sdim return tmp; 119285101Semaste} 120314564Sdimpcm_feeder feeder_root = { "root", NULL, NULL, feed_root }; 121285101Semaste 122285101Semaste/*****************************************************************************/ 123285101Semaste 124314564Sdimstatic int 125360784Sdimfeed_8to16(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 126285101Semaste{ 127314564Sdim int i, j, k; 128314564Sdim k = f->source->feed(f->source, b, count / 2, stream); 129314564Sdim j = k - 1; 130314564Sdim i = j * 2 + 1; 131285101Semaste while (i > 0 && j >= 0) { 132314564Sdim b[i--] = b[j--]; 133285101Semaste b[i--] = 0; 134285101Semaste } 135285101Semaste return k * 2; 136314564Sdim} 137314564Sdimstatic pcm_feeder feeder_8to16 = { "8to16", NULL, NULL, feed_8to16 }; 138353358Sdim 139360784Sdim/*****************************************************************************/ 140285101Semaste 141314564Sdimstatic int 142314564Sdimfeed_16to8_init(pcm_feeder *f) 143314564Sdim{ 144314564Sdim f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); 145285101Semaste return (f->data == NULL); 146314564Sdim} 147285101Semaste 148314564Sdimstatic int 149314564Sdimfeed_16to8_free(pcm_feeder *f) 150314564Sdim{ 151314564Sdim if (f->data) free(f->data, M_DEVBUF); 152285101Semaste f->data = NULL; 153285101Semaste return 0; 154341825Sdim} 155341825Sdim 156360784Sdimstatic int 157341825Sdimfeed_16to8le(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 158341825Sdim{ 159341825Sdim u_int32_t i = 0, toget = count * 2; 160341825Sdim int j = 1, k; 161341825Sdim k = f->source->feed(f->source, f->data, min(toget, FEEDBUFSZ), stream); 162341825Sdim while (j < k) { 163341825Sdim b[i++] = ((u_int8_t *)f->data)[j]; 164341825Sdim j += 2; 165341825Sdim } 166314564Sdim return i; 167360784Sdim} 168314564Sdimstatic pcm_feeder feeder_16to8le = 169314564Sdim { "16to8le", feed_16to8_init, feed_16to8_free, feed_16to8le }; 170285101Semaste 171285101Semaste/*****************************************************************************/ 172285101Semaste 173314564Sdimstatic int 174360784Sdimfeed_monotostereo8(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 175314564Sdim{ 176314564Sdim int i, j, k = f->source->feed(f->source, b, count / 2, stream); 177314564Sdim j = k - 1; 178285101Semaste i = j * 2 + 1; 179285101Semaste while (i > 0 && j >= 0) { 180285101Semaste b[i--] = b[j]; 181314564Sdim b[i--] = b[j]; 182314564Sdim j--; 183360784Sdim } 184314564Sdim return k * 2; 185314564Sdim} 186314564Sdimstatic pcm_feeder feeder_monotostereo8 = 187285101Semaste { "monotostereo8", NULL, NULL, feed_monotostereo8 }; 188285101Semaste 189285101Semaste/*****************************************************************************/ 190314564Sdim 191360784Sdimstatic int 192285101Semastefeed_stereotomono8_init(pcm_feeder *f) 193314564Sdim{ 194314564Sdim f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); 195314564Sdim return (f->data == NULL); 196314564Sdim} 197314564Sdim 198314564Sdimstatic int 199314564Sdimfeed_stereotomono8_free(pcm_feeder *f) 200314564Sdim{ 201314564Sdim if (f->data) free(f->data, M_DEVBUF); 202314564Sdim f->data = NULL; 203314564Sdim return 0; 204344779Sdim} 205314564Sdim 206285101Semastestatic int 207314564Sdimfeed_stereotomono8(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 208285101Semaste{ 209314564Sdim u_int32_t i = 0, toget = count * 2; 210285101Semaste int j = 0, k; 211285101Semaste k = f->source->feed(f->source, f->data, min(toget, FEEDBUFSZ), stream); 212285101Semaste while (j < k) { 213314564Sdim b[i++] = ((u_int8_t *)f->data)[j]; 214360784Sdim j += 2; 215285101Semaste } 216314564Sdim return i; 217314564Sdim} 218314564Sdimstatic pcm_feeder feeder_stereotomono8 = 219344779Sdim { "stereotomono8", feed_stereotomono8_init, feed_stereotomono8_free, 220314564Sdim feed_stereotomono8 }; 221285101Semaste 222314564Sdim/*****************************************************************************/ 223285101Semaste 224285101Semastestatic int 225285101Semastefeed_endian(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 226314564Sdim{ 227360784Sdim u_int8_t t; 228314564Sdim int i = 0, j = f->source->feed(f->source, b, count, stream); 229314564Sdim while (i < j) { 230314564Sdim t = b[i]; 231314564Sdim b[i] = b[i + 1]; 232314564Sdim b[i + 1] = t; 233314564Sdim i += 2; 234314564Sdim } 235314564Sdim return count; 236344779Sdim} 237314564Sdimstatic pcm_feeder feeder_endian = { "endian", NULL, NULL, feed_endian }; 238285101Semaste 239314564Sdim/*****************************************************************************/ 240285101Semaste 241285101Semastestatic int 242285101Semastefeed_sign(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 243314564Sdim{ 244314564Sdim int i = 0, j = f->source->feed(f->source, b, count, stream); 245360784Sdim int ssz = (int)f->data, ofs = ssz - 1; 246314564Sdim while (i < j) { 247314564Sdim b[i + ofs] ^= 0x80; 248314564Sdim i += ssz; 249314564Sdim } 250314564Sdim return i; 251314564Sdim} 252314564Sdimstatic pcm_feeder feeder_sign8 = 253314564Sdim { "sign8", NULL, NULL, feed_sign, (void *)1 }; 254341825Sdimstatic pcm_feeder feeder_sign16 = 255341825Sdim { "sign16", NULL, NULL, feed_sign, (void *)2 }; 256314564Sdim 257314564Sdim/*****************************************************************************/ 258314564Sdim 259314564Sdimstatic int 260314564Sdimfeed_table(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 261314564Sdim{ 262314564Sdim int i = 0, j = f->source->feed(f->source, b, count, stream); 263314564Sdim while (i < j) { 264314564Sdim b[i] = ((u_int8_t *)f->data)[b[i]]; 265344779Sdim i++; 266314564Sdim } 267285101Semaste return i; 268314564Sdim} 269285101Semastestatic pcm_feeder feeder_ulawtou8 = 270285101Semaste { "ulawtou8", NULL, NULL, feed_table, ulaw_to_u8 }; 271285101Semastestatic pcm_feeder feeder_u8toulaw = 272314564Sdim { "u8toulaw", NULL, NULL, feed_table, u8_to_ulaw }; 273360784Sdim 274314564Sdim/*****************************************************************************/ 275314564Sdim 276314564Sdimstruct fmtspec { 277314564Sdim int stereo; 278285101Semaste int sign; 279314564Sdim int bit16; 280285101Semaste int bigendian; 281285101Semaste int ulaw; 282314564Sdim int bad; 283314564Sdim}; 284360784Sdim 285314564Sdimstruct fmtcvt { 286314564Sdim pcm_feeder *f; 287314564Sdim struct fmtspec ispec, ospec; 288314564Sdim}; 289314564Sdim 290314564Sdimstruct fmtcvt cvttab[] = { 291314564Sdim {&feeder_ulawtou8, {-1, 0, 0, 0, 1}, {-1, 0, 0, 0, 0}}, 292314564Sdim {&feeder_u8toulaw, {-1, 0, 0, 0, 0}, {-1, 0, 0, 0, 1}}, 293314564Sdim {&feeder_sign8, {-1, 0, 0, 0, 0}, {-1, 1, 0, 0, 0}}, 294285101Semaste {&feeder_sign8, {-1, 1, 0, 0, 0}, {-1, 0, 0, 0, 0}}, 295314564Sdim {&feeder_monotostereo8, { 0, -1, 0, 0, -1}, { 1, -1, 0, 0, -1}}, 296285101Semaste {&feeder_stereotomono8, { 1, -1, 0, 0, -1}, { 0, -1, 0, 0, -1}}, 297314564Sdim {&feeder_sign16, {-1, 0, 1, 0, 0}, {-1, 1, 1, 0, 0}}, 298314564Sdim {&feeder_sign16, {-1, 1, 1, 0, 0}, {-1, 0, 1, 0, 0}}, 299285101Semaste {&feeder_8to16, {-1, -1, 0, 0, 0}, {-1, -1, 1, 0, 0}}, 300314564Sdim {&feeder_16to8le, {-1, -1, 1, 0, 0}, {-1, -1, 0, 0, 0}}, 301285101Semaste {&feeder_endian, {-1, -1, 1, 0, 0}, {-1, -1, 1, 1, 0}}, 302285101Semaste {&feeder_endian, {-1, -1, 1, 1, 0}, {-1, -1, 1, 0, 0}}, 303314564Sdim}; 304314564Sdim#define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt)) 305314564Sdim 306360784Sdimstatic int 307314564Sdimgetspec(u_int32_t fmt, struct fmtspec *spec) 308314564Sdim{ 309314564Sdim spec->stereo = (fmt & AFMT_STEREO)? 1 : 0; 310314564Sdim spec->sign = (fmt & AFMT_SIGNED)? 1 : 0; 311314564Sdim spec->bit16 = (fmt & AFMT_16BIT)? 1 : 0; 312285101Semaste spec->bigendian = (fmt & AFMT_BIGENDIAN)? 1 : 0; 313314564Sdim spec->ulaw = (fmt & AFMT_MU_LAW)? 1 : 0; 314314564Sdim spec->bad = (fmt & (AFMT_A_LAW | AFMT_MPEG))? 1 : 0; 315314564Sdim return 0; 316285101Semaste} 317285101Semaste 318314564Sdimstatic int 319314564Sdimcmp(int x, int y) 320360784Sdim{ 321314564Sdim return (x == -1 || x == y || y == -1)? 1 : 0; 322314564Sdim} 323314564Sdim 324314564Sdimstatic int 325314564Sdimcmpspec(struct fmtspec *x, struct fmtspec *y) 326285101Semaste{ 327314564Sdim int i = 0; 328285101Semaste if (cmp(x->stereo, y->stereo)) i |= 0x01; 329314564Sdim if (cmp(x->sign, y->sign)) i |= 0x02; 330314564Sdim if (cmp(x->bit16, y->bit16)) i |= 0x04; 331314564Sdim if (cmp(x->bigendian, y->bigendian)) i |= 0x08; 332285101Semaste if (cmp(x->ulaw, y->ulaw)) i |= 0x10; 333285101Semaste return i; 334314564Sdim} 335360784Sdim 336285101Semastestatic int 337314564Sdimcvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s) 338314564Sdim{ 339314564Sdim int i = cmpspec(s, &cvt->ospec); 340314564Sdim chn_addfeeder(c, cvt->f); 341314564Sdim if (cvt->ospec.stereo != -1) s->stereo = cvt->ospec.stereo; 342314564Sdim if (cvt->ospec.sign != -1) s->sign = cvt->ospec.sign; 343314564Sdim if (cvt->ospec.bit16 != -1) s->bit16 = cvt->ospec.bit16; 344285101Semaste if (cvt->ospec.bigendian != -1) s->bigendian = cvt->ospec.bigendian; 345314564Sdim if (cvt->ospec.ulaw != -1) s->ulaw = cvt->ospec.ulaw; 346285101Semaste return i; 347285101Semaste} 348314564Sdim 349314564Sdimint 350285101Semastechn_feedchain(pcm_channel *c) 351360784Sdim{ 352314564Sdim int i, chosen, iter; 353327952Sdim u_int32_t mask; 354285101Semaste struct fmtspec s, t; 355285101Semaste struct fmtcvt *e; 356285101Semaste 357314564Sdim while (chn_removefeeder(c) != -1); 358285101Semaste if ((c->format & chn_getcaps(c)->formats) == c->format) 359285101Semaste return c->format; 360314564Sdim getspec(c->format, &s); 361285101Semaste if (s.bad) return -1; 362285101Semaste getspec(chn_getcaps(c)->bestfmt, &t); 363314564Sdim mask = (~cmpspec(&s, &t)) & 0x1f; 364285101Semaste iter = 0; 365314564Sdim do { 366360784Sdim if (mask == 0 || iter >= 8) break; 367314564Sdim chosen = -1; 368314564Sdim for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) { 369285101Semaste e = &cvttab[i]; 370314564Sdim if ((cmpspec(&s, &e->ispec) == 0x1f) && 371285101Semaste ((~cmpspec(&e->ispec, &e->ospec)) & mask)) 372285101Semaste chosen = i; 373314564Sdim } 374360784Sdim if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s); 375285101Semaste iter++; 376314564Sdim } while (chosen != -1); 377314564Sdim return (iter < 8)? chn_getcaps(c)->bestfmt : -1; 378314564Sdim} 379314564Sdim 380314564Sdimstatic int 381314564Sdimchn_addfeeder(pcm_channel *c, pcm_feeder *f) 382314564Sdim{ 383314564Sdim pcm_feeder *n; 384314564Sdim n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT); 385285101Semaste *n = *f; 386314564Sdim n->source = c->feeder; 387285101Semaste c->feeder = n; 388285101Semaste if (n->init) n->init(n); 389314564Sdim return 0; 390285101Semaste} 391285101Semaste 392314564Sdimstatic int 393285101Semastechn_removefeeder(pcm_channel *c) 394314564Sdim{ 395285101Semaste pcm_feeder *f; 396314564Sdim if (c->feeder == &feeder_root) return -1; 397314564Sdim f = c->feeder->source; 398314564Sdim if (c->feeder->free) c->feeder->free(c->feeder); 399285101Semaste free(c->feeder, M_DEVBUF); 400285101Semaste c->feeder = f; 401314564Sdim return 0; 402285101Semaste} 403314564Sdim 404314564Sdim