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