feeder.c revision 52713
150724Scg/* 250724Scg * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 350724Scg * All rights reserved. 450724Scg * 550724Scg * Redistribution and use in source and binary forms, with or without 650724Scg * modification, are permitted provided that the following conditions 750724Scg * are met: 850724Scg * 1. Redistributions of source code must retain the above copyright 950724Scg * notice, this list of conditions and the following disclaimer. 1050724Scg * 2. Redistributions in binary form must reproduce the above copyright 1150724Scg * notice, this list of conditions and the following disclaimer in the 1250724Scg * documentation and/or other materials provided with the distribution. 1350724Scg * 1450724Scg * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1550724Scg * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1650724Scg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1750724Scg * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1850724Scg * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1950724Scg * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2050724Scg * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2150724Scg * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2250724Scg * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2350724Scg * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2450724Scg * SUCH DAMAGE. 2550724Scg * 2650733Speter * $FreeBSD: head/sys/dev/sound/pcm/feeder.c 52713 1999-10-31 08:58:51Z tanimura $ 2750724Scg */ 2850724Scg 2950724Scg#include <dev/pcm/sound.h> 3050724Scg 3150724Scgstatic int chn_addfeeder(pcm_channel *c, pcm_feeder *f); 3250724Scgstatic int chn_removefeeder(pcm_channel *c); 3350724Scg 3450724Scg#define FEEDBUFSZ 8192 3550724Scg 3650724Scgstatic unsigned char ulaw_to_u8[] = { 3750724Scg 3, 7, 11, 15, 19, 23, 27, 31, 3850724Scg 35, 39, 43, 47, 51, 55, 59, 63, 3950724Scg 66, 68, 70, 72, 74, 76, 78, 80, 4050724Scg 82, 84, 86, 88, 90, 92, 94, 96, 4150724Scg 98, 99, 100, 101, 102, 103, 104, 105, 4250724Scg 106, 107, 108, 109, 110, 111, 112, 113, 4350724Scg 113, 114, 114, 115, 115, 116, 116, 117, 4450724Scg 117, 118, 118, 119, 119, 120, 120, 121, 4550724Scg 121, 121, 122, 122, 122, 122, 123, 123, 4650724Scg 123, 123, 124, 124, 124, 124, 125, 125, 4750724Scg 125, 125, 125, 125, 126, 126, 126, 126, 4850724Scg 126, 126, 126, 126, 127, 127, 127, 127, 4950724Scg 127, 127, 127, 127, 127, 127, 127, 127, 5050724Scg 128, 128, 128, 128, 128, 128, 128, 128, 5150724Scg 128, 128, 128, 128, 128, 128, 128, 128, 5250724Scg 128, 128, 128, 128, 128, 128, 128, 128, 5350724Scg 253, 249, 245, 241, 237, 233, 229, 225, 5450724Scg 221, 217, 213, 209, 205, 201, 197, 193, 5550724Scg 190, 188, 186, 184, 182, 180, 178, 176, 5650724Scg 174, 172, 170, 168, 166, 164, 162, 160, 5750724Scg 158, 157, 156, 155, 154, 153, 152, 151, 5850724Scg 150, 149, 148, 147, 146, 145, 144, 143, 5950724Scg 143, 142, 142, 141, 141, 140, 140, 139, 6050724Scg 139, 138, 138, 137, 137, 136, 136, 135, 6150724Scg 135, 135, 134, 134, 134, 134, 133, 133, 6250724Scg 133, 133, 132, 132, 132, 132, 131, 131, 6350724Scg 131, 131, 131, 131, 130, 130, 130, 130, 6450724Scg 130, 130, 130, 130, 129, 129, 129, 129, 6550724Scg 129, 129, 129, 129, 129, 129, 129, 129, 6650724Scg 128, 128, 128, 128, 128, 128, 128, 128, 6750724Scg 128, 128, 128, 128, 128, 128, 128, 128, 6850724Scg 128, 128, 128, 128, 128, 128, 128, 128, 6950724Scg}; 7050724Scg 7150724Scgstatic unsigned char u8_to_ulaw[] = { 7250724Scg 0, 0, 0, 0, 0, 1, 1, 1, 7350724Scg 1, 2, 2, 2, 2, 3, 3, 3, 7450724Scg 3, 4, 4, 4, 4, 5, 5, 5, 7550724Scg 5, 6, 6, 6, 6, 7, 7, 7, 7650724Scg 7, 8, 8, 8, 8, 9, 9, 9, 7750724Scg 9, 10, 10, 10, 10, 11, 11, 11, 7850724Scg 11, 12, 12, 12, 12, 13, 13, 13, 7950724Scg 13, 14, 14, 14, 14, 15, 15, 15, 8050724Scg 15, 16, 16, 17, 17, 18, 18, 19, 8150724Scg 19, 20, 20, 21, 21, 22, 22, 23, 8250724Scg 23, 24, 24, 25, 25, 26, 26, 27, 8350724Scg 27, 28, 28, 29, 29, 30, 30, 31, 8450724Scg 31, 32, 33, 34, 35, 36, 37, 38, 8550724Scg 39, 40, 41, 42, 43, 44, 45, 46, 8650724Scg 47, 49, 51, 53, 55, 57, 59, 61, 8750724Scg 63, 66, 70, 74, 78, 84, 92, 104, 8850724Scg 254, 231, 219, 211, 205, 201, 197, 193, 8950724Scg 190, 188, 186, 184, 182, 180, 178, 176, 9050724Scg 175, 174, 173, 172, 171, 170, 169, 168, 9150724Scg 167, 166, 165, 164, 163, 162, 161, 160, 9250724Scg 159, 159, 158, 158, 157, 157, 156, 156, 9350724Scg 155, 155, 154, 154, 153, 153, 152, 152, 9450724Scg 151, 151, 150, 150, 149, 149, 148, 148, 9550724Scg 147, 147, 146, 146, 145, 145, 144, 144, 9650724Scg 143, 143, 143, 143, 142, 142, 142, 142, 9750724Scg 141, 141, 141, 141, 140, 140, 140, 140, 9850724Scg 139, 139, 139, 139, 138, 138, 138, 138, 9950724Scg 137, 137, 137, 137, 136, 136, 136, 136, 10050724Scg 135, 135, 135, 135, 134, 134, 134, 134, 10150724Scg 133, 133, 133, 133, 132, 132, 132, 132, 10250724Scg 131, 131, 131, 131, 130, 130, 130, 130, 10350724Scg 129, 129, 129, 129, 128, 128, 128, 128, 10450724Scg}; 10550724Scg 10650724Scg/*****************************************************************************/ 10750724Scg 10850724Scgstatic int 10950724Scgfeed_root(pcm_feeder *feeder, u_int8_t *buffer, u_int32_t count, struct uio *stream) 11050724Scg{ 11150923Scg int ret, tmp = 0, c = 0; 11250724Scg if (!count) panic("feed_root: count == 0"); 11350923Scg while ((stream->uio_resid > 0) && (c < count)) { 11450923Scg tmp = stream->uio_resid; 11550923Scg ret = uiomove(buffer + c, count - c, stream); 11650923Scg if (ret) panic("feed_root: uiomove failed"); 11750923Scg tmp -= stream->uio_resid; 11850923Scg c += tmp; 11950923Scg } 12052713Stanimura if (!c) panic("feed_root: uiomove didn't"); 12152713Stanimura return c; 12250724Scg} 12351769Scgpcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root }; 12450724Scg 12550724Scg/*****************************************************************************/ 12650724Scg 12750724Scgstatic int 12850724Scgfeed_8to16(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 12950724Scg{ 13050724Scg int i, j, k; 13150724Scg k = f->source->feed(f->source, b, count / 2, stream); 13250724Scg j = k - 1; 13350724Scg i = j * 2 + 1; 13450724Scg while (i > 0 && j >= 0) { 13550724Scg b[i--] = b[j--]; 13650724Scg b[i--] = 0; 13750724Scg } 13850724Scg return k * 2; 13950724Scg} 14051769Scgstatic pcm_feeder feeder_8to16 = { "8to16", 0, NULL, NULL, feed_8to16 }; 14150724Scg 14250724Scg/*****************************************************************************/ 14350724Scg 14450724Scgstatic int 14550724Scgfeed_16to8_init(pcm_feeder *f) 14650724Scg{ 14750724Scg f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); 14850724Scg return (f->data == NULL); 14950724Scg} 15050724Scg 15150724Scgstatic int 15250724Scgfeed_16to8_free(pcm_feeder *f) 15350724Scg{ 15450724Scg if (f->data) free(f->data, M_DEVBUF); 15550724Scg f->data = NULL; 15650724Scg return 0; 15750724Scg} 15850724Scg 15950724Scgstatic int 16050724Scgfeed_16to8le(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 16150724Scg{ 16250724Scg u_int32_t i = 0, toget = count * 2; 16350724Scg int j = 1, k; 16450724Scg k = f->source->feed(f->source, f->data, min(toget, FEEDBUFSZ), stream); 16550724Scg while (j < k) { 16650724Scg b[i++] = ((u_int8_t *)f->data)[j]; 16750724Scg j += 2; 16850724Scg } 16950724Scg return i; 17050724Scg} 17150724Scgstatic pcm_feeder feeder_16to8le = 17251769Scg { "16to8le", 1, feed_16to8_init, feed_16to8_free, feed_16to8le }; 17350724Scg 17450724Scg/*****************************************************************************/ 17550724Scg 17650724Scgstatic int 17750724Scgfeed_monotostereo8(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 17850724Scg{ 17950724Scg int i, j, k = f->source->feed(f->source, b, count / 2, stream); 18050724Scg j = k - 1; 18150724Scg i = j * 2 + 1; 18250724Scg while (i > 0 && j >= 0) { 18350724Scg b[i--] = b[j]; 18450724Scg b[i--] = b[j]; 18550724Scg j--; 18650724Scg } 18750724Scg return k * 2; 18850724Scg} 18950724Scgstatic pcm_feeder feeder_monotostereo8 = 19051769Scg { "monotostereo8", 0, NULL, NULL, feed_monotostereo8 }; 19150724Scg 19250724Scg/*****************************************************************************/ 19350724Scg 19450724Scgstatic int 19550724Scgfeed_stereotomono8_init(pcm_feeder *f) 19650724Scg{ 19750724Scg f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); 19850724Scg return (f->data == NULL); 19950724Scg} 20050724Scg 20150724Scgstatic int 20250724Scgfeed_stereotomono8_free(pcm_feeder *f) 20350724Scg{ 20450724Scg if (f->data) free(f->data, M_DEVBUF); 20550724Scg f->data = NULL; 20650724Scg return 0; 20750724Scg} 20850724Scg 20950724Scgstatic int 21050724Scgfeed_stereotomono8(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 21150724Scg{ 21250724Scg u_int32_t i = 0, toget = count * 2; 21350724Scg int j = 0, k; 21450724Scg k = f->source->feed(f->source, f->data, min(toget, FEEDBUFSZ), stream); 21550724Scg while (j < k) { 21650724Scg b[i++] = ((u_int8_t *)f->data)[j]; 21750724Scg j += 2; 21850724Scg } 21950724Scg return i; 22050724Scg} 22150724Scgstatic pcm_feeder feeder_stereotomono8 = 22251769Scg { "stereotomono8", 1, feed_stereotomono8_init, feed_stereotomono8_free, 22350724Scg feed_stereotomono8 }; 22450724Scg 22550724Scg/*****************************************************************************/ 22650724Scg 22750724Scgstatic int 22850724Scgfeed_endian(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 22950724Scg{ 23050724Scg u_int8_t t; 23150724Scg int i = 0, j = f->source->feed(f->source, b, count, stream); 23250724Scg while (i < j) { 23350724Scg t = b[i]; 23450724Scg b[i] = b[i + 1]; 23550724Scg b[i + 1] = t; 23650724Scg i += 2; 23750724Scg } 23850923Scg return i; 23950724Scg} 24051769Scgstatic pcm_feeder feeder_endian = { "endian", -1, NULL, NULL, feed_endian }; 24150724Scg 24250724Scg/*****************************************************************************/ 24350724Scg 24450724Scgstatic int 24550724Scgfeed_sign(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 24650724Scg{ 24750724Scg int i = 0, j = f->source->feed(f->source, b, count, stream); 24850724Scg int ssz = (int)f->data, ofs = ssz - 1; 24950724Scg while (i < j) { 25050724Scg b[i + ofs] ^= 0x80; 25150724Scg i += ssz; 25250724Scg } 25350724Scg return i; 25450724Scg} 25550724Scgstatic pcm_feeder feeder_sign8 = 25651769Scg { "sign8", 0, NULL, NULL, feed_sign, (void *)1 }; 25750724Scgstatic pcm_feeder feeder_sign16 = 25851769Scg { "sign16", -1, NULL, NULL, feed_sign, (void *)2 }; 25950724Scg 26050724Scg/*****************************************************************************/ 26150724Scg 26250724Scgstatic int 26350724Scgfeed_table(pcm_feeder *f, u_int8_t *b, u_int32_t count, struct uio *stream) 26450724Scg{ 26550724Scg int i = 0, j = f->source->feed(f->source, b, count, stream); 26650724Scg while (i < j) { 26750724Scg b[i] = ((u_int8_t *)f->data)[b[i]]; 26850724Scg i++; 26950724Scg } 27050724Scg return i; 27150724Scg} 27250724Scgstatic pcm_feeder feeder_ulawtou8 = 27351769Scg { "ulawtou8", 0, NULL, NULL, feed_table, ulaw_to_u8 }; 27450724Scgstatic pcm_feeder feeder_u8toulaw = 27551769Scg { "u8toulaw", 0, NULL, NULL, feed_table, u8_to_ulaw }; 27650724Scg 27750724Scg/*****************************************************************************/ 27850724Scg 27950724Scgstruct fmtspec { 28050724Scg int stereo; 28150724Scg int sign; 28250724Scg int bit16; 28350724Scg int bigendian; 28450724Scg int ulaw; 28550724Scg int bad; 28650724Scg}; 28750724Scg 28850724Scgstruct fmtcvt { 28950724Scg pcm_feeder *f; 29050724Scg struct fmtspec ispec, ospec; 29150724Scg}; 29250724Scg 29350724Scgstruct fmtcvt cvttab[] = { 29450724Scg {&feeder_ulawtou8, {-1, 0, 0, 0, 1}, {-1, 0, 0, 0, 0}}, 29550724Scg {&feeder_u8toulaw, {-1, 0, 0, 0, 0}, {-1, 0, 0, 0, 1}}, 29650724Scg {&feeder_sign8, {-1, 0, 0, 0, 0}, {-1, 1, 0, 0, 0}}, 29750724Scg {&feeder_sign8, {-1, 1, 0, 0, 0}, {-1, 0, 0, 0, 0}}, 29850724Scg {&feeder_monotostereo8, { 0, -1, 0, 0, -1}, { 1, -1, 0, 0, -1}}, 29950724Scg {&feeder_stereotomono8, { 1, -1, 0, 0, -1}, { 0, -1, 0, 0, -1}}, 30050724Scg {&feeder_sign16, {-1, 0, 1, 0, 0}, {-1, 1, 1, 0, 0}}, 30150724Scg {&feeder_sign16, {-1, 1, 1, 0, 0}, {-1, 0, 1, 0, 0}}, 30250724Scg {&feeder_8to16, {-1, -1, 0, 0, 0}, {-1, -1, 1, 0, 0}}, 30350724Scg {&feeder_16to8le, {-1, -1, 1, 0, 0}, {-1, -1, 0, 0, 0}}, 30450724Scg {&feeder_endian, {-1, -1, 1, 0, 0}, {-1, -1, 1, 1, 0}}, 30550724Scg {&feeder_endian, {-1, -1, 1, 1, 0}, {-1, -1, 1, 0, 0}}, 30650724Scg}; 30750724Scg#define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt)) 30850724Scg 30950724Scgstatic int 31050724Scggetspec(u_int32_t fmt, struct fmtspec *spec) 31150724Scg{ 31250724Scg spec->stereo = (fmt & AFMT_STEREO)? 1 : 0; 31350724Scg spec->sign = (fmt & AFMT_SIGNED)? 1 : 0; 31450724Scg spec->bit16 = (fmt & AFMT_16BIT)? 1 : 0; 31550724Scg spec->bigendian = (fmt & AFMT_BIGENDIAN)? 1 : 0; 31650724Scg spec->ulaw = (fmt & AFMT_MU_LAW)? 1 : 0; 31750724Scg spec->bad = (fmt & (AFMT_A_LAW | AFMT_MPEG))? 1 : 0; 31850724Scg return 0; 31950724Scg} 32050724Scg 32150724Scgstatic int 32250724Scgcmp(int x, int y) 32350724Scg{ 32450724Scg return (x == -1 || x == y || y == -1)? 1 : 0; 32550724Scg} 32650724Scg 32750724Scgstatic int 32850724Scgcmpspec(struct fmtspec *x, struct fmtspec *y) 32950724Scg{ 33050724Scg int i = 0; 33150724Scg if (cmp(x->stereo, y->stereo)) i |= 0x01; 33250724Scg if (cmp(x->sign, y->sign)) i |= 0x02; 33350724Scg if (cmp(x->bit16, y->bit16)) i |= 0x04; 33450724Scg if (cmp(x->bigendian, y->bigendian)) i |= 0x08; 33550724Scg if (cmp(x->ulaw, y->ulaw)) i |= 0x10; 33650724Scg return i; 33750724Scg} 33850724Scg 33950724Scgstatic int 34050724Scgcvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s) 34150724Scg{ 34250724Scg int i = cmpspec(s, &cvt->ospec); 34350724Scg chn_addfeeder(c, cvt->f); 34450724Scg if (cvt->ospec.stereo != -1) s->stereo = cvt->ospec.stereo; 34550724Scg if (cvt->ospec.sign != -1) s->sign = cvt->ospec.sign; 34650724Scg if (cvt->ospec.bit16 != -1) s->bit16 = cvt->ospec.bit16; 34750724Scg if (cvt->ospec.bigendian != -1) s->bigendian = cvt->ospec.bigendian; 34850724Scg if (cvt->ospec.ulaw != -1) s->ulaw = cvt->ospec.ulaw; 34950724Scg return i; 35050724Scg} 35150724Scg 35250724Scgint 35350724Scgchn_feedchain(pcm_channel *c) 35450724Scg{ 35550724Scg int i, chosen, iter; 35650724Scg u_int32_t mask; 35750724Scg struct fmtspec s, t; 35850724Scg struct fmtcvt *e; 35950724Scg 36050724Scg while (chn_removefeeder(c) != -1); 36151769Scg c->align = 0; 36250724Scg if ((c->format & chn_getcaps(c)->formats) == c->format) 36350724Scg return c->format; 36450724Scg getspec(c->format, &s); 36550724Scg if (s.bad) return -1; 36650724Scg getspec(chn_getcaps(c)->bestfmt, &t); 36750724Scg mask = (~cmpspec(&s, &t)) & 0x1f; 36850724Scg iter = 0; 36950724Scg do { 37050724Scg if (mask == 0 || iter >= 8) break; 37150724Scg chosen = -1; 37250724Scg for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) { 37350724Scg e = &cvttab[i]; 37450724Scg if ((cmpspec(&s, &e->ispec) == 0x1f) && 37550724Scg ((~cmpspec(&e->ispec, &e->ospec)) & mask)) 37650724Scg chosen = i; 37750724Scg } 37850724Scg if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s); 37950724Scg iter++; 38050724Scg } while (chosen != -1); 38150724Scg return (iter < 8)? chn_getcaps(c)->bestfmt : -1; 38250724Scg} 38350724Scg 38450724Scgstatic int 38550724Scgchn_addfeeder(pcm_channel *c, pcm_feeder *f) 38650724Scg{ 38750724Scg pcm_feeder *n; 38850724Scg n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT); 38950724Scg *n = *f; 39050724Scg n->source = c->feeder; 39150724Scg c->feeder = n; 39250724Scg if (n->init) n->init(n); 39351769Scg if (n->align > 0) c->align += n->align; 39451769Scg else if (n->align < 0 && c->align < -n->align) c->align -= n->align; 39550724Scg return 0; 39650724Scg} 39750724Scg 39850724Scgstatic int 39950724Scgchn_removefeeder(pcm_channel *c) 40050724Scg{ 40150724Scg pcm_feeder *f; 40250724Scg if (c->feeder == &feeder_root) return -1; 40350724Scg f = c->feeder->source; 40450724Scg if (c->feeder->free) c->feeder->free(c->feeder); 40550724Scg free(c->feeder, M_DEVBUF); 40650724Scg c->feeder = f; 40750724Scg return 0; 40850724Scg} 40950724Scg 410