feeder.c revision 53465
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 53465 1999-11-20 16:50:33Z cg $ 2750724Scg */ 2850724Scg 2953465Scg#include <dev/sound/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 10953205Scgfeed_root(pcm_feeder *feeder, pcm_channel *ch, 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"); 11353205Scg count &= ~((1 << ch->align) - 1); 11453205Scg if (!count) panic("feed_root: aligned count == 0"); 11553205Scg if (ch->smegcnt > 0) { 11653205Scg c = min(ch->smegcnt, count); 11753205Scg bcopy(ch->smegbuf, buffer, c); 11853205Scg ch->smegcnt -= c; 11953205Scg } 12050923Scg while ((stream->uio_resid > 0) && (c < count)) { 12150923Scg tmp = stream->uio_resid; 12250923Scg ret = uiomove(buffer + c, count - c, stream); 12350923Scg if (ret) panic("feed_root: uiomove failed"); 12450923Scg tmp -= stream->uio_resid; 12550923Scg c += tmp; 12650923Scg } 12752713Stanimura if (!c) panic("feed_root: uiomove didn't"); 12852713Stanimura return c; 12950724Scg} 13051769Scgpcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root }; 13150724Scg 13250724Scg/*****************************************************************************/ 13350724Scg 13450724Scgstatic int 13553205Scgfeed_8to16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) 13650724Scg{ 13750724Scg int i, j, k; 13853205Scg k = f->source->feed(f->source, c, b, count / 2, stream); 13950724Scg j = k - 1; 14050724Scg i = j * 2 + 1; 14150724Scg while (i > 0 && j >= 0) { 14250724Scg b[i--] = b[j--]; 14350724Scg b[i--] = 0; 14450724Scg } 14550724Scg return k * 2; 14650724Scg} 14751769Scgstatic pcm_feeder feeder_8to16 = { "8to16", 0, NULL, NULL, feed_8to16 }; 14850724Scg 14950724Scg/*****************************************************************************/ 15050724Scg 15150724Scgstatic int 15250724Scgfeed_16to8_init(pcm_feeder *f) 15350724Scg{ 15450724Scg f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); 15550724Scg return (f->data == NULL); 15650724Scg} 15750724Scg 15850724Scgstatic int 15950724Scgfeed_16to8_free(pcm_feeder *f) 16050724Scg{ 16150724Scg if (f->data) free(f->data, M_DEVBUF); 16250724Scg f->data = NULL; 16350724Scg return 0; 16450724Scg} 16550724Scg 16650724Scgstatic int 16753205Scgfeed_16to8le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) 16850724Scg{ 16950724Scg u_int32_t i = 0, toget = count * 2; 17050724Scg int j = 1, k; 17153205Scg k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); 17250724Scg while (j < k) { 17350724Scg b[i++] = ((u_int8_t *)f->data)[j]; 17450724Scg j += 2; 17550724Scg } 17650724Scg return i; 17750724Scg} 17850724Scgstatic pcm_feeder feeder_16to8le = 17951769Scg { "16to8le", 1, feed_16to8_init, feed_16to8_free, feed_16to8le }; 18050724Scg 18150724Scg/*****************************************************************************/ 18250724Scg 18350724Scgstatic int 18453205Scgfeed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) 18550724Scg{ 18653205Scg int i, j, k = f->source->feed(f->source, c, b, count / 2, stream); 18750724Scg j = k - 1; 18850724Scg i = j * 2 + 1; 18950724Scg while (i > 0 && j >= 0) { 19050724Scg b[i--] = b[j]; 19150724Scg b[i--] = b[j]; 19250724Scg j--; 19350724Scg } 19450724Scg return k * 2; 19550724Scg} 19650724Scgstatic pcm_feeder feeder_monotostereo8 = 19751769Scg { "monotostereo8", 0, NULL, NULL, feed_monotostereo8 }; 19850724Scg 19950724Scg/*****************************************************************************/ 20050724Scg 20150724Scgstatic int 20250724Scgfeed_stereotomono8_init(pcm_feeder *f) 20350724Scg{ 20450724Scg f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); 20550724Scg return (f->data == NULL); 20650724Scg} 20750724Scg 20850724Scgstatic int 20950724Scgfeed_stereotomono8_free(pcm_feeder *f) 21050724Scg{ 21150724Scg if (f->data) free(f->data, M_DEVBUF); 21250724Scg f->data = NULL; 21350724Scg return 0; 21450724Scg} 21550724Scg 21650724Scgstatic int 21753205Scgfeed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) 21850724Scg{ 21950724Scg u_int32_t i = 0, toget = count * 2; 22050724Scg int j = 0, k; 22153205Scg k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream); 22250724Scg while (j < k) { 22350724Scg b[i++] = ((u_int8_t *)f->data)[j]; 22450724Scg j += 2; 22550724Scg } 22650724Scg return i; 22750724Scg} 22850724Scgstatic pcm_feeder feeder_stereotomono8 = 22951769Scg { "stereotomono8", 1, feed_stereotomono8_init, feed_stereotomono8_free, 23050724Scg feed_stereotomono8 }; 23150724Scg 23250724Scg/*****************************************************************************/ 23350724Scg 23450724Scgstatic int 23553205Scgfeed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) 23650724Scg{ 23750724Scg u_int8_t t; 23853205Scg int i = 0, j = f->source->feed(f->source, c, b, count, stream); 23950724Scg while (i < j) { 24050724Scg t = b[i]; 24150724Scg b[i] = b[i + 1]; 24250724Scg b[i + 1] = t; 24350724Scg i += 2; 24450724Scg } 24550923Scg return i; 24650724Scg} 24751769Scgstatic pcm_feeder feeder_endian = { "endian", -1, NULL, NULL, feed_endian }; 24850724Scg 24950724Scg/*****************************************************************************/ 25050724Scg 25150724Scgstatic int 25253205Scgfeed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) 25350724Scg{ 25453205Scg int i = 0, j = f->source->feed(f->source, c, b, count, stream); 25550724Scg int ssz = (int)f->data, ofs = ssz - 1; 25650724Scg while (i < j) { 25750724Scg b[i + ofs] ^= 0x80; 25850724Scg i += ssz; 25950724Scg } 26050724Scg return i; 26150724Scg} 26250724Scgstatic pcm_feeder feeder_sign8 = 26351769Scg { "sign8", 0, NULL, NULL, feed_sign, (void *)1 }; 26450724Scgstatic pcm_feeder feeder_sign16 = 26551769Scg { "sign16", -1, NULL, NULL, feed_sign, (void *)2 }; 26650724Scg 26750724Scg/*****************************************************************************/ 26850724Scg 26950724Scgstatic int 27053205Scgfeed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream) 27150724Scg{ 27253205Scg int i = 0, j = f->source->feed(f->source, c, b, count, stream); 27350724Scg while (i < j) { 27450724Scg b[i] = ((u_int8_t *)f->data)[b[i]]; 27550724Scg i++; 27650724Scg } 27750724Scg return i; 27850724Scg} 27950724Scgstatic pcm_feeder feeder_ulawtou8 = 28051769Scg { "ulawtou8", 0, NULL, NULL, feed_table, ulaw_to_u8 }; 28150724Scgstatic pcm_feeder feeder_u8toulaw = 28251769Scg { "u8toulaw", 0, NULL, NULL, feed_table, u8_to_ulaw }; 28350724Scg 28450724Scg/*****************************************************************************/ 28550724Scg 28650724Scgstruct fmtspec { 28750724Scg int stereo; 28850724Scg int sign; 28950724Scg int bit16; 29050724Scg int bigendian; 29150724Scg int ulaw; 29250724Scg int bad; 29350724Scg}; 29450724Scg 29550724Scgstruct fmtcvt { 29650724Scg pcm_feeder *f; 29750724Scg struct fmtspec ispec, ospec; 29850724Scg}; 29950724Scg 30050724Scgstruct fmtcvt cvttab[] = { 30150724Scg {&feeder_ulawtou8, {-1, 0, 0, 0, 1}, {-1, 0, 0, 0, 0}}, 30250724Scg {&feeder_u8toulaw, {-1, 0, 0, 0, 0}, {-1, 0, 0, 0, 1}}, 30350724Scg {&feeder_sign8, {-1, 0, 0, 0, 0}, {-1, 1, 0, 0, 0}}, 30450724Scg {&feeder_sign8, {-1, 1, 0, 0, 0}, {-1, 0, 0, 0, 0}}, 30550724Scg {&feeder_monotostereo8, { 0, -1, 0, 0, -1}, { 1, -1, 0, 0, -1}}, 30650724Scg {&feeder_stereotomono8, { 1, -1, 0, 0, -1}, { 0, -1, 0, 0, -1}}, 30750724Scg {&feeder_sign16, {-1, 0, 1, 0, 0}, {-1, 1, 1, 0, 0}}, 30850724Scg {&feeder_sign16, {-1, 1, 1, 0, 0}, {-1, 0, 1, 0, 0}}, 30950724Scg {&feeder_8to16, {-1, -1, 0, 0, 0}, {-1, -1, 1, 0, 0}}, 31050724Scg {&feeder_16to8le, {-1, -1, 1, 0, 0}, {-1, -1, 0, 0, 0}}, 31150724Scg {&feeder_endian, {-1, -1, 1, 0, 0}, {-1, -1, 1, 1, 0}}, 31250724Scg {&feeder_endian, {-1, -1, 1, 1, 0}, {-1, -1, 1, 0, 0}}, 31350724Scg}; 31450724Scg#define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt)) 31550724Scg 31650724Scgstatic int 31750724Scggetspec(u_int32_t fmt, struct fmtspec *spec) 31850724Scg{ 31950724Scg spec->stereo = (fmt & AFMT_STEREO)? 1 : 0; 32050724Scg spec->sign = (fmt & AFMT_SIGNED)? 1 : 0; 32150724Scg spec->bit16 = (fmt & AFMT_16BIT)? 1 : 0; 32250724Scg spec->bigendian = (fmt & AFMT_BIGENDIAN)? 1 : 0; 32350724Scg spec->ulaw = (fmt & AFMT_MU_LAW)? 1 : 0; 32450724Scg spec->bad = (fmt & (AFMT_A_LAW | AFMT_MPEG))? 1 : 0; 32550724Scg return 0; 32650724Scg} 32750724Scg 32850724Scgstatic int 32950724Scgcmp(int x, int y) 33050724Scg{ 33150724Scg return (x == -1 || x == y || y == -1)? 1 : 0; 33250724Scg} 33350724Scg 33450724Scgstatic int 33550724Scgcmpspec(struct fmtspec *x, struct fmtspec *y) 33650724Scg{ 33750724Scg int i = 0; 33850724Scg if (cmp(x->stereo, y->stereo)) i |= 0x01; 33950724Scg if (cmp(x->sign, y->sign)) i |= 0x02; 34050724Scg if (cmp(x->bit16, y->bit16)) i |= 0x04; 34150724Scg if (cmp(x->bigendian, y->bigendian)) i |= 0x08; 34250724Scg if (cmp(x->ulaw, y->ulaw)) i |= 0x10; 34350724Scg return i; 34450724Scg} 34550724Scg 34650724Scgstatic int 34750724Scgcvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s) 34850724Scg{ 34950724Scg int i = cmpspec(s, &cvt->ospec); 35050724Scg chn_addfeeder(c, cvt->f); 35150724Scg if (cvt->ospec.stereo != -1) s->stereo = cvt->ospec.stereo; 35250724Scg if (cvt->ospec.sign != -1) s->sign = cvt->ospec.sign; 35350724Scg if (cvt->ospec.bit16 != -1) s->bit16 = cvt->ospec.bit16; 35450724Scg if (cvt->ospec.bigendian != -1) s->bigendian = cvt->ospec.bigendian; 35550724Scg if (cvt->ospec.ulaw != -1) s->ulaw = cvt->ospec.ulaw; 35650724Scg return i; 35750724Scg} 35850724Scg 35950724Scgint 36050724Scgchn_feedchain(pcm_channel *c) 36150724Scg{ 36250724Scg int i, chosen, iter; 36350724Scg u_int32_t mask; 36450724Scg struct fmtspec s, t; 36550724Scg struct fmtcvt *e; 36650724Scg 36750724Scg while (chn_removefeeder(c) != -1); 36851769Scg c->align = 0; 36950724Scg if ((c->format & chn_getcaps(c)->formats) == c->format) 37050724Scg return c->format; 37150724Scg getspec(c->format, &s); 37250724Scg if (s.bad) return -1; 37350724Scg getspec(chn_getcaps(c)->bestfmt, &t); 37450724Scg mask = (~cmpspec(&s, &t)) & 0x1f; 37550724Scg iter = 0; 37650724Scg do { 37750724Scg if (mask == 0 || iter >= 8) break; 37850724Scg chosen = -1; 37950724Scg for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) { 38050724Scg e = &cvttab[i]; 38150724Scg if ((cmpspec(&s, &e->ispec) == 0x1f) && 38250724Scg ((~cmpspec(&e->ispec, &e->ospec)) & mask)) 38350724Scg chosen = i; 38450724Scg } 38550724Scg if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s); 38650724Scg iter++; 38750724Scg } while (chosen != -1); 38850724Scg return (iter < 8)? chn_getcaps(c)->bestfmt : -1; 38950724Scg} 39050724Scg 39150724Scgstatic int 39250724Scgchn_addfeeder(pcm_channel *c, pcm_feeder *f) 39350724Scg{ 39450724Scg pcm_feeder *n; 39550724Scg n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT); 39650724Scg *n = *f; 39750724Scg n->source = c->feeder; 39850724Scg c->feeder = n; 39950724Scg if (n->init) n->init(n); 40051769Scg if (n->align > 0) c->align += n->align; 40151769Scg else if (n->align < 0 && c->align < -n->align) c->align -= n->align; 40250724Scg return 0; 40350724Scg} 40450724Scg 40550724Scgstatic int 40650724Scgchn_removefeeder(pcm_channel *c) 40750724Scg{ 40850724Scg pcm_feeder *f; 40950724Scg if (c->feeder == &feeder_root) return -1; 41050724Scg f = c->feeder->source; 41150724Scg if (c->feeder->free) c->feeder->free(c->feeder); 41250724Scg free(c->feeder, M_DEVBUF); 41350724Scg c->feeder = f; 41450724Scg return 0; 41550724Scg} 41650724Scg 417