]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/sound/pcm/feeder_rate.c
This commit was generated by cvs2svn to compensate for changes in r165743,
[FreeBSD/FreeBSD.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  * 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);