feeder_rate.c revision 75320
1/*
2 * Copyright (c) 1999 Cameron Grant <gandalf@vilnya.demon.co.uk>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 *
26 * $FreeBSD: head/sys/dev/sound/pcm/feeder_rate.c 75320 2001-04-08 20:26:22Z cg $
27 */
28
29#include <dev/sound/pcm/sound.h>
30
31#include "feeder_if.h"
32
33MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder");
34
35#define FEEDBUFSZ	8192
36#undef FEEDER_DEBUG
37
38struct feed_rate_info {
39	u_int32_t src, dst;
40	int srcpos, srcinc;
41	int16_t *buffer;
42	u_int16_t alpha;
43};
44
45static int
46feed_rate_setup(struct pcm_feeder *f)
47{
48	struct feed_rate_info *info = f->data;
49
50	info->srcinc = (info->src << 16) / info->dst;
51	/* srcinc is 16.16 fixed point increment for srcpos for each dstpos */
52	info->srcpos = 0;
53	return 0;
54}
55
56static int
57feed_rate_set(struct pcm_feeder *f, int what, int value)
58{
59	struct feed_rate_info *info = f->data;
60
61	switch(what) {
62	case FEEDRATE_SRC:
63		info->src = value;
64		break;
65	case FEEDRATE_DST:
66		info->dst = value;
67		break;
68	default:
69		return -1;
70	}
71	return feed_rate_setup(f);
72}
73
74static int
75feed_rate_init(struct pcm_feeder *f)
76{
77	struct feed_rate_info *info;
78
79	info = malloc(sizeof(*info), M_RATEFEEDER, M_WAITOK | M_ZERO);
80	if (info == NULL)
81		return ENOMEM;
82	info->buffer = malloc(FEEDBUFSZ, M_RATEFEEDER, M_WAITOK | M_ZERO);
83	if (info->buffer == NULL) {
84		free(info, M_RATEFEEDER);
85		return ENOMEM;
86	}
87	info->src = DSP_DEFAULT_SPEED;
88	info->dst = DSP_DEFAULT_SPEED;
89	info->alpha = 0;
90	f->data = info;
91	return feed_rate_setup(f);
92}
93
94static int
95feed_rate_free(struct pcm_feeder *f)
96{
97	struct feed_rate_info *info = f->data;
98
99	if (info) {
100		if (info->buffer)
101			free(info->buffer, M_RATEFEEDER);
102		free(info, M_RATEFEEDER);
103	}
104	f->data = NULL;
105	return 0;
106}
107
108static int
109feed_rate(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
110{
111	struct feed_rate_info *info = f->data;
112	int16_t *destbuf = (int16_t *)b;
113	int fetch, v, alpha, hidelta, spos, dpos;
114
115	/*
116	 * at this point:
117	 * info->srcpos is 24.8 fixed offset into the fetchbuffer.  0 <= srcpos <= 0xff
118	 *
119	 * our input and output are always AFMT_S16LE stereo.  this simplifies things.
120	 */
121
122	/*
123	 * we start by fetching enough source data into our buffer to generate
124	 * about as much as was requested.  we put it at offset 2 in the
125	 * buffer so that we can interpolate from the last samples in the
126	 * previous iteration- when we finish we will move our last samples
127	 * to the start of the buffer.
128	 */
129	spos = 0;
130	dpos = 0;
131
132	/* fetch is in bytes */
133	fetch = (count * info->srcinc) >> 16;
134	fetch = min(fetch, FEEDBUFSZ - 4) & ~3;
135	if (fetch == 0)
136		return 0;
137	fetch = FEEDER_FEED(f->source, c, ((u_int8_t *)info->buffer) + 4, fetch, source);
138	fetch /= 2;
139
140	alpha = info->alpha;
141	hidelta = min(info->srcinc >> 16, 1) * 2;
142	while ((spos + hidelta + 1) < fetch) {
143		v = (info->buffer[spos] * (0xffff - alpha)) + (info->buffer[spos + hidelta] * alpha);
144		destbuf[dpos++] = v >> 16;
145
146		v = (info->buffer[spos + 1] * (0xffff - alpha)) + (info->buffer[spos + hidelta + 1] * alpha);
147		destbuf[dpos++] = v >> 16;
148
149		alpha += info->srcinc;
150		spos += (alpha >> 16) * 2;
151		alpha &= 0xffff;
152
153	}
154	info->alpha = alpha & 0xffff;
155	info->buffer[0] = info->buffer[spos - hidelta];
156	info->buffer[1] = info->buffer[spos - hidelta + 1];
157
158	count = dpos * 2;
159	return count;
160}
161
162static struct pcm_feederdesc feeder_rate_desc[] = {
163	{FEEDER_RATE, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
164	{0},
165};
166static kobj_method_t feeder_rate_methods[] = {
167    	KOBJMETHOD(feeder_init,		feed_rate_init),
168    	KOBJMETHOD(feeder_free,		feed_rate_free),
169    	KOBJMETHOD(feeder_set,		feed_rate_set),
170    	KOBJMETHOD(feeder_feed,		feed_rate),
171	{ 0, 0 }
172};
173FEEDER_DECLARE(feeder_rate, 2, NULL);
174
175
176