Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 18 Nov 1999 21:19:55 -0500 (EST)
From:      vns@delta.odessa.ua
To:        FreeBSD-gnats-submit@freebsd.org
Subject:   kern/14990: new-pcm caveats
Message-ID:  <199911190219.VAA00758@mindspring.com>

next in thread | raw e-mail | index | archive | help

>Number:         14990
>Category:       kern
>Synopsis:       new-pcm caveats
>Confidential:   no
>Severity:       non-critical
>Priority:       medium
>Responsible:    freebsd-bugs
>State:          open
>Quarter:        
>Keywords:       
>Date-Required:
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Thu Nov 18 18:30:00 PST 1999
>Closed-Date:
>Last-Modified:
>Originator:     Vladimir N.Silyaev
>Release:        FreeBSD 4.0-CURRENT i386
>Organization:
>Environment:

FreeBSD 4.0-CURRENT i386

Crystal Sound sound card
pcm0: <CS4236> at port 0x534-0x537,0x388-0x38b,0x220-0x22f irq 7 drq 1,0 on isa0

mpg123 Version 0.59q (1999/Jan/26) software audio MPEG decoder.

>Description:
	Some times, after changing tracks, the sound card produce white
	noise instead of audio clip.

>How-To-Repeat:

	Start mpg123 program, then press ^Z key (pause the program) and
	then execute bg command. After some tries sound card begin to
	produce white noise.


>Fix:
	This error, as say my own investigation, don't depend from soundcard
	type, but caused some problem, which occured when underflow
	condition occured in the DMA buffer. The current implementation
	don't work properly with this situation at all. 
	So next patch try to solve most of the problem occured with
	play back in the such case of situations.

	After patch follow simple program, which was used for easy testing
	sound card driver in wide spectrum of formats and frequencies.

Index: channel.c
===================================================================
RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/channel.c,v
retrieving revision 1.5
diff -u -r1.5 channel.c
--- channel.c	1999/11/15 23:57:33	1.5
+++ channel.c	1999/11/19 00:25:20
@@ -37,6 +37,7 @@
 #define CANCHANGE(c) (!(c)->buffer.dl)
 
 static void chn_stintr(pcm_channel *c);
+static void chn_clearbuf(pcm_channel *c, int length);
 /*
  * SOUND OUTPUT
 
@@ -103,7 +104,7 @@
 {
 	if (ISA_DMA(&c->buffer)) {
 		/* tell isa_dma to bounce data in/out */
-    	} else panic("chn_isadmabounce called on invalid channel");
+    	} else KASSERT(1, ("chn_isadmabounce called on invalid channel"));
 }
 
 static int
@@ -153,27 +154,53 @@
  * NOTE: when we are using auto dma in the device, rl might become
  * negative.
  */
+DEB (static int chn_updatecount=0);
+
 void
 chn_dmaupdate(pcm_channel *c)
 {
 	snd_dbuf *b = &c->buffer;
-	int delta, hwptr = chn_getptr(c);
+	int delta, hwptr;
+	DEB (int b_rl=b->rl; int b_fl=b->fl; int b_rp=b->rp; int b_fp=b->fp);
 
+	hwptr = chn_getptr(c);
 	if (c->direction == PCMDIR_PLAY) {
 		delta = (b->bufsize + hwptr - b->rp) % b->bufsize;
 		b->rp = hwptr;
 		b->rl -= delta;
 		b->fl += delta;
+		DEB(if (b->rl<0) printf("OUCH!(%d) rl %d(%d) delta %d bufsize %d hwptr %d rp %d(%d)\n", chn_updatecount++, b->rl, b_rl, delta, b->bufsize, hwptr, b->rp, b_rp)); 
 	} else {
 		delta = (b->bufsize + hwptr - b->fp) % b->bufsize;
 		b->fp = hwptr;
 		b->rl += delta;
 		b->fl -= delta;
+		DEB(if (b->fl<0) printf("OUCH!(%d) fl %d(%d) delta %d bufsize %d hwptr %d fp %d(%d)\n", chn_updatecount++, b->fl, b_fl, delta, b->bufsize, hwptr, b->fp, b_fp)); 
 	}
 	b->total += delta;
 }
 
 /*
+ * Check channel for underflow occured, reset DMA buffer in case of 
+ * underflow. It must be called at spltty().
+ */
+static void
+chn_checkunderflow(pcm_channel *c)
+{
+    	snd_dbuf *b = &c->buffer;
+	
+	if (b->underflow) {
+		DEB(printf("Clear underflow condition\n"));
+		b->rp = b->fp = chn_getptr(c);
+		b->rl = 0;
+		b->fl = b->bufsize;
+	  	b->underflow=0;
+	} else {
+		chn_dmaupdate(c);
+	}
+}
+
+/*
  * Write interrupt routine. Can be called from other places (e.g.
  * to start a paused transfer), but with interrupts disabled.
  */
@@ -183,7 +210,8 @@
     	snd_dbuf *b = &c->buffer;
     	int start;
 
-    	if (b->dl) chn_dmadone(c);
+    	if (b->underflow) return; /* nothing new happened */
+	if (b->dl) chn_dmadone(c);
 
     	/*
      	* start another dma operation only if have ready data in the buffer,
@@ -198,40 +226,31 @@
     	if (start) {
 		int l;
 		chn_dmaupdate(c);
-		l = min(b->rl, c->blocksize) & DMA_ALIGN_MASK;
 		if (c->flags & CHN_F_MAPPED) l = c->blocksize;
+		else l = min(b->rl, c->blocksize) & DMA_ALIGN_MASK;
 		/*
 	 	* check if we need to reprogram the DMA on the sound card.
-	 	* This happens if the size has changed _and_ the new size
-	 	* is smaller, or it matches the blocksize.
+	 	* This happens if the size has changed from zero
 	 	*
-	 	* 0 <= l <= blocksize
-	 	* 0 <= dl <= blocksize
-	 	* reprog if (dl == 0 || l != dl)
-	 	* was:
-		* l != b->dl && (b->dl == 0 || l < b->dl || l == c->blocksize)
 	 	*/
