xref: /freebsd/sys/dev/sound/pcm/feeder_rate.c (revision 2b743a9e9ddc6736208dc8ca1ce06ce64ad20a19)
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