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
This commit is contained in:
Ariff Abdullah 2006-01-22 15:06:49 +00:00
parent 8c0d9af5bf
commit d9bd844573
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=154684
5 changed files with 226 additions and 45 deletions

View File

@ -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"));
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"));
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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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.
*/

View File

@ -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;
}