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/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, 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 } 123 pcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root }; 124 125 /*****************************************************************************/ 126 127 static int 128 feed_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 } 140 static pcm_feeder feeder_8to16 = { "8to16", 0, NULL, NULL, feed_8to16 }; 141 142 /*****************************************************************************/ 143 144 static int 145 feed_16to8_init(pcm_feeder *f) 146 { 147 f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); 148 return (f->data == NULL); 149 } 150 151 static int 152 feed_16to8_free(pcm_feeder *f) 153 { 154 if (f->data) free(f->data, M_DEVBUF); 155 f->data = NULL; 156 return 0; 157 } 158 159 static int 160 feed_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 } 171 static pcm_feeder feeder_16to8le = 172 { "16to8le", 1, feed_16to8_init, feed_16to8_free, feed_16to8le }; 173 174 /*****************************************************************************/ 175 176 static int 177 feed_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 } 189 static pcm_feeder feeder_monotostereo8 = 190 { "monotostereo8", 0, NULL, NULL, feed_monotostereo8 }; 191 192 /*****************************************************************************/ 193 194 static int 195 feed_stereotomono8_init(pcm_feeder *f) 196 { 197 f->data = malloc(FEEDBUFSZ, M_DEVBUF, M_NOWAIT); 198 return (f->data == NULL); 199 } 200 201 static int 202 feed_stereotomono8_free(pcm_feeder *f) 203 { 204 if (f->data) free(f->data, M_DEVBUF); 205 f->data = NULL; 206 return 0; 207 } 208 209 static int 210 feed_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 } 221 static pcm_feeder feeder_stereotomono8 = 222 { "stereotomono8", 1, feed_stereotomono8_init, feed_stereotomono8_free, 223 feed_stereotomono8 }; 224 225 /*****************************************************************************/ 226 227 static int 228 feed_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 } 240 static pcm_feeder feeder_endian = { "endian", -1, NULL, NULL, feed_endian }; 241 242 /*****************************************************************************/ 243 244 static int 245 feed_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 } 255 static pcm_feeder feeder_sign8 = 256 { "sign8", 0, NULL, NULL, feed_sign, (void *)1 }; 257 static pcm_feeder feeder_sign16 = 258 { "sign16", -1, NULL, NULL, feed_sign, (void *)2 }; 259 260 /*****************************************************************************/ 261 262 static int 263 feed_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 } 272 static pcm_feeder feeder_ulawtou8 = 273 { "ulawtou8", 0, NULL, NULL, feed_table, ulaw_to_u8 }; 274 static pcm_feeder feeder_u8toulaw = 275 { "u8toulaw", 0, NULL, NULL, feed_table, u8_to_ulaw }; 276 277 /*****************************************************************************/ 278 279 struct fmtspec { 280 int stereo; 281 int sign; 282 int bit16; 283 int bigendian; 284 int ulaw; 285 int bad; 286 }; 287 288 struct fmtcvt { 289 pcm_feeder *f; 290 struct fmtspec ispec, ospec; 291 }; 292 293 struct 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 309 static int 310 getspec(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 321 static int 322 cmp(int x, int y) 323 { 324 return (x == -1 || x == y || y == -1)? 1 : 0; 325 } 326 327 static int 328 cmpspec(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 339 static int 340 cvtapply(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 352 int 353 chn_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 384 static int 385 chn_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 398 static int 399 chn_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