]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/sound/pcm/feeder_rate.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / sound / pcm / feeder_rate.c
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);