1 /*- 2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk> 3 * Copyright (c) 2003 Orion Hodson <orion@freebsd.org> 4 * Copyright (c) 2005 Ariff Abdullah <skywizard@MyBSD.org.my> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 * 28 * 2005-06-11: 29 * ========== 30 * 31 * *New* and rewritten soft sample rate converter supporting arbitary sample 32 * rate, fine grained scalling/coefficients and unified up/down stereo 33 * converter. Most of disclaimers from orion's previous version also applied 34 * here, regarding with linear interpolation deficiencies, pre/post 35 * anti-aliasing filtering issues. This version comes with much simpler and 36 * tighter interface, although it works almost exactly like the older one. 37 * 38 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 39 * * 40 * This new implementation is fully dedicated in memory of Cameron Grant, * 41 * the creator of magnificent, highly addictive feeder infrastructure. * 42 * * 43 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 44 * 45 * Orion's notes: 46 * ============= 47 * 48 * This rate conversion code uses linear interpolation without any 49 * pre- or post- interpolation filtering to combat aliasing. This 50 * greatly limits the sound quality and should be addressed at some 51 * stage in the future. 52 * 53 * Since this accuracy of interpolation is sensitive and examination 54 * of the algorithm output is harder from the kernel, th code is 55 * designed to be compiled in the kernel and in a userland test 56 * harness. This is done by selectively including and excluding code 57 * with several portions based on whether _KERNEL is defined. It's a 58 * little ugly, but exceedingly useful. The testsuite and its 59 * revisions can be found at: 60 * http://people.freebsd.org/~orion/files/feedrate/ 61 * 62 * Special thanks to Ken Marx for exposing flaws in the code and for 63 * testing revisions. 64 */ 65 66 #include <dev/sound/pcm/sound.h> 67 #include "feeder_if.h" 68 69 SND_DECLARE_FILE("$FreeBSD$"); 70 71 #define RATE_ASSERT(x, y) /* KASSERT(x,y) */ 72 #define RATE_TRACE(x...) /* printf(x) */ 73 74 MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder"); 75 76 #define FEEDBUFSZ 8192 77 #define ROUNDHZ 25 78 #define RATEMIN 4000 79 /* 8000 * 138 or 11025 * 100 . This is insane, indeed! */ 80 #define RATEMAX 1102500 81 #define MINGAIN 92 82 #define MAXGAIN 96 83 84 #define FEEDRATE_CONVERT_64 0 85 #define FEEDRATE_CONVERT_SCALE64 1 86 #define FEEDRATE_CONVERT_SCALE32 2 87 #define FEEDRATE_CONVERT_PLAIN 3 88 #define FEEDRATE_CONVERT_FIXED 4 89 #define FEEDRATE_CONVERT_OPTIMAL 5 90 #define FEEDRATE_CONVERT_WORST 6 91 92 #define FEEDRATE_64_MAXROLL 32 93 #define FEEDRATE_32_MAXROLL 16 94 95 struct feed_rate_info { 96 uint32_t src, dst; /* rounded source / destination rates */ 97 uint32_t rsrc, rdst; /* original source / destination rates */ 98 uint32_t gx, gy; /* interpolation / decimation ratio */ 99 uint32_t alpha; /* interpolation distance */ 100 uint32_t pos, bpos; /* current sample / buffer positions */ 101 uint32_t bufsz; /* total buffer size */ 102 int32_t scale, roll; /* scale / roll factor */ 103 int16_t *buffer; 104 uint32_t (*convert)(struct feed_rate_info *, int16_t *, uint32_t); 105 }; 106 107 static uint32_t 108 feed_convert_64(struct feed_rate_info *, int16_t *, uint32_t); 109 static uint32_t 110 feed_convert_scale64(struct feed_rate_info *, int16_t *, uint32_t); 111 static uint32_t 112 feed_convert_scale32(struct feed_rate_info *, int16_t *, uint32_t); 113 static uint32_t 114 feed_convert_plain(struct feed_rate_info *, int16_t *, uint32_t); 115 116 int feeder_rate_ratemin = RATEMIN; 117 int feeder_rate_ratemax = RATEMAX; 118 /* 119 * See 'Feeder Scaling Type' below.. 120 */ 121 static int feeder_rate_scaling = FEEDRATE_CONVERT_OPTIMAL; 122 static int feeder_rate_buffersize = FEEDBUFSZ & ~1; 123 124 /* 125 * sysctls.. I love sysctls.. 126 */ 127 TUNABLE_INT("hw.snd.feeder_rate_ratemin", &feeder_rate_ratemin); 128 TUNABLE_INT("hw.snd.feeder_rate_ratemax", &feeder_rate_ratemin); 129 TUNABLE_INT("hw.snd.feeder_rate_scaling", &feeder_rate_scaling); 130 TUNABLE_INT("hw.snd.feeder_rate_buffersize", &feeder_rate_buffersize); 131 132 static int 133 sysctl_hw_snd_feeder_rate_ratemin(SYSCTL_HANDLER_ARGS) 134 { 135 int err, val; 136 137 val = feeder_rate_ratemin; 138 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 139 if (val < 1 || val >= feeder_rate_ratemax) 140 err = EINVAL; 141 else 142 feeder_rate_ratemin = val; 143 return err; 144 } 145 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemin, CTLTYPE_INT | CTLFLAG_RW, 146 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemin, "I", ""); 147 148 static int 149 sysctl_hw_snd_feeder_rate_ratemax(SYSCTL_HANDLER_ARGS) 150 { 151 int err, val; 152 153 val = feeder_rate_ratemax; 154 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 155 if (val <= feeder_rate_ratemin || val > 0x7fffff) 156 err = EINVAL; 157 else 158 feeder_rate_ratemax = val; 159 return err; 160 } 161 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_ratemax, CTLTYPE_INT | CTLFLAG_RW, 162 0, sizeof(int), sysctl_hw_snd_feeder_rate_ratemax, "I", ""); 163 164 static int 165 sysctl_hw_snd_feeder_rate_scaling(SYSCTL_HANDLER_ARGS) 166 { 167 int err, val; 168 169 val = feeder_rate_scaling; 170 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 171 /* 172 * Feeder Scaling Type 173 * =================== 174 * 175 * 1. Plain 64bit (high precision) 176 * 2. 64bit scaling (high precision, CPU friendly, but can 177 * cause gain up/down). 178 * 3. 32bit scaling (somehow can cause hz roundup, gain 179 * up/down). 180 * 4. Plain copy (default if src == dst. Except if src == dst, 181 * this is the worst / silly conversion method!). 182 * 183 * Sysctl options:- 184 * 185 * 0 - Plain 64bit - no fallback. 186 * 1 - 64bit scaling - no fallback. 187 * 2 - 32bit scaling - no fallback. 188 * 3 - Plain copy - no fallback. 189 * 4 - Fixed rate. Means that, choose optimal conversion method 190 * without causing hz roundup. 191 * 32bit scaling (as long as hz roundup does not occur), 192 * 64bit scaling, Plain 64bit. 193 * 5 - Optimal / CPU friendly (DEFAULT). 194 * 32bit scaling, 64bit scaling, Plain 64bit 195 * 6 - Optimal to worst, no 64bit arithmetic involved. 196 * 32bit scaling, Plain copy. 197 */ 198 if (val < FEEDRATE_CONVERT_64 || val > FEEDRATE_CONVERT_WORST) 199 err = EINVAL; 200 else 201 feeder_rate_scaling = val; 202 return err; 203 } 204 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_scaling, CTLTYPE_INT | CTLFLAG_RW, 205 0, sizeof(int), sysctl_hw_snd_feeder_rate_scaling, "I", ""); 206 207 static int 208 sysctl_hw_snd_feeder_rate_buffersize(SYSCTL_HANDLER_ARGS) 209 { 210 int err, val; 211 212 val = feeder_rate_buffersize; 213 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 214 /* 215 * Don't waste too much kernel space 216 */ 217 if (val < 2 || val > 65536) 218 err = EINVAL; 219 else 220 feeder_rate_buffersize = val & ~1; 221 return err; 222 } 223 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_buffersize, CTLTYPE_INT | CTLFLAG_RW, 224 0, sizeof(int), sysctl_hw_snd_feeder_rate_buffersize, "I", ""); 225 226 static void 227 feed_speed_ratio(uint32_t x, uint32_t y, uint32_t *gx, uint32_t *gy) 228 { 229 uint32_t w, src = x, dst = y; 230 231 while (y != 0) { 232 w = x % y; 233 x = y; 234 y = w; 235 } 236 *gx = src / x; 237 *gy = dst / x; 238 } 239 240 static void 241 feed_scale_roll(uint32_t dst, int32_t *scale, int32_t *roll, int32_t max) 242 { 243 int64_t k, tscale; 244 int32_t j, troll; 245 246 *scale = *roll = -1; 247 for (j = MAXGAIN; j >= MINGAIN; j -= 3) { 248 for (troll = 0; troll < max; troll++) { 249 tscale = (1 << troll) / dst; 250 k = (tscale * dst * 100) >> troll; 251 if (k > j && k <= 100) { 252 *scale = tscale; 253 *roll = troll; 254 return; 255 } 256 } 257 } 258 } 259 260 static int 261 feed_get_best_coef(uint32_t *src, uint32_t *dst, uint32_t *gx, uint32_t *gy, 262 int32_t *scale, int32_t *roll) 263 { 264 uint32_t tsrc, tdst, sscale, dscale; 265 int32_t tscale, troll; 266 int i, j, hzmin, hzmax; 267 268 *scale = *roll = -1; 269 for (i = 0; i < 2; i++) { 270 hzmin = (ROUNDHZ * i) + 1; 271 hzmax = hzmin + ROUNDHZ; 272 for (j = hzmin; j < hzmax; j++) { 273 tsrc = *src - (*src % j); 274 tdst = *dst; 275 if (tsrc < 1 || tdst < 1) 276 goto coef_failed; 277 feed_speed_ratio(tsrc, tdst, &sscale, &dscale); 278 feed_scale_roll(dscale, &tscale, &troll, 279 FEEDRATE_32_MAXROLL); 280 if (tscale != -1 && troll != -1) { 281 *src = tsrc; 282 *gx = sscale; 283 *gy = dscale; 284 *scale = tscale; 285 *roll = troll; 286 return j; 287 } 288 } 289 for (j = hzmin; j < hzmax; j++) { 290 tsrc = *src - (*src % j); 291 tdst = *dst - (*dst % j); 292 if (tsrc < 1 || tdst < 1) 293 goto coef_failed; 294 feed_speed_ratio(tsrc, tdst, &sscale, &dscale); 295 feed_scale_roll(dscale, &tscale, &troll, 296 FEEDRATE_32_MAXROLL); 297 if (tscale != -1 && troll != -1) { 298 *src = tsrc; 299 *dst = tdst; 300 *gx = sscale; 301 *gy = dscale; 302 *scale = tscale; 303 *roll = troll; 304 return j; 305 } 306 } 307 for (j = hzmin; j < hzmax; j++) { 308 tsrc = *src; 309 tdst = *dst - (*dst % j); 310 if (tsrc < 1 || tdst < 1) 311 goto coef_failed; 312 feed_speed_ratio(tsrc, tdst, &sscale, &dscale); 313 feed_scale_roll(dscale, &tscale, &troll, 314 FEEDRATE_32_MAXROLL); 315 if (tscale != -1 && troll != -1) { 316 *src = tsrc; 317 *dst = tdst; 318 *gx = sscale; 319 *gy = dscale; 320 *scale = tscale; 321 *roll = troll; 322 return j; 323 } 324 } 325 } 326 coef_failed: 327 feed_speed_ratio(*src, *dst, gx, gy); 328 feed_scale_roll(*gy, scale, roll, FEEDRATE_32_MAXROLL); 329 return 0; 330 } 331 332 static void 333 feed_rate_reset(struct feed_rate_info *info) 334 { 335 info->scale = -1; 336 info->roll = -1; 337 info->src = info->rsrc; 338 info->dst = info->rdst; 339 info->gx = 0; 340 info->gy = 0; 341 } 342 343 static int 344 feed_rate_setup(struct pcm_feeder *f) 345 { 346 struct feed_rate_info *info = f->data; 347 int r = 0; 348 349 info->pos = 2; 350 info->bpos = 4; 351 info->alpha = 0; 352 feed_rate_reset(info); 353 if (info->src == info->dst) { 354 /* 355 * No conversion ever needed. Just do plain copy. 356 */ 357 info->convert = feed_convert_plain; 358 info->gx = 1; 359 info->gy = 1; 360 } else { 361 switch (feeder_rate_scaling) { 362 case FEEDRATE_CONVERT_64: 363 feed_speed_ratio(info->src, info->dst, 364 &info->gx, &info->gy); 365 info->convert = feed_convert_64; 366 break; 367 case FEEDRATE_CONVERT_SCALE64: 368 feed_speed_ratio(info->src, info->dst, 369 &info->gx, &info->gy); 370 feed_scale_roll(info->gy, &info->scale, 371 &info->roll, FEEDRATE_64_MAXROLL); 372 if (info->scale == -1 || info->roll == -1) 373 return -1; 374 info->convert = feed_convert_scale64; 375 break; 376 case FEEDRATE_CONVERT_SCALE32: 377 r = feed_get_best_coef(&info->src, &info->dst, 378 &info->gx, &info->gy, &info->scale, 379 &info->roll); 380 if (r == 0) 381 return -1; 382 info->convert = feed_convert_scale32; 383 break; 384 case FEEDRATE_CONVERT_PLAIN: 385 feed_speed_ratio(info->src, info->dst, 386 &info->gx, &info->gy); 387 info->convert = feed_convert_plain; 388 break; 389 case FEEDRATE_CONVERT_FIXED: 390 r = feed_get_best_coef(&info->src, &info->dst, 391 &info->gx, &info->gy, &info->scale, 392 &info->roll); 393 if (r != 0 && info->src == info->rsrc && 394 info->dst == info->rdst) 395 info->convert = feed_convert_scale32; 396 else { 397 /* Fallback */ 398 feed_rate_reset(info); 399 feed_speed_ratio(info->src, info->dst, 400 &info->gx, &info->gy); 401 feed_scale_roll(info->gy, &info->scale, 402 &info->roll, FEEDRATE_64_MAXROLL); 403 if (info->scale != -1 && info->roll != -1) 404 info->convert = feed_convert_scale64; 405 else 406 info->convert = feed_convert_64; 407 } 408 break; 409 case FEEDRATE_CONVERT_OPTIMAL: 410 r = feed_get_best_coef(&info->src, &info->dst, 411 &info->gx, &info->gy, &info->scale, 412 &info->roll); 413 if (r != 0) 414 info->convert = feed_convert_scale32; 415 else { 416 /* Fallback */ 417 feed_rate_reset(info); 418 feed_speed_ratio(info->src, info->dst, 419 &info->gx, &info->gy); 420 feed_scale_roll(info->gy, &info->scale, 421 &info->roll, FEEDRATE_64_MAXROLL); 422 if (info->scale != -1 && info->roll != -1) 423 info->convert = feed_convert_scale64; 424 else 425 info->convert = feed_convert_64; 426 } 427 break; 428 case FEEDRATE_CONVERT_WORST: 429 r = feed_get_best_coef(&info->src, &info->dst, 430 &info->gx, &info->gy, &info->scale, 431 &info->roll); 432 if (r != 0) 433 info->convert = feed_convert_scale32; 434 else { 435 /* Fallback */ 436 feed_rate_reset(info); 437 feed_speed_ratio(info->src, info->dst, 438 &info->gx, &info->gy); 439 info->convert = feed_convert_plain; 440 } 441 break; 442 default: 443 return -1; 444 break; 445 } 446 /* No way! */ 447 if (info->gx == 0 || info->gy == 0) 448 return -1; 449 /* 450 * No need to interpolate/decimate, just do plain copy. 451 * This probably caused by Hz roundup. 452 */ 453 if (info->gx == info->gy) 454 info->convert = feed_convert_plain; 455 } 456 return 0; 457 } 458 459 static int 460 feed_rate_set(struct pcm_feeder *f, int what, int value) 461 { 462 struct feed_rate_info *info = f->data; 463 464 if (value < feeder_rate_ratemin || value > feeder_rate_ratemax) 465 return -1; 466 467 switch (what) { 468 case FEEDRATE_SRC: 469 info->rsrc = value; 470 break; 471 case FEEDRATE_DST: 472 info->rdst = value; 473 break; 474 default: 475 return -1; 476 } 477 return feed_rate_setup(f); 478 } 479 480 static int 481 feed_rate_get(struct pcm_feeder *f, int what) 482 { 483 struct feed_rate_info *info = f->data; 484 485 /* 486 * Return *real* src/dst rate. 487 */ 488 switch (what) { 489 case FEEDRATE_SRC: 490 return info->rsrc; 491 case FEEDRATE_DST: 492 return info->rdst; 493 default: 494 return -1; 495 } 496 return -1; 497 } 498 499 static int 500 feed_rate_init(struct pcm_feeder *f) 501 { 502 struct feed_rate_info *info; 503 504 info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO); 505 if (info == NULL) 506 return ENOMEM; 507 /* 508 * bufsz = sample from last cycle + conversion space 509 */ 510 info->bufsz = 2 + feeder_rate_buffersize; 511 info->buffer = malloc(sizeof(*info->buffer) * info->bufsz, 512 M_RATEFEEDER, M_NOWAIT | M_ZERO); 513 if (info->buffer == NULL) { 514 free(info, M_RATEFEEDER); 515 return ENOMEM; 516 } 517 info->rsrc = DSP_DEFAULT_SPEED; 518 info->rdst = DSP_DEFAULT_SPEED; 519 f->data = info; 520 return feed_rate_setup(f); 521 } 522 523 static int 524 feed_rate_free(struct pcm_feeder *f) 525 { 526 struct feed_rate_info *info = f->data; 527 528 if (info) { 529 if (info->buffer) 530 free(info->buffer, M_RATEFEEDER); 531 free(info, M_RATEFEEDER); 532 } 533 f->data = NULL; 534 return 0; 535 } 536 537 static uint32_t 538 feed_convert_64(struct feed_rate_info *info, int16_t *dst, uint32_t max) 539 { 540 int64_t x, alpha, distance; 541 uint32_t ret; 542 int32_t pos, bpos, gx, gy; 543 int16_t *src; 544 /* 545 * Plain, straight forward 64bit arith. No bit-magic applied here. 546 */ 547 ret = 0; 548 alpha = info->alpha; 549 gx = info->gx; 550 gy = info->gy; 551 pos = info->pos; 552 bpos = info->bpos; 553 src = info->buffer; 554 for (;;) { 555 if (alpha < gx) { 556 alpha += gy; 557 pos += 2; 558 if (pos == bpos) 559 break; 560 } else { 561 alpha -= gx; 562 distance = gy - alpha; 563 x = (alpha * src[pos - 2]) + (distance * src[pos]); 564 dst[ret++] = x / gy; 565 x = (alpha * src[pos - 1]) + (distance * src[pos + 1]); 566 dst[ret++] = x / gy; 567 if (ret == max) 568 break; 569 } 570 } 571 info->alpha = alpha; 572 info->pos = pos; 573 return ret; 574 } 575 576 static uint32_t 577 feed_convert_scale64(struct feed_rate_info *info, int16_t *dst, uint32_t max) 578 { 579 int64_t x, alpha, distance; 580 uint32_t ret; 581 int32_t pos, bpos, gx, gy, roll; 582 int16_t *src; 583 /* 584 * 64bit scaling. 585 */ 586 ret = 0; 587 roll = info->roll; 588 alpha = info->alpha * info->scale; 589 gx = info->gx * info->scale; 590 gy = info->gy * info->scale; 591 pos = info->pos; 592 bpos = info->bpos; 593 src = info->buffer; 594 for (;;) { 595 if (alpha < gx) { 596 alpha += gy; 597 pos += 2; 598 if (pos == bpos) 599 break; 600 } else { 601 alpha -= gx; 602 distance = gy - alpha; 603 x = (alpha * src[pos - 2]) + (distance * src[pos]); 604 dst[ret++] = x >> roll; 605 x = (alpha * src[pos - 1]) + (distance * src[pos + 1]); 606 dst[ret++] = x >> roll; 607 if (ret == max) 608 break; 609 } 610 } 611 info->alpha = alpha / info->scale; 612 info->pos = pos; 613 return ret; 614 } 615 616 static uint32_t 617 feed_convert_scale32(struct feed_rate_info *info, int16_t *dst, uint32_t max) 618 { 619 uint32_t ret; 620 int32_t x, pos, bpos, gx, gy, alpha, roll, distance; 621 int16_t *src; 622 /* 623 * 32bit scaling. 624 */ 625 ret = 0; 626 roll = info->roll; 627 alpha = info->alpha * info->scale; 628 gx = info->gx * info->scale; 629 gy = info->gy * info->scale; 630 pos = info->pos; 631 bpos = info->bpos; 632 src = info->buffer; 633 for (;;) { 634 if (alpha < gx) { 635 alpha += gy; 636 pos += 2; 637 if (pos == bpos) 638 break; 639 } else { 640 alpha -= gx; 641 distance = gy - alpha; 642 x = (alpha * src[pos - 2]) + (distance * src[pos]); 643 dst[ret++] = x >> roll; 644 x = (alpha * src[pos - 1]) + (distance * src[pos + 1]); 645 dst[ret++] = x >> roll; 646 if (ret == max) 647 break; 648 } 649 } 650 info->alpha = alpha / info->scale; 651 info->pos = pos; 652 return ret; 653 } 654 655 static uint32_t 656 feed_convert_plain(struct feed_rate_info *info, int16_t *dst, uint32_t max) 657 { 658 uint32_t ret; 659 int32_t pos, bpos, gx, gy, alpha; 660 int16_t *src; 661 /* 662 * Plain copy. 663 */ 664 ret = 0; 665 gx = info->gx; 666 gy = info->gy; 667 alpha = info->alpha; 668 pos = info->pos; 669 bpos = info->bpos; 670 src = info->buffer; 671 for (;;) { 672 if (alpha < gx) { 673 alpha += gy; 674 pos += 2; 675 if (pos == bpos) 676 break; 677 } else { 678 alpha -= gx; 679 dst[ret++] = src[pos]; 680 dst[ret++] = src[pos + 1]; 681 if (ret == max) 682 break; 683 } 684 } 685 info->pos = pos; 686 info->alpha = alpha; 687 return ret; 688 } 689 690 static int32_t 691 feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 692 uint32_t count, void *source) 693 { 694 struct feed_rate_info *info = f->data; 695 uint32_t i; 696 int32_t fetch, slot; 697 int16_t *dst = (int16_t *)b; 698 /* 699 * This loop has been optimized to generalize both up / down 700 * sampling without causing missing samples or excessive buffer 701 * feeding. 702 */ 703 RATE_ASSERT(count >= 4 && count % 4 == 0, 704 ("%s: Count size not byte integral\n", __func__)); 705 count >>= 1; 706 slot = (((info->gx * (count >> 1)) + info->gy - info->alpha - 1) / info->gy) << 1; 707 /* 708 * Optimize buffer feeding aggresively to ensure calculated slot 709 * can be fitted nicely into available buffer free space, hence 710 * avoiding multiple feeding. 711 */ 712 if (info->pos != 2 && info->bpos - info->pos == 2 && 713 info->bpos + slot > info->bufsz) { 714 /* 715 * Copy last unit sample and its previous to 716 * beginning of buffer. 717 */ 718 info->buffer[0] = info->buffer[info->pos - 2]; 719 info->buffer[1] = info->buffer[info->pos - 1]; 720 info->buffer[2] = info->buffer[info->pos]; 721 info->buffer[3] = info->buffer[info->pos + 1]; 722 info->pos = 2; 723 info->bpos = 4; 724 } 725 RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", 726 __func__, slot)); 727 i = 0; 728 for (;;) { 729 for (;;) { 730 fetch = info->bufsz - info->bpos; 731 RATE_ASSERT(fetch >= 0, 732 ("%s: Buffer overrun: %d > %d\n", 733 __func__, info->bpos, info->bufsz)); 734 if (slot < fetch) 735 fetch = slot; 736 if (fetch > 0) { 737 RATE_ASSERT(fetch % 2 == 0, 738 ("%s: Fetch size not sample integral\n", 739 __func__)); 740 fetch = FEEDER_FEED(f->source, c, 741 (uint8_t *)(info->buffer + info->bpos), 742 fetch << 1, source); 743 if (fetch == 0) 744 break; 745 RATE_ASSERT(fetch % 4 == 0, 746 ("%s: Fetch size not byte integral\n", 747 __func__)); 748 fetch >>= 1; 749 info->bpos += fetch; 750 slot -= fetch; 751 RATE_ASSERT(slot >= 0, 752 ("%s: Negative Slot: %d\n", __func__ 753 slot)); 754 if (slot == 0) 755 break; 756 if (info->bpos == info->bufsz) 757 break; 758 } else 759 break; 760 } 761 if (info->pos == info->bpos) { 762 RATE_ASSERT(info->pos == 2, 763 ("%s: EOF while in progress\n", __func__)); 764 break; 765 } 766 RATE_ASSERT(info->pos <= info->bpos, 767 ("%s: Buffer overrun: %d > %d\n", __func__, 768 info->pos, info->bpos)); 769 RATE_ASSERT(info->pos < info->bpos, 770 ("%s: Zero buffer!\n", __func__)); 771 RATE_ASSERT((info->bpos - info->pos) % 2 == 0, 772 ("%s: Buffer not sample integral\n", __func__)); 773 i += info->convert(info, dst + i, count - i); 774 RATE_ASSERT(info->pos <= info->bpos, 775 ("%s: Buffer overrun: %d > %d\n", 776 __func__, info->pos, info->bpos)); 777 if (info->pos == info->bpos) { 778 /* 779 * End of buffer cycle. Copy last unit sample 780 * to beginning of buffer so next cycle can 781 * interpolate using it. 782 */ 783 info->buffer[0] = info->buffer[info->pos - 2]; 784 info->buffer[1] = info->buffer[info->pos - 1]; 785 info->bpos = 2; 786 info->pos = 2; 787 } 788 if (i == count) 789 break; 790 } 791 return i << 1; 792 } 793 794 static struct pcm_feederdesc feeder_rate_desc[] = { 795 {FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 796 {0, 0, 0, 0}, 797 }; 798 static kobj_method_t feeder_rate_methods[] = { 799 KOBJMETHOD(feeder_init, feed_rate_init), 800 KOBJMETHOD(feeder_free, feed_rate_free), 801 KOBJMETHOD(feeder_set, feed_rate_set), 802 KOBJMETHOD(feeder_get, feed_rate_get), 803 KOBJMETHOD(feeder_feed, feed_rate), 804 {0, 0} 805 }; 806 FEEDER_DECLARE(feeder_rate, 2, NULL); 807