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