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