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