If audio interrupts are missed (as this happens on some MP systems
authorratchov <ratchov@openbsd.org>
Fri, 6 Aug 2010 06:52:17 +0000 (06:52 +0000)
committerratchov <ratchov@openbsd.org>
Fri, 6 Aug 2010 06:52:17 +0000 (06:52 +0000)
now), play and record directons may be out of sync, and since the play
direction is used as clock source, we may end up with data ariving
_before_ the time it was recorded. This breaks the sndio ``device
model'' and most full-duplex apps relying on it, starting with aucat
in its default mode.

Workaround this by using the direction that's ahead as clock source,
ensuring that recorded data never arrives before the clock tick it was
recorded. This prevents apps from crashing but won't fix stuttering
caused by missed interrupts.

ok deraadt@

lib/libsndio/sun.c

index 914fd5d..946e493 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: sun.c,v 1.39 2010/07/21 23:00:16 ratchov Exp $        */
+/*     $OpenBSD: sun.c,v 1.40 2010/08/06 06:52:17 ratchov Exp $        */
 /*
  * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
  *
@@ -891,7 +891,7 @@ sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
 {
        struct sun_hdl *hdl = (struct sun_hdl *)sh;
        struct audio_offset ao;
-       int xrun, dmove, dierr = 0, doerr = 0, doffset = 0;
+       int xrun, dmove, dierr = 0, doerr = 0, delta;
        int revents = pfd->revents;
 
        if (hdl->sio.mode & SIO_PLAY) {
@@ -902,8 +902,8 @@ sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
                }
                doerr = xrun - hdl->oerr;
                hdl->oerr = xrun;
-               if (hdl->sio.mode & SIO_REC)
-                       doffset += doerr;
+               if (!(hdl->sio.mode & SIO_REC))
+                       dierr = doerr;
        }
        if (hdl->sio.mode & SIO_REC) {
                if (ioctl(hdl->fd, AUDIO_RERROR, &xrun) < 0) {
@@ -913,10 +913,10 @@ sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
                }
                dierr = xrun - hdl->ierr;
                hdl->ierr = xrun;
-               if (hdl->sio.mode & SIO_PLAY)
-                       doffset -= dierr;
+               if (!(hdl->sio.mode & SIO_PLAY))
+                       doerr = dierr;
        }
-       hdl->offset += doffset;
+       hdl->offset += doerr - dierr;
        dmove = dierr > doerr ? dierr : doerr;
        hdl->idelta -= dmove;
        hdl->odelta -= dmove;
@@ -927,25 +927,29 @@ sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
                        hdl->sio.eof = 1;
                        return POLLHUP;
                }
-               hdl->odelta += (ao.samples - hdl->obytes) / hdl->obpf;
+               delta = (ao.samples - hdl->obytes) / hdl->obpf;
                hdl->obytes = ao.samples;
-               if (hdl->odelta > 0) {
-                       sio_onmove_cb(&hdl->sio, hdl->odelta);
-                       hdl->odelta = 0;
-               }
+               hdl->odelta += delta;
+               if (!(hdl->sio.mode & SIO_REC))
+                       hdl->idelta += delta;
        }
-       if ((revents & POLLIN) && !(hdl->sio.mode & SIO_PLAY)) {
+       if ((revents & POLLIN) && (hdl->sio.mode & SIO_REC)) {
                if (ioctl(hdl->fd, AUDIO_GETIOFFS, &ao) < 0) {
                        DPERROR("sun_revents: GETIOFFS");
                        hdl->sio.eof = 1;
                        return POLLHUP;
                }
-               hdl->idelta += (ao.samples - hdl->ibytes) / hdl->ibpf;
+               delta = (ao.samples - hdl->ibytes) / hdl->ibpf;
                hdl->ibytes = ao.samples;
-               if (hdl->idelta > 0) {
-                       sio_onmove_cb(&hdl->sio, hdl->idelta);
-                       hdl->idelta = 0;
-               }
+               hdl->idelta += delta;
+               if (!(hdl->sio.mode & SIO_PLAY))
+                       hdl->odelta += delta;
+       }
+       delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
+       if (delta > 0) {
+               sio_onmove_cb(&hdl->sio, delta);
+               hdl->idelta -= delta;
+               hdl->odelta -= delta;
        }
 
        /*