1 /*- 2 * Copyright (c) 1999 Cameron Grant <cg@FreeBSD.org> 3 * Copyright (c) 2003 Orion Hodson <orion@FreeBSD.org> 4 * Copyright (c) 2005 Ariff Abdullah <ariff@FreeBSD.org> 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 * 2006-02-21: 29 * ========== 30 * 31 * Major cleanup and overhaul to remove much redundant codes. 32 * Highlights: 33 * 1) Support for signed / unsigned 16, 24 and 32 bit, 34 * big / little endian, 35 * 2) Unlimited channels. 36 * 37 * 2005-06-11: 38 * ========== 39 * 40 * *New* and rewritten soft sample rate converter supporting arbitrary sample 41 * rates, fine grained scaling/coefficients and a unified up/down stereo 42 * converter. Most of the disclaimers from orion's notes also applies 43 * here, regarding linear interpolation deficiencies and pre/post 44 * anti-aliasing filtering issues. This version comes with a much simpler and 45 * tighter interface, although it works almost exactly like the older one. 46 * 47 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 48 * * 49 * This new implementation is fully dedicated in memory of Cameron Grant, * 50 * the creator of the magnificent, highly addictive feeder infrastructure. * 51 * * 52 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 53 * 54 * Orion's notes: 55 * ============= 56 * 57 * This rate conversion code uses linear interpolation without any 58 * pre- or post- interpolation filtering to combat aliasing. This 59 * greatly limits the sound quality and should be addressed at some 60 * stage in the future. 61 * 62 * Since this accuracy of interpolation is sensitive and examination 63 * of the algorithm output is harder from the kernel, the code is 64 * designed to be compiled in the kernel and in a userland test 65 * harness. This is done by selectively including and excluding code 66 * with several portions based on whether _KERNEL is defined. It's a 67 * little ugly, but exceedingly useful. The testsuite and its 68 * revisions can be found at: 69 * http://people.freebsd.org/~orion/files/feedrate/ 70 * 71 * Special thanks to Ken Marx for exposing flaws in the code and for 72 * testing revisions. 73 */ 74 75 #include <dev/sound/pcm/sound.h> 76 #include "feeder_if.h" 77 78 SND_DECLARE_FILE("$FreeBSD$"); 79 80 #define RATE_ASSERT(x, y) /* KASSERT(x,y) */ 81 #define RATE_TEST(x, y) /* if (!(x)) printf y */ 82 #define RATE_TRACE(x...) /* printf(x) */ 83 84 MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder"); 85 86 /* 87 * Don't overflow 32bit integer, since everything is done 88 * within 32bit arithmetic. 89 */ 90 #define RATE_FACTOR_MIN 1 91 #define RATE_FACTOR_MAX PCM_S24_MAX 92 #define RATE_FACTOR_SAFE(val) (!((val) < RATE_FACTOR_MIN || \ 93 (val) > RATE_FACTOR_MAX)) 94 95 struct feed_rate_info; 96 97 typedef uint32_t (*feed_rate_converter)(struct feed_rate_info *, 98 uint8_t *, uint32_t); 99 100 struct feed_rate_info { 101 uint32_t src, dst; /* rounded source / destination rates */ 102 uint32_t rsrc, rdst; /* original source / destination rates */ 103 uint32_t gx, gy; /* interpolation / decimation ratio */ 104 uint32_t alpha; /* interpolation distance */ 105 uint32_t pos, bpos; /* current sample / buffer positions */ 106 uint32_t bufsz; /* total buffer size limit */ 107 uint32_t bufsz_init; /* allocated buffer size */ 108 uint32_t channels; /* total channels */ 109 uint32_t bps; /* bytes-per-sample */ 110 #ifdef FEEDRATE_STRAY 111 uint32_t stray; /* stray bytes */ 112 #endif 113 uint8_t *buffer; 114 feed_rate_converter convert; 115 }; 116 117 int feeder_rate_min = FEEDRATE_RATEMIN; 118 int feeder_rate_max = FEEDRATE_RATEMAX; 119 int feeder_rate_round = FEEDRATE_ROUNDHZ; 120 121 TUNABLE_INT("hw.snd.feeder_rate_min", &feeder_rate_min); 122 TUNABLE_INT("hw.snd.feeder_rate_max", &feeder_rate_max); 123 TUNABLE_INT("hw.snd.feeder_rate_round", &feeder_rate_round); 124 125 static int 126 sysctl_hw_snd_feeder_rate_min(SYSCTL_HANDLER_ARGS) 127 { 128 int err, val; 129 130 val = feeder_rate_min; 131 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 132 if (err != 0 || req->newptr == NULL) 133 return (err); 134 if (RATE_FACTOR_SAFE(val) && val < feeder_rate_max) 135 feeder_rate_min = val; 136 else 137 err = EINVAL; 138 return (err); 139 } 140 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_min, CTLTYPE_INT | CTLFLAG_RW, 141 0, sizeof(int), sysctl_hw_snd_feeder_rate_min, "I", 142 "minimum allowable rate"); 143 144 static int 145 sysctl_hw_snd_feeder_rate_max(SYSCTL_HANDLER_ARGS) 146 { 147 int err, val; 148 149 val = feeder_rate_max; 150 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 151 if (err != 0 || req->newptr == NULL) 152 return (err); 153 if (RATE_FACTOR_SAFE(val) && val > feeder_rate_min) 154 feeder_rate_max = val; 155 else 156 err = EINVAL; 157 return (err); 158 } 159 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_max, CTLTYPE_INT | CTLFLAG_RW, 160 0, sizeof(int), sysctl_hw_snd_feeder_rate_max, "I", 161 "maximum allowable rate"); 162 163 static int 164 sysctl_hw_snd_feeder_rate_round(SYSCTL_HANDLER_ARGS) 165 { 166 int err, val; 167 168 val = feeder_rate_round; 169 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 170 if (err != 0 || req->newptr == NULL) 171 return (err); 172 if (val < FEEDRATE_ROUNDHZ_MIN || val > FEEDRATE_ROUNDHZ_MAX) 173 err = EINVAL; 174 else 175 feeder_rate_round = val - (val % FEEDRATE_ROUNDHZ); 176 return (err); 177 } 178 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_round, CTLTYPE_INT | CTLFLAG_RW, 179 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I", 180 "sample rate converter rounding threshold"); 181 182 #define FEEDER_RATE_CONVERT(FMTBIT, RATE_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ 183 static uint32_t \ 184 feed_convert_##SIGNS##FMTBIT##ENDIANS(struct feed_rate_info *info, \ 185 uint8_t *dst, uint32_t max) \ 186 { \ 187 uint32_t ret, smpsz, ch, pos, bpos, gx, gy, alpha, d1, d2; \ 188 int32_t x, y; \ 189 int i; \ 190 uint8_t *src, *sx, *sy; \ 191 \ 192 ret = 0; \ 193 alpha = info->alpha; \ 194 gx = info->gx; \ 195 gy = info->gy; \ 196 pos = info->pos; \ 197 bpos = info->bpos; \ 198 src = info->buffer + pos; \ 199 ch = info->channels; \ 200 smpsz = PCM_##FMTBIT##_BPS * ch; \ 201 for (;;) { \ 202 if (alpha < gx) { \ 203 alpha += gy; \ 204 pos += smpsz; \ 205 if (pos == bpos) \ 206 break; \ 207 src += smpsz; \ 208 } else { \ 209 alpha -= gx; \ 210 d1 = (alpha << PCM_FXSHIFT) / gy; \ 211 d2 = (1U << PCM_FXSHIFT) - d1; \ 212 sx = src - smpsz; \ 213 sy = src; \ 214 i = ch; \ 215 do { \ 216 x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx); \ 217 y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy); \ 218 x = (((RATE_INTCAST)x * d1) + \ 219 ((RATE_INTCAST)y * d2)) >> PCM_FXSHIFT; \ 220 PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, x); \ 221 dst += PCM_##FMTBIT##_BPS; \ 222 sx += PCM_##FMTBIT##_BPS; \ 223 sy += PCM_##FMTBIT##_BPS; \ 224 ret += PCM_##FMTBIT##_BPS; \ 225 } while (--i != 0); \ 226 if (ret == max) \ 227 break; \ 228 } \ 229 } \ 230 info->alpha = alpha; \ 231 info->pos = pos; \ 232 return (ret); \ 233 } 234 235 FEEDER_RATE_CONVERT(8, int32_t, S, s, NE, ne) 236 FEEDER_RATE_CONVERT(16, int32_t, S, s, LE, le) 237 FEEDER_RATE_CONVERT(24, int32_t, S, s, LE, le) 238 FEEDER_RATE_CONVERT(32, intpcm_t, S, s, LE, le) 239 FEEDER_RATE_CONVERT(16, int32_t, S, s, BE, be) 240 FEEDER_RATE_CONVERT(24, int32_t, S, s, BE, be) 241 FEEDER_RATE_CONVERT(32, intpcm_t, S, s, BE, be) 242 FEEDER_RATE_CONVERT(8, int32_t, U, u, NE, ne) 243 FEEDER_RATE_CONVERT(16, int32_t, U, u, LE, le) 244 FEEDER_RATE_CONVERT(24, int32_t, U, u, LE, le) 245 FEEDER_RATE_CONVERT(32, intpcm_t, U, u, LE, le) 246 FEEDER_RATE_CONVERT(16, int32_t, U, u, BE, be) 247 FEEDER_RATE_CONVERT(24, int32_t, U, u, BE, be) 248 FEEDER_RATE_CONVERT(32, intpcm_t, U, u, BE, be) 249 250 static void 251 feed_speed_ratio(uint32_t src, uint32_t dst, uint32_t *gx, uint32_t *gy) 252 { 253 uint32_t w, x = src, y = dst; 254 255 while (y != 0) { 256 w = x % y; 257 x = y; 258 y = w; 259 } 260 *gx = src / x; 261 *gy = dst / x; 262 } 263 264 static void 265 feed_rate_reset(struct feed_rate_info *info) 266 { 267 info->src = info->rsrc - (info->rsrc % 268 ((feeder_rate_round > 0) ? feeder_rate_round : 1)); 269 info->dst = info->rdst - (info->rdst % 270 ((feeder_rate_round > 0) ? feeder_rate_round : 1)); 271 info->gx = 1; 272 info->gy = 1; 273 info->alpha = 0; 274 info->channels = 1; 275 info->bps = PCM_8_BPS; 276 info->convert = NULL; 277 info->bufsz = info->bufsz_init; 278 info->pos = 1; 279 info->bpos = 2; 280 #ifdef FEEDRATE_STRAY 281 info->stray = 0; 282 #endif 283 } 284 285 static int 286 feed_rate_setup(struct pcm_feeder *f) 287 { 288 struct feed_rate_info *info = f->data; 289 static const struct { 290 uint32_t format; /* pcm / audio format */ 291 uint32_t bps; /* bytes-per-sample, regardless of 292 total channels */ 293 feed_rate_converter convert; 294 } convtbl[] = { 295 { AFMT_S8, PCM_8_BPS, feed_convert_s8ne }, 296 { AFMT_S16_LE, PCM_16_BPS, feed_convert_s16le }, 297 { AFMT_S24_LE, PCM_24_BPS, feed_convert_s24le }, 298 { AFMT_S32_LE, PCM_32_BPS, feed_convert_s32le }, 299 { AFMT_S16_BE, PCM_16_BPS, feed_convert_s16be }, 300 { AFMT_S24_BE, PCM_24_BPS, feed_convert_s24be }, 301 { AFMT_S32_BE, PCM_32_BPS, feed_convert_s32be }, 302 { AFMT_U8, PCM_8_BPS, feed_convert_u8ne }, 303 { AFMT_U16_LE, PCM_16_BPS, feed_convert_u16le }, 304 { AFMT_U24_LE, PCM_24_BPS, feed_convert_u24le }, 305 { AFMT_U32_LE, PCM_32_BPS, feed_convert_u32le }, 306 { AFMT_U16_BE, PCM_16_BPS, feed_convert_u16be }, 307 { AFMT_U24_BE, PCM_24_BPS, feed_convert_u24be }, 308 { AFMT_U32_BE, PCM_32_BPS, feed_convert_u32be }, 309 { 0, 0, NULL }, 310 }; 311 uint32_t i; 312 313 feed_rate_reset(info); 314 315 if (info->src != info->dst) 316 feed_speed_ratio(info->src, info->dst, &info->gx, &info->gy); 317 318 if (!(RATE_FACTOR_SAFE(info->gx) && RATE_FACTOR_SAFE(info->gy))) 319 return (-1); 320 321 for (i = 0; i < sizeof(convtbl) / sizeof(*convtbl); i++) { 322 if (convtbl[i].format == 0) 323 return (-1); 324 if ((f->desc->out & ~AFMT_STEREO) == convtbl[i].format) { 325 info->bps = convtbl[i].bps; 326 info->convert = convtbl[i].convert; 327 break; 328 } 329 } 330 331 /* 332 * No need to interpolate/decimate, just do plain copy. 333 */ 334 if (info->gx == info->gy) 335 info->convert = NULL; 336 337 info->channels = (f->desc->out & AFMT_STEREO) ? 2 : 1; 338 info->pos = info->bps * info->channels; 339 info->bpos = info->pos << 1; 340 info->bufsz -= info->bufsz % info->pos; 341 342 memset(info->buffer, sndbuf_zerodata(f->desc->out), info->bpos); 343 344 RATE_TRACE("%s: %u (%u) -> %u (%u) [%u/%u] , " 345 "format=0x%08x, channels=%u, bufsz=%u\n", 346 __func__, info->src, info->rsrc, info->dst, info->rdst, 347 info->gx, info->gy, f->desc->out, info->channels, 348 info->bufsz - info->pos); 349 350 return (0); 351 } 352 353 static int 354 feed_rate_set(struct pcm_feeder *f, int what, int32_t value) 355 { 356 struct feed_rate_info *info = f->data; 357 358 if (value < feeder_rate_min || value > feeder_rate_max) 359 return (-1); 360 361 switch (what) { 362 case FEEDRATE_SRC: 363 info->rsrc = value; 364 break; 365 case FEEDRATE_DST: 366 info->rdst = value; 367 break; 368 default: 369 return (-1); 370 } 371 return (feed_rate_setup(f)); 372 } 373 374 static int 375 feed_rate_get(struct pcm_feeder *f, int what) 376 { 377 struct feed_rate_info *info = f->data; 378 379 switch (what) { 380 case FEEDRATE_SRC: 381 return (info->rsrc); 382 case FEEDRATE_DST: 383 return (info->rdst); 384 default: 385 return (-1); 386 } 387 return (-1); 388 } 389 390 static int 391 feed_rate_init(struct pcm_feeder *f) 392 { 393 struct feed_rate_info *info; 394 395 if (f->desc->out != f->desc->in) 396 return (EINVAL); 397 398 info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO); 399 if (info == NULL) 400 return (ENOMEM); 401 /* 402 * bufsz = sample from last cycle + conversion space 403 */ 404 info->bufsz_init = 8 + feeder_buffersize; 405 info->buffer = malloc(sizeof(*info->buffer) * info->bufsz_init, 406 M_RATEFEEDER, M_NOWAIT | M_ZERO); 407 if (info->buffer == NULL) { 408 free(info, M_RATEFEEDER); 409 return (ENOMEM); 410 } 411 info->rsrc = DSP_DEFAULT_SPEED; 412 info->rdst = DSP_DEFAULT_SPEED; 413 f->data = info; 414 return (feed_rate_setup(f)); 415 } 416 417 static int 418 feed_rate_free(struct pcm_feeder *f) 419 { 420 struct feed_rate_info *info = f->data; 421 422 if (info != NULL) { 423 if (info->buffer != NULL) 424 free(info->buffer, M_RATEFEEDER); 425 free(info, M_RATEFEEDER); 426 } 427 f->data = NULL; 428 return (0); 429 } 430 431 static int 432 feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 433 uint32_t count, void *source) 434 { 435 struct feed_rate_info *info = f->data; 436 uint32_t i, smpsz; 437 int32_t fetch, slot; 438 439 if (info->convert == NULL) 440 return (FEEDER_FEED(f->source, c, b, count, source)); 441 442 /* 443 * This loop has been optimized to generalize both up / down 444 * sampling without causing missing samples or excessive buffer 445 * feeding. The tricky part is to calculate *precise* (slot) value 446 * needed for the entire conversion space since we are bound to 447 * return and fill up the buffer according to the requested 'count'. 448 * Too much feeding will cause the extra buffer stay within temporary 449 * circular buffer forever and always manifest itself as a truncated 450 * sound during end of playback / recording. Too few, and we end up 451 * with possible underruns and waste of cpu cycles. 452 * 453 * 'Stray' management exist to combat with possible unaligned 454 * buffering by the caller. 455 */ 456 smpsz = info->bps * info->channels; 457 RATE_TEST(count >= smpsz && (count % smpsz) == 0, 458 ("%s: Count size not sample integral (%d)\n", __func__, count)); 459 if (count < smpsz) 460 return (0); 461 count -= count % smpsz; 462 /* 463 * This slot count formula will stay here for the next million years 464 * to come. This is the key of our circular buffering precision. 465 */ 466 slot = (((info->gx * (count / smpsz)) + info->gy - info->alpha - 1) / 467 info->gy) * smpsz; 468 RATE_TEST((slot % smpsz) == 0, 469 ("%s: Slot count not sample integral (%d)\n", __func__, slot)); 470 #ifdef FEEDRATE_STRAY 471 RATE_TEST(info->stray == 0, ("%s: [1] Stray bytes: %u\n", __func__, 472 info->stray)); 473 #endif 474 if (info->pos != smpsz && info->bpos - info->pos == smpsz && 475 info->bpos + slot > info->bufsz) { 476 /* 477 * Copy last unit sample and its previous to 478 * beginning of buffer. 479 */ 480 bcopy(info->buffer + info->pos - smpsz, info->buffer, 481 sizeof(*info->buffer) * (smpsz << 1)); 482 info->pos = smpsz; 483 info->bpos = smpsz << 1; 484 } 485 RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", __func__, slot)); 486 i = 0; 487 for (;;) { 488 for (;;) { 489 fetch = info->bufsz - info->bpos; 490 #ifdef FEEDRATE_STRAY 491 fetch -= info->stray; 492 #endif 493 RATE_ASSERT(fetch >= 0, 494 ("%s: [1] Buffer overrun: %d > %d\n", __func__, 495 info->bpos, info->bufsz)); 496 if (slot < fetch) 497 fetch = slot; 498 #ifdef FEEDRATE_STRAY 499 if (fetch < 1) 500 #else 501 if (fetch < smpsz) 502 #endif 503 break; 504 RATE_ASSERT((int)(info->bpos 505 #ifdef FEEDRATE_STRAY 506 - info->stray 507 #endif 508 ) >= 0 && 509 (info->bpos - info->stray) < info->bufsz, 510 ("%s: DANGER - BUFFER OVERRUN! bufsz=%d, pos=%d\n", 511 __func__, info->bufsz, info->bpos 512 #ifdef FEEDRATE_STRAY 513 - info->stray 514 #endif 515 )); 516 fetch = FEEDER_FEED(f->source, c, 517 info->buffer + info->bpos 518 #ifdef FEEDRATE_STRAY 519 - info->stray 520 #endif 521 , fetch, source); 522 #ifdef FEEDRATE_STRAY 523 info->stray = 0; 524 if (fetch == 0) 525 #else 526 if (fetch < smpsz) 527 #endif 528 break; 529 RATE_TEST((fetch % smpsz) == 0, 530 ("%s: Fetch size not sample integral (%d)\n", 531 __func__, fetch)); 532 #ifdef FEEDRATE_STRAY 533 info->stray += fetch % smpsz; 534 RATE_TEST(info->stray == 0, 535 ("%s: Stray bytes detected (%d)\n", __func__, 536 info->stray)); 537 #endif 538 fetch -= fetch % smpsz; 539 info->bpos += fetch; 540 slot -= fetch; 541 RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", 542 __func__, slot)); 543 if (slot == 0 || info->bpos == info->bufsz) 544 break; 545 } 546 if (info->pos == info->bpos) { 547 RATE_TEST(info->pos == smpsz, 548 ("%s: EOF while in progress\n", __func__)); 549 break; 550 } 551 RATE_ASSERT(info->pos <= info->bpos, 552 ("%s: [2] Buffer overrun: %d > %d\n", __func__, info->pos, 553 info->bpos)); 554 RATE_ASSERT(info->pos < info->bpos, 555 ("%s: Zero buffer!\n", __func__)); 556 RATE_ASSERT(((info->bpos - info->pos) % smpsz) == 0, 557 ("%s: Buffer not sample integral (%d)\n", __func__, 558 info->bpos - info->pos)); 559 i += info->convert(info, b + i, count - i); 560 RATE_ASSERT(info->pos <= info->bpos, 561 ("%s: [3] Buffer overrun: %d > %d\n", __func__, info->pos, 562 info->bpos)); 563 if (info->pos == info->bpos) { 564 /* 565 * End of buffer cycle. Copy last unit sample 566 * to beginning of buffer so next cycle can 567 * interpolate using it. 568 */ 569 #ifdef FEEDRATE_STRAY 570 RATE_TEST(info->stray == 0, 571 ("%s: [2] Stray bytes: %u\n", __func__, 572 info->stray)); 573 #endif 574 bcopy(info->buffer + info->pos - smpsz, info->buffer, 575 sizeof(*info->buffer) * smpsz); 576 info->bpos = smpsz; 577 info->pos = smpsz; 578 } 579 if (i == count) 580 break; 581 } 582 583 RATE_TEST((slot == 0 && count == i) || (slot > 0 && count > i && 584 info->pos == info->bpos && info->pos == smpsz), 585 ("%s: Inconsistent slot/count! " 586 "Count Expect: %u , Got: %u, Slot Left: %d\n", __func__, count, i, 587 slot)); 588 589 #ifdef FEEDRATE_STRAY 590 RATE_TEST(info->stray == 0, ("%s: [3] Stray bytes: %u\n", __func__, 591 info->stray)); 592 #endif 593 594 return (i); 595 } 596 597 static struct pcm_feederdesc feeder_rate_desc[] = { 598 {FEEDER_RATE, AFMT_S8, AFMT_S8, 0}, 599 {FEEDER_RATE, AFMT_S16_LE, AFMT_S16_LE, 0}, 600 {FEEDER_RATE, AFMT_S24_LE, AFMT_S24_LE, 0}, 601 {FEEDER_RATE, AFMT_S32_LE, AFMT_S32_LE, 0}, 602 {FEEDER_RATE, AFMT_S16_BE, AFMT_S16_BE, 0}, 603 {FEEDER_RATE, AFMT_S24_BE, AFMT_S24_BE, 0}, 604 {FEEDER_RATE, AFMT_S32_BE, AFMT_S32_BE, 0}, 605 {FEEDER_RATE, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, 606 {FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 607 {FEEDER_RATE, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, 608 {FEEDER_RATE, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, 609 {FEEDER_RATE, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, 610 {FEEDER_RATE, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, 611 {FEEDER_RATE, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, 612 {FEEDER_RATE, AFMT_U8, AFMT_U8, 0}, 613 {FEEDER_RATE, AFMT_U16_LE, AFMT_U16_LE, 0}, 614 {FEEDER_RATE, AFMT_U24_LE, AFMT_U24_LE, 0}, 615 {FEEDER_RATE, AFMT_U32_LE, AFMT_U32_LE, 0}, 616 {FEEDER_RATE, AFMT_U16_BE, AFMT_U16_BE, 0}, 617 {FEEDER_RATE, AFMT_U24_BE, AFMT_U24_BE, 0}, 618 {FEEDER_RATE, AFMT_U32_BE, AFMT_U32_BE, 0}, 619 {FEEDER_RATE, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, 620 {FEEDER_RATE, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, 621 {FEEDER_RATE, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, 622 {FEEDER_RATE, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, 623 {FEEDER_RATE, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, 624 {FEEDER_RATE, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, 625 {FEEDER_RATE, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, 626 {0, 0, 0, 0}, 627 }; 628 629 static kobj_method_t feeder_rate_methods[] = { 630 KOBJMETHOD(feeder_init, feed_rate_init), 631 KOBJMETHOD(feeder_free, feed_rate_free), 632 KOBJMETHOD(feeder_set, feed_rate_set), 633 KOBJMETHOD(feeder_get, feed_rate_get), 634 KOBJMETHOD(feeder_feed, feed_rate), 635 {0, 0} 636 }; 637 638 FEEDER_DECLARE(feeder_rate, 2, NULL); 639