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 *, uint8_t *, uint32_t); 98 99 struct feed_rate_info { 100 uint32_t src, dst; /* rounded source / destination rates */ 101 uint32_t rsrc, rdst; /* original source / destination rates */ 102 uint32_t gx, gy; /* interpolation / decimation ratio */ 103 uint32_t alpha; /* interpolation distance */ 104 uint32_t pos, bpos; /* current sample / buffer positions */ 105 uint32_t bufsz; /* total buffer size limit */ 106 uint32_t bufsz_init; /* allocated buffer size */ 107 uint32_t channels; /* total channels */ 108 uint32_t bps; /* bytes-per-sample */ 109 uint32_t stray; /* stray bytes */ 110 uint8_t *buffer; 111 feed_rate_converter convert; 112 }; 113 114 int feeder_rate_min = FEEDRATE_RATEMIN; 115 int feeder_rate_max = FEEDRATE_RATEMAX; 116 int feeder_rate_round = FEEDRATE_ROUNDHZ; 117 118 TUNABLE_INT("hw.snd.feeder_rate_min", &feeder_rate_min); 119 TUNABLE_INT("hw.snd.feeder_rate_max", &feeder_rate_max); 120 TUNABLE_INT("hw.snd.feeder_rate_round", &feeder_rate_round); 121 122 static int 123 sysctl_hw_snd_feeder_rate_min(SYSCTL_HANDLER_ARGS) 124 { 125 int err, val; 126 127 val = feeder_rate_min; 128 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 129 if (RATE_FACTOR_SAFE(val) && val < feeder_rate_max) 130 feeder_rate_min = val; 131 else 132 err = EINVAL; 133 return err; 134 } 135 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_min, CTLTYPE_INT | CTLFLAG_RW, 136 0, sizeof(int), sysctl_hw_snd_feeder_rate_min, "I", 137 "minimum allowable rate"); 138 139 static int 140 sysctl_hw_snd_feeder_rate_max(SYSCTL_HANDLER_ARGS) 141 { 142 int err, val; 143 144 val = feeder_rate_max; 145 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 146 if (RATE_FACTOR_SAFE(val) && val > feeder_rate_min) 147 feeder_rate_max = val; 148 else 149 err = EINVAL; 150 return err; 151 } 152 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_max, CTLTYPE_INT | CTLFLAG_RW, 153 0, sizeof(int), sysctl_hw_snd_feeder_rate_max, "I", 154 "maximum allowable rate"); 155 156 static int 157 sysctl_hw_snd_feeder_rate_round(SYSCTL_HANDLER_ARGS) 158 { 159 int err, val; 160 161 val = feeder_rate_round; 162 err = sysctl_handle_int(oidp, &val, sizeof(val), req); 163 if (val < FEEDRATE_ROUNDHZ_MIN || val > FEEDRATE_ROUNDHZ_MAX) 164 err = EINVAL; 165 else 166 feeder_rate_round = val - (val % FEEDRATE_ROUNDHZ); 167 return err; 168 } 169 SYSCTL_PROC(_hw_snd, OID_AUTO, feeder_rate_round, CTLTYPE_INT | CTLFLAG_RW, 170 0, sizeof(int), sysctl_hw_snd_feeder_rate_round, "I", 171 "sample rate converter rounding threshold"); 172 173 #define FEEDER_RATE_CONVERT(FMTBIT, RATE_INTCAST, SIGN, SIGNS, ENDIAN, ENDIANS) \ 174 static uint32_t \ 175 feed_convert_##SIGNS##FMTBIT##ENDIANS(struct feed_rate_info *info, \ 176 uint8_t *dst, uint32_t max) \ 177 { \ 178 uint32_t ret, smpsz, bps, ch, pos, bpos, gx, gy, alpha, distance; \ 179 int32_t x, y; \ 180 int i; \ 181 uint8_t *src, *sx, *sy; \ 182 \ 183 ret = 0; \ 184 alpha = info->alpha; \ 185 gx = info->gx; \ 186 gy = info->gy; \ 187 pos = info->pos; \ 188 bpos = info->bpos; \ 189 src = info->buffer + pos; \ 190 ch = info->channels; \ 191 bps = info->bps; \ 192 smpsz = bps * ch; \ 193 for (;;) { \ 194 if (alpha < gx) { \ 195 alpha += gy; \ 196 pos += smpsz; \ 197 if (pos == bpos) \ 198 break; \ 199 src += smpsz; \ 200 } else { \ 201 alpha -= gx; \ 202 distance = (alpha << PCM_FXSHIFT) / gy; \ 203 sx = src - smpsz; \ 204 sy = src; \ 205 i = ch; \ 206 do { \ 207 x = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sx); \ 208 y = PCM_READ_##SIGN##FMTBIT##_##ENDIAN(sy); \ 209 x = (((RATE_INTCAST)x * distance) + \ 210 ((RATE_INTCAST)y * ((1 << PCM_FXSHIFT) - \ 211 distance))) >> PCM_FXSHIFT; \ 212 PCM_WRITE_##SIGN##FMTBIT##_##ENDIAN(dst, x); \ 213 dst += bps; \ 214 sx += bps; \ 215 sy += bps; \ 216 ret += bps; \ 217 } while (--i); \ 218 if (ret == max) \ 219 break; \ 220 } \ 221 } \ 222 info->alpha = alpha; \ 223 info->pos = pos; \ 224 return ret; \ 225 } 226 227 FEEDER_RATE_CONVERT(8, int32_t, S, s, NE, ne) 228 FEEDER_RATE_CONVERT(16, int32_t, S, s, LE, le) 229 FEEDER_RATE_CONVERT(24, int32_t, S, s, LE, le) 230 FEEDER_RATE_CONVERT(32, intpcm_t, S, s, LE, le) 231 FEEDER_RATE_CONVERT(16, int32_t, S, s, BE, be) 232 FEEDER_RATE_CONVERT(24, int32_t, S, s, BE, be) 233 FEEDER_RATE_CONVERT(32, intpcm_t, S, s, BE, be) 234 /* unsigned */ 235 FEEDER_RATE_CONVERT(8, int32_t, U, u, NE, ne) 236 FEEDER_RATE_CONVERT(16, int32_t, U, u, LE, le) 237 FEEDER_RATE_CONVERT(24, int32_t, U, u, LE, le) 238 FEEDER_RATE_CONVERT(32, intpcm_t, U, u, LE, le) 239 FEEDER_RATE_CONVERT(16, int32_t, U, u, BE, be) 240 FEEDER_RATE_CONVERT(24, int32_t, U, u, BE, be) 241 FEEDER_RATE_CONVERT(32, intpcm_t, U, u, BE, be) 242 243 static void 244 feed_speed_ratio(uint32_t src, uint32_t dst, uint32_t *gx, uint32_t *gy) 245 { 246 uint32_t w, x = src, y = dst; 247 248 while (y != 0) { 249 w = x % y; 250 x = y; 251 y = w; 252 } 253 *gx = src / x; 254 *gy = dst / x; 255 } 256 257 static void 258 feed_rate_reset(struct feed_rate_info *info) 259 { 260 info->src = info->rsrc - (info->rsrc % 261 ((feeder_rate_round > 0) ? feeder_rate_round : 1)); 262 info->dst = info->rdst - (info->rdst % 263 ((feeder_rate_round > 0) ? feeder_rate_round : 1)); 264 info->gx = 1; 265 info->gy = 1; 266 info->alpha = 0; 267 info->channels = 2; 268 info->bps = 2; 269 info->convert = NULL; 270 info->bufsz = info->bufsz_init; 271 info->pos = 4; 272 info->bpos = 8; 273 info->stray = 0; 274 } 275 276 static int 277 feed_rate_setup(struct pcm_feeder *f) 278 { 279 struct feed_rate_info *info = f->data; 280 static const struct { 281 uint32_t format; /* pcm / audio format */ 282 uint32_t bps; /* bytes-per-sample, regardless of 283 total channels */ 284 feed_rate_converter convert; 285 } convtbl[] = { 286 { AFMT_S8, PCM_8_BPS, feed_convert_s8ne }, 287 { AFMT_S16_LE, PCM_16_BPS, feed_convert_s16le }, 288 { AFMT_S24_LE, PCM_24_BPS, feed_convert_s24le }, 289 { AFMT_S32_LE, PCM_32_BPS, feed_convert_s32le }, 290 { AFMT_S16_BE, PCM_16_BPS, feed_convert_s16be }, 291 { AFMT_S24_BE, PCM_24_BPS, feed_convert_s24be }, 292 { AFMT_S32_BE, PCM_32_BPS, feed_convert_s32be }, 293 /* unsigned */ 294 { AFMT_U8, PCM_8_BPS, feed_convert_u8ne }, 295 { AFMT_U16_LE, PCM_16_BPS, feed_convert_u16le }, 296 { AFMT_U24_LE, PCM_24_BPS, feed_convert_u24le }, 297 { AFMT_U32_LE, PCM_32_BPS, feed_convert_u32le }, 298 { AFMT_U16_BE, PCM_16_BPS, feed_convert_u16be }, 299 { AFMT_U24_BE, PCM_24_BPS, feed_convert_u24be }, 300 { AFMT_U32_BE, PCM_32_BPS, feed_convert_u32be }, 301 { 0, 0, NULL }, 302 }; 303 uint32_t i; 304 305 feed_rate_reset(info); 306 307 if (info->src != info->dst) 308 feed_speed_ratio(info->src, info->dst, 309 &info->gx, &info->gy); 310 311 if (!(RATE_FACTOR_SAFE(info->gx) && RATE_FACTOR_SAFE(info->gy))) 312 return -1; 313 314 for (i = 0; i < sizeof(convtbl) / sizeof(*convtbl); i++) { 315 if (convtbl[i].format == 0) 316 return -1; 317 if ((f->desc->out & ~AFMT_STEREO) == convtbl[i].format) { 318 info->bps = convtbl[i].bps; 319 info->convert = convtbl[i].convert; 320 break; 321 } 322 } 323 324 /* 325 * No need to interpolate/decimate, just do plain copy. 326 */ 327 if (info->gx == info->gy) 328 info->convert = NULL; 329 330 info->channels = (f->desc->out & AFMT_STEREO) ? 2 : 1; 331 info->pos = info->bps * info->channels; 332 info->bpos = info->pos << 1; 333 info->bufsz -= info->bufsz % info->pos; 334 335 memset(info->buffer, sndbuf_zerodata(f->desc->out), info->bpos); 336 337 RATE_TRACE("%s: %u (%u) -> %u (%u) [%u/%u] , " 338 "format=0x%08x, channels=%u, bufsz=%u\n", 339 __func__, info->src, info->rsrc, info->dst, info->rdst, 340 info->gx, info->gy, 341 f->desc->out, info->channels, 342 info->bufsz - info->pos); 343 344 return 0; 345 } 346 347 static int 348 feed_rate_set(struct pcm_feeder *f, int what, int32_t value) 349 { 350 struct feed_rate_info *info = f->data; 351 352 if (value < feeder_rate_min || value > feeder_rate_max) 353 return -1; 354 355 switch (what) { 356 case FEEDRATE_SRC: 357 info->rsrc = value; 358 break; 359 case FEEDRATE_DST: 360 info->rdst = value; 361 break; 362 default: 363 return -1; 364 } 365 return feed_rate_setup(f); 366 } 367 368 static int 369 feed_rate_get(struct pcm_feeder *f, int what) 370 { 371 struct feed_rate_info *info = f->data; 372 373 switch (what) { 374 case FEEDRATE_SRC: 375 return info->rsrc; 376 case FEEDRATE_DST: 377 return info->rdst; 378 default: 379 return -1; 380 } 381 return -1; 382 } 383 384 static int 385 feed_rate_init(struct pcm_feeder *f) 386 { 387 struct feed_rate_info *info; 388 389 if (f->desc->out != f->desc->in) 390 return EINVAL; 391 392 info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO); 393 if (info == NULL) 394 return ENOMEM; 395 /* 396 * bufsz = sample from last cycle + conversion space 397 */ 398 info->bufsz_init = 8 + feeder_buffersize; 399 info->buffer = malloc(sizeof(*info->buffer) * info->bufsz_init, 400 M_RATEFEEDER, M_NOWAIT | M_ZERO); 401 if (info->buffer == NULL) { 402 free(info, M_RATEFEEDER); 403 return ENOMEM; 404 } 405 info->rsrc = DSP_DEFAULT_SPEED; 406 info->rdst = DSP_DEFAULT_SPEED; 407 f->data = info; 408 return feed_rate_setup(f); 409 } 410 411 static int 412 feed_rate_free(struct pcm_feeder *f) 413 { 414 struct feed_rate_info *info = f->data; 415 416 if (info) { 417 if (info->buffer) 418 free(info->buffer, M_RATEFEEDER); 419 free(info, M_RATEFEEDER); 420 } 421 f->data = NULL; 422 return 0; 423 } 424 425 static int 426 feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b, 427 uint32_t count, void *source) 428 { 429 struct feed_rate_info *info = f->data; 430 uint32_t i, smpsz; 431 int32_t fetch, slot; 432 433 if (info->convert == NULL) 434 return FEEDER_FEED(f->source, c, b, count, source); 435 436 /* 437 * This loop has been optimized to generalize both up / down 438 * sampling without causing missing samples or excessive buffer 439 * feeding. The tricky part is to calculate *precise* (slot) value 440 * needed for the entire conversion space since we are bound to 441 * return and fill up the buffer according to the requested 'count'. 442 * Too much feeding will cause the extra buffer stay within temporary 443 * circular buffer forever and always manifest itself as a truncated 444 * sound during end of playback / recording. Too few, and we end up 445 * with possible underruns and waste of cpu cycles. 446 * 447 * 'Stray' management exist to combat with possible unaligned 448 * buffering by the caller. 449 */ 450 smpsz = info->bps * info->channels; 451 RATE_TEST(count >= smpsz && (count % smpsz) == 0, 452 ("%s: Count size not sample integral (%d)\n", __func__, count)); 453 if (count < smpsz) 454 return 0; 455 count -= count % smpsz; 456 /* 457 * This slot count formula will stay here for the next million years 458 * to come. This is the key of our circular buffering precision. 459 */ 460 slot = (((info->gx * (count / smpsz)) + info->gy - info->alpha - 1) / info->gy) * smpsz; 461 RATE_TEST((slot % smpsz) == 0, ("%s: Slot count not sample integral (%d)\n", 462 __func__, slot)); 463 RATE_TEST(info->stray == 0, ("%s: [1] Stray bytes: %u\n", 464 __func__,info->stray)); 465 if (info->pos != smpsz && info->bpos - info->pos == smpsz && 466 info->bpos + slot > info->bufsz) { 467 /* 468 * Copy last unit sample and its previous to 469 * beginning of buffer. 470 */ 471 bcopy(info->buffer + info->pos - smpsz, info->buffer, 472 sizeof(*info->buffer) * (smpsz << 1)); 473 info->pos = smpsz; 474 info->bpos = smpsz << 1; 475 } 476 RATE_ASSERT(slot >= 0, ("%s: Negative Slot: %d\n", 477 __func__, slot)); 478 i = 0; 479 for (;;) { 480 for (;;) { 481 fetch = info->bufsz - info->bpos; 482 fetch -= info->stray; 483 RATE_ASSERT(fetch >= 0, 484 ("%s: [1] Buffer overrun: %d > %d\n", 485 __func__, info->bpos, info->bufsz)); 486 if (slot < fetch) 487 fetch = slot; 488 if (fetch > 0) { 489 RATE_ASSERT((int32_t)(info->bpos - info->stray) >= 0 && 490 (info->bpos - info->stray) < info->bufsz, 491 ("%s: DANGER - BUFFER OVERRUN! bufsz=%d, pos=%d\n", __func__, 492 info->bufsz, info->bpos - info->stray)); 493 fetch = FEEDER_FEED(f->source, c, 494 info->buffer + info->bpos - info->stray, 495 fetch, source); 496 info->stray = 0; 497 if (fetch == 0) 498 break; 499 RATE_TEST((fetch % smpsz) == 0, 500 ("%s: Fetch size not sample integral (%d)\n", 501 __func__, fetch)); 502 info->stray += fetch % smpsz; 503 RATE_TEST(info->stray == 0, 504 ("%s: Stray bytes detected (%d)\n", 505 __func__, info->stray)); 506 fetch -= fetch % smpsz; 507 info->bpos += fetch; 508 slot -= fetch; 509 RATE_ASSERT(slot >= 0, 510 ("%s: Negative Slot: %d\n", __func__, 511 slot)); 512 if (slot == 0) 513 break; 514 if (info->bpos == info->bufsz) 515 break; 516 } else 517 break; 518 } 519 if (info->pos == info->bpos) { 520 RATE_TEST(info->pos == smpsz, 521 ("%s: EOF while in progress\n", __func__)); 522 break; 523 } 524 RATE_ASSERT(info->pos <= info->bpos, 525 ("%s: [2] Buffer overrun: %d > %d\n", __func__, 526 info->pos, info->bpos)); 527 RATE_ASSERT(info->pos < info->bpos, 528 ("%s: Zero buffer!\n", __func__)); 529 RATE_ASSERT(((info->bpos - info->pos) % smpsz) == 0, 530 ("%s: Buffer not sample integral (%d)\n", 531 __func__, info->bpos - info->pos)); 532 i += info->convert(info, b + i, count - i); 533 RATE_ASSERT(info->pos <= info->bpos, 534 ("%s: [3] Buffer overrun: %d > %d\n", 535 __func__, info->pos, info->bpos)); 536 if (info->pos == info->bpos) { 537 /* 538 * End of buffer cycle. Copy last unit sample 539 * to beginning of buffer so next cycle can 540 * interpolate using it. 541 */ 542 RATE_TEST(info->stray == 0, ("%s: [2] Stray bytes: %u\n", __func__, info->stray)); 543 bcopy(info->buffer + info->pos - smpsz, info->buffer, 544 sizeof(*info->buffer) * smpsz); 545 info->bpos = smpsz; 546 info->pos = smpsz; 547 } 548 if (i == count) 549 break; 550 } 551 552 RATE_TEST((slot == 0 && count == i) || 553 (slot > 0 && count > i && 554 info->pos == info->bpos && info->pos == smpsz), 555 ("%s: Inconsistent slot/count! " 556 "Count Expect: %u , Got: %u, Slot Left: %d\n", 557 __func__, count, i, slot)); 558 559 RATE_TEST(info->stray == 0, ("%s: [3] Stray bytes: %u\n", __func__, info->stray)); 560 561 return i; 562 } 563 564 static struct pcm_feederdesc feeder_rate_desc[] = { 565 {FEEDER_RATE, AFMT_S8, AFMT_S8, 0}, 566 {FEEDER_RATE, AFMT_S16_LE, AFMT_S16_LE, 0}, 567 {FEEDER_RATE, AFMT_S24_LE, AFMT_S24_LE, 0}, 568 {FEEDER_RATE, AFMT_S32_LE, AFMT_S32_LE, 0}, 569 {FEEDER_RATE, AFMT_S16_BE, AFMT_S16_BE, 0}, 570 {FEEDER_RATE, AFMT_S24_BE, AFMT_S24_BE, 0}, 571 {FEEDER_RATE, AFMT_S32_BE, AFMT_S32_BE, 0}, 572 {FEEDER_RATE, AFMT_S8 | AFMT_STEREO, AFMT_S8 | AFMT_STEREO, 0}, 573 {FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0}, 574 {FEEDER_RATE, AFMT_S24_LE | AFMT_STEREO, AFMT_S24_LE | AFMT_STEREO, 0}, 575 {FEEDER_RATE, AFMT_S32_LE | AFMT_STEREO, AFMT_S32_LE | AFMT_STEREO, 0}, 576 {FEEDER_RATE, AFMT_S16_BE | AFMT_STEREO, AFMT_S16_BE | AFMT_STEREO, 0}, 577 {FEEDER_RATE, AFMT_S24_BE | AFMT_STEREO, AFMT_S24_BE | AFMT_STEREO, 0}, 578 {FEEDER_RATE, AFMT_S32_BE | AFMT_STEREO, AFMT_S32_BE | AFMT_STEREO, 0}, 579 /* unsigned */ 580 {FEEDER_RATE, AFMT_U8, AFMT_U8, 0}, 581 {FEEDER_RATE, AFMT_U16_LE, AFMT_U16_LE, 0}, 582 {FEEDER_RATE, AFMT_U24_LE, AFMT_U24_LE, 0}, 583 {FEEDER_RATE, AFMT_U32_LE, AFMT_U32_LE, 0}, 584 {FEEDER_RATE, AFMT_U16_BE, AFMT_U16_BE, 0}, 585 {FEEDER_RATE, AFMT_U24_BE, AFMT_U24_BE, 0}, 586 {FEEDER_RATE, AFMT_U32_BE, AFMT_U32_BE, 0}, 587 {FEEDER_RATE, AFMT_U8 | AFMT_STEREO, AFMT_U8 | AFMT_STEREO, 0}, 588 {FEEDER_RATE, AFMT_U16_LE | AFMT_STEREO, AFMT_U16_LE | AFMT_STEREO, 0}, 589 {FEEDER_RATE, AFMT_U24_LE | AFMT_STEREO, AFMT_U24_LE | AFMT_STEREO, 0}, 590 {FEEDER_RATE, AFMT_U32_LE | AFMT_STEREO, AFMT_U32_LE | AFMT_STEREO, 0}, 591 {FEEDER_RATE, AFMT_U16_BE | AFMT_STEREO, AFMT_U16_BE | AFMT_STEREO, 0}, 592 {FEEDER_RATE, AFMT_U24_BE | AFMT_STEREO, AFMT_U24_BE | AFMT_STEREO, 0}, 593 {FEEDER_RATE, AFMT_U32_BE | AFMT_STEREO, AFMT_U32_BE | AFMT_STEREO, 0}, 594 {0, 0, 0, 0}, 595 }; 596 597 static kobj_method_t feeder_rate_methods[] = { 598 KOBJMETHOD(feeder_init, feed_rate_init), 599 KOBJMETHOD(feeder_free, feed_rate_free), 600 KOBJMETHOD(feeder_set, feed_rate_set), 601 KOBJMETHOD(feeder_get, feed_rate_get), 602 KOBJMETHOD(feeder_feed, feed_rate), 603 {0, 0} 604 }; 605 606 FEEDER_DECLARE(feeder_rate, 2, NULL); 607