feeder.c revision 53205
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/dev/sound/pcm/feeder.c 53205 1999-11-15 23:57:33Z cg $
27 */
28
29#include <dev/pcm/sound.h>
30
31static int chn_addfeeder(pcm_channel *c, pcm_feeder *f);
32static int chn_removefeeder(pcm_channel *c);
33
34#define FEEDBUFSZ	8192
35
36static unsigned char ulaw_to_u8[] = {
37     3,    7,   11,   15,   19,   23,   27,   31,
38    35,   39,   43,   47,   51,   55,   59,   63,
39    66,   68,   70,   72,   74,   76,   78,   80,
40    82,   84,   86,   88,   90,   92,   94,   96,
41    98,   99,  100,  101,  102,  103,  104,  105,
42   106,  107,  108,  109,  110,  111,  112,  113,
43   113,  114,  114,  115,  115,  116,  116,  117,
44   117,  118,  118,  119,  119,  120,  120,  121,
45   121,  121,  122,  122,  122,  122,  123,  123,
46   123,  123,  124,  124,  124,  124,  125,  125,
47   125,  125,  125,  125,  126,  126,  126,  126,
48   126,  126,  126,  126,  127,  127,  127,  127,
49   127,  127,  127,  127,  127,  127,  127,  127,
50   128,  128,  128,  128,  128,  128,  128,  128,
51   128,  128,  128,  128,  128,  128,  128,  128,
52   128,  128,  128,  128,  128,  128,  128,  128,
53   253,  249,  245,  241,  237,  233,  229,  225,
54   221,  217,  213,  209,  205,  201,  197,  193,
55   190,  188,  186,  184,  182,  180,  178,  176,
56   174,  172,  170,  168,  166,  164,  162,  160,
57   158,  157,  156,  155,  154,  153,  152,  151,
58   150,  149,  148,  147,  146,  145,  144,  143,
59   143,  142,  142,  141,  141,  140,  140,  139,
60   139,  138,  138,  137,  137,  136,  136,  135,
61   135,  135,  134,  134,  134,  134,  133,  133,
62   133,  133,  132,  132,  132,  132,  131,  131,
63   131,  131,  131,  131,  130,  130,  130,  130,
64   130,  130,  130,  130,  129,  129,  129,  129,
65   129,  129,  129,  129,  129,  129,  129,  129,
66   128,  128,  128,  128,  128,  128,  128,  128,
67   128,  128,  128,  128,  128,  128,  128,  128,
68   128,  128,  128,  128,  128,  128,  128,  128,
69};
70
71static unsigned char u8_to_ulaw[] = {
72     0,    0,    0,    0,    0,    1,    1,    1,
73     1,    2,    2,    2,    2,    3,    3,    3,
74     3,    4,    4,    4,    4,    5,    5,    5,
75     5,    6,    6,    6,    6,    7,    7,    7,
76     7,    8,    8,    8,    8,    9,    9,    9,
77     9,   10,   10,   10,   10,   11,   11,   11,
78    11,   12,   12,   12,   12,   13,   13,   13,
79    13,   14,   14,   14,   14,   15,   15,   15,
80    15,   16,   16,   17,   17,   18,   18,   19,
81    19,   20,   20,   21,   21,   22,   22,   23,
82    23,   24,   24,   25,   25,   26,   26,   27,
83    27,   28,   28,   29,   29,   30,   30,   31,
84    31,   32,   33,   34,   35,   36,   37,   38,
85    39,   40,   41,   42,   43,   44,   45,   46,
86    47,   49,   51,   53,   55,   57,   59,   61,
87    63,   66,   70,   74,   78,   84,   92,  104,
88   254,  231,  219,  211,  205,  201,  197,  193,
89   190,  188,  186,  184,  182,  180,  178,  176,
90   175,  174,  173,  172,  171,  170,  169,  168,
91   167,  166,  165,  164,  163,  162,  161,  160,
92   159,  159,  158,  158,  157,  157,  156,  156,
93   155,  155,  154,  154,  153,  153,  152,  152,
94   151,  151,  150,  150,  149,  149,  148,  148,
95   147,  147,  146,  146,  145,  145,  144,  144,
96   143,  143,  143,  143,  142,  142,  142,  142,
97   141,  141,  141,  141,  140,  140,  140,  140,
98   139,  139,  139,  139,  138,  138,  138,  138,
99   137,  137,  137,  137,  136,  136,  136,  136,
100   135,  135,  135,  135,  134,  134,  134,  134,
101   133,  133,  133,  133,  132,  132,  132,  132,
102   131,  131,  131,  131,  130,  130,  130,  130,
103   129,  129,  129,  129,  128,  128,  128,  128,
104};
105
106/*****************************************************************************/
107
108static int
109feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream)
110{
111	int ret, tmp = 0, c = 0;
112	if (!count) panic("feed_root: count == 0");
113	count &= ~((1 << ch->align) - 1);
114	if (!count) panic("feed_root: aligned count == 0");
115	if (ch->smegcnt > 0) {
116		c = min(ch->smegcnt, count);
117		bcopy(ch->smegbuf, buffer, c);
118		ch->smegcnt -= c;
119	}
120	while ((stream->uio_resid > 0) && (c < count)) {
121		tmp = stream->uio_resid;
122		ret = uiomove(buffer + c, count - c, stream);
123		if (ret) panic("feed_root: uiomove failed");
124		tmp -= stream->uio_resid;
125		c += tmp;
126	}
127	if (!c) panic("feed_root: uiomove didn't");
128	return c;
129}
130pcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root };
131
132/*****************************************************************************/
133
134static int
135feed_8to16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
136{
137	int i, j, k;
138	k = f->source->feed(f->source, c, b, count / 2, stream);
139	j = k - 1;
140	i = j * 2 + 1;
141	while (i > 0 && j >= 0) {
142		b[i--] = b[j--];
143		b[i--] = 0;
144	}
145	return k * 2;
146}
147static pcm_feeder feeder_8to16 = { "8to16", 0, NULL, NULL, feed_8to16 };
148
149/*****************************************************************************/
150
151static int
152feed_16to8_init(pcm_feeder *f)
153{
154	f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
155	return (f->data == NULL);
156}
157
158static int
159feed_16to8_free(pcm_feeder *f)
160{
161	if (f->data) free(f->data, M_DEVBUF);
162	f->data = NULL;
163	return 0;
164}
165
166static int
167feed_16to8le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
168{
169	u_int32_t i = 0, toget = count * 2;
170	int j = 1, k;
171	k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
172	while (j < k) {
173		b[i++] = ((u_int8_t *)f->data)[j];
174		j += 2;
175	}
176	return i;
177}
178static pcm_feeder feeder_16to8le =
179	{ "16to8le", 1, feed_16to8_init, feed_16to8_free, feed_16to8le };
180
181/*****************************************************************************/
182
183static int
184feed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
185{
186	int i, j, k = f->source->feed(f->source, c, b, count / 2, stream);
187	j = k - 1;
188	i = j * 2 + 1;
189	while (i > 0 && j >= 0) {
190		b[i--] = b[j];
191		b[i--] = b[j];
192		j--;
193	}
194	return k * 2;
195}
196static pcm_feeder feeder_monotostereo8 =
197	{ "monotostereo8", 0, NULL, NULL, feed_monotostereo8 };
198
199/*****************************************************************************/
200
201static int
202feed_stereotomono8_init(pcm_feeder *f)
203{
204	f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
205	return (f->data == NULL);
206}
207
208static int
209feed_stereotomono8_free(pcm_feeder *f)
210{
211	if (f->data) free(f->data, M_DEVBUF);
212	f->data = NULL;
213	return 0;
214}
215
216static int
217feed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
218{
219	u_int32_t i = 0, toget = count * 2;
220	int j = 0, k;
221	k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
222	while (j < k) {
223		b[i++] = ((u_int8_t *)f->data)[j];
224		j += 2;
225	}
226	return i;
227}
228static pcm_feeder feeder_stereotomono8 =
229	{ "stereotomono8", 1, feed_stereotomono8_init, feed_stereotomono8_free,
230	feed_stereotomono8 };
231
232/*****************************************************************************/
233
234static int
235feed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
236{
237	u_int8_t t;
238	int i = 0, j = f->source->feed(f->source, c, b, count, stream);
239	while (i < j) {
240		t = b[i];
241		b[i] = b[i + 1];
242		b[i + 1] = t;
243		i += 2;
244	}
245	return i;
246}
247static pcm_feeder feeder_endian = { "endian", -1, NULL, NULL, feed_endian };
248
249/*****************************************************************************/
250
251static int
252feed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
253{
254	int i = 0, j = f->source->feed(f->source, c, b, count, stream);
255	int ssz = (int)f->data, ofs = ssz - 1;
256	while (i < j) {
257		b[i + ofs] ^= 0x80;
258		i += ssz;
259	}
260	return i;
261}
262static pcm_feeder feeder_sign8 =
263	{ "sign8", 0, NULL, NULL, feed_sign, (void *)1 };
264static pcm_feeder feeder_sign16 =
265	{ "sign16", -1, NULL, NULL, feed_sign, (void *)2 };
266
267/*****************************************************************************/
268
269static int
270feed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
271{
272	int i = 0, j = f->source->feed(f->source, c, b, count, stream);
273	while (i < j) {
274		b[i] = ((u_int8_t *)f->data)[b[i]];
275		i++;
276	}
277	return i;
278}
279static pcm_feeder feeder_ulawtou8 =
280	{ "ulawtou8", 0, NULL, NULL, feed_table, ulaw_to_u8 };
281static pcm_feeder feeder_u8toulaw =
282	{ "u8toulaw", 0, NULL, NULL, feed_table, u8_to_ulaw };
283
284/*****************************************************************************/
285
286struct fmtspec {
287	int stereo;
288	int sign;
289	int bit16;
290	int bigendian;
291	int ulaw;
292	int bad;
293};
294
295struct fmtcvt {
296	pcm_feeder *f;
297	struct fmtspec ispec, ospec;
298};
299
300struct fmtcvt cvttab[] = {
301	{&feeder_ulawtou8, 	{-1,  0, 0, 0,  1}, 	{-1,  0, 0, 0,  0}},
302	{&feeder_u8toulaw, 	{-1,  0, 0, 0,  0}, 	{-1,  0, 0, 0,  1}},
303	{&feeder_sign8,		{-1,  0, 0, 0,  0},	{-1,  1, 0, 0,  0}},
304	{&feeder_sign8,		{-1,  1, 0, 0,  0},	{-1,  0, 0, 0,  0}},
305	{&feeder_monotostereo8,	{ 0, -1, 0, 0, -1},	{ 1, -1, 0, 0, -1}},
306	{&feeder_stereotomono8, { 1, -1, 0, 0, -1},	{ 0, -1, 0, 0, -1}},
307	{&feeder_sign16,	{-1,  0, 1, 0,  0},	{-1,  1, 1, 0,  0}},
308	{&feeder_sign16,	{-1,  1, 1, 0,  0},	{-1,  0, 1, 0,  0}},
309	{&feeder_8to16,		{-1, -1, 0, 0,  0},	{-1, -1, 1, 0,  0}},
310	{&feeder_16to8le,	{-1, -1, 1, 0,  0},	{-1, -1, 0, 0,  0}},
311	{&feeder_endian,	{-1, -1, 1, 0,  0},	{-1, -1, 1, 1,  0}},
312	{&feeder_endian,	{-1, -1, 1, 1,  0},	{-1, -1, 1, 0,  0}},
313};
314#define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt))
315
316static int
317getspec(u_int32_t fmt, struct fmtspec *spec)
318{
319	spec->stereo = (fmt & AFMT_STEREO)? 1 : 0;
320	spec->sign = (fmt & AFMT_SIGNED)? 1 : 0;
321	spec->bit16 = (fmt & AFMT_16BIT)? 1 : 0;
322	spec->bigendian = (fmt & AFMT_BIGENDIAN)? 1 : 0;
323	spec->ulaw = (fmt & AFMT_MU_LAW)? 1 : 0;
324	spec->bad = (fmt & (AFMT_A_LAW | AFMT_MPEG))? 1 : 0;
325	return 0;
326}
327
328static int
329cmp(int x, int y)
330{
331	return (x == -1 || x == y || y == -1)? 1 : 0;
332}
333
334static int
335cmpspec(struct fmtspec *x, struct fmtspec *y)
336{
337	int i = 0;
338	if (cmp(x->stereo, y->stereo)) i |= 0x01;
339	if (cmp(x->sign, y->sign)) i |= 0x02;
340	if (cmp(x->bit16, y->bit16)) i |= 0x04;
341	if (cmp(x->bigendian, y->bigendian)) i |= 0x08;
342	if (cmp(x->ulaw, y->ulaw)) i |= 0x10;
343	return i;
344}
345
346static int
347cvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s)
348{
349	int i = cmpspec(s, &cvt->ospec);
350	chn_addfeeder(c, cvt->f);
351	if (cvt->ospec.stereo != -1) s->stereo = cvt->ospec.stereo;
352	if (cvt->ospec.sign != -1) s->sign = cvt->ospec.sign;
353	if (cvt->ospec.bit16 != -1) s->bit16 = cvt->ospec.bit16;
354	if (cvt->ospec.bigendian != -1) s->bigendian = cvt->ospec.bigendian;
355	if (cvt->ospec.ulaw != -1) s->ulaw = cvt->ospec.ulaw;
356	return i;
357}
358
359int
360chn_feedchain(pcm_channel *c)
361{
362	int i, chosen, iter;
363	u_int32_t mask;
364	struct fmtspec s, t;
365	struct fmtcvt *e;
366
367	while (chn_removefeeder(c) != -1);
368	c->align = 0;
369	if ((c->format & chn_getcaps(c)->formats) == c->format)
370		return c->format;
371	getspec(c->format, &s);
372	if (s.bad) return -1;
373	getspec(chn_getcaps(c)->bestfmt, &t);
374	mask = (~cmpspec(&s, &t)) & 0x1f;
375	iter = 0;
376	do {
377		if (mask == 0 || iter >= 8) break;
378		chosen = -1;
379		for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) {
380			e = &cvttab[i];
381			if ((cmpspec(&s, &e->ispec) == 0x1f) &&
382			   ((~cmpspec(&e->ispec, &e->ospec)) & mask))
383			   chosen = i;
384		}
385		if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s);
386		iter++;
387	} while (chosen != -1);
388	return (iter < 8)? chn_getcaps(c)->bestfmt : -1;
389}
390
391static int
392chn_addfeeder(pcm_channel *c, pcm_feeder *f)
393{
394	pcm_feeder *n;
395	n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT);
396	*n = *f;
397	n->source = c->feeder;
398	c->feeder = n;
399	if (n->init) n->init(n);
400	if (n->align > 0) c->align += n->align;
401	else if (n->align < 0 && c->align < -n->align) c->align -= n->align;
402	return 0;
403}
404
405static int
406chn_removefeeder(pcm_channel *c)
407{
408	pcm_feeder *f;
409	if (c->feeder == &feeder_root) return -1;
410	f = c->feeder->source;
411	if (c->feeder->free) c->feeder->free(c->feeder);
412	free(c->feeder, M_DEVBUF);
413	c->feeder = f;
414	return 0;
415}
416
417