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