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:
parent
8c0d9af5bf
commit
d9bd844573
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
*/
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user