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