feeder_rate.c revision 170815
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
80SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/feeder_rate.c 170815 2007-06-16 03:37:28Z ariff $");
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
86MALLOC_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
97struct feed_rate_info;
98
99typedef uint32_t (*feed_rate_converter)(struct feed_rate_info *,
100							uint8_t *, uint32_t);
101
102struct 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
119int feeder_rate_min = FEEDRATE_RATEMIN;
120int feeder_rate_max = FEEDRATE_RATEMAX;
121int feeder_rate_round = FEEDRATE_ROUNDHZ;
122
123TUNABLE_INT("hw.snd.feeder_rate_min", &feeder_rate_min);
124TUNABLE_INT("hw.snd.feeder_rate_max", &feeder_rate_max);
125TUNABLE_INT("hw.snd.feeder_rate_round", &feeder_rate_round);
126
127static int
128sysctl_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}
142SYSCTL_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
146static int
147sysctl_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}
161SYSCTL_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
165static int
166sysctl_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}
180SYSCTL_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)	\
185static uint32_t									\
186feed_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
237FEEDER_RATE_CONVERT(8, int32_t, S, s, NE, ne)
238FEEDER_RATE_CONVERT(16, int32_t, S, s, LE, le)
239FEEDER_RATE_CONVERT(24, int32_t, S, s, LE, le)
240FEEDER_RATE_CONVERT(32, intpcm_t, S, s, LE, le)
241FEEDER_RATE_CONVERT(16, int32_t, S, s, BE, be)
242FEEDER_RATE_CONVERT(24, int32_t, S, s, BE, be)
243FEEDER_RATE_CONVERT(32, intpcm_t, S, s, BE, be)
244FEEDER_RATE_CONVERT(8, int32_t, U, u, NE, ne)
245FEEDER_RATE_CONVERT(16, int32_t, U, u, LE, le)
246FEEDER_RATE_CONVERT(24, int32_t, U, u, LE, le)
247FEEDER_RATE_CONVERT(32, intpcm_t, U, u, LE, le)
248FEEDER_RATE_CONVERT(16, int32_t, U, u, BE, be)
249FEEDER_RATE_CONVERT(24, int32_t, U, u, BE, be)
250FEEDER_RATE_CONVERT(32, intpcm_t, U, u, BE, be)
251
252static void
253feed_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
266static void
267feed_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
287static int
288feed_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
355static int
356feed_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
376static int
377feed_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
392static int
393feed_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
419static int
420feed_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
433static int
434feed_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
599static 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
631static 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
640FEEDER_DECLARE(feeder_rate, 2, NULL);
641