Deleted Added
sdiff udiff text old ( 52713 ) new ( 53205 )
full compact
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