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(¤t,NULL);
X timersub(stop, ¤t, &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(¶m,sizeof(param));
X param.play_rate=rate;
X param.play_format=format;
X rc=ioctl(snd->fd, AIOSFMT, ¶m);
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>
