kern/59208: sound patch to reduce pops and crackles and fix select on vchans

Mathew Kanner mat at hak.cnd.mcgill.ca
Wed Nov 12 01:20:17 PST 2003


>Number:         59208
>Category:       kern
>Synopsis:       sound patch to reduce pops and crackles and fix select on vchans
>Confidential:   no
>Severity:       non-critical
>Priority:       low
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Nov 12 01:20:14 PST 2003
>Closed-Date:
>Last-Modified:
>Originator:     Mathew Kanner
>Release:        FreeBSD 5.1-CURRENT i386
>Organization:
>Environment:
System: FreeBSD tube.mine.nu 5.1-CURRENT-20031021-JPSNAP FreeBSD 5.1-CURRENT-20031021-JPSNAP #0: Tue Oct 21 01:07:08 GMT 2003     root at ushi.jp.freebsd.org:/usr/obj/usr/src/sys/GENERIC  i386

	
>Description:
	[1]While playing with a oss test library with the creative labs opensource 
	soundblaster driver, I noticed that the playback tests had large pops and crackles.
	This was due to the application creating an small low fragment size expecting the
	OS to override it.  The result was such a small buffer underruns were frequent.
	[2] Vchans don't wakeup from select.

	
>How-To-Repeat:
	uh, broad strokes.

	fd=open('/dev/dsp', WRONLY|NONBLOCKING);
	...
	ioctl(setfragsize,(400,32))
	while(1)
	...
		select on fd
		write(fd,buf,fragsize)
	 
	
>Fix:

	Fix [1]: Use a bigger buffer.  I created a sysctl, hw.snd.fragsps, that upper-bound constrains the frags-per-second to a value.  128 works for me.  0 disables.
	Fix [2]: Stroll down the list of children (when present) during a chn_wakeup


--- channel.c.old	Wed Nov 12 02:42:43 2003
+++ channel.c	Wed Nov 12 03:59:31 2003
@@ -41,6 +41,10 @@
 #define DEB(x) x
 */
 
+static int chn_fragsps = 0;
+SYSCTL_INT(_hw_snd, OID_AUTO, fragsps, CTLFLAG_RW,
+	&chn_fragsps, 1, "max fragments per second, 0 to disable");
+
 static int chn_targetirqrate = 32;
 TUNABLE_INT("hw.snd.targetirqrate", &chn_targetirqrate);
 
@@ -59,7 +63,7 @@
 	return err;
 }
 SYSCTL_PROC(_hw_snd, OID_AUTO, targetirqrate, CTLTYPE_INT | CTLFLAG_RW,
-	0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "");
+	0, sizeof(int), sysctl_hw_snd_targetirqrate, "I", "default fragment targets this IRQ rate");
 static int report_soft_formats = 1;
 SYSCTL_INT(_hw_snd, OID_AUTO, report_soft_formats, CTLFLAG_RW,
 	&report_soft_formats, 1, "report software-emulated formats");
@@ -113,10 +117,17 @@
 chn_wakeup(struct pcm_channel *c)
 {
     	struct snd_dbuf *bs = c->bufsoft;
+        struct pcmchan_children *pce;
 
-	CHN_LOCKASSERT(c);
-	if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
-		selwakeup(sndbuf_getsel(bs));
+//	CHN_LOCKASSERT(c);
+	if (SLIST_EMPTY(&c->children)) {
+		if (SEL_WAITING(sndbuf_getsel(bs)) && chn_polltrigger(c))
+			selwakeup(sndbuf_getsel(bs));
+	} else {
+		SLIST_FOREACH(pce, &c->children, link) {
+			chn_wakeup(pce->channel);
+		}
+	}
 	wakeup(bs);
 }
 
@@ -931,7 +942,7 @@
 {
 	struct snd_dbuf *b = c->bufhard;
 	struct snd_dbuf *bs = c->bufsoft;
-	int bufsz, irqhz, tmp, ret;
+	int irqhz, tmp, ret;
 
 	CHN_LOCKASSERT(c);
 	if (!CANCHANGE(c) || (c->flags & CHN_F_MAPPED))
@@ -960,14 +971,23 @@
 			DEB(printf("%s: updating (%d, %d)\n", __func__, blkcnt, blksz));
 		}
 	} else {
+		if ( chn_fragsps != 0 && 
+			sndbuf_getbps(bs) * sndbuf_getspd(bs) / blksz > chn_fragsps) 
+		{
+			blksz = sndbuf_getbps(bs) * sndbuf_getspd(bs) / chn_fragsps;
+			tmp = 32;
+			while (tmp < blksz)
+				tmp <<= 1;
+			blksz = tmp;
+			if (blksz * blkcnt > CHN_2NDBUFMAXSIZE)
+				blkcnt = CHN_2NDBUFMAXSIZE / blksz;
+		}
 		ret = EINVAL;
 		if ((blksz < 16) || (blkcnt < 2) || (blkcnt * blksz > CHN_2NDBUFMAXSIZE))
 			goto out;
 		ret = 0;
 		c->flags |= CHN_F_HAS_SIZE;
 	}
-
-	bufsz = blkcnt * blksz;
 
 	ret = sndbuf_remalloc(bs, blkcnt, blksz);
 	if (ret)
	


>Release-Note:
>Audit-Trail:
>Unformatted:


More information about the freebsd-bugs mailing list