svn commit: r233164 - stable/9/sys/dev/sound/pcm
Alexander Motin
mav at FreeBSD.org
Mon Mar 19 07:24:28 UTC 2012
Author: mav
Date: Mon Mar 19 07:24:26 2012
New Revision: 233164
URL: http://svn.freebsd.org/changeset/base/233164
Log:
MFC r230845:
Make sound(4) more flexible in setting soft buffer and block sizes when
hardware imposes strict limitations on hard buffer and block sizes.
Previous code set soft buffer to be no smaller then hard buffer. On some
cards with fixed 64K physical buffer that caused up to 800ms play latency.
New code allows to set soft buffer size down to just two blocks of the hard
buffer and to not write more then that size ahead to the hardware buffer.
As result of that change I was able to reduce full practically measured
record-playback loop delay in those conditions down to only about 115ms
with theoretical playback latency of only about 50ms.
New code works fine for both vchans and direct cases. In both cases sound(4)
tries to follow hw.snd.latency_profile and hw.snd.latency values and
application-requested buffer and block sizes as much as limitation of two
hardware blocks allows.
Modified:
stable/9/sys/dev/sound/pcm/buffer.c
stable/9/sys/dev/sound/pcm/buffer.h
stable/9/sys/dev/sound/pcm/channel.c
Directory Properties:
stable/9/sys/ (props changed)
Modified: stable/9/sys/dev/sound/pcm/buffer.c
==============================================================================
--- stable/9/sys/dev/sound/pcm/buffer.c Mon Mar 19 07:15:42 2012 (r233163)
+++ stable/9/sys/dev/sound/pcm/buffer.c Mon Mar 19 07:24:26 2012 (r233164)
@@ -301,6 +301,15 @@ sndbuf_fillsilence(struct snd_dbuf *b)
b->rl = b->bufsize;
}
+void
+sndbuf_fillsilence_rl(struct snd_dbuf *b, u_int rl)
+{
+ if (b->bufsize > 0)
+ memset(sndbuf_getbuf(b), sndbuf_zerodata(b->fmt), b->bufsize);
+ b->rp = 0;
+ b->rl = min(b->bufsize, rl);
+}
+
/**
* @brief Reset buffer w/o flushing statistics
*
Modified: stable/9/sys/dev/sound/pcm/buffer.h
==============================================================================
--- stable/9/sys/dev/sound/pcm/buffer.h Mon Mar 19 07:15:42 2012 (r233163)
+++ stable/9/sys/dev/sound/pcm/buffer.h Mon Mar 19 07:24:26 2012 (r233164)
@@ -74,6 +74,7 @@ int sndbuf_remalloc(struct snd_dbuf *b,
void sndbuf_reset(struct snd_dbuf *b);
void sndbuf_clear(struct snd_dbuf *b, unsigned int length);
void sndbuf_fillsilence(struct snd_dbuf *b);
+void sndbuf_fillsilence_rl(struct snd_dbuf *b, u_int rl);
void sndbuf_softreset(struct snd_dbuf *b);
void sndbuf_clearshadow(struct snd_dbuf *b);
Modified: stable/9/sys/dev/sound/pcm/channel.c
==============================================================================
--- stable/9/sys/dev/sound/pcm/channel.c Mon Mar 19 07:15:42 2012 (r233163)
+++ stable/9/sys/dev/sound/pcm/channel.c Mon Mar 19 07:24:26 2012 (r233164)
@@ -395,24 +395,28 @@ chn_wrfeed(struct pcm_channel *c)
{
struct snd_dbuf *b = c->bufhard;
struct snd_dbuf *bs = c->bufsoft;
- unsigned int amt;
+ unsigned int amt, want, wasfree;
CHN_LOCKASSERT(c);
if ((c->flags & CHN_F_MMAP) && !(c->flags & CHN_F_CLOSING))
sndbuf_acquire(bs, NULL, sndbuf_getfree(bs));
- amt = sndbuf_getfree(b);
+ wasfree = sndbuf_getfree(b);
+ want = min(sndbuf_getsize(b),
+ imax(0, sndbuf_xbytes(sndbuf_getsize(bs), bs, b) -
+ sndbuf_getready(b)));
+ amt = min(wasfree, want);
if (amt > 0)
sndbuf_feed(bs, b, c, c->feeder, amt);
/*
* Possible xruns. There should be no empty space left in buffer.
*/
- if (sndbuf_getfree(b) > 0)
+ if (sndbuf_getready(b) < want)
c->xruns++;
- if (sndbuf_getfree(b) < amt)
+ if (sndbuf_getfree(b) < wasfree)
chn_wakeup(c);
}
@@ -721,7 +725,8 @@ chn_start(struct pcm_channel *c, int for
}
if (c->parentchannel == NULL) {
if (c->direction == PCMDIR_PLAY)
- sndbuf_fillsilence(b);
+ sndbuf_fillsilence_rl(b,
+ sndbuf_xbytes(sndbuf_getsize(bs), bs, b));
if (snd_verbose > 3)
device_printf(c->dev,
"%s(): %s starting! (%s/%s) "
@@ -1728,7 +1733,7 @@ chn_resizebuf(struct pcm_channel *c, int
int blkcnt, int blksz)
{
struct snd_dbuf *b, *bs, *pb;
- int sblksz, sblkcnt, hblksz, hblkcnt, limit = 1;
+ int sblksz, sblkcnt, hblksz, hblkcnt, limit = 0, nsblksz, nsblkcnt;
int ret;
CHN_LOCKASSERT(c);
@@ -1748,7 +1753,6 @@ chn_resizebuf(struct pcm_channel *c, int
return EINVAL;
else {
c->latency = latency;
- limit = 0;
}
bs = c->bufsoft;
@@ -1783,19 +1787,22 @@ chn_resizebuf(struct pcm_channel *c, int
*/
sblksz = round_blksz(blksz, sndbuf_getalign(bs));
sblkcnt = round_pow2(blkcnt);
- limit = 0;
}
if (c->parentchannel != NULL) {
- pb = CHN_BUF_PARENT(c, NULL);
+ pb = c->parentchannel->bufsoft;
CHN_UNLOCK(c);
CHN_LOCK(c->parentchannel);
chn_notify(c->parentchannel, CHN_N_BLOCKSIZE);
CHN_UNLOCK(c->parentchannel);
CHN_LOCK(c);
- limit = (limit != 0 && pb != NULL) ?
- sndbuf_xbytes(sndbuf_getsize(pb), pb, bs) : 0;
- c->timeout = c->parentchannel->timeout;
+ if (c->direction == PCMDIR_PLAY) {
+ limit = (pb != NULL) ?
+ sndbuf_xbytes(sndbuf_getsize(pb), pb, bs) : 0;
+ } else {
+ limit = (pb != NULL) ?
+ sndbuf_xbytes(sndbuf_getblksz(pb), pb, bs) * 2 : 0;
+ }
} else {
hblkcnt = 2;
if (c->flags & CHN_F_HAS_SIZE) {
@@ -1836,21 +1843,22 @@ chn_resizebuf(struct pcm_channel *c, int
CHN_LOCK(c);
if (!CHN_EMPTY(c, children)) {
- sblksz = round_blksz(
- sndbuf_xbytes(sndbuf_getsize(b) >> 1, b, bs),
+ nsblksz = round_blksz(
+ sndbuf_xbytes(sndbuf_getblksz(b), b, bs),
sndbuf_getalign(bs));
- sblkcnt = 2;
+ nsblkcnt = sndbuf_getblkcnt(b);
+ if (c->direction == PCMDIR_PLAY) {
+ do {
+ nsblkcnt--;
+ } while (nsblkcnt >= 2 &&
+ nsblksz * nsblkcnt >= sblksz * sblkcnt);
+ nsblkcnt++;
+ }
+ sblksz = nsblksz;
+ sblkcnt = nsblkcnt;
limit = 0;
- } else if (limit != 0)
- limit = sndbuf_xbytes(sndbuf_getsize(b), b, bs);
-
- /*
- * Interrupt timeout
- */
- c->timeout = ((u_int64_t)hz * sndbuf_getsize(b)) /
- ((u_int64_t)sndbuf_getspd(b) * sndbuf_getalign(b));
- if (c->timeout < 1)
- c->timeout = 1;
+ } else
+ limit = sndbuf_xbytes(sndbuf_getblksz(b), b, bs) * 2;
}
if (limit > CHN_2NDBUFMAXSIZE)
@@ -1887,6 +1895,16 @@ chn_resizebuf(struct pcm_channel *c, int
}
/*
+ * Interrupt timeout
+ */
+ c->timeout = ((u_int64_t)hz * sndbuf_getsize(bs)) /
+ ((u_int64_t)sndbuf_getspd(bs) * sndbuf_getalign(bs));
+ if (c->parentchannel != NULL)
+ c->timeout = min(c->timeout, c->parentchannel->timeout);
+ if (c->timeout < 1)
+ c->timeout = 1;
+
+ /*
* OSSv4 docs: "By default OSS will set the low water level equal
* to the fragment size which is optimal in most cases."
*/
More information about the svn-src-stable-9
mailing list