-		if (b->dl == 0 || l != b->dl) {
-	    		/* size has changed. Stop and restart */
-	    		DEB(printf("wrintr: bsz %d -> %d, rp %d rl %d\n",
-				   b->dl, l, b->rp, b->rl));
-	    		if (b->dl) chn_trigger(c, PCMTRIG_STOP);
-	    		b->dl = l; /* record new transfer size */
+		if (b->dl == 0) {
+			/* Start DMA operation */
+	    		b->dl = c->blocksize ; /* record new transfer size */
 	    		chn_trigger(c, PCMTRIG_START);
+		} else if (b->dl != l) {
+			/* 
+			 * we are near to underflow condition, so to prevent
+			 * audio 'clicks' clear next 1.5*dl bytes
+			 */
+			 chn_clearbuf(c, (b->dl*3)/2);
 		}
     	} else {
 		/* cannot start a new dma transfer */
 		DEB(printf("cannot start wr-dma flags 0x%08x rp %d rl %d\n",
 			   c->flags, b->rp, b->rl));
-		if (b->dl) { /* was active */
-	    		b->dl = 0;
-	    		chn_trigger(c, PCMTRIG_STOP);
-#if 0
-            		if (c->flags & CHN_F_WRITING)
-				DEB(printf("got wrint while reloading\n"));
-	    		else if (b->rl <= 0) /* XXX added 980110 lr */
-				chn_resetbuf(c);
-#endif
+		if (b->dl) { /* DMA was active */
+			b->underflow = 1; /* set underflow flag */
+			chn_clearbuf(c, b->bufsize); /* and clear all DMA buffer */
 		}
     	}
 }
@@ -255,9 +274,10 @@
 int
 chn_write(pcm_channel *c, struct uio *buf)
 {
-	int 		a, l, w, timeout, ret = 0;
+	int 		a, l, w, timeout, ret = 0, rc;
 	long		s;
 	snd_dbuf       *b = &c->buffer;
+	int		threshold, maxthreshold, minthreshold;
 
 	if (c->flags & CHN_F_WRITING) {
 		/* This shouldn't happen and is actually silly
@@ -267,35 +287,47 @@
 		return EBUSY;
 	}
 	a = (1 << c->align) - 1;
+	maxthreshold=(b->dl/4+a)&(~a);
+	minthreshold=a;
 	c->flags |= CHN_F_WRITING;
-	while ((c->smegcnt + buf->uio_resid) > a) {
-		s = spltty();
-		chn_dmaupdate(c);
-		splx(s);
-		if (b->fl < DMA_ALIGN_THRESHOLD) {
+	s = spltty();
+	chn_checkunderflow(c);
+	splx(s);
+	while ((buf->uio_resid+c->smegcnt) > minthreshold ) { /* Don't allow write unaligned data */
+		threshold = min ((buf->uio_resid+c->smegcnt), maxthreshold);
+		if (b->fl < threshold) {
 			if (c->flags & CHN_F_NBIO) break;
 			timeout = (buf->uio_resid >= b->dl)? hz : 1;
-			ret = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
-			if (ret == EINTR) chn_abort(c);
-			if (ret == EINTR || ret == ERESTART) break;
-			ret = 0;
-			continue;
-		}
+			rc = tsleep(b, PRIBIO | PCATCH, "pcmwr", timeout);
+			if (rc == 0 || rc == EWOULDBLOCK) {
+				s = spltty();
+				chn_checkunderflow(c);
+				splx(s);				
+				if (b->fl < minthreshold) continue; /* write only alligned chunk of data */
+			} else {
+#if 0
+			 	if (ret == EINTR) chn_abort(c);
+#endif			
+				ret=rc;
+				break;
+			}
+		} 
 		/* ensure we always have a whole number of samples */
-		l = min(b->fl, b->bufsize - b->fp) & ~a;
-		if (l == 0) break;
+		l = min(b->fl, b->bufsize - b->fp);
+		KASSERT(!(l & a),("unaligned write %d, %d fl %d fp %d bufsize %d", l, a + 1, b->fl, b->fp, b->bufsize));
 		w = c->feeder->feed(c->feeder, c, b->buf + b->fp, l, buf);
-		if (w == 0) panic("no feed");
+		KASSERT(w, ("chn_write: no feed"));
 		s = spltty();
 		b->rl += w;
 		b->fl -= w;
 		b->fp = (b->fp + w) % b->bufsize;
 	      	splx(s);
-		if (b->rl && !b->dl) chn_stintr(c);
+		DEB(if(1) printf("write %d bytes fp %d rl %d\n",w ,b->fp, b->rl));
+		if (!b->dl) chn_stintr(c);
 	}
 	if ((ret == 0) && (buf->uio_resid > 0)) {
 		l = buf->uio_resid;
-		if ((c->smegcnt + l) >= SMEGBUFSZ) panic("resid overflow %d", l);
+		KASSERT( (c->smegcnt + l) < SMEGBUFSZ, ("resid overflow %d", l));
 		uiomove(c->smegbuf + c->smegcnt, l, buf);
 		c->smegcnt += l;
 	}
@@ -472,44 +504,61 @@
 	return 0;
 }
 
+static void
+chn_clearbuf(pcm_channel *c, int length)
+{
+int i;
+u_int16_t data, *p;
+
+	snd_dbuf *b = &c->buffer;
+	/* rely on length & DMA_ALIGN_MASK == 0 */
+	length&=DMA_ALIGN_MASK;
+	if (c->hwfmt & AFMT_SIGNED)	data = 0x00; else data = 0x80;
+	if (c->hwfmt & AFMT_16BIT)	data <<= 8; else data |= data << 8;
+	if (c->hwfmt & AFMT_BIGENDIAN)
+		data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00);
+	for (i = b->fp, p=(u_int16_t*)(b->buf+b->fp) ; i < b->bufsize && length; i += 2, length-=2)
+		*p++ = data;
+	for (i = 0, p=(u_int16_t*)b->buf; i < b->bufsize && length; i += 2, length-=2)
+		*p++ = data;
+
+	return;
+}
+
 void
 chn_resetbuf(pcm_channel *c)
 {
 	snd_dbuf *b = &c->buffer;
-	u_int16_t data, *p;
-	u_int32_t i;
 
 	c->smegcnt = 0;
 	c->buffer.sample_size = 1;
 	c->buffer.sample_size <<= (c->hwfmt & AFMT_STEREO)? 1 : 0;
 	c->buffer.sample_size <<= (c->hwfmt & AFMT_16BIT)? 1 : 0;
-	/* rely on bufsize & 3 == 0 */
-	if (c->hwfmt & AFMT_SIGNED)	data = 0x00; else data = 0x80;
-	if (c->hwfmt & AFMT_16BIT)	data <<= 8; else data |= data << 8;
-	if (c->hwfmt & AFMT_BIGENDIAN)
-		data = ((data >> 8) & 0x00ff) | ((data << 8) & 0xff00);
-	for (i = 0, p = (u_int16_t *)b->buf; i < b->bufsize; i += 2)
-		*p++ = data;
 	b->rp = b->fp = 0;
 	b->dl = b->rl = 0;
+	b->fl = b->bufsize;
+	chn_clearbuf(c, b->bufsize);
 	b->prev_total = b->total = 0;
 	b->prev_int_count = b->int_count = 0;
 	b->first_poll = 1;
-	b->fl = b->bufsize;
+	b->underflow=0;
 }
 
 void
 buf_isadma(snd_dbuf *b, int go)
 {
 	if (ISA_DMA(b)) {
-        	if (go == PCMTRIG_START) isa_dmastart(b->dir | B_RAW, b->buf,
-						      b->bufsize, b->chan);
-		else {
+        	if (go == PCMTRIG_START) {
+			DEB(printf("buf 0x%p ISA DMA started\n", b));
+			isa_dmastart(b->dir | B_RAW, b->buf,
+					b->bufsize, b->chan);
+		} else {
+			DEB(printf("buf 0x%p ISA DMA stopped\n", b));
 			isa_dmastop(b->chan);
 			isa_dmadone(b->dir | B_RAW, b->buf, b->bufsize,
 				    b->chan);
 		}
-    	} else panic("buf_isadma called on invalid channel");
+    	} else KASSERT(1, ("buf_isadma called on invalid channel"));
 }
 
 int
