summaryrefslogtreecommitdiffstats
path: root/sys/dev/sound/pcm
diff options
context:
space:
mode:
authorariff <ariff@FreeBSD.org>2006-01-22 15:06:49 +0000
committerariff <ariff@FreeBSD.org>2006-01-22 15:06:49 +0000
commit3a3753e6561928c0918f5f5c65642d7661d68468 (patch)
tree324fadb042b8f178336f219e5a0d1749d51d031f /sys/dev/sound/pcm
parent7c1b5ba95ea0c00aed508231bd1a7a1c41210cd5 (diff)
downloadFreeBSD-src-3a3753e6561928c0918f5f5c65642d7661d68468.zip
FreeBSD-src-3a3753e6561928c0918f5f5c65642d7661d68468.tar.gz
Various fixups:
feeder.h: feeder.c: - Implement scoring mechanisme to select best format for conversion. This is actually part of newer format chaining procedures which will be commited someday. Confusion during chaining process solved by this scoring since it will try to reduce list of from/to formats to a single, best format. Related PR: kern/91683 channel.c: - Simplify feeder building process since we have smarter format chaining. feeder_fmt.c: - Add few more sign conversion feeders for 24 and 32 bit format. feeder_rate.c: - Force buffer / bytes allignment. Unaligned buffer may cause panics during recording on pure 32bit sample format if it involves feeder_rate as part of feeders chain. Tested on: ATI IXP, force 32bit recording. MFC after: 5 days
Diffstat (limited to 'sys/dev/sound/pcm')
-rw-r--r--sys/dev/sound/pcm/channel.c40
-rw-r--r--sys/dev/sound/pcm/feeder.c124
-rw-r--r--sys/dev/sound/pcm/feeder.h4
-rw-r--r--sys/dev/sound/pcm/feeder_fmt.c66
-rw-r--r--sys/dev/sound/pcm/feeder_rate.c39
5 files changed, 227 insertions, 46 deletions
diff --git a/sys/dev/sound/pcm/channel.c b/sys/dev/sound/pcm/channel.c
index 802a502..0b16dc4 100644
--- a/sys/dev/sound/pcm/channel.c
+++ b/sys/dev/sound/pcm/channel.c
@@ -1360,19 +1360,15 @@ chn_buildfeeder(struct pcm_channel *c)
return EOPNOTSUPP;
}
- if ((type == FEEDER_RATE &&
- !fmtvalid(fc->desc->in, fmtlist))
- || c->feeder->desc->out != fc->desc->in) {
- DEB(printf("build fmtchain from 0x%x to 0x%x: ", c->feeder->desc->out, fc->desc->in));
- tmp[0] = fc->desc->in;
- tmp[1] = 0;
- if (chn_fmtchain(c, tmp) == 0) {
- DEB(printf("failed\n"));
-
- return ENODEV;
- }
- DEB(printf("ok\n"));
+ DEB(printf("build fmtchain from 0x%08x to 0x%08x: ", c->feeder->desc->out, fc->desc->in));
+ tmp[0] = fc->desc->in;
+ tmp[1] = 0;
+ if (chn_fmtchain(c, tmp) == 0) {
+ DEB(printf("failed\n"));
+
+ return ENODEV;
}
+ DEB(printf("ok\n"));
err = chn_addfeeder(c, fc, fc->desc);
if (err) {
@@ -1384,21 +1380,15 @@ chn_buildfeeder(struct pcm_channel *c)
}
}
- if (fmtvalid(c->feeder->desc->out, fmtlist)
- && !(c->direction == PCMDIR_REC &&
- c->format != c->feeder->desc->out))
- hwfmt = c->feeder->desc->out;
- else {
- if (c->direction == PCMDIR_REC) {
- tmp[0] = c->format;
- tmp[1] = 0;
- hwfmt = chn_fmtchain(c, tmp);
- } else
- hwfmt = chn_fmtchain(c, fmtlist);
- }
+ if (c->direction == PCMDIR_REC) {
+ tmp[0] = c->format;
+ tmp[1] = 0;
+ hwfmt = chn_fmtchain(c, tmp);
+ } else
+ hwfmt = chn_fmtchain(c, fmtlist);
if (hwfmt == 0 || !fmtvalid(hwfmt, fmtlist)) {
- DEB(printf("Invalid hardware format: 0x%x\n", hwfmt));
+ DEB(printf("Invalid hardware format: 0x%08x\n", hwfmt));
return ENODEV;
}
diff --git a/sys/dev/sound/pcm/feeder.c b/sys/dev/sound/pcm/feeder.c
index 9bbbfc8..c48344d 100644
--- a/sys/dev/sound/pcm/feeder.c
+++ b/sys/dev/sound/pcm/feeder.c
@@ -265,9 +265,9 @@ feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *sto
struct feedertab_entry *fte;
struct pcm_feeder *try, *ret;
- /* printf("trying %s (%x -> %x)...\n", source->class->name, source->desc->in, source->desc->out); */
+ DEB(printf("trying %s (0x%08x -> 0x%08x)...\n", source->class->name, source->desc->in, source->desc->out));
if (fmtvalid(source->desc->out, to)) {
- /* printf("got it\n"); */
+ DEB(printf("got it\n"));
return source;
}
@@ -295,11 +295,99 @@ feeder_fmtchain(u_int32_t *to, struct pcm_feeder *source, struct pcm_feeder *sto
return NULL;
}
+int
+chn_fmtscore(u_int32_t fmt)
+{
+ if (fmt & AFMT_32BIT)
+ return 32;
+ if (fmt & AFMT_24BIT)
+ return 24;
+ if (fmt & AFMT_16BIT)
+ return 16;
+ if (fmt & (AFMT_U8|AFMT_S8))
+ return 8;
+ return 4;
+}
+
+u_int32_t
+chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts)
+{
+ u_int32_t best;
+ int i, score, score2, oldscore;
+
+ best = 0;
+ score = chn_fmtscore(fmt);
+ oldscore = 0;
+ for (i = 0; fmts[i] != 0; i++) {
+ score2 = chn_fmtscore(fmts[i]);
+ if (oldscore == 0 || (score2 == score) ||
+ (score2 > oldscore && score2 < score) ||
+ (score2 < oldscore && score2 > score)) {
+ best = fmts[i];
+ oldscore = score2;
+ }
+ }
+ return best;
+}
+
+u_int32_t
+chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts)
+{
+ u_int32_t best;
+ int i, score, score2, oldscore;
+
+ best = 0;
+ score = chn_fmtscore(fmt);
+ oldscore = 0;
+ for (i = 0; fmts[i] != 0; i++) {
+ if ((fmt & AFMT_STEREO) == (fmts[i] & AFMT_STEREO)) {
+ score2 = chn_fmtscore(fmts[i]);
+ if (oldscore == 0 || (score2 == score) ||
+ (score2 > oldscore && score2 < score) ||
+ (score2 < oldscore && score2 > score)) {
+ best = fmts[i];
+ oldscore = score2;
+ }
+ }
+ }
+ return best;
+}
+
+u_int32_t
+chn_fmtbest(u_int32_t fmt, u_int32_t *fmts)
+{
+ u_int32_t best1, best2;
+ int score, score1, score2;
+
+ best1 = chn_fmtbeststereo(fmt, fmts);
+ best2 = chn_fmtbestbit(fmt, fmts);
+
+ if (best1 != 0 && best2 != 0) {
+ if (fmt & AFMT_STEREO)
+ return best1;
+ else {
+ score = chn_fmtscore(fmt);
+ score1 = chn_fmtscore(best1);
+ score2 = chn_fmtscore(best2);
+ if (score2 == score)
+ return best2;
+ else if (score1 == score || score1 > score2)
+ return best1;
+ return best2;
+ }
+ } else if (best2 == 0)
+ return best1;
+ else if (best1 == 0)
+ return best2;
+
+ return best1;
+}
+
u_int32_t
chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
{
struct pcm_feeder *try, *del, *stop;
- u_int32_t tmpfrom[2], best, *from;
+ u_int32_t tmpfrom[2], tmpto[2], best, *from;
int i, max, bestmax;
KASSERT(c != NULL, ("c == NULL"));
@@ -311,19 +399,40 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
if (c->direction == PCMDIR_REC && c->feeder->desc->type == FEEDER_ROOT) {
from = chn_getcaps(c)->fmtlist;
+ if (fmtvalid(to[0], from))
+ from = to;
+ else {
+ best = chn_fmtbest(to[0], from);
+ if (best != 0) {
+ tmpfrom[0] = best;
+ tmpfrom[1] = 0;
+ from = tmpfrom;
+ }
+ }
} else {
tmpfrom[0] = c->feeder->desc->out;
tmpfrom[1] = 0;
from = tmpfrom;
+ if (to[1] != 0) {
+ if (fmtvalid(tmpfrom[0], to)) {
+ tmpto[0] = tmpfrom[0];
+ tmpto[1] = 0;
+ to = tmpto;
+ } else {
+ best = chn_fmtbest(tmpfrom[0], to);
+ if (best != 0) {
+ tmpto[0] = best;
+ tmpto[1] = 0;
+ to = tmpto;
+ }
+ }
+ }
}
i = 0;
best = 0;
bestmax = 100;
- while (from[i] != 0)
- i++;
- while (i > 0) {
- i--;
+ while (from[i] != 0) {
c->feeder->desc->out = from[i];
try = NULL;
max = 0;
@@ -341,6 +450,7 @@ chn_fmtchain(struct pcm_channel *c, u_int32_t *to)
try = try->source;
feeder_destroy(del);
}
+ i++;
}
if (best == 0)
return 0;
diff --git a/sys/dev/sound/pcm/feeder.h b/sys/dev/sound/pcm/feeder.h
index 1a9c37b..545fd24 100644
--- a/sys/dev/sound/pcm/feeder.h
+++ b/sys/dev/sound/pcm/feeder.h
@@ -53,6 +53,10 @@ struct pcm_feeder {
void feeder_register(void *p);
struct feeder_class *feeder_getclass(struct pcm_feederdesc *desc);
+int chn_fmtscore(u_int32_t fmt);
+u_int32_t chn_fmtbestbit(u_int32_t fmt, u_int32_t *fmts);
+u_int32_t chn_fmtbeststereo(u_int32_t fmt, u_int32_t *fmts);
+u_int32_t chn_fmtbest(u_int32_t fmt, u_int32_t *fmts);
u_int32_t chn_fmtchain(struct pcm_channel *c, u_int32_t *to);
int chn_addfeeder(struct pcm_channel *c, struct feeder_class *fc, struct pcm_feederdesc *desc);
int chn_removefeeder(struct pcm_channel *c);
diff --git a/sys/dev/sound/pcm/feeder_fmt.c b/sys/dev/sound/pcm/feeder_fmt.c
index ee791e3..5609b99 100644
--- a/sys/dev/sound/pcm/feeder_fmt.c
+++ b/sys/dev/sound/pcm/feeder_fmt.c
@@ -945,6 +945,72 @@ static kobj_method_t feeder_sign16le_methods[] = {
{0, 0}
};
FEEDER_DECLARE(feeder_sign16le, 0, NULL);
+
+static int
+feed_sign24le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int i, j = FEEDER_FEED(f->source, c, b, count, source);
+
+ if (j < 3) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, j);
+ return 0;
+ }
+ FMT_TEST(j % 3, "%s: Bytes not 24bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(j -= j % 3);
+ i = j;
+ while (i > 0) {
+ b[--i] ^= 0x80;
+ i -= 2;
+ }
+ return j;
+}
+static struct pcm_feederdesc feeder_sign24le_desc[] = {
+ {FEEDER_FMT, AFMT_U24_LE, AFMT_S24_LE, 0},
+ {FEEDER_FMT, AFMT_U24_LE|AFMT_STEREO, AFMT_S24_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S24_LE, AFMT_U24_LE, 0},
+ {FEEDER_FMT, AFMT_S24_LE|AFMT_STEREO, AFMT_U24_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_sign24le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_sign24le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_sign24le, 0, NULL);
+
+static int
+feed_sign32le(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
+ uint32_t count, void *source)
+{
+ int i, j = FEEDER_FEED(f->source, c, b, count, source);
+
+ if (j < 4) {
+ FMT_TRACE("%s: Not enough data (Got: %d bytes)\n",
+ __func__, j);
+ return 0;
+ }
+ FMT_TEST(j & 3, "%s: Bytes not 32bit aligned.\n", __func__);
+ FMT_ALIGNBYTE(j &= ~3);
+ i = j;
+ while (i > 0) {
+ b[--i] ^= 0x80;
+ i -= 3;
+ }
+ return j;
+}
+static struct pcm_feederdesc feeder_sign32le_desc[] = {
+ {FEEDER_FMT, AFMT_U32_LE, AFMT_S32_LE, 0},
+ {FEEDER_FMT, AFMT_U32_LE|AFMT_STEREO, AFMT_S32_LE|AFMT_STEREO, 0},
+ {FEEDER_FMT, AFMT_S32_LE, AFMT_U32_LE, 0},
+ {FEEDER_FMT, AFMT_S32_LE|AFMT_STEREO, AFMT_U32_LE|AFMT_STEREO, 0},
+ {0, 0, 0, 0},
+};
+static kobj_method_t feeder_sign32le_methods[] = {
+ KOBJMETHOD(feeder_feed, feed_sign32le),
+ {0, 0}
+};
+FEEDER_DECLARE(feeder_sign32le, 0, NULL);
/*
* Sign conversion end.
*/
diff --git a/sys/dev/sound/pcm/feeder_rate.c b/sys/dev/sound/pcm/feeder_rate.c
index 977d77d..a8f55e8 100644
--- a/sys/dev/sound/pcm/feeder_rate.c
+++ b/sys/dev/sound/pcm/feeder_rate.c
@@ -69,6 +69,7 @@
SND_DECLARE_FILE("$FreeBSD$");
#define RATE_ASSERT(x, y) /* KASSERT(x,y) */
+#define RATE_TEST(x, y) /* if (!(x)) printf y */
#define RATE_TRACE(x...) /* printf(x) */
MALLOC_DEFINE(M_RATEFEEDER, "ratefeed", "pcm rate feeder");
@@ -700,10 +701,16 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
* sampling without causing missing samples or excessive buffer
* feeding.
*/
- RATE_ASSERT(count >= 4 && count % 4 == 0,
- ("%s: Count size not byte integral\n", __func__));
+ RATE_TEST(count >= 4 && (count & 3) == 0,
+ ("%s: Count size not byte integral (%d)\n", __func__, count));
+ if (count < 4)
+ return 0;
count >>= 1;
+ count &= ~1;
slot = (((info->gx * (count >> 1)) + info->gy - info->alpha - 1) / info->gy) << 1;
+ RATE_TEST((slot & 1) == 0, ("%s: Slot count not sample integral (%d)\n",
+ __func__, slot));
+ slot &= ~1;
/*
* Optimize buffer feeding aggresively to ensure calculated slot
* can be fitted nicely into available buffer free space, hence
@@ -729,23 +736,25 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
for (;;) {
fetch = info->bufsz - info->bpos;
RATE_ASSERT(fetch >= 0,
- ("%s: Buffer overrun: %d > %d\n",
+ ("%s: [1] Buffer overrun: %d > %d\n",
__func__, info->bpos, info->bufsz));
if (slot < fetch)
fetch = slot;
+ fetch &= ~1;
if (fetch > 0) {
- RATE_ASSERT(fetch % 2 == 0,
- ("%s: Fetch size not sample integral\n",
- __func__));
+ RATE_TEST((fetch & 1) == 0,
+ ("%s: Fetch size not sample integral (%d)\n",
+ __func__, fetch));
fetch = FEEDER_FEED(f->source, c,
(uint8_t *)(info->buffer + info->bpos),
fetch << 1, source);
if (fetch == 0)
break;
- RATE_ASSERT(fetch % 4 == 0,
- ("%s: Fetch size not byte integral\n",
- __func__));
+ RATE_TEST((fetch & 3) == 0,
+ ("%s: Fetch size not byte integral (%d)\n",
+ __func__, fetch));
fetch >>= 1;
+ fetch &= ~1;
info->bpos += fetch;
slot -= fetch;
RATE_ASSERT(slot >= 0,
@@ -759,20 +768,21 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
break;
}
if (info->pos == info->bpos) {
- RATE_ASSERT(info->pos == 2,
+ RATE_TEST(info->pos == 2,
("%s: EOF while in progress\n", __func__));
break;
}
RATE_ASSERT(info->pos <= info->bpos,
- ("%s: Buffer overrun: %d > %d\n", __func__,
+ ("%s: [2] Buffer overrun: %d > %d\n", __func__,
info->pos, info->bpos));
RATE_ASSERT(info->pos < info->bpos,
("%s: Zero buffer!\n", __func__));
- RATE_ASSERT((info->bpos - info->pos) % 2 == 0,
- ("%s: Buffer not sample integral\n", __func__));
+ RATE_ASSERT(((info->bpos - info->pos) & 1) == 0,
+ ("%s: Buffer not sample integral (%d)\n",
+ __func__, info->bpos - info->pos));
i += info->convert(info, dst + i, count - i);
RATE_ASSERT(info->pos <= info->bpos,
- ("%s: Buffer overrun: %d > %d\n",
+ ("%s: [3] Buffer overrun: %d > %d\n",
__func__, info->pos, info->bpos));
if (info->pos == info->bpos) {
/*
@@ -788,6 +798,7 @@ feed_rate(struct pcm_feeder *f, struct pcm_channel *c, uint8_t *b,
if (i == count)
break;
}
+ RATE_TEST(count == i, ("Expect: %u , Got: %u\n", count << 1, i << 1));
return i << 1;
}
OpenPOWER on IntegriCloud