diff options
-rw-r--r-- | sys/dev/sound/pcm/channel.c | 40 | ||||
-rw-r--r-- | sys/dev/sound/pcm/feeder.c | 124 | ||||
-rw-r--r-- | sys/dev/sound/pcm/feeder.h | 4 | ||||
-rw-r--r-- | sys/dev/sound/pcm/feeder_fmt.c | 66 | ||||
-rw-r--r-- | sys/dev/sound/pcm/feeder_rate.c | 39 |
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; } |