Skip site navigation (1)Skip section navigation (2)
Date:      Mon, 19 Mar 2012 07:24:26 +0000 (UTC)
From:      Alexander Motin <mav@FreeBSD.org>
To:        src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-stable@freebsd.org, svn-src-stable-9@freebsd.org
Subject:   svn commit: r233164 - stable/9/sys/dev/sound/pcm
Message-ID:  <201203190724.q2J7OQ3i056795@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
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."
 	 */



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201203190724.q2J7OQ3i056795>