2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
29 #include <dev/sound/pcm/sound.h>
31 static int chn_addfeeder(pcm_channel *c, pcm_feeder *f);
32 static int chn_removefeeder(pcm_channel *c);
34 #define FEEDBUFSZ 8192
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,
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,
106 /*****************************************************************************/
109 feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream)
112 KASSERT(count, ("feed_root: count == 0"));
113 count &= ~((1 << ch->align) - 1);
114 KASSERT(count, ("feed_root: aligned count == 0"));
116 if (ch->smegcnt > 0) {
117 c = min(ch->smegcnt, count);
118 bcopy(ch->smegbuf, buffer, c);
121 count = min(count, stream->uio_resid);
123 ret = uiomove(buffer, count, stream);
124 KASSERT(ret == 0, ("feed_root: uiomove failed"));
129 pcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root };
131 /*****************************************************************************/
134 feed_8to16(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
137 k = f->source->feed(f->source, c, b, count / 2, stream);
140 while (i > 0 && j >= 0) {
146 static pcm_feeder feeder_8to16 = { "8to16", 0, NULL, NULL, feed_8to16 };
148 /*****************************************************************************/
151 feed_16to8_init(pcm_feeder *f)
153 f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
154 return (f->data == NULL);
158 feed_16to8_free(pcm_feeder *f)
160 if (f->data) free(f->data, M_DEVBUF);
166 feed_16to8le(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
168 u_int32_t i = 0, toget = count * 2;
170 k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
172 b[i++] = ((u_int8_t *)f->data)[j];
177 static pcm_feeder feeder_16to8le =
178 { "16to8le", 1, feed_16to8_init, feed_16to8_free, feed_16to8le };
180 /*****************************************************************************/
183 feed_monotostereo8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
185 int i, j, k = f->source->feed(f->source, c, b, count / 2, stream);
188 while (i > 0 && j >= 0) {
195 static pcm_feeder feeder_monotostereo8 =
196 { "monotostereo8", 0, NULL, NULL, feed_monotostereo8 };
198 /*****************************************************************************/
201 feed_stereotomono8_init(pcm_feeder *f)
203 f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT);
204 return (f->data == NULL);
208 feed_stereotomono8_free(pcm_feeder *f)
210 if (f->data) free(f->data, M_DEVBUF);
216 feed_stereotomono8(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
218 u_int32_t i = 0, toget = count * 2;
220 k = f->source->feed(f->source, c, f->data, min(toget, FEEDBUFSZ), stream);
222 b[i++] = ((u_int8_t *)f->data)[j];
227 static pcm_feeder feeder_stereotomono8 =
228 { "stereotomono8", 1, feed_stereotomono8_init, feed_stereotomono8_free,
229 feed_stereotomono8 };
231 /*****************************************************************************/
234 feed_endian(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
237 int i = 0, j = f->source->feed(f->source, c, b, count, stream);
246 static pcm_feeder feeder_endian = { "endian", -1, NULL, NULL, feed_endian };
248 /*****************************************************************************/
251 feed_sign(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
253 int i = 0, j = f->source->feed(f->source, c, b, count, stream);
254 int ssz = (int)f->data, ofs = ssz - 1;
261 static pcm_feeder feeder_sign8 =
262 { "sign8", 0, NULL, NULL, feed_sign, (void *)1 };
263 static pcm_feeder feeder_sign16 =
264 { "sign16", -1, NULL, NULL, feed_sign, (void *)2 };
266 /*****************************************************************************/
269 feed_table(pcm_feeder *f, pcm_channel *c, u_int8_t *b, u_int32_t count, struct uio *stream)
271 int i = 0, j = f->source->feed(f->source, c, b, count, stream);
273 b[i] = ((u_int8_t *)f->data)[b[i]];
278 static pcm_feeder feeder_ulawtou8 =
279 { "ulawtou8", 0, NULL, NULL, feed_table, ulaw_to_u8 };
280 static pcm_feeder feeder_u8toulaw =
281 { "u8toulaw", 0, NULL, NULL, feed_table, u8_to_ulaw };
283 /*****************************************************************************/
296 struct fmtspec ispec, ospec;
299 struct 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}},
313 #define FEEDERTABSZ (sizeof(cvttab) / sizeof(struct fmtcvt))
316 getspec(u_int32_t fmt, struct fmtspec *spec)
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;
330 return (x == -1 || x == y || y == -1)? 1 : 0;
334 cmpspec(struct fmtspec *x, struct fmtspec *y)
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;
346 cvtapply(pcm_channel *c, struct fmtcvt *cvt, struct fmtspec *s)
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;
359 chn_feedchain(pcm_channel *c)
366 while (chn_removefeeder(c) != -1);
368 if ((c->format & chn_getcaps(c)->formats) == 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;
376 if (mask == 0 || iter >= 8) break;
378 for (i = 0; i < FEEDERTABSZ && chosen == -1; i++) {
380 if ((cmpspec(&s, &e->ispec) == 0x1f) &&
381 ((~cmpspec(&e->ispec, &e->ospec)) & mask))
384 if (chosen != -1) mask &= cvtapply(c, &cvttab[chosen], &s);
386 } while (chosen != -1);
387 return (iter < 8)? chn_getcaps(c)->bestfmt : -1;
391 chn_addfeeder(pcm_channel *c, pcm_feeder *f)
394 n = malloc(sizeof(pcm_feeder), M_DEVBUF, M_NOWAIT);
396 n->source = c->feeder;
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;
405 chn_removefeeder(pcm_channel *c)
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);