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