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