Skip site navigation (1)Skip section navigation (2)
Date:      Fri, 8 Jun 2012 13:22:50 +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-8@freebsd.org
Subject:   svn commit: r236754 - stable/8/sys/dev/sound/pcm
Message-ID:  <201206081322.q58DMolQ061701@svn.freebsd.org>

next in thread | raw e-mail | index | archive | help
Author: mav
Date: Fri Jun  8 13:22:50 2012
New Revision: 236754
URL: http://svn.freebsd.org/changeset/base/236754

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/8/sys/dev/sound/pcm/buffer.c
  stable/8/sys/dev/sound/pcm/buffer.h
  stable/8/sys/dev/sound/pcm/channel.c
Directory Properties:
  stable/8/sys/   (props changed)

Modified: stable/8/sys/dev/sound/pcm/buffer.c
==============================================================================
--- stable/8/sys/dev/sound/pcm/buffer.c	Fri Jun  8 13:10:18 2012	(r236753)
+++ stable/8/sys/dev/sound/pcm/buffer.c	Fri Jun  8 13:22:50 2012	(r236754)
@@ -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/8/sys/dev/sound/pcm/buffer.h
==============================================================================
--- stable/8/sys/dev/sound/pcm/buffer.h	Fri Jun  8 13:10:18 2012	(r236753)
+++ stable/8/sys/dev/sound/pcm/buffer.h	Fri Jun  8 13:22:50 2012	(r236754)
@@ -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/8/sys/dev/sound/pcm/channel.c
==============================================================================
--- stable/8/sys/dev/sound/pcm/channel.c	Fri Jun  8 13:10:18 2012	(r236753)
+++ stable/8/sys/dev/sound/pcm/channel.c	Fri Jun  8 13:22:50 2012	(r236754)
@@ -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?201206081322.q58DMolQ061701>