@@ -519,7 +568,7 @@
 		int i = b->dl? isa_dmastatus(b->chan) : b->bufsize;
 		if (i < 0) i = 0;
 		return b->bufsize - i;
-    	} else panic("buf_isadmaptr called on invalid channel");
+    	} else KASSERT(1, ("buf_isadmaptr called on invalid channel"));
 	return -1;
 }
 
@@ -543,7 +592,7 @@
 	    		ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmsyn", 1);
 	    		splx(s);
 	    		if (ret == ERESTART || ret == EINTR) {
-				printf("tsleep returns %d\n", ret);
+				DEB(printf("chn_sync: tsleep returns %d\n", ret));
 				return -1;
 	    		}
 		} else break;
@@ -605,18 +654,18 @@
     	DEB(printf("snd_flush c->flags 0x%08x\n", c->flags));
     	c->flags |= CHN_F_CLOSING;
     	if (c->direction != PCMDIR_PLAY) chn_abort(c);
-    	else while (b->dl) {
+    	else if (b->dl) while (!b->underflow) {
 		/* still pending output data. */
 		ret = tsleep((caddr_t)b, PRIBIO | PCATCH, "pcmflu", hz);
-		chn_dmaupdate(c);
+		DEB(chn_dmaupdate(c));
 		DEB(printf("snd_sync: now rl : fl  %d : %d\n", b->rl, b->fl));
-		if (ret == EINTR) {
-	    		printf("tsleep returns %d\n", ret);
+		if (ret == EINTR || ret == ERESTART) {
+	    		DEB(printf("chn_flush: tsleep returns %d\n", ret));
 	    		return -1;
 		}
 		if (ret && --count == 0) {
-	    		printf("timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n",
-			       b->rl, c->flags);
+	    		DEB(printf("chn_flush: timeout flushing dbuf_out, cnt 0x%x flags 0x%x\n",\
+			       b->rl, c->flags));
 	    		break;
 		}
     	}
@@ -641,11 +690,13 @@
 	if ((c->flags & CHN_F_INIT) && CANCHANGE(c)) {
 		chn_setformat(c, c->format);
 		chn_setspeed(c, c->speed);
-		chn_setblocksize(c, c->blocksize);
+		chn_setblocksize(c, (c->flags & CHN_F_HAS_SIZE) ? c->blocksize : 0);
 		chn_setvolume(c, (c->volume >> 8) & 0xff, c->volume & 0xff);
 		c->flags &= ~CHN_F_INIT;
 		return 1;
 	}
+	if (CANCHANGE(c) && !(c->flags & CHN_F_HAS_SIZE) )  
+		chn_setblocksize(c, 0); /* Apply new block size */
 	return 0;
 }
 
@@ -716,10 +767,10 @@
 		c->flags &= ~CHN_F_HAS_SIZE;
 		if (blksz >= 2) c->flags |= CHN_F_HAS_SIZE;
 		blksz = abs(blksz);
-		if (blksz < 2) blksz = (c->buffer.sample_size * c->speed) >> 2;
+		if (blksz < 2) blksz = c->buffer.sample_size * (c->speed / 4); /* 1/4 sec */
 		RANGE(blksz, 1024, c->buffer.bufsize / 4);
-		blksz &= ~3;
-		c->blocksize = c->setblocksize(c->devinfo, blksz);
+		blksz &= DMA_ALIGN_MASK;
+		c->blocksize = c->setblocksize(c->devinfo, blksz)&DMA_ALIGN_MASK;
 		return c->blocksize;
 	}
 	c->blocksize = blksz;
@@ -736,7 +787,14 @@
 int
 chn_getptr(pcm_channel *c)
 {
-	return c->getptr(c->devinfo);
+	int hwptr;
+	int a = (1 << c->align) - 1;
+
+	hwptr=c->getptr(c->devinfo);
+	/* don't allow unaligned values in the hwa ptr */
+	hwptr &= ~a ; /* Apply channel align mask */
+	hwptr &= DMA_ALIGN_MASK; /* Apply DMA align mask */
+	return hwptr;
 }
 
 pcmchan_caps *
Index: datatypes.h
===================================================================
RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/datatypes.h,v
retrieving revision 1.4
diff -u -r1.4 datatypes.h
--- datatypes.h	1999/11/15 23:57:33	1.4
+++ datatypes.h	1999/11/18 21:13:22
@ -58,7 +58,7 @@
  */
 
 struct _snd_dbuf {
-        char *buf;
+        u_int8_t *buf;
         int bufsize;
         volatile int rp, fp; /* pointers to the ready and free area */
         volatile int dl; /* transfer size */
@@ -71,6 +71,7 @@
 	u_long prev_total; /* copy of the above when GETxPTR called */
 	int first_poll;
 	bus_dmamap_t dmamap;
+	int underflow;
 };
 
 typedef int (pcmfeed_init_t)(pcm_feeder *feeder);
