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