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