summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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