Index: dsp.c
===================================================================
RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/dsp.c,v
retrieving revision 1.7
diff -u -r1.7 dsp.c
--- dsp.c	1999/11/13 18:31:29	1.7
+++ dsp.c	1999/11/18 22:40:53
@ -51,8 +51,8 @@
 static int
 getchns(snddev_info *d, int chan, pcm_channel **rdch, pcm_channel **wrch)
 {
-	if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
-		panic("read and write both prioritised");
+	KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
+		("getchns: read and write both prioritised"));
 	if (d->flags & SD_F_SIMPLEX) {
 		*rdch = (d->flags & SD_F_PRIO_RD)? d->arec[chan] : &d->fakechan;
 		*wrch = (d->flags & SD_F_PRIO_WR)? d->aplay[chan] : &d->fakechan;
@@ -66,8 +66,8 @@
 static void
 setchns(snddev_info *d, int chan)
 {
-	if ((d->flags & SD_F_PRIO_SET) == SD_F_PRIO_SET)
-		panic("read and write both prioritised");
+	KASSERT((d->flags & SD_F_PRIO_SET) != SD_F_PRIO_SET, \
+		("getchns: read and write both prioritised"));
 	d->flags |= SD_F_DIR_SET;
 	if (d->flags & SD_F_EVILSB16) {
 		if ((d->flags & SD_F_PRIO_RD) && (d->aplay[chan])) {
@@ -159,7 +159,10 @@
 		chn_abort(rdch);
 		rdch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
 	}
-	if (wrch) wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
+	if (wrch) {
+		chn_flush(wrch);
+		wrch->flags &= ~(CHN_F_BUSY | CHN_F_RUNNING | CHN_F_MAPPED);
+	}
 	d->aplay[chan] = NULL;
 	d->arec[chan] = NULL;
 	return 0;
@@ -173,8 +176,8 @@
 	if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_RD;
 	if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
 	getchns(d, chan, &rdch, &wrch);
-	if (!rdch || !(rdch->flags & CHN_F_BUSY))
-		panic("dsp_read: non%s channel", rdch? "busy" : "existant");
+	KASSERT(wrch, ("dsp_read: nonexistant channel"));
+	KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_read: nonbusy channel"));
 	if (rdch->flags & CHN_F_MAPPED) return EINVAL;
 	if (!(rdch->flags & CHN_F_RUNNING)) {
 		rdch->flags |= CHN_F_RUNNING;
@@ -191,8 +194,8 @@
 	if (!(d->flags & SD_F_PRIO_SET)) d->flags |= SD_F_PRIO_WR;
 	if (!(d->flags & SD_F_DIR_SET)) setchns(d, chan);
 	getchns(d, chan, &rdch, &wrch);
-	if (!wrch || !(wrch->flags & CHN_F_BUSY))
-		panic("dsp_write: non%s channel", wrch? "busy" : "existant");
+	KASSERT(wrch, ("dsp_write: nonexistant channel"));
+	KASSERT(wrch->flags & CHN_F_BUSY, ("dsp_write: nonbusy channel"));
 	if (wrch->flags & CHN_F_MAPPED) return EINVAL;
 	if (!(wrch->flags & CHN_F_RUNNING)) {
 		wrch->flags |= CHN_F_RUNNING;
@@ -519,7 +522,7 @@
     	case SOUND_PCM_READ_FILTER:
 		/* dunno what these do, don't sound important */
     	default:
-		DEB(printf("default ioctl snd%d fn 0x%08x fail\n", unit, cmd));
+		DEB(printf("default ioctl chan%d fn 0x%08lx fail\n", chan, cmd));
 		ret = EINVAL;
 		break;
     	}
Index: feeder.c
===================================================================
RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/feeder.c,v
retrieving revision 1.6
diff -u -r1.6 feeder.c
--- feeder.c	1999/11/15 23:57:32	1.6
+++ feeder.c	1999/11/18 22:42:48
@ -108,24 +108,21 @@
 static int
 feed_root(pcm_feeder *feeder, pcm_channel *ch, u_int8_t *buffer, u_int32_t count, struct uio *stream)
 {
-	int ret, tmp = 0, c = 0;
-	if (!count) panic("feed_root: count == 0");
-	count &= ~((1 << ch->align) - 1);
-	if (!count) panic("feed_root: aligned count == 0");
-	if (ch->smegcnt > 0) {
-		c = min(ch->smegcnt, count);
-		bcopy(ch->smegbuf, buffer, c);
-		ch->smegcnt -= c;
+	int ret, c=0;
+	KASSERT(count, ("feed_root: count == 0"));
+ 	count &= ~((1 << ch->align) - 1);
+	KASSERT(count, ("feed_root: aligned count == 0"));
+ 	if (ch->smegcnt > 0) {
+ 		c = min(ch->smegcnt, count);
+ 		bcopy(ch->smegbuf, buffer, c);
+ 		ch->smegcnt -= c;
+ 	}
+ 	count = min(count, stream->uio_resid);
+	if (count) {
+		ret = uiomove(buffer, count, stream);
+		KASSERT(ret==0, ("feed_root: uiomove failed"));
 	}
-	while ((stream->uio_resid > 0) && (c < count)) {
-		tmp = stream->uio_resid;
-		ret = uiomove(buffer + c, count - c, stream);
-		if (ret) panic("feed_root: uiomove failed");
-		tmp -= stream->uio_resid;
-		c += tmp;
-	}
-	if (!c) panic("feed_root: uiomove didn't");
-	return c;
+	return c+count;
 }
 pcm_feeder feeder_root = { "root", 0, NULL, NULL, feed_root };
 
Index: sound.c
===================================================================
RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/sound.c,v
retrieving revision 1.7
diff -u -r1.7 sound.c
--- sound.c	1999/11/06 05:31:47	1.7
+++ sound.c	1999/11/18 19:05:02
@ -352,7 +352,7 @@
     	int dev, chan;
     	snddev_info *d = get_snddev_info(i_dev, NULL, &dev, &chan);
 
-    	DEB(printf("sndpoll dev 0x%04x events 0x%08x\n", i_dev, events));
+    	DEB(printf("sndpoll d 0x%p dev 0x%04x events 0x%08x\n", d, dev, events));
 
     	if (d == NULL) return ENXIO;
 
Index: sound.h
===================================================================
RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/sound.h,v
retrieving revision 1.3
diff -u -r1.3 sound.h
--- sound.h	1999/09/28 21:43:35	1.3
+++ sound.h	1999/11/18 19:05:02
@ -160,7 +160,7 @@
 #define BVDDB(x) if (bootverbose) x
 
 #ifndef DEB
-#define DEB(x)
+#define DEB(x) 
 #endif
 
 int pcm_addchan(device_t dev, int dir, pcm_channel *templ, void *devinfo);
Index: isa/mss.c
===================================================================
RCS file: /home/vns/cvs/FreeBSD//src/sys/dev/pcm/isa/mss.c,v
retrieving revision 1.32
diff -u -r1.32 mss.c
--- mss.c	1999/11/15 17:02:32	1.32
+++ mss.c	1999/11/18 22:14:26
@ -348,7 +348,7 @@
 		mss->opti_offset =
 			(rman_get_start(mss->conf_base) & ~3) + 2
 			- rman_get_start(mss->conf_base);
-		printf("mss_init: opti_offset=%d\n", mss->opti_offset);
+		BVDDB(printf("mss_init: opti_offset=%d\n", mss->opti_offset));
     		opti_wr(mss, 4, 0xd6); /* fifo empty, OPL3, audio enable, SB3.2 */
     		ad_write(mss, 10, 2); /* enable interrupts */
     		opti_wr(mss, 6, 2);  /* MCIR6: mss enable, sb disable */
@@ -933,9 +933,10 @@
 		if (FULL_DUPLEX(mss)) ad_write(mss, 24, ~c); /* ack selectively */
 		else io_wr(mss, MSS_STATUS, 0);	/* Clear interrupt status */
     	}
-    	if (i == 10) printf("mss_intr: irq, but not from mss\n");
-    	else if (served == 0) {
-		printf("mss_intr: unexpected irq with reason %x\n", c);
+    	if (i == 10) {
+		BVDDB(printf("mss_intr: irq, but not from mss\n"));
+	} else if (served == 0) {
+		BVDDB(printf("mss_intr: unexpected irq with reason %x\n", c));
 		/*
 	 	* this should not happen... I have no idea what to do now.
 	 	* maybe should do a sanity check and restart dmas ?
@@ -1043,7 +1044,7 @@
     	u_char   prev;
 
     	if ((mss->bd_flags & BD_F_MCE_BIT) == 0) {
-		printf("--- hey, leave_MCE: MCE bit was not set!\n");
+		DEB(printf("--- hey, leave_MCE: MCE bit was not set!\n"));
 		return;
     	}
 
@@ -1245,8 +1246,8 @@
         	ad_write(mss, 9, m);
         	if (ad_read(mss, 9) == m) break;
     	}
-    	if (retry == 0) printf("start dma, failed to set bit 0x%02x 0x%02x\n",
-			       m, ad_read(mss, 9));
+    	if (retry == 0) BVDDB(printf("stop dma, failed to set bit 0x%02x 0x%02x\n", \
+			       m, ad_read(mss, 9)));
     	return 0;
 }
 
@@ -1407,7 +1408,7 @@
 #if 0
     	reason = io_rd(mss, MSS_STATUS);
     	if (!(reason & 1)) {/* no int, maybe a shared line ? */
-		printf("intr: flag 0, mcir11 0x%02x\n", ad_read(mss, 11));
+		DEB(printf("intr: flag 0, mcir11 0x%02x\n", ad_read(mss, 11)));
 		return;
     	}
 #endif
@@ -1424,8 +1425,8 @@
 		DEB(printf("Warning: MPU interrupt\n");)
 		mc11 |= 0x20;
     	}
-    	if (mc11 & masked) printf("irq reset failed, mc11 0x%02x, 0x%02x\n",
-                              	  mc11, masked);
+    	if (mc11 & masked) BVDDB(printf("irq reset failed, mc11 0x%02x, 0x%02x\n",\
+                              	  mc11, masked));
     	masked |= mc11;
     	/*
      	* the nice OPTi931 sets the IRQ line before setting the bits in
@@ -1438,7 +1439,7 @@
 	    		if (--loops) goto again;
 	    		else DDB(printf("intr, but mc11 not set\n");)
 		}
-		if (loops == 0) printf("intr, nothing in mcir11 0x%02x\n", mc11);
+		if (loops == 0) BVDDB(printf("intr, nothing in mcir11 0x%02x\n", mc11));
 		return;
     	}


===================================================================


===================================================================

# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	Makefile
#	README
#	sound.c
#
echo x - Makefile
sed 's/^X//' >Makefile << 'END-of-Makefile'
XPROG=	sound
XSRCS=	sound.c
XLDADD= -lm
XNOMAN=	yes
X#DEBUG_FLAGS=	-g
XCFLAGS= -Wall
X
X.include <bsd.prog.mk>
END-of-Makefile
echo x - README
sed 's/^X//' >README << 'END-of-README'
XSound driver testing utility.
X
XThis program simple produce series of harmonic signals and silence to
Xsoundcard. When program launched without arguments, it's produce
X5 secs 1KHz tone, than 3 secs pause, and than again tone and pause.
XWhen tone produced status changed from 'sound playing' to 'sound sync',
Xand when pause status changed to 'silent'. When sound card's working
Xproperly the signal must be to have right tone frequency and changing
Xstatus must be synchronized within change of soundcard output.
X
XThis program have the following command line arguments:
X-h  -   Print short information about command line arguments
X-?  -   the same
X-i  - 	Catch INT signal, so you may press Ctrl-C, and sound output must
X 	my stay the same, without any clicks or noise.      
X-d <device>
X    	Change device to 'device', default /dev/dsp
X-T <periods>
X	Change number of tone/pause to 'periods', default 2
X-a <nsec>
X	Change duration of tone to 'nsec', default 5
X-s <nsec>
X	Change duration of pause to 'nsec', default 2
X-p <freq>
X	Change frequency of generated tone, default 1000Hz
X-f <formats>
X	Set list of formats, delimited by coma (','), for each of
X	which the sound card will be tested, for example U8,STEREO|U16_LE.
X	Default STEREO|U16_LE
X-r <rates>
X	Set list of rates, delimited by coma (','), for each of
X	which the sound card will be tested, for example 8000,11050.
X	Default 44100.
X-b <blocksize>
X	Set the size of blocks, which will be used as arg to write routine.
X
X
XAuthor:
XVladimir N. Silyaev  vns@delta.odessa.ua
X
END-of-README
echo x - sound.c
sed 's/^X//' >sound.c << 'END-of-sound.c'
X/*
X * sound.c
X * 
X * Soundcard/driver testing utility
X *
X * Copyright by Vladimir N. Silyaev 1999
X * 
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions are
X * met: 1. Redistributions of source code must retain the above copyright
X * notice, this list of conditions and the following disclaimer. 2.
X * Redistributions in binary form must reproduce the above copyright notice,
X * this list of conditions and the following disclaimer in the documentation
X * and/or other materials provided with the distribution.
X * 
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY
X * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
X * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
X * DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
X * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
X * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
X * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
X * SUCH DAMAGE.
X * 
X * $Id: sound.c,v 1.7 1999/11/19 01:05:13 vsilyaev Exp $
X * 
X */
X
X#include <unistd.h>
X#include <stdlib.h>
X#include <stdio.h>
X#include <strings.h>
X#include <string.h>
X#include <fcntl.h>
X#include <math.h>
X#include <ctype.h>
X#include <assert.h>
X#include <errno.h>
X#include <signal.h>
X#include <err.h>
X#include <sysexits.h>
X#include <sys/time.h>
X#include <sys/soundcard.h>
X
X#define DEFAULT_RATE	44100
X#define	DEFAULT_FORMAT	(AFMT_U16_LE|AFMT_STEREO)
X#define DEFAULT_FREQ	1000
X#define	DEFAULT_PLAY_TIME 5
X#define DEFAULT_PAUSE_TIME	3
X#define DEFAULT_NPERIODS	2
X#define DEFAULT_BLOCK_SIZE	16384
X
Xtypedef struct{
X u_long *values;
X size_t length;
X} l_array;
X
Xtypedef struct {
X l_array formats;
X l_array rates;
X char	*device;
X int	 play_freq;
X int	 play_time;
X int	 pause_time;
X int	 nperiods;
X int	 catch_sigint;
X int	 write_block_size;
X} sound_options;
X
Xstatic sound_options _app_options;
Xstatic sound_options *app_options=&_app_options;
X
Xtypedef struct {
X char *name;
X int fd; /* File descriptor */
X snd_capabilities info;
X u_long format,rate;
X} sound_info ;
Xstatic int sound_change_param(sound_info *snd, u_long rate, u_long format);
X
Xtypedef struct {
X u_long format;
X u_long rate;
X int   freq;
X float f_freq;
X int   nsamples;
X int   sample_len;
X int   align;
X float t_sample;
X union {
X  void  *data;
X  u_int8_t *data8;
X  u_int8_t (*data8_s)[2];
X  u_int16_t *data16;
X  u_int16_t (*data16_s)[2];
X } data;
X} sound_sample;
X
Xtypedef struct {
X u_long	offset; /* in samples, not in bytes */
X} sample_play;
X
Xstatic int
Xl_array_init(l_array *array)
X{
X	bzero(array, sizeof(*array));
X	array->values=NULL;
X	array->length=0;
X	return 0;
X}
X
Xstatic int
Xl_array_shutdown(l_array *array)
X{
X	free(array->values);
X	return 0;
X}
X
Xstatic int
Xl_array_push(l_array *array, u_long value)
X{
X	array->values=realloc(array->values,(array->length+1)*sizeof(*array->values));
X	array->values[array->length++]=value;
X	return 0;
X}
X
Xstatic int
Xl_array_get(l_array *array, int index, u_long *pvalues)
X{
X	if (index>=array->length) return -1;
X	if (pvalues) *pvalues=array->values[index];
X	return 0;
X}
X
Xstatic int
Xsound_options_init(sound_options *options)
X{
X	bzero(options, sizeof(*options));
X	l_array_init(&options->formats);
X	l_array_init(&options->rates);
X	options->device=NULL;
X	return 0;
X}
X
Xstatic int
Xsound_options_shutdown(sound_options *options)
X{
X	l_array_shutdown(&options->formats);
X	l_array_shutdown(&options->rates);
X	return 0;
X}
X
X
Xstatic const char fields_sep[] = ",";
X
Xstatic int
Xfill_rates(l_array *rates, char *str)
X{
Xchar *word;
Xint rate;
X
X	while ( (word=strsep(&str, fields_sep)) != NULL ) {
X		rate = atoi(word);
X		if (rate==0) {
X			fprintf(stderr, "Invalid bit rate '%s'\n", word);
X			continue;
X		}
X		l_array_push(rates, rate);
X	}
X	return 0;
X}
X
Xstatic const struct {
Xu_long	bit;
Xconst char *name;
X} known_formats[] = {
X#define FORMAT(name) {AFMT_##name, #name}	
X	FORMAT(U8),
X	FORMAT(U16_LE),
X	FORMAT(STEREO) 
X#undef FORMAT
X};
X
Xstatic int
Xstricmp(const char *str1,const char *str2)
X{
Xint ch1,ch2;
X
X	for(;;) {
X		ch1=*str1++;
X	  	ch2=*str2++;
X		ch1=toupper(ch1);
X		ch2=toupper(ch2);
X		if (ch1!=ch2) return ch1-ch2;
X		if (ch1==0) return 0;
X	}
X	/* NOT REACHED */
X}
X
Xstatic u_long
Xparse_format(const char *sformat)
X{
Xchar *str_alloc=strdup(sformat);
Xconst int n_known_formats=sizeof(known_formats)/sizeof(*known_formats);
Xu_long format=0;
Xint i;
Xu_long bit;
Xint nbits=0;
Xchar *str,*word;
X
X	str=str_alloc;
X	while ( (word=strsep(&str, "|")) != NULL) {
X		for (bit=0,i=0;i<n_known_formats;i++) {
X			if (stricmp(known_formats[i].name, word)==0) {
X				bit=known_formats[i].bit;
X				break;
X			}
X		}
X		if (!bit) {
X			fprintf(stderr, "Unknown format specifier '%s'\n", word);
X		} else {
X			format|=bit;
X			if (bit!=AFMT_STEREO) nbits++;
X		}
X	}
X	if (nbits!=1) {
X		format=0;
X	}
X	free(str_alloc);
X	return format;
X}
X
Xstatic int
Xfill_formats(l_array *formats, char *str)
X{
Xchar *word;
Xu_long format;
X
X	while ( (word=strsep(&str, fields_sep)) != NULL ) {
X		format = parse_format(word);
X		if (format==0)
X			fprintf(stderr, "Invalid format '%s'\n", word);
X		else	
X			l_array_push(formats, format);
X	}
X	return 0;
X}
X
Xstatic void
Xusage(const char *name)
X{
X       fprintf(stderr,
X		 "usage: %s [-h/?] [-i] [-d <device>] [-T <periods>] [-a <nsec>] [-s <nsec>] [-p <freq>] [-f <formats>] [-r <rates>] [-b <blocksize>]"
X	      "\n", name);
X	exit(EX_USAGE);
X}
X
Xstatic int
Xsound_options_fill(sound_options *options, int argc, char *argv[])
X{
Xint option;
X	
X	options->play_freq=0;
X	options->play_time=0;
X	options->pause_time=0;
X	options->nperiods=0;
X	options->device=NULL;
X	options->catch_sigint=0;
X	options->write_block_size=0;
X	while( (option=getopt(argc, argv, "ib:d:T:a:s:p:r:f:h?")) != -1 ) {
X		switch(option) {
X		case 'i':
X		 options->catch_sigint=1;
X		 break;
X		case 'd':
X		 options->device=strdup(optarg);
X		 break;
X		case 'T':
X		 options->nperiods=atoi(optarg);
X		 break;
X		case 'a':
X		 options->play_time=atoi(optarg);
X		 break;
X		case 's':
X		 options->pause_time=atoi(optarg);
X		 break;
X		case 'p':
X		 options->play_freq=atoi(optarg);
X		 break;
X		case 'b':
X		 options->write_block_size=atoi(optarg);
X		 break;
X		case 'r':
X		 fill_rates(&options->rates,optarg);
X		 break;
X		case 'f':
X		 fill_formats(&options->formats,optarg);
X		 break;
X		case 'h':
X		case '?':
X		default:
X		 usage(argv[0]);
X		 /* NOT REACHED */
X		}
X	}
X	if (l_array_get(&options->rates, 0, NULL)<0) 
X		l_array_push(&options->rates, DEFAULT_RATE);
X	if (l_array_get(&options->formats, 0, NULL)<0)
X		l_array_push(&options->formats, DEFAULT_FORMAT);
X	if (options->play_freq<=0) {
X		options->play_freq=DEFAULT_FREQ;
X	}
X	if (options->play_time<=0) {
X		options->play_time=DEFAULT_PLAY_TIME;
X	}
X	if (options->pause_time<=0) {
X		options->pause_time=DEFAULT_PAUSE_TIME;
X	}
X	if (options->nperiods<=0) {
X		options->nperiods=DEFAULT_NPERIODS;
X	}
X	if (options->write_block_size<=0) {
X		options->write_block_size=DEFAULT_BLOCK_SIZE;
X	}
X	if (options->device==NULL) {
X		options->device=strdup("/dev/dsp");
X	}
X	return 0;
X}
X
Xstatic int 
Xfind_nearest_for_freq(int rate,int freq)
X{
Xint i;
Xint nsamples;
Xfloat diff;
Xfloat min_diff=freq;
Xint min_periods=1;
X
X	for (i=1;i<1024;i++) {
X		nsamples=(int)ceil((i*(float)rate)/freq);
X		diff=fabs(freq-rate/nsamples);
X		if (diff<min_diff) {
X			min_periods=i;
X		}
X	}
X	return min_periods;
X}
X
Xstatic int
Xsample_init(sound_sample *sample, int format, int rate, int freq)
X{
Xint i;
Xint nperiods;
Xfloat f_sample;
Xconst float amp=0.5;
Xint data8,data16;
X
X	bzero(sample, sizeof(*sample));
X	sample->format=format;
X	sample->rate=rate;
X	sample->freq=freq;
X	switch(format&~AFMT_STEREO) {
X	case AFMT_U8:
X	   sample->sample_len=1;
X	   break;
X	case AFMT_U16_LE:
X	   sample->sample_len=2;
X	   break;
X	default:
X	   return -1;
X	}
X	if (format&AFMT_STEREO) sample->sample_len*=2;
X
X	sample->align=sample->sample_len;
X	nperiods=find_nearest_for_freq(rate,freq);
X	sample->nsamples=(int)ceil((nperiods*(float)rate)/freq);
X#if 0
X	fprintf(stderr, "Real freq %.1f HZ\n",((float)nperiods*rate)/sample->nsamples);
X#endif
X	sample->t_sample=(2*M_PI*nperiods)/sample->nsamples;
X	sample->data.data=malloc(sample->nsamples*sample->sample_len);
X	assert(sample->data.data);
X	for (i=0;i<sample->nsamples;i++) {
X	 f_sample=amp*sin(sample->t_sample*i);
X	 data8=(int)floor(f_sample*128+0.5);
X	 data16=(int)floor(f_sample*32768+0.5);
X	 switch(format) {
X	 case AFMT_U8:
X		 sample->data.data8[i]=(u_int8_t)(data8+128);
X		 break;
X	 case AFMT_U8|AFMT_STEREO:
X		 sample->data.data8_s[i][0]=
X		 sample->data.data8_s[i][1]=(u_int8_t)(data8+128);
X		 break;
X	 case AFMT_U16_LE:
X		 sample->data.data16[i]=(u_int16_t)(data16+32768);
X		 break;
X	 case AFMT_U16_LE|AFMT_STEREO:
X		 sample->data.data16_s[i][0]=
X		 sample->data.data16_s[i][1]=(u_int16_t)(data16+32768);
X		 break;
X	 }
X	}
X	return 0;
X}
X
X
Xstatic int
Xsample_shutdown(sound_sample *sample)
X{
X	free(sample->data.data);
X	sample->data.data=0;
X	return 0;
X}
X
Xstatic int
Xsample_play_init(sample_play *splay)
X{
X	bzero(splay, sizeof(*splay));
X	return 0;
X}
X
Xtypedef struct {
X u_long remain;
X u_int8_t *dest;
X unsigned offset;
X size_t bufsize;
X u_long	ncopy;
X} sound_dest_helper;
X
Xstatic void
Xcopy_data_helper(sound_sample *sample, sample_play *splay, sound_dest_helper *helper) 
X{
Xsize_t ncopy;
X
X	 ncopy=helper->remain*sample->sample_len;
X	 memcpy(helper->dest, sample->data.data8+helper->offset, ncopy);
X	 helper->dest+=ncopy;
X	 helper->ncopy+=ncopy;
X	 helper->bufsize-=helper->remain;
X	 splay->offset+=helper->remain;
X}
X
Xstatic u_long
Xsample_play_fill_data(sound_sample *sample, sample_play *splay, void *buf, size_t bufsize)
X{
Xsound_dest_helper helper;
X
X	assert(bufsize%sample->sample_len==0);
X	helper.bufsize = bufsize/sample->sample_len;
X	helper.remain = splay->offset%sample->nsamples;
X	helper.dest = (u_int8_t*)buf;
X	helper.ncopy = 0;
X	if (helper.remain) {
X	 helper.offset = helper.remain * sample->sample_len;
X	 helper.remain = sample->nsamples - helper.remain;
X	 if (helper.remain>helper.bufsize) helper.remain=helper.bufsize;
X	 copy_data_helper(sample, splay, &helper);
X	}
X	while (helper.bufsize>=sample->nsamples) {
X	 helper.offset=0;
X	 helper.remain=sample->nsamples;
X	 copy_data_helper(sample, splay, &helper);
X	}
X	if (helper.bufsize) {
X	 helper.offset=0;
X	 helper.remain=helper.bufsize;
X	 copy_data_helper(sample, splay, &helper);
X	}
X	return helper.ncopy;
X}
X
Xstatic int
Xsample_play_shutdown(sample_play *splay)
X{
X	return 0;
X}
X
Xstatic int 
Xsound_open(sound_info *snd)
X{
Xsnd_capabilities info;
Xint rc;
Xint sound;
X
X	bzero(snd, sizeof(*snd));
X	snd->name=strdup(app_options->device);
X	sound=open(snd->name ,O_RDWR);
X
X	if (sound<0) {
X		err(EX_UNAVAILABLE,snd->name);
X	}
X	
X	rc=ioctl(sound,AIOGCAP,&info);
X 	if (rc<0) return rc;
X	#if 0
X	printf("soundcard capability: rate_min %lu, rate_max %lu,\n"
X	       "formats %08lx, \n"
X	       "bufsize %lu,\n"
X	       "mixers %08lx, inputs %08lx, left %u, right %u\n",
X		info.rate_min,info.rate_max,
X		info.formats,
X		info.bufsize,
X		info.mixers, info.inputs, info.left, info.right);
X	#endif
X	snd->info=info;
X	snd->fd=sound;
X	return 0;
X}
X
Xstatic void wait_for_time(const struct timeval *stop)
X{
Xstruct timeval current,remain_tv;
Xstruct timespec remain;
Xint rc;
X
X	gettimeofday(&current,NULL);
X	timersub(stop, &current, &remain_tv);
X	TIMEVAL_TO_TIMESPEC(&remain_tv, &remain);
X	/* printf("%d,%ld\n", (int)remain.tv_sec,remain.tv_nsec); */
X	for (;;) {
X	 rc=nanosleep(&remain,&remain);
X	 if (rc!=-1 || errno!=EINTR) break;
X	}
X	return;
X}
X
X#define SOUND_WRITE	1
X	
Xstatic int
Xsound_play(sound_info *snd,sound_sample *sample)
X{
Xu_int8_t *data,*pdata;
Xsample_play play;
Xstruct timeval begin,stop;
Xconst int play_time=app_options->play_time,pause_time=app_options->pause_time;
Xu_long nsamples,offset;
Xunsigned nwrite,nwrote;
Xint i;
Xint rc;
Xint block_size;
X
X	block_size=app_options->write_block_size;
X	data=malloc(block_size);
X	assert(data);
X	nsamples=sample->rate*play_time*sample->sample_len;
X	for (i=0;i<app_options->nperiods;i++) {
X	 sample_play_init(&play);
X	 gettimeofday(&begin,NULL);
X	 fprintf(stderr,"\rsound playing%20c\r", ' ');
X	 for (offset=0;offset<nsamples;) {
X	  if (nsamples-offset>block_size) {
X	    nwrite=block_size;
X	  } else {
X	    nwrite=nsamples-offset;
X	    nwrite=((nwrite+sample->align-1)/sample->align)*sample->align;
X	  }
X	  sample_play_fill_data(sample,&play,data,nwrite);
X	  assert((nwrite%sample->align)==0);
X	  #if SOUND_WRITE
X	  for (nwrote=0,pdata=data;nwrite;) {
X	   rc=write(snd->fd,pdata,nwrite);
X	   if (rc<0) {
X		warn("Error on writing %s",snd->name);
X		if (errno == EINTR) continue;
X		break;
X	   }
X	   nwrote+=rc;
X	   pdata+=rc;
X	   nwrite-=rc;
X	  }
X	  #else
X	  nwrote=nwrite;
X	  #endif
X	  offset+=nwrote; 
X	 }
X	 #if SOUND_WRITE
X	 /*
X	 rc=ioctl(snd->fd, SNDCTL_DSP_SYNC, NULL);
X	 if (rc<0) warn("Error on sync at %s",snd->name);
X	 printf(" sync done\n");
X	 */
X	 #endif
X	 stop=begin;
X	 stop.tv_sec+=play_time;
X	 fprintf(stderr,"\rsound sync%20c\r", ' ');
X	 wait_for_time(&stop);
X	 fprintf(stderr,"\rsilent%20c\r", ' ');
X	 stop=begin;
X	 stop.tv_sec+=play_time+pause_time;
X	 wait_for_time(&stop);	 
X	 #if 0
X	 sound_change_param(snd, snd->rate, snd->format);
X	 #endif
X	 sample_play_shutdown(&play);
X	}
X	free(data);
X	return 0;
X}
X
X	
Xstatic int 
Xsound_sync(sound_info *snd)
X{
X	close(snd->fd);
X	snd->fd=open(snd->name ,O_RDWR);
X
X	if (snd->fd<0) {
X		err(EX_UNAVAILABLE,snd->name);
X	}
X	return 0;
X}
X
Xchar *
Xformat2str(u_long format, u_long rate, char *dest, size_t maxlen)
X{
Xchar str[64];
Xconst char *name;
Xconst char *stereo;
X
X	switch(format&~AFMT_STEREO) {
X	case AFMT_U8:
X	   name="Unsigned 8-bit";
X	   break;
X	case AFMT_U16_LE:
X	   name="Unsigned 16-bit, Little endian";
X	   break;
X	default:
X	   snprintf(str,sizeof(str),"Unknown [0x%08lx]", format&~AFMT_STEREO);
X	   name=str;
X	   break;
X	}
X	if (format&AFMT_STEREO) stereo="Stereo, ";
X	else stereo="";
X	snprintf(dest,maxlen,"%s%s rate %lu", stereo, name, rate);
X	return dest;
X}
X
Xstatic int
Xsound_change_param(sound_info *snd, u_long rate, u_long format)
X{
Xsnd_chan_param param;
Xint rc;
Xchar str[256];
Xchar str2[256];
X
X	rc=sound_sync(snd);
X	if (rc<0) return rc;
X	bzero(&param,sizeof(param));
X	param.play_rate=rate;
X	param.play_format=format;
X	rc=ioctl(snd->fd, AIOSFMT, &param);
X	if (rc<0) {
X		warn("Device %s: format %s rejected",
X			snd->name, format2str(format, rate, str, sizeof(str)));
X		return rc;
X	} 
X	if (param.play_format!=format || param.play_rate!=rate) {
X		fprintf(stderr, "Device %s: format %s unsupported, "
X		       "has been changed to %s\n",
X			snd->name,
X			format2str(format, rate, str, sizeof(str)),
X			format2str(param.play_format, param.play_rate,
X					str2, sizeof(str2))
X		);
X		return -1;
X	}	
X	snd->format=format;
X	snd->rate=rate;
X	return 0;
X}
X
Xstatic int 
Xsound_test(sound_info *snd)
X{
Xint rate_index, format_index;
Xu_long rate, format;
Xint rc;
Xsound_sample sample;
Xchar sformat[256];
X
X	for(rate_index=0;l_array_get(&app_options->rates, rate_index, &rate)>=0;rate_index++) {
X	   for(format_index=0;l_array_get(&app_options->formats, format_index, &format)>=0; format_index++) {
X		rc=sound_change_param(snd, rate, format);
X		if (rc<0) continue;
X		format2str(format, rate, sformat, sizeof(sformat));
X		fprintf(stderr, "%s%30c\n", sformat,' ');
X		rc=sample_init(&sample, format, rate, app_options->play_freq);
X		if (rc<0) {
X			fprintf(stderr, "Unsupported format %s\n", sformat);
X			continue;
X		}
X		sound_play(snd, &sample);
X		sample_shutdown(&sample);
X	   }
X	}
X	return 0;
X}
X
X		
Xstatic int 
Xsound_close(sound_info *snd)
X{
X	if (snd->fd>0) close(snd->fd);
X	snd->fd=-1;
X	free(snd->name);
X	snd->name=NULL;
X	return 0;
X}
X
Xstatic void 
Xsig_int_handler(int dummy)
X{
X}
X
Xint main(int argc, char **argv)
X{
Xsound_info snd;
X
X	sound_options_init(app_options);
X	sound_options_fill(app_options,argc,argv);
X	if (app_options->catch_sigint)
X		signal(SIGINT,sig_int_handler);
X 	sound_open(&snd);
X	sound_test(&snd);
X	sound_close(&snd);
X	sound_options_shutdown(app_options);
X	return 0;
X}
END-of-sound.c
exit


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


To Unsubscribe: send mail to majordomo@FreeBSD.org
with "unsubscribe freebsd-bugs" in the body of the message




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