summaryrefslogtreecommitdiff
path: root/lib/libsndio
diff options
context:
space:
mode:
authorAlexandre Ratchov <ratchov@cvs.openbsd.org>2013-08-24 12:32:36 +0000
committerAlexandre Ratchov <ratchov@cvs.openbsd.org>2013-08-24 12:32:36 +0000
commit1cfd14998d914c90a36c5e3456a9bd754bf60a2e (patch)
treed944b397e8b14651ee13f3071d80dc54c582b41b /lib/libsndio
parent8cacaa9a558698840b06432b2090a55a1cad33e2 (diff)
Move underrun/overrun recovery code from the sun-api back-end to
the the generic code, so it can be used by other back-ends as well. No behavior change (hopefully).
Diffstat (limited to 'lib/libsndio')
-rw-r--r--lib/libsndio/sio.c160
-rw-r--r--lib/libsndio/sio_priv.h15
-rw-r--r--lib/libsndio/sio_sun.c107
3 files changed, 138 insertions, 144 deletions
diff --git a/lib/libsndio/sio.c b/lib/libsndio/sio.c
index a3ebdc102e2..d8ccf0c666c 100644
--- a/lib/libsndio/sio.c
+++ b/lib/libsndio/sio.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sio.c,v 1.13 2013/04/03 03:13:32 guenther Exp $ */
+/* $OpenBSD: sio.c,v 1.14 2013/08/24 12:32:34 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -98,6 +98,10 @@ sio_close(struct sio_hdl *hdl)
int
sio_start(struct sio_hdl *hdl)
{
+#ifdef DEBUG
+ struct timespec ts;
+#endif
+
if (hdl->eof) {
DPRINTF("sio_start: eof\n");
return 0;
@@ -110,9 +114,11 @@ sio_start(struct sio_hdl *hdl)
#ifdef DEBUG
if (!sio_getpar(hdl, &hdl->par))
return 0;
- hdl->pollcnt = hdl->wcnt = hdl->rcnt = hdl->realpos = 0;
- clock_gettime(CLOCK_MONOTONIC, &hdl->ts);
+ hdl->pollcnt = hdl->wcnt = hdl->rcnt = hdl->cpos = 0;
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+ hdl->start_nsec = 1000000000LL * ts.tv_sec + ts.tv_nsec;
#endif
+ hdl->rdrop = hdl->wsil = 0;
if (!hdl->ops->start(hdl))
return 0;
hdl->started = 1;
@@ -236,6 +242,46 @@ sio_psleep(struct sio_hdl *hdl, int event)
return 1;
}
+static int
+sio_rdrop(struct sio_hdl *hdl)
+{
+#define DROP_NMAX 0x1000
+ static char dummy[DROP_NMAX];
+ ssize_t n, todo;
+
+ while (hdl->rdrop > 0) {
+ todo = hdl->rdrop;
+ if (todo > DROP_NMAX)
+ todo = DROP_NMAX;
+ n = hdl->ops->read(hdl, dummy, todo);
+ if (n == 0)
+ return 0;
+ hdl->rdrop -= n;
+ DPRINTF("sio_rdrop: dropped %zu bytes\n", n);
+ }
+ return 1;
+}
+
+static int
+sio_wsil(struct sio_hdl *hdl)
+{
+#define ZERO_NMAX 0x1000
+ static char zero[ZERO_NMAX];
+ ssize_t n, todo;
+
+ while (hdl->wsil > 0) {
+ todo = hdl->wsil;
+ if (todo > ZERO_NMAX)
+ todo = ZERO_NMAX;
+ n = hdl->ops->write(hdl, zero, todo);
+ if (n == 0)
+ return 0;
+ hdl->wsil -= n;
+ DPRINTF("sio_wsil: inserted %zu bytes\n", n);
+ }
+ return 1;
+}
+
size_t
sio_read(struct sio_hdl *hdl, void *buf, size_t len)
{
@@ -256,6 +302,8 @@ sio_read(struct sio_hdl *hdl, void *buf, size_t len)
DPRINTF("sio_read: zero length read ignored\n");
return 0;
}
+ if (!sio_rdrop(hdl))
+ return 0;
while (todo > 0) {
n = hdl->ops->read(hdl, data, todo);
if (n == 0) {
@@ -280,13 +328,6 @@ sio_write(struct sio_hdl *hdl, const void *buf, size_t len)
unsigned int n;
const unsigned char *data = buf;
size_t todo = len;
-#ifdef DEBUG
- struct timespec ts0, ts1, dts;
- unsigned int us;
-
- if (sndio_debug >= 2)
- clock_gettime(CLOCK_MONOTONIC, &ts0);
-#endif
if (hdl->eof) {
DPRINTF("sio_write: eof\n");
@@ -301,6 +342,8 @@ sio_write(struct sio_hdl *hdl, const void *buf, size_t len)
DPRINTF("sio_write: zero length write ignored\n");
return 0;
}
+ if (!sio_wsil(hdl))
+ return 0;
while (todo > 0) {
n = hdl->ops->write(hdl, data, todo);
if (n == 0) {
@@ -316,19 +359,6 @@ sio_write(struct sio_hdl *hdl, const void *buf, size_t len)
hdl->wcnt += n;
#endif
}
-#ifdef DEBUG
- if (sndio_debug >= 2) {
- clock_gettime(CLOCK_MONOTONIC, &ts1);
- timespecsub(&ts0, &hdl->ts, &dts);
- DPRINTF("%lld.%09ld: ", (long long)dts.tv_sec, dts.tv_nsec);
-
- timespecsub(&ts1, &ts0, &dts);
- us = dts.tv_sec * 1000000 + dts.tv_nsec/1000;
- DPRINTF(
- "sio_write: wrote %d bytes of %d in %uus\n",
- (int)(len - todo), (int)len, us);
- }
-#endif
return len - todo;
}
@@ -353,8 +383,7 @@ sio_revents(struct sio_hdl *hdl, struct pollfd *pfd)
{
int revents;
#ifdef DEBUG
- struct timespec ts0, ts1, dts;
- unsigned int us;
+ struct timespec ts0, ts1;
if (sndio_debug >= 2)
clock_gettime(CLOCK_MONOTONIC, &ts0);
@@ -368,17 +397,20 @@ sio_revents(struct sio_hdl *hdl, struct pollfd *pfd)
if (!hdl->started)
return revents & POLLHUP;
#ifdef DEBUG
- if (sndio_debug >= 2) {
+ if (sndio_debug >= 3) {
clock_gettime(CLOCK_MONOTONIC, &ts1);
- timespecsub(&ts0, &hdl->ts, &dts);
- DPRINTF("%lld.%09ld: ", (long long)dts.tv_sec, dts.tv_nsec);
-
- timespecsub(&ts1, &ts0, &dts);
- us = dts.tv_sec * 1000000 + dts.tv_nsec/1000;
- DPRINTF("sio_revents: revents = 0x%x, complete in %uus\n",
- revents, us);
+ DPRINTF("%09lld: sio_revents: revents = 0x%x, took %lldns\n",
+ 1000000000LL * ts0.tv_sec +
+ ts0.tv_nsec - hdl->start_nsec,
+ revents,
+ 1000000000LL * (ts1.tv_sec - ts0.tv_sec) +
+ ts1.tv_nsec - ts0.tv_nsec);
}
#endif
+ if ((hdl->mode & SIO_PLAY) && !sio_wsil(hdl))
+ revents &= ~POLLOUT;
+ if ((hdl->mode & SIO_REC) && !sio_rdrop(hdl))
+ revents &= ~POLLIN;
return revents;
}
@@ -400,28 +432,54 @@ sio_onmove(struct sio_hdl *hdl, void (*cb)(void *, int), void *addr)
hdl->move_addr = addr;
}
+#ifdef DEBUG
+void
+sio_printpos(struct sio_hdl *hdl)
+{
+ struct timespec ts;
+ long long rpos, rdiff;
+ long long cpos, cdiff;
+ long long wpos, wdiff;
+
+ clock_gettime(CLOCK_MONOTONIC, &ts);
+
+ rpos = (hdl->mode & SIO_REC) ?
+ hdl->rcnt / (hdl->par.bps * hdl->par.rchan) : 0;
+ wpos = (hdl->mode & SIO_PLAY) ?
+ hdl->wcnt / (hdl->par.bps * hdl->par.pchan) : 0;
+
+ cdiff = hdl->cpos % hdl->par.round;
+ cpos = hdl->cpos / hdl->par.round;
+ if (cdiff > hdl->par.round / 2) {
+ cpos++;
+ cdiff = cdiff - hdl->par.round;
+ }
+ rdiff = rpos % hdl->par.round;
+ rpos = rpos / hdl->par.round;
+ if (rdiff > hdl->par.round / 2) {
+ rpos++;
+ rdiff = rdiff - hdl->par.round;
+ }
+ wdiff = wpos % hdl->par.round;
+ wpos = wpos / hdl->par.round;
+ if (wdiff > hdl->par.round / 2) {
+ wpos++;
+ wdiff = wdiff - hdl->par.round;
+ }
+ DPRINTF("%011lld: "
+ "clk %+5lld%+5lld, wr %+5lld%+5lld rd: %+5lld%+5lld\n",
+ 1000000000LL * ts.tv_sec + ts.tv_nsec - hdl->start_nsec,
+ cpos, cdiff, wpos, wdiff, rpos, rdiff);
+}
+#endif
+
void
sio_onmove_cb(struct sio_hdl *hdl, int delta)
{
#ifdef DEBUG
- struct timespec ts0, dts;
- long long playpos;
-
- if (sndio_debug >= 3 && (hdl->mode & SIO_PLAY)) {
- clock_gettime(CLOCK_MONOTONIC, &ts0);
- timespecsub(&ts0, &hdl->ts, &dts);
- DPRINTF("%lld.%09ld: ", (long long)dts.tv_sec, dts.tv_nsec);
- hdl->realpos += delta;
- playpos = hdl->wcnt / (hdl->par.bps * hdl->par.pchan);
- DPRINTF("sio_onmove_cb: delta = %+7d, "
- "plat = %+7lld, "
- "realpos = %+7lld, "
- "bufused = %+7lld\n",
- delta,
- playpos - hdl->realpos,
- hdl->realpos,
- hdl->realpos < 0 ? playpos : playpos - hdl->realpos);
- }
+ hdl->cpos += delta;
+ if (sndio_debug >= 2)
+ sio_printpos(hdl);
#endif
if (hdl->move_cb)
hdl->move_cb(hdl->move_addr, delta);
diff --git a/lib/libsndio/sio_priv.h b/lib/libsndio/sio_priv.h
index f95885d2aa9..b8792ffcc46 100644
--- a/lib/libsndio/sio_priv.h
+++ b/lib/libsndio/sio_priv.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sio_priv.h,v 1.5 2013/04/03 03:13:32 guenther Exp $ */
+/* $OpenBSD: sio_priv.h,v 1.6 2013/08/24 12:32:35 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -35,12 +35,14 @@ struct sio_hdl {
int started; /* true if started */
int nbio; /* true if non-blocking io */
int eof; /* true if error occured */
+ int rdrop; /* recorded bytes to drop */
+ int wsil; /* silence to play */
#ifdef DEBUG
unsigned long long pollcnt; /* times sio_revents was called */
- unsigned long long wcnt; /* bytes written with sio_write() */
- unsigned long long rcnt; /* bytes read with sio_read() */
- long long realpos;
- struct timespec ts;
+ long long wcnt; /* bytes written with sio_write() */
+ long long rcnt; /* bytes read with sio_read() */
+ long long cpos;
+ long long start_nsec;
struct sio_par par;
#endif
};
@@ -70,5 +72,8 @@ void sio_create(struct sio_hdl *, struct sio_ops *, unsigned, int);
void sio_destroy(struct sio_hdl *);
void sio_onmove_cb(struct sio_hdl *, int);
void sio_onvol_cb(struct sio_hdl *, unsigned);
+#ifdef DEBUG
+void sio_printpos(struct sio_hdl *);
+#endif
#endif /* !defined(SNDIO_PRIV_H) */
diff --git a/lib/libsndio/sio_sun.c b/lib/libsndio/sio_sun.c
index 880017566be..779e864f9ce 100644
--- a/lib/libsndio/sio_sun.c
+++ b/lib/libsndio/sio_sun.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sio_sun.c,v 1.7 2012/09/14 22:50:26 ratchov Exp $ */
+/* $OpenBSD: sio_sun.c,v 1.8 2013/08/24 12:32:35 ratchov Exp $ */
/*
* Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
*
@@ -50,9 +50,7 @@ struct sio_sun_hdl {
unsigned int ibpf, obpf; /* bytes per frame */
unsigned int ibytes, obytes; /* bytes the hw transferred */
unsigned int ierr, oerr; /* frames the hw dropped */
- int offset; /* frames play is ahead of record */
int idelta, odelta; /* position reported to client */
- int mix_fd, mix_index; /* /dev/mixerN stuff */
};
static void sio_sun_close(struct sio_hdl *);
@@ -450,7 +448,6 @@ sio_sun_start(struct sio_hdl *sh)
hdl->obytes = 0;
hdl->ierr = 0;
hdl->oerr = 0;
- hdl->offset = 0;
hdl->idelta = 0;
hdl->odelta = 0;
@@ -726,48 +723,12 @@ sio_sun_getpar(struct sio_hdl *sh, struct sio_par *par)
return 1;
}
-/*
- * drop recorded samples to compensate xruns
- */
-static int
-sio_sun_rdrop(struct sio_sun_hdl *hdl)
-{
-#define DROP_NMAX 0x1000
- static char dropbuf[DROP_NMAX];
- ssize_t n, todo;
-
- while (hdl->offset > 0) {
- todo = hdl->offset * hdl->ibpf;
- if (todo > DROP_NMAX)
- todo = DROP_NMAX - DROP_NMAX % hdl->ibpf;
- while ((n = read(hdl->fd, dropbuf, todo)) < 0) {
- if (errno == EINTR)
- continue;
- if (errno != EAGAIN) {
- DPERROR("sio_sun_rdrop: read");
- hdl->sio.eof = 1;
- }
- return 0;
- }
- if (n == 0) {
- DPRINTF("sio_sun_rdrop: eof\n");
- hdl->sio.eof = 1;
- return 0;
- }
- hdl->offset -= (int)n / (int)hdl->ibpf;
- DPRINTF("sio_sun_rdrop: dropped %ld/%ld bytes\n", n, todo);
- }
- return 1;
-}
-
static size_t
sio_sun_read(struct sio_hdl *sh, void *buf, size_t len)
{
struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
ssize_t n;
- if (!sio_sun_rdrop(hdl))
- return 0;
while ((n = read(hdl->fd, buf, len)) < 0) {
if (errno == EINTR)
continue;
@@ -817,37 +778,6 @@ sio_sun_autostart(struct sio_sun_hdl *hdl)
return 1;
}
-/*
- * insert silence to play to compensate xruns
- */
-static int
-sio_sun_wsil(struct sio_sun_hdl *hdl)
-{
-#define ZERO_NMAX 0x1000
- static char zero[ZERO_NMAX];
- ssize_t n, todo;
-
- while (hdl->offset < 0) {
- todo = (int)-hdl->offset * (int)hdl->obpf;
- if (todo > ZERO_NMAX)
- todo = ZERO_NMAX - ZERO_NMAX % hdl->obpf;
- while ((n = write(hdl->fd, zero, todo)) < 0) {
- if (errno == EINTR)
- continue;
- if (errno != EAGAIN) {
- DPERROR("sio_sun_wsil: write");
- hdl->sio.eof = 1;
- return 0;
- }
- return 0;
- }
- hdl->offset += (int)n / (int)hdl->obpf;
- DPRINTF("sio_sun_wsil: inserted %ld/%ld bytes\n", n, todo);
- }
- return 1;
-}
-
-
static size_t
sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len)
{
@@ -855,8 +785,6 @@ sio_sun_write(struct sio_hdl *sh, const void *buf, size_t len)
const unsigned char *data = buf;
ssize_t n, todo;
- if (!sio_sun_wsil(hdl))
- return 0;
todo = len;
while ((n = write(hdl->fd, data, todo)) < 0) {
if (errno == EINTR)
@@ -895,7 +823,7 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
{
struct sio_sun_hdl *hdl = (struct sio_sun_hdl *)sh;
struct audio_offset ao;
- int xrun, dmove, dierr = 0, doerr = 0, delta;
+ int xrun, dierr = 0, doerr = 0, offset, delta;
int revents = pfd->revents;
if (!hdl->sio.started)
@@ -934,6 +862,8 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
hdl->oerr = xrun;
if (!(hdl->sio.mode & SIO_REC))
dierr = doerr;
+ if (doerr > 0)
+ DPRINTF("play xrun %d\n", doerr);
}
if (hdl->sio.mode & SIO_REC) {
if (ioctl(hdl->fd, AUDIO_RERROR, &xrun) < 0) {
@@ -945,11 +875,21 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
hdl->ierr = xrun;
if (!(hdl->sio.mode & SIO_PLAY))
doerr = dierr;
+ if (dierr > 0)
+ DPRINTF("rec xrun %d\n", dierr);
+ }
+ offset = doerr - dierr;
+ if (offset > 0) {
+ hdl->sio.rdrop += offset * hdl->ibpf;
+ hdl->idelta -= doerr;
+ hdl->odelta -= doerr;
+ DPRINTF("will drop %d and pause %d\n", offset, doerr);
+ } else if (offset < 0) {
+ hdl->sio.wsil += -offset * hdl->obpf;
+ hdl->idelta -= dierr;
+ hdl->odelta -= dierr;
+ DPRINTF("will insert %d and pause %d\n", -offset, dierr);
}
- hdl->offset += doerr - dierr;
- dmove = dierr > doerr ? dierr : doerr;
- hdl->idelta -= dmove;
- hdl->odelta -= dmove;
delta = (hdl->idelta > hdl->odelta) ? hdl->idelta : hdl->odelta;
if (delta > 0) {
@@ -958,16 +898,7 @@ sio_sun_revents(struct sio_hdl *sh, struct pollfd *pfd)
hdl->odelta -= delta;
}
- /*
- * drop recorded samples or insert silence to play
- * right now to adjust revents, and avoid busy loops
- * programs
- */
if (hdl->filling)
- revents |= POLLOUT;
- if ((hdl->sio.mode & SIO_PLAY) && !sio_sun_wsil(hdl))
- revents &= ~POLLOUT;
- if ((hdl->sio.mode & SIO_REC) && !sio_sun_rdrop(hdl))
- revents &= ~POLLIN;
+ revents |= POLLOUT; /* XXX: is this necessary ? */
return revents;
}