feeder.c revision 54155
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 54155 1999-12-05 19:09:13Z cg $
27 */
28
29#include <dev/sound/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, c = 0, s;
112	KASSERT(count, ("feed_root: count == 0"));
113	count &= ~((1 << ch->align) - 1);
114	KASSERT(count, ("feed_root: aligned count == 0"));
115	s = spltty();
116	if (ch->smegcnt > 0) {
117		c = min(ch->smegcnt, count);
118		bcopy(ch->smegbuf, buffer, c);
119		ch->smegcnt -= c;
120	}
121	count = min(count, stream->uio_resid);
122	if (count) {
123		ret = uiomove(buffer, count, stream);
124		KASSERT(ret == 0, ("feed_root: uiomove failed"));
125	}
126	splx(s);
127	return c + count;
128}
129pcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root };
130
131/*****************************************************************************/
132
133static int
134feed_8to16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
135{
136	int i, j, k;
137	k = f->source->feed(f->source, c, b, count / 2, stream);
138	j = k - 1;
139	i = j * 2 + 1;
140	while (i > 0 && j >= 0) {
141		b[i--] = b[j--];
142		b[i--] = 0;
143	}
144	return k * 2;
145}
146static pcm_feeder feeder_8to16 = { "8to16", 0, NULL, NULL, feed_8to16 };
147
148/*****************************************************************************/
149
150static int
151feed_16to8_init(pcm_feeder *f)
152{
153	f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
154	return (f->data == NULL);
155}
156
157static int
158feed_16to8_free(pcm_feeder *f)
159{
160	if (f->data) free(f->data, M_DEVBUF);
161	f->data = NULL;
162	return 0;
163}
164
165static int
166feed_16to8le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
167{
168	u_int32_t i = 0, toget = count * 2;
169	int j = 1, k;
170	k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
171	while (j < k) {
172		b[i++] = ((u_int8_t *)f->data)[j];
173		j += 2;
174	}
175	return i;
176}
177static pcm_feeder feeder_16to8le =
178	{ "16to8le", 1, feed_16to8_init, feed_16to8_free, feed_16to8le };
179
180/*****************************************************************************/
181
182static int
183feed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
184{
185	int i, j, k = f->source->feed(f->source, c, b, count / 2, stream);
186	j = k - 1;
187	i = j * 2 + 1;
188	while (i > 0 && j >= 0) {
189		b[i--] = b[j];
190		b[i--] = b[j];
191		j--;
192	}
193	return k * 2;
194}
195static pcm_feeder feeder_monotostereo8 =
196	{ "monotostereo8", 0, NULL, NULL, feed_monotostereo8 };
197
198/*****************************************************************************/
199
200static int
201feed_stereotomono8_init(pcm_feeder *f)
202{
203	f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
204	return (f->data == NULL);
205}
206
207static int
208feed_stereotomono8_free(pcm_feeder *f)
209{
210	if (f->data) free(f->data, M_DEVBUF);
211	f->data = NULL;
212	return 0;
213}
214
215static int
216feed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
217{
218	u_int32_t i = 0, toget = count * 2;
219	int j = 0, k;
220	k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
221	while (j < k) {
222		b[i++] = ((u_int8_t *)f->data)[j];
223		j += 2;
224	}
225	return i;
226}
227static pcm_feeder feeder_stereotomono8 =
228	{ "stereotomono8", 1, feed_stereotomono8_init, feed_stereotomono8_free,
229	feed_stereotomono8 };
230
231/*****************************************************************************/
232
233static int
234feed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
235{
236	u_int8_t t;
237	int i = 0, j = f->source->feed(f->source, c, b, count, stream);
238	while (i < j) {
239		t = b[i];
240		b[i] = b[i + 1];
241		b[i + 1] = t;
242		i += 2;
243	}
244	return i;
245}
246static pcm_feeder feeder_endian = { "endian", -1, NULL, NULL, feed_endian };
247
248/*****************************************************************************/
249
250static int
251feed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
252{
253	int i = 0, j = f->source->feed(f->source, c, b, count, stream);
254	int ssz = (int)f->data, ofs = ssz - 1;
255	while (i < j) {
256		b[i + ofs] ^= 0x80;
257		i += ssz;
258	}
259	return i;
260}
261static pcm_feeder feeder_sign8 =
262	{ "sign8", 0, NULL, NULL, feed_sign, (void *)1 };
263static pcm_feeder feeder_sign16 =
264	{ "sign16", -1, NULL, NULL, feed_sign, (void *)2 };
265
266/*****************************************************************************/
267
268static int
269feed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
270{
271	int i = 0, j = f->source->feed(f->source, c, b, count, stream);
272	while (i < j) {
273		b[i] = ((u_int8_t *)f->data)[b[i]];
274		i++;
275	}
276	return i;
277}
278static pcm_feeder feeder_ulawtou8 =
279	{ "ulawtou8", 0, NULL, NULL, feed_table, ulaw_to_u8 };
280static pcm_feeder feeder_u8toulaw =
281	{ "u8toulaw", 0, NULL, NULL, feed_table, u8_to_ulaw };
282
283/*****************************************************************************/
284
285struct fmtspec {
286	int stereo;
287	int sign;
288	int bit16;
289	int bigendian;
290	int ulaw;
291	int bad;
292};
293
294struct fmtcvt {
295	pcm_feeder *f;
296	struct fmtspec ispec, ospec;
297};
298
299struct fmtcvt cvttab[] = {
300	{&feeder_ulawtou8, 	{-1,  0, 0, 0,  1}, 	{-1,  0, 0, 0,  0}},
301	{&feeder_u8toulaw, 	{-1,  0, 0, 0,  0}, 	{-1,  0, 0, 0,  1}},
302	{&feeder_sign8,		{-1,  0, 0, 0,  0},	{-1,  1, 0, 0,  0}},
303	{&feeder_sign8,		{-1,  1, 0, 0,  0},	{-1,  0, 0, 0,  0}},
304	{&feeder_monotostereo8,	{ 0, -1, 0, 0, -1},	{ 1, -1, 0, 0, -1}},
305	{&feeder_stereotomono8, { 1, -1, 0, 0, -1},	{ 0, -1, 0, 0, -1}},
306	{&feeder_sign16,	{-1,  0, 1, 0,  0},	{-1,  1, 1, 0,  0}},
307	{&feeder_sign16,	{-1,  1, 1, 0,  0},	{-1,  0, 1, 0,  0}},
308	{&feeder_8to16,		{-1, -1, 0, 0,  0},	{-1, -1, 1, 0,  0}},
309	{&feeder_16to8le,	{-1, -1, 1, 0,  0},	{-1, -1, 0, 0,  0}},
310	{&feeder_endian,	{-1, -1, 1, 0,  0},	{-1, -1, 1, 1,  0}},
311	{&feeder_endian,	{-1, -1, 1, 1,  0},	{-1, -1, 1, 0,  0}},
312};
313#define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt))
314
315static int
316getspec(u_int32_t fmt, struct fmtspec *spec)
317{
318	spec->stereo = (fmt & AFMT_STEREO)? 1 : 0;
319	spec->sign = (fmt & AFMT_SIGNED)? 1 : 0;
320	spec->bit16 = (fmt & AFMT_16BIT)? 1 : 0;
321	spec->bigendian = (fmt & AFMT_BIGENDIAN)? 1 : 0;
322	spec->ulaw = (fmt & AFMT_MU_LAW)? 1 : 0;
323	spec->bad = (fmt & (AFMT_A_LAW | AFMT_MPEG))? 1 : 0;
324	return 0;
325}
326
327static int
328cmp(int x, int y)
329{
330	return (x == -1 || x == y || y == -1)? 1 : 0;
331}
332
333static int
334cmpspec(struct fmtspec *x, struct fmtspec *y)
335{
336	int i = 0;
337	if (cmp(x->stereo, y->stereo)) i |= 0x01;
338	if (cmp(x->sign, y->sign)) i |= 0x02;
339	if (cmp(x->bit16, y->bit16)) i |= 0x04;
340	if (cmp(x->bigendian, y->bigendian)) i |= 0x08;
341	if (cmp(x->ulaw, y->ulaw)) i |= 0x10;
342	return i;
343}
344
345static int
346cvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s)
347{
348	int i = cmpspec(s, &cvt->ospec);
349	chn_addfeeder(c, cvt->f);
350	if (cvt->ospec.stereo != -1) s->stereo = cvt->ospec.stereo;
351	if (cvt->ospec.sign != -1) s->sign = cvt->ospec.sign;
352	if (cvt->ospec.bit16 != -1) s->bit16 = cvt->ospec.bit16;
353	if (cvt->ospec.bigendian != -1) s->bigendian = cvt->ospec.bigendian;
354	if (cvt->ospec.ulaw != -1) s->ulaw = cvt->ospec.ulaw;
355	return i;
356}
357
358int
359chn_feedchain(pcm_channel *c)
360{
361	int i, chosen, iter;
362	u_int32_t mask;
363	struct fmtspec s, t;
364	struct fmtcvt *e;
365
366	while (chn_removefeeder(c) != -1);
367	c->align = 0;
368	if ((c->format & chn_getcaps(c)->formats) == c->format)
369		return c->format;
370	getspec(c->format, &s);
371	if (s.bad) return -1;
372	getspec(chn_getcaps(c)->bestfmt, &t);
373	mask = (~cmpspec(&s, &t)) & 0x1f;
374	iter = 0;
375	do {
376		if (mask == 0 || iter >= 8) break;
377		chosen = -1;
378		for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) {
379			e = &cvttab[i];
380			if ((cmpspec(&s, &e->ispec) == 0x1f) &&
381			   ((~cmpspec(&e->ispec, &e->ospec)) & mask))
382			   chosen = i;
383		}
384		if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s);
385		iter++;
386	} while (chosen != -1);
387	return (iter < 8)? chn_getcaps(c)->bestfmt : -1;
388}
389
390static int
391chn_addfeeder(pcm_channel *c, pcm_feeder *f)
392{
393	pcm_feeder *n;
394	n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT);
395	*n = *f;
396	n->source = c->feeder;
397	c->feeder = n;
398	if (n->init) n->init(n);
399	if (n->align > 0) c->align += n->align;
400	else if (n->align < 0 && c->align < -n->align) c->align -= n->align;
401	return 0;
402}
403
404static int
405chn_removefeeder(pcm_channel *c)
406{
407	pcm_feeder *f;
408	if (c->feeder == &feeder_root) return -1;
409	f = c->feeder->source;
410	if (c->feeder->free) c->feeder->free(c->feeder);
411	free(c->feeder, M_DEVBUF);
412	c->feeder = f;
413	return 0;
414}
415
416