summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pcm/feeder_rate.c
diff options
context:
space:
mode:
authororion <orion@FreeBSD.org>2003-01-30 16:32:56 +0000
committerorion <orion@FreeBSD.org>2003-01-30 16:32:56 +0000
commita16f92695c9dac1fa0ebfebf80f89727eaba8b3c (patch)
tree9d654254b5724814addb4e88f4be02e6795d043f /sys/dev/sound/pcm/feeder_rate.c
parent8bfc65ceab6068576f9a219ef35ae128b2501c6c (diff)
downloadFreeBSD-src-a16f92695c9dac1fa0ebfebf80f89727eaba8b3c.zip
FreeBSD-src-a16f92695c9dac1fa0ebfebf80f89727eaba8b3c.tar.gz
o Constrain inputs to 25Hz granularity so interpolator can operate
between any pair of values in range 4-96kHz. Thanks to Ken Marks for discovering there were problems with the previous version. o Use a non-recursive gcd routine.
Diffstat (limited to 'sys/dev/sound/pcm/feeder_rate.c')
-rw-r--r--sys/dev/sound/pcm/feeder_rate.c161
1 files changed, 97 insertions, 64 deletions
diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c
index 70f1058..1bbe0f5 100644
--- a/sys/dev/sound/pcm/feeder_rate.c
+++ b/sys/dev/sound/pcm/feeder_rate.c
@@ -35,8 +35,12 @@
* designed to be compiled in the kernel and in a userland test
* harness. This is done by selectively including and excluding code
* with several portions based on whether _KERNEL is defined. It's a
- * little ugly, but very useful. The testsuite and its revisions can
- * be found at http://people.freebsd.org/~orion/feedrate/
+ * little ugly, but exceedingly useful. The testsuite and its
+ * revisions can be found at:
+ * http://people.freebsd.org/~orion/feedrate/
+ *
+ * Special thanks to Ken Marx for exposing flaws in the code and for
+ * testing revisions.
*/
#ifdef _KERNEL
@@ -45,22 +49,30 @@
#include "feeder_if.h"
SND_DECLARE_FILE("$FreeBSD$");
-
MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder");
#define RATE_ASSERT(x, y) /* KASSERT(x,y) */
-
#define RATE_TRACE(x...) /* printf(x) */
-#else /* _KERNEL */
-
-/* #define RATE_DEBUG */
-
-#include "test_rate_head.h"
-
#endif /* _KERNEL */
-#define FEEDBUFSZ 8192
+/*****************************************************************************/
+/* All of the following coefficients are coupled. They are chosen to be
+ * good in the operating space 4000-96000kHz work. Decreasing the
+ * granularity increases the required buffer size and affects the gain
+ * values at different points in the space. These values were found by
+ * running the test program with -p (probe) and some trial and error.
+ *
+ * ROUNDHZ the granularity of sample rates (fits n*11025 and n*8000).
+ * FEEDBUFSZ the amount of buffer space.
+ * MINGAIN the minimum acceptable gain in coefficients search.
+ */
+#define ROUNDHZ 25
+#define FEEDBUFSZ 8192
+#define MINGAIN 92
+
+#define RATEMIN 4000
+#define RATEMAX 96000
struct feed_rate_info;
@@ -75,10 +87,8 @@ static int
convert_stereo_down(struct feed_rate_info *info,
uint32_t src_ticks, uint32_t dst_ticks, int16_t *dst);
-
struct feed_rate_info {
uint32_t src, dst; /* source and destination rates */
- int16_t *buffer; /* input buffer */
uint16_t buffer_ticks; /* number of available samples in buffer */
uint16_t buffer_pos; /* next available sample in buffer */
uint16_t rounds; /* maximum number of cycle rounds w buffer */
@@ -90,24 +100,28 @@ struct feed_rate_info {
uint16_t channels; /* 1 = mono, 2 = stereo */
rate_convert_method convert;
+ int16_t buffer[FEEDBUFSZ];
};
-#define src_ticks_per_cycle(info) (info->dscale * info->rounds)
-
-#define dst_ticks_per_cycle(info) (info->sscale * info->rounds)
-
-#define bytes_per_tick(info) (info->channels * 2)
-
+#define bytes_per_sample 2
+#define src_ticks_per_cycle(info) (info->dscale * info->rounds)
+#define dst_ticks_per_cycle(info) (info->sscale * info->rounds)
+#define bytes_per_tick(info) (info->channels * bytes_per_sample)
#define src_bytes_per_cycle(info) \
- (src_ticks_per_cycle(info) * bytes_per_tick(info))
-
+ (src_ticks_per_cycle(info) * bytes_per_tick(info))
#define dst_bytes_per_cycle(info) \
- (dst_ticks_per_cycle(info) * bytes_per_tick(info))
+ (dst_ticks_per_cycle(info) * bytes_per_tick(info))
static uint32_t
-gcd(uint32_t n1, uint32_t n2)
+gcd(uint32_t x, uint32_t y)
{
- return n2 ? gcd(n2, n1 % n2) : n1;
+ uint32_t w;
+ while (y != 0) {
+ w = x % y;
+ x = y;
+ y = w;
+ }
+ return x;
}
static int
@@ -122,7 +136,6 @@ feed_rate_setup(struct pcm_feeder *f)
info->dscale = info->src / g;
info->alpha = 0;
- info->channels = 2;
info->buffer_ticks = 0;
info->buffer_pos = 0;
@@ -139,16 +152,24 @@ feed_rate_setup(struct pcm_feeder *f)
* src_ticks_per_cycle here since it used by src_ticks_per_cycle.
*/
info->rounds = 1;
- info->rounds = FEEDBUFSZ /
- (src_ticks_per_cycle(info) * bytes_per_tick(info)) - 1;
-
+ r = (FEEDBUFSZ - bytes_per_tick(info)) /
+ (src_ticks_per_cycle(info) * bytes_per_tick(info));
+ if (r == 0) {
+ RATE_TRACE("Insufficient buffer space for conversion %d -> %d "
+ "(%d < %d)\n", info->src, info->dst, FEEDBUFSZ,
+ src_ticks_per_cycle(info) * bytes_per_tick(info));
+ return -1;
+ }
+ info->rounds = r;
+
/*
* Find scale and roll combination that allows us to trade
* costly divide operations in the main loop for multiply-rolls.
*/
- for (l = 99; l > 90; l -= 3) {
- for (mroll = 2; mroll < 16; mroll ++) {
+ for (l = 96; l >= MINGAIN; l -= 3) {
+ for (mroll = 0; mroll < 16; mroll ++) {
mscale = (1 << mroll) / info->sscale;
+
r = (mscale * info->sscale * 100) >> mroll;
if (r > l && r <= 100) {
info->mscale = mscale;
@@ -162,22 +183,34 @@ feed_rate_setup(struct pcm_feeder *f)
}
}
}
- RATE_TRACE("Failed to find a converter within 90%% gain.");
+
+ RATE_TRACE("Failed to find a converter within %d%% gain for "
+ "%d to %d.\n", l, info->src, info->dst);
- return -1;
+ return -2;
}
static int
feed_rate_set(struct pcm_feeder *f, int what, int value)
{
struct feed_rate_info *info = f->data;
-
+ int rvalue;
+
+ if (value < RATEMIN || value > RATEMAX) {
+ return -1;
+ }
+
+ rvalue = (value / ROUNDHZ) * ROUNDHZ;
+ if (value - rvalue > ROUNDHZ / 2) {
+ rvalue += ROUNDHZ;
+ }
+
switch(what) {
case FEEDRATE_SRC:
- info->src = value;
+ info->src = rvalue;
break;
case FEEDRATE_DST:
- info->dst = value;
+ info->dst = rvalue;
break;
default:
return -1;
@@ -210,16 +243,13 @@ feed_rate_init(struct pcm_feeder *f)
info = malloc(sizeof(*info), M_RATEFEEDER, M_NOWAIT | M_ZERO);
if (info == NULL)
return ENOMEM;
- info->buffer = malloc(FEEDBUFSZ, M_RATEFEEDER, M_NOWAIT | M_ZERO);
- if (info->buffer == NULL) {
- free(info, M_RATEFEEDER);
- return ENOMEM;
- }
+
info->src = DSP_DEFAULT_SPEED;
info->dst = DSP_DEFAULT_SPEED;
+ info->channels = 2;
f->data = info;
- return feed_rate_setup(f);
+ return 0;
}
static int
@@ -228,8 +258,6 @@ feed_rate_free(struct pcm_feeder *f)
struct feed_rate_info *info = f->data;
if (info) {
- if (info->buffer)
- free(info->buffer, M_RATEFEEDER);
free(info, M_RATEFEEDER);
}
f->data = NULL;
@@ -238,9 +266,9 @@ feed_rate_free(struct pcm_feeder *f)
static int
convert_stereo_up(struct feed_rate_info *info,
- uint32_t src_ticks,
- uint32_t dst_ticks,
- int16_t *dst)
+ uint32_t src_ticks,
+ uint32_t dst_ticks,
+ int16_t *dst)
{
uint32_t max_dst_ticks;
int32_t alpha, dalpha, malpha, mroll, sp, dp, se, de, x, o;
@@ -269,7 +297,7 @@ convert_stereo_up(struct feed_rate_info *info,
dp = 0;
de = dst_ticks * 2;
/*
- * FYI: unrolling this loop manually does not help much here because
+ * Unrolling this loop manually does not help much here because
* of the alpha, malpha comparison.
*/
while (dp < de) {
@@ -294,9 +322,9 @@ convert_stereo_up(struct feed_rate_info *info,
static int
convert_stereo_down(struct feed_rate_info *info,
- uint32_t src_ticks,
- uint32_t dst_ticks,
- int16_t *dst)
+ uint32_t src_ticks,
+ uint32_t dst_ticks,
+ int16_t *dst)
{
int32_t alpha, dalpha, malpha, mroll, sp, dp, se, de, x, o, m,
mdalpha, mstep;
@@ -337,13 +365,13 @@ convert_stereo_down(struct feed_rate_info *info,
}
}
- info->buffer_pos = sp / info->channels;
+ info->buffer_pos = sp / 2;
info->alpha = alpha / info->mscale;
RATE_ASSERT(info->buffer_pos <= info->buffer_ticks,
("%s: Source overrun\n", __func__));
- return dp / info->channels;
+ return dp / 2;
}
static int
@@ -363,7 +391,7 @@ feed_rate(struct pcm_feeder *f,
while (done < count) {
/* Slurp in more data if input buffer is not full */
- if (info->buffer_ticks < src_ticks_per_cycle(info)) {
+ while (info->buffer_ticks < src_ticks_per_cycle(info)) {
uint8_t *u8b;
int fetch;
fetch = src_bytes_per_cycle(info) -
@@ -381,25 +409,30 @@ feed_rate(struct pcm_feeder *f,
("%s: buffer overfilled (%d > %d).",
__func__, info->buffer_ticks,
src_ticks_per_cycle(info)));
- if (fetch == 0 && info->buffer_pos == info->buffer_ticks)
+ if (fetch == 0)
break;
}
/* Find amount of input buffer data that should be processed */
d_ticks = (count - done) / bytes_per_tick(info);
s_ticks = info->buffer_ticks - info->buffer_pos;
+ if (info->buffer_ticks != src_ticks_per_cycle(info)) {
+ if (s_ticks > 8)
+ s_ticks -= 8;
+ else
+ s_ticks = 0;
+ }
d_ticks = info->convert(info, s_ticks, d_ticks,
- (uint16_t*)(b + done));
+ (int16_t*)(b + done));
if (d_ticks == 0)
break;
done += d_ticks * bytes_per_tick(info);
RATE_ASSERT(info->buffer_pos <= info->buffer_ticks,
("%s: buffer_ticks too big\n", __func__));
-
- RATE_TRACE("%s: ticks %5d pos %d\n",
- __func__, info->buffer_ticks, info->buffer_pos);
+ RATE_TRACE("%s: ticks %5d pos %d\n", __func__,
+ info->buffer_ticks, info->buffer_pos);
if (src_ticks_per_cycle(info) <= info->buffer_pos) {
/* End of cycle reached, copy last samples to start */
@@ -409,8 +442,8 @@ feed_rate(struct pcm_feeder *f,
bytes_per_tick(info));
RATE_ASSERT(info->alpha == 0,
- ("%s: completed cycle with alpha non-zero",
- __func__, info->alpha));
+ ("%s: completed cycle with "
+ "alpha non-zero", __func__, info->alpha));
info->buffer_pos = 0;
info->buffer_ticks = 0;
@@ -421,6 +454,10 @@ feed_rate(struct pcm_feeder *f,
("%s: generated too many bytes of data (%d > %d).",
__func__, done, count));
+ if (done != count) {
+ RATE_TRACE("Only did %d of %d\n", done, count);
+ }
+
return done;
}
@@ -440,8 +477,4 @@ static kobj_method_t feeder_rate_methods[] = {
};
FEEDER_DECLARE(feeder_rate, 2, NULL);
-#else /* _KERNEL */
-
-#include "test_rate_tail.h"
-
#endif /* _KERNEL */
OpenPOWER on IntegriCloud