feeder.c revision 53205
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 417